Skip to content

Instantly share code, notes, and snippets.

@syg
Created July 3, 2012 05:10
Show Gist options
  • Save syg/3037812 to your computer and use it in GitHub Desktop.
Save syg/3037812 to your computer and use it in GitHub Desktop.
//
// There are 2 types of native classes in playerGlobal:
//
// (1) ActionScript data and methods, but have [native(cls="..")] metadata
// Examples: Matrix, Point, etc
// (2) Native data (accessed via getters and setters) and methods
// Examples: Transform, ApplicationDomain, etc
//
// (1), despite all functionality being in ActionScript, are represented by
// native classes because they need to be accessed by other native
// functions/classes. For example, Transform's matrix setter is native and
// accepts a Matrix object.
//
// If these classes already mirror something you need internally, like if you
// already have a TobiasMatrix as used by your renderer implementation, you
// can add a toNative function on the instance.
//
// (2) are the normal natives, but they may need to read ActionScript
// properties.
//
// If these classes already mirror something you have internally, like if you
// already have a TobiasTransform, you can use glue() as below.
//
// The AVM2-visible object will have a property "d" that points to the
// internal, implementation object, and the implementation object will have a
// property "p" that points to the AVM2-visible object.
//
//
// To read an ActionScript property, such as point's "x", that has no native
// counterpart, just use the mangled name directly, like "p.public$x".
//
// To read an ActionScript property that has a backing, like DisplayObject's
// "x", use the d-pointer, like "displayObject.d.x".
//
//
// This leaves the question of native classes that don't mirror something
// that's there internally and isn't needed by AVM1 or anything else. Suppose
// we have such a class C, then the CClass() function should implement
// everything itself without a separate internal implementation object, since
// nothing internal needs it anyways.
//
//
// Example 1:
//
// flash.geom.Matrix and flash.geom.Transform, assuming that there are
// internal representations of TobiasMatrix and TobiasTransform.
//
function MatrixClass(runtime, scope, instance, baseClass) {
var c = new Class("MatrixClass", instance, C(instance));
c.extend(baseClass);
instance.toNative = function () {
return new TobiasMatrix(this.public$a, this.public$b,
this.public$c, this.public$d,
this.public$tx, this.public$ty);
};
return c;
}
function TransformClass(runtime, scope, instance, baseClass) {
var c = new Class("TransformClass", instance, C(instance));
c.extend(baseClass);
// XXX: I think it's safe to cache classes using the creation-time domain,
// since we know these to be in builtins or player globals.
const domain = runtime.domain;
const MatrixInstance = domain.getClass("flash.geom.Matrix").instance;
c.nativeMethods = {
ctor: function (displayObject) {
glue(new TobiasTransform(displayObject.d), this);
},
"get matrix": function () {
// |this.d.matrix| is a TobiasMatrix
var m = this.d.matrix;
return new MatrixInstance(m.a, m.b, m.c, m.d, m.tx, m.ty);
},
"set matrix": function (m) {
this.d.matrix = m.toNative();
}
// Other methods and getters and setters
};
}
//
// Example 2:
//
// flash.geom.Matrix and flash.geom.Transform, assuming that these are only
// ever needed by ActionScript and nothing internal.
//
function MatrixClass(runtime, scope, instance, baseClass) {
var c = new Class("MatrixClass", instance, C(instance));
c.extend(baseClass);
// Do nothing special.
return c;
}
function TransformClass(runtime, scope, instance, baseClass) {
var c = new Class("TransformClass", instance, C(instance));
c.extend(baseClass);
// XXX: I think it's safe to cache classes using the creation-time domain,
// since we know these to be in builtins or player globals.
const domain = runtime.domain;
const MatrixInstance = domain.getClass("flash.geom.Matrix").instance;
c.nativeMethods = {
ctor: function (displayObject) {
// Assuming that display objects are backed by some implementation via
// its d-pointer.
this.target = displayObject.d;
},
"get matrix": function () {
var target = this.target;
return new MatrixInstance(
target.scaleX,
target.rotation * Math.PI / 180,
target.scaleY,
target.x,
target.y
};
},
"set matrix": function (m) {
var target = this.target;
// Since m is now not backed by any implementation object and has no
// d-pointer, just read its properties directly via the mangled names.
//
// In this case all these properties are Numbers which are reflected
// directly.
var sx = Math.sqrt(m.public$d * m.public$d + m.public$c * m.public$c);
var sy = Math.sqrt(m.public$a * m.public$a + m.public$b * m.public$b);
target.scaleX = m.public$a > 0 ? sx : -sx;
target.rotation = Math.atan(m.public$a / m.public$b) * 180 / Math.PI;
target.scaleY = m.public$d > 0 ? sy : -sy;
target.x = m.public$tx;
target.y = m.public$ty;
}
// Other methods and getters and setters
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment