blob: 6a8924b1c040f350c4b26a55325ecf769ad4fe61 [file] [log] [blame]
// Copyright (c) 2019, 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 'package:kernel/ast.dart';
import 'package:kernel/class_hierarchy.dart';
import 'package:kernel/type_environment.dart';
import '../../base/common.dart';
import '../builder/builder.dart';
import '../builder/class_builder.dart';
import '../builder/extension_builder.dart';
import '../builder/library_builder.dart';
import '../builder/metadata_builder.dart';
import '../builder/procedure_builder.dart';
import '../builder/type_builder.dart';
import '../builder/type_variable_builder.dart';
import '../fasta_codes.dart'
show
messagePatchDeclarationMismatch,
messagePatchDeclarationOrigin,
noLength,
templateExtensionMemberConflictsWithObjectMember;
import '../kernel/kernel_helper.dart';
import '../operator.dart';
import '../problems.dart';
import '../scope.dart';
import '../util/helpers.dart';
import 'source_field_builder.dart';
import 'source_library_builder.dart';
import 'source_member_builder.dart';
import 'source_procedure_builder.dart';
const String extensionThisName = '#this';
class SourceExtensionBuilder extends ExtensionBuilderImpl {
final Extension _extension;
SourceExtensionBuilder? _origin;
SourceExtensionBuilder? patchForTesting;
@override
final List<TypeVariableBuilder>? typeParameters;
@override
final TypeBuilder onType;
final ExtensionTypeShowHideClauseBuilder extensionTypeShowHideClauseBuilder;
SourceExtensionBuilder(
List<MetadataBuilder>? metadata,
int modifiers,
String name,
this.typeParameters,
this.onType,
this.extensionTypeShowHideClauseBuilder,
Scope scope,
SourceLibraryBuilder parent,
bool isExtensionTypeDeclaration,
int startOffset,
int nameOffset,
int endOffset,
Extension? referenceFrom)
: _extension = new Extension(
name: name,
fileUri: parent.fileUri,
typeParameters:
TypeVariableBuilder.typeParametersFromBuilders(typeParameters),
reference: referenceFrom?.reference)
..isExtensionTypeDeclaration = isExtensionTypeDeclaration
..fileOffset = nameOffset,
super(metadata, modifiers, name, parent, nameOffset, scope);
@override
SourceLibraryBuilder get library => super.library as SourceLibraryBuilder;
@override
SourceExtensionBuilder get origin => _origin ?? this;
@override
Extension get extension => isPatch ? origin._extension : _extension;
/// Builds the [Extension] for this extension build and inserts the members
/// into the [Library] of [libraryBuilder].
///
/// [addMembersToLibrary] is `true` if the extension members should be added
/// to the library. This is `false` if the extension is in conflict with
/// another library member. In this case, the extension member should not be
/// added to the library to avoid name clashes with other members in the
/// library.
Extension build(
SourceLibraryBuilder libraryBuilder, LibraryBuilder coreLibrary,
{required bool addMembersToLibrary}) {
_extension.onType = onType.build(libraryBuilder);
extensionTypeShowHideClauseBuilder.buildAndStoreTypes(
_extension, libraryBuilder);
SourceLibraryBuilder.checkMemberConflicts(library, scope,
checkForInstanceVsStaticConflict: true,
checkForMethodVsSetterConflict: true);
ClassBuilder objectClassBuilder =
coreLibrary.lookupLocalMember('Object', required: true) as ClassBuilder;
void buildBuilders(String name, Builder? declaration) {
while (declaration != null) {
Builder? objectGetter = objectClassBuilder.lookupLocalMember(name);
Builder? objectSetter =
objectClassBuilder.lookupLocalMember(name, setter: true);
if (objectGetter != null || objectSetter != null) {
addProblem(
templateExtensionMemberConflictsWithObjectMember
.withArguments(name),
declaration.charOffset,
name.length);
}
if (declaration.parent != this) {
if (fileUri != declaration.parent!.fileUri) {
unexpected("$fileUri", "${declaration.parent!.fileUri}", charOffset,
fileUri);
} else {
unexpected(fullNameForErrors, declaration.parent!.fullNameForErrors,
charOffset, fileUri);
}
} else if (declaration is SourceMemberBuilder) {
SourceMemberBuilder memberBuilder = declaration;
memberBuilder.buildMembers(libraryBuilder,
(Member member, BuiltMemberKind memberKind) {
if (addMembersToLibrary &&
!memberBuilder.isPatch &&
!memberBuilder.isDuplicate &&
!memberBuilder.isConflictingSetter) {
ExtensionMemberKind kind;
String name = memberBuilder.name;
switch (memberKind) {
case BuiltMemberKind.Constructor:
case BuiltMemberKind.RedirectingFactory:
case BuiltMemberKind.Field:
case BuiltMemberKind.Method:
unhandled(
"${member.runtimeType}:${memberKind}",
"buildMembers",
memberBuilder.charOffset,
memberBuilder.fileUri);
case BuiltMemberKind.ExtensionField:
case BuiltMemberKind.LateIsSetField:
kind = ExtensionMemberKind.Field;
break;
case BuiltMemberKind.ExtensionMethod:
kind = ExtensionMemberKind.Method;
break;
case BuiltMemberKind.ExtensionGetter:
case BuiltMemberKind.LateGetter:
kind = ExtensionMemberKind.Getter;
break;
case BuiltMemberKind.ExtensionSetter:
case BuiltMemberKind.LateSetter:
kind = ExtensionMemberKind.Setter;
break;
case BuiltMemberKind.ExtensionOperator:
kind = ExtensionMemberKind.Operator;
break;
case BuiltMemberKind.ExtensionTearOff:
kind = ExtensionMemberKind.TearOff;
break;
}
// ignore: unnecessary_null_comparison
assert(kind != null);
Reference memberReference;
if (member is Field) {
libraryBuilder.library.addField(member);
memberReference = member.fieldReference;
} else if (member is Procedure) {
libraryBuilder.library.addProcedure(member);
memberReference = member.reference;
} else {
unhandled("${member.runtimeType}", "buildBuilders",
member.fileOffset, member.fileUri);
}
extension.members.add(new ExtensionMemberDescriptor(
name: new Name(name, libraryBuilder.library),
member: memberReference,
isStatic: memberBuilder.isStatic,
kind: kind));
}
});
} else {
unhandled("${declaration.runtimeType}", "buildBuilders",
declaration.charOffset, declaration.fileUri);
}
declaration = declaration.next;
}
}
scope.forEach(buildBuilders);
return _extension;
}
@override
void applyPatch(Builder patch) {
if (patch is SourceExtensionBuilder) {
patch._origin = this;
if (retainDataForTesting) {
patchForTesting = patch;
}
scope.forEachLocalMember((String name, Builder member) {
Builder? memberPatch =
patch.scope.lookupLocalMember(name, setter: false);
if (memberPatch != null) {
member.applyPatch(memberPatch);
}
});
scope.forEachLocalSetter((String name, Builder member) {
Builder? memberPatch =
patch.scope.lookupLocalMember(name, setter: true);
if (memberPatch != null) {
member.applyPatch(memberPatch);
}
});
// TODO(johnniwinther): Check that type parameters and on-type match
// with origin declaration.
} else {
library.addProblem(messagePatchDeclarationMismatch, patch.charOffset,
noLength, patch.fileUri, context: [
messagePatchDeclarationOrigin.withLocation(
fileUri, charOffset, noLength)
]);
}
}
@override
int finishPatch() {
if (!isPatch) return 0;
int count = 0;
scope.forEach((String name, Builder declaration) {
count += declaration.finishPatch();
});
return count;
}
void checkTypesInOutline(TypeEnvironment typeEnvironment) {
library.checkBoundsInTypeParameters(
typeEnvironment, extension.typeParameters, fileUri);
// Check on clause.
// ignore: unnecessary_null_comparison
if (_extension.onType != null) {
library.checkBoundsInType(_extension.onType, typeEnvironment,
onType.fileUri!, onType.charOffset!);
}
forEach((String name, Builder builder) {
if (builder is SourceFieldBuilder) {
// Check fields.
library.checkTypesInField(builder, typeEnvironment);
} else if (builder is SourceProcedureBuilder) {
// Check procedures
library.checkTypesInFunctionBuilder(builder, typeEnvironment);
if (builder.isGetter) {
Builder? setterDeclaration =
scope.lookupLocalMember(builder.name, setter: true);
if (setterDeclaration != null) {
library.checkGetterSetterTypes(builder,
setterDeclaration as ProcedureBuilder, typeEnvironment);
}
}
} else {
assert(false, "Unexpected member: $builder.");
}
});
}
@override
void buildOutlineExpressions(
SourceLibraryBuilder library,
ClassHierarchy classHierarchy,
List<DelayedActionPerformer> delayedActionPerformers,
List<SynthesizedFunctionNode> synthesizedFunctionNodes) {
MetadataBuilder.buildAnnotations(isPatch ? origin.extension : extension,
metadata, library, this, null, fileUri, library.scope);
if (typeParameters != null) {
for (int i = 0; i < typeParameters!.length; i++) {
typeParameters![i].buildOutlineExpressions(library, this, null,
classHierarchy, delayedActionPerformers, scope.parent!);
}
}
void build(String ignore, Builder declaration) {
SourceMemberBuilder member = declaration as SourceMemberBuilder;
member.buildOutlineExpressions(library, classHierarchy,
delayedActionPerformers, synthesizedFunctionNodes);
}
scope.forEach(build);
}
}
class ExtensionTypeShowHideClauseBuilder {
final List<TypeBuilder> shownSupertypes;
final List<String> shownGetters;
final List<String> shownSetters;
final List<String> shownMembersOrTypes;
final List<Operator> shownOperators;
final List<TypeBuilder> hiddenSupertypes;
final List<String> hiddenGetters;
final List<String> hiddenSetters;
final List<String> hiddenMembersOrTypes;
final List<Operator> hiddenOperators;
ExtensionTypeShowHideClauseBuilder(
{required this.shownSupertypes,
required this.shownGetters,
required this.shownSetters,
required this.shownMembersOrTypes,
required this.shownOperators,
required this.hiddenSupertypes,
required this.hiddenGetters,
required this.hiddenSetters,
required this.hiddenMembersOrTypes,
required this.hiddenOperators});
void buildAndStoreTypes(Extension extension, LibraryBuilder libraryBuilder) {
List<Supertype> builtShownSupertypes = shownSupertypes
.map(
(t) => t.buildSupertype(libraryBuilder, t.charOffset!, t.fileUri!)!)
.toList();
List<Supertype> builtHiddenSupertypes = hiddenSupertypes
.map(
(t) => t.buildSupertype(libraryBuilder, t.charOffset!, t.fileUri!)!)
.toList();
ExtensionTypeShowHideClause showHideClause =
extension.showHideClause ?? new ExtensionTypeShowHideClause();
showHideClause.shownSupertypes.addAll(builtShownSupertypes);
showHideClause.hiddenSupertypes.addAll(builtHiddenSupertypes);
extension.showHideClause ??= showHideClause;
}
}