Skip to content

Commit e356352

Browse files
johnniwinthercommit-bot@chromium.org
authored andcommitted
[cfe] Add tear off function for extension instance methods.
Change-Id: Ia92640f5dd99cd3eeb5ba737a646472b5f5260a0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/114844 Commit-Queue: Johnni Winther <[email protected]> Reviewed-by: Dmitry Stefantsov <[email protected]>
1 parent ab9cd54 commit e356352

File tree

48 files changed

+965
-162
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+965
-162
lines changed

pkg/front_end/lib/src/fasta/builder/procedure_builder.dart

Lines changed: 163 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44

55
library fasta.procedure_builder;
66

7-
import 'package:kernel/type_algebra.dart' show containsTypeVariable, substitute;
7+
import 'dart:core' hide MapEntry;
8+
9+
import 'package:kernel/ast.dart';
10+
811
import 'package:kernel/type_algebra.dart';
912

1013
import 'builder.dart'
@@ -19,38 +22,7 @@ import 'builder.dart'
1922
TypeVariableBuilder;
2023

2124
import 'extension_builder.dart';
22-
23-
import 'package:kernel/ast.dart'
24-
show
25-
Arguments,
26-
AsyncMarker,
27-
Block,
28-
Class,
29-
Constructor,
30-
ConstructorInvocation,
31-
DartType,
32-
DynamicType,
33-
EmptyStatement,
34-
Expression,
35-
FunctionNode,
36-
Initializer,
37-
InterfaceType,
38-
Member,
39-
Name,
40-
Procedure,
41-
ProcedureKind,
42-
RedirectingInitializer,
43-
ReturnStatement,
44-
Statement,
45-
StaticInvocation,
46-
StringLiteral,
47-
SuperInitializer,
48-
TreeNode,
49-
TypeParameter,
50-
TypeParameterType,
51-
VariableDeclaration,
52-
VariableGet,
53-
setParents;
25+
import 'type_variable_builder.dart';
5426

5527
import '../../scanner/token.dart' show Token;
5628

@@ -475,6 +447,10 @@ class ProcedureBuilder extends FunctionBuilder {
475447

476448
bool hadTypesInferred = false;
477449

450+
/// If this is an extension instance method then [_extensionTearOff] holds
451+
/// the synthetically created tear off function.
452+
Procedure _extensionTearOff;
453+
478454
ProcedureBuilder(
479455
List<MetadataBuilder> metadata,
480456
int modifiers,
@@ -600,7 +576,7 @@ class ProcedureBuilder extends FunctionBuilder {
600576
_procedure.isExternal = isExternal;
601577
_procedure.isConst = isConst;
602578
if (isExtensionMethod) {
603-
ExtensionBuilder extension = parent;
579+
ExtensionBuilder extensionBuilder = parent;
604580
procedure.isExtensionMember = true;
605581
procedure.isStatic = true;
606582
String kindInfix = '';
@@ -625,18 +601,169 @@ class ProcedureBuilder extends FunctionBuilder {
625601
procedure.kind = ProcedureKind.Method;
626602
}
627603
procedure.name = new Name(
628-
'${extension.name}|${kindInfix}${name}', libraryBuilder.library);
604+
'${extensionBuilder.name}|${kindInfix}${name}',
605+
libraryBuilder.library);
629606
} else {
630607
_procedure.isStatic = isStatic;
631608
_procedure.name = new Name(name, libraryBuilder.library);
632609
}
633610
}
611+
if (extensionTearOff != null) {
612+
_buildExtensionTearOff(libraryBuilder, parent);
613+
}
634614
return _procedure;
635615
}
636616

