Preface to translation

Some netizens are not sure why D2JSP Can execute JavaScript The script program was surprised , So I translated this article , In the original here . This tutorial teaches you how to use SpiderMonkey Create one that can perform JavaScript The script C++ Program , And let JavaScript Scripts manipulate your C++ The internal data of the program 、 operation . You can see from this tutorial that SpiderMonkey With the help of the engine , Give Way C++ The program supports JavaScript Scripting is a very easy What happened , What's better is SpiderMonkey Can also be in Macintosh and Unix Platform use .
SpiderMonkey yes GeckoFirefox Browser kernel ) Of JavaScript The script engine , Please refer to here .

Here is the translation .

------------------------------------------------

The purpose of this tutorial is to teach you how to use JavaScript As a scripting language, make your C++ The program itself takes the initiative .

SpiderMonkey

SpiderMonkey yes Mozilla Part of the project , use C language , Is responsible for running JavaScript Script engine . The other one is called Rhino Of Java engine .

SpiderMonkey The latest version number of can be found in here download . It's published as source code , So you have to compile it yourself ( Translation notes : In fact, there are many compiled binary version numbers on the Internet ,google once js32.dll You can find ).Visual C++ Users can be in src Found under folder Workspace project project File to compile , The result of the compilation is a result called 'js32.dll' Of dll file .

SpiderMonkey Can also be in Macintosh and Unix Upper use , To learn how to compile on these platforms, read Readme.html.

stay C++ Run in JavaScript Program

step 1- establish JavaScript runtime( Execution time instance )

Initialize a JavaScript runtime You can use JS_NewRuntime Method , The method will be runtime Allocate memory , You have to specify a number of bytes at the same time , When the memory allocation exceeds this number, the garbage collector will execute on its own initiative .

JSRuntime *rt = JS_NewRuntime(1000000L);
if ( rt == NULL )
...{
    // Do some error reporting
}

step 2- establish context( Context )

Context Indicates the stack size required for script execution , The amount of private memory allocated to the script execution stack . Each script has its own context Related to .

When one context Is being used by a script or thread , Other scripts or threads cannot use this context. Just at the end of the script or thread , The context Can be reused by the next script or thread .

Create a new context You can use JS_NewContext Method .context It has to be associated with a runtime, call JS_NewContext Method must also specify the stack size .

JSContext *cx = JS_NewContext(m_rt, 8192);
if ( cx == NULL )
...{
    // Do some error reporting
}

step 3- Initialize global objects

Before a script starts executing , It's necessary to initialize some common ones that most scripts will use JavaScript Functions and built-in (build-in) Class object .

Global objects are in a JSClass The structure describes the narrative . The structure can be initialized as follows :

JSClass globalClass =
...{
    "Global", 0,
    JS_PropertyStub,  JS_PropertyStub,
    JS_PropertyStub, JS_PropertyStub,
    JS_EnumerateStub, JS_ResolveStub,
    JS_ConvertStub,  JS_FinalizeStub
};
 Now create and initialize this global object :
JSObject *globalObj = JS_NewObject(cx, &globalClass, 0, 0);
JS_InitStandardClasses(cx, globalObj);

step 4- Run script

One way to run a script is to use JS_EvaluateScript Method :

std::string script = "var today = Date(); today.toString();"
jsval rval;
uintN lineno = 0;
JSBool ok = JS_EvaluateScript(cx, globalObj, script.c_str(), 
                              script.length(), "script", lineno, &rval);

In this script , If it works correctly, the data of the day will be saved in rval in .rval Include the result of the last run function .JS_EvaluteScript return JS_TRUE Represents successful operation , return JS_FALSE It means there is a mistake .

from rval To get the corresponding string value, you can use the following methods . I don't want to explain all the details here , For more specific information, please check by yourself API file .

JSString *str = JS_ValueToString(cx, rval);
std::cout << JS_GetStringBytes(str);

step 5- Clean up the script engine

 You have to clean up the script engine before the end of the program :
JS_DestroyContext(cx);
JS_DestroyRuntime(rt);

stay C++ Define one in JavaScript Classes used in

The class definitions used in this example are as follows :

class Customer
...{
public:
    int GetAge() ...{ return m_age; }
    void SetAge(int newAge) ...{ m_age = newAge; }
    std::string GetName() ...{ return m_name; }
    void SetName(std::string newName) ...{ m_name = newName; }

private:
    int m_age;
    std::string m_name;
};

