mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 18:38:55 +00:00
783 lines
24 KiB
C++
783 lines
24 KiB
C++
|
|
|
|
#include "helpers.h"
|
|
#include "root.h"
|
|
#include "headers-handwritten.h"
|
|
#include <JavaScriptCore/JSCJSValueInlines.h>
|
|
|
|
#include "JavaScriptCore/JSCJSValue.h"
|
|
#include "JavaScriptCore/PutPropertySlot.h"
|
|
|
|
#include "wtf/SIMDUTF.h"
|
|
#include "JSDOMURL.h"
|
|
#include "DOMURL.h"
|
|
#include "ZigGlobalObject.h"
|
|
#include "IDLTypes.h"
|
|
|
|
#include <limits>
|
|
#include <wtf/Seconds.h>
|
|
#include <wtf/text/ExternalStringImpl.h>
|
|
#include <JavaScriptCore/JSONObject.h>
|
|
#include <wtf/text/AtomString.h>
|
|
#include <wtf/text/WTFString.h>
|
|
|
|
#include "JSDOMWrapperCache.h"
|
|
#include "JSDOMAttribute.h"
|
|
#include "JSDOMBinding.h"
|
|
#include "JSDOMConstructor.h"
|
|
#include "JSDOMConvertAny.h"
|
|
#include "JSDOMConvertBase.h"
|
|
#include "JSDOMConvertBoolean.h"
|
|
#include "JSDOMConvertInterface.h"
|
|
#include "JSDOMConvertStrings.h"
|
|
#include "JSDOMExceptionHandling.h"
|
|
#include "JSDOMGlobalObjectInlines.h"
|
|
#include "JSDOMOperation.h"
|
|
|
|
#include "GCDefferalContext.h"
|
|
#include "wtf/StdLibExtras.h"
|
|
#include "wtf/text/StringImpl.h"
|
|
#include "wtf/text/StringToIntegerConversion.h"
|
|
|
|
extern "C" void mi_free(void* ptr);
|
|
|
|
using namespace JSC;
|
|
extern "C" BunString BunString__fromBytes(const char* bytes, size_t length);
|
|
|
|
extern "C" [[ZIG_EXPORT(nothrow)]] bool Bun__WTFStringImpl__hasPrefix(const WTF::StringImpl* impl, const char* bytes, size_t length)
|
|
{
|
|
return impl->startsWith({ bytes, length });
|
|
}
|
|
|
|
extern "C" [[ZIG_EXPORT(nothrow)]] void Bun__WTFStringImpl__deref(WTF::StringImpl* impl)
|
|
{
|
|
impl->deref();
|
|
}
|
|
extern "C" [[ZIG_EXPORT(nothrow)]] void Bun__WTFStringImpl__ref(WTF::StringImpl* impl)
|
|
{
|
|
impl->ref();
|
|
}
|
|
|
|
extern "C" [[ZIG_EXPORT(nothrow)]] bool BunString__fromJS(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue encodedValue, BunString* bunString)
|
|
{
|
|
JSC::JSValue value = JSC::JSValue::decode(encodedValue);
|
|
*bunString = Bun::toString(globalObject, value);
|
|
return bunString->tag != BunStringTag::Dead;
|
|
}
|
|
|
|
extern "C" [[ZIG_EXPORT(nothrow)]] BunString BunString__createAtom(const char* bytes, size_t length)
|
|
{
|
|
ASSERT(simdutf::validate_ascii(bytes, length));
|
|
auto atom = tryMakeAtomString(String(StringImpl::createWithoutCopying({ bytes, length })));
|
|
return { BunStringTag::WTFStringImpl, { .wtf = atom.releaseImpl().leakRef() } };
|
|
}
|
|
|
|
extern "C" [[ZIG_EXPORT(nothrow)]] BunString BunString__tryCreateAtom(const char* bytes, size_t length)
|
|
{
|
|
if (simdutf::validate_ascii(bytes, length)) {
|
|
auto atom = tryMakeAtomString(String(StringImpl::createWithoutCopying({ bytes, length })));
|
|
if (atom.isNull())
|
|
return { BunStringTag::Dead, {} };
|
|
return { BunStringTag::WTFStringImpl, { .wtf = atom.releaseImpl().leakRef() } };
|
|
}
|
|
|
|
return { BunStringTag::Dead, {} };
|
|
}
|
|
|
|
extern "C" [[ZIG_EXPORT(zero_is_throw)]] JSC::EncodedJSValue BunString__createUTF8ForJS(JSC::JSGlobalObject* globalObject, const char* ptr, size_t length)
|
|
{
|
|
auto& vm = JSC::getVM(globalObject);
|
|
auto scope = DECLARE_THROW_SCOPE(vm);
|
|
if (length == 0) {
|
|
return JSValue::encode(jsEmptyString(vm));
|
|
}
|
|
if (simdutf::validate_ascii(ptr, length)) {
|
|
return JSValue::encode(jsString(vm, WTF::String(std::span<const LChar>(reinterpret_cast<const LChar*>(ptr), length))));
|
|
}
|
|
|
|
auto str = WTF::String::fromUTF8ReplacingInvalidSequences(std::span { reinterpret_cast<const LChar*>(ptr), length });
|
|
EXCEPTION_ASSERT(str.isNull() == !!scope.exception());
|
|
if (str.isNull()) [[unlikely]] {
|
|
throwOutOfMemoryError(globalObject, scope);
|
|
return {};
|
|
}
|
|
scope.assertNoException();
|
|
return JSValue::encode(jsString(vm, WTFMove(str)));
|
|
}
|
|
|
|
extern "C" JSC::EncodedJSValue BunString__transferToJS(BunString* bunString, JSC::JSGlobalObject* globalObject)
|
|
{
|
|
auto& vm = JSC::getVM(globalObject);
|
|
|
|
if (bunString->tag == BunStringTag::Empty || bunString->tag == BunStringTag::Dead) [[unlikely]] {
|
|
return JSValue::encode(JSC::jsEmptyString(vm));
|
|
}
|
|
|
|
if (bunString->tag == BunStringTag::WTFStringImpl) [[likely]] {
|
|
#if ASSERT_ENABLED
|
|
unsigned refCount = bunString->impl.wtf->refCount();
|
|
ASSERT(refCount > 0 && !bunString->impl.wtf->isEmpty());
|
|
#endif
|
|
auto str = bunString->toWTFString();
|
|
#if ASSERT_ENABLED
|
|
unsigned newRefCount = bunString->impl.wtf->refCount();
|
|
ASSERT(newRefCount == refCount + 1);
|
|
#endif
|
|
bunString->impl.wtf->deref();
|
|
*bunString = { .tag = BunStringTag::Dead };
|
|
return JSValue::encode(jsString(vm, WTFMove(str)));
|
|
}
|
|
|
|
WTF::String str = bunString->toWTFString();
|
|
*bunString = { .tag = BunStringTag::Dead };
|
|
return JSValue::encode(jsString(vm, WTFMove(str)));
|
|
}
|
|
|
|
// int64_t max to say "not a number"
|
|
extern "C" [[ZIG_EXPORT(nothrow)]] int64_t BunString__toInt32(const BunString* bunString)
|
|
{
|
|
if (bunString->tag == BunStringTag::Empty || bunString->tag == BunStringTag::Dead) {
|
|
return std::numeric_limits<int64_t>::max();
|
|
}
|
|
|
|
String str = bunString->toWTFString();
|
|
auto val = WTF::parseIntegerAllowingTrailingJunk<int32_t>(str);
|
|
if (val) {
|
|
return val.value();
|
|
}
|
|
|
|
return std::numeric_limits<int64_t>::max();
|
|
}
|
|
|
|
namespace Bun {
|
|
|
|
JSC::JSString* toJS(JSC::JSGlobalObject* globalObject, BunString bunString)
|
|
{
|
|
if (bunString.tag == BunStringTag::Empty || bunString.tag == BunStringTag::Dead) {
|
|
return JSC::jsEmptyString(globalObject->vm());
|
|
}
|
|
if (bunString.tag == BunStringTag::WTFStringImpl) {
|
|
#if ASSERT_ENABLED
|
|
ASSERT(bunString.impl.wtf->hasAtLeastOneRef() && !bunString.impl.wtf->isEmpty());
|
|
#endif
|
|
|
|
return JSC::jsString(globalObject->vm(), String(bunString.impl.wtf));
|
|
}
|
|
|
|
if (bunString.tag == BunStringTag::StaticZigString) {
|
|
return JSC::jsString(globalObject->vm(), Zig::toStringStatic(bunString.impl.zig));
|
|
}
|
|
|
|
return Zig::toJSStringGC(bunString.impl.zig, globalObject);
|
|
}
|
|
|
|
BunString toString(const char* bytes, size_t length)
|
|
{
|
|
return BunString__fromBytes(bytes, length);
|
|
}
|
|
|
|
BunString fromJS(JSC::JSGlobalObject* globalObject, JSValue value)
|
|
{
|
|
WTF::String str = value.toWTFString(globalObject);
|
|
if (str.isNull()) [[unlikely]] {
|
|
return { BunStringTag::Dead };
|
|
}
|
|
if (str.length() == 0) [[unlikely]] {
|
|
return { BunStringTag::Empty };
|
|
}
|
|
|
|
auto impl = str.releaseImpl();
|
|
|
|
return { BunStringTag::WTFStringImpl, { .wtf = impl.leakRef() } };
|
|
}
|
|
|
|
extern "C" [[ZIG_EXPORT(nothrow)]] void BunString__toThreadSafe(BunString* str)
|
|
{
|
|
if (str->tag == BunStringTag::WTFStringImpl) {
|
|
auto impl = str->impl.wtf->isolatedCopy();
|
|
if (impl.ptr() != str->impl.wtf) {
|
|
str->impl.wtf = &impl.leakRef();
|
|
}
|
|
}
|
|
}
|
|
|
|
BunString toString(JSC::JSGlobalObject* globalObject, JSValue value)
|
|
{
|
|
return fromJS(globalObject, value);
|
|
}
|
|
|
|
BunString toStringRef(JSC::JSGlobalObject* globalObject, JSValue value)
|
|
{
|
|
auto str = value.toWTFString(globalObject);
|
|
if (str.isNull()) [[unlikely]] {
|
|
return { BunStringTag::Dead };
|
|
}
|
|
if (str.length() == 0) [[unlikely]] {
|
|
return { BunStringTag::Empty };
|
|
}
|
|
|
|
StringImpl* impl = str.impl();
|
|
|
|
impl->ref();
|
|
|
|
return { BunStringTag::WTFStringImpl, { .wtf = impl } };
|
|
}
|
|
|
|
BunString toString(WTF::String& wtfString)
|
|
{
|
|
if (wtfString.isEmpty())
|
|
return { BunStringTag::Empty };
|
|
|
|
return { BunStringTag::WTFStringImpl, { .wtf = wtfString.impl() } };
|
|
}
|
|
BunString toString(const WTF::String& wtfString)
|
|
{
|
|
if (wtfString.isEmpty())
|
|
return { BunStringTag::Empty };
|
|
|
|
return { BunStringTag::WTFStringImpl, { .wtf = wtfString.impl() } };
|
|
}
|
|
BunString toString(WTF::StringImpl* wtfString)
|
|
{
|
|
if (wtfString->isEmpty())
|
|
return { BunStringTag::Empty };
|
|
|
|
return { BunStringTag::WTFStringImpl, { .wtf = wtfString } };
|
|
}
|
|
|
|
BunString toStringRef(WTF::String& wtfString)
|
|
{
|
|
if (wtfString.isEmpty())
|
|
return { BunStringTag::Empty };
|
|
|
|
wtfString.impl()->ref();
|
|
return { BunStringTag::WTFStringImpl, { .wtf = wtfString.impl() } };
|
|
}
|
|
BunString toStringRef(const WTF::String& wtfString)
|
|
{
|
|
if (wtfString.isEmpty())
|
|
return { BunStringTag::Empty };
|
|
|
|
wtfString.impl()->ref();
|
|
return { BunStringTag::WTFStringImpl, { .wtf = wtfString.impl() } };
|
|
}
|
|
BunString toStringRef(WTF::StringImpl* wtfString)
|
|
{
|
|
if (wtfString->isEmpty())
|
|
return { BunStringTag::Empty };
|
|
|
|
wtfString->ref();
|
|
|
|
return { BunStringTag::WTFStringImpl, { .wtf = wtfString } };
|
|
}
|
|
|
|
BunString toStringView(StringView view)
|
|
{
|
|
return {
|
|
BunStringTag::ZigString,
|
|
{ .zig = toZigString(view) }
|
|
};
|
|
}
|
|
|
|
}
|
|
|
|
extern "C" JSC::EncodedJSValue BunString__toJS(JSC::JSGlobalObject* globalObject, const BunString* bunString)
|
|
{
|
|
return JSValue::encode(Bun::toJS(globalObject, *bunString));
|
|
}
|
|
|
|
extern "C" [[ZIG_EXPORT(nothrow)]] BunString BunString__fromUTF16Unitialized(size_t length)
|
|
{
|
|
ASSERT(length > 0);
|
|
std::span<char16_t> ptr;
|
|
auto impl = WTF::StringImpl::tryCreateUninitialized(length, ptr);
|
|
if (!impl) [[unlikely]] {
|
|
return { .tag = BunStringTag::Dead };
|
|
}
|
|
return { BunStringTag::WTFStringImpl, { .wtf = impl.leakRef() } };
|
|
}
|
|
|
|
extern "C" [[ZIG_EXPORT(nothrow)]] BunString BunString__fromLatin1Unitialized(size_t length)
|
|
{
|
|
ASSERT(length > 0);
|
|
std::span<LChar> ptr;
|
|
auto impl = WTF::StringImpl::tryCreateUninitialized(length, ptr);
|
|
if (!impl) [[unlikely]] {
|
|
return { .tag = BunStringTag::Dead };
|
|
}
|
|
return { BunStringTag::WTFStringImpl, { .wtf = impl.leakRef() } };
|
|
}
|
|
|
|
extern "C" BunString BunString__fromUTF8(const char* bytes, size_t length)
|
|
{
|
|
ASSERT(length > 0);
|
|
if (simdutf::validate_utf8(bytes, length)) {
|
|
size_t u16Length = simdutf::utf16_length_from_utf8(bytes, length);
|
|
std::span<char16_t> ptr;
|
|
auto impl = WTF::StringImpl::tryCreateUninitialized(static_cast<unsigned int>(u16Length), ptr);
|
|
if (!impl) [[unlikely]] {
|
|
return { .tag = BunStringTag::Dead };
|
|
}
|
|
RELEASE_ASSERT(simdutf::convert_utf8_to_utf16(bytes, length, ptr.data()) == u16Length);
|
|
return { BunStringTag::WTFStringImpl, { .wtf = impl.leakRef() } };
|
|
}
|
|
|
|
auto str = WTF::String::fromUTF8ReplacingInvalidSequences(std::span { reinterpret_cast<const LChar*>(bytes), length });
|
|
if (str.isNull()) [[unlikely]] {
|
|
return { .tag = BunStringTag::Dead };
|
|
}
|
|
auto impl = str.releaseImpl();
|
|
return Bun::toString(impl.leakRef());
|
|
}
|
|
|
|
extern "C" [[ZIG_EXPORT(nothrow)]] BunString BunString__fromLatin1(const char* bytes, size_t length)
|
|
{
|
|
ASSERT(length > 0);
|
|
std::span<LChar> ptr;
|
|
auto impl = WTF::StringImpl::tryCreateUninitialized(length, ptr);
|
|
if (!impl) [[unlikely]] {
|
|
return { .tag = BunStringTag::Dead };
|
|
}
|
|
memcpy(ptr.data(), bytes, length);
|
|
|
|
return { BunStringTag::WTFStringImpl, { .wtf = impl.leakRef() } };
|
|
}
|
|
|
|
extern "C" [[ZIG_EXPORT(nothrow)]] BunString BunString__fromUTF16ToLatin1(const char16_t* bytes, size_t length)
|
|
{
|
|
ASSERT(length > 0);
|
|
ASSERT_WITH_MESSAGE(simdutf::validate_utf16le(bytes, length), "This function only accepts ascii UTF16 strings");
|
|
size_t outLength = simdutf::latin1_length_from_utf16(length);
|
|
std::span<LChar> ptr;
|
|
auto impl = WTF::StringImpl::tryCreateUninitialized(outLength, ptr);
|
|
if (!impl) [[unlikely]] {
|
|
return { BunStringTag::Dead };
|
|
}
|
|
|
|
size_t latin1_length = simdutf::convert_valid_utf16le_to_latin1(bytes, length, reinterpret_cast<char*>(ptr.data()));
|
|
ASSERT_WITH_MESSAGE(latin1_length == outLength, "Failed to convert UTF16 to Latin1");
|
|
return { BunStringTag::WTFStringImpl, { .wtf = impl.leakRef() } };
|
|
}
|
|
|
|
extern "C" [[ZIG_EXPORT(nothrow)]] BunString BunString__fromUTF16(const char16_t* bytes, size_t length)
|
|
{
|
|
ASSERT(length > 0);
|
|
std::span<char16_t> ptr;
|
|
auto impl = WTF::StringImpl::tryCreateUninitialized(length, ptr);
|
|
if (!impl) [[unlikely]] {
|
|
return { .tag = BunStringTag::Dead };
|
|
}
|
|
memcpy(ptr.data(), bytes, length * sizeof(char16_t));
|
|
return { BunStringTag::WTFStringImpl, { .wtf = impl.leakRef() } };
|
|
}
|
|
|
|
extern "C" [[ZIG_EXPORT(nothrow)]] BunString BunString__fromBytes(const char* bytes, size_t length)
|
|
{
|
|
ASSERT(length > 0);
|
|
if (simdutf::validate_ascii(bytes, length)) {
|
|
return BunString__fromLatin1(bytes, length);
|
|
}
|
|
|
|
return BunString__fromUTF8(bytes, length);
|
|
}
|
|
|
|
extern "C" BunString BunString__createStaticExternal(const char* bytes, size_t length, bool isLatin1)
|
|
{
|
|
Ref<WTF::ExternalStringImpl> impl = isLatin1 ? WTF::ExternalStringImpl::createStatic({ reinterpret_cast<const LChar*>(bytes), length }) :
|
|
|
|
WTF::ExternalStringImpl::createStatic({ reinterpret_cast<const char16_t*>(bytes), length });
|
|
|
|
return { BunStringTag::WTFStringImpl, { .wtf = &impl.leakRef() } };
|
|
}
|
|
|
|
extern "C" BunString BunString__createExternal(const char* bytes, size_t length, bool isLatin1, void* ctx, void (*callback)(void* arg0, void* arg1, size_t arg2))
|
|
{
|
|
Ref<WTF::ExternalStringImpl> impl = isLatin1 ? WTF::ExternalStringImpl::create({ reinterpret_cast<const LChar*>(bytes), length }, ctx, callback) :
|
|
|
|
WTF::ExternalStringImpl::create({ reinterpret_cast<const char16_t*>(bytes), length }, ctx, callback);
|
|
|
|
return { BunStringTag::WTFStringImpl, { .wtf = &impl.leakRef() } };
|
|
}
|
|
|
|
extern "C" [[ZIG_EXPORT(zero_is_throw)]] JSC::EncodedJSValue BunString__toJSON(
|
|
JSC::JSGlobalObject* globalObject,
|
|
BunString* bunString)
|
|
{
|
|
auto scope = DECLARE_THROW_SCOPE(globalObject->vm());
|
|
JSC::JSValue result = JSC::JSONParse(globalObject, bunString->toWTFString());
|
|
|
|
if (!result && !scope.exception()) {
|
|
scope.throwException(globalObject, createSyntaxError(globalObject, "Failed to parse JSON"_s));
|
|
}
|
|
|
|
RETURN_IF_EXCEPTION(scope, {});
|
|
|
|
return JSC::JSValue::encode(result);
|
|
}
|
|
|
|
extern "C" JSC::EncodedJSValue BunString__createArray(
|
|
JSC::JSGlobalObject* globalObject,
|
|
const BunString* ptr, size_t length)
|
|
{
|
|
if (length == 0)
|
|
return JSValue::encode(JSC::constructEmptyArray(globalObject, nullptr));
|
|
|
|
auto& vm = JSC::getVM(globalObject);
|
|
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
|
|
|
// Using tryCreateUninitialized here breaks stuff..
|
|
// https://github.com/oven-sh/bun/issues/3931
|
|
JSC::JSArray* array = constructEmptyArray(globalObject, nullptr, length);
|
|
RETURN_IF_EXCEPTION(throwScope, {});
|
|
|
|
for (size_t i = 0; i < length; ++i) {
|
|
array->putDirectIndex(globalObject, i, Bun::toJS(globalObject, *ptr++));
|
|
RETURN_IF_EXCEPTION(throwScope, {});
|
|
}
|
|
|
|
return JSValue::encode(array);
|
|
}
|
|
|
|
extern "C" [[ZIG_EXPORT(nothrow)]] void BunString__toWTFString(BunString* bunString)
|
|
{
|
|
WTF::String str;
|
|
if (bunString->tag == BunStringTag::ZigString) {
|
|
if (Zig::isTaggedExternalPtr(bunString->impl.zig.ptr)) {
|
|
str = Zig::toString(bunString->impl.zig);
|
|
} else {
|
|
str = Zig::toStringCopy(bunString->impl.zig);
|
|
}
|
|
|
|
} else if (bunString->tag == BunStringTag::StaticZigString) {
|
|
str = Zig::toStringStatic(bunString->impl.zig);
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
auto impl = str.releaseImpl();
|
|
bunString->impl.wtf = impl.leakRef();
|
|
bunString->tag = BunStringTag::WTFStringImpl;
|
|
}
|
|
|
|
extern "C" BunString URL__getFileURLString(BunString* filePath)
|
|
{
|
|
return Bun::toStringRef(WTF::URL::fileURLWithFileSystemPath(filePath->toWTFString()).stringWithoutFragmentIdentifier());
|
|
}
|
|
|
|
extern "C" size_t URL__originLength(const char* latin1_slice, size_t len)
|
|
{
|
|
WTF::String string = WTF::StringView(latin1_slice, len, true).toString();
|
|
if (!string)
|
|
return 0;
|
|
WTF::URL url(string);
|
|
if (!url.isValid())
|
|
return 0;
|
|
return url.pathStart();
|
|
}
|
|
|
|
extern "C" JSC::EncodedJSValue BunString__toJSDOMURL(JSC::JSGlobalObject* lexicalGlobalObject, BunString* bunString)
|
|
{
|
|
auto& globalObject = *jsCast<Zig::GlobalObject*>(lexicalGlobalObject);
|
|
auto& vm = globalObject.vm();
|
|
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
|
|
|
auto str = bunString->toWTFString(BunString::ZeroCopy);
|
|
|
|
auto object = WebCore::DOMURL::create(str, String());
|
|
auto jsValue = WebCore::toJSNewlyCreated<WebCore::IDLInterface<WebCore::DOMURL>>(*lexicalGlobalObject, globalObject, throwScope, WTFMove(object));
|
|
auto* jsDOMURL = jsCast<WebCore::JSDOMURL*>(jsValue.asCell());
|
|
vm.heap.reportExtraMemoryAllocated(jsDOMURL, jsDOMURL->wrapped().memoryCostForGC());
|
|
RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(jsValue));
|
|
}
|
|
|
|
extern "C" WTF::URL* URL__fromJS(EncodedJSValue encodedValue, JSC::JSGlobalObject* globalObject)
|
|
{
|
|
auto throwScope = DECLARE_THROW_SCOPE(globalObject->vm());
|
|
JSC::JSValue value = JSC::JSValue::decode(encodedValue);
|
|
auto str = value.toWTFString(globalObject);
|
|
RETURN_IF_EXCEPTION(throwScope, nullptr);
|
|
if (str.isEmpty()) {
|
|
return nullptr;
|
|
}
|
|
|
|
auto url = WTF::URL(str);
|
|
if (!url.isValid() || url.isNull())
|
|
return nullptr;
|
|
|
|
return new WTF::URL(WTFMove(url));
|
|
}
|
|
|
|
extern "C" BunString URL__getHrefFromJS(EncodedJSValue encodedValue, JSC::JSGlobalObject* globalObject)
|
|
{
|
|
auto throwScope = DECLARE_THROW_SCOPE(globalObject->vm());
|
|
JSC::JSValue value = JSC::JSValue::decode(encodedValue);
|
|
auto str = value.toWTFString(globalObject);
|
|
RETURN_IF_EXCEPTION(throwScope, { BunStringTag::Dead });
|
|
if (str.isEmpty()) {
|
|
return { BunStringTag::Dead };
|
|
}
|
|
|
|
auto url = WTF::URL(str);
|
|
if (!url.isValid() || url.isEmpty())
|
|
return { BunStringTag::Dead };
|
|
|
|
return Bun::toStringRef(url.string());
|
|
}
|
|
|
|
extern "C" BunString URL__getHref(BunString* input)
|
|
{
|
|
auto&& str = input->toWTFString();
|
|
auto url = WTF::URL(str);
|
|
if (!url.isValid() || url.isEmpty())
|
|
return { BunStringTag::Dead };
|
|
|
|
return Bun::toStringRef(url.string());
|
|
}
|
|
|
|
extern "C" BunString URL__pathFromFileURL(BunString* input)
|
|
{
|
|
auto&& str = input->toWTFString();
|
|
auto url = WTF::URL(str);
|
|
if (!url.isValid() || url.isEmpty())
|
|
return { BunStringTag::Dead };
|
|
|
|
return Bun::toStringRef(url.fileSystemPath());
|
|
}
|
|
|
|
extern "C" BunString URL__getHrefJoin(BunString* baseStr, BunString* relativeStr)
|
|
{
|
|
auto base = baseStr->toWTFString();
|
|
auto relative = relativeStr->toWTFString();
|
|
auto url = WTF::URL(WTF::URL(base), relative);
|
|
if (!url.isValid() || url.isEmpty())
|
|
return { BunStringTag::Dead };
|
|
|
|
return Bun::toStringRef(url.string());
|
|
}
|
|
|
|
extern "C" WTF::URL* URL__fromString(BunString* input)
|
|
{
|
|
auto&& str = input->toWTFString();
|
|
auto url = WTF::URL(str);
|
|
if (!url.isValid())
|
|
return nullptr;
|
|
|
|
return new WTF::URL(WTFMove(url));
|
|
}
|
|
|
|
extern "C" BunString URL__protocol(WTF::URL* url)
|
|
{
|
|
return Bun::toStringRef(url->protocol().toStringWithoutCopying());
|
|
}
|
|
|
|
extern "C" void URL__deinit(WTF::URL* url)
|
|
{
|
|
delete url;
|
|
}
|
|
|
|
extern "C" BunString URL__href(WTF::URL* url)
|
|
{
|
|
return Bun::toStringRef(url->string());
|
|
}
|
|
|
|
extern "C" BunString URL__username(WTF::URL* url)
|
|
{
|
|
return Bun::toStringRef(url->user());
|
|
}
|
|
|
|
extern "C" BunString URL__password(WTF::URL* url)
|
|
{
|
|
return Bun::toStringRef(url->password());
|
|
}
|
|
|
|
extern "C" BunString URL__search(WTF::URL* url)
|
|
{
|
|
return Bun::toStringRef(url->query().toStringWithoutCopying());
|
|
}
|
|
|
|
extern "C" BunString URL__host(WTF::URL* url)
|
|
{
|
|
return Bun::toStringRef(url->host().toStringWithoutCopying());
|
|
}
|
|
extern "C" BunString URL__hostname(WTF::URL* url)
|
|
{
|
|
return Bun::toStringRef(url->hostAndPort());
|
|
}
|
|
|
|
extern "C" uint32_t URL__port(WTF::URL* url)
|
|
{
|
|
auto port = url->port();
|
|
|
|
if (port.has_value()) {
|
|
return port.value();
|
|
}
|
|
|
|
return std::numeric_limits<uint32_t>::max();
|
|
}
|
|
|
|
extern "C" BunString URL__pathname(WTF::URL* url)
|
|
{
|
|
return Bun::toStringRef(url->path().toStringWithoutCopying());
|
|
}
|
|
|
|
size_t BunString::utf8ByteLength(const WTF::String& str)
|
|
{
|
|
if (str.isEmpty())
|
|
return 0;
|
|
|
|
if (str.is8Bit()) {
|
|
const auto s = str.span8();
|
|
return simdutf::utf8_length_from_latin1(reinterpret_cast<const char*>(s.data()), static_cast<size_t>(s.size()));
|
|
} else {
|
|
const auto s = str.span16();
|
|
return simdutf::utf8_length_from_utf16(reinterpret_cast<const char16_t*>(s.data()), static_cast<size_t>(s.size()));
|
|
}
|
|
}
|
|
|
|
WTF::String BunString::toWTFString() const
|
|
{
|
|
if (this->tag == BunStringTag::ZigString) {
|
|
if (Zig::isTaggedExternalPtr(this->impl.zig.ptr)) {
|
|
return Zig::toString(this->impl.zig);
|
|
} else {
|
|
return Zig::toStringCopy(this->impl.zig);
|
|
}
|
|
} else if (this->tag == BunStringTag::StaticZigString) {
|
|
return Zig::toStringCopy(this->impl.zig);
|
|
} else if (this->tag == BunStringTag::WTFStringImpl) {
|
|
return WTF::String(this->impl.wtf);
|
|
}
|
|
|
|
return WTF::String();
|
|
}
|
|
|
|
WTF::String BunString::toWTFString(ZeroCopyTag) const
|
|
{
|
|
if (this->tag == BunStringTag::ZigString) {
|
|
if (Zig::isTaggedUTF8Ptr(this->impl.zig.ptr)) {
|
|
return Zig::toStringCopy(this->impl.zig);
|
|
} else {
|
|
return Zig::toString(this->impl.zig);
|
|
}
|
|
} else if (this->tag == BunStringTag::StaticZigString) {
|
|
return Zig::toStringStatic(this->impl.zig);
|
|
} else if (this->tag == BunStringTag::WTFStringImpl) {
|
|
ASSERT(this->impl.wtf->refCount() > 0 && !this->impl.wtf->isEmpty());
|
|
return WTF::String(this->impl.wtf);
|
|
}
|
|
|
|
return WTF::String();
|
|
}
|
|
|
|
WTF::String BunString::toWTFString(NonNullTag) const
|
|
{
|
|
WTF::String res = toWTFString(ZeroCopy);
|
|
if (res.isNull()) {
|
|
// TODO(dylan-conway): also use emptyString in toWTFString(ZeroCopy) and toWTFString. This will
|
|
// require reviewing each call site for isNull() checks and most likely changing them to isEmpty()
|
|
return WTF::emptyString();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
WTF::String BunString::transferToWTFString()
|
|
{
|
|
if (this->tag == BunStringTag::ZigString) {
|
|
if (Zig::isTaggedUTF8Ptr(this->impl.zig.ptr)) {
|
|
auto str = Zig::toStringCopy(this->impl.zig);
|
|
*this = Zig::BunStringEmpty;
|
|
return str;
|
|
} else {
|
|
auto str = Zig::toString(this->impl.zig);
|
|
*this = Zig::BunStringEmpty;
|
|
return str;
|
|
}
|
|
} else if (this->tag == BunStringTag::StaticZigString) {
|
|
auto str = Zig::toStringStatic(this->impl.zig);
|
|
*this = Zig::BunStringEmpty;
|
|
return str;
|
|
} else if (this->tag == BunStringTag::WTFStringImpl) {
|
|
ASSERT(this->impl.wtf->refCount() > 0 && !this->impl.wtf->isEmpty());
|
|
|
|
auto str = WTF::String(this->impl.wtf);
|
|
this->impl.wtf->deref();
|
|
*this = Zig::BunStringEmpty;
|
|
return str;
|
|
}
|
|
|
|
return WTF::String();
|
|
}
|
|
|
|
extern "C" BunString BunString__createExternalGloballyAllocatedLatin1(
|
|
const LChar* bytes,
|
|
size_t length)
|
|
{
|
|
ASSERT(length > 0);
|
|
Ref<WTF::ExternalStringImpl> impl = WTF::ExternalStringImpl::create({ bytes, length }, nullptr, [](void*, void* ptr, size_t) {
|
|
#if __has_feature(address_sanitizer)
|
|
// mimalloc is disabled in ASAN builds
|
|
free(ptr);
|
|
#else
|
|
mi_free(ptr);
|
|
#endif
|
|
});
|
|
return { BunStringTag::WTFStringImpl, { .wtf = &impl.leakRef() } };
|
|
}
|
|
|
|
extern "C" BunString BunString__createExternalGloballyAllocatedUTF16(
|
|
const char16_t* bytes,
|
|
size_t length)
|
|
{
|
|
ASSERT(length > 0);
|
|
Ref<WTF::ExternalStringImpl> impl = WTF::ExternalStringImpl::create({ bytes, length }, nullptr, [](void*, void* ptr, size_t) {
|
|
#if __has_feature(address_sanitizer)
|
|
// mimalloc is disabled in ASAN builds
|
|
free(ptr);
|
|
#else
|
|
mi_free(ptr);
|
|
#endif
|
|
});
|
|
return { BunStringTag::WTFStringImpl, { .wtf = &impl.leakRef() } };
|
|
}
|
|
|
|
extern "C" [[ZIG_EXPORT(nothrow)]] bool WTFStringImpl__isThreadSafe(
|
|
const WTF::StringImpl* wtf)
|
|
{
|
|
if (wtf->bufferOwnership() != StringImpl::BufferOwnership::BufferInternal)
|
|
return false;
|
|
|
|
return !(wtf->isSymbol() || wtf->isAtom());
|
|
}
|
|
|
|
extern "C" [[ZIG_EXPORT(nothrow)]] void Bun__WTFStringImpl__ensureHash(WTF::StringImpl* str)
|
|
{
|
|
str->hash();
|
|
}
|
|
|
|
extern "C" void JSC__JSValue__putBunString(
|
|
JSC::EncodedJSValue encodedTarget,
|
|
JSC::JSGlobalObject* global,
|
|
const BunString* key,
|
|
JSC::EncodedJSValue encodedValue)
|
|
{
|
|
JSC::JSObject* target = JSC::JSValue::decode(encodedTarget).getObject();
|
|
JSC::JSValue value = JSC::JSValue::decode(encodedValue);
|
|
auto& vm = global->vm();
|
|
WTF::String str = key->tag == BunStringTag::Empty ? WTF::emptyString() : key->toWTFString();
|
|
Identifier id = Identifier::fromString(vm, str);
|
|
target->putDirect(vm, id, value, 0);
|
|
}
|
|
|
|
bool BunString::isEmpty() const
|
|
{
|
|
switch (this->tag) {
|
|
case BunStringTag::WTFStringImpl:
|
|
return impl.wtf->isEmpty();
|
|
case BunStringTag::ZigString:
|
|
case BunStringTag::StaticZigString:
|
|
return impl.zig.len == 0;
|
|
default:
|
|
return true;
|
|
}
|
|
}
|