Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/jsc/bindings/DOMFormData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ void DOMFormData::append(const String& name, const String& value)

void DOMFormData::append(const String& name, RefPtr<Blob> blob, const String& filename)
{
blob->setFileName(replaceUnpairedSurrogatesWithReplacementCharacter(String(filename)));
// A non-null fileName is what makes toJS(Blob&) wrap this entry as a File.
blob->setFileName(filename.isNull() ? emptyString() : replaceUnpairedSurrogatesWithReplacementCharacter(String(filename)));
Comment thread
robobun marked this conversation as resolved.
m_items.append({ replaceUnpairedSurrogatesWithReplacementCharacter(String(name)), blob });
}
void DOMFormData::remove(const StringView name)
Expand Down Expand Up @@ -148,7 +149,7 @@ void DOMFormData::set(const String& name, const String& value)

void DOMFormData::set(const String& name, RefPtr<Blob> blob, const String& filename)
{
blob->setFileName(filename);
blob->setFileName(filename.isNull() ? emptyString() : filename);
set(name, { name, blob });
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

Expand Down
182 changes: 145 additions & 37 deletions src/jsc/bindings/JSDOMFile.cpp
Original file line number Diff line number Diff line change
@@ -1,66 +1,149 @@
#include "root.h"
#include "ZigGeneratedClasses.h"
#include "ZigGlobalObject.h"
#include <JavaScriptCore/ObjectConstructor.h>
#include <JavaScriptCore/InternalFunction.h>
#include <JavaScriptCore/FunctionPrototype.h>
#include <JavaScriptCore/LazyClassStructure.h>
#include <JavaScriptCore/LazyClassStructureInlines.h>
#include "JSDOMFile.h"

using namespace JSC;

extern "C" SYSV_ABI void* JSDOMFile__construct(JSC::JSGlobalObject*, JSC::CallFrame* callframe);
extern "C" SYSV_ABI bool JSDOMFile__hasInstance(EncodedJSValue, JSC::JSGlobalObject*, EncodedJSValue);

// TODO: make this inehrit from JSBlob instead of InternalFunction
// That will let us remove this hack for [Symbol.hasInstance] and fix the prototype chain.
class JSDOMFile : public JSC::InternalFunction {
using Base = JSC::InternalFunction;
extern "C" SYSV_ABI JSC::EncodedJSValue JSDOMFile__getName(void* ptr, JSC::EncodedJSValue thisValue, JSC::JSGlobalObject* globalObject);
extern "C" SYSV_ABI bool JSDOMFile__setName(void* ptr, JSC::EncodedJSValue thisValue, JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue value);
extern "C" SYSV_ABI JSC::EncodedJSValue JSDOMFile__getLastModified(void* ptr, JSC::JSGlobalObject* globalObject);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

namespace Bun {

JSC_DEFINE_CUSTOM_GETTER(domFilePrototype_nameGetter, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue encodedThisValue, PropertyName))
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto throwScope = DECLARE_THROW_SCOPE(vm);
auto* thisObject = dynamicDowncast<WebCore::JSBlob>(JSValue::decode(encodedThisValue));
if (!thisObject) [[unlikely]] {
return JSValue::encode(jsUndefined());
}
JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject);

if (JSValue cachedValue = thisObject->m_name.get())
return JSValue::encode(cachedValue);

JSC::JSValue result = JSC::JSValue::decode(
JSDOMFile__getName(thisObject->wrapped(), encodedThisValue, lexicalGlobalObject));
RETURN_IF_EXCEPTION(throwScope, {});
thisObject->m_name.set(vm, thisObject, result);
RELEASE_AND_RETURN(throwScope, JSValue::encode(result));
}

JSC_DEFINE_CUSTOM_SETTER(domFilePrototype_nameSetter, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue encodedThisValue, EncodedJSValue encodedValue, PropertyName attributeName))
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto throwScope = DECLARE_THROW_SCOPE(vm);
auto* thisObject = dynamicDowncast<WebCore::JSBlob>(JSValue::decode(encodedThisValue));
if (!thisObject) [[unlikely]] {
return false;
}
JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject);
bool result = JSDOMFile__setName(thisObject->wrapped(), encodedThisValue, lexicalGlobalObject, encodedValue);
RELEASE_AND_RETURN(throwScope, result);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

JSC_DEFINE_CUSTOM_GETTER(domFilePrototype_lastModifiedGetter, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue encodedThisValue, PropertyName))
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto throwScope = DECLARE_THROW_SCOPE(vm);
auto* thisObject = dynamicDowncast<WebCore::JSBlob>(JSValue::decode(encodedThisValue));
if (!thisObject) [[unlikely]] {
return JSValue::encode(jsUndefined());
}
JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject);
JSC::EncodedJSValue result = JSDOMFile__getLastModified(thisObject->wrapped(), lexicalGlobalObject);
RETURN_IF_EXCEPTION(throwScope, {});
RELEASE_AND_RETURN(throwScope, result);
}

