Created
July 3, 2012 05:10
-
-
Save syg/3037812 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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