Comparing the before and after


#1

The first release of Redtamarin date back from 2008
see some archives here

around v0.3.0 (or was it 0.2.5 ?) I introduced a Socket class
that was in 2011, that’s the before moment

now, 5 years later, in 2016, I’m reintroducing again that socket utility class
after it had been removed for so long

Between this before and after, a hell lot of things have changed
so here a little recap :slight_smile:

Warning
This is a deep introspection into the guts of Redtamarin
not only the technical but also its philosophy on “how to do things”


The Before

One thing, that was (and still is) at the core of Redtamarin, is to do thing cross-platform.

The same way a dev expect a new String() to behave the same everywhere,
I had to make this new Socket() also behave the same under Windows, Mac OS X and Linux.

Also at that time, I was not using the CLIB in the same way, you could import things like
C.stdlib etc. but it was mainly used as low-level support, I was not even trying to replicate the full POSIX.

So when it came to implement sockets, implementing a package like C.sys.socket was not even on the table, I decided to directly implement a native socket class.

To do that, in the C++ frontend, you first declare an abstract Socket class and then you provide
an implementation for each of your platforms: PosixSocket would cover both Linux and Mac OS X, and WinSocket would provide for Windows.

But those are not yet the native class, they are just internal classes defined in the shell and wether you are compiling for a specific platform, the right implementation is included.

The native class is something else named SocketClass in C++ and defined like that in AS3

package avmplus
{

    /**
     * Provides basic sync socket.
     */
    [native(cls="::avmshell::SocketClass", instance="::avmshell::SocketObject", methods="auto")]
    public class Socket
    {
        //...
    }
}

And it would work like that, let’s say you want to use the method bind()
in AS3 you create a socket and then use the bind method
eg.

var sock:Socket = new Socket( ... );
var binding:Boolean = sock.bind();

But here what happen when you call bind() from AS3

  • bind() is just a wrapper function that in fact call the native method _bind()
package avmplus
{

    /**
     * Provides basic sync socket.
     */
    [native(cls="::avmshell::SocketClass", instance="::avmshell::SocketObject", methods="auto")]
    public class Socket
    {
        //...
       private native function _bind( port:int ):Boolean;

       public function bind( port:uint ):Boolean
        {
            var result:Boolean = _bind( port );

            if( result )
            {
                trace( "bind to port " + port );
                _bound = true;
                _port  = port;
            }

            return result;
        }
    }
}
  • so in fact it calls the native method _bind() from the C++ SocketClass instance SocketObject
namespace avmshell
{

    SocketObject::SocketObject(VTable *vtable, ScriptObject *delegate, int sockd)
        : ScriptObject(vtable, delegate)
    {
        //...
    }

    bool SocketObject::_bind(const int port)
    {
        return socket->Bind( port );
    }

}
  • which in fact call the Bind() method of the abstract class Socket
    the SocketObject define one instance of a socket object
namespace avmshell
{
    /**
    * Abstract base class for performing platform-specific socket operations
    * This class needs to be derived and its methods implemented by platform
    * to enable the shell to perform basic blocking sockets
    */
    class Socket
    {
    public:

        /**
        * Virtual Destructor
        */
        virtual ~Socket() {}

        // Server initialization.
        virtual bool Bind(const int port) = 0;

    }
}
  • but we don’t really call the abstract, we call its implementation
    let’s see with the PosixSocket
namespace avmshell
{
    
    bool PosixSocket::Bind(const int port)
    {
        if(!IsValid()) {
            return false;
        }

        sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
        addr.sin_port = htons(port);
        int status = bind(_socket,
                          reinterpret_cast<struct sockaddr *>(&addr),
                          sizeof(addr));
        return status == 0;
    }
}

And now if I was including the Windows implementation you would probably get a “WTF moment”, well… let’s include WinSocket

namespace avmshell
{
    
