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 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
130 useMaps = false; | 130 useMaps = false; |
131 } | 131 } |
132 | 132 |
133 /** Create either a list or a map to hold the object's state, depending | 133 /** Create either a list or a map to hold the object's state, depending |
134 * on the [useMaps] variable. If using a Map, we wrap it in order to keep | 134 * on the [useMaps] variable. If using a Map, we wrap it in order to keep |
135 * the protocol compatible. See [configureForLists]/[configureForMaps]. | 135 * the protocol compatible. See [configureForLists]/[configureForMaps]. |
136 */ | 136 */ |
137 createStateHolder() => | 137 createStateHolder() => |
138 useMaps ? new _MapWrapper(fields.contents) : new List(fields.length); | 138 useMaps ? new _MapWrapper(fields.contents) : new List(fields.length); |
139 | 139 |
140 /** Wrap the state if it's passed in as a map. */ | 140 /** |
141 makeIndexableByNumber(state) => | 141 * Wrap the state if it's passed in as a map, and if the keys are references, |
142 (state is Map) ? new _MapWrapper.fromMap(state, fields.contents) : state; | 142 * resolve them to the strings we expect. We leave the previous keys in there |
| 143 * as well, as they shouldn't be harmful, and it costs more to remove them. |
| 144 */ |
| 145 makeIndexableByNumber(state) { |
| 146 if (!(state is Map)) return state; |
| 147 // TODO(alanknight): This is quite inefficient, and we do it twice per |
| 148 // instance. If the keys are references, we need to turn them into strings |
| 149 // before we can look at indexing them by field position. It's also eager, |
| 150 // but we know our keys are always primitives, so we don't have to worry |
| 151 // about their instances not having been created yet. |
| 152 for (var each in state.keys) { |
| 153 if (each is Reference) { |
| 154 var inflated = each.inflated(); |
| 155 state[inflated] = state[each]; |
| 156 } |
| 157 } |
| 158 return new _MapWrapper.fromMap(state, fields.contents); |
| 159 } |
143 | 160 |
144 /** | 161 /** |
145 * Extract the state from [object] using an instanceMirror and the field | 162 * Extract the state from [object] using an instanceMirror and the field |
146 * names in [fields]. Call the function [callback] on each value. | 163 * names in [fields]. Call the function [callback] on each value. |
147 */ | 164 */ |
148 extractState(object, Function callback) { | 165 extractState(object, Function callback) { |
149 var result = createStateHolder(); | 166 var result = createStateHolder(); |
150 var mirror = reflect(object); | 167 var mirror = reflect(object); |
151 | 168 |
152 keysAndValues(fields).forEach( | 169 keysAndValues(fields).forEach( |
153 (index, field) { | 170 (index, field) { |
154 var value = _value(mirror, field); | 171 var value = _value(mirror, field); |
| 172 callback(field.name); |
155 callback(checkForEssentialLists(index, value)); | 173 callback(checkForEssentialLists(index, value)); |
156 result[index] = value; | 174 result[index] = value; |
157 }); | 175 }); |
158 return _unwrap(result); | 176 return _unwrap(result); |
159 } | 177 } |
160 | 178 |
| 179 flatten(state, Writer writer) { |
| 180 if (state is List) { |
| 181 keysAndValues(state).forEach((index, value) { |
| 182 state[index] = writer._referenceFor(value); |
| 183 }); |
| 184 } else { |
| 185 var newState = new Map(); |
| 186 keysAndValues(state).forEach((key, value) { |
| 187 newState[writer._referenceFor(key)] = writer._referenceFor(value); |
| 188 }); |
| 189 return newState; |
| 190 } |
| 191 } |
| 192 |
161 /** | 193 /** |
162 * If the value is a List, and the field is a constructor field or | 194 * If the value is a List, and the field is a constructor field or |
163 * otherwise specially designated, we wrap it in something that indicates | 195 * otherwise specially designated, we wrap it in something that indicates |
164 * a restriction on the rules that can be used. Which in this case amounts | 196 * a restriction on the rules that can be used. Which in this case amounts |
165 * to designating the rule, since we so far only have one rule per object. | 197 * to designating the rule, since we so far only have one rule per object. |
166 */ | 198 */ |
167 checkForEssentialLists(index, value) { | 199 checkForEssentialLists(index, value) { |
168 if (value is List && fields.contents[index].isEssential) { | 200 if (value is List && fields.contents[index].isEssential) { |
169 return new DesignatedRuleForObject(value, | 201 return new DesignatedRuleForObject(value, |
170 (SerializationRule rule) => rule is ListRuleEssential); | 202 (SerializationRule rule) => rule is ListRuleEssential); |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
212 void _findFields(List constructorFields, List regularFields, | 244 void _findFields(List constructorFields, List regularFields, |
213 List excludeFields) { | 245 List excludeFields) { |
214 fields = new _FieldList(type); | 246 fields = new _FieldList(type); |
215 fields.constructorFields = constructorFields; | 247 fields.constructorFields = constructorFields; |
216 fields.regular = regularFields; | 248 fields.regular = regularFields; |
217 // TODO(alanknight): The order of this matters. It shouldn't. | 249 // TODO(alanknight): The order of this matters. It shouldn't. |
218 fields.exclude = excludeFields; | 250 fields.exclude = excludeFields; |
219 fields.figureOutFields(); | 251 fields.figureOutFields(); |
220 } | 252 } |
221 | 253 |
| 254 bool get hasVariableLengthEntries => false; |
| 255 |
| 256 int get dataLength => fields.length; |
| 257 |
222 /** | 258 /** |
223 * Extract the value of the field [fieldName] from the object reflected | 259 * Extract the value of [field] from the object reflected |
224 * by [mirror]. | 260 * by [mirror]. |
225 */ | 261 */ |
226 // TODO(alanknight): The framework should be resilient if there are fields | 262 // TODO(alanknight): The framework should be resilient if there are fields |
227 // it expects that are missing, either for the case of de-serializing to a | 263 // it expects that are missing, either for the case of de-serializing to a |
228 // different definition, or for the case that tree-shaking has removed state. | 264 // different definition, or for the case that tree-shaking has removed state. |
229 // TODO(alanknight): This, and other places, rely on synchronous access to | 265 // TODO(alanknight): This, and other places, rely on synchronous access to |
230 // mirrors. Should be changed to use a synchronous API once one is available, | 266 // mirrors. Should be changed to use a synchronous API once one is available, |
231 // or to be async, but that would be extremely ugly. | 267 // or to be async, but that would be extremely ugly. |
232 _value(InstanceMirror mirror, _Field field) => field.valueIn(mirror); | 268 _value(InstanceMirror mirror, _Field field) => field.valueIn(mirror); |
233 } | 269 } |
(...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
507 regularFields().mappedBy((x) => x.index).toList(); | 543 regularFields().mappedBy((x) => x.index).toList(); |
508 | 544 |
509 /** | 545 /** |
510 * If we weren't given any non-constructor fields to use, figure out what | 546 * If we weren't given any non-constructor fields to use, figure out what |
511 * we think they ought to be, based on the class definition. | 547 * we think they ought to be, based on the class definition. |
512 * We find public fields, getters that have corresponding setters, and getters | 548 * We find public fields, getters that have corresponding setters, and getters |
513 * that are listed in the constructor fields. | 549 * that are listed in the constructor fields. |
514 */ | 550 */ |
515 void figureOutFields() { | 551 void figureOutFields() { |
516 Iterable names(Iterable<DeclarationMirror> mirrors) => | 552 Iterable names(Iterable<DeclarationMirror> mirrors) => |
517 mirrors.mappedBy((each) => each.simpleName); | 553 mirrors.mappedBy((each) => each.simpleName).toList(); |
518 | 554 |
519 if (!_shouldFigureOutFields || !regularFields().isEmpty) return; | 555 if (!_shouldFigureOutFields || !regularFields().isEmpty) return; |
520 var fields = publicFields(mirror); | 556 var fields = publicFields(mirror); |
521 var getters = publicGetters(mirror); | 557 var getters = publicGetters(mirror); |
522 var gettersWithSetters = getters.where( (each) | 558 var gettersWithSetters = getters.where( (each) |
523 => mirror.setters["${each.simpleName}="] != null); | 559 => mirror.setters["${each.simpleName}="] != null); |
524 var gettersThatMatchConstructor = getters.where((each) | 560 var gettersThatMatchConstructor = getters.where((each) |
525 => (named(each.simpleName) != null) && | 561 => (named(each.simpleName) != null) && |
526 (named(each.simpleName).usedInConstructor)).toList(); | 562 (named(each.simpleName).usedInConstructor)).toList(); |
527 addAllNotExplicitlyExcluded(names(fields)); | 563 addAllNotExplicitlyExcluded(names(fields)); |
528 addAllNotExplicitlyExcluded(names(gettersWithSetters)); | 564 addAllNotExplicitlyExcluded(names(gettersWithSetters)); |
529 addAllNotExplicitlyExcluded(names(gettersThatMatchConstructor)); | 565 addAllNotExplicitlyExcluded(names(gettersThatMatchConstructor)); |
530 } | 566 } |
| 567 |
| 568 toString() => "FieldList($contents)"; |
531 } | 569 } |
532 | 570 |
533 /** | 571 /** |
534 * Provide a typedef for the setWith argument to setFieldWith. It would | 572 * Provide a typedef for the setWith argument to setFieldWith. It would |
535 * be nice if we could put this closer to the definition. | 573 * be nice if we could put this closer to the definition. |
536 */ | 574 */ |
537 typedef SetWithFunction(InstanceMirror m, object); | 575 typedef SetWithFunction(InstanceMirror m, object); |
538 | 576 |
539 /** | 577 /** |
540 * This represents a constructor that is to be used when re-creating a | 578 * This represents a constructor that is to be used when re-creating a |
(...skipping 22 matching lines...) Expand all Loading... |
563 if (fieldNumbers == null) fieldNumbers = const []; | 601 if (fieldNumbers == null) fieldNumbers = const []; |
564 } | 602 } |
565 | 603 |
566 /** | 604 /** |
567 * Find the field values in [state] and pass them to the constructor. | 605 * Find the field values in [state] and pass them to the constructor. |
568 * If any of [fieldNumbers] is not an int, then use it as a literal value. | 606 * If any of [fieldNumbers] is not an int, then use it as a literal value. |
569 */ | 607 */ |
570 constructFrom(state, Reader r) { | 608 constructFrom(state, Reader r) { |
571 // TODO(alanknight): Handle named parameters | 609 // TODO(alanknight): Handle named parameters |
572 Collection inflated = fieldNumbers.mappedBy( | 610 Collection inflated = fieldNumbers.mappedBy( |
573 (x) => (x is int) ? reflect(r.inflateReference(state[x])) : reflect(x)) | 611 (x) => (x is int) ? reflect(r.inflateReference(state[x])) : reflect(x)); |
574 .toList(); | 612 var result = type.newInstance(name, inflated.toList()); |
575 var result = type.newInstance(name, inflated); | |
576 return deprecatedFutureValue(result); | 613 return deprecatedFutureValue(result); |
577 } | 614 } |
578 } | 615 } |
579 | 616 |
580 /** | 617 /** |
581 * This wraps a map to make it indexable by integer field numbers. It translates | 618 * This wraps a map to make it indexable by integer field numbers. It translates |
582 * from the index into a field name and then looks it up in the map. | 619 * from the index into a field name and then looks it up in the map. |
583 */ | 620 */ |
584 class _MapWrapper { | 621 class _MapWrapper { |
585 Map<String, dynamic> _map = new Map<String, dynamic>(); | 622 final _map; |
586 List fieldList; | 623 List fieldList; |
587 _MapWrapper(this.fieldList); | 624 _MapWrapper(this.fieldList) : _map = new Map(); |
588 _MapWrapper.fromMap(this._map, this.fieldList); | 625 _MapWrapper.fromMap(this._map, this.fieldList); |
589 | 626 |
590 operator [](key) => _map[fieldList[key].name]; | 627 operator [](key) => _map[fieldList[key].name]; |
| 628 |
591 operator []=(key, value) { _map[fieldList[key].name] = value; } | 629 operator []=(key, value) { _map[fieldList[key].name] = value; } |
592 get length => _map.length; | 630 get length => _map.length; |
593 | 631 |
594 asMap() => _map; | 632 asMap() => _map; |
595 } | 633 } |
OLD | NEW |