Skip to content

Commit c033dd2

Browse files
MarkzipanCommit Queue
authored andcommitted
[ddc] Updating tearoffs to be evaluated on access.
Tearoffs are now represented as a closure that resolves an underlying bound context and property on access. `_boundMethod` and RTI getters must also be evaluated late. Additionally, we now both canonicalize static methods and tag them with their types at class-declaration time (though lazily) - so that late resolved closures have access to their types. Some tests have been updated to expect simpler errors. DDC traditionally emits slightly different errors that might aid in debugging. Change-Id: I1f762b8df45e0766d16dbc8688073768c8bfd233 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/401321 Reviewed-by: Nicholas Shahan <[email protected]> Commit-Queue: Mark Zhou <[email protected]>
1 parent 609f2f9 commit c033dd2

File tree

17 files changed

+7955
-70
lines changed

17 files changed

+7955
-70
lines changed

pkg/dev_compiler/lib/src/kernel/compiler_new.dart

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,12 +1236,32 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
12361236

12371237
var jsCtors = _defineConstructors(c, className);
12381238
var jsProperties = _emitClassProperties(c);
1239+
var jsStaticMethodTypeTags = <js_ast.Statement>[];
1240+
for (var member in c.procedures) {
1241+
// TODO(#57049): We tag all static members because we don't know if
1242+
// they've been changed after a hot reload. This won't be necessary if we
1243+
// can tag them during the delta diff phase.
1244+
if (member.isStatic && _reifyTearoff(member) && !member.isExternal) {
1245+
var propertyAccessor = _emitStaticTarget(member);
1246+
var result = js.call(
1247+
'#.#', [propertyAccessor.receiver, propertyAccessor.selector]);
1248+
// We only need to tag static functions that are torn off at
1249+
// compile-time. We attach these at late so tearoffs have access to
1250+
// their types.
1251+
var reifiedType = member.function
1252+
.computeThisFunctionType(member.enclosingLibrary.nonNullable);
1253+
jsStaticMethodTypeTags.add(
1254+
_emitFunctionTagged(result, reifiedType, asLazy: true)
1255+
.toStatement());
1256+
}
1257+
}
12391258

12401259
_emitSuperHelperSymbols(body);
12411260

12421261
// Emit the class, e.g. `core.Object = class Object { ... }`
12431262
_defineClass(c, className, jsProperties, body);
12441263
body.addAll(jsCtors);
1264+
body.addAll(jsStaticMethodTypeTags);
12451265

12461266
// Emit things that come after the ES6 `class ... { ... }`.
12471267

@@ -2473,7 +2493,6 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
24732493
} else {
24742494
method.sourceInformation = _nodeEnd(member.fileEndOffset);
24752495
}
2476-
24772496
return method;
24782497
}
24792498

@@ -3454,10 +3473,23 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
34543473
.where((p) =>
34553474
!p.isExternal && !p.isAbstract && !_isStaticInteropTearOff(p))
34563475
.toList();
3457-
_moduleItems.addAll(procedures
3458-
.where((p) => !p.isAccessor)
3459-
.map(_emitLibraryFunction)
3460-
.toList());
3476+
for (var p in procedures) {
3477+
if (!p.isAccessor) {
3478+
_moduleItems.add(_emitLibraryFunction(p));
3479+
}
3480+
// TODO(#57049): We tag all static members because we don't know if
3481+
// they've been changed after a hot reload. This won't be necessary if we
3482+
// can tag them during the delta diff phase.
3483+
if (p.isStatic && _reifyTearoff(p) && !p.isExternal) {
3484+
var nameExpr = _emitTopLevelName(p);
3485+
_moduleItems.add(_emitFunctionTagged(
3486+
nameExpr,
3487+
p.function
3488+
.computeThisFunctionType(p.enclosingLibrary.nonNullable),
3489+
asLazy: true)
3490+
.toStatement());
3491+
}
3492+
}
34613493
var accessors =
34623494
procedures.where((p) => p.isAccessor).map(_emitLibraryAccessor);
34633495
libraryProperties.addAll(accessors);
@@ -3638,22 +3670,27 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
36383670
return candidateName;
36393671
}
36403672

