Skip to content

Instantly share code, notes, and snippets.

@ialex32x
Last active January 25, 2025 02:20
Show Gist options
  • Save ialex32x/8f345e9c04e5d53dd5b56a17c803d641 to your computer and use it in GitHub Desktop.
Save ialex32x/8f345e9c04e5d53dd5b56a17c803d641 to your computer and use it in GitHub Desktop.
jsc hello world from scratch
#include <iterator>
#include <cstdint>
#include <thread>
#include <JavaScriptCore/JavaScriptCore.h>
JSValueRef ObjectCallAsFunctionCallback(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) {
char buf[1024];
for (int i = 0; i < argumentCount; ++i) {
JSValueRef val = arguments[i];
if (JSStringRef str = JSValueToStringCopy(ctx, val, nullptr)) {
if (size_t len = JSStringGetUTF8CString(str, buf, std::size(buf))) {
printf("%s ", buf);
}
}
}
printf("\n");
return JSValueMakeUndefined(ctx);
}
JSValueRef MakeInteropCall(JSGlobalContextRef ctx, const char *str) {
JSStringRef code = JSStringCreateWithUTF8CString(str);
JSValueRef rval = JSEvaluateScript(ctx, code, nullptr, nullptr, 0, nullptr);
JSStringRelease(code);
return rval;
}
JSClassRef instance_class = nullptr;
JSObjectRef instance_proto = nullptr;
JSObjectRef External_constructor(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) {
printf("--- constructor ---\n");
// JSClassRef clazz = (JSClassRef) JSObjectGetPrivate(constructor);
// assert(clazz == instance_class);
JSObjectRef obj = JSObjectMake(ctx, instance_class, (void*) 123);
JSObjectSetPrototype(ctx, obj, instance_proto);
assert(JSObjectGetPrivate(obj) == (void*) 123);
return obj;
}
JSValueRef External_call(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) {
printf("--- External_call --- %lu\n", (uintptr_t) JSObjectGetPrivate(thisObject));
if (exception) {
*exception = JSObjectMakeError(ctx, 0, nullptr, nullptr);
}
return nullptr;
}
bool External_C_hasInstance(JSContextRef ctx, JSObjectRef constructor, JSValueRef possibleInstance, JSValueRef* exception) {
printf("--- ExternalHasInstance (C) ---\n");
JSValueRef p = JSObjectGetPrototype(ctx, (JSObjectRef) possibleInstance);
JSStringRef propName = JSStringCreateWithUTF8CString("constructor");
bool eq = JSObjectGetProperty(ctx, (JSObjectRef) p, propName, nullptr) == constructor;
JSStringRelease(propName);
return eq;
// if (JSValueIsObjectOfClass(ctx, possibleInstance, instance_class)) {
// void* data = JSObjectGetPrivate((JSObjectRef) possibleInstance);
// return data == (void*) 123;
// }
// return false;
}
bool External_I_hasInstance(JSContextRef ctx, JSObjectRef constructor, JSValueRef possibleInstance, JSValueRef* exception) {
printf("--- ExternalHasInstance (I) ---\n");
JSValueRef p = JSObjectGetPrototype(ctx, (JSObjectRef) possibleInstance);
JSStringRef propName = JSStringCreateWithUTF8CString("constructor");
bool eq = JSObjectGetProperty(ctx, (JSObjectRef) p, propName, nullptr) == constructor;
JSStringRelease(propName);
return eq;
// if (JSValueIsObjectOfClass(ctx, possibleInstance, instance_class)) {
// void* data = JSObjectGetPrivate((JSObjectRef) possibleInstance);
// return data == (void*) 123;
// }
// return false;
}
static void External_initialize(JSContextRef context, JSObjectRef object)
{
printf("--- initialize --- %lu\n", (uintptr_t) JSObjectGetPrivate(object));
}
static size_t get_thread_id() {
std::hash<std::thread::id> hasher;
return hasher(std::this_thread::get_id());
}
static void External_finalize(JSObjectRef object)
{
printf("--- finalize --- %lu (thread:%zu)\n", (uintptr_t) JSObjectGetPrivate(object), get_thread_id());
}
JSValueRef External_check(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) {
printf("--- External_check --- %lu\n", (uintptr_t) JSObjectGetPrivate(thisObject));
return JSValueMakeUndefined(ctx);
}
JSValueRef func_callback(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) {
printf("--- func_callback ---\n");
return JSValueMakeUndefined(ctx);
}
int main(int argc, const char * argv[]) {
printf("--- main_thread --- %zu\n", get_thread_id());
JSContextGroupRef rt = JSContextGroupCreate();
JSGlobalContextRef ctx = JSGlobalContextCreateInGroup(rt, nullptr);
JSObjectRef globalObject = JSContextGetGlobalObject(ctx);
//JSContextGetGroup(<#JSContextRef ctx#>)
//JSObjectSetPrivate(JSObjectRef object, <#void *data#>)
//JSObjectGetPrivate(<#JSObjectRef object#>)
{
// JSObjectMakeConstructor(ctx, class_ref, ExternalConstructor);
//JSClassRetain(class_ref);
// JSObjectRef obj = JSObjectMake(ctx, class_ref, nullptr);
// printf("External obj? %d, %d\n", !!obj, JSValueIsObjectOfClass(ctx, obj, class_ref));
JSStringRef className = JSStringCreateWithUTF8CString("External");
static const JSStaticFunction functions[] = {
{ "nullCall", 0, kJSPropertyAttributeNone },
{ 0, 0, 0 },
};
// Type Definition
JSClassRef type_class = nullptr;
{
JSClassDefinition class_def = kJSClassDefinitionEmpty;
class_def.className = "External_C";
class_def.attributes = kJSClassAttributeNoAutomaticPrototype;
// class_def.initialize = External_initialize;
// class_def.finalize = External_finalize;
class_def.callAsFunction = &External_call;
// class_def.staticFunctions = functions;
class_def.hasInstance = &External_C_hasInstance;
class_def.callAsConstructor = &External_constructor;
type_class = JSClassCreate(&class_def);
}
// Instance Definition
{
JSClassDefinition class_def = kJSClassDefinitionEmpty;
class_def.attributes = kJSClassAttributeNoAutomaticPrototype;
class_def.initialize = &External_initialize;
class_def.finalize = &External_finalize;
// class_def.hasInstance = &External_I_hasInstance;
instance_class = JSClassCreate(&class_def);
}
JSObjectRef constructor = JSObjectMake(ctx, type_class, (void*) instance_class);
// JSObjectRef constructor = JSObjectMakeConstructor(ctx, type_class, &External_constructor); // JSObjectSetPrivate(constructor, instance_class); assert(JSObjectGetPrivate(constructor) == instance_class);
// JSObjectRef constructor = JSObjectMakeFunctionWithCallback(ctx, className, &func_callback);
assert(constructor);
JSClassRelease(type_class);
//assert(JSObjectIsConstructor(ctx, constructor));
// set constructor.name
// {
// JSStringRef name = JSStringCreateWithUTF8CString("name");
// JSValueRef error = nullptr;
// JSObjectSetProperty(ctx, constructor, name, JSValueMakeString(ctx, className), kJSPropertyAttributeNone, &error);
// assert(!error);
// JSStringRelease(name);
// }
// set constructor.prototype
{
JSObjectRef prototype = JSObjectMake(ctx, nullptr, nullptr);
// set class methods (on prototype)
{
JSStringRef func_str = JSStringCreateWithUTF8CString("func");
JSObjectRef func = JSObjectMakeFunctionWithCallback(ctx, func_str, &func_callback);
JSObjectSetProperty(ctx, prototype, func_str, func, kJSPropertyAttributeNone, nullptr);
JSStringRelease(func_str);
}
{
JSStringRef func_str = JSStringCreateWithUTF8CString("check");
JSObjectRef func = JSObjectMakeFunctionWithCallback(ctx, func_str, &External_check);
JSObjectSetProperty(ctx, prototype, func_str, func, kJSPropertyAttributeNone, nullptr);
JSStringRelease(func_str);
}
JSValueProtect(ctx, prototype);
instance_proto = prototype;
// constructor.prototype assignment
JSStringRef prototype_str = JSStringCreateWithUTF8CString("prototype");
JSObjectSetProperty(ctx, constructor, prototype_str, prototype, kJSPropertyAttributeNone, nullptr);
JSStringRelease(prototype_str);
// prototype.constructor assignment
JSStringRef constructor_str = JSStringCreateWithUTF8CString("constructor");
JSObjectSetProperty(ctx, prototype, constructor_str, constructor, kJSPropertyAttributeNone, nullptr);
JSStringRelease(constructor_str);
}
JSObjectSetProperty(ctx, globalObject, className, constructor, kJSPropertyAttributeNone, nullptr);
JSStringRelease(className);
}
JSStringRef logFunctionName = JSStringCreateWithUTF8CString("log");
JSObjectRef functionObject = JSObjectMakeFunctionWithCallback(ctx, logFunctionName, &ObjectCallAsFunctionCallback);
JSObjectSetProperty(ctx, globalObject, logFunctionName, functionObject, kJSPropertyAttributeNone, nullptr);
JSValueProtect(ctx, functionObject);
JSValueUnprotect(ctx, functionObject);
JSValueRef rval = MakeInteropCall(ctx, R"--(
(function() {
try {
log("External instanceof External:", External instanceof External);
log("External instanceof Object:", External instanceof Object);
log("External instanceof Date:", External instanceof Date);
log("Object instanceof External:", Object instanceof External);
log("Date instanceof External:", Date instanceof External);
log("typeof External:", typeof External);
const d = new Date();
let test = new External();
test.check();
test.func();
log("test instanceof External:", test instanceof External);
log("d instanceof External:", d instanceof External);
test = undefined;
} catch (error) {
log(error);
}
})
)--");
if (JSValueIsObject(ctx, rval)) {
JSValueProtect(ctx, rval);
// JSObjectGetPropertyAtIndex(<#JSContextRef ctx#>, <#JSObjectRef object#>, <#unsigned int propertyIndex#>, <#JSValueRef *exception#>)
// JSObjectRef obj = (JSObjectRef) rval;
JSObjectRef obj = JSValueToObject(ctx, rval, nullptr);
if (JSObjectIsFunction(ctx, obj)) {
JSObjectRef promise = JSObjectMakeDeferredPromise(ctx, nullptr, nullptr, nullptr);
JSValueRef arguments[] = { promise, JSValueMakeNumber(ctx, 1.0), JSValueMakeBoolean(ctx, true) };
JSObjectCallAsFunction(ctx, obj, (JSObjectRef) JSValueMakeUndefined(ctx), std::size(arguments), arguments, nullptr);
}
JSValueUnprotect(ctx, rval);
}
JSGarbageCollect(ctx);
/* memory management code to prevent memory leaks */
JSStringRelease(logFunctionName);
JSClassRelease(instance_class);
JSValueUnprotect(ctx, instance_proto);
JSGlobalContextRelease(ctx);
JSContextGroupRelease(rt);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment