Skip to content

Commit 1806e4c

Browse files
nshahanCommit Queue
authored andcommitted
[ddc] Use direct cast methods for primitive casts
The `_as` methods for primitive types are designed to be called from both the `_as` selector and directly from generated code. For the primitive types that are known at compile time and cannot change via linking or hot reload, we can generate direct calls. Adapted from https://dart-review.googlesource.com/c/sdk/+/420380 In addition to the base change: * Remove `int` cast helpers from DDC only code in favor of the versions from dart:_rti. * Moves existing optimizations from `visitAsExpression` to `_emitCast` to apply them consistently. Change-Id: I13d24e3756400f2358556812728db17bc73f544a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/427040 Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Nicholas Shahan <[email protected]> Reviewed-by: Stephen Adams <[email protected]>
1 parent 2b5b551 commit 1806e4c

File tree

4 files changed

+231
-147
lines changed

4 files changed

+231
-147
lines changed

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

Lines changed: 109 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,19 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
449449

450450
final Procedure _assertInteropMethod;
451451

452+
// The direct `_as` methods for primitive types.
453+
final Member _asBool;
454+
final Member _asDouble;
455+
final Member _asInt;
456+
final Member _asNum;
457+
final Member _asObject;
458+
final Member _asString;
459+
final Member _asBoolQ;
460+
final Member _asDoubleQ;
461+
final Member _asIntQ;
462+
final Member _asNumQ;
463+
final Member _asStringQ;
464+
452465
final DevCompilerConstants _constants;
453466

454467
final NullableInference _nullableInference;
@@ -552,7 +565,18 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
552565
ExtensionIndex(_coreTypes, _staticTypeContext.typeEnvironment),
553566
_inlineTester = BasicInlineTester(_constants),
554567
_rtiLibrary = sdk.getLibrary('dart:_rti'),
555-
_rtiClass = sdk.getClass('dart:_rti', 'Rti');
568+
_rtiClass = sdk.getClass('dart:_rti', 'Rti'),
569+
_asBool = sdk.getTopLevelMember('dart:_rti', '_asBool'),
570+
_asDouble = sdk.getTopLevelMember('dart:_rti', '_asDouble'),
571+
_asInt = sdk.getTopLevelMember('dart:_rti', '_asInt'),
572+
_asNum = sdk.getTopLevelMember('dart:_rti', '_asNum'),
573+
_asObject = sdk.getTopLevelMember('dart:_rti', '_asObject'),
574+
_asString = sdk.getTopLevelMember('dart:_rti', '_asString'),
575+
_asBoolQ = sdk.getTopLevelMember('dart:_rti', '_asBoolQ'),
576+
_asDoubleQ = sdk.getTopLevelMember('dart:_rti', '_asDoubleQ'),
577+
_asIntQ = sdk.getTopLevelMember('dart:_rti', '_asIntQ'),
578+
_asNumQ = sdk.getTopLevelMember('dart:_rti', '_asNumQ'),
579+
_asStringQ = sdk.getTopLevelMember('dart:_rti', '_asStringQ');
556580

