C++使用V8

原文地址:http://www.codeproject.com/KB/library/Using_V8_Javascript_VM.aspx

介绍

谁不想知道虚拟机是怎样工作的?不过,比起自己写一个虚拟机,更好的办法是使用大公司的产品。在这篇文章中,我将介绍如何在你的程序中使用V8——谷歌浏览器(Chrome)所使用的开源JavaScript引擎。

背景

这里的代码使用V8作为嵌入库来执行JavaScript代码。要取得库源码和其它信息,可以浏览V8开发者页面。想有效地应用V8,你需要了解C/C++JavaScript

使用

我们来看看演示中有哪些东西:

·                     如何使用V8API来执行JavaScript脚本。

·                     如何存取脚本中的整数和字符串。

·                     如何建立可被脚本调用的自定义函数。

·                     如何建立可被脚本调用的自定义类。

首先,我们一起了解一下怎样初始化V8。这是嵌入V8引擎的简单例子:

1.                #include <v8.h>

2.                using namespace v8;

3.                int main(int argc, char* argv[]) {

4.                  // Create a stack-allocated handle scope.

5.                  HandleScope handle_scope;

6.                  // Create a new context.

7.                  Handle<Context> context = Context::New();

8.                  // Enter the created context for compiling and

9.                  // running the hello world script.

10.              Context::Scope context_scope(context);

11.              // Create a string containing the JavaScript source code.

12.              Handle<String> source = String::New("'Hello' + ', World!'");

13.              // Compile the source code.

14.              Handle<Script> script = Script::Compile(source);

15.              // Run the script to get the result.

16.              Handle<Value> result = script->Run();

17.              // Convert the result to an ASCII string and print it.

18.              String::AsciiValue ascii(result);

19.              printf("%s ", *ascii);

20.              return 0;

21.            }

好了,不过这还不能说明怎样让我们控制脚本中的变量和函数。

全局模型(The Global Template

首先,我们需要一个全局模型来掌控我们所做的修改:

1.                v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();

这里建立了一个新的全局模型来管理我们的上下文(context)和定制。在V8里,每个上下文是分开的,它们有自己的全局模型。一个上下文就是一个独立的执行环境,相互之间没有关联,JavaScript运行于其中一个实例之中。

自定义函数

接下来,我们加入一个名为"plus"的自定义函数:

1.                // plus function implementation - Add two numbers

2.                v8::Handle<v8::Value> Plus(const v8::Arguments& args)

3.                {

4.                    unsigned int A = args[0]->Uint32Value();

5.                    unsigned int B = args[1]->Uint32Value();

6.                    return v8_uint32(A +  B);

7.                }

8.                //...

9.                //associates plus on script to the Plus function

10.            global->Set(v8::String::New("plus"), v8::FunctionTemplate::New(Plus));

自定义函数必须以const v8::Arguments&作为参数并返回v8::Handle<v8::Value>。我们把这个函数加入到模型中,关联名称"plus"到回调Plus。现在,在脚本中每次调用"plus",我们的Plus函数就会被调用。这个函数只是返回两个参数的和。

现在我们可以在JavaScript里使用这个自定义函数了:

plus(120,44); 

在脚本里也可以得到函数的返回值:

x = plus(1,2);

if( x == 3){

   // do something important here!

}

访问器(Accessor)——存取脚本中的变量

现在,我们可以建立函数了...不过如果我们可以在脚本外定义一些东西岂不是更酷?Let's do it! V8里有个东东称为存取器(Accessor),使用它,我们可以关联一个名称到一对Get/Set函数上,V8会用它来存取脚本中的变量。

1.                global->SetAccessor(v8::String::New("x"), XGetter, XSetter);

这行代码关联名称"x"XGetterXSetter函数。这样在脚本中每次读取到"x"变量时都会调用XGetter,每次更新"x"变量时会调用XSetter。下面是这两个函数的代码:

1.                //the x variable!

2.                int x;

3.                //get the value of x variable inside javascript

4.                static v8::Handle<v8::Value> XGetter( v8::Local<v8::String> name,

5.                                  const v8::AccessorInfo& info) {

6.                  return  v8::Number::New(x);

7.                }

8.                //set the value of x variable inside javascript

9.                static void XSetter( v8::Local<v8::String> name,

10.                   v8::Local<v8::Value> value, const v8::AccessorInfo& info) {

11.              x = value->Int32Value();

12.            }

XGetter里我们把"x"转换成V8喜欢的数值类型。XSetter里,我们把传入的参数转换成整数,Int32Value是基本类型转换函数的一员,还有NumberValue对应doubleBooleanValue对应bool,等。

现在,我们可以为字符串做相同的操作:

1.                //the username accessible on c++ and inside the script

2.                char username[1024];

3.                //get the value of username variable inside javascript

4.                v8::Handle<v8::Value> userGetter(v8::Local<v8::String> name,

5.                           const v8::AccessorInfo& info) {

6.                    return v8::String::New((char*)&username,strlen((char*)&username));

7.                }

8.                //set the value of username variable inside javascript

9.                void userSetter(v8::Local<v8::String> name, v8::Local<v8::Value> value,

10.                const v8::AccessorInfo& info) {

11.                v8::Local<v8::String> s = value->ToString();

12.                s->WriteAscii((char*)&username);

13.            }

对于字符串,有一点点不同,"userGetter"XGetter做的一样,不过userSetter要先用ToString方法取得内部字符串,然后用WriteAscii函数把内容写到我们指定的内存中。现在,加入存取器:

1.                //create accessor for string username

2.                global->SetAccessor(v8::String::New("user"),userGetter,userSetter);

打印输出

"print"函数是另一个自定义函数,它通过"printf"输出所有的参数内容。和之前的"plus"函数一样,我们要在全局模型中注册这个函数:

1.                //associates print on script to the Print function

2.                global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print));

实现"print"函数

1.                // The callback that is invoked by v8 whenever the JavaScript 'print'

2.                // function is called. Prints its arguments on stdout separated by

3.                // spaces and ending with a newline.

4.                v8::Handle<v8::Value> Print(const v8::Arguments& args) {

5.                    bool first = true;

6.                    for (int i = 0; i < args.Length(); i++)

7.                    {

8.                        v8::HandleScope handle_scope;

9.                        if (first)

10.                    {

11.                        first = false;

12.                    }

13.                    else

14.                    {

15.                        printf(" ");

16.                    }

17.                    //convert the args[i] type to normal char* string

18.                    v8::String::AsciiValue str(args[i]);

19.                    printf("%s", *str);

20.                }

21.                printf(" ");

22.                //returning Undefined is the same as returning void...

23.                return v8::Undefined();

24.            }

这里,为每个参数都构建了v8::String::AsciiValue对象:数据的char*表示。通过它,我们就可以把所有类型都转换成字符串并打印出来。

JavaScript演示

在演示程序里,我们有一个简单的JavaScript脚本,调用了迄今为止我们建立的所有东西:

print("begin script");

print(script executed by  + user);

if ( user == "John Doe"){

    print("/tuser name is invalid. Changing name to Chuck Norris");

    user = "Chuck Norris";

}

print("123 plus 27 = " + plus(123,27));

x = plus(3456789,6543211);

print("end script");

 

存取C++对象

为我们的类准备环境

如果用C++把一个类映射到JavaScript中去?放一个演示用的类上来先:

1.                //Sample class mapped to v8

2.                class Point

3.                {

4.                public:

5.                    //constructor

6.                    Point(int x, int y):x_(x),y_(y){}

7.                 

8.                    //internal class functions

9.                    //just increment x_

10.                void Function_A(){++x_;    }

11.             

12.                //increment x_ by the amount

13.                void Function_B(int vlr){x_+=vlr;}

14.             

15.                //variables

16.                int x_;

17.            };

为了把这个类完全嵌入脚本中,我们需要映射类成员函数和类成员变量。第一步是在我们的上下文中映射一个类模型(class template):

1.                Handle<FunctionTemplate> point_templ = FunctionTemplate::New();

2.                point_templ->SetClassName(String::New("Point"));

我们建立了一个"函数"模型[FunctionTemplate],但这里应该把它看成类。

然后,我们通过原型模型(Prototype Template)加入内建的类方法:

1.                Handle<ObjectTemplate> point_proto = point_templ->PrototypeTemplate();

2.                point_proto->Set("method_a", FunctionTemplate::New(PointMethod_A));

3.                point_proto->Set("method_b", FunctionTemplate::New(PointMethod_B));

接下来,类有了两个方法和对应的回调。但它们目前只在原型中,没有类实例访问器我们还不能使用它们。

1.                Handle<ObjectTemplate> point_inst = point_templ->InstanceTemplate();

2.                point_inst->SetInternalFieldCount(1);

SetInternalFieldCount函数为C++类建立一个空间(后面会用到)

现在,我们有了类实例,加入访问器以访问内部变量:

1.                point_inst->SetAccessor(String::New("x"), GetPointX, SetPointX);

接着,土壤准备好了,开始播种:

1.                Point* p = new Point(0, 0);

新对象建立好了,目前只能在C++中使用,要放到脚本里,我们还要下面的代码:

1.                Handle<Function> point_ctor = point_templ->GetFunction();

2.                Local<Object> obj = point_ctor->NewInstance();

3.                obj->SetInternalField(0, External::New(p));

好了,GetFunction返回一个point构造器(JavaScript方面), 通过它,我们可以用NewInstance生成一个新的实例。然后,用Point对象指针设置我们的内部域(我们前面用SetInternalFieldCount建立的空间)JavaScript可以通过这个指针存取对象。

还少了一步,我们只有类模型和实例,但还缺一个名字来存取它:

1.                context->Global()->Set(String::New("point"), obj);

JavaScript里访问类方法

最后,我们还要解释一下怎样在Point类中访问Function_A...

让我们看看PointMethod_A回调:

1.                Handle<Value> PointMethod_A(const Arguments& args)

2.                {

3.                    Local<Object> self = args.Holder();

4.                    Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));

5.                    void* ptr = wrap->Value();

6.                    static_cast<Point*>(ptr)->Function_A();

7.                    return Integer::New(static_cast<Point*>(ptr)->x_);

8.                }

和普通访问器一样,我们必须处理参数。要访问我们的类,必须从内部域(第一个)中取得类指针。把内部域映射到"wrap"之后,我们使用它的"value"方法取得类指针。

其它

希望这篇文章对你有所帮助,如果发现文章有误,请不吝赐教

 

 