step 1-JavaScript class

from Customer Class to derive one you want in JavaScript The new ones used in C++ class , Or create one that includes Customer A new class of type member variables .

to JavaScript There has to be a class to use JSClass structure , To do that, you have to create a JSClass Static member variable of type , This variable will be used by other classes , So we have to declare it as public Variable . Other classes can use this structure to infer the type of objects ( see JS_InstanceOf API).

// JSCustomer.h
class JSCustomer
...{
public:
    JSCustomer() : m_pCustomer(NULL) 
    ...{
    }

    ~JSCustomer()
    ...{
        delete m_pCustomer;
        m_pCustomer = NULL;
    }

    static JSClass customerClass;

protected:
    void setCustomer(Customer *customer) 
    ...{
        m_pCustomer = customer; 
    }

    Customer* getCustomer()
    ...{
        return m_pCustomer; 
    }

private:
    Customer *m_pCustomer;

};

The JSClass The structure includes JavaScript The name of the class 、 Flag bits and the name of the callback function for the script engine . For example , The script engine uses a callback function to get a property value from a class .

stay C++ Class is defined in the implementation file JSClass The structure is as follows :

// JSCustomer.cpp
JSClass JSCustomer::customerClass = 
...{
    "Customer", JSCLASS_HAS_PRIVATE,
        JS_PropertyStub, JS_PropertyStub,
        JSCustomer::JSGetProperty, JSCustomer::JSSetProperty,
        JS_EnumerateStub, JS_ResolveStub, 
        JS_ConvertStub, JSCustomer::JSDestructor
};

The callback function used is JSCustomer::JSGetProperty,JSCustomer::JSSetProperty and JSCustomer::JSDestructor. Script engine calls JSGetProperty Get attribute value , call JSSetProperty Setting property values , call JSDestructor destructor JavaScript object .

JSCLASS_HAS_PRIVATE The flag bit causes the script engine to allocate some memory , So that you can JavaScript Add some self-defined data to the object , For example, you can use it to hold class pointers .

Callback function to C++ Class static member function mode exists :

static JSBool JSGetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
static JSBool JSSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
static JSBool JSConstructor(JSContext *cx, JSObject *obj, uintN argc, 
                            jsval *argv, jsval *rval);
static void JSDestructor(JSContext *cx, JSObject *obj);

step 2- Initialize your JavaScript object

Create another one called JSInit Static method of , See the following example , This method will be created in the application JavaScript runtime When called .

static JSObject *JSInit(JSContext *cx, JSObject *obj, JSObject *proto);

JSInit The implementation of the method is about as follows :

JSObject *JSCustomer::JSInit(JSContext *cx, JSObject *obj, JSObject *proto)
...{
    JSObject *newObj = JS_InitClass(cx, obj, proto, &customerClass,
        JSCustomer::JSConstructor, 0,
        JSCustomer::customer_properties, JSCustomer::customer_methods,
        NULL, NULL);
    return newObj;
}

