Files
bun.sh/test/js/bun/plugin/native_plugin.c
Zack Radisic 6a4dd91e95 More tests
2024-11-25 22:56:20 -08:00

301 lines
8.7 KiB
C

/*
Dummy plugin which counts the occurences of the word "foo" in the source code,
replacing it with "boo".
It stores the number of occurences in the External struct.
*/
#include <stdlib.h>
#include <stdio.h>
#include <node_api.h>
#include <string.h>
#include <stdatomic.h>
typedef struct {
atomic_size_t foo_count;
// For testing logging error logic
atomic_bool throws_an_error;
} External;
typedef struct {
size_t __struct_size;
void* bun;
const uint8_t* path_ptr;
size_t path_len;
const uint8_t* namespace_ptr;
size_t namespace_len;
uint8_t default_loader;
void *external;
} OnBeforeParseArguments;
typedef struct BunLogOptions BunLogOptions;
typedef struct OnBeforeParseResult {
size_t __struct_size;
uint8_t* source_ptr;
size_t source_len;
uint8_t loader;
int (*fetchSourceCode)(
const OnBeforeParseArguments* args,
struct OnBeforeParseResult* result
);
void* plugin_source_code_context;
void (*free_plugin_source_code_context)(void* ctx);
void (*log)(const OnBeforeParseArguments* args, BunLogOptions* options);
} OnBeforeParseResult;
typedef struct BunLogOptions {
size_t __struct_size;
const uint8_t* message_ptr;
size_t message_len;
const uint8_t* path_ptr;
size_t path_len;
const uint8_t* source_line_text_ptr;
size_t source_line_text_len;
int8_t level;
int line;
int lineEnd;
int column;
int columnEnd;
} BunLogOptions;
typedef enum {
BUN_LOG_LEVEL_VERBOSE = 0,
BUN_LOG_LEVEL_DEBUG = 1,
BUN_LOG_LEVEL_INFO = 2,
BUN_LOG_LEVEL_WARN = 3,
BUN_LOG_LEVEL_ERROR = 4,
} BunLogLevel;
void log_error(const OnBeforeParseArguments* args, const OnBeforeParseResult* result, BunLogLevel level, const char* message, size_t message_len) {
BunLogOptions options = (BunLogOptions) {
.message_ptr = (uint8_t*)message,
.message_len = message_len,
.path_ptr = args->path_ptr,
.path_len = args->path_len,
.source_line_text_ptr = NULL,
.source_line_text_len = 0,
.level = level,
.line = 0,
.lineEnd = 0,
.column = 0,
.columnEnd = 0,
};
(result->log)(args, &options);
}
void plugin_impl(const OnBeforeParseArguments* args, OnBeforeParseResult* result) {
// if (args->__struct_size < sizeof(OnBeforeParseArguments)) {
// log_error(args, result, BUN_LOG_LEVEL_ERROR, "Invalid OnBeforeParseArguments struct size", sizeof("Invalid OnBeforeParseArguments struct size") - 1);
// return;
// }
if (args->external) {
External* external = (External*)args->external;
if (atomic_load(&external->throws_an_error)) {
log_error(args, result, BUN_LOG_LEVEL_ERROR, "Throwing an error", sizeof("Throwing an error") - 1);
return;
}
}
int fetch_result = result->fetchSourceCode(args, result);
if (fetch_result != 0) {
printf("FUCK\n");
exit(1);
}
int foo_count = 0;
const char *end = (const char *)result->source_ptr + result->source_len;
char *cursor = strnstr((const char *)result->source_ptr, "foo", result->source_len);
while (cursor != NULL) {
foo_count++;
cursor += 3;
if (cursor + 3 < end) {
cursor = strnstr((const char*) cursor, "foo", (size_t) (end - cursor));
} else break;
}
if (foo_count > 0) {
char *new_source = (char *)malloc(result->source_len);
if (new_source == NULL) {
printf("FUCK\n");
exit(1);
}
memcpy(new_source, result->source_ptr, result->source_len);
cursor = strnstr(new_source, "foo", result->source_len);
while (cursor != NULL) {
*cursor = 'b';
cursor += 3;
if (cursor + 3 < end) {
cursor = strnstr((const char*) cursor, "foo", (size_t) (end - cursor));
} else break;
}
if (args->external) {
External *external = (External*)args->external;
atomic_fetch_add(&external->foo_count, foo_count);
}
result->source_ptr = (uint8_t*)new_source;
result->source_len = result->source_len;
} else {
result->source_ptr = NULL;
result->source_len = 0;
result->loader = 0;
}
}
void finalizer(napi_env env, void* data, void* hint) {
External* external = (External*)data;
if (external != NULL) {
free(external);
}
}
napi_value create_external(napi_env env, napi_callback_info info) {
napi_status status;
// Allocate the External struct
External* external = malloc(sizeof(External));
if (external == NULL) {
napi_throw_error(env, NULL, "Failed to allocate memory");
return NULL;
}
external->foo_count = 0;
// Create the external wrapper
napi_value result;
status = napi_create_external(env, external, finalizer, NULL, &result);
if (status != napi_ok) {
free(external);
napi_throw_error(env, NULL, "Failed to create external");
return NULL;
}
return result;
}
napi_value set_throws_errors(napi_env env, napi_callback_info info) {
napi_status status;
External* external;
size_t argc = 1;
napi_value args[1];
status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
if (status != napi_ok) {
napi_throw_error(env, NULL, "Failed to parse arguments");
return NULL;
}
if (argc < 1) {
napi_throw_error(env, NULL, "Wrong number of arguments");
return NULL;
}
status = napi_get_value_external(env, args[0], (void**)&external);
if (status != napi_ok) {
napi_throw_error(env, NULL, "Failed to get external");
return NULL;
}
bool throws;
status = napi_get_value_bool(env, args[0], &throws);
if (status != napi_ok) {
napi_throw_error(env, NULL, "Failed to get boolean value");
return NULL;
}
atomic_store(&external->throws_an_error, throws);
return NULL;
}
napi_value get_foo_count(napi_env env, napi_callback_info info) {
napi_status status;
External* external;
size_t argc = 1;
napi_value args[1];
status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
if (status != napi_ok) {
napi_throw_error(env, NULL, "Failed to parse arguments");
return NULL;
}
if (argc < 1) {
napi_throw_error(env, NULL, "Wrong number of arguments");
return NULL;
}
status = napi_get_value_external(env, args[0], (void**)&external);
if (status != napi_ok) {
napi_throw_error(env, NULL, "Failed to get external");
return NULL;
}
size_t foo_count = atomic_load(&external->foo_count);
if (foo_count > INT32_MAX) {
napi_throw_error(env, NULL, "Too many foos! This probably means undefined memory or heap corruption.");
return NULL;
}
napi_value result;
status = napi_create_int32(env, foo_count, &result);
if (status != napi_ok) {
napi_throw_error(env, NULL, "Failed to create array");
return NULL;
}
return result;
}
napi_value Init(napi_env env, napi_value exports) {
napi_status status;
napi_value fn_get_names;
napi_value fn_create_external;
napi_value fn_set_throws_errors;
// Register get_names function
status = napi_create_function(env, NULL, 0, get_foo_count, NULL, &fn_get_names);
if (status != napi_ok) {
napi_throw_error(env, NULL, "Failed to create get_names function");
return NULL;
}
status = napi_set_named_property(env, exports, "getFooCount", fn_get_names);
if (status != napi_ok) {
napi_throw_error(env, NULL, "Failed to add get_names function to exports");
return NULL;
}
// Register set_throws_errors function
status = napi_create_function(env, NULL, 0, set_throws_errors, NULL, &fn_set_throws_errors);
if (status != napi_ok) {
napi_throw_error(env, NULL, "Failed to create set_throws_errors function");
return NULL;
}
status = napi_set_named_property(env, exports, "setThrowsErrors", fn_set_throws_errors);
if (status != napi_ok) {
napi_throw_error(env, NULL, "Failed to add set_throws_errors function to exports");
return NULL;
}
// Register create_external function
status = napi_create_function(env, NULL, 0, create_external, NULL, &fn_create_external);
if (status != napi_ok) {
napi_throw_error(env, NULL, "Failed to create create_external function");
return NULL;
}
status = napi_set_named_property(env, exports, "createExternal", fn_create_external);
if (status != napi_ok) {
napi_throw_error(env, NULL, "Failed to add create_external function to exports");
return NULL;
}
return exports;
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)