Merge pull request #1 from ethereum/master

Update to go-ethereum master
This commit is contained in:
Boqin Qin 2020-02-13 12:05:37 +08:00 committed by GitHub
commit 96490a10be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 291 additions and 89 deletions

View file

@ -127,15 +127,28 @@ func (b *SimulatedBackend) rollback() {
b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database()) b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database())
} }
// stateByBlockNumber retrieves a state by a given blocknumber.
func (b *SimulatedBackend) stateByBlockNumber(ctx context.Context, blockNumber *big.Int) (*state.StateDB, error) {
if blockNumber == nil || blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) == 0 {
return b.blockchain.State()
}
block, err := b.BlockByNumber(ctx, blockNumber)
if err != nil {
return nil, err
}
return b.blockchain.StateAt(block.Hash())
}
// CodeAt returns the code associated with a certain account in the blockchain. // CodeAt returns the code associated with a certain account in the blockchain.
func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { statedb, err := b.stateByBlockNumber(ctx, blockNumber)
return nil, errBlockNumberUnsupported if err != nil {
return nil, err
} }
statedb, _ := b.blockchain.State()
return statedb.GetCode(contract), nil return statedb.GetCode(contract), nil
} }
@ -144,10 +157,11 @@ func (b *SimulatedBackend) BalanceAt(ctx context.Context, contract common.Addres
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { statedb, err := b.stateByBlockNumber(ctx, blockNumber)
return nil, errBlockNumberUnsupported if err != nil {
return nil, err
} }
statedb, _ := b.blockchain.State()
return statedb.GetBalance(contract), nil return statedb.GetBalance(contract), nil
} }
@ -156,10 +170,11 @@ func (b *SimulatedBackend) NonceAt(ctx context.Context, contract common.Address,
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { statedb, err := b.stateByBlockNumber(ctx, blockNumber)
return 0, errBlockNumberUnsupported if err != nil {
return 0, err
} }
statedb, _ := b.blockchain.State()
return statedb.GetNonce(contract), nil return statedb.GetNonce(contract), nil
} }
@ -168,10 +183,11 @@ func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Addres
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { statedb, err := b.stateByBlockNumber(ctx, blockNumber)
return nil, errBlockNumberUnsupported if err != nil {
return nil, err
} }
statedb, _ := b.blockchain.State()
val := statedb.GetState(contract, key) val := statedb.GetState(contract, key)
return val[:], nil return val[:], nil
} }

View file

@ -141,6 +141,11 @@ func (am *Manager) Wallets() []Wallet {
am.lock.RLock() am.lock.RLock()
defer am.lock.RUnlock() defer am.lock.RUnlock()
return am.walletsNoLock()
}
// walletsNoLock returns all registered wallets. Callers must hold am.lock.
func (am *Manager) walletsNoLock() []Wallet {
cpy := make([]Wallet, len(am.wallets)) cpy := make([]Wallet, len(am.wallets))
copy(cpy, am.wallets) copy(cpy, am.wallets)
return cpy return cpy
@ -155,7 +160,7 @@ func (am *Manager) Wallet(url string) (Wallet, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, wallet := range am.Wallets() { for _, wallet := range am.walletsNoLock() {
if wallet.URL() == parsed { if wallet.URL() == parsed {
return wallet, nil return wallet, nil
} }

View file

@ -80,6 +80,7 @@ type RetestethEthAPI interface {
SendRawTransaction(ctx context.Context, rawTx hexutil.Bytes) (common.Hash, error) SendRawTransaction(ctx context.Context, rawTx hexutil.Bytes) (common.Hash, error)
BlockNumber(ctx context.Context) (uint64, error) BlockNumber(ctx context.Context) (uint64, error)
GetBlockByNumber(ctx context.Context, blockNr math.HexOrDecimal64, fullTx bool) (map[string]interface{}, error) GetBlockByNumber(ctx context.Context, blockNr math.HexOrDecimal64, fullTx bool) (map[string]interface{}, error)
GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error)
GetBalance(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (*math.HexOrDecimal256, error) GetBalance(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (*math.HexOrDecimal256, error)
GetCode(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (hexutil.Bytes, error) GetCode(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (hexutil.Bytes, error)
GetTransactionCount(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (uint64, error) GetTransactionCount(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (uint64, error)
@ -618,6 +619,20 @@ func (api *RetestethAPI) GetBlockByNumber(ctx context.Context, blockNr math.HexO
return nil, fmt.Errorf("block %d not found", blockNr) return nil, fmt.Errorf("block %d not found", blockNr)
} }
func (api *RetestethAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error) {
block := api.blockchain.GetBlockByHash(blockHash)
if block != nil {
response, err := RPCMarshalBlock(block, true, fullTx)
if err != nil {
return nil, err
}
response["author"] = response["miner"]
response["totalDifficulty"] = (*hexutil.Big)(api.blockchain.GetTd(block.Hash(), block.Number().Uint64()))
return response, err
}
return nil, fmt.Errorf("block 0x%x not found", blockHash)
}
func (api *RetestethAPI) AccountRange(ctx context.Context, func (api *RetestethAPI) AccountRange(ctx context.Context,
blockHashOrNumber *math.HexOrDecimal256, txIndex uint64, blockHashOrNumber *math.HexOrDecimal256, txIndex uint64,
addressHash *math.HexOrDecimal256, maxResults uint64, addressHash *math.HexOrDecimal256, maxResults uint64,

View file

@ -31,44 +31,93 @@ func Now() AbsTime {
return AbsTime(monotime.Now()) return AbsTime(monotime.Now())
} }
// Add returns t + d. // Add returns t + d as absolute time.
func (t AbsTime) Add(d time.Duration) AbsTime { func (t AbsTime) Add(d time.Duration) AbsTime {
return t + AbsTime(d) return t + AbsTime(d)
} }
// Sub returns t - t2 as a duration.
func (t AbsTime) Sub(t2 AbsTime) time.Duration {
return time.Duration(t - t2)
}
// The Clock interface makes it possible to replace the monotonic system clock with // The Clock interface makes it possible to replace the monotonic system clock with
// a simulated clock. // a simulated clock.
type Clock interface { type Clock interface {
Now() AbsTime Now() AbsTime
Sleep(time.Duration) Sleep(time.Duration)
After(time.Duration) <-chan time.Time NewTimer(time.Duration) ChanTimer
After(time.Duration) <-chan AbsTime
AfterFunc(d time.Duration, f func()) Timer AfterFunc(d time.Duration, f func()) Timer
} }
// Timer represents a cancellable event returned by AfterFunc // Timer is a cancellable event created by AfterFunc.
type Timer interface { type Timer interface {
// Stop cancels the timer. It returns false if the timer has already
// expired or been stopped.
Stop() bool Stop() bool
} }
// ChanTimer is a cancellable event created by NewTimer.
type ChanTimer interface {
Timer
// The channel returned by C receives a value when the timer expires.
C() <-chan AbsTime
// Reset reschedules the timer with a new timeout.
// It should be invoked only on stopped or expired timers with drained channels.
Reset(time.Duration)
}
// System implements Clock using the system clock. // System implements Clock using the system clock.
type System struct{} type System struct{}
// Now returns the current monotonic time. // Now returns the current monotonic time.
func (System) Now() AbsTime { func (c System) Now() AbsTime {
return AbsTime(monotime.Now()) return AbsTime(monotime.Now())
} }
// Sleep blocks for the given duration. // Sleep blocks for the given duration.
func (System) Sleep(d time.Duration) { func (c System) Sleep(d time.Duration) {
time.Sleep(d) time.Sleep(d)
} }
// NewTimer creates a timer which can be rescheduled.
func (c System) NewTimer(d time.Duration) ChanTimer {
ch := make(chan AbsTime, 1)
t := time.AfterFunc(d, func() {
// This send is non-blocking because that's how time.Timer
// behaves. It doesn't matter in the happy case, but does
// when Reset is misused.
select {
case ch <- c.Now():
default:
}
})
return &systemTimer{t, ch}
}
// After returns a channel which receives the current time after d has elapsed. // After returns a channel which receives the current time after d has elapsed.
func (System) After(d time.Duration) <-chan time.Time { func (c System) After(d time.Duration) <-chan AbsTime {
return time.After(d) ch := make(chan AbsTime, 1)
time.AfterFunc(d, func() { ch <- c.Now() })
return ch
} }
// AfterFunc runs f on a new goroutine after the duration has elapsed. // AfterFunc runs f on a new goroutine after the duration has elapsed.
func (System) AfterFunc(d time.Duration, f func()) Timer { func (c System) AfterFunc(d time.Duration, f func()) Timer {
return time.AfterFunc(d, f) return time.AfterFunc(d, f)
} }
type systemTimer struct {
*time.Timer
ch <-chan AbsTime
}
func (st *systemTimer) Reset(d time.Duration) {
st.Timer.Reset(d)
}
func (st *systemTimer) C() <-chan AbsTime {
return st.ch
}

View file

@ -17,6 +17,7 @@
package mclock package mclock
import ( import (
"container/heap"
"sync" "sync"
"time" "time"
) )
@ -32,18 +33,24 @@ import (
// the timeout using a channel or semaphore. // the timeout using a channel or semaphore.
type Simulated struct { type Simulated struct {
now AbsTime now AbsTime
scheduled []*simTimer scheduled simTimerHeap
mu sync.RWMutex mu sync.RWMutex
cond *sync.Cond cond *sync.Cond
lastId uint64
} }
// simTimer implements Timer on the virtual clock. // simTimer implements ChanTimer on the virtual clock.
type simTimer struct { type simTimer struct {
do func() at AbsTime
at AbsTime index int // position in s.scheduled
id uint64 s *Simulated
s *Simulated do func()
ch <-chan AbsTime
}
func (s *Simulated) init() {
if s.cond == nil {
s.cond = sync.NewCond(&s.mu)
}
} }
// Run moves the clock by the given duration, executing all timers before that duration. // Run moves the clock by the given duration, executing all timers before that duration.
@ -53,14 +60,9 @@ func (s *Simulated) Run(d time.Duration) {
end := s.now + AbsTime(d) end := s.now + AbsTime(d)
var do []func() var do []func()
for len(s.scheduled) > 0 { for len(s.scheduled) > 0 && s.scheduled[0].at <= end {
ev := s.scheduled[0] ev := heap.Pop(&s.scheduled).(*simTimer)
if ev.at > end {
break
}
s.now = ev.at
do = append(do, ev.do) do = append(do, ev.do)
s.scheduled = s.scheduled[1:]
} }
s.now = end s.now = end
s.mu.Unlock() s.mu.Unlock()
@ -102,14 +104,22 @@ func (s *Simulated) Sleep(d time.Duration) {
<-s.After(d) <-s.After(d)
} }
// NewTimer creates a timer which fires when the clock has advanced by d.
func (s *Simulated) NewTimer(d time.Duration) ChanTimer {
s.mu.Lock()
defer s.mu.Unlock()
ch := make(chan AbsTime, 1)
var timer *simTimer
timer = s.schedule(d, func() { ch <- timer.at })
timer.ch = ch
return timer
}
// After returns a channel which receives the current time after the clock // After returns a channel which receives the current time after the clock
// has advanced by d. // has advanced by d.
func (s *Simulated) After(d time.Duration) <-chan time.Time { func (s *Simulated) After(d time.Duration) <-chan AbsTime {
after := make(chan time.Time, 1) return s.NewTimer(d).C()
s.AfterFunc(d, func() {
after <- (time.Time{}).Add(time.Duration(s.now))
})
return after
} }
// AfterFunc runs fn after the clock has advanced by d. Unlike with the system // AfterFunc runs fn after the clock has advanced by d. Unlike with the system
@ -117,46 +127,83 @@ func (s *Simulated) After(d time.Duration) <-chan time.Time {
func (s *Simulated) AfterFunc(d time.Duration, fn func()) Timer { func (s *Simulated) AfterFunc(d time.Duration, fn func()) Timer {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
return s.schedule(d, fn)
}
func (s *Simulated) schedule(d time.Duration, fn func()) *simTimer {
s.init() s.init()
at := s.now + AbsTime(d) at := s.now + AbsTime(d)
s.lastId++
id := s.lastId
l, h := 0, len(s.scheduled)
ll := h
for l != h {
m := (l + h) / 2
if (at < s.scheduled[m].at) || ((at == s.scheduled[m].at) && (id < s.scheduled[m].id)) {
h = m
} else {
l = m + 1
}
}
ev := &simTimer{do: fn, at: at, s: s} ev := &simTimer{do: fn, at: at, s: s}
s.scheduled = append(s.scheduled, nil) heap.Push(&s.scheduled, ev)
copy(s.scheduled[l+1:], s.scheduled[l:ll])
s.scheduled[l] = ev
s.cond.Broadcast() s.cond.Broadcast()
return ev return ev
} }
func (ev *simTimer) Stop() bool { func (ev *simTimer) Stop() bool {
s := ev.s ev.s.mu.Lock()
s.mu.Lock() defer ev.s.mu.Unlock()
defer s.mu.Unlock()
for i := 0; i < len(s.scheduled); i++ { if ev.index < 0 {
if s.scheduled[i] == ev { return false
s.scheduled = append(s.scheduled[:i], s.scheduled[i+1:]...)
s.cond.Broadcast()
return true
}
} }
return false heap.Remove(&ev.s.scheduled, ev.index)
ev.s.cond.Broadcast()
ev.index = -1
return true
} }
func (s *Simulated) init() { func (ev *simTimer) Reset(d time.Duration) {
if s.cond == nil { if ev.ch == nil {
s.cond = sync.NewCond(&s.mu) panic("mclock: Reset() on timer created by AfterFunc")
} }
ev.s.mu.Lock()
defer ev.s.mu.Unlock()
ev.at = ev.s.now.Add(d)
if ev.index < 0 {
heap.Push(&ev.s.scheduled, ev) // already expired
} else {
heap.Fix(&ev.s.scheduled, ev.index) // hasn't fired yet, reschedule
}
ev.s.cond.Broadcast()
}
func (ev *simTimer) C() <-chan AbsTime {
if ev.ch == nil {
panic("mclock: C() on timer created by AfterFunc")
}
return ev.ch
}
type simTimerHeap []*simTimer
func (h *simTimerHeap) Len() int {
return len(*h)
}
func (h *simTimerHeap) Less(i, j int) bool {
return (*h)[i].at < (*h)[j].at
}
func (h *simTimerHeap) Swap(i, j int) {
(*h)[i], (*h)[j] = (*h)[j], (*h)[i]
(*h)[i].index = i
(*h)[j].index = j
}
func (h *simTimerHeap) Push(x interface{}) {
t := x.(*simTimer)
t.index = len(*h)
*h = append(*h, t)
}
func (h *simTimerHeap) Pop() interface{} {
end := len(*h) - 1
t := (*h)[end]
t.index = -1
(*h)[end] = nil
*h = (*h)[:end]
return t
} }

View file

@ -25,14 +25,16 @@ var _ Clock = System{}
var _ Clock = new(Simulated) var _ Clock = new(Simulated)
func TestSimulatedAfter(t *testing.T) { func TestSimulatedAfter(t *testing.T) {
const timeout = 30 * time.Minute
const adv = time.Minute
var ( var (
c Simulated timeout = 30 * time.Minute
end = c.Now().Add(timeout) offset = 99 * time.Hour
ch = c.After(timeout) adv = 11 * time.Minute
c Simulated
) )
c.Run(offset)
end := c.Now().Add(timeout)
ch := c.After(timeout)
for c.Now() < end.Add(-adv) { for c.Now() < end.Add(-adv) {
c.Run(adv) c.Run(adv)
select { select {
@ -45,8 +47,8 @@ func TestSimulatedAfter(t *testing.T) {
c.Run(adv) c.Run(adv)
select { select {
case stamp := <-ch: case stamp := <-ch:
want := time.Time{}.Add(timeout) want := AbsTime(0).Add(offset).Add(timeout)
if !stamp.Equal(want) { if stamp != want {
t.Errorf("Wrong time sent on timer channel: got %v, want %v", stamp, want) t.Errorf("Wrong time sent on timer channel: got %v, want %v", stamp, want)
} }
default: default:
@ -113,3 +115,48 @@ func TestSimulatedSleep(t *testing.T) {
t.Fatal("Sleep didn't return in time") t.Fatal("Sleep didn't return in time")
} }
} }
func TestSimulatedTimerReset(t *testing.T) {
var (
c Simulated
timeout = 1 * time.Hour
)
timer := c.NewTimer(timeout)
c.Run(2 * timeout)
select {
case ftime := <-timer.C():
if ftime != AbsTime(timeout) {
t.Fatalf("wrong time %v sent on timer channel, want %v", ftime, AbsTime(timeout))
}
default:
t.Fatal("timer didn't fire")
}
timer.Reset(timeout)
c.Run(2 * timeout)
select {
case ftime := <-timer.C():
if ftime != AbsTime(3*timeout) {
t.Fatalf("wrong time %v sent on timer channel, want %v", ftime, AbsTime(3*timeout))
}
default:
t.Fatal("timer didn't fire again")
}
}
func TestSimulatedTimerStop(t *testing.T) {
var (
c Simulated
timeout = 1 * time.Hour
)
timer := c.NewTimer(timeout)
c.Run(2 * timeout)
if timer.Stop() {
t.Errorf("Stop returned true for fired timer")
}
select {
case <-timer.C():
default:
t.Fatal("timer didn't fire")
}
}

View file

@ -138,6 +138,7 @@ func (f *Feed) Send(value interface{}) (nsent int) {
if !f.typecheck(rvalue.Type()) { if !f.typecheck(rvalue.Type()) {
f.sendLock <- struct{}{} f.sendLock <- struct{}{}
f.mu.Unlock()
panic(feedTypeError{op: "Send", got: rvalue.Type(), want: f.etype}) panic(feedTypeError{op: "Send", got: rvalue.Type(), want: f.etype})
} }
f.mu.Unlock() f.mu.Unlock()

View file

@ -145,7 +145,6 @@ func (s *resubscribeSub) loop() {
func (s *resubscribeSub) subscribe() Subscription { func (s *resubscribeSub) subscribe() Subscription {
subscribed := make(chan error) subscribed := make(chan error)
var sub Subscription var sub Subscription
retry:
for { for {
s.lastTry = mclock.Now() s.lastTry = mclock.Now()
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
@ -157,19 +156,19 @@ retry:
select { select {
case err := <-subscribed: case err := <-subscribed:
cancel() cancel()
if err != nil { if err == nil {
// Subscribing failed, wait before launching the next try. if sub == nil {
if s.backoffWait() { panic("event: ResubscribeFunc returned nil subscription and no error")
return nil
} }
continue retry return sub
} }
if sub == nil { // Subscribing failed, wait before launching the next try.
panic("event: ResubscribeFunc returned nil subscription and no error") if s.backoffWait() {
return nil // unsubscribed during wait
} }
return sub
case <-s.unsub: case <-s.unsub:
cancel() cancel()
<-subscribed // avoid leaking the s.fn goroutine.
return nil return nil
} }
} }

View file

@ -102,7 +102,7 @@ func TestResubscribe(t *testing.T) {
func TestResubscribeAbort(t *testing.T) { func TestResubscribeAbort(t *testing.T) {
t.Parallel() t.Parallel()
done := make(chan error) done := make(chan error, 1)
sub := Resubscribe(0, func(ctx context.Context) (Subscription, error) { sub := Resubscribe(0, func(ctx context.Context) (Subscription, error) {
select { select {
case <-ctx.Done(): case <-ctx.Done():

View file

@ -128,6 +128,7 @@ func (h *serverHandler) handle(p *peer) error {
} }
// Reject light clients if server is not synced. // Reject light clients if server is not synced.
if !h.synced() { if !h.synced() {
p.Log().Debug("Light server not synced, rejecting peer")
return p2p.DiscRequested return p2p.DiscRequested
} }
defer p.fcClient.Disconnect() defer p.fcClient.Disconnect()

View file

@ -287,7 +287,7 @@ func (n *ExecNode) Stop() error {
if err := n.Cmd.Process.Signal(syscall.SIGTERM); err != nil { if err := n.Cmd.Process.Signal(syscall.SIGTERM); err != nil {
return n.Cmd.Process.Kill() return n.Cmd.Process.Kill()
} }
waitErr := make(chan error) waitErr := make(chan error, 1)
go func() { go func() {
waitErr <- n.Cmd.Wait() waitErr <- n.Cmd.Wait()
}() }()

View file

@ -276,6 +276,9 @@ func (c *Client) Call(result interface{}, method string, args ...interface{}) er
// The result must be a pointer so that package json can unmarshal into it. You // The result must be a pointer so that package json can unmarshal into it. You
// can also pass nil, in which case the result is ignored. // can also pass nil, in which case the result is ignored.
func (c *Client) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { func (c *Client) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error {
if result != nil && reflect.TypeOf(result).Kind() != reflect.Ptr {
return fmt.Errorf("call result parameter must be pointer or nil interface: %v", result)
}
msg, err := c.newMessage(method, args...) msg, err := c.newMessage(method, args...)
if err != nil { if err != nil {
return err return err

View file

@ -49,6 +49,23 @@ func TestClientRequest(t *testing.T) {
} }
} }
func TestClientResponseType(t *testing.T) {
server := newTestServer()
defer server.Stop()
client := DialInProc(server)
defer client.Close()
if err := client.Call(nil, "test_echo", "hello", 10, &echoArgs{"world"}); err != nil {
t.Errorf("Passing nil as result should be fine, but got an error: %v", err)
}
var resultVar echoResult
// Note: passing the var, not a ref
err := client.Call(resultVar, "test_echo", "hello", 10, &echoArgs{"world"})
if err == nil {
t.Error("Passing a var as result should be an error")
}
}
func TestClientBatchRequest(t *testing.T) { func TestClientBatchRequest(t *testing.T) {
server := newTestServer() server := newTestServer()
defer server.Stop() defer server.Stop()

View file

@ -923,7 +923,9 @@ func isPrimitiveTypeValid(primitiveType string) bool {
primitiveType == "bytes30" || primitiveType == "bytes30" ||
primitiveType == "bytes30[]" || primitiveType == "bytes30[]" ||
primitiveType == "bytes31" || primitiveType == "bytes31" ||
primitiveType == "bytes31[]" { primitiveType == "bytes31[]" ||
primitiveType == "bytes32" ||
primitiveType == "bytes32[]" {
return true return true
} }
if primitiveType == "int" || if primitiveType == "int" ||