Skip to content
File

Blob: src/workerd/jsg/jsvalue.c++

29.4 KB
1#include "jsvalue.h"
2 
3#include "buffersource.h"
4#include "ser.h"
5#include "simdutf.h"
6 
7#include <v8.h>
8 
9#include <kj/string-tree.h>
10#include <kj/string.h>
11 
12#include <cmath>
13 
14namespace workerd::jsg {
15 
16JsValue::JsValue(v8::Local<v8::Value> inner): inner(inner) {
17 requireOnStack(this);
18}
19 
20bool JsValue::operator==(const JsValue& other) const {
21 return inner == other.inner;
22}
23 
24bool JsValue::strictEquals(const JsValue& other) const {
25 return inner->StrictEquals(other.inner);
26}
27 
28JsMap::operator JsObject() {
29 return JsObject(inner);
30}
31 
32void JsMap::set(Lock& js, const JsValue& name, const JsValue& value) {
33 check(inner->Set(js.v8Context(), name.inner, value.inner));
34}
35 
36void JsMap::set(Lock& js, kj::StringPtr name, const JsValue& value) {
37 set(js, js.strIntern(name), value);
38}
39 
40JsValue JsMap::get(Lock& js, const JsValue& name) {
41 return JsValue(check(inner->Get(js.v8Context(), name.inner)));
42}
43 
44JsValue JsMap::get(Lock& js, kj::StringPtr name) {
45 return get(js, js.strIntern(name));
46}
47 
48bool JsMap::has(Lock& js, const JsValue& name) {
49 return check(inner->Has(js.v8Context(), name.inner));
50}
51 
52bool JsMap::has(Lock& js, kj::StringPtr name) {
53 return has(js, js.strIntern(name));
54}
55 
56void JsMap::delete_(Lock& js, const JsValue& name) {
57 check(inner->Delete(js.v8Context(), name.inner));
58}
59 
60void JsMap::delete_(Lock& js, kj::StringPtr name) {
61 delete_(js, js.strIntern(name));
62}
63 
64void JsObject::defineProperty(Lock& js, kj::StringPtr name, const JsValue& value) {
65 v8::Local<v8::String> nameStr = js.strIntern(name);
66 check(inner->DefineOwnProperty(js.v8Context(), nameStr, value));
67}
68 
69void JsObject::setReadOnly(Lock& js, kj::StringPtr name, const JsValue& value) {
70 v8::Local<v8::String> nameStr = js.strIntern(name);
71 check(inner->DefineOwnProperty(js.v8Context(), nameStr, value,
72 static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete)));
73}
74 
75void JsObject::setNonEnumerable(Lock& js, const JsSymbol& name, const JsValue& value) {
76 check(inner->DefineOwnProperty(
77 js.v8Context(), name.inner, value.inner, v8::PropertyAttribute::DontEnum));
78}
79 
80void JsObject::setPrivate(Lock& js, kj::StringPtr name, const JsValue& value) {
81 auto p = v8::Private::ForApi(js.v8Isolate, v8StrIntern(js.v8Isolate, name));
82 check(inner->SetPrivate(js.v8Context(), p, value.inner));
83}
84 
85JsValue JsObject::getPrivate(Lock& js, kj::StringPtr name) {
86 auto p = v8::Private::ForApi(js.v8Isolate, v8StrIntern(js.v8Isolate, name));
87 return JsValue(check(inner->GetPrivate(js.v8Context(), p)));
88}
89 
90bool JsObject::hasPrivate(Lock& js, kj::StringPtr name) {
91 auto p = v8::Private::ForApi(js.v8Isolate, v8StrIntern(js.v8Isolate, name));
92 return check(inner->HasPrivate(js.v8Context(), p));
93}
94 
95int JsObject::hashCode() const {
96 return kj::hashCode(inner->GetIdentityHash());
97}
98 
99kj::String JsObject::getConstructorName() {
100 return kj::str(inner->GetConstructorName());
101}
102 
103JsArray JsObject::getPropertyNames(Lock& js,
104 KeyCollectionFilter keyFilter,
105 PropertyFilter propertyFilter,
106 IndexFilter indexFilter) {
107 auto v8keyFilter = keyFilter == KeyCollectionFilter::INCLUDE_PROTOTYPES
108 ? v8::KeyCollectionMode::kIncludePrototypes
109 : v8::KeyCollectionMode::kOwnOnly;
110 auto v8PropertyFilter = static_cast<v8::PropertyFilter>(propertyFilter);
111 auto v8IndexFilter = indexFilter == IndexFilter::INCLUDE_INDICES
112 ? v8::IndexFilter::kIncludeIndices
113 : v8::IndexFilter::kSkipIndices;
114 return JsArray(
115 check(inner->GetPropertyNames(js.v8Context(), v8keyFilter, v8PropertyFilter, v8IndexFilter)));
116}
117 
118JsArray JsObject::previewEntries(bool* isKeyValue) {
119 return JsArray(check(inner->PreviewEntries(isKeyValue)));
120}
121 
122void JsObject::recursivelyFreeze(Lock& js) {
123 jsg::recursivelyFreeze(js.v8Context(), inner);
124}
125 
126void JsObject::seal(Lock& js) {
127 check(inner->SetIntegrityLevel(js.v8Context(), v8::IntegrityLevel::kSealed));
128}
129 
130JsObject JsObject::jsonClone(Lock& js) {
131 auto tmp = JsValue(inner).toJson(js);
132 auto obj = KJ_ASSERT_NONNULL(JsValue::fromJson(js, tmp).tryCast<jsg::JsObject>());
133 return JsObject(obj);
134}
135 
136JsValue JsObject::getPrototype(Lock& js) {
137 if (inner->IsProxy()) {
138 // Here we emulate the behavior of v8's GetPrototype() function for proxies.
139 // If the proxy has a getPrototypeOf trap, we call it and return the result.
140 // Otherwise we return the prototype of the target object.
141 // Note that we do not check if the target object is extensible or not, or
142 // if the returned prototype is consistent with the target's prototype if
143 // the target is not extensible. See the comment below for more details.
144 auto proxy = inner.As<v8::Proxy>();
145 JSG_REQUIRE(!proxy->IsRevoked(), TypeError, "Proxy is revoked");
146 auto handler = proxy->GetHandler();
147 JSG_REQUIRE(handler->IsObject(), TypeError, "Proxy handler is not an object");
148 auto jsHandler = JsObject(handler.As<v8::Object>());
149 auto trap = jsHandler.get(js, "getPrototypeOf"_kj);
150 auto target = proxy->GetTarget();
151 if (trap.isUndefined()) {
152 JSG_REQUIRE(target->IsObject(), TypeError, "Proxy target is not an object");
153 // Run this through getPrototype to handle the case where the target is also a proxy.
154 return JsObject(target.As<v8::Object>()).getPrototype(js);
155 }
156 JSG_REQUIRE(trap.isFunction(), TypeError, "Proxy getPrototypeOf trap is not a function");
157 v8::Local<v8::Function> fn = ((v8::Local<v8::Value>)trap).As<v8::Function>();
158 v8::Local<v8::Value> args[] = {target};
159 auto ret = JsValue(check(fn->Call(js.v8Context(), jsHandler.inner, 1, args)));
160 JSG_REQUIRE(ret.isObject() || ret.isNull(), TypeError,
161 "Proxy getPrototypeOf trap did not return an object or null");
162 // TODO(maybe): V8 performs additional checks on the returned value to
163 // see if the proxy and the target are extensible or not, and if the
164 // returned prototype is consistent with the target's prototype if they
165 // are not extensible. To strictly match v8's behavior we should do the
166 // same but (a) v8 does not expose the necessary APIs to do so, and (b)
167 // it is not clear if we actually need to perform the additional check
168 // given how we are currently using this function.
169 return ret;
170 }
171#if V8_MAJOR_VERSION >= 15 || (V8_MAJOR_VERSION == 14 && V8_MINOR_VERSION >= 7)
172 return JsValue(inner->GetPrototype());
173#else
174 // TODO(cleanup): Remove when unnecessary.
175 return JsValue(inner->GetPrototypeV2());
176#endif
177}
178 
179kj::String JsSymbol::description(Lock& js) const {
180 auto desc = inner->Description(js.v8Isolate);
181 if (desc.IsEmpty() || desc->IsUndefined()) {
182 return kj::String();
183 }
184 return kj::str(desc);
185}
186 
187void JsSet::add(Lock& js, const JsValue& value) {
188 check(inner->Add(js.v8Context(), value.inner));
189}
190 
191bool JsSet::has(Lock& js, const JsValue& value) const {
192 return check(inner->Has(js.v8Context(), value.inner));
193}
194 
195bool JsSet::delete_(Lock& js, const JsValue& value) {
196 return check(inner->Delete(js.v8Context(), value.inner));
197}
198 
199void JsSet::addAll(Lock& js, kj::ArrayPtr<const JsValue> values) {
200 for (const JsValue& value: values) {
201 check(inner->Add(js.v8Context(), value.inner));
202 }
203}
204 
205void JsSet::clear() {
206 inner->Clear();
207}
208 
209size_t JsSet::size() const {
210 return inner->Size();
211}
212 
213JsSet::operator JsArray() const {
214 return JsArray(inner->AsArray());
215}
216 
217kj::Maybe<int32_t> JsInt32::value(Lock& js) const {
218 KJ_ASSERT(!inner.IsEmpty());
219 int32_t value;
220 // The Int32Value(...) operation can fail with a JS exception, in which case
221 // we return kj::none and the error should be allowed to propagate.
222 if (inner->Int32Value(js.v8Context()).To(&value)) {
223 return value;
224 }
225 return kj::none;
226}
227 
228kj::Maybe<uint32_t> JsUint32::value(Lock& js) const {
229 KJ_ASSERT(!inner.IsEmpty());
230 uint32_t value;
231 // The Uint32Value(...) operation can fail with a JS exception, in which case
232 // we return kj::none and the error should be allowed to propagate.
233 if (inner->Uint32Value(js.v8Context()).To(&value)) {
234 return value;
235 }
236 return kj::none;
237};
238 
239kj::Maybe<int64_t> JsBigInt::toInt64(Lock& js) const {
240 KJ_ASSERT(!inner.IsEmpty());
241 bool lossless = false;
242 int64_t value = inner->Int64Value(&lossless);
243 if (!lossless) {
244 js.v8Isolate->ThrowException(js.rangeError("BigInt value does not fit in int64_t"));
245 return kj::none;
246 }
247 return value;
248}
249 
250kj::Maybe<uint64_t> JsBigInt::toUint64(Lock& js) const {
251 KJ_ASSERT(!inner.IsEmpty());
252 bool lossless = false;
253 uint64_t value = inner->Uint64Value(&lossless);
254 if (!lossless) {
255 js.v8Isolate->ThrowException(js.rangeError("BigInt value does not fit in uint64_t"));
256 return kj::none;
257 }
258 return value;
259}
260 
261kj::Maybe<double> JsNumber::value(Lock& js) const {
262 KJ_ASSERT(!inner.IsEmpty());
263 double value;
264 // The NumberValue(...) operation can fail with a JS exception, in which case
265 // we return kj::none and the error should be allowed to propagate.
266 if (inner->NumberValue(js.v8Context()).To(&value)) {
267 return value;
268 }
269 return kj::none;
270}
271 
272// ECMA-262, 15th edition, 21.1.2.5. Number.isSafeInteger
273bool JsNumber::isSafeInteger(Lock& js) const {
274 KJ_ASSERT(!inner.IsEmpty());
275 if (!inner->IsNumber()) return false;
276 KJ_IF_SOME(value, value(js)) {
277 if (std::isnan(value) || std::isinf(value) || std::trunc(value) != value) return false;
278 if (std::abs(value) <= static_cast<double>(MAX_SAFE_INTEGER)) return true;
279 }
280 return false;
281}
282 
283kj::Maybe<double> JsNumber::toSafeInteger(Lock& js) const {
284 if (isSafeInteger(js)) {
285 return inner.As<v8::Number>()->Value();
286 }
287 return kj::none;
288}
289 
290bool JsValue::isTruthy(Lock& js) const {
291 KJ_ASSERT(!inner.IsEmpty());
292 return inner->BooleanValue(js.v8Isolate);
293}
294 
295kj::String JsValue::toString(Lock& js) const {
296 KJ_ASSERT(!inner.IsEmpty());
297 return kj::str(inner);
298}
299 
300JsString JsValue::toJsString(Lock& js) const {
301 KJ_ASSERT(!inner.IsEmpty());
302 return JsString(check(inner->ToString(js.v8Context())));
303}
304 
305kj::String JsValue::typeOf(Lock& js) const {
306 KJ_ASSERT(!inner.IsEmpty());
307 return kj::str(inner->TypeOf(js.v8Isolate));
308}
309 
310#define V(Type) \
311 bool JsValue::is##Type() const { \
312 return inner->Is##Type(); \
313 }
314JS_IS_TYPES(V)
315#undef V
316 
317kj::String JsValue::toJson(Lock& js) const {
318 return kj::str(check(v8::JSON::Stringify(js.v8Context(), inner)));
319}
320 
321JsValue JsValue::fromJson(Lock& js, kj::ArrayPtr<const char> input) {
322 return JsValue(check(v8::JSON::Parse(js.v8Context(), js.str(input))));
323}
324 
325JsValue JsValue::fromJson(Lock& js, const JsValue& input) {
326 return JsValue(check(v8::JSON::Parse(js.v8Context(), input.inner.As<v8::String>())));
327}
328 
329bool JsBoolean::value(Lock& js) const {
330 return inner->BooleanValue(js.v8Isolate);
331}
332 
333uint32_t JsArray::size() const {
334 return inner->Length();
335}
336 
337JsValue JsArray::get(Lock& js, uint32_t i) const {
338 return JsValue(check(inner->Get(js.v8Context(), i)));
339}
340 
341void JsArray::add(Lock& js, const JsValue& value) {
342 check(inner->Set(js.v8Context(), size(), value.inner));
343}
344 
345JsArray::operator JsObject() const {
346 return JsObject(inner.As<v8::Object>());
347}
348 
349kj::String JsString::toString(jsg::Lock& js) const {
350 auto buf = kj::heapArray<char>(inner->Utf8LengthV2(js.v8Isolate) + 1);
351 inner->WriteUtf8V2(js.v8Isolate, buf.begin(), buf.size(), v8::String::WriteFlags::kNullTerminate);
352 return kj::String(kj::mv(buf));
353}
354 
355jsg::USVString JsString::toUSVString(Lock& js) const {
356 auto buf = kj::heapArray<char>(inner->Utf8LengthV2(js.v8Isolate) + 1);
357 inner->WriteUtf8V2(js.v8Isolate, buf.begin(), buf.size(),
358 v8::String::WriteFlags::kNullTerminate | v8::String::WriteFlags::kReplaceInvalidUtf8);
359 return jsg::USVString(kj::mv(buf));
360}
361 
362jsg::DOMString JsString::toDOMString(Lock& js) const {
363 auto buf = kj::heapArray<char>(inner->Utf8LengthV2(js.v8Isolate) + 1);
364 inner->WriteUtf8V2(js.v8Isolate, buf.begin(), buf.size(), v8::String::WriteFlags::kNullTerminate);
365 return jsg::DOMString(kj::mv(buf));
366}
367 
368int JsString::hashCode() const {
369 return kj::hashCode(inner->GetIdentityHash());
370}
371 
372JsString JsString::concat(Lock& js, const JsString& one, const JsString& two) {
373 return JsString(v8::String::Concat(js.v8Isolate, one.inner, two.inner));
374}
375 
376bool JsString::operator==(const JsString& other) const {
377 return inner->StringEquals(other.inner);
378}
379 
380JsString JsString::internalize(Lock& js) const {
381 return JsString(inner->InternalizeString(js.v8Isolate));
382}
383 
384JsString::WriteIntoStatus JsString::writeInto(
385 Lock& js, kj::ArrayPtr<char> buffer, WriteFlags options) const {
386 WriteIntoStatus result = {0, 0};
387 if (buffer.size() > 0) {
388 result.written =
389 inner->WriteUtf8V2(js.v8Isolate, buffer.begin(), buffer.size(), options, &result.read);
390 }
391 return result;
392}
393 
394JsString::WriteIntoStatus JsString::writeInto(
395 Lock& js, kj::ArrayPtr<uint16_t> buffer, WriteFlags options) const {
396 WriteIntoStatus result = {0, 0};
397 if (buffer.size() > 0) {
398 result.written = kj::min(buffer.size(), length(js));
399 inner->WriteV2(js.v8Isolate, 0, result.written, buffer.begin(), options);
400 result.read = length(js);
401 }
402 return result;
403}
404 
405JsString::WriteIntoStatus JsString::writeInto(
406 Lock& js, kj::ArrayPtr<kj::byte> buffer, WriteFlags options) const {
407 WriteIntoStatus result = {0, 0};
408 if (buffer.size() > 0) {
409 result.written = kj::min(buffer.size(), length(js));
410 inner->WriteOneByteV2(
411 js.v8Isolate, 0, kj::min(length(js), buffer.size()), buffer.begin(), options);
412 result.read = length(js);
413 }
414 return result;
415}
416 
417bool JsString::isFlat() const {
418 return inner->IsFlat();
419}
420 
421bool JsString::containsOnlyOneByte() const {
422 return inner->ContainsOnlyOneByte();
423}
424 
425kj::Maybe<JsArray> JsRegExp::operator()(Lock& js, const JsString& input) const {
426 auto result = check(inner->Exec(js.v8Context(), input));
427 if (result->IsNullOrUndefined()) return kj::none;
428 return JsArray(result.As<v8::Array>());
429}
430 
431kj::Maybe<JsArray> JsRegExp::operator()(Lock& js, kj::StringPtr input) const {
432 auto result = check(inner->Exec(js.v8Context(), js.str(input)));
433 if (result->IsNull()) return kj::none;
434 return JsArray(result.As<v8::Array>());
435}
436 
437bool JsRegExp::match(Lock& js, kj::StringPtr input) {
438 auto result = check(inner->Exec(js.v8Context(), js.str(input)));
439 return !result->IsNull();
440}
441 
442kj::String JsDate::toUTCString(jsg::Lock& js) const {
443 JsString str(inner->ToUTCString());
444 return str.toString(js);
445}
446 
447kj::String JsDate::toISOString(jsg::Lock& js) const {
448 JsString str(inner->ToISOString());
449 return str.toString(js);
450}
451 
452JsDate::operator kj::Date() const {
453 return kj::UNIX_EPOCH + (int64_t(inner->ValueOf()) * kj::MILLISECONDS);
454}
455 
456JsRegExp Lock::regexp(kj::StringPtr str, RegExpFlags flags, kj::Maybe<uint32_t> backtrackLimit) {
457 KJ_IF_SOME(limit, backtrackLimit) {
458 return JsRegExp(check(v8::RegExp::NewWithBacktrackLimit(
459 v8Context(), v8Str(v8Isolate, str), static_cast<v8::RegExp::Flags>(flags), limit)));
460 }
461 return JsRegExp(check(
462 v8::RegExp::New(v8Context(), v8Str(v8Isolate, str), static_cast<v8::RegExp::Flags>(flags))));
463}
464 
465JsObject Lock::obj(kj::ArrayPtr<const kj::StringPtr> keys, kj::ArrayPtr<JsValue> values) {
466 KJ_DASSERT(keys.size() == values.size());
467 v8::LocalVector<v8::Name> keys_(v8Isolate, keys.size());
468 v8::LocalVector<v8::Value> values_(v8Isolate, keys.size());
469 for (size_t i = 0; i < keys.size(); i++) {
470 keys_[i] = strIntern(keys[i]).inner;
471 values_[i] = values[i];
472 }
473 return JsObject(v8::Object::New(
474 v8Isolate, v8::Object::New(v8Isolate), keys_.data(), values_.data(), keys.size()));
475}
476 
477JsObject Lock::objNoProto(kj::ArrayPtr<kj::StringPtr> keys, kj::ArrayPtr<JsValue> values) {
478 KJ_DASSERT(keys.size() == values.size());
479 v8::LocalVector<v8::Name> keys_(v8Isolate, keys.size());
480 v8::LocalVector<v8::Value> values_(v8Isolate, keys.size());
481 for (size_t i = 0; i < keys.size(); i++) {
482 keys_[i] = strIntern(keys[i]).inner;
483 values_[i] = values[i];
484 }
485 return JsObject(
486 v8::Object::New(v8Isolate, v8::Null(v8Isolate), keys_.data(), values_.data(), keys.size()));
487}
488 
489JsArray Lock::arr(kj::ArrayPtr<JsValue> values) {
490 v8::LocalVector<v8::Value> items(v8Isolate, values.size());
491 for (size_t n = 0; n < values.size(); n++) {
492 items[n] = values[n];
493 }
494 return JsArray(v8::Array::New(v8Isolate, items.data(), items.size()));
495}
496 
497#define V(Name) \
498 JsSymbol Lock::symbol##Name() { \
499 return JsSymbol(v8::Symbol::Get##Name(v8Isolate)); \
500 }
501JS_V8_SYMBOLS(V)
502#undef V
503 
504JsDate Lock::date(kj::StringPtr date) {
505 v8::Local<v8::Value> converted = check(v8::Date::Parse(v8Context(), str(date)));
506 KJ_REQUIRE(converted->IsDate());
507 return JsDate(converted.As<v8::Date>());
508}
509 
510JsPromise Lock::rejectedJsPromise(jsg::JsValue exception) {
511 v8::EscapableHandleScope handleScope(v8Isolate);
512 auto context = v8Context();
513 auto resolver = check(v8::Promise::Resolver::New(context));
514 check(resolver->Reject(context, exception));
515 return JsPromise(handleScope.Escape(resolver->GetPromise()));
516}
517 
518JsPromise Lock::rejectedJsPromise(kj::Exception&& exception, ExceptionToJsOptions options) {
519 return rejectedJsPromise(exceptionToJsValue(kj::mv(exception), options).getHandle(*this));
520}
521 
522JsPromise Lock::resolvedJsPromise(jsg::JsValue value) {
523 v8::EscapableHandleScope handleScope(v8Isolate);
524 auto context = v8Context();
525 auto resolver = check(v8::Promise::Resolver::New(context));
526 check(resolver->Resolve(context, value));
527 return JsPromise(handleScope.Escape(resolver->GetPromise()));
528}
529 
530PromiseState JsPromise::state() {
531 switch (inner->State()) {
532 case v8::Promise::PromiseState::kPending:
533 return PromiseState::PENDING;
534 case v8::Promise::PromiseState::kFulfilled:
535 return PromiseState::FULFILLED;
536 case v8::Promise::PromiseState::kRejected:
537 return PromiseState::REJECTED;
538 }
539 KJ_UNREACHABLE
540}
541 
542JsValue JsPromise::result() {
543 return JsValue(inner->Result());
544}
545 
546JsValue JsProxy::target() {
547 return JsValue(inner->GetTarget());
548}
549 
550JsValue JsProxy::handler() {
551 return JsValue(inner->GetHandler());
552}
553 
554JsRef<JsValue> JsValue::addRef(Lock& js) {
555 return JsRef<JsValue>(js, *this);
556}
557 
558JsValue JsValue::structuredClone(Lock& js, kj::Maybe<kj::Array<JsValue>> maybeTransfers) {
559 return jsg::structuredClone(js, *this, kj::mv(maybeTransfers));
560}
561 
562JsMessage JsMessage::create(Lock& js, const JsValue& exception) {
563 return JsMessage(v8::Exception::CreateMessage(js.v8Isolate, exception));
564}
565 
566void JsMessage::addJsStackTrace(Lock& js, kj::Vector<kj::String>& lines) {
567 if (inner.IsEmpty()) return;
568 
569 // TODO(someday): Relying on v8::Message to pass around source locations means
570 // we can't provide the module name for errors like compiling wasm modules. We
571 // should have our own type, but it requires a refactor of how we pass around errors
572 // for script startup.
573 
574 static constexpr auto addLineCol = [](kj::StringTree str, int line, int col) {
575 if (line != v8::Message::kNoLineNumberInfo) {
576 str = kj::strTree(kj::mv(str), ":", line);
577 if (col != v8::Message::kNoColumnInfo) {
578 str = kj::strTree(kj::mv(str), ":", col);
579 }
580 }
581 return str;
582 };
583 
584 auto context = js.v8Context();
585 auto trace = inner->GetStackTrace();
586 if (trace.IsEmpty() || trace->GetFrameCount() == 0) {
587 kj::StringTree locationStr;
588 
589 auto resourceNameVal = inner->GetScriptResourceName();
590 if (resourceNameVal->IsString()) {
591 auto resourceName = resourceNameVal.As<v8::String>();
592 if (!resourceName.IsEmpty() && resourceName->Length() != 0) {
593 locationStr = kj::strTree(" at ", resourceName);
594 }
595 }
596 
597 auto lineNumber = jsg::check(inner->GetLineNumber(context));
598 auto columnNumber = jsg::check(inner->GetStartColumn(context));
599 locationStr = addLineCol(kj::mv(locationStr), lineNumber, columnNumber);
600 
601 if (locationStr.size() > 0) {
602 lines.add(locationStr.flatten());
603 }
604 } else {
605 for (auto i: kj::zeroTo(trace->GetFrameCount())) {
606 auto frame = trace->GetFrame(js.v8Isolate, i);
607 kj::StringTree locationStr;
608 
609 auto scriptName = frame->GetScriptName();
610 if (!scriptName.IsEmpty() && scriptName->Length() != 0) {
611 locationStr = kj::strTree(" at ", scriptName);
612 } else {
613 locationStr = kj::strTree(" at worker.js");
614 }
615 
616 auto lineNumber = frame->GetLineNumber();
617 auto columnNumber = frame->GetColumn();
618 locationStr = addLineCol(kj::mv(locationStr), lineNumber, columnNumber);
619 
620 auto func = frame->GetFunctionName();
621 if (!func.IsEmpty() && func->Length() != 0) {
622 locationStr = kj::strTree(kj::mv(locationStr), " in ", func);
623 }
624 
625 lines.add(locationStr.flatten());
626 }
627 }
628}
629 
630size_t JsFunction::length(Lock& js) const {
631 JsObject obj = *this;
632 auto lengthVal = obj.get(js, "length"_kj);
633 KJ_IF_SOME(num, lengthVal.tryCast<jsg::JsNumber>()) {
634 return static_cast<size_t>(num.value(js).orDefault(0));
635 }
636 return 0;
637}
638 
639JsString JsFunction::name(Lock& js) const {
640 JsObject obj = *this;
641 auto nameVal = obj.get(js, "name"_kj);
642 // It really shouldn't ever be possible for the name property to be non-string,
643 // but just in case, we check and throw if that happens.
644 return JSG_REQUIRE_NONNULL(
645 nameVal.tryCast<jsg::JsString>(), TypeError, "Function name is not a string");
646}
647 
648JsValue JsFunction::call(Lock& js, const JsValue& recv, v8::LocalVector<v8::Value>& args) const {
649 v8::Local<v8::Function> fn = *this;
650 return JsValue(check(fn->Call(js.v8Context(), recv, args.size(), args.data())));
651}
652 
653JsValue JsFunction::callNoReceiver(Lock& js, v8::LocalVector<v8::Value>& args) const {
654 return call(js, js.null(), args);
655}
656 
657uint JsFunction::hashCode() const {
658 v8::Local<v8::Function> obj = *this;
659 return kj::hashCode(obj->GetIdentityHash());
660}
661 
662BufferSource Lock::bytes(kj::Array<kj::byte> data) {
663 return BufferSource(*this, BackingStore::from(*this, kj::mv(data)));
664}
665 
666// ======================================================================================
667// JsArrayBuffer
668 
669JsArrayBuffer JsArrayBuffer::create(Lock& js, size_t length) {
670 JSG_REQUIRE(length < v8::ArrayBuffer::kMaxByteLength, RangeError, "The length is too large");
671 auto backing = v8::ArrayBuffer::NewBackingStore(js.v8Isolate, length,
672 v8::BackingStoreInitializationMode::kZeroInitialized,
673 v8::BackingStoreOnFailureMode::kReturnNull);
674 JSG_REQUIRE(backing != nullptr, RangeError, "Failed to allocate memory for ArrayBuffer");
675 return create(js, kj::mv(backing));
676}
677 
678JsArrayBuffer JsArrayBuffer::create(Lock& js, kj::ArrayPtr<const kj::byte> data) {
679 auto buf = create(js, data.size());
680 buf.asArrayPtr().copyFrom(data);
681 return buf;
682}
683 
684JsArrayBuffer JsArrayBuffer::create(Lock& js, std::unique_ptr<v8::BackingStore> backingStore) {
685 return JsArrayBuffer(v8::ArrayBuffer::New(js.v8Isolate, kj::mv(backingStore)));
686}
687 
688kj::ArrayPtr<kj::byte> JsArrayBuffer::asArrayPtr() {
689 v8::Local<v8::ArrayBuffer> inner = *this;
690 if (inner->WasDetached()) [[unlikely]] {
691 return nullptr;
692 }
693 void* data = inner->GetBackingStore()->Data();
694 size_t length = inner->ByteLength();
695 return kj::ArrayPtr(static_cast<kj::byte*>(data), length);
696}
697 
698kj::ArrayPtr<const kj::byte> JsArrayBuffer::asArrayPtr() const {
699 v8::Local<v8::ArrayBuffer> inner = *this;
700 if (inner->WasDetached()) [[unlikely]] {
701 return nullptr;
702 }
703 const void* data = inner->GetBackingStore()->Data();
704 size_t length = inner->ByteLength();
705 return kj::ArrayPtr(static_cast<const kj::byte*>(data), length);
706}
707 
708JsArrayBuffer JsArrayBuffer::slice(Lock& js, size_t newLength) const {
709 JSG_REQUIRE(newLength <= size(), RangeError, "New length exceeds buffer length");
710 auto backing = v8::ArrayBuffer::NewBackingStore(js.v8Isolate, newLength,
711 v8::BackingStoreInitializationMode::kUninitialized,
712 v8::BackingStoreOnFailureMode::kReturnNull);
713 JSG_REQUIRE(backing != nullptr, RangeError, "Failed to allocate memory for ArrayBuffer");
714 auto dest = kj::ArrayPtr(static_cast<kj::byte*>(backing->Data()), newLength);
715 v8::Local<v8::ArrayBuffer> inner = *this;
716 dest.copyFrom(
717 kj::ArrayPtr(static_cast<const kj::byte*>(inner->GetBackingStore()->Data()), newLength));
718 return JsArrayBuffer(v8::ArrayBuffer::New(js.v8Isolate, kj::mv(backing)));
719}
720 
721size_t JsArrayBuffer::size() const {
722 v8::Local<v8::ArrayBuffer> inner = *this;
723 return inner->ByteLength();
724}
725 
726kj::Array<kj::byte> JsArrayBuffer::copy() {
727 auto ptr = asArrayPtr();
728 return kj::heapArray(ptr);
729}
730 
731// ======================================================================================
732// JsArrayBufferView
733 
734size_t JsArrayBufferView::size() const {
735 v8::Local<v8::ArrayBufferView> inner = *this;
736 return inner->ByteLength();
737}
738 
739bool JsArrayBufferView::isIntegerType() const {
740 v8::Local<v8::ArrayBufferView> inner = *this;
741 return inner->IsUint8Array() || inner->IsUint8ClampedArray() || inner->IsInt8Array() ||
742 inner->IsUint16Array() || inner->IsInt16Array() || inner->IsUint32Array() ||
743 inner->IsInt32Array() || inner->IsBigInt64Array() || inner->IsBigUint64Array();
744}
745 
746// ======================================================================================
747// JsBufferSource
748 
749kj::ArrayPtr<kj::byte> JsBufferSource::asArrayPtr() {
750 v8::Local<v8::Value> inner = *this;
751 if (inner->IsArrayBuffer()) {
752 auto buf = inner.As<v8::ArrayBuffer>();
753 if (buf->WasDetached()) [[unlikely]] {
754 return nullptr;
755 }
756 return kj::ArrayPtr(static_cast<kj::byte*>(buf->Data()), buf->ByteLength());
757 } else if (inner->IsSharedArrayBuffer()) {
758 auto buf = inner.As<v8::SharedArrayBuffer>();
759 return kj::ArrayPtr(static_cast<kj::byte*>(buf->Data()), buf->ByteLength());
760 } else {
761 KJ_DASSERT(inner->IsArrayBufferView());
762 auto view = inner.As<v8::ArrayBufferView>();
763 auto buf = view->Buffer();
764 if (buf->WasDetached()) [[unlikely]] {
765 return nullptr;
766 }
767 kj::byte* data = static_cast<kj::byte*>(buf->Data()) + view->ByteOffset();
768 return kj::ArrayPtr(data, view->ByteLength());
769 }
770}
771 
772size_t JsBufferSource::size() const {
773 v8::Local<v8::Value> inner = *this;
774 if (inner->IsArrayBuffer()) {
775 auto buf = inner.As<v8::ArrayBuffer>();
776 if (buf->WasDetached()) [[unlikely]] {
777 return 0;
778 }
779 return buf->ByteLength();
780 } else if (inner->IsSharedArrayBuffer()) {
781 auto buf = inner.As<v8::SharedArrayBuffer>();
782 return buf->ByteLength();
783 } else {
784 KJ_DASSERT(inner->IsArrayBufferView());
785 auto view = inner.As<v8::ArrayBufferView>();
786 if (view->Buffer()->WasDetached()) [[unlikely]] {
787 return 0;
788 }
789 return view->ByteLength();
790 }
791}
792 
793bool JsBufferSource::isIntegerType() const {
794 v8::Local<v8::Value> inner = *this;
795 return inner->IsArrayBuffer() || inner->IsSharedArrayBuffer() || inner->IsUint8Array() ||
796 inner->IsUint8ClampedArray() || inner->IsInt8Array() || inner->IsUint16Array() ||
797 inner->IsInt16Array() || inner->IsUint32Array() || inner->IsInt32Array() ||
798 inner->IsBigInt64Array() || inner->IsBigUint64Array();
799}
800 
801bool JsBufferSource::isSharedArrayBuffer() const {
802 v8::Local<v8::Value> inner = *this;
803 return inner->IsSharedArrayBuffer();
804}
805 
806bool JsBufferSource::isArrayBuffer() const {
807 v8::Local<v8::Value> inner = *this;
808 return inner->IsArrayBuffer();
809}
810 
811bool JsBufferSource::isArrayBufferView() const {
812 v8::Local<v8::Value> inner = *this;
813 return inner->IsArrayBufferView();
814}
815 
816kj::Array<kj::byte> JsBufferSource::copy() {
817 auto ptr = asArrayPtr();
818 return kj::heapArray(ptr);
819}
820 
821bool JsBufferSource::isResizable() const {
822 v8::Local<v8::Value> inner = *this;
823 if (inner->IsArrayBuffer()) {
824 return inner.As<v8::ArrayBuffer>()->IsResizableByUserJavaScript();
825 } else if (inner->IsArrayBufferView()) {
826 return inner.As<v8::ArrayBufferView>()->Buffer()->IsResizableByUserJavaScript();
827 }
828 return false;
829}
830 
831// ======================================================================================
832// JsUint8Array
833 
834JsUint8Array JsUint8Array::create(Lock& js, size_t length) {
835 JSG_REQUIRE(length < v8::ArrayBuffer::kMaxByteLength, RangeError, "The length is too large");
836 auto backing = v8::ArrayBuffer::NewBackingStore(js.v8Isolate, length,
837 v8::BackingStoreInitializationMode::kZeroInitialized,
838 v8::BackingStoreOnFailureMode::kReturnNull);
839 JSG_REQUIRE(backing != nullptr, RangeError, "Failed to allocate memory for Uint8Array");
840 return create(js, kj::mv(backing), 0, length);
841}
842 
843JsUint8Array JsUint8Array::create(Lock& js, kj::ArrayPtr<const kj::byte> data) {
844 auto buf = create(js, data.size());
845 buf.asArrayPtr().copyFrom(data);
846 return buf;
847}
848 
849JsUint8Array JsUint8Array::create(Lock& js, JsArrayBuffer& buffer) {
850 v8::Local<v8::ArrayBuffer> ab = buffer;
851 return JsUint8Array(v8::Uint8Array::New(ab, 0, ab->ByteLength()));
852}
853 
854JsUint8Array JsUint8Array::create(
855 Lock& js, std::unique_ptr<v8::BackingStore> backingStore, size_t byteOffset, size_t length) {
856 return JsUint8Array(v8::Uint8Array::New(
857 v8::ArrayBuffer::New(js.v8Isolate, kj::mv(backingStore)), byteOffset, length));
858}
859 
860JsUint8Array JsUint8Array::slice(Lock& js, size_t newLength) const {
861 JSG_REQUIRE(newLength <= size(), RangeError, "New length exceeds array length");
862 auto u8 = v8::Uint8Array::New(inner->Buffer(), inner->ByteOffset(), newLength);
863 return JsUint8Array(u8);
864}
865 
866kj::ArrayPtr<const kj::byte> JsUint8Array::asArrayPtr() const {
867 auto buf = inner->Buffer();
868 if (buf->WasDetached()) [[unlikely]] {
869 return nullptr;
870 }
871 const kj::byte* data = static_cast<const kj::byte*>(buf->Data()) + inner->ByteOffset();
872 size_t length = inner->ByteLength();
873 return kj::ArrayPtr(data, length);
874}
875 
876size_t JsUint8Array::size() const {
877 return inner->ByteLength();
878}
879 
880kj::Array<kj::byte> JsUint8Array::copy() {
881 auto ptr = asArrayPtr();
882 return kj::heapArray(ptr);
883}
884 
885} // namespace workerd::jsg