mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 15:17:07 +00:00
Isolates: allow sending of arbitrary objects in dart2js.
BUG= http://dartbug.com/12628 BUG= http://dartbug.com/9245 R=lrn@google.com, sigurdm@google.com, sra@google.com Committed: https://code.google.com/p/dart/source/detail?r=42161 Reverted: https://code.google.com/p/dart/source/detail?r=42206 Review URL: https://codereview.chromium.org//742873002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@42209 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
56defa8060
commit
a5f3c39b21
14 changed files with 1012 additions and 613 deletions
|
@ -85,7 +85,7 @@ class ClassEmitter extends CodeEmitterHelper {
|
|||
OutputUnit outputUnit =
|
||||
compiler.deferredLoadTask.outputUnitForElement(classElement);
|
||||
emitter.emitPrecompiledConstructor(
|
||||
outputUnit, constructorName, constructorAst);
|
||||
outputUnit, constructorName, constructorAst, fields);
|
||||
}
|
||||
|
||||
/// Returns `true` if fields added.
|
||||
|
|
|
@ -74,9 +74,8 @@ class OldEmitter implements Emitter {
|
|||
* precompiled function.
|
||||
*
|
||||
* To save space, dart2js normally generates constructors and accessors
|
||||
* dynamically. This doesn't work in CSP mode, and may impact startup time
|
||||
* negatively. So dart2js will emit these functions to a separate file that
|
||||
* can be optionally included to support CSP mode or for faster startup.
|
||||
* dynamically. This doesn't work in CSP mode, so dart2js emits them directly
|
||||
* when in CSP mode.
|
||||
*/
|
||||
Map<OutputUnit, List<jsAst.Node>> _cspPrecompiledFunctions =
|
||||
new Map<OutputUnit, List<jsAst.Node>>();
|
||||
|
@ -166,6 +165,11 @@ class OldEmitter implements Emitter {
|
|||
String get makeConstListProperty
|
||||
=> namer.getMappedInstanceName('makeConstantList');
|
||||
|
||||
/// The name of the property that contains all field names.
|
||||
///
|
||||
/// This property is added to constructors when isolate support is enabled.
|
||||
static const String FIELD_NAMES_PROPERTY_NAME = r"$__fields__";
|
||||
|
||||
/// For deferred loading we communicate the initializers via this global var.
|
||||
final String deferredInitializers = r"$dart_deferred_initializers";
|
||||
|
||||
|
@ -182,7 +186,7 @@ class OldEmitter implements Emitter {
|
|||
}
|
||||
|
||||
jsAst.Expression isolateLazyInitializerAccess(Element element) {
|
||||
return jsAst.js('#.#', [namer.globalObjectFor(element),
|
||||
return jsAst.js('#.#', [namer.globalObjectFor(element),
|
||||
namer.getLazyInitializerName(element)]);
|
||||
}
|
||||
|
||||
|
@ -190,7 +194,7 @@ class OldEmitter implements Emitter {
|
|||
return jsAst.js('#.#()',
|
||||
[namer.globalObjectFor(element), namer.getStaticClosureName(element)]);
|
||||
}
|
||||
|
||||
|
||||
jsAst.PropertyAccess globalPropertyAccess(Element element) {
|
||||
String name = namer.getNameX(element);
|
||||
jsAst.PropertyAccess pa = new jsAst.PropertyAccess.field(
|
||||
|
@ -198,19 +202,19 @@ class OldEmitter implements Emitter {
|
|||
name);
|
||||
return pa;
|
||||
}
|
||||
|
||||
|
||||
jsAst.PropertyAccess staticFieldAccess(Element element) {
|
||||
return globalPropertyAccess(element);
|
||||
}
|
||||
|
||||
|
||||
jsAst.PropertyAccess staticFunctionAccess(Element element) {
|
||||
return globalPropertyAccess(element);
|
||||
}
|
||||
|
||||
|
||||
jsAst.PropertyAccess classAccess(Element element) {
|
||||
return globalPropertyAccess(element);
|
||||
}
|
||||
|
||||
|
||||
jsAst.PropertyAccess typedefAccess(Element element) {
|
||||
return globalPropertyAccess(element);
|
||||
}
|
||||
|
@ -288,7 +292,7 @@ class OldEmitter implements Emitter {
|
|||
}''');
|
||||
}
|
||||
|
||||
List get defineClassFunction {
|
||||
List<jsAst.Node> get defineClassFunction {
|
||||
// First the class name, then the field names in an array and the members
|
||||
// (inside an Object literal).
|
||||
// The caller can also pass in the constructor as a function if needed.
|
||||
|
@ -303,45 +307,99 @@ class OldEmitter implements Emitter {
|
|||
// },
|
||||
// });
|
||||
|
||||
var defineClass = js('''function(name, fields) {
|
||||
var accessors = [];
|
||||
bool hasIsolateSupport = compiler.hasIsolateSupport;
|
||||
String fieldNamesProperty = FIELD_NAMES_PROPERTY_NAME;
|
||||
|
||||
var str = "function " + name + "(";
|
||||
var body = "";
|
||||
jsAst.Expression defineClass = js('''
|
||||
function(name, fields) {
|
||||
var accessors = [];
|
||||
|
||||
var str = "function " + name + "(";
|
||||
var body = "";
|
||||
if (#hasIsolateSupport) { var fieldNames = ""; }
|
||||
|
||||
for (var i = 0; i < fields.length; i++) {
|
||||
if(i != 0) str += ", ";
|
||||
|
||||
var field = generateAccessor(fields[i], accessors, name);
|
||||
if (#hasIsolateSupport) { fieldNames += "'" + field + "',"; }
|
||||
var parameter = "parameter_" + field;
|
||||
str += parameter;
|
||||
body += ("this." + field + " = " + parameter + ";\\n");
|
||||
}
|
||||
str += ") {\\n" + body + "}\\n";
|
||||
str += name + ".builtin\$cls=\\"" + name + "\\";\\n";
|
||||
str += "\$desc=\$collectedClasses." + name + ";\\n";
|
||||
str += "if(\$desc instanceof Array) \$desc = \$desc[1];\\n";
|
||||
str += name + ".prototype = \$desc;\\n";
|
||||
if (typeof defineClass.name != "string") {
|
||||
str += name + ".name=\\"" + name + "\\";\\n";
|
||||
}
|
||||
if (#hasIsolateSupport) {
|
||||
str += name + ".$fieldNamesProperty=[" + fieldNames + "];\\n";
|
||||
}
|
||||
str += accessors.join("");
|
||||
|
||||
return str;
|
||||
}''', { 'hasIsolateSupport': hasIsolateSupport });
|
||||
|
||||
for (var i = 0; i < fields.length; i++) {
|
||||
if(i != 0) str += ", ";
|
||||
|
||||
var field = generateAccessor(fields[i], accessors, name);
|
||||
var parameter = "parameter_" + field;
|
||||
str += parameter;
|
||||
body += ("this." + field + " = " + parameter + ";\\n");
|
||||
}
|
||||
str += ") {\\n" + body + "}\\n";
|
||||
str += name + ".builtin\$cls=\\"" + name + "\\";\\n";
|
||||
str += "\$desc=\$collectedClasses." + name + ";\\n";
|
||||
str += "if(\$desc instanceof Array) \$desc = \$desc[1];\\n";
|
||||
str += name + ".prototype = \$desc;\\n";
|
||||
if (typeof defineClass.name != "string") {
|
||||
str += name + ".name=\\"" + name + "\\";\\n";
|
||||
}
|
||||
str += accessors.join("");
|
||||
|
||||
return str;
|
||||
}''');
|
||||
// Declare a function called "generateAccessor". This is used in
|
||||
// defineClassFunction (it's a local declaration in init()).
|
||||
List<jsAst.Node> saveDefineClass = [];
|
||||
if (compiler.hasIncrementalSupport) {
|
||||
saveDefineClass.add(
|
||||
js(r'self.$dart_unsafe_eval.defineClass = defineClass'));
|
||||
}
|
||||
return [
|
||||
List result = <jsAst.Node>[
|
||||
generateAccessorFunction,
|
||||
js('$generateAccessorHolder = generateAccessor'),
|
||||
new jsAst.FunctionDeclaration(
|
||||
new jsAst.VariableDeclaration('defineClass'), defineClass) ]
|
||||
..addAll(saveDefineClass);
|
||||
new jsAst.VariableDeclaration('defineClass'), defineClass) ];
|
||||
|
||||
if (compiler.hasIncrementalSupport) {
|
||||
result.add(
|
||||
js(r'self.$dart_unsafe_eval.defineClass = defineClass'));
|
||||
}
|
||||
|
||||
if (hasIsolateSupport) {
|
||||
jsAst.Expression classIdExtractorAccess =
|
||||
generateEmbeddedGlobalAccess(embeddedNames.CLASS_ID_EXTRACTOR);
|
||||
var classIdExtractorAssignment =
|
||||
js('# = function(o) { return o.constructor.name; }',
|
||||
classIdExtractorAccess);
|
||||
|
||||
jsAst.Expression classFieldsExtractorAccess =
|
||||
generateEmbeddedGlobalAccess(embeddedNames.CLASS_FIELDS_EXTRACTOR);
|
||||
var classFieldsExtractorAssignment = js('''
|
||||
# = function(o) {
|
||||
var fieldNames = o.constructor.$fieldNamesProperty;
|
||||
if (!fieldNames) return []; // TODO(floitsch): do something else here.
|
||||
var result = [];
|
||||
result.length = fieldNames.length;
|
||||
for (var i = 0; i < fieldNames.length; i++) {
|
||||
result[i] = o[fieldNames[i]];
|
||||
}
|
||||
return result;
|
||||
}''', classFieldsExtractorAccess);
|
||||
|
||||
jsAst.Expression instanceFromClassIdAccess =
|
||||
generateEmbeddedGlobalAccess(embeddedNames.INSTANCE_FROM_CLASS_ID);
|
||||
jsAst.Expression allClassesAccess =
|
||||
generateEmbeddedGlobalAccess(embeddedNames.ALL_CLASSES);
|
||||
var instanceFromClassIdAssignment =
|
||||
js('# = function(name) { return new #[name](); }',
|
||||
[instanceFromClassIdAccess, allClassesAccess]);
|
||||
|
||||
jsAst.Expression initializeEmptyInstanceAccess =
|
||||
generateEmbeddedGlobalAccess(embeddedNames.INITIALIZE_EMPTY_INSTANCE);
|
||||
var initializeEmptyInstanceAssignment = js('''
|
||||
# = function(name, o, fields) {
|
||||
#[name].apply(o, fields);
|
||||
return o;
|
||||
}''', [ initializeEmptyInstanceAccess, allClassesAccess ]);
|
||||
|
||||
result.addAll([classIdExtractorAssignment,
|
||||
classFieldsExtractorAssignment,
|
||||
instanceFromClassIdAssignment,
|
||||
initializeEmptyInstanceAssignment]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Needs defineClass to be defined. */
|
||||
|
@ -1310,25 +1368,34 @@ class OldEmitter implements Emitter {
|
|||
|
||||
void emitPrecompiledConstructor(OutputUnit outputUnit,
|
||||
String constructorName,
|
||||
jsAst.Expression constructorAst) {
|
||||
jsAst.Expression constructorAst,
|
||||
List<String> fields) {
|
||||
cspPrecompiledFunctionFor(outputUnit).add(
|
||||
new jsAst.FunctionDeclaration(
|
||||
new jsAst.VariableDeclaration(constructorName), constructorAst));
|
||||
cspPrecompiledFunctionFor(outputUnit).add(
|
||||
js.statement(r'''{
|
||||
#.builtin$cls = #;
|
||||
if (!"name" in #)
|
||||
#.name = #;
|
||||
$desc=$collectedClasses.#;
|
||||
|
||||
String fieldNamesProperty = FIELD_NAMES_PROPERTY_NAME;
|
||||
bool hasIsolateSupport = compiler.hasIsolateSupport;
|
||||
jsAst.Node fieldNamesArray =
|
||||
hasIsolateSupport ? js.stringArray(fields) : new jsAst.LiteralNull();
|
||||
|
||||
cspPrecompiledFunctionFor(outputUnit).add(js.statement(r'''
|
||||
{
|
||||
#constructorName.builtin$cls = #constructorNameString;
|
||||
if (!"name" in #constructorName)
|
||||
#constructorName.name = #constructorNameString;
|
||||
$desc = $collectedClasses.#constructorName;
|
||||
if ($desc instanceof Array) $desc = $desc[1];
|
||||
#.prototype = $desc;
|
||||
#constructorName.prototype = $desc;
|
||||
''' /* next string is not a raw string */ '''
|
||||
if (#hasIsolateSupport) {
|
||||
#constructorName.$fieldNamesProperty = #fieldNamesArray;
|
||||
}
|
||||
}''',
|
||||
[ constructorName, js.string(constructorName),
|
||||
constructorName,
|
||||
constructorName, js.string(constructorName),
|
||||
constructorName,
|
||||
constructorName
|
||||
]));
|
||||
{"constructorName": constructorName,
|
||||
"constructorNameString": js.string(constructorName),
|
||||
"hasIsolateSupport": hasIsolateSupport,
|
||||
"fieldNamesArray": fieldNamesArray}));
|
||||
|
||||
cspPrecompiledConstructorNamesFor(outputUnit).add(js('#', constructorName));
|
||||
}
|
||||
|
@ -1391,9 +1458,11 @@ class OldEmitter implements Emitter {
|
|||
// Also emit a trivial constructor for CSP mode.
|
||||
String constructorName = mangledName;
|
||||
jsAst.Expression constructorAst = js('function() {}');
|
||||
List<String> fieldNames = [];
|
||||
emitPrecompiledConstructor(mainOutputUnit,
|
||||
constructorName,
|
||||
constructorAst);
|
||||
constructorAst,
|
||||
fieldNames);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
// Patch file for dart:collection classes.
|
||||
import 'dart:_foreign_helper' show JS;
|
||||
import 'dart:_js_helper' show fillLiteralMap, NoInline, NoThrows, patch;
|
||||
import 'dart:_js_helper' show
|
||||
fillLiteralMap, InternalMap, NoInline, NoThrows, patch;
|
||||
|
||||
@patch
|
||||
class HashMap<K, V> {
|
||||
|
@ -529,7 +530,7 @@ class LinkedHashMap<K, V> {
|
|||
}
|
||||
}
|
||||
|
||||
class _LinkedHashMap<K, V> implements LinkedHashMap<K, V> {
|
||||
class _LinkedHashMap<K, V> implements LinkedHashMap<K, V>, InternalMap {
|
||||
int _length = 0;
|
||||
|
||||
// The hash map contents are divided into three parts: one part for
|
||||
|
|
|
@ -5,19 +5,27 @@
|
|||
library _isolate_helper;
|
||||
|
||||
import 'shared/embedded_names.dart' show
|
||||
CLASS_ID_EXTRACTOR,
|
||||
CLASS_FIELDS_EXTRACTOR,
|
||||
CURRENT_SCRIPT,
|
||||
GLOBAL_FUNCTIONS;
|
||||
GLOBAL_FUNCTIONS,
|
||||
INITIALIZE_EMPTY_INSTANCE,
|
||||
INSTANCE_FROM_CLASS_ID;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:collection' show Queue, HashMap;
|
||||
import 'dart:isolate';
|
||||
import 'dart:_native_typed_data' show NativeByteBuffer, NativeTypedData;
|
||||
|
||||
import 'dart:_js_helper' show
|
||||
Closure,
|
||||
InternalMap,
|
||||
Null,
|
||||
Primitives,
|
||||
convertDartClosureToJS,
|
||||
random64,
|
||||
requiresPreamble;
|
||||
|
||||
import 'dart:_foreign_helper' show DART_CLOSURE_TO_JS,
|
||||
JS,
|
||||
JS_CREATE_ISOLATE,
|
||||
|
@ -26,7 +34,17 @@ import 'dart:_foreign_helper' show DART_CLOSURE_TO_JS,
|
|||
JS_EMBEDDED_GLOBAL,
|
||||
JS_SET_CURRENT_ISOLATE,
|
||||
IsolateContext;
|
||||
import 'dart:_interceptors' show JSExtendableArray;
|
||||
|
||||
import 'dart:_interceptors' show Interceptor,
|
||||
JSArray,
|
||||
JSExtendableArray,
|
||||
JSFixedArray,
|
||||
JSIndexable,
|
||||
JSMutableArray,
|
||||
JSObject;
|
||||
|
||||
|
||||
part 'isolate_serialization.dart';
|
||||
|
||||
/**
|
||||
* Called by the compiler to support switching
|
||||
|
@ -185,13 +203,6 @@ class _Manager {
|
|||
*/
|
||||
bool get useWorkers => supportsWorkers;
|
||||
|
||||
/**
|
||||
* Whether to use the web-worker JSON-based message serialization protocol. By
|
||||
* default this is only used with web workers. For debugging, you can force
|
||||
* using this protocol by changing this field value to [:true:].
|
||||
*/
|
||||
bool get needSerialization => useWorkers;
|
||||
|
||||
/**
|
||||
* Registry of isolates. Isolates must be registered if, and only if, receive
|
||||
* ports are alive. Normally no open receive-ports means that the isolate is
|
||||
|
@ -980,6 +991,10 @@ class IsolateNatives {
|
|||
bool startPaused,
|
||||
SendPort replyPort,
|
||||
void onError(String message)) {
|
||||
// Make sure that the args list is a fresh generic list. A newly spawned
|
||||
// isolate should be able to assume that the arguments list is an
|
||||
// extendable list.
|
||||
if (args != null) args = new List<String>.from(args);
|
||||
if (_globalState.isWorker) {
|
||||
_globalState.mainManager.postMessage(_serializeMessage({
|
||||
'command': 'spawn-worker',
|
||||
|
@ -1007,8 +1022,13 @@ class IsolateNatives {
|
|||
throw new UnsupportedError(
|
||||
"Currently spawnUri is not supported without web workers.");
|
||||
}
|
||||
message = _serializeMessage(message);
|
||||
args = _serializeMessage(args); // Or just args.toList() ?
|
||||
// Clone the message to enforce the restrictions we have on isolate
|
||||
// messages.
|
||||
message = _clone(message);
|
||||
// Make sure that the args list is a fresh generic list. A newly spawned
|
||||
// isolate should be able to assume that the arguments list is an
|
||||
// extendable list.
|
||||
if (args != null) args = new List<String>.from(args);
|
||||
_globalState.topEventLoop.enqueue(new _IsolateContext(), () {
|
||||
final func = _getJSFunctionFromName(functionName);
|
||||
_startIsolate(func, args, message, isSpawnUri, startPaused, replyPort);
|
||||
|
@ -1165,28 +1185,15 @@ class _NativeJsSendPort extends _BaseSendPort implements SendPort {
|
|||
final isolate = _globalState.isolates[_isolateId];
|
||||
if (isolate == null) return;
|
||||
if (_receivePort._isClosed) return;
|
||||
// We force serialization/deserialization as a simple way to ensure
|
||||
// isolate communication restrictions are respected between isolates that
|
||||
// live in the same worker. [_NativeJsSendPort] delivers both messages
|
||||
// from the same worker and messages from other workers. In particular,
|
||||
// messages sent from a worker via a [_WorkerSendPort] are received at
|
||||
// [_processWorkerMessage] and forwarded to a native port. In such cases,
|
||||
// here we'll see [_globalState.currentContext == null].
|
||||
final shouldSerialize = _globalState.currentContext != null
|
||||
&& _globalState.currentContext.id != _isolateId;
|
||||
var msg = message;
|
||||
if (shouldSerialize) {
|
||||
msg = _serializeMessage(msg);
|
||||
}
|
||||
// Clone the message to enforce the restrictions we have on isolate
|
||||
// messages.
|
||||
var msg = _clone(message);
|
||||
if (isolate.controlPort == _receivePort) {
|
||||
isolate.handleControlMessage(msg);
|
||||
return;
|
||||
}
|
||||
_globalState.topEventLoop.enqueue(isolate, () {
|
||||
if (!_receivePort._isClosed) {
|
||||
if (shouldSerialize) {
|
||||
msg = _deserializeMessage(msg);
|
||||
}
|
||||
_receivePort._add(msg);
|
||||
}
|
||||
}, 'receive $message');
|
||||
|
@ -1317,420 +1324,6 @@ class ReceivePortImpl extends Stream implements ReceivePort {
|
|||
SendPort get sendPort => _rawPort.sendPort;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************
|
||||
Inserted from lib/isolate/dart2js/messages.dart
|
||||
********************************************************/
|
||||
|
||||
// Defines message visitors, serialization, and deserialization.
|
||||
|
||||
/** Serialize [message] (or simulate serialization). */
|
||||
_serializeMessage(message) {
|
||||
if (_globalState.needSerialization) {
|
||||
return new _JsSerializer().traverse(message);
|
||||
} else {
|
||||
return new _JsCopier().traverse(message);
|
||||
}
|
||||
}
|
||||
|
||||
/** Deserialize [message] (or simulate deserialization). */
|
||||
_deserializeMessage(message) {
|
||||
if (_globalState.needSerialization) {
|
||||
return new _JsDeserializer().deserialize(message);
|
||||
} else {
|
||||
// Nothing more to do.
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
class _JsSerializer extends _Serializer {
|
||||
|
||||
_JsSerializer() : super() { _visited = new _JsVisitedMap(); }
|
||||
|
||||
visitSendPort(SendPort x) {
|
||||
if (x is _NativeJsSendPort) return visitNativeJsSendPort(x);
|
||||
if (x is _WorkerSendPort) return visitWorkerSendPort(x);
|
||||
throw "Illegal underlying port $x";
|
||||
}
|
||||
|
||||
visitCapability(Capability x) {
|
||||
if (x is CapabilityImpl) {
|
||||
return ['capability', x._id];
|
||||
}
|
||||
throw "Capability not serializable: $x";
|
||||
}
|
||||
|
||||
visitNativeJsSendPort(_NativeJsSendPort port) {
|
||||
return ['sendport', _globalState.currentManagerId,
|
||||
port._isolateId, port._receivePort._id];
|
||||
}
|
||||
|
||||
visitWorkerSendPort(_WorkerSendPort port) {
|
||||
return ['sendport', port._workerId, port._isolateId, port._receivePortId];
|
||||
}
|
||||
|
||||
visitFunction(Function topLevelFunction) {
|
||||
final name = IsolateNatives._getJSFunctionName(topLevelFunction);
|
||||
if (name == null) {
|
||||
throw new UnsupportedError(
|
||||
"only top-level functions can be sent.");
|
||||
}
|
||||
return ['function', name];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class _JsCopier extends _Copier {
|
||||
|
||||
_JsCopier() : super() { _visited = new _JsVisitedMap(); }
|
||||
|
||||
visitSendPort(SendPort x) {
|
||||
if (x is _NativeJsSendPort) return visitNativeJsSendPort(x);
|
||||
if (x is _WorkerSendPort) return visitWorkerSendPort(x);
|
||||
throw "Illegal underlying port $x";
|
||||
}
|
||||
|
||||
visitCapability(Capability x) {
|
||||
if (x is CapabilityImpl) {
|
||||
return new CapabilityImpl._internal(x._id);
|
||||
}
|
||||
throw "Capability not serializable: $x";
|
||||
}
|
||||
|
||||
SendPort visitNativeJsSendPort(_NativeJsSendPort port) {
|
||||
return new _NativeJsSendPort(port._receivePort, port._isolateId);
|
||||
}
|
||||
|
||||
SendPort visitWorkerSendPort(_WorkerSendPort port) {
|
||||
return new _WorkerSendPort(
|
||||
port._workerId, port._isolateId, port._receivePortId);
|
||||
}
|
||||
|
||||
Function visitFunction(Function topLevelFunction) {
|
||||
final name = IsolateNatives._getJSFunctionName(topLevelFunction);
|
||||
if (name == null) {
|
||||
throw new UnsupportedError(
|
||||
"only top-level functions can be sent.");
|
||||
}
|
||||
// Is this getting it from the correct isolate? Is it just topLevelFunction?
|
||||
return IsolateNatives._getJSFunctionFromName(name);
|
||||
}
|
||||
}
|
||||
|
||||
class _JsDeserializer extends _Deserializer {
|
||||
|
||||
SendPort deserializeSendPort(List list) {
|
||||
int managerId = list[1];
|
||||
int isolateId = list[2];
|
||||
int receivePortId = list[3];
|
||||
// If two isolates are in the same manager, we use NativeJsSendPorts to
|
||||
// deliver messages directly without using postMessage.
|
||||
if (managerId == _globalState.currentManagerId) {
|
||||
var isolate = _globalState.isolates[isolateId];
|
||||
if (isolate == null) return null; // Isolate has been closed.
|
||||
var receivePort = isolate.lookup(receivePortId);
|
||||
if (receivePort == null) return null; // Port has been closed.
|
||||
return new _NativeJsSendPort(receivePort, isolateId);
|
||||
} else {
|
||||
return new _WorkerSendPort(managerId, isolateId, receivePortId);
|
||||
}
|
||||
}
|
||||
|
||||
Capability deserializeCapability(List list) {
|
||||
return new CapabilityImpl._internal(list[1]);
|
||||
}
|
||||
|
||||
Function deserializeFunction(List list) {
|
||||
return IsolateNatives._getJSFunctionFromName(list[1]);
|
||||
}
|
||||
}
|
||||
|
||||
class _JsVisitedMap implements _MessageTraverserVisitedMap {
|
||||
List tagged;
|
||||
|
||||
/** Retrieves any information stored in the native object [object]. */
|
||||
operator[](var object) {
|
||||
return _getAttachedInfo(object);
|
||||
}
|
||||
|
||||
/** Injects some information into the native [object]. */
|
||||
void operator[]=(var object, var info) {
|
||||
tagged.add(object);
|
||||
_setAttachedInfo(object, info);
|
||||
}
|
||||
|
||||
/** Get ready to rumble. */
|
||||
void reset() {
|
||||
assert(tagged == null);
|
||||
tagged = new List();
|
||||
}
|
||||
|
||||
/** Remove all information injected in the native objects. */
|
||||
void cleanup() {
|
||||
for (int i = 0, length = tagged.length; i < length; i++) {
|
||||
_clearAttachedInfo(tagged[i]);
|
||||
}
|
||||
tagged = null;
|
||||
}
|
||||
|
||||
void _clearAttachedInfo(var o) {
|
||||
JS("void", "#['__MessageTraverser__attached_info__'] = #", o, null);
|
||||
}
|
||||
|
||||
void _setAttachedInfo(var o, var info) {
|
||||
JS("void", "#['__MessageTraverser__attached_info__'] = #", o, info);
|
||||
}
|
||||
|
||||
_getAttachedInfo(var o) {
|
||||
return JS("", "#['__MessageTraverser__attached_info__']", o);
|
||||
}
|
||||
}
|
||||
|
||||
// only visible for testing purposes
|
||||
// TODO(sigmund): remove once we can disable privacy for testing (bug #1882)
|
||||
class TestingOnly {
|
||||
static copy(x) {
|
||||
return new _JsCopier().traverse(x);
|
||||
}
|
||||
|
||||
// only visible for testing purposes
|
||||
static serialize(x) {
|
||||
_Serializer serializer = new _JsSerializer();
|
||||
_Deserializer deserializer = new _JsDeserializer();
|
||||
return deserializer.deserialize(serializer.traverse(x));
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************
|
||||
Inserted from lib/isolate/serialization.dart
|
||||
********************************************************/
|
||||
|
||||
class _MessageTraverserVisitedMap {
|
||||
|
||||
operator[](var object) => null;
|
||||
void operator[]=(var object, var info) { }
|
||||
|
||||
void reset() { }
|
||||
void cleanup() { }
|
||||
|
||||
}
|
||||
|
||||
/** Abstract visitor for dart objects that can be sent as isolate messages. */
|
||||
abstract class _MessageTraverser {
|
||||
|
||||
_MessageTraverserVisitedMap _visited;
|
||||
_MessageTraverser() : _visited = new _MessageTraverserVisitedMap();
|
||||
|
||||
/** Visitor's entry point. */
|
||||
traverse(var x) {
|
||||
if (isPrimitive(x)) return visitPrimitive(x);
|
||||
_visited.reset();
|
||||
var result;
|
||||
try {
|
||||
result = _dispatch(x);
|
||||
} finally {
|
||||
_visited.cleanup();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
_dispatch(var x) {
|
||||
// This code likely fails for user classes implementing
|
||||
// SendPort and Capability because it assumes the internal classes.
|
||||
if (isPrimitive(x)) return visitPrimitive(x);
|
||||
if (x is List) return visitList(x);
|
||||
if (x is Map) return visitMap(x);
|
||||
if (x is SendPort) return visitSendPort(x);
|
||||
if (x is Capability) return visitCapability(x);
|
||||
if (x is Function) return visitFunction(x);
|
||||
|
||||
// Overridable fallback.
|
||||
return visitObject(x);
|
||||
}
|
||||
|
||||
visitPrimitive(x);
|
||||
visitList(List x);
|
||||
visitMap(Map x);
|
||||
visitSendPort(SendPort x);
|
||||
visitCapability(Capability x);
|
||||
visitFunction(Function f);
|
||||
|
||||
visitObject(Object x) {
|
||||
// TODO(floitsch): make this a real exception. (which one)?
|
||||
throw "Message serialization: Illegal value $x passed";
|
||||
}
|
||||
|
||||
static bool isPrimitive(x) {
|
||||
return (x == null) || (x is String) || (x is num) || (x is bool);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** A visitor that recursively copies a message. */
|
||||
class _Copier extends _MessageTraverser {
|
||||
|
||||
visitPrimitive(x) => x;
|
||||
|
||||
List visitList(List list) {
|
||||
List copy = _visited[list];
|
||||
if (copy != null) return copy;
|
||||
|
||||
int len = list.length;
|
||||
|
||||
// TODO(floitsch): we loose the generic type of the List.
|
||||
copy = new List(len);
|
||||
_visited[list] = copy;
|
||||
for (int i = 0; i < len; i++) {
|
||||
copy[i] = _dispatch(list[i]);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
Map visitMap(Map map) {
|
||||
Map copy = _visited[map];
|
||||
if (copy != null) return copy;
|
||||
|
||||
// TODO(floitsch): we loose the generic type of the map.
|
||||
copy = new Map();
|
||||
_visited[map] = copy;
|
||||
map.forEach((key, val) {
|
||||
copy[_dispatch(key)] = _dispatch(val);
|
||||
});
|
||||
return copy;
|
||||
}
|
||||
|
||||
visitFunction(Function f) => throw new UnimplementedError();
|
||||
|
||||
visitSendPort(SendPort x) => throw new UnimplementedError();
|
||||
|
||||
visitCapability(Capability x) => throw new UnimplementedError();
|
||||
}
|
||||
|
||||
/** Visitor that serializes a message as a JSON array. */
|
||||
class _Serializer extends _MessageTraverser {
|
||||
int _nextFreeRefId = 0;
|
||||
|
||||
visitPrimitive(x) => x;
|
||||
|
||||
visitList(List list) {
|
||||
int copyId = _visited[list];
|
||||
if (copyId != null) return ['ref', copyId];
|
||||
|
||||
int id = _nextFreeRefId++;
|
||||
_visited[list] = id;
|
||||
var jsArray = _serializeList(list);
|
||||
// TODO(floitsch): we are losing the generic type.
|
||||
return ['list', id, jsArray];
|
||||
}
|
||||
|
||||
visitMap(Map map) {
|
||||
int copyId = _visited[map];
|
||||
if (copyId != null) return ['ref', copyId];
|
||||
|
||||
int id = _nextFreeRefId++;
|
||||
_visited[map] = id;
|
||||
var keys = _serializeList(map.keys.toList());
|
||||
var values = _serializeList(map.values.toList());
|
||||
// TODO(floitsch): we are losing the generic type.
|
||||
return ['map', id, keys, values];
|
||||
}
|
||||
|
||||
_serializeList(List list) {
|
||||
int len = list.length;
|
||||
// Use a growable list because we do not add extra properties on
|
||||
// them.
|
||||
var result = new List()..length = len;
|
||||
for (int i = 0; i < len; i++) {
|
||||
result[i] = _dispatch(list[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
visitSendPort(SendPort x) => throw new UnimplementedError();
|
||||
|
||||
visitCapability(Capability x) => throw new UnimplementedError();
|
||||
|
||||
visitFunction(Function f) => throw new UnimplementedError();
|
||||
}
|
||||
|
||||
/** Deserializes arrays created with [_Serializer]. */
|
||||
abstract class _Deserializer {
|
||||
Map<int, dynamic> _deserialized;
|
||||
|
||||
_Deserializer();
|
||||
|
||||
static bool isPrimitive(x) {
|
||||
return (x == null) || (x is String) || (x is num) || (x is bool);
|
||||
}
|
||||
|
||||
deserialize(x) {
|
||||
if (isPrimitive(x)) return x;
|
||||
// TODO(floitsch): this should be new HashMap<int, dynamic>()
|
||||
_deserialized = new HashMap();
|
||||
return _deserializeHelper(x);
|
||||
}
|
||||
|
||||
_deserializeHelper(x) {
|
||||
if (isPrimitive(x)) return x;
|
||||
assert(x is List);
|
||||
switch (x[0]) {
|
||||
case 'ref': return _deserializeRef(x);
|
||||
case 'list': return _deserializeList(x);
|
||||
case 'map': return _deserializeMap(x);
|
||||
case 'sendport': return deserializeSendPort(x);
|
||||
case 'capability': return deserializeCapability(x);
|
||||
case 'function' : return deserializeFunction(x);
|
||||
default: return deserializeObject(x);
|
||||
}
|
||||
}
|
||||
|
||||
_deserializeRef(List x) {
|
||||
int id = x[1];
|
||||
var result = _deserialized[id];
|
||||
assert(result != null);
|
||||
return result;
|
||||
}
|
||||
|
||||
List _deserializeList(List x) {
|
||||
int id = x[1];
|
||||
// We rely on the fact that Dart-lists are directly mapped to Js-arrays.
|
||||
List dartList = x[2];
|
||||
_deserialized[id] = dartList;
|
||||
int len = dartList.length;
|
||||
for (int i = 0; i < len; i++) {
|
||||
dartList[i] = _deserializeHelper(dartList[i]);
|
||||
}
|
||||
return dartList;
|
||||
}
|
||||
|
||||
Map _deserializeMap(List x) {
|
||||
Map result = new Map();
|
||||
int id = x[1];
|
||||
_deserialized[id] = result;
|
||||
List keys = x[2];
|
||||
List values = x[3];
|
||||
int len = keys.length;
|
||||
assert(len == values.length);
|
||||
for (int i = 0; i < len; i++) {
|
||||
var key = _deserializeHelper(keys[i]);
|
||||
var value = _deserializeHelper(values[i]);
|
||||
result[key] = value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
deserializeFunction(List x);
|
||||
|
||||
deserializeSendPort(List x);
|
||||
|
||||
deserializeCapability(List x);
|
||||
|
||||
deserializeObject(List x) {
|
||||
// TODO(floitsch): Use real exception (which one?).
|
||||
throw "Unexpected serialized object";
|
||||
}
|
||||
}
|
||||
|
||||
class TimerImpl implements Timer {
|
||||
final bool _once;
|
||||
bool _inEventLoop = false;
|
||||
|
|
361
sdk/lib/_internal/compiler/js_lib/isolate_serialization.dart
Normal file
361
sdk/lib/_internal/compiler/js_lib/isolate_serialization.dart
Normal file
|
@ -0,0 +1,361 @@
|
|||
// Copyright (c) 2014, 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 _isolate_helper;
|
||||
|
||||
/// Serialize [message].
|
||||
_serializeMessage(message) {
|
||||
return new _Serializer().serialize(message);
|
||||
}
|
||||
|
||||
/// Deserialize [message].
|
||||
_deserializeMessage(message) {
|
||||
return new _Deserializer().deserialize(message);
|
||||
}
|
||||
|
||||
/// Clones the message.
|
||||
///
|
||||
/// Contrary to a `_deserializeMessage(_serializeMessage(x))` the `_clone`
|
||||
/// function will not try to adjust SendPort values and pass them through.
|
||||
_clone(message) {
|
||||
_Serializer serializer = new _Serializer(serializeSendPorts: false);
|
||||
_Deserializer deserializer = new _Deserializer();
|
||||
return deserializer.deserialize(serializer.serialize(message));
|
||||
}
|
||||
|
||||
class _Serializer {
|
||||
final bool _serializeSendPorts;
|
||||
Map<dynamic, int> serializedObjectIds = new Map<dynamic, int>.identity();
|
||||
|
||||
_Serializer({serializeSendPorts: true})
|
||||
: _serializeSendPorts = serializeSendPorts;
|
||||
|
||||
/// Returns a message that can be transmitted through web-worker channels.
|
||||
serialize(x) {
|
||||
if (isPrimitive(x)) return serializePrimitive(x);
|
||||
|
||||
int serializationId = serializedObjectIds[x];
|
||||
if (serializationId != null) return makeRef(serializationId);
|
||||
|
||||
serializationId = serializedObjectIds.length;
|
||||
serializedObjectIds[x] = serializationId;
|
||||
|
||||
if (x is NativeByteBuffer) return serializeByteBuffer(x);
|
||||
if (x is NativeTypedData) return serializeTypedData(x);
|
||||
if (x is JSIndexable) return serializeJSIndexable(x);
|
||||
if (x is InternalMap) return serializeMap(x);
|
||||
|
||||
if (x is JSObject) return serializeJSObject(x);
|
||||
|
||||
// We should not have any interceptors any more.
|
||||
if (x is Interceptor) unsupported(x);
|
||||
|
||||
if (x is RawReceivePort) {
|
||||
unsupported(x, "RawReceivePorts can't be transmitted:");
|
||||
}
|
||||
|
||||
// SendPorts need their workerIds adjusted (either during serialization or
|
||||
// deserialization).
|
||||
if (x is _NativeJsSendPort) return serializeJsSendPort(x);
|
||||
if (x is _WorkerSendPort) return serializeWorkerSendPort(x);
|
||||
|
||||
if (x is Closure) return serializeClosure(x);
|
||||
|
||||
return serializeDartObject(x);
|
||||
}
|
||||
|
||||
void unsupported(x, [String message]) {
|
||||
if (message == null) message = "Can't transmit:";
|
||||
throw new UnsupportedError("$message $x");
|
||||
}
|
||||
|
||||
makeRef(int serializationId) => ["ref", serializationId];
|
||||
|
||||
bool isPrimitive(x) => x == null || x is String || x is num || x is bool;
|
||||
serializePrimitive(primitive) => primitive;
|
||||
|
||||
serializeByteBuffer(NativeByteBuffer buffer) {
|
||||
return ["buffer", buffer];
|
||||
}
|
||||
|
||||
serializeTypedData(NativeTypedData data) {
|
||||
return ["typed", data];
|
||||
}
|
||||
|
||||
serializeJSIndexable(JSIndexable indexable) {
|
||||
// Strings are JSIndexable but should have been treated earlier.
|
||||
assert(indexable is! String);
|
||||
List serialized = serializeArray(indexable);
|
||||
if (indexable is JSFixedArray) return ["fixed", serialized];
|
||||
if (indexable is JSExtendableArray) return ["extendable", serialized];
|
||||
// MutableArray check must be last, since JSFixedArray and JSExtendableArray
|
||||
// extend JSMutableArray.
|
||||
if (indexable is JSMutableArray) return ["mutable", serialized];
|
||||
// The only JSArrays left are the const Lists (as in `const [1, 2]`).
|
||||
if (indexable is JSArray) return ["const", serialized];
|
||||
unsupported(indexable, "Can't serialize indexable: ");
|
||||
return null;
|
||||
}
|
||||
|
||||
serializeArray(JSArray x) {
|
||||
List serialized = [];
|
||||
serialized.length = x.length;
|
||||
for (int i = 0; i < x.length; i++) {
|
||||
serialized[i] = serialize(x[i]);
|
||||
}
|
||||
return serialized;
|
||||
}
|
||||
|
||||
serializeArrayInPlace(JSArray x) {
|
||||
for (int i = 0; i < x.length; i++) {
|
||||
x[i] = serialize(x[i]);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
serializeMap(Map x) {
|
||||
Function serializeTearOff = serialize;
|
||||
return ['map',
|
||||
x.keys.map(serializeTearOff).toList(),
|
||||
x.values.map(serializeTearOff).toList()];
|
||||
}
|
||||
|
||||
serializeJSObject(JSObject x) {
|
||||
// Don't serialize objects if their `constructor` property isn't `Object`
|
||||
// or undefined/null.
|
||||
// A different constructor is taken as a sign that the object has complex
|
||||
// internal state, or that it is a function, and won't be serialized.
|
||||
if (JS('bool', '!!(#.constructor)', x) &&
|
||||
JS('bool', 'x.constructor !== Object')) {
|
||||
unsupported(x, "Only plain JS Objects are supported:");
|
||||
}
|
||||
List keys = JS('JSArray', 'Object.keys(#)', x);
|
||||
List values = [];
|
||||
values.length = keys.length;
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
values[i] = serialize(JS('', '#[#]', x, keys[i]));
|
||||
}
|
||||
return ['js-object', keys, values];
|
||||
}
|
||||
|
||||
serializeWorkerSendPort(_WorkerSendPort x) {
|
||||
if (_serializeSendPorts) {
|
||||
return ['sendport', x._workerId, x._isolateId, x._receivePortId];
|
||||
}
|
||||
return ['raw sendport', x];
|
||||
}
|
||||
|
||||
serializeJsSendPort(_NativeJsSendPort x) {
|
||||
if (_serializeSendPorts) {
|
||||
int workerId = _globalState.currentManagerId;
|
||||
return ['sendport', workerId, x._isolateId, x._receivePort._id];
|
||||
}
|
||||
return ['raw sendport', x];
|
||||
}
|
||||
|
||||
serializeCapability(CapabilityImpl x) => ['capability', x._id];
|
||||
|
||||
serializeClosure(Closure x) {
|
||||
final name = IsolateNatives._getJSFunctionName(x);
|
||||
if (name == null) {
|
||||
unsupported(x, "Closures can't be transmitted:");
|
||||
}
|
||||
return ['function', name];
|
||||
}
|
||||
|
||||
serializeDartObject(x) {
|
||||
var classExtractor = JS_EMBEDDED_GLOBAL('', CLASS_ID_EXTRACTOR);
|
||||
var fieldsExtractor = JS_EMBEDDED_GLOBAL('', CLASS_FIELDS_EXTRACTOR);
|
||||
String classId = JS('String', '#(#)', classExtractor, x);
|
||||
List fields = JS('JSArray', '#(#)', fieldsExtractor, x);
|
||||
return ['dart', classId, serializeArrayInPlace(fields)];
|
||||
}
|
||||
}
|
||||
|
||||
class _Deserializer {
|
||||
/// When `true`, encodes sendports specially so that they can be adjusted on
|
||||
/// the receiving end.
|
||||
///
|
||||
/// When `false`, sendports are cloned like any other object.
|
||||
final bool _adjustSendPorts;
|
||||
|
||||
List<dynamic> deserializedObjects = new List<dynamic>();
|
||||
|
||||
_Deserializer({adjustSendPorts: true}) : _adjustSendPorts = adjustSendPorts;
|
||||
|
||||
/// Returns a message that can be transmitted through web-worker channels.
|
||||
deserialize(x) {
|
||||
if (isPrimitive(x)) return deserializePrimitive(x);
|
||||
|
||||
if (x is! JSArray) throw new ArgumentError("Bad serialized message: $x");
|
||||
|
||||
switch (x.first) {
|
||||
case "ref": return deserializeRef(x);
|
||||
case "buffer": return deserializeByteBuffer(x);
|
||||
case "typed": return deserializeTypedData(x);
|
||||
case "fixed": return deserializeFixed(x);
|
||||
case "extendable": return deserializeExtendable(x);
|
||||
case "mutable": return deserializeMutable(x);
|
||||
case "const": return deserializeConst(x);
|
||||
case "map": return deserializeMap(x);
|
||||
case "sendport": return deserializeSendPort(x);
|
||||
case "raw sendport": return deserializeRawSendPort(x);
|
||||
case "js-object": return deserializeJSObject(x);
|
||||
case "function": return deserializeClosure(x);
|
||||
case "dart": return deserializeDartObject(x);
|
||||
default: throw "couldn't deserialize: $x";
|
||||
}
|
||||
}
|
||||
|
||||
bool isPrimitive(x) => x == null || x is String || x is num || x is bool;
|
||||
deserializePrimitive(x) => x;
|
||||
|
||||
// ['ref', id].
|
||||
deserializeRef(x) {
|
||||
assert(x[0] == 'ref');
|
||||
int serializationId = x[1];
|
||||
return deserializedObjects[serializationId];
|
||||
}
|
||||
|
||||
// ['buffer', <byte buffer>].
|
||||
NativeByteBuffer deserializeByteBuffer(x) {
|
||||
assert(x[0] == 'buffer');
|
||||
NativeByteBuffer result = x[1];
|
||||
deserializedObjects.add(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// ['typed', <typed array>].
|
||||
NativeTypedData deserializeTypedData(x) {
|
||||
assert(x[0] == 'typed');
|
||||
NativeTypedData result = x[1];
|
||||
deserializedObjects.add(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Updates the given array in place with its deserialized content.
|
||||
List deserializeArrayInPlace(JSArray x) {
|
||||
for (int i = 0; i < x.length; i++) {
|
||||
x[i] = deserialize(x[i]);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
// ['fixed', <array>].
|
||||
List deserializeFixed(x) {
|
||||
assert(x[0] == 'fixed');
|
||||
List result = x[1];
|
||||
deserializedObjects.add(result);
|
||||
return new JSArray.markFixed(deserializeArrayInPlace(result));
|
||||
}
|
||||
|
||||
// ['extendable', <array>].
|
||||
List deserializeExtendable(x) {
|
||||
assert(x[0] == 'extendable');
|
||||
List result = x[1];
|
||||
deserializedObjects.add(result);
|
||||
return new JSArray.markGrowable(deserializeArrayInPlace(result));
|
||||
}
|
||||
|
||||
// ['mutable', <array>].
|
||||
List deserializeMutable(x) {
|
||||
assert(x[0] == 'mutable');
|
||||
List result = x[1];
|
||||
deserializedObjects.add(result);
|
||||
return deserializeArrayInPlace(result);
|
||||
}
|
||||
|
||||
// ['const', <array>].
|
||||
List deserializeConst(x) {
|
||||
assert(x[0] == 'const');
|
||||
List result = x[1];
|
||||
deserializedObjects.add(result);
|
||||
// TODO(floitsch): need to mark list as non-changeable.
|
||||
return new JSArray.markFixed(deserializeArrayInPlace(result));
|
||||
}
|
||||
|
||||
// ['map', <key-list>, <value-list>].
|
||||
Map deserializeMap(x) {
|
||||
assert(x[0] == 'map');
|
||||
List keys = x[1];
|
||||
List values = x[2];
|
||||
Map result = {};
|
||||
deserializedObjects.add(result);
|
||||
// We need to keep the order of how objects were serialized.
|
||||
// First deserialize all keys, and then only deserialize the values.
|
||||
keys = keys.map(deserialize).toList();
|
||||
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
result[keys[i]] = deserialize(values[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// ['sendport', <managerId>, <isolateId>, <receivePortId>].
|
||||
SendPort deserializeSendPort(x) {
|
||||
assert(x[0] == 'sendport');
|
||||
int managerId = x[1];
|
||||
int isolateId = x[2];
|
||||
int receivePortId = x[3];
|
||||
SendPort result;
|
||||
// If two isolates are in the same manager, we use NativeJsSendPorts to
|
||||
// deliver messages directly without using postMessage.
|
||||
if (managerId == _globalState.currentManagerId) {
|
||||
var isolate = _globalState.isolates[isolateId];
|
||||
if (isolate == null) return null; // Isolate has been closed.
|
||||
var receivePort = isolate.lookup(receivePortId);
|
||||
if (receivePort == null) return null; // Port has been closed.
|
||||
result = new _NativeJsSendPort(receivePort, isolateId);
|
||||
} else {
|
||||
result = new _WorkerSendPort(managerId, isolateId, receivePortId);
|
||||
}
|
||||
deserializedObjects.add(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// ['raw sendport', <sendport>].
|
||||
SendPort deserializeRawSendPort(x) {
|
||||
assert(x[0] == 'raw sendport');
|
||||
SendPort result = x[1];
|
||||
deserializedObjects.add(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// ['js-object', <key-list>, <value-list>].
|
||||
deserializeJSObject(x) {
|
||||
assert(x[0] == 'js-object');
|
||||
List keys = x[1];
|
||||
List values = x[2];
|
||||
var o = JS('', '{}');
|
||||
deserializedObjects.add(o);
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
JS('', '#[#]=#', o, keys[i], deserialize(values[i]));
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
// ['function', <name>].
|
||||
Function deserializeClosure(x) {
|
||||
assert(x[0] == 'function');
|
||||
String name = x[1];
|
||||
Function result = IsolateNatives._getJSFunctionFromName(name);
|
||||
deserializedObjects.add(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// ['dart', <class-id>, <field-list>].
|
||||
deserializeDartObject(x) {
|
||||
assert(x[0] == 'dart');
|
||||
String classId = x[1];
|
||||
List fields = x[2];
|
||||
var instanceFromClassId = JS_EMBEDDED_GLOBAL('', INSTANCE_FROM_CLASS_ID);
|
||||
var initializeObject = JS_EMBEDDED_GLOBAL('', INITIALIZE_EMPTY_INSTANCE);
|
||||
|
||||
var emptyInstance = JS('', '#(#)', instanceFromClassId, classId);
|
||||
deserializedObjects.add(emptyInstance);
|
||||
deserializeArrayInPlace(fields);
|
||||
return JS('', '#(#, #, #)',
|
||||
initializeObject, classId, emptyInstance, fields);
|
||||
}
|
||||
}
|
|
@ -77,6 +77,12 @@ class _Patch {
|
|||
|
||||
const _Patch patch = const _Patch();
|
||||
|
||||
|
||||
/// Marks the internal map in dart2js, so that internal libraries can is-check
|
||||
// them.
|
||||
abstract class InternalMap {
|
||||
}
|
||||
|
||||
/// No-op method that is called to inform the compiler that preambles might
|
||||
/// be needed when executing the resulting JS file in a command-line
|
||||
/// JS engine.
|
||||
|
|
|
@ -443,8 +443,11 @@ applyHooksTransformer(transformer, hooks) {
|
|||
const _baseHooks = const JS_CONST(r'''
|
||||
function() {
|
||||
function typeNameInChrome(o) {
|
||||
var name = o.constructor.name;
|
||||
if (name) return name;
|
||||
var constructor = o.constructor;
|
||||
if (constructor) {
|
||||
var name = constructor.name;
|
||||
if (name) return name;
|
||||
}
|
||||
var s = Object.prototype.toString.call(o);
|
||||
return s.substring(8, s.length - 1);
|
||||
}
|
||||
|
|
|
@ -31,4 +31,8 @@ const CURRENT_SCRIPT = 'currentScript';
|
|||
const DEFERRED_LIBRARY_URIS = 'deferredLibraryUris';
|
||||
const DEFERRED_LIBRARY_HASHES = 'deferredLibraryHashes';
|
||||
const INITIALIZE_LOADED_HUNK = 'initializeLoadedHunk';
|
||||
const IS_HUNK_LOADED = 'isHunkLoaded';
|
||||
const IS_HUNK_LOADED = 'isHunkLoaded';
|
||||
const CLASS_ID_EXTRACTOR = 'classIdExtractor';
|
||||
const CLASS_FIELDS_EXTRACTOR = 'classFieldsExtractor';
|
||||
const INSTANCE_FROM_CLASS_ID = "instanceFromClassId";
|
||||
const INITIALIZE_EMPTY_INSTANCE = "initializeEmptyInstance";
|
||||
|
|
|
@ -153,13 +153,12 @@ void testUnsendable(name, func) {
|
|||
|
||||
asyncStart();
|
||||
var noReply = new RawReceivePort((_) { throw "Unexpected message: $_"; });
|
||||
// Currently succeedes incorrectly in dart2js.
|
||||
Expect.throws(() { /// 01: ok
|
||||
noReply.sendPort.send(func); /// 01: continued
|
||||
}, null, "send direct"); /// 01: continued
|
||||
Expect.throws(() { /// 01: continued
|
||||
noReply.sendPort.send([func]); /// 01: continued
|
||||
}, null, "send wrapped"); /// 01: continued
|
||||
Expect.throws(() {
|
||||
noReply.sendPort.send(func);
|
||||
}, null, "send direct");
|
||||
Expect.throws(() {
|
||||
noReply.sendPort.send([func]);
|
||||
}, null, "send wrapped");
|
||||
scheduleMicrotask(() {
|
||||
noReply.close();
|
||||
asyncEnd();
|
||||
|
|
|
@ -10,7 +10,6 @@ isolate_stress_test: Fail # Issue 12588: This should be able to pass when we hav
|
|||
mandel_isolate_test: Skip # Uses 600 MB Ram on our 1 GB test device.
|
||||
|
||||
[ $compiler == none || $compiler == dart2dart ]
|
||||
serialization_test: SkipByDesign # Tests dart2js-specific serialization code
|
||||
compile_time_error_test/01: Skip # Issue 12587
|
||||
start_paused_test: Fail # Not implemented yet
|
||||
ondone_test: Fail # Not implemented yet
|
||||
|
@ -25,6 +24,13 @@ handle_error2_test: Fail # Not implemented yet
|
|||
handle_error3_test: Fail # Not implemented yet
|
||||
function_send_test: Fail # Not implemented yet
|
||||
|
||||
message3_test/constList_identical: RuntimeError # Issue 21816
|
||||
message3_test/constMap: RuntimeError # Issue 21816
|
||||
message3_test/fun: RuntimeError # Issue 21585
|
||||
message3_test/constInstance: RuntimeError # Issue 21816
|
||||
message3_test/byteBuffer: Crash # Issue 21818
|
||||
message3_test/int32x4: Crash # Issue 21818
|
||||
|
||||
[ $compiler == none && $runtime == ContentShellOnAndroid ]
|
||||
*: Skip # Isolate tests are timing out flakily on Android content_shell. Issue 19795
|
||||
|
||||
|
@ -37,11 +43,6 @@ browser/*: SkipByDesign # Browser specific tests
|
|||
[ $compiler == dart2js && $runtime == jsshell ]
|
||||
pause_test: Fail # non-zero timer not supported.
|
||||
|
||||
[ $compiler == dart2js ]
|
||||
serialization_test: RuntimeError # Issue 1882, tries to access class TestingOnly declared in isolate_patch.dart
|
||||
|
||||
function_send_test/01: RuntimeError # dart2js allows sending closures to the same isolate.
|
||||
|
||||
[ $compiler == dart2js && $runtime == safari ]
|
||||
cross_isolate_message_test: Skip # Issue 12627
|
||||
message_test: Skip # Issue 12627
|
||||
|
@ -49,13 +50,14 @@ message_test: Skip # Issue 12627
|
|||
[ $compiler == dart2js ]
|
||||
spawn_uri_vm_test: SkipByDesign # Test uses a ".dart" URI.
|
||||
spawn_uri_nested_vm_test: SkipByDesign # Test uses a ".dart" URI.
|
||||
message3_test/constList: RuntimeError # Issue 21817
|
||||
message3_test/constList_identical: RuntimeError # Issue 21817
|
||||
message3_test/constMap: RuntimeError # Issue 21817
|
||||
message3_test/constInstance: RuntimeError # Issue 21817
|
||||
|
||||
[ $compiler == dart2js && $jscl ]
|
||||
spawn_uri_test: SkipByDesign # Loading another file is not supported in JS shell
|
||||
|
||||
[ $compiler == dart2js && $runtime == none ]
|
||||
serialization_test: Pass # Issue 12628
|
||||
|
||||
[ $compiler == dart2js && $runtime == chromeOnAndroid ]
|
||||
isolate_stress_test: Pass, Slow # TODO(kasperl): Please triage.
|
||||
|
||||
|
@ -93,6 +95,8 @@ spawn_uri_nested_vm_test: Skip # Issue 14479: This test is timing out.
|
|||
compile_time_error_test/none: Fail, OK # Issue 13921 Dom isolates don't support spawnFunction
|
||||
isolate_import_test/none: Fail, OK # Issue 13921 Dom isolates don't support spawnFunction
|
||||
isolate_stress_test: Skip # Issue 13921 Dom isolates don't support spawnFunction
|
||||
message3_test: Fail, OK # Issue 13921 Dom isolates don't support spawnFunction
|
||||
object_leak_test: Fail, OK # Issue 13921 Dom isolates don't support spawnFunction
|
||||
simple_message_test/none: Fail, OK # Issue 13921 Dom isolates don't support spawnFunction
|
||||
spawn_uri_missing_from_isolate_test: RuntimeError # http://dartbug.com/17649
|
||||
spawn_uri_missing_test: Skip # Times out.
|
||||
|
@ -100,7 +104,6 @@ spawn_uri_missing_test: Skip # Times out.
|
|||
[ $compiler == dartanalyzer || $compiler == dart2analyzer ]
|
||||
browser/typed_data_message_test: StaticWarning
|
||||
mint_maker_test: StaticWarning
|
||||
serialization_test: StaticWarning
|
||||
|
||||
[ $compiler != none || $runtime != vm ]
|
||||
package_root_test: SkipByDesign # Uses dart:io.
|
||||
|
|
438
tests/isolate/message3_test.dart
Normal file
438
tests/isolate/message3_test.dart
Normal file
|
@ -0,0 +1,438 @@
|
|||
// 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.
|
||||
|
||||
// Dart test program for testing serialization of messages.
|
||||
// VMOptions=--enable_type_checks --enable_asserts
|
||||
|
||||
library MessageTest;
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:isolate';
|
||||
import 'package:async_helper/async_helper.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
import 'dart:typed_data';
|
||||
|
||||
void echoMain(msg) {
|
||||
SendPort replyTo = msg[0];
|
||||
SendPort pong = msg[1];
|
||||
ReceivePort port = new ReceivePort();
|
||||
replyTo.send(port.sendPort);
|
||||
port.listen((msg) {
|
||||
if (msg == "halt") {
|
||||
port.close();
|
||||
} else {
|
||||
pong.send(msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
class A {
|
||||
var field = 499;
|
||||
|
||||
A();
|
||||
A.named(this.field);
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
final field2;
|
||||
B() : field2 = 99;
|
||||
B.named(this.field2, x) : super.named(x);
|
||||
}
|
||||
|
||||
class C extends B {
|
||||
var field = 33;
|
||||
|
||||
get superField => super.field;
|
||||
get superField2 => super.field2;
|
||||
}
|
||||
|
||||
class M {
|
||||
get field2 => 11;
|
||||
}
|
||||
|
||||
class D extends C with M {
|
||||
var gee = 123;
|
||||
}
|
||||
|
||||
class E {
|
||||
Function fun;
|
||||
E(this.fun);
|
||||
|
||||
static fooFun() => 499;
|
||||
instanceFun() => 1234;
|
||||
}
|
||||
barFun() => 42;
|
||||
|
||||
class F {
|
||||
final field = "field";
|
||||
const F();
|
||||
}
|
||||
|
||||
class G {
|
||||
final field;
|
||||
const G(this.field);
|
||||
}
|
||||
|
||||
class Value {
|
||||
final val;
|
||||
Value(this.val);
|
||||
|
||||
operator==(other) {
|
||||
if (other is! Value) return false;
|
||||
return other.val == val;
|
||||
}
|
||||
|
||||
get hashCode => val;
|
||||
}
|
||||
|
||||
void runTests(SendPort ping, Queue checks) {
|
||||
ping.send("abc");
|
||||
checks.add((x) => Expect.equals("abc", x));
|
||||
|
||||
ping.send([1, 2]);
|
||||
checks.add((x) {
|
||||
Expect.isTrue(x is List);
|
||||
Expect.listEquals([1, 2], x);
|
||||
// Make sure the list is mutable.
|
||||
x[0] = 0;
|
||||
Expect.equals(0, x[0]);
|
||||
// List must be extendable.
|
||||
x.add(3);
|
||||
Expect.equals(3, x[2]);
|
||||
});
|
||||
|
||||
List fixed = new List(2);
|
||||
fixed[0] = 0;
|
||||
fixed[1] = 1;
|
||||
ping.send(fixed);
|
||||
checks.add((x) {
|
||||
Expect.isTrue(x is List);
|
||||
Expect.listEquals([0, 1], x);
|
||||
// List must be mutable.
|
||||
x[0] = 3;
|
||||
Expect.equals(3, x[0]);
|
||||
// List must be fixed length.
|
||||
Expect.throws(() { x.add(5); });
|
||||
});
|
||||
|
||||
List cyclic = [];
|
||||
cyclic.add(cyclic);
|
||||
ping.send(cyclic);
|
||||
checks.add((x) {
|
||||
Expect.isTrue(x is List);
|
||||
Expect.equals(1, x.length);
|
||||
Expect.identical(x, x[0]);
|
||||
// List must be mutable.
|
||||
x[0] = 55;
|
||||
Expect.equals(55, x[0]);
|
||||
// List must be extendable.
|
||||
x.add(42);
|
||||
Expect.equals(42, x[1]);
|
||||
});
|
||||
|
||||
List cyclic2 = new List(1);
|
||||
cyclic2[0] = cyclic2;
|
||||
ping.send(cyclic2);
|
||||
checks.add((x) {
|
||||
Expect.isTrue(x is List);
|
||||
Expect.equals(1, x.length);
|
||||
Expect.identical(x, x[0]);
|
||||
// List must be mutable.
|
||||
x[0] = 55;
|
||||
Expect.equals(55, x[0]);
|
||||
// List must be fixed.
|
||||
Expect.throws(() => x.add(42));
|
||||
});
|
||||
|
||||
List constList = const [1, 2];
|
||||
ping.send(constList);
|
||||
checks.add((x) {
|
||||
Expect.isTrue(x is List);
|
||||
Expect.listEquals([1, 2], x);
|
||||
// Make sure the list is immutable.
|
||||
Expect.throws(() => x[0] = 0); /// constList: ok
|
||||
// List must not be extendable.
|
||||
Expect.throws(() => x.add(3));
|
||||
Expect.identical(x, constList); /// constList_identical: ok
|
||||
});
|
||||
|
||||
Uint8List uint8 = new Uint8List(2);
|
||||
uint8[0] = 0;
|
||||
uint8[1] = 1;
|
||||
ping.send(uint8);
|
||||
checks.add((x) {
|
||||
Expect.isTrue(x is Uint8List);
|
||||
Expect.equals(2, x.length);
|
||||
Expect.equals(0, x[0]);
|
||||
Expect.equals(1, x[1]);
|
||||
});
|
||||
|
||||
Uint16List uint16 = new Uint16List(2);
|
||||
uint16[0] = 0;
|
||||
uint16[1] = 1;
|
||||
ByteBuffer byteBuffer = uint16.buffer;
|
||||
ping.send(byteBuffer); /// byteBuffer: ok
|
||||
checks.add( /// byteBuffer: ok
|
||||
(x) {
|
||||
Expect.isTrue(x is ByteBuffer);
|
||||
Uint16List uint16View = new Uint16List.view(x);
|
||||
Expect.equals(2, uint16View.length);
|
||||
Expect.equals(0, uint16View[0]);
|
||||
Expect.equals(1, uint16View[1]);
|
||||
}
|
||||
) /// byteBuffer: ok
|
||||
;
|
||||
|
||||
Int32x4List list32x4 = new Int32x4List(2);
|
||||
list32x4[0] = new Int32x4(1, 2, 3, 4);
|
||||
list32x4[1] = new Int32x4(5, 6, 7, 8);
|
||||
ping.send(list32x4); /// int32x4: ok
|
||||
checks.add( /// int32x4: ok
|
||||
(x) {
|
||||
Expect.isTrue(x is Int32x4List);
|
||||
Expect.equals(2, x.length);
|
||||
Int32x4 entry1 = x[0];
|
||||
Int32x4 entry2 = x[1];
|
||||
Expect.equals(1, entry1.x);
|
||||
Expect.equals(2, entry1.y);
|
||||
Expect.equals(3, entry1.z);
|
||||
Expect.equals(4, entry1.w);
|
||||
Expect.equals(5, entry2.x);
|
||||
Expect.equals(6, entry2.y);
|
||||
Expect.equals(7, entry2.z);
|
||||
Expect.equals(8, entry2.w);
|
||||
}
|
||||
) /// int32x4: ok
|
||||
;
|
||||
|
||||
ping.send({"foo": 499, "bar": 32});
|
||||
checks.add((x) {
|
||||
Expect.isTrue(x is LinkedHashMap);
|
||||
Expect.listEquals(["foo", "bar"], x.keys.toList());
|
||||
Expect.listEquals([499, 32], x.values.toList());
|
||||
// Must be mutable.
|
||||
x["foo"] = 22;
|
||||
Expect.equals(22, x["foo"]);
|
||||
// Must be extendable.
|
||||
x["gee"] = 499;
|
||||
Expect.equals(499, x["gee"]);
|
||||
});
|
||||
|
||||
ping.send({0: 499, 1: 32});
|
||||
checks.add((x) {
|
||||
Expect.isTrue(x is LinkedHashMap);
|
||||
Expect.listEquals([0, 1], x.keys.toList());
|
||||
Expect.listEquals([499, 32], x.values.toList());
|
||||
// Must be mutable.
|
||||
x[0] = 22;
|
||||
Expect.equals(22, x[0]);
|
||||
// Must be extendable.
|
||||
x["gee"] = 499;
|
||||
Expect.equals(499, x["gee"]);
|
||||
});
|
||||
|
||||
Map cyclicMap = {};
|
||||
cyclicMap["cycle"] = cyclicMap;
|
||||
ping.send(cyclicMap);
|
||||
checks.add((x) {
|
||||
Expect.isTrue(x is LinkedHashMap);
|
||||
Expect.identical(x, x["cycle"]);
|
||||
// Must be mutable.
|
||||
x["cycle"] = 22;
|
||||
Expect.equals(22, x["cycle"]);
|
||||
// Must be extendable.
|
||||
x["gee"] = 499;
|
||||
Expect.equals(499, x["gee"]);
|
||||
});
|
||||
|
||||
Map constMap = const {'foo': 499};
|
||||
ping.send(constMap);
|
||||
checks.add((x) {
|
||||
Expect.isTrue(x is Map);
|
||||
print(x.length);
|
||||
Expect.equals(1, x.length);
|
||||
Expect.equals(499, x['foo']);
|
||||
Expect.identical(constMap, x); /// constMap: ok
|
||||
Expect.throws(() => constMap['bar'] = 42);
|
||||
});
|
||||
|
||||
ping.send(new A());
|
||||
checks.add((x) {
|
||||
Expect.isTrue(x is A);
|
||||
Expect.equals(499, x.field);
|
||||
});
|
||||
|
||||
ping.send(new A.named(42));
|
||||
checks.add((x) {
|
||||
Expect.isTrue(x is A);
|
||||
Expect.equals(42, x.field);
|
||||
});
|
||||
|
||||
ping.send(new B());
|
||||
checks.add((x) {
|
||||
Expect.isTrue(x is A);
|
||||
Expect.isTrue(x is B);
|
||||
Expect.equals(499, x.field);
|
||||
Expect.equals(99, x.field2);
|
||||
Expect.throws(() => x.field2 = 22);
|
||||
});
|
||||
|
||||
ping.send(new B.named(1, 2));
|
||||
checks.add((x) {
|
||||
Expect.isTrue(x is A);
|
||||
Expect.isTrue(x is B);
|
||||
Expect.equals(2, x.field);
|
||||
Expect.equals(1, x.field2);
|
||||
Expect.throws(() => x.field2 = 22);
|
||||
});
|
||||
|
||||
ping.send(new C());
|
||||
checks.add((x) {
|
||||
Expect.isTrue(x is A);
|
||||
Expect.isTrue(x is B);
|
||||
Expect.isTrue(x is C);
|
||||
Expect.equals(33, x.field);
|
||||
Expect.equals(99, x.field2);
|
||||
Expect.equals(499, x.superField);
|
||||
Expect.throws(() => x.field2 = 22);
|
||||
});
|
||||
|
||||
ping.send(new D());
|
||||
checks.add((x) {
|
||||
Expect.isTrue(x is A);
|
||||
Expect.isTrue(x is B);
|
||||
Expect.isTrue(x is C);
|
||||
Expect.isTrue(x is D);
|
||||
Expect.isTrue(x is M);
|
||||
Expect.equals(33, x.field);
|
||||
Expect.equals(11, x.field2);
|
||||
Expect.equals(499, x.superField);
|
||||
Expect.equals(99, x.superField2);
|
||||
Expect.throws(() => x.field2 = 22);
|
||||
});
|
||||
|
||||
D cyclicD = new D();
|
||||
cyclicD.field = cyclicD;
|
||||
ping.send(cyclicD);
|
||||
checks.add((x) {
|
||||
Expect.isTrue(x is A);
|
||||
Expect.isTrue(x is B);
|
||||
Expect.isTrue(x is C);
|
||||
Expect.isTrue(x is D);
|
||||
Expect.isTrue(x is M);
|
||||
Expect.identical(x, x.field);
|
||||
Expect.equals(11, x.field2);
|
||||
Expect.equals(499, x.superField);
|
||||
Expect.equals(99, x.superField2);
|
||||
Expect.throws(() => x.field2 = 22);
|
||||
});
|
||||
|
||||
ping.send(new E(E.fooFun)); /// fun: ok
|
||||
checks.add((x) { /// fun: continued
|
||||
Expect.equals(E.fooFun, x.fun); /// fun: continued
|
||||
Expect.equals(499, x.fun()); /// fun: continued
|
||||
}); /// fun: continued
|
||||
|
||||
ping.send(new E(barFun)); /// fun: continued
|
||||
checks.add((x) { /// fun: continued
|
||||
Expect.equals(barFun, x.fun); /// fun: continued
|
||||
Expect.equals(42, x.fun()); /// fun: continued
|
||||
}); /// fun: continued
|
||||
|
||||
Expect.throws(() => ping.send(new E(new E(null).instanceFun)));
|
||||
|
||||
F nonConstF = new F();
|
||||
ping.send(nonConstF);
|
||||
checks.add((x) {
|
||||
Expect.equals("field", x.field);
|
||||
Expect.isFalse(identical(nonConstF, x));
|
||||
});
|
||||
|
||||
const F constF = const F();
|
||||
ping.send(constF);
|
||||
checks.add((x) {
|
||||
Expect.equals("field", x.field);
|
||||
Expect.identical(constF, x); /// constInstance: ok
|
||||
});
|
||||
|
||||
G g1 = new G(nonConstF);
|
||||
G g2 = new G(constF);
|
||||
G g3 = const G(constF);
|
||||
ping.send(g1);
|
||||
ping.send(g2);
|
||||
ping.send(g3);
|
||||
|
||||
checks.add((x) { // g1.
|
||||
Expect.isTrue(x is G);
|
||||
Expect.isFalse(identical(g1, x));
|
||||
F f = x.field;
|
||||
Expect.equals("field", f.field);
|
||||
Expect.isFalse(identical(nonConstF, f));
|
||||
});
|
||||
checks.add((x) { // g1.
|
||||
Expect.isTrue(x is G);
|
||||
Expect.isFalse(identical(g1, x));
|
||||
F f = x.field;
|
||||
Expect.equals("field", f.field);
|
||||
Expect.identical(constF, f); /// constInstance: continued
|
||||
});
|
||||
checks.add((x) { // g3.
|
||||
Expect.isTrue(x is G);
|
||||
Expect.identical(g1, x); /// constInstance: continued
|
||||
F f = x.field;
|
||||
Expect.equals("field", f.field);
|
||||
Expect.identical(constF, f); /// constInstance: continued
|
||||
});
|
||||
|
||||
// Make sure objects in a map are serialized and deserialized in the correct
|
||||
// order.
|
||||
Map m = new Map();
|
||||
Value val1 = new Value(1);
|
||||
Value val2 = new Value(2);
|
||||
m[val1] = val2;
|
||||
m[val2] = val1;
|
||||
// Possible bug we want to catch:
|
||||
// serializer runs through keys first, and then the values:
|
||||
// - id1 = val1, id2 = val2, ref[id2], ref[id1]
|
||||
// deserializer runs through the keys and values in order:
|
||||
// - val1; // id1.
|
||||
// - ref[id2]; // boom. Wasn't deserialized yet.
|
||||
ping.send(m);
|
||||
checks.add((x) {
|
||||
Expect.isTrue(x is Map);
|
||||
Expect.equals(2, x.length);
|
||||
Expect.equals(val2, x[val1]);
|
||||
Expect.equals(val1, x[val2]);
|
||||
Expect.identical(x.keys.elementAt(0), x.values.elementAt(1));
|
||||
Expect.identical(x.keys.elementAt(1), x.values.elementAt(0));
|
||||
});
|
||||
}
|
||||
|
||||
void main() {
|
||||
asyncStart();
|
||||
Queue checks = new Queue();
|
||||
ReceivePort testPort = new ReceivePort();
|
||||
Completer completer = new Completer();
|
||||
|
||||
testPort.listen((msg) {
|
||||
Function check = checks.removeFirst();
|
||||
check(msg);
|
||||
if (checks.isEmpty) {
|
||||
completer.complete();
|
||||
testPort.close();
|
||||
}
|
||||
});
|
||||
|
||||
ReceivePort initialReplyPort = new ReceivePort();
|
||||
Isolate
|
||||
.spawn(echoMain, [initialReplyPort.sendPort, testPort.sendPort])
|
||||
.then((_) => initialReplyPort.first)
|
||||
.then((SendPort ping) {
|
||||
runTests(ping, checks);
|
||||
Expect.isTrue(checks.length > 0);
|
||||
completer.future
|
||||
.then((_) => ping.send("halt"))
|
||||
.then((_) => asyncEnd());
|
||||
});
|
||||
}
|
|
@ -16,25 +16,23 @@ class A {
|
|||
}
|
||||
|
||||
fun(msg) {
|
||||
print("received: ${msg.x}");
|
||||
msg.x = 1;
|
||||
print("done updating: ${msg.x}");
|
||||
var a = msg[0];
|
||||
var replyTo = msg[1];
|
||||
print("received: ${a.x}");
|
||||
a.x = 1;
|
||||
print("done updating: ${a.x}");
|
||||
replyTo.send("done");
|
||||
}
|
||||
|
||||
main() {
|
||||
asyncStart();
|
||||
var a = new A();
|
||||
// Sending an A object to another isolate should not work.
|
||||
Isolate.spawn(fun, a).then((isolate) {
|
||||
new Timer(const Duration(milliseconds: 300), () {
|
||||
// Changes in other isolate must not reach here.
|
||||
Expect.equals(0, a.x);
|
||||
asyncEnd();
|
||||
});
|
||||
}, onError: (e) {
|
||||
// Sending an A isn't required to work.
|
||||
// It works in the VM, but not in dart2js.
|
||||
print("Send of A failed:\n$e");
|
||||
ReceivePort rp = new ReceivePort();
|
||||
Isolate.spawn(fun, [a, rp.sendPort]);
|
||||
rp.first.then((msg) {
|
||||
Expect.equals("done", msg);
|
||||
// Changes in other isolate must not reach here.
|
||||
Expect.equals(0, a.x);
|
||||
asyncEnd();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
// Copyright (c) 2011, 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.
|
||||
|
||||
// Dart test program for testing serialization of messages without spawning
|
||||
// isolates.
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Serialization test.
|
||||
// ---------------------------------------------------------------------------
|
||||
library SerializationTest;
|
||||
import "package:expect/expect.dart";
|
||||
import 'dart:isolate';
|
||||
|
||||
main() {
|
||||
// TODO(sigmund): fix once we can disable privacy for testing (bug #1882)
|
||||
testAllTypes(TestingOnly.copy);
|
||||
testAllTypes(TestingOnly.serialize);
|
||||
}
|
||||
|
||||
void testAllTypes(Function f) {
|
||||
copyAndVerify(0, f);
|
||||
copyAndVerify(499, f);
|
||||
copyAndVerify(true, f);
|
||||
copyAndVerify(false, f);
|
||||
copyAndVerify("", f);
|
||||
copyAndVerify("foo", f);
|
||||
copyAndVerify([], f);
|
||||
copyAndVerify([1, 2], f);
|
||||
copyAndVerify([[]], f);
|
||||
copyAndVerify([1, []], f);
|
||||
copyAndVerify({}, f);
|
||||
copyAndVerify({ 'a': 3 }, f);
|
||||
copyAndVerify({ 'a': 3, 'b': 5, 'c': 8 }, f);
|
||||
copyAndVerify({ 'a': [1, 2] }, f);
|
||||
copyAndVerify({ 'b': { 'c' : 99 } }, f);
|
||||
copyAndVerify([ { 'a': 499 }, { 'b': 42 } ], f);
|
||||
|
||||
var port = new ReceivePort();
|
||||
Expect.throws(() => f(port));
|
||||
port.close();
|
||||
|
||||
var a = [ 1, 3, 5 ];
|
||||
var b = { 'b': 49 };
|
||||
var c = [ a, b, a, b, a ];
|
||||
var copied = f(c);
|
||||
verify(c, copied);
|
||||
Expect.isFalse(identical(c, copied));
|
||||
Expect.identical(copied[0], copied[2]);
|
||||
Expect.identical(copied[0], copied[4]);
|
||||
Expect.identical(copied[1], copied[3]);
|
||||
}
|
||||
|
||||
void copyAndVerify(o, Function f) {
|
||||
var copy = f(o);
|
||||
verify(o, copy);
|
||||
}
|
||||
|
||||
void verify(o, copy) {
|
||||
if ((o is bool) || (o is num) || (o is String)) {
|
||||
Expect.equals(o, copy);
|
||||
} else if (o is List) {
|
||||
Expect.isTrue(copy is List);
|
||||
Expect.equals(o.length, copy.length);
|
||||
for (int i = 0; i < o.length; i++) {
|
||||
verify(o[i], copy[i]);
|
||||
}
|
||||
} else if (o is Map) {
|
||||
Expect.isTrue(copy is Map);
|
||||
Expect.equals(o.length, copy.length);
|
||||
o.forEach((key, value) {
|
||||
Expect.isTrue(copy.containsKey(key));
|
||||
verify(value, copy[key]);
|
||||
});
|
||||
} else {
|
||||
Expect.fail("Unexpected object encountered");
|
||||
}
|
||||
}
|
|
@ -141,6 +141,8 @@ const_switch_test/04: RuntimeError # Issue 17960
|
|||
|
||||
deferred_constant_list_test: RuntimeError # Issue 21293
|
||||
|
||||
enum_const_test: RuntimeError # Issue 21817
|
||||
|
||||
# Compilation errors.
|
||||
const_var_test: CompileTimeError # Issue 12793
|
||||
map_literal3_test: CompileTimeError # Issue 12793
|
||||
|
|
Loading…
Reference in a new issue