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

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

Issue 686773002: Various cleanup/simplification in object-observe.js (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 6 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2012 the V8 project authors. All rights reserved. 1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 "use strict"; 5 "use strict";
6 6
7 // Overview: 7 // Overview:
8 // 8 //
9 // This file contains all of the routing and accounting for Object.observe. 9 // This file contains all of the routing and accounting for Object.observe.
10 // User code will interact with these mechanisms via the Object.observe APIs 10 // User code will interact with these mechanisms via the Object.observe APIs
(...skipping 15 matching lines...) Expand all
26 // 3) observationState.pendingObservers. This is the set of observers which 26 // 3) observationState.pendingObservers. This is the set of observers which
27 // have change records which must be delivered. During "normal" delivery 27 // have change records which must be delivered. During "normal" delivery
28 // (i.e. not Object.deliverChangeRecords), this is the mechanism by which 28 // (i.e. not Object.deliverChangeRecords), this is the mechanism by which
29 // callbacks are invoked in the proper order until there are no more 29 // callbacks are invoked in the proper order until there are no more
30 // change records pending to a callback. 30 // change records pending to a callback.
31 // 31 //
32 // Note that in order to reduce allocation and processing costs, the 32 // Note that in order to reduce allocation and processing costs, the
33 // implementation of (1) and (2) have "optimized" states which represent 33 // implementation of (1) and (2) have "optimized" states which represent
34 // common cases which can be handled more efficiently. 34 // common cases which can be handled more efficiently.
35 35
36 var observationState; 36 var observationState = %GetObservationState();
37 37
38 function GetObservationStateJS() { 38 // This is run during the first context creation in an isolate.
39 if (IS_UNDEFINED(observationState)) 39 if (IS_UNDEFINED(observationState.callbackInfoMap)) {
40 observationState = %GetObservationState(); 40 observationState.callbackInfoMap = %ObservationWeakMapCreate();
41 41 observationState.objectInfoMap = %ObservationWeakMapCreate();
42 if (IS_UNDEFINED(observationState.callbackInfoMap)) { 42 observationState.notifierObjectInfoMap = %ObservationWeakMapCreate();
43 observationState.callbackInfoMap = %ObservationWeakMapCreate(); 43 observationState.pendingObservers = null;
44 observationState.objectInfoMap = %ObservationWeakMapCreate(); 44 observationState.nextCallbackPriority = 0;
45 observationState.notifierObjectInfoMap = %ObservationWeakMapCreate(); 45 observationState.lastMicrotaskId = 0;
46 observationState.pendingObservers = null;
47 observationState.nextCallbackPriority = 0;
48 observationState.lastMicrotaskId = 0;
49 }
50
51 return observationState;
52 }
53
54 function GetWeakMapWrapper() {
55 function MapWrapper(map) {
56 this.map_ = map;
57 };
58
59 MapWrapper.prototype = {
60 __proto__: null,
61 get: function(key) {
62 return %WeakCollectionGet(this.map_, key);
63 },
64 set: function(key, value) {
65 %WeakCollectionSet(this.map_, key, value);
66 },
67 has: function(key) {
68 return !IS_UNDEFINED(this.get(key));
69 }
70 };
71
72 return MapWrapper;
73 }
74
75 var contextMaps;
76
77 function GetContextMaps() {
78 if (IS_UNDEFINED(contextMaps)) {
79 var map = GetWeakMapWrapper();
80 var observationState = GetObservationStateJS();
81 contextMaps = {
82 callbackInfoMap: new map(observationState.callbackInfoMap),
83 objectInfoMap: new map(observationState.objectInfoMap),
84 notifierObjectInfoMap: new map(observationState.notifierObjectInfoMap)
85 };
86 }
87
88 return contextMaps;
89 }
90
91 function GetCallbackInfoMap() {
92 return GetContextMaps().callbackInfoMap;
93 }
94
95 function GetObjectInfoMap() {
96 return GetContextMaps().objectInfoMap;
97 }
98
99 function GetNotifierObjectInfoMap() {
100 return GetContextMaps().notifierObjectInfoMap;
101 } 46 }
102 47
103 function GetPendingObservers() { 48 function GetPendingObservers() {
104 return GetObservationStateJS().pendingObservers; 49 return observationState.pendingObservers;
105 } 50 }
106 51
107 function SetPendingObservers(pendingObservers) { 52 function SetPendingObservers(pendingObservers) {
108 GetObservationStateJS().pendingObservers = pendingObservers; 53 observationState.pendingObservers = pendingObservers;
109 } 54 }
110 55
111 function GetNextCallbackPriority() { 56 function GetNextCallbackPriority() {
112 return GetObservationStateJS().nextCallbackPriority++; 57 return observationState.nextCallbackPriority++;
113 } 58 }
114 59
115 function nullProtoObject() { 60 function nullProtoObject() {
116 return { __proto__: null }; 61 return { __proto__: null };
117 } 62 }
118 63
119 function TypeMapCreate() { 64 function TypeMapCreate() {
120 return nullProtoObject(); 65 return nullProtoObject();
121 } 66 }
122 67
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
197 if (!%_IsJSProxy(object)) { 142 if (!%_IsJSProxy(object)) {
198 %SetIsObserved(object); 143 %SetIsObserved(object);
199 } 144 }
200 objectInfo = { 145 objectInfo = {
201 object: object, 146 object: object,
202 changeObservers: null, 147 changeObservers: null,
203 notifier: null, 148 notifier: null,
204 performing: null, 149 performing: null,
205 performingCount: 0, 150 performingCount: 0,
206 }; 151 };
207 GetObjectInfoMap().set(object, objectInfo); 152 %WeakCollectionSet(observationState.objectInfoMap, object, objectInfo);
208 } 153 }
209 return objectInfo; 154 return objectInfo;
210 } 155 }
211 156
212 function ObjectInfoGet(object) { 157 function ObjectInfoGet(object) {
213 return GetObjectInfoMap().get(object); 158 return %WeakCollectionGet(observationState.objectInfoMap, object);
214 } 159 }
215 160
216 function ObjectInfoGetFromNotifier(notifier) { 161 function ObjectInfoGetFromNotifier(notifier) {
217 return GetNotifierObjectInfoMap().get(notifier); 162 return %WeakCollectionGet(observationState.notifierObjectInfoMap, notifier);
218 } 163 }
219 164
220 function ObjectInfoGetNotifier(objectInfo) { 165 function ObjectInfoGetNotifier(objectInfo) {
221 if (IS_NULL(objectInfo.notifier)) { 166 if (IS_NULL(objectInfo.notifier)) {
222 objectInfo.notifier = { __proto__: notifierPrototype }; 167 objectInfo.notifier = { __proto__: notifierPrototype };
223 GetNotifierObjectInfoMap().set(objectInfo.notifier, objectInfo); 168 %WeakCollectionSet(observationState.notifierObjectInfoMap,
169 objectInfo.notifier, objectInfo);
224 } 170 }
225 171
226 return objectInfo.notifier; 172 return objectInfo.notifier;
227 } 173 }
228 174
229 function ObjectInfoGetObject(objectInfo) {
230 return objectInfo.object;
231 }
232
233 function ChangeObserversIsOptimized(changeObservers) { 175 function ChangeObserversIsOptimized(changeObservers) {
234 return typeof changeObservers === 'function' || 176 return IS_SPEC_FUNCTION(changeObservers) ||
235 typeof changeObservers.callback === 'function'; 177 IS_SPEC_FUNCTION(changeObservers.callback);
236 } 178 }
237 179
238 // The set of observers on an object is called 'changeObservers'. The first 180 // The set of observers on an object is called 'changeObservers'. The first
239 // observer is referenced directly via objectInfo.changeObservers. When a second 181 // observer is referenced directly via objectInfo.changeObservers. When a second
240 // is added, changeObservers "normalizes" to become a mapping of callback 182 // is added, changeObservers "normalizes" to become a mapping of callback
241 // priority -> observer and is then stored on objectInfo.changeObservers. 183 // priority -> observer and is then stored on objectInfo.changeObservers.
242 function ObjectInfoNormalizeChangeObservers(objectInfo) { 184 function ObjectInfoNormalizeChangeObservers(objectInfo) {
243 if (ChangeObserversIsOptimized(objectInfo.changeObservers)) { 185 if (ChangeObserversIsOptimized(objectInfo.changeObservers)) {
244 var observer = objectInfo.changeObservers; 186 var observer = objectInfo.changeObservers;
245 var callback = ObserverGetCallback(observer); 187 var callback = ObserverGetCallback(observer);
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
321 var len = ToInteger(arg.length); 263 var len = ToInteger(arg.length);
322 if (len < 0) len = 0; 264 if (len < 0) len = 0;
323 265
324 return TypeMapCreateFromList(arg, len); 266 return TypeMapCreateFromList(arg, len);
325 } 267 }
326 268
327 // CallbackInfo's optimized state is just a number which represents its global 269 // CallbackInfo's optimized state is just a number which represents its global
328 // priority. When a change record must be enqueued for the callback, it 270 // priority. When a change record must be enqueued for the callback, it
329 // normalizes. When delivery clears any pending change records, it re-optimizes. 271 // normalizes. When delivery clears any pending change records, it re-optimizes.
330 function CallbackInfoGet(callback) { 272 function CallbackInfoGet(callback) {
331 return GetCallbackInfoMap().get(callback); 273 return %WeakCollectionGet(observationState.callbackInfoMap, callback);
274 }
275
276 function CallbackInfoSet(callback, callbackInfo) {
277 %WeakCollectionSet(observationState.callbackInfoMap, callback, callbackInfo);
332 } 278 }
333 279
334 function CallbackInfoGetOrCreate(callback) { 280 function CallbackInfoGetOrCreate(callback) {
335 var callbackInfo = GetCallbackInfoMap().get(callback); 281 var callbackInfo = CallbackInfoGet(callback);
336 if (!IS_UNDEFINED(callbackInfo)) 282 if (!IS_UNDEFINED(callbackInfo))
337 return callbackInfo; 283 return callbackInfo;
338 284
339 var priority = GetNextCallbackPriority(); 285 var priority = GetNextCallbackPriority();
340 GetCallbackInfoMap().set(callback, priority); 286 CallbackInfoSet(callback, priority);
341 return priority; 287 return priority;
342 } 288 }
343 289
344 function CallbackInfoGetPriority(callbackInfo) { 290 function CallbackInfoGetPriority(callbackInfo) {
345 if (IS_NUMBER(callbackInfo)) 291 if (IS_NUMBER(callbackInfo))
346 return callbackInfo; 292 return callbackInfo;
347 else 293 else
348 return callbackInfo.priority; 294 return callbackInfo.priority;
349 } 295 }
350 296
351 function CallbackInfoNormalize(callback) { 297 function CallbackInfoNormalize(callback) {
352 var callbackInfo = GetCallbackInfoMap().get(callback); 298 var callbackInfo = CallbackInfoGet(callback);
353 if (IS_NUMBER(callbackInfo)) { 299 if (IS_NUMBER(callbackInfo)) {
354 var priority = callbackInfo; 300 var priority = callbackInfo;
355 callbackInfo = new InternalArray; 301 callbackInfo = new InternalArray;
356 callbackInfo.priority = priority; 302 callbackInfo.priority = priority;
357 GetCallbackInfoMap().set(callback, callbackInfo); 303 CallbackInfoSet(callback, callbackInfo);
358 } 304 }
359 return callbackInfo; 305 return callbackInfo;
360 } 306 }
361 307
362 function ObjectObserve(object, callback, acceptList) { 308 function ObjectObserve(object, callback, acceptList) {
363 if (!IS_SPEC_OBJECT(object)) 309 if (!IS_SPEC_OBJECT(object))
364 throw MakeTypeError("observe_non_object", ["observe"]); 310 throw MakeTypeError("observe_non_object", ["observe"]);
365 if (%IsJSGlobalProxy(object)) 311 if (%IsJSGlobalProxy(object))
366 throw MakeTypeError("observe_global_proxy", ["observe"]); 312 throw MakeTypeError("observe_global_proxy", ["observe"]);
367 if (!IS_SPEC_FUNCTION(callback)) 313 if (!IS_SPEC_FUNCTION(callback))
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
416 var callback = ObserverGetCallback(observer); 362 var callback = ObserverGetCallback(observer);
417 if (!%ObserverObjectAndRecordHaveSameOrigin(callback, changeRecord.object, 363 if (!%ObserverObjectAndRecordHaveSameOrigin(callback, changeRecord.object,
418 changeRecord)) { 364 changeRecord)) {
419 return; 365 return;
420 } 366 }
421 367
422 var callbackInfo = CallbackInfoNormalize(callback); 368 var callbackInfo = CallbackInfoNormalize(callback);
423 if (IS_NULL(GetPendingObservers())) { 369 if (IS_NULL(GetPendingObservers())) {
424 SetPendingObservers(nullProtoObject()); 370 SetPendingObservers(nullProtoObject());
425 if (DEBUG_IS_ACTIVE) { 371 if (DEBUG_IS_ACTIVE) {
426 var id = ++GetObservationStateJS().lastMicrotaskId; 372 var id = ++observationState.lastMicrotaskId;
427 var name = "Object.observe"; 373 var name = "Object.observe";
428 %EnqueueMicrotask(function() { 374 %EnqueueMicrotask(function() {
429 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); 375 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name });
430 ObserveMicrotaskRunner(); 376 ObserveMicrotaskRunner();
431 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name }); 377 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name });
432 }); 378 });
433 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); 379 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name });
434 } else { 380 } else {
435 %EnqueueMicrotask(ObserveMicrotaskRunner); 381 %EnqueueMicrotask(ObserveMicrotaskRunner);
436 } 382 }
437 } 383 }
438 GetPendingObservers()[callbackInfo.priority] = callback; 384 GetPendingObservers()[callbackInfo.priority] = callback;
439 callbackInfo.push(changeRecord); 385 callbackInfo.push(changeRecord);
440 } 386 }
441 387
442 function ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord, type) { 388 function ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord, type) {
443 if (!ObjectInfoHasActiveObservers(objectInfo)) 389 if (!ObjectInfoHasActiveObservers(objectInfo))
444 return; 390 return;
445 391
446 var hasType = !IS_UNDEFINED(type); 392 var hasType = !IS_UNDEFINED(type);
447 var newRecord = hasType ? 393 var newRecord = hasType ?
448 { object: ObjectInfoGetObject(objectInfo), type: type } : 394 { object: objectInfo.object, type: type } :
449 { object: ObjectInfoGetObject(objectInfo) }; 395 { object: objectInfo.object };
450 396
451 for (var prop in changeRecord) { 397 for (var prop in changeRecord) {
452 if (prop === 'object' || (hasType && prop === 'type')) continue; 398 if (prop === 'object' || (hasType && prop === 'type')) continue;
453 %DefineDataPropertyUnchecked( 399 %DefineDataPropertyUnchecked(
454 newRecord, prop, changeRecord[prop], READ_ONLY + DONT_DELETE); 400 newRecord, prop, changeRecord[prop], READ_ONLY + DONT_DELETE);
455 } 401 }
456 ObjectFreezeJS(newRecord); 402 ObjectFreezeJS(newRecord);
457 403
458 ObjectInfoEnqueueInternalChangeRecord(objectInfo, newRecord); 404 ObjectInfoEnqueueInternalChangeRecord(objectInfo, newRecord);
459 } 405 }
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after
587 var getNotifierFn = %GetObjectContextObjectGetNotifier(object); 533 var getNotifierFn = %GetObjectContextObjectGetNotifier(object);
588 return getNotifierFn(object); 534 return getNotifierFn(object);
589 } 535 }
590 536
591 function NativeObjectGetNotifier(object) { 537 function NativeObjectGetNotifier(object) {
592 var objectInfo = ObjectInfoGetOrCreate(object); 538 var objectInfo = ObjectInfoGetOrCreate(object);
593 return ObjectInfoGetNotifier(objectInfo); 539 return ObjectInfoGetNotifier(objectInfo);
594 } 540 }
595 541
596 function CallbackDeliverPending(callback) { 542 function CallbackDeliverPending(callback) {
597 var callbackInfo = GetCallbackInfoMap().get(callback); 543 var callbackInfo = CallbackInfoGet(callback);
598 if (IS_UNDEFINED(callbackInfo) || IS_NUMBER(callbackInfo)) 544 if (IS_UNDEFINED(callbackInfo) || IS_NUMBER(callbackInfo))
599 return false; 545 return false;
600 546
601 // Clear the pending change records from callback and return it to its 547 // Clear the pending change records from callback and return it to its
602 // "optimized" state. 548 // "optimized" state.
603 var priority = callbackInfo.priority; 549 var priority = callbackInfo.priority;
604 GetCallbackInfoMap().set(callback, priority); 550 CallbackInfoSet(callback, priority);
605 551
606 if (GetPendingObservers()) 552 var pendingObservers = GetPendingObservers();
607 delete GetPendingObservers()[priority]; 553 if (!IS_NULL(pendingObservers))
554 delete pendingObservers[priority];
608 555
609 var delivered = []; 556 var delivered = [];
610 %MoveArrayContents(callbackInfo, delivered); 557 %MoveArrayContents(callbackInfo, delivered);
611 558
612 try { 559 try {
613 %_CallFunction(UNDEFINED, delivered, callback); 560 %_CallFunction(UNDEFINED, delivered, callback);
614 } catch (ex) {} // TODO(rossberg): perhaps log uncaught exceptions. 561 } catch (ex) {} // TODO(rossberg): perhaps log uncaught exceptions.
615 return true; 562 return true;
616 } 563 }
617 564
618 function ObjectDeliverChangeRecords(callback) { 565 function ObjectDeliverChangeRecords(callback) {
619 if (!IS_SPEC_FUNCTION(callback)) 566 if (!IS_SPEC_FUNCTION(callback))
620 throw MakeTypeError("observe_non_function", ["deliverChangeRecords"]); 567 throw MakeTypeError("observe_non_function", ["deliverChangeRecords"]);
621 568
622 while (CallbackDeliverPending(callback)) {} 569 while (CallbackDeliverPending(callback)) {}
623 } 570 }
624 571
625 function ObserveMicrotaskRunner() { 572 function ObserveMicrotaskRunner() {
626 var pendingObservers = GetPendingObservers(); 573 var pendingObservers = GetPendingObservers();
627 if (pendingObservers) { 574 if (!IS_NULL(pendingObservers)) {
628 SetPendingObservers(null); 575 SetPendingObservers(null);
629 for (var i in pendingObservers) { 576 for (var i in pendingObservers) {
630 CallbackDeliverPending(pendingObservers[i]); 577 CallbackDeliverPending(pendingObservers[i]);
631 } 578 }
632 } 579 }
633 } 580 }
634 581
635 function SetupObjectObserve() { 582 function SetupObjectObserve() {
636 %CheckIsBootstrapping(); 583 %CheckIsBootstrapping();
637 InstallFunctions($Object, DONT_ENUM, $Array( 584 InstallFunctions($Object, DONT_ENUM, $Array(
638 "deliverChangeRecords", ObjectDeliverChangeRecords, 585 "deliverChangeRecords", ObjectDeliverChangeRecords,
639 "getNotifier", ObjectGetNotifier, 586 "getNotifier", ObjectGetNotifier,
640 "observe", ObjectObserve, 587 "observe", ObjectObserve,
641 "unobserve", ObjectUnobserve 588 "unobserve", ObjectUnobserve
642 )); 589 ));
643 InstallFunctions($Array, DONT_ENUM, $Array( 590 InstallFunctions($Array, DONT_ENUM, $Array(
644 "observe", ArrayObserve, 591 "observe", ArrayObserve,
645 "unobserve", ArrayUnobserve 592 "unobserve", ArrayUnobserve
646 )); 593 ));
647 InstallFunctions(notifierPrototype, DONT_ENUM, $Array( 594 InstallFunctions(notifierPrototype, DONT_ENUM, $Array(
648 "notify", ObjectNotifierNotify, 595 "notify", ObjectNotifierNotify,
649 "performChange", ObjectNotifierPerformChange 596 "performChange", ObjectNotifierPerformChange
650 )); 597 ));
651 } 598 }
652 599
653 SetupObjectObserve(); 600 SetupObjectObserve();
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698