请教问题:Nodejs 编写的addon内 如何通过V8引擎运行JS代码

请教问题:Nodejs 编写的addon内 如何通过V8引擎运行JS代码

###run.cpp:

#define BUILDING_NODE_EXTENSION
#include <node.h>

using namespace v8;

Handle<Value> run(const Arguments& args) {
	HandleScope scope;

	if (args.Length() < 1) {
		ThrowException(Exception::TypeError(String::New("Args Error")));
		return scope.Close(Undefined());
	}
	if (!args[0]->IsString()) {
		ThrowException(Exception::TypeError(String::New("Args Error")));
		return scope.Close(Undefined());
	}	
	
	Persistent<Context> context = Context::New();
	Context::Scope context_scope(context);

	Local<Script> scritp = Script::Compile(args[0]->ToString());

	Local<Value> result = scritp->Run();

	return scope.Close(result);
}

void Init(Handle<Object> exports) {
	exports->Set(String::NewSymbol("run"),
			FunctionTemplate::New(run)->GetFunction());
}

NODE_MODULE(run, Init)

###test.js:

var addons = require('./run.node');
addons.run("console.log('hello world')");

###运行

使用命令:node test.js 报错:

ReferenceError: console is not defined
    at <anonymous>:1:1
    at Object.<anonymous> (C:\Users\fx\Documents\Visual Studio 2010\Projects\helloworld\Debug\test.js:2:8)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:902:3

也不知道是啥问题,跪求高手现身


4 回复

你遇到的问题是因为你在V8上下文中运行的JavaScript代码中尝试访问全局对象global中的一些内置对象(如console),但这些对象并没有被自动包含在你的V8上下文中。为了解决这个问题,你需要手动创建并添加这些全局对象到你的V8上下文中。

下面是修改后的run.cpp文件示例:

#define BUILDING_NODE_EXTENSION
#include <node.h>
#include <v8.h>

using namespace v8;

Handle<Value> run(const Arguments& args) {
    HandleScope scope;

    if (args.Length() < 1) {
        ThrowException(Exception::TypeError(String::New("Args Error")));
        return scope.Close(Undefined());
    }
    
    if (!args[0]->IsString()) {
        ThrowException(Exception::TypeError(String::New("Args Error")));
        return scope.Close(Undefined());
    }

    // 创建一个新的V8上下文
    Persistent<Context> context = Context::New();
    Context::Scope context_scope(context);

    // 创建一个全局对象模板
    Local<ObjectTemplate> globalTpl = ObjectTemplate::New();
    
    // 添加console对象到全局对象模板中
    globalTpl->Set(String::New("console"), FunctionTemplate::New(ConsoleLog));

    // 使用全局对象模板创建新的全局对象
    Local<Object> globalObj = globalTpl->NewInstance();
    
    // 将全局对象添加到当前V8上下文中
    Context::Scope context_scope(context);
    context->Global()->Set(String::New("global"), globalObj);

    // 编译并运行JavaScript代码
    Local<Script> script = Script::Compile(args[0]->ToString());
    Local<Value> result = script->Run();

    return scope.Close(result);
}

// 定义console.log函数
void ConsoleLog(const FunctionCallbackInfo<Value>& args) {
    for (int i = 0; i < args.Length(); i++) {
        printf("%s ", *String::Utf8Value(args[i]->ToString()));
    }
    printf("\n");
}

void Init(Handle<Object> exports) {
    exports->Set(String::NewSymbol("run"),
                 FunctionTemplate::New(run)->GetFunction());
}

NODE_MODULE(run, Init)

在上述代码中,我们定义了一个ConsoleLog函数来模拟console.log的行为,并将其添加到全局对象模板中。然后,我们将这个全局对象模板实例化为一个新的全局对象,并将其设置为当前V8上下文的全局对象。这样,当我们在V8上下文中运行JavaScript代码时,就可以正确地访问console对象了。

请注意,为了使这段代码工作,你需要将ConsoleLog函数定义为void ConsoleLog(const FunctionCallbackInfo<Value>& args)的形式,并确保它能够处理传入的参数。在这个例子中,我们只是简单地打印了传递给它的所有参数。


可能 原因是console 不是标准的js库,你试试addons.run("‘Hello’ + ‘, World!’");看看结果是不是Hello,World

嗯,确实是这样,但是我想实现的功能类似于这里的 module._compile 方法,这个方法能将文件读入后compile成function。

// Native extension for .js
Module._extensions['.js'] = function(module, filename) {
  var content = NativeModule.require('fs').readFileSync(filename, 'utf8');
  module._compile(stripBOM(content), filename);
};

你遇到的问题是因为在你编译的 Script 中没有创建全局上下文,因此 console 对象未被定义。你需要确保在编译脚本时包含全局上下文。

你可以修改你的 run.cpp 文件,使它包含一个完整的 V8 上下文。以下是修改后的代码:

#define BUILDING_NODE_EXTENSION
#include <node.h>

using namespace v8;

Handle<Value> run(const Arguments& args) {
    HandleScope scope;

    if (args.Length() < 1) {
        ThrowException(Exception::TypeError(String::New("Args Error")));
        return scope.Close(Undefined());
    }
    if (!args[0]->IsString()) {
        ThrowException(Exception::TypeError(String::New("Args Error")));
        return scope.Close(Undefined());
    }    
    
    // 创建一个新的上下文并进入该上下文
    Persistent<Context> context = Context::New();
    Context::Scope context_scope(context);

    // 创建全局对象模板
    Local<ObjectTemplate> global = ObjectTemplate::New();
    global->Set(String::New("console"), FunctionTemplate::New(consoleLog));

    // 创建新上下文并设置全局对象模板
    Persistent<Context> persistent_context = Context::New(isolate, NULL, global);
    Context::Scope context_scope(persistent_context);

    // 编译并运行脚本
    Local<String> source = args[0]->ToString();
    Local<Script> script = Script::Compile(source);
    Local<Value> result = script->Run();

    // 清理
    persistent_context.Dispose();
    persistent_context.Clear();

    return scope.Close(result);
}

// 定义 console.log 函数
Local<Value> consoleLog(const Arguments& args) {
    for (int i = 0; i < args.Length(); i++) {
        printf("%s\n", *String::AsciiValue(args[i]->ToString()));
    }
    return Undefined();
}

void Init(Handle<Object> exports) {
    exports->Set(String::NewSymbol("run"),
            FunctionTemplate::New(run)->GetFunction());
}

NODE_MODULE(run, Init)

在这个示例中,我们定义了一个新的 consoleLog 函数,并将其添加到全局对象模板中。这样可以确保 console 对象在编译的脚本中可用。

然后你可以像之前一样运行你的 test.js 脚本,应该不会再出现 ReferenceError 错误了。

回到顶部