Files
bun.sh/src/bun.js/bindings/wtf-bindings.cpp
Jarred Sumner f4d14c02c7 Implement process.report.getReport() (#7171)
* Prevent assertion failure

* Implement process.report.getReport()

* Update process.test.js

* }

* Update BunProcess.cpp

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-11-17 03:35:35 -08:00

310 lines
10 KiB
C++

#include "root.h"
#include "wtf-bindings.h"
#include <wtf/StackTrace.h>
#include <wtf/dtoa.h>
#include <atomic>
#if OS(WINDOWS)
#include <uv.h>
#endif
#if !OS(WINDOWS)
#include <stdatomic.h>
#include <termios.h>
static int orig_termios_fd = -1;
static struct termios orig_termios;
static _Atomic int orig_termios_spinlock;
static std::once_flag reset_once_flag;
static int current_tty_mode = 0;
static struct termios orig_tty_termios;
int uv__tcsetattr(int fd, int how, const struct termios* term)
{
int rc;
do
rc = tcsetattr(fd, how, term);
while (rc == -1 && errno == EINTR);
if (rc == -1)
return errno;
return 0;
}
extern "C" int uv_tty_reset_mode(void)
{
int saved_errno;
int err;
saved_errno = errno;
if (atomic_exchange(&orig_termios_spinlock, 1))
return 16; // UV_EBUSY; /* In uv_tty_set_mode(). */
err = 0;
if (orig_termios_fd != -1)
err = uv__tcsetattr(orig_termios_fd, TCSANOW, &orig_termios);
atomic_store(&orig_termios_spinlock, 0);
errno = saved_errno;
return err;
}
static void uv__tty_make_raw(struct termios* tio)
{
assert(tio != NULL);
#if defined __sun || defined __MVS__
/*
* This implementation of cfmakeraw for Solaris and derivatives is taken from
* http://www.perkin.org.uk/posts/solaris-portability-cfmakeraw.html.
*/
tio->c_iflag &= ~(IMAXBEL | IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
tio->c_oflag &= ~OPOST;
tio->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
tio->c_cflag &= ~(CSIZE | PARENB);
tio->c_cflag |= CS8;
/*
* By default, most software expects a pending read to block until at
* least one byte becomes available. As per termio(7I), this requires
* setting the MIN and TIME parameters appropriately.
*
* As a somewhat unfortunate artifact of history, the MIN and TIME slots
* in the control character array overlap with the EOF and EOL slots used
* for canonical mode processing. Because the EOF character needs to be
* the ASCII EOT value (aka Control-D), it has the byte value 4. When
* switching to raw mode, this is interpreted as a MIN value of 4; i.e.,
* reads will block until at least four bytes have been input.
*
* Other platforms with a distinct MIN slot like Linux and FreeBSD appear
* to default to a MIN value of 1, so we'll force that value here:
*/
tio->c_cc[VMIN] = 1;
tio->c_cc[VTIME] = 0;
#else
cfmakeraw(tio);
#endif /* #ifdef __sun */
}
#endif
extern "C" void Bun__atexit(void (*func)(void));
extern "C" int Bun__ttySetMode(int fd, int mode)
{
#if !OS(WINDOWS)
struct termios tmp;
int expected;
int rc;
if (current_tty_mode == mode)
return 0;
if (current_tty_mode == 0 && mode != 0) {
do {
rc = tcgetattr(fd, &orig_tty_termios);
} while (rc == -1 && errno == EINTR);
if (rc == -1)
return errno;
/* This is used for uv_tty_reset_mode() */
do {
expected = 0;
} while (!atomic_compare_exchange_strong(&orig_termios_spinlock, &expected, 1));
if (orig_termios_fd == -1) {
orig_termios = orig_tty_termios;
orig_termios_fd = fd;
}
atomic_store(&orig_termios_spinlock, 0);
}
tmp = orig_tty_termios;
switch (mode) {
case 0: // normal
break;
case 1: // raw
tmp.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
tmp.c_oflag |= (ONLCR);
tmp.c_cflag |= (CS8);
tmp.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
tmp.c_cc[VMIN] = 1;
tmp.c_cc[VTIME] = 0;
std::call_once(reset_once_flag, [] {
Bun__atexit([] {
uv_tty_reset_mode();
});
});
break;
case 2: // io
uv__tty_make_raw(&tmp);
std::call_once(reset_once_flag, [] {
Bun__atexit([] {
uv_tty_reset_mode();
});
});
break;
}
/* Apply changes after draining */
rc = uv__tcsetattr(fd, TCSADRAIN, &tmp);
if (rc == 0)
current_tty_mode = mode;
return rc;
#else
return 0;
#endif
}
extern "C" double WTF__parseDouble(const LChar* string, size_t length, size_t* position)
{
return WTF::parseDouble(string, length, *position);
}
extern "C" void WTF__copyLCharsFromUCharSource(LChar* destination, const UChar* source, size_t length)
{
WTF::StringImpl::copyCharacters(destination, source, length);
}
extern "C" void Bun__crashReportWrite(void* ctx, const char* message, size_t length);
extern "C" void Bun__crashReportDumpStackTrace(void* ctx)
{
static constexpr int framesToShow = 32;
static constexpr int framesToSkip = 2;
void* stack[framesToShow + framesToSkip];
int frames = framesToShow + framesToSkip;
WTFGetBacktrace(stack, &frames);
int size = frames - framesToSkip;
bool isFirst = true;
for (int frameNumber = 0; frameNumber < size; ++frameNumber) {
auto demangled = WTF::StackTraceSymbolResolver::demangle(stack[frameNumber]);
StringPrintStream out;
if (isFirst) {
isFirst = false;
if (demangled)
out.printf("\n%-3d %p %s", frameNumber, stack[frameNumber], demangled->demangledName() ? demangled->demangledName() : demangled->mangledName());
else
out.printf("\n%-3d %p", frameNumber, stack[frameNumber]);
} else {
if (demangled)
out.printf("%-3d ??? %s", frameNumber, demangled->demangledName() ? demangled->demangledName() : demangled->mangledName());
else
out.printf("%-3d ???", frameNumber);
}
auto str = out.toCString();
Bun__crashReportWrite(ctx, str.data(), str.length());
}
}
// For whatever reason
// Doing this in C++/C is 2x faster than doing it in Zig.
// However, it's still slower than it should be.
static constexpr size_t encodeMapSize = 64;
static constexpr char base64URLEncMap[encodeMapSize] = {
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B,
0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56,
0x57, 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72,
0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, 0x32,
0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2D, 0x5F
};
extern "C" size_t WTF__base64URLEncode(const unsigned char* __restrict inputDataBuffer, size_t inputDataBufferSize,
unsigned char* __restrict destinationDataBuffer,
size_t destinationDataBufferSize)
{
size_t sidx = 0;
size_t didx = 0;
if (inputDataBufferSize > 1) {
while (sidx < inputDataBufferSize - 2) {
destinationDataBuffer[didx++] = base64URLEncMap[(inputDataBuffer[sidx] >> 2) & 077];
destinationDataBuffer[didx++] = base64URLEncMap[((inputDataBuffer[sidx + 1] >> 4) & 017) | ((inputDataBuffer[sidx] << 4) & 077)];
destinationDataBuffer[didx++] = base64URLEncMap[((inputDataBuffer[sidx + 2] >> 6) & 003) | ((inputDataBuffer[sidx + 1] << 2) & 077)];
destinationDataBuffer[didx++] = base64URLEncMap[inputDataBuffer[sidx + 2] & 077];
sidx += 3;
}
}
if (sidx < inputDataBufferSize) {
destinationDataBuffer[didx++] = base64URLEncMap[(inputDataBuffer[sidx] >> 2) & 077];
if (sidx < inputDataBufferSize - 1) {
destinationDataBuffer[didx++] = base64URLEncMap[((inputDataBuffer[sidx + 1] >> 4) & 017) | ((inputDataBuffer[sidx] << 4) & 077)];
destinationDataBuffer[didx++] = base64URLEncMap[(inputDataBuffer[sidx + 1] << 2) & 077];
} else
destinationDataBuffer[didx++] = base64URLEncMap[(inputDataBuffer[sidx] << 4) & 077];
}
while (didx < destinationDataBufferSize)
destinationDataBuffer[didx++] = '=';
return destinationDataBufferSize;
}
namespace Bun {
String base64URLEncodeToString(Vector<uint8_t> data)
{
auto size = data.size();
size_t encodedLength = ((size * 4) + 2) / 3;
if (!encodedLength)
return String();
LChar* ptr;
auto result = String::createUninitialized(encodedLength, ptr);
if (UNLIKELY(!ptr)) {
RELEASE_ASSERT_NOT_REACHED();
return String();
}
encodedLength = WTF__base64URLEncode(data.data(), data.size(), ptr, encodedLength);
RELEASE_ASSERT(result.length() == encodedLength);
return result;
}
// https://github.com/oven-sh/WebKit/blob/b7bc2ba65db9774d201018f2e1a0a891d6365c13/Source/JavaScriptCore/runtime/DatePrototype.cpp#L323-L345
size_t toISOString(JSC::VM& vm, double date, char in[64])
{
if (!std::isfinite(date))
return 0;
GregorianDateTime gregorianDateTime;
vm.dateCache.msToGregorianDateTime(date, WTF::TimeType::UTCTime, gregorianDateTime);
// Maximum amount of space we need in buffer: 7 (max. digits in year) + 2 * 5 (2 characters each for month, day, hour, minute, second) + 4 (. + 3 digits for milliseconds)
// 6 for formatting and one for null termination = 28. We add one extra character to allow us to force null termination.
char buffer[28];
// If the year is outside the bounds of 0 and 9999 inclusive we want to use the extended year format (ES 15.9.1.15.1).
int ms = static_cast<int>(fmod(date, msPerSecond));
if (ms < 0)
ms += msPerSecond;
int charactersWritten;
if (gregorianDateTime.year() > 9999 || gregorianDateTime.year() < 0)
charactersWritten = snprintf(buffer, sizeof(buffer), "%+07d-%02d-%02dT%02d:%02d:%02d.%03dZ", gregorianDateTime.year(), gregorianDateTime.month() + 1, gregorianDateTime.monthDay(), gregorianDateTime.hour(), gregorianDateTime.minute(), gregorianDateTime.second(), ms);
else
charactersWritten = snprintf(buffer, sizeof(buffer), "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", gregorianDateTime.year(), gregorianDateTime.month() + 1, gregorianDateTime.monthDay(), gregorianDateTime.hour(), gregorianDateTime.minute(), gregorianDateTime.second(), ms);
ASSERT(charactersWritten > 0 && static_cast<unsigned>(charactersWritten) < sizeof(buffer));
memcpy(in, buffer, charactersWritten + 1);
if (static_cast<unsigned>(charactersWritten) >= sizeof(buffer))
return 0;
return charactersWritten;
}
}