FLASM 1.41
Обновлено: 19.08.2002

  • Что такое flasm
  • Скачать
  • Что нового
  • Использование
  • Защита ActionScript-кода
  • История проекта
  • Виртуальная машина Flash
  • Синтаксис Ассемблера
  • Внедрение flasm-кода в ActionScript
  • Техники оптимизации
  • Устаревший синтаксис
  • Различия в размерах файлов
  • Большие скрипты
  • Ошибки и сбои
  • Ресурсы
  • Условия использования
  • История изменений
  • Состояние проекта
  • Как вы можете помочь
  • Наслаждайтесь

    Что такое flasm

    flasm 1.41 - ассемблер/дизассемблер SWF-файлов, работающий из командной строки. Он дизассемблирует SWF-файл целиком, включая все линейки (timelines), обработчики событий мувиклипов и кнопок. После этого желательно сделать некоторую оптимизацию дизассемблированного кода вручную. Затем flasm заменит все команды в оригинальном SWF-файле на оптимизированные вами. Flasm полностью поддерживает Flash MX.

    Также существует возможность встраивать команды flasm в ActionScript-код, что значительно упрощает процесс оптимизации больших проектов.

    Кроме оптимизации, дизассемблирование flasm'ом предоставляет возможность "порыться внутри" кода, что помогает глубже вникнуть в ActionScript. Flasm не декомпилятор, он позволяет получить читабельное представление кода, но не исходный ActionScript-код.

    Ищете простое описание? Хорошо, можете не читать всю страницу. Для начала разберитесь с использованием. Потом прочитайте про виртуальную машину Flash и убедитесь, что поняли концепцию регистров и стека. Дизассемблируйте несколько своих флеш-клипов, начинайте с чего-нибудь простого, чтобы понять, как работает компилятор. Если разобрались, то вы усвоили 80% того что нужно. Теперь... прочитайте всю страницу.
     

    Скачать

    Windows binary: flasm14.zip
    Mac OS X binary: flasmac14.tgz
    Linux binary (x86): flasmlinux14.tgz
    Документация (этот файл на английском) включена в архивы.

    Хотите сами скомпилировать из исходников?

    Платформонезависимые исходники: flasm14src.zip
    Вам понадобится GCC или CC компилятор с установленными пакетами flex, bizon и zlib. По идее должно скомпилироваться без проблем. Тестировалось с djgpp на win98se, cygwin на win98se и win2k, mac os X и Linux. Для cygwin нужно установить еще и пакет mingw и mingw'овскую версию zlib'а из MinGW packages repository. Под Windows, MS Visual C++ или другие пакеты, несовместимые с *nix, потребуют ряда изменений в исходниках т.к. flasm использует некоторые POSIX функции. Разбирайтесь с cygwin.
     

    Новое в версии 1.41

    Обновления в предыдущих версиях
     

    Использование

    Распакуйте дистрибутив в любое место. Поскольку flasm работает из командной строки, потребуется открыть досовское (MSDOS) окно (под Windows). На MAC'ах потребуется открыть окно терминала: Applications/Utilities/Terminal. Теперь, используя DOS-команду CD, перейдите в директорию с flasm'ом. Например:

    cd c:\Flash\flasm (windows)
    или
    cd Flash/flasm (mac)

    Вызванный без параметров, flasm покажет список существующих ключей:

    flasm ключ имя-файла

    ключи
    -d   Дизассемблировать swf-файл с выводом в консоль
    -a   Ассемблировать указанный проект flasm
    -u   Обновить swf-файл, заменить макросы flasm
    -z   Сжать swf-файл Zlib'ом
    -x   Распаковать swf-файл

    -d foo.swf
    Дизассемблировать foo.swf в консоль.

    -d foo.swf > foo.flm
    Дизассемблировать foo.swf, вывод перенаправить в foo.flm.
    Альтернатива - просто вызвать flasm без ключа: flasm foo.swf
    Файл foo.flm будет создан в той же папке.

    -a foo.flm
    Ассемблировать foo.flm и обновить swf, указанный внутри.
    Создается резервная копия оригинала с расширением .$WF

    -u foo.swf
    Дизассемблировать foo.swf во временный файл.
    Выполняет макросы flasm, встроенные в SFW.
    Автоматически выполняет элементарные оптимизации: удаляет дублирующиеся нотации, заменяет 0.0 на 0, перестраивает пул констант.
    Обновляет SWF. Создается резервная копия оригинала с расширением .$WF

    В принципе неплохая идея обновлять финальные swf-файлы с ключом -u.
    Может они и не станут работать заметно быстрее, но по крайней мере будут немного меньше.

    -x foo.swf
    Только для Flash MX. Распаковывает foo.swf, создается резервная копия оригинала с расширением .$WF

    -z foo.swf
    Жмет foo.swf. Создается резервная копия оригинала с расширением .$WF. Исходник необязательно должен быть МХ'овым, просто после сжатия он будет воспроизводиться только МХ плейером.

    Для удобства работы с swf-файлами, можно добавить flasm в контекстное меню Windows, вызываемое правым щелчком мыши. Для этого запустите windows explorer (проводник), выберите View > Folder Options > File Types и выберите Flash player movie (или подобный) тип, связанный с расширением .swf. Нажмите кнопку Edit, затем кнопку New. В поле Action введите Disassemble. Нажмите кнопку Browse, перейдите в директорию, в которой находится flasm и дважды щелкните на flasm.exe. Не задавайте никаких параметров. Нажмите OK, Close и еще раз Close. Теперь щелкните правой кнопкой мыши на любом swf-файле и в открывшемся контекстном меню выберите Disassemble - будет создан дизассемблированный файл с расширением .flm. Возможна дальнейшая автоматизация, добавлением flasm -u для обновления swf-файлов или flasm -a для ассемблирования flm-файлов.

    Если не хотите делать этого, попробуйте воспользоваться winflasm - графической оболочкой (GUI) для flasm.
     

    Защита ActionScript-кода

    Поскольку формат SWF-файла открыт, то любой созданный вами контент может быть из этого файла извлечен. Задача flasm'а — помочь вам оптимизировать код, превратив его в байтовый код. Flasm, в отличие от других продуктов, имеющихся на рынке, не помогает делать обратное преобразование байткода в обычный код ActionScript, который можно было бы украсть через буфер обмена.

    К сожалению, полумеры типа включения в каждый кадр специальных шифрующих выражений и даже низкоуровневое автоматическое манипулирование с помощью инструментов запутывания кода (obfuscator'ы) не сделают ваш ActionScript действительно защищенным (не говоря уже о довольно бесполезных тегах protect и enableDebugger). Это всего лишь вопрос времени — когда следующие версии смотрелок ActionScript-кода смогут обойти такую защиту. Если не верите, почитайте это. Java имеет сходный формат файла и более долгую историю, чем ActionScript, так что борьба декомпиляторов с запутывалками идет уже давно.

    Тем не менее, существуют инструменты, позволяющие поэкспериментировать с запутыванием. Старейший из них, это obfu от Dave Hayden (Дэйв Хэйдэн). Он использует простой трюк управления потоком, что заводит декомпиляторы в тупик. Другой кажется, идет тем же путем. Viewer Screwer от Робина Дебройла (Robin Debreuil), это настоящий запутыватель, он переименовывает все ваши переменные, делая код просто нечитабельным, хотя и доступным при этом. Я не пробовал ничего из вышеперечисленного. Тем не менее, исходя из прочитанного я понял, что ни один из этих инструментов не достиг стадии стабильного, готового к выпуску продукта, хотя инструмент Робина кажется мне самым перспективным и многообещающим в этом направлении.

    Если вы хотите спрятать сложный 3D-движок, в разработке которого вы провели сотни часов, вам может помочь оптимизация его с помощью flasm'а. Это сделает кражу ваших скриптов намного более сложной задачей. Обычные инструменты декомпиляции умеют распознавать определенные закономерности (patterns) в байтовом коде, соответствующие высокоуровневым выражениям ActionScript. Производя оптимизацию, вы уничтожаете такие закономерности. Посмотрите этот простой пример.

    Дополнение: вышенаписанное некоторых сбивает с толку. Они просто спрашивают меня, как это сделать :) Я должен сказать: вы будете работать недели с вашим проектом, задавшись целью оптимизации и неплохим побочным эффектом в виде усложнения жизни декомпиляторов. Не ищите здесь какие-то скрытые ключи или обещания.

    И снова скажу, все это об утаивании алгоритмов, но не паролей. Нет способа спрятать пароль в машине пользователя — только некоторые техники, основанные на работе с сервером, могут предоставить вам дополнительную степень безопасности.

    Если Вы программируете на Cи и хотите сами реализовать защиту от детрансляторов, почитайте мои соображения по этому поводу и углубляйтесь в код flasm'а. Все Flash-сообщество, и особенно новички, просто помешаны на этом.
     

    История проекта

    dave@opaque.net в сотрудничестве с Damien Morton выпустил flasm в апреле 2001 года. Оригинальная версия могла дизассемблировать главный таймлайн swf-файла и ассемблировать в первый кадр или во внешнюю библиотеку. Хотя flasm был достаточно полезен для оптимизации функций, все же было сложно обрабатывать им множество ситуаций из реальных задач типа включения событий клипов (onClipEvents) и .т.п. Так что я расширил функциональность flasm'а и устранил некоторые глюки. Затем Дэйв открыл проект на sourceforge.net, так что любой может поучаствовать в разработке flasm. Тем не менее, в настоящее время я единственный, кто его разрабатывает. Надеюсь, в один прекрасный день эта ситуация изменится. Хотя я и добавил некоторую функциональность и устранил некоторые глюки, во flasm все еще есть множество достойных реализации вещей.
     

    Виртуальная машина Flash

  • Стек
  • Пул констант
  • Регистры
  • Область видимости

    Каждое простое выражение языка ActionScript среда Flash компилирует в пару простых команд байтового кода (байткода). Например, a=b*b; превращается в

    constants 'a', 'b'
    push 'a', 'b'
    getVariable
    push 'b'
    getVariable
    multiply
    setVariable

    Код приведенный выше — визуальное представление байтовых кодов, созданных с помощью flasm. Flash проигрыватель/плагин работает внутри виртуальной машины, которая интерпретирует байткоды.

    Я вызываю команды (actions) внутри кадров или, другими словами, по событиям появления блоков команд. Flash выполняет блоки команд друг за другом, так что поток выполнения кода в блоке никогда не прерывается ни событием gotoAndPlay(), ни аналогичными ему командами. Думаете, реальное параллельное выполнение кода должно быть лучше, чем это? Я уверен, что это должно эффективно повлиять на стабильность Flash Player'а, которая теперь стала довольно высокой, принимая во внимание все вещи, происходящие в сложном клипе.

    Стек

    Виртуальная машина Flash основана на стеке, то есть вы не можете сослаться на конкретную ячейку памяти. Стек — это место в памяти, где данные могут храниться таким образом, что последняя вошедшая (pushed) величина будет извлечена (poped) из стека первой. Все команды читают (pop) операнды из стека и кладут результат (если таковой имеется) в стек. Операнд может быть целым числом (integer), строкой (string), числом с плавающей запятой (float) или ссылкой на объект (на самом деле являясь его именем) и т.п.

    Дальнейшее объяснение стека от Роберта Пеннера (Robert Penner):

    Если вам знакомы методы Array.push и Array.pop, знайте, что эти команды подобны манипуляциям со стеком. Стек похож на массив значений, за исключением того факта, что в каждый отдельный момент времени вы можете получить значение только последнего (верхнего) элемента, или положить сверху еще один элемент-значение или поменять местами два последние элемента.
    Например, для сложения двух чисел вам нужно положить оба эти числа в стек, затем вызвать команду add. Команда add вынет из стека два верхних числа, сложит их и положит полученное число в стек.

    Заметьте, что команда pop не вызывает ошибок, даже если стек пуст.

    Есть две команды, которые дают вам дополнительные возможности для работы со стеком: dup и swap. dup дублирует верхнее значение стека, swap меняет местами два верхних значения. В настоящее время Flash как-то неактивно использует dup и swap, вы можете увидеть это при дизассеиблинге, но две эти команды имеют очень важное значение для оптимизации.

    Каждое выражение ActionScript, независимо от его сложности, очищает стек после себя, чтобы избежать переполнения памяти. Работая с Flash, вы не видите весь этот байткод и вам не нужно об этом беспокоиться. Но производя с помощью flasm изменения на уровне байткода вы должны всегда помнить, что находится в стеке. Неправильные манипуляции со стеком чаще всего не приводят к ошибкам в проигрывателе. Вы не увидите десять тысяч мертвых записей в стеке, которые были порождены одним из циклов написанного вами кода, но скорость работы проигрывателя может очень снизиться и вероятно в какой-то момент времени просто возникнет переполнение памяти, отведенной под ваш swf-файл.

    Пул констант

    В начале каждого блока команд, в котором некие переменные, методы или строки используются более одного раза, Flash создает так называемый пул констант. Фактически, если хотя бы одна переменная используется дважды, создается пул для всех строк данного блока кода. Вот пример:

    constants 'bottom', 'paused', 'aliensleft', 'fire'

    В пуле констант может храниться до 65535 строк (теоретически). В последствии на них можно ссылаться из вашего кода через однобайтовый (первые 256 строк пула) или двухбайтовый индекс (остальные строки пула). Чаще всего в пуле хранится не более 256 строк, так что вы редко встретите в swf двухбайтовые ссылки. Практически, количество строк ограничено общим размером команды constants, а этот размер не может быть более 65535 байт, как и размер любой другой команды.

    Помещение в стек строк или методов, а не ссылок на них в виде констант, отличается только размером кода, но не скоростью выполнения. Допустим, вы помещаете в стек строку "paused". push 'paused' после определения соответствующей константы (constants definition) сгенерирует байткод, выглядящий на самом деле как

    push byte: the second constant from the list

    "поместить в стек однобайтовую ссылку на вторую константу в из пула констант", а не

    push string: 'paused'

    т.е. "поместить в стек всю строку "paused", заметьте, строка займет в стеке значительно больше места, чем однобайтовая ссылка. Хотя сам Flash никогда не переопределяет пул констант где-нибудь посередине блока команд, теоретически вы можете делать это.

    В режиме обновления (flasm -u foo.swf) flasm пересобирает все константы, удаляя пустые строки и те строки, на которые ссылка в коде встречается только один раз.

    Регистры

    Виртуальная машина Flash обладает четырьмя регистрами, которые адресуются как r:0, r:1, r:2, r:3. Доступ к регистрам осуществляется намного быстрее, чем доступ к переменным, так что лучше хранить наиболее часто используемые переменные в регистрах. Только r:0 в настоящее время используется Flash, так что вам есть где развернуться с вашей собственной оптимизацией. Чтобы сохранить что-либо в регистре, вы должны сначала положить это в стек, а затем выполнить команду setRegister:

    push 'paused'
    getVariable
    setRegister r:1

    Теперь значение переменной paused хранится в регистре r:1. В следующий раз, вместо того, чтобы обращаться к этой переменной через выражение paused, используйте выражение push r:1.
    Примечание: В отличите от большинства других команд, setRegister не забирает верхнее значение из стека! Если вам не нужно хранить в стеке это значение, вы должны вручную вытолкнуть его оттуда, применив команду pop.

    Область видимости

    Я провел некоторое тестирование видимости регистров и стека из разных кадров.

    Регистры не глобальны. Значение, хранящееся в регистре, если оно определено в одном из кадров области видимости _root, будет доступно только в этом кадре. Если там же определена некая функция или прикреплен мувиклип, они могут получить доступ к этому значению в регистре. Похоже на то, что как только в swf встречается тег showFrame, регистры снова исчезают, (т.е. очищаются?).

    Во Flash 5 стек был глобальным. Если в первом кадре поместить в стек значение, то в пятом кадре это значение можно успешно трассировать. Оно также доступно из мувиклипов. Это означает, что Flash 5 не очищал стек для вас. Но во Flash MX ситуация изменилась: похоже, проигрыватель Flash MX очищает содержимое стека после выполнения каждого блока команд.

    Что касается меня, я всегда подхожу к стеку и регистрам как к локальным, относительно кадров или событий. Даже если сейчас стек позволяет большее, все равно его поведение полностью зависит от внутреннего поведения Flash-player'а, которое может очень даже измениться в будущих версиях.

    Насчет констант. Если пул констант определен в начале кадра, то он доступен из любой функции этого кадра, его не нужно переопределять. Flash компилирует код ActionScript именно таким образом, я никогда в дизассемблированном коде не встречал определений констант в функциях. И напротив, каждое событие имеет свой собственный пул констант.

    Не очень понятно? Извините — приглашаю к дальнейшим исследованиям.
     

    Синтаксис Ассемблера

  • Алфавитный указатель команд
  • Неизвестные команды
  • Теги protect, enableDebugger и enableDebugger2
  • Вложенные включения (includes)
  • Типы данных и команда push
  • Управление потоком
  • События кнопок
  • getProperty/setProperty
  • Управление воспроизведением клипа
  • enumerate/enumerateValue
  • setTarget/setTargetExpr
  • ifFrameLoaded/ifFrameLoadedExpr

    Flasm 1.41 дизассемблирует/асссемблирует все команды, поддерживаемые Flash, независимо от версии. Однако со времен изначального flasm произошло много изменений в синтаксисе, так что сначала подекомпилируйте с помощью flasm 1.41, прежде чем попытаетесь компилировать.

    Каждый flasm-проект должен начинаться с команды movie 'moviename.swf'. Где moviename.swf — имя вашего исходного swf-файла. Не забывайте заключать имя файла в кавычки. При ассемблировании flasm в первую очередь читает эту строку и пытается перезаписать этот файл. При этом создается резервная копия исходного swf-файла с расширением .$wf. Если по каким-либо причинам обновление исходного файла невозможно, он сохраняется неизменным и резервная копия не создается.

    Если Flash встречает атрибут compressed сразу после имени клипа (movie 'moviename.swf' compressed), swf будет сжат (как во Flash MX) после ассемблирования. Исходный swf-файл может быть как сжатым, так и не сжатым, ключевое слово compressed определяет сжатие уже обновленного swf-файла.

    Flasm чувствителен к регистру символов (за исключением строковых значений, которые могут быть чувствительными к регистру). Если в своих строках вы используете одинарные кавычки, превращайте их в escape-последовательности таким образом: 'it\'s beautiful'. Вы также можете заключать строки в двойные кавычки: "it's beautiful".

    Комментарии точно такие же, как в ActionScript:

    // вычисление расстояния

    Многострочные комментарии:

    /* вычисление
    расстояния */

    Ключевые концепции ассемблера и некоторые его команды описаны в этом документе, если вы дизассемблируете любой из ваших swf-файлов, вы быстро подружитесь и с остальными командами.

    Полный алфавитный указатель команд

    addandbitwiseAndbitwiseOr
    bitwiseXorbranchbranchIfTruecallFrame
    callFunctioncallMethodchrconcat
    constantsdecrementdeletedelete2
    dividedupduplicateClipenumerate
    enumerateValueequalsfunctiongetMember
    getPropertygetTimergetVariablegetUrl
    getUrl2gotoAndPlaygotoAndStopgotoFrame
    gotoLabelgreaterThanifFrameLoadedifFrameLoadedExpr
    #includeincrementinitArrayinitObject
    instanceOfintlessThanmbChr
    loadMovieloadMovieNumloadVariablesloadVariablesNum
    mbLengthmbOrdmbSubstringmodulo
    multiplynextFramenewnewMethod
    notoldAddoldEqualsoldLessThan
    orordplaypop
    prevFramepushrandomremoveClip
    returnsetMembersetPropertysetRegister
    setTargetsetTargetExprsetVariableshiftLeft
    shiftRightshiftRight2startDragstop
    stopDragstopSoundsstrictEqualsstringEq
    stringLengthstringLessThansubstringsubtract
    swapswfActiontargetPathtoggleQuality
    toNumbertoStringtracetypeof
    varvarequalswith 

    Я ввел некоторые дополнительные конструкции для соответствия структуре swf-файла. Они работают как контейнеры для команд Flash.

    frame
    defineButton
    defineMovieClip
    movie
    on
    onClipEven
    placeMovieClip

    Также поддерживаются теги protect, enableDebugger и enableDebugger2.

    Пожалуйста, не изменяйте структуру swf-файла! Это означает, что не нужно удалять, заменять или добавлять контейнеры для блоков команд! Хорошо, вы можете добавить или удалить некоторые события без особого вреда. Но если вы удалите кадр или измените id мувиклипа, flasm больше не сможет найти их или любые связанные с ними выражения при ассемблировании.

    Неизвестные команды

    Flasm понимает каждую команду у Flash 3/4/5/MX*, за исключением возможных подразновидностей кода, которые теперь Flash может использовать. Часть места для кода зарезервировано для приложений сторонних разработчиков. Например, программа Quicktime фирмы Apple добавляет для команд Quicktime - тег 0xAA. Flasm способен дизассемблировать/ассемблировать команды, не понимая их. В таком случае строка при дизассемблировании выглядит следующим образом:

    swfAction 0x02 // Unknown action!

    Если в команде есть дополнительные данные, в которых присутствует шестнадцатеричная часть:

    swfAction 0xAA hexdata 0x43,0x12,0x18 // Unknown action!

    Данные показываются как список шестнадцатеричных байтов, разделенных запятой. Если вы определили собственные команды для некоторых своих авторских приложений, то не нужно включать тег длины в поле шестнадцатеричных данных - длина вычислится и добавится автоматически, если будет обнаружено ключевое слово шестнадцатеричных данных. И не забудь, что только код 0x80 может быть дополнительными данными.

    * Все команды Flash MX, которые я обнаружил, здесь есть. Но возможно некоторые и пропустил. Если обнаружите swfAction при дизассемблировании, сообщите пожалуйста!

    Теги protect, enableDebugger и enableDebugger2

    Как намек на авторизацию программы, Macromedia ввела protect (защиту). Она означает, что автор конкретного swf не хочет, чтобы тот открывался в среде разработки Flash. В действительности, подобная защита не действенна. Любая программа, которая имеет дело с swf, может просто ее игнорировать. Во Flasm protect можно увидеть, с возможностью ее добавления или удаления. Вы можете разместить ее в любом месте swf, хотя обычно она находится где-то в начале. Имейте в виду, что protect - не команда, поэтому она должна быть вне блоков команд, за их пределами. Компилятор Flash кодирует пароли как текст, длиной 28 символов. Flasm показывает кодированную строку, но не пароль. В действительности, первые 3 символа кажется всегда "$1$", вероятно это идентификатор схемы шифрования или что-то подобное.

    enableDebugger - другая попытка обеспечить безопасность содержимого swf. Если всегда защищаться паролем (Flasm будет показывать закодированную строку), то этот тег даст вам возможность "дистанционной отладки" swf. Если вы не знаете пароль, отладчик не позволит проникнуть внутрь. Если удалить пароль, отладчик тоже не позволит проникнуть внутрь. Но если изменить параметр у enableDebugger, как '$1$.e$7cXTDev5MooPv3voVnOMX1', то пустой пароль будет принят. Печально.

    Уясним это четко и ясно: вышеуказанные теги, включая кодированные пароли, не дают реальной защиты и могут быть благополучно удалены или изменены.

    Flash MX позволяет осуществлять отладку на уровне исходного кода, если есть новый тег enableDebugger2, который используется вместо enableDebugger. Хотя у него нет принципиальных отличий, Flasm не покажет другой тег (63) или содержимое внешнего файла, используемого отладчиком, не зная что-либо об их формате.

    Вложенные включения (includes)

    Я получал просьбы, включить во flasm поддержку макроса #include, чтобы помочь поддерживать большие проекты, где было бы возможно быстро оперировать 5.000 строчек flasm-кода. Теперь это есть и работает так, как и ожидается - если обнаружен #include 'loop.flm', то этот макрос будет заменен содержимым loop.flm. Можно точно так же использовать вложенные и многократные вложения: foo.flm включает routine.flm, который включает loop.flm и calc.flm. Максимальная глубина вложений - 10.

    Типы данных и команда push

    Итак, push это основная команда в swf и мы подробно на ней остановимся. С тех пор как вы можете помещать все виды значений на стек, команда push имеет внутренний атрибут типа в swf. В то время как вы не видите и не можете обращаться к push-типу изнутри flasm, он сам решает, какой тип использовать, основываясь на форматировании ваших данных.

    push-типКоличество байтСодержимоеПример

    0string length + 1stringpush 'Hello'
    14floatpush Y_PROPERTY
    20nullpush NULL
    30undefinedpush UNDEF
    41registerpush r:2
    51booleanpush TRUE
    68doublepush 3.1415926
    74integerpush 25
    81constant (0-255)push 'Hello'
    92constant (256-65534)   push 'Hello'

    Строки должны заключаться в одинарные или двойные кавычки и могут содержать escape-символы: \b, \f, \n, \r, \t и \\. Нельзя прерывать линии внутри строки. Если flasm обнаружит оператор push 'Hello', он сперва поищет в пуле текущий блок действий. Если строка в нем определена, то будет помещена 1- или 2-байтовая ссылка (push-тип 8 или 9), если нет, то сама строка (тип 0).

    Целые числа распознаются, как десятичные и шестнадцатеричные символы (0xF0F0). Числа с плавающей точкой: -3.1415926. Так же поддерживаются символы 9.4e-10. В дополнении к этому, константы _NAN, POSITIVE_INFINITY и NEGATIVE_INFINITY, определяются как с плавающей точкой.

    0.0 считается, как число с плавающей точкой, 0 - как целое. Внутри себя компилятор Flash хранит 0, как число с плавающей точкой 0.0. В режиме обновления (update) flasm будет автоматически заменять все случаи 0.0 на 0, экономя 4 байта на каждой такой замене.

    Push-тип 1 используется во Flash, чтобы только хранить свойство значений. Смотри getProperty/setProperty, чтобы увидеть список свойств констант. Flash 4 хранил все числовые значения, как строчные (push-тип 0), Flash 5 использует тип 7 для целых и push-тип 6 для чисел с плавающей точкой.

    Однако, Flash не единственная программа для создания swf. Сейчас я знаю, как минимум, одну программу стороннего производителя (3D-Flash Animator), которая использует тип 1 для свойства констант, если это возможно. Все значения, которые не могут быть приведены к какой-то константе, будут показаны, как числа с плавающей точкой: -3.1415926f или 100.0f. Вы тоже можете использовать это положение в своих flasm-проектах, экономя 4 байта на каждом числе. Любое значение с плавающей точкой, которое заканчивается f, будет обработано, как переменная с одинарной точностью и сохранена в виде push-тип 1 (осторожно с ограничением точности). Так же определятся и константы _NANF, POSITIVE_INFINITYF и NEGATIVE_INFINITYF.

    Один оператор push может оперировать составным значений различных типов: push 'Hello', 3.141, XSCALE_PROPERTY. Это не просто сокращение во flasm трех одиночных команд push, это путь сокращения и ускорения.

    Управление потоком

    Переходы внутри блока команд осуществляются командами branch и branchIfTrue. Любая высокоуровневая конструкция управления ходом выполнения, такая как if (..) then .. else .. или while(..) do .. преобразуется в последовательность команд branch/branchIfTrue. Команда branch производит безусловный переход на указанную метку в коде. Например, конструкция if .. then .. else всегда содержит команду безусловного перехода (т.е. branch) после блока then, в результате выполнения которой происходит переход к концу блока if без выполнения кода в блоке else. Разрешены переходы как вперёд, так и назад (по отношению к команде перехода). В частности, переход назад используется при трансляции циклов.

    branchiIfTrue извлекает значение с вершины стека (как правило, это результат работы предшествующей команды сравнения - прим. переводчика). Во Flash 5, если значение == true (извлечённое со стека значение в случае необходимости преобразуется к логическому типу), производится переход на указанную метку. Flash 4 сравнивает числа (а не логические значения) - если условие == 0, то переход не производится, если условие != 0, то производится переход на указанную метку.

    Относительные смещения переходов хранятся в swf в виде чисел вместе с каждой командой перехода. При дизассемблировании flasm создаёт уникальные (в пределах одного блока команд - прим. переводчика) метки с именами label1 .. labelN для каждого смещения, использованного в командах перехода. Такое преобразование скрывает от вас числовые смещения переходов и делает дизассемблированный код более удобочитаемым. Синтаксис команд перехода branch labelM или branchIfTrue labelN, где labelM и labelN - имена меток в коде, на которые производится переход. Использованные метки должны быть определены где-либо в том же блоке команд. Вы, конечно же, не обязаны использовать имена меток вида label5. Старайтесь использовать осмысленные имена меток (например, LoopStart:, SearchComplete: и им подобные).

    Давайте рассмотрим пример в действительности быстрого цикла с обратным отсчётом, который не может быть написан на ActionScript (и не может быть декомпилирован в корректный ActionScript).

    push 0,1,2,3,4,5,6,7,8,9,10
    loopstart:
    dup
    trace
    branchIfTrue loopstart 
    

    Вначале на стек помещаются 10 значений. Примите во внимание, что последнее помещённое на стек значение (в нашем примере это число 10) окажется на вершине стека. Затем в цикле мы создаём копию находящегося на вершине стека значения, так как оно потребуется нам дважды: команда trace возьмёт со стека первое значение, branchIfTrue получит второе в качестве условия цикла. Так как при выполнении branchIfTrue числовое значение на вершине стека будет преобразовано в логическое, то выполнение цикла продолжится до тех пор, пока на вершине стека не окажется 0 (нуль), который будет преобразован в логическое значение false и послужит сигналом к выходу из цикла.

    События кнопок

    Каждое отдельное событие кнопки типа on(...) содержит одно или несколько из следующих событий:

    idleToOverUpoverUpToIdleoverUpToOverDown
    overDownToOverUp    overDownToOutDown    outDownToOverDown
    outDownToIdleidleToOverDownoverDownToIdle
    keyPress  

    keyPress используется в форме keyPress 'символ' или keyPress константа, например, keyPress 'a' или keyPress _SPACE. Все константы, которые вы можете использовать при разработке Flash, определены как:

    _LEFT_RIGHT_UP_DN_HOME_END_INS
    _DEL_BACKSPACE_ENTER_PGUP_PGDN_TAB_SPACE

    Вы можете изменять события кнопок с помощью flasm-кода.

    getProperty и setProperty

    Обработка операндов getProperty/setProperty во Flash в чем-то противоречива. ActionScript-функция getProperty("a",_y) может быть записана следующими способами:

    push 'a', 1
    getProperty
        или     push 'a', '1'
    getProperty
        или     push 'a', Y_PROPERTY
    getProperty

    Вы (и Flash тоже) можете положить _y в стек как целое, как строку или как число с плавающей точкой. Все укладываемые в стек вышеперечисленные типы данных во Flash 5 обрабатываются без проблем, где нужно производится приведение типов. Во Flash 4, однако, нельзя было положить в стек целое число, а только строку или число с плавающей точкой. Следующая таблица показывает определенные во flasm константы свойств (они являются числами с плавающей точкой с точностью до одного знака после запятой):

    Название во FlashНомер   Константа flasmЗначение константы

    _x0X_PROPERTY0.0f
    _y1Y_PROPERTY1.0f
    _xscale2XSCALE_PROPERTY2.0f
    _yscale3YSCALE_PROPERTY3.0f
    _currentframe4CURRENTFRAME_PROPERTY4.0f
    _totalframes5TOTALFRAMES_PROPERTY5.0f
    _alpha6ALPHA_PROPERTY6.0f
    _visible7VISIBLE_PROPERTY7.0f
    _width8WIDTH_PROPERTY8.0f
    _height9HEIGHT_PROPERTY9.0f
    _rotation10ROTATION_PROPERTY10.0f
    _target11TARGET_PROPERTY11.0f
    _framesloaded 12FRAMESLOADED_PROPERTY12.0f
    _name13NAME_PROPERTY13.0f
    _droptarget14DROPTARGET_PROPERTY14.0f
    _url15URL_PROPERTY15.0f
    _highquality16HIGHQUALITY_PROPERTY16.0f
    _focusrect17FOCUSRECT_PROPERTY17.0f
    _soundbuftime18SOUNDBUFTIME_PROPERTY18.0f
    _quality19QUALITY_PROPERTY19.0f
    _xmouse20XMOUSE_PROPERTY)20.0f
    _ymouse21YMOUSE_PROPERTY21.0f

    По какой-то причине Flash 5 компилирует getProperty, используя push с числом с плавающей запятой, а setProperty, используя push с целым числом: так, setProperty("box2", _y, getProperty("box1", _y)) компилируется в

    push 'box2', Y_PROPERTY, 'box1', 1
    getProperty
    setProperty

    Если вы откомпилируете то же самое для формата Flash 4, то вместо целочисленного push будет использоваться push со строкой.

    Flasm по возможности дизассемблирует push с плавающей точкой в одну из констант, перечисленных в таблице выше, потому что этот тип push'а используется Flash'ем только в контексте getProperty/setProperty. Если используются числа или строки, тем не менее, flasm даже не пытается найти их значения. Чтобы делать так, flasm должен видеть, что за этим скрывается, чтобы понять, например, означает ли push 2 что-то вроде push _xscale или этот push 2 производится для вычисления выражения 2 + 2. Жаль, но это за пределами возможностей дизассемблера.

    Управление воспроизведением клипа

    SWF поддерживает три операции для этой задачи: gotoFrame(номер кадра в качестве операнда), gotoFrame2(номер кадра берется из стека) и gotoLabel(метка кадра в качестве операнда). Во flasm имена операций gotoFrame и gotoLabel совпадают с их аналогами в SWF, а вот операция gotoFrame2 отсутствует. Для вашего удобства gotoFrame2 представлена двумя операциями gotoAndPlay/gotoAndStop. В SWF gotoFrame2 представляет одну операцию с байтовым флагом play/stop. Также, если у вас в проекте несколько сцен, Flash добавляет еще один аргумент – общее число кадров во всех сценах до той, на которую осуществляется переход. Это число будет помещено в стек и эти кадры будут пропущены флеш-плеером. Благодаря этому возможно использование gotoAndPlay/gotoAndStop c внутренней адресацией кадров относительно текущей сцены, вместо абсолютной адресации относительно начала SWF. Запомните, сцен в SWF не существует. В этом случае flasm вам выдаст что-то типа gotoAndStop skip 10. Заметьте, что при этом у вас возникнут проблемы, если в выражении вместо числового номера кадра будет указана строковая метка. Флеш-плеер, не задумываясь, добавит также количество кадров, которое нужно пропустить и попадет на неправильный кадр. Попробуйте использовать _root.gotoAndStop(). В данном случае вместо обычной команды используется метод MovieClip’a. Он не выполняет коррекции и правильно обработает метку.

    К тому же, Flash 5 предлагает использовать gotoAndPlay/gotoAndStop методы объекта MovieClip для управления мувиклипами (передавая их, в виде строки). Сравните дизассемблированный код двух эквивалентных ActionScript-конструкций:

    // tellTarget("myClip") gotoAndPlay(25);
    setTarget 'myClip'
      gotoFrame 24
      play
    end
    

    и

    // myClip.gotoAndPlay(25);
    push 25, 1, 'myClip'
    getVariable
    push 'gotoAndPlay'
    callMethod
    pop
    

    Методы во Flash 5 значительно медленнее старых базовых команд, но у вас есть возможность переписать их, используя ООП. Заметьте, что gotoFrame начинает отсчет кадров с нуля, в то время как методы "верхнего уровня" начинают отсчет кадров с единицы.

    GotoLabel редко встречается в дизассемблинге, потому что флеш при экспорте SWF подставляет команду, использующую числовую адресацию кадров. Флеш оставит gotoLabel, только если не сможет вычислить номер кадра (если метка не в том же таймлайне?). Сами метки, тем не менее, все равно в SWF остаются и к ним можно получить доступ, даже если все переходы на эти метки были заменены.

    enumerate и enumerateValue

    Команда enumerate это нечто особенное. Вот как она работает:

    1. Извлекает из стека имя объекта.
    2. Вычисляет объект с указанным именем (как getVariable).
    3. Добавляет NULL в стек.
    4. Добавляет в стек все объекты потомки.

    Во Flash MX добавлена команда enumerateValue, вычисляющая безымянные объекты, которые уже находятся в стеке (не извлекая предварительно их имя).

    Обе команды используются только в for .. in циклах. Flash циклически обрабатывает всех потомков объекта до тех пор, пока не будет найден NULL. Ссылка на текущего потомка хранится в r:0 для доступа из тела цикла. Такой цикл работает эффективнее чем обычный for или while циклы. Снижение производительности может произойти только при работе с очень большими массивами, потому что при этом стек перегружен большим количеством данных.

    setTarget и setTargetExpr

    Команда setTarget соответствует команде tellTarget в ActionScript. Если целевой объект задан в виде выражения, то используется команда setTargetExpr, которая извлекает из стека строку-выражение. Flasm отображает это таким образом:

    setTarget '/defender'
      gotoFrame 1
      play
    end
        или     setTargetExpr
      gotoFrame 1
      play
    end

    Команды end на самом деле не существует, Flash использует setTarget '' чтобы отметить, где заканчивается "нацеленный" код.

    setTarget '/defender'
    gotoFrame 1
    play
    setTarget ''
        или     setTargetExpr
    gotoFrame 1
    play
    setTarget ''

    Так как любой блок setTarget во Flash 5 оформляется одинаково, я решил отображать их в более "читабельном" виде. Flash 3 или 4 порой грешат отсутствием команд setTarget '' в конце блоков. В этом случае flasm сам добавит их в процессе дизассемблирования, закрывая setTarget ... end блоки. Использование вложенных блоков setTarget не допускается.

    ifFrameLoaded/ifFrameLoadedExpr

    Блоки ifFrameLoaded frameNum .. end и ifFrameLoadedExpr .. end соответствуют теговым командам waitForFrame и waitForFrame2 в swf формате. ifFrameLoadedExpr извлекает номер кадра из стека. Я выбрал ActionScript-подобные имена потому что "waitForFrameExpr .. end" звучит некорректно - выполнить, если еще не загружено. Использование вложенных блоков ifFrameLoaded не допускается.
     

    Внедрение flasm-кода в ActionScript

    Если flasm запускается с ключом -u (flasm -u foo.swf), то он обрабатывает встроенные в ваш ActionScript макросы и добавляет в swf flasm-команды. Это не совсем похоже на внедрение ассемблера в Си или Паскаль. Нужно использовать специальный синтаксис, чтобы Flash смог откомпилировать скрипт без ошибок. В данный момент flasm поддерживает для этого две возможные конструкции в ActionScript: $flasm ... $end и $include(). Например:

    $flasm
    "push 'Hello world!'"
    "push myTextField"
    "setVariable"
    $end
    

    Этот набор команд делает то же самое, что и ActionScript-команда myTextField = "Hello world!";. Обратите внимание, что $flasm и $end - это переменные, а не функции, так что, пожалуйста, не пишите $flasm() или $end(). Все flasm-команды между $flasm и $end должны быть заключены в двойные кавычки. Точку с запятой после команды ставить не требуется (но допустимо). Блоки $flasm ... $end можно вставлять в любом месте вашего скрипта, так что не беспокойтесь об этом. Есть ли какие-то ограничения? Естественно. Не помещайте обычный ActionScript внутри $flasm блока, это не будет работать. Не задавайте кадров или мувиклипов во внедренном flasm. Если вы встраиваете, вы уже внутри какого-то определения кадра или события. Удостоверьтесь, что стек пуст после выполнения вашего кода. Это не ограничение, скорее совет, вы ведь не хотите терять свободную память?

    Все flasm-команды работают так, как вы и ожидаете, но нужно учитывать одну важную особенность - если вы объявляете константы во внедренных скриптах, flasm не изменит их значение, а добавит их в блок команд в корневой области. Также flasm реорганизует корневую область, удаляя пустые строки и константы, используемые только один раз, облегчая тем самым swf еще на несколько байт. Заметьте, что flasm не тронет ваши внедренные константы или строки, он перестроит только корневую область, созданную Flash'ем.

    Угадайте, что делает команда $include("foo.flm")? Не думаю, что нужно пояснять. Одно важное замечание: используйте только обычные слэши и не используйте обратные в пути к файлу. Последние будут проигнорированы, если не удалены Flash'ем. И еще одно: не вставляйте $include() внутри блока $flasm .. $end.

    Если в файле foo.flm объявляются константы, то эти строки будут добавлены в корневую область. Хотя $include("foo.flm") выглядит, как сокращенный аналог команд $flasm; "#include 'foo.flm'"; $end, это не так. Последнее выполняется в момент компиляции и не добавляет константы из foo.flm в корневую область. Вместо этого объявление констант удалит корневую область - будьте внимательны.

    Не смотря на то, что flasm 1.41 прекрасно работает со встроенной в MX компрессией, для этого потребуется два дополнительных шага: декомпрессия и обратная компрессия. Если ваш компьютер слабоват, вы можете предпочесть отключить компрессию в настройках публикации. Вы всегда сможете сжать swf через flasm -z на последнем этапе перед выпуском продукта.

    Проверка встроенных действий прямо из среды разработки

    Конечно, вы можете экспортировать swf-файл, обновить его с помощью flasm, а затем проверить, нет ли ошибок. Но проверка прямо из среды разработки намного привлекательнее. К несчастью, среда разработчика Flash IDE не предусматривает программного интерфейса для вставки программ предварительной обработки типа flasm'а. И к счастью, Sven König нашел один способ, а я реализовал его во flasm'е. С настройкой этого дела придется поморочиться, но зато будет работать просто волшебно, как только вы получите это в свое распоряжение. Я опишу процедуру для Windows, для Macintosh'а она почти такая же. Итак:

    1. Для Flash 5: скопируйте flasm.exe, libz.dll и flasm.ini в поддиректорию Browser в установочной директории Flash. Что касается Flash MX, которая где только не хранит свои настройки: сначала найдите нужную вам директорию и копируйте файлы в нее:
    Windows 2000 или XP: C:\Documents and Settings\[имя пользователя]\Application Data\Macromedia\Flash MX\Configuration\Browser
    Windows 98 или ME: C:\Windows\Application Data\Macromedia\Flash MX\Configuration\Browser
    Windows NT: [Windows directory]\profiles\[имя пользователя]\Application Data\Macromedia\Flash MX\Configuration\Browser
    Mac OS X: Hard Drive/Users/Library/Application Support/Macromedia/FlashMX/Configuration/Browser

    Заметьте, что встраивание flasm'а во Flash на Маках мною не проверялось, так что напишите мне пару строк, если вам удастся это сделать.

    2. Переименуйте flasm.exe в iexplore.exe

    3. Создайте ярлык для вашего нового iexplore.exe в той же самой поддиректории. Не беспокойтесь, это никак не скажется на реальном броузере.

    4. Откройте flasm.ini в текстовом редакторе. Измените значения flaplayer и flabrowser таким образом, чтобы они указывали пути к вашему Flash-player'у и броузеру соответственно. Длинные имена файлов не поддерживаются в DOS, так что вам нужно понять, как правильно пишется соответствующий путь с использованием коротких имен файлов, типа "C:\PROGRA~1\INTERN~1\IEXPLORE.EXE" или чего-то вроде этого. Даже если вы работаете на Win 2000, пожалуйста, используйте короткие имена файлов. Задайте значение flatest как "flaplayer", если хотите тестировать ваши файлы во Flash-player'е, или как "flabrowser", если будете тестировать в броузере. На моей машине flasm.ini выглядит как:

    flaplayer = C:\GRAPHICS\Flash5~1\PLAYER\FlashPLA.EXE
    flabrowser = C:\PROGRA~1\INTERN~1\IEXPLORE.EXE
    flatest = flaplayer
    

    Вы можете изменить значение flatest позже, при тестировании, причем для этого не потребуется никакой перезагрузки, ни машины, ни среды разработки Flash.

    6. Все. Теперь откройте ваш файл во Flash, вставьте flasm-код, убедитесь, что в настройках публикации установлены флажки "HTML" и "use default names" и жмите F12 (предпросмотр публикации).

    Мы просто заставили Flash думать, что flasm, это броузер. Flash будет компилировать swf, искать ярлык броузера, увидит, что имя (iexplore.exe) подходит и передаст имя HTML-файла flasm'у. После вычисления имени реального swf-файла (здесь есть что вычислять, так как путь закодирован для передачи как URL (URL encoded)), flasm обновит swf и вызовет броузер или плеер для просмотра готового результата. На короткое время появится окно сеанса DOS, но оно останется открытым только в случае возникновения ошибок, сообщения о которых появятся в этом же окне. Мне кажется (по собственному опыту) что самой популярной ошибкой будет:

    Could not start: c:\...\...\foo.exe

    ("Не могу запустить такой-то файл.."), из-за того, что в строке flaplayer или flabrowser написан неправильный путь. Исправьте путь и пробуйте снова.

    Не работает? Не беспокойтесь, все в порядке, если вы дошли до этого места. Иногда Flash просто не запускает flasm. Введите что-нибудь в окно actions. Или снимите галочку "HTML" в настройках публикации, опубликуйте, установите галочку обратно и снова опубликуйте. Или удалите ярлык для iexplore из директории "browser" опубликуйте, восстановите ярлык.

    Директива $include может не срабатывать в среде разработки Flash, если вы предварительно не экспортировали swf в нужное место, потому что Flash иногда компилирует swf в директорию по умолчанию.

    Как при этом заниматься отладкой? Стек и регистры: в ресурсах есть ссылка на небольшой дебагер от Pavils Jurjans, вы можете подстроить его под свои нужды. Трассировка и переменные: используйте для этого debug-версию Flash-player'а. Или скачайте iolib. Или напишите собственную функцию трассировки, которая будет работать в броузере. Подойдите к делу творчески! (Be creative).
     

    Техники оптимизации

  • Оптимизации ActionScript
  • Flasm-оптимизации
  • Двойные отрицания
  • Благодарности

    Огромные растровые картинки, не оптимизированные векторные, неадекватные показатели частоты кадров, анимация многих клипов одновременно, использование больших XML-файлов, тонны текстовых полей с редактируемыми текстами, высококачественный потоковый звук или просто просмотр swf на Маках — в 95% всех случаев, низкая производительность swf не имеет ничего общего с ActionScript. flasm, хоть и является "еще одним крутым инструментом", не решит вышеуказанные проблемы. Оптимизация с помощью flasm имеет смысл в играх, 3D-движках, алгоритме поиска пути, когда действительно преобразовываются большие наборы данных — короче говоря, когда идут вычисления.

    Если вы не знаете точно, где "узкое место" вашего проекта: то ли тормозит из-за медленной отрисовки, то ли из-за медленных расчетов, есть один простой трюк: сделайте окно проигрывателя или броузера очень маленьким. Если производительность значительно возрастет, значит, вам скорее нужно сначала заняться оптимизацией графики или структуры мувиклипов.

    В стандартном языке программирования, большая часть времени работы программы протекает в циклах и функциях, вызываемых из этих циклов. А во Flash есть еще и циклы в кадрах и частые или параллельно вызываемые события, они также должны быть исследованы. Период. Не пытайтесь оптимизировать каждую строку вашего кода — вы просто сделаете ее нечитабельной, за исключением некоторых особо важных мест и никто никогда не обратит внимание на 10.000 часов вашей изнурительной работы. После того, как вы обнаружили в коде критическое место, сначала попытайтесь найти для него алгоритм получше. Это место не может быть улучшено? В самом деле? Значит, начинайте изменять ActionScript.

    Оптимизации кода ActionScript

    Большой вопрос: помогает ли Flash MX повысить производительность? Пока реальное тестирование все еще никем не проведено, вот мое ощущение: в то время, как многие вещи типа производительности XML действительно стали лучше, старый, "устаревший" синтаксис Flash 4 все еще работает быстрее, чем красивый скрипт с точечной нотацией путей и большая часть советов в этой главе продолжает быть актуальной и для устаревших swf-файлов, и для созданных во Flash MX. Кроме того, в то время как проигрыватель стал лучше, компилятор лучше не стал: не надейтесь получить большую скорость, просто перекомпилировав ваши старые исходники в Flash MX, но производя при этом swf-файлы для Flash-player 5.

    Долгое время в эти советы не вносились изменения, если некоторые из них покажутся вам бессмысленными или не работающими во Flash MX, пожалуйста, сообщите об этом мне.

    Блоки команд всегда выполняются от начала до конца, не прерываясь при этом ни событиями, ни командой gotoAndPlay(). Вот почему любой большой цикл for будет нагружать Flash player, не производя при этом никаких обновлений экрана.

    Почему стиль кодирования Flash 5 работает медленнее, чем старые команды Flash 4? Casper Shuirink обнаружил забавную практику для Flash player 5: новые классы и методы внутри него создаются при помощи ActionScript! Включая алгоритмы работы со строками, методы массивов и мувиклипов — все. Большая их часть "завернута" в команды Flash 4. Посмотрите, что нашел Каспер. И снова: это не визуальное представление, это настоящий скрипт, использующийся внутри Flash 5.

    Нелегко догадаться, из-за чего Flash может работать медленно. Математические функции, включая обработку чисел с плавающей запятой, в общем работают неплохо. Пути к мувиклипам в стиле Flash 5 очень удобны, но чертовски медленно работают. Наихудший пример: myMC.gotoAndStop(), работает в 25 раз медленнее, чем tellTarget("myMC") gotoAndStop(). Там, где важна производительность, всегда обращайтесь к объектам в синтаксисе Flash 4, пишите ../:myVar а не _parent.myVar. Также продолжайте использовать getProperty для составных путей типа a=getProperty("main/mc" add n,_x) и eval. Если вы декомпилируете и сравните некоторые ваши файлы, вы сможете все увидеть сами, просто подсчитав количество компилируемых действий. Разве эти действия не считаются "устаревшими" во Flash 5? Читайте: Устаревший синтаксис.

    eval — это нечто особенное по сравнению со, скажем, this или любым другим ключевым словом ActionScript. Фактически, eval — это что-то вроде макроса, он не имеет собственного байткода, но просто пишет свои аргументы в стек во время компиляции. Несомненно, это работает быстрее.

    Используйте tellTarget вместо with везде, где это возможно.

    Определяйте локальные переменные функций с помощью ключевого слова var, но не используйте var в циклах. Локальные переменные работают быстрее (и вообще относятся к хорошей практике программирования). К несчастью, длина идентификатора также имеет значение, так что выбирайте для переменных короткие имена. Это утверждение можно распространить также и на встроенные функции. Создание шортката для функции t = Math.tan и замена всех вызовов Math.tan на t преследует две цели: во первых, не будет производиться лишний поиск объекта Math, а затем метода tan, и само по себе имя получается короче (а значит, обрабатывается быстрее). Это работает только для методов и функций Flash 5, функции Flash 4 будут замедляться.

    Искусство инициализации строк: если вы в своем коде используете строчные функции Flash 4 типа ord(), то сначала инициализируйте строки с помощью выражения a="my string". Flash 5 работает быстрее, если строки проинициализированы с помощью a=new String("my string"). Похоже на то, что Flash player производит дополнительные преобразования литералов в объекты и обратно, а на это уходит время. Тем не менее, в критических местах вашего приложения избегайте работы со строками в стиле Flash 5.

    Некоторые функции работают медленно независимо от того, как вы их вызовете: String.split() действительно ужасна, и sp = String.split не помогает. Branden Hall создал собственные строчные функции, которые назначаются поверх используемых по умолчанию во Flash. Там, где для работы со строками невозможно использовать синтаксис Flash 4, используйте функции от Брендана.

    Еще один пример: random() быстрее, чем Math.random() и не такая уж плохая функция. Даже если на Math.random сделана короткая ссылка-шорткат, как было описано выше.

    Используйте a[a.length] = 25 вместо a.push(25). Да! Метод Array.push() выглядит симпатичнее, ему даже соответствует меньший байткод, но он медленнее.

    Насколько мне удалось установить при тестировании, многомерные массивы работают не медленнее, чем одномерные, хотя я слышал от людей, что они все же тормозят.

    Трюк с заменой b = a*4 на b = a«2 (побитовый сдвиг) не добавляет скорости в ActionScript.

    Flash пытается предварительно вычислять постоянные части ваших выражений. Порядок вычислений зависит от приоритета операторов. Как заметил Robert Penner, rad = Math.PI/180 действительно сохранит вычисленное значение в swf, в то время как rad = c*Math.PI/180 этого не сделает. Вывод: явно указывайте приоритет, чтобы срабатывало предварительное вычисление констант (rad = c*(Math.PI/180) для данного случая).

    Компилятор Flash также оптимизируют вызовы библиотечных функций с постоянными аргументами. Вызовы типа f = Math.sin(0.25) или f = Math.max(3,5) никогда не сохраняются в swf, а вместо них сохраняется вычисленное значение. Не сохранятся выражения if, условные выражения которых всегда разрешаются в false. Если вы проведете собственные тесты производительности, не смущайтесь, обнаружив вышеприведенные факты.

    Немного общей оптимизации:

    не оптимизированный кодоптимизированный код

    b = a+10;
    c = b*2;
    c = (b = a+10)*2;
    // Flash использует быстрый регистр

    d = a*(b/n+c/n);
    e = b*(b/n+c/n);
    t = (b+c)/n;
    d = a*t;
    e = b*t;

    for (i=0;i<10;i++) {
       if (a!=0) {
          someFunction(i*10);
       }
    }
    var d = someFunction;
    if (a) {
       for (var i=0;i<100;i+=10) {
          d(i);
       }
    }

    for (i=0; i<x.length; i++)
       x[i] *= Math.PI*Math.cos(y);
    var len = x.length;
    pc = Math.PI*Math.cos(y);
    for (var i=0; i<len; i+=2) {
       x[i] *= pc;
       x[i+1] *= pc;
    }

    Циклы for и while не показывают различий в скорости выполнения. Все зависит от того, как вы их пишете. Наиболее оптимальная форма ActionScript-записи их обоих, когда производится выполнение цикла с обратным отсчетом "вниз" до 0, производит одинаковый байткод: for(var i = 10; i--;) {} и i = 10; while (i--) {}. Третья часть цикла for, отсутствующая в моем примере, на самом деле находится в теле цикла, так что вы можете сравнивать его с нормальным циклом while.

    Цикл for .. in, использующий ключевое слово enumerate, будет пробегать через массив слегка быстрее, чем обычный цикл for — как минимум для массивов среднего размера. Для огромных массивов истинным является обратное утверждение.

    Избегайте множественных параллельных функций hitTest() в событиях, это часто встречается в играх. Если игрок должен умереть после любого столкновения с врагом и у вас есть 100 дублированных мувиклипов с врагами, не навешивайте код на событие enterFrame вражеских клипов. Создайте новый мувиклип и вставьте вражеский клип в него. Затем дублируйте врагов внутри этого родительского клипа. Теперь вы можете проверять только один hitTest(), если имеет место столкновение. Если нужно, используйте математику для вычисления конкретного врага, с которым произошло столкновение. Так как большую часть времени никаких столкновений не происходит, вы получите действительно значительное повышение частоты кадров (fps). Если нужно проверять столкновения многих объектов друг с другом, начните с чтения этой статьи.

    Flash-player для Макинтоша очень медленный по сравнению с аналогичным для PC. Начинайте тестирование на маках как можно раньше.

    Производя тесты, помните: встроенный в среду разработки Flash-player использует много памяти и ведет себя иначе, чем обычный standalone-player и плагин для браузера. Кажется, Internet Explorer показывает лучшую производительность при прямом воспроизведении swf (а не включенном в html). Если вы запустите два окна броузера с swf одновременно, производительность резко упадет. Даже если окно с swf теряет фокус, производительность swf падает.

    Список ни коим образом нельзя считать полным, есть еще тонны вещей, которые я еще сам должен обнаружить. Я по большей части не говорю "Медленнее в 3.45 раза", потому что такие сравнения очень контекстно-зависимы, точные значения могут варьироваться. Мое "медленнее" просто означает "заметно медленнее и причем в любой ситуации".

    Flasm-оптимизации

    После того, как вы закончили оптимизацию ActionScript, можно приступать к оптимизации с помощью flasm. В общем, есть две важные низкоуровневые штуки, недоступные из ActionScript и таким образом являющиеся предметом для работы flasm'а: стек и регистры. Давайте для начала прооптимизируем простой цикл, используя для этого стек. Вот наш код ActionScript:

    for (n=0; n<1000; n++) {
        someFunction(n)
    }
    

    Flash компилирует этот цикл в следующие байтовые коды:

    constants 'n', 'someFunction' // Сохраняет все константы в пуле
                                  // констант
    push 'n', 0.0                 // Кладет строку 'n' и начальное
                                  // значение 0 в стек
    setVariable                   // Инициализация счетчика цикла: n = 0
      label1:                     // Начало цикла
    push 'n'
    getVariable                   // Снова берет значение переменной 'n'
    push 1000                     // Кладет в стек предельное значение
                                  // для цикла
    lessThan                      // Вычисляет булево выражение: "n<1000?"
    not                           // Инвертирует: теперь "n >= 1000?"
    branchIfTrue label2           // Если в стеке "true", идет к его концу
    
    push 'n'                      // Тело цикла
    getVariable                   // Снова берет значение 'n'
    push 1, 'someFunction'        // Кладет в стек кол-во аргументов (1)
                                  // и имя функции
    callFunction                  // Вызывает функцию с n в качестве
                                  // аргумента
    pop                           // Выталкивает из стека возможный
                                  // результат выполнения функции — циклу
                                  // он не нужен
    
    push 'n', 'n'                 // Дважды кладет 'n' в стек
    getVariable                   // Снова вычисляет 'n'
    increment                     // теперь в стеке n+1
    setVariable                   // n = n+1
    branch label1                 // безусловно переходит на начало
                                  // цикла
      label2:                     // конец цикла — вызванный
                                  // вышеприведенным branchIfTrue
    

    Сразу можно заметить, что переменная n вычисляется здесь множество раз. Действие getVariable работает медленнее, чем операции со стеком и n используется только как локальный счетчик. Почему бы не отказаться от переменной n, хранить счетчик в стеке, использовать его снова и снова, таким образом устранив все вызовы getVariable? Нам также не нужно объявление пула констант, так как n исчезнет, а имя функции someFunction будет использовано всего один раз. Количество переходов (jumps) также может быть уменьшено до одного. Мы знаем, что должны вызвать someFunction(0), так что нет необходимости проверять условие в начале цикла. Посмотрите на оптимизированную версию:

    push 0                  // Не нужно дублировать 0.0, и целое число 0
                            // справится
      loopStart:            // Выбираем осмысленное название
    dup                     // Дублируем (dup) счетчик — наша функция
                            // "съест это"
    
    push 1, 'someFunction'' // Кладем в стек кол-во аргументов (1)
                            // и имя функции
    callFunction            // производятся вызовы функции с аргументом n
    pop                     // Выталкиваем из стека неиспользуемый
                            // результат выполнения функции
                            // Теперь счетчик снова наверху стека
    increment               // Увеличиваем его
    dup                     // Дублируем (dup) счетчик — вычисление
                            // условия "съест" его
    push 1000               // Кладем в стек конечное значение цикла
    lessThan                // Вычисляем условие: counter < 1000?
    branchIfTrue loopStart  // Прыгаем в начало цикла, счетчик наверху
                            // стека
    pop                     // Следует удалить счетчик из стека после
                            // завершения цикла
    

    Мы даже можем сделать больше. Скажем, если наша функция заполняет массив некими вычисленными значениями, то нет разницы, считать ли от 0 до 999 или в обратном порядке от 999 "вниз" до 0. В этом случае мы можем избавиться от команды lessThan, так как команда branchIfTrue умеет преобразовывать значение 0 в "false", а все другие числа в "true".

    push 1000
        loopStart:
    decrement
    dup
    push 1, 'someFunction''
    callFunction
    pop
    dup
    branchIfTrue loopStart
    pop
    

    Мы переместили команду decrement на вершину цикла, потому что в противном случае команда branchIfTrue автоматически привела бы к выходу из цикла, если значение переменной counter равно 0 и не позволило бы нам выполнить someFunction(0).

    Как видите, мы пришли к весьма чистой версии цикла и это будет работать намного быстрее, чем оригинальный цикл Flash. Насколько именно это будет быстрее работать, зависит от того, что делает функция someFunction(). На следующем шаге мы оптимизируем ее.

    Теперь подумаем, стоит ли вообще использовать регистры? Они быстрее, чем переменные, но все же медленнее, чем стек. Почему бы не держать все величины в стеке, так, чтобы они перемещались наверх точно в тот момент, когда они нужны?

    Проблема в том, что если вы работаете с двумя или более переменными, ваш алгоритм может затребовать их в иной очередности, чем та, в которой они хранятся. Если некое значение требуется, скажем, в начале и в конце вашей функции — тогда нет проблем, оно счастливо живет себе на дне стека, ожидая момента, когда его позовут и позволяет вам работать с другими значениями, находящимися наверху. Но для часто затребуемых значений это не работает. В то время как у нас есть команда swap, меняющая местами два верхних значения в стеке, мы не можем получить прямой доступ к третьему. Даже если найдете некий иллюзорный метод доступа сразу ко многим переменным, вы просто тормознете выполнение кода большим количеством команд swap.

    В общем: dup конечно быстрее чем setRegister r:1 и затем push r:1. Но если вам нужно делать swap, ситуация меняется: комбинация dup/swap не быстрее, чем setRegister/push. Фактически, она будет даже немного медленнее, если вы сольете push r:1 с другими ему подобными: push r:3, r:1, X_PROPERTY — слияние push вносит реальные различия.

    Хватит слов, приступим к оптимизации реального кода ActionScript. Давайте напишем функцию получения минимального значения в массиве чисел. Наш код выглядит так:

    Array.prototype.min = function () {
        var m = Number.MAX_VALUE;
        var i = this.length;
        while (i-- > 0) {
            if (this[i] < m)
                m = this[i];
        }
        return m;
    } 
    

    Если мы начнем цикл с var m = this[0]; и затем будем считать "вниз" до 1 вместо 0, функция выполнится немного быстрее. Хотя это нам не поможет. Мы попробуем делать проверку на 0 позже в байткодах. Проверка на 1 не нужна.

    Мы можем также попробовать этот код:

    Array.prototype.min = function () {
        var m = Number.MAX_VALUE;
        for (var i in this) {
            if (this[i] < m)
                if (typeof(this[i]) == 'number')
                    m = this[i];
        }
        return m;
    } 
    

    Думаю, что в данном случае это лучшая из всех возможных ActionScript-оптимизаций кода, потому что Flash компилирует циклы for .. in в более эффективные байткоды, чем в случае с каким-либо другим циклом. В то же время, остается меньше простора для нашей оптимизации. Я оптимизировал оба варианта и начинать лучше с первого. Посмотрите на оптимизированные байткоды:

    push 'Array'                 // Array.prototype.min
    getVariable
    push 'prototype'
    getMember
    push 'min'
    
    function ()
      push 'this'
      getVariable                // вычислить 'this'
      setRegister r:1            // сохранить его в r:1
      pop                        // удалить его из стека
    
      push 1.79769313486231e+308 // Number.MAX_VALUE
      setRegister r:2            // сохранить Number.MAX_VALUE в r:2
      pop                        // удалить его из стека
    
      push 'this.length'
      getVariable                // вычислить this.length
                                 // цикл вниз до 0
    loopStart:                   // counter в стеке
      decrement                  // counter--
      setRegister r:0            // сохранить counter в r:0
      push r:0, r:2, r:1, r:0    // стек:
                                 // counter, this, min, counter, counter
      getMember                  // стек:
                                 // this[counter], min, counter, counter
      lessThan                   // стек:
                                 // min<this[counter]?, counter, counter
      branchIfTrue loopContinue  // стек: counter, counter
                                 // если условие не выполняется, найдено
                                 // новое минимальное значение
                                 // (а мы ищем min)
      push r:1, r:0              // стек: counter, this, counter, counter
      getMember                  // стек: this[counter], counter, counter
      setRegister r:2            // сохранить this[counter] в r:2 как новое
                                 // значение min
      pop                        // удалить его из стека
                                 // стек: counter, counter
    loopContinue:                // проверить, достигли ли мы уже 0:
      branchIfTrue loopStart     // стек после команды branch: counter
                                 // цикл закончен, теперь почистим стек:
      pop                        // вынем последнее значение counter
                                 // (равно 0)
      push r:2                   // заберем min из r:2
      return                     // возвращаем min
    end
    
    setMember                    // назначаем функции Array.prototype.min
    

    Функция оптимизирована для больших случайных массивов. Самое "трудное" место оптимизировано максимально — все команды push скомпонованы в одно выражение. Мы даже допускаем множественные присвоения одинаковых значений переменной min (когда min == this[counter]), чтобы сделать это. По этой же причине мы храним counter в r:0, а не в стеке. Одна из моих первых версий так и работала — swap'ы, dup'ы и множественные push. Так делать нехорошо. И еще, при нахождении нового значения min, this[counter] вычисляется еще один раз. Мы могли бы хранить его в r:3 после первого вычисления, но это замедлит процесс в общем — больше команд выполняется в главной части цикла.

    А вот оптимизированная версия цикла for .. in:

    function ()
      push 'this'
      getVariable
      setRegister r:1
      pop
     
      push 1.79769313486231e+308
      setRegister r:2
      pop
     
      push 'this'
      enumerate
     
    loopStart:
      setRegister r:0
      push NULL
      equals
      branchIfTrue loopEnd
     
      push r:2, r:1, r:0
      getMember
      lessThan
      branchIfTrue loopStart
     
      push r:1, r:0
      getMember
      setRegister r:2
      pop
     
      branch loopStart
     
    loopEnd:
      push r:2
      return
    end 
    

    Как видите, эти два варианта частично похожи. Интересное место представляет собой enumerate. Все ссылки на элементы массива укладываются Флэшем в стек за одно действие. На самом дне хранится значение NULL, цикл постоянно ищет его как условие своего завершения. Такой код в чем-то более элегантен, чем предыдущая версия. Но на больших массивах он начнет тормозить, потому что в стеке хранится целый массив, сравните это с хранением 4-5-ти значений, как было в предыдущей версии.

    Итак, насколько быстры наши функции после всего этого? Оптимизированная версия "нормального цикла" в три раза быстрее, чем самая быстрая версия на ActionScript. А оптимизированная версия цикла for .. in быстрее приблизительно в 2-2.5 раза.

    И последнее замечание: кроме оптимизации существующего кода ActionScript, во Flash есть такие вещи, которые вы просто не можете делать (либо они получатся крайне неэффективными), не привлекая байтовые коды. Посмотрите на высказывание Роберта Пеннера о передаче различного количества аргументов в функции.

    Двойные отрицания

    В некоторых случаях Flash пишет в вашем коде двойные отрицания (nots). Рассмотрите код if (a<=b) ...  else ... Поскольку указаны только lessThan и branchIfTrue, а не moreThan или branchIfFalse, созданы две инверсии:

    push 'b'
    getVariable
    push 'a'
    getVariable
    lessThan    // a>b?
    not         // now inverted: a<=b?
    not         // prepare for branch to the else condition: again a>b?
    branchIfTrue elseCondition
    

    Как вы видите, Flash 5 не слишком гибко трактует ваши требования, не меняет порядок операндов в выражении и не использует иной образец для условного оператора if. Это действительно не имеет смысла. Единственной причиной этого может быть попытка преобразования в Boolean. Однако следующая команда, которую вы видите в коде – branchIfTrue. И эта команда преобразует типы непосредственно. Таким образом, flasm автоматически удалит эти nots в update mode (в режиме модификации).

    Благодарности

    Мои особые благодарности предназначены людям из рассылки Flashcoders, чьи идеи помогли мне лучше понять оптимизацию и выразились в вышеупомянутых примерах:

    Rasheed Abdal-Aziz, Ralf Bokelberg, Robin Debreuil, Zeh Fernando, Gary Fixler, Branden Hall, Dave Hayden, Damien Morton, Amos Olson, Robert Penner, Casper Schuirink.
     

    Устаревший синтаксис

    Поскольку Macromedia продолжает называть некоторые команды "устаревшими" (deprecated), я также решил оставить мои нижеследующие выкладки неизменными. Они были написаны достаточно давно, оказались верными, и теперь, вероятно, относятся к Flash 7 (или Flash XXL). Моя единственная ошибка - MX IDE не поддерживает устаревшие команды.

    Всякий раз при использовании команд или синтаксиса 4-го Flash, мы слышим, что они устарели, "плохой код" и что их следует избегать. Я считаю важным подчеркнуть здесь, что ActionScript и bytecodes – это разные вещи...

    Когда вышел релиз Flash 6, он не повлиял на работу созданных ранее swf-файлов. Я не верю, что Flash 6 не сможет проиграть swf, созданные в 5-м или 4-м Flash. Примером могут служить миллионы сайтов: многие из них ни разу не были обновлены. Станут ли они оптимизировать свой код, чтобы он выполнялся лучше, чем в 4-м плеере? Трудно поверить, но если и так, Flash 6 player еще будет расширять круг своих пользователей – прошел почти год после выхода релиза, а значительная часть пользователей еще раздумывает.

    Если код Flash 4 не будет поддерживаться во Flash 6 IDE (весьма вероятно), это значит, что вам придется внести некоторые изменения в ваш код и только если вы обновляете его в 6-й версии. Flash 6 наверняка сделает большинство изменений автоматически – как синтаксис файла Flash 4 конвертируется при открытии в Flash 5.

    Если нет – в конце концов, есть возможность полностью заменить скрипт. Если нет – кто-нибудь напишет утилиту для этого. Можете ли вы представить себе, что Flash 6 не сможет открыть Flash 5 fla-исходник?

    Что касается меня, я посоветую не беспокоиться насчет устаревших команд, а просто писать код, наиболее соответствующий решению вашей задачи. Тем не менее, я не убеждаю вас забывать о синтаксисе Flash 5. Использовать Flash 4 имеет смысл лишь в случае проблем со скоростью.
     

    Различия в размерах файлов

    После компиляции из flasm-исходника или публикации swf из flasm, вы в большинстве случаев обнаружите, что ваш swf уменьшился на несколько байт, даже если вы ничего не меняли в байткоде. Помимо тривиальной оптимизации, которую flasm делает при обновлении (update mode), для этого есть еще одна причина. По определению, Flash может использовать в swf блоки длиной в 2 или 6 байт. Причем 6 байт требуются только в том случае, если длина блока (в исходнике) превышает 62 байта. Flash, тем не менее, довольно часто использует в swf записи длиной в 6 байт там, где достаточно 2 байт. Хотя flasm в некоторых случаях делает так же, большинство блоков оптимизируются в процессе компиляции. Например, я экономлю 400 байт на файле в 90 Кб, ничего не оптимизируя. Не знаю, есть ли тут особая выгода, но по крайней мере, можно радоваться неожиданному подарку от flasm.
     

    Большие скрипты

    Хотя хорошей практикой остается удерживать размер (компилированного) кода в пределах 64 Кб на фрейм, иногда код получается гораздо большего объема. Но основные actions – константы, функции и другие, - ограничены 64k по причине длины поля в 2 байта. Превышение станет возможным с того момента, когда Flash создаст (в 99% случаях) постоянное согласование всех переменных и методов. К сожалению, Flash сам не может сообщить вам, что код слишком велик. Компилятор флеша записывает превышающие значения длины записей без ошибок или предупреждений. Если вы пытаетесь воспроизвести swf, у Flash-плеера происходит сбой, или не выполняются некоторые команды. Flasm остановит дизассемблирование с сообщением об ошибке, если столкнется с такой ситуацией.
     

    Ошибки и сбои

    Adobe LiveMotion 2 помещает в события странный код. Flasm укажет на непонятный "скрипт", который вовсе не предназначен для выполнения, однако вводит в заблуждение (или попросту засоряет память). Наиболее вероятно, это ошибка в LiveMotion, поскольку это не соответствует формату swf. Возможно, ОНИ знают что-то, а я нет. Если у вас проблемы с swf, созданном в LiveMotion – свяжитесь с НИМИ :) Если вы уверены, что я в этом не прав – свяжитесь со мной.

    Мне не известно о других ошибках. Если вы обнаружите любую нетривиальную ошибку – без стеснения вышлите мне ваш файл. Поясню: если вы дизассемблируете swf и ассемблируете его обратно без изменений, результирующий файл обязан работать должным образом. Режим обновления (Update mode) также должен работать. Во flasm нет ничего необъяснимого или каких-либо недокументированных особенностей. Если у вас возникла проблема - должно быть, в нашем инструменте есть серьезная ошибка, и ваше сообщение для нас очень ценно.
     

    Ресурсы

    Данная страница в оригинале лежит по адресу http://flasm.sourceforge.net или http://www.nowrap.de/flasm.html.

    Исходник на том же сервере: http://sourceforge.net/projects/flasm, хотя несколько устаревший. Так как я – единственный, кто работает над flasm в настоящее время, бессмысленно постоянно синхронизировать все ресурсы. Возьмите самый свежий файл отсюда.

    Оригинальная страница flasm: http://www.opaque.net/~dave/flasm/. Там постоянно находится первая версия и полезные объяснения Дейва по поводу байт-кодов Flash 5.

    Замечательный пример - 3D-движок от Florian Krüsch, оптимизированный при помощи flasm.

    Сравните анимацию дерева, сделанную Amos Olson: стандартный ActionScript и оптимизированный.

    Посмотрите path finding swf сделанный с помощью flasm от Casper Schuirink. Здесь исходник.

    Грустная история: David Emberton поддерживал flasmaniacs рассылку посвященную исключительно flasm. Его провайдер неожиданно прекратил поддержку рассылки, теперь ни рассылка, ни архив не доступны. Спасибо Branden Hall, новый SWFcoders mailing list стартовал (19.08.02). Для подписки пошлите пустое сообщение на swfcoders-subscribe@chattyfig.figleaf.com (нормальный режим рассылки) или swfcoders-digest-subscribe@chattyfig.figleaf.com (рассылка в режиме дайджеста).

    Многие вопросы касаемые ActionScript обсуждаются в популярном листе, модерируемом Branden Hall, на http://chattyfig.figleaf.com. Это место, где можно многое узнать также и про оптимизацию flasm. Хотя не вздумайте спросить там что-то вроде "Что такое event?" Не стоит ожидать вежливую реакцию на такой вид вопросов. Вначале прочитайте Flash help, а затем - архив Flashcoders.

    Flashcoders Wiki обещает превратиться в самый совершенный и современный ресурс по ActionScript, очень информативно.

    http://www.openswf.org ресурс для обсуждения формата swf. В настоящее время доступны только описания по Flash 4. Будет ли больше?

    Вы можете захотеть посетить Macromedia Flash player format and SDK licensing на http://www.macromedia.com/software/Flash/open/licensing/. С тех пор как я перестал пользоваться их ресурсом, я не знаю, насколько подробна их специализация Flash 5.

    На сайте посвященном прототипам вы найдете функции, используемые Flash, но улучшенные (для скорости и универсальности), а также много полезных новых. Часто лучше начинать оптимизацию flasm с одной из них.

    Pavils Jurjans написал небольшой debugger для flasm, полезного при внедрении кода flasm в ActionScript. Отладчик показывает стек и регистрирует контент. Я надеюсь, что-то подобное будет включено в последующие выпуски flasm's.

    Albert Chosky сделал файлы подсветки синтаксиса flasm для EditPlus. Если вы используете EditPlus, полезно будет скачать их.

    Для людей, которые не любят работать с командной строкой и не хотят регистрировать flasm как обработчик swf в эксплорере, есть winflasm от Sharriff Aina - простой оконный интерфейс для flasm.

    Файл подсветки синтаксиса для UltraEdit, предоставленный анонимным русским flasmer. Не знаю, насколько он закончен, но спасибо в любом случае.
     

    Условия использования

    Да, flasm абсолютно бесплатен и распространяется согласно BSD-style лицензии:

    Copyright (c) 2001, 2002 Opaque Industries and Igor Kogan
    All rights reserved.

    Распространение и использование в исходном или бинарном коде, с изменениями или без, разрешаются при условии, что нижеследующие соглашения выполнены:

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

    Macromedia(r) и Flash(tm) зарегистрированные торговые марки, собственность Macromedia, Inc., в США и/или других странах.

    Macromedia(r) не является спонсором, партнером или гарантом данного продукта и/или услуг.
     

    История изменений

    flasm 1.4

    полностью поддерживает новые возможности, предоставленные Flash MX:

    flasm 1.36

    flasm 1.35

    flasm 1.32

    flasm 1.3

    flasm 1.22

    flasm 1.21

    flasm 1.2

    Это была моя первая "официальная" версия, главным отличием от оригинала Дейва была способность иметь дело с целым swf. Добавлено дизассемблирование событий, несколько отсутствующих байткодов, исправлено несколько ошибок. Не помню точно все изменения, этой страницы еще не было.
     

    Состояние проекта

    Плохие новости: я не собираюсь реализовать новые восхитительные возможности. Время идет и я уже довольно сильно потерял к этому интерес. Сейчас погружен в другие вещи: Linux, Python и т.д. Хорошие новости: flasm работает достаточно стабильно, а исправления глюков (если будут такие) будут проводиться. Возможно, сейчас лучшее время для того, чтобы поднять flasm на уровень выше и сделать его Самым Важным Инструментом Flash для каждого :)

    Вот кое-что из того, что я хотел бы видеть в новых релизах:

    Генерирование XML

    Чтобы flasm парсил XML и на лету писал для него соответствующие ActionScript-структуры. Вместо того, чтобы использовать обработку на сервере, вы бы на своей машине создавали swf, содержащий "частично загруженный" в главный клип XML. В любом случае это намного быстрее Flash-парсеров. А еще можно написать парсер с проверкой документа на валидность.
    P.S. Это намерение стало устаревшим, так как во Flash MX производительность работы с XML повысилась, но только для Flash MX player, Flash 5 player по прежнему слаб.

    Защита от декомпиляторов

    Задача не легкая, но выполнимая. Сейчас я могу запутать текущие версии декомпиляторов, но они потратят всего неделю, чтобы это пофиксить, так что "вешать" декомпилятор - не очень хорошая идея. Лучшей схемой защиты является та, при которой не возникают ошибки в декомпиляторе, но код просто не работает после перекомпиляции. Я все еще не знаю точно, как это сделать.

    Второй лучший способ: декомпилятор на самом деле не производит выполнение кода или воспроизведение swf, так что наличие определенных свойств во время выполнения может быть использовано для создания "непредсказуемых" ветвлений кода. Если вы сделаете что-то вроде if (root._width>0), у вас есть совершенное ветвление, его результат предопределен и в то же время он не может быть определен obfuscator'ом. Он может декодировать if, но не знает, куда ведет ветка кода. В блоке else (мертвый код) вы можете разместить любой сбивающий с толку мусор. Теперь управляющий поток нечитаем, потери в скорости минимальны, ведь произошла только замена безусловного перехода на условный.

    Третий лучший способ: запутывание. Это означает переименование всех переменных, функций и мувиклипов в нечитаемый мусор. Робин Дебройл реализовал этот метод в своем Viewer Screwer'е. По разным причинам выполнить такую задачу очень сложно. Можно было бы создавать все типы объектов, основываясь на именах объектов во время выполнения. Но это означает, что вы никогда не будете уверены в правильной работе вашего "запутывальщика" на любом swf без побочных эффектов.

    Опасная возможность размещения кода в других тегах, скажем, в тегах для растровых изображений и перехода туда и обратно, скорее убьет сам Flash player. Декомпилятор все еще будет способен переходить по условиям и декодировать это. Так что я лучше буду оперировать обработкой условных переходов так, как того ожидает Flash player.

    Замена переменных регистров в режиме IDE

    Если кто-то пишет $r1 = a+b в ActionScript, flasm обновит swf и сохранит a+b в r1. Это ясная и хорошая концепция, предложенная когда-то Eli Jehoel, но (вопреки ожиданиям) это не так легко реализовать. В двух словах - flasm должен будет постоянно вести учет содержимого стека.

    ActionScript profiler/tracer

    Трэйсер очень нужен - если вы работали с flasm из среды разработки Flash, вы это уже знаете. Профайлер: flasm писал бы код профайлера в начале/конце каждого кадра/события/функции. Затем значения (количество вызовов, максимальное время выполнения) были бы сохранены, скажем, в Object.profiler и к ним можно было бы получить доступ из ActionScript. Это скорее задача интерфейса Flash, ее не так сложно реализовать на языке C. Во Flash нам нужно скроллируемое/перетаскиваемое/масштабируемое/минимизируемое окно, содержащее результаты трассирования и записи профайлера. В профайлере пользователю надо дать возможность сортировать записи по различным критериям (вы знаете, это как в ms outlook).
     

    Как вы можете помочь

    Если вы более-менее понимаете формат SWF, знаете C и слышали о Flex и Bison, вы можете помочь в разработке. В настоящее время код не очень чист, так что не стесняйтесь спрашивать меня. И в любом случае есть хорошие шансы, что вы окажетесь лучшим разработчиком, чем я!

    Я хочу знать о вашем опыте применения flasm в реальных проектах. Что-то нелогично? Баг-репорты? Нужны новые возможности? Пожалуйста не спрашивайте хоть о Windows IDE :) Не забывайте о пользователях Макинтош (вы знаете этих странных персон в черном, работающих в дизайн-агентствах). Добавьте собственные определения (definitions) к вашему любимому редактору кода и распространяйте их (речь идет о файлах синтаксиса flasm для текстовых редакторов, чтобы последние могли "понимать" flasm, раскрашивать его код и т.п. - пр. перев.). Файлы синтаксиса flasm для EditPlus уже существуют, создал их Albert Chosky. Файл синтаксиса для UltraEdit тоже есть.

    Кстати о Маках. У меня нет Мака и я пока не собираюсь его заводить. Было бы хорошо, если бы кто-то описал свой рабочий опыт применения Flash/flasm на Маке.

    Но еще интереснее узнать о ваших оптимизациях. Если я пропустил что-то важное, сообщите мне, пожалуйста, об этом. Если у вас есть весьма своеобразная работа, но вы считаете, что ваш "пофлазмованный" swf вдохновляет, я могу поставить линк на этой странице.

    И напоследок: я не считаю свой английский ужасным. Не думаю также, что он хорош (в любом случае он сух и беден в сравнении с моим русским или немецким). Если вы видите в этом документе что-либо, что нуждается в корректировке, может быть описано намного лучше, считается плохим стилем, содержит неправильный политический намек или вредит людям доброй воли в мире — скажите мне.
     

    Наслаждайтесь

    Igor Kogan
    Dave Hayden


    Перевод сделан силами участников конференции ruFlash