Haxe to SWC - protected getters/setters - actionscript-3

I am porting a library from AS3 to Haxe and I need to make protected getters/setters. Target is a *.swc file.
My Haxe code looks like this:
private var foo(get, never):Int;
#:getter(foo)
private function get_foo():Int {
return 0;
}
private var bar:Int;
However the produced *.swc file is a little different:
native public function get foo():int;
native protected var bar:int;
Is there any known workaround?

It seems that #:getter and -D swf-protected (or #:protected) don't play well together. Only one or the other is applied...
Example:
class Test {
var foo(get, never):Int;
#:protected #:getter(foo) private function get_foo():Int return 0;
var bar(get, never):Int;
#:getter(bar) #:protected private function get_bar():Int return 0;
}
generates:
protected function get get_foo() : int { return 0; }
public function get bar() : int { return 0; }
You should open an issue on the official repository.

You could try add swf-protected global compiler flag

Related

Flex actionscript, get data from AsyncToken call

I have Flex/Java project with blazeDS. Now I have an actionscript file that call a method of another actionscript that call the remoteObject (java class who make a simple select on db)
Here's the code:
Home.as
..
private var _dm:DataManager = new DataManager;
public function getPerson():void { // this is connect to a button in .mxml
_dm.getPerson();
}
..
DataManager.as
public class DataManager {
private var _service:RemoteObject;
private var _url:URLRequest;
private var loCs:ChannelSet = new ChannelSet();
public function DataManager () {
_service = new RemoteObject("PeopleDAO");
loCs.addChannel(new AMFChannel("canale", "http://localhost:8080/FlexTRYOUT/messagebroker/amf"));
_service.channelSet = loCs;
}
private function onFault(event:FaultEvent,token:Object):void {
var _fail:String = "fault";
}
private function onResult(event:ResultEvent,token:Object):void {
per = event.result as People; // is a bean class
Alert.show(per.nome);
}
public function getPerson():void {
var token:AsyncToken = _service.getPersona();
token.addResponder(new AsyncResponder(onResult,onFault));
}
}
The call works fine, it calls java method names getPerson() of the DataManger.java class. It return simply one object with name and surname (it's just a hello world to understand this damned AsyncCall). The problem is that I don't know how send this result to Home.as with a classic (java) return type. I have the result in onResult method and I don't know how to get it.
I try to follow Brian instructions and I just waste my time. Maybe because I'm not a flex actionscript programmer but I added the code Brian posted and:
public function getPerson():void { // this is connect to a button in .mxml
_dm.addEventListener(DATA_RECEIVED, onPersonFound); * compile error 1
_dm.getPerson();
}
error is DATA_RECEIVED is undefined
than in DataManager:
public class DataManager {
public static const DATA_RECEIVED:String = "DATA_RECEIVED";
...
private function onResult(event:ResultEvent,token:Object):void {
per = event.result as People; // is a bean class
dispatchEvent(new DataReceivedEvent(DATA_RECEIVED, per)); * compile error 2
}
}
error 2 is call of possible undefined method dispatchEvent
Where is the mistake? Please guys write the complete code because I'm on flex - actionscript - blazeds from two days and I have a few time to try solution. Thanks
OK, Sorry for all this post, I just create new one (but more elaborated and clear) with the same question. Step by Step I'm studing this language and I manage to implement the Brian code but DataManager.as class must extend EventDispatcher, if I don't extend this I have the compile error I posted. At moment I mangage to obtain the resultEvent data in the method defined in the addEventListener call (onPeopleFound in this case). Thanks a lot Brian I think I surely need your help again in future (at least until acceptance of the project). Bye
You can adjust method getPerson to have two parameters referencing the callback functions.
public function getPerson(onResultCallback:Function, onFaultCallback:Function):void {
var token:AsyncToken = _service.getPersona();
token.addResponder(new AsyncResponder(onResultCallback,onFaultCallback));
}
This way you can receive data in an instance of the class you need.
One option is to dispatch an event when you get the data back from the Java call:
Home.as
...
public function getPerson():void { // this is connect to a button in .mxml
_dm.addEventListener(DATA_RECEIVED, onPersonFound);
_dm.getPerson();
}
private function onPersonFound(dataEvent:DataReceivedEvent):void {
var person:People = dataEvent.people;
//Do important processing...
}
...
In DataManager.as
public class DataManager {
public static const DATA_RECEIVED:String = "DATA_RECEIVED";
...
private function onResult(event:ResultEvent,token:Object):void {
per = event.result as People; // is a bean class
dispatchEvent(new DataReceivedEvent(DATA_RECEIVED, per));
}
}
And DataReceivedEvent.as will look like the answer to How to dispatch an event with added data - AS3
public class DataReceivedEvent extends Event
{
public static const DATA_RECEIVED:String = "DATA_RECEIVED";
// this is the object you want to pass through your event.
public var result:Object;
public function DataReceivedEvent(type:String, result:Object, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
this.result = result;
}
// always create a clone() method for events in case you want to redispatch them.
public override function clone():Event
{
return new DataReceivedEvent(type, result, bubbles, cancelable);
}
}

Re-defining named functions at runtime

What I am trying to do is kind of odd, but I am wondering if anyone can come up with a clever way to do what I want to do. Basically, I want to re-define a named function at runtime. I can do this with anonymous functions, but I can't figure out a way to do it for named functions. I want to do this so that I can implement a "spy" functionality on an object for a testing framework (a port of Jasmine to Flex).
Take, for instance, this class:
public class TestClass
{
public var anonymous:Function = function():void {
trace("original anonymous");
};
public function named():void {
trace("original named");
}
}
I can easily re-define the anonymous function because it is just a variable. Javascript uses this idiom a lot.
var testClass:TestClass = new TestClass();
testClass.anonymous = function():void { trace("overridden anonymous"); }
BUT, when I do the same thing for named functions, you get a compile-time error:
// Does not compile
testClass.named = function():void { trace("overridden named"); }
I tried to make it a bit more "squishy" but this leads to a runtime failure "Cannot assign to a method named on TestClass".
// Compiles with runtime failure
testClass["named"] = function():void { trace("overridden named"); }
Can anyone more clever than I come up with a way to hack this? Can the bytecode be hijacked? Something?
I want to modify an object, not a
class
But object doesn't contain functions, only non-static variables. I tried to use prototype property and replace method there, but original method still gets called instead of injected one.
About "hack" bytecode, do you mean "hack" already loaded SWF in runtime? I think it's not possible. I'm sure, though, you can parse SWF with something like as3swf, find method in bytecode, replace it and save result in new SWF.
I had an idea bout making a function "cache" . This might work with what you need.
Let's say you have a class "Car" with a method you need to redefine at runtime:
public class Car extends Sprite
{
private var functionCache:Function;
public function Car()
{
super();
}
public function flexibleFunction(functionBody:*=null):void{
if(functionBody is Function){
functionBody.call();
functionCache=functionBody;
} else {
functionCache(functionBody);
}
}
}
Usage:
public class Main extends Sprite
{
private var car:Car;
public function Main()
{
car = new Car();
car.flexibleFunction(function(){trace("redefine test #1")});
car.flexibleFunction();
car.flexibleFunction(function(doParametersWork:String="let's see"){trace("redefine test #2: " + doParametersWork);});
car.flexibleFunction("yes they do");
car.flexibleFunction();
}
}
an easy way to accomplish what you want is to simply pass a new function to the original function and execute it from there:
package
{
//Imports
import flash.display.Sprite;
//Class
public class RedefineFunction extends Sprite
{
//Constructor
public function RedefineFunction()
{
originalFunction();
originalFunction(redefinedFunction);
}
//Original Function
public function originalFunction(redefinition:Function = null):void
{
if (redefinition != null)
redefinition();
else
trace("Original Function Definition");
}
//Redefined Function
private function redefinedFunction():void
{
trace("Redefined Function Definition")
}
}
}
traces:
Original Function Definition
Redefined Function Definition

Does AS3 provide any way to stop a returned object being modified?

If an AS3 method returns a reference to a complex type, is there any way to make that 'readonly', like how you can have const member functions in C++? An architecture I want to use calls for a class building itself from a passed template object... and really the template object should not be modifiable. I'm currently forced to add call-back enumerators and/or lots of extra accessor methods.
Flex has an ObjectUtil.clone() method that will make a deep copy. The copy will still by modifiable, but since it's a copy, the changes won't propagate back to the original.
The method is no complicated so if you're not using Flex, just add this to a util class:
public static function copy(value:Object):Object
{
var buffer:ByteArray = new ByteArray();
buffer.writeObject(value);
buffer.position = 0;
var result:Object = buffer.readObject();
return result;
}
There is no way to do that in AS3, there is Sam's way of doing it, but it still requires copying that object before you return it, depending on the complexity of that object, it can impact the performance.
Immutable interfaces are a near-equivillant to const-correctness. Here's an example:
interface CPoint {
function get x():Number;
function get y():Number;
}
class Point implements CPoint {
private var _x:Number;
private var _y:Number;
public function get x():Number { return _x; }
public function get y():Number { return _y; }
public function set x(val:Number) { _x = val; }
public function set y(val:Number) { _y = val; }
public function normalize():void {
var length:Number = Math.sqrt(_x*_x + _y*_y);
_x /= length;
_y /= length;
}
public function Point(x:Number, y:Number) {
_x = x; _y = y;
}
}
If you return a Point as a CPoint reference, then its fields cannot be altered. You can do an explicit cast to a Point from a CPoint to force access, but you can do the same thing with const casting in C++.
Unfortunately, AS3 doesn't support covariance like it should, so things get unnecessarily difficult for const sub-objects. For example, if you had a Line class that was made up of two points, you might want to say line.start.x = 47; if you have full access to the line, but allow reading of line.start.x through an immutable interface. You could do this if there was covariance, but instead you'll need to add separate get properties for mutable and immutable properties. So, you'd end up instead with line.cstart.x for reads from a CLine. Something like this:
interface CLine {
function get cstart():CPoint;
function get cend():CPoint;
}
class Line implements CLine {
private var _end:Point;
private var _start:Point;
public function get cend():CPoint { return _end; }
public function get cstart():CPoint { return _start; }
public function get end():Point { return _end; }
public function get start():Point { return _start; }
public function Line(x1:Number, y1:Number, x2:Number, y2:Number) {
_start = new Point(x1, y1);
_end = new Point(x2, y2);
}
}
I would create a flash.utils.proxy object. You could create a proxy object that has read only implementation of a child that is passed in.
Here is the documentation for creating a proxy object. http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/utils/Proxy.html
Note: Proxy is pretty damn slow, since you'll be bypassing native object checking, and replacing it with a function call -- which when using a lot will be slow. I would do some simple performance testing first.
note: This is pseudo-code.
use namespace flash_proxy;
dynamic class ReadOnly extends flash.utils.Proxy {
private var target:Object;
public function ReadOnly(target:Object) {
this.target = target;
}
flash_proxy function getProperty(name:*):*
return target[name];
}
flash_proxy function setProperty(name:*, value:*):void
// throw an error or do nothing
}
}
You could then do:
var readOnly:ReadOnly = new ReadOnly(stage.loaderInfo.parameters);
readOnly.someparameter = 'newvalue';
trace(readOnly.someparameter); // should be old value

