Reference Guide
This reference guide contains detailed information about all aspects of the Crossbridge toolchain. The sections are laid out roughly in the order in which it makes most sense to learn about, but it is probably a good idea to have one or two of the samples open while reading this through so you can see how things work in practice as well.
- Compiling With GCC
- Executable Formats
- Using Crossbridge With Existing Codebases
- Building SWCs
- Crossbridge-Specific GCC Command Line Options
- Crossbridge-Specific Preprocessor Macros
- Optimizing SWFs and SWCs
- Behind The Scenes: LLVM Bitcode
- Obfuscation
- Other Tools
- Interop between C/C++ and ActionScript
- Flash++
- Implementing a Console
- Implementing a Preloader
- Implementing a Virtual File System
- Embedded / Zipped VFS
- HTTP VFS
- LSO VFS
- The VFS API
- The IKernel Interface
- Concurrency in Flash Player 11.5
- APIs unsupported in background Workers
- APIs with behavioral changes in background Workers
- Creating SWCs with SWIG
- The SWIG Workflow
- Creating a SWIG Interface File
- C++ Operator Overloading
- C++ Function/Method Overloading
- C++ Constructors and Destructors
- Typemaps
- AS3 SWIG Directives
- LTO and SWIG
- Debugging with GDB
- Setting Up GDB
- Compiling a Debug SWF
- Running a SWF
- Stepping Through Code
- Inspecting Data
- Evaluating Expressions And Calling C/C++ Functions
- ActionScript Specific Commands
- Debugging SWFs Built From Crossbridge SWCs
- Debugging Multi-threaded Applications with GDB
- Profiling with Adobe Scout
- Included Libraries
Compiling With GCC
Crossbridge uses a version of LLVM-GCC 4.2 with a custom LLVM backend that generates ActionScript bytecode capable of running within the Flash Runtime. More specifically, the bytecode runs in the ActionScript virtual machine (AVM). This lets you compile arbitrary C/C++ code into a managed bytecode format capable of high performance execution within the Flash Runtime on Mac, Windows, or Linux in any supported browser.
Tip: Think of Crossbridge as you would any cross compiler toolchain that targets an architecture that isn't your native system architecture. All of the concepts are the same.
When using GCC to compile a single source file into an object file, you usually only have one choice of object file format: the native format supported by the target architecture. But with LLVM-GCC you have two formats: the native format, and LLVM bitcode.
The native format is used when you want to perform short incremental compiles without any cross-object optimization. Use LLVM bitcode when you want to perform an expensive Link Time Optimized (LTO) build where the majority of the optimization occurs during the final link step.
With Crossbridge, the "native" object format is ActionScript bytecode (ABC). Object files generated as ABC or LLVM bitcode are interchangeable and won't affect your build process, but typically you will either want to perform a quick un-optimized build with all object files compiled to ABC or a final fully-optimized build with everything compiled as LLVM bitcode.
By default the Crossbridge version of GCC will produce ABC:
# test.o will be ABC gcc test.c -c -o test.o
To generate LLVM bitcode, specify either -emit-llvm or -O4 when invoking GCC:
# test.o will be LLVM bitcode gcc test.c -emit-llvm -c -o test.o gcc test.c -O4 -c -o test.o
The whole Crossbridge toolchain understands these object file formats so the standard nm
, ar
and ranlib
tools for inspecting and archiving object files will work on them just as they would work on native object files in a native toolchain.
When performing the final link step you must also specify -emit-llvm or -O4 if you want an LTO build, otherwise the ABC versions of the system libraries (libc, libstdc++, etc) will be used instead of the LLVM bitcode versions.
Executable Formats
With a native toolchain the final output of GCC, when linking your object files together, is typically an executable program suitable for running on the target architecture. With Crossbridge there are two different output formats:
- Projectors
- SWFs
For example, compile a simple hello world example using the following command:
~/crossbridge/sdk/usr/bin/gcc hello.c -o hello
If you inspect the resulting executable, or attempt to run it, you might be surprised to see it execute; Wasn't Crossbridge supposed to be producing something that doesn't execute natively? By default Crossbridge compiles the program into ABC format and embeds it within a copy of the AVM runtime so that it can run as a native command-line program.
Crossbridge works this way because build systems often try to compile and execute small programs as part of the configure step of their build process to determine certain facts about the environment they are running in. By producing native Projector executables by default, Crossbridge is able to seamlessly work with almost any build system, just include the sdk/usr/bin directory in your shell's PATH
variable.
To generate a SWF, the executable format suitable for use in the Flash Runtime, you pass the -emit-swf argument to GCC:
gcc -emit-swf hello.c -o hello.swf
The resulting SWF can run inside your web browser or within the standalone version of the Flash Runtime, and will be significantly smaller than the projector version as it does not include the AVM.
Using Crossbridge With Existing Codebases
To use Crossbridge to compile a codebase that uses a command-line build system like AutoConf, CMake or Make, all you have to do is ensure that the Crossbridge SDK's bin directory appears first in your PATH
environment variable when invoking the build command.
The real world examples in the samples directory show this being done for a variety of build systems including CMake (Bullet Physics example) and Make (Lua example). The usual way to invoke a Make file to target Crossbridge is as follows:
PATH="~/crossbridge/sdk/usr/bin":$PATH make
Crossbridge's tools will be chosen in preference to any native tools on your path, but only for the invocation of make, after that your PATH
will return to normal.
Building SWCs
A SWC is the Flash Runtime equivalent of a shared library. You can link a SWC into a pure ActionScript project either with an IDE (Flash Builder, for example), or via the command-line ActionScript compiler (named mxmlc). A SWC contains the compiled ActionScript bytecode, along with an API catalog so that Flash Builder can perform code hinting while a developer writes ActionScript that uses the API exposed by a SWC.
When you generate a SWC you must specify an AS3 package name to contain the generated code, and the internal Crossbridge boilerplate code. This lets you link multiple Crossbridge-generated SWCs into one SWF without any function or class collisions. Anywhere you would have previously seen a name starting with com.adobe.flascc
this namespace will be replaced with the string passed in the gcc/g++ -emit-swc=...
argument.
Crossbridge-Specific GCC Command Line Options
The version of gcc that comes with Crossbridge has several custom options for controlling the generated code. You can see a list of them by running gcc --target-help
. The table below gives an explanation of what each option does, and how it can be used.
Option | Explanation |
---|---|
-disable-telemetry |
By default Crossbridge generates SWFs that support Adobe Scout's advanced metrics. This means that stack trace information will be visible to anybody profiling the SWF. |
-emit-swc= |
Emits a SWC that can be linked into a Flash Builder project or distributed for others to link into their own projects. You must specify the namespace that you want to use to replace the default com.adobe.flascc namespace, this lets you link multiple Crossbridge-generated SWCs into a single project. |
-emit-swf | Emit a SWF that can be executed by the Flash Runtime. |
-enable-debugger |
Generate SWF debug information and include it within the SWF. It isn't necessary to specify this option if you already compile your code with -g. |
-fllvm-llc-opt= | Pass an argument through to the final invocation of llc (LLVM-IR->ABC codegen). For more information on what options llc accepts read the LLVM documentation. |
-fllvm-opt-opt= |
Pass an argument through to the final invocation of opt (LLVM-IR->LLVM-IR optimization). For more information on what options opt accepts read the LLVM documentation. |
-flto-api= | Specifies a file containing the public API LLVM should preserve (implies that the optimizer is free to strip any symbols not mentioned in this file that aren't referenced by the symbols mentioned in this file) |
-no-swf-preloader |
By default Crossbridge injects a simple preloader into your SWF, which displays a loading bar until the SWF has downloaded. Use this option to disable the preloader. |
-swf-ns= | Performs the same namespace re-writing as the -emit-swc= option when generating a SWF, by default no namespace rewriting takes place when generating a SWF. |
-swf-preloader= |
Lets you use a custom SWF as the preloader. All of the SWF tags within the specified SWF will be included at the beginning of the generated SWF. To see a sample preloader, review sdk/usr/share/DefaultPreloader.as. |
-swf-size=WxH |
Specifies the initial width and height of the generated SWF. |
-swf-version= |
Specifies the SWF version of the generated SWF. Remember that certain APIs will only be available to your code if your SWF version is high enough. By default the SWF version is 18. |
-symbol-abc= |
If you have compiled a custom Console.as you must point to the location of the ABC so that it can be used as the root sprite within the generated SWF. |
-symbol-class= |
If you have changed the namespace or name of the class within your Console.as implementation you must specify it here in the form ":0:com.adobe.flascc::Console" where "com.adobe.flascc" is the package name and "Console" is the class name. |
Crossbridge-Specific Preprocessor Macros
Crossbridge exposes some non-standard preprocessor macros that you can use to conditionally compile code:
Macro | Value |
---|---|
__AVM2 , __AVM2__ | 1 |
__FLASHPLAYER , __FLASHPLAYER__ | 1 |
__SWF_VER , __SWF_VER__ | default: 18, or whatever was passed to GCC via -swf-version= |
Optimizing SWFs and SWCs
By default gcc/g++ compiles individual source files into ABC files. All the tools in the toolchain understand ABC as a native object file format. When it comes to link time the linker understands how to link multiple ABCs together to produce a SWF/SWC. This is equivalent to most typical native gcc/g++ workflows, and supports a fast turn around time.
Although this workflow is great for iterative development it omits optimizations that require cross-compilation-unit knowledge. To produce more optimal code we must perform Whole Program Optimization by compiling to LLVM bitcode instead of ABC and then optimizing the code during the link step when all the code is available to the optimizer.
Setting the gcc/g++ optimization level to -O4 causes it to produce LLVM bitcode instead of ABC. This is also a fully supported object file format in the Crossbridge toolchain. When multiple LLVM bitcode files are given to the linker they are merged and optimized together, producing more optimal code.
To allow LLVM to optimize your code as much as possible, you must provide a list of C/C++ symbols that you want to preserve. By doing this, LLVM can choose to remove unused functions or even rewrite function prototypes in cases where it thinks that would result in more optimal code. By providing a minimal list of symbols that must remain with their current name and prototype intact, LLVM knows how far it can take these kinds of optimizations without breaking your code.
The Quake1 example uses this kind of optimization to get the best possible performance. To enable this level of optimization create a text file with the names of the symbols you want to preserve and pass it to gcc/g++ using the -flto-api= argument. Any symbol within the Crossbridge generated code that is referenced from ActionScript must be listed in this file or it might be removed or renamed by LLVM.
A sample for what you need to include in most exports.txt files:
# built in symbols that must always be preserved _start1 malloc free memcpy memmove flascc_uiTickProc _sync_synchronize # symbols for C++ exception handling _Unwind_SjLj_Register _Unwind_SjLj_Resume _Unwind_SjLj_Unregister _Unwind_SjLj_RaiseException
Any symbols referenced by your ActionScript must also be listed here. For more examples of exports.txt being used refer to sample 9, sample 11, sample 12, and also the Bullet Physics and Quake 1 examples.
Behind The Scenes: LLVM Bitcode
Using the -O4 option to get whole program optimization should satisfy most use cases. However if you want to get your hands dirty and explore the possibilities of some of the more exotic bitcode optimizations and transformations you can try out some of the llvm tools included in the SDK. Crossbridge is currently based on LLVM 2.9.
To perform a manual whole program optimized build with special optimizations,
first compile individual source files with -O4 to produce bitcode.
After you have bitcode object files you can merge them into one bitcode file
using llvm-link
, optimize the resulting binary with opt
and then pass it to
gcc
for the final link step.
Crossbridge isn't limited to C and C++; you can use any front end capable of generating LLVM
bitcode. As long as the bitcode doesn't have native dependencies,
simply pass it to the Crossbridge gcc
to perform the final codegen and link step.
If you do experiment with other frontends the Crossbridge team would love to hear about your
experience.
Obfuscation
Crossbridge is not designed to be an obfuscator for either ActionScript or C/C++ code. Although the generated ActionScript bytecode should be no easier to reverse engineer than natively compiled machine code the Crossbridge engineering team makes no strong claims about this, nor do they test whether this is true.
By default the mangled C++ name of every source function will be trivially visible in both the decompiled output and also in ActionScript debugger and profilers. To hide the names of any generated C/C++ functions you can pass -fllvm-opt-opt=-strip
to gcc so that any function names not listed in your exports.txt file will be renamed to a symbol of the form __unnamed_N
. Functions that need to be called from ActionScript, which you must protect by listing in exports.txt, will not be renamed.
For true obfuscation you will need to find a third party solution that either obfuscates the LLVM bitcode or the ActionScript bytecode in the final SWF. A search on the web should yield plenty of results for SWF obfuscators, but the Crossbridge engineering team has not investigated which ones are reliable.
To understand the problem from the other side you might want to play around with various SWF decompilers and ActionScript bytecode disassemblers to get a better understanding of what exactly is in a SWF and how easy/hard it is to extract the various components from it.
Other Tools
Many build systems use a tool called pkg-config
as part of their configure step to determine what libraries have been installed on a system, so that dependencies can be validated and compiler options can be setup. Crossbridge ships with a version of pkg-config
so that build systems will find the Crossbridge versions of the included libraries rather than the native system libs. The directory for pkg-config
files is sdk/usr/lib/pkgconfig/ and you use the variable flascc_sdk_root
to make pkg-config paths aware of the actual location of the Crossbridge SDK without having to hardcode the path.
Crossbridge ships with several small python utilities in the sdk/usr/bin directory, which you can use for debugging and inspecting SWFs:
Utility | Description |
---|---|
swf-info.py | Dumps information about the structure of a SWF file including the various tags, SWF version, SWF size, etc. |
swfdink.py | Lets you change the SWF version and make the SWF compressed or uncompressed. |
projector-dis.py | Breaks apart a shell projector executable into its components. |
Interop between C/C++ and ActionScript
The interop between AS3 and C/C++ in Crossbridge is based around the concept of inline assembly. In a native C/C++ workflow you would use inline assembly statements to allow you to pass arbitrary strings containing platform specific instructions to the assembler to be included in your final executable. With Crossbridge the underlying system is the Flash Runtime, and the language it supports natively is AS3, so these inline assembly statements let you talk to the native Flash API set in the language it was designed with.
The GCC syntax for inline assembly allows for a simple form of pattern matching substitution so that C variables can be used by the AS3 code. This mechanism is powerful enough to express all of the interop you would want to do, but it is tedious to write and not type-safe. Here is an example of how you can pass a C variable into ActionScript and return a value back to C using the inline_as3
macro:
#include <AS3/AS3.h> // Use AS3 to sqrt a double! double somenumber = 45.0; double result = 0.0; inline_as3( "%0 = Math.sqrt(%1);\n" : "=r"(result) : "r"(somenumber) ); // Back in C we can take the result and print it out: printf("sqrt of %f is %f\n", somenumber, result);
To see a fully working example of the basic interop layer in action look at the helloworld.c file in sample 2. Or for a more complete description of the syntax for GCC's inline asm blocks see http://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html or http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html.
To better understand how to use inline assembly with Crossbridge you will need to have a basic understanding of how the code generation strategy works. Lets take a look at the ActionScript generated for some C and C++ code (simplified for clarity):
// Test.cpp class TestClass { public: void testmethod(); }; static void somePrivateFunction() { //contents } extern "C" void someCFunction() { //contents } void TestClass::testmethod() { somePrivateFunction(); }
// Test.as package C_Run { import C_Run_test_2E_s_3A_49A4F40E_2D_3219_2D_4479_2D_9E8F_2D_BCF50DA75E9E.* import avm2.intrinsics.memory.* import com.adobe.flascc.CModule public function F_someCFunction():void { // contents } } package C_Run_test_2E_s_3A_49A4F40E_2D_3219_2D_4479_2D_9E8F_2D_BCF50DA75E9E { import C_Run.* import avm2.intrinsics.memory.* import com.adobe.flascc.CModule public function F__ZL19somePrivateFunctionv():void { // contents } } package C_Run { import C_Run_test_2E_s_3A_49A4F40E_2D_3219_2D_4479_2D_9E8F_2D_BCF50DA75E9E.* import avm2.intrinsics.memory.* import com.adobe.flascc.CModule public function F__ZN9TestClass10testmethodEv():void { // contents } }
The two important things to note about this code generation strategy are that Crossbridge doesn't attempt to synthesize ActionScript classes from C++ classes and there are two different namespaces used during code generation: C_Run (the public package) and a much longer one that contains a globally unique identifier (the private package).
These public and private packages are used to differentiate between functions that should only be visible to other functions within the same compilation unit and functions that should be visible to the rest of the codebase. Because the private package name is unique no other compilation unit will import it and so anything defined within it will be invisible to the rest of the codebase.
When using inline assembly to inject additional ActionScript into the generated code you have a choice between the inline_as3
and package_as3
macros. The former injects code into the body of a generated function and so it can only be used for injecting additional statements and expressions whereas package_as3
can be used to inject global package-level variables, classes, interfaces, etc.
The package_as3
macro will inject its contents into the private package by default, but this can be changed using the "#package" directive within the inline asm:
package_as3("#package public\n var somepublicvar:int = 42;"); package_as3("#package private\n var someprivatevar:int = 42;"); package_as3("#package com.example.yourpackage\n var somespecialvar:int = 42;");
Although you can define almost anything at package-level scope such as additional ActionScript classes and interfaces you will probably find it best to use it just for things local to the current compilation unit.
Flash++
To make life easier for C++ developers Crossbridge includes the AS3 wrapper interface generator (as3wig.jar), which can generate C++ bindings for any AS3 classes.
The Crossbridge team has applied this tool on the internal AS3 classes in the Flash Runtime to generate a C++ library called Flash++, which exposes all of the Flash APIs as strongly typed C++ classes.
Here is a snippet of C++ code using the Flash++ classes to construct a new sprite and draw a circle into it. If you are familiar with AS3 this should look just like what you would write in AS3 only with C++ syntax. The mySprite
and graphics
object in this example are both reference-counted wrappers around the actual AS3 objects; they can be stored on the stack or in the heap without fear of their underlying AS3 object being garbage collected.
flash::display::Sprite mySprite = flash::display::Sprite::_new(); flash::display::Graphics graphics = mySprite->graphics; // draw simple filled circle graphics->beginFill(0xff00ff, 1.0); graphics->drawCircle(0.0, 0.0, 50.0); graphics->endFill();
In order to use the Flash++ library you will need to #include <Flash++.h>
and pass the -lFlash++ -lAS3++
compiler arguments to g++. To see a fully working example of the Flash++ interop layer in action look at the c++interop.cpp file in sample 2, the pthreads.cpp file in sample 9 and the Flash++ examples page.
The as3wig tool is included with the Crossbridge SDK so that you can generate bindings for your own ActionScript code. Sample 12 uses as3wig to generate bindings for the AGALAssembler ActionScript codebase and then uses those bindings from C++ at runtime to compile AGAL assembly into AGAL bytecode for use with Stage3D. Take a look at the Makefile for sample 12 to see how to invoke as3wig and how to compile the resulting wrapper code.
Implementing a Console
trace()
function can be sent to a file known as the flash log file (flashlog.txt). Information on how to enable this, and where to find the flash log file can be found here.
The Console
class is the first class that is created and executed when your SWF loads. It controls when and how the Crossbridge compiled code is initialized. It is also used by the underlying Crossbridge virtual kernel implementation as the object that handles read
and write
requests to the various standard input, output and error terminal streams. It also handles all fcntl
and ioctl
calls. If you aren't familiar with all of these system calls then don't worry, the default implementation is setup to redirect console output to the flash log file and also to a console on screen.
The source code for the default Console
implementation can be found in sdk/usr/share/Console.as. If you include a modified version of this in your project you will need to compile it into an ABC and also use the gcc option -symbol-abc=foo.abc
in your final link step to instruct gcc to use your version instead of the default. If you also modify the name of the class in this file you will also need to give gcc this information -symbol-class=:0:com.adobe.flascc::Console
where "com.adobe.flascc" is the package name and "Console" is the class name.
Take a look at the default implementation of the console, the most important things to notice are as follows:
- It implements the
ISpecialFile
interface (read
,write
,fcntl
andioctl
methods) - It bails out of its constructor when running in a background worker (This is explained in the section on concurrency)
- It decides whether to run in the background or foreground based on whether the player supports multi-threading
- The enterFrame handler calls
CModule.serviceUIRequests
to ensure any background thread delegation requests are serviced
How much more code you add to the Console
beyond handling console output and starting the Crossbridge code is up to you. As you will see in the section on interop, it is possible to interact with all of the Flash APIs using the C++ wrappers included in Flash++.h, but you might prefer to write it in pure AS3 in your Console
implementation.
You will see in the init
method that there are a few different ways of starting the Crossbridge generated content, depending on whether you want the code to run in the foreground or the background. This is explained in more detail in the documentation of CModule.startAsync
, CModule.startBackground
, and the section on concurrency.
When your SWF uses a preloader it actually won't be the first class created. To handle this case the Console class constructor takes an argument to specify a DisplayObjectContainer
that you can add the Console
to in order to put the Console
on the Flash Runtime Stage
. You will see the code to handle this case in the default preloader's constructor.
The call to CModule.serviceUIRequests
is explained more in the multi-threading section, but essentially if your code is running in a background worker and needs to perform an operation that requires access to APIs that only the foreground thread (on which the Console is running) has access to, it must block and delegate the code to the foreground thread. Calling this method services any pending requests from background threads.
Within the AS3 functions that Crossbridge generates for your code, certain AS3 variable names are considered reserved and are used by the code generator to maintain internal Crossbridge state within a function. However, C/C++ local variables will not conflict with these variables as they are renamed by the code generator when the final AS3 is generated. But you must ensure that any AS3 variables you declare using the AS3_DeclareVar
macro from AS3.h don't conflict with these names. The reserved AS3 variables are esp
,ESP
,ebp
,eax
,edx
,st0
,ram
,sjid
,tsjid
,tcbp
,_as3ReturnValue
and also iN
fN
where N can be any integer
Implementing a Preloader
Your Crossbridge compiled code starts executing when the whole SWF file has been loaded in the browser, but it's a best practice to show some kind of loading screen before then, so the user knows that something is happening. SWF files are designed to be streamed and make it easy to embed a small animation at the start that can play while the rest of the file is loading.

The default preloader in action
By default Crossbridge injects a simple preloader into every SWF it produces. This default preloader shows a black screen with a white loading bar that progresses across the screen as the SWF loads. If an HTTP VFS is being used, it can easily be hooked up to the progress bar to show the progress of the VFS as it is downloaded in parallel with the main SWF. You can see an example of this preloader in action in this Neverball demo.
If you don't want a preloader in your SWF (for example if it is loaded via a different SWF and doesn't need to show a UI while loading), you can disable it via the gcc option -no-swf-preloader
.
The source code for the default preloader ships with the SDK and can be found in sdk/usr/share/DefaultPreloader.as. This should give you a good template to start with as you develop your own preloader. Once you have compiled your modified version to a SWF you can tell gcc to use this instead of the default preloader by passing it the option -swf-preloader=foo.swf
.
The contents of the preloader SWF are injected into the Crossbridge SWF at the very beginning, followed by a ShowFrame
tag, which causes the preloader to execute even before the rest of the file has loaded. Once the preloader receives the event indicating that the SWF has loaded, it calls gotoAndStop
to initialize the rest of the SWF, and it then calls the constructor for the Console
class, which then takes control of the screen.
The preloader SWF can be created in a graphical tool such as Flash Pro if you want to make something more attractive, possibly with embedded images, sounds and fonts. As long as the SWF contains some ActionScript code that achieves the same effect as the default preloader with regards to handling the Event.COMPLETE
event.
More information about the SWF file format can be found in the SWF specification, but it isn't necessary to understand things at this low level to be able to understand how preloaders work.
Implementing a Virtual File System
Any time your C/C++ code performs file IO, no matter what framework is being used, at some point it will use the low level libc
API for opening, reading and writing files. Crossbridge implements these low level APIs, but also exposes a high-level ActionScript interface that makes it easy to populate the virtual filesystem with files and directories.
By default the filesystem has one root directory "/" and contains no files or directories. An object implementing IBackingStore
is responsible for handling file listing, reading and writing for the files and directories within a sub tree of the global filesystem.
Using multiple BackingStores at the same time is supported. For example, you could provide some fixed data in an embedded VFS mounted at "/data" and implement a read-write BackingStore for user data in "/user" that perhaps saves updated files back to a web-server.
Several built-in VFS types are supported but the interfaces can be implemented to provide completely custom types. Here are the supported builtin types of VFS, which can also be seen by running genfs --help
:
- Embedded
- Embedded + Zipped
- HTTP
- Local Shared Object
Embedded / Zipped VFS
The simplest way to provide a filesystem for your SWF is to embed it within the SWF itself. Crossbridge provides a tool called genfs
, which converts a directory structure on your real filesystem into a set of ActionScript classes that can be compiled into your SWF and instantiated at runtime.
To create an embedded VFS called "myvfs" from the contents of the folder called "sourcedirectory" you would use the following command:
~/crossbridge/sdk/usr/bin/genfs --type=embed --name=myvfs sourcedirectory vfsprefix
The output consists of multiple ActionScript files prefixed with "vfsprefix" and a file called vfsprefix.rules, which contains a makefile rule file that could be included in your makefile to compile the files (this is optional).
To compile all of the VFS files into one ABC file suitable for linking into your final SWF you can use ASC2:
java -jar ~/crossbridge/usr/lib/asc2.jar -merge -md -AS3 -strict -optimize \ -import ~/crossbridge/usr/lib/builtin.abc \ -import ~/crossbridge/usr/lib/playerglobal.abc \ -import ~/crossbridge/usr/lib/BinaryData.abc \ -import ~/crossbridge/usr/lib/ISpecialFile.abc \ -import ~/crossbridge/usr/lib/IBackingStore.aBC \ -import ~/crossbridge/usr/lib/IVFS.abc \ -import ~/crossbridge/usr/lib/InMemoryBackingStore.abc \ -import ~/crossbridge/usr/lib/PlayerKernel.abc \ vfsprefix*.as -outdir . -out myvfs
This creates a file called myvfs.abc in the current directory, which you pass to gcc in the final link step of your project. To instantiate the VFS at runtime you can construct it like this:
import com.adobe.flascc.vfs.myvfs; myvfs();
If you didn't specify a class name with the "--name" argument the default class name is "RootFSBackingStore".
Switching to an embedded and compressed VFS is as simple as changing the value of the "--type" switch to "compressed". This will produce only one ActionScript source file that contains your VFS as an embedded zip file. All of the previous commands should work just the same for this type of VFS.
HTTP VFS
genfs
supports another type of VFS that can load a predetermined set of files via relative URLs. As with the embedded example, the first step is to run genfs
on the local directory you want to make available as a VFS:
~/crossbridge/sdk/usr/bin/genfs --type=http --name=myvfs sourcedirectory vfsprefix
This time genfs
generates files named vfsprefix_fileNchunkM where N and M are integers. Most files will fit into one file but files over 10 megabytes will be split into smaller chunks so that browsers are more likely to cache the files.
A manifest file will also be generated that lists all of the chunks and is designed to be included by the HTTP VFS ActionScript implementation. To compile this, you must copy the file sdk/usr/share/HTTPBackingStore.as to the current directory and run the following command:
java -jar ~/crossbridge/usr/lib/asc2.jar -merge -md -AS3 -strict -optimize \ -import ~/crossbridge/usr/lib/builtin.abc \ -import ~/crossbridge/usr/lib/playerglobal.abc \ -import ~/crossbridge/usr/lib/BinaryData.abc \ -import ~/crossbridge/usr/lib/ISpecialFile.abc \ -import ~/crossbridge/usr/lib/IBackingStore.aBC \ -import ~/crossbridge/usr/lib/IVFS.abc \ -import ~/crossbridge/usr/lib/InMemoryBackingStore.abc \ -import ~/crossbridge/usr/lib/PlayerKernel.abc \ HTTPBackingStore.as -outdir . -out myvfs
Using the resulting class is slightly more complex than the embedded VFS because it isn't usable until the files have downloaded. The resulting class dispatches several events so that you can handle progress, failure and completion. The events are the same as those dispatched by the URLLoader
.
If you look in the default preloader implementation you will see that it has code that handles the web VFS for you. To make use of this you must compile your web VFS into the preloader. A working example of this can be found in the Neverball sample.
LSO VFS
Flash has a feature known as Local Shared Objects. LSOs provide domain-specific structured data storage outside the browser cache. A working implementation of the VFS API that supports loading and saving to an LSO can be found in sdk/usr/share/LSOBackingStore.as.
By default every domain is allowed 100Kb of LSO storage space, but more can be requested by using the SharedObject API to request permission from the user.
The VFS API
All of these VFS implementations are implemented on top of the VFS ActionScript API. To implement a minimal filesystem you only need to implement the IBackingStore
interface.
The IKernel Interface
The libc implementation provided with Crossbridge is compiled from the source code for the FreeBSD 8.1 libc implementation. libc is usually the lowest dependency in any software stack before you reach the operating system kernel and therefore makes assumptions about the environment in which it is running. In the FreeBSD implementation of libc these assumptions manifest as function calls whose implementations are defined within the FreeBSD kernel itself. To satisfy these "syscalls" Crossbridge provides an ActionScript interface and a default implementation that implements each of these syscalls.
For most users, the default implementation will be sufficient, but advanced users might want to customize the behavior or provide implementations of some of the more exotic syscalls that Crossbridge has left undefined.
The IKernel
interface does not try to hide the fact that it is a low level API. To understand how each syscall works you should read the FreeBSD documentation and possibly even the FreeBSD kernel source code to see what the expected behavior is. You can read the ActionScript interface description here.
Once you have implemented this interface you can instruct Crossbridge to use your implementation by setting CModule.kernel
in your Console implementation, as shown below:
CModule.kernel = new YourIKernelImpl()
Concurrency in Flash Player 11.5
Before diving into multi-threading it is important to understand how this is implemented in the Flash Player, as it will affect the way you think about writing your AS3 interop code.
From an ActionScript developers point of view Flash player 11.4 introduces a simple Worker based model of shared-nothing multi threading where each Worker (sometimes called an Isolate) is fully separated from the others and cannot share references to any ActionScript objects. Communication between Workers is done by an API called the MessageChannel
that allows copies of objects to be sent between threads. This allows for high-level concurrent programming in AS3 without having to worry about low level issues around locking and synchronization.
To enable developers to also program with a more powerful low-level shared-memory multi-threading model Flash Player 11.5 allows ByteArray
objects to be shared between workers. Various concurrency primitives (Mutex/CAS/Fence/Condition) are also available to help synchronize access to ByteArrays
that are being operated on by multiple Workers. Crossbridge implements all of the necessary low level thread synchronization primitives needed to enable us to support the industry standard pthreads library. When calling pthread_create
to create a new C thread a new Flash Worker is setup that shares the Crossbridge domainMemory
ByteArray
. Using this shared access to the Crossbridge ByteArray
and the low level primitives to ensure correct synchronization Crossbridge is able to faithfully implement pthreads in the Flash player. This makes it possible to compile and run existing C/C++ based multi-threaded code within the Flash Player without needing to modify the code.
The first SWF that the Flash player runs is known as the primordial Worker; Because it runs on the UI thread in the runtime it has access to the full set of APIs that Flash exposes. Subsequently created workers are all considered children of this primordial worker and have access to most, but not all, of the APIs the primordial worker can use. Because of this it might be necessary for background Workers to communicate with the primordial Worker to get it to perform actions on the child worker's behalf.
What this means for Pthreads is that it is occasionally desirable for a C thread to be run on the main Flash Worker instead of the Worker that was created to run that thread. Crossbridge includes functionality that allows you to very easily block a background thread until the main worker is in a position to service it by running a C function on the main thread whilst maintaining any thread-local state from the background thread. These requests are serviced when CModule.serviceUIRequests
is invoked from the main worker. The default Console callsCModule.serviceUIRequests
every ENTER_FRAME
event. To ensure things work as expected the primordial Worker can only impersonate another C thread when that thread has been put to sleep using the avm2_self_msleep
function. To wake up a thread that was being impersonated the impersonator must end with a call to avm2_wake
. This is demonstrated in sample 9.
APIs unsupported in background Workers
The following APIs will not be available from within a background worker. Any attempt to construct an instance of any of these will throw an IllegalOperationError
with the message "This feature is not available within this context," the errorID 3731 will be the same in all instances, allowing developers to key off of this value.
flash.desktop.Clipboard
calling constructor will throw; calling generalClipboard will return nullflash.desktop.NativeDragManager
isSupported returns falseflash.desktop.Updater
isSupported returns falseflash.display.NativeMenu
isSupported returns falseflash.display.NativeWindow
isSupported returns false; ctor will throw; supportsMenu property false; supportsNotification property is false; supportsTransparency is false; systemMinSize is null; systemMaxSize is null;flash.external.ExtensionContext
createExtensionContext() will throw; getExtensionDirectory will throw;flash.external.ExternalInterface
available returns false; objectID property is null;flash.html.*
HTMLLoader.isSupported returns falseflash.media.Camera
isSupported returns false; getCamera returns null; names property is null;flash.media.CameraRoll
Mobile only. No changes made here for this version as workers are not supported on mobile.flash.media.CameraUI
Mobile only. No changes made here for this version as workers are not supported on mobile.flash.media.StageWebView
isSupported returns false; constructor will throw;flash.net.drm.*
DRMManager.isSupported returns false; getDRMManager() returns null;flash.printing.*
PrintJob.isSupported returns false; constructor will throw; active is always false; printers is null; supportsPageSetupDialog is false;flash.security.XMLSignatureValidator
isSupported returns false; calling constructor will throw.flash.system.IME
isSupported returns false; get conversionMode returns null; set conversionMode is a no op; get enabled returns false; set enabled is a no op; doConversion() is a no op; setCompositionString() is a no op.flash.system.SystemUpdater
calling constructor throwsflash.text.StageText
calling constructor throwsflash.ui.ContextMenu
isSupported returns false; calling constructor will throwflash.ui.Mouse
all methods are no-ops; setting 'cursor' property is a no-op
APIs with behavioral changes in background Workers
The following APIs have modified behavior when running from within a background worker. Some calls on methods are no-ops, while others will throw an IllegalOperationError or equivalent as is consistent with the documented API. Return values should be consistent with the documentation to the extent possible. For example, if a method returns an Array with elements in it under normal conditions, when executing from within a background worker, it will return an empty Array. Some of the drawing APIs spawn multiple threads based on the number of CPUs on the host system. Filters, blitting, and rasterizing will create threads to perform these operations. When running in a background worker these threaded operations are turned off.
flash.accessibility.Accessibility
- active — always returns false
- updateProperties(); — no-op
flash.desktop.NativeApplication
- properties
- supportsDefaultApplication, supportsDockIcon, supportsMenu, supportsStartAtLogin, supportsSystemTrayIcon — all return false
- activeWindow — returns null
- autoExit — setter is a no-op
- idleThreshold — setter is a no-op
- openedWindows — returns an empty Array []
- systemIdleMode — setter is no-op
- methods
- activate(), clear(), copy(), cut(), paste(), selectAll() — no-op
- exit() — forces this background worker to stop and shutdown
- events
- no events are supported — background workers will not receive any NativeApplication events.
- properties
flash.display.Stage
- allowsFullScreen — always returns false
- stage3Ds — always returns empty Vector
- stageVideos — always returns empty Vector
- supportsOrientationChange — always returns false
- wmodeGPU — always returns false
flash.filesystem.File
- browseForDirectory(), browseForOpen(), browseForOpenMultiple(), browseForSave() — no-op
flash.net.FileReference
- browse(), download(), save() — no-op; always returns false
flash.net.FileReferenceList
- browse() — no-op; always returns false
flash.system.System
- ime — always returns null
- exit() — forces this background worker to stop and shutdown
- setClipboard() — no-op
Creating SWCs with SWIG
The Simplified Wrapper and Interface Generator (SWIG) is a tool that parses C/C++ header files and automatically generates the interop glue code required to expose that C/C++ library to a different target language. Crossbridge extends SWIG with a backend that targets the Crossbridge environment so that SWIG can automatically generate wrappers using the interop functionality introduced earlier on this page. This is ideal when you have a large codebase that you want to expose to ActionScript without writing lots of interop code by hand.
SWIG is not perfect. It is able to interpret enough C/C++ to be extremely useful, but occasionally you will run into code that its parser cannot understand. In these situations you must provide workarounds, typically hiding the misinterpreted code from SWIG and exposing the functionality to ActionScript via a custom wrapper.
There are also situations where the source language (C/C++) supports constructs that the target language (AS3 for Crossbridge) does not support. Some relevant examples include overloaded functions and operator overloading, neither of which are supported in ActionScript. To work around these limitations SWIG has support for detecting and renaming functions, but some manual work is required to tell SWIG which functions to rename and what they should be called.
The SWIG website has documentation and examples of how to use SWIG. The majority of this is applicable, regardless of the target language, so it is a valuable resource even when using SWIG to target ActionScript.
The SWIG Workflow
To use SWIG, perform the following steps:
- Write a SWIG interface file
- Process the interface file with SWIG to produce the ActionScript and C/C++ wrapper implementations
- Compile the ActionScript wrapper implementation into an ABC file.
- Compile the C/C++ wrapper implementation into an LLVM bitcode or ABC object file.
- Perform your final link including both of the compiled wrapper implementations.
Creating a SWIG Interface File
The SWIG interface file contains all of the directives used to control SWIG's behavior. Use it to specify which header files from your codebase SWIG should parse and any custom rules needed to alter how SWIG wraps the C/C++ types that it finds in those headers.
From this interface file, SWIG generates the resulting interop API in two parts.
- ActionScript interface - Defines the packages, functions, and classes that an ActionScript developer using the final SWC will be expected to use.
- C/C++ layer - Contains code that helps to bridge the gap between the ActionScript interface and the actual codebase. Typically this includes functions that perform the actual type conversion between C++ and ActionScript objects. This code is not part of the public API and is used internally by the glue code SWIG generates.
Within your interface file you can add custom code to either half of the resulting interface. This allows you to generate simple helper C++ functions and expose them via ActionScript without having to modify your actual codebase. You might want to add custom code when you find that a common idiom in your C++ code is better exposed to ActionScript via a simpler high-level API.
Here is an example of a simple interface file which we'll assume is called MyAPI.i:
%module MyAPI %include "MyAPI.h"
To process this interface file, you invoke SWIG like this:
~/crossbridge/sdk/usr/bin/swig -as3 -outdir . -includeall -ignoremissing -o MyAPI_wrapper.c MyAPI.i
The argument -includeall instructs SWIG to follow all #include
statements and the -ignoremissing argument instructs SWIG that it should not stop when it is unable to locate an include file. Neither is required and you should consult the SWIG documentation for more information about other available options.
This produces two output files MyAPI.as and MyAPI_wrapper.c that represent the two halves of the SWIG interface.
To add code into the generated C/C++ half of the wrapper you can do so like this in your interface file:
%{ #include <stdio.h> int someHelperFunction(int x) { printf("someHelperFunction called!\n"); return somethingUseful(x); } %}
To hide certain types from appearing in the final API you can use the ignore directive, to rename a type you can use the rename directive. For an example of the ignore directive in use look at samples/Example_Bullet/bullet.i.
%ignore functionToHide(int arg1, int arg2); %rename oldFunctionName newFunctionName;
C++ Operator Overloading
AS3 does not support operator overloading so to be able to handle classes that use overloaded operators you need to use SWIG's rename directives to expose the operators as normal AS3 methods. In the following example the plus operator will be renamed to a method called add
:
%rename (add) Foo::operator+; class Foo { public: const Foo& operator+(const Foo &rhs); };
C++ Function/Method Overloading
AS3 does not support function or method overloading so if you do need to expose overloaded functions or methods in your AS3 interface you will need to use the SWIG rename directive to map them to uniquely named methods in AS3. In the following example the overloaded add
method is renamed to avoid conflicts:
%rename (addInteger) Foo::add(int); %rename (addNumber) Foo::add(double); class Foo { public: const int add(int rhs); const int add(double rhs); };
C++ Constructors and Destructors
The rules around C++ copy constructors, default constructors and implicit destructors are complex. SWIG will attempt to create wrappers for these methods wherever possible, but there are cases where it might not generate the wrapper you want, or conversely might generate wrappers you don't want.
The SWIG documentation has a whole section on the various directives that can be used to control the behavior of wrapper generation for constructors and destructors that can be found here.
Typemaps
SWIG uses a concept called typemaps where each language backend is able to specify how to map C/C++ types into the target language. The Crossbridge version of SWIG ships with a set of default typemaps for ActionScript that map the basic C/C++ types into equivalent ActionScript types:
C Type | ActionScript Type | Notes |
---|---|---|
char | String | AS3 has no char type. |
int | int | |
float | Number | AS3 has no float type so all floats are promoted to double. |
double | Number | |
char * | String | By default Crossbridge assumes that char* means a null terminated string, but this can be overridden globally or locally for a particular function in your project if necessary. |
unsigned char | String | |
bool | Boolean | |
void | void | |
function pointer | Function | |
regular pointer | int | |
any other type | * |
All of the default SWIG typemaps for ActionScript can be found in sdk/usr/share/swig/2.0.4/as3. By re-declaring typemaps in your SWIG interface file you can override the defaults without having to modify the typemaps in the SDK.
AS3 SWIG Directives
A few custom AS3 specific SWIG directives have been exposed to control some aspects of the wrapper generation. When a derived C++ class implements a virtual function as public that was declared protected/private in the base class you will need to suppress the usual use of the "override" keyword as you can see in this example:
/* File : example.c */ class base { protected: virtual ~base(){} }; class derived : public base { public: virtual ~derived() {} }; /* File : example.i */ %module ExampleModule %as3suppressoverride derived::~derived;
When using typemaps that require custom ActionScript types to be used in the generated AS3 wrappers you may need to import classes from custom packages. This can be achieved with the as3import directive that allows you to inject as3import annotations into the method declarations in the generated C/C++ wrapper code.
/* File : example.i */ %module ExampleModule // When the generated wrapper for a function needs custom imports // You can add them via the as3import directive. Multiple namespaces // should be comma delimited. %as3import("com.foo.test1") bar1; %as3import("com.foo.test1,com.bar.test2") bar2; /* File : example.cpp */ void bar1() { } void bar2() { }
LTO and SWIG
As mentioned in the section on compiling code with GCC, the most optimal way to compile code is with -O4 -flto-api=.... When using SWIG the exports.txt file that you use to preserve symbol names must include all of the wrapper functions generated by SWIG.
To preserve the wrappers generated by SWIG, use the nm tool to generate a list of all of the defined symbols within the C++ half of the SWIG wrapper. To convert the output of the list into a format suitable for inclusion in the exports file, you can use the common unix utilities sed and awk to process the resulting list:
# First we generate the C and AS3 wrappers swig -as3 -module MyLib swig.i -o MyAPI_wrapper.c # Second we compile the C wrappers to bitcode g++ -O4 -c MyLib_wrapper.c # The final command consists of several steps: # 1) Run nm on the bitcode to generate a list of symbols # 2) Filter the symbol list using grep to only show defined ("T") symbols # 3) Use awk to select just the symbol name # 4) replace the "__" prefix with a single underscore # 5) append the output to an existing exports.txt file nm MyLib_wrapper.o | grep " T " | awk '{print $3}' | sed 's/__/_/' >> exports.txt
The Bullet Physics example uses this technique when compiling Bullet into a SWC for optimal performance. Take a look in the makefile to see it being used in practice samples/Example_BulletPhysicsLibrary/Makefile.
Debugging With GDB
Like most GCC-based toolchains Crossbridge uses GDB as its debugger. Crossbridge provides a modified version of GDB that can debug your Crossbridge compiled C/C++ code while it is running within the Flash Runtime. GDB can be used from the command line or from within IDEs that provide support for GDB like Eclipse CDT. Crossbridge does not currently offer official support for debugging within an IDE.
Setting Up GDB
In order to use GDB on your system you will need to set a couple of variables:
- Set the FLASCC_GDB_RUNTIME environment variable
This is a path to the program that will launch the SWF that is being debugged. This path can either point to the standalone Flash Player Debugger or a web browser which has the Flash Player Debugger plugin installed.
This is an example of setting the path to a standalone Flash Player Debugger:
Mac> export FLASCC_GDB_RUNTIME="/Users/path/to/Flash Player.app" Win> export FLASCC_GDB_RUNTIME="/cygdrive/path/to/FlashPlayerDebugger.exe"
This is an example of setting the path to a web browser:
Mac> export FLASCC_GDB_RUNTIME="/Applications/Safari.app" Win> export FLASCC_GDB_RUNTIME="/cygdrive/path/to/iexplore.exe"
Note: If you are using Firefox you might want to extend or disable the plugin hang detection time to avoid the browser prematurely killing the SWF while you are debugging.
- Set the ScriptStuckTimeout variable in mms.cfg
The Flash Player stops running programs if they take too long. This feature can interfere with the debugger. To ensure reliable operation of the debugger edit your mms.cfg file to include the ScriptStuckTimeout parameter.
Platform Location of mms.cfg Windows 7 (64-bit) C:\Windows\SysWow64\Macromed\Flash Windows 7 (32-bit) C:\Windows\System32\Macromed\Flash Mac OSX /Library/Application Support/Macromedia
ScriptStuckTimeout=600
See the Flash Player Administration Guide (Chapter 4) for more information about setting up an mms.cfg file.
Compiling a Debug SWF
To be able to debug your code you must compile it with debug info. Using the previous hello world example we compile with the -g -O0 options:
Mac> ~/crossbridge/sdk/usr/bin/gcc -g -O0 hello.c -emit-swf -o hello.swf Win> /cygdrive/c/crossbridge/sdk/usr/bin/gcc.exe -g -O0 hello.c -emit-swf -o hello.swf
This produces un-optimized code with full debug info so that GDB is able to fully inspect your data structures and understand where it is within the code while you step through it.
Running a SWF
Now that your environment is set up, you can run GDB by passing it the path to the file that you want to debug. If your using the standalone Flash Player Debugger then this would be the path to the SWF file. If you are using a browser then the path could be either directly to the SWF file or to the HTML file that embeds the SWF.
This example uses the standalone player to launch the SWF directly:
Mac> ~/crossbridge/sdk/usr/bin/gdb hello.swf Win> /cygdrive/c/crossbridge/sdk/usr/bin/gdb.exe hello.swf
You should now be in a normal GDB console that looks like this:
GNU gdb (GDB) 7.3 Copyright (C) 2011 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "--host=x86_64-apple-darwin10 --target=avm2-elf". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>... (gdb)
Set a breakpoint by issuing the break main command and answering y when asked to make breakpoint pending on future shared library load. This warning is harmless and simply means that because the SWF has not started running GDB doesn't yet know where to set the breakpoint; once the SWF has started executing GDB will get the information it needs and place the breakpoint appropriately.
To start the content issue the run command. GDB will launch the content and attach to the Flash Runtime. You should see the following output in the GDB console:
(gdb) run Starting program: helloWorld.swf 0xdddddddd in ?? () Breakpoint 1, 0xf0000030 in main (argc=0, argv=0x200ff0) at helloWorld.c:20 20 int s = 2; (gdb)
You are now ready to debug your SWF; normal GDB commands to set breakpoints and step through the code should work as you would expect.
Stepping Through Code
All of the normal GDB commands for stepping through code are available to you, try using step or next to step over or into code:
Breakpoint 1, 0xf000007b in main (argc=0, argv=0x200ff0) at debuggingexample.c:28 28 int s = 2; (gdb) step 29 int t = 1; (gdb) next 32 for (i = 0; i < 8; i++){ (gdb)
A great reference for GDB is this quick reference that lists the syntax for all of the commonly used commands.
Inspecting Data
info locals will show you the values of all the C/C++ local variables and info args will show you the arguments to the current function:
#0 0xf0000086 in main (argc=0, argv=0x200ff0) at debuggingexample.c:33 33 t *= 2; (gdb) info locals s = 2 t = 2 i = 1 (gdb) info args argc = 0 argv = 0x200ff0
Evaluating Expressions And Calling C/C++ Functions
GDB has a basic C/C++ expression parser that can be used to evaluate the value of simple expressions while debugging without having to recompile any code. This expression parser also handles function calls as the following example based on the code in samples/08_GDB/debuggingexample.c demonstrates:
Breakpoint 1, 0xf000007b in main (argc=0, argv=0x200ff0) at debuggingexample.c:28 28 int s = 2; (gdb) step 29 int t = 1; (gdb) call square(4) $1 = 16 (gdb) call square(s) $2 = 4
ActionScript Specific Commands
As far as GDB knows, it is remotely debugging a real native C/C++ application, so when it prints out information about the current call stack or local variables, it prints out the current C/C++ call stack or the current C/C++ local variables. To see further up the callstack into ActionScript code, or to inspect the values of ActionScript local variables, Crossbridge implements several AS3-specific versions of the regular GDB commands, prefixed with as3.
AS3 Specific command | Description |
---|---|
as3bt | Shows the backtrace of the entire ActionScript stack. This displays mangled names for all of the currently executing C/C++ code along with the names of any ActionScript functions above them on the callstack. |
as3locals | Displays the values of ActionScript local variables. Within the stack frame for a C/C++ function you will see internal mangled variable names along with any AS3 variables defined using the AS3_DeclareVar macro. |
as3args | Displays the ActionScript arguments to the function. For a normal C/C++ function this displays nothing, but if you are within a function that was annotated using the AS3 interop function annotation syntax, it lists the arguments specified in the ActionScript signature for the function. |
set as3namespace | When debugging a SWC (or a SWF compiled with -swf-ns ) you must inform GDB what namespace you wish to debug, the default is com.adobe.flascc. This must be done before the run command is used. |
show as3namespace | Shows the namespace GDB is using to debug, the default is com.adobe.flascc. |
as3sync | Causes GDB to re-read all information about the state of the program without stepping. |
as3up | Switches to a higher ActionScript stack frame, which lets you inspect stack frames that might not be part of the compiled C/C++ code; for example the code within Console.as. |
as3down | Switches to a lower stack frame, this lets you inspect stack frames that might not be part of the compiled C/C++ code; for example the code within Console.as |
as3finish | Runs until the current ActionScript function exits. |
as3step | Steps over the currently executing instruction, but follows function calls. This might correspond to one C/C++ step or within an AS3 stack frame it might correspond to one ActionScript expression. |
as3next | Steps over the currently executing instruction without following function calls. This might correspond to one C/C++ step or within an AS3 stack frame it might correspond with one ActionScript expression. |
Here is example output from the as3bt command:
(gdb) as3bt (*)global/C_Run::F_main()[M__2F_var_2F_folders_2F_i4_2F_i45TNQjSGBi1tjLCAp7gh_2B__2B__2B__2B_TI_2F__2D_Tmp_2D__2F__2F_ccPpenxF_2E_o_3A_9ED146A0_2D_11AC_2D_435C_2D_A296_2D_2A134CDD921E:27] global/C_Run::F__start1()[M__2F_p4_2F_sb_2F_alcmain_2F_mainline_2F_sdk_2F_usr_2F_lib_2F_crt1_c_2E_o_3A_7A5FAA4B_2D_E8B6_2D_45F6_2D_97F5_2D_1FB6A080CB53:18] flascc::CModule$/callFun() flascc::CModule$/start() flascc::CModule$/_initLib() global/C_Run::initLib() flascc::Console/initG() (gdb)
Debugging SWFs Built From Crossbridge SWCs
When you compile code into a SWC using the -emit-swc
option, all of your code will be placed within the namespace you specify rather than the default com.adobe.flascc
namespace. Because your SWF could contain classes within any number of namespaces GDB needs to be told which namespace you want to use.
To see what namespace GDB is currently working with use the show as3namespace command:
(gdb) show as3namespace The AS3 namespace of the inferior is "com.adobe.flascc".
To select the namespace use the command set as3namespace before running the SWF:
(gdb) set as3namespace com.your.lib (gdb)
You must also use this command if you have implemented the optional namespace re-writing argument for SWFs using the -swf-ns
option.
Debugging Multi-threaded Applications with GDB
Multi-threaded C/C++ applications that use the pthread library can be compiled with Crossbridge to run in Flash Player 11.5 (or newer). This page describes how to get started using GDB with multi-threaded applications.
Profiling with Adobe Scout

Profiling the code from sample 4 using Adobe Scout.
SWFs generated with Crossbridge can be profiled using Adobe Scout, a tool for performance profiling Flash content. Scout gives you a sampling code profiler and exposes information that lets you diagnose problems with your usage of the Stage3D APIs.
Scout understands the mangling scheme used by Crossbridge and GCC and will show your C/C++ functions in the ActionScript sampler window as they appear in your C/C++ codebase. This makes it easy to identify performance bottlenecks in your C/C++ code when running as a SWF.
Profiling multi-threaded SWFs is currently an experimental feature in Scout that is disabled by default. To enable it select "preferences" from the main menu then click on the "Beta Features" tab. In this tab ensure that the "Start sessions for ActionScript Workers" checkbox is ticked. When using this experimental feature each worker will show up as though it were a separate SWF being profiled within Scout's UI.
Included Libraries
To help you get started, Crossbridge includes several commonly used open-source libraries already compiled and ready for use within your own projects.
Library Name | Description |
---|---|
libjpeg | Library for decoding JPEG images. |
libpng | Library for decoding PNG images. http://www.libpng.org/pub/png/libpng.html |
zlib | zlib de/compression library. |
dmalloc | Debug malloc implementation (for tracking down memory leaks/smashes). http://dmalloc.com/ |
libffi | Helps create interop layers between languages. http://sourceware.org/libffi/ |
libSDL | Simple DirectMedia Layer. This library exposes a cross-platform API for handling input, sound and video. It was compiled with experimental support for the included Flash-based version of libvgl. http://www.libsdl.org/ |
libvgl | BSD's low level framebuffer and input handling library. Crossbridge includes a version with an experimental backend for Flash that is used in the Quake1 example. http://www.freebsd.org/cgi/man.cgi?query=vgl |