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

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

Issue 23491002: Revert "This patch implements optimized objectInfo structure which manages the set of observers ass… (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Try again Created 7 years, 3 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
« no previous file with comments | « no previous file | test/cctest/test-object-observe.cc » ('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 // 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"; 28 "use strict";
29 29
30 // Overview:
31 //
32 // 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
34 // 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
36 // proper change records for objects which were mutated. The Object.observe
37 // routing and accounting consists primarily of three participants
38 //
39 // 1) ObjectInfo. This represents the observed state of a given object. It
40 // records what callbacks are observing the object, with what options, and
41 // what "change types" are in progress on the object (i.e. via
42 // notifier.performChange).
43 //
44 // 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
46 // priority of the callback (which determines delivery order between
47 // callbacks).
48 //
49 // 3) observationState.pendingObservers. This is the set of observers which
50 // have change records which must be delivered. During "normal" delivery
51 // (i.e. not Object.deliverChangeRecords), this is the mechanism by which
52 // callbacks are invoked in the proper order until there are no more
53 // change records pending to a callback.
54 //
55 // Note that in order to reduce allocation and processing costs, the
56 // implementation of (1) and (2) have "optimized" states which represent
57 // common cases which can be handled more efficiently.
58
59 var observationState = %GetObservationState(); 30 var observationState = %GetObservationState();
60 if (IS_UNDEFINED(observationState.callbackInfoMap)) { 31 if (IS_UNDEFINED(observationState.callbackInfoMap)) {
61 observationState.callbackInfoMap = %ObservationWeakMapCreate(); 32 observationState.callbackInfoMap = %ObservationWeakMapCreate();
62 observationState.objectInfoMap = %ObservationWeakMapCreate(); 33 observationState.objectInfoMap = %ObservationWeakMapCreate();
63 observationState.notifierObjectInfoMap = %ObservationWeakMapCreate(); 34 observationState.notifierTargetMap = %ObservationWeakMapCreate();
64 observationState.pendingObservers = null; 35 observationState.pendingObservers = new InternalArray;
65 observationState.nextCallbackPriority = 0; 36 observationState.nextCallbackPriority = 0;
66 } 37 }
67 38
68 function ObservationWeakMap(map) { 39 function ObservationWeakMap(map) {
69 this.map_ = map; 40 this.map_ = map;
70 } 41 }
71 42
72 ObservationWeakMap.prototype = { 43 ObservationWeakMap.prototype = {
73 get: function(key) { 44 get: function(key) {
74 key = %UnwrapGlobalProxy(key); 45 key = %UnwrapGlobalProxy(key);
75 if (!IS_SPEC_OBJECT(key)) return void 0; 46 if (!IS_SPEC_OBJECT(key)) return void 0;
76 return %WeakCollectionGet(this.map_, key); 47 return %WeakCollectionGet(this.map_, key);
77 }, 48 },
78 set: function(key, value) { 49 set: function(key, value) {
79 key = %UnwrapGlobalProxy(key); 50 key = %UnwrapGlobalProxy(key);
80 if (!IS_SPEC_OBJECT(key)) return void 0; 51 if (!IS_SPEC_OBJECT(key)) return void 0;
81 %WeakCollectionSet(this.map_, key, value); 52 %WeakCollectionSet(this.map_, key, value);
82 }, 53 },
83 has: function(key) { 54 has: function(key) {
84 return !IS_UNDEFINED(this.get(key)); 55 return !IS_UNDEFINED(this.get(key));
85 } 56 }
86 }; 57 };
87 58
88 var callbackInfoMap = 59 var callbackInfoMap =
89 new ObservationWeakMap(observationState.callbackInfoMap); 60 new ObservationWeakMap(observationState.callbackInfoMap);
90 var objectInfoMap = new ObservationWeakMap(observationState.objectInfoMap); 61 var objectInfoMap = new ObservationWeakMap(observationState.objectInfoMap);
91 var notifierObjectInfoMap = 62 var notifierTargetMap =
92 new ObservationWeakMap(observationState.notifierObjectInfoMap); 63 new ObservationWeakMap(observationState.notifierTargetMap);
93 64
94 function TypeMapCreate() { 65 function CreateObjectInfo(object) {
95 return { __proto__: null }; 66 var info = {
67 changeObservers: new InternalArray,
68 notifier: null,
69 inactiveObservers: new InternalArray,
70 performing: { __proto__: null },
71 performingCount: 0,
72 };
73 objectInfoMap.set(object, info);
74 return info;
96 } 75 }
97 76
98 function TypeMapAddType(typeMap, type, ignoreDuplicate) { 77 var defaultAcceptTypes = {
99 typeMap[type] = ignoreDuplicate ? 1 : (typeMap[type] || 0) + 1; 78 __proto__: null,
79 'new': true,
80 'updated': true,
81 'deleted': true,
82 'prototype': true,
83 'reconfigured': true
84 };
85
86 function CreateObserver(callback, accept) {
87 var observer = {
88 __proto__: null,
89 callback: callback,
90 accept: defaultAcceptTypes
91 };
92
93 if (IS_UNDEFINED(accept))
94 return observer;
95
96 var acceptMap = { __proto__: null };
97 for (var i = 0; i < accept.length; i++)
98 acceptMap[accept[i]] = true;
99
100 observer.accept = acceptMap;
101 return observer;
100 } 102 }
101 103
102 function TypeMapRemoveType(typeMap, type) { 104 function ObserverIsActive(observer, objectInfo) {
103 typeMap[type]--; 105 if (objectInfo.performingCount === 0)
104 }
105
106 function TypeMapCreateFromList(typeList) {
107 var typeMap = TypeMapCreate();
108 for (var i = 0; i < typeList.length; i++) {
109 TypeMapAddType(typeMap, typeList[i], true);
110 }
111 return typeMap;
112 }
113
114 function TypeMapHasType(typeMap, type) {
115 return !!typeMap[type];
116 }
117
118 function TypeMapIsDisjointFrom(typeMap1, typeMap2) {
119 if (!typeMap1 || !typeMap2)
120 return true; 106 return true;
121 107
122 for (var type in typeMap1) { 108 var performing = objectInfo.performing;
123 if (TypeMapHasType(typeMap1, type) && TypeMapHasType(typeMap2, type)) 109 for (var type in performing) {
110 if (performing[type] > 0 && observer.accept[type])
124 return false; 111 return false;
125 } 112 }
126 113
127 return true; 114 return true;
128 } 115 }
129 116
130 var defaultAcceptTypes = TypeMapCreateFromList([ 117 function ObserverIsInactive(observer, objectInfo) {
131 'new', 118 return !ObserverIsActive(observer, objectInfo);
132 'updated',
133 'deleted',
134 'prototype',
135 'reconfigured'
136 ]);
137
138 // An Observer is a registration to observe an object by a callback with
139 // a given set of accept types. If the set of accept types is the default
140 // set for Object.observe, the observer is represented as a direct reference
141 // to the callback. An observer never changes its accept types and thus never
142 // needs to "normalize".
143 function ObserverCreate(callback, acceptList) {
144 return IS_UNDEFINED(acceptList) ? callback : {
145 __proto__: null,
146 callback: callback,
147 accept: TypeMapCreateFromList(acceptList)
148 };
149 } 119 }
150 120
151 function ObserverGetCallback(observer) { 121 function RemoveNullElements(from) {
152 return IS_SPEC_FUNCTION(observer) ? observer : observer.callback; 122 var i = 0;
123 var j = 0;
124 for (; i < from.length; i++) {
125 if (from[i] === null)
126 continue;
127 if (j < i)
128 from[j] = from[i];
129 j++;
130 }
131
132 if (i !== j)
133 from.length = from.length - (i - j);
153 } 134 }
154 135
155 function ObserverGetAcceptTypes(observer) { 136 function RepartitionObservers(conditionFn, from, to, objectInfo) {
156 return IS_SPEC_FUNCTION(observer) ? defaultAcceptTypes : observer.accept; 137 var anyRemoved = false;
138 for (var i = 0; i < from.length; i++) {
139 var observer = from[i];
140 if (conditionFn(observer, objectInfo)) {
141 anyRemoved = true;
142 from[i] = null;
143 to.push(observer);
144 }
145 }
146
147 if (anyRemoved)
148 RemoveNullElements(from);
157 } 149 }
158 150
159 function ObserverIsActive(observer, objectInfo) { 151 function BeginPerformChange(objectInfo, type) {
160 return TypeMapIsDisjointFrom(ObjectInfoGetPerformingTypes(objectInfo), 152 objectInfo.performing[type] = (objectInfo.performing[type] || 0) + 1;
161 ObserverGetAcceptTypes(observer)); 153 objectInfo.performingCount++;
154 RepartitionObservers(ObserverIsInactive,
155 objectInfo.changeObservers,
156 objectInfo.inactiveObservers,
157 objectInfo);
162 } 158 }
163 159
164 function ObjectInfoGet(object) { 160 function EndPerformChange(objectInfo, type) {
165 var objectInfo = objectInfoMap.get(object); 161 objectInfo.performing[type]--;
166 if (IS_UNDEFINED(objectInfo)) { 162 objectInfo.performingCount--;
167 if (!%IsJSProxy(object)) 163 RepartitionObservers(ObserverIsActive,
168 %SetIsObserved(object); 164 objectInfo.inactiveObservers,
169 165 objectInfo.changeObservers,
170 objectInfo = { 166 objectInfo);
171 object: object,
172 changeObservers: null,
173 notifier: null,
174 performing: null,
175 performingCount: 0,
176 };
177 objectInfoMap.set(object, objectInfo);
178 }
179 return objectInfo;
180 } 167 }
181 168
182 function ObjectInfoGetFromNotifier(notifier) { 169 function EnsureObserverRemoved(objectInfo, callback) {
183 return notifierObjectInfoMap.get(notifier); 170 function remove(observerList) {
184 } 171 for (var i = 0; i < observerList.length; i++) {
185 172 if (observerList[i].callback === callback) {
186 function ObjectInfoGetNotifier(objectInfo) { 173 observerList.splice(i, 1);
187 if (IS_NULL(objectInfo.notifier)) { 174 return true;
188 objectInfo.notifier = { __proto__: notifierPrototype }; 175 }
189 notifierObjectInfoMap.set(objectInfo.notifier, objectInfo); 176 }
177 return false;
190 } 178 }
191 179
192 return objectInfo.notifier; 180 if (!remove(objectInfo.changeObservers))
193 } 181 remove(objectInfo.inactiveObservers);
194
195 function ObjectInfoGetObject(objectInfo) {
196 return objectInfo.object;
197 }
198
199 function ChangeObserversIsOptimized(changeObservers) {
200 return typeof changeObservers === 'function' ||
201 typeof changeObservers.callback === 'function';
202 }
203
204 // The set of observers on an object is called 'changeObservers'. The first
205 // observer is referenced directly via objectInfo.changeObservers. When a second
206 // is added, changeObservers "normalizes" to become a mapping of callback
207 // priority -> observer and is then stored on objectInfo.changeObservers.
208 function ObjectInfoNormalizeChangeObservers(objectInfo) {
209 if (ChangeObserversIsOptimized(objectInfo.changeObservers)) {
210 var observer = objectInfo.changeObservers;
211 var callback = ObserverGetCallback(observer);
212 var callbackInfo = CallbackInfoGet(callback);
213 var priority = CallbackInfoGetPriority(callbackInfo);
214 objectInfo.changeObservers = { __proto__: null };
215 objectInfo.changeObservers[priority] = observer;
216 }
217 }
218
219 function ObjectInfoAddObserver(objectInfo, callback, acceptList) {
220 var callbackInfo = CallbackInfoGetOrCreate(callback);
221 var observer = ObserverCreate(callback, acceptList);
222
223 if (!objectInfo.changeObservers) {
224 objectInfo.changeObservers = observer;
225 return;
226 }
227
228 ObjectInfoNormalizeChangeObservers(objectInfo);
229 var priority = CallbackInfoGetPriority(callbackInfo);
230 objectInfo.changeObservers[priority] = observer;
231 }
232
233 function ObjectInfoRemoveObserver(objectInfo, callback) {
234 if (!objectInfo.changeObservers)
235 return;
236
237 if (ChangeObserversIsOptimized(objectInfo.changeObservers)) {
238 if (callback === ObserverGetCallback(objectInfo.changeObservers))
239 objectInfo.changeObservers = null;
240 return;
241 }
242
243 var callbackInfo = CallbackInfoGet(callback);
244 var priority = CallbackInfoGetPriority(callbackInfo);
245 delete objectInfo.changeObservers[priority];
246 }
247
248 function ObjectInfoHasActiveObservers(objectInfo) {
249 if (IS_UNDEFINED(objectInfo) || !objectInfo.changeObservers)
250 return false;
251
252 if (ChangeObserversIsOptimized(objectInfo.changeObservers))
253 return ObserverIsActive(objectInfo.changeObservers, objectInfo);
254
255 for (var priority in objectInfo.changeObservers) {
256 if (ObserverIsActive(objectInfo.changeObservers[priority], objectInfo))
257 return true;
258 }
259
260 return false;
261 }
262
263 function ObjectInfoAddPerformingType(objectInfo, type) {
264 objectInfo.performing = objectInfo.performing || TypeMapCreate();
265 TypeMapAddType(objectInfo.performing, type);
266 objectInfo.performingCount++;
267 }
268
269 function ObjectInfoRemovePerformingType(objectInfo, type) {
270 objectInfo.performingCount--;
271 TypeMapRemoveType(objectInfo.performing, type);
272 }
273
274 function ObjectInfoGetPerformingTypes(objectInfo) {
275 return objectInfo.performingCount > 0 ? objectInfo.performing : null;
276 } 182 }
277 183
278 function AcceptArgIsValid(arg) { 184 function AcceptArgIsValid(arg) {
279 if (IS_UNDEFINED(arg)) 185 if (IS_UNDEFINED(arg))
280 return true; 186 return true;
281 187
282 if (!IS_SPEC_OBJECT(arg) || 188 if (!IS_SPEC_OBJECT(arg) ||
283 !IS_NUMBER(arg.length) || 189 !IS_NUMBER(arg.length) ||
284 arg.length < 0) 190 arg.length < 0)
285 return false; 191 return false;
286 192
287 var length = arg.length; 193 var length = arg.length;
288 for (var i = 0; i < length; i++) { 194 for (var i = 0; i < length; i++) {
289 if (!IS_STRING(arg[i])) 195 if (!IS_STRING(arg[i]))
290 return false; 196 return false;
291 } 197 }
292 return true; 198 return true;
293 } 199 }
294 200
295 // CallbackInfo's optimized state is just a number which represents its global 201 function EnsureCallbackPriority(callback) {
296 // priority. When a change record must be enqueued for the callback, it 202 if (!callbackInfoMap.has(callback))
297 // normalizes. When delivery clears any pending change records, it re-optimizes. 203 callbackInfoMap.set(callback, observationState.nextCallbackPriority++);
298 function CallbackInfoGet(callback) {
299 return callbackInfoMap.get(callback);
300 } 204 }
301 205
302 function CallbackInfoGetOrCreate(callback) { 206 function NormalizeCallbackInfo(callback) {
303 var callbackInfo = callbackInfoMap.get(callback);
304 if (!IS_UNDEFINED(callbackInfo))
305 return callbackInfo;
306
307 var priority = observationState.nextCallbackPriority++
308 callbackInfoMap.set(callback, priority);
309 return priority;
310 }
311
312 function CallbackInfoGetPriority(callbackInfo) {
313 if (IS_NUMBER(callbackInfo))
314 return callbackInfo;
315 else
316 return callbackInfo.priority;
317 }
318
319 function CallbackInfoNormalize(callback) {
320 var callbackInfo = callbackInfoMap.get(callback); 207 var callbackInfo = callbackInfoMap.get(callback);
321 if (IS_NUMBER(callbackInfo)) { 208 if (IS_NUMBER(callbackInfo)) {
322 var priority = callbackInfo; 209 var priority = callbackInfo;
323 callbackInfo = new InternalArray; 210 callbackInfo = new InternalArray;
324 callbackInfo.priority = priority; 211 callbackInfo.priority = priority;
325 callbackInfoMap.set(callback, callbackInfo); 212 callbackInfoMap.set(callback, callbackInfo);
326 } 213 }
327 return callbackInfo; 214 return callbackInfo;
328 } 215 }
329 216
330 function ObjectObserve(object, callback, acceptList) { 217 function ObjectObserve(object, callback, accept) {
331 if (!IS_SPEC_OBJECT(object)) 218 if (!IS_SPEC_OBJECT(object))
332 throw MakeTypeError("observe_non_object", ["observe"]); 219 throw MakeTypeError("observe_non_object", ["observe"]);
333 if (!IS_SPEC_FUNCTION(callback)) 220 if (!IS_SPEC_FUNCTION(callback))
334 throw MakeTypeError("observe_non_function", ["observe"]); 221 throw MakeTypeError("observe_non_function", ["observe"]);
335 if (ObjectIsFrozen(callback)) 222 if (ObjectIsFrozen(callback))
336 throw MakeTypeError("observe_callback_frozen"); 223 throw MakeTypeError("observe_callback_frozen");
337 if (!AcceptArgIsValid(acceptList)) 224 if (!AcceptArgIsValid(accept))
338 throw MakeTypeError("observe_accept_invalid"); 225 throw MakeTypeError("observe_accept_invalid");
339 226
340 var objectInfo = ObjectInfoGet(object); 227 EnsureCallbackPriority(callback);
341 ObjectInfoAddObserver(objectInfo, callback, acceptList); 228
229 var objectInfo = objectInfoMap.get(object);
230 if (IS_UNDEFINED(objectInfo)) {
231 objectInfo = CreateObjectInfo(object);
232 %SetIsObserved(object);
233 }
234
235 EnsureObserverRemoved(objectInfo, callback);
236
237 var observer = CreateObserver(callback, accept);
238 if (ObserverIsActive(observer, objectInfo))
239 objectInfo.changeObservers.push(observer);
240 else
241 objectInfo.inactiveObservers.push(observer);
242
342 return object; 243 return object;
343 } 244 }
344 245
345 function ObjectUnobserve(object, callback) { 246 function ObjectUnobserve(object, callback) {
346 if (!IS_SPEC_OBJECT(object)) 247 if (!IS_SPEC_OBJECT(object))
347 throw MakeTypeError("observe_non_object", ["unobserve"]); 248 throw MakeTypeError("observe_non_object", ["unobserve"]);
348 if (!IS_SPEC_FUNCTION(callback)) 249 if (!IS_SPEC_FUNCTION(callback))
349 throw MakeTypeError("observe_non_function", ["unobserve"]); 250 throw MakeTypeError("observe_non_function", ["unobserve"]);
350 251
351 var objectInfo = objectInfoMap.get(object); 252 var objectInfo = objectInfoMap.get(object);
352 if (IS_UNDEFINED(objectInfo)) 253 if (IS_UNDEFINED(objectInfo))
353 return object; 254 return object;
354 255
355 ObjectInfoRemoveObserver(objectInfo, callback); 256 EnsureObserverRemoved(objectInfo, callback);
356 return object; 257 return object;
357 } 258 }
358 259
359 function ArrayObserve(object, callback) { 260 function ArrayObserve(object, callback) {
360 return ObjectObserve(object, callback, ['new', 261 return ObjectObserve(object, callback, ['new',
361 'updated', 262 'updated',
362 'deleted', 263 'deleted',
363 'splice']); 264 'splice']);
364 } 265 }
365 266
366 function ArrayUnobserve(object, callback) { 267 function ArrayUnobserve(object, callback) {
367 return ObjectUnobserve(object, callback); 268 return ObjectUnobserve(object, callback);
368 } 269 }
369 270
370 function ObserverEnqueueIfActive(observer, objectInfo, changeRecord) { 271 function EnqueueToCallback(callback, changeRecord) {
371 if (!ObserverIsActive(observer, objectInfo) || 272 var callbackInfo = NormalizeCallbackInfo(callback);
372 !TypeMapHasType(ObserverGetAcceptTypes(observer), changeRecord.type)) {
373 return;
374 }
375
376 var callback = ObserverGetCallback(observer);
377 var callbackInfo = CallbackInfoNormalize(callback);
378 if (!observationState.pendingObservers)
379 observationState.pendingObservers = { __proto__: null };
380 observationState.pendingObservers[callbackInfo.priority] = callback; 273 observationState.pendingObservers[callbackInfo.priority] = callback;
381 callbackInfo.push(changeRecord); 274 callbackInfo.push(changeRecord);
382 %SetObserverDeliveryPending(); 275 %SetObserverDeliveryPending();
383 } 276 }
384 277
385 function ObjectInfoEnqueueChangeRecord(objectInfo, changeRecord) { 278 function EnqueueChangeRecord(changeRecord, observers) {
386 // TODO(rossberg): adjust once there is a story for symbols vs proxies. 279 // TODO(rossberg): adjust once there is a story for symbols vs proxies.
387 if (IS_SYMBOL(changeRecord.name)) return; 280 if (IS_SYMBOL(changeRecord.name)) return;
388 281
389 if (ChangeObserversIsOptimized(objectInfo.changeObservers)) { 282 for (var i = 0; i < observers.length; i++) {
390 var observer = objectInfo.changeObservers; 283 var observer = observers[i];
391 ObserverEnqueueIfActive(observer, objectInfo, changeRecord); 284 if (IS_UNDEFINED(observer.accept[changeRecord.type]))
392 return; 285 continue;
393 }
394 286
395 for (var priority in objectInfo.changeObservers) { 287 EnqueueToCallback(observer.callback, changeRecord);
396 var observer = objectInfo.changeObservers[priority];
397 ObserverEnqueueIfActive(observer, objectInfo, changeRecord);
398 } 288 }
399 } 289 }
400 290
401 function BeginPerformSplice(array) { 291 function BeginPerformSplice(array) {
402 var objectInfo = objectInfoMap.get(array); 292 var objectInfo = objectInfoMap.get(array);
403 if (!IS_UNDEFINED(objectInfo)) 293 if (!IS_UNDEFINED(objectInfo))
404 ObjectInfoAddPerformingType(objectInfo, 'splice'); 294 BeginPerformChange(objectInfo, 'splice');
405 } 295 }
406 296
407 function EndPerformSplice(array) { 297 function EndPerformSplice(array) {
408 var objectInfo = objectInfoMap.get(array); 298 var objectInfo = objectInfoMap.get(array);
409 if (!IS_UNDEFINED(objectInfo)) 299 if (!IS_UNDEFINED(objectInfo))
410 ObjectInfoRemovePerformingType(objectInfo, 'splice'); 300 EndPerformChange(objectInfo, 'splice');
411 } 301 }
412 302
413 function EnqueueSpliceRecord(array, index, removed, addedCount) { 303 function EnqueueSpliceRecord(array, index, removed, addedCount) {
414 var objectInfo = objectInfoMap.get(array); 304 var objectInfo = objectInfoMap.get(array);
415 if (!ObjectInfoHasActiveObservers(objectInfo)) 305 if (IS_UNDEFINED(objectInfo) || objectInfo.changeObservers.length === 0)
416 return; 306 return;
417 307
418 var changeRecord = { 308 var changeRecord = {
419 type: 'splice', 309 type: 'splice',
420 object: array, 310 object: array,
421 index: index, 311 index: index,
422 removed: removed, 312 removed: removed,
423 addedCount: addedCount 313 addedCount: addedCount
424 }; 314 };
425 315
426 ObjectFreeze(changeRecord); 316 ObjectFreeze(changeRecord);
427 ObjectFreeze(changeRecord.removed); 317 ObjectFreeze(changeRecord.removed);
428 ObjectInfoEnqueueChangeRecord(objectInfo, changeRecord); 318 EnqueueChangeRecord(changeRecord, objectInfo.changeObservers);
429 } 319 }
430 320
431 function NotifyChange(type, object, name, oldValue) { 321 function NotifyChange(type, object, name, oldValue) {
432 var objectInfo = objectInfoMap.get(object); 322 var objectInfo = objectInfoMap.get(object);
433 if (!ObjectInfoHasActiveObservers(objectInfo)) 323 if (objectInfo.changeObservers.length === 0)
434 return; 324 return;
435 325
436 var changeRecord = (arguments.length < 4) ? 326 var changeRecord = (arguments.length < 4) ?
437 { type: type, object: object, name: name } : 327 { type: type, object: object, name: name } :
438 { type: type, object: object, name: name, oldValue: oldValue }; 328 { type: type, object: object, name: name, oldValue: oldValue };
439 ObjectFreeze(changeRecord); 329 ObjectFreeze(changeRecord);
440 ObjectInfoEnqueueChangeRecord(objectInfo, changeRecord); 330 EnqueueChangeRecord(changeRecord, objectInfo.changeObservers);
441 } 331 }
442 332
443 var notifierPrototype = {}; 333 var notifierPrototype = {};
444 334
445 function ObjectNotifierNotify(changeRecord) { 335 function ObjectNotifierNotify(changeRecord) {
446 if (!IS_SPEC_OBJECT(this)) 336 if (!IS_SPEC_OBJECT(this))
447 throw MakeTypeError("called_on_non_object", ["notify"]); 337 throw MakeTypeError("called_on_non_object", ["notify"]);
448 338
449 var objectInfo = ObjectInfoGetFromNotifier(this); 339 var target = notifierTargetMap.get(this);
450 if (IS_UNDEFINED(objectInfo)) 340 if (IS_UNDEFINED(target))
451 throw MakeTypeError("observe_notify_non_notifier"); 341 throw MakeTypeError("observe_notify_non_notifier");
452 if (!IS_STRING(changeRecord.type)) 342 if (!IS_STRING(changeRecord.type))
453 throw MakeTypeError("observe_type_non_string"); 343 throw MakeTypeError("observe_type_non_string");
454 344
455 if (!ObjectInfoHasActiveObservers(objectInfo)) 345 var objectInfo = objectInfoMap.get(target);
346 if (IS_UNDEFINED(objectInfo) || objectInfo.changeObservers.length === 0)
456 return; 347 return;
457 348
458 var newRecord = { object: ObjectInfoGetObject(objectInfo) }; 349 var newRecord = { object: target };
459 for (var prop in changeRecord) { 350 for (var prop in changeRecord) {
460 if (prop === 'object') continue; 351 if (prop === 'object') continue;
461 %DefineOrRedefineDataProperty(newRecord, prop, changeRecord[prop], 352 %DefineOrRedefineDataProperty(newRecord, prop, changeRecord[prop],
462 READ_ONLY + DONT_DELETE); 353 READ_ONLY + DONT_DELETE);
463 } 354 }
464 ObjectFreeze(newRecord); 355 ObjectFreeze(newRecord);
465 356
466 ObjectInfoEnqueueChangeRecord(objectInfo, newRecord); 357 EnqueueChangeRecord(newRecord, objectInfo.changeObservers);
467 } 358 }
468 359
469 function ObjectNotifierPerformChange(changeType, changeFn, receiver) { 360 function ObjectNotifierPerformChange(changeType, changeFn, receiver) {
470 if (!IS_SPEC_OBJECT(this)) 361 if (!IS_SPEC_OBJECT(this))
471 throw MakeTypeError("called_on_non_object", ["performChange"]); 362 throw MakeTypeError("called_on_non_object", ["performChange"]);
472 363
473 var objectInfo = ObjectInfoGetFromNotifier(this); 364 var target = notifierTargetMap.get(this);
474 365 if (IS_UNDEFINED(target))
475 if (IS_UNDEFINED(objectInfo))
476 throw MakeTypeError("observe_notify_non_notifier"); 366 throw MakeTypeError("observe_notify_non_notifier");
477 if (!IS_STRING(changeType)) 367 if (!IS_STRING(changeType))
478 throw MakeTypeError("observe_perform_non_string"); 368 throw MakeTypeError("observe_perform_non_string");
479 if (!IS_SPEC_FUNCTION(changeFn)) 369 if (!IS_SPEC_FUNCTION(changeFn))
480 throw MakeTypeError("observe_perform_non_function"); 370 throw MakeTypeError("observe_perform_non_function");
481 371
482 if (IS_NULL_OR_UNDEFINED(receiver)) { 372 if (IS_NULL_OR_UNDEFINED(receiver)) {
483 receiver = %GetDefaultReceiver(changeFn) || receiver; 373 receiver = %GetDefaultReceiver(changeFn) || receiver;
484 } else if (!IS_SPEC_OBJECT(receiver) && %IsClassicModeFunction(changeFn)) { 374 } else if (!IS_SPEC_OBJECT(receiver) && %IsClassicModeFunction(changeFn)) {
485 receiver = ToObject(receiver); 375 receiver = ToObject(receiver);
486 } 376 }
487 377
488 ObjectInfoAddPerformingType(objectInfo, changeType); 378 var objectInfo = objectInfoMap.get(target);
379 if (IS_UNDEFINED(objectInfo))
380 return;
381
382 BeginPerformChange(objectInfo, changeType);
489 try { 383 try {
490 %_CallFunction(receiver, changeFn); 384 %_CallFunction(receiver, changeFn);
491 } finally { 385 } finally {
492 ObjectInfoRemovePerformingType(objectInfo, changeType); 386 EndPerformChange(objectInfo, changeType);
493 } 387 }
494 } 388 }
495 389
496 function ObjectGetNotifier(object) { 390 function ObjectGetNotifier(object) {
497 if (!IS_SPEC_OBJECT(object)) 391 if (!IS_SPEC_OBJECT(object))
498 throw MakeTypeError("observe_non_object", ["getNotifier"]); 392 throw MakeTypeError("observe_non_object", ["getNotifier"]);
499 393
500 if (ObjectIsFrozen(object)) return null; 394 if (ObjectIsFrozen(object)) return null;
501 395
502 var objectInfo = ObjectInfoGet(object); 396 var objectInfo = objectInfoMap.get(object);
503 return ObjectInfoGetNotifier(objectInfo); 397 if (IS_UNDEFINED(objectInfo)) {
398 objectInfo = CreateObjectInfo(object);
399 %SetIsObserved(object);
400 }
401
402 if (IS_NULL(objectInfo.notifier)) {
403 objectInfo.notifier = { __proto__: notifierPrototype };
404 notifierTargetMap.set(objectInfo.notifier, object);
405 }
406
407 return objectInfo.notifier;
504 } 408 }
505 409
506 function CallbackDeliverPending(callback) { 410 function CallbackDeliverPending(callback) {
507 var callbackInfo = callbackInfoMap.get(callback); 411 var callbackInfo = callbackInfoMap.get(callback);
508 if (IS_UNDEFINED(callbackInfo) || IS_NUMBER(callbackInfo)) 412 if (IS_UNDEFINED(callbackInfo) || IS_NUMBER(callbackInfo))
509 return false; 413 return false;
510 414
511 // Clear the pending change records from callback and return it to its 415 // Clear the pending change records from callback and return it to its
512 // "optimized" state. 416 // "optimized" state.
513 var priority = callbackInfo.priority; 417 var priority = callbackInfo.priority;
514 callbackInfoMap.set(callback, priority); 418 callbackInfoMap.set(callback, priority);
515 419
516 if (observationState.pendingObservers) 420 delete observationState.pendingObservers[priority];
517 delete observationState.pendingObservers[priority];
518
519 var delivered = []; 421 var delivered = [];
520 %MoveArrayContents(callbackInfo, delivered); 422 %MoveArrayContents(callbackInfo, delivered);
521 423
522 try { 424 try {
523 %Call(void 0, delivered, callback); 425 %Call(void 0, delivered, callback);
524 } catch (ex) {} 426 } catch (ex) {}
525 return true; 427 return true;
526 } 428 }
527 429
528 function ObjectDeliverChangeRecords(callback) { 430 function ObjectDeliverChangeRecords(callback) {
529 if (!IS_SPEC_FUNCTION(callback)) 431 if (!IS_SPEC_FUNCTION(callback))
530 throw MakeTypeError("observe_non_function", ["deliverChangeRecords"]); 432 throw MakeTypeError("observe_non_function", ["deliverChangeRecords"]);
531 433
532 while (CallbackDeliverPending(callback)) {} 434 while (CallbackDeliverPending(callback)) {}
533 } 435 }
534 436
535 function DeliverChangeRecords() { 437 function DeliverChangeRecords() {
536 while (observationState.pendingObservers) { 438 while (observationState.pendingObservers.length) {
537 var pendingObservers = observationState.pendingObservers; 439 var pendingObservers = observationState.pendingObservers;
538 observationState.pendingObservers = null; 440 observationState.pendingObservers = new InternalArray;
539 for (var i in pendingObservers) { 441 for (var i in pendingObservers) {
540 CallbackDeliverPending(pendingObservers[i]); 442 CallbackDeliverPending(pendingObservers[i]);
541 } 443 }
542 } 444 }
543 } 445 }
544 446
545 function SetupObjectObserve() { 447 function SetupObjectObserve() {
546 %CheckIsBootstrapping(); 448 %CheckIsBootstrapping();
547 InstallFunctions($Object, DONT_ENUM, $Array( 449 InstallFunctions($Object, DONT_ENUM, $Array(
548 "deliverChangeRecords", ObjectDeliverChangeRecords, 450 "deliverChangeRecords", ObjectDeliverChangeRecords,
549 "getNotifier", ObjectGetNotifier, 451 "getNotifier", ObjectGetNotifier,
550 "observe", ObjectObserve, 452 "observe", ObjectObserve,
551 "unobserve", ObjectUnobserve 453 "unobserve", ObjectUnobserve
552 )); 454 ));
553 InstallFunctions($Array, DONT_ENUM, $Array( 455 InstallFunctions($Array, DONT_ENUM, $Array(
554 "observe", ArrayObserve, 456 "observe", ArrayObserve,
555 "unobserve", ArrayUnobserve 457 "unobserve", ArrayUnobserve
556 )); 458 ));
557 InstallFunctions(notifierPrototype, DONT_ENUM, $Array( 459 InstallFunctions(notifierPrototype, DONT_ENUM, $Array(
558 "notify", ObjectNotifierNotify, 460 "notify", ObjectNotifierNotify,
559 "performChange", ObjectNotifierPerformChange 461 "performChange", ObjectNotifierPerformChange
560 )); 462 ));
561 } 463 }
562 464
563 SetupObjectObserve(); 465 SetupObjectObserve();
OLDNEW
« no previous file with comments | « no previous file | test/cctest/test-object-observe.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698