views:

260

answers:

2

I've been writing a system that at runtime generates some templates, and then generates some objects based on those templates. I had the idea that the templates could be extensions of the Class class, but that resulted in some magnificent errors:

VerifyError: Error #1107: The ABC data is corrupt, attempt to read out of bounds.

What I'm wondering is if subclassing Class is even possible, if there is perhaps some case where doing this would be appropriate, and not just a gross misuse of OOP. I believe it should be possible, as ActionScript allows you to create variables of the Class type. This use is described in the LiveDocs entry for Class, yet I've seen no mentions of subclassing Class.

Here's a pseudocode example:

class Foo extends Class

var A:Foo = new Foo(); // A is a class
trace(A is Class); // true, right?
var b = new A(); // b is an instance of class A

I have no idea what the type of b would be. Would it be this?

trace(b is A); // true?

But then, could you type the variable b to type A, as in var b:A = new A();, even though A doesn't exist until runtime? I have read that this is not possible as A is not statically resolvable, but I am doing my own tests to explore this possibility.

Ultimately, the purpose is to generate new classes at runtime, and to then create instances of those classes.

In summary: can you subclass the Class class? If so, how can you do it without errors, and what type are the instances of the instances of the Class subclass?

+5  A: 

From the livedoc in the first paragraph it states Every Class object is an instance of the Class class. Therefore, it would be an error to extend Class as any class object already is a Class.

You said a system that generates some templates, and then generates some objects based on those templates. It sounds like what you are really trying to do is create an interface.

An interface should be used when your template does not provide any implementation. That is, you declare a bunch of fields and methods but provide no implementation of the methods. You would declare such a class as follows:

public interface Foo {
    function bar:void;
}

Now to create a class that implements this interface you simply do the following.

public class MyClass implements Foo {
    function bar:void {
        ..... implementation goes here .....
    }
}

It is possible for a class to implement multiple interfaces, while it may extend only one class.

If however, you want to provide any implementation in your template class then it is likely that you are best off just creating a class that has any commonalities that its subclasses have and just use plain old inheritance.

Hope this helps.

Matt
Matt, thank you for your answer. I'm going to clarify the question a little further, since you've covered the issue as I've presented it rather well, yet I realize now that I didn't fully specify the use I have in mind. Interfaces will not work in this case as an Interface in Actionscript isn't an object per se (though it is a data type, according to the docs), it's a construct used for standardizing a class's public interface. What I'm looking for is an object-representation of a class that may be dynamically manipulated and instantiated at runtime.
spiralganglion
Also, re: your first paragraph: why would it be an error? I don't see that, though it seems I'm experiencing it (see: ABC Error). If every object of type Class is an instance of the Class class, why can I not create a subclass of the Class class, and then create instances of the subclass, since they'll still have the Class class in their ancestry?
spiralganglion
Lastly, I will vote up your answer as soon as I have 15 rep. As this is my first question ever, and today is my first day actively posting on the site (though I've been a long time fan and reader), it'll take me a little bit before I'm all the way on board and able to participate fully. Again, thank you.
spiralganglion
It kind of sounds like you are wanting to alter your class implementation at run time (unless I'm totally misunderstanding you). As far as I'm aware this isn't possible, classes must be fully implemented at run time and the implementation can not be changed during. However, if you want to use a number of different classes interchangeably during run time depending on the situation then you likely would want to create an interface that each of the interchangeable classes would implement. Then you need only to implement the logic that determines which to do. Let me know if this helps :-)
Matt
Yes, you've got it. I want to generate new classes from scratch (well, from 'templates') at runtime. I see this as a sort of metaprogramming, and as Actionscript is capable of some metaprogramming (eg: `eval("x" + i).apply(y, z)`) I'm rather curious to see what other metaprogramming abilities it has. I've read that you possibly cannot type variables to a class dynamically at runtime as they're not statically resolvable, but I still haven't figured out anything about generating new Class instances. Ultimately, this all deals with my own curiosity more than my need to address a particular issue.
spiralganglion
Ahh, I see what you mean now. I have to say I'm curious to see if it works. I'm going to play around with this a little and I'll get back to you. Very interesting...
Matt
So I've done some digging, and discovered that the eval function is not in actionscript 3, it is however in AC2. So are you using AC2? If so then this explains why eval does not work. It is my understanding that AC3 is not intended to support metaprogramming. It is possible that AC2 may support this (kind of) but from it seems that this is not a capability that is intended in actionscript.
Matt
Sorry, I'm not actually using the eval global function. I had forgotten that they removed it from AS3, as I used to use it when I was an AS2 programmer - at least, for experimental purposes. It was always too slow for much practical usage. However, you can still have similar behaviour result from using associative array-style object property access, `myObj["x" + i]`, but yes, this is not really metaprogramming.
spiralganglion
"What I find interesting is that my class that extends Class causes no compile time errors" - I think that this is something that is syntactically correct but not logically correct, which is why it is not a compile error. A class is already a Class so logically it does not make sense to make a class extend something that it already is. I'm pretty sure that you are trying to make the language do something that it was never intended to be capable of. (I could be wrong, but everything I see provides evidence that this can't be done in actionscript).
Matt
I've learned that extending both the Class class and the Function class cause **runtime** errors, but that the error from extending the Function class is actually rather specific (you can't extend a class marked 'final'), and the error from extending the Class class seems rather... well.. mistaken. Compare this with extending a class that doesn't exist: you get a compiler error!
spiralganglion
At this point, I would agree with you Matt. It seems as though this is something Actionscript is not intended to do, and yet.. they haven't expressly disallowed it, it's just not supported.
spiralganglion
But great question! I really found it interesting to research! :-D
Matt
It was worth 5 badges, haha; that seems good for a first question. I'll mark your answer as correct since you nailed it in the first paragraph, but I might later post another answer if I find a way to actually subclass Class (without *cheating*).
spiralganglion
Awesome! Yea if you do find a way to do it I would love to hear about it!
Matt
Well, I think this kills it: the Class class doesn't have a constructor. Try this: `var classObj:Class = new Class();` - it throws this error: `TypeError: Error #1115: Class$ is not a constructor.` That might be what caused the ABC OOB error - there's no constructor for it to read. Not mentioned before: in the stack trace for the ABC error, it cites the error as originating from the $init global! Duh! I'm kicking myself now.
spiralganglion
You can compile actionscript at runtime. It's been done here: http://eval.hurlant.com/. Also, if you want to add properties or methods at runtime, you can make your class dynamic (by default classes are sealed). And also, you can opt-in for ecma-script style (it's a compiler flag) so you can do prototype manipulation, pretty much like in Javascript.
Juan Pablo Califano
Yeah, I wanted to avoid prototypal inheritance as it's a little slower, and this application needs to execute quickly for as many instances as possible. That's also why I wanted to avoid untyped variables, and was looking to type variables to runtime-generated classes. I'm not looking to compile AS at runtime, but that library looks super cool. Thanks for the insight.
spiralganglion
+1  A: 

Here are the findings of my own in-depth research:

Extending the Class class seems to be intrinsically impossible, though this is not well documented anywhere that I've yet seen. From my investigation, I am now convinced that the Class class itself does not include all of the qualities that the other Top Level classes have, even though they all are said to extend from the Object class.

Further frustrating is the fact that subclassing different Top Level classes will result in several different error messages, making it somewhat difficult to tell what issues are at play. To begin with a simple example, if you attempt to subclass many of ActionScript's primitive datatypes (int, uint, Number, String, Boolean, etc), you get the following compiler error:

1016: Base class is final.

This makes sense, because looking at the docs for any of these classes reveals that they are indeed final:

Package       Top Level
Class         public final class Boolean
Inheritance   Boolean -> Object

The final keyword, of course, means that another class may not extend the class marked as final. Now for a more involved example, lets look at an extension of the Function class. The Function class is not final, according to the docs. Since it's not final, does this mean we can extend the Function class to create our own specialized function objects? Here is a definition:

class MyFunction extends Function { /*...*/ }

.. and then at runtime:

VerifyError: Error #1103: Class ::MyFunction cannot extend final base class.

Compare this with the primitive datatype error above. That error happened at compile time, as the inherited primitive class was actually marked as final. The Function class is not marked final but the class still behaves as if it was, only at runtime.