static const HashTableValue JSDOMFilePrototypeTableValues[] = {
{ "name"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor | PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, domFilePrototype_nameGetter, domFilePrototype_nameSetter } },
{ "lastModified"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor | PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, domFilePrototype_lastModifiedGetter, 0 } },
};
Comment thread
claude[bot] marked this conversation as resolved.

class JSDOMFilePrototype final : public JSC::JSNonFinalObject {
public:
JSDOMFile(JSC::VM& vm, JSC::Structure* structure)
: Base(vm, structure, call, construct)
using Base = JSC::JSNonFinalObject;
static constexpr unsigned StructureFlags = Base::StructureFlags;

static JSDOMFilePrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
{
JSDOMFilePrototype* prototype = new (NotNull, JSC::allocateCell<JSDOMFilePrototype>(vm)) JSDOMFilePrototype(vm, structure);
prototype->finishCreation(vm, globalObject);
return prototype;
}

DECLARE_INFO;
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
{
auto* structure = JSC::Structure::create(vm, globalObject, prototype, TypeInfo(JSC::ObjectType, StructureFlags), info());
structure->setMayBePrototype(true);
return structure;
}

static constexpr unsigned StructureFlags = (Base::StructureFlags & ~ImplementsDefaultHasInstance) | ImplementsHasInstance;
DECLARE_INFO;

template<typename CellType, JSC::SubspaceAccess>
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
{
return &vm.internalFunctionSpace();
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSDOMFilePrototype, Base);
return &vm.plainObjectSpace();
}
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)

private:
JSDOMFilePrototype(JSC::VM& vm, JSC::Structure* structure)
: Base(vm, structure)
{
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(InternalFunctionType, StructureFlags), info());
}

void finishCreation(JSC::VM& vm)
void finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
{
Base::finishCreation(vm, 2, "File"_s);
Base::finishCreation(vm);
reifyStaticProperties(vm, WebCore::JSBlob::info(), JSDOMFilePrototypeTableValues, *this);
Comment thread
robobun marked this conversation as resolved.
this->putDirect(vm, vm.propertyNames->toStringTagSymbol, jsString(vm, String("File"_s)), PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly | 0);
}
};

static JSDOMFile* create(JSC::VM& vm, JSGlobalObject* globalObject)
{
auto* zigGlobal = defaultGlobalObject(globalObject);
auto structure = createStructure(vm, globalObject, zigGlobal->functionPrototype());
auto* object = new (NotNull, JSC::allocateCell<JSDOMFile>(vm)) JSDOMFile(vm, structure);
object->finishCreation(vm);
const JSC::ClassInfo JSDOMFilePrototype::s_info = { "File"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSDOMFilePrototype) };

// This is not quite right. But we'll fix it if someone files an issue about it.
object->putDirect(vm, vm.propertyNames->prototype, zigGlobal->JSBlobPrototype(), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | 0);
class JSDOMFileConstructor final : public JSC::InternalFunction {
using Base = JSC::InternalFunction;

return object;
public:
JSDOMFileConstructor(JSC::VM& vm, JSC::Structure* structure)
: Base(vm, structure, call, construct)
{
}

static bool customHasInstance(JSObject* object, JSGlobalObject* globalObject, JSValue value)
DECLARE_INFO;

static constexpr unsigned StructureFlags = Base::StructureFlags;

template<typename CellType, JSC::SubspaceAccess>
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
{
if (!value.isObject())
return false;
return &vm.internalFunctionSpace();
}
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
{
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(InternalFunctionType, StructureFlags), info());
}

// Note: this breaks [Symbol.hasInstance]
// We must do this for now until we update the code generator to export classes
return JSDOMFile__hasInstance(JSValue::encode(object), globalObject, JSValue::encode(value));
static JSDOMFileConstructor* create(JSC::VM& vm, JSGlobalObject* globalObject, JSObject* prototype)
{
auto* zigGlobal = defaultGlobalObject(globalObject);
auto* structure = createStructure(vm, globalObject, zigGlobal->JSBlobConstructor());
auto* object = new (NotNull, JSC::allocateCell<JSDOMFileConstructor>(vm)) JSDOMFileConstructor(vm, structure);
object->finishCreation(vm, prototype);
return object;
}