    bool WinSocket::Bind(const int port)
    {
        if(!IsValid()) {
            return false;
        }

        sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
        addr.sin_port = htons( (u_short)port );
        int status = bind(_socket,
                          reinterpret_cast<struct sockaddr *>(&addr),
                          sizeof(addr));
        return status == 0;
    }
}

I mean WTF right ? it is the same implementation!!!

So why do all that complicated setup with an abstract class and 2 implementations ?

Mainly because even if most of the w32socket API follow more or less the BSD socket API
there are still many differences

  • POSIX (Mac OS X, Linux, etc.) use a socket descriptor (an integer)
    to refer to a socket instance
  • w32socket use a special handle type SOCKET
    to refer to a socket instance
  • some methods are different
    POSIX use close( _socket )
    w32socket use closesocket( _socket )
  • and tons of little details like that

but most importantly this one big detail:
w32socket need be initialised before you can use any of the socket functions
eg.

    bool WinSocket::Setup()
    {
        //initialize Winsock32
        WSADATA winsock_data;
        WORD version_requested;
        int err;
        
        version_requested = MAKEWORD(1, 0);
        err = WSAStartup(version_requested, &winsock_data);
        
        if(err != 0) {
            printf( "Unable to initialize Winsock, err = %d\n", WinSocket::LastError() );
        }
        
        return err == 0;
    }

while you don’t need to do anything on POSIX

    bool PosixSocket::Setup()
    {
        //nothing to do on POSIX
        return true;
    }

In itself the approach of using an abstract class and 2 different implementations is not that bad,
you could do almost the same with AS3, like define an AS3 interface and provide for example an implementation for AIR Desktop and another implementation for AIR Mobile.

No the real problem with all that is adapting a C API (the BSD socket), meant to be used with functions and data structure, into an “Object Oriented” C++ API which is then translated to an AS3 class.

All that after few years of experience lead to a very different approach.


The After

One of the best advice I received when I started to work with the Tamarin source code was from one of the Adobe engineer (which sadly I forgotten the name and can not find back in the mailing list post archives), who told me “try to write as less as C++ as possible”.

Translating to: if you are in a situation where you need to write a lot of logic or code structure and you do that in C/C++, it will be error prone, hard and difficult; if instead you just write the bare minimum necessary in C/C++ and then do all the “glue” in AS3, then things are much much simpler.

It does not seem much like that, but this is the very basis of Redtamarin.

If you write a program in C/C++ and you have nothing else, then sure you have no other choices, but our situation is much different, we are in the context of a virtual machine that can not only interpret/execute AS3 code but can also communicate with the C/C++ host.

This is basically the VMPI, or the Virtual Machine Programming Interface.

It started small, and I basically copied what was already done

  • first you have VMPI.h
    which before anything select the platform
#include "system-selection.h"

#if AVMSYSTEM_WIN32
  #include "win32/win32-platform.h"
#elif AVMSYSTEM_UNIX
  #include "unix/unix-platform.h"
#elif AVMSYSTEM_MAC
  #include "mac/mac-platform.h"
#elif AVMSYSTEM_SYMBIAN
  #include "symbian/symbian-platform.h"
#endif
  • in those platform headers you will find basic stuff like that
#define VMPI_isalnum ::isalnum
#define VMPI_isalpha ::isalpha
#define VMPI_iscntrl ::iscntrl
#define VMPI_isdigit ::isdigit
  • right after that VMPI.h then
    define a bunch of function signatures
    for example:
/**
* This method should return the current UTC date and time in milliseconds
* @return UTC date and time in milliseconds
*/
extern double       VMPI_getDate();
  • each platform have PortUtil class which implement what is defined in the header
    • WinPortUtils.cpp for Windows
    • PosixPortUtils.cpp for Linux in general
    • MacPortUtils.cpp for Mac OS X thing that deviates from Linux
    • etc.
      (in the sources you can see also platform like Android, Solaris, etc.)

let’s see VMPI_getDate() for example

in PosixPortUtils.cpp

double VMPI_getDate()
{
    struct timeval tv;
    struct timezone tz; // Unused

    gettimeofday(&tv, &tz);
    double v = (tv.tv_sec + (tv.tv_usec/kMicroPerSec)) * kMsecPerSecond;
    double ip;
    ::modf(v, &ip); // strip fractional part
    return ip;
}

in WinPortUtils.cpp

#define FILETIME_EPOCH_BIAS ((LONGLONG)116444736000000000)
#define FILETIME_MS_FACTOR (10000.0)

static double NormalizeFileTime(FILETIME* ft)
{
    LARGE_INTEGER li;
    li.LowPart = ft->dwLowDateTime;
    li.HighPart = ft->dwHighDateTime;

    return ((double) (li.QuadPart - FILETIME_EPOCH_BIAS)) / FILETIME_MS_FACTOR;
}

static double NormalizeSystemTime(SYSTEMTIME* st)
{
    FILETIME ft;
    SystemTimeToFileTime(st, &ft);
    return NormalizeFileTime(&ft);
}

double VMPI_getDate()
{
    SYSTEMTIME stime;
    GetSystemTime(&stime);
    return NormalizeSystemTime(&stime);
}

The implementation is different but it does work like an interface, the function name, the parameters, the return type are all the same.

Also, the type used are all “primary” types, eg. if you need to deal with strings you pass around a char array, not a Stringp class (which we usually use when doing native code in the VM).

And so how all that is used ?

in C++ it allows you to have only one Date class that can be used by any platforms

namespace avmplus
{
    /**
     * Date is used to provide the underpinnings for the Date class.
     * It is a layer over OS-specific date/time functionality.
     */
    class Date
    {
    public:
        Date();
        Date(const Date& toCopy) {
            m_time = toCopy.m_time;
        }
        Date& operator= (const Date& toCopy) {
            m_time = toCopy.m_time;
            return *this;
        }
        Date(double time) { m_time = TimeClip(time); }
        Date(double year,
             double month,
             double date,
             double hours,
             double min,
             double sec,
             double msec,
             bool utcFlag);
        ~Date() { m_time = 0; }

    private:
        double m_time;
    }
}

namespace avmplus
{

    Date::Date()
    {
        m_time = VMPI_getDate();
    }

    Date::Date(double year,
               double month,
               double date,
               double hours,
               double min,
               double sec,
               double msec,
               bool utcFlag)
    {
        if (year < 100) {
            year += 1900;
        }
        m_time = MakeDate(MakeDay(year, month, date),
                          MakeTime(hours, min, sec, msec));
        if (!utcFlag) {
            m_time = UTC(m_time);
        }
    }

}

Later on, you can find the DateClass which is the native definition for the AS3 class Date and reuse the C++ Date above internally.

All that is how we do cross-platform stuff in Redtamarin, we just continued to do what was done in Tamarin/avmplus, but we just added more stuff :smile:.

This also explain why we don’t use libraries like Boost or POCO, because we don’t need to, the VMPI is our own cross-platform library.

And from that point here come CLIB or the “C Standard library for AS3”.

CLIB started small and was merely used this and there to make some C function calls deemed useful like getlogin() to obtain the current user, etc.

And one day we had this “eureka” moment, I remember quite well it was with the open() function (before you could find it in System.popen(), now it is Program.open()).

This open() function is basically using the C function popen() (pipe open) to execute a command-line, read the standard output and return the result as a string.

It was posted on Corsair blog in 2013
Before And After, Contemplating what have been done
*this is a stub, I still got the archive, long story short I want to republish this blog but implemented in AS3 so I didn’t restored the backup yet

as you can see I reuse old blog post to create about the same years later lol
anyway, what I posted contained those

Before, in RedTamarin you had something called System.popen()
that you could use like that

import avmplus.System;

trace( System.popen( "ping -c 3 www.google.com" ) );