Now, we come to the main question's issue: extending the Class class. As with the Function class, the Class class is not final. Further, the Class class is dynamic, meaning new properties may be added to a Class object at runtime.

As an interesting side-note: The Function class is also dynamic, and I believe this part of what allows for continued support of the old prototypal inheritance mechanisms present in the ECMAscript dialect. In this dialect, Functions are used as classes of a sort (well, prototypes), and the ability for functions to have properties added at runtime is part of the power of prototypal inheritance.

Now, it is my understanding that properties of a Class object are the same as static properties available to any instance of that class. Thus, it should make logical sense that someone might wish to manipulate a Class object at runtime, allowing them to change the behaviour of that class and it's instances. That the Class class is dynamic reinforces this notion.

Since the Class class is not final, I was curious to see if one may extend the Class class, to create their own specialization of the Class model and work somewhere in the meta-language domain. I'll leave for another day the discussion of why someone would want to do this, and what power it would hypothetically allow. Now for the concluding example, let us extend the Class class. Here's a definition:

// the definition causes no errors on its own, even though the compiler "sees" it
class MyClass extends Class { /*...*/ }

/* elsewhere */

MyClass; // the only mention of MyClass beyond the definition

.. and then at runtime:

verify global$init()
                    stack:
                    scope: 
                     locals: global 

/* snip about 120 lines */

   46:getlex 34
                    stack: global Class$?
                    scope: global Object$ Class$ 
                     locals: global 
   48:newclass MyClass$cinit()
VerifyError: Error #1107: The ABC data is corrupt, attempt to read out of bounds.

    at global$init()

Holy stacktrace! The VerifyError is reserved for malformed SWF data. Based on what I could find, this is also how a "bug" in the Flash Player tends to manifest. In either case, this is slightly beyond an ordinary ActionScript error.

At this point, it gets rather difficult to understand exactly what is happening, but this is what I've been able to deduce thus far.

VerifyError: Error #1107: The ABC data is corrupt, attempt to read out of bounds.

I (mistakenly, see below comment) believe that "ABC" stands for Abstract Base Class, which is a term applied to classes that cannot be instantiated, only extended. However, the above terrifying error comes not at a point of instantiation, but at the first access of the MyClass Class subclass. In fact, the example's code, I never once instantiate a MyClass object, I only refer to the MyClass class itself.

I did a few further tests and discovered that Class objects do not appear to have constructors, at least, of the sort that typically come from Object subclasses. Simply typing new Class(); anywhere in your code will nicely demonstrate this fact, but you may investigate this further by inspecting the .constructor property, and with other tricks. As a result of this, instances of the Class class are at best second-class objects, as they cannot be constructed at runtime.

At first I suspected that this was the exact cause of my nasty VerifyError. However, I now believe that there are many other elements to a class, invisible to our own ActionScript code, that may or may not exist within the Class class, Function class, or other curious places. Certainly, when the Flash Player attempts to access one of those necessary for the extension of a base class, and it doesn't exist (as Class is possibly an ABC, thus missing certain elements present in a normal class), one could well expect to see an out of bounds VerifyError.

In summary, extending the Class class looks to be impossible at this time. It would appear that the Class class does not include all of the qualities that most of the other Top Level classes inherit from Object, though this is difficult to test.

I would prefer to see a more specific error message result from extending Class, but at the moment there is no such thing. I would love to see some metaprogramming capability return to ActionScript. For now it's good enough to know conclusively that it cannot be done, at least, in this way.

spiralganglion
ABC in this case stands for ActionScript Bytecode. That trace you posted is the ABC that's executing when the exception is thrown, the last line being the "newclass" opcode. If you check the AVM docs for newclass (http://www.adobe.com/devnet/actionscript/articles/avm2overview.pdf), you'll see that the base classes (in this case, Class and Object) must be on the stack, and their static initializers will be run. Perhaps this is the root of the exception--Class has no static initializer, and looking for it causes an out-of-bounds read.
tclem
tclem, thanks for the link. I had no idea that they provided such a document. I will read over it and revise my post with what I learn.
spiralganglion