Skip to content

Commit 7cecd9a

Browse files
committed
Fixes for HashLink
1 parent c2e14cb commit 7cecd9a

3 files changed

Lines changed: 217 additions & 2 deletions

File tree

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
package polymod.hscript._internal;
2+
3+
#if (!macro && hl)
4+
@:build(polymod.hscript._internal.HLWrapperMacro.buildWrapperClass())
5+
class HLMath extends Math {}
6+
7+
@:build(polymod.hscript._internal.HLWrapperMacro.buildWrapperClass())
8+
@:haxe.warning("-WDeprecated")
9+
class HLStd extends Std
10+
{
11+
public static function is(v:Dynamic, t:Dynamic)
12+
return isOfType(v, t);
13+
14+
public static function isOfType(v:Dynamic, t:Dynamic):Bool
15+
{
16+
// HL oddly uses hl.Bytes for strings sometimes
17+
// We do a hack since we can't just do isOfType(v, hl.Bytes)
18+
if (t == String && !Std.isOfType(v, String))
19+
{
20+
function bytesTest(_:hl.Bytes) {}
21+
try
22+
{
23+
bytesTest(v);
24+
return true;
25+
}
26+
catch (_)
27+
{
28+
return false;
29+
}
30+
}
31+
32+
return Std.isOfType(v, t);
33+
}
34+
}
35+
#else
36+
import haxe.macro.MacroStringTools;
37+
import haxe.macro.TypedExprTools;
38+
import haxe.macro.Context;
39+
import haxe.macro.Expr;
40+
import haxe.macro.Type;
41+
42+
using Lambda;
43+
using haxe.macro.TypeTools;
44+
using haxe.macro.ComplexTypeTools;
45+
using haxe.macro.ExprTools;
46+
using StringTools;
47+
48+
/**
49+
* Macro that generates wrapper fields for substitutes of `std` classes to make them avaliable to Reflection.
50+
* Currently only works for static fields.
51+
*/
52+
class HLWrapperMacro
53+
{
54+
public static macro function buildWrapperClass():Array<Field>
55+
{
56+
var localClass = Context.getLocalClass().get();
57+
var superClass = localClass.superClass;
58+
if (superClass == null) throw 'Class ${localClass.name} does not extend class it wants to wrap';
59+
var cls = superClass.t.get();
60+
var buildFields = Context.getBuildFields();
61+
62+
for (field in cls.statics.get())
63+
{
64+
if (field.isPublic && !buildFields.exists((f) -> f.name == field.name))
65+
{
66+
var wrapper = generateWrapper(field, cls);
67+
if (wrapper != null) buildFields.push(wrapper);
68+
}
69+
}
70+
buildFields.push(generateToString(cls));
71+
72+
return buildFields;
73+
}
74+
75+
static function generateWrapper(field:ClassField, cls:ClassType):Null<Field>
76+
{
77+
if (field == null) throw 'Field is null';
78+
79+
var newField:Field =
80+
{
81+
name: field.name,
82+
doc: field.doc,
83+
meta: null,
84+
pos: field.pos,
85+
access: [APublic, AStatic, AInline],
86+
kind: null
87+
};
88+
89+
function populateNewField(t:Type):Bool
90+
{
91+
return switch (t)
92+
{
93+
case TLazy(lz):
94+
var ty = lz();
95+
return populateNewField(ty);
96+
case TFun(args, ret):
97+
var funcArgs:Array<FunctionArg> = [
98+
for (arg in args)
99+
{
100+
name: arg.name,
101+
opt: arg.opt,
102+
type: Context.toComplexType(arg.t)
103+
}
104+
];
105+
var ret = Context.toComplexType(ret);
106+
var callArgs:Array<Expr> = [for (arg in funcArgs) macro $i{arg.name}];
107+
var funcParams:Array<TypeParamDecl> = [for (p in field.params) {name: p.name, constraints: getConstraints(p.t)}];
108+
var body:Expr = macro $p{[cls.name, field.name]}($a{callArgs});
109+
110+
newField.kind = FFun(
111+
{
112+
args: funcArgs,
113+
params: funcParams,
114+
ret: ret,
115+
expr: doesReturnVoid(ret) ? (macro $body) : (macro return $body)
116+
});
117+
return true;
118+
default: false;
119+
}
120+
}
121+
122+
if (populateNewField(field.type))
123+
{
124+
return newField;
125+
}
126+
else if (field.expr() == null)
127+
{
128+
var overKind = switch (field.kind)
129+
{
130+
case FVar(_, _):
131+
// We're overriding a VARIABLE, it shouldn't be modifiable
132+
FieldType.FProp('default', 'null', Context.toComplexType(field.type), macro $p{[cls.name, field.name]});
133+
default: throw "Not implemented!";
134+
}
135+
136+
return {
137+
name: field.name,
138+
doc: field.doc,
139+
meta: field.meta.get(),
140+
pos: field.pos,
141+
access: [APublic, AStatic],
142+
kind: overKind
143+
};
144+
}
145+
146+
return null;
147+
}
148+
149+
static function generateToString(cls:ClassType):Field
150+
{
151+
return {
152+
name: 'toString',
153+
access: [APublic],
154+
pos: cls.pos,
155+
kind: FFun(
156+
{
157+
args: [],
158+
ret: macro :String,
159+
expr: macro return $v{cls.name}
160+
})
161+
};
162+
}
163+
164+
static function getConstraints(t:Type)
165+
{
166+
return switch (t)
167+
{
168+
case TInst(_.get() => c, _):
169+
switch (c.kind)
170+
{
171+
case KTypeParameter(consts): [for (c in consts) Context.toComplexType(c)];
172+
default: throw 'Invalid class kind, it is not a TypeParameter';
173+
}
174+
case _: throw '$t has not been implemented!';
175+
}
176+
}
177+
178+
static inline function doesReturnVoid(rt:ComplexType)
179+
{
180+
return switch (rt)
181+
{
182+
case TPath(tp): tp.name == "Void";
183+
default: false;
184+
}
185+
}
186+
}
187+
#end

