Click here to Skip to main content
15,891,372 members
Articles / Containers / Virtual Machine

Using V8 - Google's Chrome JavaScript Virtual Machine

Rate me:
Please Sign up or sign in to vote.
4.90/5 (43 votes)
5 Sep 2008Public Domain6 min read 266K   7.1K   125  
Tutorial and sample explaining how to use the V8 virtual machine inside your application.
// V8xLua_benchmark.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <string.h>
#include <iostream>
// for SetThreadAffinityMask
#include <windows.h>
// for __rdtsc intrinsic
#include <intrin.h>

#pragma warning(disable : 4996) /* strcpy deprecation... argh*/

char username[1024];
int x;

#define DISABLE_PRINTING
#define HOW_MANY_TIMES 100000
static const char *progname = "V8xLua_benchmark";

/*
###################################################################################################
###################################################################################################
###################################################################################################
###################################################################################################
Lua stuff begin here
###################################################################################################
###################################################################################################
###################################################################################################
###################################################################################################
*/
extern "C"{
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}


static int print_lua(lua_State *L) {
  int n = lua_gettop(L);  /* number of arguments */
  int i;
  lua_getglobal(L, "tostring");
  for (i=1; i<=n; i++) {
    const char *s;
    lua_pushvalue(L, -1);  /* function to be called */
    lua_pushvalue(L, i);   /* value to print */
    lua_call(L, 1, 1);
    s = lua_tostring(L, -1);  /* get result */
    if (s == NULL)
      return luaL_error(L, LUA_QL("tostring") " must return a string to "
                           LUA_QL("print"));
#ifndef DISABLE_PRINTING
    //if (i>1) fputs("\t", stdout);
    fputs(s, stdout);
#endif
    lua_pop(L, 1);  /* pop result */
  }
#ifndef DISABLE_PRINTING
  fputs("\n", stdout);
#endif
  return 0;
}

static int Plus(lua_State *L) {
  int a = luaL_checkint(L,1);
  int b = luaL_checkint(L,2);
  lua_pushinteger(L,a+b);
  return 1;
}
static int SetUsername (lua_State *L) {
  size_t l;
  char *s = (char*)luaL_checklstring(L, 1, &l);
  strcpy((char*)&username,s);
  return 1;
}
static int GetUsername(lua_State *L) {
  luaL_Buffer b;
  const char *e=(char*)&username;
  if(!e){
     //variable doesnt exists!
     lua_pushnil(L);
      return 0;
  }
  luaL_buffinit(L, &b);
  luaL_addstring(&b,e);
  luaL_pushresult(&b);
  return 1;
}
static int SetX(lua_State *L) {
  x = luaL_checkint(L,1);
  return 1;
}
static int GetX(lua_State *L) {
  lua_pushinteger(L,x);
  return 1;
}

static void l_message (const char *pname, const char *msg) {
    if (pname)
        fprintf(stderr, "%s: ", pname);
    fprintf(stderr, "%s\n", msg);
    fflush(stderr);
}
static int report (lua_State *L, int status) {
    if (status && !lua_isnil(L, -1)) {
        const char *msg = lua_tostring(L, -1);
        if (msg == NULL)
            msg = "(error object is not a string)";
        l_message(progname, msg);
        lua_pop(L, 1);
    }
    return status;
}
static int traceback (lua_State *L) {
    lua_getfield(L, LUA_GLOBALSINDEX, "debug");
    if (!lua_istable(L, -1)) {
        lua_pop(L, 1);
        return 1;
    }
    lua_getfield(L, -1, "traceback");
    if (!lua_isfunction(L, -1)) {
        lua_pop(L, 2);
        return 1;
    }
    lua_pushvalue(L, 1);  
    lua_pushinteger(L, 2);  
    lua_call(L, 2, 1);  
    return 1;
}
static int docall (lua_State *L, int narg, int clear) {
    int status;
    int base = lua_gettop(L) - narg;  /* function index */
    lua_pushcfunction(L, traceback);  /* push traceback function */
    lua_insert(L, base);  /* put it under chunk and args */
    status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base);
    lua_remove(L, base);  /* remove traceback function */
    /* force a complete garbage collection in case of errors */
    if (status != 0)
        lua_gc(L, LUA_GCCOLLECT, 0);
    return status;
}
static int execute_script (lua_State *L,const char *script_name, int n) {
  int status;
  status = luaL_loadfile(L,script_name);
  if (status == 0)
    status = docall(L, 0, 0);
  else
    lua_pop(L, 0);      
  return report(L, status);
}
void Run_Lua()
{
    lua_State *l;
    l = lua_open();
    luaL_openlibs(l);

    //new functions
    lua_register(l, "print_lua", print_lua);
	lua_register(l, "plus", Plus);

	//Get/Set for string
	lua_register(l, "SetUsername", SetUsername);
    lua_register(l, "GetUsername", GetUsername);

	//Get/Set for integer
	lua_register(l, "SetX", SetX);
    lua_register(l, "GetX", GetX);
	
	//set x
	x = 0;

	//set username 
	strcpy((char*)&username,"John Doe");
#ifndef DISABLE_PRINTING
	printf("################### BEFORE SCRIPT EXECUTION ###################\n");
#endif
	unsigned __int64 loadTime = 0, runTime = 0;
	unsigned __int64 start, end;

	int status;

	//execute script
#ifdef DISABLE_PRINTING
	for(int i=0;i<HOW_MANY_TIMES;i++)
#endif
	{
		start = __rdtsc();
		status = luaL_loadfile(l,"script.lua");
		end = __rdtsc();
		if (status != 0)
		{
			lua_pop(l, 0);      
			printf ("Error loading file\n");
			break;
		}

		loadTime += end - start;

		start = __rdtsc();
		status = docall(l, 0, 0);
		end = __rdtsc();

		runTime += end - start;

		report(l, status);
	}

	std::cout << "Lua load time avg: " << loadTime / HOW_MANY_TIMES << std::endl;
	std::cout << "Lua run time avg: " << runTime / HOW_MANY_TIMES << std::endl;

	std::cout << "Lua total time avg: " << (loadTime + runTime) / HOW_MANY_TIMES << std::endl << std::endl;

#ifndef DISABLE_PRINTING
	//check if my username changed...
	if(strcmp((char*)&username,"John Doe")!=0)
	{
		printf("################### AFTER SCRIPT EXECUTION ###################\n");
		printf("Script changed my username to %s\n",(char*)&username);
		printf("x value after script [%d]\n",x);
	}
#endif
	//Remember to destroy the Lua State 
    lua_close(l);

}

