diff options
Diffstat (limited to 'src/bun.js')
-rw-r--r-- | src/bun.js/bindings/JSBufferList.cpp | 58 | ||||
-rw-r--r-- | src/bun.js/test/jest.zig | 82 |
2 files changed, 106 insertions, 34 deletions
diff --git a/src/bun.js/bindings/JSBufferList.cpp b/src/bun.js/bindings/JSBufferList.cpp index e16c8cf88..e54b433e5 100644 --- a/src/bun.js/bindings/JSBufferList.cpp +++ b/src/bun.js/bindings/JSBufferList.cpp @@ -2,7 +2,6 @@ #include "JSBuffer.h" #include "JavaScriptCore/Lookup.h" #include "JavaScriptCore/ObjectConstructor.h" -#include "JavaScriptCore/JSGenericTypedArrayViewInlines.h" #include "ZigGlobalObject.h" #include "JSDOMOperation.h" #include "headers.h" @@ -48,6 +47,9 @@ JSC::JSValue JSBufferList::concat(JSC::VM& vm, JSC::JSGlobalObject* lexicalGloba if (UNLIKELY(!array)) { return throwTypeError(lexicalGlobalObject, throwScope, "concat can only be called when all buffers are Uint8Array"_s); } + if (UNLIKELY(array->byteLength() > n)) { + return throwRangeError(lexicalGlobalObject, throwScope, "specified size too small to fit all buffers"_s); + } RELEASE_AND_RETURN(throwScope, array); } // Buffer.allocUnsafe(n >>> 0) @@ -106,10 +108,10 @@ JSC::JSValue JSBufferList::consume(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlob return _getBuffer(vm, lexicalGlobalObject, n); } -JSC::JSValue JSBufferList::_getString(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, int32_t n) +JSC::JSValue JSBufferList::_getString(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, int32_t total) { auto throwScope = DECLARE_THROW_SCOPE(vm); - if (n == 0 || length() == 0) { + if (total <= 0 || length() == 0) { RELEASE_AND_RETURN(throwScope, JSC::jsEmptyString(vm)); } @@ -118,7 +120,8 @@ JSC::JSValue JSBufferList::_getString(JSC::VM& vm, JSC::JSGlobalObject* lexicalG if (UNLIKELY(!str)) { return throwTypeError(lexicalGlobalObject, throwScope, "_getString can only be called when all buffers are string"_s); } - size_t len = str->length(); + const size_t len = str->length(); + size_t n = total; if (n == len) { m_deque.removeFirst(); @@ -131,32 +134,33 @@ JSC::JSValue JSBufferList::_getString(JSC::VM& vm, JSC::JSGlobalObject* lexicalG } JSRopeString::RopeBuilder<RecordOverflow> ropeBuilder(vm); - for (const auto end = m_deque.end(); iter != end && n > 0; ++iter) { + for (const auto end = m_deque.end(); iter != end; ++iter) { JSC::JSString* str = JSC::jsDynamicCast<JSC::JSString*>(iter->get()); if (UNLIKELY(!str)) { return throwTypeError(lexicalGlobalObject, throwScope, "_getString can only be called when all buffers are string"_s); } - size_t len = str->length(); + const size_t len = str->length(); if (n < len) { JSString* firstHalf = JSC::jsSubstring(lexicalGlobalObject, str, 0, n); if (!ropeBuilder.append(firstHalf)) return throwOutOfMemoryError(lexicalGlobalObject, throwScope); iter->set(vm, this, JSC::jsSubstring(lexicalGlobalObject, str, n, len - n)); - } else { - if (!ropeBuilder.append(str)) - return throwOutOfMemoryError(lexicalGlobalObject, throwScope); - m_deque.removeFirst(); + break; } - n -= static_cast<int32_t>(len); + if (!ropeBuilder.append(str)) + return throwOutOfMemoryError(lexicalGlobalObject, throwScope); + m_deque.removeFirst(); + if (n == len) break; + n -= len; } RELEASE_AND_RETURN(throwScope, ropeBuilder.release()); } -JSC::JSValue JSBufferList::_getBuffer(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, int32_t n) +JSC::JSValue JSBufferList::_getBuffer(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, int32_t total) { auto throwScope = DECLARE_THROW_SCOPE(vm); auto* subclassStructure = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject)->JSBufferSubclassStructure(); - if (n == 0 || length() == 0) { + if (total <= 0 || length() == 0) { // Buffer.alloc(0) RELEASE_AND_RETURN(throwScope, JSC::JSUint8Array::create(lexicalGlobalObject, subclassStructure, 0)); } @@ -166,13 +170,15 @@ JSC::JSValue JSBufferList::_getBuffer(JSC::VM& vm, JSC::JSGlobalObject* lexicalG if (UNLIKELY(!array)) { return throwTypeError(lexicalGlobalObject, throwScope, "_getBuffer can only be called when all buffers are Uint8Array"_s); } - size_t len = array->byteLength(); + const size_t len = array->byteLength(); + size_t n = total; if (n == len) { + m_deque.removeFirst(); RELEASE_AND_RETURN(throwScope, array); } if (n < len) { - auto buffer = array->existingBuffer(); + auto buffer = array->possiblySharedBuffer(); JSC::JSUint8Array* retArray = JSC::JSUint8Array::create(lexicalGlobalObject, subclassStructure, buffer, 0, n); JSC::JSUint8Array* newArray = JSC::JSUint8Array::create(lexicalGlobalObject, subclassStructure, buffer, n, len - n); iter->set(vm, this, newArray); @@ -182,29 +188,31 @@ JSC::JSValue JSBufferList::_getBuffer(JSC::VM& vm, JSC::JSGlobalObject* lexicalG // Buffer.allocUnsafe(n >>> 0) auto arrayBuffer = JSC::ArrayBuffer::tryCreateUninitialized(n, 1); if (UNLIKELY(!arrayBuffer)) { - return throwTypeError(lexicalGlobalObject, throwScope); + return throwOutOfMemoryError(lexicalGlobalObject, throwScope); } JSC::JSUint8Array* uint8Array = JSC::JSUint8Array::create(lexicalGlobalObject, subclassStructure, WTFMove(arrayBuffer), 0, n); size_t offset = 0; - for (const auto end = m_deque.end(); iter != end && n > 0; ++iter) { + for (const auto end = m_deque.end(); iter != end; ++iter) { JSC::JSUint8Array* array = JSC::jsDynamicCast<JSC::JSUint8Array*>(iter->get()); if (UNLIKELY(!array)) { return throwTypeError(lexicalGlobalObject, throwScope, "_getBuffer can only be called when all buffers are Uint8Array"_s); } - size_t len = array->byteLength(); + const size_t len = array->byteLength(); if (n < len) { if (UNLIKELY(!uint8Array->setFromTypedArray(lexicalGlobalObject, offset, array, 0, n, JSC::CopyType::Unobservable))) { return throwOutOfMemoryError(lexicalGlobalObject, throwScope); } - JSC::JSUint8Array* newArray = JSC::JSUint8Array::create(lexicalGlobalObject, subclassStructure, array->existingBuffer(), n, len - n); + auto buffer = array->possiblySharedBuffer(); + JSC::JSUint8Array* newArray = JSC::JSUint8Array::create(lexicalGlobalObject, subclassStructure, buffer, n, len - n); iter->set(vm, this, newArray); - } else { - if (UNLIKELY(!uint8Array->setFromTypedArray(lexicalGlobalObject, offset, array, 0, len, JSC::CopyType::Unobservable))) { - return throwOutOfMemoryError(lexicalGlobalObject, throwScope); - } - m_deque.removeFirst(); + break; + } + if (UNLIKELY(!uint8Array->setFromTypedArray(lexicalGlobalObject, offset, array, 0, len, JSC::CopyType::Unobservable))) { + return throwOutOfMemoryError(lexicalGlobalObject, throwScope); } - n -= static_cast<int32_t>(len); + m_deque.removeFirst(); + if (n == len) break; + n -= len; offset += len; } RELEASE_AND_RETURN(throwScope, uint8Array); diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig index 24c1bd0e1..ba9234c6d 100644 --- a/src/bun.js/test/jest.zig +++ b/src/bun.js/test/jest.zig @@ -535,7 +535,7 @@ pub const Expect = struct { defer this.postMatch(globalObject); const thisValue = callFrame.this(); const value: JSValue = Expect.capturedValueGetCached(thisValue) orelse { - globalObject.throw("Interal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); + globalObject.throw("Internal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); return .zero; }; value.ensureStillAlive(); @@ -564,7 +564,7 @@ pub const Expect = struct { const thisValue = callFrame.this(); const value: JSValue = Expect.capturedValueGetCached(thisValue) orelse { - globalObject.throw("Interal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); + globalObject.throw("Internal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); return .zero; }; value.ensureStillAlive(); @@ -596,7 +596,7 @@ pub const Expect = struct { const thisValue = callFrame.this(); const value: JSValue = Expect.capturedValueGetCached(thisValue) orelse { - globalObject.throw("Interal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); + globalObject.throw("Internal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); return .zero; }; value.ensureStillAlive(); @@ -623,7 +623,7 @@ pub const Expect = struct { const thisValue = callFrame.this(); const value: JSValue = Expect.capturedValueGetCached(thisValue) orelse { - globalObject.throw("Interal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); + globalObject.throw("Internal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); return .zero; }; value.ensureStillAlive(); @@ -851,7 +851,7 @@ pub const Expect = struct { other_value.ensureStillAlive(); const value = Expect.capturedValueGetCached(thisValue) orelse { - globalObject.throw("Internal consistency error: thie expect(value) was garbage collected but it should not have been!", .{}); + globalObject.throw("Internal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); return .zero; }; value.ensureStillAlive(); @@ -914,7 +914,7 @@ pub const Expect = struct { other_value.ensureStillAlive(); const value = Expect.capturedValueGetCached(thisValue) orelse { - globalObject.throw("Internal consistency error: thie expect(value) was garbage collected but it should not have been!", .{}); + globalObject.throw("Internal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); return .zero; }; value.ensureStillAlive(); @@ -977,7 +977,7 @@ pub const Expect = struct { other_value.ensureStillAlive(); const value = Expect.capturedValueGetCached(thisValue) orelse { - globalObject.throw("Internal consistency error: thie expect(value) was garbage collected but it should not have been!", .{}); + globalObject.throw("Internal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); return .zero; }; value.ensureStillAlive(); @@ -1040,7 +1040,7 @@ pub const Expect = struct { other_value.ensureStillAlive(); const value = Expect.capturedValueGetCached(thisValue) orelse { - globalObject.throw("Internal consistency error: thie expect(value) was garbage collected but it should not have been!", .{}); + globalObject.throw("Internal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); return .zero; }; value.ensureStillAlive(); @@ -1080,6 +1080,71 @@ pub const Expect = struct { return .zero; } + pub fn toThrow(this: *Expect, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + defer this.postMatch(globalObject); + + const thisValue = callFrame.this(); + const _arguments = callFrame.arguments(1); + const arguments: []const JSValue = _arguments.ptr[0.._arguments.len]; + + if (arguments.len < 1) { + globalObject.throwInvalidArguments("toThrow() requires 1 argument", .{}); + return .zero; + } + + if (this.scope.tests.items.len <= this.test_id) { + globalObject.throw("toThrow() must be called in a test", .{}); + return .zero; + } + + active_test_expectation_counter.actual += 1; + + const expected = arguments[0]; + expected.ensureStillAlive(); + + const value = Expect.capturedValueGetCached(thisValue) orelse { + globalObject.throw("Internal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); + return .zero; + }; + value.ensureStillAlive(); + + if ((!value.jsType().isFunction())) { + globalObject.throw("Expected value must be a function", .{}); + return .zero; + } + + const not = this.op.contains(.not); + const result = value.call(globalObject, &.{}).toError(globalObject); + if (result.isUndefined()) { + if (not) return thisValue; + globalObject.throw("Expected function to throw", .{}); + return .zero; + } else if(not) { + globalObject.throw("Expected function not to throw", .{}); + return .zero; + } + + if (expected.isString()) { + const message = result.get(globalObject, "message"); + // TODO support partial match + const pass = if (message) |actual| expected.isSameValue(actual, globalObject) else false; + + if (pass) return thisValue; + globalObject.throw("\n\tExpected: {s}\n\tReceived: {s}", .{ + expected.getZigString(globalObject), + if (message) |actual| actual.getZigString(globalObject) else ZigString.init("undefined"), + }); + return .zero; + } + + if (result.isInstanceOf(globalObject, expected)) return thisValue; + globalObject.throw("\n\tExpected type: {s}\n\tReceived type: {s}", .{ + expected.getName(globalObject), + if (result.get(globalObject, "name")) |name| name.getZigString(globalObject) else ZigString.init("<UnknownError>"), + }); + return .zero; + } + pub const toHaveBeenCalledTimes = notImplementedJSCFn; pub const toHaveBeenCalledWith = notImplementedJSCFn; pub const toHaveBeenLastCalledWith = notImplementedJSCFn; @@ -1095,7 +1160,6 @@ pub const Expect = struct { pub const toMatchObject = notImplementedJSCFn; pub const toMatchSnapshot = notImplementedJSCFn; pub const toMatchInlineSnapshot = notImplementedJSCFn; - pub const toThrow = notImplementedJSCFn; pub const toThrowErrorMatchingSnapshot = notImplementedJSCFn; pub const toThrowErrorMatchingInlineSnapshot = notImplementedJSCFn; |