Index: sdk/lib/isolate/mangler.dart |
diff --git a/sdk/lib/isolate/mangler.dart b/sdk/lib/isolate/mangler.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a0e16721a4d9d0cd220681319342f05cd7555382 |
--- /dev/null |
+++ b/sdk/lib/isolate/mangler.dart |
@@ -0,0 +1,270 @@ |
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+part of dart.isolate; |
+ |
+class _IsolateEncoder { |
+ final manglingToken; |
+ // TODO(floitsch): switch to identity set. |
+ final Map _encoded = new Map(); |
+ final Map _visiting = new Map(); |
+ final Function _mangle; |
+ static const int _REFERENCE = 0; |
+ static const int _DECLARATION = 1; |
+ static const int _ESCAPED = 2; |
+ static const int _MANGLED = 3; |
+ |
+ _IsolateEncoder(this.manglingToken, mangle(data)) |
+ : this._mangle = mangle; |
+ |
+ encode(var data) { |
+ if (data is num || data is String || data is bool || data == null) { |
+ return data; |
+ } |
+ |
+ if (_encoded.containsKey(data)) return _encoded[data]; |
+ if (_visiting.containsKey(data)) { |
+ // Self reference. |
+ var selfReference = _visiting[data]; |
+ if (selfReference == data) { |
+ // Nobody used the self-reference yet. |
+ selfReference = _createReference(); |
+ _visiting[data] = selfReference; |
+ } |
+ return selfReference; |
+ } |
+ _visiting[data] = data; |
+ |
+ var result; |
+ |
+ if (data is List) { |
+ bool hasBeenDuplicated = false; |
+ result = data; |
+ for (int i = 0; i < data.length; i++) { |
+ var mangled = encode(data[i]); |
+ if (mangled != data[i] && !hasBeenDuplicated) { |
+ result = new List.fixedLength(data.length); |
+ for (int j = 0; j < i; j++) { |
+ result[j] = data[j]; |
+ } |
+ hasBeenDuplicated = true; |
+ } |
+ if (hasBeenDuplicated) { |
+ result[i] = mangled; |
+ } |
+ } |
+ result = _escapeIfNecessary(result); |
+ } else if (data is Set) { |
+ // TODO(floitsch): should we accept sets? |
+ bool needsCopy = false; |
+ for (var entry in data) { |
+ var encoded = encode(entry); |
+ if (encoded != entry) { |
+ needsCopy = true; |
+ break; |
+ } |
+ } |
+ result = data; |
+ if (needsCopy) { |
+ result = new Set(); |
+ data.forEach((entry) { |
+ result.add(encode(entry)); |
+ }); |
+ } |
+ } else if (data is Map) { |
+ bool needsCopy = false; |
+ data.forEach((key, value) { |
+ var encodedKey = encode(key); |
+ var encodedValue = encode(value); |
+ if (encodedKey != key) needsCopy = true; |
+ if (encodedValue != value) needsCopy = true; |
+ }); |
+ result = data; |
+ if (needsCopy) { |
+ result = new Map(); |
+ data.forEach((key, value) { |
+ result[encode(key)] = encode(value); |
+ }); |
+ } |
+ } else { |
+ // We don't handle self-references for user data. |
+ // TODO(floitsch): we could keep the reference and throw when we see it |
+ // again. However now the user has at least the possibility to do |
+ // cyclic data-structures. |
+ _visiting.remove(data); |
+ result = _mangle(data); |
+ if (result != data) { |
+ result = _wrapMangled(encode(result)); |
+ } |
+ } |
+ |
+ var selfReference = _visiting[data]; |
+ if (selfReference != null && selfReference != data) { |
+ // A self-reference has been used. |
+ result = _declareReference(selfReference, result); |
+ } |
+ _encoded[data] = result; |
+ |
+ _visiting.remove(data); |
+ return result; |
+ } |
+ |
+ _createReference() => [manglingToken, _REFERENCE]; |
+ _declareReference(reference, data) { |
+ return [manglingToken, _DECLARATION, reference, data]; |
+ } |
+ |
+ _wrapMangled(data) => [manglingToken, _MANGLED, data]; |
+ _escapeIfNecessary(List list) { |
+ if (!list.isEmpty && list[0] == manglingToken) { |
+ return [manglingToken, _ESCAPED, list]; |
+ } else { |
+ return list; |
+ } |
+ } |
+} |
+ |
+class _IsolateDecoder { |
+ final manglingToken; |
+ final Map _decoded = new Map(); |
+ final Function _unmangle; |
+ static const int _REFERENCE = _IsolateEncoder._REFERENCE; |
+ static const int _DECLARATION = _IsolateEncoder._DECLARATION; |
+ static const int _ESCAPED = _IsolateEncoder._ESCAPED; |
+ static const int _MANGLED = _IsolateEncoder._MANGLED; |
+ |
+ _IsolateDecoder(this.manglingToken, unmangle(data)) |
+ : this._unmangle = unmangle; |
+ |
+ decode(var data) { |
+ if (data is num || data is String || data is bool || data == null) { |
+ return data; |
+ } |
+ |
+ if (_decoded.containsKey(data)) return _decoded[data]; |
+ |
+ if (_isDeclaration(data)) { |
+ var reference = _extractReference(data); |
+ var declared = _extractDeclared(data); |
+ return _decodeObject(declared, reference); |
+ } else { |
+ return _decodeObject(data, null); |
+ } |
+ } |
+ |
+ _decodeObject(data, reference) { |
+ if (_decoded.containsKey(data)) { |
+ assert(reference == null); |
+ return _decoded[data]; |
+ } |
+ |
+ // If the data was a reference then we would have found it in the _decoded |
+ // map. |
+ assert(!_isReference(data)); |
+ |
+ var result; |
+ if (_isMangled(data)) { |
+ assert(reference == null); |
+ List mangled = _extractMangled(data); |
+ var decoded = decode(mangled); |
+ result = _unmangle(decoded); |
+ } else if (data is List) { |
+ if (_isEscaped(data)) data = _extractEscaped(data); |
+ assert(!_isMarked(data)); |
+ result = data; |
+ bool hasBeenDuplicated = false; |
+ List duplicate() { |
+ assert(!hasBeenDuplicated); |
+ result = new List(); |
+ result.length = data.length; |
+ if (reference != null) _decoded[reference] = result; |
+ hasBeenDuplicated = true; |
+ } |
+ |
+ if (reference != null) duplicate(); |
+ for (int i = 0; i < data.length; i++) { |
+ var decoded = decode(data[i]); |
+ if (decoded != data[i] && !hasBeenDuplicated) { |
+ duplicate(); |
+ for (int j = 0; j < i; j++) { |
+ result[j] = data[j]; |
+ } |
+ } |
+ if (hasBeenDuplicated) { |
+ result[i] = decoded; |
+ } |
+ } |
+ } else if (data is Set) { |
+ bool needsCopy = reference != null; |
+ if (!needsCopy) { |
+ for (var entry in data) { |
+ var decoded = decode(entry); |
+ if (decoded != entry) { |
+ needsCopy = true; |
+ break; |
+ } |
+ } |
+ } |
+ result = data; |
+ if (needsCopy) { |
+ result = new Set(); |
+ if (reference != null) _decoded[reference] = result; |
+ for (var entry in data) { |
+ result.add(decode(entry)); |
+ } |
+ } |
+ } else if (data is Map) { |
+ bool needsCopy = reference != null; |
+ if (!needsCopy) { |
+ data.forEach((key, value) { |
+ var decodedKey = decode(key); |
+ var decodedValue = decode(value); |
+ if (decodedKey != key) needsCopy = true; |
+ if (decodedValue != value) needsCopy = true; |
+ }); |
+ } |
+ result = data; |
+ if (needsCopy) { |
+ result = new Map(); |
+ if (reference != null) _decoded[reference] = result; |
+ data.forEach((key, value) { |
+ result[decode(key)] = decode(value); |
+ }); |
+ } |
+ } else { |
+ result = data; |
+ } |
+ _decoded[data] = result; |
+ return result; |
+ } |
+ |
+ _isMarked(data) { |
+ if (data is List && !data.isEmpty && data[0] == manglingToken) { |
+ assert(data.length > 1); |
+ return true; |
+ } |
+ return false; |
+ } |
+ _isReference(data) => _isMarked(data) && data[1] == _REFERENCE; |
+ _isDeclaration(data) => _isMarked(data) && data[1] == _DECLARATION; |
+ _isMangled(data) => _isMarked(data) && data[1] == _MANGLED; |
+ _isEscaped(data) => _isMarked(data) && data[1] == _ESCAPED; |
+ |
+ _extractReference(declaration) { |
+ assert(_isDeclaration(declaration)); |
+ return declaration[2]; |
+ } |
+ _extractDeclared(declaration) { |
+ assert(_isDeclaration(declaration)); |
+ return declaration[3]; |
+ } |
+ _extractMangled(wrappedMangled) { |
+ assert(_isMangled(wrappedMangled)); |
+ return wrappedMangled[2]; |
+ } |
+ _extractEscaped(data) { |
+ assert(_isEscaped(data)); |
+ return data[2]; |
+ } |
+} |