Skip to content

Instantly share code, notes, and snippets.

@skial
Created February 20, 2014 14:10
Show Gist options
  • Select an option

  • Save skial/9114412 to your computer and use it in GitHub Desktop.

Select an option

Save skial/9114412 to your computer and use it in GitHub Desktop.
Initial step towards creating `Hijacked` type's. Probably a more appropriate name for this.

Initial step towards creating Hijacked types. Probably a more appropriate name for this.

The final idea would allow you to register with a compiler macro for types/methods/ops you are interested in. This would allow you to know when someone modifies an object with them only marking the type first. With further macros you could remove requiring a user to manually mark a type entirely.

The code attached will create a new abstract that manually forwards all variables/methods. A more polished version would only manually forward calls for variables/methods/ops you have registered interest in and use the compiler meta @:forward for the rest.

Super simplified example.

Haxe.

// registered interest in push, unshift, @:arrayWrite etc
// with the callback inserting a method call after each method
public static function main() {
    var a:Hijacked<Array<String>> = ['a','b','c'];
    a.push('d');
    a.unshift('e');
    a[0] = 'A';
}

public static function updateView() {...}

Javascript output.

Main.main = function() {
	var a = ["a","b","c"];
	a.push("d");
	Main.updateView();
	a.unshift("e");
	Main.updateView();
	a[0] = "A";
	Main.updateView();
};
package ;
import haxe.macro.Type;
import haxe.macro.Expr;
import haxe.macro.Context;
using haxe.macro.Tools;
class MacroHijacked {
#if macro
private static var counter:Int = 0;
public static function build() {
var type = Context.getLocalType();
switch (type) {
case TInst(_, params):
type = params[0];
switch (type) {
case TInst(rcls, params):
var cls = rcls.get();
var ifields = cls.fields.get();
var sfields = cls.statics.get();
var ctype = Context.toComplexType( type );
var others:Array<Array<Field>->Void> = [];
var fields:Array<Field> = [for (field in ifields) if (field.isPublic) {
var _type = field.type.applyTypeParameters(cls.params, params);
{
name: field.name,
access: [APublic, switch(field.kind) { case FMethod(_):AInline; case _:APublic; } ],
kind: switch (field.kind) {
case FVar(_, _):
others.push( function(f) {
f.push( {
name: 'get_${field.name}',
access: [APrivate,AInline],
kind: FFun( { args:[], ret:_type.toComplexType(), expr:Context.parse( 'return this.${field.name}', field.pos ) } ),
pos: field.pos,
} );
} );
FProp('get', 'never', _type.toComplexType(), null);
case FMethod(k):
var typed = field.expr();
var fargs:Array<FunctionArg> = [];
var nargs:Array<String> = [];
var ret:Type = null;
if (typed != null) switch (typed.expr) {
case TFunction(f):
fargs = [for (arg in f.args) {
var _t = try arg.v.t.applyTypeParameters(cls.params, params) catch (_e:Dynamic) arg.v.t;
{ name:arg.v.name, type:_t.toComplexType() };
} ];
nargs = [for (arg in f.args) arg.v.name];
ret = f.t;
case _:
} else switch (field.type) {
case TFun(a, r):
fargs = [for (aa in a) { name:aa.name, type:aa.t.applyTypeParameters(cls.params, params).toComplexType() } ];
nargs = [for (aa in a) aa.name];
ret = r;
case _:
}
var args = nargs.join(",");
ret = ret.applyTypeParameters(cls.params, params);
FFun( {
args: fargs,
ret: ret.toComplexType(),
expr: macro {
return $e { Context.parse( 'this.${field.name}($args)', field.pos ) };
},
params: [for (param in field.params) {
{ name: param.name, params: [{name:param.t.getClass().name, params:[]}] }
}],
} );
},
pos: field.pos,
}
}];
for (other in others) other(fields);
var td = {
name: 'Hijacked${cls.name}_$counter',
pack: [],
pos: cls.pos,
params: [],
kind: TDAbstract( ctype, [ctype], [ctype] ),
fields: fields,
}
Context.defineType( td );
type = Context.getType( td.name );
counter++;
case _:
}
case _:
}
return type;
}
#end
}
package ;
class Main {
static function main() {
var jackedArray:Hijacked<Array<String>> = ['hello'];
}
}
@:genericBuild(MacroHijacked.build())
class Hijacked<T> {
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment