-
Notifications
You must be signed in to change notification settings - Fork 398
Implementing Flash APIs
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.
Mozilla 2019