aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2023-02-13 04:37:12 -0800
committerGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2023-02-13 04:37:23 -0800
commit37186f4b0a6b97ffb3ac1ec65ddc2146126b4545 (patch)
tree99bd379ab22c7531339c72c7d03f4fc9e6468b78
parentf310d7414b3a9863d99773317620e93c1e8b6693 (diff)
downloadbun-37186f4b0a6b97ffb3ac1ec65ddc2146126b4545.tar.gz
bun-37186f4b0a6b97ffb3ac1ec65ddc2146126b4545.tar.zst
bun-37186f4b0a6b97ffb3ac1ec65ddc2146126b4545.zip
Add pretty printer for FormData
-rw-r--r--src/bun.js/bindings/exports.zig14
-rw-r--r--src/bun.js/bindings/webcore/JSDOMFormData.cpp75
-rw-r--r--src/bun.js/bindings/webcore/JSDOMFormData.h3
-rw-r--r--src/bun.js/webcore/response.zig1
-rw-r--r--test/bun.js/FormData.test.ts9
5 files changed, 101 insertions, 1 deletions
diff --git a/src/bun.js/bindings/exports.zig b/src/bun.js/bindings/exports.zig
index b9dd350a6..09f52bfe3 100644
--- a/src/bun.js/bindings/exports.zig
+++ b/src/bun.js/bindings/exports.zig
@@ -2084,6 +2084,20 @@ pub const ZigConsoleClient = struct {
} else if (value.as(JSC.WebCore.Blob)) |blob| {
blob.writeFormat(this, writer_, enable_ansi_colors) catch {};
return;
+ } else if (value.as(JSC.DOMFormData) != null) {
+ const toJSONFunction = value.get(this.globalThis, "toJSON").?;
+
+ this.addForNewLine("FormData (entries) ".len);
+ writer.writeAll(comptime Output.prettyFmt("<r><blue>FormData<r> <d>(entries)<r> ", enable_ansi_colors));
+
+ return this.printAs(
+ .Object,
+ Writer,
+ writer_,
+ toJSONFunction.callWithThis(this.globalThis, value, &.{}),
+ .Object,
+ enable_ansi_colors,
+ );
} else if (jsType != .DOMWrapper) {
if (CAPI.JSObjectGetPrivate(value.asRef())) |private_data_ptr| {
const priv_data = JSPrivateDataPtr.from(private_data_ptr);
diff --git a/src/bun.js/bindings/webcore/JSDOMFormData.cpp b/src/bun.js/bindings/webcore/JSDOMFormData.cpp
index 44c28bd00..3578db7ff 100644
--- a/src/bun.js/bindings/webcore/JSDOMFormData.cpp
+++ b/src/bun.js/bindings/webcore/JSDOMFormData.cpp
@@ -55,6 +55,7 @@
#include <wtf/PointerPreparations.h>
#include <wtf/URL.h>
#include "ZigGeneratedClasses.h"
+#include "GCDefferalContext.h"
namespace WebCore {
using namespace JSC;
@@ -100,6 +101,9 @@ static JSC_DECLARE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_keys);
static JSC_DECLARE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_values);
static JSC_DECLARE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_forEach);
+// Non-standard functions
+static JSC_DECLARE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_toJSON);
+
// Attributes
static JSC_DECLARE_CUSTOM_GETTER(jsDOMFormDataConstructor);
@@ -191,6 +195,7 @@ static const HashTableValue JSDOMFormDataPrototypeTableValues[] = {
{ "keys"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDOMFormDataPrototypeFunction_keys, 0 } },
{ "values"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDOMFormDataPrototypeFunction_values, 0 } },
{ "forEach"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDOMFormDataPrototypeFunction_forEach, 1 } },
+ { "toJSON"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDOMFormDataPrototypeFunction_toJSON, 1 } },
};
const ClassInfo JSDOMFormDataPrototype::s_info = { "FormData"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSDOMFormDataPrototype) };
@@ -478,6 +483,76 @@ JSC_DEFINE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_set, (JSGlobalObject * l
return IDLOperation<JSDOMFormData>::call<jsDOMFormDataPrototypeFunction_setOverloadDispatcher>(*lexicalGlobalObject, *callFrame, "set");
}
+/**
+ * Non standard function.
+ **/
+static inline JSC::EncodedJSValue jsDOMFormDataPrototypeFunction_toJSONBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSDOMFormData>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+
+ auto& impl = castedThis->wrapped();
+ size_t size = impl.count();
+ JSObject* obj;
+ if (size == 0) {
+ obj = constructEmptyObject(lexicalGlobalObject);
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ RELEASE_AND_RETURN(throwScope, JSValue::encode(obj));
+ } else if (size < 64) {
+ obj = constructEmptyObject(lexicalGlobalObject, lexicalGlobalObject->objectPrototype(), size + 1);
+ } else {
+ obj = constructEmptyObject(lexicalGlobalObject);
+ }
+
+ obj->putDirect(vm, vm.propertyNames->toStringTagSymbol, jsString(vm, "FormData"), PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly);
+
+ auto iter = impl.items();
+ WTF::HashSet<String> seenKeys;
+
+ auto toJSValue = [&](const DOMFormData::FormDataEntryValue& entry) -> JSValue {
+ return toJS<IDLNullable<IDLUnion<IDLUSVString, IDLInterface<Blob>>>>(*lexicalGlobalObject, *castedThis->globalObject(), throwScope, entry);
+ };
+
+ for (auto& entry : iter) {
+ auto& key = entry.name;
+ auto& value = entry.data;
+ auto ident = Identifier::fromString(vm, key);
+ if (seenKeys.contains(key)) {
+ JSValue jsValue = obj->getDirect(vm, ident);
+ if (jsValue.isString() || jsValue.inherits<JSBlob>()) {
+ GCDeferralContext deferralContext(lexicalGlobalObject->vm());
+ JSC::ObjectInitializationScope initializationScope(lexicalGlobalObject->vm());
+
+ JSC::JSArray* array = JSC::JSArray::tryCreateUninitializedRestricted(
+ initializationScope, &deferralContext,
+ lexicalGlobalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous),
+ 2);
+
+ array->initializeIndex(initializationScope, 0, jsValue);
+ array->initializeIndex(initializationScope, 1, toJSValue(value));
+ obj->putDirect(vm, ident, array, 0);
+ } else if (jsValue.isObject() && jsValue.getObject()->inherits<JSC::JSArray>()) {
+ JSC::JSArray* array = jsCast<JSC::JSArray*>(jsValue.getObject());
+ array->push(lexicalGlobalObject, toJSValue(value));
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+
+ } else {
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+ } else {
+ seenKeys.add(key);
+ obj->putDirect(vm, ident, toJSValue(value), 0);
+ }
+ }
+
+ RELEASE_AND_RETURN(throwScope, JSValue::encode(obj));
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_toJSON, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ return IDLOperation<JSDOMFormData>::call<jsDOMFormDataPrototypeFunction_toJSONBody>(*lexicalGlobalObject, *callFrame, "toJSON");
+}
+
struct DOMFormDataIteratorTraits {
static constexpr JSDOMIteratorType type = JSDOMIteratorType::Map;
using KeyType = IDLUSVString;
diff --git a/src/bun.js/bindings/webcore/JSDOMFormData.h b/src/bun.js/bindings/webcore/JSDOMFormData.h
index 085c42c5d..511cbfd1b 100644
--- a/src/bun.js/bindings/webcore/JSDOMFormData.h
+++ b/src/bun.js/bindings/webcore/JSDOMFormData.h
@@ -45,7 +45,7 @@ public:
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(), JSC::NonArray);
+ return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::JSType(WebCore::JSDOMWrapperType), StructureFlags), info(), JSC::NonArray);
}
static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*);
@@ -57,6 +57,7 @@ public:
}
static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm);
static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&);
+
protected:
JSDOMFormData(JSC::Structure*, JSDOMGlobalObject&, Ref<DOMFormData>&&);
diff --git a/src/bun.js/webcore/response.zig b/src/bun.js/webcore/response.zig
index 8af1c3958..de88cfca9 100644
--- a/src/bun.js/webcore/response.zig
+++ b/src/bun.js/webcore/response.zig
@@ -894,6 +894,7 @@ pub const Fetch = struct {
var body_value = body_const;
// TODO: buffer ReadableStream?
// we have to explicitly check for InternalBlob
+
body = body_value.useAsAnyBlob();
} else {
// an error was thrown
diff --git a/test/bun.js/FormData.test.ts b/test/bun.js/FormData.test.ts
index dbe1a8ef0..8e06aa96d 100644
--- a/test/bun.js/FormData.test.ts
+++ b/test/bun.js/FormData.test.ts
@@ -324,6 +324,15 @@ describe("FormData", () => {
});
});
+ it("Bun.inspect", () => {
+ const formData = new FormData();
+ formData.append("foo", "bar");
+ formData.append("foo", new Blob(["bar"]));
+ formData.append("bar", "baz");
+ formData.append("boop", Bun.file("missing"));
+ expect(Bun.inspect(formData).length > 0).toBe(true);
+ });
+
describe("URLEncoded", () => {
test("should parse URL encoded", async () => {
const response = new Response("foo=bar&baz=qux", {