3641-
js_ast.Expression _emitFunctionTagged(
3642-
js_ast.Expression fn, FunctionType type) {
3673+
js_ast.Expression _emitFunctionTagged(js_ast.Expression fn, FunctionType type,
3674+
{bool asLazy = false}) {
36433675
var typeRep = _emitType(
36443676
// Avoid tagging a closure as Function? or Function*
36453677
type.withDeclaredNullability(Nullability.nonNullable));
36463678
if (type.typeParameters.isEmpty) {
3647-
return _runtimeCall('fn(#, #)', [fn, typeRep]);
3679+
return asLazy
3680+
? _runtimeCall('lazyFn(#, () => #)', [fn, typeRep])
3681+
: _runtimeCall('fn(#, #)', [fn, typeRep]);
36483682
} else {
36493683
var typeParameterDefaults = [
36503684
for (var parameter in type.typeParameters)
36513685
_emitType(parameter.defaultType)
36523686
];
36533687
var defaultInstantiatedBounds =
36543688
_emitConstList(const DynamicType(), typeParameterDefaults);
3655-
return _runtimeCall(
3656-
'gFn(#, #, #)', [fn, typeRep, defaultInstantiatedBounds]);
3689+
return asLazy
3690+
? _runtimeCall('lazyGFn(#, () => #, () => #)',
3691+
[fn, typeRep, defaultInstantiatedBounds])
3692+
: _runtimeCall(
3693+
'gFn(#, #, #)', [fn, typeRep, defaultInstantiatedBounds]);
36573694
}
36583695
}
36593696

@@ -5422,7 +5459,7 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
54225459
}
54235460
var jsMemberName = _emitMemberName(memberName, member: member);
54245461
if (_reifyTearoff(member)) {
5425-
return _runtimeCall('bind(#, #)', [jsReceiver, jsMemberName]);
5462+
return _runtimeCall('tearoff(#, #)', [jsReceiver, jsMemberName]);
54265463
}
54275464
var jsPropertyAccess = js_ast.PropertyAccess(jsReceiver, jsMemberName);
54285465
return isJsMember(member)
@@ -5612,15 +5649,12 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
56125649
_emitStaticGet(node.target);
56135650

56145651
js_ast.Expression _emitStaticGet(Member target) {
5615-
var result = _emitStaticTarget(target);
5652+
var propertyAccessor = _emitStaticTarget(target);
5653+
var context = propertyAccessor.receiver;
5654+
var property = propertyAccessor.selector;
5655+
var result = js.call('#.#', [context, property]);
56165656
if (_reifyTearoff(target)) {
5617-
// TODO(jmesserly): we could tag static/top-level function types once
5618-
// in the module initialization, rather than at the point where they
5619-
// escape.
5620-
return _emitFunctionTagged(
5621-
result,
5622-
target.function!
5623-
.computeThisFunctionType(target.enclosingLibrary.nonNullable));
5657+
return _runtimeCall('staticTearoff(#, #)', [context, property]);
56245658
}
56255659
return result;
56265660
}
@@ -6721,7 +6755,7 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
67216755
}
67226756