/*
###################################################################################################
###################################################################################################
###################################################################################################
###################################################################################################
V8 stuff begin here
###################################################################################################
###################################################################################################
###################################################################################################
###################################################################################################
*/
#include <v8.h>

v8::Handle<v8::ObjectTemplate> global;
v8::Handle<v8::Context> context;


//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();
}

//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);
}

//convert unsigned int to value
static v8::Local<v8::Value> v8_uint32(unsigned int x) {
	return v8::Uint32::New(x);
}

// 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);
}


// 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
		{
#ifndef DISABLE_PRINTING
			printf(" ");
#endif
		}
		//convert the args[i] type to normal char* string
		v8::String::AsciiValue str(args[i]);
#ifndef DISABLE_PRINTING
		printf("%s", *str);
#endif
	}
#ifndef DISABLE_PRINTING
	printf("\n");
#endif
	//returning Undefined is the same as returning void...
	return v8::Undefined();
}

// Executes a string within the current v8 context.
bool ExecuteString(v8::Handle<v8::String> source,v8::Handle<v8::String> name) {
	unsigned __int64 loadTime = 0, runTime = 0;
	unsigned __int64 start, end;

	v8::HandleScope handle_scope;
	//access global context within this scope
	v8::Context::Scope context_scope(context);
	v8::Handle<v8::Value> result;
	//exception handler
	v8::TryCatch try_catch;

#ifdef DISABLE_PRINTING
	for(int i=0;i<HOW_MANY_TIMES;i++)
	{
#endif
		start = __rdtsc();
		//compile script to binary code - JIT
		v8::Handle<v8::Script> script = v8::Script::Compile(source, name);
		end = __rdtsc();

		loadTime += end - start;

		//check if we got problems on compilation
		if (script.IsEmpty()) {

			// Print errors that happened during compilation.
			v8::String::AsciiValue error(try_catch.Exception());
			printf("%s\n", *error);
			return false;
		}
		else
		{
			start = __rdtsc();
			//no errors , let's continue
			result = script->Run();
			end = __rdtsc();

			runTime += end - start;
		}
#ifdef DISABLE_PRINTING
	}
#endif

	std::cout << "V8 load time avg: " << loadTime / HOW_MANY_TIMES << std::endl;
	std::cout << "V8 run time avg: " << runTime / HOW_MANY_TIMES << std::endl;

	std::cout << "V8 total time avg: " << (loadTime + runTime) / HOW_MANY_TIMES << std::endl << std::endl;

	//check if execution ended with errors
	if(result.IsEmpty())
	{
		// Print errors that happened during execution.
		v8::String::AsciiValue error(try_catch.Exception());
		printf("%s\n", *error);
		return false;
	}
	else 
	{
		if (!result->IsUndefined())
		{
			// If all went well and the result wasn't undefined then print
			// the returned value.
			v8::String::AsciiValue str(result);
			printf("script result [%s]\n", *str);
		}
		return true;
	}
}

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>

