# Software Internals

## A Tour of Gnash

The top level of Gnash has several libraries, `libgnashbase`, `libgnashgeo`, `libgnashserver`, `libgasnhasobjs`and `libgnashbackend`. There are two utility programs included for debug parsing and processing of Flash movie files to test the Actionscript interpreter. There is also a standalone flash movie player.

### The Libraries

#### libgnashbase

Libgnashbase contains support classes used by the rest of the code. Among these classes is a small and efficient STL library clone that uses smart pointers. A current goal is to replace this small STL clone with standard STL containers to reduce the amount of code that has to be maintained, and to add functionality not existing in the current smaller implementation.

Gnash makes heavy use of smart pointers, so memory allocations are freed up automatically by the interpreter.

#### libgnashgeo

Libgnashgeo contains code for device independent graphics routines.

#### libgnashserver

Libgnashserver is the guts of the interpreter itself. This is where the main code for the interpreter lives.

#### libgnashasobjs

Libgnashasobjs contains all the ActionScript classes used by the interpreter.

#### libgnashamf

AMF is the data format used internally by Flash. This is Gnash's support library to handle AMF data. This is currently unused, but when the LocalConnection class is more fully implemented, this will be used to transfer data between flash movies.

#### libgnashbackend

Libgnashbackend is a small library containing OpenGL and SDL code that glues this display to the Gnash display.

#### libgnashplugin

Libgnashplugin is the Mozilla/Firefox plugin.

#### libklashpart

Libklashpart is the Konqueror plugin.

### The Applications

There are currently a few standalone programs in Gnash to assist with Gnash development, and to play a flash movie.

#### The Standalone Player

This is the standalone OpenGL back-end used to play movies. There are several command line options and keyboard control keys used by Gnash that are documented here.

#### Gparser

Gparser use the Gnash parser to disassemble the flash movie, and dumps the object types, the depth, and other information to make sure Gnash is parsing the file correctly.

#### Gprocesser

Gprocesser is used to print out the actions (using the -va option) or the parsing (using the -vp option) of a flash movie. It is also used to produce the `.gsc` files that Gnash uses to cache data, thereby speeding up the loading of files.

### The Plugin

The plugin is designed to work within Mozilla or Firefox, although there is Konqueror support as well. The plugin uses the Mozilla NSPR plugin API to be cross platform, and portable, as well as being well integrated into Mozilla based browsers.

One future thought for the plugin is to use the new Firefox 1.5 or greater version of Firefox. This version has added a drawable canvas window that supports hardware acceleration, and is designed to support things like rendering directly into the canvas without needing OpenGL.

#### Current Status

As of March 30, 2006, the plugin works! This works in a fashion similar to MozPlugger in that the standalone player is used instead of using a thread. This gets around the issue of having to maintain a seperate player to support the plugin. It also gets around the other issues that Gnash itself is not thread safe at this time.

There are a few limitations in the current implementation, but it works well enough to be used for web surfing. The main limitations are the SDL version has no event handling, and sound doesn't work yet.

#### GUI Support

Any plugin that wants to display in a browser window needs to be tied into the windowing system of the platform being used. On GNU/Linux systems, Firefox is a GTK2+ application. There is also KDE support through the use of the Klash plugin.

Gnash can use either SDL or GTK now to create the window, and to handle events for the standalone player. Work is underway to add a portable interface for more windowing toolkits to allow better embedded device support when running in framebuffer only devices.

The SDL version is more limited, but runs on all platforms, including win32. It has no support for event handling, which means mouse clicks, keyboard presses, and window resizing doesn't work. I personally find the default event handler slow and unresponsive. Gnash has support to use fast events, (currently not enabled) which is an SDL hack using a background thread to pump events into the SDL event queue at a much higher rate.

There are a variety of development libraries that build a GUI widget system on top of SDL and OpenGL. The use of these to add menus and dialog boxes to the SDL version is being considered.

The GTK support is currently the most functional, and the best integrated into Firefox. The performance of this version is better than the SDL version because of the more efficient event handling within GTK. For the best end user experience, use the GTK enabled version.

