CopperSpice API  1.7.4
Scriptable Applications

## Classes

class  QScriptClass
Interface for defining custom behavior of CsScript objects More...

class  QScriptClassPropertyIterator
Iterator interface for custom CsScript objects More...

class  QScriptContext
CsScript function invocation More...

class  QScriptContextInfo

class  QScriptEngine
Environment for evaluating CsScript code More...

class  QScriptEngineAgent
Interface to report events pertaining to QScriptEngine execution More...

class  QScriptExtensionPlugin
An abstract base class for custom QScript extension plugins More...

class  QScriptProgram
Encapsulates a CsScript program More...

class  QScriptString
Acts as a handle to "interned" strings in a QScriptEngine More...

class  QScriptSyntaxCheckResult
Result of a script syntax check More...

class  QScriptValue
Acts as a container for the CsScript data types More...

class  QScriptValueIterator
Java-style iterator for QScriptValue More...

class  QScriptable

## Detailed Description

CopperSpice provides support for application scripting with ECMAScript. The following guides and references cover aspects of programming with ECMAScript.

### Language Overview

CsScript is based on the ECMAScript scripting language, as defined in standard ECMA-262. Microsoft's JScript, and Netscape's JavaScript are also based on the ECMAScript standard. For an overview of ECMAScript refer to ECMAScript reference.

If you are not familiar with the ECMAScript language there are several existing tutorials and books which cover this subject, such as "JavaScript: The Definitive Guide".

### Basic Usage

To evaluate script code create a QScriptEngine and call its evaluate() function, passing the script code (text) to evaluate as argument.

qDebug() << "the magic number is:" << engine.evaluate("1 + 2").toNumber();

The return value will be the result of the evaluation (represented as a QScriptValue object); this can be converted to standard C++ and CopperSpice types.

Custom properties can be made available to scripts by registering them with the script engine. This is most easily done by setting properties of the script engine's Global Object:

engine.globalObject().setProperty("foo", 123);
qDebug() << "foo times two is:" << engine.evaluate("foo * 2").toNumber();

This places the properties in the script environment, thus making them available to script code.

### Making a QObject Available to the Script Engine

Any QObject instance can be made available for use with scripts.

When a QObject is passed to the QScriptEngine::newQObject() function, a CsScript wrapper object is created that can be used to make the QObject's signals, slots, properties, and child objects available to scripts.

Here is an example of making an instance of a QObject subclass available to script code under the name "myObject":

QObject *someObject = new MyObject;
QScriptValue objectValue = engine.newQObject(someObject);
engine.globalObject().setProperty("myObject", objectValue);

This will create a global variable called myObject in the script environment. The variable serves as a proxy to the underlying C++ object. Note that the name of the script variable can be anything; i.e., it is not dependent upon QObject::objectName().

The newQObject() function accepts two additional optional arguments: one is the ownership mode, and the other is a collection of options that allow you to control certain aspects of how the QScriptValue that wraps the QObject should behave. We will come back to the usage of these arguments later.

### Using Signals and Slots

CsScript adapts Signals and Slots feature for scripting. There are three principal ways to use signals and slots with CsScript:

• Hybrid C++/script: C++ application code connects a signal to a script function. The script function can, for example, be a function that the user has typed in, or one that you have read from a file. This approach is useful if you have a QObject but do not want to expose the object itself to the scripting environment; you just want a script to be able to define how a signal should be reacted to, and leave it up to the C++ side of your application to establish the connection.
• Hybrid script/C++: A script can connect signals and slots to establish connections between pre-defined objects that the application exposes to the scripting environment. In this scenario, the slots themselves are still written in C++, but the definition of the connections is fully dynamic (script-defined).
• Purely script-defined: A script can both define signal handler functions (effectively "slots written in CopperSpice Script"), and set up the connections that utilize those handlers. For example, a script can define a function that will handle the QLineEdit::returnPressed() signal, and then connect that signal to the script function.

Use the qScriptConnect() function to connect a C++ signal to a script function. In the following example a script signal handler is defined that will handle the QLineEdit::textChanged() signal:

QLineEdit *edit = new QLineEdit(...);
QScriptValue handler = eng.evaluate("(function(text) { print('text was changed to', text); })");
qScriptConnect(edit, SIGNAL(textChanged(const QString &)), QScriptValue(), handler);

The first two arguments to qScriptConnect() are the same as you would pass to QObject::connect() to establish a normal C++ connection. The third argument is the script object that will act as the this object when the signal handler is invoked; in the above example we pass an invalid script value, so the this object will be the Global Object. The fourth argument is the script function ("slot") itself. The following example shows how the this argument can be put to use:

QLineEdit *edit1 = new QLineEdit(...);
QLineEdit *edit2 = new QLineEdit(...);
QScriptValue handler = eng.evaluate("(function() { print('I am', this.name); })");
QScriptValue obj1 = eng.newObject();
obj1.setProperty("name", "the walrus");
QScriptValue obj2 = eng.newObject();
obj2.setProperty("name", "Sam");
qScriptConnect(edit1, SIGNAL(returnPressed()), obj1, handler);
qScriptConnect(edit2, SIGNAL(returnPressed()), obj2, handler);

We create two QLineEdit objects and define a single signal handler function. The connections use the same handler function, but the function will be invoked with a different this object depending on which object's signal was triggered, so the output of the print() statement will be different for each.

In script code, CopperSpice Script uses a different syntax for connecting to and disconnecting from signals than the familiar C++ syntax; i.e., QObject::connect(). To connect to a signal, you reference the relevant signal as a property of the sender object, and invoke its connect() function. There are three overloads of connect(), each with a corresponding disconnect() overload. The following subsections describe these three forms.

### Signal to Function Connections

connect(function)

In this form of connection, the argument to connect() is the function to connect to the signal.

function myInterestingScriptFunction() {
// ...
}
// ...
myQObject.somethingChanged.connect(myInterestingScriptFunction);

The argument can be a CopperSpice Script function as in the above example, or a QObject slot as in the following example:

myQObject.somethingChanged.connect(myOtherQObject.doSomething);

When the argument is a QObject slot, the argument types of the signal and slot do not necessarily have to be compatible; CsScript will, if necessary, perform conversion of the signal arguments to match the argument types of the slot.

To disconnect from a signal invoke the signal's disconnect() function, passing the function to disconnect as argument:

myQObject.somethingChanged.disconnect(myInterestingFunction);
myQObject.somethingChanged.disconnect(myOtherQObject.doSomething);

When a script function is invoked in response to a signal, the this object will be the Global Object.

### Signal to Member Function Connections

connect(thisObject, function)

In this form of the connect() function, the first argument is the object that will be bound to the variable, this, when the function specified using the second argument is invoked.

If you have a push button in a form, you typically want to do something involving the form in response to the button's clicked signal; passing the form as the this object makes sense in such a case.

var obj = { x: 123 };
var fun = function() { print(this.x); };
myQObject.somethingChanged.connect(obj, fun);

To disconnect from the signal, pass the same arguments to disconnect():

myQObject.somethingChanged.disconnect(obj, fun);

### Signal to Named Member Function Connections

connect(thisObject, functionName)

In this form of the connect() function, the first argument is the object that will be bound to the variable, this, when a function is invoked in response to the signal. The second argument specifies the name of a function that is connected to the signal, and this refers to a member function of the object passed as the first argument (thisObject in the above scheme).

Note that the function is resolved when the connection is made, not when the signal is emitted.

var obj = { x: 123, fun: function() { print(this.x); } };
myQObject.somethingChanged.connect(obj, "fun");

To disconnect from the signal, pass the same arguments to disconnect():

myQObject.somethingChanged.disconnect(obj, "fun");

### Error Handling

When connect() or disconnect() succeeds, the function will return undefined; otherwise, it will throw a script exception. You can obtain an error message from the resulting Error object. Example:

try {
myQObject.somethingChanged.connect(myQObject, "slotThatDoesntExist");
} catch (e) {
print(e);
}

### Emitting Signals from Scripts

To emit a signal from script code, you simply invoke the signal function, passing the relevant arguments:

class="js">
myQObject.somethingChanged("hello");

It is currently not possible to define a new signal in a script; i.e., all signals must be defined by C++ classes.

When a signal or slot is overloaded, CsScript will attempt to pick the right overload based on the actual types of the QScriptValue arguments involved in the function invocation. For example, if your class has slots myOverloadedSlot(int) and myOverloadedSlot(QString), the following script code will behave reasonably:

You can specify a particular overload by using array-style property access with the normalized signature of the C++ function as the property name:

myQObject['myOverloadedSlot(int)']("10"); // call int overload; the argument is converted to an int
myQObject['myOverloadedSlot(QString)'](10); // call QString overload; the argument is converted to a string

If the overloads have different number of arguments, CsScript will pick the overload with the argument count that best matches the actual number of arguments passed to the slot.

For overloaded signals, CopperSpice Script will throw an error if you try to connect to the signal by name; you have to refer to the signal with the full normalized signature of the particular overload you want to connect to.

### Accessing Properties

