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

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

Issue 1086813005: Wrap object observe implementation in a function. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Created 5 years, 8 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
« no previous file with comments | « src/bootstrapper.cc ('k') | src/v8natives.js » ('j') | 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 var $observeNotifyChange;
6 var $observeEnqueueSpliceRecord;
7 var $observeBeginPerformSplice;
8 var $observeEndPerformSplice;
9 var $observeNativeObjectObserve;
10 var $observeNativeObjectGetNotifier;
11 var $observeNativeObjectNotifierPerformChange;
12
13 (function() {
14
5 "use strict"; 15 "use strict";
6 16
17 %CheckIsBootstrapping();
18
19 var GlobalArray = global.Array;
20 var GlobalObject = global.Object;
21
22 // -------------------------------------------------------------------
23
7 // Overview: 24 // Overview:
8 // 25 //
9 // This file contains all of the routing and accounting for Object.observe. 26 // 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 27 // User code will interact with these mechanisms via the Object.observe APIs
11 // and, as a side effect of mutation objects which are observed. The V8 runtime 28 // and, as a side effect of mutation objects which are observed. The V8 runtime
12 // (both C++ and JS) will interact with these mechanisms primarily by enqueuing 29 // (both C++ and JS) will interact with these mechanisms primarily by enqueuing
13 // proper change records for objects which were mutated. The Object.observe 30 // proper change records for objects which were mutated. The Object.observe
14 // routing and accounting consists primarily of three participants 31 // routing and accounting consists primarily of three participants
15 // 32 //
16 // 1) ObjectInfo. This represents the observed state of a given object. It 33 // 1) ObjectInfo. This represents the observed state of a given object. It
(...skipping 11 matching lines...) Expand all
28 // (i.e. not Object.deliverChangeRecords), this is the mechanism by which 45 // (i.e. not Object.deliverChangeRecords), this is the mechanism by which
29 // callbacks are invoked in the proper order until there are no more 46 // callbacks are invoked in the proper order until there are no more
30 // change records pending to a callback. 47 // change records pending to a callback.
31 // 48 //
32 // Note that in order to reduce allocation and processing costs, the 49 // Note that in order to reduce allocation and processing costs, the
33 // implementation of (1) and (2) have "optimized" states which represent 50 // implementation of (1) and (2) have "optimized" states which represent
34 // common cases which can be handled more efficiently. 51 // common cases which can be handled more efficiently.
35 52
36 var observationState; 53 var observationState;
37 54
55 var notifierPrototype = {};
56
38 // We have to wait until after bootstrapping to grab a reference to the 57 // We have to wait until after bootstrapping to grab a reference to the
39 // observationState object, since it's not possible to serialize that 58 // observationState object, since it's not possible to serialize that
40 // reference into the snapshot. 59 // reference into the snapshot.
41 function GetObservationStateJS() { 60 function GetObservationStateJS() {
42 if (IS_UNDEFINED(observationState)) { 61 if (IS_UNDEFINED(observationState)) {
43 observationState = %GetObservationState(); 62 observationState = %GetObservationState();
44 } 63 }
45 64
46 // TODO(adamk): Consider moving this code into heap.cc 65 // TODO(adamk): Consider moving this code into heap.cc
47 if (IS_UNDEFINED(observationState.callbackInfoMap)) { 66 if (IS_UNDEFINED(observationState.callbackInfoMap)) {
48 observationState.callbackInfoMap = %ObservationWeakMapCreate(); 67 observationState.callbackInfoMap = %ObservationWeakMapCreate();
49 observationState.objectInfoMap = %ObservationWeakMapCreate(); 68 observationState.objectInfoMap = %ObservationWeakMapCreate();
50 observationState.notifierObjectInfoMap = %ObservationWeakMapCreate(); 69 observationState.notifierObjectInfoMap = %ObservationWeakMapCreate();
51 observationState.pendingObservers = null; 70 observationState.pendingObservers = null;
52 observationState.nextCallbackPriority = 0; 71 observationState.nextCallbackPriority = 0;
53 observationState.lastMicrotaskId = 0; 72 observationState.lastMicrotaskId = 0;
54 } 73 }
55 74
56 return observationState; 75 return observationState;
57 } 76 }
58 77
78
59 function GetPendingObservers() { 79 function GetPendingObservers() {
60 return GetObservationStateJS().pendingObservers; 80 return GetObservationStateJS().pendingObservers;
61 } 81 }
62 82
83
63 function SetPendingObservers(pendingObservers) { 84 function SetPendingObservers(pendingObservers) {
64 GetObservationStateJS().pendingObservers = pendingObservers; 85 GetObservationStateJS().pendingObservers = pendingObservers;
65 } 86 }
66 87
88
67 function GetNextCallbackPriority() { 89 function GetNextCallbackPriority() {
68 return GetObservationStateJS().nextCallbackPriority++; 90 return GetObservationStateJS().nextCallbackPriority++;
69 } 91 }
70 92
93
71 function nullProtoObject() { 94 function nullProtoObject() {
72 return { __proto__: null }; 95 return { __proto__: null };
73 } 96 }
74 97
98
75 function TypeMapCreate() { 99 function TypeMapCreate() {
76 return nullProtoObject(); 100 return nullProtoObject();
77 } 101 }
78 102
103
79 function TypeMapAddType(typeMap, type, ignoreDuplicate) { 104 function TypeMapAddType(typeMap, type, ignoreDuplicate) {
80 typeMap[type] = ignoreDuplicate ? 1 : (typeMap[type] || 0) + 1; 105 typeMap[type] = ignoreDuplicate ? 1 : (typeMap[type] || 0) + 1;
81 } 106 }
82 107
108
83 function TypeMapRemoveType(typeMap, type) { 109 function TypeMapRemoveType(typeMap, type) {
84 typeMap[type]--; 110 typeMap[type]--;
85 } 111 }
86 112
113
87 function TypeMapCreateFromList(typeList, length) { 114 function TypeMapCreateFromList(typeList, length) {
88 var typeMap = TypeMapCreate(); 115 var typeMap = TypeMapCreate();
89 for (var i = 0; i < length; i++) { 116 for (var i = 0; i < length; i++) {
90 TypeMapAddType(typeMap, typeList[i], true); 117 TypeMapAddType(typeMap, typeList[i], true);
91 } 118 }
92 return typeMap; 119 return typeMap;
93 } 120 }
94 121
122
95 function TypeMapHasType(typeMap, type) { 123 function TypeMapHasType(typeMap, type) {
96 return !!typeMap[type]; 124 return !!typeMap[type];
97 } 125 }
98 126
127
99 function TypeMapIsDisjointFrom(typeMap1, typeMap2) { 128 function TypeMapIsDisjointFrom(typeMap1, typeMap2) {
100 if (!typeMap1 || !typeMap2) 129 if (!typeMap1 || !typeMap2)
101 return true; 130 return true;
102 131
103 for (var type in typeMap1) { 132 for (var type in typeMap1) {
104 if (TypeMapHasType(typeMap1, type) && TypeMapHasType(typeMap2, type)) 133 if (TypeMapHasType(typeMap1, type) && TypeMapHasType(typeMap2, type))
105 return false; 134 return false;
106 } 135 }
107 136
108 return true; 137 return true;
109 } 138 }
110 139
140
111 var defaultAcceptTypes = (function() { 141 var defaultAcceptTypes = (function() {
112 var defaultTypes = [ 142 var defaultTypes = [
113 'add', 143 'add',
114 'update', 144 'update',
115 'delete', 145 'delete',
116 'setPrototype', 146 'setPrototype',
117 'reconfigure', 147 'reconfigure',
118 'preventExtensions' 148 'preventExtensions'
119 ]; 149 ];
120 return TypeMapCreateFromList(defaultTypes, defaultTypes.length); 150 return TypeMapCreateFromList(defaultTypes, defaultTypes.length);
121 })(); 151 })();
122 152
153
123 // An Observer is a registration to observe an object by a callback with 154 // An Observer is a registration to observe an object by a callback with
124 // a given set of accept types. If the set of accept types is the default 155 // a given set of accept types. If the set of accept types is the default
125 // set for Object.observe, the observer is represented as a direct reference 156 // set for Object.observe, the observer is represented as a direct reference
126 // to the callback. An observer never changes its accept types and thus never 157 // to the callback. An observer never changes its accept types and thus never
127 // needs to "normalize". 158 // needs to "normalize".
128 function ObserverCreate(callback, acceptList) { 159 function ObserverCreate(callback, acceptList) {
129 if (IS_UNDEFINED(acceptList)) 160 if (IS_UNDEFINED(acceptList))
130 return callback; 161 return callback;
131 var observer = nullProtoObject(); 162 var observer = nullProtoObject();
132 observer.callback = callback; 163 observer.callback = callback;
133 observer.accept = acceptList; 164 observer.accept = acceptList;
134 return observer; 165 return observer;
135 } 166 }
136 167
168
137 function ObserverGetCallback(observer) { 169 function ObserverGetCallback(observer) {
138 return IS_SPEC_FUNCTION(observer) ? observer : observer.callback; 170 return IS_SPEC_FUNCTION(observer) ? observer : observer.callback;
139 } 171 }
140 172
173
141 function ObserverGetAcceptTypes(observer) { 174 function ObserverGetAcceptTypes(observer) {
142 return IS_SPEC_FUNCTION(observer) ? defaultAcceptTypes : observer.accept; 175 return IS_SPEC_FUNCTION(observer) ? defaultAcceptTypes : observer.accept;
143 } 176 }
144 177
178
145 function ObserverIsActive(observer, objectInfo) { 179 function ObserverIsActive(observer, objectInfo) {
146 return TypeMapIsDisjointFrom(ObjectInfoGetPerformingTypes(objectInfo), 180 return TypeMapIsDisjointFrom(ObjectInfoGetPerformingTypes(objectInfo),
147 ObserverGetAcceptTypes(observer)); 181 ObserverGetAcceptTypes(observer));
148 } 182 }
149 183
184
150 function ObjectInfoGetOrCreate(object) { 185 function ObjectInfoGetOrCreate(object) {
151 var objectInfo = ObjectInfoGet(object); 186 var objectInfo = ObjectInfoGet(object);
152 if (IS_UNDEFINED(objectInfo)) { 187 if (IS_UNDEFINED(objectInfo)) {
153 if (!%_IsJSProxy(object)) { 188 if (!%_IsJSProxy(object)) {
154 %SetIsObserved(object); 189 %SetIsObserved(object);
155 } 190 }
156 objectInfo = { 191 objectInfo = {
157 object: object, 192 object: object,
158 changeObservers: null, 193 changeObservers: null,
159 notifier: null, 194 notifier: null,
160 performing: null, 195 performing: null,
161 performingCount: 0, 196 performingCount: 0,
162 }; 197 };
163 %WeakCollectionSet(GetObservationStateJS().objectInfoMap, 198 %WeakCollectionSet(GetObservationStateJS().objectInfoMap,
164 object, objectInfo); 199 object, objectInfo);
165 } 200 }
166 return objectInfo; 201 return objectInfo;
167 } 202 }
168 203
204
169 function ObjectInfoGet(object) { 205 function ObjectInfoGet(object) {
170 return %WeakCollectionGet(GetObservationStateJS().objectInfoMap, object); 206 return %WeakCollectionGet(GetObservationStateJS().objectInfoMap, object);
171 } 207 }
172 208
209
173 function ObjectInfoGetFromNotifier(notifier) { 210 function ObjectInfoGetFromNotifier(notifier) {
174 return %WeakCollectionGet(GetObservationStateJS().notifierObjectInfoMap, 211 return %WeakCollectionGet(GetObservationStateJS().notifierObjectInfoMap,
175 notifier); 212 notifier);
176 } 213 }
177 214
215
178 function ObjectInfoGetNotifier(objectInfo) { 216 function ObjectInfoGetNotifier(objectInfo) {
179 if (IS_NULL(objectInfo.notifier)) { 217 if (IS_NULL(objectInfo.notifier)) {
180 objectInfo.notifier = { __proto__: notifierPrototype }; 218 objectInfo.notifier = { __proto__: notifierPrototype };
181 %WeakCollectionSet(GetObservationStateJS().notifierObjectInfoMap, 219 %WeakCollectionSet(GetObservationStateJS().notifierObjectInfoMap,
182 objectInfo.notifier, objectInfo); 220 objectInfo.notifier, objectInfo);
183 } 221 }
184 222
185 return objectInfo.notifier; 223 return objectInfo.notifier;
186 } 224 }
187 225
226
188 function ChangeObserversIsOptimized(changeObservers) { 227 function ChangeObserversIsOptimized(changeObservers) {
189 return IS_SPEC_FUNCTION(changeObservers) || 228 return IS_SPEC_FUNCTION(changeObservers) ||
190 IS_SPEC_FUNCTION(changeObservers.callback); 229 IS_SPEC_FUNCTION(changeObservers.callback);
191 } 230 }
192 231
232
193 // The set of observers on an object is called 'changeObservers'. The first 233 // The set of observers on an object is called 'changeObservers'. The first
194 // observer is referenced directly via objectInfo.changeObservers. When a second 234 // observer is referenced directly via objectInfo.changeObservers. When a second
195 // is added, changeObservers "normalizes" to become a mapping of callback 235 // is added, changeObservers "normalizes" to become a mapping of callback
196 // priority -> observer and is then stored on objectInfo.changeObservers. 236 // priority -> observer and is then stored on objectInfo.changeObservers.
197 function ObjectInfoNormalizeChangeObservers(objectInfo) { 237 function ObjectInfoNormalizeChangeObservers(objectInfo) {
198 if (ChangeObserversIsOptimized(objectInfo.changeObservers)) { 238 if (ChangeObserversIsOptimized(objectInfo.changeObservers)) {
199 var observer = objectInfo.changeObservers; 239 var observer = objectInfo.changeObservers;
200 var callback = ObserverGetCallback(observer); 240 var callback = ObserverGetCallback(observer);
201 var callbackInfo = CallbackInfoGet(callback); 241 var callbackInfo = CallbackInfoGet(callback);
202 var priority = CallbackInfoGetPriority(callbackInfo); 242 var priority = CallbackInfoGetPriority(callbackInfo);
203 objectInfo.changeObservers = nullProtoObject(); 243 objectInfo.changeObservers = nullProtoObject();
204 objectInfo.changeObservers[priority] = observer; 244 objectInfo.changeObservers[priority] = observer;
205 } 245 }
206 } 246 }
207 247
248
208 function ObjectInfoAddObserver(objectInfo, callback, acceptList) { 249 function ObjectInfoAddObserver(objectInfo, callback, acceptList) {
209 var callbackInfo = CallbackInfoGetOrCreate(callback); 250 var callbackInfo = CallbackInfoGetOrCreate(callback);
210 var observer = ObserverCreate(callback, acceptList); 251 var observer = ObserverCreate(callback, acceptList);
211 252
212 if (!objectInfo.changeObservers) { 253 if (!objectInfo.changeObservers) {
213 objectInfo.changeObservers = observer; 254 objectInfo.changeObservers = observer;
214 return; 255 return;
215 } 256 }
216 257
217 ObjectInfoNormalizeChangeObservers(objectInfo); 258 ObjectInfoNormalizeChangeObservers(objectInfo);
(...skipping 25 matching lines...) Expand all
243 284
244 for (var priority in objectInfo.changeObservers) { 285 for (var priority in objectInfo.changeObservers) {
245 var observer = objectInfo.changeObservers[priority]; 286 var observer = objectInfo.changeObservers[priority];
246 if (!IS_NULL(observer) && ObserverIsActive(observer, objectInfo)) 287 if (!IS_NULL(observer) && ObserverIsActive(observer, objectInfo))
247 return true; 288 return true;
248 } 289 }
249 290
250 return false; 291 return false;
251 } 292 }
252 293
294
253 function ObjectInfoAddPerformingType(objectInfo, type) { 295 function ObjectInfoAddPerformingType(objectInfo, type) {
254 objectInfo.performing = objectInfo.performing || TypeMapCreate(); 296 objectInfo.performing = objectInfo.performing || TypeMapCreate();
255 TypeMapAddType(objectInfo.performing, type); 297 TypeMapAddType(objectInfo.performing, type);
256 objectInfo.performingCount++; 298 objectInfo.performingCount++;
257 } 299 }
258 300
301
259 function ObjectInfoRemovePerformingType(objectInfo, type) { 302 function ObjectInfoRemovePerformingType(objectInfo, type) {
260 objectInfo.performingCount--; 303 objectInfo.performingCount--;
261 TypeMapRemoveType(objectInfo.performing, type); 304 TypeMapRemoveType(objectInfo.performing, type);
262 } 305 }
263 306
307
264 function ObjectInfoGetPerformingTypes(objectInfo) { 308 function ObjectInfoGetPerformingTypes(objectInfo) {
265 return objectInfo.performingCount > 0 ? objectInfo.performing : null; 309 return objectInfo.performingCount > 0 ? objectInfo.performing : null;
266 } 310 }
267 311
312
268 function ConvertAcceptListToTypeMap(arg) { 313 function ConvertAcceptListToTypeMap(arg) {
269 // We use undefined as a sentinel for the default accept list. 314 // We use undefined as a sentinel for the default accept list.
270 if (IS_UNDEFINED(arg)) 315 if (IS_UNDEFINED(arg))
271 return arg; 316 return arg;
272 317
273 if (!IS_SPEC_OBJECT(arg)) 318 if (!IS_SPEC_OBJECT(arg))
274 throw MakeTypeError("observe_invalid_accept"); 319 throw MakeTypeError("observe_invalid_accept");
275 320
276 var len = ToInteger(arg.length); 321 var len = ToInteger(arg.length);
277 if (len < 0) len = 0; 322 if (len < 0) len = 0;
278 323
279 return TypeMapCreateFromList(arg, len); 324 return TypeMapCreateFromList(arg, len);
280 } 325 }
281 326
327
282 // CallbackInfo's optimized state is just a number which represents its global 328 // CallbackInfo's optimized state is just a number which represents its global
283 // priority. When a change record must be enqueued for the callback, it 329 // priority. When a change record must be enqueued for the callback, it
284 // normalizes. When delivery clears any pending change records, it re-optimizes. 330 // normalizes. When delivery clears any pending change records, it re-optimizes.
285 function CallbackInfoGet(callback) { 331 function CallbackInfoGet(callback) {
286 return %WeakCollectionGet(GetObservationStateJS().callbackInfoMap, callback); 332 return %WeakCollectionGet(GetObservationStateJS().callbackInfoMap, callback);
287 } 333 }
288 334
335
289 function CallbackInfoSet(callback, callbackInfo) { 336 function CallbackInfoSet(callback, callbackInfo) {
290 %WeakCollectionSet(GetObservationStateJS().callbackInfoMap, 337 %WeakCollectionSet(GetObservationStateJS().callbackInfoMap,
291 callback, callbackInfo); 338 callback, callbackInfo);
292 } 339 }
293 340
341
294 function CallbackInfoGetOrCreate(callback) { 342 function CallbackInfoGetOrCreate(callback) {
295 var callbackInfo = CallbackInfoGet(callback); 343 var callbackInfo = CallbackInfoGet(callback);
296 if (!IS_UNDEFINED(callbackInfo)) 344 if (!IS_UNDEFINED(callbackInfo))
297 return callbackInfo; 345 return callbackInfo;
298 346
299 var priority = GetNextCallbackPriority(); 347 var priority = GetNextCallbackPriority();
300 CallbackInfoSet(callback, priority); 348 CallbackInfoSet(callback, priority);
301 return priority; 349 return priority;
302 } 350 }
303 351
352
304 function CallbackInfoGetPriority(callbackInfo) { 353 function CallbackInfoGetPriority(callbackInfo) {
305 if (IS_NUMBER(callbackInfo)) 354 if (IS_NUMBER(callbackInfo))
306 return callbackInfo; 355 return callbackInfo;
307 else 356 else
308 return callbackInfo.priority; 357 return callbackInfo.priority;
309 } 358 }
310 359
360
311 function CallbackInfoNormalize(callback) { 361 function CallbackInfoNormalize(callback) {
312 var callbackInfo = CallbackInfoGet(callback); 362 var callbackInfo = CallbackInfoGet(callback);
313 if (IS_NUMBER(callbackInfo)) { 363 if (IS_NUMBER(callbackInfo)) {
314 var priority = callbackInfo; 364 var priority = callbackInfo;
315 callbackInfo = new InternalArray; 365 callbackInfo = new InternalArray;
316 callbackInfo.priority = priority; 366 callbackInfo.priority = priority;
317 CallbackInfoSet(callback, callbackInfo); 367 CallbackInfoSet(callback, callbackInfo);
318 } 368 }
319 return callbackInfo; 369 return callbackInfo;
320 } 370 }
321 371
372
322 function ObjectObserve(object, callback, acceptList) { 373 function ObjectObserve(object, callback, acceptList) {
323 if (!IS_SPEC_OBJECT(object)) 374 if (!IS_SPEC_OBJECT(object))
324 throw MakeTypeError("observe_non_object", ["observe"]); 375 throw MakeTypeError("observe_non_object", ["observe"]);
325 if (%IsJSGlobalProxy(object)) 376 if (%IsJSGlobalProxy(object))
326 throw MakeTypeError("observe_global_proxy", ["observe"]); 377 throw MakeTypeError("observe_global_proxy", ["observe"]);
327 if (!IS_SPEC_FUNCTION(callback)) 378 if (!IS_SPEC_FUNCTION(callback))
328 throw MakeTypeError("observe_non_function", ["observe"]); 379 throw MakeTypeError("observe_non_function", ["observe"]);
329 if (ObjectIsFrozen(callback)) 380 if (ObjectIsFrozen(callback))
330 throw MakeTypeError("observe_callback_frozen"); 381 throw MakeTypeError("observe_callback_frozen");
331 382
332 var objectObserveFn = %GetObjectContextObjectObserve(object); 383 var objectObserveFn = %GetObjectContextObjectObserve(object);
333 return objectObserveFn(object, callback, acceptList); 384 return objectObserveFn(object, callback, acceptList);
334 } 385 }
335 386
387
336 function NativeObjectObserve(object, callback, acceptList) { 388 function NativeObjectObserve(object, callback, acceptList) {
337 var objectInfo = ObjectInfoGetOrCreate(object); 389 var objectInfo = ObjectInfoGetOrCreate(object);
338 var typeList = ConvertAcceptListToTypeMap(acceptList); 390 var typeList = ConvertAcceptListToTypeMap(acceptList);
339 ObjectInfoAddObserver(objectInfo, callback, typeList); 391 ObjectInfoAddObserver(objectInfo, callback, typeList);
340 return object; 392 return object;
341 } 393 }
342 394
395
343 function ObjectUnobserve(object, callback) { 396 function ObjectUnobserve(object, callback) {
344 if (!IS_SPEC_OBJECT(object)) 397 if (!IS_SPEC_OBJECT(object))
345 throw MakeTypeError("observe_non_object", ["unobserve"]); 398 throw MakeTypeError("observe_non_object", ["unobserve"]);
346 if (%IsJSGlobalProxy(object)) 399 if (%IsJSGlobalProxy(object))
347 throw MakeTypeError("observe_global_proxy", ["unobserve"]); 400 throw MakeTypeError("observe_global_proxy", ["unobserve"]);
348 if (!IS_SPEC_FUNCTION(callback)) 401 if (!IS_SPEC_FUNCTION(callback))
349 throw MakeTypeError("observe_non_function", ["unobserve"]); 402 throw MakeTypeError("observe_non_function", ["unobserve"]);
350 403
351 var objectInfo = ObjectInfoGet(object); 404 var objectInfo = ObjectInfoGet(object);
352 if (IS_UNDEFINED(objectInfo)) 405 if (IS_UNDEFINED(objectInfo))
353 return object; 406 return object;
354 407
355 ObjectInfoRemoveObserver(objectInfo, callback); 408 ObjectInfoRemoveObserver(objectInfo, callback);
356 return object; 409 return object;
357 } 410 }
358 411
412
359 function ArrayObserve(object, callback) { 413 function ArrayObserve(object, callback) {
360 return ObjectObserve(object, callback, ['add', 414 return ObjectObserve(object, callback, ['add',
361 'update', 415 'update',
362 'delete', 416 'delete',
363 'splice']); 417 'splice']);
364 } 418 }
365 419
420
366 function ArrayUnobserve(object, callback) { 421 function ArrayUnobserve(object, callback) {
367 return ObjectUnobserve(object, callback); 422 return ObjectUnobserve(object, callback);
368 } 423 }
369 424
425
370 function ObserverEnqueueIfActive(observer, objectInfo, changeRecord) { 426 function ObserverEnqueueIfActive(observer, objectInfo, changeRecord) {
371 if (!ObserverIsActive(observer, objectInfo) || 427 if (!ObserverIsActive(observer, objectInfo) ||
372 !TypeMapHasType(ObserverGetAcceptTypes(observer), changeRecord.type)) { 428 !TypeMapHasType(ObserverGetAcceptTypes(observer), changeRecord.type)) {
373 return; 429 return;
374 } 430 }
375 431
376 var callback = ObserverGetCallback(observer); 432 var callback = ObserverGetCallback(observer);
377 if (!%ObserverObjectAndRecordHaveSameOrigin(callback, changeRecord.object, 433 if (!%ObserverObjectAndRecordHaveSameOrigin(callback, changeRecord.object,
378 changeRecord)) { 434 changeRecord)) {
379 return; 435 return;
(...skipping 12 matching lines...) Expand all
392 }); 448 });
393 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); 449 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name });
394 } else { 450 } else {
395 %EnqueueMicrotask(ObserveMicrotaskRunner); 451 %EnqueueMicrotask(ObserveMicrotaskRunner);
396 } 452 }
397 } 453 }
398 GetPendingObservers()[callbackInfo.priority] = callback; 454 GetPendingObservers()[callbackInfo.priority] = callback;
399 callbackInfo.push(changeRecord); 455 callbackInfo.push(changeRecord);
400 } 456 }
401 457
458
402 function ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord, type) { 459 function ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord, type) {
403 if (!ObjectInfoHasActiveObservers(objectInfo)) 460 if (!ObjectInfoHasActiveObservers(objectInfo))
404 return; 461 return;
405 462
406 var hasType = !IS_UNDEFINED(type); 463 var hasType = !IS_UNDEFINED(type);
407 var newRecord = hasType ? 464 var newRecord = hasType ?
408 { object: objectInfo.object, type: type } : 465 { object: objectInfo.object, type: type } :
409 { object: objectInfo.object }; 466 { object: objectInfo.object };
410 467
411 for (var prop in changeRecord) { 468 for (var prop in changeRecord) {
412 if (prop === 'object' || (hasType && prop === 'type')) continue; 469 if (prop === 'object' || (hasType && prop === 'type')) continue;
413 %DefineDataPropertyUnchecked( 470 %DefineDataPropertyUnchecked(
414 newRecord, prop, changeRecord[prop], READ_ONLY + DONT_DELETE); 471 newRecord, prop, changeRecord[prop], READ_ONLY + DONT_DELETE);
415 } 472 }
416 ObjectFreezeJS(newRecord); 473 ObjectFreezeJS(newRecord);
417 474
418 ObjectInfoEnqueueInternalChangeRecord(objectInfo, newRecord); 475 ObjectInfoEnqueueInternalChangeRecord(objectInfo, newRecord);
419 } 476 }
420 477
478
421 function ObjectInfoEnqueueInternalChangeRecord(objectInfo, changeRecord) { 479 function ObjectInfoEnqueueInternalChangeRecord(objectInfo, changeRecord) {
422 // TODO(rossberg): adjust once there is a story for symbols vs proxies. 480 // TODO(rossberg): adjust once there is a story for symbols vs proxies.
423 if (IS_SYMBOL(changeRecord.name)) return; 481 if (IS_SYMBOL(changeRecord.name)) return;
424 482
425 if (ChangeObserversIsOptimized(objectInfo.changeObservers)) { 483 if (ChangeObserversIsOptimized(objectInfo.changeObservers)) {
426 var observer = objectInfo.changeObservers; 484 var observer = objectInfo.changeObservers;
427 ObserverEnqueueIfActive(observer, objectInfo, changeRecord); 485 ObserverEnqueueIfActive(observer, objectInfo, changeRecord);
428 return; 486 return;
429 } 487 }
430 488
431 for (var priority in objectInfo.changeObservers) { 489 for (var priority in objectInfo.changeObservers) {
432 var observer = objectInfo.changeObservers[priority]; 490 var observer = objectInfo.changeObservers[priority];
433 if (IS_NULL(observer)) 491 if (IS_NULL(observer))
434 continue; 492 continue;
435 ObserverEnqueueIfActive(observer, objectInfo, changeRecord); 493 ObserverEnqueueIfActive(observer, objectInfo, changeRecord);
436 } 494 }
437 } 495 }
438 496
497
439 function BeginPerformSplice(array) { 498 function BeginPerformSplice(array) {
440 var objectInfo = ObjectInfoGet(array); 499 var objectInfo = ObjectInfoGet(array);
441 if (!IS_UNDEFINED(objectInfo)) 500 if (!IS_UNDEFINED(objectInfo))
442 ObjectInfoAddPerformingType(objectInfo, 'splice'); 501 ObjectInfoAddPerformingType(objectInfo, 'splice');
443 } 502 }
444 503
504
445 function EndPerformSplice(array) { 505 function EndPerformSplice(array) {
446 var objectInfo = ObjectInfoGet(array); 506 var objectInfo = ObjectInfoGet(array);
447 if (!IS_UNDEFINED(objectInfo)) 507 if (!IS_UNDEFINED(objectInfo))
448 ObjectInfoRemovePerformingType(objectInfo, 'splice'); 508 ObjectInfoRemovePerformingType(objectInfo, 'splice');
449 } 509 }
450 510
511
451 function EnqueueSpliceRecord(array, index, removed, addedCount) { 512 function EnqueueSpliceRecord(array, index, removed, addedCount) {
452 var objectInfo = ObjectInfoGet(array); 513 var objectInfo = ObjectInfoGet(array);
453 if (!ObjectInfoHasActiveObservers(objectInfo)) 514 if (!ObjectInfoHasActiveObservers(objectInfo))
454 return; 515 return;
455 516
456 var changeRecord = { 517 var changeRecord = {
457 type: 'splice', 518 type: 'splice',
458 object: array, 519 object: array,
459 index: index, 520 index: index,
460 removed: removed, 521 removed: removed,
461 addedCount: addedCount 522 addedCount: addedCount
462 }; 523 };
463 524
464 ObjectFreezeJS(changeRecord); 525 ObjectFreezeJS(changeRecord);
465 ObjectFreezeJS(changeRecord.removed); 526 ObjectFreezeJS(changeRecord.removed);
466 ObjectInfoEnqueueInternalChangeRecord(objectInfo, changeRecord); 527 ObjectInfoEnqueueInternalChangeRecord(objectInfo, changeRecord);
467 } 528 }
468 529
530
469 function NotifyChange(type, object, name, oldValue) { 531 function NotifyChange(type, object, name, oldValue) {
470 var objectInfo = ObjectInfoGet(object); 532 var objectInfo = ObjectInfoGet(object);
471 if (!ObjectInfoHasActiveObservers(objectInfo)) 533 if (!ObjectInfoHasActiveObservers(objectInfo))
472 return; 534 return;
473 535
474 var changeRecord; 536 var changeRecord;
475 if (arguments.length == 2) { 537 if (arguments.length == 2) {
476 changeRecord = { type: type, object: object }; 538 changeRecord = { type: type, object: object };
477 } else if (arguments.length == 3) { 539 } else if (arguments.length == 3) {
478 changeRecord = { type: type, object: object, name: name }; 540 changeRecord = { type: type, object: object, name: name };
479 } else { 541 } else {
480 changeRecord = { 542 changeRecord = {
481 type: type, 543 type: type,
482 object: object, 544 object: object,
483 name: name, 545 name: name,
484 oldValue: oldValue 546 oldValue: oldValue
485 }; 547 };
486 } 548 }
487 549
488 ObjectFreezeJS(changeRecord); 550 ObjectFreezeJS(changeRecord);
489 ObjectInfoEnqueueInternalChangeRecord(objectInfo, changeRecord); 551 ObjectInfoEnqueueInternalChangeRecord(objectInfo, changeRecord);
490 } 552 }
491 553
492 var notifierPrototype = {};
493 554
494 function ObjectNotifierNotify(changeRecord) { 555 function ObjectNotifierNotify(changeRecord) {
495 if (!IS_SPEC_OBJECT(this)) 556 if (!IS_SPEC_OBJECT(this))
496 throw MakeTypeError("called_on_non_object", ["notify"]); 557 throw MakeTypeError("called_on_non_object", ["notify"]);
497 558
498 var objectInfo = ObjectInfoGetFromNotifier(this); 559 var objectInfo = ObjectInfoGetFromNotifier(this);
499 if (IS_UNDEFINED(objectInfo)) 560 if (IS_UNDEFINED(objectInfo))
500 throw MakeTypeError("observe_notify_non_notifier"); 561 throw MakeTypeError("observe_notify_non_notifier");
501 if (!IS_STRING(changeRecord.type)) 562 if (!IS_STRING(changeRecord.type))
502 throw MakeTypeError("observe_type_non_string"); 563 throw MakeTypeError("observe_type_non_string");
503 564
504 ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord); 565 ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord);
505 } 566 }
506 567
568
507 function ObjectNotifierPerformChange(changeType, changeFn) { 569 function ObjectNotifierPerformChange(changeType, changeFn) {
508 if (!IS_SPEC_OBJECT(this)) 570 if (!IS_SPEC_OBJECT(this))
509 throw MakeTypeError("called_on_non_object", ["performChange"]); 571 throw MakeTypeError("called_on_non_object", ["performChange"]);
510 572
511 var objectInfo = ObjectInfoGetFromNotifier(this); 573 var objectInfo = ObjectInfoGetFromNotifier(this);
512 if (IS_UNDEFINED(objectInfo)) 574 if (IS_UNDEFINED(objectInfo))
513 throw MakeTypeError("observe_notify_non_notifier"); 575 throw MakeTypeError("observe_notify_non_notifier");
514 if (!IS_STRING(changeType)) 576 if (!IS_STRING(changeType))
515 throw MakeTypeError("observe_perform_non_string"); 577 throw MakeTypeError("observe_perform_non_string");
516 if (!IS_SPEC_FUNCTION(changeFn)) 578 if (!IS_SPEC_FUNCTION(changeFn))
517 throw MakeTypeError("observe_perform_non_function"); 579 throw MakeTypeError("observe_perform_non_function");
518 580
519 var performChangeFn = %GetObjectContextNotifierPerformChange(objectInfo); 581 var performChangeFn = %GetObjectContextNotifierPerformChange(objectInfo);
520 performChangeFn(objectInfo, changeType, changeFn); 582 performChangeFn(objectInfo, changeType, changeFn);
521 } 583 }
522 584
585
523 function NativeObjectNotifierPerformChange(objectInfo, changeType, changeFn) { 586 function NativeObjectNotifierPerformChange(objectInfo, changeType, changeFn) {
524 ObjectInfoAddPerformingType(objectInfo, changeType); 587 ObjectInfoAddPerformingType(objectInfo, changeType);
525 588
526 var changeRecord; 589 var changeRecord;
527 try { 590 try {
528 changeRecord = %_CallFunction(UNDEFINED, changeFn); 591 changeRecord = %_CallFunction(UNDEFINED, changeFn);
529 } finally { 592 } finally {
530 ObjectInfoRemovePerformingType(objectInfo, changeType); 593 ObjectInfoRemovePerformingType(objectInfo, changeType);
531 } 594 }
532 595
533 if (IS_SPEC_OBJECT(changeRecord)) 596 if (IS_SPEC_OBJECT(changeRecord))
534 ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord, changeType); 597 ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord, changeType);
535 } 598 }
536 599
600
537 function ObjectGetNotifier(object) { 601 function ObjectGetNotifier(object) {
538 if (!IS_SPEC_OBJECT(object)) 602 if (!IS_SPEC_OBJECT(object))
539 throw MakeTypeError("observe_non_object", ["getNotifier"]); 603 throw MakeTypeError("observe_non_object", ["getNotifier"]);
540 if (%IsJSGlobalProxy(object)) 604 if (%IsJSGlobalProxy(object))
541 throw MakeTypeError("observe_global_proxy", ["getNotifier"]); 605 throw MakeTypeError("observe_global_proxy", ["getNotifier"]);
542 606
543 if (ObjectIsFrozen(object)) return null; 607 if (ObjectIsFrozen(object)) return null;
544 608
545 if (!%ObjectWasCreatedInCurrentOrigin(object)) return null; 609 if (!%ObjectWasCreatedInCurrentOrigin(object)) return null;
546 610
547 var getNotifierFn = %GetObjectContextObjectGetNotifier(object); 611 var getNotifierFn = %GetObjectContextObjectGetNotifier(object);
548 return getNotifierFn(object); 612 return getNotifierFn(object);
549 } 613 }
550 614
615
551 function NativeObjectGetNotifier(object) { 616 function NativeObjectGetNotifier(object) {
552 var objectInfo = ObjectInfoGetOrCreate(object); 617 var objectInfo = ObjectInfoGetOrCreate(object);
553 return ObjectInfoGetNotifier(objectInfo); 618 return ObjectInfoGetNotifier(objectInfo);
554 } 619 }
555 620
621
556 function CallbackDeliverPending(callback) { 622 function CallbackDeliverPending(callback) {
557 var callbackInfo = CallbackInfoGet(callback); 623 var callbackInfo = CallbackInfoGet(callback);
558 if (IS_UNDEFINED(callbackInfo) || IS_NUMBER(callbackInfo)) 624 if (IS_UNDEFINED(callbackInfo) || IS_NUMBER(callbackInfo))
559 return false; 625 return false;
560 626
561 // Clear the pending change records from callback and return it to its 627 // Clear the pending change records from callback and return it to its
562 // "optimized" state. 628 // "optimized" state.
563 var priority = callbackInfo.priority; 629 var priority = callbackInfo.priority;
564 CallbackInfoSet(callback, priority); 630 CallbackInfoSet(callback, priority);
565 631
566 var pendingObservers = GetPendingObservers(); 632 var pendingObservers = GetPendingObservers();
567 if (!IS_NULL(pendingObservers)) 633 if (!IS_NULL(pendingObservers))
568 delete pendingObservers[priority]; 634 delete pendingObservers[priority];
569 635
570 // TODO: combine the following runtime calls for perf optimization. 636 // TODO: combine the following runtime calls for perf optimization.
571 var delivered = []; 637 var delivered = [];
572 %MoveArrayContents(callbackInfo, delivered); 638 %MoveArrayContents(callbackInfo, delivered);
573 %DeliverObservationChangeRecords(callback, delivered); 639 %DeliverObservationChangeRecords(callback, delivered);
574 640
575 return true; 641 return true;
576 } 642 }
577 643
644
578 function ObjectDeliverChangeRecords(callback) { 645 function ObjectDeliverChangeRecords(callback) {
579 if (!IS_SPEC_FUNCTION(callback)) 646 if (!IS_SPEC_FUNCTION(callback))
580 throw MakeTypeError("observe_non_function", ["deliverChangeRecords"]); 647 throw MakeTypeError("observe_non_function", ["deliverChangeRecords"]);
581 648
582 while (CallbackDeliverPending(callback)) {} 649 while (CallbackDeliverPending(callback)) {}
583 } 650 }
584 651
652
585 function ObserveMicrotaskRunner() { 653 function ObserveMicrotaskRunner() {
586 var pendingObservers = GetPendingObservers(); 654 var pendingObservers = GetPendingObservers();
587 if (!IS_NULL(pendingObservers)) { 655 if (!IS_NULL(pendingObservers)) {
588 SetPendingObservers(null); 656 SetPendingObservers(null);
589 for (var i in pendingObservers) { 657 for (var i in pendingObservers) {
590 CallbackDeliverPending(pendingObservers[i]); 658 CallbackDeliverPending(pendingObservers[i]);
591 } 659 }
592 } 660 }
593 } 661 }
594 662
595 function SetupObjectObserve() { 663 // -------------------------------------------------------------------
596 %CheckIsBootstrapping();
597 InstallFunctions($Object, DONT_ENUM, [
598 "deliverChangeRecords", ObjectDeliverChangeRecords,
599 "getNotifier", ObjectGetNotifier,
600 "observe", ObjectObserve,
601 "unobserve", ObjectUnobserve
602 ]);
603 InstallFunctions($Array, DONT_ENUM, [
604 "observe", ArrayObserve,
605 "unobserve", ArrayUnobserve
606 ]);
607 InstallFunctions(notifierPrototype, DONT_ENUM, [
608 "notify", ObjectNotifierNotify,
609 "performChange", ObjectNotifierPerformChange
610 ]);
611 }
612 664
613 SetupObjectObserve(); 665 InstallFunctions(GlobalObject, DONT_ENUM, [
666 "deliverChangeRecords", ObjectDeliverChangeRecords,
667 "getNotifier", ObjectGetNotifier,
668 "observe", ObjectObserve,
669 "unobserve", ObjectUnobserve
670 ]);
671 InstallFunctions(GlobalArray, DONT_ENUM, [
672 "observe", ArrayObserve,
673 "unobserve", ArrayUnobserve
674 ]);
675 InstallFunctions(notifierPrototype, DONT_ENUM, [
676 "notify", ObjectNotifierNotify,
677 "performChange", ObjectNotifierPerformChange
678 ]);
679
680 $observeNotifyChange = NotifyChange;
681 $observeEnqueueSpliceRecord = EnqueueSpliceRecord;
682 $observeBeginPerformSplice = BeginPerformSplice;
683 $observeEndPerformSplice = EndPerformSplice;
684 $observeNativeObjectObserve = NativeObjectObserve;
685 $observeNativeObjectGetNotifier = NativeObjectGetNotifier;
686 $observeNativeObjectNotifierPerformChange = NativeObjectNotifierPerformChange;
687
688 })();
OLDNEW
« no previous file with comments | « src/bootstrapper.cc ('k') | src/v8natives.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698