May 18, 2007

Tamarin gotchas

If you have used the Flex SDK and want to use Tamarin, you will soon realize that there are some bits of differences in the way you can import libraries (*.abc files) and compile your custom code under Tamarin.

When you're compiling avmplus, by default the resulting binary will contain the core builtins as Object, String, Array, etc. what you may call the global library, and will also contain avmplus dedicated classes as avmplus.System, avmplus.File, etc. what you may call the shell library.

Now let create our first little program, let's say we are tired to use asc.jar and we would like to have a nice asc.exe.

asc.as


package
    {
    
    import avmplus.System;
    
    var cmdline = "java -jar asc.jar";
    
    if( System.argv.length > 0 )
        {
        cmdline += " " + System.argv.join( " " );
        }
    
    System.exec( cmdline );
    
    }

let's compile it
c:\>java -jar asc.jar asc.as
asc.abc, 205 bytes written
let's run it
c:\>avmplus asc.abc
ActionScript 3.0 for AVM+
version 1.0 build d628
Copyright (c) 2003-2004 Macromedia, Inc.
Copyright (c) 1998-2003 Mountain View Compiler Company
All rights reserved
...
cool it seems to work, now let's make that exe.
c:\>java -jar asc.jar -exe avmplus.exe asc.as
asc.abc, 205 bytes written
asc.exe, 467157 bytes written
and run it
c:\>asc
ActionScript 3.0 for AVM+
version 1.0 build d628
Copyright (c) 2003-2004 Macromedia, Inc.
Copyright (c) 1998-2003 Mountain View Compiler Company
All rights reserved
...
great it's working.


But what if we would like to compile asc.as in strict mode ?
c:\>java -jar asc.jar -strict asc.as
[Compiler] Error #1172: Definition avmplus:System could not be found.
   asc.as, Ln 5, Col 20:
       import avmplus.System;
   ...................^

[Compiler] Error #1120: Access of undefined property System.
   asc.as, Ln 9, Col 9:
       if( System.argv.length > 0 )
   ........^

[Compiler] Error #1120: Access of undefined property System.
   asc.as, Ln 11, Col 26:
           cmdline += " " + System.argv.join( " " );
   .........................^

[Compiler] Error #1120: Access of undefined property System.
   asc.as, Ln 14, Col 5:
       System.exec( cmdline );
   ....^

4 errors found
Ok there are errors but this is still good news, now we know that we can compile a source code that use imports without using strict mode and the bytecode keep the reference to the import and so if the import is correct in the end the compiled program will work.
The other good news is if we use strict mode the compiler tell us about that missing import and give us a pretty detailled list of where the errors are.

So here the first gotcha, even if the avmplus.exe contain the bytecode for the class avmplus.System, if you want to compile in strict mode and import that class you will need to compile the shell library and use it as an import when compiling your source code.

The shell library are all the *.as files than can be found in \js\tamarin\shell\, to compile them in one library just define your own shell.as that will include those files.

shell.as

include "shell\\ByteArray.as"
include "shell\\Domain.as"

include "shell\\Endian.as"

include "shell\\StringBuilder.as"

include "shell\\UIntArray.as"
include "shell\\UShortArray.as"
include "shell\\ShortArray.as"
include "shell\\IntArray.as"
include "shell\\FloatArray.as"
include "shell\\DoubleArray.as"

include "shell\\Java.as"

include "shell\\Dictionary.as"

include "shell\\toplevel.as"

You can do it anyway you want, me to keep it simple, I copied all the \js\tamarin\shell\*.as files to \sandbox\shell\*.as and I compile the lib from \sandbox\shell.as.
And you compile it like that
c:\>java -jar asc.jar shell.as
But here again you will have another gotcha, it does not compile (yep even in non-strict mode), you will obtain these errors
[Compiler] Error #1017: The definition of base class Object was not found.
   D:\sources\opensource\Tamarin\sandbox\shell\ByteArray.as, Ln 1, Col 1:
   /* ***** BEGIN LICENSE BLOCK *****
   ^
...
Here the compiler want the base class Object, and this one you can only have it by compiling the global library.

To compile the global library, you take all the files from \js\tamarin\core\*.as and you copy them to \sandbox\core\*.as, and you compile it from \sandbox\global.as.

shell.as

include "core\\builtin.as"

include "core\\ErrorConstants.as"
include "core\\Error.as"

include "core\\Math.as"

include "core\\Date.as"
include "core\\RegExp.as"
include "core\\XML.as"

Take into account that builtin.as already include those files
include "Object.as"
include "Class.as"
include "Function.as"
include "Namespace.as"
include "Boolean.as"
include "Number.as"
include "String.as"
include "Array.as"
include "actionscript.lang.as"
Don't change the order of the includes, it's important (and logic, Object should always be the first class defined for ex).

Ok, let's compile global.as
c:\>java -jar asc.jar global.as
global.abc, 31729 bytes written
now we can compile shell.as
c:\>java -jar asc.jar -import global.abc shell.as
shell.abc, 3864 bytes written
and finally we can compile our asc.as in strict mode
c:\>java -jar asc.jar -import shell.abc -strict asc.as
[Compiler] Error #1046: Type was not found or was not a compile-time constant:
 String.

[Compiler] Error #1046: Type was not found or was not a compile-time constant:
 String.

[Compiler] Error #1046: Type was not found or was not a compile-time constant:
 uint.

[Compiler] Error #1046: Type was not found or was not a compile-time constant:
 int.
...
ahem... stuck again. Let's explain why.
You need the shell.abc library to be able to reference avmplus.System, but as soon as you use this library you will also need the global.abc library that define the base classes as String, int, uint, etc.

When earlier we compiled shell.abc importing global.abc, the final shell.abc file contained only the code defined in shell.as, not the codes defined in global.as, so you see now our problem is to be able to use the 2 libraries at the same time, and yes it is a problem because asc.jar only allow you to import 1 library, not 2.

So in fact what we need is a tamarin library that contain the bytecodes of shell.as and global.as.

Tamarin.as

include "global.as"

include "shell.as"


c:\>java -jar asc.jar Tamarin.as
Tamarin.abc, 35787 bytes written
And now we can compile asc.as in strict mode
c:\>java -jar asc.jar -import Tamarin.abc -strict asc.as
asc.abc, 200 bytes written
but as ultimately we want an exe
c:\>java -jar asc.jar -exe avmplus.exe -import Tamarin.abc -strict asc.as
asc.abc, 200 bytes written
asc.exe, 467152 bytes written
Now we are ready to write full blown command line tools in ES4 :), but that will be in another post.

ps: asc.exe wil work fine as long as you keep asc.jar in the same directory

Be the first to comment