Use Extension Member data instead of desugar api.

Swap to finding the mapping of Procedure to ExtensionMemberDescriptor
data, instead of using the front_end desugar api for extension
member kind, name, and isStatic information.

Change-Id: I4bca55e8f1d3044def420060ddecc75600452a11
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/214305
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Reviewed-by: Srujan Gaddam <srujzs@google.com>
This commit is contained in:
Riley Porter 2021-09-24 01:41:48 +00:00 committed by commit-bot@chromium.org
parent bd067153d5
commit dc4d16fc1b
4 changed files with 41 additions and 154 deletions

View file

@ -2,7 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a // 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. // BSD-style license that can be found in the LICENSE file.
import 'package:front_end/src/api_unstable/dart2js.dart' as api;
import 'package:kernel/ast.dart'; import 'package:kernel/ast.dart';
import 'package:kernel/class_hierarchy.dart'; import 'package:kernel/class_hierarchy.dart';
import 'package:kernel/core_types.dart'; import 'package:kernel/core_types.dart';
@ -37,6 +36,7 @@ class JsUtilOptimizer extends Transformer {
final CoreTypes _coreTypes; final CoreTypes _coreTypes;
final StatefulStaticTypeContext _staticTypeContext; final StatefulStaticTypeContext _staticTypeContext;
Map<Reference, ExtensionMemberDescriptor>? _extensionMemberIndex;
JsUtilOptimizer(this._coreTypes, ClassHierarchy hierarchy) JsUtilOptimizer(this._coreTypes, ClassHierarchy hierarchy)
: _jsTarget = : _jsTarget =
@ -74,6 +74,7 @@ class JsUtilOptimizer extends Transformer {
_staticTypeContext.enterLibrary(lib); _staticTypeContext.enterLibrary(lib);
lib.transformChildren(this); lib.transformChildren(this);
_staticTypeContext.leaveLibrary(lib); _staticTypeContext.leaveLibrary(lib);
_extensionMemberIndex = null;
return lib; return lib;
} }
@ -88,21 +89,24 @@ class JsUtilOptimizer extends Transformer {
@override @override
visitProcedure(Procedure node) { visitProcedure(Procedure node) {
_staticTypeContext.enterMember(node); _staticTypeContext.enterMember(node);
if (node.isExternal && node.isExtensionMember && !_isDeclaredStatic(node)) { var transformedBody;
var transformedBody; if (node.isExternal && node.isExtensionMember) {
if (api.getExtensionMemberKind(node) == ProcedureKind.Getter) { var index = _extensionMemberIndex ??=
transformedBody = _getExternalGetterBody(node); _createExtensionMembersIndex(node.enclosingLibrary);
} else if (api.getExtensionMemberKind(node) == ProcedureKind.Setter) { var nodeDescriptor = index[node.reference]!;
transformedBody = _getExternalSetterBody(node); if (!nodeDescriptor.isStatic) {
} else if (api.getExtensionMemberKind(node) == ProcedureKind.Method) { if (nodeDescriptor.kind == ExtensionMemberKind.Getter) {
transformedBody = _getExternalMethodBody(node); transformedBody = _getExternalGetterBody(node);
} } else if (nodeDescriptor.kind == ExtensionMemberKind.Setter) {
// TODO(rileyporter): Add transformation for static members and any transformedBody = _getExternalSetterBody(node);
// operators we decide to support. } else if (nodeDescriptor.kind == ExtensionMemberKind.Method) {
if (transformedBody != null) { transformedBody = _getExternalMethodBody(node);
node.function.body = transformedBody; }
node.isExternal = false;
} }
}
if (transformedBody != null) {
node.function.body = transformedBody;
node.isExternal = false;
} else { } else {
node.transformChildren(this); node.transformChildren(this);
} }
@ -110,6 +114,18 @@ class JsUtilOptimizer extends Transformer {
return node; return node;
} }
/// Returns and initializes `_extensionMemberIndex` to an index of the member
/// reference to the member `ExtensionMemberDescriptor`, for all extension
/// members in the given [library].
Map<Reference, ExtensionMemberDescriptor> _createExtensionMembersIndex(
Library library) {
_extensionMemberIndex = {};
library.extensions.forEach((extension) => extension.members.forEach(
(descriptor) =>
_extensionMemberIndex![descriptor.member] = descriptor));
return _extensionMemberIndex!;
}
/// Returns a new function body for the given [node] external getter. /// Returns a new function body for the given [node] external getter.
/// ///
/// The new function body will call the optimized version of /// The new function body will call the optimized version of
@ -121,7 +137,7 @@ class JsUtilOptimizer extends Transformer {
_getPropertyTarget, _getPropertyTarget,
Arguments([ Arguments([
VariableGet(function.positionalParameters.first), VariableGet(function.positionalParameters.first),
StringLiteral(_getMemberName(node)) StringLiteral(_getExtensionMemberName(node))
])) ]))
..fileOffset = node.fileOffset; ..fileOffset = node.fileOffset;
return ReturnStatement(AsExpression( return ReturnStatement(AsExpression(
@ -139,7 +155,7 @@ class JsUtilOptimizer extends Transformer {
_setPropertyTarget, _setPropertyTarget,
Arguments([ Arguments([
VariableGet(function.positionalParameters.first), VariableGet(function.positionalParameters.first),
StringLiteral(_getMemberName(node)), StringLiteral(_getExtensionMemberName(node)),
VariableGet(function.positionalParameters.last) VariableGet(function.positionalParameters.last)
])) ]))
..fileOffset = node.fileOffset; ..fileOffset = node.fileOffset;
@ -157,7 +173,7 @@ class JsUtilOptimizer extends Transformer {
_callMethodTarget, _callMethodTarget,
Arguments([ Arguments([
VariableGet(function.positionalParameters.first), VariableGet(function.positionalParameters.first),
StringLiteral(_getMemberName(node)), StringLiteral(_getExtensionMemberName(node)),
ListLiteral(function.positionalParameters ListLiteral(function.positionalParameters
.sublist(1) .sublist(1)
.map((argument) => VariableGet(argument)) .map((argument) => VariableGet(argument))
@ -168,29 +184,17 @@ class JsUtilOptimizer extends Transformer {
_lowerCallMethod(callMethodInvocation), function.returnType)); _lowerCallMethod(callMethodInvocation), function.returnType));
} }
/// Returns the member name, either from the `@JS` annotation if non-empty, /// Returns the extension member name.
/// or parsed from CFE generated node name. ///
String _getMemberName(Procedure node) { /// Returns either the name from the `@JS` annotation if non-empty, or the
/// declared name of the extension member. Does not return the CFE generated
/// name for the top level member for this extension member.
String _getExtensionMemberName(Procedure node) {
var jsAnnotationName = getJSName(node); var jsAnnotationName = getJSName(node);
if (jsAnnotationName.isNotEmpty) { if (jsAnnotationName.isNotEmpty) {
return jsAnnotationName; return jsAnnotationName;
} }
// TODO(rileyporter): Switch to using the ExtensionMemberDescriptor data. return _extensionMemberIndex![node.reference]!.name.text;
var nodeName = node.name.text;
if (nodeName.contains('#')) {
return nodeName.substring(nodeName.indexOf('#') + 1);
} else {
return nodeName.substring(nodeName.indexOf('|') + 1);
}
}
/// Returns whether the given extension [node] is declared as `static`.
///
/// All extension members have `isStatic` true, but the members declared as
/// static will not have a synthesized `this` variable.
bool _isDeclaredStatic(Procedure node) {
return node.function.positionalParameters.isEmpty ||
node.function.positionalParameters.first.name != '#this';
} }
/// Replaces js_util method calls with optimization when possible. /// Replaces js_util method calls with optimization when possible.

View file

@ -8,7 +8,5 @@ environment:
dependencies: dependencies:
_fe_analyzer_shared: _fe_analyzer_shared:
path: ../_fe_analyzer_shared path: ../_fe_analyzer_shared
front_end:
path: ../front_end
kernel: kernel:
path: ../kernel path: ../kernel

View file

@ -241,23 +241,3 @@ Iterable<String> getSupportedLibraryNames(
// TODO(sigmund): Delete this API once `member.isRedirectingFactory` // TODO(sigmund): Delete this API once `member.isRedirectingFactory`
// is implemented correctly for patch files (Issue #33495). // is implemented correctly for patch files (Issue #33495).
bool isRedirectingFactory(ir.Procedure member) => member.isRedirectingFactory; bool isRedirectingFactory(ir.Procedure member) => member.isRedirectingFactory;
/// Determines what `ProcedureKind` the given extension [member] is based on
/// the member name.
///
/// Note: classifies operators as `ProcedureKind.Method`.
ir.ProcedureKind getExtensionMemberKind(ir.Procedure member) {
assert(member.isExtensionMember);
String name = member.name.text;
int pipeIndex = name.indexOf('|');
int poundIndex = name.indexOf('#');
assert(pipeIndex >= 0);
if (poundIndex >= 0) {
String getOrSet = name.substring(pipeIndex + 1, poundIndex);
return getOrSet == 'get'
? ir.ProcedureKind.Getter
: ir.ProcedureKind.Setter;
} else {
return member.kind;
}
}

View file

@ -28,7 +28,6 @@ Future<void> main() async {
await testRedirectingFactoryDirect(); await testRedirectingFactoryDirect();
await testRedirectingFactorySerialized(); await testRedirectingFactorySerialized();
await testRedirectingFactoryPatchFile(); await testRedirectingFactoryPatchFile();
await testExtensionMemberKind();
}); });
} }
@ -80,97 +79,3 @@ class _B implements A {
_B(int x); _B(int x);
} }
'''; ''';
Future<void> testExtensionMemberKind() async {
var component = await compileUnit(['e.dart'], {'e.dart': extensionSource});
var library = component.libraries
.firstWhere((l) => l.importUri.path.endsWith('e.dart'));
var descriptors =
library.extensions.expand((extension) => extension.members).toList();
// Check generated getters and setters for fields.
var fieldGetter =
findExtensionField(descriptors, 'field', ir.ExtensionMemberKind.Getter);
Expect.equals(
api.getExtensionMemberKind(fieldGetter), ir.ProcedureKind.Getter);
var fieldSetter =
findExtensionField(descriptors, 'field', ir.ExtensionMemberKind.Setter);
Expect.equals(
api.getExtensionMemberKind(fieldSetter), ir.ProcedureKind.Setter);
var staticFieldGetter = findExtensionField(
descriptors, 'staticField', ir.ExtensionMemberKind.Getter);
Expect.equals(
api.getExtensionMemberKind(staticFieldGetter), ir.ProcedureKind.Getter);
var staticFieldSetter = findExtensionField(
descriptors, 'staticField', ir.ExtensionMemberKind.Setter);
Expect.equals(
api.getExtensionMemberKind(staticFieldSetter), ir.ProcedureKind.Setter);
// Check getters and setters.
var getter = findExtensionMember(descriptors, 'getter');
Expect.equals(api.getExtensionMemberKind(getter), ir.ProcedureKind.Getter);
var setter = findExtensionMember(descriptors, 'setter');
Expect.equals(api.getExtensionMemberKind(setter), ir.ProcedureKind.Setter);
var staticGetter = findExtensionMember(descriptors, 'staticGetter');
Expect.equals(
api.getExtensionMemberKind(staticGetter), ir.ProcedureKind.Getter);
var staticSetter = findExtensionMember(descriptors, 'staticSetter');
Expect.equals(
api.getExtensionMemberKind(staticSetter), ir.ProcedureKind.Setter);
// Check methods.
var method = findExtensionMember(descriptors, 'method');
Expect.equals(api.getExtensionMemberKind(method), ir.ProcedureKind.Method);
var methodTearoff = findExtensionTearoff(descriptors, 'get#method');
Expect.equals(
api.getExtensionMemberKind(methodTearoff), ir.ProcedureKind.Getter);
var staticMethod = findExtensionMember(descriptors, 'staticMethod');
Expect.equals(
api.getExtensionMemberKind(staticMethod), ir.ProcedureKind.Method);
// Check operators.
var operator = findExtensionMember(descriptors, '+');
Expect.equals(api.getExtensionMemberKind(operator), ir.ProcedureKind.Method);
}
ir.Member findExtensionMember(
List<ir.ExtensionMemberDescriptor> descriptors, String memberName) {
return descriptors
.firstWhere((d) => d.name.text == memberName)
.member
.asMember;
}
ir.Member findExtensionField(List<ir.ExtensionMemberDescriptor> descriptors,
String fieldName, ir.ExtensionMemberKind kind) {
return descriptors
.firstWhere((d) => d.name.text == fieldName && d.kind == kind)
.member
.asMember;
}
ir.Member findExtensionTearoff(
List<ir.ExtensionMemberDescriptor> descriptors, String memberName) {
return descriptors
.map((d) => d.member.asMember)
.firstWhere((m) => m.name.text.contains(memberName));
}
const extensionSource = '''
class Foo {}
extension Ext on Foo {
external int field;
external static int staticField;
external get getter;
external set setter(_);
external static get staticGetter;
external static set staticSetter(_);
external int method();
external static int staticMethod();
external operator +(_);
}
''';