The properties of the QObject are available as properties of the corresponding CsScript object. When you manipulate a property in script code, the C++ get/set method for that property will automatically be invoked. For example, if your C++ class has a property declared as follows:

CS_PROPERTY_WRITE(enabled, setEnabled)

then script code can do things like the following:

myQObject.enabled = true;
// ...
myQObject.enabled = ! myQObject.enabled;

### Accessing Child QObjects

Every named child of the QObject (that is, for which QObject::objectName() is not an empty string) is by default available as a property of the CsScript wrapper object. For example, if you have a QDialog with a child widget whose objectName property is "okButton", you can access this object in script code through the expression

myDialog.okButton

Since objectName is itself a CS_PROPERTY, you can manipulate the name in script code to, for example, rename an object:

myDialog.okButton.objectName = "cancelButton";
// from now on, myDialog.cancelButton references the button

You can also use the functions findChild() and findChildren() to find children. These two functions behave identically to QObject::findChild() and QObject::findChildren(), respectively.

For example, we can use these functions to find objects using strings and regular expressions:

var okButton = myDialog.findChild("okButton");
if (okButton != null) {
// do something with the OK button
}
var buttons = myDialog.findChildren(RegExp("button[0-9]+"));
for (var i = 0; i < buttons.length; ++i) {
// do something with buttons[i]
}

You typically want to use findChild() when manipulating a form that uses nested layouts; that way the script is isolated from the details about which particular layout a widget is located in.

### Controlling QObject Ownership

CopperSpice Script uses garbage collection to reclaim memory used by script objects when they are no longer needed; an object's memory can be automatically reclaimed when it is no longer referenced anywhere in the scripting environment. CopperSpice Script lets you control what happens to the underlying C++ QObject when the wrapper object is reclaimed (i.e., whether the QObject is deleted or not); you do this when you create an object by passing an ownership mode as the second argument to QScriptEngine::newQObject().

Knowing how CopperSpice Script deals with ownership is important, since it can help you avoid situations where a C++ object is not deleted when it should be (causing memory leaks), or where a C++ object is deleted when it should not be (typically causing a crash if C++ code later tries to access that object).

### CopperSpice Ownership

By default the script engine does not take ownership of the QObject which is passed to QScriptEngine::newQObject(). The object is managed according to the CopperSpice object ownership. This mode is appropriate when you are wrapping C++ objects which are part of your application's core. For example, when they an object should persist regardless of what happens in the scripting environment. Another way of stating this is that the C++ objects should outlive the script engine.

Refer to Object Trees for additional information.

### Script Ownership

Specifying QScriptEngine::ScriptOwnership as the ownership mode will cause the script engine to take full ownership of the QObject and delete it when it determines that it is safe to do so (i.e., when there are no more references to it in script code). This ownership mode is appropriate if the QObject does not have a parent object, and/or the QObject is created in the context of the script engine and is not intended to outlive the script engine.

For example, a constructor function that constructs QObject only to be used in the script environment is a good candidate:

QScriptValue myQObjectConstructor(QScriptContext *context, QScriptEngine *engine) {
// let the engine manage the new object's lifetime.
return engine->newQObject(new MyQObject(), QScriptEngine::ScriptOwnership);
}

### Auto-Ownership

With QScriptEngine::AutoOwnership the ownership is based on whether the QObject has a parent or not. If the CsScript garbage collector finds that the QObject is no longer referenced within the script environment, the QObject will be deleted only if it does not have a parent.

### What Happens When Someone Else Deletes the QObject?

It is possible that a wrapped QObject is deleted outside of CopperSpice Script's control; i.e., without regard to the ownership mode specified. In this case, the wrapper object will still be an object (unlike the C++ pointer it wraps, the script object will not become null). Any attempt to access properties of the script object will, however, result in a script exception being thrown.

Note that QScriptValue::isQObject() will still return true for a deleted QObject, since it tests the type of the script object, not whether the internal pointer is non-null. In other words, if QScriptValue::isQObject() returns true but QScriptValue::toQObject() returns a null pointer, this indicates that the QObject has been deleted outside of CopperSpice Script (perhaps accidentally).

QScriptEngine::newQObject() can take a third argument which allows you to control various aspects of the access to the QObject through the CsScript wrapper object it returns.

QScriptEngine::ExcludeChildObjects specifies that child objects of the QObject should not appear as properties of the wrapper object.

QScriptEngine::ExcludeSuperClassProperties and QScriptEngine::ExcludeSuperClassMethods can be used to avoid exposing members that are inherited from the QObject's superclass. This is useful for defining a "pure" interface where inherited members do not make sense from a scripting perspective; e.g., you do not want script authors to be able to change the objectName property of the object or invoke the deleteLater() slot.

QScriptEngine::AutoCreateDynamicProperties specifies that properties that do not already exist in the QObject should be created as dynamic properties of the QObject, rather than as properties of the CsScript wrapper object. If you want new properties to truly become persistent properties of the QObject, rather than properties that are destroyed along with the wrapper object (and that are not shared if the QObject is wrapped multiple times with QScriptEngine::newQObject()), you should use this option.

QScriptEngine::SkipMethodsInEnumeration specifies that signals and slots should be skipped when enumerating the properties of the QObject wrapper in a for-in script statement. This is useful when defining prototype objects, since by convention function properties of prototypes should not be enumerable.

### Making a QObject based Class Constructible from a Script

The QScriptEngine::newQObject() function is used to wrap an existing QObject instance, so that it can be made available to scripts. A different scenario is that you want scripts to be able to construct new objects, not just access existing ones.

The CopperSpice meta type system currently does not provide dynamic binding of constructors for QObject based classes. If you want to make such a class new-able from scripts, CopperSpice Script can generate a reasonable script constructor for you. Refer to QScriptEngine::scriptValueFromQMetaObject().

You can also use QScriptEngine::newFunction() to wrap your own factory function, and add it to the script environment; see QScriptEngine::newQMetaObject() for an example.

### Enum Values

Values for enums declared with CS_ENUMS are not available as properties of individual wrapper objects; rather, they are properties of the QMetaObject wrapper object that can be created with QScriptEngine::newQMetaObject().

### Conversion Between CsScript and C++ Types

CsScript will perform type conversion when a value needs to be converted from the script side to the C++ side or vice versa; for instance, when a C++ signal triggers a script function, when you access a QObject property in script code, or when you call QScriptEngine::toScriptValue() or QScriptEngine::fromScriptValue() in C++. CsScript provides default conversion operations for many of the built-in CopperSpice types. You can change the conversion operation for a type (including your custom C++ types) by registering your own conversion functions with qScriptRegisterMetaType().

### Conversion from CsScript to C++

The following table describes the default conversion from a QScriptValue to a C++ type.

C++ TypeDefault Conversion
boolQScriptValue::toBool()
intQScriptValue::toInt32()
uintQScriptValue::toUInt32()
floatfloat(QScriptValue::toNumber())
doubleQScriptValue::toNumber()
shortshort(QScriptValue::toInt32())
ushortQScriptValue::toUInt16()
charchar(QScriptValue::toInt32())
ucharunsigned char(QScriptValue::toInt32())
qlonglongqlonglong(QScriptValue::toInteger())
qulonglongqulonglong(QScriptValue::toInteger())
QStringAn empty string if the QScriptValue is null or undefined; QScriptValue::toString() otherwise.
QDateTimeQScriptValue::toDateTime()
QDateQScriptValue::toDateTime()
QRegularExpressionQScriptValue::toRegExp()
QObject*QScriptValue::toQObject()
QWidget*QScriptValue::toQObject()
QVariantQScriptValue::toVariant()
QCharIf the QScriptValue is a string, the result is the first character of the string, or a null QChar if the string is empty; otherwise, the result is a QChar constructed from the unicode obtained by converting the QScriptValue to a ushort.
QStringListIf the QScriptValue is an array, the result is a QStringList constructed from the result of QScriptValue::toString() for each array element; otherwise, the result is an empty QStringList.
QVariantListIf the QScriptValue is an array, the result is a QVariantList constructed from the result of QScriptValue::toVariant() for each array element; otherwise, the result is an empty QVariantList.
QVariantMapIf the QScriptValue is an object, the result is a QVariantMap with a (key, value) pair of the form (propertyName, propertyValue.toVariant()) for each property, using QScriptValueIterator to iterate over the object's properties.
QObjectListIf the QScriptValue is an array, the result is a QObjectList constructed from the result of QScriptValue::toQObject() for each array element; otherwise, the result is an empty QObjectList.
QList<int>If the QScriptValue is an array, the result is a QList<int> constructed from the result of QScriptValue::toInt32() for each array element; otherwise, the result is an empty QList<int>.

Additionally, CsScript will handle the following cases:

• If the QScriptValue is a QObject and the target type name ends with * (i.e., it is a pointer), the QObject pointer will be cast to the target type with qobject_cast().
• If the QScriptValue is a QVariant and the target type name ends with * (i.e., it is a pointer), and the userType() of the QVariant is the type that the target type points to, the result is a pointer to the QVariant's data.
• If the QScriptValue is a QVariant and it can be converted to the target type (according to QVariant::canConvert()), the QVariant will be cast to the target type with qvariant_cast().

