mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 03:07:49 +00:00
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:
parent
bd067153d5
commit
dc4d16fc1b
|
@ -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) {
|
||||
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 (api.getExtensionMemberKind(node) == ProcedureKind.Setter) {
|
||||
} else if (nodeDescriptor.kind == ExtensionMemberKind.Setter) {
|
||||
transformedBody = _getExternalSetterBody(node);
|
||||
} else if (api.getExtensionMemberKind(node) == ProcedureKind.Method) {
|
||||
} else if (nodeDescriptor.kind == ExtensionMemberKind.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;
|
||||
}
|
||||
} 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.
|
||||
|
|
|
@ -8,7 +8,5 @@ environment:
|
|||
dependencies:
|
||||
_fe_analyzer_shared:
|
||||
path: ../_fe_analyzer_shared
|
||||
front_end:
|
||||
path: ../front_end
|
||||
kernel:
|
||||
path: ../kernel
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 +(_);
|
||||
}
|
||||
''';
|
||||
|
|
Loading…
Reference in a new issue