blob: b35f547eda0b89f5a1530e39d552f8f86b55e601 [file] [log] [blame]
danno@chromium.org72204d52012-10-31 10:02:10 +00001// Copyright 2012 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
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.
27
28"use strict";
29
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +000030var observationState = %GetObservationState();
31if (IS_UNDEFINED(observationState.observerInfoMap)) {
mmassi@chromium.org2f0efde2013-02-06 14:12:58 +000032 observationState.observerInfoMap = %ObservationWeakMapCreate();
33 observationState.objectInfoMap = %ObservationWeakMapCreate();
34 observationState.notifierTargetMap = %ObservationWeakMapCreate();
yangguo@chromium.orgfb377212012-11-16 14:43:43 +000035 observationState.pendingObservers = new InternalArray;
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +000036 observationState.observerPriority = 0;
danno@chromium.org72204d52012-10-31 10:02:10 +000037}
38
mmassi@chromium.org2f0efde2013-02-06 14:12:58 +000039function ObservationWeakMap(map) {
40 this.map_ = map;
danno@chromium.org72204d52012-10-31 10:02:10 +000041}
42
mmassi@chromium.org2f0efde2013-02-06 14:12:58 +000043ObservationWeakMap.prototype = {
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +000044 get: function(key) {
mmassi@chromium.org2f0efde2013-02-06 14:12:58 +000045 key = %UnwrapGlobalProxy(key);
46 if (!IS_SPEC_OBJECT(key)) return void 0;
47 return %WeakMapGet(this.map_, key);
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +000048 },
49 set: function(key, value) {
mmassi@chromium.org2f0efde2013-02-06 14:12:58 +000050 key = %UnwrapGlobalProxy(key);
51 if (!IS_SPEC_OBJECT(key)) return void 0;
52 %WeakMapSet(this.map_, key, value);
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +000053 },
54 has: function(key) {
mstarzinger@chromium.org32280cf2012-12-06 17:32:37 +000055 return !IS_UNDEFINED(this.get(key));
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +000056 }
57};
58
mmassi@chromium.org2f0efde2013-02-06 14:12:58 +000059var observerInfoMap =
60 new ObservationWeakMap(observationState.observerInfoMap);
61var objectInfoMap = new ObservationWeakMap(observationState.objectInfoMap);
62var notifierTargetMap =
63 new ObservationWeakMap(observationState.notifierTargetMap);
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +000064
65function CreateObjectInfo(object) {
66 var info = {
67 changeObservers: new InternalArray,
68 notifier: null,
69 };
70 objectInfoMap.set(object, info);
71 return info;
72}
danno@chromium.org72204d52012-10-31 10:02:10 +000073
74function ObjectObserve(object, callback) {
75 if (!IS_SPEC_OBJECT(object))
76 throw MakeTypeError("observe_non_object", ["observe"]);
77 if (!IS_SPEC_FUNCTION(callback))
78 throw MakeTypeError("observe_non_function", ["observe"]);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +000079 if (ObjectIsFrozen(callback))
danno@chromium.org72204d52012-10-31 10:02:10 +000080 throw MakeTypeError("observe_callback_frozen");
81
82 if (!observerInfoMap.has(callback)) {
danno@chromium.org72204d52012-10-31 10:02:10 +000083 observerInfoMap.set(callback, {
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +000084 pendingChangeRecords: null,
85 priority: observationState.observerPriority++,
danno@chromium.org72204d52012-10-31 10:02:10 +000086 });
87 }
88
89 var objectInfo = objectInfoMap.get(object);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000090 if (IS_UNDEFINED(objectInfo)) objectInfo = CreateObjectInfo(object);
91 %SetIsObserved(object, true);
danno@chromium.org72204d52012-10-31 10:02:10 +000092
93 var changeObservers = objectInfo.changeObservers;
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000094 if (changeObservers.indexOf(callback) < 0) changeObservers.push(callback);
danno@chromium.org72204d52012-10-31 10:02:10 +000095
ulan@chromium.org8e8d8822012-11-23 14:36:46 +000096 return object;
danno@chromium.org72204d52012-10-31 10:02:10 +000097}
98
99function ObjectUnobserve(object, callback) {
100 if (!IS_SPEC_OBJECT(object))
101 throw MakeTypeError("observe_non_object", ["unobserve"]);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000102 if (!IS_SPEC_FUNCTION(callback))
103 throw MakeTypeError("observe_non_function", ["unobserve"]);
danno@chromium.org72204d52012-10-31 10:02:10 +0000104
105 var objectInfo = objectInfoMap.get(object);
106 if (IS_UNDEFINED(objectInfo))
ulan@chromium.org8e8d8822012-11-23 14:36:46 +0000107 return object;
danno@chromium.org72204d52012-10-31 10:02:10 +0000108
109 var changeObservers = objectInfo.changeObservers;
110 var index = changeObservers.indexOf(callback);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000111 if (index >= 0) {
ulan@chromium.org8e8d8822012-11-23 14:36:46 +0000112 changeObservers.splice(index, 1);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000113 if (changeObservers.length === 0) %SetIsObserved(object, false);
114 }
danno@chromium.org72204d52012-10-31 10:02:10 +0000115
ulan@chromium.org8e8d8822012-11-23 14:36:46 +0000116 return object;
danno@chromium.org72204d52012-10-31 10:02:10 +0000117}
118
119function EnqueueChangeRecord(changeRecord, observers) {
120 for (var i = 0; i < observers.length; i++) {
121 var observer = observers[i];
122 var observerInfo = observerInfoMap.get(observer);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000123 observationState.pendingObservers[observerInfo.priority] = observer;
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +0000124 %SetObserverDeliveryPending();
danno@chromium.org72204d52012-10-31 10:02:10 +0000125 if (IS_NULL(observerInfo.pendingChangeRecords)) {
126 observerInfo.pendingChangeRecords = new InternalArray(changeRecord);
127 } else {
128 observerInfo.pendingChangeRecords.push(changeRecord);
129 }
130 }
131}
132
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +0000133function NotifyChange(type, object, name, oldValue) {
134 var objectInfo = objectInfoMap.get(object);
135 var changeRecord = (arguments.length < 4) ?
136 { type: type, object: object, name: name } :
137 { type: type, object: object, name: name, oldValue: oldValue };
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000138 ObjectFreeze(changeRecord);
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +0000139 EnqueueChangeRecord(changeRecord, objectInfo.changeObservers);
140}
141
142var notifierPrototype = {};
143
144function ObjectNotifierNotify(changeRecord) {
145 if (!IS_SPEC_OBJECT(this))
146 throw MakeTypeError("called_on_non_object", ["notify"]);
147
148 var target = notifierTargetMap.get(this);
149 if (IS_UNDEFINED(target))
150 throw MakeTypeError("observe_notify_non_notifier");
danno@chromium.org72204d52012-10-31 10:02:10 +0000151 if (!IS_STRING(changeRecord.type))
152 throw MakeTypeError("observe_type_non_string");
153
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +0000154 var objectInfo = objectInfoMap.get(target);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000155 if (IS_UNDEFINED(objectInfo) || objectInfo.changeObservers.length === 0)
danno@chromium.org72204d52012-10-31 10:02:10 +0000156 return;
157
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000158 var newRecord = { object: target };
danno@chromium.org72204d52012-10-31 10:02:10 +0000159 for (var prop in changeRecord) {
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000160 if (prop === 'object') continue;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000161 %DefineOrRedefineDataProperty(newRecord, prop, changeRecord[prop],
162 READ_ONLY + DONT_DELETE);
danno@chromium.org72204d52012-10-31 10:02:10 +0000163 }
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000164 ObjectFreeze(newRecord);
danno@chromium.org72204d52012-10-31 10:02:10 +0000165
166 EnqueueChangeRecord(newRecord, objectInfo.changeObservers);
167}
168
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +0000169function ObjectGetNotifier(object) {
170 if (!IS_SPEC_OBJECT(object))
171 throw MakeTypeError("observe_non_object", ["getNotifier"]);
danno@chromium.org72204d52012-10-31 10:02:10 +0000172
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000173 if (ObjectIsFrozen(object)) return null;
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +0000174
175 var objectInfo = objectInfoMap.get(object);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000176 if (IS_UNDEFINED(objectInfo)) objectInfo = CreateObjectInfo(object);
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +0000177
178 if (IS_NULL(objectInfo.notifier)) {
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000179 objectInfo.notifier = { __proto__: notifierPrototype };
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +0000180 notifierTargetMap.set(objectInfo.notifier, object);
181 }
182
183 return objectInfo.notifier;
184}
185
186function DeliverChangeRecordsForObserver(observer) {
187 var observerInfo = observerInfoMap.get(observer);
danno@chromium.org72204d52012-10-31 10:02:10 +0000188 if (IS_UNDEFINED(observerInfo))
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000189 return false;
danno@chromium.org72204d52012-10-31 10:02:10 +0000190
191 var pendingChangeRecords = observerInfo.pendingChangeRecords;
192 if (IS_NULL(pendingChangeRecords))
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000193 return false;
danno@chromium.org72204d52012-10-31 10:02:10 +0000194
195 observerInfo.pendingChangeRecords = null;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000196 delete observationState.pendingObservers[observerInfo.priority];
danno@chromium.org72204d52012-10-31 10:02:10 +0000197 var delivered = [];
198 %MoveArrayContents(pendingChangeRecords, delivered);
199 try {
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +0000200 %Call(void 0, delivered, observer);
danno@chromium.org72204d52012-10-31 10:02:10 +0000201 } catch (ex) {}
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000202 return true;
danno@chromium.org72204d52012-10-31 10:02:10 +0000203}
204
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +0000205function ObjectDeliverChangeRecords(callback) {
206 if (!IS_SPEC_FUNCTION(callback))
207 throw MakeTypeError("observe_non_function", ["deliverChangeRecords"]);
208
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000209 while (DeliverChangeRecordsForObserver(callback)) {}
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +0000210}
211
212function DeliverChangeRecords() {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000213 while (observationState.pendingObservers.length) {
214 var pendingObservers = observationState.pendingObservers;
215 observationState.pendingObservers = new InternalArray;
216 for (var i in pendingObservers) {
217 DeliverChangeRecordsForObserver(pendingObservers[i]);
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +0000218 }
219 }
220}
221
danno@chromium.org72204d52012-10-31 10:02:10 +0000222function SetupObjectObserve() {
223 %CheckIsBootstrapping();
224 InstallFunctions($Object, DONT_ENUM, $Array(
225 "deliverChangeRecords", ObjectDeliverChangeRecords,
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +0000226 "getNotifier", ObjectGetNotifier,
danno@chromium.org72204d52012-10-31 10:02:10 +0000227 "observe", ObjectObserve,
228 "unobserve", ObjectUnobserve
229 ));
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +0000230 InstallFunctions(notifierPrototype, DONT_ENUM, $Array(
231 "notify", ObjectNotifierNotify
232 ));
danno@chromium.org72204d52012-10-31 10:02:10 +0000233}
234
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +0000235SetupObjectObserve();