### Default Conversion from C++ to CsScript

The following table describes the default behavior when a QScriptValue is constructed from a C++ type:

C++ TypeDefault Construction
voidQScriptEngine::undefinedValue()
boolQScriptValue(engine, value)
intQScriptValue(engine, value)
uintQScriptValue(engine, value)
floatQScriptValue(engine, value)
doubleQScriptValue(engine, value)
shortQScriptValue(engine, value)
ushortQScriptValue(engine, value)
charQScriptValue(engine, value)
ucharQScriptValue(engine, value)
QStringQScriptValue(engine, value)
qlonglong QScriptValue(engine, qsreal(value)). Note that the conversion may lead to loss of precision, since not all 64-bit integers can be represented using the qsreal type.
qulonglong QScriptValue(engine, qsreal(value)). Note that the conversion may lead to loss of precision, since not all 64-bit unsigned integers can be represented using the qsreal type.
QCharQScriptValue(this, value.unicode())
QDateTimeQScriptEngine::newDate(value)
QDateQScriptEngine::newDate(value)
QRegularExpressionQScriptEngine::newRegExp(value)
QObject*QScriptEngine::newQObject(value)
QWidget*QScriptEngine::newQObject(value)
QVariantQScriptEngine::newVariant(value)
QStringListA new script array (created with QScriptEngine::newArray()), whose elements are created using the QScriptValue(QScriptEngine *, QString) constructor for each element of the list.
QVariantListA new script array (created with QScriptEngine::newArray()), whose elements are created using QScriptEngine::newVariant() for each element of the list.
QVariantMapA new script object (created with QScriptEngine::newObject()), whose properties are initialized according to the (key, value) pairs of the map.
QObjectListA new script array (created with QScriptEngine::newArray()), whose elements are created using QScriptEngine::newQObject() for each element of the list.
QList<int>A new script array (created with QScriptEngine::newArray()), whose elements are created using the QScriptValue(QScriptEngine *, int) constructor for each element of the list.

Other types (including custom types) will be wrapped using QScriptEngine::newVariant(). For null pointers of any type, the result is QScriptEngine::nullValue().

### How to Design and Implement Application Objects

This section explains how to implement application objects and provides the necessary technical background material.

### Making a C++ object available to Scripts Written in CsScript

Making C++ classes and objects available to a scripting language is not trivial because scripting languages tend to be more dynamic than C++, and it must be possible to introspect objects (query information such as function names, function signatures, properties, etc., at run-time). Standard C++ does not provide features for this.

To understand how to make C++ objects available to CopperSpice Script, some basic knowledge of the Meta Object system is required. Refer to the CopperSpice Object Model and the Meta Object System.

However, this knowledge is not essential in the simplest cases. To make an object available in CsScript, it must derive from QObject. All classes which derive from QObject can be introspected and can provide the information needed by the scripting engine at run-time; e.g., class name, functions, signatures. Because we obtain the information we need about classes dynamically at run-time, there is no need to write wrappers for QObject derived classes.

### Making C++ Class Member Functions Available in CsScript

The meta object system also makes information about signals and slots dynamically available at run-time. By default, for QObject subclasses, only the signals and slots are automatically made available to scripts. This is very convenient because, in practice, we normally only want to make specially chosen functions available to JavaScript. When you create a QObject subclass, make sure that the functions you want to expose to CsScript are public slots.

For example, the following class definition enables scripting only for certain functions:

class MyObject : public QObject
{
CS_OBJECT(MyObject)
public:
MyObject( ... );
void aNonScriptableFunction();
// these slots will be available in CsScript
CS_SLOT_1(Public, void calculate())
CS_SLOT_2(calculate)
CS_SLOT_1(Public, void setEnabled(bool enabled))
CS_SLOT_2(setEnabled)
CS_SLOT_1(Public, bool isEnabled())
CS_SLOT_2(isEnabled)
};

In the example above, aNonScriptableFunction() is not declared as a slot, so it will not be available in CsScript. The other three functions will automatically be made available in CsScript because they are declared in the public slots section of the class definition.

It is possible to make any function script-invokable by specifying the CS_INVOKABLE() modifier when declaring the function:

class MyObject : public QObject
{
CS_OBJECT(MyObject)
public:
CS_INVOKABLE_METHOD_1(Public, void thisMethodIsInvokableInCsScript())
CS_INVOKABLE_METHOD_2(thisMethodIsInvokableInCsScript)
};

Once declared with CS_INVOKABLE(), the method can be invoked from CsScript code just as if it were a slot. Although such a method is not a slot, you can still specify it as the target function in a call to connect() in script code; connect() accepts both native and non-native functions as targets.

As discussed in Default Conversion from %CsScript to C++, CopperSpice Script handles conversion for many C++ types. If your function takes arguments for which CsScript does not handle conversion, you need to supply conversion functions. This is done using the qScriptRegisterMetaType() function.

### Making C++ Class Properties Available in CsScript

In the previous example, if we wanted to get or set a property using CsScript we would have to write code like the following:

var obj = new MyObject;
obj.setEnabled( true );
print( "obj is enabled: " + obj.isEnabled() );

Scripting languages often provide a property syntax to modify and retrieve properties (in our case the enabled state) of an object. Many script programmers would want to write the above code like this:

var obj = new MyObject;
obj.enabled = true;
print( "obj is enabled: " + obj.enabled );

To make this possible, you must define properties in the C++ QObject subclass. For example, the following MyObject class declaration declares a boolean property called enabled, which uses the function setEnabled(bool) as its setter function and isEnabled() as its getter function:

class MyObject : public QObject
{
CS_OBJECT(MyObject)
// define the enabled property
CS_PROPERTY_WRITE(enabled, setEnabled)
public:
MyObject( ... );
void aNonScriptableFunction();
// these slots will be available in CsScript
CS_SLOT_1(Public, void calculate())
CS_SLOT_2(calculate)
CS_SLOT_1(Public, void setEnabled(bool enabled))
CS_SLOT_2(setEnabled)
CS_SLOT_1(Public, bool isEnabled())
CS_SLOT_2(isEnabled)
};

The only difference from the original code is the use of the macro CS_PROPERTY() which takes the type and name of the property, and the names of the setter and getter functions as arguments.

If you do not want a property of your class to be accessible in CsScript, you set the SCRIPTABLE attribute to false when declaring the property. By default the SCRIPTABLE attribute is true. For example:

CS_PROPERTY_WRITE(nonScriptableProperty, bar)
CS_PROPERTY_SCRIPTABLE(nonScriptableProperty, false)

### Reacting to C++ Objects Signals in Scripts

In the CopperSpice object model, signals are used as a notification mechanism between QObjects. This means one object can connect a signal to another object's slot and, every time the signal is emitted, the slot is called. This connection is established using the QObject::connect() function.

The signals and slots mechanism is also available to CsScript programmers. The code to declare a signal in C++ is the same, regardless of whether the signal will be connected to a slot in C++ or in CsScript.

class MyObject : public QObject
{
CS_OBJECT(MyObject)
// define the enabled property
CS_PROPERTY_WRITE(enabled, setEnabled)
public:
MyObject( ... );
void aNonScriptableFunction();
CS_SIGNAL_1(Public, void enabledChanged(bool newState))
CS_SIGNAL_2(enabledChanged,newState)
// these slots will be available in CsScript
CS_SLOT_1(Public, void calculate())
CS_SLOT_2(calculate)
CS_SLOT_1(Public, void setEnabled(bool enabled))
CS_SLOT_2(setEnabled)
CS_SLOT_1(Public, bool isEnabled())
CS_SLOT_2(isEnabled)
};

The only change we have made to the code in the previous section is to declare a signals section with the relevant signal. Now, the script writer can define a function and connect to the object like this:

function enabledChangedHandler( b )
{
print( "state changed to: " + b );
}
function init()
{
var obj = new MyObject();
// connect a script function to the signal
obj["enabledChanged(bool)"].connect(enabledChangedHandler);
obj.enabled = true;
print( "obj is enabled: " + obj.enabled );
}

### Design of Application Objects

The previous section described how to implement C++ objects which can be used in CsScript. Application objects are the same kind of objects, and they make your application's functionality available to JavaScript. Since the C++ application is already written in CopperSpice, many objects are already QObjects. The easiest approach would be to simply add all these QObject as application objects to the scripting engine. For small applications this might be sufficient, but for larger applications this is probably not the right approach. The problem is that this method reveals too much of the internal API and gives script programmers access to application internals which should not be exposed.

Generally, the best way of making application functionality available to JavaScript is to code some QObjects which define the applications public API using signals, slots, and properties. This gives you complete control of the functionality made available by the application. The implementations of these objects simply call the functions in the application which do the real work. So, instead of making all your QObjects available to the scripting engine, just add the wrapper QObjects.

### Returning QObject Pointers

