Skip to content

Commit 1a56829

Browse files
author
kasperl@google.com
committed
Use JsonMap even for JSON.decode with custom reviver.
R=floitsch@google.com BUG= Review URL: https://codereview.chromium.org//367683002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@37931 260f80e4-7a28-3924-810f-c04153c831b5
1 parent 14f8d56 commit 1a56829

File tree

3 files changed

+55
-26
lines changed

3 files changed

+55
-26
lines changed

sdk/lib/_internal/lib/convert_patch.dart

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ _parseJson(String source, reviver(key, value)) {
5252
* in-place.
5353
*/
5454
_convertJsonToDart(json, reviver(key, value)) {
55-
var revive = reviver == null ? (key, value) => value : reviver;
56-
55+
assert(reviver != null);
5756
walk(e) {
5857
// JavaScript null, string, number, bool are in the correct representation.
5958
if (JS('bool', '# == null', e) || JS('bool', 'typeof # != "object"', e)) {
@@ -64,41 +63,36 @@ _convertJsonToDart(json, reviver(key, value)) {
6463
// TODO(sra): Replace this test with cheaper '#.constructor === Array' when
6564
// bug 621 below is fixed.
6665
if (JS('bool', 'Object.getPrototypeOf(#) === Array.prototype', e)) {
67-
// Teach compiler the type is known by passing it through a JS-expression.
68-
var list = JS('JSExtendableArray', '#', e);
6966
// In-place update of the elements since JS Array is a Dart List.
70-
for (int i = 0; i < list.length; i++) {
67+
for (int i = 0; i < JS('int', '#.length', e); i++) {
7168
// Use JS indexing to avoid range checks. We know this is the only
7269
// reference to the list, but the compiler will likely never be able to
7370
// tell that this instance of the list cannot have its length changed by
7471
// the reviver even though it later will be passed to the reviver at the
7572
// outer level.
76-
var item = JS('', '#[#]', list, i);
77-
JS('', '#[#]=#', list, i, revive(i, walk(item)));
73+
var item = JS('', '#[#]', e, i);
74+
JS('', '#[#]=#', e, i, reviver(i, walk(item)));
7875
}
79-
return list;
76+
return e;
8077
}
8178

82-
// Otherwise it is a plain Object, so copy to a Map.
83-
var keys = JS('JSExtendableArray', 'Object.keys(#)', e);
84-
Map map = {};
79+
// Otherwise it is a plain object, so copy to a JSON map, so we process
80+
// and revive all entries recursively.
81+
_JsonMap map = new _JsonMap(e);
82+
var processed = map._processed;
83+
List<String> keys = map._computeKeys();
8584
for (int i = 0; i < keys.length; i++) {
8685
String key = keys[i];
87-
map[key] = revive(key, walk(JS('', '#[#]', e, key)));
88-
}
89-
// V8 has a bug with properties named "__proto__"
90-
// https://code.google.com/p/v8/issues/detail?id=621
91-
var proto = JS('', '#.__proto__', e);
92-
// __proto__ can be undefined on IE9.
93-
if (JS('bool',
94-
'typeof # !== "undefined" && # !== Object.prototype',
95-
proto, proto)) {
96-
map['__proto__'] = revive('__proto__', walk(proto));
86+
var revived = reviver(key, walk(JS('', '#[#]', e, key)));
87+
JS('', '#[#]=#', processed, key, revived);
9788
}
89+
90+
// Update the JSON map structure so future access is cheaper.
91+
map._original = processed; // Don't keep two objects around.
9892
return map;
9993
}
10094

101-
return revive(null, walk(json));
95+
return reviver(null, walk(json));
10296
}
10397

10498
_convertJsonToDartLazy(object) {
@@ -179,8 +173,12 @@ class _JsonMap implements LinkedHashMap {
179173
if (_isUpgraded) {
180174
_upgradedMap[key] = value;
181175
} else if (containsKey(key)) {
182-
_setProperty(_processed, key, value);
183-
_setProperty(_original, key, null); // Reclaim memory.
176+
var processed = _processed;
177+
_setProperty(processed, key, value);
178+
var original = _original;
179+
if (!identical(original, processed)) {
180+
_setProperty(original, key, null); // Reclaim memory.
181+
}
184182
} else {
185183
_upgrade()[key] = value;
186184
}

tests/corelib/json_map_test.dart

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@ import "package:expect/expect.dart";
88
import 'dart:convert' show JSON;
99
import 'dart:collection' show LinkedHashMap, HashMap;
1010

11-
Map jsonify(Map map) => JSON.decode(JSON.encode(map));
11+
bool useReviver = false;
12+
Map jsonify(Map map) {
13+
String encoded = JSON.encode(map);
14+
return useReviver
15+
? JSON.decode(encoded, reviver: (key, value) => value)
16+
: JSON.decode(encoded);
17+
}
1218

1319
List listEach(Map map) {
1420
var result = [];
@@ -20,6 +26,12 @@ List listEach(Map map) {
2026
}
2127

2228
void main() {
29+
test(false);
30+
test(true);
31+
}
32+
33+
void test(bool revive) {
34+
useReviver = revive;
2335
testEmpty(jsonify({}));
2436
testAtoB(jsonify({'a': 'b'}));
2537

@@ -45,6 +57,7 @@ void main() {
4557
testClear();
4658

4759
testListEntry();
60+
testMutation();
4861
}
4962

5063
void testEmpty(Map map) {
@@ -322,3 +335,12 @@ void testListEntry() {
322335
Expect.equals(8, list[1]);
323336
Expect.equals(9, list[2]['b']);
324337
}
338+
339+
void testMutation() {
340+
Map map = jsonify({'a': 0});
341+
Expect.listEquals(['a', 0], listEach(map));
342+
map['a'] = 1;
343+
Expect.listEquals(['a', 1], listEach(map));
344+
map['a']++;
345+
Expect.listEquals(['a', 2], listEach(map));
346+
}

tests/corelib/map_test.dart

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import "package:expect/expect.dart";
77
import 'dart:collection';
88
import 'dart:convert' show JSON;
99

10-
Map newJsonMap() => JSON.decode('{}');
10+
Map newJsonMap()
11+
=> JSON.decode('{}');
12+
Map newJsonMapCustomReviver()
13+
=> JSON.decode('{}', reviver: (key, value) => value);
1114

1215
void main() {
1316
test(new HashMap());
@@ -19,6 +22,7 @@ void main() {
1922
test(new MapBaseMap());
2023
test(new MapMixinMap());
2124
test(newJsonMap());
25+
test(newJsonMapCustomReviver());
2226
testLinkedHashMap();
2327
testMapLiteral();
2428
testNullValue();
@@ -35,6 +39,7 @@ void main() {
3539
testWeirdStringKeys(new MapBaseMap<String, String>());
3640
testWeirdStringKeys(new MapMixinMap<String, String>());
3741
testWeirdStringKeys(newJsonMap());
42+
testWeirdStringKeys(newJsonMapCustomReviver());
3843

3944
testNumericKeys(new Map());
4045
testNumericKeys(new Map<num, String>());
@@ -49,6 +54,7 @@ void main() {
4954
testNumericKeys(new MapBaseMap<num, String>());
5055
testNumericKeys(new MapMixinMap<num, String>());
5156
testNumericKeys(newJsonMap());
57+
testNumericKeys(newJsonMapCustomReviver());
5258

5359
testNaNKeys(new Map());
5460
testNaNKeys(new Map<num, String>());
@@ -59,6 +65,7 @@ void main() {
5965
testNaNKeys(new MapBaseMap<num, String>());
6066
testNaNKeys(new MapMixinMap<num, String>());
6167
testNaNKeys(newJsonMap());
68+
testNaNKeys(newJsonMapCustomReviver());
6269
// Identity maps fail the NaN-keys tests because the test assumes that
6370
// NaN is not equal to NaN.
6471

@@ -87,6 +94,7 @@ void main() {
8794
testIterationOrder(new LinkedHashMap());
8895
testIterationOrder(new LinkedHashMap.identity());
8996
testIterationOrder(newJsonMap());
97+
testIterationOrder(newJsonMapCustomReviver());
9098

9199
testOtherKeys(new SplayTreeMap<int, int>());
92100
testOtherKeys(new SplayTreeMap<int, int>((int a, int b) => a - b,
@@ -110,6 +118,7 @@ void main() {
110118
testOtherKeys(new MapBaseMap<int, int>());
111119
testOtherKeys(new MapMixinMap<int, int>());
112120
testOtherKeys(newJsonMap());
121+
testOtherKeys(newJsonMapCustomReviver());
113122

114123
testUnmodifiableMap(const {1 : 37});
115124
testUnmodifiableMap(new UnmodifiableMapView({1 : 37}));

0 commit comments

Comments
 (0)