//output
/*
PING www.l.google.com (74.125.230.81): 56 data bytes
64 bytes from 74.125.230.81: icmp_seq=0 ttl=57 time=13.421 ms
64 bytes from 74.125.230.81: icmp_seq=1 ttl=57 time=13.054 ms
64 bytes from 74.125.230.81: icmp_seq=2 ttl=57 time=12.545 ms

--- www.l.google.com ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 12.545/13.007/13.421/0.359 ms

*/

The C++ implementation

    Stringp SystemClass::popenRead(Stringp command)
    {
        if (!command) {
            toplevel()->throwArgumentError(kNullArgumentError, "command");
        }

        StUTF8String commandUTF8(command);
        FILE *read_fp;
        char buffer[BUFSIZ + 1];
        int chars_read;

        VMPI_memset(buffer, '\0', sizeof(buffer));
        read_fp = VMPI_popen(commandUTF8.c_str(), "r");
        Stringp output = core()->newStringUTF8( "" );
        
        if (read_fp != NULL) {
            chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp);
            output = output->append( core()->newStringUTF8(buffer, chars_read) );
            
            while(chars_read > 0) {
                buffer[chars_read - 1] = '\0';
                chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp);
                output = output->append( core()->newStringUTF8(buffer, chars_read) );
            }
            
            VMPI_pclose(read_fp);
            return output;
        }
        
        return NULL;
    }

and then I showed an AS3 implementation using the CLIB

import C.stdio.*;
import flash.utils.ByteArray;

/* Executes the specified command line and returns the output.
*/
public static function popenRead( command:String ):String
{
        //we pipe open the command into a FILE stream
	var fp:FILE = popen( command, "r" ); //r is for read
	var output:String = ""; //to save the output
        
        //if the file pointer exists we successfully opened the stream
	if( fp )
	{
                //we prepare a bytearray to save the data
		var bytes:ByteArray = new ByteArray();

                //we read 100 bytes from the stream into the data
		var read:int = fread( bytes, 100, fp ); //will return the number of bytes read
		
                /* for all the time there are bytes available
                   read will return the number of bytes read
                   at the end of the file it will return zero
                */
		while( read > 0 )
		{
                        //we continuously save the number of bytes read
			read = fread( bytes, 100, fp );
                        //we keep reading 100 bytes at a time
                        //100 is completely arbitrary you can pick any number
                        //the max limit is 4294967296 bytes or uint.MAX_VALUE or 4GB
		}

                //we close the stream
		pclose( fp );

                //we write the saved bytes into a string
		output += bytes.readUTF();
		return output;
	}

        //if for some reason the stream did not open we just return an empty string
	return "";
}

And after that for the next few years I kept at implementing this CLIB :smile:.

But why ?

Here what I saw at the time

  • humm I already got stuff like VMPI_popen(), VMPI_close(), etc.
  • I just need to add “a bit more” (cute but naive)
  • and then if I map all those VMPI functions to their C functions counterpart
    VMPI_popen to C.stdio.popen
    VMPI_fread to C.stdio.fread
    VMPI_pclose to C.stdio.pclose
    etc.
  • I could basically write a lot of the code logic directly in AS3
    “as if” I was writing C code

And yeah agreed the concept is not really “new”, if you just look at PHP, Python, etc.
they are already doing that for years

But, thanks to AS3 packages, I was able to define it a bit better in a C main package
with “header includes” sub-package.

In C you would have

#include <stdio.h>

in AS3 you have

import C.stdio.*;

Which I find more elegant and precise than what are doing PHP, Python, etc.

But what all this have to do with sockets you could ask ?
well… a lot !!!

Once your mind is set to develop a “more or less” standard C library for AS3,
you try to find and replicate a standard, so I first started with what you can find in the C standard library.

But this is a bit disappointing as the C standard library doesn’t really do much,
then you go one step higher and you look into the POSIX standard (Portable Operating System Interface).

