Permalink
Feb 1, 2020
May 17, 2019
May 17, 2019
May 17, 2019
May 17, 2019
May 17, 2019
Feb 1, 2020
Jan 15, 2018
Apr 3, 2019
Apr 3, 2019
Feb 1, 2020
Apr 3, 2019
Apr 3, 2019
Apr 3, 2019
Dec 13, 2018
Nov 6, 2019
Dec 10, 2018
Dec 10, 2018
Mar 7, 2018
Feb 26, 2018
Feb 1, 2020
Dec 10, 2018
Dec 10, 2018
Dec 10, 2018
Jul 14, 2020
Jul 31, 2018
Jul 31, 2018
Dec 10, 2018
Nov 2, 2018
Jul 31, 2018
Jun 11, 2020
Dec 10, 2018
Jun 11, 2020
Apr 18, 2020
Jun 11, 2020
May 27, 2018
Apr 18, 2020
Feb 1, 2020
Feb 3, 2018
Feb 1, 2020
Jul 4, 2020
Jul 4, 2020
Mar 7, 2018
Mar 7, 2018
Nov 9, 2015
May 17, 2019
Feb 26, 2018
Apr 3, 2019
May 17, 2019
Feb 26, 2018
Apr 3, 2019
Dec 13, 2018
Mar 7, 2018
Mar 7, 2018
Feb 26, 2018
Apr 14, 2019
Dec 13, 2018
May 17, 2019
Feb 26, 2018
Apr 3, 2019
Dec 1, 2018
Jun 29, 2020
Jun 28, 2018
May 16, 2020
May 16, 2020
Feb 1, 2020
Mar 7, 2018
Aug 2, 2018
Feb 26, 2018
Newer
100644
1087 lines (971 sloc)
31.8 KB
3
/*
4
Implementation of Python dictionaries
5
6
We can't use Javascript's Map here, because the behaviour is not exactly the
7
same (eg with keys that are instances of classes with a __hash__ method...)
8
and because Map is much slower than regular Javascript objects.
9
10
A Python dictionary is implemented as a Javascript objects with these
11
attributes:
12
. $version: an integer with an initial value of 0, incremented at each
13
insertion
14
. $numeric_dict: for keys of type int
15
. $string_dict and $str_hash: for keys of type str
16
. $object_dict: for keys of other types
17
18
The value associated to a key in $numeric_dict and $string_dict is a pair
19
[value, rank] where "value" is the value associated with the key and "rank"
20
is the value of the dict attribute $version when the pair is inserted. This
21
is required to keep track of the insertion order, mandatory since Python 3.7.
22
23
For keys that are not str or int, their hash value is computed. Since several
34
var set_ops = ["eq", "add", "sub", "and", "or", "xor", "le", "lt", "ge", "gt"]
35
36
$B.make_view = function(name, set_like){
37
var klass = $B.make_class(name, function(items){
38
return {
39
__class__: klass,
41
counter: -1,
42
items: items,
43
len: items.length
44
}
45
})
46
47
if(set_like){
48
for(var i = 0, len = set_ops.length; i < len; i++){
49
var op = "__" + set_ops[i] + "__"
50
klass[op] = (function(op){
51
return function(self, other){
52
// compare set of items to other
53
return _b_.set[op](_b_.set.$factory(self),
54
_b_.set.$factory(other))
55
}
56
})(op)
57
}
58
}
59
klass.__iter__ = function(self){
60
var it = klass.$iterator.$factory(self.items)
61
it.len_func = self.len_func
62
return it
63
}
69
klass.__repr__ = function(self){
70
return klass.$infos.__name__ + '(' + _b_.repr(self.items) + ')'
71
}
72
74
return klass
75
}
76
77
// Special version of __next__ for iterators on dict keys / values / items.
78
// Checks that the dictionary size didn't change during iteration.
79
function dict_iterator_next(self){
80
if(self.len_func() != self.len){
82
}
83
self.counter++
84
if(self.counter < self.items.length){
85
return self.items[self.counter]
86
}
87
throw _b_.StopIteration.$factory("StopIteration")
88
}
89
101
dict.$to_obj = function(d){
102
// Function applied to dictionary that only have string keys,
103
// return a Javascript objects with the kays mapped to the value,
104
// excluding the insertion rank
105
var res = {}
106
for(var key in d.$string_dict){
107
res[key] = d.$string_dict[key][0]
108
}
109
return res
110
}
111
121
if(val === undefined){val = _b_.NotImplemented}
122
else if(val === null){val = $N}
126
}else{
127
for(var k in d.$numeric_dict){
128
items.push([parseFloat(k), d.$numeric_dict[k]])
129
}
133
for(var k in d.$object_dict){
134
d.$object_dict[k].forEach(function(item){
135
items.push(item)
136
})
137
}
138
// sort by insertion order
139
items.sort(function(a, b){
140
return a[1][1] - b[1][1]
141
})
142
items = items.map(function(item){return [item[0], item[1][0]]})
145
if(ix !== undefined){
146
return items.map(function(item){return item[ix]})
147
}else{
148
items.__class__ = _b_.tuple
149
return items.map(function(item){
150
item.__class__ = _b_.tuple; return item}
151
)
152
}
155
$B.dict_to_list = to_list // used in py_types.js
156
157
// Special version of __next__ for iterators on dict keys / values / items.
158
// Checks that the dictionary size didn't change during iteration.
159
function dict_iterator_next(self){
160
if(self.len_func() != self.len){
162
}
163
self.counter++
164
if(self.counter < self.items.length){
165
return self.items[self.counter]
177
si(left, _l[i][0], _l[i][1])
178
if(right.$version != right_version){
179
throw _b_.RuntimeError.$factory("dict mutated during update")
180
}
181
}
184
function rank(self, hash, key){
185
// Search if object key, with hash = hash(key), is in
186
// self.$object_dict
187
var pairs = self.$object_dict[hash]
188
if(pairs !== undefined){
189
for(var i = 0, len = pairs.length; i < len; i++){
190
if($B.rich_comp("__eq__", key, pairs[i][0])){
191
return i
192
}
193
}
194
}
195
return -1
196
}
197
206
var $ = $B.args("__contains__", 2, {self: null, key: null},
207
["self", "key"], arguments, {}, null, null),
210
if(self.$is_namespace){key = $B.to_alias(key)} // issue 1244
211
212
if(self.$jsobj){
213
return self.$jsobj[key] !== undefined
214
}
220
return self.$numeric_dict[key] !== undefined
221
}
222
223
var hash = _b_.hash(key)
224
if(self.$str_hash[hash] !== undefined &&
225
$B.rich_comp("__eq__", key, self.$str_hash[hash])){return true}
226
if(self.$numeric_dict[hash] !== undefined &&
227
$B.rich_comp("__eq__", key, hash)){return true}
228
return rank(self, hash, key) > -1
233
var $ = $B.args("__eq__", 2, {self: null, arg: null},
234
["self", "arg"], arguments, {}, null, null),
243
switch(typeof arg){
244
case "string":
245
if(self.$string_dict[arg] === undefined){
265
if((ix = rank(self, hash, arg)) > -1){
266
self.$object_dict[hash].splice(ix, 1)
267
}else{
276
var $ = $B.args("__eq__", 2, {self: null, other: null},
277
["self", "other"], arguments, {}, null, null),
283
if(self.$jsobj){self = jsobj2dict(self.$jsobj)}
284
if(other.$jsobj){other = jsobj2dict(other.$jsobj)}
295
if(!$B.rich_comp("__eq__", other.$numeric_dict[k][0],
296
self.$numeric_dict[k][0])){
300
var pairs = other.$object_dict[k],
301
flag = false
302
for(var i = 0, len = pairs.length; i < len; i++){
303
if($B.rich_comp("__eq__", k, pairs[i][0]) &&
304
$B.rich_comp("__eq__", self.$numeric_dict[k],
305
pairs[i][1])){
306
flag = true
307
break
308
}
323
var pairs = self.$object_dict[hash]
324
// Get all (key, value) pairs in other that have the same hash
325
var other_pairs = []
326
if(other.$numeric_dict[hash] !== undefined){
327
other_pairs.push([hash, other.$numeric_dict[hash]])
328
}
330
other_pairs = other_pairs.concat(other.$object_dict[hash])
331
}
332
if(other_pairs.length == 0){
333
return false
334
}
335
for(var i = 0, len_i = pairs.length; i < len_i; i++){
336
var flag = false
337
var key = pairs[i][0],
339
for(var j = 0, len_j = other_pairs.length; j < len_j; j++){
340
if($B.rich_comp("__eq__", key, other_pairs[j][0]) &&
355
var $ = $B.args("__getitem__", 2, {self: null, arg: null},
356
["self", "arg"], arguments, {}, null, null),
372
373
switch(typeof arg){
374
case "string":
375
if(self.$string_dict[arg] !== undefined){
383
break
384
}
385
386
// since the key is more complex use 'default' method of getting item
387
457
for(var key in first){
458
self.$string_dict[key] = [first[key], self.$order++]
459
}
460
return _b_.None
461
}else if(first.$jsobj){
462
self.$jsobj = {}
463
for(var attr in first.$jsobj){
464
self.$jsobj[attr] = first.$jsobj[attr]
474
arguments, {}, "first", "second")
475
var args = $.first
476
if(args.length > 1){
477
throw _b_.TypeError.$factory("dict expected at most 1 argument" +
478
", got 2")
479
}else if(args.length == 1){
480
args = args[0]
481
if(args.__class__ === dict){
482
['$string_dict', '$str_hash', '$numeric_dict', '$object_dict'].
483
forEach(function(d){
484
for(key in args[d]){self[d][key] = args[d][key]}
485
})
489
var keys = $B.$getattr(args, "keys", null)
490
if(keys !== null){
491
var gi = $B.$getattr(args, "__getitem__", null)
492
if(gi !== null){
493
// has keys and __getitem__ : it's a mapping, iterate on
494
// keys and values
495
gi = $B.$call(gi)
496
var kiter = _b_.iter($B.$call(keys)())
497
while(true){
498
try{
499
var key = _b_.next(kiter),
500
value = gi(key)
501
dict.__setitem__(self, key, value)
502
}catch(err){
503
if(err.__class__ === _b_.StopIteration){
504
break
505
}
506
throw err
507
}
508
}
509
return $N
510
}
511
}
512
if(! Array.isArray(args)){
513
args = _b_.list.$factory(args)
514
}
515
// Form "dict([[key1, value1], [key2,value2], ...])"
541
dict.__ior__ = function(self, other){
542
// PEP 584
543
dict.update(self, other)
544
return self
545
}
546
555
for(var k in self.$numeric_dict){_count++}
556
for(var k in self.$string_dict){_count++}
557
for(var hash in self.$object_dict){
558
_count += self.$object_dict[hash].length
559
}
585
dict.__or__ = function(self, other){
586
// PEP 584
587
if(! _b_.isinstance(other, dict)){
588
return _b_.NotImplemented
589
}
590
var res = dict.copy(self)
591
dict.update(res, other)
592
return res
593
}
594
605
try{
606
res.push(repr(item[0]) + ": " + repr(item[1]))
607
}catch(err){
608
throw err
609
}
615
dict.__ror__ = function(self, other){
616
// PEP 584
617
if(! _b_.isinstance(other, dict)){
618
return _b_.NotImplemented
619
}
620
var res = dict.copy(other)
621
dict.update(res, self)
622
return res
623
}
624
627
["self", "key", "value"], arguments, {}, null, null)
628
return dict.$setitem($.self, $.key, $.value)
629
}
634
// If key is a string, set:
635
// - $string_dict[key] = [value, order] where "order" is an auto-increment
636
// unique id to keep track of insertion order
637
// - $str_hash[hash(key)] to key
638
//
639
// If key is a number, set $numeric_dict[key] = value
640
//
641
// If key is another object, compute its hash value:
642
// - if the hash is a key of $str_hash, and key == $str_hash[hash],
643
// replace $string_dict[$str_hash[hash]] by value
644
// - if the hash is a key of $numeric_dict, and hash == key, replace
645
// $numeric_dict[hash] by value
646
// - if the hash is a key of $object_dict: $object_dict[hash] is a list
647
// of [k, v] pairs. If key is equal to one of the "k", replace the
648
// matching v by value. Otherwise, add [key, value] to the list
649
// - else set $object_dict[hash] = [[key, value]]
650
//
651
// In all cases, increment attribute $version, used to detect dictionary
654
// Parameter $hash is only set if this method is called by setdefault.
655
// In this case the hash of key has already been computed and we
656
// know that the key is not present in the dictionary, so it's no
657
// use computing hash(key) again, nor testing equality of keys
666
// If class attribute __init__ or __new__ are reset,
667
// the factory function has to change
668
self.$jsobj.$factory = $B.$instance_creator(self.$jsobj)
669
}
670
}else{
678
if(self.$string_dict === undefined){
679
console.log("pas de string dict", self, key, value)
680
}
681
if(self.$string_dict[key] !== undefined){
682
self.$string_dict[key][0] = value
683
}else{
684
self.$string_dict[key] = [value, self.$order++]
685
self.$str_hash[str_hash(key)] = key
686
self.$version++
687
}
690
if(self.$numeric_dict[key] !== undefined){
691
// existing key: preserve order
692
self.$numeric_dict[key][0] = value
693
}else{
694
// new key
695
self.$numeric_dict[key] = [value, self.$order++]
696
self.$version++
697
}
718
// If $setitem is called from setdefault, don't test equality of key
719
// with any object
720
if($hash){
721
if(self.$object_dict[$hash] !== undefined){
725
}
726
self.$version++
727
return $N
728
}
729
var ix = rank(self, hash, key)
730
if(ix > -1){
731
// reset value
786
var $ = $B.args("fromkeys", 3, {cls: null, keys: null, value: null},
787
["cls", "keys", "value"], arguments, {value: _b_.None}, null, null),
799
if(klass === dict){dict.$setitem(res, key, value)}
800
else{$B.$getattr(res, "__setitem__")(key, value)}
811
var $ = $B.args("get", 3, {self: null, key: null, _default: null},
812
["self", "key", "_default"], arguments, {_default: $N}, null, null)
815
catch(err){
816
if(_b_.isinstance(err, _b_.KeyError)){return $._default}
817
else{throw err}
818
}
819
}
820
821
var dict_items = $B.make_view("dict_items", true)
822
dict_items.$iterator = $B.make_iterator_class("dict_itemiterator")
826
var _len = arguments.length - 1,
827
_msg = "items() takes no arguments (" + _len + " given)"
830
var it = dict_items.$factory(to_list(self))
831
it.len_func = function(){return dict.__len__(self)}
832
return it
835
var dict_keys = $B.make_view("dict_keys", true)
836
dict_keys.$iterator = $B.make_iterator_class("dict_keyiterator")
840
var _len = arguments.length - 1,
841
_msg = "keys() takes no arguments (" + _len + " given)"
844
var it = dict_keys.$factory(to_list(self, 0))
845
it.len_func = function(){return dict.__len__(self)}
846
return it
851
var missing = {},
852
$ = $B.args("pop", 3, {self: null, key: null, _default: null},
853
["self", "key", "_default"], arguments, {_default: missing}, null, null),
885
var $ = $B.args("setdefault", 3, {self: null, key: null, _default: null},
886
["self", "key", "_default"], arguments, {_default: $N}, null, null),
897
var hash = key.$hash
898
key.$hash = undefined
899
dict.$setitem(self, key, _default, hash)
906
var $ = $B.args("update", 1, {"self": null}, ["self"], arguments,
907
{}, "args", "kw"),
908
self = $.self,
909
args = $.args,
910
kw = $.kw
911
if(args.length > 0){
912
var o = args[0]
919
var _keys = _b_.list.$factory($B.$call($B.$getattr(o, "keys"))())
920
for(var i = 0, len = _keys.length; i < len; i++){
922
dict.$setitem(self, _keys[i], _value)
923
}
924
}else{
925
var it = _b_.iter(o),
926
i = 0
927
while(true){
928
try{
929
var item = _b_.next(it)
930
}catch(err){
931
if(err.__class__ === _b_.StopIteration){break}
932
throw err
933
}
934
try{
935
key_value = _b_.list.$factory(item)
936
}catch(err){
937
throw _b_.TypeError.$factory("cannot convert dictionary" +
938
" update sequence element #" + i + " to a sequence")
939
}
940
if(key_value.length !== 2){
941
throw _b_.ValueError.$factory("dictionary update " +
942
"sequence element #" + i + " has length " +
943
key_value.length + "; 2 is required")
944
}
945
dict.$setitem(self, key_value[0], key_value[1])
946
i++
955
var dict_values = $B.make_view("dict_values")
956
dict_values.$iterator = $B.make_iterator_class("dict_valueiterator")
960
var _len = arguments.length - 1,
961
_msg = "values() takes no arguments (" + _len + " given)"
964
var it = dict_values.$factory(to_list(self, 1))
965
it.len_func = function(){return dict.__len__(self)}
966
return it
971
var args = [res]
972
for(var i = 0, len = arguments.length; i < len ; i++){
973
args.push(arguments[i])
974
}
975
dict.__init__.apply(null, args)
983
$B.empty_dict = function(){
984
return {
985
__class__: dict,
986
$numeric_dict : {},
987
$object_dict : {},
988
$string_dict : {},
989
$str_hash: {},
995
// This must be done after set_func_names, otherwise dict.fromkeys doesn't
996
// have the attribute $infos
997
dict.fromkeys = _b_.classmethod.$factory(dict.fromkeys)
998
999
$B.getset_descriptor = $B.make_class("getset_descriptor",
1000
function(klass, attr){
1001
return {
1002
__class__: $B.getset_descriptor,
1004
cls: klass,
1005
attr: attr
1006
}
1007
}
1008
)
1009
1010
$B.getset_descriptor.__repr__ = $B.getset_descriptor.__str__ = function(self){
1011
return `<attribute '${self.attr}' of '${self.cls.$infos.__name__}' objects>`
1012
}
1013
1014
$B.set_func_names($B.getset_descriptor, "builtins")
1015
1020
// obj is a dictionary, with $string_dict table such that
1021
// obj.$string_dict[key] = [value, rank]
1022
// Transform it into an object with attribute $jsobj such that
1023
// res.$jsobj[key] = value
1024
var res = $B.obj_dict(dict.$to_obj(obj))
1034
throw _b_.TypeError.$factory("'mappingproxy' object does not support " +
1035
"item assignment")
1038
for(var attr in dict){
1039
if(mappingproxy[attr] !== undefined ||
1040
["__class__", "__mro__", "__new__", "__init__", "__delitem__",
1041
"clear", "fromkeys", "pop", "popitem", "setdefault",
1042
"update"].indexOf(attr) > -1){
1043
continue
1044
}
1045
if(typeof dict[attr] == "function"){
1046
mappingproxy[attr] = (function(key){
1047
return function(){
1048
return dict[key].apply(null, arguments)
1049
}
1050
})(attr)
1051
}else{
1052
mappingproxy[attr] = dict[attr]
1053
}
1054
}
1055
1078
if(klass !== undefined && klass.$native){
1079
throw _b_.AttributeError.$factory(klass.__name__ +