Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 part of serialization; | |
| 6 | |
| 7 /** | |
| 8 * The abstract superclass for serialization rules. | |
| 9 */ | |
| 10 abstract class SerializationRule { | |
| 11 /** | |
| 12 * Rules belong uniquely to a particular Serialization instance, and can | |
| 13 * be identified within it by number. | |
| 14 */ | |
| 15 int number; | |
|
Jennifer Messerly
2012/11/20 01:20:47
Is this the index of the rule? I can't recall how
Alan Knight
2012/11/20 12:17:54
I think that would be quite tricky. I definitely w
| |
| 16 | |
| 17 /** Return true if this rule applies to this object, false otherwise. */ | |
| 18 bool appliesTo(object); | |
| 19 | |
| 20 /** | |
| 21 * This extracts the state from the object, calling [f] for each value | |
| 22 * as it is extracted, and returning an object representing the whole | |
| 23 * state at the end. The state that results will still have direct | |
| 24 * pointers to objects, rather than references. | |
| 25 */ | |
| 26 Object extractState(object, void f(value)); | |
| 27 | |
| 28 /** | |
| 29 * Given the variables representing the state of an object, flatten it | |
| 30 * by turning object pointers into Reference objects where needed. This | |
| 31 * destructively modifies the state object. | |
| 32 * | |
| 33 * This has a default implementation which assumes that object is indexable, | |
| 34 * so either conforms to Map or List. Subclasses may override to do something | |
| 35 * different. | |
| 36 */ | |
| 37 // This has to be a separate operation from extracting, because we extract | |
| 38 // as we are traversing the objects, so we don't yet have the objects to | |
| 39 // generate references for them. It might be possible to avoid that by | |
| 40 // doing a depth-first rather than breadth-first traversal, but I'm not | |
| 41 // sure it's worth it. | |
| 42 void flatten(state, Writer writer) { | |
| 43 keysAndValues(state).forEach((key, value) { | |
| 44 var reference = writer._referenceFor(value); | |
| 45 state[key] = (reference == null) ? value : reference; | |
| 46 }); | |
| 47 } | |
| 48 | |
| 49 /** Return true if this rule should only be applied when we are the first | |
| 50 * rule found that applies to this object. This may or may not be a hack | |
| 51 * that will disappear once we have better support for multiple rules. | |
| 52 * We want to have multiple different rules that apply to the same object. We | |
| 53 * also want to have multiple different rules that might exclusively apply | |
| 54 * to the same object. So, we want either ListRule or ListRuleEssential, and | |
| 55 * only one of them can be there. But on the other hand, we may want both | |
| 56 * ListRule and BasicRule. So we identify the kinds of rules that can share. | |
| 57 * If mustBePrimary returns true, then this rule will only be chosen if no | |
| 58 * other rule has been found yet. This means that the ordering of rules in | |
| 59 * the serialization is significant, which is unpleasant, but we'll have | |
| 60 * to see how bad it is. | |
| 61 */ | |
| 62 // TODO(alanknight): Reconsider whether this should be handled differently. | |
| 63 get mustBePrimary => false; | |
| 64 | |
| 65 /** | |
| 66 * Create the new object corresponding to [state] using the rules | |
| 67 * from [reader]. This may involve recursively inflating "essential" | |
| 68 * references in the state, which are those that are required for the | |
| 69 * object's constructor. It is up to the rule what state is considered | |
| 70 * essential. | |
| 71 */ | |
| 72 inflateEssential(state, reader); | |
|
Jennifer Messerly
2012/11/20 01:20:47
Apologies if I mentioned this before -- I find "es
Alan Knight
2012/11/20 12:17:54
I made up the terminology, and we can certainly ch
| |
| 73 | |
| 74 /** | |
| 75 * The [object] has already been created. Set any of its non-essential | |
| 76 * variables from the representation in [state]. Where there are references | |
| 77 * to other objects they are resolved in the context of [reader]. | |
| 78 */ | |
| 79 inflateNonEssential(state, object, Reader reader); | |
| 80 | |
| 81 /** | |
| 82 * If we have an object [o] as part of our state, should we represent that | |
| 83 * directly, or should we make a reference for it. By default we use a | |
| 84 * reference for everything. | |
| 85 */ | |
| 86 bool shouldUseReferenceFor(Object o, Writer w) => true; | |
| 87 | |
| 88 /** | |
| 89 * This writes the data from our internal representation into a List. | |
| 90 * It is used in order to write to a flat format, and is likely to be | |
| 91 * folded into a more general mechanism for supporting different output | |
| 92 * formats. | |
| 93 */ | |
| 94 // TODO(alanknight): This really shouldn't exist, but is a temporary measure | |
| 95 // for writing to a a flat format until that's more fleshed out. It takes | |
| 96 // the internal representation of the rule's state, which is particularly | |
| 97 // bad. The default implementation treats the ruleData as a List of Lists | |
| 98 // of references. | |
| 99 void dumpStateInto(List ruleData, List target) { | |
| 100 // Needing the intermediate is also bad for performance, but tricky | |
| 101 // to do otherwise without a mechanism to precalculate the size. | |
| 102 var intermediate = new List(); | |
| 103 var totalLength = 0; | |
| 104 for (var eachList in ruleData) { | |
| 105 // TODO(alanknight): Abstract this out better, this really won't scale. | |
| 106 if (this is ListRule) | |
| 107 intermediate.add(eachList.length); | |
| 108 for (var eachRef in eachList) { | |
| 109 if (eachRef == null) { | |
| 110 intermediate..add(null)..add(null); | |
| 111 } else { | |
| 112 eachRef.writeToList(intermediate); | |
| 113 } | |
| 114 } | |
| 115 } | |
| 116 target.addAll(intermediate); | |
| 117 } | |
| 118 | |
| 119 /** | |
| 120 * The inverse of dumpStateInto, this reads the rule's state from an | |
| 121 * iterator in a flat format. | |
| 122 */ | |
| 123 pullStateFrom(Iterator stream); | |
| 124 } | |
| 125 | |
| 126 /** | |
| 127 * This rule handles things that implement List. It will recreate them as | |
| 128 * whatever the default implemenation of List is on the target platform. | |
| 129 */ | |
| 130 class ListRule extends SerializationRule { | |
| 131 | |
| 132 appliesTo(object) => object is List; | |
| 133 | |
| 134 state(List list) => new List.from(list); | |
| 135 | |
| 136 List extractState(List list, f) { | |
| 137 var result = new List(); | |
| 138 for (var each in list) { | |
| 139 result.add(each); | |
| 140 f(each); | |
| 141 } | |
| 142 return result; | |
| 143 } | |
| 144 | |
| 145 inflateEssential(List state, Reader r) => new List(); | |
| 146 | |
| 147 // For a list, we consider all of its state non-essential and add it | |
| 148 // after creation. | |
| 149 inflateNonEssential(List state, List newList, Reader r) { | |
| 150 populateContents(state, newList, r); | |
| 151 } | |
| 152 | |
| 153 void populateContents(List state, List newList, Reader r) { | |
| 154 for(var each in state) { | |
| 155 newList.add(r.inflateReference(each)); | |
| 156 } | |
| 157 } | |
| 158 | |
| 159 /** | |
| 160 * When reading from a flat format we are given [stream] and need to pull as | |
| 161 * much data from it as we need. Our format is that we have an integer N | |
| 162 * indicating the number of objects and then for each object a length M, | |
| 163 * and then M references, where a reference is stored in the stream as two | |
| 164 * integers. Or, in the special case of null, two nulls. | |
| 165 */ | |
| 166 pullStateFrom(Iterator stream) { | |
| 167 // TODO(alanknight): This is much too close to the basicRule implementation, | |
| 168 // and I'd refactor them if I didn't think this whole mechanism needed to | |
| 169 // change soon. | |
| 170 var dataLength = stream.next(); | |
| 171 var ruleData = new List(); | |
| 172 for (var i = 0; i < dataLength; i++) { | |
| 173 var subLength = stream.next(); | |
| 174 var subList = new List(); | |
| 175 ruleData.add(subList); | |
| 176 for (var j = 0; j < subLength; j++) { | |
| 177 var a = stream.next(); | |
| 178 var b = stream.next(); | |
| 179 if (!(a is int)) { | |
| 180 // This wasn't a reference, just use the first object as a literal. | |
| 181 // particularly used for the case of null. | |
| 182 subList.add(a); | |
| 183 } else { | |
| 184 subList.add(new Reference(this, a, b)); | |
| 185 } | |
| 186 } | |
| 187 } | |
| 188 return ruleData; | |
| 189 } | |
| 190 } | |
| 191 | |
| 192 /** | |
| 193 * This is a subclass of ListRule where all of the list's contents are | |
| 194 * considered essential state. This is needed if an object X contains a List L, | |
| 195 * but it expects L's contents to be fixed when X's constructor is called. | |
| 196 */ | |
| 197 class ListRuleEssential extends ListRule { | |
| 198 | |
| 199 /** Create the new List and also inflate all of its contents. */ | |
| 200 inflateEssential(List state, Reader r) { | |
| 201 var object = super.inflateEssential(state, r); | |
| 202 populateContents(state, object, r); | |
| 203 return object; | |
| 204 } | |
| 205 | |
| 206 /** Does nothing, because all the work has been done in inflateEssential. */ | |
| 207 inflateNonEssential(state, newList, reader) {} | |
| 208 | |
| 209 bool get mustBePrimary => true; | |
| 210 } | |
| 211 | |
| 212 /** | |
| 213 * This rule handles primitive types, defined as those that we can normally | |
| 214 * represent directly in the output format. We hard-code that to mean | |
| 215 * num, String, and bool. | |
| 216 */ | |
| 217 class PrimitiveRule extends SerializationRule { | |
| 218 appliesTo(object) { | |
| 219 return isPrimitive(object); | |
| 220 } | |
| 221 extractState(object, Function f) => object; | |
| 222 void flatten(object, Writer writer) {} | |
| 223 inflateEssential(state, Reader r) => state; | |
| 224 inflateNonEssential(object, _, Reader r) {} | |
| 225 | |
| 226 /** Indicate whether we should save pointers to this object as references | |
| 227 * or store the object directly. For primitives this depends on the format, | |
| 228 * so we delegate to the writer. | |
| 229 */ | |
| 230 bool shouldUseReferenceFor(Object o, Writer w) => | |
| 231 w.shouldUseReferencesForPrimitives; | |
| 232 | |
| 233 /** | |
| 234 * This writes the data from our internal representation into a List. | |
| 235 * It is used in order to write to a flat format, and is likely to be | |
| 236 * folded into a more general mechanism for supporting different output | |
| 237 * formats. For primitives, the ruleData is our list of all the | |
| 238 * primitives and just add it into the target. | |
| 239 */ | |
| 240 void dumpStateInto(List ruleData, List target) { | |
| 241 target.addAll(ruleData); | |
| 242 } | |
| 243 | |
| 244 /** | |
| 245 * When reading from a flat format we are given [stream] and need to pull as | |
| 246 * much data from it as we need. Our format is that we have an integer N | |
| 247 * indicating the number of objects and then N simple objects. | |
| 248 */ | |
| 249 pullStateFrom(Iterator stream) { | |
| 250 var dataLength = stream.next(); | |
| 251 var ruleData = new List(); | |
| 252 for (var i = 0; i < dataLength; i++) { | |
| 253 ruleData.add(stream.next()); | |
| 254 } | |
| 255 return ruleData; | |
| 256 } | |
| 257 } | |
| 258 | |
| 259 /** Helper function for PrimitiveRule to tell which objects it applies to. */ | |
| 260 bool isPrimitive(Object object) { | |
| 261 return (object is num) || (object is String) || (object is bool); | |
|
Jennifer Messerly
2012/11/20 01:20:47
personally I wouldn't use parens here. C++ precede
Alan Knight
2012/11/20 12:17:54
Done.
| |
| 262 } | |
| 263 | |
| 264 /** Typedef for the object construction closure used in ClosureToMapRule. */ | |
| 265 typedef Object ConstructType(Map); | |
| 266 | |
| 267 /** Typedef for the state-getting closure used in ClosureToMapRule. */ | |
| 268 typedef Map<String, Object> GetStateType(Object); | |
| 269 | |
| 270 /** Typedef for the state-setting closure used in ClosureToMapRule. */ | |
| 271 typedef void NonEssentialStateType(Object, Map); | |
| 272 | |
| 273 /** | |
| 274 * This is a rule where the extraction and creation are hard-coded as | |
| 275 * closures. The result is expected to be a map indexed by field name. | |
| 276 */ | |
| 277 class ClosureToMapRule extends SerializationRule { | |
| 278 | |
| 279 /** The runtimeType of objects that this rule applies to. Used in appliesTo.*/ | |
| 280 Type type; | |
|
Jennifer Messerly
2012/11/20 01:20:47
final?
Alan Knight
2012/11/20 12:17:54
Done.
| |
| 281 | |
| 282 /** The function for constructing new objects when reading. */ | |
| 283 ConstructType construct; | |
| 284 | |
| 285 /** The function for returning an object's state as a Map. */ | |
| 286 GetStateType getState; | |
| 287 | |
| 288 /** The function for setting an object's state from a Map. */ | |
| 289 NonEssentialStateType setNonEssentialState; | |
| 290 | |
| 291 /** | |
| 292 * Create a ClosureToMapRule for the given [type] which gets an object's | |
| 293 * state by calling [getState], creates a new object by calling [construct] | |
| 294 * and sets the new object's state by calling [setNonEssentialState]. | |
| 295 */ | |
| 296 ClosureToMapRule(this.type, this.getState, this.construct, | |
| 297 this.setNonEssentialState); | |
| 298 | |
| 299 /** | |
| 300 * If we deserialize a ClosureToMapRule we can't actually use it, because | |
| 301 * we don't have the closures, so generate a stub that just returns the | |
| 302 * raw state object. | |
| 303 */ | |
| 304 ClosureToMapRule.stub(this.type) { | |
| 305 getState = (x) { throw new SerializationException( | |
| 306 'Closures cannot be serialized'); }; | |
| 307 construct = (state) => state; | |
| 308 setNonEssentialState = (object, state) {}; | |
| 309 } | |
| 310 | |
| 311 bool appliesTo(object) => object.runtimeType == type; | |
| 312 | |
| 313 extractState(object, Function f) { | |
| 314 Map state = getState(object); | |
| 315 values(state).forEach(f); | |
| 316 return state; | |
| 317 } | |
| 318 | |
| 319 // TODO(alanknight): We're inflating twice here. How to avoid doing | |
| 320 // that without giving the user even more stuff to specify. | |
| 321 // Worse than that, by inflating everything in advance, we are are | |
| 322 // forcing all the state to be essential. | |
| 323 Object inflateEssential(Map<String, Object> state, Reader r) { | |
| 324 var inflated = values(state).map((x) => r.inflateReference(x)); | |
| 325 return construct(inflated); | |
| 326 } | |
| 327 | |
| 328 void inflateNonEssential(state, object, Reader r) { | |
| 329 if (setNonEssentialState == null) return; | |
| 330 var inflated = values(state).map((x) => r.inflateReference(x)); | |
| 331 setNonEssentialState(inflated, object); | |
| 332 } | |
| 333 } | |
| 334 | |
| 335 /** | |
| 336 * This rule handles things we can't pass directly, but only by reference. | |
| 337 * It extracts an identifier we can use to pass them. | |
| 338 */ | |
| 339 class ClassMirrorRule extends SerializationRule { | |
| 340 // TODO(alanknight): This probably generalizes to any named object. | |
| 341 bool appliesTo(object) { | |
| 342 return object is ClassMirror; | |
| 343 } | |
| 344 extractState(object, Function f) => f(object.simpleName); | |
| 345 void flatten(object, Writer writer) {} | |
| 346 inflateEssential(state, Reader r) => r.externalObjectNamed(state); | |
| 347 inflateNonEssential(object, _, Reader r) {} | |
|
Jennifer Messerly
2012/11/20 01:20:47
seems unusual to see "_" in the middle of a method
Alan Knight
2012/11/20 12:17:54
Done.
| |
| 348 } | |
| 349 | |
| 350 /** | |
| 351 * This is polyfill because we can't hash ClassMirror right now. We | |
| 352 * don't bother implementing most of its methods because we don't need them. | |
| 353 */ | |
| 354 // TODO(alanknight): Remove this when you can hash mirrors directly | |
| 355 class ClassMirrorWrapper implements ClassMirror { | |
| 356 ClassMirror mirror; | |
| 357 ClassMirrorWrapper(this.mirror); | |
| 358 get simpleName => mirror.simpleName; | |
| 359 get hashCode => simpleName.hashCode; | |
| 360 operator ==(x) => x is ClassMirror && simpleName == x.simpleName; | |
| 361 } | |
| 362 | |
| 363 | |
| OLD | NEW |