Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(177)

Unified Diff: sdk/lib/_internal/compiler/js_lib/isolate_helper.dart

Issue 1212513002: sdk files reorganization to make dart2js a proper package (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: renamed Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: sdk/lib/_internal/compiler/js_lib/isolate_helper.dart
diff --git a/sdk/lib/_internal/compiler/js_lib/isolate_helper.dart b/sdk/lib/_internal/compiler/js_lib/isolate_helper.dart
deleted file mode 100644
index e3f13bff076baa03c518ac68d1073c3d3582d0c5..0000000000000000000000000000000000000000
--- a/sdk/lib/_internal/compiler/js_lib/isolate_helper.dart
+++ /dev/null
@@ -1,1452 +0,0 @@
-// 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.
-
-library _isolate_helper;
-
-import 'dart:_js_embedded_names' show
- CLASS_ID_EXTRACTOR,
- CLASS_FIELDS_EXTRACTOR,
- CREATE_NEW_ISOLATE,
- CURRENT_SCRIPT,
- GLOBAL_FUNCTIONS,
- INITIALIZE_EMPTY_INSTANCE,
- INSTANCE_FROM_CLASS_ID;
-
-import 'dart:async';
-import 'dart:collection' show Queue;
-import 'dart:isolate';
-import 'dart:_native_typed_data' show NativeByteBuffer, NativeTypedData;
-
-import 'dart:_js_helper' show
- Closure,
- InternalMap,
- Null,
- Primitives,
- convertDartClosureToJS,
- isDartObject,
- random64,
- requiresPreamble;
-
-import 'dart:_foreign_helper' show DART_CLOSURE_TO_JS,
- JS,
- JS_CURRENT_ISOLATE_CONTEXT,
- JS_CURRENT_ISOLATE,
- JS_EMBEDDED_GLOBAL,
- JS_SET_CURRENT_ISOLATE,
- IsolateContext;
-
-import 'dart:_interceptors' show Interceptor,
- JSArray,
- JSExtendableArray,
- JSFixedArray,
- JSIndexable,
- JSMutableArray,
- JSObject;
-
-
-part 'isolate_serialization.dart';
-
-/**
- * Called by the compiler to support switching
- * between isolates when we get a callback from the DOM.
- */
-_callInIsolate(_IsolateContext isolate, Function function) {
- var result = isolate.eval(function);
- // If we are not already running the event-loop start it now.
- if (!_currentIsolate()._isExecutingEvent) _globalState.topEventLoop.run();
- return result;
-}
-
-/// Marks entering a JavaScript async operation to keep the worker alive.
-///
-/// To be called by library code before starting an async operation controlled
-/// by the JavaScript event handler.
-///
-/// Also call [leaveJsAsync] in all callback handlers marking the end of that
-/// async operation (also error handlers) so the worker can be released.
-///
-/// These functions only has to be called for code that can be run from a
-/// worker-isolate (so not for general dom operations).
-enterJsAsync() {
- _globalState.topEventLoop._activeJsAsyncCount++;
-}
-
-/// Marks leaving a javascript async operation.
-///
-/// See [enterJsAsync].
-leaveJsAsync() {
- _globalState.topEventLoop._activeJsAsyncCount--;
- assert(_globalState.topEventLoop._activeJsAsyncCount >= 0);
-}
-
-/// Returns true if we are currently in a worker context.
-bool isWorker() => _globalState.isWorker;
-
-/**
- * Called by the compiler to fetch the current isolate context.
- */
-_IsolateContext _currentIsolate() => _globalState.currentContext;
-
-/**
- * Wrapper that takes the dart entry point and runs it within an isolate. The
- * dart2js compiler will inject a call of the form
- * [: startRootIsolate(main); :] when it determines that this wrapping
- * is needed. For single-isolate applications (e.g. hello world), this
- * call is not emitted.
- */
-void startRootIsolate(entry, args) {
- // The dartMainRunner can inject a new arguments array. We pass the arguments
- // through a "JS", so that the type-inferrer loses track of it.
- args = JS("", "#", args);
- if (args == null) args = [];
- if (args is! List) {
- throw new ArgumentError("Arguments to main must be a List: $args");
- }
- _globalState = new _Manager(entry);
-
- // Don't start the main loop again, if we are in a worker.
- if (_globalState.isWorker) return;
- final rootContext = new _IsolateContext();
- _globalState.rootContext = rootContext;
-
- // BUG(5151491): Setting currentContext should not be necessary, but
- // because closures passed to the DOM as event handlers do not bind their
- // isolate automatically we try to give them a reasonable context to live in
- // by having a "default" isolate (the first one created).
- _globalState.currentContext = rootContext;
- if (entry is _MainFunctionArgs) {
- rootContext.eval(() { entry(args); });
- } else if (entry is _MainFunctionArgsMessage) {
- rootContext.eval(() { entry(args, null); });
- } else {
- rootContext.eval(entry);
- }
- _globalState.topEventLoop.run();
-}
-
-/********************************************************
- Inserted from lib/isolate/dart2js/isolateimpl.dart
- ********************************************************/
-
-/**
- * Concepts used here:
- *
- * "manager" - A manager contains one or more isolates, schedules their
- * execution, and performs other plumbing on their behalf. The isolate
- * present at the creation of the manager is designated as its "root isolate".
- * A manager may, for example, be implemented on a web Worker.
- *
- * [_Manager] - State present within a manager (exactly once, as a global).
- *
- * [_ManagerStub] - A handle held within one manager that allows interaction
- * with another manager. A target manager may be addressed by zero or more
- * [_ManagerStub]s.
- * TODO(ahe): The _ManagerStub concept is broken. It was an attempt
- * to create a common interface between the native Worker class and
- * _MainManagerStub.
- */
-
-/**
- * A native object that is shared across isolates. This object is visible to all
- * isolates running under the same manager (either UI or background web worker).
- *
- * This is code that is intended to 'escape' the isolate boundaries in order to
- * implement the semantics of isolates in JavaScript. Without this we would have
- * been forced to implement more code (including the top-level event loop) in
- * JavaScript itself.
- */
-// TODO(eub, sigmund): move the "manager" to be entirely in JS.
-// Running any Dart code outside the context of an isolate gives it
-// the chance to break the isolate abstraction.
-_Manager get _globalState => JS("_Manager", "init.globalState");
-
-set _globalState(_Manager val) {
- JS("void", "init.globalState = #", val);
-}
-
-/** State associated with the current manager. See [globalState]. */
-// TODO(sigmund): split in multiple classes: global, thread, main-worker states?
-class _Manager {
-
- /** Next available isolate id within this [_Manager]. */
- int nextIsolateId = 0;
-
- /** id assigned to this [_Manager]. */
- int currentManagerId = 0;
-
- /**
- * Next available manager id. Only used by the main manager to assign a unique
- * id to each manager created by it.
- */
- int nextManagerId = 1;
-
- /** Context for the currently running [Isolate]. */
- _IsolateContext currentContext = null;
-
- /** Context for the root [Isolate] that first run in this [_Manager]. */
- _IsolateContext rootContext = null;
-
- /** The top-level event loop. */
- _EventLoop topEventLoop;
-
- /** Whether this program is running from the command line. */
- bool fromCommandLine;
-
- /** Whether this [_Manager] is running as a web worker. */
- bool isWorker;
-
- /** Whether we support spawning web workers. */
- bool supportsWorkers;
-
- /**
- * Whether to use web workers when implementing isolates. Set to false for
- * debugging/testing.
- */
- bool get useWorkers => supportsWorkers;
-
- /**
- * 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
- * dead, but DOM callbacks could resurrect it.
- */
- Map<int, _IsolateContext> isolates;
-
- /** Reference to the main [_Manager]. Null in the main [_Manager] itself. */
- _MainManagerStub mainManager;
-
- /// Registry of active Web Workers. Only used in the main [_Manager].
- Map<int, dynamic /* Worker */> managers;
-
- /** The entry point given by [startRootIsolate]. */
- final Function entry;
-
- _Manager(this.entry) {
- _nativeDetectEnvironment();
- topEventLoop = new _EventLoop();
- isolates = new Map<int, _IsolateContext>();
- managers = new Map<int, dynamic>();
- if (isWorker) { // "if we are not the main manager ourself" is the intent.
- mainManager = new _MainManagerStub();
- _nativeInitWorkerMessageHandler();
- }
- }
-
- void _nativeDetectEnvironment() {
- bool isWindowDefined = globalWindow != null;
- bool isWorkerDefined = globalWorker != null;
-
- isWorker = !isWindowDefined && globalPostMessageDefined;
- supportsWorkers = isWorker
- || (isWorkerDefined && IsolateNatives.thisScript != null);
- fromCommandLine = !isWindowDefined && !isWorker;
- }
-
- void _nativeInitWorkerMessageHandler() {
- var function = JS('',
- "(function (f, a) { return function (e) { f(a, e); }})(#, #)",
- DART_CLOSURE_TO_JS(IsolateNatives._processWorkerMessage),
- mainManager);
- JS("void", r"self.onmessage = #", function);
- // We ensure dartPrint is defined so that the implementation of the Dart
- // print method knows what to call.
- JS('', '''self.dartPrint = self.dartPrint || (function(serialize) {
- return function (object) {
- if (self.console && self.console.log) {
- self.console.log(object)
- } else {
- self.postMessage(serialize(object));
- }
- }
-})(#)''', DART_CLOSURE_TO_JS(_serializePrintMessage));
- }
-
- static _serializePrintMessage(object) {
- return _serializeMessage({"command": "print", "msg": object});
- }
-
- /**
- * Close the worker running this code if all isolates are done and
- * there are no active async JavaScript tasks still running.
- */
- void maybeCloseWorker() {
- if (isWorker
- && isolates.isEmpty
- && topEventLoop._activeJsAsyncCount == 0) {
- mainManager.postMessage(_serializeMessage({'command': 'close'}));
- }
- }
-}
-
-/** Context information tracked for each isolate. */
-class _IsolateContext implements IsolateContext {
- /** Current isolate id. */
- final int id = _globalState.nextIsolateId++;
-
- /** Registry of receive ports currently active on this isolate. */
- final Map<int, RawReceivePortImpl> ports = new Map<int, RawReceivePortImpl>();
-
- /** Registry of weak receive ports currently active on this isolate. */
- final Set<int> weakPorts = new Set<int>();
-
- /** Holds isolate globals (statics and top-level properties). */
- // native object containing all globals of an isolate.
- final isolateStatics =
- JS('', "#()", JS_EMBEDDED_GLOBAL('', CREATE_NEW_ISOLATE));
-
- final RawReceivePortImpl controlPort = new RawReceivePortImpl._controlPort();
-
- final Capability pauseCapability = new Capability();
- final Capability terminateCapability = new Capability(); // License to kill.
-
- /// Boolean flag set when the initial method of the isolate has been executed.
- ///
- /// Used to avoid considering the isolate dead when it has no open
- /// receive ports and no scheduled timers, because it hasn't had time to
- /// create them yet.
- bool initialized = false;
-
- // TODO(lrn): Store these in single "PauseState" object, so they don't take
- // up as much room when not pausing.
- bool isPaused = false;
- List<_IsolateEvent> delayedEvents = [];
- Set<Capability> pauseTokens = new Set();
-
- // Container with the "on exit" handler send-ports and responses.
- var doneHandlers;
-
- /**
- * Queue of functions to call when the current event is complete.
- *
- * These events are not just put at the front of the event queue, because
- * they represent control messages, and should be handled even if the
- * event queue is paused.
- */
- var _scheduledControlEvents;
- bool _isExecutingEvent = false;
-
- /** Whether uncaught errors are considered fatal. */
- bool errorsAreFatal = true;
-
- // Set of ports that listen to uncaught errors.
- Set<SendPort> errorPorts = new Set();
-
- _IsolateContext() {
- this.registerWeak(controlPort._id, controlPort);
- }
-
- void addPause(Capability authentification, Capability resume) {
- if (pauseCapability != authentification) return;
- if (pauseTokens.add(resume) && !isPaused) {
- isPaused = true;
- }
- _updateGlobalState();
- }
-
- void removePause(Capability resume) {
- if (!isPaused) return;
- pauseTokens.remove(resume);
- if (pauseTokens.isEmpty) {
- while(delayedEvents.isNotEmpty) {
- _IsolateEvent event = delayedEvents.removeLast();
- _globalState.topEventLoop.prequeue(event);
- }
- isPaused = false;
- }
- _updateGlobalState();
- }
-
- void addDoneListener(SendPort responsePort, Object response) {
- if (doneHandlers == null) {
- doneHandlers = [];
- }
- for (int i = 0; i < doneHandlers.length; i += 2) {
- if (responsePort == doneHandlers[i]) {
- doneHandlers[i + 1] = response;
- return;
- }
- }
- doneHandlers.add(responsePort);
- doneHandlers.add(response);
- }
-
- void removeDoneListener(SendPort responsePort) {
- if (doneHandlers == null) return;
- for (int i = 0; i < doneHandlers.length; i += 2) {
- if (responsePort == doneHandlers[i]) {
- doneHandlers.removeRange(i, i + 2);
- return;
- }
- }
- }
-
- void setErrorsFatal(Capability authentification, bool errorsAreFatal) {
- if (terminateCapability != authentification) return;
- this.errorsAreFatal = errorsAreFatal;
- }
-
- void handlePing(SendPort responsePort, int pingType, Object response) {
- if (pingType == Isolate.IMMEDIATE ||
- (pingType == Isolate.BEFORE_NEXT_EVENT &&
- !_isExecutingEvent)) {
- responsePort.send(response);
- return;
- }
- void respond() { responsePort.send(response); }
- assert(pingType == Isolate.BEFORE_NEXT_EVENT);
- if (_scheduledControlEvents == null) {
- _scheduledControlEvents = new Queue();
- }
- _scheduledControlEvents.addLast(respond);
- }
-
- void handleKill(Capability authentification, int priority) {
- if (this.terminateCapability != authentification) return;
- if (priority == Isolate.IMMEDIATE ||
- (priority == Isolate.BEFORE_NEXT_EVENT &&
- !_isExecutingEvent)) {
- kill();
- return;
- }
- assert(priority == Isolate.BEFORE_NEXT_EVENT);
- if (_scheduledControlEvents == null) {
- _scheduledControlEvents = new Queue();
- }
- _scheduledControlEvents.addLast(kill);
- }
-
- void addErrorListener(SendPort port) {
- errorPorts.add(port);
- }
-
- void removeErrorListener(SendPort port) {
- errorPorts.remove(port);
- }
-
- /** Function called with an uncaught error. */
- void handleUncaughtError(error, StackTrace stackTrace) {
- // Just print the error if there is no error listener registered.
- if (errorPorts.isEmpty) {
- // An uncaught error in the root isolate will terminate the program?
- if (errorsAreFatal && identical(this, _globalState.rootContext)) {
- // The error will be rethrown to reach the global scope, so
- // don't print it.
- return;
- }
- if (JS('bool', 'self.console && self.console.error')) {
- JS('void', 'self.console.error(#, #)', error, stackTrace);
- } else {
- print(error);
- if (stackTrace != null) print(stackTrace);
- }
- return;
- }
- List message = new List(2)
- ..[0] = error.toString()
- ..[1] = (stackTrace == null) ? null : stackTrace.toString();
- for (SendPort port in errorPorts) port.send(message);
- }
-
- /**
- * Run [code] in the context of the isolate represented by [this].
- */
- dynamic eval(Function code) {
- var old = _globalState.currentContext;
- _globalState.currentContext = this;
- this._setGlobals();
- var result = null;
- var oldIsExecutingEvent = _isExecutingEvent;
- _isExecutingEvent = true;
- try {
- result = code();
- } catch (e, s) {
- handleUncaughtError(e, s);
- if (errorsAreFatal) {
- kill();
- // An uncaught error in the root context terminates all isolates.
- if (identical(this, _globalState.rootContext)) {
- rethrow;
- }
- }
- } finally {
- _isExecutingEvent = oldIsExecutingEvent;
- _globalState.currentContext = old;
- if (old != null) old._setGlobals();
- if (_scheduledControlEvents != null) {
- while (_scheduledControlEvents.isNotEmpty) {
- (_scheduledControlEvents.removeFirst())();
- }
- }
- }
- return result;
- }
-
- void _setGlobals() {
- JS_SET_CURRENT_ISOLATE(isolateStatics);
- }
-
- /**
- * Handle messages comming in on the control port.
- *
- * These events do not go through the event queue.
- * The `_globalState.currentContext` context is not set to this context
- * during the handling.
- */
- void handleControlMessage(message) {
- switch (message[0]) {
- case "pause":
- addPause(message[1], message[2]);
- break;
- case "resume":
- removePause(message[1]);
- break;
- case 'add-ondone':
- addDoneListener(message[1], message[2]);
- break;
- case 'remove-ondone':
- removeDoneListener(message[1]);
- break;
- case 'set-errors-fatal':
- setErrorsFatal(message[1], message[2]);
- break;
- case "ping":
- handlePing(message[1], message[2], message[3]);
- break;
- case "kill":
- handleKill(message[1], message[2]);
- break;
- case "getErrors":
- addErrorListener(message[1]);
- break;
- case "stopErrors":
- removeErrorListener(message[1]);
- break;
- default:
- }
- }
-
- /** Looks up a port registered for this isolate. */
- RawReceivePortImpl lookup(int portId) => ports[portId];
-
- void _addRegistration(int portId, RawReceivePortImpl port) {
- if (ports.containsKey(portId)) {
- throw new Exception("Registry: ports must be registered only once.");
- }
- ports[portId] = port;
- }
-
- /** Registers a port on this isolate. */
- void register(int portId, RawReceivePortImpl port) {
- _addRegistration(portId, port);
- _updateGlobalState();
- }
-
- /**
- * Registers a weak port on this isolate.
- *
- * The port does not keep the isolate active.
- */
- void registerWeak(int portId, RawReceivePortImpl port) {
- weakPorts.add(portId);
- _addRegistration(portId, port);
- }
-
- void _updateGlobalState() {
- if (ports.length - weakPorts.length > 0 || isPaused || !initialized) {
- _globalState.isolates[id] = this; // indicate this isolate is active
- } else {
- kill();
- }
- }
-
- void kill() {
- if (_scheduledControlEvents != null) {
- // Kill all pending events.
- _scheduledControlEvents.clear();
- }
- // Stop listening on all ports.
- // This should happen before sending events to done handlers, in case
- // we are listening on ourselves.
- // Closes all ports, including control port.
- for (var port in ports.values) {
- port._close();
- }
- ports.clear();
- weakPorts.clear();
- _globalState.isolates.remove(id); // indicate this isolate is not active
- errorPorts.clear();
- if (doneHandlers != null) {
- for (int i = 0; i < doneHandlers.length; i += 2) {
- SendPort responsePort = doneHandlers[i];
- Object response = doneHandlers[i + 1];
- responsePort.send(response);
- }
- doneHandlers = null;
- }
- }
-
- /** Unregister a port on this isolate. */
- void unregister(int portId) {
- ports.remove(portId);
- weakPorts.remove(portId);
- _updateGlobalState();
- }
-}
-
-/** Represent the event loop on a javascript thread (DOM or worker). */
-class _EventLoop {
- final Queue<_IsolateEvent> events = new Queue<_IsolateEvent>();
-
- /// The number of waiting callbacks not controlled by the dart event loop.
- ///
- /// This could be timers or http requests. The worker will only be killed if
- /// this count reaches 0.
- /// Access this by using [enterJsAsync] before starting a JavaScript async
- /// operation and [leaveJsAsync] when the callback has fired.
- int _activeJsAsyncCount = 0;
-
- _EventLoop();
-
- void enqueue(isolate, fn, msg) {
- events.addLast(new _IsolateEvent(isolate, fn, msg));
- }
-
- void prequeue(_IsolateEvent event) {
- events.addFirst(event);
- }
-
- _IsolateEvent dequeue() {
- if (events.isEmpty) return null;
- return events.removeFirst();
- }
-
- void checkOpenReceivePortsFromCommandLine() {
- if (_globalState.rootContext != null
- && _globalState.isolates.containsKey(_globalState.rootContext.id)
- && _globalState.fromCommandLine
- && _globalState.rootContext.ports.isEmpty) {
- // We want to reach here only on the main [_Manager] and only
- // on the command-line. In the browser the isolate might
- // still be alive due to DOM callbacks, but the presumption is
- // that on the command-line, no future events can be injected
- // into the event queue once it's empty. Node has setTimeout
- // so this presumption is incorrect there. We think(?) that
- // in d8 this assumption is valid.
- throw new Exception("Program exited with open ReceivePorts.");
- }
- }
-
- /** Process a single event, if any. */
- bool runIteration() {
- final event = dequeue();
- if (event == null) {
- checkOpenReceivePortsFromCommandLine();
- _globalState.maybeCloseWorker();
- return false;
- }
- event.process();
- return true;
- }
-
- /**
- * Runs multiple iterations of the run-loop. If possible, each iteration is
- * run asynchronously.
- */
- void _runHelper() {
- if (globalWindow != null) {
- // Run each iteration from the browser's top event loop.
- void next() {
- if (!runIteration()) return;
- Timer.run(next);
- }
- next();
- } else {
- // Run synchronously until no more iterations are available.
- while (runIteration()) {}
- }
- }
-
- /**
- * Call [_runHelper] but ensure that worker exceptions are propagated.
- */
- void run() {
- if (!_globalState.isWorker) {
- _runHelper();
- } else {
- try {
- _runHelper();
- } catch (e, trace) {
- _globalState.mainManager.postMessage(_serializeMessage(
- {'command': 'error', 'msg': '$e\n$trace' }));
- }
- }
- }
-}
-
-/** An event in the top-level event queue. */
-class _IsolateEvent {
- _IsolateContext isolate;
- Function fn;
- String message;
-
- _IsolateEvent(this.isolate, this.fn, this.message);
-
- void process() {
- if (isolate.isPaused) {
- isolate.delayedEvents.add(this);
- return;
- }
- isolate.eval(fn);
- }
-}
-
-/** A stub for interacting with the main manager. */
-class _MainManagerStub {
- void postMessage(msg) {
- // "self" is a way to refer to the global context object that
- // works in HTML pages and in Web Workers. It does not work in d8
- // and Firefox jsshell, because that would have been too easy.
- //
- // See: http://www.w3.org/TR/workers/#the-global-scope
- // and: http://www.w3.org/TR/Window/#dfn-self-attribute
- requiresPreamble();
- JS("void", r"self.postMessage(#)", msg);
- }
-}
-
-const String _SPAWNED_SIGNAL = "spawned";
-const String _SPAWN_FAILED_SIGNAL = "spawn failed";
-
-get globalWindow {
- requiresPreamble();
- return JS('', "self.window");
-}
-
-get globalWorker {
- requiresPreamble();
- return JS('', "self.Worker");
-}
-bool get globalPostMessageDefined {
- requiresPreamble();
- return JS('bool', "!!self.postMessage");
-}
-
-typedef _MainFunction();
-typedef _MainFunctionArgs(args);
-typedef _MainFunctionArgsMessage(args, message);
-
-/// Note: IsolateNatives depends on _globalState which is only set up correctly
-/// when 'dart:isolate' has been imported.
-class IsolateNatives {
-
- // We set [enableSpawnWorker] to true (not null) when calling isolate
- // primitives that require support for spawning workers. The field starts out
- // by being null, and dart2js' type inference will track if it can have a
- // non-null value. So by testing if this value is not null, we generate code
- // that dart2js knows is dead when worker support isn't needed.
- // TODO(herhut): Initialize this to false when able to track compile-time
- // constants.
- static var enableSpawnWorker;
-
- static String thisScript = computeThisScript();
-
- /// Associates an ID with a native worker object.
- static final Expando<int> workerIds = new Expando<int>();
-
- /**
- * The src url for the script tag that loaded this function.
- *
- * Used to create JavaScript workers and load deferred libraries.
- */
- static String computeThisScript() {
- var currentScript = JS_EMBEDDED_GLOBAL('', CURRENT_SCRIPT);
- if (currentScript != null) {
- return JS('String', 'String(#.src)', currentScript);
- }
- // A worker has no script tag - so get an url from a stack-trace.
- if (_globalState.isWorker) return computeThisScriptFromTrace();
- return null;
- }
-
- static String computeThisScriptFromTrace() {
- var stack = JS('String|Null', 'new Error().stack');
- if (stack == null) {
- // According to Internet Explorer documentation, the stack
- // property is not set until the exception is thrown. The stack
- // property was not provided until IE10.
- stack = JS('String|Null',
- '(function() {'
- 'try { throw new Error() } catch(e) { return e.stack }'
- '})()');
- if (stack == null) throw new UnsupportedError('No stack trace');
- }
- var pattern, matches;
-
- // This pattern matches V8, Chrome, and Internet Explorer stack
- // traces that look like this:
- // Error
- // at methodName (URI:LINE:COLUMN)
- pattern = JS('',
- r'new RegExp("^ *at [^(]*\\((.*):[0-9]*:[0-9]*\\)$", "m")');
-
-
- matches = JS('JSExtendableArray|Null', '#.match(#)', stack, pattern);
- if (matches != null) return JS('String', '#[1]', matches);
-
- // This pattern matches Firefox stack traces that look like this:
- // methodName@URI:LINE
- pattern = JS('', r'new RegExp("^[^@]*@(.*):[0-9]*$", "m")');
-
- matches = JS('JSExtendableArray|Null', '#.match(#)', stack, pattern);
- if (matches != null) return JS('String', '#[1]', matches);
-
- throw new UnsupportedError('Cannot extract URI from "$stack"');
- }
-
- /**
- * Assume that [e] is a browser message event and extract its message data.
- * We don't import the dom explicitly so, when workers are disabled, this
- * library can also run on top of nodejs.
- */
- static _getEventData(e) => JS("", "#.data", e);
-
- /**
- * Process messages on a worker, either to control the worker instance or to
- * pass messages along to the isolate running in the worker.
- */
- static void _processWorkerMessage(/* Worker */ sender, e) {
- var msg = _deserializeMessage(_getEventData(e));
- switch (msg['command']) {
- case 'start':
- _globalState.currentManagerId = msg['id'];
- String functionName = msg['functionName'];
- Function entryPoint = (functionName == null)
- ? _globalState.entry
- : _getJSFunctionFromName(functionName);
- var args = msg['args'];
- var message = _deserializeMessage(msg['msg']);
- var isSpawnUri = msg['isSpawnUri'];
- var startPaused = msg['startPaused'];
- var replyTo = _deserializeMessage(msg['replyTo']);
- var context = new _IsolateContext();
- _globalState.topEventLoop.enqueue(context, () {
- _startIsolate(entryPoint, args, message,
- isSpawnUri, startPaused, replyTo);
- }, 'worker-start');
- // Make sure we always have a current context in this worker.
- // TODO(7907): This is currently needed because we're using
- // Timers to implement Futures, and this isolate library
- // implementation uses Futures. We should either stop using
- // Futures in this library, or re-adapt if Futures get a
- // different implementation.
- _globalState.currentContext = context;
- _globalState.topEventLoop.run();
- break;
- case 'spawn-worker':
- if (enableSpawnWorker != null) handleSpawnWorkerRequest(msg);
- break;
- case 'message':
- SendPort port = msg['port'];
- // If the port has been closed, we ignore the message.
- if (port != null) {
- msg['port'].send(msg['msg']);
- }
- _globalState.topEventLoop.run();
- break;
- case 'close':
- _globalState.managers.remove(workerIds[sender]);
- JS('void', '#.terminate()', sender);
- _globalState.topEventLoop.run();
- break;
- case 'log':
- _log(msg['msg']);
- break;
- case 'print':
- if (_globalState.isWorker) {
- _globalState.mainManager.postMessage(
- _serializeMessage({'command': 'print', 'msg': msg}));
- } else {
- print(msg['msg']);
- }
- break;
- case 'error':
- throw msg['msg'];
- }
- }
-
- static handleSpawnWorkerRequest(msg) {
- var replyPort = msg['replyPort'];
- spawn(msg['functionName'], msg['uri'],
- msg['args'], msg['msg'],
- false, msg['isSpawnUri'], msg['startPaused']).then((msg) {
- replyPort.send(msg);
- }, onError: (String errorMessage) {
- replyPort.send([_SPAWN_FAILED_SIGNAL, errorMessage]);
- });
- }
-
- /** Log a message, forwarding to the main [_Manager] if appropriate. */
- static _log(msg) {
- if (_globalState.isWorker) {
- _globalState.mainManager.postMessage(
- _serializeMessage({'command': 'log', 'msg': msg }));
- } else {
- try {
- _consoleLog(msg);
- } catch (e, trace) {
- throw new Exception(trace);
- }
- }
- }
-
- static void _consoleLog(msg) {
- requiresPreamble();
- JS("void", r"self.console.log(#)", msg);
- }
-
- static _getJSFunctionFromName(String functionName) {
- var globalFunctionsContainer = JS_EMBEDDED_GLOBAL("", GLOBAL_FUNCTIONS);
- return JS("", "#[#]()", globalFunctionsContainer, functionName);
- }
-
- /**
- * Get a string name for the function, if possible. The result for
- * anonymous functions is browser-dependent -- it may be "" or "anonymous"
- * but you should probably not count on this.
- */
- static String _getJSFunctionName(Function f) {
- return (f is Closure) ? JS("String|Null", r'#.$name', f) : null;
- }
-
- /** Create a new JavaScript object instance given its constructor. */
- static dynamic _allocate(var ctor) {
- return JS("", "new #()", ctor);
- }
-
- static Future<List> spawnFunction(void topLevelFunction(message),
- var message,
- bool startPaused) {
- IsolateNatives.enableSpawnWorker = true;
- final name = _getJSFunctionName(topLevelFunction);
- if (name == null) {
- throw new UnsupportedError(
- "only top-level functions can be spawned.");
- }
- bool isLight = false;
- bool isSpawnUri = false;
- return spawn(name, null, null, message, isLight, isSpawnUri, startPaused);
- }
-
- static Future<List> spawnUri(Uri uri, List<String> args, var message,
- bool startPaused) {
- IsolateNatives.enableSpawnWorker = true;
- bool isLight = false;
- bool isSpawnUri = true;
- return spawn(null, uri.toString(), args, message,
- isLight, isSpawnUri, startPaused);
- }
-
- // TODO(sigmund): clean up above, after we make the new API the default:
-
- /// If [uri] is `null` it is replaced with the current script.
- static Future<List> spawn(String functionName, String uri,
- List<String> args, message,
- bool isLight, bool isSpawnUri, bool startPaused) {
- // Assume that the compiled version of the Dart file lives just next to the
- // dart file.
- // TODO(floitsch): support precompiled version of dart2js output.
- if (uri != null && uri.endsWith(".dart")) uri += ".js";
-
- ReceivePort port = new ReceivePort();
- Completer<List> completer = new Completer();
- port.first.then((msg) {
- if (msg[0] == _SPAWNED_SIGNAL) {
- completer.complete(msg);
- } else {
- assert(msg[0] == _SPAWN_FAILED_SIGNAL);
- completer.completeError(msg[1]);
- }
- });
-
- SendPort signalReply = port.sendPort;
-
- if (_globalState.useWorkers && !isLight) {
- _startWorker(
- functionName, uri, args, message, isSpawnUri, startPaused,
- signalReply, (String message) => completer.completeError(message));
- } else {
- _startNonWorker(
- functionName, uri, args, message, isSpawnUri, startPaused,
- signalReply);
- }
- return completer.future;
- }
-
- static void _startWorker(
- String functionName, String uri,
- List<String> args, message,
- bool isSpawnUri,
- 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',
- 'functionName': functionName,
- 'args': args,
- 'msg': message,
- 'uri': uri,
- 'isSpawnUri': isSpawnUri,
- 'startPaused': startPaused,
- 'replyPort': replyPort}));
- } else {
- _spawnWorker(functionName, uri, args, message,
- isSpawnUri, startPaused, replyPort, onError);
- }
- }
-
- static void _startNonWorker(
- String functionName, String uri,
- List<String> args, var message,
- bool isSpawnUri,
- bool startPaused,
- SendPort replyPort) {
- // TODO(eub): support IE9 using an iframe -- Dart issue 1702.
- if (uri != null) {
- throw new UnsupportedError(
- "Currently spawnUri is not supported without web workers.");
- }
- // 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);
- }, 'nonworker start');
- }
-
- static Isolate get currentIsolate {
- _IsolateContext context = JS_CURRENT_ISOLATE_CONTEXT();
- return new Isolate(context.controlPort.sendPort,
- pauseCapability: context.pauseCapability,
- terminateCapability: context.terminateCapability);
- }
-
- static void _startIsolate(Function topLevel,
- List<String> args, message,
- bool isSpawnUri,
- bool startPaused,
- SendPort replyTo) {
- _IsolateContext context = JS_CURRENT_ISOLATE_CONTEXT();
- Primitives.initializeStatics(context.id);
- // The isolate's port does not keep the isolate open.
- replyTo.send([_SPAWNED_SIGNAL,
- context.controlPort.sendPort,
- context.pauseCapability,
- context.terminateCapability]);
-
- void runStartFunction() {
- context.initialized = true;
- if (!isSpawnUri) {
- topLevel(message);
- } else if (topLevel is _MainFunctionArgsMessage) {
- topLevel(args, message);
- } else if (topLevel is _MainFunctionArgs) {
- topLevel(args);
- } else {
- topLevel();
- }
- }
-
- if (startPaused) {
- context.addPause(context.pauseCapability, context.pauseCapability);
- _globalState.topEventLoop.enqueue(context, runStartFunction,
- 'start isolate');
- } else {
- runStartFunction();
- }
- }
-
- /**
- * Spawns an isolate in a worker. [factoryName] is the Javascript constructor
- * name for the isolate entry point class.
- */
- static void _spawnWorker(functionName, String uri,
- List<String> args, message,
- bool isSpawnUri,
- bool startPaused,
- SendPort replyPort,
- void onError(String message)) {
- if (uri == null) uri = thisScript;
- final worker = JS('var', 'new Worker(#)', uri);
- // Trampolines are used when wanting to call a Dart closure from
- // JavaScript. The helper function DART_CLOSURE_TO_JS only accepts
- // top-level or static methods, and the trampoline allows us to capture
- // arguments and values which can be passed to a static method.
- final onerrorTrampoline = JS(
- '',
- '''
-(function (f, u, c) {
- return function(e) {
- return f(e, u, c)
- }
-})(#, #, #)''',
- DART_CLOSURE_TO_JS(workerOnError), uri, onError);
- JS('void', '#.onerror = #', worker, onerrorTrampoline);
-
- var processWorkerMessageTrampoline = JS(
- '',
- """
-(function (f, a) {
- return function (e) {
- // We can stop listening for errors when the first message is received as
- // we only listen for messages to determine if the uri was bad.
- e.onerror = null;
- return f(a, e);
- }
-})(#, #)""",
- DART_CLOSURE_TO_JS(_processWorkerMessage),
- worker);
- JS('void', '#.onmessage = #', worker, processWorkerMessageTrampoline);
- var workerId = _globalState.nextManagerId++;
- // We also store the id on the worker itself so that we can unregister it.
- workerIds[worker] = workerId;
- _globalState.managers[workerId] = worker;
- JS('void', '#.postMessage(#)', worker, _serializeMessage({
- 'command': 'start',
- 'id': workerId,
- // Note: we serialize replyPort twice because the child worker needs to
- // first deserialize the worker id, before it can correctly deserialize
- // the port (port deserialization is sensitive to what is the current
- // workerId).
- 'replyTo': _serializeMessage(replyPort),
- 'args': args,
- 'msg': _serializeMessage(message),
- 'isSpawnUri': isSpawnUri,
- 'startPaused': startPaused,
- 'functionName': functionName }));
- }
-
- static bool workerOnError(
- /* Event */ event,
- String uri,
- void onError(String message)) {
- // Attempt to shut up the browser, as the error has been handled. Chrome
- // ignores this :-(
- JS('void', '#.preventDefault()', event);
- String message = JS('String|Null', '#.message', event);
- if (message == null) {
- // Some browsers, including Chrome, fail to provide a proper error
- // event.
- message = 'Error spawning worker for $uri';
- } else {
- message = 'Error spawning worker for $uri ($message)';
- }
- onError(message);
- return true;
- }
-}
-
-/********************************************************
- Inserted from lib/isolate/dart2js/ports.dart
- ********************************************************/
-
-/** Common functionality to all send ports. */
-abstract class _BaseSendPort implements SendPort {
- /** Id for the destination isolate. */
- final int _isolateId;
-
- const _BaseSendPort(this._isolateId);
-
- void _checkReplyTo(SendPort replyTo) {
- if (replyTo != null
- && replyTo is! _NativeJsSendPort
- && replyTo is! _WorkerSendPort) {
- throw new Exception("SendPort.send: Illegal replyTo port type");
- }
- }
-
- void send(var message);
- bool operator ==(var other);
- int get hashCode;
-}
-
-/** A send port that delivers messages in-memory via native JavaScript calls. */
-class _NativeJsSendPort extends _BaseSendPort implements SendPort {
- final RawReceivePortImpl _receivePort;
-
- const _NativeJsSendPort(this._receivePort, int isolateId) : super(isolateId);
-
- void send(var message) {
- // Check that the isolate still runs and the port is still open
- final isolate = _globalState.isolates[_isolateId];
- if (isolate == null) return;
- if (_receivePort._isClosed) return;
- // 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) {
- _receivePort._add(msg);
- }
- }, 'receive $message');
- }
-
- bool operator ==(var other) => (other is _NativeJsSendPort) &&
- (_receivePort == other._receivePort);
-
- int get hashCode => _receivePort._id;
-}
-
-/** A send port that delivers messages via worker.postMessage. */
-// TODO(eub): abstract this for iframes.
-class _WorkerSendPort extends _BaseSendPort implements SendPort {
- final int _workerId;
- final int _receivePortId;
-
- const _WorkerSendPort(this._workerId, int isolateId, this._receivePortId)
- : super(isolateId);
-
- void send(var message) {
- final workerMessage = _serializeMessage({
- 'command': 'message',
- 'port': this,
- 'msg': message});
-
- if (_globalState.isWorker) {
- // Communication from one worker to another go through the
- // main worker.
- _globalState.mainManager.postMessage(workerMessage);
- } else {
- // Deliver the message only if the worker is still alive.
- /* Worker */ var manager = _globalState.managers[_workerId];
- if (manager != null) {
- JS('void', '#.postMessage(#)', manager, workerMessage);
- }
- }
- }
-
- bool operator ==(var other) {
- return (other is _WorkerSendPort) &&
- (_workerId == other._workerId) &&
- (_isolateId == other._isolateId) &&
- (_receivePortId == other._receivePortId);
- }
-
- int get hashCode {
- // TODO(sigmund): use a standard hash when we get one available in corelib.
- return (_workerId << 16) ^ (_isolateId << 8) ^ _receivePortId;
- }
-}
-
-class RawReceivePortImpl implements RawReceivePort {
- static int _nextFreeId = 1;
-
- final int _id;
- Function _handler;
- bool _isClosed = false;
-
- RawReceivePortImpl(this._handler) : _id = _nextFreeId++ {
- _globalState.currentContext.register(_id, this);
- }
-
- RawReceivePortImpl.weak(this._handler) : _id = _nextFreeId++ {
- _globalState.currentContext.registerWeak(_id, this);
- }
-
- // Creates the control port of an isolate.
- // This is created before the isolate context object itself,
- // so it cannot access the static _nextFreeId field.
- RawReceivePortImpl._controlPort() : _handler = null, _id = 0;
-
- void set handler(Function newHandler) {
- _handler = newHandler;
- }
-
- // Close the port without unregistering it.
- // Used by an isolate context to close all ports when shutting down.
- void _close() {
- _isClosed = true;
- _handler = null;
- }
-
- void close() {
- if (_isClosed) return;
- _isClosed = true;
- _handler = null;
- _globalState.currentContext.unregister(_id);
- }
-
- void _add(dataEvent) {
- if (_isClosed) return;
- _handler(dataEvent);
- }
-
- SendPort get sendPort {
- return new _NativeJsSendPort(this, _globalState.currentContext.id);
- }
-}
-
-class ReceivePortImpl extends Stream implements ReceivePort {
- final RawReceivePort _rawPort;
- StreamController _controller;
-
- ReceivePortImpl() : this.fromRawReceivePort(new RawReceivePortImpl(null));
-
- ReceivePortImpl.weak()
- : this.fromRawReceivePort(new RawReceivePortImpl.weak(null));
-
- ReceivePortImpl.fromRawReceivePort(this._rawPort) {
- _controller = new StreamController(onCancel: close, sync: true);
- _rawPort.handler = _controller.add;
- }
-
- StreamSubscription listen(void onData(var event),
- {Function onError,
- void onDone(),
- bool cancelOnError}) {
- return _controller.stream.listen(onData, onError: onError, onDone: onDone,
- cancelOnError: cancelOnError);
- }
-
- void close() {
- _rawPort.close();
- _controller.close();
- }
-
- SendPort get sendPort => _rawPort.sendPort;
-}
-
-class TimerImpl implements Timer {
- final bool _once;
- bool _inEventLoop = false;
- int _handle;
-
- TimerImpl(int milliseconds, void callback())
- : _once = true {
- if (milliseconds == 0 && (!hasTimer() || _globalState.isWorker)) {
-
- void internalCallback() {
- _handle = null;
- callback();
- }
-
- // Setting _handle to something different from null indicates that the
- // callback has not been run. Hence, the choice of 1 is arbitrary.
- _handle = 1;
-
- // This makes a dependency between the async library and the
- // event loop of the isolate library. The compiler makes sure
- // that the event loop is compiled if [Timer] is used.
- // TODO(7907): In case of web workers, we need to use the event
- // loop instead of setTimeout, to make sure the futures get executed in
- // order.
- _globalState.topEventLoop.enqueue(
- _globalState.currentContext, internalCallback, 'timer');
- _inEventLoop = true;
- } else if (hasTimer()) {
-
- void internalCallback() {
- _handle = null;
- leaveJsAsync();
- callback();
- }
-
- enterJsAsync();
-
- _handle = JS('int', 'self.setTimeout(#, #)',
- convertDartClosureToJS(internalCallback, 0),
- milliseconds);
- } else {
- assert(milliseconds > 0);
- throw new UnsupportedError("Timer greater than 0.");
- }
- }
-
- TimerImpl.periodic(int milliseconds, void callback(Timer timer))
- : _once = false {
- if (hasTimer()) {
- enterJsAsync();
- _handle = JS('int', 'self.setInterval(#, #)',
- convertDartClosureToJS(() { callback(this); }, 0),
- milliseconds);
- } else {
- throw new UnsupportedError("Periodic timer.");
- }
- }
-
- void cancel() {
- if (hasTimer()) {
- if (_inEventLoop) {
- throw new UnsupportedError("Timer in event loop cannot be canceled.");
- }
- if (_handle == null) return;
- leaveJsAsync();
- if (_once) {
- JS('void', 'self.clearTimeout(#)', _handle);
- } else {
- JS('void', 'self.clearInterval(#)', _handle);
- }
- _handle = null;
- } else {
- throw new UnsupportedError("Canceling a timer.");
- }
- }
-
- bool get isActive => _handle != null;
-}
-
-bool hasTimer() {
- requiresPreamble();
- return JS('', 'self.setTimeout') != null;
-}
-
-
-/**
- * Implementation class for [Capability].
- *
- * It has the same name to make it harder for users to distinguish.
- */
-class CapabilityImpl implements Capability {
- /** Internal random secret identifying the capability. */
- final int _id;
-
- CapabilityImpl() : this._internal(random64());
-
- CapabilityImpl._internal(this._id);
-
- int get hashCode {
- // Thomas Wang 32 bit Mix.
- // http://www.concentric.net/~Ttwang/tech/inthash.htm
- // (via https://gist.github.com/badboy/6267743)
- int hash = _id;
- hash = (hash >> 0) ^ (hash ~/ 0x100000000); // To 32 bit from ~64.
- hash = (~hash + (hash << 15)) & 0xFFFFFFFF;
- hash ^= hash >> 12;
- hash = (hash * 5) & 0xFFFFFFFF;
- hash ^= hash >> 4;
- hash = (hash * 2057) & 0xFFFFFFFF;
- hash ^= hash >> 16;
- return hash;
- }
-
- bool operator==(Object other) {
- if (identical(other, this)) return true;
- if (other is CapabilityImpl) {
- return identical(_id, other._id);
- }
- return false;
- }
-}

Powered by Google App Engine
This is Rietveld 408576698