617+
/// Creates a top level function that creates a tear off of an extension
618+
/// instance method.
619+
///
620+
/// For this declaration
621+
///
622+
/// extension E<T> on A<T> {
623+
/// X method<S>(S s, Y y) {}
624+
/// }
625+
///
626+
/// we create the top level function
627+
///
628+
/// X E|method<T, S>(A<T> #this, S s, Y y) {}
629+
///
630+
/// and the tear off function
631+
///
632+
/// X Function<S>(S, Y) E|get#method<T>(A<T> #this) {
633+
/// return (S s, Y y) => E|method<T, S>(#this, s, y);
634+
/// }
635+
///
636+
void _buildExtensionTearOff(
637+
SourceLibraryBuilder libraryBuilder, ExtensionBuilder extensionBuilder) {
638+
assert(
639+
_extensionTearOff != null, "No extension tear off created for $this.");
640+
if (_extensionTearOff.name != null) return;
641+
642+
int fileOffset = _procedure.fileOffset;
643+
644+
int extensionTypeParameterCount =
645+
extensionBuilder.typeParameters?.length ?? 0;
646+
647+
List<TypeParameter> typeParameters = <TypeParameter>[];
648+
649+
List<DartType> typeArguments = <DartType>[];
650+
for (TypeParameter typeParameter in function.typeParameters) {
651+
TypeParameter newTypeParameter = new TypeParameter(typeParameter.name);
652+
typeParameters.add(newTypeParameter);
653+
typeArguments.add(new TypeParameterType(newTypeParameter));
654+
}
655+
656+
List<TypeParameter> tearOffTypeParameters = <TypeParameter>[];
657+
List<TypeParameter> closureTypeParameters = <TypeParameter>[];
658+
Substitution substitution =
659+
Substitution.fromPairs(function.typeParameters, typeArguments);
660+
for (int index = 0; index < typeParameters.length; index++) {
661+
TypeParameter newTypeParameter = typeParameters[index];
662+
newTypeParameter.bound =
663+
substitution.substituteType(function.typeParameters[index].bound);
664+
newTypeParameter.defaultType = function.typeParameters[index].defaultType;
665+
if (index < extensionTypeParameterCount) {
666+
tearOffTypeParameters.add(newTypeParameter);
667+
} else {
668+
closureTypeParameters.add(newTypeParameter);
669+
}
670+
}
671+
672+
VariableDeclaration copyParameter(
673+
VariableDeclaration parameter, DartType type,
674+
{bool isOptional}) {
675+
// TODO(johnniwinther): Handle default values.
676+
return new VariableDeclaration(parameter.name,
677+
type: type,
678+
initializer: isOptional ? new NullLiteral() : null,
679+
isFinal: parameter.isFinal)
680+
..fileOffset = parameter.fileOffset;
681+
}
682+
683+
VariableDeclaration extensionThis = copyParameter(
684+
function.positionalParameters.first,
685+
substitution.substituteType(function.positionalParameters.first.type),
686+
isOptional: false);
687+
688+
DartType closureReturnType =
689+
substitution.substituteType(function.returnType);
690+
List<VariableDeclaration> closurePositionalParameters = [];
691+
List<Expression> closurePositionalArguments = [];
692+
693+
for (int position = 0;
694+
position < function.positionalParameters.length;
695+
position++) {
696+
VariableDeclaration parameter = function.positionalParameters[position];
697+
if (position == 0) {
698+
/// Pass `this` as a captured variable.
699+
closurePositionalArguments
700+
.add(new VariableGet(extensionThis)..fileOffset = fileOffset);
701+
} else {
702+
DartType type = substitution.substituteType(parameter.type);
703+
VariableDeclaration newParameter = copyParameter(parameter, type,
704+
isOptional: position >= function.requiredParameterCount);
705+
closurePositionalParameters.add(newParameter);
706+
closurePositionalArguments
707+
.add(new VariableGet(newParameter)..fileOffset = fileOffset);
708+
}
709+
}
710+
List<VariableDeclaration> closureNamedParameters = [];
711+
List<NamedExpression> closureNamedArguments = [];
712+
for (VariableDeclaration parameter in function.namedParameters) {
713+
DartType type = substitution.substituteType(parameter.type);
714+
VariableDeclaration newParameter =
715+
copyParameter(parameter, type, isOptional: true);
716+
closureNamedParameters.add(newParameter);
717+
closureNamedArguments.add(new NamedExpression(parameter.name,
718+
new VariableGet(newParameter)..fileOffset = fileOffset));
719+
}
720+
721+
Statement closureBody = new ReturnStatement(
722+
new StaticInvocation(
723+
procedure,
724+
new Arguments(closurePositionalArguments,
725+
types: typeArguments, named: closureNamedArguments))
726+
..fileOffset = fileOffset)
727+
..fileOffset = fileOffset;
728+
729+
FunctionExpression closure = new FunctionExpression(new FunctionNode(
730+
closureBody,
731+
typeParameters: closureTypeParameters,
732+
positionalParameters: closurePositionalParameters,
733+
namedParameters: closureNamedParameters,
734+
requiredParameterCount: procedure.function.requiredParameterCount - 1,
735+
returnType: closureReturnType,
736+
asyncMarker: procedure.function.asyncMarker,
737+
dartAsyncMarker: procedure.function.dartAsyncMarker))
738+
..fileOffset = fileOffset;
739+
740+
_extensionTearOff
741+
..name = new Name(
742+
'${extensionBuilder.name}|get#${name}', libraryBuilder.library)
743+
..function = new FunctionNode(
744+
new ReturnStatement(closure)..fileOffset = fileOffset,
745+
typeParameters: tearOffTypeParameters,
746+
positionalParameters: [extensionThis],
747+
requiredParameterCount: 1,
748+
returnType: closure.function.functionType)
749+
..fileUri = fileUri
750+
..fileOffset = fileOffset;
751+
_extensionTearOff.function.parent = _extensionTearOff;
752+
}
753+
637754
/// The [Procedure] built by this builder.
638755
Procedure get procedure => isPatch ? origin.procedure : _procedure;
639756

