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

Side by Side Diff: src/object-observe.js

Issue 183683022: Enable Object.observe by default (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 6 years, 9 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2012 the V8 project authors. All rights reserved. 1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without 2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are 3 // modification, are permitted provided that the following conditions are
4 // met: 4 // met:
5 // 5 //
6 // * Redistributions of source code must retain the above copyright 6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer. 7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above 8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following 9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided 10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution. 11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its 12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived 13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission. 14 // from this software without specific prior written permission.
15 // 15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 27
28 "use strict";
rafaelw 2014/03/05 00:48:46 it seems like all the built-in native js files are
rossberg 2014/03/06 08:41:35 That's surprising. The directive here should only
rafaelw 2014/03/07 04:33:06 You are right. I tried adding it back and got not
rossberg 2014/03/07 07:14:44 Please keep it nevertheless -- we have a midterm g
rafaelw 2014/03/08 02:48:19 Ok. Seems wrong, but I'll leave it in. On 2014/03
29
30 // Overview: 28 // Overview:
31 // 29 //
32 // This file contains all of the routing and accounting for Object.observe. 30 // This file contains all of the routing and accounting for Object.observe.
33 // User code will interact with these mechanisms via the Object.observe APIs 31 // User code will interact with these mechanisms via the Object.observe APIs
34 // and, as a side effect of mutation objects which are observed. The V8 runtime 32 // and, as a side effect of mutation objects which are observed. The V8 runtime
35 // (both C++ and JS) will interact with these mechanisms primarily by enqueuing 33 // (both C++ and JS) will interact with these mechanisms primarily by enqueuing
36 // proper change records for objects which were mutated. The Object.observe 34 // proper change records for objects which were mutated. The Object.observe
37 // routing and accounting consists primarily of three participants 35 // routing and accounting consists primarily of three participants
38 // 36 //
39 // 1) ObjectInfo. This represents the observed state of a given object. It 37 // 1) ObjectInfo. This represents the observed state of a given object. It
40 // records what callbacks are observing the object, with what options, and 38 // records what callbacks are observing the object, with what options, and
41 // what "change types" are in progress on the object (i.e. via 39 // what "change types" are in progress on the object (i.e. via
42 // notifier.performChange). 40 // notifier.performChange).
43 // 41 //
44 // 2) CallbackInfo. This represents a callback used for observation. It holds 42 // 2) CallbackInfo. This represents a callback used for observation. It holds
45 // the records which must be delivered to the callback, as well as the global 43 // the records which must be delivered to the callback, as well as the global
46 // priority of the callback (which determines delivery order between 44 // priority of the callback (which determines delivery order between
47 // callbacks). 45 // callbacks).
48 // 46 //
49 // 3) observationState.pendingObservers. This is the set of observers which 47 // 3) observationState.pendingObservers. This is the set of observers which
50 // have change records which must be delivered. During "normal" delivery 48 // have change records which must be delivered. During "normal" delivery
51 // (i.e. not Object.deliverChangeRecords), this is the mechanism by which 49 // (i.e. not Object.deliverChangeRecords), this is the mechanism by which
52 // callbacks are invoked in the proper order until there are no more 50 // callbacks are invoked in the proper order until there are no more
53 // change records pending to a callback. 51 // change records pending to a callback.
54 // 52 //
55 // Note that in order to reduce allocation and processing costs, the 53 // Note that in order to reduce allocation and processing costs, the
56 // implementation of (1) and (2) have "optimized" states which represent 54 // implementation of (1) and (2) have "optimized" states which represent
57 // common cases which can be handled more efficiently. 55 // common cases which can be handled more efficiently.
58 56
59 var observationState = %GetObservationState(); 57 var observationState;
60 if (IS_UNDEFINED(observationState.callbackInfoMap)) { 58
61 observationState.callbackInfoMap = %ObservationWeakMapCreate(); 59 function GetObservationState() {
62 observationState.objectInfoMap = %ObservationWeakMapCreate(); 60 if (IS_UNDEFINED(observationState))
63 observationState.notifierObjectInfoMap = %ObservationWeakMapCreate(); 61 observationState = %GetObservationState();
64 observationState.pendingObservers = null; 62
65 observationState.nextCallbackPriority = 0; 63 if (IS_UNDEFINED(observationState.callbackInfoMap)) {
64 observationState.callbackInfoMap = %ObservationWeakMapCreate();
65 observationState.objectInfoMap = %ObservationWeakMapCreate();
66 observationState.notifierObjectInfoMap = %ObservationWeakMapCreate();
67 observationState.pendingObservers = null;
68 observationState.nextCallbackPriority = 0;
69 }
70
71 return observationState;
66 } 72 }
67 73
68 function ObservationWeakMap(map) { 74 function GetWeakMapWrapper() {
69 this.map_ = map; 75 function MapWrapper(map) {
76 this.map_ = map;
77 };
78
79 MapWrapper.prototype = {
80 get: function(key) {
81 key = %UnwrapGlobalProxy(key);
82 if (!IS_SPEC_OBJECT(key)) return UNDEFINED;
83 return %WeakCollectionGet(this.map_, key);
84 },
85 set: function(key, value) {
86 key = %UnwrapGlobalProxy(key);
87 if (!IS_SPEC_OBJECT(key)) return UNDEFINED;
88 %WeakCollectionSet(this.map_, key, value);
89 },
90 has: function(key) {
91 return !IS_UNDEFINED(this.get(key));
92 }
93 };
94
95 return MapWrapper;
70 } 96 }
71 97
72 ObservationWeakMap.prototype = { 98 var contextMaps;
73 get: function(key) { 99
74 key = %UnwrapGlobalProxy(key); 100 function GetContextMaps() {
75 if (!IS_SPEC_OBJECT(key)) return UNDEFINED; 101 if (IS_UNDEFINED(contextMaps)) {
76 return %WeakCollectionGet(this.map_, key); 102 var map = GetWeakMapWrapper();
77 }, 103 var observationState = GetObservationState();
78 set: function(key, value) { 104 contextMaps = {
79 key = %UnwrapGlobalProxy(key); 105 callbackInfoMap: new map(observationState.callbackInfoMap),
80 if (!IS_SPEC_OBJECT(key)) return UNDEFINED; 106 objectInfoMap: new map(observationState.objectInfoMap),
81 %WeakCollectionSet(this.map_, key, value); 107 notifierObjectInfoMap: new map(observationState.notifierObjectInfoMap)
82 }, 108 };
83 has: function(key) {
84 return !IS_UNDEFINED(this.get(key));
85 } 109 }
86 };
87 110
88 var callbackInfoMap = 111 return contextMaps;
89 new ObservationWeakMap(observationState.callbackInfoMap); 112 }
90 var objectInfoMap = new ObservationWeakMap(observationState.objectInfoMap); 113
91 var notifierObjectInfoMap = 114 function GetCallbackInfoMap() {
92 new ObservationWeakMap(observationState.notifierObjectInfoMap); 115 return GetContextMaps().callbackInfoMap;
116 }
117
118 function GetObjectInfoMap() {
119 return GetContextMaps().objectInfoMap;
120 }
121
122 function GetNotifierObjectInfoMap() {
123 return GetContextMaps().notifierObjectInfoMap;
124 }
125
126 function GetPendingObservers() {
127 return GetObservationState().pendingObservers;
128 }
129
130 function SetPendingObservers(pendingObservers) {
131 GetObservationState().pendingObservers = pendingObservers;
132 }
133
134 function GetNextCallbackPriority() {
135 return GetObservationState().nextCallbackPriority++;
136 }
93 137
94 function nullProtoObject() { 138 function nullProtoObject() {
95 return { __proto__: null }; 139 return { __proto__: null };
96 } 140 }
97 141
98 function TypeMapCreate() { 142 function TypeMapCreate() {
99 return nullProtoObject(); 143 return nullProtoObject();
100 } 144 }
101 145
102 function TypeMapAddType(typeMap, type, ignoreDuplicate) { 146 function TypeMapAddType(typeMap, type, ignoreDuplicate) {
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
173 if (!%IsJSProxy(object)) 217 if (!%IsJSProxy(object))
174 %SetIsObserved(object); 218 %SetIsObserved(object);
175 219
176 objectInfo = { 220 objectInfo = {
177 object: object, 221 object: object,
178 changeObservers: null, 222 changeObservers: null,
179 notifier: null, 223 notifier: null,
180 performing: null, 224 performing: null,
181 performingCount: 0, 225 performingCount: 0,
182 }; 226 };
183 objectInfoMap.set(object, objectInfo); 227 GetObjectInfoMap().set(object, objectInfo);
184 } 228 }
185 return objectInfo; 229 return objectInfo;
186 } 230 }
187 231
188 function ObjectInfoGet(object) { 232 function ObjectInfoGet(object) {
189 return objectInfoMap.get(object); 233 return GetObjectInfoMap().get(object);
190 } 234 }
191 235
192 function ObjectInfoGetFromNotifier(notifier) { 236 function ObjectInfoGetFromNotifier(notifier) {
193 return notifierObjectInfoMap.get(notifier); 237 return GetNotifierObjectInfoMap().get(notifier);
194 } 238 }
195 239
196 function ObjectInfoGetNotifier(objectInfo) { 240 function ObjectInfoGetNotifier(objectInfo) {
197 if (IS_NULL(objectInfo.notifier)) { 241 if (IS_NULL(objectInfo.notifier)) {
198 objectInfo.notifier = { __proto__: notifierPrototype }; 242 objectInfo.notifier = { __proto__: notifierPrototype };
199 notifierObjectInfoMap.set(objectInfo.notifier, objectInfo); 243 GetNotifierObjectInfoMap().set(objectInfo.notifier, objectInfo);
200 } 244 }
201 245
202 return objectInfo.notifier; 246 return objectInfo.notifier;
203 } 247 }
204 248
205 function ObjectInfoGetObject(objectInfo) { 249 function ObjectInfoGetObject(objectInfo) {
206 return objectInfo.object; 250 return objectInfo.object;
207 } 251 }
208 252
209 function ChangeObserversIsOptimized(changeObservers) { 253 function ChangeObserversIsOptimized(changeObservers) {
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after
295 arg.length < 0) 339 arg.length < 0)
296 return false; 340 return false;
297 341
298 return true; 342 return true;
299 } 343 }
300 344
301 // CallbackInfo's optimized state is just a number which represents its global 345 // CallbackInfo's optimized state is just a number which represents its global
302 // priority. When a change record must be enqueued for the callback, it 346 // priority. When a change record must be enqueued for the callback, it
303 // normalizes. When delivery clears any pending change records, it re-optimizes. 347 // normalizes. When delivery clears any pending change records, it re-optimizes.
304 function CallbackInfoGet(callback) { 348 function CallbackInfoGet(callback) {
305 return callbackInfoMap.get(callback); 349 return GetCallbackInfoMap().get(callback);
306 } 350 }
307 351
308 function CallbackInfoGetOrCreate(callback) { 352 function CallbackInfoGetOrCreate(callback) {
309 var callbackInfo = callbackInfoMap.get(callback); 353 var callbackInfo = GetCallbackInfoMap().get(callback);
310 if (!IS_UNDEFINED(callbackInfo)) 354 if (!IS_UNDEFINED(callbackInfo))
311 return callbackInfo; 355 return callbackInfo;
312 356
313 var priority = observationState.nextCallbackPriority++ 357 var priority = GetNextCallbackPriority();
314 callbackInfoMap.set(callback, priority); 358 GetCallbackInfoMap().set(callback, priority);
315 return priority; 359 return priority;
316 } 360 }
317 361
318 function CallbackInfoGetPriority(callbackInfo) { 362 function CallbackInfoGetPriority(callbackInfo) {
319 if (IS_NUMBER(callbackInfo)) 363 if (IS_NUMBER(callbackInfo))
320 return callbackInfo; 364 return callbackInfo;
321 else 365 else
322 return callbackInfo.priority; 366 return callbackInfo.priority;
323 } 367 }
324 368
325 function CallbackInfoNormalize(callback) { 369 function CallbackInfoNormalize(callback) {
326 var callbackInfo = callbackInfoMap.get(callback); 370 var callbackInfo = GetCallbackInfoMap().get(callback);
327 if (IS_NUMBER(callbackInfo)) { 371 if (IS_NUMBER(callbackInfo)) {
328 var priority = callbackInfo; 372 var priority = callbackInfo;
329 callbackInfo = new InternalArray; 373 callbackInfo = new InternalArray;
330 callbackInfo.priority = priority; 374 callbackInfo.priority = priority;
331 callbackInfoMap.set(callback, callbackInfo); 375 GetCallbackInfoMap().set(callback, callbackInfo);
332 } 376 }
333 return callbackInfo; 377 return callbackInfo;
334 } 378 }
335 379
336 function ObjectObserve(object, callback, acceptList) { 380 function ObjectObserve(object, callback, acceptList) {
337 if (!IS_SPEC_OBJECT(object)) 381 if (!IS_SPEC_OBJECT(object))
338 throw MakeTypeError("observe_non_object", ["observe"]); 382 throw MakeTypeError("observe_non_object", ["observe"]);
339 if (!IS_SPEC_FUNCTION(callback)) 383 if (!IS_SPEC_FUNCTION(callback))
340 throw MakeTypeError("observe_non_function", ["observe"]); 384 throw MakeTypeError("observe_non_function", ["observe"]);
341 if (ObjectIsFrozen(callback)) 385 if (ObjectIsFrozen(callback))
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
383 var callback = ObserverGetCallback(observer); 427 var callback = ObserverGetCallback(observer);
384 if (needsAccessCheck && 428 if (needsAccessCheck &&
385 // Drop all splice records on the floor for access-checked objects 429 // Drop all splice records on the floor for access-checked objects
386 (changeRecord.type == 'splice' || 430 (changeRecord.type == 'splice' ||
387 !%IsAccessAllowedForObserver( 431 !%IsAccessAllowedForObserver(
388 callback, changeRecord.object, changeRecord.name))) { 432 callback, changeRecord.object, changeRecord.name))) {
389 return; 433 return;
390 } 434 }
391 435
392 var callbackInfo = CallbackInfoNormalize(callback); 436 var callbackInfo = CallbackInfoNormalize(callback);
393 if (IS_NULL(observationState.pendingObservers)) { 437 if (IS_NULL(GetPendingObservers())) {
394 observationState.pendingObservers = nullProtoObject(); 438 SetPendingObservers(nullProtoObject())
395 GetMicrotaskQueue().push(ObserveMicrotaskRunner); 439 GetMicrotaskQueue().push(ObserveMicrotaskRunner);
396 %SetMicrotaskPending(true); 440 %SetMicrotaskPending(true);
397 } 441 }
398 observationState.pendingObservers[callbackInfo.priority] = callback; 442 GetPendingObservers()[callbackInfo.priority] = callback;
399 callbackInfo.push(changeRecord); 443 callbackInfo.push(changeRecord);
400 } 444 }
401 445
402 function ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord, type) { 446 function ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord, type) {
403 if (!ObjectInfoHasActiveObservers(objectInfo)) 447 if (!ObjectInfoHasActiveObservers(objectInfo))
404 return; 448 return;
405 449
406 var hasType = !IS_UNDEFINED(type); 450 var hasType = !IS_UNDEFINED(type);
407 var newRecord = hasType ? 451 var newRecord = hasType ?
408 { object: ObjectInfoGetObject(objectInfo), type: type } : 452 { object: ObjectInfoGetObject(objectInfo), type: type } :
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
541 if (!IS_SPEC_OBJECT(object)) 585 if (!IS_SPEC_OBJECT(object))
542 throw MakeTypeError("observe_non_object", ["getNotifier"]); 586 throw MakeTypeError("observe_non_object", ["getNotifier"]);
543 587
544 if (ObjectIsFrozen(object)) return null; 588 if (ObjectIsFrozen(object)) return null;
545 589
546 var objectInfo = ObjectInfoGetOrCreate(object); 590 var objectInfo = ObjectInfoGetOrCreate(object);
547 return ObjectInfoGetNotifier(objectInfo); 591 return ObjectInfoGetNotifier(objectInfo);
548 } 592 }
549 593
550 function CallbackDeliverPending(callback) { 594 function CallbackDeliverPending(callback) {
551 var callbackInfo = callbackInfoMap.get(callback); 595 var callbackInfo = GetCallbackInfoMap().get(callback);
552 if (IS_UNDEFINED(callbackInfo) || IS_NUMBER(callbackInfo)) 596 if (IS_UNDEFINED(callbackInfo) || IS_NUMBER(callbackInfo))
553 return false; 597 return false;
554 598
555 // Clear the pending change records from callback and return it to its 599 // Clear the pending change records from callback and return it to its
556 // "optimized" state. 600 // "optimized" state.
557 var priority = callbackInfo.priority; 601 var priority = callbackInfo.priority;
558 callbackInfoMap.set(callback, priority); 602 GetCallbackInfoMap().set(callback, priority);
559 603
560 if (observationState.pendingObservers) 604 if (GetPendingObservers())
561 delete observationState.pendingObservers[priority]; 605 delete GetPendingObservers()[priority];
562 606
563 var delivered = []; 607 var delivered = [];
564 %MoveArrayContents(callbackInfo, delivered); 608 %MoveArrayContents(callbackInfo, delivered);
565 609
566 try { 610 try {
567 %_CallFunction(UNDEFINED, delivered, callback); 611 %_CallFunction(UNDEFINED, delivered, callback);
568 } catch (ex) {} // TODO(rossberg): perhaps log uncaught exceptions. 612 } catch (ex) {} // TODO(rossberg): perhaps log uncaught exceptions.
569 return true; 613 return true;
570 } 614 }
571 615
572 function ObjectDeliverChangeRecords(callback) { 616 function ObjectDeliverChangeRecords(callback) {
573 if (!IS_SPEC_FUNCTION(callback)) 617 if (!IS_SPEC_FUNCTION(callback))
574 throw MakeTypeError("observe_non_function", ["deliverChangeRecords"]); 618 throw MakeTypeError("observe_non_function", ["deliverChangeRecords"]);
575 619
576 while (CallbackDeliverPending(callback)) {} 620 while (CallbackDeliverPending(callback)) {}
577 } 621 }
578 622
579 function ObserveMicrotaskRunner() { 623 function ObserveMicrotaskRunner() {
580 var pendingObservers = observationState.pendingObservers; 624 var pendingObservers = GetPendingObservers();
581 if (pendingObservers) { 625 if (pendingObservers) {
582 observationState.pendingObservers = null; 626 SetPendingObservers(null);
583 for (var i in pendingObservers) { 627 for (var i in pendingObservers) {
584 CallbackDeliverPending(pendingObservers[i]); 628 CallbackDeliverPending(pendingObservers[i]);
585 } 629 }
586 } 630 }
587 } 631 }
588 632
589 function SetupObjectObserve() { 633 function SetupObjectObserve() {
590 %CheckIsBootstrapping(); 634 %CheckIsBootstrapping();
591 InstallFunctions($Object, DONT_ENUM, $Array( 635 InstallFunctions($Object, DONT_ENUM, $Array(
592 "deliverChangeRecords", ObjectDeliverChangeRecords, 636 "deliverChangeRecords", ObjectDeliverChangeRecords,
593 "getNotifier", ObjectGetNotifier, 637 "getNotifier", ObjectGetNotifier,
594 "observe", ObjectObserve, 638 "observe", ObjectObserve,
595 "unobserve", ObjectUnobserve 639 "unobserve", ObjectUnobserve
596 )); 640 ));
597 InstallFunctions($Array, DONT_ENUM, $Array( 641 InstallFunctions($Array, DONT_ENUM, $Array(
598 "observe", ArrayObserve, 642 "observe", ArrayObserve,
599 "unobserve", ArrayUnobserve 643 "unobserve", ArrayUnobserve
600 )); 644 ));
601 InstallFunctions(notifierPrototype, DONT_ENUM, $Array( 645 InstallFunctions(notifierPrototype, DONT_ENUM, $Array(
602 "notify", ObjectNotifierNotify, 646 "notify", ObjectNotifierNotify,
603 "performChange", ObjectNotifierPerformChange 647 "performChange", ObjectNotifierPerformChange
604 )); 648 ));
605 } 649 }
606 650
607 SetupObjectObserve(); 651 SetupObjectObserve();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698