Index: frog/frogsh |
diff --git a/frog/frogsh b/frog/frogsh |
index c8bc4a3254d4e79ee794b6b2c0c15d9f54dc3be1..3ed48df486cac65587a4601a7b78bdc508d6c8b2 100755 |
--- a/frog/frogsh |
+++ b/frog/frogsh |
@@ -332,8 +332,12 @@ function $varMethod(name, methods) { |
} |
Object.prototype.get$typeName = function() { |
- // TODO(vsm): how can we make this go down the fast path for Chrome? |
- //(for Chrome: return this.constructor.name;) |
+ // TODO(sigmund): find a way to make this work on all browsers, including |
+ // checking the typeName on prototype objects (so we can fix dynamic |
+ // dispatching on $varMethod). |
+ if (window.constructor.name == 'DOMWindow') { // fast-path for Chrome |
+ return this.constructor.name; |
+ } |
var str = Object.prototype.toString.call(this); |
return str.substring(8, str.length - 1); |
} |
@@ -347,7 +351,7 @@ function $patchMethod(obj, name, methods) { |
while (obj && !(method = methods[obj.get$typeName()])) { |
obj = Object.getPrototypeOf(obj); |
} |
- Object.defineProperty(proto, name, {value: method || methods['Object']}); |
+ obj[name] = method || methods['Object']; |
} |
// ********** Code for Clock ************** |
function Clock() {} |
@@ -515,6 +519,499 @@ function print(obj) { |
} |
} |
// ********** Library dart:coreimpl ************** |
+// ********** Natives isolate.js ************** |
+// 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. |
+ |
+var isolate$current = null; |
+var isolate$rootIsolate = null; // Will only be set in the main worker. |
+var isolate$inits = []; |
+var isolate$globalThis = this; |
+ |
+var isolate$inWorker = |
+ (typeof isolate$globalThis['importScripts']) != "undefined"; |
+var isolate$supportsWorkers = |
+ isolate$inWorker || ((typeof isolate$globalThis['Worker']) != 'undefined'); |
+ |
+var isolate$MAIN_WORKER_ID = 0; |
+// Non-main workers will update the id variable. |
+var isolate$thisWorkerId = isolate$MAIN_WORKER_ID; |
+ |
+// Whether to use web workers when implementing isolates. |
+var isolate$useWorkers = isolate$supportsWorkers; |
+// Uncomment this to not use web workers even if they're available. |
+// isolate$useWorkers = false; |
+ |
+// Whether to use the web-worker JSON-based message serialization protocol, |
+// even if not using web workers. |
+var isolate$useWorkerSerializationProtocol = false; |
+// Uncomment this to always use the web-worker JSON-based message |
+// serialization protocol, e.g. for testing purposes. |
+// isolate$useWorkerSerializationProtocol = true; |
+ |
+ |
+// ------- SendPort ------- |
+ |
+function isolate$receiveMessage(port, isolate, |
+ serializedMessage, serializedReplyTo) { |
+ isolate$IsolateEvent.enqueue(isolate, function() { |
+ var message = isolate$deserializeMessage(serializedMessage); |
+ var replyTo = isolate$deserializeMessage(serializedReplyTo); |
+ port._callback(message, replyTo); |
+ }); |
+} |
+ |
+// -------- Registry --------- |
+function isolate$Registry() { |
+ this.map = {}; |
+ this.count = 0; |
+} |
+ |
+isolate$Registry.prototype.register = function(id, val) { |
+ if (this.map[id]) { |
+ throw Error("Registry: Elements must be registered only once."); |
+ } |
+ this.map[id] = val; |
+ this.count++; |
+}; |
+ |
+isolate$Registry.prototype.unregister = function(id) { |
+ if (id in this.map) { |
+ delete this.map[id]; |
+ this.count--; |
+ } |
+}; |
+ |
+isolate$Registry.prototype.get = function(id) { |
+ return this.map[id]; |
+}; |
+ |
+isolate$Registry.prototype.isEmpty = function() { |
+ return this.count === 0; |
+}; |
+ |
+ |
+// ------- Worker registry ------- |
+// Only used in the main worker. |
+var isolate$workerRegistry = new isolate$Registry(); |
+ |
+// ------- Isolate registry ------- |
+// 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. |
+var isolate$isolateRegistry = new isolate$Registry(); |
+ |
+// ------- Debugging log function ------- |
+function isolate$log(msg) { |
+ return; |
+ if (isolate$inWorker) { |
+ isolate$mainWorker.postMessage({ command: 'log', msg: msg }); |
+ } else { |
+ try { |
+ isolate$globalThis.console.log(msg); |
+ } catch(e) { |
+ throw String(e.stack); |
+ } |
+ } |
+} |
+ |
+function isolate$initializeWorker(workerId) { |
+ isolate$thisWorkerId = workerId; |
+} |
+ |
+var isolate$workerPrint = false; |
+if (isolate$inWorker) { |
+ isolate$workerPrint = function(msg){ |
+ isolate$mainWorker.postMessage({ command: 'print', msg: msg }); |
+ } |
+} |
+ |
+// ------- Message handler ------- |
+function isolate$processWorkerMessage(sender, e) { |
+ var msg = e.data; |
+ switch (msg.command) { |
+ case 'start': |
+ isolate$log("starting worker: " + msg.id + " " + msg.factoryName); |
+ isolate$initializeWorker(msg.id); |
+ var runnerObject = new (isolate$globalThis[msg.factoryName])(); |
+ var serializedReplyTo = msg.replyTo; |
+ isolate$IsolateEvent.enqueue(new isolate$Isolate(), function() { |
+ var replyTo = isolate$deserializeMessage(serializedReplyTo); |
+ _IsolateJsUtil._startIsolate(runnerObject, replyTo); |
+ }); |
+ isolate$runEventLoop(); |
+ break; |
+ case 'spawn-worker': |
+ isolate$spawnWorker(msg.factoryName, msg.replyPort); |
+ break; |
+ case 'message': |
+ IsolateNatives.sendMessage( |
+ msg.workerId, msg.isolateId, msg.portId, msg.msg, msg.replyTo); |
+ isolate$runEventLoop(); |
+ break; |
+ case 'close': |
+ isolate$log("Closing Worker"); |
+ isolate$workerRegistry.unregister(sender.id); |
+ sender.terminate(); |
+ isolate$runEventLoop(); |
+ break; |
+ case 'log': |
+ isolate$log(msg.msg); |
+ break; |
+ case 'print': |
+ _IsolateJsUtil._print(msg.msg); |
+ break; |
+ case 'error': |
+ throw msg.msg; |
+ break; |
+ } |
+} |
+ |
+ |
+if (isolate$supportsWorkers) { |
+ isolate$globalThis.onmessage = function(e) { |
+ isolate$processWorkerMessage(isolate$mainWorker, e); |
+ }; |
+} |
+ |
+// ------- Default Worker ------- |
+function isolate$MainWorker() { |
+ this.id = isolate$MAIN_WORKER_ID; |
+} |
+ |
+var isolate$mainWorker = new isolate$MainWorker(); |
+isolate$mainWorker.postMessage = function(msg) { |
+ isolate$globalThis.postMessage(msg); |
+}; |
+ |
+var isolate$nextFreeIsolateId = 1; |
+ |
+// Native methods for isolate functionality. |
+/** |
+ * @constructor |
+ */ |
+function isolate$Isolate() { |
+ // The isolate ids is only unique within the current worker and frame. |
+ this.id = isolate$nextFreeIsolateId++; |
+ // When storing information on DOM nodes the isolate's id is not enough. |
+ // We instead use a token with a hashcode. The token can be stored in the |
+ // DOM node (since it is small and will not keep much data alive). |
+ this.token = new Object(); |
+ this.token.hashCode = (Math.random() * 0xFFFFFFF) >>> 0; |
+ this.receivePorts = new isolate$Registry(); |
+ this.run(function() { |
+ // The Dart-to-JavaScript compiler builds a list of functions that |
+ // need to run for each isolate to setup the state of static |
+ // variables. Run through the list and execute each function. |
+ for (var i = 0, len = isolate$inits.length; i < len; i++) { |
+ isolate$inits[i](); |
+ } |
+ }); |
+} |
+ |
+// It is allowed to stack 'run' calls. The stacked isolates can be different. |
+// That is Isolate1.run could call the DOM which then calls Isolate2.run. |
+isolate$Isolate.prototype.run = function(code) { |
+ var old = isolate$current; |
+ isolate$current = this; |
+ var result = null; |
+ try { |
+ result = code(); |
+ } finally { |
+ isolate$current = old; |
+ } |
+ return result; |
+}; |
+ |
+isolate$Isolate.prototype.registerReceivePort = function(id, port) { |
+ if (this.receivePorts.isEmpty()) { |
+ isolate$isolateRegistry.register(this.id, this); |
+ } |
+ this.receivePorts.register(id, port); |
+}; |
+ |
+isolate$Isolate.prototype.unregisterReceivePort = function(id) { |
+ this.receivePorts.unregister(id); |
+ if (this.receivePorts.isEmpty()) { |
+ isolate$isolateRegistry.unregister(this.id); |
+ } |
+}; |
+ |
+isolate$Isolate.prototype.getReceivePortForId = function(id) { |
+ return this.receivePorts.get(id); |
+}; |
+ |
+var isolate$events = []; |
+ |
+/** |
+ * @constructor |
+ */ |
+function isolate$IsolateEvent(isolate, fn) { |
+ this.isolate = isolate; |
+ this.fn = fn; |
+} |
+ |
+isolate$IsolateEvent.prototype.process = function() { |
+ this.isolate.run(this.fn); |
+}; |
+ |
+isolate$IsolateEvent.enqueue = function(isolate, fn) { |
+ isolate$events.push(new isolate$IsolateEvent(isolate, fn)); |
+}; |
+ |
+ |
+isolate$IsolateEvent.dequeue = function() { |
+ if (isolate$events.length == 0) return null; |
+ var result = isolate$events[0]; |
+ isolate$events.splice(0, 1); |
+ return result; |
+}; |
+ |
+function IsolateNatives() {} |
+ |
+IsolateNatives.sendMessage = function (workerId, isolateId, receivePortId, |
+ message, replyTo) { |
+ // Both, the message and the replyTo are already serialized. |
+ if (workerId == isolate$thisWorkerId) { |
+ var isolate = isolate$isolateRegistry.get(isolateId); |
+ if (!isolate) return; // Isolate has been closed. |
+ var receivePort = isolate.getReceivePortForId(receivePortId); |
+ if (!receivePort) return; // ReceivePort has been closed. |
+ isolate$receiveMessage(receivePort, isolate, message, replyTo); |
+ } else { |
+ var worker; |
+ if (isolate$inWorker) { |
+ worker = isolate$mainWorker; |
+ } else { |
+ worker = isolate$workerRegistry.get(workerId); |
+ } |
+ worker.postMessage({ command: 'message', |
+ workerId: workerId, |
+ isolateId: isolateId, |
+ portId: receivePortId, |
+ msg: message, |
+ replyTo: replyTo }); |
+ } |
+} |
+ |
+// Wrap a 0-arg dom-callback to bind it with the current isolate: |
+function $wrap_call$0(fn) { return fn && fn.wrap$call$0(); } |
+Function.prototype.wrap$call$0 = function() { |
+ var isolate = isolate$current; |
+ var self = this; |
+ this.wrap$0 = function() { |
+ isolate.run(function() { |
+ self(); |
+ }); |
+ isolate$runEventLoop(); |
+ }; |
+ this.wrap$call$0 = function() { return this.wrap$0; }; |
+ return this.wrap$0; |
+} |
+ |
+// Wrap a 1-arg dom-callback to bind it with the current isolate: |
+function $wrap_call$1(fn) { return fn && fn.wrap$call$1(); } |
+Function.prototype.wrap$call$1 = function() { |
+ var isolate = isolate$current; |
+ var self = this; |
+ this.wrap$1 = function(arg) { |
+ isolate.run(function() { |
+ self(arg); |
+ }); |
+ isolate$runEventLoop(); |
+ }; |
+ this.wrap$call$1 = function() { return this.wrap$1; }; |
+ return this.wrap$1; |
+} |
+ |
+IsolateNatives._spawn = function(runnable, light, replyPort) { |
+ // TODO(floitsch): throw exception if runnable's class doesn't have a |
+ // default constructor. |
+ if (isolate$useWorkers && !light) { |
+ isolate$startWorker(runnable, replyPort); |
+ } else { |
+ isolate$startNonWorker(runnable, replyPort); |
+ } |
+} |
+ |
+IsolateNatives.get$shouldSerialize = function() { |
+ return isolate$useWorkers || isolate$useWorkerSerializationProtocol; |
+} |
+ |
+IsolateNatives.registerPort = function(id, port) { |
+ isolate$current.registerReceivePort(id, port); |
+} |
+ |
+IsolateNatives.unregisterPort = function(id) { |
+ isolate$current.unregisterReceivePort(id); |
+} |
+ |
+IsolateNatives._currentWorkerId = function() { |
+ return isolate$thisWorkerId; |
+} |
+ |
+IsolateNatives._currentIsolateId = function() { |
+ return isolate$current.id; |
+} |
+ |
+function isolate$startNonWorker(runnable, replyTo) { |
+ // Spawn a new isolate and create the receive port in it. |
+ var spawned = new isolate$Isolate(); |
+ |
+ // Instead of just running the provided runnable, we create a |
+ // new cloned instance of it with a fresh state in the spawned |
+ // isolate. This way, we do not get cross-isolate references |
+ // through the runnable. |
+ var ctor = runnable.constructor; |
+ isolate$IsolateEvent.enqueue(spawned, function() { |
+ _IsolateJsUtil._startIsolate(new ctor(), replyTo); |
+ }); |
+} |
+ |
+// This field is only used by the main worker. |
+var isolate$nextFreeWorkerId = isolate$thisWorkerId + 1; |
+ |
+var isolate$thisScript = function() { |
+ if (!isolate$supportsWorkers || isolate$inWorker) return null; |
+ |
+ // TODO(5334778): Find a cross-platform non-brittle way of getting the |
+ // currently running script. |
+ var scripts = document.getElementsByTagName('script'); |
+ // The scripts variable only contains the scripts that have already been |
+ // executed. The last one is the currently running script. |
+ var script = scripts[scripts.length - 1]; |
+ var src = script.src; |
+ if (!src) { |
+ // TODO() |
+ src = "FIXME:5407062" + "_" + Math.random().toString(); |
+ script.src = src; |
+ } |
+ return src; |
+}(); |
+ |
+function isolate$startWorker(runnable, replyPort) { |
+ // TODO(sigmund): make this browser independent |
+ var factoryName = runnable.constructor.name; |
+ var serializedReplyPort = isolate$serializeMessage(replyPort); |
+ if (isolate$inWorker) { |
+ isolate$mainWorker.postMessage({ command: 'spawn-worker', |
+ factoryName: factoryName, |
+ replyPort: serializedReplyPort } ); |
+ } else { |
+ isolate$spawnWorker(factoryName, serializedReplyPort); |
+ } |
+} |
+ |
+function isolate$spawnWorker(factoryName, serializedReplyPort) { |
+ var worker = new Worker(isolate$thisScript); |
+ worker.onmessage = function(e) { |
+ isolate$processWorkerMessage(worker, e); |
+ }; |
+ var workerId = isolate$nextFreeWorkerId++; |
+ // We also store the id on the worker itself so that we can unregister it. |
+ worker.id = workerId; |
+ isolate$workerRegistry.register(workerId, worker); |
+ worker.postMessage({ command: 'start', |
+ id: workerId, |
+ replyTo: serializedReplyPort, |
+ factoryName: factoryName }); |
+} |
+ |
+function isolate$closeWorkerIfNecessary() { |
+ if (!isolate$isolateRegistry.isEmpty()) return; |
+ isolate$mainWorker.postMessage( { command: 'close' } ); |
+} |
+ |
+function isolate$doOneEventLoopIteration() { |
+ var CONTINUE_LOOP = true; |
+ var STOP_LOOP = false; |
+ var event = isolate$IsolateEvent.dequeue(); |
+ if (!event) { |
+ if (isolate$inWorker) { |
+ isolate$closeWorkerIfNecessary(); |
+ } else if (!isolate$isolateRegistry.isEmpty() && |
+ isolate$workerRegistry.isEmpty() && |
+ !isolate$supportsWorkers && (typeof(window) == 'undefined')) { |
+ // This should only trigger when running on the command-line. |
+ // We don't want this check to execute in the browser where the isolate |
+ // might still be alive due to DOM callbacks. |
+ // throw Error("Program exited with open ReceivePorts."); |
+ } |
+ return STOP_LOOP; |
+ } else { |
+ event.process(); |
+ return CONTINUE_LOOP; |
+ } |
+} |
+ |
+function isolate$doRunEventLoop() { |
+ if (typeof window != 'undefined' && window.setTimeout) { |
+ (function next() { |
+ var continueLoop = isolate$doOneEventLoopIteration(); |
+ if (!continueLoop) return; |
+ // TODO(kasperl): It might turn out to be too expensive to call |
+ // setTimeout for every single event. This needs more investigation. |
+ window.setTimeout(next, 0); |
+ })(); |
+ } else { |
+ while (true) { |
+ var continueLoop = isolate$doOneEventLoopIteration(); |
+ if (!continueLoop) break; |
+ } |
+ } |
+} |
+ |
+function isolate$runEventLoop() { |
+ if (!isolate$inWorker) { |
+ isolate$doRunEventLoop(); |
+ } else { |
+ try { |
+ isolate$doRunEventLoop(); |
+ } catch(e) { |
+ // TODO(floitsch): try to send stack-trace to the other side. |
+ isolate$mainWorker.postMessage({ command: 'error', msg: "" + e }); |
+ } |
+ } |
+} |
+ |
+function RunEntry(entry, args) { |
+ // Don't start the main loop again, if we are in a worker. |
+ if (isolate$inWorker) return; |
+ var isolate = new isolate$Isolate(); |
+ isolate$rootIsolate = isolate; |
+ isolate$IsolateEvent.enqueue(isolate, function() { |
+ entry(args); |
+ }); |
+ isolate$runEventLoop(); |
+ |
+ // BUG(5151491): This 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). |
+ isolate$current = isolate; |
+} |
+ |
+// ------- Message Serializing and Deserializing ------- |
+ |
+function isolate$serializeMessage(message) { |
+ if (isolate$useWorkers || isolate$useWorkerSerializationProtocol) { |
+ return _IsolateJsUtil._serializeObject(message); |
+ } else { |
+ return _IsolateJsUtil._copyObject(message); |
+ } |
+} |
+ |
+function isolate$deserializeMessage(message_) { |
+ if (isolate$useWorkers || isolate$useWorkerSerializationProtocol) { |
+ return _IsolateJsUtil._deserializeMessage(message_); |
+ } else { |
+ // Nothing more to do. |
+ return message_; |
+ } |
+} |
+ |
+function _IsolateJsUtil() {} |
// ********** Code for ListFactory ************** |
ListFactory = Array; |
ListFactory.prototype.is$List = function(){return this;}; |
@@ -8621,7 +9118,7 @@ WorldGenerator.prototype.run = function() { |
this.writeTypes(this.main.declaringType.get$library()); |
this._writeDynamicStubs(world.functionType); |
this._writeGlobals(); |
- this.writer.writeln(('' + mainCall.code + ';')); |
+ this.writer.writeln(('RunEntry(function () {' + mainCall.code + ';}, []);')); |
} |
WorldGenerator.prototype.globalForStaticField = function(field, fieldValue, dependencies) { |
var fullname = ("" + field.declaringType.get$jsname() + "." + field.get$jsname() + ""); |
@@ -17476,7 +17973,11 @@ Value.prototype.convertTo = function(context, toType, node, isDynamic) { |
var myCall = this.type.getCallMethod(); |
if ($notnull_bool(myCall == null || myCall.get$parameters().length != arity)) { |
var stub = world.functionType.getCallStub(Arguments.Arguments$bare$factory(arity)); |
- return new Value(toType, ('to\$' + stub.name + '(' + this.code + ')'), false, true, false); |
+ var val = new Value(toType, ('to\$' + stub.name + '(' + this.code + ')'), false, true, false); |
+ return $notnull_bool(this._isDomCallback(toType) && !$notnull_bool(this._isDomCallback(this.type))) ? val._wrapDomCallback(toType, arity) : val; |
+ } |
+ else if ($notnull_bool(this._isDomCallback(toType) && !$notnull_bool(this._isDomCallback(this.type)))) { |
+ return this._wrapDomCallback(toType, arity); |
} |
} |
if ($notnull_bool(!$notnull_bool(options.enableTypeChecks))) { |
@@ -17511,6 +18012,12 @@ Value.prototype.convertToNonNullBool = function(context, node) { |
} |
} |
} |
+Value.prototype._isDomCallback = function(toType) { |
+ return ((toType.get$definition() instanceof FunctionTypeDefinition) && $eq(toType.get$library(), world.get$dom())); |
+} |
+Value.prototype._wrapDomCallback = function(toType, arity) { |
+ return new Value(toType, ('\$wrap_call\$' + arity + '(' + this.code + ')'), false, true, false); |
+} |
Value.prototype._typeAssert = function(context, toType, node) { |
if ($notnull_bool((toType instanceof ParameterType))) { |
var p = toType; |
@@ -17590,8 +18097,8 @@ Value.prototype.invokeNoSuchMethod = function(context, name, node, args) { |
return this._tryResolveMember(context, 'noSuchMethod').invoke$4(context, node, this, new Arguments(null, noSuchArgs)); |
} |
Value.prototype.invokeSpecial = function(name, args, returnType) { |
- $assert(name.startsWith('\$'), "name.startsWith('\\$')", "value.dart", 417, 12); |
- $assert(!$notnull_bool(args.get$hasNames()), "!args.hasNames", "value.dart", 418, 12); |
+ $assert(name.startsWith('\$'), "name.startsWith('\\$')", "value.dart", 431, 12); |
+ $assert(!$notnull_bool(args.get$hasNames()), "!args.hasNames", "value.dart", 432, 12); |
var argsString = args.getCode(); |
if ($notnull_bool(name == '\$index' || name == '\$setindex')) { |
return new Value(returnType, ('' + this.code + '.' + name + '(' + argsString + ')'), false, true, false); |
@@ -18666,4 +19173,4 @@ var const$222 = ImmutableList.ImmutableList$from$factory([const$133, const$135, |
SOURCE, |
STATIC, |
TYPEDEF ]*/; |
-main(); |
+RunEntry(function () {main();}, []); |
jimhug
2011/11/08 15:39:01
500 lines is a lot to add - but considering that w
Siggi Cherem (dart-lang)
2011/11/08 17:56:42
Yeah - I'll be working this week on bringing this
|