@@ -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