Skip to content

Commit 3f5314f

Browse files
rakudramaCommit Queue
authored andcommitted
Reapply "[dart2js,rti] Add direct methods for as JSObject"
This reverts commit 43b5c14. This time, fall back on code that works for mocks and fakes. Issue: #60746 Change-Id: Iecb822486729f8b10377c94bf87d06406bb0f5bc Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/434920 Commit-Queue: Stephen Adams <[email protected]> Reviewed-by: Mayank Patke <[email protected]>
1 parent 111f979 commit 3f5314f

File tree

5 files changed

+95
-3
lines changed

5 files changed

+95
-3
lines changed

pkg/compiler/lib/src/common/elements.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,9 @@ abstract class CommonElements {
700700

701701
late final ClassEntity jsUInt31Class = _findInterceptorsClass('JSUInt31');
702702

703+
// Static interop JavaScript object interface class.
704+
late final ClassEntity jsObjectClass = _findInterceptorsClass('JSObject');
705+
703706
/// Returns `true` member is the 'findIndexForNativeSubclassType' method
704707
/// declared in `dart:_interceptors`.
705708
bool isFindIndexForNativeSubclassType(MemberEntity member) {
@@ -1100,6 +1103,11 @@ abstract class CommonElements {
11001103
FunctionEntity get specializedAsStringNullable =>
11011104
_findRtiFunction('_asStringQ');
11021105

1106+
FunctionEntity get specializedAsJSObject => _findRtiFunction('_asJSObject');
1107+
1108+
FunctionEntity get specializedAsJSObjectNullable =>
1109+
_findRtiFunction('_asJSObjectQ');
1110+
11031111
FunctionEntity get instantiatedGenericFunctionType =>
11041112
_findRtiFunction('instantiatedGenericFunctionType');
11051113

pkg/compiler/lib/src/js_backend/specialized_checks.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,15 @@ class SpecializedChecks {
192192
return commonElements.specializedAsInt;
193193
}
194194

195+
if (element == commonElements.objectClass) {
196+
if (!nullable) return commonElements.specializedAsObject;
197+
}
198+
199+
if (element == commonElements.jsObjectClass) {
200+
if (nullable) return commonElements.specializedAsJSObjectNullable;
201+
return commonElements.specializedAsJSObject;
202+
}
203+
195204
return null;
196205
}
197206
}

pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1973,6 +1973,7 @@ class FragmentEmitter {
19731973
commonElements.listClass,
19741974
commonElements.objectClass,
19751975
commonElements.mapClass,
1976+
commonElements.jsObjectClass,
19761977
];
19771978
// TODO(floitsch): this should probably be on a per-fragment basis.
19781979

sdk/lib/_internal/js_shared/lib/rti.dart

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,6 +1337,8 @@ Object? _specializedAsCheck(Rti testRti) {
13371337
asFn = RAW_DART_FUNCTION_REF(_asNumQ);
13381338
} else if (_Utils.isIdentical(testRti, TYPE_REF<double?>())) {
13391339
asFn = RAW_DART_FUNCTION_REF(_asDoubleQ);
1340+
} else if (_Utils.isIdentical(testRti, TYPE_REF<JSObject?>())) {
1341+
asFn = RAW_DART_FUNCTION_REF(_asJSObjectQ);
13401342
}
13411343
} else {
13421344
if (_Utils.isIdentical(testRti, TYPE_REF<int>())) {
@@ -1349,6 +1351,8 @@ Object? _specializedAsCheck(Rti testRti) {
13491351
asFn = RAW_DART_FUNCTION_REF(_asNum);
13501352
} else if (_Utils.isIdentical(testRti, TYPE_REF<double>())) {
13511353
asFn = RAW_DART_FUNCTION_REF(_asDouble);
1354+
} else if (_Utils.isIdentical(testRti, TYPE_REF<JSObject>())) {
1355+
asFn = RAW_DART_FUNCTION_REF(_asJSObject);
13521356
}
13531357
}
13541358
return asFn;
@@ -1460,6 +1464,27 @@ bool _isJSObject(Object? object) {
14601464
return false;
14611465
}
14621466

1467+
/// A version of _isJSObject that does not need to be attached to an Rti `_is`
1468+
/// method. The Rti is needed on the uncommon slow path (Dart mocks and fakes of
1469+
/// interp objects), so this method falls back to using a full `is JSObject`
1470+
/// expression, with some duplicated work on the uncommon paths.
1471+
bool _isJSObjectStandalone(Object? object) {
1472+
// Keep the control flow here matching `_isJSObject`.
1473+
if (JS('bool', 'typeof # == "object"', object)) {
1474+
if (_isDartObject(object)) {
1475+
return object is JSObject;
1476+
}
1477+
return true;
1478+
}
1479+
if (JS('bool', 'typeof # == "function"', object)) {
1480+
if (JS_GET_FLAG('DEV_COMPILER')) {
1481+
return object is JSObject;
1482+
}
1483+
return true;
1484+
}
1485+
return false;
1486+
}
1487+
14631488
/// General unspecialized 'as' check that works for any type.
14641489
/// Called from generated code.
14651490
@pragma('dart2js:stack-starts-at-throw')
@@ -1701,6 +1726,23 @@ String? _asStringQ(dynamic object) {
17011726
throw _TypeError.forType(object, 'String?');
17021727
}
17031728

1729+
/// Specialization for 'as JSObject'.
1730+
/// Called from generated code.
1731+
@pragma('dart2js:stack-starts-at-throw')
1732+
JSObject _asJSObject(Object? object) {
1733+
if (_isJSObjectStandalone(object)) return _Utils.asJSObject(object);
1734+
throw _TypeError.forType(object, 'JSObject');
1735+
}
1736+
1737+
/// Specialization for 'as JSObject?'.
1738+
/// Called from generated code.
1739+
@pragma('dart2js:stack-starts-at-throw')
1740+
JSObject? _asJSObjectQ(Object? object) {
1741+
if (object == null) return _Utils.asNull(object);
1742+
if (_isJSObjectStandalone(object)) return _Utils.asJSObject(object);
1743+
throw _TypeError.forType(object, 'JSObject?');
1744+
}
1745+
17041746
String _rtiArrayToString(Object? array, List<String>? genericContext) {
17051747
String s = '', sep = '';
17061748
for (int i = 0; i < _Utils.arrayLength(array); i++) {
@@ -3920,6 +3962,7 @@ class _Utils {
39203962
static Rti asRti(Object? s) => JS('Rti', '#', s);
39213963
static Rti? asRtiOrNull(Object? s) => JS('Rti|Null', '#', s);
39223964
static _Type as_Type(Object? o) => JS('_Type', '#', o);
3965+
static JSObject asJSObject(Object? o) => JS('', '#', o);
39233966

39243967
static bool isString(Object? o) => JS('bool', 'typeof # == "string"', o);
39253968
static bool isNum(Object? o) => JS('bool', 'typeof # == "number"', o);

tests/web/internal/html_mocks_with_static_interop_test.dart

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,14 @@ class MockDocument implements HtmlDocument {
2323
}
2424

2525
void main() {
26-
test(MockWindow(), true);
27-
test(window, false);
26+
testIs(MockWindow(), true);
27+
testIs(window, false);
28+
29+
testAs(MockWindow(), true);
30+
testAs(window, false);
2831
}
2932

30-
void test(Window w, bool isMock) {
33+
void testIs(Window w, bool isMock) {
3134
Expect.type<Window>(w);
3235
Expect.type<dartJsInterop.JSObject>(w);
3336

@@ -46,3 +49,31 @@ void test(Window w, bool isMock) {
4649
Expect.isFalse(doc is MockDocument);
4750
}
4851
}
52+
53+
void testAs(Window w, bool isMock) {
54+
asCheck<Window>(w);
55+
asCheck<dartJsInterop.JSObject>(w);
56+
57+
final doc = w.document;
58+
59+
asCheck<HtmlDocument>(doc);
60+
asCheck<dartJsInterop.JSObject>(doc);
61+
62+
if (isMock) {
63+
asCheck<MockWindow>(w);
64+
asCheck<MockDocument>(doc);
65+
w as MockWindow;
66+
doc as MockDocument;
67+
} else {
68+
Expect.throws(() => asCheck<MockWindow>(w));
69+
Expect.throws(() => asCheck<MockDocument>(doc));
70+
Expect.throws(() => w as MockWindow);
71+
Expect.throws(() => doc as MockDocument);
72+
}
73+
74+
asCheck<String>('hello');
75+
Expect.throws(() => asCheck<String>(w));
76+
}
77+
78+
@pragma('dart2js:never-inline')
79+
void asCheck<T>(Object? o) => o as T;

0 commit comments

Comments
 (0)