From ac8db43485ebd2ce7c86fa7673d73dd53b30e3bf Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Thu, 22 Aug 2024 23:25:41 -0700 Subject: [PATCH] Throw ERR_INVALID_THIS in DOM types (#13484) --- src/bun.js/bindings/DOMFormData.h | 4 +++ src/bun.js/bindings/ErrorCode.cpp | 10 +++++++ src/bun.js/bindings/ErrorCode.h | 2 ++ src/bun.js/bindings/ExceptionCode.h | 5 +++- .../bindings/JSDOMExceptionHandling.cpp | 21 +++++++++------ src/bun.js/bindings/URLSearchParams.h | 2 ++ src/bun.js/bindings/webcore/FetchHeaders.h | 9 +++++++ src/bun.js/bindings/webcore/JSDOMIterator.h | 26 ++++++++++++------- .../bindings/webcore/JSDOMPromiseDeferred.cpp | 4 ++- 9 files changed, 63 insertions(+), 20 deletions(-) diff --git a/src/bun.js/bindings/DOMFormData.h b/src/bun.js/bindings/DOMFormData.h index d0ff71bc4d..447be4e927 100644 --- a/src/bun.js/bindings/DOMFormData.h +++ b/src/bun.js/bindings/DOMFormData.h @@ -35,8 +35,11 @@ #include #include #include "blob.h" + namespace WebCore { +class ScriptExecutionContext; + template class ExceptionOr; class HTMLElement; class HTMLFormElement; @@ -84,6 +87,7 @@ public: size_t m_index { 0 }; }; Iterator createIterator() { return Iterator { *this }; } + Iterator createIterator(const ScriptExecutionContext* context) { return Iterator { *this }; } private: // explicit DOMFormData(ScriptExecutionContext*, const PAL::TextEncoding& = PAL::UTF8Encoding()); diff --git a/src/bun.js/bindings/ErrorCode.cpp b/src/bun.js/bindings/ErrorCode.cpp index 20fa1ab8a1..d1481cf1cf 100644 --- a/src/bun.js/bindings/ErrorCode.cpp +++ b/src/bun.js/bindings/ErrorCode.cpp @@ -399,6 +399,11 @@ extern "C" JSC::EncodedJSValue WebCore__CommonAbortReason__toJS(JSC::JSGlobalObj return JSC::JSValue::encode(WebCore::toJS(globalObject, abortReason)); } +JSC::JSObject* Bun::createInvalidThisError(JSC::JSGlobalObject* globalObject, const String& message) +{ + return Bun::createError(globalObject, Bun::ErrorCode::ERR_INVALID_THIS, message); +} + JSC::JSObject* Bun::createInvalidThisError(JSC::JSGlobalObject* globalObject, JSC::JSValue thisValue, const ASCIILiteral typeName) { if (thisValue.isEmpty() || thisValue.isUndefined()) { @@ -408,4 +413,9 @@ JSC::JSObject* Bun::createInvalidThisError(JSC::JSGlobalObject* globalObject, JS // Pathological case: the this value returns a string which is extremely long or causes an out of memory error. const auto& typeString = thisValue.isString() ? String("a string"_s) : JSC::errorDescriptionForValue(globalObject, thisValue); return Bun::createError(globalObject, Bun::ErrorCode::ERR_INVALID_THIS, makeString("Expected this to be instanceof "_s, typeName, ", but received "_s, typeString)); +} + +JSC::EncodedJSValue Bun::throwError(JSC::JSGlobalObject* globalObject, JSC::ThrowScope& scope, Bun::ErrorCode code, const WTF::String& message) +{ + return JSC::JSValue::encode(scope.throwException(globalObject, createError(globalObject, code, message))); } \ No newline at end of file diff --git a/src/bun.js/bindings/ErrorCode.h b/src/bun.js/bindings/ErrorCode.h index 0f80ff639b..da3e5a01c7 100644 --- a/src/bun.js/bindings/ErrorCode.h +++ b/src/bun.js/bindings/ErrorCode.h @@ -46,12 +46,14 @@ private: void finishCreation(VM&); }; +JSC::EncodedJSValue throwError(JSC::JSGlobalObject* globalObject, JSC::ThrowScope& scope, ErrorCode code, const WTF::String& message); JSC::JSObject* createError(Zig::GlobalObject* globalObject, ErrorCode code, const WTF::String& message); JSC::JSObject* createError(JSC::JSGlobalObject* globalObject, ErrorCode code, const WTF::String& message); JSC::JSObject* createError(Zig::GlobalObject* globalObject, ErrorCode code, JSC::JSValue message); JSC::JSObject* createError(VM& vm, Zig::GlobalObject* globalObject, ErrorCode code, JSValue message, JSValue options = jsUndefined()); JSC::JSValue toJS(JSC::JSGlobalObject*, ErrorCode); JSObject* createInvalidThisError(JSGlobalObject* globalObject, JSValue thisValue, const ASCIILiteral typeName); +JSObject* createInvalidThisError(JSGlobalObject* globalObject, const String& message); JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_INVALID_ARG_TYPE); JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_OUT_OF_RANGE); diff --git a/src/bun.js/bindings/ExceptionCode.h b/src/bun.js/bindings/ExceptionCode.h index d63691db1c..3fa8fd6df2 100644 --- a/src/bun.js/bindings/ExceptionCode.h +++ b/src/bun.js/bindings/ExceptionCode.h @@ -71,6 +71,8 @@ enum ExceptionCode { // Used to indicate to the bindings that a JS exception was thrown below and it should be propagated. ExistingExceptionError, + + InvalidThisError, }; } // namespace WebCore @@ -116,7 +118,8 @@ template<> struct EnumTraits { WebCore::ExceptionCode::TypeError, WebCore::ExceptionCode::JSSyntaxError, WebCore::ExceptionCode::StackOverflowError, - WebCore::ExceptionCode::ExistingExceptionError>; + WebCore::ExceptionCode::ExistingExceptionError, + WebCore::ExceptionCode::InvalidThisError>; }; } // namespace WTF diff --git a/src/bun.js/bindings/JSDOMExceptionHandling.cpp b/src/bun.js/bindings/JSDOMExceptionHandling.cpp index acb1a30948..6645b6c9ea 100644 --- a/src/bun.js/bindings/JSDOMExceptionHandling.cpp +++ b/src/bun.js/bindings/JSDOMExceptionHandling.cpp @@ -21,6 +21,8 @@ #include "root.h" +#include "ErrorCode.h" + #include "DOMException.h" #include "JSDOMException.h" #include "JSDOMExceptionHandling.h" @@ -173,6 +175,9 @@ JSValue createDOMException(JSGlobalObject* lexicalGlobalObject, ExceptionCode ec case ExceptionCode::OutOfMemoryError: return createOutOfMemoryError(lexicalGlobalObject); + case ExceptionCode::InvalidThisError: + return Bun::createInvalidThisError(lexicalGlobalObject, message.isEmpty() ? "Expected this to be of a different type"_s : message); + default: { // FIXME: All callers to createDOMException need to pass in the correct global object. // For now, we're going to assume the lexicalGlobalObject. Which is wrong in cases like this: @@ -233,22 +238,22 @@ void throwSecurityError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScop JSC::EncodedJSValue throwArgumentMustBeEnumError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, unsigned argumentIndex, ASCIILiteral argumentName, ASCIILiteral functionInterfaceName, ASCIILiteral functionName, ASCIILiteral expectedValues) { - return throwVMTypeError(&lexicalGlobalObject, scope, makeArgumentTypeErrorMessage(argumentIndex, argumentName, functionInterfaceName, functionName, "one of: "_s, expectedValues)); + return Bun::throwError(&lexicalGlobalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, makeArgumentTypeErrorMessage(argumentIndex, argumentName, functionInterfaceName, functionName, "one of: "_s, expectedValues)); } JSC::EncodedJSValue throwArgumentMustBeFunctionError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, unsigned argumentIndex, ASCIILiteral argumentName, ASCIILiteral interfaceName, ASCIILiteral functionName) { - return throwVMTypeError(&lexicalGlobalObject, scope, makeArgumentTypeErrorMessage(argumentIndex, argumentName, interfaceName, functionName, "a function"_s)); + return Bun::throwError(&lexicalGlobalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, makeArgumentTypeErrorMessage(argumentIndex, argumentName, interfaceName, functionName, "a function"_s)); } JSC::EncodedJSValue throwArgumentMustBeObjectError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, unsigned argumentIndex, ASCIILiteral argumentName, ASCIILiteral interfaceName, ASCIILiteral functionName) { - return throwVMTypeError(&lexicalGlobalObject, scope, makeArgumentTypeErrorMessage(argumentIndex, argumentName, interfaceName, functionName, "an object"_s)); + return Bun::throwError(&lexicalGlobalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, makeArgumentTypeErrorMessage(argumentIndex, argumentName, interfaceName, functionName, "an object"_s)); } JSC::EncodedJSValue throwArgumentTypeError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, unsigned argumentIndex, ASCIILiteral argumentName, ASCIILiteral functionInterfaceName, ASCIILiteral functionName, ASCIILiteral expectedType) { - return throwVMTypeError(&lexicalGlobalObject, scope, makeArgumentTypeErrorMessage(argumentIndex, argumentName, functionInterfaceName, functionName, "an instance of "_s, expectedType)); + return Bun::throwError(&lexicalGlobalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, makeArgumentTypeErrorMessage(argumentIndex, argumentName, functionInterfaceName, functionName, "an instance of "_s, expectedType)); } void throwAttributeTypeError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, ASCIILiteral interfaceName, ASCIILiteral attributeName, ASCIILiteral expectedType) @@ -258,7 +263,7 @@ void throwAttributeTypeError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::Thro JSC::EncodedJSValue throwRequiredMemberTypeError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, ASCIILiteral memberName, ASCIILiteral dictionaryName, ASCIILiteral expectedType) { - return throwVMTypeError(&lexicalGlobalObject, scope, makeString("Member "_s, dictionaryName, '.', memberName, " is required and must be an instance of "_s, expectedType)); + return Bun::throwError(&lexicalGlobalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, makeString("Member "_s, dictionaryName, '.', memberName, " is required and must be an instance of "_s, expectedType)); } JSC::EncodedJSValue throwConstructorScriptExecutionContextUnavailableError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, ASCIILiteral interfaceName) @@ -268,7 +273,7 @@ JSC::EncodedJSValue throwConstructorScriptExecutionContextUnavailableError(JSC:: void throwSequenceTypeError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { - throwTypeError(lexicalGlobalObject, scope, "Value is not a sequence"_s); + Bun::throwError(&lexicalGlobalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, "Value is not a sequence"_s); } void throwNonFiniteTypeError(JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) @@ -294,12 +299,12 @@ String makeUnsupportedIndexedSetterErrorMessage(ASCIILiteral interfaceName) EncodedJSValue throwThisTypeError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, const char* interfaceName, const char* functionName) { - return throwTypeError(lexicalGlobalObject, scope, makeThisTypeErrorMessage(interfaceName, functionName)); + return JSValue::encode(scope.throwException(&lexicalGlobalObject, Bun::createInvalidThisError(&lexicalGlobalObject, makeThisTypeErrorMessage(interfaceName, functionName)))); } JSC::EncodedJSValue rejectPromiseWithThisTypeError(DeferredPromise& promise, const char* interfaceName, const char* methodName) { - promise.reject(ExceptionCode::TypeError, makeThisTypeErrorMessage(interfaceName, methodName)); + promise.reject(ExceptionCode::InvalidThisError, makeThisTypeErrorMessage(interfaceName, methodName)); return JSValue::encode(jsUndefined()); } diff --git a/src/bun.js/bindings/URLSearchParams.h b/src/bun.js/bindings/URLSearchParams.h index 02258c82cc..65400d3672 100644 --- a/src/bun.js/bindings/URLSearchParams.h +++ b/src/bun.js/bindings/URLSearchParams.h @@ -34,6 +34,7 @@ namespace WebCore { class DOMURL; +class ScriptExecutionContext; class URLSearchParams : public RefCounted { public: @@ -66,6 +67,7 @@ public: size_t m_index { 0 }; }; Iterator createIterator() { return Iterator { *this }; } + Iterator createIterator(const ScriptExecutionContext* context) { return Iterator { *this }; } private: const Vector>& pairs() const { return m_pairs; } diff --git a/src/bun.js/bindings/webcore/FetchHeaders.h b/src/bun.js/bindings/webcore/FetchHeaders.h index 18bd9acefe..8e3713539b 100644 --- a/src/bun.js/bindings/webcore/FetchHeaders.h +++ b/src/bun.js/bindings/webcore/FetchHeaders.h @@ -35,7 +35,11 @@ #include namespace WebCore { + +class ScriptExecutionContext; + DECLARE_ALLOCATOR_WITH_HEAP_IDENTIFIER(FetchHeaders); + class FetchHeaders : public RefCounted { WTF_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(FetchHeaders); @@ -103,6 +107,11 @@ public: return Iterator(*this, lowerCaseKeys); } + Iterator createIterator(const ScriptExecutionContext* context) + { + return Iterator(*this, true); + } + void setInternalHeaders(HTTPHeaderMap&& headers) { m_headers = WTFMove(headers); } const HTTPHeaderMap& internalHeaders() const { return m_headers; } diff --git a/src/bun.js/bindings/webcore/JSDOMIterator.h b/src/bun.js/bindings/webcore/JSDOMIterator.h index f0f8efa91a..4a63eba283 100644 --- a/src/bun.js/bindings/webcore/JSDOMIterator.h +++ b/src/bun.js/bindings/webcore/JSDOMIterator.h @@ -30,7 +30,8 @@ #include #include #include - +#include "ErrorCode.h" +#include "JavaScriptCore/Interpreter.h" namespace WebCore { void addValueIterableMethods(JSC::JSGlobalObject&, JSC::JSObject&); @@ -101,7 +102,9 @@ public: static Prototype* createPrototype(JSC::VM& vm, JSC::JSGlobalObject& globalObject) { - return Prototype::create(vm, &globalObject, Prototype::createStructure(vm, &globalObject, globalObject.iteratorPrototype())); + auto* structure = Prototype::createStructure(vm, &globalObject, globalObject.iteratorPrototype()); + structure->setMayBePrototype(true); + return Prototype::create(vm, &globalObject, structure); } JSC::JSValue next(JSC::JSGlobalObject&); @@ -111,7 +114,7 @@ public: protected: JSDOMIteratorBase(JSC::Structure* structure, JSWrapper& iteratedObject, IterationKind kind) : Base(structure, *iteratedObject.globalObject()) - , m_iterator(iteratedObject.wrapped().createIterator()) + , m_iterator(iteratedObject.wrapped().createIterator(iteratedObject.globalObject()->scriptExecutionContext())) , m_kind(kind) { } @@ -211,10 +214,12 @@ template JSC::JSValue iteratorForEach(JSC::JSGlobalObject& JSC::JSValue thisValue = callFrame.argument(1); auto callData = JSC::getCallData(callback); - if (callData.type == JSC::CallData::Type::None) - return throwTypeError(&lexicalGlobalObject, scope, "Cannot call callback"_s); + if (callData.type == JSC::CallData::Type::None) { + Bun::throwError(&lexicalGlobalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, "Cannot call callback on a non-function"_s); + return {}; + } - auto iterator = thisObject.wrapped().createIterator(); + auto iterator = thisObject.wrapped().createIterator(JSC::jsCast(&lexicalGlobalObject)->scriptExecutionContext()); while (auto value = iterator.next()) { JSC::MarkedArgumentBuffer arguments; appendForEachArguments(lexicalGlobalObject, *thisObject.globalObject(), arguments, value); @@ -256,8 +261,9 @@ JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSDOMIteratorPrototype*>(callFrame->thisValue()); - if (!iterator) - return JSC::JSValue::encode(throwTypeError(globalObject, scope, "Cannot call next() on a non-Iterator object"_s)); + if (!iterator) { + return Bun::throwError(globalObject, scope, Bun::ErrorCode::ERR_INVALID_THIS, "Cannot call next() on a non-Iterator object"_s); + } return JSC::JSValue::encode(iterator->next(*globalObject)); } @@ -268,8 +274,8 @@ void JSDOMIteratorPrototype::finishCreation(JSC::VM& Base::finishCreation(vm); ASSERT(inherits(info())); - JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->next, next, 0, 0, ImplementationVisibility::Public, JSC::NoIntrinsic); + JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->next, next, 0, 0, JSC::ImplementationVisibility::Public); JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); } -} \ No newline at end of file +} diff --git a/src/bun.js/bindings/webcore/JSDOMPromiseDeferred.cpp b/src/bun.js/bindings/webcore/JSDOMPromiseDeferred.cpp index f4ed3ef683..a1399773c3 100644 --- a/src/bun.js/bindings/webcore/JSDOMPromiseDeferred.cpp +++ b/src/bun.js/bindings/webcore/JSDOMPromiseDeferred.cpp @@ -38,6 +38,8 @@ #include #include #include +#include "ErrorCode.h" +#include "JavaScriptCore/ErrorInstance.h" namespace WebCore { using namespace JSC; @@ -241,7 +243,7 @@ JSC::EncodedJSValue createRejectedPromiseWithTypeError(JSC::JSGlobalObject& lexi auto promiseConstructor = globalObject.promiseConstructor(); auto rejectFunction = promiseConstructor->get(&lexicalGlobalObject, vm.propertyNames->builtinNames().rejectPrivateName()); RETURN_IF_EXCEPTION(scope, {}); - auto* rejectionValue = static_cast(createTypeError(&lexicalGlobalObject, errorMessage)); + ErrorInstance* rejectionValue = static_cast(cause == RejectedPromiseWithTypeErrorCause::InvalidThis ? Bun::createInvalidThisError(&lexicalGlobalObject, errorMessage) : createTypeError(&lexicalGlobalObject, errorMessage)); if (cause == RejectedPromiseWithTypeErrorCause::NativeGetter) rejectionValue->setNativeGetterTypeError();