757+
/// If this is an extension instance method then [_extensionTearOff] holds
758+
/// the synthetically created tear off function.
759+
Procedure get extensionTearOff {
760+
if (isExtensionInstanceMember && kind == ProcedureKind.Method) {
761+
_extensionTearOff ??= new Procedure(null, ProcedureKind.Method, null,
762+
isStatic: true, isExtensionMember: true);
763+
}
764+
return _extensionTearOff;
765+
}
766+
640767
Member get member => procedure;
641768

642769
@override
@@ -745,7 +872,7 @@ class ConstructorBuilder extends FunctionBuilder {
745872
return false;
746873
}
747874

748-
Constructor build(SourceLibraryBuilder libraryBuilder) {
875+
Member build(SourceLibraryBuilder libraryBuilder) {
749876
if (_constructor.name == null) {
750877
_constructor.function = buildFunction(libraryBuilder);
751878
_constructor.function.parent = _constructor;

pkg/front_end/lib/src/fasta/kernel/expression_generator.dart

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import '../constant_context.dart' show ConstantContext;
3131

3232
import '../builder/builder.dart' show PrefixBuilder, TypeDeclarationBuilder;
3333
import '../builder/declaration_builder.dart';
34+
import '../builder/procedure_builder.dart';
3435

3536
import '../fasta_codes.dart'
3637
show
@@ -1596,21 +1597,19 @@ class StaticAccessGenerator extends Generator {
15961597
///
15971598
class ExtensionInstanceAccessGenerator extends Generator {
15981599
/// The static [Member] generated for an instance extension member which is
1599-
/// used for performing a read or invocation on this subexpression.
1600+
/// used for performing a read on this subexpression.
16001601
///
16011602
/// This can be `null` if the subexpression doesn't have a readable target.
16021603
/// For instance if the subexpression is a setter without a corresponding
16031604
/// getter.
16041605
final Procedure readTarget;
16051606

1606-
/// `true` if the [readTarget] is declared as a regular method in the
1607-
/// extension.
1607+
/// The static [Member] generated for an instance extension member which is
1608+
/// used for performing an invocation on this subexpression.
16081609
///
1609-
/// All extension instance members are converted into to top level methods so
1610-
/// this field is needed to know whether a read should be a tear off, which
1611-
/// is the case for regular methods, or an invocation should be a read follow
1612-
/// by a call, which is the case for getters.
1613-
final bool readTargetIsRegularMethod;
1610+
/// This can be `null` if the subexpression doesn't have an invokable target.
1611+
/// For instance if the subexpression is a getter or setter.
1612+
final Procedure invokeTarget;
16141613

16151614
/// The static [Member] generated for an instance extension member which is
16161615
/// used for performing a write on this subexpression.
@@ -1634,7 +1633,7 @@ class ExtensionInstanceAccessGenerator extends Generator {
16341633
ExpressionGeneratorHelper helper,
16351634
Token token,
16361635
this.readTarget,
1637-
this.readTargetIsRegularMethod,
1636+
this.invokeTarget,
16381637
this.writeTarget,
16391638
this.extensionThis,
16401639
this.extensionTypeParameters)
@@ -1662,23 +1661,21 @@ class ExtensionInstanceAccessGenerator extends Generator {
16621661
offsetForToken(token),
16631662
helper.uri);
16641663
}
1665-
bool readTargetIsRegularMethod = declaration.isRegularMethod;
1666-
Procedure getter;
1667-
if (declaration.isGetter || declaration.isRegularMethod) {
1668-
getter = declaration.target;
1664+
Procedure readTarget;
1665+
Procedure invokeTarget;
1666+
if (declaration.isGetter) {
1667+
readTarget = declaration.target;
1668+
} else if (declaration.isRegularMethod) {
1669+
ProcedureBuilder procedureBuilder = declaration;
1670+
readTarget = procedureBuilder.extensionTearOff;
1671+
invokeTarget = procedureBuilder.procedure;
16691672
}
1670-
Procedure setter;
1673+
Procedure writeTarget;
16711674
if (builderSetter != null && builderSetter.isSetter) {
1672-
setter = builderSetter.target;
1675+
writeTarget = builderSetter.target;
16731676
}
1674-
return new ExtensionInstanceAccessGenerator(
1675-
helper,
1676-
token,
1677-
getter,
1678-
readTargetIsRegularMethod,
1679-
setter,
1680-
extensionThis,
1681-
extensionTypeParameters);
1677+
return new ExtensionInstanceAccessGenerator(helper, token, readTarget,
1678+
invokeTarget, writeTarget, extensionThis, extensionTypeParameters);
16821679
}
16831680

16841681
@override
@@ -1695,9 +1692,6 @@ class ExtensionInstanceAccessGenerator extends Generator {
16951692
if (complexAssignment != null) {
16961693
read = _helper.desugarSyntheticExpression(read);
16971694
}
1698-
} else if (readTargetIsRegularMethod) {
1699-
read = _helper.createExtensionTearOff(
1700-
fileOffset, readTarget, extensionThis, extensionTypeParameters);
17011695
} else {
17021696
List<DartType> typeArguments;
17031697
if (extensionTypeParameters != null) {
@@ -1748,7 +1742,7 @@ class ExtensionInstanceAccessGenerator extends Generator {
17481742

17491743
@override
17501744
Expression doInvocation(int offset, Arguments arguments) {
1751-
if (readTargetIsRegularMethod) {
1745+
if (invokeTarget != null) {
17521746
List<Expression> positionalArguments = [
17531747
_helper.createVariableGet(extensionThis, offset)
17541748
]..addAll(arguments.positional);
@@ -1765,7 +1759,7 @@ class ExtensionInstanceAccessGenerator extends Generator {
17651759
typeArguments = arguments.types;
17661760
}
17671761
return _helper.buildStaticInvocation(
1768-
readTarget,
1762+
invokeTarget,
17691763
_forest.createArguments(fileOffset, positionalArguments,
17701764
named: arguments.named, types: typeArguments),
17711765
charOffset: offset);

pkg/front_end/lib/src/fasta/source/source_class_builder.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,10 @@ class SourceClassBuilder extends ClassBuilder
129129
cls.addMember(field);
130130
}
131131
} else if (declaration is FunctionBuilder) {
132-
Member function = declaration.build(library);
133-
function.parent = cls;
132+
Member member = declaration.build(library);
133+
member.parent = cls;
134134
if (!declaration.isPatch && declaration.next == null) {
135-
cls.addMember(function);
135+
cls.addMember(member);
136136
}
137137
} else {
138138
unhandled("${declaration.runtimeType}", "buildBuilders",

0 commit comments

Comments
 (0)