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