67236757
/// Emits the target of a [StaticInvocation], [StaticGet], or [StaticSet].
6724-
js_ast.Expression _emitStaticTarget(Member target) {
6758+
js_ast.PropertyAccess _emitStaticTarget(Member target) {
67256759
var c = target.enclosingClass;
67266760
if (c != null) {
67276761
// A static native element should just forward directly to the JS type's
@@ -6731,9 +6765,12 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
67316765
if (isExternal && (target as Procedure).isStatic) {
67326766
var nativeName = _extensionTypes.getNativePeers(c);
67336767
if (nativeName.isNotEmpty) {
6734-
var memberName = _annotationName(target, isJSName) ??
6735-
_emitStaticMemberName(target.name.text, target);
6736-
return _runtimeCall('global.#.#', [nativeName[0], memberName]);
6768+
var annotationName = _annotationName(target, isJSName);
6769+
var memberName = annotationName == null
6770+
? _emitStaticMemberName(target.name.text, target)
6771+
: js.string(annotationName);
6772+
return js_ast.PropertyAccess(
6773+
_runtimeCall('global.#', [nativeName[0]]), memberName);
67376774
}
67386775
}
67396776
return js_ast.PropertyAccess(_emitStaticClassName(c, isExternal),

pkg/dev_compiler/lib/src/kernel/expression_compiler.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,9 +233,9 @@ class ExpressionCompiler {
233233

234234
var args = localJsScope.join(',\n ');
235235
jsExpression = jsExpression.split('\n').join('\n ');
236-
var callExpression = '\n ($jsExpression('
237-
'\n $args'
238-
'\n ))';
236+
// We check for '_boundMethod' in case tearoffs are returned.
237+
var callExpression = '((() => {var output = $jsExpression($args); '
238+
'return output?._boundMethod || output;})())';
239239

240240
_log('Compiled expression \n$expression to $callExpression');
241241
return callExpression;

pkg/dev_compiler/test/expression_compiler/runtime_debugger_api_test.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -697,11 +697,13 @@ void runSharedTests(
697697
test('getFunctionName (static method)', () async {
698698
var getFunctionName =
699699
setup.emitLibraryBundle ? 'getFunctionName' : 'getFunctionMetadata';
700+
var expectedName =
701+
setup.emitLibraryBundle ? 'BaseClass.staticMethod' : 'staticMethod';
700702

701703
await driver.checkRuntimeInFrame(
702704
breakpointId: 'BP',
703705
expression: 'dart.$getFunctionName(staticMethod)',
704-
expectedResult: 'staticMethod');
706+
expectedResult: expectedName);
705707
});
706708

707709
test('getFunctionName (global method)', () async {

sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/debugger.dart

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -270,15 +270,21 @@ void _collectFieldDescriptors(
270270
/// returns `C.functionName`.
271271
/// Otherwise, returns function name.
272272
String getFunctionMetadata(@notNull Function function) {
273-
var name = _get<String>(function, 'name');
273+
var boundName = _get<String?>(function, '_boundName');
274+
var name = boundName ?? _get<String>(function, 'name');
274275
var boundObject = _get<Object?>(function, '_boundObject');
275276

276277
if (boundObject != null) {
277-
var cls = _get<Object>(boundObject, 'constructor');
278-
var className = _dartClassName(cls);
279-
280-
var boundMethod = _get<Object>(function, '_boundMethod');
281-
name = className + '.' + _get<String>(boundMethod, 'name');
278+
if (_hasConstructor(boundObject)) {
279+
// Bound objects for global methods are libraries, which don't have a
280+
// constructor.
281+
var constructor = _get<Object>(boundObject, 'constructor');
282+
if (JS<bool>('', '# == null', constructor)) return name;
283+
if (_isDartClassObject(boundObject)) {
284+
var className = _dartClassName(boundObject);
285+
name = className + '.' + name;
286+
}
287+
}
282288
}
283289
return name;
284290
}
@@ -306,7 +312,9 @@ Object getObjectMetadata(@notNull Object object) {
306312
// When the object is actually represented by a JavaScript Array use
307313
// the interceptor class that matches the reified type.
308314
? JS_CLASS_REF(JSArray)
309-
: _get<Object?>(object, 'constructor');
315+
: (_hasConstructor(object)
316+
? _get<Object>(object, 'constructor')
317+
: object);
310318
if (cls != null) {
311319
libraryId = getLibraryUri(cls);
312320
}
@@ -391,7 +399,8 @@ Object getObjectMetadata(@notNull Object object) {
391399
@notNull
392400
List<String> getObjectFieldNames(@notNull Object object) {
393401
var fieldNames = <String>[];
394-
var cls = _get<Object>(object, 'constructor');
402+
var cls =
403+
_hasConstructor(object) ? _get<Object>(object, 'constructor') : object;
395404

396405
_collectObjectFieldNames(fieldNames, getFields(cls));
397406
return fieldNames..sort();
@@ -538,6 +547,10 @@ Object getSubRange(@notNull Object object, int offset, int count) {
538547
return object;
539548
}
540549

550+
@notNull
551+
bool _hasConstructor(@notNull Object object) =>
552+
_get(object, 'constructor') != null;
553+
541554
@notNull
542555
bool _isDartClassObject(@notNull Object object) =>
543556
_get(object, rti.interfaceTypeRecipePropertyName) != null;

0 commit comments

Comments
 (0)