GTK also allows Gnash to have menus and dialog boxes. Currently this is only being utilized in a limited fashion for now. There is a right mouse button menu that allows the user to control the movie being player the same way the existing keyboard commands do.

#### Mozplugger

Mozplugger. is a `Mozilla/Firefox` plugin that uses external programs to play video, audio, and other multimedia content in the browser. With some support added to the external application, it's possible to force the external program to use the internal window in the browser where this plugin is supposed to display. This enables one to then run the standalone player and display it's output in the browser.

While this is not an optimal solution, it does enable one to use Gnash as the flash player when browsing. The main issue appears to be that the Flash movie being played doesn't get any mouse or keyboard input. That may be a mozplugger configuration issue though.

Use of MozPlugger is obsolete now that the Gnash plugin works. Still this may be useful still on some platforms.

Add this to your `$(HOME)/.mozilla/mozpluggerrc` file to enable this:

    	    application/x-shockwave-flash:swf:Shockwave Gnash
            nokill embed noisy ignore_errors hidden fill swallow(Gnash) loop: gnash -v "$file" -x $window
            : gnash -v "$file" -x $window

Once this is added, you must delete the `$(HOME)/.mozilla/firefox/pluginreg.dat` file to force Firefox to register the plugins again. This is an ascii text file, so if the patch has been added correctly, you'll see an entry for `swf` files after it is recreated. You will need to restart Firefox to recreate this file.

This file is not recreated immediately when restarting Firefox, but waits till the first time a plugin is used. You can force creation of this file by typing `about:plugins` into the URL entry of the browser window. The output will also contain information about the mozplugger. You should see an entry for Gnash now.

#### Klash

Klash is MozPlugger type support for KDE's Konqueror web browser. Klash makes Gnash a `kpart`, so it's integrated into KDE better than when using MozPlugger. Klash uses the standalone player, utilizing Gnash's "-x" window plugin command line option.

By default, Klash is not built. To enable building Klash, use the `--enable-klash` option when configuring. Other than installing, there is nothing else that needs to be done to install Klash.

&plugin;

&logging;

## ActionScript Support

### Adding New ActionScript Classes

Adding a new ActionScript class is a relatively simple process. A new file is created to hold the code, with an associated header file. The file name is usually the name of the ActionScript class itself, something like `XML`. All implementations are written in `C++`. In the CVS source tree for Gnash, there is a utility file called `gen-files.sh` that can be used to generate a template for a new ActionScript class. At this time templates have been generated for all documented ActionScript classes.

#### Defining a new ActionScript Class

The header file defines the class and it's methods. The symbol name used to look up the code that implements the ActionScript class is added later.

Each class needs an associated version that is a derived form of the `as_object` class used to internally represent objects in Gnash. At it's simplest, this structure just encapsulates an object of the desired class.

    	    class Foo {
    	        public:
    		    foo() {};
    		    ~foo() {};
    		    bool GetBar() { return _bar; }
    		private:
    		    bool _bar;
    	    }
    	    struct foo_as_object : public gnash::as_object {
    	        Foo obj;
    	    }

The `obj` is an instantiation of the data for this object. It isn't required unless this object needs internal data that has to stay resident in the player.

Whenever this object is being handled the code for this class, it is initially accessed by it's derived binding. Internal data for this object can be accessed through the `obj`.

    	    foo_as_object *foo = env.top(0).to_object();
    	    bool result = foo->obj.GetBar();

A more complex example might be to add hooks to the constructor an destructor for the class to keep track of memory allocations and cleanup. In this case only a debug statement is printed.

    	    struct foo_as_object : public gnash::as_object {
    	        Foo obj;
    	        foo_as_object() {
    	            log_msg("\tCreating foo_as_object at %p \n", this);
    	        };
    	        ~foo_as_object() {
    	            log_msg("\tDeleting foo_as_object at %p \n", this);
    	        };
    	    }:

An even more complex example might be to add hooks to how the list of member of a class is kept. The element `m_members`, is a list of each symbol name and it's associated code. Normally this is kept internally within the interpreter engine, but in this example for certain methods we want to return a point to itself, instead of getting the data from the list.

    	    struct xml_as_object : public gnash::as_object {
    	        XML obj;
    	        xmlnode_as_object() {
    	            log_msg("\tCreating xmlnode_as_object at %p \n", this);
    	        };
    	        ~xmlnode_as_object() {
    	            log_msg("\tDeleting xmlnode_as_object at %p \n", this);
    	        };
    	        virtual bool get_member(const tu_stringi& name, as_value* val) {
    	            if ((name == "firstChild") || (name == "childNodes")) {
    	                val->set_as_object_interface(this);
    		        return true;
    	            }
    		    if (m_members.get(name, val) == false) {
    		        if (m_prototype != NULL) {
    		            return m_prototype->get_member(name, val);
    	                }
    		        return false;
    		    }
    	          return true;
    	      }
    	  };

#### Instantiating a new Class

To add a new object to the list maintained by Gnash, it needs to be added to the function `gnash::action_init()`, in `action.cpp`. The symbol name is specified along with a function pointer that will be called when the symbol name is seen by the interpreter.

    	    obj->set_member("XML", as_value(xml_new));

The function used to instantiate a new object is passed the creation data in a `fn_call` data structure. This is used to pass data both into and returned from this function.

The `fn_call` data structure has several methods for operating on the data for the function. `fn_call::nargs` is a variable that soecifies how many arguments are being passed in. Al the arguments are on a stack. To pop an argument off the stack, use `fn.env->top(0)`. In this case popping the first argument off the stack.

The object popped off the stack also has it's own methods. The main one of interest is `get_type`. This returns the type of the object being referred to.

    	  if (fn.env->top(0).get_type() == as_value::STRING) {
    	     ...
    	  }

The supported data types for an object are `BOOLEAN`, `STRING`, `NUMBER`, `OBJECT`, `C_FUNCTION`, `AS_FUNCTION`. Because they are defined as part of the as_value class, they need to always have the class name prefixed to use these as a constant. You can retrieve the value of an `as_value` using the conversion methods. For example, `to_tu_string` returns the value as string using the Gnash small STL library. Similarly, `to_number` would return this same value as a `double.`

To add methods to the class, a new class needs to be instantiated as an object. Each ActionScript object can have child methods attached in a similar way as the object was. In this case, the built-in `set_member` function is used.

    	    xml_obj = new xml_as_object;
    	    xml_obj->set_member("load", &xml_load);

To make the object active within the interpreter, the new object gets returned by the function using the `fn_call` typed parameter.

    	    fn.result->set_as_object_interface(xml_obj);

A complete example of a function used to instantiate a new ActionScript object is as follows. This example also calls internally defined methods in the class, in this case to process and XML file, and create the parsed XML tree.

    	    void
    	    xml_new(const fn_call& fn) {
    	        as_value      inum;
    	        xml_as_object *xml_obj;

    	        if (fn.nargs > 0) {
    	            if (fn.env->top(0).get_type() == as_value::STRING) {
    	                xml_obj = new xml_as_object;
    		        tu_string datain = fn.env->top(0).to_tu_string();
    		        xml_obj->obj.parseXML(datain);
    		        xml_obj->obj.setupFrame(xml_obj, xml_obj->obj.firstChild(), true);
    	            } else {
    	                xml_as_object *xml_obj = (xml_as_object*)fn.env->top(0).to_object();
    		        fn.result->set_as_object_interface(xml_obj);
    		        return;
    	            }
    	        } else {
    	            xml_obj = new xml_as_object;
    	            xml_obj->set_member("load", &xml_load);
    	            xml_obj->set_member("loaded", &xml_loaded);
    	        }
    	        fn.result->set_as_object_interface(xml_obj);
    	    }

##### Adding a Property

Adding a new new property to an object is similar to adding a callback for a method. Instead of using a C function, a string or number is used.

    	      as_obj->set_member("nodeName", as_value("HelloWorld"));

When this is looked up by a Flash movie as a property, the value is found, instead of needing to make a function call. This scrap of ActionScript code as compiled by Ming's `makeswf` compiler shows the difference.

    	      // Call the hasChildNodes() function
    	      if (node.hasChildNodes() == true) {
    	          trace("CHILDREN");
    	      }
    	      // Get the value of the nodeName property
    	      if (node.nodeName == "HelloWorld") {
    	          trace("MATCHED");
    	      }