And so you decide you gonna implement the C POSIX library because its very nature match your new found philosophy

The C POSIX library is a specification of a C standard library for POSIX systems. It was developed at the same time as the ANSI C standard. Some effort was made to make POSIX compatible with standard C; POSIX includes additional functions to those introduced in standard C.

And when it come to sockets, you don’t think only in term of implementing the BSD socket API,
you are thinking of what is defined in POSIX

  • <arpa/inet.h>
  • <net/if.h>
  • <netdb.h>
  • <netinet/in.h>
  • <netinet/tcp.h>
  • <sys/select.h>
  • <sys/socket.h>

It makes things clearer but not necessarily more simple

the pros are

  • it is much simpler to map a C function to a definition in VMPI
    eg. bind() become VMPI_bind()
  • the AS3 definitions are also simpler
    we just declare the native function in its respective package
    which follow the naming of the C header
    bind() is declared in <sys/socket.h>
    so we define its native counterpart in C.sys.socket.*
  • if the implementation differ between platforms
    we do that in the VMPI function, still a bit of C code to write
    but much much less that if we were writing a full blown C++ class
  • it gives you access to the system at a very low-level
    and so it is extremely powerful

the cons are

  • there is much more definitions to write
    you are not anymore just adding 1 native C function to a specific thing
    you are implementing a standard with rules etc.
  • POSIX is very friendly under Linux, Mac OS X etc.
    much less under Windows
  • you often feel you are reinventing a library like Boost
    (but not really, things are very different even if some parts are similar)
  • for an AS3 dev, if he/she wants to use your low-level POSIX stuff
    he/she then need to learn the POSIX stuff, and low-level stuff is hard
  • even if the C++ part get simpler you don’t remove completely the need
    to write a lot of logic in C++, so yeah not a perfect solution

the TL;DR is

extremely powerful low-level access to stuff,
but man that low-level stuff is freaking hard to write even in AS3

Yep, that’s the main problem, it’s like the ANE (ActionScript Native Extension)
most dev would have no problem using the final results
but most dev would have a hard time writing the code providing that results

not saying that AS3 dev are “stupid”, but writing ANE, or writing low-level C-like code in AS3,
require a particular mindset and/or is a more advanced task that not everyone want to deal with.

And that is exactly why I’m explaining all this and reintroducing a socket utility class in Redtamarin.

Not everyone want or need to write 100 lines of C-like code in AS3 to just connect to a server with sockets, you would rather use something like (and me too)

var sock:Socket = new Socket();
    sock.connect( "www.google.com", 80 );

for the sake of it, with the CLIB you would do that

import C.arpa.inet.*;
import C.errno.*;
import C.stdlib.*;
import C.netdb.*;
import C.netinet.*;
import C.sys.socket.*;
import C.unistd.*;

var hostname:String             = "www.google.com";
var port:String                 = "80";
var remoteAddress:String        = "";
var remotePort:uint             = 0;
var enableErrorChecking:Boolean = true;

var hints:addrinfo = new addrinfo();
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_family   = AF_UNSPEC;

var eaierr:CEAIrror = new CEAIrror();
var addrlist:Array  = getaddrinfo( hostname, port, hints, eaierr );

if( !addrlist )
{
    if( enableErrorChecking ) { throw eaierr; }
    
    exit( 1 );
}

var i:uint;
var info:addrinfo;
var sockfd:int;
var result:int;

for( i = 0; i < addrlist.length; i++ )
{
    info = addrlist[i];
    
    sockfd = socket( info.ai_family, info.ai_socktype, info.ai_protocol );
    if( sockfd < 0 )
    {
        var e:CError = new CError( "", errno );
        if( enableErrorChecking ) { throw e; }
        
        sockfd = -1;
        break;
    }
    
    result = connect( sockfd, info.ai_addr );
    if( result < 0 )
    {
        var e:CError = new CError( "", errno );
        if( enableErrorChecking ) { throw e; }
        
        sockfd = -1;
        break;
    }

    break;
}

