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

时间:2017-10-25 23:05:44

标签: c++ javascriptcore

我在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 ++。

1 个答案:

答案 0 :(得分:1)

看起来您需要使用C API来实现这一目标。有关详细信息,请参阅this article。特别是,&#34;将本地对象定义为JavaScript&#34;部分。

引用文章的列表与您想要实现的内容非常相似:

#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 \0 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;
}