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
// 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/class_hierarchy.dart';
import 'package:kernel/core_types.dart';
@ -37,6 +36,7 @@ class JsUtilOptimizer extends Transformer {
final CoreTypes _coreTypes;
final StatefulStaticTypeContext _staticTypeContext;
Map<Reference, ExtensionMemberDescriptor>? _extensionMemberIndex;
JsUtilOptimizer(this._coreTypes, ClassHierarchy hierarchy)
: _jsTarget =
@ -74,6 +74,7 @@ class JsUtilOptimizer extends Transformer {
_staticTypeContext.enterLibrary(lib);
lib.transformChildren(this);
_staticTypeContext.leaveLibrary(lib);
_extensionMemberIndex = null;
return lib;
}
@ -88,21 +89,24 @@ class JsUtilOptimizer extends Transformer {
@override
visitProcedure(Procedure node) {
_staticTypeContext.enterMember(node);
if (node.isExternal && node.isExtensionMember && !_isDeclaredStatic(node)) {
var transformedBody;
if (api.getExtensionMemberKind(node) == ProcedureKind.Getter) {
transformedBody = _getExternalGetterBody(node);
} else if (api.getExtensionMemberKind(node) == ProcedureKind.Setter) {
transformedBody = _getExternalSetterBody(node);
} else if (api.getExtensionMemberKind(node) == ProcedureKind.Method) {
transformedBody = _getExternalMethodBody(node);
}
// TODO(rileyporter): Add transformation for static members and any
// operators we decide to support.
if (transformedBody != null) {
node.function.body = transformedBody;
node.isExternal = false;
var transformedBody;
if (node.isExternal && node.isExtensionMember) {
var index = _extensionMemberIndex ??=
_createExtensionMembersIndex(node.enclosingLibrary);
var nodeDescriptor = index[node.reference]!;
if (!nodeDescriptor.isStatic) {
if (nodeDescriptor.kind == ExtensionMemberKind.Getter) {
transformedBody = _getExternalGetterBody(node);
} else if (nodeDescriptor.kind == ExtensionMemberKind.Setter) {
transformedBody = _getExternalSetterBody(node);
} else if (nodeDescriptor.kind == ExtensionMemberKind.Method) {
transformedBody = _getExternalMethodBody(node);
}
}
}
if (transformedBody != null) {
node.function.body = transformedBody;
node.isExternal = false;
} else {
node.transformChildren(this);
}
@ -110,6 +114,18 @@ class JsUtilOptimizer extends Transformer {
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.
///
/// The new function body will call the optimized version of
@ -121,7 +137,7 @@ class JsUtilOptimizer extends Transformer {
_getPropertyTarget,
Arguments([
VariableGet(function.positionalParameters.first),
StringLiteral(_getMemberName(node))
StringLiteral(_getExtensionMemberName(node))
]))
..fileOffset = node.fileOffset;
return ReturnStatement(AsExpression(
@ -139,7 +155,7 @@ class JsUtilOptimizer extends Transformer {
_setPropertyTarget,
Arguments([
VariableGet(function.positionalParameters.first),
StringLiteral(_getMemberName(node)),
StringLiteral(_getExtensionMemberName(node)),
VariableGet(function.positionalParameters.last)
]))
..fileOffset = node.fileOffset;
@ -157,7 +173,7 @@ class JsUtilOptimizer extends Transformer {
_callMethodTarget,
Arguments([
VariableGet(function.positionalParameters.first),
StringLiteral(_getMemberName(node)),
StringLiteral(_getExtensionMemberName(node)),
ListLiteral(function.positionalParameters
.sublist(1)
.map((argument) => VariableGet(argument))
@ -168,29 +184,17 @@ class JsUtilOptimizer extends Transformer {
_lowerCallMethod(callMethodInvocation), function.returnType));
}
/// Returns the member name, either from the `@JS` annotation if non-empty,
/// or parsed from CFE generated node name.
String _getMemberName(Procedure node) {
/// Returns the extension member name.
///
/// 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);
if (jsAnnotationName.isNotEmpty) {
return jsAnnotationName;
}
// TODO(rileyporter): Switch to using the ExtensionMemberDescriptor data.
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';
return _extensionMemberIndex![node.reference]!.name.text;
}
/// Replaces js_util method calls with optimization when possible.

View file

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

View file

@ -241,23 +241,3 @@ Iterable<String> getSupportedLibraryNames(
// TODO(sigmund): Delete this API once `member.isRedirectingFactory`
// is implemented correctly for patch files (Issue #33495).
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 testRedirectingFactorySerialized();
await testRedirectingFactoryPatchFile();
await testExtensionMemberKind();
});
}
@ -80,97 +79,3 @@ class _B implements A {
_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 +(_);
}
''';