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

Unified Diff: sdk/lib/js/dartium/js_dartium.dart

Issue 27514003: A few API changes: (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 2 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/js/dartium/js_dartium.dart
diff --git a/sdk/lib/js/dartium/js_dartium.dart b/sdk/lib/js/dartium/js_dartium.dart
index 234a4bf3bff3f352481c7667dd5b22612853a160..8cc3928b9e6f41eefc48d78b8c9bdd5b5c07d36c 100644
--- a/sdk/lib/js/dartium/js_dartium.dart
+++ b/sdk/lib/js/dartium/js_dartium.dart
@@ -2,419 +2,114 @@
// 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.
-/**
- * The js.dart library provides simple JavaScript invocation from Dart that
- * works on both Dartium and on other modern browsers via Dart2JS.
- *
- * It provides a model based on scoped [JsObject] objects. Proxies give Dart
- * code access to JavaScript objects, fields, and functions as well as the
- * ability to pass Dart objects and functions to JavaScript functions. Scopes
- * enable developers to use proxies without memory leaks - a common challenge
- * with cross-runtime interoperation.
- *
- * The top-level [context] getter provides a [JsObject] to the global JavaScript
- * context for the page your Dart code is running on. In the following example:
- *
- * import 'dart:js';
- *
- * void main() {
- * context.callMethod('alert', ['Hello from Dart via JavaScript']);
- * }
- *
- * context['alert'] creates a proxy to the top-level alert function in
- * JavaScript. It is invoked from Dart as a regular function that forwards to
- * the underlying JavaScript one. By default, proxies are released when
- * the currently executing event completes, e.g., when main is completes
- * in this example.
- *
- * The library also enables JavaScript proxies to Dart objects and functions.
- * For example, the following Dart code:
- *
- * context['dartCallback'] = new Callback.once((x) => print(x*2));
- *
- * defines a top-level JavaScript function 'dartCallback' that is a proxy to
- * the corresponding Dart function. The [Callback.once] constructor allows the
- * proxy to the Dart function to be retained across multiple events;
- * instead it is released after the first invocation. (This is a common
- * pattern for asychronous callbacks.)
- *
- * Note, parameters and return values are intuitively passed by value for
- * primitives and by reference for non-primitives. In the latter case, the
- * references are automatically wrapped and unwrapped as proxies by the library.
- *
- * This library also allows construction of JavaScripts objects given a
- * [JsObject] to a corresponding JavaScript constructor. For example, if the
- * following JavaScript is loaded on the page:
- *
- * function Foo(x) {
- * this.x = x;
- * }
- *
- * Foo.prototype.add = function(other) {
- * return new Foo(this.x + other.x);
- * }
- *
- * then, the following Dart:
- *
- * var foo = new JsObject(context['Foo'], [42]);
- * var foo2 = foo.callMethod('add', [foo]);
- * print(foo2['x']);
- *
- * will construct a JavaScript Foo object with the parameter 42, invoke its
- * add method, and return a [JsObject] to a new Foo object whose x field is 84.
- */
-
library dart.js;
-import 'dart:collection' show HashMap;
-import 'dart:html';
-import 'dart:isolate';
-
-// Global ports to manage communication from Dart to JS.
+import 'dart:nativewrappers';
-SendPortSync _jsPortSync = window.lookupPort('dart-js-context');
-SendPortSync _jsPortCreate = window.lookupPort('dart-js-create');
-SendPortSync _jsPortInstanceof = window.lookupPort('dart-js-instanceof');
-SendPortSync _jsPortDeleteProperty = window.lookupPort('dart-js-delete-property');
-SendPortSync _jsPortConvert = window.lookupPort('dart-js-convert');
+JsObject _cachedContext;
-final String _objectIdPrefix = 'dart-obj-ref';
-final String _functionIdPrefix = 'dart-fun-ref';
-final _objectTable = new _ObjectTable();
-final _functionTable = new _ObjectTable.forFunctions();
+JsObject get _context native "Js_context_Callback";
-// Port to handle and forward requests to the underlying Dart objects.
-// A remote proxy is uniquely identified by an ID and SendPortSync.
-ReceivePortSync _port = new ReceivePortSync()
- ..receive((msg) {
- try {
- var id = msg[0];
- var method = msg[1];
- if (method == '#call') {
- var receiver = _getObjectTable(id).get(id);
- var result;
- if (receiver is Function) {
- // remove the first argument, which is 'this', but never
- // used for a raw function
- var args = msg[2].sublist(1).map(_deserialize).toList();
- result = Function.apply(receiver, args);
- } else if (receiver is Callback) {
- var args = msg[2].map(_deserialize).toList();
- result = receiver._call(args);
- } else {
- throw new StateError('bad function type: $receiver');
- }
- return ['return', _serialize(result)];
- } else {
- // TODO(vsm): Support a mechanism to register a handler here.
- throw 'Invocation unsupported on non-function Dart proxies';
- }
- } catch (e) {
- // TODO(vsm): callSync should just handle exceptions itself.
- return ['throws', '$e'];
- }
- });
-
-_ObjectTable _getObjectTable(String id) {
- if (id.startsWith(_functionIdPrefix)) return _functionTable;
- if (id.startsWith(_objectIdPrefix)) return _objectTable;
- throw new ArgumentError('internal error: invalid object id: $id');
-}
-
-JsObject _context;
-
-/**
- * Returns a proxy to the global JavaScript context for this page.
- */
JsObject get context {
- if (_context == null) {
- var port = _jsPortSync;
- if (port == null) {
- return null;
- }
- _context = _deserialize(_jsPortSync.callSync([]));
+ if (_cachedContext == null) {
+ _cachedContext = _context;
}
- return _context;
-}
-
-/**
- * Converts a json-like [data] to a JavaScript map or array and return a
- * [JsObject] to it.
- */
-JsObject jsify(dynamic data) => data == null ? null : new JsObject._json(data);
-
-/**
- * Converts a local Dart function to a callback that can be passed to
- * JavaScript.
- */
-class Callback implements Serializable<JsFunction> {
- final bool _withThis;
- final Function _function;
- JsFunction _jsFunction;
-
- Callback._(this._function, this._withThis) {
- var id = _functionTable.add(this);
- _jsFunction = new JsFunction._internal(_port.toSendPort(), id);
- }
-
- factory Callback(Function f) => new Callback._(f, false);
- factory Callback.withThis(Function f) => new Callback._(f, true);
-
- dynamic _call(List args) {
- var arguments = (_withThis) ? args : args.sublist(1);
- return Function.apply(_function, arguments);
- }
-
- JsFunction toJs() => _jsFunction;
+ return _cachedContext;
}
-/**
- * Proxies to JavaScript objects.
- */
-class JsObject implements Serializable<JsObject> {
- final SendPortSync _port;
- final String _id;
-
- /**
- * Constructs a [JsObject] to a new JavaScript object by invoking a (proxy to
- * a) JavaScript [constructor]. The [arguments] list should contain either
- * primitive values, DOM elements, or Proxies.
- */
- factory JsObject(Serializable<JsFunction> constructor, [List arguments]) {
- final params = [constructor];
- if (arguments != null) params.addAll(arguments);
- final serialized = params.map(_serialize).toList();
- final result = _jsPortCreate.callSync(serialized);
- return _deserialize(result);
+class JsObject extends NativeFieldWrapperClass2 {
+ JsObject.internal();
+
+ factory JsObject(JsFunction constructor, [List arguments]) => _create(constructor, arguments);
+
+ static JsObject _create(JsFunction constructor, arguments) native "JsObject_constructorCallback";
+
+ /**
+ * Expert users only:
+ * Use this constructor only if you want to gain access to JS expandos
+ * attached to a browser native object such as a Node.
+ * Not all native browser objects can be converted using fromBrowserObject.
+ * Currently the following types are supported:
+ * * Node
+ * * ArrayBuffer
+ * * Blob
+ * * ImageData
+ * * IDBKeyRange
+ * TODO(jacobr): support Event, Window and NodeList as well.
+ */
+ factory JsObject.fromBrowserObject(var object) {
+ if (object is num || object is String || object is bool || object == null) {
+ throw new ArgumentError(
+ "object cannot be a num, string, bool, or null");
+ }
+ return _fromBrowserObject(object);
}
/**
- * Constructs a [JsObject] to a new JavaScript map or list created defined via
- * Dart map or list.
+ * Converts a json-like [object] to a JavaScript map or array and return a
+ * [JsObject] to it.
*/
- factory JsObject._json(data) => _convert(data);
-
- static _convert(data) =>
- _deserialize(_jsPortConvert.callSync(_serializeDataTree(data)));
-
- static _serializeDataTree(data) {
- if (data is Map) {
- final entries = new List();
- for (var key in data.keys) {
- entries.add([key, _serializeDataTree(data[key])]);
- }
- return ['map', entries];
- } else if (data is Iterable) {
- return ['list', data.map(_serializeDataTree).toList()];
- } else {
- return ['simple', _serialize(data)];
+ factory JsObject.jsify(object) {
+ if ((object is! Map) && (object is! Iterable)) {
+ throw new ArgumentError("object must be a Map or Iterable");
}
+ return _jsify(object);
}
- JsObject._internal(this._port, this._id);
+ static JSObject _jsify(object) native "JsObject_jsify";
- JsObject toJs() => this;
+ static JsObject _fromBrowserObject(object) native "JsObject_fromBrowserObject";
- // Resolve whether this is needed.
- operator[](arg) => _forward(this, '[]', 'method', [ arg ]);
+ operator[](key) native "JsObject_[]";
+ operator[]=(key, value) native "JsObject_[]=";
- // Resolve whether this is needed.
- operator[]=(key, value) => _forward(this, '[]=', 'method', [ key, value ]);
+ int get hashCode native "JsObject_hashCode";
- int get hashCode => _id.hashCode;
+ operator==(other) => other is JsObject && _identityEquality(this, other);
- // Test if this is equivalent to another Proxy. This essentially
- // maps to JavaScript's === operator.
- operator==(other) => other is JsObject && this._id == other._id;
+ static bool _identityEquality(JsObject a, JsObject b) native "JsObject_identityEquality";
- /**
- * Check if this [JsObject] has a [name] property.
- */
- bool hasProperty(String name) => _forward(this, name, 'hasProperty', []);
+ bool hasProperty(String property) native "JsObject_hasProperty";
alexandre.ardhuin 2013/10/20 07:17:00 `property` should be dynamic like in js_dart2js.da
- /**
- * Delete the [name] property.
- */
- void deleteProperty(String name) {
- _jsPortDeleteProperty.callSync([this, name].map(_serialize).toList());
- }
+ void deleteProperty(JsFunction name) native "JsObject_deleteProperty";
alexandre.ardhuin 2013/10/20 07:17:00 `name` should be dynamic like in js_dart2js.dart
- /**
- * Check if this [JsObject] is instance of [type].
- */
- bool instanceof(Serializable<JsFunction> type) =>
- _jsPortInstanceof.callSync([this, type].map(_serialize).toList());
+ bool instanceof(JsFunction type) native "JsObject_instanceof";
String toString() {
try {
- return _forward(this, 'toString', 'method', []);
+ return _toString();
} catch(e) {
return super.toString();
}
}
- callMethod(String name, [List args]) {
- return _forward(this, name, 'method', args != null ? args : []);
- }
+ String _toString() native "JsObject_toString";
- // Forward member accesses to the backing JavaScript object.
- static _forward(JsObject receiver, String member, String kind, List args) {
- var result = receiver._port.callSync([receiver._id, member, kind,
- args.map(_serialize).toList()]);
- switch (result[0]) {
- case 'return': return _deserialize(result[1]);
- case 'throws': throw _deserialize(result[1]);
- case 'none':
- throw new NoSuchMethodError(receiver, new Symbol(member), args, {});
- default: throw 'Invalid return value';
+ callMethod(String name, [List args]) {
+ try {
+ return _callMethod(name, args);
+ } catch(e) {
+ if (hasProperty(name)) {
+ rethrow;
+ } else {
+ throw new NoSuchMethodError(this, new Symbol(name), args, null);
+ }
}
}
-}
-
-/// A [JsObject] subtype to JavaScript functions.
-class JsFunction extends JsObject implements Serializable<JsFunction> {
- JsFunction._internal(SendPortSync port, String id)
- : super._internal(port, id);
-
- apply(thisArg, [List args]) {
- return JsObject._forward(this, '', 'apply',
- [thisArg]..addAll(args == null ? [] : args));
- }
-}
-/// Marker class used to indicate it is serializable to js. If a class is a
-/// [Serializable] the "toJs" method will be called and the result will be used
-/// as value.
-abstract class Serializable<T> {
- T toJs();
+ _callMethod(String name, List args) native "JsObject_callMethod";
}
-class _ObjectTable {
- final String name;
- final Map<String, Object> objects;
- final Map<Object, String> ids;
- int nextId = 0;
+class JsFunction extends JsObject {
+ JsFunction.internal();
- // Creates a table that uses an identity Map to store IDs
- _ObjectTable()
- : name = _objectIdPrefix,
- objects = new HashMap<String, Object>(),
- ids = new HashMap<Object, String>.identity();
-
- // Creates a table that uses an equality-based Map to store IDs, since
- // closurized methods may be equal, but not identical
- _ObjectTable.forFunctions()
- : name = _functionIdPrefix,
- objects = new HashMap<String, Object>(),
- ids = new HashMap<Object, String>();
-
- // Adds a new object to the table. If [id] is not given, a new unique ID is
- // generated. Returns the ID.
- String add(Object o, {String id}) {
- // TODO(vsm): Cache x and reuse id.
- if (id == null) id = ids[o];
- if (id == null) id = '$name-${nextId++}';
- ids[o] = id;
- objects[id] = o;
- return id;
- }
-
- // Gets an object by ID.
- Object get(String id) => objects[id];
-
- bool contains(String id) => objects.containsKey(id);
-
- String getId(Object o) => ids[o];
-
- // Gets the current number of objects kept alive by this table.
- get count => objects.length;
-}
-
-// Dart serialization support.
-
-_serialize(var message) {
- if (message == null) {
- return null; // Convert undefined to null.
- } else if (message is String ||
- message is num ||
- message is bool) {
- // Primitives are passed directly through.
- return message;
- } else if (message is SendPortSync) {
- // Non-proxied objects are serialized.
- return message;
- } else if (message is JsFunction) {
- // Remote function proxy.
- return ['funcref', message._id, message._port];
- } else if (message is JsObject) {
- // Remote object proxy.
- return ['objref', message._id, message._port];
- } else if (message is Serializable) {
- // use of result of toJs()
- return _serialize(message.toJs());
- } else if (message is Function) {
- var id = _functionTable.getId(message);
- if (id != null) {
- return ['funcref', id, _port.toSendPort()];
- }
- id = _functionTable.add(message);
- return ['funcref', id, _port.toSendPort()];
- } else {
- // Local object proxy.
- return ['objref', _objectTable.add(message), _port.toSendPort()];
- }
-}
-
-_deserialize(var message) {
- deserializeFunction(message) {
- var id = message[1];
- var port = message[2];
- if (port == _port.toSendPort()) {
- // Local function.
- return _functionTable.get(id);
- } else {
- // Remote function.
- var jsFunction = _functionTable.get(id);
- if (jsFunction == null) {
- jsFunction = new JsFunction._internal(port, id);
- _functionTable.add(jsFunction, id: id);
- }
- return jsFunction;
- }
- }
+ /**
+ * Returns a [JsFunction] that captures its 'this' binding and calls [f]
+ * with the value of this passed as the first argument.
+ */
+ factory JsFunction.withThis(Function f) => _withThis(f);
- deserializeObject(message) {
- var id = message[1];
- var port = message[2];
- if (port == _port.toSendPort()) {
- // Local object.
- return _objectTable.get(id);
- } else {
- // Remote object.
- var jsObject = _objectTable.get(id);
- if (jsObject == null) {
- jsObject = new JsObject._internal(port, id);
- _objectTable.add(jsObject, id: id);
- }
- return jsObject;
- }
- }
+ apply(List args, {thisArg}) native "JsFunction_apply";
- if (message == null) {
- return null; // Convert undefined to null.
- } else if (message is String ||
- message is num ||
- message is bool) {
- // Primitives are passed directly through.
- return message;
- } else if (message is SendPortSync) {
- // Serialized type.
- return message;
- }
- var tag = message[0];
- switch (tag) {
- case 'funcref': return deserializeFunction(message);
- case 'objref': return deserializeObject(message);
- }
- throw 'Unsupported serialized data: $message';
+ static JsFunction _withThis(Function f) native "JsFunction_withThis";
}

Powered by Google App Engine
This is Rietveld 408576698