Skip to content

Commit 2cc7d38

Browse files
rakudramaCommit Queue
authored andcommitted
[rti/js_interop] Accelerate is JSObject
Change-Id: I36501521db9922068f8e5246e9edb0e63b561d0f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/422200 Commit-Queue: Stephen Adams <sra@google.com> Reviewed-by: Srujan Gaddam <srujzs@google.com>
1 parent b7aaf90 commit 2cc7d38

File tree

2 files changed

+173
-1
lines changed

2 files changed

+173
-1
lines changed

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import 'dart:_foreign_helper'
1818
RAW_DART_FUNCTION_REF,
1919
TYPE_REF;
2020
import 'dart:_interceptors'
21-
show JavaScriptFunction, JSArray, JSNull, JSUnmodifiableArray;
21+
show JavaScriptFunction, JSArray, JSNull, JSUnmodifiableArray, JSObject;
2222
import 'dart:_js_helper'
2323
as records
2424
show createRecordTypePredicate, getRtiForRecord;
@@ -1306,6 +1306,8 @@ Object? _simpleSpecializedIsTest(Rti testRti) {
13061306
isFn = RAW_DART_FUNCTION_REF(_isString);
13071307
} else if (_Utils.isIdentical(testRti, TYPE_REF<bool>())) {
13081308
isFn = RAW_DART_FUNCTION_REF(_isBool);
1309+
} else if (_Utils.isIdentical(testRti, TYPE_REF<JSObject>())) {
1310+
isFn = RAW_DART_FUNCTION_REF(_isJSObject);
13091311
}
13101312
return isFn;
13111313
}
@@ -1676,6 +1678,24 @@ String? _asStringQ(dynamic object) {
16761678
throw _TypeError.forType(object, 'String?');
16771679
}
16781680

