blob: 3c0632b137415807d1524a9d3cd76f12e72263ad [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/declaration_builders.dart';
import '../builder/variable_builder.dart';
import 'lookup_result.dart';
import 'scope.dart';
abstract class LocalScope implements LookupScope {
@override
ScopeKind get kind;
@override
LookupResult? lookup(String name, {int fileOffset = -1});
LocalScope createNestedScope(
{required String debugName, required ScopeKind kind});
LocalScope createNestedFixedScope(
{required String debugName,
required Map<String, VariableBuilder> local,
required ScopeKind kind});
Iterable<VariableBuilder> get localVariables;
VariableBuilder? 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, VariableBuilder builder);
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, VariableBuilder> local,
required ScopeKind kind}) {
return new FixedLocalScope(
kind: kind, parent: this, local: local, debugName: debugName);
}
}
mixin LocalScopeMixin implements LocalScope {
LocalScope? get _parent;
Map<String, VariableBuilder>? get _local;
@override
Iterable<VariableBuilder> get localVariables => _local?.values ?? const [];
@override
LookupResult? lookup(String name, {int fileOffset = -1}) {
_recordUse(name, fileOffset);
return _local?[name] ?? _parent?.lookup(name, fileOffset: fileOffset);
}
@override
VariableBuilder? lookupLocalVariable(String name) {
return _local?[name];
}
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 LocalScopeMixin
implements LocalScope {
@override
final LocalScope? _parent;
final String _debugName;
/// Names declared in this scope.
@override
Map<String, VariableBuilder>? _local;
@override
Map<String, List<int>>? usedNames;
@override
final ScopeKind kind;
LocalScopeImpl(this._parent, this.kind, this._debugName);
@override
List<int>? declare(String name, VariableBuilder 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}, $_debugName, ${_local?.keys})";
}
mixin ImmutableLocalScopeMixin implements LocalScope {
@override
List<int>? declare(String name, VariableBuilder builder) {
throw new UnsupportedError('$runtimeType($kind).declare');
}
@override
// Coverage-ignore(suite): Not run.
Map<String, List<int>>? get usedNames => null;
}
final class LocalTypeParameterScope extends BaseLocalScope
with ImmutableLocalScopeMixin {
final LocalScope? _parent;
@override
final ScopeKind kind;
final Map<String, TypeParameterBuilder>? _local;
final String _debugName;
LocalTypeParameterScope(
{required this.kind,
LocalScope? parent,
Map<String, TypeParameterBuilder>? local,
required String debugName})
: _parent = parent,
_local = local,
_debugName = debugName;
@override
// Coverage-ignore(suite): Not run.
Iterable<VariableBuilder> get localVariables => const [];
@override
LookupResult? lookup(String name, {int fileOffset = -1}) {
return _local?[name] ?? _parent?.lookup(name, fileOffset: fileOffset);
}
@override
// Coverage-ignore(suite): Not run.
VariableBuilder? lookupLocalVariable(String name) => null;
@override
// Coverage-ignore(suite): Not run.
void forEachExtension(void Function(ExtensionBuilder) f) {
_parent?.forEachExtension(f);
}
@override
String toString() => "$runtimeType(${kind}, $_debugName, ${_local?.keys})";
}
final class FixedLocalScope extends BaseLocalScope
with ImmutableLocalScopeMixin, LocalScopeMixin {
@override
final LocalScope? _parent;
@override
final ScopeKind kind;
@override
final Map<String, VariableBuilder>? _local;
final String _debugName;
FixedLocalScope(
{required this.kind,
LocalScope? parent,
Map<String, VariableBuilder>? local,
required String debugName})
: _parent = parent,
_local = local,
_debugName = debugName;
@override
String toString() => "$runtimeType(${kind}, $_debugName, ${_local?.keys})";
}
final class FormalParameterScope extends BaseLocalScope
with ImmutableLocalScopeMixin, LocalScopeMixin {
@override
final LocalScope? _parent;
@override
final Map<String, VariableBuilder>? _local;
FormalParameterScope(
{required LookupScope parent, Map<String, VariableBuilder>? local})
: _parent = new EnclosingLocalScope(parent),
_local = local;
@override
ScopeKind get kind => ScopeKind.formals;
@override
String toString() =>
"$runtimeType(${kind}, formal parameter, ${_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<VariableBuilder> get localVariables => const [];
@override
LookupResult? lookup(String name, {int fileOffset = -1}) {
return _scope.lookup(name);
}
@override
// Coverage-ignore(suite): Not run.
VariableBuilder? lookupLocalVariable(String name) => null;
@override
// Coverage-ignore(suite): Not run.
void forEachExtension(void Function(ExtensionBuilder) f) {
_scope.forEachExtension(f);
}
@override
String toString() => "$runtimeType(${kind},$_scope)";
}