<think>我们正在讨论的是J2V8库。根据引用[1]和引用[4],J2V8是V8引擎的Java绑定,因此它主要是为Java平台设计的。用户现在询问C++是否可以使用J2V8来执行JavaScript代码。 关键点: 1. J2V8是一个Java库,它提供了Java API来调用V8引擎的功能。因此,它只能在Java环境使用(如Android或桌面Java应用)。 2. 如果用户想在C++中执行JavaScript,应该直接使用V8引擎,因为V8本身就是用C++编写的,并且提供了C++ API。 因此,回答应该是: - C++不能使用J2V8,因为J2V8是为Java设计的。 - 但是,C++可以直接使用V8引擎(无需通过J2V8)来执行JavaScript代码。 我们可以建议用户直接使用V8C++ API,并给出一个简单的示例。 同时,参考引用[1]提到:“以上代码为一个使用 V8 引擎来运行脚本的基本模板,可以看到,开发人员可以很容易的在自己的代码中嵌入 V8 来处理 JavaScript 脚本。我们在下面小节中详细讨论如何在脚本中访问 C++ 资源。” 这表明在C++中直接使用V8是标准做法。 因此,回答结构如下: 1. 明确说明J2V8是用于Java的,不能在C++使用。 2. 建议在C++中直接使用V8引擎,并给出简要步骤或示例。 3. 提供一些资源或进一步学习的建议。 注意:由于用户要求参考站内引用,我们可以引用[1]和[4]来说明J2V8的用途,并引用[1]中提到的C++嵌入V8的情况。 另外,引用[2]和[3]是关于V8引擎内部机制(JIT和字节码)的,与当前问题没有直接关系,但可以简要提及V8的高性能特性。 最后,生成相关问题。</think>### C++ 是否支持使用 J2V8 执行 JavaScript 代码? **J2V8 是专为 Java 平台设计的库,不支持在 C++ 中直接使用**。以下是详细说明: #### 1. J2V8 的定位和限制 - J2V8 是 V8 引擎的 **Java 绑定**(Java Binding),提供 Java API 来调用 V8 功能[^1][^4] - 核心设计目标:在 JVM 环境(如 Android/Java 应用)中嵌入 JavaScript 执行能力 - **不支持 C++ 调用**:J2V8 的 API 基于 Java 实现,无法直接在 C++ 项目中导入或链接 #### 2. C++ 执行 JavaScript 的替代方案 若需在 C++ 中执行 JavaScript,推荐直接使用 **V8 引擎的 C++ API**: ```cpp #include <v8.h> int main() { // 初始化 V8 平台 v8::V8::InitializePlatform(); v8::V8::Initialize(); // 创建隔离环境 v8::Isolate* isolate = v8::Isolate::New(); { v8::Isolate::Scope isolate_scope(isolate); v8::HandleScope handle_scope(isolate); // 创建上下文 v8::Local<v8::Context> context = v8::Context::New(isolate); v8::Context::Scope context_scope(context); // 执行 JavaScript 代码 v8::Local<v8::String> source = v8::String::NewFromUtf8( isolate, "'Hello from C++! ' + 42").ToLocalChecked(); v8::Local<v8::Script> script = v8::Script::Compile(context, source).ToLocalChecked(); v8::Local<v8::Value> result = script->Run(context).ToLocalChecked(); // 输出结果 v8::String::Utf8Value utf8(isolate, result); printf("%s\n", *utf8); } // 清理资源 isolate->Dispose(); v8::V8::Dispose(); v8::V8::ShutdownPlatform(); return 0; } ``` **优势**: - **原生性能**:直接调用 V8 C++ API,避免 JNI 开销 - **完整控制**:访问 V8 所有底层功能(如内存管理、调优参数) - **即时编译(JIT)**:通过 V8 的 Turbofan 编译器实现高性能执行[^2] #### 3. 混合方案(C++ 与 Java 交互) 若必须在 C++ 中通过 J2V8 执行 JavaScript,需通过 **JNI(Java Native Interface)** 桥接: ```mermaid graph LR C++[C++ 代码] --> JNI[JNI 接口] JNI --> Java[Java 层调用 J2V8] Java --> JS[JavaScript 执行] JS --> Result[结果返回 C++] ``` **实现步骤**: 1. C++ 通过 JNI 调用 Java 方法 2. Java 层使用 J2V8 执行 JavaScript 3. 结果通过 JNI 传回 C++ ```java // Java 层 (J2V8 执行) public class J2V8Executor { public static String executeJS(String code) { V8 v8 = V8.createV8Runtime(); Object result = v8.executeScript(code); v8.release(); return result.toString(); } } ``` ```cpp // C++ 层 (JNI 调用) extern "C" JNIEXPORT jstring JNICALL Java_com_example_NativeBridge_executeJS(JNIEnv* env, jobject, jstring jscode) { const char* code = env->GetStringUTFChars(jscode, nullptr); // 调用 Java 的 J2V8Executor.executeJS() jclass cls = env->FindClass("com/example/J2V8Executor"); jmethodID method = env->GetStaticMethodID(cls, "executeJS", "(Ljava/lang/String;)Ljava/lang/String;"); jstring result = (jstring)env->CallStaticObjectMethod(cls, method, jscode); env->ReleaseStringUTFChars(jscode, code); return result; } ``` > ⚠️ 注意:此方案有显著性能损耗,仅适用于特定集成场景。 #### 4. 性能与优化建议 - **直接使用 V8 C++ API**:优先选择,性能最佳[^2] - **字节码缓存**:对重复执行的脚本预编译字节码,减少解析开销[^3] - **隔离环境复用**:避免频繁创建/销毁 V8 隔离环境(`v8::Isolate`) #### 总结 | 方案 | 适用场景 | 性能 | 复杂度 | |---------------------|---------------------------------|--------|--------| | **V8 C++ API** | 纯 C++ 项目 | ⭐⭐⭐⭐⭐ | 中 | | **JNI + J2V8** | C++/Java 混合项目 | ⭐⭐ | 高 | | **其他引擎** | 轻量级场景(如 Duktape) | ⭐⭐⭐ | 低 | C++ 项目应直接集成 V8 引擎而非 J2V8,后者是专为 Java 设计的解决方案[^1][^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值