Skip to content
This repository has been archived by the owner on Jul 3, 2019. It is now read-only.

Implementing Flash APIs

mbebenita edited this page Dec 18, 2012 · 2 revisions

The easiest way to start contributing to Shumway is to implement missing Flash APIs. There are many missing features so finding something to do shouldn't be a problem. Let's take a concrete example, the Sound APIs.

In the Flash IDE click on the first key frame in the timeline and add the following code to the actions window:

var s:Sound = new Sound(new URLRequest("http://.../file.mp3"));
s.play();

This is a very simple .mp3 player. Let's see what happens in Shumway if we run the generated .swf file:

Calling undefined native method: public$flash$media::Sound::private$flash$media$Sound$_load
Calling undefined native method: public$flash$media::Sound::public$play 

This means that somewhere along the execution path of the program the native functions _load and play of the flash.media.Sound class are called and are not implemented. You can find out who calls these functions by setting a breakpoint in runtime.js class where the "Calling undefined native ..." message is printed. You'll notice that _load is called from the load function which in turn is called by the Sound constructor.

The next step is to look at the Sound Class API Reference. Unfortunately, there is no documentation for the native _load function but there is something for load which can give us some hints. This is a typical pattern in the Flash APIs, public AS3 functions wrapping around native functions that are implemented in C/C++. In Shumway we need to implement these native methods in JavaScript. The code for this needs to go in the src/flash/media/Sound.js file which implements the native functions for AS3 Sound class. There is a lot of magic in how the Sound.js file and the AS3 Sound class interact, but you can ignore that for now and use the Shumway Disassembler to generate empty stubs for native methods.

From the avm2/bin directory, run js avm.js -d ../generated/playerGlobal.abc. This will disassemble the playerGlobal.abc file where all the API code is defined. It will also generate stubs you can use to implement native functions. Search for class Sound and you'll see the following generated code:

const SoundDefinition = (function () {
  return {
    // (pA:URLRequest = null, pB:SoundLoaderContext = null)
    initialize: function () {
    }
    __glue__: {
      native: {
        static: {
        },
        instance: {
          // (pA:URLRequest, pB:Boolean, pC:Number) -> void
          _load: function _load(pA, pB, pC) {
            notImplemented("Sound._load");
          }, ..
        }
      }
    }
  };
}).call(this);

This is the JavaScript definition of the Sound class. It doesn't look anything like the JavaScript class pattern. This is because the real class is implemented in ActionScript3 (defined in the playerGlobals.abc) and is compiled dynamically at runtime. The code in Sound.js above patches the native instance method of Sound._load with some JavaScript code. If you run the example again, you'll see a notImplemented exception.

Good, now we can implement the _load function, but how? Well, the first argument to load is an URLRequest which we probably need to download using XHR. If we look into URLRequest we notice there are a bunch of options that need to be taken into consideration. If we hack something in the _load function that simply looks at the .url property and reads the data we may not have a correct implementation. To do a better job, let's look at the URLRequest class documentation. The first comment says the request object is passed to various classes. One of which is the URLStream. Great, we can use the URLStream class to download the .mp3 file.

_load: function _load(pA, pB, pC) {
  var stream = new flash.net.URLStream();
  stream.load(pA);
}

Only problem is, URLStream.load is also native and it's not implemented. So we need to take care of that first. We can create a simple test case for URLStream and start the process all over again.