Index: sdk/lib/html/html_common/conversions_dartium.dart |
diff --git a/sdk/lib/html/html_common/conversions_dartium.dart b/sdk/lib/html/html_common/conversions_dartium.dart |
index ad71852616a530d98e955cbeb415304199d7f0c2..66299a05389fd4c7b79a0ef086ac052eccdd82ec 100644 |
--- a/sdk/lib/html/html_common/conversions_dartium.dart |
+++ b/sdk/lib/html/html_common/conversions_dartium.dart |
@@ -14,7 +14,14 @@ class _StructuredCloneDartium extends _StructuredClone { |
cloneNotRequired(e) => e is js.JsObject; |
} |
-class _AcceptStructuredCloneDartium extends _AcceptStructuredClone { |
+/// A version of _AcceptStructuredClone, but using a different algorithm |
+/// so we can take advantage of an identity HashMap on Dartium without |
+/// the bad side-effect of modifying the JS source objects if we do the same in |
+/// dart2js. |
+/// |
+/// This no longer inherits anything from _AcceptStructuredClone |
+/// and is never used polymorphically with it, so it doesn't inherit. |
+class _AcceptStructuredCloneDartium { |
newDartList(length) => new List(length); |
// JsObjects won't be identical, but will be equal only if the underlying |
@@ -28,6 +35,75 @@ class _AcceptStructuredCloneDartium extends _AcceptStructuredClone { |
action(key, jsObject[key]); |
} |
} |
+ |
+ // Keep track of the clones, keyed by the original object. If we're |
+ // not copying, these may be the same. |
+ var clones = new HashMap.identity(); |
+ bool mustCopy = false; |
+ |
+ Object findSlot(value) { |
+ return clones.putIfAbsent(value, () => null); |
+ } |
+ |
+ writeSlot(original, x) { clones[original] = x; } |
+ |
+ walk(e) { |
+ if (e == null) return e; |
+ if (e is bool) return e; |
+ if (e is num) return e; |
+ if (e is String) return e; |
+ |
+ if (isJavaScriptDate(e)) { |
+ return convertNativeToDart_DateTime(e); |
+ } |
+ |
+ if (isJavaScriptRegExp(e)) { |
+ // TODO(sra). |
+ throw new UnimplementedError('structured clone of RegExp'); |
+ } |
+ |
+ if (isJavaScriptPromise(e)) { |
+ return convertNativePromiseToDartFuture(e); |
+ } |
+ |
+ if (isJavaScriptSimpleObject(e)) { |
+ // TODO(sra): If mustCopy is false, swizzle the prototype for one of a Map |
+ // implementation that uses the properies as storage. |
+ var copy = findSlot(e); |
+ if (copy != null) return copy; |
+ copy = {}; |
+ |
+ writeSlot(e, copy); |
+ forEachJsField(e, (key, value) => copy[key] = walk(value)); |
+ return copy; |
+ } |
+ |
+ if (isJavaScriptArray(e)) { |
+ var copy = findSlot(e); |
+ if (copy != null) return copy; |
+ |
+ int length = e.length; |
+ // Since a JavaScript Array is an instance of Dart List, we can modify it |
+ // in-place unless we must copy. |
+ copy = mustCopy ? newDartList(length) : e; |
+ writeSlot(e, copy); |
+ |
+ for (int i = 0; i < length; i++) { |
+ copy[i] = walk(e[i]); |
+ } |
+ return copy; |
+ } |
+ |
+ // Assume anything else is already a valid Dart object, either by having |
+ // already been processed, or e.g. a clonable native class. |
+ return e; |
+ } |
+ |
+ convertNativeToDart_AcceptStructuredClone(object, {mustCopy: false}) { |
+ this.mustCopy = mustCopy; |
+ var copy = walk(object); |
+ return copy; |
+ } |
} |
final _dateConstructor = js.context["Date"]; |