Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Model.qualifiedNameOfMember, add fast way to find the path to JsonBufferBuilder #87

Merged
merged 3 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions pkgs/dart_model/lib/src/dart_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.

import 'dart_model.g.dart';
import 'json_buffer/json_buffer_builder.dart';

export 'dart_model.g.dart';

Expand All @@ -11,3 +12,90 @@ extension QualifiedNameExtension on QualifiedName {

bool equals(QualifiedName other) => other.uri == uri && other.name == name;
}

extension ModelExtension on Model {
/// Returns the path in the model to [member], or `null` if [member] is not
/// in this [Model].
///
/// Comparison is by identity, not by value, so the exact instance must be in
/// this [Model].
///
/// TODO(davidmorgan): this works for any node, but it's not clear yet which
/// types of node we want this functionality exposed for.
/// TODO(davidmorgan): a list of path segments is probably more useful than
/// `String`.
QualifiedName? qualifiedNameOfMember(Member member) =>
_qualifiedNameOf(member.node);

/// Returns the [QualifiedName] in the model to [node], or `null` if [node] is not in this [Model].
QualifiedName? _qualifiedNameOf(Map<String, Object?> node) {
final members = _getParent(node);
if (members == null) return null;
final interface = _getParent(members);
if (interface == null) return null;
final scopes = _getParent(interface);
if (scopes == null) return null;
final library = _getParent(scopes);
if (library == null) return null;
final libraries = _getParent(library);
if (libraries == null) return null;

final uri = _keyOf(library, libraries);
final name = _keyOf(interface, scopes);

return QualifiedName(uri: uri, name: name);
}

/// Returns the key of [value] in [map].
///
/// Throws if [value] is not in [map].
String _keyOf(Object value, Map<String, Object?> map) {
for (final entry in map.entries) {
if (entry.value == value) return entry.key;
}
throw ArgumentError('Value not in map: $value, $map');
}

/// Gets the `Map` that contains [node], or `null` if there isn't one.
Map<String, Object?>? _getParent(Map<String, Object?> node) {
// If both maps are in the same `JsonBufferBuilder` then the parent is
// immediately available.
if (this case MapInBuffer thisMapInBuffer) {
if (node case MapInBuffer thatMapInBuffer) {
if (thisMapInBuffer.buffer == thatMapInBuffer.buffer) {
return thatMapInBuffer.parent;
}
}
}
// Otherwise, build a `Map` of references to parents and use that.
return _lazyParentsMap[node];
}

/// Gets a `Map` from values to parent `Map`s.
Map<Map<String, Object?>, Map<String, Object?>> get _lazyParentsMap {
var result = _parentsMaps[this];
jakemac53 marked this conversation as resolved.
Show resolved Hide resolved
if (result == null) {
result =
_parentsMaps[this] = <Map<String, Object?>, Map<String, Object?>>{};
_buildParentsMap(node, result);
}
return result;
}

/// Builds a `Map` from values to parent `Map`s.
static void _buildParentsMap(Map<String, Object?> parent,
Map<Map<String, Object?>, Map<String, Object?>> result) {
for (final child in parent.values.whereType<Map<String, Object?>>()) {
if (result.containsKey(child)) {
throw StateError(
'Same node found twice.\n\nChild:\n$child\n\nParent:\n$parent');
} else {
result[child] = parent;
_buildParentsMap(child, result);
}
}
}
}

/// Expando storing a `Map` from values to parent `Map`s.
final _parentsMaps = Expando<Map<Map<String, Object?>, Map<String, Object?>>>();
47 changes: 33 additions & 14 deletions pkgs/dart_model/lib/src/json_buffer/closed_map.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,25 @@ extension ClosedMaps on JsonBufferBuilder {
}

/// Returns the [_ClosedMap] at [pointer].
Map<String, Object?> _readClosedMap(_Pointer pointer) {
return _ClosedMap(this, pointer);
Map<String, Object?> _readClosedMap(
_Pointer pointer, Map<String, Object?>? parent) {
return _ClosedMap(this, pointer, parent);
}
}

class _ClosedMap
with MapMixin<String, Object?>, _EntryMapMixin<String, Object?> {
final JsonBufferBuilder _buffer;
with MapMixin<String, Object?>, _EntryMapMixin<String, Object?>
implements MapInBuffer {
@override
final JsonBufferBuilder buffer;
final _Pointer _pointer;
@override
final Map<String, Object?>? parent;
@override
final int length;

_ClosedMap(this._buffer, this._pointer)
: length = _buffer._readLength(_pointer);
_ClosedMap(this.buffer, this._pointer, this.parent)
: length = buffer._readLength(_pointer);

@override
Object? operator [](Object? key) {
Expand All @@ -70,18 +75,18 @@ class _ClosedMap

@override
late final Iterable<String> keys = _IteratorFunctionIterable(
() => _ClosedMapKeyIterator(_buffer, _pointer, length),
() => _ClosedMapKeyIterator(buffer, this, _pointer, length),
length: length);

@override
late final Iterable<Object?> values = _IteratorFunctionIterable(
() => _ClosedMapValueIterator(_buffer, _pointer, length),
() => _ClosedMapValueIterator(buffer, this, _pointer, length),
length: length);

@override
late final Iterable<MapEntry<String, Object?>> entries =
_IteratorFunctionIterable(
() => _ClosedMapEntryIterator(_buffer, _pointer, length),
() => _ClosedMapEntryIterator(buffer, this, _pointer, length),
length: length);

@override
Expand All @@ -101,16 +106,26 @@ class _ClosedMap
throw UnsupportedError(
'This JsonBufferBuilder map is read-only, see "createGrowableMap".');
}

@override
bool operator ==(Object other) =>
other is _ClosedMap &&
other.buffer == buffer &&
other._pointer == _pointer;

@override
int get hashCode => Object.hash(buffer, _pointer);
}

/// `Iterator` that reads a "closed map" in a [JsonBufferBuilder].
abstract class _ClosedMapIterator<T> implements Iterator<T> {
final JsonBufferBuilder _buffer;
final _ClosedMap _parent;
final _Pointer _last;

_Pointer _pointer;

_ClosedMapIterator(this._buffer, _Pointer pointer, int length)
_ClosedMapIterator(this._buffer, this._parent, _Pointer pointer, int length)
: _last = pointer + _lengthSize + length * ClosedMaps._entrySize,
// Subtract because `moveNext` is called before reading.
_pointer = pointer + _lengthSize - ClosedMaps._entrySize;
Expand All @@ -119,7 +134,8 @@ abstract class _ClosedMapIterator<T> implements Iterator<T> {
T get current;

String get _currentKey => _buffer._readString(_buffer._readPointer(_pointer));
Object? get _currentValue => _buffer._readAny(_pointer + _pointerSize);
Object? get _currentValue =>
_buffer._readAny(_pointer + _pointerSize, parent: _parent);

@override
bool moveNext() {
Expand All @@ -131,22 +147,25 @@ abstract class _ClosedMapIterator<T> implements Iterator<T> {
}

class _ClosedMapKeyIterator extends _ClosedMapIterator<String> {
_ClosedMapKeyIterator(super._buffer, super.pointer, super.length);
_ClosedMapKeyIterator(
super._buffer, super._porent, super.pointer, super.length);

@override
String get current => _currentKey;
}

class _ClosedMapValueIterator extends _ClosedMapIterator<Object?> {
_ClosedMapValueIterator(super._buffer, super.pointer, super.length);
_ClosedMapValueIterator(
super._buffer, super._porent, super.pointer, super.length);

@override
Object? get current => _currentValue;
}

class _ClosedMapEntryIterator
extends _ClosedMapIterator<MapEntry<String, Object?>> {
_ClosedMapEntryIterator(super._buffer, super.pointer, super.length);
_ClosedMapEntryIterator(
super._buffer, super._porent, super.pointer, super.length);

@override
MapEntry<String, Object?> get current => MapEntry(_currentKey, _currentValue);
Expand Down
65 changes: 41 additions & 24 deletions pkgs/dart_model/lib/src/json_buffer/growable_map.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ extension GrowableMaps on JsonBufferBuilder {
// Initially a "growable map" is just a null pointer and zero size, so
// there is nothing to write.
_explanations?.pop();
return _readGrowableMap<V>(pointer);
return _readGrowableMap<V>(pointer, null);
}

/// Returns the [_Pointer] to [map].
Expand All @@ -57,26 +57,32 @@ extension GrowableMaps on JsonBufferBuilder {

/// Throws if [map is backed by a different buffer to `this`.
void _checkGrowableMapOwnership(_GrowableMap map) {
if (map._buffer != this) {
if (map.buffer != this) {
throw UnsupportedError('Maps created with `createGrowableMap` can only '
'be added to the JsonBufferBuilder instance that created them.');
}
}

/// Returns the [_GrowableMap] at [pointer].
Map<String, V> _readGrowableMap<V>(_Pointer pointer) {
return _GrowableMap<V>(this, pointer);
Map<String, V> _readGrowableMap<V>(
_Pointer pointer, Map<String, Object?>? parent) {
return _GrowableMap<V>(this, pointer, parent);
}
}

class _GrowableMap<V> with MapMixin<String, V>, _EntryMapMixin<String, V> {
final JsonBufferBuilder _buffer;
class _GrowableMap<V>
with MapMixin<String, V>, _EntryMapMixin<String, V>
implements MapInBuffer {
@override
final JsonBufferBuilder buffer;
final _Pointer _pointer;
@override
final Map<String, Object?>? parent;
int _length;
_Pointer? _lastPointer;

_GrowableMap(this._buffer, this._pointer)
: _length = _buffer._readLength(_pointer + _pointerSize);
_GrowableMap(this.buffer, this._pointer, this.parent)
: _length = buffer._readLength(_pointer + _pointerSize);

@override
int get length => _length;
Expand All @@ -94,17 +100,17 @@ class _GrowableMap<V> with MapMixin<String, V>, _EntryMapMixin<String, V> {

@override
late final Iterable<String> keys = _IteratorFunctionIterable(
() => _GrowableMapKeyIterator(_buffer, _pointer),
() => _GrowableMapKeyIterator(buffer, this, _pointer),
length: length);

@override
late final Iterable<V> values = _IteratorFunctionIterable(
() => _GrowableMapValueIterator<V>(_buffer, _pointer),
() => _GrowableMapValueIterator<V>(buffer, this, _pointer),
length: length);

@override
late final Iterable<MapEntry<String, V>> entries = _IteratorFunctionIterable(
() => _GrowableMapEntryIterator(_buffer, _pointer),
() => _GrowableMapEntryIterator(buffer, this, _pointer),
length: length);

/// Add [value] to the map with key [key].
Expand All @@ -115,32 +121,32 @@ class _GrowableMap<V> with MapMixin<String, V>, _EntryMapMixin<String, V> {
/// iterable.
@override
void operator []=(String key, V value) {
_buffer._explanations?.push('GrowableMap[]= $key $value');
buffer._explanations?.push('GrowableMap[]= $key $value');

// If `_lastPointer` is not set yet, walk the map to find the end of it.
if (_lastPointer == null) {
final iterator = _GrowableMapEntryIterator<V>(_buffer, _pointer);
final iterator = _GrowableMapEntryIterator<V>(buffer, this, _pointer);
_lastPointer = _pointer;
while (iterator.moveNext()) {
_lastPointer = iterator._pointer;
}
}

// Reserve and write the new node.
final pointer = _buffer._reserve(GrowableMaps._entrySize);
final pointer = buffer._reserve(GrowableMaps._entrySize);
final entryPointer = pointer + _pointerSize;
_buffer._writePointer(entryPointer, _buffer._pointerToString(key));
_buffer._writeAny(entryPointer + _pointerSize, value);
buffer._writePointer(entryPointer, buffer._pointerToString(key));
buffer._writeAny(entryPointer + _pointerSize, value);

// Point to the new node in the previous node.
_buffer._writePointer(_lastPointer!, pointer);
buffer._writePointer(_lastPointer!, pointer);
// Update `_lastPointer` to the new node.
_lastPointer = pointer;

// Update length.
++_length;
_buffer._writeLength(_pointer + _pointerSize, length, allowOverwrite: true);
_buffer._explanations?.pop();
buffer._writeLength(_pointer + _pointerSize, length, allowOverwrite: true);
buffer._explanations?.pop();
}

@override
Expand All @@ -152,22 +158,33 @@ class _GrowableMap<V> with MapMixin<String, V>, _EntryMapMixin<String, V> {
void clear() {
throw UnsupportedError('JsonBufferBuilder growable maps are append only.');
}

@override
bool operator ==(Object other) =>
other is _GrowableMap &&
other.buffer == buffer &&
other._pointer == _pointer;

@override
int get hashCode => Object.hash(buffer, _pointer);
}

/// `Iterator` that reads a "growable map" in a [JsonBufferBuilder].
abstract class _GrowableMapIterator<T> implements Iterator<T> {
final JsonBufferBuilder _buffer;
final _GrowableMap _parent;
_Pointer _pointer;

_GrowableMapIterator(this._buffer, this._pointer);
_GrowableMapIterator(this._buffer, this._parent, this._pointer);

@override
T get current;

String get _currentKey =>
_buffer._readString(_buffer._readPointer(_pointer + _pointerSize));
Object? get _currentValue =>
_buffer._readAny(_pointer + _pointerSize + GrowableMaps._keySize);
_buffer._readAny(_pointer + _pointerSize + GrowableMaps._keySize,
parent: _parent);

@override
bool moveNext() {
Expand All @@ -177,22 +194,22 @@ abstract class _GrowableMapIterator<T> implements Iterator<T> {
}

class _GrowableMapKeyIterator extends _GrowableMapIterator<String> {
_GrowableMapKeyIterator(super._buffer, super._pointer);
_GrowableMapKeyIterator(super._buffer, super._parent, super._pointer);

@override
String get current => _currentKey;
}

class _GrowableMapValueIterator<V> extends _GrowableMapIterator<V> {
_GrowableMapValueIterator(super._buffer, super._pointer);
_GrowableMapValueIterator(super._buffer, super._parent, super._pointer);

@override
V get current => _currentValue as V;
}

class _GrowableMapEntryIterator<V>
extends _GrowableMapIterator<MapEntry<String, V>> {
_GrowableMapEntryIterator(super._buffer, super._pointer);
_GrowableMapEntryIterator(super._buffer, super._parent, super._pointer);

@override
MapEntry<String, V> get current => MapEntry(_currentKey, _currentValue as V);
Expand Down
Loading