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
|
// 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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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 +(_);
|
|
||||||
}
|
|
||||||
''';
|
|
||||||
|
|
Loading…
Reference in a new issue