Skip to content
File

Blob: src/workerd/jsg/value-test.c++

49.0 KB
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 "jsg-test.h"
6 
7namespace workerd::jsg::test {
8namespace {
9 
10V8System v8System;
11class ContextGlobalObject: public Object, public ContextGlobal {};
12 
13struct BoolContext: public ContextGlobalObject {
14 kj::String takeBool(bool b) {
15 return kj::str(b);
16 }
17 JSG_RESOURCE_TYPE(BoolContext) {
18 JSG_METHOD(takeBool);
19 }
20};
21JSG_DECLARE_ISOLATE_TYPE(BoolIsolate, BoolContext);
22 
23KJ_TEST("bool") {
24 Evaluator<BoolContext, BoolIsolate> e(v8System);
25 e.expectEval("takeBool(false)", "string", "false");
26 e.expectEval("takeBool(true)", "string", "true");
27 e.expectEval("takeBool(123)", "string", "true");
28 e.expectEval("takeBool({})", "string", "true");
29 e.expectEval("takeBool('')", "string", "false");
30 e.expectEval("takeBool('false')", "string", "true");
31 e.expectEval("takeBool(null)", "string", "false");
32 e.expectEval("takeBool(undefined)", "string", "false");
33 e.expectEval("takeBool()", "throws",
34 "TypeError: Failed to execute 'takeBool' on 'BoolContext': parameter 1 is "
35 "not of type 'boolean'.");
36}
37 
38// ========================================================================================
39 
40struct OptionalContext: public ContextGlobalObject {
41 struct TestOptionalFields {
42 Optional<kj::String> optional;
43 LenientOptional<kj::String> lenient;
44 kj::Maybe<kj::String> nullable;
45 
46 JSG_STRUCT(optional, lenient, nullable);
47 };
48 
49 struct TestAllOptionalFields {
50 Optional<kj::String> optString;
51 Optional<double> optDouble;
52 
53 JSG_STRUCT(optString, optDouble);
54 };
55 
56 double takeOptional(jsg::Lock& js, Optional<Ref<NumberBox>> num) {
57 return kj::mv(num).orDefault(js.alloc<NumberBox>(321))->value;
58 }
59 double takeMaybe(jsg::Lock& js, kj::Maybe<Ref<NumberBox>> num) {
60 return kj::mv(num).orDefault(js.alloc<NumberBox>(321))->value;
61 }
62 double takeLenientOptional(jsg::Lock& js, LenientOptional<Ref<NumberBox>> num) {
63 return kj::mv(num).orDefault(js.alloc<NumberBox>(321))->value;
64 }
65 kj::String takeOptionalMaybe(Optional<kj::Maybe<kj::String>> arg) {
66 return kj::mv(arg).orDefault(kj::str("(absent)")).orDefault(kj::str("(null)"));
67 }
68 Optional<Ref<NumberBox>> returnOptional(jsg::Lock& js, double value) {
69 if (value == 321)
70 return kj::none;
71 else
72 return js.alloc<NumberBox>(value);
73 }
74 kj::Maybe<Ref<NumberBox>> returnMaybe(jsg::Lock& js, double value) {
75 if (value == 321)
76 return kj::none;
77 else
78 return js.alloc<NumberBox>(value);
79 }
80 
81 kj::String readTestOptionalFields(TestOptionalFields s) {
82 return kj::str(kj::mv(s.optional).orDefault(kj::str("(absent)")), ", ",
83 kj::mv(s.lenient).orDefault(kj::str("(absent)")), ", ",
84 kj::mv(s.nullable).orDefault(kj::str("(absent)")));
85 }
86 TestOptionalFields makeTestOptionalFields(Optional<kj::String> optional,
87 LenientOptional<kj::String> lenient,
88 kj::Maybe<kj::String> nullable) {
89 return {kj::mv(optional), kj::mv(lenient), kj::mv(nullable)};
90 }
91 
92 kj::String readTestAllOptionalFields(TestAllOptionalFields s) {
93 return kj::str(kj::mv(s.optString).orDefault(kj::str("(absent)")), ", ",
94 kj::mv(s.optDouble).orDefault(321));
95 }
96 
97 JSG_RESOURCE_TYPE(OptionalContext) {
98 JSG_NESTED_TYPE(NumberBox);
99 JSG_METHOD(takeOptional);
100 JSG_METHOD(takeMaybe);
101 JSG_METHOD(takeLenientOptional);
102 JSG_METHOD(takeOptionalMaybe);
103 JSG_METHOD(returnOptional);
104 JSG_METHOD(returnMaybe);
105 JSG_METHOD(readTestOptionalFields);
106 JSG_METHOD(makeTestOptionalFields);
107 JSG_METHOD(readTestAllOptionalFields);
108 }
109};
110JSG_DECLARE_ISOLATE_TYPE(OptionalIsolate,
111 OptionalContext,
112 OptionalContext::TestOptionalFields,
113 OptionalContext::TestAllOptionalFields,
114 NumberBox);
115 
116KJ_TEST("optionals and maybes") {
117 Evaluator<OptionalContext, OptionalIsolate> e(v8System);
118 e.getIsolate().setUsingFastJsgStruct();
119 e.expectEval("takeOptional(new NumberBox(123))", "number", "123");
120 e.expectEval("takeOptional()", "number", "321");
121 e.expectEval("takeOptional(undefined)", "number", "321");
122 e.expectEval("returnOptional(123).value", "number", "123");
123 e.expectEval("returnOptional(321)", "undefined", "undefined");
124 
125 e.expectEval("takeMaybe(new NumberBox(123))", "number", "123");
126 e.expectEval("takeMaybe(null)", "number", "321");
127 e.expectEval("takeMaybe(undefined)", "number", "321");
128 e.expectEval("returnMaybe(123).value", "number", "123");
129 e.expectEval("returnMaybe(321)", "object", "null");
130 
131 e.expectEval("takeMaybe()", "throws",
132 "TypeError: Failed to execute 'takeMaybe' on 'OptionalContext': parameter 1 is not "
133 "of type 'NumberBox'.");
134 e.expectEval("takeOptional(null)", "throws",
135 "TypeError: Failed to execute 'takeOptional' on 'OptionalContext': parameter 1 is not "
136 "of type 'NumberBox'.");
137 
138 e.expectEval("takeLenientOptional(new NumberBox(123))", "number", "123");
139 e.expectEval("takeLenientOptional()", "number", "321");
140 e.expectEval("takeLenientOptional(undefined)", "number", "321");
141 e.expectEval("takeLenientOptional(null)", "number", "321");
142 e.expectEval("takeLenientOptional((foo) => {})", "number", "321");
143 
144 e.expectEval("takeOptionalMaybe()", "string", "(absent)");
145 e.expectEval("takeOptionalMaybe(null)", "string", "(null)");
146 e.expectEval("takeOptionalMaybe(undefined)", "string", "(absent)");
147 e.expectEval("takeOptionalMaybe('a string')", "string", "a string");
148 
149 e.expectEval(
150 "readTestOptionalFields({nullable: null})", "string", "(absent), (absent), (absent)");
151 e.expectEval("readTestOptionalFields({optional: 'foo', lenient: 'bar', nullable: null})",
152 "string", "foo, bar, (absent)");
153 e.expectEval("readTestOptionalFields({optional: 'foo', lenient: 'bar', nullable: 'baz'})",
154 "string", "foo, bar, baz");
155 
156#define ENUMERATE_OBJECT \
157 "var items = [];\n" \
158 "for (var key in object) {\n" \
159 " items.push(key + ': ' + object[key]);\n" \
160 "}\n" \
161 "items.join(', ')"
162 
163 e.expectEval(
164 "var object = makeTestOptionalFields(undefined, undefined, null);\n" ENUMERATE_OBJECT,
165 "string", "optional: undefined, lenient: undefined, nullable: null");
166 e.expectEval("var object = makeTestOptionalFields('foo', 'bar', null);\n" ENUMERATE_OBJECT,
167 "string", "optional: foo, lenient: bar, nullable: null");
168 e.expectEval("var object = makeTestOptionalFields('foo', 'bar', 'baz');\n" ENUMERATE_OBJECT,
169 "string", "optional: foo, lenient: bar, nullable: baz");
170 e.expectEval(
171 "var object = makeTestOptionalFields(undefined, undefined, 'bar');\n" ENUMERATE_OBJECT,
172 "string", "optional: undefined, lenient: undefined, nullable: bar");
173#undef ENUMERATE_OBJECT
174 
175 e.expectEval("readTestAllOptionalFields({})", "string", "(absent), 321");
176 e.expectEval("readTestAllOptionalFields(null)", "string", "(absent), 321");
177 e.expectEval("readTestAllOptionalFields(undefined)", "string", "(absent), 321");
178 e.expectEval("readTestAllOptionalFields()", "throws",
179 "TypeError: Failed to execute 'readTestAllOptionalFields' on 'OptionalContext': "
180 "parameter 1 is not of type 'TestAllOptionalFields'.");
181}
182 
183// ========================================================================================
184struct MaybeContext: public ContextGlobalObject {
185 
186 void test(kj::Maybe<kj::OneOf<NonCoercible<kj::String>>> arg) {}
187 
188 JSG_RESOURCE_TYPE(MaybeContext) {
189 JSG_METHOD(test);
190 }
191};
192JSG_DECLARE_ISOLATE_TYPE(MaybeIsolate, MaybeContext);
193 
194KJ_TEST("maybes - don't substitute null") {
195 
196 static const auto config = JsgConfig{
197 .noSubstituteNull = true,
198 };
199 
200 struct MaybeConfig {
201 operator const JsgConfig&() const {
202 return config;
203 }
204 };
205 
206 // This version uses the MaybeConfig above that sets noSubstituteNull = true.
207 Evaluator<MaybeContext, MaybeIsolate, MaybeConfig> e(v8System);
208 e.expectEval("test({})", "throws",
209 "TypeError: Failed to execute 'test' on 'MaybeContext': parameter 1 is not "
210 "of type 'string'.");
211 
212 // This version uses the default JsgConfig with the noSubstituteNull = false.
213 Evaluator<MaybeContext, MaybeIsolate, JsgConfig> e2(v8System);
214 e2.expectEval("test({})", "undefined", "undefined");
215}
216 
217// ========================================================================================
218 
219struct OneOfContext: public ContextGlobalObject {
220 kj::String takeOneOf(kj::OneOf<double, kj::String, Ref<NumberBox>> value) {
221 if (value.is<double>()) {
222 return kj::str("double: ", value.get<double>());
223 } else if (value.is<kj::String>()) {
224 return kj::str("kj::String: ", value.get<kj::String>());
225 } else if (value.is<Ref<NumberBox>>()) {
226 return kj::str("NumberBox: ", value.get<Ref<NumberBox>>()->value);
227 } else {
228 return kj::str("none of the above -- can't get here");
229 }
230 }
231 kj::OneOf<double, kj::String, Ref<NumberBox>> returnOneOf(
232 kj::Maybe<double> num, kj::Maybe<kj::String> str, kj::Maybe<Ref<NumberBox>> box) {
233 kj::OneOf<double, kj::String, Ref<NumberBox>> result;
234 KJ_IF_SOME(n, num) {
235 result.init<double>(n);
236 } else KJ_IF_SOME(s, str) {
237 result.init<kj::String>(kj::mv(s));
238 } else KJ_IF_SOME(b, box) {
239 result.init<Ref<NumberBox>>(b.addRef());
240 }
241 return result;
242 }
243 
244 using StringOrBool = kj::OneOf<kj::String, bool>;
245 using NumberOrBool = kj::OneOf<double, bool>;
246 using StringOrNumber = kj::OneOf<kj::String, double>;
247 
248 kj::String takeStringOrBool(StringOrBool value) {
249 KJ_SWITCH_ONEOF(value) {
250 KJ_CASE_ONEOF(s, kj::String) {
251 return kj::str("kj::String: ", s);
252 }
253 KJ_CASE_ONEOF(b, bool) {
254 return kj::str("bool: ", b);
255 }
256 }
257 KJ_UNREACHABLE;
258 }
259 kj::String takeNumberOrBool(NumberOrBool value) {
260 KJ_SWITCH_ONEOF(value) {
261 KJ_CASE_ONEOF(d, double) {
262 return kj::str("double: ", d);
263 }
264 KJ_CASE_ONEOF(b, bool) {
265 return kj::str("bool: ", b);
266 }
267 }
268 KJ_UNREACHABLE;
269 }
270 kj::String takeStringOrNumber(StringOrNumber value) {
271 KJ_SWITCH_ONEOF(value) {
272 KJ_CASE_ONEOF(s, kj::String) {
273 return kj::str("kj::String: ", s);
274 }
275 KJ_CASE_ONEOF(d, double) {
276 return kj::str("double: ", d);
277 }
278 }
279 KJ_UNREACHABLE;
280 }
281 
282 using NestedOneOf = kj::OneOf<double, StringOrBool>;
283 
284 kj::String takeNestedOneOf(NestedOneOf value) {
285 KJ_SWITCH_ONEOF(value) {
286 KJ_CASE_ONEOF(d, double) {
287 return kj::str("double: ", d);
288 }
289 KJ_CASE_ONEOF(oof, kj::OneOf<kj::String, bool>) {
290 KJ_SWITCH_ONEOF(oof) {
291 KJ_CASE_ONEOF(s, kj::String) {
292 return kj::str("kj::String: ", s);
293 }
294 KJ_CASE_ONEOF(b, bool) {
295 return kj::str("bool: ", b);
296 }
297 }
298 }
299 }
300 KJ_UNREACHABLE;
301 }
302 
303 JSG_RESOURCE_TYPE(OneOfContext) {
304 JSG_NESTED_TYPE(NumberBox);
305 JSG_METHOD(takeOneOf);
306 JSG_METHOD(returnOneOf);
307 JSG_METHOD(takeStringOrBool);
308 JSG_METHOD(takeNumberOrBool);
309 JSG_METHOD(takeStringOrNumber);
310 JSG_METHOD(takeNestedOneOf);
311 }
312};
313JSG_DECLARE_ISOLATE_TYPE(OneOfIsolate, OneOfContext, NumberBox);
314 
315KJ_TEST("OneOf") {
316 Evaluator<OneOfContext, OneOfIsolate> e(v8System);
317 e.expectEval("takeOneOf(123)", "string", "double: 123");
318 e.expectEval("takeOneOf('foo')", "string", "kj::String: foo");
319 e.expectEval("takeOneOf(new NumberBox(321))", "string", "NumberBox: 321");
320 e.expectEval("takeOneOf(undefined)", "string", "kj::String: undefined");
321 
322 e.expectEval("returnOneOf(123, null, null)", "number", "123");
323 e.expectEval("returnOneOf(null, 'foo', null)", "string", "foo");
324 e.expectEval("returnOneOf(null, null, new NumberBox(321)).value", "number", "321");
325 e.expectEval("returnOneOf(null, null, null)", "undefined", "undefined");
326 
327 e.expectEval("takeStringOrBool(123)", "string", "kj::String: 123");
328 e.expectEval("takeStringOrBool('123')", "string", "kj::String: 123");
329 e.expectEval("takeStringOrBool(true)", "string", "bool: true");
330 
331 e.expectEval("takeNumberOrBool(123)", "string", "double: 123");
332 e.expectEval("takeNumberOrBool('123')", "string", "double: 123");
333 e.expectEval("takeNumberOrBool(true)", "string", "bool: true");
334 
335 e.expectEval("takeStringOrNumber(123)", "string", "double: 123");
336 e.expectEval("takeStringOrNumber('123')", "string", "kj::String: 123");
337 e.expectEval("takeStringOrNumber(true)", "string", "kj::String: true");
338 
339 e.expectEval("takeNestedOneOf(123)", "string", "double: 123");
340 e.expectEval("takeNestedOneOf('123')", "string", "kj::String: 123");
341 e.expectEval("takeNestedOneOf(true)", "string", "bool: true");
342 e.expectEval("takeNestedOneOf(undefined)", "string", "kj::String: undefined");
343 e.expectEval("takeNestedOneOf(null)", "string", "kj::String: null");
344 e.expectEval("takeNestedOneOf({})", "string", "kj::String: [object Object]");
345}
346 
347// ========================================================================================
348 
349struct DictContext: public ContextGlobalObject {
350 kj::String takeDict(Dict<Ref<NumberBox>> dict) {
351 return kj::strArray(
352 KJ_MAP(f, dict.fields) { return kj::str(f.name, ": ", f.value->value); }, ", ");
353 }
354 kj::String takeDictOfFunctions(Lock& js, Dict<Function<int()>> dict) {
355 return kj::strArray(
356 KJ_MAP(f, dict.fields) { return kj::str(f.name, ": ", f.value(js)); }, ", ");
357 }
358 Dict<Ref<NumberBox>> returnDict(jsg::Lock& js) {
359 auto builder = kj::heapArrayBuilder<Dict<Ref<NumberBox>>::Field>(3);
360 builder.add(Dict<Ref<NumberBox>>::Field{kj::str("foo"), js.alloc<NumberBox>(123)});
361 builder.add(Dict<Ref<NumberBox>>::Field{kj::str("bar"), js.alloc<NumberBox>(456)});
362 builder.add(Dict<Ref<NumberBox>>::Field{kj::str("baz"), js.alloc<NumberBox>(789)});
363 return {builder.finish()};
364 }
365 
366 JSG_RESOURCE_TYPE(DictContext) {
367 JSG_NESTED_TYPE(NumberBox);
368 JSG_METHOD(takeDict);
369 JSG_METHOD(takeDictOfFunctions);
370 JSG_METHOD(returnDict);
371 }
372};
373JSG_DECLARE_ISOLATE_TYPE(DictIsolate, DictContext, NumberBox);
374 
375KJ_TEST("dicts") {
376 Evaluator<DictContext, DictIsolate> e(v8System);
377 e.expectEval(
378 "takeDict({foo: new NumberBox(123), bar: new NumberBox(456), baz: new NumberBox(789)})",
379 "string", "foo: 123, bar: 456, baz: 789");
380 e.expectEval("var dict = returnDict();\n"
381 "[dict.foo.value, dict.bar.value, dict.baz.value].join(', ')",
382 "string", "123, 456, 789");
383 
384 e.expectEval("takeDict({foo: new NumberBox(123), bar: 456, baz: new NumberBox(789)})", "throws",
385 "TypeError: Incorrect type for map entry 'bar': the provided value is not of type "
386 "'NumberBox'.");
387 
388 e.expectEval("takeDictOfFunctions({\n"
389 " foo() { return this.bar() + 123; },\n"
390 " bar() { return 456; }\n"
391 "})",
392 "string", "foo: 579, bar: 456");
393}
394 
395// ========================================================================================
396 
397struct IntContext: public ContextGlobalObject {
398 kj::String takeInt(int i) {
399 return kj::str("int: ", i);
400 }
401 int returnInt() {
402 return 123;
403 }
404 JSG_RESOURCE_TYPE(IntContext) {
405 JSG_METHOD(takeInt);
406 JSG_METHOD(returnInt);
407 }
408};
409JSG_DECLARE_ISOLATE_TYPE(IntIsolate, IntContext);
410 
411KJ_TEST("integers") {
412 Evaluator<IntContext, IntIsolate> e(v8System);
413 e.expectEval("takeInt(123)", "string", "int: 123");
414 e.expectEval("returnInt()", "number", "123");
415 
416 e.expectEval("takeInt(1)", "string", "int: 1");
417 e.expectEval("takeInt(-1)", "string", "int: -1");
418 e.expectEval("takeInt(123.5)", "string", "int: 123");
419 e.expectEval("takeInt(null)", "string", "int: 0");
420 e.expectEval("takeInt(undefined)", "string", "int: 0");
421 e.expectEval("takeInt(Number.NaN)", "string", "int: 0");
422 e.expectEval("takeInt(Number.POSITIVE_INFINITY)", "string", "int: 0");
423 e.expectEval("takeInt(Number.NEGATIVE_INFINITY)", "string", "int: 0");
424 e.expectEval("takeInt({})", "string", "int: 0");
425 
426 e.expectEval("takeInt(2147483647)", "string", "int: 2147483647");
427 e.expectEval("takeInt(-2147483648)", "string", "int: -2147483648");
428 
429 e.expectEval("takeInt(2147483648)", "throws",
430 "TypeError: Value out of range. Must be between "
431 "-2147483648 and 2147483647 (inclusive).");
432 e.expectEval("takeInt(-2147483649)", "throws",
433 "TypeError: Value out of range. Must be between "
434 "-2147483648 and 2147483647 (inclusive).");
435 e.expectEval("takeInt(Number.MAX_SAFE_INTEGER)", "throws",
436 "TypeError: Value out of range. Must be between "
437 "-2147483648 and 2147483647 (inclusive).");
438 e.expectEval("takeInt(-Number.MAX_SAFE_INTEGER)", "throws",
439 "TypeError: Value out of range. Must be between "
440 "-2147483648 and 2147483647 (inclusive).");
441}
442 
443// ========================================================================================
444struct Uint32Context: public ContextGlobalObject {
445 kj::String takeUint32(uint32_t i) {
446 return kj::str("uint32_t: ", i);
447 }
448 uint32_t returnUint32() {
449 return 123;
450 }
451 uint32_t takeOneOfUint32(kj::OneOf<kj::String, uint32_t> i) {
452 KJ_SWITCH_ONEOF(i) {
453 KJ_CASE_ONEOF(str, kj::String) {
454 KJ_FAIL_ASSERT("Should not have been interpreted as a string.");
455 }
456 KJ_CASE_ONEOF(num, uint32_t) {
457 return num;
458 }
459 }
460 KJ_UNREACHABLE;
461 }
462 
463 JSG_RESOURCE_TYPE(Uint32Context) {
464 JSG_METHOD(takeUint32);
465 JSG_METHOD(takeOneOfUint32);
466 JSG_METHOD(returnUint32);
467 }
468};
469JSG_DECLARE_ISOLATE_TYPE(Uint32Isolate, Uint32Context);
470KJ_TEST("unsigned integers") {
471 Evaluator<Uint32Context, Uint32Isolate> e(v8System);
472 e.expectEval("takeUint32(123)", "string", "uint32_t: 123");
473 e.expectEval("returnUint32()", "number", "123");
474 
475 e.expectEval("takeUint32(1)", "string", "uint32_t: 1");
476 e.expectEval("takeUint32(123.5)", "string", "uint32_t: 123");
477 e.expectEval("takeUint32(null)", "string", "uint32_t: 0");
478 
479 e.expectEval("takeOneOfUint32(1)", "number", "1");
480 
481 e.expectEval("takeUint32(-1)", "throws",
482 "TypeError: The value cannot be converted because it is negative and this "
483 "API expects a positive number.");
484 e.expectEval("takeUint32({})", "throws",
485 "TypeError: The value cannot be converted because it is not an integer.");
486 e.expectEval("takeUint32(undefined)", "throws",
487 "TypeError: The value cannot be converted because it is not an integer.");
488 e.expectEval("takeUint32(Number.NaN)", "throws",
489 "TypeError: The value cannot be converted because it is not an integer.");
490 e.expectEval("takeUint32(Number.POSITIVE_INFINITY)", "throws",
491 "TypeError: The value cannot be converted because it is not an integer.");
492 e.expectEval("takeUint32(Number.NEGATIVE_INFINITY)", "throws",
493 "TypeError: The value cannot be converted because it is not an integer.");
494 
495 e.expectEval("takeUint32(4294967295)", "string", "uint32_t: 4294967295");
496 
497 e.expectEval("takeUint32(4294967296)", "throws",
498 "TypeError: Value out of range. Must be less than or equal to 4294967295.");
499 e.expectEval("takeUint32(Number.MAX_SAFE_INTEGER)", "throws",
500 "TypeError: Value out of range. Must be less than or equal to 4294967295.");
501}
502 
503// ========================================================================================
504struct Uint64Context: public ContextGlobalObject {
505 kj::String takeUint64(uint64_t i) {
506 return kj::str("uint64_t: ", i);
507 }
508 uint64_t returnUint64() {
509 return 123;
510 }
511 kj::String takeInt64(int64_t i) {
512 return kj::str("int64_t: ", i);
513 }
514 uint64_t takeOneOfUint64(kj::OneOf<kj::String, uint64_t> i) {
515 KJ_SWITCH_ONEOF(i) {
516 KJ_CASE_ONEOF(str, kj::String) {
517 KJ_FAIL_ASSERT("Should not have been interpreted as a string.");
518 }
519 KJ_CASE_ONEOF(num, uint64_t) {
520 return num;
521 }
522 }
523 KJ_UNREACHABLE;
524 }
525 int64_t takeOneOfInt64(kj::OneOf<kj::String, int64_t> i) {
526 KJ_SWITCH_ONEOF(i) {
527 KJ_CASE_ONEOF(str, kj::String) {
528 KJ_FAIL_ASSERT("Should not have been interpreted as a string.");
529 }
530 KJ_CASE_ONEOF(num, int64_t) {
531 return num;
532 }
533 }
534 KJ_UNREACHABLE;
535 }
536 int64_t returnInt64() {
537 return 123;
538 }
539 JSG_RESOURCE_TYPE(Uint64Context) {
540 JSG_METHOD(takeUint64);
541 JSG_METHOD(takeOneOfUint64);
542 JSG_METHOD(takeOneOfInt64);
543 JSG_METHOD(returnUint64);
544 JSG_METHOD(takeInt64);
545 JSG_METHOD(returnInt64);
546 }
547};
548JSG_DECLARE_ISOLATE_TYPE(Uint64Isolate, Uint64Context);
549KJ_TEST("bigints") {
550 Evaluator<Uint64Context, Uint64Isolate> e(v8System);
551 e.expectEval("takeUint64(123)", "string", "uint64_t: 123");
552 e.expectEval("takeUint64(123n)", "string", "uint64_t: 123");
553 e.expectEval("takeUint64(1n)", "string", "uint64_t: 1");
554 e.expectEval("takeUint64(1)", "string", "uint64_t: 1");
555 e.expectEval("takeUint64(123.5)", "string", "uint64_t: 123");
556 e.expectEval("takeUint64(null)", "string", "uint64_t: 0");
557 e.expectEval("takeUint64(BigInt(1))", "string", "uint64_t: 1");
558 
559 e.expectEval("takeOneOfUint64(1)", "bigint", "1");
560 e.expectEval("takeOneOfUint64(1n)", "bigint", "1");
561 
562 e.expectEval("takeOneOfInt64(1)", "bigint", "1");
563 e.expectEval("takeOneOfInt64(1n)", "bigint", "1");
564 
565 e.expectEval("takeInt64(123)", "string", "int64_t: 123");
566 e.expectEval("takeInt64(123n)", "string", "int64_t: 123");
567 
568 e.expectEval("takeInt64(1n)", "string", "int64_t: 1");
569 e.expectEval("takeInt64(-1n)", "string", "int64_t: -1");
570 e.expectEval("takeInt64(1)", "string", "int64_t: 1");
571 e.expectEval("takeInt64(-1)", "string", "int64_t: -1");
572 e.expectEval("takeInt64(123.5)", "string", "int64_t: 123");
573 e.expectEval("takeInt64(null)", "string", "int64_t: 0");
574 e.expectEval("takeInt64('1')", "string", "int64_t: 1");
575 e.expectEval("takeInt64(BigInt(-1))", "string", "int64_t: -1");
576 
577 e.expectEval("returnUint64()", "bigint", "123");
578 e.expectEval("returnInt64()", "bigint", "123");
579 
580 e.expectEval("takeUint64(-1)", "throws",
581 "TypeError: The value cannot be converted because it is negative and this "
582 "API expects a positive bigint.");
583 e.expectEval("takeUint64(-1n)", "throws",
584 "TypeError: The value cannot be converted because it is either negative and "
585 "this API expects a positive bigint, or the value would be truncated.");
586 
587 e.expectEval("takeUint64(undefined)", "throws",
588 "TypeError: The value cannot be converted because it is not an integer.");
589 e.expectEval("takeInt64(undefined)", "throws",
590 "TypeError: The value cannot be converted because it is not an integer.");
591 e.expectEval("takeInt64('hello')", "throws",
592 "TypeError: The value cannot be converted because it is not an integer.");
593 e.expectEval("takeInt64({})", "throws",
594 "TypeError: The value cannot be converted because it is not an integer.");
595 e.expectEval("takeInt64(Number.NaN)", "throws",
596 "TypeError: The value cannot be converted because it is not an integer.");
597 e.expectEval("takeInt64(Number.POSITIVE_INFINITY)", "throws",
598 "TypeError: The value cannot be converted because it is not an integer.");
599 e.expectEval("takeInt64(Number.NEGATIVE_INFINITY)", "throws",
600 "TypeError: The value cannot be converted because it is not an integer.");
601 
602 e.expectEval("takeUint64('hello')", "throws",
603 "TypeError: The value cannot be converted because it is not an integer.");
604 e.expectEval("takeUint64({})", "throws",
605 "TypeError: The value cannot be converted because it is not an integer.");
606 e.expectEval("takeUint64(Number.NaN)", "throws",
607 "TypeError: The value cannot be converted because it is not an integer.");
608 e.expectEval("takeUint64(Number.POSITIVE_INFINITY)", "throws",
609 "TypeError: The value cannot be converted because it is not an integer.");
610 e.expectEval("takeUint64(Number.NEGATIVE_INFINITY)", "throws",
611 "TypeError: The value cannot be converted because it is not an integer.");
612 
613 e.expectEval("takeUint64(18446744073709551615n)", "string", "uint64_t: 18446744073709551615");
614 
615 e.expectEval("takeUint64(18446744073709551616n)", "throws",
616 "TypeError: The value cannot be converted because it is either negative "
617 "and this API expects a positive bigint, or the value would be truncated.");
618 
619 e.expectEval("takeInt64(9223372036854775807n)", "string", "int64_t: 9223372036854775807");
620 e.expectEval("takeInt64(9223372036854775808n)", "throws",
621 "TypeError: The value cannot be converted because it would be truncated.");
622}
623 
624// ========================================================================================
625 
626struct Int8Context: public ContextGlobalObject {
627 kj::String takeInt8(int8_t i) {
628 return kj::str("int8_t: ", i);
629 }
630 kj::String takeUint8(uint8_t i) {
631 return kj::str("uint8_t: ", i);
632 }
633 int8_t returnInt8() {
634 return 123;
635 }
636 uint8_t returnUint8() {
637 return 123;
638 }
639 JSG_RESOURCE_TYPE(Int8Context) {
640 JSG_METHOD(takeInt8);
641 JSG_METHOD(takeUint8);
642 JSG_METHOD(returnInt8);
643 JSG_METHOD(returnUint8);
644 }
645};
646JSG_DECLARE_ISOLATE_TYPE(Int8Isolate, Int8Context);
647 
648KJ_TEST("int8 integers") {
649 Evaluator<Int8Context, Int8Isolate> e(v8System);
650 e.expectEval("takeInt8(123)", "string", "int8_t: 123");
651 e.expectEval("takeUint8(123)", "string", "uint8_t: 123");
652 e.expectEval("returnInt8()", "number", "123");
653 e.expectEval("returnUint8()", "number", "123");
654 
655 e.expectEval("takeInt8(1)", "string", "int8_t: 1");
656 e.expectEval("takeInt8(-1)", "string", "int8_t: -1");
657 e.expectEval("takeInt8(123.5)", "string", "int8_t: 123");
658 
659 e.expectEval("takeInt8(127)", "string", "int8_t: 127");
660 e.expectEval("takeInt8(-128)", "string", "int8_t: -128");
661 e.expectEval("takeUint8(255)", "string", "uint8_t: 255");
662 
663 e.expectEval("takeUint8(-1)", "throws",
664 "TypeError: The value cannot be converted because it is negative and this "
665 "API expects a positive number.");
666 e.expectEval("takeInt8(32768)", "throws",
667 "TypeError: Value out of range. Must be between "
668 "-128 and 127 (inclusive).");
669 e.expectEval("takeInt8(-32769)", "throws",
670 "TypeError: Value out of range. Must be between "
671 "-128 and 127 (inclusive).");
672 e.expectEval("takeInt8(Number.MAX_SAFE_INTEGER)", "throws",
673 "TypeError: Value out of range. Must be between "
674 "-128 and 127 (inclusive).");
675 e.expectEval("takeInt8(-Number.MAX_SAFE_INTEGER)", "throws",
676 "TypeError: Value out of range. Must be between "
677 "-128 and 127 (inclusive).");
678}
679 
680// ========================================================================================
681 
682struct Int16Context: public ContextGlobalObject {
683 kj::String takeInt16(int16_t i) {
684 return kj::str("int16_t: ", i);
685 }
686 kj::String takeUint16(uint16_t i) {
687 return kj::str("uint16_t: ", i);
688 }
689 int16_t returnInt16() {
690 return 123;
691 }
692 uint16_t returnUint16() {
693 return 123;
694 }
695 JSG_RESOURCE_TYPE(Int16Context) {
696 JSG_METHOD(takeInt16);
697 JSG_METHOD(takeUint16);
698 JSG_METHOD(returnInt16);
699 JSG_METHOD(returnUint16);
700 }
701};
702JSG_DECLARE_ISOLATE_TYPE(Int16Isolate, Int16Context);
703 
704KJ_TEST("int16 integers") {
705 Evaluator<Int16Context, Int16Isolate> e(v8System);
706 e.expectEval("takeInt16(123)", "string", "int16_t: 123");
707 e.expectEval("takeUint16(123)", "string", "uint16_t: 123");
708 e.expectEval("returnInt16()", "number", "123");
709 e.expectEval("returnUint16()", "number", "123");
710 
711 e.expectEval("takeInt16(1)", "string", "int16_t: 1");
712 e.expectEval("takeInt16(-1)", "string", "int16_t: -1");
713 e.expectEval("takeInt16(123.5)", "string", "int16_t: 123");
714 
715 e.expectEval("takeInt16(32767)", "string", "int16_t: 32767");
716 e.expectEval("takeInt16(-32768)", "string", "int16_t: -32768");
717 e.expectEval("takeUint16(65535)", "string", "uint16_t: 65535");
718 
719 e.expectEval("takeUint16(-1)", "throws",
720 "TypeError: The value cannot be converted because it is negative and this "
721 "API expects a positive number.");
722 e.expectEval("takeInt16(32768)", "throws",
723 "TypeError: Value out of range. Must be between "
724 "-32768 and 32767 (inclusive).");
725 e.expectEval("takeInt16(-32769)", "throws",
726 "TypeError: Value out of range. Must be between "
727 "-32768 and 32767 (inclusive).");
728 e.expectEval("takeInt16(Number.MAX_SAFE_INTEGER)", "throws",
729 "TypeError: Value out of range. Must be between "
730 "-32768 and 32767 (inclusive).");
731 e.expectEval("takeInt16(-Number.MAX_SAFE_INTEGER)", "throws",
732 "TypeError: Value out of range. Must be between "
733 "-32768 and 32767 (inclusive).");
734}
735 
736// ========================================================================================
737 
738struct DoubleContext: public ContextGlobalObject {
739 kj::String takeDouble(double d) {
740 return kj::str("double: ", d);
741 }
742 double returnDouble() {
743 return 123.5;
744 }
745 
746 JSG_RESOURCE_TYPE(DoubleContext) {
747 JSG_NESTED_TYPE(NumberBox);
748 JSG_METHOD(takeDouble);
749 JSG_METHOD(returnDouble);
750 }
751};
752JSG_DECLARE_ISOLATE_TYPE(DoubleIsolate, DoubleContext, NumberBox);
753 
754KJ_TEST("floating points") {
755 Evaluator<DoubleContext, DoubleIsolate> e(v8System);
756 e.expectEval("takeDouble(123)", "string", "double: 123");
757 e.expectEval("takeDouble(123.5)", "string", "double: 123.5");
758 e.expectEval("takeDouble('123')", "string", "double: 123");
759 e.expectEval("takeDouble(' \\r\\n123')", "string", "double: 123");
760 e.expectEval("takeDouble('0x7b')", "string", "double: 123");
761 e.expectEval("takeDouble(true)", "string", "double: 1");
762 e.expectEval("takeDouble(Number.MAX_SAFE_INTEGER)", "string", "double: 9007199254740991");
763 e.expectEval("takeDouble({ valueOf: function() { return 456.7; } })", "string", "double: 456.7");
764 e.expectEval("returnDouble()", "number", "123.5");
765 
766 e.expectEval("takeDouble([Symbol.iterator])", "throws",
767 "TypeError: Cannot convert a Symbol value to a string");
768 e.expectEval("takeDouble('123asdf')", "string", "double: nan");
769 e.expectEval("takeDouble('asdf123')", "string", "double: nan");
770 e.expectEval("takeDouble(null)", "string", "double: 0");
771 e.expectEval("takeDouble(undefined)", "string", "double: nan");
772 e.expectEval("takeDouble(Number.NaN)", "string", "double: nan");
773 e.expectEval("takeDouble(Number.POSITIVE_INFINITY)", "string", "double: inf");
774 e.expectEval("takeDouble(Number.NEGATIVE_INFINITY)", "string", "double: -inf");
775 e.expectEval("takeDouble({})", "string", "double: nan");
776 e.expectEval("takeDouble(new NumberBox(321))", "string", "double: nan");
777}
778 
779// ========================================================================================
780 
781struct StringContext: public ContextGlobalObject {
782 kj::String takeString(kj::String s) {
783 return kj::mv(s);
784 }
785 JSG_RESOURCE_TYPE(StringContext) {
786 JSG_METHOD(takeString);
787 }
788};
789JSG_DECLARE_ISOLATE_TYPE(StringIsolate, StringContext);
790 
791KJ_TEST("kj::Strings") {
792 Evaluator<StringContext, StringIsolate> e(v8System);
793 e.expectEval("takeString(false)", "string", "false");
794 e.expectEval("takeString(true)", "string", "true");
795 e.expectEval("takeString(123)", "string", "123");
796 e.expectEval("takeString(Number.NaN)", "string", "NaN");
797 e.expectEval("takeString(Number.POSITIVE_INFINITY)", "string", "Infinity");
798 e.expectEval("takeString(null)", "string", "null");
799 e.expectEval("takeString(undefined)", "string", "undefined");
800 e.expectEval("takeString('an actual string')", "string", "an actual string");
801 e.expectEval(
802 "takeString({ toString: function() { return 'toString()ed'; } })", "string", "toString()ed");
803}
804 
805// ========================================================================================
806struct USVStringContext: public ContextGlobalObject {
807 jsg::USVString takeUSVString(jsg::USVString s) {
808 return kj::mv(s);
809 }
810 
811 JSG_RESOURCE_TYPE(USVStringContext) {
812 JSG_METHOD(takeUSVString);
813 }
814};
815 
816JSG_DECLARE_ISOLATE_TYPE(USVStringIsolate, USVStringContext);
817 
818KJ_TEST("jsg::USVStrings") {
819 Evaluator<USVStringContext, USVStringIsolate> e(v8System);
820 e.expectEval("takeUSVString('hello world')", "string", "hello world");
821 // From JS to C++: Characters forbidden in UTF-8, like unpaired surrogates, are replaced with the Unicode replacement character
822 // From C++ to JS: The replacement character is valid and is converted from UTF-8 to UTF-16
823 e.expectEval("takeUSVString('\\uD835x')", "string", u8"\uFFFDx");
824 e.expectEval("takeUSVString('\\uD835x\\uDC53')", "string", u8"\uFFFDx\uFFFD");
825}
826 
827// ========================================================================================
828struct DOMStringContext: public ContextGlobalObject {
829 jsg::DOMString takeDOMString(jsg::DOMString s) {
830 return kj::mv(s);
831 }
832 
833 JSG_RESOURCE_TYPE(DOMStringContext) {
834 JSG_METHOD(takeDOMString);
835 }
836};
837 
838JSG_DECLARE_ISOLATE_TYPE(DOMStringIsolate, DOMStringContext);
839 
840KJ_TEST("jsg::DOMStrings") {
841 Evaluator<DOMStringContext, DOMStringIsolate> e(v8System);
842 e.expectEval("takeDOMString('hello world')", "string", "hello world");
843 // From JS to C++: Characters forbidden in UTF-8, like unpaired surrogates, are encoded anyway making this a WTF-8 encoded value.
844 // From C++ to JS: Each invalid byte in the encoding of the unpaired surrogate is replaced with the Unicode replacement character.
845 // WTF, that's 3 replacement characters for each unpaired surrogate.
846 // TODO(someday): Fix the conversion back into a V8 string such that we get back the WTF-16 value we had originally (an unpaired surrogate)
847 e.expectEval("[...takeDOMString('\\uD835x')]", "object", u8"\uFFFD,\uFFFD,\uFFFD,x");
848 e.expectEval("[...takeDOMString('\\uD835x\\uDC53')]", "object",
849 u8"\uFFFD,\uFFFD,\uFFFD,x,\uFFFD,\uFFFD,\uFFFD");
850}
851 
852// ========================================================================================
853 
854struct RawContext: public ContextGlobalObject {
855 struct TwoValues {
856 Value $foo;
857 Value $bar;
858 JSG_STRUCT($foo, $bar);
859 };
860 TwoValues twoValues(Value foo, Value bar) {
861 return {kj::mv(foo), kj::mv(bar)};
862 }
863 JSG_RESOURCE_TYPE(RawContext) {
864 JSG_METHOD(twoValues);
865 }
866};
867JSG_DECLARE_ISOLATE_TYPE(RawIsolate, RawContext, RawContext::TwoValues);
868 
869KJ_TEST("Raw Values") {
870 Evaluator<RawContext, RawIsolate> e(v8System);
871 e.expectEval("JSON.stringify(twoValues({baz: 123}, 'abcd'))",
872 
873 "string", "{\"foo\":{\"baz\":123},\"bar\":\"abcd\"}");
874}
875 
876// ========================================================================================
877 
878struct DateContext: public ContextGlobalObject {
879 kj::Date takeDate(kj::Date date) {
880 return date;
881 }
882 JSG_RESOURCE_TYPE(DateContext) {
883 JSG_METHOD(takeDate);
884 }
885};
886JSG_DECLARE_ISOLATE_TYPE(DateIsolate, DateContext);
887 
888KJ_TEST("Date Values") {
889 Evaluator<DateContext, DateIsolate> e(v8System);
890 e.expectEval("takeDate(new Date('2022-01-22T00:54:57.893Z')).toUTCString()", "string",
891 "Sat, 22 Jan 2022 00:54:57 GMT");
892 e.expectEval("takeDate(12345).valueOf()", "number", "12345");
893 e.expectEval("takeDate(8640000000000000).valueOf()", "throws",
894 "TypeError: This API doesn't support dates after 2189."),
895 e.expectEval("takeDate(-8640000000000000).valueOf()", "throws",
896 "TypeError: This API doesn't support dates before 1687."),
897 e.expectEval("takeDate(1/0)", "throws",
898 "TypeError: The value cannot be converted because it is not a valid Date."),
899 e.expectEval("takeDate(new Date(1/0))", "throws",
900 "TypeError: The value cannot be converted because it is not a valid Date."),
901 e.expectEval("takeDate(new Date('1800-01-22T00:54:57.893Z')).toUTCString()", "string",
902 "Wed, 22 Jan 1800 00:54:57 GMT");
903 e.expectEval("takeDate('2022-01-22T00:54:57.893Z')", "throws",
904 "TypeError: Failed to execute 'takeDate' on 'DateContext': parameter "
905 "1 is not of type 'date'.");
906}
907 
908// ========================================================================================
909 
910struct ArrayContext: public ContextGlobalObject {
911 kj::Array<int> takeArray(kj::Array<int> array) {
912 // The ArrayWrapper uses a stack array with a max size of 64. This is just a
913 // quick test to ensure that arrays larger than that are properly supported.
914 KJ_ASSERT(array.size() == 65);
915 KJ_ASSERT(array[64] == 1);
916 return kj::mv(array);
917 }
918 kj::Array<int> takeArguments(int i, Arguments<int> array) {
919 KJ_ASSERT(i == 123);
920 return kj::mv(array);
921 }
922 JSG_RESOURCE_TYPE(ArrayContext) {
923 JSG_METHOD(takeArray);
924 JSG_METHOD(takeArguments);
925 }
926};
927JSG_DECLARE_ISOLATE_TYPE(ArrayIsolate, ArrayContext);
928 
929KJ_TEST("Array Values") {
930 Evaluator<ArrayContext, ArrayIsolate> e(v8System);
931 e.expectEval("m = Array(65); m[64] = 1; takeArray(m)[64]", "number", "1");
932 
933 e.expectEval("takeArguments(123, 456, 789, 321).join(', ')", "string", "456, 789, 321");
934}
935 
936// ========================================================================================
937 
938struct SetContext: public ContextGlobalObject {
939 kj::HashSet<kj::String> takeSet(kj::HashSet<kj::String> set) {
940 KJ_ASSERT(set.contains("42"_kj));
941 return kj::mv(set);
942 }
943 kj::HashSet<kj::String> returnStrings(int i, int j, int k) {
944 auto result = kj::HashSet<kj::String>();
945 result.insert(kj::str(i));
946 result.insert(kj::str(j));
947 result.insert(kj::str(k));
948 return kj::mv(result);
949 }
950 JSG_RESOURCE_TYPE(SetContext) {
951 JSG_METHOD(takeSet);
952 JSG_METHOD(returnStrings);
953 }
954};
955JSG_DECLARE_ISOLATE_TYPE(SetIsolate, SetContext);
956 
957KJ_TEST("Set Values") {
958 Evaluator<SetContext, SetIsolate> e(v8System);
959 e.expectEval("m = new Set(); m.add('42'); takeSet(m).has('42')", "boolean", "true");
960 e.expectEval(
961 "const toString = () => '42'; takeSet(new Set([{ toString }, { toString }])).has('42')",
962 "throws", "TypeError: Duplicate values in the set after unwrapping.");
963 
964 e.expectEval("returnStrings(123, 1024, 456).has('1024')", "boolean", "true");
965}
966 
967// ========================================================================================
968 
969struct SequenceContext: public ContextGlobalObject {
970 Sequence<kj::String> testSequence(Sequence<kj::String> sequence) {
971 KJ_ASSERT(sequence.size() == 2);
972 KJ_ASSERT(sequence[0] == "a");
973 KJ_ASSERT(sequence[1] == "b");
974 return kj::mv(sequence);
975 }
976 
977 Sequence<int> testInt(Sequence<int> sequence) {
978 KJ_ASSERT(sequence.size(), 2);
979 return kj::mv(sequence);
980 }
981 
982 struct Foo {
983 kj::String a;
984 JSG_STRUCT(a);
985 };
986 
987 Sequence<Foo> testFoo(Sequence<Foo> sequence) {
988 KJ_ASSERT(sequence.size() == 1);
989 return kj::mv(sequence);
990 }
991 
992 // Because the kj::OneOf lists kj::String separately, and because JavaScript
993 // strings are technically iterable, we want to make sure that the Sequence
994 // ignores strings.
995 bool oneof(kj::OneOf<kj::String, Sequence<kj::String>> input) {
996 KJ_SWITCH_ONEOF(input) {
997 KJ_CASE_ONEOF(str, kj::String) {
998 KJ_ASSERT(str == "aa");
999 return true;
1000 }
1001 KJ_CASE_ONEOF(seq, Sequence<kj::String>) {
1002 KJ_ASSERT(seq[0] == "b");
1003 KJ_ASSERT(seq[1] == "b");
1004 return true;
1005 }
1006 }
1007 KJ_UNREACHABLE;
1008 }
1009 
1010 JSG_RESOURCE_TYPE(SequenceContext) {
1011 JSG_METHOD(testSequence);
1012 JSG_METHOD(testInt);
1013 JSG_METHOD(testFoo);
1014 JSG_METHOD(oneof);
1015 }
1016};
1017JSG_DECLARE_ISOLATE_TYPE(SequenceIsolate, SequenceContext, SequenceContext::Foo);
1018 
1019KJ_TEST("Sequence Values") {
1020 Evaluator<SequenceContext, SequenceIsolate> e(v8System);
1021 e.expectEval("testSequence(['a', 'b']).join('')", "string", "ab");
1022 e.expectEval("const val = {*[Symbol.iterator]() { yield 'a'; yield 'b'; }};"
1023 "testSequence(val).join('')",
1024 "string", "ab");
1025 e.expectEval("testInt([1,2]).join('')", "string", "12");
1026 e.expectEval("testInt([1,'2']).join('')", "string", "12");
1027 e.expectEval("testInt([1,'a']).join('')", "string", "10");
1028 e.expectEval("testInt([1,null]).join('')", "string", "10");
1029 e.expectEval("testInt([1,NaN]).join('')", "string", "10");
1030 e.expectEval("testFoo([{a:'a'}])[0].a", "string", "a");
1031 e.expectEval("oneof('aa')", "boolean", "true");
1032 e.expectEval("oneof(['b', 'b'])", "boolean", "true");
1033 e.expectEval("testFoo({a:'a'})", "throws",
1034 "TypeError: Failed to execute 'testFoo' on 'SequenceContext': parameter 1 is not of type 'Sequence'.");
1035}
1036 
1037// ========================================================================================
1038 
1039struct NonCoercibleContext: public ContextGlobalObject {
1040 template <CoercibleType T>
1041 bool test(NonCoercible<T>) {
1042 return true;
1043 }
1044 
1045 template <CoercibleType T>
1046 bool testCoerced(T) {
1047 return true;
1048 }
1049 
1050 bool testMaybeString(Optional<NonCoercible<kj::String>> value) {
1051 KJ_IF_SOME(v, value) {
1052 KJ_ASSERT(v.value != "null"_kj);
1053 }
1054 return true;
1055 }
1056 
1057 bool testMaybeStringCoerced(Optional<kj::String> value) {
1058 KJ_ASSERT(KJ_ASSERT_NONNULL(value) == "null"_kj);
1059 return true;
1060 }
1061 
1062 bool testOneOf(kj::OneOf<NonCoercible<bool>, NonCoercible<kj::String>> value) {
1063 return true;
1064 }
1065 
1066 JSG_RESOURCE_TYPE(NonCoercibleContext) {
1067 JSG_METHOD_NAMED(testString, template test<kj::String>);
1068 JSG_METHOD_NAMED(testStringCoerced, template testCoerced<kj::String>);
1069 JSG_METHOD_NAMED(testUSVString, template test<jsg::USVString>);
1070 JSG_METHOD_NAMED(testUSVStringCoerced, template testCoerced<jsg::USVString>);
1071 JSG_METHOD_NAMED(testDOMString, template test<jsg::DOMString>);
1072 JSG_METHOD_NAMED(testDOMStringCoerced, template testCoerced<jsg::DOMString>);
1073 JSG_METHOD_NAMED(testBoolean, template test<bool>);
1074 JSG_METHOD_NAMED(testBooleanCoerced, template testCoerced<bool>);
1075 JSG_METHOD_NAMED(testDouble, template test<double>);
1076 JSG_METHOD_NAMED(testDoubleCoerced, template testCoerced<double>);
1077 JSG_METHOD(testMaybeString);
1078 JSG_METHOD(testMaybeStringCoerced);
1079 JSG_METHOD(testOneOf);
1080 }
1081};
1082JSG_DECLARE_ISOLATE_TYPE(NonCoercibleIsolate, NonCoercibleContext);
1083 
1084KJ_TEST("NonCoercible Values") {
1085 Evaluator<NonCoercibleContext, NonCoercibleIsolate> e(v8System);
1086 e.expectEval("testString('')", "boolean", "true");
1087 e.expectEval("testString(null)", "throws",
1088 "TypeError: Failed to execute 'testString' on 'NonCoercibleContext': parameter 1 is "
1089 "not of type 'string'.");
1090 e.expectEval("testString({})", "throws",
1091 "TypeError: Failed to execute 'testString' on 'NonCoercibleContext': parameter 1 is "
1092 "not of type 'string'.");
1093 e.expectEval("testString(1)", "throws",
1094 "TypeError: Failed to execute 'testString' on 'NonCoercibleContext': parameter 1 is "
1095 "not of type 'string'.");
1096 e.expectEval("testStringCoerced('')", "boolean", "true");
1097 e.expectEval("testStringCoerced(null)", "boolean", "true");
1098 e.expectEval("testStringCoerced({})", "boolean", "true");
1099 e.expectEval("testStringCoerced(1)", "boolean", "true");
1100 
1101 e.expectEval("testUSVString('hi')", "boolean", "true");
1102 e.expectEval("testUSVString(null)", "throws",
1103 "TypeError: Failed to execute 'testUSVString' on 'NonCoercibleContext': parameter 1 is "
1104 "not of type 'USVString'.");
1105 e.expectEval("testUSVStringCoerced('hi')", "boolean", "true");
1106 e.expectEval("testUSVStringCoerced(null)", "boolean", "true");
1107 
1108 e.expectEval("testDOMString('hi')", "boolean", "true");
1109 e.expectEval("testDOMString(null)", "throws",
1110 "TypeError: Failed to execute 'testDOMString' on 'NonCoercibleContext': parameter 1 is "
1111 "not of type 'DOMString'.");
1112 e.expectEval("testDOMStringCoerced('hi')", "boolean", "true");
1113 e.expectEval("testDOMStringCoerced(null)", "boolean", "true");
1114 
1115 e.expectEval("testBoolean(true)", "boolean", "true");
1116 e.expectEval("testBoolean(null)", "throws",
1117 "TypeError: Failed to execute 'testBoolean' on 'NonCoercibleContext': parameter 1 is"
1118 " not of type 'boolean'.");
1119 e.expectEval("testBooleanCoerced(true)", "boolean", "true");
1120 e.expectEval("testBooleanCoerced(null)", "boolean", "true");
1121 
1122 e.expectEval("testDouble(1.1)", "boolean", "true");
1123 e.expectEval("testDouble(Infinity)", "boolean", "true");
1124 e.expectEval("testDouble(NaN)", "boolean", "true");
1125 e.expectEval("testDouble(null)", "throws",
1126 "TypeError: Failed to execute 'testDouble' on 'NonCoercibleContext': parameter 1 is"
1127 " not of type 'number'.");
1128 e.expectEval("testDoubleCoerced(1.1)", "boolean", "true");
1129 e.expectEval("testDoubleCoerced(null)", "boolean", "true");
1130 
1131 e.expectEval("testMaybeString('')", "boolean", "true");
1132 e.expectEval("testMaybeString(undefined)", "boolean", "true");
1133 e.expectEval("testMaybeString(null)", "throws",
1134 "TypeError: Failed to execute 'testMaybeString' on 'NonCoercibleContext': parameter"
1135 " 1 is not of type 'string'.");
1136 e.expectEval("testMaybeString(1)", "throws",
1137 "TypeError: Failed to execute 'testMaybeString' on 'NonCoercibleContext': parameter"
1138 " 1 is not of type 'string'.");
1139 
1140 e.expectEval("testMaybeStringCoerced(null)", "boolean", "true");
1141 
1142 e.expectEval("testOneOf(false)", "boolean", "true");
1143 e.expectEval("testOneOf('')", "boolean", "true");
1144 e.expectEval("testOneOf(new String(''))", "throws",
1145 "TypeError: Failed to execute 'testOneOf' on 'NonCoercibleContext': parameter 1 is"
1146 " not of type 'boolean or string'.");
1147}
1148 
1149// ========================================================================================
1150 
1151struct MemoizedIdentityContext: public ContextGlobalObject {
1152 static constexpr kj::Date DATE = kj::UNIX_EPOCH + 123 * kj::MILLISECONDS;
1153 MemoizedIdentity<kj::Date> date = DATE;
1154 
1155 kj::Date getDate() {
1156 return DATE;
1157 }
1158 
1159 MemoizedIdentity<kj::Date>& getDateMemoized() {
1160 return date;
1161 }
1162 
1163 JSG_RESOURCE_TYPE(MemoizedIdentityContext) {
1164 JSG_METHOD(getDate);
1165 JSG_METHOD(getDateMemoized);
1166 }
1167};
1168JSG_DECLARE_ISOLATE_TYPE(MemoizedIdentityIsolate, MemoizedIdentityContext);
1169 
1170KJ_TEST("MemoizedIdentity Values") {
1171 Evaluator<MemoizedIdentityContext, MemoizedIdentityIsolate> e(v8System);
1172 e.expectEval("getDate() === getDate()", "boolean", "false");
1173 e.expectEval("getDateMemoized() === getDateMemoized()", "boolean", "true");
1174}
1175 
1176// ========================================================================================
1177 
1178struct IdentifiedContext: public ContextGlobalObject {
1179 kj::String compare(jsg::Lock& js, Identified<kj::Date> a, Identified<kj::Date> b) {
1180 bool result = a.identity == b.identity;
1181 KJ_EXPECT(a.identity.hashCode() != 0);
1182 KJ_EXPECT(b.identity.hashCode() != 0);
1183 if (result) {
1184 KJ_EXPECT(a.identity.hashCode() == b.identity.hashCode());
1185 }
1186 KJ_EXPECT(a.identity.hashCode() ==
1187 kj::hashCode(a.identity.getHandle(js.v8Isolate)->GetIdentityHash()));
1188 KJ_EXPECT(b.identity.hashCode() ==
1189 kj::hashCode(b.identity.getHandle(js.v8Isolate)->GetIdentityHash()));
1190 
1191 return kj::str(result, ' ', a.unwrapped - b.unwrapped);
1192 }
1193 
1194 JSG_RESOURCE_TYPE(IdentifiedContext) {
1195 JSG_METHOD(compare);
1196 }
1197};
1198JSG_DECLARE_ISOLATE_TYPE(IdentifiedIsolate, IdentifiedContext);
1199 
1200KJ_TEST("Identified values") {
1201 Evaluator<IdentifiedContext, IdentifiedIsolate> e(v8System);
1202 
1203 e.expectEval("compare(new Date(123), new Date(123))", "string", "false 0ns");
1204 e.expectEval("compare(new Date(456), new Date(123))", "string", "false 333ms");
1205 e.expectEval("let d = new Date(123); compare(d, d)", "string", "true 0ns");
1206}
1207 
1208// ========================================================================================
1209 
1210struct ExceptionContext: public ContextGlobalObject {
1211 
1212 kj::String testToException(kj::Exception exception) {
1213 return kj::str(exception.getDescription());
1214 }
1215 
1216 kj::Exception testFromException(int n) {
1217 switch (n) {
1218 case 1:
1219 return JSG_KJ_EXCEPTION(FAILED, TypeError, "boom");
1220 case 2:
1221 return JSG_KJ_EXCEPTION(FAILED, DOMAbortError, "boom");
1222 }
1223 KJ_UNREACHABLE;
1224 }
1225 
1226 JSG_RESOURCE_TYPE(ExceptionContext) {
1227 JSG_METHOD(testToException);
1228 JSG_METHOD(testFromException);
1229 JSG_NESTED_TYPE(DOMException);
1230 }
1231};
1232JSG_DECLARE_ISOLATE_TYPE(ExceptionIsolate, ExceptionContext);
1233 
1234KJ_TEST("kj::Exception wrapper works") {
1235 Evaluator<ExceptionContext, ExceptionIsolate> e(v8System);
1236 
1237 e.expectEval("testToException(new DOMException('boom', 'AbortError'))", "string",
1238 "jsg.DOMException(AbortError): boom");
1239 e.expectEval("testToException(new SyntaxError('boom'))", "string", "jsg.SyntaxError: boom");
1240 e.expectEval("testToException(undefined)", "string", "jsg.Error: undefined");
1241 e.expectEval("testToException(1)", "string", "jsg.Error: 1");
1242 
1243 e.expectEval("testFromException(1)", "object", "TypeError: boom");
1244 e.expectEval("testFromException(2)", "object", "AbortError: boom");
1245}
1246 
1247// ========================================================================================
1248struct NameContext: public ContextGlobalObject {
1249 Name name(Name value) {
1250 return kj::mv(value);
1251 }
1252 
1253 Name forSymbol(Lock& js, kj::String symbol) {
1254 return js.newSymbol(symbol);
1255 }
1256 
1257 Name forSymbolShared(Lock& js, kj::String symbol) {
1258 return js.newSharedSymbol(symbol);
1259 }
1260 
1261 Name forSymbolApi(Lock& js, kj::String symbol) {
1262 return js.newApiSymbol(symbol);
1263 }
1264 
1265 JSG_RESOURCE_TYPE(NameContext) {
1266 JSG_METHOD(name);
1267 JSG_METHOD(forSymbol);
1268 JSG_METHOD(forSymbolShared);
1269 JSG_METHOD(forSymbolApi);
1270 }
1271};
1272JSG_DECLARE_ISOLATE_TYPE(NameIsolate, NameContext);
1273 
1274KJ_TEST("jsg::Name works") {
1275 Evaluator<NameContext, NameIsolate> e(v8System);
1276 e.expectEval("name('hello')", "string", "hello");
1277 e.expectEval("name(Symbol('foo')).description", "string", "foo");
1278 e.expectEval("name(Symbol.for('foo')).description", "string", "foo");
1279 e.expectEval("forSymbol('foo').description", "string", "foo");
1280 e.expectEval("forSymbolShared('foo').description", "string", "foo");
1281 e.expectEval("forSymbolApi('foo').description", "string", "foo");
1282 e.expectEval("forSymbol('foo') !== Symbol.for('foo')", "boolean", "true");
1283 e.expectEval("forSymbolShared('foo') === Symbol.for('foo')", "boolean", "true");
1284 e.expectEval("forSymbolShared('foo') !== forSymbolApi('foo')", "boolean", "true");
1285}
1286 
1287} // namespace
1288} // namespace workerd::jsg::test