Remove 'id' field from infos, compute an id during serialization (#59)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d4b9042..f9a4cf4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 0.6.0-dev.0.0
+
+* The fields `Info.id` and `Info.serializedId` have been removed. These
+ properties were only used for serialization and deserialization. Those values
+ are now computed during the serialization process instead.
+
## 0.5.17
* Make `live_code_size_analysis` print library URIs and not library names.
diff --git a/lib/info.dart b/lib/info.dart
index 8bc7e4c..a3cb639 100644
--- a/lib/info.dart
+++ b/lib/info.dart
@@ -27,14 +27,6 @@
/// Name of the element associated with this info.
String name;
- /// An id to uniquely identify this info among infos of the same [kind].
- // TODO(kevmoo) Consider removing `id` entirely. Make it an impl detail of
- // the JSON encoder
- int get id;
-
- /// A globally unique id combining [kind] and [id] together.
- String get serializedId;
-
/// Id used by the compiler when instrumenting code for code coverage.
// TODO(sigmund): It would be nice if we could use the same id for
// serialization and for coverage. Could we unify them?
@@ -53,46 +45,12 @@
// TODO(sigmund): add more:
// - inputSize: bytes used in the Dart source program
abstract class BasicInfo implements Info {
- static final Set<int> _ids = new Set<int>();
-
- /// Frees internal cache used for id uniqueness.
- static void resetIds() => BasicInfo._ids.clear();
-
final InfoKind kind;
- int _id;
- // TODO(kevmoo) Make computation of id explicit and not on-demand.
- int get id {
- if (_id == null) {
- assert(this is LibraryInfo ||
- this is ConstantInfo ||
- this is OutputUnitInfo ||
- this.parent != null);
-
- if (this is ConstantInfo) {
- // No name and no parent, so `longName` isn't helpful
- assert(this.name == null);
- assert(this.parent == null);
- assert((this as ConstantInfo).code != null);
- // Instead, use the content of the code.
- _id = (this as ConstantInfo).code.hashCode;
- } else {
- _id = longName(this, useLibraryUri: true, forId: true).hashCode;
- }
- while (!_ids.add(_id)) {
- _id++;
- }
- }
-
- return _id;
- }
-
String coverageId;
int size;
Info parent;
- String get serializedId => '${kindToString(kind)}/$id';
-
String name;
/// If using deferred libraries, where the element associated with this info
@@ -101,18 +59,16 @@
BasicInfo(this.kind, this.name, this.outputUnit, this.size, this.coverageId);
- BasicInfo._fromId(String serializedId)
- : kind = _kindFromSerializedId(serializedId),
- _id = _idFromSerializedId(serializedId);
+ BasicInfo._(this.kind);
- String toString() => '$serializedId $name [$size]';
+ String toString() => '$kind $name [$size]';
}
/// Info associated with elements containing executable code (like fields and
/// methods)
abstract class CodeInfo implements Info {
/// How does this function or field depend on others.
- final Set<DependencyInfo> uses = new SplayTreeSet<DependencyInfo>();
+ final List<DependencyInfo> uses = [];
}
/// The entire information produced while compiling a program.
@@ -250,7 +206,7 @@
LibraryInfo(String name, this.uri, OutputUnitInfo outputUnit, int size)
: super(InfoKind.library, name, outputUnit, size, null);
- LibraryInfo._(String serializedId) : super._fromId(serializedId);
+ LibraryInfo._() : super._(InfoKind.library);
T accept<T>(InfoVisitor<T> visitor) => visitor.visitLibrary(this);
}
@@ -265,7 +221,7 @@
OutputUnitInfo(String name, int size)
: super(InfoKind.outputUnit, name, null, size, null);
- OutputUnitInfo._(String serializedId) : super._fromId(serializedId);
+ OutputUnitInfo._() : super._(InfoKind.outputUnit);
T accept<T>(InfoVisitor<T> visitor) => visitor.visitOutput(this);
}
@@ -288,7 +244,7 @@
{String name, this.isAbstract, OutputUnitInfo outputUnit, int size: 0})
: super(InfoKind.clazz, name, outputUnit, size, null);
- ClassInfo._(String serializedId) : super._fromId(serializedId);
+ ClassInfo._() : super._(InfoKind.clazz);
T accept<T>(InfoVisitor<T> visitor) => visitor.visitClass(this);
}
@@ -303,7 +259,7 @@
ConstantInfo({int size: 0, this.code, OutputUnitInfo outputUnit})
: super(InfoKind.constant, null, outputUnit, size, null);
- ConstantInfo._(String serializedId) : super._fromId(serializedId);
+ ConstantInfo._() : super._(InfoKind.constant);
T accept<T>(InfoVisitor<T> visitor) => visitor.visitConstant(this);
}
@@ -340,7 +296,7 @@
this.isConst})
: super(InfoKind.field, name, outputUnit, size, coverageId);
- FieldInfo._(String serializedId) : super._fromId(serializedId);
+ FieldInfo._() : super._(InfoKind.field);
T accept<T>(InfoVisitor<T> visitor) => visitor.visitField(this);
}
@@ -353,7 +309,7 @@
TypedefInfo(String name, this.type, OutputUnitInfo outputUnit)
: super(InfoKind.typedef, name, outputUnit, 0, null);
- TypedefInfo._(String serializedId) : super._fromId(serializedId);
+ TypedefInfo._() : super._(InfoKind.typedef);
T accept<T>(InfoVisitor<T> visitor) => visitor.visitTypedef(this);
}
@@ -417,7 +373,7 @@
this.measurements})
: super(InfoKind.function, name, outputUnit, size, coverageId);
- FunctionInfo._(String serializedId) : super._fromId(serializedId);
+ FunctionInfo._() : super._(InfoKind.function);
T accept<T>(InfoVisitor<T> visitor) => visitor.visitFunction(this);
}
@@ -431,13 +387,13 @@
{String name, OutputUnitInfo outputUnit, int size: 0, this.function})
: super(InfoKind.closure, name, outputUnit, size, null);
- ClosureInfo._(String serializedId) : super._fromId(serializedId);
+ ClosureInfo._() : super._(InfoKind.closure);
T accept<T>(InfoVisitor<T> visitor) => visitor.visitClosure(this);
}
/// Information about how a dependency is used.
-class DependencyInfo implements Comparable<DependencyInfo> {
+class DependencyInfo {
/// The dependency, either a FunctionInfo or FieldInfo.
final Info target;
@@ -447,21 +403,6 @@
final String mask;
DependencyInfo(this.target, this.mask);
-
- int compareTo(DependencyInfo other) {
- var value = target.serializedId.compareTo(other.target.serializedId);
- if (value == 0) {
- value = mask.compareTo(other.mask);
- }
- return value;
- }
-
- bool operator ==(other) =>
- other is DependencyInfo &&
- target.serializedId == other.target.serializedId &&
- mask == other.mask;
-
- int get hashCode => target.serializedId.hashCode * 37 ^ mask.hashCode;
}
/// Name and type information about a function parameter.
@@ -522,12 +463,6 @@
}
}
-int _idFromSerializedId(String serializedId) =>
- int.parse(serializedId.substring(serializedId.indexOf('/') + 1));
-
-InfoKind _kindFromSerializedId(String serializedId) =>
- kindFromString(serializedId.substring(0, serializedId.indexOf('/')));
-
InfoKind kindFromString(String kind) {
switch (kind) {
case 'library':
diff --git a/lib/json_info_codec.dart b/lib/json_info_codec.dart
index ea4b52e..ec0e6c1 100644
--- a/lib/json_info_codec.dart
+++ b/lib/json_info_codec.dart
@@ -5,8 +5,8 @@
/// Converters and codecs for converting between JSON and [Info] classes.
part of dart2js_info.info;
-List<String> _toSortedSerializIds(Iterable<Info> infos) =>
- infos.map((i) => i.serializedId).toList()..sort(compareNatural);
+List<String> _toSortedSerializedIds(Iterable<Info> infos, Map<Info, Id> ids) =>
+ infos.map((i) => ids[i].serializedId).toList()..sort(compareNatural);
// TODO(sigmund): add unit tests.
class JsonToAllInfoConverter extends Converter<Map<String, dynamic>, AllInfo> {
@@ -41,19 +41,11 @@
result.constants.addAll(
(elements['constant'] as Map).values.map((c) => parseConstant(c)));
- var idMap = new HashMap<String, Info>();
- for (var f in result.functions) {
- idMap[f.serializedId] = f;
- }
- for (var f in result.fields) {
- idMap[f.serializedId] = f;
- }
-
json['holding'].forEach((k, deps) {
- var src = idMap[k];
+ var src = registry[k];
assert(src != null);
for (var dep in deps) {
- var target = idMap[dep['id']];
+ var target = registry[dep['id']];
assert(target != null);
(src as CodeInfo).uses.add(new DependencyInfo(target, dep['mask']));
}
@@ -61,7 +53,7 @@
json['dependencies']?.forEach((String k, dependencies) {
List<String> deps = dependencies;
- result.dependencies[idMap[k]] = deps.map((d) => idMap[d]).toList();
+ result.dependencies[registry[k]] = deps.map((d) => registry[d]).toList();
});
result.outputUnits
@@ -310,21 +302,21 @@
}
return registry.putIfAbsent(serializedId, () {
if (serializedId.startsWith('function/')) {
- return new FunctionInfo._(serializedId);
+ return new FunctionInfo._();
} else if (serializedId.startsWith('closure/')) {
- return new ClosureInfo._(serializedId);
+ return new ClosureInfo._();
} else if (serializedId.startsWith('library/')) {
- return new LibraryInfo._(serializedId);
+ return new LibraryInfo._();
} else if (serializedId.startsWith('class/')) {
- return new ClassInfo._(serializedId);
+ return new ClassInfo._();
} else if (serializedId.startsWith('field/')) {
- return new FieldInfo._(serializedId);
+ return new FieldInfo._();
} else if (serializedId.startsWith('constant/')) {
- return new ConstantInfo._(serializedId);
+ return new ConstantInfo._();
} else if (serializedId.startsWith('typedef/')) {
- return new TypedefInfo._(serializedId);
+ return new TypedefInfo._();
} else if (serializedId.startsWith('outputUnit/')) {
- return new OutputUnitInfo._(serializedId);
+ return new OutputUnitInfo._();
}
assert(false);
});
@@ -333,13 +325,43 @@
class AllInfoToJsonConverter extends Converter<AllInfo, Map>
implements InfoVisitor<Map> {
+ final Map<Info, Id> ids = new HashMap<Info, Id>();
+ final Set<int> usedIds = new Set<int>();
+
+ Id idFor(Info info) {
+ var serializedId = ids[info];
+ if (serializedId != null) return serializedId;
+
+ assert(info is LibraryInfo ||
+ info is ConstantInfo ||
+ info is OutputUnitInfo ||
+ info.parent != null);
+
+ int id;
+ if (info is ConstantInfo) {
+ // No name and no parent, so `longName` isn't helpful
+ assert(info.name == null);
+ assert(info.parent == null);
+ assert(info.code != null);
+ // Instead, use the content of the code.
+ id = info.code.hashCode;
+ } else {
+ id = longName(info, useLibraryUri: true, forId: true).hashCode;
+ }
+ while (!usedIds.add(id)) {
+ id++;
+ }
+ serializedId = new Id(info.kind, id);
+ return ids[info] = serializedId;
+ }
+
Map convert(AllInfo info) => info.accept(this);
Map _visitList(List<Info> infos) {
// Using SplayTree to maintain a consistent order of keys
var map = new SplayTreeMap<String, Map>(compareNatural);
for (var info in infos) {
- map['${info.id}'] = info.accept(this);
+ map['${idFor(info).id}'] = info.accept(this);
}
return map;
}
@@ -364,13 +386,16 @@
}
Map _visitDependencyInfo(DependencyInfo info) =>
- {'id': info.target.serializedId, 'mask': info.mask};
+ {'id': idFor(info.target).serializedId, 'mask': info.mask};
Map _visitAllInfoHolding(AllInfo allInfo) {
var map = new SplayTreeMap<String, List>(compareNatural);
void helper(CodeInfo info) {
if (info.uses.isEmpty) return;
- map[info.serializedId] = info.uses.map(_visitDependencyInfo).toList();
+ map[idFor(info).serializedId] = info.uses
+ .map(_visitDependencyInfo)
+ .toList()
+ ..sort((a, b) => a['id'].compareTo(b['id']));
}
allInfo.functions.forEach(helper);
@@ -381,7 +406,7 @@
Map _visitAllInfoDependencies(AllInfo allInfo) {
var map = new SplayTreeMap<String, List>(compareNatural);
allInfo.dependencies.forEach((k, v) {
- map[k.serializedId] = _toSortedSerializIds(v);
+ map[idFor(k).serializedId] = _toSortedSerializedIds(v, ids);
});
return map;
}
@@ -404,7 +429,7 @@
Map visitProgram(ProgramInfo info) {
return {
- 'entrypoint': info.entrypoint.serializedId,
+ 'entrypoint': idFor(info.entrypoint),
'size': info.size,
'dart2jsVersion': info.dart2jsVersion,
'compilationMoment': '${info.compilationMoment}',
@@ -422,7 +447,7 @@
Map _visitBasicInfo(BasicInfo info) {
var res = {
- 'id': info.serializedId,
+ 'id': idFor(info).serializedId,
'kind': kindToString(info.kind),
'name': info.name,
'size': info.size,
@@ -430,22 +455,24 @@
// TODO(sigmund): Omit this also when outputUnit.id == 0 (most code is in
// the main output unit by default).
if (info.outputUnit != null) {
- res['outputUnit'] = info.outputUnit.serializedId;
+ res['outputUnit'] = idFor(info.outputUnit).serializedId;
}
if (info.coverageId != null) res['coverageId'] = info.coverageId;
- if (info.parent != null) res['parent'] = info.parent.serializedId;
+ if (info.parent != null) res['parent'] = idFor(info.parent).serializedId;
return res;
}
Map visitLibrary(LibraryInfo info) {
return _visitBasicInfo(info)
..addAll(<String, Object>{
- 'children': _toSortedSerializIds([
- info.topLevelFunctions,
- info.topLevelVariables,
- info.classes,
- info.typedefs
- ].expand((i) => i)),
+ 'children': _toSortedSerializedIds(
+ [
+ info.topLevelFunctions,
+ info.topLevelVariables,
+ info.classes,
+ info.typedefs
+ ].expand((i) => i),
+ ids),
'canonicalUri': '${info.uri}',
});
}
@@ -455,15 +482,15 @@
..addAll(<String, Object>{
// TODO(sigmund): change format, include only when abstract is true.
'modifiers': {'abstract': info.isAbstract},
- 'children':
- _toSortedSerializIds([info.fields, info.functions].expand((i) => i))
+ 'children': _toSortedSerializedIds(
+ [info.fields, info.functions].expand((i) => i), ids)
});
}
Map visitField(FieldInfo info) {
var result = _visitBasicInfo(info)
..addAll(<String, Object>{
- 'children': _toSortedSerializIds(info.closures),
+ 'children': _toSortedSerializedIds(info.closures, ids),
'inferredType': info.inferredType,
'code': info.code,
'type': info.type,
@@ -471,7 +498,7 @@
if (info.isConst) {
result['const'] = true;
if (info.initializer != null) {
- result['initializer'] = info.initializer.serializedId;
+ result['initializer'] = idFor(info.initializer).serializedId;
}
}
return result;
@@ -524,7 +551,7 @@
Map visitFunction(FunctionInfo info) {
return _visitBasicInfo(info)
..addAll(<String, Object>{
- 'children': _toSortedSerializIds(info.closures),
+ 'children': _toSortedSerializedIds(info.closures, ids),
'modifiers': _visitFunctionModifiers(info.modifiers),
'returnType': info.returnType,
'inferredReturnType': info.inferredReturnType,
@@ -542,7 +569,7 @@
Map visitClosure(ClosureInfo info) {
return _visitBasicInfo(info)
- ..addAll(<String, Object>{'function': info.function.serializedId});
+ ..addAll(<String, Object>{'function': idFor(info.function).serializedId});
}
visitTypedef(TypedefInfo info) => _visitBasicInfo(info)..['type'] = info.type;
@@ -555,3 +582,12 @@
final Converter<AllInfo, Map> encoder = new AllInfoToJsonConverter();
final Converter<Map, AllInfo> decoder = new JsonToAllInfoConverter();
}
+
+class Id {
+ final InfoKind kind;
+ final int id;
+
+ Id(this.kind, this.id);
+
+ String get serializedId => '$kind/$id';
+}
diff --git a/lib/proto_info_codec.dart b/lib/proto_info_codec.dart
index cbeb35c..db9b6a0 100644
--- a/lib/proto_info_codec.dart
+++ b/lib/proto_info_codec.dart
@@ -9,6 +9,7 @@
import 'info.dart';
import 'src/proto/info.pb.dart';
+import 'src/util.dart';
export 'src/proto/info.pb.dart';
@@ -23,11 +24,42 @@
}
class AllInfoToProtoConverter extends Converter<AllInfo, AllInfoPB> {
+ final Map<Info, Id> ids = {};
+ final Set<int> usedIds = new Set<int>();
+
+ Id idFor(Info info) {
+ if (info == null) return null;
+ var serializedId = ids[info];
+ if (serializedId != null) return serializedId;
+
+ assert(info is LibraryInfo ||
+ info is ConstantInfo ||
+ info is OutputUnitInfo ||
+ info.parent != null);
+
+ int id;
+ if (info is ConstantInfo) {
+ // No name and no parent, so `longName` isn't helpful
+ assert(info.name == null);
+ assert(info.parent == null);
+ assert(info.code != null);
+ // Instead, use the content of the code.
+ id = info.code.hashCode;
+ } else {
+ id = longName(info, useLibraryUri: true, forId: true).hashCode;
+ }
+ while (!usedIds.add(id)) {
+ id++;
+ }
+ serializedId = new Id(info.kind, id);
+ return ids[info] = serializedId;
+ }
+
AllInfoPB convert(AllInfo info) => _convertToAllInfoPB(info);
- static DependencyInfoPB _convertToDependencyInfoPB(DependencyInfo info) {
+ DependencyInfoPB _convertToDependencyInfoPB(DependencyInfo info) {
return new DependencyInfoPB()
- ..targetId = info.target?.serializedId
+ ..targetId = idFor(info.target)?.serializedId
..mask = info.mask;
}
@@ -65,24 +97,28 @@
return proto;
}
- static LibraryInfoPB _convertToLibraryInfoPB(LibraryInfo info) {
+ LibraryInfoPB _convertToLibraryInfoPB(LibraryInfo info) {
final proto = new LibraryInfoPB()..uri = info.uri.toString();
proto.childrenIds
- .addAll(info.topLevelFunctions.map((func) => func.serializedId));
+ .addAll(info.topLevelFunctions.map((func) => idFor(func).serializedId));
+ proto.childrenIds.addAll(
+ info.topLevelVariables.map((field) => idFor(field).serializedId));
proto.childrenIds
- .addAll(info.topLevelVariables.map((field) => field.serializedId));
- proto.childrenIds.addAll(info.classes.map((clazz) => clazz.serializedId));
- proto.childrenIds.addAll(info.typedefs.map((def) => def.serializedId));
+ .addAll(info.classes.map((clazz) => idFor(clazz).serializedId));
+ proto.childrenIds
+ .addAll(info.typedefs.map((def) => idFor(def).serializedId));
return proto;
}
- static ClassInfoPB _convertToClassInfoPB(ClassInfo info) {
+ ClassInfoPB _convertToClassInfoPB(ClassInfo info) {
final proto = new ClassInfoPB()..isAbstract = info.isAbstract;
- proto.childrenIds.addAll(info.functions.map((func) => func.serializedId));
- proto.childrenIds.addAll(info.fields.map((field) => field.serializedId));
+ proto.childrenIds
+ .addAll(info.functions.map((func) => idFor(func).serializedId));
+ proto.childrenIds
+ .addAll(info.fields.map((field) => idFor(field).serializedId));
return proto;
}
@@ -96,7 +132,7 @@
..isExternal = modifiers.isExternal;
}
- static FunctionInfoPB _convertToFunctionInfoPB(FunctionInfo info) {
+ FunctionInfoPB _convertToFunctionInfoPB(FunctionInfo info) {
final proto = new FunctionInfoPB()
..functionModifiers = _convertToFunctionModifiers(info.modifiers)
..inlinedCount = info.inlinedCount ?? 0;
@@ -122,13 +158,13 @@
}
proto.childrenIds
- .addAll(info.closures.map(((closure) => closure.serializedId)));
+ .addAll(info.closures.map(((closure) => idFor(closure).serializedId)));
proto.parameters.addAll(info.parameters.map(_convertToParameterInfoPB));
return proto;
}
- static FieldInfoPB _convertToFieldInfoPB(FieldInfo info) {
+ FieldInfoPB _convertToFieldInfoPB(FieldInfo info) {
final proto = new FieldInfoPB()
..type = info.type
..inferredType = info.inferredType
@@ -139,11 +175,11 @@
}
if (info.initializer != null) {
- proto.initializerId = info.initializer.serializedId;
+ proto.initializerId = idFor(info.initializer).serializedId;
}
proto.childrenIds
- .addAll(info.closures.map((closure) => closure.serializedId));
+ .addAll(info.closures.map((closure) => idFor(closure).serializedId));
return proto;
}
@@ -162,14 +198,14 @@
return new TypedefInfoPB()..type = info.type;
}
- static ClosureInfoPB _convertToClosureInfoPB(ClosureInfo info) {
- return new ClosureInfoPB()..functionId = info.function.serializedId;
+ ClosureInfoPB _convertToClosureInfoPB(ClosureInfo info) {
+ return new ClosureInfoPB()..functionId = idFor(info.function).serializedId;
}
- static InfoPB _convertToInfoPB(Info info) {
+ InfoPB _convertToInfoPB(Info info) {
final proto = new InfoPB()
- ..id = info.id
- ..serializedId = info.serializedId
+ ..id = idFor(info).id
+ ..serializedId = idFor(info).serializedId
..size = info.size;
if (info.name != null) {
@@ -177,7 +213,7 @@
}
if (info.parent != null) {
- proto.parentId = info.parent.serializedId;
+ proto.parentId = idFor(info.parent).serializedId;
}
if (info.coverageId != null) {
@@ -188,7 +224,7 @@
// TODO(lorenvs): Similar to the JSON codec, omit this for the default
// output unit. At the moment, there is no easy way to identify which
// output unit is the default on [OutputUnitInfo].
- proto.outputUnitId = info.outputUnit.serializedId;
+ proto.outputUnitId = idFor(info.outputUnit).serializedId;
}
if (info is CodeInfo) {
@@ -216,9 +252,9 @@
return proto;
}
- static ProgramInfoPB _convertToProgramInfoPB(ProgramInfo info) {
+ ProgramInfoPB _convertToProgramInfoPB(ProgramInfo info) {
return new ProgramInfoPB()
- ..entrypointId = info.entrypoint.serializedId
+ ..entrypointId = idFor(info.entrypoint).serializedId
..size = info.size
..dart2jsVersion = info.dart2jsVersion
..compilationMoment =
@@ -234,8 +270,8 @@
..minified = info.minified ?? false;
}
- static Iterable<AllInfoPB_AllInfosEntry>
- _convertToAllInfosEntries<T extends Info>(Iterable<T> infos) sync* {
+ Iterable<AllInfoPB_AllInfosEntry> _convertToAllInfosEntries<T extends Info>(
+ Iterable<T> infos) sync* {
for (final info in infos) {
final infoProto = _convertToInfoPB(info);
final entry = new AllInfoPB_AllInfosEntry()
@@ -261,7 +297,7 @@
return proto;
}
- static AllInfoPB _convertToAllInfoPB(AllInfo info) {
+ AllInfoPB _convertToAllInfoPB(AllInfo info) {
final proto = new AllInfoPB()
..program = _convertToProgramInfoPB(info.program);
@@ -291,3 +327,12 @@
final Converter<AllInfo, AllInfoPB> encoder = new AllInfoToProtoConverter();
final Converter<AllInfoPB, AllInfo> decoder = new ProtoToAllInfoConverter();
}
+
+class Id {
+ final InfoKind kind;
+ final int id;
+
+ Id(this.kind, this.id);
+
+ String get serializedId => '$kind/$id';
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index 29f99fa..1a12162 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: dart2js_info
-version: 0.5.17
+version: 0.6.0-dev
description: >
Libraries and tools to process data produced when running dart2js with