floitsch@google.com 2014-12-09 16:41:58 +00:00
parent 56defa8060
commit a5f3c39b21
14 changed files with 1012 additions and 613 deletions

View file

@ -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.

View file

@ -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);
}
}

View file

@ -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

View file

@ -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;

View 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);
}
}

View file

@ -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.

View file

@ -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);
}

View file

@ -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";

View file

@ -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();

View file

@ -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.

View 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());
});
}

View file

@ -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();
});
}

View file

@ -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");
}
}

View file

@ -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