polymod/hscript/_internal/PolymodAbstractScriptClass.hx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ abstract PolymodAbstractScriptClass(PolymodScriptClass) from PolymodScriptClass
225225
@:privateAccess this._interp.error(EInvalidAccess(name));
226226
// throw "field '" + name + "' does not exist in script class '" + this.fullyQualifiedName + "' or super class '" + Type.getClassName(Type.getClass(this.superClass)) + "'";
227227
}
228+
return null;
228229
}
229230

230231
private static function retrieveClassObjectFields(o:Dynamic):Array<String>

polymod/hscript/_internal/PolymodInterpEx.hx

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,8 @@ class PolymodInterpEx extends Interp
239239
{
240240
super.resetVariables();
241241

242-
variables.set("Math", Math);
243-
variables.set("Std", Std);
242+
variables.set("Math", #if hl polymod.hscript._internal.HLWrapperMacro.HLMath #else Math #end);
243+
variables.set("Std", #if hl polymod.hscript._internal.HLWrapperMacro.HLStd #else Std #end);
244244

245245
variables.set("Array", Array);
246246
variables.set("Bool", Bool);
@@ -1120,7 +1120,12 @@ class PolymodInterpEx extends Interp
11201120
{
11211121
try
11221122
{
1123+
#if hl
1124+
// HL is a bit weird with iterators with arguments
1125+
v = Reflect.callMethod(v, v.iterator, []);
1126+
#else
11231127
v = v.iterator();
1128+
#end
11241129
}
11251130
catch (e:Dynamic) {};
11261131
}
@@ -1276,6 +1281,7 @@ class PolymodInterpEx extends Interp
12761281
}
12771282

12781283
var oCls:String = Util.getTypeNameOf(o);
1284+
#if hl oCls = oCls.replace('$', ''); #end
12791285

12801286
// Check if the field is a blacklisted static field.
12811287
if (PolymodScriptClass.blacklistedStaticFields.exists(o) && PolymodScriptClass.blacklistedStaticFields.get(o).contains(f))
@@ -1355,6 +1361,19 @@ class PolymodInterpEx extends Interp
13551361
// #end
13561362
// return result;
13571363
}
1364+
#if (hl && haxe4)
1365+
else if (Std.isOfType(o, Enum))
1366+
{
1367+
try
1368+
{
1369+
return (o : Enum<Dynamic>).createByName(f);
1370+
}
1371+
catch (e)
1372+
{
1373+
errorEx(EInvalidAccess(f));
1374+
}
1375+
}
1376+
#end
13581377

13591378
var abstractKey:String = '$oCls.$f';
13601379
if (PolymodScriptClass.abstractClassStatics.exists(abstractKey))
@@ -1363,6 +1382,12 @@ class PolymodInterpEx extends Interp
13631382
}
13641383

13651384
// Default behavior
1385+
#if hl
1386+
// On HL, hasField on properties returns true but Reflect.field
1387+
// might return null so we have to check if a getter exists too.
1388+
// This happens mostly when the programmer mistakenly makes the field access (get, null) instead of (get, never)
1389+
return Reflect.getProperty(o, f);
1390+
#else
13661391
if (Reflect.hasField(o, f))
13671392
{
13681393
return Reflect.field(o, f);
@@ -1378,6 +1403,7 @@ class PolymodInterpEx extends Interp
13781403
return Reflect.field(o, f);
13791404
}
13801405
}
1406+
#end
13811407
// return super.get(o, f);
13821408
}
13831409

@@ -1386,6 +1412,7 @@ class PolymodInterpEx extends Interp
13861412
if (o == null) errorEx(ENullObjectReference(f));
13871413

13881414
var oCls:String = Util.getTypeNameOf(o);
1415+
#if hl oCls = oCls.replace('$', ''); #end
13891416

13901417
// Check if the field is a blacklisted static field.
13911418
if (PolymodScriptClass.blacklistedStaticFields.exists(o) && PolymodScriptClass.blacklistedStaticFields.get(o).contains(f))

0 commit comments

Comments
 (0)