Skip to content

Commit

Permalink
Add TextDecoderStream, TextEncoderStream, and `TextDecoder.decode…
Browse files Browse the repository at this point in the history
…("", { stream: true})` (#13115)
  • Loading branch information
dylan-conway authored Aug 7, 2024
1 parent d449697 commit 9f7c6e3
Show file tree
Hide file tree
Showing 30 changed files with 2,214 additions and 255 deletions.
4 changes: 2 additions & 2 deletions docs/runtime/nodejs-apis.md
Original file line number Diff line number Diff line change
Expand Up @@ -413,15 +413,15 @@ The table below lists all globals implemented by Node.js and Bun's current compa

### [`TextDecoderStream`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoderStream)

🔴 Not implemented.
🟢 Fully implemented.

### [`TextEncoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder)

🟢 Fully implemented.

### [`TextEncoderStream`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoderStream)

🔴 Not implemented.
🟢 Fully implemented.

### [`TransformStream`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream)

Expand Down
1 change: 1 addition & 0 deletions src/bun.js/bindings/JSDOMWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class JSDOMWrapper : public JSDOMObject {
using DOMWrapped = ImplementationClass;

ImplementationClass& wrapped() const { return m_wrapped; }
Ref<ImplementationClass> protectedWrapped() const { return m_wrapped; }
static ptrdiff_t offsetOfWrapped() { return OBJECT_OFFSETOF(JSDOMWrapper, m_wrapped); }
constexpr static bool hasCustomPtrTraits() { return !std::is_same_v<PtrTraits, RawPtrTraits<ImplementationClass>>; };

Expand Down
7 changes: 7 additions & 0 deletions src/bun.js/bindings/ZigGlobalObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@
#include "JSSQLStatement.h"
#include "JSStringDecoder.h"
#include "JSTextEncoder.h"
#include "JSTextEncoderStream.h"
#include "JSTextEncoderStreamEncoder.h"
#include "JSTextDecoderStream.h"
#include "JSTransformStream.h"
#include "JSTransformStreamDefaultController.h"
#include "JSURLSearchParams.h"
Expand Down Expand Up @@ -1269,6 +1272,9 @@ WEBCORE_GENERATED_CONSTRUCTOR_GETTER(ReadableStreamDefaultController)
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(ReadableStreamDefaultReader)
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(SubtleCrypto);
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(TextEncoder);
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(TextEncoderStream);
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(TextEncoderStreamEncoder);
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(TextDecoderStream);
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(TransformStream)
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(TransformStreamDefaultController)
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(URLSearchParams);
Expand Down Expand Up @@ -3378,6 +3384,7 @@ void GlobalObject::addBuiltinGlobals(JSC::VM& vm)
GlobalPropertyInfo(builtinNames.internalModuleRegistryPrivateName(), this->internalModuleRegistry(), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
GlobalPropertyInfo(builtinNames.processBindingConstantsPrivateName(), this->processBindingConstants(), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
GlobalPropertyInfo(builtinNames.requireMapPrivateName(), this->requireMap(), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly | 0),
GlobalPropertyInfo(builtinNames.TextEncoderStreamEncoderPrivateName(), TextEncoderStreamEncoderConstructorCallback(vm, this), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly | 0),
};
addStaticGlobals(staticGlobals, std::size(staticGlobals));

Expand Down
2 changes: 2 additions & 0 deletions src/bun.js/bindings/ZigGlobalObject.lut.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@
ReadableStreamDefaultReader ReadableStreamDefaultReaderConstructorCallback PropertyCallback
SubtleCrypto SubtleCryptoConstructorCallback PropertyCallback
TextEncoder TextEncoderConstructorCallback PropertyCallback
TextEncoderStream TextEncoderStreamConstructorCallback PropertyCallback
TextDecoderStream TextDecoderStreamConstructorCallback PropertyCallback
TransformStream TransformStreamConstructorCallback PropertyCallback
TransformStreamDefaultController TransformStreamDefaultControllerConstructorCallback PropertyCallback
URL DOMURLConstructorCallback PropertyCallback
Expand Down
8 changes: 5 additions & 3 deletions src/bun.js/bindings/bindings.zig
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ pub const ZigString = extern struct {

pub fn dupeForJS(utf8: []const u8, allocator: std.mem.Allocator) !ZigString {
if (try strings.toUTF16Alloc(allocator, utf8, false, false)) |utf16| {
var out = ZigString.init16(utf16);
var out = ZigString.initUTF16(utf16);
out.mark();
out.markUTF16();
return out;
Expand Down Expand Up @@ -629,8 +629,10 @@ pub const ZigString = extern struct {
return shim.cppFn("toAtomicValue", .{ this, globalThis });
}

pub fn init16(slice_: []const u16) ZigString {
var out = ZigString{ ._unsafe_ptr_do_not_use = std.mem.sliceAsBytes(slice_).ptr, .len = slice_.len };
pub fn initUTF16(items: []const u16) ZigString {
var out = ZigString.Empty;
out._unsafe_ptr_do_not_use = @ptrCast(items.ptr);
out.len = items.len;
out.markUTF16();
return out;
}
Expand Down
21 changes: 13 additions & 8 deletions src/bun.js/bindings/napi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
#include <JavaScriptCore/JSArrayBuffer.h>
#include <JavaScriptCore/FunctionPrototype.h>
#include "CommonJSModuleRecord.h"
#include "wtf/text/ASCIIFastPath.h"

// #include <iostream>
using namespace JSC;
Expand Down Expand Up @@ -723,12 +724,15 @@ extern "C" napi_status napi_create_arraybuffer(napi_env env,
// it doesn't copy the string
// but it's only safe to use if we are not setting a property
// because we can't guarantee the lifetime of it
#define PROPERTY_NAME_FROM_UTF8(identifierName) \
size_t utf8Len = strlen(utf8name); \
JSC::PropertyName identifierName = LIKELY(charactersAreAllASCII(std::span { reinterpret_cast<const LChar*>(utf8name), utf8Len })) ? JSC::PropertyName(JSC::Identifier::fromString(vm, WTF::String(WTF::StringImpl::createWithoutCopying({ utf8name, utf8Len })))) : JSC::PropertyName(JSC::Identifier::fromString(vm, WTF::String::fromUTF8(utf8name)));
#define PROPERTY_NAME_FROM_UTF8(identifierName) \
size_t utf8Len = strlen(utf8Name); \
WTF::String nameString = LIKELY(WTF::charactersAreAllASCII(std::span { reinterpret_cast<const LChar*>(utf8Name), utf8Len })) \
? WTF::String(WTF::StringImpl::createWithoutCopying({ utf8Name, utf8Len })) \
: WTF::String::fromUTF8(utf8Name); \
JSC::PropertyName identifierName = JSC::Identifier::fromString(vm, nameString);

extern "C" napi_status napi_has_named_property(napi_env env, napi_value object,
const char* utf8name,
const char* utf8Name,
bool* result)
{
NAPI_PREMABLE
Expand All @@ -740,22 +744,23 @@ extern "C" napi_status napi_has_named_property(napi_env env, napi_value object,
auto globalObject = toJS(env);
auto& vm = globalObject->vm();

auto* target = toJS(object).getObject();
JSObject* target = toJS(object).getObject();
if (UNLIKELY(!target)) {
return napi_object_expected;
}

PROPERTY_NAME_FROM_UTF8(name);

auto scope = DECLARE_CATCH_SCOPE(vm);
*result = !!target->getIfPropertyExists(globalObject, name);
PropertySlot slot(target, PropertySlot::InternalMethodType::HasProperty);
*result = target->getPropertySlot(globalObject, name, slot);
RETURN_IF_EXCEPTION(scope, napi_generic_failure);

scope.clearException();
return napi_ok;
}
extern "C" napi_status napi_get_named_property(napi_env env, napi_value object,
const char* utf8name,
const char* utf8Name,
napi_value* result)
{
NAPI_PREMABLE
Expand All @@ -767,7 +772,7 @@ extern "C" napi_status napi_get_named_property(napi_env env, napi_value object,
auto globalObject = toJS(env);
auto& vm = globalObject->vm();

auto* target = toJS(object).getObject();
JSObject* target = toJS(object).getObject();
if (UNLIKELY(!target)) {
return napi_object_expected;
}
Expand Down
6 changes: 3 additions & 3 deletions src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -476,11 +476,11 @@ class DOMClientIsoSubspaces {
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForStaticRange;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForText;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForTextDecoder;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForTextDecoderStream;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForTextDecoderStream;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForTextDecoderStreamDecoder;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForTextEncoder;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForTextEncoderStream;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForTextEncoderStreamEncoder;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForTextEncoderStream;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForTextEncoderStreamEncoder;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForTextEvent;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForTransitionEvent;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForTreeWalker;
Expand Down
6 changes: 3 additions & 3 deletions src/bun.js/bindings/webcore/DOMIsoSubspaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -469,11 +469,11 @@ class DOMIsoSubspaces {
// std::unique_ptr<IsoSubspace> m_subspaceForStaticRange;
// std::unique_ptr<IsoSubspace> m_subspaceForText;
// std::unique_ptr<IsoSubspace> m_subspaceForTextDecoder;
// std::unique_ptr<IsoSubspace> m_subspaceForTextDecoderStream;
std::unique_ptr<IsoSubspace> m_subspaceForTextDecoderStream;
// std::unique_ptr<IsoSubspace> m_subspaceForTextDecoderStreamDecoder;
std::unique_ptr<IsoSubspace> m_subspaceForTextEncoder;
// std::unique_ptr<IsoSubspace> m_subspaceForTextEncoderStream;
// std::unique_ptr<IsoSubspace> m_subspaceForTextEncoderStreamEncoder;
std::unique_ptr<IsoSubspace> m_subspaceForTextEncoderStream;
std::unique_ptr<IsoSubspace> m_subspaceForTextEncoderStreamEncoder;
// std::unique_ptr<IsoSubspace> m_subspaceForTextEvent;
// std::unique_ptr<IsoSubspace> m_subspaceForTransitionEvent;
// std::unique_ptr<IsoSubspace> m_subspaceForTreeWalker;
Expand Down
170 changes: 170 additions & 0 deletions src/bun.js/bindings/webcore/JSTextDecoderStream.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/*
This file is part of the WebKit open source project.
This file has been generated by generate-bindings.pl. DO NOT MODIFY!
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/

#include "config.h"
#include "JSTextDecoderStream.h"

#include "ExtendedDOMClientIsoSubspaces.h"
#include "ExtendedDOMIsoSubspaces.h"
#include "JSDOMAttribute.h"
#include "JSDOMBinding.h"
#include "JSDOMBuiltinConstructor.h"
#include "JSDOMExceptionHandling.h"
#include "JSDOMGlobalObjectInlines.h"
#include "JSDOMWrapperCache.h"
// #include "TextDecoderStreamBuiltins.h"
#include "WebCoreJSClientData.h"
#include <JavaScriptCore/FunctionPrototype.h>
#include <JavaScriptCore/JSCInlines.h>
#include <JavaScriptCore/JSDestructibleObjectHeapCellType.h>
#include <JavaScriptCore/SlotVisitorMacros.h>
#include <JavaScriptCore/SubspaceInlines.h>
#include <wtf/GetPtr.h>
#include <wtf/PointerPreparations.h>

namespace WebCore {
using namespace JSC;

// Attributes

static JSC_DECLARE_CUSTOM_GETTER(jsTextDecoderStreamConstructor);

class JSTextDecoderStreamPrototype final : public JSC::JSNonFinalObject {
public:
using Base = JSC::JSNonFinalObject;
static JSTextDecoderStreamPrototype* create(JSC::VM& vm, JSDOMGlobalObject* globalObject, JSC::Structure* structure)
{
JSTextDecoderStreamPrototype* ptr = new (NotNull, JSC::allocateCell<JSTextDecoderStreamPrototype>(vm)) JSTextDecoderStreamPrototype(vm, globalObject, structure);
ptr->finishCreation(vm);
return ptr;
}

DECLARE_INFO;
template<typename CellType, JSC::SubspaceAccess>
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
{
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSTextDecoderStreamPrototype, Base);
return &vm.plainObjectSpace();
}
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
{
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
}

private:
JSTextDecoderStreamPrototype(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure)
: JSC::JSNonFinalObject(vm, structure)
{
}

void finishCreation(JSC::VM&);
};
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSTextDecoderStreamPrototype, JSTextDecoderStreamPrototype::Base);

using JSTextDecoderStreamDOMConstructor = JSDOMBuiltinConstructor<JSTextDecoderStream>;

template<> const ClassInfo JSTextDecoderStreamDOMConstructor::s_info = { "TextDecoderStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSTextDecoderStreamDOMConstructor) };

template<> JSValue JSTextDecoderStreamDOMConstructor::prototypeForStructure(JSC::VM& vm, const JSDOMGlobalObject& globalObject)
{
UNUSED_PARAM(vm);
return globalObject.functionPrototype();
}

template<> void JSTextDecoderStreamDOMConstructor::initializeProperties(VM& vm, JSDOMGlobalObject& globalObject)
{
putDirect(vm, vm.propertyNames->length, jsNumber(0), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum);
JSString* nameString = jsNontrivialString(vm, "TextDecoderStream"_s);
m_originalName.set(vm, this, nameString);
putDirect(vm, vm.propertyNames->name, nameString, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum);
putDirect(vm, vm.propertyNames->prototype, JSTextDecoderStream::prototype(vm, globalObject), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete);
}

template<> FunctionExecutable* JSTextDecoderStreamDOMConstructor::initializeExecutable(VM& vm)
{
return textDecoderStreamInitializeTextDecoderStreamCodeGenerator(vm);
}

/* Hash table for prototype */

static const HashTableValue JSTextDecoderStreamPrototypeTableValues[] = {
{ "constructor"_s, static_cast<unsigned>(PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::GetterSetterType, jsTextDecoderStreamConstructor, 0 } },
{ "encoding"_s, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin, NoIntrinsic, { HashTableValue::BuiltinAccessorType, textDecoderStreamEncodingCodeGenerator, 0 } },
{ "fatal"_s, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin, NoIntrinsic, { HashTableValue::BuiltinAccessorType, textDecoderStreamFatalCodeGenerator, 0 } },
{ "ignoreBOM"_s, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin, NoIntrinsic, { HashTableValue::BuiltinAccessorType, textDecoderStreamIgnoreBOMCodeGenerator, 0 } },
{ "readable"_s, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin, NoIntrinsic, { HashTableValue::BuiltinAccessorType, textDecoderStreamReadableCodeGenerator, 0 } },
{ "writable"_s, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin, NoIntrinsic, { HashTableValue::BuiltinAccessorType, textDecoderStreamWritableCodeGenerator, 0 } },
};

const ClassInfo JSTextDecoderStreamPrototype::s_info = { "TextDecoderStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSTextDecoderStreamPrototype) };

void JSTextDecoderStreamPrototype::finishCreation(VM& vm)
{
Base::finishCreation(vm);
reifyStaticProperties(vm, JSTextDecoderStream::info(), JSTextDecoderStreamPrototypeTableValues, *this);
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
}

const ClassInfo JSTextDecoderStream::s_info = { "TextDecoderStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSTextDecoderStream) };

JSTextDecoderStream::JSTextDecoderStream(Structure* structure, JSDOMGlobalObject& globalObject)
: JSDOMObject(structure, globalObject)
{
}

JSObject* JSTextDecoderStream::createPrototype(VM& vm, JSDOMGlobalObject& globalObject)
{
auto* structure = JSTextDecoderStreamPrototype::createStructure(vm, &globalObject, globalObject.objectPrototype());
structure->setMayBePrototype(true);
return JSTextDecoderStreamPrototype::create(vm, &globalObject, structure);
}

JSObject* JSTextDecoderStream::prototype(VM& vm, JSDOMGlobalObject& globalObject)
{
return getDOMPrototype<JSTextDecoderStream>(vm, globalObject);
}

JSValue JSTextDecoderStream::getConstructor(VM& vm, const JSGlobalObject* globalObject)
{
return getDOMConstructor<JSTextDecoderStreamDOMConstructor, DOMConstructorID::TextDecoderStream>(vm, *jsCast<const JSDOMGlobalObject*>(globalObject));
}

void JSTextDecoderStream::destroy(JSC::JSCell* cell)
{
JSTextDecoderStream* thisObject = static_cast<JSTextDecoderStream*>(cell);
thisObject->JSTextDecoderStream::~JSTextDecoderStream();
}

JSC_DEFINE_CUSTOM_GETTER(jsTextDecoderStreamConstructor, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto throwScope = DECLARE_THROW_SCOPE(vm);
auto* prototype = jsDynamicCast<JSTextDecoderStreamPrototype*>(JSValue::decode(thisValue));
if (UNLIKELY(!prototype))
return throwVMTypeError(lexicalGlobalObject, throwScope);
return JSValue::encode(JSTextDecoderStream::getConstructor(vm, prototype->globalObject()));
}

JSC::GCClient::IsoSubspace* JSTextDecoderStream::subspaceForImpl(JSC::VM& vm)
{
return WebCore::subspaceForImpl<JSTextDecoderStream, UseCustomHeapCellType::No>(vm, [](auto& spaces) { return spaces.m_clientSubspaceForTextDecoderStream.get(); }, [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForTextDecoderStream = std::forward<decltype(space)>(space); }, [](auto& spaces) { return spaces.m_subspaceForTextDecoderStream.get(); }, [](auto& spaces, auto&& space) { spaces.m_subspaceForTextDecoderStream = std::forward<decltype(space)>(space); });
}

}
Loading

0 comments on commit 9f7c6e3

Please sign in to comment.