Chromium Code Reviews| 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..2333985516079562bf6cf33209b01474d0281f91 |
| --- /dev/null |
| +++ b/sdk/lib/isolate/mangler.dart |
| @@ -0,0 +1,250 @@ |
| +// 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. |
| + |
| +class _IsolateEncoder { |
| + final manglingToken; |
| + 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]; |
|
Lasse Reichstein Nielsen
2012/11/22 10:23:15
This uses equality, not identiity. We should use a
floitsch
2012/11/22 20:06:12
Added TODO.
|
| + 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; |
| + } |
| + |
| + 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(data.length); |
| + for (int j = 0; j < i; j++) { |
| + result[j] = data[j]; |
| + } |
|
Lasse Reichstein Nielsen
2012/11/22 10:23:15
hasBeenDuplicated = true; ?
floitsch
2012/11/22 20:06:12
Done.
|
| + } |
| + if (hasBeenDuplicated) { |
| + result[i] = mangled; |
| + } |
| + } |
| + result = _escapeIfNecessary(result); |
| + } else if (data is Set) { |
| + bool needsCopy = false; |
| + data.forEach((entry) { |
| + var encoded = encode(entry); |
| + if (encoded != entry) needsCopy = true; |
|
Lasse Reichstein Nielsen
2012/11/22 10:23:15
and break. No need to test more after the first mo
floitsch
2012/11/22 20:06:12
Duplicate encoding is not a problem since they are
|
| + }); |
| + 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; |
|
Lasse Reichstein Nielsen
2012/11/22 10:23:15
And break after the first encoding is found.
floitsch
2012/11/22 20:06:12
cannot. This is a map.forEach.
Lasse Reichstein Nielsen
2012/11/23 12:12:38
True. You would need to iterate the keys instead.
|
| + }); |
| + 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. |
| + _visiting.remove(data); |
|
Lasse Reichstein Nielsen
2012/11/22 10:23:15
Consider keeping it and throwing if you see the sa
floitsch
2012/11/22 20:06:12
Added TODO.
|
| + 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 = encode(_declareReference(selfReference, data)); |
| + } |
| + _encoded[data] = result; |
| + |
| + _visiting.remove(data); |
| + return result; |
| + } |
| + |
| + _createReference() => [manglingToken, _REFERENCE]; |
|
Lasse Reichstein Nielsen
2012/11/22 10:23:15
What is it a reference to? I.e., how can you tell
floitsch
2012/11/22 20:06:12
Done.
|
| + _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]; |
|
Lasse Reichstein Nielsen
2012/11/22 10:23:15
Then the list contents are not encoded. You should
floitsch
2012/11/22 20:06:12
_escapeIfNecessary is only called on a encoded lis
Lasse Reichstein Nielsen
2012/11/23 12:12:38
Ah, true.
|
| + } 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]; |
|
Lasse Reichstein Nielsen
2012/11/22 10:23:15
if 'data' comes from a JSON.parse, it's not equal
floitsch
2012/11/22 20:06:12
data comes from the isolate-communication. These g
|
| + |
| + if (_isDeclaration(data)) { |
| + var reference = _extractReference(data); |
| + var declared = _extractDeclared(data); |
| + return _decodeObject(declared, reference); |
|
Lasse Reichstein Nielsen
2012/11/22 10:23:15
So the reference is just a list: [magic#, REFERENC
floitsch
2012/11/22 20:06:12
This works because of the guarantees of the send/r
|
| + } else { |
| + return _decodeObject(data, null); |
| + } |
| + } |
| + |
| + _decodeObject(data, reference) { |
| + if (_decoded.containsKey(data)) { |
|
Lasse Reichstein Nielsen
2012/11/22 10:23:15
I don't think this lookup will ever succeed if the
floitsch
2012/11/22 20:06:12
which it hasn't.
|
| + assert(reference == null); |
| + return _decoded[data]; |
| + } |
| + |
| + // If the data was a reference then we would have found it in the _decoded |
| + // map. |
|
Lasse Reichstein Nielsen
2012/11/22 10:23:15
I find that unlikely.
floitsch
2012/11/22 20:06:12
see above.
|
| + 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); |
|
Lasse Reichstein Nielsen
2012/11/22 10:23:15
Here you decode the escaped data, even if it wasn'
floitsch
2012/11/22 20:06:12
data cannot be mangled and escaped at the same tim
Lasse Reichstein Nielsen
2012/11/23 12:12:38
Ack, isMangled is not the same as isMarked.
|
| + 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) { |
| + data.forEach((entry) { |
| + var decoded = decode(entry); |
| + if (decoded != entry) needsCopy = true; |
|
Lasse Reichstein Nielsen
2012/11/22 10:23:15
and break.
floitsch
2012/11/22 20:06:12
Done.
|
| + }); |
| + } |
| + result = data; |
| + if (needsCopy) { |
| + result = new Set(); |
| + if (reference != null) _decoded[reference] = result; |
| + data.forEach((entry) { |
| + 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) |
| + => data is List && !data.isEmpty && data[0] == manglingToken; |
|
Lasse Reichstein Nielsen
2012/11/22 10:23:15
'!data.isEmpty' -> 'data.length >= 2' or you can't
floitsch
2012/11/22 20:06:12
added assert.
|
| + _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]; |
| + } |
| +} |