File
Blob: src/workerd/jsg/rtti-test.c++
| 1 | // Copyright (c) 2017-2022 Cloudflare, Inc. |
| 2 | // Licensed under the Apache 2.0 license found in the LICENSE file or at: |
| 3 | // https://opensource.org/licenses/Apache-2.0 |
| 4 | |
| 5 | #include <workerd/jsg/jsg.h> |
| 6 | #include <workerd/jsg/rtti-test.capnp.h> |
| 7 | #include <workerd/jsg/rtti.h> |
| 8 | #include <workerd/jsg/type-wrapper.h> |
| 9 | |
| 10 | #include <capnp/message.h> |
| 11 | #include <capnp/serialize-text.h> |
| 12 | #include <kj/test.h> |
| 13 | |
| 14 | struct MockConfig {}; |
| 15 | |
| 16 | namespace workerd::jsg::rtti { |
| 17 | namespace { |
| 18 | |
| 19 | template <typename T> |
| 20 | kj::String tType() { |
| 21 | // returns textual encoding of rtti. |
| 22 | Builder<MockConfig> builder((MockConfig())); |
| 23 | auto type = builder.type<T>(); |
| 24 | capnp::TextCodec codec; |
| 25 | return codec.encode(type); |
| 26 | } |
| 27 | |
| 28 | template <typename T> |
| 29 | kj::String tStructure() { |
| 30 | // returns textual encoding of structure. |
| 31 | Builder<MockConfig> builder((MockConfig())); |
| 32 | auto type = builder.structure<T>(); |
| 33 | capnp::TextCodec codec; |
| 34 | return codec.encode(type); |
| 35 | } |
| 36 | |
| 37 | KJ_TEST("jsg::Js* types") { |
| 38 | KJ_EXPECT(tType<JsValue>() == "(unknown = void)"); |
| 39 | KJ_EXPECT(tType<JsObject>() == "(object = void)"); |
| 40 | KJ_EXPECT(tType<JsBoolean>() == "(boolt = void)"); |
| 41 | KJ_EXPECT(tType<JsArray>() == "(array = (element = (unknown = void), name = \"jsg::JsArray\"))"); |
| 42 | KJ_EXPECT(tType<JsString>() == "(string = (name = \"jsg::JsString\"))"); |
| 43 | KJ_EXPECT(tType<JsBigInt>() == "(number = (name = \"jsg::JsBigInt\"))"); |
| 44 | KJ_EXPECT(tType<JsNumber>() == "(number = (name = \"jsg::JsNumber\"))"); |
| 45 | KJ_EXPECT(tType<JsInt32>() == "(number = (name = \"jsg::JsInt32\"))"); |
| 46 | KJ_EXPECT(tType<JsUint32>() == "(number = (name = \"jsg::JsUint32\"))"); |
| 47 | KJ_EXPECT(tType<JsDate>() == "(builtin = (type = kjDate))"); |
| 48 | KJ_EXPECT(tType<JsRegExp>() == "(unknown = void)"); |
| 49 | KJ_EXPECT(tType<JsMap>() == "(unknown = void)"); |
| 50 | KJ_EXPECT(tType<JsSet>() == "(unknown = void)"); |
| 51 | KJ_EXPECT(tType<JsSymbol>() == "(unknown = void)"); |
| 52 | |
| 53 | KJ_EXPECT(tType<JsRef<JsValue>>() == "(unknown = void)"); |
| 54 | KJ_EXPECT(tType<JsRef<JsObject>>() == "(object = void)"); |
| 55 | KJ_EXPECT(tType<JsRef<JsBoolean>>() == "(boolt = void)"); |
| 56 | KJ_EXPECT( |
| 57 | tType<JsRef<JsArray>>() == "(array = (element = (unknown = void), name = \"jsg::JsArray\"))"); |
| 58 | KJ_EXPECT(tType<JsRef<JsString>>() == "(string = (name = \"jsg::JsString\"))"); |
| 59 | KJ_EXPECT(tType<JsRef<JsBigInt>>() == "(number = (name = \"jsg::JsBigInt\"))"); |
| 60 | KJ_EXPECT(tType<JsRef<JsNumber>>() == "(number = (name = \"jsg::JsNumber\"))"); |
| 61 | KJ_EXPECT(tType<JsRef<JsInt32>>() == "(number = (name = \"jsg::JsInt32\"))"); |
| 62 | KJ_EXPECT(tType<JsRef<JsUint32>>() == "(number = (name = \"jsg::JsUint32\"))"); |
| 63 | KJ_EXPECT(tType<JsRef<JsDate>>() == "(builtin = (type = kjDate))"); |
| 64 | KJ_EXPECT(tType<JsRef<JsRegExp>>() == "(unknown = void)"); |
| 65 | KJ_EXPECT(tType<JsRef<JsMap>>() == "(unknown = void)"); |
| 66 | KJ_EXPECT(tType<JsRef<JsSet>>() == "(unknown = void)"); |
| 67 | KJ_EXPECT(tType<JsRef<JsSymbol>>() == "(unknown = void)"); |
| 68 | } |
| 69 | |
| 70 | KJ_TEST("primitive types") { |
| 71 | KJ_EXPECT(tType<void>() == "(voidt = void)"); |
| 72 | KJ_EXPECT(tType<bool>() == "(boolt = void)"); |
| 73 | KJ_EXPECT(tType<v8::Value>() == "(unknown = void)"); |
| 74 | } |
| 75 | |
| 76 | KJ_TEST("number types") { |
| 77 | KJ_EXPECT(tType<char>() == "(number = (name = \"char\"))"); |
| 78 | KJ_EXPECT(tType<signed char>() == "(number = (name = \"signed char\"))"); |
| 79 | KJ_EXPECT(tType<unsigned char>() == "(number = (name = \"unsigned char\"))"); |
| 80 | KJ_EXPECT(tType<short>() == "(number = (name = \"short\"))"); |
| 81 | KJ_EXPECT(tType<unsigned short>() == "(number = (name = \"unsigned short\"))"); |
| 82 | KJ_EXPECT(tType<int>() == "(number = (name = \"int\"))"); |
| 83 | KJ_EXPECT(tType<unsigned int>() == "(number = (name = \"unsigned int\"))"); |
| 84 | KJ_EXPECT(tType<long>() == "(number = (name = \"long\"))"); |
| 85 | KJ_EXPECT(tType<unsigned long>() == "(number = (name = \"unsigned long\"))"); |
| 86 | |
| 87 | KJ_EXPECT(tType<double>() == "(number = (name = \"double\"))"); |
| 88 | } |
| 89 | |
| 90 | KJ_TEST("string types") { |
| 91 | KJ_EXPECT(tType<kj::String>() == "(string = (name = \"kj::String\"))"); |
| 92 | KJ_EXPECT(tType<kj::StringPtr>() == "(string = (name = \"kj::StringPtr\"))"); |
| 93 | KJ_EXPECT(tType<v8::String>() == "(string = (name = \"v8::String\"))"); |
| 94 | KJ_EXPECT(tType<USVString>() == "(string = (name = \"USVString\"))"); |
| 95 | } |
| 96 | |
| 97 | KJ_TEST("object types") { |
| 98 | KJ_EXPECT(tType<v8::Object>() == "(object = void)"); |
| 99 | KJ_EXPECT(tType<jsg::Object>() == "(object = void)"); |
| 100 | } |
| 101 | |
| 102 | KJ_TEST("promises") { |
| 103 | KJ_EXPECT(tType<kj::Promise<void>>() == "(promise = (value = (voidt = void)))"); |
| 104 | KJ_EXPECT(tType<kj::Promise<int>>() == "(promise = (value = (number = (name = \"int\"))))"); |
| 105 | KJ_EXPECT(tType<jsg::Promise<int>>() == "(promise = (value = (number = (name = \"int\"))))"); |
| 106 | KJ_EXPECT(tType<v8::Promise>() == "(promise = (value = (unknown = void)))"); |
| 107 | } |
| 108 | |
| 109 | KJ_TEST("generic types") { |
| 110 | KJ_EXPECT(tType<Ref<v8::Object>>() == "(object = void)"); |
| 111 | KJ_EXPECT(tType<V8Ref<v8::Object>>() == "(object = void)"); |
| 112 | KJ_EXPECT(tType<HashableV8Ref<v8::Object>>() == "(object = void)"); |
| 113 | KJ_EXPECT(tType<v8::Local<v8::Object>>() == "(object = void)"); |
| 114 | KJ_EXPECT(tType<jsg::Identified<v8::Object>>() == "(object = void)"); |
| 115 | KJ_EXPECT(tType<jsg::MemoizedIdentity<v8::Object>>() == "(object = void)"); |
| 116 | KJ_EXPECT(tType<jsg::NonCoercible<kj::String>>() == "(string = (name = \"kj::String\"))"); |
| 117 | |
| 118 | KJ_EXPECT(tType<kj::Array<int>>() == |
| 119 | "(array = (element = (number = (name = \"int\")), name = \"kj::Array\"))"); |
| 120 | KJ_EXPECT(tType<kj::ArrayPtr<int>>() == |
| 121 | "(array = (element = (number = (name = \"int\")), name = \"kj::ArrayPtr\"))"); |
| 122 | KJ_EXPECT(tType<jsg::Sequence<int>>() == |
| 123 | "(array = (element = (number = (name = \"int\")), name = \"jsg::Sequence\"))"); |
| 124 | |
| 125 | KJ_EXPECT(tType<kj::Maybe<int>>() == |
| 126 | "(maybe = (value = (number = (name = \"int\")), name = \"kj::Maybe\"))"); |
| 127 | KJ_EXPECT(tType<jsg::Optional<int>>() == |
| 128 | "(maybe = (value = (number = (name = \"int\")), name = \"jsg::Optional\"))"); |
| 129 | KJ_EXPECT(tType<jsg::LenientOptional<int>>() == |
| 130 | "(maybe = (value = (number = (name = \"int\")), name = \"jsg::LenientOptional\"))"); |
| 131 | |
| 132 | KJ_EXPECT(tType<jsg::Dict<int>>() == |
| 133 | "(dict = (key = (string = (name = \"kj::String\")), value = (number = (name = \"int\"))))"); |
| 134 | KJ_EXPECT((tType<jsg::Dict<int, double>>()) == |
| 135 | "(dict = (key = (number = (name = \"double\")), value = (number = (name = \"int\"))))"); |
| 136 | |
| 137 | KJ_EXPECT((tType<kj::OneOf<int, double>>()) == |
| 138 | "(oneOf = (variants = [" |
| 139 | "(number = (name = \"int\")), " |
| 140 | "(number = (name = \"double\"))]))"); |
| 141 | KJ_EXPECT((tType<kj::OneOf<int, double, kj::String>>()) == |
| 142 | "(oneOf = (variants = [" |
| 143 | "(number = (name = \"int\")), " |
| 144 | "(number = (name = \"double\")), " |
| 145 | "(string = (name = \"kj::String\"))]))"); |
| 146 | } |
| 147 | |
| 148 | KJ_TEST("builtins") { |
| 149 | KJ_EXPECT(tType<jsg::BufferSource>() == "(builtin = (type = jsgBufferSource))"); |
| 150 | KJ_EXPECT(tType<v8::Uint8Array>() == "(builtin = (type = v8Uint8Array))"); |
| 151 | KJ_EXPECT(tType<v8::ArrayBufferView>() == "(builtin = (type = v8ArrayBufferView))"); |
| 152 | KJ_EXPECT(tType<v8::Function>() == "(builtin = (type = v8Function))"); |
| 153 | KJ_EXPECT(tType<kj::Date>() == "(builtin = (type = kjDate))"); |
| 154 | } |
| 155 | |
| 156 | KJ_TEST("jsgImpl") { |
| 157 | KJ_EXPECT(tType<jsg::Lock>() == "(jsgImpl = (type = jsgLock))"); |
| 158 | KJ_EXPECT(tType<jsg::SelfRef>() == "(jsgImpl = (type = jsgSelfRef))"); |
| 159 | KJ_EXPECT(tType<jsg::Unimplemented>() == "(jsgImpl = (type = jsgUnimplemented))"); |
| 160 | KJ_EXPECT(tType<v8::Isolate*>() == "(jsgImpl = (type = v8Isolate))"); |
| 161 | KJ_EXPECT(tType<MockConfig>() == "(jsgImpl = (type = configuration))"); |
| 162 | KJ_EXPECT(tType<jsg::TypeHandler<kj::Date>>() == "(jsgImpl = (type = jsgTypeHandler))"); |
| 163 | KJ_EXPECT(tType<v8::FunctionCallbackInfo<v8::Value>>() == |
| 164 | "(jsgImpl = (type = v8FunctionCallbackInfo))"); |
| 165 | KJ_EXPECT(tType<v8::PropertyCallbackInfo<v8::Value>>() == |
| 166 | "(jsgImpl = (type = v8PropertyCallbackInfo))"); |
| 167 | } |
| 168 | |
| 169 | KJ_TEST("functions") { |
| 170 | KJ_EXPECT(tType<jsg::Function<int()>>() == |
| 171 | "(function = (returnType = (number = (name = \"int\")), args = []))"); |
| 172 | KJ_EXPECT(tType<jsg::Function<void(int a, double b)>>() == |
| 173 | "(function = (returnType = (voidt = void), args = [(number = (name = \"int\")), (number = (name = \"double\"))]))"); |
| 174 | } |
| 175 | |
| 176 | KJ_TEST("c++ modifiers") { |
| 177 | KJ_EXPECT(tType<const int>() == "(number = (name = \"int\"))"); |
| 178 | KJ_EXPECT(tType<int&>() == "(number = (name = \"int\"))"); |
| 179 | KJ_EXPECT(tType<int&&>() == "(number = (name = \"int\"))"); |
| 180 | KJ_EXPECT(tType<const int&>() == "(number = (name = \"int\"))"); |
| 181 | } |
| 182 | |
| 183 | struct Base: public Object { |
| 184 | JSG_RESOURCE_TYPE(Base) { |
| 185 | JSG_INHERIT_INTRINSIC(v8::kIteratorPrototype); |
| 186 | } |
| 187 | }; |
| 188 | |
| 189 | struct TestResource: public Base { |
| 190 | void instanceMethod(int i, double f) {} |
| 191 | static int staticMethod() { |
| 192 | return 42; |
| 193 | } |
| 194 | |
| 195 | int getSize() { |
| 196 | return 1; |
| 197 | } |
| 198 | void setSize(int size) {} |
| 199 | |
| 200 | static jsg::Ref<TestResource> constructor(jsg::Optional<kj::String> label); |
| 201 | |
| 202 | JSG_RESOURCE_TYPE(TestResource) { |
| 203 | JSG_INHERIT(Base); |
| 204 | |
| 205 | JSG_METHOD(instanceMethod); |
| 206 | JSG_STATIC_METHOD(staticMethod); |
| 207 | JSG_INSTANCE_PROPERTY(size, getSize, setSize); |
| 208 | JSG_READONLY_INSTANCE_PROPERTY(readonlySize, getSize); |
| 209 | JSG_LAZY_INSTANCE_PROPERTY(lazySize, getSize); |
| 210 | JSG_LAZY_READONLY_INSTANCE_PROPERTY(lazyReadonlySize, getSize); |
| 211 | JSG_PROTOTYPE_PROPERTY(protoSize, getSize, setSize); |
| 212 | JSG_READONLY_PROTOTYPE_PROPERTY(protoReadonlySize, getSize); |
| 213 | } |
| 214 | }; |
| 215 | |
| 216 | KJ_TEST("resource reference") { |
| 217 | KJ_EXPECT(tType<TestResource>() == |
| 218 | "(structure = (name = \"TestResource\", fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::TestResource\"))"); |
| 219 | } |
| 220 | |
| 221 | KJ_TEST("resource structure") { |
| 222 | KJ_EXPECT(tStructure<Base>() == |
| 223 | "(name = \"Base\", members = [], " |
| 224 | "extends = (intrinsic = (name = \"v8::kIteratorPrototype\")), " |
| 225 | "iterable = false, asyncIterable = false, " |
| 226 | "fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::Base\", tsRoot = false, " |
| 227 | "disposable = false, asyncDisposable = false)"); |
| 228 | |
| 229 | KJ_EXPECT(tStructure<TestResource>() == |
| 230 | "(name = \"TestResource\", members = [" |
| 231 | "(method = (name = \"instanceMethod\", returnType = (voidt = void), args = [(number = (name = \"int\")), (number = (name = \"double\"))], static = false, fastApiCompatible = true)), " |
| 232 | "(method = (name = \"staticMethod\", returnType = (number = (name = \"int\")), args = [], static = true, fastApiCompatible = true)), " |
| 233 | "(property = (name = \"size\", type = (number = (name = \"int\")), readonly = false, lazy = false, prototype = false, getterFastApiCompatible = true, setterFastApiCompatible = true)), " |
| 234 | "(property = (name = \"readonlySize\", type = (number = (name = \"int\")), readonly = true, lazy = false, prototype = false, getterFastApiCompatible = true, setterFastApiCompatible = false)), " |
| 235 | "(property = (name = \"lazySize\", type = (number = (name = \"int\")), readonly = false, lazy = true, prototype = false, getterFastApiCompatible = false, setterFastApiCompatible = false)), " |
| 236 | "(property = (name = \"lazyReadonlySize\", type = (number = (name = \"int\")), readonly = true, lazy = true, prototype = false, getterFastApiCompatible = false, setterFastApiCompatible = false)), " |
| 237 | "(property = (name = \"protoSize\", type = (number = (name = \"int\")), readonly = false, lazy = false, prototype = true, getterFastApiCompatible = true, setterFastApiCompatible = true)), " |
| 238 | "(property = (name = \"protoReadonlySize\", type = (number = (name = \"int\")), readonly = true, lazy = false, prototype = true, getterFastApiCompatible = false, setterFastApiCompatible = false)), " |
| 239 | "(constructor = (args = [(maybe = (value = (string = (name = \"kj::String\")), name = \"jsg::Optional\"))]))], " |
| 240 | "extends = (structure = (name = \"Base\", fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::Base\")), " |
| 241 | "iterable = false, asyncIterable = false, " |
| 242 | "fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::TestResource\", tsRoot = false, " |
| 243 | "disposable = false, asyncDisposable = false)"); |
| 244 | } |
| 245 | |
| 246 | struct TestNested: jsg::Object { |
| 247 | JSG_RESOURCE_TYPE(TestNested) { |
| 248 | JSG_NESTED_TYPE(Base); |
| 249 | }; |
| 250 | }; |
| 251 | |
| 252 | KJ_TEST("nested structure") { |
| 253 | KJ_EXPECT(tStructure<TestNested>() == |
| 254 | "(name = \"TestNested\", members = [(" |
| 255 | "nested = (" |
| 256 | "structure = (" |
| 257 | "name = \"Base\", members = [], " |
| 258 | "extends = (intrinsic = (name = \"v8::kIteratorPrototype\")), " |
| 259 | "iterable = false, asyncIterable = false, " |
| 260 | "fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::Base\", " |
| 261 | "tsRoot = false, disposable = false, asyncDisposable = false" |
| 262 | "), " |
| 263 | "name = \"Base\"))" |
| 264 | "], " |
| 265 | "iterable = false, asyncIterable = false, " |
| 266 | "fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::TestNested\", tsRoot = false, " |
| 267 | "disposable = false, asyncDisposable = false)"); |
| 268 | } |
| 269 | |
| 270 | struct TestConstant: jsg::Object { |
| 271 | static constexpr int ENABLED [[maybe_unused]] = 1; |
| 272 | |
| 273 | enum Type { |
| 274 | CIRCLE = 2, |
| 275 | }; |
| 276 | |
| 277 | JSG_RESOURCE_TYPE(TestConstant) { |
| 278 | JSG_STATIC_CONSTANT(ENABLED); |
| 279 | JSG_STATIC_CONSTANT(CIRCLE); |
| 280 | }; |
| 281 | }; |
| 282 | |
| 283 | KJ_TEST("constant members") { |
| 284 | KJ_EXPECT(tStructure<TestConstant>() == |
| 285 | "(name = \"TestConstant\", members = [" |
| 286 | "(constant = (name = \"ENABLED\", value = 1)), " |
| 287 | "(constant = (name = \"CIRCLE\", value = 2))], " |
| 288 | "iterable = false, asyncIterable = false, " |
| 289 | "fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::TestConstant\", " |
| 290 | "tsRoot = false, disposable = false, asyncDisposable = false)"); |
| 291 | } |
| 292 | |
| 293 | struct TestStruct { |
| 294 | int a; |
| 295 | bool b; |
| 296 | JSG_STRUCT(a, b); |
| 297 | }; |
| 298 | |
| 299 | KJ_TEST("struct reference") { |
| 300 | KJ_EXPECT(tType<TestStruct>() == |
| 301 | "(structure = (name = \"TestStruct\", fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::TestStruct\"))"); |
| 302 | } |
| 303 | |
| 304 | KJ_TEST("struct structure") { |
| 305 | KJ_EXPECT(tStructure<TestStruct>() == |
| 306 | "(name = \"TestStruct\", members = [" |
| 307 | "(property = (name = \"a\", type = (number = (name = \"int\")), readonly = false, lazy = false, prototype = false, getterFastApiCompatible = false, setterFastApiCompatible = false)), " |
| 308 | "(property = (name = \"b\", type = (boolt = void), readonly = false, lazy = false, prototype = false, getterFastApiCompatible = false, setterFastApiCompatible = false))], " |
| 309 | "iterable = false, asyncIterable = false, " |
| 310 | "fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::TestStruct\", " |
| 311 | "tsRoot = false, disposable = false, asyncDisposable = false)"); |
| 312 | } |
| 313 | |
| 314 | struct TestSymbolTable: public jsg::Object { |
| 315 | void acceptResource(const TestResource& resource) {}; |
| 316 | void recursiveTypeFunction(const TestSymbolTable& table) {} |
| 317 | |
| 318 | JSG_RESOURCE_TYPE(TestSymbolTable) { |
| 319 | JSG_METHOD(acceptResource); |
| 320 | JSG_METHOD(recursiveTypeFunction); |
| 321 | }; |
| 322 | }; |
| 323 | |
| 324 | KJ_TEST("symbol table") { |
| 325 | Builder<MockConfig> builder((MockConfig())); |
| 326 | auto type = builder.structure<TestSymbolTable>(); |
| 327 | capnp::TextCodec codec; |
| 328 | |
| 329 | KJ_EXPECT(codec.encode(type) == |
| 330 | "(name = \"TestSymbolTable\", members = [" |
| 331 | "(method = (name = \"acceptResource\", returnType = (voidt = void), args = [(structure = (name = \"TestResource\", fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::TestResource\"))], static = false, fastApiCompatible = true)), " |
| 332 | "(method = (name = \"recursiveTypeFunction\", returnType = (voidt = void), args = [(structure = (name = \"TestSymbolTable\", fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::TestSymbolTable\"))], static = false, fastApiCompatible = true))], " |
| 333 | "iterable = false, asyncIterable = false, " |
| 334 | "fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::TestSymbolTable\", " |
| 335 | "tsRoot = false, disposable = false, asyncDisposable = false)"); |
| 336 | |
| 337 | KJ_EXPECT(builder.structure("workerd::jsg::rtti::(anonymous namespace)::TestSymbolTable"_kj) != |
| 338 | kj::none); |
| 339 | KJ_EXPECT( |
| 340 | builder.structure("workerd::jsg::rtti::(anonymous namespace)::TestResource"_kj) != kj::none); |
| 341 | KJ_EXPECT(KJ_REQUIRE_NONNULL( |
| 342 | builder.structure("workerd::jsg::rtti::(anonymous namespace)::TestResource"_kj)) |
| 343 | .getMembers() |
| 344 | .size() > 0); |
| 345 | } |
| 346 | |
| 347 | struct TestTypeScriptResourceType: public jsg::Object { |
| 348 | int getThing() { |
| 349 | return 42; |
| 350 | } |
| 351 | |
| 352 | JSG_RESOURCE_TYPE(TestTypeScriptResourceType) { |
| 353 | JSG_READONLY_INSTANCE_PROPERTY(thing, getThing); |
| 354 | |
| 355 | JSG_TS_ROOT(); |
| 356 | JSG_TS_DEFINE(interface Define {}); |
| 357 | JSG_TS_OVERRIDE({ readonly thing: 42 }); |
| 358 | }; |
| 359 | }; |
| 360 | |
| 361 | struct TestTypeScriptStruct { |
| 362 | int structThing; |
| 363 | JSG_STRUCT(structThing); |
| 364 | |
| 365 | JSG_STRUCT_TS_ROOT(); |
| 366 | JSG_STRUCT_TS_DEFINE(interface StructDefine {}); |
| 367 | JSG_STRUCT_TS_OVERRIDE(RenamedStructThing { structThing: 42 }); |
| 368 | }; |
| 369 | |
| 370 | struct TestTypeScriptStdioResourceType: public jsg::Object { |
| 371 | int getThing() { |
| 372 | return 42; |
| 373 | } |
| 374 | |
| 375 | JSG_RESOURCE_TYPE(TestTypeScriptStdioResourceType) { |
| 376 | JSG_READONLY_INSTANCE_PROPERTY(thing, getThing); |
| 377 | |
| 378 | JSG_TS_ROOT(); |
| 379 | JSG_TS_DEFINE(interface Define { stdin: 0; stdout: 1; stderr: 2 }); |
| 380 | JSG_TS_OVERRIDE({ stdin: 0; stdout: 1; stderr: 2 }); |
| 381 | }; |
| 382 | }; |
| 383 | |
| 384 | struct TestTypeScriptStdioStruct { |
| 385 | int structThing; |
| 386 | JSG_STRUCT(structThing); |
| 387 | |
| 388 | JSG_STRUCT_TS_ROOT(); |
| 389 | JSG_STRUCT_TS_DEFINE(interface StructDefine { stdin: 0; stdout: 1; stderr: 2 }); |
| 390 | JSG_STRUCT_TS_OVERRIDE(Renamed { stdin: 0; stdout: 1; stderr: 2 }); |
| 391 | }; |
| 392 | |
| 393 | KJ_TEST("typescript macros") { |
| 394 | KJ_EXPECT(tStructure<TestTypeScriptResourceType>() == |
| 395 | "(name = \"TestTypeScriptResourceType\", members = [" |
| 396 | "(property = (name = \"thing\", type = (number = (name = \"int\")), readonly = true, lazy = false, prototype = false, getterFastApiCompatible = true, setterFastApiCompatible = false))], " |
| 397 | "iterable = false, asyncIterable = false, " |
| 398 | "fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::TestTypeScriptResourceType\", " |
| 399 | "tsRoot = true, " |
| 400 | "tsOverride = \"{ readonly thing: 42 }\", " |
| 401 | "tsDefine = \"interface Define {}\", " |
| 402 | "disposable = false, asyncDisposable = false)"); |
| 403 | KJ_EXPECT(tStructure<TestTypeScriptStruct>() == |
| 404 | "(name = \"TestTypeScriptStruct\", members = [" |
| 405 | "(property = (name = \"structThing\", type = (number = (name = \"int\")), readonly = false, lazy = false, prototype = false, getterFastApiCompatible = false, setterFastApiCompatible = false))], " |
| 406 | "iterable = false, asyncIterable = false, " |
| 407 | "fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::TestTypeScriptStruct\", " |
| 408 | "tsRoot = true, " |
| 409 | "tsOverride = \"RenamedStructThing { structThing: 42 }\", " |
| 410 | "tsDefine = \"interface StructDefine {}\", " |
| 411 | "disposable = false, asyncDisposable = false)"); |
| 412 | } |
| 413 | |
| 414 | KJ_TEST("typescript macros with stdio identifiers") { |
| 415 | // `<stdio.h>` on Darwin defines `stdin`/`stdout`/`stderr` as macros. The TS override and define |
| 416 | // macros must capture the user's source verbatim so these tokens are not expanded. |
| 417 | KJ_EXPECT(tStructure<TestTypeScriptStdioResourceType>() == |
| 418 | "(name = \"TestTypeScriptStdioResourceType\", members = [" |
| 419 | "(property = (name = \"thing\", type = (number = (name = \"int\")), readonly = true, lazy = false, prototype = false, getterFastApiCompatible = true, setterFastApiCompatible = false))], " |
| 420 | "iterable = false, asyncIterable = false, " |
| 421 | "fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::TestTypeScriptStdioResourceType\", " |
| 422 | "tsRoot = true, " |
| 423 | "tsOverride = \"{ stdin: 0; stdout: 1; stderr: 2 }\", " |
| 424 | "tsDefine = \"interface Define { stdin: 0; stdout: 1; stderr: 2 }\", " |
| 425 | "disposable = false, asyncDisposable = false)"); |
| 426 | KJ_EXPECT(tStructure<TestTypeScriptStdioStruct>() == |
| 427 | "(name = \"TestTypeScriptStdioStruct\", members = [" |
| 428 | "(property = (name = \"structThing\", type = (number = (name = \"int\")), readonly = false, lazy = false, prototype = false, getterFastApiCompatible = false, setterFastApiCompatible = false))], " |
| 429 | "iterable = false, asyncIterable = false, " |
| 430 | "fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::TestTypeScriptStdioStruct\", " |
| 431 | "tsRoot = true, " |
| 432 | "tsOverride = \"Renamed { stdin: 0; stdout: 1; stderr: 2 }\", " |
| 433 | "tsDefine = \"interface StructDefine { stdin: 0; stdout: 1; stderr: 2 }\", " |
| 434 | "disposable = false, asyncDisposable = false)"); |
| 435 | } |
| 436 | |
| 437 | } // namespace |
| 438 | } // namespace workerd::jsg::rtti |