diff options
Diffstat (limited to 'gcc/testsuite/go.test/test/chan')
-rw-r--r-- | gcc/testsuite/go.test/test/chan/doubleselect.go | 36 | ||||
-rw-r--r-- | gcc/testsuite/go.test/test/chan/nonblock.go | 153 | ||||
-rw-r--r-- | gcc/testsuite/go.test/test/chan/perm.go | 46 | ||||
-rw-r--r-- | gcc/testsuite/go.test/test/chan/select3.go | 39 | ||||
-rw-r--r-- | gcc/testsuite/go.test/test/chan/select4.go | 25 | ||||
-rw-r--r-- | gcc/testsuite/go.test/test/chan/select5.go | 482 | ||||
-rw-r--r-- | gcc/testsuite/go.test/test/chan/sendstmt.go | 37 |
7 files changed, 715 insertions, 103 deletions
diff --git a/gcc/testsuite/go.test/test/chan/doubleselect.go b/gcc/testsuite/go.test/test/chan/doubleselect.go index 592d2f54a43..3c7412ed6ab 100644 --- a/gcc/testsuite/go.test/test/chan/doubleselect.go +++ b/gcc/testsuite/go.test/test/chan/doubleselect.go @@ -21,6 +21,8 @@ var iterations *int = flag.Int("n", 100000, "number of iterations") func sender(n int, c1, c2, c3, c4 chan<- int) { defer close(c1) defer close(c2) + defer close(c3) + defer close(c4) for i := 0; i < n; i++ { select { @@ -35,26 +37,18 @@ func sender(n int, c1, c2, c3, c4 chan<- int) { // mux receives the values from sender and forwards them onto another channel. // It would be simplier to just have sender's four cases all be the same // channel, but this doesn't actually trigger the bug. -func mux(out chan<- int, in <-chan int) { - for { - v := <-in - if closed(in) { - close(out) - break - } +func mux(out chan<- int, in <-chan int, done chan<- bool) { + for v := range in { out <- v } + done <- true } // recver gets a steam of values from the four mux's and checks for duplicates. func recver(in <-chan int) { seen := make(map[int]bool) - for { - v := <-in - if closed(in) { - break - } + for v := range in { if _, ok := seen[v]; ok { println("got duplicate value: ", v) panic("fail") @@ -70,15 +64,23 @@ func main() { c2 := make(chan int) c3 := make(chan int) c4 := make(chan int) + done := make(chan bool) cmux := make(chan int) go sender(*iterations, c1, c2, c3, c4) - go mux(cmux, c1) - go mux(cmux, c2) - go mux(cmux, c3) - go mux(cmux, c4) + go mux(cmux, c1, done) + go mux(cmux, c2, done) + go mux(cmux, c3, done) + go mux(cmux, c4, done) + go func() { + <-done + <-done + <-done + <-done + close(cmux) + }() // We keep the recver because it might catch more bugs in the future. // However, the result of the bug linked to at the top is that we'll - // end up panicing with: "throw: bad g->status in ready". + // end up panicking with: "throw: bad g->status in ready". recver(cmux) print("PASS\n") } diff --git a/gcc/testsuite/go.test/test/chan/nonblock.go b/gcc/testsuite/go.test/test/chan/nonblock.go index 52f04bfb12f..33afb329165 100644 --- a/gcc/testsuite/go.test/test/chan/nonblock.go +++ b/gcc/testsuite/go.test/test/chan/nonblock.go @@ -76,7 +76,6 @@ func main() { var i64 int64 var b bool var s string - var ok bool var sync = make(chan bool) @@ -86,35 +85,45 @@ func main() { cb := make(chan bool, buffer) cs := make(chan string, buffer) - i32, ok = <-c32 - if ok { + select { + case i32 = <-c32: panic("blocked i32sender") + default: } - i64, ok = <-c64 - if ok { + select { + case i64 = <-c64: panic("blocked i64sender") + default: } - b, ok = <-cb - if ok { + select { + case b = <-cb: panic("blocked bsender") + default: } - s, ok = <-cs - if ok { + select { + case s = <-cs: panic("blocked ssender") + default: } go i32receiver(c32, sync) try := 0 - for !(c32 <- 123) { - try++ - if try > maxTries { - println("i32receiver buffer=", buffer) - panic("fail") + Send32: + for { + select { + case c32 <- 123: + break Send32 + default: + try++ + if try > maxTries { + println("i32receiver buffer=", buffer) + panic("fail") + } + sleep() } - sleep() } <-sync @@ -123,13 +132,19 @@ func main() { <-sync } try = 0 - for i32, ok = <-c32; !ok; i32, ok = <-c32 { - try++ - if try > maxTries { - println("i32sender buffer=", buffer) - panic("fail") + Recv32: + for { + select { + case i32 = <-c32: + break Recv32 + default: + try++ + if try > maxTries { + println("i32sender buffer=", buffer) + panic("fail") + } + sleep() } - sleep() } if i32 != 234 { panic("i32sender value") @@ -140,12 +155,18 @@ func main() { go i64receiver(c64, sync) try = 0 - for !(c64 <- 123456) { - try++ - if try > maxTries { - panic("i64receiver") + Send64: + for { + select { + case c64 <- 123456: + break Send64 + default: + try++ + if try > maxTries { + panic("i64receiver") + } + sleep() } - sleep() } <-sync @@ -154,12 +175,18 @@ func main() { <-sync } try = 0 - for i64, ok = <-c64; !ok; i64, ok = <-c64 { - try++ - if try > maxTries { - panic("i64sender") + Recv64: + for { + select { + case i64 = <-c64: + break Recv64 + default: + try++ + if try > maxTries { + panic("i64sender") + } + sleep() } - sleep() } if i64 != 234567 { panic("i64sender value") @@ -170,12 +197,18 @@ func main() { go breceiver(cb, sync) try = 0 - for !(cb <- true) { - try++ - if try > maxTries { - panic("breceiver") + SendBool: + for { + select { + case cb <- true: + break SendBool + default: + try++ + if try > maxTries { + panic("breceiver") + } + sleep() } - sleep() } <-sync @@ -184,12 +217,18 @@ func main() { <-sync } try = 0 - for b, ok = <-cb; !ok; b, ok = <-cb { - try++ - if try > maxTries { - panic("bsender") + RecvBool: + for { + select { + case b = <-cb: + break RecvBool + default: + try++ + if try > maxTries { + panic("bsender") + } + sleep() } - sleep() } if !b { panic("bsender value") @@ -200,12 +239,18 @@ func main() { go sreceiver(cs, sync) try = 0 - for !(cs <- "hello") { - try++ - if try > maxTries { - panic("sreceiver") + SendString: + for { + select { + case cs <- "hello": + break SendString + default: + try++ + if try > maxTries { + panic("sreceiver") + } + sleep() } - sleep() } <-sync @@ -214,12 +259,18 @@ func main() { <-sync } try = 0 - for s, ok = <-cs; !ok; s, ok = <-cs { - try++ - if try > maxTries { - panic("ssender") + RecvString: + for { + select { + case s = <-cs: + break RecvString + default: + try++ + if try > maxTries { + panic("ssender") + } + sleep() } - sleep() } if s != "hello again" { panic("ssender value") diff --git a/gcc/testsuite/go.test/test/chan/perm.go b/gcc/testsuite/go.test/test/chan/perm.go index d08c035193d..038ff94e369 100644 --- a/gcc/testsuite/go.test/test/chan/perm.go +++ b/gcc/testsuite/go.test/test/chan/perm.go @@ -9,49 +9,43 @@ package main var ( cr <-chan int cs chan<- int - c chan int + c chan int ) func main() { - cr = c // ok - cs = c // ok - c = cr // ERROR "illegal types|incompatible|cannot" - c = cs // ERROR "illegal types|incompatible|cannot" - cr = cs // ERROR "illegal types|incompatible|cannot" - cs = cr // ERROR "illegal types|incompatible|cannot" - - c <- 0 // ok - ok := c <- 0 // ok - _ = ok - <-c // ok + cr = c // ok + cs = c // ok + c = cr // ERROR "illegal types|incompatible|cannot" + c = cs // ERROR "illegal types|incompatible|cannot" + cr = cs // ERROR "illegal types|incompatible|cannot" + cs = cr // ERROR "illegal types|incompatible|cannot" + + c <- 0 // ok + <-c // ok x, ok := <-c // ok _, _ = x, ok - cr <- 0 // ERROR "send" - ok = cr <- 0 // ERROR "send" - _ = ok - <-cr // ok + cr <- 0 // ERROR "send" + <-cr // ok x, ok = <-cr // ok _, _ = x, ok - cs <- 0 // ok - ok = cs <- 0 // ok - _ = ok - <-cs // ERROR "receive" + cs <- 0 // ok + <-cs // ERROR "receive" x, ok = <-cs // ERROR "receive" _, _ = x, ok select { - case c <- 0: // ok - case x := <-c: // ok + case c <- 0: // ok + case x := <-c: // ok _ = x - case cr <- 0: // ERROR "send" - case x := <-cr: // ok + case cr <- 0: // ERROR "send" + case x := <-cr: // ok _ = x - case cs <- 0: // ok - case x := <-cs: // ERROR "receive" + case cs <- 0: // ok + case x := <-cs: // ERROR "receive" _ = x } } diff --git a/gcc/testsuite/go.test/test/chan/select3.go b/gcc/testsuite/go.test/test/chan/select3.go index a1a2ef50b56..b4e8f8e4bf9 100644 --- a/gcc/testsuite/go.test/test/chan/select3.go +++ b/gcc/testsuite/go.test/test/chan/select3.go @@ -88,22 +88,22 @@ func main() { ch <- 7 }) - // receiving (a small number of times) from a closed channel never blocks + // receiving from a closed channel never blocks testBlock(never, func() { for i := 0; i < 10; i++ { if <-closedch != 0 { panic("expected zero value when reading from closed channel") } + if x, ok := <-closedch; x != 0 || ok { + println("closedch:", x, ok) + panic("expected 0, false from closed channel") + } } }) - // sending (a small number of times) to a closed channel is not specified - // but the current implementation doesn't block: test that different - // implementations behave the same - testBlock(never, func() { - for i := 0; i < 10; i++ { - closedch <- 7 - } + // sending to a closed channel panics. + testPanic(always, func() { + closedch <- 7 }) // receiving from a non-ready channel always blocks @@ -189,7 +189,7 @@ func main() { } }) - // selects with closed channels don't block + // selects with closed channels behave like ordinary operations testBlock(never, func() { select { case <-closedch: @@ -197,7 +197,28 @@ func main() { }) testBlock(never, func() { select { + case x := <-closedch: + _ = x + } + }) + testBlock(never, func() { + select { + case x, ok := <-closedch: + _, _ = x, ok + } + }) + testPanic(always, func() { + select { case closedch <- 7: } }) + + // select should not get confused if it sees itself + testBlock(always, func() { + c := make(chan int) + select { + case c <- 1: + case <-c: + } + }) } diff --git a/gcc/testsuite/go.test/test/chan/select4.go b/gcc/testsuite/go.test/test/chan/select4.go new file mode 100644 index 00000000000..46618ac8812 --- /dev/null +++ b/gcc/testsuite/go.test/test/chan/select4.go @@ -0,0 +1,25 @@ +// $G $D/$F.go && $L $F.$A && ./$A.out + +package main + +func f() *int { + println("BUG: called f") + return new(int) +} + +func main() { + var x struct { + a int + } + c := make(chan int, 1) + c1 := make(chan int) + c <- 42 + select { + case *f() = <-c1: + // nothing + case x.a = <-c: + if x.a != 42 { + println("BUG:", x.a) + } + } +} diff --git a/gcc/testsuite/go.test/test/chan/select5.go b/gcc/testsuite/go.test/test/chan/select5.go new file mode 100644 index 00000000000..e7ca9e015c1 --- /dev/null +++ b/gcc/testsuite/go.test/test/chan/select5.go @@ -0,0 +1,482 @@ +// $G $D/$F.go && $L $F.$A && ./$A.out >tmp.go && +// $G tmp.go && $L tmp.$A && ./$A.out || echo BUG: select5 +// rm -f tmp.go + +// 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. + +// Generate test of channel operations and simple selects. +// Only doing one real send or receive at a time, but phrased +// in various ways that the compiler may or may not rewrite +// into simpler expressions. + +package main + +import ( + "bufio" + "fmt" + "io" + "os" + "template" +) + +func main() { + out := bufio.NewWriter(os.Stdout) + fmt.Fprintln(out, header) + a := new(arg) + + // Generate each kind of test as a separate function to avoid + // hitting the 6g optimizer with one enormous function. + // If we name all the functions init we don't have to + // maintain a list of which ones to run. + do := func(t *template.Template) { + fmt.Fprintln(out, `func init() {`) + for ; next(); a.reset() { + run(t, a, out) + } + fmt.Fprintln(out, `}`) + } + + do(recv) + do(send) + do(recvOrder) + do(sendOrder) + do(nonblock) + + fmt.Fprintln(out, "//", a.nreset, "cases") + out.Flush() +} + +func run(t *template.Template, a interface{}, out io.Writer) { + if err := t.Execute(out, a); err != nil { + panic(err) + } +} + +type arg struct{ + def bool + nreset int +} + +func (a *arg) Maybe() bool { + return maybe() +} + +func (a *arg) MaybeDefault() bool { + if a.def { + return false + } + a.def = maybe() + return a.def +} + +func (a *arg) MustDefault() bool { + return !a.def +} + +func (a *arg) reset() { + a.def = false + a.nreset++ +} + +const header = `// GENERATED BY select5.go; DO NOT EDIT + +package main + +// channel is buffered so test is single-goroutine. +// we are not interested in the concurrency aspects +// of select, just testing that the right calls happen. +var c = make(chan int, 1) +var nilch chan int +var n = 1 +var x int +var i interface{} +var dummy = make(chan int) +var m = make(map[int]int) +var order = 0 + +func f(p *int) *int { + return p +} + +// check order of operations by ensuring that +// successive calls to checkorder have increasing o values. +func checkorder(o int) { + if o <= order { + println("invalid order", o, "after", order) + panic("order") + } + order = o +} + +func fc(c chan int, o int) chan int { + checkorder(o) + return c +} + +func fp(p *int, o int) *int { + checkorder(o) + return p +} + +func fn(n, o int) int { + checkorder(o) + return n +} + +func die(x int) { + println("have", x, "want", n) + panic("chan") +} + +func main() { + // everything happens in init funcs +} +` + +func parse(s string) *template.Template { + t := template.New(nil) + t.SetDelims("〈", "〉") + if err := t.Parse(s); err != nil { + panic(s) + } + return t +} + +var recv = parse(` + 〈# Send n, receive it one way or another into x, check that they match.〉 + c <- n + 〈.section Maybe〉 + x = <-c + 〈.or〉 + select { + 〈# Blocking or non-blocking, before the receive.〉 + 〈# The compiler implements two-case select where one is default with custom code,〉 + 〈# so test the default branch both before and after the send.〉 + 〈.section MaybeDefault〉 + default: + panic("nonblock") + 〈.end〉 + 〈# Receive from c. Different cases are direct, indirect, :=, interface, and map assignment.〉 + 〈.section Maybe〉 + case x = <-c: + 〈.or〉〈.section Maybe〉 + case *f(&x) = <-c: + 〈.or〉〈.section Maybe〉 + case y := <-c: + x = y + 〈.or〉〈.section Maybe〉 + case i = <-c: + x = i.(int) + 〈.or〉 + case m[13] = <-c: + x = m[13] + 〈.end〉〈.end〉〈.end〉〈.end〉 + 〈# Blocking or non-blocking again, after the receive.〉 + 〈.section MaybeDefault〉 + default: + panic("nonblock") + 〈.end〉 + 〈# Dummy send, receive to keep compiler from optimizing select.〉 + 〈.section Maybe〉 + case dummy <- 1: + panic("dummy send") + 〈.end〉 + 〈.section Maybe〉 + case <-dummy: + panic("dummy receive") + 〈.end〉 + 〈# Nil channel send, receive to keep compiler from optimizing select.〉 + 〈.section Maybe〉 + case nilch <- 1: + panic("nilch send") + 〈.end〉 + 〈.section Maybe〉 + case <-nilch: + panic("nilch recv") + 〈.end〉 + } + 〈.end〉 + if x != n { + die(x) + } + n++ +`) + +var recvOrder = parse(` + 〈# Send n, receive it one way or another into x, check that they match.〉 + 〈# Check order of operations along the way by calling functions that check〉 + 〈# that the argument sequence is strictly increasing.〉 + order = 0 + c <- n + 〈.section Maybe〉 + 〈# Outside of select, left-to-right rule applies.〉 + 〈# (Inside select, assignment waits until case is chosen,〉 + 〈# so right hand side happens before anything on left hand side.〉 + *fp(&x, 1) = <-fc(c, 2) + 〈.or〉〈.section Maybe〉 + m[fn(13, 1)] = <-fc(c, 2) + x = m[13] + 〈.or〉 + select { + 〈# Blocking or non-blocking, before the receive.〉 + 〈# The compiler implements two-case select where one is default with custom code,〉 + 〈# so test the default branch both before and after the send.〉 + 〈.section MaybeDefault〉 + default: + panic("nonblock") + 〈.end〉 + 〈# Receive from c. Different cases are direct, indirect, :=, interface, and map assignment.〉 + 〈.section Maybe〉 + case *fp(&x, 100) = <-fc(c, 1): + 〈.or〉〈.section Maybe〉 + case y := <-fc(c, 1): + x = y + 〈.or〉〈.section Maybe〉 + case i = <-fc(c, 1): + x = i.(int) + 〈.or〉 + case m[fn(13, 100)] = <-fc(c, 1): + x = m[13] + 〈.end〉〈.end〉〈.end〉 + 〈# Blocking or non-blocking again, after the receive.〉 + 〈.section MaybeDefault〉 + default: + panic("nonblock") + 〈.end〉 + 〈# Dummy send, receive to keep compiler from optimizing select.〉 + 〈.section Maybe〉 + case fc(dummy, 2) <- fn(1, 3): + panic("dummy send") + 〈.end〉 + 〈.section Maybe〉 + case <-fc(dummy, 4): + panic("dummy receive") + 〈.end〉 + 〈# Nil channel send, receive to keep compiler from optimizing select.〉 + 〈.section Maybe〉 + case fc(nilch, 5) <- fn(1, 6): + panic("nilch send") + 〈.end〉 + 〈.section Maybe〉 + case <-fc(nilch, 7): + panic("nilch recv") + 〈.end〉 + } + 〈.end〉〈.end〉 + if x != n { + die(x) + } + n++ +`) + +var send = parse(` + 〈# Send n one way or another, receive it into x, check that they match.〉 + 〈.section Maybe〉 + c <- n + 〈.or〉 + select { + 〈# Blocking or non-blocking, before the receive (same reason as in recv).〉 + 〈.section MaybeDefault〉 + default: + panic("nonblock") + 〈.end〉 + 〈# Send c <- n. No real special cases here, because no values come back〉 + 〈# from the send operation.〉 + case c <- n: + 〈# Blocking or non-blocking.〉 + 〈.section MaybeDefault〉 + default: + panic("nonblock") + 〈.end〉 + 〈# Dummy send, receive to keep compiler from optimizing select.〉 + 〈.section Maybe〉 + case dummy <- 1: + panic("dummy send") + 〈.end〉 + 〈.section Maybe〉 + case <-dummy: + panic("dummy receive") + 〈.end〉 + 〈# Nil channel send, receive to keep compiler from optimizing select.〉 + 〈.section Maybe〉 + case nilch <- 1: + panic("nilch send") + 〈.end〉 + 〈.section Maybe〉 + case <-nilch: + panic("nilch recv") + 〈.end〉 + } + 〈.end〉 + x = <-c + if x != n { + die(x) + } + n++ +`) + +var sendOrder = parse(` + 〈# Send n one way or another, receive it into x, check that they match.〉 + 〈# Check order of operations along the way by calling functions that check〉 + 〈# that the argument sequence is strictly increasing.〉 + order = 0 + 〈.section Maybe〉 + fc(c, 1) <- fn(n, 2) + 〈.or〉 + select { + 〈# Blocking or non-blocking, before the receive (same reason as in recv).〉 + 〈.section MaybeDefault〉 + default: + panic("nonblock") + 〈.end〉 + 〈# Send c <- n. No real special cases here, because no values come back〉 + 〈# from the send operation.〉 + case fc(c, 1) <- fn(n, 2): + 〈# Blocking or non-blocking.〉 + 〈.section MaybeDefault〉 + default: + panic("nonblock") + 〈.end〉 + 〈# Dummy send, receive to keep compiler from optimizing select.〉 + 〈.section Maybe〉 + case fc(dummy, 3) <- fn(1, 4): + panic("dummy send") + 〈.end〉 + 〈.section Maybe〉 + case <-fc(dummy, 5): + panic("dummy receive") + 〈.end〉 + 〈# Nil channel send, receive to keep compiler from optimizing select.〉 + 〈.section Maybe〉 + case fc(nilch, 6) <- fn(1, 7): + panic("nilch send") + 〈.end〉 + 〈.section Maybe〉 + case <-fc(nilch, 8): + panic("nilch recv") + 〈.end〉 + } + 〈.end〉 + x = <-c + if x != n { + die(x) + } + n++ +`) + +var nonblock = parse(` + x = n + 〈# Test various combinations of non-blocking operations.〉 + 〈# Receive assignments must not edit or even attempt to compute the address of the lhs.〉 + select { + 〈.section MaybeDefault〉 + default: + 〈.end〉 + 〈.section Maybe〉 + case dummy <- 1: + panic("dummy <- 1") + 〈.end〉 + 〈.section Maybe〉 + case nilch <- 1: + panic("nilch <- 1") + 〈.end〉 + 〈.section Maybe〉 + case <-dummy: + panic("<-dummy") + 〈.end〉 + 〈.section Maybe〉 + case x = <-dummy: + panic("<-dummy x") + 〈.end〉 + 〈.section Maybe〉 + case **(**int)(nil) = <-dummy: + panic("<-dummy (and didn't crash saving result!)") + 〈.end〉 + 〈.section Maybe〉 + case <-nilch: + panic("<-nilch") + 〈.end〉 + 〈.section Maybe〉 + case x = <-nilch: + panic("<-nilch x") + 〈.end〉 + 〈.section Maybe〉 + case **(**int)(nil) = <-nilch: + panic("<-nilch (and didn't crash saving result!)") + 〈.end〉 + 〈.section MustDefault〉 + default: + 〈.end〉 + } + if x != n { + die(x) + } + n++ +`) + +// Code for enumerating all possible paths through +// some logic. The logic should call choose(n) when +// it wants to choose between n possibilities. +// On successive runs through the logic, choose(n) +// will return 0, 1, ..., n-1. The helper maybe() is +// similar but returns true and then false. +// +// Given a function gen that generates an output +// using choose and maybe, code can generate all +// possible outputs using +// +// for next() { +// gen() +// } + +type choice struct { + i, n int +} + +var choices []choice +var cp int = -1 + +func maybe() bool { + return choose(2) == 0 +} + +func choose(n int) int { + if cp >= len(choices) { + // never asked this before: start with 0. + choices = append(choices, choice{0, n}) + cp = len(choices) + return 0 + } + // otherwise give recorded answer + if n != choices[cp].n { + panic("inconsistent choices") + } + i := choices[cp].i + cp++ + return i +} + +func next() bool { + if cp < 0 { + // start a new round + cp = 0 + return true + } + + // increment last choice sequence + cp = len(choices)-1 + for cp >= 0 && choices[cp].i == choices[cp].n-1 { + cp-- + } + if cp < 0 { + choices = choices[:0] + return false + } + choices[cp].i++ + choices = choices[:cp+1] + cp = 0 + return true +} + diff --git a/gcc/testsuite/go.test/test/chan/sendstmt.go b/gcc/testsuite/go.test/test/chan/sendstmt.go new file mode 100644 index 00000000000..ee6f765cf88 --- /dev/null +++ b/gcc/testsuite/go.test/test/chan/sendstmt.go @@ -0,0 +1,37 @@ +// $G $D/$F.go && $L $F.$A && ./$A.out + +// 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. + +// Test various parsing cases that are a little +// different now that send is a statement, not a expression. + +package main + +func main() { + chanchan() + sendprec() +} + +func chanchan() { + cc := make(chan chan int, 1) + c := make(chan int, 1) + cc <- c + select { + case <-cc <- 2: + default: + panic("nonblock") + } + if <-c != 2 { + panic("bad receive") + } +} + +func sendprec() { + c := make(chan bool, 1) + c <- false || true // not a syntax error: same as c <- (false || true) + if !<-c { + panic("sent false") + } +} |