557581
/// The library for dart:core in the SDK.
558582
Library get _coreLibrary => _coreTypes.coreLibrary;
@@ -6963,82 +6987,98 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
69636987
var fromExpr = node.operand;
69646988
var jsFrom = _visitExpression(fromExpr);
69656989
if (node.isUnchecked) return jsFrom;
6966-
var to = node.type.extensionTypeErasure;
6967-
var from = fromExpr.getStaticType(_staticTypeContext).extensionTypeErasure;
6968-
6969-
// If the check was put here by static analysis to ensure soundness, we
6970-
// can't skip it. For example, one could implement covariant generic caller
6971-
// side checks like this:
6972-
//
6973-
// typedef F<T>(T t);
6974-
// class C<T> {
6975-
// F<T> f;
6976-
// add(T t) {
6977-
// // required check `t as T`
6978-
// }
6979-
// }
6980-
// main() {
6981-
// C<Object> c = new C<int>()..f = (int x) => x.isEven;
6982-
// c.f('hi'); // required check `c.f as F<Object>`
6983-
// c.add('hi);
6984-
// }
6985-
//
6986-
var isTypeError = node.isTypeError;
6987-
if (!isTypeError &&
6988-
_types.isSubtypeOf(from, to, SubtypeCheckMode.withNullabilities)) {
6989-
return jsFrom;
6990-
}
6991-
6992-
if (!isTypeError &&
6993-
DartTypeEquivalence(_coreTypes, ignoreTopLevelNullability: true)
6994-
.areEqual(from, to) &&
6995-
_mustBeNonNullable(to)) {
6996-
// If the underlying type is the same, we only need a null check.
6997-
return _runtimeCall('nullCast(#, #)', [jsFrom, _emitType(to)]);
6998-
}
6999-
7000-
// All Dart number types map to a JS double. We can specialize these
7001-
// cases.
7002-
if (_typeRep.isNumber(from) && _typeRep.isNumber(to)) {
7003-
// If `to` is some form of `num`, it should have been filtered above.
7004-
7005-
// * -> double? : no-op
7006-
if (to == _coreTypes.doubleNullableRawType) {
7007-
return jsFrom;
7008-
}
7009-
7010-
// * -> double : null check
7011-
if (to == _coreTypes.doubleNonNullableRawType) {
7012-
if (from.nullability == Nullability.nonNullable) {
7013-
return jsFrom;
6990+
return _emitCast(jsFrom, node.type,
6991+
fromStaticType: fromExpr.getStaticType(_staticTypeContext),
6992+
isTypeError: node.isTypeError);
6993+
}
6994+
6995+
js_ast.Expression _emitCast(js_ast.Expression value, DartType toType,
6996+
{DartType? fromStaticType, bool isTypeError = false}) {
6997+
toType = toType.extensionTypeErasure;
6998+
if (_types.isTop(toType)) return value;
6999+
if (fromStaticType != null) {
7000+
fromStaticType = fromStaticType.extensionTypeErasure;
7001+
// If the check was put here by static analysis to ensure soundness, we
7002+
// can't skip it. For example, one could implement covariant generic
7003+
// caller side checks like this:
7004+
//
7005+
// typedef F<T>(T t);
7006+
// class C<T> {
7007+
// F<T> f;
7008+
// add(T t) {
7009+
// // required check `t as T`
7010+
// }
7011+
// }
7012+
// main() {
7013+
// C<Object> c = new C<int>()..f = (int x) => x.isEven;
7014+
// c.f('hi'); // required check `c.f as F<Object>`
7015+
// c.add('hi);
7016+
// }
7017+
//
7018+
if (!isTypeError &&
7019+
_types.isSubtypeOf(
7020+
fromStaticType, toType, SubtypeCheckMode.withNullabilities)) {
7021+
return value;
7022+
}
7023+
if (!isTypeError &&
7024+
_mustBeNonNullable(toType) &&
7025+
DartTypeEquivalence(_coreTypes, ignoreTopLevelNullability: true)
7026+
.areEqual(fromStaticType, toType)) {
7027+
// If the underlying type is the same, we only need a null check.
7028+
return _runtimeCall('nullCast(#, #)', [value, _emitType(toType)]);
7029+
}
7030+
// All Dart number types map to a JavaScript Number. We can specialize
7031+
// these cases.
7032+
if (_typeRep.isNumber(fromStaticType) && _typeRep.isNumber(toType)) {
7033+
// If `toType` is some form of `num`, it should have been filtered
7034+
// above.
7035+
if (toType == _coreTypes.doubleNullableRawType) {
7036+
// Any number/nullability -> double? : no-op
7037+
return value;
7038+
}
7039+
if (toType == _coreTypes.doubleNonNullableRawType) {
7040+
if (fromStaticType.nullability == Nullability.nonNullable) {
7041+
// Any non-nullable number -> double : no-op
7042+
return value;
7043+
}
7044+
// Any number/nullability -> double : null check
7045+
return _runtimeCall('nullCast(#, #)', [value, _emitType(toType)]);
70147046
}
7015-
return _runtimeCall('nullCast(#, #)', [jsFrom, _emitType(to)]);
7016-
}
7017-
7018-
// * -> int : asInt check
7019-
if (to == _coreTypes.intNonNullableRawType) {
7020-
return _runtimeCall('asInt(#)', [jsFrom]);
7021-
}
7022-
7023-
// * -> int? : asNullableInt check
7024-
if (to == _coreTypes.intNullableRawType) {
7025-
return _runtimeCall('asNullableInt(#)', [jsFrom]);
70267047
}
70277048
}
7028-
7029-
return _emitCast(jsFrom, to);
7030-
}
7031-
7032-
js_ast.Expression _emitCast(js_ast.Expression expr, DartType type) {
7033-
var normalizedType = type.extensionTypeErasure;
7034-
if (_types.isTop(normalizedType)) return expr;
7049+
var directMethod = _directCastMethod(toType);
7050+
if (directMethod != null) {
7051+
return js.call('#(#)', [_emitTopLevelName(directMethod), value]);
7052+
}
70357053
return js.call('#.#(#)', [
7036-
_emitType(normalizedType),
7054+
_emitType(toType),
70377055
_emitMemberName(js_ast.FixedNames.rtiAsField, memberClass: _rtiClass),
7038-
expr
7056+
value
70397057
]);
70407058
}
70417059

7060+
/// Returns the direct `_as` method when [type] is a primitive type otherwise,
7061+
/// `null`.
7062+
Member? _directCastMethod(DartType type) {
7063+
if (type is InterfaceType && type.typeArguments.isEmpty) {
7064+
if (type.nullability == Nullability.nonNullable) {
7065+
if (type == _types.coreTypes.boolNonNullableRawType) return _asBool;
7066+
if (type == _types.coreTypes.doubleNonNullableRawType) return _asDouble;
7067+
if (type == _types.coreTypes.intNonNullableRawType) return _asInt;
7068+
if (type == _types.coreTypes.numNonNullableRawType) return _asNum;
7069+
if (type == _types.coreTypes.objectNonNullableRawType) return _asObject;
7070+
if (type == _types.coreTypes.stringNonNullableRawType) return _asString;
7071+
} else if (type.nullability == Nullability.nullable) {
7072+
if (type == _types.coreTypes.boolNullableRawType) return _asBoolQ;
7073+
if (type == _types.coreTypes.doubleNullableRawType) return _asDoubleQ;
7074+
if (type == _types.coreTypes.intNullableRawType) return _asIntQ;
7075+
if (type == _types.coreTypes.numNullableRawType) return _asNumQ;
7076+
if (type == _types.coreTypes.stringNullableRawType) return _asStringQ;
7077+
}
7078+
}
7079+
return null;
7080+
}
7081+
70427082
@override
70437083
js_ast.Expression visitSymbolLiteral(SymbolLiteral node) =>
70447084
_emitDartSymbol(node.value);

0 commit comments

Comments
 (0)