### Parameter Passing

Parameters are passed to the callback functions for a class's methods and properties using the `fn_call` data structure. This data structure contains all the incoming parameters for a callback, as well as it contains the final result from the callback to be passed back into the player.

#### Getting Parameter Data

Parameter data is passed on the stack in a similar way to any function call in any language. There are several fields of the `fn_call` data structure that get used in this example:

    	    xml_as_object *xml_obj = (xml_as_object*)fn.this_ptr;
    	    if (fn.nargs) {
    	        filespec = fn.env->bottom(fn.first_arg_bottom_index).to_string;
    	    }

Using the `fn` variable which was passed as the sole parameter of the callback, we can access the data. The `fn.this_ptr` returns a reference to the class that is invoking this method. This is how the object data can be retrieved. The `fn.nargs` is a count of how many objects are being passed into the callback.

The current stack of the player is passed in via the `fn.env` field. This is used to access the data passes to this callback. To find the location of this particular stack frame, the `fn.first_arg_bottom_index` field is used to point to the stack frame. More detail on the environment stack can be found here..

For this example, the stack has a `as_environment::top()` and a `as_environment::bottom()` that are used to pull arguments off the stack. When using `fn.first_arg_bottom_index`, the `as_environment::bottom()` method should be used as in the example.

The top of the stack for this frame can also be accessed using the `as_environment::top()` method. Top takes an integer index as to which value to retrieve,

    	  if (fn.nargs > 0) {
    	   name = fn.env->top(0).to_string());
    	  }

If the type of the object is needed, that can be accessed by using the `as_value::get_type()` method. There are more details on the types of values in the Handling Values section of this manual.

    	  if (fn.nargs > 0) {
    	      if (fn.env->top(0).get_type() == as_value::STRING) {
    	          name = fn.env->top(0).to_string);
    	      }
    	      if (fn.env->top(0).get_type() == as_value::NUMBER) {
    	          value = fn.env->top(0).to_number);
    	      }
    	  }

The internal data for this object can be accessed through the base class. Any data set using this object will stay resident in the player.

    	     foo_as_object *foo_obj = (foo_as_object*)fn.this_ptr;
    	     bool bar = foo_obj->obj.GetBar();

#### Returning Data

Data is also returned in the data structure passed to the callback. This example calls a method of the object passed in on the stack, and then sets the return code to be the return code of calling the method.

    	    // Set the argument to the function event handler based on
    	    // whether the load was successful or failed.
    	    ret = xml_obj->obj.load(filespec);
    	    fn.result->set_bool(ret);

The `result` field of the `fn` variable is a `gnash::as_value` object., so it's possible to set the value of several data types.

Here is a short list of the most often used data types returned from callbacks:

as_value::set_bool()

  Set the result to a boolean value.

as_value::set_int()

  Set the result to an integer value.

as_value::set_double()

  Set the result to a floating point double value.

as_value::set_string()

  Set the result to a `const char*` value.

as_value::set_as_object_interface()

  Set the result to an object value.

### Level of Conformance

Gnash is a capable of reading SWF v7 files and opcodes. Not all ActionScript classes are implemented yet, but all of the most heavily used ones are. Many ActionScript classes are partially implemented, there is support for all of the commonly used methods of each class.

This is a quick list of what is currently implemented in Gnash as of January, 2006.