static JSC_HOST_CALL_ATTRIBUTES JSC::EncodedJSValue construct(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame)
Expand All @@ -69,15 +152,15 @@ class JSDOMFile : public JSC::InternalFunction {
auto& vm = JSC::getVM(globalObject);
JSObject* newTarget = asObject(callFrame->newTarget());
auto* constructor = globalObject->JSDOMFileConstructor();
Structure* structure = globalObject->JSBlobStructure();
Structure* structure = globalObject->JSDOMFileStructure();
if (constructor != newTarget) {
auto scope = DECLARE_THROW_SCOPE(vm);

auto* functionGlobalObject = static_cast<Zig::GlobalObject*>(
auto* functionGlobalObject = defaultGlobalObject(
// ShadowRealm functions belong to a different global object.
getFunctionRealm(lexicalGlobalObject, newTarget));
RETURN_IF_EXCEPTION(scope, {});
structure = InternalFunction::createSubclassStructure(lexicalGlobalObject, newTarget, functionGlobalObject->JSBlobStructure());
structure = InternalFunction::createSubclassStructure(lexicalGlobalObject, newTarget, functionGlobalObject->JSDOMFileStructure());
RETURN_IF_EXCEPTION(scope, {});
}

Expand All @@ -97,15 +180,40 @@ class JSDOMFile : public JSC::InternalFunction {
throwTypeError(lexicalGlobalObject, scope, "Class constructor File cannot be invoked without 'new'"_s);
return {};
}
};

const JSC::ClassInfo JSDOMFile::s_info = { "File"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSDOMFile) };
private:
void finishCreation(JSC::VM& vm, JSObject* prototype)
{
Base::finishCreation(vm, 2, "File"_s);
putDirect(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly | 0);
}
};

namespace Bun {
const JSC::ClassInfo JSDOMFileConstructor::s_info = { "File"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSDOMFileConstructor) };

JSC::JSObject* createJSDOMFileConstructor(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
void initJSDOMFileClassStructure(JSC::LazyClassStructure::Initializer& init)
{
return JSDOMFile::create(vm, globalObject);
auto* zigGlobal = defaultGlobalObject(init.global);
auto* superPrototype = zigGlobal->JSBlobPrototype();
auto* protoStructure = JSDOMFilePrototype::createStructure(init.vm, init.global, superPrototype);
auto* prototype = JSDOMFilePrototype::create(init.vm, init.global, protoStructure);
auto* structure = WebCore::JSBlob::createStructure(init.vm, init.global, prototype);
auto* constructor = JSDOMFileConstructor::create(init.vm, init.global, prototype);
init.setPrototype(prototype);
init.setStructure(structure);
init.setConstructor(constructor);
}

extern "C" SYSV_ABI size_t Blob__estimatedSize(void* ptr);

extern "C" SYSV_ABI JSC::EncodedJSValue BUN__createJSDOMFileUnsafely(JSC::JSGlobalObject* lexicalGlobalObject, void* ptr)
{
auto* globalObject = defaultGlobalObject(lexicalGlobalObject);
auto& vm = JSC::getVM(globalObject);
auto* structure = globalObject->JSDOMFileStructure();
auto* instance = WebCore::JSBlob::create(vm, globalObject, structure, ptr);
vm.heap.reportExtraMemoryAllocated(instance, Blob__estimatedSize(ptr));
return JSValue::encode(instance);
}
Comment thread
robobun marked this conversation as resolved.

} // namespace Bun
3 changes: 2 additions & 1 deletion src/jsc/bindings/JSDOMFile.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#pragma once

#include "root.h"
#include <JavaScriptCore/LazyClassStructure.h>

namespace Bun {
JSC::JSObject* createJSDOMFileConstructor(JSC::VM&, JSC::JSGlobalObject*);
void initJSDOMFileClassStructure(JSC::LazyClassStructure::Initializer&);
}
2 changes: 1 addition & 1 deletion src/jsc/bindings/JSS3File.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ JSC::Structure* JSS3File::createStructure(JSC::JSGlobalObject* globalObject)
{
auto& vm = JSC::getVM(globalObject);

JSC::JSObject* superPrototype = defaultGlobalObject(globalObject)->JSBlobPrototype();
JSC::JSObject* superPrototype = defaultGlobalObject(globalObject)->JSDOMFileStructure()->storedPrototypeObject();
auto* protoStructure = JSS3FilePrototype::createStructure(vm, globalObject, superPrototype);
auto* prototype = JSS3FilePrototype::create(vm, globalObject, protoStructure);
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(static_cast<JSC::JSType>(0b11101110), StructureFlags), info(), NonArray);
Expand Down
11 changes: 5 additions & 6 deletions src/jsc/bindings/ZigGlobalObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2136,12 +2136,6 @@ void GlobalObject::finishCreation(VM& vm)
init.set(CustomGetterSetter::create(init.vm, errorInstanceLazyStackCustomGetter, errorInstanceLazyStackCustomSetter));
});

