>> Flow 1 (client to server)
-00000000 16 03 01 00 79 01 00 00 75 03 03 00 00 00 00 00 |....y...u.......|
+00000000 16 03 01 00 81 01 00 00 7d 03 03 00 00 00 00 00 |........}.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 1e c0 2f |.............../|
00000030 c0 2b c0 30 c0 2c c0 11 c0 07 c0 13 c0 09 c0 14 |.+.0.,..........|
-00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 2e |...../.5........|
+00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 36 |...../.5.......6|
00000050 00 05 00 05 01 00 00 00 00 00 0a 00 08 00 06 00 |................|
-00000060 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 0a 00 |................|
-00000070 08 04 01 04 03 02 01 02 03 ff 01 00 01 00 |..............|
+00000060 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 0e 00 |................|
+00000070 0c 04 01 04 03 05 01 05 03 02 01 02 03 ff 01 00 |................|
+00000080 01 00 00 12 00 00 |......|
>>> Flow 2 (server to client)
-00000000 16 03 02 00 51 02 00 00 4d 03 02 4d 2e 22 e8 40 |....Q...M..M.".@|
-00000010 e2 be 4a dd cc 74 ce 49 40 04 76 fa 30 11 04 64 |..J..t.I@.v.0..d|
-00000020 d3 d8 28 cc ef cb 1b 08 70 ca 28 20 74 90 27 2e |..(.....p.( t.'.|
-00000030 c7 bb 53 66 93 49 da 00 d9 96 06 cf ac 08 40 66 |..Sf.I........@f|
-00000040 41 eb 6e c8 32 2d 10 a6 42 7b 0d 5e 00 05 00 00 |A.n.2-..B{.^....|
+00000000 16 03 02 00 51 02 00 00 4d 03 02 7e 38 ae 3c 50 |....Q...M..~8.>> Flow 4 (server to client)
-00000000 14 03 02 00 01 01 16 03 02 00 24 62 dc 82 ed 69 |..........$b...i|
-00000010 cc e2 19 72 9c 1b 84 15 77 d8 a0 35 7d b7 47 55 |...r....w..5}.GU|
-00000020 95 4f 67 ad f1 8f 91 01 c0 31 2f 54 64 40 35 |.Og......1/Td@5|
+00000000 14 03 02 00 01 01 16 03 02 00 24 a3 f3 22 a8 32 |..........$..".2|
+00000010 63 c3 88 5c 0f fb 2d 47 21 0d 62 e2 db aa ed ae |c..\..-G!.b.....|
+00000020 b6 5f e3 c8 98 fc 91 5e 04 83 cf c3 21 17 ce |._.....^....!..|
>>> Flow 5 (client to server)
-00000000 17 03 02 00 1a 63 52 52 39 6a 98 7a a8 41 cd b4 |.....cRR9j.z.A..|
-00000010 e4 de 75 32 76 9a ee 44 96 d0 e9 66 b8 0a b5 15 |..u2v..D...f....|
-00000020 03 02 00 16 9f 06 3f 07 78 12 b7 70 db 48 fc ef |......?.x..p.H..|
-00000030 ff 6e a7 4f e5 82 7f 0c f2 35 |.n.O.....5|
+00000000 17 03 02 00 1a f1 e4 46 c7 14 91 4b c6 25 fd aa |.......F...K.%..|
+00000010 5d dd 3f 61 ac 9c 79 68 bc e6 0f a1 e4 f3 73 15 |].?a..yh......s.|
+00000020 03 02 00 16 6b 8d 23 3c 99 b4 c2 23 3c 27 fd 41 |....k.#<...#<'.A|
+00000030 cc 04 e5 fc e7 f9 d9 81 0a b8 |..........|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ALPN b/src/crypto/tls/testdata/Client-TLSv12-ALPN
index a30f1a2ab2..9ecb065f09 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-ALPN
+++ b/src/crypto/tls/testdata/Client-TLSv12-ALPN
@@ -1,20 +1,20 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 00 91 01 00 00 8d 03 03 00 00 00 00 00 |................|
+00000000 16 03 01 00 99 01 00 00 95 03 03 00 00 00 00 00 |................|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 1e c0 2f |.............../|
00000030 c0 2b c0 30 c0 2c c0 11 c0 07 c0 13 c0 09 c0 14 |.+.0.,..........|
-00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 46 |...../.5.......F|
+00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 4e |...../.5.......N|
00000050 33 74 00 00 00 05 00 05 01 00 00 00 00 00 0a 00 |3t..............|
00000060 08 00 06 00 17 00 18 00 19 00 0b 00 02 01 00 00 |................|
-00000070 0d 00 0a 00 08 04 01 04 03 02 01 02 03 ff 01 00 |................|
-00000080 01 00 00 10 00 10 00 0e 06 70 72 6f 74 6f 32 06 |.........proto2.|
-00000090 70 72 6f 74 6f 31 |proto1|
+00000070 0d 00 0e 00 0c 04 01 04 03 05 01 05 03 02 01 02 |................|
+00000080 03 ff 01 00 01 00 00 10 00 10 00 0e 06 70 72 6f |.............pro|
+00000090 74 6f 32 06 70 72 6f 74 6f 31 00 12 00 00 |to2.proto1....|
>>> Flow 2 (server to client)
-00000000 16 03 03 00 66 02 00 00 62 03 03 7e 48 0b 4a 89 |....f...b..~H.J.|
-00000010 d3 3a a1 8a 8c 8b 11 bb ea c5 21 5c df 3c 81 2b |.:........!\.<.+|
-00000020 c5 c0 7c f9 fd d7 cb 10 1b dd d4 20 b4 8a a5 07 |..|........ ....|
-00000030 32 e7 04 9c 1c 73 87 cd e3 ae ff 8b 5c d7 56 6c |2....s......\.Vl|
-00000040 03 24 7d 35 4c ad 31 52 c3 cd 5c b0 c0 2f 00 00 |.$}5L.1R..\../..|
+00000000 16 03 03 00 66 02 00 00 62 03 03 56 83 34 0f 9e |....f...b..V.4..|
+00000010 dd 02 1c 4f 4f 09 d0 2c df e6 c1 d2 4a c0 6a e7 |...OO..,....J.j.|
+00000020 1e 65 51 c2 42 01 05 70 4a 6c 97 20 0f a8 fb d8 |.eQ.B..pJl. ....|
+00000030 2f 0f 75 21 17 f8 dd 63 28 4a 18 f6 b1 e5 6f 7c |/.u!...c(J....o||
+00000040 1d 09 d4 13 bf 66 3a bd c5 48 14 fc c0 2f 00 00 |.....f:..H.../..|
00000050 1a ff 01 00 01 00 00 0b 00 04 03 00 01 02 00 10 |................|
00000060 00 09 00 07 06 70 72 6f 74 6f 31 16 03 03 02 be |.....proto1.....|
00000070 0b 00 02 ba 00 02 b7 00 02 b4 30 82 02 b0 30 82 |..........0...0.|
@@ -61,19 +61,19 @@
00000300 b6 d8 c9 75 90 96 8c 0f 52 98 b5 cd 98 1f 89 20 |...u....R...... |
00000310 5f f2 a0 1c a3 1b 96 94 dd a9 fd 57 e9 70 e8 26 |_..........W.p.&|
00000320 6d 71 99 9b 26 6e 38 50 29 6c 90 a7 bd d9 16 03 |mq..&n8P)l......|
-00000330 03 00 cd 0c 00 00 c9 03 00 17 41 04 36 ae 35 52 |..........A.6.5R|
-00000340 e2 d1 7b 5f 96 91 06 73 30 0c c8 cb 42 e3 95 11 |..{_...s0...B...|
-00000350 52 02 5a 8a 8a a4 b3 f9 03 f0 6d 8b 23 3e 73 44 |R.Z.......m.#>sD|
-00000360 2d 3e fb 05 ac c2 0a f4 96 07 58 aa fc 9f f4 8b |->........X.....|
-00000370 38 af 46 6a a6 87 b7 6d 65 eb 75 17 04 01 00 80 |8.Fj...me.u.....|
-00000380 44 0d 99 2f 79 3d 66 0b 7c 76 f8 95 14 78 90 f9 |D../y=f.|v...x..|
-00000390 ee bb 74 9b 01 25 62 a3 58 d6 8d 4b 43 0a 18 16 |..t..%b.X..KC...|
-000003a0 4d 44 fa 01 13 de 32 36 16 6a 4d 9a 6d ab dd e5 |MD....26.jM.m...|
-000003b0 a8 9d 9e 4a f8 18 fd da 95 99 02 20 29 b3 79 f6 |...J....... ).y.|
-000003c0 c7 c4 eb 81 45 ef 20 5f 2b ed 5f 72 a5 5f 99 0b |....E. _+._r._..|
-000003d0 54 25 0d db 11 7f 64 ec 5a 2f 38 c7 74 29 77 f0 |T%....d.Z/8.t)w.|
-000003e0 4b 9c 92 72 02 4c f3 bf ee ba e1 51 fb b4 ac e6 |K..r.L.....Q....|
-000003f0 0c 4c 19 bc 9a b7 e9 fd 8a 86 bf 37 d5 0b 1d 2a |.L.........7...*|
+00000330 03 00 cd 0c 00 00 c9 03 00 17 41 04 85 b7 f7 7c |..........A....||
+00000340 49 4e 97 14 07 51 bc 56 2d 3f cf 1d 29 08 ac 6a |IN...Q.V-?..)..j|
+00000350 b4 e7 0d 62 d8 fd 4d 03 29 0d f8 6c 36 6f 4d 5f |...b..M.)..l6oM_|
+00000360 b7 5a 8e 37 3e c2 d9 dc f4 15 52 e9 87 71 0f e5 |.Z.7>.....R..q..|
+00000370 4e a6 88 0e 54 35 e0 8b 50 91 e1 c4 04 01 00 80 |N...T5..P.......|
+00000380 51 eb f8 d6 52 ba f5 b5 0a 22 5f 91 fe f7 ee 43 |Q...R...."_....C|
+00000390 f8 af 52 b6 27 2c fc 14 e2 fb 41 61 ff 7c b9 be |..R.',....Aa.|..|
+000003a0 f9 78 be dc 18 32 8c 4d ef 46 c0 5a a7 91 6a 1b |.x...2.M.F.Z..j.|
+000003b0 47 78 46 39 47 81 8a 2d b4 cb fd bb 44 3e a7 b7 |GxF9G..-....D>..|
+000003c0 cc 4e df 17 7b 2b 38 49 fa 9d 9f 4e cd ed f2 16 |.N..{+8I...N....|
+000003d0 03 d9 68 cf c9 5a 08 32 f8 ed 02 30 54 61 f6 c0 |..h..Z.2...0Ta..|
+000003e0 f6 78 bc ad 04 9c 8e 90 7d 3d f5 35 86 aa 6e e9 |.x......}=.5..n.|
+000003f0 a2 9a d3 86 27 9f 2d 6e ea 6e ad 82 0e aa ef 97 |....'.-n.n......|
00000400 16 03 03 00 04 0e 00 00 00 |.........|
>>> Flow 3 (client to server)
00000000 16 03 03 00 46 10 00 00 42 41 04 1e 18 37 ef 0d |....F...BA...7..|
@@ -81,17 +81,17 @@
00000020 a7 24 20 3e b2 56 1c ce 97 28 5e f8 2b 2d 4f 9e |.$ >.V...(^.+-O.|
00000030 f1 07 9f 6c 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 |...lK[.V.2B.X..I|
00000040 a6 b5 68 1a 41 03 56 6b dc 5a 89 14 03 03 00 01 |..h.A.Vk.Z......|
-00000050 01 16 03 03 00 28 00 00 00 00 00 00 00 00 27 e5 |.....(........'.|
-00000060 ee c8 9a 3e d6 70 d6 1a 1b ad d2 1a 88 be 77 fd |...>.p........w.|
-00000070 bc e2 33 13 22 52 df be 67 30 da 10 5c cf |..3."R..g0..\.|
+00000050 01 16 03 03 00 28 00 00 00 00 00 00 00 00 47 18 |.....(........G.|
+00000060 39 03 93 d9 5b 27 29 70 52 68 15 79 f2 60 e6 58 |9...[')pRh.y.`.X|
+00000070 d9 98 cd ce a1 8f 4d ee 2c f0 34 9f fa 73 |......M.,.4..s|
>>> Flow 4 (server to client)
-00000000 14 03 03 00 01 01 16 03 03 00 28 81 ad 88 a5 2e |..........(.....|
-00000010 1f 26 3c 53 16 a7 d4 c2 13 08 52 6e ac 3b 00 9d |.&.MjB|
+00000020 1b 9b bd 07 6b 19 ff f9 2c 19 3c c8 e7 06 fa c8 |....k...,.<.....|
+00000030 3d 52 b4 |=R.|
>>> Flow 5 (client to server)
-00000000 17 03 03 00 1e 00 00 00 00 00 00 00 01 ab b6 ac |................|
-00000010 55 5d 72 b0 7a a1 0e 17 8d 1b 71 77 79 ef 32 6b |U]r.z.....qwy.2k|
-00000020 4e c2 df 15 03 03 00 1a 00 00 00 00 00 00 00 02 |N...............|
-00000030 34 1e 22 35 71 60 cd cf 75 2b 73 94 b6 5f 09 1d |4."5q`..u+s.._..|
-00000040 1b b5 |..|
+00000000 17 03 03 00 1e 00 00 00 00 00 00 00 01 14 96 ec |................|
+00000010 f4 bb ae 45 81 0c 39 10 e2 3a 91 51 04 2c 01 a8 |...E..9..:.Q.,..|
+00000020 8b a3 25 15 03 03 00 1a 00 00 00 00 00 00 00 02 |..%.............|
+00000030 fe 1a 53 01 17 ad a1 30 0a 73 17 9f 39 b4 30 ac |..S....0.s..9.0.|
+00000040 91 ee |..|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ALPN-NoMatch b/src/crypto/tls/testdata/Client-TLSv12-ALPN-NoMatch
index 57f2f361b8..a22ffaeb49 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-ALPN-NoMatch
+++ b/src/crypto/tls/testdata/Client-TLSv12-ALPN-NoMatch
@@ -1,19 +1,20 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 00 8a 01 00 00 86 03 03 00 00 00 00 00 |................|
+00000000 16 03 01 00 92 01 00 00 8e 03 03 00 00 00 00 00 |................|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 1e c0 2f |.............../|
00000030 c0 2b c0 30 c0 2c c0 11 c0 07 c0 13 c0 09 c0 14 |.+.0.,..........|
-00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 3f |...../.5.......?|
+00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 47 |...../.5.......G|
00000050 33 74 00 00 00 05 00 05 01 00 00 00 00 00 0a 00 |3t..............|
00000060 08 00 06 00 17 00 18 00 19 00 0b 00 02 01 00 00 |................|
-00000070 0d 00 0a 00 08 04 01 04 03 02 01 02 03 ff 01 00 |................|
-00000080 01 00 00 10 00 09 00 07 06 70 72 6f 74 6f 33 |.........proto3|
+00000070 0d 00 0e 00 0c 04 01 04 03 05 01 05 03 02 01 02 |................|
+00000080 03 ff 01 00 01 00 00 10 00 09 00 07 06 70 72 6f |.............pro|
+00000090 74 6f 33 00 12 00 00 |to3....|
>>> Flow 2 (server to client)
-00000000 16 03 03 00 59 02 00 00 55 03 03 f0 ff a6 91 ca |....Y...U.......|
-00000010 e9 d7 bc 31 4c 5e 15 b0 24 41 78 17 87 a8 1c 7d |...1L^..$Ax....}|
-00000020 eb bd 28 f6 57 7f 01 ab b4 02 a7 20 38 08 43 7e |..(.W...... 8.C~|
-00000030 ca 3c 5f ba 62 bb b0 10 30 f3 f2 03 68 ef 01 43 |.<_.b...0...h..C|
-00000040 3b 70 2c 37 80 fe 1c af bc f5 db 60 c0 2f 00 00 |;p,7.......`./..|
+00000000 16 03 03 00 59 02 00 00 55 03 03 94 d7 79 73 82 |....Y...U....ys.|
+00000010 87 7c 85 6e 8a 1b 7d bf 69 c9 98 0c 44 bd f6 78 |.|.n..}.i...D..x|
+00000020 d2 80 dc d8 7d 80 bb 91 4b d4 ed 20 fe 9f 2f 7b |....}...K.. ../{|
+00000030 f2 1a 44 36 cd ce af f1 b5 01 8a ac 18 e4 2b 23 |..D6..........+#|
+00000040 a8 ab 1a 32 23 8b 0b e2 81 a8 0a 40 c0 2f 00 00 |...2#......@./..|
00000050 0d ff 01 00 01 00 00 0b 00 04 03 00 01 02 16 03 |................|
00000060 03 02 be 0b 00 02 ba 00 02 b7 00 02 b4 30 82 02 |.............0..|
00000070 b0 30 82 02 19 a0 03 02 01 02 02 09 00 85 b0 bb |.0..............|
@@ -59,37 +60,37 @@
000002f0 5f 33 c4 b6 d8 c9 75 90 96 8c 0f 52 98 b5 cd 98 |_3....u....R....|
00000300 1f 89 20 5f f2 a0 1c a3 1b 96 94 dd a9 fd 57 e9 |.. _..........W.|
00000310 70 e8 26 6d 71 99 9b 26 6e 38 50 29 6c 90 a7 bd |p.&mq..&n8P)l...|
-00000320 d9 16 03 03 00 cd 0c 00 00 c9 03 00 17 41 04 4e |.............A.N|
-00000330 38 ec 28 ce cb f6 6b 74 96 74 92 46 9a 41 4a 02 |8.(...kt.t.F.AJ.|
-00000340 33 cb f0 d9 24 20 fd e0 d4 8b 24 b2 1f 24 ac 38 |3...$ ....$..$.8|
-00000350 79 cc ec ff 25 c9 30 f6 85 84 51 ee cb 59 8b 0d |y...%.0...Q..Y..|
-00000360 e2 38 3d e0 24 83 84 da ef 67 f5 f7 8a 0a c0 04 |.8=.$....g......|
-00000370 01 00 80 82 72 af cb 74 fb 8c 02 d5 d4 d9 26 04 |....r..t......&.|
-00000380 06 59 64 f0 50 ce cf ed 15 b4 24 95 47 8a c6 17 |.Yd.P.....$.G...|
-00000390 b0 da a4 13 20 88 e9 b8 ef cd b2 f1 35 5a 88 81 |.... .......5Z..|
-000003a0 19 03 ee f4 74 a2 23 27 bc e9 bf f2 06 06 58 f3 |....t.#'......X.|
-000003b0 ef b6 5e de 76 58 8c ec a6 d0 d3 1e 44 ec ac 61 |..^.vX......D..a|
-000003c0 62 91 a6 9e 36 ef 64 e9 a5 2e e8 88 69 30 0f b3 |b...6.d.....i0..|
-000003d0 84 0a b4 d1 3b a5 fe 9e 96 1a ad 7b 8a 24 7e a7 |....;......{.$~.|
-000003e0 af 5b 6d 11 be 1f 2b 7a 5f 62 f7 ae be 2e 99 ec |.[m...+z_b......|
-000003f0 05 b6 7c 16 03 03 00 04 0e 00 00 00 |..|.........|
+00000320 d9 16 03 03 00 cd 0c 00 00 c9 03 00 17 41 04 7d |.............A.}|
+00000330 75 a5 53 0b a5 4d a6 81 e0 df c4 11 c9 b5 31 ba |u.S..M........1.|
+00000340 9f 7b 51 04 57 c6 e0 b9 b0 bc 4f bc 71 74 8a 2e |.{Q.W.....O.qt..|
+00000350 d1 f6 39 36 94 4e c7 d3 a7 1b 2c b5 55 04 71 01 |..96.N....,.U.q.|
+00000360 9e 2b 42 1e 8b a4 40 b2 13 4f 03 1f 51 9e 5c 04 |.+B...@..O..Q.\.|
+00000370 01 00 80 68 05 c7 4a ca df 00 85 2b 53 f7 4f c3 |...h..J....+S.O.|
+00000380 b4 0f e8 f7 b8 30 b7 36 56 65 7b 03 6a 72 f1 aa |.....0.6Ve{.jr..|
+00000390 54 30 90 9e c7 dc fc 03 96 15 70 67 13 12 a4 f4 |T0........pg....|
+000003a0 42 f0 f9 a1 48 c0 44 44 77 0e ea fd cb b5 6e 19 |B...H.DDw.....n.|
+000003b0 89 94 a7 12 67 87 47 19 c3 00 2d c4 9b d4 dc 66 |....g.G...-....f|
+000003c0 fa ca d7 97 79 9b 28 7f 74 d4 37 c0 06 63 d4 9e |....y.(.t.7..c..|
+000003d0 a1 53 16 5a 8e d7 a5 cc 90 4d 63 f9 0c 18 85 7f |.S.Z.....Mc.....|
+000003e0 0e 35 3a 49 73 88 82 51 41 e5 2d 58 aa 38 3e bd |.5:Is..QA.-X.8>.|
+000003f0 3d d8 da 16 03 03 00 04 0e 00 00 00 |=...........|
>>> Flow 3 (client to server)
00000000 16 03 03 00 46 10 00 00 42 41 04 1e 18 37 ef 0d |....F...BA...7..|
00000010 19 51 88 35 75 71 b5 e5 54 5b 12 2e 8f 09 67 fd |.Q.5uq..T[....g.|
00000020 a7 24 20 3e b2 56 1c ce 97 28 5e f8 2b 2d 4f 9e |.$ >.V...(^.+-O.|
00000030 f1 07 9f 6c 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 |...lK[.V.2B.X..I|
00000040 a6 b5 68 1a 41 03 56 6b dc 5a 89 14 03 03 00 01 |..h.A.Vk.Z......|
-00000050 01 16 03 03 00 28 00 00 00 00 00 00 00 00 d4 91 |.....(..........|
-00000060 e7 17 05 14 7a ce cf 0c 3b c1 a6 a7 4a 57 70 9a |....z...;...JWp.|
-00000070 cf 0e ec 59 19 d3 ba 90 97 51 8b 60 8e 03 |...Y.....Q.`..|
+00000050 01 16 03 03 00 28 00 00 00 00 00 00 00 00 a8 da |.....(..........|
+00000060 74 a6 d0 a5 26 86 f3 5f 89 a4 af ac 9c 1a 01 1f |t...&.._........|
+00000070 89 8a 1c fc cf 68 3e a5 a3 20 1a b3 78 af |.....h>.. ..x.|
>>> Flow 4 (server to client)
-00000000 14 03 03 00 01 01 16 03 03 00 28 55 64 22 e3 20 |..........(Ud". |
-00000010 eb 69 63 44 b4 68 89 29 d6 c8 83 d8 6c 30 2f af |.icD.h.)....l0/.|
-00000020 2a 86 b0 ea ce 57 b8 9c 69 9a e3 fe 86 7e 0a bf |*....W..i....~..|
-00000030 08 f1 fe |...|
+00000000 14 03 03 00 01 01 16 03 03 00 28 1a f2 a9 e8 71 |..........(....q|
+00000010 b2 a6 ca 36 4a ea 55 f6 20 03 fd f7 90 c3 af 30 |...6J.U. ......0|
+00000020 d3 29 c3 d7 1b d6 4d 3e 61 55 94 0d 4e 3e 83 1a |.)....M>aU..N>..|
+00000030 97 dd 19 |...|
>>> Flow 5 (client to server)
-00000000 17 03 03 00 1e 00 00 00 00 00 00 00 01 29 39 41 |.............)9A|
-00000010 c4 ff e3 3e 38 bf 06 09 d2 d9 05 84 66 60 58 e8 |...>8.......f`X.|
-00000020 3a 74 f5 15 03 03 00 1a 00 00 00 00 00 00 00 02 |:t..............|
-00000030 b4 1f e4 7b 84 1e 87 57 97 f6 f2 12 df 40 85 fe |...{...W.....@..|
-00000040 d0 d1 |..|
+00000000 17 03 03 00 1e 00 00 00 00 00 00 00 01 94 5b 5e |..............[^|
+00000010 51 a1 52 ee 19 78 78 ef 12 0d 9c 66 bf e2 48 cb |Q.R..xx....f..H.|
+00000020 f6 00 1e 15 03 03 00 1a 00 00 00 00 00 00 00 02 |................|
+00000030 cd 5d 31 58 d9 5a 12 65 5b c6 7e 4e e2 04 e7 1d |.]1X.Z.e[.~N....|
+00000040 b1 4c |.L|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ClientCert-ECDSA-ECDSA b/src/crypto/tls/testdata/Client-TLSv12-ClientCert-ECDSA-ECDSA
index 7871a05ed9..1470ba7a25 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-ClientCert-ECDSA-ECDSA
+++ b/src/crypto/tls/testdata/Client-TLSv12-ClientCert-ECDSA-ECDSA
@@ -1,18 +1,19 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 00 79 01 00 00 75 03 03 00 00 00 00 00 |....y...u.......|
+00000000 16 03 01 00 81 01 00 00 7d 03 03 00 00 00 00 00 |........}.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 1e c0 2f |.............../|
00000030 c0 2b c0 30 c0 2c c0 11 c0 07 c0 13 c0 09 c0 14 |.+.0.,..........|
-00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 2e |...../.5........|
+00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 36 |...../.5.......6|
00000050 00 05 00 05 01 00 00 00 00 00 0a 00 08 00 06 00 |................|
-00000060 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 0a 00 |................|
-00000070 08 04 01 04 03 02 01 02 03 ff 01 00 01 00 |..............|
+00000060 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 0e 00 |................|
+00000070 0c 04 01 04 03 05 01 05 03 02 01 02 03 ff 01 00 |................|
+00000080 01 00 00 12 00 00 |......|
>>> Flow 2 (server to client)
-00000000 16 03 03 00 59 02 00 00 55 03 03 e1 64 a4 cd 65 |....Y...U...d..e|
-00000010 5a 19 5f 07 68 cb af f2 74 76 a2 99 18 e4 9e 00 |Z._.h...tv......|
-00000020 6a 72 6b 84 dd 1c ec cd 64 45 34 20 96 c3 54 88 |jrk.....dE4 ..T.|
-00000030 00 ec aa 32 95 2c ad 08 47 64 fd 2e d4 1f 8e 5e |...2.,..Gd.....^|
-00000040 ec 39 aa ba 6a 3c 8c c7 a6 63 55 8e c0 09 00 00 |.9..j<...cU.....|
+00000000 16 03 03 00 59 02 00 00 55 03 03 a5 28 60 99 bf |....Y...U...(`..|
+00000010 c7 54 04 87 60 ad c5 32 f6 bf ed 11 47 de 4d ff |.T..`..2....G.M.|
+00000020 99 e1 8f 88 f6 af 10 6e 29 74 0a 20 1d 39 cb e0 |.......n)t. .9..|
+00000030 a5 11 fe 8e 23 11 83 c7 a6 53 fc 97 03 9d ff 7c |....#....S.....||
+00000040 cf 51 ba 41 64 61 38 22 5c c6 4a 04 c0 09 00 00 |.Q.Ada8"\.J.....|
00000050 0d ff 01 00 01 00 00 0b 00 04 03 00 01 02 16 03 |................|
00000060 03 02 0e 0b 00 02 0a 00 02 07 00 02 04 30 82 02 |.............0..|
00000070 00 30 82 01 62 02 09 00 b8 bf 2d 47 a0 d2 eb f4 |.0..b.....-G....|
@@ -47,24 +48,23 @@
00000240 13 83 0d 94 06 bb d4 37 7a f6 ec 7a c9 86 2e dd |.......7z..z....|
00000250 d7 11 69 7f 85 7c 56 de fb 31 78 2b e4 c7 78 0d |..i..|V..1x+..x.|
00000260 ae cb be 9e 4e 36 24 31 7b 6a 0f 39 95 12 07 8f |....N6$1{j.9....|
-00000270 2a 16 03 03 00 d8 0c 00 00 d4 03 00 17 41 04 bb |*............A..|
-00000280 6d 5c 62 98 a7 6c bd f1 9b 4b 09 16 31 59 6a 51 |m\b..l...K..1YjQ|
-00000290 83 c8 9f 75 9c f8 09 b0 ee 39 01 e3 7a 25 9d 66 |...u.....9..z%.f|
-000002a0 fe 14 14 15 45 1b 51 a4 47 fe 1e 58 01 28 96 13 |....E.Q.G..X.(..|
-000002b0 2a 0e 0b 40 b2 22 db 2f e6 f4 88 0a 58 92 10 04 |*..@."./....X...|
-000002c0 03 00 8b 30 81 88 02 42 01 13 f5 38 52 04 f7 3b |...0...B...8R..;|
-000002d0 55 96 ef 39 77 be 4f 85 07 18 e9 47 49 b4 bb 57 |U..9w.O....GI..W|
-000002e0 c9 c0 93 2e 9e b2 5e 3f 14 ce 43 f0 93 b5 a4 66 |......^?..C....f|
-000002f0 8c fe 3a 06 fc a7 bb 9d 87 46 b8 20 1f 0a 31 c6 |..:......F. ..1.|
-00000300 80 b0 2d fa e5 06 5f 78 b2 da 02 42 01 c0 bd 12 |..-..._x...B....|
-00000310 5b ec 79 dd bf a4 54 f1 3b a8 b8 9a 50 ac a9 7c |[.y...T.;...P..||
-00000320 d2 a6 b5 dd 84 ee dd eb 3e c7 52 1c 65 ac 1e 37 |........>.R.e..7|
-00000330 4f a8 87 fa 05 8a a4 69 c9 59 53 65 ee 8e 4c 1b |O......i.YSe..L.|
-00000340 6c d0 88 b8 65 de 85 f8 fe f9 27 96 b8 c0 16 03 |l...e.....'.....|
-00000350 03 00 2e 0d 00 00 26 03 01 02 40 00 1e 06 01 06 |......&...@.....|
-00000360 02 06 03 05 01 05 02 05 03 04 01 04 02 04 03 03 |................|
-00000370 01 03 02 03 03 02 01 02 02 02 03 00 00 0e 00 00 |................|
-00000380 00 |.|
+00000270 2a 16 03 03 00 d7 0c 00 00 d3 03 00 17 41 04 c4 |*............A..|
+00000280 0a 40 05 84 eb 90 3c 13 d0 90 af 69 fa 5c 20 75 |.@....<....i.\ u|
+00000290 e1 9b f2 30 f7 df cc 75 2c 35 7e 38 16 99 7d 57 |...0...u,5~8..}W|
+000002a0 6d d7 f0 93 2d 1d c8 03 89 6e 52 3b 20 e5 8a 5f |m...-....nR; .._|
+000002b0 6d ca 6e 6a ca 51 f8 a4 dc 1d ec 3e 73 c9 72 04 |m.nj.Q.....>s.r.|
+000002c0 03 00 8a 30 81 87 02 41 37 bf 0d 1d c1 9a 37 39 |...0...A7.....79|
+000002d0 4d 4a f8 17 50 5d 4c 78 d4 25 99 9d 81 48 98 a8 |MJ..P]Lx.%...H..|
+000002e0 ff 2d 3f 98 4b 9f d8 96 2b fa 37 cc e8 66 25 0e |.-?.K...+.7..f%.|
+000002f0 d3 5e 53 c5 3b ad 17 3f 21 ce d2 45 d8 93 95 6c |.^S.;..?!..E...l|
+00000300 25 f9 5a 10 9f 37 c8 14 a6 02 42 00 e6 bd 9a 89 |%.Z..7....B.....|
+00000310 8e 73 40 f4 90 e6 d8 e2 98 51 10 23 fb 98 e5 47 |.s@......Q.#...G|
+00000320 0c 2a 7a 2f 02 66 a8 20 e4 cb 4f ba 14 1d 9e 3a |.*z/.f. ..O....:|
+00000330 2f 09 47 44 02 e0 9f 30 21 71 f0 99 09 de 23 d2 |/.GD...0!q....#.|
+00000340 f5 f0 b2 93 70 a3 8f 79 b9 4f 88 0b 35 16 03 03 |....p..y.O..5...|
+00000350 00 2e 0d 00 00 26 03 01 02 40 00 1e 06 01 06 02 |.....&...@......|
+00000360 06 03 05 01 05 02 05 03 04 01 04 02 04 03 03 01 |................|
+00000370 03 02 03 03 02 01 02 02 02 03 00 00 0e 00 00 00 |................|
>>> Flow 3 (client to server)
00000000 16 03 03 02 0a 0b 00 02 06 00 02 03 00 02 00 30 |...............0|
00000010 82 01 fc 30 82 01 5e 02 09 00 9a 30 84 6c 26 35 |...0..^....0.l&5|
@@ -103,32 +103,32 @@
00000220 51 88 35 75 71 b5 e5 54 5b 12 2e 8f 09 67 fd a7 |Q.5uq..T[....g..|
00000230 24 20 3e b2 56 1c ce 97 28 5e f8 2b 2d 4f 9e f1 |$ >.V...(^.+-O..|
00000240 07 9f 6c 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 a6 |..lK[.V.2B.X..I.|
-00000250 b5 68 1a 41 03 56 6b dc 5a 89 16 03 03 00 93 0f |.h.A.Vk.Z.......|
-00000260 00 00 8f 04 03 00 8b 30 81 88 02 42 00 e9 db 80 |.......0...B....|
-00000270 e2 67 5d 00 21 88 67 99 7f df de 90 77 86 1e b7 |.g].!.g.....w...|
-00000280 28 b1 2d 08 8d 02 de 9a 29 2b ca b9 9c 48 ad bd |(.-.....)+...H..|
-00000290 58 16 68 ad a3 0f 08 4c 01 52 e7 54 97 7c 06 0a |X.h....L.R.T.|..|
-000002a0 9e c8 97 61 e6 a9 53 62 fb b1 e3 b1 d7 03 02 42 |...a..Sb.......B|
-000002b0 01 f3 99 af dc e6 69 af 1d fb d5 d0 63 bd d1 17 |......i.....c...|
-000002c0 d2 ca a5 10 97 1a 94 93 df c4 94 27 53 77 1a 9e |...........'Sw..|
-000002d0 9b a5 e6 dd 0d cf 49 46 4c 5b 83 a4 52 f2 8b d6 |......IFL[..R...|
-000002e0 b2 5f 40 e5 c3 d6 7f a2 2c 50 4d 4c 81 54 80 5b |._@.....,PML.T.[|
-000002f0 72 c7 14 03 03 00 01 01 16 03 03 00 40 00 00 00 |r...........@...|
-00000300 00 00 00 00 00 00 00 00 00 00 00 00 00 96 9f 5a |...............Z|
-00000310 9c e3 d0 6c 5f 11 c4 cf e4 34 1a 54 7e dc ec 1d |...l_....4.T~...|
-00000320 cd 08 eb 5c b4 32 1b d0 e5 12 1f 7a e7 86 16 56 |...\.2.....z...V|
-00000330 a7 10 20 e1 59 31 65 63 12 7d 45 2d 2a |.. .Y1ec.}E-*|
+00000250 b5 68 1a 41 03 56 6b dc 5a 89 16 03 03 00 92 0f |.h.A.Vk.Z.......|
+00000260 00 00 8e 05 03 00 8a 30 81 87 02 42 00 cc a4 ad |.......0...B....|
+00000270 0b ff 09 40 8f 2c a6 37 72 1d f7 d2 19 74 85 ad |...@.,.7r....t..|
+00000280 ac 33 b0 b8 5b 56 39 cf b0 ef 46 68 94 39 4c d0 |.3..[V9...Fh.9L.|
+00000290 f4 97 32 10 99 36 c5 95 c8 14 23 37 78 46 5c a9 |..2..6....#7xF\.|
+000002a0 20 95 65 47 ff 54 02 f1 aa 1d d7 bc 39 2d 02 41 | .eG.T......9-.A|
+000002b0 2e f9 d6 8c e8 c5 a9 6f 10 4f d6 5f 4e 88 e9 71 |.......o.O._N..q|
+000002c0 23 5b 6f b8 ab 19 d3 dd ec f3 32 e3 3b fa 41 a2 |#[o.......2.;.A.|
+000002d0 e8 ae dc 27 8d 4e 79 f4 47 ef c9 8f bf 0b 41 3b |...'.Ny.G.....A;|
+000002e0 94 16 cb 8f 1e b5 f3 4e 6e 42 46 35 1a 0c ca 79 |.......NnBF5...y|
+000002f0 4b 14 03 03 00 01 01 16 03 03 00 40 00 00 00 00 |K..........@....|
+00000300 00 00 00 00 00 00 00 00 00 00 00 00 64 1c d9 9f |............d...|
+00000310 34 ec c2 74 76 7a 9f cf 95 19 be 8d 6a 2f 25 96 |4..tvz......j/%.|
+00000320 df de 18 ca 0e c9 d4 2f e4 b0 34 10 5b 72 7a 18 |......./..4.[rz.|
+00000330 5c 64 d7 fc 2e 1b 28 10 ae a6 31 e9 |\d....(...1.|
>>> Flow 4 (server to client)
-00000000 14 03 03 00 01 01 16 03 03 00 40 54 bd b1 39 e6 |..........@T..9.|
-00000010 a7 d0 76 5e 7e 91 0d 81 d1 c6 82 05 79 90 24 fc |..v^~.......y.$.|
-00000020 26 b7 ec e6 b8 72 05 59 bd 00 99 f7 dd f4 44 1e |&....r.Y......D.|
-00000030 79 4d 6d a1 22 4a e3 2c 41 05 ec 5a f7 32 17 ff |yMm."J.,A..Z.2..|
-00000040 d3 1b ee 21 71 98 99 b7 85 34 b3 |...!q....4.|
+00000000 14 03 03 00 01 01 16 03 03 00 40 27 6f 24 a3 0c |..........@'o$..|
+00000010 6d d7 68 4a fb 43 b0 97 02 6c 22 7e 2f a1 f1 7a |m.hJ.C...l"~/..z|
+00000020 37 bf 38 82 dc a0 83 24 01 4b c0 4f 15 e1 7c 4c |7.8....$.K.O..|L|
+00000030 d4 cd b8 e2 71 af f5 20 7d f9 4a 48 4b f0 a1 f3 |....q.. }.JHK...|
+00000040 7b 02 29 18 c0 87 a5 dd c4 73 8e |{.)......s.|
>>> Flow 5 (client to server)
00000000 17 03 03 00 30 00 00 00 00 00 00 00 00 00 00 00 |....0...........|
-00000010 00 00 00 00 00 b9 81 3f 48 14 95 9b 39 85 2a 9e |.......?H...9.*.|
-00000020 44 ec bb cf c2 29 a9 44 f7 8a 6b 3f 92 13 dd 0e |D....).D..k?....|
-00000030 c6 6b a1 51 79 15 03 03 00 30 00 00 00 00 00 00 |.k.Qy....0......|
-00000040 00 00 00 00 00 00 00 00 00 00 1c 93 91 23 12 11 |.............#..|
-00000050 cc 30 fb 22 9e 23 b7 60 8a 3d 4c e6 52 2b 3e 6b |.0.".#.`.=L.R+>k|
-00000060 8e 47 91 b1 68 50 07 8a d1 6f |.G..hP...o|
+00000010 00 00 00 00 00 bf 7a e1 23 0d d0 13 6e 96 81 6d |......z.#...n..m|
+00000020 32 56 0f 75 7e 01 88 5f 6d e6 d6 ca ec 3c 17 e9 |2V.u~.._m....<..|
+00000030 44 a9 c0 1c a4 15 03 03 00 30 00 00 00 00 00 00 |D........0......|
+00000040 00 00 00 00 00 00 00 00 00 00 76 be 7a 77 29 01 |..........v.zw).|
+00000050 8e 13 02 66 81 43 a0 55 03 35 22 09 de ea 52 bb |...f.C.U.5"...R.|
+00000060 51 cc c1 09 0e 9b 4d bd 94 85 |Q.....M...|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ClientCert-ECDSA-RSA b/src/crypto/tls/testdata/Client-TLSv12-ClientCert-ECDSA-RSA
index 24c1b9acdd..95c5782ab0 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-ClientCert-ECDSA-RSA
+++ b/src/crypto/tls/testdata/Client-TLSv12-ClientCert-ECDSA-RSA
@@ -1,18 +1,19 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 00 79 01 00 00 75 03 03 00 00 00 00 00 |....y...u.......|
+00000000 16 03 01 00 81 01 00 00 7d 03 03 00 00 00 00 00 |........}.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 1e c0 2f |.............../|
00000030 c0 2b c0 30 c0 2c c0 11 c0 07 c0 13 c0 09 c0 14 |.+.0.,..........|
-00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 2e |...../.5........|
+00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 36 |...../.5.......6|
00000050 00 05 00 05 01 00 00 00 00 00 0a 00 08 00 06 00 |................|
-00000060 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 0a 00 |................|
-00000070 08 04 01 04 03 02 01 02 03 ff 01 00 01 00 |..............|
+00000060 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 0e 00 |................|
+00000070 0c 04 01 04 03 05 01 05 03 02 01 02 03 ff 01 00 |................|
+00000080 01 00 00 12 00 00 |......|
>>> Flow 2 (server to client)
-00000000 16 03 03 00 51 02 00 00 4d 03 03 7a 6c c2 d1 69 |....Q...M..zl..i|
-00000010 af 86 6f 03 3c e4 70 ae 03 39 fd c6 3a a9 a4 b2 |..o.<.p..9..:...|
-00000020 96 1e 50 f2 f4 16 50 e4 a2 f2 41 20 f8 83 3b 45 |..P...P...A ..;E|
-00000030 0d 5b 88 bc 87 6c 81 23 e3 1d e0 7e 22 f5 6d 95 |.[...l.#...~".m.|
-00000040 58 63 39 cf 4f 80 80 cc 41 bb b1 4c 00 05 00 00 |Xc9.O...A..L....|
+00000000 16 03 03 00 51 02 00 00 4d 03 03 79 e8 35 e3 d2 |....Q...M..y.5..|
+00000010 c0 5e 39 d1 46 da 9c 94 56 20 e2 06 d6 9b f6 dd |.^9.F...V ......|
+00000020 4f 7a c1 e8 34 a1 9f 8b c2 e1 fb 20 66 9c 5a 9a |Oz..4...... f.Z.|
+00000030 3d 22 ab 8e d8 81 03 94 68 a0 6c 72 d8 23 0b 4b |="......h.lr.#.K|
+00000040 fe 9d c7 49 a7 7c bd fa b5 7a 5e 5b 00 05 00 00 |...I.|...z^[....|
00000050 05 ff 01 00 01 00 16 03 03 02 be 0b 00 02 ba 00 |................|
00000060 02 b7 00 02 b4 30 82 02 b0 30 82 02 19 a0 03 02 |.....0...0......|
00000070 01 02 02 09 00 85 b0 bb a4 8a 7f b8 ca 30 0d 06 |.............0..|
@@ -103,26 +104,25 @@
00000260 ce 39 4c 9c 86 00 08 c2 4b e2 c6 ec 2f f7 ce e6 |.9L.....K.../...|
00000270 bd 77 82 6f 23 b6 e0 bd a2 92 b7 3a ac e8 56 f1 |.w.o#......:..V.|
00000280 af 54 5e 46 87 e9 3b 33 e7 b8 28 b7 d6 c8 90 35 |.T^F..;3..(....5|
-00000290 d4 1c 43 d1 30 6f 55 4e 0a 70 16 03 03 00 93 0f |..C.0oUN.p......|
-000002a0 00 00 8f 04 03 00 8b 30 81 88 02 42 00 87 a3 50 |.......0...B...P|
-000002b0 77 2a 46 97 68 1e ca 47 d2 46 a3 f7 37 e7 1c 3c |w*F.h..G.F..7..<|
-000002c0 e3 16 dc b9 93 b9 76 af da 46 b1 da 47 bc 8b 9c |......v..F..G...|
-000002d0 ff 61 76 45 2b cf a6 85 4a 45 d4 51 98 18 31 c5 |.avE+...JE.Q..1.|
-000002e0 61 54 3b ae 88 ca 56 ac 90 29 de f2 20 a6 02 42 |aT;...V..).. ..B|
-000002f0 01 0d 54 c6 a5 14 c2 c0 83 5d ee 32 d3 c6 05 d7 |..T......].2....|
-00000300 0c 40 42 ca 8a 69 5e cc 9b f5 c4 9b 7c 81 e9 b7 |.@B..i^.....|...|
-00000310 dd 01 c0 e5 93 de 75 d2 6b 26 dd 16 2a ec d0 0e |......u.k&..*...|
-00000320 50 76 ee 36 ac 42 a3 0b 64 dd 4d 47 18 3e 5c 18 |Pv.6.B..d.MG.>\.|
-00000330 16 3b 14 03 03 00 01 01 16 03 03 00 24 c2 c7 3a |.;..........$..:|
-00000340 a2 9b 93 ea 75 1c b6 47 60 2e 15 cf b8 63 73 8a |....u..G`....cs.|
-00000350 2c b8 86 a8 12 1d cb 30 e2 38 fe 0f 02 57 43 f0 |,......0.8...WC.|
-00000360 07 |.|
+00000290 d4 1c 43 d1 30 6f 55 4e 0a 70 16 03 03 00 92 0f |..C.0oUN.p......|
+000002a0 00 00 8e 05 03 00 8a 30 81 87 02 41 19 c7 50 06 |.......0...A..P.|
+000002b0 42 82 f9 e5 ec 0b f7 65 7e b1 19 53 5f 23 ab 19 |B......e~..S_#..|
+000002c0 54 08 ec d2 a7 22 dd 83 7c 97 76 59 a5 6b f4 1d |T...."..|.vY.k..|
+000002d0 92 86 34 2d ce 71 bb 01 d2 8a 67 0e a8 fb 51 e4 |..4-.q....g...Q.|
+000002e0 69 9c 27 23 74 b9 fd 6f b6 5e 48 a0 cc 02 42 01 |i.'#t..o.^H...B.|
+000002f0 50 97 b7 95 14 f4 a6 f2 95 63 17 38 59 a1 51 95 |P........c.8Y.Q.|
+00000300 1e bc 99 fb fd 82 8b ab cb 4d 8e 17 a9 f8 e9 c2 |.........M......|
+00000310 9b 93 15 02 50 e6 c2 05 54 e7 8a ec 6f 93 1f 79 |....P...T...o..y|
+00000320 8d 67 e7 2d d6 65 ab 97 fd be 20 97 bd 6b c4 fc |.g.-.e.... ..k..|
+00000330 02 14 03 03 00 01 01 16 03 03 00 24 24 df 52 6e |...........$$.Rn|
+00000340 c1 35 48 fe 60 77 28 69 36 fe 96 a1 72 db a2 f5 |.5H.`w(i6...r...|
+00000350 d0 b7 c3 d9 67 e5 ee f2 d9 18 bf f0 35 80 06 c2 |....g.......5...|
>>> Flow 4 (server to client)
-00000000 14 03 03 00 01 01 16 03 03 00 24 ca e7 84 5c 1b |..........$...\.|
-00000010 94 4c d8 78 6f 3f 80 b2 f9 9f fd c5 a5 fd 6f 89 |.L.xo?........o.|
-00000020 d7 50 a1 81 bf d0 9d eb 75 10 69 97 35 74 06 |.P......u.i.5t.|
+00000000 14 03 03 00 01 01 16 03 03 00 24 40 5b dc 01 59 |..........$@[..Y|
+00000010 33 6e 61 5a 6d fc c8 a5 f5 00 9b 55 77 c5 e6 f2 |3naZm......Uw...|
+00000020 c6 5c b6 2f 94 3c 72 5b b5 0c 3e 78 88 e6 44 |.\./.x..D|
>>> Flow 5 (client to server)
-00000000 17 03 03 00 1a 4a 11 45 18 75 a7 47 d3 36 ad 24 |.....J.E.u.G.6.$|
-00000010 fc d0 68 44 f2 9a 05 54 a2 44 e3 a7 33 74 99 15 |..hD...T.D..3t..|
-00000020 03 03 00 16 d5 d5 75 a9 a9 ef f5 31 50 f7 00 08 |......u....1P...|
-00000030 78 0a 00 1f c8 42 db c7 15 6b |x....B...k|
+00000000 17 03 03 00 1a cd 2f 11 b1 3a e4 1c 31 95 9b c4 |....../..:..1...|
+00000010 37 20 9f 03 d3 45 a4 15 e1 09 1e 0c f6 5d d3 15 |7 ...E.......]..|
+00000020 03 03 00 16 d7 f6 a1 d0 ad 41 69 73 c0 40 22 f2 |.........Ais.@".|
+00000030 5f e8 c3 50 f9 35 fc 59 e0 3a |_..P.5.Y.:|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-AES256-GCM-SHA384 b/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-AES256-GCM-SHA384
index e7ca34c774..52e3befe61 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-AES256-GCM-SHA384
+++ b/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-AES256-GCM-SHA384
@@ -1,18 +1,19 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 00 79 01 00 00 75 03 03 00 00 00 00 00 |....y...u.......|
+00000000 16 03 01 00 81 01 00 00 7d 03 03 00 00 00 00 00 |........}.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 1e c0 2f |.............../|
00000030 c0 2b c0 30 c0 2c c0 11 c0 07 c0 13 c0 09 c0 14 |.+.0.,..........|
-00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 2e |...../.5........|
+00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 36 |...../.5.......6|
00000050 00 05 00 05 01 00 00 00 00 00 0a 00 08 00 06 00 |................|
-00000060 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 0a 00 |................|
-00000070 08 04 01 04 03 02 01 02 03 ff 01 00 01 00 |..............|
+00000060 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 0e 00 |................|
+00000070 0c 04 01 04 03 05 01 05 03 02 01 02 03 ff 01 00 |................|
+00000080 01 00 00 12 00 00 |......|
>>> Flow 2 (server to client)
-00000000 16 03 03 00 59 02 00 00 55 03 03 2d 12 aa 2a 67 |....Y...U..-..*g|
-00000010 e1 6c 55 dc 1c 0b 3f 94 39 7a 2f e3 4e d4 85 cb |.lU...?.9z/.N...|
-00000020 31 ff da 09 dd e0 92 75 6c e8 0b 20 e1 e7 fc 09 |1......ul.. ....|
-00000030 bd 12 b8 5c b2 54 75 01 7a f5 65 95 d7 87 66 77 |...\.Tu.z.e...fw|
-00000040 03 1f 25 23 cb 39 9f 47 2b 5c fd bd c0 30 00 00 |..%#.9.G+\...0..|
+00000000 16 03 03 00 59 02 00 00 55 03 03 74 fe 19 af 4b |....Y...U..t...K|
+00000010 f3 d8 92 62 5a df 90 2c cc 09 fd 79 45 26 cd 52 |...bZ..,...yE&.R|
+00000020 9a e6 da 16 99 fe 1d 91 79 a7 a0 20 b3 13 e9 03 |........y.. ....|
+00000030 52 23 5f f0 55 59 f1 9e 00 a7 77 97 90 ed 2b fb |R#_.UY....w...+.|
+00000040 9c ab fe b1 db ea 16 95 95 68 b0 e9 c0 30 00 00 |.........h...0..|
00000050 0d ff 01 00 01 00 00 0b 00 04 03 00 01 02 16 03 |................|
00000060 03 02 be 0b 00 02 ba 00 02 b7 00 02 b4 30 82 02 |.............0..|
00000070 b0 30 82 02 19 a0 03 02 01 02 02 09 00 85 b0 bb |.0..............|
@@ -58,20 +59,20 @@
000002f0 5f 33 c4 b6 d8 c9 75 90 96 8c 0f 52 98 b5 cd 98 |_3....u....R....|
00000300 1f 89 20 5f f2 a0 1c a3 1b 96 94 dd a9 fd 57 e9 |.. _..........W.|
00000310 70 e8 26 6d 71 99 9b 26 6e 38 50 29 6c 90 a7 bd |p.&mq..&n8P)l...|
-00000320 d9 16 03 03 00 cd 0c 00 00 c9 03 00 17 41 04 99 |.............A..|
-00000330 91 58 07 9b 2b 79 26 ad cb 37 07 5e f3 e3 75 81 |.X..+y&..7.^..u.|
-00000340 32 50 39 59 a4 7c c0 b8 c2 f4 16 de dc c3 9f ba |2P9Y.|..........|
-00000350 04 42 a4 15 9c 8f 4e da 35 88 fc e5 b1 03 70 85 |.B....N.5.....p.|
-00000360 64 a3 6e 59 15 8c 92 11 4c 10 d9 90 f4 a9 9b 04 |d.nY....L.......|
-00000370 01 00 80 4e d1 02 4d d0 a7 7d 01 42 7a b6 75 ed |...N..M..}.Bz.u.|
-00000380 ea 10 a3 66 a2 35 94 2d 2d 7a 32 55 63 23 df 8c |...f.5.--z2Uc#..|
-00000390 9e ec d2 19 df bb e0 02 70 c0 50 4f 05 d8 ec 1c |........p.PO....|
-000003a0 40 a1 a5 ae 2c 80 5b 6f b1 f9 f9 74 20 dc 4f d7 |@...,.[o...t .O.|
-000003b0 23 b3 25 61 a7 5e 76 37 a7 17 f3 54 47 08 d9 2c |#.%a.^v7...TG..,|
-000003c0 fb ea 4f 56 51 ee 5c cc 2f 4d 80 66 7b 21 78 1d |..OVQ.\./M.f{!x.|
-000003d0 ef a0 71 96 cc 3d 09 8e 37 fd bc 9f 26 be 75 48 |..q..=..7...&.uH|
-000003e0 b2 a1 39 0e b3 d3 73 f5 f1 68 4f aa 03 92 c0 1f |..9...s..hO.....|
-000003f0 90 74 a9 16 03 03 00 2e 0d 00 00 26 03 01 02 40 |.t.........&...@|
+00000320 d9 16 03 03 00 cd 0c 00 00 c9 03 00 17 41 04 22 |.............A."|
+00000330 84 e9 5e 6e 35 92 a3 83 73 0a d6 0d 1a c1 4d ab |..^n5...s.....M.|
+00000340 1f ab c2 dc 3f 53 a5 7d 38 c0 92 a4 82 e1 3c c5 |....?S.}8.....<.|
+00000350 24 60 20 a5 3b c5 65 ba 9c f1 b9 a5 b9 c2 70 73 |$` .;.e.......ps|
+00000360 19 74 a6 d1 0f 75 b4 75 e0 e8 60 20 e9 23 fe 04 |.t...u.u..` .#..|
+00000370 01 00 80 92 e0 56 3f 48 0d 10 23 48 b5 95 b6 91 |.....V?H..#H....|
+00000380 3e 8a 2e c7 02 e2 85 0e 59 c8 03 24 d9 1a 1a 25 |>.......Y..$...%|
+00000390 8e 12 bb 0b 83 ac 51 36 81 3f bc 0e be b9 3b 1d |......Q6.?....;.|
+000003a0 67 56 21 4d 24 36 84 05 61 e7 70 60 d5 8e ae 97 |gV!M$6..a.p`....|
+000003b0 b8 3a d3 b1 94 72 52 cd b0 0d dd 46 b1 15 3b 58 |.:...rR....F..;X|
+000003c0 c1 a4 63 2c 4c 31 f9 c7 4f 27 c1 0f f0 24 36 72 |..c,L1..O'...$6r|
+000003d0 e0 f8 51 12 86 c2 13 ed 6b 84 a8 15 c3 d0 39 55 |..Q.....k.....9U|
+000003e0 a4 60 50 88 c9 1e 60 60 aa 8d a5 31 3e 35 c3 f8 |.`P...``...1>5..|
+000003f0 2c 90 1c 16 03 03 00 2e 0d 00 00 26 03 01 02 40 |,..........&...@|
00000400 00 1e 06 01 06 02 06 03 05 01 05 02 05 03 04 01 |................|
00000410 04 02 04 03 03 01 03 02 03 03 02 01 02 02 02 03 |................|
00000420 00 00 0e 00 00 00 |......|
@@ -113,26 +114,26 @@
00000220 a7 24 20 3e b2 56 1c ce 97 28 5e f8 2b 2d 4f 9e |.$ >.V...(^.+-O.|
00000230 f1 07 9f 6c 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 |...lK[.V.2B.X..I|
00000240 a6 b5 68 1a 41 03 56 6b dc 5a 89 16 03 03 00 88 |..h.A.Vk.Z......|
-00000250 0f 00 00 84 05 01 00 80 2c 1c b4 c4 d6 73 62 3a |........,....sb:|
-00000260 86 37 c5 cb 3d 28 5f 3b 7f e2 08 f8 38 ef dc c4 |.7..=(_;....8...|
-00000270 a9 13 b6 82 28 0a 3a 67 48 01 c7 54 1d 4f b4 b4 |....(.:gH..T.O..|
-00000280 4e a8 5b fc b6 9b 27 7c e3 a6 d7 88 62 2d 2c ca |N.[...'|....b-,.|
-00000290 35 55 b3 99 ac 4e 28 45 55 29 3e 30 fc 46 6b 86 |5U...N(EU)>0.Fk.|
-000002a0 20 0b b1 d0 7c c5 07 cd d8 49 5a 88 dd c2 bc 5d | ...|....IZ....]|
-000002b0 5f ad 52 d8 be 56 e4 fd f8 ab ef 17 04 08 50 a5 |_.R..V........P.|
-000002c0 2f 52 58 e5 31 51 e4 83 44 41 c0 8e 16 cf 39 4f |/RX.1Q..DA....9O|
-000002d0 3d de c8 19 1e 5c c3 a7 14 03 03 00 01 01 16 03 |=....\..........|
-000002e0 03 00 28 00 00 00 00 00 00 00 00 2c e0 65 72 59 |..(........,.erY|
-000002f0 1e 0a ff 8b 58 74 14 c8 c5 fa db 08 06 4f a1 d4 |....Xt.......O..|
-00000300 20 cc f4 3e 6a f5 5c 0f 8e 26 1d | ..>j.\..&.|
+00000250 0f 00 00 84 05 01 00 80 33 bb 8e 67 64 03 e7 7e |........3..gd..~|
+00000260 be a5 b1 bc cf 7a 07 24 01 17 c3 3d 4b 72 dd c3 |.....z.$...=Kr..|
+00000270 64 a7 36 e8 49 ab b6 87 ce d6 af 9e 07 22 76 e8 |d.6.I........"v.|
+00000280 0d 44 a3 36 c9 eb a4 49 85 cf 72 67 e8 2a a7 5b |.D.6...I..rg.*.[|
+00000290 d3 f2 46 af 53 48 c6 13 f7 0b 5b 9c c7 4d 3e 05 |..F.SH....[..M>.|
+000002a0 3c 0f 69 a7 40 3a e8 70 04 01 1c 29 b2 42 0f 5f |<.i.@:.p...).B._|
+000002b0 1c d5 b7 5c c2 17 07 7f bd a2 b3 9a 95 81 51 24 |...\..........Q$|
+000002c0 54 5c 42 d6 a4 76 c0 d7 54 d2 11 54 bf fd dc a0 |T\B..v..T..T....|
+000002d0 ee 95 26 64 59 a0 fc 51 14 03 03 00 01 01 16 03 |..&dY..Q........|
+000002e0 03 00 28 00 00 00 00 00 00 00 00 af f4 8a be d9 |..(.............|
+000002f0 ff f1 44 e4 41 ab 9b b3 d8 b0 3d 3f 6b c5 1d b6 |..D.A.....=?k...|
+00000300 1d 9e 35 f5 20 f4 2a af e8 35 77 |..5. .*..5w|
>>> Flow 4 (server to client)
-00000000 14 03 03 00 01 01 16 03 03 00 28 59 f7 e1 f5 7c |..........(Y...||
-00000010 ef 54 7c ee 08 29 50 82 d2 43 32 f5 c1 bc af 0c |.T|..)P..C2.....|
-00000020 5f 4f 6e 9a fd 65 8c 4d ef c4 0e ec 6a ea 46 73 |_On..e.M....j.Fs|
-00000030 e2 9f 4a |..J|
+00000000 14 03 03 00 01 01 16 03 03 00 28 ff 0d 47 63 2b |..........(..Gc+|
+00000010 bd 00 3a ad 82 e3 a7 b3 b0 84 4a 26 f4 30 78 20 |..:.......J&.0x |
+00000020 80 f2 2b 15 98 61 1c cb 8b 17 67 8a 11 96 aa 93 |..+..a....g.....|
+00000030 68 f7 fb |h..|
>>> Flow 5 (client to server)
-00000000 17 03 03 00 1e 00 00 00 00 00 00 00 01 4d b2 6d |.............M.m|
-00000010 73 75 d3 68 3d a5 7c 98 32 3f b2 4a 47 3f b2 95 |su.h=.|.2?.JG?..|
-00000020 8f cd 99 15 03 03 00 1a 00 00 00 00 00 00 00 02 |................|
-00000030 91 31 70 57 68 0a e1 e1 1b ca f0 62 ab 22 da 3d |.1pWh......b.".=|
-00000040 e1 64 |.d|
+00000000 17 03 03 00 1e 00 00 00 00 00 00 00 01 a6 8d 7b |...............{|
+00000010 99 5e a2 e1 95 bb 5f e4 01 f4 0e 20 52 b4 64 4e |.^...._.... R.dN|
+00000020 86 b1 3f 15 03 03 00 1a 00 00 00 00 00 00 00 02 |..?.............|
+00000030 61 98 eb d0 7c ac bd 00 ac 7a e1 32 20 3e 81 b6 |a...|....z.2 >..|
+00000040 9d d5 |..|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-ECDSA b/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-ECDSA
index e77002665f..23bf29d776 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-ECDSA
+++ b/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-ECDSA
@@ -1,18 +1,19 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 00 79 01 00 00 75 03 03 00 00 00 00 00 |....y...u.......|
+00000000 16 03 01 00 81 01 00 00 7d 03 03 00 00 00 00 00 |........}.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 1e c0 2f |.............../|
00000030 c0 2b c0 30 c0 2c c0 11 c0 07 c0 13 c0 09 c0 14 |.+.0.,..........|
-00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 2e |...../.5........|
+00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 36 |...../.5.......6|
00000050 00 05 00 05 01 00 00 00 00 00 0a 00 08 00 06 00 |................|
-00000060 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 0a 00 |................|
-00000070 08 04 01 04 03 02 01 02 03 ff 01 00 01 00 |..............|
+00000060 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 0e 00 |................|
+00000070 0c 04 01 04 03 05 01 05 03 02 01 02 03 ff 01 00 |................|
+00000080 01 00 00 12 00 00 |......|
>>> Flow 2 (server to client)
-00000000 16 03 03 00 59 02 00 00 55 03 03 ef cd 72 a3 35 |....Y...U....r.5|
-00000010 e7 11 9f 67 a4 42 9e 34 03 b4 ab e1 0d 4f a4 09 |...g.B.4.....O..|
-00000020 4e e1 8d 52 d2 d0 0e 0e f0 7a 74 20 da 3f 9c d8 |N..R.....zt .?..|
-00000030 e3 c6 5c a1 e8 5e a0 48 50 e8 70 aa 96 a7 84 4a |..\..^.HP.p....J|
-00000040 3a b3 c3 21 24 30 6c 7a d5 b4 9b 9c c0 09 00 00 |:..!$0lz........|
+00000000 16 03 03 00 59 02 00 00 55 03 03 b3 7f 4e e7 11 |....Y...U....N..|
+00000010 6d bc 56 ec 9c a8 61 08 d6 5a 2a 42 7b f1 94 0a |m.V...a..Z*B{...|
+00000020 29 35 8b 7e 23 a0 6c 59 23 cf 39 20 84 09 b6 5b |)5.~#.lY#.9 ...[|
+00000030 2f 46 80 3b 26 92 fd 81 e9 24 8c e2 b8 64 a2 03 |/F.;&....$...d..|
+00000040 3a 68 c3 7b 44 f8 28 41 e2 d3 6c 7c c0 09 00 00 |:h.{D.(A..l|....|
00000050 0d ff 01 00 01 00 00 0b 00 04 03 00 01 02 16 03 |................|
00000060 03 02 0e 0b 00 02 0a 00 02 07 00 02 04 30 82 02 |.............0..|
00000070 00 30 82 01 62 02 09 00 b8 bf 2d 47 a0 d2 eb f4 |.0..b.....-G....|
@@ -47,24 +48,23 @@
00000240 13 83 0d 94 06 bb d4 37 7a f6 ec 7a c9 86 2e dd |.......7z..z....|
00000250 d7 11 69 7f 85 7c 56 de fb 31 78 2b e4 c7 78 0d |..i..|V..1x+..x.|
00000260 ae cb be 9e 4e 36 24 31 7b 6a 0f 39 95 12 07 8f |....N6$1{j.9....|
-00000270 2a 16 03 03 00 d8 0c 00 00 d4 03 00 17 41 04 7e |*............A.~|
-00000280 3b ae 99 3d b7 3b da 7a 44 b1 b3 0d 41 36 c5 47 |;..=.;.zD...A6.G|
-00000290 b2 b9 65 44 79 2a c4 a9 e3 a8 ee 6a 77 3b ee d8 |..eDy*.....jw;..|
-000002a0 ee 11 0a 20 61 9b be 03 54 29 63 b3 fb 91 6f 34 |... a...T)c...o4|
-000002b0 cb ad 6c 5e 00 5f 0a c7 fd 70 d4 d6 de 5a 00 04 |..l^._...p...Z..|
-000002c0 03 00 8b 30 81 88 02 42 00 c2 21 72 c5 61 07 2f |...0...B..!r.a./|
-000002d0 0e af fd d5 22 43 e5 2e 06 51 29 73 c2 ec 50 34 |...."C...Q)s..P4|
-000002e0 76 ab 67 fe 37 49 68 54 4b 16 d2 7a 4c 04 02 b2 |v.g.7IhTK..zL...|
-000002f0 0a 66 28 fb b5 bf 5b 00 4b dc bf e2 9e 99 a7 0c |.f(...[.K.......|
-00000300 7c 64 36 79 d6 4e 99 70 5f 97 02 42 01 bb 4b 10 ||d6y.N.p_..B..K.|
-00000310 36 f1 38 c1 42 de e9 68 41 2d 0a 4b 19 eb 3c 6b |6.8.B..hA-.K.. ?......Jj.Ew|
-00000330 8c 8d f4 01 d3 15 91 3e 64 22 16 bd b5 2a 07 52 |.......>d"...*.R|
-00000340 63 e5 de 0c 22 90 2e 2f e9 b4 3f ab b8 27 16 03 |c..."../..?..'..|
-00000350 03 00 2e 0d 00 00 26 03 01 02 40 00 1e 06 01 06 |......&...@.....|
-00000360 02 06 03 05 01 05 02 05 03 04 01 04 02 04 03 03 |................|
-00000370 01 03 02 03 03 02 01 02 02 02 03 00 00 0e 00 00 |................|
-00000380 00 |.|
+00000270 2a 16 03 03 00 d7 0c 00 00 d3 03 00 17 41 04 0f |*............A..|
+00000280 4d b0 41 d4 dc 6b 8a 85 52 eb eb 18 4a 8f a7 e6 |M.A..k..R...J...|
+00000290 24 52 e5 86 be 57 d7 0a e7 23 84 a8 a9 6c 96 08 |$R...W...#...l..|
+000002a0 4b f7 47 32 79 d9 df 81 f6 05 40 63 3b 14 67 3b |K.G2y.....@c;.g;|
+000002b0 ea 01 a0 0d 43 1a 36 29 b3 51 7a e4 af 1b 67 04 |....C.6).Qz...g.|
+000002c0 03 00 8a 30 81 87 02 42 01 8e 57 8a b8 b7 5b 2f |...0...B..W...[/|
+000002d0 9c 31 74 d8 7d 68 d7 6e 83 73 5f fb d0 cd de 66 |.1t.}h.n.s_....f|
+000002e0 60 fa 0a 0a 15 0b 30 3b 08 b6 f1 3e 4f 20 13 62 |`.....0;...>O .b|
+000002f0 b5 ff 86 81 dc 42 a1 4c af c8 ff b3 24 81 d8 e1 |.....B.L....$...|
+00000300 d1 09 0c 32 11 92 5e dd 3f 87 02 41 76 a7 29 48 |...2..^.?..Av.)H|
+00000310 52 68 1c 72 4d d5 39 bf fa 61 ec b2 27 ce 10 4e |Rh.rM.9..a..'..N|
+00000320 86 12 3d 1e 04 9c 11 b7 b4 0c cf 98 9d 01 c3 93 |..=.............|
+00000330 cf 83 9e 92 9a ca fd 8f b1 9f 1b 20 c4 fb a4 46 |........... ...F|
+00000340 60 fc fd d5 33 b0 8f b5 b5 c8 a4 70 c5 16 03 03 |`...3......p....|
+00000350 00 2e 0d 00 00 26 03 01 02 40 00 1e 06 01 06 02 |.....&...@......|
+00000360 06 03 05 01 05 02 05 03 04 01 04 02 04 03 03 01 |................|
+00000370 03 02 03 03 02 01 02 02 02 03 00 00 0e 00 00 00 |................|
>>> Flow 3 (client to server)
00000000 16 03 03 01 fb 0b 00 01 f7 00 01 f4 00 01 f1 30 |...............0|
00000010 82 01 ed 30 82 01 58 a0 03 02 01 02 02 01 00 30 |...0..X........0|
@@ -103,31 +103,31 @@
00000220 a7 24 20 3e b2 56 1c ce 97 28 5e f8 2b 2d 4f 9e |.$ >.V...(^.+-O.|
00000230 f1 07 9f 6c 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 |...lK[.V.2B.X..I|
00000240 a6 b5 68 1a 41 03 56 6b dc 5a 89 16 03 03 00 88 |..h.A.Vk.Z......|
-00000250 0f 00 00 84 04 01 00 80 07 7e 14 14 83 b9 d9 52 |.........~.....R|
-00000260 fd db c0 a4 79 37 b7 91 0b bb d6 ab d0 d1 c8 2e |....y7..........|
-00000270 35 5b 58 3f ce 6b f6 a9 01 95 34 a9 8b da 6b 23 |5[X?.k....4...k#|
-00000280 b7 99 11 75 3e f8 db bc ab 9b d4 8f 4f 89 12 d9 |...u>.......O...|
-00000290 2d 18 0e 54 2d 61 ff 9a 0e 3d 50 66 1d c2 e0 f6 |-..T-a...=Pf....|
-000002a0 4d 65 ca e2 08 af 29 cf 6d ab 63 72 ad 7c 03 a1 |Me....).m.cr.|..|
-000002b0 1e a1 f4 75 f5 54 58 28 3b 7d f7 21 d5 67 ec 60 |...u.TX(;}.!.g.`|
-000002c0 3b 59 81 ac f5 9a c6 cb 6a af da 7e 29 c4 c2 68 |;Y......j..~)..h|
-000002d0 53 34 aa b8 0e 58 61 24 14 03 03 00 01 01 16 03 |S4...Xa$........|
+00000250 0f 00 00 84 05 01 00 80 02 19 16 cc 97 ad 70 20 |..............p |
+00000260 bd 64 63 dd b6 81 a0 16 b3 46 4b 42 ff 21 58 2c |.dc......FKB.!X,|
+00000270 bb 2b 4c e1 4e d7 49 4d 5c 7c 63 32 3e ef e6 ad |.+L.N.IM\|c2>...|
+00000280 85 3f ab b4 5c 2a 37 76 8b 28 56 08 4f 08 b9 51 |.?..\*7v.(V.O..Q|
+00000290 71 14 07 27 47 45 11 a0 03 cf 72 7d 67 ef 31 8d |q..'GE....r}g.1.|
+000002a0 e7 db 36 76 b1 b3 f4 bf aa 6c c4 56 94 35 71 e1 |..6v.....l.V.5q.|
+000002b0 dd 88 6d 15 90 c8 70 ad d8 95 55 42 9b c1 45 19 |..m...p...UB..E.|
+000002c0 36 ce 87 c6 fd 94 8a d4 98 6e ec 18 d5 da 59 54 |6........n....YT|
+000002d0 80 a7 8c 90 ae 55 20 1c 14 03 03 00 01 01 16 03 |.....U .........|
000002e0 03 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 |..@.............|
-000002f0 00 00 00 a4 af 4b 95 ec 53 cf 49 8d b4 6c e0 3b |.....K..S.I..l.;|
-00000300 76 60 23 9b 2a f3 2c 12 61 18 cf 56 7c 1d 8c 01 |v`#.*.,.a..V|...|
-00000310 a8 bb 19 4d 1f ff ff 73 a2 90 e5 87 7b 85 d3 1b |...M...s....{...|
-00000320 74 6d 36 |tm6|
+000002f0 00 00 00 58 fe bc 5c ba b2 a9 96 77 2f 95 c9 10 |...X..\....w/...|
+00000300 fd 6d fc 6a 88 8c df 82 c3 a4 3d cc 28 f4 bf 7d |.m.j......=.(..}|
+00000310 4a f8 3d 97 36 e5 a0 76 92 94 da dd cc f5 e4 0e |J.=.6..v........|
+00000320 7a c4 2c |z.,|
>>> Flow 4 (server to client)
-00000000 14 03 03 00 01 01 16 03 03 00 40 fb 28 05 0a 99 |..........@.(...|
-00000010 61 d1 c7 52 a7 9e 95 a5 c1 11 3c 81 ee f2 b8 68 |a..R......<....h|
-00000020 a6 35 e2 7e bb 3c e7 7b 61 72 08 29 3a a5 e9 d3 |.5.~.<.{ar.):...|
-00000030 39 9c d2 0f 38 12 9b 92 79 36 58 bc f3 23 85 76 |9...8...y6X..#.v|
-00000040 1c 7b 6c 49 0c bc 00 61 20 1b ff |.{lI...a ..|
+00000000 14 03 03 00 01 01 16 03 03 00 40 81 ab 5a 66 a8 |..........@..Zf.|
+00000010 0f a5 d3 07 00 66 45 1f 31 a9 ef f7 a0 d9 23 46 |.....fE.1.....#F|
+00000020 f0 3e 50 18 99 e3 5a bd eb b7 1d 81 d5 95 d5 ee |.>P...Z.........|
+00000030 21 31 41 4b 19 92 b5 95 36 da 21 c0 4a 2a a0 1c |!1AK....6.!.J*..|
+00000040 a3 9f 8e a0 6f 9d 37 5e 12 11 03 |....o.7^...|
>>> Flow 5 (client to server)
00000000 17 03 03 00 30 00 00 00 00 00 00 00 00 00 00 00 |....0...........|
-00000010 00 00 00 00 00 fa f6 0b 1f f6 28 c7 4c 6c c8 8d |..........(.Ll..|
-00000020 9c 97 5f 3d 22 bb 45 fc 07 ae 3a 7e 74 01 7c 71 |.._=".E...:~t.|q|
-00000030 39 45 15 d3 c7 15 03 03 00 30 00 00 00 00 00 00 |9E.......0......|
-00000040 00 00 00 00 00 00 00 00 00 00 a1 43 03 79 a4 4e |...........C.y.N|
-00000050 bd 0f 17 c4 d0 29 1a 8a dd 7b e7 48 3c e4 4b 8a |.....)...{.H<.K.|
-00000060 53 3d 1d 18 f9 05 fd 4b 73 4a |S=.....KsJ|
+00000010 00 00 00 00 00 a9 51 94 19 72 ab 9f 3e 97 5e 99 |......Q..r..>.^.|
+00000020 2c ec 13 48 3e 10 54 5f 8a 85 88 4d 1a a8 f5 ed |,..H>.T_...M....|
+00000030 c3 4f a9 59 a3 15 03 03 00 30 00 00 00 00 00 00 |.O.Y.....0......|
+00000040 00 00 00 00 00 00 00 00 00 00 25 00 6d 2f a0 f6 |..........%.m/..|
+00000050 ce 8a 30 ba 53 da 97 c6 11 f3 d2 f3 9e 66 d6 dd |..0.S........f..|
+00000060 19 f3 ee 07 03 d3 e6 f1 30 32 |........02|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-RSA b/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-RSA
index 66a661c41a..ff79aa236c 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-RSA
+++ b/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-RSA
@@ -1,18 +1,19 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 00 79 01 00 00 75 03 03 00 00 00 00 00 |....y...u.......|
+00000000 16 03 01 00 81 01 00 00 7d 03 03 00 00 00 00 00 |........}.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 1e c0 2f |.............../|
00000030 c0 2b c0 30 c0 2c c0 11 c0 07 c0 13 c0 09 c0 14 |.+.0.,..........|
-00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 2e |...../.5........|
+00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 36 |...../.5.......6|
00000050 00 05 00 05 01 00 00 00 00 00 0a 00 08 00 06 00 |................|
-00000060 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 0a 00 |................|
-00000070 08 04 01 04 03 02 01 02 03 ff 01 00 01 00 |..............|
+00000060 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 0e 00 |................|
+00000070 0c 04 01 04 03 05 01 05 03 02 01 02 03 ff 01 00 |................|
+00000080 01 00 00 12 00 00 |......|
>>> Flow 2 (server to client)
-00000000 16 03 03 00 51 02 00 00 4d 03 03 32 d8 c5 23 e3 |....Q...M..2..#.|
-00000010 c7 4c d9 e9 d9 bd 1d d4 70 60 df 01 46 dc ca c5 |.L......p`..F...|
-00000020 d3 1b 57 28 f0 c4 4b 1c b3 8d 13 20 4a b8 d7 eb |..W(..K.... J...|
-00000030 70 9e e5 6d 6f 8c d4 0d a4 25 3a ce 91 e0 25 68 |p..mo....%:...%h|
-00000040 6f 25 38 0d 91 57 af 28 39 9c 61 85 00 05 00 00 |o%8..W.(9.a.....|
+00000000 16 03 03 00 51 02 00 00 4d 03 03 b3 b2 22 69 e4 |....Q...M...."i.|
+00000010 1a a1 56 94 26 0c 43 b7 89 0c 34 ce dc 5a c8 ca |..V.&.C...4..Z..|
+00000020 e2 42 92 5c 75 9a b3 22 22 64 38 20 6d 2c 26 0b |.B.\u..""d8 m,&.|
+00000030 34 b6 b8 20 36 e2 58 e5 ee 1f e2 9f a0 75 f6 d9 |4.. 6.X......u..|
+00000040 0c e4 39 ce 3c 8e 2e f8 e8 d1 a2 16 00 05 00 00 |..9.<...........|
00000050 05 ff 01 00 01 00 16 03 03 02 be 0b 00 02 ba 00 |................|
00000060 02 b7 00 02 b4 30 82 02 b0 30 82 02 19 a0 03 02 |.....0...0......|
00000070 01 02 02 09 00 85 b0 bb a4 8a 7f b8 ca 30 0d 06 |.............0..|
@@ -103,24 +104,24 @@
00000260 e6 bd 77 82 6f 23 b6 e0 bd a2 92 b7 3a ac e8 56 |..w.o#......:..V|
00000270 f1 af 54 5e 46 87 e9 3b 33 e7 b8 28 b7 d6 c8 90 |..T^F..;3..(....|
00000280 35 d4 1c 43 d1 30 6f 55 4e 0a 70 16 03 03 00 88 |5..C.0oUN.p.....|
-00000290 0f 00 00 84 04 01 00 80 3a 55 0a c6 97 2d 71 bc |........:U...-q.|
-000002a0 9d e1 ec 5b cb 3d de 64 8e fd 99 c0 55 1f d5 d1 |...[.=.d....U...|
-000002b0 ae 74 79 b8 1d 25 3e 4d 19 32 62 ca 04 82 f4 3f |.ty..%>M.2b....?|
-000002c0 7c 2b 7a 82 a6 86 2b d3 ba b0 ad 48 c4 c9 33 e6 ||+z...+....H..3.|
-000002d0 c8 2c 4a 06 75 a6 e7 49 65 53 54 33 27 55 7a 30 |.,J.u..IeST3'Uz0|
-000002e0 55 64 ef a0 d9 96 29 69 3f 90 ba b3 e4 aa 4e 5f |Ud....)i?.....N_|
-000002f0 1d 00 c2 90 c2 04 f9 9b 7f f1 e5 fd f2 1e 57 fd |..............W.|
-00000300 fc 0b 70 81 71 9a 43 9b 80 ff 96 42 f5 8d ff 2f |..p.q.C....B.../|
-00000310 4f d9 48 e2 6e bf 9e f2 14 03 03 00 01 01 16 03 |O.H.n...........|
-00000320 03 00 24 32 b3 61 bd 9a e1 21 79 60 f0 4b 6c 26 |..$2.a...!y`.Kl&|
-00000330 15 91 14 6d bc 42 9b c0 21 5f 93 2d d0 f7 db 9f |...m.B..!_.-....|
-00000340 81 60 bd d6 34 fa 31 |.`..4.1|
+00000290 0f 00 00 84 05 01 00 80 01 24 8d bb 05 61 2d 29 |.........$...a-)|
+000002a0 12 11 90 f5 57 21 be b7 29 76 55 63 94 8e 7b 4d |....W!..)vUc..{M|
+000002b0 3b 3d 89 5b 1f b9 e1 8c 36 68 6f 31 21 50 af e4 |;=.[....6ho1!P..|
+000002c0 9f ca a5 68 55 b9 eb 36 75 3a 0c be 11 30 28 c8 |...hU..6u:...0(.|
+000002d0 8b 82 93 9a 71 37 4d 4e 4f d2 0c 2f 13 36 ad c3 |....q7MNO../.6..|
+000002e0 df 8a 1b 59 b2 f9 8b a7 74 63 75 4a f4 9d e0 6b |...Y....tcuJ...k|
+000002f0 42 02 5a a9 6e a4 a8 24 d3 23 f7 09 ee b0 dc c4 |B.Z.n..$.#......|
+00000300 6f 87 58 72 e7 e3 87 b3 6b 15 ba 7f dd 9b 93 91 |o.Xr....k.......|
+00000310 5b 21 a0 31 31 bd 15 b5 14 03 03 00 01 01 16 03 |[!.11...........|
+00000320 03 00 24 fc 0e 7c e8 3e 8b b5 dc c9 3d 38 61 a1 |..$..|.>....=8a.|
+00000330 24 d6 77 1f 06 1f 30 32 ba dd 05 68 45 f1 4f 0d |$.w...02...hE.O.|
+00000340 2e 24 09 ad c1 e5 b7 |.$.....|
>>> Flow 4 (server to client)
-00000000 14 03 03 00 01 01 16 03 03 00 24 5c 99 fe 86 6f |..........$\...o|
-00000010 89 c3 e1 ed 24 1f a5 81 a8 fd 2a 8d 28 01 cd 86 |....$.....*.(...|
-00000020 11 48 5c 13 fe f4 29 dd ff 15 70 da 68 3b d8 |.H\...)...p.h;.|
+00000000 14 03 03 00 01 01 16 03 03 00 24 d7 b6 b3 0a e6 |..........$.....|
+00000010 86 9a 25 e4 38 de d0 57 ff 93 0b f4 de 76 3d 00 |..%.8..W.....v=.|
+00000020 64 35 cf 70 f6 ea 74 2d b0 71 2d 92 e2 df eb |d5.p..t-.q-....|
>>> Flow 5 (client to server)
-00000000 17 03 03 00 1a a9 5b 30 f3 9d 98 b0 a6 a6 4c 52 |......[0......LR|
-00000010 35 c9 aa 88 24 12 0f b0 53 88 21 8a 39 56 62 15 |5...$...S.!.9Vb.|
-00000020 03 03 00 16 ff 5f 0a cf 48 2a bd 2f e9 db 50 bc |....._..H*./..P.|
-00000030 11 3c c8 d2 61 69 6c 84 22 bf |.<..ail.".|
+00000000 17 03 03 00 1a db bd 43 12 7c b5 83 b5 18 9d 6a |.......C.|.....j|
+00000010 70 3f 5a eb cb d0 ba d4 03 3e a0 7b 25 f0 41 15 |p?Z......>.{%.A.|
+00000020 03 03 00 16 f8 f2 a3 27 a5 c7 25 d9 6c 08 b1 96 |.......'..%.l...|
+00000030 38 22 38 df 16 fb e2 9f 61 a3 |8"8.....a.|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES
index 9ff4d3ccf1..e700e16352 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES
+++ b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES
@@ -1,18 +1,19 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 00 79 01 00 00 75 03 03 00 00 00 00 00 |....y...u.......|
+00000000 16 03 01 00 81 01 00 00 7d 03 03 00 00 00 00 00 |........}.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 1e c0 2f |.............../|
00000030 c0 2b c0 30 c0 2c c0 11 c0 07 c0 13 c0 09 c0 14 |.+.0.,..........|
-00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 2e |...../.5........|
+00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 36 |...../.5.......6|
00000050 00 05 00 05 01 00 00 00 00 00 0a 00 08 00 06 00 |................|
-00000060 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 0a 00 |................|
-00000070 08 04 01 04 03 02 01 02 03 ff 01 00 01 00 |..............|
+00000060 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 0e 00 |................|
+00000070 0c 04 01 04 03 05 01 05 03 02 01 02 03 ff 01 00 |................|
+00000080 01 00 00 12 00 00 |......|
>>> Flow 2 (server to client)
-00000000 16 03 03 00 59 02 00 00 55 03 03 97 dc 20 65 0f |....Y...U.... e.|
-00000010 3f 83 4a 55 06 27 32 2f 68 81 f9 4a 6d 0a 8c 3e |?.JU.'2/h..Jm..>|
-00000020 c0 aa c1 c2 e1 09 a8 a0 a5 e3 42 20 7b ed 80 22 |..........B {.."|
-00000030 22 f9 84 ab 6d f5 63 18 bc f8 dc 7d 13 31 6b 4b |"...m.c....}.1kK|
-00000040 85 c0 63 8d e5 d8 29 c8 ad 09 d7 b7 c0 09 00 00 |..c...).........|
+00000000 16 03 03 00 59 02 00 00 55 03 03 21 9b eb 15 24 |....Y...U..!...$|
+00000010 46 b6 c1 85 f5 be c5 0d e2 6b 60 bc ee 73 b1 fb |F........k`..s..|
+00000020 34 6f f0 b8 f0 9e 1c 26 a4 4b 0f 20 cb 2b 84 a2 |4o.....&.K. .+..|
+00000030 cb a5 48 70 fe 84 25 b0 16 20 14 a1 83 21 fc f9 |..Hp..%.. ...!..|
+00000040 82 fc 9e 1a d1 3b 56 69 ab c5 0e 2c c0 09 00 00 |.....;Vi...,....|
00000050 0d ff 01 00 01 00 00 0b 00 04 03 00 01 02 16 03 |................|
00000060 03 02 0e 0b 00 02 0a 00 02 07 00 02 04 30 82 02 |.............0..|
00000070 00 30 82 01 62 02 09 00 b8 bf 2d 47 a0 d2 eb f4 |.0..b.....-G....|
@@ -47,20 +48,20 @@
00000240 13 83 0d 94 06 bb d4 37 7a f6 ec 7a c9 86 2e dd |.......7z..z....|
00000250 d7 11 69 7f 85 7c 56 de fb 31 78 2b e4 c7 78 0d |..i..|V..1x+..x.|
00000260 ae cb be 9e 4e 36 24 31 7b 6a 0f 39 95 12 07 8f |....N6$1{j.9....|
-00000270 2a 16 03 03 00 d8 0c 00 00 d4 03 00 17 41 04 dd |*............A..|
-00000280 34 64 e4 ba 63 e0 25 f2 6b cd 24 21 58 8b e1 08 |4d..c.%.k.$!X...|
-00000290 eb 09 6f 93 e2 cd 19 13 d0 e6 5a 0c ee 57 b9 ab |..o.......Z..W..|
-000002a0 21 be 8d b5 47 1e a8 01 a4 de c4 de a7 d5 eb dd |!...G...........|
-000002b0 d9 bd 66 1a 71 0a b7 a1 3d 10 8e b6 2d 73 ba 04 |..f.q...=...-s..|
-000002c0 03 00 8b 30 81 88 02 42 01 c6 4a 64 2b 66 7f cb |...0...B..Jd+f..|
-000002d0 28 eb ad 05 d4 86 a0 d6 0f 12 52 03 fc 66 3f 76 |(.........R..f?v|
-000002e0 db 85 8f b4 f1 45 04 f5 10 27 b3 76 62 9a bc 7b |.....E...'.vb..{|
-000002f0 f9 6e f6 45 fb 15 9c eb 5c 70 ca b2 40 00 f8 18 |.n.E....\p..@...|
-00000300 b9 e4 28 fc e4 b7 d8 15 70 1a 02 42 01 d3 8f 53 |..(.....p..B...S|
-00000310 57 b5 e4 f5 84 97 a2 e9 07 5a f8 67 bd 03 02 6d |W........Z.g...m|
-00000320 ea 4e 14 da 12 2c d0 7c 89 a0 93 97 46 c9 62 ee |.N...,.|....F.b.|
-00000330 c0 d3 d6 bf 04 11 af 19 96 6b a9 86 f8 2c 2f ab |.........k...,/.|
-00000340 89 20 45 94 b6 d1 43 64 fc eb 2e ff 80 37 16 03 |. E...Cd.....7..|
+00000270 2a 16 03 03 00 d8 0c 00 00 d4 03 00 17 41 04 b6 |*............A..|
+00000280 3f 37 33 68 cb 79 c0 86 f4 9d 12 ac c4 9d 8c 9b |?73h.y..........|
+00000290 59 1c d4 a9 01 9f 2d cb 80 24 02 ec e0 ff d1 8c |Y.....-..$......|
+000002a0 bd 82 67 3f 47 58 1a 2e 6b 61 f6 8e 4e 27 7f 49 |..g?GX..ka..N'.I|
+000002b0 b5 45 f1 0b 9a 33 ff 53 ac 65 e2 82 7a 18 5c 04 |.E...3.S.e..z.\.|
+000002c0 03 00 8b 30 81 88 02 42 00 e1 2d ff 5d e7 77 f1 |...0...B..-.].w.|
+000002d0 12 d9 e4 c2 4d cd 9c b5 ee e4 fd 21 b2 d8 53 a9 |....M......!..S.|
+000002e0 42 e7 c5 9b 51 c3 59 37 a5 08 d4 e6 29 12 c5 56 |B...Q.Y7....)..V|
+000002f0 b8 fe f0 bb 77 87 a3 ee 09 b0 8c cd 1c 39 9e b5 |....w........9..|
+00000300 d9 15 63 53 cb d7 f1 55 5b 48 02 42 01 19 10 8a |..cS...U[H.B....|
+00000310 7a ee 95 b1 77 44 d4 a3 bf d1 f3 f1 b0 d8 c7 7e |z...wD.........~|
+00000320 42 c0 83 04 f5 f7 9c c0 ce 6a 98 47 9d 21 29 84 |B........j.G.!).|
+00000330 c8 be 6b 67 4e fc c6 26 ec 63 df 00 33 e6 d2 f7 |..kgN..&.c..3...|
+00000340 34 93 85 9b 1b 0f e0 89 42 b6 0b 94 1b 80 16 03 |4.......B.......|
00000350 03 00 04 0e 00 00 00 |.......|
>>> Flow 3 (client to server)
00000000 16 03 03 00 46 10 00 00 42 41 04 1e 18 37 ef 0d |....F...BA...7..|
@@ -69,21 +70,21 @@
00000030 f1 07 9f 6c 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 |...lK[.V.2B.X..I|
00000040 a6 b5 68 1a 41 03 56 6b dc 5a 89 14 03 03 00 01 |..h.A.Vk.Z......|
00000050 01 16 03 03 00 40 00 00 00 00 00 00 00 00 00 00 |.....@..........|
-00000060 00 00 00 00 00 00 21 2a 44 9c f5 f7 b5 0f 43 f4 |......!*D.....C.|
-00000070 19 03 02 64 c0 9a a0 d1 50 89 f2 f2 dd a1 dc 72 |...d....P......r|
-00000080 da 08 d1 5c 75 fa 54 ee bf c8 76 5f 57 df 62 2b |...\u.T...v_W.b+|
-00000090 36 48 40 c4 a4 ac |6H@...|
+00000060 00 00 00 00 00 00 50 73 9c 9f a8 d7 78 ac 06 14 |......Ps....x...|
+00000070 8f ae fc fb ef 7d 99 db b7 c9 91 dd f2 fe da 1b |.....}..........|
+00000080 aa 9e 7d e4 5c 2f 5f dd 74 aa fe 03 51 e7 cd 98 |..}.\/_.t...Q...|
+00000090 e9 21 19 c9 6f 59 |.!..oY|
>>> Flow 4 (server to client)
-00000000 14 03 03 00 01 01 16 03 03 00 40 72 a7 fe d8 23 |..........@r...#|
-00000010 6a 4f 4c 11 09 5d 0e d3 86 4e d6 e8 96 cb ac 71 |jOL..]...N.....q|
-00000020 68 e2 50 94 eb e4 d2 9b 61 56 e2 17 50 5e fb b2 |h.P.....aV..P^..|
-00000030 fe a0 1f 8d 74 2c c6 d0 ba 5e f7 73 b8 00 8d b5 |....t,...^.s....|
-00000040 57 e1 41 90 21 15 91 6d 69 25 83 |W.A.!..mi%.|
+00000000 14 03 03 00 01 01 16 03 03 00 40 47 18 b5 1b 75 |..........@G...u|
+00000010 b8 a3 63 ab 77 d3 47 cb 14 26 b4 88 fe 15 db 22 |..c.w.G..&....."|
+00000020 76 3b 25 d3 68 8e f2 a7 d5 03 2b 82 7b b1 0f 10 |v;%.h.....+.{...|
+00000030 49 6a 3d 95 d0 4b 55 0e 14 eb bb a7 34 bb 57 b3 |Ij=..KU.....4.W.|
+00000040 5d fb 7e 15 80 5a fa f3 3a df 90 |].~..Z..:..|
>>> Flow 5 (client to server)
00000000 17 03 03 00 30 00 00 00 00 00 00 00 00 00 00 00 |....0...........|
-00000010 00 00 00 00 00 80 70 b8 c4 f1 ef 0c 2e 87 5c fc |......p.......\.|
-00000020 fb 54 19 4d 42 42 09 32 32 dd 54 b9 6e 35 ea 13 |.T.MBB.22.T.n5..|
-00000030 e1 2b 4c 7e e6 15 03 03 00 30 00 00 00 00 00 00 |.+L~.....0......|
-00000040 00 00 00 00 00 00 00 00 00 00 3e aa 24 38 78 63 |..........>.$8xc|
-00000050 ae 5c d4 28 2d 3b 7c 1b 66 2f 07 02 00 e1 78 dd |.\.(-;|.f/....x.|
-00000060 6e 43 e7 23 da 55 55 33 a2 d8 |nC.#.UU3..|
+00000010 00 00 00 00 00 70 74 e2 60 fc 3a 7a b7 5e 16 07 |.....pt.`.:z.^..|
+00000020 22 92 07 fe 92 53 c4 43 1b 8f 94 07 84 48 2b 50 |"....S.C.....H+P|
+00000030 ab 1d 6d 49 ed 15 03 03 00 30 00 00 00 00 00 00 |..mI.....0......|
+00000040 00 00 00 00 00 00 00 00 00 00 ce a8 ba 91 0b e4 |................|
+00000050 8c 38 23 9b 8b 2c 0a 0c 63 79 61 f4 b6 25 f7 41 |.8#..,..cya..%.A|
+00000060 04 9f b0 8f e0 e5 24 44 2f e9 |......$D/.|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES-GCM b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES-GCM
index 9b7d61d1ea..607ecdcb24 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES-GCM
+++ b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES-GCM
@@ -1,18 +1,19 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 00 79 01 00 00 75 03 03 00 00 00 00 00 |....y...u.......|
+00000000 16 03 01 00 81 01 00 00 7d 03 03 00 00 00 00 00 |........}.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 1e c0 2f |.............../|
00000030 c0 2b c0 30 c0 2c c0 11 c0 07 c0 13 c0 09 c0 14 |.+.0.,..........|
-00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 2e |...../.5........|
+00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 36 |...../.5.......6|
00000050 00 05 00 05 01 00 00 00 00 00 0a 00 08 00 06 00 |................|
-00000060 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 0a 00 |................|
-00000070 08 04 01 04 03 02 01 02 03 ff 01 00 01 00 |..............|
+00000060 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 0e 00 |................|
+00000070 0c 04 01 04 03 05 01 05 03 02 01 02 03 ff 01 00 |................|
+00000080 01 00 00 12 00 00 |......|
>>> Flow 2 (server to client)
-00000000 16 03 03 00 59 02 00 00 55 03 03 c9 39 e6 18 c8 |....Y...U...9...|
-00000010 4a 7f f3 23 75 99 22 80 48 bc e3 a7 eb 49 d5 95 |J..#u.".H....I..|
-00000020 b1 ec 1d 9e 44 09 6e d9 b7 b2 f8 20 30 fd 2b 50 |....D.n.... 0.+P|
-00000030 d2 91 de c3 d0 84 a9 d5 ba c0 45 0f 18 c4 98 73 |..........E....s|
-00000040 4b cf c6 82 dd 88 0d 35 28 8e f8 d3 c0 2b 00 00 |K......5(....+..|
+00000000 16 03 03 00 59 02 00 00 55 03 03 91 8a 4f 94 29 |....Y...U....O.)|
+00000010 32 fa 66 7a 7f b8 a7 04 5c 34 b9 7e 12 83 35 1f |2.fz....\4.~..5.|
+00000020 93 b0 af e0 9f 71 07 5e 2f d7 ca 20 52 dc 0d e7 |.....q.^/.. R...|
+00000030 f8 16 db 90 9a 78 2f 03 0b f0 ae a7 2f c6 b4 4c |.....x/...../..L|
+00000040 62 e7 de 32 d5 68 61 f3 07 e4 60 d2 c0 2b 00 00 |b..2.ha...`..+..|
00000050 0d ff 01 00 01 00 00 0b 00 04 03 00 01 02 16 03 |................|
00000060 03 02 0e 0b 00 02 0a 00 02 07 00 02 04 30 82 02 |.............0..|
00000070 00 30 82 01 62 02 09 00 b8 bf 2d 47 a0 d2 eb f4 |.0..b.....-G....|
@@ -47,20 +48,20 @@
00000240 13 83 0d 94 06 bb d4 37 7a f6 ec 7a c9 86 2e dd |.......7z..z....|
00000250 d7 11 69 7f 85 7c 56 de fb 31 78 2b e4 c7 78 0d |..i..|V..1x+..x.|
00000260 ae cb be 9e 4e 36 24 31 7b 6a 0f 39 95 12 07 8f |....N6$1{j.9....|
-00000270 2a 16 03 03 00 d8 0c 00 00 d4 03 00 17 41 04 91 |*............A..|
-00000280 d0 f0 1b df 51 57 74 f3 62 ee d5 9e e8 7d bd 65 |....QWt.b....}.e|
-00000290 69 0a 5a 2b 75 c3 3c f7 24 3f 91 26 34 fe d8 8f |i.Z+u.<.$?.&4...|
-000002a0 fa d3 7e f6 f5 01 89 7b f5 69 5c c2 52 41 81 93 |..~....{.i\.RA..|
-000002b0 c4 9e 01 5d 96 fa db 41 3d 0b 78 58 ad 29 b5 04 |...]...A=.xX.)..|
-000002c0 03 00 8b 30 81 88 02 42 01 92 7c 0a 7c 79 d1 41 |...0...B..|.|y.A|
-000002d0 98 b7 57 37 10 d9 31 41 2e fe d5 a8 94 26 fa 59 |..W7..1A.....&.Y|
-000002e0 78 bf 15 c0 cf e7 a9 09 a8 6f 97 45 1b 3f e6 60 |x........o.E.?.`|
-000002f0 2d 78 dc ec 99 0f 92 43 64 20 c4 6b 59 16 df 66 |-x.....Cd .kY..f|
-00000300 83 a0 f1 d1 91 c1 8a 29 ce 4d 02 42 01 61 a2 6c |.......).M.B.a.l|
-00000310 84 58 58 0b 74 fa 9e 4c 33 6a b5 b1 a9 da ad 1c |.XX.t..L3j......|
-00000320 d9 33 25 91 59 a0 f2 21 ae b1 14 15 4a d1 65 50 |.3%.Y..!....J.eP|
-00000330 0e 1d 1e bc f6 29 da 22 09 20 de 75 30 ac 0a 1e |.....).". .u0...|
-00000340 7e 46 98 89 dd 6d e4 6a 9b 83 b5 85 f3 74 16 03 |~F...m.j.....t..|
+00000270 2a 16 03 03 00 d8 0c 00 00 d4 03 00 17 41 04 26 |*............A.&|
+00000280 c1 67 14 4b 9e b0 45 8c 27 bf a3 a2 78 5b 56 ad |.g.K..E.'...x[V.|
+00000290 d1 21 56 53 df 86 e9 91 de e3 f9 5d e6 f6 5d 79 |.!VS.......]..]y|
+000002a0 11 8b 60 f9 c2 9a c6 3f 6b 72 cd 7c d7 0e 13 64 |..`....?kr.|...d|
+000002b0 af e8 9f 40 35 e6 fb 04 0c 60 aa 19 61 dd 24 04 |...@5....`..a.$.|
+000002c0 03 00 8b 30 81 88 02 42 00 9d e1 02 5d 8b b1 45 |...0...B....]..E|
+000002d0 e5 c7 b6 94 27 df 36 31 fd 5e 47 fe c8 0f 5f 17 |....'.61.^G..._.|
+000002e0 b1 92 56 76 29 45 3d 90 be 91 6e 2c a7 b2 e1 33 |..Vv)E=...n,...3|
+000002f0 3b f9 3c bb 80 58 c2 d8 a8 59 82 16 dc 9e dd 60 |;.<..X...Y.....`|
+00000300 ff 82 b9 0c 5a ca ff f3 02 2c 02 42 00 a4 c0 d3 |....Z....,.B....|
+00000310 aa 1d 69 52 c0 06 fa 93 e8 50 da a4 2f 72 c9 4a |..iR.....P../r.J|
+00000320 2c 43 7f 95 05 f7 7a f3 4a 2e 2d ce 13 be 80 40 |,C....z.J.-....@|
+00000330 a4 3b b2 f0 73 8d f1 d4 7b a3 ff 01 e1 58 71 31 |.;..s...{....Xq1|
+00000340 fc d8 2f b3 ef 62 2e b7 ac f5 c4 bc b8 68 16 03 |../..b.......h..|
00000350 03 00 04 0e 00 00 00 |.......|
>>> Flow 3 (client to server)
00000000 16 03 03 00 46 10 00 00 42 41 04 1e 18 37 ef 0d |....F...BA...7..|
@@ -68,17 +69,17 @@
00000020 a7 24 20 3e b2 56 1c ce 97 28 5e f8 2b 2d 4f 9e |.$ >.V...(^.+-O.|
00000030 f1 07 9f 6c 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 |...lK[.V.2B.X..I|
00000040 a6 b5 68 1a 41 03 56 6b dc 5a 89 14 03 03 00 01 |..h.A.Vk.Z......|
-00000050 01 16 03 03 00 28 00 00 00 00 00 00 00 00 b0 4d |.....(.........M|
-00000060 e2 ad 33 40 f2 44 e3 c7 ad a5 c6 c7 e5 00 07 68 |..3@.D.........h|
-00000070 72 80 d5 89 f0 aa 72 2b 36 5a 51 f6 f0 6a |r.....r+6ZQ..j|
+00000050 01 16 03 03 00 28 00 00 00 00 00 00 00 00 83 cf |.....(..........|
+00000060 ef 50 c2 e7 da b9 74 7f 1c e0 b8 fb dc 39 c9 98 |.P....t......9..|
+00000070 0c a3 7d 8c c6 fa 6f f2 ee 44 a0 a0 03 18 |..}...o..D....|
>>> Flow 4 (server to client)
-00000000 14 03 03 00 01 01 16 03 03 00 28 5d b6 1b 59 71 |..........(]..Yq|
-00000010 f0 7a 2c 4f d5 f0 7b a7 ab 56 48 4d b4 f7 5c bc |.z,O..{..VHM..\.|
-00000020 34 d6 cc 02 4f 1f 45 b2 e9 ff 96 0e a2 47 d6 4e |4...O.E......G.N|
-00000030 47 83 68 |G.h|
+00000000 14 03 03 00 01 01 16 03 03 00 28 73 c4 48 24 3d |..........(s.H$=|
+00000010 8f 5f f3 8c fc fd 63 be 64 39 d5 56 67 bd d7 c4 |._....c.d9.Vg...|
+00000020 0d 57 88 1a 45 a6 f3 ad 11 b2 5a 41 58 33 f3 d3 |.W..E.....ZAX3..|
+00000030 58 fa 21 |X.!|
>>> Flow 5 (client to server)
-00000000 17 03 03 00 1e 00 00 00 00 00 00 00 01 2f f1 95 |............./..|
-00000010 75 5e 0d fb 48 9b 40 10 6d bb 81 7e d2 ca 68 ae |u^..H.@.m..~..h.|
-00000020 84 47 d2 15 03 03 00 1a 00 00 00 00 00 00 00 02 |.G..............|
-00000030 26 87 82 85 fa 5f a2 b2 19 b2 4e 81 f6 0f c6 c5 |&...._....N.....|
-00000040 e0 3e |.>|
+00000000 17 03 03 00 1e 00 00 00 00 00 00 00 01 65 5e 55 |.............e^U|
+00000010 32 be 00 77 6e 1d 8e 8f 95 33 24 3d 7a c2 b0 3f |2..wn....3$=z..?|
+00000020 ca aa 97 15 03 03 00 1a 00 00 00 00 00 00 00 02 |................|
+00000030 b2 71 6e 42 6a 0d cf c9 ac 14 a4 b5 9c c9 71 60 |.qnBj.........q`|
+00000040 d7 c2 |..|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES256-GCM-SHA384 b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES256-GCM-SHA384
index 068b402a34..df2f7376de 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES256-GCM-SHA384
+++ b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES256-GCM-SHA384
@@ -1,18 +1,19 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 00 79 01 00 00 75 03 03 00 00 00 00 00 |....y...u.......|
+00000000 16 03 01 00 81 01 00 00 7d 03 03 00 00 00 00 00 |........}.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 1e c0 2f |.............../|
00000030 c0 2b c0 30 c0 2c c0 11 c0 07 c0 13 c0 09 c0 14 |.+.0.,..........|
-00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 2e |...../.5........|
+00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 36 |...../.5.......6|
00000050 00 05 00 05 01 00 00 00 00 00 0a 00 08 00 06 00 |................|
-00000060 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 0a 00 |................|
-00000070 08 04 01 04 03 02 01 02 03 ff 01 00 01 00 |..............|
+00000060 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 0e 00 |................|
+00000070 0c 04 01 04 03 05 01 05 03 02 01 02 03 ff 01 00 |................|
+00000080 01 00 00 12 00 00 |......|
>>> Flow 2 (server to client)
-00000000 16 03 03 00 59 02 00 00 55 03 03 d2 dd 5a 60 0d |....Y...U....Z`.|
-00000010 25 72 ed e6 89 6e 4d d8 1c 75 76 e4 37 5f 06 80 |%r...nM..uv.7_..|
-00000020 26 23 48 02 cd c6 b1 e5 59 89 b2 20 99 9e e6 31 |H.....Y.. ...1|
-00000030 8f ca b2 aa 68 b2 6b 2e c0 f3 f8 e9 56 f4 60 90 |....h.k.....V.`.|
-00000040 bb 5d 79 fd 4f f5 71 15 5b e7 31 20 c0 2c 00 00 |.]y.O.q.[.1 .,..|
+00000000 16 03 03 00 59 02 00 00 55 03 03 11 50 81 a7 ef |....Y...U...P...|
+00000010 3f bd a5 a9 41 11 e6 86 b2 a3 d8 bf 29 c3 d4 f4 |?...A.......)...|
+00000020 b6 20 2d cb 94 1b 0e dd 99 d1 0b 20 78 92 23 31 |. -........ x.#1|
+00000030 e3 fc 99 67 1f fd f3 2a fc 9c 4c 74 6e 32 e4 f8 |...g...*..Ltn2..|
+00000040 ed 6d 2e 6d ad a9 a9 bf 63 27 7e 44 c0 2c 00 00 |.m.m....c'~D.,..|
00000050 0d ff 01 00 01 00 00 0b 00 04 03 00 01 02 16 03 |................|
00000060 03 02 0e 0b 00 02 0a 00 02 07 00 02 04 30 82 02 |.............0..|
00000070 00 30 82 01 62 02 09 00 b8 bf 2d 47 a0 d2 eb f4 |.0..b.....-G....|
@@ -47,38 +48,38 @@
00000240 13 83 0d 94 06 bb d4 37 7a f6 ec 7a c9 86 2e dd |.......7z..z....|
00000250 d7 11 69 7f 85 7c 56 de fb 31 78 2b e4 c7 78 0d |..i..|V..1x+..x.|
00000260 ae cb be 9e 4e 36 24 31 7b 6a 0f 39 95 12 07 8f |....N6$1{j.9....|
-00000270 2a 16 03 03 00 d8 0c 00 00 d4 03 00 17 41 04 16 |*............A..|
-00000280 80 a7 71 18 d7 2e 0e 9b a0 ae 58 8b ff 56 c5 21 |..q.......X..V.!|
-00000290 44 b1 ff 7e 2a 1a c4 39 91 d8 f5 cb 67 6c eb 24 |D..~*..9....gl.$|
-000002a0 86 e3 2f 79 ca 07 a4 6a ad 92 3e 36 79 f0 00 25 |../y...j..>6y..%|
-000002b0 b5 b8 31 e5 3c 2e f1 5e 16 23 69 c4 14 a5 93 04 |..1.<..^.#i.....|
-000002c0 03 00 8b 30 81 88 02 42 01 68 cb 9b f4 22 71 10 |...0...B.h..."q.|
-000002d0 c5 5f 02 7c ab b4 db 6e af 35 89 3b ad 4d 6b 40 |._.|...n.5.;.Mk@|
-000002e0 62 64 8b e5 6c e1 9a bd 21 05 25 cb e9 b4 7a 31 |bd..l...!.%...z1|
-000002f0 2e 63 4f 77 4c 3f ab 7b 67 21 02 ae 8b 0a 7b 7e |.cOwL?.{g!....{~|
-00000300 f9 0f a8 df b1 14 0e ef 5e 66 02 42 01 c7 50 11 |........^f.B..P.|
-00000310 28 e9 aa 1d ea 52 60 af 37 35 73 13 bd f9 dd 54 |(....R`.75s....T|
-00000320 8e 34 db 9a 78 20 61 d4 6c 7f 72 06 4e 7a 58 07 |.4..x a.l.r.NzX.|
-00000330 d9 87 01 82 b8 dc 39 72 48 41 a4 ef 58 8e dd c6 |......9rHA..X...|
-00000340 8c 0d d3 c1 c6 36 79 e1 d0 78 dd 1c 89 9a 16 03 |.....6y..x......|
-00000350 03 00 04 0e 00 00 00 |.......|
+00000270 2a 16 03 03 00 d7 0c 00 00 d3 03 00 17 41 04 fc |*............A..|
+00000280 e6 25 27 3c 76 10 a8 9e d3 a4 a8 68 31 06 85 fc |.%'>> Flow 3 (client to server)
00000000 16 03 03 00 46 10 00 00 42 41 04 1e 18 37 ef 0d |....F...BA...7..|
00000010 19 51 88 35 75 71 b5 e5 54 5b 12 2e 8f 09 67 fd |.Q.5uq..T[....g.|
00000020 a7 24 20 3e b2 56 1c ce 97 28 5e f8 2b 2d 4f 9e |.$ >.V...(^.+-O.|
00000030 f1 07 9f 6c 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 |...lK[.V.2B.X..I|
00000040 a6 b5 68 1a 41 03 56 6b dc 5a 89 14 03 03 00 01 |..h.A.Vk.Z......|
-00000050 01 16 03 03 00 28 00 00 00 00 00 00 00 00 5f f3 |.....(........_.|
-00000060 89 d5 29 18 bb 58 6f 28 f6 15 46 a2 1b 0a 49 9a |..)..Xo(..F...I.|
-00000070 66 ab 83 31 36 f7 f6 74 35 45 2e db 80 b9 |f..16..t5E....|
+00000050 01 16 03 03 00 28 00 00 00 00 00 00 00 00 a3 3f |.....(.........?|
+00000060 be 65 91 cd fe 37 43 e0 ea 6f 15 9d c2 aa 6a 02 |.e...7C..o....j.|
+00000070 20 b8 bc b5 c8 9a 1c d4 c4 e5 9b 2e 39 e7 | ...........9.|
>>> Flow 4 (server to client)
-00000000 14 03 03 00 01 01 16 03 03 00 28 ef 24 92 74 6b |..........(.$.tk|
-00000010 d1 a7 26 2a 52 6e 15 70 10 65 e4 a9 89 8d 56 04 |..&*Rn.p.e....V.|
-00000020 29 d1 36 f5 aa 64 9b 34 b9 53 df fa de 47 c4 1b |).6..d.4.S...G..|
-00000030 36 59 88 |6Y.|
+00000000 14 03 03 00 01 01 16 03 03 00 28 7c b7 1f 13 9e |..........(|....|
+00000010 21 d2 eb db 32 fc 36 d0 53 e1 11 04 ce d0 61 33 |!...2.6.S.....a3|
+00000020 1e 30 3d 91 c3 6a 0d 98 55 f5 e0 5c ca 77 fa 72 |.0=..j..U..\.w.r|
+00000030 63 6a be |cj.|
>>> Flow 5 (client to server)
-00000000 17 03 03 00 1e 00 00 00 00 00 00 00 01 36 2e 40 |.............6.@|
-00000010 ed b9 f0 05 2e 08 64 28 3a da 3f 4b 80 26 6b e3 |......d(:.?K.&k.|
-00000020 97 0e 43 15 03 03 00 1a 00 00 00 00 00 00 00 02 |..C.............|
-00000030 bd 85 57 7c 08 f1 76 bf 57 16 fe 5f f7 b4 de 43 |..W|..v.W.._...C|
-00000040 64 36 |d6|
+00000000 17 03 03 00 1e 00 00 00 00 00 00 00 01 d9 db db |................|
+00000010 4b 3a ae 5c a4 dc 96 33 ed b5 a0 70 64 1f 96 2f |K:.\...3...pd../|
+00000020 b6 cd 1e 15 03 03 00 1a 00 00 00 00 00 00 00 02 |................|
+00000030 18 a0 d1 98 a6 71 c9 56 36 bd 1a 46 4b 5b 45 29 |.....q.V6..FK[E)|
+00000040 1f dd |..|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ECDHE-RSA-AES b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-RSA-AES
index 3c7516148e..994ebb1e37 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-ECDHE-RSA-AES
+++ b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-RSA-AES
@@ -1,18 +1,19 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 00 79 01 00 00 75 03 03 00 00 00 00 00 |....y...u.......|
+00000000 16 03 01 00 81 01 00 00 7d 03 03 00 00 00 00 00 |........}.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 1e c0 2f |.............../|
00000030 c0 2b c0 30 c0 2c c0 11 c0 07 c0 13 c0 09 c0 14 |.+.0.,..........|
-00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 2e |...../.5........|
+00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 36 |...../.5.......6|
00000050 00 05 00 05 01 00 00 00 00 00 0a 00 08 00 06 00 |................|
-00000060 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 0a 00 |................|
-00000070 08 04 01 04 03 02 01 02 03 ff 01 00 01 00 |..............|
+00000060 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 0e 00 |................|
+00000070 0c 04 01 04 03 05 01 05 03 02 01 02 03 ff 01 00 |................|
+00000080 01 00 00 12 00 00 |......|
>>> Flow 2 (server to client)
-00000000 16 03 03 00 59 02 00 00 55 03 03 81 ab f4 92 ec |....Y...U.......|
-00000010 b8 99 85 43 62 31 8e 58 63 c0 04 03 82 b4 f5 49 |...Cb1.Xc......I|
-00000020 d5 2d cd 24 de a0 24 29 39 93 90 20 ad 9c 35 ad |.-.$..$)9.. ..5.|
-00000030 20 1d 35 0a 6e 29 99 48 72 e6 fc 19 ae e6 7f 4f | .5.n).Hr......O|
-00000040 47 01 24 f4 9d 9e d7 0e 06 25 a8 93 c0 13 00 00 |G.$......%......|
+00000000 16 03 03 00 59 02 00 00 55 03 03 e6 ae 89 0d 22 |....Y...U......"|
+00000010 e5 e0 cd 57 a3 ca 71 4f 17 2f 64 77 f8 30 89 ef |...W..qO./dw.0..|
+00000020 e8 19 70 ac dd 2c c5 9f 84 7d 1d 20 1c 59 3c fe |..p..,...}. .Y<.|
+00000030 a9 ec 10 dd 38 3b 43 fe 6b 09 e5 e4 83 d9 7a 78 |....8;C.k.....zx|
+00000040 86 08 33 da 9b e1 09 d8 c9 07 34 19 c0 13 00 00 |..3.......4.....|
00000050 0d ff 01 00 01 00 00 0b 00 04 03 00 01 02 16 03 |................|
00000060 03 02 be 0b 00 02 ba 00 02 b7 00 02 b4 30 82 02 |.............0..|
00000070 b0 30 82 02 19 a0 03 02 01 02 02 09 00 85 b0 bb |.0..............|
@@ -58,20 +59,20 @@
000002f0 5f 33 c4 b6 d8 c9 75 90 96 8c 0f 52 98 b5 cd 98 |_3....u....R....|
00000300 1f 89 20 5f f2 a0 1c a3 1b 96 94 dd a9 fd 57 e9 |.. _..........W.|
00000310 70 e8 26 6d 71 99 9b 26 6e 38 50 29 6c 90 a7 bd |p.&mq..&n8P)l...|
-00000320 d9 16 03 03 00 cd 0c 00 00 c9 03 00 17 41 04 a3 |.............A..|
-00000330 b7 75 d0 ba b1 e1 4e aa 08 36 e2 90 52 3c e8 8c |.u....N..6..R<..|
-00000340 78 54 61 e6 ec 60 ad 95 9b 1e a0 de a4 14 95 31 |xTa..`.........1|
-00000350 fb fc 23 5b e7 22 da 68 a1 c4 68 da 7e 62 08 6e |..#[.".h..h.~b.n|
-00000360 40 0a 3d ac 28 f2 70 17 44 24 43 b6 12 f0 0e 04 |@.=.(.p.D$C.....|
-00000370 01 00 80 3c 1e 25 16 dc f0 d0 ac 3e 63 d3 c6 ee |...<.%.....>c...|
-00000380 ed 1b 1c 8b 9d ec 41 d9 10 56 f6 19 35 61 49 fc |......A..V..5aI.|
-00000390 e6 03 f5 29 89 a1 61 46 78 0b 9b 4e f9 26 18 58 |...)..aFx..N.&.X|
-000003a0 50 64 c2 a6 fb 61 d0 29 e2 f9 b1 56 07 91 69 8d |Pd...a.)...V..i.|
-000003b0 ec 69 0e ab 91 70 a9 82 52 4f b1 d8 31 28 e2 49 |.i...p..RO..1(.I|
-000003c0 fa fa 26 c7 f9 cf 30 6e 01 59 3f de 0d 56 c8 9e |..&...0n.Y?..V..|
-000003d0 ae fd 49 2a 66 a0 bb 0b b4 f8 02 7f c8 b2 53 14 |..I*f.........S.|
-000003e0 f1 7f a9 3a 02 cd 33 04 cf 73 8b 5a 61 f3 d3 5e |...:..3..s.Za..^|
-000003f0 24 78 43 16 03 03 00 04 0e 00 00 00 |$xC.........|
+00000320 d9 16 03 03 00 cd 0c 00 00 c9 03 00 17 41 04 77 |.............A.w|
+00000330 87 a7 ad f6 f8 34 82 05 ef bb 14 6d c7 8b 7b 2a |.....4.....m..{*|
+00000340 4d ca 41 65 58 3c 83 fa 4d ce 0c 74 46 85 fe 38 |M.AeX<..M..tF..8|
+00000350 95 80 ee 7c c2 bf f2 be a3 c6 bf f3 aa 07 23 40 |...|..........#@|
+00000360 7e cc 74 4a 4e 2e 69 af 6b e0 42 8a fc 41 be 04 |~.tJN.i.k.B..A..|
+00000370 01 00 80 99 ed a8 3a ef 93 1b 4c 17 80 9e cc eb |......:...L.....|
+00000380 da 39 fb c8 9a 73 e1 96 20 3e 41 fa 8b 1a b1 68 |.9...s.. >A....h|
+00000390 cd 47 bc 4b 7b 0c 14 da 87 d3 36 09 5e 37 33 88 |.G.K{.....6.^73.|
+000003a0 7f 88 07 87 46 ec e5 72 a8 59 92 07 fa 4d 02 dc |....F..r.Y...M..|
+000003b0 bf 3a f5 e4 77 0b a6 85 ce 43 ee 1b 90 30 7f ec |.:..w....C...0..|
+000003c0 88 79 f8 88 59 af 6b 7f 2d 88 de 92 cd c8 36 cf |.y..Y.k.-.....6.|
+000003d0 ba b9 08 6a c4 3d d7 9a 48 50 e1 67 d0 62 a5 b3 |...j.=..HP.g.b..|
+000003e0 b0 5f 2e 16 ee 4d 7d a2 cf d9 93 19 89 b7 64 0f |._...M}.......d.|
+000003f0 0f 8e 3d 16 03 03 00 04 0e 00 00 00 |..=.........|
>>> Flow 3 (client to server)
00000000 16 03 03 00 46 10 00 00 42 41 04 1e 18 37 ef 0d |....F...BA...7..|
00000010 19 51 88 35 75 71 b5 e5 54 5b 12 2e 8f 09 67 fd |.Q.5uq..T[....g.|
@@ -79,21 +80,21 @@
00000030 f1 07 9f 6c 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 |...lK[.V.2B.X..I|
00000040 a6 b5 68 1a 41 03 56 6b dc 5a 89 14 03 03 00 01 |..h.A.Vk.Z......|
00000050 01 16 03 03 00 40 00 00 00 00 00 00 00 00 00 00 |.....@..........|
-00000060 00 00 00 00 00 00 58 40 67 61 f4 eb d6 54 b5 f4 |......X@ga...T..|
-00000070 08 d8 27 18 ff 7f c5 58 d1 1e 43 d3 92 74 fe a8 |..'....X..C..t..|
-00000080 a6 f8 09 4e 44 0e 0e 6a 3b 72 7e 12 1f b2 bd 9c |...ND..j;r~.....|
-00000090 f8 f3 c0 f0 4e 5e |....N^|
+00000060 00 00 00 00 00 00 f2 20 58 ec f1 88 a6 26 79 9d |....... X....&y.|
+00000070 2e 9b 02 b5 5e da e2 c1 c5 8d c8 93 6f 6d 07 4e |....^.......om.N|
+00000080 fa dd ee cb b1 ae c7 3b 09 b2 cc 64 7a cd 98 91 |.......;...dz...|
+00000090 cb f8 3c 34 3b ed |..<4;.|
>>> Flow 4 (server to client)
-00000000 14 03 03 00 01 01 16 03 03 00 40 ac 13 13 7a 41 |..........@...zA|
-00000010 ef 34 2e 9c 03 52 01 84 6b c3 f4 67 48 f5 32 fb |.4...R..k..gH.2.|
-00000020 07 b2 6a cf a8 57 c5 7a 16 03 02 b5 9f 90 4c 28 |..j..W.z......L(|
-00000030 65 48 0d e6 43 48 f2 06 22 88 db 90 d9 6e da 07 |eH..CH.."....n..|
-00000040 59 1f 1c 6e af 74 ab 83 68 12 15 |Y..n.t..h..|
+00000000 14 03 03 00 01 01 16 03 03 00 40 4c a1 8d bd 49 |..........@L...I|
+00000010 33 d3 72 fb 2f 23 7e 11 29 fc d2 ff 9b 67 30 c8 |3.r./#~.)....g0.|
+00000020 be c1 bc 51 6e 92 a5 f4 9d e3 b3 f9 d2 d4 c4 a5 |...Qn...........|
+00000030 83 23 90 b3 17 00 35 18 c5 ef 8b 18 a3 cf ed 9d |.#....5.........|
+00000040 a9 52 c9 11 0a c9 55 c2 76 df 78 |.R....U.v.x|
>>> Flow 5 (client to server)
00000000 17 03 03 00 30 00 00 00 00 00 00 00 00 00 00 00 |....0...........|
-00000010 00 00 00 00 00 87 cf e1 7e 13 ec 82 ca 75 e0 4d |........~....u.M|
-00000020 ca 17 a3 de c0 2a 54 b3 3e 4d cf 73 46 c8 a3 cf |.....*T.>M.sF...|
-00000030 ad 54 1c 74 46 15 03 03 00 30 00 00 00 00 00 00 |.T.tF....0......|
-00000040 00 00 00 00 00 00 00 00 00 00 d3 9d a4 fd 16 8d |................|
-00000050 83 1b 7c c2 53 8e 10 7b e3 3c d5 23 8e c4 9c 74 |..|.S..{.<.#...t|
-00000060 86 9e 66 59 81 41 a1 14 8e 59 |..fY.A...Y|
+00000010 00 00 00 00 00 60 40 d0 bf 8f ef 05 2b 89 d7 bb |.....`@.....+...|
+00000020 27 d0 1f b2 cf c3 ff 8e be 69 16 a9 b3 03 e8 3c |'........i.....<|
+00000030 30 1d 58 39 4a 15 03 03 00 30 00 00 00 00 00 00 |0.X9J....0......|
+00000040 00 00 00 00 00 00 00 00 00 00 de 61 51 a1 3c fc |...........aQ.<.|
+00000050 1c 7b e6 f2 7d e0 aa 80 2d 9c e9 22 09 5c dd 8a |.{..}...-..".\..|
+00000060 55 cc c4 77 34 97 05 88 98 d3 |U..w4.....|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-RSA-RC4 b/src/crypto/tls/testdata/Client-TLSv12-RSA-RC4
index b3c112e83d..73e34c0ce3 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-RSA-RC4
+++ b/src/crypto/tls/testdata/Client-TLSv12-RSA-RC4
@@ -1,18 +1,19 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 00 79 01 00 00 75 03 03 00 00 00 00 00 |....y...u.......|
+00000000 16 03 01 00 81 01 00 00 7d 03 03 00 00 00 00 00 |........}.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 1e c0 2f |.............../|
00000030 c0 2b c0 30 c0 2c c0 11 c0 07 c0 13 c0 09 c0 14 |.+.0.,..........|
-00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 2e |...../.5........|
+00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 36 |...../.5.......6|
00000050 00 05 00 05 01 00 00 00 00 00 0a 00 08 00 06 00 |................|
-00000060 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 0a 00 |................|
-00000070 08 04 01 04 03 02 01 02 03 ff 01 00 01 00 |..............|
+00000060 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 0e 00 |................|
+00000070 0c 04 01 04 03 05 01 05 03 02 01 02 03 ff 01 00 |................|
+00000080 01 00 00 12 00 00 |......|
>>> Flow 2 (server to client)
-00000000 16 03 03 00 51 02 00 00 4d 03 03 39 d1 22 07 3f |....Q...M..9.".?|
-00000010 57 87 49 e1 92 8f c8 45 b6 8d 49 f2 dd 91 e0 6f |W.I....E..I....o|
-00000020 86 cd 38 c4 f5 8f d1 f2 ff 13 19 20 5f 98 f8 87 |..8........ _...|
-00000030 8e 6b 63 53 67 65 88 fc e4 02 47 4d 0b 52 bc 0c |.kcSge....GM.R..|
-00000040 8a 08 23 45 74 89 ce 77 ac 15 1c 16 00 05 00 00 |..#Et..w........|
+00000000 16 03 03 00 51 02 00 00 4d 03 03 e8 ae 4c 97 41 |....Q...M....L.A|
+00000010 78 0f 08 84 d4 4a 80 6d a2 e1 d0 67 40 8f 01 8b |x....J.m...g@...|
+00000020 20 54 cb 28 16 52 04 fd 3c c2 84 20 30 96 f0 51 | T.(.R..<.. 0..Q|
+00000030 72 86 6a d8 47 b9 47 e3 a4 ad 97 77 a9 77 1a f9 |r.j.G.G....w.w..|
+00000040 ba 63 33 32 4f 43 09 1c e1 bd 1b 3b 00 05 00 00 |.c32OC.....;....|
00000050 05 ff 01 00 01 00 16 03 03 02 be 0b 00 02 ba 00 |................|
00000060 02 b7 00 02 b4 30 82 02 b0 30 82 02 19 a0 03 02 |.....0...0......|
00000070 01 02 02 09 00 85 b0 bb a4 8a 7f b8 ca 30 0d 06 |.............0..|
@@ -69,15 +70,15 @@
00000060 e6 bd 77 82 6f 23 b6 e0 bd a2 92 b7 3a ac e8 56 |..w.o#......:..V|
00000070 f1 af 54 5e 46 87 e9 3b 33 e7 b8 28 b7 d6 c8 90 |..T^F..;3..(....|
00000080 35 d4 1c 43 d1 30 6f 55 4e 0a 70 14 03 03 00 01 |5..C.0oUN.p.....|
-00000090 01 16 03 03 00 24 d3 d5 a4 0c ae 33 1e d4 d8 ba |.....$.....3....|
-000000a0 67 e5 93 31 e2 e9 08 c8 9e 27 d8 9b 20 d5 59 4d |g..1.....'.. .YM|
-000000b0 d0 f9 d9 bd 82 f7 62 7c 95 0b |......b|..|
+00000090 01 16 03 03 00 24 54 8c 7f 71 03 7c 98 e5 97 65 |.....$T..q.|...e|
+000000a0 51 13 b2 9d 4a b8 c9 c1 e6 11 1b 50 c8 1b c0 46 |Q...J......P...F|
+000000b0 a7 cb 13 97 92 a0 51 d4 a9 e5 |......Q...|
>>> Flow 4 (server to client)
-00000000 14 03 03 00 01 01 16 03 03 00 24 b2 af 7d da e2 |..........$..}..|
-00000010 b4 4f 9e ee 68 d4 bf eb d3 09 63 de 61 e1 c2 12 |.O..h.....c.a...|
-00000020 ba 56 d8 dc 5f 9e 31 fe 1c d4 70 2a 1a 80 3c |.V.._.1...p*..<|
+00000000 14 03 03 00 01 01 16 03 03 00 24 37 ca ae 55 79 |..........$7..Uy|
+00000010 e7 0a 70 55 1e d1 76 61 57 46 d2 c0 d0 ed 3d 70 |..pU..vaWF....=p|
+00000020 1f 02 f2 06 5b 3e 50 ec 13 4b 67 e2 7c bd 45 |....[>P..Kg.|.E|
>>> Flow 5 (client to server)
-00000000 17 03 03 00 1a 43 f5 b5 0e 1b 1f 20 2a 09 27 e5 |.....C..... *.'.|
-00000010 dc 11 cf e6 07 31 2b fc 60 52 86 2b 41 b0 c2 15 |.....1+.`R.+A...|
-00000020 03 03 00 16 6a b9 06 9b c6 e9 6d ad ed 2d cc 0f |....j.....m..-..|
-00000030 bc 0a f1 0c 2d 0d 74 29 17 6b |....-.t).k|
+00000000 17 03 03 00 1a c4 13 68 ec e0 38 a1 07 35 da d7 |.......h..8..5..|
+00000010 c4 6b f9 5c ed a7 8a cb 96 7a 22 7c ca a5 30 15 |.k.\.....z"|..0.|
+00000020 03 03 00 16 f7 a7 8d 41 b0 c1 4b 61 60 b0 b2 ed |.......A..Ka`...|
+00000030 4a ab c3 54 d5 20 eb 67 b7 8f |J..T. .g..|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-SCT b/src/crypto/tls/testdata/Client-TLSv12-SCT
new file mode 100644
index 0000000000..826c9f0a57
--- /dev/null
+++ b/src/crypto/tls/testdata/Client-TLSv12-SCT
@@ -0,0 +1,118 @@
+>>> Flow 1 (client to server)
+00000000 16 03 01 00 81 01 00 00 7d 03 03 00 00 00 00 00 |........}.......|
+00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 1e c0 2f |.............../|
+00000030 c0 2b c0 30 c0 2c c0 11 c0 07 c0 13 c0 09 c0 14 |.+.0.,..........|
+00000040 c0 0a 00 05 00 2f 00 35 c0 12 00 0a 01 00 00 36 |...../.5.......6|
+00000050 00 05 00 05 01 00 00 00 00 00 0a 00 08 00 06 00 |................|
+00000060 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 0e 00 |................|
+00000070 0c 04 01 04 03 05 01 05 03 02 01 02 03 ff 01 00 |................|
+00000080 01 00 00 12 00 00 |......|
+>>> Flow 2 (server to client)
+00000000 16 03 03 01 c6 02 00 01 c2 03 03 1b f6 69 c1 c2 |.............i..|
+00000010 36 77 72 32 69 95 c9 e7 db 9b 5d bd 59 ba 08 02 |6wr2i.....].Y...|
+00000020 1e 76 11 c4 8e 49 08 22 8e 8a 5a 20 44 ec d9 13 |.v...I."..Z D...|
+00000030 23 ad 05 45 48 29 00 c6 11 3d 5a 5c a1 ee 34 2b |#..EH)...=Z\..4+|
+00000040 58 ef 34 5b 7e 42 08 84 23 66 56 ee c0 2f 00 01 |X.4[~B..#fV../..|
+00000050 7a ff 01 00 01 00 00 0b 00 04 03 00 01 02 00 12 |z...............|
+00000060 01 69 01 67 00 75 00 a4 b9 09 90 b4 18 58 14 87 |.i.g.u.......X..|
+00000070 bb 13 a2 cc 67 70 0a 3c 35 98 04 f9 1b df b8 e3 |....gp.<5.......|
+00000080 77 cd 0e c8 0d dc 10 00 00 01 47 97 99 ee 16 00 |w.........G.....|
+00000090 00 04 03 00 46 30 44 02 20 1c 4b 82 5d 95 6e 67 |....F0D. .K.].ng|
+000000a0 5b db 04 95 4b f6 ce f4 32 3e 86 7a 7a 32 ab 18 |[...K...2>.zz2..|
+000000b0 60 74 de 08 da 05 91 4c 2f 02 20 73 54 1b 6e 7f |`t.....L/. sT.n.|
+000000c0 a1 b0 7d 11 bc e6 f3 85 2f 97 66 1a f7 8a e4 10 |..}...../.f.....|
+000000d0 25 8f 12 f4 6f 39 0f d2 9e 18 f0 00 76 00 68 f6 |%...o9......v.h.|
+000000e0 98 f8 1f 64 82 be 3a 8c ee b9 28 1d 4c fc 71 51 |...d..:...(.L.qQ|
+000000f0 5d 67 93 d4 44 d1 0a 67 ac bb 4f 4f fb c4 00 00 |]g..D..g..OO....|
+00000100 01 47 97 e1 b5 70 00 00 04 03 00 47 30 45 02 20 |.G...p.....G0E. |
+00000110 32 21 14 38 06 d8 72 2e 00 30 64 1a e2 e8 6d 4e |2!.8..r..0d...mN|
+00000120 5a e1 d9 42 1e 82 4b 96 25 89 d5 26 13 d3 9c fa |Z..B..K.%..&....|
+00000130 02 21 00 8f 12 28 64 51 4f 44 d5 8c 18 62 23 b2 |.!...(dQOD...b#.|
+00000140 43 93 33 05 f3 43 55 a1 d9 ee cd c5 71 35 91 dd |C.3..CU.....q5..|
+00000150 49 d1 0b 00 76 00 ee 4b bd b7 75 ce 60 ba e1 42 |I...v..K..u.`..B|
+00000160 69 1f ab e1 9e 66 a3 0f 7e 5f b0 72 d8 83 00 c4 |i....f..~_.r....|
+00000170 7b 89 7a a8 fd cb 00 00 01 48 5c 64 8a 87 00 00 |{.z......H\d....|
+00000180 04 03 00 47 30 45 02 20 29 89 d6 b0 53 d3 d2 e9 |...G0E. )...S...|
+00000190 91 bc f1 b5 40 be 1e 2e e7 5c b4 74 27 ed 8f 9b |....@....\.t'...|
+000001a0 02 e9 fa c2 4c ba a2 be 02 21 00 af 43 64 52 71 |....L....!..CdRq|
+000001b0 15 29 58 40 91 c7 08 16 96 03 a8 73 a5 65 a0 6c |.)X@.......s.e.l|
+000001c0 b8 48 56 5a b6 29 83 64 6d 2a 9d 16 03 03 02 be |.HVZ.).dm*......|
+000001d0 0b 00 02 ba 00 02 b7 00 02 b4 30 82 02 b0 30 82 |..........0...0.|
+000001e0 02 19 a0 03 02 01 02 02 09 00 85 b0 bb a4 8a 7f |................|
+000001f0 b8 ca 30 0d 06 09 2a 86 48 86 f7 0d 01 01 05 05 |..0...*.H.......|
+00000200 00 30 45 31 0b 30 09 06 03 55 04 06 13 02 41 55 |.0E1.0...U....AU|
+00000210 31 13 30 11 06 03 55 04 08 13 0a 53 6f 6d 65 2d |1.0...U....Some-|
+00000220 53 74 61 74 65 31 21 30 1f 06 03 55 04 0a 13 18 |State1!0...U....|
+00000230 49 6e 74 65 72 6e 65 74 20 57 69 64 67 69 74 73 |Internet Widgits|
+00000240 20 50 74 79 20 4c 74 64 30 1e 17 0d 31 30 30 34 | Pty Ltd0...1004|
+00000250 32 34 30 39 30 39 33 38 5a 17 0d 31 31 30 34 32 |24090938Z..11042|
+00000260 34 30 39 30 39 33 38 5a 30 45 31 0b 30 09 06 03 |4090938Z0E1.0...|
+00000270 55 04 06 13 02 41 55 31 13 30 11 06 03 55 04 08 |U....AU1.0...U..|
+00000280 13 0a 53 6f 6d 65 2d 53 74 61 74 65 31 21 30 1f |..Some-State1!0.|
+00000290 06 03 55 04 0a 13 18 49 6e 74 65 72 6e 65 74 20 |..U....Internet |
+000002a0 57 69 64 67 69 74 73 20 50 74 79 20 4c 74 64 30 |Widgits Pty Ltd0|
+000002b0 81 9f 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 |..0...*.H.......|
+000002c0 00 03 81 8d 00 30 81 89 02 81 81 00 bb 79 d6 f5 |.....0.......y..|
+000002d0 17 b5 e5 bf 46 10 d0 dc 69 be e6 2b 07 43 5a d0 |....F...i..+.CZ.|
+000002e0 03 2d 8a 7a 43 85 b7 14 52 e7 a5 65 4c 2c 78 b8 |.-.zC...R..eL,x.|
+000002f0 23 8c b5 b4 82 e5 de 1f 95 3b 7e 62 a5 2c a5 33 |#........;~b.,.3|
+00000300 d6 fe 12 5c 7a 56 fc f5 06 bf fa 58 7b 26 3f b5 |...\zV.....X{&?.|
+00000310 cd 04 d3 d0 c9 21 96 4a c7 f4 54 9f 5a bf ef 42 |.....!.J..T.Z..B|
+00000320 71 00 fe 18 99 07 7f 7e 88 7d 7d f1 04 39 c4 a2 |q......~.}}..9..|
+00000330 2e db 51 c9 7c e3 c0 4c 3b 32 66 01 cf af b1 1d |..Q.|..L;2f.....|
+00000340 b8 71 9a 1d db db 89 6b ae da 2d 79 02 03 01 00 |.q.....k..-y....|
+00000350 01 a3 81 a7 30 81 a4 30 1d 06 03 55 1d 0e 04 16 |....0..0...U....|
+00000360 04 14 b1 ad e2 85 5a cf cb 28 db 69 ce 23 69 de |......Z..(.i.#i.|
+00000370 d3 26 8e 18 88 39 30 75 06 03 55 1d 23 04 6e 30 |.&...90u..U.#.n0|
+00000380 6c 80 14 b1 ad e2 85 5a cf cb 28 db 69 ce 23 69 |l......Z..(.i.#i|
+00000390 de d3 26 8e 18 88 39 a1 49 a4 47 30 45 31 0b 30 |..&...9.I.G0E1.0|
+000003a0 09 06 03 55 04 06 13 02 41 55 31 13 30 11 06 03 |...U....AU1.0...|
+000003b0 55 04 08 13 0a 53 6f 6d 65 2d 53 74 61 74 65 31 |U....Some-State1|
+000003c0 21 30 1f 06 03 55 04 0a 13 18 49 6e 74 65 72 6e |!0...U....Intern|
+000003d0 65 74 20 57 69 64 67 69 74 73 20 50 74 79 20 4c |et Widgits Pty L|
+000003e0 74 64 82 09 00 85 b0 bb a4 8a 7f b8 ca 30 0c 06 |td...........0..|
+000003f0 03 55 1d 13 04 05 30 03 01 01 ff 30 0d 06 09 2a |.U....0....0...*|
+00000400 86 48 86 f7 0d 01 01 05 05 00 03 81 81 00 08 6c |.H.............l|
+00000410 45 24 c7 6b b1 59 ab 0c 52 cc f2 b0 14 d7 87 9d |E$.k.Y..R.......|
+00000420 7a 64 75 b5 5a 95 66 e4 c5 2b 8e ae 12 66 1f eb |zdu.Z.f..+...f..|
+00000430 4f 38 b3 6e 60 d3 92 fd f7 41 08 b5 25 13 b1 18 |O8.n`....A..%...|
+00000440 7a 24 fb 30 1d ba ed 98 b9 17 ec e7 d7 31 59 db |z$.0.........1Y.|
+00000450 95 d3 1d 78 ea 50 56 5c d5 82 5a 2d 5a 5f 33 c4 |...x.PV\..Z-Z_3.|
+00000460 b6 d8 c9 75 90 96 8c 0f 52 98 b5 cd 98 1f 89 20 |...u....R...... |
+00000470 5f f2 a0 1c a3 1b 96 94 dd a9 fd 57 e9 70 e8 26 |_..........W.p.&|
+00000480 6d 71 99 9b 26 6e 38 50 29 6c 90 a7 bd d9 16 03 |mq..&n8P)l......|
+00000490 03 00 cd 0c 00 00 c9 03 00 17 41 04 d7 61 5b 05 |..........A..a[.|
+000004a0 de 22 d3 3d 00 72 a5 be 0a c1 76 94 a1 34 41 6e |.".=.r....v..4An|
+000004b0 55 f2 74 91 d2 6f 5c 47 87 c8 4b eb ab ab 10 b9 |U.t..o\G..K.....|
+000004c0 f9 0a bc 63 03 5f 90 5b e3 6f e1 44 97 cc bf d2 |...c._.[.o.D....|
+000004d0 e8 0d f5 9c 2e 9d 07 2c b2 00 90 0b 04 01 00 80 |.......,........|
+000004e0 67 3d c7 73 42 b9 b2 fd 4b dd 02 57 87 95 20 75 |g=.sB...K..W.. u|
+000004f0 da c1 e7 d3 33 09 01 5d e9 32 d7 20 7f 92 a9 dd |....3..].2. ....|
+00000500 bb 17 c5 ee f2 07 b2 04 1d 5e 1f c2 41 66 3f 14 |.........^..Af?.|
+00000510 90 cd 84 ac 49 46 04 3e ce 89 7d 79 42 2a 8c 56 |....IF.>..}yB*.V|
+00000520 93 d3 9c 3b 57 38 9e 91 af 62 ad 86 40 29 3d 46 |...;W8...b..@)=F|
+00000530 c7 cc f4 3f a1 7d ee 53 3d 94 1c 85 b9 1d a9 5f |...?.}.S=......_|
+00000540 10 8e ee 38 5e 98 5d 39 31 79 83 cd f9 02 a8 a9 |...8^.]91y......|
+00000550 b8 82 21 33 40 ed 27 54 a3 6e 64 cb e9 ce dd e1 |..!3@.'T.nd.....|
+00000560 16 03 03 00 04 0e 00 00 00 |.........|
+>>> Flow 3 (client to server)
+00000000 16 03 03 00 46 10 00 00 42 41 04 1e 18 37 ef 0d |....F...BA...7..|
+00000010 19 51 88 35 75 71 b5 e5 54 5b 12 2e 8f 09 67 fd |.Q.5uq..T[....g.|
+00000020 a7 24 20 3e b2 56 1c ce 97 28 5e f8 2b 2d 4f 9e |.$ >.V...(^.+-O.|
+00000030 f1 07 9f 6c 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 |...lK[.V.2B.X..I|
+00000040 a6 b5 68 1a 41 03 56 6b dc 5a 89 14 03 03 00 01 |..h.A.Vk.Z......|
+00000050 01 16 03 03 00 28 00 00 00 00 00 00 00 00 60 0e |.....(........`.|
+00000060 49 99 7a 9f 28 6e 46 03 a8 fd 0e b7 ed bb 9c ba |I.z.(nF.........|
+00000070 07 9c 4d cc 26 2b c2 70 a0 26 38 a0 f2 a0 |..M.&+.p.&8...|
+>>> Flow 4 (server to client)
+00000000 14 03 03 00 01 01 16 03 03 00 28 d2 ef 8f f4 7b |..........(....{|
+00000010 7a 9b c8 98 a4 36 f2 be 61 46 0e af f4 6f 63 71 |z....6..aF...ocq|
+00000020 6e bd 87 ea 1b f2 95 ad 36 7d a3 52 7f b2 b6 45 |n.......6}.R...E|
+00000030 3f 0b 62 |?.b|
+>>> Flow 5 (client to server)
+00000000 17 03 03 00 1e 00 00 00 00 00 00 00 01 53 a1 85 |.............S..|
+00000010 ce 3c c1 64 39 80 fb db 67 ec 48 20 7f e9 82 f4 |.<.d9...g.H ....|
+00000020 2d 69 0a 15 03 03 00 1a 00 00 00 00 00 00 00 02 |-i..............|
+00000030 ab 78 11 1b 80 55 23 db 07 c5 7f c3 5e 19 d8 b3 |.x...U#.....^...|
+00000040 f8 c6 |..|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-ALPN b/src/crypto/tls/testdata/Server-TLSv12-ALPN
index ca804295da..6c7521edcc 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-ALPN
+++ b/src/crypto/tls/testdata/Server-TLSv12-ALPN
@@ -1,7 +1,7 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 01 78 01 00 01 74 03 03 73 99 93 cd 3d |....x...t..s...=|
-00000010 e8 60 23 0d 6a e8 f5 e3 46 ca 38 44 85 ca 79 c8 |.`#.j...F.8D..y.|
-00000020 96 be 94 bd 43 d5 14 2b 20 da 5c 00 00 c4 c0 30 |....C..+ .\....0|
+00000000 16 03 01 01 6b 01 00 01 67 03 03 8c 16 4a 54 65 |....k...g....JTe|
+00000010 9f cf 83 7e 6c da 46 9c 79 cd 7f 67 29 e2 55 af |...~l.F.y..g).U.|
+00000020 c2 d3 24 7b f6 a1 ea 96 43 95 8b 00 00 b6 c0 30 |..${....C......0|
00000030 c0 2c c0 28 c0 24 c0 14 c0 0a 00 a5 00 a3 00 a1 |.,.(.$..........|
00000040 00 9f 00 6b 00 6a 00 69 00 68 00 39 00 38 00 37 |...k.j.i.h.9.8.7|
00000050 00 36 00 88 00 87 00 86 00 85 c0 32 c0 2e c0 2a |.6.........2...*|
@@ -13,16 +13,15 @@
000000b0 00 3c 00 2f 00 96 00 41 00 07 c0 11 c0 07 c0 0c |.<./...A........|
000000c0 c0 02 00 05 00 04 c0 12 c0 08 00 16 00 13 00 10 |................|
000000d0 00 0d c0 0d c0 03 00 0a 00 15 00 12 00 0f 00 0c |................|
-000000e0 00 09 00 14 00 11 00 0e 00 0b 00 08 00 06 00 03 |................|
-000000f0 00 ff 01 00 00 87 00 0b 00 04 03 00 01 02 00 0a |................|
-00000100 00 3a 00 38 00 0e 00 0d 00 19 00 1c 00 0b 00 0c |.:.8............|
-00000110 00 1b 00 18 00 09 00 0a 00 1a 00 16 00 17 00 08 |................|
-00000120 00 06 00 07 00 14 00 15 00 04 00 05 00 12 00 13 |................|
-00000130 00 01 00 02 00 03 00 0f 00 10 00 11 00 23 00 00 |.............#..|
-00000140 00 0d 00 20 00 1e 06 01 06 02 06 03 05 01 05 02 |... ............|
-00000150 05 03 04 01 04 02 04 03 03 01 03 02 03 03 02 01 |................|
-00000160 02 02 02 03 00 0f 00 01 01 00 10 00 10 00 0e 06 |................|
-00000170 70 72 6f 74 6f 32 06 70 72 6f 74 6f 31 |proto2.proto1|
+000000e0 00 09 00 ff 02 01 00 00 87 00 0b 00 04 03 00 01 |................|
+000000f0 02 00 0a 00 3a 00 38 00 0e 00 0d 00 19 00 1c 00 |....:.8.........|
+00000100 0b 00 0c 00 1b 00 18 00 09 00 0a 00 1a 00 16 00 |................|
+00000110 17 00 08 00 06 00 07 00 14 00 15 00 04 00 05 00 |................|
+00000120 12 00 13 00 01 00 02 00 03 00 0f 00 10 00 11 00 |................|
+00000130 23 00 00 00 0d 00 20 00 1e 06 01 06 02 06 03 05 |#..... .........|
+00000140 01 05 02 05 03 04 01 04 02 04 03 03 01 03 02 03 |................|
+00000150 03 02 01 02 02 02 03 00 0f 00 01 01 00 10 00 10 |................|
+00000160 00 0e 06 70 72 6f 74 6f 32 06 70 72 6f 74 6f 31 |...proto2.proto1|
>>> Flow 2 (server to client)
00000000 16 03 03 00 42 02 00 00 3e 03 03 00 00 00 00 00 |....B...>.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
@@ -77,39 +76,40 @@
00000320 35 75 71 b5 e5 54 5b 12 2e 8f 09 67 fd a7 24 20 |5uq..T[....g..$ |
00000330 3e b2 56 1c ce 97 28 5e f8 2b 2d 4f 9e f1 07 9f |>.V...(^.+-O....|
00000340 6c 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 a6 b5 68 |lK[.V.2B.X..I..h|
-00000350 1a 41 03 56 6b dc 5a 89 04 01 00 80 52 f3 4c 3f |.A.Vk.Z.....R.L?|
-00000360 c4 82 3c 4f 8f dc f5 33 c5 12 41 80 dc ea f2 84 |..'.|
+000003c0 91 f6 5b 33 cb e4 ce 74 52 2a c2 d1 d3 e0 44 7d |..[3...tR*....D}|
+000003d0 a1 4f 15 ad 83 29 3a c6 9f 87 22 6d 16 03 03 00 |.O...):..."m....|
000003e0 04 0e 00 00 00 |.....|
>>> Flow 3 (client to server)
-00000000 16 03 03 00 46 10 00 00 42 41 04 e2 86 c1 a0 c0 |....F...BA......|
-00000010 45 9a da 1a 70 a1 3e b6 9c b7 2e ec dd 2b 0a c6 |E...p.>......+..|
-00000020 50 59 95 fe 8e 54 83 06 b6 68 42 60 56 de b2 b3 |PY...T...hB`V...|
-00000030 b9 14 f0 e0 e2 2e a3 7f ec 01 4d 10 8a 43 ab 33 |..........M..C.3|
-00000040 18 f4 b9 5d 6c ae cd 90 3e f4 64 14 03 03 00 01 |...]l...>.d.....|
-00000050 01 16 03 03 00 28 47 e5 15 81 5b f4 a0 6a 61 d6 |.....(G...[..ja.|
-00000060 df 5e 60 f1 d4 dc 55 45 84 0b ef 56 42 0b 42 1d |.^`...UE...VB.B.|
-00000070 28 b4 90 a6 2a 47 41 97 3b 91 5c 74 ab 02 |(...*GA.;.\t..|
+00000000 16 03 03 00 46 10 00 00 42 41 04 cb 9a 95 ae 57 |....F...BA.....W|
+00000010 6e 67 8a 10 30 60 b4 2c 9e f7 18 bb d1 ad 09 96 |ng..0`.,........|
+00000020 02 e3 48 b1 a3 b8 7d 29 42 2b f7 68 f6 8e 2c f8 |..H...})B+.h..,.|
+00000030 b7 12 5c 9c 08 2a b5 8a 13 b2 4c 08 62 e0 83 c0 |..\..*....L.b...|
+00000040 4a 42 6e 5a 35 24 85 2f 58 6c 8c 14 03 03 00 01 |JBnZ5$./Xl......|
+00000050 01 16 03 03 00 28 3f b4 ed a5 a6 03 e0 db 72 cf |.....(?.......r.|
+00000060 a7 c1 a1 32 93 38 8c c4 9d c7 07 44 8d 83 78 5f |...2.8.....D..x_|
+00000070 c1 07 5c 0e b1 a9 5f e3 6d e7 6e 2d 5f d8 |..\..._.m.n-_.|
>>> Flow 4 (server to client)
-00000000 16 03 03 00 72 04 00 00 6e 00 00 00 00 00 68 00 |....r...n.....h.|
-00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 65 |...............e|
-00000020 ea 8b e4 ef ba 19 39 3a 95 90 2b 6d 0d 59 ac 36 |......9:..+m.Y.6|
-00000030 be 71 eb b4 25 51 86 cc 80 43 ea 60 e0 53 30 ba |.q..%Q...C.`.S0.|
-00000040 3e b9 c3 29 9b 26 94 5a 43 36 d0 65 be a7 f1 06 |>..).&.ZC6.e....|
-00000050 99 e3 c5 d7 f2 59 23 11 c5 99 27 5c 7f 43 94 0e |.....Y#...'\.C..|
-00000060 b3 35 7a 66 d9 c4 49 53 2a 28 b6 3d e7 0f c5 d5 |.5zf..IS*(.=....|
-00000070 a2 d8 15 a8 3a 88 f7 14 03 03 00 01 01 16 03 03 |....:...........|
-00000080 00 28 00 00 00 00 00 00 00 00 07 2e 75 1d 9a 12 |.(..........u...|
-00000090 9f e9 7e 0b 42 dd 7b 8e ae 58 ac 49 78 8d fb 3f |..~.B.{..X.Ix..?|
-000000a0 21 e8 ef 91 3c 02 a6 23 d5 cc 17 03 03 00 25 00 |!...<..#......%.|
-000000b0 00 00 00 00 00 00 01 bb 04 db f2 86 63 96 01 60 |............c..`|
-000000c0 bb f4 68 f9 50 2a f0 15 82 f8 a1 73 bf cd 5f 4d |..h.P*.....s.._M|
-000000d0 1a 73 67 91 15 03 03 00 1a 00 00 00 00 00 00 00 |.sg.............|
-000000e0 02 02 79 34 67 e2 67 d5 52 59 91 76 90 10 c8 41 |..y4g.g.RY.v...A|
-000000f0 c5 56 c9 |.V.|
+00000000 16 03 03 00 82 04 00 00 7e 00 00 00 00 00 78 50 |........~.....xP|
+00000010 46 ad c1 db a8 38 86 7b 2b bb fd d0 c3 42 3e 00 |F....8.{+....B>.|
+00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 94 |................|
+00000030 6f ec 80 83 61 69 c5 a3 b0 d3 be 06 98 78 ef 8e |o...ai.......x..|
+00000040 7e 7c 3a 28 a1 54 e5 e3 51 bf f1 ef 57 0a fd b9 |~|:(.T..Q...W...|
+00000050 fb 02 61 ed 27 2b cf fc 0c d9 eb 7a 9e 79 a5 02 |..a.'+.....z.y..|
+00000060 8a 84 f2 26 b2 33 94 03 f7 13 3c da f7 16 f1 f5 |...&.3....<.....|
+00000070 74 95 9d d2 20 05 ab d3 cb 62 af 5a 97 d1 99 bd |t... ....b.Z....|
+00000080 e5 70 48 36 73 bf 2e 14 03 03 00 01 01 16 03 03 |.pH6s...........|
+00000090 00 28 00 00 00 00 00 00 00 00 fd fc dc fd c2 ee |.(..............|
+000000a0 29 c8 b9 87 fc d1 fc ae 8b 71 24 20 75 35 eb a9 |)........q$ u5..|
+000000b0 df 54 72 2c 6e 53 f7 9b 25 c9 17 03 03 00 25 00 |.Tr,nS..%.....%.|
+000000c0 00 00 00 00 00 00 01 91 5a 43 e7 eb f1 72 f5 fa |........ZC...r..|
+000000d0 6d c0 c1 63 47 2e c9 11 d7 20 2d db 0c 98 08 46 |m..cG.... -....F|
+000000e0 7b c8 d7 0e 15 03 03 00 1a 00 00 00 00 00 00 00 |{...............|
+000000f0 02 a0 60 01 85 aa 70 e8 3a ff 89 fc 06 ac 58 a1 |..`...p.:.....X.|
+00000100 43 6e ba |Cn.|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-ALPN-NoMatch b/src/crypto/tls/testdata/Server-TLSv12-ALPN-NoMatch
index 54f2fe864b..40c2b01537 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-ALPN-NoMatch
+++ b/src/crypto/tls/testdata/Server-TLSv12-ALPN-NoMatch
@@ -1,7 +1,7 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 01 78 01 00 01 74 03 03 ba 93 c5 44 7d |....x...t.....D}|
-00000010 cf bf e3 d4 ad 9a ff 3a 48 ec 46 11 1a e5 68 87 |.......:H.F...h.|
-00000020 d1 f0 3b 7c da 86 b9 8f 5d a7 59 00 00 c4 c0 30 |..;|....].Y....0|
+00000000 16 03 01 01 6b 01 00 01 67 03 03 0e ea ae 76 69 |....k...g.....vi|
+00000010 53 2e 97 2f e1 d5 74 fb 93 5c 6a 5a 65 bd 15 c5 |S../..t..\jZe...|
+00000020 7d 41 4c 5e 71 9a 46 9f cc 59 0a 00 00 b6 c0 30 |}AL^q.F..Y.....0|
00000030 c0 2c c0 28 c0 24 c0 14 c0 0a 00 a5 00 a3 00 a1 |.,.(.$..........|
00000040 00 9f 00 6b 00 6a 00 69 00 68 00 39 00 38 00 37 |...k.j.i.h.9.8.7|
00000050 00 36 00 88 00 87 00 86 00 85 c0 32 c0 2e c0 2a |.6.........2...*|
@@ -13,16 +13,15 @@
000000b0 00 3c 00 2f 00 96 00 41 00 07 c0 11 c0 07 c0 0c |.<./...A........|
000000c0 c0 02 00 05 00 04 c0 12 c0 08 00 16 00 13 00 10 |................|
000000d0 00 0d c0 0d c0 03 00 0a 00 15 00 12 00 0f 00 0c |................|
-000000e0 00 09 00 14 00 11 00 0e 00 0b 00 08 00 06 00 03 |................|
-000000f0 00 ff 01 00 00 87 00 0b 00 04 03 00 01 02 00 0a |................|
-00000100 00 3a 00 38 00 0e 00 0d 00 19 00 1c 00 0b 00 0c |.:.8............|
-00000110 00 1b 00 18 00 09 00 0a 00 1a 00 16 00 17 00 08 |................|
-00000120 00 06 00 07 00 14 00 15 00 04 00 05 00 12 00 13 |................|
-00000130 00 01 00 02 00 03 00 0f 00 10 00 11 00 23 00 00 |.............#..|
-00000140 00 0d 00 20 00 1e 06 01 06 02 06 03 05 01 05 02 |... ............|
-00000150 05 03 04 01 04 02 04 03 03 01 03 02 03 03 02 01 |................|
-00000160 02 02 02 03 00 0f 00 01 01 00 10 00 10 00 0e 06 |................|
-00000170 70 72 6f 74 6f 32 06 70 72 6f 74 6f 31 |proto2.proto1|
+000000e0 00 09 00 ff 02 01 00 00 87 00 0b 00 04 03 00 01 |................|
+000000f0 02 00 0a 00 3a 00 38 00 0e 00 0d 00 19 00 1c 00 |....:.8.........|
+00000100 0b 00 0c 00 1b 00 18 00 09 00 0a 00 1a 00 16 00 |................|
+00000110 17 00 08 00 06 00 07 00 14 00 15 00 04 00 05 00 |................|
+00000120 12 00 13 00 01 00 02 00 03 00 0f 00 10 00 11 00 |................|
+00000130 23 00 00 00 0d 00 20 00 1e 06 01 06 02 06 03 05 |#..... .........|
+00000140 01 05 02 05 03 04 01 04 02 04 03 03 01 03 02 03 |................|
+00000150 03 02 01 02 02 02 03 00 0f 00 01 01 00 10 00 10 |................|
+00000160 00 0e 06 70 72 6f 74 6f 32 06 70 72 6f 74 6f 31 |...proto2.proto1|
>>> Flow 2 (server to client)
00000000 16 03 03 00 35 02 00 00 31 03 03 00 00 00 00 00 |....5...1.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
@@ -76,39 +75,40 @@
00000310 19 51 88 35 75 71 b5 e5 54 5b 12 2e 8f 09 67 fd |.Q.5uq..T[....g.|
00000320 a7 24 20 3e b2 56 1c ce 97 28 5e f8 2b 2d 4f 9e |.$ >.V...(^.+-O.|
00000330 f1 07 9f 6c 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 |...lK[.V.2B.X..I|
-00000340 a6 b5 68 1a 41 03 56 6b dc 5a 89 04 01 00 80 52 |..h.A.Vk.Z.....R|
-00000350 78 35 42 fa 35 a6 19 22 d1 03 f4 ed 65 31 ff fe |x5B.5.."....e1..|
-00000360 d6 83 d5 db a1 6b 7d 88 2f 53 7a e8 2a cf a7 e4 |.....k}./Sz.*...|
-00000370 83 0f e7 b6 60 60 91 65 ee ce b0 e9 5c bb 8c fd |....``.e....\...|
-00000380 10 5e c7 17 cb 1b bc db 19 59 23 5d 76 3a f8 87 |.^.......Y#]v:..|
-00000390 d8 2d a7 a2 d8 7b cc e5 f8 82 7c ed bf 08 c4 67 |.-...{....|....g|
-000003a0 c5 f6 a6 5a 2f 9f 59 cb 62 f6 b4 f3 3c d6 f5 dc |...Z/.Y.b...<...|
-000003b0 20 27 d9 14 36 5c a9 8d f6 7b c2 db 9f 84 fc 0d | '..6\...{......|
-000003c0 d3 3a d2 bf 4a 3b 3c 3e 13 eb f9 03 d2 cf 6f 16 |.:..J;<>......o.|
+00000340 a6 b5 68 1a 41 03 56 6b dc 5a 89 05 01 00 80 b3 |..h.A.Vk.Z......|
+00000350 74 f2 d1 0e 38 f2 b2 92 da 2c 48 c7 3f 6a d3 02 |t...8....,H.?j..|
+00000360 60 58 11 9c 08 e4 4c a5 89 c6 94 10 2d 74 3d f4 |`X....L.....-t=.|
+00000370 fd 0b 5b 5c ec 9c 89 00 da db 11 84 28 0f ca 43 |..[\........(..C|
+00000380 6a a7 f7 9c b0 2f 7d a8 6a c5 a1 d1 64 0a a2 04 |j..../}.j...d...|
+00000390 36 3f eb f1 cd d6 91 bc 45 fd e9 7c ac ff 9e 49 |6?......E..|...I|
+000003a0 e3 f1 f4 64 d7 ed 6c 0b ac a7 7e d8 16 8d d6 d8 |...d..l...~.....|
+000003b0 01 d1 24 ef b7 db 47 ed 42 b1 4f a3 89 ac ad 74 |..$...G.B.O....t|
+000003c0 c1 fe 29 9a 59 78 b6 cb c2 07 32 76 8b 8f 5c 16 |..).Yx....2v..\.|
000003d0 03 03 00 04 0e 00 00 00 |........|
>>> Flow 3 (client to server)
-00000000 16 03 03 00 46 10 00 00 42 41 04 f2 52 42 97 0a |....F...BA..RB..|
-00000010 df a1 e0 cb 4e 5e 3c e5 45 0e de b3 eb 3d cd c2 |....N^<.E....=..|
-00000020 78 77 ff ec 6e 74 c2 e5 9e 89 58 6f 2b bc 41 5b |xw..nt....Xo+.A[|
-00000030 d5 8f d0 ea ce c6 c9 11 74 0a c1 33 2a 52 c2 30 |........t..3*R.0|
-00000040 73 08 5f 20 f2 0a 45 95 81 a8 eb 14 03 03 00 01 |s._ ..E.........|
-00000050 01 16 03 03 00 28 52 9e 4c 11 49 07 9f b5 4b 2f |.....(R.L.I...K/|
-00000060 45 79 0c d9 cb ae 45 7d 17 1e c2 5a d3 ea bd 8b |Ey....E}...Z....|
-00000070 0d 94 b1 40 a2 56 6e a5 f8 a2 5b f8 63 73 |...@.Vn...[.cs|
+00000000 16 03 03 00 46 10 00 00 42 41 04 3a 5c 78 52 d4 |....F...BA.:\xR.|
+00000010 63 c4 7d 04 76 71 2a db 9d c2 f7 71 10 4c d3 2f |c.}.vq*....q.L./|
+00000020 35 9e 35 21 93 d0 ca 00 e7 35 ca 8d 18 d8 ad 07 |5.5!.....5......|
+00000030 9b ca e2 34 bf 84 5e 2a 51 08 01 98 66 5d 5a 94 |...4..^*Q...f]Z.|
+00000040 28 06 b4 bd 2b 47 05 16 d1 15 04 14 03 03 00 01 |(...+G..........|
+00000050 01 16 03 03 00 28 db 5d dd 42 7a 90 10 bd 7d c9 |.....(.].Bz...}.|
+00000060 ab d5 9b a9 28 03 13 b9 c7 8d fa 49 81 3d 14 a2 |....(......I.=..|
+00000070 f5 b1 14 d9 26 52 ee 29 15 b5 3c d9 d6 a6 |....&R.)..<...|
>>> Flow 4 (server to client)
-00000000 16 03 03 00 72 04 00 00 6e 00 00 00 00 00 68 00 |....r...n.....h.|
-00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 65 |...............e|
-00000020 ea 8b e4 ef ba f6 cb 68 be 7f f0 66 1a c6 3c c6 |.......h...f..<.|
-00000030 ee 5f 60 3a 62 20 c5 e8 ea 99 92 84 c1 45 a1 76 |._`:b .......E.v|
-00000040 7c a7 f2 cd 40 72 9b 38 51 77 f2 ae 54 dd 67 37 ||...@r.8Qw..T.g7|
-00000050 f8 98 43 2e 55 59 23 3b 50 26 87 ca 6b 2d 45 d6 |..C.UY#;P&..k-E.|
-00000060 3c 85 29 f4 52 58 83 98 ae ad a9 64 8b d1 cc 9c |<.).RX.....d....|
-00000070 88 3f a8 f9 d2 d3 33 14 03 03 00 01 01 16 03 03 |.?....3.........|
-00000080 00 28 00 00 00 00 00 00 00 00 84 6d 6d 57 fb dc |.(.........mmW..|
-00000090 09 54 c4 9a fc d7 dd 45 f5 c3 57 fd e9 16 76 ab |.T.....E..W...v.|
-000000a0 a8 85 eb 34 e7 21 30 85 56 ed 17 03 03 00 25 00 |...4.!0.V.....%.|
-000000b0 00 00 00 00 00 00 01 05 62 69 79 cb c0 74 ad 64 |........biy..t.d|
-000000c0 0a 0c 2a 10 2a b7 8e e2 92 6e 12 3b d7 64 df d7 |..*.*....n.;.d..|
-000000d0 4f da 52 c6 15 03 03 00 1a 00 00 00 00 00 00 00 |O.R.............|
-000000e0 02 b9 dc 49 b9 2a 12 58 3a 4b 4c e0 c8 b2 e9 d9 |...I.*.X:KL.....|
-000000f0 dc 48 17 |.H.|
+00000000 16 03 03 00 82 04 00 00 7e 00 00 00 00 00 78 50 |........~.....xP|
+00000010 46 ad c1 db a8 38 86 7b 2b bb fd d0 c3 42 3e 00 |F....8.{+....B>.|
+00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 94 |................|
+00000030 6f ec 80 83 61 32 9c da f8 14 9d 56 31 08 89 38 |o...a2.....V1..8|
+00000040 f5 fc dc 73 bf c6 bc 1d 48 7c 64 41 71 7c b7 2b |...s....H|dAq|.+|
+00000050 4a b5 2f d5 1b bf 21 e0 56 29 bd e9 e5 d0 80 ab |J./...!.V)......|
+00000060 d1 d9 d9 81 77 33 94 45 2e 27 4c 22 e3 2b 1f 33 |....w3.E.'L".+.3|
+00000070 96 86 9d 0c 70 ad 1e 56 50 14 11 b6 b1 40 7e f5 |....p..VP....@~.|
+00000080 44 27 70 61 52 d0 66 14 03 03 00 01 01 16 03 03 |D'paR.f.........|
+00000090 00 28 00 00 00 00 00 00 00 00 5a e0 b2 53 80 89 |.(........Z..S..|
+000000a0 40 81 02 bc 62 d0 83 7f fb 95 52 98 4d 2d 7f 70 |@...b.....R.M-.p|
+000000b0 2e 76 43 2b 60 3c fd 4b 0e e4 17 03 03 00 25 00 |.vC+`<.K......%.|
+000000c0 00 00 00 00 00 00 01 e9 04 b5 f5 92 e7 ac 20 e5 |.............. .|
+000000d0 1c 88 5c c3 c4 21 87 cf 3a 81 04 2e 50 70 c7 20 |..\..!..:...Pp. |
+000000e0 3b e5 e7 21 15 03 03 00 1a 00 00 00 00 00 00 00 |;..!............|
+000000f0 02 8a 92 dd 25 70 1b 32 86 22 d0 29 79 ff 06 f4 |....%p.2.".)y...|
+00000100 c3 82 9f |...|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-CipherSuiteCertPreferenceECDSA b/src/crypto/tls/testdata/Server-TLSv12-CipherSuiteCertPreferenceECDSA
index cc06d5f838..160bf83db5 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-CipherSuiteCertPreferenceECDSA
+++ b/src/crypto/tls/testdata/Server-TLSv12-CipherSuiteCertPreferenceECDSA
@@ -1,7 +1,7 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 01 61 01 00 01 5d 03 03 47 c5 84 0f 55 |....a...]..G...U|
-00000010 83 4d 4a 1c 48 51 15 e4 74 72 84 70 2f 24 e9 ab |.MJ.HQ..tr.p/$..|
-00000020 42 1e 01 e1 85 27 2f b5 c1 43 14 00 00 c4 c0 30 |B....'/..C.....0|
+00000000 16 03 01 01 53 01 00 01 4f 03 03 0b 19 5d 5b 51 |....S...O....][Q|
+00000010 85 cc 1a 6b 6e 46 0d 63 07 9e 68 f0 36 e1 50 1e |...knF.c..h.6.P.|
+00000020 e0 f5 28 24 ab 7e bf 5a 4d 4b f6 00 00 b6 c0 30 |..($.~.ZMK.....0|
00000030 c0 2c c0 28 c0 24 c0 14 c0 0a 00 a5 00 a3 00 a1 |.,.(.$..........|
00000040 00 9f 00 6b 00 6a 00 69 00 68 00 39 00 38 00 37 |...k.j.i.h.9.8.7|
00000050 00 36 00 88 00 87 00 86 00 85 c0 32 c0 2e c0 2a |.6.........2...*|
@@ -13,15 +13,14 @@
000000b0 00 3c 00 2f 00 96 00 41 00 07 c0 11 c0 07 c0 0c |.<./...A........|
000000c0 c0 02 00 05 00 04 c0 12 c0 08 00 16 00 13 00 10 |................|
000000d0 00 0d c0 0d c0 03 00 0a 00 15 00 12 00 0f 00 0c |................|
-000000e0 00 09 00 14 00 11 00 0e 00 0b 00 08 00 06 00 03 |................|
-000000f0 00 ff 02 01 00 00 6f 00 0b 00 04 03 00 01 02 00 |......o.........|
-00000100 0a 00 3a 00 38 00 0e 00 0d 00 19 00 1c 00 0b 00 |..:.8...........|
-00000110 0c 00 1b 00 18 00 09 00 0a 00 1a 00 16 00 17 00 |................|
-00000120 08 00 06 00 07 00 14 00 15 00 04 00 05 00 12 00 |................|
-00000130 13 00 01 00 02 00 03 00 0f 00 10 00 11 00 0d 00 |................|
-00000140 20 00 1e 06 01 06 02 06 03 05 01 05 02 05 03 04 | ...............|
-00000150 01 04 02 04 03 03 01 03 02 03 03 02 01 02 02 02 |................|
-00000160 03 00 0f 00 01 01 |......|
+000000e0 00 09 00 ff 02 01 00 00 6f 00 0b 00 04 03 00 01 |........o.......|
+000000f0 02 00 0a 00 3a 00 38 00 0e 00 0d 00 19 00 1c 00 |....:.8.........|
+00000100 0b 00 0c 00 1b 00 18 00 09 00 0a 00 1a 00 16 00 |................|
+00000110 17 00 08 00 06 00 07 00 14 00 15 00 04 00 05 00 |................|
+00000120 12 00 13 00 01 00 02 00 03 00 0f 00 10 00 11 00 |................|
+00000130 0d 00 20 00 1e 06 01 06 02 06 03 05 01 05 02 05 |.. .............|
+00000140 03 04 01 04 02 04 03 03 01 03 02 03 03 02 01 02 |................|
+00000150 02 02 03 00 0f 00 01 01 |........|
>>> Flow 2 (server to client)
00000000 16 03 03 00 31 02 00 00 2d 03 03 00 00 00 00 00 |....1...-.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
@@ -64,38 +63,38 @@
00000260 75 71 b5 e5 54 5b 12 2e 8f 09 67 fd a7 24 20 3e |uq..T[....g..$ >|
00000270 b2 56 1c ce 97 28 5e f8 2b 2d 4f 9e f1 07 9f 6c |.V...(^.+-O....l|
00000280 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 a6 b5 68 1a |K[.V.2B.X..I..h.|
-00000290 41 03 56 6b dc 5a 89 04 03 00 8a 30 81 87 02 42 |A.Vk.Z.....0...B|
-000002a0 00 e5 db 39 b2 73 2b 4b 19 66 d6 d6 de d2 ed ae |...9.s+K.f......|
-000002b0 0c ac 74 96 12 b2 e0 87 73 7c 63 18 8c 58 3f 56 |..t.....s|c..X?V|
-000002c0 4c fe 0f a5 2d b9 b8 1c 7d 4d 49 b9 ca f0 52 01 |L...-...}MI...R.|
-000002d0 12 e2 a9 54 9f 4d ab b7 93 71 3c 1b 96 b0 87 8b |...T.M...q<.....|
-000002e0 87 c3 02 41 79 c3 50 88 2f 9a b8 a3 f0 14 63 ee |...Ay.P./.....c.|
-000002f0 d6 76 dd d4 1d 1c ce 4c ba 53 40 ac 01 d9 62 a7 |.v.....L.S@...b.|
-00000300 bc ee 66 67 fc da f4 b3 0f fd 50 5d 31 0e 2d 41 |..fg......P]1.-A|
-00000310 64 d5 51 30 a3 0e ee 20 f9 9d 0e 11 df 68 a6 f4 |d.Q0... .....h..|
-00000320 54 d4 54 7a 05 16 03 03 00 04 0e 00 00 00 |T.Tz..........|
+00000290 41 03 56 6b dc 5a 89 05 03 00 8a 30 81 87 02 42 |A.Vk.Z.....0...B|
+000002a0 01 c0 83 f1 6b 74 4d 1f f5 51 d3 df 44 27 ee 1b |....ktM..Q..D'..|
+000002b0 32 e8 93 a5 e5 9c c7 df 8e 82 bf c1 0a 4a 52 df |2............JR.|
+000002c0 b8 f3 53 15 88 a1 c0 ae 0a b6 f9 35 ca 6f 52 a1 |..S........5.oR.|
+000002d0 de 45 5d 4d 96 6e 9e b6 10 a1 e8 8d 2b 93 e3 9d |.E]M.n......+...|
+000002e0 85 30 02 41 6d 41 10 65 8a d3 fe a7 9c 4e 06 a4 |.0.AmA.e.....N..|
+000002f0 8e aa 9f af e8 ec 49 f1 2c 2b 72 62 df 2f d7 ef |......I.,+rb./..|
+00000300 da b1 b2 a3 d6 29 dc 7a 8b 11 b7 28 01 f2 6e 89 |.....).z...(..n.|
+00000310 20 dc f6 44 65 56 00 b2 4d c5 46 54 ea a4 09 bc | ..DeV..M.FT....|
+00000320 23 94 d3 52 c1 16 03 03 00 04 0e 00 00 00 |#..R..........|
>>> Flow 3 (client to server)
-00000000 16 03 03 00 46 10 00 00 42 41 04 00 a1 de 66 1d |....F...BA....f.|
-00000010 05 57 3b d2 0e 5f ba 4d e9 b7 93 0a 3e bd 25 98 |.W;.._.M....>.%.|
-00000020 76 a4 8a c9 d3 c8 04 85 f6 8d 4f 3e 32 4c 25 cb |v.........O>2L%.|
-00000030 b5 57 86 b5 04 9d f6 ba f3 17 c8 43 cb eb 8b d0 |.W.........C....|
-00000040 ed 99 23 c7 4d 63 95 e2 cd 52 ba 14 03 03 00 01 |..#.Mc...R......|
-00000050 01 16 03 03 00 40 33 b4 f5 4a 64 88 ef dc b6 e5 |.....@3..Jd.....|
-00000060 b6 1e 40 3e 64 5a 2c 11 97 69 4c b6 7a 4c 9a 8a |..@>dZ,..iL.zL..|
-00000070 71 f2 60 e9 39 db 96 2a a3 33 1b 7f 43 15 8b 99 |q.`.9..*.3..C...|
-00000080 0b 52 c2 92 86 fe 57 0d da fe 62 44 89 ce 65 4f |.R....W...bD..eO|
-00000090 a5 8d 29 8e 10 50 |..)..P|
+00000000 16 03 03 00 46 10 00 00 42 41 04 a0 ef 6e 6e 4f |....F...BA...nnO|
+00000010 3f ac d1 89 57 cf 8e da 25 46 b5 50 51 68 bc 06 |?...W...%F.PQh..|
+00000020 6a b6 0b a5 82 c3 3d c3 d2 d6 70 ac 32 35 bf 62 |j.....=...p.25.b|
+00000030 e9 6f 8f a6 8b 89 68 04 a3 57 0b 7b 2b 33 a3 01 |.o....h..W.{+3..|
+00000040 b1 7e d3 7b 04 6c 68 b8 bc da ff 14 03 03 00 01 |.~.{.lh.........|
+00000050 01 16 03 03 00 40 04 25 dd 22 f7 7a 2b 55 0d f9 |.....@.%.".z+U..|
+00000060 cc 9c 3d 59 bc 4b 99 86 99 5f c2 75 fd 62 d7 6e |..=Y.K..._.u.b.n|
+00000070 78 89 90 af 61 59 0f d3 7b 1a 3f 3b 87 09 b5 7c |x...aY..{.?;...||
+00000080 fd 75 cc 5c f0 41 80 03 8c 7a 1d b0 50 34 32 95 |.u.\.A...z..P42.|
+00000090 8d 74 95 f9 54 cc |.t..T.|
>>> Flow 4 (server to client)
00000000 14 03 03 00 01 01 16 03 03 00 40 00 00 00 00 00 |..........@.....|
-00000010 00 00 00 00 00 00 00 00 00 00 00 29 56 67 0c 28 |...........)Vg.(|
-00000020 ca 74 e1 ae c6 e2 30 3b f2 8b f0 fd c2 eb 11 c0 |.t....0;........|
-00000030 0e 50 eb d8 4e de e3 32 6b 69 77 d8 d7 bd 94 30 |.P..N..2kiw....0|
-00000040 1e bf 03 f0 31 98 d8 07 c0 27 4b 17 03 03 00 40 |....1....'K....@|
+00000010 00 00 00 00 00 00 00 00 00 00 00 bf e6 3e cf 4a |.............>.J|
+00000020 ab 55 af 47 2b fe 01 8e 4a 9e 95 e5 39 16 b1 17 |.U.G+...J...9...|
+00000030 66 93 20 80 5a e9 cc f1 16 79 4f 37 20 60 40 c7 |f. .Z....yO7 `@.|
+00000040 c3 91 28 14 9d 35 02 72 df 66 17 17 03 03 00 40 |..(..5.r.f.....@|
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
-00000060 97 7f df af a3 9e cc a1 64 5f ab 91 8a 6f fd 19 |........d_...o..|
-00000070 be 94 95 6d bb de 12 a9 54 10 b5 95 f3 68 77 73 |...m....T....hws|
-00000080 14 09 b7 3b ca b8 88 6d fd 0a 2d 24 c1 94 ce ce |...;...m..-$....|
+00000060 ac 56 df 2e 4d 72 cb cf 8a 5e e7 36 8b 12 12 f3 |.V..Mr...^.6....|
+00000070 f8 31 9a 81 b9 2a 75 00 7f c7 2c 3b 86 bf 54 ac |.1...*u...,;..T.|
+00000080 76 97 d8 e2 c5 a4 d3 2d 15 64 5e 8a 0e c9 46 f5 |v......-.d^...F.|
00000090 15 03 03 00 30 00 00 00 00 00 00 00 00 00 00 00 |....0...........|
-000000a0 00 00 00 00 00 04 6d b7 9e 15 51 c7 f8 de ab d0 |......m...Q.....|
-000000b0 a0 45 7f 4f bc 59 73 45 e9 a8 b1 0e 9b c3 36 c7 |.E.O.YsE......6.|
-000000c0 cb db 55 19 db |..U..|
+000000a0 00 00 00 00 00 9e e7 4b 50 d0 7c a9 dd 0c 3f 9b |.......KP.|...?.|
+000000b0 04 c9 f5 f0 88 e3 84 c0 69 05 c2 c0 83 43 5a 8c |........i....CZ.|
+000000c0 be e4 c4 35 f0 |...5.|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-CipherSuiteCertPreferenceRSA b/src/crypto/tls/testdata/Server-TLSv12-CipherSuiteCertPreferenceRSA
index 0d9bb0c8b7..f25f3e79e2 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-CipherSuiteCertPreferenceRSA
+++ b/src/crypto/tls/testdata/Server-TLSv12-CipherSuiteCertPreferenceRSA
@@ -1,7 +1,7 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 01 61 01 00 01 5d 03 03 17 83 43 01 d8 |....a...]....C..|
-00000010 14 04 ed 55 41 35 99 cd f6 50 e6 47 10 60 7d d9 |...UA5...P.G.`}.|
-00000020 d1 f4 0f a1 bf 27 ab 0c 44 56 a5 00 00 c4 c0 30 |.....'..DV.....0|
+00000000 16 03 01 01 53 01 00 01 4f 03 03 66 97 e7 64 f3 |....S...O..f..d.|
+00000010 e7 d2 ba ba 31 f5 d6 ce 50 c7 48 2e 80 48 f1 1f |....1...P.H..H..|
+00000020 6a a0 f1 da 7b 7b 45 ac ad e9 73 00 00 b6 c0 30 |j...{{E...s....0|
00000030 c0 2c c0 28 c0 24 c0 14 c0 0a 00 a5 00 a3 00 a1 |.,.(.$..........|
00000040 00 9f 00 6b 00 6a 00 69 00 68 00 39 00 38 00 37 |...k.j.i.h.9.8.7|
00000050 00 36 00 88 00 87 00 86 00 85 c0 32 c0 2e c0 2a |.6.........2...*|
@@ -13,15 +13,14 @@
000000b0 00 3c 00 2f 00 96 00 41 00 07 c0 11 c0 07 c0 0c |.<./...A........|
000000c0 c0 02 00 05 00 04 c0 12 c0 08 00 16 00 13 00 10 |................|
000000d0 00 0d c0 0d c0 03 00 0a 00 15 00 12 00 0f 00 0c |................|
-000000e0 00 09 00 14 00 11 00 0e 00 0b 00 08 00 06 00 03 |................|
-000000f0 00 ff 02 01 00 00 6f 00 0b 00 04 03 00 01 02 00 |......o.........|
-00000100 0a 00 3a 00 38 00 0e 00 0d 00 19 00 1c 00 0b 00 |..:.8...........|
-00000110 0c 00 1b 00 18 00 09 00 0a 00 1a 00 16 00 17 00 |................|
-00000120 08 00 06 00 07 00 14 00 15 00 04 00 05 00 12 00 |................|
-00000130 13 00 01 00 02 00 03 00 0f 00 10 00 11 00 0d 00 |................|
-00000140 20 00 1e 06 01 06 02 06 03 05 01 05 02 05 03 04 | ...............|
-00000150 01 04 02 04 03 03 01 03 02 03 03 02 01 02 02 02 |................|
-00000160 03 00 0f 00 01 01 |......|
+000000e0 00 09 00 ff 02 01 00 00 6f 00 0b 00 04 03 00 01 |........o.......|
+000000f0 02 00 0a 00 3a 00 38 00 0e 00 0d 00 19 00 1c 00 |....:.8.........|
+00000100 0b 00 0c 00 1b 00 18 00 09 00 0a 00 1a 00 16 00 |................|
+00000110 17 00 08 00 06 00 07 00 14 00 15 00 04 00 05 00 |................|
+00000120 12 00 13 00 01 00 02 00 03 00 0f 00 10 00 11 00 |................|
+00000130 0d 00 20 00 1e 06 01 06 02 06 03 05 01 05 02 05 |.. .............|
+00000140 03 04 01 04 02 04 03 03 01 03 02 03 03 02 01 02 |................|
+00000150 02 02 03 00 0f 00 01 01 |........|
>>> Flow 2 (server to client)
00000000 16 03 03 00 31 02 00 00 2d 03 03 00 00 00 00 00 |....1...-.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
@@ -75,38 +74,38 @@
00000310 75 71 b5 e5 54 5b 12 2e 8f 09 67 fd a7 24 20 3e |uq..T[....g..$ >|
00000320 b2 56 1c ce 97 28 5e f8 2b 2d 4f 9e f1 07 9f 6c |.V...(^.+-O....l|
00000330 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 a6 b5 68 1a |K[.V.2B.X..I..h.|
-00000340 41 03 56 6b dc 5a 89 04 01 00 80 7f 65 76 11 35 |A.Vk.Z......ev.5|
-00000350 e1 9c c6 0c 21 41 d6 b4 22 2f a3 02 57 5c 40 5c |....!A.."/..W\@\|
-00000360 2e 0c 5f 07 01 d1 78 29 a3 7b 65 37 1c c6 51 a8 |.._...x).{e7..Q.|
-00000370 e1 70 b4 73 9a cf 37 73 c8 ce 7c 8b 60 9e 0f e4 |.p.s..7s..|.`...|
-00000380 d7 2e 2a a8 fd 5a 0c 8a e7 e0 4c ca 0b 28 6d ea |..*..Z....L..(m.|
-00000390 39 da 9f ac 6c 23 f0 c6 fe 21 a8 ad fb e8 c9 6d |9...l#...!.....m|
-000003a0 96 86 75 4d 88 f0 e8 71 e0 dc 32 b9 81 f9 f3 fe |..uM...q..2.....|
-000003b0 64 e1 34 62 dc e2 0a 21 a3 7e 70 0d b0 f3 9d 13 |d.4b...!.~p.....|
-000003c0 5c 81 58 24 97 a9 fb 1d 99 60 a7 16 03 03 00 04 |\.X$.....`......|
+00000340 41 03 56 6b dc 5a 89 05 01 00 80 32 97 6b fe 26 |A.Vk.Z.....2.k.&|
+00000350 2f 9d b8 7d 5e cd 30 1b 24 e8 e5 d7 fd 35 7b 7b |/..}^.0.$....5{{|
+00000360 43 23 64 ea 69 a8 30 7d 72 f4 bf 02 f5 6d bd 7e |C#d.i.0}r....m.~|
+00000370 52 98 a2 a8 f4 43 6e 9f bc 02 2e 5f 08 21 6e b2 |R....Cn...._.!n.|
+00000380 63 d7 63 4a b8 74 57 52 6c 04 a1 b1 1d 2c 1b ab |c.cJ.tWRl....,..|
+00000390 ff ba e8 cf 04 eb 18 66 4d 4b 62 b8 27 e3 24 9b |.......fMKb.'.$.|
+000003a0 5f 73 19 dc 5c 20 75 c5 e3 09 bc 00 34 75 c8 fe |_s..\ u.....4u..|
+000003b0 b2 83 83 0f 0a b3 64 d7 52 97 d2 34 54 cf 54 88 |......d.R..4T.T.|
+000003c0 fa d7 0b cf 56 91 de 96 2f 35 de 16 03 03 00 04 |....V.../5......|
000003d0 0e 00 00 00 |....|
>>> Flow 3 (client to server)
-00000000 16 03 03 00 46 10 00 00 42 41 04 27 9e 27 b7 bf |....F...BA.'.'..|
-00000010 b1 a9 b0 5b 7d 1c 9a 02 51 6e 03 ba 60 5a a2 50 |...[}...Qn..`Z.P|
-00000020 17 a6 2a e4 02 cd 6f ed a2 97 49 49 0c 36 70 53 |..*...o...II.6pS|
-00000030 ce 0c 79 49 14 55 9a 7f a6 0a 10 65 4f e4 c6 10 |..yI.U.....eO...|
-00000040 c2 31 68 5c 0e 5c ab 8a a8 1c 52 14 03 03 00 01 |.1h\.\....R.....|
-00000050 01 16 03 03 00 40 25 59 40 9e 5b 9a d5 95 a1 59 |.....@%Y@.[....Y|
-00000060 d4 1d ea 0c 02 d1 66 29 c2 d5 d4 24 7a c7 9e 47 |......f)...$z..G|
-00000070 f7 79 a1 5f 72 fb c8 10 61 81 e8 e3 fb 16 7e 30 |.y._r...a.....~0|
-00000080 e2 cc 95 d2 24 2a 0f 94 96 b6 0a 27 1a 87 c9 36 |....$*.....'...6|
-00000090 c7 b4 e9 21 d1 94 |...!..|
+00000000 16 03 03 00 46 10 00 00 42 41 04 f8 15 aa a4 68 |....F...BA.....h|
+00000010 80 59 4e 9f b5 d5 3c e5 da 4b 89 36 bf 26 cb 69 |.YN...<..K.6.&.i|
+00000020 e8 99 d8 a5 3e af 1c ac b6 bc c9 dd c5 0b 7e d8 |....>.........~.|
+00000030 c6 6a 41 80 8d cd 1a 06 23 cd 5a db ec 67 77 fe |.jA.....#.Z..gw.|
+00000040 e6 cc 6f bb ea b6 5d e7 03 c9 45 14 03 03 00 01 |..o...]...E.....|
+00000050 01 16 03 03 00 40 7d 9d 03 ca 10 11 dc 09 60 23 |.....@}.......`#|
+00000060 25 db b3 d2 98 2c 37 9e d1 de 45 01 3e d7 90 ff |%....,7...E.>...|
+00000070 48 df 65 3f 75 d6 50 21 38 c4 df 0d 24 04 33 54 |H.e?u.P!8...$.3T|
+00000080 4f 3b 95 80 a6 ab 63 07 83 5b a0 e8 68 60 95 03 |O;....c..[..h`..|
+00000090 6d 33 6e dd 88 56 |m3n..V|
>>> Flow 4 (server to client)
00000000 14 03 03 00 01 01 16 03 03 00 40 00 00 00 00 00 |..........@.....|
-00000010 00 00 00 00 00 00 00 00 00 00 00 fe 1d f1 25 18 |..............%.|
-00000020 dd 7a 1e 10 f0 86 7f 75 74 44 a5 89 92 c8 21 ff |.z.....utD....!.|
-00000030 b8 1b bf 79 3f 19 8e 12 04 65 58 a7 e5 96 52 3a |...y?....eX...R:|
-00000040 15 af 57 d8 9e 46 6f 3f 0d 89 67 17 03 03 00 40 |..W..Fo?..g....@|
+00000010 00 00 00 00 00 00 00 00 00 00 00 74 52 26 02 91 |...........tR&..|
+00000020 cb 32 d9 03 3f c3 cb 31 71 19 64 8f 12 96 87 22 |.2..?..1q.d...."|
+00000030 ae da 10 2d 7d 37 d0 38 e3 b8 8d 21 ea 44 c4 3c |...-}7.8...!.D.<|
+00000040 36 ea 0a 38 07 37 ea a2 c3 e8 02 17 03 03 00 40 |6..8.7.........@|
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
-00000060 1b fb 13 e3 c0 eb 94 bb 4f e1 9e 47 6a ce 9b 6c |........O..Gj..l|
-00000070 03 2f bf 0b 2f 08 36 48 b2 00 26 ab fb cc 3c 2f |./../.6H..&...|
-00000080 8d 99 82 86 a1 b7 15 04 f0 59 97 d7 50 61 1c a1 |.........Y..Pa..|
+00000060 24 49 ee a7 14 10 1d f9 93 9f 03 63 ab ff 54 9a |$I.........c..T.|
+00000070 9d 26 3d 0d d1 3d ca 5b ff 69 c8 17 89 fb 7c d2 |.&=..=.[.i....|.|
+00000080 4b a6 cd d6 91 e3 89 10 1a 6d f4 f5 ae bc 6e 96 |K........m....n.|
00000090 15 03 03 00 30 00 00 00 00 00 00 00 00 00 00 00 |....0...........|
-000000a0 00 00 00 00 00 95 ed 23 80 9e f5 f2 37 ac 2d 50 |.......#....7.-P|
-000000b0 3d 30 de 68 be 25 c0 72 cc c9 2d 80 41 82 f7 0e |=0.h.%.r..-.A...|
-000000c0 2e 86 0e 07 41 |....A|
+000000a0 00 00 00 00 00 1f 11 a1 c3 1d 1b d0 63 09 77 2f |............c.w/|
+000000b0 1a 6a 2c 7b cc f5 01 74 4a a4 81 36 a8 70 c6 96 |.j,{...tJ..6.p..|
+000000c0 fa 12 8e dc bf |.....|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-ClientAuthRequestedAndECDSAGiven b/src/crypto/tls/testdata/Server-TLSv12-ClientAuthRequestedAndECDSAGiven
index 547f79834d..048d10b59e 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-ClientAuthRequestedAndECDSAGiven
+++ b/src/crypto/tls/testdata/Server-TLSv12-ClientAuthRequestedAndECDSAGiven
@@ -1,11 +1,10 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 00 5c 01 00 00 58 03 03 52 cc 57 59 65 |....\...X..R.WYe|
-00000010 ae b3 ec a4 7a 05 f7 ec 39 22 7d 8c 91 96 6b e0 |....z...9"}...k.|
-00000020 69 81 ff 88 28 17 60 ac 94 19 ff 00 00 04 00 05 |i...(.`.........|
-00000030 00 ff 01 00 00 2b 00 0d 00 22 00 20 06 01 06 02 |.....+...". ....|
-00000040 06 03 05 01 05 02 05 03 04 01 04 02 04 03 03 01 |................|
-00000050 03 02 03 03 02 01 02 02 02 03 01 01 00 0f 00 01 |................|
-00000060 01 |.|
+00000000 16 03 01 00 5b 01 00 00 57 03 03 54 2a 20 9e ee |....[...W..T* ..|
+00000010 10 ae 28 a1 a8 5a fd 71 a8 f7 23 35 24 60 dd 25 |..(..Z.q..#5$`.%|
+00000020 c6 fe 46 f6 b3 9d 09 0f 5f 0f af 00 00 04 00 05 |..F....._.......|
+00000030 00 ff 02 01 00 00 29 00 0d 00 20 00 1e 06 01 06 |......)... .....|
+00000040 02 06 03 05 01 05 02 05 03 04 01 04 02 04 03 03 |................|
+00000050 01 03 02 03 03 02 01 02 02 02 03 00 0f 00 01 01 |................|
>>> Flow 2 (server to client)
00000000 16 03 03 00 31 02 00 00 2d 03 03 00 00 00 00 00 |....1...-.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
@@ -54,9 +53,9 @@
000002c0 50 56 5c d5 82 5a 2d 5a 5f 33 c4 b6 d8 c9 75 90 |PV\..Z-Z_3....u.|
000002d0 96 8c 0f 52 98 b5 cd 98 1f 89 20 5f f2 a0 1c a3 |...R...... _....|
000002e0 1b 96 94 dd a9 fd 57 e9 70 e8 26 6d 71 99 9b 26 |......W.p.&mq..&|
-000002f0 6e 38 50 29 6c 90 a7 bd d9 16 03 03 00 0f 0d 00 |n8P)l...........|
-00000300 00 0b 02 01 40 00 04 04 01 04 03 00 00 16 03 03 |....@...........|
-00000310 00 04 0e 00 00 00 |......|
+000002f0 6e 38 50 29 6c 90 a7 bd d9 16 03 03 00 17 0d 00 |n8P)l...........|
+00000300 00 13 02 01 40 00 0c 04 01 04 03 05 01 05 03 02 |....@...........|
+00000310 01 02 03 00 00 16 03 03 00 04 0e 00 00 00 |..............|
>>> Flow 3 (client to server)
00000000 16 03 03 02 0a 0b 00 02 06 00 02 03 00 02 00 30 |...............0|
00000010 82 01 fc 30 82 01 5e 02 09 00 9a 30 84 6c 26 35 |...0..^....0.l&5|
@@ -91,32 +90,33 @@
000001e0 be e8 91 b3 da 1a f5 5d a3 23 f5 26 8b 45 70 8d |.......].#.&.Ep.|
000001f0 65 62 9b 7e 01 99 3d 18 f6 10 9a 38 61 9b 2e 57 |eb.~..=....8a..W|
00000200 e4 fa cc b1 8a ce e2 23 a0 87 f0 e1 67 51 eb 16 |.......#....gQ..|
-00000210 03 03 00 86 10 00 00 82 00 80 47 5a 2f b8 78 46 |..........GZ/.xF|
-00000220 9f 3c fc ab 8b 35 c9 77 da c3 96 78 31 7c 2b 4f |.<...5.w...x1|+O|
-00000230 56 be 0f 33 bd 17 bc 1c 86 5a ae b3 0f 8b 18 2f |V..3.....Z...../|
-00000240 48 0d e0 0a 20 d3 53 96 88 d2 8a 7d b6 58 13 44 |H... .S....}.X.D|
-00000250 a5 e8 19 6d 02 df a6 1b 79 c5 54 c2 ef 4d 41 4f |...m....y.T..MAO|
-00000260 04 1c eb 37 55 b7 2b f4 7c 6d 37 9c f1 89 a0 2c |...7U.+.|m7....,|
-00000270 0f ba 10 09 e4 a1 ee 0a 7e 9a fd 2c 32 63 1c 55 |........~..,2c.U|
-00000280 85 38 de d0 7b 5f 46 03 1f cc 4d 69 51 97 d8 d7 |.8..{_F...MiQ...|
-00000290 88 6f ba 43 04 b0 42 09 61 5e 16 03 03 00 92 0f |.o.C..B.a^......|
-000002a0 00 00 8e 04 03 00 8a 30 81 87 02 41 14 3d 4c 71 |.......0...A.=Lq|
-000002b0 c2 32 4a 20 ee b7 69 17 55 e8 99 55 11 76 51 7a |.2J ..i.U..U.vQz|
-000002c0 74 55 e7 e8 c3 3b b3 70 db 1c 8e f6 8a d4 99 40 |tU...;.p.......@|
-000002d0 6e da 04 fd 7a 47 41 d6 ae c0 63 ad fd 91 a8 58 |n...zGA...c....X|
-000002e0 24 b9 ac 2f 7a 4c bf 5b 24 12 cb 3a f3 02 42 00 |$../zL.[$..:..B.|
-000002f0 90 f9 48 97 0e d4 33 99 09 9f 1d a8 97 16 60 82 |..H...3.......`.|
-00000300 85 cc 5a 5d 79 f7 2f 03 2a c0 b8 12 61 ac 9f 88 |..Z]y./.*...a...|
-00000310 1d 0d 9e 0a ee 28 a8 5a e2 42 b7 94 e2 e6 0e 13 |.....(.Z.B......|
-00000320 c8 64 dc 4e d3 6b 10 d6 83 41 9c dc d4 53 c3 08 |.d.N.k...A...S..|
-00000330 19 14 03 03 00 01 01 16 03 03 00 24 ef bd e3 23 |...........$...#|
-00000340 10 23 ae 6e b5 12 eb 9c 21 78 db 36 fd bf 7f ee |.#.n....!x.6....|
-00000350 6f c8 00 2d b6 35 cc 2f 38 73 ae a4 34 cf 0d df |o..-.5./8s..4...|
+00000210 03 03 00 86 10 00 00 82 00 80 51 0a a7 21 d3 f5 |..........Q..!..|
+00000220 f2 16 ba b1 a0 15 58 ef d6 a1 cd 03 fe b6 95 84 |......X.........|
+00000230 35 c4 c1 0b d2 8a 01 af 3d 01 15 29 1d 2d 1e c9 |5.......=..).-..|
+00000240 12 0e df 49 87 8a 3a 67 c3 6a a6 04 10 3d 45 e4 |...I..:g.j...=E.|
+00000250 cb d3 aa 37 29 5a 45 6f f4 20 8c 85 9d 1b 0e 95 |...7)ZEo. ......|
+00000260 f4 0e 03 e5 0f 85 fc e1 20 3e 0f 44 f9 8e fe 28 |........ >.D...(|
+00000270 65 2b ae 12 18 72 26 dd 22 96 31 81 27 dd f7 69 |e+...r&.".1.'..i|
+00000280 b4 90 93 a4 0c f5 b6 f8 73 f3 85 f5 c5 03 1d 74 |........s......t|
+00000290 1a 83 e0 5c 77 bc 41 00 85 bc 16 03 03 00 93 0f |...\w.A.........|
+000002a0 00 00 8f 04 03 00 8b 30 81 88 02 42 01 33 86 47 |.......0...B.3.G|
+000002b0 4d 91 1a 71 e5 64 25 f2 0b 88 14 34 8d ca e5 d0 |M..q.d%....4....|
+000002c0 09 5b b7 c0 c2 e5 2c 0d 05 36 ee cc 11 1b 6a 8b |.[....,..6....j.|
+000002d0 ff 15 0c 7f c2 c4 8d 4a 13 33 2f fa ba c9 ae 58 |.......J.3/....X|
+000002e0 97 5a 96 17 e3 df 69 f1 d4 a6 23 7a ef a0 02 42 |.Z....i...#z...B|
+000002f0 01 2a 94 f3 db 84 42 64 85 5b 31 27 c2 72 c2 24 |.*....Bd.[1'.r.$|
+00000300 74 b0 78 e6 ba 09 3d 9f 83 2d 28 29 12 c4 ac 8b |t.x...=..-()....|
+00000310 e0 32 d2 30 2e 06 53 15 85 3c 29 a9 a5 13 98 93 |.2.0..S..<).....|
+00000320 f4 76 02 96 6a 60 3f 0f ad 85 b7 66 49 03 c1 2a |.v..j`?....fI..*|
+00000330 bc 86 14 03 03 00 01 01 16 03 03 00 24 fc e5 8a |............$...|
+00000340 f9 82 bd 89 d7 db e3 53 09 85 01 cc bc 25 93 96 |.......S.....%..|
+00000350 d3 74 c7 e9 15 f3 e1 f9 01 6e 77 08 92 39 69 b5 |.t.......nw..9i.|
+00000360 43 |C|
>>> Flow 4 (server to client)
-00000000 14 03 03 00 01 01 16 03 03 00 24 a7 50 0f 50 b4 |..........$.P.P.|
-00000010 1c c3 4d f3 7a 64 df 65 ac 35 22 13 46 cc ec 36 |..M.zd.e.5".F..6|
-00000020 e6 d2 f3 67 94 6a 18 85 9f 4a 3c 44 a3 58 b0 17 |...g.j...J>> Flow 1 (client to server)
-00000000 16 03 01 00 5c 01 00 00 58 03 03 52 cc 57 59 6b |....\...X..R.WYk|
-00000010 11 07 04 39 77 20 c2 b4 3f cb 0a c9 53 fe 5b 3e |...9w ..?...S.[>|
-00000020 5f 58 2c 7e 30 69 e1 8e 6c 9d c8 00 00 04 00 05 |_X,~0i..l.......|
-00000030 00 ff 01 00 00 2b 00 0d 00 22 00 20 06 01 06 02 |.....+...". ....|
-00000040 06 03 05 01 05 02 05 03 04 01 04 02 04 03 03 01 |................|
-00000050 03 02 03 03 02 01 02 02 02 03 01 01 00 0f 00 01 |................|
-00000060 01 |.|
+00000000 16 03 01 00 5b 01 00 00 57 03 03 de ba cc 44 30 |....[...W.....D0|
+00000010 69 67 7f 91 d6 08 2c d0 1f 5c 60 d5 a5 76 c7 15 |ig....,..\`..v..|
+00000020 fd fd 83 a0 20 b3 63 c1 ed 06 d3 00 00 04 00 05 |.... .c.........|
+00000030 00 ff 02 01 00 00 29 00 0d 00 20 00 1e 06 01 06 |......)... .....|
+00000040 02 06 03 05 01 05 02 05 03 04 01 04 02 04 03 03 |................|
+00000050 01 03 02 03 03 02 01 02 02 02 03 00 0f 00 01 01 |................|
>>> Flow 2 (server to client)
00000000 16 03 03 00 31 02 00 00 2d 03 03 00 00 00 00 00 |....1...-.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
@@ -54,9 +53,9 @@
000002c0 50 56 5c d5 82 5a 2d 5a 5f 33 c4 b6 d8 c9 75 90 |PV\..Z-Z_3....u.|
000002d0 96 8c 0f 52 98 b5 cd 98 1f 89 20 5f f2 a0 1c a3 |...R...... _....|
000002e0 1b 96 94 dd a9 fd 57 e9 70 e8 26 6d 71 99 9b 26 |......W.p.&mq..&|
-000002f0 6e 38 50 29 6c 90 a7 bd d9 16 03 03 00 0f 0d 00 |n8P)l...........|
-00000300 00 0b 02 01 40 00 04 04 01 04 03 00 00 16 03 03 |....@...........|
-00000310 00 04 0e 00 00 00 |......|
+000002f0 6e 38 50 29 6c 90 a7 bd d9 16 03 03 00 17 0d 00 |n8P)l...........|
+00000300 00 13 02 01 40 00 0c 04 01 04 03 05 01 05 03 02 |....@...........|
+00000310 01 02 03 00 00 16 03 03 00 04 0e 00 00 00 |..............|
>>> Flow 3 (client to server)
00000000 16 03 03 01 fb 0b 00 01 f7 00 01 f4 00 01 f1 30 |...............0|
00000010 82 01 ed 30 82 01 58 a0 03 02 01 02 02 01 00 30 |...0..X........0|
@@ -90,32 +89,32 @@
000001d0 8b ec ab 67 be c8 64 b0 11 50 46 58 17 6b 99 1c |...g..d..PFX.k..|
000001e0 d3 1d fc 06 f1 0e e5 96 a8 0c f9 78 20 b7 44 18 |...........x .D.|
000001f0 51 8d 10 7e 4f 94 67 df a3 4e 70 73 8e 90 91 85 |Q..~O.g..Nps....|
-00000200 16 03 03 00 86 10 00 00 82 00 80 44 89 7d aa 26 |...........D.}.&|
-00000210 30 ce 6b db 25 70 b0 1e 16 fa 5b 3a dd 4a 4b bd |0.k.%p....[:.JK.|
-00000220 ec ee 50 9d 21 ba 52 b5 51 4f a8 65 d8 2e 41 e2 |..P.!.R.QO.e..A.|
-00000230 e1 dc f3 1a df 58 4f 87 7a d3 e1 e1 1c 13 b2 0b |.....XO.z.......|
-00000240 b7 43 b7 92 f2 df 19 bb 79 71 e0 71 44 ab 19 2f |.C......yq.qD../|
-00000250 37 11 ac 62 50 b6 f1 53 fe aa b4 bc 29 8e 0b 4c |7..bP..S....)..L|
-00000260 0b 12 8d d5 84 a9 fa a9 ea 16 aa c3 0d da 32 c8 |..............2.|
-00000270 e0 4c 9f 99 f8 69 cd a8 c3 b1 76 42 67 f3 ff 15 |.L...i....vBg...|
-00000280 52 95 43 66 da 49 43 25 9d e5 eb 16 03 03 00 88 |R.Cf.IC%........|
-00000290 0f 00 00 84 04 01 00 80 01 d5 0e 1c 75 97 89 52 |............u..R|
-000002a0 1a f0 cc ef 93 6e 71 b2 b1 38 8c 50 11 f7 a3 02 |.....nq..8.P....|
-000002b0 71 c4 d5 6f 8d 01 83 06 2e ea 5a 10 8a 0d d0 fc |q..o......Z.....|
-000002c0 b6 a2 63 af 4f 99 b5 eb ab fd 01 c2 fb 26 fc fd |..c.O........&..|
-000002d0 ad 2c b3 63 b3 87 a6 f5 14 ea 7d e7 fe a8 e7 7e |.,.c......}....~|
-000002e0 20 ab b9 f6 c3 58 bd c0 f3 96 eb 83 dc 42 6c 0d | ....X.......Bl.|
-000002f0 5e e8 09 55 c7 b8 24 05 dd e1 7c af 9f 2c 22 6c |^..U..$...|..,"l|
-00000300 fa b8 94 13 3b f1 09 e1 38 59 fc a1 8c cb aa ca |....;...8Y......|
-00000310 f8 e0 2a 9c 36 f9 c3 2b 14 03 03 00 01 01 16 03 |..*.6..+........|
-00000320 03 00 24 d0 12 7c cc d2 3e 37 1f f4 7d b4 c0 fc |..$..|..>7..}...|
-00000330 19 f6 c8 ea 62 12 e0 0d af 62 d4 69 f7 96 5a c0 |....b....b.i..Z.|
-00000340 97 d3 bb b0 a3 f7 3f |......?|
+00000200 16 03 03 00 86 10 00 00 82 00 80 6c 19 7b fb 96 |...........l.{..|
+00000210 52 57 c1 8b 78 72 4b 67 c9 66 ce 61 71 6c 83 90 |RW..xrKg.f.aql..|
+00000220 56 90 fc bc 40 41 12 30 bf 3f 20 58 75 3d 31 45 |V...@A.0.? Xu=1E|
+00000230 67 61 57 fc 0f 48 1d e1 a4 ec 75 4d bc e3 09 62 |gaW..H....uM...b|
+00000240 98 8e 40 cb 53 cb 97 31 32 b7 20 f3 dd 5d 29 62 |..@.S..12. ..])b|
+00000250 90 18 94 98 84 d8 42 34 fc a4 35 80 55 46 32 a1 |......B4..5.UF2.|
+00000260 6f 0e d2 33 c3 53 6a 46 87 74 fd 68 15 35 b8 c4 |o..3.SjF.t.h.5..|
+00000270 2b da b4 55 9c 6e 87 94 01 6a 72 12 8a 01 8a 37 |+..U.n...jr....7|
+00000280 ac 5b d7 55 27 40 58 fc 0d 30 12 16 03 03 00 88 |.[.U'@X..0......|
+00000290 0f 00 00 84 04 01 00 80 06 71 c6 af f8 53 b9 eb |.........q...S..|
+000002a0 9a 99 44 fa 65 dc af cc 69 b4 cf 10 fc b0 f2 33 |..D.e...i......3|
+000002b0 da 8a 3f cb f1 8d 51 ae 90 7d f3 02 57 0e ea b9 |..?...Q..}..W...|
+000002c0 37 2d 2e dc ad 89 44 04 4f 21 04 44 54 9f 9e 92 |7-....D.O!.DT...|
+000002d0 7e b7 4c 53 b8 3c 89 c0 c1 1e f7 df cc c6 2f 76 |~.LS.<......../v|
+000002e0 be 91 bc a0 4d c7 e1 df 9d 19 e3 0d ab 17 e4 d4 |....M...........|
+000002f0 c3 4c 75 e2 71 5c a3 df 9a 17 82 38 2a 4b f8 ae |.Lu.q\.....8*K..|
+00000300 ad 13 a7 f3 13 f8 72 78 49 fe 80 d9 bd 77 a3 48 |......rxI....w.H|
+00000310 43 a7 76 00 15 1a 14 5d 14 03 03 00 01 01 16 03 |C.v....]........|
+00000320 03 00 24 1d 80 33 0a 75 ae d5 13 d5 46 f9 89 3d |..$..3.u....F..=|
+00000330 15 95 f2 c5 2d c5 e0 7f cc 6e d7 36 9f 72 d6 74 |....-....n.6.r.t|
+00000340 7b c1 4f ed 89 ff c1 |{.O....|
>>> Flow 4 (server to client)
-00000000 14 03 03 00 01 01 16 03 03 00 24 cd 20 85 1e 74 |..........$. ..t|
-00000010 18 b2 71 48 d5 10 61 c6 b0 18 26 83 c2 7f f1 b1 |..qH..a...&.....|
-00000020 2f b5 35 d0 47 a8 99 9a 9a a5 62 64 fb f9 29 17 |/.5.G.....bd..).|
-00000030 03 03 00 21 22 7b ed 61 e3 9b 6d 98 b9 23 98 e3 |...!"{.a..m..#..|
-00000040 55 11 b8 0f 7e 2b e1 c1 d4 f1 83 79 c3 f8 03 f0 |U...~+.....y....|
-00000050 02 5c 61 24 d7 15 03 03 00 16 14 2b a3 5a 56 f0 |.\a$.......+.ZV.|
-00000060 92 da d0 e6 32 91 d8 30 7a b4 d0 a2 93 f5 01 ea |....2..0z.......|
+00000000 14 03 03 00 01 01 16 03 03 00 24 6c f7 56 21 17 |..........$l.V!.|
+00000010 3d d6 ad 06 34 a6 0d a0 22 b6 26 38 a2 b2 39 52 |=...4...".&8..9R|
+00000020 8a cb 0e 5b b2 f6 32 b2 da 71 8a 72 13 40 f2 17 |...[..2..q.r.@..|
+00000030 03 03 00 21 69 bc 35 92 9c 17 4d aa ce 85 35 35 |...!i.5...M...55|
+00000040 98 8e c3 ff fd 36 9b 47 dd a7 85 8a 20 05 05 b8 |.....6.G.... ...|
+00000050 87 31 66 41 a0 15 03 03 00 16 e9 28 e0 39 fe da |.1fA.......(.9..|
+00000060 b1 0a c3 22 cf 9e 33 60 b9 52 18 ad 03 c8 ee 6b |..."..3`.R.....k|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-ClientAuthRequestedNotGiven b/src/crypto/tls/testdata/Server-TLSv12-ClientAuthRequestedNotGiven
index 562fe1aaa0..698409cd1c 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-ClientAuthRequestedNotGiven
+++ b/src/crypto/tls/testdata/Server-TLSv12-ClientAuthRequestedNotGiven
@@ -1,11 +1,10 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 00 5c 01 00 00 58 03 03 52 cc 57 59 1b |....\...X..R.WY.|
-00000010 08 fe f7 8a bf 07 84 2b 60 a6 13 2d 15 13 f8 b6 |.......+`..-....|
-00000020 d4 b6 3b f2 7a 98 ff 32 a0 68 7c 00 00 04 00 05 |..;.z..2.h|.....|
-00000030 00 ff 01 00 00 2b 00 0d 00 22 00 20 06 01 06 02 |.....+...". ....|
-00000040 06 03 05 01 05 02 05 03 04 01 04 02 04 03 03 01 |................|
-00000050 03 02 03 03 02 01 02 02 02 03 01 01 00 0f 00 01 |................|
-00000060 01 |.|
+00000000 16 03 01 00 5b 01 00 00 57 03 03 72 1e c6 5e 14 |....[...W..r..^.|
+00000010 4d 88 9c 2c 6e fb bb bb 37 26 f7 0f 06 d5 e8 aa |M..,n...7&......|
+00000020 53 e0 df d0 de 81 f8 33 b1 86 ff 00 00 04 00 05 |S......3........|
+00000030 00 ff 02 01 00 00 29 00 0d 00 20 00 1e 06 01 06 |......)... .....|
+00000040 02 06 03 05 01 05 02 05 03 04 01 04 02 04 03 03 |................|
+00000050 01 03 02 03 03 02 01 02 02 02 03 00 0f 00 01 01 |................|
>>> Flow 2 (server to client)
00000000 16 03 03 00 31 02 00 00 2d 03 03 00 00 00 00 00 |....1...-.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
@@ -54,28 +53,28 @@
000002c0 50 56 5c d5 82 5a 2d 5a 5f 33 c4 b6 d8 c9 75 90 |PV\..Z-Z_3....u.|
000002d0 96 8c 0f 52 98 b5 cd 98 1f 89 20 5f f2 a0 1c a3 |...R...... _....|
000002e0 1b 96 94 dd a9 fd 57 e9 70 e8 26 6d 71 99 9b 26 |......W.p.&mq..&|
-000002f0 6e 38 50 29 6c 90 a7 bd d9 16 03 03 00 0f 0d 00 |n8P)l...........|
-00000300 00 0b 02 01 40 00 04 04 01 04 03 00 00 16 03 03 |....@...........|
-00000310 00 04 0e 00 00 00 |......|
+000002f0 6e 38 50 29 6c 90 a7 bd d9 16 03 03 00 17 0d 00 |n8P)l...........|
+00000300 00 13 02 01 40 00 0c 04 01 04 03 05 01 05 03 02 |....@...........|
+00000310 01 02 03 00 00 16 03 03 00 04 0e 00 00 00 |..............|
>>> Flow 3 (client to server)
00000000 16 03 03 00 07 0b 00 00 03 00 00 00 16 03 03 00 |................|
-00000010 86 10 00 00 82 00 80 6b 51 48 d3 18 7d 30 e0 0c |.......kQH..}0..|
-00000020 20 8d f3 e4 39 47 30 0e a5 85 79 f9 8b 11 50 9e | ...9G0...y...P.|
-00000030 81 71 5c 26 c6 bb cb aa d5 00 d1 89 79 b1 77 2d |.q\&........y.w-|
-00000040 eb 9b 86 7c 52 c6 f7 b7 10 b0 b6 94 22 51 b8 12 |...|R......."Q..|
-00000050 3c 09 35 8e 1b cc f4 3b b7 b8 78 ab 89 59 41 49 |<.5....;..x..YAI|
-00000060 21 31 eb f0 f8 94 63 3d e6 96 8f b6 63 95 05 dd |!1....c=....c...|
-00000070 46 b3 00 8a d6 83 75 99 1b 5a 48 0a 23 b5 10 c1 |F.....u..ZH.#...|
-00000080 95 b5 bc 15 72 b5 f5 a0 62 e2 1d c0 ff d2 87 a5 |....r...b.......|
-00000090 97 5c 33 49 a7 26 35 14 03 03 00 01 01 16 03 03 |.\3I.&5.........|
-000000a0 00 24 61 38 1f 9d fb d9 65 2e 02 07 fb be f9 85 |.$a8....e.......|
-000000b0 8d 15 34 c0 d1 0e 4e 10 3c 25 60 2f ac 04 21 66 |..4...N.<%`/..!f|
-000000c0 04 9d 9a 60 31 72 |...`1r|
+00000010 86 10 00 00 82 00 80 5b 13 f1 87 cb 3b f1 72 32 |.......[....;.r2|
+00000020 a0 24 fd ea 6a b1 de 7d 54 4a ff a5 b0 01 8f de |.$..j..}TJ......|
+00000030 95 d1 a9 7f d4 1d 97 0f 52 cb 9b 60 5d cb 37 d0 |........R..`].7.|
+00000040 37 eb 8f 2b c6 08 86 b7 d2 87 8e 12 78 af a8 a3 |7..+........x...|
+00000050 a5 35 cc b7 33 25 60 9a cb 4e b9 b9 51 e0 4c 69 |.5..3%`..N..Q.Li|
+00000060 7b 2f 68 39 9e 40 c0 0c 77 4f 04 f5 47 7f 45 55 |{/h9.@..wO..G.EU|
+00000070 e9 90 f1 9e df 11 4a af 9f cc 7e 96 31 d7 b4 d2 |......J...~.1...|
+00000080 bb 0d 03 56 9b 97 4e 40 53 af 2d c0 2a f9 29 8e |...V..N@S.-.*.).|
+00000090 82 d0 f7 9a 89 59 5f 14 03 03 00 01 01 16 03 03 |.....Y_.........|
+000000a0 00 24 d1 27 e7 27 e2 a4 81 4e 02 be 66 7e 78 44 |.$.'.'...N..f~xD|
+000000b0 ee 40 84 f7 fa 17 ad 6e 99 5c 48 df bb c0 94 f5 |.@.....n.\H.....|
+000000c0 a2 c6 30 6d 2d 08 |..0m-.|
>>> Flow 4 (server to client)
-00000000 14 03 03 00 01 01 16 03 03 00 24 fe 0e 3e 84 af |..........$..>..|
-00000010 e5 6b 10 ed 41 9c 2b e0 ba e0 2b 53 61 36 1b 40 |.k..A.+...+Sa6.@|
-00000020 35 de 3a c7 c3 5c df 74 67 f7 05 74 84 f5 e1 17 |5.:..\.tg..t....|
-00000030 03 03 00 21 d3 8d 81 85 b7 1f 30 bd 89 33 f9 81 |...!......0..3..|
-00000040 89 f7 af d1 be b0 c1 46 e3 df 32 f6 dc 2f 4d 82 |.......F..2../M.|
-00000050 0a 84 9f 5b 03 15 03 03 00 16 13 af 37 91 82 67 |...[........7..g|
-00000060 b0 7c 5e 0e ec 8e cc 31 a0 ea a5 72 a4 2b 0b 73 |.|^....1...r.+.s|
+00000000 14 03 03 00 01 01 16 03 03 00 24 41 b5 43 2a 5c |..........$A.C*\|
+00000010 f5 f6 e1 b5 a2 cc 1c c8 86 ae cc a2 0c c4 6b 73 |..............ks|
+00000020 ab 3d 91 09 04 6d e0 18 af 85 dd 90 9a eb 44 17 |.=...m........D.|
+00000030 03 03 00 21 74 b4 fd cb c4 7b 81 67 76 18 0f b3 |...!t....{.gv...|
+00000040 b7 3f 30 2f c9 86 23 f1 79 38 ef 0c da 52 b7 ab |.?0/..#.y8...R..|
+00000050 f7 9a ca 27 73 15 03 03 00 16 37 2b 19 cc bd f9 |...'s.....7+....|
+00000060 cd 14 30 21 4f c0 45 e9 e7 ac 7c c4 93 51 f6 aa |..0!O.E...|..Q..|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-ECDHE-ECDSA-AES b/src/crypto/tls/testdata/Server-TLSv12-ECDHE-ECDSA-AES
index 0e480be437..3b7238ad29 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-ECDHE-ECDSA-AES
+++ b/src/crypto/tls/testdata/Server-TLSv12-ECDHE-ECDSA-AES
@@ -1,7 +1,7 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 00 a1 01 00 00 9d 03 03 eb 02 58 55 90 |.............XU.|
-00000010 a0 ba 80 52 28 a5 36 35 ee 6d eb e1 b0 d3 5d 89 |...R(.65.m....].|
-00000020 e8 2d a3 5e b1 83 e8 2f 00 f2 1e 00 00 04 c0 0a |.-.^.../........|
+00000000 16 03 01 00 a1 01 00 00 9d 03 03 0f b7 07 5f c7 |.............._.|
+00000010 18 b8 39 6d 92 b3 90 ed bf 5c 48 7c 6a 56 ee e9 |..9m.....\H|jV..|
+00000020 7a 5b 5f 71 a4 f0 7f 47 57 73 78 00 00 04 c0 0a |z[_q...GWsx.....|
00000030 00 ff 02 01 00 00 6f 00 0b 00 04 03 00 01 02 00 |......o.........|
00000040 0a 00 3a 00 38 00 0e 00 0d 00 19 00 1c 00 0b 00 |..:.8...........|
00000050 0c 00 1b 00 18 00 09 00 0a 00 1a 00 16 00 17 00 |................|
@@ -47,43 +47,43 @@
00000210 0e bd 3f a3 8c 25 c1 33 13 83 0d 94 06 bb d4 37 |..?..%.3.......7|
00000220 7a f6 ec 7a c9 86 2e dd d7 11 69 7f 85 7c 56 de |z..z......i..|V.|
00000230 fb 31 78 2b e4 c7 78 0d ae cb be 9e 4e 36 24 31 |.1x+..x.....N6$1|
-00000240 7b 6a 0f 39 95 12 07 8f 2a 16 03 03 00 d7 0c 00 |{j.9....*.......|
-00000250 00 d3 03 00 17 41 04 1e 18 37 ef 0d 19 51 88 35 |.....A...7...Q.5|
+00000240 7b 6a 0f 39 95 12 07 8f 2a 16 03 03 00 d8 0c 00 |{j.9....*.......|
+00000250 00 d4 03 00 17 41 04 1e 18 37 ef 0d 19 51 88 35 |.....A...7...Q.5|
00000260 75 71 b5 e5 54 5b 12 2e 8f 09 67 fd a7 24 20 3e |uq..T[....g..$ >|
00000270 b2 56 1c ce 97 28 5e f8 2b 2d 4f 9e f1 07 9f 6c |.V...(^.+-O....l|
00000280 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 a6 b5 68 1a |K[.V.2B.X..I..h.|
-00000290 41 03 56 6b dc 5a 89 04 03 00 8a 30 81 87 02 41 |A.Vk.Z.....0...A|
-000002a0 68 cb cd c0 12 5e 1e b3 cc d9 47 e7 b5 11 5e be |h....^....G...^.|
-000002b0 74 5b 90 93 3f 4c 07 20 a1 94 50 bb 23 82 fc 5b |t[..?L. ..P.#..[|
-000002c0 78 87 a3 a1 fe 7c 6f 84 93 8f b8 f7 2e 56 65 85 |x....|o......Ve.|
-000002d0 1d 9e 8e 52 b0 89 b0 a7 66 58 98 55 30 64 94 91 |...R....fX.U0d..|
-000002e0 8e 02 42 01 3d 74 eb a4 64 9e 7c 8b 05 57 5c f7 |..B.=t..d.|..W\.|
-000002f0 fe a7 58 f4 21 7b 75 ea 51 1e 1c be 80 4e 00 d1 |..X.!{u.Q....N..|
-00000300 06 80 58 90 c2 f3 47 da 22 8b a2 6b f0 2e 34 d0 |..X...G."..k..4.|
-00000310 1a 84 54 87 62 96 b9 2c 91 9f 3f 93 24 df 6c a3 |..T.b..,..?.$.l.|
-00000320 77 1f d2 e4 30 16 03 03 00 04 0e 00 00 00 |w...0.........|
+00000290 41 03 56 6b dc 5a 89 05 03 00 8b 30 81 88 02 42 |A.Vk.Z.....0...B|
+000002a0 00 d3 cf 21 cd 3c 2e 11 f5 f8 1d c8 c1 57 4b f8 |...!.<.......WK.|
+000002b0 1a c0 2b 1d 47 0f 2d a5 ac a1 c8 83 5d 76 87 05 |..+.G.-.....]v..|
+000002c0 2b 0d 36 d5 57 9f b9 8a a0 a2 94 67 6a cd 29 db |+.6.W......gj.).|
+000002d0 04 b0 6b 06 d9 f7 17 9f 1c 60 92 e7 4e 50 48 7f |..k......`..NPH.|
+000002e0 dc d0 02 42 01 56 fd 38 bd 05 a5 16 6d 91 d1 ce |...B.V.8....m...|
+000002f0 bb 8c 45 b2 76 2f 92 9c 8b 94 57 7d de 53 8b 7b |..E.v/....W}.S.{|
+00000300 80 26 6c 4a 43 4b a6 c9 46 49 08 ab c7 57 f3 d9 |.&lJCK..FI...W..|
+00000310 fa 1d 55 fe 91 de 8a 0d 8b d1 44 96 87 85 cb 02 |..U.......D.....|
+00000320 76 9c 00 ad 5f b8 16 03 03 00 04 0e 00 00 00 |v..._..........|
>>> Flow 3 (client to server)
-00000000 16 03 03 00 46 10 00 00 42 41 04 04 12 72 83 8c |....F...BA...r..|
-00000010 55 75 c4 34 48 5d d4 e3 9a 34 54 46 83 2f 69 36 |Uu.4H]...4TF./i6|
-00000020 e2 98 65 4d d2 b3 d4 6e 35 93 42 80 02 bd 26 a4 |..eM...n5.B...&.|
-00000030 ca 53 8d 50 c8 78 ec e3 0d 68 31 12 11 fe 76 52 |.S.P.x...h1...vR|
-00000040 0d 2f 63 76 43 74 5b 55 bd 9a 01 14 03 03 00 01 |./cvCt[U........|
-00000050 01 16 03 03 00 40 5f b8 b6 b9 cd 93 37 67 53 6f |.....@_.....7gSo|
-00000060 cd 2d c6 c2 92 28 d6 ab b7 31 ab 75 99 c8 83 7d |.-...(...1.u...}|
-00000070 99 69 03 2d 52 6f ae 06 ec 34 05 fc 3b dd d0 0b |.i.-Ro...4..;...|
-00000080 22 4a 84 cd 86 6c 11 10 3d 0d 9c 0f c8 c9 24 a6 |"J...l..=.....$.|
-00000090 f3 e6 e7 87 31 13 |....1.|
+00000000 16 03 03 00 46 10 00 00 42 41 04 0b dc ea 22 05 |....F...BA....".|
+00000010 44 c2 09 47 65 31 3b 0b e1 05 1a 87 8c 2d 3b 56 |D..Ge1;......-;V|
+00000020 49 34 27 3e d6 3b 93 e2 12 7f 5d 7b dc 85 c8 96 |I4'>.;....]{....|
+00000030 4c 8c f9 18 6f 15 cf db 6e 2c 14 6a c9 dd 1c 70 |L...o...n,.j...p|
+00000040 7e 05 c4 17 71 76 df 10 ee 8c b1 14 03 03 00 01 |~...qv..........|
+00000050 01 16 03 03 00 40 ff 12 88 36 3c 00 17 d1 b9 41 |.....@...6<....A|
+00000060 7a 12 25 94 4c 90 65 62 d8 09 ab f9 b4 ee c3 de |z.%.L.eb........|
+00000070 46 2f cb ee 18 76 4f 76 8e dd 89 fc 7a 21 3b 5f |F/...vOv....z!;_|
+00000080 ff ac 1c 03 aa be 96 82 82 ea 2e 22 2a 80 b3 86 |..........."*...|
+00000090 38 e4 4d 90 91 46 |8.M..F|
>>> Flow 4 (server to client)
00000000 14 03 03 00 01 01 16 03 03 00 40 00 00 00 00 00 |..........@.....|
-00000010 00 00 00 00 00 00 00 00 00 00 00 a9 71 da 7a 3d |............q.z=|
-00000020 c1 17 da fa 05 ac ed a6 79 79 31 67 83 de 86 92 |........yy1g....|
-00000030 de 7e 6f 5c dc d7 e8 29 df 51 15 a1 6f 38 84 a5 |.~o\...).Q..o8..|
-00000040 a6 e4 f2 56 8a cc bf ad f4 b8 0c 17 03 03 00 40 |...V...........@|
+00000010 00 00 00 00 00 00 00 00 00 00 00 e5 c1 f0 6a db |..............j.|
+00000020 05 98 ed 33 94 73 7f 13 7f 78 17 7f d1 9e c5 a7 |...3.s...x......|
+00000030 62 7f 85 14 2c 7d b2 8e ef 75 a9 df 92 cc 22 20 |b...,}...u...." |
+00000040 66 08 85 22 d3 ea 5c 4c 4c c8 d7 17 03 03 00 40 |f.."..\LL......@|
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
-00000060 b1 a6 a1 eb f4 26 ef e9 25 7d c8 b5 a5 4b dc c8 |.....&..%}...K..|
-00000070 32 58 d6 c3 94 e7 f7 20 10 9e a1 db 10 db e4 42 |2X..... .......B|
-00000080 3c c5 26 e7 70 f2 d7 f0 38 10 a7 63 61 22 1b 57 |<.&.p...8..ca".W|
+00000060 f2 20 07 d2 13 ca ed 01 c9 7b 91 14 01 2c 08 f5 |. .......{...,..|
+00000070 8a 69 94 bc 19 9a d9 65 6b 15 04 b4 45 17 ec 6f |.i.....ek...E..o|
+00000080 85 de 31 dc a2 de 8b 4d 53 57 66 4a 29 21 5a 20 |..1....MSWfJ)!Z |
00000090 15 03 03 00 30 00 00 00 00 00 00 00 00 00 00 00 |....0...........|
-000000a0 00 00 00 00 00 29 ea 96 ea 08 88 6c 5d 67 91 f7 |.....).....l]g..|
-000000b0 31 8e b8 3a 9a d5 87 2a 81 2f 80 fb 7f b5 80 03 |1..:...*./......|
-000000c0 9c 8b 7e 39 7b |..~9{|
+000000a0 00 00 00 00 00 55 15 f7 89 8d 75 57 7e 92 db ec |.....U....uW~...|
+000000b0 32 ec 07 5c 83 32 36 59 61 f1 9d a6 7a eb 76 c1 |2..\.26Ya...z.v.|
+000000c0 c7 96 3f 4d 0a |..?M.|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-IssueTicket b/src/crypto/tls/testdata/Server-TLSv12-IssueTicket
index e3e62f2242..b20ad95764 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-IssueTicket
+++ b/src/crypto/tls/testdata/Server-TLSv12-IssueTicket
@@ -1,11 +1,11 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 00 60 01 00 00 5c 03 03 52 cc 57 59 7e |....`...\..R.WY~|
-00000010 43 5c 3b fd 50 ab 61 3f 64 a4 f9 bd ba 8c 28 e1 |C\;.P.a?d.....(.|
-00000020 f9 a1 45 7e 48 9e 62 af 25 de 0e 00 00 04 00 05 |..E~H.b.%.......|
-00000030 00 ff 01 00 00 2f 00 23 00 00 00 0d 00 22 00 20 |...../.#.....". |
-00000040 06 01 06 02 06 03 05 01 05 02 05 03 04 01 04 02 |................|
-00000050 04 03 03 01 03 02 03 03 02 01 02 02 02 03 01 01 |................|
-00000060 00 0f 00 01 01 |.....|
+00000000 16 03 01 00 5f 01 00 00 5b 03 03 01 02 22 4f 51 |...._...[...."OQ|
+00000010 53 d9 c0 f2 4b 61 53 2d 04 cd ab 95 ed 6a 74 8c |S...KaS-.....jt.|
+00000020 96 00 70 e3 bf d0 5a 03 7a 1e 75 00 00 04 00 05 |..p...Z.z.u.....|
+00000030 00 ff 02 01 00 00 2d 00 23 00 00 00 0d 00 20 00 |......-.#..... .|
+00000040 1e 06 01 06 02 06 03 05 01 05 02 05 03 04 01 04 |................|
+00000050 02 04 03 03 01 03 02 03 03 02 01 02 02 02 03 00 |................|
+00000060 0f 00 01 01 |....|
>>> Flow 2 (server to client)
00000000 16 03 03 00 35 02 00 00 31 03 03 00 00 00 00 00 |....5...1.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
@@ -57,31 +57,32 @@
000002f0 71 99 9b 26 6e 38 50 29 6c 90 a7 bd d9 16 03 03 |q..&n8P)l.......|
00000300 00 04 0e 00 00 00 |......|
>>> Flow 3 (client to server)
-00000000 16 03 03 00 86 10 00 00 82 00 80 6e 2e 79 82 3a |...........n.y.:|
-00000010 c4 68 72 f5 a2 42 3d 71 f9 ec 22 8c 0b fa f0 82 |.hr..B=q..".....|
-00000020 82 c0 cb fc 52 0a 51 03 04 8c eb 4a 4e 4f b6 49 |....R.Q....JNO.I|
-00000030 ef 94 65 21 3c f7 9d 46 85 6e 35 d5 17 6b ff a3 |..e!<..F.n5..k..|
-00000040 5e 4d c1 36 1a 2f 68 f5 06 d4 2d 73 4f 1c 3b 7b |^M.6./h...-sO.;{|
-00000050 c1 fa 4e 7e 7c f9 6c 13 a6 f4 3a 43 e9 aa be 22 |..N~|.l...:C..."|
-00000060 85 6f 2f 7c 5b b0 08 e2 86 b2 ae cb a9 12 d8 32 |.o/|[..........2|
-00000070 80 1d e4 2e 5d c3 66 d1 19 e5 89 33 2a 88 24 40 |....].f....3*.$@|
-00000080 2a 6d 6b b5 f1 92 4b 66 06 b8 49 14 03 03 00 01 |*mk...Kf..I.....|
-00000090 01 16 03 03 00 24 16 49 e2 a0 67 31 cf 0d 72 cb |.....$.I..g1..r.|
-000000a0 ac 16 2c 80 37 71 69 f7 5f c4 d3 00 19 b7 4b fb |..,.7qi._.....K.|
-000000b0 e5 e9 74 8e 30 b3 1c c5 ae e6 |..t.0.....|
+00000000 16 03 03 00 86 10 00 00 82 00 80 80 38 a6 b0 01 |............8...|
+00000010 2a 9e cf 11 34 45 e8 6d f5 1c 44 ef 74 74 61 32 |*...4E.m..D.tta2|
+00000020 71 5f f8 c1 a9 65 2d af 7e 7e 38 84 d3 f2 b9 3d |q_...e-.~~8....=|
+00000030 76 12 b8 e0 41 7e 25 2a 53 b0 1a c7 8d bd d6 3d |v...A~%*S......=|
+00000040 a5 8a dd 94 76 80 fc 3e fd 41 ac 71 c3 ad 0e 1f |....v..>.A.q....|
+00000050 30 a7 7a 64 e2 f3 f7 c1 1f bc 53 99 35 4e 24 34 |0.zd......S.5N$4|
+00000060 e9 25 20 d0 da 00 30 d4 16 40 5e 78 8e 72 ea 03 |.% ...0..@^x.r..|
+00000070 9e eb ca 89 4e 2f 60 d0 0c 9d 98 44 e0 7c 19 a4 |....N/`....D.|..|
+00000080 ec 0f 6b 67 35 06 08 9c d9 2d bb 14 03 03 00 01 |..kg5....-......|
+00000090 01 16 03 03 00 24 ca d6 25 be 3b a7 b0 e1 42 3b |.....$..%.;...B;|
+000000a0 ce ef a5 7e b6 4a d5 74 e1 ca bf 34 6c 67 3b 02 |...~.J.t...4lg;.|
+000000b0 0a f5 e8 e7 d1 a8 a6 2d cb 02 |.......-..|
>>> Flow 4 (server to client)
-00000000 16 03 03 00 72 04 00 00 6e 00 00 00 00 00 68 00 |....r...n.....h.|
-00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 65 |...............e|
-00000020 ea 4b d1 ef ba 06 38 1e e1 88 82 3a cd 03 ac 3b |.K....8....:...;|
-00000030 39 0a e0 19 fd af 6c 57 30 df 31 6e f7 92 38 4b |9.....lW0.1n..8K|
-00000040 5d 77 90 39 ff 32 51 f5 ed 12 d7 b0 7c 4d 6c c5 |]w.9.2Q.....|Ml.|
-00000050 76 e4 72 48 3e 59 23 fe 0d 15 df f4 ba ea b9 67 |v.rH>Y#........g|
-00000060 16 23 8f 7d 15 b6 11 f1 ab d7 d4 cd a3 21 82 92 |.#.}.........!..|
-00000070 2a 12 cf 95 f3 60 b2 14 03 03 00 01 01 16 03 03 |*....`..........|
-00000080 00 24 89 ad 87 04 4f 08 dc 2a 71 37 fb f1 95 d1 |.$....O..*q7....|
-00000090 2e 3c c2 6e 0f 38 5d e4 0e c3 f7 27 d0 46 a3 c1 |.<.n.8]....'.F..|
-000000a0 a8 3b 06 ed 96 ec 17 03 03 00 21 30 d4 9f 0b 49 |.;........!0...I|
-000000b0 9f a2 a8 a1 2c 0a 79 93 56 2d 8a ee 85 ed 62 42 |....,.y.V-....bB|
-000000c0 8c 18 fe 7a 09 3a 24 c4 5e ed 7d 2a 15 03 03 00 |...z.:$.^.}*....|
-000000d0 16 a0 24 0a 8b 90 4c fc 99 ba 67 bb 04 1e 59 69 |..$...L...g...Yi|
-000000e0 c2 98 49 b5 00 0b e0 |..I....|
+00000000 16 03 03 00 82 04 00 00 7e 00 00 00 00 00 78 50 |........~.....xP|
+00000010 46 ad c1 db a8 38 86 7b 2b bb fd d0 c3 42 3e 00 |F....8.{+....B>.|
+00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 94 |................|
+00000030 6f 2c b5 83 61 e8 c1 5d af d6 da c9 8f df 1e c4 |o,..a..]........|
+00000040 16 47 a0 dd cf 3c 9d 95 11 fe 01 fb 52 5b d0 aa |.G...<......R[..|
+00000050 56 fb 04 d5 7f 89 31 7d 75 e3 df f4 28 6a fb 1f |V.....1}u...(j..|
+00000060 76 ee 77 55 0b 33 94 82 e2 ee 73 2f 7f a7 f6 7c |v.wU.3....s/...||
+00000070 68 25 eb fd 56 5b 89 29 b4 32 b6 92 57 3f c3 f9 |h%..V[.).2..W?..|
+00000080 01 fb 01 25 7f 0f 10 14 03 03 00 01 01 16 03 03 |...%............|
+00000090 00 24 9a 9b 1b 57 2c 86 71 0c 6d 4f 6c 40 a2 98 |.$...W,.q.mOl@..|
+000000a0 7d e3 f5 75 0e 4a b7 82 1c d8 f7 8c 22 a5 5b 34 |}..u.J......".[4|
+000000b0 19 79 12 e2 a4 e6 17 03 03 00 21 53 7a cc 02 0f |.y........!Sz...|
+000000c0 6d b5 9d 8c ff 4a 2d 29 31 59 38 96 bb 6b a8 93 |m....J-)1Y8..k..|
+000000d0 09 af 38 c7 4d 6e 31 ef 18 d4 59 35 15 03 03 00 |..8.Mn1...Y5....|
+000000e0 16 1e 04 62 d6 6b 6c fc 0b 70 f8 32 d0 11 59 64 |...b.kl..p.2..Yd|
+000000f0 11 71 b0 ab ac 2d 6d |.q...-m|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-IssueTicketPreDisable b/src/crypto/tls/testdata/Server-TLSv12-IssueTicketPreDisable
index 30f0026815..e1ac9a61d2 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-IssueTicketPreDisable
+++ b/src/crypto/tls/testdata/Server-TLSv12-IssueTicketPreDisable
@@ -1,11 +1,11 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 00 60 01 00 00 5c 03 03 54 23 54 02 17 |....`...\..T#T..|
-00000010 f3 53 13 3d 48 88 c3 19 b9 d1 3d 33 7f f5 99 56 |.S.=H.....=3...V|
-00000020 04 71 1b d9 d5 64 8a 0d 4a 54 00 00 00 04 00 05 |.q...d..JT......|
-00000030 00 ff 01 00 00 2f 00 23 00 00 00 0d 00 22 00 20 |...../.#.....". |
-00000040 06 01 06 02 06 03 05 01 05 02 05 03 04 01 04 02 |................|
-00000050 04 03 03 01 03 02 03 03 02 01 02 02 02 03 01 01 |................|
-00000060 00 0f 00 01 01 |.....|
+00000000 16 03 01 00 5f 01 00 00 5b 03 03 be c5 99 df f1 |...._...[.......|
+00000010 cc c8 fd d9 4c c5 09 18 5f 59 9a 78 47 ef 00 d5 |....L..._Y.xG...|
+00000020 81 45 3e ac a0 a5 ee d6 d0 8c d8 00 00 04 00 05 |.E>.............|
+00000030 00 ff 02 01 00 00 2d 00 23 00 00 00 0d 00 20 00 |......-.#..... .|
+00000040 1e 06 01 06 02 06 03 05 01 05 02 05 03 04 01 04 |................|
+00000050 02 04 03 03 01 03 02 03 03 02 01 02 02 02 03 00 |................|
+00000060 0f 00 01 01 |....|
>>> Flow 2 (server to client)
00000000 16 03 03 00 35 02 00 00 31 03 03 00 00 00 00 00 |....5...1.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
@@ -57,31 +57,32 @@
000002f0 71 99 9b 26 6e 38 50 29 6c 90 a7 bd d9 16 03 03 |q..&n8P)l.......|
00000300 00 04 0e 00 00 00 |......|
>>> Flow 3 (client to server)
-00000000 16 03 03 00 86 10 00 00 82 00 80 27 e9 a4 f7 e7 |...........'....|
-00000010 df 25 de 84 8c 1f d6 e6 c3 11 28 55 9a c1 91 37 |.%........(U...7|
-00000020 84 f5 ba f8 80 0d ca 50 cb 1e 72 f7 97 6f c2 b2 |.......P..r..o..|
-00000030 04 4d 13 7c e0 6e a0 1f 91 e1 38 1b a2 c0 55 16 |.M.|.n....8...U.|
-00000040 7f 29 fc ed 1c 1a cf 72 14 c3 00 c1 dd 36 36 af |.).....r.....66.|
-00000050 a6 e4 a8 be ba ec 13 d0 1e d0 1d fd e1 5b 27 fd |.............['.|
-00000060 9a da 2e 12 c8 b0 b9 c2 b9 76 ec 7f 3c 98 b6 63 |.........v..<..c|
-00000070 bc da f0 07 7a 3d e7 61 f4 2f 12 80 3b f9 3b cc |....z=.a./..;.;.|
-00000080 05 c8 2f 7e 28 b2 73 bf 97 61 29 14 03 03 00 01 |../~(.s..a).....|
-00000090 01 16 03 03 00 24 17 59 a9 45 53 46 33 96 50 dd |.....$.Y.ESF3.P.|
-000000a0 3e 23 aa 91 38 f8 56 4a 2f 1a f2 b1 44 9b ce 17 |>#..8.VJ/...D...|
-000000b0 6b 8a 89 76 bc 67 b8 8b ba 90 |k..v.g....|
+00000000 16 03 03 00 86 10 00 00 82 00 80 59 1f 86 2f cd |...........Y../.|
+00000010 b9 8f 0d e8 f9 3a 5b a8 73 2f 33 8b c6 ef 5e e2 |.....:[.s/3...^.|
+00000020 78 93 fa 40 b7 b4 cb e7 3e 35 15 33 24 1d 63 5d |x..@....>5.3$.c]|
+00000030 dc 8d 45 94 3f 19 ed e0 3a f3 4e 44 62 1d ff ea |..E.?...:.NDb...|
+00000040 d6 e4 01 b0 26 c5 36 34 78 d1 e6 62 27 62 f0 29 |....&.64x..b'b.)|
+00000050 fd 7d 13 af 59 0a 41 fa 78 42 7d 0d d8 07 79 23 |.}..Y.A.xB}...y#|
+00000060 5e 4e cd 03 b1 3c bb 6d fb 19 54 49 f1 c7 d7 54 |^N...<.m..TI...T|
+00000070 3e af 11 40 8b 7e 3d 2c 8b e3 02 ad fd 29 88 48 |>..@.~=,.....).H|
+00000080 b1 ed 52 74 50 a7 ef 99 9f af bd 14 03 03 00 01 |..RtP...........|
+00000090 01 16 03 03 00 24 f3 c1 8c ee e7 4d 07 80 c4 c3 |.....$.....M....|
+000000a0 09 87 85 cd 64 46 73 c7 17 4e 9e 90 4c 63 30 35 |....dFs..N..Lc05|
+000000b0 52 f5 10 f6 60 75 fc 93 41 57 |R...`u..AW|
>>> Flow 4 (server to client)
-00000000 16 03 03 00 72 04 00 00 6e 00 00 00 00 00 68 00 |....r...n.....h.|
-00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 65 |...............e|
-00000020 ea 4b d1 ef ba 2d db 0c ba 9a d4 20 76 57 c8 ec |.K...-..... vW..|
-00000030 dc 2d 77 fb fb 3b 93 5f 53 e0 14 4f 90 fb d6 55 |.-w..;._S..O...U|
-00000040 57 8c 8d 0d 25 ea 5d 0d f2 91 e5 12 22 12 ec 7b |W...%.]....."..{|
-00000050 5f b6 6e fd 07 59 23 24 fc b1 97 ca ea 56 a5 c2 |_.n..Y#$.....V..|
-00000060 a0 e4 9e 99 64 f2 64 d0 75 7a 46 63 e3 dc 21 ed |....d.d.uzFc..!.|
-00000070 78 56 e9 e1 ab 66 80 14 03 03 00 01 01 16 03 03 |xV...f..........|
-00000080 00 24 fc 14 68 07 17 1f df b7 84 cb fd c1 e0 e4 |.$..h...........|
-00000090 f2 1a ea 34 b5 00 7f 70 be c8 1c 0a d6 55 e3 57 |...4...p.....U.W|
-000000a0 50 4e 6d 7d 8a 5d 17 03 03 00 21 24 27 50 40 c1 |PNm}.]....!$'P@.|
-000000b0 c5 bd c7 9f 95 d9 ba 2e 7b 0e db ea a7 31 81 05 |........{....1..|
-000000c0 75 43 b1 63 cf b8 55 92 ef 76 98 a9 15 03 03 00 |uC.c..U..v......|
-000000d0 16 d7 ea 3c 79 e7 a6 2f 61 39 ec 4e 95 86 48 5e |....|
+00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 94 |................|
+00000030 6f 2c b5 83 61 98 30 ec c6 53 ac a0 2a a9 72 53 |o,..a.0..S..*.rS|
+00000040 64 7c c5 d5 db 0a 80 d0 1e ea 59 c8 b8 60 ff b9 |d|........Y..`..|
+00000050 3d 06 68 16 cd 60 3b 15 e9 59 c1 a2 18 76 c2 1f |=.h..`;..Y...v..|
+00000060 fd 77 00 e6 38 33 94 98 69 cb 23 4a 61 d7 fe 1a |.w..83..i.#Ja...|
+00000070 e7 3a 57 b1 78 c7 c0 d1 03 bb 83 69 72 b9 25 c3 |.:W.x......ir.%.|
+00000080 2f f7 56 2e 95 6f 88 14 03 03 00 01 01 16 03 03 |/.V..o..........|
+00000090 00 24 a6 8c 15 5c ae a0 8c 03 cc d2 2c 45 aa 7a |.$...\......,E.z|
+000000a0 1d 1a ed 58 f4 92 a2 0d b0 a4 81 90 e3 a6 0b 09 |...X............|
+000000b0 8f f2 1b 61 c7 f7 17 03 03 00 21 cf 8f 7a 50 bc |...a......!..zP.|
+000000c0 a9 b6 d2 88 24 21 0b ef 5c e5 1c 34 4a d9 b6 b5 |....$!..\..4J...|
+000000d0 88 c6 14 8c 79 96 c5 0c 31 22 f8 7d 15 03 03 00 |....y...1".}....|
+000000e0 16 e7 69 82 9d e6 54 2d f9 6d 04 a9 5b 3e bc f9 |..i...T-.m..[>..|
+000000f0 4e 1a 07 04 7a 56 50 |N...zVP|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-RSA-AES-GCM b/src/crypto/tls/testdata/Server-TLSv12-RSA-AES-GCM
index 0ddfe022f2..f499d528f5 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-RSA-AES-GCM
+++ b/src/crypto/tls/testdata/Server-TLSv12-RSA-AES-GCM
@@ -1,15 +1,15 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 00 9c 01 00 00 98 03 03 53 04 f1 30 73 |...........S..0s|
-00000010 a1 ea 8c d2 90 1c c6 d6 0d 3c af 58 21 65 90 25 |.........<.X!e.%|
-00000020 5e fa f4 27 22 65 c9 68 90 b9 04 00 00 04 c0 2f |^..'"e.h......./|
-00000030 00 ff 01 00 00 6b 00 0b 00 04 03 00 01 02 00 0a |.....k..........|
-00000040 00 34 00 32 00 0e 00 0d 00 19 00 0b 00 0c 00 18 |.4.2............|
-00000050 00 09 00 0a 00 16 00 17 00 08 00 06 00 07 00 14 |................|
-00000060 00 15 00 04 00 05 00 12 00 13 00 01 00 02 00 03 |................|
-00000070 00 0f 00 10 00 11 00 0d 00 22 00 20 06 01 06 02 |.........". ....|
-00000080 06 03 05 01 05 02 05 03 04 01 04 02 04 03 03 01 |................|
-00000090 03 02 03 03 02 01 02 02 02 03 01 01 00 0f 00 01 |................|
-000000a0 01 |.|
+00000000 16 03 01 00 a1 01 00 00 9d 03 03 9a bc 85 2d 9c |..............-.|
+00000010 c3 b2 9a c4 2e 4d 2a 49 04 5f 0e 39 9a bd 17 d9 |.....M*I._.9....|
+00000020 1f 5e 07 82 2e 86 03 79 e5 b0 35 00 00 04 c0 2f |.^.....y..5..../|
+00000030 00 ff 02 01 00 00 6f 00 0b 00 04 03 00 01 02 00 |......o.........|
+00000040 0a 00 3a 00 38 00 0e 00 0d 00 19 00 1c 00 0b 00 |..:.8...........|
+00000050 0c 00 1b 00 18 00 09 00 0a 00 1a 00 16 00 17 00 |................|
+00000060 08 00 06 00 07 00 14 00 15 00 04 00 05 00 12 00 |................|
+00000070 13 00 01 00 02 00 03 00 0f 00 10 00 11 00 0d 00 |................|
+00000080 20 00 1e 06 01 06 02 06 03 05 01 05 02 05 03 04 | ...............|
+00000090 01 04 02 04 03 03 01 03 02 03 03 02 01 02 02 02 |................|
+000000a0 03 00 0f 00 01 01 |......|
>>> Flow 2 (server to client)
00000000 16 03 03 00 31 02 00 00 2d 03 03 00 00 00 00 00 |....1...-.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
@@ -63,31 +63,31 @@
00000310 75 71 b5 e5 54 5b 12 2e 8f 09 67 fd a7 24 20 3e |uq..T[....g..$ >|
00000320 b2 56 1c ce 97 28 5e f8 2b 2d 4f 9e f1 07 9f 6c |.V...(^.+-O....l|
00000330 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 a6 b5 68 1a |K[.V.2B.X..I..h.|
-00000340 41 03 56 6b dc 5a 89 04 01 00 80 a2 54 61 84 29 |A.Vk.Z......Ta.)|
-00000350 3e 97 4b 97 9a 9f 5c c0 49 6d 86 d2 79 8e 95 a1 |>.K...\.Im..y...|
-00000360 0a 5a 36 73 34 bb 05 73 35 47 e1 2b 5d f3 ef 36 |.Z6s4..s5G.+]..6|
-00000370 a8 32 e2 7e ef aa 3f 1f b3 64 60 d4 06 2e 98 e3 |.2.~..?..d`.....|
-00000380 11 e2 60 3c d6 20 17 63 b2 6f a0 cd 21 01 2b 4e |..`<. .c.o..!.+N|
-00000390 b2 a8 55 04 39 37 5c 6c 71 66 4d a3 eb 1b 83 67 |..U.97\lqfM....g|
-000003a0 6b 15 a0 56 9a f1 a2 79 92 29 ce 58 3c 10 4d 65 |k..V...y.).X<.Me|
-000003b0 1f 22 e3 ea d8 74 aa 01 7e ca f3 89 23 41 4d bd |."...t..~...#AM.|
-000003c0 df 77 4e 59 54 97 74 ad 07 ea c0 16 03 03 00 04 |.wNYT.t.........|
+00000340 41 03 56 6b dc 5a 89 05 01 00 80 ba cb 00 ec a3 |A.Vk.Z..........|
+00000350 0f 9b 9b 0a f8 5c 6f 46 b4 93 a9 de 47 35 43 62 |.....\oF....G5Cb|
+00000360 06 d5 19 36 25 80 7c 81 3a df 01 6e f1 8f 50 4f |...6%.|.:..n..PO|
+00000370 23 ee 1e 13 47 c6 9b 1c 3d 5f e3 8b 60 c3 f1 c4 |#...G...=_..`...|
+00000380 4b 36 4c 3c 71 bb 5c 0d 17 38 3a da 8c 3b c7 48 |K6L>> Flow 3 (client to server)
-00000000 16 03 03 00 46 10 00 00 42 41 04 45 65 ce f7 b9 |....F...BA.Ee...|
-00000010 52 e3 fb 13 db 91 f2 65 43 84 57 f5 1a 19 a0 e6 |R......eC.W.....|
-00000020 89 2d bb 2c 83 6b 62 f6 6f 1f 26 ae 59 67 bd dc |.-.,.kb.o.&.Yg..|
-00000030 c4 9e 0b dc 7d 6e f8 6b 95 8c 61 47 3d cd d1 df |....}n.k..aG=...|
-00000040 82 45 30 81 c3 a3 49 5d 85 59 70 14 03 03 00 01 |.E0...I].Yp.....|
-00000050 01 16 03 03 00 28 3f aa 85 33 f9 c6 95 a0 56 ff |.....(?..3....V.|
-00000060 1c f1 5a ba 6e 41 50 0c ab 92 e1 e2 8e 89 1c f1 |..Z.nAP.........|
-00000070 fa 54 1b f1 f5 00 01 12 6d c4 96 78 b6 87 |.T......m..x..|
+00000000 16 03 03 00 46 10 00 00 42 41 04 e3 f4 0a 79 25 |....F...BA....y%|
+00000010 6a a9 07 ae 8f 18 74 f3 0b e9 b1 0f da 35 58 4b |j.....t......5XK|
+00000020 91 ca d7 58 46 39 51 a2 ef 9a c0 ec e6 b2 e5 27 |...XF9Q........'|
+00000030 97 7e 45 fd b8 1f 72 8d ee 9b 58 82 45 2f e5 f4 |.~E...r...X.E/..|
+00000040 dd d5 cb ad f8 ac 4d 53 39 92 b9 14 03 03 00 01 |......MS9.......|
+00000050 01 16 03 03 00 28 b8 f7 4e 1a 2f d7 5b c9 6c ab |.....(..N./.[.l.|
+00000060 bc 90 ce d0 dd 58 68 ff 43 00 55 23 25 e1 4b 33 |.....Xh.C.U#%.K3|
+00000070 d4 60 96 88 df f2 23 dd b0 a9 f1 5b 91 73 |.`....#....[.s|
>>> Flow 4 (server to client)
00000000 14 03 03 00 01 01 16 03 03 00 28 00 00 00 00 00 |..........(.....|
-00000010 00 00 00 94 5c be 46 05 d6 d0 b0 3a 56 dc 2c 10 |....\.F....:V.,.|
-00000020 0f 6f 5d 33 33 7f a5 4e 74 84 bf 63 87 c4 f4 49 |.o]33..Nt..c...I|
-00000030 bc 6b ab 17 03 03 00 25 00 00 00 00 00 00 00 01 |.k.....%........|
-00000040 7e 4f f9 ae ae fe 6b a0 4a f8 0f 0b b4 b6 65 b6 |~O....k.J.....e.|
-00000050 be 24 5f 94 6d d1 db 54 11 07 b9 ce 01 15 03 03 |.$_.m..T........|
-00000060 00 1a 00 00 00 00 00 00 00 02 a8 1c d6 62 ac fd |.............b..|
-00000070 77 ba 23 92 5d 34 f1 17 c7 e1 1c 99 |w.#.]4......|
+00000010 00 00 00 2c 58 45 b5 85 96 2b 55 45 3c ff 4e 46 |...,XE...+UE<.NF|
+00000020 75 96 8e c2 44 24 24 3c bc 24 e7 fb a4 b1 5b b3 |u...D$$<.$....[.|
+00000030 b6 9a e6 17 03 03 00 25 00 00 00 00 00 00 00 01 |.......%........|
+00000040 89 60 e2 59 3a ab 37 21 89 95 ef c6 1d fb 55 4d |.`.Y:.7!......UM|
+00000050 9b ba b5 55 2b 11 81 5c d3 56 26 84 be 15 03 03 |...U+..\.V&.....|
+00000060 00 1a 00 00 00 00 00 00 00 02 66 4c a5 50 b7 7b |..........fL.P.{|
+00000070 20 60 08 0f 80 33 84 b2 f4 e4 81 d4 | `...3......|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-RSA-AES256-GCM-SHA384 b/src/crypto/tls/testdata/Server-TLSv12-RSA-AES256-GCM-SHA384
index 1c979c3fc0..a8aaf5f5d9 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-RSA-AES256-GCM-SHA384
+++ b/src/crypto/tls/testdata/Server-TLSv12-RSA-AES256-GCM-SHA384
@@ -1,14 +1,15 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 00 9a 01 00 00 96 03 03 37 7c 30 6d df |...........7|0m.|
-00000010 4d 94 90 04 59 df ec 01 11 77 29 b6 4f 95 50 ef |M...Y....w).O.P.|
-00000020 ca d0 0f f4 a6 35 98 3b ee 16 72 00 00 04 c0 30 |.....5.;..r....0|
-00000030 00 ff 01 00 00 69 00 0b 00 04 03 00 01 02 00 0a |.....i..........|
-00000040 00 34 00 32 00 0e 00 0d 00 19 00 0b 00 0c 00 18 |.4.2............|
-00000050 00 09 00 0a 00 16 00 17 00 08 00 06 00 07 00 14 |................|
-00000060 00 15 00 04 00 05 00 12 00 13 00 01 00 02 00 03 |................|
-00000070 00 0f 00 10 00 11 00 0d 00 20 00 1e 06 01 06 02 |......... ......|
-00000080 06 03 05 01 05 02 05 03 04 01 04 02 04 03 03 01 |................|
-00000090 03 02 03 03 02 01 02 02 02 03 00 0f 00 01 01 |...............|
+00000000 16 03 01 00 a1 01 00 00 9d 03 03 77 d9 ce 2c 4d |...........w..,M|
+00000010 da 71 bb b0 04 5f e1 29 ea 23 9f 21 2d cb 0b 75 |.q..._.).#.!-..u|
+00000020 be 29 65 df 8b a1 50 3f 14 a8 c0 00 00 04 c0 30 |.)e...P?.......0|
+00000030 00 ff 02 01 00 00 6f 00 0b 00 04 03 00 01 02 00 |......o.........|
+00000040 0a 00 3a 00 38 00 0e 00 0d 00 19 00 1c 00 0b 00 |..:.8...........|
+00000050 0c 00 1b 00 18 00 09 00 0a 00 1a 00 16 00 17 00 |................|
+00000060 08 00 06 00 07 00 14 00 15 00 04 00 05 00 12 00 |................|
+00000070 13 00 01 00 02 00 03 00 0f 00 10 00 11 00 0d 00 |................|
+00000080 20 00 1e 06 01 06 02 06 03 05 01 05 02 05 03 04 | ...............|
+00000090 01 04 02 04 03 03 01 03 02 03 03 02 01 02 02 02 |................|
+000000a0 03 00 0f 00 01 01 |......|
>>> Flow 2 (server to client)
00000000 16 03 03 00 31 02 00 00 2d 03 03 00 00 00 00 00 |....1...-.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
@@ -62,31 +63,31 @@
00000310 75 71 b5 e5 54 5b 12 2e 8f 09 67 fd a7 24 20 3e |uq..T[....g..$ >|
00000320 b2 56 1c ce 97 28 5e f8 2b 2d 4f 9e f1 07 9f 6c |.V...(^.+-O....l|
00000330 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 a6 b5 68 1a |K[.V.2B.X..I..h.|
-00000340 41 03 56 6b dc 5a 89 04 01 00 80 4f 66 0e d5 7f |A.Vk.Z.....Of...|
-00000350 a8 99 4d dc 5b a7 b0 32 67 b2 8a 2e ca 90 58 f0 |..M.[..2g.....X.|
-00000360 8d f1 fd 74 c1 3c 84 28 9d 25 7e 0a 61 f8 90 2d |...t.<.(.%~.a..-|
-00000370 99 f3 90 c9 26 ab a7 d2 38 87 e1 2b 12 6e 93 17 |....&...8..+.n..|
-00000380 3c 2f 11 8c d8 67 73 11 68 b9 d0 a7 ad 44 83 72 |.v.iM|
-000003b0 df 49 d4 f6 b4 ac f6 0b 1d d1 68 61 30 b1 52 07 |.I........ha0.R.|
-000003c0 a5 6d 31 5e 13 24 8c 32 cd 76 57 16 03 03 00 04 |.m1^.$.2.vW.....|
+00000340 41 03 56 6b dc 5a 89 05 01 00 80 3f a6 7f 45 a0 |A.Vk.Z.....?..E.|
+00000350 a7 43 d0 8a 4b 50 39 77 c3 61 eb 88 73 02 de 5c |.C..KP9w.a..s..\|
+00000360 4d 57 45 80 c1 ee 31 be bc ca 70 48 a1 a5 6c 40 |MWE...1...pH..l@|
+00000370 de 54 4f dd e2 2d d8 7a 4c 68 f0 fe 28 28 ec 45 |.TO..-.zLh..((.E|
+00000380 45 79 c6 f6 68 9d 5f b8 eb 76 0b 53 72 76 6f a7 |Ey..h._..v.Srvo.|
+00000390 00 9b 18 ba 85 ad f3 10 3d cf 08 9c 63 b2 84 4f |........=...c..O|
+000003a0 57 b6 b6 d8 3e ec c3 76 22 9e cf 31 d5 4c 68 f1 |W...>..v"..1.Lh.|
+000003b0 f3 a5 61 8a df b1 78 0f 14 9d 57 ef a8 d6 3b f5 |..a...x...W...;.|
+000003c0 a4 77 dd aa 5f e3 8b 2b cf d2 64 16 03 03 00 04 |.w.._..+..d.....|
000003d0 0e 00 00 00 |....|
>>> Flow 3 (client to server)
-00000000 16 03 03 00 46 10 00 00 42 41 04 08 73 d7 79 87 |....F...BA..s.y.|
-00000010 39 45 dd 69 33 71 a0 48 a0 8b 6e 2f 99 dc a1 4f |9E.i3q.H..n/...O|
-00000020 21 ca 70 b3 98 fe cc 5a 94 04 1b 8d 4d a4 46 24 |!.p....Z....M.F$|
-00000030 c6 61 bd e1 49 92 83 8d ea 22 fb b1 43 90 24 7e |.a..I...."..C.$~|
-00000040 d0 e5 4b cb c3 8a 41 f7 fd d1 9f 14 03 03 00 01 |..K...A.........|
-00000050 01 16 03 03 00 28 e3 99 f0 d3 65 4e 29 dd d6 eb |.....(....eN)...|
-00000060 c0 b3 f9 e2 8b bb 68 61 b2 7f 63 db de fb ae d2 |......ha..c.....|
-00000070 94 b7 45 9b 34 cb a4 26 3f 04 92 34 02 89 |..E.4..&?..4..|
+00000000 16 03 03 00 46 10 00 00 42 41 04 eb 6a bc 30 f6 |....F...BA..j.0.|
+00000010 ef 35 bb 4d 35 31 34 93 27 59 d9 6a fc e4 6c ac |.5.M514.'Y.j..l.|
+00000020 fe 23 c9 b4 88 66 98 15 a3 84 dc 53 95 ea bb 43 |.#...f.....S...C|
+00000030 16 dc 03 5e 4a 83 d2 47 32 a0 2d ad 56 40 bd 24 |...^J..G2.-.V@.$|
+00000040 ad 47 af fa f3 d7 50 d3 b5 02 67 14 03 03 00 01 |.G....P...g.....|
+00000050 01 16 03 03 00 28 8c 86 3b 84 aa 50 d3 e6 bf c9 |.....(..;..P....|
+00000060 c6 03 4b c5 07 ac 12 96 88 95 64 92 0d 0c d9 8f |..K.......d.....|
+00000070 11 2f e2 9e 32 e8 2b 4e a7 1a 88 0a a5 f6 |./..2.+N......|
>>> Flow 4 (server to client)
00000000 14 03 03 00 01 01 16 03 03 00 28 00 00 00 00 00 |..........(.....|
-00000010 00 00 00 5e 63 30 5d 4d 2b 87 3f 7b 9c 06 2e 44 |...^c0]M+.?{...D|
-00000020 92 c5 d0 e8 07 fa 9f db a7 2c dc ec 16 78 bd 37 |.........,...x.7|
-00000030 8a f7 24 17 03 03 00 25 00 00 00 00 00 00 00 01 |..$....%........|
-00000040 0b af 29 75 f0 67 6b 78 8c 3a 65 44 53 25 9e d5 |..)u.gkx.:eDS%..|
-00000050 8e 7e 24 5f c9 95 a1 3e 63 d2 52 09 32 15 03 03 |.~$_...>c.R.2...|
-00000060 00 1a 00 00 00 00 00 00 00 02 0b f2 f7 93 57 b3 |..............W.|
-00000070 5b 19 fd e7 a1 0f e9 41 ca f5 74 17 |[......A..t.|
+00000010 00 00 00 ac 6a 42 ae b7 31 ba 00 dd 82 2f 54 ad |....jB..1..../T.|
+00000020 85 f6 77 2a 3c 68 24 e6 f5 4b 0e 80 4f 4c 60 04 |..w*>> Flow 1 (client to server)
-00000000 16 03 01 00 e8 01 00 00 e4 03 03 52 cc 57 59 c3 |...........R.WY.|
-00000010 8b df 97 05 d8 5f 16 22 b4 b1 e7 cb 7d 2f 9b 58 |....._."....}/.X|
-00000020 a3 f4 d7 2c a4 c1 9d 49 ed 4b ba 20 90 da 90 3e |...,...I.K. ...>|
-00000030 36 19 7a db 56 43 26 f7 dc 42 57 33 22 ed 9d a4 |6.z.VC&..BW3"...|
-00000040 9d 53 da f8 9d 4e 60 66 71 a0 2e 2e 00 04 00 05 |.S...N`fq.......|
-00000050 00 ff 01 00 00 97 00 23 00 68 00 00 00 00 00 00 |.......#.h......|
-00000060 00 00 00 00 00 00 00 00 00 00 65 ea 4b d1 ef ba |..........e.K...|
-00000070 06 38 1e e1 88 82 3a cd 03 ac 3b 39 0a e0 19 fd |.8....:...;9....|
-00000080 af 6c 57 30 df 31 6e f7 92 38 4b 5d 77 90 39 ff |.lW0.1n..8K]w.9.|
-00000090 32 51 f5 ed 12 d7 b0 7c 4d 6c c5 76 e4 72 48 3e |2Q.....|Ml.v.rH>|
-000000a0 59 23 fe 0d 15 df f4 ba ea b9 67 16 23 8f 7d 15 |Y#........g.#.}.|
-000000b0 b6 11 f1 ab d7 d4 cd a3 21 82 92 2a 12 cf 95 f3 |........!..*....|
-000000c0 60 b2 00 0d 00 22 00 20 06 01 06 02 06 03 05 01 |`....". ........|
-000000d0 05 02 05 03 04 01 04 02 04 03 03 01 03 02 03 03 |................|
-000000e0 02 01 02 02 02 03 01 01 00 0f 00 01 01 |.............|
+00000000 16 03 01 00 f7 01 00 00 f3 03 03 6a 1a d3 0a d3 |...........j....|
+00000010 e0 34 f9 c4 1b cc 42 bc 0b eb 97 fd 51 b7 77 fd |.4....B.....Q.w.|
+00000020 50 0a 13 8c b6 ac 8e a1 ba 1f 74 20 fb 19 d1 6a |P.........t ...j|
+00000030 cf 1c 8b fb 77 97 7b 11 a5 fe 66 dc b8 b6 21 ad |....w.{...f...!.|
+00000040 8b b4 5f 38 ca 51 ca a3 af 40 84 8b 00 04 00 05 |.._8.Q...@......|
+00000050 00 ff 02 01 00 00 a5 00 23 00 78 50 46 ad c1 db |........#.xPF...|
+00000060 a8 38 86 7b 2b bb fd d0 c3 42 3e 00 00 00 00 00 |.8.{+....B>.....|
+00000070 00 00 00 00 00 00 00 00 00 00 00 94 6f 2c b5 83 |............o,..|
+00000080 61 e8 c1 5d af d6 da c9 8f df 1e c4 16 47 a0 dd |a..].........G..|
+00000090 cf 3c 9d 95 11 fe 01 fb 52 5b d0 aa 56 fb 04 d5 |.<......R[..V...|
+000000a0 7f 89 31 7d 75 e3 df f4 28 6a fb 1f 76 ee 77 55 |..1}u...(j..v.wU|
+000000b0 0b 33 94 82 e2 ee 73 2f 7f a7 f6 7c 68 25 eb fd |.3....s/...|h%..|
+000000c0 56 5b 89 29 b4 32 b6 92 57 3f c3 f9 01 fb 01 25 |V[.).2..W?.....%|
+000000d0 7f 0f 10 00 0d 00 20 00 1e 06 01 06 02 06 03 05 |...... .........|
+000000e0 01 05 02 05 03 04 01 04 02 04 03 03 01 03 02 03 |................|
+000000f0 03 02 01 02 02 02 03 00 0f 00 01 01 |............|
>>> Flow 2 (server to client)
00000000 16 03 03 00 51 02 00 00 4d 03 03 00 00 00 00 00 |....Q...M.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
-00000020 00 00 00 00 00 00 00 00 00 00 00 20 90 da 90 3e |........... ...>|
-00000030 36 19 7a db 56 43 26 f7 dc 42 57 33 22 ed 9d a4 |6.z.VC&..BW3"...|
-00000040 9d 53 da f8 9d 4e 60 66 71 a0 2e 2e 00 05 00 00 |.S...N`fq.......|
+00000020 00 00 00 00 00 00 00 00 00 00 00 20 fb 19 d1 6a |........... ...j|
+00000030 cf 1c 8b fb 77 97 7b 11 a5 fe 66 dc b8 b6 21 ad |....w.{...f...!.|
+00000040 8b b4 5f 38 ca 51 ca a3 af 40 84 8b 00 05 00 00 |.._8.Q...@......|
00000050 05 ff 01 00 01 00 14 03 03 00 01 01 16 03 03 00 |................|
-00000060 24 11 12 ff 28 10 14 4c e5 0e ad a7 fa f3 92 fb |$...(..L........|
-00000070 13 7d ae f2 b2 4a 6b a1 9e 67 cf a8 f7 8c 6f a0 |.}...Jk..g....o.|
-00000080 6c 30 0e 18 55 |l0..U|
+00000060 24 0e 65 19 5e 79 90 4b 40 13 f1 5b 2f ed 0b f5 |$.e.^y.K@..[/...|
+00000070 cc 39 23 24 91 b3 b2 49 f6 9b d5 60 f3 ed bd 2a |.9#$...I...`...*|
+00000080 31 00 14 5a 8e |1..Z.|
>>> Flow 3 (client to server)
-00000000 14 03 03 00 01 01 16 03 03 00 24 0d 46 41 8b 24 |..........$.FA.$|
-00000010 36 01 a9 fd 8b ec fc e6 b1 83 96 df 0d 3e 53 54 |6............>ST|
-00000020 58 b8 43 f2 a6 25 5e 1a ae 19 9e d2 28 44 92 |X.C..%^.....(D.|
+00000000 14 03 03 00 01 01 16 03 03 00 24 72 4d 5d 05 d6 |..........$rM]..|
+00000010 79 93 41 21 a7 86 75 49 50 fe b2 6c a9 38 d7 5e |y.A!..uIP..l.8.^|
+00000020 b7 f7 33 18 b0 53 ab ab b7 5b ee 09 e7 83 51 |..3..S...[....Q|
>>> Flow 4 (server to client)
-00000000 17 03 03 00 21 c4 fb f6 53 bb 3e 04 cc 0b a0 03 |....!...S.>.....|
-00000010 fa 49 96 da b5 8d b2 f2 e5 d8 f3 5c 27 57 4f 9c |.I.........\'WO.|
-00000020 30 00 34 fc 52 92 15 03 03 00 16 a3 02 7a 50 d2 |0.4.R........zP.|
-00000030 c6 b3 fc 69 8f e4 94 ae ab 22 ad 05 1d 15 69 b9 |...i....."....i.|
-00000040 a5 |.|
+00000000 17 03 03 00 21 1a 35 ab 27 ac db 7f e4 11 f2 b4 |....!.5.'.......|
+00000010 38 f5 3f 5d be 7a 58 74 92 05 a5 9c 8e a8 f2 ca |8.?].zXt........|
+00000020 cd f0 2e 18 62 57 15 03 03 00 16 33 18 76 93 bb |....bW.....3.v..|
+00000030 48 86 cc 13 79 ad e2 51 c6 ac 3e 89 2a 4f 05 e1 |H...y..Q..>.*O..|
+00000040 ee |.|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-ResumeDisabled b/src/crypto/tls/testdata/Server-TLSv12-ResumeDisabled
index db833f6555..9cbbd3f073 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-ResumeDisabled
+++ b/src/crypto/tls/testdata/Server-TLSv12-ResumeDisabled
@@ -1,19 +1,20 @@
>>> Flow 1 (client to server)
-00000000 16 03 01 00 e8 01 00 00 e4 03 03 54 23 54 02 a5 |...........T#T..|
-00000010 10 11 0f 6d e5 2d 2f e8 bb 52 b1 38 3f 65 01 43 |...m.-/..R.8?e.C|
-00000020 36 cc 48 f6 09 22 a1 85 20 28 3c 20 35 8b fe 7a |6.H..".. (< 5..z|
-00000030 41 3b 59 3a 5d b9 b3 21 f0 62 e9 0d 7b af f5 5d |A;Y:]..!.b..{..]|
-00000040 fa 65 1a 40 c8 ca cd 74 8c ef d2 fb 00 04 00 05 |.e.@...t........|
-00000050 00 ff 01 00 00 97 00 23 00 68 00 00 00 00 00 00 |.......#.h......|
-00000060 00 00 00 00 00 00 00 00 00 00 65 ea 4b d1 ef ba |..........e.K...|
-00000070 2d db 0c ba 9a d4 20 76 57 c8 ec dc 2d 77 fb fb |-..... vW...-w..|
-00000080 3b 93 5f 53 e0 14 4f 90 fb d6 55 57 8c 8d 0d 25 |;._S..O...UW...%|
-00000090 ea 5d 0d f2 91 e5 12 22 12 ec 7b 5f b6 6e fd 07 |.]....."..{_.n..|
-000000a0 59 23 24 fc b1 97 ca ea 56 a5 c2 a0 e4 9e 99 64 |Y#$.....V......d|
-000000b0 f2 64 d0 75 7a 46 63 e3 dc 21 ed 78 56 e9 e1 ab |.d.uzFc..!.xV...|
-000000c0 66 80 00 0d 00 22 00 20 06 01 06 02 06 03 05 01 |f....". ........|
-000000d0 05 02 05 03 04 01 04 02 04 03 03 01 03 02 03 03 |................|
-000000e0 02 01 02 02 02 03 01 01 00 0f 00 01 01 |.............|
+00000000 16 03 01 00 f7 01 00 00 f3 03 03 c0 99 dc 56 0b |..............V.|
+00000010 50 7d 49 f8 f3 f7 60 a1 c7 38 e0 90 1f de 78 c3 |P}I...`..8....x.|
+00000020 43 04 0d b4 4c c0 6e 01 40 ec 3a 20 93 7c bd 44 |C...L.n.@.: .|.D|
+00000030 57 52 7d dd 4d db b6 6d cc d5 44 34 a6 64 87 cb |WR}.M..m..D4.d..|
+00000040 cb dc 38 d4 33 3a 1a 6f fc f0 6f 73 00 04 00 05 |..8.3:.o..os....|
+00000050 00 ff 02 01 00 00 a5 00 23 00 78 50 46 ad c1 db |........#.xPF...|
+00000060 a8 38 86 7b 2b bb fd d0 c3 42 3e 00 00 00 00 00 |.8.{+....B>.....|
+00000070 00 00 00 00 00 00 00 00 00 00 00 94 6f 2c b5 83 |............o,..|
+00000080 61 98 30 ec c6 53 ac a0 2a a9 72 53 64 7c c5 d5 |a.0..S..*.rSd|..|
+00000090 db 0a 80 d0 1e ea 59 c8 b8 60 ff b9 3d 06 68 16 |......Y..`..=.h.|
+000000a0 cd 60 3b 15 e9 59 c1 a2 18 76 c2 1f fd 77 00 e6 |.`;..Y...v...w..|
+000000b0 38 33 94 98 69 cb 23 4a 61 d7 fe 1a e7 3a 57 b1 |83..i.#Ja....:W.|
+000000c0 78 c7 c0 d1 03 bb 83 69 72 b9 25 c3 2f f7 56 2e |x......ir.%./.V.|
+000000d0 95 6f 88 00 0d 00 20 00 1e 06 01 06 02 06 03 05 |.o.... .........|
+000000e0 01 05 02 05 03 04 01 04 02 04 03 03 01 03 02 03 |................|
+000000f0 03 02 01 02 02 02 03 00 0f 00 01 01 |............|
>>> Flow 2 (server to client)
00000000 16 03 03 00 31 02 00 00 2d 03 03 00 00 00 00 00 |....1...-.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
@@ -65,23 +66,23 @@
000002f0 6e 38 50 29 6c 90 a7 bd d9 16 03 03 00 04 0e 00 |n8P)l...........|
00000300 00 00 |..|
>>> Flow 3 (client to server)
-00000000 16 03 03 00 86 10 00 00 82 00 80 ae 02 dd 1f 1a |................|
-00000010 86 83 f5 2f 82 46 4b 29 58 aa a1 b3 56 8b 4e 40 |.../.FK)X...V.N@|
-00000020 ef 23 65 67 ad 48 e5 e1 fd ae dd bf 68 fd bd a6 |.#eg.H......h...|
-00000030 13 a0 7e 05 ab f7 20 e1 6a 4e d1 37 93 08 1d c9 |..~... .jN.7....|
-00000040 37 e0 b5 34 28 bf 20 45 45 da 0f 7e 51 a7 c6 ae |7..4(. EE..~Q...|
-00000050 61 6c 07 1b 73 ef da 6e 25 c4 ed be e3 3f da ae |al..s..n%....?..|
-00000060 cd 3c 17 9c 2e ee fb 47 9d b3 a1 b2 c3 5d e0 83 |.<.....G.....]..|
-00000070 74 20 37 2d 72 d6 d0 4d 58 0e 26 1c 50 22 95 08 |t 7-r..MX.&.P"..|
-00000080 7d e0 5f 86 99 9e 2c 2e a7 a0 7f 14 03 03 00 01 |}._...,.........|
-00000090 01 16 03 03 00 24 a2 ab 41 25 a5 cf 04 18 1d 98 |.....$..A%......|
-000000a0 88 6c 59 21 86 33 54 f4 35 b4 21 6e a5 29 d5 6e |.lY!.3T.5.!n.).n|
-000000b0 3d 08 72 b0 af 46 b5 8f 6b 86 |=.r..F..k.|
+00000000 16 03 03 00 86 10 00 00 82 00 80 5d 49 92 9d 5b |...........]I..[|
+00000010 41 7a 83 f0 6d 32 de b8 49 00 2d e0 2f f9 f1 12 |Az..m2..I.-./...|
+00000020 0f 49 45 2b 58 fd 1d 72 49 e7 74 74 bc 97 73 f7 |.IE+X..rI.tt..s.|
+00000030 01 a9 10 53 ea 4a b5 5d 09 92 01 62 b7 50 cd 46 |...S.J.]...b.P.F|
+00000040 79 ec 35 08 0d 41 5f 09 41 fa 77 30 48 14 6b fe |y.5..A_.A.w0H.k.|
+00000050 ca 12 d7 97 61 7a da 52 89 07 52 b0 81 c0 54 35 |....az.R..R...T5|
+00000060 7d 36 6c be 85 45 6b 67 e3 06 55 f7 af 40 d5 7d |}6l..Ekg..U..@.}|
+00000070 34 bb ee 0c 49 6b fb 0a c0 ec 04 85 28 4f 15 d7 |4...Ik......(O..|
+00000080 f3 e5 92 86 30 27 e9 15 b7 1d ae 14 03 03 00 01 |....0'..........|
+00000090 01 16 03 03 00 24 64 7a 6c c1 71 df b3 a2 a7 a8 |.....$dzl.q.....|
+000000a0 ea fd 04 d6 7c fc eb a1 18 21 42 f4 ba 09 75 1c |....|....!B...u.|
+000000b0 f7 00 01 37 cc bb e1 11 c9 ef |...7......|
>>> Flow 4 (server to client)
-00000000 14 03 03 00 01 01 16 03 03 00 24 59 20 4d c2 17 |..........$Y M..|
-00000010 8b 3c 9b 33 d9 f9 ef fb 80 18 1f 67 a7 58 12 89 |.<.3.......g.X..|
-00000020 4e 73 0f 2d 7b e6 c4 a6 79 73 01 da 22 e8 54 17 |Ns.-{...ys..".T.|
-00000030 03 03 00 21 36 ca 64 0f 4a 12 a5 50 3d 97 bb 39 |...!6.d.J..P=..9|
-00000040 02 fc ed d1 82 6a 9a 2e 21 79 f6 e1 b3 cc 32 db |.....j..!y....2.|
-00000050 0f 5d b3 fb a5 15 03 03 00 16 51 f4 be 57 7a df |.]........Q..Wz.|
-00000060 f1 f2 bd b5 51 5e 45 80 be 0b 9a 0c d1 19 3c 79 |....Q^E.......>> Flow 1 (client to server)
+00000000 16 03 01 00 6f 01 00 00 6b 03 03 6a 01 e6 6b 95 |....o...k..j..k.|
+00000010 30 a8 26 2a ba 53 dd 79 6e 54 fa 69 ee e0 23 d8 |0.&*.S.ynT.i..#.|
+00000020 1c 9e 7a 9a ea 7d 13 30 c1 00 d6 00 00 04 00 2f |..z..}.0......./|
+00000030 00 ff 02 01 00 00 3d 00 00 00 10 00 0e 00 00 0b |......=.........|
+00000040 73 6e 69 74 65 73 74 2e 63 6f 6d 00 0d 00 20 00 |snitest.com... .|
+00000050 1e 06 01 06 02 06 03 05 01 05 02 05 03 04 01 04 |................|
+00000060 02 04 03 03 01 03 02 03 03 02 01 02 02 02 03 00 |................|
+00000070 0f 00 01 01 |....|
+>>> Flow 2 (server to client)
+00000000 16 03 03 00 31 02 00 00 2d 03 03 00 00 00 00 00 |....1...-.......|
+00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 2f 00 00 |............./..|
+00000030 05 ff 01 00 01 00 16 03 03 02 00 0b 00 01 fc 00 |................|
+00000040 01 f9 00 01 f6 30 82 01 f2 30 82 01 5d a0 03 02 |.....0...0..]...|
+00000050 01 02 02 01 00 30 0b 06 09 2a 86 48 86 f7 0d 01 |.....0...*.H....|
+00000060 01 05 30 28 31 10 30 0e 06 03 55 04 0a 13 07 41 |..0(1.0...U....A|
+00000070 63 6d 65 20 43 6f 31 14 30 12 06 03 55 04 03 13 |cme Co1.0...U...|
+00000080 0b 73 6e 69 74 65 73 74 2e 63 6f 6d 30 1e 17 0d |.snitest.com0...|
+00000090 31 32 30 34 31 31 31 37 34 30 33 35 5a 17 0d 31 |120411174035Z..1|
+000000a0 33 30 34 31 31 31 37 34 35 33 35 5a 30 28 31 10 |30411174535Z0(1.|
+000000b0 30 0e 06 03 55 04 0a 13 07 41 63 6d 65 20 43 6f |0...U....Acme Co|
+000000c0 31 14 30 12 06 03 55 04 03 13 0b 73 6e 69 74 65 |1.0...U....snite|
+000000d0 73 74 2e 63 6f 6d 30 81 9d 30 0b 06 09 2a 86 48 |st.com0..0...*.H|
+000000e0 86 f7 0d 01 01 01 03 81 8d 00 30 81 89 02 81 81 |..........0.....|
+000000f0 00 bb 79 d6 f5 17 b5 e5 bf 46 10 d0 dc 69 be e6 |..y......F...i..|
+00000100 2b 07 43 5a d0 03 2d 8a 7a 43 85 b7 14 52 e7 a5 |+.CZ..-.zC...R..|
+00000110 65 4c 2c 78 b8 23 8c b5 b4 82 e5 de 1f 95 3b 7e |eL,x.#........;~|
+00000120 62 a5 2c a5 33 d6 fe 12 5c 7a 56 fc f5 06 bf fa |b.,.3...\zV.....|
+00000130 58 7b 26 3f b5 cd 04 d3 d0 c9 21 96 4a c7 f4 54 |X{&?......!.J..T|
+00000140 9f 5a bf ef 42 71 00 fe 18 99 07 7f 7e 88 7d 7d |.Z..Bq......~.}}|
+00000150 f1 04 39 c4 a2 2e db 51 c9 7c e3 c0 4c 3b 32 66 |..9....Q.|..L;2f|
+00000160 01 cf af b1 1d b8 71 9a 1d db db 89 6b ae da 2d |......q.....k..-|
+00000170 79 02 03 01 00 01 a3 32 30 30 30 0e 06 03 55 1d |y......2000...U.|
+00000180 0f 01 01 ff 04 04 03 02 00 a0 30 0d 06 03 55 1d |..........0...U.|
+00000190 0e 04 06 04 04 01 02 03 04 30 0f 06 03 55 1d 23 |.........0...U.#|
+000001a0 04 08 30 06 80 04 01 02 03 04 30 0b 06 09 2a 86 |..0.......0...*.|
+000001b0 48 86 f7 0d 01 01 05 03 81 81 00 89 c6 45 5f 1c |H............E_.|
+000001c0 1f 5e f8 eb 1a b1 74 ee 24 39 05 9f 5c 42 59 bb |.^....t.$9..\BY.|
+000001d0 1a 8d 86 cd b1 d0 56 f5 6a 71 7d a4 0e 95 ab 90 |......V.jq}.....|
+000001e0 f5 9e 8d ea f6 27 c1 57 99 50 94 db 08 02 26 6e |.....'.W.P....&n|
+000001f0 b3 4f c6 84 2d ea 8a 4b 68 d9 c1 38 91 03 ab 84 |.O..-..Kh..8....|
+00000200 fb 9e 1f 85 d9 b5 d2 3f f2 31 2c 86 70 fb b5 40 |.......?.1,.p..@|
+00000210 14 82 45 a4 eb af e2 64 d9 0c 8a 4c f4 f8 5b 0f |..E....d...L..[.|
+00000220 ac 12 ac 2f c4 a3 15 4b ad 52 46 28 68 af 96 c6 |.../...K.RF(h...|
+00000230 2c 65 25 d6 52 b6 e3 18 45 bd cc 16 03 03 00 04 |,e%.R...E.......|
+00000240 0e 00 00 00 |....|
+>>> Flow 3 (client to server)
+00000000 16 03 03 00 86 10 00 00 82 00 80 03 e1 70 db fd |.............p..|
+00000010 a5 69 b6 da ef 2a 3b 81 4a 2a bc 21 fb ae a2 31 |.i...*;.J*.!...1|
+00000020 ac d9 34 f0 62 ff da 86 9c b1 c6 e1 cd 63 dc 42 |..4.b........c.B|
+00000030 9a 72 b2 cf 6c 6a e0 f5 30 b7 7e 9e 4e a1 fe 64 |.r..lj..0.~.N..d|
+00000040 f8 a3 f6 f2 d1 44 3a 82 5a 39 c1 ac 29 22 f7 90 |.....D:.Z9..)"..|
+00000050 71 ee 2e 62 c1 1a 22 6f 00 7d 73 4c e4 a3 d2 9a |q..b.."o.}sL....|
+00000060 a4 34 4d fb 68 8e 99 91 e3 6d 14 f2 ac 4d 36 60 |.4M.h....m...M6`|
+00000070 9b 79 26 4b ff cf 80 3b 8d 8a 04 20 86 73 bb ff |.y&K...;... .s..|
+00000080 c3 99 22 04 89 0c 2c 65 a0 d6 4d 14 03 03 00 01 |.."...,e..M.....|
+00000090 01 16 03 03 00 40 a9 0b 6e 2d 74 2a ae 5e c3 36 |.....@..n-t*.^.6|
+000000a0 50 80 8a d0 81 b6 b4 76 56 39 c0 b6 f8 d9 d6 fb |P......vV9......|
+000000b0 bb 24 2d 82 ec 9f f7 d3 4d 9d e1 78 df ea 6a 0a |.$-.....M..x..j.|
+000000c0 59 e8 b2 36 31 8e 70 eb 3e e5 e9 1b 9d f3 d7 09 |Y..61.p.>.......|
+000000d0 9b 80 55 b5 e4 e4 |..U...|
+>>> Flow 4 (server to client)
+00000000 14 03 03 00 01 01 16 03 03 00 40 00 00 00 00 00 |..........@.....|
+00000010 00 00 00 00 00 00 00 00 00 00 00 a4 81 32 49 09 |.............2I.|
+00000020 07 8c 05 d1 1c 18 fe ca d7 b8 7f 66 b0 42 2b 6d |...........f.B+m|
+00000030 5a cc 72 01 3e 75 3c 23 a2 b7 56 64 b2 b3 0f 1d |Z.r.>u<#..Vd....|
+00000040 ce a7 02 f7 88 bf 78 93 37 85 12 17 03 03 00 40 |......x.7......@|
+00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+00000060 49 b3 f9 ba be 6f df 65 cd 37 79 07 57 40 a4 30 |I....o.e.7y.W@.0|
+00000070 f1 f6 d7 2e 87 d2 bd 5c f9 cf 13 c5 91 eb 0c 41 |.......\.......A|
+00000080 c3 13 fb b2 de 0c 59 a9 1e e4 d5 b2 ea 4f 88 df |......Y......O..|
+00000090 15 03 03 00 30 00 00 00 00 00 00 00 00 00 00 00 |....0...........|
+000000a0 00 00 00 00 00 b2 e1 cb 94 94 30 e1 da 42 ea df |..........0..B..|
+000000b0 fc 8d 54 8d b2 d9 3b 33 4e 53 98 e9 14 e3 c1 d9 |..T...;3NS......|
+000000c0 de b8 e5 51 ca |...Q.|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-SNI-GetCertificateNotFound b/src/crypto/tls/testdata/Server-TLSv12-SNI-GetCertificateNotFound
new file mode 100644
index 0000000000..ebd284bce0
--- /dev/null
+++ b/src/crypto/tls/testdata/Server-TLSv12-SNI-GetCertificateNotFound
@@ -0,0 +1,76 @@
+>>> Flow 1 (client to server)
+00000000 16 03 01 00 6f 01 00 00 6b 03 03 3d 70 50 1f cc |....o...k..=pP..|
+00000010 5b 7a b5 67 c2 fc e8 65 a9 3d 7c 2a 93 ef 0d 41 |[z.g...e.=|*...A|
+00000020 a1 7d 78 a8 af 7d 70 af ce 1b 0e 00 00 04 00 2f |.}x..}p......../|
+00000030 00 ff 02 01 00 00 3d 00 00 00 10 00 0e 00 00 0b |......=.........|
+00000040 73 6e 69 74 65 73 74 2e 63 6f 6d 00 0d 00 20 00 |snitest.com... .|
+00000050 1e 06 01 06 02 06 03 05 01 05 02 05 03 04 01 04 |................|
+00000060 02 04 03 03 01 03 02 03 03 02 01 02 02 02 03 00 |................|
+00000070 0f 00 01 01 |....|
+>>> Flow 2 (server to client)
+00000000 16 03 03 00 31 02 00 00 2d 03 03 00 00 00 00 00 |....1...-.......|
+00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 2f 00 00 |............./..|
+00000030 05 ff 01 00 01 00 16 03 03 02 00 0b 00 01 fc 00 |................|
+00000040 01 f9 00 01 f6 30 82 01 f2 30 82 01 5d a0 03 02 |.....0...0..]...|
+00000050 01 02 02 01 00 30 0b 06 09 2a 86 48 86 f7 0d 01 |.....0...*.H....|
+00000060 01 05 30 28 31 10 30 0e 06 03 55 04 0a 13 07 41 |..0(1.0...U....A|
+00000070 63 6d 65 20 43 6f 31 14 30 12 06 03 55 04 03 13 |cme Co1.0...U...|
+00000080 0b 73 6e 69 74 65 73 74 2e 63 6f 6d 30 1e 17 0d |.snitest.com0...|
+00000090 31 32 30 34 31 31 31 37 34 30 33 35 5a 17 0d 31 |120411174035Z..1|
+000000a0 33 30 34 31 31 31 37 34 35 33 35 5a 30 28 31 10 |30411174535Z0(1.|
+000000b0 30 0e 06 03 55 04 0a 13 07 41 63 6d 65 20 43 6f |0...U....Acme Co|
+000000c0 31 14 30 12 06 03 55 04 03 13 0b 73 6e 69 74 65 |1.0...U....snite|
+000000d0 73 74 2e 63 6f 6d 30 81 9d 30 0b 06 09 2a 86 48 |st.com0..0...*.H|
+000000e0 86 f7 0d 01 01 01 03 81 8d 00 30 81 89 02 81 81 |..........0.....|
+000000f0 00 bb 79 d6 f5 17 b5 e5 bf 46 10 d0 dc 69 be e6 |..y......F...i..|
+00000100 2b 07 43 5a d0 03 2d 8a 7a 43 85 b7 14 52 e7 a5 |+.CZ..-.zC...R..|
+00000110 65 4c 2c 78 b8 23 8c b5 b4 82 e5 de 1f 95 3b 7e |eL,x.#........;~|
+00000120 62 a5 2c a5 33 d6 fe 12 5c 7a 56 fc f5 06 bf fa |b.,.3...\zV.....|
+00000130 58 7b 26 3f b5 cd 04 d3 d0 c9 21 96 4a c7 f4 54 |X{&?......!.J..T|
+00000140 9f 5a bf ef 42 71 00 fe 18 99 07 7f 7e 88 7d 7d |.Z..Bq......~.}}|
+00000150 f1 04 39 c4 a2 2e db 51 c9 7c e3 c0 4c 3b 32 66 |..9....Q.|..L;2f|
+00000160 01 cf af b1 1d b8 71 9a 1d db db 89 6b ae da 2d |......q.....k..-|
+00000170 79 02 03 01 00 01 a3 32 30 30 30 0e 06 03 55 1d |y......2000...U.|
+00000180 0f 01 01 ff 04 04 03 02 00 a0 30 0d 06 03 55 1d |..........0...U.|
+00000190 0e 04 06 04 04 01 02 03 04 30 0f 06 03 55 1d 23 |.........0...U.#|
+000001a0 04 08 30 06 80 04 01 02 03 04 30 0b 06 09 2a 86 |..0.......0...*.|
+000001b0 48 86 f7 0d 01 01 05 03 81 81 00 89 c6 45 5f 1c |H............E_.|
+000001c0 1f 5e f8 eb 1a b1 74 ee 24 39 05 9f 5c 42 59 bb |.^....t.$9..\BY.|
+000001d0 1a 8d 86 cd b1 d0 56 f5 6a 71 7d a4 0e 95 ab 90 |......V.jq}.....|
+000001e0 f5 9e 8d ea f6 27 c1 57 99 50 94 db 08 02 26 6e |.....'.W.P....&n|
+000001f0 b3 4f c6 84 2d ea 8a 4b 68 d9 c1 38 91 03 ab 84 |.O..-..Kh..8....|
+00000200 fb 9e 1f 85 d9 b5 d2 3f f2 31 2c 86 70 fb b5 40 |.......?.1,.p..@|
+00000210 14 82 45 a4 eb af e2 64 d9 0c 8a 4c f4 f8 5b 0f |..E....d...L..[.|
+00000220 ac 12 ac 2f c4 a3 15 4b ad 52 46 28 68 af 96 c6 |.../...K.RF(h...|
+00000230 2c 65 25 d6 52 b6 e3 18 45 bd cc 16 03 03 00 04 |,e%.R...E.......|
+00000240 0e 00 00 00 |....|
+>>> Flow 3 (client to server)
+00000000 16 03 03 00 86 10 00 00 82 00 80 1c 6d 66 c4 c1 |............mf..|
+00000010 92 07 67 6c f7 54 32 70 31 53 89 8d 60 29 d8 df |..gl.T2p1S..`)..|
+00000020 8b b9 62 a0 3c 79 e3 67 45 e9 6d 6e f5 9b cd 18 |..b.>> Flow 4 (server to client)
+00000000 14 03 03 00 01 01 16 03 03 00 40 00 00 00 00 00 |..........@.....|
+00000010 00 00 00 00 00 00 00 00 00 00 00 88 5b 91 f7 4e |............[..N|
+00000020 07 30 30 98 88 5c de 79 e0 63 1a 91 17 3a 2f bf |.00..\.y.c...:/.|
+00000030 8b 6b 61 f9 56 d7 43 bf 87 42 34 f3 09 b1 5e 67 |.ka.V.C..B4...^g|
+00000040 a2 33 0d 15 b8 7d c5 a8 35 50 4f 17 03 03 00 40 |.3...}..5PO....@|
+00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+00000060 50 27 94 e0 5a da 24 8f b2 d3 f3 f6 a4 1e f6 ac |P'..Z.$.........|
+00000070 b9 2a 48 85 41 f7 84 4c 1e f4 ca aa 90 1f f8 1b |.*H.A..L........|
+00000080 2f 53 01 2b 5e 48 82 ab f0 70 ad 4b 8f 5d bd 27 |/S.+^H...p.K.].'|
+00000090 15 03 03 00 30 00 00 00 00 00 00 00 00 00 00 00 |....0...........|
+000000a0 00 00 00 00 00 5c 38 ba 1e 5a 18 9a a0 31 aa 98 |.....\8..Z...1..|
+000000b0 68 3e ba 01 42 ee b7 78 1a ed 56 f8 b6 49 a7 4c |h>..B..x..V..I.L|
+000000c0 e1 8d dd 1c 9c |.....|
diff --git a/src/crypto/tls/ticket.go b/src/crypto/tls/ticket.go
index 0923027c70..7be50ce68c 100644
--- a/src/crypto/tls/ticket.go
+++ b/src/crypto/tls/ticket.go
@@ -22,6 +22,9 @@ type sessionState struct {
cipherSuite uint16
masterSecret []byte
certificates [][]byte
+ // usedOldKey is true if the ticket from which this session came from
+ // was encrypted with an older key and thus should be refreshed.
+ usedOldKey bool
}
func (s *sessionState) equal(i interface{}) bool {
@@ -132,20 +135,23 @@ func (s *sessionState) unmarshal(data []byte) bool {
func (c *Conn) encryptTicket(state *sessionState) ([]byte, error) {
serialized := state.marshal()
- encrypted := make([]byte, aes.BlockSize+len(serialized)+sha256.Size)
- iv := encrypted[:aes.BlockSize]
+ encrypted := make([]byte, ticketKeyNameLen+aes.BlockSize+len(serialized)+sha256.Size)
+ keyName := encrypted[:ticketKeyNameLen]
+ iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+aes.BlockSize]
macBytes := encrypted[len(encrypted)-sha256.Size:]
if _, err := io.ReadFull(c.config.rand(), iv); err != nil {
return nil, err
}
- block, err := aes.NewCipher(c.config.SessionTicketKey[:16])
+ key := c.config.ticketKeys()[0]
+ copy(keyName, key.keyName[:])
+ block, err := aes.NewCipher(key.aesKey[:])
if err != nil {
return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error())
}
- cipher.NewCTR(block, iv).XORKeyStream(encrypted[aes.BlockSize:], serialized)
+ cipher.NewCTR(block, iv).XORKeyStream(encrypted[ticketKeyNameLen+aes.BlockSize:], serialized)
- mac := hmac.New(sha256.New, c.config.SessionTicketKey[16:32])
+ mac := hmac.New(sha256.New, key.hmacKey[:])
mac.Write(encrypted[:len(encrypted)-sha256.Size])
mac.Sum(macBytes[:0])
@@ -154,14 +160,29 @@ func (c *Conn) encryptTicket(state *sessionState) ([]byte, error) {
func (c *Conn) decryptTicket(encrypted []byte) (*sessionState, bool) {
if c.config.SessionTicketsDisabled ||
- len(encrypted) < aes.BlockSize+sha256.Size {
+ len(encrypted) < ticketKeyNameLen+aes.BlockSize+sha256.Size {
return nil, false
}
- iv := encrypted[:aes.BlockSize]
+ keyName := encrypted[:ticketKeyNameLen]
+ iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+aes.BlockSize]
macBytes := encrypted[len(encrypted)-sha256.Size:]
- mac := hmac.New(sha256.New, c.config.SessionTicketKey[16:32])
+ keys := c.config.ticketKeys()
+ keyIndex := -1
+ for i, candidateKey := range keys {
+ if bytes.Equal(keyName, candidateKey.keyName[:]) {
+ keyIndex = i
+ break
+ }
+ }
+
+ if keyIndex == -1 {
+ return nil, false
+ }
+ key := &keys[keyIndex]
+
+ mac := hmac.New(sha256.New, key.hmacKey[:])
mac.Write(encrypted[:len(encrypted)-sha256.Size])
expected := mac.Sum(nil)
@@ -169,15 +190,15 @@ func (c *Conn) decryptTicket(encrypted []byte) (*sessionState, bool) {
return nil, false
}
- block, err := aes.NewCipher(c.config.SessionTicketKey[:16])
+ block, err := aes.NewCipher(key.aesKey[:])
if err != nil {
return nil, false
}
- ciphertext := encrypted[aes.BlockSize : len(encrypted)-sha256.Size]
+ ciphertext := encrypted[ticketKeyNameLen+aes.BlockSize : len(encrypted)-sha256.Size]
plaintext := ciphertext
cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext)
- state := new(sessionState)
+ state := &sessionState{usedOldKey: keyIndex > 0}
ok := state.unmarshal(plaintext)
return state, ok
}
diff --git a/src/crypto/x509/root_cgo_darwin.go b/src/crypto/x509/root_cgo_darwin.go
index 39a5781a8e..9fb08ddf0b 100644
--- a/src/crypto/x509/root_cgo_darwin.go
+++ b/src/crypto/x509/root_cgo_darwin.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 cgo,!arm
+// +build cgo,!arm,!arm64
package x509
diff --git a/src/crypto/x509/root_darwin.go b/src/crypto/x509/root_darwin.go
index 96b6599dac..78de56c221 100644
--- a/src/crypto/x509/root_darwin.go
+++ b/src/crypto/x509/root_darwin.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.
-//go:generate go run root_darwin_arm_gen.go -output root_darwin_arm.go
+//go:generate go run root_darwin_arm_gen.go -output root_darwin_armx.go
package x509
diff --git a/src/crypto/x509/root_darwin_arm_gen.go b/src/crypto/x509/root_darwin_arm_gen.go
index 194e016045..40e85b752e 100644
--- a/src/crypto/x509/root_darwin_arm_gen.go
+++ b/src/crypto/x509/root_darwin_arm_gen.go
@@ -4,7 +4,7 @@
// +build ignore
-// Generates root_darwin_arm.go.
+// Generates root_darwin_armx.go.
//
// As of iOS 8, there is no API for querying the system trusted X.509 root
// certificates. We could use SecTrustEvaluate to verify that a trust chain
@@ -31,7 +31,7 @@ import (
"strings"
)
-var output = flag.String("output", "root_darwin_arm.go", "file name to write")
+var output = flag.String("output", "root_darwin_armx.go", "file name to write")
func main() {
certs, err := selectCerts()
@@ -178,6 +178,9 @@ const header = `
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build darwin
+// +build arm arm64
+
package x509
func initSystemRoots() {
diff --git a/src/crypto/x509/root_darwin_arm.go b/src/crypto/x509/root_darwin_armx.go
similarity index 99%
rename from src/crypto/x509/root_darwin_arm.go
rename to src/crypto/x509/root_darwin_armx.go
index 43cd9ec2bd..62b7d24c91 100644
--- a/src/crypto/x509/root_darwin_arm.go
+++ b/src/crypto/x509/root_darwin_armx.go
@@ -1,9 +1,12 @@
-// Created by root_darwin_arm_gen --output root_darwin_arm.go; DO NOT EDIT
+// Created by root_darwin_arm_gen --output root_darwin_armx.go; DO NOT EDIT
// Copyright 2015 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 darwin
+// +build arm arm64
+
package x509
func initSystemRoots() {
diff --git a/src/crypto/x509/root_darwin_test.go b/src/crypto/x509/root_darwin_test.go
index e4718d0e33..cc6d23c505 100644
--- a/src/crypto/x509/root_darwin_test.go
+++ b/src/crypto/x509/root_darwin_test.go
@@ -10,8 +10,9 @@ import (
)
func TestSystemRoots(t *testing.T) {
- if runtime.GOARCH == "arm" {
- t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
+ switch runtime.GOARCH {
+ case "arm", "arm64":
+ t.Skipf("skipping on %s/%s, no system root", runtime.GOOS, runtime.GOARCH)
}
sysRoots := systemRootsPool() // actual system roots
diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go
index 7226d0a8d5..21b870c171 100644
--- a/src/crypto/x509/verify.go
+++ b/src/crypto/x509/verify.go
@@ -215,6 +215,10 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e
return c.systemVerify(&opts)
}
+ if len(c.UnhandledCriticalExtensions) > 0 {
+ return nil, UnhandledCriticalExtension{}
+ }
+
if opts.Roots == nil {
opts.Roots = systemRootsPool()
if opts.Roots == nil {
diff --git a/src/crypto/x509/x509.go b/src/crypto/x509/x509.go
index e75120e9f3..be6c013464 100644
--- a/src/crypto/x509/x509.go
+++ b/src/crypto/x509/x509.go
@@ -37,8 +37,10 @@ type pkixPublicKey struct {
// typically found in PEM blocks with "BEGIN PUBLIC KEY".
func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err error) {
var pki publicKeyInfo
- if _, err = asn1.Unmarshal(derBytes, &pki); err != nil {
- return
+ if rest, err := asn1.Unmarshal(derBytes, &pki); err != nil {
+ return nil, err
+ } else if len(rest) != 0 {
+ return nil, errors.New("x509: trailing data after ASN.1 of public-key")
}
algo := getPublicKeyAlgorithmFromOID(pki.Algorithm.Algorithm)
if algo == UnknownPublicKeyAlgorithm {
@@ -488,6 +490,16 @@ type Certificate struct {
// field is not populated when parsing certificates, see Extensions.
ExtraExtensions []pkix.Extension
+ // UnhandledCriticalExtensions contains a list of extension IDs that
+ // were not (fully) processed when parsing. Verify will fail if this
+ // slice is non-empty, unless verification is delegated to an OS
+ // library which understands all the critical extensions.
+ //
+ // Users can access these extensions using Extensions and can remove
+ // elements from this slice if they believe that they have been
+ // handled.
+ UnhandledCriticalExtensions []asn1.ObjectIdentifier
+
ExtKeyUsage []ExtKeyUsage // Sequence of extended key usages.
UnknownExtKeyUsage []asn1.ObjectIdentifier // Encountered extended key usages unknown to this package.
@@ -619,6 +631,12 @@ func (c *Certificate) CheckSignatureFrom(parent *Certificate) (err error) {
// CheckSignature verifies that signature is a valid signature over signed from
// c's public key.
func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature []byte) (err error) {
+ return checkSignature(algo, signed, signature, c.PublicKey)
+}
+
+// CheckSignature verifies that signature is a valid signature over signed from
+// a crypto.PublicKey.
+func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey crypto.PublicKey) (err error) {
var hashType crypto.Hash
switch algo {
@@ -642,13 +660,15 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature
h.Write(signed)
digest := h.Sum(nil)
- switch pub := c.PublicKey.(type) {
+ switch pub := publicKey.(type) {
case *rsa.PublicKey:
return rsa.VerifyPKCS1v15(pub, hashType, digest, signature)
case *dsa.PublicKey:
dsaSig := new(dsaSignature)
- if _, err := asn1.Unmarshal(signature, dsaSig); err != nil {
+ if rest, err := asn1.Unmarshal(signature, dsaSig); err != nil {
return err
+ } else if len(rest) != 0 {
+ return errors.New("x509: trailing data after DSA signature")
}
if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 {
return errors.New("x509: DSA signature contained zero or negative values")
@@ -659,8 +679,10 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature
return
case *ecdsa.PublicKey:
ecdsaSig := new(ecdsaSignature)
- if _, err := asn1.Unmarshal(signature, ecdsaSig); err != nil {
+ if rest, err := asn1.Unmarshal(signature, ecdsaSig); err != nil {
return err
+ } else if len(rest) != 0 {
+ return errors.New("x509: trailing data after ECDSA signature")
}
if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
return errors.New("x509: ECDSA signature contained zero or negative values")
@@ -729,10 +751,13 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{
switch algo {
case RSA:
p := new(rsaPublicKey)
- _, err := asn1.Unmarshal(asn1Data, p)
+ rest, err := asn1.Unmarshal(asn1Data, p)
if err != nil {
return nil, err
}
+ if len(rest) != 0 {
+ return nil, errors.New("x509: trailing data after RSA public key")
+ }
if p.N.Sign() <= 0 {
return nil, errors.New("x509: RSA modulus is not a positive number")
@@ -748,16 +773,22 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{
return pub, nil
case DSA:
var p *big.Int
- _, err := asn1.Unmarshal(asn1Data, &p)
+ rest, err := asn1.Unmarshal(asn1Data, &p)
if err != nil {
return nil, err
}
+ if len(rest) != 0 {
+ return nil, errors.New("x509: trailing data after DSA public key")
+ }
paramsData := keyData.Algorithm.Parameters.FullBytes
params := new(dsaAlgorithmParameters)
- _, err = asn1.Unmarshal(paramsData, params)
+ rest, err = asn1.Unmarshal(paramsData, params)
if err != nil {
return nil, err
}
+ if len(rest) != 0 {
+ return nil, errors.New("x509: trailing data after DSA parameters")
+ }
if p.Sign() <= 0 || params.P.Sign() <= 0 || params.Q.Sign() <= 0 || params.G.Sign() <= 0 {
return nil, errors.New("x509: zero or negative DSA parameter")
}
@@ -773,10 +804,13 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{
case ECDSA:
paramsData := keyData.Algorithm.Parameters.FullBytes
namedCurveOID := new(asn1.ObjectIdentifier)
- _, err := asn1.Unmarshal(paramsData, namedCurveOID)
+ rest, err := asn1.Unmarshal(paramsData, namedCurveOID)
if err != nil {
return nil, err
}
+ if len(rest) != 0 {
+ return nil, errors.New("x509: trailing data after ECDSA parameters")
+ }
namedCurve := namedCurveFromOID(*namedCurveOID)
if namedCurve == nil {
return nil, errors.New("x509: unsupported elliptic curve")
@@ -814,7 +848,11 @@ func parseSANExtension(value []byte) (dnsNames, emailAddresses []string, ipAddre
// iPAddress [7] OCTET STRING,
// registeredID [8] OBJECT IDENTIFIER }
var seq asn1.RawValue
- if _, err = asn1.Unmarshal(value, &seq); err != nil {
+ var rest []byte
+ if rest, err = asn1.Unmarshal(value, &seq); err != nil {
+ return
+ } else if len(rest) != 0 {
+ err = errors.New("x509: trailing data after X.509 extension")
return
}
if !seq.IsCompound || seq.Tag != 16 || seq.Class != 0 {
@@ -822,7 +860,7 @@ func parseSANExtension(value []byte) (dnsNames, emailAddresses []string, ipAddre
return
}
- rest := seq.Bytes
+ rest = seq.Bytes
for len(rest) > 0 {
var v asn1.RawValue
rest, err = asn1.Unmarshal(rest, &v)
@@ -876,11 +914,15 @@ func parseCertificate(in *certificate) (*Certificate, error) {
out.SerialNumber = in.TBSCertificate.SerialNumber
var issuer, subject pkix.RDNSequence
- if _, err := asn1.Unmarshal(in.TBSCertificate.Subject.FullBytes, &subject); err != nil {
+ if rest, err := asn1.Unmarshal(in.TBSCertificate.Subject.FullBytes, &subject); err != nil {
return nil, err
+ } else if len(rest) != 0 {
+ return nil, errors.New("x509: trailing data after X.509 subject")
}
- if _, err := asn1.Unmarshal(in.TBSCertificate.Issuer.FullBytes, &issuer); err != nil {
+ if rest, err := asn1.Unmarshal(in.TBSCertificate.Issuer.FullBytes, &issuer); err != nil {
return nil, err
+ } else if len(rest) != 0 {
+ return nil, errors.New("x509: trailing data after X.509 subject")
}
out.Issuer.FillFromRDNSequence(&issuer)
@@ -891,15 +933,17 @@ func parseCertificate(in *certificate) (*Certificate, error) {
for _, e := range in.TBSCertificate.Extensions {
out.Extensions = append(out.Extensions, e)
- failIfCritical := false
+ unhandled := false
if len(e.Id) == 4 && e.Id[0] == 2 && e.Id[1] == 5 && e.Id[2] == 29 {
switch e.Id[3] {
case 15:
// RFC 5280, 4.2.1.3
var usageBits asn1.BitString
- if _, err := asn1.Unmarshal(e.Value, &usageBits); err != nil {
+ if rest, err := asn1.Unmarshal(e.Value, &usageBits); err != nil {
return nil, err
+ } else if len(rest) != 0 {
+ return nil, errors.New("x509: trailing data after X.509 KeyUsage")
}
var usage int
@@ -913,8 +957,10 @@ func parseCertificate(in *certificate) (*Certificate, error) {
case 19:
// RFC 5280, 4.2.1.9
var constraints basicConstraints
- if _, err := asn1.Unmarshal(e.Value, &constraints); err != nil {
+ if rest, err := asn1.Unmarshal(e.Value, &constraints); err != nil {
return nil, err
+ } else if len(rest) != 0 {
+ return nil, errors.New("x509: trailing data after X.509 BasicConstraints")
}
out.BasicConstraintsValid = true
@@ -930,7 +976,7 @@ func parseCertificate(in *certificate) (*Certificate, error) {
if len(out.DNSNames) == 0 && len(out.EmailAddresses) == 0 && len(out.IPAddresses) == 0 {
// If we didn't parse anything then we do the critical check, below.
- failIfCritical = true
+ unhandled = true
}
case 30:
@@ -950,8 +996,10 @@ func parseCertificate(in *certificate) (*Certificate, error) {
// BaseDistance ::= INTEGER (0..MAX)
var constraints nameConstraints
- if _, err := asn1.Unmarshal(e.Value, &constraints); err != nil {
+ if rest, err := asn1.Unmarshal(e.Value, &constraints); err != nil {
return nil, err
+ } else if len(rest) != 0 {
+ return nil, errors.New("x509: trailing data after X.509 NameConstraints")
}
if len(constraints.Excluded) > 0 && e.Critical {
@@ -983,15 +1031,20 @@ func parseCertificate(in *certificate) (*Certificate, error) {
// nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
var cdp []distributionPoint
- if _, err := asn1.Unmarshal(e.Value, &cdp); err != nil {
+ if rest, err := asn1.Unmarshal(e.Value, &cdp); err != nil {
return nil, err
+ } else if len(rest) != 0 {
+ return nil, errors.New("x509: trailing data after X.509 CRL distribution point")
}
for _, dp := range cdp {
var n asn1.RawValue
- if _, err = asn1.Unmarshal(dp.DistributionPoint.FullName.Bytes, &n); err != nil {
+ if _, err := asn1.Unmarshal(dp.DistributionPoint.FullName.Bytes, &n); err != nil {
return nil, err
}
+ // Trailing data after the fullName is
+ // allowed because other elements of
+ // the SEQUENCE can appear.
if n.Tag == 6 {
out.CRLDistributionPoints = append(out.CRLDistributionPoints, string(n.Bytes))
@@ -1001,8 +1054,10 @@ func parseCertificate(in *certificate) (*Certificate, error) {
case 35:
// RFC 5280, 4.2.1.1
var a authKeyId
- if _, err = asn1.Unmarshal(e.Value, &a); err != nil {
+ if rest, err := asn1.Unmarshal(e.Value, &a); err != nil {
return nil, err
+ } else if len(rest) != 0 {
+ return nil, errors.New("x509: trailing data after X.509 authority key-id")
}
out.AuthorityKeyId = a.Id
@@ -1016,8 +1071,10 @@ func parseCertificate(in *certificate) (*Certificate, error) {
// KeyPurposeId ::= OBJECT IDENTIFIER
var keyUsage []asn1.ObjectIdentifier
- if _, err = asn1.Unmarshal(e.Value, &keyUsage); err != nil {
+ if rest, err := asn1.Unmarshal(e.Value, &keyUsage); err != nil {
return nil, err
+ } else if len(rest) != 0 {
+ return nil, errors.New("x509: trailing data after X.509 ExtendedKeyUsage")
}
for _, u := range keyUsage {
@@ -1031,16 +1088,20 @@ func parseCertificate(in *certificate) (*Certificate, error) {
case 14:
// RFC 5280, 4.2.1.2
var keyid []byte
- if _, err = asn1.Unmarshal(e.Value, &keyid); err != nil {
+ if rest, err := asn1.Unmarshal(e.Value, &keyid); err != nil {
return nil, err
+ } else if len(rest) != 0 {
+ return nil, errors.New("x509: trailing data after X.509 authority key-id")
}
out.SubjectKeyId = keyid
case 32:
// RFC 5280 4.2.1.4: Certificate Policies
var policies []policyInformation
- if _, err = asn1.Unmarshal(e.Value, &policies); err != nil {
+ if rest, err := asn1.Unmarshal(e.Value, &policies); err != nil {
return nil, err
+ } else if len(rest) != 0 {
+ return nil, errors.New("x509: trailing data after X.509 certificate policies")
}
out.PolicyIdentifiers = make([]asn1.ObjectIdentifier, len(policies))
for i, policy := range policies {
@@ -1048,14 +1109,16 @@ func parseCertificate(in *certificate) (*Certificate, error) {
}
default:
- // Unknown extensions cause an error if marked as critical.
- failIfCritical = true
+ // Unknown extensions are recorded if critical.
+ unhandled = true
}
} else if e.Id.Equal(oidExtensionAuthorityInfoAccess) {
// RFC 5280 4.2.2.1: Authority Information Access
var aia []authorityInfoAccess
- if _, err = asn1.Unmarshal(e.Value, &aia); err != nil {
+ if rest, err := asn1.Unmarshal(e.Value, &aia); err != nil {
return nil, err
+ } else if len(rest) != 0 {
+ return nil, errors.New("x509: trailing data after X.509 authority information")
}
for _, v := range aia {
@@ -1070,12 +1133,12 @@ func parseCertificate(in *certificate) (*Certificate, error) {
}
}
} else {
- // Unknown extensions cause an error if marked as critical.
- failIfCritical = true
+ // Unknown extensions are recorded if critical.
+ unhandled = true
}
- if e.Critical && failIfCritical {
- return out, UnhandledCriticalExtension{}
+ if e.Critical && unhandled {
+ out.UnhandledCriticalExtensions = append(out.UnhandledCriticalExtensions, e.Id)
}
}
@@ -1562,11 +1625,12 @@ func ParseCRL(crlBytes []byte) (certList *pkix.CertificateList, err error) {
// ParseDERCRL parses a DER encoded CRL from the given bytes.
func ParseDERCRL(derBytes []byte) (certList *pkix.CertificateList, err error) {
certList = new(pkix.CertificateList)
- _, err = asn1.Unmarshal(derBytes, certList)
- if err != nil {
- certList = nil
+ if rest, err := asn1.Unmarshal(derBytes, certList); err != nil {
+ return nil, err
+ } else if len(rest) != 0 {
+ return nil, errors.New("x509: trailing data after CRL")
}
- return
+ return certList, nil
}
// CreateCRL returns a DER encoded CRL, signed by this Certificate, that
@@ -1669,11 +1733,11 @@ type CertificateRequest struct {
// signature requests (see RFC 2986):
type tbsCertificateRequest struct {
- Raw asn1.RawContent
- Version int
- Subject asn1.RawValue
- PublicKey publicKeyInfo
- Attributes []pkix.AttributeTypeAndValueSET `asn1:"tag:0"`
+ Raw asn1.RawContent
+ Version int
+ Subject asn1.RawValue
+ PublicKey publicKeyInfo
+ RawAttributes []asn1.RawValue `asn1:"tag:0"`
}
type certificateRequest struct {
@@ -1687,6 +1751,36 @@ type certificateRequest struct {
// extensions in a CSR.
var oidExtensionRequest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 14}
+// newRawAttributes converts AttributeTypeAndValueSETs from a template
+// CertificateRequest's Attributes into tbsCertificateRequest RawAttributes.
+func newRawAttributes(attributes []pkix.AttributeTypeAndValueSET) ([]asn1.RawValue, error) {
+ var rawAttributes []asn1.RawValue
+ b, err := asn1.Marshal(attributes)
+ rest, err := asn1.Unmarshal(b, &rawAttributes)
+ if err != nil {
+ return nil, err
+ }
+ if len(rest) != 0 {
+ return nil, errors.New("x509: failed to unmarshall raw CSR Attributes")
+ }
+ return rawAttributes, nil
+}
+
+// parseRawAttributes Unmarshals RawAttributes intos AttributeTypeAndValueSETs.
+func parseRawAttributes(rawAttributes []asn1.RawValue) []pkix.AttributeTypeAndValueSET {
+ var attributes []pkix.AttributeTypeAndValueSET
+ for _, rawAttr := range rawAttributes {
+ var attr pkix.AttributeTypeAndValueSET
+ rest, err := asn1.Unmarshal(rawAttr.FullBytes, &attr)
+ // Ignore attributes that don't parse into pkix.AttributeTypeAndValueSET
+ // (i.e.: challengePassword or unstructuredName).
+ if err == nil && len(rest) == 0 {
+ attributes = append(attributes, attr)
+ }
+ }
+ return attributes
+}
+
// CreateCertificateRequest creates a new certificate based on a template. The
// following members of template are used: Subject, Attributes,
// SignatureAlgorithm, Extensions, DNSNames, EmailAddresses, and IPAddresses.
@@ -1799,6 +1893,11 @@ func CreateCertificateRequest(rand io.Reader, template *CertificateRequest, priv
}
}
+ rawAttributes, err := newRawAttributes(attributes)
+ if err != nil {
+ return
+ }
+
tbsCSR := tbsCertificateRequest{
Version: 0, // PKCS #10, RFC 2986
Subject: asn1.RawValue{FullBytes: asn1Subject},
@@ -1809,7 +1908,7 @@ func CreateCertificateRequest(rand io.Reader, template *CertificateRequest, priv
BitLength: len(publicKeyBytes) * 8,
},
},
- Attributes: attributes,
+ RawAttributes: rawAttributes,
}
tbsCSRContents, err := asn1.Marshal(tbsCSR)
@@ -1866,7 +1965,7 @@ func parseCertificateRequest(in *certificateRequest) (*CertificateRequest, error
PublicKeyAlgorithm: getPublicKeyAlgorithmFromOID(in.TBSCSR.PublicKey.Algorithm.Algorithm),
Version: in.TBSCSR.Version,
- Attributes: in.TBSCSR.Attributes,
+ Attributes: parseRawAttributes(in.TBSCSR.RawAttributes),
}
var err error
@@ -1876,15 +1975,17 @@ func parseCertificateRequest(in *certificateRequest) (*CertificateRequest, error
}
var subject pkix.RDNSequence
- if _, err := asn1.Unmarshal(in.TBSCSR.Subject.FullBytes, &subject); err != nil {
+ if rest, err := asn1.Unmarshal(in.TBSCSR.Subject.FullBytes, &subject); err != nil {
return nil, err
+ } else if len(rest) != 0 {
+ return nil, errors.New("x509: trailing data after X.509 Subject")
}
out.Subject.FillFromRDNSequence(&subject)
var extensions []pkix.AttributeTypeAndValue
- for _, atvSet := range in.TBSCSR.Attributes {
+ for _, atvSet := range out.Attributes {
if !atvSet.Type.Equal(oidExtensionRequest) {
continue
}
@@ -1920,3 +2021,8 @@ func parseCertificateRequest(in *certificateRequest) (*CertificateRequest, error
return out, nil
}
+
+// CheckSignature verifies that the signature on c is a valid signature
+func (c *CertificateRequest) CheckSignature() (err error) {
+ return checkSignature(c.SignatureAlgorithm, c.RawTBSCertificateRequest, c.Signature, c.PublicKey)
+}
diff --git a/src/crypto/x509/x509_test.go b/src/crypto/x509/x509_test.go
index b74cbaba1e..86a8b16cba 100644
--- a/src/crypto/x509/x509_test.go
+++ b/src/crypto/x509/x509_test.go
@@ -509,7 +509,13 @@ func TestUnknownCriticalExtension(t *testing.T) {
CommonName: "foo",
},
NotBefore: time.Unix(1000, 0),
- NotAfter: time.Unix(100000, 0),
+ NotAfter: time.Now().AddDate(1, 0, 0),
+
+ BasicConstraintsValid: true,
+ IsCA: true,
+
+ KeyUsage: KeyUsageCertSign,
+ ExtKeyUsage: []ExtKeyUsage{ExtKeyUsageServerAuth},
ExtraExtensions: []pkix.Extension{
{
@@ -525,13 +531,29 @@ func TestUnknownCriticalExtension(t *testing.T) {
t.Fatalf("failed to create certificate: %s", err)
}
- _, err = ParseCertificate(derBytes)
+ cert, err := ParseCertificate(derBytes)
+ if err != nil {
+ t.Fatalf("Certificate with unknown critical extension was not parsed: %s", err)
+ }
+
+ roots := NewCertPool()
+ roots.AddCert(cert)
+
+ // Setting Roots ensures that Verify won't delegate to the OS
+ // library and thus the correct error should always be
+ // returned.
+ _, err = cert.Verify(VerifyOptions{Roots: roots})
if err == nil {
- t.Fatalf("Certificate with critical extension was parsed without error.")
+ t.Fatal("Certificate with unknown critical extension was verified without error")
}
if _, ok := err.(UnhandledCriticalExtension); !ok {
t.Fatalf("Error was %#v, but wanted one of type UnhandledCriticalExtension", err)
}
+
+ cert.UnhandledCriticalExtensions = nil
+ if _, err = cert.Verify(VerifyOptions{Roots: roots}); err != nil {
+ t.Errorf("Certificate failed to verify after unhandled critical extensions were cleared: %s", err)
+ }
}
}
@@ -830,8 +852,9 @@ func TestImports(t *testing.T) {
case "android", "nacl":
t.Skipf("skipping on %s", runtime.GOOS)
case "darwin":
- if runtime.GOARCH == "arm" {
- t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
+ switch runtime.GOARCH {
+ case "arm", "arm64":
+ t.Skipf("skipping on %s/%s, cannot fork", runtime.GOOS, runtime.GOARCH)
}
}
@@ -903,6 +926,12 @@ func TestCreateCertificateRequest(t *testing.T) {
continue
}
+ err = out.CheckSignature()
+ if err != nil {
+ t.Errorf("%s: failed to check certificate request signature: %s", test.name, err)
+ continue
+ }
+
if out.Subject.CommonName != template.Subject.CommonName {
t.Errorf("%s: output subject common name and template subject common name don't match", test.name)
} else if len(out.Subject.Organization) != len(template.Subject.Organization) {
@@ -1014,33 +1043,35 @@ func TestCertificateRequestOverrides(t *testing.T) {
}
func TestParseCertificateRequest(t *testing.T) {
- csrBytes := fromBase64(csrBase64)
- csr, err := ParseCertificateRequest(csrBytes)
- if err != nil {
- t.Fatalf("failed to parse CSR: %s", err)
- }
-
- if len(csr.EmailAddresses) != 1 || csr.EmailAddresses[0] != "gopher@golang.org" {
- t.Errorf("incorrect email addresses found: %v", csr.EmailAddresses)
- }
-
- if len(csr.DNSNames) != 1 || csr.DNSNames[0] != "test.example.com" {
- t.Errorf("incorrect DNS names found: %v", csr.DNSNames)
- }
-
- if len(csr.Subject.Country) != 1 || csr.Subject.Country[0] != "AU" {
- t.Errorf("incorrect Subject name: %v", csr.Subject)
- }
-
- found := false
- for _, e := range csr.Extensions {
- if e.Id.Equal(oidExtensionBasicConstraints) {
- found = true
- break
+ for _, csrBase64 := range csrBase64Array {
+ csrBytes := fromBase64(csrBase64)
+ csr, err := ParseCertificateRequest(csrBytes)
+ if err != nil {
+ t.Fatalf("failed to parse CSR: %s", err)
+ }
+
+ if len(csr.EmailAddresses) != 1 || csr.EmailAddresses[0] != "gopher@golang.org" {
+ t.Errorf("incorrect email addresses found: %v", csr.EmailAddresses)
+ }
+
+ if len(csr.DNSNames) != 1 || csr.DNSNames[0] != "test.example.com" {
+ t.Errorf("incorrect DNS names found: %v", csr.DNSNames)
+ }
+
+ if len(csr.Subject.Country) != 1 || csr.Subject.Country[0] != "AU" {
+ t.Errorf("incorrect Subject name: %v", csr.Subject)
+ }
+
+ found := false
+ for _, e := range csr.Extensions {
+ if e.Id.Equal(oidExtensionBasicConstraints) {
+ found = true
+ break
+ }
+ }
+ if !found {
+ t.Errorf("basic constraints extension not found in CSR")
}
- }
- if !found {
- t.Errorf("basic constraints extension not found in CSR")
}
}
@@ -1129,12 +1160,20 @@ func TestASN1BitLength(t *testing.T) {
}
}
-// This CSR was generated with OpenSSL:
-// openssl req -out CSR.csr -new -newkey rsa:2048 -nodes -keyout privateKey.key -config openssl.cnf
+// These CSR was generated with OpenSSL:
+// openssl req -out CSR.csr -new -sha256 -nodes -keyout privateKey.key -config openssl.cnf
//
-// The openssl.cnf needs to include this section:
+// With openssl.cnf containing the following sections:
// [ v3_req ]
// basicConstraints = CA:FALSE
// keyUsage = nonRepudiation, digitalSignature, keyEncipherment
// subjectAltName = email:gopher@golang.org,DNS:test.example.com
-const csrBase64 = "MIIC4zCCAcsCAQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOY+MVedRg2JEnyeLcSzcsMv2VcsTfkB5+Etd6hihAh6MrGezNyASMMKuQN6YhCX1icQDiQtGsDLTtheNnSXK06tAhHjAP/hGlszRJp+5+rP2M58fDBAkUBEhskbCUWwpY14jFtVuGNJ8vF8h8IeczdolvQhX9lVai9G0EUXJMliMKdjA899H0mRs9PzHyidyrXFNiZlQXfD8Kg7gETn2Ny965iyI6ujAIYSCvam6TnxRHYH2MBKyVGvsYGbPYUQJCsgdgyajEg6ekihvQY3SzO1HSAlZAd7d1QYO4VeWJ2mY6Wu3Jpmh+AmG19S9CcHqGjd0bhuAX9cpPOKgnEmqn0CAwEAAaBZMFcGCSqGSIb3DQEJDjFKMEgwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwLgYDVR0RBCcwJYERZ29waGVyQGdvbGFuZy5vcmeCEHRlc3QuZXhhbXBsZS5jb20wDQYJKoZIhvcNAQEFBQADggEBAC9+QpKfdabxwCWwf4IEe1cKjdXLS1ScSuw27a3kZzQiPV78WJMa6dB8dqhdH5BRwGZ/qsgLrO6ZHlNeIv2Ib41Ccq71ecHW/nXc94A1BzJ/bVdI9LZcmTUvR1/m1jCpN7UqQ0ml1u9VihK7Pe762hEYxuWDQzYEU0l15S/bXmqeq3eF1A59XT/2jwe5+NV0Wwf4UQlkTXsAQMsJ+KzrQafd8Qv2A49o048uRvmjeJDrXLawGVianZ7D5A6Fpd1rZh6XcjqBpmgLw41DRQWENOdzhy+HyphKRv1MlY8OLkNqpGMhu8DdgJVGoT16DGiickoEa7Z3UCPVNgdTkT9jq7U="
+// [ req_attributes ]
+// challengePassword = ignored challenge
+// unstructuredName = ignored unstructured name
+var csrBase64Array = [...]string{
+ // Just [ v3_req ]
+ "MIIDHDCCAgQCAQAwfjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLQ29tbW9uIE5hbWUxITAfBgkqhkiG9w0BCQEWEnRlc3RAZW1haWwuYWRkcmVzczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK1GY4YFx2ujlZEOJxQVYmsjUnLsd5nFVnNpLE4cV+77sgv9NPNlB8uhn3MXt5leD34rm/2BisCHOifPucYlSrszo2beuKhvwn4+2FxDmWtBEMu/QA16L5IvoOfYZm/gJTsPwKDqvaR0tTU67a9OtxwNTBMI56YKtmwd/o8d3hYv9cg+9ZGAZ/gKONcg/OWYx/XRh6bd0g8DMbCikpWgXKDsvvK1Nk+VtkDO1JxuBaj4Lz/p/MifTfnHoqHxWOWl4EaTs4Ychxsv34/rSj1KD1tJqorIv5Xv2aqv4sjxfbrYzX4kvS5SC1goIovLnhj5UjmQ3Qy8u65eow/LLWw+YFcCAwEAAaBZMFcGCSqGSIb3DQEJDjFKMEgwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwLgYDVR0RBCcwJYERZ29waGVyQGdvbGFuZy5vcmeCEHRlc3QuZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQADggEBAB6VPMRrchvNW61Tokyq3ZvO6/NoGIbuwUn54q6l5VZW0Ep5Nq8juhegSSnaJ0jrovmUgKDN9vEo2KxuAtwG6udS6Ami3zP+hRd4k9Q8djJPb78nrjzWiindLK5Fps9U5mMoi1ER8ViveyAOTfnZt/jsKUaRsscY2FzE9t9/o5moE6LTcHUS4Ap1eheR+J72WOnQYn3cifYaemsA9MJuLko+kQ6xseqttbh9zjqd9fiCSh/LNkzos9c+mg2yMADitaZinAh+HZi50ooEbjaT3erNq9O6RqwJlgD00g6MQdoz9bTAryCUhCQfkIaepmQ7BxS0pqWNW3MMwfDwx/Snz6g=",
+ // Both [ v3_req ] and [ req_attributes ]
+ "MIIDaTCCAlECAQAwfjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLQ29tbW9uIE5hbWUxITAfBgkqhkiG9w0BCQEWEnRlc3RAZW1haWwuYWRkcmVzczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK1GY4YFx2ujlZEOJxQVYmsjUnLsd5nFVnNpLE4cV+77sgv9NPNlB8uhn3MXt5leD34rm/2BisCHOifPucYlSrszo2beuKhvwn4+2FxDmWtBEMu/QA16L5IvoOfYZm/gJTsPwKDqvaR0tTU67a9OtxwNTBMI56YKtmwd/o8d3hYv9cg+9ZGAZ/gKONcg/OWYx/XRh6bd0g8DMbCikpWgXKDsvvK1Nk+VtkDO1JxuBaj4Lz/p/MifTfnHoqHxWOWl4EaTs4Ychxsv34/rSj1KD1tJqorIv5Xv2aqv4sjxfbrYzX4kvS5SC1goIovLnhj5UjmQ3Qy8u65eow/LLWw+YFcCAwEAAaCBpTAgBgkqhkiG9w0BCQcxEwwRaWdub3JlZCBjaGFsbGVuZ2UwKAYJKoZIhvcNAQkCMRsMGWlnbm9yZWQgdW5zdHJ1Y3R1cmVkIG5hbWUwVwYJKoZIhvcNAQkOMUowSDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAuBgNVHREEJzAlgRFnb3BoZXJAZ29sYW5nLm9yZ4IQdGVzdC5leGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAgxe2N5O48EMsYE7o0rZBB0wi3Ov5/yYfnmmVI22Y3sP6VXbLDW0+UWIeSccOhzUCcZ/G4qcrfhhx6gTZTeA01nP7TdTJURvWAH5iFqj9sQ0qnLq6nEcVHij3sG6M5+BxAIVClQBk6lTCzgphc835Fjj6qSLuJ20XHdL5UfUbiJxx299CHgyBRL+hBUIPfz8p+ZgamyAuDLfnj54zzcRVyLlrmMLNPZNll1Q70RxoU6uWvLH8wB8vQe3Q/guSGubLyLRTUQVPh+dw1L4t8MKFWfX/48jwRM4gIRHFHPeAAE9D9YAoqdIvj/iFm/eQ++7DP8MDwOZWsXeB6jjwHuLmkQ==",
+}
diff --git a/src/database/sql/fakedb_test.go b/src/database/sql/fakedb_test.go
index a993fd46ed..8cbbb29a7c 100644
--- a/src/database/sql/fakedb_test.go
+++ b/src/database/sql/fakedb_test.go
@@ -89,7 +89,10 @@ type fakeConn struct {
stmtsMade int
stmtsClosed int
numPrepare int
- bad bool
+
+ // bad connection tests; see isBad()
+ bad bool
+ stickyBad bool
}
func (c *fakeConn) incrStat(v *int) {
@@ -243,13 +246,15 @@ func (db *fakeDB) columnType(table, column string) (typ string, ok bool) {
}
func (c *fakeConn) isBad() bool {
- // if not simulating bad conn, do nothing
- if !c.bad {
+ if c.stickyBad {
+ return true
+ } else if c.bad {
+ // alternate between bad conn and not bad conn
+ c.db.badConn = !c.db.badConn
+ return c.db.badConn
+ } else {
return false
}
- // alternate between bad conn and not bad conn
- c.db.badConn = !c.db.badConn
- return c.db.badConn
}
func (c *fakeConn) Begin() (driver.Tx, error) {
@@ -466,7 +471,7 @@ func (c *fakeConn) Prepare(query string) (driver.Stmt, error) {
panic("nil c.db; conn = " + fmt.Sprintf("%#v", c))
}
- if hookPrepareBadConn != nil && hookPrepareBadConn() {
+ if c.stickyBad || (hookPrepareBadConn != nil && hookPrepareBadConn()) {
return nil, driver.ErrBadConn
}
@@ -529,7 +534,7 @@ func (s *fakeStmt) Exec(args []driver.Value) (driver.Result, error) {
return nil, errClosed
}
- if hookExecBadConn != nil && hookExecBadConn() {
+ if s.c.stickyBad || (hookExecBadConn != nil && hookExecBadConn()) {
return nil, driver.ErrBadConn
}
@@ -613,7 +618,7 @@ func (s *fakeStmt) Query(args []driver.Value) (driver.Rows, error) {
return nil, errClosed
}
- if hookQueryBadConn != nil && hookQueryBadConn() {
+ if s.c.stickyBad || (hookQueryBadConn != nil && hookQueryBadConn()) {
return nil, driver.ErrBadConn
}
diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go
index b0e8894673..96c93ed1c6 100644
--- a/src/database/sql/sql.go
+++ b/src/database/sql/sql.go
@@ -235,6 +235,18 @@ type DB struct {
maxOpen int // <= 0 means unlimited
}
+// connReuseStrategy determines how (*DB).conn returns database connections.
+type connReuseStrategy uint8
+
+const (
+ // alwaysNewConn forces a new connection to the database.
+ alwaysNewConn connReuseStrategy = iota
+ // cachedOrNewConn returns a cached connection, if available, else waits
+ // for one to become available (if MaxOpenConns has been reached) or
+ // creates a new database connection.
+ cachedOrNewConn
+)
+
// driverConn wraps a driver.Conn with a mutex, to
// be held during all calls into the Conn. (including any calls onto
// interfaces returned via that Conn, such as calls on Tx, Stmt,
@@ -465,7 +477,7 @@ func (db *DB) Ping() error {
// TODO(bradfitz): give drivers an optional hook to implement
// this in a more efficient or more reliable way, if they
// have one.
- dc, err := db.conn()
+ dc, err := db.conn(cachedOrNewConn)
if err != nil {
return err
}
@@ -651,17 +663,28 @@ type connRequest struct {
var errDBClosed = errors.New("sql: database is closed")
-// conn returns a newly-opened or cached *driverConn
-func (db *DB) conn() (*driverConn, error) {
+// conn returns a newly-opened or cached *driverConn.
+func (db *DB) conn(strategy connReuseStrategy) (*driverConn, error) {
db.mu.Lock()
if db.closed {
db.mu.Unlock()
return nil, errDBClosed
}
- // If db.maxOpen > 0 and the number of open connections is over the limit
- // and there are no free connection, make a request and wait.
- if db.maxOpen > 0 && db.numOpen >= db.maxOpen && len(db.freeConn) == 0 {
+ // Prefer a free connection, if possible.
+ numFree := len(db.freeConn)
+ if strategy == cachedOrNewConn && numFree > 0 {
+ conn := db.freeConn[0]
+ copy(db.freeConn, db.freeConn[1:])
+ db.freeConn = db.freeConn[:numFree-1]
+ conn.inUse = true
+ db.mu.Unlock()
+ return conn, nil
+ }
+
+ // Out of free connections or we were asked not to use one. If we're not
+ // allowed to open any more connections, make a request and wait.
+ if db.maxOpen > 0 && db.numOpen >= db.maxOpen {
// Make the connRequest channel. It's buffered so that the
// connectionOpener doesn't block while waiting for the req to be read.
req := make(chan connRequest, 1)
@@ -671,15 +694,6 @@ func (db *DB) conn() (*driverConn, error) {
return ret.conn, ret.err
}
- if c := len(db.freeConn); c > 0 {
- conn := db.freeConn[0]
- copy(db.freeConn, db.freeConn[1:])
- db.freeConn = db.freeConn[:c-1]
- conn.inUse = true
- db.mu.Unlock()
- return conn, nil
- }
-
db.numOpen++ // optimistically
db.mu.Unlock()
ci, err := db.driver.Open(db.dsn)
@@ -782,6 +796,9 @@ func (db *DB) putConn(dc *driverConn, err error) {
// If a connRequest was fulfilled or the *driverConn was placed in the
// freeConn list, then true is returned, otherwise false is returned.
func (db *DB) putConnDBLocked(dc *driverConn, err error) bool {
+ if db.maxOpen > 0 && db.numOpen > db.maxOpen {
+ return false
+ }
if c := len(db.connRequests); c > 0 {
req := db.connRequests[0]
// This copy is O(n) but in practice faster than a linked list.
@@ -805,8 +822,9 @@ func (db *DB) putConnDBLocked(dc *driverConn, err error) bool {
}
// maxBadConnRetries is the number of maximum retries if the driver returns
-// driver.ErrBadConn to signal a broken connection.
-const maxBadConnRetries = 10
+// driver.ErrBadConn to signal a broken connection before forcing a new
+// connection to be opened.
+const maxBadConnRetries = 2
// Prepare creates a prepared statement for later queries or executions.
// Multiple queries or executions may be run concurrently from the
@@ -815,22 +833,25 @@ func (db *DB) Prepare(query string) (*Stmt, error) {
var stmt *Stmt
var err error
for i := 0; i < maxBadConnRetries; i++ {
- stmt, err = db.prepare(query)
+ stmt, err = db.prepare(query, cachedOrNewConn)
if err != driver.ErrBadConn {
break
}
}
+ if err == driver.ErrBadConn {
+ return db.prepare(query, alwaysNewConn)
+ }
return stmt, err
}
-func (db *DB) prepare(query string) (*Stmt, error) {
+func (db *DB) prepare(query string, strategy connReuseStrategy) (*Stmt, error) {
// TODO: check if db.driver supports an optional
// driver.Preparer interface and call that instead, if so,
// otherwise we make a prepared statement that's bound
// to a connection, and to execute this prepared statement
// we either need to use this connection (if it's free), else
// get a new connection + re-prepare + execute on that one.
- dc, err := db.conn()
+ dc, err := db.conn(strategy)
if err != nil {
return nil, err
}
@@ -858,16 +879,19 @@ func (db *DB) Exec(query string, args ...interface{}) (Result, error) {
var res Result
var err error
for i := 0; i < maxBadConnRetries; i++ {
- res, err = db.exec(query, args)
+ res, err = db.exec(query, args, cachedOrNewConn)
if err != driver.ErrBadConn {
break
}
}
+ if err == driver.ErrBadConn {
+ return db.exec(query, args, alwaysNewConn)
+ }
return res, err
}
-func (db *DB) exec(query string, args []interface{}) (res Result, err error) {
- dc, err := db.conn()
+func (db *DB) exec(query string, args []interface{}, strategy connReuseStrategy) (res Result, err error) {
+ dc, err := db.conn(strategy)
if err != nil {
return nil, err
}
@@ -907,16 +931,19 @@ func (db *DB) Query(query string, args ...interface{}) (*Rows, error) {
var rows *Rows
var err error
for i := 0; i < maxBadConnRetries; i++ {
- rows, err = db.query(query, args)
+ rows, err = db.query(query, args, cachedOrNewConn)
if err != driver.ErrBadConn {
break
}
}
+ if err == driver.ErrBadConn {
+ return db.query(query, args, alwaysNewConn)
+ }
return rows, err
}
-func (db *DB) query(query string, args []interface{}) (*Rows, error) {
- ci, err := db.conn()
+func (db *DB) query(query string, args []interface{}, strategy connReuseStrategy) (*Rows, error) {
+ ci, err := db.conn(strategy)
if err != nil {
return nil, err
}
@@ -995,16 +1022,19 @@ func (db *DB) Begin() (*Tx, error) {
var tx *Tx
var err error
for i := 0; i < maxBadConnRetries; i++ {
- tx, err = db.begin()
+ tx, err = db.begin(cachedOrNewConn)
if err != driver.ErrBadConn {
break
}
}
+ if err == driver.ErrBadConn {
+ return db.begin(alwaysNewConn)
+ }
return tx, err
}
-func (db *DB) begin() (tx *Tx, err error) {
- dc, err := db.conn()
+func (db *DB) begin(strategy connReuseStrategy) (tx *Tx, err error) {
+ dc, err := db.conn(strategy)
if err != nil {
return nil, err
}
@@ -1393,7 +1423,7 @@ func (s *Stmt) connStmt() (ci *driverConn, releaseConn func(error), si driver.St
s.mu.Unlock()
// TODO(bradfitz): or always wait for one? make configurable later?
- dc, err := s.db.conn()
+ dc, err := s.db.conn(cachedOrNewConn)
if err != nil {
return nil, nil, nil, err
}
diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go
index e225ffe6fa..94f80a6223 100644
--- a/src/database/sql/sql_test.go
+++ b/src/database/sql/sql_test.go
@@ -1070,6 +1070,57 @@ func TestMaxOpenConns(t *testing.T) {
}
}
+// Issue 9453: tests that SetMaxOpenConns can be lowered at runtime
+// and affects the subsequent release of connections.
+func TestMaxOpenConnsOnBusy(t *testing.T) {
+ defer setHookpostCloseConn(nil)
+ setHookpostCloseConn(func(_ *fakeConn, err error) {
+ if err != nil {
+ t.Errorf("Error closing fakeConn: %v", err)
+ }
+ })
+
+ db := newTestDB(t, "magicquery")
+ defer closeDB(t, db)
+
+ db.SetMaxOpenConns(3)
+
+ conn0, err := db.conn(cachedOrNewConn)
+ if err != nil {
+ t.Fatalf("db open conn fail: %v", err)
+ }
+
+ conn1, err := db.conn(cachedOrNewConn)
+ if err != nil {
+ t.Fatalf("db open conn fail: %v", err)
+ }
+
+ conn2, err := db.conn(cachedOrNewConn)
+ if err != nil {
+ t.Fatalf("db open conn fail: %v", err)
+ }
+
+ if g, w := db.numOpen, 3; g != w {
+ t.Errorf("free conns = %d; want %d", g, w)
+ }
+
+ db.SetMaxOpenConns(2)
+ if g, w := db.numOpen, 3; g != w {
+ t.Errorf("free conns = %d; want %d", g, w)
+ }
+
+ conn0.releaseConn(nil)
+ conn1.releaseConn(nil)
+ if g, w := db.numOpen, 2; g != w {
+ t.Errorf("free conns = %d; want %d", g, w)
+ }
+
+ conn2.releaseConn(nil)
+ if g, w := db.numOpen, 2; g != w {
+ t.Errorf("free conns = %d; want %d", g, w)
+ }
+}
+
func TestSingleOpenConn(t *testing.T) {
db := newTestDB(t, "people")
defer closeDB(t, db)
@@ -1334,6 +1385,79 @@ func TestStmtCloseOrder(t *testing.T) {
}
}
+// Test cases where there's more than maxBadConnRetries bad connections in the
+// pool (issue 8834)
+func TestManyErrBadConn(t *testing.T) {
+ manyErrBadConnSetup := func() *DB {
+ db := newTestDB(t, "people")
+
+ nconn := maxBadConnRetries + 1
+ db.SetMaxIdleConns(nconn)
+ db.SetMaxOpenConns(nconn)
+ // open enough connections
+ func() {
+ for i := 0; i < nconn; i++ {
+ rows, err := db.Query("SELECT|people|age,name|")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer rows.Close()
+ }
+ }()
+
+ if db.numOpen != nconn {
+ t.Fatalf("unexpected numOpen %d (was expecting %d)", db.numOpen, nconn)
+ } else if len(db.freeConn) != nconn {
+ t.Fatalf("unexpected len(db.freeConn) %d (was expecting %d)", len(db.freeConn), nconn)
+ }
+ for _, conn := range db.freeConn {
+ conn.ci.(*fakeConn).stickyBad = true
+ }
+ return db
+ }
+
+ // Query
+ db := manyErrBadConnSetup()
+ defer closeDB(t, db)
+ rows, err := db.Query("SELECT|people|age,name|")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err = rows.Close(); err != nil {
+ t.Fatal(err)
+ }
+
+ // Exec
+ db = manyErrBadConnSetup()
+ defer closeDB(t, db)
+ _, err = db.Exec("INSERT|people|name=Julia,age=19")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Begin
+ db = manyErrBadConnSetup()
+ defer closeDB(t, db)
+ tx, err := db.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err = tx.Rollback(); err != nil {
+ t.Fatal(err)
+ }
+
+ // Prepare
+ db = manyErrBadConnSetup()
+ defer closeDB(t, db)
+ stmt, err := db.Prepare("SELECT|people|age,name|")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err = stmt.Close(); err != nil {
+ t.Fatal(err)
+ }
+}
+
// golang.org/issue/5781
func TestErrBadConnReconnect(t *testing.T) {
db := newTestDB(t, "foo")
diff --git a/src/debug/dwarf/class_string.go b/src/debug/dwarf/class_string.go
new file mode 100644
index 0000000000..0b1206b9f3
--- /dev/null
+++ b/src/debug/dwarf/class_string.go
@@ -0,0 +1,17 @@
+// generated by stringer -type=Class; DO NOT EDIT
+
+package dwarf
+
+import "fmt"
+
+const _Class_name = "ClassAddressClassBlockClassConstantClassExprLocClassFlagClassLinePtrClassLocListPtrClassMacPtrClassRangeListPtrClassReferenceClassReferenceSigClassStringClassReferenceAltClassStringAlt"
+
+var _Class_index = [...]uint8{0, 12, 22, 35, 47, 56, 68, 83, 94, 111, 125, 142, 153, 170, 184}
+
+func (i Class) String() string {
+ i -= 1
+ if i < 0 || i+1 >= Class(len(_Class_index)) {
+ return fmt.Sprintf("Class(%d)", i+1)
+ }
+ return _Class_name[_Class_index[i]:_Class_index[i+1]]
+}
diff --git a/src/debug/dwarf/entry.go b/src/debug/dwarf/entry.go
index 2742ae0eab..a94be32a21 100644
--- a/src/debug/dwarf/entry.go
+++ b/src/debug/dwarf/entry.go
@@ -23,8 +23,9 @@ type abbrev struct {
}
type afield struct {
- attr Attr
- fmt format
+ attr Attr
+ fmt format
+ class Class
}
// a map from entry format ids to their descriptions
@@ -32,7 +33,7 @@ type abbrevTable map[uint32]abbrev
// ParseAbbrev returns the abbreviation table that starts at byte off
// in the .debug_abbrev section.
-func (d *Data) parseAbbrev(off uint32) (abbrevTable, error) {
+func (d *Data) parseAbbrev(off uint32, vers int) (abbrevTable, error) {
if m, ok := d.abbrevCache[off]; ok {
return m, nil
}
@@ -80,6 +81,7 @@ func (d *Data) parseAbbrev(off uint32) (abbrevTable, error) {
for i := range a.field {
a.field[i].attr = Attr(b.uint())
a.field[i].fmt = format(b.uint())
+ a.field[i].class = formToClass(a.field[i].fmt, a.field[i].attr, vers, &b)
}
b.uint()
b.uint()
@@ -93,6 +95,118 @@ func (d *Data) parseAbbrev(off uint32) (abbrevTable, error) {
return m, nil
}
+// attrIsExprloc indicates attributes that allow exprloc values that
+// are encoded as block values in DWARF 2 and 3. See DWARF 4, Figure
+// 20.
+var attrIsExprloc = map[Attr]bool{
+ AttrLocation: true,
+ AttrByteSize: true,
+ AttrBitOffset: true,
+ AttrBitSize: true,
+ AttrStringLength: true,
+ AttrLowerBound: true,
+ AttrReturnAddr: true,
+ AttrStrideSize: true,
+ AttrUpperBound: true,
+ AttrCount: true,
+ AttrDataMemberLoc: true,
+ AttrFrameBase: true,
+ AttrSegment: true,
+ AttrStaticLink: true,
+ AttrUseLocation: true,
+ AttrVtableElemLoc: true,
+ AttrAllocated: true,
+ AttrAssociated: true,
+ AttrDataLocation: true,
+ AttrStride: true,
+}
+
+// attrPtrClass indicates the *ptr class of attributes that have
+// encoding formSecOffset in DWARF 4 or formData* in DWARF 2 and 3.
+var attrPtrClass = map[Attr]Class{
+ AttrLocation: ClassLocListPtr,
+ AttrStmtList: ClassLinePtr,
+ AttrStringLength: ClassLocListPtr,
+ AttrReturnAddr: ClassLocListPtr,
+ AttrStartScope: ClassRangeListPtr,
+ AttrDataMemberLoc: ClassLocListPtr,
+ AttrFrameBase: ClassLocListPtr,
+ AttrMacroInfo: ClassMacPtr,
+ AttrSegment: ClassLocListPtr,
+ AttrStaticLink: ClassLocListPtr,
+ AttrUseLocation: ClassLocListPtr,
+ AttrVtableElemLoc: ClassLocListPtr,
+ AttrRanges: ClassRangeListPtr,
+}
+
+// formToClass returns the DWARF 4 Class for the given form. If the
+// DWARF version is less then 4, it will disambiguate some forms
+// depending on the attribute.
+func formToClass(form format, attr Attr, vers int, b *buf) Class {
+ switch form {
+ default:
+ b.error("cannot determine class of unknown attribute form")
+ return 0
+
+ case formAddr:
+ return ClassAddress
+
+ case formDwarfBlock1, formDwarfBlock2, formDwarfBlock4, formDwarfBlock:
+ // In DWARF 2 and 3, ClassExprLoc was encoded as a
+ // block. DWARF 4 distinguishes ClassBlock and
+ // ClassExprLoc, but there are no attributes that can
+ // be both, so we also promote ClassBlock values in
+ // DWARF 4 that should be ClassExprLoc in case
+ // producers get this wrong.
+ if attrIsExprloc[attr] {
+ return ClassExprLoc
+ }
+ return ClassBlock
+
+ case formData1, formData2, formData4, formData8, formSdata, formUdata:
+ // In DWARF 2 and 3, ClassPtr was encoded as a
+ // constant. Unlike ClassExprLoc/ClassBlock, some
+ // DWARF 4 attributes need to distinguish Class*Ptr
+ // from ClassConstant, so we only do this promotion
+ // for versions 2 and 3.
+ if class, ok := attrPtrClass[attr]; vers < 4 && ok {
+ return class
+ }
+ return ClassConstant
+
+ case formFlag, formFlagPresent:
+ return ClassFlag
+
+ case formRefAddr, formRef1, formRef2, formRef4, formRef8, formRefUdata:
+ return ClassReference
+
+ case formRefSig8:
+ return ClassReferenceSig
+
+ case formString, formStrp:
+ return ClassString
+
+ case formSecOffset:
+ // DWARF 4 defines four *ptr classes, but doesn't
+ // distinguish them in the encoding. Disambiguate
+ // these classes using the attribute.
+ if class, ok := attrPtrClass[attr]; ok {
+ return class
+ }
+ b.error("cannot determine class of unknown attribute with formSecOffset")
+ return 0
+
+ case formExprloc:
+ return ClassExprLoc
+
+ case formGnuRefAlt:
+ return ClassReferenceAlt
+
+ case formGnuStrpAlt:
+ return ClassStringAlt
+ }
+}
+
// An entry is a sequence of attribute/value pairs.
type Entry struct {
Offset Offset // offset of Entry in DWARF info
@@ -102,9 +216,115 @@ type Entry struct {
}
// A Field is a single attribute/value pair in an Entry.
+//
+// A value can be one of several "attribute classes" defined by DWARF.
+// The Go types corresponding to each class are:
+//
+// DWARF class Go type Class
+// ----------- ------- -----
+// address uint64 ClassAddress
+// block []byte ClassBlock
+// constant int64 ClassConstant
+// flag bool ClassFlag
+// reference
+// to info dwarf.Offset ClassReference
+// to type unit uint64 ClassReferenceSig
+// string string ClassString
+// exprloc []byte ClassExprLoc
+// lineptr int64 ClassLinePtr
+// loclistptr int64 ClassLocListPtr
+// macptr int64 ClassMacPtr
+// rangelistptr int64 ClassRangeListPtr
type Field struct {
- Attr Attr
- Val interface{}
+ Attr Attr
+ Val interface{}
+ Class Class
+}
+
+// A Class is the DWARF 4 class of an attibute value.
+//
+// In general, a given attribute's value may take on one of several
+// possible classes defined by DWARF, each of which leads to a
+// slightly different interpretation of the attribute.
+//
+// DWARF version 4 distinguishes attribute value classes more finely
+// than previous versions of DWARF. The reader will disambiguate
+// coarser classes from earlier versions of DWARF into the appropriate
+// DWARF 4 class. For example, DWARF 2 uses "constant" for constants
+// as well as all types of section offsets, but the reader will
+// canonicalize attributes in DWARF 2 files that refer to section
+// offsets to one of the Class*Ptr classes, even though these classes
+// were only defined in DWARF 3.
+type Class int
+
+const (
+ // ClassAddress represents values of type uint64 that are
+ // addresses on the target machine.
+ ClassAddress Class = 1 + iota
+
+ // ClassBlock represents values of type []byte whose
+ // interpretation depends on the attribute.
+ ClassBlock
+
+ // ClassConstant represents values of type int64 that are
+ // constants. The interpretation of this constant depends on
+ // the attribute.
+ ClassConstant
+
+ // ClassExprLoc represents values of type []byte that contain
+ // an encoded DWARF expression or location description.
+ ClassExprLoc
+
+ // ClassFlag represents values of type bool.
+ ClassFlag
+
+ // ClassLinePtr represents values that are an int64 offset
+ // into the "line" section.
+ ClassLinePtr
+
+ // ClassLocListPtr repersents values that are an int64 offset
+ // into the "loclist" section.
+ ClassLocListPtr
+
+ // ClassMacPtr represents values that are an int64 offset into
+ // the "mac" section.
+ ClassMacPtr
+
+ // ClassMacPtr represents values that are an int64 offset into
+ // the "rangelist" section.
+ ClassRangeListPtr
+
+ // ClassReference represents values that are an Offset offset
+ // of an Entry in the info section (for use with Reader.Seek).
+ // The DWARF specification combines ClassReference and
+ // ClassReferenceSig into class "reference".
+ ClassReference
+
+ // ClassReferenceSig represents values that are a uint64 type
+ // signature referencing a type Entry.
+ ClassReferenceSig
+
+ // ClassString represents values that are strings. If the
+ // compilation unit specifies the AttrUseUTF8 flag (strongly
+ // recommended), the string value will be encoded in UTF-8.
+ // Otherwise, the encoding is unspecified.
+ ClassString
+
+ // ClassReferenceAlt represents values of type int64 that are
+ // an offset into the DWARF "info" section of an alternate
+ // object file.
+ ClassReferenceAlt
+
+ // ClassStringAlt represents values of type int64 that are an
+ // offset into the DWARF string section of an alternate object
+ // file.
+ ClassStringAlt
+)
+
+//go:generate stringer -type=Class
+
+func (i Class) GoString() string {
+ return "dwarf." + i.String()
}
// Val returns the value associated with attribute Attr in Entry,
@@ -115,9 +335,18 @@ type Field struct {
// v, ok := e.Val(AttrSibling).(int64)
//
func (e *Entry) Val(a Attr) interface{} {
- for _, f := range e.Field {
+ if f := e.AttrField(a); f != nil {
+ return f.Val
+ }
+ return nil
+}
+
+// AttrField returns the Field associated with attribute Attr in
+// Entry, or nil if there is no such attribute.
+func (e *Entry) AttrField(a Attr) *Field {
+ for i, f := range e.Field {
if f.Attr == a {
- return f.Val
+ return &e.Field[i]
}
}
return nil
@@ -148,6 +377,7 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry {
}
for i := range e.Field {
e.Field[i].Attr = a.field[i].attr
+ e.Field[i].Class = a.field[i].class
fmt := a.field[i].fmt
if fmt == formIndirect {
fmt = format(b.uint())
@@ -292,6 +522,12 @@ func (d *Data) Reader() *Reader {
return r
}
+// AddressSize returns the size in bytes of addresses in the current compilation
+// unit.
+func (r *Reader) AddressSize() int {
+ return r.d.unit[r.unit].asize
+}
+
// Seek positions the Reader at offset off in the encoded entry stream.
// Offset 0 can be used to denote the first entry.
func (r *Reader) Seek(off Offset) {
@@ -311,6 +547,7 @@ func (r *Reader) Seek(off Offset) {
i := d.offsetToUnit(off)
if i == -1 {
r.err = errors.New("offset out of range")
+ return
}
u := &d.unit[i]
r.unit = i
diff --git a/src/debug/dwarf/type.go b/src/debug/dwarf/type.go
index 6986b19e72..a5daa1d0bb 100644
--- a/src/debug/dwarf/type.go
+++ b/src/debug/dwarf/type.go
@@ -268,6 +268,9 @@ type typeReader interface {
Next() (*Entry, error)
clone() typeReader
offset() Offset
+ // AddressSize returns the size in bytes of addresses in the current
+ // compilation unit.
+ AddressSize() int
}
// Type reads the type at off in the DWARF ``info'' section.
@@ -286,6 +289,7 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
if err != nil {
return nil, err
}
+ addressSize := r.AddressSize()
if e == nil || e.Offset != off {
return nil, DecodeError{name, off, "no type at offset"}
}
@@ -668,6 +672,12 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
b, ok := e.Val(AttrByteSize).(int64)
if !ok {
b = -1
+ switch t := typ.(type) {
+ case *TypedefType:
+ b = t.Type.Size()
+ case *PtrType:
+ b = int64(addressSize)
+ }
}
typ.Common().ByteSize = b
}
diff --git a/src/debug/dwarf/typeunit.go b/src/debug/dwarf/typeunit.go
index 80971bbb90..9cfb4a8b25 100644
--- a/src/debug/dwarf/typeunit.go
+++ b/src/debug/dwarf/typeunit.go
@@ -33,9 +33,9 @@ func (d *Data) parseTypes(name string, types []byte) error {
return b.err
}
hdroff := b.off
- vers := b.uint16()
+ vers := int(b.uint16())
if vers != 4 {
- b.error("unsupported DWARF version " + strconv.Itoa(int(vers)))
+ b.error("unsupported DWARF version " + strconv.Itoa(vers))
return b.err
}
var ao uint32
@@ -49,7 +49,7 @@ func (d *Data) parseTypes(name string, types []byte) error {
}
ao = uint32(ao64)
}
- atable, err := d.parseAbbrev(ao)
+ atable, err := d.parseAbbrev(ao, vers)
if err != nil {
return err
}
@@ -129,6 +129,11 @@ func (tur *typeUnitReader) Seek(off Offset) {
tur.b = makeBuf(tur.d, tur.tu, tur.tu.name, off, tur.tu.data[doff:])
}
+// AddressSize returns the size in bytes of addresses in the current type unit.
+func (tur *typeUnitReader) AddressSize() int {
+ return tur.tu.unit.asize
+}
+
// Next reads the next Entry from the type unit.
func (tur *typeUnitReader) Next() (*Entry, error) {
if tur.err != nil {
diff --git a/src/debug/dwarf/unit.go b/src/debug/dwarf/unit.go
index 901ba0dabf..ceb6cdbff3 100644
--- a/src/debug/dwarf/unit.go
+++ b/src/debug/dwarf/unit.go
@@ -67,7 +67,7 @@ func (d *Data) parseUnits() ([]unit, error) {
break
}
u.vers = int(vers)
- atable, err := d.parseAbbrev(b.uint32())
+ atable, err := d.parseAbbrev(b.uint32(), u.vers)
if err != nil {
if b.err == nil {
b.err = err
diff --git a/src/debug/elf/file_test.go b/src/debug/elf/file_test.go
index 0d10ec51b1..48fe9d26e1 100644
--- a/src/debug/elf/file_test.go
+++ b/src/debug/elf/file_test.go
@@ -245,56 +245,56 @@ var relocationTests = []relocationTest{
{
"testdata/go-relocation-test-gcc441-x86-64.obj",
[]relocationTestEntry{
- {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}},
+ {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1", Class: dwarf.ClassString}, {Attr: dwarf.AttrLanguage, Val: int64(1), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrName, Val: "go-relocation-test.c", Class: dwarf.ClassString}, {Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6), Class: dwarf.ClassAddress}, {Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr}}}},
},
},
{
"testdata/go-relocation-test-gcc441-x86.obj",
[]relocationTestEntry{
- {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "t.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x5)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}},
+ {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1", Class: dwarf.ClassString}, {Attr: dwarf.AttrLanguage, Val: int64(1), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrName, Val: "t.c", Class: dwarf.ClassString}, {Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress}, {Attr: dwarf.AttrHighpc, Val: uint64(0x5), Class: dwarf.ClassAddress}, {Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr}}}},
},
},
{
"testdata/go-relocation-test-gcc424-x86-64.obj",
[]relocationTestEntry{
- {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.2.4 (Ubuntu 4.2.4-1ubuntu4)"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc424.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}},
+ {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.2.4 (Ubuntu 4.2.4-1ubuntu4)", Class: dwarf.ClassString}, {Attr: dwarf.AttrLanguage, Val: int64(1), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc424.c", Class: dwarf.ClassString}, {Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6), Class: dwarf.ClassAddress}, {Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr}}}},
},
},
{
"testdata/go-relocation-test-gcc482-aarch64.obj",
[]relocationTestEntry{
- {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.8.2 -g -fstack-protector"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc482.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: int64(0x24)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}},
+ {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.8.2 -g -fstack-protector", Class: dwarf.ClassString}, {Attr: dwarf.AttrLanguage, Val: int64(1), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc482.c", Class: dwarf.ClassString}, {Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress}, {Attr: dwarf.AttrHighpc, Val: int64(0x24), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr}}}},
},
},
{
"testdata/go-relocation-test-gcc492-arm.obj",
[]relocationTestEntry{
- {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.9.2 20141224 (prerelease) -march=armv7-a -mfloat-abi=hard -mfpu=vfpv3-d16 -mtls-dialect=gnu -g"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc492.c"}, {Attr: dwarf.AttrCompDir, Val: "/root/go/src/debug/elf/testdata"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: int64(0x28)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}},
+ {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.9.2 20141224 (prerelease) -march=armv7-a -mfloat-abi=hard -mfpu=vfpv3-d16 -mtls-dialect=gnu -g", Class: dwarf.ClassString}, {Attr: dwarf.AttrLanguage, Val: int64(1), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc492.c", Class: dwarf.ClassString}, {Attr: dwarf.AttrCompDir, Val: "/root/go/src/debug/elf/testdata", Class: dwarf.ClassString}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress}, {Attr: dwarf.AttrHighpc, Val: int64(0x28), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr}}}},
},
},
{
"testdata/go-relocation-test-gcc5-ppc.obj",
[]relocationTestEntry{
- {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{dwarf.Field{Attr: dwarf.AttrProducer, Val: "GNU C11 5.0.0 20150116 (experimental) -Asystem=linux -Asystem=unix -Asystem=posix -g"}, dwarf.Field{Attr: dwarf.AttrLanguage, Val: int64(12)}, dwarf.Field{Attr: dwarf.AttrName, Val: "go-relocation-test-gcc5-ppc.c"}, dwarf.Field{Attr: dwarf.AttrCompDir, Val: "/tmp"}, dwarf.Field{Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, dwarf.Field{Attr: dwarf.AttrHighpc, Val: int64(0x44)}, dwarf.Field{Attr: dwarf.AttrStmtList, Val: int64(0)}}}},
+ {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{dwarf.Field{Attr: dwarf.AttrProducer, Val: "GNU C11 5.0.0 20150116 (experimental) -Asystem=linux -Asystem=unix -Asystem=posix -g", Class: dwarf.ClassString}, dwarf.Field{Attr: dwarf.AttrLanguage, Val: int64(12), Class: dwarf.ClassConstant}, dwarf.Field{Attr: dwarf.AttrName, Val: "go-relocation-test-gcc5-ppc.c", Class: dwarf.ClassString}, dwarf.Field{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString}, dwarf.Field{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress}, dwarf.Field{Attr: dwarf.AttrHighpc, Val: int64(0x44), Class: dwarf.ClassConstant}, dwarf.Field{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr}}}},
},
},
{
"testdata/go-relocation-test-gcc482-ppc64le.obj",
[]relocationTestEntry{
- {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{dwarf.Field{Attr: dwarf.AttrProducer, Val: "GNU C 4.8.2 -Asystem=linux -Asystem=unix -Asystem=posix -msecure-plt -mtune=power8 -mcpu=power7 -gdwarf-2 -fstack-protector"}, dwarf.Field{Attr: dwarf.AttrLanguage, Val: int64(1)}, dwarf.Field{Attr: dwarf.AttrName, Val: "go-relocation-test-gcc482-ppc64le.c"}, dwarf.Field{Attr: dwarf.AttrCompDir, Val: "/tmp"}, dwarf.Field{Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, dwarf.Field{Attr: dwarf.AttrHighpc, Val: uint64(0x24)}, dwarf.Field{Attr: dwarf.AttrStmtList, Val: int64(0)}}}},
+ {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{dwarf.Field{Attr: dwarf.AttrProducer, Val: "GNU C 4.8.2 -Asystem=linux -Asystem=unix -Asystem=posix -msecure-plt -mtune=power8 -mcpu=power7 -gdwarf-2 -fstack-protector", Class: dwarf.ClassString}, dwarf.Field{Attr: dwarf.AttrLanguage, Val: int64(1), Class: dwarf.ClassConstant}, dwarf.Field{Attr: dwarf.AttrName, Val: "go-relocation-test-gcc482-ppc64le.c", Class: dwarf.ClassString}, dwarf.Field{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString}, dwarf.Field{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress}, dwarf.Field{Attr: dwarf.AttrHighpc, Val: uint64(0x24), Class: dwarf.ClassAddress}, dwarf.Field{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr}}}},
},
},
{
"testdata/go-relocation-test-clang-x86.obj",
[]relocationTestEntry{
- {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "clang version google3-trunk (trunk r209387)"}, {Attr: dwarf.AttrLanguage, Val: int64(12)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-clang.c"}, {Attr: dwarf.AttrStmtList, Val: int64(0)}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}}}},
+ {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "clang version google3-trunk (trunk r209387)", Class: dwarf.ClassString}, {Attr: dwarf.AttrLanguage, Val: int64(12), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrName, Val: "go-relocation-test-clang.c", Class: dwarf.ClassString}, {Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr}, {Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString}}}},
},
},
{
"testdata/gcc-amd64-openbsd-debug-with-rela.obj",
[]relocationTestEntry{
- {203, &dwarf.Entry{Offset: 0xc62, Tag: dwarf.TagMember, Children: false, Field: []dwarf.Field{{Attr: dwarf.AttrName, Val: "it_interval"}, {Attr: dwarf.AttrDeclFile, Val: int64(7)}, {Attr: dwarf.AttrDeclLine, Val: int64(236)}, {Attr: dwarf.AttrType, Val: dwarf.Offset(0xb7f)}, {Attr: dwarf.AttrDataMemberLoc, Val: []byte{0x23, 0x0}}}}},
- {204, &dwarf.Entry{Offset: 0xc70, Tag: dwarf.TagMember, Children: false, Field: []dwarf.Field{{Attr: dwarf.AttrName, Val: "it_value"}, {Attr: dwarf.AttrDeclFile, Val: int64(7)}, {Attr: dwarf.AttrDeclLine, Val: int64(237)}, {Attr: dwarf.AttrType, Val: dwarf.Offset(0xb7f)}, {Attr: dwarf.AttrDataMemberLoc, Val: []byte{0x23, 0x10}}}}},
+ {203, &dwarf.Entry{Offset: 0xc62, Tag: dwarf.TagMember, Children: false, Field: []dwarf.Field{{Attr: dwarf.AttrName, Val: "it_interval", Class: dwarf.ClassString}, {Attr: dwarf.AttrDeclFile, Val: int64(7), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrDeclLine, Val: int64(236), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrType, Val: dwarf.Offset(0xb7f), Class: dwarf.ClassReference}, {Attr: dwarf.AttrDataMemberLoc, Val: []byte{0x23, 0x0}, Class: dwarf.ClassExprLoc}}}},
+ {204, &dwarf.Entry{Offset: 0xc70, Tag: dwarf.TagMember, Children: false, Field: []dwarf.Field{{Attr: dwarf.AttrName, Val: "it_value", Class: dwarf.ClassString}, {Attr: dwarf.AttrDeclFile, Val: int64(7), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrDeclLine, Val: int64(237), Class: dwarf.ClassConstant}, {Attr: dwarf.AttrType, Val: dwarf.Offset(0xb7f), Class: dwarf.ClassReference}, {Attr: dwarf.AttrDataMemberLoc, Val: []byte{0x23, 0x10}, Class: dwarf.ClassExprLoc}}}},
},
},
}
diff --git a/src/debug/gosym/pclntab_test.go b/src/debug/gosym/pclntab_test.go
index 1415fac276..6855a65bbe 100644
--- a/src/debug/gosym/pclntab_test.go
+++ b/src/debug/gosym/pclntab_test.go
@@ -84,7 +84,11 @@ func crack(file string, t *testing.T) (*elf.File, *Table) {
}
func parse(file string, f *elf.File, t *testing.T) (*elf.File, *Table) {
- symdat, err := f.Section(".gosymtab").Data()
+ s := f.Section(".gosymtab")
+ if s == nil {
+ t.Skip("no .gosymtab section")
+ }
+ symdat, err := s.Data()
if err != nil {
f.Close()
t.Fatalf("reading %s gosymtab: %v", file, err)
diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go
index aa99ca5cc5..7172c1c786 100644
--- a/src/encoding/asn1/asn1.go
+++ b/src/encoding/asn1/asn1.go
@@ -579,6 +579,8 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
result, err = parseObjectIdentifier(innerBytes)
case tagUTCTime:
result, err = parseUTCTime(innerBytes)
+ case tagGeneralizedTime:
+ result, err = parseGeneralizedTime(innerBytes)
case tagOctetString:
result = innerBytes
default:
diff --git a/src/encoding/asn1/asn1_test.go b/src/encoding/asn1/asn1_test.go
index 4e864d08ac..1a2ae2569f 100644
--- a/src/encoding/asn1/asn1_test.go
+++ b/src/encoding/asn1/asn1_test.go
@@ -358,6 +358,8 @@ func newBool(b bool) *bool { return &b }
var parseFieldParametersTestData []parseFieldParametersTest = []parseFieldParametersTest{
{"", fieldParameters{}},
{"ia5", fieldParameters{stringType: tagIA5String}},
+ {"generalized", fieldParameters{timeType: tagGeneralizedTime}},
+ {"utc", fieldParameters{timeType: tagUTCTime}},
{"printable", fieldParameters{stringType: tagPrintableString}},
{"optional", fieldParameters{optional: true}},
{"explicit", fieldParameters{explicit: true, tag: new(int)}},
@@ -366,7 +368,7 @@ var parseFieldParametersTestData []parseFieldParametersTest = []parseFieldParame
{"default:42", fieldParameters{defaultValue: newInt64(42)}},
{"tag:17", fieldParameters{tag: newInt(17)}},
{"optional,explicit,default:42,tag:17", fieldParameters{optional: true, explicit: true, defaultValue: newInt64(42), tag: newInt(17)}},
- {"optional,explicit,default:42,tag:17,rubbish1", fieldParameters{true, true, false, newInt64(42), newInt(17), 0, false, false}},
+ {"optional,explicit,default:42,tag:17,rubbish1", fieldParameters{true, true, false, newInt64(42), newInt(17), 0, 0, false, false}},
{"set", fieldParameters{set: true}},
}
diff --git a/src/encoding/asn1/common.go b/src/encoding/asn1/common.go
index 33a117ece1..ab85e0496f 100644
--- a/src/encoding/asn1/common.go
+++ b/src/encoding/asn1/common.go
@@ -74,6 +74,7 @@ type fieldParameters struct {
defaultValue *int64 // a default value for INTEGER typed fields (maybe nil).
tag *int // the EXPLICIT or IMPLICIT tag (maybe nil).
stringType int // the string tag to use when marshaling.
+ timeType int // the time tag to use when marshaling.
set bool // true iff this should be encoded as a SET
omitEmpty bool // true iff this should be omitted if empty when marshaling.
@@ -94,6 +95,10 @@ func parseFieldParameters(str string) (ret fieldParameters) {
if ret.tag == nil {
ret.tag = new(int)
}
+ case part == "generalized":
+ ret.timeType = tagGeneralizedTime
+ case part == "utc":
+ ret.timeType = tagUTCTime
case part == "ia5":
ret.stringType = tagIA5String
case part == "printable":
diff --git a/src/encoding/asn1/marshal.go b/src/encoding/asn1/marshal.go
index bf92c04c9f..67a019db2d 100644
--- a/src/encoding/asn1/marshal.go
+++ b/src/encoding/asn1/marshal.go
@@ -410,9 +410,11 @@ func stripTagAndLength(in []byte) []byte {
func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameters) (err error) {
switch value.Type() {
+ case flagType:
+ return nil
case timeType:
t := value.Interface().(time.Time)
- if outsideUTCRange(t) {
+ if params.timeType == tagGeneralizedTime || outsideUTCRange(t) {
return marshalGeneralizedTime(out, t)
} else {
return marshalUTCTime(out, t)
@@ -552,6 +554,10 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
}
class := classUniversal
+ if params.timeType != 0 && tag != tagUTCTime {
+ return StructuralError{"explicit time type given to non-time member"}
+ }
+
if params.stringType != 0 && tag != tagPrintableString {
return StructuralError{"explicit string type given to non-string member"}
}
@@ -575,7 +581,7 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
tag = params.stringType
}
case tagUTCTime:
- if outsideUTCRange(v.Interface().(time.Time)) {
+ if params.timeType == tagGeneralizedTime || outsideUTCRange(v.Interface().(time.Time)) {
tag = tagGeneralizedTime
}
}
diff --git a/src/encoding/asn1/marshal_test.go b/src/encoding/asn1/marshal_test.go
index 5b0115f28c..cdca8aa336 100644
--- a/src/encoding/asn1/marshal_test.go
+++ b/src/encoding/asn1/marshal_test.go
@@ -42,6 +42,14 @@ type explicitTagTest struct {
A int `asn1:"explicit,tag:5"`
}
+type flagTest struct {
+ A Flag `asn1:"tag:0,optional"`
+}
+
+type generalizedTimeTest struct {
+ A time.Time `asn1:"generalized"`
+}
+
type ia5StringTest struct {
A string `asn1:"ia5"`
}
@@ -92,10 +100,13 @@ var marshalTests = []marshalTest{
{[]byte{1, 2, 3}, "0403010203"},
{implicitTagTest{64}, "3003850140"},
{explicitTagTest{64}, "3005a503020140"},
+ {flagTest{true}, "30028000"},
+ {flagTest{false}, "3000"},
{time.Unix(0, 0).UTC(), "170d3730303130313030303030305a"},
{time.Unix(1258325776, 0).UTC(), "170d3039313131353232353631365a"},
{time.Unix(1258325776, 0).In(PST), "17113039313131353134353631362d30383030"},
{farFuture(), "180f32313030303430353132303130315a"},
+ {generalizedTimeTest{time.Unix(1258325776, 0).UTC()}, "3011180f32303039313131353232353631365a"},
{BitString{[]byte{0x80}, 1}, "03020780"},
{BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"},
{ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"},
diff --git a/src/encoding/base64/base64.go b/src/encoding/base64/base64.go
index db4a409cb3..3302fb4a74 100644
--- a/src/encoding/base64/base64.go
+++ b/src/encoding/base64/base64.go
@@ -6,10 +6,8 @@
package base64
import (
- "bytes"
"io"
"strconv"
- "strings"
)
/*
@@ -22,7 +20,7 @@ import (
// (RFC 1421). RFC 4648 also defines an alternate encoding, which is
// the standard encoding with - and _ substituted for + and /.
type Encoding struct {
- encode string
+ encode [64]byte
decodeMap [256]byte
padChar rune
}
@@ -40,9 +38,14 @@ const encodeURL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678
// The resulting Encoding uses the default padding character ('='),
// which may be changed or disabled via WithPadding.
func NewEncoding(encoder string) *Encoding {
+ if len(encoder) != 64 {
+ panic("encoding alphabet is not 64-bytes long")
+ }
+
e := new(Encoding)
- e.encode = encoder
e.padChar = StdPadding
+ copy(e.encode[:], encoder)
+
for i := 0; i < len(e.decodeMap); i++ {
e.decodeMap[i] = 0xFF
}
@@ -77,13 +80,6 @@ var RawStdEncoding = StdEncoding.WithPadding(NoPadding)
// This is the same as URLEncoding but omits padding characters.
var RawURLEncoding = URLEncoding.WithPadding(NoPadding)
-var removeNewlinesMapper = func(r rune) rune {
- if r == '\r' || r == '\n' {
- return -1
- }
- return r
-}
-
/*
* Encoder
*/
@@ -99,46 +95,45 @@ func (enc *Encoding) Encode(dst, src []byte) {
return
}
- for len(src) > 0 {
- var b0, b1, b2, b3 byte
+ di, si := 0, 0
+ n := (len(src) / 3) * 3
+ for si < n {
+ // Convert 3x 8bit source bytes into 4 bytes
+ val := uint(src[si+0])<<16 | uint(src[si+1])<<8 | uint(src[si+2])
- // Unpack 4x 6-bit source blocks into a 4 byte
- // destination quantum
- switch len(src) {
- default:
- b3 = src[2] & 0x3F
- b2 = src[2] >> 6
- fallthrough
- case 2:
- b2 |= (src[1] << 2) & 0x3F
- b1 = src[1] >> 4
- fallthrough
- case 1:
- b1 |= (src[0] << 4) & 0x3F
- b0 = src[0] >> 2
+ dst[di+0] = enc.encode[val>>18&0x3F]
+ dst[di+1] = enc.encode[val>>12&0x3F]
+ dst[di+2] = enc.encode[val>>6&0x3F]
+ dst[di+3] = enc.encode[val&0x3F]
+
+ si += 3
+ di += 4
+ }
+
+ remain := len(src) - si
+ if remain == 0 {
+ return
+ }
+ // Add the remaining small block
+ val := uint(src[si+0]) << 16
+ if remain == 2 {
+ val |= uint(src[si+1]) << 8
+ }
+
+ dst[di+0] = enc.encode[val>>18&0x3F]
+ dst[di+1] = enc.encode[val>>12&0x3F]
+
+ switch remain {
+ case 2:
+ dst[di+2] = enc.encode[val>>6&0x3F]
+ if enc.padChar != NoPadding {
+ dst[di+3] = byte(enc.padChar)
}
-
- // Encode 6-bit blocks using the base64 alphabet
- dst[0] = enc.encode[b0]
- dst[1] = enc.encode[b1]
- if len(src) >= 3 {
- dst[2] = enc.encode[b2]
- dst[3] = enc.encode[b3]
- } else { // Final incomplete quantum
- if len(src) >= 2 {
- dst[2] = enc.encode[b2]
- }
- if enc.padChar != NoPadding {
- if len(src) < 2 {
- dst[2] = byte(enc.padChar)
- }
- dst[3] = byte(enc.padChar)
- }
- break
+ case 1:
+ if enc.padChar != NoPadding {
+ dst[di+2] = byte(enc.padChar)
+ dst[di+3] = byte(enc.padChar)
}
-
- src = src[3:]
- dst = dst[4:]
}
}
@@ -248,67 +243,83 @@ func (e CorruptInputError) Error() string {
// decode is like Decode but returns an additional 'end' value, which
// indicates if end-of-message padding or a partial quantum was encountered
-// and thus any additional data is an error. This method assumes that src has been
-// stripped of all supported whitespace ('\r' and '\n').
+// and thus any additional data is an error.
func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
- olen := len(src)
- for len(src) > 0 && !end {
+ si := 0
+
+ // skip over newlines
+ for si < len(src) && (src[si] == '\n' || src[si] == '\r') {
+ si++
+ }
+
+ for si < len(src) && !end {
// Decode quantum using the base64 alphabet
var dbuf [4]byte
dinc, dlen := 3, 4
for j := range dbuf {
- if len(src) == 0 {
+ if len(src) == si {
if enc.padChar != NoPadding || j < 2 {
- return n, false, CorruptInputError(olen - len(src) - j)
+ return n, false, CorruptInputError(si - j)
}
dinc, dlen, end = j-1, j, true
break
}
- in := src[0]
- src = src[1:]
+ in := src[si]
+
+ si++
+ // skip over newlines
+ for si < len(src) && (src[si] == '\n' || src[si] == '\r') {
+ si++
+ }
+
if rune(in) == enc.padChar {
// We've reached the end and there's padding
switch j {
case 0, 1:
// incorrect padding
- return n, false, CorruptInputError(olen - len(src) - 1)
+ return n, false, CorruptInputError(si - 1)
case 2:
// "==" is expected, the first "=" is already consumed.
- if len(src) == 0 {
+ if si == len(src) {
// not enough padding
- return n, false, CorruptInputError(olen)
+ return n, false, CorruptInputError(len(src))
}
- if rune(src[0]) != enc.padChar {
+ if rune(src[si]) != enc.padChar {
// incorrect padding
- return n, false, CorruptInputError(olen - len(src) - 1)
+ return n, false, CorruptInputError(si - 1)
+ }
+
+ si++
+ // skip over newlines
+ for si < len(src) && (src[si] == '\n' || src[si] == '\r') {
+ si++
}
- src = src[1:]
}
- if len(src) > 0 {
+ if si < len(src) {
// trailing garbage
- err = CorruptInputError(olen - len(src))
+ err = CorruptInputError(si)
}
dinc, dlen, end = 3, j, true
break
}
dbuf[j] = enc.decodeMap[in]
if dbuf[j] == 0xFF {
- return n, false, CorruptInputError(olen - len(src) - 1)
+ return n, false, CorruptInputError(si - 1)
}
}
- // Pack 4x 6-bit source blocks into 3 byte destination
- // quantum
+ // Convert 4x 6bit source bytes into 3 bytes
+ val := uint(dbuf[0])<<18 | uint(dbuf[1])<<12 | uint(dbuf[2])<<6 | uint(dbuf[3])
switch dlen {
case 4:
- dst[2] = dbuf[2]<<6 | dbuf[3]
+ dst[2] = byte(val >> 0)
fallthrough
case 3:
- dst[1] = dbuf[1]<<4 | dbuf[2]>>2
+ dst[1] = byte(val >> 8)
fallthrough
case 2:
- dst[0] = dbuf[0]<<2 | dbuf[1]>>4
+ dst[0] = byte(val >> 16)
}
dst = dst[dinc:]
n += dlen - 1
@@ -323,14 +334,12 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
// number of bytes successfully written and CorruptInputError.
// New line characters (\r and \n) are ignored.
func (enc *Encoding) Decode(dst, src []byte) (n int, err error) {
- src = bytes.Map(removeNewlinesMapper, src)
n, _, err = enc.decode(dst, src)
return
}
// DecodeString returns the bytes represented by the base64 string s.
func (enc *Encoding) DecodeString(s string) ([]byte, error) {
- s = strings.Map(removeNewlinesMapper, s)
dbuf := make([]byte, enc.DecodedLen(len(s)))
n, _, err := enc.decode(dbuf, []byte(s))
return dbuf[:n], err
@@ -359,6 +368,8 @@ func (d *decoder) Read(p []byte) (n int, err error) {
return n, nil
}
+ // This code assumes that d.r strips supported whitespace ('\r' and '\n').
+
// Read a chunk.
nn := len(p) / 3 * 4
if nn < 4 {
diff --git a/src/encoding/csv/reader.go b/src/encoding/csv/reader.go
index d9432954ac..d0a09044fb 100644
--- a/src/encoding/csv/reader.go
+++ b/src/encoding/csv/reader.go
@@ -228,6 +228,12 @@ func (r *Reader) parseRecord() (fields []string, err error) {
}
r.r.UnreadRune()
+ // If FieldsPerRecord is greater then 0 we can assume the final
+ // length of fields to be equal to FieldsPerRecord.
+ if r.FieldsPerRecord > 0 {
+ fields = make([]string, 0, r.FieldsPerRecord)
+ }
+
// At this point we have at least one field.
for {
haveField, delim, err := r.parseField()
diff --git a/src/encoding/csv/reader_test.go b/src/encoding/csv/reader_test.go
index 123df06bc8..b3c4f3bf18 100644
--- a/src/encoding/csv/reader_test.go
+++ b/src/encoding/csv/reader_test.go
@@ -282,3 +282,25 @@ func TestRead(t *testing.T) {
}
}
}
+
+func BenchmarkRead(b *testing.B) {
+ data := `x,y,z,w
+x,y,z,
+x,y,,
+x,,,
+,,,
+"x","y","z","w"
+"x","y","z",""
+"x","y","",""
+"x","","",""
+"","","",""
+`
+
+ for i := 0; i < b.N; i++ {
+ _, err := NewReader(strings.NewReader(data)).ReadAll()
+
+ if err != nil {
+ b.Fatalf("could not read data: %s", err)
+ }
+ }
+}
diff --git a/src/encoding/gob/codec_test.go b/src/encoding/gob/codec_test.go
index 56a7298fa5..c2583bfee3 100644
--- a/src/encoding/gob/codec_test.go
+++ b/src/encoding/gob/codec_test.go
@@ -1473,3 +1473,22 @@ func TestFuzzOneByte(t *testing.T) {
}
}
}
+
+// Don't crash, just give error with invalid type id.
+// Issue 9649.
+func TestErrorInvalidTypeId(t *testing.T) {
+ data := []byte{0x01, 0x00, 0x01, 0x00}
+ d := NewDecoder(bytes.NewReader(data))
+ // When running d.Decode(&foo) the first time the decoder stops
+ // after []byte{0x01, 0x00} and reports an errBadType. Running
+ // d.Decode(&foo) again on exactly the same input sequence should
+ // give another errBadType, but instead caused a panic because
+ // decoderMap wasn't cleaned up properly after the first error.
+ for i := 0; i < 2; i++ {
+ var foo struct{}
+ err := d.Decode(&foo)
+ if err != errBadType {
+ t.Fatal("decode: expected %s, got %s", errBadType, err)
+ }
+ }
+}
diff --git a/src/encoding/gob/decode.go b/src/encoding/gob/decode.go
index a5bef93141..b34110f6f9 100644
--- a/src/encoding/gob/decode.go
+++ b/src/encoding/gob/decode.go
@@ -182,6 +182,17 @@ func (state *decoderState) decodeInt() int64 {
return int64(x >> 1)
}
+// getLength decodes the next uint and makes sure it is a possible
+// size for a data item that follows, which means it must fit in a
+// non-negative int and fit in the buffer.
+func (state *decoderState) getLength() (int, bool) {
+ n := int(state.decodeUint())
+ if n < 0 || state.b.Len() < n || tooBig <= n {
+ return 0, false
+ }
+ return n, true
+}
+
// decOp is the signature of a decoding operator for a given type.
type decOp func(i *decInstr, state *decoderState, v reflect.Value)
@@ -363,16 +374,9 @@ func decComplex128(i *decInstr, state *decoderState, value reflect.Value) {
// describing the data.
// uint8 slices are encoded as an unsigned count followed by the raw bytes.
func decUint8Slice(i *decInstr, state *decoderState, value reflect.Value) {
- u := state.decodeUint()
- n := int(u)
- if n < 0 || uint64(n) != u {
- errorf("length of %s exceeds input size (%d bytes)", value.Type(), u)
- }
- if n > state.b.Len() {
- errorf("%s data too long for buffer: %d", value.Type(), n)
- }
- if n > tooBig {
- errorf("byte slice too big: %d", n)
+ n, ok := state.getLength()
+ if !ok {
+ errorf("bad %s slice length: %d", value.Type(), n)
}
if value.Cap() < n {
value.Set(reflect.MakeSlice(value.Type(), n, n))
@@ -388,13 +392,9 @@ func decUint8Slice(i *decInstr, state *decoderState, value reflect.Value) {
// describing the data.
// Strings are encoded as an unsigned count followed by the raw bytes.
func decString(i *decInstr, state *decoderState, value reflect.Value) {
- u := state.decodeUint()
- n := int(u)
- if n < 0 || uint64(n) != u || n > state.b.Len() {
- errorf("length of %s exceeds input size (%d bytes)", value.Type(), u)
- }
- if n > state.b.Len() {
- errorf("%s data too long for buffer: %d", value.Type(), n)
+ n, ok := state.getLength()
+ if !ok {
+ errorf("bad %s slice length: %d", value.Type(), n)
}
// Read the data.
data := make([]byte, n)
@@ -406,7 +406,11 @@ func decString(i *decInstr, state *decoderState, value reflect.Value) {
// ignoreUint8Array skips over the data for a byte slice value with no destination.
func ignoreUint8Array(i *decInstr, state *decoderState, value reflect.Value) {
- b := make([]byte, state.decodeUint())
+ n, ok := state.getLength()
+ if !ok {
+ errorf("slice length too large")
+ }
+ b := make([]byte, n)
state.b.Read(b)
}
@@ -571,6 +575,9 @@ func (dec *Decoder) decodeMap(mtyp reflect.Type, state *decoderState, value refl
func (dec *Decoder) ignoreArrayHelper(state *decoderState, elemOp decOp, length int) {
instr := &decInstr{elemOp, 0, nil, errors.New("no error")}
for i := 0; i < length; i++ {
+ if state.b.Len() == 0 {
+ errorf("decoding array or slice: length exceeds input size (%d elements)", length)
+ }
elemOp(instr, state, noValue)
}
}
@@ -678,7 +685,11 @@ func (dec *Decoder) decodeInterface(ityp reflect.Type, state *decoderState, valu
// ignoreInterface discards the data for an interface value with no destination.
func (dec *Decoder) ignoreInterface(state *decoderState) {
// Read the name of the concrete type.
- b := make([]byte, state.decodeUint())
+ n, ok := state.getLength()
+ if !ok {
+ errorf("bad interface encoding: name too large for buffer")
+ }
+ b := make([]byte, n)
_, err := state.b.Read(b)
if err != nil {
error_(err)
@@ -688,14 +699,22 @@ func (dec *Decoder) ignoreInterface(state *decoderState) {
error_(dec.err)
}
// At this point, the decoder buffer contains a delimited value. Just toss it.
- state.b.Drop(int(state.decodeUint()))
+ n, ok = state.getLength()
+ if !ok {
+ errorf("bad interface encoding: data length too large for buffer")
+ }
+ state.b.Drop(n)
}
// decodeGobDecoder decodes something implementing the GobDecoder interface.
// The data is encoded as a byte slice.
func (dec *Decoder) decodeGobDecoder(ut *userTypeInfo, state *decoderState, value reflect.Value) {
// Read the bytes for the value.
- b := make([]byte, state.decodeUint())
+ n, ok := state.getLength()
+ if !ok {
+ errorf("GobDecoder: length too large for buffer")
+ }
+ b := make([]byte, n)
_, err := state.b.Read(b)
if err != nil {
error_(err)
@@ -717,7 +736,11 @@ func (dec *Decoder) decodeGobDecoder(ut *userTypeInfo, state *decoderState, valu
// ignoreGobDecoder discards the data for a GobDecoder value with no destination.
func (dec *Decoder) ignoreGobDecoder(state *decoderState) {
// Read the bytes for the value.
- b := make([]byte, state.decodeUint())
+ n, ok := state.getLength()
+ if !ok {
+ errorf("GobDecoder: length too large for buffer")
+ }
+ b := make([]byte, n)
_, err := state.b.Read(b)
if err != nil {
error_(err)
@@ -1043,6 +1066,7 @@ func (dec *Decoder) compileIgnoreSingle(remoteId typeId) (engine *decEngine, err
// compileDec compiles the decoder engine for a value. If the value is not a struct,
// it calls out to compileSingle.
func (dec *Decoder) compileDec(remoteId typeId, ut *userTypeInfo) (engine *decEngine, err error) {
+ defer catchError(&err)
rt := ut.base
srt := rt
if srt.Kind() != reflect.Struct || ut.externalDec != 0 {
@@ -1116,7 +1140,7 @@ type emptyStruct struct{}
var emptyStructType = reflect.TypeOf(emptyStruct{})
-// getDecEnginePtr returns the engine for the specified type when the value is to be discarded.
+// getIgnoreEnginePtr returns the engine for the specified type when the value is to be discarded.
func (dec *Decoder) getIgnoreEnginePtr(wireId typeId) (enginePtr **decEngine, err error) {
var ok bool
if enginePtr, ok = dec.ignorerCache[wireId]; !ok {
@@ -1155,8 +1179,9 @@ func (dec *Decoder) decodeValue(wireId typeId, value reflect.Value) {
value = decAlloc(value)
engine := *enginePtr
if st := base; st.Kind() == reflect.Struct && ut.externalDec == 0 {
+ wt := dec.wireType[wireId]
if engine.numInstr == 0 && st.NumField() > 0 &&
- dec.wireType[wireId] != nil && len(dec.wireType[wireId].StructT.Field) > 0 {
+ wt != nil && len(wt.StructT.Field) > 0 {
name := base.Name()
errorf("type mismatch: no fields matched compiling decoder for %s", name)
}
diff --git a/src/encoding/gob/encoder_test.go b/src/encoding/gob/encoder_test.go
index 4af7195209..c0bd379c93 100644
--- a/src/encoding/gob/encoder_test.go
+++ b/src/encoding/gob/encoder_test.go
@@ -6,8 +6,8 @@ package gob
import (
"bytes"
+ "encoding/hex"
"fmt"
- "io"
"reflect"
"strings"
"testing"
@@ -187,24 +187,6 @@ func TestWrongTypeDecoder(t *testing.T) {
badTypeCheck(new(ET4), true, "different type of field", t)
}
-func corruptDataCheck(s string, err error, t *testing.T) {
- b := bytes.NewBufferString(s)
- dec := NewDecoder(b)
- err1 := dec.Decode(new(ET2))
- if err1 != err {
- t.Errorf("from %q expected error %s; got %s", s, err, err1)
- }
-}
-
-// Check that we survive bad data.
-func TestBadData(t *testing.T) {
- corruptDataCheck("", io.EOF, t)
- corruptDataCheck("\x7Fhi", io.ErrUnexpectedEOF, t)
- corruptDataCheck("\x03now is the time for all good men", errBadType, t)
- // issue 6323.
- corruptDataCheck("\x04\x24foo", errRange, t)
-}
-
// Types not supported at top level by the Encoder.
var unsupportedValues = []interface{}{
make(chan int),
@@ -954,3 +936,43 @@ func TestErrorForHugeSlice(t *testing.T) {
t.Fatalf("decode: expected slice too big error, got %s", err.Error())
}
}
+
+type badDataTest struct {
+ input string // The input encoded as a hex string.
+ error string // A substring of the error that should result.
+ data interface{} // What to decode into.
+}
+
+var badDataTests = []badDataTest{
+ {"", "EOF", nil},
+ {"7F6869", "unexpected EOF", nil},
+ {"036e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e", "unknown type id", new(ET2)},
+ {"0424666f6f", "field numbers out of bounds", new(ET2)}, // Issue 6323.
+ {"05100028557b02027f8302", "interface encoding", nil}, // Issue 10270.
+ // Issue 10273.
+ {"130a00fb5dad0bf8ff020263e70002fa28020202a89859", "slice length too large", nil},
+ {"0f1000fb285d003316020735ff023a65c5", "interface encoding", nil},
+ {"03fffb0616fffc00f902ff02ff03bf005d02885802a311a8120228022c028ee7", "GobDecoder", nil},
+ // Issue 10491.
+ {"10fe010f020102fe01100001fe010e000016fe010d030102fe010e00010101015801fe01100000000bfe011000f85555555555555555", "length exceeds input size", nil},
+}
+
+// TestBadData tests that various problems caused by malformed input
+// are caught as errors and do not cause panics.
+func TestBadData(t *testing.T) {
+ for i, test := range badDataTests {
+ data, err := hex.DecodeString(test.input)
+ if err != nil {
+ t.Fatalf("#%d: hex error: %s", i, err)
+ }
+ d := NewDecoder(bytes.NewReader(data))
+ err = d.Decode(test.data)
+ if err == nil {
+ t.Errorf("decode: no error")
+ continue
+ }
+ if !strings.Contains(err.Error(), test.error) {
+ t.Errorf("#%d: decode: expected %q error, got %s", i, test.error, err.Error())
+ }
+ }
+}
diff --git a/src/encoding/xml/marshal.go b/src/encoding/xml/marshal.go
index a0e2058d89..d0899c0fa6 100644
--- a/src/encoding/xml/marshal.go
+++ b/src/encoding/xml/marshal.go
@@ -209,7 +209,7 @@ func (enc *Encoder) EncodeToken(t Token) error {
return err
}
case CharData:
- EscapeText(p, t)
+ escapeText(p, t, false)
case Comment:
if bytes.Contains(t, endComment) {
return fmt.Errorf("xml: EncodeToken of Comment containing --> marker")
diff --git a/src/encoding/xml/marshal_test.go b/src/encoding/xml/marshal_test.go
index 8362421db7..5e9718c20c 100644
--- a/src/encoding/xml/marshal_test.go
+++ b/src/encoding/xml/marshal_test.go
@@ -1297,7 +1297,7 @@ var encodeTokenTests = []struct {
toks: []Token{
CharData(" \t\n"),
},
- want: `
`,
+ want: " \n",
}, {
desc: "comment",
toks: []Token{
diff --git a/src/encoding/xml/xml.go b/src/encoding/xml/xml.go
index 0c64cd730d..00792c4f27 100644
--- a/src/encoding/xml/xml.go
+++ b/src/encoding/xml/xml.go
@@ -1863,6 +1863,13 @@ var (
// EscapeText writes to w the properly escaped XML equivalent
// of the plain text data s.
func EscapeText(w io.Writer, s []byte) error {
+ return escapeText(w, s, true)
+}
+
+// escapeText writes to w the properly escaped XML equivalent
+// of the plain text data s. If escapeNewline is true, newline
+// characters will be escaped.
+func escapeText(w io.Writer, s []byte, escapeNewline bool) error {
var esc []byte
last := 0
for i := 0; i < len(s); {
@@ -1882,6 +1889,9 @@ func EscapeText(w io.Writer, s []byte) error {
case '\t':
esc = esc_tab
case '\n':
+ if !escapeNewline {
+ continue
+ }
esc = esc_nl
case '\r':
esc = esc_cr
diff --git a/src/expvar/expvar.go b/src/expvar/expvar.go
index 2cb515a678..24c2d6b29a 100644
--- a/src/expvar/expvar.go
+++ b/src/expvar/expvar.go
@@ -26,6 +26,7 @@ import (
"encoding/json"
"fmt"
"log"
+ "math"
"net/http"
"os"
"runtime"
@@ -59,28 +60,30 @@ func (v *Int) Set(value int64) {
// Float is a 64-bit float variable that satisfies the Var interface.
type Float struct {
- mu sync.RWMutex
- f float64
+ f uint64
}
func (v *Float) String() string {
- v.mu.RLock()
- defer v.mu.RUnlock()
- return strconv.FormatFloat(v.f, 'g', -1, 64)
+ return strconv.FormatFloat(
+ math.Float64frombits(atomic.LoadUint64(&v.f)), 'g', -1, 64)
}
// Add adds delta to v.
func (v *Float) Add(delta float64) {
- v.mu.Lock()
- defer v.mu.Unlock()
- v.f += delta
+ for {
+ cur := atomic.LoadUint64(&v.f)
+ curVal := math.Float64frombits(cur)
+ nxtVal := curVal + delta
+ nxt := math.Float64bits(nxtVal)
+ if atomic.CompareAndSwapUint64(&v.f, cur, nxt) {
+ return
+ }
+ }
}
// Set sets v to value.
func (v *Float) Set(value float64) {
- v.mu.Lock()
- defer v.mu.Unlock()
- v.f = value
+ atomic.StoreUint64(&v.f, math.Float64bits(value))
}
// Map is a string-to-Var map variable that satisfies the Var interface.
diff --git a/src/expvar/expvar_test.go b/src/expvar/expvar_test.go
index 11e6497b96..8bc633e4a9 100644
--- a/src/expvar/expvar_test.go
+++ b/src/expvar/expvar_test.go
@@ -7,11 +7,13 @@ package expvar
import (
"bytes"
"encoding/json"
+ "math"
"net"
"net/http/httptest"
"runtime"
"strconv"
"sync"
+ "sync/atomic"
"testing"
)
@@ -70,6 +72,10 @@ func BenchmarkIntSet(b *testing.B) {
})
}
+func (v *Float) val() float64 {
+ return math.Float64frombits(atomic.LoadUint64(&v.f))
+}
+
func TestFloat(t *testing.T) {
RemoveAll()
reqs := NewFloat("requests-float")
@@ -82,8 +88,8 @@ func TestFloat(t *testing.T) {
reqs.Add(1.5)
reqs.Add(1.25)
- if reqs.f != 2.75 {
- t.Errorf("reqs.f = %v, want 2.75", reqs.f)
+ if v := reqs.val(); v != 2.75 {
+ t.Errorf("reqs.val() = %v, want 2.75", v)
}
if s := reqs.String(); s != "2.75" {
@@ -91,8 +97,8 @@ func TestFloat(t *testing.T) {
}
reqs.Add(-2)
- if reqs.f != 0.75 {
- t.Errorf("reqs.f = %v, want 0.75", reqs.f)
+ if v := reqs.val(); v != 0.75 {
+ t.Errorf("reqs.val() = %v, want 0.75", v)
}
}
@@ -157,8 +163,8 @@ func TestMapCounter(t *testing.T) {
if x := colors.m["blue"].(*Int).i; x != 4 {
t.Errorf("colors.m[\"blue\"] = %v, want 4", x)
}
- if x := colors.m[`green "midori"`].(*Float).f; x != 4.125 {
- t.Errorf("colors.m[`green \"midori\"] = %v, want 3.14", x)
+ if x := colors.m[`green "midori"`].(*Float).val(); x != 4.125 {
+ t.Errorf("colors.m[`green \"midori\"] = %v, want 4.125", x)
}
// colors.String() should be '{"red":3, "blue":4}',
diff --git a/src/fmt/doc.go b/src/fmt/doc.go
index 9ba11f4a2b..cbca6ab492 100644
--- a/src/fmt/doc.go
+++ b/src/fmt/doc.go
@@ -138,20 +138,23 @@
formatting considerations apply for operands that implement
certain interfaces. In order of application:
- 1. If an operand implements the Formatter interface, it will
+ 1. If the operand is a reflect.Value, the concrete value it
+ holds is printed as if it was the operand.
+
+ 2. If an operand implements the Formatter interface, it will
be invoked. Formatter provides fine control of formatting.
- 2. If the %v verb is used with the # flag (%#v) and the operand
+ 3. If the %v verb is used with the # flag (%#v) and the operand
implements the GoStringer interface, that will be invoked.
If the format (which is implicitly %v for Println etc.) is valid
for a string (%s %q %v %x %X), the following two rules apply:
- 3. If an operand implements the error interface, the Error method
+ 4. If an operand implements the error interface, the Error method
will be invoked to convert the object to a string, which will then
be formatted as required by the verb (if any).
- 4. If an operand implements method String() string, that method
+ 5. If an operand implements method String() string, that method
will be invoked to convert the object to a string, which will then
be formatted as required by the verb (if any).
diff --git a/src/fmt/fmt_test.go b/src/fmt/fmt_test.go
index c14bd2f45c..ba99cb0f6a 100644
--- a/src/fmt/fmt_test.go
+++ b/src/fmt/fmt_test.go
@@ -9,6 +9,7 @@ import (
. "fmt"
"io"
"math"
+ "reflect"
"runtime"
"strings"
"testing"
@@ -394,6 +395,8 @@ var fmtTests = []struct {
{"%v", &slice, "&[1 2 3 4 5]"},
{"%v", &islice, "&[1 hello 2.5 ]"},
{"%v", &bslice, "&[1 2 3 4 5]"},
+ {"%v", []byte{1}, "[1]"},
+ {"%v", []byte{}, "[]"},
// complexes with %v
{"%v", 1 + 2i, "(1+2i)"},
@@ -447,6 +450,12 @@ var fmtTests = []struct {
{"%d", []int{1, 2, 15}, `[1 2 15]`},
{"%d", []byte{1, 2, 15}, `[1 2 15]`},
{"%q", []string{"a", "b"}, `["a" "b"]`},
+ {"% 02x", []byte{1}, "01"},
+ {"% 02x", []byte{1, 2, 3}, "01 02 03"},
+ // Special care for empty slices.
+ {"%x", []byte{}, ""},
+ {"%02x", []byte{}, ""},
+ {"% 02x", []byte{}, ""},
// renamings
{"%v", renamedBool(true), "true"},
@@ -528,6 +537,8 @@ var fmtTests = []struct {
{"%s", nil, "%!s()"},
{"%T", nil, ""},
{"%-1", 100, "%!(NOVERB)%!(EXTRA int=100)"},
+ {"%017091901790959340919092959340919017929593813360", 0, "%!(NOVERB)%!(EXTRA int=0)"},
+ {"%184467440737095516170v", 0, "%!(NOVERB)%!(EXTRA int=0)"},
// The "" show up because maps are printed by
// first obtaining a list of keys and then looking up
@@ -546,6 +557,11 @@ var fmtTests = []struct {
{"%0.100f", 1.0, zeroFill("1.", 100, "")},
{"%0.100f", -1.0, zeroFill("-1.", 100, "")},
+ // Used to panic: integer function didn't look at f.prec or f.unicode.
+ {"%#.80x", 42, "0x0000000000000000000000000000000000000000000000000000000000000000000000000000002a"},
+ {"%.80U", 42, "U+0000000000000000000000000000000000000000000000000000000000000000000000000000002A"},
+ {"%#.80U", '日', "U+000000000000000000000000000000000000000000000000000000000000000000000000000065E5 '日'"},
+
// Comparison of padding rules with C printf.
/*
C program:
@@ -671,6 +687,20 @@ var fmtTests = []struct {
{"%x", byteFormatterSlice, "61626364"},
// This next case seems wrong, but the docs say the Formatter wins here.
{"%#v", byteFormatterSlice, "[]fmt_test.byteFormatter{X, X, X, X}"},
+
+ // reflect.Value handled specially in Go 1.5, making it possible to
+ // see inside non-exported fields (which cannot be accessed with Interface()).
+ // Issue 8965.
+ {"%v", reflect.ValueOf(A{}).Field(0).String(), ""}, // Equivalent to the old way.
+ {"%v", reflect.ValueOf(A{}).Field(0), "0"}, // Sees inside the field.
+
+ // verbs apply to the extracted value too.
+ {"%s", reflect.ValueOf("hello"), "hello"},
+ {"%q", reflect.ValueOf("hello"), `"hello"`},
+ {"%#04x", reflect.ValueOf(256), "0x0100"},
+
+ // invalid reflect.Value doesn't crash.
+ {"%v", reflect.Value{}, ""},
}
// zeroFill generates zero-filled strings of the specified width. The length
@@ -797,6 +827,7 @@ var reorderTests = []struct {
{"%d %d %d %#[1]o %#o %#o %#o", SE{11, 12, 13}, "11 12 13 013 014 015 %!o(MISSING)"},
{"%[5]d %[2]d %d", SE{1, 2, 3}, "%!d(BADINDEX) 2 3"},
{"%d %[3]d %d", SE{1, 2}, "1 %!d(BADINDEX) 2"}, // Erroneous index does not affect sequence.
+ {"%.[]", SE{}, "%!](BADINDEX)"}, // Issue 10675
}
func TestReorder(t *testing.T) {
diff --git a/src/fmt/format.go b/src/fmt/format.go
index 4d97d1443e..099f8a5e00 100644
--- a/src/fmt/format.go
+++ b/src/fmt/format.go
@@ -163,12 +163,20 @@ func (f *fmt) integer(a int64, base uint64, signedness bool, digits string) {
}
var buf []byte = f.intbuf[0:]
- if f.widPresent {
- width := f.wid
+ if f.widPresent || f.precPresent {
+ width := f.wid + f.prec // Only one will be set, both are positive; this provides the maximum.
if base == 16 && f.sharp {
// Also adds "0x".
width += 2
}
+ if f.unicode {
+ // Also adds "U+".
+ width += 2
+ if f.uniQuote {
+ // Also adds " 'x'".
+ width += 1 + 1 + utf8.UTFMax + 1
+ }
+ }
if width > nByte {
// We're going to need a bigger boat.
buf = make([]byte, width)
@@ -335,7 +343,7 @@ func (f *fmt) fmt_sbx(s string, b []byte, digits string) {
}
buf = append(buf, digits[c>>4], digits[c&0xF])
}
- f.pad(buf)
+ f.buf.Write(buf)
}
// fmt_sx formats a string as a hexadecimal encoding of its bytes.
diff --git a/src/fmt/print.go b/src/fmt/print.go
index 59a30d221e..d07835da49 100644
--- a/src/fmt/print.go
+++ b/src/fmt/print.go
@@ -291,6 +291,11 @@ func parsenum(s string, start, end int) (num int, isnum bool, newi int) {
return 0, false, end
}
for newi = start; newi < end && '0' <= s[newi] && s[newi] <= '9'; newi++ {
+ const maxInt32 = 1<<31 - 1 // 31 bits is plenty for a width.
+ max := maxInt32/10 - 1
+ if num > max {
+ return 0, false, end // Overflow; crazy long number most likely.
+ }
num = num*10 + int(s[newi]-'0')
isnum = true
}
@@ -789,6 +794,8 @@ func (p *pp) printArg(arg interface{}, verb rune, depth int) (wasString bool) {
case []byte:
p.fmtBytes(f, verb, nil, depth)
wasString = verb == 's'
+ case reflect.Value:
+ return p.printReflectValue(f, verb, depth)
default:
// If the type is not simple, it might have methods.
if handled := p.handleMethods(verb, depth); handled {
@@ -845,6 +852,8 @@ func (p *pp) printReflectValue(value reflect.Value, verb rune, depth int) (wasSt
p.value = value
BigSwitch:
switch f := value; f.Kind() {
+ case reflect.Invalid:
+ p.buf.WriteString("")
case reflect.Bool:
p.fmtBool(f.Bool(), verb)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
@@ -1027,6 +1036,11 @@ func intFromArg(a []interface{}, argNum int) (num int, isInt bool, newArgNum int
// up to the closing paren, if present, and whether the number parsed
// ok. The bytes to consume will be 1 if no closing paren is present.
func parseArgNumber(format string) (index int, wid int, ok bool) {
+ // There must be at least 3 bytes: [n].
+ if len(format) < 3 {
+ return 0, 1, false
+ }
+
// Find closing bracket.
for i := 1; i < len(format); i++ {
if format[i] == ']' {
@@ -1053,7 +1067,7 @@ func (p *pp) argNumber(argNum int, format string, i int, numArgs int) (newArgNum
return index, i + wid, true
}
p.goodArgNum = false
- return argNum, i + wid, true
+ return argNum, i + wid, ok
}
func (p *pp) doPrintf(format string, a []interface{}) {
@@ -1123,7 +1137,7 @@ func (p *pp) doPrintf(format string, a []interface{}) {
p.goodArgNum = false
}
argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a))
- if format[i] == '*' {
+ if i < end && format[i] == '*' {
i++
p.fmt.prec, p.fmt.precPresent, argNum = intFromArg(a, argNum)
if !p.fmt.precPresent {
diff --git a/src/fmt/scan.go b/src/fmt/scan.go
index 93cd553a57..95725303d9 100644
--- a/src/fmt/scan.go
+++ b/src/fmt/scan.go
@@ -81,6 +81,7 @@ func Scanln(a ...interface{}) (n int, err error) {
// 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.
+// If that is less than the number of arguments, err will report why.
func Scanf(format string, a ...interface{}) (n int, err error) {
return Fscanf(os.Stdin, format, a...)
}
diff --git a/src/go/ast/walk.go b/src/go/ast/walk.go
index 73ac38647a..8ca21959b1 100644
--- a/src/go/ast/walk.go
+++ b/src/go/ast/walk.go
@@ -361,8 +361,7 @@ func Walk(v Visitor, node Node) {
}
default:
- fmt.Printf("ast.Walk: unexpected node type %T", n)
- panic("ast.Walk")
+ panic(fmt.Sprintf("ast.Walk: unexpected node type %T", n))
}
v.Visit(nil)
@@ -379,7 +378,8 @@ func (f inspector) Visit(node Node) Visitor {
// Inspect traverses an AST in depth-first order: It starts by calling
// f(node); node must not be nil. If f returns true, Inspect invokes f
-// for all the non-nil children of node, recursively.
+// recursively for each of the non-nil children of node, followed by a
+// call of f(nil).
//
func Inspect(node Node, f func(Node) bool) {
Walk(inspector(f), node)
diff --git a/src/go/build/build.go b/src/go/build/build.go
index b590105a5c..124da40d3b 100644
--- a/src/go/build/build.go
+++ b/src/go/build/build.go
@@ -259,12 +259,15 @@ var Default Context = defaultContext()
var cgoEnabled = map[string]bool{
"darwin/386": true,
"darwin/amd64": true,
+ "darwin/arm": true,
+ "darwin/arm64": true,
"dragonfly/amd64": true,
"freebsd/386": true,
"freebsd/amd64": true,
"linux/386": true,
"linux/amd64": true,
"linux/arm": true,
+ "linux/arm64": true,
"linux/ppc64le": true,
"android/386": true,
"android/amd64": true,
@@ -274,6 +277,7 @@ var cgoEnabled = map[string]bool{
"netbsd/arm": true,
"openbsd/386": true,
"openbsd/amd64": true,
+ "solaris/amd64": true,
"windows/386": true,
"windows/amd64": true,
}
@@ -353,6 +357,7 @@ type Package struct {
Root string // root of Go tree where this package lives
SrcRoot string // package source root directory ("" if unknown)
PkgRoot string // package install root directory ("" if unknown)
+ PkgTargetRoot string // architecture dependent install root directory ("" if unknown)
BinDir string // command install directory ("" if unknown)
Goroot bool // package found in Go root
PkgObj string // installed .a file
@@ -461,18 +466,21 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
return p, fmt.Errorf("import %q: invalid import path", path)
}
+ var pkgtargetroot string
var pkga string
var pkgerr error
+ suffix := ""
+ if ctxt.InstallSuffix != "" {
+ suffix = "_" + ctxt.InstallSuffix
+ }
switch ctxt.Compiler {
case "gccgo":
+ pkgtargetroot = "pkg/gccgo_" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix
dir, elem := pathpkg.Split(p.ImportPath)
- pkga = "pkg/gccgo_" + ctxt.GOOS + "_" + ctxt.GOARCH + "/" + dir + "lib" + elem + ".a"
+ pkga = pkgtargetroot + "/" + dir + "lib" + elem + ".a"
case "gc":
- suffix := ""
- if ctxt.InstallSuffix != "" {
- suffix = "_" + ctxt.InstallSuffix
- }
- pkga = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix + "/" + p.ImportPath + ".a"
+ pkgtargetroot = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix
+ pkga = pkgtargetroot + "/" + p.ImportPath + ".a"
default:
// Save error for end of function.
pkgerr = fmt.Errorf("import %q: unknown compiler %q", path, ctxt.Compiler)
@@ -589,6 +597,7 @@ Found:
p.PkgRoot = ctxt.joinPath(p.Root, "pkg")
p.BinDir = ctxt.joinPath(p.Root, "bin")
if pkga != "" {
+ p.PkgTargetRoot = ctxt.joinPath(p.Root, pkgtargetroot)
p.PkgObj = ctxt.joinPath(p.Root, pkga)
}
}
diff --git a/src/go/build/build_test.go b/src/go/build/build_test.go
index cc51174ef4..92c3fe3764 100644
--- a/src/go/build/build_test.go
+++ b/src/go/build/build_test.go
@@ -109,8 +109,11 @@ func TestMultiplePackageImport(t *testing.T) {
}
func TestLocalDirectory(t *testing.T) {
- if runtime.GOOS == "darwin" && runtime.GOARCH == "arm" {
- t.Skipf("skipping on %s/%s, no valid GOROOT", runtime.GOOS, runtime.GOARCH)
+ if runtime.GOOS == "darwin" {
+ switch runtime.GOARCH {
+ case "arm", "arm64":
+ t.Skipf("skipping on %s/%s, no valid GOROOT", runtime.GOOS, runtime.GOARCH)
+ }
}
cwd, err := os.Getwd()
@@ -227,8 +230,11 @@ func TestMatchFile(t *testing.T) {
}
func TestImportCmd(t *testing.T) {
- if runtime.GOOS == "darwin" && runtime.GOARCH == "arm" {
- t.Skipf("skipping on %s/%s, no valid GOROOT", runtime.GOOS, runtime.GOARCH)
+ if runtime.GOOS == "darwin" {
+ switch runtime.GOARCH {
+ case "arm", "arm64":
+ t.Skipf("skipping on %s/%s, no valid GOROOT", runtime.GOOS, runtime.GOARCH)
+ }
}
p, err := Import("cmd/internal/objfile", "", 0)
diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
index 5719ffcec6..84c1e2ab31 100644
--- a/src/go/build/deps_test.go
+++ b/src/go/build/deps_test.go
@@ -8,8 +8,11 @@
package build
import (
+ "os"
+ "path/filepath"
"runtime"
"sort"
+ "strings"
"testing"
)
@@ -122,7 +125,7 @@ var pkgDeps = map[string][]string{
// Operating system access.
"syscall": {"L0", "unicode/utf16"},
- "time": {"L0", "syscall"},
+ "time": {"L0", "syscall", "internal/syscall/windows/registry"},
"os": {"L1", "os", "syscall", "time", "internal/syscall/windows"},
"path/filepath": {"L2", "os", "syscall"},
"io/ioutil": {"L2", "os", "path/filepath", "time"},
@@ -213,7 +216,7 @@ var pkgDeps = map[string][]string{
"image/png": {"L4", "compress/zlib"},
"index/suffixarray": {"L4", "regexp"},
"math/big": {"L4"},
- "mime": {"L4", "OS", "syscall"},
+ "mime": {"L4", "OS", "syscall", "internal/syscall/windows/registry"},
"net/url": {"L4"},
"text/scanner": {"L4", "OS"},
"text/template/parse": {"L4"},
@@ -240,7 +243,7 @@ var pkgDeps = map[string][]string{
// Basic networking.
// Because net must be used by any package that wants to
// do networking portably, it must have a small dependency set: just L1+basic os.
- "net": {"L1", "CGO", "os", "syscall", "time", "internal/syscall/windows"},
+ "net": {"L1", "CGO", "os", "syscall", "time", "internal/syscall/windows", "internal/singleflight"},
// NET enables use of basic network-related packages.
"NET": {
@@ -279,7 +282,7 @@ var pkgDeps = map[string][]string{
// Random byte, number generation.
// This would be part of core crypto except that it imports
// math/big, which imports fmt.
- "crypto/rand": {"L4", "CRYPTO", "OS", "math/big", "syscall", "internal/syscall"},
+ "crypto/rand": {"L4", "CRYPTO", "OS", "math/big", "syscall", "internal/syscall/unix"},
// Mathematical crypto: dependencies on fmt (L4) and math/big.
// We could avoid some of the fmt, but math/big imports fmt anyway.
@@ -330,6 +333,30 @@ var pkgDeps = map[string][]string{
"net/http/pprof": {"L4", "OS", "html/template", "net/http", "runtime/pprof"},
"net/rpc": {"L4", "NET", "encoding/gob", "html/template", "net/http"},
"net/rpc/jsonrpc": {"L4", "NET", "encoding/json", "net/rpc"},
+
+ // Packages below are grandfathered because of issue 10475.
+ // When updating these entries, move them to an appropriate
+ // location above and assign them a justified set of
+ // dependencies. Do not simply update them in situ.
+ "container/heap": {"sort"},
+ "debug/plan9obj": {"encoding/binary", "errors", "fmt", "io", "os"},
+ "go/constants": {"fmt", "go/token", "math/big", "strconv"},
+ "go/format": {"bytes", "fmt", "go/ast", "go/parser", "go/printer", "go/token", "internal/format", "io"},
+ "go/importer": {"go/internal/gcimporter", "go/types", "io", "runtime"},
+ "go/internal/gcimporter": {"bufio", "errors", "fmt", "go/build", "go/constants", "go/token", "go/types", "io", "os", "path/filepath", "strconv", "strings", "text/scanner"},
+ "go/types": {"bytes", "container/heap", "fmt", "go/ast", "go/constants", "go/parser", "go/token", "io", "math", "path", "sort", "strconv", "strings", "sync", "unicode"},
+ "image/internal/imageutil": {"image"},
+ "internal/format": {"bytes", "go/ast", "go/parser", "go/printer", "go/token", "strings"},
+ "internal/mime": {"bytes", "encoding/base64", "errors", "fmt", "io", "io/ioutil", "strconv", "strings", "unicode"},
+ "internal/singleflight": {"sync"},
+ "internal/syscall/unix": {"runtime", "sync/atomic", "syscall", "unsafe"},
+ "internal/syscall/windows": {"syscall", "unsafe"},
+ "internal/syscall/windows/registry": {"errors", "io", "syscall", "unicode/utf16", "unsafe"},
+ "internal/trace": {"bufio", "bytes", "fmt", "io", "os", "os/exec", "sort", "strconv", "strings"},
+ "mime/quotedprintable": {"bufio", "bytes", "fmt", "io"},
+ "net/http/cookiejar": {"errors", "fmt", "net", "net/http", "net/url", "sort", "strings", "sync", "time", "unicode/utf8"},
+ "net/http/internal": {"bufio", "bytes", "errors", "fmt", "io"},
+ "net/internal/socktest": {"fmt", "sync", "syscall"},
}
// isMacro reports whether p is a package dependency macro
@@ -375,30 +402,61 @@ var allowedErrors = map[osPkg]bool{
osPkg{"plan9", "log/syslog"}: true,
}
+// listStdPkgs returns the same list of packages as "go list std".
+func listStdPkgs(goroot string) ([]string, error) {
+ // Based on cmd/go's matchPackages function.
+ var pkgs []string
+
+ src := filepath.Join(goroot, "src") + string(filepath.Separator)
+ walkFn := func(path string, fi os.FileInfo, err error) error {
+ if err != nil || !fi.IsDir() || path == src {
+ return nil
+ }
+
+ base := filepath.Base(path)
+ if strings.HasPrefix(base, ".") || strings.HasPrefix(base, "_") || base == "testdata" {
+ return filepath.SkipDir
+ }
+
+ name := filepath.ToSlash(path[len(src):])
+ if name == "builtin" || name == "cmd" || strings.Contains(name, ".") {
+ return filepath.SkipDir
+ }
+
+ pkgs = append(pkgs, name)
+ return nil
+ }
+ if err := filepath.Walk(src, walkFn); err != nil {
+ return nil, err
+ }
+ return pkgs, nil
+}
+
func TestDependencies(t *testing.T) {
- if runtime.GOOS == "nacl" || (runtime.GOOS == "darwin" && runtime.GOARCH == "arm") {
+ iOS := runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64")
+ if runtime.GOOS == "nacl" || iOS {
// Tests run in a limited file system and we do not
// provide access to every source file.
- t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
+ t.Skipf("skipping on %s/%s, missing full GOROOT", runtime.GOOS, runtime.GOARCH)
}
- var all []string
- for k := range pkgDeps {
- all = append(all, k)
+ ctxt := Default
+ all, err := listStdPkgs(ctxt.GOROOT)
+ if err != nil {
+ t.Fatal(err)
}
sort.Strings(all)
- ctxt := Default
test := func(mustImport bool) {
for _, pkg := range all {
- if isMacro(pkg) {
- continue
- }
if pkg == "runtime/cgo" && !ctxt.CgoEnabled {
continue
}
p, err := ctxt.Import(pkg, "", 0)
if err != nil {
+ if _, ok := err.(*NoGoError); ok {
+ continue
+ }
if allowedErrors[osPkg{ctxt.GOOS, pkg}] {
continue
}
diff --git a/src/go/build/syslist.go b/src/go/build/syslist.go
index e84a06666f..2c2cac94e9 100644
--- a/src/go/build/syslist.go
+++ b/src/go/build/syslist.go
@@ -5,4 +5,4 @@
package build
const goosList = "android darwin dragonfly freebsd linux nacl netbsd openbsd plan9 solaris windows "
-const goarchList = "386 amd64 amd64p32 arm arm64 ppc64 ppc64le "
+const goarchList = "386 amd64 amd64p32 arm arm64 ppc64 ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc s390 s390x sparc sparc64 "
diff --git a/src/go/constants/go13.go b/src/go/constants/go13.go
new file mode 100644
index 0000000000..f445b82154
--- /dev/null
+++ b/src/go/constants/go13.go
@@ -0,0 +1,24 @@
+// Copyright 2014 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 !go1.4
+
+package constants
+
+import (
+ "math"
+ "math/big"
+)
+
+func ratToFloat32(x *big.Rat) (float32, bool) {
+ // Before 1.4, there's no Rat.Float32.
+ // Emulate it, albeit at the cost of
+ // imprecision in corner cases.
+ x64, exact := x.Float64()
+ x32 := float32(x64)
+ if math.IsInf(float64(x32), 0) {
+ exact = false
+ }
+ return x32, exact
+}
diff --git a/src/go/constants/go14.go b/src/go/constants/go14.go
new file mode 100644
index 0000000000..c698fa6de9
--- /dev/null
+++ b/src/go/constants/go14.go
@@ -0,0 +1,13 @@
+// Copyright 2014 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 go1.4
+
+package constants
+
+import "math/big"
+
+func ratToFloat32(x *big.Rat) (float32, bool) {
+ return x.Float32()
+}
diff --git a/src/go/constants/value.go b/src/go/constants/value.go
new file mode 100644
index 0000000000..ad4533c900
--- /dev/null
+++ b/src/go/constants/value.go
@@ -0,0 +1,925 @@
+// 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 constants implements Values representing untyped
+// Go constants and the corresponding operations. Values
+// and operations may have arbitrary or unlimited precision.
+//
+// A special Unknown value may be used when a value
+// is unknown due to an error. Operations on unknown
+// values produce unknown values unless specified
+// otherwise.
+//
+package constants // import "go/constants"
+
+import (
+ "fmt"
+ "go/token"
+ "math/big"
+ "strconv"
+)
+
+// Kind specifies the kind of value represented by a Value.
+type Kind int
+
+// Implementation note: Kinds must be enumerated in
+// order of increasing "complexity" (used by match).
+
+const (
+ // unknown values
+ Unknown Kind = iota
+
+ // non-numeric values
+ Bool
+ String
+
+ // numeric values
+ Int
+ Float
+ Complex
+)
+
+// A Value represents a mathematically exact value of a given Kind.
+type Value interface {
+ // Kind returns the value kind; it is always the smallest
+ // kind in which the value can be represented exactly.
+ Kind() Kind
+
+ // String returns a human-readable form of the value.
+ String() string
+
+ // Prevent external implementations.
+ implementsValue()
+}
+
+// ----------------------------------------------------------------------------
+// Implementations
+
+type (
+ unknownVal struct{}
+ boolVal bool
+ stringVal string
+ int64Val int64
+ intVal struct{ val *big.Int }
+ floatVal struct{ val *big.Rat }
+ complexVal struct{ re, im *big.Rat }
+)
+
+func (unknownVal) Kind() Kind { return Unknown }
+func (boolVal) Kind() Kind { return Bool }
+func (stringVal) Kind() Kind { return String }
+func (int64Val) Kind() Kind { return Int }
+func (intVal) Kind() Kind { return Int }
+func (floatVal) Kind() Kind { return Float }
+func (complexVal) Kind() Kind { return Complex }
+
+func (unknownVal) String() string { return "unknown" }
+func (x boolVal) String() string { return fmt.Sprintf("%v", bool(x)) }
+func (x stringVal) String() string { return strconv.Quote(string(x)) }
+func (x int64Val) String() string { return strconv.FormatInt(int64(x), 10) }
+func (x intVal) String() string { return x.val.String() }
+func (x floatVal) String() string { return x.val.String() }
+func (x complexVal) String() string { return fmt.Sprintf("(%s + %si)", x.re, x.im) }
+
+func (unknownVal) implementsValue() {}
+func (boolVal) implementsValue() {}
+func (stringVal) implementsValue() {}
+func (int64Val) implementsValue() {}
+func (intVal) implementsValue() {}
+func (floatVal) implementsValue() {}
+func (complexVal) implementsValue() {}
+
+// int64 bounds
+var (
+ minInt64 = big.NewInt(-1 << 63)
+ maxInt64 = big.NewInt(1<<63 - 1)
+)
+
+func normInt(x *big.Int) Value {
+ if minInt64.Cmp(x) <= 0 && x.Cmp(maxInt64) <= 0 {
+ return int64Val(x.Int64())
+ }
+ return intVal{x}
+}
+
+func normFloat(x *big.Rat) Value {
+ if x.IsInt() {
+ return normInt(x.Num())
+ }
+ return floatVal{x}
+}
+
+func normComplex(re, im *big.Rat) Value {
+ if im.Sign() == 0 {
+ return normFloat(re)
+ }
+ return complexVal{re, im}
+}
+
+// ----------------------------------------------------------------------------
+// Factories
+
+// MakeUnknown returns the Unknown value.
+func MakeUnknown() Value { return unknownVal{} }
+
+// MakeBool returns the Bool value for x.
+func MakeBool(b bool) Value { return boolVal(b) }
+
+// MakeString returns the String value for x.
+func MakeString(s string) Value { return stringVal(s) }
+
+// MakeInt64 returns the Int value for x.
+func MakeInt64(x int64) Value { return int64Val(x) }
+
+// MakeUint64 returns the Int value for x.
+func MakeUint64(x uint64) Value { return normInt(new(big.Int).SetUint64(x)) }
+
+// MakeFloat64 returns the numeric value for x.
+// If x is not finite, the result is unknown.
+func MakeFloat64(x float64) Value {
+ if f := new(big.Rat).SetFloat64(x); f != nil {
+ return normFloat(f)
+ }
+ return unknownVal{}
+}
+
+// MakeFromLiteral returns the corresponding integer, floating-point,
+// imaginary, character, or string value for a Go literal string.
+// If prec > 0, prec specifies an upper limit for the precision of
+// a numeric value. If the literal string is invalid, the result is
+// nil.
+// BUG(gri) Only prec == 0 is supported at the moment.
+func MakeFromLiteral(lit string, tok token.Token, prec uint) Value {
+ if prec != 0 {
+ panic("limited precision not supported")
+ }
+ switch tok {
+ case token.INT:
+ if x, err := strconv.ParseInt(lit, 0, 64); err == nil {
+ return int64Val(x)
+ }
+ if x, ok := new(big.Int).SetString(lit, 0); ok {
+ return intVal{x}
+ }
+
+ case token.FLOAT:
+ if x, ok := new(big.Rat).SetString(lit); ok {
+ return normFloat(x)
+ }
+
+ case token.IMAG:
+ if n := len(lit); n > 0 && lit[n-1] == 'i' {
+ if im, ok := new(big.Rat).SetString(lit[0 : n-1]); ok {
+ return normComplex(big.NewRat(0, 1), im)
+ }
+ }
+
+ case token.CHAR:
+ if n := len(lit); n >= 2 {
+ if code, _, _, err := strconv.UnquoteChar(lit[1:n-1], '\''); err == nil {
+ return int64Val(code)
+ }
+ }
+
+ case token.STRING:
+ if s, err := strconv.Unquote(lit); err == nil {
+ return stringVal(s)
+ }
+ }
+
+ return nil
+}
+
+// ----------------------------------------------------------------------------
+// Accessors
+//
+// For unknown arguments the result is the zero value for the respective
+// accessor type, except for Sign, where the result is 1.
+
+// BoolVal returns the Go boolean value of x, which must be a Bool or an Unknown.
+// If x is Unknown, the result is false.
+func BoolVal(x Value) bool {
+ switch x := x.(type) {
+ case boolVal:
+ return bool(x)
+ case unknownVal:
+ return false
+ }
+ panic(fmt.Sprintf("%v not a Bool", x))
+}
+
+// StringVal returns the Go string value of x, which must be a String or an Unknown.
+// If x is Unknown, the result is "".
+func StringVal(x Value) string {
+ switch x := x.(type) {
+ case stringVal:
+ return string(x)
+ case unknownVal:
+ return ""
+ }
+ panic(fmt.Sprintf("%v not a String", x))
+}
+
+// Int64Val returns the Go int64 value of x and whether the result is exact;
+// x must be an Int or an Unknown. If the result is not exact, its value is undefined.
+// If x is Unknown, the result is (0, false).
+func Int64Val(x Value) (int64, bool) {
+ switch x := x.(type) {
+ case int64Val:
+ return int64(x), true
+ case intVal:
+ return x.val.Int64(), x.val.BitLen() <= 63
+ case unknownVal:
+ return 0, false
+ }
+ panic(fmt.Sprintf("%v not an Int", x))
+}
+
+// Uint64Val returns the Go uint64 value of x and whether the result is exact;
+// x must be an Int or an Unknown. If the result is not exact, its value is undefined.
+// If x is Unknown, the result is (0, false).
+func Uint64Val(x Value) (uint64, bool) {
+ switch x := x.(type) {
+ case int64Val:
+ return uint64(x), x >= 0
+ case intVal:
+ return x.val.Uint64(), x.val.Sign() >= 0 && x.val.BitLen() <= 64
+ case unknownVal:
+ return 0, false
+ }
+ panic(fmt.Sprintf("%v not an Int", x))
+}
+
+// Float32Val is like Float64Val but for float32 instead of float64.
+func Float32Val(x Value) (float32, bool) {
+ switch x := x.(type) {
+ case int64Val:
+ f := float32(x)
+ return f, int64Val(f) == x
+ case intVal:
+ return ratToFloat32(new(big.Rat).SetFrac(x.val, int1))
+ case floatVal:
+ return ratToFloat32(x.val)
+ case unknownVal:
+ return 0, false
+ }
+ panic(fmt.Sprintf("%v not a Float", x))
+}
+
+// Float64Val returns the nearest Go float64 value of x and whether the result is exact;
+// x must be numeric but not Complex, or Unknown. For values too small (too close to 0)
+// to represent as float64, Float64Val silently underflows to 0. The result sign always
+// matches the sign of x, even for 0.
+// If x is Unknown, the result is (0, false).
+func Float64Val(x Value) (float64, bool) {
+ switch x := x.(type) {
+ case int64Val:
+ f := float64(int64(x))
+ return f, int64Val(f) == x
+ case intVal:
+ return new(big.Rat).SetFrac(x.val, int1).Float64()
+ case floatVal:
+ return x.val.Float64()
+ case unknownVal:
+ return 0, false
+ }
+ panic(fmt.Sprintf("%v not a Float", x))
+}
+
+// BitLen returns the number of bits required to represent
+// the absolute value x in binary representation; x must be an Int or an Unknown.
+// If x is Unknown, the result is 0.
+func BitLen(x Value) int {
+ switch x := x.(type) {
+ case int64Val:
+ return new(big.Int).SetInt64(int64(x)).BitLen()
+ case intVal:
+ return x.val.BitLen()
+ case unknownVal:
+ return 0
+ }
+ panic(fmt.Sprintf("%v not an Int", x))
+}
+
+// Sign returns -1, 0, or 1 depending on whether x < 0, x == 0, or x > 0;
+// x must be numeric or Unknown. For complex values x, the sign is 0 if x == 0,
+// otherwise it is != 0. If x is Unknown, the result is 1.
+func Sign(x Value) int {
+ switch x := x.(type) {
+ case int64Val:
+ switch {
+ case x < 0:
+ return -1
+ case x > 0:
+ return 1
+ }
+ return 0
+ case intVal:
+ return x.val.Sign()
+ case floatVal:
+ return x.val.Sign()
+ case complexVal:
+ return x.re.Sign() | x.im.Sign()
+ case unknownVal:
+ return 1 // avoid spurious division by zero errors
+ }
+ panic(fmt.Sprintf("%v not numeric", x))
+}
+
+// ----------------------------------------------------------------------------
+// Support for serializing/deserializing integers
+
+const (
+ // Compute the size of a Word in bytes.
+ _m = ^big.Word(0)
+ _log = _m>>8&1 + _m>>16&1 + _m>>32&1
+ wordSize = 1 << _log
+)
+
+// Bytes returns the bytes for the absolute value of x in little-
+// endian binary representation; x must be an Int.
+func Bytes(x Value) []byte {
+ var val *big.Int
+ switch x := x.(type) {
+ case int64Val:
+ val = new(big.Int).SetInt64(int64(x))
+ case intVal:
+ val = x.val
+ default:
+ panic(fmt.Sprintf("%v not an Int", x))
+ }
+
+ words := val.Bits()
+ bytes := make([]byte, len(words)*wordSize)
+
+ i := 0
+ for _, w := range words {
+ for j := 0; j < wordSize; j++ {
+ bytes[i] = byte(w)
+ w >>= 8
+ i++
+ }
+ }
+ // remove leading 0's
+ for i > 0 && bytes[i-1] == 0 {
+ i--
+ }
+
+ return bytes[:i]
+}
+
+// MakeFromBytes returns the Int value given the bytes of its little-endian
+// binary representation. An empty byte slice argument represents 0.
+func MakeFromBytes(bytes []byte) Value {
+ words := make([]big.Word, (len(bytes)+(wordSize-1))/wordSize)
+
+ i := 0
+ var w big.Word
+ var s uint
+ for _, b := range bytes {
+ w |= big.Word(b) << s
+ if s += 8; s == wordSize*8 {
+ words[i] = w
+ i++
+ w = 0
+ s = 0
+ }
+ }
+ // store last word
+ if i < len(words) {
+ words[i] = w
+ i++
+ }
+ // remove leading 0's
+ for i > 0 && words[i-1] == 0 {
+ i--
+ }
+
+ return normInt(new(big.Int).SetBits(words[:i]))
+}
+
+// ----------------------------------------------------------------------------
+// Support for disassembling fractions
+
+// Num returns the numerator of x; x must be Int, Float, or Unknown.
+// If x is Unknown, the result is Unknown, otherwise it is an Int
+// with the same sign as x.
+func Num(x Value) Value {
+ switch x := x.(type) {
+ case unknownVal, int64Val, intVal:
+ return x
+ case floatVal:
+ return normInt(x.val.Num())
+ }
+ panic(fmt.Sprintf("%v not Int or Float", x))
+}
+
+// Denom returns the denominator of x; x must be Int, Float, or Unknown.
+// If x is Unknown, the result is Unknown, otherwise it is an Int >= 1.
+func Denom(x Value) Value {
+ switch x := x.(type) {
+ case unknownVal:
+ return x
+ case int64Val, intVal:
+ return int64Val(1)
+ case floatVal:
+ return normInt(x.val.Denom())
+ }
+ panic(fmt.Sprintf("%v not Int or Float", x))
+}
+
+// ----------------------------------------------------------------------------
+// Support for assembling/disassembling complex numbers
+
+// MakeImag returns the numeric value x*i (possibly 0);
+// x must be Int, Float, or Unknown.
+// If x is Unknown, the result is Unknown.
+func MakeImag(x Value) Value {
+ var im *big.Rat
+ switch x := x.(type) {
+ case unknownVal:
+ return x
+ case int64Val:
+ im = big.NewRat(int64(x), 1)
+ case intVal:
+ im = new(big.Rat).SetFrac(x.val, int1)
+ case floatVal:
+ im = x.val
+ default:
+ panic(fmt.Sprintf("%v not Int or Float", x))
+ }
+ return normComplex(rat0, im)
+}
+
+// Real returns the real part of x, which must be a numeric or unknown value.
+// If x is Unknown, the result is Unknown.
+func Real(x Value) Value {
+ switch x := x.(type) {
+ case unknownVal, int64Val, intVal, floatVal:
+ return x
+ case complexVal:
+ return normFloat(x.re)
+ }
+ panic(fmt.Sprintf("%v not numeric", x))
+}
+
+// Imag returns the imaginary part of x, which must be a numeric or unknown value.
+// If x is Unknown, the result is Unknown.
+func Imag(x Value) Value {
+ switch x := x.(type) {
+ case unknownVal:
+ return x
+ case int64Val, intVal, floatVal:
+ return int64Val(0)
+ case complexVal:
+ return normFloat(x.im)
+ }
+ panic(fmt.Sprintf("%v not numeric", x))
+}
+
+// ----------------------------------------------------------------------------
+// Operations
+
+// is32bit reports whether x can be represented using 32 bits.
+func is32bit(x int64) bool {
+ const s = 32
+ return -1<<(s-1) <= x && x <= 1<<(s-1)-1
+}
+
+// is63bit reports whether x can be represented using 63 bits.
+func is63bit(x int64) bool {
+ const s = 63
+ return -1<<(s-1) <= x && x <= 1<<(s-1)-1
+}
+
+// UnaryOp returns the result of the unary expression op y.
+// The operation must be defined for the operand.
+// If prec > 0 it specifies the ^ (xor) result size in bits.
+// If y is Unknown, the result is Unknown.
+//
+func UnaryOp(op token.Token, y Value, prec uint) Value {
+ switch op {
+ case token.ADD:
+ switch y.(type) {
+ case unknownVal, int64Val, intVal, floatVal, complexVal:
+ return y
+ }
+
+ case token.SUB:
+ switch y := y.(type) {
+ case unknownVal:
+ return y
+ case int64Val:
+ if z := -y; z != y {
+ return z // no overflow
+ }
+ return normInt(new(big.Int).Neg(big.NewInt(int64(y))))
+ case intVal:
+ return normInt(new(big.Int).Neg(y.val))
+ case floatVal:
+ return normFloat(new(big.Rat).Neg(y.val))
+ case complexVal:
+ return normComplex(new(big.Rat).Neg(y.re), new(big.Rat).Neg(y.im))
+ }
+
+ case token.XOR:
+ var z big.Int
+ switch y := y.(type) {
+ case unknownVal:
+ return y
+ case int64Val:
+ z.Not(big.NewInt(int64(y)))
+ case intVal:
+ z.Not(y.val)
+ default:
+ goto Error
+ }
+ // For unsigned types, the result will be negative and
+ // thus "too large": We must limit the result precision
+ // to the type's precision.
+ if prec > 0 {
+ z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), prec)) // z &^= (-1)< ord(y) {
+ y, x = match(y, x)
+ return x, y
+ }
+ // ord(x) <= ord(y)
+
+ switch x := x.(type) {
+ case unknownVal:
+ return x, x
+
+ case boolVal, stringVal, complexVal:
+ return x, y
+
+ case int64Val:
+ switch y := y.(type) {
+ case int64Val:
+ return x, y
+ case intVal:
+ return intVal{big.NewInt(int64(x))}, y
+ case floatVal:
+ return floatVal{big.NewRat(int64(x), 1)}, y
+ case complexVal:
+ return complexVal{big.NewRat(int64(x), 1), rat0}, y
+ }
+
+ case intVal:
+ switch y := y.(type) {
+ case intVal:
+ return x, y
+ case floatVal:
+ return floatVal{new(big.Rat).SetFrac(x.val, int1)}, y
+ case complexVal:
+ return complexVal{new(big.Rat).SetFrac(x.val, int1), rat0}, y
+ }
+
+ case floatVal:
+ switch y := y.(type) {
+ case floatVal:
+ return x, y
+ case complexVal:
+ return complexVal{x.val, rat0}, y
+ }
+ }
+
+ panic("unreachable")
+}
+
+// BinaryOp returns the result of the binary expression x op y.
+// The operation must be defined for the operands. If one of the
+// operands is Unknown, the result is Unknown.
+// To force integer division of Int operands, use op == token.QUO_ASSIGN
+// instead of token.QUO; the result is guaranteed to be Int in this case.
+// Division by zero leads to a run-time panic.
+//
+func BinaryOp(x Value, op token.Token, y Value) Value {
+ x, y = match(x, y)
+
+ switch x := x.(type) {
+ case unknownVal:
+ return x
+
+ case boolVal:
+ y := y.(boolVal)
+ switch op {
+ case token.LAND:
+ return x && y
+ case token.LOR:
+ return x || y
+ }
+
+ case int64Val:
+ a := int64(x)
+ b := int64(y.(int64Val))
+ var c int64
+ switch op {
+ case token.ADD:
+ if !is63bit(a) || !is63bit(b) {
+ return normInt(new(big.Int).Add(big.NewInt(a), big.NewInt(b)))
+ }
+ c = a + b
+ case token.SUB:
+ if !is63bit(a) || !is63bit(b) {
+ return normInt(new(big.Int).Sub(big.NewInt(a), big.NewInt(b)))
+ }
+ c = a - b
+ case token.MUL:
+ if !is32bit(a) || !is32bit(b) {
+ return normInt(new(big.Int).Mul(big.NewInt(a), big.NewInt(b)))
+ }
+ c = a * b
+ case token.QUO:
+ return normFloat(new(big.Rat).SetFrac(big.NewInt(a), big.NewInt(b)))
+ case token.QUO_ASSIGN: // force integer division
+ c = a / b
+ case token.REM:
+ c = a % b
+ case token.AND:
+ c = a & b
+ case token.OR:
+ c = a | b
+ case token.XOR:
+ c = a ^ b
+ case token.AND_NOT:
+ c = a &^ b
+ default:
+ goto Error
+ }
+ return int64Val(c)
+
+ case intVal:
+ a := x.val
+ b := y.(intVal).val
+ var c big.Int
+ switch op {
+ case token.ADD:
+ c.Add(a, b)
+ case token.SUB:
+ c.Sub(a, b)
+ case token.MUL:
+ c.Mul(a, b)
+ case token.QUO:
+ return normFloat(new(big.Rat).SetFrac(a, b))
+ case token.QUO_ASSIGN: // force integer division
+ c.Quo(a, b)
+ case token.REM:
+ c.Rem(a, b)
+ case token.AND:
+ c.And(a, b)
+ case token.OR:
+ c.Or(a, b)
+ case token.XOR:
+ c.Xor(a, b)
+ case token.AND_NOT:
+ c.AndNot(a, b)
+ default:
+ goto Error
+ }
+ return normInt(&c)
+
+ case floatVal:
+ a := x.val
+ b := y.(floatVal).val
+ var c big.Rat
+ switch op {
+ case token.ADD:
+ c.Add(a, b)
+ case token.SUB:
+ c.Sub(a, b)
+ case token.MUL:
+ c.Mul(a, b)
+ case token.QUO:
+ c.Quo(a, b)
+ default:
+ goto Error
+ }
+ return normFloat(&c)
+
+ case complexVal:
+ y := y.(complexVal)
+ a, b := x.re, x.im
+ c, d := y.re, y.im
+ var re, im big.Rat
+ switch op {
+ case token.ADD:
+ // (a+c) + i(b+d)
+ re.Add(a, c)
+ im.Add(b, d)
+ case token.SUB:
+ // (a-c) + i(b-d)
+ re.Sub(a, c)
+ im.Sub(b, d)
+ case token.MUL:
+ // (ac-bd) + i(bc+ad)
+ var ac, bd, bc, ad big.Rat
+ ac.Mul(a, c)
+ bd.Mul(b, d)
+ bc.Mul(b, c)
+ ad.Mul(a, d)
+ re.Sub(&ac, &bd)
+ im.Add(&bc, &ad)
+ case token.QUO:
+ // (ac+bd)/s + i(bc-ad)/s, with s = cc + dd
+ var ac, bd, bc, ad, s, cc, dd big.Rat
+ ac.Mul(a, c)
+ bd.Mul(b, d)
+ bc.Mul(b, c)
+ ad.Mul(a, d)
+ cc.Mul(c, c)
+ dd.Mul(d, d)
+ s.Add(&cc, &dd)
+ re.Add(&ac, &bd)
+ re.Quo(&re, &s)
+ im.Sub(&bc, &ad)
+ im.Quo(&im, &s)
+ default:
+ goto Error
+ }
+ return normComplex(&re, &im)
+
+ case stringVal:
+ if op == token.ADD {
+ return x + y.(stringVal)
+ }
+ }
+
+Error:
+ panic(fmt.Sprintf("invalid binary operation %v %s %v", x, op, y))
+}
+
+// Shift returns the result of the shift expression x op s
+// with op == token.SHL or token.SHR (<< or >>). x must be
+// an Int or an Unknown. If x is Unknown, the result is x.
+//
+func Shift(x Value, op token.Token, s uint) Value {
+ switch x := x.(type) {
+ case unknownVal:
+ return x
+
+ case int64Val:
+ if s == 0 {
+ return x
+ }
+ switch op {
+ case token.SHL:
+ z := big.NewInt(int64(x))
+ return normInt(z.Lsh(z, s))
+ case token.SHR:
+ return x >> s
+ }
+
+ case intVal:
+ if s == 0 {
+ return x
+ }
+ var z big.Int
+ switch op {
+ case token.SHL:
+ return normInt(z.Lsh(x.val, s))
+ case token.SHR:
+ return normInt(z.Rsh(x.val, s))
+ }
+ }
+
+ panic(fmt.Sprintf("invalid shift %v %s %d", x, op, s))
+}
+
+func cmpZero(x int, op token.Token) bool {
+ switch op {
+ case token.EQL:
+ return x == 0
+ case token.NEQ:
+ return x != 0
+ case token.LSS:
+ return x < 0
+ case token.LEQ:
+ return x <= 0
+ case token.GTR:
+ return x > 0
+ case token.GEQ:
+ return x >= 0
+ }
+ panic("unreachable")
+}
+
+// Compare returns the result of the comparison x op y.
+// The comparison must be defined for the operands.
+// If one of the operands is Unknown, the result is
+// false.
+//
+func Compare(x Value, op token.Token, y Value) bool {
+ x, y = match(x, y)
+
+ switch x := x.(type) {
+ case unknownVal:
+ return false
+
+ case boolVal:
+ y := y.(boolVal)
+ switch op {
+ case token.EQL:
+ return x == y
+ case token.NEQ:
+ return x != y
+ }
+
+ case int64Val:
+ y := y.(int64Val)
+ switch op {
+ case token.EQL:
+ return x == y
+ case token.NEQ:
+ return x != y
+ case token.LSS:
+ return x < y
+ case token.LEQ:
+ return x <= y
+ case token.GTR:
+ return x > y
+ case token.GEQ:
+ return x >= y
+ }
+
+ case intVal:
+ return cmpZero(x.val.Cmp(y.(intVal).val), op)
+
+ case floatVal:
+ return cmpZero(x.val.Cmp(y.(floatVal).val), op)
+
+ case complexVal:
+ y := y.(complexVal)
+ re := x.re.Cmp(y.re)
+ im := x.im.Cmp(y.im)
+ switch op {
+ case token.EQL:
+ return re == 0 && im == 0
+ case token.NEQ:
+ return re != 0 || im != 0
+ }
+
+ case stringVal:
+ y := y.(stringVal)
+ switch op {
+ case token.EQL:
+ return x == y
+ case token.NEQ:
+ return x != y
+ case token.LSS:
+ return x < y
+ case token.LEQ:
+ return x <= y
+ case token.GTR:
+ return x > y
+ case token.GEQ:
+ return x >= y
+ }
+ }
+
+ panic(fmt.Sprintf("invalid comparison %v %s %v", x, op, y))
+}
diff --git a/src/go/constants/value_test.go b/src/go/constants/value_test.go
new file mode 100644
index 0000000000..6a74e2d13c
--- /dev/null
+++ b/src/go/constants/value_test.go
@@ -0,0 +1,375 @@
+// 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 constants
+
+import (
+ "go/token"
+ "strings"
+ "testing"
+)
+
+// TODO(gri) expand this test framework
+
+var opTests = []string{
+ // unary operations
+ `+ 0 = 0`,
+ `+ ? = ?`,
+ `- 1 = -1`,
+ `- ? = ?`,
+ `^ 0 = -1`,
+ `^ ? = ?`,
+
+ `! true = false`,
+ `! false = true`,
+ `! ? = ?`,
+
+ // etc.
+
+ // binary operations
+ `"" + "" = ""`,
+ `"foo" + "" = "foo"`,
+ `"" + "bar" = "bar"`,
+ `"foo" + "bar" = "foobar"`,
+
+ `0 + 0 = 0`,
+ `0 + 0.1 = 0.1`,
+ `0 + 0.1i = 0.1i`,
+ `0.1 + 0.9 = 1`,
+ `1e100 + 1e100 = 2e100`,
+ `? + 0 = ?`,
+ `0 + ? = ?`,
+
+ `0 - 0 = 0`,
+ `0 - 0.1 = -0.1`,
+ `0 - 0.1i = -0.1i`,
+ `1e100 - 1e100 = 0`,
+ `? - 0 = ?`,
+ `0 - ? = ?`,
+
+ `0 * 0 = 0`,
+ `1 * 0.1 = 0.1`,
+ `1 * 0.1i = 0.1i`,
+ `1i * 1i = -1`,
+ `? * 0 = ?`,
+ `0 * ? = ?`,
+
+ `0 / 0 = "division_by_zero"`,
+ `10 / 2 = 5`,
+ `5 / 3 = 5/3`,
+ `5i / 3i = 5/3`,
+ `? / 0 = ?`,
+ `0 / ? = ?`,
+
+ `0 % 0 = "runtime_error:_integer_divide_by_zero"`, // TODO(gri) should be the same as for /
+ `10 % 3 = 1`,
+ `? % 0 = ?`,
+ `0 % ? = ?`,
+
+ `0 & 0 = 0`,
+ `12345 & 0 = 0`,
+ `0xff & 0xf = 0xf`,
+ `? & 0 = ?`,
+ `0 & ? = ?`,
+
+ `0 | 0 = 0`,
+ `12345 | 0 = 12345`,
+ `0xb | 0xa0 = 0xab`,
+ `? | 0 = ?`,
+ `0 | ? = ?`,
+
+ `0 ^ 0 = 0`,
+ `1 ^ -1 = -2`,
+ `? ^ 0 = ?`,
+ `0 ^ ? = ?`,
+
+ `0 &^ 0 = 0`,
+ `0xf &^ 1 = 0xe`,
+ `1 &^ 0xf = 0`,
+ // etc.
+
+ // shifts
+ `0 << 0 = 0`,
+ `1 << 10 = 1024`,
+ `0 >> 0 = 0`,
+ `1024 >> 10 == 1`,
+ `? << 0 == ?`,
+ `? >> 10 == ?`,
+ // etc.
+
+ // comparisons
+ `false == false = true`,
+ `false == true = false`,
+ `true == false = false`,
+ `true == true = true`,
+
+ `false != false = false`,
+ `false != true = true`,
+ `true != false = true`,
+ `true != true = false`,
+
+ `"foo" == "bar" = false`,
+ `"foo" != "bar" = true`,
+ `"foo" < "bar" = false`,
+ `"foo" <= "bar" = false`,
+ `"foo" > "bar" = true`,
+ `"foo" >= "bar" = true`,
+
+ `0 == 0 = true`,
+ `0 != 0 = false`,
+ `0 < 10 = true`,
+ `10 <= 10 = true`,
+ `0 > 10 = false`,
+ `10 >= 10 = true`,
+
+ `1/123456789 == 1/123456789 == true`,
+ `1/123456789 != 1/123456789 == false`,
+ `1/123456789 < 1/123456788 == true`,
+ `1/123456788 <= 1/123456789 == false`,
+ `0.11 > 0.11 = false`,
+ `0.11 >= 0.11 = true`,
+
+ `? == 0 = false`,
+ `? != 0 = false`,
+ `? < 10 = false`,
+ `? <= 10 = false`,
+ `? > 10 = false`,
+ `? >= 10 = false`,
+
+ `0 == ? = false`,
+ `0 != ? = false`,
+ `0 < ? = false`,
+ `10 <= ? = false`,
+ `0 > ? = false`,
+ `10 >= ? = false`,
+
+ // etc.
+}
+
+func TestOps(t *testing.T) {
+ for _, test := range opTests {
+ a := strings.Split(test, " ")
+ i := 0 // operator index
+
+ var x, x0 Value
+ switch len(a) {
+ case 4:
+ // unary operation
+ case 5:
+ // binary operation
+ x, x0 = val(a[0]), val(a[0])
+ i = 1
+ default:
+ t.Errorf("invalid test case: %s", test)
+ continue
+ }
+
+ op, ok := optab[a[i]]
+ if !ok {
+ panic("missing optab entry for " + a[i])
+ }
+
+ y, y0 := val(a[i+1]), val(a[i+1])
+
+ got := doOp(x, op, y)
+ want := val(a[i+3])
+ if !eql(got, want) {
+ t.Errorf("%s: got %s; want %s", test, got, want)
+ }
+ if x0 != nil && !eql(x, x0) {
+ t.Errorf("%s: x changed to %s", test, x)
+ }
+ if !eql(y, y0) {
+ t.Errorf("%s: y changed to %s", test, y)
+ }
+ }
+}
+
+func eql(x, y Value) bool {
+ _, ux := x.(unknownVal)
+ _, uy := y.(unknownVal)
+ if ux || uy {
+ return ux == uy
+ }
+ return Compare(x, token.EQL, y)
+}
+
+// ----------------------------------------------------------------------------
+// Support functions
+
+func val(lit string) Value {
+ if len(lit) == 0 {
+ return MakeUnknown()
+ }
+
+ switch lit {
+ case "?":
+ return MakeUnknown()
+ case "true":
+ return MakeBool(true)
+ case "false":
+ return MakeBool(false)
+ }
+
+ tok := token.INT
+ switch first, last := lit[0], lit[len(lit)-1]; {
+ case first == '"' || first == '`':
+ tok = token.STRING
+ lit = strings.Replace(lit, "_", " ", -1)
+ case first == '\'':
+ tok = token.CHAR
+ case last == 'i':
+ tok = token.IMAG
+ default:
+ if !strings.HasPrefix(lit, "0x") && strings.ContainsAny(lit, "./Ee") {
+ tok = token.FLOAT
+ }
+ }
+
+ return MakeFromLiteral(lit, tok, 0)
+}
+
+var optab = map[string]token.Token{
+ "!": token.NOT,
+
+ "+": token.ADD,
+ "-": token.SUB,
+ "*": token.MUL,
+ "/": token.QUO,
+ "%": token.REM,
+
+ "<<": token.SHL,
+ ">>": token.SHR,
+
+ "&": token.AND,
+ "|": token.OR,
+ "^": token.XOR,
+ "&^": token.AND_NOT,
+
+ "==": token.EQL,
+ "!=": token.NEQ,
+ "<": token.LSS,
+ "<=": token.LEQ,
+ ">": token.GTR,
+ ">=": token.GEQ,
+}
+
+func panicHandler(v *Value) {
+ switch p := recover().(type) {
+ case nil:
+ // nothing to do
+ case string:
+ *v = MakeString(p)
+ case error:
+ *v = MakeString(p.Error())
+ default:
+ panic(p)
+ }
+}
+
+func doOp(x Value, op token.Token, y Value) (z Value) {
+ defer panicHandler(&z)
+
+ if x == nil {
+ return UnaryOp(op, y, 0)
+ }
+
+ switch op {
+ case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ:
+ return MakeBool(Compare(x, op, y))
+ case token.SHL, token.SHR:
+ s, _ := Int64Val(y)
+ return Shift(x, op, uint(s))
+ default:
+ return BinaryOp(x, op, y)
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Other tests
+
+var fracTests = []string{
+ "0 0 1",
+ "1 1 1",
+ "-1 -1 1",
+ "1.2 6 5",
+ "-0.991 -991 1000",
+ "1e100 1e100 1",
+}
+
+func TestFractions(t *testing.T) {
+ for _, test := range fracTests {
+ a := strings.Split(test, " ")
+ if len(a) != 3 {
+ t.Errorf("invalid test case: %s", test)
+ continue
+ }
+
+ x := val(a[0])
+ n := val(a[1])
+ d := val(a[2])
+
+ if got := Num(x); !eql(got, n) {
+ t.Errorf("%s: got num = %s; want %s", test, got, n)
+ }
+
+ if got := Denom(x); !eql(got, d) {
+ t.Errorf("%s: got denom = %s; want %s", test, got, d)
+ }
+ }
+}
+
+var bytesTests = []string{
+ "0",
+ "1",
+ "123456789",
+ "123456789012345678901234567890123456789012345678901234567890",
+}
+
+func TestBytes(t *testing.T) {
+ for _, test := range bytesTests {
+ x := val(test)
+ bytes := Bytes(x)
+
+ // special case 0
+ if Sign(x) == 0 && len(bytes) != 0 {
+ t.Errorf("%s: got %v; want empty byte slice", test, bytes)
+ }
+
+ if n := len(bytes); n > 0 && bytes[n-1] == 0 {
+ t.Errorf("%s: got %v; want no leading 0 byte", test, bytes)
+ }
+
+ if got := MakeFromBytes(bytes); !eql(got, x) {
+ t.Errorf("%s: got %s; want %s (bytes = %v)", test, got, x, bytes)
+ }
+ }
+}
+
+func TestUnknown(t *testing.T) {
+ u := MakeUnknown()
+ var values = []Value{
+ u,
+ MakeBool(false), // token.ADD ok below, operation is never considered
+ MakeString(""),
+ MakeInt64(1),
+ MakeFromLiteral("-1234567890123456789012345678901234567890", token.INT, 0),
+ MakeFloat64(1.2),
+ MakeImag(MakeFloat64(1.2)),
+ }
+ for _, val := range values {
+ x, y := val, u
+ for i := range [2]int{} {
+ if i == 1 {
+ x, y = y, x
+ }
+ if got := BinaryOp(x, token.ADD, y); got.Kind() != Unknown {
+ t.Errorf("%s + %s: got %s; want %s", x, y, got, u)
+ }
+ if got := Compare(x, token.EQL, y); got {
+ t.Errorf("%s == %s: got true; want false", x, y)
+ }
+ }
+ }
+}
diff --git a/src/go/importer/importer.go b/src/go/importer/importer.go
new file mode 100644
index 0000000000..1ac44c7302
--- /dev/null
+++ b/src/go/importer/importer.go
@@ -0,0 +1,46 @@
+// Copyright 2015 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 importer provides access to export data importers.
+package importer
+
+import (
+ "go/internal/gcimporter"
+ "go/types"
+ "io"
+ "runtime"
+)
+
+// A Lookup function returns a reader to access package data for
+// a given import path, or an error if no matching package is found.
+type Lookup func(path string) (io.ReadCloser, error)
+
+// For returns an Importer for the given compiler and lookup interface,
+// or nil. Supported compilers are "gc", and "gccgo". If lookup is nil,
+// the default package lookup mechanism for the given compiler is used.
+func For(compiler string, lookup Lookup) types.Importer {
+ switch compiler {
+ case "gc":
+ if lookup == nil {
+ return make(gcimports)
+ }
+ panic("gc importer for custom import path lookup not yet implemented")
+ case "gccgo":
+ // TODO(gri) We have the code. Plug it in.
+ panic("gccgo importer unimplemented")
+ }
+ // compiler not supported
+ return nil
+}
+
+// Default returns an Importer for the compiler that built the running binary.
+func Default() types.Importer {
+ return For(runtime.Compiler, nil)
+}
+
+type gcimports map[string]*types.Package
+
+func (m gcimports) Import(path string) (*types.Package, error) {
+ return gcimporter.Import(m, path)
+}
diff --git a/src/go/internal/gcimporter/exportdata.go b/src/go/internal/gcimporter/exportdata.go
new file mode 100644
index 0000000000..657742bb6d
--- /dev/null
+++ b/src/go/internal/gcimporter/exportdata.go
@@ -0,0 +1,108 @@
+// 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 FindExportData.
+
+package gcimporter
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ "io"
+ "strconv"
+ "strings"
+)
+
+func readGopackHeader(r *bufio.Reader) (name string, size int, err error) {
+ // See $GOROOT/include/ar.h.
+ hdr := make([]byte, 16+12+6+6+8+10+2)
+ _, err = io.ReadFull(r, hdr)
+ if err != nil {
+ return
+ }
+ // leave for debugging
+ if false {
+ fmt.Printf("header: %s", hdr)
+ }
+ s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10]))
+ size, err = strconv.Atoi(s)
+ if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' {
+ err = errors.New("invalid archive header")
+ return
+ }
+ name = strings.TrimSpace(string(hdr[:16]))
+ return
+}
+
+// FindExportData positions the reader r at the beginning of the
+// export data section of an underlying GC-created object/archive
+// file by reading from it. The reader must be positioned at the
+// start of the file before calling this function.
+//
+func FindExportData(r *bufio.Reader) (err error) {
+ // Read first line to make sure this is an object file.
+ line, err := r.ReadSlice('\n')
+ if err != nil {
+ return
+ }
+ if string(line) == "!\n" {
+ // Archive file. Scan to __.PKGDEF.
+ var name string
+ var size int
+ if name, size, err = readGopackHeader(r); err != nil {
+ return
+ }
+
+ // Optional leading __.GOSYMDEF or __.SYMDEF.
+ // Read and discard.
+ if name == "__.SYMDEF" || name == "__.GOSYMDEF" {
+ const block = 4096
+ tmp := make([]byte, block)
+ for size > 0 {
+ n := size
+ if n > block {
+ n = block
+ }
+ if _, err = io.ReadFull(r, tmp[:n]); err != nil {
+ return
+ }
+ size -= n
+ }
+
+ if name, size, err = readGopackHeader(r); err != nil {
+ return
+ }
+ }
+
+ // First real entry should be __.PKGDEF.
+ if name != "__.PKGDEF" {
+ err = errors.New("go archive is missing __.PKGDEF")
+ return
+ }
+
+ // Read first line of __.PKGDEF data, so that line
+ // is once again the first line of the input.
+ if line, err = r.ReadSlice('\n'); err != nil {
+ return
+ }
+ }
+
+ // Now at __.PKGDEF in archive or still at beginning of file.
+ // Either way, line should begin with "go object ".
+ if !strings.HasPrefix(string(line), "go object ") {
+ err = errors.New("not a go object file")
+ return
+ }
+
+ // Skip over object header to export data.
+ // Begins after first line with $$.
+ for line[0] != '$' {
+ if line, err = r.ReadSlice('\n'); err != nil {
+ return
+ }
+ }
+
+ return
+}
diff --git a/src/go/internal/gcimporter/gcimporter.go b/src/go/internal/gcimporter/gcimporter.go
new file mode 100644
index 0000000000..ee83a725fa
--- /dev/null
+++ b/src/go/internal/gcimporter/gcimporter.go
@@ -0,0 +1,957 @@
+// 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 gcimporter implements Import for gc-generated object files.
+package gcimporter // import "go/internal/gcimporter"
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ "go/build"
+ "go/token"
+ "io"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "text/scanner"
+
+ exact "go/constants"
+ "go/types"
+)
+
+// debugging/development support
+const debug = false
+
+var pkgExts = [...]string{".a", ".5", ".6", ".7", ".8", ".9"}
+
+// FindPkg returns the filename and unique package id for an import
+// path based on package information provided by build.Import (using
+// the build.Default build.Context).
+// If no file was found, an empty filename is returned.
+//
+func FindPkg(path, srcDir string) (filename, id string) {
+ if len(path) == 0 {
+ return
+ }
+
+ id = path
+ var noext string
+ switch {
+ default:
+ // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x"
+ // Don't require the source files to be present.
+ bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary)
+ if bp.PkgObj == "" {
+ return
+ }
+ noext = strings.TrimSuffix(bp.PkgObj, ".a")
+
+ case build.IsLocalImport(path):
+ // "./x" -> "/this/directory/x.ext", "/this/directory/x"
+ noext = filepath.Join(srcDir, path)
+ id = noext
+
+ case filepath.IsAbs(path):
+ // for completeness only - go/build.Import
+ // does not support absolute imports
+ // "/x" -> "/x.ext", "/x"
+ noext = path
+ }
+
+ // try extensions
+ for _, ext := range pkgExts {
+ filename = noext + ext
+ if f, err := os.Stat(filename); err == nil && !f.IsDir() {
+ return
+ }
+ }
+
+ filename = "" // not found
+ return
+}
+
+// ImportData imports a package by reading the gc-generated export data,
+// adds the corresponding package object to the imports map indexed by id,
+// and returns the object.
+//
+// The imports map must contains all packages already imported. The data
+// reader position must be the beginning of the export data section. The
+// filename is only used in error messages.
+//
+// If imports[id] contains the completely imported package, that package
+// can be used directly, and there is no need to call this function (but
+// there is also no harm but for extra time used).
+//
+func ImportData(imports map[string]*types.Package, filename, id string, data io.Reader) (pkg *types.Package, err error) {
+ // support for parser error handling
+ defer func() {
+ switch r := recover().(type) {
+ case nil:
+ // nothing to do
+ case importError:
+ err = r
+ default:
+ panic(r) // internal error
+ }
+ }()
+
+ var p parser
+ p.init(filename, id, data, imports)
+ pkg = p.parseExport()
+
+ return
+}
+
+// Import imports a gc-generated package given its import path, adds the
+// corresponding package object to the imports map, and returns the object.
+// Local import paths are interpreted relative to the current working directory.
+// The imports map must contains all packages already imported.
+//
+func Import(imports map[string]*types.Package, path string) (pkg *types.Package, err error) {
+ // package "unsafe" is handled by the type checker
+ if path == "unsafe" {
+ panic(`gcimporter.Import called for package "unsafe"`)
+ }
+
+ srcDir := "."
+ if build.IsLocalImport(path) {
+ srcDir, err = os.Getwd()
+ if err != nil {
+ return
+ }
+ }
+
+ filename, id := FindPkg(path, srcDir)
+ if filename == "" {
+ err = fmt.Errorf("can't find import: %s", id)
+ return
+ }
+
+ // no need to re-import if the package was imported completely before
+ if pkg = imports[id]; pkg != nil && pkg.Complete() {
+ return
+ }
+
+ // open file
+ f, err := os.Open(filename)
+ if err != nil {
+ return
+ }
+ defer func() {
+ f.Close()
+ if err != nil {
+ // add file name to error
+ err = fmt.Errorf("reading export data: %s: %v", filename, err)
+ }
+ }()
+
+ buf := bufio.NewReader(f)
+ if err = FindExportData(buf); err != nil {
+ return
+ }
+
+ pkg, err = ImportData(imports, filename, id, buf)
+
+ return
+}
+
+// ----------------------------------------------------------------------------
+// Parser
+
+// TODO(gri) Imported objects don't have position information.
+// Ideally use the debug table line info; alternatively
+// create some fake position (or the position of the
+// import). That way error messages referring to imported
+// objects can print meaningful information.
+
+// parser parses the exports inside a gc compiler-produced
+// object/archive file and populates its scope with the results.
+type parser struct {
+ scanner scanner.Scanner
+ tok rune // current token
+ lit string // literal string; only valid for Ident, Int, String tokens
+ id string // package id of imported package
+ imports map[string]*types.Package // package id -> package object
+}
+
+func (p *parser) init(filename, id string, src io.Reader, imports map[string]*types.Package) {
+ p.scanner.Init(src)
+ p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) }
+ p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanChars | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments
+ p.scanner.Whitespace = 1<<'\t' | 1<<' '
+ p.scanner.Filename = filename // for good error messages
+ p.next()
+ p.id = id
+ p.imports = imports
+ if debug {
+ // check consistency of imports map
+ for _, pkg := range imports {
+ if pkg.Name() == "" {
+ fmt.Printf("no package name for %s\n", pkg.Path())
+ }
+ }
+ }
+}
+
+func (p *parser) next() {
+ p.tok = p.scanner.Scan()
+ switch p.tok {
+ case scanner.Ident, scanner.Int, scanner.Char, scanner.String, '·':
+ p.lit = p.scanner.TokenText()
+ default:
+ p.lit = ""
+ }
+ if debug {
+ fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit)
+ }
+}
+
+func declTypeName(pkg *types.Package, name string) *types.TypeName {
+ scope := pkg.Scope()
+ if obj := scope.Lookup(name); obj != nil {
+ return obj.(*types.TypeName)
+ }
+ obj := types.NewTypeName(token.NoPos, pkg, name, nil)
+ // a named type may be referred to before the underlying type
+ // is known - set it up
+ types.NewNamed(obj, nil, nil)
+ scope.Insert(obj)
+ return obj
+}
+
+// ----------------------------------------------------------------------------
+// Error handling
+
+// Internal errors are boxed as importErrors.
+type importError struct {
+ pos scanner.Position
+ err error
+}
+
+func (e importError) Error() string {
+ return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err)
+}
+
+func (p *parser) error(err interface{}) {
+ if s, ok := err.(string); ok {
+ err = errors.New(s)
+ }
+ // panic with a runtime.Error if err is not an error
+ panic(importError{p.scanner.Pos(), err.(error)})
+}
+
+func (p *parser) errorf(format string, args ...interface{}) {
+ p.error(fmt.Sprintf(format, args...))
+}
+
+func (p *parser) expect(tok rune) string {
+ lit := p.lit
+ if p.tok != tok {
+ p.errorf("expected %s, got %s (%s)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit)
+ }
+ p.next()
+ return lit
+}
+
+func (p *parser) expectSpecial(tok string) {
+ sep := 'x' // not white space
+ i := 0
+ for i < len(tok) && p.tok == rune(tok[i]) && sep > ' ' {
+ sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token
+ p.next()
+ i++
+ }
+ if i < len(tok) {
+ p.errorf("expected %q, got %q", tok, tok[0:i])
+ }
+}
+
+func (p *parser) expectKeyword(keyword string) {
+ lit := p.expect(scanner.Ident)
+ if lit != keyword {
+ p.errorf("expected keyword %s, got %q", keyword, lit)
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Qualified and unqualified names
+
+// PackageId = string_lit .
+//
+func (p *parser) parsePackageId() string {
+ id, err := strconv.Unquote(p.expect(scanner.String))
+ if err != nil {
+ p.error(err)
+ }
+ // id == "" stands for the imported package id
+ // (only known at time of package installation)
+ if id == "" {
+ id = p.id
+ }
+ return id
+}
+
+// PackageName = ident .
+//
+func (p *parser) parsePackageName() string {
+ return p.expect(scanner.Ident)
+}
+
+// dotIdentifier = ( ident | '·' ) { ident | int | '·' } .
+func (p *parser) parseDotIdent() string {
+ ident := ""
+ if p.tok != scanner.Int {
+ sep := 'x' // not white space
+ for (p.tok == scanner.Ident || p.tok == scanner.Int || p.tok == '·') && sep > ' ' {
+ ident += p.lit
+ sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token
+ p.next()
+ }
+ }
+ if ident == "" {
+ p.expect(scanner.Ident) // use expect() for error handling
+ }
+ return ident
+}
+
+// QualifiedName = "@" PackageId "." ( "?" | dotIdentifier ) .
+//
+func (p *parser) parseQualifiedName() (id, name string) {
+ p.expect('@')
+ id = p.parsePackageId()
+ p.expect('.')
+ // Per rev f280b8a485fd (10/2/2013), qualified names may be used for anonymous fields.
+ if p.tok == '?' {
+ p.next()
+ } else {
+ name = p.parseDotIdent()
+ }
+ return
+}
+
+// getPkg returns the package for a given id. If the package is
+// not found but we have a package name, create the package and
+// add it to the p.imports map.
+//
+func (p *parser) getPkg(id, name string) *types.Package {
+ // package unsafe is not in the imports map - handle explicitly
+ if id == "unsafe" {
+ return types.Unsafe
+ }
+ pkg := p.imports[id]
+ if pkg == nil && name != "" {
+ pkg = types.NewPackage(id, name)
+ p.imports[id] = pkg
+ }
+ return pkg
+}
+
+// parseExportedName is like parseQualifiedName, but
+// the package id is resolved to an imported *types.Package.
+//
+func (p *parser) parseExportedName() (pkg *types.Package, name string) {
+ id, name := p.parseQualifiedName()
+ pkg = p.getPkg(id, "")
+ if pkg == nil {
+ p.errorf("%s package not found", id)
+ }
+ return
+}
+
+// ----------------------------------------------------------------------------
+// Types
+
+// BasicType = identifier .
+//
+func (p *parser) parseBasicType() types.Type {
+ id := p.expect(scanner.Ident)
+ obj := types.Universe.Lookup(id)
+ if obj, ok := obj.(*types.TypeName); ok {
+ return obj.Type()
+ }
+ p.errorf("not a basic type: %s", id)
+ return nil
+}
+
+// ArrayType = "[" int_lit "]" Type .
+//
+func (p *parser) parseArrayType() types.Type {
+ // "[" already consumed and lookahead known not to be "]"
+ lit := p.expect(scanner.Int)
+ p.expect(']')
+ elem := p.parseType()
+ n, err := strconv.ParseInt(lit, 10, 64)
+ if err != nil {
+ p.error(err)
+ }
+ return types.NewArray(elem, n)
+}
+
+// MapType = "map" "[" Type "]" Type .
+//
+func (p *parser) parseMapType() types.Type {
+ p.expectKeyword("map")
+ p.expect('[')
+ key := p.parseType()
+ p.expect(']')
+ elem := p.parseType()
+ return types.NewMap(key, elem)
+}
+
+// Name = identifier | "?" | QualifiedName .
+//
+// If materializePkg is set, the returned package is guaranteed to be set.
+// For fully qualified names, the returned package may be a fake package
+// (without name, scope, and not in the p.imports map), created for the
+// sole purpose of providing a package path. Fake packages are created
+// when the package id is not found in the p.imports map; in that case
+// we cannot create a real package because we don't have a package name.
+// For non-qualified names, the returned package is the imported package.
+//
+func (p *parser) parseName(materializePkg bool) (pkg *types.Package, name string) {
+ switch p.tok {
+ case scanner.Ident:
+ pkg = p.imports[p.id]
+ name = p.lit
+ p.next()
+ case '?':
+ // anonymous
+ pkg = p.imports[p.id]
+ p.next()
+ case '@':
+ // exported name prefixed with package path
+ var id string
+ id, name = p.parseQualifiedName()
+ if materializePkg {
+ // we don't have a package name - if the package
+ // doesn't exist yet, create a fake package instead
+ pkg = p.getPkg(id, "")
+ if pkg == nil {
+ pkg = types.NewPackage(id, "")
+ }
+ }
+ default:
+ p.error("name expected")
+ }
+ return
+}
+
+func deref(typ types.Type) types.Type {
+ if p, _ := typ.(*types.Pointer); p != nil {
+ return p.Elem()
+ }
+ return typ
+}
+
+// Field = Name Type [ string_lit ] .
+//
+func (p *parser) parseField() (*types.Var, string) {
+ pkg, name := p.parseName(true)
+ typ := p.parseType()
+ anonymous := false
+ if name == "" {
+ // anonymous field - typ must be T or *T and T must be a type name
+ switch typ := deref(typ).(type) {
+ case *types.Basic: // basic types are named types
+ pkg = nil
+ name = typ.Name()
+ case *types.Named:
+ name = typ.Obj().Name()
+ default:
+ p.errorf("anonymous field expected")
+ }
+ anonymous = true
+ }
+ tag := ""
+ if p.tok == scanner.String {
+ s := p.expect(scanner.String)
+ var err error
+ tag, err = strconv.Unquote(s)
+ if err != nil {
+ p.errorf("invalid struct tag %s: %s", s, err)
+ }
+ }
+ return types.NewField(token.NoPos, pkg, name, typ, anonymous), tag
+}
+
+// StructType = "struct" "{" [ FieldList ] "}" .
+// FieldList = Field { ";" Field } .
+//
+func (p *parser) parseStructType() types.Type {
+ var fields []*types.Var
+ var tags []string
+
+ p.expectKeyword("struct")
+ p.expect('{')
+ for i := 0; p.tok != '}' && p.tok != scanner.EOF; i++ {
+ if i > 0 {
+ p.expect(';')
+ }
+ fld, tag := p.parseField()
+ if tag != "" && tags == nil {
+ tags = make([]string, i)
+ }
+ if tags != nil {
+ tags = append(tags, tag)
+ }
+ fields = append(fields, fld)
+ }
+ p.expect('}')
+
+ return types.NewStruct(fields, tags)
+}
+
+// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] .
+//
+func (p *parser) parseParameter() (par *types.Var, isVariadic bool) {
+ _, name := p.parseName(false)
+ // remove gc-specific parameter numbering
+ if i := strings.Index(name, "·"); i >= 0 {
+ name = name[:i]
+ }
+ if p.tok == '.' {
+ p.expectSpecial("...")
+ isVariadic = true
+ }
+ typ := p.parseType()
+ if isVariadic {
+ typ = types.NewSlice(typ)
+ }
+ // ignore argument tag (e.g. "noescape")
+ if p.tok == scanner.String {
+ p.next()
+ }
+ // TODO(gri) should we provide a package?
+ par = types.NewVar(token.NoPos, nil, name, typ)
+ return
+}
+
+// Parameters = "(" [ ParameterList ] ")" .
+// ParameterList = { Parameter "," } Parameter .
+//
+func (p *parser) parseParameters() (list []*types.Var, isVariadic bool) {
+ p.expect('(')
+ for p.tok != ')' && p.tok != scanner.EOF {
+ if len(list) > 0 {
+ p.expect(',')
+ }
+ par, variadic := p.parseParameter()
+ list = append(list, par)
+ if variadic {
+ if isVariadic {
+ p.error("... not on final argument")
+ }
+ isVariadic = true
+ }
+ }
+ p.expect(')')
+
+ return
+}
+
+// Signature = Parameters [ Result ] .
+// Result = Type | Parameters .
+//
+func (p *parser) parseSignature(recv *types.Var) *types.Signature {
+ params, isVariadic := p.parseParameters()
+
+ // optional result type
+ var results []*types.Var
+ if p.tok == '(' {
+ var variadic bool
+ results, variadic = p.parseParameters()
+ if variadic {
+ p.error("... not permitted on result type")
+ }
+ }
+
+ return types.NewSignature(nil, recv, types.NewTuple(params...), types.NewTuple(results...), isVariadic)
+}
+
+// InterfaceType = "interface" "{" [ MethodList ] "}" .
+// MethodList = Method { ";" Method } .
+// Method = Name Signature .
+//
+// The methods of embedded interfaces are always "inlined"
+// by the compiler and thus embedded interfaces are never
+// visible in the export data.
+//
+func (p *parser) parseInterfaceType() types.Type {
+ var methods []*types.Func
+
+ p.expectKeyword("interface")
+ p.expect('{')
+ for i := 0; p.tok != '}' && p.tok != scanner.EOF; i++ {
+ if i > 0 {
+ p.expect(';')
+ }
+ pkg, name := p.parseName(true)
+ sig := p.parseSignature(nil)
+ methods = append(methods, types.NewFunc(token.NoPos, pkg, name, sig))
+ }
+ p.expect('}')
+
+ // Complete requires the type's embedded interfaces to be fully defined,
+ // but we do not define any
+ return types.NewInterface(methods, nil).Complete()
+}
+
+// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type .
+//
+func (p *parser) parseChanType() types.Type {
+ dir := types.SendRecv
+ if p.tok == scanner.Ident {
+ p.expectKeyword("chan")
+ if p.tok == '<' {
+ p.expectSpecial("<-")
+ dir = types.SendOnly
+ }
+ } else {
+ p.expectSpecial("<-")
+ p.expectKeyword("chan")
+ dir = types.RecvOnly
+ }
+ elem := p.parseType()
+ return types.NewChan(dir, elem)
+}
+
+// Type =
+// BasicType | TypeName | ArrayType | SliceType | StructType |
+// PointerType | FuncType | InterfaceType | MapType | ChanType |
+// "(" Type ")" .
+//
+// BasicType = ident .
+// TypeName = ExportedName .
+// SliceType = "[" "]" Type .
+// PointerType = "*" Type .
+// FuncType = "func" Signature .
+//
+func (p *parser) parseType() types.Type {
+ switch p.tok {
+ case scanner.Ident:
+ switch p.lit {
+ default:
+ return p.parseBasicType()
+ case "struct":
+ return p.parseStructType()
+ case "func":
+ // FuncType
+ p.next()
+ return p.parseSignature(nil)
+ case "interface":
+ return p.parseInterfaceType()
+ case "map":
+ return p.parseMapType()
+ case "chan":
+ return p.parseChanType()
+ }
+ case '@':
+ // TypeName
+ pkg, name := p.parseExportedName()
+ return declTypeName(pkg, name).Type()
+ case '[':
+ p.next() // look ahead
+ if p.tok == ']' {
+ // SliceType
+ p.next()
+ return types.NewSlice(p.parseType())
+ }
+ return p.parseArrayType()
+ case '*':
+ // PointerType
+ p.next()
+ return types.NewPointer(p.parseType())
+ case '<':
+ return p.parseChanType()
+ case '(':
+ // "(" Type ")"
+ p.next()
+ typ := p.parseType()
+ p.expect(')')
+ return typ
+ }
+ p.errorf("expected type, got %s (%q)", scanner.TokenString(p.tok), p.lit)
+ return nil
+}
+
+// ----------------------------------------------------------------------------
+// Declarations
+
+// ImportDecl = "import" PackageName PackageId .
+//
+func (p *parser) parseImportDecl() {
+ p.expectKeyword("import")
+ name := p.parsePackageName()
+ p.getPkg(p.parsePackageId(), name)
+}
+
+// int_lit = [ "+" | "-" ] { "0" ... "9" } .
+//
+func (p *parser) parseInt() string {
+ s := ""
+ switch p.tok {
+ case '-':
+ s = "-"
+ p.next()
+ case '+':
+ p.next()
+ }
+ return s + p.expect(scanner.Int)
+}
+
+// number = int_lit [ "p" int_lit ] .
+//
+func (p *parser) parseNumber() (typ *types.Basic, val exact.Value) {
+ // mantissa
+ mant := exact.MakeFromLiteral(p.parseInt(), token.INT, 0)
+ if mant == nil {
+ panic("invalid mantissa")
+ }
+
+ if p.lit == "p" {
+ // exponent (base 2)
+ p.next()
+ exp, err := strconv.ParseInt(p.parseInt(), 10, 0)
+ if err != nil {
+ p.error(err)
+ }
+ if exp < 0 {
+ denom := exact.MakeInt64(1)
+ denom = exact.Shift(denom, token.SHL, uint(-exp))
+ typ = types.Typ[types.UntypedFloat]
+ val = exact.BinaryOp(mant, token.QUO, denom)
+ return
+ }
+ if exp > 0 {
+ mant = exact.Shift(mant, token.SHL, uint(exp))
+ }
+ typ = types.Typ[types.UntypedFloat]
+ val = mant
+ return
+ }
+
+ typ = types.Typ[types.UntypedInt]
+ val = mant
+ return
+}
+
+// ConstDecl = "const" ExportedName [ Type ] "=" Literal .
+// Literal = bool_lit | int_lit | float_lit | complex_lit | rune_lit | string_lit .
+// bool_lit = "true" | "false" .
+// complex_lit = "(" float_lit "+" float_lit "i" ")" .
+// rune_lit = "(" int_lit "+" int_lit ")" .
+// string_lit = `"` { unicode_char } `"` .
+//
+func (p *parser) parseConstDecl() {
+ p.expectKeyword("const")
+ pkg, name := p.parseExportedName()
+
+ var typ0 types.Type
+ if p.tok != '=' {
+ typ0 = p.parseType()
+ }
+
+ p.expect('=')
+ var typ types.Type
+ var val exact.Value
+ switch p.tok {
+ case scanner.Ident:
+ // bool_lit
+ if p.lit != "true" && p.lit != "false" {
+ p.error("expected true or false")
+ }
+ typ = types.Typ[types.UntypedBool]
+ val = exact.MakeBool(p.lit == "true")
+ p.next()
+
+ case '-', scanner.Int:
+ // int_lit
+ typ, val = p.parseNumber()
+
+ case '(':
+ // complex_lit or rune_lit
+ p.next()
+ if p.tok == scanner.Char {
+ p.next()
+ p.expect('+')
+ typ = types.Typ[types.UntypedRune]
+ _, val = p.parseNumber()
+ p.expect(')')
+ break
+ }
+ _, re := p.parseNumber()
+ p.expect('+')
+ _, im := p.parseNumber()
+ p.expectKeyword("i")
+ p.expect(')')
+ typ = types.Typ[types.UntypedComplex]
+ val = exact.BinaryOp(re, token.ADD, exact.MakeImag(im))
+
+ case scanner.Char:
+ // rune_lit
+ typ = types.Typ[types.UntypedRune]
+ val = exact.MakeFromLiteral(p.lit, token.CHAR, 0)
+ p.next()
+
+ case scanner.String:
+ // string_lit
+ typ = types.Typ[types.UntypedString]
+ val = exact.MakeFromLiteral(p.lit, token.STRING, 0)
+ p.next()
+
+ default:
+ p.errorf("expected literal got %s", scanner.TokenString(p.tok))
+ }
+
+ if typ0 == nil {
+ typ0 = typ
+ }
+
+ pkg.Scope().Insert(types.NewConst(token.NoPos, pkg, name, typ0, val))
+}
+
+// TypeDecl = "type" ExportedName Type .
+//
+func (p *parser) parseTypeDecl() {
+ p.expectKeyword("type")
+ pkg, name := p.parseExportedName()
+ obj := declTypeName(pkg, name)
+
+ // The type object may have been imported before and thus already
+ // have a type associated with it. We still need to parse the type
+ // structure, but throw it away if the object already has a type.
+ // This ensures that all imports refer to the same type object for
+ // a given type declaration.
+ typ := p.parseType()
+
+ if name := obj.Type().(*types.Named); name.Underlying() == nil {
+ name.SetUnderlying(typ)
+ }
+}
+
+// VarDecl = "var" ExportedName Type .
+//
+func (p *parser) parseVarDecl() {
+ p.expectKeyword("var")
+ pkg, name := p.parseExportedName()
+ typ := p.parseType()
+ pkg.Scope().Insert(types.NewVar(token.NoPos, pkg, name, typ))
+}
+
+// Func = Signature [ Body ] .
+// Body = "{" ... "}" .
+//
+func (p *parser) parseFunc(recv *types.Var) *types.Signature {
+ sig := p.parseSignature(recv)
+ if p.tok == '{' {
+ p.next()
+ for i := 1; i > 0; p.next() {
+ switch p.tok {
+ case '{':
+ i++
+ case '}':
+ i--
+ }
+ }
+ }
+ return sig
+}
+
+// MethodDecl = "func" Receiver Name Func .
+// Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" .
+//
+func (p *parser) parseMethodDecl() {
+ // "func" already consumed
+ p.expect('(')
+ recv, _ := p.parseParameter() // receiver
+ p.expect(')')
+
+ // determine receiver base type object
+ base := deref(recv.Type()).(*types.Named)
+
+ // parse method name, signature, and possibly inlined body
+ _, name := p.parseName(true)
+ sig := p.parseFunc(recv)
+
+ // methods always belong to the same package as the base type object
+ pkg := base.Obj().Pkg()
+
+ // add method to type unless type was imported before
+ // and method exists already
+ // TODO(gri) This leads to a quadratic algorithm - ok for now because method counts are small.
+ base.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig))
+}
+
+// FuncDecl = "func" ExportedName Func .
+//
+func (p *parser) parseFuncDecl() {
+ // "func" already consumed
+ pkg, name := p.parseExportedName()
+ typ := p.parseFunc(nil)
+ pkg.Scope().Insert(types.NewFunc(token.NoPos, pkg, name, typ))
+}
+
+// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" .
+//
+func (p *parser) parseDecl() {
+ if p.tok == scanner.Ident {
+ switch p.lit {
+ case "import":
+ p.parseImportDecl()
+ case "const":
+ p.parseConstDecl()
+ case "type":
+ p.parseTypeDecl()
+ case "var":
+ p.parseVarDecl()
+ case "func":
+ p.next() // look ahead
+ if p.tok == '(' {
+ p.parseMethodDecl()
+ } else {
+ p.parseFuncDecl()
+ }
+ }
+ }
+ p.expect('\n')
+}
+
+// ----------------------------------------------------------------------------
+// Export
+
+// Export = "PackageClause { Decl } "$$" .
+// PackageClause = "package" PackageName [ "safe" ] "\n" .
+//
+func (p *parser) parseExport() *types.Package {
+ p.expectKeyword("package")
+ name := p.parsePackageName()
+ if p.tok == scanner.Ident && p.lit == "safe" {
+ // package was compiled with -u option - ignore
+ p.next()
+ }
+ p.expect('\n')
+
+ pkg := p.getPkg(p.id, name)
+
+ for p.tok != '$' && p.tok != scanner.EOF {
+ p.parseDecl()
+ }
+
+ if ch := p.scanner.Peek(); p.tok != '$' || ch != '$' {
+ // don't call next()/expect() since reading past the
+ // export data may cause scanner errors (e.g. NUL chars)
+ p.errorf("expected '$$', got %s %c", scanner.TokenString(p.tok), ch)
+ }
+
+ if n := p.scanner.ErrorCount; n != 0 {
+ p.errorf("expected no scanner errors, got %d", n)
+ }
+
+ // package was imported completely and without errors
+ pkg.MarkComplete()
+
+ return pkg
+}
diff --git a/src/go/internal/gcimporter/gcimporter_test.go b/src/go/internal/gcimporter/gcimporter_test.go
new file mode 100644
index 0000000000..5d4de39712
--- /dev/null
+++ b/src/go/internal/gcimporter/gcimporter_test.go
@@ -0,0 +1,231 @@
+// 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 gcimporter
+
+import (
+ "go/build"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+
+ "go/types"
+)
+
+// skipSpecialPlatforms causes the test to be skipped for platforms where
+// builders (build.golang.org) don't have access to compiled packages for
+// import.
+func skipSpecialPlatforms(t *testing.T) {
+ switch platform := runtime.GOOS + "-" + runtime.GOARCH; platform {
+ case "nacl-amd64p32",
+ "nacl-386",
+ "nacl-arm",
+ "darwin-arm",
+ "darwin-arm64":
+ t.Skipf("no compiled packages available for import on %s", platform)
+ }
+}
+
+var gcPath string // Go compiler path
+
+func init() {
+ if char, err := build.ArchChar(runtime.GOARCH); err == nil {
+ gcPath = filepath.Join(build.ToolDir, char+"g")
+ return
+ }
+ gcPath = "unknown-GOARCH-compiler"
+}
+
+func compile(t *testing.T, dirname, filename string) string {
+ cmd := exec.Command(gcPath, filename)
+ cmd.Dir = dirname
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Logf("%s", out)
+ t.Fatalf("%s %s failed: %s", gcPath, filename, err)
+ }
+ archCh, _ := build.ArchChar(runtime.GOARCH)
+ // filename should end with ".go"
+ return filepath.Join(dirname, filename[:len(filename)-2]+archCh)
+}
+
+// Use the same global imports map for all tests. The effect is
+// as if all tested packages were imported into a single package.
+var imports = make(map[string]*types.Package)
+
+func testPath(t *testing.T, path string) bool {
+ t0 := time.Now()
+ _, err := Import(imports, path)
+ if err != nil {
+ t.Errorf("testPath(%s): %s", path, err)
+ return false
+ }
+ t.Logf("testPath(%s): %v", path, time.Since(t0))
+ return true
+}
+
+const maxTime = 30 * time.Second
+
+func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
+ dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir)
+ list, err := ioutil.ReadDir(dirname)
+ if err != nil {
+ t.Fatalf("testDir(%s): %s", dirname, err)
+ }
+ for _, f := range list {
+ if time.Now().After(endTime) {
+ t.Log("testing time used up")
+ return
+ }
+ switch {
+ case !f.IsDir():
+ // try extensions
+ for _, ext := range pkgExts {
+ if strings.HasSuffix(f.Name(), ext) {
+ name := f.Name()[0 : len(f.Name())-len(ext)] // remove extension
+ if testPath(t, filepath.Join(dir, name)) {
+ nimports++
+ }
+ }
+ }
+ case f.IsDir():
+ nimports += testDir(t, filepath.Join(dir, f.Name()), endTime)
+ }
+ }
+ return
+}
+
+func TestImport(t *testing.T) {
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ return
+ }
+
+ // On cross-compile builds, the path will not exist.
+ // Need to use GOHOSTOS, which is not available.
+ if _, err := os.Stat(gcPath); err != nil {
+ t.Skipf("skipping test: %v", err)
+ }
+
+ if outFn := compile(t, "testdata", "exports.go"); outFn != "" {
+ defer os.Remove(outFn)
+ }
+
+ nimports := 0
+ if testPath(t, "./testdata/exports") {
+ nimports++
+ }
+ nimports += testDir(t, "", time.Now().Add(maxTime)) // installed packages
+ t.Logf("tested %d imports", nimports)
+}
+
+var importedObjectTests = []struct {
+ name string
+ want string
+}{
+ {"math.Pi", "const Pi untyped float"},
+ {"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"},
+ {"io.ReadWriter", "type ReadWriter interface{Read(p []byte) (n int, err error); Write(p []byte) (n int, err error)}"},
+ {"math.Sin", "func Sin(x float64) float64"},
+ // TODO(gri) add more tests
+}
+
+func TestImportedTypes(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ return
+ }
+
+ for _, test := range importedObjectTests {
+ s := strings.Split(test.name, ".")
+ if len(s) != 2 {
+ t.Fatal("inconsistent test data")
+ }
+ importPath := s[0]
+ objName := s[1]
+
+ pkg, err := Import(imports, importPath)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ obj := pkg.Scope().Lookup(objName)
+ if obj == nil {
+ t.Errorf("%s: object not found", test.name)
+ continue
+ }
+
+ got := types.ObjectString(pkg, obj)
+ if got != test.want {
+ t.Errorf("%s: got %q; want %q", test.name, got, test.want)
+ }
+ }
+}
+
+func TestIssue5815(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ return
+ }
+
+ pkg, err := Import(make(map[string]*types.Package), "strings")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ scope := pkg.Scope()
+ for _, name := range scope.Names() {
+ obj := scope.Lookup(name)
+ if obj.Pkg() == nil {
+ t.Errorf("no pkg for %s", obj)
+ }
+ if tname, _ := obj.(*types.TypeName); tname != nil {
+ named := tname.Type().(*types.Named)
+ for i := 0; i < named.NumMethods(); i++ {
+ m := named.Method(i)
+ if m.Pkg() == nil {
+ t.Errorf("no pkg for %s", m)
+ }
+ }
+ }
+ }
+}
+
+// Smoke test to ensure that imported methods get the correct package.
+func TestCorrectMethodPackage(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ return
+ }
+
+ imports := make(map[string]*types.Package)
+ _, err := Import(imports, "net/http")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ mutex := imports["sync"].Scope().Lookup("Mutex").(*types.TypeName).Type()
+ mset := types.NewMethodSet(types.NewPointer(mutex)) // methods of *sync.Mutex
+ sel := mset.Lookup(nil, "Lock")
+ lock := sel.Obj().(*types.Func)
+ if got, want := lock.Pkg().Path(), "sync"; got != want {
+ t.Errorf("got package path %q; want %q", got, want)
+ }
+}
diff --git a/src/go/internal/gcimporter/testdata/exports.go b/src/go/internal/gcimporter/testdata/exports.go
new file mode 100644
index 0000000000..8ee28b0942
--- /dev/null
+++ b/src/go/internal/gcimporter/testdata/exports.go
@@ -0,0 +1,89 @@
+// 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 is used to generate an object file which
+// serves as test file for gcimporter_test.go.
+
+package exports
+
+import (
+ "go/ast"
+)
+
+// Issue 3682: Correctly read dotted identifiers from export data.
+const init1 = 0
+
+func init() {}
+
+const (
+ C0 int = 0
+ C1 = 3.14159265
+ C2 = 2.718281828i
+ C3 = -123.456e-789
+ C4 = +123.456E+789
+ C5 = 1234i
+ C6 = "foo\n"
+ C7 = `bar\n`
+)
+
+type (
+ T1 int
+ T2 [10]int
+ T3 []int
+ T4 *int
+ T5 chan int
+ T6a chan<- int
+ T6b chan (<-chan int)
+ T6c chan<- (chan int)
+ T7 <-chan *ast.File
+ T8 struct{}
+ T9 struct {
+ a int
+ b, c float32
+ d []string `go:"tag"`
+ }
+ T10 struct {
+ T8
+ T9
+ _ *T10
+ }
+ T11 map[int]string
+ T12 interface{}
+ T13 interface {
+ m1()
+ m2(int) float32
+ }
+ T14 interface {
+ T12
+ T13
+ m3(x ...struct{}) []T9
+ }
+ T15 func()
+ T16 func(int)
+ T17 func(x int)
+ T18 func() float32
+ T19 func() (x float32)
+ T20 func(...interface{})
+ T21 struct{ next *T21 }
+ T22 struct{ link *T23 }
+ T23 struct{ link *T22 }
+ T24 *T24
+ T25 *T26
+ T26 *T27
+ T27 *T25
+ T28 func(T28) T28
+)
+
+var (
+ V0 int
+ V1 = -991.0
+)
+
+func F1() {}
+func F2(x int) {}
+func F3() int { return 0 }
+func F4() float32 { return 0 }
+func F5(a, b, c int, u, v, w struct{ x, y T1 }, more ...interface{}) (p, q, r chan<- T10)
+
+func (p *T1) M1()
diff --git a/src/go/scanner/errors.go b/src/go/scanner/errors.go
index 7c9ab254ee..bf7bfa30e4 100644
--- a/src/go/scanner/errors.go
+++ b/src/go/scanner/errors.go
@@ -54,25 +54,16 @@ func (p ErrorList) Less(i, j int) bool {
// Note that it is not sufficient to simply compare file offsets because
// the offsets do not reflect modified line information (through //line
// comments).
- if e.Filename < f.Filename {
- return true
+ if e.Filename != f.Filename {
+ return e.Filename < f.Filename
}
- if e.Filename == f.Filename {
- if e.Line < f.Line {
- return true
- }
- if e.Line == f.Line {
- if e.Column < f.Column {
- return true
- }
- if e.Column == f.Column {
- if p[i].Msg < p[j].Msg {
- return true
- }
- }
- }
+ if e.Line != f.Line {
+ return e.Line < f.Line
}
- return false
+ if e.Column != f.Column {
+ return e.Column < f.Column
+ }
+ return p[i].Msg < p[j].Msg
}
// Sort sorts an ErrorList. *Error entries are sorted by position,
diff --git a/src/go/types.bash b/src/go/types.bash
new file mode 100644
index 0000000000..1a384d410a
--- /dev/null
+++ b/src/go/types.bash
@@ -0,0 +1,97 @@
+#!/bin/bash
+
+# Copyright 2015 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.
+
+# Run this script to update the packages ./exact and ./types
+# in the $GOROOT/src/go directory. They are vendored from the
+# original sources in x/tools. Imports are renamed as needed.
+#
+# Delete this script once go/exact and go/types don't exist anymore in x/tools.
+#
+# NOTE(adonovan): the standard packages have intentionally diverged
+# from x/tools, so this script is a unlikely to be useful. Upstream
+# changes should be cherry-picked in to the standard library.
+
+set -e
+
+### Safety first.
+if [ ! -d "$GOPATH" ]; then
+ echo 2>&1 '$GOPATH must be set.'
+ exit 1
+fi
+if [ ! -d "$GOROOT" ]; then
+ echo 2>&1 '$GOROOT must be set.'
+ exit 1
+fi
+
+GODIR=$GOROOT/src/go
+
+function vendor() (
+ SRCDIR=$GOPATH/src/golang.org/x/tools/$1
+ DSTDIR=$GODIR/$2
+
+ echo 2>&1 "vendoring $SRCDIR => $DSTDIR"
+
+ # create directory
+ rm -rf $DSTDIR
+ mkdir -p $DSTDIR
+ cd $DSTDIR
+
+ # copy go sources and update import paths
+ for f in $SRCDIR/*.go; do
+ # copy $f and update imports
+ sed -e 's|"golang.org/x/tools/go/exact"|"go/exact"|' \
+ -e 's|"golang.org/x/tools/go/types"|"go/types"|' \
+ -e 's|"golang.org/x/tools/go/gcimporter"|"go/internal/gcimporter"|' \
+ $f | gofmt > tmp.go
+ mv -f tmp.go `basename $f`
+ done
+
+ # copy testdata, if any
+ if [ -e $SRCDIR/testdata ]; then
+ cp -R $SRCDIR/testdata/ $DSTDIR/testdata/
+ fi
+)
+
+function install() (
+ PKG=$GODIR/$1
+
+ echo 2>&1 "installing $PKG"
+ cd $PKG
+ go install
+)
+
+function test() (
+ PKG=$GODIR/$1
+
+ echo 2>&1 "testing $PKG"
+ cd $PKG
+ if ! go test; then
+ echo 2>&1 "TESTING $PKG FAILED"
+ exit 1
+ fi
+)
+
+### go/exact
+vendor go/exact exact
+test exact
+install exact
+
+### go/types
+vendor go/types types
+# cannot test w/o gcimporter
+install types
+
+### go/gcimporter
+vendor go/gcimporter internal/gcimporter
+test internal/gcimporter
+install internal/gcimporter
+
+### test go/types (requires gcimporter)
+test types
+
+# All done.
+echo 2>&1 "DONE"
+exit 0
diff --git a/src/go/types/api.go b/src/go/types/api.go
new file mode 100644
index 0000000000..a2a55e31e7
--- /dev/null
+++ b/src/go/types/api.go
@@ -0,0 +1,336 @@
+// 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 types declares the data types and implements
+// the algorithms for type-checking of Go packages. Use
+// Config.Check to invoke the type checker for a package.
+// Alternatively, create a new type checked with NewChecker
+// and invoke it incrementally by calling Checker.Files.
+//
+// Type-checking consists of several interdependent phases:
+//
+// Name resolution maps each identifier (ast.Ident) in the program to the
+// language object (Object) it denotes.
+// Use Info.{Defs,Uses,Implicits} for the results of name resolution.
+//
+// Constant folding computes the exact constant value (exact.Value) for
+// every expression (ast.Expr) that is a compile-time constant.
+// Use Info.Types[expr].Value for the results of constant folding.
+//
+// Type inference computes the type (Type) of every expression (ast.Expr)
+// and checks for compliance with the language specification.
+// Use Info.Types[expr].Type for the results of type inference.
+//
+package types // import "go/types"
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+ "go/token"
+)
+
+// An Error describes a type-checking error; it implements the error interface.
+// A "soft" error is an error that still permits a valid interpretation of a
+// package (such as "unused variable"); "hard" errors may lead to unpredictable
+// behavior if ignored.
+type Error struct {
+ Fset *token.FileSet // file set for interpretation of Pos
+ Pos token.Pos // error position
+ Msg string // error message
+ Soft bool // if set, error is "soft"
+}
+
+// Error returns an error string formatted as follows:
+// filename:line:column: message
+func (err Error) Error() string {
+ return fmt.Sprintf("%s: %s", err.Fset.Position(err.Pos), err.Msg)
+}
+
+// An importer resolves import paths to Packages.
+// See go/importer for existing implementations.
+type Importer interface {
+ // Import returns the imported package for the given import
+ // path, or an error if the package couldn't be imported.
+ // Import is responsible for returning the same package for
+ // matching import paths.
+ Import(path string) (*Package, error)
+}
+
+// A Config specifies the configuration for type checking.
+// The zero value for Config is a ready-to-use default configuration.
+type Config struct {
+ // If IgnoreFuncBodies is set, function bodies are not
+ // type-checked.
+ IgnoreFuncBodies bool
+
+ // If FakeImportC is set, `import "C"` (for packages requiring Cgo)
+ // declares an empty "C" package and errors are omitted for qualified
+ // identifiers referring to package C (which won't find an object).
+ // This feature is intended for the standard library cmd/api tool.
+ //
+ // Caution: Effects may be unpredictable due to follow-up errors.
+ // Do not use casually!
+ FakeImportC bool
+
+ // If Error != nil, it is called with each error found
+ // during type checking; err has dynamic type Error.
+ // Secondary errors (for instance, to enumerate all types
+ // involved in an invalid recursive type declaration) have
+ // error strings that start with a '\t' character.
+ // If Error == nil, type-checking stops with the first
+ // error found.
+ Error func(err error)
+
+ // Importer is called for each import declaration except when
+ // importing package "unsafe". An error is reported if an
+ // importer is needed but none was installed.
+ Importer Importer
+
+ // If Sizes != nil, it provides the sizing functions for package unsafe.
+ // Otherwise &StdSizes{WordSize: 8, MaxAlign: 8} is used instead.
+ Sizes Sizes
+
+ // If DisableUnusedImportCheck is set, packages are not checked
+ // for unused imports.
+ DisableUnusedImportCheck bool
+}
+
+// Info holds result type information for a type-checked package.
+// Only the information for which a map is provided is collected.
+// If the package has type errors, the collected information may
+// be incomplete.
+type Info struct {
+ // Types maps expressions to their types, and for constant
+ // expressions, their values. Invalid expressions are omitted.
+ //
+ // For (possibly parenthesized) identifiers denoting built-in
+ // functions, the recorded signatures are call-site specific:
+ // if the call result is not a constant, the recorded type is
+ // an argument-specific signature. Otherwise, the recorded type
+ // is invalid.
+ //
+ // Identifiers on the lhs of declarations (i.e., the identifiers
+ // which are being declared) are collected in the Defs map.
+ // Identifiers denoting packages are collected in the Uses maps.
+ Types map[ast.Expr]TypeAndValue
+
+ // Defs maps identifiers to the objects they define (including
+ // package names, dots "." of dot-imports, and blank "_" identifiers).
+ // For identifiers that do not denote objects (e.g., the package name
+ // in package clauses, or symbolic variables t in t := x.(type) of
+ // type switch headers), the corresponding objects are nil.
+ //
+ // For an anonymous field, Defs returns the field *Var it defines.
+ //
+ // Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos()
+ Defs map[*ast.Ident]Object
+
+ // Uses maps identifiers to the objects they denote.
+ //
+ // For an anonymous field, Uses returns the *TypeName it denotes.
+ //
+ // Invariant: Uses[id].Pos() != id.Pos()
+ Uses map[*ast.Ident]Object
+
+ // Implicits maps nodes to their implicitly declared objects, if any.
+ // The following node and object types may appear:
+ //
+ // node declared object
+ //
+ // *ast.ImportSpec *PkgName for dot-imports and imports without renames
+ // *ast.CaseClause type-specific *Var for each type switch case clause (incl. default)
+ // *ast.Field anonymous struct field or parameter *Var
+ //
+ Implicits map[ast.Node]Object
+
+ // Selections maps selector expressions (excluding qualified identifiers)
+ // to their corresponding selections.
+ Selections map[*ast.SelectorExpr]*Selection
+
+ // Scopes maps ast.Nodes to the scopes they define. Package scopes are not
+ // associated with a specific node but with all files belonging to a package.
+ // Thus, the package scope can be found in the type-checked Package object.
+ // Scopes nest, with the Universe scope being the outermost scope, enclosing
+ // the package scope, which contains (one or more) files scopes, which enclose
+ // function scopes which in turn enclose statement and function literal scopes.
+ // Note that even though package-level functions are declared in the package
+ // scope, the function scopes are embedded in the file scope of the file
+ // containing the function declaration.
+ //
+ // The following node types may appear in Scopes:
+ //
+ // *ast.File
+ // *ast.FuncType
+ // *ast.BlockStmt
+ // *ast.IfStmt
+ // *ast.SwitchStmt
+ // *ast.TypeSwitchStmt
+ // *ast.CaseClause
+ // *ast.CommClause
+ // *ast.ForStmt
+ // *ast.RangeStmt
+ //
+ Scopes map[ast.Node]*Scope
+
+ // InitOrder is the list of package-level initializers in the order in which
+ // they must be executed. Initializers referring to variables related by an
+ // initialization dependency appear in topological order, the others appear
+ // in source order. Variables without an initialization expression do not
+ // appear in this list.
+ InitOrder []*Initializer
+}
+
+// TypeOf returns the type of expression e, or nil if not found.
+// Precondition: the Types, Uses and Defs maps are populated.
+//
+func (info *Info) TypeOf(e ast.Expr) Type {
+ if t, ok := info.Types[e]; ok {
+ return t.Type
+ }
+ if id, _ := e.(*ast.Ident); id != nil {
+ if obj := info.ObjectOf(id); obj != nil {
+ return obj.Type()
+ }
+ }
+ return nil
+}
+
+// ObjectOf returns the object denoted by the specified id,
+// or nil if not found.
+//
+// If id is an anonymous struct field, ObjectOf returns the field (*Var)
+// it uses, not the type (*TypeName) it defines.
+//
+// Precondition: the Uses and Defs maps are populated.
+//
+func (info *Info) ObjectOf(id *ast.Ident) Object {
+ if obj, _ := info.Defs[id]; obj != nil {
+ return obj
+ }
+ return info.Uses[id]
+}
+
+// TypeAndValue reports the type and value (for constants)
+// of the corresponding expression.
+type TypeAndValue struct {
+ mode operandMode
+ Type Type
+ Value exact.Value
+}
+
+// TODO(gri) Consider eliminating the IsVoid predicate. Instead, report
+// "void" values as regular values but with the empty tuple type.
+
+// IsVoid reports whether the corresponding expression
+// is a function call without results.
+func (tv TypeAndValue) IsVoid() bool {
+ return tv.mode == novalue
+}
+
+// IsType reports whether the corresponding expression specifies a type.
+func (tv TypeAndValue) IsType() bool {
+ return tv.mode == typexpr
+}
+
+// IsBuiltin reports whether the corresponding expression denotes
+// a (possibly parenthesized) built-in function.
+func (tv TypeAndValue) IsBuiltin() bool {
+ return tv.mode == builtin
+}
+
+// IsValue reports whether the corresponding expression is a value.
+// Builtins are not considered values. Constant values have a non-
+// nil Value.
+func (tv TypeAndValue) IsValue() bool {
+ switch tv.mode {
+ case constant, variable, mapindex, value, commaok:
+ return true
+ }
+ return false
+}
+
+// IsNil reports whether the corresponding expression denotes the
+// predeclared value nil.
+func (tv TypeAndValue) IsNil() bool {
+ return tv.mode == value && tv.Type == Typ[UntypedNil]
+}
+
+// Addressable reports whether the corresponding expression
+// is addressable (http://golang.org/ref/spec#Address_operators).
+func (tv TypeAndValue) Addressable() bool {
+ return tv.mode == variable
+}
+
+// Assignable reports whether the corresponding expression
+// is assignable to (provided a value of the right type).
+func (tv TypeAndValue) Assignable() bool {
+ return tv.mode == variable || tv.mode == mapindex
+}
+
+// HasOk reports whether the corresponding expression may be
+// used on the lhs of a comma-ok assignment.
+func (tv TypeAndValue) HasOk() bool {
+ return tv.mode == commaok || tv.mode == mapindex
+}
+
+// An Initializer describes a package-level variable, or a list of variables in case
+// of a multi-valued initialization expression, and the corresponding initialization
+// expression.
+type Initializer struct {
+ Lhs []*Var // var Lhs = Rhs
+ Rhs ast.Expr
+}
+
+func (init *Initializer) String() string {
+ var buf bytes.Buffer
+ for i, lhs := range init.Lhs {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ buf.WriteString(lhs.Name())
+ }
+ buf.WriteString(" = ")
+ WriteExpr(&buf, init.Rhs)
+ return buf.String()
+}
+
+// Check type-checks a package and returns the resulting package object,
+// the first error if any, and if info != nil, additional type information.
+// The package is marked as complete if no errors occurred, otherwise it is
+// incomplete. See Config.Error for controlling behavior in the presence of
+// errors.
+//
+// The package is specified by a list of *ast.Files and corresponding
+// file set, and the package path the package is identified with.
+// The clean path must not be empty or dot (".").
+func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, info *Info) (*Package, error) {
+ pkg := NewPackage(path, "")
+ return pkg, NewChecker(conf, fset, pkg, info).Files(files)
+}
+
+// AssertableTo reports whether a value of type V can be asserted to have type T.
+func AssertableTo(V *Interface, T Type) bool {
+ m, _ := assertableTo(V, T)
+ return m == nil
+}
+
+// AssignableTo reports whether a value of type V is assignable to a variable of type T.
+func AssignableTo(V, T Type) bool {
+ x := operand{mode: value, typ: V}
+ return x.assignableTo(nil, T) // config not needed for non-constant x
+}
+
+// ConvertibleTo reports whether a value of type V is convertible to a value of type T.
+func ConvertibleTo(V, T Type) bool {
+ x := operand{mode: value, typ: V}
+ return x.convertibleTo(nil, T) // config not needed for non-constant x
+}
+
+// Implements reports whether type V implements interface T.
+func Implements(V Type, T *Interface) bool {
+ f, _ := MissingMethod(V, T, true)
+ return f == nil
+}
diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go
new file mode 100644
index 0000000000..3ab909c8dd
--- /dev/null
+++ b/src/go/types/api_test.go
@@ -0,0 +1,958 @@
+// 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 types_test
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/importer"
+ "go/parser"
+ "go/token"
+ "runtime"
+ "strings"
+ "testing"
+
+ . "go/types"
+)
+
+// skipSpecialPlatforms causes the test to be skipped for platforms where
+// builders (build.golang.org) don't have access to compiled packages for
+// import.
+func skipSpecialPlatforms(t *testing.T) {
+ switch platform := runtime.GOOS + "-" + runtime.GOARCH; platform {
+ case "nacl-amd64p32",
+ "nacl-386",
+ "nacl-arm",
+ "darwin-arm",
+ "darwin-arm64":
+ t.Skipf("no compiled packages available for import on %s", platform)
+ }
+}
+
+func pkgFor(path, source string, info *Info) (*Package, error) {
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, path, source, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ conf := Config{Importer: importer.Default()}
+ return conf.Check(f.Name.Name, fset, []*ast.File{f}, info)
+}
+
+func mustTypecheck(t *testing.T, path, source string, info *Info) string {
+ pkg, err := pkgFor(path, source, info)
+ if err != nil {
+ name := path
+ if pkg != nil {
+ name = "package " + pkg.Name()
+ }
+ t.Fatalf("%s: didn't type-check (%s)", name, err)
+ }
+ return pkg.Name()
+}
+
+func TestValuesInfo(t *testing.T) {
+ var tests = []struct {
+ src string
+ expr string // constant expression
+ typ string // constant type
+ val string // constant value
+ }{
+ {`package a0; const _ = false`, `false`, `untyped bool`, `false`},
+ {`package a1; const _ = 0`, `0`, `untyped int`, `0`},
+ {`package a2; const _ = 'A'`, `'A'`, `untyped rune`, `65`},
+ {`package a3; const _ = 0.`, `0.`, `untyped float`, `0`},
+ {`package a4; const _ = 0i`, `0i`, `untyped complex`, `0`},
+ {`package a5; const _ = "foo"`, `"foo"`, `untyped string`, `"foo"`},
+
+ {`package b0; var _ = false`, `false`, `bool`, `false`},
+ {`package b1; var _ = 0`, `0`, `int`, `0`},
+ {`package b2; var _ = 'A'`, `'A'`, `rune`, `65`},
+ {`package b3; var _ = 0.`, `0.`, `float64`, `0`},
+ {`package b4; var _ = 0i`, `0i`, `complex128`, `0`},
+ {`package b5; var _ = "foo"`, `"foo"`, `string`, `"foo"`},
+
+ {`package c0a; var _ = bool(false)`, `false`, `bool`, `false`},
+ {`package c0b; var _ = bool(false)`, `bool(false)`, `bool`, `false`},
+ {`package c0c; type T bool; var _ = T(false)`, `T(false)`, `c0c.T`, `false`},
+
+ {`package c1a; var _ = int(0)`, `0`, `int`, `0`},
+ {`package c1b; var _ = int(0)`, `int(0)`, `int`, `0`},
+ {`package c1c; type T int; var _ = T(0)`, `T(0)`, `c1c.T`, `0`},
+
+ {`package c2a; var _ = rune('A')`, `'A'`, `rune`, `65`},
+ {`package c2b; var _ = rune('A')`, `rune('A')`, `rune`, `65`},
+ {`package c2c; type T rune; var _ = T('A')`, `T('A')`, `c2c.T`, `65`},
+
+ {`package c3a; var _ = float32(0.)`, `0.`, `float32`, `0`},
+ {`package c3b; var _ = float32(0.)`, `float32(0.)`, `float32`, `0`},
+ {`package c3c; type T float32; var _ = T(0.)`, `T(0.)`, `c3c.T`, `0`},
+
+ {`package c4a; var _ = complex64(0i)`, `0i`, `complex64`, `0`},
+ {`package c4b; var _ = complex64(0i)`, `complex64(0i)`, `complex64`, `0`},
+ {`package c4c; type T complex64; var _ = T(0i)`, `T(0i)`, `c4c.T`, `0`},
+
+ {`package c5a; var _ = string("foo")`, `"foo"`, `string`, `"foo"`},
+ {`package c5b; var _ = string("foo")`, `string("foo")`, `string`, `"foo"`},
+ {`package c5c; type T string; var _ = T("foo")`, `T("foo")`, `c5c.T`, `"foo"`},
+
+ {`package d0; var _ = []byte("foo")`, `"foo"`, `string`, `"foo"`},
+ {`package d1; var _ = []byte(string("foo"))`, `"foo"`, `string`, `"foo"`},
+ {`package d2; var _ = []byte(string("foo"))`, `string("foo")`, `string`, `"foo"`},
+ {`package d3; type T []byte; var _ = T("foo")`, `"foo"`, `string`, `"foo"`},
+
+ {`package e0; const _ = float32( 1e-200)`, `float32(1e-200)`, `float32`, `0`},
+ {`package e1; const _ = float32(-1e-200)`, `float32(-1e-200)`, `float32`, `0`},
+ {`package e2; const _ = float64( 1e-2000)`, `float64(1e-2000)`, `float64`, `0`},
+ {`package e3; const _ = float64(-1e-2000)`, `float64(-1e-2000)`, `float64`, `0`},
+ {`package e4; const _ = complex64( 1e-200)`, `complex64(1e-200)`, `complex64`, `0`},
+ {`package e5; const _ = complex64(-1e-200)`, `complex64(-1e-200)`, `complex64`, `0`},
+ {`package e6; const _ = complex128( 1e-2000)`, `complex128(1e-2000)`, `complex128`, `0`},
+ {`package e7; const _ = complex128(-1e-2000)`, `complex128(-1e-2000)`, `complex128`, `0`},
+
+ {`package f0 ; var _ float32 = 1e-200`, `1e-200`, `float32`, `0`},
+ {`package f1 ; var _ float32 = -1e-200`, `-1e-200`, `float32`, `0`},
+ {`package f2a; var _ float64 = 1e-2000`, `1e-2000`, `float64`, `0`},
+ {`package f3a; var _ float64 = -1e-2000`, `-1e-2000`, `float64`, `0`},
+ {`package f2b; var _ = 1e-2000`, `1e-2000`, `float64`, `0`},
+ {`package f3b; var _ = -1e-2000`, `-1e-2000`, `float64`, `0`},
+ {`package f4 ; var _ complex64 = 1e-200 `, `1e-200`, `complex64`, `0`},
+ {`package f5 ; var _ complex64 = -1e-200 `, `-1e-200`, `complex64`, `0`},
+ {`package f6a; var _ complex128 = 1e-2000i`, `1e-2000i`, `complex128`, `0`},
+ {`package f7a; var _ complex128 = -1e-2000i`, `-1e-2000i`, `complex128`, `0`},
+ {`package f6b; var _ = 1e-2000i`, `1e-2000i`, `complex128`, `0`},
+ {`package f7b; var _ = -1e-2000i`, `-1e-2000i`, `complex128`, `0`},
+ }
+
+ for _, test := range tests {
+ info := Info{
+ Types: make(map[ast.Expr]TypeAndValue),
+ }
+ name := mustTypecheck(t, "ValuesInfo", test.src, &info)
+
+ // look for constant expression
+ var expr ast.Expr
+ for e := range info.Types {
+ if ExprString(e) == test.expr {
+ expr = e
+ break
+ }
+ }
+ if expr == nil {
+ t.Errorf("package %s: no expression found for %s", name, test.expr)
+ continue
+ }
+ tv := info.Types[expr]
+
+ // check that type is correct
+ if got := tv.Type.String(); got != test.typ {
+ t.Errorf("package %s: got type %s; want %s", name, got, test.typ)
+ continue
+ }
+
+ // check that value is correct
+ if got := tv.Value.String(); got != test.val {
+ t.Errorf("package %s: got value %s; want %s", name, got, test.val)
+ }
+ }
+}
+
+func TestTypesInfo(t *testing.T) {
+ var tests = []struct {
+ src string
+ expr string // expression
+ typ string // value type
+ }{
+ // single-valued expressions of untyped constants
+ {`package b0; var x interface{} = false`, `false`, `bool`},
+ {`package b1; var x interface{} = 0`, `0`, `int`},
+ {`package b2; var x interface{} = 0.`, `0.`, `float64`},
+ {`package b3; var x interface{} = 0i`, `0i`, `complex128`},
+ {`package b4; var x interface{} = "foo"`, `"foo"`, `string`},
+
+ // comma-ok expressions
+ {`package p0; var x interface{}; var _, _ = x.(int)`,
+ `x.(int)`,
+ `(int, bool)`,
+ },
+ {`package p1; var x interface{}; func _() { _, _ = x.(int) }`,
+ `x.(int)`,
+ `(int, bool)`,
+ },
+ // TODO(gri): uncomment if we accept issue 8189.
+ // {`package p2; type mybool bool; var m map[string]complex128; var b mybool; func _() { _, b = m["foo"] }`,
+ // `m["foo"]`,
+ // `(complex128, p2.mybool)`,
+ // },
+ // TODO(gri): remove if we accept issue 8189.
+ {`package p2; var m map[string]complex128; var b bool; func _() { _, b = m["foo"] }`,
+ `m["foo"]`,
+ `(complex128, bool)`,
+ },
+ {`package p3; var c chan string; var _, _ = <-c`,
+ `<-c`,
+ `(string, bool)`,
+ },
+
+ // issue 6796
+ {`package issue6796_a; var x interface{}; var _, _ = (x.(int))`,
+ `x.(int)`,
+ `(int, bool)`,
+ },
+ {`package issue6796_b; var c chan string; var _, _ = (<-c)`,
+ `(<-c)`,
+ `(string, bool)`,
+ },
+ {`package issue6796_c; var c chan string; var _, _ = (<-c)`,
+ `<-c`,
+ `(string, bool)`,
+ },
+ {`package issue6796_d; var c chan string; var _, _ = ((<-c))`,
+ `(<-c)`,
+ `(string, bool)`,
+ },
+ {`package issue6796_e; func f(c chan string) { _, _ = ((<-c)) }`,
+ `(<-c)`,
+ `(string, bool)`,
+ },
+
+ // issue 7060
+ {`package issue7060_a; var ( m map[int]string; x, ok = m[0] )`,
+ `m[0]`,
+ `(string, bool)`,
+ },
+ {`package issue7060_b; var ( m map[int]string; x, ok interface{} = m[0] )`,
+ `m[0]`,
+ `(string, bool)`,
+ },
+ {`package issue7060_c; func f(x interface{}, ok bool, m map[int]string) { x, ok = m[0] }`,
+ `m[0]`,
+ `(string, bool)`,
+ },
+ {`package issue7060_d; var ( ch chan string; x, ok = <-ch )`,
+ `<-ch`,
+ `(string, bool)`,
+ },
+ {`package issue7060_e; var ( ch chan string; x, ok interface{} = <-ch )`,
+ `<-ch`,
+ `(string, bool)`,
+ },
+ {`package issue7060_f; func f(x interface{}, ok bool, ch chan string) { x, ok = <-ch }`,
+ `<-ch`,
+ `(string, bool)`,
+ },
+ }
+
+ for _, test := range tests {
+ info := Info{Types: make(map[ast.Expr]TypeAndValue)}
+ name := mustTypecheck(t, "TypesInfo", test.src, &info)
+
+ // look for expression type
+ var typ Type
+ for e, tv := range info.Types {
+ if ExprString(e) == test.expr {
+ typ = tv.Type
+ break
+ }
+ }
+ if typ == nil {
+ t.Errorf("package %s: no type found for %s", name, test.expr)
+ continue
+ }
+
+ // check that type is correct
+ if got := typ.String(); got != test.typ {
+ t.Errorf("package %s: got %s; want %s", name, got, test.typ)
+ }
+ }
+}
+
+func predString(tv TypeAndValue) string {
+ var buf bytes.Buffer
+ pred := func(b bool, s string) {
+ if b {
+ if buf.Len() > 0 {
+ buf.WriteString(", ")
+ }
+ buf.WriteString(s)
+ }
+ }
+
+ pred(tv.IsVoid(), "void")
+ pred(tv.IsType(), "type")
+ pred(tv.IsBuiltin(), "builtin")
+ pred(tv.IsValue() && tv.Value != nil, "const")
+ pred(tv.IsValue() && tv.Value == nil, "value")
+ pred(tv.IsNil(), "nil")
+ pred(tv.Addressable(), "addressable")
+ pred(tv.Assignable(), "assignable")
+ pred(tv.HasOk(), "hasOk")
+
+ if buf.Len() == 0 {
+ return "invalid"
+ }
+ return buf.String()
+}
+
+func TestPredicatesInfo(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ var tests = []struct {
+ src string
+ expr string
+ pred string
+ }{
+ // void
+ {`package n0; func f() { f() }`, `f()`, `void`},
+
+ // types
+ {`package t0; type _ int`, `int`, `type`},
+ {`package t1; type _ []int`, `[]int`, `type`},
+ {`package t2; type _ func()`, `func()`, `type`},
+
+ // built-ins
+ {`package b0; var _ = len("")`, `len`, `builtin`},
+ {`package b1; var _ = (len)("")`, `(len)`, `builtin`},
+
+ // constants
+ {`package c0; var _ = 42`, `42`, `const`},
+ {`package c1; var _ = "foo" + "bar"`, `"foo" + "bar"`, `const`},
+ {`package c2; const (i = 1i; _ = i)`, `i`, `const`},
+
+ // values
+ {`package v0; var (a, b int; _ = a + b)`, `a + b`, `value`},
+ {`package v1; var _ = &[]int{1}`, `([]int literal)`, `value`},
+ {`package v2; var _ = func(){}`, `(func() literal)`, `value`},
+ {`package v4; func f() { _ = f }`, `f`, `value`},
+ {`package v3; var _ *int = nil`, `nil`, `value, nil`},
+ {`package v3; var _ *int = (nil)`, `(nil)`, `value, nil`},
+
+ // addressable (and thus assignable) operands
+ {`package a0; var (x int; _ = x)`, `x`, `value, addressable, assignable`},
+ {`package a1; var (p *int; _ = *p)`, `*p`, `value, addressable, assignable`},
+ {`package a2; var (s []int; _ = s[0])`, `s[0]`, `value, addressable, assignable`},
+ {`package a3; var (s struct{f int}; _ = s.f)`, `s.f`, `value, addressable, assignable`},
+ {`package a4; var (a [10]int; _ = a[0])`, `a[0]`, `value, addressable, assignable`},
+ {`package a5; func _(x int) { _ = x }`, `x`, `value, addressable, assignable`},
+ {`package a6; func _()(x int) { _ = x; return }`, `x`, `value, addressable, assignable`},
+ {`package a7; type T int; func (x T) _() { _ = x }`, `x`, `value, addressable, assignable`},
+ // composite literals are not addressable
+
+ // assignable but not addressable values
+ {`package s0; var (m map[int]int; _ = m[0])`, `m[0]`, `value, assignable, hasOk`},
+ {`package s1; var (m map[int]int; _, _ = m[0])`, `m[0]`, `value, assignable, hasOk`},
+
+ // hasOk expressions
+ {`package k0; var (ch chan int; _ = <-ch)`, `<-ch`, `value, hasOk`},
+ {`package k1; var (ch chan int; _, _ = <-ch)`, `<-ch`, `value, hasOk`},
+
+ // missing entries
+ // - package names are collected in the Uses map
+ // - identifiers being declared are collected in the Defs map
+ {`package m0; import "os"; func _() { _ = os.Stdout }`, `os`, ``},
+ {`package m1; import p "os"; func _() { _ = p.Stdout }`, `p`, ``},
+ {`package m2; const c = 0`, `c`, ``},
+ {`package m3; type T int`, `T`, ``},
+ {`package m4; var v int`, `v`, ``},
+ {`package m5; func f() {}`, `f`, ``},
+ {`package m6; func _(x int) {}`, `x`, ``},
+ {`package m6; func _()(x int) { return }`, `x`, ``},
+ {`package m6; type T int; func (x T) _() {}`, `x`, ``},
+ }
+
+ for _, test := range tests {
+ info := Info{Types: make(map[ast.Expr]TypeAndValue)}
+ name := mustTypecheck(t, "PredicatesInfo", test.src, &info)
+
+ // look for expression predicates
+ got := ""
+ for e, tv := range info.Types {
+ //println(name, ExprString(e))
+ if ExprString(e) == test.expr {
+ got = predString(tv)
+ break
+ }
+ }
+
+ if got != test.pred {
+ t.Errorf("package %s: got %s; want %s", name, got, test.pred)
+ }
+ }
+}
+
+func TestScopesInfo(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ var tests = []struct {
+ src string
+ scopes []string // list of scope descriptors of the form kind:varlist
+ }{
+ {`package p0`, []string{
+ "file:",
+ }},
+ {`package p1; import ( "fmt"; m "math"; _ "os" ); var ( _ = fmt.Println; _ = m.Pi )`, []string{
+ "file:fmt m",
+ }},
+ {`package p2; func _() {}`, []string{
+ "file:", "func:",
+ }},
+ {`package p3; func _(x, y int) {}`, []string{
+ "file:", "func:x y",
+ }},
+ {`package p4; func _(x, y int) { x, z := 1, 2; _ = z }`, []string{
+ "file:", "func:x y z", // redeclaration of x
+ }},
+ {`package p5; func _(x, y int) (u, _ int) { return }`, []string{
+ "file:", "func:u x y",
+ }},
+ {`package p6; func _() { { var x int; _ = x } }`, []string{
+ "file:", "func:", "block:x",
+ }},
+ {`package p7; func _() { if true {} }`, []string{
+ "file:", "func:", "if:", "block:",
+ }},
+ {`package p8; func _() { if x := 0; x < 0 { y := x; _ = y } }`, []string{
+ "file:", "func:", "if:x", "block:y",
+ }},
+ {`package p9; func _() { switch x := 0; x {} }`, []string{
+ "file:", "func:", "switch:x",
+ }},
+ {`package p10; func _() { switch x := 0; x { case 1: y := x; _ = y; default: }}`, []string{
+ "file:", "func:", "switch:x", "case:y", "case:",
+ }},
+ {`package p11; func _(t interface{}) { switch t.(type) {} }`, []string{
+ "file:", "func:t", "type switch:",
+ }},
+ {`package p12; func _(t interface{}) { switch t := t; t.(type) {} }`, []string{
+ "file:", "func:t", "type switch:t",
+ }},
+ {`package p13; func _(t interface{}) { switch x := t.(type) { case int: _ = x } }`, []string{
+ "file:", "func:t", "type switch:", "case:x", // x implicitly declared
+ }},
+ {`package p14; func _() { select{} }`, []string{
+ "file:", "func:",
+ }},
+ {`package p15; func _(c chan int) { select{ case <-c: } }`, []string{
+ "file:", "func:c", "comm:",
+ }},
+ {`package p16; func _(c chan int) { select{ case i := <-c: x := i; _ = x} }`, []string{
+ "file:", "func:c", "comm:i x",
+ }},
+ {`package p17; func _() { for{} }`, []string{
+ "file:", "func:", "for:", "block:",
+ }},
+ {`package p18; func _(n int) { for i := 0; i < n; i++ { _ = i } }`, []string{
+ "file:", "func:n", "for:i", "block:",
+ }},
+ {`package p19; func _(a []int) { for i := range a { _ = i} }`, []string{
+ "file:", "func:a", "range:i", "block:",
+ }},
+ {`package p20; var s int; func _(a []int) { for i, x := range a { s += x; _ = i } }`, []string{
+ "file:", "func:a", "range:i x", "block:",
+ }},
+ }
+
+ for _, test := range tests {
+ info := Info{Scopes: make(map[ast.Node]*Scope)}
+ name := mustTypecheck(t, "ScopesInfo", test.src, &info)
+
+ // number of scopes must match
+ if len(info.Scopes) != len(test.scopes) {
+ t.Errorf("package %s: got %d scopes; want %d", name, len(info.Scopes), len(test.scopes))
+ }
+
+ // scope descriptions must match
+ for node, scope := range info.Scopes {
+ kind := ""
+ switch node.(type) {
+ case *ast.File:
+ kind = "file"
+ case *ast.FuncType:
+ kind = "func"
+ case *ast.BlockStmt:
+ kind = "block"
+ case *ast.IfStmt:
+ kind = "if"
+ case *ast.SwitchStmt:
+ kind = "switch"
+ case *ast.TypeSwitchStmt:
+ kind = "type switch"
+ case *ast.CaseClause:
+ kind = "case"
+ case *ast.CommClause:
+ kind = "comm"
+ case *ast.ForStmt:
+ kind = "for"
+ case *ast.RangeStmt:
+ kind = "range"
+ }
+
+ // look for matching scope description
+ desc := kind + ":" + strings.Join(scope.Names(), " ")
+ found := false
+ for _, d := range test.scopes {
+ if desc == d {
+ found = true
+ break
+ }
+ }
+ if !found {
+ t.Errorf("package %s: no matching scope found for %s", name, desc)
+ }
+ }
+ }
+}
+
+func TestInitOrderInfo(t *testing.T) {
+ var tests = []struct {
+ src string
+ inits []string
+ }{
+ {`package p0; var (x = 1; y = x)`, []string{
+ "x = 1", "y = x",
+ }},
+ {`package p1; var (a = 1; b = 2; c = 3)`, []string{
+ "a = 1", "b = 2", "c = 3",
+ }},
+ {`package p2; var (a, b, c = 1, 2, 3)`, []string{
+ "a = 1", "b = 2", "c = 3",
+ }},
+ {`package p3; var _ = f(); func f() int { return 1 }`, []string{
+ "_ = f()", // blank var
+ }},
+ {`package p4; var (a = 0; x = y; y = z; z = 0)`, []string{
+ "a = 0", "z = 0", "y = z", "x = y",
+ }},
+ {`package p5; var (a, _ = m[0]; m map[int]string)`, []string{
+ "a, _ = m[0]", // blank var
+ }},
+ {`package p6; var a, b = f(); func f() (_, _ int) { return z, z }; var z = 0`, []string{
+ "z = 0", "a, b = f()",
+ }},
+ {`package p7; var (a = func() int { return b }(); b = 1)`, []string{
+ "b = 1", "a = (func() int literal)()",
+ }},
+ {`package p8; var (a, b = func() (_, _ int) { return c, c }(); c = 1)`, []string{
+ "c = 1", "a, b = (func() (_, _ int) literal)()",
+ }},
+ {`package p9; type T struct{}; func (T) m() int { _ = y; return 0 }; var x, y = T.m, 1`, []string{
+ "y = 1", "x = T.m",
+ }},
+ {`package p10; var (d = c + b; a = 0; b = 0; c = 0)`, []string{
+ "a = 0", "b = 0", "c = 0", "d = c + b",
+ }},
+ {`package p11; var (a = e + c; b = d + c; c = 0; d = 0; e = 0)`, []string{
+ "c = 0", "d = 0", "b = d + c", "e = 0", "a = e + c",
+ }},
+ // emit an initializer for n:1 initializations only once (not for each node
+ // on the lhs which may appear in different order in the dependency graph)
+ {`package p12; var (a = x; b = 0; x, y = m[0]; m map[int]int)`, []string{
+ "b = 0", "x, y = m[0]", "a = x",
+ }},
+ // test case from spec section on package initialization
+ {`package p12
+
+ var (
+ a = c + b
+ b = f()
+ c = f()
+ d = 3
+ )
+
+ func f() int {
+ d++
+ return d
+ }`, []string{
+ "d = 3", "b = f()", "c = f()", "a = c + b",
+ }},
+ // test case for issue 7131
+ {`package main
+
+ var counter int
+ func next() int { counter++; return counter }
+
+ var _ = makeOrder()
+ func makeOrder() []int { return []int{f, b, d, e, c, a} }
+
+ var a = next()
+ var b, c = next(), next()
+ var d, e, f = next(), next(), next()
+ `, []string{
+ "a = next()", "b = next()", "c = next()", "d = next()", "e = next()", "f = next()", "_ = makeOrder()",
+ }},
+ }
+
+ for _, test := range tests {
+ info := Info{}
+ name := mustTypecheck(t, "InitOrderInfo", test.src, &info)
+
+ // number of initializers must match
+ if len(info.InitOrder) != len(test.inits) {
+ t.Errorf("package %s: got %d initializers; want %d", name, len(info.InitOrder), len(test.inits))
+ continue
+ }
+
+ // initializers must match
+ for i, want := range test.inits {
+ got := info.InitOrder[i].String()
+ if got != want {
+ t.Errorf("package %s, init %d: got %s; want %s", name, i, got, want)
+ continue
+ }
+ }
+ }
+}
+
+func TestMultiFileInitOrder(t *testing.T) {
+ fset := token.NewFileSet()
+ mustParse := func(src string) *ast.File {
+ f, err := parser.ParseFile(fset, "main", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return f
+ }
+
+ fileA := mustParse(`package main; var a = 1`)
+ fileB := mustParse(`package main; var b = 2`)
+
+ // The initialization order must not depend on the parse
+ // order of the files, only on the presentation order to
+ // the type-checker.
+ for _, test := range []struct {
+ files []*ast.File
+ want string
+ }{
+ {[]*ast.File{fileA, fileB}, "[a = 1 b = 2]"},
+ {[]*ast.File{fileB, fileA}, "[b = 2 a = 1]"},
+ } {
+ var info Info
+ if _, err := new(Config).Check("main", fset, test.files, &info); err != nil {
+ t.Fatal(err)
+ }
+ if got := fmt.Sprint(info.InitOrder); got != test.want {
+ t.Fatalf("got %s; want %s", got, test.want)
+ }
+ }
+}
+
+func TestFiles(t *testing.T) {
+ var sources = []string{
+ "package p; type T struct{}; func (T) m1() {}",
+ "package p; func (T) m2() {}; var x interface{ m1(); m2() } = T{}",
+ "package p; func (T) m3() {}; var y interface{ m1(); m2(); m3() } = T{}",
+ "package p",
+ }
+
+ var conf Config
+ fset := token.NewFileSet()
+ pkg := NewPackage("p", "p")
+ var info Info
+ check := NewChecker(&conf, fset, pkg, &info)
+
+ for i, src := range sources {
+ filename := fmt.Sprintf("sources%d", i)
+ f, err := parser.ParseFile(fset, filename, src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := check.Files([]*ast.File{f}); err != nil {
+ t.Error(err)
+ }
+ }
+
+ // check InitOrder is [x y]
+ var vars []string
+ for _, init := range info.InitOrder {
+ for _, v := range init.Lhs {
+ vars = append(vars, v.Name())
+ }
+ }
+ if got, want := fmt.Sprint(vars), "[x y]"; got != want {
+ t.Errorf("InitOrder == %s, want %s", got, want)
+ }
+}
+
+type testImporter map[string]*Package
+
+func (m testImporter) Import(path string) (*Package, error) {
+ if pkg := m[path]; pkg != nil {
+ return pkg, nil
+ }
+ return nil, fmt.Errorf("package %q not found", path)
+}
+
+func TestSelection(t *testing.T) {
+ selections := make(map[*ast.SelectorExpr]*Selection)
+
+ fset := token.NewFileSet()
+ imports := make(testImporter)
+ conf := Config{Importer: imports}
+ makePkg := func(path, src string) {
+ f, err := parser.ParseFile(fset, path+".go", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ pkg, err := conf.Check(path, fset, []*ast.File{f}, &Info{Selections: selections})
+ if err != nil {
+ t.Fatal(err)
+ }
+ imports[path] = pkg
+ }
+
+ const libSrc = `
+package lib
+type T float64
+const C T = 3
+var V T
+func F() {}
+func (T) M() {}
+`
+ const mainSrc = `
+package main
+import "lib"
+
+type A struct {
+ *B
+ C
+}
+
+type B struct {
+ b int
+}
+
+func (B) f(int)
+
+type C struct {
+ c int
+}
+
+func (C) g()
+func (*C) h()
+
+func main() {
+ // qualified identifiers
+ var _ lib.T
+ _ = lib.C
+ _ = lib.F
+ _ = lib.V
+ _ = lib.T.M
+
+ // fields
+ _ = A{}.B
+ _ = new(A).B
+
+ _ = A{}.C
+ _ = new(A).C
+
+ _ = A{}.b
+ _ = new(A).b
+
+ _ = A{}.c
+ _ = new(A).c
+
+ // methods
+ _ = A{}.f
+ _ = new(A).f
+ _ = A{}.g
+ _ = new(A).g
+ _ = new(A).h
+
+ _ = B{}.f
+ _ = new(B).f
+
+ _ = C{}.g
+ _ = new(C).g
+ _ = new(C).h
+
+ // method expressions
+ _ = A.f
+ _ = (*A).f
+ _ = B.f
+ _ = (*B).f
+}`
+
+ wantOut := map[string][2]string{
+ "lib.T.M": {"method expr (lib.T) M(lib.T)", ".[0]"},
+
+ "A{}.B": {"field (main.A) B *main.B", ".[0]"},
+ "new(A).B": {"field (*main.A) B *main.B", "->[0]"},
+ "A{}.C": {"field (main.A) C main.C", ".[1]"},
+ "new(A).C": {"field (*main.A) C main.C", "->[1]"},
+ "A{}.b": {"field (main.A) b int", "->[0 0]"},
+ "new(A).b": {"field (*main.A) b int", "->[0 0]"},
+ "A{}.c": {"field (main.A) c int", ".[1 0]"},
+ "new(A).c": {"field (*main.A) c int", "->[1 0]"},
+
+ "A{}.f": {"method (main.A) f(int)", "->[0 0]"},
+ "new(A).f": {"method (*main.A) f(int)", "->[0 0]"},
+ "A{}.g": {"method (main.A) g()", ".[1 0]"},
+ "new(A).g": {"method (*main.A) g()", "->[1 0]"},
+ "new(A).h": {"method (*main.A) h()", "->[1 1]"}, // TODO(gri) should this report .[1 1] ?
+ "B{}.f": {"method (main.B) f(int)", ".[0]"},
+ "new(B).f": {"method (*main.B) f(int)", "->[0]"},
+ "C{}.g": {"method (main.C) g()", ".[0]"},
+ "new(C).g": {"method (*main.C) g()", "->[0]"},
+ "new(C).h": {"method (*main.C) h()", "->[1]"}, // TODO(gri) should this report .[1] ?
+
+ "A.f": {"method expr (main.A) f(main.A, int)", "->[0 0]"},
+ "(*A).f": {"method expr (*main.A) f(*main.A, int)", "->[0 0]"},
+ "B.f": {"method expr (main.B) f(main.B, int)", ".[0]"},
+ "(*B).f": {"method expr (*main.B) f(*main.B, int)", "->[0]"},
+ }
+
+ makePkg("lib", libSrc)
+ makePkg("main", mainSrc)
+
+ for e, sel := range selections {
+ sel.String() // assertion: must not panic
+
+ start := fset.Position(e.Pos()).Offset
+ end := fset.Position(e.End()).Offset
+ syntax := mainSrc[start:end] // (all SelectorExprs are in main, not lib)
+
+ direct := "."
+ if sel.Indirect() {
+ direct = "->"
+ }
+ got := [2]string{
+ sel.String(),
+ fmt.Sprintf("%s%v", direct, sel.Index()),
+ }
+ want := wantOut[syntax]
+ if want != got {
+ t.Errorf("%s: got %q; want %q", syntax, got, want)
+ }
+ delete(wantOut, syntax)
+
+ // We must explicitly assert properties of the
+ // Signature's receiver since it doesn't participate
+ // in Identical() or String().
+ sig, _ := sel.Type().(*Signature)
+ if sel.Kind() == MethodVal {
+ got := sig.Recv().Type()
+ want := sel.Recv()
+ if !Identical(got, want) {
+ t.Errorf("%s: Recv() = %s, want %s", syntax, got, want)
+ }
+ } else if sig != nil && sig.Recv() != nil {
+ t.Errorf("%s: signature has receiver %s", sig, sig.Recv().Type())
+ }
+ }
+ // Assert that all wantOut entries were used exactly once.
+ for syntax := range wantOut {
+ t.Errorf("no ast.Selection found with syntax %q", syntax)
+ }
+}
+
+func TestIssue8518(t *testing.T) {
+ fset := token.NewFileSet()
+ imports := make(testImporter)
+ conf := Config{
+ Error: func(err error) { t.Log(err) }, // don't exit after first error
+ Importer: imports,
+ }
+ makePkg := func(path, src string) {
+ f, err := parser.ParseFile(fset, path, src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ pkg, _ := conf.Check(path, fset, []*ast.File{f}, nil) // errors logged via conf.Error
+ imports[path] = pkg
+ }
+
+ const libSrc = `
+package a
+import "missing"
+const C1 = foo
+const C2 = missing.C
+`
+
+ const mainSrc = `
+package main
+import "a"
+var _ = a.C1
+var _ = a.C2
+`
+
+ makePkg("a", libSrc)
+ makePkg("main", mainSrc) // don't crash when type-checking this package
+}
+
+func TestLookupFieldOrMethod(t *testing.T) {
+ // Test cases assume a lookup of the form a.f or x.f, where a stands for an
+ // addressable value, and x for a non-addressable value (even though a variable
+ // for ease of test case writing).
+ var tests = []struct {
+ src string
+ found bool
+ index []int
+ indirect bool
+ }{
+ // field lookups
+ {"var x T; type T struct{}", false, nil, false},
+ {"var x T; type T struct{ f int }", true, []int{0}, false},
+ {"var x T; type T struct{ a, b, f, c int }", true, []int{2}, false},
+
+ // method lookups
+ {"var a T; type T struct{}; func (T) f() {}", true, []int{0}, false},
+ {"var a *T; type T struct{}; func (T) f() {}", true, []int{0}, true},
+ {"var a T; type T struct{}; func (*T) f() {}", true, []int{0}, false},
+ {"var a *T; type T struct{}; func (*T) f() {}", true, []int{0}, true}, // TODO(gri) should this report indirect = false?
+
+ // collisions
+ {"type ( E1 struct{ f int }; E2 struct{ f int }; x struct{ E1; *E2 })", false, []int{1, 0}, false},
+ {"type ( E1 struct{ f int }; E2 struct{}; x struct{ E1; *E2 }); func (E2) f() {}", false, []int{1, 0}, false},
+
+ // outside methodset
+ // (*T).f method exists, but value of type T is not addressable
+ {"var x T; type T struct{}; func (*T) f() {}", false, nil, true},
+ }
+
+ for _, test := range tests {
+ pkg, err := pkgFor("test", "package p;"+test.src, nil)
+ if err != nil {
+ t.Errorf("%s: incorrect test case: %s", test.src, err)
+ continue
+ }
+
+ obj := pkg.Scope().Lookup("a")
+ if obj == nil {
+ if obj = pkg.Scope().Lookup("x"); obj == nil {
+ t.Errorf("%s: incorrect test case - no object a or x", test.src)
+ continue
+ }
+ }
+
+ f, index, indirect := LookupFieldOrMethod(obj.Type(), obj.Name() == "a", pkg, "f")
+ if (f != nil) != test.found {
+ if f == nil {
+ t.Errorf("%s: got no object; want one", test.src)
+ } else {
+ t.Errorf("%s: got object = %v; want none", test.src, f)
+ }
+ }
+ if !sameSlice(index, test.index) {
+ t.Errorf("%s: got index = %v; want %v", test.src, index, test.index)
+ }
+ if indirect != test.indirect {
+ t.Errorf("%s: got indirect = %v; want %v", test.src, indirect, test.indirect)
+ }
+ }
+}
+
+func sameSlice(a, b []int) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ for i, x := range a {
+ if x != b[i] {
+ return false
+ }
+ }
+ return true
+}
diff --git a/src/go/types/assignments.go b/src/go/types/assignments.go
new file mode 100644
index 0000000000..14ee286bf6
--- /dev/null
+++ b/src/go/types/assignments.go
@@ -0,0 +1,323 @@
+// 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 initialization and assignment checks.
+
+package types
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+// assignment reports whether x can be assigned to a variable of type T,
+// if necessary by attempting to convert untyped values to the appropriate
+// type. If x.mode == invalid upon return, then assignment has already
+// issued an error message and the caller doesn't have to report another.
+// Use T == nil to indicate assignment to an untyped blank identifier.
+//
+// TODO(gri) Should find a better way to handle in-band errors.
+//
+func (check *Checker) assignment(x *operand, T Type) bool {
+ switch x.mode {
+ case invalid:
+ return true // error reported before
+ case constant, variable, mapindex, value, commaok:
+ // ok
+ default:
+ unreachable()
+ }
+
+ // x must be a single value
+ // (tuple types are never named - no need for underlying type)
+ if t, _ := x.typ.(*Tuple); t != nil {
+ assert(t.Len() > 1)
+ check.errorf(x.pos(), "%d-valued expression %s used as single value", t.Len(), x)
+ x.mode = invalid
+ return false
+ }
+
+ if isUntyped(x.typ) {
+ target := T
+ // spec: "If an untyped constant is assigned to a variable of interface
+ // type or the blank identifier, the constant is first converted to type
+ // bool, rune, int, float64, complex128 or string respectively, depending
+ // on whether the value is a boolean, rune, integer, floating-point, complex,
+ // or string constant."
+ if T == nil || IsInterface(T) {
+ if T == nil && x.typ == Typ[UntypedNil] {
+ check.errorf(x.pos(), "use of untyped nil")
+ x.mode = invalid
+ return false
+ }
+ target = defaultType(x.typ)
+ }
+ check.convertUntyped(x, target)
+ if x.mode == invalid {
+ return false
+ }
+ }
+
+ // spec: "If a left-hand side is the blank identifier, any typed or
+ // non-constant value except for the predeclared identifier nil may
+ // be assigned to it."
+ return T == nil || x.assignableTo(check.conf, T)
+}
+
+func (check *Checker) initConst(lhs *Const, x *operand) {
+ if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] {
+ if lhs.typ == nil {
+ lhs.typ = Typ[Invalid]
+ }
+ return
+ }
+
+ // rhs must be a constant
+ if x.mode != constant {
+ check.errorf(x.pos(), "%s is not constant", x)
+ if lhs.typ == nil {
+ lhs.typ = Typ[Invalid]
+ }
+ return
+ }
+ assert(isConstType(x.typ))
+
+ // If the lhs doesn't have a type yet, use the type of x.
+ if lhs.typ == nil {
+ lhs.typ = x.typ
+ }
+
+ if !check.assignment(x, lhs.typ) {
+ if x.mode != invalid {
+ check.errorf(x.pos(), "cannot define constant %s (type %s) as %s", lhs.Name(), lhs.typ, x)
+ }
+ return
+ }
+
+ lhs.val = x.val
+}
+
+// If result is set, lhs is a function result parameter and x is a return result.
+func (check *Checker) initVar(lhs *Var, x *operand, result bool) Type {
+ if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] {
+ if lhs.typ == nil {
+ lhs.typ = Typ[Invalid]
+ }
+ return nil
+ }
+
+ // If the lhs doesn't have a type yet, use the type of x.
+ if lhs.typ == nil {
+ typ := x.typ
+ if isUntyped(typ) {
+ // convert untyped types to default types
+ if typ == Typ[UntypedNil] {
+ check.errorf(x.pos(), "use of untyped nil")
+ lhs.typ = Typ[Invalid]
+ return nil
+ }
+ typ = defaultType(typ)
+ }
+ lhs.typ = typ
+ }
+
+ if !check.assignment(x, lhs.typ) {
+ if x.mode != invalid {
+ if result {
+ // don't refer to lhs.name because it may be an anonymous result parameter
+ check.errorf(x.pos(), "cannot return %s as value of type %s", x, lhs.typ)
+ } else {
+ check.errorf(x.pos(), "cannot initialize %s with %s", lhs, x)
+ }
+ }
+ return nil
+ }
+
+ return x.typ
+}
+
+func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type {
+ if x.mode == invalid || x.typ == Typ[Invalid] {
+ return nil
+ }
+
+ // Determine if the lhs is a (possibly parenthesized) identifier.
+ ident, _ := unparen(lhs).(*ast.Ident)
+
+ // Don't evaluate lhs if it is the blank identifier.
+ if ident != nil && ident.Name == "_" {
+ check.recordDef(ident, nil)
+ if !check.assignment(x, nil) {
+ assert(x.mode == invalid)
+ x.typ = nil
+ }
+ return x.typ
+ }
+
+ // If the lhs is an identifier denoting a variable v, this assignment
+ // is not a 'use' of v. Remember current value of v.used and restore
+ // after evaluating the lhs via check.expr.
+ var v *Var
+ var v_used bool
+ if ident != nil {
+ if _, obj := check.scope.LookupParent(ident.Name); obj != nil {
+ v, _ = obj.(*Var)
+ if v != nil {
+ v_used = v.used
+ }
+ }
+ }
+
+ var z operand
+ check.expr(&z, lhs)
+ if v != nil {
+ v.used = v_used // restore v.used
+ }
+
+ if z.mode == invalid || z.typ == Typ[Invalid] {
+ return nil
+ }
+
+ // spec: "Each left-hand side operand must be addressable, a map index
+ // expression, or the blank identifier. Operands may be parenthesized."
+ switch z.mode {
+ case invalid:
+ return nil
+ case variable, mapindex:
+ // ok
+ default:
+ check.errorf(z.pos(), "cannot assign to %s", &z)
+ return nil
+ }
+
+ if !check.assignment(x, z.typ) {
+ if x.mode != invalid {
+ check.errorf(x.pos(), "cannot assign %s to %s", x, &z)
+ }
+ return nil
+ }
+
+ return x.typ
+}
+
+// If returnPos is valid, initVars is called to type-check the assignment of
+// return expressions, and returnPos is the position of the return statement.
+func (check *Checker) initVars(lhs []*Var, rhs []ast.Expr, returnPos token.Pos) {
+ l := len(lhs)
+ get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2 && !returnPos.IsValid())
+ if get == nil || l != r {
+ // invalidate lhs and use rhs
+ for _, obj := range lhs {
+ if obj.typ == nil {
+ obj.typ = Typ[Invalid]
+ }
+ }
+ if get == nil {
+ return // error reported by unpack
+ }
+ check.useGetter(get, r)
+ if returnPos.IsValid() {
+ check.errorf(returnPos, "wrong number of return values (want %d, got %d)", l, r)
+ return
+ }
+ check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r)
+ return
+ }
+
+ var x operand
+ if commaOk {
+ var a [2]Type
+ for i := range a {
+ get(&x, i)
+ a[i] = check.initVar(lhs[i], &x, returnPos.IsValid())
+ }
+ check.recordCommaOkTypes(rhs[0], a)
+ return
+ }
+
+ for i, lhs := range lhs {
+ get(&x, i)
+ check.initVar(lhs, &x, returnPos.IsValid())
+ }
+}
+
+func (check *Checker) assignVars(lhs, rhs []ast.Expr) {
+ l := len(lhs)
+ get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2)
+ if get == nil {
+ return // error reported by unpack
+ }
+ if l != r {
+ check.useGetter(get, r)
+ check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r)
+ return
+ }
+
+ var x operand
+ if commaOk {
+ var a [2]Type
+ for i := range a {
+ get(&x, i)
+ a[i] = check.assignVar(lhs[i], &x)
+ }
+ check.recordCommaOkTypes(rhs[0], a)
+ return
+ }
+
+ for i, lhs := range lhs {
+ get(&x, i)
+ check.assignVar(lhs, &x)
+ }
+}
+
+func (check *Checker) shortVarDecl(pos token.Pos, lhs, rhs []ast.Expr) {
+ scope := check.scope
+
+ // collect lhs variables
+ var newVars []*Var
+ var lhsVars = make([]*Var, len(lhs))
+ for i, lhs := range lhs {
+ var obj *Var
+ if ident, _ := lhs.(*ast.Ident); ident != nil {
+ // Use the correct obj if the ident is redeclared. The
+ // variable's scope starts after the declaration; so we
+ // must use Scope.Lookup here and call Scope.Insert
+ // (via check.declare) later.
+ name := ident.Name
+ if alt := scope.Lookup(name); alt != nil {
+ // redeclared object must be a variable
+ if alt, _ := alt.(*Var); alt != nil {
+ obj = alt
+ } else {
+ check.errorf(lhs.Pos(), "cannot assign to %s", lhs)
+ }
+ check.recordUse(ident, alt)
+ } else {
+ // declare new variable, possibly a blank (_) variable
+ obj = NewVar(ident.Pos(), check.pkg, name, nil)
+ if name != "_" {
+ newVars = append(newVars, obj)
+ }
+ check.recordDef(ident, obj)
+ }
+ } else {
+ check.errorf(lhs.Pos(), "cannot declare %s", lhs)
+ }
+ if obj == nil {
+ obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
+ }
+ lhsVars[i] = obj
+ }
+
+ check.initVars(lhsVars, rhs, token.NoPos)
+
+ // declare new variables
+ if len(newVars) > 0 {
+ for _, obj := range newVars {
+ check.declare(scope, nil, obj) // recordObject already called
+ }
+ } else {
+ check.softErrorf(pos, "no new variables on left side of :=")
+ }
+}
diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go
new file mode 100644
index 0000000000..203a9c196d
--- /dev/null
+++ b/src/go/types/builtins.go
@@ -0,0 +1,627 @@
+// 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 implements typechecking of builtin function calls.
+
+package types
+
+import (
+ "go/ast"
+ exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+ "go/token"
+)
+
+// builtin type-checks a call to the built-in specified by id and
+// returns true if the call is valid, with *x holding the result;
+// but x.expr is not set. If the call is invalid, the result is
+// false, and *x is undefined.
+//
+func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ bool) {
+ // append is the only built-in that permits the use of ... for the last argument
+ bin := predeclaredFuncs[id]
+ if call.Ellipsis.IsValid() && id != _Append {
+ check.invalidOp(call.Ellipsis, "invalid use of ... with built-in %s", bin.name)
+ check.use(call.Args...)
+ return
+ }
+
+ // For len(x) and cap(x) we need to know if x contains any function calls or
+ // receive operations. Save/restore current setting and set hasCallOrRecv to
+ // false for the evaluation of x so that we can check it afterwards.
+ // Note: We must do this _before_ calling unpack because unpack evaluates the
+ // first argument before we even call arg(x, 0)!
+ if id == _Len || id == _Cap {
+ defer func(b bool) {
+ check.hasCallOrRecv = b
+ }(check.hasCallOrRecv)
+ check.hasCallOrRecv = false
+ }
+
+ // determine actual arguments
+ var arg getter
+ nargs := len(call.Args)
+ switch id {
+ default:
+ // make argument getter
+ arg, nargs, _ = unpack(func(x *operand, i int) { check.expr(x, call.Args[i]) }, nargs, false)
+ if arg == nil {
+ return
+ }
+ // evaluate first argument, if present
+ if nargs > 0 {
+ arg(x, 0)
+ if x.mode == invalid {
+ return
+ }
+ }
+ case _Make, _New, _Offsetof, _Trace:
+ // arguments require special handling
+ }
+
+ // check argument count
+ {
+ msg := ""
+ if nargs < bin.nargs {
+ msg = "not enough"
+ } else if !bin.variadic && nargs > bin.nargs {
+ msg = "too many"
+ }
+ if msg != "" {
+ check.invalidOp(call.Rparen, "%s arguments for %s (expected %d, found %d)", msg, call, bin.nargs, nargs)
+ return
+ }
+ }
+
+ switch id {
+ case _Append:
+ // append(s S, x ...T) S, where T is the element type of S
+ // spec: "The variadic function append appends zero or more values x to s of type
+ // S, which must be a slice type, and returns the resulting slice, also of type S.
+ // The values x are passed to a parameter of type ...T where T is the element type
+ // of S and the respective parameter passing rules apply."
+ S := x.typ
+ var T Type
+ if s, _ := S.Underlying().(*Slice); s != nil {
+ T = s.elem
+ } else {
+ check.invalidArg(x.pos(), "%s is not a slice", x)
+ return
+ }
+
+ // remember arguments that have been evaluated already
+ alist := []operand{*x}
+
+ // spec: "As a special case, append also accepts a first argument assignable
+ // to type []byte with a second argument of string type followed by ... .
+ // This form appends the bytes of the string.
+ if nargs == 2 && call.Ellipsis.IsValid() && x.assignableTo(check.conf, NewSlice(UniverseByte)) {
+ arg(x, 1)
+ if x.mode == invalid {
+ return
+ }
+ if isString(x.typ) {
+ if check.Types != nil {
+ sig := makeSig(S, S, x.typ)
+ sig.variadic = true
+ check.recordBuiltinType(call.Fun, sig)
+ }
+ x.mode = value
+ x.typ = S
+ break
+ }
+ alist = append(alist, *x)
+ // fallthrough
+ }
+
+ // check general case by creating custom signature
+ sig := makeSig(S, S, NewSlice(T)) // []T required for variadic signature
+ sig.variadic = true
+ check.arguments(x, call, sig, func(x *operand, i int) {
+ // only evaluate arguments that have not been evaluated before
+ if i < len(alist) {
+ *x = alist[i]
+ return
+ }
+ arg(x, i)
+ }, nargs)
+ // ok to continue even if check.arguments reported errors
+
+ x.mode = value
+ x.typ = S
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, sig)
+ }
+
+ case _Cap, _Len:
+ // cap(x)
+ // len(x)
+ mode := invalid
+ var typ Type
+ var val exact.Value
+ switch typ = implicitArrayDeref(x.typ.Underlying()); t := typ.(type) {
+ case *Basic:
+ if isString(t) && id == _Len {
+ if x.mode == constant {
+ mode = constant
+ val = exact.MakeInt64(int64(len(exact.StringVal(x.val))))
+ } else {
+ mode = value
+ }
+ }
+
+ case *Array:
+ mode = value
+ // spec: "The expressions len(s) and cap(s) are constants
+ // if the type of s is an array or pointer to an array and
+ // the expression s does not contain channel receives or
+ // function calls; in this case s is not evaluated."
+ if !check.hasCallOrRecv {
+ mode = constant
+ val = exact.MakeInt64(t.len)
+ }
+
+ case *Slice, *Chan:
+ mode = value
+
+ case *Map:
+ if id == _Len {
+ mode = value
+ }
+ }
+
+ if mode == invalid {
+ check.invalidArg(x.pos(), "%s for %s", x, bin.name)
+ return
+ }
+
+ x.mode = mode
+ x.typ = Typ[Int]
+ x.val = val
+ if check.Types != nil && mode != constant {
+ check.recordBuiltinType(call.Fun, makeSig(x.typ, typ))
+ }
+
+ case _Close:
+ // close(c)
+ c, _ := x.typ.Underlying().(*Chan)
+ if c == nil {
+ check.invalidArg(x.pos(), "%s is not a channel", x)
+ return
+ }
+ if c.dir == RecvOnly {
+ check.invalidArg(x.pos(), "%s must not be a receive-only channel", x)
+ return
+ }
+
+ x.mode = novalue
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(nil, c))
+ }
+
+ case _Complex:
+ // complex(x, y realT) complexT
+ if !check.complexArg(x) {
+ return
+ }
+
+ var y operand
+ arg(&y, 1)
+ if y.mode == invalid {
+ return
+ }
+ if !check.complexArg(&y) {
+ return
+ }
+
+ check.convertUntyped(x, y.typ)
+ if x.mode == invalid {
+ return
+ }
+ check.convertUntyped(&y, x.typ)
+ if y.mode == invalid {
+ return
+ }
+
+ if !Identical(x.typ, y.typ) {
+ check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ)
+ return
+ }
+
+ if x.mode == constant && y.mode == constant {
+ x.val = exact.BinaryOp(x.val, token.ADD, exact.MakeImag(y.val))
+ } else {
+ x.mode = value
+ }
+
+ realT := x.typ
+ complexT := Typ[Invalid]
+ switch realT.Underlying().(*Basic).kind {
+ case Float32:
+ complexT = Typ[Complex64]
+ case Float64:
+ complexT = Typ[Complex128]
+ case UntypedInt, UntypedRune, UntypedFloat:
+ if x.mode == constant {
+ realT = defaultType(realT).(*Basic)
+ complexT = Typ[UntypedComplex]
+ } else {
+ // untyped but not constant; probably because one
+ // operand is a non-constant shift of untyped lhs
+ realT = Typ[Float64]
+ complexT = Typ[Complex128]
+ }
+ default:
+ check.invalidArg(x.pos(), "float32 or float64 arguments expected")
+ return
+ }
+
+ x.typ = complexT
+ if check.Types != nil && x.mode != constant {
+ check.recordBuiltinType(call.Fun, makeSig(complexT, realT, realT))
+ }
+
+ if x.mode != constant {
+ // The arguments have now their final types, which at run-
+ // time will be materialized. Update the expression trees.
+ // If the current types are untyped, the materialized type
+ // is the respective default type.
+ // (If the result is constant, the arguments are never
+ // materialized and there is nothing to do.)
+ check.updateExprType(x.expr, realT, true)
+ check.updateExprType(y.expr, realT, true)
+ }
+
+ case _Copy:
+ // copy(x, y []T) int
+ var dst Type
+ if t, _ := x.typ.Underlying().(*Slice); t != nil {
+ dst = t.elem
+ }
+
+ var y operand
+ arg(&y, 1)
+ if y.mode == invalid {
+ return
+ }
+ var src Type
+ switch t := y.typ.Underlying().(type) {
+ case *Basic:
+ if isString(y.typ) {
+ src = UniverseByte
+ }
+ case *Slice:
+ src = t.elem
+ }
+
+ if dst == nil || src == nil {
+ check.invalidArg(x.pos(), "copy expects slice arguments; found %s and %s", x, &y)
+ return
+ }
+
+ if !Identical(dst, src) {
+ check.invalidArg(x.pos(), "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src)
+ return
+ }
+
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(Typ[Int], x.typ, y.typ))
+ }
+ x.mode = value
+ x.typ = Typ[Int]
+
+ case _Delete:
+ // delete(m, k)
+ m, _ := x.typ.Underlying().(*Map)
+ if m == nil {
+ check.invalidArg(x.pos(), "%s is not a map", x)
+ return
+ }
+ arg(x, 1) // k
+ if x.mode == invalid {
+ return
+ }
+
+ if !x.assignableTo(check.conf, m.key) {
+ check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.key)
+ return
+ }
+
+ x.mode = novalue
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(nil, m, m.key))
+ }
+
+ case _Imag, _Real:
+ // imag(complexT) realT
+ // real(complexT) realT
+ if !isComplex(x.typ) {
+ check.invalidArg(x.pos(), "%s must be a complex number", x)
+ return
+ }
+ if x.mode == constant {
+ if id == _Real {
+ x.val = exact.Real(x.val)
+ } else {
+ x.val = exact.Imag(x.val)
+ }
+ } else {
+ x.mode = value
+ }
+ var k BasicKind
+ switch x.typ.Underlying().(*Basic).kind {
+ case Complex64:
+ k = Float32
+ case Complex128:
+ k = Float64
+ case UntypedComplex:
+ k = UntypedFloat
+ default:
+ unreachable()
+ }
+
+ if check.Types != nil && x.mode != constant {
+ check.recordBuiltinType(call.Fun, makeSig(Typ[k], x.typ))
+ }
+ x.typ = Typ[k]
+
+ case _Make:
+ // make(T, n)
+ // make(T, n, m)
+ // (no argument evaluated yet)
+ arg0 := call.Args[0]
+ T := check.typ(arg0)
+ if T == Typ[Invalid] {
+ return
+ }
+
+ var min int // minimum number of arguments
+ switch T.Underlying().(type) {
+ case *Slice:
+ min = 2
+ case *Map, *Chan:
+ min = 1
+ default:
+ check.invalidArg(arg0.Pos(), "cannot make %s; type must be slice, map, or channel", arg0)
+ return
+ }
+ if nargs < min || min+1 < nargs {
+ check.errorf(call.Pos(), "%s expects %d or %d arguments; found %d", call, min, min+1, nargs)
+ return
+ }
+ var sizes []int64 // constant integer arguments, if any
+ for _, arg := range call.Args[1:] {
+ if s, ok := check.index(arg, -1); ok && s >= 0 {
+ sizes = append(sizes, s)
+ }
+ }
+ if len(sizes) == 2 && sizes[0] > sizes[1] {
+ check.invalidArg(call.Args[1].Pos(), "length and capacity swapped")
+ // safe to continue
+ }
+ x.mode = value
+ x.typ = T
+ if check.Types != nil {
+ params := [...]Type{T, Typ[Int], Typ[Int]}
+ check.recordBuiltinType(call.Fun, makeSig(x.typ, params[:1+len(sizes)]...))
+ }
+
+ case _New:
+ // new(T)
+ // (no argument evaluated yet)
+ T := check.typ(call.Args[0])
+ if T == Typ[Invalid] {
+ return
+ }
+
+ x.mode = value
+ x.typ = &Pointer{base: T}
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(x.typ, T))
+ }
+
+ case _Panic:
+ // panic(x)
+ T := new(Interface)
+ if !check.assignment(x, T) {
+ assert(x.mode == invalid)
+ return
+ }
+
+ x.mode = novalue
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(nil, T))
+ }
+
+ case _Print, _Println:
+ // print(x, y, ...)
+ // println(x, y, ...)
+ var params []Type
+ if nargs > 0 {
+ params = make([]Type, nargs)
+ for i := 0; i < nargs; i++ {
+ if i > 0 {
+ arg(x, i) // first argument already evaluated
+ }
+ if !check.assignment(x, nil) {
+ assert(x.mode == invalid)
+ return
+ }
+ params[i] = x.typ
+ }
+ }
+
+ x.mode = novalue
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(nil, params...))
+ }
+
+ case _Recover:
+ // recover() interface{}
+ x.mode = value
+ x.typ = new(Interface)
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(x.typ))
+ }
+
+ case _Alignof:
+ // unsafe.Alignof(x T) uintptr
+ if !check.assignment(x, nil) {
+ assert(x.mode == invalid)
+ return
+ }
+
+ x.mode = constant
+ x.val = exact.MakeInt64(check.conf.alignof(x.typ))
+ x.typ = Typ[Uintptr]
+ // result is constant - no need to record signature
+
+ case _Offsetof:
+ // unsafe.Offsetof(x T) uintptr, where x must be a selector
+ // (no argument evaluated yet)
+ arg0 := call.Args[0]
+ selx, _ := unparen(arg0).(*ast.SelectorExpr)
+ if selx == nil {
+ check.invalidArg(arg0.Pos(), "%s is not a selector expression", arg0)
+ check.use(arg0)
+ return
+ }
+
+ check.expr(x, selx.X)
+ if x.mode == invalid {
+ return
+ }
+
+ base := derefStructPtr(x.typ)
+ sel := selx.Sel.Name
+ obj, index, indirect := LookupFieldOrMethod(base, false, check.pkg, sel)
+ switch obj.(type) {
+ case nil:
+ check.invalidArg(x.pos(), "%s has no single field %s", base, sel)
+ return
+ case *Func:
+ // TODO(gri) Using derefStructPtr may result in methods being found
+ // that don't actually exist. An error either way, but the error
+ // message is confusing. See: http://play.golang.org/p/al75v23kUy ,
+ // but go/types reports: "invalid argument: x.m is a method value".
+ check.invalidArg(arg0.Pos(), "%s is a method value", arg0)
+ return
+ }
+ if indirect {
+ check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, base)
+ return
+ }
+
+ // TODO(gri) Should we pass x.typ instead of base (and indirect report if derefStructPtr indirected)?
+ check.recordSelection(selx, FieldVal, base, obj, index, false)
+
+ offs := check.conf.offsetof(base, index)
+ x.mode = constant
+ x.val = exact.MakeInt64(offs)
+ x.typ = Typ[Uintptr]
+ // result is constant - no need to record signature
+
+ case _Sizeof:
+ // unsafe.Sizeof(x T) uintptr
+ if !check.assignment(x, nil) {
+ assert(x.mode == invalid)
+ return
+ }
+
+ x.mode = constant
+ x.val = exact.MakeInt64(check.conf.sizeof(x.typ))
+ x.typ = Typ[Uintptr]
+ // result is constant - no need to record signature
+
+ case _Assert:
+ // assert(pred) causes a typechecker error if pred is false.
+ // The result of assert is the value of pred if there is no error.
+ // Note: assert is only available in self-test mode.
+ if x.mode != constant || !isBoolean(x.typ) {
+ check.invalidArg(x.pos(), "%s is not a boolean constant", x)
+ return
+ }
+ if x.val.Kind() != exact.Bool {
+ check.errorf(x.pos(), "internal error: value of %s should be a boolean constant", x)
+ return
+ }
+ if !exact.BoolVal(x.val) {
+ check.errorf(call.Pos(), "%s failed", call)
+ // compile-time assertion failure - safe to continue
+ }
+ // result is constant - no need to record signature
+
+ case _Trace:
+ // trace(x, y, z, ...) dumps the positions, expressions, and
+ // values of its arguments. The result of trace is the value
+ // of the first argument.
+ // Note: trace is only available in self-test mode.
+ // (no argument evaluated yet)
+ if nargs == 0 {
+ check.dump("%s: trace() without arguments", call.Pos())
+ x.mode = novalue
+ break
+ }
+ var t operand
+ x1 := x
+ for _, arg := range call.Args {
+ check.rawExpr(x1, arg, nil) // permit trace for types, e.g.: new(trace(T))
+ check.dump("%s: %s", x1.pos(), x1)
+ x1 = &t // use incoming x only for first argument
+ }
+ // trace is only available in test mode - no need to record signature
+
+ default:
+ unreachable()
+ }
+
+ return true
+}
+
+// makeSig makes a signature for the given argument and result types.
+// Default types are used for untyped arguments, and res may be nil.
+func makeSig(res Type, args ...Type) *Signature {
+ list := make([]*Var, len(args))
+ for i, param := range args {
+ list[i] = NewVar(token.NoPos, nil, "", defaultType(param))
+ }
+ params := NewTuple(list...)
+ var result *Tuple
+ if res != nil {
+ assert(!isUntyped(res))
+ result = NewTuple(NewVar(token.NoPos, nil, "", res))
+ }
+ return &Signature{params: params, results: result}
+}
+
+// implicitArrayDeref returns A if typ is of the form *A and A is an array;
+// otherwise it returns typ.
+//
+func implicitArrayDeref(typ Type) Type {
+ if p, ok := typ.(*Pointer); ok {
+ if a, ok := p.base.Underlying().(*Array); ok {
+ return a
+ }
+ }
+ return typ
+}
+
+// unparen returns e with any enclosing parentheses stripped.
+func unparen(e ast.Expr) ast.Expr {
+ for {
+ p, ok := e.(*ast.ParenExpr)
+ if !ok {
+ return e
+ }
+ e = p.X
+ }
+}
+
+func (check *Checker) complexArg(x *operand) bool {
+ t, _ := x.typ.Underlying().(*Basic)
+ if t != nil && (t.info&IsFloat != 0 || t.kind == UntypedInt || t.kind == UntypedRune) {
+ return true
+ }
+ check.invalidArg(x.pos(), "%s must be a float32, float64, or an untyped non-complex numeric constant", x)
+ return false
+}
diff --git a/src/go/types/builtins_test.go b/src/go/types/builtins_test.go
new file mode 100644
index 0000000000..9835a48267
--- /dev/null
+++ b/src/go/types/builtins_test.go
@@ -0,0 +1,204 @@
+// 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 types_test
+
+import (
+ "fmt"
+ "go/ast"
+ "go/importer"
+ "go/parser"
+ "testing"
+
+ . "go/types"
+)
+
+var builtinCalls = []struct {
+ name, src, sig string
+}{
+ {"append", `var s []int; _ = append(s)`, `func([]int, ...int) []int`},
+ {"append", `var s []int; _ = append(s, 0)`, `func([]int, ...int) []int`},
+ {"append", `var s []int; _ = (append)(s, 0)`, `func([]int, ...int) []int`},
+ {"append", `var s []byte; _ = ((append))(s, 0)`, `func([]byte, ...byte) []byte`},
+ {"append", `var s []byte; _ = append(s, "foo"...)`, `func([]byte, string...) []byte`},
+ {"append", `type T []byte; var s T; var str string; _ = append(s, str...)`, `func(p.T, string...) p.T`},
+ {"append", `type T []byte; type U string; var s T; var str U; _ = append(s, str...)`, `func(p.T, p.U...) p.T`},
+
+ {"cap", `var s [10]int; _ = cap(s)`, `invalid type`}, // constant
+ {"cap", `var s [10]int; _ = cap(&s)`, `invalid type`}, // constant
+ {"cap", `var s []int64; _ = cap(s)`, `func([]int64) int`},
+ {"cap", `var c chan<-bool; _ = cap(c)`, `func(chan<- bool) int`},
+
+ {"len", `_ = len("foo")`, `invalid type`}, // constant
+ {"len", `var s string; _ = len(s)`, `func(string) int`},
+ {"len", `var s [10]int; _ = len(s)`, `invalid type`}, // constant
+ {"len", `var s [10]int; _ = len(&s)`, `invalid type`}, // constant
+ {"len", `var s []int64; _ = len(s)`, `func([]int64) int`},
+ {"len", `var c chan<-bool; _ = len(c)`, `func(chan<- bool) int`},
+ {"len", `var m map[string]float32; _ = len(m)`, `func(map[string]float32) int`},
+
+ {"close", `var c chan int; close(c)`, `func(chan int)`},
+ {"close", `var c chan<- chan string; close(c)`, `func(chan<- chan string)`},
+
+ {"complex", `_ = complex(1, 0)`, `invalid type`}, // constant
+ {"complex", `var re float32; _ = complex(re, 1.0)`, `func(float32, float32) complex64`},
+ {"complex", `var im float64; _ = complex(1, im)`, `func(float64, float64) complex128`},
+ {"complex", `type F32 float32; var re, im F32; _ = complex(re, im)`, `func(p.F32, p.F32) complex64`},
+ {"complex", `type F64 float64; var re, im F64; _ = complex(re, im)`, `func(p.F64, p.F64) complex128`},
+
+ {"copy", `var src, dst []byte; copy(dst, src)`, `func([]byte, []byte) int`},
+ {"copy", `type T [][]int; var src, dst T; _ = copy(dst, src)`, `func(p.T, p.T) int`},
+ {"copy", `var src string; var dst []byte; copy(dst, src)`, `func([]byte, string) int`},
+ {"copy", `type T string; type U []byte; var src T; var dst U; copy(dst, src)`, `func(p.U, p.T) int`},
+ {"copy", `var dst []byte; copy(dst, "hello")`, `func([]byte, string) int`},
+
+ {"delete", `var m map[string]bool; delete(m, "foo")`, `func(map[string]bool, string)`},
+ {"delete", `type (K string; V int); var m map[K]V; delete(m, "foo")`, `func(map[p.K]p.V, p.K)`},
+
+ {"imag", `_ = imag(1i)`, `invalid type`}, // constant
+ {"imag", `var c complex64; _ = imag(c)`, `func(complex64) float32`},
+ {"imag", `var c complex128; _ = imag(c)`, `func(complex128) float64`},
+ {"imag", `type C64 complex64; var c C64; _ = imag(c)`, `func(p.C64) float32`},
+ {"imag", `type C128 complex128; var c C128; _ = imag(c)`, `func(p.C128) float64`},
+
+ {"real", `_ = real(1i)`, `invalid type`}, // constant
+ {"real", `var c complex64; _ = real(c)`, `func(complex64) float32`},
+ {"real", `var c complex128; _ = real(c)`, `func(complex128) float64`},
+ {"real", `type C64 complex64; var c C64; _ = real(c)`, `func(p.C64) float32`},
+ {"real", `type C128 complex128; var c C128; _ = real(c)`, `func(p.C128) float64`},
+
+ {"make", `_ = make([]int, 10)`, `func([]int, int) []int`},
+ {"make", `type T []byte; _ = make(T, 10, 20)`, `func(p.T, int, int) p.T`},
+
+ {"new", `_ = new(int)`, `func(int) *int`},
+ {"new", `type T struct{}; _ = new(T)`, `func(p.T) *p.T`},
+
+ {"panic", `panic(0)`, `func(interface{})`},
+ {"panic", `panic("foo")`, `func(interface{})`},
+
+ {"print", `print()`, `func()`},
+ {"print", `print(0)`, `func(int)`},
+ {"print", `print(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`},
+
+ {"println", `println()`, `func()`},
+ {"println", `println(0)`, `func(int)`},
+ {"println", `println(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`},
+
+ {"recover", `recover()`, `func() interface{}`},
+ {"recover", `_ = recover()`, `func() interface{}`},
+
+ {"Alignof", `_ = unsafe.Alignof(0)`, `invalid type`}, // constant
+ {"Alignof", `var x struct{}; _ = unsafe.Alignof(x)`, `invalid type`}, // constant
+
+ {"Offsetof", `var x struct{f bool}; _ = unsafe.Offsetof(x.f)`, `invalid type`}, // constant
+ {"Offsetof", `var x struct{_ int; f bool}; _ = unsafe.Offsetof((&x).f)`, `invalid type`}, // constant
+
+ {"Sizeof", `_ = unsafe.Sizeof(0)`, `invalid type`}, // constant
+ {"Sizeof", `var x struct{}; _ = unsafe.Sizeof(x)`, `invalid type`}, // constant
+
+ {"assert", `assert(true)`, `invalid type`}, // constant
+ {"assert", `type B bool; const pred B = 1 < 2; assert(pred)`, `invalid type`}, // constant
+
+ // no tests for trace since it produces output as a side-effect
+}
+
+func TestBuiltinSignatures(t *testing.T) {
+ DefPredeclaredTestFuncs()
+
+ seen := map[string]bool{"trace": true} // no test for trace built-in; add it manually
+ for _, call := range builtinCalls {
+ testBuiltinSignature(t, call.name, call.src, call.sig)
+ seen[call.name] = true
+ }
+
+ // make sure we didn't miss one
+ for _, name := range Universe.Names() {
+ if _, ok := Universe.Lookup(name).(*Builtin); ok && !seen[name] {
+ t.Errorf("missing test for %s", name)
+ }
+ }
+ for _, name := range Unsafe.Scope().Names() {
+ if _, ok := Unsafe.Scope().Lookup(name).(*Builtin); ok && !seen[name] {
+ t.Errorf("missing test for unsafe.%s", name)
+ }
+ }
+}
+
+func testBuiltinSignature(t *testing.T, name, src0, want string) {
+ src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _() { %s }`, src0)
+ f, err := parser.ParseFile(fset, "", src, 0)
+ if err != nil {
+ t.Errorf("%s: %s", src0, err)
+ return
+ }
+
+ conf := Config{Importer: importer.Default()}
+ uses := make(map[*ast.Ident]Object)
+ types := make(map[ast.Expr]TypeAndValue)
+ _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Uses: uses, Types: types})
+ if err != nil {
+ t.Errorf("%s: %s", src0, err)
+ return
+ }
+
+ // find called function
+ n := 0
+ var fun ast.Expr
+ for x := range types {
+ if call, _ := x.(*ast.CallExpr); call != nil {
+ fun = call.Fun
+ n++
+ }
+ }
+ if n != 1 {
+ t.Errorf("%s: got %d CallExprs; want 1", src0, n)
+ return
+ }
+
+ // check recorded types for fun and descendents (may be parenthesized)
+ for {
+ // the recorded type for the built-in must match the wanted signature
+ typ := types[fun].Type
+ if typ == nil {
+ t.Errorf("%s: no type recorded for %s", src0, ExprString(fun))
+ return
+ }
+ if got := typ.String(); got != want {
+ t.Errorf("%s: got type %s; want %s", src0, got, want)
+ return
+ }
+
+ // called function must be a (possibly parenthesized, qualified)
+ // identifier denoting the expected built-in
+ switch p := fun.(type) {
+ case *ast.Ident:
+ obj := uses[p]
+ if obj == nil {
+ t.Errorf("%s: no object found for %s", src0, p)
+ return
+ }
+ bin, _ := obj.(*Builtin)
+ if bin == nil {
+ t.Errorf("%s: %s does not denote a built-in", src0, p)
+ return
+ }
+ if bin.Name() != name {
+ t.Errorf("%s: got built-in %s; want %s", src0, bin.Name(), name)
+ return
+ }
+ return // we're done
+
+ case *ast.ParenExpr:
+ fun = p.X // unpack
+
+ case *ast.SelectorExpr:
+ // built-in from package unsafe - ignore details
+ return // we're done
+
+ default:
+ t.Errorf("%s: invalid function call", src0)
+ return
+ }
+ }
+}
diff --git a/src/go/types/call.go b/src/go/types/call.go
new file mode 100644
index 0000000000..7f366a8cd0
--- /dev/null
+++ b/src/go/types/call.go
@@ -0,0 +1,441 @@
+// 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 typechecking of call and selector expressions.
+
+package types
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind {
+ check.exprOrType(x, e.Fun)
+
+ switch x.mode {
+ case invalid:
+ check.use(e.Args...)
+ x.mode = invalid
+ x.expr = e
+ return statement
+
+ case typexpr:
+ // conversion
+ T := x.typ
+ x.mode = invalid
+ switch n := len(e.Args); n {
+ case 0:
+ check.errorf(e.Rparen, "missing argument in conversion to %s", T)
+ case 1:
+ check.expr(x, e.Args[0])
+ if x.mode != invalid {
+ check.conversion(x, T)
+ }
+ default:
+ check.errorf(e.Args[n-1].Pos(), "too many arguments in conversion to %s", T)
+ }
+ x.expr = e
+ return conversion
+
+ case builtin:
+ id := x.id
+ if !check.builtin(x, e, id) {
+ x.mode = invalid
+ }
+ x.expr = e
+ // a non-constant result implies a function call
+ if x.mode != invalid && x.mode != constant {
+ check.hasCallOrRecv = true
+ }
+ return predeclaredFuncs[id].kind
+
+ default:
+ // function/method call
+ sig, _ := x.typ.Underlying().(*Signature)
+ if sig == nil {
+ check.invalidOp(x.pos(), "cannot call non-function %s", x)
+ x.mode = invalid
+ x.expr = e
+ return statement
+ }
+
+ arg, n, _ := unpack(func(x *operand, i int) { check.expr(x, e.Args[i]) }, len(e.Args), false)
+ if arg == nil {
+ x.mode = invalid
+ x.expr = e
+ return statement
+ }
+
+ check.arguments(x, e, sig, arg, n)
+
+ // determine result
+ switch sig.results.Len() {
+ case 0:
+ x.mode = novalue
+ case 1:
+ x.mode = value
+ x.typ = sig.results.vars[0].typ // unpack tuple
+ default:
+ x.mode = value
+ x.typ = sig.results
+ }
+ x.expr = e
+ check.hasCallOrRecv = true
+
+ return statement
+ }
+}
+
+// use type-checks each argument.
+// Useful to make sure expressions are evaluated
+// (and variables are "used") in the presence of other errors.
+func (check *Checker) use(arg ...ast.Expr) {
+ var x operand
+ for _, e := range arg {
+ check.rawExpr(&x, e, nil)
+ }
+}
+
+// useGetter is like use, but takes a getter instead of a list of expressions.
+// It should be called instead of use if a getter is present to avoid repeated
+// evaluation of the first argument (since the getter was likely obtained via
+// unpack, which may have evaluated the first argument already).
+func (check *Checker) useGetter(get getter, n int) {
+ var x operand
+ for i := 0; i < n; i++ {
+ get(&x, i)
+ }
+}
+
+// A getter sets x as the i'th operand, where 0 <= i < n and n is the total
+// number of operands (context-specific, and maintained elsewhere). A getter
+// type-checks the i'th operand; the details of the actual check are getter-
+// specific.
+type getter func(x *operand, i int)
+
+// unpack takes a getter get and a number of operands n. If n == 1, unpack
+// calls the incoming getter for the first operand. If that operand is
+// invalid, unpack returns (nil, 0, false). Otherwise, if that operand is a
+// function call, or a comma-ok expression and allowCommaOk is set, the result
+// is a new getter and operand count providing access to the function results,
+// or comma-ok values, respectively. The third result value reports if it
+// is indeed the comma-ok case. In all other cases, the incoming getter and
+// operand count are returned unchanged, and the third result value is false.
+//
+// In other words, if there's exactly one operand that - after type-checking
+// by calling get - stands for multiple operands, the resulting getter provides
+// access to those operands instead.
+//
+// If the returned getter is called at most once for a given operand index i
+// (including i == 0), that operand is guaranteed to cause only one call of
+// the incoming getter with that i.
+//
+func unpack(get getter, n int, allowCommaOk bool) (getter, int, bool) {
+ if n == 1 {
+ // possibly result of an n-valued function call or comma,ok value
+ var x0 operand
+ get(&x0, 0)
+ if x0.mode == invalid {
+ return nil, 0, false
+ }
+
+ if t, ok := x0.typ.(*Tuple); ok {
+ // result of an n-valued function call
+ return func(x *operand, i int) {
+ x.mode = value
+ x.expr = x0.expr
+ x.typ = t.At(i).typ
+ }, t.Len(), false
+ }
+
+ if x0.mode == mapindex || x0.mode == commaok {
+ // comma-ok value
+ if allowCommaOk {
+ a := [2]Type{x0.typ, Typ[UntypedBool]}
+ return func(x *operand, i int) {
+ x.mode = value
+ x.expr = x0.expr
+ x.typ = a[i]
+ }, 2, true
+ }
+ x0.mode = value
+ }
+
+ // single value
+ return func(x *operand, i int) {
+ if i != 0 {
+ unreachable()
+ }
+ *x = x0
+ }, 1, false
+ }
+
+ // zero or multiple values
+ return get, n, false
+}
+
+// arguments checks argument passing for the call with the given signature.
+// The arg function provides the operand for the i'th argument.
+func (check *Checker) arguments(x *operand, call *ast.CallExpr, sig *Signature, arg getter, n int) {
+ if call.Ellipsis.IsValid() {
+ // last argument is of the form x...
+ if len(call.Args) == 1 && n > 1 {
+ // f()... is not permitted if f() is multi-valued
+ check.errorf(call.Ellipsis, "cannot use ... with %d-valued expression %s", n, call.Args[0])
+ check.useGetter(arg, n)
+ return
+ }
+ if !sig.variadic {
+ check.errorf(call.Ellipsis, "cannot use ... in call to non-variadic %s", call.Fun)
+ check.useGetter(arg, n)
+ return
+ }
+ }
+
+ // evaluate arguments
+ for i := 0; i < n; i++ {
+ arg(x, i)
+ if x.mode != invalid {
+ var ellipsis token.Pos
+ if i == n-1 && call.Ellipsis.IsValid() {
+ ellipsis = call.Ellipsis
+ }
+ check.argument(sig, i, x, ellipsis)
+ }
+ }
+
+ // check argument count
+ if sig.variadic {
+ // a variadic function accepts an "empty"
+ // last argument: count one extra
+ n++
+ }
+ if n < sig.params.Len() {
+ check.errorf(call.Rparen, "too few arguments in call to %s", call.Fun)
+ // ok to continue
+ }
+}
+
+// argument checks passing of argument x to the i'th parameter of the given signature.
+// If ellipsis is valid, the argument is followed by ... at that position in the call.
+func (check *Checker) argument(sig *Signature, i int, x *operand, ellipsis token.Pos) {
+ n := sig.params.Len()
+
+ // determine parameter type
+ var typ Type
+ switch {
+ case i < n:
+ typ = sig.params.vars[i].typ
+ case sig.variadic:
+ typ = sig.params.vars[n-1].typ
+ if debug {
+ if _, ok := typ.(*Slice); !ok {
+ check.dump("%s: expected unnamed slice type, got %s", sig.params.vars[n-1].Pos(), typ)
+ }
+ }
+ default:
+ check.errorf(x.pos(), "too many arguments")
+ return
+ }
+
+ if ellipsis.IsValid() {
+ // argument is of the form x...
+ if i != n-1 {
+ check.errorf(ellipsis, "can only use ... with matching parameter")
+ return
+ }
+ switch t := x.typ.Underlying().(type) {
+ case *Slice:
+ // ok
+ case *Tuple:
+ check.errorf(ellipsis, "cannot use ... with %d-valued expression %s", t.Len(), x)
+ return
+ default:
+ check.errorf(x.pos(), "cannot use %s as parameter of type %s", x, typ)
+ return
+ }
+ } else if sig.variadic && i >= n-1 {
+ // use the variadic parameter slice's element type
+ typ = typ.(*Slice).elem
+ }
+
+ if !check.assignment(x, typ) && x.mode != invalid {
+ check.errorf(x.pos(), "cannot pass argument %s to parameter of type %s", x, typ)
+ }
+}
+
+func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
+ // these must be declared before the "goto Error" statements
+ var (
+ obj Object
+ index []int
+ indirect bool
+ )
+
+ sel := e.Sel.Name
+ // If the identifier refers to a package, handle everything here
+ // so we don't need a "package" mode for operands: package names
+ // can only appear in qualified identifiers which are mapped to
+ // selector expressions.
+ if ident, ok := e.X.(*ast.Ident); ok {
+ _, obj := check.scope.LookupParent(ident.Name)
+ if pkg, _ := obj.(*PkgName); pkg != nil {
+ assert(pkg.pkg == check.pkg)
+ check.recordUse(ident, pkg)
+ pkg.used = true
+ exp := pkg.imported.scope.Lookup(sel)
+ if exp == nil {
+ if !pkg.imported.fake {
+ check.errorf(e.Pos(), "%s not declared by package %s", sel, ident)
+ }
+ goto Error
+ }
+ if !exp.Exported() {
+ check.errorf(e.Pos(), "%s not exported by package %s", sel, ident)
+ // ok to continue
+ }
+ check.recordUse(e.Sel, exp)
+ // Simplified version of the code for *ast.Idents:
+ // - imported objects are always fully initialized
+ switch exp := exp.(type) {
+ case *Const:
+ assert(exp.Val() != nil)
+ x.mode = constant
+ x.typ = exp.typ
+ x.val = exp.val
+ case *TypeName:
+ x.mode = typexpr
+ x.typ = exp.typ
+ case *Var:
+ x.mode = variable
+ x.typ = exp.typ
+ case *Func:
+ x.mode = value
+ x.typ = exp.typ
+ case *Builtin:
+ x.mode = builtin
+ x.typ = exp.typ
+ x.id = exp.id
+ default:
+ unreachable()
+ }
+ x.expr = e
+ return
+ }
+ }
+
+ check.exprOrType(x, e.X)
+ if x.mode == invalid {
+ goto Error
+ }
+
+ obj, index, indirect = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel)
+ if obj == nil {
+ switch {
+ case index != nil:
+ // TODO(gri) should provide actual type where the conflict happens
+ check.invalidOp(e.Pos(), "ambiguous selector %s", sel)
+ case indirect:
+ check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x.typ)
+ default:
+ check.invalidOp(e.Pos(), "%s has no field or method %s", x, sel)
+ }
+ goto Error
+ }
+
+ if x.mode == typexpr {
+ // method expression
+ m, _ := obj.(*Func)
+ if m == nil {
+ check.invalidOp(e.Pos(), "%s has no method %s", x, sel)
+ goto Error
+ }
+
+ check.recordSelection(e, MethodExpr, x.typ, m, index, indirect)
+
+ // the receiver type becomes the type of the first function
+ // argument of the method expression's function type
+ var params []*Var
+ sig := m.typ.(*Signature)
+ if sig.params != nil {
+ params = sig.params.vars
+ }
+ x.mode = value
+ x.typ = &Signature{
+ params: NewTuple(append([]*Var{NewVar(token.NoPos, check.pkg, "", x.typ)}, params...)...),
+ results: sig.results,
+ variadic: sig.variadic,
+ }
+
+ check.addDeclDep(m)
+
+ } else {
+ // regular selector
+ switch obj := obj.(type) {
+ case *Var:
+ check.recordSelection(e, FieldVal, x.typ, obj, index, indirect)
+ if x.mode == variable || indirect {
+ x.mode = variable
+ } else {
+ x.mode = value
+ }
+ x.typ = obj.typ
+
+ case *Func:
+ // TODO(gri) If we needed to take into account the receiver's
+ // addressability, should we report the type &(x.typ) instead?
+ check.recordSelection(e, MethodVal, x.typ, obj, index, indirect)
+
+ if debug {
+ // Verify that LookupFieldOrMethod and MethodSet.Lookup agree.
+ typ := x.typ
+ if x.mode == variable {
+ // If typ is not an (unnamed) pointer or an interface,
+ // use *typ instead, because the method set of *typ
+ // includes the methods of typ.
+ // Variables are addressable, so we can always take their
+ // address.
+ if _, ok := typ.(*Pointer); !ok && !IsInterface(typ) {
+ typ = &Pointer{base: typ}
+ }
+ }
+ // If we created a synthetic pointer type above, we will throw
+ // away the method set computed here after use.
+ // TODO(gri) Method set computation should probably always compute
+ // both, the value and the pointer receiver method set and represent
+ // them in a single structure.
+ // TODO(gri) Consider also using a method set cache for the lifetime
+ // of checker once we rely on MethodSet lookup instead of individual
+ // lookup.
+ mset := NewMethodSet(typ)
+ if m := mset.Lookup(check.pkg, sel); m == nil || m.obj != obj {
+ check.dump("%s: (%s).%v -> %s", e.Pos(), typ, obj.name, m)
+ check.dump("%s\n", mset)
+ panic("method sets and lookup don't agree")
+ }
+ }
+
+ x.mode = value
+
+ // remove receiver
+ sig := *obj.typ.(*Signature)
+ sig.recv = nil
+ x.typ = &sig
+
+ check.addDeclDep(obj)
+
+ default:
+ unreachable()
+ }
+ }
+
+ // everything went well
+ x.expr = e
+ return
+
+Error:
+ x.mode = invalid
+ x.expr = e
+}
diff --git a/src/go/types/check.go b/src/go/types/check.go
new file mode 100644
index 0000000000..b4c356a6ed
--- /dev/null
+++ b/src/go/types/check.go
@@ -0,0 +1,357 @@
+// 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 the Check function, which drives type-checking.
+
+package types
+
+import (
+ "go/ast"
+ exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+ "go/token"
+)
+
+// debugging/development support
+const (
+ debug = false // leave on during development
+ trace = false // turn on for detailed type resolution traces
+)
+
+// If Strict is set, the type-checker enforces additional
+// rules not specified by the Go 1 spec, but which will
+// catch guaranteed run-time errors if the respective
+// code is executed. In other words, programs passing in
+// Strict mode are Go 1 compliant, but not all Go 1 programs
+// will pass in Strict mode. The additional rules are:
+//
+// - A type assertion x.(T) where T is an interface type
+// is invalid if any (statically known) method that exists
+// for both x and T have different signatures.
+//
+const strict = false
+
+// exprInfo stores information about an untyped expression.
+type exprInfo struct {
+ isLhs bool // expression is lhs operand of a shift with delayed type-check
+ mode operandMode
+ typ *Basic
+ val exact.Value // constant value; or nil (if not a constant)
+}
+
+// funcInfo stores the information required for type-checking a function.
+type funcInfo struct {
+ name string // for debugging/tracing only
+ decl *declInfo // for cycle detection
+ sig *Signature
+ body *ast.BlockStmt
+}
+
+// A context represents the context within which an object is type-checked.
+type context struct {
+ decl *declInfo // package-level declaration whose init expression/function body is checked
+ scope *Scope // top-most scope for lookups
+ iota exact.Value // value of iota in a constant declaration; nil otherwise
+ sig *Signature // function signature if inside a function; nil otherwise
+ hasLabel bool // set if a function makes use of labels (only ~1% of functions); unused outside functions
+ hasCallOrRecv bool // set if an expression contains a function call or channel receive operation
+}
+
+// A Checker maintains the state of the type checker.
+// It must be created with NewChecker.
+type Checker struct {
+ // package information
+ // (initialized by NewChecker, valid for the life-time of checker)
+ conf *Config
+ fset *token.FileSet
+ pkg *Package
+ *Info
+ objMap map[Object]*declInfo // maps package-level object to declaration info
+
+ // information collected during type-checking of a set of package files
+ // (initialized by Files, valid only for the duration of check.Files;
+ // maps and lists are allocated on demand)
+ files []*ast.File // package files
+ unusedDotImports map[*Scope]map[*Package]token.Pos // positions of unused dot-imported packages for each file scope
+
+ firstErr error // first error encountered
+ methods map[string][]*Func // maps type names to associated methods
+ untyped map[ast.Expr]exprInfo // map of expressions without final type
+ funcs []funcInfo // list of functions to type-check
+ delayed []func() // delayed checks requiring fully setup types
+
+ // context within which the current object is type-checked
+ // (valid only for the duration of type-checking a specific object)
+ context
+
+ // debugging
+ indent int // indentation for tracing
+}
+
+// addUnusedImport adds the position of a dot-imported package
+// pkg to the map of dot imports for the given file scope.
+func (check *Checker) addUnusedDotImport(scope *Scope, pkg *Package, pos token.Pos) {
+ mm := check.unusedDotImports
+ if mm == nil {
+ mm = make(map[*Scope]map[*Package]token.Pos)
+ check.unusedDotImports = mm
+ }
+ m := mm[scope]
+ if m == nil {
+ m = make(map[*Package]token.Pos)
+ mm[scope] = m
+ }
+ m[pkg] = pos
+}
+
+// addDeclDep adds the dependency edge (check.decl -> to) if check.decl exists
+func (check *Checker) addDeclDep(to Object) {
+ from := check.decl
+ if from == nil {
+ return // not in a package-level init expression
+ }
+ if _, found := check.objMap[to]; !found {
+ return // to is not a package-level object
+ }
+ from.addDep(to)
+}
+
+func (check *Checker) assocMethod(tname string, meth *Func) {
+ m := check.methods
+ if m == nil {
+ m = make(map[string][]*Func)
+ check.methods = m
+ }
+ m[tname] = append(m[tname], meth)
+}
+
+func (check *Checker) rememberUntyped(e ast.Expr, lhs bool, mode operandMode, typ *Basic, val exact.Value) {
+ m := check.untyped
+ if m == nil {
+ m = make(map[ast.Expr]exprInfo)
+ check.untyped = m
+ }
+ m[e] = exprInfo{lhs, mode, typ, val}
+}
+
+func (check *Checker) later(name string, decl *declInfo, sig *Signature, body *ast.BlockStmt) {
+ check.funcs = append(check.funcs, funcInfo{name, decl, sig, body})
+}
+
+func (check *Checker) delay(f func()) {
+ check.delayed = append(check.delayed, f)
+}
+
+// NewChecker returns a new Checker instance for a given package.
+// Package files may be added incrementally via checker.Files.
+func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Checker {
+ // make sure we have a configuration
+ if conf == nil {
+ conf = new(Config)
+ }
+
+ // make sure we have an info struct
+ if info == nil {
+ info = new(Info)
+ }
+
+ return &Checker{
+ conf: conf,
+ fset: fset,
+ pkg: pkg,
+ Info: info,
+ objMap: make(map[Object]*declInfo),
+ }
+}
+
+// initFiles initializes the files-specific portion of checker.
+// The provided files must all belong to the same package.
+func (check *Checker) initFiles(files []*ast.File) {
+ // start with a clean slate (check.Files may be called multiple times)
+ check.files = nil
+ check.unusedDotImports = nil
+
+ check.firstErr = nil
+ check.methods = nil
+ check.untyped = nil
+ check.funcs = nil
+ check.delayed = nil
+
+ // determine package name and collect valid files
+ pkg := check.pkg
+ for _, file := range files {
+ switch name := file.Name.Name; pkg.name {
+ case "":
+ if name != "_" {
+ pkg.name = name
+ } else {
+ check.errorf(file.Name.Pos(), "invalid package name _")
+ }
+ fallthrough
+
+ case name:
+ check.files = append(check.files, file)
+
+ default:
+ check.errorf(file.Package, "package %s; expected %s", name, pkg.name)
+ // ignore this file
+ }
+ }
+}
+
+// A bailout panic is used for early termination.
+type bailout struct{}
+
+func (check *Checker) handleBailout(err *error) {
+ switch p := recover().(type) {
+ case nil, bailout:
+ // normal return or early exit
+ *err = check.firstErr
+ default:
+ // re-panic
+ panic(p)
+ }
+}
+
+// Files checks the provided files as part of the checker's package.
+func (check *Checker) Files(files []*ast.File) (err error) {
+ defer check.handleBailout(&err)
+
+ check.initFiles(files)
+
+ check.collectObjects()
+
+ check.packageObjects(check.resolveOrder())
+
+ check.functionBodies()
+
+ check.initOrder()
+
+ if !check.conf.DisableUnusedImportCheck {
+ check.unusedImports()
+ }
+
+ // perform delayed checks
+ for _, f := range check.delayed {
+ f()
+ }
+
+ check.recordUntyped()
+
+ check.pkg.complete = true
+ return
+}
+
+func (check *Checker) recordUntyped() {
+ if !debug && check.Types == nil {
+ return // nothing to do
+ }
+
+ for x, info := range check.untyped {
+ if debug && isTyped(info.typ) {
+ check.dump("%s: %s (type %s) is typed", x.Pos(), x, info.typ)
+ unreachable()
+ }
+ check.recordTypeAndValue(x, info.mode, info.typ, info.val)
+ }
+}
+
+func (check *Checker) recordTypeAndValue(x ast.Expr, mode operandMode, typ Type, val exact.Value) {
+ assert(x != nil)
+ assert(typ != nil)
+ if mode == invalid {
+ return // omit
+ }
+ assert(typ != nil)
+ if mode == constant {
+ assert(val != nil)
+ assert(typ == Typ[Invalid] || isConstType(typ))
+ }
+ if m := check.Types; m != nil {
+ m[x] = TypeAndValue{mode, typ, val}
+ }
+}
+
+func (check *Checker) recordBuiltinType(f ast.Expr, sig *Signature) {
+ // f must be a (possibly parenthesized) identifier denoting a built-in
+ // (built-ins in package unsafe always produce a constant result and
+ // we don't record their signatures, so we don't see qualified idents
+ // here): record the signature for f and possible children.
+ for {
+ check.recordTypeAndValue(f, builtin, sig, nil)
+ switch p := f.(type) {
+ case *ast.Ident:
+ return // we're done
+ case *ast.ParenExpr:
+ f = p.X
+ default:
+ unreachable()
+ }
+ }
+}
+
+func (check *Checker) recordCommaOkTypes(x ast.Expr, a [2]Type) {
+ assert(x != nil)
+ if a[0] == nil || a[1] == nil {
+ return
+ }
+ assert(isTyped(a[0]) && isTyped(a[1]) && isBoolean(a[1]))
+ if m := check.Types; m != nil {
+ for {
+ tv := m[x]
+ assert(tv.Type != nil) // should have been recorded already
+ pos := x.Pos()
+ tv.Type = NewTuple(
+ NewVar(pos, check.pkg, "", a[0]),
+ NewVar(pos, check.pkg, "", a[1]),
+ )
+ m[x] = tv
+ // if x is a parenthesized expression (p.X), update p.X
+ p, _ := x.(*ast.ParenExpr)
+ if p == nil {
+ break
+ }
+ x = p.X
+ }
+ }
+}
+
+func (check *Checker) recordDef(id *ast.Ident, obj Object) {
+ assert(id != nil)
+ if m := check.Defs; m != nil {
+ m[id] = obj
+ }
+}
+
+func (check *Checker) recordUse(id *ast.Ident, obj Object) {
+ assert(id != nil)
+ assert(obj != nil)
+ if m := check.Uses; m != nil {
+ m[id] = obj
+ }
+}
+
+func (check *Checker) recordImplicit(node ast.Node, obj Object) {
+ assert(node != nil)
+ assert(obj != nil)
+ if m := check.Implicits; m != nil {
+ m[node] = obj
+ }
+}
+
+func (check *Checker) recordSelection(x *ast.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) {
+ assert(obj != nil && (recv == nil || len(index) > 0))
+ check.recordUse(x.Sel, obj)
+ // TODO(gri) Should we also call recordTypeAndValue?
+ if m := check.Selections; m != nil {
+ m[x] = &Selection{kind, recv, obj, index, indirect}
+ }
+}
+
+func (check *Checker) recordScope(node ast.Node, scope *Scope) {
+ assert(node != nil)
+ assert(scope != nil)
+ if m := check.Scopes; m != nil {
+ m[node] = scope
+ }
+}
diff --git a/src/go/types/check_test.go b/src/go/types/check_test.go
new file mode 100644
index 0000000000..25843927d0
--- /dev/null
+++ b/src/go/types/check_test.go
@@ -0,0 +1,297 @@
+// 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 a typechecker test harness. The packages specified
+// in tests are typechecked. Error messages reported by the typechecker are
+// compared against the error messages expected in the test files.
+//
+// Expected errors are indicated in the test files by putting a comment
+// of the form /* ERROR "rx" */ immediately following an offending token.
+// The harness will verify that an error matching the regular expression
+// rx is reported at that source position. Consecutive comments may be
+// used to indicate multiple errors for the same token position.
+//
+// For instance, the following test file indicates that a "not declared"
+// error should be reported for the undeclared variable x:
+//
+// package p
+// func f() {
+// _ = x /* ERROR "not declared" */ + 1
+// }
+
+// TODO(gri) Also collect strict mode errors of the form /* STRICT ... */
+// and test against strict mode.
+
+package types_test
+
+import (
+ "flag"
+ "go/ast"
+ "go/importer"
+ "go/parser"
+ "go/scanner"
+ "go/token"
+ "io/ioutil"
+ "regexp"
+ "strings"
+ "testing"
+
+ . "go/types"
+)
+
+var (
+ listErrors = flag.Bool("list", false, "list errors")
+ testFiles = flag.String("files", "", "space-separated list of test files")
+)
+
+// The test filenames do not end in .go so that they are invisible
+// to gofmt since they contain comments that must not change their
+// positions relative to surrounding tokens.
+
+// Each tests entry is list of files belonging to the same package.
+var tests = [][]string{
+ {"testdata/errors.src"},
+ {"testdata/importdecl0a.src", "testdata/importdecl0b.src"},
+ {"testdata/importdecl1a.src", "testdata/importdecl1b.src"},
+ {"testdata/cycles.src"},
+ {"testdata/cycles1.src"},
+ {"testdata/cycles2.src"},
+ {"testdata/cycles3.src"},
+ {"testdata/cycles4.src"},
+ {"testdata/init0.src"},
+ {"testdata/init1.src"},
+ {"testdata/init2.src"},
+ {"testdata/decls0.src"},
+ {"testdata/decls1.src"},
+ {"testdata/decls2a.src", "testdata/decls2b.src"},
+ {"testdata/decls3.src"},
+ {"testdata/const0.src"},
+ {"testdata/const1.src"},
+ {"testdata/constdecl.src"},
+ {"testdata/vardecl.src"},
+ {"testdata/expr0.src"},
+ {"testdata/expr1.src"},
+ {"testdata/expr2.src"},
+ {"testdata/expr3.src"},
+ {"testdata/methodsets.src"},
+ {"testdata/shifts.src"},
+ {"testdata/builtins.src"},
+ {"testdata/conversions.src"},
+ {"testdata/stmt0.src"},
+ {"testdata/stmt1.src"},
+ {"testdata/gotos.src"},
+ {"testdata/labels.src"},
+ {"testdata/issues.src"},
+ {"testdata/blank.src"},
+}
+
+var fset = token.NewFileSet()
+
+// Positioned errors are of the form filename:line:column: message .
+var posMsgRx = regexp.MustCompile(`^(.*:[0-9]+:[0-9]+): *(.*)`)
+
+// splitError splits an error's error message into a position string
+// and the actual error message. If there's no position information,
+// pos is the empty string, and msg is the entire error message.
+//
+func splitError(err error) (pos, msg string) {
+ msg = err.Error()
+ if m := posMsgRx.FindStringSubmatch(msg); len(m) == 3 {
+ pos = m[1]
+ msg = m[2]
+ }
+ return
+}
+
+func parseFiles(t *testing.T, filenames []string) ([]*ast.File, []error) {
+ var files []*ast.File
+ var errlist []error
+ for _, filename := range filenames {
+ file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors)
+ if file == nil {
+ t.Fatalf("%s: %s", filename, err)
+ }
+ files = append(files, file)
+ if err != nil {
+ if list, _ := err.(scanner.ErrorList); len(list) > 0 {
+ for _, err := range list {
+ errlist = append(errlist, err)
+ }
+ } else {
+ errlist = append(errlist, err)
+ }
+ }
+ }
+ return files, errlist
+}
+
+// ERROR comments must start with text `ERROR "rx"` or `ERROR rx` where
+// rx is a regular expression that matches the expected error message.
+// Space around "rx" or rx is ignored. Use the form `ERROR HERE "rx"`
+// for error messages that are located immediately after rather than
+// at a token's position.
+//
+var errRx = regexp.MustCompile(`^ *ERROR *(HERE)? *"?([^"]*)"?`)
+
+// errMap collects the regular expressions of ERROR comments found
+// in files and returns them as a map of error positions to error messages.
+//
+func errMap(t *testing.T, testname string, files []*ast.File) map[string][]string {
+ // map of position strings to lists of error message patterns
+ errmap := make(map[string][]string)
+
+ for _, file := range files {
+ filename := fset.Position(file.Package).Filename
+ src, err := ioutil.ReadFile(filename)
+ if err != nil {
+ t.Fatalf("%s: could not read %s", testname, filename)
+ }
+
+ var s scanner.Scanner
+ s.Init(fset.AddFile(filename, -1, len(src)), src, nil, scanner.ScanComments)
+ var prev token.Pos // position of last non-comment, non-semicolon token
+ var here token.Pos // position immediately after the token at position prev
+
+ scanFile:
+ for {
+ pos, tok, lit := s.Scan()
+ switch tok {
+ case token.EOF:
+ break scanFile
+ case token.COMMENT:
+ if lit[1] == '*' {
+ lit = lit[:len(lit)-2] // strip trailing */
+ }
+ if s := errRx.FindStringSubmatch(lit[2:]); len(s) == 3 {
+ pos := prev
+ if s[1] == "HERE" {
+ pos = here
+ }
+ p := fset.Position(pos).String()
+ errmap[p] = append(errmap[p], strings.TrimSpace(s[2]))
+ }
+ case token.SEMICOLON:
+ // ignore automatically inserted semicolon
+ if lit == "\n" {
+ continue scanFile
+ }
+ fallthrough
+ default:
+ prev = pos
+ var l int // token length
+ if tok.IsLiteral() {
+ l = len(lit)
+ } else {
+ l = len(tok.String())
+ }
+ here = prev + token.Pos(l)
+ }
+ }
+ }
+
+ return errmap
+}
+
+func eliminate(t *testing.T, errmap map[string][]string, errlist []error) {
+ for _, err := range errlist {
+ pos, gotMsg := splitError(err)
+ list := errmap[pos]
+ index := -1 // list index of matching message, if any
+ // we expect one of the messages in list to match the error at pos
+ for i, wantRx := range list {
+ rx, err := regexp.Compile(wantRx)
+ if err != nil {
+ t.Errorf("%s: %v", pos, err)
+ continue
+ }
+ if rx.MatchString(gotMsg) {
+ index = i
+ break
+ }
+ }
+ if index >= 0 {
+ // eliminate from list
+ if n := len(list) - 1; n > 0 {
+ // not the last entry - swap in last element and shorten list by 1
+ list[index] = list[n]
+ errmap[pos] = list[:n]
+ } else {
+ // last entry - remove list from map
+ delete(errmap, pos)
+ }
+ } else {
+ t.Errorf("%s: no error expected: %q", pos, gotMsg)
+ }
+ }
+}
+
+func checkFiles(t *testing.T, testfiles []string) {
+ // parse files and collect parser errors
+ files, errlist := parseFiles(t, testfiles)
+
+ pkgName := ""
+ if len(files) > 0 {
+ pkgName = files[0].Name.Name
+ }
+
+ if *listErrors && len(errlist) > 0 {
+ t.Errorf("--- %s:", pkgName)
+ for _, err := range errlist {
+ t.Error(err)
+ }
+ }
+
+ // typecheck and collect typechecker errors
+ var conf Config
+ conf.Importer = importer.Default()
+ conf.Error = func(err error) {
+ if *listErrors {
+ t.Error(err)
+ return
+ }
+ // Ignore secondary error messages starting with "\t";
+ // they are clarifying messages for a primary error.
+ if !strings.Contains(err.Error(), ": \t") {
+ errlist = append(errlist, err)
+ }
+ }
+ conf.Check(pkgName, fset, files, nil)
+
+ if *listErrors {
+ return
+ }
+
+ // match and eliminate errors;
+ // we are expecting the following errors
+ errmap := errMap(t, pkgName, files)
+ eliminate(t, errmap, errlist)
+
+ // there should be no expected errors left
+ if len(errmap) > 0 {
+ t.Errorf("--- %s: %d source positions with expected (but not reported) errors:", pkgName, len(errmap))
+ for pos, list := range errmap {
+ for _, rx := range list {
+ t.Errorf("%s: %q", pos, rx)
+ }
+ }
+ }
+}
+
+func TestCheck(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // Declare builtins for testing.
+ DefPredeclaredTestFuncs()
+
+ // If explicit test files are specified, only check those.
+ if files := *testFiles; files != "" {
+ checkFiles(t, strings.Split(files, " "))
+ return
+ }
+
+ // Otherwise, run all the tests.
+ for _, files := range tests {
+ checkFiles(t, files)
+ }
+}
diff --git a/src/go/types/conversions.go b/src/go/types/conversions.go
new file mode 100644
index 0000000000..0cf9953c4f
--- /dev/null
+++ b/src/go/types/conversions.go
@@ -0,0 +1,146 @@
+// 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 implements typechecking of conversions.
+
+package types
+
+import exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+
+// Conversion type-checks the conversion T(x).
+// The result is in x.
+func (check *Checker) conversion(x *operand, T Type) {
+ constArg := x.mode == constant
+
+ var ok bool
+ switch {
+ case constArg && isConstType(T):
+ // constant conversion
+ switch t := T.Underlying().(*Basic); {
+ case representableConst(x.val, check.conf, t.kind, &x.val):
+ ok = true
+ case x.isInteger() && isString(t):
+ codepoint := int64(-1)
+ if i, ok := exact.Int64Val(x.val); ok {
+ codepoint = i
+ }
+ // If codepoint < 0 the absolute value is too large (or unknown) for
+ // conversion. This is the same as converting any other out-of-range
+ // value - let string(codepoint) do the work.
+ x.val = exact.MakeString(string(codepoint))
+ ok = true
+ }
+ case x.convertibleTo(check.conf, T):
+ // non-constant conversion
+ x.mode = value
+ ok = true
+ }
+
+ if !ok {
+ check.errorf(x.pos(), "cannot convert %s to %s", x, T)
+ x.mode = invalid
+ return
+ }
+
+ // The conversion argument types are final. For untyped values the
+ // conversion provides the type, per the spec: "A constant may be
+ // given a type explicitly by a constant declaration or conversion,...".
+ final := x.typ
+ if isUntyped(x.typ) {
+ final = T
+ // - For conversions to interfaces, use the argument's default type.
+ // - For conversions of untyped constants to non-constant types, also
+ // use the default type (e.g., []byte("foo") should report string
+ // not []byte as type for the constant "foo").
+ // - Keep untyped nil for untyped nil arguments.
+ if IsInterface(T) || constArg && !isConstType(T) {
+ final = defaultType(x.typ)
+ }
+ check.updateExprType(x.expr, final, true)
+ }
+
+ x.typ = T
+}
+
+func (x *operand) convertibleTo(conf *Config, T Type) bool {
+ // "x is assignable to T"
+ if x.assignableTo(conf, T) {
+ return true
+ }
+
+ // "x's type and T have identical underlying types"
+ V := x.typ
+ Vu := V.Underlying()
+ Tu := T.Underlying()
+ if Identical(Vu, Tu) {
+ return true
+ }
+
+ // "x's type and T are unnamed pointer types and their pointer base types have identical underlying types"
+ if V, ok := V.(*Pointer); ok {
+ if T, ok := T.(*Pointer); ok {
+ if Identical(V.base.Underlying(), T.base.Underlying()) {
+ return true
+ }
+ }
+ }
+
+ // "x's type and T are both integer or floating point types"
+ if (isInteger(V) || isFloat(V)) && (isInteger(T) || isFloat(T)) {
+ return true
+ }
+
+ // "x's type and T are both complex types"
+ if isComplex(V) && isComplex(T) {
+ return true
+ }
+
+ // "x is an integer or a slice of bytes or runes and T is a string type"
+ if (isInteger(V) || isBytesOrRunes(Vu)) && isString(T) {
+ return true
+ }
+
+ // "x is a string and T is a slice of bytes or runes"
+ if isString(V) && isBytesOrRunes(Tu) {
+ return true
+ }
+
+ // package unsafe:
+ // "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer"
+ if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(T) {
+ return true
+ }
+ // "and vice versa"
+ if isUnsafePointer(V) && (isPointer(Tu) || isUintptr(Tu)) {
+ return true
+ }
+
+ return false
+}
+
+func isUintptr(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.kind == Uintptr
+}
+
+func isUnsafePointer(typ Type) bool {
+ // TODO(gri): Is this (typ.Underlying() instead of just typ) correct?
+ // The spec does not say so, but gc claims it is. See also
+ // issue 6326.
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.kind == UnsafePointer
+}
+
+func isPointer(typ Type) bool {
+ _, ok := typ.Underlying().(*Pointer)
+ return ok
+}
+
+func isBytesOrRunes(typ Type) bool {
+ if s, ok := typ.(*Slice); ok {
+ t, ok := s.elem.Underlying().(*Basic)
+ return ok && (t.kind == Byte || t.kind == Rune)
+ }
+ return false
+}
diff --git a/src/go/types/decl.go b/src/go/types/decl.go
new file mode 100644
index 0000000000..c2c18ecd06
--- /dev/null
+++ b/src/go/types/decl.go
@@ -0,0 +1,418 @@
+// Copyright 2014 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 types
+
+import (
+ "go/ast"
+ exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+ "go/token"
+)
+
+func (check *Checker) reportAltDecl(obj Object) {
+ if pos := obj.Pos(); pos.IsValid() {
+ // We use "other" rather than "previous" here because
+ // the first declaration seen may not be textually
+ // earlier in the source.
+ check.errorf(pos, "\tother declaration of %s", obj.Name()) // secondary error, \t indented
+ }
+}
+
+func (check *Checker) declare(scope *Scope, id *ast.Ident, obj Object) {
+ // spec: "The blank identifier, represented by the underscore
+ // character _, may be used in a declaration like any other
+ // identifier but the declaration does not introduce a new
+ // binding."
+ if obj.Name() != "_" {
+ if alt := scope.Insert(obj); alt != nil {
+ check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name())
+ check.reportAltDecl(alt)
+ return
+ }
+ }
+ if id != nil {
+ check.recordDef(id, obj)
+ }
+}
+
+// objDecl type-checks the declaration of obj in its respective (file) context.
+// See check.typ for the details on def and path.
+func (check *Checker) objDecl(obj Object, def *Named, path []*TypeName) {
+ if obj.Type() != nil {
+ return // already checked - nothing to do
+ }
+
+ if trace {
+ check.trace(obj.Pos(), "-- declaring %s", obj.Name())
+ check.indent++
+ defer func() {
+ check.indent--
+ check.trace(obj.Pos(), "=> %s", obj)
+ }()
+ }
+
+ d := check.objMap[obj]
+ if d == nil {
+ check.dump("%s: %s should have been declared", obj.Pos(), obj.Name())
+ unreachable()
+ }
+
+ // save/restore current context and setup object context
+ defer func(ctxt context) {
+ check.context = ctxt
+ }(check.context)
+ check.context = context{
+ scope: d.file,
+ }
+
+ // Const and var declarations must not have initialization
+ // cycles. We track them by remembering the current declaration
+ // in check.decl. Initialization expressions depending on other
+ // consts, vars, or functions, add dependencies to the current
+ // check.decl.
+ switch obj := obj.(type) {
+ case *Const:
+ check.decl = d // new package-level const decl
+ check.constDecl(obj, d.typ, d.init)
+ case *Var:
+ check.decl = d // new package-level var decl
+ check.varDecl(obj, d.lhs, d.typ, d.init)
+ case *TypeName:
+ // invalid recursive types are detected via path
+ check.typeDecl(obj, d.typ, def, path)
+ case *Func:
+ // functions may be recursive - no need to track dependencies
+ check.funcDecl(obj, d)
+ default:
+ unreachable()
+ }
+}
+
+func (check *Checker) constDecl(obj *Const, typ, init ast.Expr) {
+ assert(obj.typ == nil)
+
+ if obj.visited {
+ obj.typ = Typ[Invalid]
+ return
+ }
+ obj.visited = true
+
+ // use the correct value of iota
+ assert(check.iota == nil)
+ check.iota = obj.val
+ defer func() { check.iota = nil }()
+
+ // provide valid constant value under all circumstances
+ obj.val = exact.MakeUnknown()
+
+ // determine type, if any
+ if typ != nil {
+ t := check.typ(typ)
+ if !isConstType(t) {
+ check.errorf(typ.Pos(), "invalid constant type %s", t)
+ obj.typ = Typ[Invalid]
+ return
+ }
+ obj.typ = t
+ }
+
+ // check initialization
+ var x operand
+ if init != nil {
+ check.expr(&x, init)
+ }
+ check.initConst(obj, &x)
+}
+
+func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init ast.Expr) {
+ assert(obj.typ == nil)
+
+ if obj.visited {
+ obj.typ = Typ[Invalid]
+ return
+ }
+ obj.visited = true
+
+ // var declarations cannot use iota
+ assert(check.iota == nil)
+
+ // determine type, if any
+ if typ != nil {
+ obj.typ = check.typ(typ)
+ }
+
+ // check initialization
+ if init == nil {
+ if typ == nil {
+ // error reported before by arityMatch
+ obj.typ = Typ[Invalid]
+ }
+ return
+ }
+
+ if lhs == nil || len(lhs) == 1 {
+ assert(lhs == nil || lhs[0] == obj)
+ var x operand
+ check.expr(&x, init)
+ check.initVar(obj, &x, false)
+ return
+ }
+
+ if debug {
+ // obj must be one of lhs
+ found := false
+ for _, lhs := range lhs {
+ if obj == lhs {
+ found = true
+ break
+ }
+ }
+ if !found {
+ panic("inconsistent lhs")
+ }
+ }
+ check.initVars(lhs, []ast.Expr{init}, token.NoPos)
+}
+
+// underlying returns the underlying type of typ; possibly by following
+// forward chains of named types. Such chains only exist while named types
+// are incomplete.
+func underlying(typ Type) Type {
+ for {
+ n, _ := typ.(*Named)
+ if n == nil {
+ break
+ }
+ typ = n.underlying
+ }
+ return typ
+}
+
+func (n *Named) setUnderlying(typ Type) {
+ if n != nil {
+ n.underlying = typ
+ }
+}
+
+func (check *Checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, path []*TypeName) {
+ assert(obj.typ == nil)
+
+ // type declarations cannot use iota
+ assert(check.iota == nil)
+
+ named := &Named{obj: obj}
+ def.setUnderlying(named)
+ obj.typ = named // make sure recursive type declarations terminate
+
+ // determine underlying type of named
+ check.typExpr(typ, named, append(path, obj))
+
+ // The underlying type of named may be itself a named type that is
+ // incomplete:
+ //
+ // type (
+ // A B
+ // B *C
+ // C A
+ // )
+ //
+ // The type of C is the (named) type of A which is incomplete,
+ // and which has as its underlying type the named type B.
+ // Determine the (final, unnamed) underlying type by resolving
+ // any forward chain (they always end in an unnamed type).
+ named.underlying = underlying(named.underlying)
+
+ // check and add associated methods
+ // TODO(gri) It's easy to create pathological cases where the
+ // current approach is incorrect: In general we need to know
+ // and add all methods _before_ type-checking the type.
+ // See http://play.golang.org/p/WMpE0q2wK8
+ check.addMethodDecls(obj)
+}
+
+func (check *Checker) addMethodDecls(obj *TypeName) {
+ // get associated methods
+ methods := check.methods[obj.name]
+ if len(methods) == 0 {
+ return // no methods
+ }
+ delete(check.methods, obj.name)
+
+ // use an objset to check for name conflicts
+ var mset objset
+
+ // spec: "If the base type is a struct type, the non-blank method
+ // and field names must be distinct."
+ base := obj.typ.(*Named)
+ if t, _ := base.underlying.(*Struct); t != nil {
+ for _, fld := range t.fields {
+ if fld.name != "_" {
+ assert(mset.insert(fld) == nil)
+ }
+ }
+ }
+
+ // Checker.Files may be called multiple times; additional package files
+ // may add methods to already type-checked types. Add pre-existing methods
+ // so that we can detect redeclarations.
+ for _, m := range base.methods {
+ assert(m.name != "_")
+ assert(mset.insert(m) == nil)
+ }
+
+ // type-check methods
+ for _, m := range methods {
+ // spec: "For a base type, the non-blank names of methods bound
+ // to it must be unique."
+ if m.name != "_" {
+ if alt := mset.insert(m); alt != nil {
+ switch alt.(type) {
+ case *Var:
+ check.errorf(m.pos, "field and method with the same name %s", m.name)
+ case *Func:
+ check.errorf(m.pos, "method %s already declared for %s", m.name, base)
+ default:
+ unreachable()
+ }
+ check.reportAltDecl(alt)
+ continue
+ }
+ }
+ check.objDecl(m, nil, nil)
+ // methods with blank _ names cannot be found - don't keep them
+ if m.name != "_" {
+ base.methods = append(base.methods, m)
+ }
+ }
+}
+
+func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
+ assert(obj.typ == nil)
+
+ // func declarations cannot use iota
+ assert(check.iota == nil)
+
+ sig := new(Signature)
+ obj.typ = sig // guard against cycles
+ fdecl := decl.fdecl
+ check.funcType(sig, fdecl.Recv, fdecl.Type)
+ if sig.recv == nil && obj.name == "init" && (sig.params.Len() > 0 || sig.results.Len() > 0) {
+ check.errorf(fdecl.Pos(), "func init must have no arguments and no return values")
+ // ok to continue
+ }
+
+ // function body must be type-checked after global declarations
+ // (functions implemented elsewhere have no body)
+ if !check.conf.IgnoreFuncBodies && fdecl.Body != nil {
+ check.later(obj.name, decl, sig, fdecl.Body)
+ }
+}
+
+func (check *Checker) declStmt(decl ast.Decl) {
+ pkg := check.pkg
+
+ switch d := decl.(type) {
+ case *ast.BadDecl:
+ // ignore
+
+ case *ast.GenDecl:
+ var last *ast.ValueSpec // last ValueSpec with type or init exprs seen
+ for iota, spec := range d.Specs {
+ switch s := spec.(type) {
+ case *ast.ValueSpec:
+ switch d.Tok {
+ case token.CONST:
+ // determine which init exprs to use
+ switch {
+ case s.Type != nil || len(s.Values) > 0:
+ last = s
+ case last == nil:
+ last = new(ast.ValueSpec) // make sure last exists
+ }
+
+ // declare all constants
+ lhs := make([]*Const, len(s.Names))
+ for i, name := range s.Names {
+ obj := NewConst(name.Pos(), pkg, name.Name, nil, exact.MakeInt64(int64(iota)))
+ lhs[i] = obj
+
+ var init ast.Expr
+ if i < len(last.Values) {
+ init = last.Values[i]
+ }
+
+ check.constDecl(obj, last.Type, init)
+ }
+
+ check.arityMatch(s, last)
+
+ for i, name := range s.Names {
+ check.declare(check.scope, name, lhs[i])
+ }
+
+ case token.VAR:
+ lhs0 := make([]*Var, len(s.Names))
+ for i, name := range s.Names {
+ lhs0[i] = NewVar(name.Pos(), pkg, name.Name, nil)
+ }
+
+ // initialize all variables
+ for i, obj := range lhs0 {
+ var lhs []*Var
+ var init ast.Expr
+ switch len(s.Values) {
+ case len(s.Names):
+ // lhs and rhs match
+ init = s.Values[i]
+ case 1:
+ // rhs is expected to be a multi-valued expression
+ lhs = lhs0
+ init = s.Values[0]
+ default:
+ if i < len(s.Values) {
+ init = s.Values[i]
+ }
+ }
+ check.varDecl(obj, lhs, s.Type, init)
+ if len(s.Values) == 1 {
+ // If we have a single lhs variable we are done either way.
+ // If we have a single rhs expression, it must be a multi-
+ // valued expression, in which case handling the first lhs
+ // variable will cause all lhs variables to have a type
+ // assigned, and we are done as well.
+ if debug {
+ for _, obj := range lhs0 {
+ assert(obj.typ != nil)
+ }
+ }
+ break
+ }
+ }
+
+ check.arityMatch(s, nil)
+
+ // declare all variables
+ // (only at this point are the variable scopes (parents) set)
+ for i, name := range s.Names {
+ check.declare(check.scope, name, lhs0[i])
+ }
+
+ default:
+ check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
+ }
+
+ case *ast.TypeSpec:
+ obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
+ check.declare(check.scope, s.Name, obj)
+ check.typeDecl(obj, s.Type, nil, nil)
+
+ default:
+ check.invalidAST(s.Pos(), "const, type, or var declaration expected")
+ }
+ }
+
+ default:
+ check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
+ }
+}
diff --git a/src/go/types/errors.go b/src/go/types/errors.go
new file mode 100644
index 0000000000..0a9dd0e19b
--- /dev/null
+++ b/src/go/types/errors.go
@@ -0,0 +1,96 @@
+// 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 implements various error reporters.
+
+package types
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "strings"
+)
+
+func assert(p bool) {
+ if !p {
+ panic("assertion failed")
+ }
+}
+
+func unreachable() {
+ panic("unreachable")
+}
+
+func (check *Checker) sprintf(format string, args ...interface{}) string {
+ for i, arg := range args {
+ switch a := arg.(type) {
+ case nil:
+ arg = ""
+ case operand:
+ panic("internal error: should always pass *operand")
+ case *operand:
+ arg = operandString(check.pkg, a)
+ case token.Pos:
+ arg = check.fset.Position(a).String()
+ case ast.Expr:
+ arg = ExprString(a)
+ case Object:
+ arg = ObjectString(check.pkg, a)
+ case Type:
+ arg = TypeString(check.pkg, a)
+ }
+ args[i] = arg
+ }
+ return fmt.Sprintf(format, args...)
+}
+
+func (check *Checker) trace(pos token.Pos, format string, args ...interface{}) {
+ fmt.Printf("%s:\t%s%s\n",
+ check.fset.Position(pos),
+ strings.Repeat(". ", check.indent),
+ check.sprintf(format, args...),
+ )
+}
+
+// dump is only needed for debugging
+func (check *Checker) dump(format string, args ...interface{}) {
+ fmt.Println(check.sprintf(format, args...))
+}
+
+func (check *Checker) err(pos token.Pos, msg string, soft bool) {
+ err := Error{check.fset, pos, msg, soft}
+ if check.firstErr == nil {
+ check.firstErr = err
+ }
+ f := check.conf.Error
+ if f == nil {
+ panic(bailout{}) // report only first error
+ }
+ f(err)
+}
+
+func (check *Checker) error(pos token.Pos, msg string) {
+ check.err(pos, msg, false)
+}
+
+func (check *Checker) errorf(pos token.Pos, format string, args ...interface{}) {
+ check.err(pos, check.sprintf(format, args...), false)
+}
+
+func (check *Checker) softErrorf(pos token.Pos, format string, args ...interface{}) {
+ check.err(pos, check.sprintf(format, args...), true)
+}
+
+func (check *Checker) invalidAST(pos token.Pos, format string, args ...interface{}) {
+ check.errorf(pos, "invalid AST: "+format, args...)
+}
+
+func (check *Checker) invalidArg(pos token.Pos, format string, args ...interface{}) {
+ check.errorf(pos, "invalid argument: "+format, args...)
+}
+
+func (check *Checker) invalidOp(pos token.Pos, format string, args ...interface{}) {
+ check.errorf(pos, "invalid operation: "+format, args...)
+}
diff --git a/src/go/types/eval.go b/src/go/types/eval.go
new file mode 100644
index 0000000000..7fa319e169
--- /dev/null
+++ b/src/go/types/eval.go
@@ -0,0 +1,95 @@
+// 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 New, Eval and EvalNode.
+
+package types
+
+import (
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/token"
+)
+
+// New is a convenience function to create a new type from a given
+// expression or type literal string evaluated in Universe scope.
+// New(str) is shorthand for Eval(str, nil, nil), but only returns
+// the type result, and panics in case of an error.
+// Position info for objects in the result type is undefined.
+//
+func New(str string) Type {
+ tv, err := Eval(str, nil, nil)
+ if err != nil {
+ panic(err)
+ }
+ return tv.Type
+}
+
+// Eval returns the type and, if constant, the value for the
+// expression or type literal string str evaluated in scope.
+// If the expression contains function literals, the function
+// bodies are ignored (though they must be syntactically correct).
+//
+// If pkg == nil, the Universe scope is used and the provided
+// scope is ignored. Otherwise, the scope must belong to the
+// package (either the package scope, or nested within the
+// package scope).
+//
+// An error is returned if the scope is incorrect, the string
+// has syntax errors, or if it cannot be evaluated in the scope.
+// Position info for objects in the result type is undefined.
+//
+// Note: Eval should not be used instead of running Check to compute
+// types and values, but in addition to Check. Eval will re-evaluate
+// its argument each time, and it also does not know about the context
+// in which an expression is used (e.g., an assignment). Thus, top-
+// level untyped constants will return an untyped type rather then the
+// respective context-specific type.
+//
+func Eval(str string, pkg *Package, scope *Scope) (TypeAndValue, error) {
+ node, err := parser.ParseExpr(str)
+ if err != nil {
+ return TypeAndValue{}, err
+ }
+
+ // Create a file set that looks structurally identical to the
+ // one created by parser.ParseExpr for correct error positions.
+ fset := token.NewFileSet()
+ fset.AddFile("", len(str), fset.Base()).SetLinesForContent([]byte(str))
+
+ return EvalNode(fset, node, pkg, scope)
+}
+
+// EvalNode is like Eval but instead of string it accepts
+// an expression node and respective file set.
+//
+// An error is returned if the scope is incorrect
+// if the node cannot be evaluated in the scope.
+//
+func EvalNode(fset *token.FileSet, node ast.Expr, pkg *Package, scope *Scope) (tv TypeAndValue, err error) {
+ // verify package/scope relationship
+ if pkg == nil {
+ scope = Universe
+ } else {
+ s := scope
+ for s != nil && s != pkg.scope {
+ s = s.parent
+ }
+ // s == nil || s == pkg.scope
+ if s == nil {
+ return TypeAndValue{}, fmt.Errorf("scope does not belong to package %s", pkg.name)
+ }
+ }
+
+ // initialize checker
+ check := NewChecker(nil, fset, pkg, nil)
+ check.scope = scope
+ defer check.handleBailout(&err)
+
+ // evaluate node
+ var x operand
+ check.rawExpr(&x, node, nil)
+ return TypeAndValue{x.mode, x.typ, x.val}, nil
+}
diff --git a/src/go/types/eval_test.go b/src/go/types/eval_test.go
new file mode 100644
index 0000000000..bc27a8bb23
--- /dev/null
+++ b/src/go/types/eval_test.go
@@ -0,0 +1,152 @@
+// 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 Eval.
+
+package types_test
+
+import (
+ "go/ast"
+ "go/importer"
+ "go/parser"
+ "go/token"
+ "strings"
+ "testing"
+
+ _ "go/internal/gcimporter"
+ . "go/types"
+)
+
+func testEval(t *testing.T, pkg *Package, scope *Scope, str string, typ Type, typStr, valStr string) {
+ gotTv, err := Eval(str, pkg, scope)
+ if err != nil {
+ t.Errorf("Eval(%q) failed: %s", str, err)
+ return
+ }
+ if gotTv.Type == nil {
+ t.Errorf("Eval(%q) got nil type but no error", str)
+ return
+ }
+
+ // compare types
+ if typ != nil {
+ // we have a type, check identity
+ if !Identical(gotTv.Type, typ) {
+ t.Errorf("Eval(%q) got type %s, want %s", str, gotTv.Type, typ)
+ return
+ }
+ } else {
+ // we have a string, compare type string
+ gotStr := gotTv.Type.String()
+ if gotStr != typStr {
+ t.Errorf("Eval(%q) got type %s, want %s", str, gotStr, typStr)
+ return
+ }
+ }
+
+ // compare values
+ gotStr := ""
+ if gotTv.Value != nil {
+ gotStr = gotTv.Value.String()
+ }
+ if gotStr != valStr {
+ t.Errorf("Eval(%q) got value %s, want %s", str, gotStr, valStr)
+ }
+}
+
+func TestEvalBasic(t *testing.T) {
+ for _, typ := range Typ[Bool : String+1] {
+ testEval(t, nil, nil, typ.Name(), typ, "", "")
+ }
+}
+
+func TestEvalComposite(t *testing.T) {
+ for _, test := range independentTestTypes {
+ testEval(t, nil, nil, test.src, nil, test.str, "")
+ }
+}
+
+func TestEvalArith(t *testing.T) {
+ var tests = []string{
+ `true`,
+ `false == false`,
+ `12345678 + 87654321 == 99999999`,
+ `10 * 20 == 200`,
+ `(1<<1000)*2 >> 100 == 2<<900`,
+ `"foo" + "bar" == "foobar"`,
+ `"abc" <= "bcd"`,
+ `len([10]struct{}{}) == 2*5`,
+ }
+ for _, test := range tests {
+ testEval(t, nil, nil, test, Typ[UntypedBool], "", "true")
+ }
+}
+
+func TestEvalContext(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ src := `
+package p
+import "fmt"
+import m "math"
+const c = 3.0
+type T []int
+func f(a int, s string) float64 {
+ fmt.Println("calling f")
+ _ = m.Pi // use package math
+ const d int = c + 1
+ var x int
+ x = a + len(s)
+ return float64(x)
+}
+`
+ fset := token.NewFileSet()
+ file, err := parser.ParseFile(fset, "p", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ conf := Config{Importer: importer.Default()}
+ pkg, err := conf.Check("p", fset, []*ast.File{file}, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ pkgScope := pkg.Scope()
+ if n := pkgScope.NumChildren(); n != 1 {
+ t.Fatalf("got %d file scopes, want 1", n)
+ }
+
+ fileScope := pkgScope.Child(0)
+ if n := fileScope.NumChildren(); n != 1 {
+ t.Fatalf("got %d functions scopes, want 1", n)
+ }
+
+ funcScope := fileScope.Child(0)
+
+ var tests = []string{
+ `true => true, untyped bool`,
+ `fmt.Println => , func(a ...interface{}) (n int, err error)`,
+ `c => 3, untyped float`,
+ `T => , p.T`,
+ `a => , int`,
+ `s => , string`,
+ `d => 4, int`,
+ `x => , int`,
+ `d/c => 1, int`,
+ `c/2 => 3/2, untyped float`,
+ `m.Pi < m.E => false, untyped bool`,
+ }
+ for _, test := range tests {
+ str, typ := split(test, ", ")
+ str, val := split(str, "=>")
+ testEval(t, pkg, funcScope, str, nil, typ, val)
+ }
+}
+
+// split splits string s at the first occurrence of s.
+func split(s, sep string) (string, string) {
+ i := strings.Index(s, sep)
+ return strings.TrimSpace(s[:i]), strings.TrimSpace(s[i+len(sep):])
+}
diff --git a/src/go/types/expr.go b/src/go/types/expr.go
new file mode 100644
index 0000000000..f91d89e8b8
--- /dev/null
+++ b/src/go/types/expr.go
@@ -0,0 +1,1482 @@
+// 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 implements typechecking of expressions.
+
+package types
+
+import (
+ "fmt"
+ "go/ast"
+ exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+ "go/token"
+ "math"
+)
+
+/*
+Basic algorithm:
+
+Expressions are checked recursively, top down. Expression checker functions
+are generally of the form:
+
+ func f(x *operand, e *ast.Expr, ...)
+
+where e is the expression to be checked, and x is the result of the check.
+The check performed by f may fail in which case x.mode == invalid, and
+related error messages will have been issued by f.
+
+If a hint argument is present, it is the composite literal element type
+of an outer composite literal; it is used to type-check composite literal
+elements that have no explicit type specification in the source
+(e.g.: []T{{...}, {...}}, the hint is the type T in this case).
+
+All expressions are checked via rawExpr, which dispatches according
+to expression kind. Upon returning, rawExpr is recording the types and
+constant values for all expressions that have an untyped type (those types
+may change on the way up in the expression tree). Usually these are constants,
+but the results of comparisons or non-constant shifts of untyped constants
+may also be untyped, but not constant.
+
+Untyped expressions may eventually become fully typed (i.e., not untyped),
+typically when the value is assigned to a variable, or is used otherwise.
+The updateExprType method is used to record this final type and update
+the recorded types: the type-checked expression tree is again traversed down,
+and the new type is propagated as needed. Untyped constant expression values
+that become fully typed must now be representable by the full type (constant
+sub-expression trees are left alone except for their roots). This mechanism
+ensures that a client sees the actual (run-time) type an untyped value would
+have. It also permits type-checking of lhs shift operands "as if the shift
+were not present": when updateExprType visits an untyped lhs shift operand
+and assigns it it's final type, that type must be an integer type, and a
+constant lhs must be representable as an integer.
+
+When an expression gets its final type, either on the way out from rawExpr,
+on the way down in updateExprType, or at the end of the type checker run,
+the type (and constant value, if any) is recorded via Info.Types, if present.
+*/
+
+type opPredicates map[token.Token]func(Type) bool
+
+var unaryOpPredicates = opPredicates{
+ token.ADD: isNumeric,
+ token.SUB: isNumeric,
+ token.XOR: isInteger,
+ token.NOT: isBoolean,
+}
+
+func (check *Checker) op(m opPredicates, x *operand, op token.Token) bool {
+ if pred := m[op]; pred != nil {
+ if !pred(x.typ) {
+ check.invalidOp(x.pos(), "operator %s not defined for %s", op, x)
+ return false
+ }
+ } else {
+ check.invalidAST(x.pos(), "unknown operator %s", op)
+ return false
+ }
+ return true
+}
+
+func (check *Checker) unary(x *operand, op token.Token) {
+ switch op {
+ case token.AND:
+ // spec: "As an exception to the addressability
+ // requirement x may also be a composite literal."
+ if _, ok := unparen(x.expr).(*ast.CompositeLit); !ok && x.mode != variable {
+ check.invalidOp(x.pos(), "cannot take address of %s", x)
+ x.mode = invalid
+ return
+ }
+ x.mode = value
+ x.typ = &Pointer{base: x.typ}
+ return
+
+ case token.ARROW:
+ typ, ok := x.typ.Underlying().(*Chan)
+ if !ok {
+ check.invalidOp(x.pos(), "cannot receive from non-channel %s", x)
+ x.mode = invalid
+ return
+ }
+ if typ.dir == SendOnly {
+ check.invalidOp(x.pos(), "cannot receive from send-only channel %s", x)
+ x.mode = invalid
+ return
+ }
+ x.mode = commaok
+ x.typ = typ.elem
+ check.hasCallOrRecv = true
+ return
+ }
+
+ if !check.op(unaryOpPredicates, x, op) {
+ x.mode = invalid
+ return
+ }
+
+ if x.mode == constant {
+ typ := x.typ.Underlying().(*Basic)
+ var prec uint
+ if isUnsigned(typ) {
+ prec = uint(check.conf.sizeof(typ) * 8)
+ }
+ x.val = exact.UnaryOp(op, x.val, prec)
+ // Typed constants must be representable in
+ // their type after each constant operation.
+ if isTyped(typ) {
+ check.representable(x, typ)
+ }
+ return
+ }
+
+ x.mode = value
+ // x.typ remains unchanged
+}
+
+func isShift(op token.Token) bool {
+ return op == token.SHL || op == token.SHR
+}
+
+func isComparison(op token.Token) bool {
+ // Note: tokens are not ordered well to make this much easier
+ switch op {
+ case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ:
+ return true
+ }
+ return false
+}
+
+func fitsFloat32(x exact.Value) bool {
+ f32, _ := exact.Float32Val(x)
+ f := float64(f32)
+ return !math.IsInf(f, 0)
+}
+
+func roundFloat32(x exact.Value) exact.Value {
+ f32, _ := exact.Float32Val(x)
+ f := float64(f32)
+ if !math.IsInf(f, 0) {
+ return exact.MakeFloat64(f)
+ }
+ return nil
+}
+
+func fitsFloat64(x exact.Value) bool {
+ f, _ := exact.Float64Val(x)
+ return !math.IsInf(f, 0)
+}
+
+func roundFloat64(x exact.Value) exact.Value {
+ f, _ := exact.Float64Val(x)
+ if !math.IsInf(f, 0) {
+ return exact.MakeFloat64(f)
+ }
+ return nil
+}
+
+// representableConst reports whether x can be represented as
+// value of the given basic type kind and for the configuration
+// provided (only needed for int/uint sizes).
+//
+// If rounded != nil, *rounded is set to the rounded value of x for
+// representable floating-point values; it is left alone otherwise.
+// It is ok to provide the addressof the first argument for rounded.
+func representableConst(x exact.Value, conf *Config, as BasicKind, rounded *exact.Value) bool {
+ switch x.Kind() {
+ case exact.Unknown:
+ return true
+
+ case exact.Bool:
+ return as == Bool || as == UntypedBool
+
+ case exact.Int:
+ if x, ok := exact.Int64Val(x); ok {
+ switch as {
+ case Int:
+ var s = uint(conf.sizeof(Typ[as])) * 8
+ return int64(-1)<<(s-1) <= x && x <= int64(1)<<(s-1)-1
+ case Int8:
+ const s = 8
+ return -1<<(s-1) <= x && x <= 1<<(s-1)-1
+ case Int16:
+ const s = 16
+ return -1<<(s-1) <= x && x <= 1<<(s-1)-1
+ case Int32:
+ const s = 32
+ return -1<<(s-1) <= x && x <= 1<<(s-1)-1
+ case Int64:
+ return true
+ case Uint, Uintptr:
+ if s := uint(conf.sizeof(Typ[as])) * 8; s < 64 {
+ return 0 <= x && x <= int64(1)<= 0 && n <= int(s)
+ case Uint64:
+ return exact.Sign(x) >= 0 && n <= 64
+ case Float32, Complex64:
+ if rounded == nil {
+ return fitsFloat32(x)
+ }
+ r := roundFloat32(x)
+ if r != nil {
+ *rounded = r
+ return true
+ }
+ case Float64, Complex128:
+ if rounded == nil {
+ return fitsFloat64(x)
+ }
+ r := roundFloat64(x)
+ if r != nil {
+ *rounded = r
+ return true
+ }
+ case UntypedInt, UntypedFloat, UntypedComplex:
+ return true
+ }
+
+ case exact.Float:
+ switch as {
+ case Float32, Complex64:
+ if rounded == nil {
+ return fitsFloat32(x)
+ }
+ r := roundFloat32(x)
+ if r != nil {
+ *rounded = r
+ return true
+ }
+ case Float64, Complex128:
+ if rounded == nil {
+ return fitsFloat64(x)
+ }
+ r := roundFloat64(x)
+ if r != nil {
+ *rounded = r
+ return true
+ }
+ case UntypedFloat, UntypedComplex:
+ return true
+ }
+
+ case exact.Complex:
+ switch as {
+ case Complex64:
+ if rounded == nil {
+ return fitsFloat32(exact.Real(x)) && fitsFloat32(exact.Imag(x))
+ }
+ re := roundFloat32(exact.Real(x))
+ im := roundFloat32(exact.Imag(x))
+ if re != nil && im != nil {
+ *rounded = exact.BinaryOp(re, token.ADD, exact.MakeImag(im))
+ return true
+ }
+ case Complex128:
+ if rounded == nil {
+ return fitsFloat64(exact.Real(x)) && fitsFloat64(exact.Imag(x))
+ }
+ re := roundFloat64(exact.Real(x))
+ im := roundFloat64(exact.Imag(x))
+ if re != nil && im != nil {
+ *rounded = exact.BinaryOp(re, token.ADD, exact.MakeImag(im))
+ return true
+ }
+ case UntypedComplex:
+ return true
+ }
+
+ case exact.String:
+ return as == String || as == UntypedString
+
+ default:
+ unreachable()
+ }
+
+ return false
+}
+
+// representable checks that a constant operand is representable in the given basic type.
+func (check *Checker) representable(x *operand, typ *Basic) {
+ assert(x.mode == constant)
+ if !representableConst(x.val, check.conf, typ.kind, &x.val) {
+ var msg string
+ if isNumeric(x.typ) && isNumeric(typ) {
+ // numeric conversion : error msg
+ //
+ // integer -> integer : overflows
+ // integer -> float : overflows (actually not possible)
+ // float -> integer : truncated
+ // float -> float : overflows
+ //
+ if !isInteger(x.typ) && isInteger(typ) {
+ msg = "%s truncated to %s"
+ } else {
+ msg = "%s overflows %s"
+ }
+ } else {
+ msg = "cannot convert %s to %s"
+ }
+ check.errorf(x.pos(), msg, x, typ)
+ x.mode = invalid
+ }
+}
+
+// updateExprType updates the type of x to typ and invokes itself
+// recursively for the operands of x, depending on expression kind.
+// If typ is still an untyped and not the final type, updateExprType
+// only updates the recorded untyped type for x and possibly its
+// operands. Otherwise (i.e., typ is not an untyped type anymore,
+// or it is the final type for x), the type and value are recorded.
+// Also, if x is a constant, it must be representable as a value of typ,
+// and if x is the (formerly untyped) lhs operand of a non-constant
+// shift, it must be an integer value.
+//
+func (check *Checker) updateExprType(x ast.Expr, typ Type, final bool) {
+ old, found := check.untyped[x]
+ if !found {
+ return // nothing to do
+ }
+
+ // update operands of x if necessary
+ switch x := x.(type) {
+ case *ast.BadExpr,
+ *ast.FuncLit,
+ *ast.CompositeLit,
+ *ast.IndexExpr,
+ *ast.SliceExpr,
+ *ast.TypeAssertExpr,
+ *ast.StarExpr,
+ *ast.KeyValueExpr,
+ *ast.ArrayType,
+ *ast.StructType,
+ *ast.FuncType,
+ *ast.InterfaceType,
+ *ast.MapType,
+ *ast.ChanType:
+ // These expression are never untyped - nothing to do.
+ // The respective sub-expressions got their final types
+ // upon assignment or use.
+ if debug {
+ check.dump("%s: found old type(%s): %s (new: %s)", x.Pos(), x, old.typ, typ)
+ unreachable()
+ }
+ return
+
+ case *ast.CallExpr:
+ // Resulting in an untyped constant (e.g., built-in complex).
+ // The respective calls take care of calling updateExprType
+ // for the arguments if necessary.
+
+ case *ast.Ident, *ast.BasicLit, *ast.SelectorExpr:
+ // An identifier denoting a constant, a constant literal,
+ // or a qualified identifier (imported untyped constant).
+ // No operands to take care of.
+
+ case *ast.ParenExpr:
+ check.updateExprType(x.X, typ, final)
+
+ case *ast.UnaryExpr:
+ // If x is a constant, the operands were constants.
+ // They don't need to be updated since they never
+ // get "materialized" into a typed value; and they
+ // will be processed at the end of the type check.
+ if old.val != nil {
+ break
+ }
+ check.updateExprType(x.X, typ, final)
+
+ case *ast.BinaryExpr:
+ if old.val != nil {
+ break // see comment for unary expressions
+ }
+ if isComparison(x.Op) {
+ // The result type is independent of operand types
+ // and the operand types must have final types.
+ } else if isShift(x.Op) {
+ // The result type depends only on lhs operand.
+ // The rhs type was updated when checking the shift.
+ check.updateExprType(x.X, typ, final)
+ } else {
+ // The operand types match the result type.
+ check.updateExprType(x.X, typ, final)
+ check.updateExprType(x.Y, typ, final)
+ }
+
+ default:
+ unreachable()
+ }
+
+ // If the new type is not final and still untyped, just
+ // update the recorded type.
+ if !final && isUntyped(typ) {
+ old.typ = typ.Underlying().(*Basic)
+ check.untyped[x] = old
+ return
+ }
+
+ // Otherwise we have the final (typed or untyped type).
+ // Remove it from the map of yet untyped expressions.
+ delete(check.untyped, x)
+
+ // If x is the lhs of a shift, its final type must be integer.
+ // We already know from the shift check that it is representable
+ // as an integer if it is a constant.
+ if old.isLhs && !isInteger(typ) {
+ check.invalidOp(x.Pos(), "shifted operand %s (type %s) must be integer", x, typ)
+ return
+ }
+
+ // Everything's fine, record final type and value for x.
+ check.recordTypeAndValue(x, old.mode, typ, old.val)
+}
+
+// updateExprVal updates the value of x to val.
+func (check *Checker) updateExprVal(x ast.Expr, val exact.Value) {
+ if info, ok := check.untyped[x]; ok {
+ info.val = val
+ check.untyped[x] = info
+ }
+}
+
+// convertUntyped attempts to set the type of an untyped value to the target type.
+func (check *Checker) convertUntyped(x *operand, target Type) {
+ if x.mode == invalid || isTyped(x.typ) || target == Typ[Invalid] {
+ return
+ }
+
+ // TODO(gri) Sloppy code - clean up. This function is central
+ // to assignment and expression checking.
+
+ if isUntyped(target) {
+ // both x and target are untyped
+ xkind := x.typ.(*Basic).kind
+ tkind := target.(*Basic).kind
+ if isNumeric(x.typ) && isNumeric(target) {
+ if xkind < tkind {
+ x.typ = target
+ check.updateExprType(x.expr, target, false)
+ }
+ } else if xkind != tkind {
+ goto Error
+ }
+ return
+ }
+
+ // typed target
+ switch t := target.Underlying().(type) {
+ case *Basic:
+ if x.mode == constant {
+ check.representable(x, t)
+ if x.mode == invalid {
+ return
+ }
+ // expression value may have been rounded - update if needed
+ // TODO(gri) A floating-point value may silently underflow to
+ // zero. If it was negative, the sign is lost. See issue 6898.
+ check.updateExprVal(x.expr, x.val)
+ } else {
+ // Non-constant untyped values may appear as the
+ // result of comparisons (untyped bool), intermediate
+ // (delayed-checked) rhs operands of shifts, and as
+ // the value nil.
+ switch x.typ.(*Basic).kind {
+ case UntypedBool:
+ if !isBoolean(target) {
+ goto Error
+ }
+ case UntypedInt, UntypedRune, UntypedFloat, UntypedComplex:
+ if !isNumeric(target) {
+ goto Error
+ }
+ case UntypedString:
+ // Non-constant untyped string values are not
+ // permitted by the spec and should not occur.
+ unreachable()
+ case UntypedNil:
+ // Unsafe.Pointer is a basic type that includes nil.
+ if !hasNil(target) {
+ goto Error
+ }
+ default:
+ goto Error
+ }
+ }
+ case *Interface:
+ if !x.isNil() && !t.Empty() /* empty interfaces are ok */ {
+ goto Error
+ }
+ // Update operand types to the default type rather then
+ // the target (interface) type: values must have concrete
+ // dynamic types. If the value is nil, keep it untyped
+ // (this is important for tools such as go vet which need
+ // the dynamic type for argument checking of say, print
+ // functions)
+ if x.isNil() {
+ target = Typ[UntypedNil]
+ } else {
+ // cannot assign untyped values to non-empty interfaces
+ if !t.Empty() {
+ goto Error
+ }
+ target = defaultType(x.typ)
+ }
+ case *Pointer, *Signature, *Slice, *Map, *Chan:
+ if !x.isNil() {
+ goto Error
+ }
+ // keep nil untyped - see comment for interfaces, above
+ target = Typ[UntypedNil]
+ default:
+ goto Error
+ }
+
+ x.typ = target
+ check.updateExprType(x.expr, target, true) // UntypedNils are final
+ return
+
+Error:
+ check.errorf(x.pos(), "cannot convert %s to %s", x, target)
+ x.mode = invalid
+}
+
+func (check *Checker) comparison(x, y *operand, op token.Token) {
+ // spec: "In any comparison, the first operand must be assignable
+ // to the type of the second operand, or vice versa."
+ err := ""
+ if x.assignableTo(check.conf, y.typ) || y.assignableTo(check.conf, x.typ) {
+ defined := false
+ switch op {
+ case token.EQL, token.NEQ:
+ // spec: "The equality operators == and != apply to operands that are comparable."
+ defined = Comparable(x.typ) || x.isNil() && hasNil(y.typ) || y.isNil() && hasNil(x.typ)
+ case token.LSS, token.LEQ, token.GTR, token.GEQ:
+ // spec: The ordering operators <, <=, >, and >= apply to operands that are ordered."
+ defined = isOrdered(x.typ)
+ default:
+ unreachable()
+ }
+ if !defined {
+ typ := x.typ
+ if x.isNil() {
+ typ = y.typ
+ }
+ err = check.sprintf("operator %s not defined for %s", op, typ)
+ }
+ } else {
+ err = check.sprintf("mismatched types %s and %s", x.typ, y.typ)
+ }
+
+ if err != "" {
+ check.errorf(x.pos(), "cannot compare %s %s %s (%s)", x.expr, op, y.expr, err)
+ x.mode = invalid
+ return
+ }
+
+ if x.mode == constant && y.mode == constant {
+ x.val = exact.MakeBool(exact.Compare(x.val, op, y.val))
+ // The operands are never materialized; no need to update
+ // their types.
+ } else {
+ x.mode = value
+ // The operands have now their final types, which at run-
+ // time will be materialized. Update the expression trees.
+ // If the current types are untyped, the materialized type
+ // is the respective default type.
+ check.updateExprType(x.expr, defaultType(x.typ), true)
+ check.updateExprType(y.expr, defaultType(y.typ), true)
+ }
+
+ // spec: "Comparison operators compare two operands and yield
+ // an untyped boolean value."
+ x.typ = Typ[UntypedBool]
+}
+
+func (check *Checker) shift(x, y *operand, op token.Token) {
+ untypedx := isUntyped(x.typ)
+
+ // The lhs must be of integer type or be representable
+ // as an integer; otherwise the shift has no chance.
+ if !isInteger(x.typ) && (!untypedx || !representableConst(x.val, nil, UntypedInt, nil)) {
+ check.invalidOp(x.pos(), "shifted operand %s must be integer", x)
+ x.mode = invalid
+ return
+ }
+
+ // spec: "The right operand in a shift expression must have unsigned
+ // integer type or be an untyped constant that can be converted to
+ // unsigned integer type."
+ switch {
+ case isInteger(y.typ) && isUnsigned(y.typ):
+ // nothing to do
+ case isUntyped(y.typ):
+ check.convertUntyped(y, Typ[UntypedInt])
+ if y.mode == invalid {
+ x.mode = invalid
+ return
+ }
+ default:
+ check.invalidOp(y.pos(), "shift count %s must be unsigned integer", y)
+ x.mode = invalid
+ return
+ }
+
+ if x.mode == constant {
+ if y.mode == constant {
+ // rhs must be within reasonable bounds
+ const stupidShift = 1023 - 1 + 52 // so we can express smallestFloat64
+ s, ok := exact.Uint64Val(y.val)
+ if !ok || s > stupidShift {
+ check.invalidOp(y.pos(), "stupid shift count %s", y)
+ x.mode = invalid
+ return
+ }
+ // The lhs is representable as an integer but may not be an integer
+ // (e.g., 2.0, an untyped float) - this can only happen for untyped
+ // non-integer numeric constants. Correct the type so that the shift
+ // result is of integer type.
+ if !isInteger(x.typ) {
+ x.typ = Typ[UntypedInt]
+ }
+ x.val = exact.Shift(x.val, op, uint(s))
+ return
+ }
+
+ // non-constant shift with constant lhs
+ if untypedx {
+ // spec: "If the left operand of a non-constant shift
+ // expression is an untyped constant, the type of the
+ // constant is what it would be if the shift expression
+ // were replaced by its left operand alone.".
+ //
+ // Delay operand checking until we know the final type:
+ // The lhs expression must be in the untyped map, mark
+ // the entry as lhs shift operand.
+ info, found := check.untyped[x.expr]
+ assert(found)
+ info.isLhs = true
+ check.untyped[x.expr] = info
+ // keep x's type
+ x.mode = value
+ return
+ }
+ }
+
+ // constant rhs must be >= 0
+ if y.mode == constant && exact.Sign(y.val) < 0 {
+ check.invalidOp(y.pos(), "shift count %s must not be negative", y)
+ }
+
+ // non-constant shift - lhs must be an integer
+ if !isInteger(x.typ) {
+ check.invalidOp(x.pos(), "shifted operand %s must be integer", x)
+ x.mode = invalid
+ return
+ }
+
+ x.mode = value
+}
+
+var binaryOpPredicates = opPredicates{
+ token.ADD: func(typ Type) bool { return isNumeric(typ) || isString(typ) },
+ token.SUB: isNumeric,
+ token.MUL: isNumeric,
+ token.QUO: isNumeric,
+ token.REM: isInteger,
+
+ token.AND: isInteger,
+ token.OR: isInteger,
+ token.XOR: isInteger,
+ token.AND_NOT: isInteger,
+
+ token.LAND: isBoolean,
+ token.LOR: isBoolean,
+}
+
+func (check *Checker) binary(x *operand, lhs, rhs ast.Expr, op token.Token) {
+ var y operand
+
+ check.expr(x, lhs)
+ check.expr(&y, rhs)
+
+ if x.mode == invalid {
+ return
+ }
+ if y.mode == invalid {
+ x.mode = invalid
+ x.expr = y.expr
+ return
+ }
+
+ if isShift(op) {
+ check.shift(x, &y, op)
+ return
+ }
+
+ check.convertUntyped(x, y.typ)
+ if x.mode == invalid {
+ return
+ }
+ check.convertUntyped(&y, x.typ)
+ if y.mode == invalid {
+ x.mode = invalid
+ return
+ }
+
+ if isComparison(op) {
+ check.comparison(x, &y, op)
+ return
+ }
+
+ if !Identical(x.typ, y.typ) {
+ // only report an error if we have valid types
+ // (otherwise we had an error reported elsewhere already)
+ if x.typ != Typ[Invalid] && y.typ != Typ[Invalid] {
+ check.invalidOp(x.pos(), "mismatched types %s and %s", x.typ, y.typ)
+ }
+ x.mode = invalid
+ return
+ }
+
+ if !check.op(binaryOpPredicates, x, op) {
+ x.mode = invalid
+ return
+ }
+
+ if (op == token.QUO || op == token.REM) && (x.mode == constant || isInteger(x.typ)) && y.mode == constant && exact.Sign(y.val) == 0 {
+ check.invalidOp(y.pos(), "division by zero")
+ x.mode = invalid
+ return
+ }
+
+ if x.mode == constant && y.mode == constant {
+ typ := x.typ.Underlying().(*Basic)
+ // force integer division of integer operands
+ if op == token.QUO && isInteger(typ) {
+ op = token.QUO_ASSIGN
+ }
+ x.val = exact.BinaryOp(x.val, op, y.val)
+ // Typed constants must be representable in
+ // their type after each constant operation.
+ if isTyped(typ) {
+ check.representable(x, typ)
+ }
+ return
+ }
+
+ x.mode = value
+ // x.typ is unchanged
+}
+
+// index checks an index expression for validity.
+// If max >= 0, it is the upper bound for index.
+// If index is valid and the result i >= 0, then i is the constant value of index.
+func (check *Checker) index(index ast.Expr, max int64) (i int64, valid bool) {
+ var x operand
+ check.expr(&x, index)
+ if x.mode == invalid {
+ return
+ }
+
+ // an untyped constant must be representable as Int
+ check.convertUntyped(&x, Typ[Int])
+ if x.mode == invalid {
+ return
+ }
+
+ // the index must be of integer type
+ if !isInteger(x.typ) {
+ check.invalidArg(x.pos(), "index %s must be integer", &x)
+ return
+ }
+
+ // a constant index i must be in bounds
+ if x.mode == constant {
+ if exact.Sign(x.val) < 0 {
+ check.invalidArg(x.pos(), "index %s must not be negative", &x)
+ return
+ }
+ i, valid = exact.Int64Val(x.val)
+ if !valid || max >= 0 && i >= max {
+ check.errorf(x.pos(), "index %s is out of bounds", &x)
+ return i, false
+ }
+ // 0 <= i [ && i < max ]
+ return i, true
+ }
+
+ return -1, true
+}
+
+// indexElts checks the elements (elts) of an array or slice composite literal
+// against the literal's element type (typ), and the element indices against
+// the literal length if known (length >= 0). It returns the length of the
+// literal (maximum index value + 1).
+//
+func (check *Checker) indexedElts(elts []ast.Expr, typ Type, length int64) int64 {
+ visited := make(map[int64]bool, len(elts))
+ var index, max int64
+ for _, e := range elts {
+ // determine and check index
+ validIndex := false
+ eval := e
+ if kv, _ := e.(*ast.KeyValueExpr); kv != nil {
+ if i, ok := check.index(kv.Key, length); ok {
+ if i >= 0 {
+ index = i
+ validIndex = true
+ } else {
+ check.errorf(e.Pos(), "index %s must be integer constant", kv.Key)
+ }
+ }
+ eval = kv.Value
+ } else if length >= 0 && index >= length {
+ check.errorf(e.Pos(), "index %d is out of bounds (>= %d)", index, length)
+ } else {
+ validIndex = true
+ }
+
+ // if we have a valid index, check for duplicate entries
+ if validIndex {
+ if visited[index] {
+ check.errorf(e.Pos(), "duplicate index %d in array or slice literal", index)
+ }
+ visited[index] = true
+ }
+ index++
+ if index > max {
+ max = index
+ }
+
+ // check element against composite literal element type
+ var x operand
+ check.exprWithHint(&x, eval, typ)
+ if !check.assignment(&x, typ) && x.mode != invalid {
+ check.errorf(x.pos(), "cannot use %s as %s value in array or slice literal", &x, typ)
+ }
+ }
+ return max
+}
+
+// exprKind describes the kind of an expression; the kind
+// determines if an expression is valid in 'statement context'.
+type exprKind int
+
+const (
+ conversion exprKind = iota
+ expression
+ statement
+)
+
+// rawExpr typechecks expression e and initializes x with the expression
+// value or type. If an error occurred, x.mode is set to invalid.
+// If hint != nil, it is the type of a composite literal element.
+//
+func (check *Checker) rawExpr(x *operand, e ast.Expr, hint Type) exprKind {
+ if trace {
+ check.trace(e.Pos(), "%s", e)
+ check.indent++
+ defer func() {
+ check.indent--
+ check.trace(e.Pos(), "=> %s", x)
+ }()
+ }
+
+ kind := check.exprInternal(x, e, hint)
+
+ // convert x into a user-friendly set of values
+ // TODO(gri) this code can be simplified
+ var typ Type
+ var val exact.Value
+ switch x.mode {
+ case invalid:
+ typ = Typ[Invalid]
+ case novalue:
+ typ = (*Tuple)(nil)
+ case constant:
+ typ = x.typ
+ val = x.val
+ default:
+ typ = x.typ
+ }
+ assert(x.expr != nil && typ != nil)
+
+ if isUntyped(typ) {
+ // delay type and value recording until we know the type
+ // or until the end of type checking
+ check.rememberUntyped(x.expr, false, x.mode, typ.(*Basic), val)
+ } else {
+ check.recordTypeAndValue(e, x.mode, typ, val)
+ }
+
+ return kind
+}
+
+// exprInternal contains the core of type checking of expressions.
+// Must only be called by rawExpr.
+//
+func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
+ // make sure x has a valid state in case of bailout
+ // (was issue 5770)
+ x.mode = invalid
+ x.typ = Typ[Invalid]
+
+ switch e := e.(type) {
+ case *ast.BadExpr:
+ goto Error // error was reported before
+
+ case *ast.Ident:
+ check.ident(x, e, nil, nil)
+
+ case *ast.Ellipsis:
+ // ellipses are handled explicitly where they are legal
+ // (array composite literals and parameter lists)
+ check.error(e.Pos(), "invalid use of '...'")
+ goto Error
+
+ case *ast.BasicLit:
+ x.setConst(e.Kind, e.Value)
+ if x.mode == invalid {
+ check.invalidAST(e.Pos(), "invalid literal %v", e.Value)
+ goto Error
+ }
+
+ case *ast.FuncLit:
+ if sig, ok := check.typ(e.Type).(*Signature); ok {
+ // Anonymous functions are considered part of the
+ // init expression/func declaration which contains
+ // them: use existing package-level declaration info.
+ check.funcBody(check.decl, "", sig, e.Body)
+ x.mode = value
+ x.typ = sig
+ } else {
+ check.invalidAST(e.Pos(), "invalid function literal %s", e)
+ goto Error
+ }
+
+ case *ast.CompositeLit:
+ typ := hint
+ openArray := false
+ if e.Type != nil {
+ // [...]T array types may only appear with composite literals.
+ // Check for them here so we don't have to handle ... in general.
+ typ = nil
+ if atyp, _ := e.Type.(*ast.ArrayType); atyp != nil && atyp.Len != nil {
+ if ellip, _ := atyp.Len.(*ast.Ellipsis); ellip != nil && ellip.Elt == nil {
+ // We have an "open" [...]T array type.
+ // Create a new ArrayType with unknown length (-1)
+ // and finish setting it up after analyzing the literal.
+ typ = &Array{len: -1, elem: check.typ(atyp.Elt)}
+ openArray = true
+ }
+ }
+ if typ == nil {
+ typ = check.typ(e.Type)
+ }
+ }
+ if typ == nil {
+ // TODO(gri) provide better error messages depending on context
+ check.error(e.Pos(), "missing type in composite literal")
+ goto Error
+ }
+
+ switch typ, _ := deref(typ); utyp := typ.Underlying().(type) {
+ case *Struct:
+ if len(e.Elts) == 0 {
+ break
+ }
+ fields := utyp.fields
+ if _, ok := e.Elts[0].(*ast.KeyValueExpr); ok {
+ // all elements must have keys
+ visited := make([]bool, len(fields))
+ for _, e := range e.Elts {
+ kv, _ := e.(*ast.KeyValueExpr)
+ if kv == nil {
+ check.error(e.Pos(), "mixture of field:value and value elements in struct literal")
+ continue
+ }
+ key, _ := kv.Key.(*ast.Ident)
+ if key == nil {
+ check.errorf(kv.Pos(), "invalid field name %s in struct literal", kv.Key)
+ continue
+ }
+ i := fieldIndex(utyp.fields, check.pkg, key.Name)
+ if i < 0 {
+ check.errorf(kv.Pos(), "unknown field %s in struct literal", key.Name)
+ continue
+ }
+ fld := fields[i]
+ check.recordUse(key, fld)
+ // 0 <= i < len(fields)
+ if visited[i] {
+ check.errorf(kv.Pos(), "duplicate field name %s in struct literal", key.Name)
+ continue
+ }
+ visited[i] = true
+ check.expr(x, kv.Value)
+ etyp := fld.typ
+ if !check.assignment(x, etyp) {
+ if x.mode != invalid {
+ check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp)
+ }
+ continue
+ }
+ }
+ } else {
+ // no element must have a key
+ for i, e := range e.Elts {
+ if kv, _ := e.(*ast.KeyValueExpr); kv != nil {
+ check.error(kv.Pos(), "mixture of field:value and value elements in struct literal")
+ continue
+ }
+ check.expr(x, e)
+ if i >= len(fields) {
+ check.error(x.pos(), "too many values in struct literal")
+ break // cannot continue
+ }
+ // i < len(fields)
+ fld := fields[i]
+ if !fld.Exported() && fld.pkg != check.pkg {
+ check.errorf(x.pos(), "implicit assignment to unexported field %s in %s literal", fld.name, typ)
+ continue
+ }
+ etyp := fld.typ
+ if !check.assignment(x, etyp) {
+ if x.mode != invalid {
+ check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp)
+ }
+ continue
+ }
+ }
+ if len(e.Elts) < len(fields) {
+ check.error(e.Rbrace, "too few values in struct literal")
+ // ok to continue
+ }
+ }
+
+ case *Array:
+ n := check.indexedElts(e.Elts, utyp.elem, utyp.len)
+ // if we have an "open" [...]T array, set the length now that we know it
+ if openArray {
+ utyp.len = n
+ }
+
+ case *Slice:
+ check.indexedElts(e.Elts, utyp.elem, -1)
+
+ case *Map:
+ visited := make(map[interface{}][]Type, len(e.Elts))
+ for _, e := range e.Elts {
+ kv, _ := e.(*ast.KeyValueExpr)
+ if kv == nil {
+ check.error(e.Pos(), "missing key in map literal")
+ continue
+ }
+ check.exprWithHint(x, kv.Key, utyp.key)
+ if !check.assignment(x, utyp.key) {
+ if x.mode != invalid {
+ check.errorf(x.pos(), "cannot use %s as %s key in map literal", x, utyp.key)
+ }
+ continue
+ }
+ if x.mode == constant {
+ duplicate := false
+ // if the key is of interface type, the type is also significant when checking for duplicates
+ if _, ok := utyp.key.Underlying().(*Interface); ok {
+ for _, vtyp := range visited[x.val] {
+ if Identical(vtyp, x.typ) {
+ duplicate = true
+ break
+ }
+ }
+ visited[x.val] = append(visited[x.val], x.typ)
+ } else {
+ _, duplicate = visited[x.val]
+ visited[x.val] = nil
+ }
+ if duplicate {
+ check.errorf(x.pos(), "duplicate key %s in map literal", x.val)
+ continue
+ }
+ }
+ check.exprWithHint(x, kv.Value, utyp.elem)
+ if !check.assignment(x, utyp.elem) {
+ if x.mode != invalid {
+ check.errorf(x.pos(), "cannot use %s as %s value in map literal", x, utyp.elem)
+ }
+ continue
+ }
+ }
+
+ default:
+ // if utyp is invalid, an error was reported before
+ if utyp != Typ[Invalid] {
+ check.errorf(e.Pos(), "invalid composite literal type %s", typ)
+ goto Error
+ }
+ }
+
+ x.mode = value
+ x.typ = typ
+
+ case *ast.ParenExpr:
+ kind := check.rawExpr(x, e.X, nil)
+ x.expr = e
+ return kind
+
+ case *ast.SelectorExpr:
+ check.selector(x, e)
+
+ case *ast.IndexExpr:
+ check.expr(x, e.X)
+ if x.mode == invalid {
+ goto Error
+ }
+
+ valid := false
+ length := int64(-1) // valid if >= 0
+ switch typ := x.typ.Underlying().(type) {
+ case *Basic:
+ if isString(typ) {
+ valid = true
+ if x.mode == constant {
+ length = int64(len(exact.StringVal(x.val)))
+ }
+ // an indexed string always yields a byte value
+ // (not a constant) even if the string and the
+ // index are constant
+ x.mode = value
+ x.typ = UniverseByte // use 'byte' name
+ }
+
+ case *Array:
+ valid = true
+ length = typ.len
+ if x.mode != variable {
+ x.mode = value
+ }
+ x.typ = typ.elem
+
+ case *Pointer:
+ if typ, _ := typ.base.Underlying().(*Array); typ != nil {
+ valid = true
+ length = typ.len
+ x.mode = variable
+ x.typ = typ.elem
+ }
+
+ case *Slice:
+ valid = true
+ x.mode = variable
+ x.typ = typ.elem
+
+ case *Map:
+ var key operand
+ check.expr(&key, e.Index)
+ if !check.assignment(&key, typ.key) {
+ if key.mode != invalid {
+ check.invalidOp(key.pos(), "cannot use %s as map index of type %s", &key, typ.key)
+ }
+ goto Error
+ }
+ x.mode = mapindex
+ x.typ = typ.elem
+ x.expr = e
+ return expression
+ }
+
+ if !valid {
+ check.invalidOp(x.pos(), "cannot index %s", x)
+ goto Error
+ }
+
+ if e.Index == nil {
+ check.invalidAST(e.Pos(), "missing index for %s", x)
+ goto Error
+ }
+
+ check.index(e.Index, length)
+ // ok to continue
+
+ case *ast.SliceExpr:
+ check.expr(x, e.X)
+ if x.mode == invalid {
+ goto Error
+ }
+
+ valid := false
+ length := int64(-1) // valid if >= 0
+ switch typ := x.typ.Underlying().(type) {
+ case *Basic:
+ if isString(typ) {
+ if slice3(e) {
+ check.invalidOp(x.pos(), "3-index slice of string")
+ goto Error
+ }
+ valid = true
+ if x.mode == constant {
+ length = int64(len(exact.StringVal(x.val)))
+ }
+ // spec: "For untyped string operands the result
+ // is a non-constant value of type string."
+ if typ.kind == UntypedString {
+ x.typ = Typ[String]
+ }
+ }
+
+ case *Array:
+ valid = true
+ length = typ.len
+ if x.mode != variable {
+ check.invalidOp(x.pos(), "cannot slice %s (value not addressable)", x)
+ goto Error
+ }
+ x.typ = &Slice{elem: typ.elem}
+
+ case *Pointer:
+ if typ, _ := typ.base.Underlying().(*Array); typ != nil {
+ valid = true
+ length = typ.len
+ x.typ = &Slice{elem: typ.elem}
+ }
+
+ case *Slice:
+ valid = true
+ // x.typ doesn't change
+ }
+
+ if !valid {
+ check.invalidOp(x.pos(), "cannot slice %s", x)
+ goto Error
+ }
+
+ x.mode = value
+
+ // spec: "Only the first index may be omitted; it defaults to 0."
+ if slice3(e) && (e.High == nil || sliceMax(e) == nil) {
+ check.error(e.Rbrack, "2nd and 3rd index required in 3-index slice")
+ goto Error
+ }
+
+ // check indices
+ var ind [3]int64
+ for i, expr := range []ast.Expr{e.Low, e.High, sliceMax(e)} {
+ x := int64(-1)
+ switch {
+ case expr != nil:
+ // The "capacity" is only known statically for strings, arrays,
+ // and pointers to arrays, and it is the same as the length for
+ // those types.
+ max := int64(-1)
+ if length >= 0 {
+ max = length + 1
+ }
+ if t, ok := check.index(expr, max); ok && t >= 0 {
+ x = t
+ }
+ case i == 0:
+ // default is 0 for the first index
+ x = 0
+ case length >= 0:
+ // default is length (== capacity) otherwise
+ x = length
+ }
+ ind[i] = x
+ }
+
+ // constant indices must be in range
+ // (check.index already checks that existing indices >= 0)
+ L:
+ for i, x := range ind[:len(ind)-1] {
+ if x > 0 {
+ for _, y := range ind[i+1:] {
+ if y >= 0 && x > y {
+ check.errorf(e.Rbrack, "invalid slice indices: %d > %d", x, y)
+ break L // only report one error, ok to continue
+ }
+ }
+ }
+ }
+
+ case *ast.TypeAssertExpr:
+ check.expr(x, e.X)
+ if x.mode == invalid {
+ goto Error
+ }
+ xtyp, _ := x.typ.Underlying().(*Interface)
+ if xtyp == nil {
+ check.invalidOp(x.pos(), "%s is not an interface", x)
+ goto Error
+ }
+ // x.(type) expressions are handled explicitly in type switches
+ if e.Type == nil {
+ check.invalidAST(e.Pos(), "use of .(type) outside type switch")
+ goto Error
+ }
+ T := check.typ(e.Type)
+ if T == Typ[Invalid] {
+ goto Error
+ }
+ check.typeAssertion(x.pos(), x, xtyp, T)
+ x.mode = commaok
+ x.typ = T
+
+ case *ast.CallExpr:
+ return check.call(x, e)
+
+ case *ast.StarExpr:
+ check.exprOrType(x, e.X)
+ switch x.mode {
+ case invalid:
+ goto Error
+ case typexpr:
+ x.typ = &Pointer{base: x.typ}
+ default:
+ if typ, ok := x.typ.Underlying().(*Pointer); ok {
+ x.mode = variable
+ x.typ = typ.base
+ } else {
+ check.invalidOp(x.pos(), "cannot indirect %s", x)
+ goto Error
+ }
+ }
+
+ case *ast.UnaryExpr:
+ check.expr(x, e.X)
+ if x.mode == invalid {
+ goto Error
+ }
+ check.unary(x, e.Op)
+ if x.mode == invalid {
+ goto Error
+ }
+ if e.Op == token.ARROW {
+ x.expr = e
+ return statement // receive operations may appear in statement context
+ }
+
+ case *ast.BinaryExpr:
+ check.binary(x, e.X, e.Y, e.Op)
+ if x.mode == invalid {
+ goto Error
+ }
+
+ case *ast.KeyValueExpr:
+ // key:value expressions are handled in composite literals
+ check.invalidAST(e.Pos(), "no key:value expected")
+ goto Error
+
+ case *ast.ArrayType, *ast.StructType, *ast.FuncType,
+ *ast.InterfaceType, *ast.MapType, *ast.ChanType:
+ x.mode = typexpr
+ x.typ = check.typ(e)
+ // Note: rawExpr (caller of exprInternal) will call check.recordTypeAndValue
+ // even though check.typ has already called it. This is fine as both
+ // times the same expression and type are recorded. It is also not a
+ // performance issue because we only reach here for composite literal
+ // types, which are comparatively rare.
+
+ default:
+ panic(fmt.Sprintf("%s: unknown expression type %T", check.fset.Position(e.Pos()), e))
+ }
+
+ // everything went well
+ x.expr = e
+ return expression
+
+Error:
+ x.mode = invalid
+ x.expr = e
+ return statement // avoid follow-up errors
+}
+
+// typeAssertion checks that x.(T) is legal; xtyp must be the type of x.
+func (check *Checker) typeAssertion(pos token.Pos, x *operand, xtyp *Interface, T Type) {
+ method, wrongType := assertableTo(xtyp, T)
+ if method == nil {
+ return
+ }
+
+ var msg string
+ if wrongType {
+ msg = "wrong type for method"
+ } else {
+ msg = "missing method"
+ }
+ check.errorf(pos, "%s cannot have dynamic type %s (%s %s)", x, T, msg, method.name)
+}
+
+// expr typechecks expression e and initializes x with the expression value.
+// If an error occurred, x.mode is set to invalid.
+//
+func (check *Checker) expr(x *operand, e ast.Expr) {
+ check.rawExpr(x, e, nil)
+ var msg string
+ switch x.mode {
+ default:
+ return
+ case novalue:
+ msg = "used as value"
+ case builtin:
+ msg = "must be called"
+ case typexpr:
+ msg = "is not an expression"
+ }
+ check.errorf(x.pos(), "%s %s", x, msg)
+ x.mode = invalid
+}
+
+// exprWithHint typechecks expression e and initializes x with the expression value.
+// If an error occurred, x.mode is set to invalid.
+// If hint != nil, it is the type of a composite literal element.
+//
+func (check *Checker) exprWithHint(x *operand, e ast.Expr, hint Type) {
+ assert(hint != nil)
+ check.rawExpr(x, e, hint)
+ var msg string
+ switch x.mode {
+ default:
+ return
+ case novalue:
+ msg = "used as value"
+ case builtin:
+ msg = "must be called"
+ case typexpr:
+ msg = "is not an expression"
+ }
+ check.errorf(x.pos(), "%s %s", x, msg)
+ x.mode = invalid
+}
+
+// exprOrType typechecks expression or type e and initializes x with the expression value or type.
+// If an error occurred, x.mode is set to invalid.
+//
+func (check *Checker) exprOrType(x *operand, e ast.Expr) {
+ check.rawExpr(x, e, nil)
+ if x.mode == novalue {
+ check.errorf(x.pos(), "%s used as value or type", x)
+ x.mode = invalid
+ }
+}
diff --git a/src/go/types/exprstring.go b/src/go/types/exprstring.go
new file mode 100644
index 0000000000..370bdf3532
--- /dev/null
+++ b/src/go/types/exprstring.go
@@ -0,0 +1,220 @@
+// 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 printing of expressions.
+
+package types
+
+import (
+ "bytes"
+ "go/ast"
+)
+
+// ExprString returns the (possibly simplified) string representation for x.
+func ExprString(x ast.Expr) string {
+ var buf bytes.Buffer
+ WriteExpr(&buf, x)
+ return buf.String()
+}
+
+// WriteExpr writes the (possibly simplified) string representation for x to buf.
+func WriteExpr(buf *bytes.Buffer, x ast.Expr) {
+ // The AST preserves source-level parentheses so there is
+ // no need to introduce them here to correct for different
+ // operator precedences. (This assumes that the AST was
+ // generated by a Go parser.)
+
+ switch x := x.(type) {
+ default:
+ buf.WriteString("(bad expr)") // nil, ast.BadExpr, ast.KeyValueExpr
+
+ case *ast.Ident:
+ buf.WriteString(x.Name)
+
+ case *ast.Ellipsis:
+ buf.WriteString("...")
+ if x.Elt != nil {
+ WriteExpr(buf, x.Elt)
+ }
+
+ case *ast.BasicLit:
+ buf.WriteString(x.Value)
+
+ case *ast.FuncLit:
+ buf.WriteByte('(')
+ WriteExpr(buf, x.Type)
+ buf.WriteString(" literal)") // simplified
+
+ case *ast.CompositeLit:
+ buf.WriteByte('(')
+ WriteExpr(buf, x.Type)
+ buf.WriteString(" literal)") // simplified
+
+ case *ast.ParenExpr:
+ buf.WriteByte('(')
+ WriteExpr(buf, x.X)
+ buf.WriteByte(')')
+
+ case *ast.SelectorExpr:
+ WriteExpr(buf, x.X)
+ buf.WriteByte('.')
+ buf.WriteString(x.Sel.Name)
+
+ case *ast.IndexExpr:
+ WriteExpr(buf, x.X)
+ buf.WriteByte('[')
+ WriteExpr(buf, x.Index)
+ buf.WriteByte(']')
+
+ case *ast.SliceExpr:
+ WriteExpr(buf, x.X)
+ buf.WriteByte('[')
+ if x.Low != nil {
+ WriteExpr(buf, x.Low)
+ }
+ buf.WriteByte(':')
+ if x.High != nil {
+ WriteExpr(buf, x.High)
+ }
+ if x.Slice3 {
+ buf.WriteByte(':')
+ if x.Max != nil {
+ WriteExpr(buf, x.Max)
+ }
+ }
+ buf.WriteByte(']')
+
+ case *ast.TypeAssertExpr:
+ WriteExpr(buf, x.X)
+ buf.WriteString(".(")
+ WriteExpr(buf, x.Type)
+ buf.WriteByte(')')
+
+ case *ast.CallExpr:
+ WriteExpr(buf, x.Fun)
+ buf.WriteByte('(')
+ for i, arg := range x.Args {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ WriteExpr(buf, arg)
+ }
+ if x.Ellipsis.IsValid() {
+ buf.WriteString("...")
+ }
+ buf.WriteByte(')')
+
+ case *ast.StarExpr:
+ buf.WriteByte('*')
+ WriteExpr(buf, x.X)
+
+ case *ast.UnaryExpr:
+ buf.WriteString(x.Op.String())
+ WriteExpr(buf, x.X)
+
+ case *ast.BinaryExpr:
+ WriteExpr(buf, x.X)
+ buf.WriteByte(' ')
+ buf.WriteString(x.Op.String())
+ buf.WriteByte(' ')
+ WriteExpr(buf, x.Y)
+
+ case *ast.ArrayType:
+ buf.WriteByte('[')
+ if x.Len != nil {
+ WriteExpr(buf, x.Len)
+ }
+ buf.WriteByte(']')
+ WriteExpr(buf, x.Elt)
+
+ case *ast.StructType:
+ buf.WriteString("struct{")
+ writeFieldList(buf, x.Fields, "; ", false)
+ buf.WriteByte('}')
+
+ case *ast.FuncType:
+ buf.WriteString("func")
+ writeSigExpr(buf, x)
+
+ case *ast.InterfaceType:
+ buf.WriteString("interface{")
+ writeFieldList(buf, x.Methods, "; ", true)
+ buf.WriteByte('}')
+
+ case *ast.MapType:
+ buf.WriteString("map[")
+ WriteExpr(buf, x.Key)
+ buf.WriteByte(']')
+ WriteExpr(buf, x.Value)
+
+ case *ast.ChanType:
+ var s string
+ switch x.Dir {
+ case ast.SEND:
+ s = "chan<- "
+ case ast.RECV:
+ s = "<-chan "
+ default:
+ s = "chan "
+ }
+ buf.WriteString(s)
+ WriteExpr(buf, x.Value)
+ }
+}
+
+func writeSigExpr(buf *bytes.Buffer, sig *ast.FuncType) {
+ buf.WriteByte('(')
+ writeFieldList(buf, sig.Params, ", ", false)
+ buf.WriteByte(')')
+
+ res := sig.Results
+ n := res.NumFields()
+ if n == 0 {
+ // no result
+ return
+ }
+
+ buf.WriteByte(' ')
+ if n == 1 && len(res.List[0].Names) == 0 {
+ // single unnamed result
+ WriteExpr(buf, res.List[0].Type)
+ return
+ }
+
+ // multiple or named result(s)
+ buf.WriteByte('(')
+ writeFieldList(buf, res, ", ", false)
+ buf.WriteByte(')')
+}
+
+func writeFieldList(buf *bytes.Buffer, fields *ast.FieldList, sep string, iface bool) {
+ for i, f := range fields.List {
+ if i > 0 {
+ buf.WriteString(sep)
+ }
+
+ // field list names
+ for i, name := range f.Names {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ buf.WriteString(name.Name)
+ }
+
+ // types of interface methods consist of signatures only
+ if sig, _ := f.Type.(*ast.FuncType); sig != nil && iface {
+ writeSigExpr(buf, sig)
+ continue
+ }
+
+ // named fields are separated with a blank from the field type
+ if len(f.Names) > 0 {
+ buf.WriteByte(' ')
+ }
+
+ WriteExpr(buf, f.Type)
+
+ // ignore tag
+ }
+}
diff --git a/src/go/types/exprstring_test.go b/src/go/types/exprstring_test.go
new file mode 100644
index 0000000000..51102881c9
--- /dev/null
+++ b/src/go/types/exprstring_test.go
@@ -0,0 +1,94 @@
+// 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 types_test
+
+import (
+ "go/parser"
+ "testing"
+
+ . "go/types"
+)
+
+var testExprs = []testEntry{
+ // basic type literals
+ dup("x"),
+ dup("true"),
+ dup("42"),
+ dup("3.1415"),
+ dup("2.71828i"),
+ dup(`'a'`),
+ dup(`"foo"`),
+ dup("`bar`"),
+
+ // func and composite literals
+ {"func(){}", "(func() literal)"},
+ {"func(x int) complex128 {}", "(func(x int) complex128 literal)"},
+ {"[]int{1, 2, 3}", "([]int literal)"},
+
+ // non-type expressions
+ dup("(x)"),
+ dup("x.f"),
+ dup("a[i]"),
+
+ dup("s[:]"),
+ dup("s[i:]"),
+ dup("s[:j]"),
+ dup("s[i:j]"),
+ dup("s[:j:k]"),
+ dup("s[i:j:k]"),
+
+ dup("x.(T)"),
+
+ dup("x.([10]int)"),
+ dup("x.([...]int)"),
+
+ dup("x.(struct{})"),
+ dup("x.(struct{x int; y, z float32; E})"),
+
+ dup("x.(func())"),
+ dup("x.(func(x int))"),
+ dup("x.(func() int)"),
+ dup("x.(func(x, y int, z float32) (r int))"),
+ dup("x.(func(a, b, c int))"),
+ dup("x.(func(x ...T))"),
+
+ dup("x.(interface{})"),
+ dup("x.(interface{m(); n(x int); E})"),
+ dup("x.(interface{m(); n(x int) T; E; F})"),
+
+ dup("x.(map[K]V)"),
+
+ dup("x.(chan E)"),
+ dup("x.(<-chan E)"),
+ dup("x.(chan<- chan int)"),
+ dup("x.(chan<- <-chan int)"),
+ dup("x.(<-chan chan int)"),
+ dup("x.(chan (<-chan int))"),
+
+ dup("f()"),
+ dup("f(x)"),
+ dup("int(x)"),
+ dup("f(x, x + y)"),
+ dup("f(s...)"),
+ dup("f(a, s...)"),
+
+ dup("*x"),
+ dup("&x"),
+ dup("x + y"),
+ dup("x + y << (2 * s)"),
+}
+
+func TestExprString(t *testing.T) {
+ for _, test := range testExprs {
+ x, err := parser.ParseExpr(test.src)
+ if err != nil {
+ t.Errorf("%s: %s", test.src, err)
+ continue
+ }
+ if got := ExprString(x); got != test.str {
+ t.Errorf("%s: got %s, want %s", test.src, got, test.str)
+ }
+ }
+}
diff --git a/src/net/dial_gen_test.go b/src/go/types/go11.go
similarity index 51%
rename from src/net/dial_gen_test.go
rename to src/go/types/go11.go
index c857acd06d..cf41cabeea 100644
--- a/src/net/dial_gen_test.go
+++ b/src/go/types/go11.go
@@ -2,10 +2,16 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build windows plan9
+// +build !go1.2
-package net
+package types
-func init() {
- testingIssue5349 = true
+import "go/ast"
+
+func slice3(x *ast.SliceExpr) bool {
+ return false
+}
+
+func sliceMax(x *ast.SliceExpr) ast.Expr {
+ return nil
}
diff --git a/src/go/types/go12.go b/src/go/types/go12.go
new file mode 100644
index 0000000000..2017442154
--- /dev/null
+++ b/src/go/types/go12.go
@@ -0,0 +1,17 @@
+// 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 go1.2
+
+package types
+
+import "go/ast"
+
+func slice3(x *ast.SliceExpr) bool {
+ return x.Slice3
+}
+
+func sliceMax(x *ast.SliceExpr) ast.Expr {
+ return x.Max
+}
diff --git a/src/go/types/hilbert_test.go b/src/go/types/hilbert_test.go
new file mode 100644
index 0000000000..cfd51b1d64
--- /dev/null
+++ b/src/go/types/hilbert_test.go
@@ -0,0 +1,234 @@
+// 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 types_test
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/importer"
+ "go/parser"
+ "go/token"
+ "io/ioutil"
+ "testing"
+
+ . "go/types"
+)
+
+var (
+ H = flag.Int("H", 5, "Hilbert matrix size")
+ out = flag.String("out", "", "write generated program to out")
+)
+
+func TestHilbert(t *testing.T) {
+ // generate source
+ src := program(*H, *out)
+ if *out != "" {
+ ioutil.WriteFile(*out, src, 0666)
+ return
+ }
+
+ // parse source
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "hilbert.go", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // type-check file
+ DefPredeclaredTestFuncs() // define assert built-in
+ conf := Config{Importer: importer.Default()}
+ _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func program(n int, out string) []byte {
+ var g gen
+
+ g.p(`// WARNING: GENERATED FILE - DO NOT MODIFY MANUALLY!
+// (To generate, in go/types directory: go test -run=Hilbert -H=%d -out=%q)
+
+// This program tests arbitrary precision constant arithmetic
+// by generating the constant elements of a Hilbert matrix H,
+// its inverse I, and the product P = H*I. The product should
+// be the identity matrix.
+package main
+
+func main() {
+ if !ok {
+ printProduct()
+ return
+ }
+ println("PASS")
+}
+
+`, n, out)
+ g.hilbert(n)
+ g.inverse(n)
+ g.product(n)
+ g.verify(n)
+ g.printProduct(n)
+ g.binomials(2*n - 1)
+ g.factorials(2*n - 1)
+
+ return g.Bytes()
+}
+
+type gen struct {
+ bytes.Buffer
+}
+
+func (g *gen) p(format string, args ...interface{}) {
+ fmt.Fprintf(&g.Buffer, format, args...)
+}
+
+func (g *gen) hilbert(n int) {
+ g.p(`// Hilbert matrix, n = %d
+const (
+`, n)
+ for i := 0; i < n; i++ {
+ g.p("\t")
+ for j := 0; j < n; j++ {
+ if j > 0 {
+ g.p(", ")
+ }
+ g.p("h%d_%d", i, j)
+ }
+ if i == 0 {
+ g.p(" = ")
+ for j := 0; j < n; j++ {
+ if j > 0 {
+ g.p(", ")
+ }
+ g.p("1.0/(iota + %d)", j+1)
+ }
+ }
+ g.p("\n")
+ }
+ g.p(")\n\n")
+}
+
+func (g *gen) inverse(n int) {
+ g.p(`// Inverse Hilbert matrix
+const (
+`)
+ for i := 0; i < n; i++ {
+ for j := 0; j < n; j++ {
+ s := "+"
+ if (i+j)&1 != 0 {
+ s = "-"
+ }
+ g.p("\ti%d_%d = %s%d * b%d_%d * b%d_%d * b%d_%d * b%d_%d\n",
+ i, j, s, i+j+1, n+i, n-j-1, n+j, n-i-1, i+j, i, i+j, i)
+ }
+ g.p("\n")
+ }
+ g.p(")\n\n")
+}
+
+func (g *gen) product(n int) {
+ g.p(`// Product matrix
+const (
+`)
+ for i := 0; i < n; i++ {
+ for j := 0; j < n; j++ {
+ g.p("\tp%d_%d = ", i, j)
+ for k := 0; k < n; k++ {
+ if k > 0 {
+ g.p(" + ")
+ }
+ g.p("h%d_%d*i%d_%d", i, k, k, j)
+ }
+ g.p("\n")
+ }
+ g.p("\n")
+ }
+ g.p(")\n\n")
+}
+
+func (g *gen) verify(n int) {
+ g.p(`// Verify that product is the identity matrix
+const ok =
+`)
+ for i := 0; i < n; i++ {
+ for j := 0; j < n; j++ {
+ if j == 0 {
+ g.p("\t")
+ } else {
+ g.p(" && ")
+ }
+ v := 0
+ if i == j {
+ v = 1
+ }
+ g.p("p%d_%d == %d", i, j, v)
+ }
+ g.p(" &&\n")
+ }
+ g.p("\ttrue\n\n")
+
+ // verify ok at type-check time
+ if *out == "" {
+ g.p("const _ = assert(ok)\n\n")
+ }
+}
+
+func (g *gen) printProduct(n int) {
+ g.p("func printProduct() {\n")
+ for i := 0; i < n; i++ {
+ g.p("\tprintln(")
+ for j := 0; j < n; j++ {
+ if j > 0 {
+ g.p(", ")
+ }
+ g.p("p%d_%d", i, j)
+ }
+ g.p(")\n")
+ }
+ g.p("}\n\n")
+}
+
+func (g *gen) mulRange(a, b int) {
+ if a > b {
+ g.p("1")
+ return
+ }
+ for i := a; i <= b; i++ {
+ if i > a {
+ g.p("*")
+ }
+ g.p("%d", i)
+ }
+}
+
+func (g *gen) binomials(n int) {
+ g.p(`// Binomials
+const (
+`)
+ for j := 0; j <= n; j++ {
+ if j > 0 {
+ g.p("\n")
+ }
+ for k := 0; k <= j; k++ {
+ g.p("\tb%d_%d = f%d / (f%d*f%d)\n", j, k, j, k, j-k)
+ }
+ }
+ g.p(")\n\n")
+}
+
+func (g *gen) factorials(n int) {
+ g.p(`// Factorials
+const (
+ f0 = 1
+ f1 = 1
+`)
+ for i := 2; i <= n; i++ {
+ g.p("\tf%d = f%d * %d\n", i, i-1, i)
+ }
+ g.p(")\n\n")
+}
diff --git a/src/go/types/initorder.go b/src/go/types/initorder.go
new file mode 100644
index 0000000000..0fd567b269
--- /dev/null
+++ b/src/go/types/initorder.go
@@ -0,0 +1,222 @@
+// Copyright 2014 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 types
+
+import (
+ "container/heap"
+ "fmt"
+)
+
+// initOrder computes the Info.InitOrder for package variables.
+func (check *Checker) initOrder() {
+ // An InitOrder may already have been computed if a package is
+ // built from several calls to (*Checker).Files. Clear it.
+ check.Info.InitOrder = check.Info.InitOrder[:0]
+
+ // compute the object dependency graph and
+ // initialize a priority queue with the list
+ // of graph nodes
+ pq := nodeQueue(dependencyGraph(check.objMap))
+ heap.Init(&pq)
+
+ const debug = false
+ if debug {
+ fmt.Printf("package %s: object dependency graph\n", check.pkg.Name())
+ for _, n := range pq {
+ for _, o := range n.out {
+ fmt.Printf("\t%s -> %s\n", n.obj.Name(), o.obj.Name())
+ }
+ }
+ fmt.Println()
+ fmt.Printf("package %s: initialization order\n", check.pkg.Name())
+ }
+
+ // determine initialization order by removing the highest priority node
+ // (the one with the fewest dependencies) and its edges from the graph,
+ // repeatedly, until there are no nodes left.
+ // In a valid Go program, those nodes always have zero dependencies (after
+ // removing all incoming dependencies), otherwise there are initialization
+ // cycles.
+ mark := 0
+ emitted := make(map[*declInfo]bool)
+ for len(pq) > 0 {
+ // get the next node
+ n := heap.Pop(&pq).(*objNode)
+
+ // if n still depends on other nodes, we have a cycle
+ if n.in > 0 {
+ mark++ // mark nodes using a different value each time
+ cycle := findPath(n, n, mark)
+ if i := valIndex(cycle); i >= 0 {
+ check.reportCycle(cycle, i)
+ }
+ // ok to continue, but the variable initialization order
+ // will be incorrect at this point since it assumes no
+ // cycle errors
+ }
+
+ // reduce dependency count of all dependent nodes
+ // and update priority queue
+ for _, out := range n.out {
+ out.in--
+ heap.Fix(&pq, out.index)
+ }
+
+ // record the init order for variables with initializers only
+ v, _ := n.obj.(*Var)
+ info := check.objMap[v]
+ if v == nil || !info.hasInitializer() {
+ continue
+ }
+
+ // n:1 variable declarations such as: a, b = f()
+ // introduce a node for each lhs variable (here: a, b);
+ // but they all have the same initializer - emit only
+ // one, for the first variable seen
+ if emitted[info] {
+ continue // initializer already emitted, if any
+ }
+ emitted[info] = true
+
+ infoLhs := info.lhs // possibly nil (see declInfo.lhs field comment)
+ if infoLhs == nil {
+ infoLhs = []*Var{v}
+ }
+ init := &Initializer{infoLhs, info.init}
+ check.Info.InitOrder = append(check.Info.InitOrder, init)
+
+ if debug {
+ fmt.Printf("\t%s\n", init)
+ }
+ }
+
+ if debug {
+ fmt.Println()
+ }
+}
+
+// findPath returns the (reversed) list of nodes z, ... c, b, a,
+// such that there is a path (list of edges) from a to z.
+// If there is no such path, the result is nil.
+// Nodes marked with the value mark are considered "visited";
+// unvisited nodes are marked during the graph search.
+func findPath(a, z *objNode, mark int) []*objNode {
+ if a.mark == mark {
+ return nil // node already seen
+ }
+ a.mark = mark
+
+ for _, n := range a.out {
+ if n == z {
+ return []*objNode{z}
+ }
+ if P := findPath(n, z, mark); P != nil {
+ return append(P, n)
+ }
+ }
+
+ return nil
+}
+
+// valIndex returns the index of the first constant or variable in a,
+// if any; or a value < 0.
+func valIndex(a []*objNode) int {
+ for i, n := range a {
+ switch n.obj.(type) {
+ case *Const, *Var:
+ return i
+ }
+ }
+ return -1
+}
+
+// reportCycle reports an error for the cycle starting at i.
+func (check *Checker) reportCycle(cycle []*objNode, i int) {
+ obj := cycle[i].obj
+ check.errorf(obj.Pos(), "initialization cycle for %s", obj.Name())
+ // print cycle
+ for _ = range cycle {
+ check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented
+ i++
+ if i >= len(cycle) {
+ i = 0
+ }
+ obj = cycle[i].obj
+ }
+ check.errorf(obj.Pos(), "\t%s", obj.Name())
+}
+
+// An objNode represents a node in the object dependency graph.
+// Each node b in a.out represents an edge a->b indicating that
+// b depends on a.
+// Nodes may be marked for cycle detection. A node n is marked
+// if n.mark corresponds to the current mark value.
+type objNode struct {
+ obj Object // object represented by this node
+ in int // number of nodes this node depends on
+ out []*objNode // list of nodes that depend on this node
+ index int // node index in list of nodes
+ mark int // for cycle detection
+}
+
+// dependencyGraph computes the transposed object dependency graph
+// from the given objMap. The transposed graph is returned as a list
+// of nodes; an edge d->n indicates that node n depends on node d.
+func dependencyGraph(objMap map[Object]*declInfo) []*objNode {
+ // M maps each object to its corresponding node
+ M := make(map[Object]*objNode, len(objMap))
+ for obj := range objMap {
+ M[obj] = &objNode{obj: obj}
+ }
+
+ // G is the graph of nodes n
+ G := make([]*objNode, len(M))
+ i := 0
+ for obj, n := range M {
+ deps := objMap[obj].deps
+ n.in = len(deps)
+ for d := range deps {
+ d := M[d] // node n depends on node d
+ d.out = append(d.out, n) // add edge d->n
+ }
+
+ G[i] = n
+ n.index = i
+ i++
+ }
+
+ return G
+}
+
+// nodeQueue implements the container/heap interface;
+// a nodeQueue may be used as a priority queue.
+type nodeQueue []*objNode
+
+func (a nodeQueue) Len() int { return len(a) }
+
+func (a nodeQueue) Swap(i, j int) {
+ x, y := a[i], a[j]
+ a[i], a[j] = y, x
+ x.index, y.index = j, i
+}
+
+func (a nodeQueue) Less(i, j int) bool {
+ x, y := a[i], a[j]
+ // nodes are prioritized by number of incoming dependencies (1st key)
+ // and source order (2nd key)
+ return x.in < y.in || x.in == y.in && x.obj.order() < y.obj.order()
+}
+
+func (a *nodeQueue) Push(x interface{}) {
+ panic("unreachable")
+}
+
+func (a *nodeQueue) Pop() interface{} {
+ n := len(*a)
+ x := (*a)[n-1]
+ x.index = -1 // for safety
+ *a = (*a)[:n-1]
+ return x
+}
diff --git a/src/go/types/issues_test.go b/src/go/types/issues_test.go
new file mode 100644
index 0000000000..672c78dfc2
--- /dev/null
+++ b/src/go/types/issues_test.go
@@ -0,0 +1,206 @@
+// 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 tests for various issues.
+
+package types_test
+
+import (
+ "fmt"
+ "go/ast"
+ "go/importer"
+ "go/parser"
+ "sort"
+ "strings"
+ "testing"
+
+ . "go/types"
+)
+
+func TestIssue5770(t *testing.T) {
+ src := `package p; type S struct{T}`
+ f, err := parser.ParseFile(fset, "", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ conf := Config{Importer: importer.Default()}
+ _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, nil) // do not crash
+ want := "undeclared name: T"
+ if err == nil || !strings.Contains(err.Error(), want) {
+ t.Errorf("got: %v; want: %s", err, want)
+ }
+}
+
+func TestIssue5849(t *testing.T) {
+ src := `
+package p
+var (
+ s uint
+ _ = uint8(8)
+ _ = uint16(16) << s
+ _ = uint32(32 << s)
+ _ = uint64(64 << s + s)
+ _ = (interface{})("foo")
+ _ = (interface{})(nil)
+)`
+ f, err := parser.ParseFile(fset, "", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var conf Config
+ types := make(map[ast.Expr]TypeAndValue)
+ _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for x, tv := range types {
+ var want Type
+ switch x := x.(type) {
+ case *ast.BasicLit:
+ switch x.Value {
+ case `8`:
+ want = Typ[Uint8]
+ case `16`:
+ want = Typ[Uint16]
+ case `32`:
+ want = Typ[Uint32]
+ case `64`:
+ want = Typ[Uint] // because of "+ s", s is of type uint
+ case `"foo"`:
+ want = Typ[String]
+ }
+ case *ast.Ident:
+ if x.Name == "nil" {
+ want = Typ[UntypedNil]
+ }
+ }
+ if want != nil && !Identical(tv.Type, want) {
+ t.Errorf("got %s; want %s", tv.Type, want)
+ }
+ }
+}
+
+func TestIssue6413(t *testing.T) {
+ src := `
+package p
+func f() int {
+ defer f()
+ go f()
+ return 0
+}
+`
+ f, err := parser.ParseFile(fset, "", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var conf Config
+ types := make(map[ast.Expr]TypeAndValue)
+ _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ want := Typ[Int]
+ n := 0
+ for x, tv := range types {
+ if _, ok := x.(*ast.CallExpr); ok {
+ if tv.Type != want {
+ t.Errorf("%s: got %s; want %s", fset.Position(x.Pos()), tv.Type, want)
+ }
+ n++
+ }
+ }
+
+ if n != 2 {
+ t.Errorf("got %d CallExprs; want 2", n)
+ }
+}
+
+func TestIssue7245(t *testing.T) {
+ src := `
+package p
+func (T) m() (res bool) { return }
+type T struct{} // receiver type after method declaration
+`
+ f, err := parser.ParseFile(fset, "", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var conf Config
+ defs := make(map[*ast.Ident]Object)
+ _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Defs: defs})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ m := f.Decls[0].(*ast.FuncDecl)
+ res1 := defs[m.Name].(*Func).Type().(*Signature).Results().At(0)
+ res2 := defs[m.Type.Results.List[0].Names[0]].(*Var)
+
+ if res1 != res2 {
+ t.Errorf("got %s (%p) != %s (%p)", res1, res2, res1, res2)
+ }
+}
+
+// This tests that uses of existing vars on the LHS of an assignment
+// are Uses, not Defs; and also that the (illegal) use of a non-var on
+// the LHS of an assignment is a Use nonetheless.
+func TestIssue7827(t *testing.T) {
+ const src = `
+package p
+func _() {
+ const w = 1 // defs w
+ x, y := 2, 3 // defs x, y
+ w, x, z := 4, 5, 6 // uses w, x, defs z; error: cannot assign to w
+ _, _, _ = x, y, z // uses x, y, z
+}
+`
+ const want = `L3 defs func p._()
+L4 defs const w untyped int
+L5 defs var x int
+L5 defs var y int
+L6 defs var z int
+L6 uses const w untyped int
+L6 uses var x int
+L7 uses var x int
+L7 uses var y int
+L7 uses var z int`
+
+ f, err := parser.ParseFile(fset, "", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // don't abort at the first error
+ conf := Config{Error: func(err error) { t.Log(err) }}
+ defs := make(map[*ast.Ident]Object)
+ uses := make(map[*ast.Ident]Object)
+ _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Defs: defs, Uses: uses})
+ if s := fmt.Sprint(err); !strings.HasSuffix(s, "cannot assign to w") {
+ t.Errorf("Check: unexpected error: %s", s)
+ }
+
+ var facts []string
+ for id, obj := range defs {
+ if obj != nil {
+ fact := fmt.Sprintf("L%d defs %s", fset.Position(id.Pos()).Line, obj)
+ facts = append(facts, fact)
+ }
+ }
+ for id, obj := range uses {
+ fact := fmt.Sprintf("L%d uses %s", fset.Position(id.Pos()).Line, obj)
+ facts = append(facts, fact)
+ }
+ sort.Strings(facts)
+
+ got := strings.Join(facts, "\n")
+ if got != want {
+ t.Errorf("Unexpected defs/uses\ngot:\n%s\nwant:\n%s", got, want)
+ }
+}
diff --git a/src/go/types/labels.go b/src/go/types/labels.go
new file mode 100644
index 0000000000..d6ffc52368
--- /dev/null
+++ b/src/go/types/labels.go
@@ -0,0 +1,268 @@
+// 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 types
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+// labels checks correct label use in body.
+func (check *Checker) labels(body *ast.BlockStmt) {
+ // set of all labels in this body
+ all := NewScope(nil, "label")
+
+ fwdJumps := check.blockBranches(all, nil, nil, body.List)
+
+ // If there are any forward jumps left, no label was found for
+ // the corresponding goto statements. Either those labels were
+ // never defined, or they are inside blocks and not reachable
+ // for the respective gotos.
+ for _, jmp := range fwdJumps {
+ var msg string
+ name := jmp.Label.Name
+ if alt := all.Lookup(name); alt != nil {
+ msg = "goto %s jumps into block"
+ alt.(*Label).used = true // avoid another error
+ } else {
+ msg = "label %s not declared"
+ }
+ check.errorf(jmp.Label.Pos(), msg, name)
+ }
+
+ // spec: "It is illegal to define a label that is never used."
+ for _, obj := range all.elems {
+ if lbl := obj.(*Label); !lbl.used {
+ check.softErrorf(lbl.pos, "label %s declared but not used", lbl.name)
+ }
+ }
+}
+
+// A block tracks label declarations in a block and its enclosing blocks.
+type block struct {
+ parent *block // enclosing block
+ lstmt *ast.LabeledStmt // labeled statement to which this block belongs, or nil
+ labels map[string]*ast.LabeledStmt // allocated lazily
+}
+
+// insert records a new label declaration for the current block.
+// The label must not have been declared before in any block.
+func (b *block) insert(s *ast.LabeledStmt) {
+ name := s.Label.Name
+ if debug {
+ assert(b.gotoTarget(name) == nil)
+ }
+ labels := b.labels
+ if labels == nil {
+ labels = make(map[string]*ast.LabeledStmt)
+ b.labels = labels
+ }
+ labels[name] = s
+}
+
+// gotoTarget returns the labeled statement in the current
+// or an enclosing block with the given label name, or nil.
+func (b *block) gotoTarget(name string) *ast.LabeledStmt {
+ for s := b; s != nil; s = s.parent {
+ if t := s.labels[name]; t != nil {
+ return t
+ }
+ }
+ return nil
+}
+
+// enclosingTarget returns the innermost enclosing labeled
+// statement with the given label name, or nil.
+func (b *block) enclosingTarget(name string) *ast.LabeledStmt {
+ for s := b; s != nil; s = s.parent {
+ if t := s.lstmt; t != nil && t.Label.Name == name {
+ return t
+ }
+ }
+ return nil
+}
+
+// blockBranches processes a block's statement list and returns the set of outgoing forward jumps.
+// all is the scope of all declared labels, parent the set of labels declared in the immediately
+// enclosing block, and lstmt is the labeled statement this block is associated with (or nil).
+func (check *Checker) blockBranches(all *Scope, parent *block, lstmt *ast.LabeledStmt, list []ast.Stmt) []*ast.BranchStmt {
+ b := &block{parent: parent, lstmt: lstmt}
+
+ var (
+ varDeclPos token.Pos
+ fwdJumps, badJumps []*ast.BranchStmt
+ )
+
+ // All forward jumps jumping over a variable declaration are possibly
+ // invalid (they may still jump out of the block and be ok).
+ // recordVarDecl records them for the given position.
+ recordVarDecl := func(pos token.Pos) {
+ varDeclPos = pos
+ badJumps = append(badJumps[:0], fwdJumps...) // copy fwdJumps to badJumps
+ }
+
+ jumpsOverVarDecl := func(jmp *ast.BranchStmt) bool {
+ if varDeclPos.IsValid() {
+ for _, bad := range badJumps {
+ if jmp == bad {
+ return true
+ }
+ }
+ }
+ return false
+ }
+
+ blockBranches := func(lstmt *ast.LabeledStmt, list []ast.Stmt) {
+ // Unresolved forward jumps inside the nested block
+ // become forward jumps in the current block.
+ fwdJumps = append(fwdJumps, check.blockBranches(all, b, lstmt, list)...)
+ }
+
+ var stmtBranches func(ast.Stmt)
+ stmtBranches = func(s ast.Stmt) {
+ switch s := s.(type) {
+ case *ast.DeclStmt:
+ if d, _ := s.Decl.(*ast.GenDecl); d != nil && d.Tok == token.VAR {
+ recordVarDecl(d.Pos())
+ }
+
+ case *ast.LabeledStmt:
+ // declare non-blank label
+ if name := s.Label.Name; name != "_" {
+ lbl := NewLabel(s.Label.Pos(), check.pkg, name)
+ if alt := all.Insert(lbl); alt != nil {
+ check.softErrorf(lbl.pos, "label %s already declared", name)
+ check.reportAltDecl(alt)
+ // ok to continue
+ } else {
+ b.insert(s)
+ check.recordDef(s.Label, lbl)
+ }
+ // resolve matching forward jumps and remove them from fwdJumps
+ i := 0
+ for _, jmp := range fwdJumps {
+ if jmp.Label.Name == name {
+ // match
+ lbl.used = true
+ check.recordUse(jmp.Label, lbl)
+ if jumpsOverVarDecl(jmp) {
+ check.softErrorf(
+ jmp.Label.Pos(),
+ "goto %s jumps over variable declaration at line %d",
+ name,
+ check.fset.Position(varDeclPos).Line,
+ )
+ // ok to continue
+ }
+ } else {
+ // no match - record new forward jump
+ fwdJumps[i] = jmp
+ i++
+ }
+ }
+ fwdJumps = fwdJumps[:i]
+ lstmt = s
+ }
+ stmtBranches(s.Stmt)
+
+ case *ast.BranchStmt:
+ if s.Label == nil {
+ return // checked in 1st pass (check.stmt)
+ }
+
+ // determine and validate target
+ name := s.Label.Name
+ switch s.Tok {
+ case token.BREAK:
+ // spec: "If there is a label, it must be that of an enclosing
+ // "for", "switch", or "select" statement, and that is the one
+ // whose execution terminates."
+ valid := false
+ if t := b.enclosingTarget(name); t != nil {
+ switch t.Stmt.(type) {
+ case *ast.SwitchStmt, *ast.TypeSwitchStmt, *ast.SelectStmt, *ast.ForStmt, *ast.RangeStmt:
+ valid = true
+ }
+ }
+ if !valid {
+ check.errorf(s.Label.Pos(), "invalid break label %s", name)
+ return
+ }
+
+ case token.CONTINUE:
+ // spec: "If there is a label, it must be that of an enclosing
+ // "for" statement, and that is the one whose execution advances."
+ valid := false
+ if t := b.enclosingTarget(name); t != nil {
+ switch t.Stmt.(type) {
+ case *ast.ForStmt, *ast.RangeStmt:
+ valid = true
+ }
+ }
+ if !valid {
+ check.errorf(s.Label.Pos(), "invalid continue label %s", name)
+ return
+ }
+
+ case token.GOTO:
+ if b.gotoTarget(name) == nil {
+ // label may be declared later - add branch to forward jumps
+ fwdJumps = append(fwdJumps, s)
+ return
+ }
+
+ default:
+ check.invalidAST(s.Pos(), "branch statement: %s %s", s.Tok, name)
+ return
+ }
+
+ // record label use
+ obj := all.Lookup(name)
+ obj.(*Label).used = true
+ check.recordUse(s.Label, obj)
+
+ case *ast.AssignStmt:
+ if s.Tok == token.DEFINE {
+ recordVarDecl(s.Pos())
+ }
+
+ case *ast.BlockStmt:
+ blockBranches(lstmt, s.List)
+
+ case *ast.IfStmt:
+ stmtBranches(s.Body)
+ if s.Else != nil {
+ stmtBranches(s.Else)
+ }
+
+ case *ast.CaseClause:
+ blockBranches(nil, s.Body)
+
+ case *ast.SwitchStmt:
+ stmtBranches(s.Body)
+
+ case *ast.TypeSwitchStmt:
+ stmtBranches(s.Body)
+
+ case *ast.CommClause:
+ blockBranches(nil, s.Body)
+
+ case *ast.SelectStmt:
+ stmtBranches(s.Body)
+
+ case *ast.ForStmt:
+ stmtBranches(s.Body)
+
+ case *ast.RangeStmt:
+ stmtBranches(s.Body)
+ }
+ }
+
+ for _, s := range list {
+ stmtBranches(s)
+ }
+
+ return fwdJumps
+}
diff --git a/src/go/types/lookup.go b/src/go/types/lookup.go
new file mode 100644
index 0000000000..3caca5519b
--- /dev/null
+++ b/src/go/types/lookup.go
@@ -0,0 +1,341 @@
+// 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 various field and method lookup functions.
+
+package types
+
+// LookupFieldOrMethod looks up a field or method with given package and name
+// in T and returns the corresponding *Var or *Func, an index sequence, and a
+// bool indicating if there were any pointer indirections on the path to the
+// field or method. If addressable is set, T is the type of an addressable
+// variable (only matters for method lookups).
+//
+// The last index entry is the field or method index in the (possibly embedded)
+// type where the entry was found, either:
+//
+// 1) the list of declared methods of a named type; or
+// 2) the list of all methods (method set) of an interface type; or
+// 3) the list of fields of a struct type.
+//
+// The earlier index entries are the indices of the anonymous struct fields
+// traversed to get to the found entry, starting at depth 0.
+//
+// If no entry is found, a nil object is returned. In this case, the returned
+// index and indirect values have the following meaning:
+//
+// - If index != nil, the index sequence points to an ambiguous entry
+// (the same name appeared more than once at the same embedding level).
+//
+// - If indirect is set, a method with a pointer receiver type was found
+// but there was no pointer on the path from the actual receiver type to
+// the method's formal receiver base type, nor was the receiver addressable.
+//
+func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
+ // Methods cannot be associated to a named pointer type
+ // (spec: "The type denoted by T is called the receiver base type;
+ // it must not be a pointer or interface type and it must be declared
+ // in the same package as the method.").
+ // Thus, if we have a named pointer type, proceed with the underlying
+ // pointer type but discard the result if it is a method since we would
+ // not have found it for T (see also issue 8590).
+ if t, _ := T.(*Named); t != nil {
+ if p, _ := t.underlying.(*Pointer); p != nil {
+ obj, index, indirect = lookupFieldOrMethod(p, false, pkg, name)
+ if _, ok := obj.(*Func); ok {
+ return nil, nil, false
+ }
+ return
+ }
+ }
+
+ return lookupFieldOrMethod(T, addressable, pkg, name)
+}
+
+// TODO(gri) The named type consolidation and seen maps below must be
+// indexed by unique keys for a given type. Verify that named
+// types always have only one representation (even when imported
+// indirectly via different packages.)
+
+func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
+ // WARNING: The code in this function is extremely subtle - do not modify casually!
+ // This function and NewMethodSet should be kept in sync.
+
+ if name == "_" {
+ return // blank fields/methods are never found
+ }
+
+ typ, isPtr := deref(T)
+ named, _ := typ.(*Named)
+
+ // *typ where typ is an interface has no methods.
+ if isPtr {
+ utyp := typ
+ if named != nil {
+ utyp = named.underlying
+ }
+ if _, ok := utyp.(*Interface); ok {
+ return
+ }
+ }
+
+ // Start with typ as single entry at shallowest depth.
+ // If typ is not a named type, insert a nil type instead.
+ current := []embeddedType{{named, nil, isPtr, false}}
+
+ // named types that we have seen already, allocated lazily
+ var seen map[*Named]bool
+
+ // search current depth
+ for len(current) > 0 {
+ var next []embeddedType // embedded types found at current depth
+
+ // look for (pkg, name) in all types at current depth
+ for _, e := range current {
+ // The very first time only, e.typ may be nil.
+ // In this case, we don't have a named type and
+ // we simply continue with the underlying type.
+ if e.typ != nil {
+ if seen[e.typ] {
+ // We have seen this type before, at a more shallow depth
+ // (note that multiples of this type at the current depth
+ // were consolidated before). The type at that depth shadows
+ // this same type at the current depth, so we can ignore
+ // this one.
+ continue
+ }
+ if seen == nil {
+ seen = make(map[*Named]bool)
+ }
+ seen[e.typ] = true
+
+ // look for a matching attached method
+ if i, m := lookupMethod(e.typ.methods, pkg, name); m != nil {
+ // potential match
+ assert(m.typ != nil)
+ index = concat(e.index, i)
+ if obj != nil || e.multiples {
+ return nil, index, false // collision
+ }
+ obj = m
+ indirect = e.indirect
+ continue // we can't have a matching field or interface method
+ }
+
+ // continue with underlying type
+ typ = e.typ.underlying
+ }
+
+ switch t := typ.(type) {
+ case *Struct:
+ // look for a matching field and collect embedded types
+ for i, f := range t.fields {
+ if f.sameId(pkg, name) {
+ assert(f.typ != nil)
+ index = concat(e.index, i)
+ if obj != nil || e.multiples {
+ return nil, index, false // collision
+ }
+ obj = f
+ indirect = e.indirect
+ continue // we can't have a matching interface method
+ }
+ // Collect embedded struct fields for searching the next
+ // lower depth, but only if we have not seen a match yet
+ // (if we have a match it is either the desired field or
+ // we have a name collision on the same depth; in either
+ // case we don't need to look further).
+ // Embedded fields are always of the form T or *T where
+ // T is a named type. If e.typ appeared multiple times at
+ // this depth, f.typ appears multiple times at the next
+ // depth.
+ if obj == nil && f.anonymous {
+ // Ignore embedded basic types - only user-defined
+ // named types can have methods or struct fields.
+ typ, isPtr := deref(f.typ)
+ if t, _ := typ.(*Named); t != nil {
+ next = append(next, embeddedType{t, concat(e.index, i), e.indirect || isPtr, e.multiples})
+ }
+ }
+ }
+
+ case *Interface:
+ // look for a matching method
+ // TODO(gri) t.allMethods is sorted - use binary search
+ if i, m := lookupMethod(t.allMethods, pkg, name); m != nil {
+ assert(m.typ != nil)
+ index = concat(e.index, i)
+ if obj != nil || e.multiples {
+ return nil, index, false // collision
+ }
+ obj = m
+ indirect = e.indirect
+ }
+ }
+ }
+
+ if obj != nil {
+ // found a potential match
+ // spec: "A method call x.m() is valid if the method set of (the type of) x
+ // contains m and the argument list can be assigned to the parameter
+ // list of m. If x is addressable and &x's method set contains m, x.m()
+ // is shorthand for (&x).m()".
+ if f, _ := obj.(*Func); f != nil && ptrRecv(f) && !indirect && !addressable {
+ return nil, nil, true // pointer/addressable receiver required
+ }
+ return
+ }
+
+ current = consolidateMultiples(next)
+ }
+
+ return nil, nil, false // not found
+}
+
+// embeddedType represents an embedded named type
+type embeddedType struct {
+ typ *Named // nil means use the outer typ variable instead
+ index []int // embedded field indices, starting with index at depth 0
+ indirect bool // if set, there was a pointer indirection on the path to this field
+ multiples bool // if set, typ appears multiple times at this depth
+}
+
+// consolidateMultiples collects multiple list entries with the same type
+// into a single entry marked as containing multiples. The result is the
+// consolidated list.
+func consolidateMultiples(list []embeddedType) []embeddedType {
+ if len(list) <= 1 {
+ return list // at most one entry - nothing to do
+ }
+
+ n := 0 // number of entries w/ unique type
+ prev := make(map[*Named]int) // index at which type was previously seen
+ for _, e := range list {
+ if i, found := prev[e.typ]; found {
+ list[i].multiples = true
+ // ignore this entry
+ } else {
+ prev[e.typ] = n
+ list[n] = e
+ n++
+ }
+ }
+ return list[:n]
+}
+
+// MissingMethod returns (nil, false) if V implements T, otherwise it
+// returns a missing method required by T and whether it is missing or
+// just has the wrong type.
+//
+// For non-interface types V, or if static is set, V implements T if all
+// methods of T are present in V. Otherwise (V is an interface and static
+// is not set), MissingMethod only checks that methods of T which are also
+// present in V have matching types (e.g., for a type assertion x.(T) where
+// x is of interface type V).
+//
+func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) {
+ // fast path for common case
+ if T.Empty() {
+ return
+ }
+
+ // TODO(gri) Consider using method sets here. Might be more efficient.
+
+ if ityp, _ := V.Underlying().(*Interface); ityp != nil {
+ // TODO(gri) allMethods is sorted - can do this more efficiently
+ for _, m := range T.allMethods {
+ _, obj := lookupMethod(ityp.allMethods, m.pkg, m.name)
+ switch {
+ case obj == nil:
+ if static {
+ return m, false
+ }
+ case !Identical(obj.Type(), m.typ):
+ return m, true
+ }
+ }
+ return
+ }
+
+ // A concrete type implements T if it implements all methods of T.
+ for _, m := range T.allMethods {
+ obj, _, _ := lookupFieldOrMethod(V, false, m.pkg, m.name)
+
+ f, _ := obj.(*Func)
+ if f == nil {
+ return m, false
+ }
+
+ if !Identical(f.typ, m.typ) {
+ return m, true
+ }
+ }
+
+ return
+}
+
+// assertableTo reports whether a value of type V can be asserted to have type T.
+// It returns (nil, false) as affirmative answer. Otherwise it returns a missing
+// method required by V and whether it is missing or just has the wrong type.
+func assertableTo(V *Interface, T Type) (method *Func, wrongType bool) {
+ // no static check is required if T is an interface
+ // spec: "If T is an interface type, x.(T) asserts that the
+ // dynamic type of x implements the interface T."
+ if _, ok := T.Underlying().(*Interface); ok && !strict {
+ return
+ }
+ return MissingMethod(T, V, false)
+}
+
+// deref dereferences typ if it is a *Pointer and returns its base and true.
+// Otherwise it returns (typ, false).
+func deref(typ Type) (Type, bool) {
+ if p, _ := typ.(*Pointer); p != nil {
+ return p.base, true
+ }
+ return typ, false
+}
+
+// derefStructPtr dereferences typ if it is a (named or unnamed) pointer to a
+// (named or unnamed) struct and returns its base. Otherwise it returns typ.
+func derefStructPtr(typ Type) Type {
+ if p, _ := typ.Underlying().(*Pointer); p != nil {
+ if _, ok := p.base.Underlying().(*Struct); ok {
+ return p.base
+ }
+ }
+ return typ
+}
+
+// concat returns the result of concatenating list and i.
+// The result does not share its underlying array with list.
+func concat(list []int, i int) []int {
+ var t []int
+ t = append(t, list...)
+ return append(t, i)
+}
+
+// fieldIndex returns the index for the field with matching package and name, or a value < 0.
+func fieldIndex(fields []*Var, pkg *Package, name string) int {
+ if name != "_" {
+ for i, f := range fields {
+ if f.sameId(pkg, name) {
+ return i
+ }
+ }
+ }
+ return -1
+}
+
+// lookupMethod returns the index of and method with matching package and name, or (-1, nil).
+func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) {
+ if name != "_" {
+ for i, m := range methods {
+ if m.sameId(pkg, name) {
+ return i, m
+ }
+ }
+ }
+ return -1, nil
+}
diff --git a/src/go/types/methodset.go b/src/go/types/methodset.go
new file mode 100644
index 0000000000..8aff6f9ba4
--- /dev/null
+++ b/src/go/types/methodset.go
@@ -0,0 +1,271 @@
+// 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 method sets.
+
+package types
+
+import (
+ "bytes"
+ "fmt"
+ "sort"
+)
+
+// A MethodSet is an ordered set of concrete or abstract (interface) methods;
+// a method is a MethodVal selection, and they are ordered by ascending m.Obj().Id().
+// The zero value for a MethodSet is a ready-to-use empty method set.
+type MethodSet struct {
+ list []*Selection
+}
+
+func (s *MethodSet) String() string {
+ if s.Len() == 0 {
+ return "MethodSet {}"
+ }
+
+ var buf bytes.Buffer
+ fmt.Fprintln(&buf, "MethodSet {")
+ for _, f := range s.list {
+ fmt.Fprintf(&buf, "\t%s\n", f)
+ }
+ fmt.Fprintln(&buf, "}")
+ return buf.String()
+}
+
+// Len returns the number of methods in s.
+func (s *MethodSet) Len() int { return len(s.list) }
+
+// At returns the i'th method in s for 0 <= i < s.Len().
+func (s *MethodSet) At(i int) *Selection { return s.list[i] }
+
+// Lookup returns the method with matching package and name, or nil if not found.
+func (s *MethodSet) Lookup(pkg *Package, name string) *Selection {
+ if s.Len() == 0 {
+ return nil
+ }
+
+ key := Id(pkg, name)
+ i := sort.Search(len(s.list), func(i int) bool {
+ m := s.list[i]
+ return m.obj.Id() >= key
+ })
+ if i < len(s.list) {
+ m := s.list[i]
+ if m.obj.Id() == key {
+ return m
+ }
+ }
+ return nil
+}
+
+// Shared empty method set.
+var emptyMethodSet MethodSet
+
+// NewMethodSet returns the method set for the given type T. It
+// always returns a non-nil method set, even if it is empty.
+//
+// A MethodSetCache handles repeat queries more efficiently.
+//
+func NewMethodSet(T Type) *MethodSet {
+ // WARNING: The code in this function is extremely subtle - do not modify casually!
+ // This function and lookupFieldOrMethod should be kept in sync.
+
+ // method set up to the current depth, allocated lazily
+ var base methodSet
+
+ typ, isPtr := deref(T)
+ named, _ := typ.(*Named)
+
+ // *typ where typ is an interface has no methods.
+ if isPtr {
+ utyp := typ
+ if named != nil {
+ utyp = named.underlying
+ }
+ if _, ok := utyp.(*Interface); ok {
+ return &emptyMethodSet
+ }
+ }
+
+ // Start with typ as single entry at shallowest depth.
+ // If typ is not a named type, insert a nil type instead.
+ current := []embeddedType{{named, nil, isPtr, false}}
+
+ // named types that we have seen already, allocated lazily
+ var seen map[*Named]bool
+
+ // collect methods at current depth
+ for len(current) > 0 {
+ var next []embeddedType // embedded types found at current depth
+
+ // field and method sets at current depth, allocated lazily
+ var fset fieldSet
+ var mset methodSet
+
+ for _, e := range current {
+ // The very first time only, e.typ may be nil.
+ // In this case, we don't have a named type and
+ // we simply continue with the underlying type.
+ if e.typ != nil {
+ if seen[e.typ] {
+ // We have seen this type before, at a more shallow depth
+ // (note that multiples of this type at the current depth
+ // were consolidated before). The type at that depth shadows
+ // this same type at the current depth, so we can ignore
+ // this one.
+ continue
+ }
+ if seen == nil {
+ seen = make(map[*Named]bool)
+ }
+ seen[e.typ] = true
+
+ mset = mset.add(e.typ.methods, e.index, e.indirect, e.multiples)
+
+ // continue with underlying type
+ typ = e.typ.underlying
+ }
+
+ switch t := typ.(type) {
+ case *Struct:
+ for i, f := range t.fields {
+ fset = fset.add(f, e.multiples)
+
+ // Embedded fields are always of the form T or *T where
+ // T is a named type. If typ appeared multiple times at
+ // this depth, f.Type appears multiple times at the next
+ // depth.
+ if f.anonymous {
+ // Ignore embedded basic types - only user-defined
+ // named types can have methods or struct fields.
+ typ, isPtr := deref(f.typ)
+ if t, _ := typ.(*Named); t != nil {
+ next = append(next, embeddedType{t, concat(e.index, i), e.indirect || isPtr, e.multiples})
+ }
+ }
+ }
+
+ case *Interface:
+ mset = mset.add(t.allMethods, e.index, true, e.multiples)
+ }
+ }
+
+ // Add methods and collisions at this depth to base if no entries with matching
+ // names exist already.
+ for k, m := range mset {
+ if _, found := base[k]; !found {
+ // Fields collide with methods of the same name at this depth.
+ if _, found := fset[k]; found {
+ m = nil // collision
+ }
+ if base == nil {
+ base = make(methodSet)
+ }
+ base[k] = m
+ }
+ }
+
+ // Multiple fields with matching names collide at this depth and shadow all
+ // entries further down; add them as collisions to base if no entries with
+ // matching names exist already.
+ for k, f := range fset {
+ if f == nil {
+ if _, found := base[k]; !found {
+ if base == nil {
+ base = make(methodSet)
+ }
+ base[k] = nil // collision
+ }
+ }
+ }
+
+ current = consolidateMultiples(next)
+ }
+
+ if len(base) == 0 {
+ return &emptyMethodSet
+ }
+
+ // collect methods
+ var list []*Selection
+ for _, m := range base {
+ if m != nil {
+ m.recv = T
+ list = append(list, m)
+ }
+ }
+ sort.Sort(byUniqueName(list))
+ return &MethodSet{list}
+}
+
+// A fieldSet is a set of fields and name collisions.
+// A collision indicates that multiple fields with the
+// same unique id appeared.
+type fieldSet map[string]*Var // a nil entry indicates a name collision
+
+// Add adds field f to the field set s.
+// If multiples is set, f appears multiple times
+// and is treated as a collision.
+func (s fieldSet) add(f *Var, multiples bool) fieldSet {
+ if s == nil {
+ s = make(fieldSet)
+ }
+ key := f.Id()
+ // if f is not in the set, add it
+ if !multiples {
+ if _, found := s[key]; !found {
+ s[key] = f
+ return s
+ }
+ }
+ s[key] = nil // collision
+ return s
+}
+
+// A methodSet is a set of methods and name collisions.
+// A collision indicates that multiple methods with the
+// same unique id appeared.
+type methodSet map[string]*Selection // a nil entry indicates a name collision
+
+// Add adds all functions in list to the method set s.
+// If multiples is set, every function in list appears multiple times
+// and is treated as a collision.
+func (s methodSet) add(list []*Func, index []int, indirect bool, multiples bool) methodSet {
+ if len(list) == 0 {
+ return s
+ }
+ if s == nil {
+ s = make(methodSet)
+ }
+ for i, f := range list {
+ key := f.Id()
+ // if f is not in the set, add it
+ if !multiples {
+ // TODO(gri) A found method may not be added because it's not in the method set
+ // (!indirect && ptrRecv(f)). A 2nd method on the same level may be in the method
+ // set and may not collide with the first one, thus leading to a false positive.
+ // Is that possible? Investigate.
+ if _, found := s[key]; !found && (indirect || !ptrRecv(f)) {
+ s[key] = &Selection{MethodVal, nil, f, concat(index, i), indirect}
+ continue
+ }
+ }
+ s[key] = nil // collision
+ }
+ return s
+}
+
+// ptrRecv reports whether the receiver is of the form *T.
+// The receiver must exist.
+func ptrRecv(f *Func) bool {
+ _, isPtr := deref(f.typ.(*Signature).recv.typ)
+ return isPtr
+}
+
+// byUniqueName function lists can be sorted by their unique names.
+type byUniqueName []*Selection
+
+func (a byUniqueName) Len() int { return len(a) }
+func (a byUniqueName) Less(i, j int) bool { return a[i].obj.Id() < a[j].obj.Id() }
+func (a byUniqueName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
diff --git a/src/go/types/methodsetcache.go b/src/go/types/methodsetcache.go
new file mode 100644
index 0000000000..5a482e952a
--- /dev/null
+++ b/src/go/types/methodsetcache.go
@@ -0,0 +1,69 @@
+// Copyright 2014 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 a cache of method sets.
+
+package types
+
+import "sync"
+
+// A MethodSetCache records the method set of each type T for which
+// MethodSet(T) is called so that repeat queries are fast.
+// The zero value is a ready-to-use cache instance.
+type MethodSetCache struct {
+ mu sync.Mutex
+ named map[*Named]struct{ value, pointer *MethodSet } // method sets for named N and *N
+ others map[Type]*MethodSet // all other types
+}
+
+// MethodSet returns the method set of type T. It is thread-safe.
+//
+// If cache is nil, this function is equivalent to NewMethodSet(T).
+// Utility functions can thus expose an optional *MethodSetCache
+// parameter to clients that care about performance.
+//
+func (cache *MethodSetCache) MethodSet(T Type) *MethodSet {
+ if cache == nil {
+ return NewMethodSet(T)
+ }
+ cache.mu.Lock()
+ defer cache.mu.Unlock()
+
+ switch T := T.(type) {
+ case *Named:
+ return cache.lookupNamed(T).value
+
+ case *Pointer:
+ if N, ok := T.Elem().(*Named); ok {
+ return cache.lookupNamed(N).pointer
+ }
+ }
+
+ // all other types
+ // (The map uses pointer equivalence, not type identity.)
+ mset := cache.others[T]
+ if mset == nil {
+ mset = NewMethodSet(T)
+ if cache.others == nil {
+ cache.others = make(map[Type]*MethodSet)
+ }
+ cache.others[T] = mset
+ }
+ return mset
+}
+
+func (cache *MethodSetCache) lookupNamed(named *Named) struct{ value, pointer *MethodSet } {
+ if cache.named == nil {
+ cache.named = make(map[*Named]struct{ value, pointer *MethodSet })
+ }
+ // Avoid recomputing mset(*T) for each distinct Pointer
+ // instance whose underlying type is a named type.
+ msets, ok := cache.named[named]
+ if !ok {
+ msets.value = NewMethodSet(named)
+ msets.pointer = NewMethodSet(NewPointer(named))
+ cache.named[named] = msets
+ }
+ return msets
+}
diff --git a/src/go/types/object.go b/src/go/types/object.go
new file mode 100644
index 0000000000..2404753b36
--- /dev/null
+++ b/src/go/types/object.go
@@ -0,0 +1,339 @@
+// 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 types
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+ "go/token"
+)
+
+// TODO(gri) Document factory, accessor methods, and fields. General clean-up.
+
+// An Object describes a named language entity such as a package,
+// constant, type, variable, function (incl. methods), or label.
+// All objects implement the Object interface.
+//
+type Object interface {
+ Parent() *Scope // scope in which this object is declared
+ Pos() token.Pos // position of object identifier in declaration
+ Pkg() *Package // nil for objects in the Universe scope and labels
+ Name() string // package local object name
+ Type() Type // object type
+ Exported() bool // reports whether the name starts with a capital letter
+ Id() string // object id (see Id below)
+
+ // String returns a human-readable string of the object.
+ String() string
+
+ // order reflects a package-level object's source order: if object
+ // a is before object b in the source, then a.order() < b.order().
+ // order returns a value > 0 for package-level objects; it returns
+ // 0 for all other objects (including objects in file scopes).
+ order() uint32
+
+ // setOrder sets the order number of the object. It must be > 0.
+ setOrder(uint32)
+
+ // setParent sets the parent scope of the object.
+ setParent(*Scope)
+
+ // sameId reports whether obj.Id() and Id(pkg, name) are the same.
+ sameId(pkg *Package, name string) bool
+}
+
+// Id returns name if it is exported, otherwise it
+// returns the name qualified with the package path.
+func Id(pkg *Package, name string) string {
+ if ast.IsExported(name) {
+ return name
+ }
+ // unexported names need the package path for differentiation
+ // (if there's no package, make sure we don't start with '.'
+ // as that may change the order of methods between a setup
+ // inside a package and outside a package - which breaks some
+ // tests)
+ path := "_"
+ // TODO(gri): shouldn't !ast.IsExported(name) => pkg != nil be an precondition?
+ // if pkg == nil {
+ // panic("nil package in lookup of unexported name")
+ // }
+ if pkg != nil {
+ path = pkg.path
+ if path == "" {
+ path = "_"
+ }
+ }
+ return path + "." + name
+}
+
+// An object implements the common parts of an Object.
+type object struct {
+ parent *Scope
+ pos token.Pos
+ pkg *Package
+ name string
+ typ Type
+ order_ uint32
+}
+
+func (obj *object) Parent() *Scope { return obj.parent }
+func (obj *object) Pos() token.Pos { return obj.pos }
+func (obj *object) Pkg() *Package { return obj.pkg }
+func (obj *object) Name() string { return obj.name }
+func (obj *object) Type() Type { return obj.typ }
+func (obj *object) Exported() bool { return ast.IsExported(obj.name) }
+func (obj *object) Id() string { return Id(obj.pkg, obj.name) }
+func (obj *object) String() string { panic("abstract") }
+func (obj *object) order() uint32 { return obj.order_ }
+
+func (obj *object) setOrder(order uint32) { assert(order > 0); obj.order_ = order }
+func (obj *object) setParent(parent *Scope) { obj.parent = parent }
+
+func (obj *object) sameId(pkg *Package, name string) bool {
+ // spec:
+ // "Two identifiers are different if they are spelled differently,
+ // or if they appear in different packages and are not exported.
+ // Otherwise, they are the same."
+ if name != obj.name {
+ return false
+ }
+ // obj.Name == name
+ if obj.Exported() {
+ return true
+ }
+ // not exported, so packages must be the same (pkg == nil for
+ // fields in Universe scope; this can only happen for types
+ // introduced via Eval)
+ if pkg == nil || obj.pkg == nil {
+ return pkg == obj.pkg
+ }
+ // pkg != nil && obj.pkg != nil
+ return pkg.path == obj.pkg.path
+}
+
+// A PkgName represents an imported Go package.
+type PkgName struct {
+ object
+ imported *Package
+ used bool // set if the package was used
+}
+
+func NewPkgName(pos token.Pos, pkg *Package, name string, imported *Package) *PkgName {
+ return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], 0}, imported, false}
+}
+
+// Imported returns the package that was imported.
+// It is distinct from Pkg(), which is the package containing the import statement.
+func (obj *PkgName) Imported() *Package { return obj.imported }
+
+// A Const represents a declared constant.
+type Const struct {
+ object
+ val exact.Value
+ visited bool // for initialization cycle detection
+}
+
+func NewConst(pos token.Pos, pkg *Package, name string, typ Type, val exact.Value) *Const {
+ return &Const{object{nil, pos, pkg, name, typ, 0}, val, false}
+}
+
+func (obj *Const) Val() exact.Value { return obj.val }
+
+// A TypeName represents a declared type.
+type TypeName struct {
+ object
+}
+
+func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName {
+ return &TypeName{object{nil, pos, pkg, name, typ, 0}}
+}
+
+// A Variable represents a declared variable (including function parameters and results, and struct fields).
+type Var struct {
+ object
+ anonymous bool // if set, the variable is an anonymous struct field, and name is the type name
+ visited bool // for initialization cycle detection
+ isField bool // var is struct field
+ used bool // set if the variable was used
+}
+
+func NewVar(pos token.Pos, pkg *Package, name string, typ Type) *Var {
+ return &Var{object: object{nil, pos, pkg, name, typ, 0}}
+}
+
+func NewParam(pos token.Pos, pkg *Package, name string, typ Type) *Var {
+ return &Var{object: object{nil, pos, pkg, name, typ, 0}, used: true} // parameters are always 'used'
+}
+
+func NewField(pos token.Pos, pkg *Package, name string, typ Type, anonymous bool) *Var {
+ return &Var{object: object{nil, pos, pkg, name, typ, 0}, anonymous: anonymous, isField: true}
+}
+
+func (obj *Var) Anonymous() bool { return obj.anonymous }
+
+func (obj *Var) IsField() bool { return obj.isField }
+
+// A Func represents a declared function, concrete method, or abstract
+// (interface) method. Its Type() is always a *Signature.
+// An abstract method may belong to many interfaces due to embedding.
+type Func struct {
+ object
+}
+
+func NewFunc(pos token.Pos, pkg *Package, name string, sig *Signature) *Func {
+ // don't store a nil signature
+ var typ Type
+ if sig != nil {
+ typ = sig
+ }
+ return &Func{object{nil, pos, pkg, name, typ, 0}}
+}
+
+// FullName returns the package- or receiver-type-qualified name of
+// function or method obj.
+func (obj *Func) FullName() string {
+ var buf bytes.Buffer
+ writeFuncName(&buf, nil, obj)
+ return buf.String()
+}
+
+func (obj *Func) Scope() *Scope {
+ return obj.typ.(*Signature).scope
+}
+
+// A Label represents a declared label.
+type Label struct {
+ object
+ used bool // set if the label was used
+}
+
+func NewLabel(pos token.Pos, pkg *Package, name string) *Label {
+ return &Label{object{pos: pos, pkg: pkg, name: name, typ: Typ[Invalid]}, false}
+}
+
+// A Builtin represents a built-in function.
+// Builtins don't have a valid type.
+type Builtin struct {
+ object
+ id builtinId
+}
+
+func newBuiltin(id builtinId) *Builtin {
+ return &Builtin{object{name: predeclaredFuncs[id].name, typ: Typ[Invalid]}, id}
+}
+
+// Nil represents the predeclared value nil.
+type Nil struct {
+ object
+}
+
+func writeObject(buf *bytes.Buffer, this *Package, obj Object) {
+ typ := obj.Type()
+ switch obj := obj.(type) {
+ case *PkgName:
+ fmt.Fprintf(buf, "package %s", obj.Name())
+ if path := obj.imported.path; path != "" && path != obj.name {
+ fmt.Fprintf(buf, " (%q)", path)
+ }
+ return
+
+ case *Const:
+ buf.WriteString("const")
+
+ case *TypeName:
+ buf.WriteString("type")
+ typ = typ.Underlying()
+
+ case *Var:
+ if obj.isField {
+ buf.WriteString("field")
+ } else {
+ buf.WriteString("var")
+ }
+
+ case *Func:
+ buf.WriteString("func ")
+ writeFuncName(buf, this, obj)
+ if typ != nil {
+ WriteSignature(buf, this, typ.(*Signature))
+ }
+ return
+
+ case *Label:
+ buf.WriteString("label")
+ typ = nil
+
+ case *Builtin:
+ buf.WriteString("builtin")
+ typ = nil
+
+ case *Nil:
+ buf.WriteString("nil")
+ return
+
+ default:
+ panic(fmt.Sprintf("writeObject(%T)", obj))
+ }
+
+ buf.WriteByte(' ')
+
+ // For package-level objects, package-qualify the name,
+ // except for intra-package references (this != nil).
+ if pkg := obj.Pkg(); pkg != nil && this != pkg && pkg.scope.Lookup(obj.Name()) == obj {
+ buf.WriteString(pkg.path)
+ buf.WriteByte('.')
+ }
+ buf.WriteString(obj.Name())
+ if typ != nil {
+ buf.WriteByte(' ')
+ WriteType(buf, this, typ)
+ }
+}
+
+// ObjectString returns the string form of obj.
+// Object and type names are printed package-qualified
+// only if they do not belong to this package.
+//
+func ObjectString(this *Package, obj Object) string {
+ var buf bytes.Buffer
+ writeObject(&buf, this, obj)
+ return buf.String()
+}
+
+func (obj *PkgName) String() string { return ObjectString(nil, obj) }
+func (obj *Const) String() string { return ObjectString(nil, obj) }
+func (obj *TypeName) String() string { return ObjectString(nil, obj) }
+func (obj *Var) String() string { return ObjectString(nil, obj) }
+func (obj *Func) String() string { return ObjectString(nil, obj) }
+func (obj *Label) String() string { return ObjectString(nil, obj) }
+func (obj *Builtin) String() string { return ObjectString(nil, obj) }
+func (obj *Nil) String() string { return ObjectString(nil, obj) }
+
+func writeFuncName(buf *bytes.Buffer, this *Package, f *Func) {
+ if f.typ != nil {
+ sig := f.typ.(*Signature)
+ if recv := sig.Recv(); recv != nil {
+ buf.WriteByte('(')
+ if _, ok := recv.Type().(*Interface); ok {
+ // gcimporter creates abstract methods of
+ // named interfaces using the interface type
+ // (not the named type) as the receiver.
+ // Don't print it in full.
+ buf.WriteString("interface")
+ } else {
+ WriteType(buf, this, recv.Type())
+ }
+ buf.WriteByte(')')
+ buf.WriteByte('.')
+ } else if f.pkg != nil && f.pkg != this {
+ buf.WriteString(f.pkg.path)
+ buf.WriteByte('.')
+ }
+ }
+ buf.WriteString(f.name)
+}
diff --git a/src/go/types/objset.go b/src/go/types/objset.go
new file mode 100644
index 0000000000..55eb74addb
--- /dev/null
+++ b/src/go/types/objset.go
@@ -0,0 +1,31 @@
+// 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 objsets.
+//
+// An objset is similar to a Scope but objset elements
+// are identified by their unique id, instead of their
+// object name.
+
+package types
+
+// An objset is a set of objects identified by their unique id.
+// The zero value for objset is a ready-to-use empty objset.
+type objset map[string]Object // initialized lazily
+
+// insert attempts to insert an object obj into objset s.
+// If s already contains an alternative object alt with
+// the same name, insert leaves s unchanged and returns alt.
+// Otherwise it inserts obj and returns nil.
+func (s *objset) insert(obj Object) Object {
+ id := obj.Id()
+ if alt := (*s)[id]; alt != nil {
+ return alt
+ }
+ if *s == nil {
+ *s = make(map[string]Object)
+ }
+ (*s)[id] = obj
+ return nil
+}
diff --git a/src/go/types/operand.go b/src/go/types/operand.go
new file mode 100644
index 0000000000..88c387058e
--- /dev/null
+++ b/src/go/types/operand.go
@@ -0,0 +1,286 @@
+// 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 defines operands and associated operations.
+
+package types
+
+import (
+ "bytes"
+ "go/ast"
+ exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+ "go/token"
+)
+
+// An operandMode specifies the (addressing) mode of an operand.
+type operandMode byte
+
+const (
+ invalid operandMode = iota // operand is invalid
+ novalue // operand represents no value (result of a function call w/o result)
+ builtin // operand is a built-in function
+ typexpr // operand is a type
+ constant // operand is a constant; the operand's typ is a Basic type
+ variable // operand is an addressable variable
+ mapindex // operand is a map index expression (acts like a variable on lhs, commaok on rhs of an assignment)
+ value // operand is a computed value
+ commaok // like value, but operand may be used in a comma,ok expression
+)
+
+var operandModeString = [...]string{
+ invalid: "invalid operand",
+ novalue: "no value",
+ builtin: "built-in",
+ typexpr: "type",
+ constant: "constant",
+ variable: "variable",
+ mapindex: "map index expression",
+ value: "value",
+ commaok: "comma, ok expression",
+}
+
+// An operand represents an intermediate value during type checking.
+// Operands have an (addressing) mode, the expression evaluating to
+// the operand, the operand's type, a value for constants, and an id
+// for built-in functions.
+// The zero value of operand is a ready to use invalid operand.
+//
+type operand struct {
+ mode operandMode
+ expr ast.Expr
+ typ Type
+ val exact.Value
+ id builtinId
+}
+
+// pos returns the position of the expression corresponding to x.
+// If x is invalid the position is token.NoPos.
+//
+func (x *operand) pos() token.Pos {
+ // x.expr may not be set if x is invalid
+ if x.expr == nil {
+ return token.NoPos
+ }
+ return x.expr.Pos()
+}
+
+// Operand string formats
+// (not all "untyped" cases can appear due to the type system,
+// but they fall out naturally here)
+//
+// mode format
+//
+// invalid ( )
+// novalue ( )
+// builtin ( )
+// typexpr ( )
+//
+// constant ( )
+// constant ( of type )
+// constant ( )
+// constant ( of type )
+//
+// variable ( )
+// variable ( of type )
+//
+// mapindex ( )
+// mapindex ( of type )
+//
+// value ( )
+// value ( of type )
+//
+// commaok ( )
+// commaok ( of type )
+//
+func operandString(this *Package, x *operand) string {
+ var buf bytes.Buffer
+
+ var expr string
+ if x.expr != nil {
+ expr = ExprString(x.expr)
+ } else {
+ switch x.mode {
+ case builtin:
+ expr = predeclaredFuncs[x.id].name
+ case typexpr:
+ expr = TypeString(this, x.typ)
+ case constant:
+ expr = x.val.String()
+ }
+ }
+
+ // (
+ if expr != "" {
+ buf.WriteString(expr)
+ buf.WriteString(" (")
+ }
+
+ //
+ hasType := false
+ switch x.mode {
+ case invalid, novalue, builtin, typexpr:
+ // no type
+ default:
+ // has type
+ if isUntyped(x.typ) {
+ buf.WriteString(x.typ.(*Basic).name)
+ buf.WriteByte(' ')
+ break
+ }
+ hasType = true
+ }
+
+ //
+ buf.WriteString(operandModeString[x.mode])
+
+ //
+ if x.mode == constant {
+ if s := x.val.String(); s != expr {
+ buf.WriteByte(' ')
+ buf.WriteString(s)
+ }
+ }
+
+ //
+ if hasType {
+ if x.typ != Typ[Invalid] {
+ buf.WriteString(" of type ")
+ WriteType(&buf, this, x.typ)
+ } else {
+ buf.WriteString(" with invalid type")
+ }
+ }
+
+ // )
+ if expr != "" {
+ buf.WriteByte(')')
+ }
+
+ return buf.String()
+}
+
+func (x *operand) String() string {
+ return operandString(nil, x)
+}
+
+// setConst sets x to the untyped constant for literal lit.
+func (x *operand) setConst(tok token.Token, lit string) {
+ val := exact.MakeFromLiteral(lit, tok, 0)
+ if val == nil {
+ // TODO(gri) Should we make it an unknown constant instead?
+ x.mode = invalid
+ return
+ }
+
+ var kind BasicKind
+ switch tok {
+ case token.INT:
+ kind = UntypedInt
+ case token.FLOAT:
+ kind = UntypedFloat
+ case token.IMAG:
+ kind = UntypedComplex
+ case token.CHAR:
+ kind = UntypedRune
+ case token.STRING:
+ kind = UntypedString
+ }
+
+ x.mode = constant
+ x.typ = Typ[kind]
+ x.val = val
+}
+
+// isNil reports whether x is the nil value.
+func (x *operand) isNil() bool {
+ return x.mode == value && x.typ == Typ[UntypedNil]
+}
+
+// TODO(gri) The functions operand.assignableTo, checker.convertUntyped,
+// checker.representable, and checker.assignment are
+// overlapping in functionality. Need to simplify and clean up.
+
+// assignableTo reports whether x is assignable to a variable of type T.
+func (x *operand) assignableTo(conf *Config, T Type) bool {
+ if x.mode == invalid || T == Typ[Invalid] {
+ return true // avoid spurious errors
+ }
+
+ V := x.typ
+
+ // x's type is identical to T
+ if Identical(V, T) {
+ return true
+ }
+
+ Vu := V.Underlying()
+ Tu := T.Underlying()
+
+ // T is an interface type and x implements T
+ // (Do this check first as it might succeed early.)
+ if Ti, ok := Tu.(*Interface); ok {
+ if Implements(x.typ, Ti) {
+ return true
+ }
+ }
+
+ // x's type V and T have identical underlying types
+ // and at least one of V or T is not a named type
+ if Identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) {
+ return true
+ }
+
+ // x is a bidirectional channel value, T is a channel
+ // type, x's type V and T have identical element types,
+ // and at least one of V or T is not a named type
+ if Vc, ok := Vu.(*Chan); ok && Vc.dir == SendRecv {
+ if Tc, ok := Tu.(*Chan); ok && Identical(Vc.elem, Tc.elem) {
+ return !isNamed(V) || !isNamed(T)
+ }
+ }
+
+ // x is the predeclared identifier nil and T is a pointer,
+ // function, slice, map, channel, or interface type
+ if x.isNil() {
+ switch t := Tu.(type) {
+ case *Basic:
+ if t.kind == UnsafePointer {
+ return true
+ }
+ case *Pointer, *Signature, *Slice, *Map, *Chan, *Interface:
+ return true
+ }
+ return false
+ }
+
+ // x is an untyped constant representable by a value of type T
+ // TODO(gri) This is borrowing from checker.convertUntyped and
+ // checker.representable. Need to clean up.
+ if isUntyped(Vu) {
+ switch t := Tu.(type) {
+ case *Basic:
+ if x.mode == constant {
+ return representableConst(x.val, conf, t.kind, nil)
+ }
+ // The result of a comparison is an untyped boolean,
+ // but may not be a constant.
+ if Vb, _ := Vu.(*Basic); Vb != nil {
+ return Vb.kind == UntypedBool && isBoolean(Tu)
+ }
+ case *Interface:
+ return x.isNil() || t.Empty()
+ case *Pointer, *Signature, *Slice, *Map, *Chan:
+ return x.isNil()
+ }
+ }
+
+ return false
+}
+
+// isInteger reports whether x is a (typed or untyped) integer value.
+func (x *operand) isInteger() bool {
+ return x.mode == invalid ||
+ isInteger(x.typ) ||
+ x.mode == constant && representableConst(x.val, nil, UntypedInt, nil) // no *Config required for UntypedInt
+}
diff --git a/src/go/types/ordering.go b/src/go/types/ordering.go
new file mode 100644
index 0000000000..6bb98f2dc1
--- /dev/null
+++ b/src/go/types/ordering.go
@@ -0,0 +1,127 @@
+// Copyright 2014 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 resolveOrder.
+
+package types
+
+import (
+ "go/ast"
+ "sort"
+)
+
+// resolveOrder computes the order in which package-level objects
+// must be type-checked.
+//
+// Interface types appear first in the list, sorted topologically
+// by dependencies on embedded interfaces that are also declared
+// in this package, followed by all other objects sorted in source
+// order.
+//
+// TODO(gri) Consider sorting all types by dependencies here, and
+// in the process check _and_ report type cycles. This may simplify
+// the full type-checking phase.
+//
+func (check *Checker) resolveOrder() []Object {
+ var ifaces, others []Object
+
+ // collect interface types with their dependencies, and all other objects
+ for obj := range check.objMap {
+ if ityp := check.interfaceFor(obj); ityp != nil {
+ ifaces = append(ifaces, obj)
+ // determine dependencies on embedded interfaces
+ for _, f := range ityp.Methods.List {
+ if len(f.Names) == 0 {
+ // Embedded interface: The type must be a (possibly
+ // qualified) identifier denoting another interface.
+ // Imported interfaces are already fully resolved,
+ // so we can ignore qualified identifiers.
+ if ident, _ := f.Type.(*ast.Ident); ident != nil {
+ embedded := check.pkg.scope.Lookup(ident.Name)
+ if check.interfaceFor(embedded) != nil {
+ check.objMap[obj].addDep(embedded)
+ }
+ }
+ }
+ }
+ } else {
+ others = append(others, obj)
+ }
+ }
+
+ // final object order
+ var order []Object
+
+ // sort interface types topologically by dependencies,
+ // and in source order if there are no dependencies
+ sort.Sort(inSourceOrder(ifaces))
+ if debug {
+ for _, obj := range ifaces {
+ assert(check.objMap[obj].mark == 0)
+ }
+ }
+ for _, obj := range ifaces {
+ check.appendInPostOrder(&order, obj)
+ }
+
+ // sort everything else in source order
+ sort.Sort(inSourceOrder(others))
+
+ return append(order, others...)
+}
+
+// interfaceFor returns the AST interface denoted by obj, or nil.
+func (check *Checker) interfaceFor(obj Object) *ast.InterfaceType {
+ tname, _ := obj.(*TypeName)
+ if tname == nil {
+ return nil // not a type
+ }
+ d := check.objMap[obj]
+ if d == nil {
+ check.dump("%s: %s should have been declared", obj.Pos(), obj.Name())
+ unreachable()
+ }
+ if d.typ == nil {
+ return nil // invalid AST - ignore (will be handled later)
+ }
+ ityp, _ := d.typ.(*ast.InterfaceType)
+ return ityp
+}
+
+func (check *Checker) appendInPostOrder(order *[]Object, obj Object) {
+ d := check.objMap[obj]
+ if d.mark != 0 {
+ // We've already seen this object; either because it's
+ // already added to order, or because we have a cycle.
+ // In both cases we stop. Cycle errors are reported
+ // when type-checking types.
+ return
+ }
+ d.mark = 1
+
+ for _, obj := range orderedSetObjects(d.deps) {
+ check.appendInPostOrder(order, obj)
+ }
+
+ *order = append(*order, obj)
+}
+
+func orderedSetObjects(set map[Object]bool) []Object {
+ list := make([]Object, len(set))
+ i := 0
+ for obj := range set {
+ // we don't care about the map element value
+ list[i] = obj
+ i++
+ }
+ sort.Sort(inSourceOrder(list))
+ return list
+}
+
+// inSourceOrder implements the sort.Sort interface.
+type inSourceOrder []Object
+
+func (a inSourceOrder) Len() int { return len(a) }
+func (a inSourceOrder) Less(i, j int) bool { return a[i].order() < a[j].order() }
+func (a inSourceOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
diff --git a/src/go/types/package.go b/src/go/types/package.go
new file mode 100644
index 0000000000..366ca3948d
--- /dev/null
+++ b/src/go/types/package.go
@@ -0,0 +1,58 @@
+// 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 types
+
+import "fmt"
+
+// A Package describes a Go package.
+type Package struct {
+ path string
+ name string
+ scope *Scope
+ complete bool
+ imports []*Package
+ fake bool // scope lookup errors are silently dropped if package is fake (internal use only)
+}
+
+// NewPackage returns a new Package for the given package path and name;
+// the name must not be the blank identifier.
+// The package is not complete and contains no explicit imports.
+func NewPackage(path, name string) *Package {
+ if name == "_" {
+ panic("invalid package name _")
+ }
+ scope := NewScope(Universe, fmt.Sprintf("package %q", path))
+ return &Package{path: path, name: name, scope: scope}
+}
+
+// Path returns the package path.
+func (pkg *Package) Path() string { return pkg.path }
+
+// Name returns the package name.
+func (pkg *Package) Name() string { return pkg.name }
+
+// Scope returns the (complete or incomplete) package scope
+// holding the objects declared at package level (TypeNames,
+// Consts, Vars, and Funcs).
+func (pkg *Package) Scope() *Scope { return pkg.scope }
+
+// A package is complete if its scope contains (at least) all
+// exported objects; otherwise it is incomplete.
+func (pkg *Package) Complete() bool { return pkg.complete }
+
+// MarkComplete marks a package as complete.
+func (pkg *Package) MarkComplete() { pkg.complete = true }
+
+// Imports returns the list of packages explicitly imported by
+// pkg; the list is in source order. Package unsafe is excluded.
+func (pkg *Package) Imports() []*Package { return pkg.imports }
+
+// SetImports sets the list of explicitly imported packages to list.
+// It is the caller's responsibility to make sure list elements are unique.
+func (pkg *Package) SetImports(list []*Package) { pkg.imports = list }
+
+func (pkg *Package) String() string {
+ return fmt.Sprintf("package %s (%q)", pkg.name, pkg.path)
+}
diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go
new file mode 100644
index 0000000000..b5c39d9d16
--- /dev/null
+++ b/src/go/types/predicates.go
@@ -0,0 +1,309 @@
+// 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 implements commonly used type predicates.
+
+package types
+
+import "sort"
+
+func isNamed(typ Type) bool {
+ if _, ok := typ.(*Basic); ok {
+ return ok
+ }
+ _, ok := typ.(*Named)
+ return ok
+}
+
+func isBoolean(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsBoolean != 0
+}
+
+func isInteger(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsInteger != 0
+}
+
+func isUnsigned(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsUnsigned != 0
+}
+
+func isFloat(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsFloat != 0
+}
+
+func isComplex(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsComplex != 0
+}
+
+func isNumeric(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsNumeric != 0
+}
+
+func isString(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsString != 0
+}
+
+func isTyped(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return !ok || t.info&IsUntyped == 0
+}
+
+func isUntyped(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsUntyped != 0
+}
+
+func isOrdered(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsOrdered != 0
+}
+
+func isConstType(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsConstType != 0
+}
+
+// IsInterface reports whether typ is an interface type.
+func IsInterface(typ Type) bool {
+ _, ok := typ.Underlying().(*Interface)
+ return ok
+}
+
+// Comparable reports whether values of type T are comparable.
+func Comparable(T Type) bool {
+ switch t := T.Underlying().(type) {
+ case *Basic:
+ // assume invalid types to be comparable
+ // to avoid follow-up errors
+ return t.kind != UntypedNil
+ case *Pointer, *Interface, *Chan:
+ return true
+ case *Struct:
+ for _, f := range t.fields {
+ if !Comparable(f.typ) {
+ return false
+ }
+ }
+ return true
+ case *Array:
+ return Comparable(t.elem)
+ }
+ return false
+}
+
+// hasNil reports whether a type includes the nil value.
+func hasNil(typ Type) bool {
+ switch t := typ.Underlying().(type) {
+ case *Basic:
+ return t.kind == UnsafePointer
+ case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
+ return true
+ }
+ return false
+}
+
+// Identical reports whether x and y are identical.
+func Identical(x, y Type) bool {
+ return identical(x, y, nil)
+}
+
+// An ifacePair is a node in a stack of interface type pairs compared for identity.
+type ifacePair struct {
+ x, y *Interface
+ prev *ifacePair
+}
+
+func (p *ifacePair) identical(q *ifacePair) bool {
+ return p.x == q.x && p.y == q.y || p.x == q.y && p.y == q.x
+}
+
+func identical(x, y Type, p *ifacePair) bool {
+ if x == y {
+ return true
+ }
+
+ switch x := x.(type) {
+ case *Basic:
+ // Basic types are singletons except for the rune and byte
+ // aliases, thus we cannot solely rely on the x == y check
+ // above.
+ if y, ok := y.(*Basic); ok {
+ return x.kind == y.kind
+ }
+
+ case *Array:
+ // Two array types are identical if they have identical element types
+ // and the same array length.
+ if y, ok := y.(*Array); ok {
+ return x.len == y.len && identical(x.elem, y.elem, p)
+ }
+
+ case *Slice:
+ // Two slice types are identical if they have identical element types.
+ if y, ok := y.(*Slice); ok {
+ return identical(x.elem, y.elem, p)
+ }
+
+ case *Struct:
+ // Two struct types are identical if they have the same sequence of fields,
+ // and if corresponding fields have the same names, and identical types,
+ // and identical tags. Two anonymous fields are considered to have the same
+ // name. Lower-case field names from different packages are always different.
+ if y, ok := y.(*Struct); ok {
+ if x.NumFields() == y.NumFields() {
+ for i, f := range x.fields {
+ g := y.fields[i]
+ if f.anonymous != g.anonymous ||
+ x.Tag(i) != y.Tag(i) ||
+ !f.sameId(g.pkg, g.name) ||
+ !identical(f.typ, g.typ, p) {
+ return false
+ }
+ }
+ return true
+ }
+ }
+
+ case *Pointer:
+ // Two pointer types are identical if they have identical base types.
+ if y, ok := y.(*Pointer); ok {
+ return identical(x.base, y.base, p)
+ }
+
+ case *Tuple:
+ // Two tuples types are identical if they have the same number of elements
+ // and corresponding elements have identical types.
+ if y, ok := y.(*Tuple); ok {
+ if x.Len() == y.Len() {
+ if x != nil {
+ for i, v := range x.vars {
+ w := y.vars[i]
+ if !identical(v.typ, w.typ, p) {
+ return false
+ }
+ }
+ }
+ return true
+ }
+ }
+
+ case *Signature:
+ // Two function types are identical if they have the same number of parameters
+ // and result values, corresponding parameter and result types are identical,
+ // and either both functions are variadic or neither is. Parameter and result
+ // names are not required to match.
+ if y, ok := y.(*Signature); ok {
+ return x.variadic == y.variadic &&
+ identical(x.params, y.params, p) &&
+ identical(x.results, y.results, p)
+ }
+
+ case *Interface:
+ // Two interface types are identical if they have the same set of methods with
+ // the same names and identical function types. Lower-case method names from
+ // different packages are always different. The order of the methods is irrelevant.
+ if y, ok := y.(*Interface); ok {
+ a := x.allMethods
+ b := y.allMethods
+ if len(a) == len(b) {
+ // Interface types are the only types where cycles can occur
+ // that are not "terminated" via named types; and such cycles
+ // can only be created via method parameter types that are
+ // anonymous interfaces (directly or indirectly) embedding
+ // the current interface. Example:
+ //
+ // type T interface {
+ // m() interface{T}
+ // }
+ //
+ // If two such (differently named) interfaces are compared,
+ // endless recursion occurs if the cycle is not detected.
+ //
+ // If x and y were compared before, they must be equal
+ // (if they were not, the recursion would have stopped);
+ // search the ifacePair stack for the same pair.
+ //
+ // This is a quadratic algorithm, but in practice these stacks
+ // are extremely short (bounded by the nesting depth of interface
+ // type declarations that recur via parameter types, an extremely
+ // rare occurrence). An alternative implementation might use a
+ // "visited" map, but that is probably less efficient overall.
+ q := &ifacePair{x, y, p}
+ for p != nil {
+ if p.identical(q) {
+ return true // same pair was compared before
+ }
+ p = p.prev
+ }
+ if debug {
+ assert(sort.IsSorted(byUniqueMethodName(a)))
+ assert(sort.IsSorted(byUniqueMethodName(b)))
+ }
+ for i, f := range a {
+ g := b[i]
+ if f.Id() != g.Id() || !identical(f.typ, g.typ, q) {
+ return false
+ }
+ }
+ return true
+ }
+ }
+
+ case *Map:
+ // Two map types are identical if they have identical key and value types.
+ if y, ok := y.(*Map); ok {
+ return identical(x.key, y.key, p) && identical(x.elem, y.elem, p)
+ }
+
+ case *Chan:
+ // Two channel types are identical if they have identical value types
+ // and the same direction.
+ if y, ok := y.(*Chan); ok {
+ return x.dir == y.dir && identical(x.elem, y.elem, p)
+ }
+
+ case *Named:
+ // Two named types are identical if their type names originate
+ // in the same type declaration.
+ if y, ok := y.(*Named); ok {
+ return x.obj == y.obj
+ }
+
+ default:
+ unreachable()
+ }
+
+ return false
+}
+
+// defaultType returns the default "typed" type for an "untyped" type;
+// it returns the incoming type for all other types. The default type
+// for untyped nil is untyped nil.
+//
+func defaultType(typ Type) Type {
+ if t, ok := typ.(*Basic); ok {
+ switch t.kind {
+ case UntypedBool:
+ return Typ[Bool]
+ case UntypedInt:
+ return Typ[Int]
+ case UntypedRune:
+ return UniverseRune // use 'rune' name
+ case UntypedFloat:
+ return Typ[Float64]
+ case UntypedComplex:
+ return Typ[Complex128]
+ case UntypedString:
+ return Typ[String]
+ }
+ }
+ return typ
+}
diff --git a/src/go/types/resolver.go b/src/go/types/resolver.go
new file mode 100644
index 0000000000..be46b59f11
--- /dev/null
+++ b/src/go/types/resolver.go
@@ -0,0 +1,438 @@
+// 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 types
+
+import (
+ "fmt"
+ "go/ast"
+ exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+ "go/token"
+ pathLib "path"
+ "strconv"
+ "strings"
+ "unicode"
+)
+
+// A declInfo describes a package-level const, type, var, or func declaration.
+type declInfo struct {
+ file *Scope // scope of file containing this declaration
+ lhs []*Var // lhs of n:1 variable declarations, or nil
+ typ ast.Expr // type, or nil
+ init ast.Expr // init expression, or nil
+ fdecl *ast.FuncDecl // func declaration, or nil
+
+ deps map[Object]bool // type and init dependencies; lazily allocated
+ mark int // for dependency analysis
+}
+
+// hasInitializer reports whether the declared object has an initialization
+// expression or function body.
+func (d *declInfo) hasInitializer() bool {
+ return d.init != nil || d.fdecl != nil && d.fdecl.Body != nil
+}
+
+// addDep adds obj as a dependency to d.
+func (d *declInfo) addDep(obj Object) {
+ m := d.deps
+ if m == nil {
+ m = make(map[Object]bool)
+ d.deps = m
+ }
+ m[obj] = true
+}
+
+// arityMatch checks that the lhs and rhs of a const or var decl
+// have the appropriate number of names and init exprs. For const
+// decls, init is the value spec providing the init exprs; for
+// var decls, init is nil (the init exprs are in s in this case).
+func (check *Checker) arityMatch(s, init *ast.ValueSpec) {
+ l := len(s.Names)
+ r := len(s.Values)
+ if init != nil {
+ r = len(init.Values)
+ }
+
+ switch {
+ case init == nil && r == 0:
+ // var decl w/o init expr
+ if s.Type == nil {
+ check.errorf(s.Pos(), "missing type or init expr")
+ }
+ case l < r:
+ if l < len(s.Values) {
+ // init exprs from s
+ n := s.Values[l]
+ check.errorf(n.Pos(), "extra init expr %s", n)
+ // TODO(gri) avoid declared but not used error here
+ } else {
+ // init exprs "inherited"
+ check.errorf(s.Pos(), "extra init expr at %s", init.Pos())
+ // TODO(gri) avoid declared but not used error here
+ }
+ case l > r && (init != nil || r != 1):
+ n := s.Names[r]
+ check.errorf(n.Pos(), "missing init expr for %s", n)
+ }
+}
+
+func validatedImportPath(path string) (string, error) {
+ s, err := strconv.Unquote(path)
+ if err != nil {
+ return "", err
+ }
+ if s == "" {
+ return "", fmt.Errorf("empty string")
+ }
+ const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
+ for _, r := range s {
+ if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
+ return s, fmt.Errorf("invalid character %#U", r)
+ }
+ }
+ return s, nil
+}
+
+// declarePkgObj declares obj in the package scope, records its ident -> obj mapping,
+// and updates check.objMap. The object must not be a function or method.
+func (check *Checker) declarePkgObj(ident *ast.Ident, obj Object, d *declInfo) {
+ assert(ident.Name == obj.Name())
+
+ // spec: "A package-scope or file-scope identifier with name init
+ // may only be declared to be a function with this (func()) signature."
+ if ident.Name == "init" {
+ check.errorf(ident.Pos(), "cannot declare init - must be func")
+ return
+ }
+
+ check.declare(check.pkg.scope, ident, obj)
+ check.objMap[obj] = d
+ obj.setOrder(uint32(len(check.objMap)))
+}
+
+// filename returns a filename suitable for debugging output.
+func (check *Checker) filename(fileNo int) string {
+ file := check.files[fileNo]
+ if pos := file.Pos(); pos.IsValid() {
+ return check.fset.File(pos).Name()
+ }
+ return fmt.Sprintf("file[%d]", fileNo)
+}
+
+// collectObjects collects all file and package objects and inserts them
+// into their respective scopes. It also performs imports and associates
+// methods with receiver base type names.
+func (check *Checker) collectObjects() {
+ pkg := check.pkg
+
+ // pkgImports is the set of packages already imported by any package file seen
+ // so far. Used to avoid duplicate entries in pkg.imports. Allocate and populate
+ // it (pkg.imports may not be empty if we are checking test files incrementally).
+ var pkgImports = make(map[*Package]bool)
+ for _, imp := range pkg.imports {
+ pkgImports[imp] = true
+ }
+
+ for fileNo, file := range check.files {
+ // The package identifier denotes the current package,
+ // but there is no corresponding package object.
+ check.recordDef(file.Name, nil)
+
+ fileScope := NewScope(check.pkg.scope, check.filename(fileNo))
+ check.recordScope(file, fileScope)
+
+ for _, decl := range file.Decls {
+ switch d := decl.(type) {
+ case *ast.BadDecl:
+ // ignore
+
+ case *ast.GenDecl:
+ var last *ast.ValueSpec // last ValueSpec with type or init exprs seen
+ for iota, spec := range d.Specs {
+ switch s := spec.(type) {
+ case *ast.ImportSpec:
+ // import package
+ var imp *Package
+ path, err := validatedImportPath(s.Path.Value)
+ if err != nil {
+ check.errorf(s.Path.Pos(), "invalid import path (%s)", err)
+ continue
+ }
+ if path == "C" && check.conf.FakeImportC {
+ // TODO(gri) shouldn't create a new one each time
+ imp = NewPackage("C", "C")
+ imp.fake = true
+ } else if path == "unsafe" {
+ // package "unsafe" is known to the language
+ imp = Unsafe
+ } else {
+ if importer := check.conf.Importer; importer != nil {
+ imp, err = importer.Import(path)
+ if imp == nil && err == nil {
+ err = fmt.Errorf("Config.Importer.Import(%s) returned nil but no error", path)
+ }
+ } else {
+ err = fmt.Errorf("Config.Importer not installed")
+ }
+ if err != nil {
+ check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err)
+ continue
+ }
+ }
+
+ // add package to list of explicit imports
+ // (this functionality is provided as a convenience
+ // for clients; it is not needed for type-checking)
+ if !pkgImports[imp] {
+ pkgImports[imp] = true
+ if imp != Unsafe {
+ pkg.imports = append(pkg.imports, imp)
+ }
+ }
+
+ // local name overrides imported package name
+ name := imp.name
+ if s.Name != nil {
+ name = s.Name.Name
+ if name == "init" {
+ check.errorf(s.Name.Pos(), "cannot declare init - must be func")
+ continue
+ }
+ }
+
+ obj := NewPkgName(s.Pos(), pkg, name, imp)
+ if s.Name != nil {
+ // in a dot-import, the dot represents the package
+ check.recordDef(s.Name, obj)
+ } else {
+ check.recordImplicit(s, obj)
+ }
+
+ // add import to file scope
+ if name == "." {
+ // merge imported scope with file scope
+ for _, obj := range imp.scope.elems {
+ // A package scope may contain non-exported objects,
+ // do not import them!
+ if obj.Exported() {
+ // TODO(gri) When we import a package, we create
+ // a new local package object. We should do the
+ // same for each dot-imported object. That way
+ // they can have correct position information.
+ // (We must not modify their existing position
+ // information because the same package - found
+ // via Config.Packages - may be dot-imported in
+ // another package!)
+ check.declare(fileScope, nil, obj)
+ check.recordImplicit(s, obj)
+ }
+ }
+ // add position to set of dot-import positions for this file
+ // (this is only needed for "imported but not used" errors)
+ check.addUnusedDotImport(fileScope, imp, s.Pos())
+ } else {
+ // declare imported package object in file scope
+ check.declare(fileScope, nil, obj)
+ }
+
+ case *ast.ValueSpec:
+ switch d.Tok {
+ case token.CONST:
+ // determine which initialization expressions to use
+ switch {
+ case s.Type != nil || len(s.Values) > 0:
+ last = s
+ case last == nil:
+ last = new(ast.ValueSpec) // make sure last exists
+ }
+
+ // declare all constants
+ for i, name := range s.Names {
+ obj := NewConst(name.Pos(), pkg, name.Name, nil, exact.MakeInt64(int64(iota)))
+
+ var init ast.Expr
+ if i < len(last.Values) {
+ init = last.Values[i]
+ }
+
+ d := &declInfo{file: fileScope, typ: last.Type, init: init}
+ check.declarePkgObj(name, obj, d)
+ }
+
+ check.arityMatch(s, last)
+
+ case token.VAR:
+ lhs := make([]*Var, len(s.Names))
+ // If there's exactly one rhs initializer, use
+ // the same declInfo d1 for all lhs variables
+ // so that each lhs variable depends on the same
+ // rhs initializer (n:1 var declaration).
+ var d1 *declInfo
+ if len(s.Values) == 1 {
+ // The lhs elements are only set up after the for loop below,
+ // but that's ok because declareVar only collects the declInfo
+ // for a later phase.
+ d1 = &declInfo{file: fileScope, lhs: lhs, typ: s.Type, init: s.Values[0]}
+ }
+
+ // declare all variables
+ for i, name := range s.Names {
+ obj := NewVar(name.Pos(), pkg, name.Name, nil)
+ lhs[i] = obj
+
+ d := d1
+ if d == nil {
+ // individual assignments
+ var init ast.Expr
+ if i < len(s.Values) {
+ init = s.Values[i]
+ }
+ d = &declInfo{file: fileScope, typ: s.Type, init: init}
+ }
+
+ check.declarePkgObj(name, obj, d)
+ }
+
+ check.arityMatch(s, nil)
+
+ default:
+ check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
+ }
+
+ case *ast.TypeSpec:
+ obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
+ check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, typ: s.Type})
+
+ default:
+ check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
+ }
+ }
+
+ case *ast.FuncDecl:
+ name := d.Name.Name
+ obj := NewFunc(d.Name.Pos(), pkg, name, nil)
+ if d.Recv == nil {
+ // regular function
+ if name == "init" {
+ // don't declare init functions in the package scope - they are invisible
+ obj.parent = pkg.scope
+ check.recordDef(d.Name, obj)
+ // init functions must have a body
+ if d.Body == nil {
+ check.softErrorf(obj.pos, "missing function body")
+ }
+ } else {
+ check.declare(pkg.scope, d.Name, obj)
+ }
+ } else {
+ // method
+ check.recordDef(d.Name, obj)
+ // Associate method with receiver base type name, if possible.
+ // Ignore methods that have an invalid receiver, or a blank _
+ // receiver name. They will be type-checked later, with regular
+ // functions.
+ if list := d.Recv.List; len(list) > 0 {
+ typ := list[0].Type
+ if ptr, _ := typ.(*ast.StarExpr); ptr != nil {
+ typ = ptr.X
+ }
+ if base, _ := typ.(*ast.Ident); base != nil && base.Name != "_" {
+ check.assocMethod(base.Name, obj)
+ }
+ }
+ }
+ info := &declInfo{file: fileScope, fdecl: d}
+ check.objMap[obj] = info
+ obj.setOrder(uint32(len(check.objMap)))
+
+ default:
+ check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
+ }
+ }
+ }
+
+ // verify that objects in package and file scopes have different names
+ for _, scope := range check.pkg.scope.children /* file scopes */ {
+ for _, obj := range scope.elems {
+ if alt := pkg.scope.Lookup(obj.Name()); alt != nil {
+ if pkg, ok := obj.(*PkgName); ok {
+ check.errorf(alt.Pos(), "%s already declared through import of %s", alt.Name(), pkg.Imported())
+ check.reportAltDecl(pkg)
+ } else {
+ check.errorf(alt.Pos(), "%s already declared through dot-import of %s", alt.Name(), obj.Pkg())
+ // TODO(gri) dot-imported objects don't have a position; reportAltDecl won't print anything
+ check.reportAltDecl(obj)
+ }
+ }
+ }
+ }
+}
+
+// packageObjects typechecks all package objects in objList, but not function bodies.
+func (check *Checker) packageObjects(objList []Object) {
+ // add new methods to already type-checked types (from a prior Checker.Files call)
+ for _, obj := range objList {
+ if obj, _ := obj.(*TypeName); obj != nil && obj.typ != nil {
+ check.addMethodDecls(obj)
+ }
+ }
+
+ // pre-allocate space for type declaration paths so that the underlying array is reused
+ typePath := make([]*TypeName, 0, 8)
+
+ for _, obj := range objList {
+ check.objDecl(obj, nil, typePath)
+ }
+
+ // At this point we may have a non-empty check.methods map; this means that not all
+ // entries were deleted at the end of typeDecl because the respective receiver base
+ // types were not found. In that case, an error was reported when declaring those
+ // methods. We can now safely discard this map.
+ check.methods = nil
+}
+
+// functionBodies typechecks all function bodies.
+func (check *Checker) functionBodies() {
+ for _, f := range check.funcs {
+ check.funcBody(f.decl, f.name, f.sig, f.body)
+ }
+}
+
+// unusedImports checks for unused imports.
+func (check *Checker) unusedImports() {
+ // if function bodies are not checked, packages' uses are likely missing - don't check
+ if check.conf.IgnoreFuncBodies {
+ return
+ }
+
+ // spec: "It is illegal (...) to directly import a package without referring to
+ // any of its exported identifiers. To import a package solely for its side-effects
+ // (initialization), use the blank identifier as explicit package name."
+
+ // check use of regular imported packages
+ for _, scope := range check.pkg.scope.children /* file scopes */ {
+ for _, obj := range scope.elems {
+ if obj, ok := obj.(*PkgName); ok {
+ // Unused "blank imports" are automatically ignored
+ // since _ identifiers are not entered into scopes.
+ if !obj.used {
+ path := obj.imported.path
+ base := pathLib.Base(path)
+ if obj.name == base {
+ check.softErrorf(obj.pos, "%q imported but not used", path)
+ } else {
+ check.softErrorf(obj.pos, "%q imported but not used as %s", path, obj.name)
+ }
+ }
+ }
+ }
+ }
+
+ // check use of dot-imported packages
+ for _, unusedDotImports := range check.unusedDotImports {
+ for pkg, pos := range unusedDotImports {
+ check.softErrorf(pos, "%q imported but not used", pkg.path)
+ }
+ }
+}
diff --git a/src/go/types/resolver_test.go b/src/go/types/resolver_test.go
new file mode 100644
index 0000000000..5713065354
--- /dev/null
+++ b/src/go/types/resolver_test.go
@@ -0,0 +1,208 @@
+// 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 types_test
+
+import (
+ "fmt"
+ "go/ast"
+ "go/importer"
+ "go/parser"
+ "go/token"
+ "sort"
+ "testing"
+
+ . "go/types"
+)
+
+var sources = []string{
+ `
+ package p
+ import "fmt"
+ import "math"
+ const pi = math.Pi
+ func sin(x float64) float64 {
+ return math.Sin(x)
+ }
+ var Println = fmt.Println
+ `,
+ `
+ package p
+ import "fmt"
+ type errorStringer struct { fmt.Stringer; error }
+ func f() string {
+ _ = "foo"
+ return fmt.Sprintf("%d", g())
+ }
+ func g() (x int) { return }
+ `,
+ `
+ package p
+ import . "go/parser"
+ import "sync"
+ func h() Mode { return ImportsOnly }
+ var _, x int = 1, 2
+ func init() {}
+ type T struct{ *sync.Mutex; a, b, c int}
+ type I interface{ m() }
+ var _ = T{a: 1, b: 2, c: 3}
+ func (_ T) m() {}
+ func (T) _() {}
+ var i I
+ var _ = i.m
+ func _(s []int) { for i, x := range s { _, _ = i, x } }
+ func _(x interface{}) {
+ switch x := x.(type) {
+ case int:
+ _ = x
+ }
+ switch {} // implicit 'true' tag
+ }
+ `,
+ `
+ package p
+ type S struct{}
+ func (T) _() {}
+ func (T) _() {}
+ `,
+ `
+ package p
+ func _() {
+ L0:
+ L1:
+ goto L0
+ for {
+ goto L1
+ }
+ if true {
+ goto L2
+ }
+ L2:
+ }
+ `,
+}
+
+var pkgnames = []string{
+ "fmt",
+ "math",
+}
+
+type resolveTestImporter struct {
+ importer Importer
+ imported map[string]bool
+}
+
+func (imp *resolveTestImporter) Import(path string) (*Package, error) {
+ if imp.importer == nil {
+ imp.importer = importer.Default()
+ imp.imported = make(map[string]bool)
+ }
+ pkg, err := imp.importer.Import(path)
+ if err != nil {
+ return nil, err
+ }
+ imp.imported[path] = true
+ return pkg, nil
+}
+
+func TestResolveIdents(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // parse package files
+ fset := token.NewFileSet()
+ var files []*ast.File
+ for i, src := range sources {
+ f, err := parser.ParseFile(fset, fmt.Sprintf("sources[%d]", i), src, parser.DeclarationErrors)
+ if err != nil {
+ t.Fatal(err)
+ }
+ files = append(files, f)
+ }
+
+ // resolve and type-check package AST
+ importer := new(resolveTestImporter)
+ conf := Config{Importer: importer}
+ uses := make(map[*ast.Ident]Object)
+ defs := make(map[*ast.Ident]Object)
+ _, err := conf.Check("testResolveIdents", fset, files, &Info{Defs: defs, Uses: uses})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // check that all packages were imported
+ for _, name := range pkgnames {
+ if !importer.imported[name] {
+ t.Errorf("package %s not imported", name)
+ }
+ }
+
+ // check that qualified identifiers are resolved
+ for _, f := range files {
+ ast.Inspect(f, func(n ast.Node) bool {
+ if s, ok := n.(*ast.SelectorExpr); ok {
+ if x, ok := s.X.(*ast.Ident); ok {
+ obj := uses[x]
+ if obj == nil {
+ t.Errorf("%s: unresolved qualified identifier %s", fset.Position(x.Pos()), x.Name)
+ return false
+ }
+ if _, ok := obj.(*PkgName); ok && uses[s.Sel] == nil {
+ t.Errorf("%s: unresolved selector %s", fset.Position(s.Sel.Pos()), s.Sel.Name)
+ return false
+ }
+ return false
+ }
+ return false
+ }
+ return true
+ })
+ }
+
+ for id, obj := range uses {
+ if obj == nil {
+ t.Errorf("%s: Uses[%s] == nil", fset.Position(id.Pos()), id.Name)
+ }
+ }
+
+ // check that each identifier in the source is found in uses or defs or both
+ var both []string
+ for _, f := range files {
+ ast.Inspect(f, func(n ast.Node) bool {
+ if x, ok := n.(*ast.Ident); ok {
+ var objects int
+ if _, found := uses[x]; found {
+ objects |= 1
+ delete(uses, x)
+ }
+ if _, found := defs[x]; found {
+ objects |= 2
+ delete(defs, x)
+ }
+ if objects == 0 {
+ t.Errorf("%s: unresolved identifier %s", fset.Position(x.Pos()), x.Name)
+ } else if objects == 3 {
+ both = append(both, x.Name)
+ }
+ return false
+ }
+ return true
+ })
+ }
+
+ // check the expected set of idents that are simultaneously uses and defs
+ sort.Strings(both)
+ if got, want := fmt.Sprint(both), "[Mutex Stringer error]"; got != want {
+ t.Errorf("simultaneous uses/defs = %s, want %s", got, want)
+ }
+
+ // any left-over identifiers didn't exist in the source
+ for x := range uses {
+ t.Errorf("%s: identifier %s not present in source", fset.Position(x.Pos()), x.Name)
+ }
+ for x := range defs {
+ t.Errorf("%s: identifier %s not present in source", fset.Position(x.Pos()), x.Name)
+ }
+
+ // TODO(gri) add tests to check ImplicitObj callbacks
+}
diff --git a/src/go/types/return.go b/src/go/types/return.go
new file mode 100644
index 0000000000..df5a482ad4
--- /dev/null
+++ b/src/go/types/return.go
@@ -0,0 +1,185 @@
+// 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 isTerminating.
+
+package types
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+// isTerminating reports if s is a terminating statement.
+// If s is labeled, label is the label name; otherwise s
+// is "".
+func (check *Checker) isTerminating(s ast.Stmt, label string) bool {
+ switch s := s.(type) {
+ default:
+ unreachable()
+
+ case *ast.BadStmt, *ast.DeclStmt, *ast.EmptyStmt, *ast.SendStmt,
+ *ast.IncDecStmt, *ast.AssignStmt, *ast.GoStmt, *ast.DeferStmt,
+ *ast.RangeStmt:
+ // no chance
+
+ case *ast.LabeledStmt:
+ return check.isTerminating(s.Stmt, s.Label.Name)
+
+ case *ast.ExprStmt:
+ // the predeclared (possibly parenthesized) panic() function is terminating
+ if call, _ := unparen(s.X).(*ast.CallExpr); call != nil {
+ if id, _ := call.Fun.(*ast.Ident); id != nil {
+ if _, obj := check.scope.LookupParent(id.Name); obj != nil {
+ if b, _ := obj.(*Builtin); b != nil && b.id == _Panic {
+ return true
+ }
+ }
+ }
+ }
+
+ case *ast.ReturnStmt:
+ return true
+
+ case *ast.BranchStmt:
+ if s.Tok == token.GOTO || s.Tok == token.FALLTHROUGH {
+ return true
+ }
+
+ case *ast.BlockStmt:
+ return check.isTerminatingList(s.List, "")
+
+ case *ast.IfStmt:
+ if s.Else != nil &&
+ check.isTerminating(s.Body, "") &&
+ check.isTerminating(s.Else, "") {
+ return true
+ }
+
+ case *ast.SwitchStmt:
+ return check.isTerminatingSwitch(s.Body, label)
+
+ case *ast.TypeSwitchStmt:
+ return check.isTerminatingSwitch(s.Body, label)
+
+ case *ast.SelectStmt:
+ for _, s := range s.Body.List {
+ cc := s.(*ast.CommClause)
+ if !check.isTerminatingList(cc.Body, "") || hasBreakList(cc.Body, label, true) {
+ return false
+ }
+
+ }
+ return true
+
+ case *ast.ForStmt:
+ if s.Cond == nil && !hasBreak(s.Body, label, true) {
+ return true
+ }
+ }
+
+ return false
+}
+
+func (check *Checker) isTerminatingList(list []ast.Stmt, label string) bool {
+ n := len(list)
+ return n > 0 && check.isTerminating(list[n-1], label)
+}
+
+func (check *Checker) isTerminatingSwitch(body *ast.BlockStmt, label string) bool {
+ hasDefault := false
+ for _, s := range body.List {
+ cc := s.(*ast.CaseClause)
+ if cc.List == nil {
+ hasDefault = true
+ }
+ if !check.isTerminatingList(cc.Body, "") || hasBreakList(cc.Body, label, true) {
+ return false
+ }
+ }
+ return hasDefault
+}
+
+// TODO(gri) For nested breakable statements, the current implementation of hasBreak
+// will traverse the same subtree repeatedly, once for each label. Replace
+// with a single-pass label/break matching phase.
+
+// hasBreak reports if s is or contains a break statement
+// referring to the label-ed statement or implicit-ly the
+// closest outer breakable statement.
+func hasBreak(s ast.Stmt, label string, implicit bool) bool {
+ switch s := s.(type) {
+ default:
+ unreachable()
+
+ case *ast.BadStmt, *ast.DeclStmt, *ast.EmptyStmt, *ast.ExprStmt,
+ *ast.SendStmt, *ast.IncDecStmt, *ast.AssignStmt, *ast.GoStmt,
+ *ast.DeferStmt, *ast.ReturnStmt:
+ // no chance
+
+ case *ast.LabeledStmt:
+ return hasBreak(s.Stmt, label, implicit)
+
+ case *ast.BranchStmt:
+ if s.Tok == token.BREAK {
+ if s.Label == nil {
+ return implicit
+ }
+ if s.Label.Name == label {
+ return true
+ }
+ }
+
+ case *ast.BlockStmt:
+ return hasBreakList(s.List, label, implicit)
+
+ case *ast.IfStmt:
+ if hasBreak(s.Body, label, implicit) ||
+ s.Else != nil && hasBreak(s.Else, label, implicit) {
+ return true
+ }
+
+ case *ast.CaseClause:
+ return hasBreakList(s.Body, label, implicit)
+
+ case *ast.SwitchStmt:
+ if label != "" && hasBreak(s.Body, label, false) {
+ return true
+ }
+
+ case *ast.TypeSwitchStmt:
+ if label != "" && hasBreak(s.Body, label, false) {
+ return true
+ }
+
+ case *ast.CommClause:
+ return hasBreakList(s.Body, label, implicit)
+
+ case *ast.SelectStmt:
+ if label != "" && hasBreak(s.Body, label, false) {
+ return true
+ }
+
+ case *ast.ForStmt:
+ if label != "" && hasBreak(s.Body, label, false) {
+ return true
+ }
+
+ case *ast.RangeStmt:
+ if label != "" && hasBreak(s.Body, label, false) {
+ return true
+ }
+ }
+
+ return false
+}
+
+func hasBreakList(list []ast.Stmt, label string, implicit bool) bool {
+ for _, s := range list {
+ if hasBreak(s, label, implicit) {
+ return true
+ }
+ }
+ return false
+}
diff --git a/src/go/types/scope.go b/src/go/types/scope.go
new file mode 100644
index 0000000000..8ab0f64feb
--- /dev/null
+++ b/src/go/types/scope.go
@@ -0,0 +1,145 @@
+// 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 Scopes.
+
+package types
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "sort"
+ "strings"
+)
+
+// TODO(gri) Provide scopes with a name or other mechanism so that
+// objects can use that information for better printing.
+
+// A Scope maintains a set of objects and links to its containing
+// (parent) and contained (children) scopes. Objects may be inserted
+// and looked up by name. The zero value for Scope is a ready-to-use
+// empty scope.
+type Scope struct {
+ parent *Scope
+ children []*Scope
+ comment string // for debugging only
+ elems map[string]Object // lazily allocated
+}
+
+// NewScope returns a new, empty scope contained in the given parent
+// scope, if any. The comment is for debugging only.
+func NewScope(parent *Scope, comment string) *Scope {
+ s := &Scope{parent: parent, comment: comment}
+ // don't add children to Universe scope!
+ if parent != nil && parent != Universe {
+ parent.children = append(parent.children, s)
+ }
+ return s
+}
+
+// Parent returns the scope's containing (parent) scope.
+func (s *Scope) Parent() *Scope { return s.parent }
+
+// Len() returns the number of scope elements.
+func (s *Scope) Len() int { return len(s.elems) }
+
+// Names returns the scope's element names in sorted order.
+func (s *Scope) Names() []string {
+ names := make([]string, len(s.elems))
+ i := 0
+ for name := range s.elems {
+ names[i] = name
+ i++
+ }
+ sort.Strings(names)
+ return names
+}
+
+// NumChildren() returns the number of scopes nested in s.
+func (s *Scope) NumChildren() int { return len(s.children) }
+
+// Child returns the i'th child scope for 0 <= i < NumChildren().
+func (s *Scope) Child(i int) *Scope { return s.children[i] }
+
+// Lookup returns the object in scope s with the given name if such an
+// object exists; otherwise the result is nil.
+func (s *Scope) Lookup(name string) Object {
+ return s.elems[name]
+}
+
+// LookupParent follows the parent chain of scopes starting with s until
+// it finds a scope where Lookup(name) returns a non-nil object, and then
+// returns that scope and object. If no such scope exists, the result is (nil, nil).
+//
+// Note that obj.Parent() may be different from the returned scope if the
+// object was inserted into the scope and already had a parent at that
+// time (see Insert, below). This can only happen for dot-imported objects
+// whose scope is the scope of the package that exported them.
+func (s *Scope) LookupParent(name string) (*Scope, Object) {
+ for ; s != nil; s = s.parent {
+ if obj := s.elems[name]; obj != nil {
+ return s, obj
+ }
+ }
+ return nil, nil
+}
+
+// Insert attempts to insert an object obj into scope s.
+// If s already contains an alternative object alt with
+// the same name, Insert leaves s unchanged and returns alt.
+// Otherwise it inserts obj, sets the object's parent scope
+// if not already set, and returns nil.
+func (s *Scope) Insert(obj Object) Object {
+ name := obj.Name()
+ if alt := s.elems[name]; alt != nil {
+ return alt
+ }
+ if s.elems == nil {
+ s.elems = make(map[string]Object)
+ }
+ s.elems[name] = obj
+ if obj.Parent() == nil {
+ obj.setParent(s)
+ }
+ return nil
+}
+
+// WriteTo writes a string representation of the scope to w,
+// with the scope elements sorted by name.
+// The level of indentation is controlled by n >= 0, with
+// n == 0 for no indentation.
+// If recurse is set, it also writes nested (children) scopes.
+func (s *Scope) WriteTo(w io.Writer, n int, recurse bool) {
+ const ind = ". "
+ indn := strings.Repeat(ind, n)
+
+ fmt.Fprintf(w, "%s%s scope %p {", indn, s.comment, s)
+ if len(s.elems) == 0 {
+ fmt.Fprintf(w, "}\n")
+ return
+ }
+
+ fmt.Fprintln(w)
+ indn1 := indn + ind
+ for _, name := range s.Names() {
+ fmt.Fprintf(w, "%s%s\n", indn1, s.elems[name])
+ }
+
+ if recurse {
+ for _, s := range s.children {
+ fmt.Fprintln(w)
+ s.WriteTo(w, n+1, recurse)
+ }
+ }
+
+ fmt.Fprintf(w, "%s}", indn)
+}
+
+// String returns a string representation of the scope, for debugging.
+func (s *Scope) String() string {
+ var buf bytes.Buffer
+ s.WriteTo(&buf, 0, false)
+ return buf.String()
+}
diff --git a/src/go/types/selection.go b/src/go/types/selection.go
new file mode 100644
index 0000000000..1c7016550a
--- /dev/null
+++ b/src/go/types/selection.go
@@ -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.
+
+// This file implements Selections.
+
+package types
+
+import (
+ "bytes"
+ "fmt"
+)
+
+// SelectionKind describes the kind of a selector expression x.f
+// (excluding qualified identifiers).
+type SelectionKind int
+
+const (
+ FieldVal SelectionKind = iota // x.f is a struct field selector
+ MethodVal // x.f is a method selector
+ MethodExpr // x.f is a method expression
+)
+
+// A Selection describes a selector expression x.f.
+// For the declarations:
+//
+// type T struct{ x int; E }
+// type E struct{}
+// func (e E) m() {}
+// var p *T
+//
+// the following relations exist:
+//
+// Selector Kind Recv Obj Type Index Indirect
+//
+// p.x FieldVal T x int {0} true
+// p.m MethodVal *T m func (e *T) m() {1, 0} true
+// T.m MethodExpr T m func m(_ T) {1, 0} false
+//
+type Selection struct {
+ kind SelectionKind
+ recv Type // type of x
+ obj Object // object denoted by x.f
+ index []int // path from x to x.f
+ indirect bool // set if there was any pointer indirection on the path
+}
+
+// Kind returns the selection kind.
+func (s *Selection) Kind() SelectionKind { return s.kind }
+
+// Recv returns the type of x in x.f.
+func (s *Selection) Recv() Type { return s.recv }
+
+// Obj returns the object denoted by x.f; a *Var for
+// a field selection, and a *Func in all other cases.
+func (s *Selection) Obj() Object { return s.obj }
+
+// Type returns the type of x.f, which may be different from the type of f.
+// See Selection for more information.
+func (s *Selection) Type() Type {
+ switch s.kind {
+ case MethodVal:
+ // The type of x.f is a method with its receiver type set
+ // to the type of x.
+ sig := *s.obj.(*Func).typ.(*Signature)
+ recv := *sig.recv
+ recv.typ = s.recv
+ sig.recv = &recv
+ return &sig
+
+ case MethodExpr:
+ // The type of x.f is a function (without receiver)
+ // and an additional first argument with the same type as x.
+ // TODO(gri) Similar code is already in call.go - factor!
+ // TODO(gri) Compute this eagerly to avoid allocations.
+ sig := *s.obj.(*Func).typ.(*Signature)
+ arg0 := *sig.recv
+ sig.recv = nil
+ arg0.typ = s.recv
+ var params []*Var
+ if sig.params != nil {
+ params = sig.params.vars
+ }
+ sig.params = NewTuple(append([]*Var{&arg0}, params...)...)
+ return &sig
+ }
+
+ // In all other cases, the type of x.f is the type of x.
+ return s.obj.Type()
+}
+
+// Index describes the path from x to f in x.f.
+// The last index entry is the field or method index of the type declaring f;
+// either:
+//
+// 1) the list of declared methods of a named type; or
+// 2) the list of methods of an interface type; or
+// 3) the list of fields of a struct type.
+//
+// The earlier index entries are the indices of the embedded fields implicitly
+// traversed to get from (the type of) x to f, starting at embedding depth 0.
+func (s *Selection) Index() []int { return s.index }
+
+// Indirect reports whether any pointer indirection was required to get from
+// x to f in x.f.
+func (s *Selection) Indirect() bool { return s.indirect }
+
+func (s *Selection) String() string { return SelectionString(nil, s) }
+
+// SelectionString returns the string form of s.
+// Type names are printed package-qualified
+// only if they do not belong to this package.
+//
+// Examples:
+// "field (T) f int"
+// "method (T) f(X) Y"
+// "method expr (T) f(X) Y"
+//
+func SelectionString(this *Package, s *Selection) string {
+ var k string
+ switch s.kind {
+ case FieldVal:
+ k = "field "
+ case MethodVal:
+ k = "method "
+ case MethodExpr:
+ k = "method expr "
+ default:
+ unreachable()
+ }
+ var buf bytes.Buffer
+ buf.WriteString(k)
+ buf.WriteByte('(')
+ WriteType(&buf, this, s.Recv())
+ fmt.Fprintf(&buf, ") %s", s.obj.Name())
+ if T := s.Type(); s.kind == FieldVal {
+ buf.WriteByte(' ')
+ WriteType(&buf, this, T)
+ } else {
+ WriteSignature(&buf, this, T.(*Signature))
+ }
+ return buf.String()
+}
diff --git a/src/go/types/self_test.go b/src/go/types/self_test.go
new file mode 100644
index 0000000000..85dc6ae0ec
--- /dev/null
+++ b/src/go/types/self_test.go
@@ -0,0 +1,103 @@
+// 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 types_test
+
+import (
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/importer"
+ "go/parser"
+ "go/token"
+ "path/filepath"
+ "testing"
+ "time"
+
+ _ "go/internal/gcimporter"
+ . "go/types"
+)
+
+var benchmark = flag.Bool("b", false, "run benchmarks")
+
+func TestSelf(t *testing.T) {
+ fset := token.NewFileSet()
+ files, err := pkgFiles(fset, ".")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ conf := Config{Importer: importer.Default()}
+ _, err = conf.Check("go/types", fset, files, nil)
+ if err != nil {
+ // Importing go/constants doesn't work in the
+ // build dashboard environment. Don't report an error
+ // for now so that the build remains green.
+ // TODO(gri) fix this
+ t.Log(err) // replace w/ t.Fatal eventually
+ return
+ }
+}
+
+func TestBenchmark(t *testing.T) {
+ if !*benchmark {
+ return
+ }
+
+ // We're not using testing's benchmarking mechanism directly
+ // because we want custom output.
+
+ for _, p := range []string{"types", "exact", "gcimporter"} {
+ path := filepath.Join("..", p)
+ runbench(t, path, false)
+ runbench(t, path, true)
+ fmt.Println()
+ }
+}
+
+func runbench(t *testing.T, path string, ignoreFuncBodies bool) {
+ fset := token.NewFileSet()
+ files, err := pkgFiles(fset, path)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ b := testing.Benchmark(func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ conf := Config{IgnoreFuncBodies: ignoreFuncBodies}
+ conf.Check(path, fset, files, nil)
+ }
+ })
+
+ // determine line count
+ lines := 0
+ fset.Iterate(func(f *token.File) bool {
+ lines += f.LineCount()
+ return true
+ })
+
+ d := time.Duration(b.NsPerOp())
+ fmt.Printf(
+ "%s: %s for %d lines (%d lines/s), ignoreFuncBodies = %v\n",
+ filepath.Base(path), d, lines, int64(float64(lines)/d.Seconds()), ignoreFuncBodies,
+ )
+}
+
+func pkgFiles(fset *token.FileSet, path string) ([]*ast.File, error) {
+ filenames, err := pkgFilenames(path) // from stdlib_test.go
+ if err != nil {
+ return nil, err
+ }
+
+ var files []*ast.File
+ for _, filename := range filenames {
+ file, err := parser.ParseFile(fset, filename, nil, 0)
+ if err != nil {
+ return nil, err
+ }
+ files = append(files, file)
+ }
+
+ return files, nil
+}
diff --git a/src/go/types/sizes.go b/src/go/types/sizes.go
new file mode 100644
index 0000000000..56fb310c29
--- /dev/null
+++ b/src/go/types/sizes.go
@@ -0,0 +1,211 @@
+// 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 Sizes.
+
+package types
+
+// Sizes defines the sizing functions for package unsafe.
+type Sizes interface {
+ // Alignof returns the alignment of a variable of type T.
+ // Alignof must implement the alignment guarantees required by the spec.
+ Alignof(T Type) int64
+
+ // Offsetsof returns the offsets of the given struct fields, in bytes.
+ // Offsetsof must implement the offset guarantees required by the spec.
+ Offsetsof(fields []*Var) []int64
+
+ // Sizeof returns the size of a variable of type T.
+ // Sizeof must implement the size guarantees required by the spec.
+ Sizeof(T Type) int64
+}
+
+// StdSizes is a convenience type for creating commonly used Sizes.
+// It makes the following simplifying assumptions:
+//
+// - The size of explicitly sized basic types (int16, etc.) is the
+// specified size.
+// - The size of strings and interfaces is 2*WordSize.
+// - The size of slices is 3*WordSize.
+// - The size of an array of n elements corresponds to the size of
+// a struct of n consecutive fields of the array's element type.
+// - The size of a struct is the offset of the last field plus that
+// field's size. As with all element types, if the struct is used
+// in an array its size must first be aligned to a multiple of the
+// struct's alignment.
+// - All other types have size WordSize.
+// - Arrays and structs are aligned per spec definition; all other
+// types are naturally aligned with a maximum alignment MaxAlign.
+//
+// *StdSizes implements Sizes.
+//
+type StdSizes struct {
+ WordSize int64 // word size in bytes - must be >= 4 (32bits)
+ MaxAlign int64 // maximum alignment in bytes - must be >= 1
+}
+
+func (s *StdSizes) Alignof(T Type) int64 {
+ // For arrays and structs, alignment is defined in terms
+ // of alignment of the elements and fields, respectively.
+ switch t := T.Underlying().(type) {
+ case *Array:
+ // spec: "For a variable x of array type: unsafe.Alignof(x)
+ // is the same as unsafe.Alignof(x[0]), but at least 1."
+ return s.Alignof(t.elem)
+ case *Struct:
+ // spec: "For a variable x of struct type: unsafe.Alignof(x)
+ // is the largest of the values unsafe.Alignof(x.f) for each
+ // field f of x, but at least 1."
+ max := int64(1)
+ for _, f := range t.fields {
+ if a := s.Alignof(f.typ); a > max {
+ max = a
+ }
+ }
+ return max
+ }
+ a := s.Sizeof(T) // may be 0
+ // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
+ if a < 1 {
+ return 1
+ }
+ if a > s.MaxAlign {
+ return s.MaxAlign
+ }
+ return a
+}
+
+func (s *StdSizes) Offsetsof(fields []*Var) []int64 {
+ offsets := make([]int64, len(fields))
+ var o int64
+ for i, f := range fields {
+ a := s.Alignof(f.typ)
+ o = align(o, a)
+ offsets[i] = o
+ o += s.Sizeof(f.typ)
+ }
+ return offsets
+}
+
+var basicSizes = [...]byte{
+ Bool: 1,
+ Int8: 1,
+ Int16: 2,
+ Int32: 4,
+ Int64: 8,
+ Uint8: 1,
+ Uint16: 2,
+ Uint32: 4,
+ Uint64: 8,
+ Float32: 4,
+ Float64: 8,
+ Complex64: 8,
+ Complex128: 16,
+}
+
+func (s *StdSizes) Sizeof(T Type) int64 {
+ switch t := T.Underlying().(type) {
+ case *Basic:
+ assert(isTyped(T))
+ k := t.kind
+ if int(k) < len(basicSizes) {
+ if s := basicSizes[k]; s > 0 {
+ return int64(s)
+ }
+ }
+ if k == String {
+ return s.WordSize * 2
+ }
+ case *Array:
+ n := t.len
+ if n == 0 {
+ return 0
+ }
+ a := s.Alignof(t.elem)
+ z := s.Sizeof(t.elem)
+ return align(z, a)*(n-1) + z
+ case *Slice:
+ return s.WordSize * 3
+ case *Struct:
+ n := t.NumFields()
+ if n == 0 {
+ return 0
+ }
+ offsets := t.offsets
+ if t.offsets == nil {
+ // compute offsets on demand
+ offsets = s.Offsetsof(t.fields)
+ t.offsets = offsets
+ }
+ return offsets[n-1] + s.Sizeof(t.fields[n-1].typ)
+ case *Interface:
+ return s.WordSize * 2
+ }
+ return s.WordSize // catch-all
+}
+
+// stdSizes is used if Config.Sizes == nil.
+var stdSizes = StdSizes{8, 8}
+
+func (conf *Config) alignof(T Type) int64 {
+ if s := conf.Sizes; s != nil {
+ if a := s.Alignof(T); a >= 1 {
+ return a
+ }
+ panic("Config.Sizes.Alignof returned an alignment < 1")
+ }
+ return stdSizes.Alignof(T)
+}
+
+func (conf *Config) offsetsof(T *Struct) []int64 {
+ offsets := T.offsets
+ if offsets == nil && T.NumFields() > 0 {
+ // compute offsets on demand
+ if s := conf.Sizes; s != nil {
+ offsets = s.Offsetsof(T.fields)
+ // sanity checks
+ if len(offsets) != T.NumFields() {
+ panic("Config.Sizes.Offsetsof returned the wrong number of offsets")
+ }
+ for _, o := range offsets {
+ if o < 0 {
+ panic("Config.Sizes.Offsetsof returned an offset < 0")
+ }
+ }
+ } else {
+ offsets = stdSizes.Offsetsof(T.fields)
+ }
+ T.offsets = offsets
+ }
+ return offsets
+}
+
+// offsetof returns the offset of the field specified via
+// the index sequence relative to typ. All embedded fields
+// must be structs (rather than pointer to structs).
+func (conf *Config) offsetof(typ Type, index []int) int64 {
+ var o int64
+ for _, i := range index {
+ s := typ.Underlying().(*Struct)
+ o += conf.offsetsof(s)[i]
+ typ = s.fields[i].typ
+ }
+ return o
+}
+
+func (conf *Config) sizeof(T Type) int64 {
+ if s := conf.Sizes; s != nil {
+ if z := s.Sizeof(T); z >= 0 {
+ return z
+ }
+ panic("Config.Sizes.Sizeof returned a size < 0")
+ }
+ return stdSizes.Sizeof(T)
+}
+
+// align returns the smallest y >= x such that y % a == 0.
+func align(x, a int64) int64 {
+ y := x + a - 1
+ return y - y%a
+}
diff --git a/src/go/types/stdlib_test.go b/src/go/types/stdlib_test.go
new file mode 100644
index 0000000000..d04dd71e4f
--- /dev/null
+++ b/src/go/types/stdlib_test.go
@@ -0,0 +1,275 @@
+// 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 tests types.Check by using it to
+// typecheck the standard library and tests.
+
+package types_test
+
+import (
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/importer"
+ "go/parser"
+ "go/scanner"
+ "go/token"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+
+ . "go/types"
+)
+
+var (
+ pkgCount int // number of packages processed
+ start time.Time
+
+ // Use the same importer for all std lib tests to
+ // avoid repeated importing of the same packages.
+ stdLibImporter = importer.Default()
+)
+
+func TestStdlib(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ start = time.Now()
+ walkDirs(t, filepath.Join(runtime.GOROOT(), "src"))
+ if testing.Verbose() {
+ fmt.Println(pkgCount, "packages typechecked in", time.Since(start))
+ }
+}
+
+// firstComment returns the contents of the first comment in
+// the given file, assuming there's one within the first KB.
+func firstComment(filename string) string {
+ f, err := os.Open(filename)
+ if err != nil {
+ return ""
+ }
+ defer f.Close()
+
+ var src [1 << 10]byte // read at most 1KB
+ n, _ := f.Read(src[:])
+
+ var s scanner.Scanner
+ s.Init(fset.AddFile("", fset.Base(), n), src[:n], nil, scanner.ScanComments)
+ for {
+ _, tok, lit := s.Scan()
+ switch tok {
+ case token.COMMENT:
+ // remove trailing */ of multi-line comment
+ if lit[1] == '*' {
+ lit = lit[:len(lit)-2]
+ }
+ return strings.TrimSpace(lit[2:])
+ case token.EOF:
+ return ""
+ }
+ }
+}
+
+func testTestDir(t *testing.T, path string, ignore ...string) {
+ files, err := ioutil.ReadDir(path)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ excluded := make(map[string]bool)
+ for _, filename := range ignore {
+ excluded[filename] = true
+ }
+
+ fset := token.NewFileSet()
+ for _, f := range files {
+ // filter directory contents
+ if f.IsDir() || !strings.HasSuffix(f.Name(), ".go") || excluded[f.Name()] {
+ continue
+ }
+
+ // get per-file instructions
+ expectErrors := false
+ filename := filepath.Join(path, f.Name())
+ if cmd := firstComment(filename); cmd != "" {
+ switch cmd {
+ case "skip", "compiledir":
+ continue // ignore this file
+ case "errorcheck":
+ expectErrors = true
+ }
+ }
+
+ // parse and type-check file
+ file, err := parser.ParseFile(fset, filename, nil, 0)
+ if err == nil {
+ conf := Config{Importer: stdLibImporter}
+ _, err = conf.Check(filename, fset, []*ast.File{file}, nil)
+ }
+
+ if expectErrors {
+ if err == nil {
+ t.Errorf("expected errors but found none in %s", filename)
+ }
+ } else {
+ if err != nil {
+ t.Error(err)
+ }
+ }
+ }
+}
+
+func TestStdTest(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // test/recover4.go is only built for Linux and Darwin.
+ // TODO(gri) Remove once tests consider +build tags (issue 10370).
+ if runtime.GOOS != "linux" || runtime.GOOS != "darwin" {
+ return
+ }
+
+ testTestDir(t, filepath.Join(runtime.GOROOT(), "test"),
+ "cmplxdivide.go", // also needs file cmplxdivide1.go - ignore
+ "sigchld.go", // don't work on Windows; testTestDir should consult build tags
+ )
+}
+
+func TestStdFixed(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "fixedbugs"),
+ "bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore
+ "bug459.go", // possibly incorrect test - see issue 6703 (pending spec clarification)
+ "issue3924.go", // possibly incorrect test - see issue 6671 (pending spec clarification)
+ "issue6889.go", // gc-specific test
+ )
+}
+
+func TestStdKen(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "ken"))
+}
+
+// Package paths of excluded packages.
+var excluded = map[string]bool{
+ "builtin": true,
+}
+
+// typecheck typechecks the given package files.
+func typecheck(t *testing.T, path string, filenames []string) {
+ fset := token.NewFileSet()
+
+ // parse package files
+ var files []*ast.File
+ for _, filename := range filenames {
+ file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors)
+ if err != nil {
+ // the parser error may be a list of individual errors; report them all
+ if list, ok := err.(scanner.ErrorList); ok {
+ for _, err := range list {
+ t.Error(err)
+ }
+ return
+ }
+ t.Error(err)
+ return
+ }
+
+ if testing.Verbose() {
+ if len(files) == 0 {
+ fmt.Println("package", file.Name.Name)
+ }
+ fmt.Println("\t", filename)
+ }
+
+ files = append(files, file)
+ }
+
+ // typecheck package files
+ conf := Config{
+ Error: func(err error) { t.Error(err) },
+ Importer: stdLibImporter,
+ }
+ info := Info{Uses: make(map[*ast.Ident]Object)}
+ conf.Check(path, fset, files, &info)
+ pkgCount++
+
+ // Perform checks of API invariants.
+
+ // All Objects have a package, except predeclared ones.
+ errorError := Universe.Lookup("error").Type().Underlying().(*Interface).ExplicitMethod(0) // (error).Error
+ for id, obj := range info.Uses {
+ predeclared := obj == Universe.Lookup(obj.Name()) || obj == errorError
+ if predeclared == (obj.Pkg() != nil) {
+ posn := fset.Position(id.Pos())
+ if predeclared {
+ t.Errorf("%s: predeclared object with package: %s", posn, obj)
+ } else {
+ t.Errorf("%s: user-defined object without package: %s", posn, obj)
+ }
+ }
+ }
+}
+
+// pkgFilenames returns the list of package filenames for the given directory.
+func pkgFilenames(dir string) ([]string, error) {
+ ctxt := build.Default
+ ctxt.CgoEnabled = false
+ pkg, err := ctxt.ImportDir(dir, 0)
+ if err != nil {
+ if _, nogo := err.(*build.NoGoError); nogo {
+ return nil, nil // no *.go files, not an error
+ }
+ return nil, err
+ }
+ if excluded[pkg.ImportPath] {
+ return nil, nil
+ }
+ var filenames []string
+ for _, name := range pkg.GoFiles {
+ filenames = append(filenames, filepath.Join(pkg.Dir, name))
+ }
+ for _, name := range pkg.TestGoFiles {
+ filenames = append(filenames, filepath.Join(pkg.Dir, name))
+ }
+ return filenames, nil
+}
+
+// Note: Could use filepath.Walk instead of walkDirs but that wouldn't
+// necessarily be shorter or clearer after adding the code to
+// terminate early for -short tests.
+
+func walkDirs(t *testing.T, dir string) {
+ // limit run time for short tests
+ if testing.Short() && time.Since(start) >= 750*time.Millisecond {
+ return
+ }
+
+ fis, err := ioutil.ReadDir(dir)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ // typecheck package in directory
+ files, err := pkgFilenames(dir)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if files != nil {
+ typecheck(t, dir, files)
+ }
+
+ // traverse subdirectories, but don't walk into testdata
+ for _, fi := range fis {
+ if fi.IsDir() && fi.Name() != "testdata" {
+ walkDirs(t, filepath.Join(dir, fi.Name()))
+ }
+ }
+}
diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go
new file mode 100644
index 0000000000..8b59df3eb6
--- /dev/null
+++ b/src/go/types/stmt.go
@@ -0,0 +1,731 @@
+// 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 implements typechecking of statements.
+
+package types
+
+import (
+ "fmt"
+ "go/ast"
+ exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+ "go/token"
+)
+
+func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body *ast.BlockStmt) {
+ if trace {
+ if name == "" {
+ name = ""
+ }
+ fmt.Printf("--- %s: %s {\n", name, sig)
+ defer fmt.Println("--- ")
+ }
+
+ // save/restore current context and setup function context
+ // (and use 0 indentation at function start)
+ defer func(ctxt context, indent int) {
+ check.context = ctxt
+ check.indent = indent
+ }(check.context, check.indent)
+ check.context = context{
+ decl: decl,
+ scope: sig.scope,
+ sig: sig,
+ }
+ check.indent = 0
+
+ check.stmtList(0, body.List)
+
+ if check.hasLabel {
+ check.labels(body)
+ }
+
+ if sig.results.Len() > 0 && !check.isTerminating(body, "") {
+ check.error(body.Rbrace, "missing return")
+ }
+
+ // spec: "Implementation restriction: A compiler may make it illegal to
+ // declare a variable inside a function body if the variable is never used."
+ // (One could check each scope after use, but that distributes this check
+ // over several places because CloseScope is not always called explicitly.)
+ check.usage(sig.scope)
+}
+
+func (check *Checker) usage(scope *Scope) {
+ for _, obj := range scope.elems {
+ if v, _ := obj.(*Var); v != nil && !v.used {
+ check.softErrorf(v.pos, "%s declared but not used", v.name)
+ }
+ }
+ for _, scope := range scope.children {
+ check.usage(scope)
+ }
+}
+
+// stmtContext is a bitset describing which
+// control-flow statements are permissible.
+type stmtContext uint
+
+const (
+ breakOk stmtContext = 1 << iota
+ continueOk
+ fallthroughOk
+)
+
+func (check *Checker) simpleStmt(s ast.Stmt) {
+ if s != nil {
+ check.stmt(0, s)
+ }
+}
+
+func (check *Checker) stmtList(ctxt stmtContext, list []ast.Stmt) {
+ ok := ctxt&fallthroughOk != 0
+ inner := ctxt &^ fallthroughOk
+ for i, s := range list {
+ inner := inner
+ if ok && i+1 == len(list) {
+ inner |= fallthroughOk
+ }
+ check.stmt(inner, s)
+ }
+}
+
+func (check *Checker) multipleDefaults(list []ast.Stmt) {
+ var first ast.Stmt
+ for _, s := range list {
+ var d ast.Stmt
+ switch c := s.(type) {
+ case *ast.CaseClause:
+ if len(c.List) == 0 {
+ d = s
+ }
+ case *ast.CommClause:
+ if c.Comm == nil {
+ d = s
+ }
+ default:
+ check.invalidAST(s.Pos(), "case/communication clause expected")
+ }
+ if d != nil {
+ if first != nil {
+ check.errorf(d.Pos(), "multiple defaults (first at %s)", first.Pos())
+ } else {
+ first = d
+ }
+ }
+ }
+}
+
+func (check *Checker) openScope(s ast.Stmt, comment string) {
+ scope := NewScope(check.scope, comment)
+ check.recordScope(s, scope)
+ check.scope = scope
+}
+
+func (check *Checker) closeScope() {
+ check.scope = check.scope.Parent()
+}
+
+func assignOp(op token.Token) token.Token {
+ // token_test.go verifies the token ordering this function relies on
+ if token.ADD_ASSIGN <= op && op <= token.AND_NOT_ASSIGN {
+ return op + (token.ADD - token.ADD_ASSIGN)
+ }
+ return token.ILLEGAL
+}
+
+func (check *Checker) suspendedCall(keyword string, call *ast.CallExpr) {
+ var x operand
+ var msg string
+ switch check.rawExpr(&x, call, nil) {
+ case conversion:
+ msg = "requires function call, not conversion"
+ case expression:
+ msg = "discards result of"
+ case statement:
+ return
+ default:
+ unreachable()
+ }
+ check.errorf(x.pos(), "%s %s %s", keyword, msg, &x)
+}
+
+func (check *Checker) caseValues(x operand /* copy argument (not *operand!) */, values []ast.Expr) {
+ // No duplicate checking for now. See issue 4524.
+ for _, e := range values {
+ var y operand
+ check.expr(&y, e)
+ if y.mode == invalid {
+ return
+ }
+ // TODO(gri) The convertUntyped call pair below appears in other places. Factor!
+ // Order matters: By comparing y against x, error positions are at the case values.
+ check.convertUntyped(&y, x.typ)
+ if y.mode == invalid {
+ return
+ }
+ check.convertUntyped(&x, y.typ)
+ if x.mode == invalid {
+ return
+ }
+ check.comparison(&y, &x, token.EQL)
+ }
+}
+
+func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[Type]token.Pos) (T Type) {
+L:
+ for _, e := range types {
+ T = check.typOrNil(e)
+ if T == Typ[Invalid] {
+ continue
+ }
+ // complain about duplicate types
+ // TODO(gri) use a type hash to avoid quadratic algorithm
+ for t, pos := range seen {
+ if T == nil && t == nil || T != nil && t != nil && Identical(T, t) {
+ // talk about "case" rather than "type" because of nil case
+ check.error(e.Pos(), "duplicate case in type switch")
+ check.errorf(pos, "\tprevious case %s", T) // secondary error, \t indented
+ continue L
+ }
+ }
+ seen[T] = e.Pos()
+ if T != nil {
+ check.typeAssertion(e.Pos(), x, xtyp, T)
+ }
+ }
+ return
+}
+
+// stmt typechecks statement s.
+func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
+ // statements cannot use iota in general
+ // (constant declarations set it explicitly)
+ assert(check.iota == nil)
+
+ // statements must end with the same top scope as they started with
+ if debug {
+ defer func(scope *Scope) {
+ // don't check if code is panicking
+ if p := recover(); p != nil {
+ panic(p)
+ }
+ assert(scope == check.scope)
+ }(check.scope)
+ }
+
+ inner := ctxt &^ fallthroughOk
+ switch s := s.(type) {
+ case *ast.BadStmt, *ast.EmptyStmt:
+ // ignore
+
+ case *ast.DeclStmt:
+ check.declStmt(s.Decl)
+
+ case *ast.LabeledStmt:
+ check.hasLabel = true
+ check.stmt(ctxt, s.Stmt)
+
+ case *ast.ExprStmt:
+ // spec: "With the exception of specific built-in functions,
+ // function and method calls and receive operations can appear
+ // in statement context. Such statements may be parenthesized."
+ var x operand
+ kind := check.rawExpr(&x, s.X, nil)
+ var msg string
+ switch x.mode {
+ default:
+ if kind == statement {
+ return
+ }
+ msg = "is not used"
+ case builtin:
+ msg = "must be called"
+ case typexpr:
+ msg = "is not an expression"
+ }
+ check.errorf(x.pos(), "%s %s", &x, msg)
+
+ case *ast.SendStmt:
+ var ch, x operand
+ check.expr(&ch, s.Chan)
+ check.expr(&x, s.Value)
+ if ch.mode == invalid || x.mode == invalid {
+ return
+ }
+ if tch, ok := ch.typ.Underlying().(*Chan); !ok || tch.dir == RecvOnly || !check.assignment(&x, tch.elem) {
+ if x.mode != invalid {
+ check.invalidOp(ch.pos(), "cannot send %s to channel %s", &x, &ch)
+ }
+ }
+
+ case *ast.IncDecStmt:
+ var op token.Token
+ switch s.Tok {
+ case token.INC:
+ op = token.ADD
+ case token.DEC:
+ op = token.SUB
+ default:
+ check.invalidAST(s.TokPos, "unknown inc/dec operation %s", s.Tok)
+ return
+ }
+ var x operand
+ Y := &ast.BasicLit{ValuePos: s.X.Pos(), Kind: token.INT, Value: "1"} // use x's position
+ check.binary(&x, s.X, Y, op)
+ if x.mode == invalid {
+ return
+ }
+ check.assignVar(s.X, &x)
+
+ case *ast.AssignStmt:
+ switch s.Tok {
+ case token.ASSIGN, token.DEFINE:
+ if len(s.Lhs) == 0 {
+ check.invalidAST(s.Pos(), "missing lhs in assignment")
+ return
+ }
+ if s.Tok == token.DEFINE {
+ check.shortVarDecl(s.TokPos, s.Lhs, s.Rhs)
+ } else {
+ // regular assignment
+ check.assignVars(s.Lhs, s.Rhs)
+ }
+
+ default:
+ // assignment operations
+ if len(s.Lhs) != 1 || len(s.Rhs) != 1 {
+ check.errorf(s.TokPos, "assignment operation %s requires single-valued expressions", s.Tok)
+ return
+ }
+ op := assignOp(s.Tok)
+ if op == token.ILLEGAL {
+ check.invalidAST(s.TokPos, "unknown assignment operation %s", s.Tok)
+ return
+ }
+ var x operand
+ check.binary(&x, s.Lhs[0], s.Rhs[0], op)
+ if x.mode == invalid {
+ return
+ }
+ check.assignVar(s.Lhs[0], &x)
+ }
+
+ case *ast.GoStmt:
+ check.suspendedCall("go", s.Call)
+
+ case *ast.DeferStmt:
+ check.suspendedCall("defer", s.Call)
+
+ case *ast.ReturnStmt:
+ res := check.sig.results
+ if res.Len() > 0 {
+ // function returns results
+ // (if one, say the first, result parameter is named, all of them are named)
+ if len(s.Results) == 0 && res.vars[0].name != "" {
+ // spec: "Implementation restriction: A compiler may disallow an empty expression
+ // list in a "return" statement if a different entity (constant, type, or variable)
+ // with the same name as a result parameter is in scope at the place of the return."
+ for _, obj := range res.vars {
+ if _, alt := check.scope.LookupParent(obj.name); alt != nil && alt != obj {
+ check.errorf(s.Pos(), "result parameter %s not in scope at return", obj.name)
+ check.errorf(alt.Pos(), "\tinner declaration of %s", obj)
+ // ok to continue
+ }
+ }
+ } else {
+ // return has results or result parameters are unnamed
+ check.initVars(res.vars, s.Results, s.Return)
+ }
+ } else if len(s.Results) > 0 {
+ check.error(s.Results[0].Pos(), "no result values expected")
+ check.use(s.Results...)
+ }
+
+ case *ast.BranchStmt:
+ if s.Label != nil {
+ check.hasLabel = true
+ return // checked in 2nd pass (check.labels)
+ }
+ switch s.Tok {
+ case token.BREAK:
+ if ctxt&breakOk == 0 {
+ check.error(s.Pos(), "break not in for, switch, or select statement")
+ }
+ case token.CONTINUE:
+ if ctxt&continueOk == 0 {
+ check.error(s.Pos(), "continue not in for statement")
+ }
+ case token.FALLTHROUGH:
+ if ctxt&fallthroughOk == 0 {
+ check.error(s.Pos(), "fallthrough statement out of place")
+ }
+ default:
+ check.invalidAST(s.Pos(), "branch statement: %s", s.Tok)
+ }
+
+ case *ast.BlockStmt:
+ check.openScope(s, "block")
+ defer check.closeScope()
+
+ check.stmtList(inner, s.List)
+
+ case *ast.IfStmt:
+ check.openScope(s, "if")
+ defer check.closeScope()
+
+ check.simpleStmt(s.Init)
+ var x operand
+ check.expr(&x, s.Cond)
+ if x.mode != invalid && !isBoolean(x.typ) {
+ check.error(s.Cond.Pos(), "non-boolean condition in if statement")
+ }
+ check.stmt(inner, s.Body)
+ if s.Else != nil {
+ check.stmt(inner, s.Else)
+ }
+
+ case *ast.SwitchStmt:
+ inner |= breakOk
+ check.openScope(s, "switch")
+ defer check.closeScope()
+
+ check.simpleStmt(s.Init)
+ var x operand
+ if s.Tag != nil {
+ check.expr(&x, s.Tag)
+ } else {
+ // spec: "A missing switch expression is
+ // equivalent to the boolean value true."
+ x.mode = constant
+ x.typ = Typ[Bool]
+ x.val = exact.MakeBool(true)
+ x.expr = &ast.Ident{NamePos: s.Body.Lbrace, Name: "true"}
+ }
+
+ check.multipleDefaults(s.Body.List)
+
+ for i, c := range s.Body.List {
+ clause, _ := c.(*ast.CaseClause)
+ if clause == nil {
+ check.invalidAST(c.Pos(), "incorrect expression switch case")
+ continue
+ }
+ if x.mode != invalid {
+ check.caseValues(x, clause.List)
+ }
+ check.openScope(clause, "case")
+ inner := inner
+ if i+1 < len(s.Body.List) {
+ inner |= fallthroughOk
+ }
+ check.stmtList(inner, clause.Body)
+ check.closeScope()
+ }
+
+ case *ast.TypeSwitchStmt:
+ inner |= breakOk
+ check.openScope(s, "type switch")
+ defer check.closeScope()
+
+ check.simpleStmt(s.Init)
+
+ // A type switch guard must be of the form:
+ //
+ // TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
+ //
+ // The parser is checking syntactic correctness;
+ // remaining syntactic errors are considered AST errors here.
+ // TODO(gri) better factoring of error handling (invalid ASTs)
+ //
+ var lhs *ast.Ident // lhs identifier or nil
+ var rhs ast.Expr
+ switch guard := s.Assign.(type) {
+ case *ast.ExprStmt:
+ rhs = guard.X
+ case *ast.AssignStmt:
+ if len(guard.Lhs) != 1 || guard.Tok != token.DEFINE || len(guard.Rhs) != 1 {
+ check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+ return
+ }
+
+ lhs, _ = guard.Lhs[0].(*ast.Ident)
+ if lhs == nil {
+ check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+ return
+ }
+
+ if lhs.Name == "_" {
+ // _ := x.(type) is an invalid short variable declaration
+ check.softErrorf(lhs.Pos(), "no new variable on left side of :=")
+ lhs = nil // avoid declared but not used error below
+ } else {
+ check.recordDef(lhs, nil) // lhs variable is implicitly declared in each cause clause
+ }
+
+ rhs = guard.Rhs[0]
+
+ default:
+ check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+ return
+ }
+
+ // rhs must be of the form: expr.(type) and expr must be an interface
+ expr, _ := rhs.(*ast.TypeAssertExpr)
+ if expr == nil || expr.Type != nil {
+ check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+ return
+ }
+ var x operand
+ check.expr(&x, expr.X)
+ if x.mode == invalid {
+ return
+ }
+ xtyp, _ := x.typ.Underlying().(*Interface)
+ if xtyp == nil {
+ check.errorf(x.pos(), "%s is not an interface", &x)
+ return
+ }
+
+ check.multipleDefaults(s.Body.List)
+
+ var lhsVars []*Var // list of implicitly declared lhs variables
+ seen := make(map[Type]token.Pos) // map of seen types to positions
+ for _, s := range s.Body.List {
+ clause, _ := s.(*ast.CaseClause)
+ if clause == nil {
+ check.invalidAST(s.Pos(), "incorrect type switch case")
+ continue
+ }
+ // Check each type in this type switch case.
+ T := check.caseTypes(&x, xtyp, clause.List, seen)
+ check.openScope(clause, "case")
+ // If lhs exists, declare a corresponding variable in the case-local scope.
+ if lhs != nil {
+ // spec: "The TypeSwitchGuard may include a short variable declaration.
+ // When that form is used, the variable is declared at the beginning of
+ // the implicit block in each clause. In clauses with a case listing
+ // exactly one type, the variable has that type; otherwise, the variable
+ // has the type of the expression in the TypeSwitchGuard."
+ if len(clause.List) != 1 || T == nil {
+ T = x.typ
+ }
+ obj := NewVar(lhs.Pos(), check.pkg, lhs.Name, T)
+ check.declare(check.scope, nil, obj)
+ check.recordImplicit(clause, obj)
+ // For the "declared but not used" error, all lhs variables act as
+ // one; i.e., if any one of them is 'used', all of them are 'used'.
+ // Collect them for later analysis.
+ lhsVars = append(lhsVars, obj)
+ }
+ check.stmtList(inner, clause.Body)
+ check.closeScope()
+ }
+
+ // If lhs exists, we must have at least one lhs variable that was used.
+ if lhs != nil {
+ var used bool
+ for _, v := range lhsVars {
+ if v.used {
+ used = true
+ }
+ v.used = true // avoid usage error when checking entire function
+ }
+ if !used {
+ check.softErrorf(lhs.Pos(), "%s declared but not used", lhs.Name)
+ }
+ }
+
+ case *ast.SelectStmt:
+ inner |= breakOk
+
+ check.multipleDefaults(s.Body.List)
+
+ for _, s := range s.Body.List {
+ clause, _ := s.(*ast.CommClause)
+ if clause == nil {
+ continue // error reported before
+ }
+
+ // clause.Comm must be a SendStmt, RecvStmt, or default case
+ valid := false
+ var rhs ast.Expr // rhs of RecvStmt, or nil
+ switch s := clause.Comm.(type) {
+ case nil, *ast.SendStmt:
+ valid = true
+ case *ast.AssignStmt:
+ if len(s.Rhs) == 1 {
+ rhs = s.Rhs[0]
+ }
+ case *ast.ExprStmt:
+ rhs = s.X
+ }
+
+ // if present, rhs must be a receive operation
+ if rhs != nil {
+ if x, _ := unparen(rhs).(*ast.UnaryExpr); x != nil && x.Op == token.ARROW {
+ valid = true
+ }
+ }
+
+ if !valid {
+ check.error(clause.Comm.Pos(), "select case must be send or receive (possibly with assignment)")
+ continue
+ }
+
+ check.openScope(s, "case")
+ if clause.Comm != nil {
+ check.stmt(inner, clause.Comm)
+ }
+ check.stmtList(inner, clause.Body)
+ check.closeScope()
+ }
+
+ case *ast.ForStmt:
+ inner |= breakOk | continueOk
+ check.openScope(s, "for")
+ defer check.closeScope()
+
+ check.simpleStmt(s.Init)
+ if s.Cond != nil {
+ var x operand
+ check.expr(&x, s.Cond)
+ if x.mode != invalid && !isBoolean(x.typ) {
+ check.error(s.Cond.Pos(), "non-boolean condition in for statement")
+ }
+ }
+ check.simpleStmt(s.Post)
+ // spec: "The init statement may be a short variable
+ // declaration, but the post statement must not."
+ if s, _ := s.Post.(*ast.AssignStmt); s != nil && s.Tok == token.DEFINE {
+ check.softErrorf(s.Pos(), "cannot declare in post statement")
+ check.use(s.Lhs...) // avoid follow-up errors
+ }
+ check.stmt(inner, s.Body)
+
+ case *ast.RangeStmt:
+ inner |= breakOk | continueOk
+ check.openScope(s, "for")
+ defer check.closeScope()
+
+ // check expression to iterate over
+ var x operand
+ check.expr(&x, s.X)
+
+ // determine key/value types
+ var key, val Type
+ if x.mode != invalid {
+ switch typ := x.typ.Underlying().(type) {
+ case *Basic:
+ if isString(typ) {
+ key = Typ[Int]
+ val = UniverseRune // use 'rune' name
+ }
+ case *Array:
+ key = Typ[Int]
+ val = typ.elem
+ case *Slice:
+ key = Typ[Int]
+ val = typ.elem
+ case *Pointer:
+ if typ, _ := typ.base.Underlying().(*Array); typ != nil {
+ key = Typ[Int]
+ val = typ.elem
+ }
+ case *Map:
+ key = typ.key
+ val = typ.elem
+ case *Chan:
+ key = typ.elem
+ val = Typ[Invalid]
+ if typ.dir == SendOnly {
+ check.errorf(x.pos(), "cannot range over send-only channel %s", &x)
+ // ok to continue
+ }
+ if s.Value != nil {
+ check.errorf(s.Value.Pos(), "iteration over %s permits only one iteration variable", &x)
+ // ok to continue
+ }
+ }
+ }
+
+ if key == nil {
+ check.errorf(x.pos(), "cannot range over %s", &x)
+ // ok to continue
+ }
+
+ // check assignment to/declaration of iteration variables
+ // (irregular assignment, cannot easily map to existing assignment checks)
+
+ // lhs expressions and initialization value (rhs) types
+ lhs := [2]ast.Expr{s.Key, s.Value}
+ rhs := [2]Type{key, val} // key, val may be nil
+
+ if s.Tok == token.DEFINE {
+ // short variable declaration; variable scope starts after the range clause
+ // (the for loop opens a new scope, so variables on the lhs never redeclare
+ // previously declared variables)
+ var vars []*Var
+ for i, lhs := range lhs {
+ if lhs == nil {
+ continue
+ }
+
+ // determine lhs variable
+ var obj *Var
+ if ident, _ := lhs.(*ast.Ident); ident != nil {
+ // declare new variable
+ name := ident.Name
+ obj = NewVar(ident.Pos(), check.pkg, name, nil)
+ check.recordDef(ident, obj)
+ // _ variables don't count as new variables
+ if name != "_" {
+ vars = append(vars, obj)
+ }
+ } else {
+ check.errorf(lhs.Pos(), "cannot declare %s", lhs)
+ obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
+ }
+
+ // initialize lhs variable
+ if typ := rhs[i]; typ != nil {
+ x.mode = value
+ x.expr = lhs // we don't have a better rhs expression to use here
+ x.typ = typ
+ check.initVar(obj, &x, false)
+ } else {
+ obj.typ = Typ[Invalid]
+ obj.used = true // don't complain about unused variable
+ }
+ }
+
+ // declare variables
+ if len(vars) > 0 {
+ for _, obj := range vars {
+ check.declare(check.scope, nil /* recordDef already called */, obj)
+ }
+ } else {
+ check.error(s.TokPos, "no new variables on left side of :=")
+ }
+ } else {
+ // ordinary assignment
+ for i, lhs := range lhs {
+ if lhs == nil {
+ continue
+ }
+ if typ := rhs[i]; typ != nil {
+ x.mode = value
+ x.expr = lhs // we don't have a better rhs expression to use here
+ x.typ = typ
+ check.assignVar(lhs, &x)
+ }
+ }
+ }
+
+ check.stmt(inner, s.Body)
+
+ default:
+ check.error(s.Pos(), "invalid statement")
+ }
+}
diff --git a/src/go/types/testdata/blank.src b/src/go/types/testdata/blank.src
new file mode 100644
index 0000000000..6a2507f482
--- /dev/null
+++ b/src/go/types/testdata/blank.src
@@ -0,0 +1,5 @@
+// Copyright 2014 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 _ /* ERROR invalid package name */
diff --git a/src/go/types/testdata/builtins.src b/src/go/types/testdata/builtins.src
new file mode 100644
index 0000000000..9eb551dc10
--- /dev/null
+++ b/src/go/types/testdata/builtins.src
@@ -0,0 +1,881 @@
+// 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.
+
+// builtin calls
+
+package builtins
+
+import "unsafe"
+
+func f0() {}
+
+func append1() {
+ var b byte
+ var x int
+ var s []byte
+ _ = append() // ERROR not enough arguments
+ _ = append("foo" /* ERROR not a slice */ )
+ _ = append(nil /* ERROR not a slice */ , s)
+ _ = append(x /* ERROR not a slice */ , s)
+ _ = append(s)
+ append /* ERROR not used */ (s)
+
+ _ = append(s, b)
+ _ = append(s, x /* ERROR cannot pass argument x */ )
+ _ = append(s, s /* ERROR cannot pass argument s */ )
+ _ = append(s... /* ERROR can only use ... with matching parameter */ )
+ _ = append(s, b, s... /* ERROR can only use ... with matching parameter */ )
+ _ = append(s, 1, 2, 3)
+ _ = append(s, 1, 2, 3, x /* ERROR cannot pass argument x */ , 5, 6, 6)
+ _ = append(s, 1, 2, s... /* ERROR can only use ... with matching parameter */ )
+ _ = append([]interface{}(nil), 1, 2, "foo", x, 3.1425, false)
+
+ type S []byte
+ type T string
+ var t T
+ _ = append(s, "foo" /* ERROR cannot convert */ )
+ _ = append(s, "foo"...)
+ _ = append(S(s), "foo" /* ERROR cannot convert */ )
+ _ = append(S(s), "foo"...)
+ _ = append(s, t /* ERROR cannot pass argument t */ )
+ _ = append(s, t...)
+ _ = append(s, T("foo")...)
+ _ = append(S(s), t /* ERROR cannot pass argument t */ )
+ _ = append(S(s), t...)
+ _ = append(S(s), T("foo")...)
+ _ = append([]string{}, t /* ERROR cannot pass argument t */ , "foo")
+ _ = append([]T{}, t, "foo")
+}
+
+// from the spec
+func append2() {
+ s0 := []int{0, 0}
+ s1 := append(s0, 2) // append a single element s1 == []int{0, 0, 2}
+ s2 := append(s1, 3, 5, 7) // append multiple elements s2 == []int{0, 0, 2, 3, 5, 7}
+ s3 := append(s2, s0...) // append a slice s3 == []int{0, 0, 2, 3, 5, 7, 0, 0}
+ s4 := append(s3[3:6], s3[2:]...) // append overlapping slice s4 == []int{3, 5, 7, 2, 3, 5, 7, 0, 0}
+
+ var t []interface{}
+ t = append(t, 42, 3.1415, "foo") // t == []interface{}{42, 3.1415, "foo"}
+
+ var b []byte
+ b = append(b, "bar"...) // append string contents b == []byte{'b', 'a', 'r' }
+
+ _ = s4
+}
+
+func append3() {
+ f1 := func() (s []int) { return }
+ f2 := func() (s []int, x int) { return }
+ f3 := func() (s []int, x, y int) { return }
+ f5 := func() (s []interface{}, x int, y float32, z string, b bool) { return }
+ ff := func() (int, float32) { return 0, 0 }
+ _ = append(f0 /* ERROR used as value */ ())
+ _ = append(f1())
+ _ = append(f2())
+ _ = append(f3())
+ _ = append(f5())
+ _ = append(ff /* ERROR not a slice */ ()) // TODO(gri) better error message
+}
+
+func cap1() {
+ var a [10]bool
+ var p *[20]int
+ var c chan string
+ _ = cap() // ERROR not enough arguments
+ _ = cap(1, 2) // ERROR too many arguments
+ _ = cap(42 /* ERROR invalid */)
+ const _3 = cap(a)
+ assert(_3 == 10)
+ const _4 = cap(p)
+ assert(_4 == 20)
+ _ = cap(c)
+ cap /* ERROR not used */ (c)
+
+ // issue 4744
+ type T struct{ a [10]int }
+ const _ = cap(((*T)(nil)).a)
+
+ var s [][]byte
+ _ = cap(s)
+ _ = cap(s... /* ERROR invalid use of \.\.\. */ )
+}
+
+func cap2() {
+ f1a := func() (a [10]int) { return }
+ f1s := func() (s []int) { return }
+ f2 := func() (s []int, x int) { return }
+ _ = cap(f0 /* ERROR used as value */ ())
+ _ = cap(f1a())
+ _ = cap(f1s())
+ _ = cap(f2()) // ERROR too many arguments
+}
+
+// test cases for issue 7387
+func cap3() {
+ var f = func() int { return 0 }
+ var x = f()
+ const (
+ _ = cap([4]int{})
+ _ = cap([4]int{x})
+ _ = cap /* ERROR not constant */ ([4]int{f()})
+ _ = cap /* ERROR not constant */ ([4]int{cap([]int{})})
+ _ = cap([4]int{cap([4]int{})})
+ )
+ var y float64
+ var z complex128
+ const (
+ _ = cap([4]float64{})
+ _ = cap([4]float64{y})
+ _ = cap([4]float64{real(2i)})
+ _ = cap /* ERROR not constant */ ([4]float64{real(z)})
+ )
+ var ch chan [10]int
+ const (
+ _ = cap /* ERROR not constant */ (<-ch)
+ _ = cap /* ERROR not constant */ ([4]int{(<-ch)[0]})
+ )
+}
+
+func close1() {
+ var c chan int
+ var r <-chan int
+ close() // ERROR not enough arguments
+ close(1, 2) // ERROR too many arguments
+ close(42 /* ERROR not a channel */)
+ close(r /* ERROR receive-only channel */)
+ close(c)
+ _ = close /* ERROR used as value */ (c)
+
+ var s []chan int
+ close(s... /* ERROR invalid use of \.\.\. */ )
+}
+
+func close2() {
+ f1 := func() (ch chan int) { return }
+ f2 := func() (ch chan int, x int) { return }
+ close(f0 /* ERROR used as value */ ())
+ close(f1())
+ close(f2()) // ERROR too many arguments
+}
+
+func complex1() {
+ var i32 int32
+ var f32 float32
+ var f64 float64
+ var c64 complex64
+ var c128 complex128
+ _ = complex() // ERROR not enough arguments
+ _ = complex(1) // ERROR not enough arguments
+ _ = complex(true /* ERROR invalid argument */ , 0)
+ _ = complex(i32 /* ERROR invalid argument */ , 0)
+ _ = complex("foo" /* ERROR invalid argument */ , 0)
+ _ = complex(c64 /* ERROR invalid argument */ , 0)
+ _ = complex(0, true /* ERROR invalid argument */ )
+ _ = complex(0, i32 /* ERROR invalid argument */ )
+ _ = complex(0, "foo" /* ERROR invalid argument */ )
+ _ = complex(0, c64 /* ERROR invalid argument */ )
+ _ = complex(f32, f32)
+ _ = complex(f32, 1)
+ _ = complex(f32, 1.0)
+ _ = complex(f32, 'a')
+ _ = complex(f64, f64)
+ _ = complex(f64, 1)
+ _ = complex(f64, 1.0)
+ _ = complex(f64, 'a')
+ _ = complex(f32 /* ERROR mismatched types */ , f64)
+ _ = complex(f64 /* ERROR mismatched types */ , f32)
+ _ = complex(1, 1)
+ _ = complex(1, 1.1)
+ _ = complex(1, 'a')
+ complex /* ERROR not used */ (1, 2)
+
+ var _ complex64 = complex(f32, f32)
+ var _ complex64 = complex /* ERROR cannot initialize */ (f64, f64)
+
+ var _ complex128 = complex /* ERROR cannot initialize */ (f32, f32)
+ var _ complex128 = complex(f64, f64)
+
+ // untyped constants
+ const _ int = complex(1, 0)
+ const _ float32 = complex(1, 0)
+ const _ complex64 = complex(1, 0)
+ const _ complex128 = complex(1, 0)
+
+ const _ int = complex /* ERROR int */ (1.1, 0)
+ const _ float32 = complex /* ERROR float32 */ (1, 2)
+
+ // untyped values
+ var s uint
+ _ = complex(1 /* ERROR integer */ <>8&1 + mi>>16&1 + mi>>32&1)
+ logSizeofUint = uint(mu>>8&1 + mu>>16&1 + mu>>32&1)
+ logSizeofUintptr = uint(mp>>8&1 + mp>>16&1 + mp>>32&1)
+)
+
+const (
+ minInt8 = -1<<(8< 0)
+ _ = assert(smallestFloat64 > 0)
+)
+
+const (
+ maxFloat32 = 1<<127 * (1<<24 - 1) / (1.0<<23)
+ maxFloat64 = 1<<1023 * (1<<53 - 1) / (1.0<<52)
+)
+
+const (
+ _ int8 = minInt8 /* ERROR "overflows" */ - 1
+ _ int8 = minInt8
+ _ int8 = maxInt8
+ _ int8 = maxInt8 /* ERROR "overflows" */ + 1
+ _ int8 = smallestFloat64 /* ERROR "truncated" */
+
+ _ = int8(minInt8 /* ERROR "cannot convert" */ - 1)
+ _ = int8(minInt8)
+ _ = int8(maxInt8)
+ _ = int8(maxInt8 /* ERROR "cannot convert" */ + 1)
+ _ = int8(smallestFloat64 /* ERROR "cannot convert" */)
+)
+
+const (
+ _ int16 = minInt16 /* ERROR "overflows" */ - 1
+ _ int16 = minInt16
+ _ int16 = maxInt16
+ _ int16 = maxInt16 /* ERROR "overflows" */ + 1
+ _ int16 = smallestFloat64 /* ERROR "truncated" */
+
+ _ = int16(minInt16 /* ERROR "cannot convert" */ - 1)
+ _ = int16(minInt16)
+ _ = int16(maxInt16)
+ _ = int16(maxInt16 /* ERROR "cannot convert" */ + 1)
+ _ = int16(smallestFloat64 /* ERROR "cannot convert" */)
+)
+
+const (
+ _ int32 = minInt32 /* ERROR "overflows" */ - 1
+ _ int32 = minInt32
+ _ int32 = maxInt32
+ _ int32 = maxInt32 /* ERROR "overflows" */ + 1
+ _ int32 = smallestFloat64 /* ERROR "truncated" */
+
+ _ = int32(minInt32 /* ERROR "cannot convert" */ - 1)
+ _ = int32(minInt32)
+ _ = int32(maxInt32)
+ _ = int32(maxInt32 /* ERROR "cannot convert" */ + 1)
+ _ = int32(smallestFloat64 /* ERROR "cannot convert" */)
+)
+
+const (
+ _ int64 = minInt64 /* ERROR "overflows" */ - 1
+ _ int64 = minInt64
+ _ int64 = maxInt64
+ _ int64 = maxInt64 /* ERROR "overflows" */ + 1
+ _ int64 = smallestFloat64 /* ERROR "truncated" */
+
+ _ = int64(minInt64 /* ERROR "cannot convert" */ - 1)
+ _ = int64(minInt64)
+ _ = int64(maxInt64)
+ _ = int64(maxInt64 /* ERROR "cannot convert" */ + 1)
+ _ = int64(smallestFloat64 /* ERROR "cannot convert" */)
+)
+
+const (
+ _ int = minInt /* ERROR "overflows" */ - 1
+ _ int = minInt
+ _ int = maxInt
+ _ int = maxInt /* ERROR "overflows" */ + 1
+ _ int = smallestFloat64 /* ERROR "truncated" */
+
+ _ = int(minInt /* ERROR "cannot convert" */ - 1)
+ _ = int(minInt)
+ _ = int(maxInt)
+ _ = int(maxInt /* ERROR "cannot convert" */ + 1)
+ _ = int(smallestFloat64 /* ERROR "cannot convert" */)
+)
+
+const (
+ _ uint8 = 0 /* ERROR "overflows" */ - 1
+ _ uint8 = 0
+ _ uint8 = maxUint8
+ _ uint8 = maxUint8 /* ERROR "overflows" */ + 1
+ _ uint8 = smallestFloat64 /* ERROR "truncated" */
+
+ _ = uint8(0 /* ERROR "cannot convert" */ - 1)
+ _ = uint8(0)
+ _ = uint8(maxUint8)
+ _ = uint8(maxUint8 /* ERROR "cannot convert" */ + 1)
+ _ = uint8(smallestFloat64 /* ERROR "cannot convert" */)
+)
+
+const (
+ _ uint16 = 0 /* ERROR "overflows" */ - 1
+ _ uint16 = 0
+ _ uint16 = maxUint16
+ _ uint16 = maxUint16 /* ERROR "overflows" */ + 1
+ _ uint16 = smallestFloat64 /* ERROR "truncated" */
+
+ _ = uint16(0 /* ERROR "cannot convert" */ - 1)
+ _ = uint16(0)
+ _ = uint16(maxUint16)
+ _ = uint16(maxUint16 /* ERROR "cannot convert" */ + 1)
+ _ = uint16(smallestFloat64 /* ERROR "cannot convert" */)
+)
+
+const (
+ _ uint32 = 0 /* ERROR "overflows" */ - 1
+ _ uint32 = 0
+ _ uint32 = maxUint32
+ _ uint32 = maxUint32 /* ERROR "overflows" */ + 1
+ _ uint32 = smallestFloat64 /* ERROR "truncated" */
+
+ _ = uint32(0 /* ERROR "cannot convert" */ - 1)
+ _ = uint32(0)
+ _ = uint32(maxUint32)
+ _ = uint32(maxUint32 /* ERROR "cannot convert" */ + 1)
+ _ = uint32(smallestFloat64 /* ERROR "cannot convert" */)
+)
+
+const (
+ _ uint64 = 0 /* ERROR "overflows" */ - 1
+ _ uint64 = 0
+ _ uint64 = maxUint64
+ _ uint64 = maxUint64 /* ERROR "overflows" */ + 1
+ _ uint64 = smallestFloat64 /* ERROR "truncated" */
+
+ _ = uint64(0 /* ERROR "cannot convert" */ - 1)
+ _ = uint64(0)
+ _ = uint64(maxUint64)
+ _ = uint64(maxUint64 /* ERROR "cannot convert" */ + 1)
+ _ = uint64(smallestFloat64 /* ERROR "cannot convert" */)
+)
+
+const (
+ _ uint = 0 /* ERROR "overflows" */ - 1
+ _ uint = 0
+ _ uint = maxUint
+ _ uint = maxUint /* ERROR "overflows" */ + 1
+ _ uint = smallestFloat64 /* ERROR "truncated" */
+
+ _ = uint(0 /* ERROR "cannot convert" */ - 1)
+ _ = uint(0)
+ _ = uint(maxUint)
+ _ = uint(maxUint /* ERROR "cannot convert" */ + 1)
+ _ = uint(smallestFloat64 /* ERROR "cannot convert" */)
+)
+
+const (
+ _ uintptr = 0 /* ERROR "overflows" */ - 1
+ _ uintptr = 0
+ _ uintptr = maxUintptr
+ _ uintptr = maxUintptr /* ERROR "overflows" */ + 1
+ _ uintptr = smallestFloat64 /* ERROR "truncated" */
+
+ _ = uintptr(0 /* ERROR "cannot convert" */ - 1)
+ _ = uintptr(0)
+ _ = uintptr(maxUintptr)
+ _ = uintptr(maxUintptr /* ERROR "cannot convert" */ + 1)
+ _ = uintptr(smallestFloat64 /* ERROR "cannot convert" */)
+)
+
+const (
+ _ float32 = minInt64
+ _ float64 = minInt64
+ _ complex64 = minInt64
+ _ complex128 = minInt64
+
+ _ = float32(minInt64)
+ _ = float64(minInt64)
+ _ = complex64(minInt64)
+ _ = complex128(minInt64)
+)
+
+const (
+ _ float32 = maxUint64
+ _ float64 = maxUint64
+ _ complex64 = maxUint64
+ _ complex128 = maxUint64
+
+ _ = float32(maxUint64)
+ _ = float64(maxUint64)
+ _ = complex64(maxUint64)
+ _ = complex128(maxUint64)
+)
+
+// TODO(gri) find smaller deltas below
+
+const delta32 = maxFloat32/(1 << 23)
+
+const (
+ _ float32 = - /* ERROR "overflow" */ (maxFloat32 + delta32)
+ _ float32 = -maxFloat32
+ _ float32 = maxFloat32
+ _ float32 = maxFloat32 /* ERROR "overflow" */ + delta32
+
+ _ = float32(- /* ERROR "cannot convert" */ (maxFloat32 + delta32))
+ _ = float32(-maxFloat32)
+ _ = float32(maxFloat32)
+ _ = float32(maxFloat32 /* ERROR "cannot convert" */ + delta32)
+
+ _ = assert(float32(smallestFloat32) == smallestFloat32)
+ _ = assert(float32(smallestFloat32/2) == 0)
+ _ = assert(float32(smallestFloat64) == 0)
+ _ = assert(float32(smallestFloat64/2) == 0)
+)
+
+const delta64 = maxFloat64/(1 << 52)
+
+const (
+ _ float64 = - /* ERROR "overflow" */ (maxFloat64 + delta64)
+ _ float64 = -maxFloat64
+ _ float64 = maxFloat64
+ _ float64 = maxFloat64 /* ERROR "overflow" */ + delta64
+
+ _ = float64(- /* ERROR "cannot convert" */ (maxFloat64 + delta64))
+ _ = float64(-maxFloat64)
+ _ = float64(maxFloat64)
+ _ = float64(maxFloat64 /* ERROR "cannot convert" */ + delta64)
+
+ _ = assert(float64(smallestFloat32) == smallestFloat32)
+ _ = assert(float64(smallestFloat32/2) == smallestFloat32/2)
+ _ = assert(float64(smallestFloat64) == smallestFloat64)
+ _ = assert(float64(smallestFloat64/2) == 0)
+)
+
+const (
+ _ complex64 = - /* ERROR "overflow" */ (maxFloat32 + delta32)
+ _ complex64 = -maxFloat32
+ _ complex64 = maxFloat32
+ _ complex64 = maxFloat32 /* ERROR "overflow" */ + delta32
+
+ _ = complex64(- /* ERROR "cannot convert" */ (maxFloat32 + delta32))
+ _ = complex64(-maxFloat32)
+ _ = complex64(maxFloat32)
+ _ = complex64(maxFloat32 /* ERROR "cannot convert" */ + delta32)
+)
+
+const (
+ _ complex128 = - /* ERROR "overflow" */ (maxFloat64 + delta64)
+ _ complex128 = -maxFloat64
+ _ complex128 = maxFloat64
+ _ complex128 = maxFloat64 /* ERROR "overflow" */ + delta64
+
+ _ = complex128(- /* ERROR "cannot convert" */ (maxFloat64 + delta64))
+ _ = complex128(-maxFloat64)
+ _ = complex128(maxFloat64)
+ _ = complex128(maxFloat64 /* ERROR "cannot convert" */ + delta64)
+)
+
+// Initialization of typed constant and conversion are the same:
+const (
+ f32 = 1 + smallestFloat32
+ x32 float32 = f32
+ y32 = float32(f32)
+ _ = assert(x32 - y32 == 0)
+)
+
+const (
+ f64 = 1 + smallestFloat64
+ x64 float64 = f64
+ y64 = float64(f64)
+ _ = assert(x64 - y64 == 0)
+)
diff --git a/src/go/types/testdata/constdecl.src b/src/go/types/testdata/constdecl.src
new file mode 100644
index 0000000000..6de9b13d6e
--- /dev/null
+++ b/src/go/types/testdata/constdecl.src
@@ -0,0 +1,97 @@
+// 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 constdecl
+
+import "math"
+
+var v int
+
+// Const decls must be initialized by constants.
+const _ = v /* ERROR "not constant" */
+const _ = math /* ERROR "not constant" */ .Sin(0)
+const _ = int /* ERROR "not an expression" */
+
+func _() {
+ const _ = v /* ERROR "not constant" */
+ const _ = math /* ERROR "not constant" */ .Sin(0)
+ const _ = int /* ERROR "not an expression" */
+}
+
+// Identifier and expression arity must match.
+// The first error message is produced by the parser.
+// In a real-world scenario, the type-checker would not be run
+// in this case and the 2nd error message would not appear.
+const _ /* ERROR "missing constant value" */ /* ERROR "missing init expr for _" */
+const _ = 1, 2 /* ERROR "extra init expr 2" */
+
+const _ /* ERROR "missing constant value" */ /* ERROR "missing init expr for _" */ int
+const _ int = 1, 2 /* ERROR "extra init expr 2" */
+
+const (
+ _ /* ERROR "missing constant value" */ /* ERROR "missing init expr for _" */
+ _ = 1, 2 /* ERROR "extra init expr 2" */
+
+ _ /* ERROR "missing constant value" */ /* ERROR "missing init expr for _" */ int
+ _ int = 1, 2 /* ERROR "extra init expr 2" */
+)
+
+const (
+ _ = 1
+ _
+ _, _ /* ERROR "missing init expr for _" */
+ _
+)
+
+const (
+ _, _ = 1, 2
+ _, _
+ _ /* ERROR "extra init expr at" */
+ _, _
+ _, _, _ /* ERROR "missing init expr for _" */
+ _, _
+)
+
+func _() {
+ const _ /* ERROR "missing constant value" */ /* ERROR "missing init expr for _" */
+ const _ = 1, 2 /* ERROR "extra init expr 2" */
+
+ const _ /* ERROR "missing constant value" */ /* ERROR "missing init expr for _" */ int
+ const _ int = 1, 2 /* ERROR "extra init expr 2" */
+
+ const (
+ _ /* ERROR "missing constant value" */ /* ERROR "missing init expr for _" */
+ _ = 1, 2 /* ERROR "extra init expr 2" */
+
+ _ /* ERROR "missing constant value" */ /* ERROR "missing init expr for _" */ int
+ _ int = 1, 2 /* ERROR "extra init expr 2" */
+ )
+
+ const (
+ _ = 1
+ _
+ _, _ /* ERROR "missing init expr for _" */
+ _
+ )
+
+ const (
+ _, _ = 1, 2
+ _, _
+ _ /* ERROR "extra init expr at" */
+ _, _
+ _, _, _ /* ERROR "missing init expr for _" */
+ _, _
+ )
+}
+
+// Test case for constant with invalid initialization.
+// Caused panic because the constant value was not set up (gri - 7/8/2014).
+func _() {
+ const (
+ x string = missing /* ERROR "undeclared name" */
+ y = x + ""
+ )
+}
+
+// TODO(gri) move extra tests from testdata/const0.src into here
diff --git a/src/go/types/testdata/conversions.src b/src/go/types/testdata/conversions.src
new file mode 100644
index 0000000000..4251424646
--- /dev/null
+++ b/src/go/types/testdata/conversions.src
@@ -0,0 +1,88 @@
+// 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.
+
+// conversions
+
+package conversions
+
+import "unsafe"
+
+// argument count
+var (
+ _ = int() /* ERROR "missing argument" */
+ _ = int(1, 2 /* ERROR "too many arguments" */ )
+)
+
+// numeric constant conversions are in const1.src.
+
+func string_conversions() {
+ const A = string(65)
+ assert(A == "A")
+ const E = string(-1)
+ assert(E == "\uFFFD")
+ assert(E == string(1234567890))
+
+ type myint int
+ assert(A == string(myint(65)))
+
+ type mystring string
+ const _ mystring = mystring("foo")
+
+ const _ = string(true /* ERROR "cannot convert" */ )
+ const _ = string(1.2 /* ERROR "cannot convert" */ )
+ const _ = string(nil /* ERROR "cannot convert" */ )
+}
+
+func interface_conversions() {
+ type E interface{}
+
+ type I1 interface{
+ m1()
+ }
+
+ type I2 interface{
+ m1()
+ m2(x int)
+ }
+
+ type I3 interface{
+ m1()
+ m2() int
+ }
+
+ var e E
+ var i1 I1
+ var i2 I2
+ var i3 I3
+
+ _ = E(0)
+ _ = E(nil)
+ _ = E(e)
+ _ = E(i1)
+ _ = E(i2)
+
+ _ = I1(0 /* ERROR "cannot convert" */ )
+ _ = I1(nil)
+ _ = I1(i1)
+ _ = I1(e /* ERROR "cannot convert" */ )
+ _ = I1(i2)
+
+ _ = I2(nil)
+ _ = I2(i1 /* ERROR "cannot convert" */ )
+ _ = I2(i2)
+ _ = I2(i3 /* ERROR "cannot convert" */ )
+
+ _ = I3(nil)
+ _ = I3(i1 /* ERROR "cannot convert" */ )
+ _ = I3(i2 /* ERROR "cannot convert" */ )
+ _ = I3(i3)
+
+ // TODO(gri) add more tests, improve error message
+}
+
+func issue6326() {
+ type T unsafe.Pointer
+ var x T
+ _ = uintptr(x) // see issue 6326
+}
diff --git a/src/go/types/testdata/cycles.src b/src/go/types/testdata/cycles.src
new file mode 100644
index 0000000000..621d83c945
--- /dev/null
+++ b/src/go/types/testdata/cycles.src
@@ -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.
+
+package cycles
+
+type (
+ T0 int
+ T1 /* ERROR cycle */ T1
+ T2 *T2
+
+ T3 /* ERROR cycle */ T4
+ T4 T5
+ T5 T3
+
+ T6 T7
+ T7 *T8
+ T8 T6
+
+ // arrays
+ A0 /* ERROR cycle */ [10]A0
+ A1 [10]*A1
+
+ A2 /* ERROR cycle */ [10]A3
+ A3 [10]A4
+ A4 A2
+
+ A5 [10]A6
+ A6 *A5
+
+ // slices
+ L0 []L0
+
+ // structs
+ S0 /* ERROR cycle */ struct{ _ S0 }
+ S1 /* ERROR cycle */ struct{ S1 }
+ S2 struct{ _ *S2 }
+ S3 struct{ *S3 }
+
+ S4 /* ERROR cycle */ struct{ S5 }
+ S5 struct{ S6 }
+ S6 S4
+
+ // pointers
+ P0 *P0
+
+ // functions
+ F0 func(F0)
+ F1 func() F1
+ F2 func(F2) F2
+
+ // interfaces
+ I0 /* ERROR cycle */ interface{ I0 }
+
+ I1 interface{ I2 }
+ I2 interface{ I3 }
+ I3 /* ERROR cycle */ interface{ I1 }
+
+ I4 interface{ f(I4) }
+
+ // testcase for issue 5090
+ I5 interface{ f(I6) }
+ I6 interface{ I5 }
+
+ // maps
+ M0 map[M0 /* ERROR invalid map key */ ]M0
+
+ // channels
+ C0 chan C0
+)
+
+func _() {
+ type (
+ t1 /* ERROR cycle */ t1
+ t2 *t2
+
+ t3 t4 /* ERROR undeclared */
+ t4 t5 /* ERROR undeclared */
+ t5 t3
+
+ // arrays
+ a0 /* ERROR cycle */ [10]a0
+ a1 [10]*a1
+
+ // slices
+ l0 []l0
+
+ // structs
+ s0 /* ERROR cycle */ struct{ _ s0 }
+ s1 /* ERROR cycle */ struct{ s1 }
+ s2 struct{ _ *s2 }
+ s3 struct{ *s3 }
+
+ // pointers
+ p0 *p0
+
+ // functions
+ f0 func(f0)
+ f1 func() f1
+ f2 func(f2) f2
+
+ // interfaces
+ i0 /* ERROR cycle */ interface{ i0 }
+
+ // maps
+ m0 map[m0 /* ERROR invalid map key */ ]m0
+
+ // channels
+ c0 chan c0
+ )
+}
+
+// test cases for issue 6667
+
+type A [10]map[A /* ERROR invalid map key */ ]bool
+
+type S struct {
+ m map[S /* ERROR invalid map key */ ]bool
+}
+
+// test cases for issue 7236
+// (cycle detection must not be dependent on starting point of resolution)
+
+type (
+ P1 *T9
+ T9 /* ERROR cycle */ T9
+
+ T10 /* ERROR cycle */ T10
+ P2 *T10
+)
+
+func (T11) m() {}
+
+type T11 /* ERROR cycle */ struct{ T11 }
+
+type T12 /* ERROR cycle */ struct{ T12 }
+
+func (*T12) m() {}
+
+type (
+ P3 *T13
+ T13 /* ERROR cycle */ T13
+)
\ No newline at end of file
diff --git a/src/go/types/testdata/cycles1.src b/src/go/types/testdata/cycles1.src
new file mode 100644
index 0000000000..ae2b38ebec
--- /dev/null
+++ b/src/go/types/testdata/cycles1.src
@@ -0,0 +1,77 @@
+// 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 p
+
+type (
+ A interface {
+ a() interface {
+ ABC1
+ }
+ }
+ B interface {
+ b() interface {
+ ABC2
+ }
+ }
+ C interface {
+ c() interface {
+ ABC3
+ }
+ }
+
+ AB interface {
+ A
+ B
+ }
+ BC interface {
+ B
+ C
+ }
+
+ ABC1 interface {
+ A
+ B
+ C
+ }
+ ABC2 interface {
+ AB
+ C
+ }
+ ABC3 interface {
+ A
+ BC
+ }
+)
+
+var (
+ x1 ABC1
+ x2 ABC2
+ x3 ABC3
+)
+
+func _() {
+ // all types have the same method set
+ x1 = x2
+ x2 = x1
+
+ x1 = x3
+ x3 = x1
+
+ x2 = x3
+ x3 = x2
+
+ // all methods return the same type again
+ x1 = x1.a()
+ x1 = x1.b()
+ x1 = x1.c()
+
+ x2 = x2.a()
+ x2 = x2.b()
+ x2 = x2.c()
+
+ x3 = x3.a()
+ x3 = x3.b()
+ x3 = x3.c()
+}
diff --git a/src/go/types/testdata/cycles2.src b/src/go/types/testdata/cycles2.src
new file mode 100644
index 0000000000..345ab56ea6
--- /dev/null
+++ b/src/go/types/testdata/cycles2.src
@@ -0,0 +1,118 @@
+// 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 p
+
+import "unsafe"
+
+// Test case for issue 5090
+
+type t interface {
+ f(u)
+}
+
+type u interface {
+ t
+}
+
+func _() {
+ var t t
+ var u u
+
+ t.f(t)
+ t.f(u)
+
+ u.f(t)
+ u.f(u)
+}
+
+
+// Test case for issue 6589.
+
+type A interface {
+ a() interface {
+ AB
+ }
+}
+
+type B interface {
+ a() interface {
+ AB
+ }
+}
+
+type AB interface {
+ a() interface {
+ A
+ B /* ERROR a redeclared */
+ }
+ b() interface {
+ A
+ B /* ERROR a redeclared */
+ }
+}
+
+var x AB
+var y interface {
+ A
+ B /* ERROR a redeclared */
+}
+var _ = x /* ERROR cannot compare */ == y
+
+
+// Test case for issue 6638.
+
+type T interface {
+ m() [T /* ERROR no value */ (nil).m()[0]]int
+}
+
+// Variations of this test case.
+
+type T1 interface {
+ m() [x1 /* ERROR no value */ .m()[0]]int
+}
+
+var x1 T1
+
+type T2 interface {
+ m() [len(x2 /* ERROR no value */ .m())]int
+}
+
+var x2 T2
+
+type T3 interface {
+ m() [unsafe.Sizeof(x3.m)]int
+}
+
+var x3 T3
+
+// The test case below should also report an error for
+// the cast inside the T4 interface (like it does for the
+// variable initialization). The reason why it does not is
+// that inside T4, the method x4.m depends on T4 which is not
+// fully set up yet. The x4.m method happens to have an empty
+// signature which is why the cast is permitted.
+// TODO(gri) Consider marking methods as incomplete and provide
+// a better error message in that case.
+
+type T4 interface {
+ m() [unsafe.Sizeof(cast4(x4.m))]int
+}
+
+var x4 T4
+var _ = cast4(x4 /* ERROR cannot convert */.m)
+
+type cast4 func()
+
+// This test is symmetric to the T4 case: Here the cast is
+// "correct", but it doesn't work inside the T5 interface.
+
+type T5 interface {
+ m() [unsafe.Sizeof(cast5(x5 /* ERROR cannot convert */ .m))]int
+}
+
+var x5 T5
+var _ = cast5(x5.m)
+
+type cast5 func() [0]int
diff --git a/src/go/types/testdata/cycles3.src b/src/go/types/testdata/cycles3.src
new file mode 100644
index 0000000000..3da4fb5761
--- /dev/null
+++ b/src/go/types/testdata/cycles3.src
@@ -0,0 +1,60 @@
+// 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 p
+
+import "unsafe"
+
+var (
+ _ A = A(nil).a().b().c().d().e().f()
+ _ A = A(nil).b().c().d().e().f()
+ _ A = A(nil).c().d().e().f()
+ _ A = A(nil).d().e().f()
+ _ A = A(nil).e().f()
+ _ A = A(nil).f()
+ _ A = A(nil)
+)
+
+type (
+ A interface {
+ a() B
+ B
+ }
+
+ B interface {
+ b() C
+ C
+ }
+
+ C interface {
+ c() D
+ D
+ }
+
+ D interface {
+ d() E
+ E
+ }
+
+ E interface {
+ e() F
+ F
+ }
+
+ F interface {
+ f() A
+ }
+)
+
+type (
+ U interface {
+ V
+ }
+
+ V interface {
+ v() [unsafe.Sizeof(u)]int
+ }
+)
+
+var u U
diff --git a/src/go/types/testdata/cycles4.src b/src/go/types/testdata/cycles4.src
new file mode 100644
index 0000000000..445babca68
--- /dev/null
+++ b/src/go/types/testdata/cycles4.src
@@ -0,0 +1,110 @@
+// 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 p
+
+// Check that all methods of T are collected before
+// determining the result type of m (which embeds
+// all methods of T).
+
+type T interface {
+ m() interface {T}
+ E
+}
+
+var _ = T.m(nil).m().e()
+
+type E interface {
+ e() int
+}
+
+// Check that unresolved forward chains are followed
+// (see also comment in resolver.go, checker.typeDecl).
+
+var _ = C.m(nil).m().e()
+
+type A B
+
+type B interface {
+ m() interface{C}
+ E
+}
+
+type C A
+
+// Check that interface type comparison for identity
+// does not recur endlessly.
+
+type T1 interface {
+ m() interface{T1}
+}
+
+type T2 interface {
+ m() interface{T2}
+}
+
+func _(x T1, y T2) {
+ // Checking for assignability of interfaces must check
+ // if all methods of x are present in y, and that they
+ // have identical signatures. The signatures recur via
+ // the result type, which is an interface that embeds
+ // a single method m that refers to the very interface
+ // that contains it. This requires cycle detection in
+ // identity checks for interface types.
+ x = y
+}
+
+type T3 interface {
+ m() interface{T4}
+}
+
+type T4 interface {
+ m() interface{T3}
+}
+
+func _(x T1, y T3) {
+ x = y
+}
+
+// Check that interfaces are type-checked in order of
+// (embedded interface) dependencies (was issue 7158).
+
+var x1 T5 = T7(nil)
+
+type T5 interface {
+ T6
+}
+
+type T6 interface {
+ m() T7
+}
+type T7 interface {
+ T5
+}
+
+// Actual test case from issue 7158.
+
+func wrapNode() Node {
+ return wrapElement()
+}
+
+func wrapElement() Element {
+ return nil
+}
+
+type EventTarget interface {
+ AddEventListener(Event)
+}
+
+type Node interface {
+ EventTarget
+}
+
+type Element interface {
+ Node
+}
+
+type Event interface {
+ Target() Element
+}
diff --git a/src/go/types/testdata/decls0.src b/src/go/types/testdata/decls0.src
new file mode 100644
index 0000000000..f1df3ea703
--- /dev/null
+++ b/src/go/types/testdata/decls0.src
@@ -0,0 +1,206 @@
+// 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.
+
+// type declarations
+
+package decls0
+
+import "unsafe"
+
+const pi = 3.1415
+
+type (
+ N undeclared /* ERROR "undeclared" */
+ B bool
+ I int32
+ A [10]P
+ T struct {
+ x, y P
+ }
+ P *T
+ R (*R)
+ F func(A) I
+ Y interface {
+ f(A) I
+ }
+ S [](((P)))
+ M map[I]F
+ C chan<- I
+
+ // blank types must be typechecked
+ _ pi /* ERROR "not a type" */
+ _ struct{}
+ _ struct{ pi /* ERROR "not a type" */ }
+)
+
+
+// declarations of init
+const _, init /* ERROR "cannot declare init" */ , _ = 0, 1, 2
+type init /* ERROR "cannot declare init" */ struct{}
+var _, init /* ERROR "cannot declare init" */ int
+
+func init() {}
+func init /* ERROR "missing function body" */ ()
+
+func _() { const init = 0 }
+func _() { type init int }
+func _() { var init int; _ = init }
+
+// invalid array types
+type (
+ iA0 [... /* ERROR "invalid use of '...'" */ ]byte
+ iA1 [1 /* ERROR "invalid array length" */ <<100]int
+ iA2 [- /* ERROR "invalid array length" */ 1]complex128
+ iA3 ["foo" /* ERROR "must be integer" */ ]string
+)
+
+
+type (
+ p1 pi /* ERROR "no field or method foo" */ .foo
+ p2 unsafe.Pointer
+)
+
+
+type (
+ Pi pi /* ERROR "not a type" */
+
+ a /* ERROR "illegal cycle" */ a
+ a /* ERROR "redeclared" */ int
+
+ // where the cycle error appears depends on the
+ // order in which declarations are processed
+ // (which depends on the order in which a map
+ // is iterated through)
+ b /* ERROR "illegal cycle" */ c
+ c d
+ d e
+ e b
+
+ t *t
+
+ U V
+ V *W
+ W U
+
+ P1 *S2
+ P2 P1
+
+ S0 struct {
+ }
+ S1 struct {
+ a, b, c int
+ u, v, a /* ERROR "redeclared" */ float32
+ }
+ S2 struct {
+ S0 // anonymous field
+ S0 /* ERROR "redeclared" */ int
+ }
+ S3 struct {
+ x S2
+ }
+ S4/* ERROR "illegal cycle" */ struct {
+ S4
+ }
+ S5 /* ERROR "illegal cycle" */ struct {
+ S6
+ }
+ S6 struct {
+ field S7
+ }
+ S7 struct {
+ S5
+ }
+
+ L1 []L1
+ L2 []int
+
+ A1 [10.0]int
+ A2 /* ERROR "illegal cycle" */ [10]A2
+ A3 /* ERROR "illegal cycle" */ [10]struct {
+ x A4
+ }
+ A4 [10]A3
+
+ F1 func()
+ F2 func(x, y, z float32)
+ F3 func(x, y, x /* ERROR "redeclared" */ float32)
+ F4 func() (x, y, x /* ERROR "redeclared" */ float32)
+ F5 func(x int) (x /* ERROR "redeclared" */ float32)
+ F6 func(x ...int)
+
+ I1 interface{}
+ I2 interface {
+ m1()
+ }
+ I3 interface {
+ m1()
+ m1 /* ERROR "redeclared" */ ()
+ }
+ I4 interface {
+ m1(x, y, x /* ERROR "redeclared" */ float32)
+ m2() (x, y, x /* ERROR "redeclared" */ float32)
+ m3(x int) (x /* ERROR "redeclared" */ float32)
+ }
+ I5 interface {
+ m1(I5)
+ }
+ I6 interface {
+ S0 /* ERROR "not an interface" */
+ }
+ I7 interface {
+ I1
+ I1
+ }
+ I8 /* ERROR "illegal cycle" */ interface {
+ I8
+ }
+ I9 interface {
+ I10
+ }
+ I10 interface {
+ I11
+ }
+ I11 /* ERROR "illegal cycle" */ interface {
+ I9
+ }
+
+ C1 chan int
+ C2 <-chan int
+ C3 chan<- C3
+ C4 chan C5
+ C5 chan C6
+ C6 chan C4
+
+ M1 map[Last]string
+ M2 map[string]M2
+
+ Last int
+)
+
+// cycles in function/method declarations
+// (test cases for issue 5217 and variants)
+func f1(x f1 /* ERROR "not a type" */ ) {}
+func f2(x *f2 /* ERROR "not a type" */ ) {}
+func f3() (x f3 /* ERROR "not a type" */ ) { return }
+func f4() (x *f4 /* ERROR "not a type" */ ) { return }
+
+func (S0) m1(x S0 /* ERROR "field or method" */ .m1) {}
+func (S0) m2(x *S0 /* ERROR "field or method" */ .m2) {}
+func (S0) m3() (x S0 /* ERROR "field or method" */ .m3) { return }
+func (S0) m4() (x *S0 /* ERROR "field or method" */ .m4) { return }
+
+// interfaces may not have any blank methods
+type BlankI interface {
+ _ /* ERROR "invalid method name" */ ()
+ _ /* ERROR "invalid method name" */ (float32) int
+ m()
+}
+
+// non-interface types may have multiple blank methods
+type BlankT struct{}
+
+func (BlankT) _() {}
+func (BlankT) _(int) {}
+func (BlankT) _() int { return 0 }
+func (BlankT) _(int) int { return 0}
diff --git a/src/go/types/testdata/decls1.src b/src/go/types/testdata/decls1.src
new file mode 100644
index 0000000000..7855e461e2
--- /dev/null
+++ b/src/go/types/testdata/decls1.src
@@ -0,0 +1,144 @@
+// 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.
+
+// variable declarations
+
+package decls1
+
+import (
+ "math"
+)
+
+// Global variables without initialization
+var (
+ a, b bool
+ c byte
+ d uint8
+ r rune
+ i int
+ j, k, l int
+ x, y float32
+ xx, yy float64
+ u, v complex64
+ uu, vv complex128
+ s, t string
+ array []byte
+ iface interface{}
+
+ blank _ /* ERROR "cannot use _" */
+)
+
+// Global variables with initialization
+var (
+ s1 = i + j
+ s2 = i /* ERROR "mismatched types" */ + x
+ s3 = c + d
+ s4 = s + t
+ s5 = s /* ERROR "invalid operation" */ / t
+ s6 = array[t1]
+ s7 = array[x /* ERROR "integer" */]
+ s8 = &a
+ s10 = &42 /* ERROR "cannot take address" */
+ s11 = &v
+ s12 = -(u + *t11) / *&v
+ s13 = a /* ERROR "shifted operand" */ << d
+ s14 = i << j /* ERROR "must be unsigned" */
+ s18 = math.Pi * 10.0
+ s19 = s1 /* ERROR "cannot call" */ ()
+ s20 = f0 /* ERROR "no value" */ ()
+ s21 = f6(1, s1, i)
+ s22 = f6(1, s1, uu /* ERROR "cannot pass argument" */ )
+
+ t1 int = i + j
+ t2 int = i /* ERROR "mismatched types" */ + x
+ t3 int = c /* ERROR "cannot initialize" */ + d
+ t4 string = s + t
+ t5 string = s /* ERROR "invalid operation" */ / t
+ t6 byte = array[t1]
+ t7 byte = array[x /* ERROR "must be integer" */]
+ t8 *int = & /* ERROR "cannot initialize" */ a
+ t10 *int = &42 /* ERROR "cannot take address" */
+ t11 *complex64 = &v
+ t12 complex64 = -(u + *t11) / *&v
+ t13 int = a /* ERROR "shifted operand" */ << d
+ t14 int = i << j /* ERROR "must be unsigned" */
+ t15 math /* ERROR "not in selector" */
+ t16 math /* ERROR "not declared" */ .xxx
+ t17 math /* ERROR "not a type" */ .Pi
+ t18 float64 = math.Pi * 10.0
+ t19 int = t1 /* ERROR "cannot call" */ ()
+ t20 int = f0 /* ERROR "no value" */ ()
+ t21 int = a /* ERROR "cannot initialize" */
+)
+
+// Various more complex expressions
+var (
+ u1 = x /* ERROR "not an interface" */ .(int)
+ u2 = iface.([]int)
+ u3 = iface.(a /* ERROR "not a type" */ )
+ u4, ok = iface.(int)
+ u5, ok2, ok3 = iface /* ERROR "assignment count mismatch" */ .(int)
+)
+
+// Constant expression initializations
+var (
+ v1 = 1 /* ERROR "cannot convert" */ + "foo"
+ v2 = c + 255
+ v3 = c + 256 /* ERROR "overflows" */
+ v4 = r + 2147483647
+ v5 = r + 2147483648 /* ERROR "overflows" */
+ v6 = 42
+ v7 = v6 + 9223372036854775807
+ v8 = v6 + 9223372036854775808 /* ERROR "overflows" */
+ v9 = i + 1 << 10
+ v10 byte = 1024 /* ERROR "overflows" */
+ v11 = xx/yy*yy - xx
+ v12 = true && false
+ v13 = nil /* ERROR "use of untyped nil" */
+)
+
+// Multiple assignment expressions
+var (
+ m1a, m1b = 1, 2
+ m2a, m2b, m2c /* ERROR "missing init expr for m2c" */ = 1, 2
+ m3a, m3b = 1, 2, 3 /* ERROR "extra init expr 3" */
+)
+
+func _() {
+ var (
+ m1a, m1b = 1, 2
+ m2a, m2b, m2c /* ERROR "missing init expr for m2c" */ = 1, 2
+ m3a, m3b = 1, 2, 3 /* ERROR "extra init expr 3" */
+ )
+
+ _, _ = m1a, m1b
+ _, _, _ = m2a, m2b, m2c
+ _, _ = m3a, m3b
+}
+
+// Declaration of parameters and results
+func f0() {}
+func f1(a /* ERROR "not a type" */) {}
+func f2(a, b, c d /* ERROR "not a type" */) {}
+
+func f3() int { return 0 }
+func f4() a /* ERROR "not a type" */ { return 0 }
+func f5() (a, b, c d /* ERROR "not a type" */) { return }
+
+func f6(a, b, c int) complex128 { return 0 }
+
+// Declaration of receivers
+type T struct{}
+
+func (T) m0() {}
+func (*T) m1() {}
+func (x T) m2() {}
+func (x *T) m3() {}
+
+// Initialization functions
+func init() {}
+func /* ERROR "no arguments and no return values" */ init(int) {}
+func /* ERROR "no arguments and no return values" */ init() int { return 0 }
+func /* ERROR "no arguments and no return values" */ init(int) int { return 0 }
+func (T) init(int) int { return 0 }
diff --git a/src/go/types/testdata/decls2a.src b/src/go/types/testdata/decls2a.src
new file mode 100644
index 0000000000..bdbecd9dbb
--- /dev/null
+++ b/src/go/types/testdata/decls2a.src
@@ -0,0 +1,111 @@
+// 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.
+
+// method declarations
+
+package decls2
+
+import "time"
+import "unsafe"
+
+// T1 declared before its methods.
+type T1 struct{
+ f int
+}
+
+func (T1) m() {}
+func (T1) m /* ERROR "already declared" */ () {}
+func (x *T1) f /* ERROR "field and method" */ () {}
+
+// Conflict between embedded field and method name,
+// with the embedded field being a basic type.
+type T1b struct {
+ int
+}
+
+func (T1b) int /* ERROR "field and method" */ () {}
+
+type T1c struct {
+ time.Time
+}
+
+func (T1c) Time /* ERROR "field and method" */ () int { return 0 }
+
+// Disabled for now: LookupFieldOrMethod will find Pointer even though
+// it's double-declared (it would cost extra in the common case to verify
+// this). But the MethodSet computation will not find it due to the name
+// collision caused by the double-declaration, leading to an internal
+// inconsistency while we are verifying one computation against the other.
+// var _ = T1c{}.Pointer
+
+// T2's method declared before the type.
+func (*T2) f /* ERROR "field and method" */ () {}
+
+type T2 struct {
+ f int
+}
+
+// Methods declared without a declared type.
+func (undeclared /* ERROR "undeclared" */) m() {}
+func (x *undeclared /* ERROR "undeclared" */) m() {}
+
+func (pi /* ERROR "not a type" */) m1() {}
+func (x pi /* ERROR "not a type" */) m2() {}
+func (x *pi /* ERROR "not a type" */ ) m3() {}
+
+// Blank types.
+type _ struct { m int }
+type _ struct { m int }
+
+func (_ /* ERROR "cannot use _" */) m() {}
+func m(_ /* ERROR "cannot use _" */) {}
+
+// Methods with receiver base type declared in another file.
+func (T3) m1() {}
+func (*T3) m2() {}
+func (x T3) m3() {}
+func (x *T3) f /* ERROR "field and method" */ () {}
+
+// Methods of non-struct type.
+type T4 func()
+
+func (self T4) m() func() { return self }
+
+// Methods associated with an interface.
+type T5 interface {
+ m() int
+}
+
+func (T5 /* ERROR "invalid receiver" */ ) m1() {}
+func (T5 /* ERROR "invalid receiver" */ ) m2() {}
+
+// Methods associated with a named pointer type.
+type ptr *int
+func (ptr /* ERROR "invalid receiver" */ ) _() {}
+func (* /* ERROR "invalid receiver" */ ptr) _() {}
+
+// Methods with zero or multiple receivers.
+func ( /* ERROR "missing receiver" */ ) _() {}
+func (T3, * /* ERROR "exactly one receiver" */ T3) _() {}
+func (T3, T3, T3 /* ERROR "exactly one receiver" */ ) _() {}
+func (a, b /* ERROR "exactly one receiver" */ T3) _() {}
+func (a, b, c /* ERROR "exactly one receiver" */ T3) _() {}
+
+// Methods associated with non-local or unnamed types.
+func (int /* ERROR "invalid receiver" */ ) m() {}
+func ([ /* ERROR "invalid receiver" */ ]int) m() {}
+func (time /* ERROR "invalid receiver" */ .Time) m() {}
+func (* /* ERROR "invalid receiver" */ time.Time) m() {}
+func (x /* ERROR "invalid receiver" */ interface{}) m() {}
+
+// Unsafe.Pointer is treated like a pointer when used as receiver type.
+type UP unsafe.Pointer
+func (UP /* ERROR "invalid" */ ) m1() {}
+func (* /* ERROR "invalid" */ UP) m2() {}
+
+// Double declarations across package files
+const c_double = 0
+type t_double int
+var v_double int
+func f_double() {}
diff --git a/src/go/types/testdata/decls2b.src b/src/go/types/testdata/decls2b.src
new file mode 100644
index 0000000000..e7bc394762
--- /dev/null
+++ b/src/go/types/testdata/decls2b.src
@@ -0,0 +1,65 @@
+// 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.
+
+// method declarations
+
+package decls2
+
+import "io"
+
+const pi = 3.1415
+
+func (T1) m /* ERROR "already declared" */ () {}
+func (T2) m(io.Writer) {}
+
+type T3 struct {
+ f *T3
+}
+
+type T6 struct {
+ x int
+}
+
+func (t *T6) m1() int {
+ return t.x
+}
+
+func f() {
+ var t *T6
+ t.m1()
+}
+
+// Double declarations across package files
+const c_double /* ERROR "redeclared" */ = 0
+type t_double /* ERROR "redeclared" */ int
+var v_double /* ERROR "redeclared" */ int
+func f_double /* ERROR "redeclared" */ () {}
+
+// Blank methods need to be type-checked.
+// Verify by checking that errors are reported.
+func (T /* ERROR "undeclared" */ ) _() {}
+func (T1) _(undeclared /* ERROR "undeclared" */ ) {}
+func (T1) _() int { return "foo" /* ERROR "cannot convert" */ }
+
+// Methods with undeclared receiver type can still be checked.
+// Verify by checking that errors are reported.
+func (Foo /* ERROR "undeclared" */ ) m() {}
+func (Foo /* ERROR "undeclared" */ ) m(undeclared /* ERROR "undeclared" */ ) {}
+func (Foo /* ERROR "undeclared" */ ) m() int { return "foo" /* ERROR "cannot convert" */ }
+
+func (Foo /* ERROR "undeclared" */ ) _() {}
+func (Foo /* ERROR "undeclared" */ ) _(undeclared /* ERROR "undeclared" */ ) {}
+func (Foo /* ERROR "undeclared" */ ) _() int { return "foo" /* ERROR "cannot convert" */ }
+
+// Receiver declarations are regular parameter lists;
+// receiver types may use parentheses, and the list
+// may have a trailing comma.
+type T7 struct {}
+
+func (T7) m1() {}
+func ((T7)) m2() {}
+func ((*T7)) m3() {}
+func (x *(T7),) m4() {}
+func (x (*(T7)),) m5() {}
+func (x ((*((T7)))),) m6() {}
diff --git a/src/go/types/testdata/decls3.src b/src/go/types/testdata/decls3.src
new file mode 100644
index 0000000000..80d2bc8ff8
--- /dev/null
+++ b/src/go/types/testdata/decls3.src
@@ -0,0 +1,309 @@
+// 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.
+
+// embedded types
+
+package decls3
+
+import "unsafe"
+import "fmt"
+
+// fields with the same name at the same level cancel each other out
+
+func _() {
+ type (
+ T1 struct { X int }
+ T2 struct { X int }
+ T3 struct { T1; T2 } // X is embedded twice at the same level via T1->X, T2->X
+ )
+
+ var t T3
+ _ = t /* ERROR "ambiguous selector" */ .X
+}
+
+func _() {
+ type (
+ T1 struct { X int }
+ T2 struct { T1 }
+ T3 struct { T1 }
+ T4 struct { T2; T3 } // X is embedded twice at the same level via T2->T1->X, T3->T1->X
+ )
+
+ var t T4
+ _ = t /* ERROR "ambiguous selector" */ .X
+}
+
+func issue4355() {
+ type (
+ T1 struct {X int}
+ T2 struct {T1}
+ T3 struct {T2}
+ T4 struct {T2}
+ T5 struct {T3; T4} // X is embedded twice at the same level via T3->T2->T1->X, T4->T2->T1->X
+ )
+
+ var t T5
+ _ = t /* ERROR "ambiguous selector" */ .X
+}
+
+func _() {
+ type State int
+ type A struct{ State }
+ type B struct{ fmt.State }
+ type T struct{ A; B }
+
+ var t T
+ _ = t /* ERROR "ambiguous selector" */ .State
+}
+
+// Embedded fields can be predeclared types.
+
+func _() {
+ type T0 struct{
+ int
+ float32
+ f int
+ }
+ var x T0
+ _ = x.int
+ _ = x.float32
+ _ = x.f
+
+ type T1 struct{
+ T0
+ }
+ var y T1
+ _ = y.int
+ _ = y.float32
+ _ = y.f
+}
+
+// Restrictions on embedded field types.
+
+func _() {
+ type I1 interface{}
+ type I2 interface{}
+ type P1 *int
+ type P2 *int
+ type UP unsafe.Pointer
+
+ type T1 struct {
+ I1
+ * /* ERROR "cannot be a pointer to an interface" */ I2
+ * /* ERROR "cannot be a pointer to an interface" */ error
+ P1 /* ERROR "cannot be a pointer" */
+ * /* ERROR "cannot be a pointer" */ P2
+ }
+
+ // unsafe.Pointers are treated like regular pointers when embedded
+ type T2 struct {
+ unsafe /* ERROR "cannot be unsafe.Pointer" */ .Pointer
+ */* ERROR "cannot be unsafe.Pointer" */ unsafe.Pointer
+ UP /* ERROR "cannot be unsafe.Pointer" */
+ * /* ERROR "cannot be unsafe.Pointer" */ UP
+ }
+}
+
+// Named types that are pointers.
+
+type S struct{ x int }
+func (*S) m() {}
+type P *S
+
+func _() {
+ var s *S
+ _ = s.x
+ _ = s.m
+
+ var p P
+ _ = p.x
+ _ = p /* ERROR "no field or method" */ .m
+ _ = P /* ERROR "no field or method" */ .m
+}
+
+// Borrowed from the FieldByName test cases in reflect/all_test.go.
+
+type D1 struct {
+ d int
+}
+type D2 struct {
+ d int
+}
+
+type S0 struct {
+ A, B, C int
+ D1
+ D2
+}
+
+type S1 struct {
+ B int
+ S0
+}
+
+type S2 struct {
+ A int
+ *S1
+}
+
+type S1x struct {
+ S1
+}
+
+type S1y struct {
+ S1
+}
+
+type S3 struct {
+ S1x
+ S2
+ D, E int
+ *S1y
+}
+
+type S4 struct {
+ *S4
+ A int
+}
+
+// The X in S6 and S7 annihilate, but they also block the X in S8.S9.
+type S5 struct {
+ S6
+ S7
+ S8
+}
+
+type S6 struct {
+ X int
+}
+
+type S7 S6
+
+type S8 struct {
+ S9
+}
+
+type S9 struct {
+ X int
+ Y int
+}
+
+// The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9.
+type S10 struct {
+ S11
+ S12
+ S13
+}
+
+type S11 struct {
+ S6
+}
+
+type S12 struct {
+ S6
+}
+
+type S13 struct {
+ S8
+}
+
+func _() {
+ _ = struct /* ERROR "no field or method" */ {}{}.Foo
+ _ = S0{}.A
+ _ = S0 /* ERROR "no field or method" */ {}.D
+ _ = S1{}.A
+ _ = S1{}.B
+ _ = S1{}.S0
+ _ = S1{}.C
+ _ = S2{}.A
+ _ = S2{}.S1
+ _ = S2{}.B
+ _ = S2{}.C
+ _ = S2 /* ERROR "no field or method" */ {}.D
+ _ = S3 /* ERROR "ambiguous selector" */ {}.S1
+ _ = S3{}.A
+ _ = S3 /* ERROR "ambiguous selector" */ {}.B
+ _ = S3{}.D
+ _ = S3{}.E
+ _ = S4{}.A
+ _ = S4 /* ERROR "no field or method" */ {}.B
+ _ = S5 /* ERROR "ambiguous selector" */ {}.X
+ _ = S5{}.Y
+ _ = S10 /* ERROR "ambiguous selector" */ {}.X
+ _ = S10{}.Y
+}
+
+// Borrowed from the FieldByName benchmark in reflect/all_test.go.
+
+type R0 struct {
+ *R1
+ *R2
+ *R3
+ *R4
+}
+
+type R1 struct {
+ *R5
+ *R6
+ *R7
+ *R8
+}
+
+type R2 R1
+type R3 R1
+type R4 R1
+
+type R5 struct {
+ *R9
+ *R10
+ *R11
+ *R12
+}
+
+type R6 R5
+type R7 R5
+type R8 R5
+
+type R9 struct {
+ *R13
+ *R14
+ *R15
+ *R16
+}
+
+type R10 R9
+type R11 R9
+type R12 R9
+
+type R13 struct {
+ *R17
+ *R18
+ *R19
+ *R20
+}
+
+type R14 R13
+type R15 R13
+type R16 R13
+
+type R17 struct {
+ *R21
+ *R22
+ *R23
+ *R24
+}
+
+type R18 R17
+type R19 R17
+type R20 R17
+
+type R21 struct {
+ X int
+}
+
+type R22 R21
+type R23 R21
+type R24 R21
+
+var _ = R0 /* ERROR "ambiguous selector" */ {}.X
\ No newline at end of file
diff --git a/src/go/types/testdata/errors.src b/src/go/types/testdata/errors.src
new file mode 100644
index 0000000000..45bd45a13a
--- /dev/null
+++ b/src/go/types/testdata/errors.src
@@ -0,0 +1,55 @@
+// 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 errors
+
+// Testing precise operand formatting in error messages
+// (matching messages are regular expressions, hence the \'s).
+func f(x int, m map[string]int) {
+ // no values
+ _ = f /* ERROR "f\(0, m\) \(no value\) used as value" */ (0, m)
+
+ // built-ins
+ _ = println /* ERROR "println \(built-in\) must be called" */
+
+ // types
+ _ = complex128 /* ERROR "complex128 \(type\) is not an expression" */
+
+ // constants
+ const c1 = 991
+ const c2 float32 = 0.5
+ 0 /* ERROR "0 \(untyped int constant\) is not used" */
+ c1 /* ERROR "c1 \(untyped int constant 991\) is not used" */
+ c2 /* ERROR "c2 \(constant 1/2 of type float32\) is not used" */
+ c1 /* ERROR "c1 \+ c2 \(constant 1983/2 of type float32\) is not used" */ + c2
+
+ // variables
+ x /* ERROR "x \(variable of type int\) is not used" */
+
+ // values
+ x /* ERROR "x != x \(untyped bool value\) is not used" */ != x
+ x /* ERROR "x \+ x \(value of type int\) is not used" */ + x
+
+ // value, ok's
+ const s = "foo"
+ m /* ERROR "m\[s\] \(map index expression of type int\) is not used" */ [s]
+}
+
+// Valid ERROR comments can have a variety of forms.
+func _() {
+ 0 /* ERROR "0 .* is not used" */
+ 0 /* ERROR 0 .* is not used */
+ 0 // ERROR "0 .* is not used"
+ 0 // ERROR 0 .* is not used
+}
+
+// Don't report spurious errors as a consequence of earlier errors.
+// Add more tests as needed.
+func _() {
+ if err := foo /* ERROR undeclared */ (); err != nil /* no error here */ {}
+}
+
+// Use unqualified names for package-local objects.
+type T struct{}
+var _ int = T /* ERROR value of type T */ {} // use T in error message rather then errors.T
diff --git a/src/go/types/testdata/expr0.src b/src/go/types/testdata/expr0.src
new file mode 100644
index 0000000000..5afb5d738e
--- /dev/null
+++ b/src/go/types/testdata/expr0.src
@@ -0,0 +1,168 @@
+// 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.
+
+// unary expressions
+
+package expr0
+
+type mybool bool
+
+var (
+ // bool
+ b0 = true
+ b1 bool = b0
+ b2 = !true
+ b3 = !b1
+ b4 bool = !true
+ b5 bool = !b4
+ b6 = +b0 /* ERROR "not defined" */
+ b7 = -b0 /* ERROR "not defined" */
+ b8 = ^b0 /* ERROR "not defined" */
+ b9 = *b0 /* ERROR "cannot indirect" */
+ b10 = &true /* ERROR "cannot take address" */
+ b11 = &b0
+ b12 = <-b0 /* ERROR "cannot receive" */
+ b13 = & & /* ERROR "cannot take address" */ b0
+
+ // int
+ i0 = 1
+ i1 int = i0
+ i2 = +1
+ i3 = +i0
+ i4 int = +1
+ i5 int = +i4
+ i6 = -1
+ i7 = -i0
+ i8 int = -1
+ i9 int = -i4
+ i10 = !i0 /* ERROR "not defined" */
+ i11 = ^1
+ i12 = ^i0
+ i13 int = ^1
+ i14 int = ^i4
+ i15 = *i0 /* ERROR "cannot indirect" */
+ i16 = &i0
+ i17 = *i16
+ i18 = <-i16 /* ERROR "cannot receive" */
+
+ // uint
+ u0 = uint(1)
+ u1 uint = u0
+ u2 = +1
+ u3 = +u0
+ u4 uint = +1
+ u5 uint = +u4
+ u6 = -1
+ u7 = -u0
+ u8 uint = - /* ERROR "overflows" */ 1
+ u9 uint = -u4
+ u10 = !u0 /* ERROR "not defined" */
+ u11 = ^1
+ u12 = ^i0
+ u13 uint = ^ /* ERROR "overflows" */ 1
+ u14 uint = ^u4
+ u15 = *u0 /* ERROR "cannot indirect" */
+ u16 = &u0
+ u17 = *u16
+ u18 = <-u16 /* ERROR "cannot receive" */
+ u19 = ^uint(0)
+
+ // float64
+ f0 = float64(1)
+ f1 float64 = f0
+ f2 = +1
+ f3 = +f0
+ f4 float64 = +1
+ f5 float64 = +f4
+ f6 = -1
+ f7 = -f0
+ f8 float64 = -1
+ f9 float64 = -f4
+ f10 = !f0 /* ERROR "not defined" */
+ f11 = ^1
+ f12 = ^i0
+ f13 float64 = ^1
+ f14 float64 = ^f4 /* ERROR "not defined" */
+ f15 = *f0 /* ERROR "cannot indirect" */
+ f16 = &f0
+ f17 = *u16
+ f18 = <-u16 /* ERROR "cannot receive" */
+
+ // complex128
+ c0 = complex128(1)
+ c1 complex128 = c0
+ c2 = +1
+ c3 = +c0
+ c4 complex128 = +1
+ c5 complex128 = +c4
+ c6 = -1
+ c7 = -c0
+ c8 complex128 = -1
+ c9 complex128 = -c4
+ c10 = !c0 /* ERROR "not defined" */
+ c11 = ^1
+ c12 = ^i0
+ c13 complex128 = ^1
+ c14 complex128 = ^c4 /* ERROR "not defined" */
+ c15 = *c0 /* ERROR "cannot indirect" */
+ c16 = &c0
+ c17 = *u16
+ c18 = <-u16 /* ERROR "cannot receive" */
+
+ // string
+ s0 = "foo"
+ s1 = +"foo" /* ERROR "not defined" */
+ s2 = -s0 /* ERROR "not defined" */
+ s3 = !s0 /* ERROR "not defined" */
+ s4 = ^s0 /* ERROR "not defined" */
+ s5 = *s4
+ s6 = &s4
+ s7 = *s6
+ s8 = <-s7
+
+ // channel
+ ch chan int
+ rc <-chan float64
+ sc chan <- string
+ ch0 = +ch /* ERROR "not defined" */
+ ch1 = -ch /* ERROR "not defined" */
+ ch2 = !ch /* ERROR "not defined" */
+ ch3 = ^ch /* ERROR "not defined" */
+ ch4 = *ch /* ERROR "cannot indirect" */
+ ch5 = &ch
+ ch6 = *ch5
+ ch7 = <-ch
+ ch8 = <-rc
+ ch9 = <-sc /* ERROR "cannot receive" */
+ ch10, ok = <-ch
+ // ok is of type bool
+ ch11, myok = <-ch
+ _ mybool = myok /* ERROR "cannot initialize" */
+)
+
+// address of composite literals
+type T struct{x, y int}
+
+func f() T { return T{} }
+
+var (
+ _ = &T{1, 2}
+ _ = &[...]int{}
+ _ = &[]int{}
+ _ = &[]int{}
+ _ = &map[string]T{}
+ _ = &(T{1, 2})
+ _ = &((((T{1, 2}))))
+ _ = &f /* ERROR "cannot take address" */ ()
+)
+
+// recursive pointer types
+type P *P
+
+var (
+ p1 P = new(P)
+ p2 P = *p1
+ p3 P = &p2
+)
+
diff --git a/src/log/syslog/syslog_plan9.go b/src/go/types/testdata/expr1.src
similarity index 51%
rename from src/log/syslog/syslog_plan9.go
rename to src/go/types/testdata/expr1.src
index 0c05f6f83c..8ef0aed6d2 100644
--- a/src/log/syslog/syslog_plan9.go
+++ b/src/go/types/testdata/expr1.src
@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package syslog provides a simple interface to the system log service.
-package syslog
+// binary expressions
-// BUG(akumar): This package is not implemented on Plan 9 yet.
+package expr1
diff --git a/src/go/types/testdata/expr2.src b/src/go/types/testdata/expr2.src
new file mode 100644
index 0000000000..31dc5f021c
--- /dev/null
+++ b/src/go/types/testdata/expr2.src
@@ -0,0 +1,247 @@
+// 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.
+
+// comparisons
+
+package expr2
+
+func _bool() {
+ const t = true == true
+ const f = true == false
+ _ = t /* ERROR "cannot compare" */ < f
+ _ = 0 /* ERROR "cannot convert" */ == t
+ var b bool
+ var x, y float32
+ b = x < y
+ _ = b
+ _ = struct{b bool}{x < y}
+}
+
+// corner cases
+var (
+ v0 = nil /* ERROR "cannot compare" */ == nil
+)
+
+func arrays() {
+ // basics
+ var a, b [10]int
+ _ = a == b
+ _ = a != b
+ _ = a /* ERROR < not defined */ < b
+ _ = a == nil /* ERROR cannot convert */
+
+ type C [10]int
+ var c C
+ _ = a == c
+
+ type D [10]int
+ var d D
+ _ = c /* ERROR mismatched types */ == d
+
+ var e [10]func() int
+ _ = e /* ERROR == not defined */ == e
+}
+
+func structs() {
+ // basics
+ var s, t struct {
+ x int
+ a [10]float32
+ _ bool
+ }
+ _ = s == t
+ _ = s != t
+ _ = s /* ERROR < not defined */ < t
+ _ = s == nil /* ERROR cannot convert */
+
+ type S struct {
+ x int
+ a [10]float32
+ _ bool
+ }
+ type T struct {
+ x int
+ a [10]float32
+ _ bool
+ }
+ var ss S
+ var tt T
+ _ = s == ss
+ _ = ss /* ERROR mismatched types */ == tt
+
+ var u struct {
+ x int
+ a [10]map[string]int
+ }
+ _ = u /* ERROR cannot compare */ == u
+}
+
+func pointers() {
+ // nil
+ _ = nil /* ERROR == not defined */ == nil
+ _ = nil /* ERROR != not defined */ != nil
+ _ = nil /* ERROR < not defined */ < nil
+ _ = nil /* ERROR <= not defined */ <= nil
+ _ = nil /* ERROR > not defined */ > nil
+ _ = nil /* ERROR >= not defined */ >= nil
+
+ // basics
+ var p, q *int
+ _ = p == q
+ _ = p != q
+
+ _ = p == nil
+ _ = p != nil
+ _ = nil == q
+ _ = nil != q
+
+ _ = p /* ERROR < not defined */ < q
+ _ = p /* ERROR <= not defined */ <= q
+ _ = p /* ERROR > not defined */ > q
+ _ = p /* ERROR >= not defined */ >= q
+
+ // various element types
+ type (
+ S1 struct{}
+ S2 struct{}
+ P1 *S1
+ P2 *S2
+ )
+ var (
+ ps1 *S1
+ ps2 *S2
+ p1 P1
+ p2 P2
+ )
+ _ = ps1 == ps1
+ _ = ps1 /* ERROR mismatched types */ == ps2
+ _ = ps2 /* ERROR mismatched types */ == ps1
+
+ _ = p1 == p1
+ _ = p1 /* ERROR mismatched types */ == p2
+
+ _ = p1 == ps1
+}
+
+func channels() {
+ // basics
+ var c, d chan int
+ _ = c == d
+ _ = c != d
+ _ = c == nil
+ _ = c /* ERROR < not defined */ < d
+
+ // various element types (named types)
+ type (
+ C1 chan int
+ C1r <-chan int
+ C1s chan<- int
+ C2 chan float32
+ )
+ var (
+ c1 C1
+ c1r C1r
+ c1s C1s
+ c1a chan int
+ c2 C2
+ )
+ _ = c1 == c1
+ _ = c1 /* ERROR mismatched types */ == c1r
+ _ = c1 /* ERROR mismatched types */ == c1s
+ _ = c1r /* ERROR mismatched types */ == c1s
+ _ = c1 == c1a
+ _ = c1a == c1
+ _ = c1 /* ERROR mismatched types */ == c2
+ _ = c1a /* ERROR mismatched types */ == c2
+
+ // various element types (unnamed types)
+ var (
+ d1 chan int
+ d1r <-chan int
+ d1s chan<- int
+ d1a chan<- int
+ d2 chan float32
+ )
+ _ = d1 == d1
+ _ = d1 == d1r
+ _ = d1 == d1s
+ _ = d1r /* ERROR mismatched types */ == d1s
+ _ = d1 == d1a
+ _ = d1a == d1
+ _ = d1 /* ERROR mismatched types */ == d2
+ _ = d1a /* ERROR mismatched types */ == d2
+}
+
+// for interfaces test
+type S1 struct{}
+type S11 struct{}
+type S2 struct{}
+func (*S1) m() int
+func (*S11) m() int
+func (*S11) n()
+func (*S2) m() float32
+
+func interfaces() {
+ // basics
+ var i, j interface{ m() int }
+ _ = i == j
+ _ = i != j
+ _ = i == nil
+ _ = i /* ERROR < not defined */ < j
+
+ // various interfaces
+ var ii interface { m() int; n() }
+ var k interface { m() float32 }
+ _ = i == ii
+ _ = i /* ERROR mismatched types */ == k
+
+ // interfaces vs values
+ var s1 S1
+ var s11 S11
+ var s2 S2
+
+ _ = i == 0 /* ERROR cannot convert */
+ _ = i /* ERROR mismatched types */ == s1
+ _ = i == &s1
+ _ = i == &s11
+
+ _ = i /* ERROR mismatched types */ == s2
+ _ = i /* ERROR mismatched types */ == &s2
+}
+
+func slices() {
+ // basics
+ var s []int
+ _ = s == nil
+ _ = s != nil
+ _ = s /* ERROR < not defined */ < nil
+
+ // slices are not otherwise comparable
+ _ = s /* ERROR == not defined */ == s
+ _ = s /* ERROR < not defined */ < s
+}
+
+func maps() {
+ // basics
+ var m map[string]int
+ _ = m == nil
+ _ = m != nil
+ _ = m /* ERROR < not defined */ < nil
+
+ // maps are not otherwise comparable
+ _ = m /* ERROR == not defined */ == m
+ _ = m /* ERROR < not defined */ < m
+}
+
+func funcs() {
+ // basics
+ var f func(int) float32
+ _ = f == nil
+ _ = f != nil
+ _ = f /* ERROR < not defined */ < nil
+
+ // funcs are not otherwise comparable
+ _ = f /* ERROR == not defined */ == f
+ _ = f /* ERROR < not defined */ < f
+}
diff --git a/src/go/types/testdata/expr3.src b/src/go/types/testdata/expr3.src
new file mode 100644
index 0000000000..57720954bd
--- /dev/null
+++ b/src/go/types/testdata/expr3.src
@@ -0,0 +1,534 @@
+// 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 expr3
+
+import "time"
+
+func indexes() {
+ _ = 1 /* ERROR "cannot index" */ [0]
+ _ = indexes /* ERROR "cannot index" */ [0]
+ _ = ( /* ERROR "cannot slice" */ 12 + 3)[1:2]
+
+ var a [10]int
+ _ = a[true /* ERROR "cannot convert" */ ]
+ _ = a["foo" /* ERROR "cannot convert" */ ]
+ _ = a[1.1 /* ERROR "truncated" */ ]
+ _ = a[1.0]
+ _ = a[- /* ERROR "negative" */ 1]
+ _ = a[- /* ERROR "negative" */ 1 :]
+ _ = a[: - /* ERROR "negative" */ 1]
+ _ = a[: /* ERROR "2nd index required" */ : /* ERROR "3rd index required" */ ]
+ _ = a[0: /* ERROR "2nd index required" */ : /* ERROR "3rd index required" */ ]
+ _ = a[0: /* ERROR "2nd index required" */ :10]
+ _ = a[:10:10]
+
+ var a0 int
+ a0 = a[0]
+ _ = a0
+ var a1 int32
+ a1 = a /* ERROR "cannot assign" */ [1]
+ _ = a1
+
+ _ = a[9]
+ _ = a[10 /* ERROR "index .* out of bounds" */ ]
+ _ = a[1 /* ERROR "overflows" */ <<100]
+ _ = a[10:]
+ _ = a[:10]
+ _ = a[10:10]
+ _ = a[11 /* ERROR "index .* out of bounds" */ :]
+ _ = a[: 11 /* ERROR "index .* out of bounds" */ ]
+ _ = a[: 1 /* ERROR "overflows" */ <<100]
+ _ = a[:10:10]
+ _ = a[:11 /* ERROR "index .* out of bounds" */ :10]
+ _ = a[:10:11 /* ERROR "index .* out of bounds" */ ]
+ _ = a[10:0:10] /* ERROR "invalid slice indices" */
+ _ = a[0:10:0] /* ERROR "invalid slice indices" */
+ _ = a[10:0:0] /* ERROR "invalid slice indices" */
+ _ = &a /* ERROR "cannot take address" */ [:10]
+
+ pa := &a
+ _ = pa[9]
+ _ = pa[10 /* ERROR "index .* out of bounds" */ ]
+ _ = pa[1 /* ERROR "overflows" */ <<100]
+ _ = pa[10:]
+ _ = pa[:10]
+ _ = pa[10:10]
+ _ = pa[11 /* ERROR "index .* out of bounds" */ :]
+ _ = pa[: 11 /* ERROR "index .* out of bounds" */ ]
+ _ = pa[: 1 /* ERROR "overflows" */ <<100]
+ _ = pa[:10:10]
+ _ = pa[:11 /* ERROR "index .* out of bounds" */ :10]
+ _ = pa[:10:11 /* ERROR "index .* out of bounds" */ ]
+ _ = pa[10:0:10] /* ERROR "invalid slice indices" */
+ _ = pa[0:10:0] /* ERROR "invalid slice indices" */
+ _ = pa[10:0:0] /* ERROR "invalid slice indices" */
+ _ = &pa /* ERROR "cannot take address" */ [:10]
+
+ var b [0]int
+ _ = b[0 /* ERROR "index .* out of bounds" */ ]
+ _ = b[:]
+ _ = b[0:]
+ _ = b[:0]
+ _ = b[0:0]
+ _ = b[0:0:0]
+ _ = b[1 /* ERROR "index .* out of bounds" */ :0:0]
+
+ var s []int
+ _ = s[- /* ERROR "negative" */ 1]
+ _ = s[- /* ERROR "negative" */ 1 :]
+ _ = s[: - /* ERROR "negative" */ 1]
+ _ = s[0]
+ _ = s[1:2]
+ _ = s[2:1] /* ERROR "invalid slice indices" */
+ _ = s[2:]
+ _ = s[: 1 /* ERROR "overflows" */ <<100]
+ _ = s[1 /* ERROR "overflows" */ <<100 :]
+ _ = s[1 /* ERROR "overflows" */ <<100 : 1 /* ERROR "overflows" */ <<100]
+ _ = s[: /* ERROR "2nd index required" */ : /* ERROR "3rd index required" */ ]
+ _ = s[:10:10]
+ _ = s[10:0:10] /* ERROR "invalid slice indices" */
+ _ = s[0:10:0] /* ERROR "invalid slice indices" */
+ _ = s[10:0:0] /* ERROR "invalid slice indices" */
+ _ = &s /* ERROR "cannot take address" */ [:10]
+
+ var m map[string]int
+ _ = m[0 /* ERROR "cannot convert" */ ]
+ _ = m /* ERROR "cannot slice" */ ["foo" : "bar"]
+ _ = m["foo"]
+ // ok is of type bool
+ type mybool bool
+ var ok mybool
+ _, ok = m["bar"]
+ _ = ok
+
+ var t string
+ _ = t[- /* ERROR "negative" */ 1]
+ _ = t[- /* ERROR "negative" */ 1 :]
+ _ = t[: - /* ERROR "negative" */ 1]
+ _ = t /* ERROR "3-index slice of string" */ [1:2:3]
+ _ = "foo" /* ERROR "3-index slice of string" */ [1:2:3]
+ var t0 byte
+ t0 = t[0]
+ _ = t0
+ var t1 rune
+ t1 = t /* ERROR "cannot assign" */ [2]
+ _ = t1
+ _ = ("foo" + "bar")[5]
+ _ = ("foo" + "bar")[6 /* ERROR "index .* out of bounds" */ ]
+
+ const c = "foo"
+ _ = c[- /* ERROR "negative" */ 1]
+ _ = c[- /* ERROR "negative" */ 1 :]
+ _ = c[: - /* ERROR "negative" */ 1]
+ var c0 byte
+ c0 = c[0]
+ _ = c0
+ var c2 float32
+ c2 = c /* ERROR "cannot assign" */ [2]
+ _ = c[3 /* ERROR "index .* out of bounds" */ ]
+ _ = ""[0 /* ERROR "index .* out of bounds" */ ]
+ _ = c2
+
+ _ = s[1<<30] // no compile-time error here
+
+ // issue 4913
+ type mystring string
+ var ss string
+ var ms mystring
+ var i, j int
+ ss = "foo"[1:2]
+ ss = "foo"[i:j]
+ ms = "foo" /* ERROR "cannot assign" */ [1:2]
+ ms = "foo" /* ERROR "cannot assign" */ [i:j]
+ _, _ = ss, ms
+}
+
+type T struct {
+ x int
+ y func()
+}
+
+func (*T) m() {}
+
+func method_expressions() {
+ _ = T /* ERROR "no field or method" */ .a
+ _ = T /* ERROR "has no method" */ .x
+ _ = T /* ERROR "not in method set" */ .m
+ _ = (*T).m
+
+ var f func(*T) = T /* ERROR "not in method set" */ .m
+ var g func(*T) = (*T).m
+ _, _ = f, g
+
+ _ = T /* ERROR "has no method" */ .y
+ _ = ( /* ERROR "has no method" */ *T).y
+}
+
+func struct_literals() {
+ type T0 struct {
+ a, b, c int
+ }
+
+ type T1 struct {
+ T0
+ a, b int
+ u float64
+ s string
+ }
+
+ // keyed elements
+ _ = T1{}
+ _ = T1{a: 0, 1 /* ERROR "mixture of .* elements" */ }
+ _ = T1{aa /* ERROR "unknown field" */ : 0}
+ _ = T1{1 /* ERROR "invalid field name" */ : 0}
+ _ = T1{a: 0, s: "foo", u: 0, a /* ERROR "duplicate field" */: 10}
+ _ = T1{a: "foo" /* ERROR "cannot convert" */ }
+ _ = T1{c /* ERROR "unknown field" */ : 0}
+ _ = T1{T0: { /* ERROR "missing type" */ }} // struct literal element type may not be elided
+ _ = T1{T0: T0{}}
+ _ = T1{T0 /* ERROR "invalid field name" */ .a: 0}
+
+ // unkeyed elements
+ _ = T0{1, 2, 3}
+ _ = T0{1, b /* ERROR "mixture" */ : 2, 3}
+ _ = T0{1, 2} /* ERROR "too few values" */
+ _ = T0{1, 2, 3, 4 /* ERROR "too many values" */ }
+ _ = T0{1, "foo" /* ERROR "cannot convert" */, 3.4 /* ERROR "truncated" */}
+
+ // invalid type
+ type P *struct{
+ x int
+ }
+ _ = P /* ERROR "invalid composite literal type" */ {}
+
+ // unexported fields
+ _ = time.Time{}
+ _ = time.Time{sec /* ERROR "unknown field" */ : 0}
+ _ = time.Time{
+ 0 /* ERROR implicit assignment to unexported field sec in time.Time literal */,
+ 0 /* ERROR implicit assignment */ ,
+ nil /* ERROR implicit assignment */ ,
+ }
+}
+
+func array_literals() {
+ type A0 [0]int
+ _ = A0{}
+ _ = A0{0 /* ERROR "index .* out of bounds" */}
+ _ = A0{0 /* ERROR "index .* out of bounds" */ : 0}
+
+ type A1 [10]int
+ _ = A1{}
+ _ = A1{0, 1, 2}
+ _ = A1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
+ _ = A1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 /* ERROR "index .* out of bounds" */ }
+ _ = A1{- /* ERROR "negative" */ 1: 0}
+ _ = A1{8: 8, 9}
+ _ = A1{8: 8, 9, 10 /* ERROR "index .* out of bounds" */ }
+ _ = A1{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4}
+ _ = A1{5: 5, 6, 7, 3: 3, 4}
+ _ = A1{5: 5, 6, 7, 3: 3, 4, 5 /* ERROR "duplicate index" */ }
+ _ = A1{10 /* ERROR "index .* out of bounds" */ : 10, 10 /* ERROR "index .* out of bounds" */ : 10}
+ _ = A1{5: 5, 6, 7, 3: 3, 1 /* ERROR "overflows" */ <<100: 4, 5 /* ERROR "duplicate index" */ }
+ _ = A1{5: 5, 6, 7, 4: 4, 1 /* ERROR "overflows" */ <<100: 4}
+ _ = A1{2.0}
+ _ = A1{2.1 /* ERROR "truncated" */ }
+ _ = A1{"foo" /* ERROR "cannot convert" */ }
+
+ // indices must be integer constants
+ i := 1
+ const f = 2.1
+ const s = "foo"
+ _ = A1{i /* ERROR "index i must be integer constant" */ : 0}
+ _ = A1{f /* ERROR "truncated" */ : 0}
+ _ = A1{s /* ERROR "cannot convert" */ : 0}
+
+ a0 := [...]int{}
+ assert(len(a0) == 0)
+
+ a1 := [...]int{0, 1, 2}
+ assert(len(a1) == 3)
+ var a13 [3]int
+ var a14 [4]int
+ a13 = a1
+ a14 = a1 /* ERROR "cannot assign" */
+ _, _ = a13, a14
+
+ a2 := [...]int{- /* ERROR "negative" */ 1: 0}
+ _ = a2
+
+ a3 := [...]int{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4}
+ assert(len(a3) == 5) // somewhat arbitrary
+
+ a4 := [...]complex128{0, 1, 2, 1<<10-2: -1i, 1i, 400: 10, 12, 14}
+ assert(len(a4) == 1024)
+
+ // composite literal element types may be elided
+ type T []int
+ _ = [10]T{T{}, {}, 5: T{1, 2, 3}, 7: {1, 2, 3}}
+ a6 := [...]T{T{}, {}, 5: T{1, 2, 3}, 7: {1, 2, 3}}
+ assert(len(a6) == 8)
+
+ // recursively so
+ _ = [10][10]T{{}, [10]T{{}}, {{1, 2, 3}}}
+
+ // from the spec
+ type Point struct { x, y float32 }
+ _ = [...]Point{Point{1.5, -3.5}, Point{0, 0}}
+ _ = [...]Point{{1.5, -3.5}, {0, 0}}
+ _ = [][]int{[]int{1, 2, 3}, []int{4, 5}}
+ _ = [][]int{{1, 2, 3}, {4, 5}}
+ _ = [...]*Point{&Point{1.5, -3.5}, &Point{0, 0}}
+ _ = [...]*Point{{1.5, -3.5}, {0, 0}}
+}
+
+func slice_literals() {
+ type S0 []int
+ _ = S0{}
+ _ = S0{0, 1, 2}
+ _ = S0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
+ _ = S0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
+ _ = S0{- /* ERROR "negative" */ 1: 0}
+ _ = S0{8: 8, 9}
+ _ = S0{8: 8, 9, 10}
+ _ = S0{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4}
+ _ = S0{5: 5, 6, 7, 3: 3, 4}
+ _ = S0{5: 5, 6, 7, 3: 3, 4, 5 /* ERROR "duplicate index" */ }
+ _ = S0{10: 10, 10 /* ERROR "duplicate index" */ : 10}
+ _ = S0{5: 5, 6, 7, 3: 3, 1 /* ERROR "overflows" */ <<100: 4, 5 /* ERROR "duplicate index" */ }
+ _ = S0{5: 5, 6, 7, 4: 4, 1 /* ERROR "overflows" */ <<100: 4}
+ _ = S0{2.0}
+ _ = S0{2.1 /* ERROR "truncated" */ }
+ _ = S0{"foo" /* ERROR "cannot convert" */ }
+
+ // indices must be resolved correctly
+ const index1 = 1
+ _ = S0{index1: 1}
+ _ = S0{index2: 2}
+ _ = S0{index3 /* ERROR "undeclared name" */ : 3}
+
+ // indices must be integer constants
+ i := 1
+ const f = 2.1
+ const s = "foo"
+ _ = S0{i /* ERROR "index i must be integer constant" */ : 0}
+ _ = S0{f /* ERROR "truncated" */ : 0}
+ _ = S0{s /* ERROR "cannot convert" */ : 0}
+
+ // composite literal element types may be elided
+ type T []int
+ _ = []T{T{}, {}, 5: T{1, 2, 3}, 7: {1, 2, 3}}
+ _ = [][]int{{1, 2, 3}, {4, 5}}
+
+ // recursively so
+ _ = [][]T{{}, []T{{}}, {{1, 2, 3}}}
+}
+
+const index2 int = 2
+
+type N int
+func (N) f() {}
+
+func map_literals() {
+ type M0 map[string]int
+ type M1 map[bool]int
+ type M2 map[*int]int
+
+ _ = M0{}
+ _ = M0{1 /* ERROR "missing key" */ }
+ _ = M0{1 /* ERROR "cannot convert" */ : 2}
+ _ = M0{"foo": "bar" /* ERROR "cannot convert" */ }
+ _ = M0{"foo": 1, "bar": 2, "foo" /* ERROR "duplicate key" */ : 3 }
+
+ _ = map[interface{}]int{2: 1, 2 /* ERROR "duplicate key" */ : 1}
+ _ = map[interface{}]int{int(2): 1, int16(2): 1}
+ _ = map[interface{}]int{int16(2): 1, int16 /* ERROR "duplicate key" */ (2): 1}
+
+ type S string
+
+ _ = map[interface{}]int{"a": 1, "a" /* ERROR "duplicate key" */ : 1}
+ _ = map[interface{}]int{"a": 1, S("a"): 1}
+ _ = map[interface{}]int{S("a"): 1, S /* ERROR "duplicate key" */ ("a"): 1}
+
+ type I interface {
+ f()
+ }
+
+ _ = map[I]int{N(0): 1, N(2): 1}
+ _ = map[I]int{N(2): 1, N /* ERROR "duplicate key" */ (2): 1}
+
+ // map keys must be resolved correctly
+ key1 := "foo"
+ _ = M0{key1: 1}
+ _ = M0{key2: 2}
+ _ = M0{key3 /* ERROR "undeclared name" */ : 2}
+
+ var value int
+ _ = M1{true: 1, false: 0}
+ _ = M2{nil: 0, &value: 1}
+
+ // composite literal element types may be elided
+ type T [2]int
+ _ = map[int]T{0: T{3, 4}, 1: {5, 6}}
+
+ // recursively so
+ _ = map[int][]T{0: {}, 1: {{}, T{1, 2}}}
+
+ // composite literal key types may be elided
+ _ = map[T]int{T{3, 4}: 0, {5, 6}: 1}
+
+ // recursively so
+ _ = map[[2]T]int{{}: 0, {{}}: 1, [2]T{{}}: 2, {T{1, 2}}: 3}
+
+ // composite literal element and key types may be elided
+ _ = map[T]T{{}: {}, {1, 2}: T{3, 4}, T{4, 5}: {}}
+ _ = map[T]M0{{} : {}, T{1, 2}: M0{"foo": 0}, {1, 3}: {"foo": 1}}
+
+ // recursively so
+ _ = map[[2]T][]T{{}: {}, {{}}: {{}, T{1, 2}}, [2]T{{}}: nil, {T{1, 2}}: {{}, {}}}
+
+ // from the spec
+ type Point struct { x, y float32 }
+ _ = map[string]Point{"orig": {0, 0}}
+ _ = map[*Point]string{{0, 0}: "orig"}
+}
+
+var key2 string = "bar"
+
+type I interface {
+ m()
+}
+
+type I2 interface {
+ m(int)
+}
+
+type T1 struct{}
+type T2 struct{}
+
+func (T2) m(int) {}
+
+type mybool bool
+
+func type_asserts() {
+ var x int
+ _ = x /* ERROR "not an interface" */ .(int)
+
+ var e interface{}
+ var ok bool
+ x, ok = e.(int)
+ _ = ok
+
+ // ok value is of type bool
+ var myok mybool
+ _, myok = e.(int)
+ _ = myok
+
+ var t I
+ _ = t /* ERROR "use of .* outside type switch" */ .(type)
+ _ = t /* ERROR "missing method m" */ .(T)
+ _ = t.(*T)
+ _ = t /* ERROR "missing method m" */ .(T1)
+ _ = t /* ERROR "wrong type for method m" */ .(T2)
+ _ = t /* STRICT "wrong type for method m" */ .(I2) // only an error in strict mode (issue 8561)
+
+ // e doesn't statically have an m, but may have one dynamically.
+ _ = e.(I2)
+}
+
+func f0() {}
+func f1(x int) {}
+func f2(u float32, s string) {}
+func fs(s []byte) {}
+func fv(x ...int) {}
+func fi(x ... interface{}) {}
+func (T) fm(x ...int)
+
+func g0() {}
+func g1() int { return 0}
+func g2() (u float32, s string) { return }
+func gs() []byte { return nil }
+
+func _calls() {
+ var x int
+ var y float32
+ var s []int
+
+ f0()
+ _ = f0 /* ERROR "used as value" */ ()
+ f0(g0 /* ERROR "too many arguments" */ )
+
+ f1(0)
+ f1(x)
+ f1(10.0)
+ f1() /* ERROR "too few arguments" */
+ f1(x, y /* ERROR "too many arguments" */ )
+ f1(s /* ERROR "cannot pass" */ )
+ f1(x ... /* ERROR "cannot use ..." */ )
+ f1(g0 /* ERROR "used as value" */ ())
+ f1(g1())
+ // f1(g2()) // TODO(gri) missing position in error message
+
+ f2() /* ERROR "too few arguments" */
+ f2(3.14) /* ERROR "too few arguments" */
+ f2(3.14, "foo")
+ f2(x /* ERROR "cannot pass" */ , "foo")
+ f2(g0 /* ERROR "used as value" */ ())
+ f2(g1 /* ERROR "cannot pass" */ ()) /* ERROR "too few arguments" */
+ f2(g2())
+
+ fs() /* ERROR "too few arguments" */
+ fs(g0 /* ERROR "used as value" */ ())
+ fs(g1 /* ERROR "cannot pass" */ ())
+ fs(g2 /* ERROR "cannot pass" */ /* ERROR "too many arguments" */ ())
+ fs(gs())
+
+ fv()
+ fv(1, 2.0, x)
+ fv(s /* ERROR "cannot pass" */ )
+ fv(s...)
+ fv(x /* ERROR "cannot use" */ ...)
+ fv(1, s... /* ERROR "can only use ... with matching parameter" */ )
+ fv(gs /* ERROR "cannot pass" */ ())
+ fv(gs /* ERROR "cannot pass" */ ()...)
+
+ var t T
+ t.fm()
+ t.fm(1, 2.0, x)
+ t.fm(s /* ERROR "cannot pass" */ )
+ t.fm(g1())
+ t.fm(1, s... /* ERROR "can only use ... with matching parameter" */ )
+ t.fm(gs /* ERROR "cannot pass" */ ())
+ t.fm(gs /* ERROR "cannot pass" */ ()...)
+
+ T.fm(t, )
+ T.fm(t, 1, 2.0, x)
+ T.fm(t, s /* ERROR "cannot pass" */ )
+ T.fm(t, g1())
+ T.fm(t, 1, s... /* ERROR "can only use ... with matching parameter" */ )
+ T.fm(t, gs /* ERROR "cannot pass" */ ())
+ T.fm(t, gs /* ERROR "cannot pass" */ ()...)
+
+ var i interface{ fm(x ...int) } = t
+ i.fm()
+ i.fm(1, 2.0, x)
+ i.fm(s /* ERROR "cannot pass" */ )
+ i.fm(g1())
+ i.fm(1, s... /* ERROR "can only use ... with matching parameter" */ )
+ i.fm(gs /* ERROR "cannot pass" */ ())
+ i.fm(gs /* ERROR "cannot pass" */ ()...)
+
+ fi()
+ fi(1, 2.0, x, 3.14, "foo")
+ fi(g2())
+ fi(0, g2)
+ fi(0, g2 /* ERROR "2-valued expression" */ ())
+}
+
+func issue6344() {
+ type T []interface{}
+ var x T
+ fi(x...) // ... applies also to named slices
+}
diff --git a/src/go/types/testdata/gotos.src b/src/go/types/testdata/gotos.src
new file mode 100644
index 0000000000..0c7ee44056
--- /dev/null
+++ b/src/go/types/testdata/gotos.src
@@ -0,0 +1,560 @@
+// 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 is a modified copy of $GOROOT/test/goto.go.
+
+package gotos
+
+var (
+ i, n int
+ x []int
+ c chan int
+ m map[int]int
+ s string
+)
+
+// goto after declaration okay
+func _() {
+ x := 1
+ goto L
+L:
+ _ = x
+}
+
+// goto before declaration okay
+func _() {
+ goto L
+L:
+ x := 1
+ _ = x
+}
+
+// goto across declaration not okay
+func _() {
+ goto L /* ERROR "goto L jumps over variable declaration at line 36" */
+ x := 1
+ _ = x
+L:
+}
+
+// goto across declaration in inner scope okay
+func _() {
+ goto L
+ {
+ x := 1
+ _ = x
+ }
+L:
+}
+
+// goto across declaration after inner scope not okay
+func _() {
+ goto L /* ERROR "goto L jumps over variable declaration at line 58" */
+ {
+ x := 1
+ _ = x
+ }
+ x := 1
+ _ = x
+L:
+}
+
+// goto across declaration in reverse okay
+func _() {
+L:
+ x := 1
+ _ = x
+ goto L
+}
+
+func _() {
+L: L1:
+ x := 1
+ _ = x
+ goto L
+ goto L1
+}
+
+// error shows first offending variable
+func _() {
+ goto L /* ERROR "goto L jumps over variable declaration at line 84" */
+ x := 1
+ _ = x
+ y := 1
+ _ = y
+L:
+}
+
+// goto not okay even if code path is dead
+func _() {
+ goto L /* ERROR "goto L jumps over variable declaration" */
+ x := 1
+ _ = x
+ y := 1
+ _ = y
+ return
+L:
+}
+
+// goto into outer block okay
+func _() {
+ {
+ goto L
+ }
+L:
+}
+
+func _() {
+ {
+ goto L
+ goto L1
+ }
+L: L1:
+}
+
+// goto backward into outer block okay
+func _() {
+L:
+ {
+ goto L
+ }
+}
+
+func _() {
+L: L1:
+ {
+ goto L
+ goto L1
+ }
+}
+
+// goto into inner block not okay
+func _() {
+ goto L /* ERROR "goto L jumps into block" */
+ {
+ L:
+ }
+}
+
+func _() {
+ goto L /* ERROR "goto L jumps into block" */
+ goto L1 /* ERROR "goto L1 jumps into block" */
+ {
+ L: L1:
+ }
+}
+
+// goto backward into inner block still not okay
+func _() {
+ {
+ L:
+ }
+ goto L /* ERROR "goto L jumps into block" */
+}
+
+func _() {
+ {
+ L: L1:
+ }
+ goto L /* ERROR "goto L jumps into block" */
+ goto L1 /* ERROR "goto L1 jumps into block" */
+}
+
+// error shows first (outermost) offending block
+func _() {
+ goto L /* ERROR "goto L jumps into block" */
+ {
+ {
+ {
+ L:
+ }
+ }
+ }
+}
+
+// error prefers block diagnostic over declaration diagnostic
+func _() {
+ goto L /* ERROR "goto L jumps into block" */
+ x := 1
+ _ = x
+ {
+ L:
+ }
+}
+
+// many kinds of blocks, all invalid to jump into or among,
+// but valid to jump out of
+
+// if
+
+func _() {
+L:
+ if true {
+ goto L
+ }
+}
+
+func _() {
+L:
+ if true {
+ goto L
+ } else {
+ }
+}
+
+func _() {
+L:
+ if false {
+ } else {
+ goto L
+ }
+}
+
+func _() {
+ goto L /* ERROR "goto L jumps into block" */
+ if true {
+ L:
+ }
+}
+
+func _() {
+ goto L /* ERROR "goto L jumps into block" */
+ if true {
+ L:
+ } else {
+ }
+}
+
+func _() {
+ goto L /* ERROR "goto L jumps into block" */
+ if true {
+ } else {
+ L:
+ }
+}
+
+func _() {
+ if false {
+ L:
+ } else {
+ goto L /* ERROR "goto L jumps into block" */
+ }
+}
+
+func _() {
+ if true {
+ goto L /* ERROR "goto L jumps into block" */
+ } else {
+ L:
+ }
+}
+
+func _() {
+ if true {
+ goto L /* ERROR "goto L jumps into block" */
+ } else if false {
+ L:
+ }
+}
+
+func _() {
+ if true {
+ goto L /* ERROR "goto L jumps into block" */
+ } else if false {
+ L:
+ } else {
+ }
+}
+
+func _() {
+ if true {
+ goto L /* ERROR "goto L jumps into block" */
+ } else if false {
+ } else {
+ L:
+ }
+}
+
+func _() {
+ if true {
+ goto L /* ERROR "goto L jumps into block" */
+ } else {
+ L:
+ }
+}
+
+func _() {
+ if true {
+ L:
+ } else {
+ goto L /* ERROR "goto L jumps into block" */
+ }
+}
+
+// for
+
+func _() {
+ for {
+ goto L
+ }
+L:
+}
+
+func _() {
+ for {
+ goto L
+ L:
+ }
+}
+
+func _() {
+ for {
+ L:
+ }
+ goto L /* ERROR "goto L jumps into block" */
+}
+
+func _() {
+ for {
+ goto L
+ L1:
+ }
+L:
+ goto L1 /* ERROR "goto L1 jumps into block" */
+}
+
+func _() {
+ for i < n {
+ L:
+ }
+ goto L /* ERROR "goto L jumps into block" */
+}
+
+func _() {
+ for i = 0; i < n; i++ {
+ L:
+ }
+ goto L /* ERROR "goto L jumps into block" */
+}
+
+func _() {
+ for i = range x {
+ L:
+ }
+ goto L /* ERROR "goto L jumps into block" */
+}
+
+func _() {
+ for i = range c {
+ L:
+ }
+ goto L /* ERROR "goto L jumps into block" */
+}
+
+func _() {
+ for i = range m {
+ L:
+ }
+ goto L /* ERROR "goto L jumps into block" */
+}
+
+func _() {
+ for i = range s {
+ L:
+ }
+ goto L /* ERROR "goto L jumps into block" */
+}
+
+// switch
+
+func _() {
+L:
+ switch i {
+ case 0:
+ goto L
+ }
+}
+
+func _() {
+L:
+ switch i {
+ case 0:
+
+ default:
+ goto L
+ }
+}
+
+func _() {
+ switch i {
+ case 0:
+
+ default:
+ L:
+ goto L
+ }
+}
+
+func _() {
+ switch i {
+ case 0:
+
+ default:
+ goto L
+ L:
+ }
+}
+
+func _() {
+ switch i {
+ case 0:
+ goto L
+ L:
+ ;
+ default:
+ }
+}
+
+func _() {
+ goto L /* ERROR "goto L jumps into block" */
+ switch i {
+ case 0:
+ L:
+ }
+}
+
+func _() {
+ goto L /* ERROR "goto L jumps into block" */
+ switch i {
+ case 0:
+ L:
+ ;
+ default:
+ }
+}
+
+func _() {
+ goto L /* ERROR "goto L jumps into block" */
+ switch i {
+ case 0:
+ default:
+ L:
+ }
+}
+
+func _() {
+ switch i {
+ default:
+ goto L /* ERROR "goto L jumps into block" */
+ case 0:
+ L:
+ }
+}
+
+func _() {
+ switch i {
+ case 0:
+ L:
+ ;
+ default:
+ goto L /* ERROR "goto L jumps into block" */
+ }
+}
+
+// select
+// different from switch. the statement has no implicit block around it.
+
+func _() {
+L:
+ select {
+ case <-c:
+ goto L
+ }
+}
+
+func _() {
+L:
+ select {
+ case c <- 1:
+
+ default:
+ goto L
+ }
+}
+
+func _() {
+ select {
+ case <-c:
+
+ default:
+ L:
+ goto L
+ }
+}
+
+func _() {
+ select {
+ case c <- 1:
+
+ default:
+ goto L
+ L:
+ }
+}
+
+func _() {
+ select {
+ case <-c:
+ goto L
+ L:
+ ;
+ default:
+ }
+}
+
+func _() {
+ goto L /* ERROR "goto L jumps into block" */
+ select {
+ case c <- 1:
+ L:
+ }
+}
+
+func _() {
+ goto L /* ERROR "goto L jumps into block" */
+ select {
+ case c <- 1:
+ L:
+ ;
+ default:
+ }
+}
+
+func _() {
+ goto L /* ERROR "goto L jumps into block" */
+ select {
+ case <-c:
+ default:
+ L:
+ }
+}
+
+func _() {
+ select {
+ default:
+ goto L /* ERROR "goto L jumps into block" */
+ case <-c:
+ L:
+ }
+}
+
+func _() {
+ select {
+ case <-c:
+ L:
+ ;
+ default:
+ goto L /* ERROR "goto L jumps into block" */
+ }
+}
diff --git a/src/go/types/testdata/importdecl0a.src b/src/go/types/testdata/importdecl0a.src
new file mode 100644
index 0000000000..463dcd083d
--- /dev/null
+++ b/src/go/types/testdata/importdecl0a.src
@@ -0,0 +1,53 @@
+// 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 importdecl0
+
+import ()
+
+import (
+ // we can have multiple blank imports (was bug)
+ _ "math"
+ _ "net/rpc"
+ init /* ERROR "cannot declare init" */ "fmt"
+ // reflect defines a type "flag" which shows up in the gc export data
+ "reflect"
+ . /* ERROR "imported but not used" */ "reflect"
+)
+
+import "math" /* ERROR "imported but not used" */
+import m /* ERROR "imported but not used as m" */ "math"
+import _ "math"
+
+import (
+ "math/big" /* ERROR "imported but not used" */
+ b /* ERROR "imported but not used" */ "math/big"
+ _ "math/big"
+)
+
+import "fmt"
+import f1 "fmt"
+import f2 "fmt"
+
+// reflect.flag must not be visible in this package
+type flag int
+type _ reflect /* ERROR "not exported" */ .flag
+
+// imported package name may conflict with local objects
+type reflect /* ERROR "reflect already declared" */ int
+
+// dot-imported exported objects may conflict with local objects
+type Value /* ERROR "Value already declared through dot-import of package reflect" */ struct{}
+
+var _ = fmt.Println // use "fmt"
+
+func _() {
+ f1.Println() // use "fmt"
+}
+
+func _() {
+ _ = func() {
+ f2.Println() // use "fmt"
+ }
+}
diff --git a/src/go/types/testdata/importdecl0b.src b/src/go/types/testdata/importdecl0b.src
new file mode 100644
index 0000000000..6844e70982
--- /dev/null
+++ b/src/go/types/testdata/importdecl0b.src
@@ -0,0 +1,33 @@
+// 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 importdecl0
+
+import "math"
+import m "math"
+
+import . "testing" // declares T in file scope
+import . /* ERROR "imported but not used" */ "unsafe"
+import . "fmt" // declares Println in file scope
+
+import (
+ // TODO(gri) At the moment, 2 errors are reported because both go/parser
+ // and the type checker report it. Eventually, this test should not be
+ // done by the parser anymore.
+ "" /* ERROR invalid import path */ /* ERROR invalid import path */
+ "a!b" /* ERROR invalid import path */ /* ERROR invalid import path */
+ "abc\xffdef" /* ERROR invalid import path */ /* ERROR invalid import path */
+)
+
+// using "math" in this file doesn't affect its use in other files
+const Pi0 = math.Pi
+const Pi1 = m.Pi
+
+type _ T // use "testing"
+
+func _() func() interface{} {
+ return func() interface{} {
+ return Println // use "fmt"
+ }
+}
diff --git a/src/go/types/testdata/importdecl1a.src b/src/go/types/testdata/importdecl1a.src
new file mode 100644
index 0000000000..8301820dda
--- /dev/null
+++ b/src/go/types/testdata/importdecl1a.src
@@ -0,0 +1,11 @@
+// Copyright 2014 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.
+
+// Test case for issue 8969.
+
+package importdecl1
+
+import . "unsafe"
+
+var _ Pointer // use dot-imported package unsafe
diff --git a/src/go/types/testdata/importdecl1b.src b/src/go/types/testdata/importdecl1b.src
new file mode 100644
index 0000000000..f24bb9ade9
--- /dev/null
+++ b/src/go/types/testdata/importdecl1b.src
@@ -0,0 +1,7 @@
+// Copyright 2014 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 importdecl1
+
+import . /* ERROR "imported but not used" */ "unsafe"
diff --git a/src/go/types/testdata/init0.src b/src/go/types/testdata/init0.src
new file mode 100644
index 0000000000..ef0349c70f
--- /dev/null
+++ b/src/go/types/testdata/init0.src
@@ -0,0 +1,106 @@
+// 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.
+
+// initialization cycles
+
+package init0
+
+// initialization cycles (we don't know the types)
+const (
+ s0 /* ERROR initialization cycle */ = s0
+
+ x0 /* ERROR initialization cycle */ = y0
+ y0 = x0
+
+ a0 = b0
+ b0 /* ERROR initialization cycle */ = c0
+ c0 = d0
+ d0 = b0
+)
+
+var (
+ s1 /* ERROR initialization cycle */ = s1
+
+ x1 /* ERROR initialization cycle */ = y1
+ y1 = x1
+
+ a1 = b1
+ b1 /* ERROR initialization cycle */ = c1
+ c1 = d1
+ d1 = b1
+)
+
+// initialization cycles (we know the types)
+const (
+ s2 /* ERROR initialization cycle */ int = s2
+
+ x2 /* ERROR initialization cycle */ int = y2
+ y2 = x2
+
+ a2 = b2
+ b2 /* ERROR initialization cycle */ int = c2
+ c2 = d2
+ d2 = b2
+)
+
+var (
+ s3 /* ERROR initialization cycle */ int = s3
+
+ x3 /* ERROR initialization cycle */ int = y3
+ y3 = x3
+
+ a3 = b3
+ b3 /* ERROR initialization cycle */ int = c3
+ c3 = d3
+ d3 = b3
+)
+
+// cycles via struct fields
+
+type S1 struct {
+ f int
+}
+const cx3 S1 /* ERROR invalid constant type */ = S1{cx3.f}
+var vx3 /* ERROR initialization cycle */ S1 = S1{vx3.f}
+
+// cycles via functions
+
+var x4 = x5
+var x5 /* ERROR initialization cycle */ = f1()
+func f1() int { return x5*10 }
+
+var x6, x7 /* ERROR initialization cycle */ = f2()
+var x8 = x7
+func f2() (int, int) { return f3() + f3(), 0 }
+func f3() int { return x8 }
+
+// cycles via closures
+
+var x9 /* ERROR initialization cycle */ = func() int { return x9 }()
+
+var x10 /* ERROR initialization cycle */ = f4()
+
+func f4() int {
+ _ = func() {
+ _ = x10
+ }
+ return 0
+}
+
+// cycles via method expressions
+
+type T1 struct{}
+
+func (T1) m() bool { _ = x11; return false }
+
+var x11 /* ERROR initialization cycle */ = T1.m(T1{})
+
+// cycles via method values
+
+type T2 struct{}
+
+func (T2) m() bool { _ = x12; return false }
+
+var t1 T2
+var x12 /* ERROR initialization cycle */ = t1.m
diff --git a/src/go/types/testdata/init1.src b/src/go/types/testdata/init1.src
new file mode 100644
index 0000000000..39ca31466b
--- /dev/null
+++ b/src/go/types/testdata/init1.src
@@ -0,0 +1,97 @@
+// 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.
+
+// initialization cycles
+
+package init1
+
+// issue 6683 (marked as WorkingAsIntended)
+
+type T0 struct{}
+
+func (T0) m() int { return y0 }
+
+var x0 = T0{}
+
+var y0 /* ERROR initialization cycle */ = x0.m()
+
+type T1 struct{}
+
+func (T1) m() int { return y1 }
+
+var x1 interface {
+ m() int
+} = T1{}
+
+var y1 = x1.m() // no cycle reported, x1 is of interface type
+
+// issue 6703 (modified)
+
+var x2 /* ERROR initialization cycle */ = T2.m
+
+var y2 = x2
+
+type T2 struct{}
+
+func (T2) m() int {
+ _ = y2
+ return 0
+}
+
+var x3 /* ERROR initialization cycle */ = T3.m(T3{}) // <<<< added (T3{})
+
+var y3 = x3
+
+type T3 struct{}
+
+func (T3) m() int {
+ _ = y3
+ return 0
+}
+
+var x4 /* ERROR initialization cycle */ = T4{}.m // <<<< added {}
+
+var y4 = x4
+
+type T4 struct{}
+
+func (T4) m() int {
+ _ = y4
+ return 0
+}
+
+var x5 /* ERROR initialization cycle */ = T5{}.m() // <<<< added ()
+
+var y5 = x5
+
+type T5 struct{}
+
+func (T5) m() int {
+ _ = y5
+ return 0
+}
+
+// issue 4847
+// simplified test case
+
+var x6 = f6
+var y6 /* ERROR initialization cycle */ = f6
+func f6() { _ = y6 }
+
+// full test case
+
+type (
+ E int
+ S int
+)
+
+type matcher func(s *S) E
+
+func matchList(s *S) E { return matcher(matchAnyFn)(s) }
+
+var foo = matcher(matchList)
+
+var matchAny /* ERROR initialization cycle */ = matcher(matchList)
+
+func matchAnyFn(s *S) (err E) { return matchAny(s) }
\ No newline at end of file
diff --git a/src/go/types/testdata/init2.src b/src/go/types/testdata/init2.src
new file mode 100644
index 0000000000..614db6c949
--- /dev/null
+++ b/src/go/types/testdata/init2.src
@@ -0,0 +1,139 @@
+// Copyright 2014 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.
+
+// initialization cycles
+
+package init2
+
+// cycles through functions
+
+func f1() int { _ = x1; return 0 }
+var x1 /* ERROR initialization cycle */ = f1
+
+func f2() int { _ = x2; return 0 }
+var x2 /* ERROR initialization cycle */ = f2()
+
+// cycles through method expressions
+
+type T3 int
+func (T3) m() int { _ = x3; return 0 }
+var x3 /* ERROR initialization cycle */ = T3.m
+
+type T4 int
+func (T4) m() int { _ = x4; return 0 }
+var x4 /* ERROR initialization cycle */ = T4.m(0)
+
+type T3p int
+func (*T3p) m() int { _ = x3p; return 0 }
+var x3p /* ERROR initialization cycle */ = (*T3p).m
+
+type T4p int
+func (*T4p) m() int { _ = x4p; return 0 }
+var x4p /* ERROR initialization cycle */ = (*T4p).m(nil)
+
+// cycles through method expressions of embedded methods
+
+type T5 struct { E5 }
+type E5 int
+func (E5) m() int { _ = x5; return 0 }
+var x5 /* ERROR initialization cycle */ = T5.m
+
+type T6 struct { E6 }
+type E6 int
+func (E6) m() int { _ = x6; return 0 }
+var x6 /* ERROR initialization cycle */ = T6.m(T6{0})
+
+type T5p struct { E5p }
+type E5p int
+func (*E5p) m() int { _ = x5p; return 0 }
+var x5p /* ERROR initialization cycle */ = (*T5p).m
+
+type T6p struct { E6p }
+type E6p int
+func (*E6p) m() int { _ = x6p; return 0 }
+var x6p /* ERROR initialization cycle */ = (*T6p).m(nil)
+
+// cycles through method values
+
+type T7 int
+func (T7) m() int { _ = x7; return 0 }
+var x7 /* ERROR initialization cycle */ = T7(0).m
+
+type T8 int
+func (T8) m() int { _ = x8; return 0 }
+var x8 /* ERROR initialization cycle */ = T8(0).m()
+
+type T7p int
+func (*T7p) m() int { _ = x7p; return 0 }
+var x7p /* ERROR initialization cycle */ = new(T7p).m
+
+type T8p int
+func (*T8p) m() int { _ = x8p; return 0 }
+var x8p /* ERROR initialization cycle */ = new(T8p).m()
+
+type T7v int
+func (T7v) m() int { _ = x7v; return 0 }
+var x7var T7v
+var x7v /* ERROR initialization cycle */ = x7var.m
+
+type T8v int
+func (T8v) m() int { _ = x8v; return 0 }
+var x8var T8v
+var x8v /* ERROR initialization cycle */ = x8var.m()
+
+type T7pv int
+func (*T7pv) m() int { _ = x7pv; return 0 }
+var x7pvar *T7pv
+var x7pv /* ERROR initialization cycle */ = x7pvar.m
+
+type T8pv int
+func (*T8pv) m() int { _ = x8pv; return 0 }
+var x8pvar *T8pv
+var x8pv /* ERROR initialization cycle */ = x8pvar.m()
+
+// cycles through method values of embedded methods
+
+type T9 struct { E9 }
+type E9 int
+func (E9) m() int { _ = x9; return 0 }
+var x9 /* ERROR initialization cycle */ = T9{0}.m
+
+type T10 struct { E10 }
+type E10 int
+func (E10) m() int { _ = x10; return 0 }
+var x10 /* ERROR initialization cycle */ = T10{0}.m()
+
+type T9p struct { E9p }
+type E9p int
+func (*E9p) m() int { _ = x9p; return 0 }
+var x9p /* ERROR initialization cycle */ = new(T9p).m
+
+type T10p struct { E10p }
+type E10p int
+func (*E10p) m() int { _ = x10p; return 0 }
+var x10p /* ERROR initialization cycle */ = new(T10p).m()
+
+type T9v struct { E9v }
+type E9v int
+func (E9v) m() int { _ = x9v; return 0 }
+var x9var T9v
+var x9v /* ERROR initialization cycle */ = x9var.m
+
+type T10v struct { E10v }
+type E10v int
+func (E10v) m() int { _ = x10v; return 0 }
+var x10var T10v
+var x10v /* ERROR initialization cycle */ = x10var.m()
+
+type T9pv struct { E9pv }
+type E9pv int
+func (*E9pv) m() int { _ = x9pv; return 0 }
+var x9pvar *T9pv
+var x9pv /* ERROR initialization cycle */ = x9pvar.m
+
+type T10pv struct { E10pv }
+type E10pv int
+func (*E10pv) m() int { _ = x10pv; return 0 }
+var x10pvar *T10pv
+var x10pv /* ERROR initialization cycle */ = x10pvar.m()
diff --git a/src/go/types/testdata/issues.src b/src/go/types/testdata/issues.src
new file mode 100644
index 0000000000..d08e0fd878
--- /dev/null
+++ b/src/go/types/testdata/issues.src
@@ -0,0 +1,73 @@
+// Copyright 2014 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 issues
+
+import "fmt"
+
+func issue7035() {
+ type T struct{ X int }
+ _ = func() {
+ fmt.Println() // must refer to imported fmt rather than the fmt below
+ }
+ fmt := new(T)
+ _ = fmt.X
+}
+
+func issue8066() {
+ const (
+ // TODO(gri) Enable test below for releases 1.4 and higher
+ // _ = float32(340282356779733661637539395458142568447)
+ _ = float32(340282356779733661637539395458142568448 /* ERROR cannot convert */ )
+ )
+}
+
+// Check that a missing identifier doesn't lead to a spurious error cascade.
+func issue8799a() {
+ x, ok := missing /* ERROR undeclared */ ()
+ _ = !ok
+ _ = x
+}
+
+func issue8799b(x int, ok bool) {
+ x, ok = missing /* ERROR undeclared */ ()
+ _ = !ok
+ _ = x
+}
+
+func issue9182() {
+ type Point C /* ERROR undeclared */ .Point
+ // no error for composite literal based on unknown type
+ _ = Point{x: 1, y: 2}
+}
+
+func f0() (a []int) { return }
+func f1() (a []int, b int) { return }
+func f2() (a, b []int) { return }
+
+func append_([]int, ...int) {}
+
+func issue9473(a []int, b ...int) {
+ // variadic builtin function
+ _ = append(f0())
+ _ = append(f0(), f0()...)
+ _ = append(f1())
+ _ = append(f2 /* ERROR cannot pass argument */ ())
+ _ = append(f2()... /* ERROR cannot use ... */ )
+ _ = append(f0(), f1 /* ERROR 2-valued expression */ ())
+ _ = append(f0(), f2 /* ERROR 2-valued expression */ ())
+ _ = append(f0(), f1()... /* ERROR cannot use ... */ )
+ _ = append(f0(), f2()... /* ERROR cannot use ... */ )
+
+ // variadic user-defined function
+ append_(f0())
+ append_(f0(), f0()...)
+ append_(f1())
+ append_(f2 /* ERROR cannot pass argument */ ())
+ append_(f2()... /* ERROR cannot use ... */ )
+ append_(f0(), f1 /* ERROR 2-valued expression */ ())
+ append_(f0(), f2 /* ERROR 2-valued expression */ ())
+ append_(f0(), f1()... /* ERROR cannot use */ )
+ append_(f0(), f2()... /* ERROR cannot use */ )
+}
diff --git a/src/go/types/testdata/labels.src b/src/go/types/testdata/labels.src
new file mode 100644
index 0000000000..102ffc7c17
--- /dev/null
+++ b/src/go/types/testdata/labels.src
@@ -0,0 +1,207 @@
+// 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 is a modified concatenation of the files
+// $GOROOT/test/label.go and $GOROOT/test/label1.go.
+
+package labels
+
+var x int
+
+func f0() {
+L1 /* ERROR "label L1 declared but not used" */ :
+ for {
+ }
+L2 /* ERROR "label L2 declared but not used" */ :
+ select {
+ }
+L3 /* ERROR "label L3 declared but not used" */ :
+ switch {
+ }
+L4 /* ERROR "label L4 declared but not used" */ :
+ if true {
+ }
+L5 /* ERROR "label L5 declared but not used" */ :
+ f0()
+L6:
+ f0()
+L6 /* ERROR "label L6 already declared" */ :
+ f0()
+ if x == 20 {
+ goto L6
+ }
+
+L7:
+ for {
+ break L7
+ break L8 /* ERROR "invalid break label L8" */
+ }
+
+// A label must be directly associated with a switch, select, or
+// for statement; it cannot be the label of a labeled statement.
+
+L7a /* ERROR "declared but not used" */ : L7b:
+ for {
+ break L7a /* ERROR "invalid break label L7a" */
+ continue L7a /* ERROR "invalid continue label L7a" */
+ continue L7b
+ }
+
+L8:
+ for {
+ if x == 21 {
+ continue L8
+ continue L7 /* ERROR "invalid continue label L7" */
+ }
+ }
+
+L9:
+ switch {
+ case true:
+ break L9
+ defalt /* ERROR "label defalt declared but not used" */ :
+ }
+
+L10:
+ select {
+ default:
+ break L10
+ break L9 /* ERROR "invalid break label L9" */
+ }
+
+ goto L10a
+L10a: L10b:
+ select {
+ default:
+ break L10a /* ERROR "invalid break label L10a" */
+ break L10b
+ continue L10b /* ERROR "invalid continue label L10b" */
+ }
+}
+
+func f1() {
+L1:
+ for {
+ if x == 0 {
+ break L1
+ }
+ if x == 1 {
+ continue L1
+ }
+ goto L1
+ }
+
+L2:
+ select {
+ default:
+ if x == 0 {
+ break L2
+ }
+ if x == 1 {
+ continue L2 /* ERROR "invalid continue label L2" */
+ }
+ goto L2
+ }
+
+L3:
+ switch {
+ case x > 10:
+ if x == 11 {
+ break L3
+ }
+ if x == 12 {
+ continue L3 /* ERROR "invalid continue label L3" */
+ }
+ goto L3
+ }
+
+L4:
+ if true {
+ if x == 13 {
+ break L4 /* ERROR "invalid break label L4" */
+ }
+ if x == 14 {
+ continue L4 /* ERROR "invalid continue label L4" */
+ }
+ if x == 15 {
+ goto L4
+ }
+ }
+
+L5:
+ f1()
+ if x == 16 {
+ break L5 /* ERROR "invalid break label L5" */
+ }
+ if x == 17 {
+ continue L5 /* ERROR "invalid continue label L5" */
+ }
+ if x == 18 {
+ goto L5
+ }
+
+ for {
+ if x == 19 {
+ break L1 /* ERROR "invalid break label L1" */
+ }
+ if x == 20 {
+ continue L1 /* ERROR "invalid continue label L1" */
+ }
+ if x == 21 {
+ goto L1
+ }
+ }
+}
+
+// Additional tests not in the original files.
+
+func f2() {
+L1 /* ERROR "label L1 declared but not used" */ :
+ if x == 0 {
+ for {
+ continue L1 /* ERROR "invalid continue label L1" */
+ }
+ }
+}
+
+func f3() {
+L1:
+L2:
+L3:
+ for {
+ break L1 /* ERROR "invalid break label L1" */
+ break L2 /* ERROR "invalid break label L2" */
+ break L3
+ continue L1 /* ERROR "invalid continue label L1" */
+ continue L2 /* ERROR "invalid continue label L2" */
+ continue L3
+ goto L1
+ goto L2
+ goto L3
+ }
+}
+
+// Blank labels are never declared.
+
+func f4() {
+_:
+_: // multiple blank labels are ok
+ goto _ /* ERROR "label _ not declared" */
+}
+
+func f5() {
+_:
+ for {
+ break _ /* ERROR "invalid break label _" */
+ continue _ /* ERROR "invalid continue label _" */
+ }
+}
+
+func f6() {
+_:
+ switch {
+ default:
+ break _ /* ERROR "invalid break label _" */
+ }
+}
diff --git a/src/go/types/testdata/methodsets.src b/src/go/types/testdata/methodsets.src
new file mode 100644
index 0000000000..89211468ea
--- /dev/null
+++ b/src/go/types/testdata/methodsets.src
@@ -0,0 +1,214 @@
+// 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 methodsets
+
+type T0 struct {}
+
+func (T0) v0() {}
+func (*T0) p0() {}
+
+type T1 struct {} // like T0 with different method names
+
+func (T1) v1() {}
+func (*T1) p1() {}
+
+type T2 interface {
+ v2()
+ p2()
+}
+
+type T3 struct {
+ T0
+ *T1
+ T2
+}
+
+// Method expressions
+func _() {
+ var (
+ _ func(T0) = T0.v0
+ _ = T0 /* ERROR "not in method set" */ .p0
+
+ _ func (*T0) = (*T0).v0
+ _ func (*T0) = (*T0).p0
+
+ // T1 is like T0
+
+ _ func(T2) = T2.v2
+ _ func(T2) = T2.p2
+
+ _ func(T3) = T3.v0
+ _ func(T3) = T3 /* ERROR "not in method set" */ .p0
+ _ func(T3) = T3.v1
+ _ func(T3) = T3.p1
+ _ func(T3) = T3.v2
+ _ func(T3) = T3.p2
+
+ _ func(*T3) = (*T3).v0
+ _ func(*T3) = (*T3).p0
+ _ func(*T3) = (*T3).v1
+ _ func(*T3) = (*T3).p1
+ _ func(*T3) = (*T3).v2
+ _ func(*T3) = (*T3).p2
+ )
+}
+
+// Method values with addressable receivers
+func _() {
+ var (
+ v0 T0
+ _ func() = v0.v0
+ _ func() = v0.p0
+ )
+
+ var (
+ p0 *T0
+ _ func() = p0.v0
+ _ func() = p0.p0
+ )
+
+ // T1 is like T0
+
+ var (
+ v2 T2
+ _ func() = v2.v2
+ _ func() = v2.p2
+ )
+
+ var (
+ v4 T3
+ _ func() = v4.v0
+ _ func() = v4.p0
+ _ func() = v4.v1
+ _ func() = v4.p1
+ _ func() = v4.v2
+ _ func() = v4.p2
+ )
+
+ var (
+ p4 *T3
+ _ func() = p4.v0
+ _ func() = p4.p0
+ _ func() = p4.v1
+ _ func() = p4.p1
+ _ func() = p4.v2
+ _ func() = p4.p2
+ )
+}
+
+// Method calls with addressable receivers
+func _() {
+ var v0 T0
+ v0.v0()
+ v0.p0()
+
+ var p0 *T0
+ p0.v0()
+ p0.p0()
+
+ // T1 is like T0
+
+ var v2 T2
+ v2.v2()
+ v2.p2()
+
+ var v4 T3
+ v4.v0()
+ v4.p0()
+ v4.v1()
+ v4.p1()
+ v4.v2()
+ v4.p2()
+
+ var p4 *T3
+ p4.v0()
+ p4.p0()
+ p4.v1()
+ p4.p1()
+ p4.v2()
+ p4.p2()
+}
+
+// Method values with value receivers
+func _() {
+ var (
+ _ func() = T0{}.v0
+ _ func() = T0 /* ERROR "not in method set" */ {}.p0
+
+ _ func() = (&T0{}).v0
+ _ func() = (&T0{}).p0
+
+ // T1 is like T0
+
+ // no values for T2
+
+ _ func() = T3{}.v0
+ _ func() = T3 /* ERROR "not in method set" */ {}.p0
+ _ func() = T3{}.v1
+ _ func() = T3{}.p1
+ _ func() = T3{}.v2
+ _ func() = T3{}.p2
+
+ _ func() = (&T3{}).v0
+ _ func() = (&T3{}).p0
+ _ func() = (&T3{}).v1
+ _ func() = (&T3{}).p1
+ _ func() = (&T3{}).v2
+ _ func() = (&T3{}).p2
+ )
+}
+
+// Method calls with value receivers
+func _() {
+ T0{}.v0()
+ T0 /* ERROR "not in method set" */ {}.p0()
+
+ (&T0{}).v0()
+ (&T0{}).p0()
+
+ // T1 is like T0
+
+ // no values for T2
+
+ T3{}.v0()
+ T3 /* ERROR "not in method set" */ {}.p0()
+ T3{}.v1()
+ T3{}.p1()
+ T3{}.v2()
+ T3{}.p2()
+
+ (&T3{}).v0()
+ (&T3{}).p0()
+ (&T3{}).v1()
+ (&T3{}).p1()
+ (&T3{}).v2()
+ (&T3{}).p2()
+}
+
+// *T has no methods if T is an interface type
+func issue5918() {
+ var (
+ err error
+ _ = err.Error()
+ _ func() string = err.Error
+ _ func(error) string = error.Error
+
+ perr = &err
+ _ = perr /* ERROR "no field or method" */ .Error()
+ _ func() string = perr /* ERROR "no field or method" */ .Error
+ _ func(*error) string = ( /* ERROR "no field or method" */ *error).Error
+ )
+
+ type T *interface{ m() int }
+ var (
+ x T
+ _ = (*x).m()
+ _ = (*x).m
+
+ _ = x /* ERROR "no field or method" */ .m()
+ _ = x /* ERROR "no field or method" */ .m
+ _ = T /* ERROR "no field or method" */ .m
+ )
+}
diff --git a/src/go/types/testdata/shifts.src b/src/go/types/testdata/shifts.src
new file mode 100644
index 0000000000..7f8ed06fbf
--- /dev/null
+++ b/src/go/types/testdata/shifts.src
@@ -0,0 +1,321 @@
+// 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 shifts
+
+func shifts0() {
+ // basic constant shifts
+ const (
+ s = 10
+ _ = 0<<0
+ _ = 1<> s)
+ _, _, _ = u, v, x
+}
+
+func shifts4() {
+ // shifts in comparisons w/ untyped operands
+ var s uint
+
+ _ = 1< len(fields) {
+ panic("more tags than fields")
+ }
+ return &Struct{fields: fields, tags: tags}
+}
+
+// NumFields returns the number of fields in the struct (including blank and anonymous fields).
+func (s *Struct) NumFields() int { return len(s.fields) }
+
+// Field returns the i'th field for 0 <= i < NumFields().
+func (s *Struct) Field(i int) *Var { return s.fields[i] }
+
+// Tag returns the i'th field tag for 0 <= i < NumFields().
+func (s *Struct) Tag(i int) string {
+ if i < len(s.tags) {
+ return s.tags[i]
+ }
+ return ""
+}
+
+// A Pointer represents a pointer type.
+type Pointer struct {
+ base Type // element type
+}
+
+// NewPointer returns a new pointer type for the given element (base) type.
+func NewPointer(elem Type) *Pointer { return &Pointer{base: elem} }
+
+// Elem returns the element type for the given pointer p.
+func (p *Pointer) Elem() Type { return p.base }
+
+// A Tuple represents an ordered list of variables; a nil *Tuple is a valid (empty) tuple.
+// Tuples are used as components of signatures and to represent the type of multiple
+// assignments; they are not first class types of Go.
+type Tuple struct {
+ vars []*Var
+}
+
+// NewTuple returns a new tuple for the given variables.
+func NewTuple(x ...*Var) *Tuple {
+ if len(x) > 0 {
+ return &Tuple{x}
+ }
+ return nil
+}
+
+// Len returns the number variables of tuple t.
+func (t *Tuple) Len() int {
+ if t != nil {
+ return len(t.vars)
+ }
+ return 0
+}
+
+// At returns the i'th variable of tuple t.
+func (t *Tuple) At(i int) *Var { return t.vars[i] }
+
+// A Signature represents a (non-builtin) function or method type.
+type Signature struct {
+ scope *Scope // function scope, always present
+ recv *Var // nil if not a method
+ params *Tuple // (incoming) parameters from left to right; or nil
+ results *Tuple // (outgoing) results from left to right; or nil
+ variadic bool // true if the last parameter's type is of the form ...T (or string, for append built-in only)
+}
+
+// NewSignature returns a new function type for the given receiver, parameters,
+// and results, either of which may be nil. If variadic is set, the function
+// is variadic, it must have at least one parameter, and the last parameter
+// must be of unnamed slice type.
+func NewSignature(scope *Scope, recv *Var, params, results *Tuple, variadic bool) *Signature {
+ // TODO(gri) Should we rely on the correct (non-nil) incoming scope
+ // or should this function allocate and populate a scope?
+ if variadic {
+ n := params.Len()
+ if n == 0 {
+ panic("types.NewSignature: variadic function must have at least one parameter")
+ }
+ if _, ok := params.At(n - 1).typ.(*Slice); !ok {
+ panic("types.NewSignature: variadic parameter must be of unnamed slice type")
+ }
+ }
+ return &Signature{scope, recv, params, results, variadic}
+}
+
+// Recv returns the receiver of signature s (if a method), or nil if a
+// function.
+//
+// For an abstract method, Recv returns the enclosing interface either
+// as a *Named or an *Interface. Due to embedding, an interface may
+// contain methods whose receiver type is a different interface.
+func (s *Signature) Recv() *Var { return s.recv }
+
+// Params returns the parameters of signature s, or nil.
+func (s *Signature) Params() *Tuple { return s.params }
+
+// Results returns the results of signature s, or nil.
+func (s *Signature) Results() *Tuple { return s.results }
+
+// Variadic reports whether the signature s is variadic.
+func (s *Signature) Variadic() bool { return s.variadic }
+
+// An Interface represents an interface type.
+type Interface struct {
+ methods []*Func // ordered list of explicitly declared methods
+ embeddeds []*Named // ordered list of explicitly embedded types
+
+ allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset)
+}
+
+// NewInterface returns a new interface for the given methods and embedded types.
+func NewInterface(methods []*Func, embeddeds []*Named) *Interface {
+ typ := new(Interface)
+
+ var mset objset
+ for _, m := range methods {
+ if mset.insert(m) != nil {
+ panic("multiple methods with the same name")
+ }
+ // set receiver
+ // TODO(gri) Ideally, we should use a named type here instead of
+ // typ, for less verbose printing of interface method signatures.
+ m.typ.(*Signature).recv = NewVar(m.pos, m.pkg, "", typ)
+ }
+ sort.Sort(byUniqueMethodName(methods))
+
+ if embeddeds == nil {
+ sort.Sort(byUniqueTypeName(embeddeds))
+ }
+
+ typ.methods = methods
+ typ.embeddeds = embeddeds
+ return typ
+}
+
+// NumExplicitMethods returns the number of explicitly declared methods of interface t.
+func (t *Interface) NumExplicitMethods() int { return len(t.methods) }
+
+// ExplicitMethod returns the i'th explicitly declared method of interface t for 0 <= i < t.NumExplicitMethods().
+// The methods are ordered by their unique Id.
+func (t *Interface) ExplicitMethod(i int) *Func { return t.methods[i] }
+
+// NumEmbeddeds returns the number of embedded types in interface t.
+func (t *Interface) NumEmbeddeds() int { return len(t.embeddeds) }
+
+// Embedded returns the i'th embedded type of interface t for 0 <= i < t.NumEmbeddeds().
+// The types are ordered by the corresponding TypeName's unique Id.
+func (t *Interface) Embedded(i int) *Named { return t.embeddeds[i] }
+
+// NumMethods returns the total number of methods of interface t.
+func (t *Interface) NumMethods() int { return len(t.allMethods) }
+
+// Method returns the i'th method of interface t for 0 <= i < t.NumMethods().
+// The methods are ordered by their unique Id.
+func (t *Interface) Method(i int) *Func { return t.allMethods[i] }
+
+// Empty returns true if t is the empty interface.
+func (t *Interface) Empty() bool { return len(t.allMethods) == 0 }
+
+// Complete computes the interface's method set. It must be called by users of
+// NewInterface after the interface's embedded types are fully defined and
+// before using the interface type in any way other than to form other types.
+// Complete returns the receiver.
+func (t *Interface) Complete() *Interface {
+ if t.allMethods != nil {
+ return t
+ }
+
+ var allMethods []*Func
+ if t.embeddeds == nil {
+ if t.methods == nil {
+ allMethods = make([]*Func, 0, 1)
+ } else {
+ allMethods = t.methods
+ }
+ } else {
+ allMethods = append(allMethods, t.methods...)
+ for _, et := range t.embeddeds {
+ it := et.Underlying().(*Interface)
+ it.Complete()
+ for _, tm := range it.allMethods {
+ // Make a copy of the method and adjust its receiver type.
+ newm := *tm
+ newmtyp := *tm.typ.(*Signature)
+ newm.typ = &newmtyp
+ newmtyp.recv = NewVar(newm.pos, newm.pkg, "", t)
+ allMethods = append(allMethods, &newm)
+ }
+ }
+ sort.Sort(byUniqueMethodName(allMethods))
+ }
+ t.allMethods = allMethods
+
+ return t
+}
+
+// A Map represents a map type.
+type Map struct {
+ key, elem Type
+}
+
+// NewMap returns a new map for the given key and element types.
+func NewMap(key, elem Type) *Map {
+ return &Map{key, elem}
+}
+
+// Key returns the key type of map m.
+func (m *Map) Key() Type { return m.key }
+
+// Elem returns the element type of map m.
+func (m *Map) Elem() Type { return m.elem }
+
+// A Chan represents a channel type.
+type Chan struct {
+ dir ChanDir
+ elem Type
+}
+
+// A ChanDir value indicates a channel direction.
+type ChanDir int
+
+// The direction of a channel is indicated by one of the following constants.
+const (
+ SendRecv ChanDir = iota
+ SendOnly
+ RecvOnly
+)
+
+// NewChan returns a new channel type for the given direction and element type.
+func NewChan(dir ChanDir, elem Type) *Chan {
+ return &Chan{dir, elem}
+}
+
+// Dir returns the direction of channel c.
+func (c *Chan) Dir() ChanDir { return c.dir }
+
+// Elem returns the element type of channel c.
+func (c *Chan) Elem() Type { return c.elem }
+
+// A Named represents a named type.
+type Named struct {
+ obj *TypeName // corresponding declared object
+ underlying Type // possibly a *Named during setup; never a *Named once set up completely
+ methods []*Func // methods declared for this type (not the method set of this type)
+}
+
+// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
+// The underlying type must not be a *Named.
+func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
+ if _, ok := underlying.(*Named); ok {
+ panic("types.NewNamed: underlying type must not be *Named")
+ }
+ typ := &Named{obj: obj, underlying: underlying, methods: methods}
+ if obj.typ == nil {
+ obj.typ = typ
+ }
+ return typ
+}
+
+// TypeName returns the type name for the named type t.
+func (t *Named) Obj() *TypeName { return t.obj }
+
+// NumMethods returns the number of explicit methods whose receiver is named type t.
+func (t *Named) NumMethods() int { return len(t.methods) }
+
+// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
+func (t *Named) Method(i int) *Func { return t.methods[i] }
+
+// SetUnderlying sets the underlying type and marks t as complete.
+// TODO(gri) determine if there's a better solution rather than providing this function
+func (t *Named) SetUnderlying(underlying Type) {
+ if underlying == nil {
+ panic("types.Named.SetUnderlying: underlying type must not be nil")
+ }
+ if _, ok := underlying.(*Named); ok {
+ panic("types.Named.SetUnderlying: underlying type must not be *Named")
+ }
+ t.underlying = underlying
+}
+
+// AddMethod adds method m unless it is already in the method list.
+// TODO(gri) find a better solution instead of providing this function
+func (t *Named) AddMethod(m *Func) {
+ if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 {
+ t.methods = append(t.methods, m)
+ }
+}
+
+// Implementations for Type methods.
+
+func (t *Basic) Underlying() Type { return t }
+func (t *Array) Underlying() Type { return t }
+func (t *Slice) Underlying() Type { return t }
+func (t *Struct) Underlying() Type { return t }
+func (t *Pointer) Underlying() Type { return t }
+func (t *Tuple) Underlying() Type { return t }
+func (t *Signature) Underlying() Type { return t }
+func (t *Interface) Underlying() Type { return t }
+func (t *Map) Underlying() Type { return t }
+func (t *Chan) Underlying() Type { return t }
+func (t *Named) Underlying() Type { return t.underlying }
+
+func (t *Basic) String() string { return TypeString(nil, t) }
+func (t *Array) String() string { return TypeString(nil, t) }
+func (t *Slice) String() string { return TypeString(nil, t) }
+func (t *Struct) String() string { return TypeString(nil, t) }
+func (t *Pointer) String() string { return TypeString(nil, t) }
+func (t *Tuple) String() string { return TypeString(nil, t) }
+func (t *Signature) String() string { return TypeString(nil, t) }
+func (t *Interface) String() string { return TypeString(nil, t) }
+func (t *Map) String() string { return TypeString(nil, t) }
+func (t *Chan) String() string { return TypeString(nil, t) }
+func (t *Named) String() string { return TypeString(nil, t) }
diff --git a/src/go/types/typestring.go b/src/go/types/typestring.go
new file mode 100644
index 0000000000..9a537e8177
--- /dev/null
+++ b/src/go/types/typestring.go
@@ -0,0 +1,266 @@
+// 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 printing of types.
+
+package types
+
+import (
+ "bytes"
+ "fmt"
+)
+
+// If GcCompatibilityMode is set, printing of types is modified
+// to match the representation of some types in the gc compiler:
+//
+// - byte and rune lose their alias name and simply stand for
+// uint8 and int32 respectively
+// - embedded interfaces get flattened (the embedding info is lost,
+// and certain recursive interface types cannot be printed anymore)
+//
+// This makes it easier to compare packages computed with the type-
+// checker vs packages imported from gc export data.
+//
+// Caution: This flag affects all uses of WriteType, globally.
+// It is only provided for testing in conjunction with
+// gc-generated data. It may be removed at any time.
+var GcCompatibilityMode bool
+
+// TypeString returns the string representation of typ.
+// Named types are printed package-qualified if they
+// do not belong to this package.
+func TypeString(this *Package, typ Type) string {
+ var buf bytes.Buffer
+ WriteType(&buf, this, typ)
+ return buf.String()
+}
+
+// WriteType writes the string representation of typ to buf.
+// Named types are printed package-qualified if they
+// do not belong to this package.
+func WriteType(buf *bytes.Buffer, this *Package, typ Type) {
+ writeType(buf, this, typ, make([]Type, 8))
+}
+
+func writeType(buf *bytes.Buffer, this *Package, typ Type, visited []Type) {
+ // Theoretically, this is a quadratic lookup algorithm, but in
+ // practice deeply nested composite types with unnamed component
+ // types are uncommon. This code is likely more efficient than
+ // using a map.
+ for _, t := range visited {
+ if t == typ {
+ fmt.Fprintf(buf, "○%T", typ) // cycle to typ
+ return
+ }
+ }
+ visited = append(visited, typ)
+
+ switch t := typ.(type) {
+ case nil:
+ buf.WriteString("")
+
+ case *Basic:
+ if t.kind == UnsafePointer {
+ buf.WriteString("unsafe.")
+ }
+ if GcCompatibilityMode {
+ // forget the alias names
+ switch t.kind {
+ case Byte:
+ t = Typ[Uint8]
+ case Rune:
+ t = Typ[Int32]
+ }
+ }
+ buf.WriteString(t.name)
+
+ case *Array:
+ fmt.Fprintf(buf, "[%d]", t.len)
+ writeType(buf, this, t.elem, visited)
+
+ case *Slice:
+ buf.WriteString("[]")
+ writeType(buf, this, t.elem, visited)
+
+ case *Struct:
+ buf.WriteString("struct{")
+ for i, f := range t.fields {
+ if i > 0 {
+ buf.WriteString("; ")
+ }
+ if !f.anonymous {
+ buf.WriteString(f.name)
+ buf.WriteByte(' ')
+ }
+ writeType(buf, this, f.typ, visited)
+ if tag := t.Tag(i); tag != "" {
+ fmt.Fprintf(buf, " %q", tag)
+ }
+ }
+ buf.WriteByte('}')
+
+ case *Pointer:
+ buf.WriteByte('*')
+ writeType(buf, this, t.base, visited)
+
+ case *Tuple:
+ writeTuple(buf, this, t, false, visited)
+
+ case *Signature:
+ buf.WriteString("func")
+ writeSignature(buf, this, t, visited)
+
+ case *Interface:
+ // We write the source-level methods and embedded types rather
+ // than the actual method set since resolved method signatures
+ // may have non-printable cycles if parameters have anonymous
+ // interface types that (directly or indirectly) embed the
+ // current interface. For instance, consider the result type
+ // of m:
+ //
+ // type T interface{
+ // m() interface{ T }
+ // }
+ //
+ buf.WriteString("interface{")
+ if GcCompatibilityMode {
+ // print flattened interface
+ // (useful to compare against gc-generated interfaces)
+ for i, m := range t.allMethods {
+ if i > 0 {
+ buf.WriteString("; ")
+ }
+ buf.WriteString(m.name)
+ writeSignature(buf, this, m.typ.(*Signature), visited)
+ }
+ } else {
+ // print explicit interface methods and embedded types
+ for i, m := range t.methods {
+ if i > 0 {
+ buf.WriteString("; ")
+ }
+ buf.WriteString(m.name)
+ writeSignature(buf, this, m.typ.(*Signature), visited)
+ }
+ for i, typ := range t.embeddeds {
+ if i > 0 || len(t.methods) > 0 {
+ buf.WriteString("; ")
+ }
+ writeType(buf, this, typ, visited)
+ }
+ }
+ buf.WriteByte('}')
+
+ case *Map:
+ buf.WriteString("map[")
+ writeType(buf, this, t.key, visited)
+ buf.WriteByte(']')
+ writeType(buf, this, t.elem, visited)
+
+ case *Chan:
+ var s string
+ var parens bool
+ switch t.dir {
+ case SendRecv:
+ s = "chan "
+ // chan (<-chan T) requires parentheses
+ if c, _ := t.elem.(*Chan); c != nil && c.dir == RecvOnly {
+ parens = true
+ }
+ case SendOnly:
+ s = "chan<- "
+ case RecvOnly:
+ s = "<-chan "
+ default:
+ panic("unreachable")
+ }
+ buf.WriteString(s)
+ if parens {
+ buf.WriteByte('(')
+ }
+ writeType(buf, this, t.elem, visited)
+ if parens {
+ buf.WriteByte(')')
+ }
+
+ case *Named:
+ s := ""
+ if obj := t.obj; obj != nil {
+ if pkg := obj.pkg; pkg != nil && pkg != this {
+ buf.WriteString(pkg.path)
+ buf.WriteByte('.')
+ }
+ // TODO(gri): function-local named types should be displayed
+ // differently from named types at package level to avoid
+ // ambiguity.
+ s = obj.name
+ }
+ buf.WriteString(s)
+
+ default:
+ // For externally defined implementations of Type.
+ buf.WriteString(t.String())
+ }
+}
+
+func writeTuple(buf *bytes.Buffer, this *Package, tup *Tuple, variadic bool, visited []Type) {
+ buf.WriteByte('(')
+ if tup != nil {
+ for i, v := range tup.vars {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ if v.name != "" {
+ buf.WriteString(v.name)
+ buf.WriteByte(' ')
+ }
+ typ := v.typ
+ if variadic && i == len(tup.vars)-1 {
+ if s, ok := typ.(*Slice); ok {
+ buf.WriteString("...")
+ typ = s.elem
+ } else {
+ // special case:
+ // append(s, "foo"...) leads to signature func([]byte, string...)
+ if t, ok := typ.Underlying().(*Basic); !ok || t.kind != String {
+ panic("internal error: string type expected")
+ }
+ writeType(buf, this, typ, visited)
+ buf.WriteString("...")
+ continue
+ }
+ }
+ writeType(buf, this, typ, visited)
+ }
+ }
+ buf.WriteByte(')')
+}
+
+// WriteSignature writes the representation of the signature sig to buf,
+// without a leading "func" keyword.
+// Named types are printed package-qualified if they
+// do not belong to this package.
+func WriteSignature(buf *bytes.Buffer, this *Package, sig *Signature) {
+ writeSignature(buf, this, sig, make([]Type, 8))
+}
+
+func writeSignature(buf *bytes.Buffer, this *Package, sig *Signature, visited []Type) {
+ writeTuple(buf, this, sig.params, sig.variadic, visited)
+
+ n := sig.results.Len()
+ if n == 0 {
+ // no result
+ return
+ }
+
+ buf.WriteByte(' ')
+ if n == 1 && sig.results.vars[0].name == "" {
+ // single unnamed result
+ writeType(buf, this, sig.results.vars[0].typ, visited)
+ return
+ }
+
+ // multiple or named result(s)
+ writeTuple(buf, this, sig.results, false, visited)
+}
diff --git a/src/go/types/typestring_test.go b/src/go/types/typestring_test.go
new file mode 100644
index 0000000000..ecc4ba83c0
--- /dev/null
+++ b/src/go/types/typestring_test.go
@@ -0,0 +1,161 @@
+// 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 types_test
+
+import (
+ "go/ast"
+ "go/importer"
+ "go/parser"
+ "go/token"
+ "testing"
+
+ . "go/types"
+)
+
+const filename = ""
+
+func makePkg(t *testing.T, src string) (*Package, error) {
+ fset := token.NewFileSet()
+ file, err := parser.ParseFile(fset, filename, src, parser.DeclarationErrors)
+ if err != nil {
+ return nil, err
+ }
+ // use the package name as package path
+ conf := Config{Importer: importer.Default()}
+ return conf.Check(file.Name.Name, fset, []*ast.File{file}, nil)
+}
+
+type testEntry struct {
+ src, str string
+}
+
+// dup returns a testEntry where both src and str are the same.
+func dup(s string) testEntry {
+ return testEntry{s, s}
+}
+
+// types that don't depend on any other type declarations
+var independentTestTypes = []testEntry{
+ // basic types
+ dup("int"),
+ dup("float32"),
+ dup("string"),
+
+ // arrays
+ dup("[10]int"),
+
+ // slices
+ dup("[]int"),
+ dup("[][]int"),
+
+ // structs
+ dup("struct{}"),
+ dup("struct{x int}"),
+ {`struct {
+ x, y int
+ z float32 "foo"
+ }`, `struct{x int; y int; z float32 "foo"}`},
+ {`struct {
+ string
+ elems []complex128
+ }`, `struct{string; elems []complex128}`},
+
+ // pointers
+ dup("*int"),
+ dup("***struct{}"),
+ dup("*struct{a int; b float32}"),
+
+ // functions
+ dup("func()"),
+ dup("func(x int)"),
+ {"func(x, y int)", "func(x int, y int)"},
+ {"func(x, y int, z string)", "func(x int, y int, z string)"},
+ dup("func(int)"),
+ {"func(int, string, byte)", "func(int, string, byte)"},
+
+ dup("func() int"),
+ {"func() (string)", "func() string"},
+ dup("func() (u int)"),
+ {"func() (u, v int, w string)", "func() (u int, v int, w string)"},
+
+ dup("func(int) string"),
+ dup("func(x int) string"),
+ dup("func(x int) (u string)"),
+ {"func(x, y int) (u string)", "func(x int, y int) (u string)"},
+
+ dup("func(...int) string"),
+ dup("func(x ...int) string"),
+ dup("func(x ...int) (u string)"),
+ {"func(x, y ...int) (u string)", "func(x int, y ...int) (u string)"},
+
+ // interfaces
+ dup("interface{}"),
+ dup("interface{m()}"),
+ dup(`interface{String() string; m(int) float32}`),
+
+ // maps
+ dup("map[string]int"),
+ {"map[struct{x, y int}][]byte", "map[struct{x int; y int}][]byte"},
+
+ // channels
+ dup("chan<- chan int"),
+ dup("chan<- <-chan int"),
+ dup("<-chan <-chan int"),
+ dup("chan (<-chan int)"),
+ dup("chan<- func()"),
+ dup("<-chan []func() int"),
+}
+
+// types that depend on other type declarations (src in TestTypes)
+var dependentTestTypes = []testEntry{
+ // interfaces
+ dup(`interface{io.Reader; io.Writer}`),
+ dup(`interface{m() int; io.Writer}`),
+ {`interface{m() interface{T}}`, `interface{m() interface{p.T}}`},
+}
+
+func TestTypeString(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ var tests []testEntry
+ tests = append(tests, independentTestTypes...)
+ tests = append(tests, dependentTestTypes...)
+
+ for _, test := range tests {
+ src := `package p; import "io"; type _ io.Writer; type T ` + test.src
+ pkg, err := makePkg(t, src)
+ if err != nil {
+ t.Errorf("%s: %s", src, err)
+ continue
+ }
+ typ := pkg.Scope().Lookup("T").Type().Underlying()
+ if got := typ.String(); got != test.str {
+ t.Errorf("%s: got %s, want %s", test.src, got, test.str)
+ }
+ }
+}
+
+func TestQualifiedTypeString(t *testing.T) {
+ p, _ := pkgFor("p.go", "package p; type T int", nil)
+ q, _ := pkgFor("q.go", "package q", nil)
+
+ pT := p.Scope().Lookup("T").Type()
+ for _, test := range []struct {
+ typ Type
+ this *Package
+ want string
+ }{
+ {pT, nil, "p.T"},
+ {pT, p, "T"},
+ {pT, q, "p.T"},
+ {NewPointer(pT), p, "*T"},
+ {NewPointer(pT), q, "*p.T"},
+ } {
+ if got := TypeString(test.this, test.typ); got != test.want {
+ t.Errorf("TypeString(%s, %s) = %s, want %s",
+ test.this, test.typ, got, test.want)
+ }
+ }
+}
diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go
new file mode 100644
index 0000000000..afd1dabc06
--- /dev/null
+++ b/src/go/types/typexpr.go
@@ -0,0 +1,720 @@
+// 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 type-checking of identifiers and type expressions.
+
+package types
+
+import (
+ "go/ast"
+ exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+ "go/token"
+ "sort"
+ "strconv"
+)
+
+// ident type-checks identifier e and initializes x with the value or type of e.
+// If an error occurred, x.mode is set to invalid.
+// For the meaning of def and path, see check.typ, below.
+//
+func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, path []*TypeName) {
+ x.mode = invalid
+ x.expr = e
+
+ scope, obj := check.scope.LookupParent(e.Name)
+ if obj == nil {
+ if e.Name == "_" {
+ check.errorf(e.Pos(), "cannot use _ as value or type")
+ } else {
+ check.errorf(e.Pos(), "undeclared name: %s", e.Name)
+ }
+ return
+ }
+ check.recordUse(e, obj)
+
+ check.objDecl(obj, def, path)
+ typ := obj.Type()
+ assert(typ != nil)
+
+ // The object may be dot-imported: If so, remove its package from
+ // the map of unused dot imports for the respective file scope.
+ // (This code is only needed for dot-imports. Without them,
+ // we only have to mark variables, see *Var case below).
+ if pkg := obj.Pkg(); pkg != check.pkg && pkg != nil {
+ delete(check.unusedDotImports[scope], pkg)
+ }
+
+ switch obj := obj.(type) {
+ case *PkgName:
+ check.errorf(e.Pos(), "use of package %s not in selector", obj.name)
+ return
+
+ case *Const:
+ check.addDeclDep(obj)
+ if typ == Typ[Invalid] {
+ return
+ }
+ if obj == universeIota {
+ if check.iota == nil {
+ check.errorf(e.Pos(), "cannot use iota outside constant declaration")
+ return
+ }
+ x.val = check.iota
+ } else {
+ x.val = obj.val
+ }
+ assert(x.val != nil)
+ x.mode = constant
+
+ case *TypeName:
+ x.mode = typexpr
+ // check for cycle
+ // (it's ok to iterate forward because each named type appears at most once in path)
+ for i, prev := range path {
+ if prev == obj {
+ check.errorf(obj.pos, "illegal cycle in declaration of %s", obj.name)
+ // print cycle
+ for _, obj := range path[i:] {
+ check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented
+ }
+ check.errorf(obj.Pos(), "\t%s", obj.Name())
+ // maintain x.mode == typexpr despite error
+ typ = Typ[Invalid]
+ break
+ }
+ }
+
+ case *Var:
+ if obj.pkg == check.pkg {
+ obj.used = true
+ }
+ check.addDeclDep(obj)
+ if typ == Typ[Invalid] {
+ return
+ }
+ x.mode = variable
+
+ case *Func:
+ check.addDeclDep(obj)
+ x.mode = value
+
+ case *Builtin:
+ x.id = obj.id
+ x.mode = builtin
+
+ case *Nil:
+ x.mode = value
+
+ default:
+ unreachable()
+ }
+
+ x.typ = typ
+}
+
+// typExpr type-checks the type expression e and returns its type, or Typ[Invalid].
+// If def != nil, e is the type specification for the named type def, declared
+// in a type declaration, and def.underlying will be set to the type of e before
+// any components of e are type-checked. Path contains the path of named types
+// referring to this type.
+//
+func (check *Checker) typExpr(e ast.Expr, def *Named, path []*TypeName) (T Type) {
+ if trace {
+ check.trace(e.Pos(), "%s", e)
+ check.indent++
+ defer func() {
+ check.indent--
+ check.trace(e.Pos(), "=> %s", T)
+ }()
+ }
+
+ T = check.typExprInternal(e, def, path)
+ assert(isTyped(T))
+ check.recordTypeAndValue(e, typexpr, T, nil)
+
+ return
+}
+
+func (check *Checker) typ(e ast.Expr) Type {
+ return check.typExpr(e, nil, nil)
+}
+
+// funcType type-checks a function or method type and returns its signature.
+func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast.FuncType) *Signature {
+ scope := NewScope(check.scope, "function")
+ check.recordScope(ftyp, scope)
+
+ recvList, _ := check.collectParams(scope, recvPar, false)
+ params, variadic := check.collectParams(scope, ftyp.Params, true)
+ results, _ := check.collectParams(scope, ftyp.Results, false)
+
+ if recvPar != nil {
+ // recv parameter list present (may be empty)
+ // spec: "The receiver is specified via an extra parameter section preceeding the
+ // method name. That parameter section must declare a single parameter, the receiver."
+ var recv *Var
+ switch len(recvList) {
+ case 0:
+ check.error(recvPar.Pos(), "method is missing receiver")
+ recv = NewParam(0, nil, "", Typ[Invalid]) // ignore recv below
+ default:
+ // more than one receiver
+ check.error(recvList[len(recvList)-1].Pos(), "method must have exactly one receiver")
+ fallthrough // continue with first receiver
+ case 1:
+ recv = recvList[0]
+ }
+ // spec: "The receiver type must be of the form T or *T where T is a type name."
+ // (ignore invalid types - error was reported before)
+ if t, _ := deref(recv.typ); t != Typ[Invalid] {
+ var err string
+ if T, _ := t.(*Named); T != nil {
+ // spec: "The type denoted by T is called the receiver base type; it must not
+ // be a pointer or interface type and it must be declared in the same package
+ // as the method."
+ if T.obj.pkg != check.pkg {
+ err = "type not defined in this package"
+ } else {
+ // TODO(gri) This is not correct if the underlying type is unknown yet.
+ switch u := T.underlying.(type) {
+ case *Basic:
+ // unsafe.Pointer is treated like a regular pointer
+ if u.kind == UnsafePointer {
+ err = "unsafe.Pointer"
+ }
+ case *Pointer, *Interface:
+ err = "pointer or interface type"
+ }
+ }
+ } else {
+ err = "basic or unnamed type"
+ }
+ if err != "" {
+ check.errorf(recv.pos, "invalid receiver %s (%s)", recv.typ, err)
+ // ok to continue
+ }
+ }
+ sig.recv = recv
+ }
+
+ sig.scope = scope
+ sig.params = NewTuple(params...)
+ sig.results = NewTuple(results...)
+ sig.variadic = variadic
+
+ return sig
+}
+
+// typExprInternal drives type checking of types.
+// Must only be called by typExpr.
+//
+func (check *Checker) typExprInternal(e ast.Expr, def *Named, path []*TypeName) Type {
+ switch e := e.(type) {
+ case *ast.BadExpr:
+ // ignore - error reported before
+
+ case *ast.Ident:
+ var x operand
+ check.ident(&x, e, def, path)
+
+ switch x.mode {
+ case typexpr:
+ typ := x.typ
+ def.setUnderlying(typ)
+ return typ
+ case invalid:
+ // ignore - error reported before
+ case novalue:
+ check.errorf(x.pos(), "%s used as type", &x)
+ default:
+ check.errorf(x.pos(), "%s is not a type", &x)
+ }
+
+ case *ast.SelectorExpr:
+ var x operand
+ check.selector(&x, e)
+
+ switch x.mode {
+ case typexpr:
+ typ := x.typ
+ def.setUnderlying(typ)
+ return typ
+ case invalid:
+ // ignore - error reported before
+ case novalue:
+ check.errorf(x.pos(), "%s used as type", &x)
+ default:
+ check.errorf(x.pos(), "%s is not a type", &x)
+ }
+
+ case *ast.ParenExpr:
+ return check.typExpr(e.X, def, path)
+
+ case *ast.ArrayType:
+ if e.Len != nil {
+ typ := new(Array)
+ def.setUnderlying(typ)
+ typ.len = check.arrayLength(e.Len)
+ typ.elem = check.typExpr(e.Elt, nil, path)
+ return typ
+
+ } else {
+ typ := new(Slice)
+ def.setUnderlying(typ)
+ typ.elem = check.typ(e.Elt)
+ return typ
+ }
+
+ case *ast.StructType:
+ typ := new(Struct)
+ def.setUnderlying(typ)
+ check.structType(typ, e, path)
+ return typ
+
+ case *ast.StarExpr:
+ typ := new(Pointer)
+ def.setUnderlying(typ)
+ typ.base = check.typ(e.X)
+ return typ
+
+ case *ast.FuncType:
+ typ := new(Signature)
+ def.setUnderlying(typ)
+ check.funcType(typ, nil, e)
+ return typ
+
+ case *ast.InterfaceType:
+ typ := new(Interface)
+ def.setUnderlying(typ)
+ check.interfaceType(typ, e, def, path)
+ return typ
+
+ case *ast.MapType:
+ typ := new(Map)
+ def.setUnderlying(typ)
+
+ typ.key = check.typ(e.Key)
+ typ.elem = check.typ(e.Value)
+
+ // spec: "The comparison operators == and != must be fully defined
+ // for operands of the key type; thus the key type must not be a
+ // function, map, or slice."
+ //
+ // Delay this check because it requires fully setup types;
+ // it is safe to continue in any case (was issue 6667).
+ check.delay(func() {
+ if !Comparable(typ.key) {
+ check.errorf(e.Key.Pos(), "invalid map key type %s", typ.key)
+ }
+ })
+
+ return typ
+
+ case *ast.ChanType:
+ typ := new(Chan)
+ def.setUnderlying(typ)
+
+ dir := SendRecv
+ switch e.Dir {
+ case ast.SEND | ast.RECV:
+ // nothing to do
+ case ast.SEND:
+ dir = SendOnly
+ case ast.RECV:
+ dir = RecvOnly
+ default:
+ check.invalidAST(e.Pos(), "unknown channel direction %d", e.Dir)
+ // ok to continue
+ }
+
+ typ.dir = dir
+ typ.elem = check.typ(e.Value)
+ return typ
+
+ default:
+ check.errorf(e.Pos(), "%s is not a type", e)
+ }
+
+ typ := Typ[Invalid]
+ def.setUnderlying(typ)
+ return typ
+}
+
+// typeOrNil type-checks the type expression (or nil value) e
+// and returns the typ of e, or nil.
+// If e is neither a type nor nil, typOrNil returns Typ[Invalid].
+//
+func (check *Checker) typOrNil(e ast.Expr) Type {
+ var x operand
+ check.rawExpr(&x, e, nil)
+ switch x.mode {
+ case invalid:
+ // ignore - error reported before
+ case novalue:
+ check.errorf(x.pos(), "%s used as type", &x)
+ case typexpr:
+ return x.typ
+ case value:
+ if x.isNil() {
+ return nil
+ }
+ fallthrough
+ default:
+ check.errorf(x.pos(), "%s is not a type", &x)
+ }
+ return Typ[Invalid]
+}
+
+func (check *Checker) arrayLength(e ast.Expr) int64 {
+ var x operand
+ check.expr(&x, e)
+ if x.mode != constant {
+ if x.mode != invalid {
+ check.errorf(x.pos(), "array length %s must be constant", &x)
+ }
+ return 0
+ }
+ if !x.isInteger() {
+ check.errorf(x.pos(), "array length %s must be integer", &x)
+ return 0
+ }
+ n, ok := exact.Int64Val(x.val)
+ if !ok || n < 0 {
+ check.errorf(x.pos(), "invalid array length %s", &x)
+ return 0
+ }
+ return n
+}
+
+func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, variadic bool) {
+ if list == nil {
+ return
+ }
+
+ var named, anonymous bool
+ for i, field := range list.List {
+ ftype := field.Type
+ if t, _ := ftype.(*ast.Ellipsis); t != nil {
+ ftype = t.Elt
+ if variadicOk && i == len(list.List)-1 {
+ variadic = true
+ } else {
+ check.invalidAST(field.Pos(), "... not permitted")
+ // ignore ... and continue
+ }
+ }
+ typ := check.typ(ftype)
+ // The parser ensures that f.Tag is nil and we don't
+ // care if a constructed AST contains a non-nil tag.
+ if len(field.Names) > 0 {
+ // named parameter
+ for _, name := range field.Names {
+ if name.Name == "" {
+ check.invalidAST(name.Pos(), "anonymous parameter")
+ // ok to continue
+ }
+ par := NewParam(name.Pos(), check.pkg, name.Name, typ)
+ check.declare(scope, name, par)
+ params = append(params, par)
+ }
+ named = true
+ } else {
+ // anonymous parameter
+ par := NewParam(ftype.Pos(), check.pkg, "", typ)
+ check.recordImplicit(field, par)
+ params = append(params, par)
+ anonymous = true
+ }
+ }
+
+ if named && anonymous {
+ check.invalidAST(list.Pos(), "list contains both named and anonymous parameters")
+ // ok to continue
+ }
+
+ // For a variadic function, change the last parameter's type from T to []T.
+ if variadic && len(params) > 0 {
+ last := params[len(params)-1]
+ last.typ = &Slice{elem: last.typ}
+ }
+
+ return
+}
+
+func (check *Checker) declareInSet(oset *objset, pos token.Pos, obj Object) bool {
+ if alt := oset.insert(obj); alt != nil {
+ check.errorf(pos, "%s redeclared", obj.Name())
+ check.reportAltDecl(alt)
+ return false
+ }
+ return true
+}
+
+func (check *Checker) interfaceType(iface *Interface, ityp *ast.InterfaceType, def *Named, path []*TypeName) {
+ // empty interface: common case
+ if ityp.Methods == nil {
+ return
+ }
+
+ // The parser ensures that field tags are nil and we don't
+ // care if a constructed AST contains non-nil tags.
+
+ // use named receiver type if available (for better error messages)
+ var recvTyp Type = iface
+ if def != nil {
+ recvTyp = def
+ }
+
+ // Phase 1: Collect explicitly declared methods, the corresponding
+ // signature (AST) expressions, and the list of embedded
+ // type (AST) expressions. Do not resolve signatures or
+ // embedded types yet to avoid cycles referring to this
+ // interface.
+
+ var (
+ mset objset
+ signatures []ast.Expr // list of corresponding method signatures
+ embedded []ast.Expr // list of embedded types
+ )
+ for _, f := range ityp.Methods.List {
+ if len(f.Names) > 0 {
+ // The parser ensures that there's only one method
+ // and we don't care if a constructed AST has more.
+ name := f.Names[0]
+ pos := name.Pos()
+ // spec: "As with all method sets, in an interface type,
+ // each method must have a unique non-blank name."
+ if name.Name == "_" {
+ check.errorf(pos, "invalid method name _")
+ continue
+ }
+ // Don't type-check signature yet - use an
+ // empty signature now and update it later.
+ // Since we know the receiver, set it up now
+ // (required to avoid crash in ptrRecv; see
+ // e.g. test case for issue 6638).
+ // TODO(gri) Consider marking methods signatures
+ // as incomplete, for better error messages. See
+ // also the T4 and T5 tests in testdata/cycles2.src.
+ sig := new(Signature)
+ sig.recv = NewVar(pos, check.pkg, "", recvTyp)
+ m := NewFunc(pos, check.pkg, name.Name, sig)
+ if check.declareInSet(&mset, pos, m) {
+ iface.methods = append(iface.methods, m)
+ iface.allMethods = append(iface.allMethods, m)
+ signatures = append(signatures, f.Type)
+ check.recordDef(name, m)
+ }
+ } else {
+ // embedded type
+ embedded = append(embedded, f.Type)
+ }
+ }
+
+ // Phase 2: Resolve embedded interfaces. Because an interface must not
+ // embed itself (directly or indirectly), each embedded interface
+ // can be fully resolved without depending on any method of this
+ // interface (if there is a cycle or another error, the embedded
+ // type resolves to an invalid type and is ignored).
+ // In particular, the list of methods for each embedded interface
+ // must be complete (it cannot depend on this interface), and so
+ // those methods can be added to the list of all methods of this
+ // interface.
+
+ for _, e := range embedded {
+ pos := e.Pos()
+ typ := check.typExpr(e, nil, path)
+ named, _ := typ.(*Named)
+ if named == nil {
+ if typ != Typ[Invalid] {
+ check.invalidAST(pos, "%s is not named type", typ)
+ }
+ continue
+ }
+ // determine underlying (possibly incomplete) type
+ // by following its forward chain
+ u := underlying(named)
+ embed, _ := u.(*Interface)
+ if embed == nil {
+ if u != Typ[Invalid] {
+ check.errorf(pos, "%s is not an interface", named)
+ }
+ continue
+ }
+ iface.embeddeds = append(iface.embeddeds, named)
+ // collect embedded methods
+ for _, m := range embed.allMethods {
+ if check.declareInSet(&mset, pos, m) {
+ iface.allMethods = append(iface.allMethods, m)
+ }
+ }
+ }
+
+ // Phase 3: At this point all methods have been collected for this interface.
+ // It is now safe to type-check the signatures of all explicitly
+ // declared methods, even if they refer to this interface via a cycle
+ // and embed the methods of this interface in a parameter of interface
+ // type.
+
+ for i, m := range iface.methods {
+ expr := signatures[i]
+ typ := check.typ(expr)
+ sig, _ := typ.(*Signature)
+ if sig == nil {
+ if typ != Typ[Invalid] {
+ check.invalidAST(expr.Pos(), "%s is not a method signature", typ)
+ }
+ continue // keep method with empty method signature
+ }
+ // update signature, but keep recv that was set up before
+ old := m.typ.(*Signature)
+ sig.recv = old.recv
+ *old = *sig // update signature (don't replace it!)
+ }
+
+ // TODO(gri) The list of explicit methods is only sorted for now to
+ // produce the same Interface as NewInterface. We may be able to
+ // claim source order in the future. Revisit.
+ sort.Sort(byUniqueMethodName(iface.methods))
+
+ // TODO(gri) The list of embedded types is only sorted for now to
+ // produce the same Interface as NewInterface. We may be able to
+ // claim source order in the future. Revisit.
+ sort.Sort(byUniqueTypeName(iface.embeddeds))
+
+ sort.Sort(byUniqueMethodName(iface.allMethods))
+}
+
+// byUniqueTypeName named type lists can be sorted by their unique type names.
+type byUniqueTypeName []*Named
+
+func (a byUniqueTypeName) Len() int { return len(a) }
+func (a byUniqueTypeName) Less(i, j int) bool { return a[i].obj.Id() < a[j].obj.Id() }
+func (a byUniqueTypeName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+// byUniqueMethodName method lists can be sorted by their unique method names.
+type byUniqueMethodName []*Func
+
+func (a byUniqueMethodName) Len() int { return len(a) }
+func (a byUniqueMethodName) Less(i, j int) bool { return a[i].Id() < a[j].Id() }
+func (a byUniqueMethodName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+func (check *Checker) tag(t *ast.BasicLit) string {
+ if t != nil {
+ if t.Kind == token.STRING {
+ if val, err := strconv.Unquote(t.Value); err == nil {
+ return val
+ }
+ }
+ check.invalidAST(t.Pos(), "incorrect tag syntax: %q", t.Value)
+ }
+ return ""
+}
+
+func (check *Checker) structType(styp *Struct, e *ast.StructType, path []*TypeName) {
+ list := e.Fields
+ if list == nil {
+ return
+ }
+
+ // struct fields and tags
+ var fields []*Var
+ var tags []string
+
+ // for double-declaration checks
+ var fset objset
+
+ // current field typ and tag
+ var typ Type
+ var tag string
+ // anonymous != nil indicates an anonymous field.
+ add := func(field *ast.Field, ident *ast.Ident, anonymous *TypeName, pos token.Pos) {
+ if tag != "" && tags == nil {
+ tags = make([]string, len(fields))
+ }
+ if tags != nil {
+ tags = append(tags, tag)
+ }
+
+ name := ident.Name
+ fld := NewField(pos, check.pkg, name, typ, anonymous != nil)
+ // spec: "Within a struct, non-blank field names must be unique."
+ if name == "_" || check.declareInSet(&fset, pos, fld) {
+ fields = append(fields, fld)
+ check.recordDef(ident, fld)
+ }
+ if anonymous != nil {
+ check.recordUse(ident, anonymous)
+ }
+ }
+
+ for _, f := range list.List {
+ typ = check.typExpr(f.Type, nil, path)
+ tag = check.tag(f.Tag)
+ if len(f.Names) > 0 {
+ // named fields
+ for _, name := range f.Names {
+ add(f, name, nil, name.Pos())
+ }
+ } else {
+ // anonymous field
+ name := anonymousFieldIdent(f.Type)
+ pos := f.Type.Pos()
+ t, isPtr := deref(typ)
+ switch t := t.(type) {
+ case *Basic:
+ if t == Typ[Invalid] {
+ // error was reported before
+ continue
+ }
+ // unsafe.Pointer is treated like a regular pointer
+ if t.kind == UnsafePointer {
+ check.errorf(pos, "anonymous field type cannot be unsafe.Pointer")
+ continue
+ }
+ add(f, name, Universe.Lookup(t.name).(*TypeName), pos)
+
+ case *Named:
+ // spec: "An embedded type must be specified as a type name
+ // T or as a pointer to a non-interface type name *T, and T
+ // itself may not be a pointer type."
+ switch u := t.underlying.(type) {
+ case *Basic:
+ // unsafe.Pointer is treated like a regular pointer
+ if u.kind == UnsafePointer {
+ check.errorf(pos, "anonymous field type cannot be unsafe.Pointer")
+ continue
+ }
+ case *Pointer:
+ check.errorf(pos, "anonymous field type cannot be a pointer")
+ continue
+ case *Interface:
+ if isPtr {
+ check.errorf(pos, "anonymous field type cannot be a pointer to an interface")
+ continue
+ }
+ }
+ add(f, name, t.obj, pos)
+
+ default:
+ check.invalidAST(pos, "anonymous field type %s must be named", typ)
+ }
+ }
+ }
+
+ styp.fields = fields
+ styp.tags = tags
+}
+
+func anonymousFieldIdent(e ast.Expr) *ast.Ident {
+ switch e := e.(type) {
+ case *ast.Ident:
+ return e
+ case *ast.StarExpr:
+ return anonymousFieldIdent(e.X)
+ case *ast.SelectorExpr:
+ return e.Sel
+ }
+ return nil // invalid anonymous field
+}
diff --git a/src/go/types/universe.go b/src/go/types/universe.go
new file mode 100644
index 0000000000..c02543e951
--- /dev/null
+++ b/src/go/types/universe.go
@@ -0,0 +1,223 @@
+// 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 the universe and unsafe package scopes.
+
+package types
+
+import (
+ exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove
+ "go/token"
+ "strings"
+)
+
+var (
+ Universe *Scope
+ Unsafe *Package
+ universeIota *Const
+ UniverseByte *Basic // uint8 alias, but has name "byte"
+ UniverseRune *Basic // int32 alias, but has name "rune"
+)
+
+var Typ = [...]*Basic{
+ Invalid: {Invalid, 0, "invalid type"},
+
+ Bool: {Bool, IsBoolean, "bool"},
+ Int: {Int, IsInteger, "int"},
+ Int8: {Int8, IsInteger, "int8"},
+ Int16: {Int16, IsInteger, "int16"},
+ Int32: {Int32, IsInteger, "int32"},
+ Int64: {Int64, IsInteger, "int64"},
+ Uint: {Uint, IsInteger | IsUnsigned, "uint"},
+ Uint8: {Uint8, IsInteger | IsUnsigned, "uint8"},
+ Uint16: {Uint16, IsInteger | IsUnsigned, "uint16"},
+ Uint32: {Uint32, IsInteger | IsUnsigned, "uint32"},
+ Uint64: {Uint64, IsInteger | IsUnsigned, "uint64"},
+ Uintptr: {Uintptr, IsInteger | IsUnsigned, "uintptr"},
+ Float32: {Float32, IsFloat, "float32"},
+ Float64: {Float64, IsFloat, "float64"},
+ Complex64: {Complex64, IsComplex, "complex64"},
+ Complex128: {Complex128, IsComplex, "complex128"},
+ String: {String, IsString, "string"},
+ UnsafePointer: {UnsafePointer, 0, "Pointer"},
+
+ UntypedBool: {UntypedBool, IsBoolean | IsUntyped, "untyped bool"},
+ UntypedInt: {UntypedInt, IsInteger | IsUntyped, "untyped int"},
+ UntypedRune: {UntypedRune, IsInteger | IsUntyped, "untyped rune"},
+ UntypedFloat: {UntypedFloat, IsFloat | IsUntyped, "untyped float"},
+ UntypedComplex: {UntypedComplex, IsComplex | IsUntyped, "untyped complex"},
+ UntypedString: {UntypedString, IsString | IsUntyped, "untyped string"},
+ UntypedNil: {UntypedNil, IsUntyped, "untyped nil"},
+}
+
+var aliases = [...]*Basic{
+ {Byte, IsInteger | IsUnsigned, "byte"},
+ {Rune, IsInteger, "rune"},
+}
+
+func defPredeclaredTypes() {
+ for _, t := range Typ {
+ def(NewTypeName(token.NoPos, nil, t.name, t))
+ }
+ for _, t := range aliases {
+ def(NewTypeName(token.NoPos, nil, t.name, t))
+ }
+
+ // Error has a nil package in its qualified name since it is in no package
+ res := NewVar(token.NoPos, nil, "", Typ[String])
+ sig := &Signature{results: NewTuple(res)}
+ err := NewFunc(token.NoPos, nil, "Error", sig)
+ typ := &Named{underlying: NewInterface([]*Func{err}, nil).Complete()}
+ sig.recv = NewVar(token.NoPos, nil, "", typ)
+ def(NewTypeName(token.NoPos, nil, "error", typ))
+}
+
+var predeclaredConsts = [...]struct {
+ name string
+ kind BasicKind
+ val exact.Value
+}{
+ {"true", UntypedBool, exact.MakeBool(true)},
+ {"false", UntypedBool, exact.MakeBool(false)},
+ {"iota", UntypedInt, exact.MakeInt64(0)},
+}
+
+func defPredeclaredConsts() {
+ for _, c := range predeclaredConsts {
+ def(NewConst(token.NoPos, nil, c.name, Typ[c.kind], c.val))
+ }
+}
+
+func defPredeclaredNil() {
+ def(&Nil{object{name: "nil", typ: Typ[UntypedNil]}})
+}
+
+// A builtinId is the id of a builtin function.
+type builtinId int
+
+const (
+ // universe scope
+ _Append builtinId = iota
+ _Cap
+ _Close
+ _Complex
+ _Copy
+ _Delete
+ _Imag
+ _Len
+ _Make
+ _New
+ _Panic
+ _Print
+ _Println
+ _Real
+ _Recover
+
+ // package unsafe
+ _Alignof
+ _Offsetof
+ _Sizeof
+
+ // testing support
+ _Assert
+ _Trace
+)
+
+var predeclaredFuncs = [...]struct {
+ name string
+ nargs int
+ variadic bool
+ kind exprKind
+}{
+ _Append: {"append", 1, true, expression},
+ _Cap: {"cap", 1, false, expression},
+ _Close: {"close", 1, false, statement},
+ _Complex: {"complex", 2, false, expression},
+ _Copy: {"copy", 2, false, statement},
+ _Delete: {"delete", 2, false, statement},
+ _Imag: {"imag", 1, false, expression},
+ _Len: {"len", 1, false, expression},
+ _Make: {"make", 1, true, expression},
+ _New: {"new", 1, false, expression},
+ _Panic: {"panic", 1, false, statement},
+ _Print: {"print", 0, true, statement},
+ _Println: {"println", 0, true, statement},
+ _Real: {"real", 1, false, expression},
+ _Recover: {"recover", 0, false, statement},
+
+ _Alignof: {"Alignof", 1, false, expression},
+ _Offsetof: {"Offsetof", 1, false, expression},
+ _Sizeof: {"Sizeof", 1, false, expression},
+
+ _Assert: {"assert", 1, false, statement},
+ _Trace: {"trace", 0, true, statement},
+}
+
+func defPredeclaredFuncs() {
+ for i := range predeclaredFuncs {
+ id := builtinId(i)
+ if id == _Assert || id == _Trace {
+ continue // only define these in testing environment
+ }
+ def(newBuiltin(id))
+ }
+}
+
+// DefPredeclaredTestFuncs defines the assert and trace built-ins.
+// These built-ins are intended for debugging and testing of this
+// package only.
+func DefPredeclaredTestFuncs() {
+ if Universe.Lookup("assert") != nil {
+ return // already defined
+ }
+ def(newBuiltin(_Assert))
+ def(newBuiltin(_Trace))
+}
+
+func init() {
+ Universe = NewScope(nil, "universe")
+ Unsafe = NewPackage("unsafe", "unsafe")
+ Unsafe.complete = true
+
+ defPredeclaredTypes()
+ defPredeclaredConsts()
+ defPredeclaredNil()
+ defPredeclaredFuncs()
+
+ universeIota = Universe.Lookup("iota").(*Const)
+ UniverseByte = Universe.Lookup("byte").(*TypeName).typ.(*Basic)
+ UniverseRune = Universe.Lookup("rune").(*TypeName).typ.(*Basic)
+}
+
+// Objects with names containing blanks are internal and not entered into
+// a scope. Objects with exported names are inserted in the unsafe package
+// scope; other objects are inserted in the universe scope.
+//
+func def(obj Object) {
+ name := obj.Name()
+ if strings.Index(name, " ") >= 0 {
+ return // nothing to do
+ }
+ // fix Obj link for named types
+ if typ, ok := obj.Type().(*Named); ok {
+ typ.obj = obj.(*TypeName)
+ }
+ // exported identifiers go into package unsafe
+ scope := Universe
+ if obj.Exported() {
+ scope = Unsafe.scope
+ // set Pkg field
+ switch obj := obj.(type) {
+ case *TypeName:
+ obj.pkg = Unsafe
+ case *Builtin:
+ obj.pkg = Unsafe
+ default:
+ unreachable()
+ }
+ }
+ if scope.Insert(obj) != nil {
+ panic("internal error: double declaration")
+ }
+}
diff --git a/src/hash/crc32/crc32.go b/src/hash/crc32/crc32.go
index 6a6b9473be..10867a79a8 100644
--- a/src/hash/crc32/crc32.go
+++ b/src/hash/crc32/crc32.go
@@ -5,6 +5,11 @@
// Package crc32 implements the 32-bit cyclic redundancy check, or CRC-32,
// checksum. See http://en.wikipedia.org/wiki/Cyclic_redundancy_check for
// information.
+//
+// Polynomials are represented in LSB-first form also known as reversed representation.
+//
+// See http://en.wikipedia.org/wiki/Mathematics_of_cyclic_redundancy_checks#Reversed_representations_and_reciprocal_polynomials
+// for information.
package crc32
import (
diff --git a/src/hash/crc32/example_test.go b/src/hash/crc32/example_test.go
new file mode 100644
index 0000000000..621bf83830
--- /dev/null
+++ b/src/hash/crc32/example_test.go
@@ -0,0 +1,28 @@
+// Copyright 2015 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 crc32_test
+
+import (
+ "fmt"
+ "hash/crc32"
+)
+
+func ExampleMakeTable() {
+ // In this package, the CRC polynomial is represented in reversed notation,
+ // or LSB-first representation.
+ //
+ // LSB-first representation is a hexadecimal number with n bits, in which the
+ // most significant bit represents the coefficient of x⁰ and the least significant
+ // bit represents the coefficient of xⁿ⁻¹ (the coefficient for xⁿ is implicit).
+ //
+ // For example, CRC32-Q, as defined by the following polynomial,
+ // x³²+ x³¹+ x²⁴+ x²²+ x¹⁶+ x¹⁴+ x⁸+ x⁷+ x⁵+ x³+ x¹+ x⁰
+ // has the reversed notation 0b11010101100000101000001010000001, so the value
+ // that should be passed to MakeTable is 0xD5828281.
+ crc32q := crc32.MakeTable(0xD5828281)
+ fmt.Printf("%08x\n", crc32.Checksum([]byte("Hello world"), crc32q))
+ // Output:
+ // 2964d064
+}
diff --git a/src/html/template/template.go b/src/html/template/template.go
index 64c0041c9c..bb9140a4da 100644
--- a/src/html/template/template.go
+++ b/src/html/template/template.go
@@ -51,6 +51,29 @@ func (t *Template) Templates() []*Template {
return m
}
+// Option sets options for the template. Options are described by
+// strings, either a simple string or "key=value". There can be at
+// most one equals sign in an option string. If the option string
+// is unrecognized or otherwise invalid, Option panics.
+//
+// Known options:
+//
+// missingkey: Control the behavior during execution if a map is
+// indexed with a key that is not present in the map.
+// "missingkey=default" or "missingkey=invalid"
+// The default behavior: Do nothing and continue execution.
+// If printed, the result of the index operation is the string
+// "".
+// "missingkey=zero"
+// The operation returns the zero value for the map type's element.
+// "missingkey=error"
+// Execution stops immediately with an error.
+//
+func (t *Template) Option(opt ...string) *Template {
+ t.text.Option(opt...)
+ return t
+}
+
// escape escapes all associated templates.
func (t *Template) escape() error {
t.nameSpace.mu.Lock()
diff --git a/src/html/template/transition.go b/src/html/template/transition.go
index b486fcd285..d2e028741a 100644
--- a/src/html/template/transition.go
+++ b/src/html/template/transition.go
@@ -183,24 +183,54 @@ func tHTMLCmt(c context, s []byte) (context, int) {
// specialTagEndMarkers maps element types to the character sequence that
// case-insensitively signals the end of the special tag body.
-var specialTagEndMarkers = [...]string{
- elementScript: " \t\n\f/")
+)
+
// tSpecialTagEnd is the context transition function for raw text and RCDATA
// element states.
func tSpecialTagEnd(c context, s []byte) (context, int) {
if c.element != elementNone {
- if i := strings.Index(strings.ToLower(string(s)), specialTagEndMarkers[c.element]); i != -1 {
+ if i := indexTagEnd(s, specialTagEndMarkers[c.element]); i != -1 {
return context{}, i
}
}
return c, len(s)
}
+// indexTagEnd finds the index of a special tag end in a case insensitive way, or returns -1
+func indexTagEnd(s []byte, tag []byte) int {
+ res := 0
+ plen := len(specialTagEndPrefix)
+ for len(s) > 0 {
+ // Try to find the tag end prefix first
+ i := bytes.Index(s, specialTagEndPrefix)
+ if i == -1 {
+ return i
+ }
+ s = s[i+plen:]
+ // Try to match the actual tag if there is still space for it
+ if len(tag) <= len(s) && bytes.EqualFold(tag, s[:len(tag)]) {
+ s = s[len(tag):]
+ // Check the tag is followed by a proper separator
+ if len(s) > 0 && bytes.IndexByte(tagEndSeparators, s[0]) != -1 {
+ return res + i
+ }
+ res += len(tag)
+ }
+ res += i + plen
+ }
+ return -1
+}
+
// tAttr is the context transition function for the attribute state.
func tAttr(c context, s []byte) (context, int) {
return c, len(s)
diff --git a/src/html/template/transition_test.go b/src/html/template/transition_test.go
new file mode 100644
index 0000000000..412a4c71b7
--- /dev/null
+++ b/src/html/template/transition_test.go
@@ -0,0 +1,60 @@
+// 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 template
+
+import (
+ "bytes"
+ "strings"
+ "testing"
+)
+
+func TestFindEndTag(t *testing.T) {
+ tests := []struct {
+ s, tag string
+ want int
+ }{
+ {"", "tag", -1},
+ {"hello hello", "textarea", 6},
+ {"hello hello", "textarea", 6},
+ {"hello ", "textarea", 6},
+ {"hello ", "tag", -1},
+ {"hello tag ", "textarea", 22},
+ {" ", "textarea", 0},
+ {"
", "textarea", 13},
+ {"
", "textarea", 13},
+ {"
", "textarea", 13},
+ {"
", "textarea", 14},
+ {"<", "script", 1},
+ {"", "textarea", -1},
+ }
+ for _, test := range tests {
+ if got := indexTagEnd([]byte(test.s), []byte(test.tag)); test.want != got {
+ t.Errorf("%q/%q: want\n\t%d\nbut got\n\t%d", test.s, test.tag, test.want, got)
+ }
+ }
+}
+
+func BenchmarkTemplateSpecialTags(b *testing.B) {
+
+ r := struct {
+ Name, Gift string
+ }{"Aunt Mildred", "bone china tea set"}
+
+ h1 := " "
+ h2 := ""
+ html := strings.Repeat(h1, 100) + h2 + strings.Repeat(h1, 100) + h2
+
+ var buf bytes.Buffer
+ for i := 0; i < b.N; i++ {
+ tmpl := Must(New("foo").Parse(html))
+ if err := tmpl.Execute(&buf, r); err != nil {
+ b.Fatal(err)
+ }
+ buf.Reset()
+ }
+}
diff --git a/src/image/color/color.go b/src/image/color/color.go
index 00bd8fd9b1..cae059b6da 100644
--- a/src/image/color/color.go
+++ b/src/image/color/color.go
@@ -271,32 +271,39 @@ func (p Palette) Convert(c Color) Color {
}
// Index returns the index of the palette color closest to c in Euclidean
-// R,G,B space.
+// R,G,B,A space.
func (p Palette) Index(c Color) int {
// A batch version of this computation is in image/draw/draw.go.
- cr, cg, cb, _ := c.RGBA()
- ret, bestSSD := 0, uint32(1<<32-1)
+ cr, cg, cb, ca := c.RGBA()
+ ret, bestSum := 0, uint32(1<<32-1)
for i, v := range p {
- vr, vg, vb, _ := v.RGBA()
- // We shift by 1 bit to avoid potential uint32 overflow in
- // sum-squared-difference.
- delta := (int32(cr) - int32(vr)) >> 1
- ssd := uint32(delta * delta)
- delta = (int32(cg) - int32(vg)) >> 1
- ssd += uint32(delta * delta)
- delta = (int32(cb) - int32(vb)) >> 1
- ssd += uint32(delta * delta)
- if ssd < bestSSD {
- if ssd == 0 {
+ vr, vg, vb, va := v.RGBA()
+ sum := sqDiff(cr, vr) + sqDiff(cg, vg) + sqDiff(cb, vb) + sqDiff(ca, va)
+ if sum < bestSum {
+ if sum == 0 {
return i
}
- ret, bestSSD = i, ssd
+ ret, bestSum = i, sum
}
}
return ret
}
+// sqDiff returns the squared-difference of x and y, shifted by 2 so that
+// adding four of those won't overflow a uint32.
+//
+// x and y are both assumed to be in the range [0, 0xffff].
+func sqDiff(x, y uint32) uint32 {
+ var d uint32
+ if x > y {
+ d = x - y
+ } else {
+ d = y - x
+ }
+ return (d * d) >> 2
+}
+
// Standard colors.
var (
Black = Gray16{0}
diff --git a/src/image/color/ycbcr.go b/src/image/color/ycbcr.go
index bbaaf7e188..f8c1326b3c 100644
--- a/src/image/color/ycbcr.go
+++ b/src/image/color/ycbcr.go
@@ -158,11 +158,11 @@ func RGBToCMYK(r, g, b uint8) (uint8, uint8, uint8, uint8) {
// CMYKToRGB converts a CMYK quadruple to an RGB triple.
func CMYKToRGB(c, m, y, k uint8) (uint8, uint8, uint8) {
- w := uint32(0xff - k)
- r := uint32(0xff-c) * w / 0xff
- g := uint32(0xff-m) * w / 0xff
- b := uint32(0xff-y) * w / 0xff
- return uint8(r), uint8(g), uint8(b)
+ w := uint32(0xffff - uint32(k)*0x101)
+ r := uint32(0xffff-uint32(c)*0x101) * w / 0xffff
+ g := uint32(0xffff-uint32(m)*0x101) * w / 0xffff
+ b := uint32(0xffff-uint32(y)*0x101) * w / 0xffff
+ return uint8(r >> 8), uint8(g >> 8), uint8(b >> 8)
}
// CMYK represents a fully opaque CMYK color, having 8 bits for each of cyan,
@@ -174,8 +174,14 @@ type CMYK struct {
}
func (c CMYK) RGBA() (uint32, uint32, uint32, uint32) {
- r, g, b := CMYKToRGB(c.C, c.M, c.Y, c.K)
- return uint32(r) * 0x101, uint32(g) * 0x101, uint32(b) * 0x101, 0xffff
+ // This code is a copy of the CMYKToRGB function above, except that it
+ // returns values in the range [0, 0xffff] instead of [0, 0xff].
+
+ w := uint32(0xffff - uint32(c.K)*0x101)
+ r := uint32(0xffff-uint32(c.C)*0x101) * w / 0xffff
+ g := uint32(0xffff-uint32(c.M)*0x101) * w / 0xffff
+ b := uint32(0xffff-uint32(c.Y)*0x101) * w / 0xffff
+ return uint32(r), uint32(g), uint32(b), 0xffff
}
// CMYKModel is the Model for CMYK colors.
diff --git a/src/image/color/ycbcr_test.go b/src/image/color/ycbcr_test.go
index 94c23dd9ea..6bf53f1fd3 100644
--- a/src/image/color/ycbcr_test.go
+++ b/src/image/color/ycbcr_test.go
@@ -18,14 +18,34 @@ func delta(x, y uint8) uint8 {
// TestYCbCrRoundtrip tests that a subset of RGB space can be converted to YCbCr
// and back to within 1/256 tolerance.
func TestYCbCrRoundtrip(t *testing.T) {
- for r := 0; r < 255; r += 7 {
- for g := 0; g < 255; g += 5 {
- for b := 0; b < 255; b += 3 {
+ for r := 0; r < 256; r += 7 {
+ for g := 0; g < 256; g += 5 {
+ for b := 0; b < 256; b += 3 {
r0, g0, b0 := uint8(r), uint8(g), uint8(b)
y, cb, cr := RGBToYCbCr(r0, g0, b0)
r1, g1, b1 := YCbCrToRGB(y, cb, cr)
if delta(r0, r1) > 1 || delta(g0, g1) > 1 || delta(b0, b1) > 1 {
- t.Fatalf("r0, g0, b0 = %d, %d, %d r1, g1, b1 = %d, %d, %d", r0, g0, b0, r1, g1, b1)
+ t.Fatalf("\nr0, g0, b0 = %d, %d, %d\nr1, g1, b1 = %d, %d, %d", r0, g0, b0, r1, g1, b1)
+ }
+ }
+ }
+ }
+}
+
+// TestYCbCrToRGBConsistency tests that calling the RGBA method (16 bit color)
+// then truncating to 8 bits is equivalent to calling the YCbCrToRGB function (8
+// bit color).
+func TestYCbCrToRGBConsistency(t *testing.T) {
+ for y := 0; y < 256; y += 7 {
+ for cb := 0; cb < 256; cb += 5 {
+ for cr := 0; cr < 256; cr += 3 {
+ x := YCbCr{uint8(y), uint8(cb), uint8(cr)}
+ r0, g0, b0, _ := x.RGBA()
+ r1, g1, b1 := uint8(r0>>8), uint8(g0>>8), uint8(b0>>8)
+ r2, g2, b2 := YCbCrToRGB(x.Y, x.Cb, x.Cr)
+ if r1 != r2 || g1 != g2 || b1 != b2 {
+ t.Fatalf("y, cb, cr = %d, %d, %d\nr1, g1, b1 = %d, %d, %d\nr2, g2, b2 = %d, %d, %d",
+ y, cb, cr, r1, g1, b1, r2, g2, b2)
}
}
}
@@ -35,16 +55,64 @@ func TestYCbCrRoundtrip(t *testing.T) {
// TestCMYKRoundtrip tests that a subset of RGB space can be converted to CMYK
// and back to within 1/256 tolerance.
func TestCMYKRoundtrip(t *testing.T) {
- for r := 0; r < 255; r += 7 {
- for g := 0; g < 255; g += 5 {
- for b := 0; b < 255; b += 3 {
+ for r := 0; r < 256; r += 7 {
+ for g := 0; g < 256; g += 5 {
+ for b := 0; b < 256; b += 3 {
r0, g0, b0 := uint8(r), uint8(g), uint8(b)
c, m, y, k := RGBToCMYK(r0, g0, b0)
r1, g1, b1 := CMYKToRGB(c, m, y, k)
if delta(r0, r1) > 1 || delta(g0, g1) > 1 || delta(b0, b1) > 1 {
- t.Fatalf("r0, g0, b0 = %d, %d, %d r1, g1, b1 = %d, %d, %d", r0, g0, b0, r1, g1, b1)
+ t.Fatalf("\nr0, g0, b0 = %d, %d, %d\nr1, g1, b1 = %d, %d, %d", r0, g0, b0, r1, g1, b1)
}
}
}
}
}
+
+// TestCMYKToRGBConsistency tests that calling the RGBA method (16 bit color)
+// then truncating to 8 bits is equivalent to calling the CMYKToRGB function (8
+// bit color).
+func TestCMYKToRGBConsistency(t *testing.T) {
+ for c := 0; c < 256; c += 7 {
+ for m := 0; m < 256; m += 5 {
+ for y := 0; y < 256; y += 3 {
+ for k := 0; k < 256; k += 11 {
+ x := CMYK{uint8(c), uint8(m), uint8(y), uint8(k)}
+ r0, g0, b0, _ := x.RGBA()
+ r1, g1, b1 := uint8(r0>>8), uint8(g0>>8), uint8(b0>>8)
+ r2, g2, b2 := CMYKToRGB(x.C, x.M, x.Y, x.K)
+ if r1 != r2 || g1 != g2 || b1 != b2 {
+ t.Fatalf("c, m, y, k = %d, %d, %d, %d\nr1, g1, b1 = %d, %d, %d\nr2, g2, b2 = %d, %d, %d",
+ c, m, y, k, r1, g1, b1, r2, g2, b2)
+ }
+ }
+ }
+ }
+ }
+}
+
+func TestPalette(t *testing.T) {
+ p := Palette{
+ RGBA{0xff, 0xff, 0xff, 0xff},
+ RGBA{0x80, 0x00, 0x00, 0xff},
+ RGBA{0x7f, 0x00, 0x00, 0x7f},
+ RGBA{0x00, 0x00, 0x00, 0x7f},
+ RGBA{0x00, 0x00, 0x00, 0x00},
+ RGBA{0x40, 0x40, 0x40, 0x40},
+ }
+ // Check that, for a Palette with no repeated colors, the closest color to
+ // each element is itself.
+ for i, c := range p {
+ j := p.Index(c)
+ if i != j {
+ t.Errorf("Index(%v): got %d (color = %v), want %d", c, j, p[j], i)
+ }
+ }
+ // Check that finding the closest color considers alpha, not just red,
+ // green and blue.
+ got := p.Convert(RGBA{0x80, 0x00, 0x00, 0x80})
+ want := RGBA{0x7f, 0x00, 0x00, 0x7f}
+ if got != want {
+ t.Errorf("got %v, want %v", got, want)
+ }
+}
diff --git a/src/image/draw/clip_test.go b/src/image/draw/clip_test.go
index 5903338a07..0abf53e5c7 100644
--- a/src/image/draw/clip_test.go
+++ b/src/image/draw/clip_test.go
@@ -185,17 +185,17 @@ func TestClip(t *testing.T) {
}
// Check that the clipped rectangle is contained by the dst / src / mask
- // rectangles, in their respective co-ordinate spaces.
+ // rectangles, in their respective coordinate spaces.
if !r.In(c.dr) {
t.Errorf("%s: c.dr %v does not contain r %v", c.desc, c.dr, r)
}
- // sr is r translated into src's co-ordinate space.
+ // sr is r translated into src's coordinate space.
sr := r.Add(c.sp.Sub(c.dr.Min))
if !sr.In(c.sr) {
t.Errorf("%s: c.sr %v does not contain sr %v", c.desc, c.sr, sr)
}
if !c.nilMask {
- // mr is r translated into mask's co-ordinate space.
+ // mr is r translated into mask's coordinate space.
mr := r.Add(c.mp.Sub(c.dr.Min))
if !mr.In(c.mr) {
t.Errorf("%s: c.mr %v does not contain mr %v", c.desc, c.mr, mr)
diff --git a/src/image/draw/draw.go b/src/image/draw/draw.go
index 420fd05e36..ee1126cd08 100644
--- a/src/image/draw/draw.go
+++ b/src/image/draw/draw.go
@@ -68,7 +68,7 @@ func (floydSteinberg) Draw(dst Image, r image.Rectangle, src image.Image, sp ima
}
// clip clips r against each image's bounds (after translating into the
-// destination image's co-ordinate space) and shifts the points sp and mp by
+// destination image's coordinate space) and shifts the points sp and mp by
// the same amount as the change in r.Min.
func clip(dst Image, r *image.Rectangle, src image.Image, sp *image.Point, mask image.Image, mp *image.Point) {
orig := r.Min
@@ -336,9 +336,11 @@ func drawCopySrc(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.P
ddelta = dst.Stride
sdelta = src.Stride
} else {
- // If the source start point is higher than the destination start point, then we compose the rows
- // in bottom-up order instead of top-down. Unlike the drawCopyOver function, we don't have to
- // check the x co-ordinates because the built-in copy function can handle overlapping slices.
+ // If the source start point is higher than the destination start
+ // point, then we compose the rows in bottom-up order instead of
+ // top-down. Unlike the drawCopyOver function, we don't have to check
+ // the x coordinates because the built-in copy function can handle
+ // overlapping slices.
d0 += (dy - 1) * dst.Stride
s0 += (dy - 1) * src.Stride
ddelta = -dst.Stride
@@ -552,6 +554,20 @@ func clamp(i int32) int32 {
return i
}
+// sqDiff returns the squared-difference of x and y, shifted by 2 so that
+// adding four of those won't overflow a uint32.
+//
+// x and y are both assumed to be in the range [0, 0xffff].
+func sqDiff(x, y int32) uint32 {
+ var d uint32
+ if x > y {
+ d = uint32(x - y)
+ } else {
+ d = uint32(y - x)
+ }
+ return (d * d) >> 2
+}
+
func drawPaletted(dst Image, r image.Rectangle, src image.Image, sp image.Point, floydSteinberg bool) {
// TODO(nigeltao): handle the case where the dst and src overlap.
// Does it even make sense to try and do Floyd-Steinberg whilst
@@ -561,14 +577,15 @@ func drawPaletted(dst Image, r image.Rectangle, src image.Image, sp image.Point,
// dst.At. The dst.Set equivalent is a batch version of the algorithm
// used by color.Palette's Index method in image/color/color.go, plus
// optional Floyd-Steinberg error diffusion.
- palette, pix, stride := [][3]int32(nil), []byte(nil), 0
+ palette, pix, stride := [][4]int32(nil), []byte(nil), 0
if p, ok := dst.(*image.Paletted); ok {
- palette = make([][3]int32, len(p.Palette))
+ palette = make([][4]int32, len(p.Palette))
for i, col := range p.Palette {
- r, g, b, _ := col.RGBA()
+ r, g, b, a := col.RGBA()
palette[i][0] = int32(r)
palette[i][1] = int32(g)
palette[i][2] = int32(b)
+ palette[i][3] = int32(a)
}
pix, stride = p.Pix[p.PixOffset(r.Min.X, r.Min.Y):], p.Stride
}
@@ -576,10 +593,10 @@ func drawPaletted(dst Image, r image.Rectangle, src image.Image, sp image.Point,
// quantErrorCurr and quantErrorNext are the Floyd-Steinberg quantization
// errors that have been propagated to the pixels in the current and next
// rows. The +2 simplifies calculation near the edges.
- var quantErrorCurr, quantErrorNext [][3]int32
+ var quantErrorCurr, quantErrorNext [][4]int32
if floydSteinberg {
- quantErrorCurr = make([][3]int32, r.Dx()+2)
- quantErrorNext = make([][3]int32, r.Dx()+2)
+ quantErrorCurr = make([][4]int32, r.Dx()+2)
+ quantErrorNext = make([][4]int32, r.Dx()+2)
}
// Loop over each source pixel.
@@ -588,30 +605,25 @@ func drawPaletted(dst Image, r image.Rectangle, src image.Image, sp image.Point,
for x := 0; x != r.Dx(); x++ {
// er, eg and eb are the pixel's R,G,B values plus the
// optional Floyd-Steinberg error.
- sr, sg, sb, _ := src.At(sp.X+x, sp.Y+y).RGBA()
- er, eg, eb := int32(sr), int32(sg), int32(sb)
+ sr, sg, sb, sa := src.At(sp.X+x, sp.Y+y).RGBA()
+ er, eg, eb, ea := int32(sr), int32(sg), int32(sb), int32(sa)
if floydSteinberg {
er = clamp(er + quantErrorCurr[x+1][0]/16)
eg = clamp(eg + quantErrorCurr[x+1][1]/16)
eb = clamp(eb + quantErrorCurr[x+1][2]/16)
+ ea = clamp(ea + quantErrorCurr[x+1][3]/16)
}
if palette != nil {
- // Find the closest palette color in Euclidean R,G,B space: the
- // one that minimizes sum-squared-difference. We shift by 1 bit
- // to avoid potential uint32 overflow in sum-squared-difference.
+ // Find the closest palette color in Euclidean R,G,B,A space:
+ // the one that minimizes sum-squared-difference.
// TODO(nigeltao): consider smarter algorithms.
- bestIndex, bestSSD := 0, uint32(1<<32-1)
+ bestIndex, bestSum := 0, uint32(1<<32-1)
for index, p := range palette {
- delta := (er - p[0]) >> 1
- ssd := uint32(delta * delta)
- delta = (eg - p[1]) >> 1
- ssd += uint32(delta * delta)
- delta = (eb - p[2]) >> 1
- ssd += uint32(delta * delta)
- if ssd < bestSSD {
- bestIndex, bestSSD = index, ssd
- if ssd == 0 {
+ sum := sqDiff(er, p[0]) + sqDiff(eg, p[1]) + sqDiff(eb, p[2]) + sqDiff(ea, p[3])
+ if sum < bestSum {
+ bestIndex, bestSum = index, sum
+ if sum == 0 {
break
}
}
@@ -624,11 +636,13 @@ func drawPaletted(dst Image, r image.Rectangle, src image.Image, sp image.Point,
er -= int32(palette[bestIndex][0])
eg -= int32(palette[bestIndex][1])
eb -= int32(palette[bestIndex][2])
+ ea -= int32(palette[bestIndex][3])
} else {
out.R = uint16(er)
out.G = uint16(eg)
out.B = uint16(eb)
+ out.A = uint16(ea)
// The third argument is &out instead of out (and out is
// declared outside of the inner loop) to avoid the implicit
// conversion to color.Color here allocating memory in the
@@ -638,32 +652,37 @@ func drawPaletted(dst Image, r image.Rectangle, src image.Image, sp image.Point,
if !floydSteinberg {
continue
}
- sr, sg, sb, _ = dst.At(r.Min.X+x, r.Min.Y+y).RGBA()
+ sr, sg, sb, sa = dst.At(r.Min.X+x, r.Min.Y+y).RGBA()
er -= int32(sr)
eg -= int32(sg)
eb -= int32(sb)
+ ea -= int32(sa)
}
// Propagate the Floyd-Steinberg quantization error.
quantErrorNext[x+0][0] += er * 3
quantErrorNext[x+0][1] += eg * 3
quantErrorNext[x+0][2] += eb * 3
+ quantErrorNext[x+0][3] += ea * 3
quantErrorNext[x+1][0] += er * 5
quantErrorNext[x+1][1] += eg * 5
quantErrorNext[x+1][2] += eb * 5
+ quantErrorNext[x+1][3] += ea * 5
quantErrorNext[x+2][0] += er * 1
quantErrorNext[x+2][1] += eg * 1
quantErrorNext[x+2][2] += eb * 1
+ quantErrorNext[x+2][3] += ea * 1
quantErrorCurr[x+2][0] += er * 7
quantErrorCurr[x+2][1] += eg * 7
quantErrorCurr[x+2][2] += eb * 7
+ quantErrorCurr[x+2][3] += ea * 7
}
// Recycle the quantization error buffers.
if floydSteinberg {
quantErrorCurr, quantErrorNext = quantErrorNext, quantErrorCurr
for i := range quantErrorNext {
- quantErrorNext[i] = [3]int32{}
+ quantErrorNext[i] = [4]int32{}
}
}
}
diff --git a/src/image/gif/reader.go b/src/image/gif/reader.go
index 5a863e204f..b71e041e78 100644
--- a/src/image/gif/reader.go
+++ b/src/image/gif/reader.go
@@ -32,15 +32,20 @@ type reader interface {
// Masks etc.
const (
// Fields.
- fColorMapFollows = 1 << 7
-
- // Image fields.
- ifLocalColorTable = 1 << 7
- ifInterlace = 1 << 6
- ifPixelSizeMask = 7
+ fColorTable = 1 << 7
+ fInterlace = 1 << 6
+ fColorTableBitsMask = 7
// Graphic control flags.
gcTransparentColorSet = 1 << 0
+ gcDisposalMethodMask = 7 << 2
+)
+
+// Disposal Methods.
+const (
+ DisposalNone = 0x01
+ DisposalBackground = 0x02
+ DisposalPrevious = 0x03
)
// Section indicators.
@@ -66,14 +71,10 @@ type decoder struct {
vers string
width int
height int
- flags byte
- headerFields byte
- backgroundIndex byte
loopCount int
delayTime int
-
- // Unused from header.
- aspect byte
+ backgroundIndex byte
+ disposalMethod byte
// From image descriptor.
imageFields byte
@@ -83,13 +84,13 @@ type decoder struct {
hasTransparentIndex bool
// Computed.
- pixelSize uint
- globalColorMap color.Palette
+ globalColorTable color.Palette
// Used when decoding.
- delay []int
- image []*image.Paletted
- tmp [1024]byte // must be at least 768 so we can read color map
+ delay []int
+ disposal []byte
+ image []*image.Paletted
+ tmp [1024]byte // must be at least 768 so we can read color table
}
// blockReader parses the block structure of GIF image data, which
@@ -122,7 +123,7 @@ func (b *blockReader) Read(p []byte) (int, error) {
b.err = io.EOF
return 0, b.err
}
- b.slice = b.tmp[0:blockLen]
+ b.slice = b.tmp[:blockLen]
if _, b.err = io.ReadFull(b.r, b.slice); b.err != nil {
return 0, b.err
}
@@ -149,12 +150,6 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error {
return nil
}
- if d.headerFields&fColorMapFollows != 0 {
- if d.globalColorMap, err = d.readColorMap(); err != nil {
- return err
- }
- }
-
for {
c, err := d.r.ReadByte()
if err != nil {
@@ -171,19 +166,19 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error {
if err != nil {
return err
}
- useLocalColorMap := d.imageFields&fColorMapFollows != 0
- if useLocalColorMap {
- m.Palette, err = d.readColorMap()
+ useLocalColorTable := d.imageFields&fColorTable != 0
+ if useLocalColorTable {
+ m.Palette, err = d.readColorTable(d.imageFields)
if err != nil {
return err
}
} else {
- m.Palette = d.globalColorMap
+ m.Palette = d.globalColorTable
}
if d.hasTransparentIndex && int(d.transparentIndex) < len(m.Palette) {
- if !useLocalColorMap {
- // Clone the global color map.
- m.Palette = append(color.Palette(nil), d.globalColorMap...)
+ if !useLocalColorTable {
+ // Clone the global color table.
+ m.Palette = append(color.Palette(nil), d.globalColorTable...)
}
m.Palette[d.transparentIndex] = color.RGBA{}
}
@@ -229,12 +224,13 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error {
}
// Undo the interlacing if necessary.
- if d.imageFields&ifInterlace != 0 {
+ if d.imageFields&fInterlace != 0 {
uninterlace(m)
}
d.image = append(d.image, m)
d.delay = append(d.delay, d.delayTime)
+ d.disposal = append(d.disposal, d.disposalMethod)
// The GIF89a spec, Section 23 (Graphic Control Extension) says:
// "The scope of this extension is the first graphic rendering block
// to follow." We therefore reset the GCE fields to zero.
@@ -254,44 +250,40 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error {
}
func (d *decoder) readHeaderAndScreenDescriptor() error {
- _, err := io.ReadFull(d.r, d.tmp[0:13])
+ _, err := io.ReadFull(d.r, d.tmp[:13])
if err != nil {
return err
}
- d.vers = string(d.tmp[0:6])
+ d.vers = string(d.tmp[:6])
if d.vers != "GIF87a" && d.vers != "GIF89a" {
return fmt.Errorf("gif: can't recognize format %s", d.vers)
}
d.width = int(d.tmp[6]) + int(d.tmp[7])<<8
d.height = int(d.tmp[8]) + int(d.tmp[9])<<8
- d.headerFields = d.tmp[10]
- d.backgroundIndex = d.tmp[11]
- d.aspect = d.tmp[12]
+ if fields := d.tmp[10]; fields&fColorTable != 0 {
+ d.backgroundIndex = d.tmp[11]
+ // readColorTable overwrites the contents of d.tmp, but that's OK.
+ if d.globalColorTable, err = d.readColorTable(fields); err != nil {
+ return err
+ }
+ }
+ // d.tmp[12] is the Pixel Aspect Ratio, which is ignored.
d.loopCount = -1
- d.pixelSize = uint(d.headerFields&7) + 1
return nil
}
-func (d *decoder) readColorMap() (color.Palette, error) {
- if d.pixelSize > 8 {
- return nil, fmt.Errorf("gif: can't handle %d bits per pixel", d.pixelSize)
- }
- numColors := 1 << d.pixelSize
- if d.imageFields&ifLocalColorTable != 0 {
- numColors = 1 << ((d.imageFields & ifPixelSizeMask) + 1)
- }
- numValues := 3 * numColors
- _, err := io.ReadFull(d.r, d.tmp[0:numValues])
+func (d *decoder) readColorTable(fields byte) (color.Palette, error) {
+ n := 1 << (1 + uint(fields&fColorTableBitsMask))
+ _, err := io.ReadFull(d.r, d.tmp[:3*n])
if err != nil {
- return nil, fmt.Errorf("gif: short read on color map: %s", err)
+ return nil, fmt.Errorf("gif: short read on color table: %s", err)
}
- colorMap := make(color.Palette, numColors)
- j := 0
- for i := range colorMap {
- colorMap[i] = color.RGBA{d.tmp[j+0], d.tmp[j+1], d.tmp[j+2], 0xFF}
+ j, p := 0, make(color.Palette, n)
+ for i := range p {
+ p[i] = color.RGBA{d.tmp[j+0], d.tmp[j+1], d.tmp[j+2], 0xFF}
j += 3
}
- return colorMap, nil
+ return p, nil
}
func (d *decoder) readExtension() error {
@@ -318,7 +310,7 @@ func (d *decoder) readExtension() error {
return fmt.Errorf("gif: unknown extension 0x%.2x", extension)
}
if size > 0 {
- if _, err := io.ReadFull(d.r, d.tmp[0:size]); err != nil {
+ if _, err := io.ReadFull(d.r, d.tmp[:size]); err != nil {
return err
}
}
@@ -343,12 +335,13 @@ func (d *decoder) readExtension() error {
}
func (d *decoder) readGraphicControl() error {
- if _, err := io.ReadFull(d.r, d.tmp[0:6]); err != nil {
+ if _, err := io.ReadFull(d.r, d.tmp[:6]); err != nil {
return fmt.Errorf("gif: can't read graphic control: %s", err)
}
- d.flags = d.tmp[1]
+ flags := d.tmp[1]
+ d.disposalMethod = (flags & gcDisposalMethodMask) >> 2
d.delayTime = int(d.tmp[2]) | int(d.tmp[3])<<8
- if d.flags&gcTransparentColorSet != 0 {
+ if flags&gcTransparentColorSet != 0 {
d.transparentIndex = d.tmp[4]
d.hasTransparentIndex = true
}
@@ -356,7 +349,7 @@ func (d *decoder) readGraphicControl() error {
}
func (d *decoder) newImageFromDescriptor() (*image.Paletted, error) {
- if _, err := io.ReadFull(d.r, d.tmp[0:9]); err != nil {
+ if _, err := io.ReadFull(d.r, d.tmp[:9]); err != nil {
return nil, fmt.Errorf("gif: can't read image descriptor: %s", err)
}
left := int(d.tmp[0]) + int(d.tmp[1])<<8
@@ -380,7 +373,7 @@ func (d *decoder) readBlock() (int, error) {
if n == 0 || err != nil {
return 0, err
}
- return io.ReadFull(d.r, d.tmp[0:n])
+ return io.ReadFull(d.r, d.tmp[:n])
}
// interlaceScan defines the ordering for a pass of the interlace algorithm.
@@ -429,6 +422,24 @@ type GIF struct {
Image []*image.Paletted // The successive images.
Delay []int // The successive delay times, one per frame, in 100ths of a second.
LoopCount int // The loop count.
+ // Disposal is the successive disposal methods, one per frame. For
+ // backwards compatibility, a nil Disposal is valid to pass to EncodeAll,
+ // and implies that each frame's disposal method is 0 (no disposal
+ // specified).
+ Disposal []byte
+ // Config is the global color table (palette), width and height. A nil or
+ // empty-color.Palette Config.ColorModel means that each frame has its own
+ // color table and there is no global color table. Each frame's bounds must
+ // be within the rectangle defined by the two points (0, 0) and
+ // (Config.Width, Config.Height).
+ //
+ // For backwards compatibility, a zero-valued Config is valid to pass to
+ // EncodeAll, and implies that the overall GIF's width and height equals
+ // the first frame's bounds' Rectangle.Max point.
+ Config image.Config
+ // BackgroundIndex is the background index in the global color table, for
+ // use with the DisposalBackground disposal method.
+ BackgroundIndex byte
}
// DecodeAll reads a GIF image from r and returns the sequential frames
@@ -442,6 +453,13 @@ func DecodeAll(r io.Reader) (*GIF, error) {
Image: d.image,
LoopCount: d.loopCount,
Delay: d.delay,
+ Disposal: d.disposal,
+ Config: image.Config{
+ ColorModel: d.globalColorTable,
+ Width: d.width,
+ Height: d.height,
+ },
+ BackgroundIndex: d.backgroundIndex,
}
return gif, nil
}
@@ -454,7 +472,7 @@ func DecodeConfig(r io.Reader) (image.Config, error) {
return image.Config{}, err
}
return image.Config{
- ColorModel: d.globalColorMap,
+ ColorModel: d.globalColorTable,
Width: d.width,
Height: d.height,
}, nil
diff --git a/src/image/gif/reader_test.go b/src/image/gif/reader_test.go
index 7b6f504367..94bd0a7c94 100644
--- a/src/image/gif/reader_test.go
+++ b/src/image/gif/reader_test.go
@@ -17,8 +17,8 @@ import (
const (
headerStr = "GIF89a" +
"\x02\x00\x01\x00" + // width=2, height=1
- "\x80\x00\x00" // headerFields=(a color map of 2 pixels), backgroundIndex, aspect
- paletteStr = "\x10\x20\x30\x40\x50\x60" // the color map, also known as a palette
+ "\x80\x00\x00" // headerFields=(a color table of 2 pixels), backgroundIndex, aspect
+ paletteStr = "\x10\x20\x30\x40\x50\x60" // the color table, also known as a palette
trailerStr = "\x3b"
)
@@ -141,7 +141,7 @@ var testGIF = []byte{
'G', 'I', 'F', '8', '9', 'a',
1, 0, 1, 0, // w=1, h=1 (6)
128, 0, 0, // headerFields, bg, aspect (10)
- 0, 0, 0, 1, 1, 1, // color map and graphics control (13)
+ 0, 0, 0, 1, 1, 1, // color table and graphics control (13)
0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0xff, 0x00, // (19)
// frame 1 (0,0 - 1,1)
0x2c,
diff --git a/src/image/gif/writer.go b/src/image/gif/writer.go
index 49abde704c..dd317901d4 100644
--- a/src/image/gif/writer.go
+++ b/src/image/gif/writer.go
@@ -6,6 +6,7 @@ package gif
import (
"bufio"
+ "bytes"
"compress/lzw"
"errors"
"image"
@@ -52,9 +53,13 @@ type encoder struct {
w writer
err error
// g is a reference to the data that is being encoded.
- g *GIF
- // buf is a scratch buffer. It must be at least 768 so we can write the color map.
- buf [1024]byte
+ g GIF
+ // globalCT is the size in bytes of the global color table.
+ globalCT int
+ // buf is a scratch buffer. It must be at least 256 for the blockWriter.
+ buf [256]byte
+ globalColorTable [3 * 256]byte
+ localColorTable [3 * 256]byte
}
// blockWriter writes the block structure of GIF image data, which
@@ -116,18 +121,27 @@ func (e *encoder) writeHeader() {
return
}
- pm := e.g.Image[0]
// Logical screen width and height.
- writeUint16(e.buf[0:2], uint16(pm.Bounds().Dx()))
- writeUint16(e.buf[2:4], uint16(pm.Bounds().Dy()))
+ writeUint16(e.buf[0:2], uint16(e.g.Config.Width))
+ writeUint16(e.buf[2:4], uint16(e.g.Config.Height))
e.write(e.buf[:4])
- // All frames have a local color table, so a global color table
- // is not needed.
- e.buf[0] = 0x00
- e.buf[1] = 0x00 // Background Color Index.
- e.buf[2] = 0x00 // Pixel Aspect Ratio.
- e.write(e.buf[:3])
+ if p, ok := e.g.Config.ColorModel.(color.Palette); ok && len(p) > 0 {
+ paddedSize := log2(len(p)) // Size of Global Color Table: 2^(1+n).
+ e.buf[0] = fColorTable | uint8(paddedSize)
+ e.buf[1] = e.g.BackgroundIndex
+ e.buf[2] = 0x00 // Pixel Aspect Ratio.
+ e.write(e.buf[:3])
+ e.globalCT = encodeColorTable(e.globalColorTable[:], p, paddedSize)
+ e.write(e.globalColorTable[:e.globalCT])
+ } else {
+ // All frames have a local color table, so a global color table
+ // is not needed.
+ e.buf[0] = 0x00
+ e.buf[1] = 0x00 // Background Color Index.
+ e.buf[2] = 0x00 // Pixel Aspect Ratio.
+ e.write(e.buf[:3])
+ }
// Add animation info if necessary.
if len(e.g.Image) > 1 {
@@ -147,28 +161,25 @@ func (e *encoder) writeHeader() {
}
}
-func (e *encoder) writeColorTable(p color.Palette, size int) {
- if e.err != nil {
- return
- }
-
- for i := 0; i < log2Lookup[size]; i++ {
+func encodeColorTable(dst []byte, p color.Palette, size int) int {
+ n := log2Lookup[size]
+ for i := 0; i < n; i++ {
if i < len(p) {
r, g, b, _ := p[i].RGBA()
- e.buf[3*i+0] = uint8(r >> 8)
- e.buf[3*i+1] = uint8(g >> 8)
- e.buf[3*i+2] = uint8(b >> 8)
+ dst[3*i+0] = uint8(r >> 8)
+ dst[3*i+1] = uint8(g >> 8)
+ dst[3*i+2] = uint8(b >> 8)
} else {
// Pad with black.
- e.buf[3*i+0] = 0x00
- e.buf[3*i+1] = 0x00
- e.buf[3*i+2] = 0x00
+ dst[3*i+0] = 0x00
+ dst[3*i+1] = 0x00
+ dst[3*i+2] = 0x00
}
}
- e.write(e.buf[:3*log2Lookup[size]])
+ return 3 * n
}
-func (e *encoder) writeImageBlock(pm *image.Paletted, delay int) {
+func (e *encoder) writeImageBlock(pm *image.Paletted, delay int, disposal byte) {
if e.err != nil {
return
}
@@ -179,10 +190,14 @@ func (e *encoder) writeImageBlock(pm *image.Paletted, delay int) {
}
b := pm.Bounds()
- if b.Dx() >= 1<<16 || b.Dy() >= 1<<16 || b.Min.X < 0 || b.Min.X >= 1<<16 || b.Min.Y < 0 || b.Min.Y >= 1<<16 {
+ if b.Min.X < 0 || b.Max.X >= 1<<16 || b.Min.Y < 0 || b.Max.Y >= 1<<16 {
e.err = errors.New("gif: image block is too large to encode")
return
}
+ if !b.In(image.Rectangle{Max: image.Point{e.g.Config.Width, e.g.Config.Height}}) {
+ e.err = errors.New("gif: image block is out of bounds")
+ return
+ }
transparentIndex := -1
for i, c := range pm.Palette {
@@ -192,14 +207,14 @@ func (e *encoder) writeImageBlock(pm *image.Paletted, delay int) {
}
}
- if delay > 0 || transparentIndex != -1 {
+ if delay > 0 || disposal != 0 || transparentIndex != -1 {
e.buf[0] = sExtension // Extension Introducer.
e.buf[1] = gcLabel // Graphic Control Label.
e.buf[2] = gcBlockSize // Block Size.
if transparentIndex != -1 {
- e.buf[3] = 0x01
+ e.buf[3] = 0x01 | disposal<<2
} else {
- e.buf[3] = 0x00
+ e.buf[3] = 0x00 | disposal<<2
}
writeUint16(e.buf[4:6], uint16(delay)) // Delay Time (1/100ths of a second)
@@ -220,11 +235,15 @@ func (e *encoder) writeImageBlock(pm *image.Paletted, delay int) {
e.write(e.buf[:9])
paddedSize := log2(len(pm.Palette)) // Size of Local Color Table: 2^(1+n).
- // Interlacing is not supported.
- e.writeByte(0x80 | uint8(paddedSize))
-
- // Local Color Table.
- e.writeColorTable(pm.Palette, paddedSize)
+ ct := encodeColorTable(e.localColorTable[:], pm.Palette, paddedSize)
+ if ct != e.globalCT || !bytes.Equal(e.globalColorTable[:ct], e.localColorTable[:ct]) {
+ // Use a local color table.
+ e.writeByte(fColorTable | uint8(paddedSize))
+ e.write(e.localColorTable[:ct])
+ } else {
+ // Use the global color table.
+ e.writeByte(0)
+ }
litWidth := paddedSize + 1
if litWidth < 2 {
@@ -281,7 +300,23 @@ func EncodeAll(w io.Writer, g *GIF) error {
g.LoopCount = 0
}
- e := encoder{g: g}
+ e := encoder{g: *g}
+ // The GIF.Disposal, GIF.Config and GIF.BackgroundIndex fields were added
+ // in Go 1.5. Valid Go 1.4 code, such as when the Disposal field is omitted
+ // in a GIF struct literal, should still produce valid GIFs.
+ if e.g.Disposal != nil && len(e.g.Image) != len(e.g.Disposal) {
+ return errors.New("gif: mismatched image and disposal lengths")
+ }
+ if e.g.Config == (image.Config{}) {
+ p := g.Image[0].Bounds().Max
+ e.g.Config.Width = p.X
+ e.g.Config.Height = p.Y
+ } else if e.g.Config.ColorModel != nil {
+ if _, ok := e.g.Config.ColorModel.(color.Palette); !ok {
+ return errors.New("gif: GIF color model must be a color.Palette")
+ }
+ }
+
if ww, ok := w.(writer); ok {
e.w = ww
} else {
@@ -290,7 +325,11 @@ func EncodeAll(w io.Writer, g *GIF) error {
e.writeHeader()
for i, pm := range g.Image {
- e.writeImageBlock(pm, g.Delay[i])
+ disposal := uint8(0)
+ if g.Disposal != nil {
+ disposal = g.Disposal[i]
+ }
+ e.writeImageBlock(pm, g.Delay[i], disposal)
}
e.writeByte(sTrailer)
e.flush()
@@ -326,8 +365,22 @@ func Encode(w io.Writer, m image.Image, o *Options) error {
opts.Drawer.Draw(pm, b, m, image.ZP)
}
+ // When calling Encode instead of EncodeAll, the single-frame image is
+ // translated such that its top-left corner is (0, 0), so that the single
+ // frame completely fills the overall GIF's bounds.
+ if pm.Rect.Min != (image.Point{}) {
+ dup := *pm
+ dup.Rect = dup.Rect.Sub(dup.Rect.Min)
+ pm = &dup
+ }
+
return EncodeAll(w, &GIF{
Image: []*image.Paletted{pm},
Delay: []int{0},
+ Config: image.Config{
+ ColorModel: pm.Palette,
+ Width: b.Dx(),
+ Height: b.Dy(),
+ },
})
}
diff --git a/src/image/gif/writer_test.go b/src/image/gif/writer_test.go
index 93306ffdb3..db61a5c3c2 100644
--- a/src/image/gif/writer_test.go
+++ b/src/image/gif/writer_test.go
@@ -8,10 +8,12 @@ import (
"bytes"
"image"
"image/color"
+ "image/color/palette"
_ "image/png"
"io/ioutil"
"math/rand"
"os"
+ "reflect"
"testing"
)
@@ -125,55 +127,317 @@ func TestSubImage(t *testing.T) {
}
}
+// palettesEqual reports whether two color.Palette values are equal, ignoring
+// any trailing opaque-black palette entries.
+func palettesEqual(p, q color.Palette) bool {
+ n := len(p)
+ if n > len(q) {
+ n = len(q)
+ }
+ for i := 0; i < n; i++ {
+ if p[i] != q[i] {
+ return false
+ }
+ }
+ for i := n; i < len(p); i++ {
+ r, g, b, a := p[i].RGBA()
+ if r != 0 || g != 0 || b != 0 || a != 0xffff {
+ return false
+ }
+ }
+ for i := n; i < len(q); i++ {
+ r, g, b, a := q[i].RGBA()
+ if r != 0 || g != 0 || b != 0 || a != 0xffff {
+ return false
+ }
+ }
+ return true
+}
+
var frames = []string{
"../testdata/video-001.gif",
"../testdata/video-005.gray.gif",
}
-func TestEncodeAll(t *testing.T) {
+func testEncodeAll(t *testing.T, go1Dot5Fields bool, useGlobalColorModel bool) {
+ const width, height = 150, 103
+
g0 := &GIF{
Image: make([]*image.Paletted, len(frames)),
Delay: make([]int, len(frames)),
LoopCount: 5,
}
for i, f := range frames {
- m, err := readGIF(f)
+ g, err := readGIF(f)
if err != nil {
t.Fatal(f, err)
}
- g0.Image[i] = m.Image[0]
+ m := g.Image[0]
+ if m.Bounds().Dx() != width || m.Bounds().Dy() != height {
+ t.Fatalf("frame %d had unexpected bounds: got %v, want width/height = %d/%d",
+ i, m.Bounds(), width, height)
+ }
+ g0.Image[i] = m
}
+ // The GIF.Disposal, GIF.Config and GIF.BackgroundIndex fields were added
+ // in Go 1.5. Valid Go 1.4 or earlier code should still produce valid GIFs.
+ //
+ // On the following line, color.Model is an interface type, and
+ // color.Palette is a concrete (slice) type.
+ globalColorModel, backgroundIndex := color.Model(color.Palette(nil)), uint8(0)
+ if useGlobalColorModel {
+ globalColorModel, backgroundIndex = color.Palette(palette.WebSafe), uint8(1)
+ }
+ if go1Dot5Fields {
+ g0.Disposal = make([]byte, len(g0.Image))
+ for i := range g0.Disposal {
+ g0.Disposal[i] = DisposalNone
+ }
+ g0.Config = image.Config{
+ ColorModel: globalColorModel,
+ Width: width,
+ Height: height,
+ }
+ g0.BackgroundIndex = backgroundIndex
+ }
+
var buf bytes.Buffer
if err := EncodeAll(&buf, g0); err != nil {
t.Fatal("EncodeAll:", err)
}
- g1, err := DecodeAll(&buf)
+ encoded := buf.Bytes()
+ config, err := DecodeConfig(bytes.NewReader(encoded))
+ if err != nil {
+ t.Fatal("DecodeConfig:", err)
+ }
+ g1, err := DecodeAll(bytes.NewReader(encoded))
if err != nil {
t.Fatal("DecodeAll:", err)
}
+
+ if !reflect.DeepEqual(config, g1.Config) {
+ t.Errorf("DecodeConfig inconsistent with DecodeAll")
+ }
+ if !palettesEqual(g1.Config.ColorModel.(color.Palette), globalColorModel.(color.Palette)) {
+ t.Errorf("unexpected global color model")
+ }
+ if w, h := g1.Config.Width, g1.Config.Height; w != width || h != height {
+ t.Errorf("got config width * height = %d * %d, want %d * %d", w, h, width, height)
+ }
+
if g0.LoopCount != g1.LoopCount {
t.Errorf("loop counts differ: %d and %d", g0.LoopCount, g1.LoopCount)
}
+ if backgroundIndex != g1.BackgroundIndex {
+ t.Errorf("background indexes differ: %d and %d", backgroundIndex, g1.BackgroundIndex)
+ }
+ if len(g0.Image) != len(g1.Image) {
+ t.Fatalf("image lengths differ: %d and %d", len(g0.Image), len(g1.Image))
+ }
+ if len(g1.Image) != len(g1.Delay) {
+ t.Fatalf("image and delay lengths differ: %d and %d", len(g1.Image), len(g1.Delay))
+ }
+ if len(g1.Image) != len(g1.Disposal) {
+ t.Fatalf("image and disposal lengths differ: %d and %d", len(g1.Image), len(g1.Disposal))
+ }
+
for i := range g0.Image {
m0, m1 := g0.Image[i], g1.Image[i]
if m0.Bounds() != m1.Bounds() {
- t.Errorf("%s, bounds differ: %v and %v", frames[i], m0.Bounds(), m1.Bounds())
+ t.Errorf("frame %d: bounds differ: %v and %v", i, m0.Bounds(), m1.Bounds())
}
d0, d1 := g0.Delay[i], g1.Delay[i]
if d0 != d1 {
- t.Errorf("%s: delay values differ: %d and %d", frames[i], d0, d1)
+ t.Errorf("frame %d: delay values differ: %d and %d", i, d0, d1)
+ }
+ p0, p1 := uint8(0), g1.Disposal[i]
+ if go1Dot5Fields {
+ p0 = DisposalNone
+ }
+ if p0 != p1 {
+ t.Errorf("frame %d: disposal values differ: %d and %d", i, p0, p1)
}
}
+}
- g1.Delay = make([]int, 1)
- if err := EncodeAll(ioutil.Discard, g1); err == nil {
+func TestEncodeAllGo1Dot4(t *testing.T) { testEncodeAll(t, false, false) }
+func TestEncodeAllGo1Dot5(t *testing.T) { testEncodeAll(t, true, false) }
+func TestEncodeAllGo1Dot5GlobalColorModel(t *testing.T) { testEncodeAll(t, true, true) }
+
+func TestEncodeMismatchDelay(t *testing.T) {
+ images := make([]*image.Paletted, 2)
+ for i := range images {
+ images[i] = image.NewPaletted(image.Rect(0, 0, 5, 5), palette.Plan9)
+ }
+
+ g0 := &GIF{
+ Image: images,
+ Delay: make([]int, 1),
+ }
+ if err := EncodeAll(ioutil.Discard, g0); err == nil {
t.Error("expected error from mismatched delay and image slice lengths")
}
+
+ g1 := &GIF{
+ Image: images,
+ Delay: make([]int, len(images)),
+ Disposal: make([]byte, 1),
+ }
+ for i := range g1.Disposal {
+ g1.Disposal[i] = DisposalNone
+ }
+ if err := EncodeAll(ioutil.Discard, g1); err == nil {
+ t.Error("expected error from mismatched disposal and image slice lengths")
+ }
+}
+
+func TestEncodeZeroGIF(t *testing.T) {
if err := EncodeAll(ioutil.Discard, &GIF{}); err == nil {
t.Error("expected error from providing empty gif")
}
}
+func TestEncodeAllFramesOutOfBounds(t *testing.T) {
+ images := []*image.Paletted{
+ image.NewPaletted(image.Rect(0, 0, 5, 5), palette.Plan9),
+ image.NewPaletted(image.Rect(2, 2, 8, 8), palette.Plan9),
+ image.NewPaletted(image.Rect(3, 3, 4, 4), palette.Plan9),
+ }
+ for _, upperBound := range []int{6, 10} {
+ g := &GIF{
+ Image: images,
+ Delay: make([]int, len(images)),
+ Disposal: make([]byte, len(images)),
+ Config: image.Config{
+ Width: upperBound,
+ Height: upperBound,
+ },
+ }
+ err := EncodeAll(ioutil.Discard, g)
+ if upperBound >= 8 {
+ if err != nil {
+ t.Errorf("upperBound=%d: %v", upperBound, err)
+ }
+ } else {
+ if err == nil {
+ t.Errorf("upperBound=%d: got nil error, want non-nil", upperBound)
+ }
+ }
+ }
+}
+
+func TestEncodeNonZeroMinPoint(t *testing.T) {
+ points := []image.Point{
+ image.Point{-8, -9},
+ image.Point{-4, -4},
+ image.Point{-3, +3},
+ image.Point{+0, +0},
+ image.Point{+2, +2},
+ }
+ for _, p := range points {
+ src := image.NewPaletted(image.Rectangle{Min: p, Max: p.Add(image.Point{6, 6})}, palette.Plan9)
+ var buf bytes.Buffer
+ if err := Encode(&buf, src, nil); err != nil {
+ t.Errorf("p=%v: Encode: %v", p, err)
+ continue
+ }
+ m, err := Decode(&buf)
+ if err != nil {
+ t.Errorf("p=%v: Decode: %v", p, err)
+ continue
+ }
+ if got, want := m.Bounds(), image.Rect(0, 0, 6, 6); got != want {
+ t.Errorf("p=%v: got %v, want %v", p, got, want)
+ }
+ }
+}
+
+func TestEncodeImplicitConfigSize(t *testing.T) {
+ // For backwards compatibility for Go 1.4 and earlier code, the Config
+ // field is optional, and if zero, the width and height is implied by the
+ // first (and in this case only) frame's width and height.
+ //
+ // A Config only specifies a width and height (two integers) while an
+ // image.Image's Bounds method returns an image.Rectangle (four integers).
+ // For a gif.GIF, the overall bounds' top-left point is always implicitly
+ // (0, 0), and any frame whose bounds have a negative X or Y will be
+ // outside those overall bounds, so encoding should fail.
+ for _, lowerBound := range []int{-1, 0, 1} {
+ images := []*image.Paletted{
+ image.NewPaletted(image.Rect(lowerBound, lowerBound, 4, 4), palette.Plan9),
+ }
+ g := &GIF{
+ Image: images,
+ Delay: make([]int, len(images)),
+ }
+ err := EncodeAll(ioutil.Discard, g)
+ if lowerBound >= 0 {
+ if err != nil {
+ t.Errorf("lowerBound=%d: %v", lowerBound, err)
+ }
+ } else {
+ if err == nil {
+ t.Errorf("lowerBound=%d: got nil error, want non-nil", lowerBound)
+ }
+ }
+ }
+}
+
+func TestEncodePalettes(t *testing.T) {
+ const w, h = 5, 5
+ pals := []color.Palette{{
+ color.RGBA{0x00, 0x00, 0x00, 0xff},
+ color.RGBA{0x01, 0x00, 0x00, 0xff},
+ color.RGBA{0x02, 0x00, 0x00, 0xff},
+ }, {
+ color.RGBA{0x00, 0x00, 0x00, 0xff},
+ color.RGBA{0x00, 0x01, 0x00, 0xff},
+ }, {
+ color.RGBA{0x00, 0x00, 0x03, 0xff},
+ color.RGBA{0x00, 0x00, 0x02, 0xff},
+ color.RGBA{0x00, 0x00, 0x01, 0xff},
+ color.RGBA{0x00, 0x00, 0x00, 0xff},
+ }, {
+ color.RGBA{0x10, 0x07, 0xf0, 0xff},
+ color.RGBA{0x20, 0x07, 0xf0, 0xff},
+ color.RGBA{0x30, 0x07, 0xf0, 0xff},
+ color.RGBA{0x40, 0x07, 0xf0, 0xff},
+ color.RGBA{0x50, 0x07, 0xf0, 0xff},
+ }}
+ g0 := &GIF{
+ Image: []*image.Paletted{
+ image.NewPaletted(image.Rect(0, 0, w, h), pals[0]),
+ image.NewPaletted(image.Rect(0, 0, w, h), pals[1]),
+ image.NewPaletted(image.Rect(0, 0, w, h), pals[2]),
+ image.NewPaletted(image.Rect(0, 0, w, h), pals[3]),
+ },
+ Delay: make([]int, len(pals)),
+ Disposal: make([]byte, len(pals)),
+ Config: image.Config{
+ ColorModel: pals[2],
+ Width: w,
+ Height: h,
+ },
+ }
+
+ var buf bytes.Buffer
+ if err := EncodeAll(&buf, g0); err != nil {
+ t.Fatalf("EncodeAll: %v", err)
+ }
+ g1, err := DecodeAll(&buf)
+ if err != nil {
+ t.Fatalf("DecodeAll: %v", err)
+ }
+ if len(g0.Image) != len(g1.Image) {
+ t.Fatalf("image lengths differ: %d and %d", len(g0.Image), len(g1.Image))
+ }
+ for i, m := range g1.Image {
+ if got, want := m.Palette, pals[i]; !palettesEqual(got, want) {
+ t.Errorf("frame %d:\ngot %v\nwant %v", i, got, want)
+ }
+ }
+}
+
func BenchmarkEncode(b *testing.B) {
b.StopTimer()
diff --git a/src/image/internal/imageutil/gen.go b/src/image/internal/imageutil/gen.go
index cde05ad088..6779b4959d 100644
--- a/src/image/internal/imageutil/gen.go
+++ b/src/image/internal/imageutil/gen.go
@@ -54,7 +54,7 @@ import (
// successful. If it returns false, no dst pixels were changed.
//
// This function assumes that r is entirely within dst's bounds and the
-// translation of r from dst co-ordinate space to src co-ordinate space is
+// translation of r from dst coordinate space to src coordinate space is
// entirely within src's bounds.
func DrawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Point) (ok bool) {
// This function exists in the image/internal/imageutil package because it
diff --git a/src/image/internal/imageutil/impl.go b/src/image/internal/imageutil/impl.go
index d4bd3250fd..d5dee468b3 100644
--- a/src/image/internal/imageutil/impl.go
+++ b/src/image/internal/imageutil/impl.go
@@ -11,7 +11,7 @@ import (
// successful. If it returns false, no dst pixels were changed.
//
// This function assumes that r is entirely within dst's bounds and the
-// translation of r from dst co-ordinate space to src co-ordinate space is
+// translation of r from dst coordinate space to src coordinate space is
// entirely within src's bounds.
func DrawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Point) (ok bool) {
// This function exists in the image/internal/imageutil package because it
diff --git a/src/image/jpeg/reader.go b/src/image/jpeg/reader.go
index 6a86472046..7729532219 100644
--- a/src/image/jpeg/reader.go
+++ b/src/image/jpeg/reader.go
@@ -170,7 +170,7 @@ func (d *decoder) fill() error {
// can happen when expecting to read a 0xff 0x00 byte-stuffed byte.
func (d *decoder) unreadByteStuffedByte() {
if d.bytes.nUnreadable == 0 {
- panic("jpeg: unreadByteStuffedByte call cannot be fulfilled")
+ return
}
d.bytes.i -= d.bytes.nUnreadable
d.bytes.nUnreadable = 0
@@ -217,18 +217,19 @@ func (d *decoder) readByteStuffedByte() (x byte, err error) {
return 0xff, nil
}
+ d.bytes.nUnreadable = 0
+
x, err = d.readByte()
if err != nil {
return 0, err
}
+ d.bytes.nUnreadable = 1
if x != 0xff {
- d.bytes.nUnreadable = 1
return x, nil
}
x, err = d.readByte()
if err != nil {
- d.bytes.nUnreadable = 1
return 0, err
}
d.bytes.nUnreadable = 2
@@ -242,12 +243,7 @@ func (d *decoder) readByteStuffedByte() (x byte, err error) {
// stuffing.
func (d *decoder) readFull(p []byte) error {
// Unread the overshot bytes, if any.
- if d.bytes.nUnreadable != 0 {
- if d.bits.n >= 8 {
- d.unreadByteStuffedByte()
- }
- d.bytes.nUnreadable = 0
- }
+ d.unreadByteStuffedByte()
for {
n := copy(p, d.bytes.buf[d.bytes.i:d.bytes.j])
@@ -269,12 +265,7 @@ func (d *decoder) readFull(p []byte) error {
// ignore ignores the next n bytes.
func (d *decoder) ignore(n int) error {
// Unread the overshot bytes, if any.
- if d.bytes.nUnreadable != 0 {
- if d.bits.n >= 8 {
- d.unreadByteStuffedByte()
- }
- d.bytes.nUnreadable = 0
- }
+ d.unreadByteStuffedByte()
for {
m := d.bytes.j - d.bytes.i
@@ -298,6 +289,9 @@ func (d *decoder) ignore(n int) error {
// Specified in section B.2.2.
func (d *decoder) processSOF(n int) error {
+ if d.nComp != 0 {
+ return FormatError("multiple SOF markers")
+ }
switch n {
case 6 + 3*1: // Grayscale image.
d.nComp = 1
diff --git a/src/image/jpeg/reader_test.go b/src/image/jpeg/reader_test.go
index c5a36cba21..77376152bc 100644
--- a/src/image/jpeg/reader_test.go
+++ b/src/image/jpeg/reader_test.go
@@ -15,6 +15,7 @@ import (
"os"
"strings"
"testing"
+ "time"
)
// TestDecodeProgressive tests that decoding the baseline and progressive
@@ -186,6 +187,81 @@ func pixString(pix []byte, stride, x, y int) string {
return s.String()
}
+func TestTruncatedSOSDataDoesntPanic(t *testing.T) {
+ b, err := ioutil.ReadFile("../testdata/video-005.gray.q50.jpeg")
+ if err != nil {
+ t.Fatal(err)
+ }
+ sosMarker := []byte{0xff, 0xda}
+ i := bytes.Index(b, sosMarker)
+ if i < 0 {
+ t.Fatal("SOS marker not found")
+ }
+ i += len(sosMarker)
+ j := i + 10
+ if j > len(b) {
+ j = len(b)
+ }
+ for ; i < j; i++ {
+ Decode(bytes.NewReader(b[:i]))
+ }
+}
+
+func TestLargeImageWithShortData(t *testing.T) {
+ // This input is an invalid JPEG image, based on the fuzzer-generated image
+ // in issue 10413. It is only 504 bytes, and shouldn't take long for Decode
+ // to return an error. The Start Of Frame marker gives the image dimensions
+ // as 8192 wide and 8192 high, so even if an unreadByteStuffedByte bug
+ // doesn't technically lead to an infinite loop, such a bug can still cause
+ // an unreasonably long loop for such a short input.
+ const input = "" +
+ "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49\x46\x00\x01\x01\x00\x00\x01" +
+ "\x00\x01\x00\x00\xff\xdb\x00\x43\x00\x10\x0b\x0c\x0e\x0c\x0a\x10" +
+ "\x0e\x89\x0e\x12\x11\x10\x13\x18\xff\xd8\xff\xe0\x00\x10\x4a\x46" +
+ "\x49\x46\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00\x43" +
+ "\x00\x10\x0b\x0c\x0e\x0c\x0a\x10\x0e\x0d\x0e\x12\x11\x10\x13\x18" +
+ "\x28\x1a\x18\x16\x16\x18\x31\x23\x25\x1d\x28\x3a\x33\x3d\x3c\x39" +
+ "\x33\x38\x37\x40\x48\x5c\x4e\x40\x44\x57\x45\x37\x38\x50\x6d\x51" +
+ "\x57\x5f\x62\x67\x68\x67\x3e\x4d\x71\x79\x70\x64\x78\x5c\x65\x67" +
+ "\x63\xff\xc0\x00\x0b\x08\x20\x00\x20\x00\x01\x01\x11\x00\xff\xc4" +
+ "\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00" +
+ "\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\xff" +
+ "\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05\x04\x04" +
+ "\x00\x00\x01\x7d\x01\x02\x03\x00\x04\x11\x05\x12\x21\x31\x01\x06" +
+ "\x13\x51\x61\x07\x22\x71\x14\x32\x81\x91\xa1\x08\x23\xd8\xff\xdd" +
+ "\x42\xb1\xc1\x15\x52\xd1\xf0\x24\x33\x62\x72\x82\x09\x0a\x16\x17" +
+ "\x18\x19\x1a\x25\x26\x27\x28\x29\x2a\x34\x35\x36\x37\x38\x39\x3a" +
+ "\x43\x44\x45\x46\x47\x48\x49\x4a\x53\x54\x55\x56\x57\x58\x59\x5a" +
+ "\x00\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74\x75\x76\x77\x78\x79" +
+ "\x7a\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96\x97\x98" +
+ "\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6" +
+ "\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xff\xd8\xff\xe0\x00\x10" +
+ "\x4a\x46\x49\x46\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb" +
+ "\x00\x43\x00\x10\x0b\x0c\x0e\x0c\x0a\x10\x0e\x0d\x0e\x12\x11\x10" +
+ "\x13\x18\x28\x1a\x18\x16\x16\x18\x31\x23\x25\x1d\xc8\xc9\xca\xd2" +
+ "\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8" +
+ "\xe9\xea\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x08" +
+ "\x01\x01\x00\x00\x3f\x00\xb9\xeb\x50\xb0\xdb\xc8\xa8\xe4\x63\x80" +
+ "\xdd\x31\xd6\x9d\xbb\xf2\xc5\x42\x1f\x6c\x6f\xf4\x34\xdd\x3c\xfc" +
+ "\xac\xe7\x3d\x80\xa9\xcc\x87\x34\xb3\x37\xfa\x2b\x9f\x6a\xad\x63" +
+ "\x20\x36\x9f\x78\x64\x75\xe6\xab\x7d\xb2\xde\x29\x70\xd3\x20\x27" +
+ "\xde\xaf\xa4\xf0\xca\x9f\x24\xa8\xdf\x46\xa8\x24\x84\x96\xe3\x77" +
+ "\xf9\x2e\xe0\x0a\x62\x7f\xdf\xd9"
+ c := make(chan error, 1)
+ go func() {
+ _, err := Decode(strings.NewReader(input))
+ c <- err
+ }()
+ select {
+ case err := <-c:
+ if err == nil {
+ t.Fatalf("got nil error, want non-nil")
+ }
+ case <-time.After(3 * time.Second):
+ t.Fatalf("timed out")
+ }
+}
+
func TestExtraneousData(t *testing.T) {
// Encode a 1x1 red image.
src := image.NewRGBA(image.Rect(0, 0, 1, 1))
diff --git a/src/image/png/reader.go b/src/image/png/reader.go
index 0a40ca161d..33218e1516 100644
--- a/src/image/png/reader.go
+++ b/src/image/png/reader.go
@@ -47,6 +47,10 @@ const (
cbTCA16
)
+func cbPaletted(cb int) bool {
+ return cbP1 <= cb && cb <= cbP8
+}
+
// Filter type, as per the PNG spec.
const (
ftNone = 0
@@ -81,15 +85,16 @@ var interlacing = []interlaceScan{
}
// Decoding stage.
-// The PNG specification says that the IHDR, PLTE (if present), IDAT and IEND
-// chunks must appear in that order. There may be multiple IDAT chunks, and
-// IDAT chunks must be sequential (i.e. they may not have any other chunks
-// between them).
+// The PNG specification says that the IHDR, PLTE (if present), tRNS (if
+// present), IDAT and IEND chunks must appear in that order. There may be
+// multiple IDAT chunks, and IDAT chunks must be sequential (i.e. they may not
+// have any other chunks between them).
// http://www.w3.org/TR/PNG/#5ChunkOrdering
const (
dsStart = iota
dsSeenIHDR
dsSeenPLTE
+ dsSeentRNS
dsSeenIDAT
dsSeenIEND
)
@@ -321,9 +326,15 @@ func (d *decoder) decode() (image.Image, error) {
var img image.Image
if d.interlace == itNone {
img, err = d.readImagePass(r, 0, false)
+ if err != nil {
+ return nil, err
+ }
} else if d.interlace == itAdam7 {
// Allocate a blank image of the full size.
img, err = d.readImagePass(nil, 0, true)
+ if err != nil {
+ return nil, err
+ }
for pass := 0; pass < 7; pass++ {
imagePass, err := d.readImagePass(r, pass, false)
if err != nil {
@@ -425,6 +436,9 @@ func (d *decoder) readImagePass(r io.Reader, pass int, allocateOnly bool) (image
// Read the decompressed bytes.
_, err := io.ReadFull(r, cr)
if err != nil {
+ if err == io.EOF || err == io.ErrUnexpectedEOF {
+ return nil, FormatError("not enough pixel data")
+ }
return nil, err
}
@@ -687,9 +701,10 @@ func (d *decoder) parseChunk() error {
if d.stage != dsSeenPLTE {
return chunkOrderError
}
+ d.stage = dsSeentRNS
return d.parsetRNS(length)
case "IDAT":
- if d.stage < dsSeenIHDR || d.stage > dsSeenIDAT || (d.cb == cbP8 && d.stage == dsSeenIHDR) {
+ if d.stage < dsSeenIHDR || d.stage > dsSeenIDAT || (d.stage == dsSeenIHDR && cbPaletted(d.cb)) {
return chunkOrderError
}
d.stage = dsSeenIDAT
@@ -779,7 +794,7 @@ func DecodeConfig(r io.Reader) (image.Config, error) {
}
return image.Config{}, err
}
- paletted := d.cb == cbP8 || d.cb == cbP4 || d.cb == cbP2 || d.cb == cbP1
+ paletted := cbPaletted(d.cb)
if d.stage == dsSeenIHDR && !paletted {
break
}
diff --git a/src/image/png/reader_test.go b/src/image/png/reader_test.go
index ce772eb6f0..1f92f8c221 100644
--- a/src/image/png/reader_test.go
+++ b/src/image/png/reader_test.go
@@ -6,6 +6,7 @@ package png
import (
"bufio"
+ "bytes"
"fmt"
"image"
"image/color"
@@ -319,6 +320,79 @@ func TestPalettedDecodeConfig(t *testing.T) {
}
}
+func TestIncompleteIDATOnRowBoundary(t *testing.T) {
+ // The following is an invalid 1x2 grayscale PNG image. The header is OK,
+ // but the zlib-compressed IDAT payload contains two bytes "\x02\x00",
+ // which is only one row of data (the leading "\x02" is a row filter).
+ const (
+ ihdr = "\x00\x00\x00\x0dIHDR\x00\x00\x00\x01\x00\x00\x00\x02\x08\x00\x00\x00\x00\xbc\xea\xe9\xfb"
+ idat = "\x00\x00\x00\x0eIDAT\x78\x9c\x62\x62\x00\x04\x00\x00\xff\xff\x00\x06\x00\x03\xfa\xd0\x59\xae"
+ iend = "\x00\x00\x00\x00IEND\xae\x42\x60\x82"
+ )
+ _, err := Decode(strings.NewReader(pngHeader + ihdr + idat + iend))
+ if err == nil {
+ t.Fatal("got nil error, want non-nil")
+ }
+}
+
+func TestMultipletRNSChunks(t *testing.T) {
+ /*
+ The following is a valid 1x1 paletted PNG image with a 1-element palette
+ containing color.NRGBA{0xff, 0x00, 0x00, 0x7f}:
+ 0000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452 .PNG........IHDR
+ 0000010: 0000 0001 0000 0001 0803 0000 0028 cb34 .............(.4
+ 0000020: bb00 0000 0350 4c54 45ff 0000 19e2 0937 .....PLTE......7
+ 0000030: 0000 0001 7452 4e53 7f80 5cb4 cb00 0000 ....tRNS..\.....
+ 0000040: 0e49 4441 5478 9c62 6200 0400 00ff ff00 .IDATx.bb.......
+ 0000050: 0600 03fa d059 ae00 0000 0049 454e 44ae .....Y.....IEND.
+ 0000060: 4260 82 B`.
+ Dropping the tRNS chunk makes that color's alpha 0xff instead of 0x7f.
+ */
+ const (
+ ihdr = "\x00\x00\x00\x0dIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x03\x00\x00\x00\x28\xcb\x34\xbb"
+ plte = "\x00\x00\x00\x03PLTE\xff\x00\x00\x19\xe2\x09\x37"
+ trns = "\x00\x00\x00\x01tRNS\x7f\x80\x5c\xb4\xcb"
+ idat = "\x00\x00\x00\x0eIDAT\x78\x9c\x62\x62\x00\x04\x00\x00\xff\xff\x00\x06\x00\x03\xfa\xd0\x59\xae"
+ iend = "\x00\x00\x00\x00IEND\xae\x42\x60\x82"
+ )
+ for i := 0; i < 4; i++ {
+ var b []byte
+ b = append(b, pngHeader...)
+ b = append(b, ihdr...)
+ b = append(b, plte...)
+ for j := 0; j < i; j++ {
+ b = append(b, trns...)
+ }
+ b = append(b, idat...)
+ b = append(b, iend...)
+
+ var want color.Color
+ m, err := Decode(bytes.NewReader(b))
+ switch i {
+ case 0:
+ if err != nil {
+ t.Errorf("%d tRNS chunks: %v", i, err)
+ continue
+ }
+ want = color.RGBA{0xff, 0x00, 0x00, 0xff}
+ case 1:
+ if err != nil {
+ t.Errorf("%d tRNS chunks: %v", i, err)
+ continue
+ }
+ want = color.NRGBA{0xff, 0x00, 0x00, 0x7f}
+ default:
+ if err == nil {
+ t.Errorf("%d tRNS chunks: got nil error, want non-nil", i)
+ }
+ continue
+ }
+ if got := m.At(0, 0); got != want {
+ t.Errorf("%d tRNS chunks: got %T %v, want %T %v", i, got, got, want, want)
+ }
+ }
+}
+
func benchmarkDecode(b *testing.B, filename string, bytesPerPixel int) {
b.StopTimer()
data, err := ioutil.ReadFile(filename)
diff --git a/src/net/singleflight.go b/src/internal/singleflight/singleflight.go
similarity index 73%
rename from src/net/singleflight.go
rename to src/internal/singleflight/singleflight.go
index bf599f0cc9..f4cb2d670d 100644
--- a/src/net/singleflight.go
+++ b/src/internal/singleflight/singleflight.go
@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package net
+// Package singleflight provides a duplicate function call suppression
+// mechanism.
+package singleflight
import "sync"
@@ -19,22 +21,22 @@ type call struct {
// mutex held before the WaitGroup is done, and are read but
// not written after the WaitGroup is done.
dups int
- chans []chan<- singleflightResult
+ chans []chan<- Result
}
-// singleflight represents a class of work and forms a namespace in
+// Group represents a class of work and forms a namespace in
// which units of work can be executed with duplicate suppression.
-type singleflight struct {
+type Group struct {
mu sync.Mutex // protects m
m map[string]*call // lazily initialized
}
-// singleflightResult holds the results of Do, so they can be passed
+// Result holds the results of Do, so they can be passed
// on a channel.
-type singleflightResult struct {
- v interface{}
- err error
- shared bool
+type Result struct {
+ Val interface{}
+ Err error
+ Shared bool
}
// Do executes and returns the results of the given function, making
@@ -42,7 +44,7 @@ type singleflightResult struct {
// time. If a duplicate comes in, the duplicate caller waits for the
// original to complete and receives the same results.
// The return value shared indicates whether v was given to multiple callers.
-func (g *singleflight) Do(key string, fn func() (interface{}, error)) (v interface{}, err error, shared bool) {
+func (g *Group) Do(key string, fn func() (interface{}, error)) (v interface{}, err error, shared bool) {
g.mu.Lock()
if g.m == nil {
g.m = make(map[string]*call)
@@ -64,8 +66,8 @@ func (g *singleflight) Do(key string, fn func() (interface{}, error)) (v interfa
// DoChan is like Do but returns a channel that will receive the
// results when they are ready.
-func (g *singleflight) DoChan(key string, fn func() (interface{}, error)) <-chan singleflightResult {
- ch := make(chan singleflightResult, 1)
+func (g *Group) DoChan(key string, fn func() (interface{}, error)) <-chan Result {
+ ch := make(chan Result, 1)
g.mu.Lock()
if g.m == nil {
g.m = make(map[string]*call)
@@ -76,7 +78,7 @@ func (g *singleflight) DoChan(key string, fn func() (interface{}, error)) <-chan
g.mu.Unlock()
return ch
}
- c := &call{chans: []chan<- singleflightResult{ch}}
+ c := &call{chans: []chan<- Result{ch}}
c.wg.Add(1)
g.m[key] = c
g.mu.Unlock()
@@ -87,14 +89,14 @@ func (g *singleflight) DoChan(key string, fn func() (interface{}, error)) <-chan
}
// doCall handles the single call for a key.
-func (g *singleflight) doCall(c *call, key string, fn func() (interface{}, error)) {
+func (g *Group) doCall(c *call, key string, fn func() (interface{}, error)) {
c.val, c.err = fn()
c.wg.Done()
g.mu.Lock()
delete(g.m, key)
for _, ch := range c.chans {
- ch <- singleflightResult{c.val, c.err, c.dups > 0}
+ ch <- Result{c.val, c.err, c.dups > 0}
}
g.mu.Unlock()
}
@@ -102,7 +104,7 @@ func (g *singleflight) doCall(c *call, key string, fn func() (interface{}, error
// Forget tells the singleflight to forget about a key. Future calls
// to Do for this key will call the function rather than waiting for
// an earlier call to complete.
-func (g *singleflight) Forget(key string) {
+func (g *Group) Forget(key string) {
g.mu.Lock()
delete(g.m, key)
g.mu.Unlock()
diff --git a/src/internal/singleflight/singleflight_test.go b/src/internal/singleflight/singleflight_test.go
new file mode 100644
index 0000000000..30ba7f7ab4
--- /dev/null
+++ b/src/internal/singleflight/singleflight_test.go
@@ -0,0 +1,73 @@
+// 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 singleflight
+
+import (
+ "errors"
+ "fmt"
+ "sync"
+ "sync/atomic"
+ "testing"
+ "time"
+)
+
+func TestDo(t *testing.T) {
+ var g Group
+ v, err, _ := g.Do("key", func() (interface{}, error) {
+ return "bar", nil
+ })
+ if got, want := fmt.Sprintf("%v (%T)", v, v), "bar (string)"; got != want {
+ t.Errorf("Do = %v; want %v", got, want)
+ }
+ if err != nil {
+ t.Errorf("Do error = %v", err)
+ }
+}
+
+func TestDoErr(t *testing.T) {
+ var g Group
+ someErr := errors.New("Some error")
+ v, err, _ := g.Do("key", func() (interface{}, error) {
+ return nil, someErr
+ })
+ if err != someErr {
+ t.Errorf("Do error = %v; want someErr %v", err, someErr)
+ }
+ if v != nil {
+ t.Errorf("unexpected non-nil value %#v", v)
+ }
+}
+
+func TestDoDupSuppress(t *testing.T) {
+ var g Group
+ c := make(chan string)
+ var calls int32
+ fn := func() (interface{}, error) {
+ atomic.AddInt32(&calls, 1)
+ return <-c, nil
+ }
+
+ const n = 10
+ var wg sync.WaitGroup
+ for i := 0; i < n; i++ {
+ wg.Add(1)
+ go func() {
+ v, err, _ := g.Do("key", fn)
+ if err != nil {
+ t.Errorf("Do error: %v", err)
+ }
+ if v.(string) != "bar" {
+ t.Errorf("got %q; want %q", v, "bar")
+ }
+ wg.Done()
+ }()
+ }
+ time.Sleep(100 * time.Millisecond) // let goroutines above block
+ c <- "bar"
+ wg.Wait()
+ if got := atomic.LoadInt32(&calls); got != 1 {
+ t.Errorf("number of calls = %d; want 1", got)
+ }
+}
diff --git a/src/internal/syscall/getrandom_linux.go b/src/internal/syscall/unix/getrandom_linux.go
similarity index 87%
rename from src/internal/syscall/getrandom_linux.go
rename to src/internal/syscall/unix/getrandom_linux.go
index 36d5a1ccb0..7388271ef1 100644
--- a/src/internal/syscall/getrandom_linux.go
+++ b/src/internal/syscall/unix/getrandom_linux.go
@@ -2,12 +2,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package syscall
+package unix
import (
"runtime"
"sync/atomic"
- stdsyscall "syscall"
+ "syscall"
"unsafe"
)
@@ -36,20 +36,20 @@ const (
// See https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=c6e9d6f38894798696f23c8084ca7edbf16ee895
func GetRandom(p []byte, flags GetRandomFlag) (n int, err error) {
if randomTrap == 0 {
- return 0, stdsyscall.ENOSYS
+ return 0, syscall.ENOSYS
}
if len(p) == 0 {
return 0, nil
}
if atomic.LoadInt32(&randomUnsupported) != 0 {
- return 0, stdsyscall.ENOSYS
+ return 0, syscall.ENOSYS
}
- r1, _, errno := stdsyscall.Syscall(randomTrap,
+ r1, _, errno := syscall.Syscall(randomTrap,
uintptr(unsafe.Pointer(&p[0])),
uintptr(len(p)),
uintptr(flags))
if errno != 0 {
- if errno == stdsyscall.ENOSYS {
+ if errno == syscall.ENOSYS {
atomic.StoreInt32(&randomUnsupported, 1)
}
return 0, errno
diff --git a/src/internal/syscall/windows/registry/key.go b/src/internal/syscall/windows/registry/key.go
new file mode 100644
index 0000000000..62144d39b7
--- /dev/null
+++ b/src/internal/syscall/windows/registry/key.go
@@ -0,0 +1,175 @@
+// Copyright 2015 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 windows
+
+// Package registry provides access to the Windows registry.
+//
+// Here is a simple example, opening a registry key and reading a string value from it.
+//
+// k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
+// if err != nil {
+// log.Fatal(err)
+// }
+// defer k.Close()
+//
+// s, _, err := k.GetStringValue("SystemRoot")
+// if err != nil {
+// log.Fatal(err)
+// }
+// fmt.Printf("Windows system root is %q\n", s)
+//
+// NOTE: This package is a copy of golang.org/x/sys/windows/registry
+// with KeyInfo.ModTime removed to prevent dependency cycles.
+//
+package registry
+
+import (
+ "io"
+ "syscall"
+)
+
+const (
+ // Registry key security and access rights.
+ // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724878.aspx
+ // for details.
+ ALL_ACCESS = 0xf003f
+ CREATE_LINK = 0x00020
+ CREATE_SUB_KEY = 0x00004
+ ENUMERATE_SUB_KEYS = 0x00008
+ EXECUTE = 0x20019
+ NOTIFY = 0x00010
+ QUERY_VALUE = 0x00001
+ READ = 0x20019
+ SET_VALUE = 0x00002
+ WOW64_32KEY = 0x00200
+ WOW64_64KEY = 0x00100
+ WRITE = 0x20006
+)
+
+// Key is a handle to an open Windows registry key.
+// Keys can be obtained by calling OpenKey; there are
+// also some predefined root keys such as CURRENT_USER.
+// Keys can be used directly in the Windows API.
+type Key syscall.Handle
+
+const (
+ // Windows defines some predefined root keys that are always open.
+ // An application can use these keys as entry points to the registry.
+ // Normally these keys are used in OpenKey to open new keys,
+ // but they can also be used anywhere a Key is required.
+ CLASSES_ROOT = Key(syscall.HKEY_CLASSES_ROOT)
+ CURRENT_USER = Key(syscall.HKEY_CURRENT_USER)
+ LOCAL_MACHINE = Key(syscall.HKEY_LOCAL_MACHINE)
+ USERS = Key(syscall.HKEY_USERS)
+ CURRENT_CONFIG = Key(syscall.HKEY_CURRENT_CONFIG)
+)
+
+// Close closes open key k.
+func (k Key) Close() error {
+ return syscall.RegCloseKey(syscall.Handle(k))
+}
+
+// OpenKey opens a new key with path name relative to key k.
+// It accepts any open key, including CURRENT_USER and others,
+// and returns the new key and an error.
+// The access parameter specifies desired access rights to the
+// key to be opened.
+func OpenKey(k Key, path string, access uint32) (Key, error) {
+ p, err := syscall.UTF16PtrFromString(path)
+ if err != nil {
+ return 0, err
+ }
+ var subkey syscall.Handle
+ err = syscall.RegOpenKeyEx(syscall.Handle(k), p, 0, access, &subkey)
+ if err != nil {
+ return 0, err
+ }
+ return Key(subkey), nil
+}
+
+// ReadSubKeyNames returns the names of subkeys of key k.
+// The parameter n controls the number of returned names,
+// analogous to the way os.File.Readdirnames works.
+func (k Key) ReadSubKeyNames(n int) ([]string, error) {
+ ki, err := k.Stat()
+ if err != nil {
+ return nil, err
+ }
+ names := make([]string, 0, ki.SubKeyCount)
+ buf := make([]uint16, ki.MaxSubKeyLen+1) // extra room for terminating zero byte
+loopItems:
+ for i := uint32(0); ; i++ {
+ if n > 0 {
+ if len(names) == n {
+ return names, nil
+ }
+ }
+ l := uint32(len(buf))
+ for {
+ err := syscall.RegEnumKeyEx(syscall.Handle(k), i, &buf[0], &l, nil, nil, nil, nil)
+ if err == nil {
+ break
+ }
+ if err == syscall.ERROR_MORE_DATA {
+ // Double buffer size and try again.
+ l = uint32(2 * len(buf))
+ buf = make([]uint16, l)
+ continue
+ }
+ if err == _ERROR_NO_MORE_ITEMS {
+ break loopItems
+ }
+ return names, err
+ }
+ names = append(names, syscall.UTF16ToString(buf[:l]))
+ }
+ if n > len(names) {
+ return names, io.EOF
+ }
+ return names, nil
+}
+
+// CreateKey creates a key named path under open key k.
+// CreateKey returns the new key and a boolean flag that reports
+// whether the key already existed.
+// The access parameter specifies the access rights for the key
+// to be created.
+func CreateKey(k Key, path string, access uint32) (newk Key, openedExisting bool, err error) {
+ var h syscall.Handle
+ var d uint32
+ err = regCreateKeyEx(syscall.Handle(k), syscall.StringToUTF16Ptr(path),
+ 0, nil, _REG_OPTION_NON_VOLATILE, access, nil, &h, &d)
+ if err != nil {
+ return 0, false, err
+ }
+ return Key(h), d == _REG_OPENED_EXISTING_KEY, nil
+}
+
+// DeleteKey deletes the subkey path of key k and its values.
+func DeleteKey(k Key, path string) error {
+ return regDeleteKey(syscall.Handle(k), syscall.StringToUTF16Ptr(path))
+}
+
+// A KeyInfo describes the statistics of a key. It is returned by Stat.
+type KeyInfo struct {
+ SubKeyCount uint32
+ MaxSubKeyLen uint32 // size of the key's subkey with the longest name, in Unicode characters, not including the terminating zero byte
+ ValueCount uint32
+ MaxValueNameLen uint32 // size of the key's longest value name, in Unicode characters, not including the terminating zero byte
+ MaxValueLen uint32 // longest data component among the key's values, in bytes
+ lastWriteTime syscall.Filetime
+}
+
+// Stat retrieves information about the open key k.
+func (k Key) Stat() (*KeyInfo, error) {
+ var ki KeyInfo
+ err := syscall.RegQueryInfoKey(syscall.Handle(k), nil, nil, nil,
+ &ki.SubKeyCount, &ki.MaxSubKeyLen, nil, &ki.ValueCount,
+ &ki.MaxValueNameLen, &ki.MaxValueLen, nil, &ki.lastWriteTime)
+ if err != nil {
+ return nil, err
+ }
+ return &ki, nil
+}
diff --git a/src/internal/syscall/windows/registry/registry_test.go b/src/internal/syscall/windows/registry/registry_test.go
new file mode 100644
index 0000000000..5f75febd27
--- /dev/null
+++ b/src/internal/syscall/windows/registry/registry_test.go
@@ -0,0 +1,613 @@
+// Copyright 2015 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 windows
+
+package registry_test
+
+import (
+ "bytes"
+ "crypto/rand"
+ "os"
+ "syscall"
+ "testing"
+
+ "internal/syscall/windows/registry"
+)
+
+func randKeyName(prefix string) string {
+ const numbers = "0123456789"
+ buf := make([]byte, 10)
+ rand.Read(buf)
+ for i, b := range buf {
+ buf[i] = numbers[b%byte(len(numbers))]
+ }
+ return prefix + string(buf)
+}
+
+func TestReadSubKeyNames(t *testing.T) {
+ k, err := registry.OpenKey(registry.CLASSES_ROOT, "TypeLib", registry.ENUMERATE_SUB_KEYS|registry.QUERY_VALUE)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer k.Close()
+
+ names, err := k.ReadSubKeyNames(-1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ var foundStdOle bool
+ for _, name := range names {
+ // Every PC has "stdole 2.0 OLE Automation" library installed.
+ if name == "{00020430-0000-0000-C000-000000000046}" {
+ foundStdOle = true
+ }
+ }
+ if !foundStdOle {
+ t.Fatal("could not find stdole 2.0 OLE Automation")
+ }
+}
+
+func TestCreateOpenDeleteKey(t *testing.T) {
+ k, err := registry.OpenKey(registry.CURRENT_USER, "Software", registry.QUERY_VALUE)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer k.Close()
+
+ testKName := randKeyName("TestCreateOpenDeleteKey_")
+
+ testK, exist, err := registry.CreateKey(k, testKName, registry.CREATE_SUB_KEY)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer testK.Close()
+
+ if exist {
+ t.Fatalf("key %q already exists", testKName)
+ }
+
+ testKAgain, exist, err := registry.CreateKey(k, testKName, registry.CREATE_SUB_KEY)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer testKAgain.Close()
+
+ if !exist {
+ t.Fatalf("key %q should already exist", testKName)
+ }
+
+ testKOpened, err := registry.OpenKey(k, testKName, registry.ENUMERATE_SUB_KEYS)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer testKOpened.Close()
+
+ err = registry.DeleteKey(k, testKName)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ testKOpenedAgain, err := registry.OpenKey(k, testKName, registry.ENUMERATE_SUB_KEYS)
+ if err == nil {
+ defer testKOpenedAgain.Close()
+ t.Fatalf("key %q should already been deleted", testKName)
+ }
+ if err != registry.ErrNotExist {
+ t.Fatalf(`unexpected error ("not exist" expected): %v`, err)
+ }
+}
+
+func equalStringSlice(a, b []string) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ if a == nil {
+ return true
+ }
+ for i := range a {
+ if a[i] != b[i] {
+ return false
+ }
+ }
+ return true
+}
+
+type ValueTest struct {
+ Type uint32
+ Name string
+ Value interface{}
+ WillFail bool
+}
+
+var ValueTests = []ValueTest{
+ {Type: registry.SZ, Name: "String1", Value: ""},
+ {Type: registry.SZ, Name: "String2", Value: "\000", WillFail: true},
+ {Type: registry.SZ, Name: "String3", Value: "Hello World"},
+ {Type: registry.SZ, Name: "String4", Value: "Hello World\000", WillFail: true},
+ {Type: registry.EXPAND_SZ, Name: "ExpString1", Value: ""},
+ {Type: registry.EXPAND_SZ, Name: "ExpString2", Value: "\000", WillFail: true},
+ {Type: registry.EXPAND_SZ, Name: "ExpString3", Value: "Hello World"},
+ {Type: registry.EXPAND_SZ, Name: "ExpString4", Value: "Hello\000World", WillFail: true},
+ {Type: registry.EXPAND_SZ, Name: "ExpString5", Value: "%PATH%"},
+ {Type: registry.EXPAND_SZ, Name: "ExpString6", Value: "%NO_SUCH_VARIABLE%"},
+ {Type: registry.EXPAND_SZ, Name: "ExpString7", Value: "%PATH%;."},
+ {Type: registry.BINARY, Name: "Binary1", Value: []byte{}},
+ {Type: registry.BINARY, Name: "Binary2", Value: []byte{1, 2, 3}},
+ {Type: registry.BINARY, Name: "Binary3", Value: []byte{3, 2, 1, 0, 1, 2, 3}},
+ {Type: registry.DWORD, Name: "Dword1", Value: uint64(0)},
+ {Type: registry.DWORD, Name: "Dword2", Value: uint64(1)},
+ {Type: registry.DWORD, Name: "Dword3", Value: uint64(0xff)},
+ {Type: registry.DWORD, Name: "Dword4", Value: uint64(0xffff)},
+ {Type: registry.QWORD, Name: "Qword1", Value: uint64(0)},
+ {Type: registry.QWORD, Name: "Qword2", Value: uint64(1)},
+ {Type: registry.QWORD, Name: "Qword3", Value: uint64(0xff)},
+ {Type: registry.QWORD, Name: "Qword4", Value: uint64(0xffff)},
+ {Type: registry.QWORD, Name: "Qword5", Value: uint64(0xffffff)},
+ {Type: registry.QWORD, Name: "Qword6", Value: uint64(0xffffffff)},
+ {Type: registry.MULTI_SZ, Name: "MultiString1", Value: []string{"a", "b", "c"}},
+ {Type: registry.MULTI_SZ, Name: "MultiString2", Value: []string{"abc", "", "cba"}},
+ {Type: registry.MULTI_SZ, Name: "MultiString3", Value: []string{""}},
+ {Type: registry.MULTI_SZ, Name: "MultiString4", Value: []string{"abcdef"}},
+ {Type: registry.MULTI_SZ, Name: "MultiString5", Value: []string{"\000"}, WillFail: true},
+ {Type: registry.MULTI_SZ, Name: "MultiString6", Value: []string{"a\000b"}, WillFail: true},
+ {Type: registry.MULTI_SZ, Name: "MultiString7", Value: []string{"ab", "\000", "cd"}, WillFail: true},
+ {Type: registry.MULTI_SZ, Name: "MultiString8", Value: []string{"\000", "cd"}, WillFail: true},
+ {Type: registry.MULTI_SZ, Name: "MultiString9", Value: []string{"ab", "\000"}, WillFail: true},
+}
+
+func setValues(t *testing.T, k registry.Key) {
+ for _, test := range ValueTests {
+ var err error
+ switch test.Type {
+ case registry.SZ:
+ err = k.SetStringValue(test.Name, test.Value.(string))
+ case registry.EXPAND_SZ:
+ err = k.SetExpandStringValue(test.Name, test.Value.(string))
+ case registry.MULTI_SZ:
+ err = k.SetStringsValue(test.Name, test.Value.([]string))
+ case registry.BINARY:
+ err = k.SetBinaryValue(test.Name, test.Value.([]byte))
+ case registry.DWORD:
+ err = k.SetDWordValue(test.Name, uint32(test.Value.(uint64)))
+ case registry.QWORD:
+ err = k.SetQWordValue(test.Name, test.Value.(uint64))
+ default:
+ t.Fatalf("unsupported type %d for %s value", test.Type, test.Name)
+ }
+ if test.WillFail {
+ if err == nil {
+ t.Fatalf("setting %s value %q should fail, but succeeded", test.Name, test.Value)
+ }
+ } else {
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+ }
+}
+
+func enumerateValues(t *testing.T, k registry.Key) {
+ names, err := k.ReadValueNames(-1)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ haveNames := make(map[string]bool)
+ for _, n := range names {
+ haveNames[n] = false
+ }
+ for _, test := range ValueTests {
+ wantFound := !test.WillFail
+ _, haveFound := haveNames[test.Name]
+ if wantFound && !haveFound {
+ t.Errorf("value %s is not found while enumerating", test.Name)
+ }
+ if haveFound && !wantFound {
+ t.Errorf("value %s is found while enumerating, but expected to fail", test.Name)
+ }
+ if haveFound {
+ delete(haveNames, test.Name)
+ }
+ }
+ for n, v := range haveNames {
+ t.Errorf("value %s (%v) is found while enumerating, but has not been cretaed", n, v)
+ }
+}
+
+func testErrNotExist(t *testing.T, name string, err error) {
+ if err == nil {
+ t.Errorf("%s value should not exist", name)
+ return
+ }
+ if err != registry.ErrNotExist {
+ t.Errorf("reading %s value should return 'not exist' error, but got: %s", name, err)
+ return
+ }
+}
+
+func testErrUnexpectedType(t *testing.T, test ValueTest, gottype uint32, err error) {
+ if err == nil {
+ t.Errorf("GetXValue(%q) should not succeed", test.Name)
+ return
+ }
+ if err != registry.ErrUnexpectedType {
+ t.Errorf("reading %s value should return 'unexpected key value type' error, but got: %s", test.Name, err)
+ return
+ }
+ if gottype != test.Type {
+ t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
+ return
+ }
+}
+
+func testGetStringValue(t *testing.T, k registry.Key, test ValueTest) {
+ got, gottype, err := k.GetStringValue(test.Name)
+ if err != nil {
+ t.Errorf("GetStringValue(%s) failed: %v", test.Name, err)
+ return
+ }
+ if got != test.Value {
+ t.Errorf("want %s value %q, got %q", test.Name, test.Value, got)
+ return
+ }
+ if gottype != test.Type {
+ t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
+ return
+ }
+ if gottype == registry.EXPAND_SZ {
+ _, err = registry.ExpandString(got)
+ if err != nil {
+ t.Errorf("ExpandString(%s) failed: %v", got, err)
+ return
+ }
+ }
+}
+
+func testGetIntegerValue(t *testing.T, k registry.Key, test ValueTest) {
+ got, gottype, err := k.GetIntegerValue(test.Name)
+ if err != nil {
+ t.Errorf("GetIntegerValue(%s) failed: %v", test.Name, err)
+ return
+ }
+ if got != test.Value.(uint64) {
+ t.Errorf("want %s value %v, got %v", test.Name, test.Value, got)
+ return
+ }
+ if gottype != test.Type {
+ t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
+ return
+ }
+}
+
+func testGetBinaryValue(t *testing.T, k registry.Key, test ValueTest) {
+ got, gottype, err := k.GetBinaryValue(test.Name)
+ if err != nil {
+ t.Errorf("GetBinaryValue(%s) failed: %v", test.Name, err)
+ return
+ }
+ if !bytes.Equal(got, test.Value.([]byte)) {
+ t.Errorf("want %s value %v, got %v", test.Name, test.Value, got)
+ return
+ }
+ if gottype != test.Type {
+ t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
+ return
+ }
+}
+
+func testGetStringsValue(t *testing.T, k registry.Key, test ValueTest) {
+ got, gottype, err := k.GetStringsValue(test.Name)
+ if err != nil {
+ t.Errorf("GetStringsValue(%s) failed: %v", test.Name, err)
+ return
+ }
+ if !equalStringSlice(got, test.Value.([]string)) {
+ t.Errorf("want %s value %#v, got %#v", test.Name, test.Value, got)
+ return
+ }
+ if gottype != test.Type {
+ t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
+ return
+ }
+}
+
+func testGetValue(t *testing.T, k registry.Key, test ValueTest, size int) {
+ if size <= 0 {
+ return
+ }
+ // read data with no buffer
+ gotsize, gottype, err := k.GetValue(test.Name, nil)
+ if err != nil {
+ t.Errorf("GetValue(%s, [%d]byte) failed: %v", test.Name, size, err)
+ return
+ }
+ if gotsize != size {
+ t.Errorf("want %s value size of %d, got %v", test.Name, size, gotsize)
+ return
+ }
+ if gottype != test.Type {
+ t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
+ return
+ }
+ // read data with short buffer
+ gotsize, gottype, err = k.GetValue(test.Name, make([]byte, size-1))
+ if err == nil {
+ t.Errorf("GetValue(%s, [%d]byte) should fail, but suceeded", test.Name, size-1)
+ return
+ }
+ if err != registry.ErrShortBuffer {
+ t.Errorf("reading %s value should return 'short buffer' error, but got: %s", test.Name, err)
+ return
+ }
+ if gotsize != size {
+ t.Errorf("want %s value size of %d, got %v", test.Name, size, gotsize)
+ return
+ }
+ if gottype != test.Type {
+ t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
+ return
+ }
+ // read full data
+ gotsize, gottype, err = k.GetValue(test.Name, make([]byte, size))
+ if err != nil {
+ t.Errorf("GetValue(%s, [%d]byte) failed: %v", test.Name, size, err)
+ return
+ }
+ if gotsize != size {
+ t.Errorf("want %s value size of %d, got %v", test.Name, size, gotsize)
+ return
+ }
+ if gottype != test.Type {
+ t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
+ return
+ }
+ // check GetValue returns ErrNotExist as required
+ _, _, err = k.GetValue(test.Name+"_not_there", make([]byte, size))
+ if err == nil {
+ t.Errorf("GetValue(%q) should not succeed", test.Name)
+ return
+ }
+ if err != registry.ErrNotExist {
+ t.Errorf("GetValue(%q) should return 'not exist' error, but got: %s", test.Name, err)
+ return
+ }
+}
+
+func testValues(t *testing.T, k registry.Key) {
+ for _, test := range ValueTests {
+ switch test.Type {
+ case registry.SZ, registry.EXPAND_SZ:
+ if test.WillFail {
+ _, _, err := k.GetStringValue(test.Name)
+ testErrNotExist(t, test.Name, err)
+ } else {
+ testGetStringValue(t, k, test)
+ _, gottype, err := k.GetIntegerValue(test.Name)
+ testErrUnexpectedType(t, test, gottype, err)
+ // Size of utf16 string in bytes is not perfect,
+ // but correct for current test values.
+ // Size also includes terminating 0.
+ testGetValue(t, k, test, (len(test.Value.(string))+1)*2)
+ }
+ _, _, err := k.GetStringValue(test.Name + "_string_not_created")
+ testErrNotExist(t, test.Name+"_string_not_created", err)
+ case registry.DWORD, registry.QWORD:
+ testGetIntegerValue(t, k, test)
+ _, gottype, err := k.GetBinaryValue(test.Name)
+ testErrUnexpectedType(t, test, gottype, err)
+ _, _, err = k.GetIntegerValue(test.Name + "_int_not_created")
+ testErrNotExist(t, test.Name+"_int_not_created", err)
+ size := 8
+ if test.Type == registry.DWORD {
+ size = 4
+ }
+ testGetValue(t, k, test, size)
+ case registry.BINARY:
+ testGetBinaryValue(t, k, test)
+ _, gottype, err := k.GetStringsValue(test.Name)
+ testErrUnexpectedType(t, test, gottype, err)
+ _, _, err = k.GetBinaryValue(test.Name + "_byte_not_created")
+ testErrNotExist(t, test.Name+"_byte_not_created", err)
+ testGetValue(t, k, test, len(test.Value.([]byte)))
+ case registry.MULTI_SZ:
+ if test.WillFail {
+ _, _, err := k.GetStringsValue(test.Name)
+ testErrNotExist(t, test.Name, err)
+ } else {
+ testGetStringsValue(t, k, test)
+ _, gottype, err := k.GetStringValue(test.Name)
+ testErrUnexpectedType(t, test, gottype, err)
+ size := 0
+ for _, s := range test.Value.([]string) {
+ size += len(s) + 1 // nil terminated
+ }
+ size += 1 // extra nil at the end
+ size *= 2 // count bytes, not uint16
+ testGetValue(t, k, test, size)
+ }
+ _, _, err := k.GetStringsValue(test.Name + "_strings_not_created")
+ testErrNotExist(t, test.Name+"_strings_not_created", err)
+ default:
+ t.Errorf("unsupported type %d for %s value", test.Type, test.Name)
+ continue
+ }
+ }
+}
+
+func testStat(t *testing.T, k registry.Key) {
+ subk, _, err := registry.CreateKey(k, "subkey", registry.CREATE_SUB_KEY)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ defer subk.Close()
+
+ defer registry.DeleteKey(k, "subkey")
+
+ ki, err := k.Stat()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if ki.SubKeyCount != 1 {
+ t.Error("key must have 1 subkey")
+ }
+ if ki.MaxSubKeyLen != 6 {
+ t.Error("key max subkey name length must be 6")
+ }
+ if ki.ValueCount != 24 {
+ t.Errorf("key must have 24 values, but is %d", ki.ValueCount)
+ }
+ if ki.MaxValueNameLen != 12 {
+ t.Errorf("key max value name length must be 10, but is %d", ki.MaxValueNameLen)
+ }
+ if ki.MaxValueLen != 38 {
+ t.Errorf("key max value length must be 38, but is %d", ki.MaxValueLen)
+ }
+}
+
+func deleteValues(t *testing.T, k registry.Key) {
+ for _, test := range ValueTests {
+ if test.WillFail {
+ continue
+ }
+ err := k.DeleteValue(test.Name)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ }
+ names, err := k.ReadValueNames(-1)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if len(names) != 0 {
+ t.Errorf("some values remain after deletion: %v", names)
+ }
+}
+
+func TestValues(t *testing.T) {
+ softwareK, err := registry.OpenKey(registry.CURRENT_USER, "Software", registry.QUERY_VALUE)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer softwareK.Close()
+
+ testKName := randKeyName("TestValues_")
+
+ k, exist, err := registry.CreateKey(softwareK, testKName, registry.CREATE_SUB_KEY|registry.QUERY_VALUE|registry.SET_VALUE)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer k.Close()
+
+ if exist {
+ t.Fatalf("key %q already exists", testKName)
+ }
+
+ defer registry.DeleteKey(softwareK, testKName)
+
+ setValues(t, k)
+
+ enumerateValues(t, k)
+
+ testValues(t, k)
+
+ testStat(t, k)
+
+ deleteValues(t, k)
+}
+
+func walkKey(t *testing.T, k registry.Key, kname string) {
+ names, err := k.ReadValueNames(-1)
+ if err != nil {
+ t.Fatalf("reading value names of %s failed: %v", kname, err)
+ }
+ for _, name := range names {
+ _, valtype, err := k.GetValue(name, nil)
+ if err != nil {
+ t.Fatalf("reading value type of %s of %s failed: %v", name, kname, err)
+ }
+ switch valtype {
+ case registry.NONE:
+ case registry.SZ:
+ _, _, err := k.GetStringValue(name)
+ if err != nil {
+ t.Error(err)
+ }
+ case registry.EXPAND_SZ:
+ s, _, err := k.GetStringValue(name)
+ if err != nil {
+ t.Error(err)
+ }
+ _, err = registry.ExpandString(s)
+ if err != nil {
+ t.Error(err)
+ }
+ case registry.DWORD, registry.QWORD:
+ _, _, err := k.GetIntegerValue(name)
+ if err != nil {
+ t.Error(err)
+ }
+ case registry.BINARY:
+ _, _, err := k.GetBinaryValue(name)
+ if err != nil {
+ t.Error(err)
+ }
+ case registry.MULTI_SZ:
+ _, _, err := k.GetStringsValue(name)
+ if err != nil {
+ t.Error(err)
+ }
+ case registry.FULL_RESOURCE_DESCRIPTOR, registry.RESOURCE_LIST, registry.RESOURCE_REQUIREMENTS_LIST:
+ // TODO: not implemented
+ default:
+ t.Fatalf("value type %d of %s of %s failed: %v", valtype, name, kname, err)
+ }
+ }
+
+ names, err = k.ReadSubKeyNames(-1)
+ if err != nil {
+ t.Fatalf("reading sub-keys of %s failed: %v", kname, err)
+ }
+ for _, name := range names {
+ func() {
+ subk, err := registry.OpenKey(k, name, registry.ENUMERATE_SUB_KEYS|registry.QUERY_VALUE)
+ if err != nil {
+ if err == syscall.ERROR_ACCESS_DENIED {
+ // ignore error, if we are not allowed to access this key
+ return
+ }
+ t.Fatalf("opening sub-keys %s of %s failed: %v", name, kname, err)
+ }
+ defer subk.Close()
+
+ walkKey(t, subk, kname+`\`+name)
+ }()
+ }
+}
+
+func TestWalkFullRegistry(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping long running test in short mode")
+ }
+ walkKey(t, registry.CLASSES_ROOT, "CLASSES_ROOT")
+ walkKey(t, registry.CURRENT_USER, "CURRENT_USER")
+ walkKey(t, registry.LOCAL_MACHINE, "LOCAL_MACHINE")
+ walkKey(t, registry.USERS, "USERS")
+ walkKey(t, registry.CURRENT_CONFIG, "CURRENT_CONFIG")
+}
+
+func TestExpandString(t *testing.T) {
+ got, err := registry.ExpandString("%PATH%")
+ if err != nil {
+ t.Fatal(err)
+ }
+ want := os.Getenv("PATH")
+ if got != want {
+ t.Errorf("want %q string expanded, got %q", want, got)
+ }
+}
diff --git a/src/internal/syscall/windows/registry/syscall.go b/src/internal/syscall/windows/registry/syscall.go
new file mode 100644
index 0000000000..38e573fd22
--- /dev/null
+++ b/src/internal/syscall/windows/registry/syscall.go
@@ -0,0 +1,28 @@
+// Copyright 2015 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 windows
+
+package registry
+
+import "syscall"
+
+//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall.go
+
+const (
+ _REG_OPTION_NON_VOLATILE = 0
+
+ _REG_CREATED_NEW_KEY = 1
+ _REG_OPENED_EXISTING_KEY = 2
+
+ _ERROR_NO_MORE_ITEMS syscall.Errno = 259
+)
+
+//sys regCreateKeyEx(key syscall.Handle, subkey *uint16, reserved uint32, class *uint16, options uint32, desired uint32, sa *syscall.SecurityAttributes, result *syscall.Handle, disposition *uint32) (regerrno error) = advapi32.RegCreateKeyExW
+//sys regDeleteKey(key syscall.Handle, subkey *uint16) (regerrno error) = advapi32.RegDeleteKeyW
+//sys regSetValueEx(key syscall.Handle, valueName *uint16, reserved uint32, vtype uint32, buf *byte, bufsize uint32) (regerrno error) = advapi32.RegSetValueExW
+//sys regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) = advapi32.RegEnumValueW
+//sys regDeleteValue(key syscall.Handle, name *uint16) (regerrno error) = advapi32.RegDeleteValueW
+
+//sys expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) = kernel32.ExpandEnvironmentStringsW
diff --git a/src/internal/syscall/windows/registry/value.go b/src/internal/syscall/windows/registry/value.go
new file mode 100644
index 0000000000..1c1771d30e
--- /dev/null
+++ b/src/internal/syscall/windows/registry/value.go
@@ -0,0 +1,316 @@
+// Copyright 2015 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 windows
+
+package registry
+
+import (
+ "errors"
+ "io"
+ "syscall"
+ "unicode/utf16"
+ "unsafe"
+)
+
+const (
+ // Registry value types.
+ NONE = 0
+ SZ = 1
+ EXPAND_SZ = 2
+ BINARY = 3
+ DWORD = 4
+ DWORD_BIG_ENDIAN = 5
+ LINK = 6
+ MULTI_SZ = 7
+ RESOURCE_LIST = 8
+ FULL_RESOURCE_DESCRIPTOR = 9
+ RESOURCE_REQUIREMENTS_LIST = 10
+ QWORD = 11
+)
+
+var (
+ // ErrShortBuffer is returned when the buffer was too short for the operation.
+ ErrShortBuffer = syscall.ERROR_MORE_DATA
+
+ // ErrNotExist is returned when a registry key or value does not exist.
+ ErrNotExist = syscall.ERROR_FILE_NOT_FOUND
+
+ // ErrUnexpectedType is returned by Get*Value when the value's type was unexpected.
+ ErrUnexpectedType = errors.New("unexpected key value type")
+)
+
+// GetValue retrieves the type and data for the specified value associated
+// with an open key k. It fills up buffer buf and returns the retrieved
+// byte count n. If buf is too small to fit the stored value it returns
+// ErrShortBuffer error along with the required buffer size n.
+// If no buffer is provided, it returns true and actual buffer size n.
+// If no buffer is provided, GetValue returns the value's type only.
+// If the value does not exist, the error returned is ErrNotExist.
+//
+// GetValue is a low level function. If value's type is known, use the appropriate
+// Get*Value function instead.
+func (k Key) GetValue(name string, buf []byte) (n int, valtype uint32, err error) {
+ pname, err := syscall.UTF16PtrFromString(name)
+ if err != nil {
+ return 0, 0, err
+ }
+ var pbuf *byte
+ if len(buf) > 0 {
+ pbuf = (*byte)(unsafe.Pointer(&buf[0]))
+ }
+ l := uint32(len(buf))
+ err = syscall.RegQueryValueEx(syscall.Handle(k), pname, nil, &valtype, pbuf, &l)
+ if err != nil {
+ return int(l), valtype, err
+ }
+ return int(l), valtype, nil
+}
+
+func (k Key) getValue(name string, buf []byte) (date []byte, valtype uint32, err error) {
+ p, err := syscall.UTF16PtrFromString(name)
+ if err != nil {
+ return nil, 0, err
+ }
+ var t uint32
+ n := uint32(len(buf))
+ for {
+ err = syscall.RegQueryValueEx(syscall.Handle(k), p, nil, &t, (*byte)(unsafe.Pointer(&buf[0])), &n)
+ if err == nil {
+ return buf[:n], t, nil
+ }
+ if err != syscall.ERROR_MORE_DATA {
+ return nil, 0, err
+ }
+ if n <= uint32(len(buf)) {
+ return nil, 0, err
+ }
+ buf = make([]byte, n)
+ }
+}
+
+// GetStringValue retrieves the string value for the specified
+// value name associated with an open key k. It also returns the value's type.
+// If value does not exist, GetStringValue returns ErrNotExist.
+// If value is not SZ or EXPAND_SZ, it will return the correct value
+// type and ErrUnexpectedType.
+func (k Key) GetStringValue(name string) (val string, valtype uint32, err error) {
+ data, typ, err2 := k.getValue(name, make([]byte, 64))
+ if err2 != nil {
+ return "", typ, err2
+ }
+ switch typ {
+ case SZ, EXPAND_SZ:
+ default:
+ return "", typ, ErrUnexpectedType
+ }
+ if len(data) == 0 {
+ return "", typ, nil
+ }
+ u := (*[1 << 10]uint16)(unsafe.Pointer(&data[0]))[:]
+ return syscall.UTF16ToString(u), typ, nil
+}
+
+// ExpandString expands environment-variable strings and replaces
+// them with the values defined for the current user.
+// Use ExpandString to expand EXPAND_SZ strings.
+func ExpandString(value string) (string, error) {
+ if value == "" {
+ return "", nil
+ }
+ p, err := syscall.UTF16PtrFromString(value)
+ if err != nil {
+ return "", err
+ }
+ r := make([]uint16, 100)
+ for {
+ n, err := expandEnvironmentStrings(p, &r[0], uint32(len(r)))
+ if err != nil {
+ return "", err
+ }
+ if n <= uint32(len(r)) {
+ u := (*[1 << 10]uint16)(unsafe.Pointer(&r[0]))[:]
+ return syscall.UTF16ToString(u), nil
+ }
+ r = make([]uint16, n)
+ }
+}
+
+// GetStringsValue retrieves the []string value for the specified
+// value name associated with an open key k. It also returns the value's type.
+// If value does not exist, GetStringsValue returns ErrNotExist.
+// If value is not MULTI_SZ, it will return the correct value
+// type and ErrUnexpectedType.
+func (k Key) GetStringsValue(name string) (val []string, valtype uint32, err error) {
+ data, typ, err2 := k.getValue(name, make([]byte, 64))
+ if err2 != nil {
+ return nil, typ, err2
+ }
+ if typ != MULTI_SZ {
+ return nil, typ, ErrUnexpectedType
+ }
+ val = make([]string, 0, 5)
+ p := (*[1 << 24]uint16)(unsafe.Pointer(&data[0]))[:len(data)/2]
+ p = p[:len(p)-1] // remove terminating nil
+ from := 0
+ for i, c := range p {
+ if c == 0 {
+ val = append(val, string(utf16.Decode(p[from:i])))
+ from = i + 1
+ }
+ }
+ return val, typ, nil
+}
+
+// GetIntegerValue retrieves the integer value for the specified
+// value name associated with an open key k. It also returns the value's type.
+// If value does not exist, GetIntegerValue returns ErrNotExist.
+// If value is not DWORD or QWORD, it will return the correct value
+// type and ErrUnexpectedType.
+func (k Key) GetIntegerValue(name string) (val uint64, valtype uint32, err error) {
+ data, typ, err2 := k.getValue(name, make([]byte, 8))
+ if err2 != nil {
+ return 0, typ, err2
+ }
+ switch typ {
+ case DWORD:
+ return uint64(*(*uint32)(unsafe.Pointer(&data[0]))), DWORD, nil
+ case QWORD:
+ return uint64(*(*uint64)(unsafe.Pointer(&data[0]))), QWORD, nil
+ default:
+ return 0, typ, ErrUnexpectedType
+ }
+}
+
+// GetBinaryValue retrieves the binary value for the specified
+// value name associated with an open key k. It also returns the value's type.
+// If value does not exist, GetBinaryValue returns ErrNotExist.
+// If value is not BINARY, it will return the correct value
+// type and ErrUnexpectedType.
+func (k Key) GetBinaryValue(name string) (val []byte, valtype uint32, err error) {
+ data, typ, err2 := k.getValue(name, make([]byte, 64))
+ if err2 != nil {
+ return nil, typ, err2
+ }
+ if typ != BINARY {
+ return nil, typ, ErrUnexpectedType
+ }
+ return data, typ, nil
+}
+
+func (k Key) setValue(name string, valtype uint32, data []byte) error {
+ p, err := syscall.UTF16PtrFromString(name)
+ if err != nil {
+ return err
+ }
+ if len(data) == 0 {
+ return regSetValueEx(syscall.Handle(k), p, 0, valtype, nil, 0)
+ }
+ return regSetValueEx(syscall.Handle(k), p, 0, valtype, &data[0], uint32(len(data)))
+}
+
+// SetDWordValue sets the data and type of a name value
+// under key k to value and DWORD.
+func (k Key) SetDWordValue(name string, value uint32) error {
+ return k.setValue(name, DWORD, (*[4]byte)(unsafe.Pointer(&value))[:])
+}
+
+// SetQWordValue sets the data and type of a name value
+// under key k to value and QWORD.
+func (k Key) SetQWordValue(name string, value uint64) error {
+ return k.setValue(name, QWORD, (*[8]byte)(unsafe.Pointer(&value))[:])
+}
+
+func (k Key) setStringValue(name string, valtype uint32, value string) error {
+ v, err := syscall.UTF16FromString(value)
+ if err != nil {
+ return err
+ }
+ buf := (*[1 << 10]byte)(unsafe.Pointer(&v[0]))[:len(v)*2]
+ return k.setValue(name, valtype, buf)
+}
+
+// SetStringValue sets the data and type of a name value
+// under key k to value and SZ. The value must not contain a zero byte.
+func (k Key) SetStringValue(name, value string) error {
+ return k.setStringValue(name, SZ, value)
+}
+
+// SetExpandStringValue sets the data and type of a name value
+// under key k to value and EXPAND_SZ. The value must not contain a zero byte.
+func (k Key) SetExpandStringValue(name, value string) error {
+ return k.setStringValue(name, EXPAND_SZ, value)
+}
+
+// SetStringsValue sets the data and type of a name value
+// under key k to value and MULTI_SZ. The value strings
+// must not contain a zero byte.
+func (k Key) SetStringsValue(name string, value []string) error {
+ ss := ""
+ for _, s := range value {
+ for i := 0; i < len(s); i++ {
+ if s[i] == 0 {
+ return errors.New("string cannot have 0 inside")
+ }
+ }
+ ss += s + "\x00"
+ }
+ v := utf16.Encode([]rune(ss + "\x00"))
+ buf := (*[1 << 10]byte)(unsafe.Pointer(&v[0]))[:len(v)*2]
+ return k.setValue(name, MULTI_SZ, buf)
+}
+
+// SetBinaryValue sets the data and type of a name value
+// under key k to value and BINARY.
+func (k Key) SetBinaryValue(name string, value []byte) error {
+ return k.setValue(name, BINARY, value)
+}
+
+// DeleteValue removes a named value from the key k.
+func (k Key) DeleteValue(name string) error {
+ return regDeleteValue(syscall.Handle(k), syscall.StringToUTF16Ptr(name))
+}
+
+// ReadValueNames returns the value names of key k.
+// The parameter n controls the number of returned names,
+// analogous to the way os.File.Readdirnames works.
+func (k Key) ReadValueNames(n int) ([]string, error) {
+ ki, err := k.Stat()
+ if err != nil {
+ return nil, err
+ }
+ names := make([]string, 0, ki.ValueCount)
+ buf := make([]uint16, ki.MaxValueNameLen+1) // extra room for terminating null character
+loopItems:
+ for i := uint32(0); ; i++ {
+ if n > 0 {
+ if len(names) == n {
+ return names, nil
+ }
+ }
+ l := uint32(len(buf))
+ for {
+ err := regEnumValue(syscall.Handle(k), i, &buf[0], &l, nil, nil, nil, nil)
+ if err == nil {
+ break
+ }
+ if err == syscall.ERROR_MORE_DATA {
+ println(len(buf), l)
+ // Double buffer size and try again.
+ l = uint32(2 * len(buf))
+ buf = make([]uint16, l)
+ continue
+ }
+ if err == _ERROR_NO_MORE_ITEMS {
+ break loopItems
+ }
+ return names, err
+ }
+ names = append(names, syscall.UTF16ToString(buf[:l]))
+ }
+ if n > len(names) {
+ return names, io.EOF
+ }
+ return names, nil
+}
diff --git a/src/internal/syscall/windows/registry/zsyscall_windows.go b/src/internal/syscall/windows/registry/zsyscall_windows.go
new file mode 100644
index 0000000000..2b3de633c9
--- /dev/null
+++ b/src/internal/syscall/windows/registry/zsyscall_windows.go
@@ -0,0 +1,73 @@
+// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
+
+package registry
+
+import "unsafe"
+import "syscall"
+
+var _ unsafe.Pointer
+
+var (
+ modadvapi32 = syscall.NewLazyDLL("advapi32.dll")
+ modkernel32 = syscall.NewLazyDLL("kernel32.dll")
+
+ procRegCreateKeyExW = modadvapi32.NewProc("RegCreateKeyExW")
+ procRegDeleteKeyW = modadvapi32.NewProc("RegDeleteKeyW")
+ procRegSetValueExW = modadvapi32.NewProc("RegSetValueExW")
+ procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW")
+ procRegDeleteValueW = modadvapi32.NewProc("RegDeleteValueW")
+ procExpandEnvironmentStringsW = modkernel32.NewProc("ExpandEnvironmentStringsW")
+)
+
+func regCreateKeyEx(key syscall.Handle, subkey *uint16, reserved uint32, class *uint16, options uint32, desired uint32, sa *syscall.SecurityAttributes, result *syscall.Handle, disposition *uint32) (regerrno error) {
+ r0, _, _ := syscall.Syscall9(procRegCreateKeyExW.Addr(), 9, uintptr(key), uintptr(unsafe.Pointer(subkey)), uintptr(reserved), uintptr(unsafe.Pointer(class)), uintptr(options), uintptr(desired), uintptr(unsafe.Pointer(sa)), uintptr(unsafe.Pointer(result)), uintptr(unsafe.Pointer(disposition)))
+ if r0 != 0 {
+ regerrno = syscall.Errno(r0)
+ }
+ return
+}
+
+func regDeleteKey(key syscall.Handle, subkey *uint16) (regerrno error) {
+ r0, _, _ := syscall.Syscall(procRegDeleteKeyW.Addr(), 2, uintptr(key), uintptr(unsafe.Pointer(subkey)), 0)
+ if r0 != 0 {
+ regerrno = syscall.Errno(r0)
+ }
+ return
+}
+
+func regSetValueEx(key syscall.Handle, valueName *uint16, reserved uint32, vtype uint32, buf *byte, bufsize uint32) (regerrno error) {
+ r0, _, _ := syscall.Syscall6(procRegSetValueExW.Addr(), 6, uintptr(key), uintptr(unsafe.Pointer(valueName)), uintptr(reserved), uintptr(vtype), uintptr(unsafe.Pointer(buf)), uintptr(bufsize))
+ if r0 != 0 {
+ regerrno = syscall.Errno(r0)
+ }
+ return
+}
+
+func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) {
+ r0, _, _ := syscall.Syscall9(procRegEnumValueW.Addr(), 8, uintptr(key), uintptr(index), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(valtype)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(buflen)), 0)
+ if r0 != 0 {
+ regerrno = syscall.Errno(r0)
+ }
+ return
+}
+
+func regDeleteValue(key syscall.Handle, name *uint16) (regerrno error) {
+ r0, _, _ := syscall.Syscall(procRegDeleteValueW.Addr(), 2, uintptr(key), uintptr(unsafe.Pointer(name)), 0)
+ if r0 != 0 {
+ regerrno = syscall.Errno(r0)
+ }
+ return
+}
+
+func expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) {
+ r0, _, e1 := syscall.Syscall(procExpandEnvironmentStringsW.Addr(), 3, uintptr(unsafe.Pointer(src)), uintptr(unsafe.Pointer(dst)), uintptr(size))
+ n = uint32(r0)
+ if n == 0 {
+ if e1 != 0 {
+ err = error(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go
index 49bfeea1f4..dc8a91626d 100644
--- a/src/internal/syscall/windows/syscall_windows.go
+++ b/src/internal/syscall/windows/syscall_windows.go
@@ -95,8 +95,8 @@ const (
)
//sys GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizeOfPointer *uint32) (errcode error) = iphlpapi.GetAdaptersAddresses
-
//sys GetComputerNameEx(nameformat uint32, buf *uint16, n *uint32) (err error) = GetComputerNameExW
+//sys MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) = MoveFileExW
const (
ComputerNameNetBIOS = 0
@@ -108,4 +108,23 @@ const (
ComputerNamePhysicalDnsDomain = 6
ComputerNamePhysicalDnsFullyQualified = 7
ComputerNameMax = 8
+
+ MOVEFILE_REPLACE_EXISTING = 0x1
+ MOVEFILE_COPY_ALLOWED = 0x2
+ MOVEFILE_DELAY_UNTIL_REBOOT = 0x4
+ MOVEFILE_WRITE_THROUGH = 0x8
+ MOVEFILE_CREATE_HARDLINK = 0x10
+ MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x20
)
+
+func Rename(oldpath, newpath string) error {
+ from, err := syscall.UTF16PtrFromString(oldpath)
+ if err != nil {
+ return err
+ }
+ to, err := syscall.UTF16PtrFromString(newpath)
+ if err != nil {
+ return err
+ }
+ return MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING)
+}
diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go
index 50c7c5165b..c6f607a46a 100644
--- a/src/internal/syscall/windows/zsyscall_windows.go
+++ b/src/internal/syscall/windows/zsyscall_windows.go
@@ -13,6 +13,7 @@ var (
procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses")
procGetComputerNameExW = modkernel32.NewProc("GetComputerNameExW")
+ procMoveFileExW = modkernel32.NewProc("MoveFileExW")
)
func GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizeOfPointer *uint32) (errcode error) {
@@ -34,3 +35,15 @@ func GetComputerNameEx(nameformat uint32, buf *uint16, n *uint32) (err error) {
}
return
}
+
+func MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) {
+ r1, _, e1 := syscall.Syscall(procMoveFileExW.Addr(), 3, uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), uintptr(flags))
+ if r1 == 0 {
+ if e1 != 0 {
+ err = error(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
diff --git a/src/internal/trace/parser.go b/src/internal/trace/parser.go
index f1f709e4fd..8d48bfdfe6 100644
--- a/src/internal/trace/parser.go
+++ b/src/internal/trace/parser.go
@@ -235,6 +235,12 @@ func parseEvents(rawEvents []rawEvent) (events []*Event, err error) {
EvGoBlockSelect, EvGoBlockSync, EvGoBlockCond, EvGoBlockNet,
EvGoSysBlock:
lastG = 0
+ case EvGoSysExit:
+ if e.Args[1] != 0 {
+ // EvGoSysExit emission is delayed until the thread has a P.
+ // Give it the real timestamp.
+ e.Ts = int64(e.Args[1])
+ }
}
events = append(events, e)
}
@@ -344,7 +350,6 @@ func postProcessTrace(events []*Event) error {
type pdesc struct {
running bool
g uint64
- evGC *Event
evScan *Event
evSweep *Event
}
@@ -352,6 +357,7 @@ func postProcessTrace(events []*Event) error {
gs := make(map[uint64]gdesc)
ps := make(map[int]pdesc)
gs[0] = gdesc{state: gRunning}
+ var evGC *Event
checkRunning := func(p pdesc, g gdesc, ev *Event) error {
name := EventDescriptions[ev.Type].Name
@@ -383,16 +389,16 @@ func postProcessTrace(events []*Event) error {
}
p.running = false
case EvGCStart:
- if p.evGC != nil {
+ if evGC != nil {
return fmt.Errorf("previous GC is not ended before a new one (offset %v, time %v)", ev.Off, ev.Ts)
}
- p.evGC = ev
+ evGC = ev
case EvGCDone:
- if p.evGC == nil {
+ if evGC == nil {
return fmt.Errorf("bogus GC end (offset %v, time %v)", ev.Off, ev.Ts)
}
- p.evGC.Link = ev
- p.evGC = nil
+ evGC.Link = ev
+ evGC = nil
case EvGCScanStart:
if p.evScan != nil {
return fmt.Errorf("previous scanning is not ended before a new one (offset %v, time %v)", ev.Off, ev.Ts)
@@ -423,7 +429,12 @@ func postProcessTrace(events []*Event) error {
g1.state = gWaiting
gs[ev.Args[0]] = g1
case EvGoInSyscall:
- // this case is intentionally left blank
+ g1 := gs[ev.Args[0]]
+ if g1.state != gRunnable {
+ return fmt.Errorf("g %v is not runnable before EvGoInSyscall (offset %v, time %v)", ev.Args[0], ev.Off, ev.Ts)
+ }
+ g1.state = gWaiting
+ gs[ev.Args[0]] = g1
case EvGoCreate:
if err := checkRunning(p, g, ev); err != nil {
return err
@@ -498,17 +509,18 @@ func postProcessTrace(events []*Event) error {
if err := checkRunning(p, g, ev); err != nil {
return err
}
- g.state = gRunnable
+ g.state = gWaiting
g.evStart.Link = ev
g.evStart = nil
p.g = 0
case EvGoSysExit:
- if g.state != gRunnable {
- return fmt.Errorf("g %v is not runnable during syscall exit (offset %v, time %v)", ev.G, ev.Off, ev.Ts)
+ if g.state != gWaiting {
+ return fmt.Errorf("g %v is not waiting during syscall exit (offset %v, time %v)", ev.G, ev.Off, ev.Ts)
}
if g.ev != nil && g.ev.Type == EvGoSysCall {
g.ev.Link = ev
}
+ g.state = gRunnable
g.ev = ev
case EvGoSleep, EvGoBlock, EvGoBlockSend, EvGoBlockRecv,
EvGoBlockSelect, EvGoBlockSync, EvGoBlockCond, EvGoBlockNet:
@@ -638,6 +650,18 @@ func (l eventList) Swap(i, j int) {
l[i], l[j] = l[j], l[i]
}
+// Print dumps events to stdout. For debugging.
+func Print(events []*Event) {
+ for _, ev := range events {
+ desc := EventDescriptions[ev.Type]
+ fmt.Printf("%v %v p=%v g=%v off=%v", ev.Ts, desc.Name, ev.P, ev.G, ev.Off)
+ for i, a := range desc.Args {
+ fmt.Printf(" %v=%v", a, ev.Args[i])
+ }
+ fmt.Printf("\n")
+ }
+}
+
// Event types in the trace.
// Verbatim copy from src/runtime/trace.go.
const (
@@ -670,7 +694,7 @@ const (
EvGoBlockCond = 26 // goroutine blocks on Cond [timestamp, stack]
EvGoBlockNet = 27 // goroutine blocks on network [timestamp, stack]
EvGoSysCall = 28 // syscall enter [timestamp, stack]
- EvGoSysExit = 29 // syscall exit [timestamp, goroutine id]
+ EvGoSysExit = 29 // syscall exit [timestamp, goroutine id, real timestamp]
EvGoSysBlock = 30 // syscall blocks [timestamp]
EvGoWaiting = 31 // denotes that goroutine is blocked when tracing starts [goroutine id]
EvGoInSyscall = 32 // denotes that goroutine is in syscall when tracing starts [goroutine id]
@@ -715,7 +739,7 @@ var EventDescriptions = [EvCount]struct {
EvGoBlockCond: {"GoBlockCond", true, []string{}},
EvGoBlockNet: {"GoBlockNet", true, []string{}},
EvGoSysCall: {"GoSysCall", true, []string{}},
- EvGoSysExit: {"GoSysExit", false, []string{"g"}},
+ EvGoSysExit: {"GoSysExit", false, []string{"g", "ts"}},
EvGoSysBlock: {"GoSysBlock", false, []string{}},
EvGoWaiting: {"GoWaiting", false, []string{"g"}},
EvGoInSyscall: {"GoInSyscall", false, []string{"g"}},
diff --git a/src/io/io.go b/src/io/io.go
index 7507a84929..290fc8824b 100644
--- a/src/io/io.go
+++ b/src/io/io.go
@@ -348,6 +348,23 @@ func CopyN(dst Writer, src Reader, n int64) (written int64, err error) {
// Otherwise, if dst implements the ReaderFrom interface,
// the copy is implemented by calling dst.ReadFrom(src).
func Copy(dst Writer, src Reader) (written int64, err error) {
+ return copyBuffer(dst, src, nil)
+}
+
+// CopyBuffer is identical to Copy except that it stages through the
+// provided buffer (if one is required) rather than allocating a
+// temporary one. If buf is nil, one is allocated; otherwise if it has
+// zero length, CopyBuffer panics.
+func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
+ if buf != nil && len(buf) == 0 {
+ panic("empty buffer in io.CopyBuffer")
+ }
+ return copyBuffer(dst, src, buf)
+}
+
+// copyBuffer is the actual implementation of Copy and CopyBuffer.
+// if buf is nil, one is allocated.
+func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
// If the reader has a WriteTo method, use it to do the copy.
// Avoids an allocation and a copy.
if wt, ok := src.(WriterTo); ok {
@@ -357,7 +374,9 @@ func Copy(dst Writer, src Reader) (written int64, err error) {
if rt, ok := dst.(ReaderFrom); ok {
return rt.ReadFrom(src)
}
- buf := make([]byte, 32*1024)
+ if buf == nil {
+ buf = make([]byte, 32*1024)
+ }
for {
nr, er := src.Read(buf)
if nr > 0 {
diff --git a/src/io/io_test.go b/src/io/io_test.go
index 57db1fbf0b..e892574b0b 100644
--- a/src/io/io_test.go
+++ b/src/io/io_test.go
@@ -20,7 +20,7 @@ type Buffer struct {
WriterTo // conflicts with and hides bytes.Buffer's WriterTo.
}
-// Simple tests, primarily to verify the ReadFrom and WriteTo callouts inside Copy and CopyN.
+// Simple tests, primarily to verify the ReadFrom and WriteTo callouts inside Copy, CopyBuffer and CopyN.
func TestCopy(t *testing.T) {
rb := new(Buffer)
@@ -32,6 +32,26 @@ func TestCopy(t *testing.T) {
}
}
+func TestCopyBuffer(t *testing.T) {
+ rb := new(Buffer)
+ wb := new(Buffer)
+ rb.WriteString("hello, world.")
+ CopyBuffer(wb, rb, make([]byte, 1)) // Tiny buffer to keep it honest.
+ if wb.String() != "hello, world." {
+ t.Errorf("CopyBuffer did not work properly")
+ }
+}
+
+func TestCopyBufferNil(t *testing.T) {
+ rb := new(Buffer)
+ wb := new(Buffer)
+ rb.WriteString("hello, world.")
+ CopyBuffer(wb, rb, nil) // Should allocate a buffer.
+ if wb.String() != "hello, world." {
+ t.Errorf("CopyBuffer did not work properly")
+ }
+}
+
func TestCopyReadFrom(t *testing.T) {
rb := new(Buffer)
wb := new(bytes.Buffer) // implements ReadFrom.
@@ -78,6 +98,34 @@ func TestCopyPriority(t *testing.T) {
}
}
+type zeroErrReader struct {
+ err error
+}
+
+func (r zeroErrReader) Read(p []byte) (int, error) {
+ return copy(p, []byte{0}), r.err
+}
+
+type errWriter struct {
+ err error
+}
+
+func (w errWriter) Write([]byte) (int, error) {
+ return 0, w.err
+}
+
+// In case a Read results in an error with non-zero bytes read, and
+// the subsequent Write also results in an error, the error from Write
+// is returned, as it is the one that prevented progressing further.
+func TestCopyReadErrWriteErr(t *testing.T) {
+ er, ew := errors.New("readError"), errors.New("writeError")
+ r, w := zeroErrReader{err: er}, errWriter{err: ew}
+ n, err := Copy(w, r)
+ if n != 0 || err != ew {
+ t.Errorf("Copy(zeroErrReader, errWriter) = %d, %v; want 0, writeError", n, err)
+ }
+}
+
func TestCopyN(t *testing.T) {
rb := new(Buffer)
wb := new(Buffer)
diff --git a/src/io/ioutil/tempfile.go b/src/io/ioutil/tempfile.go
index 4a06e9756f..61d4a7ad37 100644
--- a/src/io/ioutil/tempfile.go
+++ b/src/io/ioutil/tempfile.go
@@ -55,7 +55,9 @@ func TempFile(dir, prefix string) (f *os.File, err error) {
f, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
if os.IsExist(err) {
if nconflict++; nconflict > 10 {
+ randmu.Lock()
rand = reseed()
+ randmu.Unlock()
}
continue
}
@@ -82,7 +84,9 @@ func TempDir(dir, prefix string) (name string, err error) {
err = os.Mkdir(try, 0700)
if os.IsExist(err) {
if nconflict++; nconflict > 10 {
+ randmu.Lock()
rand = reseed()
+ randmu.Unlock()
}
continue
}
diff --git a/src/iostest.bash b/src/iostest.bash
index 13f5e0cd94..5e09894852 100755
--- a/src/iostest.bash
+++ b/src/iostest.bash
@@ -20,25 +20,31 @@ if [ "$GOOS" != "darwin" ]; then
echo "iostest.bash requires GOOS=darwin, got GOOS=$GOOS" 1>&2
exit 1
fi
+if [ "$GOARCH" != "arm" ] && [ "$GOARCH" != "arm64" ]; then
+ echo "iostest.bash requires GOARCH=arm or GOARCH=arm64, got GOARCH=$GOARCH" 1>&2
+ exit 1
+fi
if [ "$GOARCH" == "arm" ]; then
export GOARM=7
fi
-# Reboot to make sure previous runs do not interfere with the current run.
-# It is reasonably easy for a bad program leave an iOS device in an
-# almost unusable state.
-idevicediagnostics restart
-# Initial sleep to make sure we are restarting before we start polling.
-sleep 30
-# Poll until the device has restarted.
-until idevicediagnostics diagnostics; do
- # TODO(crawshaw): replace with a test app using go_darwin_arm_exec.
- echo "waiting for idevice to come online"
- sleep 10
-done
-# Diagnostics are reported during boot before the device can start an
-# app. Wait a little longer before trying to use the device.
-sleep 30
+if [ "$1" == "-restart" ]; then
+ # Reboot to make sure previous runs do not interfere with the current run.
+ # It is reasonably easy for a bad program leave an iOS device in an
+ # almost unusable state.
+ idevicediagnostics restart
+ # Initial sleep to make sure we are restarting before we start polling.
+ sleep 30
+ # Poll until the device has restarted.
+ until idevicediagnostics diagnostics; do
+ # TODO(crawshaw): replace with a test app using go_darwin_arm_exec.
+ echo "waiting for idevice to come online"
+ sleep 10
+ done
+ # Diagnostics are reported during boot before the device can start an
+ # app. Wait a little longer before trying to use the device.
+ sleep 30
+fi
unset GOBIN
export GOROOT=$(dirname $(pwd))
@@ -47,12 +53,17 @@ export CGO_ENABLED=1
export CC_FOR_TARGET=$GOROOT/misc/ios/clangwrap.sh
# Run the build for the host bootstrap, so we can build go_darwin_arm_exec.
-# Also lets us fail early before the (slow) adb push if the build is broken.
+# Also lets us fail early before the (slow) ios-deploy if the build is broken.
./make.bash
GOOS=$GOHOSTOS GOARCH=$GOHOSTARCH go build \
-o ../bin/go_darwin_${GOARCH}_exec \
../misc/ios/go_darwin_arm_exec.go
+if [ "$GOIOS_DEV_ID" == "" ]; then
+ echo "detecting iOS development identity"
+ eval $(GOOS=$GOHOSTOS GOARCH=$GOHOSTARCH go run ../misc/ios/detect.go)
+fi
+
# Run standard build and tests.
./all.bash --no-clean
diff --git a/src/log/log.go b/src/log/log.go
index 17646a12fa..4cfe550300 100644
--- a/src/log/log.go
+++ b/src/log/log.go
@@ -32,11 +32,12 @@ const (
// 2009/01/23 01:23:23 message
// while flags Ldate | Ltime | Lmicroseconds | Llongfile produce,
// 2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
- Ldate = 1 << iota // the date: 2009/01/23
- Ltime // the time: 01:23:23
+ Ldate = 1 << iota // the date in the local time zone: 2009/01/23
+ Ltime // the time in the local time zone: 01:23:23
Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime.
Llongfile // full file name and line number: /a/b/c/d.go:23
Lshortfile // final file name element and line number: d.go:23. overrides Llongfile
+ LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone
LstdFlags = Ldate | Ltime // initial values for the standard logger
)
@@ -88,6 +89,9 @@ func itoa(buf *[]byte, i int, wid int) {
func (l *Logger) formatHeader(buf *[]byte, t time.Time, file string, line int) {
*buf = append(*buf, l.prefix...)
+ if l.flag&LUTC != 0 {
+ t = t.UTC()
+ }
if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 {
if l.flag&Ldate != 0 {
year, month, day := t.Date()
@@ -156,7 +160,7 @@ func (l *Logger) Output(calldepth int, s string) error {
l.buf = l.buf[:0]
l.formatHeader(&l.buf, now, file, line)
l.buf = append(l.buf, s...)
- if len(s) > 0 && s[len(s)-1] != '\n' {
+ if len(s) == 0 || s[len(s)-1] != '\n' {
l.buf = append(l.buf, '\n')
}
_, err := l.out.Write(l.buf)
diff --git a/src/log/log_test.go b/src/log/log_test.go
index 14e0b29263..709de1e542 100644
--- a/src/log/log_test.go
+++ b/src/log/log_test.go
@@ -8,16 +8,19 @@ package log
import (
"bytes"
+ "fmt"
"os"
"regexp"
+ "strings"
"testing"
+ "time"
)
const (
Rdate = `[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]`
Rtime = `[0-9][0-9]:[0-9][0-9]:[0-9][0-9]`
Rmicroseconds = `\.[0-9][0-9][0-9][0-9][0-9][0-9]`
- Rline = `(54|56):` // must update if the calls to l.Printf / l.Print below move
+ Rline = `(57|59):` // must update if the calls to l.Printf / l.Print below move
Rlongfile = `.*/[A-Za-z0-9_\-]+\.go:` + Rline
Rshortfile = `[A-Za-z0-9_\-]+\.go:` + Rline
)
@@ -118,6 +121,44 @@ func TestFlagAndPrefixSetting(t *testing.T) {
}
}
+func TestUTCFlag(t *testing.T) {
+ var b bytes.Buffer
+ l := New(&b, "Test:", LstdFlags)
+ l.SetFlags(Ldate | Ltime | LUTC)
+ // Verify a log message looks right in the right time zone. Quantize to the second only.
+ now := time.Now().UTC()
+ l.Print("hello")
+ want := fmt.Sprintf("Test:%d/%.2d/%.2d %.2d:%.2d:%.2d hello\n",
+ now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
+ got := b.String()
+ if got == want {
+ return
+ }
+ // It's possible we crossed a second boundary between getting now and logging,
+ // so add a second and try again. This should very nearly always work.
+ now.Add(time.Second)
+ want = fmt.Sprintf("Test:%d/%.2d/%.2d %.2d:%.2d:%.2d hello\n",
+ now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute())
+ if got == want {
+ return
+ }
+ t.Errorf("got %q; want %q", got, want)
+}
+
+func TestEmptyPrintCreatesLine(t *testing.T) {
+ var b bytes.Buffer
+ l := New(&b, "Header:", LstdFlags)
+ l.Print()
+ l.Println("non-empty")
+ output := b.String()
+ if n := strings.Count(output, "Header"); n != 2 {
+ t.Errorf("expected 2 headers, got %d", n)
+ }
+ if n := strings.Count(output, "\n"); n != 2 {
+ t.Errorf("expected 2 lines, got %d", n)
+ }
+}
+
func BenchmarkItoa(b *testing.B) {
dst := make([]byte, 0, 64)
for i := 0; i < b.N; i++ {
diff --git a/src/log/syslog/doc.go b/src/log/syslog/doc.go
new file mode 100644
index 0000000000..54e76edb34
--- /dev/null
+++ b/src/log/syslog/doc.go
@@ -0,0 +1,18 @@
+// 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 syslog provides a simple interface to the system log
+// service. It can send messages to the syslog daemon using UNIX
+// domain sockets, UDP or TCP.
+//
+// Only one call to Dial is necessary. On write failures,
+// the syslog client will attempt to reconnect to the server
+// and write again.
+package syslog
+
+// BUG(brainman): This package is not implemented on Windows yet.
+
+// BUG(akumar): This package is not implemented on Plan 9 yet.
+
+// BUG(minux): This package is not implemented on NaCl (Native Client) yet.
diff --git a/src/log/syslog/syslog.go b/src/log/syslog/syslog.go
index 5e09599162..4bf447626f 100644
--- a/src/log/syslog/syslog.go
+++ b/src/log/syslog/syslog.go
@@ -4,13 +4,6 @@
// +build !windows,!nacl,!plan9
-// Package syslog provides a simple interface to the system log
-// service. It can send messages to the syslog daemon using UNIX
-// domain sockets, UDP or TCP.
-//
-// Only one call to Dial is necessary. On write failures,
-// the syslog client will attempt to reconnect to the server
-// and write again.
package syslog
import (
diff --git a/src/log/syslog/syslog_test.go b/src/log/syslog/syslog_test.go
index 7f7d7fd6d8..85aec536ab 100644
--- a/src/log/syslog/syslog_test.go
+++ b/src/log/syslog/syslog_test.go
@@ -121,8 +121,11 @@ func TestWithSimulated(t *testing.T) {
msg := "Test 123"
transport := []string{"unix", "unixgram", "udp", "tcp"}
- if runtime.GOOS == "darwin" && runtime.GOARCH == "arm" {
- transport = []string{"udp", "tcp"}
+ if runtime.GOOS == "darwin" {
+ switch runtime.GOARCH {
+ case "arm", "arm64":
+ transport = []string{"udp", "tcp"}
+ }
}
for _, tr := range transport {
@@ -147,8 +150,11 @@ func TestWithSimulated(t *testing.T) {
}
func TestFlap(t *testing.T) {
- if runtime.GOOS == "darwin" && runtime.GOARCH == "arm" {
- t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
+ if runtime.GOOS == "darwin" {
+ switch runtime.GOARCH {
+ case "arm", "arm64":
+ t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
+ }
}
net := "unix"
@@ -315,8 +321,11 @@ func TestConcurrentReconnect(t *testing.T) {
const N = 10
const M = 100
net := "unix"
- if runtime.GOOS == "darwin" && runtime.GOARCH == "arm" {
- net = "tcp"
+ if runtime.GOOS == "darwin" {
+ switch runtime.GOARCH {
+ case "arm", "arm64":
+ net = "tcp"
+ }
}
done := make(chan string, N*M)
addr, sock, srvWG := startServer(net, "", done)
diff --git a/src/log/syslog/syslog_windows.go b/src/log/syslog/syslog_windows.go
deleted file mode 100644
index 8d99e2e594..0000000000
--- a/src/log/syslog/syslog_windows.go
+++ /dev/null
@@ -1,8 +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 syslog provides a simple interface to the system log service.
-package syslog
-
-// BUG(brainman): This package is not implemented on Windows yet.
diff --git a/src/math/all_test.go b/src/math/all_test.go
index c07ac740e3..84061be264 100644
--- a/src/math/all_test.go
+++ b/src/math/all_test.go
@@ -2977,15 +2977,56 @@ func BenchmarkSinh(b *testing.B) {
}
}
+var Global float64
+
func BenchmarkSqrt(b *testing.B) {
+ x, y := 0.0, 10.0
for i := 0; i < b.N; i++ {
- Sqrt(10)
+ x += Sqrt(y)
}
+ Global = x
+}
+
+func BenchmarkSqrtIndirect(b *testing.B) {
+ x, y := 0.0, 10.0
+ f := Sqrt
+ for i := 0; i < b.N; i++ {
+ x += f(y)
+ }
+ Global = x
}
func BenchmarkSqrtGo(b *testing.B) {
+ x, y := 0.0, 10.0
for i := 0; i < b.N; i++ {
- SqrtGo(10)
+ x += SqrtGo(y)
+ }
+ Global = x
+}
+
+func isPrime(i int) bool {
+ // Yes, this is a dumb way to write this code,
+ // but calling Sqrt repeatedly in this way demonstrates
+ // the benefit of using a direct SQRT instruction on systems
+ // that have one, whereas the obvious loop seems not to
+ // demonstrate such a benefit.
+ for j := 2; float64(j) <= Sqrt(float64(i)); j++ {
+ if i%j == 0 {
+ return false
+ }
+ }
+ return true
+}
+
+func BenchmarkSqrtPrime(b *testing.B) {
+ any := false
+ for i := 0; i < b.N; i++ {
+ if isPrime(100003) {
+ any = true
+ }
+ }
+ if any {
+ Global = 1
}
}
diff --git a/src/math/big/arith.go b/src/math/big/arith.go
index 328c85c4f7..1ff6349d9d 100644
--- a/src/math/big/arith.go
+++ b/src/math/big/arith.go
@@ -196,7 +196,6 @@ func subVV_g(z, x, y []Word) (c Word) {
return
}
-// Argument y must be either 0 or 1.
// The resulting carry c is either 0 or 1.
func addVW_g(z, x []Word, y Word) (c Word) {
if use_addWW_g {
diff --git a/src/math/big/arith_arm64.s b/src/math/big/arith_arm64.s
index 6e10e47be3..24a717cbb0 100644
--- a/src/math/big/arith_arm64.s
+++ b/src/math/big/arith_arm64.s
@@ -9,38 +9,169 @@
// This file provides fast assembly versions for the elementary
// arithmetic operations on vectors implemented in arith.go.
+// TODO: Consider re-implementing using Advanced SIMD
+// once the assembler supports those instructions.
+
+// func mulWW(x, y Word) (z1, z0 Word)
TEXT ·mulWW(SB),NOSPLIT,$0
- B ·mulWW_g(SB)
+ MOVD x+0(FP), R0
+ MOVD y+8(FP), R1
+ MUL R0, R1, R2
+ UMULH R0, R1, R3
+ MOVD R3, z1+16(FP)
+ MOVD R2, z0+24(FP)
+ RET
+
+// func divWW(x1, x0, y Word) (q, r Word)
TEXT ·divWW(SB),NOSPLIT,$0
- B ·divWW_g(SB)
+ B ·divWW_g(SB) // ARM64 has no multiword division
+
+// func addVV(z, x, y []Word) (c Word)
TEXT ·addVV(SB),NOSPLIT,$0
- B ·addVV_g(SB)
+ MOVD z+0(FP), R3
+ MOVD z_len+8(FP), R0
+ MOVD x+24(FP), R1
+ MOVD y+48(FP), R2
+ ADDS $0, R0 // clear carry flag
+loop:
+ CBZ R0, done // careful not to touch the carry flag
+ MOVD.P 8(R1), R4
+ MOVD.P 8(R2), R5
+ ADCS R4, R5
+ MOVD.P R5, 8(R3)
+ SUB $1, R0
+ B loop
+done:
+ CSET HS, R0 // extract carry flag
+ MOVD R0, c+72(FP)
+ RET
+
+// func subVV(z, x, y []Word) (c Word)
TEXT ·subVV(SB),NOSPLIT,$0
- B ·subVV_g(SB)
+ MOVD z+0(FP), R3
+ MOVD z_len+8(FP), R0
+ MOVD x+24(FP), R1
+ MOVD y+48(FP), R2
+ CMP R0, R0 // set carry flag
+loop:
+ CBZ R0, done // careful not to touch the carry flag
+ MOVD.P 8(R1), R4
+ MOVD.P 8(R2), R5
+ SBCS R5, R4
+ MOVD.P R4, 8(R3)
+ SUB $1, R0
+ B loop
+done:
+ CSET LO, R0 // extract carry flag
+ MOVD R0, c+72(FP)
+ RET
+
+// func addVW(z, x []Word, y Word) (c Word)
TEXT ·addVW(SB),NOSPLIT,$0
- B ·addVW_g(SB)
+ MOVD z+0(FP), R3
+ MOVD z_len+8(FP), R0
+ MOVD x+24(FP), R1
+ MOVD y+48(FP), R2
+ CBZ R0, return_y
+ MOVD.P 8(R1), R4
+ ADDS R2, R4
+ MOVD.P R4, 8(R3)
+ SUB $1, R0
+loop:
+ CBZ R0, done // careful not to touch the carry flag
+ MOVD.P 8(R1), R4
+ ADCS $0, R4
+ MOVD.P R4, 8(R3)
+ SUB $1, R0
+ B loop
+done:
+ CSET HS, R0 // extract carry flag
+ MOVD R0, c+56(FP)
+ RET
+return_y: // z is empty; copy y to c
+ MOVD R2, c+56(FP)
+ RET
+
+// func subVW(z, x []Word, y Word) (c Word)
TEXT ·subVW(SB),NOSPLIT,$0
- B ·subVW_g(SB)
+ MOVD z+0(FP), R3
+ MOVD z_len+8(FP), R0
+ MOVD x+24(FP), R1
+ MOVD y+48(FP), R2
+ CBZ R0, rety
+ MOVD.P 8(R1), R4
+ SUBS R2, R4
+ MOVD.P R4, 8(R3)
+ SUB $1, R0
+loop:
+ CBZ R0, done // careful not to touch the carry flag
+ MOVD.P 8(R1), R4
+ SBCS $0, R4
+ MOVD.P R4, 8(R3)
+ SUB $1, R0
+ B loop
+done:
+ CSET LO, R0 // extract carry flag
+ MOVD R0, c+56(FP)
+ RET
+rety: // z is empty; copy y to c
+ MOVD R2, c+56(FP)
+ RET
+
+// func shlVU(z, x []Word, s uint) (c Word)
TEXT ·shlVU(SB),NOSPLIT,$0
B ·shlVU_g(SB)
+
+// func shrVU(z, x []Word, s uint) (c Word)
TEXT ·shrVU(SB),NOSPLIT,$0
B ·shrVU_g(SB)
-TEXT ·mulAddVWW(SB),NOSPLIT,$0
- B ·mulAddVWW_g(SB)
+// func mulAddVWW(z, x []Word, y, r Word) (c Word)
+TEXT ·mulAddVWW(SB),NOSPLIT,$0
+ MOVD z+0(FP), R1
+ MOVD z_len+8(FP), R0
+ MOVD x+24(FP), R2
+ MOVD y+48(FP), R3
+ MOVD r+56(FP), R4
+loop:
+ CBZ R0, done
+ MOVD.P 8(R2), R5
+ UMULH R5, R3, R7
+ MUL R5, R3, R6
+ ADDS R4, R6
+ ADC $0, R7
+ MOVD.P R6, 8(R1)
+ MOVD R7, R4
+ SUB $1, R0
+ B loop
+done:
+ MOVD R4, c+64(FP)
+ RET
+
+
+// func addMulVVW(z, x []Word, y Word) (c Word)
TEXT ·addMulVVW(SB),NOSPLIT,$0
B ·addMulVVW_g(SB)
+
+// func divWVW(z []Word, xn Word, x []Word, y Word) (r Word)
TEXT ·divWVW(SB),NOSPLIT,$0
B ·divWVW_g(SB)
+
+// func bitLen(x Word) (n int)
TEXT ·bitLen(SB),NOSPLIT,$0
- B ·bitLen_g(SB)
+ MOVD x+0(FP), R0
+ CLZ R0, R0
+ MOVD $64, R1
+ SUB R0, R1, R0
+ MOVD R0, n+8(FP)
+ RET
diff --git a/src/math/big/arith_test.go b/src/math/big/arith_test.go
index cd92dd7173..f46a494f17 100644
--- a/src/math/big/arith_test.go
+++ b/src/math/big/arith_test.go
@@ -155,6 +155,7 @@ var sumVW = []argVW{
{nat{1}, nat{1}, 0, 0},
{nat{0}, nat{_M}, 1, 1},
{nat{0, 0, 0, 0}, nat{_M, _M, _M, _M}, 1, 1},
+ {nat{585}, nat{314}, 271, 0},
}
var prodVW = []argVW{
diff --git a/src/math/big/float.go b/src/math/big/float.go
index 2e536e04ad..d46c046e67 100644
--- a/src/math/big/float.go
+++ b/src/math/big/float.go
@@ -65,15 +65,19 @@ type Float struct {
exp int32
}
-// Float operations that would lead to a NaN under IEEE-754 rules cause
-// a run-time panic of ErrNaN type.
+// An ErrNaN panic is raised by a Float operation that would lead to
+// a NaN under IEEE-754 rules. An ErrNaN implements the error interface.
type ErrNaN struct {
msg string
}
+func (err ErrNaN) Error() string {
+ return err.msg
+}
+
// NewFloat allocates and returns a new Float set to x,
// with precision 53 and rounding mode ToNearestEven.
-// NewFloat panics with ErrNan if x is a NaN.
+// NewFloat panics with ErrNaN if x is a NaN.
func NewFloat(x float64) *Float {
if math.IsNaN(x) {
panic(ErrNaN{"NewFloat(NaN)"})
@@ -1400,8 +1404,9 @@ func (x *Float) ucmp(y *Float) int {
// it is changed to the larger of x's or y's precision before the operation.
// Rounding is performed according to z's precision and rounding mode; and
// z's accuracy reports the result error relative to the exact (not rounded)
-// result.
-// BUG(gri) Float.Add panics if an operand is Inf.
+// result. Add panics with ErrNaN if x and y are infinities with opposite
+// signs. The value of z is undefined in that case.
+//
// BUG(gri) When rounding ToNegativeInf, the sign of Float values rounded to 0 is incorrect.
func (z *Float) Add(x, y *Float) *Float {
if debugFloat {
@@ -1413,46 +1418,59 @@ func (z *Float) Add(x, y *Float) *Float {
z.prec = umax32(x.prec, y.prec)
}
- // special cases
- if x.form != finite || y.form != finite {
- if x.form > finite || y.form > finite {
- // TODO(gri) handle Inf separately
- panic("Inf operand")
- }
- if x.form == zero {
- z.Set(y)
- if z.form == zero {
- z.neg = x.neg && y.neg // -0 + -0 == -0
+ if x.form == finite && y.form == finite {
+ // x + y (commom case)
+ z.neg = x.neg
+ if x.neg == y.neg {
+ // x + y == x + y
+ // (-x) + (-y) == -(x + y)
+ z.uadd(x, y)
+ } else {
+ // x + (-y) == x - y == -(y - x)
+ // (-x) + y == y - x == -(x - y)
+ if x.ucmp(y) > 0 {
+ z.usub(x, y)
+ } else {
+ z.neg = !z.neg
+ z.usub(y, x)
}
- return z
}
- // y == ±0
+ return z
+ }
+
+ if x.form == inf && y.form == inf && x.neg != y.neg {
+ // +Inf + -Inf
+ // -Inf + +Inf
+ // value of z is undefined but make sure it's valid
+ z.acc = Exact
+ z.form = zero
+ z.neg = false
+ panic(ErrNaN{"addition of infinities with opposite signs"})
+ }
+
+ if x.form == zero && y.form == zero {
+ // ±0 + ±0
+ z.acc = Exact
+ z.form = zero
+ z.neg = x.neg && y.neg // -0 + -0 == -0
+ return z
+ }
+
+ if x.form == inf || y.form == zero {
+ // ±Inf + y
+ // x + ±0
return z.Set(x)
}
- // x, y != 0
- z.neg = x.neg
- if x.neg == y.neg {
- // x + y == x + y
- // (-x) + (-y) == -(x + y)
- z.uadd(x, y)
- } else {
- // x + (-y) == x - y == -(y - x)
- // (-x) + y == y - x == -(x - y)
- if x.ucmp(y) > 0 {
- z.usub(x, y)
- } else {
- z.neg = !z.neg
- z.usub(y, x)
- }
- }
-
- return z
+ // ±0 + y
+ // x + ±Inf
+ return z.Set(y)
}
// Sub sets z to the rounded difference x-y and returns z.
// Precision, rounding, and accuracy reporting are as for Add.
-// BUG(gri) Float.Sub panics if an operand is Inf.
+// Sub panics with ErrNaN if x and y are infinities with equal
+// signs. The value of z is undefined in that case.
func (z *Float) Sub(x, y *Float) *Float {
if debugFloat {
x.validate()
@@ -1463,46 +1481,59 @@ func (z *Float) Sub(x, y *Float) *Float {
z.prec = umax32(x.prec, y.prec)
}
- // special cases
- if x.form != finite || y.form != finite {
- if x.form > finite || y.form > finite {
- // TODO(gri) handle Inf separately
- panic("Inf operand")
- }
- if x.form == zero {
- z.Neg(y)
- if z.form == zero {
- z.neg = x.neg && !y.neg // -0 - 0 == -0
+ if x.form == finite && y.form == finite {
+ // x - y (common case)
+ z.neg = x.neg
+ if x.neg != y.neg {
+ // x - (-y) == x + y
+ // (-x) - y == -(x + y)
+ z.uadd(x, y)
+ } else {
+ // x - y == x - y == -(y - x)
+ // (-x) - (-y) == y - x == -(x - y)
+ if x.ucmp(y) > 0 {
+ z.usub(x, y)
+ } else {
+ z.neg = !z.neg
+ z.usub(y, x)
}
- return z
}
- // y == ±0
+ return z
+ }
+
+ if x.form == inf && y.form == inf && x.neg == y.neg {
+ // +Inf - +Inf
+ // -Inf - -Inf
+ // value of z is undefined but make sure it's valid
+ z.acc = Exact
+ z.form = zero
+ z.neg = false
+ panic(ErrNaN{"subtraction of infinities with equal signs"})
+ }
+
+ if x.form == zero && y.form == zero {
+ // ±0 - ±0
+ z.acc = Exact
+ z.form = zero
+ z.neg = x.neg && !y.neg // -0 - +0 == -0
+ return z
+ }
+
+ if x.form == inf || y.form == zero {
+ // ±Inf - y
+ // x - ±0
return z.Set(x)
}
- // x, y != 0
- z.neg = x.neg
- if x.neg != y.neg {
- // x - (-y) == x + y
- // (-x) - y == -(x + y)
- z.uadd(x, y)
- } else {
- // x - y == x - y == -(y - x)
- // (-x) - (-y) == y - x == -(x - y)
- if x.ucmp(y) > 0 {
- z.usub(x, y)
- } else {
- z.neg = !z.neg
- z.usub(y, x)
- }
- }
-
- return z
+ // ±0 - y
+ // x - ±Inf
+ return z.Neg(y)
}
// Mul sets z to the rounded product x*y and returns z.
// Precision, rounding, and accuracy reporting are as for Add.
-// BUG(gri) Float.Mul panics if an operand is Inf.
+// Mul panics with ErrNaN if one operand is zero and the other
+// operand an infinity. The value of z is undefined in that case.
func (z *Float) Mul(x, y *Float) *Float {
if debugFloat {
x.validate()
@@ -1515,28 +1546,39 @@ func (z *Float) Mul(x, y *Float) *Float {
z.neg = x.neg != y.neg
- // special cases
- if x.form != finite || y.form != finite {
- if x.form > finite || y.form > finite {
- // TODO(gri) handle Inf separately
- panic("Inf operand")
- }
- // x == ±0 || y == ±0
- z.acc = Exact
- z.form = zero
+ if x.form == finite && y.form == finite {
+ // x * y (common case)
+ z.umul(x, y)
return z
}
- // x, y != 0
- z.umul(x, y)
+ z.acc = Exact
+ if x.form == zero && y.form == inf || x.form == inf && y.form == zero {
+ // ±0 * ±Inf
+ // ±Inf * ±0
+ // value of z is undefined but make sure it's valid
+ z.form = zero
+ z.neg = false
+ panic(ErrNaN{"multiplication of zero with infinity"})
+ }
+ if x.form == inf || y.form == inf {
+ // ±Inf * y
+ // x * ±Inf
+ z.form = inf
+ return z
+ }
+
+ // ±0 * y
+ // x * ±0
+ z.form = zero
return z
}
// Quo sets z to the rounded quotient x/y and returns z.
// Precision, rounding, and accuracy reporting are as for Add.
-// Quo panics is both operands are 0.
-// BUG(gri) Float.Quo panics if an operand is Inf.
+// Quo panics with ErrNaN if both operands are zero or infinities.
+// The value of z is undefined in that case.
func (z *Float) Quo(x, y *Float) *Float {
if debugFloat {
x.validate()
@@ -1549,29 +1591,32 @@ func (z *Float) Quo(x, y *Float) *Float {
z.neg = x.neg != y.neg
- // special cases
- z.acc = Exact
- if x.form != finite || y.form != finite {
- if x.form > finite || y.form > finite {
- // TODO(gri) handle Inf separately
- panic("Inf operand")
- }
- // x == ±0 || y == ±0
- if x.form == zero {
- if y.form == zero {
- panic("0/0")
- }
- z.form = zero
- return z
- }
- // y == ±0
- z.form = inf
+ if x.form == finite && y.form == finite {
+ // x / y (common case)
+ z.uquo(x, y)
return z
}
- // x, y != 0
- z.uquo(x, y)
+ z.acc = Exact
+ if x.form == zero && y.form == zero || x.form == inf && y.form == inf {
+ // ±0 / ±0
+ // ±Inf / ±Inf
+ // value of z is undefined but make sure it's valid
+ z.form = zero
+ z.neg = false
+ panic(ErrNaN{"division of zero by zero or infinity by infinity"})
+ }
+ if x.form == zero || y.form == inf {
+ // ±0 / y
+ // x / ±Inf
+ z.form = zero
+ return z
+ }
+
+ // x / ±0
+ // ±Inf / y
+ z.form = inf
return z
}
diff --git a/src/math/big/float_test.go b/src/math/big/float_test.go
index b3f1a60474..5b5a0247b1 100644
--- a/src/math/big/float_test.go
+++ b/src/math/big/float_test.go
@@ -12,6 +12,9 @@ import (
"testing"
)
+// Verify that ErrNaN implements the error interface.
+var _ error = ErrNaN{}
+
func (x *Float) uint64() uint64 {
u, acc := x.Uint64()
if acc != Exact {
@@ -1438,7 +1441,7 @@ func TestFloatQuoSmoke(t *testing.T) {
// TestFloatArithmeticSpecialValues tests that Float operations produce the
// correct results for combinations of zero (±0), finite (±1 and ±2.71828),
-// and non-finite (±Inf) operands.
+// and infinite (±Inf) operands.
func TestFloatArithmeticSpecialValues(t *testing.T) {
zero := 0.0
args := []float64{math.Inf(-1), -2.71828, -1, -zero, zero, 1, 2.71828, math.Inf(1)}
@@ -1456,38 +1459,53 @@ func TestFloatArithmeticSpecialValues(t *testing.T) {
t.Errorf("Float(%g) == %g (%s)", x, got, acc)
}
for _, y := range args {
- // At the moment an Inf operand always leads to a panic (known bug).
- // TODO(gri) remove this once the bug is fixed.
- if math.IsInf(x, 0) || math.IsInf(y, 0) {
- continue
- }
yy.SetFloat64(y)
- var op string
- var z float64
+ var (
+ op string
+ z float64
+ f func(z, x, y *Float) *Float
+ )
switch i {
case 0:
op = "+"
z = x + y
- got.Add(xx, yy)
+ f = (*Float).Add
case 1:
op = "-"
z = x - y
- got.Sub(xx, yy)
+ f = (*Float).Sub
case 2:
op = "*"
z = x * y
- got.Mul(xx, yy)
+ f = (*Float).Mul
case 3:
- if x == 0 && y == 0 {
- // TODO(gri) check for ErrNaN
- continue // 0/0 panics with ErrNaN
- }
op = "/"
z = x / y
- got.Quo(xx, yy)
+ f = (*Float).Quo
default:
panic("unreachable")
}
+ var errnan bool // set if execution of f panicked with ErrNaN
+ // protect execution of f
+ func() {
+ defer func() {
+ if p := recover(); p != nil {
+ _ = p.(ErrNaN) // re-panic if not ErrNaN
+ errnan = true
+ }
+ }()
+ f(got, xx, yy)
+ }()
+ if math.IsNaN(z) {
+ if !errnan {
+ t.Errorf("%5g %s %5g = %5s; want ErrNaN panic", x, op, y, got)
+ }
+ continue
+ }
+ if errnan {
+ t.Errorf("%5g %s %5g panicked with ErrNan; want %5s", x, op, y, want)
+ continue
+ }
want.SetFloat64(z)
if !alike(got, want) {
t.Errorf("%5g %s %5g = %5s; want %5s", x, op, y, got, want)
@@ -1614,7 +1632,7 @@ func TestFloatArithmeticRounding(t *testing.T) {
}
// TestFloatCmpSpecialValues tests that Cmp produces the correct results for
-// combinations of zero (±0), finite (±1 and ±2.71828), and non-finite (±Inf)
+// combinations of zero (±0), finite (±1 and ±2.71828), and infinite (±Inf)
// operands.
func TestFloatCmpSpecialValues(t *testing.T) {
zero := 0.0
diff --git a/src/math/big/floatconv.go b/src/math/big/floatconv.go
index 7dc9a2800c..b929d1202c 100644
--- a/src/math/big/floatconv.go
+++ b/src/math/big/floatconv.go
@@ -163,24 +163,54 @@ func (z *Float) Scan(r io.ByteScanner, base int) (f *Float, b int, err error) {
}
// exp10 != 0
- // compute decimal exponent power
- expabs := exp10
- if expabs < 0 {
- expabs = -expabs
- }
- powTen := nat(nil).expNN(natTen, nat(nil).setUint64(uint64(expabs)), nil)
- fpowTen := new(Float).SetInt(new(Int).SetBits(powTen))
-
// apply 10**exp10
+ p := new(Float).SetPrec(z.Prec() + 64) // use more bits for p -- TODO(gri) what is the right number?
if exp10 < 0 {
- z.uquo(z, fpowTen)
+ z.uquo(z, p.pow10(-exp10))
} else {
- z.umul(z, fpowTen)
+ z.umul(z, p.pow10(exp10))
}
return
}
+// These powers of 10 can be represented exactly as a float64.
+var pow10tab = [...]float64{
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+}
+
+// pow10 sets z to 10**n and returns z.
+// n must not be negative.
+func (z *Float) pow10(n int64) *Float {
+ if n < 0 {
+ panic("pow10 called with negative argument")
+ }
+
+ const m = int64(len(pow10tab) - 1)
+ if n <= m {
+ return z.SetFloat64(pow10tab[n])
+ }
+ // n > m
+
+ z.SetFloat64(pow10tab[m])
+ n -= m
+
+ // use more bits for f than for z
+ // TODO(gri) what is the right number?
+ f := new(Float).SetPrec(z.Prec() + 64).SetInt64(10)
+
+ for n > 0 {
+ if n&1 != 0 {
+ z.Mul(z, f)
+ }
+ f.Mul(f, f)
+ n >>= 1
+ }
+
+ return z
+}
+
// Parse is like z.Scan(r, base), but instead of reading from an
// io.ByteScanner, it parses the string s. An error is also returned
// if the string contains invalid or trailing bytes not belonging to
diff --git a/src/math/big/floatconv_test.go b/src/math/big/floatconv_test.go
index e7920d0c07..96c01eed81 100644
--- a/src/math/big/floatconv_test.go
+++ b/src/math/big/floatconv_test.go
@@ -330,6 +330,12 @@ func TestFloatFormat(t *testing.T) {
{"3e40", 100, 'f', 4, "30000000000000000000000000000000000000000.0000"},
{"3e40", 100, 'g', 40, "3e+40"},
+ // make sure "stupid" exponents don't stall the machine
+ {"1e1000000", 64, 'p', 0, "0x.88b3a28a05eade3ap3321929"},
+ {"1e1000000000", 64, 'p', 0, "0x.ecc5f45aa573d3p1538481529"},
+ {"1e-1000000", 64, 'p', 0, "0x.efb4542cc8ca418ap-3321928"},
+ {"1e-1000000000", 64, 'p', 0, "0x.8a64dd983a4c7dabp-1538481528"},
+
// TODO(gri) need tests for actual large Floats
{"0", 53, 'b', 0, "0"},
diff --git a/src/math/big/int.go b/src/math/big/int.go
index 3410ec4729..5e3125375b 100644
--- a/src/math/big/int.go
+++ b/src/math/big/int.go
@@ -7,7 +7,6 @@
package big
import (
- "errors"
"fmt"
"io"
"math/rand"
@@ -584,6 +583,124 @@ func (z *Int) ModInverse(g, n *Int) *Int {
return z
}
+// Jacobi returns the Jacobi symbol (x/y), either +1, -1, or 0.
+// The y argument must be an odd integer.
+func Jacobi(x, y *Int) int {
+ if len(y.abs) == 0 || y.abs[0]&1 == 0 {
+ panic(fmt.Sprintf("big: invalid 2nd argument to Int.Jacobi: need odd integer but got %s", y))
+ }
+
+ // We use the formulation described in chapter 2, section 2.4,
+ // "The Yacas Book of Algorithms":
+ // http://yacas.sourceforge.net/Algo.book.pdf
+
+ var a, b, c Int
+ a.Set(x)
+ b.Set(y)
+ j := 1
+
+ if b.neg {
+ if a.neg {
+ j = -1
+ }
+ b.neg = false
+ }
+
+ for {
+ if b.Cmp(intOne) == 0 {
+ return j
+ }
+ if len(a.abs) == 0 {
+ return 0
+ }
+ a.Mod(&a, &b)
+ if len(a.abs) == 0 {
+ return 0
+ }
+ // a > 0
+
+ // handle factors of 2 in 'a'
+ s := a.abs.trailingZeroBits()
+ if s&1 != 0 {
+ bmod8 := b.abs[0] & 7
+ if bmod8 == 3 || bmod8 == 5 {
+ j = -j
+ }
+ }
+ c.Rsh(&a, s) // a = 2^s*c
+
+ // swap numerator and denominator
+ if b.abs[0]&3 == 3 && c.abs[0]&3 == 3 {
+ j = -j
+ }
+ a.Set(&b)
+ b.Set(&c)
+ }
+}
+
+// ModSqrt sets z to a square root of x mod p if such a square root exists, and
+// returns z. The modulus p must be an odd prime. If x is not a square mod p,
+// ModSqrt leaves z unchanged and returns nil. This function panics if p is
+// not an odd integer.
+func (z *Int) ModSqrt(x, p *Int) *Int {
+ switch Jacobi(x, p) {
+ case -1:
+ return nil // x is not a square mod p
+ case 0:
+ return z.SetInt64(0) // sqrt(0) mod p = 0
+ case 1:
+ break
+ }
+ if x.neg || x.Cmp(p) >= 0 { // ensure 0 <= x < p
+ x = new(Int).Mod(x, p)
+ }
+
+ // Break p-1 into s*2^e such that s is odd.
+ var s Int
+ s.Sub(p, intOne)
+ e := s.abs.trailingZeroBits()
+ s.Rsh(&s, e)
+
+ // find some non-square n
+ var n Int
+ n.SetInt64(2)
+ for Jacobi(&n, p) != -1 {
+ n.Add(&n, intOne)
+ }
+
+ // Core of the Tonelli-Shanks algorithm. Follows the description in
+ // section 6 of "Square roots from 1; 24, 51, 10 to Dan Shanks" by Ezra
+ // Brown:
+ // https://www.maa.org/sites/default/files/pdf/upload_library/22/Polya/07468342.di020786.02p0470a.pdf
+ var y, b, g, t Int
+ y.Add(&s, intOne)
+ y.Rsh(&y, 1)
+ y.Exp(x, &y, p) // y = x^((s+1)/2)
+ b.Exp(x, &s, p) // b = x^s
+ g.Exp(&n, &s, p) // g = n^s
+ r := e
+ for {
+ // find the least m such that ord_p(b) = 2^m
+ var m uint
+ t.Set(&b)
+ for t.Cmp(intOne) != 0 {
+ t.Mul(&t, &t).Mod(&t, p)
+ m++
+ }
+
+ if m == 0 {
+ return z.Set(&y)
+ }
+
+ t.SetInt64(0).SetBit(&t, int(r-m-1), 1).Exp(&g, &t, p)
+ // t = g^(2^(r-m-1)) mod p
+ g.Mul(&t, &t).Mod(&g, p) // g = g^(2^(r-m)) mod p
+ y.Mul(&y, &t).Mod(&y, p)
+ b.Mul(&b, &g).Mod(&b, p)
+ r = m
+ }
+}
+
// Lsh sets z = x << n and returns z.
func (z *Int) Lsh(x *Int, n uint) *Int {
z.abs = z.abs.shl(x.abs, n)
@@ -813,7 +930,7 @@ func (z *Int) GobDecode(buf []byte) error {
}
b := buf[0]
if b>>1 != intGobVersion {
- return errors.New(fmt.Sprintf("Int.GobDecode: encoding version %d not supported", b>>1))
+ return fmt.Errorf("Int.GobDecode: encoding version %d not supported", b>>1)
}
z.neg = b&1 != 0
z.abs = z.abs.setBytes(buf[1:])
diff --git a/src/math/big/int_test.go b/src/math/big/int_test.go
index a972a7249b..c19e88addb 100644
--- a/src/math/big/int_test.go
+++ b/src/math/big/int_test.go
@@ -525,6 +525,7 @@ var expTests = []struct {
{"1234", "-1", "1", "0"},
// misc
+ {"5", "1", "3", "2"},
{"5", "-7", "", "1"},
{"-5", "-7", "", "1"},
{"5", "0", "", "1"},
@@ -703,6 +704,13 @@ var primes = []string{
"230975859993204150666423538988557839555560243929065415434980904258310530753006723857139742334640122533598517597674807096648905501653461687601339782814316124971547968912893214002992086353183070342498989426570593",
"5521712099665906221540423207019333379125265462121169655563495403888449493493629943498064604536961775110765377745550377067893607246020694972959780839151452457728855382113555867743022746090187341871655890805971735385789993",
"203956878356401977405765866929034577280193993314348263094772646453283062722701277632936616063144088173312372882677123879538709400158306567338328279154499698366071906766440037074217117805690872792848149112022286332144876183376326512083574821647933992961249917319836219304274280243803104015000563790123",
+
+ // ECC primes: http://tools.ietf.org/html/draft-ladd-safecurves-02
+ "3618502788666131106986593281521497120414687020801267626233049500247285301239", // Curve1174: 2^251-9
+ "57896044618658097711785492504343953926634992332820282019728792003956564819949", // Curve25519: 2^255-19
+ "9850501549098619803069760025035903451269934817616361666987073351061430442874302652853566563721228910201656997576599", // E-382: 2^382-105
+ "42307582002575910332922579714097346549017899709713998034217522897561970639123926132812109468141778230245837569601494931472367", // Curve41417: 2^414-17
+ "6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151", // E-521: 2^521-1
}
var composites = []string{
@@ -1248,6 +1256,136 @@ func TestModInverse(t *testing.T) {
}
}
+// testModSqrt is a helper for TestModSqrt,
+// which checks that ModSqrt can compute a square-root of elt^2.
+func testModSqrt(t *testing.T, elt, mod, sq, sqrt *Int) bool {
+ var sqChk, sqrtChk, sqrtsq Int
+ sq.Mul(elt, elt)
+ sq.Mod(sq, mod)
+ z := sqrt.ModSqrt(sq, mod)
+ if z != sqrt {
+ t.Errorf("ModSqrt returned wrong value %s", z)
+ }
+
+ // test ModSqrt arguments outside the range [0,mod)
+ sqChk.Add(sq, mod)
+ z = sqrtChk.ModSqrt(&sqChk, mod)
+ if z != &sqrtChk || z.Cmp(sqrt) != 0 {
+ t.Errorf("ModSqrt returned inconsistent value %s", z)
+ }
+ sqChk.Sub(sq, mod)
+ z = sqrtChk.ModSqrt(&sqChk, mod)
+ if z != &sqrtChk || z.Cmp(sqrt) != 0 {
+ t.Errorf("ModSqrt returned inconsistent value %s", z)
+ }
+
+ // make sure we actually got a square root
+ if sqrt.Cmp(elt) == 0 {
+ return true // we found the "desired" square root
+ }
+ sqrtsq.Mul(sqrt, sqrt) // make sure we found the "other" one
+ sqrtsq.Mod(&sqrtsq, mod)
+ return sq.Cmp(&sqrtsq) == 0
+}
+
+func TestModSqrt(t *testing.T) {
+ var elt, mod, modx4, sq, sqrt Int
+ r := rand.New(rand.NewSource(9))
+ for i, s := range primes[1:] { // skip 2, use only odd primes
+ mod.SetString(s, 10)
+ modx4.Lsh(&mod, 2)
+
+ // test a few random elements per prime
+ for x := 1; x < 5; x++ {
+ elt.Rand(r, &modx4)
+ elt.Sub(&elt, &mod) // test range [-mod, 3*mod)
+ if !testModSqrt(t, &elt, &mod, &sq, &sqrt) {
+ t.Errorf("#%d: failed (sqrt(e) = %s)", i, &sqrt)
+ }
+ }
+ }
+
+ // exhaustive test for small values
+ for n := 3; n < 100; n++ {
+ mod.SetInt64(int64(n))
+ if !mod.ProbablyPrime(10) {
+ continue
+ }
+ isSquare := make([]bool, n)
+
+ // test all the squares
+ for x := 1; x < n; x++ {
+ elt.SetInt64(int64(x))
+ if !testModSqrt(t, &elt, &mod, &sq, &sqrt) {
+ t.Errorf("#%d: failed (sqrt(%d,%d) = %s)", x, &elt, &mod, &sqrt)
+ }
+ isSquare[sq.Uint64()] = true
+ }
+
+ // test all non-squares
+ for x := 1; x < n; x++ {
+ sq.SetInt64(int64(x))
+ z := sqrt.ModSqrt(&sq, &mod)
+ if !isSquare[x] && z != nil {
+ t.Errorf("#%d: failed (sqrt(%d,%d) = nil)", x, &sqrt, &mod)
+ }
+ }
+ }
+}
+
+func TestJacobi(t *testing.T) {
+ testCases := []struct {
+ x, y int64
+ result int
+ }{
+ {0, 1, 1},
+ {0, -1, 1},
+ {1, 1, 1},
+ {1, -1, 1},
+ {0, 5, 0},
+ {1, 5, 1},
+ {2, 5, -1},
+ {-2, 5, -1},
+ {2, -5, -1},
+ {-2, -5, 1},
+ {3, 5, -1},
+ {5, 5, 0},
+ {-5, 5, 0},
+ {6, 5, 1},
+ {6, -5, 1},
+ {-6, 5, 1},
+ {-6, -5, -1},
+ }
+
+ var x, y Int
+
+ for i, test := range testCases {
+ x.SetInt64(test.x)
+ y.SetInt64(test.y)
+ expected := test.result
+ actual := Jacobi(&x, &y)
+ if actual != expected {
+ t.Errorf("#%d: Jacobi(%d, %d) = %d, but expected %d", i, test.x, test.y, actual, expected)
+ }
+ }
+}
+
+func TestJacobiPanic(t *testing.T) {
+ const failureMsg = "test failure"
+ defer func() {
+ msg := recover()
+ if msg == nil || msg == failureMsg {
+ panic(msg)
+ }
+ t.Log(msg)
+ }()
+ x := NewInt(1)
+ y := NewInt(2)
+ // Jacobi should panic when the second argument is even.
+ Jacobi(x, y)
+ panic(failureMsg)
+}
+
var encodingTests = []string{
"-539345864568634858364538753846587364875430589374589",
"-678645873",
diff --git a/src/math/big/nat.go b/src/math/big/nat.go
index 2a279d186c..7157a5487b 100644
--- a/src/math/big/nat.go
+++ b/src/math/big/nat.go
@@ -888,6 +888,13 @@ func (z nat) expNN(x, y, m nat) nat {
}
// y > 0
+ // x**1 mod m == x mod m
+ if len(y) == 1 && y[0] == 1 && len(m) != 0 {
+ _, z = z.div(z, x, m)
+ return z
+ }
+ // y > 1
+
if len(m) != 0 {
// We likely end up being as long as the modulus.
z = z.make(len(m))
diff --git a/src/math/big/rat.go b/src/math/big/rat.go
index 748796c8ca..fb16f18a96 100644
--- a/src/math/big/rat.go
+++ b/src/math/big/rat.go
@@ -546,7 +546,7 @@ func (z *Rat) GobDecode(buf []byte) error {
}
b := buf[0]
if b>>1 != ratGobVersion {
- return errors.New(fmt.Sprintf("Rat.GobDecode: encoding version %d not supported", b>>1))
+ return fmt.Errorf("Rat.GobDecode: encoding version %d not supported", b>>1)
}
const j = 1 + 4
i := j + binary.BigEndian.Uint32(buf[j-4:j])
diff --git a/src/math/sqrt.go b/src/math/sqrt.go
index fdc869992e..23cf2996c2 100644
--- a/src/math/sqrt.go
+++ b/src/math/sqrt.go
@@ -91,6 +91,11 @@ package math
// Sqrt(NaN) = NaN
func Sqrt(x float64) float64
+// Note: Sqrt is implemented in assembly on some systems.
+// Others have assembly stubs that jump to func sqrt below.
+// On systems where Sqrt is a single instruction, the compiler
+// may turn a direct call into a direct use of that instruction instead.
+
func sqrt(x float64) float64 {
// special cases
switch {
diff --git a/src/cmd/5g/util.go b/src/math/sqrt_arm64.s
similarity index 53%
rename from src/cmd/5g/util.go
rename to src/math/sqrt_arm64.s
index bb5eedb15a..9861446eb9 100644
--- a/src/cmd/5g/util.go
+++ b/src/math/sqrt_arm64.s
@@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+#include "textflag.h"
-func bool2int(b bool) int {
- if b {
- return 1
- }
- return 0
-}
+// func Sqrt(x float64) float64
+TEXT ·Sqrt(SB),NOSPLIT,$0
+ FMOVD x+0(FP), F0
+ FSQRTD F0, F0
+ FMOVD F0, ret+8(FP)
+ RET
diff --git a/src/math/stubs_arm64.s b/src/math/stubs_arm64.s
index 2ffd2289b8..eea81e9241 100644
--- a/src/math/stubs_arm64.s
+++ b/src/math/stubs_arm64.s
@@ -84,8 +84,5 @@ TEXT ·Sin(SB),NOSPLIT,$0
TEXT ·Cos(SB),NOSPLIT,$0
B ·cos(SB)
-TEXT ·Sqrt(SB),NOSPLIT,$0
- B ·sqrt(SB)
-
TEXT ·Tan(SB),NOSPLIT,$0
B ·tan(SB)
diff --git a/src/mime/quotedprintable/reader.go b/src/mime/quotedprintable/reader.go
index a39a20ef83..3bd6833da5 100644
--- a/src/mime/quotedprintable/reader.go
+++ b/src/mime/quotedprintable/reader.go
@@ -13,19 +13,16 @@ import (
"io"
)
-// Deviations from RFC 2045:
-// 1. in addition to "=\r\n", "=\n" is also treated as soft line break.
-// 2. it will pass through a '\r' or '\n' not preceded by '=', consistent
-// with other broken QP encoders & decoders.
-type reader struct {
+// Reader is a quoted-printable decoder.
+type Reader struct {
br *bufio.Reader
rerr error // last read error
line []byte // to be consumed before more of br
}
// NewReader returns a quoted-printable reader, decoding from r.
-func NewReader(r io.Reader) io.Reader {
- return &reader{
+func NewReader(r io.Reader) *Reader {
+ return &Reader{
br: bufio.NewReader(r),
}
}
@@ -43,7 +40,7 @@ func fromHex(b byte) (byte, error) {
return 0, fmt.Errorf("quotedprintable: invalid hex byte 0x%02x", b)
}
-func (q *reader) readHexByte(v []byte) (b byte, err error) {
+func readHexByte(v []byte) (b byte, err error) {
if len(v) < 2 {
return 0, io.ErrUnexpectedEOF
}
@@ -71,43 +68,48 @@ var (
softSuffix = []byte("=")
)
-func (q *reader) Read(p []byte) (n int, err error) {
+// Read reads and decodes quoted-printable data from the underlying reader.
+func (r *Reader) Read(p []byte) (n int, err error) {
+ // Deviations from RFC 2045:
+ // 1. in addition to "=\r\n", "=\n" is also treated as soft line break.
+ // 2. it will pass through a '\r' or '\n' not preceded by '=', consistent
+ // with other broken QP encoders & decoders.
for len(p) > 0 {
- if len(q.line) == 0 {
- if q.rerr != nil {
- return n, q.rerr
+ if len(r.line) == 0 {
+ if r.rerr != nil {
+ return n, r.rerr
}
- q.line, q.rerr = q.br.ReadSlice('\n')
+ r.line, r.rerr = r.br.ReadSlice('\n')
// Does the line end in CRLF instead of just LF?
- hasLF := bytes.HasSuffix(q.line, lf)
- hasCR := bytes.HasSuffix(q.line, crlf)
- wholeLine := q.line
- q.line = bytes.TrimRightFunc(wholeLine, isQPDiscardWhitespace)
- if bytes.HasSuffix(q.line, softSuffix) {
- rightStripped := wholeLine[len(q.line):]
- q.line = q.line[:len(q.line)-1]
+ hasLF := bytes.HasSuffix(r.line, lf)
+ hasCR := bytes.HasSuffix(r.line, crlf)
+ wholeLine := r.line
+ r.line = bytes.TrimRightFunc(wholeLine, isQPDiscardWhitespace)
+ if bytes.HasSuffix(r.line, softSuffix) {
+ rightStripped := wholeLine[len(r.line):]
+ r.line = r.line[:len(r.line)-1]
if !bytes.HasPrefix(rightStripped, lf) && !bytes.HasPrefix(rightStripped, crlf) {
- q.rerr = fmt.Errorf("quotedprintable: invalid bytes after =: %q", rightStripped)
+ r.rerr = fmt.Errorf("quotedprintable: invalid bytes after =: %q", rightStripped)
}
} else if hasLF {
if hasCR {
- q.line = append(q.line, '\r', '\n')
+ r.line = append(r.line, '\r', '\n')
} else {
- q.line = append(q.line, '\n')
+ r.line = append(r.line, '\n')
}
}
continue
}
- b := q.line[0]
+ b := r.line[0]
switch {
case b == '=':
- b, err = q.readHexByte(q.line[1:])
+ b, err = readHexByte(r.line[1:])
if err != nil {
return n, err
}
- q.line = q.line[2:] // 2 of the 3; other 1 is done below
+ r.line = r.line[2:] // 2 of the 3; other 1 is done below
case b == '\t' || b == '\r' || b == '\n':
break
case b < ' ' || b > '~':
@@ -115,7 +117,7 @@ func (q *reader) Read(p []byte) (n int, err error) {
}
p[0] = b
p = p[1:]
- q.line = q.line[1:]
+ r.line = r.line[1:]
n++
}
return n, nil
diff --git a/src/mime/type_windows.go b/src/mime/type_windows.go
index 60362b4b37..97b9aeba7a 100644
--- a/src/mime/type_windows.go
+++ b/src/mime/type_windows.go
@@ -5,8 +5,7 @@
package mime
import (
- "syscall"
- "unsafe"
+ "internal/syscall/windows/registry"
)
func init() {
@@ -14,49 +13,24 @@ func init() {
}
func initMimeWindows() {
- var root syscall.Handle
- rootpathp, _ := syscall.UTF16PtrFromString(`\`)
- if syscall.RegOpenKeyEx(syscall.HKEY_CLASSES_ROOT, rootpathp,
- 0, syscall.KEY_READ, &root) != nil {
+ names, err := registry.CLASSES_ROOT.ReadSubKeyNames(-1)
+ if err != nil {
return
}
- defer syscall.RegCloseKey(root)
- var count uint32
- if syscall.RegQueryInfoKey(root, nil, nil, nil, &count, nil, nil, nil, nil, nil, nil, nil) != nil {
- return
- }
- contenttypep, _ := syscall.UTF16PtrFromString("Content Type")
- var buf [1 << 10]uint16
- for i := uint32(0); i < count; i++ {
- n := uint32(len(buf))
- if syscall.RegEnumKeyEx(root, i, &buf[0], &n, nil, nil, nil, nil) != nil {
+ for _, name := range names {
+ if len(name) < 2 || name[0] != '.' { // looking for extensions only
continue
}
- ext := syscall.UTF16ToString(buf[:])
- if len(ext) < 2 || ext[0] != '.' { // looking for extensions only
+ k, err := registry.OpenKey(registry.CLASSES_ROOT, name, registry.READ)
+ if err != nil {
continue
}
- var h syscall.Handle
- extpathp, _ := syscall.UTF16PtrFromString(`\` + ext)
- if syscall.RegOpenKeyEx(
- syscall.HKEY_CLASSES_ROOT, extpathp,
- 0, syscall.KEY_READ, &h) != nil {
+ v, _, err := k.GetStringValue("Content Type")
+ k.Close()
+ if err != nil {
continue
}
- var typ uint32
- n = uint32(len(buf) * 2) // api expects array of bytes, not uint16
- if syscall.RegQueryValueEx(
- h, contenttypep,
- nil, &typ, (*byte)(unsafe.Pointer(&buf[0])), &n) != nil {
- syscall.RegCloseKey(h)
- continue
- }
- syscall.RegCloseKey(h)
- if typ != syscall.REG_SZ { // null terminated strings only
- continue
- }
- mimeType := syscall.UTF16ToString(buf[:])
- setExtensionType(ext, mimeType)
+ setExtensionType(name, v)
}
}
diff --git a/src/nacltest.bash b/src/nacltest.bash
index eb1ac3d908..049aad2ff2 100755
--- a/src/nacltest.bash
+++ b/src/nacltest.bash
@@ -78,3 +78,5 @@ GOROOT=$GOROOT_BOOTSTRAP $gobin/go run ../misc/nacl/mkzip.go -p syscall -r .. ..
# Run standard build and tests.
export PATH=$(pwd)/../misc/nacl:$PATH
GOOS=nacl GOARCH=$naclGOARCH ./all.bash
+
+rm -f syscall/fstest_nacl.go
diff --git a/src/net/cgo_android.go b/src/net/cgo_android.go
index 3819ce56a4..fe9925b840 100644
--- a/src/net/cgo_android.go
+++ b/src/net/cgo_android.go
@@ -9,6 +9,4 @@ package net
//#include
import "C"
-func cgoAddrInfoFlags() C.int {
- return C.AI_CANONNAME
-}
+const cgoAddrInfoFlags = C.AI_CANONNAME
diff --git a/src/net/cgo_bsd.go b/src/net/cgo_bsd.go
index 388eab4fe1..c5ec9dd9c8 100644
--- a/src/net/cgo_bsd.go
+++ b/src/net/cgo_bsd.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 !netgo
+// +build cgo,!netgo
// +build darwin dragonfly freebsd
package net
@@ -12,6 +12,4 @@ package net
*/
import "C"
-func cgoAddrInfoFlags() C.int {
- return (C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL) & C.AI_MASK
-}
+const cgoAddrInfoFlags = (C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL) & C.AI_MASK
diff --git a/src/net/cgo_linux.go b/src/net/cgo_linux.go
index 4ef2d0cd12..9a5f898c26 100644
--- a/src/net/cgo_linux.go
+++ b/src/net/cgo_linux.go
@@ -11,12 +11,10 @@ package net
*/
import "C"
-func cgoAddrInfoFlags() C.int {
- // NOTE(rsc): In theory there are approximately balanced
- // arguments for and against including AI_ADDRCONFIG
- // in the flags (it includes IPv4 results only on IPv4 systems,
- // and similarly for IPv6), but in practice setting it causes
- // getaddrinfo to return the wrong canonical name on Linux.
- // So definitely leave it out.
- return C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL
-}
+// NOTE(rsc): In theory there are approximately balanced
+// arguments for and against including AI_ADDRCONFIG
+// in the flags (it includes IPv4 results only on IPv4 systems,
+// and similarly for IPv6), but in practice setting it causes
+// getaddrinfo to return the wrong canonical name on Linux.
+// So definitely leave it out.
+const cgoAddrInfoFlags = C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL
diff --git a/src/net/cgo_netbsd.go b/src/net/cgo_netbsd.go
index 09c5ad2d9f..183091366c 100644
--- a/src/net/cgo_netbsd.go
+++ b/src/net/cgo_netbsd.go
@@ -11,6 +11,4 @@ package net
*/
import "C"
-func cgoAddrInfoFlags() C.int {
- return C.AI_CANONNAME
-}
+const cgoAddrInfoFlags = C.AI_CANONNAME
diff --git a/src/net/cgo_openbsd.go b/src/net/cgo_openbsd.go
index 09c5ad2d9f..183091366c 100644
--- a/src/net/cgo_openbsd.go
+++ b/src/net/cgo_openbsd.go
@@ -11,6 +11,4 @@ package net
*/
import "C"
-func cgoAddrInfoFlags() C.int {
- return C.AI_CANONNAME
-}
+const cgoAddrInfoFlags = C.AI_CANONNAME
diff --git a/src/net/cgo_solaris.go b/src/net/cgo_solaris.go
new file mode 100644
index 0000000000..2d452b9e17
--- /dev/null
+++ b/src/net/cgo_solaris.go
@@ -0,0 +1,15 @@
+// Copyright 2015 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 cgo,!netgo
+
+package net
+
+/*
+#cgo LDFLAGS: -lsocket -lnsl
+#include
+*/
+import "C"
+
+const cgoAddrInfoFlags = C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL
diff --git a/src/net/cgo_stub.go b/src/net/cgo_stub.go
index d2d40da74f..c4937efde2 100644
--- a/src/net/cgo_stub.go
+++ b/src/net/cgo_stub.go
@@ -4,10 +4,14 @@
// +build !cgo netgo
-// Stub cgo routines for systems that do not use cgo to do network lookups.
-
package net
+type addrinfoErrno int
+
+func (eai addrinfoErrno) Error() string { return "" }
+func (eai addrinfoErrno) Temporary() bool { return false }
+func (eai addrinfoErrno) Timeout() bool { return false }
+
func cgoLookupHost(name string) (addrs []string, err error, completed bool) {
return nil, nil, false
}
diff --git a/src/net/cgo_unix.go b/src/net/cgo_unix.go
index eba5777347..34588a3baa 100644
--- a/src/net/cgo_unix.go
+++ b/src/net/cgo_unix.go
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build !netgo
-// +build darwin dragonfly freebsd linux netbsd openbsd
+// +build cgo,!netgo
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package net
@@ -23,24 +23,30 @@ import (
"unsafe"
)
-func cgoLookupHost(name string) (addrs []string, err error, completed bool) {
- ip, err, completed := cgoLookupIP(name)
- for _, p := range ip {
- addrs = append(addrs, p.String())
+// An addrinfoErrno represents a getaddrinfo, getnameinfo-specific
+// error number. It's a signed number and a zero value is a non-error
+// by convention.
+type addrinfoErrno int
+
+func (eai addrinfoErrno) Error() string { return C.GoString(C.gai_strerror(C.int(eai))) }
+func (eai addrinfoErrno) Temporary() bool { return eai == C.EAI_AGAIN }
+func (eai addrinfoErrno) Timeout() bool { return false }
+
+func cgoLookupHost(name string) (hosts []string, err error, completed bool) {
+ addrs, err, completed := cgoLookupIP(name)
+ for _, addr := range addrs {
+ hosts = append(hosts, addr.String())
}
return
}
-func cgoLookupPort(net, service string) (port int, err error, completed bool) {
+func cgoLookupPort(network, service string) (port int, err error, completed bool) {
acquireThread()
defer releaseThread()
- var res *C.struct_addrinfo
var hints C.struct_addrinfo
-
- switch net {
- case "":
- // no hints
+ switch network {
+ case "": // no hints
case "tcp", "tcp4", "tcp6":
hints.ai_socktype = C.SOCK_STREAM
hints.ai_protocol = C.IPPROTO_TCP
@@ -48,10 +54,10 @@ func cgoLookupPort(net, service string) (port int, err error, completed bool) {
hints.ai_socktype = C.SOCK_DGRAM
hints.ai_protocol = C.IPPROTO_UDP
default:
- return 0, UnknownNetworkError(net), true
+ return 0, &DNSError{Err: "unknown network", Name: network + "/" + service}, true
}
- if len(net) >= 4 {
- switch net[3] {
+ if len(network) >= 4 {
+ switch network[3] {
case '4':
hints.ai_family = C.AF_INET
case '6':
@@ -60,45 +66,52 @@ func cgoLookupPort(net, service string) (port int, err error, completed bool) {
}
s := C.CString(service)
+ var res *C.struct_addrinfo
defer C.free(unsafe.Pointer(s))
- if C.getaddrinfo(nil, s, &hints, &res) == 0 {
- defer C.freeaddrinfo(res)
- for r := res; r != nil; r = r.ai_next {
- switch r.ai_family {
- default:
- continue
- case C.AF_INET:
- sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr))
- p := (*[2]byte)(unsafe.Pointer(&sa.Port))
- return int(p[0])<<8 | int(p[1]), nil, true
- case C.AF_INET6:
- sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.ai_addr))
- p := (*[2]byte)(unsafe.Pointer(&sa.Port))
- return int(p[0])<<8 | int(p[1]), nil, true
+ gerrno, err := C.getaddrinfo(nil, s, &hints, &res)
+ if gerrno != 0 {
+ switch gerrno {
+ case C.EAI_SYSTEM:
+ if err == nil { // see golang.org/issue/6232
+ err = syscall.EMFILE
}
+ default:
+ err = addrinfoErrno(gerrno)
+ }
+ return 0, &DNSError{Err: err.Error(), Name: network + "/" + service}, true
+ }
+ defer C.freeaddrinfo(res)
+
+ for r := res; r != nil; r = r.ai_next {
+ switch r.ai_family {
+ case C.AF_INET:
+ sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr))
+ p := (*[2]byte)(unsafe.Pointer(&sa.Port))
+ return int(p[0])<<8 | int(p[1]), nil, true
+ case C.AF_INET6:
+ sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.ai_addr))
+ p := (*[2]byte)(unsafe.Pointer(&sa.Port))
+ return int(p[0])<<8 | int(p[1]), nil, true
}
}
- return 0, &AddrError{"unknown port", net + "/" + service}, true
+ return 0, &DNSError{Err: "unknown port", Name: network + "/" + service}, true
}
func cgoLookupIPCNAME(name string) (addrs []IPAddr, cname string, err error, completed bool) {
acquireThread()
defer releaseThread()
- var res *C.struct_addrinfo
var hints C.struct_addrinfo
-
- hints.ai_flags = cgoAddrInfoFlags()
+ hints.ai_flags = cgoAddrInfoFlags
hints.ai_socktype = C.SOCK_STREAM
h := C.CString(name)
defer C.free(unsafe.Pointer(h))
+ var res *C.struct_addrinfo
gerrno, err := C.getaddrinfo(h, nil, &hints, &res)
if gerrno != 0 {
- var str string
- if gerrno == C.EAI_NONAME {
- str = noSuchHost
- } else if gerrno == C.EAI_SYSTEM {
+ switch gerrno {
+ case C.EAI_SYSTEM:
if err == nil {
// err should not be nil, but sometimes getaddrinfo returns
// gerrno == C.EAI_SYSTEM with err == nil on Linux.
@@ -109,13 +122,15 @@ func cgoLookupIPCNAME(name string) (addrs []IPAddr, cname string, err error, com
// comes up again. golang.org/issue/6232.
err = syscall.EMFILE
}
- str = err.Error()
- } else {
- str = C.GoString(C.gai_strerror(gerrno))
+ case C.EAI_NONAME:
+ err = errNoSuchHost
+ default:
+ err = addrinfoErrno(gerrno)
}
- return nil, "", &DNSError{Err: str, Name: name}, true
+ return nil, "", &DNSError{Err: err.Error(), Name: name}, true
}
defer C.freeaddrinfo(res)
+
if res != nil {
cname = C.GoString(res.ai_canonname)
if cname == "" {
@@ -131,8 +146,6 @@ func cgoLookupIPCNAME(name string) (addrs []IPAddr, cname string, err error, com
continue
}
switch r.ai_family {
- default:
- continue
case C.AF_INET:
sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr))
addr := IPAddr{IP: copyIP(sa.Addr[:])}
diff --git a/src/net/cgo_unix_test.go b/src/net/cgo_unix_test.go
index 33566ce9c2..55ea86a458 100644
--- a/src/net/cgo_unix_test.go
+++ b/src/net/cgo_unix_test.go
@@ -16,9 +16,9 @@ func TestCgoLookupIP(t *testing.T) {
t.Errorf("cgoLookupIP must not be a placeholder")
}
if err != nil {
- t.Errorf("cgoLookupIP failed: %v", err)
+ t.Error(err)
}
if _, err := goLookupIP(host); err != nil {
- t.Errorf("goLookupIP failed: %v", err)
+ t.Error(err)
}
}
diff --git a/src/net/cgo_windows.go b/src/net/cgo_windows.go
new file mode 100644
index 0000000000..8968b757a9
--- /dev/null
+++ b/src/net/cgo_windows.go
@@ -0,0 +1,13 @@
+// Copyright 2015 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 cgo,!netgo
+
+package net
+
+type addrinfoErrno int
+
+func (eai addrinfoErrno) Error() string { return "" }
+func (eai addrinfoErrno) Temporary() bool { return false }
+func (eai addrinfoErrno) Timeout() bool { return false }
diff --git a/src/net/conf.go b/src/net/conf.go
new file mode 100644
index 0000000000..ca7fa8708f
--- /dev/null
+++ b/src/net/conf.go
@@ -0,0 +1,234 @@
+// Copyright 2015 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 darwin dragonfly freebsd linux netbsd openbsd solaris
+
+package net
+
+import (
+ "os"
+ "runtime"
+ "sync"
+ "syscall"
+)
+
+// conf represents a system's network configuration.
+type conf struct {
+ // forceCgoLookupHost forces CGO to always be used, if available.
+ forceCgoLookupHost bool
+
+ // machine has an /etc/mdns.allow file
+ hasMDNSAllow bool
+
+ goos string // the runtime.GOOS, to ease testing
+
+ nss *nssConf
+ resolv *dnsConfig
+}
+
+var (
+ confOnce sync.Once // guards init of confVal via initConfVal
+ confVal = &conf{goos: runtime.GOOS}
+)
+
+// systemConf returns the machine's network configuration.
+func systemConf() *conf {
+ confOnce.Do(initConfVal)
+ return confVal
+}
+
+func initConfVal() {
+ // Darwin pops up annoying dialog boxes if programs try to do
+ // their own DNS requests. So always use cgo instead, which
+ // avoids that.
+ if runtime.GOOS == "darwin" {
+ confVal.forceCgoLookupHost = true
+ return
+ }
+
+ // If any environment-specified resolver options are specified,
+ // force cgo. Note that LOCALDOMAIN can change behavior merely
+ // by being specified with the empty string.
+ _, localDomainDefined := syscall.Getenv("LOCALDOMAIN")
+ if os.Getenv("RES_OPTIONS") != "" || os.Getenv("HOSTALIASES") != "" ||
+ localDomainDefined {
+ confVal.forceCgoLookupHost = true
+ return
+ }
+
+ // OpenBSD apparently lets you override the location of resolv.conf
+ // with ASR_CONFIG. If we notice that, defer to libc.
+ if runtime.GOOS == "openbsd" && os.Getenv("ASR_CONFIG") != "" {
+ confVal.forceCgoLookupHost = true
+ return
+ }
+
+ if runtime.GOOS != "openbsd" {
+ confVal.nss = parseNSSConfFile("/etc/nsswitch.conf")
+ }
+
+ confVal.resolv = dnsReadConfig("/etc/resolv.conf")
+ if confVal.resolv.err != nil && !os.IsNotExist(confVal.resolv.err) &&
+ !os.IsPermission(confVal.resolv.err) {
+ // If we can't read the resolv.conf file, assume it
+ // had something important in it and defer to cgo.
+ // libc's resolver might then fail too, but at least
+ // it wasn't our fault.
+ confVal.forceCgoLookupHost = true
+ }
+
+ if _, err := os.Stat("/etc/mdns.allow"); err == nil {
+ confVal.hasMDNSAllow = true
+ }
+}
+
+// hostLookupOrder determines which strategy to use to resolve hostname.
+func (c *conf) hostLookupOrder(hostname string) hostLookupOrder {
+ if c.forceCgoLookupHost || c.resolv.unknownOpt || c.goos == "android" {
+ return hostLookupCgo
+ }
+ if byteIndex(hostname, '\\') != -1 || byteIndex(hostname, '%') != -1 {
+ // Don't deal with special form hostnames with backslashes
+ // or '%'.
+ return hostLookupCgo
+ }
+
+ // OpenBSD is unique and doesn't use nsswitch.conf.
+ // It also doesn't support mDNS.
+ if c.goos == "openbsd" {
+ // OpenBSD's resolv.conf manpage says that a non-existent
+ // resolv.conf means "lookup" defaults to only "files",
+ // without DNS lookups.
+ if os.IsNotExist(c.resolv.err) {
+ return hostLookupFiles
+ }
+ lookup := c.resolv.lookup
+ if len(lookup) == 0 {
+ // http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man5/resolv.conf.5
+ // "If the lookup keyword is not used in the
+ // system's resolv.conf file then the assumed
+ // order is 'bind file'"
+ return hostLookupDNSFiles
+ }
+ if len(lookup) < 1 || len(lookup) > 2 {
+ return hostLookupCgo
+ }
+ switch lookup[0] {
+ case "bind":
+ if len(lookup) == 2 {
+ if lookup[1] == "file" {
+ return hostLookupDNSFiles
+ }
+ return hostLookupCgo
+ }
+ return hostLookupDNS
+ case "file":
+ if len(lookup) == 2 {
+ if lookup[1] == "bind" {
+ return hostLookupFilesDNS
+ }
+ return hostLookupCgo
+ }
+ return hostLookupFiles
+ default:
+ return hostLookupCgo
+ }
+ }
+
+ hasDot := byteIndex(hostname, '.') != -1
+
+ // Canonicalize the hostname by removing any trailing dot.
+ if stringsHasSuffix(hostname, ".") {
+ hostname = hostname[:len(hostname)-1]
+ }
+ if stringsHasSuffixFold(hostname, ".local") {
+ // Per RFC 6762, the ".local" TLD is special. And
+ // because Go's native resolver doesn't do mDNS or
+ // similar local resolution mechanisms, assume that
+ // libc might (via Avahi, etc) and use cgo.
+ return hostLookupCgo
+ }
+
+ nss := c.nss
+ srcs := nss.sources["hosts"]
+ // If /etc/nsswitch.conf doesn't exist or doesn't specify any
+ // sources for "hosts", assume Go's DNS will work fine.
+ if os.IsNotExist(nss.err) || (nss.err == nil && len(srcs) == 0) {
+ if c.goos == "solaris" {
+ // illumos defaults to "nis [NOTFOUND=return] files"
+ return hostLookupCgo
+ }
+ if c.goos == "linux" {
+ // glibc says the default is "dns [!UNAVAIL=return] files"
+ // http://www.gnu.org/software/libc/manual/html_node/Notes-on-NSS-Configuration-File.html.
+ return hostLookupDNSFiles
+ }
+ return hostLookupFilesDNS
+ }
+ if nss.err != nil {
+ // We failed to parse or open nsswitch.conf, so
+ // conservatively assume we should use cgo if it's
+ // available.
+ return hostLookupCgo
+ }
+
+ var mdnsSource, filesSource, dnsSource bool
+ var first string
+ for _, src := range srcs {
+ if src.source == "myhostname" {
+ if hasDot {
+ continue
+ }
+ return hostLookupCgo
+ }
+ if src.source == "files" || src.source == "dns" {
+ if !src.standardCriteria() {
+ return hostLookupCgo // non-standard; let libc deal with it.
+ }
+ if src.source == "files" {
+ filesSource = true
+ } else if src.source == "dns" {
+ dnsSource = true
+ }
+ if first == "" {
+ first = src.source
+ }
+ continue
+ }
+ if stringsHasPrefix(src.source, "mdns") {
+ // e.g. "mdns4", "mdns4_minimal"
+ // We already returned true before if it was *.local.
+ // libc wouldn't have found a hit on this anyway.
+ mdnsSource = true
+ continue
+ }
+ // Some source we don't know how to deal with.
+ return hostLookupCgo
+ }
+
+ // We don't parse mdns.allow files. They're rare. If one
+ // exists, it might list other TLDs (besides .local) or even
+ // '*', so just let libc deal with it.
+ if mdnsSource && c.hasMDNSAllow {
+ return hostLookupCgo
+ }
+
+ // Cases where Go can handle it without cgo and C thread
+ // overhead.
+ switch {
+ case filesSource && dnsSource:
+ if first == "files" {
+ return hostLookupFilesDNS
+ } else {
+ return hostLookupDNSFiles
+ }
+ case filesSource:
+ return hostLookupFiles
+ case dnsSource:
+ return hostLookupDNS
+ }
+
+ // Something weird. Let libc deal with it.
+ return hostLookupCgo
+}
diff --git a/src/net/conf_test.go b/src/net/conf_test.go
new file mode 100644
index 0000000000..003c615eb8
--- /dev/null
+++ b/src/net/conf_test.go
@@ -0,0 +1,297 @@
+// Copyright 2015 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 darwin dragonfly freebsd linux netbsd openbsd solaris
+
+package net
+
+import (
+ "os"
+ "strings"
+ "testing"
+)
+
+type nssHostTest struct {
+ host string
+ want hostLookupOrder
+}
+
+func nssStr(s string) *nssConf { return parseNSSConf(strings.NewReader(s)) }
+
+// represents a dnsConfig returned by parsing a nonexistent resolv.conf
+var defaultResolvConf = &dnsConfig{
+ servers: defaultNS,
+ ndots: 1,
+ timeout: 5,
+ attempts: 2,
+ err: os.ErrNotExist,
+}
+
+func TestConfHostLookupOrder(t *testing.T) {
+ tests := []struct {
+ name string
+ c *conf
+ goos string
+ hostTests []nssHostTest
+ }{
+ {
+ name: "force",
+ c: &conf{
+ forceCgoLookupHost: true,
+ nss: nssStr("foo: bar"),
+ resolv: defaultResolvConf,
+ },
+ hostTests: []nssHostTest{
+ {"foo.local", hostLookupCgo},
+ {"google.com", hostLookupCgo},
+ },
+ },
+ {
+ name: "ubuntu_trusty_avahi",
+ c: &conf{
+ nss: nssStr("hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4"),
+ resolv: defaultResolvConf,
+ },
+ hostTests: []nssHostTest{
+ {"foo.local", hostLookupCgo},
+ {"foo.local.", hostLookupCgo},
+ {"foo.LOCAL", hostLookupCgo},
+ {"foo.LOCAL.", hostLookupCgo},
+ {"google.com", hostLookupFilesDNS},
+ },
+ },
+ {
+ name: "freebsdlinux_no_resolv_conf",
+ c: &conf{
+ goos: "freebsd",
+ nss: nssStr("foo: bar"),
+ resolv: defaultResolvConf,
+ },
+ hostTests: []nssHostTest{{"google.com", hostLookupFilesDNS}},
+ },
+ // On OpenBSD, no resolv.conf means no DNS.
+ {
+ name: "openbsd_no_resolv_conf",
+ c: &conf{
+ goos: "openbsd",
+ resolv: defaultResolvConf,
+ },
+ hostTests: []nssHostTest{{"google.com", hostLookupFiles}},
+ },
+ {
+ name: "solaris_no_nsswitch",
+ c: &conf{
+ goos: "solaris",
+ nss: &nssConf{err: os.ErrNotExist},
+ resolv: defaultResolvConf,
+ },
+ hostTests: []nssHostTest{{"google.com", hostLookupCgo}},
+ },
+ {
+ name: "openbsd_lookup_bind_file",
+ c: &conf{
+ goos: "openbsd",
+ resolv: &dnsConfig{lookup: []string{"bind", "file"}},
+ },
+ hostTests: []nssHostTest{
+ {"google.com", hostLookupDNSFiles},
+ {"foo.local", hostLookupDNSFiles},
+ },
+ },
+ {
+ name: "openbsd_lookup_file_bind",
+ c: &conf{
+ goos: "openbsd",
+ resolv: &dnsConfig{lookup: []string{"file", "bind"}},
+ },
+ hostTests: []nssHostTest{{"google.com", hostLookupFilesDNS}},
+ },
+ {
+ name: "openbsd_lookup_bind",
+ c: &conf{
+ goos: "openbsd",
+ resolv: &dnsConfig{lookup: []string{"bind"}},
+ },
+ hostTests: []nssHostTest{{"google.com", hostLookupDNS}},
+ },
+ {
+ name: "openbsd_lookup_file",
+ c: &conf{
+ goos: "openbsd",
+ resolv: &dnsConfig{lookup: []string{"file"}},
+ },
+ hostTests: []nssHostTest{{"google.com", hostLookupFiles}},
+ },
+ {
+ name: "openbsd_lookup_yp",
+ c: &conf{
+ goos: "openbsd",
+ resolv: &dnsConfig{lookup: []string{"file", "bind", "yp"}},
+ },
+ hostTests: []nssHostTest{{"google.com", hostLookupCgo}},
+ },
+ {
+ name: "openbsd_lookup_two",
+ c: &conf{
+ goos: "openbsd",
+ resolv: &dnsConfig{lookup: []string{"file", "foo"}},
+ },
+ hostTests: []nssHostTest{{"google.com", hostLookupCgo}},
+ },
+ {
+ name: "openbsd_lookup_empty",
+ c: &conf{
+ goos: "openbsd",
+ resolv: &dnsConfig{lookup: nil},
+ },
+ hostTests: []nssHostTest{{"google.com", hostLookupDNSFiles}},
+ },
+ // glibc lacking an nsswitch.conf, per
+ // http://www.gnu.org/software/libc/manual/html_node/Notes-on-NSS-Configuration-File.html
+ {
+ name: "linux_no_nsswitch.conf",
+ c: &conf{
+ goos: "linux",
+ nss: &nssConf{err: os.ErrNotExist},
+ resolv: defaultResolvConf,
+ },
+ hostTests: []nssHostTest{{"google.com", hostLookupDNSFiles}},
+ },
+ {
+ name: "files_mdns_dns",
+ c: &conf{
+ nss: nssStr("hosts: files mdns dns"),
+ resolv: defaultResolvConf,
+ },
+ hostTests: []nssHostTest{
+ {"x.com", hostLookupFilesDNS},
+ {"x.local", hostLookupCgo},
+ },
+ },
+ {
+ name: "dns_special_hostnames",
+ c: &conf{
+ nss: nssStr("hosts: dns"),
+ resolv: defaultResolvConf,
+ },
+ hostTests: []nssHostTest{
+ {"x.com", hostLookupDNS},
+ {"x\\.com", hostLookupCgo}, // punt on weird glibc escape
+ {"foo.com%en0", hostLookupCgo}, // and IPv6 zones
+ },
+ },
+ {
+ name: "mdns_allow",
+ c: &conf{
+ nss: nssStr("hosts: files mdns dns"),
+ resolv: defaultResolvConf,
+ hasMDNSAllow: true,
+ },
+ hostTests: []nssHostTest{
+ {"x.com", hostLookupCgo},
+ {"x.local", hostLookupCgo},
+ },
+ },
+ {
+ name: "files_dns",
+ c: &conf{
+ nss: nssStr("hosts: files dns"),
+ resolv: defaultResolvConf,
+ },
+ hostTests: []nssHostTest{
+ {"x.com", hostLookupFilesDNS},
+ {"x", hostLookupFilesDNS},
+ {"x.local", hostLookupCgo},
+ },
+ },
+ {
+ name: "dns_files",
+ c: &conf{
+ nss: nssStr("hosts: dns files"),
+ resolv: defaultResolvConf,
+ },
+ hostTests: []nssHostTest{
+ {"x.com", hostLookupDNSFiles},
+ {"x", hostLookupDNSFiles},
+ {"x.local", hostLookupCgo},
+ },
+ },
+ {
+ name: "something_custom",
+ c: &conf{
+ nss: nssStr("hosts: dns files something_custom"),
+ resolv: defaultResolvConf,
+ },
+ hostTests: []nssHostTest{
+ {"x.com", hostLookupCgo},
+ },
+ },
+ {
+ name: "myhostname",
+ c: &conf{
+ nss: nssStr("hosts: files dns myhostname"),
+ resolv: defaultResolvConf,
+ },
+ hostTests: []nssHostTest{
+ {"x.com", hostLookupFilesDNS},
+ {"somehostname", hostLookupCgo},
+ },
+ },
+ {
+ name: "ubuntu14.04.02",
+ c: &conf{
+ nss: nssStr("hosts: files myhostname mdns4_minimal [NOTFOUND=return] dns mdns4"),
+ resolv: defaultResolvConf,
+ },
+ hostTests: []nssHostTest{
+ {"x.com", hostLookupFilesDNS},
+ {"somehostname", hostLookupCgo},
+ },
+ },
+ // Debian Squeeze is just "dns,files", but lists all
+ // the default criteria for dns, but then has a
+ // non-standard but redundant notfound=return for the
+ // files.
+ {
+ name: "debian_squeeze",
+ c: &conf{
+ nss: nssStr("hosts: dns [success=return notfound=continue unavail=continue tryagain=continue] files [notfound=return]"),
+ resolv: defaultResolvConf,
+ },
+ hostTests: []nssHostTest{
+ {"x.com", hostLookupDNSFiles},
+ {"somehostname", hostLookupDNSFiles},
+ },
+ },
+ {
+ name: "resolv.conf-unknown",
+ c: &conf{
+ nss: nssStr("foo: bar"),
+ resolv: &dnsConfig{servers: defaultNS, ndots: 1, timeout: 5, attempts: 2, unknownOpt: true},
+ },
+ hostTests: []nssHostTest{{"google.com", hostLookupCgo}},
+ },
+ // Android should always use cgo.
+ {
+ name: "android",
+ c: &conf{
+ goos: "android",
+ nss: nssStr(""),
+ resolv: defaultResolvConf,
+ },
+ hostTests: []nssHostTest{
+ {"x.com", hostLookupCgo},
+ },
+ },
+ }
+ for _, tt := range tests {
+ for _, ht := range tt.hostTests {
+ gotOrder := tt.c.hostLookupOrder(ht.host)
+ if gotOrder != ht.want {
+ t.Errorf("%s: hostLookupOrder(%q) = %v; want %v", tt.name, ht.host, gotOrder, ht.want)
+ }
+ }
+ }
+
+}
diff --git a/src/net/conn_test.go b/src/net/conn_test.go
index 912c084c70..6995c110f2 100644
--- a/src/net/conn_test.go
+++ b/src/net/conn_test.go
@@ -8,106 +8,58 @@
package net
import (
- "os"
"testing"
"time"
)
-var connTests = []struct {
- net string
- addr string
-}{
- {"tcp", "127.0.0.1:0"},
- {"unix", testUnixAddr()},
- {"unixpacket", testUnixAddr()},
-}
-
// someTimeout is used just to test that net.Conn implementations
// don't explode when their SetFooDeadline methods are called.
// It isn't actually used for testing timeouts.
const someTimeout = 10 * time.Second
func TestConnAndListener(t *testing.T) {
- for _, tt := range connTests {
- if !testableNetwork(tt.net) {
- t.Logf("skipping %s test", tt.net)
+ for i, network := range []string{"tcp", "unix", "unixpacket"} {
+ if !testableNetwork(network) {
+ t.Logf("skipping %s test", network)
continue
}
- ln, err := Listen(tt.net, tt.addr)
+ ls, err := newLocalServer(network)
if err != nil {
- t.Fatalf("Listen failed: %v", err)
+ t.Fatal(err)
}
- defer func(ln Listener, net, addr string) {
- ln.Close()
- switch net {
- case "unix", "unixpacket":
- os.Remove(addr)
- }
- }(ln, tt.net, tt.addr)
- if ln.Addr().Network() != tt.net {
- t.Fatalf("got %v; expected %v", ln.Addr().Network(), tt.net)
+ defer ls.teardown()
+ ch := make(chan error, 1)
+ handler := func(ls *localServer, ln Listener) { transponder(ln, ch) }
+ if err := ls.buildup(handler); err != nil {
+ t.Fatal(err)
+ }
+ if ls.Listener.Addr().Network() != network {
+ t.Fatalf("got %s; want %s", ls.Listener.Addr().Network(), network)
}
- done := make(chan int)
- go transponder(t, ln, done)
-
- c, err := Dial(tt.net, ln.Addr().String())
+ c, err := Dial(ls.Listener.Addr().Network(), ls.Listener.Addr().String())
if err != nil {
- t.Fatalf("Dial failed: %v", err)
+ t.Fatal(err)
}
defer c.Close()
- if c.LocalAddr().Network() != tt.net || c.LocalAddr().Network() != tt.net {
- t.Fatalf("got %v->%v; expected %v->%v", c.LocalAddr().Network(), c.RemoteAddr().Network(), tt.net, tt.net)
+ if c.LocalAddr().Network() != network || c.LocalAddr().Network() != network {
+ t.Fatalf("got %s->%s; want %s->%s", c.LocalAddr().Network(), c.RemoteAddr().Network(), network, network)
}
c.SetDeadline(time.Now().Add(someTimeout))
c.SetReadDeadline(time.Now().Add(someTimeout))
c.SetWriteDeadline(time.Now().Add(someTimeout))
- if _, err := c.Write([]byte("CONN TEST")); err != nil {
- t.Fatalf("Conn.Write failed: %v", err)
+ if _, err := c.Write([]byte("CONN AND LISTENER TEST")); err != nil {
+ t.Fatal(err)
}
rb := make([]byte, 128)
if _, err := c.Read(rb); err != nil {
- t.Fatalf("Conn.Read failed: %v", err)
+ t.Fatal(err)
}
- <-done
- }
-}
-
-func transponder(t *testing.T, ln Listener, done chan<- int) {
- defer func() { done <- 1 }()
-
- switch ln := ln.(type) {
- case *TCPListener:
- ln.SetDeadline(time.Now().Add(someTimeout))
- case *UnixListener:
- ln.SetDeadline(time.Now().Add(someTimeout))
- }
- c, err := ln.Accept()
- if err != nil {
- t.Errorf("Listener.Accept failed: %v", err)
- return
- }
- defer c.Close()
- network := ln.Addr().Network()
- if c.LocalAddr().Network() != network || c.LocalAddr().Network() != network {
- t.Errorf("got %v->%v; expected %v->%v", c.LocalAddr().Network(), c.RemoteAddr().Network(), network, network)
- return
- }
- c.SetDeadline(time.Now().Add(someTimeout))
- c.SetReadDeadline(time.Now().Add(someTimeout))
- c.SetWriteDeadline(time.Now().Add(someTimeout))
-
- b := make([]byte, 128)
- n, err := c.Read(b)
- if err != nil {
- t.Errorf("Conn.Read failed: %v", err)
- return
- }
- if _, err := c.Write(b[:n]); err != nil {
- t.Errorf("Conn.Write failed: %v", err)
- return
+ for err := range ch {
+ t.Errorf("#%d: %v", i, err)
+ }
}
}
diff --git a/src/net/dial.go b/src/net/dial.go
index 0424ed250f..4f0c6cb0ea 100644
--- a/src/net/dial.go
+++ b/src/net/dial.go
@@ -95,7 +95,7 @@ func parseNetwork(net string) (afnet string, proto int, err error) {
return "", 0, UnknownNetworkError(net)
}
-func resolveAddr(op, net, addr string, deadline time.Time) (netaddr, error) {
+func resolveAddrList(op, net, addr string, deadline time.Time) (addrList, error) {
afnet, _, err := parseNetwork(net)
if err != nil {
return nil, err
@@ -105,9 +105,13 @@ func resolveAddr(op, net, addr string, deadline time.Time) (netaddr, error) {
}
switch afnet {
case "unix", "unixgram", "unixpacket":
- return ResolveUnixAddr(afnet, addr)
+ addr, err := ResolveUnixAddr(afnet, addr)
+ if err != nil {
+ return nil, err
+ }
+ return addrList{addr}, nil
}
- return resolveInternetAddr(afnet, addr, deadline)
+ return internetAddrList(afnet, addr, deadline)
}
// Dial connects to the address on the named network.
@@ -155,33 +159,35 @@ func DialTimeout(network, address string, timeout time.Duration) (Conn, error) {
// See func Dial for a description of the network and address
// parameters.
func (d *Dialer) Dial(network, address string) (Conn, error) {
- ra, err := resolveAddr("dial", network, address, d.deadline())
+ addrs, err := resolveAddrList("dial", network, address, d.deadline())
if err != nil {
- return nil, &OpError{Op: "dial", Net: network, Addr: nil, Err: err}
+ return nil, &OpError{Op: "dial", Net: network, Source: nil, Addr: nil, Err: err}
}
var dialer func(deadline time.Time) (Conn, error)
- if ras, ok := ra.(addrList); ok && d.DualStack && network == "tcp" {
- dialer = func(deadline time.Time) (Conn, error) {
- return dialMulti(network, address, d.LocalAddr, ras, deadline)
- }
- } else {
- dialer = func(deadline time.Time) (Conn, error) {
- return dialSingle(network, address, d.LocalAddr, ra.toAddr(), deadline)
+ if d.DualStack && network == "tcp" {
+ primaries, fallbacks := addrs.partition(isIPv4)
+ if len(fallbacks) > 0 {
+ dialer = func(deadline time.Time) (Conn, error) {
+ return dialMulti(network, address, d.LocalAddr, addrList{primaries[0], fallbacks[0]}, deadline)
+ }
}
}
- c, err := dial(network, ra.toAddr(), dialer, d.deadline())
+ if dialer == nil {
+ dialer = func(deadline time.Time) (Conn, error) {
+ return dialSingle(network, address, d.LocalAddr, addrs.first(isIPv4), deadline)
+ }
+ }
+ c, err := dial(network, addrs.first(isIPv4), dialer, d.deadline())
if d.KeepAlive > 0 && err == nil {
if tc, ok := c.(*TCPConn); ok {
- tc.SetKeepAlive(true)
- tc.SetKeepAlivePeriod(d.KeepAlive)
+ setKeepAlive(tc.fd, true)
+ setKeepAlivePeriod(tc.fd, d.KeepAlive)
testHookSetKeepAlive()
}
}
return c, err
}
-var testHookSetKeepAlive = func() {} // changed by dial_test.go
-
// dialMulti attempts to establish connections to each destination of
// the list of addresses. It will return the first established
// connection and close the other connections. Otherwise it returns
@@ -208,7 +214,7 @@ func dialMulti(net, addr string, la Addr, ras addrList, deadline time.Time) (Con
// unnecessary resource starvation.
c.Close()
}
- }(ra.toAddr())
+ }(ra)
}
defer close(sig)
lastErr := errTimeout
@@ -229,7 +235,7 @@ func dialMulti(net, addr string, la Addr, ras addrList, deadline time.Time) (Con
// the destination address.
func dialSingle(net, addr string, la, ra Addr, deadline time.Time) (c Conn, err error) {
if la != nil && la.Network() != ra.Network() {
- return nil, &OpError{Op: "dial", Net: net, Addr: ra, Err: errors.New("mismatched local address type " + la.Network())}
+ return nil, &OpError{Op: "dial", Net: net, Source: la, Addr: ra, Err: errors.New("mismatched local address type " + la.Network())}
}
switch ra := ra.(type) {
case *TCPAddr:
@@ -245,7 +251,7 @@ func dialSingle(net, addr string, la, ra Addr, deadline time.Time) (c Conn, err
la, _ := la.(*UnixAddr)
c, err = dialUnix(net, la, ra, deadline)
default:
- return nil, &OpError{Op: "dial", Net: net, Addr: ra, Err: &AddrError{Err: "unexpected address type", Addr: addr}}
+ return nil, &OpError{Op: "dial", Net: net, Source: la, Addr: ra, Err: &AddrError{Err: "unexpected address type", Addr: addr}}
}
if err != nil {
return nil, err // c is non-nil interface containing nil pointer
@@ -258,18 +264,18 @@ func dialSingle(net, addr string, la, ra Addr, deadline time.Time) (c Conn, err
// "tcp6", "unix" or "unixpacket".
// See Dial for the syntax of laddr.
func Listen(net, laddr string) (Listener, error) {
- la, err := resolveAddr("listen", net, laddr, noDeadline)
+ addrs, err := resolveAddrList("listen", net, laddr, noDeadline)
if err != nil {
- return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: err}
+ return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: nil, Err: err}
}
var l Listener
- switch la := la.toAddr().(type) {
+ switch la := addrs.first(isIPv4).(type) {
case *TCPAddr:
l, err = ListenTCP(net, la)
case *UnixAddr:
l, err = ListenUnix(net, la)
default:
- return nil, &OpError{Op: "listen", Net: net, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: laddr}}
+ return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: laddr}}
}
if err != nil {
return nil, err // l is non-nil interface containing nil pointer
@@ -282,12 +288,12 @@ func Listen(net, laddr string) (Listener, error) {
// "udp6", "ip", "ip4", "ip6" or "unixgram".
// See Dial for the syntax of laddr.
func ListenPacket(net, laddr string) (PacketConn, error) {
- la, err := resolveAddr("listen", net, laddr, noDeadline)
+ addrs, err := resolveAddrList("listen", net, laddr, noDeadline)
if err != nil {
- return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: err}
+ return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: nil, Err: err}
}
var l PacketConn
- switch la := la.toAddr().(type) {
+ switch la := addrs.first(isIPv4).(type) {
case *UDPAddr:
l, err = ListenUDP(net, la)
case *IPAddr:
@@ -295,7 +301,7 @@ func ListenPacket(net, laddr string) (PacketConn, error) {
case *UnixAddr:
l, err = ListenUnixgram(net, la)
default:
- return nil, &OpError{Op: "listen", Net: net, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: laddr}}
+ return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: laddr}}
}
if err != nil {
return nil, err // l is non-nil interface containing nil pointer
diff --git a/src/net/dial_gen.go b/src/net/dial_gen.go
index ada6233003..a628f71483 100644
--- a/src/net/dial_gen.go
+++ b/src/net/dial_gen.go
@@ -6,23 +6,19 @@
package net
-import (
- "time"
-)
-
-var testingIssue5349 bool // used during tests
+import "time"
// dialChannel is the simple pure-Go implementation of dial, still
// used on operating systems where the deadline hasn't been pushed
// down into the pollserver. (Plan 9 and some old versions of Windows)
func dialChannel(net string, ra Addr, dialer func(time.Time) (Conn, error), deadline time.Time) (Conn, error) {
- var timeout time.Duration
- if !deadline.IsZero() {
- timeout = deadline.Sub(time.Now())
- }
- if timeout <= 0 {
+ if deadline.IsZero() {
return dialer(noDeadline)
}
+ timeout := deadline.Sub(time.Now())
+ if timeout <= 0 {
+ return nil, &OpError{Op: "dial", Net: net, Source: nil, Addr: ra, Err: errTimeout}
+ }
t := time.NewTimer(timeout)
defer t.Stop()
type racer struct {
@@ -31,15 +27,13 @@ func dialChannel(net string, ra Addr, dialer func(time.Time) (Conn, error), dead
}
ch := make(chan racer, 1)
go func() {
- if testingIssue5349 {
- time.Sleep(time.Millisecond)
- }
+ testHookDialChannel()
c, err := dialer(noDeadline)
ch <- racer{c, err}
}()
select {
case <-t.C:
- return nil, &OpError{Op: "dial", Net: net, Addr: ra, Err: errTimeout}
+ return nil, &OpError{Op: "dial", Net: net, Source: nil, Addr: ra, Err: errTimeout}
case racer := <-ch:
return racer.Conn, racer.error
}
diff --git a/src/net/dial_test.go b/src/net/dial_test.go
index 42898d669f..f5141bcd5e 100644
--- a/src/net/dial_test.go
+++ b/src/net/dial_test.go
@@ -5,111 +5,49 @@
package net
import (
- "bytes"
- "flag"
- "fmt"
- "io"
- "os"
- "os/exec"
- "reflect"
- "regexp"
+ "net/internal/socktest"
"runtime"
- "strconv"
"sync"
"testing"
"time"
)
-func newLocalListener(t *testing.T) Listener {
- ln, err := Listen("tcp", "127.0.0.1:0")
- if err != nil {
- ln, err = Listen("tcp6", "[::1]:0")
+var prohibitionaryDialArgTests = []struct {
+ network string
+ address string
+}{
+ {"tcp6", "127.0.0.1"},
+ {"tcp6", "::ffff:127.0.0.1"},
+}
+
+func TestProhibitionaryDialArg(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
}
+ if testing.Short() || !*testExternal {
+ t.Skip("avoid external network")
+ }
+ if !supportsIPv4map {
+ t.Skip("mapping ipv4 address inside ipv6 address not supported")
+ }
+
+ ln, err := Listen("tcp", "[::]:0")
if err != nil {
t.Fatal(err)
}
- return ln
-}
-
-func TestDialTimeout(t *testing.T) {
- origBacklog := listenerBacklog
- defer func() {
- listenerBacklog = origBacklog
- }()
- listenerBacklog = 1
-
- ln := newLocalListener(t)
defer ln.Close()
- errc := make(chan error)
-
- numConns := listenerBacklog + 100
-
- // TODO(bradfitz): It's hard to test this in a portable
- // way. This is unfortunate, but works for now.
- switch runtime.GOOS {
- case "linux":
- // The kernel will start accepting TCP connections before userspace
- // gets a chance to not accept them, so fire off a bunch to fill up
- // the kernel's backlog. Then we test we get a failure after that.
- for i := 0; i < numConns; i++ {
- go func() {
- _, err := DialTimeout("tcp", ln.Addr().String(), 200*time.Millisecond)
- errc <- err
- }()
- }
- case "darwin", "plan9", "windows":
- // At least OS X 10.7 seems to accept any number of
- // connections, ignoring listen's backlog, so resort
- // to connecting to a hopefully-dead 127/8 address.
- // Same for windows.
- //
- // Use an IANA reserved port (49151) instead of 80, because
- // on our 386 builder, this Dial succeeds, connecting
- // to an IIS web server somewhere. The data center
- // or VM or firewall must be stealing the TCP connection.
- //
- // IANA Service Name and Transport Protocol Port Number Registry
- //
- go func() {
- c, err := DialTimeout("tcp", "127.0.71.111:49151", 200*time.Millisecond)
- if err == nil {
- err = fmt.Errorf("unexpected: connected to %s!", c.RemoteAddr())
- c.Close()
- }
- errc <- err
- }()
- default:
- // TODO(bradfitz):
- // OpenBSD may have a reject route to 127/8 except 127.0.0.1/32
- // by default. FreeBSD likely works, but is untested.
- // TODO(rsc):
- // The timeout never happens on Windows. Why? Issue 3016.
- t.Skipf("skipping test on %q; untested.", runtime.GOOS)
+ _, port, err := SplitHostPort(ln.Addr().String())
+ if err != nil {
+ t.Fatal(err)
}
- connected := 0
- for {
- select {
- case <-time.After(15 * time.Second):
- t.Fatal("too slow")
- case err := <-errc:
- if err == nil {
- connected++
- if connected == numConns {
- t.Fatal("all connections connected; expected some to time out")
- }
- } else {
- terr, ok := err.(timeout)
- if !ok {
- t.Fatalf("got error %q; want error with timeout interface", err)
- }
- if !terr.Timeout() {
- t.Fatalf("got error %q; not a timeout", err)
- }
- // Pass. We saw a timeout error.
- return
- }
+ for i, tt := range prohibitionaryDialArgTests {
+ c, err := Dial(tt.network, JoinHostPort(tt.address, port))
+ if err == nil {
+ c.Close()
+ t.Errorf("#%d: %v", i, err)
}
}
}
@@ -117,7 +55,7 @@ func TestDialTimeout(t *testing.T) {
func TestSelfConnect(t *testing.T) {
if runtime.GOOS == "windows" {
// TODO(brainman): do not know why it hangs.
- t.Skip("skipping known-broken test on windows")
+ t.Skip("known-broken test on windows")
}
// Test that Dial does not honor self-connects.
@@ -160,303 +98,144 @@ func TestSelfConnect(t *testing.T) {
}
}
-var runErrorTest = flag.Bool("run_error_test", false, "let TestDialError check for dns errors")
-
-type DialErrorTest struct {
- Net string
- Raddr string
- Pattern string
-}
-
-var dialErrorTests = []DialErrorTest{
- {
- "datakit", "mh/astro/r70",
- "dial datakit mh/astro/r70: unknown network datakit",
- },
- {
- "tcp", "127.0.0.1:☺",
- "dial tcp 127.0.0.1:☺: unknown port tcp/☺",
- },
- {
- "tcp", "no-such-name.google.com.:80",
- "dial tcp no-such-name.google.com.:80: lookup no-such-name.google.com.( on .*)?: no (.*)",
- },
- {
- "tcp", "no-such-name.no-such-top-level-domain.:80",
- "dial tcp no-such-name.no-such-top-level-domain.:80: lookup no-such-name.no-such-top-level-domain.( on .*)?: no (.*)",
- },
- {
- "tcp", "no-such-name:80",
- `dial tcp no-such-name:80: lookup no-such-name\.(.*\.)?( on .*)?: no (.*)`,
- },
- {
- "tcp", "mh/astro/r70:http",
- "dial tcp mh/astro/r70:http: lookup mh/astro/r70: invalid domain name",
- },
- {
- "unix", "/etc/file-not-found",
- "dial unix /etc/file-not-found: no such file or directory",
- },
- {
- "unix", "/etc/",
- "dial unix /etc/: (permission denied|socket operation on non-socket|connection refused)",
- },
- {
- "unixpacket", "/etc/file-not-found",
- "dial unixpacket /etc/file-not-found: no such file or directory",
- },
- {
- "unixpacket", "/etc/",
- "dial unixpacket /etc/: (permission denied|socket operation on non-socket|connection refused)",
- },
-}
-
-var duplicateErrorPattern = `dial (.*) dial (.*)`
-
-func TestDialError(t *testing.T) {
- if !*runErrorTest {
- t.Logf("test disabled; use -run_error_test to enable")
- return
- }
- for i, tt := range dialErrorTests {
- c, err := Dial(tt.Net, tt.Raddr)
- if c != nil {
- c.Close()
- }
- if err == nil {
- t.Errorf("#%d: nil error, want match for %#q", i, tt.Pattern)
- continue
- }
- s := err.Error()
- match, _ := regexp.MatchString(tt.Pattern, s)
- if !match {
- t.Errorf("#%d: %q, want match for %#q", i, s, tt.Pattern)
- }
- match, _ = regexp.MatchString(duplicateErrorPattern, s)
- if match {
- t.Errorf("#%d: %q, duplicate error return from Dial", i, s)
- }
- }
-}
-
-var invalidDialAndListenArgTests = []struct {
- net string
- addr string
- err error
-}{
- {"foo", "bar", &OpError{Op: "dial", Net: "foo", Addr: nil, Err: UnknownNetworkError("foo")}},
- {"baz", "", &OpError{Op: "listen", Net: "baz", Addr: nil, Err: UnknownNetworkError("baz")}},
- {"tcp", "", &OpError{Op: "dial", Net: "tcp", Addr: nil, Err: errMissingAddress}},
-}
-
-func TestInvalidDialAndListenArgs(t *testing.T) {
- for _, tt := range invalidDialAndListenArgTests {
- var err error
- switch tt.err.(*OpError).Op {
- case "dial":
- _, err = Dial(tt.net, tt.addr)
- case "listen":
- _, err = Listen(tt.net, tt.addr)
- }
- if !reflect.DeepEqual(tt.err, err) {
- t.Fatalf("got %#v; expected %#v", err, tt.err)
- }
- }
-}
-
func TestDialTimeoutFDLeak(t *testing.T) {
- if runtime.GOOS != "linux" {
- // TODO(bradfitz): test on other platforms
- t.Skipf("skipping test on %q", runtime.GOOS)
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("%s does not have full support of socktest", runtime.GOOS)
}
- ln := newLocalListener(t)
- defer ln.Close()
+ const T = 100 * time.Millisecond
- type connErr struct {
- conn Conn
- err error
- }
- dials := listenerBacklog + 100
- // used to be listenerBacklog + 5, but was found to be unreliable, issue 4384.
- maxGoodConnect := listenerBacklog + runtime.NumCPU()*10
- resc := make(chan connErr)
- for i := 0; i < dials; i++ {
- go func() {
- conn, err := DialTimeout("tcp", ln.Addr().String(), 500*time.Millisecond)
- resc <- connErr{conn, err}
- }()
- }
-
- var firstErr string
- var ngood int
- var toClose []io.Closer
- for i := 0; i < dials; i++ {
- ce := <-resc
- if ce.err == nil {
- ngood++
- if ngood > maxGoodConnect {
- t.Errorf("%d good connects; expected at most %d", ngood, maxGoodConnect)
- }
- toClose = append(toClose, ce.conn)
- continue
+ switch runtime.GOOS {
+ case "plan9", "windows":
+ origTestHookDialChannel := testHookDialChannel
+ testHookDialChannel = func() { time.Sleep(2 * T) }
+ defer func() { testHookDialChannel = origTestHookDialChannel }()
+ if runtime.GOOS == "plan9" {
+ break
}
- err := ce.err
- if firstErr == "" {
- firstErr = err.Error()
- } else if err.Error() != firstErr {
- t.Fatalf("inconsistent error messages: first was %q, then later %q", firstErr, err)
- }
- }
- for _, c := range toClose {
- c.Close()
- }
- for i := 0; i < 100; i++ {
- if got := numFD(); got < dials {
- // Test passes.
- return
- }
- time.Sleep(10 * time.Millisecond)
- }
- if got := numFD(); got >= dials {
- t.Errorf("num fds after %d timeouts = %d; want <%d", dials, got, dials)
- }
-}
-
-func numTCP() (ntcp, nopen, nclose int, err error) {
- lsof, err := exec.Command("lsof", "-n", "-p", strconv.Itoa(os.Getpid())).Output()
- if err != nil {
- return 0, 0, 0, err
- }
- ntcp += bytes.Count(lsof, []byte("TCP"))
- for _, state := range []string{"LISTEN", "SYN_SENT", "SYN_RECEIVED", "ESTABLISHED"} {
- nopen += bytes.Count(lsof, []byte(state))
- }
- for _, state := range []string{"CLOSED", "CLOSE_WAIT", "LAST_ACK", "FIN_WAIT_1", "FIN_WAIT_2", "CLOSING", "TIME_WAIT"} {
- nclose += bytes.Count(lsof, []byte(state))
- }
- return ntcp, nopen, nclose, nil
-}
-
-func TestDialMultiFDLeak(t *testing.T) {
- t.Skip("flaky test - golang.org/issue/8764")
-
- if !supportsIPv4 || !supportsIPv6 {
- t.Skip("neither ipv4 nor ipv6 is supported")
- }
-
- halfDeadServer := func(dss *dualStackServer, ln Listener) {
- for {
- if c, err := ln.Accept(); err != nil {
- return
- } else {
- // It just keeps established
- // connections like a half-dead server
- // does.
- dss.putConn(c)
- }
- }
- }
- dss, err := newDualStackServer([]streamListener{
- {net: "tcp4", addr: "127.0.0.1"},
- {net: "tcp6", addr: "[::1]"},
- })
- if err != nil {
- t.Fatalf("newDualStackServer failed: %v", err)
- }
- defer dss.teardown()
- if err := dss.buildup(halfDeadServer); err != nil {
- t.Fatalf("dualStackServer.buildup failed: %v", err)
- }
-
- _, before, _, err := numTCP()
- if err != nil {
- t.Skipf("skipping test; error finding or running lsof: %v", err)
+ fallthrough
+ default:
+ sw.Set(socktest.FilterConnect, func(so *socktest.Status) (socktest.AfterFilter, error) {
+ time.Sleep(2 * T)
+ return nil, errTimeout
+ })
+ defer sw.Set(socktest.FilterConnect, nil)
}
+ before := sw.Sockets()
+ const N = 100
var wg sync.WaitGroup
- portnum, _, _ := dtoi(dss.port, 0)
- ras := addrList{
- // Losers that will fail to connect, see RFC 6890.
- &TCPAddr{IP: IPv4(198, 18, 0, 254), Port: portnum},
- &TCPAddr{IP: ParseIP("2001:2::254"), Port: portnum},
-
- // Winner candidates of this race.
- &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: portnum},
- &TCPAddr{IP: IPv6loopback, Port: portnum},
-
- // Losers that will have established connections.
- &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: portnum},
- &TCPAddr{IP: IPv6loopback, Port: portnum},
- }
- const T1 = 10 * time.Millisecond
- const T2 = 2 * T1
- const N = 10
+ wg.Add(N)
for i := 0; i < N; i++ {
- wg.Add(1)
go func() {
defer wg.Done()
- if c, err := dialMulti("tcp", "fast failover test", nil, ras, time.Now().Add(T1)); err == nil {
+ // This dial never starts to send any SYN
+ // segment because of above socket filter and
+ // test hook.
+ c, err := DialTimeout("tcp", "127.0.0.1:0", T)
+ if err == nil {
+ t.Errorf("unexpectedly established: tcp:%s->%s", c.LocalAddr(), c.RemoteAddr())
c.Close()
}
}()
}
wg.Wait()
- time.Sleep(T2)
-
- ntcp, after, nclose, err := numTCP()
- if err != nil {
- t.Skipf("skipping test; error finding or running lsof: %v", err)
- }
- t.Logf("tcp sessions: %v, open sessions: %v, closing sessions: %v", ntcp, after, nclose)
-
- if after != before {
- t.Fatalf("got %v open sessions; expected %v", after, before)
+ after := sw.Sockets()
+ if len(after) != len(before) {
+ t.Errorf("got %d; want %d", len(after), len(before))
}
}
-func numFD() int {
- if runtime.GOOS == "linux" {
- f, err := os.Open("/proc/self/fd")
- if err != nil {
- panic(err)
- }
- defer f.Close()
- names, err := f.Readdirnames(0)
- if err != nil {
- panic(err)
- }
- return len(names)
+func TestDialerDualStackFDLeak(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("%s does not have full support of socktest", runtime.GOOS)
+ case "windows":
+ t.Skipf("not implemented a way to cancel dial racers in TCP SYN-SENT state on %s", runtime.GOOS)
+ }
+ if !supportsIPv4 || !supportsIPv6 {
+ t.Skip("both IPv4 and IPv6 are required")
+ }
+
+ origTestHookLookupIP := testHookLookupIP
+ defer func() { testHookLookupIP = origTestHookLookupIP }()
+ testHookLookupIP = lookupLocalhost
+ handler := func(dss *dualStackServer, ln Listener) {
+ for {
+ c, err := ln.Accept()
+ if err != nil {
+ return
+ }
+ c.Close()
+ }
+ }
+ dss, err := newDualStackServer([]streamListener{
+ {network: "tcp4", address: "127.0.0.1"},
+ {network: "tcp6", address: "::1"},
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer dss.teardown()
+ if err := dss.buildup(handler); err != nil {
+ t.Fatal(err)
+ }
+
+ before := sw.Sockets()
+ const T = 100 * time.Millisecond
+ const N = 10
+ var wg sync.WaitGroup
+ wg.Add(N)
+ d := &Dialer{DualStack: true, Timeout: T}
+ for i := 0; i < N; i++ {
+ go func() {
+ defer wg.Done()
+ c, err := d.Dial("tcp", JoinHostPort("localhost", dss.port))
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ c.Close()
+ }()
+ }
+ wg.Wait()
+ time.Sleep(2 * T) // wait for the dial racers to stop
+ after := sw.Sockets()
+ if len(after) != len(before) {
+ t.Errorf("got %d; want %d", len(after), len(before))
}
- // All tests using this should be skipped anyway, but:
- panic("numFDs not implemented on " + runtime.GOOS)
}
-func TestDialer(t *testing.T) {
- ln, err := Listen("tcp4", "127.0.0.1:0")
- if err != nil {
- t.Fatalf("Listen failed: %v", err)
- }
- defer ln.Close()
+func TestDialerLocalAddr(t *testing.T) {
ch := make(chan error, 1)
- go func() {
+ handler := func(ls *localServer, ln Listener) {
c, err := ln.Accept()
if err != nil {
- ch <- fmt.Errorf("Accept failed: %v", err)
+ ch <- err
return
}
defer c.Close()
ch <- nil
- }()
-
- laddr, err := ResolveTCPAddr("tcp4", "127.0.0.1:0")
- if err != nil {
- t.Fatalf("ResolveTCPAddr failed: %v", err)
}
- d := &Dialer{LocalAddr: laddr}
- c, err := d.Dial("tcp4", ln.Addr().String())
+ ls, err := newLocalServer("tcp")
if err != nil {
- t.Fatalf("Dial failed: %v", err)
+ t.Fatal(err)
+ }
+ defer ls.teardown()
+ if err := ls.buildup(handler); err != nil {
+ t.Fatal(err)
+ }
+
+ laddr, err := ResolveTCPAddr(ls.Listener.Addr().Network(), ls.Listener.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ laddr.Port = 0
+ d := &Dialer{LocalAddr: laddr}
+ c, err := d.Dial(ls.Listener.Addr().Network(), ls.Addr().String())
+ if err != nil {
+ t.Fatal(err)
}
defer c.Close()
c.Read(make([]byte, 1))
@@ -466,61 +245,15 @@ func TestDialer(t *testing.T) {
}
}
-func TestDialDualStackLocalhost(t *testing.T) {
- switch runtime.GOOS {
- case "nacl":
- t.Skipf("skipping test on %q", runtime.GOOS)
+func TestDialerDualStack(t *testing.T) {
+ if !supportsIPv4 || !supportsIPv6 {
+ t.Skip("both IPv4 and IPv6 are required")
}
- if ips, err := LookupIP("localhost"); err != nil {
- t.Fatalf("LookupIP failed: %v", err)
- } else if len(ips) < 2 || !supportsIPv4 || !supportsIPv6 {
- t.Skip("localhost doesn't have a pair of different address family IP addresses")
- }
-
- touchAndByeServer := func(dss *dualStackServer, ln Listener) {
- for {
- if c, err := ln.Accept(); err != nil {
- return
- } else {
- c.Close()
- }
- }
- }
- dss, err := newDualStackServer([]streamListener{
- {net: "tcp4", addr: "127.0.0.1"},
- {net: "tcp6", addr: "[::1]"},
- })
- if err != nil {
- t.Fatalf("newDualStackServer failed: %v", err)
- }
- defer dss.teardown()
- if err := dss.buildup(touchAndByeServer); err != nil {
- t.Fatalf("dualStackServer.buildup failed: %v", err)
- }
-
- d := &Dialer{DualStack: true}
- for range dss.lns {
- if c, err := d.Dial("tcp", "localhost:"+dss.port); err != nil {
- t.Errorf("Dial failed: %v", err)
- } else {
- if addr := c.LocalAddr().(*TCPAddr); addr.IP.To4() != nil {
- dss.teardownNetwork("tcp4")
- } else if addr.IP.To16() != nil && addr.IP.To4() == nil {
- dss.teardownNetwork("tcp6")
- }
- c.Close()
- }
- }
-}
-
-func TestDialerKeepAlive(t *testing.T) {
- ln := newLocalListener(t)
- defer ln.Close()
- defer func() {
- testHookSetKeepAlive = func() {}
- }()
- go func() {
+ origTestHookLookupIP := testHookLookupIP
+ defer func() { testHookLookupIP = origTestHookLookupIP }()
+ testHookLookupIP = lookupLocalhost
+ handler := func(dss *dualStackServer, ln Listener) {
for {
c, err := ln.Accept()
if err != nil {
@@ -528,7 +261,58 @@ func TestDialerKeepAlive(t *testing.T) {
}
c.Close()
}
- }()
+ }
+ dss, err := newDualStackServer([]streamListener{
+ {network: "tcp4", address: "127.0.0.1"},
+ {network: "tcp6", address: "::1"},
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer dss.teardown()
+ if err := dss.buildup(handler); err != nil {
+ t.Fatal(err)
+ }
+
+ const T = 100 * time.Millisecond
+ d := &Dialer{DualStack: true, Timeout: T}
+ for range dss.lns {
+ c, err := d.Dial("tcp", JoinHostPort("localhost", dss.port))
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ switch addr := c.LocalAddr().(*TCPAddr); {
+ case addr.IP.To4() != nil:
+ dss.teardownNetwork("tcp4")
+ case addr.IP.To16() != nil && addr.IP.To4() == nil:
+ dss.teardownNetwork("tcp6")
+ }
+ c.Close()
+ }
+ time.Sleep(2 * T) // wait for the dial racers to stop
+}
+
+func TestDialerKeepAlive(t *testing.T) {
+ handler := func(ls *localServer, ln Listener) {
+ for {
+ c, err := ln.Accept()
+ if err != nil {
+ return
+ }
+ c.Close()
+ }
+ }
+ ls, err := newLocalServer("tcp")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ls.teardown()
+ if err := ls.buildup(handler); err != nil {
+ t.Fatal(err)
+ }
+ defer func() { testHookSetKeepAlive = func() {} }()
+
for _, keepAlive := range []bool{false, true} {
got := false
testHookSetKeepAlive = func() { got = true }
@@ -536,7 +320,7 @@ func TestDialerKeepAlive(t *testing.T) {
if keepAlive {
d.KeepAlive = 30 * time.Second
}
- c, err := d.Dial("tcp", ln.Addr().String())
+ c, err := d.Dial("tcp", ls.Listener.Addr().String())
if err != nil {
t.Fatal(err)
}
diff --git a/src/net/dialgoogle_test.go b/src/net/dialgoogle_test.go
deleted file mode 100644
index df5895afa7..0000000000
--- a/src/net/dialgoogle_test.go
+++ /dev/null
@@ -1,209 +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 net
-
-import (
- "flag"
- "fmt"
- "io"
- "strings"
- "syscall"
- "testing"
-)
-
-// If an IPv6 tunnel is running, we can try dialing a real IPv6 address.
-var testIPv6 = flag.Bool("ipv6", false, "assume ipv6 tunnel is present")
-
-func TestResolveGoogle(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
- }
-
- for _, network := range []string{"tcp", "tcp4", "tcp6"} {
- addr, err := ResolveTCPAddr(network, "www.google.com:http")
- if err != nil {
- if (network == "tcp" || network == "tcp4") && !supportsIPv4 {
- t.Logf("ipv4 is not supported: %v", err)
- } else if network == "tcp6" && !supportsIPv6 {
- t.Logf("ipv6 is not supported: %v", err)
- } else {
- t.Errorf("ResolveTCPAddr failed: %v", err)
- }
- continue
- }
- if (network == "tcp" || network == "tcp4") && addr.IP.To4() == nil {
- t.Errorf("got %v; expected an IPv4 address on %v", addr, network)
- } else if network == "tcp6" && (addr.IP.To16() == nil || addr.IP.To4() != nil) {
- t.Errorf("got %v; expected an IPv6 address on %v", addr, network)
- }
- }
-}
-
-func TestDialGoogle(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
- }
-
- d := &Dialer{DualStack: true}
- for _, network := range []string{"tcp", "tcp4", "tcp6"} {
- if network == "tcp" && !supportsIPv4 && !supportsIPv6 {
- t.Logf("skipping test; both ipv4 and ipv6 are not supported")
- continue
- } else if network == "tcp4" && !supportsIPv4 {
- t.Logf("skipping test; ipv4 is not supported")
- continue
- } else if network == "tcp6" && !supportsIPv6 {
- t.Logf("skipping test; ipv6 is not supported")
- continue
- } else if network == "tcp6" && !*testIPv6 {
- t.Logf("test disabled; use -ipv6 to enable")
- continue
- }
- if c, err := d.Dial(network, "www.google.com:http"); err != nil {
- t.Errorf("Dial failed: %v", err)
- } else {
- c.Close()
- }
- }
-}
-
-// fd is already connected to the destination, port 80.
-// Run an HTTP request to fetch the appropriate page.
-func fetchGoogle(t *testing.T, fd Conn, network, addr string) {
- req := []byte("GET /robots.txt HTTP/1.0\r\nHost: www.google.com\r\n\r\n")
- n, err := fd.Write(req)
-
- buf := make([]byte, 1000)
- n, err = io.ReadFull(fd, buf)
-
- if n < 1000 {
- t.Errorf("fetchGoogle: short HTTP read from %s %s - %v", network, addr, err)
- return
- }
-}
-
-func doDial(t *testing.T, network, addr string) {
- fd, err := Dial(network, addr)
- if err != nil {
- t.Errorf("Dial(%q, %q, %q) = _, %v", network, "", addr, err)
- return
- }
- fetchGoogle(t, fd, network, addr)
- fd.Close()
-}
-
-var googleaddrsipv4 = []string{
- "%d.%d.%d.%d:80",
- "www.google.com:80",
- "%d.%d.%d.%d:http",
- "www.google.com:http",
- "%03d.%03d.%03d.%03d:0080",
- "[::ffff:%d.%d.%d.%d]:80",
- "[::ffff:%02x%02x:%02x%02x]:80",
- "[0:0:0:0:0000:ffff:%d.%d.%d.%d]:80",
- "[0:0:0:0:000000:ffff:%d.%d.%d.%d]:80",
- "[0:0:0:0::ffff:%d.%d.%d.%d]:80",
-}
-
-func TestDialGoogleIPv4(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
- }
-
- // Insert an actual IPv4 address for google.com
- // into the table.
- addrs, err := LookupIP("www.google.com")
- if err != nil {
- t.Fatalf("lookup www.google.com: %v", err)
- }
- var ip IP
- for _, addr := range addrs {
- if x := addr.To4(); x != nil {
- ip = x
- break
- }
- }
- if ip == nil {
- t.Fatalf("no IPv4 addresses for www.google.com")
- }
-
- for i, s := range googleaddrsipv4 {
- if strings.Contains(s, "%") {
- googleaddrsipv4[i] = fmt.Sprintf(s, ip[0], ip[1], ip[2], ip[3])
- }
- }
-
- for i := 0; i < len(googleaddrsipv4); i++ {
- addr := googleaddrsipv4[i]
- if addr == "" {
- continue
- }
- t.Logf("-- %s --", addr)
- doDial(t, "tcp", addr)
- if addr[0] != '[' {
- doDial(t, "tcp4", addr)
- if supportsIPv6 {
- // make sure syscall.SocketDisableIPv6 flag works.
- syscall.SocketDisableIPv6 = true
- doDial(t, "tcp", addr)
- doDial(t, "tcp4", addr)
- syscall.SocketDisableIPv6 = false
- }
- }
- }
-}
-
-var googleaddrsipv6 = []string{
- "[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:80",
- "ipv6.google.com:80",
- "[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:http",
- "ipv6.google.com:http",
-}
-
-func TestDialGoogleIPv6(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
- }
- // Only run tcp6 if the kernel will take it.
- if !supportsIPv6 {
- t.Skip("skipping test; ipv6 is not supported")
- }
- if !*testIPv6 {
- t.Skip("test disabled; use -ipv6 to enable")
- }
-
- // Insert an actual IPv6 address for ipv6.google.com
- // into the table.
- addrs, err := LookupIP("ipv6.google.com")
- if err != nil {
- t.Fatalf("lookup ipv6.google.com: %v", err)
- }
- var ip IP
- for _, addr := range addrs {
- if x := addr.To16(); x != nil {
- ip = x
- break
- }
- }
- if ip == nil {
- t.Fatalf("no IPv6 addresses for ipv6.google.com")
- }
-
- for i, s := range googleaddrsipv6 {
- if strings.Contains(s, "%") {
- googleaddrsipv6[i] = fmt.Sprintf(s, ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[8], ip[9], ip[10], ip[11], ip[12], ip[13], ip[14], ip[15])
- }
- }
-
- for i := 0; i < len(googleaddrsipv6); i++ {
- addr := googleaddrsipv6[i]
- if addr == "" {
- continue
- }
- t.Logf("-- %s --", addr)
- doDial(t, "tcp", addr)
- doDial(t, "tcp6", addr)
- }
-}
diff --git a/src/net/dnsclient.go b/src/net/dnsclient.go
index 099ea45eba..e5d0ae039b 100644
--- a/src/net/dnsclient.go
+++ b/src/net/dnsclient.go
@@ -9,31 +9,6 @@ import (
"sort"
)
-// DNSError represents a DNS lookup error.
-type DNSError struct {
- Err string // description of the error
- Name string // name looked for
- Server string // server used
- IsTimeout bool
-}
-
-func (e *DNSError) Error() string {
- if e == nil {
- return ""
- }
- s := "lookup " + e.Name
- if e.Server != "" {
- s += " on " + e.Server
- }
- s += ": " + e.Err
- return s
-}
-
-func (e *DNSError) Timeout() bool { return e.IsTimeout }
-func (e *DNSError) Temporary() bool { return e.IsTimeout }
-
-const noSuchHost = "no such host"
-
// reverseaddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP
// address addr suitable for rDNS (PTR) record lookup or an error if it fails
// to parse the IP address.
@@ -43,8 +18,7 @@ func reverseaddr(addr string) (arpa string, err error) {
return "", &DNSError{Err: "unrecognized address", Name: addr}
}
if ip.To4() != nil {
- return uitoa(uint(ip[15])) + "." + uitoa(uint(ip[14])) + "." + uitoa(uint(ip[13])) + "." +
- uitoa(uint(ip[12])) + ".in-addr.arpa.", nil
+ return uitoa(uint(ip[15])) + "." + uitoa(uint(ip[14])) + "." + uitoa(uint(ip[13])) + "." + uitoa(uint(ip[12])) + ".in-addr.arpa.", nil
}
// Must be IPv6
buf := make([]byte, 0, len(ip)*4+len("ip6.arpa."))
@@ -67,7 +41,7 @@ func answer(name, server string, dns *dnsMsg, qtype uint16) (cname string, addrs
addrs = make([]dnsRR, 0, len(dns.answer))
if dns.rcode == dnsRcodeNameError && dns.recursion_available {
- return "", nil, &DNSError{Err: noSuchHost, Name: name}
+ return "", nil, &DNSError{Err: errNoSuchHost.Error(), Name: name}
}
if dns.rcode != dnsRcodeSuccess {
// None of the error codes make sense
@@ -106,7 +80,7 @@ Cname:
}
}
if len(addrs) == 0 {
- return "", nil, &DNSError{Err: noSuchHost, Name: name, Server: server}
+ return "", nil, &DNSError{Err: errNoSuchHost.Error(), Name: name, Server: server}
}
return name, addrs, nil
}
@@ -194,13 +168,10 @@ type SRV struct {
type byPriorityWeight []*SRV
func (s byPriorityWeight) Len() int { return len(s) }
-
-func (s byPriorityWeight) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-
func (s byPriorityWeight) Less(i, j int) bool {
- return s[i].Priority < s[j].Priority ||
- (s[i].Priority == s[j].Priority && s[i].Weight < s[j].Weight)
+ return s[i].Priority < s[j].Priority || (s[i].Priority == s[j].Priority && s[i].Weight < s[j].Weight)
}
+func (s byPriorityWeight) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// shuffleByWeight shuffles SRV records by weight using the algorithm
// described in RFC 2782.
@@ -248,11 +219,9 @@ type MX struct {
// byPref implements sort.Interface to sort MX records by preference
type byPref []*MX
-func (s byPref) Len() int { return len(s) }
-
+func (s byPref) Len() int { return len(s) }
func (s byPref) Less(i, j int) bool { return s[i].Pref < s[j].Pref }
-
-func (s byPref) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s byPref) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// sort reorders MX records as specified in RFC 5321.
func (s byPref) sort() {
diff --git a/src/net/dnsclient_test.go b/src/net/dnsclient_test.go
index 435eb35506..3ab2b836ef 100644
--- a/src/net/dnsclient_test.go
+++ b/src/net/dnsclient_test.go
@@ -47,7 +47,7 @@ func testUniformity(t *testing.T, size int, margin float64) {
checkDistribution(t, data, margin)
}
-func TestUniformity(t *testing.T) {
+func TestDNSSRVUniformity(t *testing.T) {
testUniformity(t, 2, 0.05)
testUniformity(t, 3, 0.10)
testUniformity(t, 10, 0.20)
diff --git a/src/net/dnsclient_unix.go b/src/net/dnsclient_unix.go
index 30c7ada5ba..5a4411f5c7 100644
--- a/src/net/dnsclient_unix.go
+++ b/src/net/dnsclient_unix.go
@@ -20,6 +20,7 @@ import (
"io"
"math/rand"
"os"
+ "strconv"
"sync"
"time"
)
@@ -185,7 +186,7 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (string, []dnsRR, err
continue
}
cname, addrs, err := answer(name, server, msg, qtype)
- if err == nil || err.(*DNSError).Err == noSuchHost {
+ if err == nil || err.(*DNSError).Err == errNoSuchHost.Error() {
return cname, addrs, err
}
lastErr = err
@@ -215,10 +216,10 @@ func convertRR_AAAA(records []dnsRR) []IP {
var cfg struct {
ch chan struct{}
- mu sync.RWMutex // protects dnsConfig and dnserr
+ mu sync.RWMutex // protects dnsConfig
dnsConfig *dnsConfig
- dnserr error
}
+
var onceLoadConfig sync.Once
// Assume dns config file is /etc/resolv.conf here
@@ -229,12 +230,12 @@ func loadDefaultConfig() {
func loadConfig(resolvConfPath string, reloadTime time.Duration, quit <-chan chan struct{}) {
var mtime time.Time
cfg.ch = make(chan struct{}, 1)
- if fi, err := os.Stat(resolvConfPath); err != nil {
- cfg.dnserr = err
- } else {
+ if fi, err := os.Stat(resolvConfPath); err == nil {
mtime = fi.ModTime()
- cfg.dnsConfig, cfg.dnserr = dnsReadConfig(resolvConfPath)
}
+
+ cfg.dnsConfig = dnsReadConfig(resolvConfPath)
+
go func() {
for {
time.Sleep(reloadTime)
@@ -257,19 +258,16 @@ func loadConfig(resolvConfPath string, reloadTime time.Duration, quit <-chan cha
}
mtime = m
// In case of error, we keep the previous config
- ncfg, err := dnsReadConfig(resolvConfPath)
- if err != nil || len(ncfg.servers) == 0 {
- continue
+ if ncfg := dnsReadConfig(resolvConfPath); ncfg.err == nil {
+ cfg.mu.Lock()
+ cfg.dnsConfig = ncfg
+ cfg.mu.Unlock()
}
- cfg.mu.Lock()
- cfg.dnsConfig = ncfg
- cfg.dnserr = nil
- cfg.mu.Unlock()
}
}()
}
-func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error) {
+func lookup(name string, qtype uint16) (cname string, rrs []dnsRR, err error) {
if !isDomainName(name) {
return name, nil, &DNSError{Err: "invalid domain name", Name: name}
}
@@ -283,10 +281,6 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error)
cfg.mu.RLock()
defer cfg.mu.RUnlock()
- if cfg.dnserr != nil || cfg.dnsConfig == nil {
- err = cfg.dnserr
- return
- }
// If name is rooted (trailing dot) or has enough dots,
// try it by itself first.
rooted := len(name) > 0 && name[len(name)-1] == '.'
@@ -296,7 +290,7 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error)
rname += "."
}
// Can try as ordinary name.
- cname, addrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
+ cname, rrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
if rooted || err == nil {
return
}
@@ -308,7 +302,7 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error)
if rname[len(rname)-1] != '.' {
rname += "."
}
- cname, addrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
+ cname, rrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
if err == nil {
return
}
@@ -317,7 +311,7 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error)
// Last ditch effort: try unsuffixed only if we haven't already,
// that is, name is not rooted and has less than ndots dots.
if count(name, '.') < cfg.dnsConfig.ndots {
- cname, addrs, err = tryOneName(cfg.dnsConfig, name+".", qtype)
+ cname, rrs, err = tryOneName(cfg.dnsConfig, name+".", qtype)
if err == nil {
return
}
@@ -332,6 +326,35 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error)
return
}
+// hostLookupOrder specifies the order of LookupHost lookup strategies.
+// It is basically a simplified representation of nsswitch.conf.
+// "files" means /etc/hosts.
+type hostLookupOrder int
+
+const (
+ // hostLookupCgo means defer to cgo.
+ hostLookupCgo hostLookupOrder = iota
+ hostLookupFilesDNS // files first
+ hostLookupDNSFiles // dns first
+ hostLookupFiles // only files
+ hostLookupDNS // only DNS
+)
+
+var lookupOrderName = map[hostLookupOrder]string{
+ hostLookupCgo: "cgo",
+ hostLookupFilesDNS: "files,dns",
+ hostLookupDNSFiles: "dns,files",
+ hostLookupFiles: "files",
+ hostLookupDNS: "dns",
+}
+
+func (o hostLookupOrder) String() string {
+ if s, ok := lookupOrderName[o]; ok {
+ return s
+ }
+ return "hostLookupOrder=" + strconv.Itoa(int(o)) + "??"
+}
+
// goLookupHost is the native Go implementation of LookupHost.
// Used only if cgoLookupHost refuses to handle the request
// (that is, only if cgoLookupHost is the stub in cgo_stub.go).
@@ -339,12 +362,18 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error)
// depending on our lookup code, so that Go and C get the same
// answers.
func goLookupHost(name string) (addrs []string, err error) {
- // Use entries from /etc/hosts if they match.
- addrs = lookupStaticHost(name)
- if len(addrs) > 0 {
- return
+ return goLookupHostOrder(name, hostLookupFilesDNS)
+}
+
+func goLookupHostOrder(name string, order hostLookupOrder) (addrs []string, err error) {
+ if order == hostLookupFilesDNS || order == hostLookupFiles {
+ // Use entries from /etc/hosts if they match.
+ addrs = lookupStaticHost(name)
+ if len(addrs) > 0 || order == hostLookupFiles {
+ return
+ }
}
- ips, err := goLookupIP(name)
+ ips, err := goLookupIPOrder(name, order)
if err != nil {
return
}
@@ -355,25 +384,30 @@ func goLookupHost(name string) (addrs []string, err error) {
return
}
+// lookup entries from /etc/hosts
+func goLookupIPFiles(name string) (addrs []IPAddr) {
+ for _, haddr := range lookupStaticHost(name) {
+ haddr, zone := splitHostZone(haddr)
+ if ip := ParseIP(haddr); ip != nil {
+ addr := IPAddr{IP: ip, Zone: zone}
+ addrs = append(addrs, addr)
+ }
+ }
+ return
+}
+
// goLookupIP is the native Go implementation of LookupIP.
// Used only if cgoLookupIP refuses to handle the request
// (that is, only if cgoLookupIP is the stub in cgo_stub.go).
-// Normally we let cgo use the C library resolver instead of
-// depending on our lookup code, so that Go and C get the same
-// answers.
func goLookupIP(name string) (addrs []IPAddr, err error) {
- // Use entries from /etc/hosts if possible.
- haddrs := lookupStaticHost(name)
- if len(haddrs) > 0 {
- for _, haddr := range haddrs {
- haddr, zone := splitHostZone(haddr)
- if ip := ParseIP(haddr); ip != nil {
- addr := IPAddr{IP: ip, Zone: zone}
- addrs = append(addrs, addr)
- }
- }
- if len(addrs) > 0 {
- return
+ return goLookupIPOrder(name, hostLookupFilesDNS)
+}
+
+func goLookupIPOrder(name string, order hostLookupOrder) (addrs []IPAddr, err error) {
+ if order == hostLookupFilesDNS || order == hostLookupFiles {
+ addrs = goLookupIPFiles(name)
+ if len(addrs) > 0 || order == hostLookupFiles {
+ return addrs, nil
}
}
type racer struct {
@@ -409,8 +443,13 @@ func goLookupIP(name string) (addrs []IPAddr, err error) {
}
}
}
- if len(addrs) == 0 && lastErr != nil {
- return nil, lastErr
+ if len(addrs) == 0 {
+ if lastErr != nil {
+ return nil, lastErr
+ }
+ if order == hostLookupDNSFiles {
+ addrs = goLookupIPFiles(name)
+ }
}
return addrs, nil
}
diff --git a/src/net/dnsclient_unix_test.go b/src/net/dnsclient_unix_test.go
index 2934634769..1b88e7762b 100644
--- a/src/net/dnsclient_unix_test.go
+++ b/src/net/dnsclient_unix_test.go
@@ -31,7 +31,7 @@ var dnsTransportFallbackTests = []struct {
func TestDNSTransportFallback(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
+ t.Skip("avoid external network")
}
for _, tt := range dnsTransportFallbackTests {
@@ -73,7 +73,7 @@ var specialDomainNameTests = []struct {
func TestSpecialDomainName(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
+ t.Skip("avoid external network")
}
server := "8.8.8.8:53"
@@ -101,9 +101,9 @@ type resolvConfTest struct {
}
func newResolvConfTest(t *testing.T) *resolvConfTest {
- dir, err := ioutil.TempDir("", "resolvConfTest")
+ dir, err := ioutil.TempDir("", "go-resolvconftest")
if err != nil {
- t.Fatalf("could not create temp dir: %v", err)
+ t.Fatal(err)
}
// Disable the default loadConfig
@@ -150,7 +150,7 @@ func (r *resolvConfTest) WantServers(want []string) {
cfg.mu.RLock()
defer cfg.mu.RUnlock()
if got := cfg.dnsConfig.servers; !reflect.DeepEqual(got, want) {
- r.Fatalf("Unexpected dns server loaded, got %v want %v", got, want)
+ r.Fatalf("unexpected dns server loaded, got %v want %v", got, want)
}
}
@@ -165,52 +165,60 @@ func (r *resolvConfTest) Close() {
func TestReloadResolvConfFail(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
+ t.Skip("avoid external network")
}
r := newResolvConfTest(t)
defer r.Close()
- // resolv.conf.tmp does not exist yet
r.Start()
- if _, err := goLookupIP("golang.org"); err == nil {
- t.Fatal("goLookupIP(missing) succeeded")
- }
-
r.SetConf("nameserver 8.8.8.8")
+
if _, err := goLookupIP("golang.org"); err != nil {
- t.Fatalf("goLookupIP(missing; good) failed: %v", err)
+ t.Fatal(err)
}
- // Using a bad resolv.conf while we had a good
- // one before should not update the config
+ // Using an empty resolv.conf should use localhost as servers
r.SetConf("")
- if _, err := goLookupIP("golang.org"); err != nil {
- t.Fatalf("goLookupIP(missing; good; bad) failed: %v", err)
+
+ if len(cfg.dnsConfig.servers) != len(defaultNS) {
+ t.Fatalf("goLookupIP(missing; good; bad) failed: servers=%v, want: %v", cfg.dnsConfig.servers, defaultNS)
+ }
+
+ for i := range cfg.dnsConfig.servers {
+ if cfg.dnsConfig.servers[i] != defaultNS[i] {
+ t.Fatalf("goLookupIP(missing; good; bad) failed: servers=%v, want: %v", cfg.dnsConfig.servers, defaultNS)
+ }
}
}
func TestReloadResolvConfChange(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
+ t.Skip("avoid external network")
}
r := newResolvConfTest(t)
defer r.Close()
- r.SetConf("nameserver 8.8.8.8")
r.Start()
+ r.SetConf("nameserver 8.8.8.8")
if _, err := goLookupIP("golang.org"); err != nil {
- t.Fatalf("goLookupIP(good) failed: %v", err)
+ t.Fatal(err)
}
r.WantServers([]string{"8.8.8.8"})
- // Using a bad resolv.conf when we had a good one
- // before should not update the config
+ // Using an empty resolv.conf should use localhost as servers
r.SetConf("")
- if _, err := goLookupIP("golang.org"); err != nil {
- t.Fatalf("goLookupIP(good; bad) failed: %v", err)
+
+ if len(cfg.dnsConfig.servers) != len(defaultNS) {
+ t.Fatalf("goLookupIP(missing; good; bad) failed: servers=%v, want: %v", cfg.dnsConfig.servers, defaultNS)
+ }
+
+ for i := range cfg.dnsConfig.servers {
+ if cfg.dnsConfig.servers[i] != defaultNS[i] {
+ t.Fatalf("goLookupIP(missing; good; bad) failed: servers=%v, want: %v", cfg.dnsConfig.servers, defaultNS)
+ }
}
// A new good config should get picked up
@@ -219,8 +227,7 @@ func TestReloadResolvConfChange(t *testing.T) {
}
func BenchmarkGoLookupIP(b *testing.B) {
- uninstallTestHooks()
- defer installTestHooks()
+ testHookUninstaller.Do(func() { uninstallTestHooks() })
for i := 0; i < b.N; i++ {
goLookupIP("www.example.com")
@@ -228,8 +235,7 @@ func BenchmarkGoLookupIP(b *testing.B) {
}
func BenchmarkGoLookupIPNoSuchHost(b *testing.B) {
- uninstallTestHooks()
- defer installTestHooks()
+ testHookUninstaller.Do(func() { uninstallTestHooks() })
for i := 0; i < b.N; i++ {
goLookupIP("some.nonexistent")
@@ -237,13 +243,10 @@ func BenchmarkGoLookupIPNoSuchHost(b *testing.B) {
}
func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) {
- uninstallTestHooks()
- defer installTestHooks()
+ testHookUninstaller.Do(func() { uninstallTestHooks() })
onceLoadConfig.Do(loadDefaultConfig)
- if cfg.dnserr != nil || cfg.dnsConfig == nil {
- b.Fatalf("loadConfig failed: %v", cfg.dnserr)
- }
+
// This looks ugly but it's safe as long as benchmarks are run
// sequentially in package testing.
orig := cfg.dnsConfig
diff --git a/src/net/dnsconfig_unix.go b/src/net/dnsconfig_unix.go
index 66ab7c4dd3..6073fdb6d8 100644
--- a/src/net/dnsconfig_unix.go
+++ b/src/net/dnsconfig_unix.go
@@ -8,30 +8,41 @@
package net
+var defaultNS = []string{"127.0.0.1", "::1"}
+
type dnsConfig struct {
- servers []string // servers to use
- search []string // suffixes to append to local name
- ndots int // number of dots in name to trigger absolute lookup
- timeout int // seconds before giving up on packet
- attempts int // lost packets before giving up on server
- rotate bool // round robin among servers
+ servers []string // servers to use
+ search []string // suffixes to append to local name
+ ndots int // number of dots in name to trigger absolute lookup
+ timeout int // seconds before giving up on packet
+ attempts int // lost packets before giving up on server
+ rotate bool // round robin among servers
+ unknownOpt bool // anything unknown was encountered
+ lookup []string // OpenBSD top-level database "lookup" order
+ err error // any error that occurs during open of resolv.conf
}
// See resolv.conf(5) on a Linux machine.
// TODO(rsc): Supposed to call uname() and chop the beginning
// of the host name to get the default search domain.
-func dnsReadConfig(filename string) (*dnsConfig, error) {
- file, err := open(filename)
- if err != nil {
- return nil, &DNSConfigError{err}
- }
- defer file.close()
+func dnsReadConfig(filename string) *dnsConfig {
conf := &dnsConfig{
ndots: 1,
timeout: 5,
attempts: 2,
}
+ file, err := open(filename)
+ if err != nil {
+ conf.servers = defaultNS
+ conf.err = err
+ return conf
+ }
+ defer file.close()
for line, ok := file.readLine(); ok; line, ok = file.readLine() {
+ if len(line) > 0 && (line[0] == ';' || line[0] == '#') {
+ // comment.
+ continue
+ }
f := getFields(line)
if len(f) < 1 {
continue
@@ -61,8 +72,7 @@ func dnsReadConfig(filename string) (*dnsConfig, error) {
}
case "options": // magic options
- for i := 1; i < len(f); i++ {
- s := f[i]
+ for _, s := range f[1:] {
switch {
case hasPrefix(s, "ndots:"):
n, _, _ := dtoi(s, 6)
@@ -84,11 +94,25 @@ func dnsReadConfig(filename string) (*dnsConfig, error) {
conf.attempts = n
case s == "rotate":
conf.rotate = true
+ default:
+ conf.unknownOpt = true
}
}
+
+ case "lookup":
+ // OpenBSD option:
+ // http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man5/resolv.conf.5
+ // "the legal space-separated values are: bind, file, yp"
+ conf.lookup = f[1:]
+
+ default:
+ conf.unknownOpt = true
}
}
- return conf, nil
+ if len(conf.servers) == 0 {
+ conf.servers = defaultNS
+ }
+ return conf
}
func hasPrefix(s, prefix string) bool {
diff --git a/src/net/dnsconfig_unix_test.go b/src/net/dnsconfig_unix_test.go
index 94fb0c32e2..c8eed61890 100644
--- a/src/net/dnsconfig_unix_test.go
+++ b/src/net/dnsconfig_unix_test.go
@@ -7,28 +7,30 @@
package net
import (
+ "os"
"reflect"
"testing"
)
var dnsReadConfigTests = []struct {
name string
- conf dnsConfig
+ want *dnsConfig
}{
{
name: "testdata/resolv.conf",
- conf: dnsConfig{
- servers: []string{"8.8.8.8", "2001:4860:4860::8888", "fe80::1%lo0"},
- search: []string{"localdomain"},
- ndots: 5,
- timeout: 10,
- attempts: 3,
- rotate: true,
+ want: &dnsConfig{
+ servers: []string{"8.8.8.8", "2001:4860:4860::8888", "fe80::1%lo0"},
+ search: []string{"localdomain"},
+ ndots: 5,
+ timeout: 10,
+ attempts: 3,
+ rotate: true,
+ unknownOpt: true, // the "options attempts 3" line
},
},
{
name: "testdata/domain-resolv.conf",
- conf: dnsConfig{
+ want: &dnsConfig{
servers: []string{"8.8.8.8"},
search: []string{"localdomain"},
ndots: 1,
@@ -38,7 +40,7 @@ var dnsReadConfigTests = []struct {
},
{
name: "testdata/search-resolv.conf",
- conf: dnsConfig{
+ want: &dnsConfig{
servers: []string{"8.8.8.8"},
search: []string{"test", "invalid"},
ndots: 1,
@@ -48,22 +50,51 @@ var dnsReadConfigTests = []struct {
},
{
name: "testdata/empty-resolv.conf",
- conf: dnsConfig{
+ want: &dnsConfig{
+ servers: defaultNS,
ndots: 1,
timeout: 5,
attempts: 2,
},
},
+ {
+ name: "testdata/openbsd-resolv.conf",
+ want: &dnsConfig{
+ ndots: 1,
+ timeout: 5,
+ attempts: 2,
+ lookup: []string{"file", "bind"},
+ servers: []string{"169.254.169.254", "10.240.0.1"},
+ search: []string{"c.symbolic-datum-552.internal."},
+ },
+ },
}
func TestDNSReadConfig(t *testing.T) {
for _, tt := range dnsReadConfigTests {
- conf, err := dnsReadConfig(tt.name)
- if err != nil {
- t.Fatal(err)
+ conf := dnsReadConfig(tt.name)
+ if conf.err != nil {
+ t.Fatal(conf.err)
}
- if !reflect.DeepEqual(conf, &tt.conf) {
- t.Errorf("got %v; want %v", conf, &tt.conf)
+ if !reflect.DeepEqual(conf, tt.want) {
+ t.Errorf("%s:\ngot: %+v\nwant: %+v", tt.name, conf, tt.want)
}
}
}
+
+func TestDNSReadMissingFile(t *testing.T) {
+ conf := dnsReadConfig("a-nonexistent-file")
+ if !os.IsNotExist(conf.err) {
+ t.Errorf("missing resolv.conf:\ngot: %v\nwant: %v", conf.err, os.ErrNotExist)
+ }
+ conf.err = nil
+ want := &dnsConfig{
+ servers: defaultNS,
+ ndots: 1,
+ timeout: 5,
+ attempts: 2,
+ }
+ if !reflect.DeepEqual(conf, want) {
+ t.Errorf("missing resolv.conf:\ngot: %+v\nwant: %+v", conf, want)
+ }
+}
diff --git a/src/net/dnsmsg.go b/src/net/dnsmsg.go
index 161afb2a55..6ecaa94823 100644
--- a/src/net/dnsmsg.go
+++ b/src/net/dnsmsg.go
@@ -306,7 +306,23 @@ func (rr *dnsRR_TXT) Header() *dnsRR_Header {
}
func (rr *dnsRR_TXT) Walk(f func(v interface{}, name, tag string) bool) bool {
- return rr.Hdr.Walk(f) && f(&rr.Txt, "Txt", "")
+ if !rr.Hdr.Walk(f) {
+ return false
+ }
+ var n uint16 = 0
+ for n < rr.Hdr.Rdlength {
+ var txt string
+ if !f(&txt, "Txt", "") {
+ return false
+ }
+ // more bytes than rr.Hdr.Rdlength said there woudld be
+ if rr.Hdr.Rdlength-n < uint16(len(txt))+1 {
+ return false
+ }
+ n += uint16(len(txt)) + 1
+ rr.Txt += txt
+ }
+ return true
}
type dnsRR_SRV struct {
diff --git a/src/net/dnsmsg_test.go b/src/net/dnsmsg_test.go
index 159a03e525..1078d77ceb 100644
--- a/src/net/dnsmsg_test.go
+++ b/src/net/dnsmsg_test.go
@@ -66,7 +66,7 @@ func TestDNSParseCorruptSRVReply(t *testing.T) {
msg := new(dnsMsg)
ok := msg.Unpack(data)
if !ok {
- t.Fatalf("unpacking packet failed")
+ t.Fatal("unpacking packet failed")
}
msg.String() // exercise this code path
if g, e := len(msg.answer), 5; g != e {
@@ -96,6 +96,93 @@ func TestDNSParseCorruptSRVReply(t *testing.T) {
}
}
+func TestDNSParseTXTReply(t *testing.T) {
+ expectedTxt1 := "v=spf1 redirect=_spf.google.com"
+ expectedTxt2 := "v=spf1 ip4:69.63.179.25 ip4:69.63.178.128/25 ip4:69.63.184.0/25 " +
+ "ip4:66.220.144.128/25 ip4:66.220.155.0/24 " +
+ "ip4:69.171.232.0/25 ip4:66.220.157.0/25 " +
+ "ip4:69.171.244.0/24 mx -all"
+
+ replies := []string{dnsTXTReply1, dnsTXTReply2}
+ expectedTxts := []string{expectedTxt1, expectedTxt2}
+
+ for i := range replies {
+ data, err := hex.DecodeString(replies[i])
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ msg := new(dnsMsg)
+ ok := msg.Unpack(data)
+ if !ok {
+ t.Errorf("test %d: unpacking packet failed", i)
+ continue
+ }
+
+ if len(msg.answer) != 1 {
+ t.Errorf("test %d: len(rr.answer) = %d; want 1", i, len(msg.answer))
+ continue
+ }
+
+ rr := msg.answer[0]
+ rrTXT, ok := rr.(*dnsRR_TXT)
+ if !ok {
+ t.Errorf("test %d: answer[0] = %T; want *dnsRR_TXT", i, rr)
+ continue
+ }
+
+ if rrTXT.Txt != expectedTxts[i] {
+ t.Errorf("test %d: Txt = %s; want %s", i, rrTXT.Txt, expectedTxts[i])
+ }
+ }
+}
+
+func TestDNSParseTXTCorruptDataLengthReply(t *testing.T) {
+ replies := []string{dnsTXTCorruptDataLengthReply1, dnsTXTCorruptDataLengthReply2}
+
+ for i := range replies {
+ data, err := hex.DecodeString(replies[i])
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ msg := new(dnsMsg)
+ ok := msg.Unpack(data)
+ if ok {
+ t.Errorf("test %d: expected to fail on unpacking corrupt packet", i)
+ }
+ }
+}
+
+func TestDNSParseTXTCorruptTXTLengthReply(t *testing.T) {
+ replies := []string{dnsTXTCorruptTXTLengthReply1, dnsTXTCorruptTXTLengthReply2}
+
+ for i := range replies {
+ data, err := hex.DecodeString(replies[i])
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ msg := new(dnsMsg)
+ ok := msg.Unpack(data)
+ // Unpacking should succeed, but we should just get the header.
+ if !ok {
+ t.Errorf("test %d: unpacking packet failed", i)
+ continue
+ }
+
+ if len(msg.answer) != 1 {
+ t.Errorf("test %d: len(rr.answer) = %d; want 1", i, len(msg.answer))
+ continue
+ }
+
+ rr := msg.answer[0]
+ if _, justHeader := rr.(*dnsRR_Header); !justHeader {
+ t.Errorf("test %d: rr = %T; expected *dnsRR_Header", i, rr)
+ }
+ }
+}
+
// Valid DNS SRV reply
const dnsSRVReply = "0901818000010005000000000c5f786d70702d736572766572045f74637006676f6f67" +
"6c6503636f6d0000210001c00c002100010000012c00210014000014950c786d70702d" +
@@ -117,3 +204,63 @@ const dnsSRVCorruptReply = "0901818000010005000000000c5f786d70702d73657276657204
"6503636f6d00c00c002100010000012c00200005000014950b786d70702d7365727665" +
"72016c06676f6f676c6503636f6d00c00c002100010000012c00FF0014000014950c78" +
"6d70702d73657276657231016c06676f6f676c6503636f6d00"
+
+// TXT reply with one
+const dnsTXTReply1 = "b3458180000100010004000505676d61696c03636f6d0000100001c00c001000010000012c00" +
+ "201f763d737066312072656469726563743d5f7370662e676f6f676c652e636f6dc00" +
+ "c0002000100025d4c000d036e733406676f6f676c65c012c00c0002000100025d4c00" +
+ "06036e7331c057c00c0002000100025d4c0006036e7333c057c00c0002000100025d4" +
+ "c0006036e7332c057c06c00010001000248b50004d8ef200ac09000010001000248b5" +
+ "0004d8ef220ac07e00010001000248b50004d8ef240ac05300010001000248b50004d" +
+ "8ef260a0000291000000000000000"
+
+// TXT reply with more than one .
+// See https://tools.ietf.org/html/rfc1035#section-3.3.14
+const dnsTXTReply2 = "a0a381800001000100020002045f7370660866616365626f6f6b03636f6d0000100001c00c0010000" +
+ "100000e1000af7f763d73706631206970343a36392e36332e3137392e3235206970343a36392e" +
+ "36332e3137382e3132382f3235206970343a36392e36332e3138342e302f3235206970343a363" +
+ "62e3232302e3134342e3132382f3235206970343a36362e3232302e3135352e302f3234206970" +
+ "343a36392e3137312e3233322e302f323520692e70343a36362e3232302e3135372e302f32352" +
+ "06970343a36392e3137312e3234342e302f3234206d78202d616c6cc0110002000100025d1500" +
+ "070161026e73c011c0110002000100025d1500040162c0ecc0ea0001000100025d15000445abe" +
+ "f0cc0fd0001000100025d15000445abff0c"
+
+// DataLength field should be sum of all TXT fields. In this case it's less.
+const dnsTXTCorruptDataLengthReply1 = "a0a381800001000100020002045f7370660866616365626f6f6b03636f6d0000100001c00c0010000" +
+ "100000e1000967f763d73706631206970343a36392e36332e3137392e3235206970343a36392e" +
+ "36332e3137382e3132382f3235206970343a36392e36332e3138342e302f3235206970343a363" +
+ "62e3232302e3134342e3132382f3235206970343a36362e3232302e3135352e302f3234206970" +
+ "343a36392e3137312e3233322e302f323520692e70343a36362e3232302e3135372e302f32352" +
+ "06970343a36392e3137312e3234342e302f3234206d78202d616c6cc0110002000100025d1500" +
+ "070161026e73c011c0110002000100025d1500040162c0ecc0ea0001000100025d15000445abe" +
+ "f0cc0fd0001000100025d15000445abff0c"
+
+// Same as above but DataLength is more than sum of TXT fields.
+const dnsTXTCorruptDataLengthReply2 = "a0a381800001000100020002045f7370660866616365626f6f6b03636f6d0000100001c00c0010000" +
+ "100000e1001227f763d73706631206970343a36392e36332e3137392e3235206970343a36392e" +
+ "36332e3137382e3132382f3235206970343a36392e36332e3138342e302f3235206970343a363" +
+ "62e3232302e3134342e3132382f3235206970343a36362e3232302e3135352e302f3234206970" +
+ "343a36392e3137312e3233322e302f323520692e70343a36362e3232302e3135372e302f32352" +
+ "06970343a36392e3137312e3234342e302f3234206d78202d616c6cc0110002000100025d1500" +
+ "070161026e73c011c0110002000100025d1500040162c0ecc0ea0001000100025d15000445abe" +
+ "f0cc0fd0001000100025d15000445abff0c"
+
+// TXT Length field is less than actual length.
+const dnsTXTCorruptTXTLengthReply1 = "a0a381800001000100020002045f7370660866616365626f6f6b03636f6d0000100001c00c0010000" +
+ "100000e1000af7f763d73706631206970343a36392e36332e3137392e3235206970343a36392e" +
+ "36332e3137382e3132382f3235206970343a36392e36332e3138342e302f3235206970343a363" +
+ "62e3232302e3134342e3132382f3235206970343a36362e3232302e3135352e302f3234206970" +
+ "343a36392e3137312e3233322e302f323520691470343a36362e3232302e3135372e302f32352" +
+ "06970343a36392e3137312e3234342e302f3234206d78202d616c6cc0110002000100025d1500" +
+ "070161026e73c011c0110002000100025d1500040162c0ecc0ea0001000100025d15000445abe" +
+ "f0cc0fd0001000100025d15000445abff0c"
+
+// TXT Length field is more than actual length.
+const dnsTXTCorruptTXTLengthReply2 = "a0a381800001000100020002045f7370660866616365626f6f6b03636f6d0000100001c00c0010000" +
+ "100000e1000af7f763d73706631206970343a36392e36332e3137392e3235206970343a36392e" +
+ "36332e3137382e3132382f3235206970343a36392e36332e3138342e302f3235206970343a363" +
+ "62e3232302e3134342e3132382f3235206970343a36362e3232302e3135352e302f3234206970" +
+ "343a36392e3137312e3233322e302f323520693370343a36362e3232302e3135372e302f32352" +
+ "06970343a36392e3137312e3234342e302f3234206d78202d616c6cc0110002000100025d1500" +
+ "070161026e73c011c0110002000100025d1500040162c0ecc0ea0001000100025d15000445abe" +
+ "f0cc0fd0001000100025d15000445abff0c"
diff --git a/src/net/dnsname_test.go b/src/net/dnsname_test.go
index 4fecf8dbe2..cc660c9d42 100644
--- a/src/net/dnsname_test.go
+++ b/src/net/dnsname_test.go
@@ -9,12 +9,12 @@ import (
"testing"
)
-type testCase struct {
+type dnsNameTest struct {
name string
result bool
}
-var tests = []testCase{
+var dnsNameTests = []dnsNameTest{
// RFC2181, section 11.
{"_xmpp-server._tcp.google.com", true},
{"foo.com", true},
@@ -30,7 +30,7 @@ var tests = []testCase{
{"b.com.", true},
}
-func getTestCases(ch chan<- testCase) {
+func emitDNSNameTest(ch chan<- dnsNameTest) {
defer close(ch)
var char59 = ""
var char63 = ""
@@ -41,38 +41,36 @@ func getTestCases(ch chan<- testCase) {
char63 = char59 + "aaaa"
char64 = char63 + "a"
- for _, tc := range tests {
+ for _, tc := range dnsNameTests {
ch <- tc
}
- ch <- testCase{char63 + ".com", true}
- ch <- testCase{char64 + ".com", false}
+ ch <- dnsNameTest{char63 + ".com", true}
+ ch <- dnsNameTest{char64 + ".com", false}
// 255 char name is fine:
- ch <- testCase{char59 + "." + char63 + "." + char63 + "." +
+ ch <- dnsNameTest{char59 + "." + char63 + "." + char63 + "." +
char63 + ".com",
true}
// 256 char name is bad:
- ch <- testCase{char59 + "a." + char63 + "." + char63 + "." +
+ ch <- dnsNameTest{char59 + "a." + char63 + "." + char63 + "." +
char63 + ".com",
false}
}
-func TestDNSNames(t *testing.T) {
- ch := make(chan testCase)
- go getTestCases(ch)
+func TestDNSName(t *testing.T) {
+ ch := make(chan dnsNameTest)
+ go emitDNSNameTest(ch)
for tc := range ch {
if isDomainName(tc.name) != tc.result {
- t.Errorf("isDomainName(%v) failed: Should be %v",
- tc.name, tc.result)
+ t.Errorf("isDomainName(%q) = %v; want %v", tc.name, !tc.result, tc.result)
}
}
}
-func BenchmarkDNSNames(b *testing.B) {
- uninstallTestHooks()
- defer installTestHooks()
+func BenchmarkDNSName(b *testing.B) {
+ testHookUninstaller.Do(func() { uninstallTestHooks() })
- benchmarks := append(tests, []testCase{
+ benchmarks := append(dnsNameTests, []dnsNameTest{
{strings.Repeat("a", 63), true},
{strings.Repeat("a", 64), false},
}...)
diff --git a/src/net/error_plan9_test.go b/src/net/error_plan9_test.go
new file mode 100644
index 0000000000..495ea96534
--- /dev/null
+++ b/src/net/error_plan9_test.go
@@ -0,0 +1,17 @@
+// Copyright 2015 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 net
+
+import "syscall"
+
+var (
+ errTimedout = syscall.ETIMEDOUT
+ errOpNotSupported = syscall.EPLAN9
+)
+
+func isPlatformError(err error) bool {
+ _, ok := err.(syscall.ErrorString)
+ return ok
+}
diff --git a/src/net/error_posix_test.go b/src/net/error_posix_test.go
new file mode 100644
index 0000000000..a642e29227
--- /dev/null
+++ b/src/net/error_posix_test.go
@@ -0,0 +1,19 @@
+// Copyright 2015 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 !plan9
+
+package net
+
+import "syscall"
+
+var (
+ errTimedout = syscall.ETIMEDOUT
+ errOpNotSupported = syscall.EOPNOTSUPP
+)
+
+func isPlatformError(err error) bool {
+ _, ok := err.(syscall.Errno)
+ return ok
+}
diff --git a/src/net/error_test.go b/src/net/error_test.go
new file mode 100644
index 0000000000..c65d3f9d8a
--- /dev/null
+++ b/src/net/error_test.go
@@ -0,0 +1,587 @@
+// Copyright 2015 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 net
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/internal/socktest"
+ "os"
+ "runtime"
+ "testing"
+ "time"
+)
+
+func (e *OpError) isValid() error {
+ if e.Op == "" {
+ return fmt.Errorf("OpError.Op is empty: %v", e)
+ }
+ if e.Net == "" {
+ return fmt.Errorf("OpError.Net is empty: %v", e)
+ }
+ for _, addr := range []Addr{e.Source, e.Addr} {
+ if addr != nil {
+ switch addr.(type) {
+ case *TCPAddr, *UDPAddr, *IPAddr, *IPNet, *UnixAddr, *pipeAddr, fileAddr:
+ default:
+ return fmt.Errorf("OpError.Source or Addr is unknown type: %T, %v", addr, e)
+ }
+ }
+ }
+ if e.Err == nil {
+ return fmt.Errorf("OpError.Err is empty: %v", e)
+ }
+ return nil
+}
+
+// parseDialError parses nestedErr and reports whether it is a valid
+// error value from Dial, Listen functions.
+// It returns nil when nestedErr is valid.
+func parseDialError(nestedErr error) error {
+ if nestedErr == nil {
+ return nil
+ }
+
+ switch err := nestedErr.(type) {
+ case *OpError:
+ if err := err.isValid(); err != nil {
+ return err
+ }
+ nestedErr = err.Err
+ goto second
+ }
+ return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
+
+second:
+ if isPlatformError(nestedErr) {
+ return nil
+ }
+ switch err := nestedErr.(type) {
+ case *AddrError, addrinfoErrno, *DNSError, InvalidAddrError, *ParseError, *timeoutError, UnknownNetworkError:
+ return nil
+ case *os.SyscallError:
+ nestedErr = err.Err
+ goto third
+ }
+ switch nestedErr {
+ case errClosing, errMissingAddress:
+ return nil
+ }
+ return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
+
+third:
+ if isPlatformError(nestedErr) {
+ return nil
+ }
+ return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
+}
+
+var dialErrorTests = []struct {
+ network, address string
+}{
+ {"foo", ""},
+ {"bar", "baz"},
+ {"datakit", "mh/astro/r70"},
+ {"tcp", ""},
+ {"tcp", "127.0.0.1:☺"},
+ {"tcp", "no-such-name:80"},
+ {"tcp", "mh/astro/r70:http"},
+
+ {"tcp", "127.0.0.1:0"},
+ {"udp", "127.0.0.1:0"},
+ {"ip:icmp", "127.0.0.1"},
+
+ {"unix", "/path/to/somewhere"},
+ {"unixgram", "/path/to/somewhere"},
+ {"unixpacket", "/path/to/somewhere"},
+}
+
+func TestDialError(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("%s does not have full support of socktest", runtime.GOOS)
+ }
+
+ origTestHookLookupIP := testHookLookupIP
+ defer func() { testHookLookupIP = origTestHookLookupIP }()
+ testHookLookupIP = func(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, error) {
+ return nil, &DNSError{Err: "dial error test", Name: "name", Server: "server", IsTimeout: true}
+ }
+ sw.Set(socktest.FilterConnect, func(so *socktest.Status) (socktest.AfterFilter, error) {
+ return nil, errOpNotSupported
+ })
+ defer sw.Set(socktest.FilterConnect, nil)
+
+ d := Dialer{Timeout: someTimeout}
+ for i, tt := range dialErrorTests {
+ c, err := d.Dial(tt.network, tt.address)
+ if err == nil {
+ t.Errorf("#%d: should fail; %s:%s->%s", i, tt.network, c.LocalAddr(), c.RemoteAddr())
+ c.Close()
+ continue
+ }
+ if c != nil {
+ t.Errorf("Dial returned non-nil interface %T(%v) with err != nil", c, c)
+ }
+ if err = parseDialError(err); err != nil {
+ t.Errorf("#%d: %v", i, err)
+ continue
+ }
+ }
+}
+
+var listenErrorTests = []struct {
+ network, address string
+}{
+ {"foo", ""},
+ {"bar", "baz"},
+ {"datakit", "mh/astro/r70"},
+ {"tcp", "127.0.0.1:☺"},
+ {"tcp", "no-such-name:80"},
+ {"tcp", "mh/astro/r70:http"},
+
+ {"tcp", "127.0.0.1:0"},
+
+ {"unix", "/path/to/somewhere"},
+ {"unixpacket", "/path/to/somewhere"},
+}
+
+func TestListenError(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("%s does not have full support of socktest", runtime.GOOS)
+ }
+
+ origTestHookLookupIP := testHookLookupIP
+ defer func() { testHookLookupIP = origTestHookLookupIP }()
+ testHookLookupIP = func(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, error) {
+ return nil, &DNSError{Err: "listen error test", Name: "name", Server: "server", IsTimeout: true}
+ }
+ sw.Set(socktest.FilterListen, func(so *socktest.Status) (socktest.AfterFilter, error) {
+ return nil, errOpNotSupported
+ })
+ defer sw.Set(socktest.FilterListen, nil)
+
+ for i, tt := range listenErrorTests {
+ ln, err := Listen(tt.network, tt.address)
+ if err == nil {
+ t.Errorf("#%d: should fail; %s:%s->", i, tt.network, ln.Addr())
+ ln.Close()
+ continue
+ }
+ if ln != nil {
+ t.Errorf("Listen returned non-nil interface %T(%v) with err != nil", ln, ln)
+ }
+ if err = parseDialError(err); err != nil {
+ t.Errorf("#%d: %v", i, err)
+ continue
+ }
+ }
+}
+
+var listenPacketErrorTests = []struct {
+ network, address string
+}{
+ {"foo", ""},
+ {"bar", "baz"},
+ {"datakit", "mh/astro/r70"},
+ {"udp", "127.0.0.1:☺"},
+ {"udp", "no-such-name:80"},
+ {"udp", "mh/astro/r70:http"},
+}
+
+func TestListenPacketError(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("%s does not have full support of socktest", runtime.GOOS)
+ }
+
+ origTestHookLookupIP := testHookLookupIP
+ defer func() { testHookLookupIP = origTestHookLookupIP }()
+ testHookLookupIP = func(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, error) {
+ return nil, &DNSError{Err: "listen error test", Name: "name", Server: "server", IsTimeout: true}
+ }
+
+ for i, tt := range listenPacketErrorTests {
+ c, err := ListenPacket(tt.network, tt.address)
+ if err == nil {
+ t.Errorf("#%d: should fail; %s:%s->", i, tt.network, c.LocalAddr())
+ c.Close()
+ continue
+ }
+ if c != nil {
+ t.Errorf("ListenPacket returned non-nil interface %T(%v) with err != nil", c, c)
+ }
+ if err = parseDialError(err); err != nil {
+ t.Errorf("#%d: %v", i, err)
+ continue
+ }
+ }
+}
+
+// parseReadError parses nestedErr and reports whether it is a valid
+// error value from Read functions.
+// It returns nil when nestedErr is valid.
+func parseReadError(nestedErr error) error {
+ if nestedErr == nil {
+ return nil
+ }
+
+ switch err := nestedErr.(type) {
+ case *OpError:
+ if err := err.isValid(); err != nil {
+ return err
+ }
+ nestedErr = err.Err
+ goto second
+ }
+ if nestedErr == io.EOF {
+ return nil
+ }
+ return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
+
+second:
+ if isPlatformError(nestedErr) {
+ return nil
+ }
+ switch err := nestedErr.(type) {
+ case *os.SyscallError:
+ nestedErr = err.Err
+ goto third
+ }
+ switch nestedErr {
+ case errClosing, errTimeout:
+ return nil
+ }
+ return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
+
+third:
+ if isPlatformError(nestedErr) {
+ return nil
+ }
+ return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
+}
+
+// parseWriteError parses nestedErr and reports whether it is a valid
+// error value from Write functions.
+// It returns nil when nestedErr is valid.
+func parseWriteError(nestedErr error) error {
+ if nestedErr == nil {
+ return nil
+ }
+
+ switch err := nestedErr.(type) {
+ case *OpError:
+ if err := err.isValid(); err != nil {
+ return err
+ }
+ nestedErr = err.Err
+ goto second
+ }
+ return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
+
+second:
+ if isPlatformError(nestedErr) {
+ return nil
+ }
+ switch err := nestedErr.(type) {
+ case *AddrError, addrinfoErrno, *DNSError, InvalidAddrError, *ParseError, *timeoutError, UnknownNetworkError:
+ return nil
+ case *os.SyscallError:
+ nestedErr = err.Err
+ goto third
+ }
+ switch nestedErr {
+ case errClosing, errTimeout, ErrWriteToConnected, io.ErrUnexpectedEOF:
+ return nil
+ }
+ return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
+
+third:
+ if isPlatformError(nestedErr) {
+ return nil
+ }
+ return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
+}
+
+// parseCloseError parses nestedErr and reports whether it is a valid
+// error value from Close functions.
+// It returns nil when nestedErr is valid.
+func parseCloseError(nestedErr error) error {
+ if nestedErr == nil {
+ return nil
+ }
+
+ switch err := nestedErr.(type) {
+ case *OpError:
+ if err := err.isValid(); err != nil {
+ return err
+ }
+ nestedErr = err.Err
+ goto second
+ }
+ return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
+
+second:
+ if isPlatformError(nestedErr) {
+ return nil
+ }
+ switch err := nestedErr.(type) {
+ case *os.SyscallError:
+ nestedErr = err.Err
+ goto third
+ case *os.PathError: // for Plan 9
+ nestedErr = err.Err
+ goto third
+ }
+ switch nestedErr {
+ case errClosing:
+ return nil
+ }
+ return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
+
+third:
+ if isPlatformError(nestedErr) {
+ return nil
+ }
+ return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
+}
+
+func TestCloseError(t *testing.T) {
+ ln, err := newLocalListener("tcp")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ln.Close()
+ c, err := Dial(ln.Addr().Network(), ln.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ for i := 0; i < 3; i++ {
+ err = c.(*TCPConn).CloseRead()
+ if perr := parseCloseError(err); perr != nil {
+ t.Errorf("#%d: %v", i, perr)
+ }
+ }
+ for i := 0; i < 3; i++ {
+ err = c.(*TCPConn).CloseWrite()
+ if perr := parseCloseError(err); perr != nil {
+ t.Errorf("#%d: %v", i, perr)
+ }
+ }
+ for i := 0; i < 3; i++ {
+ err = c.Close()
+ if perr := parseCloseError(err); perr != nil {
+ t.Errorf("#%d: %v", i, perr)
+ }
+ err = ln.Close()
+ if perr := parseCloseError(err); perr != nil {
+ t.Errorf("#%d: %v", i, perr)
+ }
+ }
+
+ pc, err := ListenPacket("udp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer pc.Close()
+
+ for i := 0; i < 3; i++ {
+ err = pc.Close()
+ if perr := parseCloseError(err); perr != nil {
+ t.Errorf("#%d: %v", i, perr)
+ }
+ }
+}
+
+// parseAcceptError parses nestedErr and reports whether it is a valid
+// error value from Accept functions.
+// It returns nil when nestedErr is valid.
+func parseAcceptError(nestedErr error) error {
+ if nestedErr == nil {
+ return nil
+ }
+
+ switch err := nestedErr.(type) {
+ case *OpError:
+ if err := err.isValid(); err != nil {
+ return err
+ }
+ nestedErr = err.Err
+ goto second
+ }
+ return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
+
+second:
+ if isPlatformError(nestedErr) {
+ return nil
+ }
+ switch err := nestedErr.(type) {
+ case *os.SyscallError:
+ nestedErr = err.Err
+ goto third
+ }
+ switch nestedErr {
+ case errClosing, errTimeout:
+ return nil
+ }
+ return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
+
+third:
+ if isPlatformError(nestedErr) {
+ return nil
+ }
+ return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
+}
+
+func TestAcceptError(t *testing.T) {
+ handler := func(ls *localServer, ln Listener) {
+ for {
+ ln.(*TCPListener).SetDeadline(time.Now().Add(5 * time.Millisecond))
+ c, err := ln.Accept()
+ if perr := parseAcceptError(err); perr != nil {
+ t.Error(perr)
+ }
+ if err != nil {
+ if c != nil {
+ t.Errorf("Accept returned non-nil interface %T(%v) with err != nil", c, c)
+ }
+ if nerr, ok := err.(Error); !ok || (!nerr.Timeout() && !nerr.Temporary()) {
+ return
+ }
+ continue
+ }
+ c.Close()
+ }
+ }
+ ls, err := newLocalServer("tcp")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := ls.buildup(handler); err != nil {
+ ls.teardown()
+ t.Fatal(err)
+ }
+
+ time.Sleep(100 * time.Millisecond)
+ ls.teardown()
+}
+
+// parseCommonError parses nestedErr and reports whether it is a valid
+// error value from miscellaneous functions.
+// It returns nil when nestedErr is valid.
+func parseCommonError(nestedErr error) error {
+ if nestedErr == nil {
+ return nil
+ }
+
+ switch err := nestedErr.(type) {
+ case *OpError:
+ if err := err.isValid(); err != nil {
+ return err
+ }
+ nestedErr = err.Err
+ goto second
+ }
+ return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
+
+second:
+ if isPlatformError(nestedErr) {
+ return nil
+ }
+ switch err := nestedErr.(type) {
+ case *os.SyscallError:
+ nestedErr = err.Err
+ goto third
+ case *os.LinkError:
+ nestedErr = err.Err
+ goto third
+ case *os.PathError:
+ nestedErr = err.Err
+ goto third
+ }
+ switch nestedErr {
+ case errClosing:
+ return nil
+ }
+ return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
+
+third:
+ if isPlatformError(nestedErr) {
+ return nil
+ }
+ return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
+}
+
+func TestFileError(t *testing.T) {
+ switch runtime.GOOS {
+ case "windows":
+ t.Skip("not supported on %s", runtime.GOOS)
+ }
+
+ f, err := ioutil.TempFile("", "go-nettest")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.Remove(f.Name())
+ defer f.Close()
+
+ c, err := FileConn(f)
+ if err != nil {
+ if c != nil {
+ t.Errorf("FileConn returned non-nil interface %T(%v) with err != nil", c, c)
+ }
+ if perr := parseCommonError(err); perr != nil {
+ t.Error(perr)
+ }
+ } else {
+ c.Close()
+ t.Error("should fail")
+ }
+ ln, err := FileListener(f)
+ if err != nil {
+ if ln != nil {
+ t.Errorf("FileListener returned non-nil interface %T(%v) with err != nil", ln, ln)
+ }
+ if perr := parseCommonError(err); perr != nil {
+ t.Error(perr)
+ }
+ } else {
+ ln.Close()
+ t.Error("should fail")
+ }
+ pc, err := FilePacketConn(f)
+ if err != nil {
+ if pc != nil {
+ t.Errorf("FilePacketConn returned non-nil interface %T(%v) with err != nil", pc, pc)
+ }
+ if perr := parseCommonError(err); perr != nil {
+ t.Error(perr)
+ }
+ } else {
+ pc.Close()
+ t.Error("should fail")
+ }
+
+ ln, err = newLocalListener("tcp")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for i := 0; i < 3; i++ {
+ f, err := ln.(*TCPListener).File()
+ if err != nil {
+ if perr := parseCommonError(err); perr != nil {
+ t.Error(perr)
+ }
+ } else {
+ f.Close()
+ }
+ ln.Close()
+ }
+}
diff --git a/src/net/external_test.go b/src/net/external_test.go
new file mode 100644
index 0000000000..20611ff420
--- /dev/null
+++ b/src/net/external_test.go
@@ -0,0 +1,196 @@
+// 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 net
+
+import (
+ "fmt"
+ "io"
+ "strings"
+ "testing"
+)
+
+func TestResolveGoogle(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Skip("avoid external network")
+ }
+ if !supportsIPv4 && !supportsIPv6 {
+ t.Skip("ipv4 and ipv6 are not supported")
+ }
+
+ for _, network := range []string{"tcp", "tcp4", "tcp6"} {
+ addr, err := ResolveTCPAddr(network, "www.google.com:http")
+ if err != nil {
+ switch {
+ case network == "tcp" && !supportsIPv4:
+ fallthrough
+ case network == "tcp4" && !supportsIPv4:
+ t.Logf("skipping test; ipv4 is not supported: %v", err)
+ case network == "tcp6" && !supportsIPv6:
+ t.Logf("skipping test; ipv6 is not supported: %v", err)
+ default:
+ t.Error(err)
+ }
+ continue
+ }
+
+ switch {
+ case network == "tcp" && addr.IP.To4() == nil:
+ fallthrough
+ case network == "tcp4" && addr.IP.To4() == nil:
+ t.Errorf("got %v; want an ipv4 address on %s", addr, network)
+ case network == "tcp6" && (addr.IP.To16() == nil || addr.IP.To4() != nil):
+ t.Errorf("got %v; want an ipv6 address on %s", addr, network)
+ }
+ }
+}
+
+var dialGoogleTests = []struct {
+ dial func(string, string) (Conn, error)
+ unreachableNetwork string
+ networks []string
+ addrs []string
+}{
+ {
+ dial: (&Dialer{DualStack: true}).Dial,
+ networks: []string{"tcp", "tcp4", "tcp6"},
+ addrs: []string{"www.google.com:http"},
+ },
+ {
+ dial: Dial,
+ unreachableNetwork: "tcp6",
+ networks: []string{"tcp", "tcp4"},
+ },
+ {
+ dial: Dial,
+ unreachableNetwork: "tcp4",
+ networks: []string{"tcp", "tcp6"},
+ },
+}
+
+func TestDialGoogle(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Skip("avoid external network")
+ }
+ if !supportsIPv4 && !supportsIPv6 {
+ t.Skip("ipv4 and ipv6 are not supported")
+ }
+
+ var err error
+ dialGoogleTests[1].addrs, dialGoogleTests[2].addrs, err = googleLiteralAddrs()
+ if err != nil {
+ t.Error(err)
+ }
+ for _, tt := range dialGoogleTests {
+ for _, network := range tt.networks {
+ switch {
+ case network == "tcp4" && !supportsIPv4:
+ t.Log("skipping test; ipv4 is not supported")
+ continue
+ case network == "tcp4" && !*testIPv4:
+ fallthrough
+ case tt.unreachableNetwork == "tcp6" && !*testIPv4:
+ t.Log("disabled; use -ipv4 to enable")
+ continue
+ case network == "tcp6" && !supportsIPv6:
+ t.Log("skipping test; ipv6 is not supported")
+ continue
+ case network == "tcp6" && !*testIPv6:
+ fallthrough
+ case tt.unreachableNetwork == "tcp4" && !*testIPv6:
+ t.Log("disabled; use -ipv6 to enable")
+ continue
+ }
+
+ disableSocketConnect(tt.unreachableNetwork)
+ for _, addr := range tt.addrs {
+ if err := fetchGoogle(tt.dial, network, addr); err != nil {
+ t.Error(err)
+ }
+ }
+ enableSocketConnect()
+ }
+ }
+}
+
+var (
+ literalAddrs4 = [...]string{
+ "%d.%d.%d.%d:80",
+ "www.google.com:80",
+ "%d.%d.%d.%d:http",
+ "www.google.com:http",
+ "%03d.%03d.%03d.%03d:0080",
+ "[::ffff:%d.%d.%d.%d]:80",
+ "[::ffff:%02x%02x:%02x%02x]:80",
+ "[0:0:0:0:0000:ffff:%d.%d.%d.%d]:80",
+ "[0:0:0:0:000000:ffff:%d.%d.%d.%d]:80",
+ "[0:0:0:0::ffff:%d.%d.%d.%d]:80",
+ }
+ literalAddrs6 = [...]string{
+ "[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:80",
+ "ipv6.google.com:80",
+ "[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:http",
+ "ipv6.google.com:http",
+ }
+)
+
+func googleLiteralAddrs() (lits4, lits6 []string, err error) {
+ ips, err := LookupIP("www.google.com")
+ if err != nil {
+ return nil, nil, err
+ }
+ if len(ips) == 0 {
+ return nil, nil, nil
+ }
+ var ip4, ip6 IP
+ for _, ip := range ips {
+ if ip4 == nil && ip.To4() != nil {
+ ip4 = ip.To4()
+ }
+ if ip6 == nil && ip.To16() != nil && ip.To4() == nil {
+ ip6 = ip.To16()
+ }
+ if ip4 != nil && ip6 != nil {
+ break
+ }
+ }
+ if ip4 != nil {
+ for i, lit4 := range literalAddrs4 {
+ if strings.Contains(lit4, "%") {
+ literalAddrs4[i] = fmt.Sprintf(lit4, ip4[0], ip4[1], ip4[2], ip4[3])
+ }
+ }
+ lits4 = literalAddrs4[:]
+ }
+ if ip6 != nil {
+ for i, lit6 := range literalAddrs6 {
+ if strings.Contains(lit6, "%") {
+ literalAddrs6[i] = fmt.Sprintf(lit6, ip6[0], ip6[1], ip6[2], ip6[3], ip6[4], ip6[5], ip6[6], ip6[7], ip6[8], ip6[9], ip6[10], ip6[11], ip6[12], ip6[13], ip6[14], ip6[15])
+ }
+ }
+ lits6 = literalAddrs6[:]
+ }
+ return
+}
+
+func fetchGoogle(dial func(string, string) (Conn, error), network, address string) error {
+ c, err := dial(network, address)
+ if err != nil {
+ return err
+ }
+ defer c.Close()
+ req := []byte("GET /robots.txt HTTP/1.0\r\nHost: www.google.com\r\n\r\n")
+ if _, err := c.Write(req); err != nil {
+ return err
+ }
+ b := make([]byte, 1000)
+ n, err := io.ReadFull(c, b)
+ if err != nil {
+ return err
+ }
+ if n < 1000 {
+ return fmt.Errorf("short read from %s:%s->%s", network, c.RemoteAddr(), c.LocalAddr())
+ }
+ return nil
+}
diff --git a/src/net/fd_plan9.go b/src/net/fd_plan9.go
index ddadb6e5bc..32766f53b5 100644
--- a/src/net/fd_plan9.go
+++ b/src/net/fd_plan9.go
@@ -11,13 +11,13 @@ import (
"time"
)
-// Network file descritor.
+// Network file descriptor.
type netFD struct {
// locking/lifetime of sysfd + serialize access to Read and Write methods
fdmu fdMutex
// immutable until Close
- proto string
+ net string
n string
dir string
ctl, data *os.File
@@ -38,8 +38,8 @@ func dial(net string, ra Addr, dialer func(time.Time) (Conn, error), deadline ti
return dialChannel(net, ra, dialer, deadline)
}
-func newFD(proto, name string, ctl, data *os.File, laddr, raddr Addr) (*netFD, error) {
- return &netFD{proto: proto, n: name, dir: netdir + "/" + proto + "/" + name, ctl: ctl, data: data, laddr: laddr, raddr: raddr}, nil
+func newFD(net, name string, ctl, data *os.File, laddr, raddr Addr) (*netFD, error) {
+ return &netFD{net: net, n: name, dir: netdir + "/" + net + "/" + name, ctl: ctl, data: data, laddr: laddr, raddr: raddr}, nil
}
func (fd *netFD) init() error {
@@ -55,7 +55,7 @@ func (fd *netFD) name() string {
if fd.raddr != nil {
rs = fd.raddr.String()
}
- return fd.proto + ":" + ls + "->" + rs
+ return fd.net + ":" + ls + "->" + rs
}
func (fd *netFD) ok() bool { return fd != nil && fd.ctl != nil }
@@ -132,7 +132,7 @@ func (fd *netFD) Read(b []byte) (n int, err error) {
}
defer fd.readUnlock()
n, err = fd.data.Read(b)
- if fd.proto == "udp" && err == io.EOF {
+ if fd.net == "udp" && err == io.EOF {
n = 0
err = nil
}
@@ -202,7 +202,7 @@ func (fd *netFD) file(f *os.File, s string) (*os.File, error) {
dfd, err := syscall.Dup(int(f.Fd()), -1)
syscall.ForkLock.RUnlock()
if err != nil {
- return nil, &OpError{"dup", s, fd.laddr, err}
+ return nil, os.NewSyscallError("dup", err)
}
return os.NewFile(uintptr(dfd), s), nil
}
diff --git a/src/net/fd_unix.go b/src/net/fd_unix.go
index 9e1976136d..64e94fecf8 100644
--- a/src/net/fd_unix.go
+++ b/src/net/fd_unix.go
@@ -93,7 +93,7 @@ func (fd *netFD) connect(la, ra syscall.Sockaddr, deadline time.Time) error {
}
fallthrough
default:
- return err
+ return os.NewSyscallError("connect", err)
}
if err := fd.init(); err != nil {
return err
@@ -116,14 +116,14 @@ func (fd *netFD) connect(la, ra syscall.Sockaddr, deadline time.Time) error {
}
nerr, err := getsockoptIntFunc(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
if err != nil {
- return err
+ return os.NewSyscallError("getsockopt", err)
}
switch err := syscall.Errno(nerr); err {
case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
case syscall.Errno(0), syscall.EISCONN:
return nil
default:
- return err
+ return os.NewSyscallError("getsockopt", err)
}
}
}
@@ -205,11 +205,7 @@ func (fd *netFD) shutdown(how int) error {
return err
}
defer fd.decref()
- err := syscall.Shutdown(fd.sysfd, how)
- if err != nil {
- return &OpError{"shutdown", fd.net, fd.laddr, err}
- }
- return nil
+ return os.NewSyscallError("shutdown", syscall.Shutdown(fd.sysfd, how))
}
func (fd *netFD) closeRead() error {
@@ -226,7 +222,7 @@ func (fd *netFD) Read(p []byte) (n int, err error) {
}
defer fd.readUnlock()
if err := fd.pd.PrepareRead(); err != nil {
- return 0, &OpError{"read", fd.net, fd.raddr, err}
+ return 0, err
}
for {
n, err = syscall.Read(int(fd.sysfd), p)
@@ -241,8 +237,8 @@ func (fd *netFD) Read(p []byte) (n int, err error) {
err = fd.eofError(n, err)
break
}
- if err != nil && err != io.EOF {
- err = &OpError{"read", fd.net, fd.raddr, err}
+ if _, ok := err.(syscall.Errno); ok {
+ err = os.NewSyscallError("read", err)
}
return
}
@@ -253,7 +249,7 @@ func (fd *netFD) readFrom(p []byte) (n int, sa syscall.Sockaddr, err error) {
}
defer fd.readUnlock()
if err := fd.pd.PrepareRead(); err != nil {
- return 0, nil, &OpError{"read", fd.net, fd.laddr, err}
+ return 0, nil, err
}
for {
n, sa, err = syscall.Recvfrom(fd.sysfd, p, 0)
@@ -268,8 +264,8 @@ func (fd *netFD) readFrom(p []byte) (n int, sa syscall.Sockaddr, err error) {
err = fd.eofError(n, err)
break
}
- if err != nil && err != io.EOF {
- err = &OpError{"read", fd.net, fd.laddr, err}
+ if _, ok := err.(syscall.Errno); ok {
+ err = os.NewSyscallError("recvfrom", err)
}
return
}
@@ -280,7 +276,7 @@ func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.S
}
defer fd.readUnlock()
if err := fd.pd.PrepareRead(); err != nil {
- return 0, 0, 0, nil, &OpError{"read", fd.net, fd.laddr, err}
+ return 0, 0, 0, nil, err
}
for {
n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0)
@@ -295,8 +291,8 @@ func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.S
err = fd.eofError(n, err)
break
}
- if err != nil && err != io.EOF {
- err = &OpError{"read", fd.net, fd.laddr, err}
+ if _, ok := err.(syscall.Errno); ok {
+ err = os.NewSyscallError("recvmsg", err)
}
return
}
@@ -307,7 +303,7 @@ func (fd *netFD) Write(p []byte) (nn int, err error) {
}
defer fd.writeUnlock()
if err := fd.pd.PrepareWrite(); err != nil {
- return 0, &OpError{"write", fd.net, fd.raddr, err}
+ return 0, err
}
for {
var n int
@@ -324,7 +320,6 @@ func (fd *netFD) Write(p []byte) (nn int, err error) {
}
}
if err != nil {
- n = 0
break
}
if n == 0 {
@@ -332,8 +327,8 @@ func (fd *netFD) Write(p []byte) (nn int, err error) {
break
}
}
- if err != nil {
- err = &OpError{"write", fd.net, fd.raddr, err}
+ if _, ok := err.(syscall.Errno); ok {
+ err = os.NewSyscallError("write", err)
}
return nn, err
}
@@ -344,7 +339,7 @@ func (fd *netFD) writeTo(p []byte, sa syscall.Sockaddr) (n int, err error) {
}
defer fd.writeUnlock()
if err := fd.pd.PrepareWrite(); err != nil {
- return 0, &OpError{"write", fd.net, fd.raddr, err}
+ return 0, err
}
for {
err = syscall.Sendto(fd.sysfd, p, 0, sa)
@@ -357,8 +352,9 @@ func (fd *netFD) writeTo(p []byte, sa syscall.Sockaddr) (n int, err error) {
}
if err == nil {
n = len(p)
- } else {
- err = &OpError{"write", fd.net, fd.raddr, err}
+ }
+ if _, ok := err.(syscall.Errno); ok {
+ err = os.NewSyscallError("sendto", err)
}
return
}
@@ -369,7 +365,7 @@ func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oob
}
defer fd.writeUnlock()
if err := fd.pd.PrepareWrite(); err != nil {
- return 0, 0, &OpError{"write", fd.net, fd.raddr, err}
+ return 0, 0, err
}
for {
n, err = syscall.SendmsgN(fd.sysfd, p, oob, sa, 0)
@@ -382,8 +378,9 @@ func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oob
}
if err == nil {
oobn = len(oob)
- } else {
- err = &OpError{"write", fd.net, fd.raddr, err}
+ }
+ if _, ok := err.(syscall.Errno); ok {
+ err = os.NewSyscallError("sendmsg", err)
}
return
}
@@ -397,21 +394,28 @@ func (fd *netFD) accept() (netfd *netFD, err error) {
var s int
var rsa syscall.Sockaddr
if err = fd.pd.PrepareRead(); err != nil {
- return nil, &OpError{"accept", fd.net, fd.laddr, err}
+ return nil, err
}
for {
s, rsa, err = accept(fd.sysfd)
if err != nil {
- if err == syscall.EAGAIN {
+ nerr, ok := err.(*os.SyscallError)
+ if !ok {
+ return nil, err
+ }
+ switch nerr.Err {
+ case syscall.EAGAIN:
if err = fd.pd.WaitRead(); err == nil {
continue
}
- } else if err == syscall.ECONNABORTED {
- // This means that a socket on the listen queue was closed
- // before we Accept()ed it; it's a silly error, so try again.
+ case syscall.ECONNABORTED:
+ // This means that a socket on the
+ // listen queue was closed before we
+ // Accept()ed it; it's a silly error,
+ // so try again.
continue
}
- return nil, &OpError{"accept", fd.net, fd.laddr, err}
+ return nil, err
}
break
}
@@ -457,7 +461,7 @@ func dupCloseOnExec(fd int) (newfd int, err error) {
// from now on.
atomic.StoreInt32(&tryDupCloexec, 0)
default:
- return -1, e1
+ return -1, os.NewSyscallError("fcntl", e1)
}
}
return dupCloseOnExecOld(fd)
@@ -470,7 +474,7 @@ func dupCloseOnExecOld(fd int) (newfd int, err error) {
defer syscall.ForkLock.RUnlock()
newfd, err = syscall.Dup(fd)
if err != nil {
- return -1, err
+ return -1, os.NewSyscallError("dup", err)
}
syscall.CloseOnExec(newfd)
return
@@ -479,7 +483,7 @@ func dupCloseOnExecOld(fd int) (newfd int, err error) {
func (fd *netFD) dup() (f *os.File, err error) {
ns, err := dupCloseOnExec(fd.sysfd)
if err != nil {
- return nil, &OpError{"dup", fd.net, fd.laddr, err}
+ return nil, err
}
// We want blocking mode for the new fd, hence the double negative.
@@ -487,7 +491,7 @@ func (fd *netFD) dup() (f *os.File, err error) {
// I/O will block the thread instead of letting us use the epoll server.
// Everything will still work, just with more threads.
if err = syscall.SetNonblock(ns, false); err != nil {
- return nil, &OpError{"setnonblock", fd.net, fd.laddr, err}
+ return nil, os.NewSyscallError("setnonblock", err)
}
return os.NewFile(uintptr(ns), fd.name()), nil
diff --git a/src/net/fd_windows.go b/src/net/fd_windows.go
index d685883716..205daff9e4 100644
--- a/src/net/fd_windows.go
+++ b/src/net/fd_windows.go
@@ -5,7 +5,6 @@
package net
import (
- "errors"
"os"
"runtime"
"sync"
@@ -39,7 +38,7 @@ func sysInit() {
var d syscall.WSAData
e := syscall.WSAStartup(uint32(0x202), &d)
if e != nil {
- initErr = os.NewSyscallError("WSAStartup", e)
+ initErr = os.NewSyscallError("wsastartup", e)
}
canCancelIO = syscall.LoadCancelIoEx() == nil
if syscall.LoadGetAddrInfo() == nil {
@@ -154,7 +153,7 @@ func (s *ioSrv) ExecIO(o *operation, name string, submit func(o *operation) erro
// Notify runtime netpoll about starting IO.
err := fd.pd.Prepare(int(o.mode))
if err != nil {
- return 0, &OpError{name, fd.net, fd.laddr, err}
+ return 0, err
}
// Start IO.
if canCancelIO {
@@ -177,7 +176,7 @@ func (s *ioSrv) ExecIO(o *operation, name string, submit func(o *operation) erro
// IO started, and we have to wait for its completion.
err = nil
default:
- return 0, &OpError{name, fd.net, fd.laddr, err}
+ return 0, err
}
// Wait for our request to complete.
err = fd.pd.Wait(int(o.mode))
@@ -185,7 +184,7 @@ func (s *ioSrv) ExecIO(o *operation, name string, submit func(o *operation) erro
// All is good. Extract our IO results and return.
if o.errno != 0 {
err = syscall.Errno(o.errno)
- return 0, &OpError{name, fd.net, fd.laddr, err}
+ return 0, err
}
return int(o.qty), nil
}
@@ -216,7 +215,7 @@ func (s *ioSrv) ExecIO(o *operation, name string, submit func(o *operation) erro
if err == syscall.ERROR_OPERATION_ABORTED { // IO Canceled
err = netpollErr
}
- return 0, &OpError{name, fd.net, fd.laddr, err}
+ return 0, err
}
// We issued cancellation request. But, it seems, IO operation succeeded
// before cancellation request run. We need to treat IO operation as
@@ -298,7 +297,7 @@ func (fd *netFD) init() error {
size := uint32(unsafe.Sizeof(flag))
err := syscall.WSAIoctl(fd.sysfd, syscall.SIO_UDP_CONNRESET, (*byte)(unsafe.Pointer(&flag)), size, nil, 0, &ret, nil, 0)
if err != nil {
- return os.NewSyscallError("WSAIoctl", err)
+ return os.NewSyscallError("wsaioctl", err)
}
}
fd.rop.mode = 'r'
@@ -332,7 +331,7 @@ func (fd *netFD) connect(la, ra syscall.Sockaddr, deadline time.Time) error {
defer fd.setWriteDeadline(noDeadline)
}
if !canUseConnectEx(fd.net) {
- return connectFunc(fd.sysfd, ra)
+ return os.NewSyscallError("connect", connectFunc(fd.sysfd, ra))
}
// ConnectEx windows API requires an unconnected, previously bound socket.
if la == nil {
@@ -345,7 +344,7 @@ func (fd *netFD) connect(la, ra syscall.Sockaddr, deadline time.Time) error {
panic("unexpected type in connect")
}
if err := syscall.Bind(fd.sysfd, la); err != nil {
- return err
+ return os.NewSyscallError("bind", err)
}
}
// Call ConnectEx API.
@@ -355,10 +354,13 @@ func (fd *netFD) connect(la, ra syscall.Sockaddr, deadline time.Time) error {
return connectExFunc(o.fd.sysfd, o.sa, nil, 0, nil, &o.o)
})
if err != nil {
+ if _, ok := err.(syscall.Errno); ok {
+ err = os.NewSyscallError("connectex", err)
+ }
return err
}
// Refresh socket properties.
- return syscall.Setsockopt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_UPDATE_CONNECT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd)))
+ return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_UPDATE_CONNECT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd))))
}
func (fd *netFD) destroy() {
@@ -438,11 +440,7 @@ func (fd *netFD) shutdown(how int) error {
return err
}
defer fd.decref()
- err := syscall.Shutdown(fd.sysfd, how)
- if err != nil {
- return &OpError{"shutdown", fd.net, fd.laddr, err}
- }
- return nil
+ return syscall.Shutdown(fd.sysfd, how)
}
func (fd *netFD) closeRead() error {
@@ -467,10 +465,13 @@ func (fd *netFD) Read(buf []byte) (int, error) {
raceAcquire(unsafe.Pointer(&ioSync))
}
err = fd.eofError(n, err)
+ if _, ok := err.(syscall.Errno); ok {
+ err = os.NewSyscallError("wsarecv", err)
+ }
return n, err
}
-func (fd *netFD) readFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) {
+func (fd *netFD) readFrom(buf []byte) (int, syscall.Sockaddr, error) {
if len(buf) == 0 {
return 0, nil, nil
}
@@ -480,7 +481,7 @@ func (fd *netFD) readFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) {
defer fd.readUnlock()
o := &fd.rop
o.InitBuf(buf)
- n, err = rsrv.ExecIO(o, "WSARecvFrom", func(o *operation) error {
+ n, err := rsrv.ExecIO(o, "WSARecvFrom", func(o *operation) error {
if o.rsa == nil {
o.rsa = new(syscall.RawSockaddrAny)
}
@@ -488,11 +489,14 @@ func (fd *netFD) readFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) {
return syscall.WSARecvFrom(o.fd.sysfd, &o.buf, 1, &o.qty, &o.flags, o.rsa, &o.rsan, &o.o, nil)
})
err = fd.eofError(n, err)
- if err != nil {
- return 0, nil, err
+ if _, ok := err.(syscall.Errno); ok {
+ err = os.NewSyscallError("wsarecvfrom", err)
}
- sa, _ = o.rsa.Sockaddr()
- return
+ if err != nil {
+ return n, nil, err
+ }
+ sa, _ := o.rsa.Sockaddr()
+ return n, sa, nil
}
func (fd *netFD) Write(buf []byte) (int, error) {
@@ -505,9 +509,13 @@ func (fd *netFD) Write(buf []byte) (int, error) {
}
o := &fd.wop
o.InitBuf(buf)
- return wsrv.ExecIO(o, "WSASend", func(o *operation) error {
+ n, err := wsrv.ExecIO(o, "WSASend", func(o *operation) error {
return syscall.WSASend(o.fd.sysfd, &o.buf, 1, &o.qty, 0, &o.o, nil)
})
+ if _, ok := err.(syscall.Errno); ok {
+ err = os.NewSyscallError("wsasend", err)
+ }
+ return n, err
}
func (fd *netFD) writeTo(buf []byte, sa syscall.Sockaddr) (int, error) {
@@ -521,23 +529,27 @@ func (fd *netFD) writeTo(buf []byte, sa syscall.Sockaddr) (int, error) {
o := &fd.wop
o.InitBuf(buf)
o.sa = sa
- return wsrv.ExecIO(o, "WSASendto", func(o *operation) error {
+ n, err := wsrv.ExecIO(o, "WSASendto", func(o *operation) error {
return syscall.WSASendto(o.fd.sysfd, &o.buf, 1, &o.qty, 0, o.sa, &o.o, nil)
})
+ if _, ok := err.(syscall.Errno); ok {
+ err = os.NewSyscallError("wsasendto", err)
+ }
+ return n, err
}
func (fd *netFD) acceptOne(rawsa []syscall.RawSockaddrAny, o *operation) (*netFD, error) {
// Get new socket.
s, err := sysSocket(fd.family, fd.sotype, 0)
if err != nil {
- return nil, &OpError{"socket", fd.net, fd.laddr, err}
+ return nil, err
}
// Associate our new socket with IOCP.
netfd, err := newFD(s, fd.family, fd.sotype, fd.net)
if err != nil {
closeFunc(s)
- return nil, &OpError{"accept", fd.net, fd.laddr, err}
+ return nil, err
}
if err := netfd.init(); err != nil {
fd.Close()
@@ -552,6 +564,9 @@ func (fd *netFD) acceptOne(rawsa []syscall.RawSockaddrAny, o *operation) (*netFD
})
if err != nil {
netfd.Close()
+ if _, ok := err.(syscall.Errno); ok {
+ err = os.NewSyscallError("acceptex", err)
+ }
return nil, err
}
@@ -559,7 +574,7 @@ func (fd *netFD) acceptOne(rawsa []syscall.RawSockaddrAny, o *operation) (*netFD
err = syscall.Setsockopt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd)))
if err != nil {
netfd.Close()
- return nil, &OpError{"Setsockopt", fd.net, fd.laddr, err}
+ return nil, os.NewSyscallError("setsockopt", err)
}
return netfd, nil
@@ -585,11 +600,11 @@ func (fd *netFD) accept() (*netFD, error) {
// before AcceptEx could complete. These errors relate to new
// connection, not to AcceptEx, so ignore broken connection and
// try AcceptEx again for more connections.
- operr, ok := err.(*OpError)
+ nerr, ok := err.(*os.SyscallError)
if !ok {
return nil, err
}
- errno, ok := operr.Err.(syscall.Errno)
+ errno, ok := nerr.Err.(syscall.Errno)
if !ok {
return nil, err
}
@@ -617,15 +632,13 @@ func (fd *netFD) accept() (*netFD, error) {
func (fd *netFD) dup() (*os.File, error) {
// TODO: Implement this
- return nil, os.NewSyscallError("dup", syscall.EWINDOWS)
+ return nil, syscall.EWINDOWS
}
-var errNoSupport = errors.New("address family not supported")
-
func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
- return 0, 0, 0, nil, errNoSupport
+ return 0, 0, 0, nil, syscall.EWINDOWS
}
func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) {
- return 0, 0, errNoSupport
+ return 0, 0, syscall.EWINDOWS
}
diff --git a/src/net/file.go b/src/net/file.go
new file mode 100644
index 0000000000..1aad477400
--- /dev/null
+++ b/src/net/file.go
@@ -0,0 +1,48 @@
+// Copyright 2015 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 net
+
+import "os"
+
+type fileAddr string
+
+func (fileAddr) Network() string { return "file+net" }
+func (f fileAddr) String() string { return string(f) }
+
+// FileConn returns a copy of the network connection corresponding to
+// the open file f.
+// It is the caller's responsibility to close f when finished.
+// Closing c does not affect f, and closing f does not affect c.
+func FileConn(f *os.File) (c Conn, err error) {
+ c, err = fileConn(f)
+ if err != nil {
+ err = &OpError{Op: "file", Net: "file+net", Source: nil, Addr: fileAddr(f.Name()), Err: err}
+ }
+ return
+}
+
+// FileListener returns a copy of the network listener corresponding
+// to the open file f.
+// It is the caller's responsibility to close ln when finished.
+// Closing ln does not affect f, and closing f does not affect ln.
+func FileListener(f *os.File) (ln Listener, err error) {
+ ln, err = fileListener(f)
+ if err != nil {
+ err = &OpError{Op: "file", Net: "file+net", Source: nil, Addr: fileAddr(f.Name()), Err: err}
+ }
+ return
+}
+
+// FilePacketConn returns a copy of the packet network connection
+// corresponding to the open file f.
+// It is the caller's responsibility to close f when finished.
+// Closing c does not affect f, and closing f does not affect c.
+func FilePacketConn(f *os.File) (c PacketConn, err error) {
+ c, err = filePacketConn(f)
+ if err != nil {
+ err = &OpError{Op: "file", Net: "file+net", Source: nil, Addr: fileAddr(f.Name()), Err: err}
+ }
+ return
+}
diff --git a/src/net/file_plan9.go b/src/net/file_plan9.go
index 068f0881dd..892775a024 100644
--- a/src/net/file_plan9.go
+++ b/src/net/file_plan9.go
@@ -86,7 +86,7 @@ func newFileFD(f *os.File) (net *netFD, err error) {
return newFD(comp[1], name, ctl, nil, laddr, nil)
}
-func newFileConn(f *os.File) (c Conn, err error) {
+func fileConn(f *os.File) (Conn, error) {
fd, err := newFileFD(f)
if err != nil {
return nil, err
@@ -109,7 +109,7 @@ func newFileConn(f *os.File) (c Conn, err error) {
return nil, syscall.EPLAN9
}
-func newFileListener(f *os.File) (l Listener, err error) {
+func fileListener(f *os.File) (Listener, error) {
fd, err := newFileFD(f)
if err != nil {
return nil, err
@@ -132,26 +132,6 @@ func newFileListener(f *os.File) (l Listener, err error) {
return &TCPListener{fd}, nil
}
-// FileConn returns a copy of the network connection corresponding to
-// the open file f. It is the caller's responsibility to close f when
-// finished. Closing c does not affect f, and closing f does not
-// affect c.
-func FileConn(f *os.File) (c Conn, err error) {
- return newFileConn(f)
-}
-
-// FileListener returns a copy of the network listener corresponding
-// to the open file f. It is the caller's responsibility to close l
-// when finished. Closing l does not affect f, and closing f does not
-// affect l.
-func FileListener(f *os.File) (l Listener, err error) {
- return newFileListener(f)
-}
-
-// FilePacketConn returns a copy of the packet network connection
-// corresponding to the open file f. It is the caller's
-// responsibility to close f when finished. Closing c does not affect
-// f, and closing f does not affect c.
-func FilePacketConn(f *os.File) (c PacketConn, err error) {
+func filePacketConn(f *os.File) (PacketConn, error) {
return nil, syscall.EPLAN9
}
diff --git a/src/net/file_stub.go b/src/net/file_stub.go
index 4281072ef9..0f7460c757 100644
--- a/src/net/file_stub.go
+++ b/src/net/file_stub.go
@@ -11,28 +11,6 @@ import (
"syscall"
)
-// FileConn returns a copy of the network connection corresponding to
-// the open file f. It is the caller's responsibility to close f when
-// finished. Closing c does not affect f, and closing f does not
-// affect c.
-func FileConn(f *os.File) (c Conn, err error) {
- return nil, syscall.ENOPROTOOPT
-
-}
-
-// FileListener returns a copy of the network listener corresponding
-// to the open file f. It is the caller's responsibility to close l
-// when finished. Closing l does not affect f, and closing f does not
-// affect l.
-func FileListener(f *os.File) (l Listener, err error) {
- return nil, syscall.ENOPROTOOPT
-
-}
-
-// FilePacketConn returns a copy of the packet network connection
-// corresponding to the open file f. It is the caller's
-// responsibility to close f when finished. Closing c does not affect
-// f, and closing f does not affect c.
-func FilePacketConn(f *os.File) (c PacketConn, err error) {
- return nil, syscall.ENOPROTOOPT
-}
+func fileConn(f *os.File) (Conn, error) { return nil, syscall.ENOPROTOOPT }
+func fileListener(f *os.File) (Listener, error) { return nil, syscall.ENOPROTOOPT }
+func filePacketConn(f *os.File) (PacketConn, error) { return nil, syscall.ENOPROTOOPT }
diff --git a/src/net/file_test.go b/src/net/file_test.go
index 609efb232e..f358f709ac 100644
--- a/src/net/file_test.go
+++ b/src/net/file_test.go
@@ -29,26 +29,26 @@ type connFile interface {
func testFileListener(t *testing.T, net, laddr string) {
l, err := Listen(net, laddr)
if err != nil {
- t.Fatalf("Listen failed: %v", err)
+ t.Fatal(err)
}
defer l.Close()
lf := l.(listenerFile)
f, err := lf.File()
if err != nil {
- t.Fatalf("File failed: %v", err)
+ t.Fatal(err)
}
c, err := FileListener(f)
if err != nil {
- t.Fatalf("FileListener failed: %v", err)
+ t.Fatal(err)
}
if !reflect.DeepEqual(l.Addr(), c.Addr()) {
- t.Fatalf("Addrs not equal: %#v != %#v", l.Addr(), c.Addr())
+ t.Fatalf("got %#v; want%#v", l.Addr(), c.Addr())
}
if err := c.Close(); err != nil {
- t.Fatalf("Close failed: %v", err)
+ t.Fatal(err)
}
if err := f.Close(); err != nil {
- t.Fatalf("Close failed: %v", err)
+ t.Fatal(err)
}
}
@@ -84,12 +84,12 @@ var fileListenerTests = []struct {
func TestFileListener(t *testing.T) {
switch runtime.GOOS {
case "nacl", "windows":
- t.Skipf("skipping test on %q", runtime.GOOS)
+ t.Skipf("not supported on %s", runtime.GOOS)
}
for _, tt := range fileListenerTests {
if !testableListenArgs(tt.net, tt.laddr, "") {
- t.Logf("skipping %s test", tt.net+":"+tt.laddr+"->")
+ t.Logf("skipping %s test", tt.net+" "+tt.laddr)
continue
}
testFileListener(t, tt.net, tt.laddr)
@@ -99,47 +99,47 @@ func TestFileListener(t *testing.T) {
func testFilePacketConn(t *testing.T, pcf packetConnFile, listen bool) {
f, err := pcf.File()
if err != nil {
- t.Fatalf("File failed: %v", err)
+ t.Fatal(err)
}
c, err := FilePacketConn(f)
if err != nil {
- t.Fatalf("FilePacketConn failed: %v", err)
+ t.Fatal(err)
}
if !reflect.DeepEqual(pcf.LocalAddr(), c.LocalAddr()) {
- t.Fatalf("LocalAddrs not equal: %#v != %#v", pcf.LocalAddr(), c.LocalAddr())
+ t.Fatalf("got %#v; want %#v", pcf.LocalAddr(), c.LocalAddr())
}
if listen {
if _, err := c.WriteTo([]byte{}, c.LocalAddr()); err != nil {
- t.Fatalf("WriteTo failed: %v", err)
+ t.Fatal(err)
}
}
if err := c.Close(); err != nil {
- t.Fatalf("Close failed: %v", err)
+ t.Fatal(err)
}
if err := f.Close(); err != nil {
- t.Fatalf("Close failed: %v", err)
+ t.Fatal(err)
}
}
func testFilePacketConnListen(t *testing.T, net, laddr string) {
l, err := ListenPacket(net, laddr)
if err != nil {
- t.Fatalf("ListenPacket failed: %v", err)
+ t.Fatal(err)
}
testFilePacketConn(t, l.(packetConnFile), true)
if err := l.Close(); err != nil {
- t.Fatalf("Close failed: %v", err)
+ t.Fatal(err)
}
}
func testFilePacketConnDial(t *testing.T, net, raddr string) {
c, err := Dial(net, raddr)
if err != nil {
- t.Fatalf("Dial failed: %v", err)
+ t.Fatal(err)
}
testFilePacketConn(t, c.(packetConnFile), false)
if err := c.Close(); err != nil {
- t.Fatalf("Close failed: %v", err)
+ t.Fatal(err)
}
}
@@ -156,7 +156,8 @@ var filePacketConnTests = []struct {
{net: "udp6", addr: "[::1]:0"},
- {net: "ip4:icmp", addr: "127.0.0.1"},
+ // TODO(mikioh,bradfitz): renable once 10730 is fixed
+ // {net: "ip4:icmp", addr: "127.0.0.1"},
{net: "unixgram", addr: "@gotest3/net"},
}
@@ -164,12 +165,12 @@ var filePacketConnTests = []struct {
func TestFilePacketConn(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "windows":
- t.Skipf("skipping test on %q", runtime.GOOS)
+ t.Skipf("not supported on %s", runtime.GOOS)
}
for _, tt := range filePacketConnTests {
if !testableListenArgs(tt.net, tt.addr, "") {
- t.Logf("skipping %s test", tt.net+":"+tt.addr+"->")
+ t.Logf("skipping %s test", tt.net+" "+tt.addr)
continue
}
if os.Getuid() != 0 && tt.net == "ip4:icmp" {
diff --git a/src/net/file_unix.go b/src/net/file_unix.go
index 8d806a1d63..147ca1ed95 100644
--- a/src/net/file_unix.go
+++ b/src/net/file_unix.go
@@ -14,12 +14,12 @@ import (
func newFileFD(f *os.File) (*netFD, error) {
fd, err := dupCloseOnExec(int(f.Fd()))
if err != nil {
- return nil, os.NewSyscallError("dup", err)
+ return nil, err
}
if err = syscall.SetNonblock(fd, true); err != nil {
closeFunc(fd)
- return nil, err
+ return nil, os.NewSyscallError("setnonblock", err)
}
sotype, err := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_TYPE)
@@ -32,9 +32,6 @@ func newFileFD(f *os.File) (*netFD, error) {
toAddr := sockaddrToTCP
lsa, _ := syscall.Getsockname(fd)
switch lsa.(type) {
- default:
- closeFunc(fd)
- return nil, syscall.EINVAL
case *syscall.SockaddrInet4:
family = syscall.AF_INET
if sotype == syscall.SOCK_DGRAM {
@@ -57,6 +54,9 @@ func newFileFD(f *os.File) (*netFD, error) {
} else if sotype == syscall.SOCK_SEQPACKET {
toAddr = sockaddrToUnixpacket
}
+ default:
+ closeFunc(fd)
+ return nil, syscall.EPROTONOSUPPORT
}
laddr := toAddr(lsa)
rsa, _ := syscall.Getpeername(fd)
@@ -75,11 +75,7 @@ func newFileFD(f *os.File) (*netFD, error) {
return netfd, nil
}
-// FileConn returns a copy of the network connection corresponding to
-// the open file f. It is the caller's responsibility to close f when
-// finished. Closing c does not affect f, and closing f does not
-// affect c.
-func FileConn(f *os.File) (c Conn, err error) {
+func fileConn(f *os.File) (Conn, error) {
fd, err := newFileFD(f)
if err != nil {
return nil, err
@@ -98,11 +94,7 @@ func FileConn(f *os.File) (c Conn, err error) {
return nil, syscall.EINVAL
}
-// FileListener returns a copy of the network listener corresponding
-// to the open file f. It is the caller's responsibility to close l
-// when finished. Closing l does not affect f, and closing f does not
-// affect l.
-func FileListener(f *os.File) (l Listener, err error) {
+func fileListener(f *os.File) (Listener, error) {
fd, err := newFileFD(f)
if err != nil {
return nil, err
@@ -117,11 +109,7 @@ func FileListener(f *os.File) (l Listener, err error) {
return nil, syscall.EINVAL
}
-// FilePacketConn returns a copy of the packet network connection
-// corresponding to the open file f. It is the caller's
-// responsibility to close f when finished. Closing c does not affect
-// f, and closing f does not affect c.
-func FilePacketConn(f *os.File) (c PacketConn, err error) {
+func filePacketConn(f *os.File) (PacketConn, error) {
fd, err := newFileFD(f)
if err != nil {
return nil, err
diff --git a/src/net/file_windows.go b/src/net/file_windows.go
index ca2b9b2262..241fa17617 100644
--- a/src/net/file_windows.go
+++ b/src/net/file_windows.go
@@ -9,29 +9,17 @@ import (
"syscall"
)
-// FileConn returns a copy of the network connection corresponding to
-// the open file f. It is the caller's responsibility to close f when
-// finished. Closing c does not affect f, and closing f does not
-// affect c.
-func FileConn(f *os.File) (c Conn, err error) {
+func fileConn(f *os.File) (Conn, error) {
// TODO: Implement this
- return nil, os.NewSyscallError("FileConn", syscall.EWINDOWS)
+ return nil, syscall.EWINDOWS
}
-// FileListener returns a copy of the network listener corresponding
-// to the open file f. It is the caller's responsibility to close l
-// when finished. Closing l does not affect f, and closing f does not
-// affect l.
-func FileListener(f *os.File) (l Listener, err error) {
+func fileListener(f *os.File) (Listener, error) {
// TODO: Implement this
- return nil, os.NewSyscallError("FileListener", syscall.EWINDOWS)
+ return nil, syscall.EWINDOWS
}
-// FilePacketConn returns a copy of the packet network connection
-// corresponding to the open file f. It is the caller's
-// responsibility to close f when finished. Closing c does not affect
-// f, and closing f does not affect c.
-func FilePacketConn(f *os.File) (c PacketConn, err error) {
+func filePacketConn(f *os.File) (PacketConn, error) {
// TODO: Implement this
- return nil, os.NewSyscallError("FilePacketConn", syscall.EWINDOWS)
+ return nil, syscall.EWINDOWS
}
diff --git a/src/net/hook.go b/src/net/hook.go
new file mode 100644
index 0000000000..32ba15e15a
--- /dev/null
+++ b/src/net/hook.go
@@ -0,0 +1,10 @@
+// Copyright 2015 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 net
+
+var (
+ testHookLookupIP = func(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, error) { return fn(host) }
+ testHookSetKeepAlive = func() {}
+)
diff --git a/src/net/hook_plan9.go b/src/net/hook_plan9.go
new file mode 100644
index 0000000000..e053348505
--- /dev/null
+++ b/src/net/hook_plan9.go
@@ -0,0 +1,9 @@
+// Copyright 2015 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 net
+
+import "time"
+
+var testHookDialChannel = func() { time.Sleep(time.Millisecond) } // see golang.org/issue/5349
diff --git a/src/net/hook_unix.go b/src/net/hook_unix.go
index 626d07fbb0..361ca5980c 100644
--- a/src/net/hook_unix.go
+++ b/src/net/hook_unix.go
@@ -9,10 +9,13 @@ package net
import "syscall"
var (
+ testHookDialChannel = func() {} // see golang.org/issue/5349
+
// Placeholders for socket system calls.
socketFunc func(int, int, int) (int, error) = syscall.Socket
closeFunc func(int) error = syscall.Close
connectFunc func(int, syscall.Sockaddr) error = syscall.Connect
+ listenFunc func(int, int) error = syscall.Listen
acceptFunc func(int) (int, syscall.Sockaddr, error) = syscall.Accept
getsockoptIntFunc func(int, int, int) (int, error) = syscall.GetsockoptInt
)
diff --git a/src/net/hook_windows.go b/src/net/hook_windows.go
index 2a6e5bf267..126b0ebdd1 100644
--- a/src/net/hook_windows.go
+++ b/src/net/hook_windows.go
@@ -4,12 +4,18 @@
package net
-import "syscall"
+import (
+ "syscall"
+ "time"
+)
var (
+ testHookDialChannel = func() { time.Sleep(time.Millisecond) } // see golang.org/issue/5349
+
// Placeholders for socket system calls.
socketFunc func(int, int, int) (syscall.Handle, error) = syscall.Socket
closeFunc func(syscall.Handle) error = syscall.Closesocket
connectFunc func(syscall.Handle, syscall.Sockaddr) error = syscall.Connect
connectExFunc func(syscall.Handle, syscall.Sockaddr, *byte, uint32, *uint32, *syscall.Overlapped) error = syscall.ConnectEx
+ listenFunc func(syscall.Handle, int) error = syscall.Listen
)
diff --git a/src/net/hosts_test.go b/src/net/hosts_test.go
index 5bb663b4c7..352ecb95d0 100644
--- a/src/net/hosts_test.go
+++ b/src/net/hosts_test.go
@@ -39,14 +39,12 @@ func TestLookupStaticHost(t *testing.T) {
tt := hosttests[i]
ips := lookupStaticHost(tt.host)
if len(ips) != len(tt.ips) {
- t.Errorf("# of hosts = %v; want %v",
- len(ips), len(tt.ips))
+ t.Errorf("# of hosts = %v; want %v", len(ips), len(tt.ips))
continue
}
for k, v := range ips {
if tt.ips[k].String() != v {
- t.Errorf("lookupStaticHost(%q) = %v; want %v",
- tt.host, v, tt.ips[k])
+ t.Errorf("lookupStaticHost(%q) = %v; want %v", tt.host, v, tt.ips[k])
}
}
}
diff --git a/src/net/http/cgi/matryoshka_test.go b/src/net/http/cgi/matryoshka_test.go
index bf28c5625b..c89c6d525e 100644
--- a/src/net/http/cgi/matryoshka_test.go
+++ b/src/net/http/cgi/matryoshka_test.go
@@ -21,10 +21,13 @@ import (
"time"
)
+// iOS cannot fork, so we skip some tests
+var iOS = runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64")
+
// This test is a CGI host (testing host.go) that runs its own binary
// as a child process testing the other half of CGI (child.go).
func TestHostingOurselves(t *testing.T) {
- if runtime.GOOS == "nacl" || (runtime.GOOS == "darwin" && runtime.GOARCH == "arm") {
+ if runtime.GOOS == "nacl" || iOS {
t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
}
@@ -93,7 +96,7 @@ func (w *limitWriter) Write(p []byte) (n int, err error) {
// If there's an error copying the child's output to the parent, test
// that we kill the child.
func TestKillChildAfterCopyError(t *testing.T) {
- if runtime.GOOS == "nacl" || (runtime.GOOS == "darwin" && runtime.GOARCH == "arm") {
+ if runtime.GOOS == "nacl" || iOS {
t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
}
@@ -140,7 +143,7 @@ func TestKillChildAfterCopyError(t *testing.T) {
// Test that a child handler writing only headers works.
// golang.org/issue/7196
func TestChildOnlyHeaders(t *testing.T) {
- if runtime.GOOS == "nacl" || (runtime.GOOS == "darwin" && runtime.GOARCH == "arm") {
+ if runtime.GOOS == "nacl" || iOS {
t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
}
diff --git a/src/net/http/client.go b/src/net/http/client.go
index 7341871036..1c5e1911e0 100644
--- a/src/net/http/client.go
+++ b/src/net/http/client.go
@@ -257,8 +257,9 @@ func shouldRedirectPost(statusCode int) bool {
return false
}
-// Get issues a GET to the specified URL. If the response is one of the following
-// redirect codes, Get follows the redirect, up to a maximum of 10 redirects:
+// Get issues a GET to the specified URL. If the response is one of
+// the following redirect codes, Get follows the redirect, up to a
+// maximum of 10 redirects:
//
// 301 (Moved Permanently)
// 302 (Found)
@@ -273,11 +274,14 @@ func shouldRedirectPost(statusCode int) bool {
// Caller should close resp.Body when done reading from it.
//
// Get is a wrapper around DefaultClient.Get.
+//
+// To make a request with custom headers, use NewRequest and
+// DefaultClient.Do.
func Get(url string) (resp *Response, err error) {
return DefaultClient.Get(url)
}
-// Get issues a GET to the specified URL. If the response is one of the
+// Get issues a GET to the specified URL. If the response is one of the
// following redirect codes, Get follows the redirect after calling the
// Client's CheckRedirect function.
//
@@ -292,6 +296,8 @@ func Get(url string) (resp *Response, err error) {
//
// When err is nil, resp always contains a non-nil resp.Body.
// Caller should close resp.Body when done reading from it.
+//
+// To make a request with custom headers, use NewRequest and Client.Do.
func (c *Client) Get(url string) (resp *Response, err error) {
req, err := NewRequest("GET", url, nil)
if err != nil {
@@ -390,7 +396,7 @@ func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bo
}
resp.Body.Close()
if urlStr = resp.Header.Get("Location"); urlStr == "" {
- err = errors.New(fmt.Sprintf("%d response missing Location header", resp.StatusCode))
+ err = fmt.Errorf("%d response missing Location header", resp.StatusCode)
break
}
base = req.URL
@@ -438,7 +444,12 @@ func defaultCheckRedirect(req *Request, via []*Request) error {
//
// Caller should close resp.Body when done reading from it.
//
-// Post is a wrapper around DefaultClient.Post
+// If the provided body is an io.Closer, it is closed after the
+// request.
+//
+// Post is a wrapper around DefaultClient.Post.
+//
+// To set custom headers, use NewRequest and DefaultClient.Do.
func Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {
return DefaultClient.Post(url, bodyType, body)
}
@@ -447,8 +458,10 @@ func Post(url string, bodyType string, body io.Reader) (resp *Response, err erro
//
// Caller should close resp.Body when done reading from it.
//
-// If the provided body is also an io.Closer, it is closed after the
+// If the provided body is an io.Closer, it is closed after the
// request.
+//
+// To set custom headers, use NewRequest and Client.Do.
func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {
req, err := NewRequest("POST", url, body)
if err != nil {
@@ -461,16 +474,22 @@ func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Respon
// PostForm issues a POST to the specified URL, with data's keys and
// values URL-encoded as the request body.
//
+// The Content-Type header is set to application/x-www-form-urlencoded.
+// To set other headers, use NewRequest and DefaultClient.Do.
+//
// When err is nil, resp always contains a non-nil resp.Body.
// Caller should close resp.Body when done reading from it.
//
-// PostForm is a wrapper around DefaultClient.PostForm
+// PostForm is a wrapper around DefaultClient.PostForm.
func PostForm(url string, data url.Values) (resp *Response, err error) {
return DefaultClient.PostForm(url, data)
}
// PostForm issues a POST to the specified URL,
-// with data's keys and values urlencoded as the request body.
+// with data's keys and values URL-encoded as the request body.
+//
+// The Content-Type header is set to application/x-www-form-urlencoded.
+// To set other headers, use NewRequest and DefaultClient.Do.
//
// When err is nil, resp always contains a non-nil resp.Body.
// Caller should close resp.Body when done reading from it.
diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go
index 18645ff00d..dc499a90b6 100644
--- a/src/net/http/client_test.go
+++ b/src/net/http/client_test.go
@@ -334,6 +334,7 @@ var echoCookiesRedirectHandler = HandlerFunc(func(w ResponseWriter, r *Request)
})
func TestClientSendsCookieFromJar(t *testing.T) {
+ defer afterTest(t)
tr := &recordingTransport{}
client := &Client{Transport: tr}
client.Jar = &TestJar{perURL: make(map[string][]*Cookie)}
diff --git a/src/net/http/export_test.go b/src/net/http/export_test.go
index 87b6c0773a..0457be50da 100644
--- a/src/net/http/export_test.go
+++ b/src/net/http/export_test.go
@@ -10,9 +10,17 @@ package http
import (
"net"
"net/url"
+ "sync"
"time"
)
+func init() {
+ // We only want to pay for this cost during testing.
+ // When not under test, these values are always nil
+ // and never assigned to.
+ testHookMu = new(sync.Mutex)
+}
+
func NewLoggingConn(baseName string, c net.Conn) net.Conn {
return newLoggingConn(baseName, c)
}
@@ -78,6 +86,20 @@ func (t *Transport) PutIdleTestConn() bool {
})
}
+func SetInstallConnClosedHook(f func()) {
+ testHookPersistConnClosedGotRes = f
+}
+
+func SetEnterRoundTripHook(f func()) {
+ testHookEnterRoundTrip = f
+}
+
+func SetReadLoopBeforeNextReadHook(f func()) {
+ testHookMu.Lock()
+ defer testHookMu.Unlock()
+ testHookReadLoopBeforeNextRead = f
+}
+
func NewTestTimeoutHandler(handler Handler, ch <-chan time.Time) Handler {
f := func() <-chan time.Time {
return ch
@@ -106,3 +128,5 @@ func SetPendingDialHooks(before, after func()) {
var ExportServerNewConn = (*Server).newConn
var ExportCloseWriteAndWait = (*conn).closeWriteAndWait
+
+var ExportErrRequestCanceled = errRequestCanceled
diff --git a/src/net/http/fs.go b/src/net/http/fs.go
index 4e69da8f7f..75720234c2 100644
--- a/src/net/http/fs.go
+++ b/src/net/http/fs.go
@@ -358,16 +358,16 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec
f, err := fs.Open(name)
if err != nil {
- // TODO expose actual error?
- NotFound(w, r)
+ msg, code := toHTTPError(err)
+ Error(w, msg, code)
return
}
defer f.Close()
d, err1 := f.Stat()
if err1 != nil {
- // TODO expose actual error?
- NotFound(w, r)
+ msg, code := toHTTPError(err)
+ Error(w, msg, code)
return
}
@@ -417,6 +417,22 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec
serveContent(w, r, d.Name(), d.ModTime(), sizeFunc, f)
}
+// toHTTPError returns a non-specific HTTP error message and status code
+// for a given non-nil error value. It's important that toHTTPError does not
+// actually return err.Error(), since msg and httpStatus are returned to users,
+// and historically Go's ServeContent always returned just "404 Not Found" for
+// all errors. We don't want to start leaking information in error messages.
+func toHTTPError(err error) (msg string, httpStatus int) {
+ if os.IsNotExist(err) {
+ return "404 page not found", StatusNotFound
+ }
+ if os.IsPermission(err) {
+ return "403 Forbidden", StatusForbidden
+ }
+ // Default:
+ return "500 Internal Server Error", StatusInternalServerError
+}
+
// localRedirect gives a Moved Permanently response.
// It does not convert relative paths to absolute paths like Redirect does.
func localRedirect(w ResponseWriter, r *Request, newPath string) {
@@ -427,7 +443,13 @@ func localRedirect(w ResponseWriter, r *Request, newPath string) {
w.WriteHeader(StatusMovedPermanently)
}
-// ServeFile replies to the request with the contents of the named file or directory.
+// ServeFile replies to the request with the contents of the named
+// file or directory.
+//
+// As a special case, ServeFile redirects any request where r.URL.Path
+// ends in "/index.html" to the same path, without the final
+// "index.html". To avoid such redirects either modify the path or
+// use ServeContent.
func ServeFile(w ResponseWriter, r *Request, name string) {
dir, file := filepath.Split(name)
serveFile(w, r, Dir(dir), file, false)
@@ -444,6 +466,10 @@ type fileHandler struct {
// use http.Dir:
//
// http.Handle("/", http.FileServer(http.Dir("/tmp")))
+//
+// As a special case, the returned file server redirects any request
+// ending in "/index.html" to the same path, without the final
+// "index.html".
func FileServer(root FileSystem) Handler {
return &fileHandler{root}
}
diff --git a/src/net/http/fs_test.go b/src/net/http/fs_test.go
index a8cfe5f4c9..794dabc40a 100644
--- a/src/net/http/fs_test.go
+++ b/src/net/http/fs_test.go
@@ -497,6 +497,7 @@ type fakeFileInfo struct {
modtime time.Time
ents []*fakeFileInfo
contents string
+ err error
}
func (f *fakeFileInfo) Name() string { return f.basename }
@@ -549,6 +550,9 @@ func (fs fakeFS) Open(name string) (File, error) {
if !ok {
return nil, os.ErrNotExist
}
+ if f.err != nil {
+ return nil, f.err
+ }
return &fakeFile{ReadSeeker: strings.NewReader(f.contents), fi: f, path: name}, nil
}
@@ -803,6 +807,31 @@ func TestServeContent(t *testing.T) {
}
}
+func TestServeContentErrorMessages(t *testing.T) {
+ defer afterTest(t)
+ fs := fakeFS{
+ "/500": &fakeFileInfo{
+ err: errors.New("random error"),
+ },
+ "/403": &fakeFileInfo{
+ err: &os.PathError{Err: os.ErrPermission},
+ },
+ }
+ ts := httptest.NewServer(FileServer(fs))
+ defer ts.Close()
+ for _, code := range []int{403, 404, 500} {
+ res, err := DefaultClient.Get(fmt.Sprintf("%s/%d", ts.URL, code))
+ if err != nil {
+ t.Errorf("Error fetching /%d: %v", code, err)
+ continue
+ }
+ if res.StatusCode != code {
+ t.Errorf("For /%d, status code = %d; want %d", code, res.StatusCode, code)
+ }
+ res.Body.Close()
+ }
+}
+
// verifies that sendfile is being used on Linux
func TestLinuxSendfile(t *testing.T) {
defer afterTest(t)
diff --git a/src/net/http/lex.go b/src/net/http/lex.go
index cb33318f49..50b14f8b32 100644
--- a/src/net/http/lex.go
+++ b/src/net/http/lex.go
@@ -4,6 +4,11 @@
package http
+import (
+ "strings"
+ "unicode/utf8"
+)
+
// This file deals with lexical matters of HTTP
var isTokenTable = [127]bool{
@@ -94,3 +99,71 @@ func isToken(r rune) bool {
func isNotToken(r rune) bool {
return !isToken(r)
}
+
+// headerValuesContainsToken reports whether any string in values
+// contains the provided token, ASCII case-insensitively.
+func headerValuesContainsToken(values []string, token string) bool {
+ for _, v := range values {
+ if headerValueContainsToken(v, token) {
+ return true
+ }
+ }
+ return false
+}
+
+// isOWS reports whether b is an optional whitespace byte, as defined
+// by RFC 7230 section 3.2.3.
+func isOWS(b byte) bool { return b == ' ' || b == '\t' }
+
+// trimOWS returns x with all optional whitespace removes from the
+// beginning and end.
+func trimOWS(x string) string {
+ // TODO: consider using strings.Trim(x, " \t") instead,
+ // if and when it's fast enough. See issue 10292.
+ // But this ASCII-only code will probably always beat UTF-8
+ // aware code.
+ for len(x) > 0 && isOWS(x[0]) {
+ x = x[1:]
+ }
+ for len(x) > 0 && isOWS(x[len(x)-1]) {
+ x = x[:len(x)-1]
+ }
+ return x
+}
+
+// headerValueContainsToken reports whether v (assumed to be a
+// 0#element, in the ABNF extension described in RFC 7230 section 7)
+// contains token amongst its comma-separated tokens, ASCII
+// case-insensitively.
+func headerValueContainsToken(v string, token string) bool {
+ v = trimOWS(v)
+ if comma := strings.IndexByte(v, ','); comma != -1 {
+ return tokenEqual(trimOWS(v[:comma]), token) || headerValueContainsToken(v[comma+1:], token)
+ }
+ return tokenEqual(v, token)
+}
+
+// lowerASCII returns the ASCII lowercase version of b.
+func lowerASCII(b byte) byte {
+ if 'A' <= b && b <= 'Z' {
+ return b + ('a' - 'A')
+ }
+ return b
+}
+
+// tokenEqual reports whether t1 and t2 are equal, ASCII case-insensitively.
+func tokenEqual(t1, t2 string) bool {
+ if len(t1) != len(t2) {
+ return false
+ }
+ for i, b := range t1 {
+ if b >= utf8.RuneSelf {
+ // No UTF-8 or non-ASCII allowed in tokens.
+ return false
+ }
+ if lowerASCII(byte(b)) != lowerASCII(t2[i]) {
+ return false
+ }
+ }
+ return true
+}
diff --git a/src/net/http/lex_test.go b/src/net/http/lex_test.go
index 6d9d294f70..986fda17dc 100644
--- a/src/net/http/lex_test.go
+++ b/src/net/http/lex_test.go
@@ -29,3 +29,73 @@ func TestIsToken(t *testing.T) {
}
}
}
+
+func TestHeaderValuesContainsToken(t *testing.T) {
+ tests := []struct {
+ vals []string
+ token string
+ want bool
+ }{
+ {
+ vals: []string{"foo"},
+ token: "foo",
+ want: true,
+ },
+ {
+ vals: []string{"bar", "foo"},
+ token: "foo",
+ want: true,
+ },
+ {
+ vals: []string{"foo"},
+ token: "FOO",
+ want: true,
+ },
+ {
+ vals: []string{"foo"},
+ token: "bar",
+ want: false,
+ },
+ {
+ vals: []string{" foo "},
+ token: "FOO",
+ want: true,
+ },
+ {
+ vals: []string{"foo,bar"},
+ token: "FOO",
+ want: true,
+ },
+ {
+ vals: []string{"bar,foo,bar"},
+ token: "FOO",
+ want: true,
+ },
+ {
+ vals: []string{"bar , foo"},
+ token: "FOO",
+ want: true,
+ },
+ {
+ vals: []string{"foo ,bar "},
+ token: "FOO",
+ want: true,
+ },
+ {
+ vals: []string{"bar, foo ,bar"},
+ token: "FOO",
+ want: true,
+ },
+ {
+ vals: []string{"bar , foo"},
+ token: "FOO",
+ want: true,
+ },
+ }
+ for _, tt := range tests {
+ got := headerValuesContainsToken(tt.vals, tt.token)
+ if got != tt.want {
+ t.Errorf("headerValuesContainsToken(%q, %q) = %v; want %v", tt.vals, tt.token, got, tt.want)
+ }
+ }
+}
diff --git a/src/net/http/main_test.go b/src/net/http/main_test.go
index c7407df707..12eea6f0e1 100644
--- a/src/net/http/main_test.go
+++ b/src/net/http/main_test.go
@@ -56,17 +56,21 @@ func goroutineLeaked() bool {
// not counting goroutines for leakage in -short mode
return false
}
- gs := interestingGoroutines()
- n := 0
- stackCount := make(map[string]int)
- for _, g := range gs {
- stackCount[g]++
- n++
- }
-
- if n == 0 {
- return false
+ var stackCount map[string]int
+ for i := 0; i < 5; i++ {
+ n := 0
+ stackCount = make(map[string]int)
+ gs := interestingGoroutines()
+ for _, g := range gs {
+ stackCount[g]++
+ n++
+ }
+ if n == 0 {
+ return false
+ }
+ // Wait for goroutines to schedule and die off:
+ time.Sleep(100 * time.Millisecond)
}
fmt.Fprintf(os.Stderr, "Too many goroutines running after net/http test(s).\n")
for stack, count := range stackCount {
diff --git a/src/net/http/npn_test.go b/src/net/http/npn_test.go
index 98b8930d06..e2e911d3dd 100644
--- a/src/net/http/npn_test.go
+++ b/src/net/http/npn_test.go
@@ -6,6 +6,7 @@ package http_test
import (
"bufio"
+ "bytes"
"crypto/tls"
"fmt"
"io"
@@ -17,6 +18,7 @@ import (
)
func TestNextProtoUpgrade(t *testing.T) {
+ defer afterTest(t)
ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {
fmt.Fprintf(w, "path=%s,proto=", r.URL.Path)
if r.TLS != nil {
@@ -38,12 +40,12 @@ func TestNextProtoUpgrade(t *testing.T) {
ts.StartTLS()
defer ts.Close()
- tr := newTLSTransport(t, ts)
- defer tr.CloseIdleConnections()
- c := &Client{Transport: tr}
-
// Normal request, without NPN.
{
+ tr := newTLSTransport(t, ts)
+ defer tr.CloseIdleConnections()
+ c := &Client{Transport: tr}
+
res, err := c.Get(ts.URL)
if err != nil {
t.Fatal(err)
@@ -60,11 +62,17 @@ func TestNextProtoUpgrade(t *testing.T) {
// Request to an advertised but unhandled NPN protocol.
// Server will hang up.
{
- tr.CloseIdleConnections()
+ tr := newTLSTransport(t, ts)
tr.TLSClientConfig.NextProtos = []string{"unhandled-proto"}
- _, err := c.Get(ts.URL)
+ defer tr.CloseIdleConnections()
+ c := &Client{Transport: tr}
+
+ res, err := c.Get(ts.URL)
if err == nil {
- t.Errorf("expected error on unhandled-proto request")
+ defer res.Body.Close()
+ var buf bytes.Buffer
+ res.Write(&buf)
+ t.Errorf("expected error on unhandled-proto request; got: %s", buf.Bytes())
}
}
diff --git a/src/net/http/request.go b/src/net/http/request.go
index 639a579bdf..43d9de378a 100644
--- a/src/net/http/request.go
+++ b/src/net/http/request.go
@@ -169,8 +169,9 @@ type Request struct {
// The HTTP client ignores Form and uses Body instead.
Form url.Values
- // PostForm contains the parsed form data from POST or PUT
- // body parameters.
+ // PostForm contains the parsed form data from POST, PATCH,
+ // or PUT body parameters.
+ //
// This field is only available after ParseForm is called.
// The HTTP client ignores PostForm and uses Body instead.
PostForm url.Values
@@ -361,12 +362,15 @@ func (r *Request) WriteProxy(w io.Writer) error {
// extraHeaders may be nil
func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) error {
- host := req.Host
+ // According to RFC 6874, an HTTP client, proxy, or other
+ // intermediary must remove any IPv6 zone identifier attached
+ // to an outgoing URI.
+ host := removeZone(req.Host)
if host == "" {
if req.URL == nil {
return errors.New("http: Request.Write on Request with no Host or URL set")
}
- host = req.URL.Host
+ host = removeZone(req.URL.Host)
}
ruri := req.URL.RequestURI()
@@ -453,6 +457,23 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err
return nil
}
+// removeZone removes IPv6 zone identifer from host.
+// E.g., "[fe80::1%en0]:8080" to "[fe80::1]:8080"
+func removeZone(host string) string {
+ if !strings.HasPrefix(host, "[") {
+ return host
+ }
+ i := strings.LastIndex(host, "]")
+ if i < 0 {
+ return host
+ }
+ j := strings.LastIndex(host[:i], "%")
+ if j < 0 {
+ return host
+ }
+ return host[:j] + host[i:]
+}
+
// ParseHTTPVersion parses a HTTP version string.
// "HTTP/1.0" returns (1, 0, true).
func ParseHTTPVersion(vers string) (major, minor int, ok bool) {
@@ -486,6 +507,13 @@ func ParseHTTPVersion(vers string) (major, minor int, ok bool) {
// If the provided body is also an io.Closer, the returned
// Request.Body is set to body and will be closed by the Client
// methods Do, Post, and PostForm, and Transport.RoundTrip.
+//
+// NewRequest returns a Request suitable for use with Client.Do or
+// Transport.RoundTrip.
+// To create a request for use with testing a Server Handler use either
+// ReadRequest or manually update the Request fields. See the Request
+// type's documentation for the difference between inbound and outbound
+// request fields.
func NewRequest(method, urlStr string, body io.Reader) (*Request, error) {
u, err := url.Parse(urlStr)
if err != nil {
@@ -585,7 +613,7 @@ func putTextprotoReader(r *textproto.Reader) {
textprotoReaderPool.Put(r)
}
-// ReadRequest reads and parses a request from b.
+// ReadRequest reads and parses an incoming request from b.
func ReadRequest(b *bufio.Reader) (req *Request, err error) {
tp := newTextprotoReader(b)
diff --git a/src/net/http/request_test.go b/src/net/http/request_test.go
index 671841ff70..a518b00449 100644
--- a/src/net/http/request_test.go
+++ b/src/net/http/request_test.go
@@ -178,6 +178,7 @@ func TestParseMultipartForm(t *testing.T) {
}
func TestRedirect(t *testing.T) {
+ defer afterTest(t)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
switch r.URL.Path {
case "/":
@@ -326,13 +327,31 @@ func TestReadRequestErrors(t *testing.T) {
}
}
+var newRequestHostTests = []struct {
+ in, out string
+}{
+ {"http://www.example.com/", "www.example.com"},
+ {"http://www.example.com:8080/", "www.example.com:8080"},
+
+ {"http://192.168.0.1/", "192.168.0.1"},
+ {"http://192.168.0.1:8080/", "192.168.0.1:8080"},
+
+ {"http://[fe80::1]/", "[fe80::1]"},
+ {"http://[fe80::1]:8080/", "[fe80::1]:8080"},
+ {"http://[fe80::1%25en0]/", "[fe80::1%en0]"},
+ {"http://[fe80::1%25en0]:8080/", "[fe80::1%en0]:8080"},
+}
+
func TestNewRequestHost(t *testing.T) {
- req, err := NewRequest("GET", "http://localhost:1234/", nil)
- if err != nil {
- t.Fatal(err)
- }
- if req.Host != "localhost:1234" {
- t.Errorf("Host = %q; want localhost:1234", req.Host)
+ for i, tt := range newRequestHostTests {
+ req, err := NewRequest("GET", tt.in, nil)
+ if err != nil {
+ t.Errorf("#%v: %v", i, err)
+ continue
+ }
+ if req.Host != tt.out {
+ t.Errorf("got %q; want %q", req.Host, tt.out)
+ }
}
}
diff --git a/src/net/http/requestwrite_test.go b/src/net/http/requestwrite_test.go
index 7a6bd58786..e9a5f5f080 100644
--- a/src/net/http/requestwrite_test.go
+++ b/src/net/http/requestwrite_test.go
@@ -455,6 +455,37 @@ var reqWriteTests = []reqWriteTest{
"ALL-CAPS: x\r\n" +
"\r\n",
},
+
+ // Request with host header field; IPv6 address with zone identifier
+ {
+ Req: Request{
+ Method: "GET",
+ URL: &url.URL{
+ Host: "[fe80::1%en0]",
+ },
+ },
+
+ WantWrite: "GET / HTTP/1.1\r\n" +
+ "Host: [fe80::1]\r\n" +
+ "User-Agent: Go 1.1 package http\r\n" +
+ "\r\n",
+ },
+
+ // Request with optional host header field; IPv6 address with zone identifier
+ {
+ Req: Request{
+ Method: "GET",
+ URL: &url.URL{
+ Host: "www.example.com",
+ },
+ Host: "[fe80::1%en0]:8080",
+ },
+
+ WantWrite: "GET / HTTP/1.1\r\n" +
+ "Host: [fe80::1]:8080\r\n" +
+ "User-Agent: Go 1.1 package http\r\n" +
+ "\r\n",
+ },
}
func TestRequestWrite(t *testing.T) {
diff --git a/src/net/http/response.go b/src/net/http/response.go
index cfe695ce5c..4afecda130 100644
--- a/src/net/http/response.go
+++ b/src/net/http/response.go
@@ -189,8 +189,10 @@ func (r *Response) ProtoAtLeast(major, minor int) bool {
r.ProtoMajor == major && r.ProtoMinor >= minor
}
-// Writes the response (header, body and trailer) in wire format. This method
-// consults the following fields of the response:
+// Write writes r to w in the HTTP/1.n server response format,
+// including the status line, headers, body, and optional trailer.
+//
+// This method consults the following fields of the response r:
//
// StatusCode
// ProtoMajor
@@ -202,7 +204,7 @@ func (r *Response) ProtoAtLeast(major, minor int) bool {
// ContentLength
// Header, values for non-canonical keys will have unpredictable behavior
//
-// Body is closed after it is sent.
+// The Response Body is closed after it is sent.
func (r *Response) Write(w io.Writer) error {
// Status line
text := r.Status
diff --git a/src/net/http/response_test.go b/src/net/http/response_test.go
index 06e940d9ab..421cf55f49 100644
--- a/src/net/http/response_test.go
+++ b/src/net/http/response_test.go
@@ -405,6 +405,57 @@ some body`,
"foobar",
},
+
+ // Both keep-alive and close, on the same Connection line. (Issue 8840)
+ {
+ "HTTP/1.1 200 OK\r\n" +
+ "Content-Length: 256\r\n" +
+ "Connection: keep-alive, close\r\n" +
+ "\r\n",
+
+ Response{
+ Status: "200 OK",
+ StatusCode: 200,
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Request: dummyReq("HEAD"),
+ Header: Header{
+ "Content-Length": {"256"},
+ },
+ TransferEncoding: nil,
+ Close: true,
+ ContentLength: 256,
+ },
+
+ "",
+ },
+
+ // Both keep-alive and close, on different Connection lines. (Issue 8840)
+ {
+ "HTTP/1.1 200 OK\r\n" +
+ "Content-Length: 256\r\n" +
+ "Connection: keep-alive\r\n" +
+ "Connection: close\r\n" +
+ "\r\n",
+
+ Response{
+ Status: "200 OK",
+ StatusCode: 200,
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Request: dummyReq("HEAD"),
+ Header: Header{
+ "Content-Length": {"256"},
+ },
+ TransferEncoding: nil,
+ Close: true,
+ ContentLength: 256,
+ },
+
+ "",
+ },
}
func TestReadResponse(t *testing.T) {
diff --git a/src/net/http/responsewrite_test.go b/src/net/http/responsewrite_test.go
index 585b13b850..5b8d47ab58 100644
--- a/src/net/http/responsewrite_test.go
+++ b/src/net/http/responsewrite_test.go
@@ -207,6 +207,21 @@ func TestResponseWrite(t *testing.T) {
},
"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
},
+
+ // When a response to a POST has Content-Length: -1, make sure we don't
+ // write the Content-Length as -1.
+ {
+ Response{
+ StatusCode: StatusOK,
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Request: &Request{Method: "POST"},
+ Header: Header{},
+ ContentLength: -1,
+ Body: ioutil.NopCloser(strings.NewReader("abcdef")),
+ },
+ "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\nabcdef",
+ },
}
for i := range respWriteTests {
diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go
index c21b57b57e..6cbe24b6b5 100644
--- a/src/net/http/serve_test.go
+++ b/src/net/http/serve_test.go
@@ -146,6 +146,7 @@ func (ht handlerTest) rawResponse(req string) string {
}
func TestConsumingBodyOnNextConn(t *testing.T) {
+ defer afterTest(t)
conn := new(testConn)
for i := 0; i < 2; i++ {
conn.readBuf.Write([]byte(
@@ -1451,19 +1452,23 @@ func testHandlerPanic(t *testing.T, withHijack bool, panicValue interface{}) {
}
}
-func TestNoDate(t *testing.T) {
+func TestServerNoDate(t *testing.T) { testServerNoHeader(t, "Date") }
+func TestServerNoContentType(t *testing.T) { testServerNoHeader(t, "Content-Type") }
+
+func testServerNoHeader(t *testing.T, header string) {
defer afterTest(t)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
- w.Header()["Date"] = nil
+ w.Header()[header] = nil
+ io.WriteString(w, "foo") // non-empty
}))
defer ts.Close()
res, err := Get(ts.URL)
if err != nil {
t.Fatal(err)
}
- _, present := res.Header["Date"]
- if present {
- t.Fatalf("Expected no Date header; got %v", res.Header["Date"])
+ res.Body.Close()
+ if got, ok := res.Header[header]; ok {
+ t.Fatalf("Expected no %s header; got %q", header, got)
}
}
@@ -2762,6 +2767,43 @@ func TestServerKeepAliveAfterWriteError(t *testing.T) {
}
}
+// Issue 9987: shouldn't add automatic Content-Length (or
+// Content-Type) if a Transfer-Encoding was set by the handler.
+func TestNoContentLengthIfTransferEncoding(t *testing.T) {
+ defer afterTest(t)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.Header().Set("Transfer-Encoding", "foo")
+ io.WriteString(w, "")
+ }))
+ defer ts.Close()
+ c, err := net.Dial("tcp", ts.Listener.Addr().String())
+ if err != nil {
+ t.Fatalf("Dial: %v", err)
+ }
+ defer c.Close()
+ if _, err := io.WriteString(c, "GET / HTTP/1.1\r\nHost: foo\r\n\r\n"); err != nil {
+ t.Fatal(err)
+ }
+ bs := bufio.NewScanner(c)
+ var got bytes.Buffer
+ for bs.Scan() {
+ if strings.TrimSpace(bs.Text()) == "" {
+ break
+ }
+ got.WriteString(bs.Text())
+ got.WriteByte('\n')
+ }
+ if err := bs.Err(); err != nil {
+ t.Fatal(err)
+ }
+ if strings.Contains(got.String(), "Content-Length") {
+ t.Errorf("Unexpected Content-Length in response headers: %s", got.String())
+ }
+ if strings.Contains(got.String(), "Content-Type") {
+ t.Errorf("Unexpected Content-Type in response headers: %s", got.String())
+ }
+}
+
func BenchmarkClientServer(b *testing.B) {
b.ReportAllocs()
b.StopTimer()
diff --git a/src/net/http/server.go b/src/net/http/server.go
index 565c87d392..dbd629210e 100644
--- a/src/net/http/server.go
+++ b/src/net/http/server.go
@@ -61,6 +61,7 @@ type ResponseWriter interface {
// WriteHeader (or Write) has no effect unless the modified
// headers were declared as trailers by setting the
// "Trailer" header before the call to WriteHeader.
+ // To suppress implicit response headers, set their value to nil.
Header() Header
// Write writes the data to the connection as part of an HTTP reply.
@@ -780,6 +781,9 @@ func (cw *chunkWriter) writeHeader(p []byte) {
foreachHeaderElement(v, cw.res.declareTrailer)
}
+ te := header.get("Transfer-Encoding")
+ hasTE := te != ""
+
// If the handler is done but never sent a Content-Length
// response header and this is our first (and last) write, set
// it, even to zero. This helps HTTP/1.0 clients keep their
@@ -792,7 +796,9 @@ func (cw *chunkWriter) writeHeader(p []byte) {
// write non-zero bytes. If it's actually 0 bytes and the
// handler never looked at the Request.Method, we just don't
// send a Content-Length header.
- if w.handlerDone && !trailers && bodyAllowedForStatus(w.status) && header.get("Content-Length") == "" && (!isHEAD || len(p) > 0) {
+ // Further, we don't send an automatic Content-Length if they
+ // set a Transfer-Encoding, because they're generally incompatible.
+ if w.handlerDone && !trailers && !hasTE && bodyAllowedForStatus(w.status) && header.get("Content-Length") == "" && (!isHEAD || len(p) > 0) {
w.contentLength = int64(len(p))
setHeader.contentLength = strconv.AppendInt(cw.res.clenBuf[:0], int64(len(p)), 10)
}
@@ -844,7 +850,7 @@ func (cw *chunkWriter) writeHeader(p []byte) {
if bodyAllowedForStatus(code) {
// If no content type, apply sniffing algorithm to body.
_, haveType := header["Content-Type"]
- if !haveType {
+ if !haveType && !hasTE {
setHeader.contentType = DetectContentType(p)
}
} else {
@@ -857,8 +863,6 @@ func (cw *chunkWriter) writeHeader(p []byte) {
setHeader.date = appendTime(cw.res.dateBuf[:0], time.Now())
}
- te := header.get("Transfer-Encoding")
- hasTE := te != ""
if hasCL && hasTE && te != "identity" {
// TODO: return an error if WriteHeader gets a return parameter
// For now just ignore the Content-Length.
diff --git a/src/net/http/transfer.go b/src/net/http/transfer.go
index c39d6cff67..5640344345 100644
--- a/src/net/http/transfer.go
+++ b/src/net/http/transfer.go
@@ -138,6 +138,9 @@ func (t *transferWriter) shouldSendContentLength() bool {
if t.ContentLength > 0 {
return true
}
+ if t.ContentLength < 0 {
+ return false
+ }
// Many servers expect a Content-Length for these methods
if t.Method == "POST" || t.Method == "PUT" {
return true
@@ -505,14 +508,13 @@ func shouldClose(major, minor int, header Header, removeCloseHeader bool) bool {
if major < 1 {
return true
} else if major == 1 && minor == 0 {
- if !strings.Contains(strings.ToLower(header.get("Connection")), "keep-alive") {
+ vv := header["Connection"]
+ if headerValuesContainsToken(vv, "close") || !headerValuesContainsToken(vv, "keep-alive") {
return true
}
return false
} else {
- // TODO: Should split on commas, toss surrounding white space,
- // and check each field.
- if strings.ToLower(header.get("Connection")) == "close" {
+ if headerValuesContainsToken(header["Connection"], "close") {
if removeCloseHeader {
header.Del("Connection")
}
diff --git a/src/net/http/transport.go b/src/net/http/transport.go
index b18e445cbc..5de5d944af 100644
--- a/src/net/http/transport.go
+++ b/src/net/http/transport.go
@@ -279,6 +279,7 @@ func (t *Transport) CloseIdleConnections() {
func (t *Transport) CancelRequest(req *Request) {
t.reqMu.Lock()
cancel := t.reqCanceler[req]
+ delete(t.reqCanceler, req)
t.reqMu.Unlock()
if cancel != nil {
cancel()
@@ -474,6 +475,25 @@ func (t *Transport) setReqCanceler(r *Request, fn func()) {
}
}
+// replaceReqCanceler replaces an existing cancel function. If there is no cancel function
+// for the request, we don't set the function and return false.
+// Since CancelRequest will clear the canceler, we can use the return value to detect if
+// the request was canceled since the last setReqCancel call.
+func (t *Transport) replaceReqCanceler(r *Request, fn func()) bool {
+ t.reqMu.Lock()
+ defer t.reqMu.Unlock()
+ _, ok := t.reqCanceler[r]
+ if !ok {
+ return false
+ }
+ if fn != nil {
+ t.reqCanceler[r] = fn
+ } else {
+ delete(t.reqCanceler, r)
+ }
+ return true
+}
+
func (t *Transport) dial(network, addr string) (c net.Conn, err error) {
if t.Dial != nil {
return t.Dial(network, addr)
@@ -490,6 +510,10 @@ var prePendingDial, postPendingDial func()
// is ready to write requests to.
func (t *Transport) getConn(req *Request, cm connectMethod) (*persistConn, error) {
if pc := t.getIdleConn(cm); pc != nil {
+ // set request canceler to some non-nil function so we
+ // can detect whether it was cleared between now and when
+ // we enter roundTrip
+ t.setReqCanceler(req, func() {})
return pc, nil
}
@@ -805,6 +829,7 @@ type persistConn struct {
numExpectedResponses int
closed bool // whether conn has been closed
broken bool // an error has happened on this connection; marked broken so it's not reused.
+ canceled bool // whether this conn was broken due a CancelRequest
// mutateHeaderFunc is an optional func to modify extra
// headers on each outbound request before it's written. (the
// original Request given to RoundTrip is not modified)
@@ -819,8 +844,18 @@ func (pc *persistConn) isBroken() bool {
return b
}
+// isCanceled reports whether this connection was closed due to CancelRequest.
+func (pc *persistConn) isCanceled() bool {
+ pc.lk.Lock()
+ defer pc.lk.Unlock()
+ return pc.canceled
+}
+
func (pc *persistConn) cancelRequest() {
- pc.conn.Close()
+ pc.lk.Lock()
+ defer pc.lk.Unlock()
+ pc.canceled = true
+ pc.closeLocked()
}
var remoteSideClosedFunc func(error) bool // or nil to use default
@@ -836,8 +871,18 @@ func remoteSideClosed(err error) bool {
}
func (pc *persistConn) readLoop() {
- alive := true
+ // eofc is used to block http.Handler goroutines reading from Response.Body
+ // at EOF until this goroutines has (potentially) added the connection
+ // back to the idle pool.
+ eofc := make(chan struct{})
+ defer close(eofc) // unblock reader on errors
+ // Read this once, before loop starts. (to avoid races in tests)
+ testHookMu.Lock()
+ testHookReadLoopBeforeNextRead := testHookReadLoopBeforeNextRead
+ testHookMu.Unlock()
+
+ alive := true
for alive {
pb, err := pc.br.Peek(1)
@@ -895,49 +940,70 @@ func (pc *persistConn) readLoop() {
alive = false
}
- var waitForBodyRead chan bool
+ var waitForBodyRead chan bool // channel is nil when there's no body
if hasBody {
waitForBodyRead = make(chan bool, 2)
resp.Body.(*bodyEOFSignal).earlyCloseFn = func() error {
- // Sending false here sets alive to
- // false and closes the connection
- // below.
waitForBodyRead <- false
return nil
}
- resp.Body.(*bodyEOFSignal).fn = func(err error) {
- waitForBodyRead <- alive &&
- err == nil &&
- !pc.sawEOF &&
- pc.wroteRequest() &&
- pc.t.putIdleConn(pc)
+ resp.Body.(*bodyEOFSignal).fn = func(err error) error {
+ isEOF := err == io.EOF
+ waitForBodyRead <- isEOF
+ if isEOF {
+ <-eofc // see comment at top
+ } else if err != nil && pc.isCanceled() {
+ return errRequestCanceled
+ }
+ return err
}
}
- if alive && !hasBody {
- alive = !pc.sawEOF &&
+ pc.lk.Lock()
+ pc.numExpectedResponses--
+ pc.lk.Unlock()
+
+ // The connection might be going away when we put the
+ // idleConn below. When that happens, we close the response channel to signal
+ // to roundTrip that the connection is gone. roundTrip waits for
+ // both closing and a response in a select, so it might choose
+ // the close channel, rather than the response.
+ // We send the response first so that roundTrip can check
+ // if there is a pending one with a non-blocking select
+ // on the response channel before erroring out.
+ rc.ch <- responseAndError{resp, err}
+
+ if hasBody {
+ // To avoid a race, wait for the just-returned
+ // response body to be fully consumed before peek on
+ // the underlying bufio reader.
+ select {
+ case bodyEOF := <-waitForBodyRead:
+ pc.t.setReqCanceler(rc.req, nil) // before pc might return to idle pool
+ alive = alive &&
+ bodyEOF &&
+ !pc.sawEOF &&
+ pc.wroteRequest() &&
+ pc.t.putIdleConn(pc)
+ if bodyEOF {
+ eofc <- struct{}{}
+ }
+ case <-pc.closech:
+ alive = false
+ }
+ } else {
+ pc.t.setReqCanceler(rc.req, nil) // before pc might return to idle pool
+ alive = alive &&
+ !pc.sawEOF &&
pc.wroteRequest() &&
pc.t.putIdleConn(pc)
}
- rc.ch <- responseAndError{resp, err}
-
- // Wait for the just-returned response body to be fully consumed
- // before we race and peek on the underlying bufio reader.
- if waitForBodyRead != nil {
- select {
- case alive = <-waitForBodyRead:
- case <-pc.closech:
- alive = false
- }
- }
-
- pc.t.setReqCanceler(rc.req, nil)
-
- if !alive {
- pc.close()
+ if hook := testHookReadLoopBeforeNextRead; hook != nil {
+ hook()
}
}
+ pc.close()
}
func (pc *persistConn) writeLoop() {
@@ -1027,9 +1093,24 @@ func (e *httpError) Temporary() bool { return true }
var errTimeout error = &httpError{err: "net/http: timeout awaiting response headers", timeout: true}
var errClosed error = &httpError{err: "net/http: transport closed before response was received"}
+var errRequestCanceled = errors.New("net/http: request canceled")
+
+// nil except for tests
+var (
+ testHookPersistConnClosedGotRes func()
+ testHookEnterRoundTrip func()
+ testHookMu sync.Locker = fakeLocker{} // guards following
+ testHookReadLoopBeforeNextRead func()
+)
func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err error) {
- pc.t.setReqCanceler(req.Request, pc.cancelRequest)
+ if hook := testHookEnterRoundTrip; hook != nil {
+ hook()
+ }
+ if !pc.t.replaceReqCanceler(req.Request, pc.cancelRequest) {
+ pc.t.putIdleConn(pc)
+ return nil, errRequestCanceled
+ }
pc.lk.Lock()
pc.numExpectedResponses++
headerFn := pc.mutateHeaderFunc
@@ -1078,8 +1159,6 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err
pc.reqch <- requestAndChan{req.Request, resc, requestedGzip}
var re responseAndError
- var pconnDeadCh = pc.closech
- var failTicker <-chan time.Time
var respHeaderTimer <-chan time.Time
WaitResponse:
for {
@@ -1095,23 +1174,25 @@ WaitResponse:
defer timer.Stop() // prevent leaks
respHeaderTimer = timer.C
}
- case <-pconnDeadCh:
+ case <-pc.closech:
// The persist connection is dead. This shouldn't
// usually happen (only with Connection: close responses
// with no response bodies), but if it does happen it
// means either a) the remote server hung up on us
// prematurely, or b) the readLoop sent us a response &
// closed its closech at roughly the same time, and we
- // selected this case first, in which case a response
- // might still be coming soon.
- //
- // We can't avoid the select race in b) by using a unbuffered
- // resc channel instead, because then goroutines can
- // leak if we exit due to other errors.
- pconnDeadCh = nil // avoid spinning
- failTicker = time.After(100 * time.Millisecond) // arbitrary time to wait for resc
- case <-failTicker:
- re = responseAndError{err: errClosed}
+ // selected this case first. If we got a response, readLoop makes sure
+ // to send it before it puts the conn and closes the channel.
+ // That way, we can fetch the response, if there is one,
+ // with a non-blocking receive.
+ select {
+ case re = <-resc:
+ if fn := testHookPersistConnClosedGotRes; fn != nil {
+ fn()
+ }
+ default:
+ re = responseAndError{err: errClosed}
+ }
break WaitResponse
case <-respHeaderTimer:
pc.close()
@@ -1122,10 +1203,6 @@ WaitResponse:
}
}
- pc.lk.Lock()
- pc.numExpectedResponses--
- pc.lk.Unlock()
-
if re.err != nil {
pc.t.setReqCanceler(req.Request, nil)
}
@@ -1173,16 +1250,18 @@ func canonicalAddr(url *url.URL) string {
// bodyEOFSignal wraps a ReadCloser but runs fn (if non-nil) at most
// once, right before its final (error-producing) Read or Close call
-// returns. If earlyCloseFn is non-nil and Close is called before
-// io.EOF is seen, earlyCloseFn is called instead of fn, and its
-// return value is the return value from Close.
+// returns. fn should return the new error to return from Read or Close.
+//
+// If earlyCloseFn is non-nil and Close is called before io.EOF is
+// seen, earlyCloseFn is called instead of fn, and its return value is
+// the return value from Close.
type bodyEOFSignal struct {
body io.ReadCloser
- mu sync.Mutex // guards following 4 fields
- closed bool // whether Close has been called
- rerr error // sticky Read error
- fn func(error) // error will be nil on Read io.EOF
- earlyCloseFn func() error // optional alt Close func used if io.EOF not seen
+ mu sync.Mutex // guards following 4 fields
+ closed bool // whether Close has been called
+ rerr error // sticky Read error
+ fn func(error) error // err will be nil on Read io.EOF
+ earlyCloseFn func() error // optional alt Close func used if io.EOF not seen
}
func (es *bodyEOFSignal) Read(p []byte) (n int, err error) {
@@ -1203,7 +1282,7 @@ func (es *bodyEOFSignal) Read(p []byte) (n int, err error) {
if es.rerr == nil {
es.rerr = err
}
- es.condfn(err)
+ err = es.condfn(err)
}
return
}
@@ -1219,20 +1298,17 @@ func (es *bodyEOFSignal) Close() error {
return es.earlyCloseFn()
}
err := es.body.Close()
- es.condfn(err)
- return err
+ return es.condfn(err)
}
// caller must hold es.mu.
-func (es *bodyEOFSignal) condfn(err error) {
+func (es *bodyEOFSignal) condfn(err error) error {
if es.fn == nil {
- return
+ return err
}
- if err == io.EOF {
- err = nil
- }
- es.fn(err)
+ err = es.fn(err)
es.fn = nil
+ return err
}
// gzipReader wraps a response body so it can lazily
@@ -1279,3 +1355,11 @@ func (nr noteEOFReader) Read(p []byte) (n int, err error) {
}
return
}
+
+// fakeLocker is a sync.Locker which does nothing. It's used to guard
+// test-only fields when not under test, to avoid runtime atomic
+// overhead.
+type fakeLocker struct{}
+
+func (fakeLocker) Lock() {}
+func (fakeLocker) Unlock() {}
diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go
index 504a6a7b56..ace58896b8 100644
--- a/src/net/http/transport_test.go
+++ b/src/net/http/transport_test.go
@@ -505,12 +505,17 @@ func TestStressSurpriseServerCloses(t *testing.T) {
tr := &Transport{DisableKeepAlives: false}
c := &Client{Transport: tr}
+ defer tr.CloseIdleConnections()
// Do a bunch of traffic from different goroutines. Send to activityc
// after each request completes, regardless of whether it failed.
+ // If these are too high, OS X exhausts its emphemeral ports
+ // and hangs waiting for them to transition TCP states. That's
+ // not what we want to test. TODO(bradfitz): use an io.Pipe
+ // dialer for this test instead?
const (
- numClients = 50
- reqsPerClient = 250
+ numClients = 20
+ reqsPerClient = 25
)
activityc := make(chan bool)
for i := 0; i < numClients; i++ {
@@ -1371,8 +1376,8 @@ func TestTransportCancelRequest(t *testing.T) {
body, err := ioutil.ReadAll(res.Body)
d := time.Since(t0)
- if err == nil {
- t.Error("expected an error reading the body")
+ if err != ExportErrRequestCanceled {
+ t.Errorf("Body.Read error = %v; want errRequestCanceled", err)
}
if string(body) != "Hello" {
t.Errorf("Body = %q; want Hello", body)
@@ -1382,7 +1387,7 @@ func TestTransportCancelRequest(t *testing.T) {
}
// Verify no outstanding requests after readLoop/writeLoop
// goroutines shut down.
- for tries := 3; tries > 0; tries-- {
+ for tries := 5; tries > 0; tries-- {
n := tr.NumPendingRequestsForTesting()
if n == 0 {
break
@@ -1431,6 +1436,7 @@ func TestTransportCancelRequestInDial(t *testing.T) {
eventLog.Printf("canceling")
tr.CancelRequest(req)
+ tr.CancelRequest(req) // used to panic on second call
select {
case <-gotres:
@@ -1821,6 +1827,11 @@ func TestIdleConnChannelLeak(t *testing.T) {
}))
defer ts.Close()
+ const nReqs = 5
+ didRead := make(chan bool, nReqs)
+ SetReadLoopBeforeNextReadHook(func() { didRead <- true })
+ defer SetReadLoopBeforeNextReadHook(nil)
+
tr := &Transport{
Dial: func(netw, addr string) (net.Conn, error) {
return net.Dial(netw, ts.Listener.Addr().String())
@@ -1833,12 +1844,28 @@ func TestIdleConnChannelLeak(t *testing.T) {
// First, without keep-alives.
for _, disableKeep := range []bool{true, false} {
tr.DisableKeepAlives = disableKeep
- for i := 0; i < 5; i++ {
+ for i := 0; i < nReqs; i++ {
_, err := c.Get(fmt.Sprintf("http://foo-host-%d.tld/", i))
if err != nil {
t.Fatal(err)
}
+ // Note: no res.Body.Close is needed here, since the
+ // response Content-Length is zero. Perhaps the test
+ // should be more explicit and use a HEAD, but tests
+ // elsewhere guarantee that zero byte responses generate
+ // a "Content-Length: 0" instead of chunking.
}
+
+ // At this point, each of the 5 Transport.readLoop goroutines
+ // are scheduling noting that there are no response bodies (see
+ // earlier comment), and are then calling putIdleConn, which
+ // decrements this count. Usually that happens quickly, which is
+ // why this test has seemed to work for ages. But it's still
+ // racey: we have wait for them to finish first. See Issue 10427
+ for i := 0; i < nReqs; i++ {
+ <-didRead
+ }
+
if got := tr.IdleConnChMapSizeForTesting(); got != 0 {
t.Fatalf("ForDisableKeepAlives = %v, map size = %d; want 0", disableKeep, got)
}
@@ -2086,6 +2113,38 @@ func TestTransportNoReuseAfterEarlyResponse(t *testing.T) {
}
}
+// Tests that we don't leak Transport persistConn.readLoop goroutines
+// when a server hangs up immediately after saying it would keep-alive.
+func TestTransportIssue10457(t *testing.T) {
+ defer afterTest(t) // used to fail in goroutine leak check
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ // Send a response with no body, keep-alive
+ // (implicit), and then lie and immediately close the
+ // connection. This forces the Transport's readLoop to
+ // immediately Peek an io.EOF and get to the point
+ // that used to hang.
+ conn, _, _ := w.(Hijacker).Hijack()
+ conn.Write([]byte("HTTP/1.1 200 OK\r\nFoo: Bar\r\nContent-Length: 0\r\n\r\n")) // keep-alive
+ conn.Close()
+ }))
+ defer ts.Close()
+ tr := &Transport{}
+ defer tr.CloseIdleConnections()
+ cl := &Client{Transport: tr}
+ res, err := cl.Get(ts.URL)
+ if err != nil {
+ t.Fatalf("Get: %v", err)
+ }
+ defer res.Body.Close()
+
+ // Just a sanity check that we at least get the response. The real
+ // test here is that the "defer afterTest" above doesn't find any
+ // leaked goroutines.
+ if got, want := res.Header.Get("Foo"), "Bar"; got != want {
+ t.Errorf("Foo header = %q; want %q", got, want)
+ }
+}
+
type errorReader struct {
err error
}
@@ -2275,6 +2334,119 @@ func TestTransportRangeAndGzip(t *testing.T) {
res.Body.Close()
}
+// Previously, we used to handle a logical race within RoundTrip by waiting for 100ms
+// in the case of an error. Changing the order of the channel operations got rid of this
+// race.
+//
+// In order to test that the channel op reordering works, we install a hook into the
+// roundTrip function which gets called if we saw the connection go away and
+// we subsequently received a response.
+func TestTransportResponseCloseRace(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping in short mode")
+ }
+ defer afterTest(t)
+
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ }))
+ defer ts.Close()
+ sawRace := false
+ SetInstallConnClosedHook(func() {
+ sawRace = true
+ })
+ defer SetInstallConnClosedHook(nil)
+ tr := &Transport{
+ DisableKeepAlives: true,
+ }
+ req, err := NewRequest("GET", ts.URL, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ // selects are not deterministic, so do this a bunch
+ // and see if we handle the logical race at least once.
+ for i := 0; i < 10000; i++ {
+ resp, err := tr.RoundTrip(req)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ continue
+ }
+ resp.Body.Close()
+ if sawRace {
+ break
+ }
+ }
+ if !sawRace {
+ t.Errorf("didn't see response/connection going away race")
+ }
+}
+
+// Test for issue 10474
+func TestTransportResponseCancelRace(t *testing.T) {
+ defer afterTest(t)
+
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ // important that this response has a body.
+ var b [1024]byte
+ w.Write(b[:])
+ }))
+ defer ts.Close()
+
+ tr := &Transport{}
+ defer tr.CloseIdleConnections()
+
+ req, err := NewRequest("GET", ts.URL, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ res, err := tr.RoundTrip(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+ // If we do an early close, Transport just throws the connection away and
+ // doesn't reuse it. In order to trigger the bug, it has to reuse the connection
+ // so read the body
+ if _, err := io.Copy(ioutil.Discard, res.Body); err != nil {
+ t.Fatal(err)
+ }
+
+ req2, err := NewRequest("GET", ts.URL, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ tr.CancelRequest(req)
+ res, err = tr.RoundTrip(req2)
+ if err != nil {
+ t.Fatal(err)
+ }
+ res.Body.Close()
+}
+
+func TestTransportDialCancelRace(t *testing.T) {
+ defer afterTest(t)
+
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {}))
+ defer ts.Close()
+
+ tr := &Transport{}
+ defer tr.CloseIdleConnections()
+
+ req, err := NewRequest("GET", ts.URL, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ SetEnterRoundTripHook(func() {
+ tr.CancelRequest(req)
+ })
+ defer SetEnterRoundTripHook(nil)
+ res, err := tr.RoundTrip(req)
+ if err != ExportErrRequestCanceled {
+ t.Errorf("expected canceled request error; got %v", err)
+ if err == nil {
+ res.Body.Close()
+ }
+ }
+}
+
func wantBody(res *http.Response, err error, want string) error {
if err != nil {
return err
diff --git a/src/net/interface.go b/src/net/interface.go
index 2e9f1ebc67..9c7b5da8fe 100644
--- a/src/net/interface.go
+++ b/src/net/interface.go
@@ -62,41 +62,61 @@ func (f Flags) String() string {
// Addrs returns interface addresses for a specific interface.
func (ifi *Interface) Addrs() ([]Addr, error) {
if ifi == nil {
- return nil, errInvalidInterface
+ return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterface}
}
- return interfaceAddrTable(ifi)
+ ifat, err := interfaceAddrTable(ifi)
+ if err != nil {
+ err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
+ }
+ return ifat, err
}
// MulticastAddrs returns multicast, joined group addresses for
// a specific interface.
func (ifi *Interface) MulticastAddrs() ([]Addr, error) {
if ifi == nil {
- return nil, errInvalidInterface
+ return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterface}
}
- return interfaceMulticastAddrTable(ifi)
+ ifat, err := interfaceMulticastAddrTable(ifi)
+ if err != nil {
+ err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
+ }
+ return ifat, err
}
// Interfaces returns a list of the system's network interfaces.
func Interfaces() ([]Interface, error) {
- return interfaceTable(0)
+ ift, err := interfaceTable(0)
+ if err != nil {
+ err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
+ }
+ return ift, err
}
// InterfaceAddrs returns a list of the system's network interface
// addresses.
func InterfaceAddrs() ([]Addr, error) {
- return interfaceAddrTable(nil)
+ ifat, err := interfaceAddrTable(nil)
+ if err != nil {
+ err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
+ }
+ return ifat, err
}
// InterfaceByIndex returns the interface specified by index.
func InterfaceByIndex(index int) (*Interface, error) {
if index <= 0 {
- return nil, errInvalidInterfaceIndex
+ return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterfaceIndex}
}
ift, err := interfaceTable(index)
if err != nil {
- return nil, err
+ return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
}
- return interfaceByIndex(ift, index)
+ ifi, err := interfaceByIndex(ift, index)
+ if err != nil {
+ err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
+ }
+ return ifi, err
}
func interfaceByIndex(ift []Interface, index int) (*Interface, error) {
@@ -111,16 +131,16 @@ func interfaceByIndex(ift []Interface, index int) (*Interface, error) {
// InterfaceByName returns the interface specified by name.
func InterfaceByName(name string) (*Interface, error) {
if name == "" {
- return nil, errInvalidInterfaceName
+ return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterfaceName}
}
ift, err := interfaceTable(0)
if err != nil {
- return nil, err
+ return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
}
for _, ifi := range ift {
if name == ifi.Name {
return &ifi, nil
}
}
- return nil, errNoSuchInterface
+ return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errNoSuchInterface}
}
diff --git a/src/net/interface_bsd.go b/src/net/interface_bsd.go
index 2f66e4fc31..208f37f9fd 100644
--- a/src/net/interface_bsd.go
+++ b/src/net/interface_bsd.go
@@ -18,11 +18,11 @@ import (
func interfaceTable(ifindex int) ([]Interface, error) {
tab, err := syscall.RouteRIB(syscall.NET_RT_IFLIST, ifindex)
if err != nil {
- return nil, os.NewSyscallError("route rib", err)
+ return nil, os.NewSyscallError("routerib", err)
}
msgs, err := syscall.ParseRoutingMessage(tab)
if err != nil {
- return nil, os.NewSyscallError("route message", err)
+ return nil, os.NewSyscallError("parseroutingmessage", err)
}
return parseInterfaceTable(ifindex, msgs)
}
@@ -51,7 +51,7 @@ loop:
func newLink(m *syscall.InterfaceMessage) (*Interface, error) {
sas, err := syscall.ParseRoutingSockaddr(m)
if err != nil {
- return nil, os.NewSyscallError("route sockaddr", err)
+ return nil, os.NewSyscallError("parseroutingsockaddr", err)
}
ifi := &Interface{Index: int(m.Header.Index), Flags: linkFlags(m.Header.Flags)}
sa, _ := sas[syscall.RTAX_IFP].(*syscall.SockaddrDatalink)
@@ -104,11 +104,11 @@ func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
}
tab, err := syscall.RouteRIB(syscall.NET_RT_IFLIST, index)
if err != nil {
- return nil, os.NewSyscallError("route rib", err)
+ return nil, os.NewSyscallError("routerib", err)
}
msgs, err := syscall.ParseRoutingMessage(tab)
if err != nil {
- return nil, os.NewSyscallError("route message", err)
+ return nil, os.NewSyscallError("parseroutingmessage", err)
}
var ift []Interface
if index == 0 {
@@ -145,7 +145,7 @@ func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
func newAddr(ifi *Interface, m *syscall.InterfaceAddrMessage) (*IPNet, error) {
sas, err := syscall.ParseRoutingSockaddr(m)
if err != nil {
- return nil, os.NewSyscallError("route sockaddr", err)
+ return nil, os.NewSyscallError("parseroutingsockaddr", err)
}
ifa := &IPNet{}
switch sa := sas[syscall.RTAX_NETMASK].(type) {
diff --git a/src/net/interface_darwin.go b/src/net/interface_darwin.go
index 475b8611ce..b7a333849d 100644
--- a/src/net/interface_darwin.go
+++ b/src/net/interface_darwin.go
@@ -14,11 +14,11 @@ import (
func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
tab, err := syscall.RouteRIB(syscall.NET_RT_IFLIST2, ifi.Index)
if err != nil {
- return nil, os.NewSyscallError("route rib", err)
+ return nil, os.NewSyscallError("routerib", err)
}
msgs, err := syscall.ParseRoutingMessage(tab)
if err != nil {
- return nil, os.NewSyscallError("route message", err)
+ return nil, os.NewSyscallError("parseroutingmessage", err)
}
var ifmat []Addr
for _, m := range msgs {
@@ -41,7 +41,7 @@ func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
func newMulticastAddr(ifi *Interface, m *syscall.InterfaceMulticastAddrMessage) (*IPAddr, error) {
sas, err := syscall.ParseRoutingSockaddr(m)
if err != nil {
- return nil, os.NewSyscallError("route sockaddr", err)
+ return nil, os.NewSyscallError("parseroutingsockaddr", err)
}
switch sa := sas[syscall.RTAX_IFA].(type) {
case *syscall.SockaddrInet4:
diff --git a/src/net/interface_freebsd.go b/src/net/interface_freebsd.go
index 13bf438103..c42d90b740 100644
--- a/src/net/interface_freebsd.go
+++ b/src/net/interface_freebsd.go
@@ -14,11 +14,11 @@ import (
func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
tab, err := syscall.RouteRIB(syscall.NET_RT_IFMALIST, ifi.Index)
if err != nil {
- return nil, os.NewSyscallError("route rib", err)
+ return nil, os.NewSyscallError("routerib", err)
}
msgs, err := syscall.ParseRoutingMessage(tab)
if err != nil {
- return nil, os.NewSyscallError("route message", err)
+ return nil, os.NewSyscallError("parseroutingmessage", err)
}
var ifmat []Addr
for _, m := range msgs {
@@ -41,7 +41,7 @@ func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
func newMulticastAddr(ifi *Interface, m *syscall.InterfaceMulticastAddrMessage) (*IPAddr, error) {
sas, err := syscall.ParseRoutingSockaddr(m)
if err != nil {
- return nil, os.NewSyscallError("route sockaddr", err)
+ return nil, os.NewSyscallError("parseroutingsockaddr", err)
}
switch sa := sas[syscall.RTAX_IFA].(type) {
case *syscall.SockaddrInet4:
diff --git a/src/net/interface_linux.go b/src/net/interface_linux.go
index 1115d0fc40..6551a3562e 100644
--- a/src/net/interface_linux.go
+++ b/src/net/interface_linux.go
@@ -16,11 +16,11 @@ import (
func interfaceTable(ifindex int) ([]Interface, error) {
tab, err := syscall.NetlinkRIB(syscall.RTM_GETLINK, syscall.AF_UNSPEC)
if err != nil {
- return nil, os.NewSyscallError("netlink rib", err)
+ return nil, os.NewSyscallError("netlinkrib", err)
}
msgs, err := syscall.ParseNetlinkMessage(tab)
if err != nil {
- return nil, os.NewSyscallError("netlink message", err)
+ return nil, os.NewSyscallError("parsenetlinkmessage", err)
}
var ift []Interface
loop:
@@ -33,7 +33,7 @@ loop:
if ifindex == 0 || ifindex == int(ifim.Index) {
attrs, err := syscall.ParseNetlinkRouteAttr(&m)
if err != nil {
- return nil, os.NewSyscallError("netlink routeattr", err)
+ return nil, os.NewSyscallError("parsenetlinkrouteattr", err)
}
ift = append(ift, *newLink(ifim, attrs))
if ifindex == int(ifim.Index) {
@@ -120,11 +120,11 @@ func linkFlags(rawFlags uint32) Flags {
func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
tab, err := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC)
if err != nil {
- return nil, os.NewSyscallError("netlink rib", err)
+ return nil, os.NewSyscallError("netlinkrib", err)
}
msgs, err := syscall.ParseNetlinkMessage(tab)
if err != nil {
- return nil, os.NewSyscallError("netlink message", err)
+ return nil, os.NewSyscallError("parsenetlinkmessage", err)
}
var ift []Interface
if ifi == nil {
@@ -160,7 +160,7 @@ loop:
}
attrs, err := syscall.ParseNetlinkRouteAttr(&m)
if err != nil {
- return nil, os.NewSyscallError("netlink routeattr", err)
+ return nil, os.NewSyscallError("parsenetlinkrouteattr", err)
}
ifa := newAddr(ifi, ifam, attrs)
if ifa != nil {
@@ -238,8 +238,8 @@ func parseProcNetIGMP(path string, ifi *Interface) []Addr {
b[i/2], _ = xtoi2(f[0][i:i+2], 0)
}
i := *(*uint32)(unsafe.Pointer(&b[:4][0]))
- ifma := IPAddr{IP: IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i))}
- ifmat = append(ifmat, ifma.toAddr())
+ ifma := &IPAddr{IP: IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i))}
+ ifmat = append(ifmat, ifma)
}
}
}
@@ -263,8 +263,8 @@ func parseProcNetIGMP6(path string, ifi *Interface) []Addr {
for i := 0; i+1 < len(f[2]); i += 2 {
b[i/2], _ = xtoi2(f[2][i:i+2], 0)
}
- ifma := IPAddr{IP: IP{b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]}}
- ifmat = append(ifmat, ifma.toAddr())
+ ifma := &IPAddr{IP: IP{b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]}}
+ ifmat = append(ifmat, ifma)
}
}
return ifmat
diff --git a/src/net/interface_linux_test.go b/src/net/interface_linux_test.go
index d8800bd0ce..059bde11c6 100644
--- a/src/net/interface_linux_test.go
+++ b/src/net/interface_linux_test.go
@@ -78,7 +78,7 @@ var (
func TestParseProcNet(t *testing.T) {
defer func() {
if p := recover(); p != nil {
- t.Fatalf("parseProcNetIGMP or parseProtNetIGMP6 panicked: %v", p)
+ t.Fatalf("panicked: %v", p)
}
}()
@@ -88,7 +88,7 @@ func TestParseProcNet(t *testing.T) {
ifmat4 = append(ifmat4, ifmat...)
}
if len(ifmat4) != numOfTestIPv4MCAddrs {
- t.Fatalf("parseProcNetIGMP returns %v addresses, expected %v", len(ifmat4), numOfTestIPv4MCAddrs)
+ t.Fatalf("got %d; want %d", len(ifmat4), numOfTestIPv4MCAddrs)
}
var ifmat6 []Addr
@@ -97,6 +97,6 @@ func TestParseProcNet(t *testing.T) {
ifmat6 = append(ifmat6, ifmat...)
}
if len(ifmat6) != numOfTestIPv6MCAddrs {
- t.Fatalf("parseProcNetIGMP6 returns %v addresses, expected %v", len(ifmat6), numOfTestIPv6MCAddrs)
+ t.Fatalf("got %d; want %d", len(ifmat6), numOfTestIPv6MCAddrs)
}
}
diff --git a/src/net/interface_test.go b/src/net/interface_test.go
index 666f11a980..0e5c2e3ddf 100644
--- a/src/net/interface_test.go
+++ b/src/net/interface_test.go
@@ -229,8 +229,7 @@ func testMulticastAddrs(t *testing.T, ifmat []Addr) (nmaf4, nmaf6 int) {
}
func BenchmarkInterfaces(b *testing.B) {
- uninstallTestHooks()
- defer installTestHooks()
+ testHookUninstaller.Do(func() { uninstallTestHooks() })
for i := 0; i < b.N; i++ {
if _, err := Interfaces(); err != nil {
@@ -240,8 +239,7 @@ func BenchmarkInterfaces(b *testing.B) {
}
func BenchmarkInterfaceByIndex(b *testing.B) {
- uninstallTestHooks()
- defer installTestHooks()
+ testHookUninstaller.Do(func() { uninstallTestHooks() })
ifi := loopbackInterface()
if ifi == nil {
@@ -255,8 +253,7 @@ func BenchmarkInterfaceByIndex(b *testing.B) {
}
func BenchmarkInterfaceByName(b *testing.B) {
- uninstallTestHooks()
- defer installTestHooks()
+ testHookUninstaller.Do(func() { uninstallTestHooks() })
ifi := loopbackInterface()
if ifi == nil {
@@ -270,8 +267,7 @@ func BenchmarkInterfaceByName(b *testing.B) {
}
func BenchmarkInterfaceAddrs(b *testing.B) {
- uninstallTestHooks()
- defer installTestHooks()
+ testHookUninstaller.Do(func() { uninstallTestHooks() })
for i := 0; i < b.N; i++ {
if _, err := InterfaceAddrs(); err != nil {
@@ -281,8 +277,7 @@ func BenchmarkInterfaceAddrs(b *testing.B) {
}
func BenchmarkInterfacesAndAddrs(b *testing.B) {
- uninstallTestHooks()
- defer installTestHooks()
+ testHookUninstaller.Do(func() { uninstallTestHooks() })
ifi := loopbackInterface()
if ifi == nil {
@@ -296,8 +291,7 @@ func BenchmarkInterfacesAndAddrs(b *testing.B) {
}
func BenchmarkInterfacesAndMulticastAddrs(b *testing.B) {
- uninstallTestHooks()
- defer installTestHooks()
+ testHookUninstaller.Do(func() { uninstallTestHooks() })
ifi := loopbackInterface()
if ifi == nil {
diff --git a/src/net/interface_unix_test.go b/src/net/interface_unix_test.go
index 01f609f15e..84bf06cbce 100644
--- a/src/net/interface_unix_test.go
+++ b/src/net/interface_unix_test.go
@@ -42,14 +42,13 @@ func (ti *testInterface) teardown() error {
func TestPointToPointInterface(t *testing.T) {
if testing.Short() {
- t.Skip("skipping test in short mode")
+ t.Skip("avoid external network")
}
- switch {
- case runtime.GOOS == "darwin":
- t.Skipf("skipping read test on %q", runtime.GOOS)
+ if runtime.GOOS == "darwin" {
+ t.Skipf("not supported on %s", runtime.GOOS)
}
if os.Getuid() != 0 {
- t.Skip("skipping test; must be root")
+ t.Skip("must be root")
}
local, remote := "169.254.0.1", "169.254.0.254"
@@ -60,21 +59,21 @@ func TestPointToPointInterface(t *testing.T) {
t.Skipf("test requries external command: %v", err)
}
if err := ti.setup(); err != nil {
- t.Fatalf("testInterface.setup failed: %v", err)
+ t.Fatal(err)
} else {
time.Sleep(3 * time.Millisecond)
}
ift, err := Interfaces()
if err != nil {
ti.teardown()
- t.Fatalf("Interfaces failed: %v", err)
+ t.Fatal(err)
}
for _, ifi := range ift {
if ti.name == ifi.Name {
ifat, err := ifi.Addrs()
if err != nil {
ti.teardown()
- t.Fatalf("Interface.Addrs failed: %v", err)
+ t.Fatal(err)
}
for _, ifa := range ifat {
if ip.Equal(ifa.(*IPNet).IP) {
@@ -85,7 +84,7 @@ func TestPointToPointInterface(t *testing.T) {
}
}
if err := ti.teardown(); err != nil {
- t.Fatalf("testInterface.teardown failed: %v", err)
+ t.Fatal(err)
} else {
time.Sleep(3 * time.Millisecond)
}
@@ -94,30 +93,30 @@ func TestPointToPointInterface(t *testing.T) {
func TestInterfaceArrivalAndDeparture(t *testing.T) {
if testing.Short() {
- t.Skip("skipping test in short mode")
+ t.Skip("avoid external network")
}
if os.Getuid() != 0 {
- t.Skip("skipping test; must be root")
+ t.Skip("must be root")
}
for i := 0; i < 3; i++ {
ift1, err := Interfaces()
if err != nil {
- t.Fatalf("Interfaces failed: %v", err)
+ t.Fatal(err)
}
ti := &testInterface{}
if err := ti.setBroadcast(5682 + i); err != nil {
t.Skipf("test requires external command: %v", err)
}
if err := ti.setup(); err != nil {
- t.Fatalf("testInterface.setup failed: %v", err)
+ t.Fatal(err)
} else {
time.Sleep(3 * time.Millisecond)
}
ift2, err := Interfaces()
if err != nil {
ti.teardown()
- t.Fatalf("Interfaces failed: %v", err)
+ t.Fatal(err)
}
if len(ift2) <= len(ift1) {
for _, ifi := range ift1 {
@@ -130,13 +129,13 @@ func TestInterfaceArrivalAndDeparture(t *testing.T) {
t.Fatalf("got %v; want gt %v", len(ift2), len(ift1))
}
if err := ti.teardown(); err != nil {
- t.Fatalf("testInterface.teardown failed: %v", err)
+ t.Fatal(err)
} else {
time.Sleep(3 * time.Millisecond)
}
ift3, err := Interfaces()
if err != nil {
- t.Fatalf("Interfaces failed: %v", err)
+ t.Fatal(err)
}
if len(ift3) >= len(ift2) {
for _, ifi := range ift2 {
diff --git a/src/net/interface_windows.go b/src/net/interface_windows.go
index 438dc874d6..e25c1ed560 100644
--- a/src/net/interface_windows.go
+++ b/src/net/interface_windows.go
@@ -27,7 +27,7 @@ func getAdapters() (*windows.IpAdapterAddresses, error) {
break
}
if err.(syscall.Errno) != syscall.ERROR_BUFFER_OVERFLOW {
- return nil, os.NewSyscallError("GetAdaptersAddresses", err)
+ return nil, os.NewSyscallError("getadaptersaddresses", err)
}
}
return &addrs[0], nil
@@ -36,16 +36,16 @@ func getAdapters() (*windows.IpAdapterAddresses, error) {
func getInterfaceInfos() ([]syscall.InterfaceInfo, error) {
s, err := sysSocket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP)
if err != nil {
- return nil, os.NewSyscallError("Socket", err)
+ return nil, err
}
- defer syscall.Closesocket(s)
+ defer closeFunc(s)
iia := [20]syscall.InterfaceInfo{}
ret := uint32(0)
size := uint32(unsafe.Sizeof(iia))
err = syscall.WSAIoctl(s, syscall.SIO_GET_INTERFACE_LIST, nil, 0, (*byte)(unsafe.Pointer(&iia[0])), size, &ret, nil, 0)
if err != nil {
- return nil, os.NewSyscallError("WSAIoctl", err)
+ return nil, os.NewSyscallError("wsaioctl", err)
}
iilen := ret / uint32(unsafe.Sizeof(iia[0]))
return iia[:iilen-1], nil
@@ -217,11 +217,11 @@ func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
case *syscall.SockaddrInet4:
ifa := &IPAddr{IP: make(IP, IPv4len)}
copy(ifa.IP, sav.Addr[:])
- ifat = append(ifat, ifa.toAddr())
+ ifat = append(ifat, ifa)
case *syscall.SockaddrInet6:
ifa := &IPAddr{IP: make(IP, IPv6len)}
copy(ifa.IP, sav.Addr[:])
- ifat = append(ifat, ifa.toAddr())
+ ifat = append(ifat, ifa)
}
}
}
diff --git a/src/net/internal/socktest/switch.go b/src/net/internal/socktest/switch.go
index 539819160e..5e558a2de3 100644
--- a/src/net/internal/socktest/switch.go
+++ b/src/net/internal/socktest/switch.go
@@ -5,7 +5,10 @@
// Package socktest provides utilities for socket testing.
package socktest
-import "sync"
+import (
+ "fmt"
+ "sync"
+)
func switchInit(sw *Switch) {
sw.fltab = make(map[FilterType]Filter)
@@ -70,7 +73,11 @@ func cookie(family, sotype, proto int) Cookie {
type Status struct {
Cookie Cookie
Err error // error status of socket system call
- SocketErr int // error status of socket by SO_ERROR
+ SocketErr error // error status of socket by SO_ERROR
+}
+
+func (so Status) String() string {
+ return fmt.Sprintf("(%s, %s, %s): syscallerr=%v, socketerr=%v", familyString(so.Cookie.Family()), typeString(so.Cookie.Type()), protocolString(so.Cookie.Protocol()), so.Err, so.SocketErr)
}
// A Stat represents a per-cookie socket statistics.
@@ -80,9 +87,20 @@ type Stat struct {
Protocol int // protocol number
Opened uint64 // number of sockets opened
- Accepted uint64 // number of sockets accepted
Connected uint64 // number of sockets connected
+ Listened uint64 // number of sockets listened
+ Accepted uint64 // number of sockets accepted
Closed uint64 // number of sockets closed
+
+ OpenFailed uint64 // number of sockets open failed
+ ConnectFailed uint64 // number of sockets connect failed
+ ListenFailed uint64 // number of sockets listen failed
+ AcceptFailed uint64 // number of sockets accept failed
+ CloseFailed uint64 // number of sockets close failed
+}
+
+func (st Stat) String() string {
+ return fmt.Sprintf("(%s, %s, %s): opened=%d, connected=%d, listened=%d, accepted=%d, closed=%d, openfailed=%d, connectfailed=%d, listenfailed=%d, acceptfailed=%d, closefailed=%d", familyString(st.Family), typeString(st.Type), protocolString(st.Protocol), st.Opened, st.Connected, st.Listened, st.Accepted, st.Closed, st.OpenFailed, st.ConnectFailed, st.ListenFailed, st.AcceptFailed, st.CloseFailed)
}
type stats map[Cookie]*Stat
@@ -101,8 +119,9 @@ type FilterType int
const (
FilterSocket FilterType = iota // for Socket
- FilterAccept // for Accept or Accept4
FilterConnect // for Connect or ConnectEx
+ FilterListen // for Listen
+ FilterAccept // for Accept or Accept4
FilterGetsockoptInt // for GetsockoptInt
FilterClose // for Close or Closesocket
)
diff --git a/src/net/internal/socktest/switch_posix.go b/src/net/internal/socktest/switch_posix.go
new file mode 100644
index 0000000000..863edef0d3
--- /dev/null
+++ b/src/net/internal/socktest/switch_posix.go
@@ -0,0 +1,58 @@
+// Copyright 2015 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 !plan9
+
+package socktest
+
+import (
+ "fmt"
+ "syscall"
+)
+
+func familyString(family int) string {
+ switch family {
+ case syscall.AF_INET:
+ return "inet4"
+ case syscall.AF_INET6:
+ return "inet6"
+ case syscall.AF_UNIX:
+ return "local"
+ default:
+ return fmt.Sprintf("%d", family)
+ }
+}
+
+func typeString(sotype int) string {
+ var s string
+ switch sotype & 0xff {
+ case syscall.SOCK_STREAM:
+ s = "stream"
+ case syscall.SOCK_DGRAM:
+ s = "datagram"
+ case syscall.SOCK_RAW:
+ s = "raw"
+ case syscall.SOCK_SEQPACKET:
+ s = "seqpacket"
+ default:
+ s = fmt.Sprintf("%d", sotype&0xff)
+ }
+ if flags := uint(sotype) & ^uint(0xff); flags != 0 {
+ s += fmt.Sprintf("|%#x", flags)
+ }
+ return s
+}
+
+func protocolString(proto int) string {
+ switch proto {
+ case 0:
+ return "default"
+ case syscall.IPPROTO_TCP:
+ return "tcp"
+ case syscall.IPPROTO_UDP:
+ return "udp"
+ default:
+ return fmt.Sprintf("%d", proto)
+ }
+}
diff --git a/src/net/internal/socktest/switch_stub.go b/src/net/internal/socktest/switch_stub.go
index be97628a00..28ce72cb85 100644
--- a/src/net/internal/socktest/switch_stub.go
+++ b/src/net/internal/socktest/switch_stub.go
@@ -8,3 +8,9 @@ package socktest
// Sockets maps a socket descriptor to the status of socket.
type Sockets map[int]Status
+
+func familyString(family int) string { return "" }
+
+func typeString(sotype int) string { return "" }
+
+func protocolString(proto int) string { return "" }
diff --git a/src/net/internal/socktest/sys_cloexec.go b/src/net/internal/socktest/sys_cloexec.go
index 61cb6aec08..340ff071e7 100644
--- a/src/net/internal/socktest/sys_cloexec.go
+++ b/src/net/internal/socktest/sys_cloexec.go
@@ -30,12 +30,13 @@ func (sw *Switch) Accept4(s, flags int) (ns int, sa syscall.Sockaddr, err error)
return -1, nil, err
}
+ sw.smu.Lock()
+ defer sw.smu.Unlock()
if so.Err != nil {
+ sw.stats.getLocked(so.Cookie).AcceptFailed++
return -1, nil, so.Err
}
- sw.smu.Lock()
nso := sw.addLocked(ns, so.Cookie.Family(), so.Cookie.Type(), so.Cookie.Protocol())
sw.stats.getLocked(nso.Cookie).Accepted++
- sw.smu.Unlock()
return ns, sa, nil
}
diff --git a/src/net/internal/socktest/sys_unix.go b/src/net/internal/socktest/sys_unix.go
index b128c019ae..4089f8cea2 100644
--- a/src/net/internal/socktest/sys_unix.go
+++ b/src/net/internal/socktest/sys_unix.go
@@ -27,13 +27,14 @@ func (sw *Switch) Socket(family, sotype, proto int) (s int, err error) {
return -1, err
}
+ sw.smu.Lock()
+ defer sw.smu.Unlock()
if so.Err != nil {
+ sw.stats.getLocked(so.Cookie).OpenFailed++
return -1, so.Err
}
- sw.smu.Lock()
nso := sw.addLocked(s, family, sotype, proto)
sw.stats.getLocked(nso.Cookie).Opened++
- sw.smu.Unlock()
return s, nil
}
@@ -56,13 +57,14 @@ func (sw *Switch) Close(s int) (err error) {
return err
}
+ sw.smu.Lock()
+ defer sw.smu.Unlock()
if so.Err != nil {
+ sw.stats.getLocked(so.Cookie).CloseFailed++
return so.Err
}
- sw.smu.Lock()
delete(sw.sotab, s)
sw.stats.getLocked(so.Cookie).Closed++
- sw.smu.Unlock()
return nil
}
@@ -85,12 +87,42 @@ func (sw *Switch) Connect(s int, sa syscall.Sockaddr) (err error) {
return err
}
+ sw.smu.Lock()
+ defer sw.smu.Unlock()
if so.Err != nil {
+ sw.stats.getLocked(so.Cookie).ConnectFailed++
return so.Err
}
- sw.smu.Lock()
sw.stats.getLocked(so.Cookie).Connected++
- sw.smu.Unlock()
+ return nil
+}
+
+// Listen wraps syscall.Listen.
+func (sw *Switch) Listen(s, backlog int) (err error) {
+ so := sw.sockso(s)
+ if so == nil {
+ return syscall.Listen(s, backlog)
+ }
+ sw.fmu.RLock()
+ f, _ := sw.fltab[FilterListen]
+ sw.fmu.RUnlock()
+
+ af, err := f.apply(so)
+ if err != nil {
+ return err
+ }
+ so.Err = syscall.Listen(s, backlog)
+ if err = af.apply(so); err != nil {
+ return err
+ }
+
+ sw.smu.Lock()
+ defer sw.smu.Unlock()
+ if so.Err != nil {
+ sw.stats.getLocked(so.Cookie).ListenFailed++
+ return so.Err
+ }
+ sw.stats.getLocked(so.Cookie).Listened++
return nil
}
@@ -116,13 +148,14 @@ func (sw *Switch) Accept(s int) (ns int, sa syscall.Sockaddr, err error) {
return -1, nil, err
}
+ sw.smu.Lock()
+ defer sw.smu.Unlock()
if so.Err != nil {
+ sw.stats.getLocked(so.Cookie).AcceptFailed++
return -1, nil, so.Err
}
- sw.smu.Lock()
nso := sw.addLocked(ns, so.Cookie.Family(), so.Cookie.Type(), so.Cookie.Protocol())
sw.stats.getLocked(nso.Cookie).Accepted++
- sw.smu.Unlock()
return ns, sa, nil
}
@@ -140,7 +173,8 @@ func (sw *Switch) GetsockoptInt(s, level, opt int) (soerr int, err error) {
if err != nil {
return -1, err
}
- so.SocketErr, so.Err = syscall.GetsockoptInt(s, level, opt)
+ soerr, so.Err = syscall.GetsockoptInt(s, level, opt)
+ so.SocketErr = syscall.Errno(soerr)
if err = af.apply(so); err != nil {
return -1, err
}
@@ -148,10 +182,10 @@ func (sw *Switch) GetsockoptInt(s, level, opt int) (soerr int, err error) {
if so.Err != nil {
return -1, so.Err
}
- if opt == syscall.SO_ERROR && (so.SocketErr == 0 || syscall.Errno(so.SocketErr) == syscall.EISCONN) {
+ if opt == syscall.SO_ERROR && (so.SocketErr == syscall.Errno(0) || so.SocketErr == syscall.EISCONN) {
sw.smu.Lock()
sw.stats.getLocked(so.Cookie).Connected++
sw.smu.Unlock()
}
- return so.SocketErr, nil
+ return soerr, nil
}
diff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go
index 30bac45512..907e01b5a2 100644
--- a/src/net/internal/socktest/sys_windows.go
+++ b/src/net/internal/socktest/sys_windows.go
@@ -25,13 +25,14 @@ func (sw *Switch) Socket(family, sotype, proto int) (s syscall.Handle, err error
return syscall.InvalidHandle, err
}
+ sw.smu.Lock()
+ defer sw.smu.Unlock()
if so.Err != nil {
+ sw.stats.getLocked(so.Cookie).OpenFailed++
return syscall.InvalidHandle, so.Err
}
- sw.smu.Lock()
nso := sw.addLocked(s, family, sotype, proto)
sw.stats.getLocked(nso.Cookie).Opened++
- sw.smu.Unlock()
return s, nil
}
@@ -54,13 +55,14 @@ func (sw *Switch) Closesocket(s syscall.Handle) (err error) {
return err
}
+ sw.smu.Lock()
+ defer sw.smu.Unlock()
if so.Err != nil {
+ sw.stats.getLocked(so.Cookie).CloseFailed++
return so.Err
}
- sw.smu.Lock()
delete(sw.sotab, s)
sw.stats.getLocked(so.Cookie).Closed++
- sw.smu.Unlock()
return nil
}
@@ -83,12 +85,13 @@ func (sw *Switch) Connect(s syscall.Handle, sa syscall.Sockaddr) (err error) {
return err
}
+ sw.smu.Lock()
+ defer sw.smu.Unlock()
if so.Err != nil {
+ sw.stats.getLocked(so.Cookie).ConnectFailed++
return so.Err
}
- sw.smu.Lock()
sw.stats.getLocked(so.Cookie).Connected++
- sw.smu.Unlock()
return nil
}
@@ -111,11 +114,41 @@ func (sw *Switch) ConnectEx(s syscall.Handle, sa syscall.Sockaddr, b *byte, n ui
return err
}
+ sw.smu.Lock()
+ defer sw.smu.Unlock()
if so.Err != nil {
+ sw.stats.getLocked(so.Cookie).ConnectFailed++
return so.Err
}
- sw.smu.Lock()
sw.stats.getLocked(so.Cookie).Connected++
- sw.smu.Unlock()
+ return nil
+}
+
+// Listen wraps syscall.Listen.
+func (sw *Switch) Listen(s syscall.Handle, backlog int) (err error) {
+ so := sw.sockso(s)
+ if so == nil {
+ return syscall.Listen(s, backlog)
+ }
+ sw.fmu.RLock()
+ f, _ := sw.fltab[FilterListen]
+ sw.fmu.RUnlock()
+
+ af, err := f.apply(so)
+ if err != nil {
+ return err
+ }
+ so.Err = syscall.Listen(s, backlog)
+ if err = af.apply(so); err != nil {
+ return err
+ }
+
+ sw.smu.Lock()
+ defer sw.smu.Unlock()
+ if so.Err != nil {
+ sw.stats.getLocked(so.Cookie).ListenFailed++
+ return so.Err
+ }
+ sw.stats.getLocked(so.Cookie).Listened++
return nil
}
diff --git a/src/net/ip.go b/src/net/ip.go
index a554165af7..a7f45642e3 100644
--- a/src/net/ip.go
+++ b/src/net/ip.go
@@ -12,8 +12,6 @@
package net
-import "errors"
-
// IP address lengths (bytes).
const (
IPv4len = 4
@@ -331,7 +329,7 @@ func (ip IP) MarshalText() ([]byte, error) {
return []byte(""), nil
}
if len(ip) != IPv4len && len(ip) != IPv6len {
- return nil, errors.New("invalid IP address")
+ return nil, &AddrError{Err: "invalid IP address", Addr: ip.String()}
}
return []byte(ip.String()), nil
}
@@ -346,7 +344,7 @@ func (ip *IP) UnmarshalText(text []byte) error {
s := string(text)
x := ParseIP(s)
if x == nil {
- return &ParseError{"IP address", s}
+ return &ParseError{Type: "IP address", Text: s}
}
*ip = x
return nil
@@ -633,16 +631,6 @@ func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string) {
return ip, zone
}
-// A ParseError represents a malformed text string and the type of string that was expected.
-type ParseError struct {
- Type string
- Text string
-}
-
-func (e *ParseError) Error() string {
- return "invalid " + e.Type + ": " + e.Text
-}
-
// ParseIP parses s as an IP address, returning the result.
// The string s can be in dotted decimal ("74.125.19.99")
// or IPv6 ("2001:4860:0:2001::68") form.
@@ -671,7 +659,7 @@ func ParseIP(s string) IP {
func ParseCIDR(s string) (IP, *IPNet, error) {
i := byteIndex(s, '/')
if i < 0 {
- return nil, nil, &ParseError{"CIDR address", s}
+ return nil, nil, &ParseError{Type: "CIDR address", Text: s}
}
addr, mask := s[:i], s[i+1:]
iplen := IPv4len
@@ -682,7 +670,7 @@ func ParseCIDR(s string) (IP, *IPNet, error) {
}
n, i, ok := dtoi(mask, 0)
if ip == nil || !ok || i != len(mask) || n < 0 || n > 8*iplen {
- return nil, nil, &ParseError{"CIDR address", s}
+ return nil, nil, &ParseError{Type: "CIDR address", Text: s}
}
m := CIDRMask(n, 8*iplen)
return ip, &IPNet{IP: ip.Mask(m), Mask: m}, nil
diff --git a/src/net/ip_test.go b/src/net/ip_test.go
index e6f4e650ca..24f67cac97 100644
--- a/src/net/ip_test.go
+++ b/src/net/ip_test.go
@@ -53,8 +53,7 @@ func TestParseIP(t *testing.T) {
}
func BenchmarkParseIP(b *testing.B) {
- uninstallTestHooks()
- defer installTestHooks()
+ testHookUninstaller.Do(func() { uninstallTestHooks() })
for i := 0; i < b.N; i++ {
for _, tt := range parseIPTests {
@@ -111,8 +110,7 @@ func TestIPString(t *testing.T) {
}
func BenchmarkIPString(b *testing.B) {
- uninstallTestHooks()
- defer installTestHooks()
+ testHookUninstaller.Do(func() { uninstallTestHooks() })
for i := 0; i < b.N; i++ {
for _, tt := range ipStringTests {
@@ -164,8 +162,7 @@ func TestIPMaskString(t *testing.T) {
}
func BenchmarkIPMaskString(b *testing.B) {
- uninstallTestHooks()
- defer installTestHooks()
+ testHookUninstaller.Do(func() { uninstallTestHooks() })
for i := 0; i < b.N; i++ {
for _, tt := range ipMaskStringTests {
@@ -197,10 +194,10 @@ var parseCIDRTests = []struct {
{"abcd:2345::/24", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2300::"), Mask: IPMask(ParseIP("ffff:ff00::"))}, nil},
{"2001:DB8::/48", ParseIP("2001:DB8::"), &IPNet{IP: ParseIP("2001:DB8::"), Mask: IPMask(ParseIP("ffff:ffff:ffff::"))}, nil},
{"2001:DB8::1/48", ParseIP("2001:DB8::1"), &IPNet{IP: ParseIP("2001:DB8::"), Mask: IPMask(ParseIP("ffff:ffff:ffff::"))}, nil},
- {"192.168.1.1/255.255.255.0", nil, nil, &ParseError{"CIDR address", "192.168.1.1/255.255.255.0"}},
- {"192.168.1.1/35", nil, nil, &ParseError{"CIDR address", "192.168.1.1/35"}},
- {"2001:db8::1/-1", nil, nil, &ParseError{"CIDR address", "2001:db8::1/-1"}},
- {"", nil, nil, &ParseError{"CIDR address", ""}},
+ {"192.168.1.1/255.255.255.0", nil, nil, &ParseError{Type: "CIDR address", Text: "192.168.1.1/255.255.255.0"}},
+ {"192.168.1.1/35", nil, nil, &ParseError{Type: "CIDR address", Text: "192.168.1.1/35"}},
+ {"2001:db8::1/-1", nil, nil, &ParseError{Type: "CIDR address", Text: "2001:db8::1/-1"}},
+ {"", nil, nil, &ParseError{Type: "CIDR address", Text: ""}},
}
func TestParseCIDR(t *testing.T) {
diff --git a/src/net/ipraw_test.go b/src/net/ipraw_test.go
index f93b9ef0b0..7221f7868b 100644
--- a/src/net/ipraw_test.go
+++ b/src/net/ipraw_test.go
@@ -5,7 +5,6 @@
package net
import (
- "fmt"
"reflect"
"testing"
)
@@ -17,7 +16,7 @@ import (
// golang.org/x/net/icmp
type resolveIPAddrTest struct {
- net string
+ network string
litAddrOrName string
addr *IPAddr
err error
@@ -44,34 +43,30 @@ var resolveIPAddrTests = []resolveIPAddrTest{
{"tcp", "1.2.3.4:123", nil, UnknownNetworkError("tcp")},
}
-func init() {
- if ifi := loopbackInterface(); ifi != nil {
- index := fmt.Sprintf("%v", ifi.Index)
- resolveIPAddrTests = append(resolveIPAddrTests, []resolveIPAddrTest{
- {"ip6", "fe80::1%" + ifi.Name, &IPAddr{IP: ParseIP("fe80::1"), Zone: zoneToString(ifi.Index)}, nil},
- {"ip6", "fe80::1%" + index, &IPAddr{IP: ParseIP("fe80::1"), Zone: index}, nil},
- }...)
- }
- if ips, err := LookupIP("localhost"); err == nil && len(ips) > 1 && supportsIPv4 && supportsIPv6 {
- resolveIPAddrTests = append(resolveIPAddrTests, []resolveIPAddrTest{
- {"ip", "localhost", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
- {"ip4", "localhost", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
- {"ip6", "localhost", &IPAddr{IP: IPv6loopback}, nil},
- }...)
- }
-}
-
func TestResolveIPAddr(t *testing.T) {
if !testableNetwork("ip+nopriv") {
t.Skip("ip+nopriv test")
}
- for _, tt := range resolveIPAddrTests {
- addr, err := ResolveIPAddr(tt.net, tt.litAddrOrName)
+ origTestHookLookupIP := testHookLookupIP
+ defer func() { testHookLookupIP = origTestHookLookupIP }()
+ testHookLookupIP = lookupLocalhost
+
+ for i, tt := range resolveIPAddrTests {
+ addr, err := ResolveIPAddr(tt.network, tt.litAddrOrName)
if err != tt.err {
- t.Fatalf("ResolveIPAddr(%v, %v) failed: %v", tt.net, tt.litAddrOrName, err)
+ t.Errorf("#%d: %v", i, err)
} else if !reflect.DeepEqual(addr, tt.addr) {
- t.Fatalf("got %#v; expected %#v", addr, tt.addr)
+ t.Errorf("#%d: got %#v; want %#v", i, addr, tt.addr)
+ }
+ if err != nil {
+ continue
+ }
+ rtaddr, err := ResolveIPAddr(addr.Network(), addr.String())
+ if err != nil {
+ t.Errorf("#%d: %v", i, err)
+ } else if !reflect.DeepEqual(rtaddr, addr) {
+ t.Errorf("#%d: got %#v; want %#v", i, rtaddr, addr)
}
}
}
@@ -93,11 +88,11 @@ func TestIPConnLocalName(t *testing.T) {
}
c, err := ListenIP(tt.net, tt.laddr)
if err != nil {
- t.Fatalf("ListenIP failed: %v", err)
+ t.Fatal(err)
}
defer c.Close()
if la := c.LocalAddr(); la == nil {
- t.Fatal("IPConn.LocalAddr failed")
+ t.Fatal("should not fail")
}
}
}
@@ -110,10 +105,10 @@ func TestIPConnRemoteName(t *testing.T) {
raddr := &IPAddr{IP: IPv4(127, 0, 0, 1).To4()}
c, err := DialIP("ip:tcp", &IPAddr{IP: IPv4(127, 0, 0, 1)}, raddr)
if err != nil {
- t.Fatalf("DialIP failed: %v", err)
+ t.Fatal(err)
}
defer c.Close()
if !reflect.DeepEqual(raddr, c.RemoteAddr()) {
- t.Fatalf("got %#v, expected %#v", c.RemoteAddr(), raddr)
+ t.Fatalf("got %#v; want %#v", c.RemoteAddr(), raddr)
}
}
diff --git a/src/net/iprawsock.go b/src/net/iprawsock.go
index 1e53ab2847..782561a418 100644
--- a/src/net/iprawsock.go
+++ b/src/net/iprawsock.go
@@ -30,13 +30,6 @@ func (a *IPAddr) isWildcard() bool {
return a.IP.IsUnspecified()
}
-func (a *IPAddr) toAddr() Addr {
- if a == nil {
- return nil
- }
- return a
-}
-
// ResolveIPAddr parses addr as an IP address of the form "host" or
// "ipv6-host%zone" and resolves the domain name on the network net,
// which must be "ip", "ip4" or "ip6".
@@ -53,9 +46,9 @@ func ResolveIPAddr(net, addr string) (*IPAddr, error) {
default:
return nil, UnknownNetworkError(net)
}
- a, err := resolveInternetAddr(afnet, addr, noDeadline)
+ addrs, err := internetAddrList(afnet, addr, noDeadline)
if err != nil {
return nil, err
}
- return a.toAddr().(*IPAddr), nil
+ return addrs.first(isIPv4).(*IPAddr), nil
}
diff --git a/src/net/iprawsock_plan9.go b/src/net/iprawsock_plan9.go
index e62d116b81..079629064e 100644
--- a/src/net/iprawsock_plan9.go
+++ b/src/net/iprawsock_plan9.go
@@ -23,12 +23,12 @@ type IPConn struct {
// Timeout() == true after a fixed time limit; see SetDeadline and
// SetReadDeadline.
func (c *IPConn) ReadFromIP(b []byte) (int, *IPAddr, error) {
- return 0, nil, syscall.EPLAN9
+ return 0, nil, &OpError{Op: "read", Net: c.fd.dir, Source: c.fd.laddr, Addr: c.fd.raddr, Err: syscall.EPLAN9}
}
// ReadFrom implements the PacketConn ReadFrom method.
func (c *IPConn) ReadFrom(b []byte) (int, Addr, error) {
- return 0, nil, syscall.EPLAN9
+ return 0, nil, &OpError{Op: "read", Net: c.fd.dir, Source: c.fd.laddr, Addr: c.fd.raddr, Err: syscall.EPLAN9}
}
// ReadMsgIP reads a packet from c, copying the payload into b and the
@@ -36,7 +36,7 @@ func (c *IPConn) ReadFrom(b []byte) (int, Addr, error) {
// bytes copied into b, the number of bytes copied into oob, the flags
// that were set on the packet and the source address of the packet.
func (c *IPConn) ReadMsgIP(b, oob []byte) (n, oobn, flags int, addr *IPAddr, err error) {
- return 0, 0, 0, nil, syscall.EPLAN9
+ return 0, 0, 0, nil, &OpError{Op: "read", Net: c.fd.dir, Source: c.fd.laddr, Addr: c.fd.raddr, Err: syscall.EPLAN9}
}
// WriteToIP writes an IP packet to addr via c, copying the payload
@@ -47,19 +47,19 @@ func (c *IPConn) ReadMsgIP(b, oob []byte) (n, oobn, flags int, addr *IPAddr, err
// SetWriteDeadline. On packet-oriented connections, write timeouts
// are rare.
func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (int, error) {
- return 0, syscall.EPLAN9
+ return 0, &OpError{Op: "write", Net: c.fd.dir, Source: c.fd.laddr, Addr: addr, Err: syscall.EPLAN9}
}
// WriteTo implements the PacketConn WriteTo method.
func (c *IPConn) WriteTo(b []byte, addr Addr) (int, error) {
- return 0, syscall.EPLAN9
+ return 0, &OpError{Op: "write", Net: c.fd.dir, Source: c.fd.laddr, Addr: addr, Err: syscall.EPLAN9}
}
// WriteMsgIP writes a packet to addr via c, copying the payload from
// b and the associated out-of-band data from oob. It returns the
// number of payload and out-of-band bytes written.
func (c *IPConn) WriteMsgIP(b, oob []byte, addr *IPAddr) (n, oobn int, err error) {
- return 0, 0, syscall.EPLAN9
+ return 0, 0, &OpError{Op: "write", Net: c.fd.dir, Source: c.fd.laddr, Addr: addr, Err: syscall.EPLAN9}
}
// DialIP connects to the remote address raddr on the network protocol
@@ -70,7 +70,7 @@ func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) {
}
func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn, error) {
- return nil, syscall.EPLAN9
+ return nil, &OpError{Op: "dial", Net: netProto, Source: laddr, Addr: raddr, Err: syscall.EPLAN9}
}
// ListenIP listens for incoming IP packets addressed to the local
@@ -78,5 +78,5 @@ func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn,
// methods can be used to receive and send IP packets with per-packet
// addressing.
func ListenIP(netProto string, laddr *IPAddr) (*IPConn, error) {
- return nil, syscall.EPLAN9
+ return nil, &OpError{Op: "listen", Net: netProto, Source: nil, Addr: laddr, Err: syscall.EPLAN9}
}
diff --git a/src/net/iprawsock_posix.go b/src/net/iprawsock_posix.go
index 94db068d7c..1102823ef6 100644
--- a/src/net/iprawsock_posix.go
+++ b/src/net/iprawsock_posix.go
@@ -80,6 +80,9 @@ func (c *IPConn) ReadFromIP(b []byte) (int, *IPAddr, error) {
case *syscall.SockaddrInet6:
addr = &IPAddr{IP: sa.Addr[0:], Zone: zoneToString(int(sa.ZoneId))}
}
+ if err != nil {
+ err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+ }
return n, addr, err
}
@@ -104,7 +107,10 @@ func (c *IPConn) ReadFrom(b []byte) (int, Addr, error) {
return 0, nil, syscall.EINVAL
}
n, addr, err := c.ReadFromIP(b)
- return n, addr.toAddr(), err
+ if addr == nil {
+ return n, nil, err
+ }
+ return n, addr, err
}
// ReadMsgIP reads a packet from c, copying the payload into b and the
@@ -123,6 +129,9 @@ func (c *IPConn) ReadMsgIP(b, oob []byte) (n, oobn, flags int, addr *IPAddr, err
case *syscall.SockaddrInet6:
addr = &IPAddr{IP: sa.Addr[0:], Zone: zoneToString(int(sa.ZoneId))}
}
+ if err != nil {
+ err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+ }
return
}
@@ -138,16 +147,20 @@ func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (int, error) {
return 0, syscall.EINVAL
}
if c.fd.isConnected {
- return 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: ErrWriteToConnected}
+ return 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: ErrWriteToConnected}
}
if addr == nil {
- return 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress}
+ return 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: errMissingAddress}
}
sa, err := addr.sockaddr(c.fd.family)
if err != nil {
- return 0, &OpError{"write", c.fd.net, addr, err}
+ return 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: err}
}
- return c.fd.writeTo(b, sa)
+ n, err := c.fd.writeTo(b, sa)
+ if err != nil {
+ err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: err}
+ }
+ return n, err
}
// WriteTo implements the PacketConn WriteTo method.
@@ -157,7 +170,7 @@ func (c *IPConn) WriteTo(b []byte, addr Addr) (int, error) {
}
a, ok := addr.(*IPAddr)
if !ok {
- return 0, &OpError{"write", c.fd.net, addr, syscall.EINVAL}
+ return 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: syscall.EINVAL}
}
return c.WriteToIP(b, a)
}
@@ -170,16 +183,21 @@ func (c *IPConn) WriteMsgIP(b, oob []byte, addr *IPAddr) (n, oobn int, err error
return 0, 0, syscall.EINVAL
}
if c.fd.isConnected {
- return 0, 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: ErrWriteToConnected}
+ return 0, 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: ErrWriteToConnected}
}
if addr == nil {
- return 0, 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress}
+ return 0, 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: errMissingAddress}
}
- sa, err := addr.sockaddr(c.fd.family)
+ var sa syscall.Sockaddr
+ sa, err = addr.sockaddr(c.fd.family)
if err != nil {
- return 0, 0, &OpError{"write", c.fd.net, addr, err}
+ return 0, 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: err}
}
- return c.fd.writeMsg(b, oob, sa)
+ n, oobn, err = c.fd.writeMsg(b, oob, sa)
+ if err != nil {
+ err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: err}
+ }
+ return
}
// DialIP connects to the remote address raddr on the network protocol
@@ -192,19 +210,19 @@ func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) {
func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn, error) {
net, proto, err := parseNetwork(netProto)
if err != nil {
- return nil, &OpError{Op: "dial", Net: netProto, Addr: raddr, Err: err}
+ return nil, &OpError{Op: "dial", Net: netProto, Source: laddr, Addr: raddr, Err: err}
}
switch net {
case "ip", "ip4", "ip6":
default:
- return nil, &OpError{Op: "dial", Net: netProto, Addr: raddr, Err: UnknownNetworkError(netProto)}
+ return nil, &OpError{Op: "dial", Net: netProto, Source: laddr, Addr: raddr, Err: UnknownNetworkError(netProto)}
}
if raddr == nil {
- return nil, &OpError{Op: "dial", Net: netProto, Addr: nil, Err: errMissingAddress}
+ return nil, &OpError{Op: "dial", Net: netProto, Source: laddr, Addr: raddr, Err: errMissingAddress}
}
fd, err := internetSocket(net, laddr, raddr, deadline, syscall.SOCK_RAW, proto, "dial")
if err != nil {
- return nil, &OpError{Op: "dial", Net: netProto, Addr: raddr, Err: err}
+ return nil, &OpError{Op: "dial", Net: netProto, Source: laddr, Addr: raddr, Err: err}
}
return newIPConn(fd), nil
}
@@ -216,16 +234,16 @@ func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn,
func ListenIP(netProto string, laddr *IPAddr) (*IPConn, error) {
net, proto, err := parseNetwork(netProto)
if err != nil {
- return nil, &OpError{Op: "dial", Net: netProto, Addr: laddr, Err: err}
+ return nil, &OpError{Op: "dial", Net: netProto, Source: nil, Addr: laddr, Err: err}
}
switch net {
case "ip", "ip4", "ip6":
default:
- return nil, &OpError{Op: "listen", Net: netProto, Addr: laddr, Err: UnknownNetworkError(netProto)}
+ return nil, &OpError{Op: "listen", Net: netProto, Source: nil, Addr: laddr, Err: UnknownNetworkError(netProto)}
}
fd, err := internetSocket(net, laddr, nil, noDeadline, syscall.SOCK_RAW, proto, "listen")
if err != nil {
- return nil, &OpError{Op: "listen", Net: netProto, Addr: laddr, Err: err}
+ return nil, &OpError{Op: "listen", Net: netProto, Source: nil, Addr: laddr, Err: err}
}
return newIPConn(fd), nil
}
diff --git a/src/net/ipsock.go b/src/net/ipsock.go
index 98d2dbffb7..6e75c33d53 100644
--- a/src/net/ipsock.go
+++ b/src/net/ipsock.go
@@ -26,93 +26,70 @@ var (
supportsIPv4map bool
)
-func init() {
- sysInit()
- supportsIPv4 = probeIPv4Stack()
- supportsIPv6, supportsIPv4map = probeIPv6Stack()
-}
-
-// A netaddr represents a network endpoint address or a list of
-// network endpoint addresses.
-type netaddr interface {
- // toAddr returns the address represented in Addr interface.
- // It returns a nil interface when the address is nil.
- toAddr() Addr
-}
-
// An addrList represents a list of network endpoint addresses.
-type addrList []netaddr
+type addrList []Addr
-func (al addrList) toAddr() Addr {
- switch len(al) {
- case 0:
- return nil
- case 1:
- return al[0].toAddr()
- default:
- // For now, we'll roughly pick first one without
- // considering dealing with any preferences such as
- // DNS TTL, transport path quality, network routing
- // information.
- return al[0].toAddr()
+// isIPv4 returns true if the Addr contains an IPv4 address.
+func isIPv4(addr Addr) bool {
+ switch addr := addr.(type) {
+ case *TCPAddr:
+ return addr.IP.To4() != nil
+ case *UDPAddr:
+ return addr.IP.To4() != nil
+ case *IPAddr:
+ return addr.IP.To4() != nil
}
+ return false
+}
+
+// first returns the first address which satisfies strategy, or if
+// none do, then the first address of any kind.
+func (addrs addrList) first(strategy func(Addr) bool) Addr {
+ for _, addr := range addrs {
+ if strategy(addr) {
+ return addr
+ }
+ }
+ return addrs[0]
+}
+
+// partition divides an address list into two categories, using a
+// strategy function to assign a boolean label to each address.
+// The first address, and any with a matching label, are returned as
+// primaries, while addresses with the opposite label are returned
+// as fallbacks. For non-empty inputs, primaries is guaranteed to be
+// non-empty.
+func (addrs addrList) partition(strategy func(Addr) bool) (primaries, fallbacks addrList) {
+ var primaryLabel bool
+ for i, addr := range addrs {
+ label := strategy(addr)
+ if i == 0 || label == primaryLabel {
+ primaryLabel = label
+ primaries = append(primaries, addr)
+ } else {
+ fallbacks = append(fallbacks, addr)
+ }
+ }
+ return
}
var errNoSuitableAddress = errors.New("no suitable address found")
-// firstFavoriteAddr returns an address or a list of addresses that
-// implement the netaddr interface. Known filters are nil, ipv4only
-// and ipv6only. It returns any address when filter is nil. The result
-// contains at least one address when error is nil.
-func firstFavoriteAddr(filter func(IPAddr) bool, ips []IPAddr, inetaddr func(IPAddr) netaddr) (netaddr, error) {
- if filter != nil {
- return firstSupportedAddr(filter, ips, inetaddr)
- }
- var (
- ipv4, ipv6, swap bool
- list addrList
- )
+// filterAddrList applies a filter to a list of IP addresses,
+// yielding a list of Addr objects. Known filters are nil, ipv4only,
+// and ipv6only. It returns every address when the filter is nil.
+// The result contains at least one address when error is nil.
+func filterAddrList(filter func(IPAddr) bool, ips []IPAddr, inetaddr func(IPAddr) Addr) (addrList, error) {
+ var addrs addrList
for _, ip := range ips {
- // We'll take any IP address, but since the dialing
- // code does not yet try multiple addresses
- // effectively, prefer to use an IPv4 address if
- // possible. This is especially relevant if localhost
- // resolves to [ipv6-localhost, ipv4-localhost]. Too
- // much code assumes localhost == ipv4-localhost.
- if ipv4only(ip) && !ipv4 {
- list = append(list, inetaddr(ip))
- ipv4 = true
- if ipv6 {
- swap = true
- }
- } else if ipv6only(ip) && !ipv6 {
- list = append(list, inetaddr(ip))
- ipv6 = true
- }
- if ipv4 && ipv6 {
- if swap {
- list[0], list[1] = list[1], list[0]
- }
- break
+ if filter == nil || filter(ip) {
+ addrs = append(addrs, inetaddr(ip))
}
}
- switch len(list) {
- case 0:
+ if len(addrs) == 0 {
return nil, errNoSuitableAddress
- case 1:
- return list[0], nil
- default:
- return list, nil
}
-}
-
-func firstSupportedAddr(filter func(IPAddr) bool, ips []IPAddr, inetaddr func(IPAddr) netaddr) (netaddr, error) {
- for _, ip := range ips {
- if filter(ip) {
- return inetaddr(ip), nil
- }
- }
- return nil, errNoSuitableAddress
+ return addrs, nil
}
// ipv4only reports whether the kernel supports IPv4 addressing mode
@@ -145,7 +122,7 @@ func SplitHostPort(hostport string) (host, port string, err error) {
// Expect the first ']' just before the last ':'.
end := byteIndex(hostport, ']')
if end < 0 {
- err = &AddrError{"missing ']' in address", hostport}
+ err = &AddrError{Err: "missing ']' in address", Addr: hostport}
return
}
switch end + 1 {
@@ -174,11 +151,11 @@ func SplitHostPort(hostport string) (host, port string, err error) {
}
}
if byteIndex(hostport[j:], '[') >= 0 {
- err = &AddrError{"unexpected '[' in address", hostport}
+ err = &AddrError{Err: "unexpected '[' in address", Addr: hostport}
return
}
if byteIndex(hostport[k:], ']') >= 0 {
- err = &AddrError{"unexpected ']' in address", hostport}
+ err = &AddrError{Err: "unexpected ']' in address", Addr: hostport}
return
}
@@ -186,15 +163,15 @@ func SplitHostPort(hostport string) (host, port string, err error) {
return
missingPort:
- err = &AddrError{"missing port in address", hostport}
+ err = &AddrError{Err: "missing port in address", Addr: hostport}
return
tooManyColons:
- err = &AddrError{"too many colons in address", hostport}
+ err = &AddrError{Err: "too many colons in address", Addr: hostport}
return
missingBrackets:
- err = &AddrError{"missing brackets in address", hostport}
+ err = &AddrError{Err: "missing brackets in address", Addr: hostport}
return
}
@@ -220,13 +197,11 @@ func JoinHostPort(host, port string) string {
return host + ":" + port
}
-// resolveInternetAddr resolves addr that is either a literal IP
-// address or a DNS name and returns an internet protocol family
-// address. It returns a list that contains a pair of different
-// address family addresses when addr is a DNS name and the name has
-// multiple address family records. The result contains at least one
-// address when error is nil.
-func resolveInternetAddr(net, addr string, deadline time.Time) (netaddr, error) {
+// internetAddrList resolves addr, which may be a literal IP
+// address or a DNS name, and returns a list of internet protocol
+// family addresses. The result contains at least one address when
+// error is nil.
+func internetAddrList(net, addr string, deadline time.Time) (addrList, error) {
var (
err error
host, port string
@@ -249,7 +224,7 @@ func resolveInternetAddr(net, addr string, deadline time.Time) (netaddr, error)
default:
return nil, UnknownNetworkError(net)
}
- inetaddr := func(ip IPAddr) netaddr {
+ inetaddr := func(ip IPAddr) Addr {
switch net {
case "tcp", "tcp4", "tcp6":
return &TCPAddr{IP: ip.IP, Port: portnum, Zone: ip.Zone}
@@ -262,16 +237,16 @@ func resolveInternetAddr(net, addr string, deadline time.Time) (netaddr, error)
}
}
if host == "" {
- return inetaddr(IPAddr{}), nil
+ return addrList{inetaddr(IPAddr{})}, nil
}
// Try as a literal IP address.
var ip IP
if ip = parseIPv4(host); ip != nil {
- return inetaddr(IPAddr{IP: ip}), nil
+ return addrList{inetaddr(IPAddr{IP: ip})}, nil
}
var zone string
if ip, zone = parseIPv6(host, true); ip != nil {
- return inetaddr(IPAddr{IP: ip, Zone: zone}), nil
+ return addrList{inetaddr(IPAddr{IP: ip, Zone: zone})}, nil
}
// Try as a DNS name.
ips, err := lookupIPDeadline(host, deadline)
@@ -285,7 +260,7 @@ func resolveInternetAddr(net, addr string, deadline time.Time) (netaddr, error)
if net != "" && net[len(net)-1] == '6' {
filter = ipv6only
}
- return firstFavoriteAddr(filter, ips, inetaddr)
+ return filterAddrList(filter, ips, inetaddr)
}
func zoneToString(zone int) string {
diff --git a/src/net/ipsock_plan9.go b/src/net/ipsock_plan9.go
index 94ceea31b0..72d640d923 100644
--- a/src/net/ipsock_plan9.go
+++ b/src/net/ipsock_plan9.go
@@ -7,7 +7,6 @@
package net
import (
- "errors"
"os"
"syscall"
)
@@ -60,15 +59,15 @@ func parsePlan9Addr(s string) (ip IP, iport int, err error) {
if i >= 0 {
addr = ParseIP(s[:i])
if addr == nil {
- return nil, 0, errors.New("parsing IP failed")
+ return nil, 0, &ParseError{Type: "IP address", Text: s}
}
}
p, _, ok := dtoi(s[i+1:], 0)
if !ok {
- return nil, 0, errors.New("parsing port failed")
+ return nil, 0, &ParseError{Type: "port", Text: s}
}
if p < 0 || p > 0xFFFF {
- return nil, 0, &AddrError{"invalid port", string(p)}
+ return nil, 0, &AddrError{Err: "invalid port", Addr: string(p)}
}
return addr, p, nil
}
@@ -95,7 +94,7 @@ func readPlan9Addr(proto, filename string) (addr Addr, err error) {
case "udp":
addr = &UDPAddr{IP: ip, Port: port}
default:
- return nil, errors.New("unknown protocol " + proto)
+ return nil, UnknownNetworkError(proto)
}
return addr, nil
}
@@ -152,23 +151,23 @@ func dialPlan9(net string, laddr, raddr Addr) (fd *netFD, err error) {
defer func() { netErr(err) }()
f, dest, proto, name, err := startPlan9(net, raddr)
if err != nil {
- return nil, &OpError{"dial", net, raddr, err}
+ return nil, &OpError{Op: "dial", Net: net, Source: laddr, Addr: raddr, Err: err}
}
_, err = f.WriteString("connect " + dest)
if err != nil {
f.Close()
- return nil, &OpError{"dial", f.Name(), raddr, err}
+ return nil, &OpError{Op: "dial", Net: f.Name(), Source: laddr, Addr: raddr, Err: err}
}
data, err := os.OpenFile(netdir+"/"+proto+"/"+name+"/data", os.O_RDWR, 0)
if err != nil {
f.Close()
- return nil, &OpError{"dial", net, raddr, err}
+ return nil, &OpError{Op: "dial", Net: net, Source: laddr, Addr: raddr, Err: err}
}
laddr, err = readPlan9Addr(proto, netdir+"/"+proto+"/"+name+"/local")
if err != nil {
data.Close()
f.Close()
- return nil, &OpError{"dial", proto, raddr, err}
+ return nil, &OpError{Op: "dial", Net: proto, Source: laddr, Addr: raddr, Err: err}
}
return newFD(proto, name, f, data, laddr, raddr)
}
@@ -177,52 +176,52 @@ func listenPlan9(net string, laddr Addr) (fd *netFD, err error) {
defer func() { netErr(err) }()
f, dest, proto, name, err := startPlan9(net, laddr)
if err != nil {
- return nil, &OpError{"listen", net, laddr, err}
+ return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr, Err: err}
}
_, err = f.WriteString("announce " + dest)
if err != nil {
f.Close()
- return nil, &OpError{"announce", proto, laddr, err}
+ return nil, &OpError{Op: "announce", Net: proto, Source: nil, Addr: laddr, Err: err}
}
laddr, err = readPlan9Addr(proto, netdir+"/"+proto+"/"+name+"/local")
if err != nil {
f.Close()
- return nil, &OpError{Op: "listen", Net: net, Err: err}
+ return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr, Err: err}
}
return newFD(proto, name, f, nil, laddr, nil)
}
-func (l *netFD) netFD() (*netFD, error) {
- return newFD(l.proto, l.n, l.ctl, l.data, l.laddr, l.raddr)
+func (fd *netFD) netFD() (*netFD, error) {
+ return newFD(fd.net, fd.n, fd.ctl, fd.data, fd.laddr, fd.raddr)
}
-func (l *netFD) acceptPlan9() (fd *netFD, err error) {
+func (fd *netFD) acceptPlan9() (nfd *netFD, err error) {
defer func() { netErr(err) }()
- if err := l.readLock(); err != nil {
+ if err := fd.readLock(); err != nil {
return nil, err
}
- defer l.readUnlock()
- f, err := os.Open(l.dir + "/listen")
+ defer fd.readUnlock()
+ f, err := os.Open(fd.dir + "/listen")
if err != nil {
- return nil, &OpError{"accept", l.dir + "/listen", l.laddr, err}
+ return nil, &OpError{Op: "accept", Net: fd.dir + "/listen", Source: nil, Addr: fd.laddr, Err: err}
}
var buf [16]byte
n, err := f.Read(buf[:])
if err != nil {
f.Close()
- return nil, &OpError{"accept", l.dir + "/listen", l.laddr, err}
+ return nil, &OpError{Op: "accept", Net: fd.dir + "/listen", Source: nil, Addr: fd.laddr, Err: err}
}
name := string(buf[:n])
- data, err := os.OpenFile(netdir+"/"+l.proto+"/"+name+"/data", os.O_RDWR, 0)
+ data, err := os.OpenFile(netdir+"/"+fd.net+"/"+name+"/data", os.O_RDWR, 0)
if err != nil {
f.Close()
- return nil, &OpError{"accept", l.proto, l.laddr, err}
+ return nil, &OpError{Op: "accept", Net: fd.net, Source: nil, Addr: fd.laddr, Err: err}
}
- raddr, err := readPlan9Addr(l.proto, netdir+"/"+l.proto+"/"+name+"/remote")
+ raddr, err := readPlan9Addr(fd.net, netdir+"/"+fd.net+"/"+name+"/remote")
if err != nil {
data.Close()
f.Close()
- return nil, &OpError{"accept", l.proto, l.laddr, err}
+ return nil, &OpError{Op: "accept", Net: fd.net, Source: nil, Addr: fd.laddr, Err: err}
}
- return newFD(l.proto, name, f, data, l.laddr, raddr)
+ return newFD(fd.net, name, f, data, fd.laddr, raddr)
}
diff --git a/src/net/ipsock_posix.go b/src/net/ipsock_posix.go
index 7597a92f6f..56b9872fd1 100644
--- a/src/net/ipsock_posix.go
+++ b/src/net/ipsock_posix.go
@@ -144,7 +144,7 @@ func ipToSockaddr(family int, ip IP, port int, zone string) (syscall.Sockaddr, e
ip = IPv4zero
}
if ip = ip.To4(); ip == nil {
- return nil, InvalidAddrError("non-IPv4 address")
+ return nil, &AddrError{Err: "non-IPv4 address", Addr: ip.String()}
}
sa := new(syscall.SockaddrInet4)
for i := 0; i < IPv4len; i++ {
@@ -163,7 +163,7 @@ func ipToSockaddr(family int, ip IP, port int, zone string) (syscall.Sockaddr, e
ip = IPv6zero
}
if ip = ip.To16(); ip == nil {
- return nil, InvalidAddrError("non-IPv6 address")
+ return nil, &AddrError{Err: "non-IPv6 address", Addr: ip.String()}
}
sa := new(syscall.SockaddrInet6)
for i := 0; i < IPv6len; i++ {
@@ -173,5 +173,5 @@ func ipToSockaddr(family int, ip IP, port int, zone string) (syscall.Sockaddr, e
sa.ZoneId = uint32(zoneToInt(zone))
return sa, nil
}
- return nil, InvalidAddrError("unexpected socket family")
+ return nil, &AddrError{Err: "invalid address family", Addr: ip.String()}
}
diff --git a/src/net/ipsock_test.go b/src/net/ipsock_test.go
index 754ccbb04f..b36557a157 100644
--- a/src/net/ipsock_test.go
+++ b/src/net/ipsock_test.go
@@ -9,14 +9,16 @@ import (
"testing"
)
-var testInetaddr = func(ip IPAddr) netaddr { return &TCPAddr{IP: ip.IP, Port: 5682, Zone: ip.Zone} }
+var testInetaddr = func(ip IPAddr) Addr { return &TCPAddr{IP: ip.IP, Port: 5682, Zone: ip.Zone} }
-var firstFavoriteAddrTests = []struct {
- filter func(IPAddr) bool
- ips []IPAddr
- inetaddr func(IPAddr) netaddr
- addr netaddr
- err error
+var addrListTests = []struct {
+ filter func(IPAddr) bool
+ ips []IPAddr
+ inetaddr func(IPAddr) Addr
+ first Addr
+ primaries addrList
+ fallbacks addrList
+ err error
}{
{
nil,
@@ -25,10 +27,9 @@ var firstFavoriteAddrTests = []struct {
{IP: IPv6loopback},
},
testInetaddr,
- addrList{
- &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
- &TCPAddr{IP: IPv6loopback, Port: 5682},
- },
+ &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
+ addrList{&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}},
+ addrList{&TCPAddr{IP: IPv6loopback, Port: 5682}},
nil,
},
{
@@ -38,10 +39,9 @@ var firstFavoriteAddrTests = []struct {
{IP: IPv4(127, 0, 0, 1)},
},
testInetaddr,
- addrList{
- &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
- &TCPAddr{IP: IPv6loopback, Port: 5682},
- },
+ &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
+ addrList{&TCPAddr{IP: IPv6loopback, Port: 5682}},
+ addrList{&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}},
nil,
},
{
@@ -52,6 +52,11 @@ var firstFavoriteAddrTests = []struct {
},
testInetaddr,
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
+ addrList{
+ &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
+ &TCPAddr{IP: IPv4(192, 168, 0, 1), Port: 5682},
+ },
+ nil,
nil,
},
{
@@ -62,6 +67,11 @@ var firstFavoriteAddrTests = []struct {
},
testInetaddr,
&TCPAddr{IP: IPv6loopback, Port: 5682},
+ addrList{
+ &TCPAddr{IP: IPv6loopback, Port: 5682},
+ &TCPAddr{IP: ParseIP("fe80::1"), Port: 5682, Zone: "eth0"},
+ },
+ nil,
nil,
},
{
@@ -73,9 +83,14 @@ var firstFavoriteAddrTests = []struct {
{IP: ParseIP("fe80::1"), Zone: "eth0"},
},
testInetaddr,
+ &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
addrList{
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
+ &TCPAddr{IP: IPv4(192, 168, 0, 1), Port: 5682},
+ },
+ addrList{
&TCPAddr{IP: IPv6loopback, Port: 5682},
+ &TCPAddr{IP: ParseIP("fe80::1"), Port: 5682, Zone: "eth0"},
},
nil,
},
@@ -88,9 +103,14 @@ var firstFavoriteAddrTests = []struct {
{IP: IPv4(192, 168, 0, 1)},
},
testInetaddr,
+ &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
+ addrList{
+ &TCPAddr{IP: IPv6loopback, Port: 5682},
+ &TCPAddr{IP: ParseIP("fe80::1"), Port: 5682, Zone: "eth0"},
+ },
addrList{
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
- &TCPAddr{IP: IPv6loopback, Port: 5682},
+ &TCPAddr{IP: IPv4(192, 168, 0, 1), Port: 5682},
},
nil,
},
@@ -103,9 +123,14 @@ var firstFavoriteAddrTests = []struct {
{IP: ParseIP("fe80::1"), Zone: "eth0"},
},
testInetaddr,
+ &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
addrList{
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
+ &TCPAddr{IP: IPv4(192, 168, 0, 1), Port: 5682},
+ },
+ addrList{
&TCPAddr{IP: IPv6loopback, Port: 5682},
+ &TCPAddr{IP: ParseIP("fe80::1"), Port: 5682, Zone: "eth0"},
},
nil,
},
@@ -118,9 +143,14 @@ var firstFavoriteAddrTests = []struct {
{IP: IPv4(192, 168, 0, 1)},
},
testInetaddr,
+ &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
+ addrList{
+ &TCPAddr{IP: IPv6loopback, Port: 5682},
+ &TCPAddr{IP: ParseIP("fe80::1"), Port: 5682, Zone: "eth0"},
+ },
addrList{
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
- &TCPAddr{IP: IPv6loopback, Port: 5682},
+ &TCPAddr{IP: IPv4(192, 168, 0, 1), Port: 5682},
},
nil,
},
@@ -133,6 +163,8 @@ var firstFavoriteAddrTests = []struct {
},
testInetaddr,
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
+ addrList{&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}},
+ nil,
nil,
},
{
@@ -143,6 +175,8 @@ var firstFavoriteAddrTests = []struct {
},
testInetaddr,
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
+ addrList{&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}},
+ nil,
nil,
},
@@ -154,6 +188,8 @@ var firstFavoriteAddrTests = []struct {
},
testInetaddr,
&TCPAddr{IP: IPv6loopback, Port: 5682},
+ addrList{&TCPAddr{IP: IPv6loopback, Port: 5682}},
+ nil,
nil,
},
{
@@ -164,30 +200,83 @@ var firstFavoriteAddrTests = []struct {
},
testInetaddr,
&TCPAddr{IP: IPv6loopback, Port: 5682},
+ addrList{&TCPAddr{IP: IPv6loopback, Port: 5682}},
+ nil,
nil,
},
- {nil, nil, testInetaddr, nil, errNoSuitableAddress},
+ {nil, nil, testInetaddr, nil, nil, nil, errNoSuitableAddress},
- {ipv4only, nil, testInetaddr, nil, errNoSuitableAddress},
- {ipv4only, []IPAddr{{IP: IPv6loopback}}, testInetaddr, nil, errNoSuitableAddress},
+ {ipv4only, nil, testInetaddr, nil, nil, nil, errNoSuitableAddress},
+ {ipv4only, []IPAddr{{IP: IPv6loopback}}, testInetaddr, nil, nil, nil, errNoSuitableAddress},
- {ipv6only, nil, testInetaddr, nil, errNoSuitableAddress},
- {ipv6only, []IPAddr{{IP: IPv4(127, 0, 0, 1)}}, testInetaddr, nil, errNoSuitableAddress},
+ {ipv6only, nil, testInetaddr, nil, nil, nil, errNoSuitableAddress},
+ {ipv6only, []IPAddr{{IP: IPv4(127, 0, 0, 1)}}, testInetaddr, nil, nil, nil, errNoSuitableAddress},
}
-func TestFirstFavoriteAddr(t *testing.T) {
+func TestAddrList(t *testing.T) {
if !supportsIPv4 || !supportsIPv6 {
- t.Skip("ipv4 or ipv6 is not supported")
+ t.Skip("both IPv4 and IPv6 are required")
}
- for i, tt := range firstFavoriteAddrTests {
- addr, err := firstFavoriteAddr(tt.filter, tt.ips, tt.inetaddr)
+ for i, tt := range addrListTests {
+ addrs, err := filterAddrList(tt.filter, tt.ips, tt.inetaddr)
if err != tt.err {
- t.Errorf("#%v: got %v; expected %v", i, err, tt.err)
+ t.Errorf("#%v: got %v; want %v", i, err, tt.err)
}
- if !reflect.DeepEqual(addr, tt.addr) {
- t.Errorf("#%v: got %v; expected %v", i, addr, tt.addr)
+ if tt.err != nil {
+ if len(addrs) != 0 {
+ t.Errorf("#%v: got %v; want 0", i, len(addrs))
+ }
+ continue
+ }
+ first := addrs.first(isIPv4)
+ if !reflect.DeepEqual(first, tt.first) {
+ t.Errorf("#%v: got %v; want %v", i, first, tt.first)
+ }
+ primaries, fallbacks := addrs.partition(isIPv4)
+ if !reflect.DeepEqual(primaries, tt.primaries) {
+ t.Errorf("#%v: got %v; want %v", i, primaries, tt.primaries)
+ }
+ if !reflect.DeepEqual(fallbacks, tt.fallbacks) {
+ t.Errorf("#%v: got %v; want %v", i, fallbacks, tt.fallbacks)
+ }
+ expectedLen := len(primaries) + len(fallbacks)
+ if len(addrs) != expectedLen {
+ t.Errorf("#%v: got %v; want %v", i, len(addrs), expectedLen)
+ }
+ }
+}
+
+func TestAddrListPartition(t *testing.T) {
+ addrs := addrList{
+ &IPAddr{IP: ParseIP("fe80::"), Zone: "eth0"},
+ &IPAddr{IP: ParseIP("fe80::1"), Zone: "eth0"},
+ &IPAddr{IP: ParseIP("fe80::2"), Zone: "eth0"},
+ }
+ cases := []struct {
+ lastByte byte
+ primaries addrList
+ fallbacks addrList
+ }{
+ {0, addrList{addrs[0]}, addrList{addrs[1], addrs[2]}},
+ {1, addrList{addrs[0], addrs[2]}, addrList{addrs[1]}},
+ {2, addrList{addrs[0], addrs[1]}, addrList{addrs[2]}},
+ {3, addrList{addrs[0], addrs[1], addrs[2]}, nil},
+ }
+ for i, tt := range cases {
+ // Inverting the function's output should not affect the outcome.
+ for _, invert := range []bool{false, true} {
+ primaries, fallbacks := addrs.partition(func(a Addr) bool {
+ ip := a.(*IPAddr).IP
+ return (ip[len(ip)-1] == tt.lastByte) != invert
+ })
+ if !reflect.DeepEqual(primaries, tt.primaries) {
+ t.Errorf("#%v: got %v; want %v", i, primaries, tt.primaries)
+ }
+ if !reflect.DeepEqual(fallbacks, tt.fallbacks) {
+ t.Errorf("#%v: got %v; want %v", i, fallbacks, tt.fallbacks)
+ }
}
}
}
diff --git a/src/net/listen_test.go b/src/net/listen_test.go
new file mode 100644
index 0000000000..8f43c846d9
--- /dev/null
+++ b/src/net/listen_test.go
@@ -0,0 +1,687 @@
+// 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 !plan9
+
+package net
+
+import (
+ "fmt"
+ "os"
+ "runtime"
+ "syscall"
+ "testing"
+)
+
+func (ln *TCPListener) port() string {
+ _, port, err := SplitHostPort(ln.Addr().String())
+ if err != nil {
+ return ""
+ }
+ return port
+}
+
+func (c *UDPConn) port() string {
+ _, port, err := SplitHostPort(c.LocalAddr().String())
+ if err != nil {
+ return ""
+ }
+ return port
+}
+
+var tcpListenerTests = []struct {
+ network string
+ address string
+}{
+ {"tcp", ""},
+ {"tcp", "0.0.0.0"},
+ {"tcp", "::ffff:0.0.0.0"},
+ {"tcp", "::"},
+
+ {"tcp", "127.0.0.1"},
+ {"tcp", "::ffff:127.0.0.1"},
+ {"tcp", "::1"},
+
+ {"tcp4", ""},
+ {"tcp4", "0.0.0.0"},
+ {"tcp4", "::ffff:0.0.0.0"},
+
+ {"tcp4", "127.0.0.1"},
+ {"tcp4", "::ffff:127.0.0.1"},
+
+ {"tcp6", ""},
+ {"tcp6", "::"},
+
+ {"tcp6", "::1"},
+}
+
+// TestTCPListener tests both single and double listen to a test
+// listener with same address family, same listening address and
+// same port.
+func TestTCPListener(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+
+ for _, tt := range tcpListenerTests {
+ if !testableListenArgs(tt.network, JoinHostPort(tt.address, "0"), "") {
+ t.Logf("skipping %s test", tt.network+" "+tt.address)
+ continue
+ }
+
+ ln1, err := Listen(tt.network, JoinHostPort(tt.address, "0"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := checkFirstListener(tt.network, ln1); err != nil {
+ ln1.Close()
+ t.Fatal(err)
+ }
+ ln2, err := Listen(tt.network, JoinHostPort(tt.address, ln1.(*TCPListener).port()))
+ if err == nil {
+ ln2.Close()
+ }
+ if err := checkSecondListener(tt.network, tt.address, err); err != nil {
+ ln1.Close()
+ t.Fatal(err)
+ }
+ ln1.Close()
+ }
+}
+
+var udpListenerTests = []struct {
+ network string
+ address string
+}{
+ {"udp", ""},
+ {"udp", "0.0.0.0"},
+ {"udp", "::ffff:0.0.0.0"},
+ {"udp", "::"},
+
+ {"udp", "127.0.0.1"},
+ {"udp", "::ffff:127.0.0.1"},
+ {"udp", "::1"},
+
+ {"udp4", ""},
+ {"udp4", "0.0.0.0"},
+ {"udp4", "::ffff:0.0.0.0"},
+
+ {"udp4", "127.0.0.1"},
+ {"udp4", "::ffff:127.0.0.1"},
+
+ {"udp6", ""},
+ {"udp6", "::"},
+
+ {"udp6", "::1"},
+}
+
+// TestUDPListener tests both single and double listen to a test
+// listener with same address family, same listening address and
+// same port.
+func TestUDPListener(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+
+ for _, tt := range udpListenerTests {
+ if !testableListenArgs(tt.network, JoinHostPort(tt.address, "0"), "") {
+ t.Logf("skipping %s test", tt.network+" "+tt.address)
+ continue
+ }
+
+ c1, err := ListenPacket(tt.network, JoinHostPort(tt.address, "0"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := checkFirstListener(tt.network, c1); err != nil {
+ c1.Close()
+ t.Fatal(err)
+ }
+ c2, err := ListenPacket(tt.network, JoinHostPort(tt.address, c1.(*UDPConn).port()))
+ if err == nil {
+ c2.Close()
+ }
+ if err := checkSecondListener(tt.network, tt.address, err); err != nil {
+ c1.Close()
+ t.Fatal(err)
+ }
+ c1.Close()
+ }
+}
+
+var dualStackTCPListenerTests = []struct {
+ network1, address1 string // first listener
+ network2, address2 string // second listener
+ xerr error // expected error value, nil or other
+}{
+ // Test cases and expected results for the attemping 2nd listen on the same port
+ // 1st listen 2nd listen darwin freebsd linux openbsd
+ // ------------------------------------------------------------------------------------
+ // "tcp" "" "tcp" "" - - - -
+ // "tcp" "" "tcp" "0.0.0.0" - - - -
+ // "tcp" "0.0.0.0" "tcp" "" - - - -
+ // ------------------------------------------------------------------------------------
+ // "tcp" "" "tcp" "[::]" - - - ok
+ // "tcp" "[::]" "tcp" "" - - - ok
+ // "tcp" "0.0.0.0" "tcp" "[::]" - - - ok
+ // "tcp" "[::]" "tcp" "0.0.0.0" - - - ok
+ // "tcp" "[::ffff:0.0.0.0]" "tcp" "[::]" - - - ok
+ // "tcp" "[::]" "tcp" "[::ffff:0.0.0.0]" - - - ok
+ // ------------------------------------------------------------------------------------
+ // "tcp4" "" "tcp6" "" ok ok ok ok
+ // "tcp6" "" "tcp4" "" ok ok ok ok
+ // "tcp4" "0.0.0.0" "tcp6" "[::]" ok ok ok ok
+ // "tcp6" "[::]" "tcp4" "0.0.0.0" ok ok ok ok
+ // ------------------------------------------------------------------------------------
+ // "tcp" "127.0.0.1" "tcp" "[::1]" ok ok ok ok
+ // "tcp" "[::1]" "tcp" "127.0.0.1" ok ok ok ok
+ // "tcp4" "127.0.0.1" "tcp6" "[::1]" ok ok ok ok
+ // "tcp6" "[::1]" "tcp4" "127.0.0.1" ok ok ok ok
+ //
+ // Platform default configurations:
+ // darwin, kernel version 11.3.0
+ // net.inet6.ip6.v6only=0 (overridable by sysctl or IPV6_V6ONLY option)
+ // freebsd, kernel version 8.2
+ // net.inet6.ip6.v6only=1 (overridable by sysctl or IPV6_V6ONLY option)
+ // linux, kernel version 3.0.0
+ // net.ipv6.bindv6only=0 (overridable by sysctl or IPV6_V6ONLY option)
+ // openbsd, kernel version 5.0
+ // net.inet6.ip6.v6only=1 (overriding is prohibited)
+
+ {"tcp", "", "tcp", "", syscall.EADDRINUSE},
+ {"tcp", "", "tcp", "0.0.0.0", syscall.EADDRINUSE},
+ {"tcp", "0.0.0.0", "tcp", "", syscall.EADDRINUSE},
+
+ {"tcp", "", "tcp", "::", syscall.EADDRINUSE},
+ {"tcp", "::", "tcp", "", syscall.EADDRINUSE},
+ {"tcp", "0.0.0.0", "tcp", "::", syscall.EADDRINUSE},
+ {"tcp", "::", "tcp", "0.0.0.0", syscall.EADDRINUSE},
+ {"tcp", "::ffff:0.0.0.0", "tcp", "::", syscall.EADDRINUSE},
+ {"tcp", "::", "tcp", "::ffff:0.0.0.0", syscall.EADDRINUSE},
+
+ {"tcp4", "", "tcp6", "", nil},
+ {"tcp6", "", "tcp4", "", nil},
+ {"tcp4", "0.0.0.0", "tcp6", "::", nil},
+ {"tcp6", "::", "tcp4", "0.0.0.0", nil},
+
+ {"tcp", "127.0.0.1", "tcp", "::1", nil},
+ {"tcp", "::1", "tcp", "127.0.0.1", nil},
+ {"tcp4", "127.0.0.1", "tcp6", "::1", nil},
+ {"tcp6", "::1", "tcp4", "127.0.0.1", nil},
+}
+
+// TestDualStackTCPListener tests both single and double listen
+// to a test listener with various address families, different
+// listening address and same port.
+func TestDualStackTCPListener(t *testing.T) {
+ switch runtime.GOOS {
+ case "dragonfly":
+ t.Skip("not supported on DragonFly, see golang.org/issue/10729")
+ case "nacl", "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if !supportsIPv4 || !supportsIPv6 {
+ t.Skip("both IPv4 and IPv6 are required")
+ }
+
+ for _, tt := range dualStackTCPListenerTests {
+ if !testableListenArgs(tt.network1, JoinHostPort(tt.address1, "0"), "") {
+ t.Logf("skipping %s test", tt.network1+" "+tt.address1)
+ continue
+ }
+
+ if runtime.GOOS == "openbsd" && differentWildcardAddr(tt.address1, tt.address2) {
+ tt.xerr = nil
+ }
+ var firstErr, secondErr error
+ for i := 0; i < 5; i++ {
+ lns, err := newDualStackListener()
+ if err != nil {
+ t.Fatal(err)
+ }
+ port := lns[0].port()
+ for _, ln := range lns {
+ ln.Close()
+ }
+ var ln1 Listener
+ ln1, firstErr = Listen(tt.network1, JoinHostPort(tt.address1, port))
+ if firstErr != nil {
+ continue
+ }
+ if err := checkFirstListener(tt.network1, ln1); err != nil {
+ ln1.Close()
+ t.Fatal(err)
+ }
+ ln2, err := Listen(tt.network2, JoinHostPort(tt.address2, ln1.(*TCPListener).port()))
+ if err == nil {
+ ln2.Close()
+ }
+ if secondErr = checkDualStackSecondListener(tt.network2, tt.address2, err, tt.xerr); secondErr != nil {
+ ln1.Close()
+ continue
+ }
+ ln1.Close()
+ break
+ }
+ if firstErr != nil {
+ t.Error(firstErr)
+ }
+ if secondErr != nil {
+ t.Error(secondErr)
+ }
+ }
+}
+
+var dualStackUDPListenerTests = []struct {
+ network1, address1 string // first listener
+ network2, address2 string // second listener
+ xerr error // expected error value, nil or other
+}{
+ {"udp", "", "udp", "", syscall.EADDRINUSE},
+ {"udp", "", "udp", "0.0.0.0", syscall.EADDRINUSE},
+ {"udp", "0.0.0.0", "udp", "", syscall.EADDRINUSE},
+
+ {"udp", "", "udp", "::", syscall.EADDRINUSE},
+ {"udp", "::", "udp", "", syscall.EADDRINUSE},
+ {"udp", "0.0.0.0", "udp", "::", syscall.EADDRINUSE},
+ {"udp", "::", "udp", "0.0.0.0", syscall.EADDRINUSE},
+ {"udp", "::ffff:0.0.0.0", "udp", "::", syscall.EADDRINUSE},
+ {"udp", "::", "udp", "::ffff:0.0.0.0", syscall.EADDRINUSE},
+
+ {"udp4", "", "udp6", "", nil},
+ {"udp6", "", "udp4", "", nil},
+ {"udp4", "0.0.0.0", "udp6", "::", nil},
+ {"udp6", "::", "udp4", "0.0.0.0", nil},
+
+ {"udp", "127.0.0.1", "udp", "::1", nil},
+ {"udp", "::1", "udp", "127.0.0.1", nil},
+ {"udp4", "127.0.0.1", "udp6", "::1", nil},
+ {"udp6", "::1", "udp4", "127.0.0.1", nil},
+}
+
+// TestDualStackUDPListener tests both single and double listen
+// to a test listener with various address families, differnet
+// listening address and same port.
+func TestDualStackUDPListener(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if !supportsIPv4 || !supportsIPv6 {
+ t.Skip("both IPv4 and IPv6 are required")
+ }
+
+ for _, tt := range dualStackUDPListenerTests {
+ if !testableListenArgs(tt.network1, JoinHostPort(tt.address1, "0"), "") {
+ t.Logf("skipping %s test", tt.network1+" "+tt.address1)
+ continue
+ }
+
+ if runtime.GOOS == "openbsd" && differentWildcardAddr(tt.address1, tt.address2) {
+ tt.xerr = nil
+ }
+ var firstErr, secondErr error
+ for i := 0; i < 5; i++ {
+ cs, err := newDualStackPacketListener()
+ if err != nil {
+ t.Fatal(err)
+ }
+ port := cs[0].port()
+ for _, c := range cs {
+ c.Close()
+ }
+ var c1 PacketConn
+ c1, firstErr = ListenPacket(tt.network1, JoinHostPort(tt.address1, port))
+ if firstErr != nil {
+ continue
+ }
+ if err := checkFirstListener(tt.network1, c1); err != nil {
+ c1.Close()
+ t.Fatal(err)
+ }
+ c2, err := ListenPacket(tt.network2, JoinHostPort(tt.address2, c1.(*UDPConn).port()))
+ if err == nil {
+ c2.Close()
+ }
+ if secondErr = checkDualStackSecondListener(tt.network2, tt.address2, err, tt.xerr); secondErr != nil {
+ c1.Close()
+ continue
+ }
+ c1.Close()
+ break
+ }
+ if firstErr != nil {
+ t.Error(firstErr)
+ }
+ if secondErr != nil {
+ t.Error(secondErr)
+ }
+ }
+}
+
+func differentWildcardAddr(i, j string) bool {
+ if (i == "" || i == "0.0.0.0" || i == "::ffff:0.0.0.0") && (j == "" || j == "0.0.0.0" || j == "::ffff:0.0.0.0") {
+ return false
+ }
+ if i == "[::]" && j == "[::]" {
+ return false
+ }
+ return true
+}
+
+func checkFirstListener(network string, ln interface{}) error {
+ switch network {
+ case "tcp":
+ fd := ln.(*TCPListener).fd
+ if err := checkDualStackAddrFamily(fd); err != nil {
+ return err
+ }
+ case "tcp4":
+ fd := ln.(*TCPListener).fd
+ if fd.family != syscall.AF_INET {
+ return fmt.Errorf("%v got %v; want %v", fd.laddr, fd.family, syscall.AF_INET)
+ }
+ case "tcp6":
+ fd := ln.(*TCPListener).fd
+ if fd.family != syscall.AF_INET6 {
+ return fmt.Errorf("%v got %v; want %v", fd.laddr, fd.family, syscall.AF_INET6)
+ }
+ case "udp":
+ fd := ln.(*UDPConn).fd
+ if err := checkDualStackAddrFamily(fd); err != nil {
+ return err
+ }
+ case "udp4":
+ fd := ln.(*UDPConn).fd
+ if fd.family != syscall.AF_INET {
+ return fmt.Errorf("%v got %v; want %v", fd.laddr, fd.family, syscall.AF_INET)
+ }
+ case "udp6":
+ fd := ln.(*UDPConn).fd
+ if fd.family != syscall.AF_INET6 {
+ return fmt.Errorf("%v got %v; want %v", fd.laddr, fd.family, syscall.AF_INET6)
+ }
+ default:
+ return UnknownNetworkError(network)
+ }
+ return nil
+}
+
+func checkSecondListener(network, address string, err error) error {
+ switch network {
+ case "tcp", "tcp4", "tcp6":
+ if err == nil {
+ return fmt.Errorf("%s should fail", network+" "+address)
+ }
+ case "udp", "udp4", "udp6":
+ if err == nil {
+ return fmt.Errorf("%s should fail", network+" "+address)
+ }
+ default:
+ return UnknownNetworkError(network)
+ }
+ return nil
+}
+
+func checkDualStackSecondListener(network, address string, err, xerr error) error {
+ switch network {
+ case "tcp", "tcp4", "tcp6":
+ if xerr == nil && err != nil || xerr != nil && err == nil {
+ return fmt.Errorf("%s got %v; want %v", network+" "+address, err, xerr)
+ }
+ case "udp", "udp4", "udp6":
+ if xerr == nil && err != nil || xerr != nil && err == nil {
+ return fmt.Errorf("%s got %v; want %v", network+" "+address, err, xerr)
+ }
+ default:
+ return UnknownNetworkError(network)
+ }
+ return nil
+}
+
+func checkDualStackAddrFamily(fd *netFD) error {
+ switch a := fd.laddr.(type) {
+ case *TCPAddr:
+ // If a node under test supports both IPv6 capability
+ // and IPv6 IPv4-mapping capability, we can assume
+ // that the node listens on a wildcard address with an
+ // AF_INET6 socket.
+ if supportsIPv4map && fd.laddr.(*TCPAddr).isWildcard() {
+ if fd.family != syscall.AF_INET6 {
+ return fmt.Errorf("Listen(%s, %v) returns %v; want %v", fd.net, fd.laddr, fd.family, syscall.AF_INET6)
+ }
+ } else {
+ if fd.family != a.family() {
+ return fmt.Errorf("Listen(%s, %v) returns %v; want %v", fd.net, fd.laddr, fd.family, a.family())
+ }
+ }
+ case *UDPAddr:
+ // If a node under test supports both IPv6 capability
+ // and IPv6 IPv4-mapping capability, we can assume
+ // that the node listens on a wildcard address with an
+ // AF_INET6 socket.
+ if supportsIPv4map && fd.laddr.(*UDPAddr).isWildcard() {
+ if fd.family != syscall.AF_INET6 {
+ return fmt.Errorf("ListenPacket(%s, %v) returns %v; want %v", fd.net, fd.laddr, fd.family, syscall.AF_INET6)
+ }
+ } else {
+ if fd.family != a.family() {
+ return fmt.Errorf("ListenPacket(%s, %v) returns %v; want %v", fd.net, fd.laddr, fd.family, a.family())
+ }
+ }
+ default:
+ return fmt.Errorf("unexpected protocol address type: %T", a)
+ }
+ return nil
+}
+
+func TestWildWildcardListener(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if testing.Short() || !*testExternal {
+ t.Skip("avoid external network")
+ }
+
+ defer func() {
+ if p := recover(); p != nil {
+ t.Fatalf("panicked: %v", p)
+ }
+ }()
+
+ if ln, err := Listen("tcp", ""); err == nil {
+ ln.Close()
+ }
+ if ln, err := ListenPacket("udp", ""); err == nil {
+ ln.Close()
+ }
+ if ln, err := ListenTCP("tcp", nil); err == nil {
+ ln.Close()
+ }
+ if ln, err := ListenUDP("udp", nil); err == nil {
+ ln.Close()
+ }
+ if ln, err := ListenIP("ip:icmp", nil); err == nil {
+ ln.Close()
+ }
+}
+
+var ipv4MulticastListenerTests = []struct {
+ net string
+ gaddr *UDPAddr // see RFC 4727
+}{
+ {"udp", &UDPAddr{IP: IPv4(224, 0, 0, 254), Port: 12345}},
+
+ {"udp4", &UDPAddr{IP: IPv4(224, 0, 0, 254), Port: 12345}},
+}
+
+// TestIPv4MulticastListener tests both single and double listen to a
+// test listener with same address family, same group address and same
+// port.
+func TestIPv4MulticastListener(t *testing.T) {
+ switch runtime.GOOS {
+ case "android", "nacl", "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ case "solaris":
+ t.Skipf("not supported on solaris, see golang.org/issue/7399")
+ }
+
+ closer := func(cs []*UDPConn) {
+ for _, c := range cs {
+ if c != nil {
+ c.Close()
+ }
+ }
+ }
+
+ for _, ifi := range []*Interface{loopbackInterface(), nil} {
+ // Note that multicast interface assignment by system
+ // is not recommended because it usually relies on
+ // routing stuff for finding out an appropriate
+ // nexthop containing both network and link layer
+ // adjacencies.
+ if ifi == nil && !*testExternal {
+ continue
+ }
+ for _, tt := range ipv4MulticastListenerTests {
+ var err error
+ cs := make([]*UDPConn, 2)
+ if cs[0], err = ListenMulticastUDP(tt.net, ifi, tt.gaddr); err != nil {
+ t.Fatal(err)
+ }
+ if err := checkMulticastListener(cs[0], tt.gaddr.IP); err != nil {
+ closer(cs)
+ t.Fatal(err)
+ }
+ if cs[1], err = ListenMulticastUDP(tt.net, ifi, tt.gaddr); err != nil {
+ closer(cs)
+ t.Fatal(err)
+ }
+ if err := checkMulticastListener(cs[1], tt.gaddr.IP); err != nil {
+ closer(cs)
+ t.Fatal(err)
+ }
+ closer(cs)
+ }
+ }
+}
+
+var ipv6MulticastListenerTests = []struct {
+ net string
+ gaddr *UDPAddr // see RFC 4727
+}{
+ {"udp", &UDPAddr{IP: ParseIP("ff01::114"), Port: 12345}},
+ {"udp", &UDPAddr{IP: ParseIP("ff02::114"), Port: 12345}},
+ {"udp", &UDPAddr{IP: ParseIP("ff04::114"), Port: 12345}},
+ {"udp", &UDPAddr{IP: ParseIP("ff05::114"), Port: 12345}},
+ {"udp", &UDPAddr{IP: ParseIP("ff08::114"), Port: 12345}},
+ {"udp", &UDPAddr{IP: ParseIP("ff0e::114"), Port: 12345}},
+
+ {"udp6", &UDPAddr{IP: ParseIP("ff01::114"), Port: 12345}},
+ {"udp6", &UDPAddr{IP: ParseIP("ff02::114"), Port: 12345}},
+ {"udp6", &UDPAddr{IP: ParseIP("ff04::114"), Port: 12345}},
+ {"udp6", &UDPAddr{IP: ParseIP("ff05::114"), Port: 12345}},
+ {"udp6", &UDPAddr{IP: ParseIP("ff08::114"), Port: 12345}},
+ {"udp6", &UDPAddr{IP: ParseIP("ff0e::114"), Port: 12345}},
+}
+
+// TestIPv6MulticastListener tests both single and double listen to a
+// test listener with same address family, same group address and same
+// port.
+func TestIPv6MulticastListener(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ case "solaris":
+ t.Skipf("not supported on solaris, see issue 7399")
+ }
+ if !supportsIPv6 {
+ t.Skip("ipv6 is not supported")
+ }
+ if os.Getuid() != 0 {
+ t.Skip("must be root")
+ }
+
+ closer := func(cs []*UDPConn) {
+ for _, c := range cs {
+ if c != nil {
+ c.Close()
+ }
+ }
+ }
+
+ for _, ifi := range []*Interface{loopbackInterface(), nil} {
+ // Note that multicast interface assignment by system
+ // is not recommended because it usually relies on
+ // routing stuff for finding out an appropriate
+ // nexthop containing both network and link layer
+ // adjacencies.
+ if ifi == nil && (!*testExternal || !*testIPv6) {
+ continue
+ }
+ for _, tt := range ipv6MulticastListenerTests {
+ var err error
+ cs := make([]*UDPConn, 2)
+ if cs[0], err = ListenMulticastUDP(tt.net, ifi, tt.gaddr); err != nil {
+ t.Fatal(err)
+ }
+ if err := checkMulticastListener(cs[0], tt.gaddr.IP); err != nil {
+ closer(cs)
+ t.Fatal(err)
+ }
+ if cs[1], err = ListenMulticastUDP(tt.net, ifi, tt.gaddr); err != nil {
+ closer(cs)
+ t.Fatal(err)
+ }
+ if err := checkMulticastListener(cs[1], tt.gaddr.IP); err != nil {
+ closer(cs)
+ t.Fatal(err)
+ }
+ closer(cs)
+ }
+ }
+}
+
+func checkMulticastListener(c *UDPConn, ip IP) error {
+ if ok, err := multicastRIBContains(ip); err != nil {
+ return err
+ } else if !ok {
+ return fmt.Errorf("%s not found in multicast rib", ip.String())
+ }
+ la := c.LocalAddr()
+ if la, ok := la.(*UDPAddr); !ok || la.Port == 0 {
+ return fmt.Errorf("got %v; want a proper address with non-zero port number", la)
+ }
+ return nil
+}
+
+func multicastRIBContains(ip IP) (bool, error) {
+ switch runtime.GOOS {
+ case "dragonfly", "netbsd", "openbsd", "plan9", "solaris", "windows":
+ return true, nil // not implemented yet
+ case "linux":
+ if runtime.GOARCH == "arm" || runtime.GOARCH == "alpha" {
+ return true, nil // not implemented yet
+ }
+ }
+ ift, err := Interfaces()
+ if err != nil {
+ return false, err
+ }
+ for _, ifi := range ift {
+ ifmat, err := ifi.MulticastAddrs()
+ if err != nil {
+ return false, err
+ }
+ for _, ifma := range ifmat {
+ if ifma.(*IPAddr).IP.Equal(ip) {
+ return true, nil
+ }
+ }
+ }
+ return false, nil
+}
diff --git a/src/net/lookup.go b/src/net/lookup.go
index 65abc81309..e2becc5a90 100644
--- a/src/net/lookup.go
+++ b/src/net/lookup.go
@@ -4,7 +4,10 @@
package net
-import "time"
+import (
+ "internal/singleflight"
+ "time"
+)
// protocols contains minimal mappings between internet protocol
// names and numbers for platforms that don't have a complete list of
@@ -39,14 +42,14 @@ func LookupIP(host string) (ips []IP, err error) {
return
}
-var lookupGroup singleflight
+var lookupGroup singleflight.Group
// lookupIPMerge wraps lookupIP, but makes sure that for any given
// host, only one lookup is in-flight at a time. The returned memory
// is always owned by the caller.
func lookupIPMerge(host string) (addrs []IPAddr, err error) {
addrsi, err, shared := lookupGroup.Do(host, func() (interface{}, error) {
- return lookupIP(host)
+ return testHookLookupIP(lookupIP, host)
})
return lookupIPReturn(addrsi, err, shared)
}
@@ -84,7 +87,7 @@ func lookupIPDeadline(host string, deadline time.Time) (addrs []IPAddr, err erro
defer t.Stop()
ch := lookupGroup.DoChan(host, func() (interface{}, error) {
- return lookupIP(host)
+ return testHookLookupIP(lookupIP, host)
})
select {
@@ -98,7 +101,7 @@ func lookupIPDeadline(host string, deadline time.Time) (addrs []IPAddr, err erro
return nil, errTimeout
case r := <-ch:
- return lookupIPReturn(r.v, r.err, r.shared)
+ return lookupIPReturn(r.Val, r.Err, r.Shared)
}
}
@@ -129,22 +132,22 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err err
}
// LookupMX returns the DNS MX records for the given domain name sorted by preference.
-func LookupMX(name string) (mx []*MX, err error) {
+func LookupMX(name string) (mxs []*MX, err error) {
return lookupMX(name)
}
// LookupNS returns the DNS NS records for the given domain name.
-func LookupNS(name string) (ns []*NS, err error) {
+func LookupNS(name string) (nss []*NS, err error) {
return lookupNS(name)
}
// LookupTXT returns the DNS TXT records for the given domain name.
-func LookupTXT(name string) (txt []string, err error) {
+func LookupTXT(name string) (txts []string, err error) {
return lookupTXT(name)
}
// LookupAddr performs a reverse lookup for the given address, returning a list
// of names mapping to that address.
-func LookupAddr(addr string) (name []string, err error) {
+func LookupAddr(addr string) (names []string, err error) {
return lookupAddr(addr)
}
diff --git a/src/net/lookup_plan9.go b/src/net/lookup_plan9.go
index 73abbad93b..c6274640bb 100644
--- a/src/net/lookup_plan9.go
+++ b/src/net/lookup_plan9.go
@@ -101,19 +101,18 @@ func lookupProtocol(name string) (proto int, err error) {
if err != nil {
return 0, err
}
- unknownProtoError := errors.New("unknown IP protocol specified: " + name)
if len(lines) == 0 {
- return 0, unknownProtoError
+ return 0, UnknownNetworkError(name)
}
f := getFields(lines[0])
if len(f) < 2 {
- return 0, unknownProtoError
+ return 0, UnknownNetworkError(name)
}
s := f[1]
if n, _, ok := dtoi(s, byteIndex(s, '=')+1); ok {
return n, nil
}
- return 0, unknownProtoError
+ return 0, UnknownNetworkError(name)
}
func lookupHost(host string) (addrs []string, err error) {
@@ -173,7 +172,7 @@ func lookupPort(network, service string) (port int, err error) {
if err != nil {
return
}
- unknownPortError := &AddrError{"unknown port", network + "/" + service}
+ unknownPortError := &AddrError{Err: "unknown port", Addr: network + "/" + service}
if len(lines) == 0 {
return 0, unknownPortError
}
diff --git a/src/net/lookup_test.go b/src/net/lookup_test.go
index 430adfd476..1f36184d55 100644
--- a/src/net/lookup_test.go
+++ b/src/net/lookup_test.go
@@ -2,20 +2,26 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// TODO It would be nice to use a mock DNS server, to eliminate
-// external dependencies.
-
package net
import (
- "flag"
"fmt"
"strings"
"testing"
"time"
)
-var testExternal = flag.Bool("external", true, "allow use of external networks during long test")
+func lookupLocalhost(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, error) {
+ switch host {
+ case "localhost":
+ return []IPAddr{
+ {IP: IPv4(127, 0, 0, 1)},
+ {IP: IPv6loopback},
+ }, nil
+ default:
+ return fn(host)
+ }
+}
var lookupGoogleSRVTests = []struct {
service, proto, name string
@@ -33,7 +39,7 @@ var lookupGoogleSRVTests = []struct {
func TestLookupGoogleSRV(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
+ t.Skip("avoid external network")
}
for _, tt := range lookupGoogleSRVTests {
@@ -57,7 +63,7 @@ func TestLookupGoogleSRV(t *testing.T) {
func TestLookupGmailMX(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
+ t.Skip("avoid external network")
}
mxs, err := LookupMX("gmail.com")
@@ -76,7 +82,7 @@ func TestLookupGmailMX(t *testing.T) {
func TestLookupGmailNS(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
+ t.Skip("avoid external network")
}
nss, err := LookupNS("gmail.com")
@@ -95,7 +101,7 @@ func TestLookupGmailNS(t *testing.T) {
func TestLookupGmailTXT(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
+ t.Skip("avoid external network")
}
txts, err := LookupTXT("gmail.com")
@@ -124,7 +130,7 @@ var lookupGooglePublicDNSAddrs = []struct {
func TestLookupGooglePublicDNSAddr(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
+ t.Skip("avoid external network")
}
for _, tt := range lookupGooglePublicDNSAddrs {
@@ -145,7 +151,7 @@ func TestLookupGooglePublicDNSAddr(t *testing.T) {
func TestLookupIANACNAME(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
+ t.Skip("avoid external network")
}
cname, err := LookupCNAME("www.iana.org")
@@ -159,7 +165,7 @@ func TestLookupIANACNAME(t *testing.T) {
func TestLookupGoogleHost(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
+ t.Skip("avoid external network")
}
addrs, err := LookupHost("google.com")
@@ -178,7 +184,7 @@ func TestLookupGoogleHost(t *testing.T) {
func TestLookupGoogleIP(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
+ t.Skip("avoid external network")
}
ips, err := LookupIP("google.com")
@@ -232,8 +238,6 @@ func TestReverseAddress(t *testing.T) {
}
}
-var testDNSFlood = flag.Bool("dnsflood", false, "whether to test dns query flooding")
-
func TestLookupIPDeadline(t *testing.T) {
if !*testDNSFlood {
t.Skip("test disabled; use -dnsflood to enable")
diff --git a/src/net/lookup_unix.go b/src/net/lookup_unix.go
index 473adf87f6..6484414e4b 100644
--- a/src/net/lookup_unix.go
+++ b/src/net/lookup_unix.go
@@ -6,10 +6,7 @@
package net
-import (
- "errors"
- "sync"
-)
+import "sync"
var onceReadProtocols sync.Once
@@ -43,126 +40,129 @@ func readProtocols() {
// lookupProtocol looks up IP protocol name in /etc/protocols and
// returns correspondent protocol number.
-func lookupProtocol(name string) (proto int, err error) {
+func lookupProtocol(name string) (int, error) {
onceReadProtocols.Do(readProtocols)
proto, found := protocols[name]
if !found {
- return 0, errors.New("unknown IP protocol specified: " + name)
+ return 0, &AddrError{Err: "unknown IP protocol specified", Addr: name}
}
- return
+ return proto, nil
}
func lookupHost(host string) (addrs []string, err error) {
- addrs, err, ok := cgoLookupHost(host)
- if !ok {
- addrs, err = goLookupHost(host)
+ order := systemConf().hostLookupOrder(host)
+ if order == hostLookupCgo {
+ if addrs, err, ok := cgoLookupHost(host); ok {
+ return addrs, err
+ }
+ // cgo not available (or netgo); fall back to Go's DNS resolver
+ order = hostLookupFilesDNS
}
- return
+ return goLookupHostOrder(host, order)
}
func lookupIP(host string) (addrs []IPAddr, err error) {
- addrs, err, ok := cgoLookupIP(host)
- if !ok {
- addrs, err = goLookupIP(host)
+ order := systemConf().hostLookupOrder(host)
+ if order == hostLookupCgo {
+ if addrs, err, ok := cgoLookupIP(host); ok {
+ return addrs, err
+ }
+ // cgo not available (or netgo); fall back to Go's DNS resolver
+ order = hostLookupFilesDNS
}
- return
+ return goLookupIPOrder(host, order)
}
-func lookupPort(network, service string) (port int, err error) {
+func lookupPort(network, service string) (int, error) {
port, err, ok := cgoLookupPort(network, service)
if !ok {
port, err = goLookupPort(network, service)
}
- return
+ return port, err
}
-func lookupCNAME(name string) (cname string, err error) {
+func lookupCNAME(name string) (string, error) {
cname, err, ok := cgoLookupCNAME(name)
if !ok {
cname, err = goLookupCNAME(name)
}
- return
+ return cname, err
}
-func lookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) {
+func lookupSRV(service, proto, name string) (string, []*SRV, error) {
var target string
if service == "" && proto == "" {
target = name
} else {
target = "_" + service + "._" + proto + "." + name
}
- var records []dnsRR
- cname, records, err = lookup(target, dnsTypeSRV)
+ cname, rrs, err := lookup(target, dnsTypeSRV)
if err != nil {
- return
+ return "", nil, err
}
- addrs = make([]*SRV, len(records))
- for i, rr := range records {
- r := rr.(*dnsRR_SRV)
- addrs[i] = &SRV{r.Target, r.Port, r.Priority, r.Weight}
+ srvs := make([]*SRV, len(rrs))
+ for i, rr := range rrs {
+ rr := rr.(*dnsRR_SRV)
+ srvs[i] = &SRV{Target: rr.Target, Port: rr.Port, Priority: rr.Priority, Weight: rr.Weight}
}
- byPriorityWeight(addrs).sort()
- return
+ byPriorityWeight(srvs).sort()
+ return cname, srvs, nil
}
-func lookupMX(name string) (mx []*MX, err error) {
- _, records, err := lookup(name, dnsTypeMX)
+func lookupMX(name string) ([]*MX, error) {
+ _, rrs, err := lookup(name, dnsTypeMX)
if err != nil {
- return
+ return nil, err
}
- mx = make([]*MX, len(records))
- for i, rr := range records {
- r := rr.(*dnsRR_MX)
- mx[i] = &MX{r.Mx, r.Pref}
+ mxs := make([]*MX, len(rrs))
+ for i, rr := range rrs {
+ rr := rr.(*dnsRR_MX)
+ mxs[i] = &MX{Host: rr.Mx, Pref: rr.Pref}
}
- byPref(mx).sort()
- return
+ byPref(mxs).sort()
+ return mxs, nil
}
-func lookupNS(name string) (ns []*NS, err error) {
- _, records, err := lookup(name, dnsTypeNS)
+func lookupNS(name string) ([]*NS, error) {
+ _, rrs, err := lookup(name, dnsTypeNS)
if err != nil {
- return
+ return nil, err
}
- ns = make([]*NS, len(records))
- for i, r := range records {
- r := r.(*dnsRR_NS)
- ns[i] = &NS{r.Ns}
+ nss := make([]*NS, len(rrs))
+ for i, rr := range rrs {
+ nss[i] = &NS{Host: rr.(*dnsRR_NS).Ns}
}
- return
+ return nss, nil
}
-func lookupTXT(name string) (txt []string, err error) {
- _, records, err := lookup(name, dnsTypeTXT)
+func lookupTXT(name string) ([]string, error) {
+ _, rrs, err := lookup(name, dnsTypeTXT)
if err != nil {
- return
+ return nil, err
}
- txt = make([]string, len(records))
- for i, r := range records {
- txt[i] = r.(*dnsRR_TXT).Txt
+ txts := make([]string, len(rrs))
+ for i, rr := range rrs {
+ txts[i] = rr.(*dnsRR_TXT).Txt
}
- return
+ return txts, nil
}
-func lookupAddr(addr string) (name []string, err error) {
- name = lookupStaticAddr(addr)
- if len(name) > 0 {
- return
+func lookupAddr(addr string) ([]string, error) {
+ names := lookupStaticAddr(addr)
+ if len(names) > 0 {
+ return names, nil
}
- var arpa string
- arpa, err = reverseaddr(addr)
+ arpa, err := reverseaddr(addr)
if err != nil {
- return
+ return nil, err
}
- var records []dnsRR
- _, records, err = lookup(arpa, dnsTypePTR)
+ _, rrs, err := lookup(arpa, dnsTypePTR)
if err != nil {
- return
+ return nil, err
}
- name = make([]string, len(records))
- for i := range records {
- r := records[i].(*dnsRR_PTR)
- name[i] = r.Ptr
+ ptrs := make([]string, len(rrs))
+ for i, rr := range rrs {
+ ptrs[i] = rr.(*dnsRR_PTR).Ptr
}
- return
+ return ptrs, nil
}
diff --git a/src/net/lookup_windows.go b/src/net/lookup_windows.go
index 6a8d9181ba..1b6d392f66 100644
--- a/src/net/lookup_windows.go
+++ b/src/net/lookup_windows.go
@@ -19,13 +19,13 @@ var (
func getprotobyname(name string) (proto int, err error) {
p, err := syscall.GetProtoByName(name)
if err != nil {
- return 0, os.NewSyscallError("GetProtoByName", err)
+ return 0, os.NewSyscallError("getorotobyname", err)
}
return int(p.Proto), nil
}
// lookupProtocol looks up IP protocol name and returns correspondent protocol number.
-func lookupProtocol(name string) (proto int, err error) {
+func lookupProtocol(name string) (int, error) {
// GetProtoByName return value is stored in thread local storage.
// Start new os thread before the call to prevent races.
type result struct {
@@ -46,27 +46,28 @@ func lookupProtocol(name string) (proto int, err error) {
if proto, ok := protocols[name]; ok {
return proto, nil
}
+ r.err = &DNSError{Err: r.err.Error(), Name: name}
}
return r.proto, r.err
}
-func lookupHost(name string) (addrs []string, err error) {
+func lookupHost(name string) ([]string, error) {
ips, err := LookupIP(name)
if err != nil {
- return
+ return nil, err
}
- addrs = make([]string, 0, len(ips))
+ addrs := make([]string, 0, len(ips))
for _, ip := range ips {
addrs = append(addrs, ip.String())
}
- return
+ return addrs, nil
}
func gethostbyname(name string) (addrs []IPAddr, err error) {
// caller already acquired thread
h, err := syscall.GetHostByName(name)
if err != nil {
- return nil, os.NewSyscallError("GetHostByName", err)
+ return nil, os.NewSyscallError("gethostbyname", err)
}
switch h.AddrType {
case syscall.AF_INET:
@@ -77,12 +78,12 @@ func gethostbyname(name string) (addrs []IPAddr, err error) {
}
addrs = addrs[0:i]
default: // TODO(vcc): Implement non IPv4 address lookups.
- return nil, os.NewSyscallError("LookupIP", syscall.EWINDOWS)
+ return nil, syscall.EWINDOWS
}
return addrs, nil
}
-func oldLookupIP(name string) (addrs []IPAddr, err error) {
+func oldLookupIP(name string) ([]IPAddr, error) {
// GetHostByName return value is stored in thread local storage.
// Start new os thread before the call to prevent races.
type result struct {
@@ -99,10 +100,13 @@ func oldLookupIP(name string) (addrs []IPAddr, err error) {
ch <- result{addrs: addrs, err: err}
}()
r := <-ch
- return addrs, r.err
+ if r.err != nil {
+ r.err = &DNSError{Err: r.err.Error(), Name: name}
+ }
+ return r.addrs, r.err
}
-func newLookupIP(name string) (addrs []IPAddr, err error) {
+func newLookupIP(name string) ([]IPAddr, error) {
acquireThread()
defer releaseThread()
hints := syscall.AddrinfoW{
@@ -113,10 +117,10 @@ func newLookupIP(name string) (addrs []IPAddr, err error) {
var result *syscall.AddrinfoW
e := syscall.GetAddrInfoW(syscall.StringToUTF16Ptr(name), nil, &hints, &result)
if e != nil {
- return nil, os.NewSyscallError("GetAddrInfoW", e)
+ return nil, &DNSError{Err: os.NewSyscallError("getaddrinfow", e).Error(), Name: name}
}
defer syscall.FreeAddrInfoW(result)
- addrs = make([]IPAddr, 0, 5)
+ addrs := make([]IPAddr, 0, 5)
for ; result != nil; result = result.Next {
addr := unsafe.Pointer(result.Addr)
switch result.Family {
@@ -128,13 +132,13 @@ func newLookupIP(name string) (addrs []IPAddr, err error) {
zone := zoneToString(int((*syscall.RawSockaddrInet6)(addr).Scope_id))
addrs = append(addrs, IPAddr{IP: IP{a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]}, Zone: zone})
default:
- return nil, os.NewSyscallError("LookupIP", syscall.EWINDOWS)
+ return nil, &DNSError{Err: syscall.EWINDOWS.Error(), Name: name}
}
}
return addrs, nil
}
-func getservbyname(network, service string) (port int, err error) {
+func getservbyname(network, service string) (int, error) {
acquireThread()
defer releaseThread()
switch network {
@@ -145,12 +149,12 @@ func getservbyname(network, service string) (port int, err error) {
}
s, err := syscall.GetServByName(service, network)
if err != nil {
- return 0, os.NewSyscallError("GetServByName", err)
+ return 0, os.NewSyscallError("getservbyname", err)
}
return int(syscall.Ntohs(s.Port)), nil
}
-func oldLookupPort(network, service string) (port int, err error) {
+func oldLookupPort(network, service string) (int, error) {
// GetServByName return value is stored in thread local storage.
// Start new os thread before the call to prevent races.
type result struct {
@@ -167,10 +171,13 @@ func oldLookupPort(network, service string) (port int, err error) {
ch <- result{port: port, err: err}
}()
r := <-ch
+ if r.err != nil {
+ r.err = &DNSError{Err: r.err.Error(), Name: network + "/" + service}
+ }
return r.port, r.err
}
-func newLookupPort(network, service string) (port int, err error) {
+func newLookupPort(network, service string) (int, error) {
acquireThread()
defer releaseThread()
var stype int32
@@ -188,11 +195,11 @@ func newLookupPort(network, service string) (port int, err error) {
var result *syscall.AddrinfoW
e := syscall.GetAddrInfoW(nil, syscall.StringToUTF16Ptr(service), &hints, &result)
if e != nil {
- return 0, os.NewSyscallError("GetAddrInfoW", e)
+ return 0, &DNSError{Err: os.NewSyscallError("getaddrinfow", e).Error(), Name: network + "/" + service}
}
defer syscall.FreeAddrInfoW(result)
if result == nil {
- return 0, os.NewSyscallError("LookupPort", syscall.EINVAL)
+ return 0, &DNSError{Err: syscall.EINVAL.Error(), Name: network + "/" + service}
}
addr := unsafe.Pointer(result.Addr)
switch result.Family {
@@ -203,10 +210,10 @@ func newLookupPort(network, service string) (port int, err error) {
a := (*syscall.RawSockaddrInet6)(addr)
return int(syscall.Ntohs(a.Port)), nil
}
- return 0, os.NewSyscallError("LookupPort", syscall.EINVAL)
+ return 0, &DNSError{Err: syscall.EINVAL.Error(), Name: network + "/" + service}
}
-func lookupCNAME(name string) (cname string, err error) {
+func lookupCNAME(name string) (string, error) {
acquireThread()
defer releaseThread()
var r *syscall.DNSRecord
@@ -220,16 +227,16 @@ func lookupCNAME(name string) (cname string, err error) {
return name, nil
}
if e != nil {
- return "", os.NewSyscallError("LookupCNAME", e)
+ return "", &DNSError{Err: os.NewSyscallError("dnsquery", e).Error(), Name: name}
}
defer syscall.DnsRecordListFree(r, 1)
resolved := resolveCNAME(syscall.StringToUTF16Ptr(name), r)
- cname = syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(resolved))[:]) + "."
- return
+ cname := syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(resolved))[:]) + "."
+ return cname, nil
}
-func lookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) {
+func lookupSRV(service, proto, name string) (string, []*SRV, error) {
acquireThread()
defer releaseThread()
var target string
@@ -241,78 +248,78 @@ func lookupSRV(service, proto, name string) (cname string, addrs []*SRV, err err
var r *syscall.DNSRecord
e := syscall.DnsQuery(target, syscall.DNS_TYPE_SRV, 0, nil, &r, nil)
if e != nil {
- return "", nil, os.NewSyscallError("LookupSRV", e)
+ return "", nil, &DNSError{Err: os.NewSyscallError("dnsquery", e).Error(), Name: target}
}
defer syscall.DnsRecordListFree(r, 1)
- addrs = make([]*SRV, 0, 10)
+ srvs := make([]*SRV, 0, 10)
for _, p := range validRecs(r, syscall.DNS_TYPE_SRV, target) {
v := (*syscall.DNSSRVData)(unsafe.Pointer(&p.Data[0]))
- addrs = append(addrs, &SRV{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Target))[:]), v.Port, v.Priority, v.Weight})
+ srvs = append(srvs, &SRV{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Target))[:]), v.Port, v.Priority, v.Weight})
}
- byPriorityWeight(addrs).sort()
- return name, addrs, nil
+ byPriorityWeight(srvs).sort()
+ return name, srvs, nil
}
-func lookupMX(name string) (mx []*MX, err error) {
+func lookupMX(name string) ([]*MX, error) {
acquireThread()
defer releaseThread()
var r *syscall.DNSRecord
e := syscall.DnsQuery(name, syscall.DNS_TYPE_MX, 0, nil, &r, nil)
if e != nil {
- return nil, os.NewSyscallError("LookupMX", e)
+ return nil, &DNSError{Err: os.NewSyscallError("dnsquery", e).Error(), Name: name}
}
defer syscall.DnsRecordListFree(r, 1)
- mx = make([]*MX, 0, 10)
+ mxs := make([]*MX, 0, 10)
for _, p := range validRecs(r, syscall.DNS_TYPE_MX, name) {
v := (*syscall.DNSMXData)(unsafe.Pointer(&p.Data[0]))
- mx = append(mx, &MX{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.NameExchange))[:]) + ".", v.Preference})
+ mxs = append(mxs, &MX{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.NameExchange))[:]) + ".", v.Preference})
}
- byPref(mx).sort()
- return mx, nil
+ byPref(mxs).sort()
+ return mxs, nil
}
-func lookupNS(name string) (ns []*NS, err error) {
+func lookupNS(name string) ([]*NS, error) {
acquireThread()
defer releaseThread()
var r *syscall.DNSRecord
e := syscall.DnsQuery(name, syscall.DNS_TYPE_NS, 0, nil, &r, nil)
if e != nil {
- return nil, os.NewSyscallError("LookupNS", e)
+ return nil, &DNSError{Err: os.NewSyscallError("dnsquery", e).Error(), Name: name}
}
defer syscall.DnsRecordListFree(r, 1)
- ns = make([]*NS, 0, 10)
+ nss := make([]*NS, 0, 10)
for _, p := range validRecs(r, syscall.DNS_TYPE_NS, name) {
v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0]))
- ns = append(ns, &NS{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]) + "."})
+ nss = append(nss, &NS{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]) + "."})
}
- return ns, nil
+ return nss, nil
}
-func lookupTXT(name string) (txt []string, err error) {
+func lookupTXT(name string) ([]string, error) {
acquireThread()
defer releaseThread()
var r *syscall.DNSRecord
e := syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &r, nil)
if e != nil {
- return nil, os.NewSyscallError("LookupTXT", e)
+ return nil, &DNSError{Err: os.NewSyscallError("dnsquery", e).Error(), Name: name}
}
defer syscall.DnsRecordListFree(r, 1)
- txt = make([]string, 0, 10)
+ txts := make([]string, 0, 10)
for _, p := range validRecs(r, syscall.DNS_TYPE_TEXT, name) {
d := (*syscall.DNSTXTData)(unsafe.Pointer(&p.Data[0]))
for _, v := range (*[1 << 10]*uint16)(unsafe.Pointer(&(d.StringArray[0])))[:d.StringCount] {
s := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(v))[:])
- txt = append(txt, s)
+ txts = append(txts, s)
}
}
- return
+ return txts, nil
}
-func lookupAddr(addr string) (name []string, err error) {
+func lookupAddr(addr string) ([]string, error) {
acquireThread()
defer releaseThread()
arpa, err := reverseaddr(addr)
@@ -322,16 +329,16 @@ func lookupAddr(addr string) (name []string, err error) {
var r *syscall.DNSRecord
e := syscall.DnsQuery(arpa, syscall.DNS_TYPE_PTR, 0, nil, &r, nil)
if e != nil {
- return nil, os.NewSyscallError("LookupAddr", e)
+ return nil, &DNSError{Err: os.NewSyscallError("dnsquery", e).Error(), Name: addr}
}
defer syscall.DnsRecordListFree(r, 1)
- name = make([]string, 0, 10)
+ ptrs := make([]string, 0, 10)
for _, p := range validRecs(r, syscall.DNS_TYPE_PTR, arpa) {
v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0]))
- name = append(name, syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]))
+ ptrs = append(ptrs, syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]))
}
- return name, nil
+ return ptrs, nil
}
const dnsSectionMask = 0x0003
diff --git a/src/net/lookup_windows_test.go b/src/net/lookup_windows_test.go
index 7495b5b578..3f64d8cec8 100644
--- a/src/net/lookup_windows_test.go
+++ b/src/net/lookup_windows_test.go
@@ -26,12 +26,13 @@ func toJson(v interface{}) string {
func TestLookupMX(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
+ t.Skip("avoid external network")
}
+
for _, server := range nslookupTestServers {
mx, err := LookupMX(server)
if err != nil {
- t.Errorf("failed %s: %s", server, err)
+ t.Error(err)
continue
}
if len(mx) == 0 {
@@ -52,8 +53,9 @@ func TestLookupMX(t *testing.T) {
func TestLookupCNAME(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
+ t.Skip("avoid external network")
}
+
for _, server := range nslookupTestServers {
cname, err := LookupCNAME(server)
if err != nil {
@@ -76,8 +78,9 @@ func TestLookupCNAME(t *testing.T) {
func TestLookupNS(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
+ t.Skip("avoid external network")
}
+
for _, server := range nslookupTestServers {
ns, err := LookupNS(server)
if err != nil {
@@ -103,8 +106,9 @@ func TestLookupNS(t *testing.T) {
func TestLookupTXT(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
+ t.Skip("avoid external network")
}
+
for _, server := range nslookupTestServers {
txt, err := LookupTXT(server)
if err != nil {
diff --git a/src/net/mac.go b/src/net/mac.go
index d616b1f689..8594a9146a 100644
--- a/src/net/mac.go
+++ b/src/net/mac.go
@@ -2,12 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// MAC address manipulations
-
package net
-import "errors"
-
const hexDigit = "0123456789abcdef"
// A HardwareAddr represents a physical hardware address.
@@ -82,5 +78,5 @@ func ParseMAC(s string) (hw HardwareAddr, err error) {
return hw, nil
error:
- return nil, errors.New("invalid MAC address: " + s)
+ return nil, &AddrError{Err: "invalid MAC address", Addr: s}
}
diff --git a/src/net/mac_test.go b/src/net/mac_test.go
index 8f9dc6685f..0af0c014f5 100644
--- a/src/net/mac_test.go
+++ b/src/net/mac_test.go
@@ -10,7 +10,7 @@ import (
"testing"
)
-var mactests = []struct {
+var parseMACTests = []struct {
in string
out HardwareAddr
err string
@@ -36,19 +36,18 @@ var mactests = []struct {
{"0123.4567.89AB.CDEF", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, ""},
}
-func match(err error, s string) bool {
- if s == "" {
- return err == nil
+func TestParseMAC(t *testing.T) {
+ match := func(err error, s string) bool {
+ if s == "" {
+ return err == nil
+ }
+ return err != nil && strings.Contains(err.Error(), s)
}
- return err != nil && strings.Contains(err.Error(), s)
-}
-func TestMACParseString(t *testing.T) {
- for i, tt := range mactests {
+ for i, tt := range parseMACTests {
out, err := ParseMAC(tt.in)
if !reflect.DeepEqual(out, tt.out) || !match(err, tt.err) {
- t.Errorf("ParseMAC(%q) = %v, %v, want %v, %v", tt.in, out, err, tt.out,
- tt.err)
+ t.Errorf("ParseMAC(%q) = %v, %v, want %v, %v", tt.in, out, err, tt.out, tt.err)
}
if tt.err == "" {
// Verify that serialization works too, and that it round-trips.
diff --git a/src/net/main_plan9_test.go b/src/net/main_plan9_test.go
index bbd47aaaf6..94501cada9 100644
--- a/src/net/main_plan9_test.go
+++ b/src/net/main_plan9_test.go
@@ -9,3 +9,7 @@ func installTestHooks() {}
func uninstallTestHooks() {}
func forceCloseSockets() {}
+
+func enableSocketConnect() {}
+
+func disableSocketConnect(network string) {}
diff --git a/src/net/main_posix_test.go b/src/net/main_posix_test.go
new file mode 100644
index 0000000000..ead311c3cd
--- /dev/null
+++ b/src/net/main_posix_test.go
@@ -0,0 +1,50 @@
+// Copyright 2015 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 !plan9
+
+package net
+
+import (
+ "net/internal/socktest"
+ "strings"
+ "syscall"
+)
+
+func enableSocketConnect() {
+ sw.Set(socktest.FilterConnect, nil)
+}
+
+func disableSocketConnect(network string) {
+ ss := strings.Split(network, ":")
+ sw.Set(socktest.FilterConnect, func(so *socktest.Status) (socktest.AfterFilter, error) {
+ switch ss[0] {
+ case "tcp4":
+ if so.Cookie.Family() == syscall.AF_INET && so.Cookie.Type() == syscall.SOCK_STREAM {
+ return nil, syscall.EHOSTUNREACH
+ }
+ case "udp4":
+ if so.Cookie.Family() == syscall.AF_INET && so.Cookie.Type() == syscall.SOCK_DGRAM {
+ return nil, syscall.EHOSTUNREACH
+ }
+ case "ip4":
+ if so.Cookie.Family() == syscall.AF_INET && so.Cookie.Type() == syscall.SOCK_RAW {
+ return nil, syscall.EHOSTUNREACH
+ }
+ case "tcp6":
+ if so.Cookie.Family() == syscall.AF_INET6 && so.Cookie.Type() == syscall.SOCK_STREAM {
+ return nil, syscall.EHOSTUNREACH
+ }
+ case "udp6":
+ if so.Cookie.Family() == syscall.AF_INET6 && so.Cookie.Type() == syscall.SOCK_DGRAM {
+ return nil, syscall.EHOSTUNREACH
+ }
+ case "ip6":
+ if so.Cookie.Family() == syscall.AF_INET6 && so.Cookie.Type() == syscall.SOCK_RAW {
+ return nil, syscall.EHOSTUNREACH
+ }
+ }
+ return nil, nil
+ })
+}
diff --git a/src/net/main_test.go b/src/net/main_test.go
index bc0f92ed25..a56b9cd5f9 100644
--- a/src/net/main_test.go
+++ b/src/net/main_test.go
@@ -5,32 +5,93 @@
package net
import (
+ "flag"
"fmt"
"net/internal/socktest"
"os"
"runtime"
"sort"
"strings"
+ "sync"
"testing"
)
-var sw socktest.Switch
+var (
+ sw socktest.Switch
+
+ // uninstallTestHooks runs just before a run of benchmarks.
+ testHookUninstaller sync.Once
+)
+
+var (
+ testDNSFlood = flag.Bool("dnsflood", false, "whether to test DNS query flooding")
+
+ testExternal = flag.Bool("external", true, "allow use of external networks during long test")
+
+ // If external IPv4 connectivity exists, we can try dialing
+ // non-node/interface local scope IPv4 addresses.
+ testIPv4 = flag.Bool("ipv4", true, "assume external IPv4 connectivity exists")
+
+ // If external IPv6 connectivity exists, we can try dialing
+ // non-node/interface local scope IPv6 addresses.
+ testIPv6 = flag.Bool("ipv6", false, "assume external IPv6 connectivity exists")
+)
func TestMain(m *testing.M) {
+ setupTestData()
installTestHooks()
st := m.Run()
+ testHookUninstaller.Do(func() { uninstallTestHooks() })
if !testing.Short() {
printLeakedGoroutines()
printLeakedSockets()
printSocketStats()
}
forceCloseSockets()
- uninstallTestHooks()
os.Exit(st)
}
+func setupTestData() {
+ if supportsIPv4 {
+ resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
+ {"tcp", "localhost:1", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 1}, nil},
+ {"tcp4", "localhost:2", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 2}, nil},
+ }...)
+ resolveUDPAddrTests = append(resolveUDPAddrTests, []resolveUDPAddrTest{
+ {"udp", "localhost:1", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 1}, nil},
+ {"udp4", "localhost:2", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 2}, nil},
+ }...)
+ resolveIPAddrTests = append(resolveIPAddrTests, []resolveIPAddrTest{
+ {"ip", "localhost", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
+ {"ip4", "localhost", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
+ }...)
+ }
+
+ if supportsIPv6 {
+ resolveTCPAddrTests = append(resolveTCPAddrTests, resolveTCPAddrTest{"tcp6", "localhost:3", &TCPAddr{IP: IPv6loopback, Port: 3}, nil})
+ resolveUDPAddrTests = append(resolveUDPAddrTests, resolveUDPAddrTest{"udp6", "localhost:3", &UDPAddr{IP: IPv6loopback, Port: 3}, nil})
+ resolveIPAddrTests = append(resolveIPAddrTests, resolveIPAddrTest{"ip6", "localhost", &IPAddr{IP: IPv6loopback}, nil})
+ }
+
+ if ifi := loopbackInterface(); ifi != nil {
+ index := fmt.Sprintf("%v", ifi.Index)
+ resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
+ {"tcp6", "[fe80::1%" + ifi.Name + "]:1", &TCPAddr{IP: ParseIP("fe80::1"), Port: 1, Zone: zoneToString(ifi.Index)}, nil},
+ {"tcp6", "[fe80::1%" + index + "]:2", &TCPAddr{IP: ParseIP("fe80::1"), Port: 2, Zone: index}, nil},
+ }...)
+ resolveUDPAddrTests = append(resolveUDPAddrTests, []resolveUDPAddrTest{
+ {"udp6", "[fe80::1%" + ifi.Name + "]:1", &UDPAddr{IP: ParseIP("fe80::1"), Port: 1, Zone: zoneToString(ifi.Index)}, nil},
+ {"udp6", "[fe80::1%" + index + "]:2", &UDPAddr{IP: ParseIP("fe80::1"), Port: 2, Zone: index}, nil},
+ }...)
+ resolveIPAddrTests = append(resolveIPAddrTests, []resolveIPAddrTest{
+ {"ip6", "fe80::1%" + ifi.Name, &IPAddr{IP: ParseIP("fe80::1"), Zone: zoneToString(ifi.Index)}, nil},
+ {"ip6", "fe80::1%" + index, &IPAddr{IP: ParseIP("fe80::1"), Zone: index}, nil},
+ }...)
+ }
+}
+
func printLeakedGoroutines() {
gss := leakedGoroutines()
if len(gss) == 0 {
@@ -43,8 +104,8 @@ func printLeakedGoroutines() {
fmt.Fprintf(os.Stderr, "\n")
}
-// leakedGoroutines returns a list of remaining goroutins used in test
-// cases.
+// leakedGoroutines returns a list of remaining goroutines used in
+// test cases.
func leakedGoroutines() []string {
var gss []string
b := make([]byte, 2<<20)
@@ -71,7 +132,7 @@ func printLeakedSockets() {
}
fmt.Fprintf(os.Stderr, "Leaked sockets:\n")
for s, so := range sos {
- fmt.Fprintf(os.Stderr, "%v: %+v\n", s, so)
+ fmt.Fprintf(os.Stderr, "%v: %v\n", s, so)
}
fmt.Fprintf(os.Stderr, "\n")
}
@@ -83,7 +144,7 @@ func printSocketStats() {
}
fmt.Fprintf(os.Stderr, "Socket statistical information:\n")
for _, st := range sts {
- fmt.Fprintf(os.Stderr, "%+v\n", st)
+ fmt.Fprintf(os.Stderr, "%v\n", st)
}
fmt.Fprintf(os.Stderr, "\n")
}
diff --git a/src/net/main_unix_test.go b/src/net/main_unix_test.go
index 637ac3dbc2..bfb4cd0065 100644
--- a/src/net/main_unix_test.go
+++ b/src/net/main_unix_test.go
@@ -11,6 +11,7 @@ var (
origSocket = socketFunc
origClose = closeFunc
origConnect = connectFunc
+ origListen = listenFunc
origAccept = acceptFunc
origGetsockoptInt = getsockoptIntFunc
@@ -22,6 +23,7 @@ func installTestHooks() {
socketFunc = sw.Socket
closeFunc = sw.Close
connectFunc = sw.Connect
+ listenFunc = sw.Listen
acceptFunc = sw.Accept
getsockoptIntFunc = sw.GetsockoptInt
@@ -34,6 +36,7 @@ func uninstallTestHooks() {
socketFunc = origSocket
closeFunc = origClose
connectFunc = origConnect
+ listenFunc = origListen
acceptFunc = origAccept
getsockoptIntFunc = origGetsockoptInt
diff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go
index 03c3796a50..2d829743ec 100644
--- a/src/net/main_windows_test.go
+++ b/src/net/main_windows_test.go
@@ -10,6 +10,7 @@ var (
origClosesocket = closeFunc
origConnect = connectFunc
origConnectEx = connectExFunc
+ origListen = listenFunc
)
func installTestHooks() {
@@ -17,6 +18,7 @@ func installTestHooks() {
closeFunc = sw.Closesocket
connectFunc = sw.Connect
connectExFunc = sw.ConnectEx
+ listenFunc = sw.Listen
}
func uninstallTestHooks() {
@@ -24,6 +26,7 @@ func uninstallTestHooks() {
closeFunc = origClosesocket
connectFunc = origConnect
connectExFunc = origConnectEx
+ listenFunc = origListen
}
func forceCloseSockets() {
diff --git a/src/net/mockserver_test.go b/src/net/mockserver_test.go
index 68ded5d757..62bcfa4022 100644
--- a/src/net/mockserver_test.go
+++ b/src/net/mockserver_test.go
@@ -4,11 +4,123 @@
package net
-import "sync"
+import (
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "sync"
+ "testing"
+ "time"
+)
+
+// testUnixAddr uses ioutil.TempFile to get a name that is unique.
+// It also uses /tmp directory in case it is prohibited to create UNIX
+// sockets in TMPDIR.
+func testUnixAddr() string {
+ f, err := ioutil.TempFile("", "go-nettest")
+ if err != nil {
+ panic(err)
+ }
+ addr := f.Name()
+ f.Close()
+ os.Remove(addr)
+ return addr
+}
+
+func newLocalListener(network string) (Listener, error) {
+ switch network {
+ case "tcp", "tcp4", "tcp6":
+ if supportsIPv4 {
+ return Listen("tcp4", "127.0.0.1:0")
+ }
+ if supportsIPv6 {
+ return Listen("tcp6", "[::1]:0")
+ }
+ case "unix", "unixpacket":
+ return Listen(network, testUnixAddr())
+ }
+ return nil, fmt.Errorf("%s is not supported", network)
+}
+
+func newDualStackListener() (lns []*TCPListener, err error) {
+ var args = []struct {
+ network string
+ TCPAddr
+ }{
+ {"tcp4", TCPAddr{IP: IPv4(127, 0, 0, 1)}},
+ {"tcp6", TCPAddr{IP: IPv6loopback}},
+ }
+ for i := 0; i < 64; i++ {
+ var port int
+ var lns []*TCPListener
+ for _, arg := range args {
+ arg.TCPAddr.Port = port
+ ln, err := ListenTCP(arg.network, &arg.TCPAddr)
+ if err != nil {
+ continue
+ }
+ port = ln.Addr().(*TCPAddr).Port
+ lns = append(lns, ln)
+ }
+ if len(lns) != len(args) {
+ for _, ln := range lns {
+ ln.Close()
+ }
+ continue
+ }
+ return lns, nil
+ }
+ return nil, errors.New("no dualstack port available")
+}
+
+type localServer struct {
+ lnmu sync.RWMutex
+ Listener
+ done chan bool // signal that indicates server stopped
+}
+
+func (ls *localServer) buildup(handler func(*localServer, Listener)) error {
+ go func() {
+ handler(ls, ls.Listener)
+ close(ls.done)
+ }()
+ return nil
+}
+
+func (ls *localServer) teardown() error {
+ ls.lnmu.Lock()
+ if ls.Listener != nil {
+ network := ls.Listener.Addr().Network()
+ address := ls.Listener.Addr().String()
+ ls.Listener.Close()
+ <-ls.done
+ ls.Listener = nil
+ switch network {
+ case "unix", "unixpacket":
+ os.Remove(address)
+ }
+ }
+ ls.lnmu.Unlock()
+ return nil
+}
+
+func newLocalServer(network string) (*localServer, error) {
+ ln, err := newLocalListener(network)
+ if err != nil {
+ return nil, err
+ }
+ return &localServer{Listener: ln, done: make(chan bool)}, nil
+}
type streamListener struct {
- net, addr string
- ln Listener
+ network, address string
+ Listener
+ done chan bool // signal that indicates server stopped
+}
+
+func (sl *streamListener) newLocalServer() (*localServer, error) {
+ return &localServer{Listener: sl.Listener, done: make(chan bool)}, nil
}
type dualStackServer struct {
@@ -20,9 +132,12 @@ type dualStackServer struct {
cs []Conn // established connections at the passive open side
}
-func (dss *dualStackServer) buildup(server func(*dualStackServer, Listener)) error {
+func (dss *dualStackServer) buildup(handler func(*dualStackServer, Listener)) error {
for i := range dss.lns {
- go server(dss, dss.lns[i].ln)
+ go func(i int) {
+ handler(dss, dss.lns[i].Listener)
+ close(dss.lns[i].done)
+ }(i)
}
return nil
}
@@ -34,12 +149,13 @@ func (dss *dualStackServer) putConn(c Conn) error {
return nil
}
-func (dss *dualStackServer) teardownNetwork(net string) error {
+func (dss *dualStackServer) teardownNetwork(network string) error {
dss.lnmu.Lock()
for i := range dss.lns {
- if net == dss.lns[i].net && dss.lns[i].ln != nil {
- dss.lns[i].ln.Close()
- dss.lns[i].ln = nil
+ if network == dss.lns[i].network && dss.lns[i].Listener != nil {
+ dss.lns[i].Listener.Close()
+ <-dss.lns[i].done
+ dss.lns[i].Listener = nil
}
}
dss.lnmu.Unlock()
@@ -49,15 +165,18 @@ func (dss *dualStackServer) teardownNetwork(net string) error {
func (dss *dualStackServer) teardown() error {
dss.lnmu.Lock()
for i := range dss.lns {
- if dss.lns[i].ln != nil {
- dss.lns[i].ln.Close()
+ if dss.lns[i].Listener != nil {
+ dss.lns[i].Listener.Close()
+ <-dss.lns[i].done
}
}
+ dss.lns = dss.lns[:0]
dss.lnmu.Unlock()
dss.cmu.Lock()
for _, c := range dss.cs {
c.Close()
}
+ dss.cs = dss.cs[:0]
dss.cmu.Unlock()
return nil
}
@@ -65,18 +184,333 @@ func (dss *dualStackServer) teardown() error {
func newDualStackServer(lns []streamListener) (*dualStackServer, error) {
dss := &dualStackServer{lns: lns, port: "0"}
for i := range dss.lns {
- ln, err := Listen(dss.lns[i].net, dss.lns[i].addr+":"+dss.port)
+ ln, err := Listen(dss.lns[i].network, JoinHostPort(dss.lns[i].address, dss.port))
if err != nil {
- dss.teardown()
+ for _, ln := range dss.lns {
+ ln.Listener.Close()
+ }
return nil, err
}
- dss.lns[i].ln = ln
+ dss.lns[i].Listener = ln
+ dss.lns[i].done = make(chan bool)
if dss.port == "0" {
if _, dss.port, err = SplitHostPort(ln.Addr().String()); err != nil {
- dss.teardown()
+ for _, ln := range dss.lns {
+ ln.Listener.Close()
+ }
return nil, err
}
}
}
return dss, nil
}
+
+func transponder(ln Listener, ch chan<- error) {
+ defer close(ch)
+
+ switch ln := ln.(type) {
+ case *TCPListener:
+ ln.SetDeadline(time.Now().Add(someTimeout))
+ case *UnixListener:
+ ln.SetDeadline(time.Now().Add(someTimeout))
+ }
+ c, err := ln.Accept()
+ if err != nil {
+ if perr := parseAcceptError(err); perr != nil {
+ ch <- perr
+ }
+ ch <- err
+ return
+ }
+ defer c.Close()
+
+ network := ln.Addr().Network()
+ if c.LocalAddr().Network() != network || c.LocalAddr().Network() != network {
+ ch <- fmt.Errorf("got %v->%v; expected %v->%v", c.LocalAddr().Network(), c.RemoteAddr().Network(), network, network)
+ return
+ }
+ c.SetDeadline(time.Now().Add(someTimeout))
+ c.SetReadDeadline(time.Now().Add(someTimeout))
+ c.SetWriteDeadline(time.Now().Add(someTimeout))
+
+ b := make([]byte, 256)
+ n, err := c.Read(b)
+ if err != nil {
+ if perr := parseReadError(err); perr != nil {
+ ch <- perr
+ }
+ ch <- err
+ return
+ }
+ if _, err := c.Write(b[:n]); err != nil {
+ if perr := parseWriteError(err); perr != nil {
+ ch <- perr
+ }
+ ch <- err
+ return
+ }
+}
+
+func transceiver(c Conn, wb []byte, ch chan<- error) {
+ defer close(ch)
+
+ c.SetDeadline(time.Now().Add(someTimeout))
+ c.SetReadDeadline(time.Now().Add(someTimeout))
+ c.SetWriteDeadline(time.Now().Add(someTimeout))
+
+ n, err := c.Write(wb)
+ if err != nil {
+ if perr := parseWriteError(err); perr != nil {
+ ch <- perr
+ }
+ ch <- err
+ return
+ }
+ if n != len(wb) {
+ ch <- fmt.Errorf("wrote %d; want %d", n, len(wb))
+ }
+ rb := make([]byte, len(wb))
+ n, err = c.Read(rb)
+ if err != nil {
+ if perr := parseReadError(err); perr != nil {
+ ch <- perr
+ }
+ ch <- err
+ return
+ }
+ if n != len(wb) {
+ ch <- fmt.Errorf("read %d; want %d", n, len(wb))
+ }
+}
+
+func timeoutReceiver(c Conn, d, min, max time.Duration, ch chan<- error) {
+ var err error
+ defer func() { ch <- err }()
+
+ t0 := time.Now()
+ if err = c.SetReadDeadline(time.Now().Add(d)); err != nil {
+ return
+ }
+ b := make([]byte, 256)
+ var n int
+ n, err = c.Read(b)
+ t1 := time.Now()
+ if n != 0 || err == nil || !err.(Error).Timeout() {
+ err = fmt.Errorf("Read did not return (0, timeout): (%d, %v)", n, err)
+ return
+ }
+ if dt := t1.Sub(t0); min > dt || dt > max && !testing.Short() {
+ err = fmt.Errorf("Read took %s; expected %s", dt, d)
+ return
+ }
+}
+
+func timeoutTransmitter(c Conn, d, min, max time.Duration, ch chan<- error) {
+ var err error
+ defer func() { ch <- err }()
+
+ t0 := time.Now()
+ if err = c.SetWriteDeadline(time.Now().Add(d)); err != nil {
+ return
+ }
+ var n int
+ for {
+ n, err = c.Write([]byte("TIMEOUT TRANSMITTER"))
+ if err != nil {
+ break
+ }
+ }
+ t1 := time.Now()
+ if err == nil || !err.(Error).Timeout() {
+ err = fmt.Errorf("Write did not return (any, timeout): (%d, %v)", n, err)
+ return
+ }
+ if dt := t1.Sub(t0); min > dt || dt > max && !testing.Short() {
+ err = fmt.Errorf("Write took %s; expected %s", dt, d)
+ return
+ }
+}
+
+func newLocalPacketListener(network string) (PacketConn, error) {
+ switch network {
+ case "udp", "udp4", "udp6":
+ if supportsIPv4 {
+ return ListenPacket("udp4", "127.0.0.1:0")
+ }
+ if supportsIPv6 {
+ return ListenPacket("udp6", "[::1]:0")
+ }
+ case "unixgram":
+ return ListenPacket(network, testUnixAddr())
+ }
+ return nil, fmt.Errorf("%s is not supported", network)
+}
+
+func newDualStackPacketListener() (cs []*UDPConn, err error) {
+ var args = []struct {
+ network string
+ UDPAddr
+ }{
+ {"udp4", UDPAddr{IP: IPv4(127, 0, 0, 1)}},
+ {"udp6", UDPAddr{IP: IPv6loopback}},
+ }
+ for i := 0; i < 64; i++ {
+ var port int
+ var cs []*UDPConn
+ for _, arg := range args {
+ arg.UDPAddr.Port = port
+ c, err := ListenUDP(arg.network, &arg.UDPAddr)
+ if err != nil {
+ continue
+ }
+ port = c.LocalAddr().(*UDPAddr).Port
+ cs = append(cs, c)
+ }
+ if len(cs) != len(args) {
+ for _, c := range cs {
+ c.Close()
+ }
+ continue
+ }
+ return cs, nil
+ }
+ return nil, errors.New("no dualstack port available")
+}
+
+type localPacketServer struct {
+ pcmu sync.RWMutex
+ PacketConn
+ done chan bool // signal that indicates server stopped
+}
+
+func (ls *localPacketServer) buildup(handler func(*localPacketServer, PacketConn)) error {
+ go func() {
+ handler(ls, ls.PacketConn)
+ close(ls.done)
+ }()
+ return nil
+}
+
+func (ls *localPacketServer) teardown() error {
+ ls.pcmu.Lock()
+ if ls.PacketConn != nil {
+ network := ls.PacketConn.LocalAddr().Network()
+ address := ls.PacketConn.LocalAddr().String()
+ ls.PacketConn.Close()
+ <-ls.done
+ ls.PacketConn = nil
+ switch network {
+ case "unixgram":
+ os.Remove(address)
+ }
+ }
+ ls.pcmu.Unlock()
+ return nil
+}
+
+func newLocalPacketServer(network string) (*localPacketServer, error) {
+ c, err := newLocalPacketListener(network)
+ if err != nil {
+ return nil, err
+ }
+ return &localPacketServer{PacketConn: c, done: make(chan bool)}, nil
+}
+
+type packetListener struct {
+ PacketConn
+}
+
+func (pl *packetListener) newLocalServer() (*localPacketServer, error) {
+ return &localPacketServer{PacketConn: pl.PacketConn, done: make(chan bool)}, nil
+}
+
+func packetTransponder(c PacketConn, ch chan<- error) {
+ defer close(ch)
+
+ c.SetDeadline(time.Now().Add(someTimeout))
+ c.SetReadDeadline(time.Now().Add(someTimeout))
+ c.SetWriteDeadline(time.Now().Add(someTimeout))
+
+ b := make([]byte, 256)
+ n, peer, err := c.ReadFrom(b)
+ if err != nil {
+ if perr := parseReadError(err); perr != nil {
+ ch <- perr
+ }
+ ch <- err
+ return
+ }
+ if peer == nil { // for connected-mode sockets
+ switch c.LocalAddr().Network() {
+ case "udp":
+ peer, err = ResolveUDPAddr("udp", string(b[:n]))
+ case "unixgram":
+ peer, err = ResolveUnixAddr("unixgram", string(b[:n]))
+ }
+ if err != nil {
+ ch <- err
+ return
+ }
+ }
+ if _, err := c.WriteTo(b[:n], peer); err != nil {
+ if perr := parseWriteError(err); perr != nil {
+ ch <- perr
+ }
+ ch <- err
+ return
+ }
+}
+
+func packetTransceiver(c PacketConn, wb []byte, dst Addr, ch chan<- error) {
+ defer close(ch)
+
+ c.SetDeadline(time.Now().Add(someTimeout))
+ c.SetReadDeadline(time.Now().Add(someTimeout))
+ c.SetWriteDeadline(time.Now().Add(someTimeout))
+
+ n, err := c.WriteTo(wb, dst)
+ if err != nil {
+ if perr := parseWriteError(err); perr != nil {
+ ch <- perr
+ }
+ ch <- err
+ return
+ }
+ if n != len(wb) {
+ ch <- fmt.Errorf("wrote %d; want %d", n, len(wb))
+ }
+ rb := make([]byte, len(wb))
+ n, _, err = c.ReadFrom(rb)
+ if err != nil {
+ if perr := parseReadError(err); perr != nil {
+ ch <- perr
+ }
+ ch <- err
+ return
+ }
+ if n != len(wb) {
+ ch <- fmt.Errorf("read %d; want %d", n, len(wb))
+ }
+}
+
+func timeoutPacketReceiver(c PacketConn, d, min, max time.Duration, ch chan<- error) {
+ var err error
+ defer func() { ch <- err }()
+
+ t0 := time.Now()
+ if err = c.SetReadDeadline(time.Now().Add(d)); err != nil {
+ return
+ }
+ b := make([]byte, 256)
+ var n int
+ n, _, err = c.ReadFrom(b)
+ t1 := time.Now()
+ if n != 0 || err == nil || !err.(Error).Timeout() {
+ err = fmt.Errorf("ReadFrom did not return (0, timeout): (%d, %v)", n, err)
+ return
+ }
+ if dt := t1.Sub(t0); min > dt || dt > max && !testing.Short() {
+ err = fmt.Errorf("ReadFrom took %s; expected %s", dt, d)
+ return
+ }
+}
diff --git a/src/net/multicast_test.go b/src/net/multicast_test.go
deleted file mode 100644
index 5f253f44a4..0000000000
--- a/src/net/multicast_test.go
+++ /dev/null
@@ -1,188 +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 net
-
-import (
- "fmt"
- "os"
- "runtime"
- "testing"
-)
-
-var ipv4MulticastListenerTests = []struct {
- net string
- gaddr *UDPAddr // see RFC 4727
-}{
- {"udp", &UDPAddr{IP: IPv4(224, 0, 0, 254), Port: 12345}},
-
- {"udp4", &UDPAddr{IP: IPv4(224, 0, 0, 254), Port: 12345}},
-}
-
-// TestIPv4MulticastListener tests both single and double listen to a
-// test listener with same address family, same group address and same
-// port.
-func TestIPv4MulticastListener(t *testing.T) {
- switch runtime.GOOS {
- case "android", "nacl", "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
- case "solaris":
- t.Skipf("skipping test on solaris, see issue 7399")
- }
-
- closer := func(cs []*UDPConn) {
- for _, c := range cs {
- if c != nil {
- c.Close()
- }
- }
- }
-
- for _, ifi := range []*Interface{loopbackInterface(), nil} {
- // Note that multicast interface assignment by system
- // is not recommended because it usually relies on
- // routing stuff for finding out an appropriate
- // nexthop containing both network and link layer
- // adjacencies.
- if ifi == nil && !*testExternal {
- continue
- }
- for _, tt := range ipv4MulticastListenerTests {
- var err error
- cs := make([]*UDPConn, 2)
- if cs[0], err = ListenMulticastUDP(tt.net, ifi, tt.gaddr); err != nil {
- t.Fatalf("First ListenMulticastUDP on %v failed: %v", ifi, err)
- }
- if err := checkMulticastListener(cs[0], tt.gaddr.IP); err != nil {
- closer(cs)
- t.Fatal(err)
- }
- if cs[1], err = ListenMulticastUDP(tt.net, ifi, tt.gaddr); err != nil {
- closer(cs)
- t.Fatalf("Second ListenMulticastUDP on %v failed: %v", ifi, err)
- }
- if err := checkMulticastListener(cs[1], tt.gaddr.IP); err != nil {
- closer(cs)
- t.Fatal(err)
- }
- closer(cs)
- }
- }
-}
-
-var ipv6MulticastListenerTests = []struct {
- net string
- gaddr *UDPAddr // see RFC 4727
-}{
- {"udp", &UDPAddr{IP: ParseIP("ff01::114"), Port: 12345}},
- {"udp", &UDPAddr{IP: ParseIP("ff02::114"), Port: 12345}},
- {"udp", &UDPAddr{IP: ParseIP("ff04::114"), Port: 12345}},
- {"udp", &UDPAddr{IP: ParseIP("ff05::114"), Port: 12345}},
- {"udp", &UDPAddr{IP: ParseIP("ff08::114"), Port: 12345}},
- {"udp", &UDPAddr{IP: ParseIP("ff0e::114"), Port: 12345}},
-
- {"udp6", &UDPAddr{IP: ParseIP("ff01::114"), Port: 12345}},
- {"udp6", &UDPAddr{IP: ParseIP("ff02::114"), Port: 12345}},
- {"udp6", &UDPAddr{IP: ParseIP("ff04::114"), Port: 12345}},
- {"udp6", &UDPAddr{IP: ParseIP("ff05::114"), Port: 12345}},
- {"udp6", &UDPAddr{IP: ParseIP("ff08::114"), Port: 12345}},
- {"udp6", &UDPAddr{IP: ParseIP("ff0e::114"), Port: 12345}},
-}
-
-// TestIPv6MulticastListener tests both single and double listen to a
-// test listener with same address family, same group address and same
-// port.
-func TestIPv6MulticastListener(t *testing.T) {
- switch runtime.GOOS {
- case "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
- case "solaris":
- t.Skipf("skipping test on solaris, see issue 7399")
- }
- if !supportsIPv6 {
- t.Skip("ipv6 is not supported")
- }
- if os.Getuid() != 0 {
- t.Skip("skipping test; must be root")
- }
-
- closer := func(cs []*UDPConn) {
- for _, c := range cs {
- if c != nil {
- c.Close()
- }
- }
- }
-
- for _, ifi := range []*Interface{loopbackInterface(), nil} {
- // Note that multicast interface assignment by system
- // is not recommended because it usually relies on
- // routing stuff for finding out an appropriate
- // nexthop containing both network and link layer
- // adjacencies.
- if ifi == nil && (!*testExternal || !*testIPv6) {
- continue
- }
- for _, tt := range ipv6MulticastListenerTests {
- var err error
- cs := make([]*UDPConn, 2)
- if cs[0], err = ListenMulticastUDP(tt.net, ifi, tt.gaddr); err != nil {
- t.Fatalf("First ListenMulticastUDP on %v failed: %v", ifi, err)
- }
- if err := checkMulticastListener(cs[0], tt.gaddr.IP); err != nil {
- closer(cs)
- t.Fatal(err)
- }
- if cs[1], err = ListenMulticastUDP(tt.net, ifi, tt.gaddr); err != nil {
- closer(cs)
- t.Fatalf("Second ListenMulticastUDP on %v failed: %v", ifi, err)
- }
- if err := checkMulticastListener(cs[1], tt.gaddr.IP); err != nil {
- closer(cs)
- t.Fatal(err)
- }
- closer(cs)
- }
- }
-}
-
-func checkMulticastListener(c *UDPConn, ip IP) error {
- if ok, err := multicastRIBContains(ip); err != nil {
- return err
- } else if !ok {
- return fmt.Errorf("%q not found in multicast RIB", ip.String())
- }
- la := c.LocalAddr()
- if la, ok := la.(*UDPAddr); !ok || la.Port == 0 {
- return fmt.Errorf("got %v; expected a proper address with non-zero port number", la)
- }
- return nil
-}
-
-func multicastRIBContains(ip IP) (bool, error) {
- switch runtime.GOOS {
- case "dragonfly", "netbsd", "openbsd", "plan9", "solaris", "windows":
- return true, nil // not implemented yet
- case "linux":
- if runtime.GOARCH == "arm" || runtime.GOARCH == "alpha" {
- return true, nil // not implemented yet
- }
- }
- ift, err := Interfaces()
- if err != nil {
- return false, err
- }
- for _, ifi := range ift {
- ifmat, err := ifi.MulticastAddrs()
- if err != nil {
- return false, err
- }
- for _, ifma := range ifmat {
- if ifma.(*IPAddr).IP.Equal(ip) {
- return true, nil
- }
- }
- }
- return false, nil
-}
diff --git a/src/net/net.go b/src/net/net.go
index 339c972906..fbeac81d27 100644
--- a/src/net/net.go
+++ b/src/net/net.go
@@ -46,6 +46,12 @@ import (
"time"
)
+func init() {
+ sysInit()
+ supportsIPv4 = probeIPv4Stack()
+ supportsIPv6, supportsIPv4map = probeIPv6Stack()
+}
+
// Addr represents a network end point address.
type Addr interface {
Network() string // name of the network
@@ -115,7 +121,11 @@ func (c *conn) Read(b []byte) (int, error) {
if !c.ok() {
return 0, syscall.EINVAL
}
- return c.fd.Read(b)
+ n, err := c.fd.Read(b)
+ if err != nil && err != io.EOF {
+ err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+ }
+ return n, err
}
// Write implements the Conn Write method.
@@ -123,7 +133,11 @@ func (c *conn) Write(b []byte) (int, error) {
if !c.ok() {
return 0, syscall.EINVAL
}
- return c.fd.Write(b)
+ n, err := c.fd.Write(b)
+ if err != nil {
+ err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+ }
+ return n, err
}
// Close closes the connection.
@@ -131,7 +145,11 @@ func (c *conn) Close() error {
if !c.ok() {
return syscall.EINVAL
}
- return c.fd.Close()
+ err := c.fd.Close()
+ if err != nil {
+ err = &OpError{Op: "close", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+ }
+ return err
}
// LocalAddr returns the local network address.
@@ -159,7 +177,10 @@ func (c *conn) SetDeadline(t time.Time) error {
if !c.ok() {
return syscall.EINVAL
}
- return c.fd.setDeadline(t)
+ if err := c.fd.setDeadline(t); err != nil {
+ return &OpError{Op: "set", Net: c.fd.net, Source: nil, Addr: c.fd.laddr, Err: err}
+ }
+ return nil
}
// SetReadDeadline implements the Conn SetReadDeadline method.
@@ -167,7 +188,10 @@ func (c *conn) SetReadDeadline(t time.Time) error {
if !c.ok() {
return syscall.EINVAL
}
- return c.fd.setReadDeadline(t)
+ if err := c.fd.setReadDeadline(t); err != nil {
+ return &OpError{Op: "set", Net: c.fd.net, Source: nil, Addr: c.fd.laddr, Err: err}
+ }
+ return nil
}
// SetWriteDeadline implements the Conn SetWriteDeadline method.
@@ -175,7 +199,10 @@ func (c *conn) SetWriteDeadline(t time.Time) error {
if !c.ok() {
return syscall.EINVAL
}
- return c.fd.setWriteDeadline(t)
+ if err := c.fd.setWriteDeadline(t); err != nil {
+ return &OpError{Op: "set", Net: c.fd.net, Source: nil, Addr: c.fd.laddr, Err: err}
+ }
+ return nil
}
// SetReadBuffer sets the size of the operating system's
@@ -184,7 +211,10 @@ func (c *conn) SetReadBuffer(bytes int) error {
if !c.ok() {
return syscall.EINVAL
}
- return setReadBuffer(c.fd, bytes)
+ if err := setReadBuffer(c.fd, bytes); err != nil {
+ return &OpError{Op: "set", Net: c.fd.net, Source: nil, Addr: c.fd.laddr, Err: err}
+ }
+ return nil
}
// SetWriteBuffer sets the size of the operating system's
@@ -193,7 +223,10 @@ func (c *conn) SetWriteBuffer(bytes int) error {
if !c.ok() {
return syscall.EINVAL
}
- return setWriteBuffer(c.fd, bytes)
+ if err := setWriteBuffer(c.fd, bytes); err != nil {
+ return &OpError{Op: "set", Net: c.fd.net, Source: nil, Addr: c.fd.laddr, Err: err}
+ }
+ return nil
}
// File sets the underlying os.File to blocking mode and returns a copy.
@@ -203,13 +236,12 @@ func (c *conn) SetWriteBuffer(bytes int) error {
// The returned os.File's file descriptor is different from the connection's.
// Attempting to change properties of the original using this duplicate
// may or may not have the desired effect.
-func (c *conn) File() (f *os.File, err error) { return c.fd.dup() }
-
-// An Error represents a network error.
-type Error interface {
- error
- Timeout() bool // Is the error a timeout?
- Temporary() bool // Is the error temporary?
+func (c *conn) File() (f *os.File, err error) {
+ f, err = c.fd.dup()
+ if err != nil {
+ err = &OpError{Op: "file", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+ }
+ return
}
// PacketConn is a generic packet-oriented network connection.
@@ -275,6 +307,13 @@ type Listener interface {
Addr() Addr
}
+// An Error represents a network error.
+type Error interface {
+ error
+ Timeout() bool // Is the error a timeout?
+ Temporary() bool // Is the error temporary?
+}
+
// Various errors contained in OpError.
var (
// For connection setup and write operations.
@@ -298,7 +337,17 @@ type OpError struct {
// such as "tcp" or "udp6".
Net string
- // Addr is the network address on which this error occurred.
+ // For operations involving a remote network connection, like
+ // Dial, Read, or Write, Source is the corresponding local
+ // network address.
+ Source Addr
+
+ // Addr is the network address for which this error occurred.
+ // For local operations, like Listen or SetDeadline, Addr is
+ // the address of the local endpoint being manipulated.
+ // For operations involving a remote network connection, like
+ // Dial, Read, or Write, Addr is the remote address of that
+ // connection.
Addr Addr
// Err is the error that occurred during the operation.
@@ -313,22 +362,21 @@ func (e *OpError) Error() string {
if e.Net != "" {
s += " " + e.Net
}
+ if e.Source != nil {
+ s += " " + e.Source.String()
+ }
if e.Addr != nil {
- s += " " + e.Addr.String()
+ if e.Source != nil {
+ s += "->"
+ } else {
+ s += " "
+ }
+ s += e.Addr.String()
}
s += ": " + e.Err.Error()
return s
}
-type temporary interface {
- Temporary() bool
-}
-
-func (e *OpError) Temporary() bool {
- t, ok := e.Err.(temporary)
- return ok && t.Temporary()
-}
-
var noDeadline = time.Time{}
type timeout interface {
@@ -336,16 +384,45 @@ type timeout interface {
}
func (e *OpError) Timeout() bool {
+ if ne, ok := e.Err.(*os.SyscallError); ok {
+ t, ok := ne.Err.(timeout)
+ return ok && t.Timeout()
+ }
t, ok := e.Err.(timeout)
return ok && t.Timeout()
}
+type temporary interface {
+ Temporary() bool
+}
+
+func (e *OpError) Temporary() bool {
+ if ne, ok := e.Err.(*os.SyscallError); ok {
+ t, ok := ne.Err.(temporary)
+ return ok && t.Temporary()
+ }
+ t, ok := e.Err.(temporary)
+ return ok && t.Temporary()
+}
+
type timeoutError struct{}
func (e *timeoutError) Error() string { return "i/o timeout" }
func (e *timeoutError) Timeout() bool { return true }
func (e *timeoutError) Temporary() bool { return true }
+// A ParseError is the error type of literal network address parsers.
+type ParseError struct {
+ // Type is the type of string that was expected, such as
+ // "IP address", "CIDR address".
+ Type string
+
+ // Text is the malformed text string.
+ Text string
+}
+
+func (e *ParseError) Error() string { return "invalid " + e.Type + ": " + e.Text }
+
type AddrError struct {
Err string
Addr string
@@ -362,19 +439,14 @@ func (e *AddrError) Error() string {
return s
}
-func (e *AddrError) Temporary() bool {
- return false
-}
-
-func (e *AddrError) Timeout() bool {
- return false
-}
+func (e *AddrError) Timeout() bool { return false }
+func (e *AddrError) Temporary() bool { return false }
type UnknownNetworkError string
func (e UnknownNetworkError) Error() string { return "unknown network " + string(e) }
-func (e UnknownNetworkError) Temporary() bool { return false }
func (e UnknownNetworkError) Timeout() bool { return false }
+func (e UnknownNetworkError) Temporary() bool { return false }
type InvalidAddrError string
@@ -383,17 +455,50 @@ func (e InvalidAddrError) Timeout() bool { return false }
func (e InvalidAddrError) Temporary() bool { return false }
// DNSConfigError represents an error reading the machine's DNS configuration.
+// (No longer used; kept for compatibility.)
type DNSConfigError struct {
Err error
}
-func (e *DNSConfigError) Error() string {
- return "error reading DNS config: " + e.Err.Error()
-}
-
+func (e *DNSConfigError) Error() string { return "error reading DNS config: " + e.Err.Error() }
func (e *DNSConfigError) Timeout() bool { return false }
func (e *DNSConfigError) Temporary() bool { return false }
+// Various errors contained in DNSError.
+var (
+ errNoSuchHost = errors.New("no such host")
+)
+
+// DNSError represents a DNS lookup error.
+type DNSError struct {
+ Err string // description of the error
+ Name string // name looked for
+ Server string // server used
+ IsTimeout bool // if true, timed out; not all timeouts set this
+}
+
+func (e *DNSError) Error() string {
+ if e == nil {
+ return ""
+ }
+ s := "lookup " + e.Name
+ if e.Server != "" {
+ s += " on " + e.Server
+ }
+ s += ": " + e.Err
+ return s
+}
+
+// Timeout reports whether the DNS lookup is known to have timed out.
+// This is not always known; a DNS lookup may fail due to a timeout
+// and return a DNSError for which Timeout returns false.
+func (e *DNSError) Timeout() bool { return e.IsTimeout }
+
+// Temporary reports whether the DNS error is known to be temporary.
+// This is not always known; a DNS lookup may fail due to a temporary
+// error and return a DNSError for which Temporary returns false.
+func (e *DNSError) Temporary() bool { return e.IsTimeout }
+
type writerOnly struct {
io.Writer
}
@@ -413,10 +518,6 @@ func genericReadFrom(w io.Writer, r io.Reader) (n int64, err error) {
var threadLimit = make(chan struct{}, 500)
-// Using send for acquire is fine here because we are not using this
-// to protect any memory. All we care about is the number of goroutines
-// making calls at a time.
-
func acquireThread() {
threadLimit <- struct{}{}
}
diff --git a/src/net/net_test.go b/src/net/net_test.go
index 5a88363eb6..3907ce4aa5 100644
--- a/src/net/net_test.go
+++ b/src/net/net_test.go
@@ -6,258 +6,251 @@ package net
import (
"io"
- "io/ioutil"
"os"
"runtime"
"testing"
- "time"
)
-func TestShutdown(t *testing.T) {
- if runtime.GOOS == "plan9" {
- t.Skipf("skipping test on %q", runtime.GOOS)
- }
- ln, err := Listen("tcp", "127.0.0.1:0")
- if err != nil {
- if ln, err = Listen("tcp6", "[::1]:0"); err != nil {
- t.Fatalf("ListenTCP on :0: %v", err)
- }
+func TestCloseRead(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
}
- go func() {
+ for _, network := range []string{"tcp", "unix", "unixpacket"} {
+ if !testableNetwork(network) {
+ t.Logf("skipping %s test", network)
+ continue
+ }
+
+ ln, err := newLocalListener(network)
+ if err != nil {
+ t.Fatal(err)
+ }
+ switch network {
+ case "unix", "unixpacket":
+ defer os.Remove(ln.Addr().String())
+ }
defer ln.Close()
- c, err := ln.Accept()
+
+ c, err := Dial(ln.Addr().Network(), ln.Addr().String())
if err != nil {
- t.Errorf("Accept: %v", err)
- return
+ t.Fatal(err)
}
- var buf [10]byte
- n, err := c.Read(buf[:])
- if n != 0 || err != io.EOF {
- t.Errorf("server Read = %d, %v; want 0, io.EOF", n, err)
- return
+ switch network {
+ case "unix", "unixpacket":
+ defer os.Remove(c.LocalAddr().String())
}
- c.Write([]byte("response"))
- c.Close()
- }()
+ defer c.Close()
- c, err := Dial("tcp", ln.Addr().String())
- if err != nil {
- t.Fatalf("Dial: %v", err)
- }
- defer c.Close()
-
- err = c.(*TCPConn).CloseWrite()
- if err != nil {
- t.Fatalf("CloseWrite: %v", err)
- }
- var buf [10]byte
- n, err := c.Read(buf[:])
- if err != nil {
- t.Fatalf("client Read: %d, %v", n, err)
- }
- got := string(buf[:n])
- if got != "response" {
- t.Errorf("read = %q, want \"response\"", got)
+ switch c := c.(type) {
+ case *TCPConn:
+ err = c.CloseRead()
+ case *UnixConn:
+ err = c.CloseRead()
+ }
+ if err != nil {
+ if perr := parseCloseError(err); perr != nil {
+ t.Error(perr)
+ }
+ t.Fatal(err)
+ }
+ var b [1]byte
+ n, err := c.Read(b[:])
+ if n != 0 || err == nil {
+ t.Fatalf("got (%d, %v); want (0, error)", n, err)
+ }
}
}
-func TestShutdownUnix(t *testing.T) {
- if !testableNetwork("unix") {
- t.Skip("unix test")
+func TestCloseWrite(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
}
- f, err := ioutil.TempFile("", "go_net_unixtest")
- if err != nil {
- t.Fatalf("TempFile: %s", err)
- }
- f.Close()
- tmpname := f.Name()
- os.Remove(tmpname)
- ln, err := Listen("unix", tmpname)
- if err != nil {
- t.Fatalf("ListenUnix on %s: %s", tmpname, err)
- }
- defer func() {
- ln.Close()
- os.Remove(tmpname)
- }()
-
- go func() {
+ handler := func(ls *localServer, ln Listener) {
c, err := ln.Accept()
if err != nil {
- t.Errorf("Accept: %v", err)
+ t.Error(err)
return
}
- var buf [10]byte
- n, err := c.Read(buf[:])
+ defer c.Close()
+
+ var b [1]byte
+ n, err := c.Read(b[:])
if n != 0 || err != io.EOF {
- t.Errorf("server Read = %d, %v; want 0, io.EOF", n, err)
+ t.Errorf("got (%d, %v); want (0, io.EOF)", n, err)
return
}
- c.Write([]byte("response"))
- c.Close()
- }()
+ switch c := c.(type) {
+ case *TCPConn:
+ err = c.CloseWrite()
+ case *UnixConn:
+ err = c.CloseWrite()
+ }
+ if err != nil {
+ if perr := parseCloseError(err); perr != nil {
+ t.Error(perr)
+ }
+ t.Error(err)
+ return
+ }
+ n, err = c.Write(b[:])
+ if err == nil {
+ t.Errorf("got (%d, %v); want (any, error)", n, err)
+ return
+ }
+ }
- c, err := Dial("unix", tmpname)
- if err != nil {
- t.Fatalf("Dial: %v", err)
- }
- defer c.Close()
+ for _, network := range []string{"tcp", "unix", "unixpacket"} {
+ if !testableNetwork(network) {
+ t.Logf("skipping %s test", network)
+ continue
+ }
- err = c.(*UnixConn).CloseWrite()
- if err != nil {
- t.Fatalf("CloseWrite: %v", err)
- }
- var buf [10]byte
- n, err := c.Read(buf[:])
- if err != nil {
- t.Fatalf("client Read: %d, %v", n, err)
- }
- got := string(buf[:n])
- if got != "response" {
- t.Errorf("read = %q, want \"response\"", got)
+ ls, err := newLocalServer(network)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ls.teardown()
+ if err := ls.buildup(handler); err != nil {
+ t.Fatal(err)
+ }
+
+ c, err := Dial(ls.Listener.Addr().Network(), ls.Listener.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ switch network {
+ case "unix", "unixpacket":
+ defer os.Remove(c.LocalAddr().String())
+ }
+ defer c.Close()
+
+ switch c := c.(type) {
+ case *TCPConn:
+ err = c.CloseWrite()
+ case *UnixConn:
+ err = c.CloseWrite()
+ }
+ if err != nil {
+ if perr := parseCloseError(err); perr != nil {
+ t.Error(perr)
+ }
+ t.Fatal(err)
+ }
+ var b [1]byte
+ n, err := c.Read(b[:])
+ if n != 0 || err != io.EOF {
+ t.Fatalf("got (%d, %v); want (0, io.EOF)", n, err)
+ }
+ n, err = c.Write(b[:])
+ if err == nil {
+ t.Fatalf("got (%d, %v); want (any, error)", n, err)
+ }
}
}
-func TestTCPListenClose(t *testing.T) {
- ln, err := Listen("tcp", "127.0.0.1:0")
- if err != nil {
- t.Fatalf("Listen failed: %v", err)
- }
+func TestConnClose(t *testing.T) {
+ for _, network := range []string{"tcp", "unix", "unixpacket"} {
+ if !testableNetwork(network) {
+ t.Logf("skipping %s test", network)
+ continue
+ }
- done := make(chan bool, 1)
- go func() {
- time.Sleep(100 * time.Millisecond)
- ln.Close()
- }()
- go func() {
+ ln, err := newLocalListener(network)
+ if err != nil {
+ t.Fatal(err)
+ }
+ switch network {
+ case "unix", "unixpacket":
+ defer os.Remove(ln.Addr().String())
+ }
+ defer ln.Close()
+
+ c, err := Dial(ln.Addr().Network(), ln.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ switch network {
+ case "unix", "unixpacket":
+ defer os.Remove(c.LocalAddr().String())
+ }
+ defer c.Close()
+
+ if err := c.Close(); err != nil {
+ if perr := parseCloseError(err); perr != nil {
+ t.Error(perr)
+ }
+ t.Fatal(err)
+ }
+ var b [1]byte
+ n, err := c.Read(b[:])
+ if n != 0 || err == nil {
+ t.Fatalf("got (%d, %v); want (0, error)", n, err)
+ }
+ }
+}
+
+func TestListenerClose(t *testing.T) {
+ for _, network := range []string{"tcp", "unix", "unixpacket"} {
+ if !testableNetwork(network) {
+ t.Logf("skipping %s test", network)
+ continue
+ }
+
+ ln, err := newLocalListener(network)
+ if err != nil {
+ t.Fatal(err)
+ }
+ switch network {
+ case "unix", "unixpacket":
+ defer os.Remove(ln.Addr().String())
+ }
+ defer ln.Close()
+
+ if err := ln.Close(); err != nil {
+ if perr := parseCloseError(err); perr != nil {
+ t.Error(perr)
+ }
+ t.Fatal(err)
+ }
c, err := ln.Accept()
if err == nil {
c.Close()
- t.Error("Accept succeeded")
- } else {
- t.Logf("Accept timeout error: %s (any error is fine)", err)
+ t.Fatal("should fail")
}
- done <- true
- }()
- select {
- case <-done:
- case <-time.After(2 * time.Second):
- t.Fatal("timeout waiting for TCP close")
}
}
-func TestUDPListenClose(t *testing.T) {
- switch runtime.GOOS {
- case "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
- }
- ln, err := ListenPacket("udp", "127.0.0.1:0")
- if err != nil {
- t.Fatalf("Listen failed: %v", err)
- }
-
- buf := make([]byte, 1000)
- done := make(chan bool, 1)
- go func() {
- time.Sleep(100 * time.Millisecond)
- ln.Close()
- }()
- go func() {
- _, _, err = ln.ReadFrom(buf)
- if err == nil {
- t.Error("ReadFrom succeeded")
- } else {
- t.Logf("ReadFrom timeout error: %s (any error is fine)", err)
+func TestPacketConnClose(t *testing.T) {
+ for _, network := range []string{"udp", "unixgram"} {
+ if !testableNetwork(network) {
+ t.Logf("skipping %s test", network)
+ continue
}
- done <- true
- }()
- select {
- case <-done:
- case <-time.After(2 * time.Second):
- t.Fatal("timeout waiting for UDP close")
- }
-}
-func TestTCPClose(t *testing.T) {
- switch runtime.GOOS {
- case "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
- }
- l, err := Listen("tcp", "127.0.0.1:0")
- if err != nil {
- t.Fatal(err)
- }
- defer l.Close()
-
- read := func(r io.Reader) error {
- var m [1]byte
- _, err := r.Read(m[:])
- return err
- }
-
- go func() {
- c, err := Dial("tcp", l.Addr().String())
+ c, err := newLocalPacketListener(network)
if err != nil {
- t.Errorf("Dial: %v", err)
- return
+ t.Fatal(err)
}
+ switch network {
+ case "unixgram":
+ defer os.Remove(c.LocalAddr().String())
+ }
+ defer c.Close()
- go read(c)
-
- time.Sleep(10 * time.Millisecond)
- c.Close()
- }()
-
- c, err := l.Accept()
- if err != nil {
- t.Fatal(err)
- }
- defer c.Close()
-
- for err == nil {
- err = read(c)
- }
- if err != nil && err != io.EOF {
- t.Fatal(err)
- }
-}
-
-func TestErrorNil(t *testing.T) {
- c, err := Dial("tcp", "127.0.0.1:65535")
- if err == nil {
- t.Fatal("Dial 127.0.0.1:65535 succeeded")
- }
- if c != nil {
- t.Fatalf("Dial returned non-nil interface %T(%v) with err != nil", c, c)
- }
-
- // Make Listen fail by relistening on the same address.
- l, err := Listen("tcp", "127.0.0.1:0")
- if err != nil {
- t.Fatalf("Listen 127.0.0.1:0: %v", err)
- }
- defer l.Close()
- l1, err := Listen("tcp", l.Addr().String())
- if err == nil {
- t.Fatalf("second Listen %v: %v", l.Addr(), err)
- }
- if l1 != nil {
- t.Fatalf("Listen returned non-nil interface %T(%v) with err != nil", l1, l1)
- }
-
- // Make ListenPacket fail by relistening on the same address.
- lp, err := ListenPacket("udp", "127.0.0.1:0")
- if err != nil {
- t.Fatalf("Listen 127.0.0.1:0: %v", err)
- }
- defer lp.Close()
- lp1, err := ListenPacket("udp", lp.LocalAddr().String())
- if err == nil {
- t.Fatalf("second Listen %v: %v", lp.LocalAddr(), err)
- }
- if lp1 != nil {
- t.Fatalf("ListenPacket returned non-nil interface %T(%v) with err != nil", lp1, lp1)
+ if err := c.Close(); err != nil {
+ if perr := parseCloseError(err); perr != nil {
+ t.Error(perr)
+ }
+ t.Fatal(err)
+ }
+ var b [1]byte
+ n, _, err := c.ReadFrom(b[:])
+ if n != 0 || err == nil {
+ t.Fatalf("got (%d, %v); want (0, error)", n, err)
+ }
}
}
diff --git a/src/net/net_windows_test.go b/src/net/net_windows_test.go
index 750a4304b2..21b47964a4 100644
--- a/src/net/net_windows_test.go
+++ b/src/net/net_windows_test.go
@@ -16,8 +16,6 @@ import (
)
func TestAcceptIgnoreSomeErrors(t *testing.T) {
- t.Skip("skipping temporarily, see issue 8662")
-
recv := func(ln Listener) (string, error) {
c, err := ln.Accept()
if err != nil {
@@ -64,7 +62,7 @@ func TestAcceptIgnoreSomeErrors(t *testing.T) {
// In child process.
c, err := Dial("tcp", envaddr)
if err != nil {
- t.Fatalf("Dial failed: %v", err)
+ t.Fatal(err)
}
fmt.Printf("sleeping\n")
time.Sleep(time.Minute) // process will be killed here
@@ -73,7 +71,7 @@ func TestAcceptIgnoreSomeErrors(t *testing.T) {
ln, err := Listen("tcp", "127.0.0.1:0")
if err != nil {
- t.Fatalf("Listen failed: %v", err)
+ t.Fatal(err)
}
defer ln.Close()
diff --git a/src/net/netgo_unix_test.go b/src/net/netgo_unix_test.go
index 9fb2a567df..1d950d67d7 100644
--- a/src/net/netgo_unix_test.go
+++ b/src/net/netgo_unix_test.go
@@ -16,9 +16,9 @@ func TestGoLookupIP(t *testing.T) {
t.Errorf("cgoLookupIP must be a placeholder")
}
if err != nil {
- t.Errorf("cgoLookupIP failed: %v", err)
+ t.Error(err)
}
if _, err := goLookupIP(host); err != nil {
- t.Errorf("goLookupIP failed: %v", err)
+ t.Error(err)
}
}
diff --git a/src/net/nss.go b/src/net/nss.go
new file mode 100644
index 0000000000..08c3e6a69f
--- /dev/null
+++ b/src/net/nss.go
@@ -0,0 +1,159 @@
+// Copyright 2015 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 darwin dragonfly freebsd linux netbsd openbsd solaris
+
+package net
+
+import (
+ "errors"
+ "io"
+ "os"
+)
+
+// nssConf represents the state of the machine's /etc/nsswitch.conf file.
+type nssConf struct {
+ err error // any error encountered opening or parsing the file
+ sources map[string][]nssSource // keyed by database (e.g. "hosts")
+}
+
+type nssSource struct {
+ source string // e.g. "compat", "files", "mdns4_minimal"
+ criteria []nssCriterion
+}
+
+// standardCriteria reports all specified criteria have the default
+// status actions.
+func (s nssSource) standardCriteria() bool {
+ for i, crit := range s.criteria {
+ if !crit.standardStatusAction(i == len(s.criteria)-1) {
+ return false
+ }
+ }
+ return true
+}
+
+// nssCriterion is the parsed structure of one of the criteria in brackets
+// after an NSS source name.
+type nssCriterion struct {
+ negate bool // if "!" was present
+ status string // e.g. "success", "unavail" (lowercase)
+ action string // e.g. "return", "continue" (lowercase)
+}
+
+// standardStatusAction reports whether c is equivalent to not
+// specifying the criterion at all. last is whether this criteria is the
+// last in the list.
+func (c nssCriterion) standardStatusAction(last bool) bool {
+ if c.negate {
+ return false
+ }
+ var def string
+ switch c.status {
+ case "success":
+ def = "return"
+ case "notfound", "unavail", "tryagain":
+ def = "continue"
+ default:
+ // Unknown status
+ return false
+ }
+ if last && c.action == "return" {
+ return true
+ }
+ return c.action == def
+}
+
+func parseNSSConfFile(file string) *nssConf {
+ f, err := os.Open(file)
+ if err != nil {
+ return &nssConf{err: err}
+ }
+ defer f.Close()
+ return parseNSSConf(f)
+}
+
+func parseNSSConf(r io.Reader) *nssConf {
+ slurp, err := readFull(r)
+ if err != nil {
+ return &nssConf{err: err}
+ }
+ conf := new(nssConf)
+ conf.err = foreachLine(slurp, func(line []byte) error {
+ line = trimSpace(removeComment(line))
+ if len(line) == 0 {
+ return nil
+ }
+ colon := bytesIndexByte(line, ':')
+ if colon == -1 {
+ return errors.New("no colon on line")
+ }
+ db := string(trimSpace(line[:colon]))
+ srcs := line[colon+1:]
+ for {
+ srcs = trimSpace(srcs)
+ if len(srcs) == 0 {
+ break
+ }
+ sp := bytesIndexByte(srcs, ' ')
+ var src string
+ if sp == -1 {
+ src = string(srcs)
+ srcs = nil // done
+ } else {
+ src = string(srcs[:sp])
+ srcs = trimSpace(srcs[sp+1:])
+ }
+ var criteria []nssCriterion
+ // See if there's a criteria block in brackets.
+ if len(srcs) > 0 && srcs[0] == '[' {
+ bclose := bytesIndexByte(srcs, ']')
+ if bclose == -1 {
+ return errors.New("unclosed criterion bracket")
+ }
+ var err error
+ criteria, err = parseCriteria(srcs[1:bclose])
+ if err != nil {
+ return errors.New("invalid criteria: " + string(srcs[1:bclose]))
+ }
+ srcs = srcs[bclose+1:]
+ }
+ if conf.sources == nil {
+ conf.sources = make(map[string][]nssSource)
+ }
+ conf.sources[db] = append(conf.sources[db], nssSource{
+ source: src,
+ criteria: criteria,
+ })
+ }
+ return nil
+ })
+ return conf
+}
+
+// parses "foo=bar !foo=bar"
+func parseCriteria(x []byte) (c []nssCriterion, err error) {
+ err = foreachField(x, func(f []byte) error {
+ not := false
+ if len(f) > 0 && f[0] == '!' {
+ not = true
+ f = f[1:]
+ }
+ if len(f) < 3 {
+ return errors.New("criterion too short")
+ }
+ eq := bytesIndexByte(f, '=')
+ if eq == -1 {
+ return errors.New("criterion lacks equal sign")
+ }
+ lowerASCIIBytes(f)
+ c = append(c, nssCriterion{
+ negate: not,
+ status: string(f[:eq]),
+ action: string(f[eq+1:]),
+ })
+ return nil
+ })
+ return
+}
diff --git a/src/net/nss_test.go b/src/net/nss_test.go
new file mode 100644
index 0000000000..371deb502d
--- /dev/null
+++ b/src/net/nss_test.go
@@ -0,0 +1,169 @@
+// Copyright 2015 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 darwin dragonfly freebsd linux netbsd openbsd solaris
+
+package net
+
+import (
+ "reflect"
+ "strings"
+ "testing"
+)
+
+const ubuntuTrustyAvahi = `# /etc/nsswitch.conf
+#
+# Example configuration of GNU Name Service Switch functionality.
+# If you have the libc-doc-reference' and nfo' packages installed, try:
+# nfo libc "Name Service Switch"' for information about this file.
+
+passwd: compat
+group: compat
+shadow: compat
+
+hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4
+networks: files
+
+protocols: db files
+services: db files
+ethers: db files
+rpc: db files
+
+netgroup: nis
+`
+
+func TestParseNSSConf(t *testing.T) {
+ tests := []struct {
+ name string
+ in string
+ want *nssConf
+ }{
+ {
+ name: "no_newline",
+ in: "foo: a b",
+ want: &nssConf{
+ sources: map[string][]nssSource{
+ "foo": {{source: "a"}, {source: "b"}},
+ },
+ },
+ },
+ {
+ name: "newline",
+ in: "foo: a b\n",
+ want: &nssConf{
+ sources: map[string][]nssSource{
+ "foo": {{source: "a"}, {source: "b"}},
+ },
+ },
+ },
+ {
+ name: "whitespace",
+ in: " foo:a b \n",
+ want: &nssConf{
+ sources: map[string][]nssSource{
+ "foo": {{source: "a"}, {source: "b"}},
+ },
+ },
+ },
+ {
+ name: "comment1",
+ in: " foo:a b#c\n",
+ want: &nssConf{
+ sources: map[string][]nssSource{
+ "foo": {{source: "a"}, {source: "b"}},
+ },
+ },
+ },
+ {
+ name: "comment2",
+ in: " foo:a b #c \n",
+ want: &nssConf{
+ sources: map[string][]nssSource{
+ "foo": {{source: "a"}, {source: "b"}},
+ },
+ },
+ },
+ {
+ name: "crit",
+ in: " foo:a b [!a=b X=Y ] c#d \n",
+ want: &nssConf{
+ sources: map[string][]nssSource{
+ "foo": {
+ {source: "a"},
+ {
+ source: "b",
+ criteria: []nssCriterion{
+ {
+ negate: true,
+ status: "a",
+ action: "b",
+ },
+ {
+ status: "x",
+ action: "y",
+ },
+ },
+ },
+ {source: "c"},
+ },
+ },
+ },
+ },
+
+ // Ubuntu Trusty w/ avahi-daemon, libavahi-* etc installed.
+ {
+ name: "ubuntu_trusty_avahi",
+ in: ubuntuTrustyAvahi,
+ want: &nssConf{
+ sources: map[string][]nssSource{
+ "passwd": {{source: "compat"}},
+ "group": {{source: "compat"}},
+ "shadow": {{source: "compat"}},
+ "hosts": {
+ {source: "files"},
+ {
+ source: "mdns4_minimal",
+ criteria: []nssCriterion{
+ {
+ negate: false,
+ status: "notfound",
+ action: "return",
+ },
+ },
+ },
+ {source: "dns"},
+ {source: "mdns4"},
+ },
+ "networks": {{source: "files"}},
+ "protocols": {
+ {source: "db"},
+ {source: "files"},
+ },
+ "services": {
+ {source: "db"},
+ {source: "files"},
+ },
+ "ethers": {
+ {source: "db"},
+ {source: "files"},
+ },
+ "rpc": {
+ {source: "db"},
+ {source: "files"},
+ },
+ "netgroup": {
+ {source: "nis"},
+ },
+ },
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ gotConf := parseNSSConf(strings.NewReader(tt.in))
+ if !reflect.DeepEqual(gotConf, tt.want) {
+ t.Errorf("%s: mismatch\n got %#v\nwant %#v", tt.name, gotConf, tt.want)
+ }
+ }
+}
diff --git a/src/net/packetconn_test.go b/src/net/packetconn_test.go
index 31e050f6d3..7f3ea8a2d0 100644
--- a/src/net/packetconn_test.go
+++ b/src/net/packetconn_test.go
@@ -54,7 +54,7 @@ func TestPacketConn(t *testing.T) {
c1, err := ListenPacket(tt.net, tt.addr1)
if err != nil {
- t.Fatalf("ListenPacket failed: %v", err)
+ t.Fatal(err)
}
defer closer(c1, tt.net, tt.addr1, tt.addr2)
c1.LocalAddr()
@@ -64,7 +64,7 @@ func TestPacketConn(t *testing.T) {
c2, err := ListenPacket(tt.net, tt.addr2)
if err != nil {
- t.Fatalf("ListenPacket failed: %v", err)
+ t.Fatal(err)
}
defer closer(c2, tt.net, tt.addr1, tt.addr2)
c2.LocalAddr()
@@ -74,17 +74,17 @@ func TestPacketConn(t *testing.T) {
rb2 := make([]byte, 128)
if _, err := c1.WriteTo(wb, c2.LocalAddr()); err != nil {
- t.Fatalf("PacketConn.WriteTo failed: %v", err)
+ t.Fatal(err)
}
if _, _, err := c2.ReadFrom(rb2); err != nil {
- t.Fatalf("PacketConn.ReadFrom failed: %v", err)
+ t.Fatal(err)
}
if _, err := c2.WriteTo(wb, c1.LocalAddr()); err != nil {
- t.Fatalf("PacketConn.WriteTo failed: %v", err)
+ t.Fatal(err)
}
rb1 := make([]byte, 128)
if _, _, err := c1.ReadFrom(rb1); err != nil {
- t.Fatalf("PacketConn.ReadFrom failed: %v", err)
+ t.Fatal(err)
}
}
}
@@ -109,7 +109,7 @@ func TestConnAndPacketConn(t *testing.T) {
c1, err := ListenPacket(tt.net, tt.addr1)
if err != nil {
- t.Fatalf("ListenPacket failed: %v", err)
+ t.Fatal(err)
}
defer closer(c1, tt.net, tt.addr1, tt.addr2)
c1.LocalAddr()
@@ -119,7 +119,7 @@ func TestConnAndPacketConn(t *testing.T) {
c2, err := Dial(tt.net, c1.LocalAddr().String())
if err != nil {
- t.Fatalf("Dial failed: %v", err)
+ t.Fatal(err)
}
defer c2.Close()
c2.LocalAddr()
@@ -129,11 +129,11 @@ func TestConnAndPacketConn(t *testing.T) {
c2.SetWriteDeadline(time.Now().Add(500 * time.Millisecond))
if _, err := c2.Write(wb); err != nil {
- t.Fatalf("Conn.Write failed: %v", err)
+ t.Fatal(err)
}
rb1 := make([]byte, 128)
if _, _, err := c1.ReadFrom(rb1); err != nil {
- t.Fatalf("PacketConn.ReadFrom failed: %v", err)
+ t.Fatal(err)
}
var dst Addr
switch tt.net {
@@ -143,11 +143,11 @@ func TestConnAndPacketConn(t *testing.T) {
dst = c2.LocalAddr()
}
if _, err := c1.WriteTo(wb, dst); err != nil {
- t.Fatalf("PacketConn.WriteTo failed: %v", err)
+ t.Fatal(err)
}
rb2 := make([]byte, 128)
if _, err := c2.Read(rb2); err != nil {
- t.Fatalf("Conn.Read failed: %v", err)
+ t.Fatal(err)
}
}
}
diff --git a/src/net/parse.go b/src/net/parse.go
index ad901fff27..5b834e64d4 100644
--- a/src/net/parse.go
+++ b/src/net/parse.go
@@ -232,3 +232,132 @@ func last(s string, b byte) int {
}
return i
}
+
+// lowerASCIIBytes makes x ASCII lowercase in-place.
+func lowerASCIIBytes(x []byte) {
+ for i, b := range x {
+ if 'A' <= b && b <= 'Z' {
+ x[i] += 'a' - 'A'
+ }
+ }
+}
+
+// lowerASCII returns the ASCII lowercase version of b.
+func lowerASCII(b byte) byte {
+ if 'A' <= b && b <= 'Z' {
+ return b + ('a' - 'A')
+ }
+ return b
+}
+
+// trimSpace returns x without any leading or trailing ASCII whitespace.
+func trimSpace(x []byte) []byte {
+ for len(x) > 0 && isSpace(x[0]) {
+ x = x[1:]
+ }
+ for len(x) > 0 && isSpace(x[len(x)-1]) {
+ x = x[:len(x)-1]
+ }
+ return x
+}
+
+// isSpace reports whether b is an ASCII space character.
+func isSpace(b byte) bool {
+ return b == ' ' || b == '\t' || b == '\n' || b == '\r'
+}
+
+// removeComment returns line, removing any '#' byte and any following
+// bytes.
+func removeComment(line []byte) []byte {
+ if i := bytesIndexByte(line, '#'); i != -1 {
+ return line[:i]
+ }
+ return line
+}
+
+// foreachLine runs fn on each line of x.
+// Each line (except for possibly the last) ends in '\n'.
+// It returns the first non-nil error returned by fn.
+func foreachLine(x []byte, fn func(line []byte) error) error {
+ for len(x) > 0 {
+ nl := bytesIndexByte(x, '\n')
+ if nl == -1 {
+ return fn(x)
+ }
+ line := x[:nl+1]
+ x = x[nl+1:]
+ if err := fn(line); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// foreachField runs fn on each non-empty run of non-space bytes in x.
+// It returns the first non-nil error returned by fn.
+func foreachField(x []byte, fn func(field []byte) error) error {
+ x = trimSpace(x)
+ for len(x) > 0 {
+ sp := bytesIndexByte(x, ' ')
+ if sp == -1 {
+ return fn(x)
+ }
+ if field := trimSpace(x[:sp]); len(field) > 0 {
+ if err := fn(field); err != nil {
+ return err
+ }
+ }
+ x = trimSpace(x[sp+1:])
+ }
+ return nil
+}
+
+// bytesIndexByte is bytes.IndexByte. It returns the index of the
+// first instance of c in s, or -1 if c is not present in s.
+func bytesIndexByte(s []byte, c byte) int {
+ for i, b := range s {
+ if b == c {
+ return i
+ }
+ }
+ return -1
+}
+
+// stringsHasSuffix is strings.HasSuffix. It reports whether s ends in
+// suffix.
+func stringsHasSuffix(s, suffix string) bool {
+ return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
+}
+
+// stringsHasSuffixFold reports whether s ends in suffix,
+// ASCII-case-insensitively.
+func stringsHasSuffixFold(s, suffix string) bool {
+ if len(suffix) > len(s) {
+ return false
+ }
+ for i := 0; i < len(suffix); i++ {
+ if lowerASCII(suffix[i]) != lowerASCII(s[len(s)-len(suffix)+i]) {
+ return false
+ }
+ }
+ return true
+}
+
+// stringsHasPrefix is strings.HasPrefix. It reports whether s begins with prefix.
+func stringsHasPrefix(s, prefix string) bool {
+ return len(s) >= len(prefix) && s[:len(prefix)] == prefix
+}
+
+func readFull(r io.Reader) (all []byte, err error) {
+ buf := make([]byte, 1024)
+ for {
+ n, err := r.Read(buf)
+ all = append(all, buf[:n]...)
+ if err == io.EOF {
+ return all, nil
+ }
+ if err != nil {
+ return nil, err
+ }
+ }
+}
diff --git a/src/net/parse_test.go b/src/net/parse_test.go
index 7b213b75bd..f5359d8c36 100644
--- a/src/net/parse_test.go
+++ b/src/net/parse_test.go
@@ -15,20 +15,20 @@ func TestReadLine(t *testing.T) {
// /etc/services file does not exist on android, plan9, windows.
switch runtime.GOOS {
case "android", "plan9", "windows":
- t.Skipf("skipping test on %q", runtime.GOOS)
+ t.Skipf("not supported on %s", runtime.GOOS)
}
filename := "/etc/services" // a nice big file
fd, err := os.Open(filename)
if err != nil {
- t.Fatalf("open %s: %v", filename, err)
+ t.Fatal(err)
}
defer fd.Close()
br := bufio.NewReader(fd)
file, err := open(filename)
if file == nil {
- t.Fatalf("net.open(%s) = nil", filename)
+ t.Fatal(err)
}
defer file.close()
@@ -41,8 +41,7 @@ func TestReadLine(t *testing.T) {
}
line, ok := file.readLine()
if (berr != nil) != !ok || bline != line {
- t.Fatalf("%s:%d (#%d)\nbufio => %q, %v\nnet => %q, %v",
- filename, lineno, byteno, bline, berr, line, ok)
+ t.Fatalf("%s:%d (#%d)\nbufio => %q, %v\nnet => %q, %v", filename, lineno, byteno, bline, berr, line, ok)
}
if !ok {
break
diff --git a/src/net/pipe.go b/src/net/pipe.go
index f1a2eca4e8..5fc830b740 100644
--- a/src/net/pipe.go
+++ b/src/net/pipe.go
@@ -55,13 +55,13 @@ func (p *pipe) RemoteAddr() Addr {
}
func (p *pipe) SetDeadline(t time.Time) error {
- return errors.New("net.Pipe does not support deadlines")
+ return &OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (p *pipe) SetReadDeadline(t time.Time) error {
- return errors.New("net.Pipe does not support deadlines")
+ return &OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (p *pipe) SetWriteDeadline(t time.Time) error {
- return errors.New("net.Pipe does not support deadlines")
+ return &OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
diff --git a/src/net/pipe_test.go b/src/net/pipe_test.go
index afe4f2408f..60c3920593 100644
--- a/src/net/pipe_test.go
+++ b/src/net/pipe_test.go
@@ -10,10 +10,10 @@ import (
"testing"
)
-func checkWrite(t *testing.T, w io.Writer, data []byte, c chan int) {
+func checkPipeWrite(t *testing.T, w io.Writer, data []byte, c chan int) {
n, err := w.Write(data)
if err != nil {
- t.Errorf("write: %v", err)
+ t.Error(err)
}
if n != len(data) {
t.Errorf("short write: %d != %d", n, len(data))
@@ -21,11 +21,11 @@ func checkWrite(t *testing.T, w io.Writer, data []byte, c chan int) {
c <- 0
}
-func checkRead(t *testing.T, r io.Reader, data []byte, wantErr error) {
+func checkPipeRead(t *testing.T, r io.Reader, data []byte, wantErr error) {
buf := make([]byte, len(data)+10)
n, err := r.Read(buf)
if err != wantErr {
- t.Errorf("read: %v", err)
+ t.Error(err)
return
}
if n != len(data) || !bytes.Equal(buf[0:n], data) {
@@ -34,23 +34,22 @@ func checkRead(t *testing.T, r io.Reader, data []byte, wantErr error) {
}
}
-// Test a simple read/write/close sequence.
+// TestPipe tests a simple read/write/close sequence.
// Assumes that the underlying io.Pipe implementation
// is solid and we're just testing the net wrapping.
-
func TestPipe(t *testing.T) {
c := make(chan int)
cli, srv := Pipe()
- go checkWrite(t, cli, []byte("hello, world"), c)
- checkRead(t, srv, []byte("hello, world"), nil)
+ go checkPipeWrite(t, cli, []byte("hello, world"), c)
+ checkPipeRead(t, srv, []byte("hello, world"), nil)
<-c
- go checkWrite(t, srv, []byte("line 2"), c)
- checkRead(t, cli, []byte("line 2"), nil)
+ go checkPipeWrite(t, srv, []byte("line 2"), c)
+ checkPipeRead(t, cli, []byte("line 2"), nil)
<-c
- go checkWrite(t, cli, []byte("a third line"), c)
- checkRead(t, srv, []byte("a third line"), nil)
+ go checkPipeWrite(t, cli, []byte("a third line"), c)
+ checkPipeRead(t, srv, []byte("a third line"), nil)
<-c
go srv.Close()
- checkRead(t, cli, nil, io.EOF)
+ checkPipeRead(t, cli, nil, io.EOF)
cli.Close()
}
diff --git a/src/net/platform_test.go b/src/net/platform_test.go
index a1e766dbcd..b700091dc5 100644
--- a/src/net/platform_test.go
+++ b/src/net/platform_test.go
@@ -40,7 +40,7 @@ func testableNetwork(network string) bool {
}
case "unixpacket":
switch runtime.GOOS {
- case "android", "darwin", "nacl", "openbsd", "plan9", "windows":
+ case "android", "darwin", "nacl", "plan9", "windows":
fallthrough
case "freebsd": // FreeBSD 8 and below don't support unixpacket
return false
@@ -103,15 +103,26 @@ func testableListenArgs(network, address, client string) bool {
return false
}
- // Test functionality of IPv6 communication using AF_INET6
- // sockets.
+ // Test functionality of IPv4 communication using AF_INET and
+ // IPv6 communication using AF_INET6 sockets.
+ if !supportsIPv4 && ip.To4() != nil {
+ return false
+ }
if !supportsIPv6 && ip.To16() != nil && ip.To4() == nil {
return false
}
+ cip := ParseIP(client)
+ if cip != nil {
+ if !supportsIPv4 && cip.To4() != nil {
+ return false
+ }
+ if !supportsIPv6 && cip.To16() != nil && cip.To4() == nil {
+ return false
+ }
+ }
// Test functionality of IPv4 communication using AF_INET6
// sockets.
- cip := ParseIP(client)
if !supportsIPv4map && (network == "tcp" || network == "udp" || network == "ip") && wildcard {
// At this point, we prefer IPv4 when ip is nil.
// See favoriteAddrFamily for further information.
diff --git a/src/net/port.go b/src/net/port.go
index c24f4ed5b1..a2a538789e 100644
--- a/src/net/port.go
+++ b/src/net/port.go
@@ -18,7 +18,7 @@ func parsePort(net, port string) (int, error) {
}
}
if p < 0 || p > 0xFFFF {
- return 0, &AddrError{"invalid port", port}
+ return 0, &AddrError{Err: "invalid port", Addr: port}
}
return p, nil
}
diff --git a/src/net/port_test.go b/src/net/port_test.go
index 4811ade69e..2dacd975e7 100644
--- a/src/net/port_test.go
+++ b/src/net/port_test.go
@@ -9,14 +9,12 @@ import (
"testing"
)
-type portTest struct {
- netw string
- name string
- port int
- ok bool
-}
-
-var porttests = []portTest{
+var portTests = []struct {
+ network string
+ name string
+ port int
+ ok bool
+}{
{"tcp", "echo", 7, true},
{"tcp", "discard", 9, true},
{"tcp", "systat", 11, true},
@@ -46,14 +44,12 @@ var porttests = []portTest{
func TestLookupPort(t *testing.T) {
switch runtime.GOOS {
case "nacl":
- t.Skipf("skipping test on %q", runtime.GOOS)
+ t.Skipf("not supported on %s", runtime.GOOS)
}
- for i := 0; i < len(porttests); i++ {
- tt := porttests[i]
- if port, err := LookupPort(tt.netw, tt.name); port != tt.port || (err == nil) != tt.ok {
- t.Errorf("LookupPort(%q, %q) = %v, %v; want %v",
- tt.netw, tt.name, port, err, tt.port)
+ for _, tt := range portTests {
+ if port, err := LookupPort(tt.network, tt.name); port != tt.port || (err == nil) != tt.ok {
+ t.Errorf("LookupPort(%q, %q) = %v, %v; want %v", tt.network, tt.name, port, err, tt.port)
}
}
}
diff --git a/src/net/port_unix.go b/src/net/port_unix.go
index 348c771c35..badf8abc79 100644
--- a/src/net/port_unix.go
+++ b/src/net/port_unix.go
@@ -69,5 +69,5 @@ func goLookupPort(network, service string) (port int, err error) {
return
}
}
- return 0, &AddrError{"unknown port", network + "/" + service}
+ return 0, &AddrError{Err: "unknown port", Addr: network + "/" + service}
}
diff --git a/src/net/protoconn_test.go b/src/net/protoconn_test.go
index 3a16ec5bc0..c6ef23b0e1 100644
--- a/src/net/protoconn_test.go
+++ b/src/net/protoconn_test.go
@@ -8,7 +8,6 @@
package net
import (
- "io/ioutil"
"os"
"runtime"
"testing"
@@ -21,33 +20,19 @@ import (
// golang.org/x/net/ipv6
// golang.org/x/net/icmp
-// testUnixAddr uses ioutil.TempFile to get a name that is unique. It
-// also uses /tmp directory in case it is prohibited to create UNIX
-// sockets in TMPDIR.
-func testUnixAddr() string {
- f, err := ioutil.TempFile("", "nettest")
- if err != nil {
- panic(err)
- }
- addr := f.Name()
- f.Close()
- os.Remove(addr)
- return addr
-}
-
func TestTCPListenerSpecificMethods(t *testing.T) {
switch runtime.GOOS {
case "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
+ t.Skipf("not supported on %s", runtime.GOOS)
}
la, err := ResolveTCPAddr("tcp4", "127.0.0.1:0")
if err != nil {
- t.Fatalf("ResolveTCPAddr failed: %v", err)
+ t.Fatal(err)
}
ln, err := ListenTCP("tcp4", la)
if err != nil {
- t.Fatalf("ListenTCP failed: %v", err)
+ t.Fatal(err)
}
defer ln.Close()
ln.Addr()
@@ -55,21 +40,21 @@ func TestTCPListenerSpecificMethods(t *testing.T) {
if c, err := ln.Accept(); err != nil {
if !err.(Error).Timeout() {
- t.Fatalf("TCPListener.Accept failed: %v", err)
+ t.Fatal(err)
}
} else {
c.Close()
}
if c, err := ln.AcceptTCP(); err != nil {
if !err.(Error).Timeout() {
- t.Fatalf("TCPListener.AcceptTCP failed: %v", err)
+ t.Fatal(err)
}
} else {
c.Close()
}
if f, err := ln.File(); err != nil {
- condFatalf(t, "TCPListener.File failed: %v", err)
+ condFatalf(t, "%v", err)
} else {
f.Close()
}
@@ -78,25 +63,30 @@ func TestTCPListenerSpecificMethods(t *testing.T) {
func TestTCPConnSpecificMethods(t *testing.T) {
la, err := ResolveTCPAddr("tcp4", "127.0.0.1:0")
if err != nil {
- t.Fatalf("ResolveTCPAddr failed: %v", err)
+ t.Fatal(err)
}
ln, err := ListenTCP("tcp4", la)
if err != nil {
- t.Fatalf("ListenTCP failed: %v", err)
+ t.Fatal(err)
}
- defer ln.Close()
- ln.Addr()
-
- done := make(chan int)
- go transponder(t, ln, done)
-
- ra, err := ResolveTCPAddr("tcp4", ln.Addr().String())
+ ch := make(chan error, 1)
+ handler := func(ls *localServer, ln Listener) { transponder(ls.Listener, ch) }
+ ls, err := (&streamListener{Listener: ln}).newLocalServer()
if err != nil {
- t.Fatalf("ResolveTCPAddr failed: %v", err)
+ t.Fatal(err)
+ }
+ defer ls.teardown()
+ if err := ls.buildup(handler); err != nil {
+ t.Fatal(err)
+ }
+
+ ra, err := ResolveTCPAddr("tcp4", ls.Listener.Addr().String())
+ if err != nil {
+ t.Fatal(err)
}
c, err := DialTCP("tcp4", nil, ra)
if err != nil {
- t.Fatalf("DialTCP failed: %v", err)
+ t.Fatal(err)
}
defer c.Close()
c.SetKeepAlive(false)
@@ -110,24 +100,26 @@ func TestTCPConnSpecificMethods(t *testing.T) {
c.SetWriteDeadline(time.Now().Add(someTimeout))
if _, err := c.Write([]byte("TCPCONN TEST")); err != nil {
- t.Fatalf("TCPConn.Write failed: %v", err)
+ t.Fatal(err)
}
rb := make([]byte, 128)
if _, err := c.Read(rb); err != nil {
- t.Fatalf("TCPConn.Read failed: %v", err)
+ t.Fatal(err)
}
- <-done
+ for err := range ch {
+ t.Error(err)
+ }
}
func TestUDPConnSpecificMethods(t *testing.T) {
la, err := ResolveUDPAddr("udp4", "127.0.0.1:0")
if err != nil {
- t.Fatalf("ResolveUDPAddr failed: %v", err)
+ t.Fatal(err)
}
c, err := ListenUDP("udp4", la)
if err != nil {
- t.Fatalf("ListenUDP failed: %v", err)
+ t.Fatal(err)
}
defer c.Close()
c.LocalAddr()
@@ -141,27 +133,27 @@ func TestUDPConnSpecificMethods(t *testing.T) {
wb := []byte("UDPCONN TEST")
rb := make([]byte, 128)
if _, err := c.WriteToUDP(wb, c.LocalAddr().(*UDPAddr)); err != nil {
- t.Fatalf("UDPConn.WriteToUDP failed: %v", err)
+ t.Fatal(err)
}
if _, _, err := c.ReadFromUDP(rb); err != nil {
- t.Fatalf("UDPConn.ReadFromUDP failed: %v", err)
+ t.Fatal(err)
}
if _, _, err := c.WriteMsgUDP(wb, nil, c.LocalAddr().(*UDPAddr)); err != nil {
- condFatalf(t, "UDPConn.WriteMsgUDP failed: %v", err)
+ condFatalf(t, "%v", err)
}
if _, _, _, _, err := c.ReadMsgUDP(rb, nil); err != nil {
- condFatalf(t, "UDPConn.ReadMsgUDP failed: %v", err)
+ condFatalf(t, "%v", err)
}
if f, err := c.File(); err != nil {
- condFatalf(t, "UDPConn.File failed: %v", err)
+ condFatalf(t, "%v", err)
} else {
f.Close()
}
defer func() {
if p := recover(); p != nil {
- t.Fatalf("UDPConn.WriteToUDP or WriteMsgUDP panicked: %v", p)
+ t.Fatalf("panicked: %v", p)
}
}()
@@ -176,11 +168,11 @@ func TestIPConnSpecificMethods(t *testing.T) {
la, err := ResolveIPAddr("ip4", "127.0.0.1")
if err != nil {
- t.Fatalf("ResolveIPAddr failed: %v", err)
+ t.Fatal(err)
}
c, err := ListenIP("ip4:icmp", la)
if err != nil {
- t.Fatalf("ListenIP failed: %v", err)
+ t.Fatal(err)
}
defer c.Close()
c.LocalAddr()
@@ -192,14 +184,14 @@ func TestIPConnSpecificMethods(t *testing.T) {
c.SetWriteBuffer(2048)
if f, err := c.File(); err != nil {
- condFatalf(t, "IPConn.File failed: %v", err)
+ condFatalf(t, "%v", err)
} else {
f.Close()
}
defer func() {
if p := recover(); p != nil {
- t.Fatalf("IPConn.WriteToIP or WriteMsgIP panicked: %v", p)
+ t.Fatalf("panicked: %v", p)
}
}()
@@ -216,11 +208,11 @@ func TestUnixListenerSpecificMethods(t *testing.T) {
addr := testUnixAddr()
la, err := ResolveUnixAddr("unix", addr)
if err != nil {
- t.Fatalf("ResolveUnixAddr failed: %v", err)
+ t.Fatal(err)
}
ln, err := ListenUnix("unix", la)
if err != nil {
- t.Fatalf("ListenUnix failed: %v", err)
+ t.Fatal(err)
}
defer ln.Close()
defer os.Remove(addr)
@@ -229,21 +221,21 @@ func TestUnixListenerSpecificMethods(t *testing.T) {
if c, err := ln.Accept(); err != nil {
if !err.(Error).Timeout() {
- t.Fatalf("UnixListener.Accept failed: %v", err)
+ t.Fatal(err)
}
} else {
c.Close()
}
if c, err := ln.AcceptUnix(); err != nil {
if !err.(Error).Timeout() {
- t.Fatalf("UnixListener.AcceptUnix failed: %v", err)
+ t.Fatal(err)
}
} else {
c.Close()
}
if f, err := ln.File(); err != nil {
- t.Fatalf("UnixListener.File failed: %v", err)
+ t.Fatal(err)
} else {
f.Close()
}
@@ -258,11 +250,11 @@ func TestUnixConnSpecificMethods(t *testing.T) {
a1, err := ResolveUnixAddr("unixgram", addr1)
if err != nil {
- t.Fatalf("ResolveUnixAddr failed: %v", err)
+ t.Fatal(err)
}
c1, err := DialUnix("unixgram", a1, nil)
if err != nil {
- t.Fatalf("DialUnix failed: %v", err)
+ t.Fatal(err)
}
defer c1.Close()
defer os.Remove(addr1)
@@ -276,11 +268,11 @@ func TestUnixConnSpecificMethods(t *testing.T) {
a2, err := ResolveUnixAddr("unixgram", addr2)
if err != nil {
- t.Fatalf("ResolveUnixAddr failed: %v", err)
+ t.Fatal(err)
}
c2, err := DialUnix("unixgram", a2, nil)
if err != nil {
- t.Fatalf("DialUnix failed: %v", err)
+ t.Fatal(err)
}
defer c2.Close()
defer os.Remove(addr2)
@@ -294,11 +286,11 @@ func TestUnixConnSpecificMethods(t *testing.T) {
a3, err := ResolveUnixAddr("unixgram", addr3)
if err != nil {
- t.Fatalf("ResolveUnixAddr failed: %v", err)
+ t.Fatal(err)
}
c3, err := ListenUnixgram("unixgram", a3)
if err != nil {
- t.Fatalf("ListenUnixgram failed: %v", err)
+ t.Fatal(err)
}
defer c3.Close()
defer os.Remove(addr3)
@@ -315,39 +307,39 @@ func TestUnixConnSpecificMethods(t *testing.T) {
rb2 := make([]byte, 128)
rb3 := make([]byte, 128)
if _, _, err := c1.WriteMsgUnix(wb, nil, a2); err != nil {
- t.Fatalf("UnixConn.WriteMsgUnix failed: %v", err)
+ t.Fatal(err)
}
if _, _, _, _, err := c2.ReadMsgUnix(rb2, nil); err != nil {
- t.Fatalf("UnixConn.ReadMsgUnix failed: %v", err)
+ t.Fatal(err)
}
if _, err := c2.WriteToUnix(wb, a1); err != nil {
- t.Fatalf("UnixConn.WriteToUnix failed: %v", err)
+ t.Fatal(err)
}
if _, _, err := c1.ReadFromUnix(rb1); err != nil {
- t.Fatalf("UnixConn.ReadFromUnix failed: %v", err)
+ t.Fatal(err)
}
if _, err := c3.WriteToUnix(wb, a1); err != nil {
- t.Fatalf("UnixConn.WriteToUnix failed: %v", err)
+ t.Fatal(err)
}
if _, _, err := c1.ReadFromUnix(rb1); err != nil {
- t.Fatalf("UnixConn.ReadFromUnix failed: %v", err)
+ t.Fatal(err)
}
if _, err := c2.WriteToUnix(wb, a3); err != nil {
- t.Fatalf("UnixConn.WriteToUnix failed: %v", err)
+ t.Fatal(err)
}
if _, _, err := c3.ReadFromUnix(rb3); err != nil {
- t.Fatalf("UnixConn.ReadFromUnix failed: %v", err)
+ t.Fatal(err)
}
if f, err := c1.File(); err != nil {
- t.Fatalf("UnixConn.File failed: %v", err)
+ t.Fatal(err)
} else {
f.Close()
}
defer func() {
if p := recover(); p != nil {
- t.Fatalf("UnixConn.WriteToUnix or WriteMsgUnix panicked: %v", p)
+ t.Fatalf("panicked: %v", p)
}
}()
diff --git a/src/net/rpc/server.go b/src/net/rpc/server.go
index 83728d55a1..6e6e881917 100644
--- a/src/net/rpc/server.go
+++ b/src/net/rpc/server.go
@@ -13,6 +13,7 @@
Only methods that satisfy these criteria will be made available for remote access;
other methods will be ignored:
+ - the method's type is exported.
- the method is exported.
- the method has two arguments, both exported (or builtin) types.
- the method's second argument is a pointer.
@@ -216,7 +217,7 @@ func isExportedOrBuiltinType(t reflect.Type) bool {
// Register publishes in the server the set of methods of the
// receiver value that satisfy the following conditions:
-// - exported method
+// - exported method of exported type
// - two arguments, both of exported type
// - the second argument is a pointer
// - one return value, of type error
diff --git a/src/net/sendfile_dragonfly.go b/src/net/sendfile_dragonfly.go
index bc88fd3b90..a9cf3fe951 100644
--- a/src/net/sendfile_dragonfly.go
+++ b/src/net/sendfile_dragonfly.go
@@ -91,13 +91,16 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
if err1 != nil {
// This includes syscall.ENOSYS (no kernel
// support) and syscall.EINVAL (fd types which
- // don't implement sendfile together)
- err = &OpError{"sendfile", c.net, c.raddr, err1}
+ // don't implement sendfile)
+ err = err1
break
}
}
if lr != nil {
lr.N = remain
}
+ if err != nil {
+ err = os.NewSyscallError("sendfile", err)
+ }
return written, err, written > 0
}
diff --git a/src/net/sendfile_freebsd.go b/src/net/sendfile_freebsd.go
index ffc147262a..d0bf6034c1 100644
--- a/src/net/sendfile_freebsd.go
+++ b/src/net/sendfile_freebsd.go
@@ -91,13 +91,16 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
if err1 != nil {
// This includes syscall.ENOSYS (no kernel
// support) and syscall.EINVAL (fd types which
- // don't implement sendfile together)
- err = &OpError{"sendfile", c.net, c.raddr, err1}
+ // don't implement sendfile)
+ err = err1
break
}
}
if lr != nil {
lr.N = remain
}
+ if err != nil {
+ err = os.NewSyscallError("sendfile", err)
+ }
return written, err, written > 0
}
diff --git a/src/net/sendfile_linux.go b/src/net/sendfile_linux.go
index 5e117636a8..5ca41c39eb 100644
--- a/src/net/sendfile_linux.go
+++ b/src/net/sendfile_linux.go
@@ -64,13 +64,16 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
if err1 != nil {
// This includes syscall.ENOSYS (no kernel
// support) and syscall.EINVAL (fd types which
- // don't implement sendfile together)
- err = &OpError{"sendfile", c.net, c.raddr, err1}
+ // don't implement sendfile)
+ err = err1
break
}
}
if lr != nil {
lr.N = remain
}
+ if err != nil {
+ err = os.NewSyscallError("sendfile", err)
+ }
return written, err, written > 0
}
diff --git a/src/net/sendfile_solaris.go b/src/net/sendfile_solaris.go
new file mode 100644
index 0000000000..0966575696
--- /dev/null
+++ b/src/net/sendfile_solaris.go
@@ -0,0 +1,110 @@
+// Copyright 2015 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 net
+
+import (
+ "io"
+ "os"
+ "syscall"
+)
+
+// Not strictly needed, but very helpful for debugging, see issue #10221.
+//go:cgo_import_dynamic _ _ "libsendfile.so"
+//go:cgo_import_dynamic _ _ "libsocket.so"
+
+// maxSendfileSize is the largest chunk size we ask the kernel to copy
+// at a time.
+const maxSendfileSize int = 4 << 20
+
+// sendFile copies the contents of r to c using the sendfile
+// system call to minimize copies.
+//
+// if handled == true, sendFile returns the number of bytes copied and any
+// non-EOF error.
+//
+// if handled == false, sendFile performed no work.
+func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
+ // Solaris uses 0 as the "until EOF" value. If you pass in more bytes than the
+ // file contains, it will loop back to the beginning ad nauseam until it's sent
+ // exactly the number of bytes told to. As such, we need to know exactly how many
+ // bytes to send.
+ var remain int64 = 0
+
+ lr, ok := r.(*io.LimitedReader)
+ if ok {
+ remain, r = lr.N, lr.R
+ if remain <= 0 {
+ return 0, nil, true
+ }
+ }
+ f, ok := r.(*os.File)
+ if !ok {
+ return 0, nil, false
+ }
+
+ if remain == 0 {
+ fi, err := f.Stat()
+ if err != nil {
+ return 0, err, false
+ }
+
+ remain = fi.Size()
+ }
+
+ // The other quirk with Solaris's sendfile implementation is that it doesn't
+ // use the current position of the file -- if you pass it offset 0, it starts
+ // from offset 0. There's no way to tell it "start from current position", so
+ // we have to manage that explicitly.
+ pos, err := f.Seek(0, os.SEEK_CUR)
+ if err != nil {
+ return 0, err, false
+ }
+
+ if err := c.writeLock(); err != nil {
+ return 0, err, true
+ }
+ defer c.writeUnlock()
+
+ dst := c.sysfd
+ src := int(f.Fd())
+ for remain > 0 {
+ n := maxSendfileSize
+ if int64(n) > remain {
+ n = int(remain)
+ }
+ pos1 := pos
+ n, err1 := syscall.Sendfile(dst, src, &pos1, n)
+ if n > 0 {
+ pos += int64(n)
+ written += int64(n)
+ remain -= int64(n)
+ }
+ if n == 0 && err1 == nil {
+ break
+ }
+ if err1 == syscall.EAGAIN {
+ if err1 = c.pd.WaitWrite(); err1 == nil {
+ continue
+ }
+ }
+ if err1 == syscall.EINTR {
+ continue
+ }
+ if err1 != nil {
+ // This includes syscall.ENOSYS (no kernel
+ // support) and syscall.EINVAL (fd types which
+ // don't implement sendfile)
+ err = err1
+ break
+ }
+ }
+ if lr != nil {
+ lr.N = remain
+ }
+ if err != nil {
+ err = os.NewSyscallError("sendfile", err)
+ }
+ return written, err, written > 0
+}
diff --git a/src/net/sendfile_stub.go b/src/net/sendfile_stub.go
index 03426ef0df..a0760b4e52 100644
--- a/src/net/sendfile_stub.go
+++ b/src/net/sendfile_stub.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 nacl netbsd openbsd solaris
+// +build darwin nacl netbsd openbsd
package net
diff --git a/src/net/sendfile_windows.go b/src/net/sendfile_windows.go
index b128ba27b0..f3f3b54b88 100644
--- a/src/net/sendfile_windows.go
+++ b/src/net/sendfile_windows.go
@@ -46,7 +46,7 @@ func sendFile(fd *netFD, r io.Reader) (written int64, err error, handled bool) {
return syscall.TransmitFile(o.fd.sysfd, o.handle, o.qty, 0, &o.o, nil, syscall.TF_WRITE_BEHIND)
})
if err != nil {
- return 0, err, false
+ return 0, os.NewSyscallError("transmitfile", err), false
}
if lr != nil {
lr.N -= int64(done)
diff --git a/src/net/server_test.go b/src/net/server_test.go
index 479c181248..fe0006b11f 100644
--- a/src/net/server_test.go
+++ b/src/net/server_test.go
@@ -5,413 +5,384 @@
package net
import (
- "flag"
- "io"
"os"
"testing"
- "time"
)
-var streamConnServerTests = []struct {
- snet string // server side
- saddr string
- cnet string // client side
- caddr string
- empty bool // test with empty data
+var tcpServerTests = []struct {
+ snet, saddr string // server endpoint
+ tnet, taddr string // target endpoint for client
}{
- {snet: "tcp", saddr: ":0", cnet: "tcp", caddr: "127.0.0.1"},
- {snet: "tcp", saddr: "0.0.0.0:0", cnet: "tcp", caddr: "127.0.0.1"},
- {snet: "tcp", saddr: "[::ffff:0.0.0.0]:0", cnet: "tcp", caddr: "127.0.0.1"},
- {snet: "tcp", saddr: "[::]:0", cnet: "tcp", caddr: "::1"},
+ {snet: "tcp", saddr: ":0", tnet: "tcp", taddr: "127.0.0.1"},
+ {snet: "tcp", saddr: "0.0.0.0:0", tnet: "tcp", taddr: "127.0.0.1"},
+ {snet: "tcp", saddr: "[::ffff:0.0.0.0]:0", tnet: "tcp", taddr: "127.0.0.1"},
+ {snet: "tcp", saddr: "[::]:0", tnet: "tcp", taddr: "::1"},
- {snet: "tcp", saddr: ":0", cnet: "tcp", caddr: "::1"},
- {snet: "tcp", saddr: "0.0.0.0:0", cnet: "tcp", caddr: "::1"},
- {snet: "tcp", saddr: "[::ffff:0.0.0.0]:0", cnet: "tcp", caddr: "::1"},
- {snet: "tcp", saddr: "[::]:0", cnet: "tcp", caddr: "127.0.0.1"},
+ {snet: "tcp", saddr: ":0", tnet: "tcp", taddr: "::1"},
+ {snet: "tcp", saddr: "0.0.0.0:0", tnet: "tcp", taddr: "::1"},
+ {snet: "tcp", saddr: "[::ffff:0.0.0.0]:0", tnet: "tcp", taddr: "::1"},
+ {snet: "tcp", saddr: "[::]:0", tnet: "tcp", taddr: "127.0.0.1"},
- {snet: "tcp", saddr: ":0", cnet: "tcp4", caddr: "127.0.0.1"},
- {snet: "tcp", saddr: "0.0.0.0:0", cnet: "tcp4", caddr: "127.0.0.1"},
- {snet: "tcp", saddr: "[::ffff:0.0.0.0]:0", cnet: "tcp4", caddr: "127.0.0.1"},
- {snet: "tcp", saddr: "[::]:0", cnet: "tcp6", caddr: "::1"},
+ {snet: "tcp", saddr: ":0", tnet: "tcp4", taddr: "127.0.0.1"},
+ {snet: "tcp", saddr: "0.0.0.0:0", tnet: "tcp4", taddr: "127.0.0.1"},
+ {snet: "tcp", saddr: "[::ffff:0.0.0.0]:0", tnet: "tcp4", taddr: "127.0.0.1"},
+ {snet: "tcp", saddr: "[::]:0", tnet: "tcp6", taddr: "::1"},
- {snet: "tcp", saddr: ":0", cnet: "tcp6", caddr: "::1"},
- {snet: "tcp", saddr: "0.0.0.0:0", cnet: "tcp6", caddr: "::1"},
- {snet: "tcp", saddr: "[::ffff:0.0.0.0]:0", cnet: "tcp6", caddr: "::1"},
- {snet: "tcp", saddr: "[::]:0", cnet: "tcp4", caddr: "127.0.0.1"},
+ {snet: "tcp", saddr: ":0", tnet: "tcp6", taddr: "::1"},
+ {snet: "tcp", saddr: "0.0.0.0:0", tnet: "tcp6", taddr: "::1"},
+ {snet: "tcp", saddr: "[::ffff:0.0.0.0]:0", tnet: "tcp6", taddr: "::1"},
+ {snet: "tcp", saddr: "[::]:0", tnet: "tcp4", taddr: "127.0.0.1"},
- {snet: "tcp", saddr: "127.0.0.1:0", cnet: "tcp", caddr: "127.0.0.1"},
- {snet: "tcp", saddr: "[::ffff:127.0.0.1]:0", cnet: "tcp", caddr: "127.0.0.1"},
- {snet: "tcp", saddr: "[::1]:0", cnet: "tcp", caddr: "::1"},
+ {snet: "tcp", saddr: "127.0.0.1:0", tnet: "tcp", taddr: "127.0.0.1"},
+ {snet: "tcp", saddr: "[::ffff:127.0.0.1]:0", tnet: "tcp", taddr: "127.0.0.1"},
+ {snet: "tcp", saddr: "[::1]:0", tnet: "tcp", taddr: "::1"},
- {snet: "tcp4", saddr: ":0", cnet: "tcp4", caddr: "127.0.0.1"},
- {snet: "tcp4", saddr: "0.0.0.0:0", cnet: "tcp4", caddr: "127.0.0.1"},
- {snet: "tcp4", saddr: "[::ffff:0.0.0.0]:0", cnet: "tcp4", caddr: "127.0.0.1"},
+ {snet: "tcp4", saddr: ":0", tnet: "tcp4", taddr: "127.0.0.1"},
+ {snet: "tcp4", saddr: "0.0.0.0:0", tnet: "tcp4", taddr: "127.0.0.1"},
+ {snet: "tcp4", saddr: "[::ffff:0.0.0.0]:0", tnet: "tcp4", taddr: "127.0.0.1"},
- {snet: "tcp4", saddr: "127.0.0.1:0", cnet: "tcp4", caddr: "127.0.0.1"},
+ {snet: "tcp4", saddr: "127.0.0.1:0", tnet: "tcp4", taddr: "127.0.0.1"},
- {snet: "tcp6", saddr: ":0", cnet: "tcp6", caddr: "::1"},
- {snet: "tcp6", saddr: "[::]:0", cnet: "tcp6", caddr: "::1"},
+ {snet: "tcp6", saddr: ":0", tnet: "tcp6", taddr: "::1"},
+ {snet: "tcp6", saddr: "[::]:0", tnet: "tcp6", taddr: "::1"},
- {snet: "tcp6", saddr: "[::1]:0", cnet: "tcp6", caddr: "::1"},
-
- {snet: "unix", saddr: testUnixAddr(), cnet: "unix", caddr: testUnixAddr()},
- {snet: "unix", saddr: "@gotest2/net", cnet: "unix", caddr: "@gotest2/net.local"},
+ {snet: "tcp6", saddr: "[::1]:0", tnet: "tcp6", taddr: "::1"},
}
-func TestStreamConnServer(t *testing.T) {
- for _, tt := range streamConnServerTests {
- if !testableListenArgs(tt.snet, tt.saddr, tt.caddr) {
- t.Logf("skipping %s test", tt.snet+":"+tt.saddr+"->"+tt.caddr)
+// TestTCPServer tests concurrent accept-read-write servers.
+func TestTCPServer(t *testing.T) {
+ const N = 3
+
+ for i, tt := range tcpServerTests {
+ if !testableListenArgs(tt.snet, tt.saddr, tt.taddr) {
+ t.Logf("skipping %s test", tt.snet+" "+tt.saddr+"->"+tt.taddr)
continue
}
- listening := make(chan string)
- done := make(chan int)
- switch tt.snet {
- case "unix":
- os.Remove(tt.saddr)
- os.Remove(tt.caddr)
- }
-
- go runStreamConnServer(t, tt.snet, tt.saddr, listening, done)
- taddr := <-listening // wait for server to start
-
- switch tt.cnet {
- case "tcp", "tcp4", "tcp6":
- _, port, err := SplitHostPort(taddr)
- if err != nil {
- t.Fatalf("SplitHostPort(%q) failed: %v", taddr, err)
- }
- taddr = JoinHostPort(tt.caddr, port)
- }
-
- runStreamConnClient(t, tt.cnet, taddr, tt.empty)
- <-done // make sure server stopped
-
- switch tt.snet {
- case "unix":
- os.Remove(tt.saddr)
- os.Remove(tt.caddr)
- }
- }
-}
-
-var seqpacketConnServerTests = []struct {
- net string
- saddr string // server address
- caddr string // client address
- empty bool // test with empty data
-}{
- {net: "unixpacket", saddr: testUnixAddr(), caddr: testUnixAddr()},
- {net: "unixpacket", saddr: "@gotest4/net", caddr: "@gotest4/net.local"},
-}
-
-func TestSeqpacketConnServer(t *testing.T) {
- for _, tt := range seqpacketConnServerTests {
- if !testableListenArgs(tt.net, tt.saddr, tt.caddr) {
- t.Logf("skipping %s test", tt.net+":"+tt.saddr+"->"+tt.caddr)
- continue
- }
- listening := make(chan string)
- done := make(chan int)
- switch tt.net {
- case "unixpacket":
- os.Remove(tt.saddr)
- os.Remove(tt.caddr)
- }
-
- go runStreamConnServer(t, tt.net, tt.saddr, listening, done)
- taddr := <-listening // wait for server to start
-
- runStreamConnClient(t, tt.net, taddr, tt.empty)
- <-done // make sure server stopped
-
- switch tt.net {
- case "unixpacket":
- os.Remove(tt.saddr)
- os.Remove(tt.caddr)
- }
- }
-}
-
-func runStreamConnServer(t *testing.T, net, laddr string, listening chan<- string, done chan<- int) {
- defer close(done)
- l, err := Listen(net, laddr)
- if err != nil {
- t.Errorf("Listen(%q, %q) failed: %v", net, laddr, err)
- listening <- ""
- return
- }
- defer l.Close()
- listening <- l.Addr().String()
-
- echo := func(rw io.ReadWriter, done chan<- int) {
- buf := make([]byte, 1024)
- for {
- n, err := rw.Read(buf[0:])
- if err != nil || n == 0 || string(buf[:n]) == "END" {
- break
- }
- rw.Write(buf[0:n])
- }
- close(done)
- }
-
-run:
- for {
- c, err := l.Accept()
+ ln, err := Listen(tt.snet, tt.saddr)
if err != nil {
- t.Logf("Accept failed: %v", err)
- continue run
+ if perr := parseDialError(err); perr != nil {
+ t.Error(perr)
+ }
+ t.Fatal(err)
+ }
+
+ var lss []*localServer
+ var tpchs []chan error
+ defer func() {
+ for _, ls := range lss {
+ ls.teardown()
+ }
+ }()
+ for i := 0; i < N; i++ {
+ ls, err := (&streamListener{Listener: ln}).newLocalServer()
+ if err != nil {
+ t.Fatal(err)
+ }
+ lss = append(lss, ls)
+ tpchs = append(tpchs, make(chan error, 1))
+ }
+ for i := 0; i < N; i++ {
+ ch := tpchs[i]
+ handler := func(ls *localServer, ln Listener) { transponder(ln, ch) }
+ if err := lss[i].buildup(handler); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ var trchs []chan error
+ for i := 0; i < N; i++ {
+ _, port, err := SplitHostPort(lss[i].Listener.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ d := Dialer{Timeout: someTimeout}
+ c, err := d.Dial(tt.tnet, JoinHostPort(tt.taddr, port))
+ if err != nil {
+ if perr := parseDialError(err); perr != nil {
+ t.Error(perr)
+ }
+ t.Fatal(err)
+ }
+ defer c.Close()
+ trchs = append(trchs, make(chan error, 1))
+ go transceiver(c, []byte("TCP SERVER TEST"), trchs[i])
+ }
+
+ for _, ch := range trchs {
+ for err := range ch {
+ t.Errorf("#%d: %v", i, err)
+ }
+ }
+ for _, ch := range tpchs {
+ for err := range ch {
+ t.Errorf("#%d: %v", i, err)
+ }
}
- echodone := make(chan int)
- go echo(c, echodone)
- <-echodone // make sure echo stopped
- c.Close()
- break run
}
}
-func runStreamConnClient(t *testing.T, net, taddr string, isEmpty bool) {
- c, err := Dial(net, taddr)
- if err != nil {
- t.Fatalf("Dial(%q, %q) failed: %v", net, taddr, err)
- }
- defer c.Close()
- c.SetReadDeadline(time.Now().Add(1 * time.Second))
-
- var wb []byte
- if !isEmpty {
- wb = []byte("StreamConnClient by Dial\n")
- }
- if n, err := c.Write(wb); err != nil || n != len(wb) {
- t.Fatalf("Write failed: %v, %v; want %v, ", n, err, len(wb))
- }
-
- rb := make([]byte, 1024)
- if n, err := c.Read(rb[0:]); err != nil || n != len(wb) {
- t.Fatalf("Read failed: %v, %v; want %v, ", n, err, len(wb))
- }
-
- // Send explicit ending for unixpacket.
- // Older Linux kernels do not stop reads on close.
- switch net {
- case "unixpacket":
- c.Write([]byte("END"))
- }
-}
-
-// Do not test empty datagrams by default.
-// It causes unexplained timeouts on some systems,
-// including Snow Leopard. I think that the kernel
-// doesn't quite expect them.
-var testDatagram = flag.Bool("datagram", false, "whether to test udp and unixgram")
-
-var datagramPacketConnServerTests = []struct {
- snet string // server side
- saddr string
- cnet string // client side
- caddr string
- dial bool // test with Dial or DialUnix
- empty bool // test with empty data
+var unixAndUnixpacketServerTests = []struct {
+ network, address string
}{
- {snet: "udp", saddr: ":0", cnet: "udp", caddr: "127.0.0.1"},
- {snet: "udp", saddr: "0.0.0.0:0", cnet: "udp", caddr: "127.0.0.1"},
- {snet: "udp", saddr: "[::ffff:0.0.0.0]:0", cnet: "udp", caddr: "127.0.0.1"},
- {snet: "udp", saddr: "[::]:0", cnet: "udp", caddr: "::1"},
+ {"unix", testUnixAddr()},
+ {"unix", "@nettest/go/unix"},
- {snet: "udp", saddr: ":0", cnet: "udp", caddr: "::1"},
- {snet: "udp", saddr: "0.0.0.0:0", cnet: "udp", caddr: "::1"},
- {snet: "udp", saddr: "[::ffff:0.0.0.0]:0", cnet: "udp", caddr: "::1"},
- {snet: "udp", saddr: "[::]:0", cnet: "udp", caddr: "127.0.0.1"},
-
- {snet: "udp", saddr: ":0", cnet: "udp4", caddr: "127.0.0.1"},
- {snet: "udp", saddr: "0.0.0.0:0", cnet: "udp4", caddr: "127.0.0.1"},
- {snet: "udp", saddr: "[::ffff:0.0.0.0]:0", cnet: "udp4", caddr: "127.0.0.1"},
- {snet: "udp", saddr: "[::]:0", cnet: "udp6", caddr: "::1"},
-
- {snet: "udp", saddr: ":0", cnet: "udp6", caddr: "::1"},
- {snet: "udp", saddr: "0.0.0.0:0", cnet: "udp6", caddr: "::1"},
- {snet: "udp", saddr: "[::ffff:0.0.0.0]:0", cnet: "udp6", caddr: "::1"},
- {snet: "udp", saddr: "[::]:0", cnet: "udp4", caddr: "127.0.0.1"},
-
- {snet: "udp", saddr: "127.0.0.1:0", cnet: "udp", caddr: "127.0.0.1"},
- {snet: "udp", saddr: "[::ffff:127.0.0.1]:0", cnet: "udp", caddr: "127.0.0.1"},
- {snet: "udp", saddr: "[::1]:0", cnet: "udp", caddr: "::1"},
-
- {snet: "udp4", saddr: ":0", cnet: "udp4", caddr: "127.0.0.1"},
- {snet: "udp4", saddr: "0.0.0.0:0", cnet: "udp4", caddr: "127.0.0.1"},
- {snet: "udp4", saddr: "[::ffff:0.0.0.0]:0", cnet: "udp4", caddr: "127.0.0.1"},
-
- {snet: "udp4", saddr: "127.0.0.1:0", cnet: "udp4", caddr: "127.0.0.1"},
-
- {snet: "udp6", saddr: ":0", cnet: "udp6", caddr: "::1"},
- {snet: "udp6", saddr: "[::]:0", cnet: "udp6", caddr: "::1"},
-
- {snet: "udp6", saddr: "[::1]:0", cnet: "udp6", caddr: "::1"},
-
- {snet: "udp", saddr: "127.0.0.1:0", cnet: "udp", caddr: "127.0.0.1", dial: true},
- {snet: "udp", saddr: "127.0.0.1:0", cnet: "udp", caddr: "127.0.0.1", empty: true},
- {snet: "udp", saddr: "127.0.0.1:0", cnet: "udp", caddr: "127.0.0.1", dial: true, empty: true},
-
- {snet: "udp", saddr: "[::1]:0", cnet: "udp", caddr: "::1", dial: true},
- {snet: "udp", saddr: "[::1]:0", cnet: "udp", caddr: "::1", empty: true},
- {snet: "udp", saddr: "[::1]:0", cnet: "udp", caddr: "::1", dial: true, empty: true},
-
- {snet: "unixgram", saddr: testUnixAddr(), cnet: "unixgram", caddr: testUnixAddr()},
- {snet: "unixgram", saddr: testUnixAddr(), cnet: "unixgram", caddr: testUnixAddr(), dial: true},
- {snet: "unixgram", saddr: testUnixAddr(), cnet: "unixgram", caddr: testUnixAddr(), empty: true},
- {snet: "unixgram", saddr: testUnixAddr(), cnet: "unixgram", caddr: testUnixAddr(), dial: true, empty: true},
-
- {snet: "unixgram", saddr: "@gotest6/net", cnet: "unixgram", caddr: "@gotest6/net.local"},
+ {"unixpacket", testUnixAddr()},
+ {"unixpacket", "@nettest/go/unixpacket"},
}
-func TestDatagramPacketConnServer(t *testing.T) {
- if !*testDatagram {
- return
- }
+// TestUnixAndUnixpacketServer tests concurrent accept-read-write
+// servers
+func TestUnixAndUnixpacketServer(t *testing.T) {
+ const N = 3
- for _, tt := range datagramPacketConnServerTests {
- if !testableListenArgs(tt.snet, tt.saddr, tt.caddr) {
- t.Logf("skipping %s test", tt.snet+":"+tt.saddr+"->"+tt.caddr)
+ for i, tt := range unixAndUnixpacketServerTests {
+ if !testableListenArgs(tt.network, tt.address, "") {
+ t.Logf("skipping %s test", tt.network+" "+tt.address)
continue
}
- listening := make(chan string)
- done := make(chan int)
- switch tt.snet {
- case "unixgram":
- os.Remove(tt.saddr)
- os.Remove(tt.caddr)
+ ln, err := Listen(tt.network, tt.address)
+ if err != nil {
+ if perr := parseDialError(err); perr != nil {
+ t.Error(perr)
+ }
+ t.Fatal(err)
}
- go runDatagramPacketConnServer(t, tt.snet, tt.saddr, listening, done)
- taddr := <-listening // wait for server to start
-
- switch tt.cnet {
- case "udp", "udp4", "udp6":
- _, port, err := SplitHostPort(taddr)
- if err != nil {
- t.Fatalf("SplitHostPort(%q) failed: %v", taddr, err)
+ var lss []*localServer
+ var tpchs []chan error
+ defer func() {
+ for _, ls := range lss {
+ ls.teardown()
}
- taddr = JoinHostPort(tt.caddr, port)
- tt.caddr = JoinHostPort(tt.caddr, "0")
+ }()
+ for i := 0; i < N; i++ {
+ ls, err := (&streamListener{Listener: ln}).newLocalServer()
+ if err != nil {
+ t.Fatal(err)
+ }
+ lss = append(lss, ls)
+ tpchs = append(tpchs, make(chan error, 1))
+ }
+ for i := 0; i < N; i++ {
+ ch := tpchs[i]
+ handler := func(ls *localServer, ln Listener) { transponder(ln, ch) }
+ if err := lss[i].buildup(handler); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ var trchs []chan error
+ for i := 0; i < N; i++ {
+ d := Dialer{Timeout: someTimeout}
+ c, err := d.Dial(lss[i].Listener.Addr().Network(), lss[i].Listener.Addr().String())
+ if err != nil {
+ if perr := parseDialError(err); perr != nil {
+ t.Error(perr)
+ }
+ t.Fatal(err)
+ }
+ defer os.Remove(c.LocalAddr().String())
+ defer c.Close()
+ trchs = append(trchs, make(chan error, 1))
+ go transceiver(c, []byte("UNIX AND UNIXPACKET SERVER TEST"), trchs[i])
+ }
+
+ for _, ch := range trchs {
+ for err := range ch {
+ t.Errorf("#%d: %v", i, err)
+ }
+ }
+ for _, ch := range tpchs {
+ for err := range ch {
+ t.Errorf("#%d: %v", i, err)
+ }
+ }
+ }
+}
+
+var udpServerTests = []struct {
+ snet, saddr string // server endpoint
+ tnet, taddr string // target endpoint for client
+ dial bool // test with Dial
+}{
+ {snet: "udp", saddr: ":0", tnet: "udp", taddr: "127.0.0.1"},
+ {snet: "udp", saddr: "0.0.0.0:0", tnet: "udp", taddr: "127.0.0.1"},
+ {snet: "udp", saddr: "[::ffff:0.0.0.0]:0", tnet: "udp", taddr: "127.0.0.1"},
+ {snet: "udp", saddr: "[::]:0", tnet: "udp", taddr: "::1"},
+
+ {snet: "udp", saddr: ":0", tnet: "udp", taddr: "::1"},
+ {snet: "udp", saddr: "0.0.0.0:0", tnet: "udp", taddr: "::1"},
+ {snet: "udp", saddr: "[::ffff:0.0.0.0]:0", tnet: "udp", taddr: "::1"},
+ {snet: "udp", saddr: "[::]:0", tnet: "udp", taddr: "127.0.0.1"},
+
+ {snet: "udp", saddr: ":0", tnet: "udp4", taddr: "127.0.0.1"},
+ {snet: "udp", saddr: "0.0.0.0:0", tnet: "udp4", taddr: "127.0.0.1"},
+ {snet: "udp", saddr: "[::ffff:0.0.0.0]:0", tnet: "udp4", taddr: "127.0.0.1"},
+ {snet: "udp", saddr: "[::]:0", tnet: "udp6", taddr: "::1"},
+
+ {snet: "udp", saddr: ":0", tnet: "udp6", taddr: "::1"},
+ {snet: "udp", saddr: "0.0.0.0:0", tnet: "udp6", taddr: "::1"},
+ {snet: "udp", saddr: "[::ffff:0.0.0.0]:0", tnet: "udp6", taddr: "::1"},
+ {snet: "udp", saddr: "[::]:0", tnet: "udp4", taddr: "127.0.0.1"},
+
+ {snet: "udp", saddr: "127.0.0.1:0", tnet: "udp", taddr: "127.0.0.1"},
+ {snet: "udp", saddr: "[::ffff:127.0.0.1]:0", tnet: "udp", taddr: "127.0.0.1"},
+ {snet: "udp", saddr: "[::1]:0", tnet: "udp", taddr: "::1"},
+
+ {snet: "udp4", saddr: ":0", tnet: "udp4", taddr: "127.0.0.1"},
+ {snet: "udp4", saddr: "0.0.0.0:0", tnet: "udp4", taddr: "127.0.0.1"},
+ {snet: "udp4", saddr: "[::ffff:0.0.0.0]:0", tnet: "udp4", taddr: "127.0.0.1"},
+
+ {snet: "udp4", saddr: "127.0.0.1:0", tnet: "udp4", taddr: "127.0.0.1"},
+
+ {snet: "udp6", saddr: ":0", tnet: "udp6", taddr: "::1"},
+ {snet: "udp6", saddr: "[::]:0", tnet: "udp6", taddr: "::1"},
+
+ {snet: "udp6", saddr: "[::1]:0", tnet: "udp6", taddr: "::1"},
+
+ {snet: "udp", saddr: "127.0.0.1:0", tnet: "udp", taddr: "127.0.0.1", dial: true},
+
+ {snet: "udp", saddr: "[::1]:0", tnet: "udp", taddr: "::1", dial: true},
+}
+
+func TestUDPServer(t *testing.T) {
+ for i, tt := range udpServerTests {
+ if !testableListenArgs(tt.snet, tt.saddr, tt.taddr) {
+ t.Logf("skipping %s test", tt.snet+" "+tt.saddr+"->"+tt.taddr)
+ continue
+ }
+
+ c1, err := ListenPacket(tt.snet, tt.saddr)
+ if err != nil {
+ if perr := parseDialError(err); perr != nil {
+ t.Error(perr)
+ }
+ t.Fatal(err)
+ }
+
+ ls, err := (&packetListener{PacketConn: c1}).newLocalServer()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ls.teardown()
+ tpch := make(chan error, 1)
+ handler := func(ls *localPacketServer, c PacketConn) { packetTransponder(c, tpch) }
+ if err := ls.buildup(handler); err != nil {
+ t.Fatal(err)
+ }
+
+ trch := make(chan error, 1)
+ _, port, err := SplitHostPort(ls.PacketConn.LocalAddr().String())
+ if err != nil {
+ t.Fatal(err)
}
if tt.dial {
- runDatagramConnClient(t, tt.cnet, tt.caddr, taddr, tt.empty)
- } else {
- runDatagramPacketConnClient(t, tt.cnet, tt.caddr, taddr, tt.empty)
- }
- <-done // tell server to stop
- <-done // make sure server stopped
-
- switch tt.snet {
- case "unixgram":
- os.Remove(tt.saddr)
- os.Remove(tt.caddr)
- }
- }
-}
-
-func runDatagramPacketConnServer(t *testing.T, net, laddr string, listening chan<- string, done chan<- int) {
- c, err := ListenPacket(net, laddr)
- if err != nil {
- t.Errorf("ListenPacket(%q, %q) failed: %v", net, laddr, err)
- listening <- ""
- done <- 1
- return
- }
- defer c.Close()
- listening <- c.LocalAddr().String()
-
- buf := make([]byte, 1024)
-run:
- for {
- c.SetReadDeadline(time.Now().Add(10 * time.Millisecond))
- n, ra, err := c.ReadFrom(buf[0:])
- if nerr, ok := err.(Error); ok && nerr.Timeout() {
- select {
- case done <- 1:
- break run
- default:
- continue run
+ d := Dialer{Timeout: someTimeout}
+ c2, err := d.Dial(tt.tnet, JoinHostPort(tt.taddr, port))
+ if err != nil {
+ if perr := parseDialError(err); perr != nil {
+ t.Error(perr)
+ }
+ t.Fatal(err)
}
+ defer c2.Close()
+ go transceiver(c2, []byte("UDP SERVER TEST"), trch)
+ } else {
+ c2, err := ListenPacket(tt.tnet, JoinHostPort(tt.taddr, "0"))
+ if err != nil {
+ if perr := parseDialError(err); perr != nil {
+ t.Error(perr)
+ }
+ t.Fatal(err)
+ }
+ defer c2.Close()
+ dst, err := ResolveUDPAddr(tt.tnet, JoinHostPort(tt.taddr, port))
+ if err != nil {
+ t.Fatal(err)
+ }
+ go packetTransceiver(c2, []byte("UDP SERVER TEST"), dst, trch)
}
- if err != nil {
- break run
- }
- if _, err = c.WriteTo(buf[0:n], ra); err != nil {
- t.Errorf("WriteTo(%v) failed: %v", ra, err)
- break run
- }
- }
- done <- 1
-}
-func runDatagramConnClient(t *testing.T, net, laddr, taddr string, isEmpty bool) {
- var c Conn
- var err error
- switch net {
- case "udp", "udp4", "udp6":
- c, err = Dial(net, taddr)
- if err != nil {
- t.Fatalf("Dial(%q, %q) failed: %v", net, taddr, err)
+ for err := range trch {
+ t.Errorf("#%d: %v", i, err)
}
- case "unixgram":
- c, err = DialUnix(net, &UnixAddr{Name: laddr, Net: net}, &UnixAddr{Name: taddr, Net: net})
- if err != nil {
- t.Fatalf("DialUnix(%q, {%q, %q}) failed: %v", net, laddr, taddr, err)
+ for err := range tpch {
+ t.Errorf("#%d: %v", i, err)
}
}
- defer c.Close()
- c.SetReadDeadline(time.Now().Add(1 * time.Second))
-
- var wb []byte
- if !isEmpty {
- wb = []byte("DatagramConnClient by Dial\n")
- }
- if n, err := c.Write(wb[0:]); err != nil || n != len(wb) {
- t.Fatalf("Write failed: %v, %v; want %v, ", n, err, len(wb))
- }
-
- rb := make([]byte, 1024)
- if n, err := c.Read(rb[0:]); err != nil || n != len(wb) {
- t.Fatalf("Read failed: %v, %v; want %v, ", n, err, len(wb))
- }
}
-func runDatagramPacketConnClient(t *testing.T, net, laddr, taddr string, isEmpty bool) {
- var ra Addr
- var err error
- switch net {
- case "udp", "udp4", "udp6":
- ra, err = ResolveUDPAddr(net, taddr)
- if err != nil {
- t.Fatalf("ResolveUDPAddr(%q, %q) failed: %v", net, taddr, err)
- }
- case "unixgram":
- ra, err = ResolveUnixAddr(net, taddr)
- if err != nil {
- t.Fatalf("ResolveUxixAddr(%q, %q) failed: %v", net, taddr, err)
- }
- }
- c, err := ListenPacket(net, laddr)
- if err != nil {
- t.Fatalf("ListenPacket(%q, %q) failed: %v", net, laddr, err)
- }
- defer c.Close()
- c.SetReadDeadline(time.Now().Add(1 * time.Second))
+var unixgramServerTests = []struct {
+ saddr string // server endpoint
+ caddr string // client endpoint
+ dial bool // test with Dial
+}{
+ {saddr: testUnixAddr(), caddr: testUnixAddr()},
+ {saddr: testUnixAddr(), caddr: testUnixAddr(), dial: true},
- var wb []byte
- if !isEmpty {
- wb = []byte("DatagramPacketConnClient by ListenPacket\n")
- }
- if n, err := c.WriteTo(wb[0:], ra); err != nil || n != len(wb) {
- t.Fatalf("WriteTo(%v) failed: %v, %v; want %v, ", ra, n, err, len(wb))
- }
+ {saddr: "@nettest/go/unixgram/server", caddr: "@nettest/go/unixgram/client"},
+}
- rb := make([]byte, 1024)
- if n, _, err := c.ReadFrom(rb[0:]); err != nil || n != len(wb) {
- t.Fatalf("ReadFrom failed: %v, %v; want %v, ", n, err, len(wb))
+func TestUnixgramServer(t *testing.T) {
+ for i, tt := range unixgramServerTests {
+ if !testableListenArgs("unixgram", tt.saddr, "") {
+ t.Logf("skipping %s test", "unixgram "+tt.saddr+"->"+tt.caddr)
+ continue
+ }
+
+ c1, err := ListenPacket("unixgram", tt.saddr)
+ if err != nil {
+ if perr := parseDialError(err); perr != nil {
+ t.Error(perr)
+ }
+ t.Fatal(err)
+ }
+
+ ls, err := (&packetListener{PacketConn: c1}).newLocalServer()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ls.teardown()
+ tpch := make(chan error, 1)
+ handler := func(ls *localPacketServer, c PacketConn) { packetTransponder(c, tpch) }
+ if err := ls.buildup(handler); err != nil {
+ t.Fatal(err)
+ }
+
+ trch := make(chan error, 1)
+ if tt.dial {
+ d := Dialer{Timeout: someTimeout, LocalAddr: &UnixAddr{Net: "unixgram", Name: tt.caddr}}
+ c2, err := d.Dial("unixgram", ls.PacketConn.LocalAddr().String())
+ if err != nil {
+ if perr := parseDialError(err); perr != nil {
+ t.Error(perr)
+ }
+ t.Fatal(err)
+ }
+ defer os.Remove(c2.LocalAddr().String())
+ defer c2.Close()
+ go transceiver(c2, []byte(c2.LocalAddr().String()), trch)
+ } else {
+ c2, err := ListenPacket("unixgram", tt.caddr)
+ if err != nil {
+ if perr := parseDialError(err); perr != nil {
+ t.Error(perr)
+ }
+ t.Fatal(err)
+ }
+ defer os.Remove(c2.LocalAddr().String())
+ defer c2.Close()
+ go packetTransceiver(c2, []byte("UNIXGRAM SERVER TEST"), ls.PacketConn.LocalAddr(), trch)
+ }
+
+ for err := range trch {
+ t.Errorf("#%d: %v", i, err)
+ }
+ for err := range tpch {
+ t.Errorf("#%d: %v", i, err)
+ }
}
}
diff --git a/src/net/sock_cloexec.go b/src/net/sock_cloexec.go
index 842d7d5538..616a101eac 100644
--- a/src/net/sock_cloexec.go
+++ b/src/net/sock_cloexec.go
@@ -9,7 +9,10 @@
package net
-import "syscall"
+import (
+ "os"
+ "syscall"
+)
// Wrapper around the socket system call that marks the returned file
// descriptor as nonblocking and close-on-exec.
@@ -20,8 +23,12 @@ func sysSocket(family, sotype, proto int) (int, error) {
// introduced in 10 kernel. If we get an EINVAL error on Linux
// or EPROTONOSUPPORT error on FreeBSD, fall back to using
// socket without them.
- if err == nil || (err != syscall.EPROTONOSUPPORT && err != syscall.EINVAL) {
- return s, err
+ switch err {
+ case nil:
+ return s, nil
+ default:
+ return -1, os.NewSyscallError("socket", err)
+ case syscall.EPROTONOSUPPORT, syscall.EINVAL:
}
// See ../syscall/exec_unix.go for description of ForkLock.
@@ -32,11 +39,11 @@ func sysSocket(family, sotype, proto int) (int, error) {
}
syscall.ForkLock.RUnlock()
if err != nil {
- return -1, err
+ return -1, os.NewSyscallError("socket", err)
}
if err = syscall.SetNonblock(s, true); err != nil {
closeFunc(s)
- return -1, err
+ return -1, os.NewSyscallError("setnonblock", err)
}
return s, nil
}
@@ -50,8 +57,10 @@ func accept(s int) (int, syscall.Sockaddr, error) {
// get an ENOSYS error on both Linux and FreeBSD, or EINVAL
// error on Linux, fall back to using accept.
switch err {
- default: // nil and errors other than the ones listed
- return ns, sa, err
+ case nil:
+ return ns, sa, nil
+ default: // errors other than the ones listed
+ return -1, sa, os.NewSyscallError("accept4", err)
case syscall.ENOSYS: // syscall missing
case syscall.EINVAL: // some Linux use this instead of ENOSYS
case syscall.EACCES: // some Linux use this instead of ENOSYS
@@ -68,11 +77,11 @@ func accept(s int) (int, syscall.Sockaddr, error) {
syscall.CloseOnExec(ns)
}
if err != nil {
- return -1, nil, err
+ return -1, nil, os.NewSyscallError("accept", err)
}
if err = syscall.SetNonblock(ns, true); err != nil {
closeFunc(ns)
- return -1, nil, err
+ return -1, nil, os.NewSyscallError("setnonblock", err)
}
return ns, sa, nil
}
diff --git a/src/net/sock_posix.go b/src/net/sock_posix.go
index 013944ebec..2634a6b646 100644
--- a/src/net/sock_posix.go
+++ b/src/net/sock_posix.go
@@ -17,8 +17,6 @@ import (
type sockaddr interface {
Addr
- netaddr
-
// family returns the platform-dependent address family
// identifier.
family() int
@@ -165,7 +163,7 @@ func (fd *netFD) listenStream(laddr sockaddr, backlog int) error {
return os.NewSyscallError("bind", err)
}
}
- if err := syscall.Listen(fd.sysfd, backlog); err != nil {
+ if err := listenFunc(fd.sysfd, backlog); err != nil {
return os.NewSyscallError("listen", err)
}
if err := fd.init(); err != nil {
diff --git a/src/net/sock_windows.go b/src/net/sock_windows.go
index 591861f443..888e70bb8e 100644
--- a/src/net/sock_windows.go
+++ b/src/net/sock_windows.go
@@ -4,7 +4,10 @@
package net
-import "syscall"
+import (
+ "os"
+ "syscall"
+)
func maxListenerBacklog() int {
// TODO: Implement this
@@ -20,5 +23,8 @@ func sysSocket(family, sotype, proto int) (syscall.Handle, error) {
syscall.CloseOnExec(s)
}
syscall.ForkLock.RUnlock()
- return s, err
+ if err != nil {
+ return syscall.InvalidHandle, os.NewSyscallError("socket", err)
+ }
+ return s, nil
}
diff --git a/src/net/sys_cloexec.go b/src/net/sys_cloexec.go
index 5a631aa56d..ba266e6534 100644
--- a/src/net/sys_cloexec.go
+++ b/src/net/sys_cloexec.go
@@ -9,7 +9,10 @@
package net
-import "syscall"
+import (
+ "os"
+ "syscall"
+)
// Wrapper around the socket system call that marks the returned file
// descriptor as nonblocking and close-on-exec.
@@ -22,11 +25,11 @@ func sysSocket(family, sotype, proto int) (int, error) {
}
syscall.ForkLock.RUnlock()
if err != nil {
- return -1, err
+ return -1, os.NewSyscallError("socket", err)
}
if err = syscall.SetNonblock(s, true); err != nil {
closeFunc(s)
- return -1, err
+ return -1, os.NewSyscallError("setnonblock", err)
}
return s, nil
}
@@ -44,11 +47,11 @@ func accept(s int) (int, syscall.Sockaddr, error) {
syscall.CloseOnExec(ns)
}
if err != nil {
- return -1, nil, err
+ return -1, nil, os.NewSyscallError("accept", err)
}
if err = syscall.SetNonblock(ns, true); err != nil {
closeFunc(ns)
- return -1, nil, err
+ return -1, nil, os.NewSyscallError("setnonblock", err)
}
return ns, sa, nil
}
diff --git a/src/net/tcp_test.go b/src/net/tcp_test.go
index 434c9c6cef..6229df2869 100644
--- a/src/net/tcp_test.go
+++ b/src/net/tcp_test.go
@@ -5,7 +5,6 @@
package net
import (
- "fmt"
"io"
"reflect"
"runtime"
@@ -59,8 +58,7 @@ func BenchmarkTCP6PersistentTimeout(b *testing.B) {
}
func benchmarkTCP(b *testing.B, persistent, timeout bool, laddr string) {
- uninstallTestHooks()
- defer installTestHooks()
+ testHookUninstaller.Do(func() { uninstallTestHooks() })
const msgLen = 512
conns := b.N
@@ -79,7 +77,7 @@ func benchmarkTCP(b *testing.B, persistent, timeout bool, laddr string) {
sendMsg := func(c Conn, buf []byte) bool {
n, err := c.Write(buf)
if n != len(buf) || err != nil {
- b.Logf("Write failed: %v", err)
+ b.Log(err)
return false
}
return true
@@ -89,7 +87,7 @@ func benchmarkTCP(b *testing.B, persistent, timeout bool, laddr string) {
n, err := c.Read(buf)
read += n
if err != nil {
- b.Logf("Read failed: %v", err)
+ b.Log(err)
return false
}
}
@@ -97,7 +95,7 @@ func benchmarkTCP(b *testing.B, persistent, timeout bool, laddr string) {
}
ln, err := Listen("tcp", laddr)
if err != nil {
- b.Fatalf("Listen failed: %v", err)
+ b.Fatal(err)
}
defer ln.Close()
serverSem := make(chan bool, numConcurrent)
@@ -137,7 +135,7 @@ func benchmarkTCP(b *testing.B, persistent, timeout bool, laddr string) {
}()
c, err := Dial("tcp", ln.Addr().String())
if err != nil {
- b.Logf("Dial failed: %v", err)
+ b.Log(err)
return
}
defer c.Close()
@@ -170,13 +168,13 @@ func BenchmarkTCP6ConcurrentReadWrite(b *testing.B) {
}
func benchmarkTCPConcurrentReadWrite(b *testing.B, laddr string) {
+ testHookUninstaller.Do(func() { uninstallTestHooks() })
+
// The benchmark creates GOMAXPROCS client/server pairs.
// Each pair creates 4 goroutines: client reader/writer and server reader/writer.
// The benchmark stresses concurrent reading and writing to the same connection.
// Such pattern is used in net/http and net/rpc.
- uninstallTestHooks()
- defer installTestHooks()
b.StopTimer()
P := runtime.GOMAXPROCS(0)
@@ -188,7 +186,7 @@ func benchmarkTCPConcurrentReadWrite(b *testing.B, laddr string) {
servers := make([]Conn, P)
ln, err := Listen("tcp", laddr)
if err != nil {
- b.Fatalf("Listen failed: %v", err)
+ b.Fatal(err)
}
defer ln.Close()
done := make(chan bool)
@@ -196,7 +194,7 @@ func benchmarkTCPConcurrentReadWrite(b *testing.B, laddr string) {
for p := 0; p < P; p++ {
s, err := ln.Accept()
if err != nil {
- b.Errorf("Accept failed: %v", err)
+ b.Error(err)
return
}
servers[p] = s
@@ -206,7 +204,7 @@ func benchmarkTCPConcurrentReadWrite(b *testing.B, laddr string) {
for p := 0; p < P; p++ {
c, err := Dial("tcp", ln.Addr().String())
if err != nil {
- b.Fatalf("Dial failed: %v", err)
+ b.Fatal(err)
}
clients[p] = c
}
@@ -229,7 +227,7 @@ func benchmarkTCPConcurrentReadWrite(b *testing.B, laddr string) {
buf[0] = v
_, err := c.Write(buf[:])
if err != nil {
- b.Errorf("Write failed: %v", err)
+ b.Error(err)
return
}
}
@@ -245,7 +243,7 @@ func benchmarkTCPConcurrentReadWrite(b *testing.B, laddr string) {
for i := 0; i < N; i++ {
_, err := s.Read(buf[:])
if err != nil {
- b.Errorf("Read failed: %v", err)
+ b.Error(err)
return
}
pipe <- buf[0]
@@ -264,7 +262,7 @@ func benchmarkTCPConcurrentReadWrite(b *testing.B, laddr string) {
buf[0] = v
_, err := s.Write(buf[:])
if err != nil {
- b.Errorf("Write failed: %v", err)
+ b.Error(err)
return
}
}
@@ -278,7 +276,7 @@ func benchmarkTCPConcurrentReadWrite(b *testing.B, laddr string) {
for i := 0; i < N; i++ {
_, err := c.Read(buf[:])
if err != nil {
- b.Errorf("Read failed: %v", err)
+ b.Error(err)
return
}
}
@@ -289,7 +287,7 @@ func benchmarkTCPConcurrentReadWrite(b *testing.B, laddr string) {
}
type resolveTCPAddrTest struct {
- net string
+ network string
litAddrOrName string
addr *TCPAddr
err error
@@ -299,8 +297,8 @@ var resolveTCPAddrTests = []resolveTCPAddrTest{
{"tcp", "127.0.0.1:0", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil},
{"tcp4", "127.0.0.1:65535", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 65535}, nil},
- {"tcp", "[::1]:1", &TCPAddr{IP: ParseIP("::1"), Port: 1}, nil},
- {"tcp6", "[::1]:65534", &TCPAddr{IP: ParseIP("::1"), Port: 65534}, nil},
+ {"tcp", "[::1]:0", &TCPAddr{IP: ParseIP("::1"), Port: 0}, nil},
+ {"tcp6", "[::1]:65535", &TCPAddr{IP: ParseIP("::1"), Port: 65535}, nil},
{"tcp", "[::1%en0]:1", &TCPAddr{IP: ParseIP("::1"), Port: 1, Zone: "en0"}, nil},
{"tcp6", "[::1%911]:2", &TCPAddr{IP: ParseIP("::1"), Port: 2, Zone: "911"}, nil},
@@ -313,41 +311,26 @@ var resolveTCPAddrTests = []resolveTCPAddrTest{
{"http", "127.0.0.1:0", nil, UnknownNetworkError("http")},
}
-func init() {
- if ifi := loopbackInterface(); ifi != nil {
- index := fmt.Sprintf("%v", ifi.Index)
- resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
- {"tcp6", "[fe80::1%" + ifi.Name + "]:3", &TCPAddr{IP: ParseIP("fe80::1"), Port: 3, Zone: zoneToString(ifi.Index)}, nil},
- {"tcp6", "[fe80::1%" + index + "]:4", &TCPAddr{IP: ParseIP("fe80::1"), Port: 4, Zone: index}, nil},
- }...)
- }
- if ips, err := LookupIP("localhost"); err == nil && len(ips) > 1 && supportsIPv4 && supportsIPv6 {
- resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
- {"tcp", "localhost:5", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5}, nil},
- {"tcp4", "localhost:6", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 6}, nil},
- {"tcp6", "localhost:7", &TCPAddr{IP: IPv6loopback, Port: 7}, nil},
- }...)
- }
-}
-
func TestResolveTCPAddr(t *testing.T) {
- for _, tt := range resolveTCPAddrTests {
- addr, err := ResolveTCPAddr(tt.net, tt.litAddrOrName)
+ origTestHookLookupIP := testHookLookupIP
+ defer func() { testHookLookupIP = origTestHookLookupIP }()
+ testHookLookupIP = lookupLocalhost
+
+ for i, tt := range resolveTCPAddrTests {
+ addr, err := ResolveTCPAddr(tt.network, tt.litAddrOrName)
if err != tt.err {
- t.Fatalf("ResolveTCPAddr(%q, %q) failed: %v", tt.net, tt.litAddrOrName, err)
+ t.Errorf("#%d: %v", i, err)
+ } else if !reflect.DeepEqual(addr, tt.addr) {
+ t.Errorf("#%d: got %#v; want %#v", i, addr, tt.addr)
}
- if !reflect.DeepEqual(addr, tt.addr) {
- t.Fatalf("ResolveTCPAddr(%q, %q) = %#v, want %#v", tt.net, tt.litAddrOrName, addr, tt.addr)
+ if err != nil {
+ continue
}
- if err == nil {
- str := addr.String()
- addr1, err := ResolveTCPAddr(tt.net, str)
- if err != nil {
- t.Fatalf("ResolveTCPAddr(%q, %q) [from %q]: %v", tt.net, str, tt.litAddrOrName, err)
- }
- if !reflect.DeepEqual(addr1, addr) {
- t.Fatalf("ResolveTCPAddr(%q, %q) [from %q] = %#v, want %#v", tt.net, str, tt.litAddrOrName, addr1, addr)
- }
+ rtaddr, err := ResolveTCPAddr(addr.Network(), addr.String())
+ if err != nil {
+ t.Errorf("#%d: %v", i, err)
+ } else if !reflect.DeepEqual(rtaddr, addr) {
+ t.Errorf("#%d: got %#v; want %#v", i, rtaddr, addr)
}
}
}
@@ -363,13 +346,13 @@ var tcpListenerNameTests = []struct {
func TestTCPListenerName(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
+ t.Skip("avoid external network")
}
for _, tt := range tcpListenerNameTests {
ln, err := ListenTCP(tt.net, tt.laddr)
if err != nil {
- t.Fatalf("ListenTCP failed: %v", err)
+ t.Fatal(err)
}
defer ln.Close()
la := ln.Addr()
@@ -381,7 +364,7 @@ func TestTCPListenerName(t *testing.T) {
func TestIPv6LinkLocalUnicastTCP(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
+ t.Skip("avoid external network")
}
if !supportsIPv6 {
t.Skip("ipv6 is not supported")
@@ -415,25 +398,31 @@ func TestIPv6LinkLocalUnicastTCP(t *testing.T) {
{"tcp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
}...)
}
- for _, tt := range tests {
+ for i, tt := range tests {
ln, err := Listen(tt.net, tt.addr)
if err != nil {
// It might return "LookupHost returned no
// suitable address" error on some platforms.
- t.Logf("Listen failed: %v", err)
+ t.Log(err)
continue
}
- defer ln.Close()
+ ls, err := (&streamListener{Listener: ln}).newLocalServer()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ls.teardown()
+ ch := make(chan error, 1)
+ handler := func(ls *localServer, ln Listener) { transponder(ln, ch) }
+ if err := ls.buildup(handler); err != nil {
+ t.Fatal(err)
+ }
if la, ok := ln.Addr().(*TCPAddr); !ok || !tt.nameLookup && la.Zone == "" {
t.Fatalf("got %v; expected a proper address with zone identifier", la)
}
- done := make(chan int)
- go transponder(t, ln, done)
-
- c, err := Dial(tt.net, ln.Addr().String())
+ c, err := Dial(tt.net, ls.Listener.Addr().String())
if err != nil {
- t.Fatalf("Dial failed: %v", err)
+ t.Fatal(err)
}
defer c.Close()
if la, ok := c.LocalAddr().(*TCPAddr); !ok || !tt.nameLookup && la.Zone == "" {
@@ -444,14 +433,16 @@ func TestIPv6LinkLocalUnicastTCP(t *testing.T) {
}
if _, err := c.Write([]byte("TCP OVER IPV6 LINKLOCAL TEST")); err != nil {
- t.Fatalf("Conn.Write failed: %v", err)
+ t.Fatal(err)
}
b := make([]byte, 32)
if _, err := c.Read(b); err != nil {
- t.Fatalf("Conn.Read failed: %v", err)
+ t.Fatal(err)
}
- <-done
+ for err := range ch {
+ t.Errorf("#%d: %v", i, err)
+ }
}
}
@@ -459,7 +450,7 @@ func TestTCPConcurrentAccept(t *testing.T) {
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
ln, err := Listen("tcp", "127.0.0.1:0")
if err != nil {
- t.Fatalf("Listen failed: %v", err)
+ t.Fatal(err)
}
const N = 10
var wg sync.WaitGroup
@@ -555,7 +546,7 @@ func TestTCPStress(t *testing.T) {
sendMsg := func(c Conn, buf []byte) bool {
n, err := c.Write(buf)
if n != len(buf) || err != nil {
- t.Logf("Write failed: %v", err)
+ t.Log(err)
return false
}
return true
@@ -565,7 +556,7 @@ func TestTCPStress(t *testing.T) {
n, err := c.Read(buf)
read += n
if err != nil {
- t.Logf("Read failed: %v", err)
+ t.Log(err)
return false
}
}
@@ -574,7 +565,7 @@ func TestTCPStress(t *testing.T) {
ln, err := Listen("tcp", "127.0.0.1:0")
if err != nil {
- t.Fatalf("Listen failed: %v", err)
+ t.Fatal(err)
}
defer ln.Close()
// Acceptor.
@@ -605,7 +596,7 @@ func TestTCPStress(t *testing.T) {
}()
c, err := Dial("tcp", ln.Addr().String())
if err != nil {
- t.Logf("Dial failed: %v", err)
+ t.Log(err)
return
}
defer c.Close()
diff --git a/src/net/tcpsock.go b/src/net/tcpsock.go
index fbadad65b0..b7c95b2b94 100644
--- a/src/net/tcpsock.go
+++ b/src/net/tcpsock.go
@@ -32,13 +32,6 @@ func (a *TCPAddr) isWildcard() bool {
return a.IP.IsUnspecified()
}
-func (a *TCPAddr) toAddr() Addr {
- if a == nil {
- return nil
- }
- return a
-}
-
// ResolveTCPAddr parses addr as a TCP address of the form "host:port"
// or "[ipv6-host%zone]:port" and resolves a pair of domain name and
// port name on the network net, which must be "tcp", "tcp4" or
@@ -53,9 +46,9 @@ func ResolveTCPAddr(net, addr string) (*TCPAddr, error) {
default:
return nil, UnknownNetworkError(net)
}
- a, err := resolveInternetAddr(net, addr, noDeadline)
+ addrs, err := internetAddrList(net, addr, noDeadline)
if err != nil {
return nil, err
}
- return a.toAddr().(*TCPAddr), nil
+ return addrs.first(isIPv4).(*TCPAddr), nil
}
diff --git a/src/net/tcpsock_plan9.go b/src/net/tcpsock_plan9.go
index ae2194277d..bc3f8fc4dd 100644
--- a/src/net/tcpsock_plan9.go
+++ b/src/net/tcpsock_plan9.go
@@ -23,7 +23,11 @@ func newTCPConn(fd *netFD) *TCPConn {
// ReadFrom implements the io.ReaderFrom ReadFrom method.
func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) {
- return genericReadFrom(c, r)
+ n, err := genericReadFrom(c, r)
+ if err != nil && err != io.EOF {
+ err = &OpError{Op: "read", Net: c.fd.dir, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+ }
+ return n, err
}
// CloseRead shuts down the reading side of the TCP connection.
@@ -32,7 +36,11 @@ func (c *TCPConn) CloseRead() error {
if !c.ok() {
return syscall.EINVAL
}
- return c.fd.closeRead()
+ err := c.fd.closeRead()
+ if err != nil {
+ err = &OpError{Op: "close", Net: c.fd.dir, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+ }
+ return err
}
// CloseWrite shuts down the writing side of the TCP connection.
@@ -41,7 +49,11 @@ func (c *TCPConn) CloseWrite() error {
if !c.ok() {
return syscall.EINVAL
}
- return c.fd.closeWrite()
+ err := c.fd.closeWrite()
+ if err != nil {
+ err = &OpError{Op: "close", Net: c.fd.dir, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+ }
+ return err
}
// SetLinger sets the behavior of Close on a connection which still
@@ -57,7 +69,7 @@ func (c *TCPConn) CloseWrite() error {
// some operating systems after sec seconds have elapsed any remaining
// unsent data may be discarded.
func (c *TCPConn) SetLinger(sec int) error {
- return syscall.EPLAN9
+ return &OpError{Op: "set", Net: c.fd.dir, Source: c.fd.laddr, Addr: c.fd.raddr, Err: syscall.EPLAN9}
}
// SetKeepAlive sets whether the operating system should send
@@ -66,7 +78,10 @@ func (c *TCPConn) SetKeepAlive(keepalive bool) error {
if !c.ok() {
return syscall.EPLAN9
}
- return setKeepAlive(c.fd, keepalive)
+ if err := setKeepAlive(c.fd, keepalive); err != nil {
+ return &OpError{Op: "set", Net: c.fd.dir, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+ }
+ return nil
}
// SetKeepAlivePeriod sets period between keep alives.
@@ -74,7 +89,10 @@ func (c *TCPConn) SetKeepAlivePeriod(d time.Duration) error {
if !c.ok() {
return syscall.EPLAN9
}
- return setKeepAlivePeriod(c.fd, d)
+ if err := setKeepAlivePeriod(c.fd, d); err != nil {
+ return &OpError{Op: "set", Net: c.fd.dir, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+ }
+ return nil
}
// SetNoDelay controls whether the operating system should delay
@@ -82,7 +100,7 @@ func (c *TCPConn) SetKeepAlivePeriod(d time.Duration) error {
// algorithm). The default is true (no delay), meaning that data is
// sent as soon as possible after a Write.
func (c *TCPConn) SetNoDelay(noDelay bool) error {
- return syscall.EPLAN9
+ return &OpError{Op: "set", Net: c.fd.dir, Source: c.fd.laddr, Addr: c.fd.raddr, Err: syscall.EPLAN9}
}
// DialTCP connects to the remote address raddr on the network net,
@@ -99,10 +117,10 @@ func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, e
switch net {
case "tcp", "tcp4", "tcp6":
default:
- return nil, &OpError{"dial", net, raddr, UnknownNetworkError(net)}
+ return nil, &OpError{Op: "dial", Net: net, Source: laddr, Addr: raddr, Err: UnknownNetworkError(net)}
}
if raddr == nil {
- return nil, &OpError{"dial", net, nil, errMissingAddress}
+ return nil, &OpError{Op: "dial", Net: net, Source: laddr, Addr: raddr, Err: errMissingAddress}
}
fd, err := dialPlan9(net, laddr, raddr)
if err != nil {
@@ -151,9 +169,13 @@ func (l *TCPListener) Close() error {
}
if _, err := l.fd.ctl.WriteString("hangup"); err != nil {
l.fd.ctl.Close()
- return &OpError{"close", l.fd.ctl.Name(), l.fd.laddr, err}
+ return &OpError{Op: "close", Net: l.fd.dir, Source: nil, Addr: l.fd.laddr, Err: err}
}
- return l.fd.ctl.Close()
+ err := l.fd.ctl.Close()
+ if err != nil {
+ err = &OpError{Op: "close", Net: l.fd.dir, Source: nil, Addr: l.fd.laddr, Err: err}
+ }
+ return err
}
// Addr returns the listener's network address, a *TCPAddr.
@@ -167,7 +189,10 @@ func (l *TCPListener) SetDeadline(t time.Time) error {
if l == nil || l.fd == nil || l.fd.ctl == nil {
return syscall.EINVAL
}
- return l.fd.setDeadline(t)
+ if err := l.fd.setDeadline(t); err != nil {
+ return &OpError{Op: "set", Net: l.fd.dir, Source: nil, Addr: l.fd.laddr, Err: err}
+ }
+ return nil
}
// File returns a copy of the underlying os.File, set to blocking
@@ -177,7 +202,13 @@ func (l *TCPListener) SetDeadline(t time.Time) error {
// The returned os.File's file descriptor is different from the
// connection's. Attempting to change properties of the original
// using this duplicate may or may not have the desired effect.
-func (l *TCPListener) File() (f *os.File, err error) { return l.dup() }
+func (l *TCPListener) File() (f *os.File, err error) {
+ f, err = l.dup()
+ if err != nil {
+ err = &OpError{Op: "file", Net: l.fd.dir, Source: nil, Addr: l.fd.laddr, Err: err}
+ }
+ return
+}
// ListenTCP announces on the TCP address laddr and returns a TCP
// listener. Net must be "tcp", "tcp4", or "tcp6". If laddr has a
@@ -187,7 +218,7 @@ func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
switch net {
case "tcp", "tcp4", "tcp6":
default:
- return nil, &OpError{"listen", net, laddr, UnknownNetworkError(net)}
+ return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr, Err: UnknownNetworkError(net)}
}
if laddr == nil {
laddr = &TCPAddr{}
diff --git a/src/net/tcpsock_posix.go b/src/net/tcpsock_posix.go
index 024dcd4f83..1f43521a9e 100644
--- a/src/net/tcpsock_posix.go
+++ b/src/net/tcpsock_posix.go
@@ -53,16 +53,23 @@ type TCPConn struct {
func newTCPConn(fd *netFD) *TCPConn {
c := &TCPConn{conn{fd}}
- c.SetNoDelay(true)
+ setNoDelay(c.fd, true)
return c
}
// ReadFrom implements the io.ReaderFrom ReadFrom method.
func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) {
if n, err, handled := sendFile(c.fd, r); handled {
+ if err != nil && err != io.EOF {
+ err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+ }
return n, err
}
- return genericReadFrom(c, r)
+ n, err := genericReadFrom(c, r)
+ if err != nil && err != io.EOF {
+ err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+ }
+ return n, err
}
// CloseRead shuts down the reading side of the TCP connection.
@@ -71,7 +78,11 @@ func (c *TCPConn) CloseRead() error {
if !c.ok() {
return syscall.EINVAL
}
- return c.fd.closeRead()
+ err := c.fd.closeRead()
+ if err != nil {
+ err = &OpError{Op: "close", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+ }
+ return err
}
// CloseWrite shuts down the writing side of the TCP connection.
@@ -80,7 +91,11 @@ func (c *TCPConn) CloseWrite() error {
if !c.ok() {
return syscall.EINVAL
}
- return c.fd.closeWrite()
+ err := c.fd.closeWrite()
+ if err != nil {
+ err = &OpError{Op: "close", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+ }
+ return err
}
// SetLinger sets the behavior of Close on a connection which still
@@ -99,7 +114,10 @@ func (c *TCPConn) SetLinger(sec int) error {
if !c.ok() {
return syscall.EINVAL
}
- return setLinger(c.fd, sec)
+ if err := setLinger(c.fd, sec); err != nil {
+ return &OpError{Op: "set", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+ }
+ return nil
}
// SetKeepAlive sets whether the operating system should send
@@ -108,7 +126,10 @@ func (c *TCPConn) SetKeepAlive(keepalive bool) error {
if !c.ok() {
return syscall.EINVAL
}
- return setKeepAlive(c.fd, keepalive)
+ if err := setKeepAlive(c.fd, keepalive); err != nil {
+ return &OpError{Op: "set", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+ }
+ return nil
}
// SetKeepAlivePeriod sets period between keep alives.
@@ -116,7 +137,10 @@ func (c *TCPConn) SetKeepAlivePeriod(d time.Duration) error {
if !c.ok() {
return syscall.EINVAL
}
- return setKeepAlivePeriod(c.fd, d)
+ if err := setKeepAlivePeriod(c.fd, d); err != nil {
+ return &OpError{Op: "set", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+ }
+ return nil
}
// SetNoDelay controls whether the operating system should delay
@@ -127,7 +151,10 @@ func (c *TCPConn) SetNoDelay(noDelay bool) error {
if !c.ok() {
return syscall.EINVAL
}
- return setNoDelay(c.fd, noDelay)
+ if err := setNoDelay(c.fd, noDelay); err != nil {
+ return &OpError{Op: "set", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+ }
+ return nil
}
// DialTCP connects to the remote address raddr on the network net,
@@ -137,10 +164,10 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
switch net {
case "tcp", "tcp4", "tcp6":
default:
- return nil, &OpError{Op: "dial", Net: net, Addr: raddr, Err: UnknownNetworkError(net)}
+ return nil, &OpError{Op: "dial", Net: net, Source: laddr, Addr: raddr, Err: UnknownNetworkError(net)}
}
if raddr == nil {
- return nil, &OpError{Op: "dial", Net: net, Addr: nil, Err: errMissingAddress}
+ return nil, &OpError{Op: "dial", Net: net, Source: laddr, Addr: raddr, Err: errMissingAddress}
}
return dialTCP(net, laddr, raddr, noDeadline)
}
@@ -180,7 +207,7 @@ func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, e
}
if err != nil {
- return nil, &OpError{Op: "dial", Net: net, Addr: raddr, Err: err}
+ return nil, &OpError{Op: "dial", Net: net, Source: laddr, Addr: raddr, Err: err}
}
return newTCPConn(fd), nil
}
@@ -226,7 +253,7 @@ func (l *TCPListener) AcceptTCP() (*TCPConn, error) {
}
fd, err := l.fd.accept()
if err != nil {
- return nil, err
+ return nil, &OpError{Op: "accept", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err}
}
return newTCPConn(fd), nil
}
@@ -247,7 +274,11 @@ func (l *TCPListener) Close() error {
if l == nil || l.fd == nil {
return syscall.EINVAL
}
- return l.fd.Close()
+ err := l.fd.Close()
+ if err != nil {
+ err = &OpError{Op: "close", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err}
+ }
+ return err
}
// Addr returns the listener's network address, a *TCPAddr.
@@ -261,7 +292,10 @@ func (l *TCPListener) SetDeadline(t time.Time) error {
if l == nil || l.fd == nil {
return syscall.EINVAL
}
- return l.fd.setDeadline(t)
+ if err := l.fd.setDeadline(t); err != nil {
+ return &OpError{Op: "set", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err}
+ }
+ return nil
}
// File returns a copy of the underlying os.File, set to blocking
@@ -271,7 +305,13 @@ func (l *TCPListener) SetDeadline(t time.Time) error {
// The returned os.File's file descriptor is different from the
// connection's. Attempting to change properties of the original
// using this duplicate may or may not have the desired effect.
-func (l *TCPListener) File() (f *os.File, err error) { return l.fd.dup() }
+func (l *TCPListener) File() (f *os.File, err error) {
+ f, err = l.fd.dup()
+ if err != nil {
+ err = &OpError{Op: "file", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err}
+ }
+ return
+}
// ListenTCP announces on the TCP address laddr and returns a TCP
// listener. Net must be "tcp", "tcp4", or "tcp6". If laddr has a
@@ -281,14 +321,14 @@ func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
switch net {
case "tcp", "tcp4", "tcp6":
default:
- return nil, &OpError{Op: "listen", Net: net, Addr: laddr, Err: UnknownNetworkError(net)}
+ return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr, Err: UnknownNetworkError(net)}
}
if laddr == nil {
laddr = &TCPAddr{}
}
fd, err := internetSocket(net, laddr, nil, noDeadline, syscall.SOCK_STREAM, 0, "listen")
if err != nil {
- return nil, &OpError{Op: "listen", Net: net, Addr: laddr, Err: err}
+ return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr, Err: err}
}
return &TCPListener{fd}, nil
}
diff --git a/src/net/tcpsockopt_solaris.go b/src/net/tcpsockopt_solaris.go
new file mode 100644
index 0000000000..31f5df0526
--- /dev/null
+++ b/src/net/tcpsockopt_solaris.go
@@ -0,0 +1,35 @@
+// Copyright 2015 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 net
+
+import (
+ "os"
+ "syscall"
+ "time"
+)
+
+func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
+ if err := fd.incref(); err != nil {
+ return err
+ }
+ defer fd.decref()
+ // The kernel expects milliseconds so round to next highest
+ // millisecond.
+ d += (time.Millisecond - time.Nanosecond)
+ msecs := int(d / time.Millisecond)
+
+ // Normally we'd do
+ // syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, secs)
+ // here, but we can't because Solaris does not have TCP_KEEPINTVL.
+ // Solaris has TCP_KEEPALIVE_ABORT_THRESHOLD, but it's not the same
+ // thing, it refers to the total time until aborting (not between
+ // probes), and it uses an exponential backoff algorithm instead of
+ // waiting the same time between probes. We can't hope for the best
+ // and do it anyway, like on Darwin, because Solaris might eventually
+ // allocate a constant with a different meaning for the value of
+ // TCP_KEEPINTVL on illumos.
+
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE_THRESHOLD, msecs))
+}
diff --git a/src/net/tcpsockopt_unix.go b/src/net/tcpsockopt_unix.go
index c9f604cad7..c8970d1b57 100644
--- a/src/net/tcpsockopt_unix.go
+++ b/src/net/tcpsockopt_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 freebsd linux netbsd solaris
+// +build freebsd linux netbsd
package net
diff --git a/src/net/tcpsockopt_windows.go b/src/net/tcpsockopt_windows.go
index 091f5233f2..ae2d7c8f18 100644
--- a/src/net/tcpsockopt_windows.go
+++ b/src/net/tcpsockopt_windows.go
@@ -28,5 +28,5 @@ func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
ret := uint32(0)
size := uint32(unsafe.Sizeof(ka))
err := syscall.WSAIoctl(fd.sysfd, syscall.SIO_KEEPALIVE_VALS, (*byte)(unsafe.Pointer(&ka)), size, nil, 0, &ret, nil, 0)
- return os.NewSyscallError("WSAIoctl", err)
+ return os.NewSyscallError("wsaioctl", err)
}
diff --git a/src/net/testdata/openbsd-resolv.conf b/src/net/testdata/openbsd-resolv.conf
new file mode 100644
index 0000000000..8281a91b4a
--- /dev/null
+++ b/src/net/testdata/openbsd-resolv.conf
@@ -0,0 +1,5 @@
+# Generated by vio0 dhclient
+search c.symbolic-datum-552.internal.
+nameserver 169.254.169.254
+nameserver 10.240.0.1
+lookup file bind
diff --git a/src/net/timeout_test.go b/src/net/timeout_test.go
index 9ef0c4d15c..cafa3755f6 100644
--- a/src/net/timeout_test.go
+++ b/src/net/timeout_test.go
@@ -8,408 +8,710 @@ import (
"fmt"
"io"
"io/ioutil"
+ "net/internal/socktest"
"runtime"
+ "sync"
"testing"
"time"
)
-func isTimeout(err error) bool {
- e, ok := err.(Error)
- return ok && e.Timeout()
+var dialTimeoutTests = []struct {
+ timeout time.Duration
+ delta time.Duration // for deadline
+
+ guard time.Duration
+ max time.Duration
+}{
+ // Tests that dial timeouts, deadlines in the past work.
+ {-5 * time.Second, 0, -5 * time.Second, 100 * time.Millisecond},
+ {0, -5 * time.Second, -5 * time.Second, 100 * time.Millisecond},
+ {-5 * time.Second, 5 * time.Second, -5 * time.Second, 100 * time.Millisecond}, // timeout over deadline
+
+ {50 * time.Millisecond, 0, 100 * time.Millisecond, time.Second},
+ {0, 50 * time.Millisecond, 100 * time.Millisecond, time.Second},
+ {50 * time.Millisecond, 5 * time.Second, 100 * time.Millisecond, time.Second}, // timeout over deadline
}
-type copyRes struct {
- n int64
- err error
- d time.Duration
+func TestDialTimeout(t *testing.T) {
+ origTestHookDialChannel := testHookDialChannel
+ defer func() { testHookDialChannel = origTestHookDialChannel }()
+ defer sw.Set(socktest.FilterConnect, nil)
+
+ for i, tt := range dialTimeoutTests {
+ switch runtime.GOOS {
+ case "plan9", "windows":
+ testHookDialChannel = func() { time.Sleep(tt.guard) }
+ if runtime.GOOS == "plan9" {
+ break
+ }
+ fallthrough
+ default:
+ sw.Set(socktest.FilterConnect, func(so *socktest.Status) (socktest.AfterFilter, error) {
+ time.Sleep(tt.guard)
+ return nil, errTimedout
+ })
+ }
+
+ ch := make(chan error)
+ d := Dialer{Timeout: tt.timeout}
+ if tt.delta != 0 {
+ d.Deadline = time.Now().Add(tt.delta)
+ }
+ max := time.NewTimer(tt.max)
+ defer max.Stop()
+ go func() {
+ // This dial never starts to send any TCP SYN
+ // segment because of above socket filter and
+ // test hook.
+ c, err := d.Dial("tcp", "127.0.0.1:0")
+ if err == nil {
+ err = fmt.Errorf("unexpectedly established: tcp:%s->%s", c.LocalAddr(), c.RemoteAddr())
+ c.Close()
+ }
+ ch <- err
+ }()
+
+ select {
+ case <-max.C:
+ t.Fatalf("#%d: Dial didn't return in an expected time", i)
+ case err := <-ch:
+ if perr := parseDialError(err); perr != nil {
+ t.Errorf("#%d: %v", i, perr)
+ }
+ if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
+ t.Fatalf("#%d: %v", i, err)
+ }
+ }
+ }
+}
+
+var acceptTimeoutTests = []struct {
+ timeout time.Duration
+ xerrs [2]error // expected errors in transition
+}{
+ // Tests that accept deadlines in the past work, even if
+ // there's incoming connections available.
+ {-5 * time.Second, [2]error{errTimeout, errTimeout}},
+
+ {50 * time.Millisecond, [2]error{nil, errTimeout}},
}
func TestAcceptTimeout(t *testing.T) {
switch runtime.GOOS {
case "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
+ t.Skipf("not supported on %s", runtime.GOOS)
}
- ln := newLocalListener(t).(*TCPListener)
+ ln, err := newLocalListener("tcp")
+ if err != nil {
+ t.Fatal(err)
+ }
defer ln.Close()
- ln.SetDeadline(time.Now().Add(-1 * time.Second))
- if _, err := ln.Accept(); !isTimeout(err) {
- t.Fatalf("Accept: expected err %v, got %v", errTimeout, err)
+
+ for i, tt := range acceptTimeoutTests {
+ if tt.timeout < 0 {
+ go func() {
+ c, err := Dial(ln.Addr().Network(), ln.Addr().String())
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ var b [1]byte
+ c.Read(b[:])
+ c.Close()
+ }()
+ }
+
+ if err := ln.(*TCPListener).SetDeadline(time.Now().Add(tt.timeout)); err != nil {
+ t.Fatalf("$%d: %v", i, err)
+ }
+ for j, xerr := range tt.xerrs {
+ for {
+ c, err := ln.Accept()
+ if xerr != nil {
+ if perr := parseAcceptError(err); perr != nil {
+ t.Errorf("#%d/%d: %v", i, j, perr)
+ }
+ if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
+ t.Fatalf("#%d/%d: %v", i, j, err)
+ }
+ }
+ if err == nil {
+ c.Close()
+ time.Sleep(tt.timeout / 3)
+ continue
+ }
+ break
+ }
+ }
}
- if _, err := ln.Accept(); !isTimeout(err) {
- t.Fatalf("Accept: expected err %v, got %v", errTimeout, err)
+}
+
+func TestAcceptTimeoutMustReturn(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
}
- ln.SetDeadline(time.Now().Add(100 * time.Millisecond))
- if _, err := ln.Accept(); !isTimeout(err) {
- t.Fatalf("Accept: expected err %v, got %v", errTimeout, err)
+
+ ln, err := newLocalListener("tcp")
+ if err != nil {
+ t.Fatal(err)
}
- if _, err := ln.Accept(); !isTimeout(err) {
- t.Fatalf("Accept: expected err %v, got %v", errTimeout, err)
- }
- ln.SetDeadline(noDeadline)
- errc := make(chan error)
+ defer ln.Close()
+
+ max := time.NewTimer(time.Second)
+ defer max.Stop()
+ ch := make(chan error)
go func() {
- _, err := ln.Accept()
- errc <- err
+ if err := ln.(*TCPListener).SetDeadline(noDeadline); err != nil {
+ t.Error(err)
+ }
+ if err := ln.(*TCPListener).SetDeadline(time.Now().Add(10 * time.Millisecond)); err != nil {
+ t.Error(err)
+ }
+ c, err := ln.Accept()
+ if err == nil {
+ c.Close()
+ }
+ ch <- err
}()
- time.Sleep(100 * time.Millisecond)
+
select {
- case err := <-errc:
- t.Fatalf("Expected Accept() to not return, but it returned with %v\n", err)
- default:
- }
- ln.Close()
- switch nerr := <-errc; err := nerr.(type) {
- case *OpError:
- if err.Err != errClosing {
- t.Fatalf("Accept: expected err %v, got %v", errClosing, err)
+ case <-max.C:
+ ln.Close()
+ <-ch // wait for tester goroutine to stop
+ t.Fatal("Accept didn't return in an expected time")
+ case err := <-ch:
+ if perr := parseAcceptError(err); perr != nil {
+ t.Error(perr)
}
- default:
- if err != errClosing {
- t.Fatalf("Accept: expected err %v, got %v", errClosing, err)
+ if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
+ t.Fatal(err)
}
}
}
+func TestAcceptTimeoutMustNotReturn(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+
+ ln, err := newLocalListener("tcp")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ln.Close()
+
+ max := time.NewTimer(100 * time.Millisecond)
+ defer max.Stop()
+ ch := make(chan error)
+ go func() {
+ if err := ln.(*TCPListener).SetDeadline(time.Now().Add(-5 * time.Second)); err != nil {
+ t.Error(err)
+ }
+ if err := ln.(*TCPListener).SetDeadline(noDeadline); err != nil {
+ t.Error(err)
+ }
+ _, err := ln.Accept()
+ ch <- err
+ }()
+
+ select {
+ case err := <-ch:
+ if perr := parseAcceptError(err); perr != nil {
+ t.Error(perr)
+ }
+ t.Fatalf("expected Accept to not return, but it returned with %v", err)
+ case <-max.C:
+ ln.Close()
+ <-ch // wait for tester goroutine to stop
+ }
+}
+
+var readTimeoutTests = []struct {
+ timeout time.Duration
+ xerrs [2]error // expected errors in transition
+}{
+ // Tests that read deadlines work, even if there's data ready
+ // to be read.
+ {-5 * time.Second, [2]error{errTimeout, errTimeout}},
+
+ {50 * time.Millisecond, [2]error{nil, errTimeout}},
+}
+
func TestReadTimeout(t *testing.T) {
switch runtime.GOOS {
case "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
+ t.Skipf("not supported on %s", runtime.GOOS)
}
- ln := newLocalListener(t)
- defer ln.Close()
- c, err := DialTCP("tcp", nil, ln.Addr().(*TCPAddr))
+ handler := func(ls *localServer, ln Listener) {
+ c, err := ln.Accept()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ c.Write([]byte("READ TIMEOUT TEST"))
+ defer c.Close()
+ }
+ ls, err := newLocalServer("tcp")
if err != nil {
- t.Fatalf("Connect: %v", err)
+ t.Fatal(err)
+ }
+ defer ls.teardown()
+ if err := ls.buildup(handler); err != nil {
+ t.Fatal(err)
+ }
+
+ c, err := Dial(ls.Listener.Addr().Network(), ls.Listener.Addr().String())
+ if err != nil {
+ t.Fatal(err)
}
defer c.Close()
- c.SetDeadline(time.Now().Add(time.Hour))
- c.SetReadDeadline(time.Now().Add(-1 * time.Second))
- buf := make([]byte, 1)
- if _, err = c.Read(buf); !isTimeout(err) {
- t.Fatalf("Read: expected err %v, got %v", errTimeout, err)
+
+ for i, tt := range readTimeoutTests {
+ if err := c.SetReadDeadline(time.Now().Add(tt.timeout)); err != nil {
+ t.Fatalf("#%d: %v", i, err)
+ }
+ var b [1]byte
+ for j, xerr := range tt.xerrs {
+ for {
+ n, err := c.Read(b[:])
+ if xerr != nil {
+ if perr := parseReadError(err); perr != nil {
+ t.Errorf("#%d/%d: %v", i, j, perr)
+ }
+ if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
+ t.Fatalf("#%d/%d: %v", i, j, err)
+ }
+ }
+ if err == nil {
+ time.Sleep(tt.timeout / 3)
+ continue
+ }
+ if n != 0 {
+ t.Fatalf("#%d/%d: read %d; want 0", i, j, n)
+ }
+ break
+ }
+ }
}
- if _, err = c.Read(buf); !isTimeout(err) {
- t.Fatalf("Read: expected err %v, got %v", errTimeout, err)
+}
+
+func TestReadTimeoutMustNotReturn(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
}
- c.SetDeadline(time.Now().Add(100 * time.Millisecond))
- if _, err = c.Read(buf); !isTimeout(err) {
- t.Fatalf("Read: expected err %v, got %v", errTimeout, err)
+
+ ln, err := newLocalListener("tcp")
+ if err != nil {
+ t.Fatal(err)
}
- if _, err = c.Read(buf); !isTimeout(err) {
- t.Fatalf("Read: expected err %v, got %v", errTimeout, err)
+ defer ln.Close()
+
+ c, err := Dial(ln.Addr().Network(), ln.Addr().String())
+ if err != nil {
+ t.Fatal(err)
}
- c.SetReadDeadline(noDeadline)
- c.SetWriteDeadline(time.Now().Add(-1 * time.Second))
- errc := make(chan error)
+ defer c.Close()
+
+ max := time.NewTimer(100 * time.Millisecond)
+ defer max.Stop()
+ ch := make(chan error)
go func() {
- _, err := c.Read(buf)
- errc <- err
+ if err := c.SetDeadline(time.Now().Add(-5 * time.Second)); err != nil {
+ t.Error(err)
+ }
+ if err := c.SetWriteDeadline(time.Now().Add(-5 * time.Second)); err != nil {
+ t.Error(err)
+ }
+ if err := c.SetReadDeadline(noDeadline); err != nil {
+ t.Error(err)
+ }
+ var b [1]byte
+ _, err := c.Read(b[:])
+ ch <- err
}()
- time.Sleep(100 * time.Millisecond)
+
select {
- case err := <-errc:
- t.Fatalf("Expected Read() to not return, but it returned with %v\n", err)
- default:
- }
- c.Close()
- switch nerr := <-errc; err := nerr.(type) {
- case *OpError:
- if err.Err != errClosing {
- t.Fatalf("Read: expected err %v, got %v", errClosing, err)
+ case err := <-ch:
+ if perr := parseReadError(err); perr != nil {
+ t.Error(perr)
}
- default:
- if err == io.EOF && runtime.GOOS == "nacl" { // close enough; golang.org/issue/8044
- break
+ t.Fatalf("expected Read to not return, but it returned with %v", err)
+ case <-max.C:
+ c.Close()
+ err := <-ch // wait for tester goroutine to stop
+ if perr := parseReadError(err); perr != nil {
+ t.Error(perr)
}
- if err != errClosing {
- t.Fatalf("Read: expected err %v, got %v", errClosing, err)
+ if err == io.EOF && runtime.GOOS == "nacl" { // see golang.org/issue/8044
+ return
+ }
+ if nerr, ok := err.(Error); !ok || nerr.Timeout() || nerr.Temporary() {
+ t.Fatal(err)
}
}
}
+var readFromTimeoutTests = []struct {
+ timeout time.Duration
+ xerrs [2]error // expected errors in transition
+}{
+ // Tests that read deadlines work, even if there's data ready
+ // to be read.
+ {-5 * time.Second, [2]error{errTimeout, errTimeout}},
+
+ {50 * time.Millisecond, [2]error{nil, errTimeout}},
+}
+
+func TestReadFromTimeout(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS) // see golang.org/issue/8916
+ }
+
+ ch := make(chan Addr)
+ defer close(ch)
+ handler := func(ls *localPacketServer, c PacketConn) {
+ if dst, ok := <-ch; ok {
+ c.WriteTo([]byte("READFROM TIMEOUT TEST"), dst)
+ }
+ }
+ ls, err := newLocalPacketServer("udp")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ls.teardown()
+ if err := ls.buildup(handler); err != nil {
+ t.Fatal(err)
+ }
+
+ host, _, err := SplitHostPort(ls.PacketConn.LocalAddr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ c, err := ListenPacket(ls.PacketConn.LocalAddr().Network(), JoinHostPort(host, "0"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+ ch <- c.LocalAddr()
+
+ for i, tt := range readFromTimeoutTests {
+ if err := c.SetReadDeadline(time.Now().Add(tt.timeout)); err != nil {
+ t.Fatalf("#%d: %v", i, err)
+ }
+ var b [1]byte
+ for j, xerr := range tt.xerrs {
+ for {
+ n, _, err := c.ReadFrom(b[:])
+ if xerr != nil {
+ if perr := parseReadError(err); perr != nil {
+ t.Errorf("#%d/%d: %v", i, j, perr)
+ }
+ if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
+ t.Fatalf("#%d/%d: %v", i, j, err)
+ }
+ }
+ if err == nil {
+ time.Sleep(tt.timeout / 3)
+ continue
+ }
+ if n != 0 {
+ t.Fatalf("#%d/%d: read %d; want 0", i, j, n)
+ }
+ break
+ }
+ }
+ }
+}
+
+var writeTimeoutTests = []struct {
+ timeout time.Duration
+ xerrs [2]error // expected errors in transition
+}{
+ // Tests that write deadlines work, even if there's buffer
+ // space available to write.
+ {-5 * time.Second, [2]error{errTimeout, errTimeout}},
+
+ {10 * time.Millisecond, [2]error{nil, errTimeout}},
+}
+
func TestWriteTimeout(t *testing.T) {
switch runtime.GOOS {
case "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
+ t.Skipf("not supported on %s", runtime.GOOS)
}
- ln := newLocalListener(t)
- defer ln.Close()
- c, err := DialTCP("tcp", nil, ln.Addr().(*TCPAddr))
- if err != nil {
- t.Fatalf("Connect: %v", err)
- }
- defer c.Close()
- c.SetDeadline(time.Now().Add(time.Hour))
- c.SetWriteDeadline(time.Now().Add(-1 * time.Second))
- buf := make([]byte, 4096)
- writeUntilTimeout := func() {
- for {
- _, err := c.Write(buf)
- if err != nil {
- if isTimeout(err) {
- return
- }
- t.Fatalf("Write: expected err %v, got %v", errTimeout, err)
- }
- }
- }
- writeUntilTimeout()
- c.SetDeadline(time.Now().Add(10 * time.Millisecond))
- writeUntilTimeout()
- writeUntilTimeout()
- c.SetWriteDeadline(noDeadline)
- c.SetReadDeadline(time.Now().Add(-1 * time.Second))
- errc := make(chan error)
- go func() {
- for {
- _, err := c.Write(buf)
- if err != nil {
- errc <- err
- }
- }
- }()
- time.Sleep(100 * time.Millisecond)
- select {
- case err := <-errc:
- t.Fatalf("Expected Write() to not return, but it returned with %v\n", err)
- default:
- }
- c.Close()
- switch nerr := <-errc; err := nerr.(type) {
- case *OpError:
- if err.Err != errClosing {
- t.Fatalf("Write: expected err %v, got %v", errClosing, err)
- }
- default:
- if err != errClosing {
- t.Fatalf("Write: expected err %v, got %v", errClosing, err)
- }
- }
-}
-
-func testTimeout(t *testing.T, net, addr string, readFrom bool) {
- c, err := Dial(net, addr)
- if err != nil {
- t.Errorf("Dial(%q, %q) failed: %v", net, addr, err)
- return
- }
- defer c.Close()
- what := "Read"
- if readFrom {
- what = "ReadFrom"
- }
-
- errc := make(chan error, 1)
- go func() {
- t0 := time.Now()
- c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
- var b [100]byte
- var n int
- var err error
- if readFrom {
- n, _, err = c.(PacketConn).ReadFrom(b[0:])
- } else {
- n, err = c.Read(b[0:])
- }
- t1 := time.Now()
- if n != 0 || err == nil || !err.(Error).Timeout() {
- errc <- fmt.Errorf("%s(%q, %q) did not return 0, timeout: %v, %v", what, net, addr, n, err)
- return
- }
- if dt := t1.Sub(t0); dt < 50*time.Millisecond || !testing.Short() && dt > 250*time.Millisecond {
- errc <- fmt.Errorf("%s(%q, %q) took %s, expected 0.1s", what, net, addr, dt)
- return
- }
- errc <- nil
- }()
- select {
- case err := <-errc:
- if err != nil {
- t.Error(err)
- }
- case <-time.After(1 * time.Second):
- t.Errorf("%s(%q, %q) took over 1 second, expected 0.1s", what, net, addr)
- }
-}
-
-func TestTimeoutUDP(t *testing.T) {
- switch runtime.GOOS {
- case "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
- }
-
- // set up a listener that won't talk back
- listening := make(chan string)
- done := make(chan int)
- go runDatagramPacketConnServer(t, "udp", "127.0.0.1:0", listening, done)
- addr := <-listening
-
- testTimeout(t, "udp", addr, false)
- testTimeout(t, "udp", addr, true)
- <-done
-}
-
-func TestTimeoutTCP(t *testing.T) {
- switch runtime.GOOS {
- case "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
- }
-
- // set up a listener that won't talk back
- listening := make(chan string)
- done := make(chan int)
- go runStreamConnServer(t, "tcp", "127.0.0.1:0", listening, done)
- addr := <-listening
-
- testTimeout(t, "tcp", addr, false)
- <-done
-}
-
-func TestDeadlineReset(t *testing.T) {
- switch runtime.GOOS {
- case "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
- }
- ln, err := Listen("tcp", "127.0.0.1:0")
+ ln, err := newLocalListener("tcp")
if err != nil {
t.Fatal(err)
}
defer ln.Close()
- tl := ln.(*TCPListener)
- tl.SetDeadline(time.Now().Add(1 * time.Minute))
- tl.SetDeadline(noDeadline) // reset it
- errc := make(chan error, 1)
- go func() {
- _, err := ln.Accept()
- errc <- err
- }()
- select {
- case <-time.After(50 * time.Millisecond):
- // Pass.
- case err := <-errc:
- // Accept should never return; we never
- // connected to it.
- t.Errorf("unexpected return from Accept; err=%v", err)
- }
-}
-func TestTimeoutAccept(t *testing.T) {
- switch runtime.GOOS {
- case "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
- }
- ln, err := Listen("tcp", "127.0.0.1:0")
- if err != nil {
- t.Fatal(err)
- }
- defer ln.Close()
- tl := ln.(*TCPListener)
- tl.SetDeadline(time.Now().Add(100 * time.Millisecond))
- errc := make(chan error, 1)
- go func() {
- _, err := ln.Accept()
- errc <- err
- }()
- select {
- case <-time.After(1 * time.Second):
- // Accept shouldn't block indefinitely
- t.Errorf("Accept didn't return in an expected time")
- case <-errc:
- // Pass.
- }
-}
-
-func TestReadWriteDeadline(t *testing.T) {
- switch runtime.GOOS {
- case "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
- }
-
- const (
- readTimeout = 50 * time.Millisecond
- writeTimeout = 250 * time.Millisecond
- )
- checkTimeout := func(command string, start time.Time, should time.Duration) {
- is := time.Now().Sub(start)
- d := is - should
- if d < -30*time.Millisecond || !testing.Short() && 150*time.Millisecond < d {
- t.Errorf("%s timeout test failed: is=%v should=%v\n", command, is, should)
- }
- }
-
- ln, err := Listen("tcp", "127.0.0.1:0")
- if err != nil {
- t.Fatalf("ListenTCP on :0: %v", err)
- }
- defer ln.Close()
-
- lnquit := make(chan bool)
-
- go func() {
- c, err := ln.Accept()
+ for i, tt := range writeTimeoutTests {
+ c, err := Dial(ln.Addr().Network(), ln.Addr().String())
if err != nil {
- t.Errorf("Accept: %v", err)
- return
+ t.Fatal(err)
}
defer c.Close()
- lnquit <- true
- }()
- c, err := Dial("tcp", ln.Addr().String())
- if err != nil {
- t.Fatalf("Dial: %v", err)
- }
- defer c.Close()
-
- start := time.Now()
- err = c.SetReadDeadline(start.Add(readTimeout))
- if err != nil {
- t.Fatalf("SetReadDeadline: %v", err)
- }
- err = c.SetWriteDeadline(start.Add(writeTimeout))
- if err != nil {
- t.Fatalf("SetWriteDeadline: %v", err)
- }
-
- quit := make(chan bool)
-
- go func() {
- var buf [10]byte
- _, err := c.Read(buf[:])
- if err == nil {
- t.Errorf("Read should not succeed")
+ if err := c.SetWriteDeadline(time.Now().Add(tt.timeout)); err != nil {
+ t.Fatalf("#%d: %v", i, err)
}
- checkTimeout("Read", start, readTimeout)
- quit <- true
- }()
-
- go func() {
- var buf [10000]byte
- for {
- _, err := c.Write(buf[:])
- if err != nil {
+ for j, xerr := range tt.xerrs {
+ for {
+ n, err := c.Write([]byte("WRITE TIMEOUT TEST"))
+ if xerr != nil {
+ if perr := parseWriteError(err); perr != nil {
+ t.Errorf("#%d/%d: %v", i, j, perr)
+ }
+ if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
+ t.Fatalf("#%d/%d: %v", i, j, err)
+ }
+ }
+ if err == nil {
+ time.Sleep(tt.timeout / 3)
+ continue
+ }
+ if n != 0 {
+ t.Fatalf("#%d/%d: wrote %d; want 0", i, j, n)
+ }
break
}
}
- checkTimeout("Write", start, writeTimeout)
- quit <- true
- }()
-
- <-quit
- <-quit
- <-lnquit
+ }
}
-type neverEnding byte
-
-func (b neverEnding) Read(p []byte) (n int, err error) {
- for i := range p {
- p[i] = byte(b)
+func TestWriteTimeoutMustNotReturn(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+
+ ln, err := newLocalListener("tcp")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ln.Close()
+
+ c, err := Dial(ln.Addr().Network(), ln.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ max := time.NewTimer(100 * time.Millisecond)
+ defer max.Stop()
+ ch := make(chan error)
+ go func() {
+ if err := c.SetDeadline(time.Now().Add(-5 * time.Second)); err != nil {
+ t.Error(err)
+ }
+ if err := c.SetReadDeadline(time.Now().Add(-5 * time.Second)); err != nil {
+ t.Error(err)
+ }
+ if err := c.SetWriteDeadline(noDeadline); err != nil {
+ t.Error(err)
+ }
+ var b [1]byte
+ for {
+ if _, err := c.Write(b[:]); err != nil {
+ ch <- err
+ break
+ }
+ }
+ }()
+
+ select {
+ case err := <-ch:
+ if perr := parseWriteError(err); perr != nil {
+ t.Error(perr)
+ }
+ t.Fatalf("expected Write to not return, but it returned with %v", err)
+ case <-max.C:
+ c.Close()
+ err := <-ch // wait for tester goroutine to stop
+ if perr := parseWriteError(err); perr != nil {
+ t.Error(perr)
+ }
+ if nerr, ok := err.(Error); !ok || nerr.Timeout() || nerr.Temporary() {
+ t.Fatal(err)
+ }
+ }
+}
+
+var writeToTimeoutTests = []struct {
+ timeout time.Duration
+ xerrs [2]error // expected errors in transition
+}{
+ // Tests that write deadlines work, even if there's buffer
+ // space available to write.
+ {-5 * time.Second, [2]error{errTimeout, errTimeout}},
+
+ {10 * time.Millisecond, [2]error{nil, errTimeout}},
+}
+
+func TestWriteToTimeout(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+
+ c1, err := newLocalPacketListener("udp")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c1.Close()
+
+ host, _, err := SplitHostPort(c1.LocalAddr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for i, tt := range writeToTimeoutTests {
+ c2, err := ListenPacket(c1.LocalAddr().Network(), JoinHostPort(host, "0"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c2.Close()
+
+ if err := c2.SetWriteDeadline(time.Now().Add(tt.timeout)); err != nil {
+ t.Fatalf("#%d: %v", i, err)
+ }
+ for j, xerr := range tt.xerrs {
+ for {
+ n, err := c2.WriteTo([]byte("WRITETO TIMEOUT TEST"), c1.LocalAddr())
+ if xerr != nil {
+ if perr := parseWriteError(err); perr != nil {
+ t.Errorf("#%d/%d: %v", i, j, perr)
+ }
+ if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
+ t.Fatalf("#%d/%d: %v", i, j, err)
+ }
+ }
+ if err == nil {
+ time.Sleep(tt.timeout / 3)
+ continue
+ }
+ if n != 0 {
+ t.Fatalf("#%d/%d: wrote %d; want 0", i, j, n)
+ }
+ break
+ }
+ }
+ }
+}
+
+func TestReadTimeoutFluctuation(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+
+ ln, err := newLocalListener("tcp")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ln.Close()
+
+ c, err := Dial(ln.Addr().Network(), ln.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ max := time.NewTimer(time.Second)
+ defer max.Stop()
+ ch := make(chan error)
+ go timeoutReceiver(c, 100*time.Millisecond, 50*time.Millisecond, 250*time.Millisecond, ch)
+
+ select {
+ case <-max.C:
+ t.Fatal("Read took over 1s; expected 0.1s")
+ case err := <-ch:
+ if perr := parseReadError(err); perr != nil {
+ t.Error(perr)
+ }
+ if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
+ t.Fatal(err)
+ }
+ }
+}
+
+func TestReadFromTimeoutFluctuation(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+
+ c1, err := newLocalPacketListener("udp")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c1.Close()
+
+ c2, err := Dial(c1.LocalAddr().Network(), c1.LocalAddr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c2.Close()
+
+ max := time.NewTimer(time.Second)
+ defer max.Stop()
+ ch := make(chan error)
+ go timeoutPacketReceiver(c2.(PacketConn), 100*time.Millisecond, 50*time.Millisecond, 250*time.Millisecond, ch)
+
+ select {
+ case <-max.C:
+ t.Fatal("ReadFrom took over 1s; expected 0.1s")
+ case err := <-ch:
+ if perr := parseReadError(err); perr != nil {
+ t.Error(perr)
+ }
+ if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
+ t.Fatal(err)
+ }
+ }
+}
+
+func TestWriteTimeoutFluctuation(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+
+ ln, err := newLocalListener("tcp")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ln.Close()
+
+ c, err := Dial(ln.Addr().Network(), ln.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ max := time.NewTimer(time.Second)
+ defer max.Stop()
+ ch := make(chan error)
+ go timeoutTransmitter(c, 100*time.Millisecond, 50*time.Millisecond, 250*time.Millisecond, ch)
+
+ select {
+ case <-max.C:
+ t.Fatal("Write took over 1s; expected 0.1s")
+ case err := <-ch:
+ if perr := parseWriteError(err); perr != nil {
+ t.Error(perr)
+ }
+ if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
+ t.Fatal(err)
+ }
}
- return len(p), nil
}
func TestVariousDeadlines1Proc(t *testing.T) {
@@ -420,36 +722,57 @@ func TestVariousDeadlines4Proc(t *testing.T) {
testVariousDeadlines(t, 4)
}
+type neverEnding byte
+
+func (b neverEnding) Read(p []byte) (int, error) {
+ for i := range p {
+ p[i] = byte(b)
+ }
+ return len(p), nil
+}
+
func testVariousDeadlines(t *testing.T, maxProcs int) {
switch runtime.GOOS {
case "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
+ t.Skipf("not supported on %s", runtime.GOOS)
}
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs))
- ln := newLocalListener(t)
- defer ln.Close()
- acceptc := make(chan error, 1)
- // The server, with no timeouts of its own, sending bytes to clients
- // as fast as it can.
- servec := make(chan copyRes)
- go func() {
+ type result struct {
+ n int64
+ err error
+ d time.Duration
+ }
+
+ ch := make(chan error, 1)
+ pasvch := make(chan result)
+ handler := func(ls *localServer, ln Listener) {
for {
c, err := ln.Accept()
if err != nil {
- acceptc <- err
+ ch <- err
return
}
+ // The server, with no timeouts of its own,
+ // sending bytes to clients as fast as it can.
go func() {
t0 := time.Now()
n, err := io.Copy(c, neverEnding('a'))
- d := time.Since(t0)
+ dt := time.Since(t0)
c.Close()
- servec <- copyRes{n, err, d}
+ pasvch <- result{n, err, dt}
}()
}
- }()
+ }
+ ls, err := newLocalServer("tcp")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ls.teardown()
+ if err := ls.buildup(handler); err != nil {
+ t.Fatal(err)
+ }
for _, timeout := range []time.Duration{
1 * time.Nanosecond,
@@ -483,236 +806,133 @@ func testVariousDeadlines(t *testing.T, maxProcs int) {
name := fmt.Sprintf("%v run %d/%d", timeout, run+1, numRuns)
t.Log(name)
- c, err := Dial("tcp", ln.Addr().String())
+ c, err := Dial(ls.Listener.Addr().Network(), ls.Listener.Addr().String())
if err != nil {
- t.Fatalf("Dial: %v", err)
+ t.Fatal(err)
}
- clientc := make(chan copyRes)
- go func() {
- t0 := time.Now()
- c.SetDeadline(t0.Add(timeout))
- n, err := io.Copy(ioutil.Discard, c)
- d := time.Since(t0)
- c.Close()
- clientc <- copyRes{n, err, d}
- }()
tooLong := 5 * time.Second
+ max := time.NewTimer(tooLong)
+ defer max.Stop()
+ actvch := make(chan result)
+ go func() {
+ t0 := time.Now()
+ if err := c.SetDeadline(t0.Add(timeout)); err != nil {
+ t.Error(err)
+ }
+ n, err := io.Copy(ioutil.Discard, c)
+ dt := time.Since(t0)
+ c.Close()
+ actvch <- result{n, err, dt}
+ }()
+
select {
- case res := <-clientc:
- if isTimeout(res.err) {
+ case res := <-actvch:
+ if nerr, ok := res.err.(Error); ok && nerr.Timeout() {
t.Logf("for %v, good client timeout after %v, reading %d bytes", name, res.d, res.n)
} else {
- t.Fatalf("for %v: client Copy = %d, %v (want timeout)", name, res.n, res.err)
+ t.Fatalf("for %v, client Copy = %d, %v; want timeout", name, res.n, res.err)
}
- case <-time.After(tooLong):
- t.Fatalf("for %v: timeout (%v) waiting for client to timeout (%v) reading", name, tooLong, timeout)
+ case <-max.C:
+ t.Fatalf("for %v, timeout (%v) waiting for client to timeout (%v) reading", name, tooLong, timeout)
}
select {
- case res := <-servec:
- t.Logf("for %v: server in %v wrote %d, %v", name, res.d, res.n, res.err)
- case err := <-acceptc:
- t.Fatalf("for %v: server Accept = %v", name, err)
- case <-time.After(tooLong):
+ case res := <-pasvch:
+ t.Logf("for %v, server in %v wrote %d: %v", name, res.d, res.n, res.err)
+ case err := <-ch:
+ t.Fatalf("for %v, Accept = %v", name, err)
+ case <-max.C:
t.Fatalf("for %v, timeout waiting for server to finish writing", name)
}
}
}
}
-// TestReadDeadlineDataAvailable tests that read deadlines work, even
-// if there's data ready to be read.
-func TestReadDeadlineDataAvailable(t *testing.T) {
+// TestReadWriteProlongedTimeout tests concurrent deadline
+// modification. Known to cause data races in the past.
+func TestReadWriteProlongedTimeout(t *testing.T) {
switch runtime.GOOS {
case "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
+ t.Skipf("not supported on %s", runtime.GOOS)
}
- ln := newLocalListener(t)
- defer ln.Close()
-
- servec := make(chan copyRes)
- const msg = "data client shouldn't read, even though it'll be waiting"
- go func() {
+ handler := func(ls *localServer, ln Listener) {
c, err := ln.Accept()
if err != nil {
- t.Errorf("Accept: %v", err)
+ t.Error(err)
return
}
defer c.Close()
- n, err := c.Write([]byte(msg))
- servec <- copyRes{n: int64(n), err: err}
- }()
- c, err := Dial("tcp", ln.Addr().String())
- if err != nil {
- t.Fatalf("Dial: %v", err)
- }
- defer c.Close()
- if res := <-servec; res.err != nil || res.n != int64(len(msg)) {
- t.Fatalf("unexpected server Write: n=%d, err=%v; want n=%d, err=nil", res.n, res.err, len(msg))
- }
- c.SetReadDeadline(time.Now().Add(-5 * time.Second)) // in the psat.
- buf := make([]byte, len(msg)/2)
- n, err := c.Read(buf)
- if n > 0 || !isTimeout(err) {
- t.Fatalf("client read = %d (%q) err=%v; want 0, timeout", n, buf[:n], err)
- }
-}
-
-// TestWriteDeadlineBufferAvailable tests that write deadlines work, even
-// if there's buffer space available to write.
-func TestWriteDeadlineBufferAvailable(t *testing.T) {
- switch runtime.GOOS {
- case "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
- }
-
- ln := newLocalListener(t)
- defer ln.Close()
-
- servec := make(chan copyRes)
- go func() {
- c, err := ln.Accept()
- if err != nil {
- t.Errorf("Accept: %v", err)
- return
- }
- defer c.Close()
- c.SetWriteDeadline(time.Now().Add(-5 * time.Second)) // in the past
- n, err := c.Write([]byte{'x'})
- servec <- copyRes{n: int64(n), err: err}
- }()
-
- c, err := Dial("tcp", ln.Addr().String())
- if err != nil {
- t.Fatalf("Dial: %v", err)
- }
- defer c.Close()
- res := <-servec
- if res.n != 0 {
- t.Errorf("Write = %d; want 0", res.n)
- }
- if !isTimeout(res.err) {
- t.Errorf("Write error = %v; want timeout", res.err)
- }
-}
-
-// TestAcceptDeadlineConnectionAvailable tests that accept deadlines work, even
-// if there's incoming connections available.
-func TestAcceptDeadlineConnectionAvailable(t *testing.T) {
- switch runtime.GOOS {
- case "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
- }
-
- ln := newLocalListener(t).(*TCPListener)
- defer ln.Close()
-
- go func() {
- c, err := Dial("tcp", ln.Addr().String())
- if err != nil {
- t.Errorf("Dial: %v", err)
- return
- }
- defer c.Close()
- var buf [1]byte
- c.Read(buf[:]) // block until the connection or listener is closed
- }()
- time.Sleep(10 * time.Millisecond)
- ln.SetDeadline(time.Now().Add(-5 * time.Second)) // in the past
- c, err := ln.Accept()
- if err == nil {
- defer c.Close()
- }
- if !isTimeout(err) {
- t.Fatalf("Accept: got %v; want timeout", err)
- }
-}
-
-// TestConnectDeadlineInThePast tests that connect deadlines work, even
-// if the connection can be established w/o blocking.
-func TestConnectDeadlineInThePast(t *testing.T) {
- switch runtime.GOOS {
- case "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
- }
-
- ln := newLocalListener(t).(*TCPListener)
- defer ln.Close()
-
- go func() {
- c, err := ln.Accept()
- if err == nil {
- defer c.Close()
- }
- }()
- time.Sleep(10 * time.Millisecond)
- c, err := DialTimeout("tcp", ln.Addr().String(), -5*time.Second) // in the past
- if err == nil {
- defer c.Close()
- }
- if !isTimeout(err) {
- t.Fatalf("DialTimeout: got %v; want timeout", err)
- }
-}
-
-// TestProlongTimeout tests concurrent deadline modification.
-// Known to cause data races in the past.
-func TestProlongTimeout(t *testing.T) {
- switch runtime.GOOS {
- case "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
- }
-
- ln := newLocalListener(t)
- defer ln.Close()
- connected := make(chan bool)
- go func() {
- s, err := ln.Accept()
- connected <- true
- if err != nil {
- t.Errorf("ln.Accept: %v", err)
- return
- }
- defer s.Close()
- s.SetDeadline(time.Now().Add(time.Hour))
+ var wg sync.WaitGroup
+ wg.Add(2)
go func() {
- var buf [4096]byte
+ defer wg.Done()
+ var b [1]byte
for {
- _, err := s.Write(buf[:])
- if err != nil {
- break
+ if err := c.SetReadDeadline(time.Now().Add(time.Hour)); err != nil {
+ if perr := parseCommonError(err); perr != nil {
+ t.Error(perr)
+ }
+ t.Error(err)
+ return
+ }
+ if _, err := c.Read(b[:]); err != nil {
+ if perr := parseReadError(err); perr != nil {
+ t.Error(perr)
+ }
+ return
}
- s.SetDeadline(time.Now().Add(time.Hour))
}
}()
- buf := make([]byte, 1)
- for {
- _, err := s.Read(buf)
- if err != nil {
- break
+ go func() {
+ defer wg.Done()
+ var b [1]byte
+ for {
+ if err := c.SetWriteDeadline(time.Now().Add(time.Hour)); err != nil {
+ if perr := parseCommonError(err); perr != nil {
+ t.Error(perr)
+ }
+ t.Error(err)
+ return
+ }
+ if _, err := c.Write(b[:]); err != nil {
+ if perr := parseWriteError(err); perr != nil {
+ t.Error(perr)
+ }
+ return
+ }
}
- s.SetDeadline(time.Now().Add(time.Hour))
- }
- }()
- c, err := Dial("tcp", ln.Addr().String())
+ }()
+ wg.Wait()
+ }
+ ls, err := newLocalServer("tcp")
if err != nil {
- t.Fatalf("DialTCP: %v", err)
+ t.Fatal(err)
+ }
+ defer ls.teardown()
+ if err := ls.buildup(handler); err != nil {
+ t.Fatal(err)
+ }
+
+ c, err := Dial(ls.Listener.Addr().Network(), ls.Listener.Addr().String())
+ if err != nil {
+ t.Fatal(err)
}
defer c.Close()
- <-connected
- for i := 0; i < 1024; i++ {
- var buf [1]byte
- c.Write(buf[:])
+
+ var b [1]byte
+ for i := 0; i < 1000; i++ {
+ c.Write(b[:])
+ c.Read(b[:])
}
}
-func TestDeadlineRace(t *testing.T) {
+func TestReadWriteDeadlineRace(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
+ t.Skipf("not supported on %s", runtime.GOOS)
}
N := 1000
@@ -720,28 +940,54 @@ func TestDeadlineRace(t *testing.T) {
N = 50
}
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
- ln := newLocalListener(t)
- defer ln.Close()
- c, err := Dial("tcp", ln.Addr().String())
+
+ ln, err := newLocalListener("tcp")
if err != nil {
- t.Fatalf("Dial: %v", err)
+ t.Fatal(err)
+ }
+ defer ln.Close()
+
+ c, err := Dial(ln.Addr().Network(), ln.Addr().String())
+ if err != nil {
+ t.Fatal(err)
}
defer c.Close()
- done := make(chan bool)
+
+ var wg sync.WaitGroup
+ wg.Add(3)
go func() {
- t := time.NewTicker(2 * time.Microsecond).C
+ defer wg.Done()
+ tic := time.NewTicker(2 * time.Microsecond)
+ defer tic.Stop()
for i := 0; i < N; i++ {
- if err := c.SetDeadline(time.Now().Add(2 * time.Microsecond)); err != nil {
+ if err := c.SetReadDeadline(time.Now().Add(2 * time.Microsecond)); err != nil {
+ if perr := parseCommonError(err); perr != nil {
+ t.Error(perr)
+ }
break
}
- <-t
+ if err := c.SetWriteDeadline(time.Now().Add(2 * time.Microsecond)); err != nil {
+ if perr := parseCommonError(err); perr != nil {
+ t.Error(perr)
+ }
+ break
+ }
+ <-tic.C
}
- done <- true
}()
- var buf [1]byte
- for i := 0; i < N; i++ {
- c.Read(buf[:]) // ignore possible timeout errors
- }
- c.Close()
- <-done
+ go func() {
+ defer wg.Done()
+ var b [1]byte
+ for i := 0; i < N; i++ {
+ c.Read(b[:]) // ignore possible timeout errors
+ }
+ }()
+ go func() {
+ defer wg.Done()
+ var b [1]byte
+ for i := 0; i < N; i++ {
+ c.Write(b[:]) // ignore possible timeout errors
+ }
+ }()
+ wg.Wait() // wait for tester goroutine to stop
}
diff --git a/src/net/udp_test.go b/src/net/udp_test.go
index 2be2c319a7..2213468e79 100644
--- a/src/net/udp_test.go
+++ b/src/net/udp_test.go
@@ -7,78 +7,63 @@ package net
import (
"reflect"
"runtime"
- "strings"
"testing"
"time"
)
-func TestResolveUDPAddr(t *testing.T) {
- for _, tt := range resolveTCPAddrTests {
- net := strings.Replace(tt.net, "tcp", "udp", -1)
- addr, err := ResolveUDPAddr(net, tt.litAddrOrName)
- if err != tt.err {
- t.Fatalf("ResolveUDPAddr(%q, %q) failed: %v", net, tt.litAddrOrName, err)
- }
- if !reflect.DeepEqual(addr, (*UDPAddr)(tt.addr)) {
- t.Fatalf("ResolveUDPAddr(%q, %q) = %#v, want %#v", net, tt.litAddrOrName, addr, tt.addr)
- }
- if err == nil {
- str := addr.String()
- addr1, err := ResolveUDPAddr(net, str)
- if err != nil {
- t.Fatalf("ResolveUDPAddr(%q, %q) [from %q]: %v", net, str, tt.litAddrOrName, err)
- }
- if !reflect.DeepEqual(addr1, addr) {
- t.Fatalf("ResolveUDPAddr(%q, %q) [from %q] = %#v, want %#v", net, str, tt.litAddrOrName, addr1, addr)
- }
- }
- }
+type resolveUDPAddrTest struct {
+ network string
+ litAddrOrName string
+ addr *UDPAddr
+ err error
}
-func TestReadFromUDP(t *testing.T) {
- switch runtime.GOOS {
- case "nacl", "plan9":
- t.Skipf("skipping test on %q, see issue 8916", runtime.GOOS)
- }
+var resolveUDPAddrTests = []resolveUDPAddrTest{
+ {"udp", "127.0.0.1:0", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil},
+ {"udp4", "127.0.0.1:65535", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 65535}, nil},
- ra, err := ResolveUDPAddr("udp", "127.0.0.1:7")
- if err != nil {
- t.Fatal(err)
- }
+ {"udp", "[::1]:0", &UDPAddr{IP: ParseIP("::1"), Port: 0}, nil},
+ {"udp6", "[::1]:65535", &UDPAddr{IP: ParseIP("::1"), Port: 65535}, nil},
- la, err := ResolveUDPAddr("udp", "127.0.0.1:0")
- if err != nil {
- t.Fatal(err)
- }
+ {"udp", "[::1%en0]:1", &UDPAddr{IP: ParseIP("::1"), Port: 1, Zone: "en0"}, nil},
+ {"udp6", "[::1%911]:2", &UDPAddr{IP: ParseIP("::1"), Port: 2, Zone: "911"}, nil},
- c, err := ListenUDP("udp", la)
- if err != nil {
- t.Fatal(err)
- }
- defer c.Close()
+ {"", "127.0.0.1:0", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, // Go 1.0 behavior
+ {"", "[::1]:0", &UDPAddr{IP: ParseIP("::1"), Port: 0}, nil}, // Go 1.0 behavior
- _, err = c.WriteToUDP([]byte("a"), ra)
- if err != nil {
- t.Fatal(err)
- }
+ {"udp", ":12345", &UDPAddr{Port: 12345}, nil},
- err = c.SetDeadline(time.Now().Add(100 * time.Millisecond))
- if err != nil {
- t.Fatal(err)
- }
- b := make([]byte, 1)
- _, _, err = c.ReadFromUDP(b)
- if err == nil {
- t.Fatal("ReadFromUDP should fail")
- } else if !isTimeout(err) {
- t.Fatal(err)
+ {"http", "127.0.0.1:0", nil, UnknownNetworkError("http")},
+}
+
+func TestResolveUDPAddr(t *testing.T) {
+ origTestHookLookupIP := testHookLookupIP
+ defer func() { testHookLookupIP = origTestHookLookupIP }()
+ testHookLookupIP = lookupLocalhost
+
+ for i, tt := range resolveUDPAddrTests {
+ addr, err := ResolveUDPAddr(tt.network, tt.litAddrOrName)
+ if err != tt.err {
+ t.Errorf("#%d: %v", i, err)
+ } else if !reflect.DeepEqual(addr, tt.addr) {
+ t.Errorf("#%d: got %#v; want %#v", i, addr, tt.addr)
+ }
+ if err != nil {
+ continue
+ }
+ rtaddr, err := ResolveUDPAddr(addr.Network(), addr.String())
+ if err != nil {
+ t.Errorf("#%d: %v", i, err)
+ } else if !reflect.DeepEqual(rtaddr, addr) {
+ t.Errorf("#%d: got %#v; want %#v", i, rtaddr, addr)
+ }
}
}
func TestWriteToUDP(t *testing.T) {
switch runtime.GOOS {
case "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
+ t.Skipf("not supported on %s", runtime.GOOS)
}
c, err := ListenPacket("udp", "127.0.0.1:0")
@@ -103,35 +88,33 @@ func testWriteToConn(t *testing.T, raddr string) {
t.Fatal(err)
}
- _, err = c.(*UDPConn).WriteToUDP([]byte("Connection-oriented mode socket"), ra)
+ b := []byte("CONNECTED-MODE SOCKET")
+ _, err = c.(*UDPConn).WriteToUDP(b, ra)
if err == nil {
- t.Fatal("WriteToUDP should fail")
+ t.Fatal("should fail")
}
if err != nil && err.(*OpError).Err != ErrWriteToConnected {
- t.Fatalf("WriteToUDP should fail as ErrWriteToConnected: %v", err)
+ t.Fatalf("should fail as ErrWriteToConnected: %v", err)
}
-
- _, err = c.(*UDPConn).WriteTo([]byte("Connection-oriented mode socket"), ra)
+ _, err = c.(*UDPConn).WriteTo(b, ra)
if err == nil {
- t.Fatal("WriteTo should fail")
+ t.Fatal("should fail")
}
if err != nil && err.(*OpError).Err != ErrWriteToConnected {
- t.Fatalf("WriteTo should fail as ErrWriteToConnected: %v", err)
+ t.Fatalf("should fail as ErrWriteToConnected: %v", err)
}
-
- _, err = c.Write([]byte("Connection-oriented mode socket"))
+ _, err = c.Write(b)
if err != nil {
t.Fatal(err)
}
-
- _, _, err = c.(*UDPConn).WriteMsgUDP([]byte("Connection-oriented mode socket"), nil, ra)
+ _, _, err = c.(*UDPConn).WriteMsgUDP(b, nil, ra)
if err == nil {
- t.Fatal("WriteMsgUDP should fail")
+ t.Fatal("should fail")
}
if err != nil && err.(*OpError).Err != ErrWriteToConnected {
- t.Fatalf("WriteMsgUDP should fail as ErrWriteToConnected: %v", err)
+ t.Fatalf("should fail as ErrWriteToConnected: %v", err)
}
- _, _, err = c.(*UDPConn).WriteMsgUDP([]byte("Connection-oriented mode socket"), nil, nil)
+ _, _, err = c.(*UDPConn).WriteMsgUDP(b, nil, nil)
switch runtime.GOOS {
case "nacl", "windows": // see golang.org/issue/9252
t.Skipf("not implemented yet on %s", runtime.GOOS)
@@ -154,29 +137,27 @@ func testWriteToPacketConn(t *testing.T, raddr string) {
t.Fatal(err)
}
- _, err = c.(*UDPConn).WriteToUDP([]byte("Connection-less mode socket"), ra)
+ b := []byte("UNCONNECTED-MODE SOCKET")
+ _, err = c.(*UDPConn).WriteToUDP(b, ra)
if err != nil {
t.Fatal(err)
}
-
- _, err = c.WriteTo([]byte("Connection-less mode socket"), ra)
+ _, err = c.WriteTo(b, ra)
if err != nil {
t.Fatal(err)
}
-
- _, err = c.(*UDPConn).Write([]byte("Connection-less mode socket"))
+ _, err = c.(*UDPConn).Write(b)
if err == nil {
- t.Fatal("Write should fail")
+ t.Fatal("should fail")
}
-
- _, _, err = c.(*UDPConn).WriteMsgUDP([]byte("Connection-less mode socket"), nil, nil)
+ _, _, err = c.(*UDPConn).WriteMsgUDP(b, nil, nil)
if err == nil {
- t.Fatal("WriteMsgUDP should fail")
+ t.Fatal("should fail")
}
if err != nil && err.(*OpError).Err != errMissingAddress {
- t.Fatalf("WriteMsgUDP should fail as errMissingAddress: %v", err)
+ t.Fatalf("should fail as errMissingAddress: %v", err)
}
- _, _, err = c.(*UDPConn).WriteMsgUDP([]byte("Connection-less mode socket"), nil, ra)
+ _, _, err = c.(*UDPConn).WriteMsgUDP(b, nil, ra)
switch runtime.GOOS {
case "nacl", "windows": // see golang.org/issue/9252
t.Skipf("not implemented yet on %s", runtime.GOOS)
@@ -198,13 +179,13 @@ var udpConnLocalNameTests = []struct {
func TestUDPConnLocalName(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
+ t.Skip("avoid external network")
}
for _, tt := range udpConnLocalNameTests {
c, err := ListenUDP(tt.net, tt.laddr)
if err != nil {
- t.Fatalf("ListenUDP failed: %v", err)
+ t.Fatal(err)
}
defer c.Close()
la := c.LocalAddr()
@@ -218,7 +199,7 @@ func TestUDPConnLocalAndRemoteNames(t *testing.T) {
for _, laddr := range []string{"", "127.0.0.1:0"} {
c1, err := ListenPacket("udp", "127.0.0.1:0")
if err != nil {
- t.Fatalf("ListenUDP failed: %v", err)
+ t.Fatal(err)
}
defer c1.Close()
@@ -226,12 +207,12 @@ func TestUDPConnLocalAndRemoteNames(t *testing.T) {
if laddr != "" {
var err error
if la, err = ResolveUDPAddr("udp", laddr); err != nil {
- t.Fatalf("ResolveUDPAddr failed: %v", err)
+ t.Fatal(err)
}
}
c2, err := DialUDP("udp", la, c1.LocalAddr().(*UDPAddr))
if err != nil {
- t.Fatalf("DialUDP failed: %v", err)
+ t.Fatal(err)
}
defer c2.Close()
@@ -254,7 +235,7 @@ func TestUDPConnLocalAndRemoteNames(t *testing.T) {
func TestIPv6LinkLocalUnicastUDP(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
+ t.Skip("avoid external network")
}
if !supportsIPv6 {
t.Skip("ipv6 is not supported")
@@ -297,7 +278,7 @@ func TestIPv6LinkLocalUnicastUDP(t *testing.T) {
if err != nil {
// It might return "LookupHost returned no
// suitable address" error on some platforms.
- t.Logf("ListenPacket failed: %v", err)
+ t.Log(err)
continue
}
defer c1.Close()
@@ -307,7 +288,7 @@ func TestIPv6LinkLocalUnicastUDP(t *testing.T) {
c2, err := Dial(tt.net, c1.LocalAddr().String())
if err != nil {
- t.Fatalf("Dial failed: %v", err)
+ t.Fatal(err)
}
defer c2.Close()
if la, ok := c2.LocalAddr().(*UDPAddr); !ok || !tt.nameLookup && la.Zone == "" {
@@ -318,11 +299,11 @@ func TestIPv6LinkLocalUnicastUDP(t *testing.T) {
}
if _, err := c2.Write([]byte("UDP OVER IPV6 LINKLOCAL TEST")); err != nil {
- t.Fatalf("Conn.Write failed: %v", err)
+ t.Fatal(err)
}
b := make([]byte, 32)
if _, from, err := c1.ReadFrom(b); err != nil {
- t.Fatalf("PacketConn.ReadFrom failed: %v", err)
+ t.Fatal(err)
} else {
if ra, ok := from.(*UDPAddr); !ok || !tt.nameLookup && ra.Zone == "" {
t.Fatalf("got %v; expected a proper address with zone identifier", ra)
@@ -330,3 +311,77 @@ func TestIPv6LinkLocalUnicastUDP(t *testing.T) {
}
}
}
+
+func TestUDPZeroBytePayload(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+
+ c, err := newLocalPacketListener("udp")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ for _, genericRead := range []bool{false, true} {
+ n, err := c.WriteTo(nil, c.LocalAddr())
+ if err != nil {
+ t.Fatal(err)
+ }
+ if n != 0 {
+ t.Errorf("got %d; want 0", n)
+ }
+ c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+ var b [1]byte
+ if genericRead {
+ _, err = c.(Conn).Read(b[:])
+ } else {
+ _, _, err = c.ReadFrom(b[:])
+ }
+ switch err {
+ case nil: // ReadFrom succeeds
+ default: // Read may timeout, it depends on the platform
+ if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
+ t.Fatal(err)
+ }
+ }
+ }
+}
+
+func TestUDPZeroByteBuffer(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+
+ c, err := newLocalPacketListener("udp")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ b := []byte("UDP ZERO BYTE BUFFER TEST")
+ for _, genericRead := range []bool{false, true} {
+ n, err := c.WriteTo(b, c.LocalAddr())
+ if err != nil {
+ t.Fatal(err)
+ }
+ if n != len(b) {
+ t.Errorf("got %d; want %d", n, len(b))
+ }
+ c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+ if genericRead {
+ _, err = c.(Conn).Read(nil)
+ } else {
+ _, _, err = c.ReadFrom(nil)
+ }
+ switch err {
+ case nil: // ReadFrom succeeds
+ default: // Read may timeout, it depends on the platform
+ if nerr, ok := err.(Error); (!ok || !nerr.Timeout()) && runtime.GOOS != "windows" { // Windows retruns WSAEMSGSIZ
+ t.Fatal(err)
+ }
+ }
+ }
+}
diff --git a/src/net/udpsock.go b/src/net/udpsock.go
index 532f7d5080..5291a3e112 100644
--- a/src/net/udpsock.go
+++ b/src/net/udpsock.go
@@ -32,13 +32,6 @@ func (a *UDPAddr) isWildcard() bool {
return a.IP.IsUnspecified()
}
-func (a *UDPAddr) toAddr() Addr {
- if a == nil {
- return nil
- }
- return a
-}
-
// ResolveUDPAddr parses addr as a UDP address of the form "host:port"
// or "[ipv6-host%zone]:port" and resolves a pair of domain name and
// port name on the network net, which must be "udp", "udp4" or
@@ -53,9 +46,9 @@ func ResolveUDPAddr(net, addr string) (*UDPAddr, error) {
default:
return nil, UnknownNetworkError(net)
}
- a, err := resolveInternetAddr(net, addr, noDeadline)
+ addrs, err := internetAddrList(net, addr, noDeadline)
if err != nil {
return nil, err
}
- return a.toAddr().(*UDPAddr), nil
+ return addrs.first(isIPv4).(*UDPAddr), nil
}
diff --git a/src/net/udpsock_plan9.go b/src/net/udpsock_plan9.go
index 269272632a..949f666d74 100644
--- a/src/net/udpsock_plan9.go
+++ b/src/net/udpsock_plan9.go
@@ -33,10 +33,10 @@ func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) {
buf := make([]byte, udpHeaderSize+len(b))
m, err := c.fd.data.Read(buf)
if err != nil {
- return
+ return 0, nil, &OpError{Op: "read", Net: c.fd.dir, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
}
if m < udpHeaderSize {
- return 0, nil, errors.New("short read reading UDP header")
+ return 0, nil, &OpError{Op: "read", Net: c.fd.dir, Source: c.fd.laddr, Addr: c.fd.raddr, Err: errors.New("short read reading UDP header")}
}
buf = buf[:m]
@@ -59,7 +59,7 @@ func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) {
// flags that were set on the packet and the source address of the
// packet.
func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr, err error) {
- return 0, 0, 0, nil, syscall.EPLAN9
+ return 0, 0, 0, nil, &OpError{Op: "read", Net: c.fd.dir, Source: c.fd.laddr, Addr: c.fd.raddr, Err: syscall.EPLAN9}
}
// WriteToUDP writes a UDP packet to addr via c, copying the payload
@@ -74,7 +74,7 @@ func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) {
return 0, syscall.EINVAL
}
if addr == nil {
- return 0, &OpError{Op: "write", Net: c.fd.dir, Addr: nil, Err: errMissingAddress}
+ return 0, &OpError{Op: "write", Net: c.fd.dir, Source: c.fd.laddr, Addr: addr, Err: errMissingAddress}
}
h := new(udpHeader)
h.raddr = addr.IP.To16()
@@ -86,7 +86,10 @@ func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) {
buf := make([]byte, udpHeaderSize+len(b))
i := copy(buf, h.Bytes())
copy(buf[i:], b)
- return c.fd.data.Write(buf)
+ if _, err := c.fd.data.Write(buf); err != nil {
+ return 0, &OpError{Op: "write", Net: c.fd.dir, Source: c.fd.laddr, Addr: addr, Err: err}
+ }
+ return len(b), nil
}
// WriteTo implements the PacketConn WriteTo method.
@@ -96,7 +99,7 @@ func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) {
}
a, ok := addr.(*UDPAddr)
if !ok {
- return 0, &OpError{"write", c.fd.dir, addr, syscall.EINVAL}
+ return 0, &OpError{Op: "write", Net: c.fd.dir, Source: c.fd.laddr, Addr: addr, Err: syscall.EINVAL}
}
return c.WriteToUDP(b, a)
}
@@ -107,7 +110,7 @@ func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) {
// out-of-band data is copied from oob. It returns the number of
// payload and out-of-band bytes written.
func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err error) {
- return 0, 0, syscall.EPLAN9
+ return 0, 0, &OpError{Op: "write", Net: c.fd.dir, Source: c.fd.laddr, Addr: addr, Err: syscall.EPLAN9}
}
// DialUDP connects to the remote address raddr on the network net,
@@ -124,10 +127,10 @@ func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (*UDPConn, e
switch net {
case "udp", "udp4", "udp6":
default:
- return nil, UnknownNetworkError(net)
+ return nil, &OpError{Op: "dial", Net: net, Source: laddr, Addr: raddr, Err: UnknownNetworkError(net)}
}
if raddr == nil {
- return nil, &OpError{"dial", net, nil, errMissingAddress}
+ return nil, &OpError{Op: "dial", Net: net, Source: laddr, Addr: raddr, Err: errMissingAddress}
}
fd, err := dialPlan9(net, laddr, raddr)
if err != nil {
@@ -175,7 +178,7 @@ func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
switch net {
case "udp", "udp4", "udp6":
default:
- return nil, UnknownNetworkError(net)
+ return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr, Err: UnknownNetworkError(net)}
}
if laddr == nil {
laddr = &UDPAddr{}
@@ -186,11 +189,11 @@ func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
}
_, err = l.ctl.WriteString("headers")
if err != nil {
- return nil, err
+ return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr, Err: err}
}
l.data, err = os.OpenFile(l.dir+"/data", os.O_RDWR, 0)
if err != nil {
- return nil, err
+ return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr, Err: err}
}
fd, err := l.netFD()
return newUDPConn(fd), err
@@ -201,5 +204,5 @@ func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
// interface to join. ListenMulticastUDP uses default multicast
// interface if ifi is nil.
func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) {
- return nil, syscall.EPLAN9
+ return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: gaddr, Err: syscall.EPLAN9}
}
diff --git a/src/net/udpsock_posix.go b/src/net/udpsock_posix.go
index 9733e7b833..2e43068be3 100644
--- a/src/net/udpsock_posix.go
+++ b/src/net/udpsock_posix.go
@@ -53,10 +53,11 @@ func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{conn{fd}} }
// ReadFromUDP can be made to time out and return an error with
// Timeout() == true after a fixed time limit; see SetDeadline and
// SetReadDeadline.
-func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) {
+func (c *UDPConn) ReadFromUDP(b []byte) (int, *UDPAddr, error) {
if !c.ok() {
return 0, nil, syscall.EINVAL
}
+ var addr *UDPAddr
n, sa, err := c.fd.readFrom(b)
switch sa := sa.(type) {
case *syscall.SockaddrInet4:
@@ -64,7 +65,10 @@ func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) {
case *syscall.SockaddrInet6:
addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))}
}
- return
+ if err != nil {
+ err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+ }
+ return n, addr, err
}
// ReadFrom implements the PacketConn ReadFrom method.
@@ -73,7 +77,10 @@ func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) {
return 0, nil, syscall.EINVAL
}
n, addr, err := c.ReadFromUDP(b)
- return n, addr.toAddr(), err
+ if addr == nil {
+ return n, nil, err
+ }
+ return n, addr, err
}
// ReadMsgUDP reads a packet from c, copying the payload into b and
@@ -93,6 +100,9 @@ func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr,
case *syscall.SockaddrInet6:
addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))}
}
+ if err != nil {
+ err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+ }
return
}
@@ -108,16 +118,20 @@ func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) {
return 0, syscall.EINVAL
}
if c.fd.isConnected {
- return 0, &OpError{"write", c.fd.net, addr, ErrWriteToConnected}
+ return 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: ErrWriteToConnected}
}
if addr == nil {
- return 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress}
+ return 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: errMissingAddress}
}
sa, err := addr.sockaddr(c.fd.family)
if err != nil {
- return 0, &OpError{"write", c.fd.net, addr, err}
+ return 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: err}
}
- return c.fd.writeTo(b, sa)
+ n, err := c.fd.writeTo(b, sa)
+ if err != nil {
+ err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: err}
+ }
+ return n, err
}
// WriteTo implements the PacketConn WriteTo method.
@@ -127,7 +141,7 @@ func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) {
}
a, ok := addr.(*UDPAddr)
if !ok {
- return 0, &OpError{"write", c.fd.net, addr, syscall.EINVAL}
+ return 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: syscall.EINVAL}
}
return c.WriteToUDP(b, a)
}
@@ -142,16 +156,21 @@ func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err er
return 0, 0, syscall.EINVAL
}
if c.fd.isConnected && addr != nil {
- return 0, 0, &OpError{"write", c.fd.net, addr, ErrWriteToConnected}
+ return 0, 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: ErrWriteToConnected}
}
if !c.fd.isConnected && addr == nil {
- return 0, 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress}
+ return 0, 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: errMissingAddress}
}
- sa, err := addr.sockaddr(c.fd.family)
+ var sa syscall.Sockaddr
+ sa, err = addr.sockaddr(c.fd.family)
if err != nil {
- return 0, 0, &OpError{"write", c.fd.net, addr, err}
+ return 0, 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: err}
}
- return c.fd.writeMsg(b, oob, sa)
+ n, oobn, err = c.fd.writeMsg(b, oob, sa)
+ if err != nil {
+ err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: err}
+ }
+ return
}
// DialUDP connects to the remote address raddr on the network net,
@@ -161,10 +180,10 @@ func DialUDP(net string, laddr, raddr *UDPAddr) (*UDPConn, error) {
switch net {
case "udp", "udp4", "udp6":
default:
- return nil, &OpError{Op: "dial", Net: net, Addr: raddr, Err: UnknownNetworkError(net)}
+ return nil, &OpError{Op: "dial", Net: net, Source: laddr, Addr: raddr, Err: UnknownNetworkError(net)}
}
if raddr == nil {
- return nil, &OpError{Op: "dial", Net: net, Addr: nil, Err: errMissingAddress}
+ return nil, &OpError{Op: "dial", Net: net, Source: laddr, Addr: raddr, Err: errMissingAddress}
}
return dialUDP(net, laddr, raddr, noDeadline)
}
@@ -172,7 +191,7 @@ func DialUDP(net string, laddr, raddr *UDPAddr) (*UDPConn, error) {
func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (*UDPConn, error) {
fd, err := internetSocket(net, laddr, raddr, deadline, syscall.SOCK_DGRAM, 0, "dial")
if err != nil {
- return nil, &OpError{Op: "dial", Net: net, Addr: raddr, Err: err}
+ return nil, &OpError{Op: "dial", Net: net, Source: laddr, Addr: raddr, Err: err}
}
return newUDPConn(fd), nil
}
@@ -188,14 +207,14 @@ func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
switch net {
case "udp", "udp4", "udp6":
default:
- return nil, &OpError{Op: "listen", Net: net, Addr: laddr, Err: UnknownNetworkError(net)}
+ return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr, Err: UnknownNetworkError(net)}
}
if laddr == nil {
laddr = &UDPAddr{}
}
fd, err := internetSocket(net, laddr, nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen")
if err != nil {
- return nil, &OpError{Op: "listen", Net: net, Addr: laddr, Err: err}
+ return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr, Err: err}
}
return newUDPConn(fd), nil
}
@@ -208,25 +227,25 @@ func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, e
switch net {
case "udp", "udp4", "udp6":
default:
- return nil, &OpError{Op: "listen", Net: net, Addr: gaddr, Err: UnknownNetworkError(net)}
+ return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: gaddr, Err: UnknownNetworkError(net)}
}
if gaddr == nil || gaddr.IP == nil {
- return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: errMissingAddress}
+ return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: gaddr, Err: errMissingAddress}
}
fd, err := internetSocket(net, gaddr, nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen")
if err != nil {
- return nil, &OpError{Op: "listen", Net: net, Addr: gaddr, Err: err}
+ return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: gaddr, Err: err}
}
c := newUDPConn(fd)
if ip4 := gaddr.IP.To4(); ip4 != nil {
if err := listenIPv4MulticastUDP(c, ifi, ip4); err != nil {
c.Close()
- return nil, &OpError{Op: "listen", Net: net, Addr: &IPAddr{IP: ip4}, Err: err}
+ return nil, &OpError{Op: "listen", Net: net, Source: c.fd.laddr, Addr: &IPAddr{IP: ip4}, Err: err}
}
} else {
if err := listenIPv6MulticastUDP(c, ifi, gaddr.IP); err != nil {
c.Close()
- return nil, &OpError{Op: "listen", Net: net, Addr: &IPAddr{IP: gaddr.IP}, Err: err}
+ return nil, &OpError{Op: "listen", Net: net, Source: c.fd.laddr, Addr: &IPAddr{IP: gaddr.IP}, Err: err}
}
}
return c, nil
diff --git a/src/net/unicast_posix_test.go b/src/net/unicast_posix_test.go
deleted file mode 100644
index ab7ef40a75..0000000000
--- a/src/net/unicast_posix_test.go
+++ /dev/null
@@ -1,469 +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 !plan9
-
-package net
-
-import (
- "runtime"
- "syscall"
- "testing"
-)
-
-var listenerTests = []struct {
- net string
- laddr string
- ipv6 bool // test with underlying AF_INET6 socket
- wildcard bool // test with wildcard address
-}{
- {net: "tcp", laddr: "", wildcard: true},
- {net: "tcp", laddr: "0.0.0.0", wildcard: true},
- {net: "tcp", laddr: "[::ffff:0.0.0.0]", wildcard: true},
- {net: "tcp", laddr: "[::]", ipv6: true, wildcard: true},
-
- {net: "tcp", laddr: "127.0.0.1"},
- {net: "tcp", laddr: "[::ffff:127.0.0.1]"},
- {net: "tcp", laddr: "[::1]", ipv6: true},
-
- {net: "tcp4", laddr: "", wildcard: true},
- {net: "tcp4", laddr: "0.0.0.0", wildcard: true},
- {net: "tcp4", laddr: "[::ffff:0.0.0.0]", wildcard: true},
-
- {net: "tcp4", laddr: "127.0.0.1"},
- {net: "tcp4", laddr: "[::ffff:127.0.0.1]"},
-
- {net: "tcp6", laddr: "", ipv6: true, wildcard: true},
- {net: "tcp6", laddr: "[::]", ipv6: true, wildcard: true},
-
- {net: "tcp6", laddr: "[::1]", ipv6: true},
-}
-
-// TestTCPListener tests both single and double listen to a test
-// listener with same address family, same listening address and
-// same port.
-func TestTCPListener(t *testing.T) {
- switch runtime.GOOS {
- case "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
- }
-
- for _, tt := range listenerTests {
- if tt.wildcard && (testing.Short() || !*testExternal) {
- continue
- }
- if tt.ipv6 && !supportsIPv6 {
- continue
- }
- l1, port := usableListenPort(t, tt.net, tt.laddr)
- checkFirstListener(t, tt.net, tt.laddr+":"+port, l1)
- l2, err := Listen(tt.net, tt.laddr+":"+port)
- checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2)
- l1.Close()
- }
-}
-
-// TestUDPListener tests both single and double listen to a test
-// listener with same address family, same listening address and
-// same port.
-func TestUDPListener(t *testing.T) {
- switch runtime.GOOS {
- case "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
- }
-
- toudpnet := func(net string) string {
- switch net {
- case "tcp":
- return "udp"
- case "tcp4":
- return "udp4"
- case "tcp6":
- return "udp6"
- }
- return "