Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(252)

Side by Side Diff: sdk/lib/_internal/compiler/js_lib/convert_patch.dart

Issue 1212513002: sdk files reorganization to make dart2js a proper package (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: renamed Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 // Patch file for dart:convert library.
6
7 import 'dart:_js_helper' show argumentErrorValue, patch;
8 import 'dart:_foreign_helper' show JS;
9 import 'dart:_interceptors' show JSExtendableArray;
10 import 'dart:_internal' show MappedIterable, ListIterable;
11 import 'dart:collection' show Maps, LinkedHashMap;
12
13 /**
14 * Parses [json] and builds the corresponding parsed JSON value.
15 *
16 * Parsed JSON values Nare of the types [num], [String], [bool], [Null],
17 * [List]s of parsed JSON values or [Map]s from [String] to parsed
18 * JSON values.
19 *
20 * The optional [reviver] function, if provided, is called once for each object
21 * or list property parsed. The arguments are the property name ([String]) or
22 * list index ([int]), and the value is the parsed value. The return value of
23 * the reviver will be used as the value of that property instead of the parsed
24 * value. The top level value is passed to the reviver with the empty string as
25 * a key.
26 *
27 * Throws [FormatException] if the input is not valid JSON text.
28 */
29 @patch
30 _parseJson(String source, reviver(key, value)) {
31 if (source is! String) throw argumentErrorValue(source);
32
33 var parsed;
34 try {
35 parsed = JS('=Object|JSExtendableArray|Null|bool|num|String',
36 'JSON.parse(#)',
37 source);
38 } catch (e) {
39 throw new FormatException(JS('String', 'String(#)', e));
40 }
41
42 if (reviver == null) {
43 return _convertJsonToDartLazy(parsed);
44 } else {
45 return _convertJsonToDart(parsed, reviver);
46 }
47 }
48
49 /**
50 * Walks the raw JavaScript value [json], replacing JavaScript Objects with
51 * Maps. [json] is expected to be freshly allocated so elements can be replaced
52 * in-place.
53 */
54 _convertJsonToDart(json, reviver(key, value)) {
55 assert(reviver != null);
56 walk(e) {
57 // JavaScript null, string, number, bool are in the correct representation.
58 if (JS('bool', '# == null', e) || JS('bool', 'typeof # != "object"', e)) {
59 return e;
60 }
61
62 // This test is needed to avoid identifing '{"__proto__":[]}' as an Array.
63 // TODO(sra): Replace this test with cheaper '#.constructor === Array' when
64 // bug 621 below is fixed.
65 if (JS('bool', 'Object.getPrototypeOf(#) === Array.prototype', e)) {
66 // In-place update of the elements since JS Array is a Dart List.
67 for (int i = 0; i < JS('int', '#.length', e); i++) {
68 // Use JS indexing to avoid range checks. We know this is the only
69 // reference to the list, but the compiler will likely never be able to
70 // tell that this instance of the list cannot have its length changed by
71 // the reviver even though it later will be passed to the reviver at the
72 // outer level.
73 var item = JS('', '#[#]', e, i);
74 JS('', '#[#]=#', e, i, reviver(i, walk(item)));
75 }
76 return e;
77 }
78
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();
84 for (int i = 0; i < keys.length; i++) {
85 String key = keys[i];
86 var revived = reviver(key, walk(JS('', '#[#]', e, key)));
87 JS('', '#[#]=#', processed, key, revived);
88 }
89
90 // Update the JSON map structure so future access is cheaper.
91 map._original = processed; // Don't keep two objects around.
92 return map;
93 }
94
95 return reviver(null, walk(json));
96 }
97
98 _convertJsonToDartLazy(object) {
99 // JavaScript null and undefined are represented as null.
100 if (object == null) return null;
101
102 // JavaScript string, number, bool already has the correct representation.
103 if (JS('bool', 'typeof # != "object"', object)) {
104 return object;
105 }
106
107 // This test is needed to avoid identifing '{"__proto__":[]}' as an array.
108 // TODO(sra): Replace this test with cheaper '#.constructor === Array' when
109 // bug https://code.google.com/p/v8/issues/detail?id=621 is fixed.
110 if (JS('bool', 'Object.getPrototypeOf(#) !== Array.prototype', object)) {
111 return new _JsonMap(object);
112 }
113
114 // Update the elements in place since JS arrays are Dart lists.
115 for (int i = 0; i < JS('int', '#.length', object); i++) {
116 // Use JS indexing to avoid range checks. We know this is the only
117 // reference to the list, but the compiler will likely never be able to
118 // tell that this instance of the list cannot have its length changed by
119 // the reviver even though it later will be passed to the reviver at the
120 // outer level.
121 var item = JS('', '#[#]', object, i);
122 JS('', '#[#]=#', object, i, _convertJsonToDartLazy(item));
123 }
124 return object;
125 }
126
127 class _JsonMap implements LinkedHashMap {
128 // The original JavaScript object remains unchanged until
129 // the map is eventually upgraded, in which case we null it
130 // out to reclaim the memory used by it.
131 var _original;
132
133 // We keep track of the map entries that we have already
134 // processed by adding them to a separate JavaScript object.
135 var _processed = _newJavaScriptObject();
136
137 // If the data slot isn't null, it represents either the list
138 // of keys (for non-upgraded JSON maps) or the upgraded map.
139 var _data = null;
140
141 _JsonMap(this._original);
142
143 operator[](key) {
144 if (_isUpgraded) {
145 return _upgradedMap[key];
146 } else if (key is !String) {
147 return null;
148 } else {
149 var result = _getProperty(_processed, key);
150 if (_isUnprocessed(result)) result = _process(key);
151 return result;
152 }
153 }
154
155 int get length => _isUpgraded
156 ? _upgradedMap.length
157 : _computeKeys().length;
158
159 bool get isEmpty => length == 0;
160 bool get isNotEmpty => length > 0;
161
162 Iterable get keys {
163 if (_isUpgraded) return _upgradedMap.keys;
164 return new _JsonMapKeyIterable(this);
165 }
166
167 Iterable get values {
168 if (_isUpgraded) return _upgradedMap.values;
169 return new MappedIterable(_computeKeys(), (each) => this[each]);
170 }
171
172 operator[]=(key, value) {
173 if (_isUpgraded) {
174 _upgradedMap[key] = value;
175 } else if (containsKey(key)) {
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 }
182 } else {
183 _upgrade()[key] = value;
184 }
185 }
186
187 void addAll(Map other) {
188 other.forEach((key, value) {
189 this[key] = value;
190 });
191 }
192
193 bool containsValue(value) {
194 if (_isUpgraded) return _upgradedMap.containsValue(value);
195 List<String> keys = _computeKeys();
196 for (int i = 0; i < keys.length; i++) {
197 String key = keys[i];
198 if (this[key] == value) return true;
199 }
200 return false;
201 }
202
203 bool containsKey(key) {
204 if (_isUpgraded) return _upgradedMap.containsKey(key);
205 if (key is !String) return false;
206 return _hasProperty(_original, key);
207 }
208
209 putIfAbsent(key, ifAbsent()) {
210 if (containsKey(key)) return this[key];
211 var value = ifAbsent();
212 this[key] = value;
213 return value;
214 }
215
216 remove(Object key) {
217 if (!_isUpgraded && !containsKey(key)) return null;
218 return _upgrade().remove(key);
219 }
220
221 void clear() {
222 if (_isUpgraded) {
223 _upgradedMap.clear();
224 } else {
225 if (_data != null) {
226 // Clear the list of keys to make sure we force
227 // a concurrent modification error if anyone is
228 // currently iterating over it.
229 _data.clear();
230 }
231 _original = _processed = null;
232 _data = {};
233 }
234 }
235
236 void forEach(void f(key, value)) {
237 if (_isUpgraded) return _upgradedMap.forEach(f);
238 List<String> keys = _computeKeys();
239 for (int i = 0; i < keys.length; i++) {
240 String key = keys[i];
241
242 // Compute the value under the assumption that the property
243 // is present but potentially not processed.
244 var value = _getProperty(_processed, key);
245 if (_isUnprocessed(value)) {
246 value = _convertJsonToDartLazy(_getProperty(_original, key));
247 _setProperty(_processed, key, value);
248 }
249
250 // Do the callback.
251 f(key, value);
252
253 // Check if invoking the callback function changed
254 // the key set. If so, throw an exception.
255 if (!identical(keys, _data)) {
256 throw new ConcurrentModificationError(this);
257 }
258 }
259 }
260
261 String toString() => Maps.mapToString(this);
262
263
264 // ------------------------------------------
265 // Private helper methods.
266 // ------------------------------------------
267
268 bool get _isUpgraded => _processed == null;
269
270 Map get _upgradedMap {
271 assert(_isUpgraded);
272 // 'cast' the union type to LinkedHashMap. It would be even better if we
273 // could 'cast' to the implementation type, since LinkedHashMap includes
274 // _JsonMap.
275 return JS('LinkedHashMap', '#', _data);
276 }
277
278 List<String> _computeKeys() {
279 assert(!_isUpgraded);
280 List keys = _data;
281 if (keys == null) {
282 keys = _data = _getPropertyNames(_original);
283 }
284 return JS('JSExtendableArray', '#', keys);
285 }
286
287 Map _upgrade() {
288 if (_isUpgraded) return _upgradedMap;
289
290 // Copy all the (key, value) pairs to a freshly allocated
291 // linked hash map thus preserving the ordering.
292 Map result = {};
293 List<String> keys = _computeKeys();
294 for (int i = 0; i < keys.length; i++) {
295 String key = keys[i];
296 result[key] = this[key];
297 }
298
299 // We only upgrade when we need to extend the map, so we can
300 // safely force a concurrent modification error in case
301 // someone is iterating over the map here.
302 if (keys.isEmpty) {
303 keys.add(null);
304 } else {
305 keys.clear();
306 }
307
308 // Clear out the associated JavaScript objects and mark the
309 // map as having been upgraded.
310 _original = _processed = null;
311 _data = result;
312 assert(_isUpgraded);
313 return result;
314 }
315
316 _process(String key) {
317 if (!_hasProperty(_original, key)) return null;
318 var result = _convertJsonToDartLazy(_getProperty(_original, key));
319 return _setProperty(_processed, key, result);
320 }
321
322
323 // ------------------------------------------
324 // Private JavaScript helper methods.
325 // ------------------------------------------
326
327 static bool _hasProperty(object, String key)
328 => JS('bool', 'Object.prototype.hasOwnProperty.call(#,#)', object, key);
329 static _getProperty(object, String key)
330 => JS('', '#[#]', object, key);
331 static _setProperty(object, String key, value)
332 => JS('', '#[#]=#', object, key, value);
333 static List _getPropertyNames(object)
334 => JS('JSExtendableArray', 'Object.keys(#)', object);
335 static bool _isUnprocessed(object)
336 => JS('bool', 'typeof(#)=="undefined"', object);
337 static _newJavaScriptObject()
338 => JS('=Object', 'Object.create(null)');
339 }
340
341 class _JsonMapKeyIterable extends ListIterable {
342 final _JsonMap _parent;
343
344 _JsonMapKeyIterable(this._parent);
345
346 int get length => _parent.length;
347
348 String elementAt(int index) {
349 return _parent._isUpgraded ? _parent.keys.elementAt(index)
350 : _parent._computeKeys()[index];
351 }
352
353 /// Although [ListIterable] defines its own iterator, we return the iterator
354 /// of the underlying list [_keys] in order to propagate
355 /// [ConcurrentModificationError]s.
356 Iterator get iterator {
357 return _parent._isUpgraded ? _parent.keys.iterator
358 : _parent._computeKeys().iterator;
359 }
360
361 /// Delegate to [parent.containsKey] to ensure the performance expected
362 /// from [Map.keys.containsKey].
363 bool contains(Object key) => _parent.containsKey(key);
364 }
365
366 @patch class JsonDecoder {
367 @patch
368 StringConversionSink startChunkedConversion(Sink<Object> sink) {
369 return new _JsonDecoderSink(_reviver, sink);
370 }
371 }
372
373 /**
374 * Implements the chunked conversion from a JSON string to its corresponding
375 * object.
376 *
377 * The sink only creates one object, but its input can be chunked.
378 */
379 // TODO(floitsch): don't accumulate everything before starting to decode.
380 class _JsonDecoderSink extends _StringSinkConversionSink {
381 final _Reviver _reviver;
382 final Sink<Object> _sink;
383
384 _JsonDecoderSink(this._reviver, this._sink)
385 : super(new StringBuffer());
386
387 void close() {
388 super.close();
389 StringBuffer buffer = _stringSink;
390 String accumulated = buffer.toString();
391 buffer.clear();
392 Object decoded = _parseJson(accumulated, _reviver);
393 _sink.add(decoded);
394 _sink.close();
395 }
396 }
397
398 @patch class Utf8Decoder {
399 @patch
400 Converter<List<int>,dynamic> fuse(Converter<String, dynamic> next) {
401 return super.fuse(next);
402 }
403
404 // Currently not intercepting UTF8 decoding.
405 @patch
406 static String _convertIntercepted(bool allowMalformed, List<int> codeUnits,
407 int start, int end) {
408 return null; // This call was not intercepted.
409 }
410 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698