OLD | NEW |
---|---|
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 |
(...skipping 16 matching lines...) Expand all Loading... | |
27 | 27 |
28 "use strict"; | 28 "use strict"; |
29 | 29 |
30 var InternalObjectIsFrozen = $Object.isFrozen; | 30 var InternalObjectIsFrozen = $Object.isFrozen; |
31 var InternalObjectFreeze = $Object.freeze; | 31 var InternalObjectFreeze = $Object.freeze; |
32 | 32 |
33 var observationState = %GetObservationState(); | 33 var observationState = %GetObservationState(); |
34 if (IS_UNDEFINED(observationState.observerInfoMap)) { | 34 if (IS_UNDEFINED(observationState.observerInfoMap)) { |
35 observationState.observerInfoMap = %CreateObjectHashTable(); | 35 observationState.observerInfoMap = %CreateObjectHashTable(); |
36 observationState.objectInfoMap = %CreateObjectHashTable(); | 36 observationState.objectInfoMap = %CreateObjectHashTable(); |
37 observationState.notifierTargetMap = %CreateObjectHashTable(); | |
37 observationState.activeObservers = new InternalArray; | 38 observationState.activeObservers = new InternalArray; |
38 observationState.observerPriority = 0; | 39 observationState.observerPriority = 0; |
39 } | 40 } |
40 | 41 |
41 function InternalObjectHashTable(tableName) { | 42 function InternalObjectHashTable(tableName) { |
42 this.tableName = tableName; | 43 this.tableName = tableName; |
43 } | 44 } |
44 | 45 |
45 InternalObjectHashTable.prototype = { | 46 InternalObjectHashTable.prototype = { |
46 get: function(key) { | 47 get: function(key) { |
47 return %ObjectHashTableGet(observationState[this.tableName], key); | 48 return %ObjectHashTableGet(observationState[this.tableName], key); |
48 }, | 49 }, |
49 set: function(key, value) { | 50 set: function(key, value) { |
50 observationState[this.tableName] = | 51 observationState[this.tableName] = |
51 %ObjectHashTableSet(observationState[this.tableName], key, value); | 52 %ObjectHashTableSet(observationState[this.tableName], key, value); |
52 }, | 53 }, |
53 has: function(key) { | 54 has: function(key) { |
54 return %ObjectHashTableHas(observationState[this.tableName], key); | 55 return %ObjectHashTableHas(observationState[this.tableName], key); |
55 } | 56 } |
56 }; | 57 }; |
57 | 58 |
58 var observerInfoMap = new InternalObjectHashTable('observerInfoMap'); | 59 var observerInfoMap = new InternalObjectHashTable('observerInfoMap'); |
59 var objectInfoMap = new InternalObjectHashTable('objectInfoMap'); | 60 var objectInfoMap = new InternalObjectHashTable('objectInfoMap'); |
61 var notifierTargetMap = new InternalObjectHashTable('notifierTargetMap'); | |
62 | |
63 function CreateObjectInfo(object) { | |
64 var info = { | |
65 changeObservers: new InternalArray, | |
rossberg
2012/11/09 14:41:25
Nit: can we perhaps rename that property to just "
adamk
2012/11/09 14:48:41
It's named like this to match the spec's [[ChangeO
| |
66 notifier: null, | |
67 }; | |
68 objectInfoMap.set(object, info); | |
69 return info; | |
70 } | |
60 | 71 |
61 function ObjectObserve(object, callback) { | 72 function ObjectObserve(object, callback) { |
62 if (!IS_SPEC_OBJECT(object)) | 73 if (!IS_SPEC_OBJECT(object)) |
63 throw MakeTypeError("observe_non_object", ["observe"]); | 74 throw MakeTypeError("observe_non_object", ["observe"]); |
64 if (!IS_SPEC_FUNCTION(callback)) | 75 if (!IS_SPEC_FUNCTION(callback)) |
65 throw MakeTypeError("observe_non_function", ["observe"]); | 76 throw MakeTypeError("observe_non_function", ["observe"]); |
66 if (InternalObjectIsFrozen(callback)) | 77 if (InternalObjectIsFrozen(callback)) |
67 throw MakeTypeError("observe_callback_frozen"); | 78 throw MakeTypeError("observe_callback_frozen"); |
68 | 79 |
69 if (!observerInfoMap.has(callback)) { | 80 if (!observerInfoMap.has(callback)) { |
70 observerInfoMap.set(callback, { | 81 observerInfoMap.set(callback, { |
71 pendingChangeRecords: null, | 82 pendingChangeRecords: null, |
72 priority: observationState.observerPriority++, | 83 priority: observationState.observerPriority++, |
73 }); | 84 }); |
74 } | 85 } |
75 | 86 |
76 var objectInfo = objectInfoMap.get(object); | 87 var objectInfo = objectInfoMap.get(object); |
77 if (IS_UNDEFINED(objectInfo)) { | 88 if (IS_UNDEFINED(objectInfo)) { |
78 // TODO: setup objectInfo.notifier | 89 objectInfo = CreateObjectInfo(object); |
79 objectInfo = { | |
80 changeObservers: new InternalArray(callback) | |
81 }; | |
82 objectInfoMap.set(object, objectInfo); | |
83 %SetIsObserved(object, true); | 90 %SetIsObserved(object, true); |
84 return; | |
85 } | 91 } |
86 | 92 |
87 var changeObservers = objectInfo.changeObservers; | 93 var changeObservers = objectInfo.changeObservers; |
88 if (changeObservers.indexOf(callback) >= 0) | 94 if (changeObservers.indexOf(callback) >= 0) |
89 return; | 95 return; |
90 | 96 |
91 changeObservers.push(callback); | 97 changeObservers.push(callback); |
92 } | 98 } |
93 | 99 |
94 function ObjectUnobserve(object, callback) { | 100 function ObjectUnobserve(object, callback) { |
(...skipping 28 matching lines...) Expand all Loading... | |
123 | 129 |
124 function NotifyChange(type, object, name, oldValue) { | 130 function NotifyChange(type, object, name, oldValue) { |
125 var objectInfo = objectInfoMap.get(object); | 131 var objectInfo = objectInfoMap.get(object); |
126 var changeRecord = (arguments.length < 4) ? | 132 var changeRecord = (arguments.length < 4) ? |
127 { type: type, object: object, name: name } : | 133 { type: type, object: object, name: name } : |
128 { type: type, object: object, name: name, oldValue: oldValue }; | 134 { type: type, object: object, name: name, oldValue: oldValue }; |
129 InternalObjectFreeze(changeRecord); | 135 InternalObjectFreeze(changeRecord); |
130 EnqueueChangeRecord(changeRecord, objectInfo.changeObservers); | 136 EnqueueChangeRecord(changeRecord, objectInfo.changeObservers); |
131 } | 137 } |
132 | 138 |
133 function ObjectNotify(object, changeRecord) { | 139 var notifierPrototype = {}; |
134 // TODO: notifier needs to be [[THIS]] | 140 |
141 function ObjectNotifierNotify(changeRecord) { | |
142 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) | |
143 throw MakeTypeError("called_on_null_or_undefined", ["notify"]); | |
144 | |
145 var target = notifierTargetMap.get(this); | |
146 if (IS_UNDEFINED(target)) | |
147 return; | |
rossberg
2012/11/09 14:41:25
This is calling the notify method on something tha
adamk
2012/11/09 14:48:41
Here's what arv said about this when I asked him a
rossberg
2012/11/09 15:06:17
Hm, I don't buy that -- I think "nominal typing" i
arv (Not doing code reviews)
2012/11/09 15:22:22
I don't remember my logic.
| |
148 | |
135 if (!IS_STRING(changeRecord.type)) | 149 if (!IS_STRING(changeRecord.type)) |
136 throw MakeTypeError("observe_type_non_string"); | 150 throw MakeTypeError("observe_type_non_string"); |
137 | 151 |
138 var objectInfo = objectInfoMap.get(object); | 152 var objectInfo = objectInfoMap.get(target); |
139 if (IS_UNDEFINED(objectInfo)) | 153 if (IS_UNDEFINED(objectInfo)) |
140 return; | 154 return; |
141 | 155 |
156 if (!objectInfo.changeObservers.length) | |
157 return; | |
158 | |
142 var newRecord = { | 159 var newRecord = { |
143 object: object // TODO: Needs to be 'object' retrieved from notifier | 160 object: target |
144 }; | 161 }; |
145 for (var prop in changeRecord) { | 162 for (var prop in changeRecord) { |
146 if (prop === 'object') | 163 if (prop === 'object') |
147 continue; | 164 continue; |
148 newRecord[prop] = changeRecord[prop]; | 165 newRecord[prop] = changeRecord[prop]; |
149 } | 166 } |
150 InternalObjectFreeze(newRecord); | 167 InternalObjectFreeze(newRecord); |
151 | 168 |
152 EnqueueChangeRecord(newRecord, objectInfo.changeObservers); | 169 EnqueueChangeRecord(newRecord, objectInfo.changeObservers); |
153 } | 170 } |
154 | 171 |
172 function ObjectGetNotifier(object) { | |
173 if (!IS_SPEC_OBJECT(object)) | |
174 throw MakeTypeError("observe_non_object", ["getNotifier"]); | |
175 | |
176 if (InternalObjectIsFrozen(object)) | |
177 return null; | |
178 | |
179 var objectInfo = objectInfoMap.get(object); | |
180 if (IS_UNDEFINED(objectInfo)) | |
181 objectInfo = CreateObjectInfo(object); | |
182 | |
183 if (IS_NULL(objectInfo.notifier)) { | |
184 objectInfo.notifier = { | |
185 __proto__: notifierPrototype | |
186 }; | |
187 notifierTargetMap.set(objectInfo.notifier, object); | |
188 } | |
189 | |
190 return objectInfo.notifier; | |
191 } | |
192 | |
155 function DeliverChangeRecordsForObserver(observer) { | 193 function DeliverChangeRecordsForObserver(observer) { |
156 var observerInfo = observerInfoMap.get(observer); | 194 var observerInfo = observerInfoMap.get(observer); |
157 if (IS_UNDEFINED(observerInfo)) | 195 if (IS_UNDEFINED(observerInfo)) |
158 return; | 196 return; |
159 | 197 |
160 var pendingChangeRecords = observerInfo.pendingChangeRecords; | 198 var pendingChangeRecords = observerInfo.pendingChangeRecords; |
161 if (IS_NULL(pendingChangeRecords)) | 199 if (IS_NULL(pendingChangeRecords)) |
162 return; | 200 return; |
163 | 201 |
164 observerInfo.pendingChangeRecords = null; | 202 observerInfo.pendingChangeRecords = null; |
(...skipping 18 matching lines...) Expand all Loading... | |
183 for (var i in activeObservers) { | 221 for (var i in activeObservers) { |
184 DeliverChangeRecordsForObserver(activeObservers[i]); | 222 DeliverChangeRecordsForObserver(activeObservers[i]); |
185 } | 223 } |
186 } | 224 } |
187 } | 225 } |
188 | 226 |
189 function SetupObjectObserve() { | 227 function SetupObjectObserve() { |
190 %CheckIsBootstrapping(); | 228 %CheckIsBootstrapping(); |
191 InstallFunctions($Object, DONT_ENUM, $Array( | 229 InstallFunctions($Object, DONT_ENUM, $Array( |
192 "deliverChangeRecords", ObjectDeliverChangeRecords, | 230 "deliverChangeRecords", ObjectDeliverChangeRecords, |
193 "notify", ObjectNotify, // TODO: Remove when getNotifier is implemented. | 231 "getNotifier", ObjectGetNotifier, |
194 "observe", ObjectObserve, | 232 "observe", ObjectObserve, |
195 "unobserve", ObjectUnobserve | 233 "unobserve", ObjectUnobserve |
196 )); | 234 )); |
235 InstallFunctions(notifierPrototype, DONT_ENUM, $Array( | |
236 "notify", ObjectNotifierNotify | |
237 )); | |
197 } | 238 } |
198 | 239 |
199 SetupObjectObserve(); | 240 SetupObjectObserve(); |
OLD | NEW |