Array access not allowed on OpenFL movieclips - haxe

UDATED
How do I go about this?
I got this from Main.hx:
function onMouseOver(e:MouseEvent){
if(Std.is(e.currentTarget, MovieClip)){
initializer (cast e.currentTarget,["scaleX",1.5,"scaleY",1.5])
}
}
Then this is the pointed function in my Animation Class
//here if i set mc:Dynamic everything goes great! but when this one
function initializer(mc:MovieClip, vars:Array<Dynamic>){
var varsLength:Int = Math.round(vars.length/2);
for(m in 0...varsLength){
ini[m] = mc[vars[2*m]];
}
}
then when i compile it, an error appears:
Error: Array access is not allowed in flash.display.MovieClip
How do I resolve this?
EDIT:
vars: are properties of the MovieClip, for example when I pass these parameters:
initializer (mcClip1,["scaleX",1.5,"scaleY",1.5])
so:
vars = ["scaleX",1.5,"scaleY",1.5]
and:
ini[m] will store "scaleX" and "scaleY"`
X-Ref: https://groups.google.com/forum/#!topic/haxelang/_hkyt__Rrzw

In AS3, you can access fields of an object via their String name using [] (array access). This is called Reflection.
In Haxe, Reflection works differently - you need to make use of the Reflect API.
It's considered bad practice - it's not type-safe, which means the compiler can do very little to help you with error messages, and it's quite slow as well. This is why the usage makes it very explicit that Reflection is actually going on (while in AS3, this fact is somewhat hidden). Consider if there are other ways of solving this problem that don't require Reflection.
Now, to get back to your example, here's what it would look like in Haxe:
function onMouseOver(e:MouseEvent){
if (Std.is(e.currentTarget, MovieClip)) {
initializer(cast e.currentTarget, ["scaleX", 1.5, "scaleY", 1.5])
}
}
function initializer(mc:MovieClip, vars:Array<Dynamic>) {
for (m in 0...Std.int(vars.length / 2)) {
ini[m] = Reflect.getProperty(mc, vars[2*m]);
}
}
Btw, your loop was running for too long since you only use half of the values in the array - if you don't divide it by two like I did, you'll end up with [scaleX, scaleY, null, null] instead of the desired [scaleX, scaleY].

Related

Why would you use the spread operator to spread a variable onto itself?

In the Google Getting started with Node.js tutorial they perform the following operation
data = {...data};
in the code for sending data to Firestore.
You can see it on their Github, line 63.
As far as I can tell this doesn't do anything.
Is there a good reason for doing this?
Is it potentially future proofing, so that if you added your own data you'd be less likely to do something like data = {data, moreData}?
#Manu's answer details what the line of code is doing, but not why it's there.
I don't know exactly why the Google code example uses this approach, but I would guess at the following reason (and would do the same myself in this situation):
Because objects in JavaScript are passed by reference, it becomes necessary to rebuild the 'data' object from it's constituent parts to avoid the original data object being further modified by the ref.set(data) call on line 64 of the example code:
await ref.set(data);
For example, in MongoDB, when you pass an object into a write or update method, Mongo will actually modify the object to add extra properties such as the datetime it was insert into a collection or it's ID within the collection. I don't know for sure if Firestore does the same, but if it doesn't now, it's possible that it may in future. If it does, and if your original code that calls the update method from Google's example code goes on to further manipulate the data object that it originally passed, that object would now have extra properties on it that may cause unexpected problems. Therefore, it's prudent to rebuild the data object from the original object's properties to avoid contamination of the original object elsewhere in code.
I hope that makes sense - the more I think about it, the more I'm convinced that this must be the reason and it's actually a great learning point.
I include the full original function from Google's code here in case others come across this in future, since the code is subject to change (copied from https://github.com/GoogleCloudPlatform/nodejs-getting-started/blob/master/bookshelf/books/firestore.js at the time of writing this answer):
// Creates a new book or updates an existing book with new data.
async function update(id, data) {
let ref;
if (id === null) {
ref = db.collection(collection).doc();
} else {
ref = db.collection(collection).doc(id);
}
data.id = ref.id;
data = {...data};
await ref.set(data);
return data;
}
It's making a shallow copy of data; let's say you have a third-party function that mutates the input:
const foo = input => {
input['changed'] = true;
}
And you need to call it, but don't want to get your object modified, so instead of:
data = {life: 42}
foo(data)
// > data
// { life: 42, changed: true }
You may use the Spread Syntax:
data = {life: 42}
foo({...data})
// > data
// { life: 42 }
Not sure if this is the particular case with Firestone but the thing is: spreading an object you get a shallow copy of that obj.
===
Related: Object copy using Spread operator actually shallow or deep?

Is it possible to get the name of variable in Groovy?

I would like to know if it is possible to retrieve the name of a variable.
For example if I have a method:
def printSomething(def something){
//instead of having the literal String something, I want to be able to use the name of the variable that was passed
println('something is: ' + something)
}
If I call this method as follows:
def ordinary = 58
printSomething(ordinary)
I want to get:
ordinary is 58
On the other hand if I call this method like this:
def extraOrdinary = 67
printSomething(extraOrdinary)
I want to get:
extraOrdinary is 67
Edit
I need the variable name because I have this snippet of code which runs before each TestSuite in Katalon Studio, basically it gives you the flexibility of passing GlobalVariables using a katalon.features file. The idea is from: kazurayam/KatalonPropertiesDemo
#BeforeTestSuite
def sampleBeforeTestSuite(TestSuiteContext testSuiteContext) {
KatalonProperties props = new KatalonProperties()
// get appropriate value for GlobalVariable.hostname loaded from katalon.properties files
WebUI.comment(">>> GlobalVariable.G_Url default value: \'${GlobalVariable.G_Url}\'");
//gets the internal value of GlobalVariable.G_Url, if it's empty then use the one from katalon.features file
String preferedHostname = props.getProperty('GlobalVariable.G_Url')
if (preferedHostname != null) {
GlobalVariable.G_Url = preferedHostname;
WebUI.comment(">>> GlobalVariable.G_Url new value: \'${preferedHostname}\'");
} else {
WebUI.comment(">>> GlobalVariable.G_Url stays unchanged");
}
//doing the same for other variables is a lot of duplicate code
}
Now this only handles 1 variable value, if I do this for say 20 variables, that is a lot of duplicate code, so I wanted to create a helper function:
def setProperty(KatalonProperties props, GlobalVariable var){
WebUI.comment(">>> " + var.getName()" + default value: \'${var}\'");
//gets the internal value of var, if it's null then use the one from katalon.features file
GlobalVariable preferedVar = props.getProperty(var.getName())
if (preferedVar != null) {
var = preferedVar;
WebUI.comment(">>> " + var.getName() + " new value: \'${preferedVar}\'");
} else {
WebUI.comment(">>> " + var.getName() + " stays unchanged");
}
}
Here I just put var.getName() to explain what I am looking for, that is just a method I assume.
Yes, this is possible with ASTTransformations or with Macros (Groovy 2.5+).
I currently don't have a proper dev environment, but here are some pointers:
Not that both options are not trivial, are not what I would recommend a Groovy novice and you'll have to do some research. If I remember correctly either option requires a separate build/project from your calling code to work reliable. Also either of them might give you obscure and hard to debug compile time errors, for example when your code expects a variable as parameter but a literal or a method call is passed. So: there be dragons. That being said: I have worked a lot with these things and they can be really fun ;)
Groovy Documentation for Macros
If you are on Groovy 2.5+ you can use Macros. For your use-case take a look at the #Macro methods section. Your Method will have two parameters: MacroContext macroContext, MethodCallExpression callExpression the latter being the interesting one. The MethodCallExpression has the getArguments()-Methods, which allows you to access the Abstract Syntax Tree Nodes that where passed to the method as parameter. In your case that should be a VariableExpression which has the getName() method to give you the name that you're looking for.
Developing AST transformations
This is the more complicated version. You'll still get to the same VariableExpression as with the Macro-Method, but it'll be tedious to get there as you'll have to identify the correct MethodCallExpression yourself. You start from a ClassNode and work your way to the VariableExpression yourself. I would recommend to use a local transformation and create an Annotation. But identifying the correct MethodCallExpression is not trivial.
no. it's not possible.
however think about using map as a parameter and passing name and value of the property:
def printSomething(Map m){
println m
}
printSomething(ordinary:58)
printSomething(extraOrdinary:67)
printSomething(ordinary:11,extraOrdinary:22)
this will output
[ordinary:58]
[extraOrdinary:67]
[ordinary:11, extraOrdinary:22]

Perl 6 - Is it possible to create an attribute trait that set a meta-attribute?

I try to create an attribute trait. The use case is to mark some attributes of a class as "crudable" in the context of an objects-to-documents-mapping while other are not.
role crud {
has Bool $.crud is default(True);
}
multi trait_mod:<is>(Attribute $a, crud, $arg) {
$a.container.VAR does crud($arg);
}
class Foo {
has $.bar is rw;
# Provide an extra nested information
has $.baz is rw is crud(True);
}
By reading and adapting some example code, I managed to get something that seems to do what I want. Here is a snippet with test case.
When I instantiate a new Foo object and set the $.bar attribute (that is not crud), it looks like that:
.Foo #0
├ $.bar is rw = 123456789
└ $.baz is rw = .Scalar+{crud} #1
└ $.crud +{crud} = True
What I understand from this is that the $.baz attribute got what I call a meta-attribute that is independent from its potential value.
It looks good to me (if I understood correctly what I did here and that my traits use is not a dirty hack). It is possible to reach $foo.baz.crud that is True. Though, I don't understand very well what .Scalar+{crud} means, and if I can set something there and how.
When I try to set the $.baz instance attribute, this error is returned:
Cannot modify an immutable Scalar+{crud} (Scalar+{crud}.new(crud => Bool::True))
in block <unit> at t/08-attribute-trait.t line 30
Note: This is the closest thing to a working solution I managed to get. I don't need different crud settings for different instances of instantiated Foo classes.
I never want to change the value of the boolean, in fact, once the object instantiated, just providing it to attributes with is crud. I am not even interested to pass a True or False value as an argument: if it would be possible to just set the boolean trait attribute to True by default, it would be enough. I didn't manage to do this though, like:
multi trait_mod:<is>(Attribute $a, :$crud!) {
# Something like this
$a.container.VAR does set-crud;
}
class Foo {
has $.bar is rw;
has $.baz is rw is crud;
}
Am I trying to do something impossible? How could I adapt this code to achieve this use case?
There are several things going on here. First of all, the signature of the trait_mod looks to be wrong. Secondly, there appears to be a bad interaction when the name of a trait is the same as an existing role. I believe this should be an NYI exception, but apparently it either goes wrong in parsing, or it goes wrong in trying to produce the error message.
Anyways, I think this is what you want:
role CRUD {}; # since CRUD is used as an acronym, I chose to use uppercase here
multi trait_mod:<is>(Attribute:D $a, :$crud!) { # note required named attribute!
$a.^mixin: CRUD if $crud; # mixin the CRUD role if a True value was given
}
class A {
has $.a is crud(False); # too bad "is !crud" is invalid syntax
has $.b is crud;
}
say "$_.name(): { $_ ~~ CRUD }" for A.^attributes; # $!a: False, $!b: True
Hope this helps.

How to get protobuf.js to output enum strings instead of integers

I'm using the latest protobuf.js with Node.js 4.4.5.
I currently struggle to get protobuf.js to output the string definitions of enums instead of integers. I tried several suggestions, but none of them worked:
https://github.com/dcodeIO/ProtoBuf.js/issues/97
https://github.com/dcodeIO/protobuf.js/issues/349
I guess it's because of API changes in protobuf.js for the first one. For the second one, I can use the suggested solution partially, but if the message is nested within other messages, the builder seems to fall back to using the integer values, although the string values have been explicitly set.
Ideally, I'd like to overwrite the function which is used for producing the enum values, but I have a hard time finding the correct one with the debugger. Or is there a better way to achieve this for deeply nested objects?
The generated JS code from protoc has a map in one direction only e.g.
proto.foo.Bar.Myenum = {
HEY: 0,
HO: 1
};
Rationale for this is here but you have to the reverse lookup in your own JS code. There are lots of easy solutions for this. I used the one at https://stackoverflow.com/a/59360329/449347 i.e.
Generic reverse mapper function ...
export function getKey(map, val) {
return Object.keys(map).find(key => map[key] === val);
}
UT ...
import { Bar } from "js/proto/bar_pb";
expect(getKey(proto.foo.Bar.Myenum, 0)).toEqual("HEY");
expect(getKey(proto.foo.Bar.Myenum, 1)).toEqual("HO");
expect(getKey(proto.foo.Bar.Myenum, 99)).toBeUndefined();

Why does my Groovy AST transform insert null at the end of my method?

I have written an AST transform that creates a setter for a JPA mapped property (it both sets the local field and calls setOwner on the other end of the relationship):
private static void createSetter(FieldNode field) {
Parameter parameter = GeneralUtils.param(field.getType(), field.getName());
BlockStatement body = new BlockStatement();
body.addStatement(assignS(fieldX(field), varX(parameter)));
MethodCallExpression setterCall = callX(varX(parameter), "setOwner", varX("this", field.getDeclaringClass()));
setterCall.setType(ClassHelper.VOID_TYPE);
body.addStatement(stmt(setterCall));
MethodNode method = new MethodNode(setterName(field.getName()), ACC_PUBLIC, ClassHelper.VOID_TYPE, new Parameter[] {parameter}, ClassNode.EMPTY_ARRAY, body);
field.getDeclaringClass().addMethod(method);
}
This works, but the generated method has a strange null statement at the end as disassembled by JD-GUI (in addition to an odd local variable):
public void setMore(Simple_MoreStuff more) {
Simple_MoreStuff localSimple_MoreStuff = more;
this.more = localSimple_MoreStuff;
more.setOwner(this);
null;
}
It doesn't seem to affect the actual correctness, but it's odd, and it seems like a bug. In MethodCallExpression, I found this comment but don't know if it relates, since my method is in fact void (I explicitly set it above, and it makes no difference):
//TODO: set correct type here
// if setting type and a methodcall is the last expression in a method,
// then the method will return null if the method itself is not void too!
// (in bytecode after call: aconst_null, areturn)
Is there a way to keep the generated method from having the spurious null?
I have not looked at JD-GUI, so I cannot tell how capable this tool is in understanding bytecode, that does not come from Java. But in general disassemblers can only somewhat show what Java code in that case might look like, by no means it is supposed to show correct code from a non-Java language. So better do not expect correct Java code if you disassemble Groovy.
In this case I suspect that JD-GUI stumbles over a workaround we have not gotten rid of yet. In several cases we add at the method end dead code, the const_null, areturn you have noticed. We do this because of problems with the verifier if a bytecode label is used at the end of a method. And since the dead code does not influence correctness we are currently using this solution.

Resources