blob: e9e15a5ee5419fe3f2eafe3040ce9e0ffedb6633 [file] [log] [blame]
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import '../builder/builder.dart';
import '../builder/declaration_builders.dart';
import 'scope.dart';
abstract class LocalScope implements LookupScope {
@override
ScopeKind get kind;
LocalScope createNestedScope(
{required String debugName, required ScopeKind kind});
LocalScope createNestedFixedScope(
{required String debugName,
required Map<String, Builder> local,
required ScopeKind kind});
Iterable<Builder> get localVariables;
Builder? lookupLocalVariable(String name);
/// Declares that the meaning of [name] in this scope is [builder].
///
/// If name was used previously in this scope, this method returns the read
/// offsets which can be used for reporting a compile-time error about
/// [name] being used before its declared.
List<int>? declare(String name, Builder builder);
void addLocalVariable(String name, Builder builder);
@override
Builder? lookupGetable(String name, int charOffset, Uri fileUri);
@override
Builder? lookupSetable(String name, int charOffset, Uri uri);
Map<String, List<int>>? get usedNames;
}
abstract base class BaseLocalScope implements LocalScope {
@override
LocalScope createNestedScope(
{required String debugName, required ScopeKind kind}) {
return new LocalScopeImpl(this, kind, debugName);
}
@override
LocalScope createNestedFixedScope(
{required String debugName,
required Map<String, Builder> local,
required ScopeKind kind}) {
return new FixedLocalScope(
kind: kind, parent: this, local: local, debugName: debugName);
}
}
mixin LocalScopeMixin implements LookupScopeMixin, LocalScope {
LookupScope? get _parent;
Map<String, Builder>? get _local;
@override
String get classNameOrDebugName;
@override
Iterable<Builder> get localVariables => _local?.values ?? const {};
@override
Builder? lookupGetable(String name, int charOffset, Uri fileUri) {
_recordUse(name, charOffset);
Builder? builder;
if (_local != null) {
builder = lookupGetableIn(name, charOffset, fileUri, _local!);
if (builder != null) return builder;
}
return builder ?? _parent?.lookupGetable(name, charOffset, fileUri);
}
@override
Builder? lookupLocalVariable(String name) {
return _local?[name];
}
@override
Builder? lookupSetable(String name, int charOffset, Uri fileUri) {
_recordUse(name, charOffset);
Builder? builder = lookupSetableIn(name, charOffset, fileUri, _local);
return builder ?? _parent?.lookupSetable(name, charOffset, fileUri);
}
void _recordUse(String name, int charOffset) {}
@override
// Coverage-ignore(suite): Not run.
void forEachExtension(void Function(ExtensionBuilder) f) {
_parent?.forEachExtension(f);
}
}
final class LocalScopeImpl extends BaseLocalScope
with LookupScopeMixin, LocalScopeMixin
implements LocalScope {
@override
final LocalScope? _parent;
@override
final String classNameOrDebugName;
/// Names declared in this scope.
@override
Map<String, Builder>? _local;
@override
Map<String, List<int>>? usedNames;
@override
final ScopeKind kind;
LocalScopeImpl(this._parent, this.kind, this.classNameOrDebugName);
@override
void addLocalVariable(String name, Builder builder) {
(_local ??= {})[name] = builder;
}
@override
List<int>? declare(String name, Builder builder) {
List<int>? previousOffsets = usedNames?[name];
if (previousOffsets != null && previousOffsets.isNotEmpty) {
return previousOffsets;
}
(_local ??= {})[name] = builder;
return null;
}
@override
void _recordUse(String name, int charOffset) {
usedNames ??= <String, List<int>>{};
// Don't use putIfAbsent to avoid the context allocation needed
// for the closure.
(usedNames![name] ??= []).add(charOffset);
}
@override
String toString() =>
"$runtimeType(${kind}, $classNameOrDebugName, ${_local?.keys})";
}
mixin ImmutableLocalScopeMixin implements LocalScope {
@override
void addLocalVariable(String name, Builder builder) {
throw new UnsupportedError('$runtimeType($kind).addLocalMember');
}
@override
List<int>? declare(String name, Builder builder) {
throw new UnsupportedError('$runtimeType($kind).declare');
}
@override
// Coverage-ignore(suite): Not run.
Map<String, List<int>>? get usedNames => null;
}
final class FixedLocalScope extends BaseLocalScope
with LookupScopeMixin, ImmutableLocalScopeMixin, LocalScopeMixin {
@override
final LocalScope? _parent;
@override
final ScopeKind kind;
@override
final Map<String, Builder>? _local;
final String _debugName;
FixedLocalScope(
{required this.kind,
LocalScope? parent,
Map<String, Builder>? local,
required String debugName})
: _parent = parent,
_local = local,
_debugName = debugName;
@override
String get classNameOrDebugName => _debugName;
@override
String toString() =>
"$runtimeType(${kind}, $classNameOrDebugName, ${_local?.keys})";
}
final class FormalParameterScope extends BaseLocalScope
with LookupScopeMixin, ImmutableLocalScopeMixin, LocalScopeMixin {
@override
final LookupScope? _parent;
@override
final Map<String, Builder>? _local;
FormalParameterScope({LookupScope? parent, Map<String, Builder>? local})
: _parent = parent,
_local = local;
@override
ScopeKind get kind => ScopeKind.formals;
@override
String get classNameOrDebugName => "formal parameter";
@override
String toString() =>
"$runtimeType(${kind}, $classNameOrDebugName, ${_local?.keys})";
}
final class EnclosingLocalScope extends BaseLocalScope
with ImmutableLocalScopeMixin {
final LookupScope _scope;
EnclosingLocalScope(this._scope);
@override
ScopeKind get kind => _scope.kind;
@override
// Coverage-ignore(suite): Not run.
Iterable<Builder> get localVariables => const [];
@override
Builder? lookupGetable(String name, int charOffset, Uri fileUri) {
return _scope.lookupGetable(name, charOffset, fileUri);
}
@override
// Coverage-ignore(suite): Not run.
Builder? lookupLocalVariable(String name) => null;
@override
Builder? lookupSetable(String name, int charOffset, Uri uri) {
return _scope.lookupSetable(name, charOffset, uri);
}
@override
// Coverage-ignore(suite): Not run.
void forEachExtension(void Function(ExtensionBuilder) f) {
_scope.forEachExtension(f);
}
@override
String toString() => "$runtimeType(${kind},$_scope)";
}