如何在C++中使用JSExport和JavascriptCore

How to use JSExport and JavascriptCore in C++

本文关键字:JSExport JavascriptCore C++      更新时间:2023-10-16

我正在一个C++项目中使用 JavascriptCore,我不知道如何将C++类公开给 javascript。例如,在Objective-C中是这样的:

@protocol MyPointExports <JSExport>
@property double x;
@property double y;
- (NSString *)description;
- (instancetype)initWithX:(double)x y:(double)y;
+ (MyPoint *)makePointWithX:(double)x y:(double)y;
@end
@interface MyPoint : NSObject <MyPointExports>
- (void)myPrivateMethod;  // Not in the MyPointExports protocol, so not visible to JavaScript code.
@end
@implementation MyPoint
// ...
@end
JSContext *context = [[JSContext alloc] init];
// export MyPoint class
context[@"MyPoint"] = [MyPoint class];

但是我不知道如何将JSExport和协议转换为C++。

看起来你需要使用 C API 来实现这一点。有关详细信息,请参阅此文章。特别是"将本机对象定义为JavaScript"部分。

引用文章中的列表看起来与您想要实现的目标非常相似:

#include <iostream>
#include <JavaScriptCore/JavaScriptCore.h>
#include <sys/stat.h>
using namespace std;
struct FilesystemPrivate {
    string path;
    bool is_directory;
    bool is_file;
    bool is_symlink;
    size_t size;
    bool exists;
};
std::string JSStringToStdString(JSStringRef jsString) {
    size_t maxBufferSize = JSStringGetMaximumUTF8CStringSize(jsString);
    char* utf8Buffer = new char[maxBufferSize];
    size_t bytesWritten = JSStringGetUTF8CString(jsString, utf8Buffer, maxBufferSize);
    std::string utf_string = std::string (utf8Buffer, bytesWritten -1); // the last byte is a null  which std::string doesn't need.
    delete [] utf8Buffer;
    return utf_string;
}
JSValueRef ObjectCallAsFunctionCallback(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) {
    for (size_t i=0; i<argumentCount; i++) {
        JSStringRef pathString = JSValueToStringCopy(ctx, arguments[i], nullptr);
        cout << JSStringToStdString(pathString);
    }
    cout << endl ;
    return JSValueMakeUndefined(ctx);
}
void setAttributes(FilesystemPrivate *fs, std::string path) {
    fs->path = path;
    struct stat statbuf;
    if (lstat(path.c_str(), &statbuf) != -1) {
        switch (statbuf.st_mode & S_IFMT){
            case S_IFREG:
                fs->is_file = true;
                break;
            case S_IFLNK:
                fs->is_symlink = true;
                break;
            case S_IFDIR:
                fs->is_directory = true;
                break;
        }
        fs->size = statbuf.st_size;
        fs->exists = true;
    }else{
        fs->exists = false;
        fs->is_file = false;
        fs->is_directory = false;
        fs->is_symlink = false;
        fs->size = 0;
    }
}
/* callbacks */
void Filesystem_Finalize(JSObjectRef object){
    FilesystemPrivate *fs = static_cast<FilesystemPrivate*>(JSObjectGetPrivate(object));
    delete fs;
}
JSObjectRef Filesystem_CallAsConstructor(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception){
    FilesystemPrivate *fs = new FilesystemPrivate();
    JSStringRef pathString = JSValueToStringCopy(ctx, arguments[0], nullptr);
    setAttributes(fs, JSStringToStdString(pathString));
    JSObjectSetPrivate(constructor, static_cast<void*>(fs));
    return constructor;
}
/* static values */
JSValueRef Filesystem_getPath(JSContextRef ctx, JSObjectRef object,JSStringRef propertyName, JSValueRef* exception) {
    FilesystemPrivate *fs = static_cast<FilesystemPrivate*>(JSObjectGetPrivate(object));
    JSStringRef pathString = JSStringCreateWithUTF8CString(fs->path.c_str());
    return JSValueMakeString(ctx, pathString);
}
bool Filesystem_setPath(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception) {
    FilesystemPrivate *fs = static_cast<FilesystemPrivate*>(JSObjectGetPrivate(object));
    JSStringRef pathString = JSValueToStringCopy(ctx, value, nullptr);
    setAttributes(fs, JSStringToStdString(pathString));
    return true;
}
JSValueRef Filesystem_getType(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) {
    FilesystemPrivate *fs = static_cast<FilesystemPrivate*>(JSObjectGetPrivate(object));
    JSStringRef pathType;
    if (fs->is_file) {
        pathType = JSStringCreateWithUTF8CString("File");
    }else if (fs->is_directory) {
        pathType = JSStringCreateWithUTF8CString("Directory");
    }else if (fs->is_symlink) {
        pathType = JSStringCreateWithUTF8CString("Symlink");
    }else{
        pathType = JSStringCreateWithUTF8CString("Unknown");
    }
    return JSValueMakeString(ctx, pathType);
}
JSValueRef Filesystem_getExist(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) {
    FilesystemPrivate *fs = static_cast<FilesystemPrivate*>(JSObjectGetPrivate(object));
    return JSValueMakeBoolean(ctx, fs->exists);
}
JSValueRef Filesystem_getSize(JSContextRef ctx, JSObjectRef object,JSStringRef propertyName, JSValueRef* exception) {
    FilesystemPrivate *fs = static_cast<FilesystemPrivate*>(JSObjectGetPrivate(object));
    return JSValueMakeNumber(ctx, static_cast<double>(fs->size));
}
JSValueRef Filesystem_remove(JSContextRef ctx, JSObjectRef function, JSObjectRef object, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception){
    FilesystemPrivate *fs = static_cast<FilesystemPrivate*>(JSObjectGetPrivate(object));
    remove(fs->path.c_str());
    return JSValueMakeUndefined(ctx);
}
JSClassRef FilesystemClass() {
    static JSClassRef filesystem_class;
    if (!filesystem_class) {
        JSClassDefinition classDefinition = kJSClassDefinitionEmpty;
        static JSStaticFunction staticFunctions[] = {
            { "remove", Filesystem_remove, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
            { 0, 0, 0 }
        };
        static JSStaticValue staticValues[] = {
            { "path", Filesystem_getPath, Filesystem_setPath, kJSPropertyAttributeDontDelete },
            { "type", Filesystem_getType, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
            { "exists", Filesystem_getExist, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
            { "size", Filesystem_getSize, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
            { 0, 0, 0, 0 }
        };
        classDefinition.className = "Filesystem";
        classDefinition.attributes = kJSClassAttributeNone;
        classDefinition.staticFunctions = staticFunctions;
        classDefinition.staticValues = staticValues;
        classDefinition.finalize = Filesystem_Finalize;
        classDefinition.callAsConstructor = Filesystem_CallAsConstructor;
        filesystem_class = JSClassCreate(&classDefinition);
    }
    return filesystem_class;
}
int main(int argc, const char * argv[]) {
    JSContextGroupRef contextGroup = JSContextGroupCreate();
    JSGlobalContextRef globalContext = JSGlobalContextCreateInGroup(contextGroup, nullptr);
    JSObjectRef globalObject = JSContextGetGlobalObject(globalContext);
    JSObjectRef functionObject = JSObjectMakeFunctionWithCallback(globalContext, JSStringCreateWithUTF8CString("log"), ObjectCallAsFunctionCallback);
    JSObjectSetProperty(globalContext, globalObject, JSStringCreateWithUTF8CString("log"), functionObject, kJSPropertyAttributeNone, nullptr);
    JSObjectRef filesystemObject = JSObjectMake(globalContext, FilesystemClass(), nullptr);
    JSObjectSetProperty(globalContext, globalObject, JSStringCreateWithUTF8CString("Filesystem"), filesystemObject, kJSPropertyAttributeNone, nullptr);
    JSEvaluateScript(globalContext, JSStringCreateWithUTF8CString("var fs = new Filesystem('/Users/{user}/Desktop/file');log(fs.exists);"), nullptr, nullptr, 1, nullptr);
    return 0;
}