char *ReadFile(const char *script_name)
{
	char *str_buff;
	
	struct _stat stat_buffer;
	size_t file_size,numread;
	FILE *input;
	if(_stat(script_name,&stat_buffer)!=0)
    {
       return NULL;
    } 
	input = fopen(script_name, "r");
	file_size=stat_buffer.st_size;
	str_buff=(char*)malloc(file_size+1);
    numread = fread((char*)str_buff,sizeof(char),file_size,input);
	str_buff[numread]='\0';
    if(numread<=0)
    {
		free(str_buff);
		return NULL;
    }
	fclose(input);
	return str_buff;
}

void Run_V8()
{
	v8::HandleScope handle_scope;
	char *script_name = "script.js";
	char *script = ReadFile(script_name);
	if(!script){
		printf("Unable to execute script.js");
		return;
	}
	//convert the string with the script to a v8 string
	v8::Handle<v8::String> source =  v8::String::New(script, strlen(script));
	//each script name must be unique , for this demo I just run one embedded script, so the name can be fixed
	v8::Handle<v8::String> name = v8::String::New(script_name,strlen(script_name)); 

	// Create a template for the global object.
	global = v8::ObjectTemplate::New();

	//associates "plus" on script to the Plus function
	global->Set(v8::String::New("plus"), v8::FunctionTemplate::New(Plus));

	//associates "print" on script to the Print function
	global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print));

	//create accessor for string username
	global->SetAccessor(v8::String::New("user"),userGetter,userSetter);

	//create accessor for integer x
	global->SetAccessor(v8::String::New("x"), XGetter, XSetter);

	//create context for the script
	context = v8::Context::New(NULL, global);

	//fill username with something
	strcpy((char*)&username,"John Doe");

	//x initialization
	x=0;
#ifndef DISABLE_PRINTING
	printf("################### BEFORE SCRIPT EXECUTION ###################\n");
#endif

	ExecuteString(source,name);
	
	//check if my username changed...
	if(strcmp((char*)&username,"John Doe")!=0)
	{
#ifndef DISABLE_PRINTING
		printf("################### AFTER SCRIPT EXECUTION ###################\n");
		printf("Script changed my username to %s\n",(char*)&username);
		printf("x value after script [%d]\n",x);
#endif
	}
	free(script);
}
int plus(int a,int b)
{
	return a+b;
}
void execute_c()
{
	int local;
#ifndef DISABLE_PRINTING
	printf("begin script");
	printf("script executed by %s\n",(char*)&username);
#endif
	if ( strcmp((char*)&username,"John Doe")==0){
#ifndef DISABLE_PRINTING
		printf("\tuser name is invalid. Changing name to Chuck Norris\n");
#endif
		strcpy((char*)&username,"Chuck Norris");
	}
	local = plus(123,27);
#ifndef DISABLE_PRINTING
	printf("123 plus 27 = %d\n",local );
#endif
	x = plus(3456789,6543211);
#ifndef DISABLE_PRINTING
	printf("end script\n");
#endif
}
//same script functionality done in c... just to have a base time
void Run_C()
{
	strcpy((char*)&username,"John Doe");
#ifndef DISABLE_PRINTING
	printf("################### BEFORE SCRIPT EXECUTION ###################\n");
#endif

	unsigned __int64 runTime = 0;
	unsigned __int64 start, end;

	//simple javascritp to test
#ifdef DISABLE_PRINTING
	for(int i=0;i<HOW_MANY_TIMES;i++)
#endif
	{
		start = __rdtsc();
		execute_c();
		end = __rdtsc();

		runTime += end - start;
	}

	std::cout << "C run time avg: " << runTime / HOW_MANY_TIMES << std::endl;

	if(strcmp((char*)&username,"John Doe")!=0)
	{
#ifndef DISABLE_PRINTING
		printf("################### AFTER SCRIPT EXECUTION ###################\n");
		printf("Script changed my username to %s\n",(char*)&username);
		printf("x value after script [%d]\n",x);
#endif
	}
}
/*
How to use:
define DISABLE_PRINTING to enable benchmark and 
undefine DISABLE_PRINTING to enable script with 1 normal execution to check output

*/
int _tmain(int argc, _TCHAR* argv[])
{
	// though on my dual core system I see no difference
	// let's force thread to run on one CPU to be confident in rdtsc results

	DWORD_PTR mask = 1;
	DWORD result = SetThreadAffinityMask(GetCurrentThread(), mask);
	if (!result)
	{
		std::cout << "Unable to set affinity mask" << std::endl;	
	}
	
	Run_Lua();
	Run_V8();
	Run_C();
	return 0;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication


Written By
Software Developer (Senior)
Brazil Brazil
I did extremly different works: services to control harware, internet banking sites, Operational System migration (from Digital to Aix , from HpUX to Linux and from tru64 4.0 to 5.1b), Grafical user interfaces for lots of programs and different OS's...
I also know and had use Delphi, c, c++, java, python, assembly and perl.

Comments and Discussions