If you have a slot that returns a QObject pointer, you should note that, by default, CopperSpice Script only handles conversion of the types QObject* and %&Widget*. This means that if your slot is declared with a signature like "MyObject* getMyObject()", CsScript does not automatically know that MyObject* should be handled in the same way as %&Object* and QWidget*. The simplest way to solve this is to only use QObject* and %&Widget* in the method signatures of your scripting interface.

Alternatively, you can register conversion functions for your custom type with the qScriptRegisterMetaType() function. In this way, you can preserve the precise typing in your C++ declarations, while still allowing pointers to your custom objects to flow seamlessly between C++ and scripts.

class MyObject : public QObject
{
CS_OBJECT(MyObject)
...
};
Q_DECLARE_METATYPE(MyObject*)
QScriptValue myObjectToScriptValue(QScriptEngine *engine, MyObject* const &in)
{ return engine->newQObject(in); }
void myObjectFromScriptValue(const QScriptValue &object, MyObject* &out)
{ out = qobject_cast<MyObject*>(object.toQObject()); }
...
qScriptRegisterMetaType(&engine, myObjectToScriptValue, myObjectFromScriptValue);

### Function Objects and Native Functions

In CopperSpice Script functions are first-class values; they are objects that can have properties of their own, just like any other type of object. They can be stored in variables and passed as arguments to other functions. Knowing how function calls in CopperSpice Script behave is useful when you want to define and use your own script functions. This section discusses this matter, and also explains how you can implement native functions; that is, CopperSpice Script functions written in C++, as opposed to functions written in the scripting language itself. Even if you will be relying mostly on the dynamic QObject binding that CopperSpice Script provides, knowing about these powerful concepts and techniques is important to understand what's actually going on when script functions are executed.

### Calling a CsScript Function from C++

Calling a CopperSpice Script function from C++ is achieved with the QScriptValue::call() function. A typical scenario is that you evaluate a script that defines a function, and at some point you want to call that function from C++, perhaps passing it some arguments, and then handle the result. The following script defines a CopperSpice Script object that has a toKelvin() function.

({ unitName: "Celsius",
toKelvin: function(x) { return x + 273; }
})

The toKelvin() function takes a temperature in Kelvin as argument, and returns the temperature converted to Celsius. The following snippet shows how the toKelvin() function might be obtained and called from C++.

QScriptValue object = engine.evaluate("({ unitName: 'Celsius', toKelvin: function(x) { return x + 273; } })");
QScriptValue toKelvin = object.property("toKelvin");
QScriptValue result = toKelvin.call(object, QScriptValueList() << 100);
qDebug() << result.toNumber(); // 373

If a script defines a global function, you can access the function as a property of QScriptEngine::globalObject(). For example, the following script defines a global function add().

return a + b;
}
\code
C++ code might call the add() function as follows.
code
qDebug() << add.call(QScriptValue(), QScriptValueList() << 1 << 2).toNumber(); // 3

As already mentioned, functions are just values in CopperSpice Script; a function by itself is not "tied to" a particular object. This is why you have to specify a this object (the first argument to QScriptValue::call()) that the function should be applied to.

If the function is supposed to act as a method (i.e. it can only be applied to a certain class of objects), it is up to the function itself to check that it is being called with a compatible this object.

Passing an invalid QScriptValue as the this argument to QScriptValue::call() indicates that the Global Object should be used as the this object; in other words, that the function should be invoked as a global function.

### The "this" Object

When a function is invoked from a script the way in which it is invoked determines the this object.

var getProperty = function(name) { return this[name]; };
name = "Global Object"; // creates a global variable
print(getProperty("name")); // "Global Object"
var myObject = { name: 'My Object' };
print(getProperty.call(myObject, "name")); // "My Object"
myObject.getProperty = getProperty;
print(myObject.getProperty("name")); // "My Object"
getProperty.name = "The getProperty() function";
getProperty.getProperty = getProperty;
getProperty.getProperty("name"); // "The getProperty() function"

Unlike C++ and Java the this object is not part of the execution scope. This means that member functions or methods, those which operate on this, must always use the this keyword to access the object's properties. For example, the following script probably does not do what you would expect or want.

var o = { a: 1, b: 2, sum: function() { return a + b; } };
print(o.sum()); // reference error, or sum of global variables a and b!!

You will get a reference error saying that 'a is not defined' or, worse, two totally unrelated global variables a and b will be used to perform the computation, if they exist. Instead, the script should look like this:

var o = { a: 1, b: 2, sum: function() { return this.a + this.b; } };
print(o.sum()); // 3

Accidentally omitting the this keyword is a typical source of errors for programmers who are used to the scoping rules of C++ and Java.

### Wrapping a Native Function

CsScript provides the QScriptEngine::newFunction() as a way of wrapping a C++ function pointer. This enables you to implement a function in C++ and add it to the script environment, so that scripts can invoke your function as if it were a "normal" script function. Here is how the previous getProperty() function can be written in C++:

QScriptValue getProperty(QScriptContext *ctx, QScriptEngine *eng)
{
QString name = ctx->argument(0).toString();
return ctx->thisObject().property(name);
}

Call QScriptEngine::newFunction() to wrap the function. This will produce a special type of function object that carries a pointer to the C++ function internally. Once the resulting wrapper has been added to the scripting environment (e.g., by setting it as a property of the Global Object), scripts can call the function without having to know nor care that it is, in fact, a native function.

Note that the name of the C++ function does not matter in the scripting sense; the name by which the function is invoked by scripts depends only on what you call the script object property in which you store the function wrapper.

It is currently not possible to wrap member functions; i.e., methods of a C++ class that require a this object.

### The QScriptContext Object

A QScriptContext holds all the state associated with a particular invocation of your function. Through the QScriptContext, you can:

• Get the arguments that were passed to the function.
• Get the this object.
• Find out whether the function was called with the new operator (the significance of this will be explained later).
• Throw a script error.
• Get the function object that's being invoked.
• Get the activation object (the object used to hold local variables).

The following sections explain how to make use of this functionality.

### Processing Function Arguments

Two things are worth noting about function arguments:

1. Any script function — including native functions — can be invoked with any number of arguments. This means that it is up to the function itself to check the argument count if necessary, and act accordingly (e.g., throw an error if the number of arguments is too large, or prepare a default value if the number is too small).
2. A value of any type can be supplied as an argument to any function. This means that it is up to you to check the type of the arguments if necessary, and act accordingly (e.g., throw an error if an argument is not an object of a certain type).

In summary, CopperSpice Script does not automatically enforce any constraints on the number or type of arguments involved in a function call.

### Formal Parameters and the Arguments Object

A native CopperSpice Script function is analogous to a script function that defines no formal parameters and only uses the built-in arguments variable to process its arguments. To see this, let's first consider how a script would normally define an add() function that takes two arguments, adds them together and returns the result:

return a + b;
}

When a script function is defined with formal parameters, their names can be viewed as mere aliases of properties of the arguments object; for example, in the add(a, b) definition's function body, a and arguments[0] refer to the same variable. This means that the add() function can equivalently be written like this:

return arguments[0] + arguments[1];
}

This latter form closely matches what a native implementation typically looks like:

{
double a = ctx->argument(0).toNumber();
double b = ctx->argument(1).toNumber();
return a + b;
}

### Checking the Number of Arguments

Again, remember that the presence (or lack) of formal parameter names in a function definition does not affect how the function may be invoked; add(1, 2, 3) is allowed by the engine, as is add(42). In the case of the add() function, the function really needs two arguments in order to do something useful. This can be expressed by the script definition as follows:

if (arguments.length != 2) {
throw Error("add() takes exactly two arguments");
}
return arguments[0] + arguments[1];
}

This would result in an error being thrown if a script invokes add() with anything other than two arguments. The native function can be modified to perform the same check:

{
if (ctx->argumentCount() != 2) {
return ctx->throwError("add() takes exactly two arguments");
}
double a = ctx->argument(0).toNumber();
double b = ctx->argument(1).toNumber();
return a + b;
}

### Checking the Types of Arguments

In addition to expecting a certain number of arguments, a function might expect that those arguments are of certain types (e.g., that the first argument is a number and that the second is a string). Such a function should explicitly check the type of arguments and/or perform a conversion, or throw an error if the type of an argument is incompatible.

As it is, the native implementation of add() shown above does not have the exact same semantics as the script counterpart; this is because the behavior of the CopperSpice Script + operator depends on the types of its operands (for example, if one of the operands is a string, string concatenation is performed). To give the script function stricter semantics (namely, that it should only add numeric operands), the argument types can be tested:

if (arguments.length != 2)
throw Error("add() takes exactly two arguments");
if (typeof arguments[0] != "number")
throw TypeError("add(): first argument is not a number");
if (typeof arguments[1] != "number")
throw TypeError("add(): second argument is not a number");
return arguments[0] + arguments[1];
}

Then an invocation like add("foo", new Array()) will cause an error to be thrown.

The C++ version can call QScriptValue::isNumber() to perform similar tests:

{
if (ctx->argumentCount() != 2)
return ctx->throwError("add() takes exactly two arguments");
if (!ctx->argument(0).isNumber())
return ctx->throwError(QScriptContext::TypeError, "add(): first argument is not a number");
if (!ctx->argument(1).isNumber())
return ctx->throwError(QScriptContext::TypeError, "add(): second argument is not a number");
double a = ctx->argument(0).toNumber();
double b = ctx->argument(1).toNumber();
return a + b;
}

A less strict script implementation might settle for performing an explicit to-number conversion before applying the + operator:

if (arguments.length != 2)
throw Error("add() takes exactly two arguments");
return Number(arguments[0]) + Number(arguments[1]);
}

In a native implementation, this is equivalent to calling QScriptValue::toNumber() without performing any type test first, since QScriptValue::toNumber() will automatically perform a type conversion if necessary.

To check if an argument is of a certain object type (class), scripts can use the instanceof operator (e.g., "arguments[0] instanceof Array" evaluates to true if the first argument is an Array object); native functions can call QScriptValue::instanceOf().

To check if an argument is of a custom C++ type, you typically use qscriptvalue_cast() and check if the result is valid. For object types, this means casting to a pointer and checking if it is non-zero; for value types, the class should have an isNull(), isValid() or similar method. Alternatively, since most custom types are transported in QVariants, you can check if the script value is a QVariant using QScriptValue::isVariant(), and then check if the QVariant can be converted to your type using QVariant::canConvert().

### Functions with Variable Numbers of Arguments

Because of the presence of the built-in arguments object, implementing functions that take a variable number of arguments is simple. In fact, as we have seen, in the technical sense all CopperSpice Script functions can be seen as variable-argument functions. As an example, consider a concat() function that takes an arbitrary number of arguments, converts the arguments to their string representation and concatenates the results; for example, concat("CopperSpice", " ", "Script ", 101) would return "CopperSpice Script 101". A script definition of concat() might look like this:

function concat() {
var result = "";
for (var i = 0; i < arguments.length; ++i)
result += String(arguments[i]);
return result;
}

Here is an equivalent native implementation:

{
QString result = "";
for (int i = 0; i < ctx->argumentCount(); ++i)
result += ctx->argument(i).toString();
return result;
}

A second use case for a variable number of arguments is to implement optional arguments. Here is how a script definition typically does it:

function sort(comparefn) {
if (comparefn == undefined)
comparefn = fn; /* replace fn with the built-in comparison function */
else if (typeof comparefn != "function")
throw TypeError("sort(): argument must be a function");
// ...
}

And here's the native equivalent:

{
QScriptValue comparefn = ctx->argument(0);
if (comparefn.isUndefined())
comparefn = /* the built-in comparison function */;
else if (!comparefn.isFunction())
return ctx->throwError(QScriptContext::TypeError, "sort(): argument is not a function");
...
}

A third use case for a variable number of arguments is to simulate C++ overloads. This involves checking the number of arguments and/or their type at the beginning of the function body (as already shown), and acting accordingly. It might be worth thinking twice before doing this, and instead favor unique function names; e.g., having separate processNumber(number) and processString(string) functions rather than a generic process(anything) function. On the caller side, this makes it harder for scripts to accidentally call the wrong overload (since they do not know or do not comprehend your custom sophisticated overloading resolution rules), and on the callee side, you avoid the need for potentially complex (read: error-prone) checks to resolve ambiguity.

### Accessing the Arguments Object

Most native functions use the QScriptContext::argument() function to access function arguments. However, it is also possible to access the built-in arguments object itself (the one referred to by the arguments variable in script code), by calling the QScriptContext::argumentsObject() function. This has three principal applications:

• The arguments object can be used to easily forward a function call to another function. In script code, this is what it typically looks like:

function foo() {
// Let bar() take care of this.
print("calling bar() with " + arguments.length + "arguments");
var result = bar.apply(this, arguments);
print("bar() returned" + result);
return result;
}

For example, foo(10, 20, 30) would result in the foo() function executing the equivalent of bar(10, 20, 30). This is useful if you want to perform some special pre- or post-processing when calling a function (e.g., to log the call to bar() without having to modify the bar() function itself, like the above example), or if you want to call a "base implementation" from a prototype function that has the exact same "signature". In C++, the forwarding function might look like this:

{
QScriptValue bar = eng->globalObject().property("bar");
QScriptValue arguments = ctx->argumentsObject();
qDebug() << "calling bar() with" << arguments.property("length").toInt32() << "arguments";
QScriptValue result = bar.apply(ctx->thisObject(), arguments);
qDebug() << "bar() returned" << result.toString();
return result;
}
• The arguments object can serve as input to a QScriptValueIterator, providing a generic way to iterate over the arguments. A debugger might use this to display the arguments object in a general purpose "CsScript Object Explorer", for example.
• The arguments object can be serialized (e.g., with JSON) and transferred to another entity (e.g., a script engine running in another thread), where the object can be deserialized and passed as argument to another script function.

### Constructor Functions

Some script functions are constructors; they are expected to initialize new objects. The following snippet is a small example:

function Book(isbn) {
this.isbn = isbn;
}
var coolBook1 = new Book("978-0131872493");
var coolBook2 = new Book("978-1593271473");

There is nothing special about constructor functions. In fact, any script function can act as a constructor function (i.e., any function can serve as the operand to new). Some functions behave differently depending on whether they are called as part of a new expression or not; for example, the expression new Number(1) will create a Number object, whereas Number("123") will perform a type conversion. Other functions, like Array(), will always create and initialize a new object (e.g., new Array() and Array() have the same effect).

A native CopperSpice Script function can call the QScriptContext::isCalledAsConstructor() function to determine if it is being called as a constructor or as a regular function. When a function is called as a constructor (i.e., it is the operand in a new expression), this has two important implications:

• The this object, QScriptContext::thisObject(), contains the new object to be initialized; the engine creates this new object automatically before invoking your function. This means that your native constructor function normally does not have to (and should not) create a new object when it is called as a constructor, since the engine has already prepared a new object. Instead your function should operate on the supplied this object.
• The constructor function should return an undefined value, QScriptEngine::undefinedValue(), to tell the engine that the this object should be the final result of the new operator. Alternatively, the function can return the this object itself.

When QScriptContext::isCalledAsConstructor() returns false, how your constructor handles this case depends on what behavior you desire. If, like the built-in Number() function, a plain function call should perform a type conversion of its argument, then you perform the conversion and return the result. If, on the other hand, you want your constructor to behave as if it was called as a constructor (with new), you have to explicitly create a new object (that is, ignore the this object), initialize that object, and return it.

The following example implements a constructor function that always creates and initializes a new object:

QScriptValue Person_ctor(QScriptContext *ctx, QScriptEngine *eng)
{
QScriptValue object;
if (ctx->isCalledAsConstructor()) {
object = ctx->thisObject();
} else {
object = eng->newObject();
object.setPrototype(ctx->callee().property("prototype"));
}
object.setProperty("name", ctx->argument(0));
return object;
}

Given this constructor, scripts would be able to use either the expression new Person("Bob") or Person("Bob") to create a new Person object; both behave in the same way.

There is no equivalent way for a function defined in script code to determine whether or not it was invoked as a constructor.

Note that, even though it is not considered good practice, there is nothing that stops you from choosing to ignore the default constructed (this) object when your function is called as a constructor and creating your own object anyway; simply have the constructor return that object. The object will "override" the default object that the engine constructed (i.e., the default object will simply be discarded internally).

### Associating Data with a Function

Even if a function is global — i.e., not associated with any particular (type of) object — you might still want to associate some data with it, so that it becomes self-contained; for example, the function could have a pointer to some C++ resource that it needs to access. If your application only uses a single script engine, or the same C++ resource can/should be shared among all script engines, you can simply use a static C++ variable and access it from within the native CopperSpice Script function.

In the case where a static C++ variable or singleton class is not appropriate, you can call QScriptValue::setProperty() on the function object, but be aware that those properties will also be accessible to script code. The alternative is to use QScriptValue::setData(); this data is not script-accessible. The implementation can access this internal data through the QScriptContext::callee() function, which returns the function object being invoked. The following example shows how this might be used:

{
QRectF magicRect = qscriptvalue_cast<QRectF>(ctx->callee().data());
QRectF sourceRect = qscriptvalue_cast<QRectF>(ctx->argument(0));
return eng->toScriptValue(sourceRect.intersected(magicRect));
}
...
QScriptValue fun = eng.newFunction(rectifier);
QRectF magicRect = QRectF(10, 20, 30, 40);
fun.setData(eng.toScriptValue(magicRect));
eng.globalObject().setProperty("rectifier", fun);

### Native Functions as Arguments to Functions

As previously mentioned, a function object can be passed as argument to another function; this is also true for native functions, naturally. As an example, here's a native comparison function that compares its two arguments numerically:

{
double first = ctx->argument(0).toNumber();
double second = ctx->argument(1).toNumber();
int result;
if (first == second)
result = 0;
else if (first < second)
result = -1;
else
result = 1;
return result;
}

The above function can be passed as argument to the standard Array.prototype.sort function to sort an array numerically, as the following C++ code illustrates:

QScriptValue comparefn = eng.newFunction(myCompare);
QScriptValue array = eng.evaluate("new Array(10, 5, 20, 15, 30)");
array.property("sort").call(array, QScriptValueList() << comparefn);
// prints "5,10,15,20,30"
qDebug() << array.toString();

Note that, in this case, we are truly treating the native function object as a value — i.e., we do not store it as a property of the scripting environment — we simply pass it on as an "anonymous" argument to another script function and then forget about it.

### The Activation Object

Every CopperSpice Script function invocation has an activation object associated with it; this object is accessible through the QScriptContext::activationObject() function. The activation object is a script object whose properties are the local variables associated with the invocation (including the arguments for which the script function has a corresponding formal parameter name). Thus, getting, modifying, creating and deleting local variables from C++ is done using the regular QScriptValue::property() and QScriptValue::setProperty() functions. The activation object itself is not directly accessible from script code (but it is implicitly accessed whenever a local variable is read from or written to).

For C++ code, there are two principal applications of the activation object:

• The activation object provides a standard way to traverse the variables associated with a function call, by using it as the input to QScriptValueIterator. This is useful for debugging purposes.
• The activation object can be used to prepare local variables that should be available when a script is evaluated inline; this can be viewed as a way of passing arguments to the script itself. This technique is typically used in conjunction with QScriptEngine::pushContext(), as in the following example:

act.setProperty("digit", 7);
qDebug() << eng.evaluate("digit + 1").toNumber(); // 8
eng.popContext();

We create a temporary execution context, create a local variable for it, evaluate the script, and finally restore the old context.

### Property Getters and Setters

A script object property can be defined in terms of a getter/setter function, similar to how a CopperSpice C++ property has read and write functions associated with it. This makes it possible for a script to use expressions like object.x instead of object.getX(); the getter/setter function for x will implicitly be invoked whenever the property is accessed. To scripts, the property looks and behaves just like a regular object property.

A single CopperSpice Script function can act as both getter and setter for a property. When it is called as a getter, the argument count is 0. When it is called as a setter, the argument count is 1; the argument is the new value of the property. In the following example, we define a native combined getter/setter that transforms the value slightly:

{
QScriptValue obj = ctx->thisObject();
QScriptValue data = obj.data();
if (!data.isValid()) {
data = eng->newObject();
obj.setData(data);
}
QScriptValue result;
if (ctx->argumentCount() == 1) {
QString str = ctx->argument(0).toString();
str.replace("Roberta", "Ken");
result = str;
data.setProperty("x", result);
} else {
result = data.property("x");
}
return result;
}

The example uses the internal data of the object to store and retrieve the transformed value. Alternatively, the property could be stored in another, "hidden" property of the object itself (e.g., __x__). A native function is free to implement whatever storage scheme it wants, as long as the external behavior of the property itself is consistent (e.g., that scripts should not be able to distinguish it from a regular property).

The following C++ code shows how an object property can be defined in terms of the native getter/setter:

QScriptValue obj = eng.newObject();
obj.setProperty("x", eng.newFunction(getSet), QScriptValue::PropertyGetter|QScriptValue::PropertySetter);

When the property is accessed, like in the following script, the getter/setter does its job behind the scenes:

obj.x = "Roberta sent me";
print(obj.x); // "Ken sent me"
obj.x = "I sent the bill to Roberta";
print(obj.x); // "I sent the bill to Ken"

It is important that the setter function, not just the getter, returns the value of the property; i.e., the setter should not return QScriptValue::UndefinedValue. This is because the result of the property assignment is the value returned by the setter, and not the right-hand side expression. Also note that you normally should not attempt to read the same property that the getter modifies within the getter itself, since this will cause the getter to be called recursively.

You can remove a property getter/setter by calling QScriptValue::setProperty(), passing an invalid QScriptValue as the getter/setter. Remember to specify the QScriptValue::PropertyGetter and QScriptValue::PropertySetter flag(s), otherwise the only thing that will happen is that the setter will be invoked with an invalid QScriptValue as its argument.

Property getters and setters can be defined and installed by script code as well, as in the following example:

obj = {};
obj.__defineGetter__("x", function() { return this._x; });
obj.__defineSetter__("x", function(v) { print("setting x to", v); this._x = v; });
obj.x = 123;

Getters and setters can only be used to implement a predeclared property. This technique ca not be used to react to an access to a property that the object does not already have. To gain total control of property access in this way, you need to subclass QScriptClass.

### Making Use of Prototype Based Inheritance

In ECMAScript inheritance is based on the concept of shared prototype objects. This is quite different from the class-based inheritance familiar to C++ programmers. With CsScript you can associate a custom prototype object with a C++ type using QScriptEngine::setDefaultPrototype(). Since the CsScript library is built using the CopperSpice meta type system this association can be made for any C++ type.

The question is when does this association need to be done and why is the binding provided by QScriptEngine::newQObject() enough. Not every C++ type is derived from QObject. Types which are not QObjects can not be introspected through the CopperSpice meta object system. If a data type is derived from QObject the functionality you want to expose to scripts still might not be registered in the meta object system since ordinary C++ methods are not declared in a way which is visible as a "slot".

It is possible to solve this problem by using conventional C++ techniques. For instance, the QRect class can be made scriptable by creating a QObject C++ wrapper class with x, y, width properties and so on, which forwarded property access and function calls to the wrapped value. However, by taking advantage of the ECMAScript object model and combining it with the CopperSpice meta object system, we can arrive at a solution that is more elegant, consistent and lightweight, supported by a small API.

The following sections explain the underlying concepts of ECMA script style prototype inheritance.

### Prototype Objects and Shared Properties

The purpose of a CsScript prototype object is to define behavior that should be shared by a set of other CsScript objects. Objects which share the same prototype object belong to the same class. This should not to be confused with the class constructs of languages like C++ and Java, ECMAScript has no such construct.

The basic prototype-based inheritance mechanism works as follows: Each CsScript object has an internal link to another object, its prototype. When a property is looked up in an object, and the object itself does not have the property, the property is looked up in the prototype object instead; if the prototype has the property, then that property is returned. Otherwise, the property is looked up in the prototype of the prototype object, and so on; this chain of objects constitutes a prototype chain. The chain of prototype objects is followed until the property is found or the end of the chain is reached.

For example, when you create a new object by the expression new Object(), the resulting object will have as its prototype the standard Object prototype, Object.prototype; through this prototype relation, the new object inherits a set of properties, including the hasOwnProperty() function and toString() function:

var o = new Object();
o.foo = 123;
print(o.hasOwnProperty('foo')); // true
print(o.hasOwnProperty('bar')); // false
print(o); // calls o.toString(), which returns "[object Object]"

The toString() function itself is not defined in o (since we did not assign anything to o.toString), so instead the toString() function in the standard Object prototype is called, which returns a highly generic string representation of o ("[object Object]").

Note that the properties of the prototype object are not copied to the new object; only a link from the new object to the prototype object is maintained. This means that changes done to the prototype object will immediately be reflected in the behavior of all objects that have the modified object as their prototype.

### Defining Classes in a Prototype-Based Universe

In CsScript a class is not defined explicitly, there is no class keyword. Instead, you define a new class in two steps:

1. Define a constructor function that will initialize new objects.
2. Set up a prototype object that defines the class interface, and assign this object to the public prototype property of the constructor function.

With this arrangement, the constructor's public prototype property will automatically be set as the prototype of objects created by applying the new operator to your constructor function; e.g., the prototype of an object created by new Foo() will be the value of Foo.prototype.

Functions that do not operate on the this object ("static" methods) are typically stored as properties of the constructor function, not as properties of the prototype object. The same is true for constants, such as enum values.

The following code defines a simple constructor function for a class called Person:

function Person(name) {
this.name = name;
}

Next, you want to set up Person.prototype as your prototype object; i.e., define the interface that should be common to all Person objects. CsScript automatically creates a default prototype object (by the expression new Object()) for every script function; you can add properties to this object, or you can assign your own custom object. (Generally speaking, any CsScript object can act as prototype for any other object.)

Here is an example of how you might want to override the toString() function that Person.prototype inherits from Object.prototype, to give your Person objects a more appropriate string representation:

Person.prototype.toString = function() { return "Person(name: " + this.name + ")"; }

This resembles the process of reimplementing a virtual function in C++. Henceforth, when the property named toString is looked up in a Person object, it will be resolved in Person.prototype, not in Object.prototype as before:

var p1 = new Person("John Doe");
var p2 = new Person("G.I. Jane");
print(p1); // "Person(name: John Doe)"
print(p2); // "Person(name: G.I. Jane)"

There are also some other interesting things we can learn about a Person object:

print(p1.hasOwnProperty('name')); // 'name' is an instance variable, so this returns true
print(p1.hasOwnProperty('toString')); // returns false; inherited from prototype
print(p1 instanceof Person); // true
print(p1 instanceof Object); // true

The hasOwnProperty() function is not inherited from Person.prototype, but rather from Object.prototype, which is the prototype of Person.prototype itself; i.e., the prototype chain of Person objects is Person.prototype followed by Object.prototype. This prototype chain establishes a class hierarchy, as demonstrated by applying the instanceof operator; instanceof checks if the value of the public prototype property of the constructor function on the right-hand side is reached by following the prototype chain of the object on the left-hand side.

When defining subclasses, there's a general pattern you can use. The following example shows how one can create a subclass of Person called Employee:

function Employee(name, salary)
{
Person.call(this, name); // call base constructor
this.salary = salary;
}
// set the prototype to be an instance of the base class
Employee.prototype = new Person();
// initialize prototype
Employee.prototype.toString = function() {
// ...
}

Again, you can use the instanceof to verify that the class relationship between Employee and Person has been correctly established:

var e = new Employee("Johnny Bravo", 5000000);
print(e instanceof Employee); // true
print(e instanceof Person); // true
print(e instanceof Object); // true
print(e instanceof Array); // false

This shows that the prototype chain of Employee objects is the same as that of Person objects, but with Employee.prototype added to the front of the chain.

### Prototype-Based Programming with the CsScript C++ API

You can use QScriptEngine::newFunction() to wrap native functions. When implementing a constructor function, you also pass the prototype object as an argument to QScriptEngine::newFunction(). You can call QScriptValue::construct() to call a constructor function, and you can use QScriptValue::call() from within a native constructor function if you need to call a base class constructor.

The QScriptable class provides a convenient way to implement a prototype object in terms of C++ slots and properties. The prototype functionality can be implemented in terms of standalone native functions that you wrap with QScriptEngine::newFunction() and set as properties of your prototype object by calling QScriptValue::setProperty().

In the implementation of your prototype functions, you use QScriptable::thisObject() (or QScriptContext::thisObject()) to obtain a reference to the QScriptValue being operated upon; then you call qscriptvalue_cast() to cast it to your C++ type, and perform the relevant operations using the usual C++ API for the type.

You associate a prototype object with a C++ type by calling QScriptEngine::setDefaultPrototype(). Once this mapping is established, CsScript will automatically assign the correct prototype when a value of such a type is wrapped in a QScriptValue; either when you explicitly call QScriptEngine::toScriptValue(), or when a value of such a type is returned from a C++ slot and internally passed back to script code by the engine. This means you do not have to implement wrapper classes if you use this approach.

As an example, let's consider how the Person class from the preceding section can be implemented in terms of the CopperSpice Script API. We begin with the native constructor function:

QScriptValue Person_ctor(QScriptContext *context, QScriptEngine *engine)
{
QString name = context->argument(0).toString();
context->thisObject().setProperty("name", name);
return engine->undefinedValue();
}

Here is the native equivalent of the Person.prototype.toString function we saw before:

QScriptValue Person_prototype_toString(QScriptContext *context, QScriptEngine *engine)
{
QString name = context->thisObject().property("name").toString();
QString result = QString::fromLatin1("Person(name: %0)").formatArg(name);
return result;
}

The Person class can then be initialized as follows:

QScriptValue ctor = engine.newFunction(Person_ctor);
ctor.property("prototype").setProperty("toString", engine.newFunction(Person_prototype_toString));
QScriptValue global = engine.globalObject();
global.setProperty("Person", ctor);

The implementation of the Employee subclass is similar. We use QScriptValue::call() to call the super-class (Person) constructor:

QScriptValue Employee_ctor(QScriptContext *context, QScriptEngine *engine)
{
QScriptValue super = context->callee().property("prototype").property("constructor");
super.call(context->thisObject(), QScriptValueList() << context->argument(0));
context->thisObject().setProperty("salary", context->argument(1));
return engine->undefinedValue();
}

The Employee class can then be initialized as follows:

QScriptValue empCtor = engine.newFunction(Employee_ctor);
empCtor.setProperty("prototype", global.property("Person").construct());
global.setProperty("Employee", empCtor);

When implementing the prototype object of a class, you may want to use the QScriptable class, as it enables you to define the API of your script class in terms of CopperSpice properties, signals and slots, and automatically handles value conversion between the CopperSpice Script and C++ side.

### Implementing Prototype Objects for Value-based Types

When implementing a prototype object for a value-based type, such as QPointF, the same general technique applies; you populate a prototype object with functionality that should be shared among instances. You then associate the prototype object with the type by calling QScriptEngine::setDefaultPrototype(). This ensures that when e.g. a value of the relevant type is returned from a slot back to the script, the prototype link of the script value will be initialized correctly.

When values of the custom type are stored in QVariants, which CsScript does by default, qscriptvalue_cast() enables you to safely cast the script value to a pointer to the C++ type. This makes it easy to do type-checking, and, for prototype functions that should modify the underlying C++ value, lets you modify the actual value contained in the script value (and not a copy of it).

Q_DECLARE_METATYPE(QPointF)
Q_DECLARE_METATYPE(QPointF*)
QScriptValue QPointF_prototype_x(QScriptContext *context, QScriptEngine *engine)
{
// Since the point is not to be modified, it's OK to cast to a value here
QPointF point = qscriptvalue_cast<QPointF>(context->thisObject());
return point.x();
}
QScriptValue QPointF_prototype_setX(QScriptContext *context, QScriptEngine *engine)
{
// Cast to a pointer to be able to modify the underlying C++ value
QPointF *point = qscriptvalue_cast<QPointF*>(context->thisObject());
if (! point) {
return context->throwError(QScriptContext::TypeError, "QPointF.prototype.setX: this object is not a QPointF");
}
point->setX(context->argument(0).toNumber());
return engine->undefinedValue();
}

### Implementing Constructors for Value-based Types

You can implement a constructor function for a value-based type by wrapping a native factory function. For example, the following function implements a simple constructor for QPoint.

QScriptValue QPoint_ctor(QScriptContext *context, QScriptEngine *engine)
{
int x = context->argument(0).toInt32();
int y = context->argument(1).toInt32();
return engine->toScriptValue(QPoint(x, y));
}
...
engine.globalObject().setProperty("QPoint", engine.newFunction(QPoint_ctor));

In the above code we simplified things a bit, e.g. we did not check the argument count to decide which QPoint C++ constructor to use. In your own constructors you have to do this type of resolution yourself, i.e. by checking the number of arguments passed to the native function, and/or by checking the type of the arguments and converting the arguments to the desired type. If you detect a problem with the arguments you may want to signal this by throwing a script exception; see QScriptContext::throwError().

### Managing Non QObject based Objects

For value-based types (e.g. QPoint), the C++ object will be destroyed when the CsScript object is garbage-collected, so managing the memory of the C++ object is not an issue. For QObjects, CsScript provides several alternatives for managing the underlying C++ object's lifetime. For polymorphic types that do not inherit from QObject, and when you unable to wrap the type in a QObject, you have to manage the lifetime of the C++ object yourself. Refer to the section about Controlling QObject Ownership.

A behavior that is often reasonable when a CopperSpice Script object wraps a C++ object, is that the C++ object is deleted when the CopperSpice Script object is garbage-collected; this is typically the case when the objects can be constructed by scripts, as opposed to the application providing the scripts with pre-made "environment" objects. A way of making the lifetime of the C++ object follow the lifetime of the CopperSpice Script object is by using a shared pointer class, such as QSharedPointer, to hold a pointer to your object; when the CopperSpice Script object containing the QSharedPointer is garbage-collected, the underlying C++ object will be deleted if there are no other references to the object.

The following snippet shows a constructor function that constructs QXmlStreamReader objects that are stored using QSharedPointer:

{
if (!context->isCalledAsConstructor())
return context->throwError(QScriptContext::SyntaxError, "please use the 'new' operator");
QIODevice *device = qobject_cast<QIODevice*>(context->argument(0).toQObject());
if (!device)
return context->throwError(QScriptContext::TypeError, "please supply a QIODevice as the first argument");
// Create the C++ object
// store the shared pointer in the script object that we are constructing
return engine->newVariant(context->thisObject(), QVariant::fromValue(pointer));
}

Prototype functions can use qscriptvalue_cast() to cast the this object to the proper type:

{
return context->throwError(QScriptContext::TypeError, "this object is not an XmlStreamReader");
}

The prototype and constructor objects are set up in the usual way:

Scripts can now construct QXmlStreamReader objects by calling the XmlStreamReader constructor, and when the CopperSpice Script object is garbage-collected (or the script engine is destroyed), the QXmlStreamReader object is destroyed as well.

### Defining Custom Script Classes with QScript Class

There are cases where neither the dynamic QObject binding provided by QScriptEngine::newQObject() or the manual binding provided by QScriptEngine::newFunction() is sufficient. For example, you might want to implement a dynamic script proxy to an underlying object; or you might want to implement an array-like class (i.e. that gives special treatment to properties that are valid array indexes, and to the property "length"). In such cases, you can subclass QScriptClass to achieve the desired behavior.

QScriptClass allows you to handle all property access for a (class of) script object through virtual get/set property functions. Iteration of custom properties is also supported through the QScriptClassPropertyIterator class; this means you can advertise properties to be reported by for-in script statements and QScriptValueIterator.