var a:String = inet_ntop( info.ai_family, info.ai_addr );
if( !a )
{
    var e:CError = new CError( "", errno );
    if( enableErrorChecking ) { throw e; }
}
else
{
    remoteAddress = a;
}

if( info.ai_family == AF_INET )
{
    // IPv4
    remotePort = ntohs( info.ai_addr.sin_port );
}
else if( info.ai_family == AF_INET6 )
{
    // IPv6
    remotePort = ntohs( info.ai_addr.sin6_port );
}


trace( "Connected on socket #" + sockfd );
trace( "to hostname:port = " + hostname+":"+port );
trace( "(" + remoteAddress + ":" + remotePort + ")" );

var closed:int = close( sockfd );
if( closed == -1 )
{
    var e:CError = new CError( "", errno );
    if( enableErrorChecking ) { throw e; }
}

trace( "Closed socket #" + sockfd );

exit( 0 );

Which we could agree is more complicated.

If before you had avmplus.Socket, now you will have shell.SocketSystem
which follow the same logic as FileSystem, OperatingSystem, etc.

In short, it provides an API for the low-level stuff like sockets.

The other difference compared to before is that SocketSystem is entirely written in AS3 with the help of the CLIB, that means all the native stuff have been moved to VPMI, it’s not a bunch of abstract and other C++ classes, nope it’s all ActionScript 3.

For convenience, I include it inside the redtamarin runtime “as if” it was a native API,
but that’s just that: a convenience, for those who do not want nor need to write the C-like code stuff.

Before redtamarin 0.4.2, you could find the equivalent code in the httplib, inside the HttpConnection class.

That is also an important difference from before in redtamarin 0.3 with a native Socket class,
you could only use that and not define your own implementation easily (you could but you would have to checkout the sources of Redtamarin, add your own native implementation in C++ and recompile the whole C++ thing which can take quite some time to master).

Now, if you just need socket functionalities you can directly use SocketSystem, it will be there embedded by default and will even work from a shell script with as3shebang, but if somehow you need to do more advanced stuff, you can also reuse the CLIB and provide your own implementation which is still hard (because you need to learn/know POSIX and use it in the context of AS3) but which is also much more easier than compiling your own C++ code.

And that my friends it is the whole philosophy of Redtamarin:

We made things in such a way that you can solve most of your problem
by writing ActionScript 3.0 code and only that

But there are even more differences

  • now this SocketSystem can work with IPv4 and IPv6
    avmglue.Socket was only working with IPv4
  • this is done in such a way that you can add function callbacks
    for example: onConnect(), onSend(), onReceiveProgress(), etc.
    avmglue.Socket could not do that at all especially o the send/receive functions
  • “as is” a library like httplib can either reuse SocketSystem
    or provide its own low-level socket connections mechanism
    all that in pure AS3
  • it will make things like flash.net.Socket much easier to implement
    see for example this earlier prototype

But wait there is more!!!

Remember or before when we described the bind() function ?
let’s look at it again

namespace avmshell
{
    
    bool PosixSocket::Bind(const int port)
    {
        //...
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
        //...
    }
}

see the part INADDR_LOOPBACK ?
this basically tell the socket to listen on the address loopback which is “127.0.0.1”

what if you needed to bind to another local IP address, like “0.0.0.0” or “192.168.0.1” (on your ethernet LAN) ?

With the old native socket class you would be stuck as this kind of details is well hidden deep in the C++ code.

But with our low-level C API it would be trivial to change, we would need to read a bit of documentation like Beej’s Guide to Network Programming and maybe a bit of POSIX on the bind definition.

There we would learn that we can use other options than INADDR_LOOPBACK

  • like AI_PASSIVE
  • or like INADDR_ANY
  • or even define our own IP to bind to
    my_addr.sin_addr.s_addr = inet_addr("10.12.110.57");

