create blog

go home go home
  1. about
  2. code
  3. wiki
  4. blog

The Script Engine

One of the components of the framework on which I have been working particularly hard in the past week is the Script Engine.

It is what it sounds like: a module designed to allow scripting to work inside the framework. Although most definitely still a work-in-progress, it now has a lot of the features I want it to have.

My goals were:

  • It should be extremely easy to make an object scriptable (less than ten minutes)
  • It should be extremely easy to pass objects to JavaScript
  • It should be extremely easy to retrieve objects from JavaScript
  • It should be possible to implement other scripting languages in future

I don’t know about the last, as I haven’t tried it yet, but the first three I think I may have succeeded in to some extent.

Basically, to give you the idea of what it does, I’m going to give a code dump showing how the script engine works. You very likely will not be interested. On the other hand, you could. So, I am posting it.

/* Create a point class */
//Mem is a base class which allows reference counting.
class Point : public Mem
{
public:
	Point()
	{
		this->x = 0; this->y = 0;
	}
 
	Point(double x, double y)
	{
		this->x = x; this->y = y;
	}
 
	double x, y;
};
 
/* register Point as a type that might be scripted */
ScriptBaseNative(Point);
 
/* Create a wrapper named Point Wrapper for point */
SWrap(Point, PointWrapper)
{
public:
	PointWrapper()
	{
		//add the x and y fields
		this->set("x", $(getX, setX));
		this->set("y", $(getY, setY));
 
		//specify that it is constructable
		constructable = true;
 
		//set the class name
		className = "Point";
	}
 
	SGet(getX)
	{
		//"self" was passed as an argument, so get the point out of it
		Point *point = self->as<Point>();
 
		//if it is not null, return x
		if (point)
			//wrapping it for Script
			return $(point->x);
 
		//otherwise, return undefined
		return Script::Undefined();
	}
 
	SSet(setX)
	{
		//"self" was passed as an argument, so get the point out of it
		//the easy way this time
		Point *point = extract(self);
 
		//if not null, set to value, the other argument
		if (point)
			point->x = value->numberValue();
	}
 
	/* Almost identical Y functions go here... */
 
	//called when a script wrapper is created
	//not when a new point needs creating
	SConstruct
	{
		Point *point = extract(self);
		//add a reference count
		if (point)
			point->ref();
	}
 
	//called when the script wrapper is destroyed
	SDestruct
	{
		Point *point = extract(self);
 
		//return if nothing
		if (!point)
			return;
 
		//deref and delete if needed
		if (point->deref() == 0)
			delete point;
	}
 
 
	//called when new Point is called in JavaScript
	SCreate
	{
		double x = 0, y = 0;
 
		if (arguments.length() > 0)
			x = arguments[0]->numberValue();
 
		if (arguments.length() > 1)
			y = arguments[1]->numberValue();
 
		return $(new Point(x, y));
	}
};
 
/* Elsewhere */
//create contexts
R<GlobalContext> myContext = new GlobalContext();
R<V8Context> v8Context = new V8Context();
 
//register type
myContext->registerType<Point>(new PointWrapper());
 
//test constructor using v8
v8Context->runScript("var jsPoint = new Point(42, 42);");
 
//load a function using v8
v8Context->runScript("function something(point){ return point.x; }");
 
//get the function
SFunction f =  myContext->get("something")->functionValue();
 
//prepare arguments
SArguments args;
args.add($(new Point(4, 2));
 
//call function
double x = 0;
if (f) x = f->apply(Script::SUndefined(), args)->numberValue();
 
std::cout << "The X value of the point is: " << x; //should be 4

It has been a bit difficult to make all of this work, and it is still very much a work-in-progress. One of the largest sources of difficulty was V8. I originally picked it because its Embedder’s Guide looked friendly, but I discovered that there was actually quite a bit that was very poorly documented. The code in v8.h had somewhat more thorough documentation, but I found it lacking as well. What is really needed is several examples, showing the best way to do everything one might want to do.

If you are curious and want to see the code, it is hosted on Launchpad.

Leave a Reply