m_JSDOMFileConstructor.initLater(
[](const Initializer<JSObject>& init) {
JSObject* fileConstructor = Bun::createJSDOMFileConstructor(init.vm, init.owner);
init.set(fileConstructor);
});

m_cryptoObject.initLater(
[](const Initializer<JSObject>& init) {
JSC::JSGlobalObject* globalObject = init.owner;
Expand Down Expand Up @@ -2184,6 +2178,11 @@ void GlobalObject::finishCreation(VM& vm)
init.set(Bun::createJSS3FileStructure(init.vm, init.owner));
});

m_JSDOMFileClassStructure.initLater(
[](LazyClassStructure::Initializer& init) {
Bun::initJSDOMFileClassStructure(init);
});

m_S3ErrorStructure.initLater(
[](const Initializer<Structure>& init) {
init.set(Bun::createS3ErrorStructure(init.vm, init.owner));
Expand Down
5 changes: 3 additions & 2 deletions src/jsc/bindings/ZigGlobalObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,7 @@ class GlobalObject : public Bun::GlobalScope {
V(public, LazyPropertyOfGlobalObject<Structure>, m_JSS3FileStructure) \
V(public, LazyPropertyOfGlobalObject<Structure>, m_S3ErrorStructure) \
\
V(public, JSC::LazyClassStructure, m_JSDOMFileClassStructure) \
V(public, JSC::LazyClassStructure, m_JSStatsClassStructure) \
V(public, JSC::LazyClassStructure, m_JSStatsBigIntClassStructure) \
V(public, JSC::LazyClassStructure, m_JSStatFSClassStructure) \
Expand Down Expand Up @@ -674,7 +675,6 @@ class GlobalObject : public Bun::GlobalScope {
V(private, LazyPropertyOfGlobalObject<Structure>, m_importMetaObjectStructure) \
V(private, LazyPropertyOfGlobalObject<Structure>, m_importMetaBakeObjectStructure) \
V(private, LazyPropertyOfGlobalObject<Structure>, m_asyncBoundFunctionStructure) \
V(public, LazyPropertyOfGlobalObject<JSC::JSObject>, m_JSDOMFileConstructor) \
V(public, LazyPropertyOfGlobalObject<JSC::JSObject>, m_JSMIMEParamsConstructor) \
V(public, LazyPropertyOfGlobalObject<JSC::JSObject>, m_JSMIMETypeConstructor) \
\
Expand Down Expand Up @@ -781,7 +781,8 @@ class GlobalObject : public Bun::GlobalScope {
JSC::JSWeakMap* napiTypeTags() const { return m_napiTypeTags.getInitializedOnMainThread(this); }

JSObject* cryptoObject() const { return m_cryptoObject.getInitializedOnMainThread(this); }
JSObject* JSDOMFileConstructor() const { return m_JSDOMFileConstructor.getInitializedOnMainThread(this); }
JSObject* JSDOMFileConstructor() const { return m_JSDOMFileClassStructure.constructorInitializedOnMainThread(this); }
Structure* JSDOMFileStructure() const { return m_JSDOMFileClassStructure.getInitializedOnMainThread(this); }

JSMap* nodeWorkerEnvironmentData() { return m_nodeWorkerEnvironmentData.get(); }
void setNodeWorkerEnvironmentData(JSMap* data);
Expand Down
2 changes: 1 addition & 1 deletion src/jsc/bindings/ZigGlobalObject.lut.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
global GlobalObject_getGlobalThis PropertyCallback

Bun GlobalObject::m_bunObject CellProperty|DontDelete|ReadOnly
File GlobalObject::m_JSDOMFileConstructor CellProperty
crypto GlobalObject::m_cryptoObject CellProperty
navigator GlobalObject::m_navigatorObject CellProperty
performance GlobalObject::m_performanceObject CellProperty
Expand All @@ -36,6 +35,7 @@
BuildError GlobalObject::m_JSBuildMessage ClassStructure
BuildMessage GlobalObject::m_JSBuildMessage ClassStructure
Crypto GlobalObject::m_JSCrypto ClassStructure
File GlobalObject::m_JSDOMFileClassStructure ClassStructure
HTMLRewriter GlobalObject::m_JSHTMLRewriter ClassStructure
Request GlobalObject::m_JSRequest ClassStructure
ResolveError GlobalObject::m_JSResolveMessage ClassStructure
Expand Down
Loading
Loading