<commit_before>package ue4hx.internal;
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;

using haxe.macro.Tools;
using StringTools;

/**
  Generates the Haxe @:uexpose class which allows Unreal types to access Haxe types
 **/
class BuildExpose {
  public static function build():Type {
    return switch (Context.getLocalType()) {
      case TInst(_, [typeToGen]):
        new BuildExpose(Context.currentPos()).generate(typeToGen);
      case _:
        throw 'assert';
    }
  }

  private var pos:Position;
  private function new(pos) {
    this.pos = pos;
  }

  public function generate(t:Type):Type {
    switch (Context.follow(t)) {
    case TInst(cl,tl):
      var clt = cl.get();
      var typeRef = TypeRef.fromBaseType(clt, this.pos),
          thisConv = TypeConv.get(t, this.pos);
      var nativeUe = thisConv.ueType;
      var expose = typeRef.getExposeHelperType();
      var toExpose = [],
          uprops = [];
      for (field in clt.statics.get()) {
        if (field.meta.has(':uproperty'))
          uprops.push(field);
        else if (shouldExpose(field))
          toExpose.push(getMethodDef(field, Static));
      }

      var nativeMethods = collectNativeMethods(clt),
          haxeMethods = collectHaxeMethods(clt);
      for (field in clt.fields.get()) {
        if (field.meta.has(':uproperty')) {
          uprops.push(field);
          continue;
        }

        if (haxeMethods.exists(field.name))
          continue; // our methods are already virtual and we don't need to override anything

        var isOverride = nativeMethods.exists(field.name);
        if (isOverride || shouldExpose(field)) {
          toExpose.push(getMethodDef(field, isOverride ? Override : Member));
        }
      }

      var buildFields = [];
      for (field in toExpose) {
        var callExpr = if (field.type.isStatic())
          typeRef.getClassPath() + '.' + field.cf.name + '(';
        else
          thisConv.glueToHaxe('self') + '.' + field.cf.name + '(';
        callExpr += [ for (arg in field.args) arg.type.glueToHaxe(arg.name) ].join(', ') + ')';

        if (!field.ret.haxeType.isVoid())
          callExpr = 'return ' + field.ret.haxeToGlue( callExpr );

        var fnArgs:Array<FunctionArg> =
          [ for (arg in field.args) { name: arg.name, type: arg.type.haxeGlueType.toComplexType() } ];
        if (!field.type.isStatic())
          fnArgs.unshift({ name: 'self', type: thisConv.haxeGlueType.toComplexType() });

        var headerDef = new HelperBuf(),
            cppDef = new HelperBuf();
        var ret = field.ret.ueType.getCppType().toString();
        cppDef = cppDef + ret + ' ' + nativeUe.getCppClass() + '::' + field.cf.name + '(';
        var modifier = field.type.isStatic() ? 'static ' : 'virtual ';
        headerDef = headerDef + modifier + ret + ' ' + field.cf.name + '(';
        var args = [ for (arg in field.args) arg.type.ueType.getCppType() + ' ' + arg.name ].join(', ') + ')';
        cppDef += args; headerDef += args;
        var native = nativeMethods[field.cf.name];
        var thisConst = false;
        if (native != null)
          thisConst = native.meta.has(':thisConst');

        if (thisConst) {
          headerDef += ' const';
          cppDef += ' const';
        }

        if (field.type == Override)
          headerDef += ' override';
        headerDef += ';\n';
        cppDef += '{\n\t';
        var args = [ for (arg in field.args) arg.type.ueToGlue( arg.name ) ];
        if (!field.type.isStatic())
          args.unshift( thisConv.ueToGlue(thisConst ? 'const_cast<${ nativeUe.getCppType() }>(this)' : 'this') );
        var cppBody = expose.getCppClass() + '::' + field.cf.name + '(' +
          args.join(', ') + ')';
        if (!field.ret.haxeType.isVoid())
          cppBody = 'return ' + field.ret.glueToUe( cppBody );
        cppDef += cppBody + ';\n}\n';

        var metas:Metadata = [
          { name: ':glueHeaderCode', params:[macro $v{headerDef.toString()}], pos: field.cf.pos },
          { name: ':glueCppCode', params:[macro $v{cppDef.toString()}], pos: field.cf.pos }
        ];
        if (field.ret.haxeType.isVoid())
          metas.push({ name: ':void', pos: field.cf.pos });
        buildFields.push({
          name: field.cf.name,
          access: [APublic, AStatic],
          kind: FFun({
            args: fnArgs,
            ret: field.ret.haxeGlueType.toComplexType(),
            expr: Context.parse(callExpr, field.cf.pos)
          }),
          meta: metas,
          pos: field.cf.pos
        });
      }

      var metas = [
        { name: ':uexpose', params:[], pos:clt.pos },
        { name: ':keep', params:[], pos:clt.pos },
        { name: ':uextern', params:[], pos:clt.pos },
      ];

      var includes = ['<unreal/helpers/GcRef.h>', '<' + expose.getClassPath().replace('.','/') + '.h>'];
      var info = addNativeUeClass(nativeUe, clt, includes, metas);

      // add createHaxeWrapper
      {
        var headerCode = 'virtual void *createHaxeWrapper()' + (info.hasHaxeSuper ? ' override;' : ';');
        var glueHeaderIncs = new Map();
        for (uprop in uprops) {
          var tconv = TypeConv.get(uprop.type, uprop.pos);
          var data = new StringBuf();
          data.add('UPROPERTY(');
          var first = true;
          for (meta in uprop.meta.extract(':uproperty')) {
            if (meta.params != null) {
              for (param in meta.params) {
                if (first) first = false; else data.add(', ');
                data.add(param.toString());
              }
            }
          }
          headerCode += data + ')\n\t';<commit_msg>Improve readability of generated exposed class<commit_after>package ue4hx.internal;
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;

using haxe.macro.Tools;
using StringTools;

/**
  Generates the Haxe @:uexpose class which allows Unreal types to access Haxe types
 **/
class BuildExpose {
  public static function build():Type {
    return switch (Context.getLocalType()) {
      case TInst(_, [typeToGen]):
        new BuildExpose(Context.currentPos()).generate(typeToGen);
      case _:
        throw 'assert';
    }
  }

  private var pos:Position;
  private function new(pos) {
    this.pos = pos;
  }

  public function generate(t:Type):Type {
    switch (Context.follow(t)) {
    case TInst(cl,tl):
      var clt = cl.get();
      var typeRef = TypeRef.fromBaseType(clt, this.pos),
          thisConv = TypeConv.get(t, this.pos);
      var nativeUe = thisConv.ueType;
      var expose = typeRef.getExposeHelperType();
      var toExpose = [],
          uprops = [];
      for (field in clt.statics.get()) {
        if (field.meta.has(':uproperty'))
          uprops.push(field);
        else if (shouldExpose(field))
          toExpose.push(getMethodDef(field, Static));
      }

      var nativeMethods = collectNativeMethods(clt),
          haxeMethods = collectHaxeMethods(clt);
      for (field in clt.fields.get()) {
        if (field.meta.has(':uproperty')) {
          uprops.push(field);
          continue;
        }

        if (haxeMethods.exists(field.name))
          continue; // our methods are already virtual and we don't need to override anything

        var isOverride = nativeMethods.exists(field.name);
        if (isOverride || shouldExpose(field)) {
          toExpose.push(getMethodDef(field, isOverride ? Override : Member));
        }
      }

      var buildFields = [];
      for (field in toExpose) {
        var callExpr = if (field.type.isStatic())
          typeRef.getClassPath() + '.' + field.cf.name + '(';
        else
          thisConv.glueToHaxe('self') + '.' + field.cf.name + '(';
        callExpr += [ for (arg in field.args) arg.type.glueToHaxe(arg.name) ].join(', ') + ')';

        if (!field.ret.haxeType.isVoid())
          callExpr = 'return ' + field.ret.haxeToGlue( callExpr );

        var fnArgs:Array<FunctionArg> =
          [ for (arg in field.args) { name: arg.name, type: arg.type.haxeGlueType.toComplexType() } ];
        if (!field.type.isStatic())
          fnArgs.unshift({ name: 'self', type: thisConv.haxeGlueType.toComplexType() });

        var headerDef = new HelperBuf(),
            cppDef = new HelperBuf();
        var ret = field.ret.ueType.getCppType().toString();
        cppDef = cppDef + ret + ' ' + nativeUe.getCppClass() + '::' + field.cf.name + '(';
        var modifier = field.type.isStatic() ? 'static ' : 'virtual ';
        headerDef = headerDef + modifier + ret + ' ' + field.cf.name + '(';
        var args = [ for (arg in field.args) arg.type.ueType.getCppType() + ' ' + arg.name ].join(', ') + ')';
        cppDef += args; headerDef += args;
        var native = nativeMethods[field.cf.name];
        var thisConst = false;
        if (native != null)
          thisConst = native.meta.has(':thisConst');

        if (thisConst) {
          headerDef += ' const';
          cppDef += ' const';
        }

        if (field.type == Override)
          headerDef += ' override';
        headerDef += ';\n';
        cppDef += '{\n\t';
        var args = [ for (arg in field.args) arg.type.ueToGlue( arg.name ) ];
        if (!field.type.isStatic())
          args.unshift( thisConv.ueToGlue(thisConst ? 'const_cast<${ nativeUe.getCppType() }>(this)' : 'this') );
        var cppBody = expose.getCppClass() + '::' + field.cf.name + '(' +
          args.join(', ') + ')';
        if (!field.ret.haxeType.isVoid())
          cppBody = 'return ' + field.ret.glueToUe( cppBody );
        cppDef += cppBody + ';\n}\n';

        var metas:Metadata = [
          { name: ':glueHeaderCode', params:[macro $v{headerDef.toString()}], pos: field.cf.pos },
          { name: ':glueCppCode', params:[macro $v{cppDef.toString()}], pos: field.cf.pos }
        ];
        if (field.ret.haxeType.isVoid())
          metas.push({ name: ':void', pos: field.cf.pos });
        buildFields.push({
          name: field.cf.name,
          access: [APublic, AStatic],
          kind: FFun({
            args: fnArgs,
            ret: field.ret.haxeGlueType.toComplexType(),
            expr: Context.parse(callExpr, field.cf.pos)
          }),
          meta: metas,
          pos: field.cf.pos
        });
      }

      var metas = [
        { name: ':uexpose', params:[], pos:clt.pos },
        { name: ':keep', params:[], pos:clt.pos },
        { name: ':uextern', params:[], pos:clt.pos },
      ];

      var includes = ['<unreal/helpers/GcRef.h>', '<' + expose.getClassPath().replace('.','/') + '.h>'];
      var info = addNativeUeClass(nativeUe, clt, includes, metas);

      // add createHaxeWrapper
      {
        var headerCode = 'virtual void *createHaxeWrapper()' + (info.hasHaxeSuper ? ' override;\n\t\t' : ';\n\t\t');
        var glueHeaderIncs = new Map();
        for (uprop in uprops) {
          var tconv = TypeConv.get(uprop.type, uprop.pos);
          var data = new StringBuf();
          data.add('UPROPERTY(');
          var first = true;
          for (meta in uprop.meta.extract(':uproperty')) {
            if (meta.params != null) {
              for (param in meta.params) {
                if (first) first = false; else data.add(', ');
                data.add(param.toString());
              }
            }
          }
          headerCode += data + ')\n\t\t';
{'fraction_non_alphanumeric': 0.15871195412439348, 'fraction_numerical': 0.000176444640494045, 'mean_word_length': 2.1497638232842458, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5438109', 'n_tokens_mistral': 3727, 'n_tokens_neox': 3651, 'n_words': 765}