| 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 // TODO(alanknight): Figure out how to reasonably separate out the things | 7 // TODO(alanknight): Figure out how to reasonably separate out the things |
| 8 // that require reflection without making the API more awkward. Or if that is | 8 // that require reflection without making the API more awkward. Or if that is |
| 9 // in fact necessary. Maybe the tree-shaking will just remove it if unused. | 9 // in fact necessary. Maybe the tree-shaking will just remove it if unused. |
| 10 | 10 |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 79 * | 79 * |
| 80 * This method lets you designate a function to use to set the value of a | 80 * This method lets you designate a function to use to set the value of a |
| 81 * field. It also makes the contents of that field be treated as essential, | 81 * field. It also makes the contents of that field be treated as essential, |
| 82 * which currently only has meaning if the field is a list. This is done | 82 * which currently only has meaning if the field is a list. This is done |
| 83 * because you might set a list field's special treatment function to add | 83 * because you might set a list field's special treatment function to add |
| 84 * each item individually and that will only work if those objects already | 84 * each item individually and that will only work if those objects already |
| 85 * exist. | 85 * exist. |
| 86 * | 86 * |
| 87 * For example, to serialize a Serialization, we need its rules to be | 87 * For example, to serialize a Serialization, we need its rules to be |
| 88 * individually added rather than just setting the rules field. | 88 * individually added rather than just setting the rules field. |
| 89 * ..addRuleFor(new Serialization()).specialTreatmentFor('rules', | 89 * ..addRuleFor(new Serialization()).setFieldWith('rules', |
| 90 * (InstanceMirror s, List rules) { | 90 * (InstanceMirror s, List rules) { |
| 91 * rules.forEach((x) => s.reflectee.addRule(x)); | 91 * rules.forEach((x) => s.reflectee.addRule(x)); |
| 92 * Note that the function is passed the owning object as well as the field | 92 * Note that the function is passed the owning object as well as the field |
| 93 * value, but that it is passed as a mirror. | 93 * value, but that it is passed as a mirror. |
| 94 */ | 94 */ |
| 95 specialTreatmentFor(String fieldName, SetWithFunction setWith) { | 95 setFieldWith(String fieldName, SetWithFunction setWith) { |
| 96 fields.addAllByName([fieldName]); | 96 fields.addAllByName([fieldName]); |
| 97 _Field field = fields.named(fieldName); | 97 _NamedField field = fields.named(fieldName); |
| 98 Function setter = (setWith == null) ? field.defaultSetter : setWith; | 98 Function setter = (setWith == null) ? field.defaultSetter : setWith; |
| 99 field.specialTreatment = setter; | 99 field.customSetter = setter; |
| 100 } | 100 } |
| 101 | 101 |
| 102 /** Return the name of the constructor used to create new instances on read.*/ | 102 /** Return the name of the constructor used to create new instances on read.*/ |
| 103 String get constructorName => constructor.name; | 103 String get constructorName => constructor.name; |
| 104 | 104 |
| 105 /** Return the list of field names to be passed to the constructor.*/ | 105 /** Return the list of field names to be passed to the constructor.*/ |
| 106 List<String> get constructorFields => fields.constructorFieldNames(); | 106 List<String> get constructorFields => fields.constructorFieldNames(); |
| 107 | 107 |
| 108 /** Return the list of field names not used in the constructor. */ | 108 /** Return the list of field names not used in the constructor. */ |
| 109 List<String> get regularFields => fields.regularFieldNames(); | 109 List<String> get regularFields => fields.regularFieldNames(); |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 216 fields.regular = regularFields; | 216 fields.regular = regularFields; |
| 217 // TODO(alanknight): The order of this matters. It shouldn't. | 217 // TODO(alanknight): The order of this matters. It shouldn't. |
| 218 fields.exclude = excludeFields; | 218 fields.exclude = excludeFields; |
| 219 fields.figureOutFields(); | 219 fields.figureOutFields(); |
| 220 } | 220 } |
| 221 | 221 |
| 222 /** | 222 /** |
| 223 * Extract the value of the field [fieldName] from the object reflected | 223 * Extract the value of the field [fieldName] from the object reflected |
| 224 * by [mirror]. | 224 * by [mirror]. |
| 225 */ | 225 */ |
| 226 // TODO (alanknight): The "is String" mechanism here should be more tightly | |
| 227 // controlled to work on just constructor fields. | |
| 228 // TODO(alanknight): The framework should be resilient if there are fields | 226 // TODO(alanknight): The framework should be resilient if there are fields |
| 229 // it expects that are missing, either for the case of de-serializing to a | 227 // it expects that are missing, either for the case of de-serializing to a |
| 230 // different definition, or for the case that tree-shaking has removed state. | 228 // different definition, or for the case that tree-shaking has removed state. |
| 231 // TODO(alanknight): This, and other places, rely on synchronous access to | 229 // TODO(alanknight): This, and other places, rely on synchronous access to |
| 232 // mirrors. Should be changed to use a synchronous API once one is available, | 230 // mirrors. Should be changed to use a synchronous API once one is available, |
| 233 // or to be async, but that would be extremely ugly. | 231 // or to be async, but that would be extremely ugly. |
| 234 _value(InstanceMirror mirror, _Field field) { | 232 _value(InstanceMirror mirror, _Field field) => field.valueIn(mirror); |
| 235 if (field.name is String) { | |
| 236 return mirror.getField(field.name).value.reflectee; | |
| 237 } else { | |
| 238 return field.name; | |
| 239 } | |
| 240 } | |
| 241 | 233 |
| 242 /** | 234 /** |
| 243 * When reading from a flat format we are given [stream] and need to pull as | 235 * When reading from a flat format we are given [stream] and need to pull as |
| 244 * much data from it as we need. Our format is that we have an integer N | 236 * much data from it as we need. Our format is that we have an integer N |
| 245 * indicating the number of objects and then for each object N fields, which | 237 * indicating the number of objects and then for each object N fields, which |
| 246 * are references, where a reference is stored in the stream as two integers. | 238 * are references, where a reference is stored in the stream as two integers. |
| 247 * Or, in the special case of null, two nulls. | 239 * Or, in the special case of null, two nulls. |
| 248 */ | 240 */ |
| 249 pullStateFrom(Iterator stream) { | 241 pullStateFrom(Iterator stream) { |
| 250 var dataLength = stream.next(); | 242 var dataLength = stream.next(); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 265 } | 257 } |
| 266 } | 258 } |
| 267 return ruleData; | 259 return ruleData; |
| 268 } | 260 } |
| 269 } | 261 } |
| 270 | 262 |
| 271 /** | 263 /** |
| 272 * This represents a field in an object. It is intended to be used as part of | 264 * This represents a field in an object. It is intended to be used as part of |
| 273 * a [_FieldList]. | 265 * a [_FieldList]. |
| 274 */ | 266 */ |
| 275 class _Field implements Comparable { | 267 abstract class _Field implements Comparable { |
| 276 /** The name of the field (or getter) */ | |
| 277 final name; | |
| 278 | 268 |
| 279 /** The FieldList that contains us. */ | 269 /** The FieldList that contains us. */ |
| 280 final _FieldList fieldList; | 270 final _FieldList fieldList; |
| 281 | 271 |
| 282 /** | 272 /** |
| 283 * Our position in the [contents] collection of [fieldList]. This is used | 273 * Our position in the [contents] collection of [fieldList]. This is used |
| 284 * to index into the state, so it's extremely important. | 274 * to index into the state, so it's extremely important. |
| 285 */ | 275 */ |
| 286 int index; | 276 int index; |
| 287 | 277 |
| 288 /** Is this field used in the constructor? */ | 278 /** Is this field used in the constructor? */ |
| 289 bool usedInConstructor = false; | 279 bool usedInConstructor = false; |
| 290 | 280 |
| 291 /** The special way to set this value registered, if this has a value. */ | 281 /** |
| 292 Function specialTreatment; | 282 * Create a new [_Field] instance. This will be either a [_NamedField] or a |
| 283 * [_ConstantField] depending on whether or not [value] corresponds to a |
| 284 * field in the class which [fieldList] models. |
| 285 */ |
| 286 factory _Field(value, _FieldList fieldList) { |
| 287 if (_isReallyAField(value, fieldList)) { |
| 288 return new _NamedField._internal(value, fieldList); |
| 289 } else { |
| 290 return new _ConstantField._internal(value, fieldList); |
| 291 } |
| 292 } |
| 293 | 293 |
| 294 _Field(this.name, this.fieldList); | 294 /** |
| 295 * Determine if [value] represents a field or getter in the class that |
| 296 * [fieldList] models. |
| 297 */ |
| 298 static bool _isReallyAField(value, _FieldList fieldList) { |
| 299 if (!(value is String)) return false; |
| 300 return hasField(value, fieldList.mirror) || |
| 301 hasGetter(value, fieldList.mirror); |
| 302 } |
| 295 | 303 |
| 296 operator ==(x) => x is _Field && (name == x.name); | 304 /** Private constructor. */ |
| 297 int get hashCode => name.hashCode; | 305 _Field._internal(this.fieldList); |
| 298 | 306 |
| 299 // Note that the field 'name' may be an arbitrary constant value, so we have | 307 /** |
| 300 // to convert it to a string for sorting. | 308 * Extracts the value for the field that this represents from the instance |
| 301 compareTo(x) => name.toString().compareTo(x.name.toString()); | 309 * mirrored by [mirror] and return it. |
| 310 */ |
| 311 valueIn(InstanceMirror mirror); |
| 302 | 312 |
| 303 // TODO(alanknight): Is this the right name, or is it confusing that essential | 313 // TODO(alanknight): Is this the right name, or is it confusing that essential |
| 304 // is not the inverse of regular. | 314 // is not the inverse of regular. |
| 305 /** Return true if this is field is not used in the constructor. */ | 315 /** Return true if this is field is not used in the constructor. */ |
| 306 bool get isRegular => !usedInConstructor; | 316 bool get isRegular => !usedInConstructor; |
| 307 | 317 |
| 308 /** | 318 /** |
| 309 * Return true if this field is treated as essential state, either because | 319 * Return true if this field is treated as essential state, either because |
| 310 * it is used in the constructor, or because it's been designated | 320 * it is used in the constructor, or because it's been designated |
| 311 * using [specialTreatmentFor]. | 321 * using [setFieldWith]. |
| 312 */ | 322 */ |
| 313 bool get isEssential => usedInConstructor || specialTreatment != null; | 323 bool get isEssential => usedInConstructor; |
| 324 |
| 325 /** Set the [value] of our field in the given mirrored [object]. */ |
| 326 void setValue(InstanceMirror object, value); |
| 327 |
| 328 // Because [x] may not be a named field, we compare the toString. We don't |
| 329 // care that much where constants come in the sort order as long as it's |
| 330 // consistent. |
| 331 compareTo(_Field x) => toString().compareTo(x.toString()); |
| 332 } |
| 333 |
| 334 /** |
| 335 * This represents a field in the object, either stored as a field or |
| 336 * accessed via getter/setter/constructor parameter. It has a name and |
| 337 * will attempt to access the state for that name using an [InstanceMirror]. |
| 338 */ |
| 339 class _NamedField extends _Field { |
| 340 /** The name of the field (or getter) */ |
| 341 final name; |
| 342 |
| 343 /** The special way to set this value registered, if this has a value. */ |
| 344 Function customSetter; |
| 345 |
| 346 _NamedField._internal(this.name, fieldList) : super._internal(fieldList); |
| 347 |
| 348 operator ==(x) => x is _NamedField && (name == x.name); |
| 349 int get hashCode => name.hashCode; |
| 350 |
| 351 /** |
| 352 * Return true if this field is treated as essential state, either because |
| 353 * it is used in the constructor, or because it's been designated |
| 354 * using [setFieldWith]. |
| 355 */ |
| 356 bool get isEssential => super.isEssential || customSetter != null; |
| 314 | 357 |
| 315 /** Set the [value] of our field in the given mirrored [object]. */ | 358 /** Set the [value] of our field in the given mirrored [object]. */ |
| 316 void setValue(InstanceMirror object, value) { | 359 void setValue(InstanceMirror object, value) { |
| 317 setter(object, value); | 360 setter(object, value); |
| 318 } | 361 } |
| 319 | 362 |
| 363 valueIn(InstanceMirror mirror) => mirror.getField(name).value.reflectee; |
| 364 |
| 320 /** Return the function to use to set our value. */ | 365 /** Return the function to use to set our value. */ |
| 321 Function get setter => | 366 Function get setter => |
| 322 (specialTreatment != null) ? specialTreatment : defaultSetter; | 367 (customSetter != null) ? customSetter : defaultSetter; |
| 323 | 368 |
| 324 /** Return a default setter function. */ | 369 /** Return a default setter function. */ |
| 325 void defaultSetter(InstanceMirror object, value) { | 370 void defaultSetter(InstanceMirror object, value) { |
| 326 object.setField(name, reflect(value)); | 371 object.setField(name, reflect(value)); |
| 327 } | 372 } |
| 328 | 373 |
| 329 String toString() => 'Field($name)'; | 374 String toString() => 'Field($name)'; |
| 330 } | 375 } |
| 331 | 376 |
| 332 /** | 377 /** |
| 378 * This represents a constant value that will be passed as a constructor |
| 379 * parameter. Rather than having a name it has a constant value. |
| 380 */ |
| 381 class _ConstantField extends _Field { |
| 382 |
| 383 /** The value we always return.*/ |
| 384 final value; |
| 385 |
| 386 _ConstantField._internal(this.value, fieldList) : super._internal(fieldList); |
| 387 |
| 388 operator ==(x) => x is _ConstantField && (value == x.value); |
| 389 int get hashCode => value.hashCode; |
| 390 String toString() => 'ConstantField($value)'; |
| 391 valueIn(InstanceMirror mirror) => value; |
| 392 |
| 393 /** We cannot be set, so setValue is a no-op. */ |
| 394 void setValue(InstanceMirror object, value) {} |
| 395 |
| 396 /** There are places where the code expects us to have an identifier, so |
| 397 * use the value for that. |
| 398 */ |
| 399 get name => value; |
| 400 } |
| 401 |
| 402 /** |
| 333 * The organization of fields in an object can be reasonably complex, so they | 403 * The organization of fields in an object can be reasonably complex, so they |
| 334 * are kept in a separate object, which also has the ability to compute the | 404 * are kept in a separate object, which also has the ability to compute the |
| 335 * default fields to use reflectively. | 405 * default fields to use reflectively. |
| 336 */ | 406 */ |
| 337 class _FieldList implements Iterable { | 407 class _FieldList implements Iterable { |
| 338 /** | 408 /** |
| 339 * All of our fields, indexed by name. Note that the names are not | 409 * All of our fields, indexed by name. Note that the names are not |
| 340 * necessarily strings. | 410 * necessarily strings. |
| 341 */ | 411 */ |
| 342 Map<dynamic, _Field> allFields = new Map<dynamic, _Field>(); | 412 Map<dynamic, _Field> allFields = new Map<dynamic, _Field>(); |
| (...skipping 16 matching lines...) Expand all Loading... |
| 359 | 429 |
| 360 /** Should we compute the fields or just use whatever we were given. */ | 430 /** Should we compute the fields or just use whatever we were given. */ |
| 361 bool _shouldFigureOutFields = true; | 431 bool _shouldFigureOutFields = true; |
| 362 | 432 |
| 363 _FieldList(this.mirror); | 433 _FieldList(this.mirror); |
| 364 | 434 |
| 365 /** Look up a field by [name]. */ | 435 /** Look up a field by [name]. */ |
| 366 _Field named(String name) => allFields[name]; | 436 _Field named(String name) => allFields[name]; |
| 367 | 437 |
| 368 /** Set the fields to be used in the constructor. */ | 438 /** Set the fields to be used in the constructor. */ |
| 369 set constructorFields(List fields) { | 439 set constructorFields(List fieldNames) { |
| 370 if (fields == null || fields.isEmpty) return; | 440 if (fieldNames == null || fieldNames.isEmpty) return; |
| 371 _constructorFields = []; | 441 _constructorFields = []; |
| 372 for (var each in fields) { | 442 for (var each in fieldNames) { |
| 373 var field = new _Field(each, this)..usedInConstructor = true; | 443 var field = new _Field(each, this)..usedInConstructor = true; |
| 374 allFields[each] = field; | 444 allFields[each] = field; |
| 375 _constructorFields.add(field); | 445 _constructorFields.add(field); |
| 376 } | 446 } |
| 377 invalidate(); | 447 invalidate(); |
| 378 } | 448 } |
| 379 | 449 |
| 380 /** Set the fields that aren't used in the constructor. */ | 450 /** Set the fields that aren't used in the constructor. */ |
| 381 set regular(List<String> fields) { | 451 set regular(List<String> fields) { |
| 382 if (fields == null) return; | 452 if (fields == null) return; |
| (...skipping 12 matching lines...) Expand all Loading... |
| 395 } | 465 } |
| 396 _excludeFields = fields; | 466 _excludeFields = fields; |
| 397 } | 467 } |
| 398 | 468 |
| 399 int get length => allFields.length; | 469 int get length => allFields.length; |
| 400 | 470 |
| 401 /** Add all the fields which aren't on the exclude list. */ | 471 /** Add all the fields which aren't on the exclude list. */ |
| 402 void addAllNotExplicitlyExcluded(List<String> aCollection) { | 472 void addAllNotExplicitlyExcluded(List<String> aCollection) { |
| 403 if (aCollection == null) return; | 473 if (aCollection == null) return; |
| 404 var names = aCollection; | 474 var names = aCollection; |
| 405 names = names.filter((x) => x is String && !_excludeFields.contains(x)); | 475 names = names.filter((x) => !_excludeFields.contains(x)); |
| 406 addAllByName(names); | 476 addAllByName(names); |
| 407 } | 477 } |
| 408 | 478 |
| 409 /** Add all the fields with the given names without any special properties. */ | 479 /** Add all the fields with the given names without any special properties. */ |
| 410 void addAllByName(List<String> names) { | 480 void addAllByName(List<String> names) { |
| 411 for (var each in names) { | 481 for (var each in names) { |
| 412 allFields.putIfAbsent(each, () => new _Field(each, this)); | 482 allFields.putIfAbsent(each, () => new _Field(each, this)); |
| 413 } | 483 } |
| 414 invalidate(); | 484 invalidate(); |
| 415 } | 485 } |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 479 var gettersThatMatchConstructor = getters.filter((each) | 549 var gettersThatMatchConstructor = getters.filter((each) |
| 480 => (named(each.simpleName) != null) && | 550 => (named(each.simpleName) != null) && |
| 481 (named(each.simpleName).usedInConstructor)); | 551 (named(each.simpleName).usedInConstructor)); |
| 482 addAllNotExplicitlyExcluded(names(fields)); | 552 addAllNotExplicitlyExcluded(names(fields)); |
| 483 addAllNotExplicitlyExcluded(names(gettersWithSetters)); | 553 addAllNotExplicitlyExcluded(names(gettersWithSetters)); |
| 484 addAllNotExplicitlyExcluded(names(gettersThatMatchConstructor)); | 554 addAllNotExplicitlyExcluded(names(gettersThatMatchConstructor)); |
| 485 } | 555 } |
| 486 } | 556 } |
| 487 | 557 |
| 488 /** | 558 /** |
| 489 * Provide a typedef for the setWith argument to specialTreatmentFor. It would | 559 * Provide a typedef for the setWith argument to setFieldWith. It would |
| 490 * be nice if we could put this closer to the definition. | 560 * be nice if we could put this closer to the definition. |
| 491 */ | 561 */ |
| 492 typedef SetWithFunction(InstanceMirror m, object); | 562 typedef SetWithFunction(InstanceMirror m, object); |
| 493 | 563 |
| 494 /** | 564 /** |
| 495 * This represents a constructor that is to be used when re-creating a | 565 * This represents a constructor that is to be used when re-creating a |
| 496 * serialized object. | 566 * serialized object. |
| 497 */ | 567 */ |
| 498 class Constructor { | 568 class Constructor { |
| 499 /** The mirror of the class we construct. */ | 569 /** The mirror of the class we construct. */ |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 540 List fieldList; | 610 List fieldList; |
| 541 _MapWrapper(this.fieldList); | 611 _MapWrapper(this.fieldList); |
| 542 _MapWrapper.fromMap(this._map, this.fieldList); | 612 _MapWrapper.fromMap(this._map, this.fieldList); |
| 543 | 613 |
| 544 operator [](key) => _map[fieldList[key].name]; | 614 operator [](key) => _map[fieldList[key].name]; |
| 545 operator []=(key, value) { _map[fieldList[key].name] = value; } | 615 operator []=(key, value) { _map[fieldList[key].name] = value; } |
| 546 get length => _map.length; | 616 get length => _map.length; |
| 547 | 617 |
| 548 asMap() => _map; | 618 asMap() => _map; |
| 549 } | 619 } |
| OLD | NEW |