# Gnash Extensions

Gnash supports extending the SWF specification by creating custom ActionScript classes that are compiled code, as opposed to the existing method of defining custom classes as ActionScript. Executing compiled code has many performance benefits over having to interpret the byte stream of the ActionScript opcodes.

I can already hear people complaining now about the concept of extending SWF, so this in no way affects Gnash's ability to play SWF movies when functioning as a browser plugin. Gnash's goal is still to function in a way that is compatible with the current proprietary Flash player.

But at the same time, we see SWF as the ideal scripting language for a digital multi-media streaming environment. There are many resources for SWF movie creators for widgets, higher level APIs, all sorts of desirable things. But for those of use committed to using free software tools for SWF, our options are very limited.

Rather than launching a multi-year project to duplicate all classes in the commercial Flash IDE, it's much more efficient to use existing development libraries much like Python or Perl do. The extension mechanism in Gnash allows wrappers to be created for any C or C++ development library. Unlike the proprietary Flash IDE, which compiles all the extension libraries into byte codes from ActionScript, the support is moved to the player side. Movies with all of the goodies of the proprietary IDE in them play in Gnash just fine, as it's all just byte codes by then.

This trick works because until SWF version 9, all the ActionScript class names and methods are passed as ASCII strings into the SWF movie. So the Gnash Virtual Machine just loads the extension file if that class name is invoked in the movie. All extension files specify the class name and methods it implements in an identical style as adding any new ActionScript class. The advantage is the class itself is compiled code, and runs much faster than the equivalent byte codes which all have to be interpreted..

## Creating A New Extension

Each new extension should live in it's own directory. The extensions included in Gnash are all in the gnash/extensions directory. Creating an extension requires a Makefile.am,

If you are adding this extension to the Gnash source tree itself, then you need to make two changes to add the new extension.

The first change is to add the directory to the list in extensions/Makefile.am. This can be done either by adding the new directory to the SUBDIRS setting, or by wrapping it in a conditional test.

The other change is to add it to the AC_OUTPUT list in configure.ac so the new directory will be configured along with the rest of Gnash.

Each extension should have an ActionScript source file included that tests the new class, and this file should be referenced in the new Makefile.am in the check_PROGRAMS variable so that "make check" works.

When creating an extension that is a wrapper for an existing development library API, it's often better to make this a thin layer, than to get carried away with creating beautiful abstractions. Higher-level classes that offer a lot of new functionality are fine, but is different than wrapping a library so it can be used from within Gnash.

### Crafting an Extension

All extensions have the same requirements, namely setting up a few defined function callbacks, which the Gnash VM then uses to do the right thing. The initial two function callbacks are for handling the interface of the newly created object so that Gnash can find and use it.

The first function is commonly called attachInterface, and this sets the other function callbacks for all the methods this class supports. The method callbacks are attached to the parent class by using init_member() to set a C function pointer to the string value used in the SWF movie.

    	// Attach DummyClass 'func1' and 'func2' methods to the given object
    	static void
    	attachInterface(as_object& obj) {
                obj.init_member("func1", &ext_func1);
                obj.init_member("func2", &ext_func2);
    	}

The second function is commonly called getInterface(), and this returns a pointer to a static prototype of the class. Gnash uses garbage collection for ActionScript objects so you need to register the static with the VM to give it a chance to be marked as reachable.

    	static as_object*
    	getInterface()
    	{
    	    static boost::intrusive_ptr<as_object> o;
    	    if (o == NULL) {
    	        o = new as_object();
                    VM::get().addStatic(o);
                    attachInterface(*o);
    	    }
    	    return o.get();
    	}

This is the callback that gets executed when constructing a new object for the VM. In this example we'll assume the new ActionScript class is called DummyExt, and has two methods, func1 and func2.

    	static as_value
    	dummyext_ctor(const fn_call& fn)
    	{
    	    DummyExt *obj = new DummyExt(); // will setup prototypes

    	    return as_value(obj);
    	}

The trick for the above simple constructor to work is that class appartenence is setup in the C++ DummyExt constructor itself, which should derive from as_object and construct the base passing it the interface (prototype) of it's class.

    	class DummyExt : public as_object
            {
            public:
    	    DummyExt()
                    :
                    as_object(getInterface()) // returns the static prototype
                {}

    	};

Initialize the extension. This is looked for by the extension handling code in each extension, and is executed when the extension is loaded. This is the main entry point into the extension. This function can be found because the prefix of dummyext, which also matches the file name of the extension. Gnash uses the name of the extension file itself when looking for the init function.

            extern "C" {
    	    void
    	    dummyext_class_init(as_object &obj)
    	    {
    	        static builtin_function* cl=NULL;
    	        if (!cl)
                    {
                        // Create a builtin function using the given constructor
                        // to instanciate objects and exporting the given interface
    	            cl = new builtin_function(&dummyext_ctor, getInterface());
    	            VM::get().addStatic(cl); // will forbid to collect the class
    	        }

    		obj.init_member("DummyExt", cl);
    	    }
            } // end of extern C

The callbacks are all C functions. Like all the other code that implements ActionScript, parameters to the function are passed in using the fn_call data structure. The return code, if any, is also returned using this data structure. this_ptr is the object that the method is a member of.

    	// Creates a new button with the label as the text.
    	as_value func1(const fn_call& fn) {
                // Following line will ensure 'func1' is called for a DummyExt instance,
                // or would throw an exception which should behave as if we returned the
                // undefined value.
                boost::intrusive_ptr<DummyExt> ptr = ensureType<DummyExt>(fn.this_ptr);

    	    if (fn.nargs > 0) {
    	        std::string label = fn.arg(0).to_string();
    		bool ret = ptr->dummy_text_label(label);
    		return as_value(ret);
    	    }
    	}

## Debugging An Extension

As extensions are loaded dynamically at runtime, debugging one can be difficult. You can use GDB, but you have the problem of not being able to set a breakpoint in Gnash until after the extension has been loaded into Gnash's VM. The easy solution is to use the Gnash debugger.

You can insert these few lines in any file that you wish to manually start the debugger. Once at the console, you can attach GDB to the process. Then you can set breakpoints, etc... and you are at the point of execution where the console was started. To then continue playing the movie, type the c (for continue) command to the Gnash console.

          // Get the debugger instance
          static Debugger& debugger = Debugger::getDefaultInstance();

          // Enable the debugger
          debugger.enabled(true);
          // Stop and drop into a console
          debugger.console();

You can also resort to the time honored technique of creating a loop before the point you want to set a breakpoint for. Gnash will stop playing the movie at this point, and then you can externally attach GDB to the running process, or type ^C to drop into the GDB command console.

          bool stall = true;
          while (stall) {
              sleep 1;
          }

Once you have set the breakpoints you want, reset the value of the stall variable to break out of the loop, and the SWF movie will then continue playing.

          (gdb) set variable stall = false;
          continue

## Included Extensions

Gnash has some extensions included in the distribution. This is mostly because they were written by the Gnash team. Extensions can be external to gnash, Gnash needs no compiled in knowledge to load an extension file.

&gtkext; &fileext; &mysqlext;