Moved to msoy area.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4019 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -1,349 +0,0 @@
|
||||
This document contains a couple of notes about some design decisions
|
||||
and some notes about flash that you may find useful.
|
||||
|
||||
TODO
|
||||
----
|
||||
- Write code that processes a dobj class in java and outputs the
|
||||
corresponding class in actionscript. This is sorta fucked because
|
||||
we want to exclude things not applicable to client code, not because
|
||||
we're trying to save every byte in the class definition, but because
|
||||
some of those methods involve whole classes we don't need on the as client.
|
||||
- Write code that generates actionscript service, listener and marshaller
|
||||
classes from a java Service class definition.
|
||||
|
||||
|
||||
Design decisions
|
||||
----------------
|
||||
- I have kept accessors named like their Java counterparts, rather
|
||||
than embracing flash's property setter/getter methods (which are really
|
||||
cool), but I am starting to lean the other way and may rewrite some stuff.
|
||||
|
||||
- I am embracing flash's event distribution model because it saved me a bunch
|
||||
of work.
|
||||
|
||||
- We could use the setter methods on DObject properties to generate dobj
|
||||
events, but so far I haven't gone there.
|
||||
|
||||
- We need a realistic HashMap implementation. Using Object properties
|
||||
(a-la my SimpleMap) is not going to cut it because keys must always
|
||||
be Strings.<strike>and there's no way to *really* remove a value from
|
||||
an Object (you can set the property to null, but now the property is
|
||||
forever defined: the key is not cleared)</strike>
|
||||
***Update: the 'delete' operator removes properties.
|
||||
|
||||
mx.utils.UIDUtil.getUID() can be used to generate a (huge) unique String
|
||||
for any object for use as a key or something.
|
||||
|
||||
It might be worth waiting, I think it's very probable that Adobe will
|
||||
add in a Hashtable class to the standard libraries...
|
||||
|
||||
- Since we cannot do streaming via reflection like we do in Java, each
|
||||
Streamable class needs to define its own readObject/writeObject methods.
|
||||
At one point we thought that maybe we could just write the class
|
||||
and have a script examine the class definition and automatically generate
|
||||
those two methods, but I don't think that's going to save us much.
|
||||
Variables cannot be marked as transient, and we often have to change the
|
||||
type locally: actionscript has Number and int which correspond to
|
||||
float/double and int/short/byte when we stream to the server. So we'd have
|
||||
to do a bunch of crazy comment annotations on each variable to be streamed
|
||||
in any class and at that point we may as well just write the streamable
|
||||
methods, IMO.
|
||||
|
||||
|
||||
Notes
|
||||
-----
|
||||
- In actionscript, 'package' is simply a block command to sweep whatever is
|
||||
defined inside the block so that it's in that package. This means
|
||||
that in addition to classes being in a package, freestanding functions
|
||||
and I believe variables and constants can be in a package.
|
||||
|
||||
We are not putting freestanding functions anywhere. Make a util class with
|
||||
static methods.
|
||||
|
||||
- ActionScript does not have inner classes. Only one public class may be
|
||||
defined in a file, and the filename must match the public class.
|
||||
However, protected classes cannot be defined within the package block!
|
||||
So it seems like the model is:
|
||||
|
||||
package com.foo {
|
||||
public class FooBar {
|
||||
// stuff
|
||||
}
|
||||
} // end: package foo
|
||||
|
||||
class HelperClass {
|
||||
// helper stuff
|
||||
}
|
||||
|
||||
|
||||
To me, this makes it seem as if the helper class is now globally scoped,
|
||||
which of course is the exact opposite of what is desired. This may
|
||||
not be the case, I haven't played with it much yet.
|
||||
|
||||
What especially sucks is that any imports must be repeated down below
|
||||
for the helper class, including importing the class defined just above.
|
||||
Again, it's unclear to me whether those imports are now globally scoped
|
||||
and will spill over onto other files... What a giant pain.
|
||||
|
||||
***Update: it turns out that the primary class in a file may be declared
|
||||
with internal accessibility. So HelperClass could live in its own file
|
||||
and access 'internal' methods on the main class. That is probably
|
||||
preferable to having them in the same file but having to re-import anyway
|
||||
and accessing only public properties of the main class from the helper.
|
||||
|
||||
|
||||
- Sandboxing classes is done with ApplicationDomains. When we load a sub-swf
|
||||
we'll want to put it into a different domain so that nothing malicious
|
||||
can be done to our classes.
|
||||
|
||||
- <strike>constructors do not defaultly call super()- be sure to do it explicitely.
|
||||
Maybe we should get in the habit of doing it in Java for consistency and
|
||||
explicitness.</strike>
|
||||
CORRECTION: super() is called implicitely, just as in Java.
|
||||
|
||||
- It's annoying how there can be only one constructor: if you have classA
|
||||
that has a 1-arg constructor and it is extended by classB, then the implicit
|
||||
super() is inserted, but this results in runtime error because the classA
|
||||
constructor is not being passed an arg. You'd think this would be caught
|
||||
at compile time...
|
||||
|
||||
- The RENDER Event is dispatched prior to each rendering, it's
|
||||
basically like tick(): it gives anything that cares a chance to update
|
||||
prior to being painted. It doesn't specify what the hell to listen on for
|
||||
this event, but since all DisplayObjects are event dispatchers then
|
||||
listening on any display object (including the stage) should work...
|
||||
|
||||
But, the damn thing doesn't get dispatched if there will be no render,
|
||||
even if the code is still running- like when the flash player window is
|
||||
minimized or obscured. Lovely.
|
||||
|
||||
I will play around with trying to just use a Timer with a 1ms interval,
|
||||
and see if the frequency is limited to the actual framerate.
|
||||
|
||||
- All methods must be marked with the 'override' keyword if they override
|
||||
a method in their parent, except for toString(), even though it's defined
|
||||
for Object. Apparently those methods are 'magic' and are not really in
|
||||
the base class. What an annoying inconsistency.
|
||||
|
||||
- 'protected' means something slightly different from java: other classes
|
||||
in the same package cannot access protected members, only subclasses may:
|
||||
|
||||
Java Class Package Subclass World
|
||||
private Y N N N
|
||||
<default> Y Y N N
|
||||
protected Y Y Y N
|
||||
public Y Y Y Y
|
||||
|
||||
ActionScript
|
||||
private Y N N N
|
||||
internal Y Y N N
|
||||
protected Y N Y N
|
||||
public Y Y Y Y
|
||||
|
||||
|
||||
- Beware of non-existant integer math:
|
||||
|
||||
var i :int = 3;
|
||||
var o :Object = someArray[i / 2];
|
||||
// o is now undefined, because we accessed array element "1.5".
|
||||
// I think arrays are just hashes, so probably you could store
|
||||
// values at element 1.5 if you desired...
|
||||
|
||||
- Similarly, methods in String take Number arguments (wha?) for character
|
||||
index positions. Totally nonsensical.
|
||||
|
||||
- Hey! Array has two constructors! How can I do that?
|
||||
- Probably they have one constructor with varargs, and it simply checks
|
||||
to see if there is only 1 arg and if it's an int, and then does something
|
||||
different. Although, we can't really be sure, because these classes are
|
||||
magic and special and don't have a corresponding .as file we can check out.
|
||||
|
||||
- I've been casting using 'as':
|
||||
var s :String = (someObject as String);
|
||||
|
||||
But I've learned that there's another way that didn't seem to be listed
|
||||
anywhere in the language reference but is more like what we'll want:
|
||||
var s :String = String(someObject);
|
||||
|
||||
The difference is that the first one tries to coerce the value to be
|
||||
of the specified type, and if it fails returns null. The second is
|
||||
more like a cast in Java, in that if it fails it generates an Error at
|
||||
runtime.
|
||||
|
||||
Note that if the types are coercable, each one will succeed in the same way:
|
||||
var o :Object = 2.5; // create a Number object
|
||||
var x :int = (o as int);
|
||||
var y :int = int(o);
|
||||
// both of these work and turn the Number 2.5 into int 2.
|
||||
|
||||
Perhaps we'll want a util method that always generates an error if the
|
||||
object's type is not identical or a subclass of the casted-to type.
|
||||
|
||||
***Update:
|
||||
var o1 :String = null;
|
||||
var o2 :String = String(o1); // ends up being "" or something
|
||||
|
||||
The 2nd kind of cast destroys null, at least for String. So fuck that,
|
||||
I was trying to use it when pulling a value out of a hash, but if it
|
||||
wasn't there it got booched.
|
||||
|
||||
***Update:
|
||||
Just not casting is "implicit casting" and will result in a type error
|
||||
at runtime. This may be what we want, but it's maddening that there's
|
||||
no way to do it explicitely.
|
||||
|
||||
var o1 :String = someObject;
|
||||
// checked at runtime, throws TypeError if failure
|
||||
|
||||
Also, when the compiler is in strict mode it flags this code, so
|
||||
we can't win.
|
||||
|
||||
I will sum up in a table:
|
||||
|
||||
* cast using "obj as Type"
|
||||
+ helps compile-time type checking
|
||||
- turns non-castable objects into null rather than generating an exception
|
||||
* casting using "Type(obj)"
|
||||
+ helps compile-time type checking
|
||||
- will coerce primitive types between each other, the most annoying
|
||||
problem being:
|
||||
var o1 :Object = null;
|
||||
var s1 :String = String(o1);
|
||||
assert(s1 === "null");
|
||||
* implicit casting ("var s :String = o")
|
||||
+ it will generate a proper TypeError at runtime
|
||||
- no compile-time checking, strict compiler generates an error (!!!)
|
||||
|
||||
|
||||
- Pitfall! This is perfectly legal:
|
||||
var b :int = 3;
|
||||
var b :int = 4;
|
||||
|
||||
This will generate a compile warning:
|
||||
var b :int = 3;
|
||||
var b :String = "three";
|
||||
It generates the warning on assigning 3 to b, because it has looked
|
||||
into the future and decided that b is a String, even though it's an
|
||||
int on that line.
|
||||
|
||||
And:
|
||||
|
||||
var b :int = 3;
|
||||
for (var ii:int = 0; ii < b; ii++) {
|
||||
var b :Number = 3.3;
|
||||
}
|
||||
trace(b); // prints "3.3", even though we've left the loop
|
||||
|
||||
|
||||
- AS3.0 allows for a bit of introspection, using the function
|
||||
flash.util.describeType(). The only problem is that if you pass in a Class
|
||||
then it always says that it's final (I guess it's the class's Class). It
|
||||
will dump information identical to the information given about an instance
|
||||
except that the dynamic/final information is lost. This is preventing
|
||||
me from correctly streaming arrays, as we need to know if the class
|
||||
is final. I can't just pass an instance in because it may be a pain
|
||||
to construct, it may even be unconstructable if the type of the array
|
||||
is an interface. Posted as a request for enhancement on the AS3.0 forums.
|
||||
|
||||
|
||||
- Actionscript's property accessors are a cool feature, but beware hidden
|
||||
performance issues: accessing a simple property of a variable
|
||||
(like myArray.length) may actually be executing arbitrary code, possibly
|
||||
creating many objects, each time.
|
||||
|
||||
- <strike>Classes without a constructor cannot be instantiated. This is a
|
||||
runtime error (grraah!).</strike>
|
||||
There is a compiler option "-compiler.warn-no-constructor"
|
||||
but it generates a flotilla of warnings from standard classes in the flash
|
||||
library, so it's slightly useless.
|
||||
***Update: What the heck. I noticed today that DSet has no constructor
|
||||
and I've never had any trouble instantiating those. Why would not having
|
||||
a constructor be an error for some classes and not others? Grraahh!
|
||||
***Update: The language spec says that a default (no arg) constructor
|
||||
is created implicitely if one is not defined. Probably when I got
|
||||
the error that a class had no constructor it was some sort of compiler
|
||||
bug.
|
||||
|
||||
- Static initializers can be emulated:
|
||||
public class A
|
||||
{
|
||||
private static function staticInit () :void
|
||||
{
|
||||
// whatever
|
||||
}
|
||||
|
||||
staticInit(); // will be placed inside the real static initializer
|
||||
}
|
||||
|
||||
- Unlike in Java, most operators are overloaded for strings:
|
||||
if (str1 > str2) { // compares asciibetically
|
||||
|
||||
- It's pissing me off that some classes magically can use array dereferencing
|
||||
([]) to do magical things, but there is no clear indication of which classes
|
||||
support it and which don't: you just have to scan through the class
|
||||
documentation. Array itself is dynamic, supposedly they needed to do that
|
||||
to store things in it, but it shouldn't be used as a dynamic class. Some
|
||||
of the collection-type classes also support []ing as does the arguments
|
||||
class. Those aren't dynamic though: they're just magical, and as far
|
||||
as I can tell there's no way to grant this magic to my own classes.
|
||||
I'd feel better about it if there were some marker interface implemented
|
||||
by all classes that can be []'d.
|
||||
|
||||
- Functions may be declared anywhere, and it seems that they have visibility
|
||||
to any variables around them at that point, as if they were an inner class
|
||||
and the variables were final:
|
||||
|
||||
var list :ArrayCollection = new ArrayCollection();
|
||||
list.addItem(foo);
|
||||
|
||||
var funcy :Function = function (i :int) :void {
|
||||
Log.debug("I can see " + list[0]);
|
||||
};
|
||||
_savedFunc = funcy;
|
||||
|
||||
Then _savedFunc can be called at any later date and it can access list[0]
|
||||
just fine.
|
||||
|
||||
This just might save our butts from insane class proliferation with service
|
||||
listeners.
|
||||
|
||||
- You can't use runtime constants as parameter initializers:
|
||||
|
||||
public const MAX_VALUE :int = 99;
|
||||
|
||||
// this is illegal because MAX_VALUE is not defined until the
|
||||
// static initializer is run for this class. It's not around at compile time.
|
||||
public function getCrap (minValue :int, maxValue :int = MAX_VALUE) :Crap
|
||||
|
||||
- Static constants are not inherited by subclasses. You can make them
|
||||
prototype rather than static and they will be.
|
||||
|
||||
- anonymous class options:
|
||||
|
||||
- pass arrays of functions, with just a convention as to which function is
|
||||
which (no compile-time type checking)
|
||||
- pass objects with functions of the right names attached (no compile-time)
|
||||
- make adapters, as necessary, for interfaces (bleah!) (Still no good
|
||||
compile-time checking, except for the # of args)
|
||||
- add code to verify the object's functions against describeType calls..
|
||||
(would need to iterate on types because describeType only finds methods
|
||||
in the terminal interface. Only # of args can be checked)
|
||||
|
||||
- Private constructors are not allowed, so the official line from Macromedia
|
||||
on creating Singleton classes, I-shit-you-not, is to do this:
|
||||
|
||||
package foo {
|
||||
|
||||
public class Singleton
|
||||
{
|
||||
public static const singleton :Singleton = new Singleton(new SecretClass());
|
||||
|
||||
public function Singleton (secret :SecretClass)
|
||||
{
|
||||
}
|
||||
}
|
||||
} // end: package
|
||||
|
||||
public class SecretClass // inaccessible outside this file
|
||||
{
|
||||
}
|
||||
Reference in New Issue
Block a user