Class Name | When Added | Conformance
--- | --- | ---
_global |  | Partially implemented.
Accessibility | swf v7 ??? | Unimplemented.
Array | swf v6 | Partially Implemented.
Boolean | swf v5 | Fully implemented.
Button | swf v6 | Most all the functionality is implemented.
Camera | swf v6 | Unimplemented
Color | swf v5 | Unknown
ContextMenu | swf v7 | Unimplemented.
CustomActions | swf v6 | Unimplemented
Date | swf v5 | Partially implemented.
Error | swf v7 | Unimplemented
Function | swf v6 | Unimplemented
Key | swf v6 | Fully implemented.
LoadVars | swf v6 | Unimplemented
LocalConnection | swf v6 | Partially implemented
Math | swf v4 | Fully implemented.
Microphone | swf v6 | Unimplemented
Mouse | swf v5 | Most all the functionality is implemented.
MovieClip | swf v3 | Partially implemented.
MovieClipLoader | swf v7 | Barely implemented.
NetConnection | swf v7 | Partially implemented
NetStream | swf v7 | Unimplemented
Number | swf v5 | Partially implemented
Object | swf v5 | Partially implemented.
Selection | swf v5 | Unimplemented
SharedObject | swf v6 | Unimplemented
Sound | swf v5 | Partially implemented
Stage | swf v6 | Unimplemented
String | swf v5 | Partially implemented
System | swf v6 | Unimplemented
TextField | swf v6 | Partially implemented
TextFormat | swf v6 | Partially implemented
Video | swf v6 | Unimplemented
XML | swf v5 | Almost fully implemented
XMLNode | swf v5 | Almost fully implemented
XMLSocket | swf v5 | Almost fully implemented

&asaccessibility; &asarray; &asboolean; &asbutton; &ascamera; &ascolor; &ascontextmenu; &asmath; &asnetconnection; &asnumber; &asstring; &ascustomactions; &asdate; &aserror; &asfunction; &askey; &asloadvars; &aslocalconnection; &asmicrophone; &asmouse; &asmovieclip; &asmoviecliploader; &asnetstream; &asobject; &asselection; &assharedobject; &assound; &asstage; &assystem; &astextfield; &astextformat; &astextsnapshot; &asvideo; &asxmlnode; &asxml; &asxmlsocket;

&opcodes;

## The Interpreter Engine

FIXME:

### The Main Loop

FIXME:

### I/O Processing

FIXME:

### Handling Values

All of the main values in Gnash as used by the interpreter, are usually an `as_value` class. This is a generic object to hold data. The supported data types for an object are `BOOLEAN`, `STRING`, `NUMBER`, `OBJECT`, `C_FUNCTION`, `AS_FUNCTION`. You can retrieve the value of an `as_value` using the conversion methods. For example, `to_tu_string` returns the value as string using the Gnash small STL library. Similarly, `to_number` would return this same value as a `double.`

`as_value` is often used as the initializer for a property or the data for a callback. This is done so the type of the object is specified along with the data.

    	    // Set the callback for a new XML object
    	    obj->set_member("XML", as_value(xml_new));

    	    // Set the property to the value of text
    	    obj->set_member("nodeName", as_value(text));

    	    // Set the property to null, but at least it exists
    	    obj->set_member("nodeValue", as_value(""));

    	    // Get the name of an object
    	    name = fn.env->top(0).to_string());

    	    // Get the value of an object
    	    value = fn.env->top(1).to_number);

#### as_value set methods

While `as_value` allows you to use any of the supported data types when invoking the constructor (as in the prior example). This is a common way to set the data and type of a value. Often it's necessary to set the value of an object after it is created, or to change the existing value. The `=` operator is also supported, so it is also possible to set a value and it's type this way as well. I sort of lean towards the explicit style of setting a type, so here's all the methods that explicitly set a value.

as_value::set_bool(bool)

  Set the value to a boolean value.

as_value::set_int(int)

  Set the value to an integer value.

as_value::set_double(double)

  Set the value to a floating point double value.

as_value::set_string(const char*)

  Set the value to a `const char*` value.

as_value::set_tu_string(int)

  Set the value to an tu_string value. Once all the containers have been converted to using standard STL classes, this method will go away.

as_value::set_nan(int)

  Set the value to an NaN (Not a Number) value.

as_value::set_null()

  Set the value so this is a `NULL` object.

as_value::set_undefined()

  Set the value so this is an `undefined` object.

as_value::set_as_object_interface(as_object *)

  Set the value to an object value.

as_value::set_as_c_function_ptr(int)

  Set the value to an value.

as_value::set_function_as_object(int)

  Set the value to an value.

#### as_value get methods

as_value::to_bool(bool)

  Return the value as a boolean.

as_value::to_number()

  Return the value as an number object.

as_value::to_string()

  Return the value as a `const char*`.

as_value::to_tu_string(int)

  Return the value as a tu_string value. Once all the containers have been converted to using standard STL classes, this method will go away.

