blob: f68ff2175ca435d2037f14bdb98be8aefa41de52 [file] [log] [blame]
// Copyright (c) 2015, 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.
import 'dart:async';
import 'dart:isolate';
import 'package:stack_trace/stack_trace.dart';
import '../frontend/expect.dart';
/// An exception that was thrown remotely.
///
/// This could be an exception thrown in a different isolate, a different
/// process, or on an entirely different computer.
class RemoteException implements Exception {
/// The original exception's message, if it had one.
///
/// If the original exception was a plain string, this will contain that
/// string.
final String message;
/// The value of the original exception's `runtimeType.toString()`.
final String type;
/// The value of the original exception's `toString()`.
final String _toString;
/// Serializes [error] and [stackTrace] into a JSON-safe object.
///
/// Other than JSON- and isolate-safety, no guarantees are made about the
/// serialized format.
static serialize(error, StackTrace stackTrace) {
var message;
if (error is String) {
message = error;
} else {
try {
message = error.message.toString();
} on NoSuchMethodError catch (_) {
// Do nothing.
}
}
// It's possible (although unlikely) for a user-defined class to have
// multiple of these supertypes. That's fine, though, since we only care
// about core-library-raised IsolateSpawnExceptions anyway.
var supertype;
if (error is TestFailure) {
supertype = 'TestFailure';
} else if (error is IsolateSpawnException) {
supertype = 'IsolateSpawnException';
}
return {
'message': message,
'type': error.runtimeType.toString(),
'supertype': supertype,
'toString': error.toString(),
'stackChain': new Chain.forTrace(stackTrace).toString()
};
}
/// Deserializes an exception serialized with [RemoteException.serialize].
///
/// The returned [AsyncError] is guaranteed to have a [RemoteException] as its
/// error and a [Chain] as its stack trace.
static AsyncError deserialize(serialized) {
return new AsyncError(_deserializeException(serialized),
new Chain.parse(serialized['stackChain']));
}
/// Deserializes the exception portion of [serialized].
static RemoteException _deserializeException(serialized) {
var message = serialized['message'];
var type = serialized['type'];
var toString = serialized['toString'];
switch (serialized['supertype']) {
case 'TestFailure':
return new _RemoteTestFailure(message, type, toString);
case 'IsolateSpawnException':
return new _RemoteIsolateSpawnException(message, type, toString);
default:
return new RemoteException._(message, type, toString);
}
}
RemoteException._(this.message, this.type, this._toString);
String toString() => _toString;
}
/// A subclass of [RemoteException] that implements [TestFailure].
///
/// It's important to preserve [TestFailure]-ness, because tests have different
/// results depending on whether an exception was a failure or an error.
class _RemoteTestFailure extends RemoteException implements TestFailure {
_RemoteTestFailure(String message, String type, String toString)
: super._(message, type, toString);
}
/// A subclass of [RemoteException] that implements [IsolateSpawnException].
class _RemoteIsolateSpawnException extends RemoteException
implements IsolateSpawnException {
_RemoteIsolateSpawnException(String message, String type, String toString)
: super._(message, type, toString);
}