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 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
Oct 24, 2018
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
1085 lines (969 sloc)
31.7 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
454
if(first === undefined){return $N}
455
if(second === undefined){
456
if(first.__class__ === $B.JSObject){
457
self.$jsobj = first.js
458
return $N
459
}else if(first.$jsobj){
460
self.$jsobj = {}
461
for(var attr in first.$jsobj){
462
self.$jsobj[attr] = first.$jsobj[attr]
472
arguments, {}, "first", "second")
473
var args = $.first
474
if(args.length > 1){
475
throw _b_.TypeError.$factory("dict expected at most 1 argument" +
476
", got 2")
477
}else if(args.length == 1){
478
args = args[0]
479
if(args.__class__ === dict){
480
['$string_dict', '$str_hash', '$numeric_dict', '$object_dict'].
481
forEach(function(d){
482
for(key in args[d]){self[d][key] = args[d][key]}
483
})
487
var keys = $B.$getattr(args, "keys", null)
488
if(keys !== null){
489
var gi = $B.$getattr(args, "__getitem__", null)
490
if(gi !== null){
491
// has keys and __getitem__ : it's a mapping, iterate on
492
// keys and values
493
gi = $B.$call(gi)
494
var kiter = _b_.iter($B.$call(keys)())
495
while(true){
496
try{
497
var key = _b_.next(kiter),
498
value = gi(key)
499
dict.__setitem__(self, key, value)
500
}catch(err){
501
if(err.__class__ === _b_.StopIteration){
502
break
503
}
504
throw err
505
}
506
}
507
return $N
508
}
509
}
510
if(! Array.isArray(args)){
511
args = _b_.list.$factory(args)
512
}
513
// Form "dict([[key1, value1], [key2,value2], ...])"
539
dict.__ior__ = function(self, other){
540
// PEP 584
541
dict.update(self, other)
542
return self
543
}
544
553
for(var k in self.$numeric_dict){_count++}
554
for(var k in self.$string_dict){_count++}
555
for(var hash in self.$object_dict){
556
_count += self.$object_dict[hash].length
557
}
583
dict.__or__ = function(self, other){
584
// PEP 584
585
if(! _b_.isinstance(other, dict)){
586
return _b_.NotImplemented
587
}
588
var res = dict.copy(self)
589
dict.update(res, other)
590
return res
591
}
592
603
try{
604
res.push(repr(item[0]) + ": " + repr(item[1]))
605
}catch(err){
606
throw err
607
}
613
dict.__ror__ = function(self, other){
614
// PEP 584
615
if(! _b_.isinstance(other, dict)){
616
return _b_.NotImplemented
617
}
618
var res = dict.copy(other)
619
dict.update(res, self)
620
return res
621
}
622
625
["self", "key", "value"], arguments, {}, null, null)
626
return dict.$setitem($.self, $.key, $.value)
627
}
632
// If key is a string, set:
633
// - $string_dict[key] = [value, order] where "order" is an auto-increment
634
// unique id to keep track of insertion order
635
// - $str_hash[hash(key)] to key
636
//
637
// If key is a number, set $numeric_dict[key] = value
638
//
639
// If key is another object, compute its hash value:
640
// - if the hash is a key of $str_hash, and key == $str_hash[hash],
641
// replace $string_dict[$str_hash[hash]] by value
642
// - if the hash is a key of $numeric_dict, and hash == key, replace
643
// $numeric_dict[hash] by value
644
// - if the hash is a key of $object_dict: $object_dict[hash] is a list
645
// of [k, v] pairs. If key is equal to one of the "k", replace the
646
// matching v by value. Otherwise, add [key, value] to the list
647
// - else set $object_dict[hash] = [[key, value]]
648
//
649
// In all cases, increment attribute $version, used to detect dictionary
652
// Parameter $hash is only set if this method is called by setdefault.
653
// In this case the hash of key has already been computed and we
654
// know that the key is not present in the dictionary, so it's no
655
// use computing hash(key) again, nor testing equality of keys
657
if(self.$from_js){
658
// dictionary created by method to_dict of JSObject instances
659
value = $B.pyobj2jsobj(value)
660
}
664
// If class attribute __init__ or __new__ are reset,
665
// the factory function has to change
666
self.$jsobj.$factory = $B.$instance_creator(self.$jsobj)
667
}
668
}else{
676
if(self.$string_dict === undefined){
677
console.log("pas de string dict", self, key, value)
678
}
679
if(self.$string_dict[key] !== undefined){
680
self.$string_dict[key][0] = value
681
}else{
682
self.$string_dict[key] = [value, self.$order++]
683
self.$str_hash[str_hash(key)] = key
684
self.$version++
685
}
688
if(self.$numeric_dict[key] !== undefined){
689
// existing key: preserve order
690
self.$numeric_dict[key][0] = value
691
}else{
692
// new key
693
self.$numeric_dict[key] = [value, self.$order++]
694
self.$version++
695
}
716
// If $setitem is called from setdefault, don't test equality of key
717
// with any object
718
if($hash){
719
if(self.$object_dict[$hash] !== undefined){
723
}
724
self.$version++
725
return $N
726
}
727
var ix = rank(self, hash, key)
728
if(ix > -1){
729
// reset value
784
var $ = $B.args("fromkeys", 3, {cls: null, keys: null, value: null},
785
["cls", "keys", "value"], arguments, {value: _b_.None}, null, null),
797
if(klass === dict){dict.$setitem(res, key, value)}
798
else{$B.$getattr(res, "__setitem__")(key, value)}
809
var $ = $B.args("get", 3, {self: null, key: null, _default: null},
810
["self", "key", "_default"], arguments, {_default: $N}, null, null)
813
catch(err){
814
if(_b_.isinstance(err, _b_.KeyError)){return $._default}
815
else{throw err}
816
}
817
}
818
819
var dict_items = $B.make_view("dict_items", true)
820
dict_items.$iterator = $B.make_iterator_class("dict_itemiterator")
824
var _len = arguments.length - 1,
825
_msg = "items() takes no arguments (" + _len + " given)"
828
var it = dict_items.$factory(to_list(self))
829
it.len_func = function(){return dict.__len__(self)}
830
return it
833
var dict_keys = $B.make_view("dict_keys", true)
834
dict_keys.$iterator = $B.make_iterator_class("dict_keyiterator")
838
var _len = arguments.length - 1,
839
_msg = "keys() takes no arguments (" + _len + " given)"
842
var it = dict_keys.$factory(to_list(self, 0))
843
it.len_func = function(){return dict.__len__(self)}
844
return it
849
var missing = {},
850
$ = $B.args("pop", 3, {self: null, key: null, _default: null},
851
["self", "key", "_default"], arguments, {_default: missing}, null, null),
883
var $ = $B.args("setdefault", 3, {self: null, key: null, _default: null},
884
["self", "key", "_default"], arguments, {_default: $N}, null, null),
895
var hash = key.$hash
896
key.$hash = undefined
897
dict.$setitem(self, key, _default, hash)
904
var $ = $B.args("update", 1, {"self": null}, ["self"], arguments,
905
{}, "args", "kw"),
906
self = $.self,
907
args = $.args,
908
kw = $.kw
909
if(args.length > 0){
910
var o = args[0]
917
var _keys = _b_.list.$factory($B.$call($B.$getattr(o, "keys"))())
918
for(var i = 0, len = _keys.length; i < len; i++){
920
dict.$setitem(self, _keys[i], _value)
921
}
922
}else{
923
var it = _b_.iter(o),
924
i = 0
925
while(true){
926
try{
927
var item = _b_.next(it)
928
}catch(err){
929
if(err.__class__ === _b_.StopIteration){break}
930
throw err
931
}
932
try{
933
key_value = _b_.list.$factory(item)
934
}catch(err){
935
throw _b_.TypeError.$factory("cannot convert dictionary" +
936
" update sequence element #" + i + " to a sequence")
937
}
938
if(key_value.length !== 2){
939
throw _b_.ValueError.$factory("dictionary update " +
940
"sequence element #" + i + " has length " +
941
key_value.length + "; 2 is required")
942
}
943
dict.$setitem(self, key_value[0], key_value[1])
944
i++
953
var dict_values = $B.make_view("dict_values")
954
dict_values.$iterator = $B.make_iterator_class("dict_valueiterator")
958
var _len = arguments.length - 1,
959
_msg = "values() takes no arguments (" + _len + " given)"
962
var it = dict_values.$factory(to_list(self, 1))
963
it.len_func = function(){return dict.__len__(self)}
964
return it
969
var args = [res]
970
for(var i = 0, len = arguments.length; i < len ; i++){
971
args.push(arguments[i])
972
}
973
dict.__init__.apply(null, args)
981
$B.empty_dict = function(){
982
return {
983
__class__: dict,
984
$numeric_dict : {},
985
$object_dict : {},
986
$string_dict : {},
987
$str_hash: {},
993
// This must be done after set_func_names, otherwise dict.fromkeys doesn't
994
// have the attribute $infos
995
dict.fromkeys = _b_.classmethod.$factory(dict.fromkeys)
996
997
$B.getset_descriptor = $B.make_class("getset_descriptor",
998
function(klass, attr){
999
return {
1000
__class__: $B.getset_descriptor,
1002
cls: klass,
1003
attr: attr
1004
}
1005
}
1006
)
1007
1008
$B.getset_descriptor.__repr__ = $B.getset_descriptor.__str__ = function(self){
1009
return `<attribute '${self.attr}' of '${self.cls.$infos.__name__}' objects>`
1010
}
1011
1012
$B.set_func_names($B.getset_descriptor, "builtins")
1013
1018
// obj is a dictionary, with $string_dict table such that
1019
// obj.$string_dict[key] = [value, rank]
1020
// Transform it into an object with attribute $jsobj such that
1021
// res.$jsobj[key] = value
1022
var res = $B.obj_dict(dict.$to_obj(obj))
1032
throw _b_.TypeError.$factory("'mappingproxy' object does not support " +
1033
"item assignment")
1036
for(var attr in dict){
1037
if(mappingproxy[attr] !== undefined ||
1038
["__class__", "__mro__", "__new__", "__init__", "__delitem__",
1039
"clear", "fromkeys", "pop", "popitem", "setdefault",
1040
"update"].indexOf(attr) > -1){
1041
continue
1042
}
1043
if(typeof dict[attr] == "function"){
1044
mappingproxy[attr] = (function(key){
1045
return function(){
1046
return dict[key].apply(null, arguments)
1047
}
1048
})(attr)
1049
}else{
1050
mappingproxy[attr] = dict[attr]
1051
}
1052
}
1053
1076
if(klass !== undefined && klass.$native){
1077
throw _b_.AttributeError.$factory(klass.__name__ +