Files
sdk/runtime/lib/isolate_patch.dart
T
Kevin Millikin 9ab1f22b41 Make some VM libraries patch cleanly using the analyzer.
Modify the following VM dart: library patches: collection, convert,
developer, _internal, isolate, math, mirrors, profiler, vmservice_io,
_vmservice, _builtin, nativewrappers, and io.

The modifications are mostly because:

    - Patches should not be able to introduce new public members to a
      library's API.  In cases where the VM's patches introduce public
      members, the patches are rewritten to introduce private members
      instead.

    - Patches should not be able to replace arbitrary members, only
      ones declared external.  In cases where the VM's patches replace
      arbitrary members, those members are declared external in the
      SDK sources and dart2js's patch files are rewritten to use the
      original SDK implementation.

BUG=
R=sigmund@google.com

Review-Url: https://codereview.chromium.org/2564383002 .
2016-12-15 11:30:20 +01:00

588 lines
18 KiB
Dart

// 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.
import "dart:collection" show HashMap;
import "dart:_internal";
@patch class ReceivePort {
@patch factory ReceivePort() = _ReceivePortImpl;
@patch factory ReceivePort.fromRawReceivePort(RawReceivePort rawPort) =
_ReceivePortImpl.fromRawReceivePort;
}
@patch class Capability {
@patch factory Capability() = _CapabilityImpl;
}
class _CapabilityImpl implements Capability {
factory _CapabilityImpl() native "CapabilityImpl_factory";
bool operator==(var other) {
return (other is _CapabilityImpl) && _equals(other);
}
int get hashCode {
return _get_hashcode();
}
_equals(other) native "CapabilityImpl_equals";
_get_hashcode() native "CapabilityImpl_get_hashcode";
}
@patch class RawReceivePort {
/**
* Opens a long-lived port for receiving messages.
*
* A [RawReceivePort] is low level and does not work with [Zone]s. It
* can not be paused. The data-handler must be set before the first
* event is received.
*/
@patch factory RawReceivePort([void handler(event)]) {
_RawReceivePortImpl result = new _RawReceivePortImpl();
result.handler = handler;
return result;
}
}
class _ReceivePortImpl extends Stream implements ReceivePort {
_ReceivePortImpl() : this.fromRawReceivePort(new RawReceivePort());
_ReceivePortImpl.fromRawReceivePort(this._rawPort) {
_controller = new StreamController(onCancel: close, sync: true);
_rawPort.handler = _controller.add;
}
SendPort get sendPort {
return _rawPort.sendPort;
}
StreamSubscription listen(void onData(var message),
{ Function onError,
void onDone(),
bool cancelOnError }) {
return _controller.stream.listen(onData,
onError: onError,
onDone: onDone,
cancelOnError: cancelOnError);
}
close() {
_rawPort.close();
_controller.close();
}
final RawReceivePort _rawPort;
StreamController _controller;
}
typedef void _ImmediateCallback();
/// The callback that has been registered through `scheduleImmediate`.
_ImmediateCallback _pendingImmediateCallback;
/// The closure that should be used as scheduleImmediateClosure, when the VM
/// is responsible for the event loop.
void _isolateScheduleImmediate(void callback()) {
assert(_pendingImmediateCallback == null);
_pendingImmediateCallback = callback;
}
void _runPendingImmediateCallback() {
if (_pendingImmediateCallback != null) {
var callback = _pendingImmediateCallback;
_pendingImmediateCallback = null;
callback();
}
}
_ImmediateCallback _removePendingImmediateCallback() {
var callback = _pendingImmediateCallback;
_pendingImmediateCallback = null;
return callback;
}
/// The embedder can execute this function to get hold of
/// [_isolateScheduleImmediate] above.
Function _getIsolateScheduleImmediateClosure() {
return _isolateScheduleImmediate;
}
class _RawReceivePortImpl implements RawReceivePort {
factory _RawReceivePortImpl() native "RawReceivePortImpl_factory";
close() {
// Close the port and remove it from the handler map.
_handlerMap.remove(this._closeInternal());
}
SendPort get sendPort {
return _get_sendport();
}
bool operator==(var other) {
return (other is _RawReceivePortImpl) &&
(this._get_id() == other._get_id());
}
int get hashCode {
return sendPort.hashCode;
}
/**** Internal implementation details ****/
_get_id() native "RawReceivePortImpl_get_id";
_get_sendport() native "RawReceivePortImpl_get_sendport";
// Called from the VM to retrieve the handler for a message.
static _lookupHandler(int id) {
var result = _handlerMap[id];
return result;
}
// Called from the VM to dispatch to the handler.
static void _handleMessage(Function handler, var message) {
// TODO(floitsch): this relies on the fact that any exception aborts the
// VM. Once we have non-fatal global exceptions we need to catch errors
// so that we can run the immediate callbacks.
handler(message);
_runPendingImmediateCallback();
}
// Call into the VM to close the VM maintained mappings.
_closeInternal() native "RawReceivePortImpl_closeInternal";
void set handler(Function value) {
_handlerMap[this._get_id()] = value;
}
// TODO(iposva): Ideally keep this map in the VM.
// id to handler mapping.
static _initHandlerMap() {
// TODO(18511): Workaround bad CheckSmi hoisting.
var tempMap = new HashMap();
// Collect feedback that not all keys are Smis.
tempMap["."] = 1;
tempMap["."] = 2;
return new HashMap();
}
static final Map _handlerMap = _initHandlerMap();
}
class _SendPortImpl implements SendPort {
/*--- public interface ---*/
void send(var message) {
_sendInternal(message);
}
bool operator==(var other) {
return (other is _SendPortImpl) && (this._get_id() == other._get_id());
}
int get hashCode {
return _get_hashcode();
}
/*--- private implementation ---*/
_get_id() native "SendPortImpl_get_id";
_get_hashcode() native "SendPortImpl_get_hashcode";
// Forward the implementation of sending messages to the VM.
void _sendInternal(var message) native "SendPortImpl_sendInternal_";
}
typedef _NullaryFunction();
typedef _UnaryFunction(args);
typedef _BinaryFunction(args, message);
/**
* Takes the real entry point as argument and invokes it with the
* initial message. Defers execution of the entry point until the
* isolate is in the message loop.
*/
void _startMainIsolate(Function entryPoint,
List<String> args) {
_startIsolate(null, // no parent port
entryPoint,
args,
null, // no message
true, // isSpawnUri
null, // no control port
null); // no capabilities
}
/**
* Takes the real entry point as argument and invokes it with the initial
* message.
*/
void _startIsolate(SendPort parentPort,
Function entryPoint,
List<String> args,
var message,
bool isSpawnUri,
RawReceivePort controlPort,
List capabilities) {
// The control port (aka the main isolate port) does not handle any messages.
if (controlPort != null) {
controlPort.handler = (_) {}; // Nobody home on the control port.
}
if (parentPort != null) {
// Build a message to our parent isolate providing access to the
// current isolate's control port and capabilities.
//
// TODO(floitsch): Send an error message if we can't find the entry point.
var readyMessage = new List(2);
readyMessage[0] = controlPort.sendPort;
readyMessage[1] = capabilities;
// Out of an excess of paranoia we clear the capabilities from the
// stack. Not really necessary.
capabilities = null;
parentPort.send(readyMessage);
}
assert(capabilities == null);
// Delay all user code handling to the next run of the message loop. This
// allows us to intercept certain conditions in the event dispatch, such as
// starting in paused state.
RawReceivePort port = new RawReceivePort();
port.handler = (_) {
port.close();
if (isSpawnUri) {
if (entryPoint is _BinaryFunction) {
entryPoint(args, message);
} else if (entryPoint is _UnaryFunction) {
entryPoint(args);
} else {
entryPoint();
}
} else {
entryPoint(message);
}
};
// Make sure the message handler is triggered.
port.sendPort.send(null);
}
@patch class Isolate {
static final _currentIsolate = _getCurrentIsolate();
static final _rootUri = _getCurrentRootUri();
@patch static Isolate get current => _currentIsolate;
@patch static Future<Uri> get packageRoot {
var hook = VMLibraryHooks.packageRootUriFuture;
if (hook == null) {
throw new UnsupportedError("Isolate.packageRoot");
}
return hook();
}
@patch static Future<Uri> get packageConfig {
var hook = VMLibraryHooks.packageConfigUriFuture;
if (hook == null) {
throw new UnsupportedError("Isolate.packageConfig");
}
return hook();
}
@patch static Future<Uri> resolvePackageUri(Uri packageUri) {
var hook = VMLibraryHooks.resolvePackageUriFuture;
if (hook == null) {
throw new UnsupportedError("Isolate.resolvePackageUri");
}
return hook(packageUri);
}
static bool _packageSupported() =>
(VMLibraryHooks.packageRootUriFuture != null) &&
(VMLibraryHooks.packageConfigUriFuture != null) &&
(VMLibraryHooks.resolvePackageUriFuture != null);
@patch static Future<Isolate> spawn(
void entryPoint(message), var message,
{bool paused: false, bool errorsAreFatal,
SendPort onExit, SendPort onError}) async {
// `paused` isn't handled yet.
RawReceivePort readyPort;
try {
// Check for the type of `entryPoint` on the spawning isolate to make
// error-handling easier.
if (entryPoint is! _UnaryFunction) {
throw new ArgumentError(entryPoint);
}
// The VM will invoke [_startIsolate] with entryPoint as argument.
readyPort = new RawReceivePort();
// We do not inherit the package root or package config settings
// from the parent isolate, instead we use the values that were
// set on the command line.
var packageRoot = VMLibraryHooks.packageRootString;
var packageConfig = VMLibraryHooks.packageConfigString;
var script = VMLibraryHooks.platformScript;
if (script == null) {
// We do not have enough information to support spawning the new
// isolate.
throw new UnsupportedError("Isolate.spawn");
}
if (script.scheme == "package") {
script = await Isolate.resolvePackageUri(script);
}
_spawnFunction(readyPort.sendPort, script.toString(), entryPoint, message,
paused, errorsAreFatal, onExit, onError,
packageRoot, packageConfig);
return await _spawnCommon(readyPort);
} catch (e, st) {
if (readyPort != null) {
readyPort.close();
}
return await new Future<Isolate>.error(e, st);
}
}
@patch static Future<Isolate> spawnUri(
Uri uri, List<String> args, var message,
{bool paused: false,
SendPort onExit,
SendPort onError,
bool errorsAreFatal,
bool checked,
Map<String, String> environment,
Uri packageRoot,
Uri packageConfig,
bool automaticPackageResolution: false}) async {
RawReceivePort readyPort;
if (environment != null) {
throw new UnimplementedError("environment");
}
// Verify that no mutually exclusive arguments have been passed.
if (automaticPackageResolution) {
if (packageRoot != null) {
throw new ArgumentError("Cannot simultaneously request "
"automaticPackageResolution and specify a"
"packageRoot.");
}
if (packageConfig != null) {
throw new ArgumentError("Cannot simultaneously request "
"automaticPackageResolution and specify a"
"packageConfig.");
}
} else {
if ((packageRoot != null) && (packageConfig != null)) {
throw new ArgumentError("Cannot simultaneously specify a "
"packageRoot and a packageConfig.");
}
}
try {
// Resolve the uri against the current isolate's root Uri first.
var spawnedUri = _rootUri.resolveUri(uri);
// Inherit this isolate's package resolution setup if not overridden.
if (!automaticPackageResolution &&
(packageRoot == null) &&
(packageConfig == null)) {
if (Isolate._packageSupported()) {
packageRoot = await Isolate.packageRoot;
packageConfig = await Isolate.packageConfig;
}
}
// Ensure to resolve package: URIs being handed in as parameters.
if (packageRoot != null) {
// Avoid calling resolvePackageUri if not stricly necessary in case
// the API is not supported.
if (packageRoot.scheme == "package") {
packageRoot = await Isolate.resolvePackageUri(packageRoot);
}
} else if (packageConfig != null) {
// Avoid calling resolvePackageUri if not strictly necessary in case
// the API is not supported.
if (packageConfig.scheme == "package") {
packageConfig = await Isolate.resolvePackageUri(packageConfig);
}
}
// The VM will invoke [_startIsolate] and not `main`.
readyPort = new RawReceivePort();
var packageRootString = packageRoot?.toString();
var packageConfigString = packageConfig?.toString();
_spawnUri(readyPort.sendPort, spawnedUri.toString(),
args, message,
paused, onExit, onError,
errorsAreFatal, checked,
null, /* environment */
packageRootString, packageConfigString);
return await _spawnCommon(readyPort);
} catch (e, st) {
if (readyPort != null) {
readyPort.close();
}
rethrow;
}
}
static Future<Isolate> _spawnCommon(RawReceivePort readyPort) {
Completer completer = new Completer<Isolate>.sync();
readyPort.handler = (readyMessage) {
readyPort.close();
if (readyMessage is List && readyMessage.length == 2) {
SendPort controlPort = readyMessage[0];
List capabilities = readyMessage[1];
completer.complete(new Isolate(controlPort,
pauseCapability: capabilities[0],
terminateCapability: capabilities[1]));
} else if (readyMessage is String) {
// We encountered an error while starting the new isolate.
completer.completeError(new IsolateSpawnException(
'Unable to spawn isolate: ${readyMessage}'));
} else {
// This shouldn't happen.
completer.completeError(new IsolateSpawnException(
"Internal error: unexpected format for ready message: "
"'${readyMessage}'"));
}
};
return completer.future;
}
// TODO(iposva): Cleanup to have only one definition.
// These values need to be kept in sync with the class IsolateMessageHandler
// in vm/isolate.cc.
static const _PAUSE = 1;
static const _RESUME = 2;
static const _PING = 3;
static const _KILL = 4;
static const _ADD_EXIT = 5;
static const _DEL_EXIT = 6;
static const _ADD_ERROR = 7;
static const _DEL_ERROR = 8;
static const _ERROR_FATAL = 9;
static void _spawnFunction(SendPort readyPort, String uri,
Function topLevelFunction,
var message, bool paused, bool errorsAreFatal,
SendPort onExit, SendPort onError,
String packageRoot, String packageConfig)
native "Isolate_spawnFunction";
static void _spawnUri(SendPort readyPort, String uri,
List<String> args, var message,
bool paused, SendPort onExit, SendPort onError,
bool errorsAreFatal, bool checked,
List environment,
String packageRoot, String packageConfig)
native "Isolate_spawnUri";
static void _sendOOB(port, msg) native "Isolate_sendOOB";
@patch void _pause(Capability resumeCapability) {
var msg = new List(4)
..[0] = 0 // Make room for OOB message type.
..[1] = _PAUSE
..[2] = pauseCapability
..[3] = resumeCapability;
_sendOOB(controlPort, msg);
}
@patch void resume(Capability resumeCapability) {
var msg = new List(4)
..[0] = 0 // Make room for OOB message type.
..[1] = _RESUME
..[2] = pauseCapability
..[3] = resumeCapability;
_sendOOB(controlPort, msg);
}
@patch void addOnExitListener(SendPort responsePort,
{Object response}) {
var msg = new List(4)
..[0] = 0 // Make room for OOB message type.
..[1] = _ADD_EXIT
..[2] = responsePort
..[3] = response;
_sendOOB(controlPort, msg);
}
@patch void removeOnExitListener(SendPort responsePort) {
var msg = new List(3)
..[0] = 0 // Make room for OOB message type.
..[1] = _DEL_EXIT
..[2] = responsePort;
_sendOOB(controlPort, msg);
}
@patch void setErrorsFatal(bool errorsAreFatal) {
var msg = new List(4)
..[0] = 0 // Make room for OOB message type.
..[1] = _ERROR_FATAL
..[2] = terminateCapability
..[3] = errorsAreFatal;
_sendOOB(controlPort, msg);
}
@patch void kill({int priority: BEFORE_NEXT_EVENT}) {
var msg = new List(4)
..[0] = 0 // Make room for OOB message type.
..[1] = _KILL
..[2] = terminateCapability
..[3] = priority;
_sendOOB(controlPort, msg);
}
@patch void ping(SendPort responsePort, {Object response,
int priority: IMMEDIATE}) {
var msg = new List(5)
..[0] = 0 // Make room for OOM message type.
..[1] = _PING
..[2] = responsePort
..[3] = priority
..[4] = response;
_sendOOB(controlPort, msg);
}
@patch void addErrorListener(SendPort port) {
var msg = new List(3)
..[0] = 0 // Make room for OOB message type.
..[1] = _ADD_ERROR
..[2] = port;
_sendOOB(controlPort, msg);
}
@patch void removeErrorListener(SendPort port) {
var msg = new List(3)
..[0] = 0 // Make room for OOB message type.
..[1] = _DEL_ERROR
..[2] = port;
_sendOOB(controlPort, msg);
}
static Isolate _getCurrentIsolate() {
List portAndCapabilities = _getPortAndCapabilitiesOfCurrentIsolate();
return new Isolate(portAndCapabilities[0],
pauseCapability: portAndCapabilities[1],
terminateCapability: portAndCapabilities[2]);
}
static List _getPortAndCapabilitiesOfCurrentIsolate()
native "Isolate_getPortAndCapabilitiesOfCurrentIsolate";
static Uri _getCurrentRootUri() {
try {
return Uri.parse(_getCurrentRootUriStr());
} catch (e, s) {
return null;
}
}
static String _getCurrentRootUriStr()
native "Isolate_getCurrentRootUriStr";
}