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
Dec 10, 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
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
Apr 18, 2020
Feb 26, 2018
Newer
100644
1073 lines (957 sloc)
31.2 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], ...])"
519
switch(typeof attr){
520
case "string":
521
self.$string_dict[attr] = kw[attr]
522
self.$str_hash[str_hash(attr)] = attr
523
break
524
case "number":
525
self.$numeric_dict[attr] = kw[attr]
526
break
527
default:
528
si(self, attr, kw[attr])
529
break
530
}
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
}
582
dict.__or__ = function(self, other){
583
// PEP 584
584
if(! _b_.isinstance(other, dict)){
585
return _b_.NotImplemented
586
}
587
var res = dict.copy(self)
588
dict.update(res, other)
589
return res
590
}
591
602
try{
603
res.push(repr(item[0]) + ": " + repr(item[1]))
604
}catch(err){
605
throw err
606
}
612
dict.__ror__ = function(self, other){
613
// PEP 584
614
if(! _b_.isinstance(other, dict)){
615
return _b_.NotImplemented
616
}
617
var res = dict.copy(other)
618
dict.update(res, self)
619
return res
620
}
621
624
["self", "key", "value"], arguments, {}, null, null)
625
return dict.$setitem($.self, $.key, $.value)
626
}
631
// If key is a string, set:
632
// - $string_dict[key] = [value, order] where "order" is an auto-increment
633
// unique id to keep track of insertion order
634
// - $str_hash[hash(key)] to key
635
//
636
// If key is a number, set $numeric_dict[key] = value
637
//
638
// If key is another object, compute its hash value:
639
// - if the hash is a key of $str_hash, and key == $str_hash[hash],
640
// replace $string_dict[$str_hash[hash]] by value
641
// - if the hash is a key of $numeric_dict, and hash == key, replace
642
// $numeric_dict[hash] by value
643
// - if the hash is a key of $object_dict: $object_dict[hash] is a list
644
// of [k, v] pairs. If key is equal to one of the "k", replace the
645
// matching v by value. Otherwise, add [key, value] to the list
646
// - else set $object_dict[hash] = [[key, value]]
647
//
648
// In all cases, increment attribute $version, used to detect dictionary
651
// Parameter $hash is only set if this method is called by setdefault.
652
// In this case the hash of key has already been computed and we
653
// know that the key is not present in the dictionary, so it's no
654
// use computing hash(key) again, nor testing equality of keys
656
if(self.$from_js){
657
// dictionary created by method to_dict of JSObject instances
658
value = $B.pyobj2jsobj(value)
659
}
663
// If class attribute __init__ or __new__ are reset,
664
// the factory function has to change
665
self.$jsobj.$factory = $B.$instance_creator(self.$jsobj)
666
}
667
}else{
675
if(self.$string_dict === undefined){
676
console.log("pas de string dict", self, key, value)
677
}
678
self.$string_dict[key] = [value, self.$version]
705
// If $setitem is called from setdefault, don't test equality of key
706
// with any object
707
if($hash){
708
if(self.$object_dict[$hash] !== undefined){
712
}
713
self.$version++
714
return $N
715
}
716
var ix = rank(self, hash, key)
717
if(ix > -1){
718
// reset value
772
var $ = $B.args("fromkeys", 3, {cls: null, keys: null, value: null},
773
["cls", "keys", "value"], arguments, {value: _b_.None}, null, null),
785
if(klass === dict){dict.$setitem(res, key, value)}
786
else{$B.$getattr(res, "__setitem__")(key, value)}
797
var $ = $B.args("get", 3, {self: null, key: null, _default: null},
798
["self", "key", "_default"], arguments, {_default: $N}, null, null)
801
catch(err){
802
if(_b_.isinstance(err, _b_.KeyError)){return $._default}
803
else{throw err}
804
}
805
}
806
807
var dict_items = $B.make_view("dict_items", true)
808
dict_items.$iterator = $B.make_iterator_class("dict_itemiterator")
812
var _len = arguments.length - 1,
813
_msg = "items() takes no arguments (" + _len + " given)"
816
var it = dict_items.$factory(to_list(self))
817
it.len_func = function(){return dict.__len__(self)}
818
return it
821
var dict_keys = $B.make_view("dict_keys", true)
822
dict_keys.$iterator = $B.make_iterator_class("dict_keyiterator")
826
var _len = arguments.length - 1,
827
_msg = "keys() takes no arguments (" + _len + " given)"
830
var it = dict_keys.$factory(to_list(self, 0))
831
it.len_func = function(){return dict.__len__(self)}
832
return it
837
var missing = {},
838
$ = $B.args("pop", 3, {self: null, key: null, _default: null},
839
["self", "key", "_default"], arguments, {_default: missing}, null, null),
871
var $ = $B.args("setdefault", 3, {self: null, key: null, _default: null},
872
["self", "key", "_default"], arguments, {_default: $N}, null, null),
883
var hash = key.$hash
884
key.$hash = undefined
885
dict.$setitem(self, key, _default, hash)
892
var $ = $B.args("update", 1, {"self": null}, ["self"], arguments,
893
{}, "args", "kw"),
894
self = $.self,
895
args = $.args,
896
kw = $.kw
897
if(args.length > 0){
898
var o = args[0]
905
var _keys = _b_.list.$factory($B.$call($B.$getattr(o, "keys"))())
906
for(var i = 0, len = _keys.length; i < len; i++){
908
dict.$setitem(self, _keys[i], _value)
909
}
910
}else{
911
var it = _b_.iter(o),
912
i = 0
913
while(true){
914
try{
915
var item = _b_.next(it)
916
}catch(err){
917
if(err.__class__ === _b_.StopIteration){break}
918
throw err
919
}
920
try{
921
key_value = _b_.list.$factory(item)
922
}catch(err){
923
throw _b_.TypeError.$factory("cannot convert dictionary" +
924
" update sequence element #" + i + " to a sequence")
925
}
926
if(key_value.length !== 2){
927
throw _b_.ValueError.$factory("dictionary update " +
928
"sequence element #" + i + " has length " +
929
key_value.length + "; 2 is required")
930
}
931
dict.$setitem(self, key_value[0], key_value[1])
932
i++
941
var dict_values = $B.make_view("dict_values")
942
dict_values.$iterator = $B.make_iterator_class("dict_valueiterator")
946
var _len = arguments.length - 1,
947
_msg = "values() takes no arguments (" + _len + " given)"
950
var it = dict_values.$factory(to_list(self, 1))
951
it.len_func = function(){return dict.__len__(self)}
952
return it
957
var args = [res]
958
for(var i = 0, len = arguments.length; i < len ; i++){
959
args.push(arguments[i])
960
}
961
dict.__init__.apply(null, args)
969
$B.empty_dict = function(){
970
return {
971
__class__: dict,
972
$numeric_dict : {},
973
$object_dict : {},
974
$string_dict : {},
975
$str_hash: {},
976
$version: 0
977
}
978
}
979
980
// This must be done after set_func_names, otherwise dict.fromkeys doesn't
981
// have the attribute $infos
982
dict.fromkeys = _b_.classmethod.$factory(dict.fromkeys)
983
984
$B.getset_descriptor = $B.make_class("getset_descriptor",
985
function(klass, attr){
986
return {
987
__class__: $B.getset_descriptor,
989
cls: klass,
990
attr: attr
991
}
992
}
993
)
994
995
$B.getset_descriptor.__repr__ = $B.getset_descriptor.__str__ = function(self){
996
return `<attribute '${self.attr}' of '${self.cls.$infos.__name__}' objects>`
997
}
998
999
$B.set_func_names($B.getset_descriptor, "builtins")
1000
1005
// obj is a dictionary, with $string_dict table such that
1006
// obj.$string_dict[key] = [value, rank]
1007
// Transform it into an object with attribute $jsobj such that
1008
// res.$jsobj[key] = value
1009
var res = $B.obj_dict(dict.$to_obj(obj))
1019
throw _b_.TypeError.$factory("'mappingproxy' object does not support " +
1020
"item assignment")
1023
for(var attr in dict){
1024
if(mappingproxy[attr] !== undefined ||
1025
["__class__", "__mro__", "__new__", "__init__", "__delitem__",
1026
"clear", "fromkeys", "pop", "popitem", "setdefault",
1027
"update"].indexOf(attr) > -1){
1028
continue
1029
}
1030
if(typeof dict[attr] == "function"){
1031
mappingproxy[attr] = (function(key){
1032
return function(){
1033
return dict[key].apply(null, arguments)
1034
}
1035
})(attr)
1036
}else{
1037
mappingproxy[attr] = dict[attr]
1038
}
1039
}
1040
1047
if(x[attr] === null){
1048
d.$string_dict[attr] = [_b_.None, d.$version]
1049
}else if(x[attr] === undefined){
1064
if(klass !== undefined && klass.$native){
1065
throw _b_.AttributeError.$factory(klass.__name__ +