So yeah the evil is in the details, but with our CLIB we can go into that kind of details.

For some specific code, being able to access that kind of low-level detail is crucial, no something you would need everyday but good to know that if you need it you can access it.

It’s very specific, for when you write your own server, but it’s important to be able to bind on different local IP, my point being the way it was done before it was unaccessible as too deep in the native code, but after when you rely on something like CLIB then this kind of thing become accessible.

As a developper it gives you a lot of options that are accessible right there in ActionScript 3.0 (no need to recompile in C++).


That’s more or the less the difference between the before and after over a 5 years period.


#2

let me add a bit more (because stupid limit in characters)

Conclusion

From this before to the after we basically learned (and assumed) the importance of the CLIB.

The main differences are we have more and more direct access to the POSIX library itself (and it will keep growing with time), which then allow us to move most of the native implementation like Filesystem, OperatingSystem, and that new SocketSystem from C++ to ActionScript 3.0.

And this define one of our main philosophy: our core focus is the ActionScript 3.0 language.

But it’s not only that, our real strength is the association of an engine AVM2 with that AS3 language, sure it is a “special” way to do it but it ends up being a quite nice API design as it allows developers to go into the guts of the system and do things differently if they want to without the need to recompile everything from C++.

Associated with another one of our core value: we are cross-platform by default.

All that allow us to provide great freedom to the developers as long as they are willing to use the ActionScript 3.0 language and learn our core API.

From CLIB we then provide default API implementations which end up being another API layer: the RNL (Redtamarin Native Library), which allow to implement yet another API layer: AVMGlue (the Flash/AIR API).

But those are not immovable our untouchable, sure some small parts are still implemented in C++, but for most of it those API can be re-implemented as you see fit, if you want or if you need to or simply if you want to experiment.

For example, you could define a PHP library where you would implement most of the PHP functions, or implement a NodeJS/CommonJS library where you would implement Node.js API and made it behave exactly like their original counterpart, or .NET/Java/etc. API, really the sky is the limit.

Those examples to illustrate a third core value: we are not here to be the clone of someone else.

Even if there is a wish to implement AVMGlue so developers coming from Flash/AIR/Flex could find themselves in familiar territory and hesitate less to use Redtamarin, we are not here to clone Flash or AIR or Flex, we are here to accompany them and/or extend them, simply put: if you already use AS3, with Redtamarin you can use it to do more.

Sometimes we have some dev who come to us and say “you should make Redtamarin like Node.js and then we will use it”, it’s not our purpose to clone Node.js in AS3, it is possible but it’s not why we are developing Redtamarin.

We are here to provide the tools (in the general sense of the term) so any developer can adapt Redtamarin to what they want to do, so if you want something like Node.js but done in AS3, then you can pick up those tools, study our low-level API and implement it yourself, at the very worst you will be missing a specific low-level C-like functions or structures, tell us and we will add that.

That’s our job: do the low-level stuff so other dev could do the higher level stuff.

That’s why I took the SocketSystem as a main example along this little journey into the guts of Redtamarin; SocketSystem is our default implementation to use socket easily, if as a dev you are more comfortable with the BSD socket API then you can ignore it and use directly what is defined in the CLIB, if you have a different background and would rather use something like flash.net.Socket you can also do that, or implement your own net.Socket inspired from the Node.js API, our copy the API that exists in Java, .NET or anything else really … you can do that too.

With Redtamarin we bet on AVM2 + AS3 as cross-platform runtime and API to bring system programming, server-side programming, etc. in the hand of ActionScript 3.0 developers.

The day when a developer will chose Redtamarin instead of PHP or Python or Ruby or anything else, because our tools (AVM2, AS3, runtime, API, libraries, etc.) help him/her solve the problem faster or better or any easier way to do things, is the day when we will win this bet :wink:.