Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/reflaxe/BaseCompiler.hx
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,23 @@ class BaseCompilerOptions {
paramTypes: Null<Array<MetaArgumentType>>,
compileFunc: Null<(MetadataEntry, Array<String>) -> Null<String>>
}> = [];

/**
Converts all static variable declarations to have a self
calling lambda function where needed. This does not affect
variables with a deterministic/constant value.

Useful if your target language does not support blocks as values
for variables, however the target language also needs to support
lambda function declarations and executing code directly on the top stack
(great examples are lua and python).

You can use the ClassVarData.canBeInlined variable to check if
the variable does not need any kind of wrapper. You can also use
it with `convertStaticVarExpressionsToFunctions` turned off to
implement your own system.
**/
public var convertStaticVarExpressionsToFunctions: Bool = true;
}

/**
Expand Down
83 changes: 82 additions & 1 deletion src/reflaxe/ReflectCompiler.hx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ using reflaxe.helpers.ModuleTypeHelper;
using reflaxe.helpers.NameMetaHelper;
using reflaxe.helpers.NullableMetaAccessHelper;
using reflaxe.helpers.TypeHelper;
using reflaxe.helpers.TypedExprHelper;

/**
The heart of Reflaxe.
Expand Down Expand Up @@ -532,7 +533,7 @@ class ReflectCompiler {
if(shouldGenerateVar(field, compiler, isStatic, readVarAccess, writeVarAccess)) {
final data = field.findVarData(cls, isStatic);
if(data != null) {
varFields.push(data);
varFields.push(preprocessVar(compiler, field, data));
} else {
throw "Variable information not found.";
}
Expand Down Expand Up @@ -584,6 +585,86 @@ class ReflectCompiler {
return data;
}


static function preprocessVar(compiler: BaseCompiler, field: ClassField, data: ClassVarData): ClassVarData {
if(data.expr == null) {
return data;
}

final fE = field.buildTField(data.classType);
var e = if (compiler.options.convertStaticVarExpressionsToFunctions) {
data.expr.transformLambaSelfCall(true);
} else {
data.expr.transformAssign(fE);
}

data.setExpr(e);

if(compiler.options.enforceNullTyping) {
NullTypeEnforcer.modifyExpression(data.expr);
}
for(preprocessor in compiler.expressionPreprocessors) {
preprocessor.process(data, compiler);
}

e = {
if (compiler.options.convertStaticVarExpressionsToFunctions) {
switch(data.expr.expr)
{
case TCall(e, _):
switch(e.unwrapParenthesis().expr)
{
case TFunction(tfunc):
final block = tfunc.expr.unwrapBlock();
if (block.length == 1)
{
final b = block[0];
switch(b.expr)
{
case TReturn(e):
e;
case _:
b;
}
}
else
data.expr;

case _: data.expr;
}

case _: data.expr;
}
} else {
data.expr;
}
}

var isInline = e.expr.match(TBinop(_, _, _));
var finalExpr = switch(e.expr)
{
case TBinop(op, e1, e2):
if (op.match(OpAssign) || op.match(OpAssignOp(_)))
{
if (Std.string(e1) == Std.string(fE))
e2;
else if (Std.string(e2) == Std.string(fE))
e1;
else
e;
}
else
e;
case _:
e;
}

data.setExpr(finalExpr);
data.setCanBeInlined(compiler.options.convertStaticVarExpressionsToFunctions ? true : isInline);

return data;
}

// =======================================================
// * transpileEnum
// =======================================================
Expand Down
112 changes: 112 additions & 0 deletions src/reflaxe/data/ClassFieldData.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package reflaxe.data;

#if (macro || reflaxe_runtime)

import reflaxe.preprocessors.ExpressionPreprocessor;
import haxe.macro.Type;

using reflaxe.helpers.ClassFieldHelper;
using reflaxe.helpers.NameMetaHelper;
using reflaxe.helpers.NullableMetaAccessHelper;
using reflaxe.helpers.NullHelper;
using reflaxe.helpers.PositionHelper;
using reflaxe.helpers.TypedExprHelper;

class ClassFieldData {
public final id: String;

public final classType: ClassType;
public final field: ClassField;

public final isStatic: Bool;

public var expr(default, null): Null<TypedExpr>;

var variableUsageCount: Null<Map<Int, Int>>; // Not quite sure if its necessary here, but keep it :P

public function new(id:String, classType: ClassType, field: ClassField, isStatic: Bool, expr:Null<TypedExpr> = null) {
this.id = id;

this.classType = classType;
this.field = field;

this.isStatic = isStatic;

this.expr = expr;
}

/**
Sets the typed expression.
Invalidates any cached information regarding the old expression.
**/
public function setExpr(e: TypedExpr) {
expr = e;

// The expression changed, so the stored usage count data is now invalid.
variableUsageCount = null;
}

/**
Works the same as `setExpr`, but takes an array of expressions.

If just one expression, it is used directly.
Multiple are converted into a block expression.
**/
public function setExprList(expressions: Array<TypedExpr>) {
if(expressions.length == 1) {
setExpr(expressions[0].trustMe());
} else if(expr != null) {
// Retain the previous expression's Position and Type.
setExpr(expr.copy(TBlock(expressions)));
} else {
throw "`expr` must not be `null` when using ClassFuncData.setExprList.";
}
}

