Files
scriptc/packages/compiler/native/host.c
T
Chris Tate 050bbd852c Collect the frontend's cycles when the native compiler disposes it
The frontend's syntax trees and program state refer to each other, so
reference counting frees almost none of them when a build disposes the
frontend. Whether a cycle pass reclaimed them before the build ended
depended on where the collector's schedule happened to be. Passes during
the frontend found its data live and widened the growth fractions, so the
next pass waited until the heap had grown well past its size at disposal,
and the dead frontend and the emitter's garbage stayed until the build
ended.

The native compiler now runs scr_collect_cycles_phase right after it
disposes the frontend, through a host function. The runtime entry point
sweeps, then restarts the growth fractions and the death rule's backoff
from the heap the sweep leaves, as after a productive pass. Under Node
the hook is unset.
2026-10-21 09:09:19 -05:00

59 lines
2.2 KiB
C

#ifndef _WIN32
#define _POSIX_C_SOURCE 200809L
#endif
#include <stdint.h>
#include <limits.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <sys/stat.h>
#include <unistd.h>
#endif
/* Accept only directories owned by this user with private POSIX access.
* Windows inherits the user's cache ACL; reject reparse points so an
* installed cache directory cannot redirect compiler payloads elsewhere. */
uint8_t scriptc_native_private_directory(const uint8_t *bytes, size_t size, uint8_t harden) {
if (size == 0 || memchr(bytes, 0, size) != NULL) return 0;
#ifdef _WIN32
(void)harden;
if (size > INT_MAX) return 0;
int length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, (const char *)bytes, (int)size, NULL, 0);
if (length <= 0) return 0;
wchar_t *path = malloc(((size_t)length + 1) * sizeof(wchar_t));
if (!path) return 0;
if (MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, (const char *)bytes, (int)size, path, length) != length) { free(path); return 0; }
path[length] = 0;
DWORD attrs = GetFileAttributesW(path);
free(path);
return attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY) != 0 && (attrs & FILE_ATTRIBUTE_REPARSE_POINT) == 0;
#else
char *path = malloc(size + 1);
if (!path) return 0;
memcpy(path, bytes, size);
path[size] = 0;
struct stat info;
int ok = lstat(path, &info) == 0 && S_ISDIR(info.st_mode) && info.st_uid == getuid();
if (ok && harden && (info.st_mode & 077) != 0) ok = chmod(path, 0700) == 0;
if (ok) ok = lstat(path, &info) == 0 && S_ISDIR(info.st_mode) && info.st_uid == getuid() && (info.st_mode & 077) == 0;
free(path);
return ok;
#endif
}
/* The runtime's full sweep after a phase's data died (scr_rc.h). */
void scr_collect_cycles_phase(void);
/* Reclaims the cycles a finished compiler phase leaves behind. The
* frontend's syntax trees and program state refer to each other, so
* reference counting frees almost none of them when the frontend is
* disposed, and the collector's schedule alone may not run a full pass
* before the build ends. */
void scriptc_native_collect_cycles(void) {
scr_collect_cycles_phase();
}