| 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; |
| 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); |
| 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); |
| 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; |
| 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) {} |
| 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 |