/**
A map of the number of times a variable is used can optionally
be provided for later reference.

This is usually calculated using `EverythingIsExprSanitizer` prior
to other optimizations.
**/
public function setVariableUsageCount(usageMap: Map<Int, Int>) {
variableUsageCount = usageMap;
}

/**
Returns the variable usage count.
If it has not been calculated yet, it is calculated here.
**/
public function getOrFindVariableUsageCount(): Map<Int, Int> {
if(expr == null) {
return [];
}

final map: Map<Int, Int> = [];
function count(e: TypedExpr) {
switch(e.expr) {
case TVar(tvar, _): {
map.set(tvar.id, 0);
}
case TLocal(tvar): {
map.set(tvar.id, (map.get(tvar.id) ?? 0) + 1);
}
case _:
}
return haxe.macro.TypedExprTools.map(e, count);
}
count(expr);
return variableUsageCount = map;
}

/**
Applies preprocessors to the `expr`.
**/
public function applyPreprocessors(compiler: BaseCompiler, preprocessors: Array<ExpressionPreprocessor>) {
for(processor in preprocessors) {
processor.process(this, compiler);
}
}
}
#end
94 changes: 2 additions & 92 deletions src/reflaxe/data/ClassFuncData.hx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,7 @@ using reflaxe.helpers.NullHelper;
using reflaxe.helpers.PositionHelper;
using reflaxe.helpers.TypedExprHelper;

class ClassFuncData {
public final id: String;

public final classType: ClassType;
public final field: ClassField;

public final isStatic: Bool;
class ClassFuncData extends ClassFieldData {
public final kind: MethodKind;

public final ret: Type;
Expand All @@ -30,29 +24,19 @@ class ClassFuncData {

public final property: Null<ClassField>;

public var expr(default, null): Null<TypedExpr>;

var variableUsageCount: Null<Map<Int, Int>>; // Access using `getOrFindVariableUsageCount`

public function new(
id: String,
classType: ClassType, field: ClassField, isStatic: Bool, kind: MethodKind, ret: Type,
args: Array<ClassFuncArg>, tfunc: Null<TFunc>, expr: Null<TypedExpr>,
extractArgumentMetadata: Bool = true,
property: Null<ClassField> = null
) {
this.id = id;

this.classType = classType;
this.field = field;

this.isStatic = isStatic;
super(id, classType, field, isStatic, expr);
this.kind = kind;

this.ret = ret;
this.args = args;
this.tfunc = tfunc;
this.expr = expr;

if(extractArgumentMetadata) {
extractArgumentMeta();
Expand All @@ -70,15 +54,6 @@ class ClassFuncData {
return new ClassFuncData(id, classType, field, isStatic, kind, ret, args, tfunc, expr, false, property);
}

/**
Applies preprocessors to the `expr`.
**/
public function applyPreprocessors(compiler: BaseCompiler, preprocessors: Array<ExpressionPreprocessor>) {
for(processor in preprocessors) {
processor.process(this, compiler);
}
}

/**
TODO: Anyway I could make this... more condensed?
**/
Expand Down Expand Up @@ -159,71 +134,6 @@ class ClassFuncData {
return isSetterName() && property != null;
}

/**
Sets the typed expression.
Invalidates any cached information regarding the old expression.
**/
public function setExpr(e: TypedExpr) {
expr = e;

// The expression changed, so the stored usage count data is now invalid.
variableUsageCount = null;
}

/**
Works the same as `setExpr`, but takes an array of expressions.

If just one expression, it is used directly.
Multiple are converted into a block expression.
**/
public function setExprList(expressions: Array<TypedExpr>) {
if(expressions.length == 1) {
setExpr(expressions[0].trustMe());
} else if(expr != null) {
// Retain the previous expression's Position and Type.
setExpr(expr.copy(TBlock(expressions)));
} else {
throw "`expr` must not be `null` when using ClassFuncData.setExprList.";
}
}

/**
Returns the variable usage count.
If it has not been calculated yet, it is calculated here.
**/
public function getOrFindVariableUsageCount(): Map<Int, Int> {
if(expr == null) {
return [];
}

final map: Map<Int, Int> = [];
function count(e: TypedExpr) {
switch(e.expr) {
case TVar(tvar, _): {
map.set(tvar.id, 0);
}
case TLocal(tvar): {
map.set(tvar.id, (map.get(tvar.id) ?? 0) + 1);
}
case _:
}
return haxe.macro.TypedExprTools.map(e, count);
}
count(expr);
return variableUsageCount = map;
}

/**
A map of the number of times a variable is used can optionally
be provided for later reference.

This is usually calculated using `EverythingIsExprSanitizer` prior
to other optimizations.
**/
public function setVariableUsageCount(usageMap: Map<Int, Int>) {
variableUsageCount = usageMap;
}

/**
Checks if the `args` of both `ClassFuncData` are identical.
**/
Expand Down
Loading