Last active
January 25, 2025 02:20
-
-
Save ialex32x/8f345e9c04e5d53dd5b56a17c803d641 to your computer and use it in GitHub Desktop.
jsc hello world from scratch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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