github.com/pygolin/runtime@v0.0.0-20201208210830-a62e3cd39798/dict_test.go (about)

     1  // Copyright 2016 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package runtime
    16  
    17  import (
    18  	"reflect"
    19  	"regexp"
    20  	"runtime"
    21  	"strconv"
    22  	"sync"
    23  	"testing"
    24  	"time"
    25  )
    26  
    27  // hashFoo is the hash of the string 'foo'. We use this to validate some corner
    28  // cases around hash collision below.
    29  // NOTE: Inline func helps support 32bit systems.
    30  var hashFoo = NewInt(func(i int64) int { return int(i) }(-4177197833195190597)).ToObject()
    31  
    32  func TestNewStringDict(t *testing.T) {
    33  	cases := []struct {
    34  		m    map[string]*Object
    35  		want *Dict
    36  	}{
    37  		{nil, NewDict()},
    38  		{map[string]*Object{"baz": NewFloat(3.14).ToObject()}, newTestDict("baz", 3.14)},
    39  		{map[string]*Object{"foo": NewInt(2).ToObject(), "bar": NewInt(4).ToObject()}, newTestDict("bar", 4, "foo", 2)},
    40  	}
    41  	for _, cas := range cases {
    42  		fun := newBuiltinFunction("newStringDict", func(*Frame, Args, KWArgs) (*Object, *BaseException) {
    43  			return newStringDict(cas.m).ToObject(), nil
    44  		}).ToObject()
    45  		if err := runInvokeTestCase(fun, &invokeTestCase{want: cas.want.ToObject()}); err != "" {
    46  			t.Error(err)
    47  		}
    48  	}
    49  }
    50  
    51  func TestDictClear(t *testing.T) {
    52  	clear := mustNotRaise(GetAttr(NewRootFrame(), DictType.ToObject(), NewStr("clear"), nil))
    53  	fun := newBuiltinFunction("TestDictClear", func(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
    54  		if _, raised := clear.Call(f, args, nil); raised != nil {
    55  			return nil, raised
    56  		}
    57  		return args[0], nil
    58  	}).ToObject()
    59  	cases := []invokeTestCase{
    60  		{args: wrapArgs(NewDict()), want: NewDict().ToObject()},
    61  		{args: wrapArgs(newStringDict(map[string]*Object{"foo": NewInt(1).ToObject()})), want: NewDict().ToObject()},
    62  		{args: wrapArgs(newTestDict(2, None, "baz", 3.14)), want: NewDict().ToObject()},
    63  		{args: wrapArgs(NewDict(), NewList()), wantExc: mustCreateException(TypeErrorType, "'clear' of 'dict' requires 1 arguments")},
    64  		{args: wrapArgs(NewDict(), None), wantExc: mustCreateException(TypeErrorType, "'clear' of 'dict' requires 1 arguments")},
    65  		{args: wrapArgs(None), wantExc: mustCreateException(TypeErrorType, "unbound method clear() must be called with dict instance as first argument (got NoneType instance instead)")},
    66  	}
    67  	for _, cas := range cases {
    68  		if err := runInvokeTestCase(fun, &cas); err != "" {
    69  			t.Error(err)
    70  		}
    71  	}
    72  }
    73  
    74  func TestDictContains(t *testing.T) {
    75  	cases := []invokeTestCase{
    76  		{args: wrapArgs(NewDict(), "foo"), want: False.ToObject()},
    77  		{args: wrapArgs(newTestDict("foo", 1, "bar", 2), "foo"), want: True.ToObject()},
    78  		{args: wrapArgs(newTestDict(3, "foo", "bar", 42), 42), want: False.ToObject()},
    79  	}
    80  	for _, cas := range cases {
    81  		if err := runInvokeMethodTestCase(DictType, "__contains__", &cas); err != "" {
    82  			t.Error(err)
    83  		}
    84  	}
    85  }
    86  
    87  func TestDictDelItem(t *testing.T) {
    88  	fun := newBuiltinFunction("TestDictDelItem", func(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
    89  		if raised := checkMethodArgs(f, "TestDictDelItem", args, DictType, ObjectType); raised != nil {
    90  			return nil, raised
    91  		}
    92  		if raised := DelItem(f, args[0], args[1]); raised != nil {
    93  			return nil, raised
    94  		}
    95  		return args[0], nil
    96  	}).ToObject()
    97  	testDict := newTestDict("a", 1, "b", 2, "c", 3)
    98  	cases := []invokeTestCase{
    99  		{args: wrapArgs(newTestDict("foo", 1), "foo"), want: NewDict().ToObject()},
   100  		{args: wrapArgs(NewDict(), 10), wantExc: mustCreateException(KeyErrorType, "10")},
   101  		{args: wrapArgs(testDict, "a"), want: newTestDict("b", 2, "c", 3).ToObject()},
   102  		{args: wrapArgs(testDict, "c"), want: newTestDict("b", 2).ToObject()},
   103  		{args: wrapArgs(testDict, "a"), wantExc: mustCreateException(KeyErrorType, "a")},
   104  		{args: wrapArgs(NewDict(), NewList()), wantExc: mustCreateException(TypeErrorType, "unhashable type: 'list'")},
   105  	}
   106  	for _, cas := range cases {
   107  		if err := runInvokeTestCase(fun, &cas); err != "" {
   108  			t.Error(err)
   109  		}
   110  	}
   111  }
   112  
   113  func TestDictDelItemString(t *testing.T) {
   114  	fun := newBuiltinFunction("TestDictDelItemString", func(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   115  		if raised := checkMethodArgs(f, "TestDictDelItemString", args, DictType, StrType); raised != nil {
   116  			return nil, raised
   117  		}
   118  		deleted, raised := toDictUnsafe(args[0]).DelItemString(f, toStrUnsafe(args[1]).Value())
   119  		if raised != nil {
   120  			return nil, raised
   121  		}
   122  		return newTestTuple(deleted, args[0]).ToObject(), nil
   123  	}).ToObject()
   124  	cases := []invokeTestCase{
   125  		{args: wrapArgs(newTestDict("foo", 1), "foo"), want: newTestTuple(true, NewDict()).ToObject()},
   126  		{args: wrapArgs(NewDict(), "qux"), want: newTestTuple(false, NewDict()).ToObject()},
   127  	}
   128  	for _, cas := range cases {
   129  		if err := runInvokeTestCase(fun, &cas); err != "" {
   130  			t.Error(err)
   131  		}
   132  	}
   133  }
   134  
   135  func TestDictEqNE(t *testing.T) {
   136  	fun := wrapFuncForTest(func(f *Frame, v, w *Object) (*Object, *BaseException) {
   137  		eq, raised := Eq(f, v, w)
   138  		if raised != nil {
   139  			return nil, raised
   140  		}
   141  		ne, raised := NE(f, v, w)
   142  		if raised != nil {
   143  			return nil, raised
   144  		}
   145  		valid := GetBool(eq == True.ToObject() && ne == False.ToObject() || eq == False.ToObject() && ne == True.ToObject()).ToObject()
   146  		if raised := Assert(f, valid, NewStr("invalid values for __eq__ or __ne__").ToObject()); raised != nil {
   147  			return nil, raised
   148  		}
   149  		return eq, nil
   150  	})
   151  	f := NewRootFrame()
   152  	large1, large2 := NewDict(), NewDict()
   153  	largeSize := 100
   154  	for i := 0; i < largeSize; i++ {
   155  		s, raised := ToStr(f, NewInt(i).ToObject())
   156  		if raised != nil {
   157  			t.Fatal(raised)
   158  		}
   159  		large1.SetItem(f, NewInt(i).ToObject(), s.ToObject())
   160  		s, raised = ToStr(f, NewInt(largeSize-i-1).ToObject())
   161  		if raised != nil {
   162  			t.Fatal(raised)
   163  		}
   164  		large2.SetItem(f, NewInt(largeSize-i-1).ToObject(), s.ToObject())
   165  	}
   166  	o := newObject(ObjectType)
   167  	cases := []invokeTestCase{
   168  		{args: wrapArgs(NewDict(), NewDict()), want: True.ToObject()},
   169  		{args: wrapArgs(NewDict(), newTestDict("foo", true)), want: False.ToObject()},
   170  		{args: wrapArgs(newTestDict("foo", "foo"), newTestDict("foo", "foo")), want: True.ToObject()},
   171  		{args: wrapArgs(newTestDict("foo", true), newTestDict("bar", true)), want: False.ToObject()},
   172  		{args: wrapArgs(newTestDict("foo", true), newTestDict("foo", newObject(ObjectType))), want: False.ToObject()},
   173  		{args: wrapArgs(newTestDict("foo", true, "bar", false), newTestDict("bar", true)), want: False.ToObject()},
   174  		{args: wrapArgs(newTestDict("foo", o, "bar", o), newTestDict("foo", o, "bar", o)), want: True.ToObject()},
   175  		{args: wrapArgs(newTestDict(2, None, "foo", o), newTestDict("foo", o, 2, None)), want: True.ToObject()},
   176  		{args: wrapArgs(large1, large2), want: True.ToObject()},
   177  		{args: wrapArgs(NewDict(), 123), want: False.ToObject()},
   178  	}
   179  	for _, cas := range cases {
   180  		if err := runInvokeTestCase(fun, &cas); err != "" {
   181  			t.Error(err)
   182  		}
   183  	}
   184  }
   185  
   186  func TestDictGet(t *testing.T) {
   187  	cases := []invokeTestCase{
   188  		{args: wrapArgs(NewDict(), "foo"), want: None},
   189  		{args: wrapArgs(newTestDict("foo", 1, "bar", 2), "foo"), want: NewInt(1).ToObject()},
   190  		{args: wrapArgs(newTestDict(3, "foo", "bar", 42), 42, "nope"), want: NewStr("nope").ToObject()},
   191  	}
   192  	for _, cas := range cases {
   193  		if err := runInvokeMethodTestCase(DictType, "get", &cas); err != "" {
   194  			t.Error(err)
   195  		}
   196  	}
   197  }
   198  
   199  func TestDictGetItem(t *testing.T) {
   200  	getItem := newBuiltinFunction("TestDictGetItem", func(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   201  		if raised := checkFunctionArgs(f, "TestDictGetItem", args, DictType, ObjectType); raised != nil {
   202  			return nil, raised
   203  		}
   204  		result, raised := toDictUnsafe(args[0]).GetItem(f, args[1])
   205  		if raised == nil && result == nil {
   206  			result = None
   207  		}
   208  		return result, raised
   209  	}).ToObject()
   210  	f := NewRootFrame()
   211  	h, raised := Hash(f, NewStr("foo").ToObject())
   212  	if raised != nil {
   213  		t.Fatal(raised)
   214  	}
   215  	if b, raised := IsTrue(f, mustNotRaise(NE(f, h.ToObject(), hashFoo))); raised != nil {
   216  		t.Fatal(raised)
   217  	} else if b {
   218  		t.Fatalf("hash('foo') = %v, want %v", h, hashFoo)
   219  	}
   220  	deletedItemDict := newTestDict(hashFoo, true, "foo", true)
   221  	deletedItemDict.DelItem(f, hashFoo)
   222  	cases := []invokeTestCase{
   223  		{args: wrapArgs(NewDict(), "foo"), want: None},
   224  		{args: wrapArgs(newStringDict(map[string]*Object{"foo": True.ToObject()}), "foo"), want: True.ToObject()},
   225  		{args: wrapArgs(newTestDict(2, "bar", "baz", 3.14), 2), want: NewStr("bar").ToObject()},
   226  		{args: wrapArgs(newTestDict(2, "bar", "baz", 3.14), 3), want: None},
   227  		{args: wrapArgs(deletedItemDict, hashFoo), want: None},
   228  		{args: wrapArgs(NewDict(), NewList()), wantExc: mustCreateException(TypeErrorType, "unhashable type: 'list'")},
   229  	}
   230  	for _, cas := range cases {
   231  		if err := runInvokeTestCase(getItem, &cas); err != "" {
   232  			t.Error(err)
   233  		}
   234  	}
   235  }
   236  
   237  // BenchmarkDictGetItem is to keep an eye on the speed of contended dict access
   238  // in a fast read loop.
   239  func BenchmarkDictSetItem(b *testing.B) {
   240  	objs := make([]*Object, 128)
   241  	for i := range objs {
   242  		objs[i] = NewInt(i).ToObject()
   243  	}
   244  
   245  	bench := func(n int) func(*testing.B) {
   246  		return func(b *testing.B) {
   247  			f := NewRootFrame()
   248  			var raised *BaseException
   249  			b.ResetTimer()
   250  			for i := 0; i < b.N; i++ {
   251  				d := NewDict()
   252  				for _, o := range objs[:n] {
   253  					raised = d.SetItem(f, o, o)
   254  				}
   255  			}
   256  			runtime.KeepAlive(raised)
   257  		}
   258  	}
   259  	b.Run("3-elements", bench(3))
   260  	b.Run("5-elements", bench(5))
   261  	b.Run("8-elements", bench(8))
   262  	b.Run("12-elements", bench(12))
   263  	b.Run("16-elements", bench(16))
   264  	b.Run("24-elements", bench(24))
   265  	b.Run("32-elements", bench(32))
   266  }
   267  
   268  // BenchmarkDictGetItem is to keep an eye on the speed of contended dict access
   269  // in a fast read loop.
   270  func BenchmarkDictGetItem(b *testing.B) {
   271  	d := newTestDict(
   272  		"foo", 1,
   273  		"bar", 2,
   274  		None, 3,
   275  		4, 5)
   276  	f := NewRootFrame()
   277  	keys := d.Keys(f)
   278  
   279  	b.ResetTimer()
   280  	b.RunParallel(func(pb *testing.PB) {
   281  		f := NewRootFrame()
   282  		var ret *Object
   283  		var raised *BaseException
   284  		for pb.Next() {
   285  			for _, k := range keys.elems {
   286  				ret, raised = d.GetItem(f, k)
   287  			}
   288  		}
   289  		runtime.KeepAlive(ret)
   290  		runtime.KeepAlive(raised)
   291  	})
   292  }
   293  
   294  func BenchmarkDictGetItemBig(b *testing.B) {
   295  	d := newTestDict(
   296  		"foo", 1,
   297  		"bar", 2,
   298  		None, 3,
   299  		4, 5)
   300  
   301  	f := NewRootFrame()
   302  	for j := 100; j < 200; j++ {
   303  		d.SetItemString(f, strconv.Itoa(j), None)
   304  	}
   305  	keys := d.Keys(f)
   306  
   307  	b.ResetTimer()
   308  	b.RunParallel(func(pb *testing.PB) {
   309  		f := NewRootFrame()
   310  		var ret *Object
   311  		var raised *BaseException
   312  		for pb.Next() {
   313  			for _, k := range keys.elems {
   314  				ret, raised = d.GetItem(f, k)
   315  			}
   316  		}
   317  		runtime.KeepAlive(ret)
   318  		runtime.KeepAlive(raised)
   319  	})
   320  }
   321  
   322  func BenchmarkDictIterItems(b *testing.B) {
   323  	bench := func(d *Dict) func(*testing.B) {
   324  		return func(b *testing.B) {
   325  			f := NewRootFrame()
   326  			args := f.MakeArgs(1)
   327  			args[0] = d.ToObject()
   328  			b.ResetTimer()
   329  
   330  			var ret *Object
   331  			var raised *BaseException
   332  			for i := 0; i < b.N; i++ {
   333  				iter, _ := dictIterItems(f, args, nil)
   334  				for {
   335  					ret, raised = Next(f, iter)
   336  					if raised != nil {
   337  						if !raised.isInstance(StopIterationType) {
   338  							b.Fatalf("iteration failed with: %v", raised)
   339  						}
   340  						f.RestoreExc(nil, nil)
   341  						break
   342  					}
   343  				}
   344  			}
   345  			runtime.KeepAlive(ret)
   346  			runtime.KeepAlive(raised)
   347  		}
   348  	}
   349  
   350  	b.Run("0-elements", bench(newTestDict()))
   351  	b.Run("1-elements", bench(newTestDict(1, 2)))
   352  	b.Run("2-elements", bench(newTestDict(1, 2, 3, 4)))
   353  	b.Run("3-elements", bench(newTestDict(1, 2, 3, 4, 5, 6)))
   354  	b.Run("4-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8)))
   355  	b.Run("5-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)))
   356  	b.Run("6-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)))
   357  	b.Run("7-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)))
   358  	b.Run("8-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)))
   359  }
   360  
   361  func BenchmarkDictIterKeys(b *testing.B) {
   362  	bench := func(d *Dict) func(*testing.B) {
   363  		return func(b *testing.B) {
   364  			f := NewRootFrame()
   365  			args := f.MakeArgs(1)
   366  			args[0] = d.ToObject()
   367  			b.ResetTimer()
   368  
   369  			var ret *Object
   370  			var raised *BaseException
   371  			for i := 0; i < b.N; i++ {
   372  				iter, _ := dictIterKeys(f, args, nil)
   373  				for {
   374  					ret, raised = Next(f, iter)
   375  					if raised != nil {
   376  						if !raised.isInstance(StopIterationType) {
   377  							b.Fatalf("iteration failed with: %v", raised)
   378  						}
   379  						f.RestoreExc(nil, nil)
   380  						break
   381  					}
   382  					ret, raised = d.GetItem(f, ret)
   383  					if raised != nil {
   384  						if !raised.isInstance(StopIterationType) {
   385  							b.Fatalf("iteration failed with: %v", raised)
   386  						}
   387  						f.RestoreExc(nil, nil)
   388  						break
   389  					}
   390  				}
   391  			}
   392  			runtime.KeepAlive(ret)
   393  			runtime.KeepAlive(raised)
   394  		}
   395  	}
   396  
   397  	b.Run("0-elements", bench(newTestDict()))
   398  	b.Run("1-elements", bench(newTestDict(1, 2)))
   399  	b.Run("2-elements", bench(newTestDict(1, 2, 3, 4)))
   400  	b.Run("3-elements", bench(newTestDict(1, 2, 3, 4, 5, 6)))
   401  	b.Run("4-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8)))
   402  	b.Run("5-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)))
   403  	b.Run("6-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)))
   404  	b.Run("7-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)))
   405  	b.Run("8-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)))
   406  	b.Run("9-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18)))
   407  }
   408  
   409  func BenchmarkDictIterValues(b *testing.B) {
   410  	bench := func(d *Dict) func(*testing.B) {
   411  		return func(b *testing.B) {
   412  			f := NewRootFrame()
   413  			args := f.MakeArgs(1)
   414  			args[0] = d.ToObject()
   415  			b.ResetTimer()
   416  
   417  			var ret *Object
   418  			var raised *BaseException
   419  			for i := 0; i < b.N; i++ {
   420  				iter, _ := dictIterValues(f, args, nil)
   421  				for {
   422  					ret, raised = Next(f, iter)
   423  					if raised != nil {
   424  						if !raised.isInstance(StopIterationType) {
   425  							b.Fatalf("iteration failed with: %v", raised)
   426  						}
   427  						f.RestoreExc(nil, nil)
   428  						break
   429  					}
   430  				}
   431  			}
   432  			runtime.KeepAlive(ret)
   433  			runtime.KeepAlive(raised)
   434  		}
   435  	}
   436  
   437  	b.Run("0-elements", bench(newTestDict()))
   438  	b.Run("1-elements", bench(newTestDict(1, 2)))
   439  	b.Run("2-elements", bench(newTestDict(1, 2, 3, 4)))
   440  	b.Run("3-elements", bench(newTestDict(1, 2, 3, 4, 5, 6)))
   441  	b.Run("4-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8)))
   442  	b.Run("5-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)))
   443  	b.Run("6-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)))
   444  	b.Run("7-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)))
   445  	b.Run("8-elements", bench(newTestDict(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)))
   446  }
   447  
   448  func TestDictGetItemString(t *testing.T) {
   449  	getItemString := newBuiltinFunction("TestDictGetItemString", func(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   450  		if raised := checkFunctionArgs(f, "TestDictGetItem", args, DictType, StrType); raised != nil {
   451  			return nil, raised
   452  		}
   453  		result, raised := toDictUnsafe(args[0]).GetItemString(f, toStrUnsafe(args[1]).Value())
   454  		if raised == nil && result == nil {
   455  			result = None
   456  		}
   457  		return result, raised
   458  	}).ToObject()
   459  	cases := []invokeTestCase{
   460  		{args: wrapArgs(NewDict(), "foo"), want: None},
   461  		{args: wrapArgs(newTestDict("foo", true), "foo"), want: True.ToObject()},
   462  		{args: wrapArgs(newTestDict(2, "bar", "baz", 3.14), "baz"), want: NewFloat(3.14).ToObject()},
   463  		{args: wrapArgs(newTestDict(2, "bar", "baz", 3.14), "qux"), want: None},
   464  	}
   465  	for _, cas := range cases {
   466  		if err := runInvokeTestCase(getItemString, &cas); err != "" {
   467  			t.Error(err)
   468  		}
   469  	}
   470  }
   471  
   472  func TestDictHasKey(t *testing.T) {
   473  	cases := []invokeTestCase{
   474  		{args: wrapArgs(NewDict(), "foo"), want: False.ToObject()},
   475  		{args: wrapArgs(newTestDict("foo", 1, "bar", 2), "foo"), want: True.ToObject()},
   476  		{args: wrapArgs(newTestDict(3, "foo", "bar", 42), 42), want: False.ToObject()},
   477  	}
   478  	for _, cas := range cases {
   479  		if err := runInvokeMethodTestCase(DictType, "has_key", &cas); err != "" {
   480  			t.Error(err)
   481  		}
   482  	}
   483  }
   484  
   485  func TestDictItemIteratorIter(t *testing.T) {
   486  	iter := &newDictItemIterator(NewDict()).Object
   487  	cas := &invokeTestCase{args: wrapArgs(iter), want: iter}
   488  	if err := runInvokeMethodTestCase(dictItemIteratorType, "__iter__", cas); err != "" {
   489  		t.Error(err)
   490  	}
   491  }
   492  
   493  func TestDictItemIterModified(t *testing.T) {
   494  	f := NewRootFrame()
   495  	iterItems := mustNotRaise(GetAttr(f, DictType.ToObject(), NewStr("iteritems"), nil))
   496  	d := NewDict()
   497  	iter := mustNotRaise(iterItems.Call(f, wrapArgs(d), nil))
   498  	if raised := d.SetItemString(f, "foo", None); raised != nil {
   499  		t.Fatal(raised)
   500  	}
   501  	cas := invokeTestCase{
   502  		args:    wrapArgs(iter),
   503  		wantExc: mustCreateException(RuntimeErrorType, "dictionary changed during iteration"),
   504  	}
   505  	if err := runInvokeMethodTestCase(dictItemIteratorType, "next", &cas); err != "" {
   506  		t.Error(err)
   507  	}
   508  }
   509  
   510  func TestDictIter(t *testing.T) {
   511  	iter := newBuiltinFunction("TestDictIter", func(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   512  		if raised := checkFunctionArgs(f, "TestDictIter", args, DictType); raised != nil {
   513  			return nil, raised
   514  		}
   515  		iter, raised := Iter(f, args[0])
   516  		if raised != nil {
   517  			return nil, raised
   518  		}
   519  		return TupleType.Call(f, []*Object{iter}, nil)
   520  	}).ToObject()
   521  	f := NewRootFrame()
   522  	deletedItemDict := newTestDict(hashFoo, None, "foo", None)
   523  	deletedItemDict.DelItem(f, hashFoo)
   524  	cases := []invokeTestCase{
   525  		{args: wrapArgs(NewDict()), want: NewTuple().ToObject()},
   526  		{args: wrapArgs(newTestDict("foo", 1, "bar", 2)), want: newTestTuple("foo", "bar").ToObject()},
   527  		{args: wrapArgs(newTestDict(123, True, "foo", False)), want: newTestTuple(123, "foo").ToObject()},
   528  		{args: wrapArgs(deletedItemDict), want: newTestTuple("foo").ToObject()},
   529  	}
   530  	for _, cas := range cases {
   531  		if err := runInvokeTestCase(iter, &cas); err != "" {
   532  			t.Error(err)
   533  		}
   534  	}
   535  }
   536  
   537  func TestDictIterKeys(t *testing.T) {
   538  	iterkeys := mustNotRaise(GetAttr(NewRootFrame(), DictType.ToObject(), NewStr("iterkeys"), nil))
   539  	fun := wrapFuncForTest(func(f *Frame, args ...*Object) (*Object, *BaseException) {
   540  		iter, raised := iterkeys.Call(f, args, nil)
   541  		if raised != nil {
   542  			return nil, raised
   543  		}
   544  		return TupleType.Call(f, Args{iter}, nil)
   545  	})
   546  	cases := []invokeTestCase{
   547  		{args: wrapArgs(NewDict()), want: NewTuple().ToObject()},
   548  		{args: wrapArgs(newTestDict("foo", 1, "bar", 2)), want: newTestTuple("foo", "bar").ToObject()},
   549  		{args: wrapArgs(NewDict(), "bad"), wantExc: mustCreateException(TypeErrorType, "'iterkeys' of 'dict' requires 1 arguments")},
   550  	}
   551  	for _, cas := range cases {
   552  		if err := runInvokeTestCase(fun, &cas); err != "" {
   553  			t.Error(err)
   554  		}
   555  	}
   556  }
   557  
   558  func TestDictIterValues(t *testing.T) {
   559  	itervalues := mustNotRaise(GetAttr(NewRootFrame(), DictType.ToObject(), NewStr("itervalues"), nil))
   560  	fun := wrapFuncForTest(func(f *Frame, args ...*Object) (*Object, *BaseException) {
   561  		iter, raised := itervalues.Call(f, args, nil)
   562  		if raised != nil {
   563  			return nil, raised
   564  		}
   565  		return TupleType.Call(f, Args{iter}, nil)
   566  	})
   567  	cases := []invokeTestCase{
   568  		{args: wrapArgs(NewDict()), want: NewTuple().ToObject()},
   569  		{args: wrapArgs(newTestDict("foo", 1, "bar", 2)), want: newTestTuple(1, 2).ToObject()},
   570  		{args: wrapArgs(NewDict(), "bad"), wantExc: mustCreateException(TypeErrorType, "'itervalues' of 'dict' requires 1 arguments")},
   571  	}
   572  	for _, cas := range cases {
   573  		if err := runInvokeTestCase(fun, &cas); err != "" {
   574  			t.Error(err)
   575  		}
   576  	}
   577  }
   578  
   579  // Tests dict.items and dict.iteritems.
   580  func TestDictItems(t *testing.T) {
   581  	f := NewRootFrame()
   582  	iterItems := mustNotRaise(GetAttr(f, DictType.ToObject(), NewStr("iteritems"), nil))
   583  	items := newBuiltinFunction("TestDictIterItems", func(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   584  		if raised := checkFunctionArgs(f, "TestDictIterItems", args, DictType); raised != nil {
   585  			return nil, raised
   586  		}
   587  		iter, raised := iterItems.Call(f, []*Object{args[0]}, nil)
   588  		if raised != nil {
   589  			return nil, raised
   590  		}
   591  		return ListType.Call(f, []*Object{iter}, nil)
   592  	}).ToObject()
   593  	deletedItemDict := newTestDict(hashFoo, None, "foo", None)
   594  	deletedItemDict.DelItem(f, hashFoo)
   595  	cases := []invokeTestCase{
   596  		{args: wrapArgs(NewDict()), want: NewList().ToObject()},
   597  		{args: wrapArgs(newTestDict("foo", 1, "bar", 2)), want: newTestList(newTestTuple("foo", 1), newTestTuple("bar", 2)).ToObject()},
   598  		{args: wrapArgs(newTestDict(123, True, "foo", False)), want: newTestList(newTestTuple(123, true), newTestTuple("foo", false)).ToObject()},
   599  		{args: wrapArgs(deletedItemDict), want: newTestList(newTestTuple("foo", None)).ToObject()},
   600  	}
   601  	for _, cas := range cases {
   602  		if err := runInvokeTestCase(items, &cas); err != "" {
   603  			t.Error(err)
   604  		}
   605  		if err := runInvokeMethodTestCase(DictType, "items", &cas); err != "" {
   606  			t.Error(err)
   607  		}
   608  	}
   609  }
   610  
   611  func TestDictKeyIteratorIter(t *testing.T) {
   612  	iter := &newDictKeyIterator(NewDict()).Object
   613  	cas := &invokeTestCase{args: wrapArgs(iter), want: iter}
   614  	if err := runInvokeMethodTestCase(dictKeyIteratorType, "__iter__", cas); err != "" {
   615  		t.Error(err)
   616  	}
   617  }
   618  
   619  func TestDictKeyIterModified(t *testing.T) {
   620  	f := NewRootFrame()
   621  	d := NewDict()
   622  	iter := mustNotRaise(Iter(f, d.ToObject()))
   623  	if raised := d.SetItemString(f, "foo", None); raised != nil {
   624  		t.Fatal(raised)
   625  	}
   626  	cas := invokeTestCase{
   627  		args:    wrapArgs(iter),
   628  		wantExc: mustCreateException(RuntimeErrorType, "dictionary changed during iteration"),
   629  	}
   630  	if err := runInvokeMethodTestCase(dictKeyIteratorType, "next", &cas); err != "" {
   631  		t.Error(err)
   632  	}
   633  }
   634  
   635  func TestDictKeys(t *testing.T) {
   636  	cases := []invokeTestCase{
   637  		{args: wrapArgs(NewDict()), want: NewList().ToObject()},
   638  		{args: wrapArgs(newTestDict("foo", None, 42, None)), want: newTestList("foo", 42).ToObject()},
   639  	}
   640  	for _, cas := range cases {
   641  		if err := runInvokeMethodTestCase(DictType, "keys", &cas); err != "" {
   642  			t.Error(err)
   643  		}
   644  	}
   645  }
   646  
   647  func TestDictPop(t *testing.T) {
   648  	cases := []invokeTestCase{
   649  		{args: wrapArgs(newTestDict("foo", 42), "foo"), want: NewInt(42).ToObject()},
   650  		{args: wrapArgs(NewDict(), "foo", 42), want: NewInt(42).ToObject()},
   651  		{args: wrapArgs(NewDict(), "foo"), wantExc: mustCreateException(KeyErrorType, "foo")},
   652  	}
   653  	for _, cas := range cases {
   654  		if err := runInvokeMethodTestCase(DictType, "pop", &cas); err != "" {
   655  			t.Error(err)
   656  		}
   657  	}
   658  }
   659  
   660  func TestDictPopItem(t *testing.T) {
   661  	popItem := mustNotRaise(GetAttr(NewRootFrame(), DictType.ToObject(), NewStr("popitem"), nil))
   662  	fun := wrapFuncForTest(func(f *Frame, d *Dict) (*Object, *BaseException) {
   663  		result := NewDict()
   664  		item, raised := popItem.Call(f, wrapArgs(d), nil)
   665  		for ; raised == nil; item, raised = popItem.Call(f, wrapArgs(d), nil) {
   666  			t := toTupleUnsafe(item)
   667  			result.SetItem(f, t.GetItem(0), t.GetItem(1))
   668  		}
   669  		if raised != nil {
   670  			if !raised.isInstance(KeyErrorType) {
   671  				return nil, raised
   672  			}
   673  			f.RestoreExc(nil, nil)
   674  		}
   675  		if raised = Assert(f, GetBool(d.Len() == 0).ToObject(), nil); raised != nil {
   676  			return nil, raised
   677  		}
   678  		return result.ToObject(), nil
   679  	})
   680  	cases := []invokeTestCase{
   681  		{args: wrapArgs(newTestDict("foo", 42)), want: newTestDict("foo", 42).ToObject()},
   682  		{args: wrapArgs(newTestDict("foo", 42, 123, "bar")), want: newTestDict("foo", 42, 123, "bar").ToObject()},
   683  	}
   684  	for _, cas := range cases {
   685  		if err := runInvokeTestCase(fun, &cas); err != "" {
   686  			t.Error(err)
   687  		}
   688  	}
   689  }
   690  
   691  func TestDictNewInit(t *testing.T) {
   692  	cases := []invokeTestCase{
   693  		{args: wrapArgs(), want: NewDict().ToObject()},
   694  		{args: wrapArgs(newTestDict("foo", 42)), want: newTestDict("foo", 42).ToObject()},
   695  		{args: wrapArgs(), kwargs: wrapKWArgs("foo", 42), want: newTestDict("foo", 42).ToObject()},
   696  		{args: wrapArgs(newTestDict("foo", 42)), kwargs: wrapKWArgs("foo", "bar"), want: newTestDict("foo", "bar").ToObject()},
   697  		{args: wrapArgs(newTestList(newTestTuple("baz", 42))), kwargs: wrapKWArgs("foo", "bar"), want: newTestDict("baz", 42, "foo", "bar").ToObject()},
   698  		{args: wrapArgs(True), wantExc: mustCreateException(TypeErrorType, "'bool' object is not iterable")},
   699  		{args: wrapArgs(NewList(), "foo"), wantExc: mustCreateException(TypeErrorType, "'__init__' requires 1 arguments")},
   700  	}
   701  	for _, cas := range cases {
   702  		if err := runInvokeTestCase(DictType.ToObject(), &cas); err != "" {
   703  			t.Error(err)
   704  		}
   705  	}
   706  }
   707  
   708  func TestDictNewRaises(t *testing.T) {
   709  	cases := []invokeTestCase{
   710  		{args: wrapArgs(), wantExc: mustCreateException(TypeErrorType, "'__new__' requires 1 arguments")},
   711  		{args: wrapArgs(123), wantExc: mustCreateException(TypeErrorType, `'__new__' requires a 'type' object but received a "int"`)},
   712  		{args: wrapArgs(NoneType), wantExc: mustCreateException(TypeErrorType, "dict.__new__(NoneType): NoneType is not a subtype of dict")},
   713  	}
   714  	for _, cas := range cases {
   715  		if err := runInvokeMethodTestCase(DictType, "__new__", &cas); err != "" {
   716  			t.Error(err)
   717  		}
   718  	}
   719  }
   720  
   721  func TestDictSetDefault(t *testing.T) {
   722  	setDefaultMethod := mustNotRaise(GetAttr(NewRootFrame(), DictType.ToObject(), NewStr("setdefault"), nil))
   723  	setDefault := newBuiltinFunction("TestDictSetDefault", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
   724  		i, raised := setDefaultMethod.Call(f, args, kwargs)
   725  		if raised != nil {
   726  			return nil, raised
   727  		}
   728  		return NewTuple(i, args[0]).ToObject(), nil
   729  	}).ToObject()
   730  	cases := []invokeTestCase{
   731  		{args: wrapArgs(NewDict(), "foo"), want: newTestTuple(None, newTestDict("foo", None)).ToObject()},
   732  		{args: wrapArgs(NewDict(), "foo", 42), want: newTestTuple(42, newTestDict("foo", 42)).ToObject()},
   733  		{args: wrapArgs(newTestDict("foo", 42), "foo"), want: newTestTuple(42, newTestDict("foo", 42)).ToObject()},
   734  		{args: wrapArgs(newTestDict("foo", 42), "foo", 43), want: newTestTuple(42, newTestDict("foo", 42)).ToObject()},
   735  		{args: wrapArgs(NewDict()), wantExc: mustCreateException(TypeErrorType, "setdefault expected at least 1 arguments, got 0")},
   736  		{args: wrapArgs(NewDict(), "foo", "bar", "baz"), wantExc: mustCreateException(TypeErrorType, "setdefault expected at most 2 arguments, got 3")},
   737  	}
   738  	for _, cas := range cases {
   739  		if err := runInvokeTestCase(setDefault, &cas); err != "" {
   740  			t.Error(err)
   741  		}
   742  	}
   743  }
   744  
   745  func TestDictSetItem(t *testing.T) {
   746  	setItem := newBuiltinFunction("TestDictSetItem", func(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   747  		if raised := checkFunctionArgs(f, "TestDictSetItem", args, DictType, ObjectType, ObjectType); raised != nil {
   748  			return nil, raised
   749  		}
   750  		d := toDictUnsafe(args[0])
   751  		if raised := d.SetItem(f, args[1], args[2]); raised != nil {
   752  			return nil, raised
   753  		}
   754  		return d.ToObject(), nil
   755  	}).ToObject()
   756  	f := NewRootFrame()
   757  	o := newObject(ObjectType)
   758  	deletedItemDict := newStringDict(map[string]*Object{"foo": None})
   759  	if _, raised := deletedItemDict.DelItemString(f, "foo"); raised != nil {
   760  		t.Fatal(raised)
   761  	}
   762  	modifiedDict := newTestDict(0, None)
   763  	modifiedType := newTestClass("Foo", []*Type{IntType}, newStringDict(map[string]*Object{
   764  		"__eq__": newBuiltinFunction("__eq__", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
   765  			for i := 1000; i < 1100; i++ {
   766  				if raised := modifiedDict.SetItem(f, NewInt(i).ToObject(), None); raised != nil {
   767  					return nil, raised
   768  				}
   769  			}
   770  			return False.ToObject(), nil
   771  		}).ToObject(),
   772  	}))
   773  	cases := []invokeTestCase{
   774  		{args: wrapArgs(NewDict(), "foo", o), want: newStringDict(map[string]*Object{"foo": o}).ToObject()},
   775  		{args: wrapArgs(newStringDict(map[string]*Object{"foo": NewInt(1).ToObject()}), "foo", 2), want: newStringDict(map[string]*Object{"foo": NewInt(2).ToObject()}).ToObject()},
   776  		{args: wrapArgs(newTestDict(2, None, "baz", 3.14), 2, o), want: newTestDict(2, o, "baz", 3.14).ToObject()},
   777  		{args: wrapArgs(deletedItemDict, "foo", o), want: newStringDict(map[string]*Object{"foo": o}).ToObject()},
   778  		{args: wrapArgs(NewDict(), NewList(), None), wantExc: mustCreateException(TypeErrorType, "unhashable type: 'list'")},
   779  		{args: wrapArgs(modifiedDict, newObject(modifiedType), None), wantExc: mustCreateException(RuntimeErrorType, "dictionary changed during write")},
   780  	}
   781  	for _, cas := range cases {
   782  		if err := runInvokeTestCase(setItem, &cas); err != "" {
   783  			t.Error(err)
   784  		}
   785  	}
   786  }
   787  
   788  func TestDictSetItemString(t *testing.T) {
   789  	setItemString := newBuiltinFunction("TestDictSetItemString", func(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   790  		if raised := checkFunctionArgs(f, "TestDictSetItemString", args, DictType, StrType, ObjectType); raised != nil {
   791  			return nil, raised
   792  		}
   793  		d := toDictUnsafe(args[0])
   794  		if raised := d.SetItemString(f, toStrUnsafe(args[1]).Value(), args[2]); raised != nil {
   795  			return nil, raised
   796  		}
   797  		return d.ToObject(), nil
   798  	}).ToObject()
   799  	o := newObject(ObjectType)
   800  	cases := []invokeTestCase{
   801  		{args: wrapArgs(NewDict(), "foo", o), want: newStringDict(map[string]*Object{"foo": o}).ToObject()},
   802  		{args: wrapArgs(newStringDict(map[string]*Object{"foo": NewInt(1).ToObject()}), "foo", 2), want: newStringDict(map[string]*Object{"foo": NewInt(2).ToObject()}).ToObject()},
   803  		{args: wrapArgs(newTestDict(2, None, "baz", 3.14), "baz", o), want: newTestDict(2, None, "baz", o).ToObject()},
   804  		{args: wrapArgs(newTestDict(hashFoo, o, "foo", None), "foo", 3.14), want: newTestDict(hashFoo, o, "foo", 3.14).ToObject()},
   805  	}
   806  	for _, cas := range cases {
   807  		if err := runInvokeTestCase(setItemString, &cas); err != "" {
   808  			t.Error(err)
   809  		}
   810  	}
   811  }
   812  
   813  func TestDictStrRepr(t *testing.T) {
   814  	recursiveDict := NewDict()
   815  	if raised := recursiveDict.SetItemString(NewRootFrame(), "key", recursiveDict.ToObject()); raised != nil {
   816  		t.Fatal(raised)
   817  	}
   818  	cases := []struct {
   819  		o            *Object
   820  		wantPatterns []string
   821  	}{
   822  		{NewDict().ToObject(), []string{"^{}$"}},
   823  		{newStringDict(map[string]*Object{"foo": NewStr("foo value").ToObject()}).ToObject(), []string{`^\{'foo': 'foo value'\}$`}},
   824  		{newStringDict(map[string]*Object{"foo": NewStr("foo value").ToObject(), "bar": NewStr("bar value").ToObject()}).ToObject(), []string{`^{.*, .*}$`, `'foo': 'foo value'`, `'bar': 'bar value'`}},
   825  		{recursiveDict.ToObject(), []string{`^{'key': {\.\.\.}}$`}},
   826  	}
   827  	for _, cas := range cases {
   828  		fun := wrapFuncForTest(func(f *Frame) *BaseException {
   829  			for _, pattern := range cas.wantPatterns {
   830  				re := regexp.MustCompile(pattern)
   831  				s, raised := ToStr(f, cas.o)
   832  				if raised != nil {
   833  					return raised
   834  				}
   835  				if !re.MatchString(s.Value()) {
   836  					t.Errorf("str(%v) = %v, want %q", cas.o, s, re)
   837  				}
   838  				s, raised = Repr(f, cas.o)
   839  				if raised != nil {
   840  					return raised
   841  				}
   842  				if !re.MatchString(s.Value()) {
   843  					t.Errorf("repr(%v) = %v, want %q", cas.o, s, re)
   844  				}
   845  			}
   846  			return nil
   847  		})
   848  		if err := runInvokeTestCase(fun, &invokeTestCase{want: None}); err != "" {
   849  			t.Error(err)
   850  		}
   851  	}
   852  }
   853  
   854  func TestDictUpdate(t *testing.T) {
   855  	updateMethod := mustNotRaise(GetAttr(NewRootFrame(), DictType.ToObject(), NewStr("update"), nil))
   856  	update := newBuiltinFunction("TestDictUpdate", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
   857  		if raised := checkFunctionVarArgs(f, "TestDictUpdate", args, DictType); raised != nil {
   858  			return nil, raised
   859  		}
   860  		if _, raised := updateMethod.Call(f, args, kwargs); raised != nil {
   861  			return nil, raised
   862  		}
   863  		return args[0], nil
   864  	}).ToObject()
   865  	cases := []invokeTestCase{
   866  		{args: wrapArgs(newTestDict(42, "foo")), want: newTestDict(42, "foo").ToObject()},
   867  		{args: wrapArgs(NewDict(), NewDict()), want: NewDict().ToObject()},
   868  		{args: wrapArgs(NewDict(), newTestDict("foo", 42, "bar", 43)), want: newTestDict("foo", 42, "bar", 43).ToObject()},
   869  		{args: wrapArgs(newTestDict(123, None), newTestDict(124, True)), want: newTestDict(123, None, 124, True).ToObject()},
   870  		{args: wrapArgs(newTestDict("foo", 3.14), newTestDict("foo", "bar")), want: newTestDict("foo", "bar").ToObject()},
   871  		{args: wrapArgs(NewDict(), NewTuple()), want: NewDict().ToObject()},
   872  		{args: wrapArgs(NewDict(), newTestList(newTestTuple("foo", 42), newTestTuple("bar", 43))), want: newTestDict("foo", 42, "bar", 43).ToObject()},
   873  		{args: wrapArgs(newTestDict(123, None), newTestTuple(newTestTuple(124, True))), want: newTestDict(123, None, 124, True).ToObject()},
   874  		{args: wrapArgs(newTestDict("foo", 3.14), newTestList(newTestList("foo", "bar"))), want: newTestDict("foo", "bar").ToObject()},
   875  		{args: wrapArgs(NewDict(), None), wantExc: mustCreateException(TypeErrorType, "'NoneType' object is not iterable")},
   876  		{args: wrapArgs(NewDict(), newTestTuple(newTestList(None, 42, "foo"))), wantExc: mustCreateException(ValueErrorType, "dictionary update sequence element has length 3; 2 is required")},
   877  		{args: wrapArgs(NewDict()), want: NewDict().ToObject()},
   878  		{args: wrapArgs(NewDict()), kwargs: wrapKWArgs("foo", "bar"), want: newTestDict("foo", "bar").ToObject()},
   879  		{args: wrapArgs(newTestDict("foo", 1, "bar", 3.14), newTestDict("foo", 2)), kwargs: wrapKWArgs("foo", 3), want: newTestDict("foo", 3, "bar", 3.14).ToObject()},
   880  	}
   881  	for _, cas := range cases {
   882  		if err := runInvokeTestCase(update, &cas); err != "" {
   883  			t.Error(err)
   884  		}
   885  	}
   886  }
   887  
   888  func TestDictValues(t *testing.T) {
   889  	cases := []invokeTestCase{
   890  		{args: wrapArgs(NewDict()), want: NewList().ToObject()},
   891  		{args: wrapArgs(newTestDict("foo", 1, "bar", 2)), want: newTestList(1, 2).ToObject()},
   892  		{args: wrapArgs(NewDict(), "bad"), wantExc: mustCreateException(TypeErrorType, "'values' of 'dict' requires 1 arguments")},
   893  	}
   894  	for _, cas := range cases {
   895  		if err := runInvokeMethodTestCase(DictType, "values", &cas); err != "" {
   896  			t.Error(err)
   897  		}
   898  	}
   899  }
   900  
   901  func TestParallelDictUpdates(t *testing.T) {
   902  	keys := []*Object{
   903  		NewStr("abc").ToObject(),
   904  		NewStr("def").ToObject(),
   905  		NewStr("ghi").ToObject(),
   906  		NewStr("jkl").ToObject(),
   907  		NewStr("mno").ToObject(),
   908  		NewStr("pqr").ToObject(),
   909  		NewStr("stu").ToObject(),
   910  		NewStr("vwx").ToObject(),
   911  		NewStr("yz0").ToObject(),
   912  		NewStr("123").ToObject(),
   913  		NewStr("456").ToObject(),
   914  		NewStr("789").ToObject(),
   915  		NewStr("ABC").ToObject(),
   916  		NewStr("DEF").ToObject(),
   917  		NewStr("GHI").ToObject(),
   918  		NewStr("JKL").ToObject(),
   919  		NewStr("MNO").ToObject(),
   920  		NewStr("PQR").ToObject(),
   921  		NewStr("STU").ToObject(),
   922  		NewStr("VWX").ToObject(),
   923  		NewStr("YZ)").ToObject(),
   924  		NewStr("!@#").ToObject(),
   925  		NewStr("$%^").ToObject(),
   926  		NewStr("&*(").ToObject(),
   927  	}
   928  
   929  	var started, finished sync.WaitGroup
   930  	stop := make(chan struct{})
   931  	runner := func(f func(*Frame, *Object, int)) {
   932  		for i := 0; i < 8; i++ {
   933  			started.Add(1)
   934  			finished.Add(1)
   935  			go func() {
   936  				defer finished.Done()
   937  				frame := NewRootFrame()
   938  				i := 0
   939  				for _, k := range keys {
   940  					f(frame, k, i)
   941  					frame.RestoreExc(nil, nil)
   942  					i++
   943  				}
   944  				started.Done()
   945  				for {
   946  					if _, ok := <-stop; !ok {
   947  						break
   948  					}
   949  					for _, k := range keys {
   950  						f(frame, k, i)
   951  						frame.RestoreExc(nil, nil)
   952  						i++
   953  					}
   954  				}
   955  			}()
   956  		}
   957  	}
   958  
   959  	d := NewDict().ToObject()
   960  	runner(func(f *Frame, k *Object, _ int) {
   961  		GetItem(f, d, k)
   962  	})
   963  
   964  	runner(func(f *Frame, k *Object, i int) {
   965  		mustNotRaise(nil, SetItem(f, d, k, NewInt(i).ToObject()))
   966  	})
   967  
   968  	runner(func(f *Frame, k *Object, _ int) {
   969  		DelItem(f, d, k)
   970  	})
   971  
   972  	started.Wait()
   973  	time.AfterFunc(time.Second, func() { close(stop) })
   974  	finished.Wait()
   975  }
   976  
   977  func newTestDict(elems ...interface{}) *Dict {
   978  	if len(elems)%2 != 0 {
   979  		panic("invalid test dict spec")
   980  	}
   981  	numItems := len(elems) / 2
   982  	d := NewDict()
   983  	f := NewRootFrame()
   984  	for i := 0; i < numItems; i++ {
   985  		k := mustNotRaise(WrapNative(f, reflect.ValueOf(elems[i*2])))
   986  		v := mustNotRaise(WrapNative(f, reflect.ValueOf(elems[i*2+1])))
   987  		d.SetItem(f, k, v)
   988  	}
   989  	return d
   990  }