### Error Handling and Debugging Facilities

Syntax errors in scripts will be reported as soon as a script is evaluated; QScriptEngine::evaluate() will return a SyntaxError object that you can convert to a string to get a description of the error.

The QScriptEngine::uncaughtExceptionBacktrace() function gives you a human-readable backtrace of the last uncaught exception. In order to get useful filename information in backtraces, you should pass proper filenames to QScriptEngine::evaluate() when evaluating your scripts.

Often an exception does not happen at the time the script is evaluated, but at a later time when a function defined by the script is actually executed. For C++ signal handlers, this is tricky; consider the case where the clicked() signal of a button is connected to a script function, and that script function causes a script exception when it is handling the signal. Where is that script exception propagated to?

The solution is to connect to the QScriptEngine::signalHandlerException() signal; this will give you notification when a signal handler causes an exception, so that you can find out what happened and/or recover from it.

QScriptEngineAgent provides an interface for reporting low-level "events" in a script engine, such as when a function is entered or when a new script statement is reached. By subclassing QScriptEngineAgent you can be notified of these events and perform some action, if you want. QScriptEngineAgent itself does not provide any debugging-specific functionality (e.g. setting breakpoints), but it is the basis of tools that do.

The QScriptEngineDebugger class can be embedded into your application.

### Redefining print()

CsScript provides a built-in print() function that can be useful for simple debugging purposes. The built-in print() function writes to standard output. You can redefine the print() function (or add your own function, e.g. debug() or log()) that redirects the text to somewhere else. The following code shows a custom print() that adds text to a QPlainTextEdit.

QScriptValue myPrintFunction(QScriptContext *context, QScriptEngine *engine)
{
QString result;
for (int i = 0; i < context->argumentCount(); ++i) {
if (i > 0)
result.append(" ");
result.append(context->argument(i).toString());
}
QScriptValue calleeData = context->callee().data();
QPlainTextEdit *edit = qobject_cast<QPlainTextEdit*>(calleeData.toQObject());
edit->appendPlainText(result);
return engine->undefinedValue();
}

The following code shows how the custom print() function may be initialized and used.

int main(int argc, char **argv)
{
QApplication app(argc, argv);
QScriptValue fun = eng.newFunction(myPrintFunction);
fun.setData(eng.newQObject(&edit));
eng.globalObject().setProperty("print", fun);
eng.evaluate("print('hello', 'world')");
edit.show();
return app.exec();
}

A pointer to the QPlainTextEdit is stored as an internal property of the script function itself, so that it can be retrieved when the function is called.

### Using CsScript Extensions

The QScriptEngine::importExtension() function can be used to load plugins into a script engine. Plugins typically add some extra functionality to the engine; for example, a plugin might add full bindings for the CopperSpice Arthur painting API, so that those classes may be used from CsScript scripts. There are currently no script plugins shipped with CopperSpice.

If you are implementing some CsScript functionality that you want other CopperSpice application developers to be able to use, refer to the section on developing an extension.

### Internationalization

CsScript supports internationalization of scripts by building on the C++ internationalization functionality. Refer to Internationalization.

### Use qsTr() for All Literal Text

Wherever your script uses "quoted text" for text that will be presented to the user, ensure that it is processed by the QCoreApplication::translate() function. Essentially all that is necessary to achieve this is to use the qsTr() script function. Example:

myButton.text = qsTr("Hello world");

The qsTr() function uses the basename of the script's filename (see QFileInfo::baseName()) as the translation context; if the filename is not unique in your project, you should use the qsTranslate() function and pass a suitable context as the first argument. Example:

myButton.text = qsTranslate("MyAwesomeScript", "Hello world");

If you need to have translatable text completely outside a function, there are two functions to help: QT_TR_NOOP() and QT_TRANSLATE_NOOP(). They merely mark the text for extraction by the lupdate utility described below. At runtime, these functions simply return the text to translate unmodified.

Example of QT_TR_NOOP():

FriendlyConversation.prototype.greeting = function(type)
{
if (FriendlyConversation['greeting_strings'] == undefined) {
FriendlyConversation['greeting_strings'] = [
QT_TR_NOOP("Hello"),
QT_TR_NOOP("Goodbye")
];
}
return qsTr(FriendlyConversation.greeting_strings[type]);
}

Example of QT_TRANSLATE_NOOP():

FriendlyConversation.prototype.greeting = function(type)
{
if (FriendlyConversation['greeting_strings'] == undefined) {
FriendlyConversation['greeting_strings'] = [
QT_TRANSLATE_NOOP("FriendlyConversation", "Hello"),
QT_TRANSLATE_NOOP("FriendlyConversation", "Goodbye")
];
}
return qsTranslate("FriendlyConversation", FriendlyConversation.greeting_strings[type]);
}

### Dynamic Text

The String.prototype.arg() function, which is modeled after QString::formatArg(), offers a simple means for substituting arguments.

FileCopier.prototype.showProgress = function(done, total, currentFileName)
{
this.label.text = qsTr("%1 of %2 files copied.\nCopying: %3")
.arg(done).arg(total).arg(currentFileName);
}

### Produce Translations

Once you are using qsTr() and/or qsTranslate() throughout your scripts, you can start producing translations of the user-visible text in your program.

The Linguist documentation provides additional information about the CopperSpice translation tools, the Linguist application, lupdate and lrelease.

Translation of a CsScript script is a three-step process:

1. Run lupdate to extract translatable text from the script source code of the CopperSpice application, resulting in a message file for translators (a TS file). The utility recognizes qsTr(), qsTranslate() and the QT_TR*_NOOP() functions described above and produces TS files (usually one per language).
2. Provide translations for the source texts in the TS file, using the Linguist application. Since TS files are in XML format, you can also edit them by hand.
3. Run lrelease to obtain a light-weight message file (a QM file) from the TS file, suitable only for end use. Think of the TS files as "source files", and QM files as "object files". The translator edits the TS files, but the users of your application only need the QM files. Both kinds of files are platform and locale independent.

Typically, you will repeat these steps for every release of your application. The lupdate utility does its best to reuse the translations from previous releases.

When running lupdate, you must specify the location of the script(s), and the name of the TS file to produce. Examples:

lupdate myscript.qs -ts myscript_la.ts

will extract translatable text from myscript.qs and create the translation file myscript_la.qs.

lupdate -extensions qs scripts/ -ts scripts_la.ts

will extract translatable text from all files ending with .qs in the scripts folder and create the translation file scripts_la.qs.

lrelease myscript_la.ts

When running lrelease you must specify the name of the TS input file. lrelease will create myscript_la.qm, the binary representation of the translation.

### Apply Translations

In your application you must use QTranslator::load() to load the translation files appropriate for the user's language, and install them using QCoreApplication::installTranslator(). Finally, you must call QScriptEngine::installTranslatorFunctions() to make the script translation functions (qsTr(), qsTranslate() and QT_TR*_NOOP()) available to scripts that are subsequently evaluated by QScriptEngine::evaluate(). For scripts that are using the qsTr() function, the proper filename must be passed as second argument to QScriptEngine::evaluate().

The Linguist application, lupdate and lrelease are installed in the bin subdirectory of the base directory CopperSpice is installed into.

### ECMAScript Compatibility

CsScript implements all the built-in objects and properties defined in the ECMA-262 standard. Refer to ECMAScript reference for an overview.

### CsScript Extensions to ECMAScript

• __proto__
The prototype of an object (QScriptValue::prototype()) can be accessed through its __proto__ property in script code. This property has the QScriptValue::Undeletable flag set. For example:

var o = new Object();
(o.__proto__ === Object.prototype); // this evaluates to true
• Object.prototype.__defineGetter__
This function installs a getter function for a property of an object. The first argument is the property name, and the second is the function to call to get the value of that property. When the function is invoked, the this object will be the object whose property is accessed. For example:
• Object.prototype.__defineSetter__
This function installs a setter function for a property of an object. The first argument is the property name, and the second is the function to call to set the value of that property. When the function is invoked, the this object will be the object whose property is accessed. For example:
• Function.prototype.connect
This function connects a signal to a slot. Usage of this function is described in the section Using Signals and Slots.
• Function.prototype.disconnect
This function disconnects a signal from a slot. Usage of this function is described in the section Using Signals and Slots.
• QObject.prototype.findChild
This function is semantically equivalent to QObject::findChild().
• QObject.prototype.findChildren
This function is semantically equivalent to QObject::findChildren().
• QObject.prototype.toString
This function returns a default string representation of a QObject.
• gc
This function invokes the garbage collector.
• Error.prototype.backtrace
This function returns a human-readable backtrace, in the form of an array of strings.
• Error objects have the following additional properties:
• lineNumber: The line number where the error occurred.
• fileName: The file name where the error occurred (if a file name was passed to QScriptEngine::evaluate()).
• stack: An array of objects describing the stack. Each object has the following properties:
• functionName: The function name, if available.
• fileName: The file name, if available.
• lineNumber: The line number, if available.