OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of serialization; | 5 part of serialization; |
6 | 6 |
7 /** | 7 /** |
8 * This writes out the state of the objects to an external format. It holds | 8 * This writes out the state of the objects to an external format. It holds |
9 * all of the intermediate state needed. The primary API for it is the | 9 * all of the intermediate state needed. The primary API for it is the |
10 * [write] method. | 10 * [write] method. |
11 */ | 11 */ |
12 // TODO(alanknight): For simple serialization formats this does a lot of work | 12 // TODO(alanknight): For simple serialization formats this does a lot of work |
13 // that isn't necessary, e.g. detecting cycles and maintaining references. | 13 // that isn't necessary, e.g. detecting cycles and maintaining references. |
14 // Consider having an abstract superclass with the basic functionality and | 14 // Consider having an abstract superclass with the basic functionality and |
15 // simple serialization subclasses where we know there aren't cycles. | 15 // simple serialization subclasses where we know there aren't cycles. |
16 class Writer { | 16 class Writer implements ReaderOrWriter { |
17 /** | 17 /** |
18 * The [serialization] holds onto the rules that define how objects | 18 * The [serialization] holds onto the rules that define how objects |
19 * are serialized. | 19 * are serialized. |
20 */ | 20 */ |
21 final Serialization serialization; | 21 final Serialization serialization; |
22 | 22 |
23 /** The [trace] object keeps track of the objects to be visited while finding | 23 /** The [trace] object keeps track of the objects to be visited while finding |
24 * the full set of objects to be written.*/ | 24 * the full set of objects to be written.*/ |
25 Trace trace; | 25 Trace trace; |
26 | 26 |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
81 /** | 81 /** |
82 * Given that we have fully populated the list of [states], and more | 82 * Given that we have fully populated the list of [states], and more |
83 * importantly, the list of [references], go through each state and turn | 83 * importantly, the list of [references], go through each state and turn |
84 * anything that requires a [Reference] into one. Since only the rules | 84 * anything that requires a [Reference] into one. Since only the rules |
85 * know the representation they use for state, delegate to them. | 85 * know the representation they use for state, delegate to them. |
86 */ | 86 */ |
87 void _flatten() { | 87 void _flatten() { |
88 for (var eachRule in rules) { | 88 for (var eachRule in rules) { |
89 _growStates(eachRule); | 89 _growStates(eachRule); |
90 var index = eachRule.number; | 90 var index = eachRule.number; |
91 for (var eachState in states[index]) { | 91 var statesForThisRule = states[index]; |
92 eachRule.flatten(eachState, this); | 92 for (var i = 0; i < statesForThisRule.length; i++) { |
| 93 var eachState = statesForThisRule[i]; |
| 94 var newState = eachRule.flatten(eachState, this); |
| 95 if (newState != null) { |
| 96 statesForThisRule[i] = newState; |
| 97 } |
93 } | 98 } |
94 } | 99 } |
95 } | 100 } |
96 | 101 |
97 /** | 102 /** |
98 * As the [trace] processes each object, it will call this method on us. | 103 * As the [trace] processes each object, it will call this method on us. |
99 * We find the rules for this object, and record the state of the object | 104 * We find the rules for this object, and record the state of the object |
100 * as determined by each rule. | 105 * as determined by each rule. |
101 */ | 106 */ |
102 void _process(object, Trace trace) { | 107 void _process(object, Trace trace) { |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
189 } | 194 } |
190 | 195 |
191 /** | 196 /** |
192 * Return a list of [Reference] objects pointing to our roots. This will be | 197 * Return a list of [Reference] objects pointing to our roots. This will be |
193 * stored in the output under "roots" in the default format. | 198 * stored in the output under "roots" in the default format. |
194 */ | 199 */ |
195 _rootReferences() => trace.roots.mappedBy(_referenceFor).toList(); | 200 _rootReferences() => trace.roots.mappedBy(_referenceFor).toList(); |
196 | 201 |
197 /** | 202 /** |
198 * Given an object, return a reference for it if one exists. If there's | 203 * Given an object, return a reference for it if one exists. If there's |
199 * no reference, return null. Once we have finished the tracing step, all | 204 * no reference, return the object itself. Once we have finished the tracing |
200 * objects that should have a reference (roughly speaking, non-primitives) | 205 * step, all objects that should have a reference (roughly speaking, |
201 * can be relied on to have a reference. | 206 * non-primitives) can be relied on to have a reference. |
202 */ | 207 */ |
203 _referenceFor(object) => references[object]; | 208 _referenceFor(object) { |
| 209 var result = references[object]; |
| 210 return (result == null) ? object : result; |
| 211 } |
204 | 212 |
205 /** | 213 /** |
206 * Return true if the [namedObjects] collection has a reference to [object]. | 214 * Return true if the [namedObjects] collection has a reference to [object]. |
207 */ | 215 */ |
208 // TODO(alanknight): Should the writer also have its own namedObjects | 216 // TODO(alanknight): Should the writer also have its own namedObjects |
209 // collection specific to the particular write, or is that just adding | 217 // collection specific to the particular write, or is that just adding |
210 // complexity for little value? | 218 // complexity for little value? |
211 hasNameFor(object) => serialization._hasNameFor(object); | 219 hasNameFor(object) => serialization._hasNameFor(object); |
212 | 220 |
213 /** | 221 /** |
214 * Return the name we have for this object in the [namedObjects] collection. | 222 * Return the name we have for this object in the [namedObjects] collection. |
215 */ | 223 */ |
216 nameFor(object) => serialization._nameFor(object); | 224 nameFor(object) => serialization._nameFor(object); |
217 | 225 |
218 // For debugging/testing purposes. Find what state a reference points to. | 226 // For debugging/testing purposes. Find what state a reference points to. |
219 stateForReference(Reference r) => states[r.ruleNumber][r.objectNumber]; | 227 stateForReference(Reference r) => states[r.ruleNumber][r.objectNumber]; |
| 228 |
| 229 /** Return the state pointed to by [reference]. */ |
| 230 resolveReference(reference) => stateForReference(reference); |
| 231 } |
| 232 |
| 233 /** |
| 234 * An abstract class for Reader and Writer, which primarily exists so we can |
| 235 * type things that will refer to one or the other, depending on which |
| 236 * operation we're doing. |
| 237 */ |
| 238 abstract class ReaderOrWriter { |
| 239 /** Return the list of serialization rules we are using.*/ |
| 240 List<SerializationRule> get rules; |
| 241 |
| 242 /** |
| 243 * Return the object, or state, that ref points to, depending on which |
| 244 * we're generating. |
| 245 */ |
| 246 resolveReference(Reference ref); |
220 } | 247 } |
221 | 248 |
222 /** | 249 /** |
223 * The main class responsible for reading. It holds | 250 * The main class responsible for reading. It holds |
224 * onto the necessary state and to the objects that have been inflated. | 251 * onto the necessary state and to the objects that have been inflated. |
225 */ | 252 */ |
226 class Reader { | 253 class Reader implements ReaderOrWriter { |
227 | 254 |
228 /** | 255 /** |
229 * The serialization that specifies how we read. Note that in contrast | 256 * The serialization that specifies how we read. Note that in contrast |
230 * to the Writer, this is not final. This is because we may be created | 257 * to the Writer, this is not final. This is because we may be created |
231 * with an empty [Serialization] and then read the rules from the data, | 258 * with an empty [Serialization] and then read the rules from the data, |
232 * if [selfDescribing] is true. | 259 * if [selfDescribing] is true. |
233 */ | 260 */ |
234 Serialization serialization; | 261 Serialization serialization; |
235 | 262 |
236 /** | 263 /** |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
381 // TODO This seems too complicated. | 408 // TODO This seems too complicated. |
382 return asReference(possibleReference, | 409 return asReference(possibleReference, |
383 ifReference: (reference) { | 410 ifReference: (reference) { |
384 var rule = ruleFor(reference); | 411 var rule = ruleFor(reference); |
385 var state = _stateFor(reference); | 412 var state = _stateFor(reference); |
386 inflateOne(rule, reference.objectNumber, state); | 413 inflateOne(rule, reference.objectNumber, state); |
387 return _objectFor(reference); | 414 return _objectFor(reference); |
388 }); | 415 }); |
389 } | 416 } |
390 | 417 |
| 418 /** Return the object pointed to by [reference]. */ |
| 419 resolveReference(reference) => inflateReference(reference); |
| 420 |
391 /** | 421 /** |
392 * Given [reference], return what we have stored as an object for it. Note | 422 * Given [reference], return what we have stored as an object for it. Note |
393 * that, depending on the current state, this might be null or a Sentinel. | 423 * that, depending on the current state, this might be null or a Sentinel. |
394 */ | 424 */ |
395 _objectFor(Reference reference) => | 425 _objectFor(Reference reference) => |
396 objects[reference.ruleNumber][reference.objectNumber]; | 426 objects[reference.ruleNumber][reference.objectNumber]; |
397 | 427 |
398 /** Given [rule], return the storage for its objects. */ | 428 /** Given [rule], return the storage for its objects. */ |
399 allObjectsForRule(SerializationRule rule) => objects[rule.number]; | 429 allObjectsForRule(SerializationRule rule) => objects[rule.number]; |
400 | 430 |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
511 } | 541 } |
512 | 542 |
513 /** | 543 /** |
514 * Any pointers to objects that can't be represented directly in the | 544 * Any pointers to objects that can't be represented directly in the |
515 * serialization format has to be stored as a reference. A reference encodes | 545 * serialization format has to be stored as a reference. A reference encodes |
516 * the rule number of the rule that saved it in the Serialization that was used | 546 * the rule number of the rule that saved it in the Serialization that was used |
517 * for writing, and the object number within that rule. | 547 * for writing, and the object number within that rule. |
518 */ | 548 */ |
519 class Reference { | 549 class Reference { |
520 /** The [Reader] or [Writer] that owns this reference. */ | 550 /** The [Reader] or [Writer] that owns this reference. */ |
521 final parent; | 551 final ReaderOrWriter parent; |
522 /** The position of the rule that controls this reference in [parent]. */ | 552 /** The position of the rule that controls this reference in [parent]. */ |
523 final int ruleNumber; | 553 final int ruleNumber; |
524 /** The index of the referred-to object in the storage of [parent] */ | 554 /** The index of the referred-to object in the storage of [parent] */ |
525 final int objectNumber; | 555 final int objectNumber; |
526 | 556 |
527 const Reference(this.parent, this.ruleNumber, this.objectNumber); | 557 Reference(this.parent, this.ruleNumber, this.objectNumber) { |
| 558 if (ruleNumber == null || objectNumber == null) { |
| 559 throw new SerializationException("Invalid Reference"); |
| 560 } |
| 561 if (parent.rules.length < ruleNumber) { |
| 562 throw new SerializationException("Invalid Reference"); |
| 563 } |
| 564 } |
| 565 |
| 566 /** |
| 567 * Return the thing this reference points to. Assumes that we have a valid |
| 568 * parent and that it is a Reader, as inflating is not meaningful when |
| 569 * writing. |
| 570 */ |
| 571 inflated() => parent.resolveReference(this); |
528 | 572 |
529 /** | 573 /** |
530 * Convert the reference to a map in JSON format. This is specific to the | 574 * Convert the reference to a map in JSON format. This is specific to the |
531 * custom JSON format we define, and must be consistent with the | 575 * custom JSON format we define, and must be consistent with the |
532 * [asReference] method. | 576 * [asReference] method. |
533 */ | 577 */ |
534 // TODO(alanknight): This is a hack both in defining a toJson specific to a | 578 // TODO(alanknight): This is a hack both in defining a toJson specific to a |
535 // particular representation, and the use of a bogus sentinel "__Ref" | 579 // particular representation, and the use of a bogus sentinel "__Ref" |
536 toJson() => { | 580 toJson() => { |
537 "__Ref" : true, | 581 "__Ref" : true, |
538 "rule" : ruleNumber, | 582 "rule" : ruleNumber, |
539 "object" : objectNumber | 583 "object" : objectNumber |
540 }; | 584 }; |
541 | 585 |
542 /** Write our information to [list]. Useful in writing to flat formats.*/ | 586 /** Write our information to [list]. Useful in writing to flat formats.*/ |
543 writeToList(List list) { | 587 writeToList(List list) { |
544 list.add(ruleNumber); | 588 list.add(ruleNumber); |
545 list.add(objectNumber); | 589 list.add(objectNumber); |
546 } | 590 } |
547 | 591 |
548 toString() => "Reference $ruleNumber, $objectNumber"; | 592 toString() => "Reference($ruleNumber, $objectNumber)"; |
549 } | 593 } |
550 | 594 |
551 /** | 595 /** |
552 * This is used during tracing to indicate that an object should be processed | 596 * This is used during tracing to indicate that an object should be processed |
553 * using a particular rule, rather than the one that might ordinarily be | 597 * using a particular rule, rather than the one that might ordinarily be |
554 * found for it. This normally only makes sense if the object is uniquely | 598 * found for it. This normally only makes sense if the object is uniquely |
555 * referenced, and is a more or less internal collection. See ListRuleEssential | 599 * referenced, and is a more or less internal collection. See ListRuleEssential |
556 * for an example. It knows how to return its object and how to filter. | 600 * for an example. It knows how to return its object and how to filter. |
557 */ | 601 */ |
558 class DesignatedRuleForObject { | 602 class DesignatedRuleForObject { |
559 Function rulePredicate; | 603 Function rulePredicate; |
560 final target; | 604 final target; |
561 | 605 |
562 DesignatedRuleForObject(this.target, this.rulePredicate); | 606 DesignatedRuleForObject(this.target, this.rulePredicate); |
563 | 607 |
564 possibleRules(List rules) => rules.where(rulePredicate).toList(); | 608 possibleRules(List rules) => rules.where(rulePredicate).toList(); |
565 } | 609 } |
OLD | NEW |