aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/bun.js')
-rw-r--r--src/bun.js/api/bun.zig34
-rw-r--r--src/bun.js/bindings/JSBuffer.cpp158
-rw-r--r--src/bun.js/bindings/JSBufferEncodingType.cpp79
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.cpp57
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.h3
-rw-r--r--src/bun.js/crypto.exports.js32
-rw-r--r--src/bun.js/node/node_os.zig140
-rw-r--r--src/bun.js/perf_hooks.exports.js1
-rw-r--r--src/bun.js/util.exports.js8
-rw-r--r--src/bun.js/webcore/encoding.zig49
10 files changed, 391 insertions, 170 deletions
diff --git a/src/bun.js/api/bun.zig b/src/bun.js/api/bun.zig
index 0b47bea27..da42e5feb 100644
--- a/src/bun.js/api/bun.zig
+++ b/src/bun.js/api/bun.zig
@@ -2873,19 +2873,25 @@ pub const Timer = struct {
const callback = this.callback.get() orelse @panic("Expected CallbackJob to have a callback function");
if (this.arguments.trySwap()) |arguments| {
- const count = arguments.getLengthOfArray(globalThis);
- if (count > 0) {
- if (count > args_buf.len) {
- args = bun.default_allocator.alloc(JSC.JSValue, count) catch unreachable;
- args_needs_deinit = true;
- } else {
- args = args_buf[0..count];
- }
- var arg = args.ptr;
- var i: u32 = 0;
- while (i < count) : (i += 1) {
- arg[0] = JSC.JSObject.getIndex(arguments, globalThis, @truncate(u32, i));
- arg += 1;
+ // Bun.sleep passes a Promise
+ if (arguments.jsType() == .JSPromise) {
+ args_buf[0] = arguments;
+ args = args_buf[0..1];
+ } else {
+ const count = arguments.getLengthOfArray(globalThis);
+ if (count > 0) {
+ if (count > args_buf.len) {
+ args = bun.default_allocator.alloc(JSC.JSValue, count) catch unreachable;
+ args_needs_deinit = true;
+ } else {
+ args = args_buf[0..count];
+ }
+ var arg = args.ptr;
+ var i: u32 = 0;
+ while (i < count) : (i += 1) {
+ arg[0] = JSC.JSObject.getIndex(arguments, globalThis, @truncate(u32, i));
+ arg += 1;
+ }
}
}
}
@@ -2916,7 +2922,7 @@ pub const Timer = struct {
this.deinit();
// get the value out of the promise
- _ = promise.result(this.globalThis.vm());
+ _ = promise.result(globalThis.vm());
},
.Pending => {
result.then(globalThis, this, CallbackJob__onResolve, CallbackJob__onReject);
diff --git a/src/bun.js/bindings/JSBuffer.cpp b/src/bun.js/bindings/JSBuffer.cpp
index 10002b664..2ce07617a 100644
--- a/src/bun.js/bindings/JSBuffer.cpp
+++ b/src/bun.js/bindings/JSBuffer.cpp
@@ -580,14 +580,14 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_compareBody(JSC::J
}
auto castedThisValue = callFrame->uncheckedArgument(0);
- JSC::JSUint8Array* castedThis = JSC::jsDynamicCast<JSC::JSUint8Array*>(castedThisValue);
+ JSC::JSArrayBufferView* castedThis = JSC::jsDynamicCast<JSC::JSArrayBufferView*>(castedThisValue);
if (UNLIKELY(!castedThis)) {
throwVMTypeError(lexicalGlobalObject, throwScope, "Expected Buffer (first argument)"_s);
return JSValue::encode(jsUndefined());
}
auto buffer = callFrame->uncheckedArgument(1);
- JSC::JSUint8Array* view = JSC::jsDynamicCast<JSC::JSUint8Array*>(buffer);
+ JSC::JSArrayBufferView* view = JSC::jsDynamicCast<JSC::JSArrayBufferView*>(buffer);
if (UNLIKELY(!view)) {
throwVMTypeError(lexicalGlobalObject, throwScope, "Expected Buffer (2nd argument)"_s);
return JSValue::encode(jsUndefined());
@@ -657,8 +657,8 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_compareBody(JSC::J
auto targetLength = targetEnd - targetStart;
auto actualLength = std::min(sourceLength, targetLength);
- auto sourceStartPtr = castedThis->typedVector() + sourceStart;
- auto targetStartPtr = view->typedVector() + targetStart;
+ auto sourceStartPtr = reinterpret_cast<unsigned char*>(castedThis->vector()) + sourceStart;
+ auto targetStartPtr = reinterpret_cast<unsigned char*>(view->vector()) + targetStart;
auto result = actualLength > 0 ? memcmp(sourceStartPtr, targetStartPtr, actualLength) : 0;
@@ -729,7 +729,10 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_concatBody(JSC::JS
static inline JSC::EncodedJSValue jsBufferConstructorFunction_isEncodingBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
- auto encoding_ = callFrame->argument(0).toString(lexicalGlobalObject);
+ auto* encoding_ = callFrame->argument(0).toStringOrNull(lexicalGlobalObject);
+ if (!encoding_)
+ return JSValue::encode(jsBoolean(false));
+
std::optional<BufferEncodingType> encoded = parseEnumeration<BufferEncodingType>(*lexicalGlobalObject, encoding_);
return JSValue::encode(jsBoolean(!!encoded));
}
@@ -955,7 +958,7 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_equalsBody(JSC::JSGl
}
auto buffer = callFrame->uncheckedArgument(0);
- JSC::JSUint8Array* view = JSC::jsDynamicCast<JSC::JSUint8Array*>(buffer);
+ JSC::JSArrayBufferView* view = JSC::jsDynamicCast<JSC::JSArrayBufferView*>(buffer);
if (UNLIKELY(!view)) {
throwVMTypeError(lexicalGlobalObject, throwScope, "Expected Buffer"_s);
return JSValue::encode(jsUndefined());
@@ -969,7 +972,7 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_equalsBody(JSC::JSGl
size_t a_length = castedThis->byteLength();
size_t b_length = view->byteLength();
auto sourceStartPtr = castedThis->typedVector();
- auto targetStartPtr = view->typedVector();
+ auto targetStartPtr = reinterpret_cast<unsigned char*>(view->vector());
// same pointer, same length, same contents
if (sourceStartPtr == targetStartPtr && a_length == b_length)
@@ -1347,6 +1350,73 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_swap64Body(JSC::JSGl
return JSC::JSValue::encode(castedThis);
}
+static inline JSC::EncodedJSValue jsBufferToString(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSUint8Array* castedThis, size_t offset, size_t length, WebCore::BufferEncodingType encoding)
+{
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ if (UNLIKELY(length == 0)) {
+ RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::jsEmptyString(vm)));
+ }
+
+ JSC::EncodedJSValue ret = 0;
+
+ switch (encoding) {
+ case WebCore::BufferEncodingType::latin1: {
+ LChar* data = nullptr;
+ auto str = String::createUninitialized(length, data);
+ memcpy(data, reinterpret_cast<const char*>(castedThis->typedVector() + offset), length);
+ return JSC::JSValue::encode(JSC::jsString(vm, WTFMove(str)));
+ }
+
+ case WebCore::BufferEncodingType::ucs2:
+ case WebCore::BufferEncodingType::utf16le: {
+ UChar* data = nullptr;
+ size_t u16length = length / 2;
+ if (u16length == 0) {
+ return JSC::JSValue::encode(JSC::jsEmptyString(vm));
+ } else {
+ auto str = String::createUninitialized(u16length, data);
+ // always zero out the last byte of the string incase the buffer is not a multiple of 2
+ data[u16length - 1] = 0;
+ memcpy(data, reinterpret_cast<const char*>(castedThis->typedVector() + offset), length);
+ return JSC::JSValue::encode(JSC::jsString(vm, WTFMove(str)));
+ }
+
+ break;
+ }
+
+ case WebCore::BufferEncodingType::ascii: {
+ // ascii: we always know the length
+ // so we might as well allocate upfront
+ LChar* data = nullptr;
+ auto str = String::createUninitialized(length, data);
+ Bun__encoding__writeLatin1(castedThis->typedVector() + offset, length, data, length, static_cast<uint8_t>(encoding));
+ return JSC::JSValue::encode(JSC::jsString(vm, WTFMove(str)));
+ }
+
+ case WebCore::BufferEncodingType::buffer:
+ case WebCore::BufferEncodingType::utf8:
+ case WebCore::BufferEncodingType::base64:
+ case WebCore::BufferEncodingType::base64url:
+ case WebCore::BufferEncodingType::hex: {
+ ret = Bun__encoding__toString(castedThis->typedVector() + offset, length, lexicalGlobalObject, static_cast<uint8_t>(encoding));
+ break;
+ }
+ default: {
+ throwTypeError(lexicalGlobalObject, scope, "Unsupported encoding? This shouldn't happen"_s);
+ break;
+ }
+ }
+
+ JSC::JSValue retValue = JSC::JSValue::decode(ret);
+ if (UNLIKELY(!retValue.isString())) {
+ scope.throwException(lexicalGlobalObject, retValue);
+ return JSC::JSValue::encode(jsUndefined());
+ }
+
+ RELEASE_AND_RETURN(scope, JSC::JSValue::encode(retValue));
+}
+
static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSArrayBufferView>::ClassParameter castedThis)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
@@ -1358,8 +1428,6 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JS
if (length == 0)
return JSC::JSValue::encode(JSC::jsEmptyString(vm));
- auto scope = DECLARE_THROW_SCOPE(vm);
-
switch (callFrame->argumentCount()) {
case 0: {
break;
@@ -1371,6 +1439,8 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JS
if (arg1.value().isString()) {
std::optional<BufferEncodingType> encoded = parseEnumeration<BufferEncodingType>(*lexicalGlobalObject, arg1.value());
if (!encoded) {
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
throwTypeError(lexicalGlobalObject, scope, "Invalid encoding"_s);
return JSC::JSValue::encode(jsUndefined());
}
@@ -1386,6 +1456,8 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JS
JSC::JSValue arg2 = callFrame->uncheckedArgument(1);
int32_t ioffset = arg2.toInt32(lexicalGlobalObject);
if (ioffset < 0) {
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
throwTypeError(lexicalGlobalObject, scope, "Offset must be a positive integer"_s);
return JSC::JSValue::encode(jsUndefined());
}
@@ -1403,61 +1475,33 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JS
length -= std::min(offset, length);
- if (UNLIKELY(length == 0)) {
- RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::jsEmptyString(vm)));
- }
+ return jsBufferToString(vm, lexicalGlobalObject, castedThis, offset, length, encoding);
+}
- JSC::EncodedJSValue ret = 0;
+// DOMJIT makes it slower! TODO: investigate why
+// JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(jsBufferPrototypeToStringWithoutTypeChecks, JSValue, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::JSUint8Array* thisValue, JSC::JSString* encodingValue));
- switch (encoding) {
- case WebCore::BufferEncodingType::latin1: {
- LChar* data = nullptr;
- auto str = String::createUninitialized(length, data);
- memcpy(data, reinterpret_cast<const char*>(castedThis->typedVector() + offset), length);
- ret = JSC::JSValue::encode(JSC::jsString(vm, WTFMove(str)));
- break;
- }
+// JSC_DEFINE_JIT_OPERATION(jsBufferPrototypeToStringWithoutTypeChecks, JSValue, (JSC::JSGlobalObject * lexicalGlobalObject, JSUint8Array* thisValue, JSString* encodingValue))
+// {
+// VM& vm = JSC::getVM(lexicalGlobalObject);
+// IGNORE_WARNINGS_BEGIN("frame-address")
+// CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
+// IGNORE_WARNINGS_END
+// JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
- case WebCore::BufferEncodingType::ucs2:
- case WebCore::BufferEncodingType::utf16le: {
- UChar* data = nullptr;
- size_t u16length = length / 2;
- if (u16length == 0) {
- ret = JSC::JSValue::encode(JSC::jsEmptyString(vm));
- } else {
- auto str = String::createUninitialized(u16length, data);
- // always zero out the last byte of the string incase the buffer is not a multiple of 2
- data[u16length - 1] = 0;
- memcpy(data, reinterpret_cast<const char*>(castedThis->typedVector() + offset), length);
- ret = JSC::JSValue::encode(JSC::jsString(vm, WTFMove(str)));
- }
+// std::optional<BufferEncodingType> encoded = parseEnumeration<BufferEncodingType>(*lexicalGlobalObject, encodingValue);
+// if (!encoded) {
+// auto scope = DECLARE_THROW_SCOPE(vm);
- break;
- }
+// throwTypeError(lexicalGlobalObject, scope, "Invalid encoding"_s);
+// return {};
+// }
- case WebCore::BufferEncodingType::buffer:
- case WebCore::BufferEncodingType::utf8:
- case WebCore::BufferEncodingType::ascii:
- case WebCore::BufferEncodingType::base64:
- case WebCore::BufferEncodingType::base64url:
- case WebCore::BufferEncodingType::hex: {
- ret = Bun__encoding__toString(castedThis->typedVector() + offset, length, lexicalGlobalObject, static_cast<uint8_t>(encoding));
- break;
- }
- default: {
- throwTypeError(lexicalGlobalObject, scope, "Unsupported encoding? This shouldn't happen"_s);
- break;
- }
- }
+// auto encoding = encoded.value();
- JSC::JSValue retValue = JSC::JSValue::decode(ret);
- if (UNLIKELY(!retValue.isString())) {
- scope.throwException(lexicalGlobalObject, retValue);
- return JSC::JSValue::encode(jsUndefined());
- }
+// return JSValue::decode(jsBufferToString(vm, lexicalGlobalObject, thisValue, 0, thisValue->byteLength(), encoding));
+// }
- RELEASE_AND_RETURN(scope, JSC::JSValue::encode(retValue));
-}
static inline JSC::EncodedJSValue jsBufferPrototypeFunction_writeBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSArrayBufferView>::ClassParameter castedThis)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
diff --git a/src/bun.js/bindings/JSBufferEncodingType.cpp b/src/bun.js/bindings/JSBufferEncodingType.cpp
index d65e90d2b..8d99dd4db 100644
--- a/src/bun.js/bindings/JSBufferEncodingType.cpp
+++ b/src/bun.js/bindings/JSBufferEncodingType.cpp
@@ -30,19 +30,21 @@
namespace WebCore {
using namespace JSC;
+static const NeverDestroyed<String> values[] = {
+ MAKE_STATIC_STRING_IMPL("utf8"),
+ MAKE_STATIC_STRING_IMPL("ucs2"),
+ MAKE_STATIC_STRING_IMPL("utf16le"),
+ MAKE_STATIC_STRING_IMPL("latin1"),
+ MAKE_STATIC_STRING_IMPL("ascii"),
+ MAKE_STATIC_STRING_IMPL("base64"),
+ MAKE_STATIC_STRING_IMPL("base64url"),
+ MAKE_STATIC_STRING_IMPL("hex"),
+ MAKE_STATIC_STRING_IMPL("buffer"),
+};
+
String convertEnumerationToString(BufferEncodingType enumerationValue)
{
- static const NeverDestroyed<String> values[] = {
- MAKE_STATIC_STRING_IMPL("utf8"),
- MAKE_STATIC_STRING_IMPL("ucs2"),
- MAKE_STATIC_STRING_IMPL("utf16le"),
- MAKE_STATIC_STRING_IMPL("latin1"),
- MAKE_STATIC_STRING_IMPL("ascii"),
- MAKE_STATIC_STRING_IMPL("base64"),
- MAKE_STATIC_STRING_IMPL("base64url"),
- MAKE_STATIC_STRING_IMPL("hex"),
- MAKE_STATIC_STRING_IMPL("buffer"),
- };
+
ASSERT(static_cast<size_t>(enumerationValue) < std::size(values));
return values[static_cast<size_t>(enumerationValue)];
}
@@ -55,9 +57,9 @@ template<> JSString* convertEnumerationToJS(JSGlobalObject& lexicalGlobalObject,
// this function is mostly copied from node
template<> std::optional<BufferEncodingType> parseEnumeration<BufferEncodingType>(JSGlobalObject& lexicalGlobalObject, JSValue value)
{
-
- JSC::JSString* str = value.toStringOrNull(&lexicalGlobalObject);
- if (!str)
+ // caller must check if value is a string
+ JSC::JSString* str = asString(value);
+ if (UNLIKELY(!str))
return std::nullopt;
auto encoding = str->value(&lexicalGlobalObject);
@@ -75,21 +77,7 @@ template<> std::optional<BufferEncodingType> parseEnumeration<BufferEncodingType
switch (encoding[0]) {
case 'u':
- case 'U':
- // utf8, utf16le
- if (encoding[1] == 't' && encoding[2] == 'f') {
- // Skip `-`
- const size_t skip = encoding[3] == '-' ? 4 : 3;
- if (encoding[skip] == '8' && encoding[skip + 1] == '\0')
- return BufferEncodingType::utf8;
- if (WTF::equalIgnoringASCIICase(encoding.substringSharingImpl(skip, 5), "16le"_s))
- return BufferEncodingType::ucs2;
- // ucs2
- } else if (encoding[1] == 'c' && encoding[2] == 's') {
- const size_t skip = encoding[3] == '-' ? 4 : 3;
- if (encoding[skip] == '2' && encoding[skip + 1] == '\0')
- return BufferEncodingType::ucs2;
- }
+ case 'U': {
if (WTF::equalIgnoringASCIICase(encoding, "utf8"_s))
return BufferEncodingType::utf8;
if (WTF::equalIgnoringASCIICase(encoding, "utf-8"_s))
@@ -103,35 +91,17 @@ template<> std::optional<BufferEncodingType> parseEnumeration<BufferEncodingType
if (WTF::equalIgnoringASCIICase(encoding, "utf-16le"_s))
return BufferEncodingType::ucs2;
break;
+ }
case 'l':
- case 'L':
- // latin1
- if (encoding[1] == 'a') {
- if (WTF::equalIgnoringASCIICase(encoding.substringSharingImpl(2, 4), "tin1"_s))
- return BufferEncodingType::latin1;
- }
+ case 'L': {
if (WTF::equalIgnoringASCIICase(encoding, "latin1"_s))
return BufferEncodingType::latin1;
break;
+ }
case 'b':
- case 'B':
- // binary is a deprecated alias of latin1
- if (encoding[1] == 'i') {
- if (WTF::equalIgnoringASCIICase(encoding.substringSharingImpl(2, 5), "nary"_s))
- return BufferEncodingType::latin1;
- // buffer
- } else if (encoding[1] == 'u') {
- if (WTF::equalIgnoringASCIICase(encoding.substringSharingImpl(2, 5), "ffer"_s))
- return BufferEncodingType::buffer;
- // base64
- } else if (encoding[1] == 'a') {
- if (WTF::equalIgnoringASCIICase(encoding.substringSharingImpl(2, 5), "se64"_s))
- return BufferEncodingType::base64;
- if (WTF::equalIgnoringASCIICase(encoding.substringSharingImpl(2, 8), "se64url"_s))
- return BufferEncodingType::base64url;
- }
+ case 'B': {
if (WTF::equalIgnoringASCIICase(encoding, "binary"_s))
return BufferEncodingType::latin1; // BINARY is a deprecated alias of LATIN1.
if (WTF::equalIgnoringASCIICase(encoding, "buffer"_s))
@@ -141,15 +111,12 @@ template<> std::optional<BufferEncodingType> parseEnumeration<BufferEncodingType
if (WTF::equalIgnoringASCIICase(encoding, "base64url"_s))
return BufferEncodingType::base64url;
break;
+ }
case 'a':
case 'A':
// ascii
- if (encoding[1] == 's') {
- if (WTF::equalIgnoringASCIICase(encoding.substringSharingImpl(2, 3), "cii"_s))
- return BufferEncodingType::ascii;
- }
- if (WTF::equalIgnoringASCIICase(encoding, "ascii"_s))
+ if (WTF::equalLettersIgnoringASCIICase(encoding, "ascii"_s))
return BufferEncodingType::ascii;
break;
diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp
index bfd775cde..54ccba343 100644
--- a/src/bun.js/bindings/ZigGlobalObject.cpp
+++ b/src/bun.js/bindings/ZigGlobalObject.cpp
@@ -108,6 +108,7 @@
#include "ModuleLoader.h"
#include "ZigGeneratedClasses.h"
+#include "JavaScriptCore/DateInstance.h"
#include "BunPlugin.h"
@@ -713,7 +714,7 @@ JSC_DEFINE_CUSTOM_GETTER(lazyProcessEnvGetter,
globalObject->processEnvObject());
}
-static JSC_DEFINE_HOST_FUNCTION(functionQueueMicrotask,
+JSC_DEFINE_HOST_FUNCTION(functionQueueMicrotask,
(JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
JSC::VM& vm = globalObject->vm();
@@ -741,7 +742,7 @@ static JSC_DEFINE_HOST_FUNCTION(functionQueueMicrotask,
return JSC::JSValue::encode(JSC::jsUndefined());
}
-static JSC_DEFINE_HOST_FUNCTION(functionSetTimeout,
+JSC_DEFINE_HOST_FUNCTION(functionSetTimeout,
(JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
JSC::VM& vm = globalObject->vm();
@@ -790,6 +791,45 @@ static JSC_DEFINE_HOST_FUNCTION(functionSetTimeout,
return Bun__Timer__setTimeout(globalObject, JSC::JSValue::encode(job), JSC::JSValue::encode(num), JSValue::encode(arguments));
}
+JSC_DEFINE_HOST_FUNCTION(functionBunSleepThenCallback,
+ (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
+{
+ JSC::VM& vm = globalObject->vm();
+
+ RELEASE_ASSERT(callFrame->argumentCount() == 1);
+ JSPromise* promise = jsCast<JSC::JSPromise*>(callFrame->argument(0));
+ RELEASE_ASSERT(promise);
+
+ promise->resolve(globalObject, JSC::jsUndefined());
+
+ return JSC::JSValue::encode(promise);
+}
+
+JSC_DEFINE_HOST_FUNCTION(functionBunSleep,
+ (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
+{
+ JSC::VM& vm = globalObject->vm();
+
+ JSC::JSValue millisecondsValue = callFrame->argument(0);
+
+ if (millisecondsValue.inherits<JSC::DateInstance>()) {
+ auto now = MonotonicTime::now();
+ auto milliseconds = jsCast<JSC::DateInstance*>(millisecondsValue)->internalNumber() - now.approximateWallTime().secondsSinceEpoch().milliseconds();
+ millisecondsValue = JSC::jsNumber(milliseconds > 0 ? milliseconds : 0);
+ }
+
+ if (!millisecondsValue.isNumber()) {
+ auto scope = DECLARE_THROW_SCOPE(globalObject->vm());
+ JSC::throwTypeError(globalObject, scope, "sleep expects a number (milliseconds)"_s);
+ return JSC::JSValue::encode(JSC::JSValue {});
+ }
+
+ Zig::GlobalObject* global = JSC::jsCast<Zig::GlobalObject*>(globalObject);
+ JSC::JSPromise* promise = JSC::JSPromise::create(vm, globalObject->promiseStructure());
+ Bun__Timer__setTimeout(globalObject, JSC::JSValue::encode(global->bunSleepThenCallback()), JSC::JSValue::encode(millisecondsValue), JSValue::encode(promise));
+ return JSC::JSValue::encode(promise);
+}
+
static JSC_DEFINE_HOST_FUNCTION(functionSetInterval,
(JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
@@ -2490,6 +2530,11 @@ void GlobalObject::finishCreation(VM& vm)
init.set(JSFunction::create(init.vm, init.owner, 4, "emitReadable"_s, WebCore::jsReadable_emitReadable_, ImplementationVisibility::Public));
});
+ m_bunSleepThenCallback.initLater(
+ [](const Initializer<JSFunction>& init) {
+ init.set(JSFunction::create(init.vm, init.owner, 1, "onSleep"_s, functionBunSleepThenCallback, ImplementationVisibility::Public));
+ });
+
m_performMicrotaskVariadicFunction.initLater(
[](const Initializer<JSFunction>& init) {
init.set(JSFunction::create(init.vm, init.owner, 4, "performMicrotaskVariadic"_s, jsFunctionPerformMicrotaskVariadic, ImplementationVisibility::Public));
@@ -3344,6 +3389,13 @@ void GlobalObject::installAPIGlobals(JSClassRef* globals, int count, JSC::VM& vm
{
+ JSC::Identifier identifier = JSC::Identifier::fromString(vm, "sleep"_s);
+ object->putDirectNativeFunction(vm, this, identifier, 1, functionBunSleep, ImplementationVisibility::Public, NoIntrinsic,
+ JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0);
+ }
+
+ {
+
JSC::Identifier identifier = JSC::Identifier::fromString(vm, "env"_s);
object->putDirectCustomAccessor(vm, identifier,
JSC::CustomGetterSetter::create(vm, lazyProcessEnvGetter, lazyProcessEnvSetter),
@@ -3529,6 +3581,7 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
thisObject->m_requireResolveFunctionStructure.visit(visitor);
thisObject->m_resolveFunctionPrototype.visit(visitor);
thisObject->m_dnsObject.visit(visitor);
+ thisObject->m_bunSleepThenCallback.visit(visitor);
for (auto& barrier : thisObject->m_thenables) {
visitor.append(barrier);
diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h
index f2364fd61..2b688f09d 100644
--- a/src/bun.js/bindings/ZigGlobalObject.h
+++ b/src/bun.js/bindings/ZigGlobalObject.h
@@ -247,6 +247,8 @@ public:
Structure* requireResolveFunctionStructure() { return m_requireResolveFunctionStructure.getInitializedOnMainThread(this); }
JSObject* requireResolveFunctionPrototype() { return m_resolveFunctionPrototype.getInitializedOnMainThread(this); }
+ JSFunction* bunSleepThenCallback() { return m_bunSleepThenCallback.getInitializedOnMainThread(this); }
+
JSObject* dnsObject() { return m_dnsObject.getInitializedOnMainThread(this); }
JSC::JSObject* processObject()
@@ -453,6 +455,7 @@ private:
LazyProperty<JSGlobalObject, JSC::Structure> m_requireResolveFunctionStructure;
LazyProperty<JSGlobalObject, JSObject> m_resolveFunctionPrototype;
LazyProperty<JSGlobalObject, JSObject> m_dnsObject;
+ LazyProperty<JSGlobalObject, JSFunction> m_bunSleepThenCallback;
DOMGuardedObjectSet m_guardedObjects WTF_GUARDED_BY_LOCK(m_gcLock);
void* m_bunVM;
diff --git a/src/bun.js/crypto.exports.js b/src/bun.js/crypto.exports.js
index fb38a9352..6f9e82b5d 100644
--- a/src/bun.js/crypto.exports.js
+++ b/src/bun.js/crypto.exports.js
@@ -6,6 +6,8 @@ var __getProtoOf = Object.getPrototypeOf,
__hasOwnProp = Object.prototype.hasOwnProperty;
var __require = id => import.meta.require(id);
+const crypto = globalThis.crypto;
+const globalCrypto = crypto;
var __esm = (fn, res) =>
function () {
@@ -89,7 +91,7 @@ var require_browser = __commonJS({
Use Chrome, Firefox or Internet Explorer 11`);
}
var Buffer2 = require_safe_buffer().Buffer,
- crypto2 = global.crypto || global.msCrypto;
+ crypto2 = globalCrypto;
crypto2 && crypto2.getRandomValues ? (module.exports = randomBytes) : (module.exports = oldBrowser);
function randomBytes(size, cb) {
if (size > MAX_UINT32) throw new RangeError("requested too many random bytes");
@@ -1588,7 +1590,7 @@ var require_async = __commonJS({
sync = require_sync_browser(),
toBuffer = require_to_buffer(),
ZERO_BUF,
- subtle = global.crypto && global.crypto.subtle,
+ subtle = globalCrypto.subtle,
toBrowser = {
sha: "SHA-1",
"sha-1": "SHA-1",
@@ -23553,15 +23555,10 @@ var require_browser10 = __commonJS({
var require_browser11 = __commonJS({
"node_modules/randomfill/browser.js"(exports) {
"use strict";
- function oldBrowser() {
- throw new Error(`secure random number generation not supported by this browser
-use chrome, FireFox or Internet Explorer 11`);
- }
var safeBuffer = require_safe_buffer(),
randombytes = require_browser(),
Buffer2 = safeBuffer.Buffer,
kBufferMaxLength = safeBuffer.kMaxLength,
- crypto2 = global.crypto || global.msCrypto,
kMaxUint32 = Math.pow(2, 32) - 1;
function assertOffset(offset, length) {
if (typeof offset != "number" || offset !== offset) throw new TypeError("offset must be a number");
@@ -23654,6 +23651,7 @@ var require_crypto_browserify2 = __commonJS({
exports.privateEncrypt = publicEncrypt.privateEncrypt;
exports.publicDecrypt = publicEncrypt.publicDecrypt;
exports.privateDecrypt = publicEncrypt.privateDecrypt;
+ exports.getRandomValues = values => crypto.getRandomValues(values);
var rf = require_browser11();
exports.randomFill = rf.randomFill;
exports.randomFillSync = rf.randomFillSync;
@@ -23692,15 +23690,6 @@ var crypto_exports = {
...require_crypto_browserify2(),
[Symbol.for("CommonJS")]: 0,
};
-__export(crypto_exports, {
- DEFAULT_ENCODING: () => DEFAULT_ENCODING,
- getRandomValues: () => getRandomValues,
- randomUUID: () => randomUUID,
- scrypt: () => scrypt,
- scryptSync: () => scryptSync,
- timingSafeEqual: () => timingSafeEqual,
- webcrypto: () => webcrypto,
-});
var DEFAULT_ENCODING = "buffer",
getRandomValues = array => crypto.getRandomValues(array),
randomUUID = () => crypto.randomUUID(),
@@ -23754,6 +23743,17 @@ timingSafeEqual &&
value: "::bunternal::",
}));
var webcrypto = crypto;
+__export(crypto_exports, {
+ DEFAULT_ENCODING: () => DEFAULT_ENCODING,
+ getRandomValues: () => getRandomValues,
+ randomUUID: () => randomUUID,
+ scrypt: () => scrypt,
+ scryptSync: () => scryptSync,
+ timingSafeEqual: () => timingSafeEqual,
+ webcrypto: () => webcrypto,
+ subtle: () => webcrypto.subtle,
+});
+
export const {
randomBytes,
rng,
diff --git a/src/bun.js/node/node_os.zig b/src/bun.js/node/node_os.zig
index 525ee991b..f7a91393e 100644
--- a/src/bun.js/node/node_os.zig
+++ b/src/bun.js/node/node_os.zig
@@ -54,11 +54,147 @@ pub const Os = struct {
return JSC.ZigString.init(Global.arch_name).withEncoding().toValue(globalThis);
}
+ const CPU = struct {
+ model: JSC.ZigString = JSC.ZigString.init("unknown"),
+ speed: u64 = 0,
+ times: struct {
+ user: u64 = 0,
+ nice: u64 = 0,
+ sys: u64 = 0,
+ idle: u64 = 0,
+ irq: u64 = 0,
+ } = .{}
+ };
+
pub fn cpus(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue {
JSC.markBinding(@src());
- // TODO:
- return JSC.JSArray.from(globalThis, &.{});
+ var cpu_buffer: [8192]CPU = undefined;
+ const cpus_or_error = if (comptime Environment.isLinux)
+ cpusImplLinux(&cpu_buffer)
+ else
+ @as(anyerror![]CPU, cpu_buffer[0..0]); // unsupported platform -> empty array
+
+ if (cpus_or_error) |list| {
+ // Convert the CPU list to a JS Array
+ const values = JSC.JSValue.createEmptyArray(globalThis, list.len);
+ for (list) |cpu, cpu_index| {
+ const obj = JSC.JSValue.createEmptyObject(globalThis, 3);
+ obj.put(globalThis, JSC.ZigString.static("model"), cpu.model.withEncoding().toValueGC(globalThis));
+ obj.put(globalThis, JSC.ZigString.static("speed"), JSC.JSValue.jsNumberFromUint64(cpu.speed));
+
+ const timesFields = comptime std.meta.fieldNames(@TypeOf(cpu.times));
+ const times = JSC.JSValue.createEmptyObject(globalThis, 5);
+ inline for (timesFields) |fieldName| {
+ times.put(globalThis, JSC.ZigString.static(fieldName),
+ JSC.JSValue.jsNumberFromUint64(@field(cpu.times, fieldName)));
+ }
+ obj.put(globalThis, JSC.ZigString.static("times"), times);
+ values.putIndex(globalThis, @intCast(u32, cpu_index), obj);
+ }
+ return values;
+
+ } else |zig_err| {
+ const msg = switch (zig_err) {
+ error.too_many_cpus => "Too many CPUs or malformed /proc/cpuinfo file",
+ error.eol => "Malformed /proc/stat file",
+ else => "An error occurred while fetching cpu information",
+ };
+ //TODO more suitable error type?
+ const err = JSC.SystemError{
+ .message = JSC.ZigString.init(msg),
+ };
+ globalThis.vm().throwError(globalThis, err.toErrorInstance(globalThis));
+ return JSC.JSValue.jsUndefined();
+ }
+ }
+
+ fn cpusImplLinux(cpu_buffer: []CPU) ![]CPU {
+ // Use a large line buffer because the /proc/stat file can have a very long list of interrupts
+ var line_buffer: [1024*8]u8 = undefined;
+ var num_cpus: usize = 0;
+
+ // Read /proc/stat to get number of CPUs and times
+ if (std.fs.openFileAbsolute("/proc/stat", .{})) |file| {
+ defer file.close();
+ var reader = file.reader();
+
+ // Skip the first line (aggregate of all CPUs)
+ try reader.skipUntilDelimiterOrEof('\n');
+
+ // Read each CPU line
+ while (try reader.readUntilDelimiterOrEof(&line_buffer, '\n')) |line| {
+
+ if (num_cpus >= cpu_buffer.len) return error.too_many_cpus;
+
+ // CPU lines are formatted as `cpu0 user nice sys idle iowait irq softirq`
+ var toks = std.mem.tokenize(u8, line, " \t");
+ const cpu_name = toks.next();
+ if (cpu_name == null or !std.mem.startsWith(u8, cpu_name.?, "cpu")) break; // done with CPUs
+
+ // Default initialize the CPU to ensure that we never return uninitialized fields
+ cpu_buffer[num_cpus] = CPU{};
+
+ //NOTE: libuv assumes this is fixed on Linux, not sure that's actually the case
+ const scale = 10;
+ cpu_buffer[num_cpus].times.user = scale * try std.fmt.parseInt(u64, toks.next() orelse return error.eol, 10);
+ cpu_buffer[num_cpus].times.nice = scale * try std.fmt.parseInt(u64, toks.next() orelse return error.eol, 10);
+ cpu_buffer[num_cpus].times.sys = scale * try std.fmt.parseInt(u64, toks.next() orelse return error.eol, 10);
+ cpu_buffer[num_cpus].times.idle = scale * try std.fmt.parseInt(u64, toks.next() orelse return error.eol, 10);
+ _ = try (toks.next() orelse error.eol); // skip iowait
+ cpu_buffer[num_cpus].times.irq = scale * try std.fmt.parseInt(u64, toks.next() orelse return error.eol, 10);
+
+ num_cpus += 1;
+ }
+ } else |_| {
+ return error.cannot_open_proc_stat;
+ }
+
+ const slice = cpu_buffer[0..num_cpus];
+
+ // Read /proc/cpuinfo to get model information (optional)
+ if (std.fs.openFileAbsolute("/proc/cpuinfo", .{})) |file| {
+ defer file.close();
+ var reader = file.reader();
+ const key_processor = "processor\t: ";
+ const key_model_name = "model name\t: ";
+
+ var cpu_index: usize = 0;
+ while (try reader.readUntilDelimiterOrEof(&line_buffer, '\n')) |line| {
+
+ if (std.mem.startsWith(u8, line, key_processor)) {
+ // If this line starts a new processor, parse the index from the line
+ const digits = std.mem.trim(u8, line[key_processor.len..], " \t\n");
+ cpu_index = try std.fmt.parseInt(usize, digits, 10);
+ if (cpu_index >= slice.len) return error.too_may_cpus;
+
+ } else if (std.mem.startsWith(u8, line, key_model_name)) {
+ // If this is the model name, extract it and store on the current cpu
+ const model_name = line[key_model_name.len..];
+ slice[cpu_index].model = JSC.ZigString.init(model_name);
+ }
+ //TODO: special handling for ARM64 (no model name)?
+ }
+ } else |_| {
+ // Do nothing: CPU default initializer has set model name to "unknown"
+ }
+
+ // Read /sys/devices/system/cpu/cpu{}/cpufreq/scaling_cur_freq to get current frequency (optional)
+ for (slice) |*cpu, cpu_index| {
+ var path_buf: [128]u8 = undefined;
+ const path = try std.fmt.bufPrint(&path_buf, "/sys/devices/system/cpu/cpu{}/cpufreq/scaling_cur_freq", .{cpu_index});
+ if (std.fs.openFileAbsolute(path, .{})) |file| {
+ defer file.close();
+
+ const bytes_read = try file.readAll(&line_buffer);
+ const digits = std.mem.trim(u8, line_buffer[0..bytes_read], " \n");
+ cpu.speed = try std.fmt.parseInt(u64, digits, 10) / 1000;
+ } else |_| {
+ // Do nothing: CPU default initializer has set speed to 0
+ }
+ }
+
+ return slice;
}
pub fn endianness(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue {
diff --git a/src/bun.js/perf_hooks.exports.js b/src/bun.js/perf_hooks.exports.js
index 8a1a9a915..c461abafe 100644
--- a/src/bun.js/perf_hooks.exports.js
+++ b/src/bun.js/perf_hooks.exports.js
@@ -22,4 +22,5 @@ export default {
PerformanceEntry,
PerformanceEntry,
PerformanceNodeTiming,
+ [Symbol.for("CommonJS")]: 0,
};
diff --git a/src/bun.js/util.exports.js b/src/bun.js/util.exports.js
index 1bba01977..c0adc0344 100644
--- a/src/bun.js/util.exports.js
+++ b/src/bun.js/util.exports.js
@@ -27,8 +27,11 @@ var require_inherits_browser = __commonJS({
};
},
});
-
-const exports = {};
+const deepEquals = Bun.deepEquals;
+const isDeepStrictEqual = (a, b) => deepEquals(a, b, true);
+const exports = {
+ isDeepStrictEqual,
+};
var getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors;
var formatRegExp = /%[sdj%]/g;
function format(f) {
@@ -576,4 +579,5 @@ export {
inherits,
promisify,
callbackify,
+ isDeepStrictEqual,
};
diff --git a/src/bun.js/webcore/encoding.zig b/src/bun.js/webcore/encoding.zig
index 6729cc4de..8a6e3224b 100644
--- a/src/bun.js/webcore/encoding.zig
+++ b/src/bun.js/webcore/encoding.zig
@@ -795,15 +795,19 @@ pub const Encoder = struct {
switch (comptime encoding) {
.ascii => {
- var to = allocator.alloc(u8, len) catch return ZigString.init("Out of memory").toErrorInstance(global);
-
- @memcpy(to.ptr, input_ptr, to.len);
+ if (bun.simdutf.validate.ascii(input)) {
+ return ZigString.init(input).toValueGC(global);
+ }
- // Hoping this gets auto vectorized
- for (to[0..to.len]) |c, i| {
- to[i] = @as(u8, @truncate(u7, c));
+ if (input.len < 512) {
+ var buf: [512]u8 = undefined;
+ var to = buf[0..input.len];
+ strings.copyLatin1IntoASCII(to, input);
+ return ZigString.init(to).toValueGC(global);
}
+ var to = allocator.alloc(u8, len) catch return ZigString.init("Out of memory").toErrorInstance(global);
+ strings.copyLatin1IntoASCII(to, input);
return ZigString.init(to).toExternalValue(global);
},
.latin1 => {
@@ -857,7 +861,7 @@ pub const Encoder = struct {
}
}
- pub fn writeU8(input: [*]const u8, len: usize, to: [*]u8, to_len: usize, comptime encoding: JSC.Node.Encoding) i64 {
+ pub fn writeU8(input: [*]const u8, len: usize, to_ptr: [*]u8, to_len: usize, comptime encoding: JSC.Node.Encoding) i64 {
if (len == 0 or to_len == 0)
return 0;
@@ -871,39 +875,42 @@ pub const Encoder = struct {
switch (comptime encoding) {
JSC.Node.Encoding.buffer => {
const written = @min(len, to_len);
- @memcpy(to, input, written);
+ @memcpy(to_ptr, input, written);
return @intCast(i64, written);
},
.latin1, .ascii => {
const written = @min(len, to_len);
- @memcpy(to, input, written);
- // Hoping this gets auto vectorized
- for (to[0..written]) |c, i| {
- to[i] = @as(u8, @truncate(u7, c));
+ var to = to_ptr[0..written];
+ var remain = input[0..written];
+
+ if (bun.simdutf.validate.ascii(remain)) {
+ @memcpy(to.ptr, remain.ptr, written);
+ } else {
+ strings.copyLatin1IntoASCII(to, remain);
}
return @intCast(i64, written);
},
.utf8 => {
// need to encode
- return @intCast(i64, strings.copyLatin1IntoUTF8(to[0..to_len], []const u8, input[0..len]).written);
+ return @intCast(i64, strings.copyLatin1IntoUTF8(to_ptr[0..to_len], []const u8, input[0..len]).written);
},
// encode latin1 into UTF16
JSC.Node.Encoding.ucs2, JSC.Node.Encoding.utf16le => {
if (to_len < 2)
return 0;
- if (std.mem.isAligned(@ptrToInt(to), @alignOf([*]u16))) {
+ if (std.mem.isAligned(@ptrToInt(to_ptr), @alignOf([*]u16))) {
var buf = input[0..len];
- var output = @ptrCast([*]u16, @alignCast(@alignOf(u16), to))[0 .. to_len / 2];
+ var output = @ptrCast([*]u16, @alignCast(@alignOf(u16), to_ptr))[0 .. to_len / 2];
var written = strings.copyLatin1IntoUTF16([]u16, output, []const u8, buf).written;
return written * 2;
} else {
var buf = input[0..len];
- var output = @ptrCast([*]align(1) u16, to)[0 .. to_len / 2];
+ var output = @ptrCast([*]align(1) u16, to_ptr)[0 .. to_len / 2];
var written = strings.copyLatin1IntoUTF16([]align(1) u16, output, []const u8, buf).written;
return written * 2;
@@ -911,7 +918,7 @@ pub const Encoder = struct {
},
JSC.Node.Encoding.hex => {
- return @intCast(i64, strings.decodeHexToBytes(to[0..to_len], u8, input[0..len]));
+ return @intCast(i64, strings.decodeHexToBytes(to_ptr[0..to_len], u8, input[0..len]));
},
JSC.Node.Encoding.base64url => {
@@ -919,18 +926,18 @@ pub const Encoder = struct {
if (slice.len == 0)
return 0;
- if (strings.eqlComptime(slice[slice.len - 2 ..][0..2], "==")) {
+ if (strings.endsWithComptime(slice, "==")) {
slice = slice[0 .. slice.len - 2];
} else if (slice[slice.len - 1] == '=') {
slice = slice[0 .. slice.len - 1];
}
- const wrote = bun.base64.decodeURLSafe(to[0..to_len], slice).written;
+ const wrote = bun.base64.decodeURLSafe(to_ptr[0..to_len], slice).written;
return @intCast(i64, wrote);
},
JSC.Node.Encoding.base64 => {
- return @intCast(i64, bun.base64.decode(to[0..to_len], input[0..len]).written);
+ return @intCast(i64, bun.base64.decode(to_ptr[0..to_len], input[0..len]).written);
},
// else => return 0,
}
@@ -1094,7 +1101,7 @@ pub const Encoder = struct {
if (slice.len == 0)
return &[_]u8{};
- if (strings.eqlComptime(slice[slice.len - 2 ..][0..2], "==")) {
+ if (strings.endsWithComptime(slice, "==")) {
slice = slice[0 .. slice.len - 2];
} else if (slice[slice.len - 1] == '=') {
slice = slice[0 .. slice.len - 1];