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

Side by Side Diff: dart/third_party/pkg/js/lib/dart_interop.js

Issue 57393002: Version 0.8.10.2 (Closed) Base URL: http://dart.googlecode.com/svn/trunk/
Patch Set: Created 7 years, 1 month 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 // THIS FILE IS AUTO GENERATED. PLEASE DO NOT EDIT.
6
7 // TODO(vsm): Move this file once we determine where assets should go. See
8 // http://dartbug.com/6101.
9
10 (function() {
11 // Proxy support for js.dart.
12
13 var globalContext = window;
14
15 // Support for binding the receiver (this) in proxied functions.
16 function bindIfFunction(f, _this) {
17 if (typeof(f) != "function") {
18 return f;
19 } else {
20 return new BoundFunction(_this, f);
21 }
22 }
23
24 function unbind(obj) {
25 if (obj instanceof BoundFunction) {
26 return obj.object;
27 } else {
28 return obj;
29 }
30 }
31
32 function getBoundThis(obj) {
33 if (obj instanceof BoundFunction) {
34 return obj._this;
35 } else {
36 return globalContext;
37 }
38 }
39
40 function BoundFunction(_this, object) {
41 this._this = _this;
42 this.object = object;
43 }
44
45 // Table for local objects and functions that are proxied.
46 function ProxiedObjectTable() {
47 // Name for debugging.
48 this.name = 'js-ref';
49
50 // Table from IDs to JS objects.
51 this.map = {};
52
53 // Generator for new IDs.
54 this._nextId = 0;
55
56 // Counter for deleted proxies.
57 this._deletedCount = 0;
58
59 // Flag for one-time initialization.
60 this._initialized = false;
61
62 // Ports for managing communication to proxies.
63 this.port = new ReceivePortSync();
64 this.sendPort = this.port.toSendPort();
65
66 // Set of IDs that are global.
67 // These will not be freed on an exitScope().
68 this.globalIds = {};
69
70 // Stack of scoped handles.
71 this.handleStack = [];
72
73 // Stack of active scopes where each value is represented by the size of
74 // the handleStack at the beginning of the scope. When an active scope
75 // is popped, the handleStack is restored to where it was when the
76 // scope was entered.
77 this.scopeIndices = [];
78 }
79
80 // Number of valid IDs. This is the number of objects (global and local)
81 // kept alive by this table.
82 ProxiedObjectTable.prototype.count = function () {
83 return Object.keys(this.map).length;
84 }
85
86 // Number of total IDs ever allocated.
87 ProxiedObjectTable.prototype.total = function () {
88 return this.count() + this._deletedCount;
89 }
90
91 // Adds an object to the table and return an ID for serialization.
92 ProxiedObjectTable.prototype.add = function (obj) {
93 if (this.scopeIndices.length == 0) {
94 throw "Cannot allocate a proxy outside of a scope.";
95 }
96 // TODO(vsm): Cache refs for each obj?
97 var ref = this.name + '-' + this._nextId++;
98 this.handleStack.push(ref);
99 this.map[ref] = obj;
100 return ref;
101 }
102
103 ProxiedObjectTable.prototype._initializeOnce = function () {
104 if (!this._initialized) {
105 this._initialize();
106 this._initialized = true;
107 }
108 }
109
110 // Enters a new scope for this table.
111 ProxiedObjectTable.prototype.enterScope = function() {
112 this._initializeOnce();
113 this.scopeIndices.push(this.handleStack.length);
114 }
115
116 // Invalidates all non-global IDs in the current scope and
117 // exit the current scope.
118 ProxiedObjectTable.prototype.exitScope = function() {
119 var start = this.scopeIndices.pop();
120 for (var i = start; i < this.handleStack.length; ++i) {
121 var key = this.handleStack[i];
122 if (!this.globalIds.hasOwnProperty(key)) {
123 delete this.map[this.handleStack[i]];
124 this._deletedCount++;
125 }
126 }
127 this.handleStack = this.handleStack.splice(0, start);
128 }
129
130 // Makes this ID globally scope. It must be explicitly invalidated.
131 ProxiedObjectTable.prototype.globalize = function(id) {
132 this.globalIds[id] = true;
133 }
134
135 // Invalidates this ID, potentially freeing its corresponding object.
136 ProxiedObjectTable.prototype.invalidate = function(id) {
137 var old = this.get(id);
138 delete this.globalIds[id];
139 delete this.map[id];
140 this._deletedCount++;
141 }
142
143 // Gets the object or function corresponding to this ID.
144 ProxiedObjectTable.prototype.get = function (id) {
145 if (!this.map.hasOwnProperty(id)) {
146 throw 'Proxy ' + id + ' has been invalidated.'
147 }
148 return this.map[id];
149 }
150
151 ProxiedObjectTable.prototype._initialize = function () {
152 // Configure this table's port to forward methods, getters, and setters
153 // from the remote proxy to the local object.
154 var table = this;
155
156 this.port.receive(function (message) {
157 // TODO(vsm): Support a mechanism to register a handler here.
158 try {
159 var object = table.get(message[0]);
160 var receiver = unbind(object);
161 var member = message[1];
162 var kind = message[2];
163 var args = message[3].map(deserialize);
164 if (kind == 'get') {
165 // Getter.
166 var field = member;
167 if (field in receiver && args.length == 0) {
168 var result = bindIfFunction(receiver[field], receiver);
169 return [ 'return', serialize(result) ];
170 }
171 } else if (kind == 'set') {
172 // Setter.
173 var field = member;
174 if (args.length == 1) {
175 return [ 'return', serialize(receiver[field] = args[0]) ];
176 }
177 } else if (kind == 'apply') {
178 // Direct function invocation.
179 var _this = getBoundThis(object);
180 return [ 'return', serialize(receiver.apply(_this, args)) ];
181 } else if (member == '[]' && args.length == 1) {
182 // Index getter.
183 var result = bindIfFunction(receiver[args[0]], receiver);
184 return [ 'return', serialize(result) ];
185 } else if (member == '[]=' && args.length == 2) {
186 // Index setter.
187 return [ 'return', serialize(receiver[args[0]] = args[1]) ];
188 } else {
189 // Member function invocation.
190 var f = receiver[member];
191 if (f) {
192 var result = f.apply(receiver, args);
193 return [ 'return', serialize(result) ];
194 }
195 }
196 return [ 'none' ];
197 } catch (e) {
198 return [ 'throws', e.toString() ];
199 }
200 });
201 }
202
203 // Singleton for local proxied objects.
204 var proxiedObjectTable = new ProxiedObjectTable();
205
206 // DOM element serialization code.
207 var _localNextElementId = 0;
208 var _DART_ID = 'data-dart_id';
209 var _DART_TEMPORARY_ATTACHED = 'data-dart_temporary_attached';
210
211 function serializeElement(e) {
212 // TODO(vsm): Use an isolate-specific id.
213 var id;
214 if (e.hasAttribute(_DART_ID)) {
215 id = e.getAttribute(_DART_ID);
216 } else {
217 id = (_localNextElementId++).toString();
218 e.setAttribute(_DART_ID, id);
219 }
220 if (e !== document.documentElement) {
221 // Element must be attached to DOM to be retrieve in js part.
222 // Attach top unattached parent to avoid detaching parent of "e" when
223 // appending "e" directly to document. We keep count of elements
224 // temporarily attached to prevent detaching top unattached parent to
225 // early. This count is equals to the length of _DART_TEMPORARY_ATTACHED
226 // attribute. There could be other elements to serialize having the same
227 // top unattached parent.
228 var top = e;
229 while (true) {
230 if (top.hasAttribute(_DART_TEMPORARY_ATTACHED)) {
231 var oldValue = top.getAttribute(_DART_TEMPORARY_ATTACHED);
232 var newValue = oldValue + "a";
233 top.setAttribute(_DART_TEMPORARY_ATTACHED, newValue);
234 break;
235 }
236 if (top.parentNode == null) {
237 top.setAttribute(_DART_TEMPORARY_ATTACHED, "a");
238 document.documentElement.appendChild(top);
239 break;
240 }
241 if (top.parentNode === document.documentElement) {
242 // e was already attached to dom
243 break;
244 }
245 top = top.parentNode;
246 }
247 }
248 return id;
249 }
250
251 function deserializeElement(id) {
252 // TODO(vsm): Clear the attribute.
253 var list = document.querySelectorAll('[' + _DART_ID + '="' + id + '"]');
254
255 if (list.length > 1) throw 'Non unique ID: ' + id;
256 if (list.length == 0) {
257 throw 'Element must be attached to the document: ' + id;
258 }
259 var e = list[0];
260 if (e !== document.documentElement) {
261 // detach temporary attached element
262 var top = e;
263 while (true) {
264 if (top.hasAttribute(_DART_TEMPORARY_ATTACHED)) {
265 var oldValue = top.getAttribute(_DART_TEMPORARY_ATTACHED);
266 var newValue = oldValue.substring(1);
267 top.setAttribute(_DART_TEMPORARY_ATTACHED, newValue);
268 // detach top only if no more elements have to be unserialized
269 if (top.getAttribute(_DART_TEMPORARY_ATTACHED).length === 0) {
270 top.removeAttribute(_DART_TEMPORARY_ATTACHED);
271 document.documentElement.removeChild(top);
272 }
273 break;
274 }
275 if (top.parentNode === document.documentElement) {
276 // e was already attached to dom
277 break;
278 }
279 top = top.parentNode;
280 }
281 }
282 return e;
283 }
284
285
286 // Type for remote proxies to Dart objects.
287 function DartProxy(id, sendPort) {
288 this.id = id;
289 this.port = sendPort;
290 }
291
292 // Serializes JS types to SendPortSync format:
293 // - primitives -> primitives
294 // - sendport -> sendport
295 // - DOM element -> [ 'domref', element-id ]
296 // - Function -> [ 'funcref', function-id, sendport ]
297 // - Object -> [ 'objref', object-id, sendport ]
298 function serialize(message) {
299 if (message == null) {
300 return null; // Convert undefined to null.
301 } else if (typeof(message) == 'string' ||
302 typeof(message) == 'number' ||
303 typeof(message) == 'boolean') {
304 // Primitives are passed directly through.
305 return message;
306 } else if (message instanceof SendPortSync) {
307 // Non-proxied objects are serialized.
308 return message;
309 } else if (message instanceof Element &&
310 (message.ownerDocument == null || message.ownerDocument == document)) {
311 return [ 'domref', serializeElement(message) ];
312 } else if (message instanceof BoundFunction &&
313 typeof(message.object) == 'function') {
314 // Local function proxy.
315 return [ 'funcref',
316 proxiedObjectTable.add(message),
317 proxiedObjectTable.sendPort ];
318 } else if (typeof(message) == 'function') {
319 if ('_dart_id' in message) {
320 // Remote function proxy.
321 var remoteId = message._dart_id;
322 var remoteSendPort = message._dart_port;
323 return [ 'funcref', remoteId, remoteSendPort ];
324 } else {
325 // Local function proxy.
326 return [ 'funcref',
327 proxiedObjectTable.add(message),
328 proxiedObjectTable.sendPort ];
329 }
330 } else if (message instanceof DartProxy) {
331 // Remote object proxy.
332 return [ 'objref', message.id, message.port ];
333 } else {
334 // Local object proxy.
335 return [ 'objref',
336 proxiedObjectTable.add(message),
337 proxiedObjectTable.sendPort ];
338 }
339 }
340
341 function deserialize(message) {
342 if (message == null) {
343 return null; // Convert undefined to null.
344 } else if (typeof(message) == 'string' ||
345 typeof(message) == 'number' ||
346 typeof(message) == 'boolean') {
347 // Primitives are passed directly through.
348 return message;
349 } else if (message instanceof SendPortSync) {
350 // Serialized type.
351 return message;
352 }
353 var tag = message[0];
354 switch (tag) {
355 case 'funcref': return deserializeFunction(message);
356 case 'objref': return deserializeObject(message);
357 case 'domref': return deserializeElement(message[1]);
358 }
359 throw 'Unsupported serialized data: ' + message;
360 }
361
362 // Create a local function that forwards to the remote function.
363 function deserializeFunction(message) {
364 var id = message[1];
365 var port = message[2];
366 // TODO(vsm): Add a more robust check for a local SendPortSync.
367 if ("receivePort" in port) {
368 // Local function.
369 return unbind(proxiedObjectTable.get(id));
370 } else {
371 // Remote function. Forward to its port.
372 var f = function () {
373 var depth = enterScope();
374 try {
375 var args = Array.prototype.slice.apply(arguments);
376 args.splice(0, 0, this);
377 args = args.map(serialize);
378 var result = port.callSync([id, '#call', args]);
379 if (result[0] == 'throws') throw deserialize(result[1]);
380 return deserialize(result[1]);
381 } finally {
382 exitScope(depth);
383 }
384 };
385 // Cache the remote id and port.
386 f._dart_id = id;
387 f._dart_port = port;
388 return f;
389 }
390 }
391
392 // Creates a DartProxy to forwards to the remote object.
393 function deserializeObject(message) {
394 var id = message[1];
395 var port = message[2];
396 // TODO(vsm): Add a more robust check for a local SendPortSync.
397 if ("receivePort" in port) {
398 // Local object.
399 return proxiedObjectTable.get(id);
400 } else {
401 // Remote object.
402 return new DartProxy(id, port);
403 }
404 }
405
406 // Remote handler to construct a new JavaScript object given its
407 // serialized constructor and arguments.
408 function construct(args) {
409 args = args.map(deserialize);
410 var constructor = unbind(args[0]);
411 args = Array.prototype.slice.call(args, 1);
412
413 // Until 10 args, the 'new' operator is used. With more arguments we use a
414 // generic way that may not work, particulary when the constructor does not
415 // have an "apply" method.
416 var ret = null;
417 if (args.length === 0) {
418 ret = new constructor();
419 } else if (args.length === 1) {
420 ret = new constructor(args[0]);
421 } else if (args.length === 2) {
422 ret = new constructor(args[0], args[1]);
423 } else if (args.length === 3) {
424 ret = new constructor(args[0], args[1], args[2]);
425 } else if (args.length === 4) {
426 ret = new constructor(args[0], args[1], args[2], args[3]);
427 } else if (args.length === 5) {
428 ret = new constructor(args[0], args[1], args[2], args[3], args[4]);
429 } else if (args.length === 6) {
430 ret = new constructor(args[0], args[1], args[2], args[3], args[4],
431 args[5]);
432 } else if (args.length === 7) {
433 ret = new constructor(args[0], args[1], args[2], args[3], args[4],
434 args[5], args[6]);
435 } else if (args.length === 8) {
436 ret = new constructor(args[0], args[1], args[2], args[3], args[4],
437 args[5], args[6], args[7]);
438 } else if (args.length === 9) {
439 ret = new constructor(args[0], args[1], args[2], args[3], args[4],
440 args[5], args[6], args[7], args[8]);
441 } else if (args.length === 10) {
442 ret = new constructor(args[0], args[1], args[2], args[3], args[4],
443 args[5], args[6], args[7], args[8], args[9]);
444 } else {
445 // Dummy Type with correct constructor.
446 var Type = function(){};
447 Type.prototype = constructor.prototype;
448
449 // Create a new instance
450 var instance = new Type();
451
452 // Call the original constructor.
453 ret = constructor.apply(instance, args);
454 ret = Object(ret) === ret ? ret : instance;
455 }
456 return serialize(ret);
457 }
458
459 // Remote handler to return the top-level JavaScript context.
460 function context(data) {
461 return serialize(globalContext);
462 }
463
464 // Remote handler to track number of live / allocated proxies.
465 function proxyCount() {
466 var live = proxiedObjectTable.count();
467 var total = proxiedObjectTable.total();
468 return [live, total];
469 }
470
471 // Return true if two JavaScript proxies are equal (==).
472 function proxyEquals(args) {
473 return deserialize(args[0]) == deserialize(args[1]);
474 }
475
476 // Return true if a JavaScript proxy is instance of a given type (instanceof).
477 function proxyInstanceof(args) {
478 var obj = unbind(deserialize(args[0]));
479 var type = unbind(deserialize(args[1]));
480 return obj instanceof type;
481 }
482
483 // Return true if a JavaScript proxy has a given property.
484 function proxyHasProperty(args) {
485 var obj = unbind(deserialize(args[0]));
486 var member = unbind(deserialize(args[1]));
487 return member in obj;
488 }
489
490 // Delete a given property of object.
491 function proxyDeleteProperty(args) {
492 var obj = unbind(deserialize(args[0]));
493 var member = unbind(deserialize(args[1]));
494 delete obj[member];
495 }
496
497 function proxyConvert(args) {
498 return serialize(deserializeDataTree(args));
499 }
500
501 function deserializeDataTree(data) {
502 var type = data[0];
503 var value = data[1];
504 if (type === 'map') {
505 var obj = {};
506 for (var i = 0; i < value.length; i++) {
507 obj[value[i][0]] = deserializeDataTree(value[i][1]);
508 }
509 return obj;
510 } else if (type === 'list') {
511 var list = [];
512 for (var i = 0; i < value.length; i++) {
513 list.push(deserializeDataTree(value[i]));
514 }
515 return list;
516 } else /* 'simple' */ {
517 return deserialize(value);
518 }
519 }
520
521 function makeGlobalPort(name, f) {
522 var port = new ReceivePortSync();
523 port.receive(f);
524 window.registerPort(name, port.toSendPort());
525 }
526
527 // Enters a new scope in the JavaScript context.
528 function enterJavaScriptScope() {
529 proxiedObjectTable.enterScope();
530 }
531
532 // Enters a new scope in both the JavaScript and Dart context.
533 var _dartEnterScopePort = null;
534 function enterScope() {
535 enterJavaScriptScope();
536 if (!_dartEnterScopePort) {
537 _dartEnterScopePort = window.lookupPort('js-dart-interop-enter-scope');
538 }
539 return _dartEnterScopePort.callSync([]);
540 }
541
542 // Exits the current scope (and invalidate local IDs) in the JavaScript
543 // context.
544 function exitJavaScriptScope() {
545 proxiedObjectTable.exitScope();
546 }
547
548 // Exits the current scope in both the JavaScript and Dart context.
549 var _dartExitScopePort = null;
550 function exitScope(depth) {
551 exitJavaScriptScope();
552 if (!_dartExitScopePort) {
553 _dartExitScopePort = window.lookupPort('js-dart-interop-exit-scope');
554 }
555 return _dartExitScopePort.callSync([ depth ]);
556 }
557
558 makeGlobalPort('dart-js-interop-context', context);
559 makeGlobalPort('dart-js-interop-create', construct);
560 makeGlobalPort('dart-js-interop-proxy-count', proxyCount);
561 makeGlobalPort('dart-js-interop-equals', proxyEquals);
562 makeGlobalPort('dart-js-interop-instanceof', proxyInstanceof);
563 makeGlobalPort('dart-js-interop-has-property', proxyHasProperty);
564 makeGlobalPort('dart-js-interop-delete-property', proxyDeleteProperty);
565 makeGlobalPort('dart-js-interop-convert', proxyConvert);
566 makeGlobalPort('dart-js-interop-enter-scope', enterJavaScriptScope);
567 makeGlobalPort('dart-js-interop-exit-scope', exitJavaScriptScope);
568 makeGlobalPort('dart-js-interop-globalize', function(data) {
569 if (data[0] == "objref" || data[0] == "funcref") return proxiedObjectTable.g lobalize(data[1]);
570 throw 'Illegal type: ' + data[0];
571 });
572 makeGlobalPort('dart-js-interop-invalidate', function(data) {
573 if (data[0] == "objref" || data[0] == "funcref") return proxiedObjectTable.i nvalidate(data[1]);
574 throw 'Illegal type: ' + data[0];
575 });
576 })();
OLDNEW
« no previous file with comments | « dart/third_party/pkg/js/gh-pages-template/index.html ('k') | dart/third_party/pkg/js/lib/js.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698