Objects are represented in scripts ( Translation notes :instantiated, In short, it's the object new When I came out ) When , Static methods JSConstructor Will be called . In this way... Can be used JS_SetPrivate API Attach some self-defined data to the object .

JSBool JSCustomer::JSConstructor(JSContext *cx, JSObject *obj, uintN argc, 
                                 jsval *argv, jsval *rval)
...{
    JSCustomer *p = new JSCustomer();

    p->setCustomer(new Customer());
    if ( ! JS_SetPrivate(cx, obj, p) )
        return JS_FALSE;
    *rval = OBJECT_TO_JSVAL(obj);
    return JS_TRUE;
}

JSConstructor The construction method can take multiple parameters , Used to initialize classes . So far, a pointer has been created on the heap , There has to be a way to destroy it , This can be done through JS_Destructor complete :

void JSCustomer::JSDestructor(JSContext *cx, JSObject *obj)
...{
    JSCustomer *p = JS_GetPrivate(cx, obj);
    delete p;
    p = NULL;
}

step 3- Add attributes

Add a JSPropertySpec Type to store attribute information , Define attributes at the same time ID Enumeration variables for .

static JSPropertySpec customer_properties[];
enum
...{
    name_prop,
    age_prop
};

In the implementation file, for example, initialize the array :

JSPropertySpec JSCustomer::customer_properties[] = 
...{ 
    ...{ "name", name_prop, JSPROP_ENUMERATE },
    ...{ "age", age_prop, JSPROP_ENUMERATE },
    ...{ 0 }
};

The last element of the array must be empty , Each of these elements is an element with 3 Array of elements . The first element is for JavaScript Name used . The second element is the only element of the attribute ID, Will be passed to the callback function . The third element is the flag bit ,JSPROP_ENUMERATE Represents the script in enumerating Customer You can see all the properties of the object , You can also specify JSPROP_READONLY To indicate that the attribute does not agree to be changed by the script .

Now it's possible to implement getting and setting Call back function :

JSBool JSCustomer::JSGetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
...{
    if (JSVAL_IS_INT(id)) 
    ...{
        Customer *priv = (Customer *) JS_GetPrivate(cx, obj);
        switch(JSVAL_TO_INT(id))
        ...{
        case name_prop:

            break;
        case age_prop:
            *vp = INT_TO_JSVAL(priv->getCustomer()->GetAge());
            break;
        }
    }
    return JS_TRUE;
}


JSBool JSCustomer::JSSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
...{
    if (JSVAL_IS_INT(id)) 
    ...{
        Customer *priv = (Customer *) JS_GetPrivate(cx, obj);
        switch(JSVAL_TO_INT(id))
        ...{
        case name_prop:
            break;
        case age_prop:
            priv->getCustomer()->SetAge(JSVAL_TO_INT(*vp));
            break;
        }
    }
    return JS_TRUE;
}

It is recommended to return... In the callback function of the property JS_TRUE. Suppose we return to JS_FALSE, When the attribute is not found in the object ( The script engine ) No search .

step 4- Add method

Create a JSFunctionSpec An array of static members of type :

static JSFunctionSpec customer_methods[];

In the implementation file, for example, initialize the array :

JSFunctionSpec wxJSFrame::wxFrame_methods[] = 
...{
    ...{ "computeReduction", computeReduction, 1, 0, 0 },
    ...{ 0 }
};

The last element must be empty , Each of these elements is an element with 5 Array of elements . The first element is the method name for the script . The second is the name of a global or static member function . The third is the number of parameters of the method . The last two can be ignored .

Create a static method in the class :

static JSBool computeReduction(JSContext *cx, JSObject *obj, uintN argc, 
                               jsval *argv, jsval *rval);

This function returns the JS_TRUE, Otherwise return to JS_FALSE. Pay attention to the real JavaScript The return value of the method is stored in rval In the parameter .

An implementation example of this method :

JSBool JSCustomer::computeReduction(JSContext *cx, JSObject *obj, uintN argc, 
                                    jsval *argv, jsval *rval)
...{
    JSCustomer *p = JS_GetPrivate(cx, obj);
    if ( p->getCustomer()->GetAge() < 25 )
        *rval = INT_TO_JSVAL(10);
    else
        *rval = INT_TO_JSVAL(5);
    return JS_TRUE;
}

Use samples

The following script uses the object created earlier :

var c = new Customer();
c.name = "Franky";
c.age = 32;
var reduction = c.computeReduction();

Don't forget to create context Initializes JavaScript object :

JSObject *obj = JSCustomer::JSInit(cx, global);

Class constant

JavaScript type

This chapter explains in JavaScript There are several types that will be used in :Integer,String,Boolean,Double,Object and Function.

In the building ......

Garbage collection

In the building ......

download

main.cpp Demonstrate how to run a javascript Program .JSCustomer.h demonstration Customer Of JavaScript The definition of a class .JSCustomer.cpp demonstration JSCustomer The implementation of the .Customer.h yes Customer C++ The definition of a class .example.js Sample scripts .

SpiderMonkey- Let your C++ The program supports JavaScript More about scripts

  1. Test whether the browser supports JavaScript Script

    If the user is not sure if the browser supports JavaScript Script , So you can apply HTML The annotation symbols provided are used to verify .HTML The notation is based on  <--  Start with  -->  The end of the . If you write in this comment symbol JavaS ...

  2. java ScriptEngine Use ( Support JavaScript Script ,eval() Functions, etc )

    Java SE 6 One of the most eye-catching new features is embedded scripting support . By default ,Java SE 6 Only support JavaScript, But that doesn't mean Java SE 6 Can only support JavaScript. stay Java SE ...

  3. Java Support JavaScript Script calculation

    Java Support JavaScript The execution computing power of scripts : import javax.script.Invocable; import javax.script.ScriptEngine; import ...

  4. IE The browser opens to JavaScript Script support

    stay IE Browser's " Tools " From the menu "internet Options ", Select... In the pop-up command dialog box " Security " tab . Under this tab " The security level of the area & ...

  5. TMS WEB Core v1.2 preview : new Electron Application support

    2019 year 2 month 20 Japan , Wednesday A few months ago , We have begun to work with Electron experiment . After working on the proof of concept , Our goal is to Delphi Developers package as much as they can Electron API. But of course it's not just available E ...

  6. Wechat applet development : Learning notes [5]——JavaScript Script

    Wechat applet development : Learning notes [5]——JavaScript Script Quick start Introduce The main development language for applets is JavaScript , Developers use JavaScript To develop business logic and call applets API Come on ...

  7. [Swift Heaven and earth ] One 、 Super tools -(6) adopt JavaScript( Script ) Code calls the source program of the device

    ***************************************** WeChat official account : Sang Zhi by Shanqing (shanqingyongzhi)* Blog Garden address : Sang Zhi by Shanqing (https://www.cnblogs. ...

  8. If your browser doesn't support javascript function

    If your browser doesn't support javascript Function or prohibited use , So visiting a lot of websites ( Including this website ) when , Some features will not be available . We suggest you open it javascript Function to get the best browsing results . Here are the possible reasons and ways to open it ...

  9. Your program supports complex scheduling ? As promised java edition

    Your program supports complex scheduling ?  This article introduces the c# edition , It's for clients , The server should also have a set of corresponding practices ,java Version of [ year ][ month ][ Japan ][ week ][ Time ] [*][*][*][*][*] ...

Random recommendation

  1. The first step of wechat background development :nodeJS+express Access to wechat background detailed tutorial

    Blog by   Waterwheel   To write Welcome to correct , Please take the link with you ——http://www.cnblogs.com/xuange306/p/4971702.html Preparatory work 1: If you don't have a server , Then you need a ...

  2. Share rabbitMQ Introduction details

    Original address http://blog.csdn.net/cugb1004101218/article/details/21243927 Catalog (?)[-] rabbitMQ documentation rabbitMQ What is it? eliminate ...

  3. java There are two ways to achieve synchronization in :syschronized and lock The difference and connection

    Lock yes java.util.concurrent.locks The interface under the package ,Lock  Implementation provides more than using synchronized  More extensive locking operations available with methods and statements , It can handle thread synchronization in a more elegant way , I ...

  4. 01Vue Data bidirectional binding

    Vue As front-end MV* framework ,Vue.js ( pronunciation /vjuː/, Be similar to  view) Is a progressive framework for building user interfaces . Unlike other heavyweight frameworks ,Vue Adopt the design of bottom-up incremental development . Vue Our core library only focuses on ...

  5. Java Web day01

    1. Configuration environment Eclipse edition :Mars.2 Release (4.5.2) jdk edition : To configure Eclipse (1) Change the code working space jsp The page code (2) Change the background color and font size (3) install jr ...

  6. logging Simple use of modules

    Log configuration #!/usr/bin/python2.7 import os import logging def get_logger(path='./', filename='access.log', ...

  7. requests Library entry 08-delete request

    Or use GitHub The interface of , Before we added a new mailbox , This use delete Request to delete the mailbox , Interface document address import requests test_url = 'https://api.github.com' ...

  8. Java SE And Eclipse error : The main class cannot be found or loaded, or the project cannot be compiled 10 A solution !【 Extract 】

    Make a statement : this BUG It really bothers me for a long time , Today we have a very powerful set of solutions , And I'm afraid that I can't find the original blogger's website in the future , So here is the excerpt copy One copy , I hope all parties can understand the infringement . The portal goes up first :http://blog.csdn.ne ...

  9. Tomcat catalina-deamon.out Log cutting Generate a file every day

    Tomcat Use jsvc Start as a daemons (daemon.sh ). such tomcat It will generate another log file (catalina-daemon.out), Not before catalina.out ...

  10. modify firefox accesskey Shortcut keys

    Chrome in , If set accesskey Words , Can pass Alt + Shortcut key It's a direct jump . But in Firefox in , It may be to prevent the shortcut key conflict in the menu , So set up Shift + Alt + fast ...