mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 15:01:30 +00:00
Add external extension methods.
CFE transformation to add a function body for external extension methods. Routes to `js_util.callMethod` with the arguments in a ListLiteral to then be further optimized to the `callMethodUnchecked` version if possible. Change-Id: Iccdda7b8c16c19b37e20b7cbceb8c32498e64d3b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/213721 Commit-Queue: Riley Porter <rileyporter@google.com> Reviewed-by: Sigmund Cherem <sigmund@google.com>
This commit is contained in:
parent
1379c867a4
commit
a0d9d35b80
3 changed files with 113 additions and 10 deletions
|
@ -88,19 +88,17 @@ class JsUtilOptimizer extends Transformer {
|
|||
@override
|
||||
visitProcedure(Procedure node) {
|
||||
_staticTypeContext.enterMember(node);
|
||||
// Getters, setters, and fields declared as `static` will be skipped,
|
||||
// because they do not have a node.kind of ProcedureKind.Method.
|
||||
if (node.isExternal &&
|
||||
node.isExtensionMember &&
|
||||
node.kind == ProcedureKind.Method) {
|
||||
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 external extension methods,
|
||||
// static members, and any operators we decide to support.
|
||||
// TODO(rileyporter): Add transformation for static members and any
|
||||
// operators we decide to support.
|
||||
if (transformedBody != null) {
|
||||
node.function.body = transformedBody;
|
||||
node.isExternal = false;
|
||||
|
@ -149,13 +147,50 @@ class JsUtilOptimizer extends Transformer {
|
|||
_lowerSetProperty(setPropertyInvocation), function.returnType));
|
||||
}
|
||||
|
||||
/// Returns a new function body for the given [node] external method.
|
||||
///
|
||||
/// The new function body will call the optimized version of
|
||||
/// `js_util.callMethod` for the given external method.
|
||||
ReturnStatement _getExternalMethodBody(Procedure node) {
|
||||
var function = node.function;
|
||||
var callMethodInvocation = StaticInvocation(
|
||||
_callMethodTarget,
|
||||
Arguments([
|
||||
VariableGet(function.positionalParameters.first),
|
||||
StringLiteral(_getMemberName(node)),
|
||||
ListLiteral(function.positionalParameters
|
||||
.sublist(1)
|
||||
.map((argument) => VariableGet(argument))
|
||||
.toList())
|
||||
]))
|
||||
..fileOffset = node.fileOffset;
|
||||
return ReturnStatement(AsExpression(
|
||||
_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) {
|
||||
var jsAnnotationName = getJSName(node);
|
||||
return jsAnnotationName.isNotEmpty
|
||||
? jsAnnotationName
|
||||
: node.name.text.substring(node.name.text.indexOf('#') + 1);
|
||||
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';
|
||||
}
|
||||
|
||||
/// Replaces js_util method calls with optimization when possible.
|
||||
|
|
|
@ -33,6 +33,14 @@ extension FooExt on Foo {
|
|||
external set setter(_);
|
||||
@JS('setterAnnotation')
|
||||
external set annotatedSetter(_);
|
||||
|
||||
external num getField();
|
||||
@JS('toString')
|
||||
external String extToString();
|
||||
external dynamic getFirstEl(list);
|
||||
external num sumFn(a, b);
|
||||
@JS('sumFn')
|
||||
external num otherSumFn(a, b);
|
||||
}
|
||||
|
||||
@JS('module.Bar')
|
||||
|
@ -56,6 +64,22 @@ void main() {
|
|||
this.getterAnnotation = a;
|
||||
}
|
||||
|
||||
Foo.prototype.toString = function() {
|
||||
return "Foo: " + this.field;
|
||||
}
|
||||
|
||||
Foo.prototype.getField = function() {
|
||||
return this.field;
|
||||
}
|
||||
|
||||
Foo.prototype.getFirstEl = function(list) {
|
||||
return list[0];
|
||||
}
|
||||
|
||||
Foo.prototype.sumFn = function(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
var module = {Bar: Foo};
|
||||
""");
|
||||
|
||||
|
@ -94,6 +118,16 @@ void main() {
|
|||
expect(js_util.getProperty(foo, 'setterAnnotation'), equals('whale'));
|
||||
});
|
||||
|
||||
test('methods', () {
|
||||
var foo = Foo(42);
|
||||
|
||||
expect(foo.getField(), equals(42));
|
||||
expect(foo.extToString(), equals('Foo: 42'));
|
||||
expect(foo.getFirstEl([1, 2, 3]), equals(1));
|
||||
expect(foo.sumFn(2, 3), equals(5));
|
||||
expect(foo.otherSumFn(10, 5), equals(15));
|
||||
});
|
||||
|
||||
test('module class', () {
|
||||
var bar = Bar(5);
|
||||
expect(js_util.getProperty(bar, 'fieldAnnotation'), equals(5));
|
||||
|
|
|
@ -30,6 +30,14 @@ extension FooExt on Foo {
|
|||
external set setter(_);
|
||||
@JS('setterAnnotation')
|
||||
external set annotatedSetter(_);
|
||||
|
||||
external num getField();
|
||||
@JS('toString')
|
||||
external String extToString();
|
||||
external dynamic getFirstEl(list);
|
||||
external num sumFn(a, b);
|
||||
@JS('sumFn')
|
||||
external num otherSumFn(a, b);
|
||||
}
|
||||
|
||||
@JS('module.Bar')
|
||||
|
@ -55,6 +63,22 @@ void main() {
|
|||
this.getterAnnotation = a;
|
||||
}
|
||||
|
||||
Foo.prototype.toString = function() {
|
||||
return "Foo: " + this.field;
|
||||
}
|
||||
|
||||
Foo.prototype.getField = function() {
|
||||
return this.field;
|
||||
}
|
||||
|
||||
Foo.prototype.getFirstEl = function(list) {
|
||||
return list[0];
|
||||
}
|
||||
|
||||
Foo.prototype.sumFn = function(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
var module = {Bar: Foo};
|
||||
""");
|
||||
|
||||
|
@ -76,6 +100,16 @@ void main() {
|
|||
expect(js_util.getProperty(foo, 'setterAnnotation'), equals('whale'));
|
||||
});
|
||||
|
||||
test('methods', () {
|
||||
var foo = Foo(42);
|
||||
|
||||
expect(foo.getField(), equals(42));
|
||||
expect(foo.extToString(), equals('Foo: 42'));
|
||||
expect(foo.getFirstEl([1, 2, 3]), equals(1));
|
||||
expect(foo.sumFn(2, 3), equals(5));
|
||||
expect(foo.otherSumFn(10, 5), equals(15));
|
||||
});
|
||||
|
||||
test('module class', () {
|
||||
var bar = Bar(5);
|
||||
expect(js_util.getProperty(bar, 'fieldAnnotation'), equals(5));
|
||||
|
|
Loading…
Reference in a new issue