| 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. |
| (...skipping 19 matching lines...) Expand all Loading... |
| 30 * value on the Serialization. | 30 * value on the Serialization. |
| 31 */ | 31 */ |
| 32 bool selfDescribing; | 32 bool selfDescribing; |
| 33 | 33 |
| 34 /** | 34 /** |
| 35 * Objects that cannot be represented in-place in the serialized form need | 35 * Objects that cannot be represented in-place in the serialized form need |
| 36 * to have references to them stored. The [Reference] objects are computed | 36 * to have references to them stored. The [Reference] objects are computed |
| 37 * once and stored here for each object. This provides some space-saving, | 37 * once and stored here for each object. This provides some space-saving, |
| 38 * but also serves to record which objects we have already seen. | 38 * but also serves to record which objects we have already seen. |
| 39 */ | 39 */ |
| 40 final Map<Object, Reference> references = | 40 final Map<dynamic, Reference> references = |
| 41 new IdentityMapPlus<Object, Reference>(); | 41 new IdentityMapPlus<dynamic, Reference>(); |
| 42 | 42 |
| 43 /** | 43 /** |
| 44 * The state of objects that need to be serialized is stored here. | 44 * The state of objects that need to be serialized is stored here. |
| 45 * Each rule has a number, and rules keep track of the objects that they | 45 * Each rule has a number, and rules keep track of the objects that they |
| 46 * serialize, in order. So the state of any object can be found by indexing | 46 * serialize, in order. So the state of any object can be found by indexing |
| 47 * from the rule number and the object number within the rule. | 47 * from the rule number and the object number within the rule. |
| 48 * The actual representation of the state is determined by the rule. Lists | 48 * The actual representation of the state is determined by the rule. Lists |
| 49 * and Maps are common, but it is arbitrary. | 49 * and Maps are common, but it is arbitrary. |
| 50 */ | 50 */ |
| 51 final List<List> states = new List<List>(); | 51 final List<List> states = new List<List>(); |
| 52 | 52 |
| 53 /** Return the list of rules we use. */ | 53 /** Return the list of rules we use. */ |
| 54 List<SerializationRule> get rules => serialization.rules; | 54 List<SerializationRule> get rules => serialization._rules; |
| 55 | 55 |
| 56 /** | 56 /** |
| 57 * Creates a new [Writer] that uses the rules from its parent | 57 * Creates a new [Writer] that uses the rules from its parent |
| 58 * [Serialization]. Serializations are do not keep any state | 58 * [Serialization]. Serializations are do not keep any state |
| 59 * related to a particular read/write, so the same one can be used | 59 * related to a particular read/write, so the same one can be used |
| 60 * for multiple different Readers/Writers. | 60 * for multiple different Readers/Writers. |
| 61 */ | 61 */ |
| 62 Writer(this.serialization) { | 62 Writer(this.serialization) { |
| 63 trace = new Trace(this); | 63 trace = new Trace(this); |
| 64 selfDescribing = serialization.selfDescribing; | 64 selfDescribing = serialization.selfDescribing; |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 140 } | 140 } |
| 141 } | 141 } |
| 142 | 142 |
| 143 /** | 143 /** |
| 144 * As the [trace] processes each object, it will call this method on us. | 144 * As the [trace] processes each object, it will call this method on us. |
| 145 * We find the rules for this object, and record the state of the object | 145 * We find the rules for this object, and record the state of the object |
| 146 * as determined by each rule. | 146 * as determined by each rule. |
| 147 */ | 147 */ |
| 148 void _process(object, Trace trace) { | 148 void _process(object, Trace trace) { |
| 149 var real = (object is DesignatedRuleForObject) ? object.target : object; | 149 var real = (object is DesignatedRuleForObject) ? object.target : object; |
| 150 for (var eachRule in serialization.rulesFor(object)) { | 150 for (var eachRule in serialization.rulesFor(object, this)) { |
| 151 _record(real, eachRule); | 151 _record(real, eachRule); |
| 152 } | 152 } |
| 153 } | 153 } |
| 154 | 154 |
| 155 /** | 155 /** |
| 156 * Record the state of [object] as determined by [rule] and keep | 156 * Record the state of [object] as determined by [rule] and keep |
| 157 * track of it. Generate a [Reference] for this object if required. | 157 * track of it. Generate a [Reference] for this object if required. |
| 158 * When it's required is up to the particular rule, but generally everything | 158 * When it's required is up to the particular rule, but generally everything |
| 159 * gets a reference except a primitive. | 159 * gets a reference except a primitive. |
| 160 * Note that at this point the states are just the same as the fields of the | 160 * Note that at this point the states are just the same as the fields of the |
| 161 * object, and haven't been flattened. | 161 * object, and haven't been flattened. |
| 162 */ | 162 */ |
| 163 void _record(Object object, SerializationRule rule) { | 163 void _record(object, SerializationRule rule) { |
| 164 if (rule.shouldUseReferenceFor(object, this)) { | 164 if (rule.shouldUseReferenceFor(object, this)) { |
| 165 references.putIfAbsent(object, () => | 165 references.putIfAbsent(object, () => |
| 166 new Reference(this, rule.number, _nextObjectNumberFor(rule))); | 166 new Reference(this, rule.number, _nextObjectNumberFor(rule))); |
| 167 var state = rule.extractState(object, trace.note); | 167 var state = rule.extractState(object, trace.note); |
| 168 _addStateForRule(rule, state); | 168 _addStateForRule(rule, state); |
| 169 } | 169 } |
| 170 } | 170 } |
| 171 | 171 |
| 172 /** | 172 /** |
| 173 * Should we store primitive objects directly or create references for them. | 173 * Should we store primitive objects directly or create references for them. |
| 174 * That depends on which format we're using, so a flat format will want | 174 * That depends on which format we're using, so a flat format will want |
| 175 * references, but the Map format can store them directly. | 175 * references, but the Map format can store them directly. |
| 176 */ | 176 */ |
| 177 bool shouldUseReferencesForPrimitives = false; | 177 bool shouldUseReferencesForPrimitives = false; |
| 178 | 178 |
| 179 /** Record a [state] entry for a particular rule. */ | 179 /** Record a [state] entry for a particular rule. */ |
| 180 void _addStateForRule(eachRule, Object state) { | 180 void _addStateForRule(eachRule, state) { |
| 181 _growStates(eachRule); | 181 _growStates(eachRule); |
| 182 states[eachRule.number].add(state); | 182 states[eachRule.number].add(state); |
| 183 } | 183 } |
| 184 | 184 |
| 185 /** Find what the object number for the thing we're about to add will be.*/ | 185 /** Find what the object number for the thing we're about to add will be.*/ |
| 186 int _nextObjectNumberFor(SerializationRule rule) { | 186 int _nextObjectNumberFor(SerializationRule rule) { |
| 187 _growStates(rule); | 187 _growStates(rule); |
| 188 return states[rule.number].length; | 188 return states[rule.number].length; |
| 189 } | 189 } |
| 190 | 190 |
| 191 /** | 191 /** |
| 192 * We store the states in a List, indexed by rule number. But rules can be | 192 * We store the states in a List, indexed by rule number. But rules can be |
| 193 * dynamically added, so we may have to grow the list. | 193 * dynamically added, so we may have to grow the list. |
| 194 */ | 194 */ |
| 195 void _growStates(eachRule) { | 195 void _growStates(eachRule) { |
| 196 while (states.length <= eachRule.number) states.add(new List()); | 196 while (states.length <= eachRule.number) states.add(new List()); |
| 197 } | 197 } |
| 198 | 198 |
| 199 /** | 199 /** |
| 200 * Return true if we have an object number for this object. This is used to | 200 * Return true if we have an object number for this object. This is used to |
| 201 * tell if we have processed the object or not. This relies on checking if we | 201 * tell if we have processed the object or not. This relies on checking if we |
| 202 * have a reference or not. That saves some space by not having to keep track | 202 * have a reference or not. That saves some space by not having to keep track |
| 203 * of simple objects, but means that if someone refers to the identical string | 203 * of simple objects, but means that if someone refers to the identical string |
| 204 * from several places, we will process it several times, and store it | 204 * from several places, we will process it several times, and store it |
| 205 * several times. That seems an acceptable tradeoff, and in cases where it | 205 * several times. That seems an acceptable tradeoff, and in cases where it |
| 206 * isn't, it's possible to apply a rule for String, or even for Strings larger | 206 * isn't, it's possible to apply a rule for String, or even for Strings larger |
| 207 * than x, which gives them references. | 207 * than x, which gives them references. |
| 208 */ | 208 */ |
| 209 bool _hasIndexFor(Object object) { | 209 bool _hasIndexFor(object) { |
| 210 return _objectNumberFor(object) != -1; | 210 return _objectNumberFor(object) != -1; |
| 211 } | 211 } |
| 212 | 212 |
| 213 /** | 213 /** |
| 214 * Given an object, find what number it has. The number is valid only in | 214 * Given an object, find what number it has. The number is valid only in |
| 215 * the context of a particular rule, and if the rule has more than one, | 215 * the context of a particular rule, and if the rule has more than one, |
| 216 * this will return the one for the primary rule, defined as the one that | 216 * this will return the one for the primary rule, defined as the one that |
| 217 * is listed in its canonical reference. | 217 * is listed in its canonical reference. |
| 218 */ | 218 */ |
| 219 int _objectNumberFor(Object object) { | 219 int _objectNumberFor(object) { |
| 220 var reference = references[object]; | 220 var reference = references[object]; |
| 221 return (reference == null) ? -1 : reference.objectNumber; | 221 return (reference == null) ? -1 : reference.objectNumber; |
| 222 } | 222 } |
| 223 | 223 |
| 224 /** | 224 /** |
| 225 * Return the serialized data in string format. Currently hard-coded to | 225 * Return the serialized data in string format. Currently hard-coded to |
| 226 * our custom JSON format. | 226 * our custom JSON format. |
| 227 */ | 227 */ |
| 228 String toStringFormat() { | 228 String toStringFormat() { |
| 229 return JSON.stringify(toMaps()); | 229 return JSON.stringify(toMaps()); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 240 * This effectively defines a custom JSON serialization format, although | 240 * This effectively defines a custom JSON serialization format, although |
| 241 * the details of the format vary depending which rules were used. | 241 * the details of the format vary depending which rules were used. |
| 242 */ | 242 */ |
| 243 Map toMaps() { | 243 Map toMaps() { |
| 244 var result = new Map(); | 244 var result = new Map(); |
| 245 var savedRules; | 245 var savedRules; |
| 246 if (selfDescribing) { | 246 if (selfDescribing) { |
| 247 var meta = serialization._ruleSerialization(); | 247 var meta = serialization._ruleSerialization(); |
| 248 var writer = new Writer(meta); | 248 var writer = new Writer(meta); |
| 249 writer.selfDescribing = false; | 249 writer.selfDescribing = false; |
| 250 savedRules = writer.write(serialization.rules); | 250 savedRules = writer.write(serialization._rules); |
| 251 } | 251 } |
| 252 result["rules"] = savedRules; | 252 result["rules"] = savedRules; |
| 253 result["data"] = states; | 253 result["data"] = states; |
| 254 result["roots"] = _rootReferences(trace.roots); | 254 result["roots"] = _rootReferences(trace.roots); |
| 255 return result; | 255 return result; |
| 256 } | 256 } |
| 257 | 257 |
| 258 /** | 258 /** |
| 259 * Return a list of [Reference] objects pointing to our roots. This will be | 259 * Return a list of [Reference] objects pointing to our roots. This will be |
| 260 * stored in the output under "roots" in the default format. | 260 * stored in the output under "roots" in the default format. |
| 261 */ | 261 */ |
| 262 _rootReferences(roots) => | 262 _rootReferences(roots) => |
| 263 roots.map(_referenceFor); | 263 roots.map(_referenceFor); |
| 264 | 264 |
| 265 /** | 265 /** |
| 266 * Given an object, return a reference for it if one exists. If there's | 266 * Given an object, return a reference for it if one exists. If there's |
| 267 * no reference, return null. Once we have finished the tracing step, all | 267 * no reference, return null. Once we have finished the tracing step, all |
| 268 * objects that should have a reference (roughly speaking, non-primitives) | 268 * objects that should have a reference (roughly speaking, non-primitives) |
| 269 * can be relied on to have a reference. | 269 * can be relied on to have a reference. |
| 270 */ | 270 */ |
| 271 _referenceFor(Object o) { | 271 _referenceFor(object) => references[object]; |
| 272 return references[o]; | 272 |
| 273 } | 273 /** |
| 274 * Return true if the [namedObjects] collection has a reference to [object]. |
| 275 */ |
| 276 // TODO(alanknight): Should the writer also have its own namedObjects |
| 277 // collection specific to the particular write, or is that just adding |
| 278 // complexity for little value? |
| 279 hasNameFor(object) => serialization._hasNameFor(object); |
| 280 |
| 281 /** |
| 282 * Return the name we have for this object in the [namedObjects] collection. |
| 283 */ |
| 284 nameFor(object) => serialization._nameFor(object); |
| 274 | 285 |
| 275 // For debugging/testing purposes. Find what state a reference points to. | 286 // For debugging/testing purposes. Find what state a reference points to. |
| 276 stateForReference(Reference r) => | 287 stateForReference(Reference r) => states[r.ruleNumber][r.objectNumber]; |
| 277 states[r.ruleNumber][r.objectNumber]; | |
| 278 } | 288 } |
| 279 | 289 |
| 280 /** | 290 /** |
| 281 * The main class responsible for reading. It holds | 291 * The main class responsible for reading. It holds |
| 282 * onto the necessary state and to the objects that have been inflated. | 292 * onto the necessary state and to the objects that have been inflated. |
| 283 */ | 293 */ |
| 284 class Reader { | 294 class Reader { |
| 285 | 295 |
| 286 /** | 296 /** |
| 287 * The serialization that specifies how we read. Note that in contrast | 297 * The serialization that specifies how we read. Note that in contrast |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 324 selfDescribing = serialization.selfDescribing; | 334 selfDescribing = serialization.selfDescribing; |
| 325 } | 335 } |
| 326 | 336 |
| 327 /** | 337 /** |
| 328 * When we read, we may need to look up objects by name in order to link to | 338 * When we read, we may need to look up objects by name in order to link to |
| 329 * them. This is particularly true if we have references to classes, | 339 * them. This is particularly true if we have references to classes, |
| 330 * functions, mirrors, or other non-portable entities. The map in which we | 340 * functions, mirrors, or other non-portable entities. The map in which we |
| 331 * look things up can be provided as an argument to read, but we can also | 341 * look things up can be provided as an argument to read, but we can also |
| 332 * provide a map here, and objects will be looked up in both places. | 342 * provide a map here, and objects will be looked up in both places. |
| 333 */ | 343 */ |
| 334 Map externalObjects; | 344 Map namedObjects; |
| 335 | 345 |
| 336 /** | 346 /** |
| 337 * Look up the reference to an external object. This can be held either in | 347 * Look up the reference to an external object. This can be held either in |
| 338 * the reader-specific list of externals or in the serializer's | 348 * the reader-specific list of externals or in the serializer's |
| 339 */ | 349 */ |
| 340 externalObjectNamed(key) { | 350 objectNamed(key) { |
| 341 var map = (externalObjects.containsKey(key)) | 351 var map = (namedObjects.containsKey(key)) |
| 342 ? externalObjects : serialization.externalObjects; | 352 ? namedObjects : serialization.namedObjects; |
| 343 if (!map.containsKey(key)) { | 353 if (!map.containsKey(key)) { |
| 344 throw 'Cannot find named object to link to: $key'; | 354 throw 'Cannot find named object to link to: $key'; |
| 345 } | 355 } |
| 346 return map[key]; | 356 return map[key]; |
| 347 } | 357 } |
| 348 | 358 |
| 349 /** | 359 /** |
| 350 * Return the list of rules to be used when writing. These come from the | 360 * Return the list of rules to be used when writing. These come from the |
| 351 * [serialization]. | 361 * [serialization]. |
| 352 */ | 362 */ |
| 353 List<SerializationRule> get rules => serialization.rules; | 363 List<SerializationRule> get rules => serialization._rules; |
| 354 | 364 |
| 355 /** | 365 /** |
| 356 * Internal use only, for testing purposes. Set the data for this reader | 366 * Internal use only, for testing purposes. Set the data for this reader |
| 357 * to a List of Lists whose size must match the number of rules. | 367 * to a List of Lists whose size must match the number of rules. |
| 358 */ | 368 */ |
| 359 // When we set the data, initialize the object storage to a matching size. | 369 // When we set the data, initialize the object storage to a matching size. |
| 360 void set data(List<List> newData) { | 370 void set data(List<List> newData) { |
| 361 _data = newData; | 371 _data = newData; |
| 362 objects = _data.map((x) => new List(x.length)); | 372 objects = _data.map((x) => new List(x.length)); |
| 363 } | 373 } |
| 364 | 374 |
| 365 /** | 375 /** |
| 366 * This is the primary method for a [Reader]. It takes the input data, | 376 * This is the primary method for a [Reader]. It takes the input data, |
| 367 * currently hard-coded to expect our custom JSON format, and returns | 377 * currently hard-coded to expect our custom JSON format, and returns |
| 368 * the root objects. | 378 * the root object. |
| 369 */ | 379 */ |
| 370 read(String input, [Map externals = const {}]) { | 380 read(String input, [Map externals = const {}]) { |
| 371 externalObjects = externals; | 381 namedObjects = externals; |
| 372 var topLevel = JSON.parse(input); | 382 var topLevel = JSON.parse(input); |
| 373 var ruleString = topLevel["rules"]; | 383 var ruleString = topLevel["rules"]; |
| 374 readRules(ruleString, externals); | 384 readRules(ruleString, externals); |
| 375 data = topLevel["data"]; | 385 data = topLevel["data"]; |
| 376 rules.forEach(inflateForRule); | 386 rules.forEach(inflateForRule); |
| 377 var roots = topLevel["roots"]; | 387 var roots = topLevel["roots"]; |
| 378 return roots.map(inflateReference); | 388 return inflateReference(roots.first); |
| 379 } | 389 } |
| 380 | 390 |
| 381 /** | 391 /** |
| 382 * If the data we are reading from has rules written to it, read them back | 392 * If the data we are reading from has rules written to it, read them back |
| 383 * and set them as the rules we will use. | 393 * and set them as the rules we will use. |
| 384 */ | 394 */ |
| 385 void readRules(String newRules, Map externals) { | 395 void readRules(String newRules, Map externals) { |
| 386 // TODO(alanknight): Replacing the serialization is kind of confusing. | 396 // TODO(alanknight): Replacing the serialization is kind of confusing. |
| 387 List rulesWeRead = (newRules == null) ? | 397 List rulesWeRead = (newRules == null) ? |
| 388 null : serialization._ruleSerialization().readOne(newRules, externals); | 398 null : serialization._ruleSerialization().read(newRules, externals); |
| 389 if (rulesWeRead != null && !rulesWeRead.isEmpty) { | 399 if (rulesWeRead != null && !rulesWeRead.isEmpty) { |
| 390 serialization = new Serialization.blank(); | 400 serialization = new Serialization.blank(); |
| 391 rulesWeRead.forEach(serialization.addRule); | 401 rulesWeRead.forEach(serialization.addRule); |
| 392 } | 402 } |
| 393 } | 403 } |
| 394 | 404 |
| 395 /** | 405 /** |
| 396 * This is a hard-coded read method for a vaguely flat format. It's just a | 406 * This is a hard-coded read method for a vaguely flat format. It's just a |
| 397 * proof of concept of handling more flat formats right now, and needs a lot | 407 * proof of concept of handling more flat formats right now, and needs a lot |
| 398 * of fixing and generalization. | 408 * of fixing and generalization. |
| 399 */ | 409 */ |
| 400 readFlat(List input, [Map externals = const {}]) { | 410 readFlat(List input, [Map externals = const {}]) { |
| 401 // TODO(alanknight): Way too much code duplication with read. Numerous | 411 // TODO(alanknight): Way too much code duplication with read. Numerous |
| 402 // code smells. | 412 // code smells. |
| 403 externalObjects = externals; | 413 namedObjects = externals; |
| 404 var topLevel = input; | 414 var topLevel = input; |
| 405 var ruleString = topLevel[0]; | 415 var ruleString = topLevel[0]; |
| 406 readRules(ruleString, externals); | 416 readRules(ruleString, externals); |
| 407 var flatData = topLevel[1]; | 417 var flatData = topLevel[1]; |
| 408 var stream = flatData.iterator(); | 418 var stream = flatData.iterator(); |
| 409 var tempData = new List(rules.length); | 419 var tempData = new List(rules.length); |
| 410 for (var eachRule in rules) { | 420 for (var eachRule in rules) { |
| 411 tempData[eachRule.number] = eachRule.pullStateFrom(stream); | 421 tempData[eachRule.number] = eachRule.pullStateFrom(stream); |
| 412 } | 422 } |
| 413 data = tempData; | 423 data = tempData; |
| 414 for (var eachRule in rules) { | 424 for (var eachRule in rules) { |
| 415 inflateForRule(eachRule); | 425 inflateForRule(eachRule); |
| 416 } | 426 } |
| 417 var rootsAsInts = topLevel[2]; | 427 var rootsAsInts = topLevel[2]; |
| 418 var rootStream = rootsAsInts.iterator(); | 428 var rootStream = rootsAsInts.iterator(); |
| 419 var roots = new List(); | 429 var roots = new List(); |
| 420 while (rootStream.hasNext) { | 430 while (rootStream.hasNext) { |
| 421 roots.add(new Reference(this, rootStream.next(), rootStream.next())); | 431 roots.add(new Reference(this, rootStream.next(), rootStream.next())); |
| 422 } | 432 } |
| 423 var x = inflateReference(roots[0]); | 433 var x = inflateReference(roots[0]); |
| 424 return roots.map((x) => inflateReference(x)); | 434 return inflateReference(roots.first); |
| 425 } | 435 } |
| 426 | 436 |
| 427 | |
| 428 /** | |
| 429 * A convenient alternative to [read] when you know there is only | |
| 430 * one object. | |
| 431 */ | |
| 432 readOne(String input, [Map externals = const {}]) => | |
| 433 read(input, externals).first; | |
| 434 | |
| 435 /** | |
| 436 * A convenient alternative to [readFlat] when you know there is only | |
| 437 * one object. | |
| 438 */ | |
| 439 readOneFlat(List input, [Map externals = const {}]) => | |
| 440 readFlat(input, externals).first; | |
| 441 | |
| 442 /** | 437 /** |
| 443 * Inflate all of the objects for [rule]. Does the essential state for all | 438 * Inflate all of the objects for [rule]. Does the essential state for all |
| 444 * objects first, then the non-essential state. This avoids cycles in | 439 * objects first, then the non-essential state. This avoids cycles in |
| 445 * non-essential state, because all the objects will have already been | 440 * non-essential state, because all the objects will have already been |
| 446 * created. | 441 * created. |
| 447 */ | 442 */ |
| 448 inflateForRule(rule) { | 443 inflateForRule(rule) { |
| 449 var dataForThisRule = _data[rule.number]; | 444 var dataForThisRule = _data[rule.number]; |
| 450 keysAndValues(dataForThisRule).forEach((position, state) { | 445 keysAndValues(dataForThisRule).forEach((position, state) { |
| 451 inflateOne(rule, position, state); | 446 inflateOne(rule, position, state); |
| 452 }); | 447 }); |
| 453 keysAndValues(dataForThisRule).forEach((position, state) { | 448 keysAndValues(dataForThisRule).forEach((position, state) { |
| 454 rule.inflateNonEssential(state, allObjectsForRule(rule)[position], this); | 449 rule.inflateNonEssential(state, allObjectsForRule(rule)[position], this); |
| 455 }); | 450 }); |
| 456 } | 451 } |
| 457 | 452 |
| 458 /** | 453 /** |
| 459 * Create a new object, based on [rule] and [state], which will | 454 * Create a new object, based on [rule] and [state], which will |
| 460 * be stored in [position] in the storage for [rule]. This will | 455 * be stored in [position] in the storage for [rule]. This will |
| 461 * follow references and recursively inflate them, leaving Sentinel objects | 456 * follow references and recursively inflate them, leaving Sentinel objects |
| 462 * to detect cycles. | 457 * to detect cycles. |
| 463 */ | 458 */ |
| 464 Object inflateOne(SerializationRule rule, position, state) { | 459 inflateOne(SerializationRule rule, position, state) { |
| 465 var existing = allObjectsForRule(rule)[position]; | 460 var existing = allObjectsForRule(rule)[position]; |
| 466 // We may already be in progress and hitting this in a cycle. | 461 // We may already be in progress and hitting this in a cycle. |
| 467 if (existing is _Sentinel) { | 462 if (existing is _Sentinel) { |
| 468 throw new SerializationException('Cycle in essential state'); | 463 throw new SerializationException('Cycle in essential state'); |
| 469 } | 464 } |
| 470 // We may have already inflated this object, at least its essential state. | 465 // We may have already inflated this object, at least its essential state. |
| 471 if (existing != null) return existing; | 466 if (existing != null) return existing; |
| 472 | 467 |
| 473 // Put a sentinel there to mark this in case of recursion. | 468 // Put a sentinel there to mark this in case of recursion. |
| 474 allObjectsForRule(rule)[position] = const _Sentinel(); | 469 allObjectsForRule(rule)[position] = const _Sentinel(); |
| 475 var newObject = rule.inflateEssential(state, this); | 470 var newObject = rule.inflateEssential(state, this); |
| 476 allObjectsForRule(rule)[position] = newObject; | 471 allObjectsForRule(rule)[position] = newObject; |
| 477 return newObject; | 472 return newObject; |
| 478 } | 473 } |
| 479 | 474 |
| 480 /** | 475 /** |
| 481 * The parameter [possibleReference] might be a reference. If it isn't, just | 476 * The parameter [possibleReference] might be a reference. If it isn't, just |
| 482 * return it. If it is, then inflate the target of the reference and return | 477 * return it. If it is, then inflate the target of the reference and return |
| 483 * the resulting object. | 478 * the resulting object. |
| 484 */ | 479 */ |
| 485 Object inflateReference(possibleReference) { | 480 inflateReference(possibleReference) { |
| 486 // If this is a primitive, return it directly. | 481 // If this is a primitive, return it directly. |
| 487 // TODO This seems too complicated. | 482 // TODO This seems too complicated. |
| 488 return asReference(possibleReference, | 483 return asReference(possibleReference, |
| 489 ifReference: (reference) { | 484 ifReference: (reference) { |
| 490 var rule = ruleFor(reference); | 485 var rule = ruleFor(reference); |
| 491 var state = _stateFor(reference); | 486 var state = _stateFor(reference); |
| 492 inflateOne(rule, reference.objectNumber, state); | 487 inflateOne(rule, reference.objectNumber, state); |
| 493 return _objectFor(reference); | 488 return _objectFor(reference); |
| 494 }); | 489 }); |
| 495 } | 490 } |
| 496 | 491 |
| 497 /** | 492 /** |
| 498 * Given [reference], return what we have stored as an object for it. Note | 493 * Given [reference], return what we have stored as an object for it. Note |
| 499 * that, depending on the current state, this might be null or a Sentinel. | 494 * that, depending on the current state, this might be null or a Sentinel. |
| 500 */ | 495 */ |
| 501 Object _objectFor(Reference reference) => | 496 _objectFor(Reference reference) => |
| 502 objects[reference.ruleNumber][reference.objectNumber]; | 497 objects[reference.ruleNumber][reference.objectNumber]; |
| 503 | 498 |
| 504 /** Given [rule], return the storage for its objects. */ | 499 /** Given [rule], return the storage for its objects. */ |
| 505 allObjectsForRule(SerializationRule rule) => objects[rule.number]; | 500 allObjectsForRule(SerializationRule rule) => objects[rule.number]; |
| 506 | 501 |
| 507 /** Given [reference], return the the state we have stored for it. */ | 502 /** Given [reference], return the the state we have stored for it. */ |
| 508 Object _stateFor(Reference reference) => | 503 _stateFor(Reference reference) => |
| 509 _data[reference.ruleNumber][reference.objectNumber]; | 504 _data[reference.ruleNumber][reference.objectNumber]; |
| 510 | 505 |
| 511 /** Given a reference, return the rule it references. */ | 506 /** Given a reference, return the rule it references. */ |
| 512 SerializationRule ruleFor(Reference reference) => | 507 SerializationRule ruleFor(Reference reference) => |
| 513 serialization.rules[reference.ruleNumber]; | 508 serialization._rules[reference.ruleNumber]; |
| 514 | 509 |
| 515 /** | 510 /** |
| 516 * Given a possible reference [anObject], call either [ifReference] or | 511 * Given a possible reference [anObject], call either [ifReference] or |
| 517 * [ifNotReference], depending if it's a reference or not. This is the | 512 * [ifNotReference], depending if it's a reference or not. This is the |
| 518 * primary place that knows about the serialized representation of a | 513 * primary place that knows about the serialized representation of a |
| 519 * reference. | 514 * reference. |
| 520 */ | 515 */ |
| 521 asReference(anObject, {Function ifReference: doNothing, | 516 asReference(anObject, {Function ifReference: doNothing, |
| 522 Function ifNotReference : doNothing}) { | 517 Function ifNotReference : doNothing}) { |
| 523 if (anObject is Reference) return ifReference(anObject); | 518 if (anObject is Reference) return ifReference(anObject); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 561 /** The root objects from which we will be tracing. */ | 556 /** The root objects from which we will be tracing. */ |
| 562 List roots = []; | 557 List roots = []; |
| 563 | 558 |
| 564 Trace(this.writer); | 559 Trace(this.writer); |
| 565 | 560 |
| 566 addRoot(object) { | 561 addRoot(object) { |
| 567 roots.add(object); | 562 roots.add(object); |
| 568 } | 563 } |
| 569 | 564 |
| 570 /** A convenience method to add a single root and trace it in one step. */ | 565 /** A convenience method to add a single root and trace it in one step. */ |
| 571 trace(Object o) { | 566 trace(object) { |
| 572 addRoot(o); | 567 addRoot(object); |
| 573 traceAll(); | 568 traceAll(); |
| 574 } | 569 } |
| 575 | 570 |
| 576 /** | 571 /** |
| 577 * Process all of the objects reachable from our roots via state that the | 572 * Process all of the objects reachable from our roots via state that the |
| 578 * serialization rules access. | 573 * serialization rules access. |
| 579 */ | 574 */ |
| 580 traceAll() { | 575 traceAll() { |
| 581 queue.addAll(roots); | 576 queue.addAll(roots); |
| 582 while (!queue.isEmpty) { | 577 while (!queue.isEmpty) { |
| 583 var next = queue.removeFirst(); | 578 var next = queue.removeFirst(); |
| 584 if (!hasProcessed(next)) writer._process(next, this); | 579 if (!hasProcessed(next)) writer._process(next, this); |
| 585 } | 580 } |
| 586 } | 581 } |
| 587 | 582 |
| 588 /** | 583 /** |
| 589 * Has this object been seen yet? We test for this by checking if the | 584 * Has this object been seen yet? We test for this by checking if the |
| 590 * writer has a reference for it. See comment for _hasIndexFor. | 585 * writer has a reference for it. See comment for _hasIndexFor. |
| 591 */ | 586 */ |
| 592 bool hasProcessed(object) { | 587 bool hasProcessed(object) { |
| 593 return writer._hasIndexFor(object); | 588 return writer._hasIndexFor(object); |
| 594 } | 589 } |
| 595 | 590 |
| 596 /** Note that we've seen [value], and add it to the queue to be processed. */ | 591 /** Note that we've seen [value], and add it to the queue to be processed. */ |
| 597 note(Object value) { | 592 note(value) { |
| 598 if (value != null) { | 593 if (value != null) { |
| 599 queue.add(value); | 594 queue.add(value); |
| 600 } | 595 } |
| 601 return value; | 596 return value; |
| 602 } | 597 } |
| 603 } | 598 } |
| 604 | 599 |
| 605 /** | 600 /** |
| 606 * Any pointers to objects that can't be represented directly in the | 601 * Any pointers to objects that can't be represented directly in the |
| 607 * serialization format has to be stored as a reference. A reference encodes | 602 * serialization format has to be stored as a reference. A reference encodes |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 645 * referenced, and is a more or less internal collection. See ListRuleEssential | 640 * referenced, and is a more or less internal collection. See ListRuleEssential |
| 646 * for an example. It knows how to return its object and how to filter. | 641 * for an example. It knows how to return its object and how to filter. |
| 647 */ | 642 */ |
| 648 class DesignatedRuleForObject { | 643 class DesignatedRuleForObject { |
| 649 Function rulePredicate; | 644 Function rulePredicate; |
| 650 final target; | 645 final target; |
| 651 | 646 |
| 652 DesignatedRuleForObject(this.target, this.rulePredicate); | 647 DesignatedRuleForObject(this.target, this.rulePredicate); |
| 653 | 648 |
| 654 possibleRules(List rules) => rules.filter(rulePredicate); | 649 possibleRules(List rules) => rules.filter(rulePredicate); |
| 655 } | 650 } |
| 656 | |
| OLD | NEW |