Отладка haXe-кода

Да, дебаггера у нас пока нет. Flex Builder тут имеет преимущество. Но работы ведутся. Разработчики плагина EcliHX для Eclipse заняты как раз дебаггером, который будет включен в этот плагин. (Пока же EcliHX имеет версию 0.0.3 и умеет только подсвечивать код, генерировать и компилировать проект).

Я сам в проектах на ActionScript2.0 использую собственное флэш-приложение — DebugConsole. Это отдельный swf-файл в отдельном окне браузера, который по LocalConnection принимает отладочные сообщения из любого (моего) флэш-приложения и отображает их всякими продвинутыми способами:

  • сообщения деляться на каналы, и можно просматривать один из них или сразу все;
  • используется выделение цветом по типу и важности сообщения;
  • длинные сообщения выводятся в укороченном виде, полную версию можно увидеть по клику;
  • возможность остановить(запустить) автоматическое скроллирование;
  • очистка вывода;

К сожалению, этот инструмент закрытый и распростронению не подлежит. Впрочем, он уже потерял свою актуальность для ActionScript-программистов в связи с наличием полноценного дебаггера во FlexBuilder. Для haXe он бы пригодился, но я не спешу портировать его с ActionScript, потому что надеюсь, что дебаггер все-таки скоро появится.

В ближайшее время хочу попробовать другой способ — сделать на Ruby простенький дебаг-сервер. Он будет открывать и слушать какой-нибудь порт, и все, что в него приходит, писать в консоль. Из флэшки я будут коннектиться на тот же порт с помошью XMLSocket и посылать в него отладочные сообщения. Как только это получится, выложу тут свое решение. (Буду делать под Linux, но поскольку Ruby мультиплатформенный, то должно работать и под Windows).

В общем, пока пользуемся простыми средствами — функцией trace. У нас эта функция будет помощнее, чем во Flash IDE.
Отличия от функции trace в ActionScript2.0

  • автоматически подставляется имя файла и номер строки, откуда был вызыван trace;
  • вывод фукнции trace можно перенаправить во внешний файл или GUI-контейнер;
  • если в качестве аргумента передан объект, то trace пытается рекурсивно обойти его свойства и представить их значения в строковом виде;

По умолчанию функция trace в haXe создает во флешке текстовое поле поверх всего остального контента и направляет сообщения в него. Это, конечно, не совсем удобно, но такое поведение можно переопределить.

На самом деле функция trace в haXe является оболочкой для низкоуровневых функций, специфичных для каждой платформы (Flash, JavaScript, Neko). Есть еще одна версия этой функции в классе haxe.Log, которая может принимать дополнительные параметры. Плюс, этот класс имеет еще парочку полезных функций:

haxe.Log.trace( val : Dynamic, pos : haxe.PosInfos ) : Void
haxe.Log.clear() : Void
haxe.Log.setColor( color : Int ) : Void //только для платформы Flash

Функция haxe.Log.trace первым параметром принимает инфу, которую нужно отобразить. Параметр типизирован как Dynamic, так что можно передавать что угодно. trace попытается сгенерировать наиболее информативное представление, если будет передан объект. Если объект имеет метод toString(), то этот метод и будет использован (поэтому рекомендуется определять toString() в каждом классе).

Вторым параметром функция принимает объект, указывающий на позицию в коде, имеющий тип haxe.PosInfos

typedef PosInfos = {
  var fileName : String;
  var lineNumber : Int;
  var className : String;
  var methodName : String;
  var customParams : Array<Dynamic>;
}

этот параметр необязательный, и если его не передавать, то такой объект будет создан автоматически, и он будет указывать на место вызова trace.

Ниже идет пример кода из книги "Professional haXe and Neko". Он демонстрирует, как можно переопределить фукнцию trace, чтобы реализовать какой-либо собственный способ отображения инфы.

class TraceHandler
{
  public static function main()
  {
    haxe.Log.trace = myTrace;
    trace( “Can I trace it?” );
    var pi : haxe.PosInfos = {
        className : “MyClass”,
        fileName : “MyClass.hx”,
        methodName : “someFunc”,
        lineNumber : 50394,
        customParams : [“let’s find out...”] };
    haxe.Log.trace( “Yes I can!”, pi );
    trace( “Definitely!!!”, “Are you sure?”, “Just to be certain...” );
  }
  public static function myTrace( v : Dynamic, ?pos : haxe.PosInfos ) : Void
  {
    var f : flash.MovieClip = flash.Lib.current;
    if ( !Std.is(f.__txtField__, flash.TextField ) )
    {
      f.createTextField( “__txtField__”, 9999, 0, 0, flash.Stage.width,
          flash.Stage.height );
    }
    for ( i in pos.customParams )
    {
      f.__txtField__.text += pos.fileName + “->” + pos.className + “->” +
          pos.methodName + “->line(“ + pos.lineNumber + “) : “ + i + “\n”;
    }
    f.__txtField__.text += pos.fileName + “->” + pos.className + “->
        + pos.methodName + “->line(“ + pos.lineNumber + “) : “ + v + “\n”;
  }
}

Обратите внимание на строку:
    haxe.Log.trace = myTrace;

в ней вся прелесть haXe — не нужны никакие делегаты! Просто присваиваем нужную фукнцию. Если так сделать в ActionScript, то функция будет вызываться, но она будет иметь область видимости в глобальном контексте, то есть, перестанет быть методом класса. И ссылка this перестанет работать. В haXe это не так, функция остается методом класса и работает this.

И напоследок — очень полезный парамет компилятора --no-traces. В больших приложениях вызовов trace может быть очень много, что добавляет лишние байты к результирующему swf-файлу, а при работе флэш-приложения создает дополнительную нагрузку на процессор. Поэтому релиз-версию приложения нужно компилировать с данной опцией. Тогда компилятор удалит из кода все вызовы trace (правда если вызывается непосредственно haxe.Log.trace, то такие вызовы будут оставлены).