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 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
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
1090 lines (974 sloc)
31.9 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.$nat != 'kw' && $B.get_class(first) === $B.JSObj){
460
for(var key in first){
461
self.$string_dict[key] = [first[key], self.$order++]
462
}
463
return _b_.None
464
}else if(first.$jsobj){
465
self.$jsobj = {}
466
for(var attr in first.$jsobj){
467
self.$jsobj[attr] = first.$jsobj[attr]
477
arguments, {}, "first", "second")
478
var args = $.first
479
if(args.length > 1){
480
throw _b_.TypeError.$factory("dict expected at most 1 argument" +
481
", got 2")
482
}else if(args.length == 1){
483
args = args[0]
484
if(args.__class__ === dict){
485
['$string_dict', '$str_hash', '$numeric_dict', '$object_dict'].
486
forEach(function(d){
487
for(key in args[d]){self[d][key] = args[d][key]}
488
})
492
var keys = $B.$getattr(args, "keys", null)
493
if(keys !== null){
494
var gi = $B.$getattr(args, "__getitem__", null)
495
if(gi !== null){
496
// has keys and __getitem__ : it's a mapping, iterate on
497
// keys and values
498
gi = $B.$call(gi)
499
var kiter = _b_.iter($B.$call(keys)())
500
while(true){
501
try{
502
var key = _b_.next(kiter),
503
value = gi(key)
504
dict.__setitem__(self, key, value)
505
}catch(err){
506
if(err.__class__ === _b_.StopIteration){
507
break
508
}
509
throw err
510
}
511
}
512
return $N
513
}
514
}
515
if(! Array.isArray(args)){
516
args = _b_.list.$factory(args)
517
}
518
// Form "dict([[key1, value1], [key2,value2], ...])"
544
dict.__ior__ = function(self, other){
545
// PEP 584
546
dict.update(self, other)
547
return self
548
}
549
558
for(var k in self.$numeric_dict){_count++}
559
for(var k in self.$string_dict){_count++}
560
for(var hash in self.$object_dict){
561
_count += self.$object_dict[hash].length
562
}
588
dict.__or__ = function(self, other){
589
// PEP 584
590
if(! _b_.isinstance(other, dict)){
591
return _b_.NotImplemented
592
}
593
var res = dict.copy(self)
594
dict.update(res, other)
595
return res
596
}
597
608
try{
609
res.push(repr(item[0]) + ": " + repr(item[1]))
610
}catch(err){
611
throw err
612
}
618
dict.__ror__ = function(self, other){
619
// PEP 584
620
if(! _b_.isinstance(other, dict)){
621
return _b_.NotImplemented
622
}
623
var res = dict.copy(other)
624
dict.update(res, self)
625
return res
626
}
627
630
["self", "key", "value"], arguments, {}, null, null)
631
return dict.$setitem($.self, $.key, $.value)
632
}
637
// If key is a string, set:
638
// - $string_dict[key] = [value, order] where "order" is an auto-increment
639
// unique id to keep track of insertion order
640
// - $str_hash[hash(key)] to key
641
//
642
// If key is a number, set $numeric_dict[key] = value
643
//
644
// If key is another object, compute its hash value:
645
// - if the hash is a key of $str_hash, and key == $str_hash[hash],
646
// replace $string_dict[$str_hash[hash]] by value
647
// - if the hash is a key of $numeric_dict, and hash == key, replace
648
// $numeric_dict[hash] by value
649
// - if the hash is a key of $object_dict: $object_dict[hash] is a list
650
// of [k, v] pairs. If key is equal to one of the "k", replace the
651
// matching v by value. Otherwise, add [key, value] to the list
652
// - else set $object_dict[hash] = [[key, value]]
653
//
654
// In all cases, increment attribute $version, used to detect dictionary
657
// Parameter $hash is only set if this method is called by setdefault.
658
// In this case the hash of key has already been computed and we
659
// know that the key is not present in the dictionary, so it's no
660
// use computing hash(key) again, nor testing equality of keys
662
if(self.$from_js){
663
// dictionary created by method to_dict of JSObject instances
664
value = $B.pyobj2jsobj(value)
665
}
669
// If class attribute __init__ or __new__ are reset,
670
// the factory function has to change
671
self.$jsobj.$factory = $B.$instance_creator(self.$jsobj)
672
}
673
}else{
681
if(self.$string_dict === undefined){
682
console.log("pas de string dict", self, key, value)
683
}
684
if(self.$string_dict[key] !== undefined){
685
self.$string_dict[key][0] = value
686
}else{
687
self.$string_dict[key] = [value, self.$order++]
688
self.$str_hash[str_hash(key)] = key
689
self.$version++
690
}
693
if(self.$numeric_dict[key] !== undefined){
694
// existing key: preserve order
695
self.$numeric_dict[key][0] = value
696
}else{
697
// new key
698
self.$numeric_dict[key] = [value, self.$order++]
699
self.$version++
700
}
721
// If $setitem is called from setdefault, don't test equality of key
722
// with any object
723
if($hash){
724
if(self.$object_dict[$hash] !== undefined){
728
}
729
self.$version++
730
return $N
731
}
732
var ix = rank(self, hash, key)
733
if(ix > -1){
734
// reset value
789
var $ = $B.args("fromkeys", 3, {cls: null, keys: null, value: null},
790
["cls", "keys", "value"], arguments, {value: _b_.None}, null, null),
802
if(klass === dict){dict.$setitem(res, key, value)}
803
else{$B.$getattr(res, "__setitem__")(key, value)}
814
var $ = $B.args("get", 3, {self: null, key: null, _default: null},
815
["self", "key", "_default"], arguments, {_default: $N}, null, null)
818
catch(err){
819
if(_b_.isinstance(err, _b_.KeyError)){return $._default}
820
else{throw err}
821
}
822
}
823
824
var dict_items = $B.make_view("dict_items", true)
825
dict_items.$iterator = $B.make_iterator_class("dict_itemiterator")
829
var _len = arguments.length - 1,
830
_msg = "items() takes no arguments (" + _len + " given)"
833
var it = dict_items.$factory(to_list(self))
834
it.len_func = function(){return dict.__len__(self)}
835
return it
838
var dict_keys = $B.make_view("dict_keys", true)
839
dict_keys.$iterator = $B.make_iterator_class("dict_keyiterator")
843
var _len = arguments.length - 1,
844
_msg = "keys() takes no arguments (" + _len + " given)"
847
var it = dict_keys.$factory(to_list(self, 0))
848
it.len_func = function(){return dict.__len__(self)}
849
return it
854
var missing = {},
855
$ = $B.args("pop", 3, {self: null, key: null, _default: null},
856
["self", "key", "_default"], arguments, {_default: missing}, null, null),
888
var $ = $B.args("setdefault", 3, {self: null, key: null, _default: null},
889
["self", "key", "_default"], arguments, {_default: $N}, null, null),
900
var hash = key.$hash
901
key.$hash = undefined
902
dict.$setitem(self, key, _default, hash)
909
var $ = $B.args("update", 1, {"self": null}, ["self"], arguments,
910
{}, "args", "kw"),
911
self = $.self,
912
args = $.args,
913
kw = $.kw
914
if(args.length > 0){
915
var o = args[0]
922
var _keys = _b_.list.$factory($B.$call($B.$getattr(o, "keys"))())
923
for(var i = 0, len = _keys.length; i < len; i++){
925
dict.$setitem(self, _keys[i], _value)
926
}
927
}else{
928
var it = _b_.iter(o),
929
i = 0
930
while(true){
931
try{
932
var item = _b_.next(it)
933
}catch(err){
934
if(err.__class__ === _b_.StopIteration){break}
935
throw err
936
}
937
try{
938
key_value = _b_.list.$factory(item)
939
}catch(err){
940
throw _b_.TypeError.$factory("cannot convert dictionary" +
941
" update sequence element #" + i + " to a sequence")
942
}
943
if(key_value.length !== 2){
944
throw _b_.ValueError.$factory("dictionary update " +
945
"sequence element #" + i + " has length " +
946
key_value.length + "; 2 is required")
947
}
948
dict.$setitem(self, key_value[0], key_value[1])
949
i++
958
var dict_values = $B.make_view("dict_values")
959
dict_values.$iterator = $B.make_iterator_class("dict_valueiterator")
963
var _len = arguments.length - 1,
964
_msg = "values() takes no arguments (" + _len + " given)"
967
var it = dict_values.$factory(to_list(self, 1))
968
it.len_func = function(){return dict.__len__(self)}
969
return it
974
var args = [res]
975
for(var i = 0, len = arguments.length; i < len ; i++){
976
args.push(arguments[i])
977
}
978
dict.__init__.apply(null, args)
986
$B.empty_dict = function(){
987
return {
988
__class__: dict,
989
$numeric_dict : {},
990
$object_dict : {},
991
$string_dict : {},
992
$str_hash: {},
998
// This must be done after set_func_names, otherwise dict.fromkeys doesn't
999
// have the attribute $infos
1000
dict.fromkeys = _b_.classmethod.$factory(dict.fromkeys)
1001
1002
$B.getset_descriptor = $B.make_class("getset_descriptor",
1003
function(klass, attr){
1004
return {
1005
__class__: $B.getset_descriptor,
1007
cls: klass,
1008
attr: attr
1009
}
1010
}
1011
)
1012
1013
$B.getset_descriptor.__repr__ = $B.getset_descriptor.__str__ = function(self){
1014
return `<attribute '${self.attr}' of '${self.cls.$infos.__name__}' objects>`
1015
}
1016
1017
$B.set_func_names($B.getset_descriptor, "builtins")
1018
1023
// obj is a dictionary, with $string_dict table such that
1024
// obj.$string_dict[key] = [value, rank]
1025
// Transform it into an object with attribute $jsobj such that
1026
// res.$jsobj[key] = value
1027
var res = $B.obj_dict(dict.$to_obj(obj))
1037
throw _b_.TypeError.$factory("'mappingproxy' object does not support " +
1038
"item assignment")
1041
for(var attr in dict){
1042
if(mappingproxy[attr] !== undefined ||
1043
["__class__", "__mro__", "__new__", "__init__", "__delitem__",
1044
"clear", "fromkeys", "pop", "popitem", "setdefault",
1045
"update"].indexOf(attr) > -1){
1046
continue
1047
}
1048
if(typeof dict[attr] == "function"){
1049
mappingproxy[attr] = (function(key){
1050
return function(){
1051
return dict[key].apply(null, arguments)
1052
}
1053
})(attr)
1054
}else{
1055
mappingproxy[attr] = dict[attr]
1056
}
1057
}
1058
1081
if(klass !== undefined && klass.$native){
1082
throw _b_.AttributeError.$factory(klass.__name__ +