as_value::is_nan()

  Return true if set to NaN (Not a Number).

as_value::is_inf()

  Returns true if the number has an infinite value.

as_value::is_finite()

  Returns true if the number has an finite value.

as_value::to_object()

  Return the value as an `as_object_interface`. This is often used as the "handle" for an object within Gnash. You would use this when you need to do `set_member()` or `get_member()` operations.

as_value::to_c_function()

  Return the value as a C function pointer.

as_value::to_as_function()

  Return the value as an ActionScript function.

### Handling Objects

FIXME:

### The Environment Stack

FIXME:

## Testing Support

### Testing Tools

Currently Gnash uses other three tools to help with testing. Two of these are free compilers for the Flash format. This lets us write simple test cases for Gnash to test specific features, and how they need to operate.

The primary compiler used at this time is Ming. The latest release of `Ming` included the compiler, `makeswf`. This lets test case development happen purely with free tools.

The other tools used, although optional, is DejaGnu. This is used to run multiple test cases in an automated manner. `DejaGnu` is used by many other GNU projects like GCC and Samba.

### Test Cases

Ming-based ActionScript test cases are located under testsuite/actionscript.all/. Other directories under testsuite/ are (or shall be) used for other kind of tests.

Writing Ming-based tests is very simple. The `makeswf` compiler makes use of the C preprocessor, thus allowing definition of macros and external files inclusion. We use these feature to provide common utilities for test units.

Each test unit includes the `check.as` file and checks expressions expected to evaluate to true. Here is an example:

    	  #include "check.as"

    	  // Test object creation
    	  check(new Object() instanceOf Object);

    	  // Test parseInt
    	  check(isNaN(parseInt('none')));

    	  // .. your tests here ...

The check() macro will `trace` PASSED or FAILED together with the expression being evaluated, the filename and the linenumber of the check. This is the format expected by DejaGnu.

Additionally, the check.as file provides a transparent way to send results to a TextField rather then using trace. This is very useful when you happen to run a flash player w/out tracing support.

Test units are built by running `make TestName.swf`. This will use TestName.as as source. To build "visual" tracing version you'd run `make TestName.vswf`.

Note that if you get a syntax error from the compiler, the line number will refer to the pre-processed file. This file is called `TestName.as.pp` and it's not thrown away by `makeswf` to make debugging easier.

### Running The Tests

#### Using DejaGnu

The simple way to run the tests is to install `DejaGnu`, and use that to run the tests. That handles all the details to compile and execute the tests. To run the tests using DejaGnu, change to the `testsuite` directory and type:

    	    make check

You can get more details by adding command line option when invoking `make`. The `make check` target in the Makefile supports a variable, `RUNTESTFLAGS` that gets passed to DejaGnu when it's invoked by `make`.

    	    make check RUNTESTFLAGS="-v -a"

This adds the`verbose (-v)` option and the `all (-a)` option. Verbose prints much more information about how DejaGnu is running the test. It may be too much information, but if you think you are having a problem with running a test case, this is a good way to track it down. The `all` option tells DejaGnu to print all the tests that `PASS`, as well as those that `FAIL`.

#### Manually Running Tests

You can also run all the test cases by hand, which is useful if you want to see all the debug output from the test case. Often the debug message that come deep from within Gnash are the most useful during development.

The first step is to compile the test case. Ming's `makeswf` program is used to compile the test case into Flash. By default, no options are required. Running `makeswf` looks like this:

    	    shellprompt> makeswf XML.as
    	    Output file name: out.swf
    	    Output compression level: 9
    	    Output SWF version: 6
    	    Preprocessing XML.as... done.
    	    Compiling `XML.as.pp' into frame 1... done.
    	    Saving output to out.swf... done.

Once you have the flash movie version of the test case, you can run it through the Gnash standalone player with a few options that enable a simple test cases to be run that has no associated graphics display, and consists only of unit level tests for an ActionScript class.

    	  shellprompt> gprocessor -v out.swf
    	  PASSED: XML::XML() constructor
    	  PASSED: XML::addRequestHeader() exists
    	  PASSED: XML::appendChild() exists
    	  ...