V8 is a JavaScript engine from Google that is used in the Chrome browser. It is fast and is available in source codes (C ++) for Linux (more precisely for gcc) and under Windows.
In the light of the growing popularity of using V8, I decided to share my (one-year) experience of using it on the Windows platform as a server-side scripting engine.
Part 1. Introduction and the simplest program that uses V8.
')
- Installation, assembly and addition to your projectSo, V8 is a fast JavaScript engine (ECMA-262) from Google, used in the Chrome browser:
code.google.com/p/v8V8 is available in source code under BSD license:
code.google.com/intl/en-RU/apis/v8/terms.htmlI'll tell you how to build it under MS Visual Studio Express Edition 2008. Installation for Windows is described here:
code.google.com/p/v8/wiki/BuildingOnWindowsSo, install:
python
www.python.org/download/windowsscons
www.scons.org/download.phpsubversion
for example,
www.sliksvn.com/pub/Slik-Subversion-1.6.5-win32.msiThen add paths to the python, scons and subversion binaries in the PATH.
Download the current V8 sources from the trunk branch (stable):
svn checkout v8.googlecode.com/svn/trunk v8
The tools \ visual studio folder contains V8 projects for Visual Studio.
It is convenient to study the code in them, but it is better to build V8 from the command line (from the root folder where SConstruct), for example, like this:
scons msvcltcg=off mode=release library=static snapshot=on env="INCLUDE:C:\Program Files\Microsoft SDKs\Windows\v6.0A\Include;C:\Program Files\Microsoft Visual Studio 9.0\VC\include,LIB:C:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib;C:\Program Files\Microsoft Visual Studio 9.0\VC\lib"
(the path to the installation of the default MS VS EE 2008).
msvcltcg is Link Time Optimization. It is recommended to include only for final releases, otherwise the size of v8.lib will increase by 3-4 times and the link speed of your project will fall.
As you can see, we collect the static library v8.lib, which will soon appear in the same folder.
We take v8.h from the include and v8.lib folder and add it to our MS Visual Studio 2008 project.
Also do not forget to do in the project:
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "winmm.lib")
Otherwise there will be problems when linking. Actually, now the V8 engine is available in our project and you can start exploring the Embedder's Guide
code.google.com/intl/en-RU/apis/v8/embed.html- Introduction to using V8Open v8.h and study.
V8 provides its functionality using C ++ classes that are declared in the namespace (namespace) v8. All important javascript structures are wrapped with the corresponding C ++ class. For example, the class v8 :: Integer is an integer (i64) in JavaScript.
v8.h
...
class Context;
class String;
class Value;
class Utils;
class Number;
class Object;
class Array;
class Int32;
class Uint32;
class External;
class Primitive;
class Boolean;
class Integer;
class Function;
class Date;
...
The most important class is the context (v8 :: Context), that is, the place within which javascript code is compiled and executed. In fact, this is also the place where the global javascript object is stored. It is difficult to imagine interaction with v8, which does not require context (but, probably, it is possible).
Inside itself, the V8 maintains its own stack where the V8 stack objects are placed. For example, to use the context you need to “enter it”. To do this, use the special class C ++ Context :: Scope (more details below). The constructor of this class places a V8 stack object on top of the V8 stack to enter the context. Calling the destructor of the class Context :: Scope removes the corresponding stack object from the top of the V8 stack. The exception catching structures (TryCatch), temporary handles storage pools (HandleScope) and other V8 objects are also placed on the V8 stack. All C ++ classes that manage these objects are the same: in the constructor we place the object on the V8 stack, in the destructor we remove it.
V8 (like any javascript runtime environment) contains a garbage collector that removes V8 objects that no one refers to. Therefore, interaction with V8 objects from C ++ code occurs through handles (v8 :: Handle), I mean pointers to pointers. Simplified it can be represented as:
handle ---> link ---> V8 slot (with usage counter) -> V8 object
Implemented handles in the form of C ++ template classes: v8 :: Handle, v8 :: Local, v8 :: Persistent.
By the time of life handles are divided into temporary (v8 :: Local) and permanent (v8 :: Persistent).
We can assume that these are analogs of C ++ stack and hip objects, that is, the lifetime of temporary handles is tied to the stack, and the permanent ones live until they are clearly not destroyed.
Further, I assume that we are in the v8 and STL namespace ("using namespace v8; using namespace std;").
So, temporary V8 handles (v8 :: Local). Used most often. Let's compare their lifetime with a C ++ stack object:
V8 C++
{ {
HandleScope handle_scope;
Local<Integer> a = Integer::New(1); int a = 1;
} }
// a
* This source code was highlighted with Source Code Highlighter .
As you can see, to store temporary V8 handles, we need a HandleScope object. This is a V8 stack object for storing those very references to V8 objects. Simplified, this is an analogue of the beginning of a block of stack variables. Creating a new temporary handle always results in creating a link in the HandleScope object that is closest to the top of the stack (V8 stack, of course). You cannot create a temporary handle without a HandleScope!
You will use temporary handles very often. For example, we need to convert a C ++ string to a V8 string. For a UTF-8 string, this is done as follows:
string some_utf8_string;
Local< String > s = String ::New(some_utf8_string.data(), some_utf8_string.size());
* This source code was highlighted with Source Code Highlighter .
Many classes (for example, Integer, String, Boolean, Date) provide the static New method (which returns a temporary handle), which allows you to create a new V8 object of this class and enter data from C ++ into it. For example (see v8.h):
class String
...
static Local< String > New( const char * data, int length = -1);
...
* This source code was highlighted with Source Code Highlighter .
However, an object of type Value can be returned:
...
class V8EXPORT Date : public Value {
public :
static Local<Value> New( double time);
...
* This source code was highlighted with Source Code Highlighter .
The Value class stores an arbitrary V8 type. This is what the javascript variable can point to: number, string, object, function, array, etc. Value provides methods for checking its type (IsUndefined (), IsNull (), IsString (), IsFunction (), etc.). Value conversion to the desired type is performed using the Cast method of the class that interests us. For example:
Local<Value> foo;
Local<Function> bar;
if (foo->IsFunction())
bar = Handle<Function>::Cast(foo);
* This source code was highlighted with Source Code Highlighter .
At the same time, Value is the base C ++ class for classes Integer, String, etc. Therefore, you can do this:
Local<Value> foo = Local< String > bar;
* This source code was highlighted with Source Code Highlighter .
- Permanent V8 handlesThere are several cases where temporary handles cannot be used. Sometimes we need to manage the life span of V8 objects ourselves.
First case: V8 context. We create the context ourselves and destroy it when it is no longer needed.
The second case: a V8 object uses an instance of a C ++ object and contains a reference to that instance within itself. Clearly, if the garbage collector suddenly decides to clean up a V8 object, then we lose the reference to the C ++ object and a memory leak occurs. However, we want the V8 objects to be destroyed! We just need to be notified that this will happen now.
All this is possible with the help of persistent handles V8 (Persistent).
I will give again the code for V8 and the analog (in the sense of lifetime) of the code in C ++.
Persistent<Integer> p; int *p = 0;
{ {
HandleScope handle_scope;
Local<Integer> a = Integer::New(1); int a = 1;
p = Persistent<Integer>::New(a); p = new int (a);
} }
Persistent<Integer> q = p; int * q = p;
p.Dispose(); delete p;
p.Clear(); p = 0;
int a = q->Int32Value(); int a = *q;
// ! ! // ! !
* This source code was highlighted with Source Code Highlighter .
So, the main way to get a permanent handle (from a temporary) is to use the Persistent :: New () call. Context is the only class that, when created (New), immediately returns a permanent handle. I hope everyone already understands why he is doing this.
We will postpone the issue of receiving notifications when destroying constant handles, but for now let's try to build the first working program that executes javascript.
To do this, we need to extract strings from the String V8 object in C ++ code. We will use the simplest method - the class String :: AsciiValue.
// Value; ,
string to_string(Local<Value> v)
{
String ::AsciiValue data(v);
const char * p = *data;
if (p == 0) return string ();
return string (p);
}
* This source code was highlighted with Source Code Highlighter .
A function that executes arbitrary javascript code will look like this:
string v8_exec( const char * javascript)
{
HandleScope handle_scope; //
Persistent<Context> context = Context::New(); //
Context::Scope context_scope(context); // "" ;
TryCatch try_catch; // javascript-
Local< String > source = String ::New(javascript); // V8
Local<Script> script = Script::Compile(source); //
if (try_catch.HasCaught()) throw to_string(try_catch.Message()->Get()); // ?
Local<Value> result = script->Run(); //
if (try_catch.HasCaught()) throw to_string(try_catch.Message()->Get()); // ?
context.Dispose(); // -
return to_string(result); //
}
* This source code was highlighted with Source Code Highlighter .
Add the code for main () and get a console application that executes the javascript code passed in the first parameter:
int main( int argc, char *argv[])
{
if (argc < 2) return -1;
try
{
string res = v8_exec(*++argv);
printf( "result: %s" , res.data());
}
catch ( string & err)
{
printf( "error: %s" , err.data());
}
}
* This source code was highlighted with Source Code Highlighter .
Run:
XXX.exe “var s = 0; for (var i = 1; i <100; i ++) s + = i; s; "
And we see:
result: 4950