Ambiguous reference when getter/setter have different visibilities

The following code raises an ambiguous reference to value at compile time:
import flash.display.Sprite;
public class Main extends Sprite
{
private var _value : Number = 0.;
public function get value() : Number { return _value; }
private function set value(v : Number) : void { _value = v; }
public function Main() : void
{
value = 42.;
}
}
I suspect some kind of bug in the compiler, though I didn't actually read the ECMA standard.
Before someone asks those questions:
Private setters do make sense.
The ambiguity also exists with custom namespaces (which is the problem I'm facing).
it is indeed a bug in the compiler, and it is listed in the bugs. its stated that its an oversite of the developers and wont be fixed any time soon.
if you are needing to specifically run a function to set privately (rather than just assign the value, in which case you can leave out the setter function completely and itll run) then you will have to run a seperate function as Sandro said.
I think this may be a limitation of AS3. You could create a private function called setValue() or if your set on having a setter you might be able to get away with this, although it's not very pretty.
package {
import flash.display.Sprite;
public class Main extends Sprite {
private var __value :Number = 0;
public function Main(): void {
_value = 42;
}
public function get value():Number {
return __value;
}
private function set _value(v:Number):void {
__value = v;
}
}
}

Creating a custom trace() class in AS3

I got this idea of expanding my trace() messages.
Why
trace() is all over my code, I want to turn them on/off by a simple command and maybe add some sort of priority functionality to the trace(), i.e.
myTrace.TraceMsg("loosehere",debugme, 0);
myTrace.TraceMsg("winhere",debugme, 1);
And when I run, only the one with the higher priority, "1" in this case, shows.
There is a lot more functionality I would like to add as well, like logging messages to file and so on.
Problem
How do trace() work? -Is it possible to overload trace() somehow? -How would I implement the custom TraceMsg(what code here?) method?
Having some serious problems finding info on this subject on our favourite search engine, so any help would be appreciated.
I have come up with a rather efficient, yet tedious way of using my own trace() function in Flash only projects, but calling it simply with
trace("this", "that", "and that too");
I basically implement one trace() method in every class of my project, that calls a public function (so that i can call the real trace() function from there.
here is what I do : in every class I call this
include "trace_implementation.as";
in the .as file comes a simple method implementation (it could be a static method too).
public function trace(... arguments){
for(var i in arguments){
myTrace(arguments[i]);
}
}
and the myTrace function is defined in its own myTrace.as file
package pt.utils{
import flash.external.ExternalInterface
public function myTrace(_s:String):void{
trace(_s);// this will call the original flash trace() function
ExternalInterface.call("console.log", _s);// to get traces outside of flash IDE
/*implement what you want here*/
}
}
so now when I compile with "omit trace actions", my whole debugging is ignored as if I used trace() simply.
the really good part here is that you could implement custom actions depending on instructions you give in the trace, so :
trace(Debug.DEBUG_MESSAGE, "message to output in debug");
trace(Profile.START_PROFILING, this, 'name');
/*do heavy code*/
trace(Profile.STOP_PROFILING, this);
then dispatch it from myTrace, or a Tracer class or anything :)
Hope this helps future tracers.
trace() itself is a top-level function, not a class, so unfortunately we cannot extend it. That being said, we can utilize it in a simple class to do just what it does normally, only in this case the trace is based on conditions (i.e. Boolean - true|false, etc). First we create the Trace class, which we wouldn't instantiate ourselves because we are utilizing a Factory design pattern through the class below, Tracer. Tracer is built around the singleton design pattern, yet utilizes the Factory pattern to instantiate instances of Trace, when the trace method of Tracer is called.
//This class is handled by Tracer, which is right below it.
//You WILL NOT instantiate these, nor hold references.
package
{
public class Trace
{
private function _value:*;
private function _trace:Boolean;
public function Trace(pValue:*, pTrace:Boolean):void
{
_value = pValue;
_trace = pTrace;
}
public function get value():*
{
return _value;
}
public function get trace():Boolean
{
return _trace;
}
}
}
//This is the important class and the only one you will work with.
package
{
/**
*Utilizes Singleton and Factory design patterns.
*/
public class Tracer
{
private var _traceArray:Array;
private static var _instance:Tracer;
public function Tracer(pvt:PrivateClass = null):void
{
if(pvt == null)
{
throw(new Error("You cannot instantiate this class directly, please use the static getInstance method."));
}
_init();
}
public static function getInstance():Tracer
{
if(Tracer._instance == null)
{
Tracer._instance = new Tracer(new PrivateClass());
}
return Tracer._instance;
}
public function trace(pValue:*, pTrace:Boolean):void
{
var trace:Trace = new Trace(pValue, pTrace);
if(trace.pTrace)
{
trace(pValue);
}
}
//Since we have the option for individual traces to be disabled
//I provide this to get access to any and all later.
public function traceAll():void
{
traceStr:String = _traceArray.toString();
}
public function get traceables():Array
{
return _traceArray;
}
//Here we provide a method to trace all, even if set to false in their constructor.
private function _init():void
{
_traceArray = new Array();
}
}
}
//Here we create a class that is OUTSIDE of the package.
//It can only be accessed from within this class file. We use this
//to make sure this class isn't instantiated directly.
class PrivateClass
{
function PrivateClass():void
{
trace('can only be accessed from within this class file');
}
}
//Now for use in doc class
package
{
import flash.display.Sprite;
import flash.events.Event;
//No need to import Tracer and Trace, they are also in the
//unnamed package.
public class DocumentClass extends Sprite
{
private var _tracer:Tracer;
public function DocumentClass():void
{
if(stage) _init();
else addEventListener(Event.ADDED_TO_STAGE, _init);
}
private function _init(e:Event = null):void
{
_tracer = Tracer.getInstance();
_tracer.trace(10*20, false);
_tracer.trace(10*20, 0); //SAME AS ABOVE
_tracer.trace("I love AS3", true); //traces
_tracer.traceAll(); //Would trace: 200, 200, I love AS3
}
}
}
Keep in mind this is off the hip and very well could have a bug or two, but the idea is there; That is to say that this is not tested, it is merely to give you an idea of how you might implement this.
I hope this helps.
Look at the Flex logging API, particularly the section: Implementing a custom logger with the logging API.
Look up the TraceTarget class as well.
You can't override trace itself, but for ease of typing I like to create a global function called 'tr'. It's a little known fact that you can create global functions in AS3, but it's easy.
Create a file called tr.as inside you main source directory (not in a subdirectory or package), with the contents:
package {
public function tr(msg:String, ...):void {
// add custom trace logic here
trace("tr message: "+msg);
}
}
If you need to have a lot of logic or static storage variables etc, it might be better to make a separate static class, and have the global tr function call out to that, such as:
package {
import org.code.MyTracer;
public function tr(msg:String, ...):void {
MyTracer.tr(msg); // all the tracing logic is inside the MyTracer class
}
}
Here is a super simple custom trace function I use. debugFlag can be set to true/false
elsewhere in the package.
public static function myTrace(... vars) :void {
if (debugFlag) {
var output:Array = new Array;
for each (var arg in vars) {
output.push(arg);
}
trace(output);
}
}
In AS2, it was possible to override the global trace function by doing something like this (taken from memory, might be a bit wrong but the gist of it is there):
public static var realTrace:Function = _global["trace"];
// This is put in some init code somewhere
_global["trace"] = myTrace;
public static function myTrace(... args):void
{
// Do whatever you want with args here, build a nice formatted string or whatever
// before passing to realTrace. Using with MTASC one could add line numbers, class
// names and all sorts of nice meta data. Or just return should you want to turn
// tracing off.
realTrace.apply(args);
}
Unfortunately I haven't found a way to do the same in AS3. Yet.
Trace is a top-level function, so you can't override it, and as far as I know, it does not fire any events. Since it's a top-level function (not contained in any named package), you can use it without import statements.
Here is an example of a top-level "Tracer" class that you can use in place of trace without import statements.
Just call "Tracer.write" or "Tracer.writeError" for tracing Error objects.
"Tracer.write" accepts a variable number of arguments, just like the built-in trace function. "Tracer.writeError" is a helper method that allows you to easily trace Error objects.
Features:
Calls built-in trace.
Keeps a log of all your calls to Tracer.write as an array of strings.
The call log is accessible as a string through getText, which joins all elements in the array with a newline character and will optionally tack on line numbers!
Fires events when new lines are added to the log, so if you have some kind of display window for the log, the display window can listen for Tracer events to update the log display in real-time as the events occur. This is great for displaying trace events when running inside a web browser or stand-alone player.
-Tracer class definition
package
{
import flash.events.EventDispatcher;
public class Tracer extends EventDispatcher
{
private static var traced_text:Array = new Array( "--Start of Trace Log--" );
public static var enabled:Boolean = true;
private static var suspended:Boolean = false;
public static var instance:Tracer = new Tracer();
public static const newline:String = "\n"; //workaround for TextField.appendText bug.. use "\n" instead of "\r". See note and link to bug post in getText method
public function Tracer()
{
}
static public function write( ...args ):void
{
if (enabled && !suspended)
{
trace.apply( null, args );
var text:String = args.join( newline );
var next_index:int = traced_text.length;
traced_text.push( text );
suspended = true; //prevent recursive calls from TracerEvent handler
instance.dispatchEvent( new TracerEvent( text, next_index ) );
suspended = false;
}
}
static public function writeError( e:Error ):void
{
write( "errorID: " + e.errorID, "errorName: " + e.name, "errorMessage: " + e.message, "stackTrace: " + e.getStackTrace() );
}
static public function getText( include_line_numbers:Boolean ):String
{
var line_count:int = traced_text.length;
var lines:Array = traced_text; //store pointer to traced_text; pointer may be changed to reference an altered array that includes line numbers
if (include_line_numbers) //create temporary trace log copy with altered lines; allows quick call to join at end
{
var new_lines:Array = new Array();
for (var i:int = 0; i < line_count; i++)
new_lines.push( i.toString() + ": " + lines[i] );
lines = new_lines;
}
return lines.join( newline ); //do not include last newline character (workaround for bug in appendText method (https://bugs.adobe.com/jira/browse/FP-1982); I have to call appendText with newline character first, otherwise it has issues like not acknoledging the newline thats already there at the end).
}
static public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
{
instance.addEventListener(type, listener, useCapture, priority, useWeakReference);
}
static public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void
{
instance.removeEventListener(type, listener, useCapture);
}
static public function willTrigger(type:String):Boolean
{
return instance.willTrigger(type);
}
static public function hasEventListener(type:String):Boolean
{
return instance.hasEventListener(type);
}
}
}
-TracerEvent class definition
package
{
import flash.events.Event;
public class TracerEvent extends Event
{
public static const WRITE:String = "te_write";
public var text:String;
public var index:int; //index of newly traced text in the traced_text array (trace log)
public function TracerEvent( text:String, index:int )
{
super( WRITE, false, false );
this.text = text;
this.index = index;
}
override public function clone():Event
{
return new TracerEvent( text, index );
}
}
}
As mentioned below, there is no way to override trace (at least not if you want your traces to reach the output stream), but it's actually very easy to create your own universally accessable logging function. Plus, you can even define a universally accessable boolean to turn logging on or off:
log.as (note that the filename must reflect the name of the function)
package {
function log(... arguments):void {
trace("Custom logging FTW!");
if (logEnabled)
trace(arguments);
}
}
logEnabled.as (note that the filename must reflect the name of the variable)
package {
var logEnabled:Boolean = true;
}
Main.as
package {
import flash.display.MovieClip;
public class Main extends MovieClip {
public function Main() {
log("Testing");
logEnabled = false;
log("Testing2");
}
}
}
Response
Custom logging FTW!
Testing
Custom logging FTW!
you dont need to override it , just create a function in your project and call it trace then any trace call will point to this.trace ;)
function trace(... arguments){
yourfunction(arguments);
}