|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionWho never wondered how virtual machines work? But, better than creating your own virtual machine, what if you can use one done by a big company focusing on improving the speed to the VM? In this article, I'll introduce how to use the V8 in your application, which is Google's open source JavaScript engine inside Chrome - Google’s new browser. BackgroundThis code uses the V8 as an embedded library to execute JavaScript code. To get the source of the library and more information, go to the V8 developer page. To effectively use the V8 library, you need to know C/C++ and JavaScript. Using the CodeLet's see what is inside the demo. The demo shows:
First, let's understand how to initialize the API. Look at this simple example of embedded V8 on C++: #include <v8.h>
using namespace v8;
int main(int argc, char* argv[]) {
// Create a stack-allocated handle scope.
HandleScope handle_scope;
// Create a new context.
Handle<Context> context = Context::New();
// Enter the created context for compiling and
// running the hello world script.
Context::Scope context_scope(context);
// Create a string containing the JavaScript source code.
Handle<String> source = String::New("'Hello' + ', World!'");
// Compile the source code.
Handle<Script> script = Script::Compile(source);
// Run the script to get the result.
Handle<Value> result = script->Run();
// Convert the result to an ASCII string and print it.
String::AsciiValue ascii(result);
printf("%s\n", *ascii);
return 0;
}
Well, but this doesn't explain how we can control variables and functions inside the script. OK, the script does something... but, we need to get this information somehow, and have customized functions to control specific behavior inside the script. The Global TemplateFirst, we need a global template to control our modifications: v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
This creates a new global template which manages our context and our customizations. This is important because in V8, each context is separated, and can have its own global template. In V8, a context is an execution environment that allows separate, unrelated, JavaScript applications to run in a single instance of V8. Adding Customized FunctionsAfter that, we can add a new function named " // plus function implementation - Add two numbers
v8::Handle<v8::Value> Plus(const v8::Arguments& args)
{
unsigned int A = args[0]->Uint32Value();
unsigned int B = args[1]->Uint32Value();
return v8_uint32(A + B);
}
//...
//associates plus on script to the Plus function
global->Set(v8::String::New("plus"), v8::FunctionTemplate::New(Plus));
Customized functions need to receive OK, now we can use a customized function in the JavaScript side: plus(120,44);
and we can use this function's result inside the script: x = plus(1,2);
if( x == 3){
// do something important here!
}
Accessors - Variables Accessible Inside the Script!Now, we can create functions... but wouldn't it be cooler if we can use something defined outside inside the script? Let's do it! The V8 have a thing called Accessor, and with it, we can associate a variable with a name and two Get / Set functions, which will be used by the V8 to access the variable when running the script. global->SetAccessor(v8::String::New("x"), XGetter, XSetter);
This associates the name " //the x variable!
int x;
//get the value of x variable inside javascript
static v8::Handle<v8::Value> XGetter( v8::Local<v8::String> name,
const v8::AccessorInfo& info) {
return v8::Number::New(x);
}
//set the value of x variable inside javascript
static void XSetter( v8::Local<v8::String> name,
v8::Local<v8::Value> value, const v8::AccessorInfo& info) {
x = value->Int32Value();
}
In the Now, we can do the same again for string ( //the username accessible on c++ and inside the script
char username[1024];
//get the value of username variable inside javascript
v8::Handle<v8::Value> userGetter(v8::Local<v8::String> name,
const v8::AccessorInfo& info) {
return v8::String::New((char*)&username,strlen((char*)&username));
}
//set the value of username variable inside javascript
void userSetter(v8::Local<v8::String> name, v8::Local<v8::Value> value,
const v8::AccessorInfo& info) {
v8::Local<v8::String> s = value->ToString();
s->WriteAscii((char*)&username);
}
For the string, things changed a little. The " //create accessor for string username
global->SetAccessor(v8::String::New("user"),userGetter,userSetter);
PrintingThe " //associates print on script to the Print function
global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print));
"print" implementation// The callback that is invoked by v8 whenever the JavaScript 'print'
// function is called. Prints its arguments on stdout separated by
// spaces and ending with a newline.
v8::Handle<v8::Value> Print(const v8::Arguments& args) {
bool first = true;
for (int i = 0; i < args.Length(); i++)
{
v8::HandleScope handle_scope;
if (first)
{
first = false;
}
else
{
printf(" ");
}
//convert the args[i] type to normal char* string
v8::String::AsciiValue str(args[i]);
printf("%s", *str);
}
printf("\n");
//returning Undefined is the same as returning void...
return v8::Undefined();
}
For each parameter, the Sample JavaScriptInside the demo program, we have this sample JavaScript to use what we have created so far: 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");
This script uses the " Accessors with C++ Objects!Preparing the Environment for our ClassNow, how to map a class to the JavaScript using C++? First, the sample class: //Sample class mapped to v8
class Point
{
public:
//constructor
Point(int x, int y):x_(x),y_(y){}
//internal class functions
//just increment x_
void Function_A(){++x_; }
//increment x_ by the amount
void Function_B(int vlr){x_+=vlr;}
//variables
int x_;
};
For this class to be fully on JavaScript, we need to map both the functions and internal variable. The first step is to map a class template on our context: Handle<FunctionTemplate> point_templ = FunctionTemplate::New();
point_templ->SetClassName(String::New("Point"));
We create a "function" template, but this can be seen as a class. This template will have the " Then. we access the prototype class template to add built-in methods for our class: Handle<ObjectTemplate> point_proto = point_templ->PrototypeTemplate();
point_proto->Set("method_a", FunctionTemplate::New(PointMethod_A));
point_proto->Set("method_b", FunctionTemplate::New(PointMethod_B));
After this, the class "knows" that it has two methods and callbacks. But, this is still in the prototype, we can't use it without accessing an instance of the class. Handle<ObjectTemplate> point_inst = point_templ->InstanceTemplate();
point_inst->SetInternalFieldCount(1);
The Now, we have the class instance and can add accessors for the internal variable: point_inst->SetAccessor(String::New("x"), GetPointX, SetPointX);
Then, we have the "ground" prepared... throw the seed: Point* p = new Point(0, 0);
The new class is created, but can only be accessed in C++. To access it, we need: Handle<Function> point_ctor = point_templ->GetFunction();
Local<Object> obj = point_ctor->NewInstance();
obj->SetInternalField(0, External::New(p));
Well, Only one step is missing. We only have a class template and instance, but no name to access it from the JavaScript: context->Global()->Set(String::New("point"), obj);
This last step links the " Accessing a Class Method Inside the JavaScriptOK, but this doesn't explain how we can access Let's look at the Handle<Value> PointMethod_A(const Arguments& args)
{
Local<Object> self = args.Holder();
Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
void* ptr = wrap->Value();
static_cast<Point*>(ptr)->Function_A();
return Integer::New(static_cast<Point*>(ptr)->x_);
}
Like a normal accessor, we must handle the arguments. But, to have access to our class, we must get the class pointer from the internal field (first one). After mapping the internal field to " Now, we have the class pointer for the " The sample v8_embedded_demo_with_object.zip implements all these steps. I hope this helps, and if something is wrong or unclear, please don't hesitate to contact. Points of InterestBefore trying to use the V8 library, I've tried to use Lua as an embedded scripting language to support easy configuration and changes by the user. I got amazed by V8 as it's faster and easier to use than Lua to implement scripting functionality in my own software. I hope this article help others in this task. Look also for Google's documentation on the V8 library! History
| ||||||||||||||||||||