OLD | NEW |
---|---|
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 | 5 |
6 // Conversions for IDBKey. | 6 // Conversions for IDBKey. |
7 // | 7 // |
8 // Per http://www.w3.org/TR/IndexedDB/#key-construct | 8 // Per http://www.w3.org/TR/IndexedDB/#key-construct |
9 // | 9 // |
10 // "A value is said to be a valid key if it is one of the following types: Array | 10 // "A value is said to be a valid key if it is one of the following types: Array |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
51 * described at | 51 * described at |
52 * http://www.whatwg.org/specs/web-apps/current-work/multipage/common-dom-interf aces.html#structured-clone | 52 * http://www.whatwg.org/specs/web-apps/current-work/multipage/common-dom-interf aces.html#structured-clone |
53 * https://www.khronos.org/registry/typedarray/specs/latest/#9 | 53 * https://www.khronos.org/registry/typedarray/specs/latest/#9 |
54 * | 54 * |
55 * Since the result of this function is expected to be passed only to JavaScript | 55 * Since the result of this function is expected to be passed only to JavaScript |
56 * operations that perform the structured clone algorithm which does not mutate | 56 * operations that perform the structured clone algorithm which does not mutate |
57 * its output, the result may share structure with the input [value]. | 57 * its output, the result may share structure with the input [value]. |
58 */ | 58 */ |
59 abstract class _StructuredClone { | 59 abstract class _StructuredClone { |
60 | 60 |
61 // TODO(sra): Replace slots with identity hash table. | 61 // Keep track of the clones, keyed by the original object. If we're |
62 var values = []; | 62 // not copying, these may be the same. |
63 var copies = []; // initially 'null', 'true' during initial DFS, then a copy. | 63 var clones = new HashMap.identity(); |
sra1
2016/03/22 00:54:21
Be careful here.
The implementation of identityHa
Alan Knight
2016/03/22 18:00:01
OK, I moved the code to use an identity map down i
| |
64 | 64 |
65 int findSlot(value) { | 65 Object findSlot(value) { |
66 int length = values.length; | 66 return clones.putIfAbsent(value, () => null); |
67 for (int i = 0; i < length; i++) { | |
68 if (identical(values[i], value)) return i; | |
69 } | |
70 values.add(value); | |
71 copies.add(null); | |
72 return length; | |
73 } | 67 } |
74 readSlot(int i) => copies[i]; | 68 readSlot(Object original) => clones[original]; |
75 writeSlot(int i, x) { copies[i] = x; } | 69 writeSlot(Object original, clone) { clones[original] = clone; } |
76 cleanupSlots() {} // Will be needed if we mark objects with a property. | 70 cleanupSlots() {} // Will be needed if we mark objects with a property. |
77 bool cloneNotRequired(object); | 71 bool cloneNotRequired(object); |
78 newJsMap(); | 72 newJsMap(); |
79 newJsList(length); | 73 newJsList(length); |
80 void putIntoMap(map, key, value); | 74 void putIntoMap(map, key, value); |
81 | 75 |
82 // Returns the input, or a clone of the input. | 76 // Returns the input, or a clone of the input. |
83 walk(e) { | 77 walk(e) { |
84 if (e == null) return e; | 78 if (e == null) return e; |
85 if (e is bool) return e; | 79 if (e is bool) return e; |
(...skipping 16 matching lines...) Expand all Loading... | |
102 | 96 |
103 if (e is File) return e; | 97 if (e is File) return e; |
104 if (e is Blob) return e; | 98 if (e is Blob) return e; |
105 if (e is FileList) return e; | 99 if (e is FileList) return e; |
106 | 100 |
107 // TODO(sra): Firefox: How to convert _TypedImageData on the other end? | 101 // TODO(sra): Firefox: How to convert _TypedImageData on the other end? |
108 if (e is ImageData) return e; | 102 if (e is ImageData) return e; |
109 if (cloneNotRequired(e)) return e; | 103 if (cloneNotRequired(e)) return e; |
110 | 104 |
111 if (e is Map) { | 105 if (e is Map) { |
112 var slot = findSlot(e); | 106 var copy = findSlot(e); |
113 var copy = readSlot(slot); | |
114 if (copy != null) return copy; | 107 if (copy != null) return copy; |
115 copy = newJsMap(); | 108 copy = newJsMap(); |
116 writeSlot(slot, copy); | 109 writeSlot(e, copy); |
117 e.forEach((key, value) { | 110 e.forEach((key, value) { |
118 putIntoMap(copy, key, walk(value)); | 111 putIntoMap(copy, key, walk(value)); |
119 }); | 112 }); |
120 return copy; | 113 return copy; |
121 } | 114 } |
122 | 115 |
123 if (e is List) { | 116 if (e is List) { |
124 // Since a JavaScript Array is an instance of Dart List it is tempting | 117 // Since a JavaScript Array is an instance of Dart List it is tempting |
125 // in dart2js to avoid making a copy of the list if there is no need | 118 // in dart2js to avoid making a copy of the list if there is no need |
126 // to copy anything reachable from the array. However, the list may have | 119 // to copy anything reachable from the array. However, the list may have |
127 // non-native properties or methods from interceptors and such, e.g. | 120 // non-native properties or methods from interceptors and such, e.g. |
128 // an immutability marker. So we had to stop doing that. | 121 // an immutability marker. So we had to stop doing that. |
129 var slot = findSlot(e); | 122 var copy = findSlot(e); |
130 var copy = readSlot(slot); | |
131 if (copy != null) return copy; | 123 if (copy != null) return copy; |
132 copy = copyList(e, slot); | 124 copy = copyList(e); |
133 return copy; | 125 return copy; |
134 } | 126 } |
135 | 127 |
136 throw new UnimplementedError('structured clone of other type'); | 128 throw new UnimplementedError('structured clone of other type'); |
137 } | 129 } |
138 | 130 |
139 copyList(List e, int slot) { | 131 copyList(List e) { |
140 int i = 0; | 132 int i = 0; |
141 int length = e.length; | 133 int length = e.length; |
142 var copy = newJsList(length); | 134 var copy = newJsList(length); |
143 writeSlot(slot, copy); | 135 writeSlot(e, copy); |
144 for ( ; i < length; i++) { | 136 for ( ; i < length; i++) { |
145 copy[i] = walk(e[i]); | 137 copy[i] = walk(e[i]); |
146 } | 138 } |
147 return copy; | 139 return copy; |
148 } | 140 } |
149 | 141 |
150 convertDartToNative_PrepareForStructuredClone(value) { | 142 convertDartToNative_PrepareForStructuredClone(value) { |
151 var copy = walk(value); | 143 var copy = walk(value); |
152 cleanupSlots(); | 144 cleanupSlots(); |
153 return copy; | 145 return copy; |
(...skipping 13 matching lines...) Expand all Loading... | |
167 * If necessary, JavaScript Dates are converted into Dart Dates. | 159 * If necessary, JavaScript Dates are converted into Dart Dates. |
168 * | 160 * |
169 * If [mustCopy] is [:true:], the entire object is copied and the original input | 161 * If [mustCopy] is [:true:], the entire object is copied and the original input |
170 * is not mutated. This should be the case where Dart and JavaScript code can | 162 * is not mutated. This should be the case where Dart and JavaScript code can |
171 * access the value, for example, via multiple event listeners for | 163 * access the value, for example, via multiple event listeners for |
172 * MessageEvents. Mutating the object to make it more 'Dart-like' would corrupt | 164 * MessageEvents. Mutating the object to make it more 'Dart-like' would corrupt |
173 * the value as seen from the JavaScript listeners. | 165 * the value as seen from the JavaScript listeners. |
174 */ | 166 */ |
175 abstract class _AcceptStructuredClone { | 167 abstract class _AcceptStructuredClone { |
176 | 168 |
177 // TODO(sra): Replace slots with identity hash table. | 169 // Keep track of the clones, keyed by the original object. If we're |
178 var values = []; | 170 // not copying, these may be the same. |
179 var copies = []; // initially 'null', 'true' during initial DFS, then a copy. | 171 var clones = new HashMap.identity(); |
180 bool mustCopy = false; | 172 bool mustCopy = false; |
181 | 173 |
182 int findSlot(value) { | 174 Object findSlot(value) { |
183 int length = values.length; | 175 return clones.putIfAbsent(value, () => null); |
184 for (int i = 0; i < length; i++) { | |
185 if (identicalInJs(values[i], value)) return i; | |
186 } | |
187 values.add(value); | |
188 copies.add(null); | |
189 return length; | |
190 } | 176 } |
191 | 177 |
192 /// Are the two objects identical, but taking into account that two JsObject | 178 /// Are the two objects identical, but taking into account that two JsObject |
193 /// wrappers may not be identical, but their underlying Js Object might be. | 179 /// wrappers may not be identical, but their underlying Js Object might be. |
194 bool identicalInJs(a, b); | 180 bool identicalInJs(a, b); |
195 readSlot(int i) => copies[i]; | 181 writeSlot(original, x) { clones[original] = x; } |
196 writeSlot(int i, x) { copies[i] = x; } | |
197 | 182 |
198 /// Iterate over the JS properties. | 183 /// Iterate over the JS properties. |
199 forEachJsField(object, action); | 184 forEachJsField(object, action); |
200 | 185 |
201 /// Create a new Dart list of the given length. May create a native List or | 186 /// Create a new Dart list of the given length. May create a native List or |
202 /// a JsArray, depending if we're in Dartium or dart2js. | 187 /// a JsArray, depending if we're in Dartium or dart2js. |
203 newDartList(length); | 188 newDartList(length); |
204 | 189 |
205 walk(e) { | 190 walk(e) { |
206 if (e == null) return e; | 191 if (e == null) return e; |
(...skipping 10 matching lines...) Expand all Loading... | |
217 throw new UnimplementedError('structured clone of RegExp'); | 202 throw new UnimplementedError('structured clone of RegExp'); |
218 } | 203 } |
219 | 204 |
220 if (isJavaScriptPromise(e)) { | 205 if (isJavaScriptPromise(e)) { |
221 return convertNativePromiseToDartFuture(e); | 206 return convertNativePromiseToDartFuture(e); |
222 } | 207 } |
223 | 208 |
224 if (isJavaScriptSimpleObject(e)) { | 209 if (isJavaScriptSimpleObject(e)) { |
225 // TODO(sra): If mustCopy is false, swizzle the prototype for one of a Map | 210 // TODO(sra): If mustCopy is false, swizzle the prototype for one of a Map |
226 // implementation that uses the properies as storage. | 211 // implementation that uses the properies as storage. |
227 var slot = findSlot(e); | 212 var copy = findSlot(e); |
228 var copy = readSlot(slot); | |
229 if (copy != null) return copy; | 213 if (copy != null) return copy; |
230 copy = {}; | 214 copy = {}; |
231 | 215 |
232 writeSlot(slot, copy); | 216 writeSlot(e, copy); |
233 forEachJsField(e, (key, value) => copy[key] = walk(value)); | 217 forEachJsField(e, (key, value) => copy[key] = walk(value)); |
234 return copy; | 218 return copy; |
235 } | 219 } |
236 | 220 |
237 if (isJavaScriptArray(e)) { | 221 if (isJavaScriptArray(e)) { |
238 var slot = findSlot(e); | 222 var copy = findSlot(e); |
239 var copy = readSlot(slot); | |
240 if (copy != null) return copy; | 223 if (copy != null) return copy; |
241 | 224 |
242 int length = e.length; | 225 int length = e.length; |
243 // Since a JavaScript Array is an instance of Dart List, we can modify it | 226 // Since a JavaScript Array is an instance of Dart List, we can modify it |
244 // in-place unless we must copy. | 227 // in-place unless we must copy. |
245 copy = mustCopy ? newDartList(length) : e; | 228 copy = mustCopy ? newDartList(length) : e; |
246 writeSlot(slot, copy); | 229 writeSlot(e, copy); |
247 | 230 |
248 for (int i = 0; i < length; i++) { | 231 for (int i = 0; i < length; i++) { |
249 copy[i] = walk(e[i]); | 232 copy[i] = walk(e[i]); |
250 } | 233 } |
251 return copy; | 234 return copy; |
252 } | 235 } |
253 | 236 |
254 // Assume anything else is already a valid Dart object, either by having | 237 // Assume anything else is already a valid Dart object, either by having |
255 // already been processed, or e.g. a clonable native class. | 238 // already been processed, or e.g. a clonable native class. |
256 return e; | 239 return e; |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
355 const String _serializedScriptValue = | 338 const String _serializedScriptValue = |
356 'num|String|bool|' | 339 'num|String|bool|' |
357 'JSExtendableArray|=Object|' | 340 'JSExtendableArray|=Object|' |
358 'Blob|File|NativeByteBuffer|NativeTypedData' | 341 'Blob|File|NativeByteBuffer|NativeTypedData' |
359 // TODO(sra): Add Date, RegExp. | 342 // TODO(sra): Add Date, RegExp. |
360 ; | 343 ; |
361 const annotation_Creates_SerializedScriptValue = | 344 const annotation_Creates_SerializedScriptValue = |
362 const Creates(_serializedScriptValue); | 345 const Creates(_serializedScriptValue); |
363 const annotation_Returns_SerializedScriptValue = | 346 const annotation_Returns_SerializedScriptValue = |
364 const Returns(_serializedScriptValue); | 347 const Returns(_serializedScriptValue); |
OLD | NEW |