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