1681+
bool _isJSObject(Object? object) {
1682+
if (object == null) return false;
1683+
if (JS('bool', 'typeof # == "object"', object)) {
1684+
if (_isDartObject(object)) return false;
1685+
return true;
1686+
}
1687+
if (JS('bool', 'typeof # == "function"', object)) {
1688+
if (JS_GET_FLAG('DEV_COMPILER')) {
1689+
// DDC functions have a signature attached.
1690+
var signatureName = JS_GET_NAME(JsGetName.SIGNATURE_NAME);
1691+
var signature = JS('', '#[#]', object, signatureName);
1692+
return signature == null;
1693+
}
1694+
return true;
1695+
}
1696+
return false;
1697+
}
1698+
16791699
String _rtiArrayToString(Object? array, List<String>? genericContext) {
16801700
String s = '', sep = '';
16811701
for (int i = 0; i < _Utils.arrayLength(array); i++) {
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// Extra checks that JS types work - Dart functions are not JSFunctions.
6+
7+
import 'dart:js_interop';
8+
9+
import 'package:expect/expect.dart';
10+
11+
@JS()
12+
external JSAny? eval(String code);
13+
14+
@pragma('dart2js:never-inline')
15+
@pragma('dart2js:assumeDynamic')
16+
confuse(x) => x;
17+
18+
class Class {
19+
Object method() => this;
20+
static Class staticMethod() => Class();
21+
}
22+
23+
/// Dart functions are not `JSObject`s.
24+
void testJSObject(void functionArgument()) {
25+
void localFunction() {
26+
functionArgument();
27+
}
28+
29+
// Top-level function.
30+
Expect.isFalse(test is JSObject);
31+
Expect.isFalse(confuse(test) is JSObject);
32+
33+
// Static function.
34+
Expect.isFalse(Class.staticMethod is JSObject);
35+
Expect.isFalse(confuse(Class.staticMethod) is JSObject);
36+
37+
Expect.isFalse(functionArgument is JSObject);
38+
Expect.isFalse(confuse(functionArgument) is JSObject);
39+
40+
Expect.isFalse(localFunction is JSObject);
41+
Expect.isFalse(confuse(localFunction) is JSObject);
42+
43+
// Function expression (aka closure)
44+
Expect.isFalse((() => localFunction) is JSObject);
45+
Expect.isFalse(confuse(() => localFunction) is JSObject);
46+
47+
// Instance tear-off
48+
Expect.isFalse(Class().method is JSObject);
49+
Expect.isFalse(confuse(Class().method) is JSObject);
50+
51+
// Top-level function.
52+
Expect.throws<TypeError>(() => test as JSObject);
53+
Expect.throws<TypeError>(() => confuse(test) as JSObject);
54+
55+
// Static function.
56+
Expect.throws<TypeError>(() => Class.staticMethod as JSObject);
57+
Expect.throws<TypeError>(() => confuse(Class.staticMethod) as JSObject);
58+
59+
Expect.throws<TypeError>(() => functionArgument as JSObject);
60+
Expect.throws<TypeError>(() => confuse(functionArgument) as JSObject);
61+
62+
Expect.throws<TypeError>(() => localFunction as JSObject);
63+
Expect.throws<TypeError>(() => confuse(localFunction) as JSObject);
64+
65+
// Function expression (aka closure)
66+
Expect.throws<TypeError>(() => (() => localFunction) as JSObject);
67+
Expect.throws<TypeError>(() => confuse(() => localFunction) as JSObject);
68+
69+
// Instance tear-off
70+
Expect.throws<TypeError>(() => Class().method as JSObject);
71+
Expect.throws<TypeError>(() => confuse(Class().method) as JSObject);
72+
}
73+
74+
/// Dart functions are not `JSFunction`s.
75+
void testJSFunction(void functionArgument()) {
76+
void localFunction() {
77+
functionArgument();
78+
}
79+
80+
// Top-level function.
81+
Expect.isFalse(test is JSFunction);
82+
Expect.isFalse(confuse(test) is JSFunction);
83+
84+
// Static function.
85+
Expect.isFalse(Class.staticMethod is JSFunction);
86+
Expect.isFalse(confuse(Class.staticMethod) is JSFunction);
87+
88+
Expect.isFalse(functionArgument is JSFunction);
89+
Expect.isFalse(confuse(functionArgument) is JSFunction);
90+
91+
Expect.isFalse(localFunction is JSFunction);
92+
Expect.isFalse(confuse(localFunction) is JSFunction);
93+
94+
// Function expression (aka closure)
95+
Expect.isFalse((() => localFunction) is JSFunction);
96+
Expect.isFalse(confuse(() => localFunction) is JSFunction);
97+
98+
// Instance tear-off
99+
Expect.isFalse(Class().method is JSFunction);
100+
Expect.isFalse(confuse(Class().method) is JSFunction);
101+
102+
// Top-level function.
103+
Expect.throws<TypeError>(() => test as JSFunction);
104+
Expect.throws<TypeError>(() => confuse(test) as JSFunction);
105+
106+
// Static function.
107+
Expect.throws<TypeError>(() => Class.staticMethod as JSFunction);
108+
Expect.throws<TypeError>(() => confuse(Class.staticMethod) as JSFunction);
109+
110+
Expect.throws<TypeError>(() => functionArgument as JSFunction);
111+
Expect.throws<TypeError>(() => confuse(functionArgument) as JSFunction);
112+
113+
Expect.throws<TypeError>(() => localFunction as JSFunction);
114+
Expect.throws<TypeError>(() => confuse(localFunction) as JSFunction);
115+
116+
// Function expression (aka closure)
117+
Expect.throws<TypeError>(() => (() => localFunction) as JSFunction);
118+
Expect.throws<TypeError>(() => confuse(() => localFunction) as JSFunction);
119+
120+
// Instance tear-off
121+
Expect.throws<TypeError>(() => Class().method as JSFunction);
122+
Expect.throws<TypeError>(() => confuse(Class().method) as JSFunction);
123+
}
124+
125+
void test(void functionArgument()) {
126+
testJSObject(functionArgument);
127+
testJSFunction(functionArgument);
128+
129+
// Some tests that are in the positive.
130+
final Object? jsFunction = eval('()=>1');
131+
132+
Expect.isTrue(jsFunction is JSObject);
133+
Expect.isTrue(jsFunction is JSFunction);
134+
135+
Expect.isTrue(confuse(jsFunction) is JSObject);
136+
Expect.isTrue(confuse(jsFunction) is JSFunction);
137+
138+
confuse(jsFunction) as JSObject;
139+
confuse(jsFunction) as JSFunction;
140+
141+
jsFunction as JSObject;
142+
jsFunction as JSFunction;
143+
}
144+
145+
void main() {
146+
test(
147+
((x) =>
148+
() => x)(1),
149+
);
150+
test(Class().method);
151+
test(Class.staticMethod);
152+
}

0 commit comments

Comments
 (0)