Skip to content

Instantly share code, notes, and snippets.

@acquitelol
Last active November 25, 2025 23:03
Show Gist options
  • Select an option

  • Save acquitelol/fd8ce04e6a5896d72b965ba84dd13668 to your computer and use it in GitHub Desktop.

Select an option

Save acquitelol/fd8ce04e6a5896d72b965ba84dd13668 to your computer and use it in GitHub Desktop.
A Node V8 module in Elle without an `extern "C"` wrapper. Note: only works on aarch64.
// This is Elle https://github.com/acquitelol/elle NOT Rust
// I just need the syntax highlighting
use std/prelude;
namespace V8Local;
external fn _ZN2v86Object3SetENS_5LocalINS_7ContextEEENS1_INS_5ValueEEES5_(
void **self,
void *context,
void *key,
void *value
) @alias(V8Local::Set) -> Option<bool>;
namespace V8Isolate;
external fn _ZN2v87Isolate10GetCurrentEv() @alias(V8Isolate::GetCurrent) -> V8Isolate *;
external fn _ZN2v87Isolate17GetCurrentContextEv(V8Isolate *self) @alias(V8Isolate::GetCurrentContext) -> void *;
struct V8FunctionTemplate @nofmt { u8 _ }
external fn _ZN2v816FunctionTemplate3NewEPNS_7IsolateEPFvRKNS_20FunctionCallbackInfoINS_5ValueEEEENS_5LocalIS4_EENSA_INS_9SignatureEEEiNS_19ConstructorBehaviorENS_14SideEffectTypeEPKNS_9CFunctionEttt(
V8Isolate *isolate,
fn(void *) callback,
void *data,
void *signature,
i32 length,
i32 behavior,
i32 side_effect_type,
fn() c_function,
u16 instance_type,
u16 allowed_receiver_instance_type_range_start,
u16 allowed_receiver_instance_type_range_end
) @alias(V8FunctionTemplate::new) -> void *;
external fn _ZN2v816FunctionTemplate11GetFunctionENS_5LocalINS_7ContextEEE(
V8FunctionTemplate *self,
void *context
) @alias(V8FunctionTemplate::GetFunction) -> void *;
struct V8HandleScope @nofmt {
@unused void *i_isolate_,
@unused void *prev_next_,
@unused void *prev_limit_
}
external fn _ZN2v811HandleScopeC1EPNS_7IsolateE(
V8HandleScope *self,
V8Isolate *isolate
) @alias(V8HandleScope::init) -> V8HandleScope;
external fn _ZN2v811HandleScopeD1Ev(V8HandleScope *self) @alias(V8HandleScope::destroy);
namespace V8String;
external fn _ZN2v86String11NewFromUtf8EPNS_7IsolateEPKcNS_13NewStringTypeEi(
V8Isolate *isolate,
string value,
i32 type,
i32 length
) @alias(V8String::NewFromUtf8) -> void **;
namespace V8Function;
external fn _ZN2v88Function7SetNameENS_5LocalINS_6StringEEE(V8Function *self, void *name) @alias(V8Function::SetName);
struct V8FunctionCallbackInfo {
void **implicit_args_,
void **values_,
u64 length_ // padded to maintain size
}
fn V8FunctionCallbackInfo::GetIsolate(V8FunctionCallbackInfo *self) -> V8Isolate * {
return self.implicit_args_[1];
}
struct NodeModule @nofmt {
i32 version,
u32 flags,
void *dso_handle,
string filename,
fn(void *, void *, void *) register_func,
fn() context_register_func,
string modname,
void *priv,
NodeModule *link
}
const NODE_MODULE_VERSION = 137;
const get_current_file = fn(ElleMeta meta) meta.file;
external fn node_module_register(NodeModule *module);
fn hello(V8FunctionCallbackInfo *args) {
isolate := args.GetIsolate();
context := isolate.GetCurrentContext();
arg := #cast(i32, args.values_[0] >> 32);
args.implicit_args_[3] = *V8String::NewFromUtf8(
isolate,
"Hello from Elle! You passed {}".format(arg),
0, -1
);
}
fn load_env() {
ElleEnv *env = mem::malloc(#size(ElleEnv));
env.allocator = ArbitraryAllocator::new();
env.default_allocator = GCAllocator::new();
env.stack_top = &0;
#env = env;
#reset_allocator();
}
fn set_method(void *recv, string name, fn(void *) callback) {
isolate := V8Isolate::GetCurrent();
scope := V8HandleScope {};
defer scope.destroy();
scope.init(isolate);
context := isolate.GetCurrentContext();
template := V8FunctionTemplate::new(isolate, callback, nil, nil, 0, 1, 0, nil, 0, 0, 0);
func := V8FunctionTemplate::GetFunction(template, context);
func_name := V8String::NewFromUtf8(isolate, name, 1, -1);
V8Function::SetName(func, func_name);
res := V8Local::Set(recv, context, func_name, func);
}
fn register_test() {
load_env();
node_module_register(Box::new(NodeModule {
version = NODE_MODULE_VERSION,
flags = 0,
dso_handle = nil,
filename = get_current_file(),
register_func = fn(recv) set_method(recv, "test", hello),
context_register_func = nil,
modname = "test",
priv = nil,
link = nil,
}).to_ptr());
}
main.node: main.o
cc -shared -fPIC main.o -L$(HOME)/.local/lib -lelle -Wl,-undefined,dynamic_lookup -o main.node
main.o: main.s
echo ".section __DATA,__mod_init_func,mod_init_funcs" >> main.s
echo ".p2align 3" >> main.s
echo ".quad _register_test" >> main.s
cc main.s -c -o main.o
main.s:
ellec main.le --asm -c
test.node:
clang++ -std=c++20 -O3 -fPIC -shared test.cpp \
-I$(HOME)/.nvm/versions/node/v19.0.0/include/node \
-o test.node \
-Wl,-undefined,dynamic_lookup
test.ll:
clang++ -std=c++20 -S -emit-llvm -O3 test.cpp \
-I$(HOME)/.nvm/versions/node/v19.0.0/include/node \
-o test.ll
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment