Javascript.RU

Создание собственных аннотаций

Java-фреймворк, на котором построен Google Closure Compiler, может быть расширен. Например, можно добавить новые аннотации, которые делают что-то с кодом.

К примеру, можно добавить @strip - аннотацию, которая уберет из итогового кода все вызовы объекта.

Работать будет так:

/** @strip */
console = {
  debug: function() { /* ... */ }
}

function doSomething() {
	console.log()
	alert(1);
}

Превратится в:

function doSomething(){alert(1)};

Создавать аннотации можно практически любые. В частности, аннотация может иметь параметры: @param {string}. Действия, которые компилятор будет совершать с аннотированным объектом (объектом, функцией и т.п.) описываются на языке Java и могут быть практически любыми.

Единственно, следует иметь в виду, что аннотация - это все же для компилятора, а не для браузера. Отлаживать удобнее всего несжатый код, а в нем аннотации будут простыми комментариями.
Хотя, даже сжатый код можно поотлаживать, используя Closure Inspector в Firefox.

Таким образом, в общем случае заведомо полезны аннотации, помогающие лучше сжать/обработать код.

Добавление аннотаций состоит из нескольких частей. Нужно указать ее в парсере javascript, добавить в список возможных аннотаций JSDoc, создать функцию для проверки аннотации, описать действия компилятора и т.п. Разберем шаги поподробнее на примере @strip.

За структуру данных JSDoc отвечает класс com.google.javascript.rhino.JSDocInfo,

Для новой аннотации следует создать флаг в общем списке:

...
private static final int MASK_FILEOVERVIEW  = 0x00001000; // @fileoverview
private static final int MASK_IMPLICITCAST  = 0x00002000; // @implicitCast
private static final int MASK_NOSIDEEFFECTS = 0x00004000; // @nosideeffects
// допишем нашу аннотацию в конец, создадим ей новую маску
private static final int MASK_STRIP = 0x00008000; // @strip

И добавим методы установки и проверки, аналогично setExport:

void setStrip(boolean value) {
  setFlag(value, MASK_STRIP);
}
public boolean isStrip() {
  return getFlag(MASK_STRIP);
}

Наша аннотация - бинарная (есть/нет), поэтому последующие действия по сути состоят в создании рядом с export аналогов с именем strip.

Класс com.google.javascript.rhino.JSDocInfoBuilder отвечает за построение объекта JSDoc из аннотаций. Добавим новую аннотацию и туда:

// аналогично recordExport..
public boolean recordStrip() {
    if (!currentInfo.isStrip()) {
      currentInfo.setStrip(true);
      populated = true;
      return true;
    } else {
      return false;
    }
}

Класс com.google.javascript.jscomp.parsing.JsDocInfoParser - последний класс парсера, требующий исправления. Он отвечает за разбор текста скрипта. Допишем новую аннотацию в список enum Annotation:

...
EXTENDS,
EXPORT,
// наша аннотация
STRIP,

.. И добавим ее в общий список recognizedAnnotations:

...
put("enum", Annotation.ENUM).
put("export", Annotation.EXPORT).
// наша новая строка
put("strip", Annotation.STRIP).

Кроме того, добавим фрагмент в метод parse, который будет записывать найденную аннотацию в JSDoc.

Можно это сделать аналогично case EXPORT:

case EXPORT:
    ...
case STRIP:
  if (!jsdocBuilder.recordStrip()) {
    parser.addWarning("msg.jsdoc.strip",
    stream.getLineno(), stream.getCharno());
  }
  token = eatTokensUntilEOL();
  continue retry;

Итак, парсер готов. Аннотация будет распознана и превращена в JSDoc. При обходе синтаксического дерева javascript, компилятор сможет вызовом node.getJSDocInfo() получить JSDoc, и затем проверить isStrip() - нет ли у данного объекта аннотации @strip.

Для того, чтобы аннотация заработала, необходимо действие. Почти все действия google closure compiler реализуются путем прохода компилятора по синтаксическому дереву.

Проход может осуществлять любой класс с интерфейсом CompilerPass. Он осуществляется в обратном порядке (Post-Order).

Мы не будем патчить существующие проходы, а создадим свой. Он будет проходить по дереву, и при нахождении узла с isStrip() == true - добавлять имя объекта в список stripTypes.

В последующих проходах этот список будет обработан компилятором, и все обращения к указанному объекту будут удалены из кода.

Вот код для такого прохода.

package com.google.javascript.jscomp;

import com.google.common.collect.Sets;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.FunctionNode;

class ApplyAnnotationStrip extends NodeTraversal.AbstractPostOrderCallback implements CompilerPass {

    private final Compiler compiler;

    ApplyAnnotationStrip(Compiler compiler) {
        this.compiler = compiler;
    }

    public void visit(NodeTraversal t, Node n, Node parent) {
        JSDocInfo jsDocInfo = n.getJSDocInfo();

        if (jsDocInfo == null || !jsDocInfo.isStrip()) return;

        /* получить имя объекта/функции */ 
        String name = null;
        if (n.getType() == Token.FUNCTION) {
            name = ((FunctionNode)n).getFunctionName();
        } else if (n.getType() == Token.ASSIGN) {
            name = n.getFirstChild().getQualifiedName();
        }

        /* добавить в список */
        CompilerOptions options = compiler.getOptions();
        if (options.stripTypes.isEmpty()) {
            options.stripTypes = Sets.newHashSet();
        }
        options.stripTypes.add(name);
    }

    public void process(Node externs, Node root) {
        NodeTraversal.traverse(compiler, root, this);
    }

}

Метод process требуется для интерфейса CompilerPass и осуществляет, собственно, проход компилятора - путем обхода дерева скрипта.

Третий аргумент NodeTraversal.traverse - объект, производящий обработку узлов во время обхода. Для этого у него должен быть метод visit. Мы реализуем его здесь же.

Метод visit, как видно из исходного кода, отрабатывает только для узлов с JSDoc и isStrip == true.

Он получает имя объекта и добавляет соответствующую строку в опцию компилятора stripTypes, которая более подробно описана в статье Автоудаление отладочных свойств и объектов.

Если коротко - последующие проходы компилятора используют эту опцию для удаления всех обращений к указанным в ней символам.

Для того, чтобы получать имя объекта, и вообще делать какие-то операции, компилятор использует Синтаксическое дерево.

Для того, чтобы в общих чертах представлять, как устроено дерево разбора javascript, можно почитать стандарт языка, документацию к Rhino, а впрочем - вполне можно что-то понять, запустив компилятор с опциями --use_only_custom_externs --print_tree --js ваш_скрипт. Такой вызов распечатает синтаксическое дерево вашего скрипта.

Итак, структура JSDoc готова, проход компилятора тоже описан, остается добавить этот проход к исполнению компилятором.

За это отвечает класс com.google.javascript.jscomp.DefaultPassConfig. Добавление прохода осуществляется посредством фабричного метода PassFactory.

Добавим новый метод аналогично уже существующим:

private final PassFactory applyAnnotationStrip =
  new PassFactory("applyAnnotationStrip", true) {
    @Override
    protected CompilerPass createInternal(AbstractCompiler compiler) {
        return new ApplyAnnotationStrip((Compiler)compiler);
    }
};

Наш проход должен осуществится до оптимизационных проходов компилятора (В частности, до прохода, удаляющего strip'нутые символы), чтобы список stripTypes был использован.
Поэтому имеет смысл добавить его в конец метода getChecks, перед assertAllOneTimePasses(checks);

protected List<PassFactory> getChecks() {
    // ...
    checks.add(processDefines);

    checks.add(applyAnnotationStrip);

    assertAllOneTimePasses(checks);
    return checks;
  }

Google Closure Compiler с нашими дополнениями должен корректно обрабатывать исходный пример.

В частности:

/** @strip */
console = {
  debug: function() { /* ... */ }
}

function doSomething() {
	console.log()
	alert(1);
}

Должно становиться (обычная оптимизация, не продвинутая):

function doSomething(){alert(1)};

У меня не составило особого труда пропатчить исходники по этому документу с нуля, но на всякий случай - вот патч к ревизии 9: stripann.patch.txt.

У кода в этой статье два основных недостатка.

Во-первых, я не использовал его в production достаточно долго. Конечно, допилить и оттестировать - вопрос (не очень большого) времени, но честно говоря, я предпочитаю опцию stripTypes командной строки компилятора.

Во-вторых, добавление аннотации требует патча нескольких файлов компилятора.

Впрочем, это не должно быть проблемой, т.к. патчи не нарушают структуру кода и будут успешно мержится с SVN.

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

В зависимости от проекта, они могут оказаться не менее полезными, чем стандартные аннотации компилятора.


Автор: LioLick (не зарегистрирован), дата: 19 октября, 2011 - 11:44
#permalink

if (n.getType() == Token.FUNCTION) {
name = ((FunctionNode)n).getFunctionName();
}

Не работает приведение:
"com.google.javascript.rhino.Node cannot be cast to com.google.javascript.rhino.FunctionNode".

Удалось ли кому-то заставить работать?


Автор: Гость (не зарегистрирован), дата: 27 июня, 2021 - 14:43
#permalink

Да


Автор: Гость (не зарегистрирован), дата: 27 июня, 2021 - 14:41
#permalink

член


Автор: Гость2 (не зарегистрирован), дата: 27 июня, 2021 - 14:42
#permalink

нехорошо так делать


Автор: Гость (не зарегистрирован), дата: 27 июня, 2021 - 14:42
#permalink

член


Автор: Гость (не зарегистрирован), дата: 27 июня, 2021 - 14:42
#permalink

член


Автор: 2048 cupcakes (не зарегистрирован), дата: 9 июля, 2021 - 06:34
#permalink

Этот метод процесса требуется для интерфейса CompilerPass и фактически выполняет переключение компилятора - путем обхода дерева сценария. Третий аргумент NodeTraversal.traverse - это объект, который обрабатывает узлы во время обхода. Для этого его необходимо посетить. Этот процесс обычно показывает код ошибки #


Автор: 2048 cupcakes (не зарегистрирован), дата: 9 июля, 2021 - 06:36
#permalink

Этот метод процесса требуется для интерфейса CompilerPass и фактически выполняет переключение компилятора - путем обхода дерева сценария. Третий аргумент NodeTraversal.traverse - это объект, который обрабатывает узлы во время обхода. Для этого его необходимо посетить. Этот процесс обычно показывает код ошибки #2048 cupcakes


Автор: Гость (не зарегистрирован), дата: 6 января, 2022 - 15:00
#permalink

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


Автор: Гость (не зарегистрирован), дата: 6 января, 2022 - 15:00
#permalink

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


Автор: Артем Платонов (не зарегистрирован), дата: 6 января, 2022 - 15:11
#permalink

Спасибо, полезные советы и рекомендации всегда нужны и лишними не будут. Мы в нашей сео компании be1 используем библиотеку скриптов для разных целей. Также редактор html кода который применяется в нашей работе над анализом сайтов построен с применением уникальных токенов на базе джава-скрипт. Ну и проверка позиции сайта в поисковиках также основана на подобных алгоритмах и позволяет найти поддомены сайта даже в сложных случаях. Джава скрипты не самое нужное в сео продвижении но тем не менее использование этой технологии помогает в нужных случаях


Автор: justicehwrknz807, дата: 10 января, 2022 - 10:20
#permalink

Good post. This sounds like a very good podcast. We are looking forward to listening to the actual podcast. Click Here


Автор: sara167 (не зарегистрирован), дата: 16 марта, 2022 - 06:26
#permalink

Thank you for sharing this data. I really enjoy what you've written on your blog. You've shared a very useful and entertaining blog post with the public. getting over it


Автор: David Klayer (не зарегистрирован), дата: 14 апреля, 2022 - 10:06
#permalink

Pixelart123 — это сайт для всех, кто хочет заниматься пиксель-артом самостоятельно! Все наши шаблоны просты в создании. Откройте для себя сотни пиксельных шаблонов, которые легко создавать, копировать или создавать! Хотите покататься на своем радужном единороге? Путешествовать по планетам Солнечной системы на борту космического корабля? Прогулка в компании жирафов, слонов и попугаев? Или помочь Марио найти свою принцессу в замке Боузера? Pixelart123 предлагает вам множество различных стилей и идей для вашего собственного маленького пиксельного мира.


Автор: Гость (не зарегистрирован), дата: 16 апреля, 2022 - 01:21
#permalink

Автор: Elzbieta Manko (не зарегистрирован), дата: 19 апреля, 2022 - 20:40
#permalink

Roksa ¿Funcionan realmente los sitios de citas sexuales? ¡Claro que sí y yo soy un testigo viviente! De verdad, puede ser difícil aceptar que hay chicas por ahí que están dispuestas a dejarte esa galleta gratis, pero seguro que hay muchas de estas chicas que no quieren nada de amor. Como que no están de humor para poemas deslizantes, o para que les cojan la mano. Todo lo que quieren es echar un polvo de la forma más orgásmica posible con un tipo o tipos que necesiten un coño en el que puedan disparar un cañón de semen. Así que, en lugar de que estas putas vayan de puerta en puerta buscando penes desocupados que puedan empalarlas desde el coño hasta el intestino delgado, acuden a sitios de citas sexuales gratuitas y se registran. De ti depende encontrar a estas chicas y darles lo que quieren. Y me refiero a cada maldito centímetro de tu polla.


Автор: Stephanie Klemt (не зарегистрирован), дата: 17 июня, 2022 - 20:38
#permalink

Erfahrene Nutten hat einen netten Chat und Unterhaltungsinhalte für Erwachsene, was die Seite zu einer aufregenden und amüsanten Plattform für wilden Spaß im Bett macht.


Автор: wendy678 (не зарегистрирован), дата: 29 июля, 2022 - 10:36
#permalink

Thanks for sharing this information. I really like your blog post very much. You have really shared a informative and interesting blog post with people. play dordle game


Автор: Гость (не зарегистрирован), дата: 12 августа, 2022 - 21:48
#permalink

Interesting thank you, this is valuable information and I find it very helpful. mesothelioma attorney in California


Автор: Гость (не зарегистрирован), дата: 13 августа, 2022 - 17:15
#permalink

Хороший сайт. Откуда вы взяли материал для этой статьи? Я прочитал несколько ваших статей, и мне очень нравится ваш стиль письма. Я также столкнулся с этим на странице Paper Minecraft и люблю такой позитив. Большое спасибо, и продолжайте в том же духе.


Автор: eugengerd (не зарегистрирован), дата: 17 сентября, 2022 - 06:16
#permalink

If you're looking for a great way to online radio luisteren Nederland, then Radiofmluisteren is the perfect website for you. With a wide selection of radio stations to choose from, you're sure to find something that you'll love listening to. Whether you're looking for music, news, or talk radio, Radiofmluisteren has it all. And best of all, it's absolutely free to use. So why not give it a try today?


Автор: Гость (не зарегистрирован), дата: 23 сентября, 2022 - 05:00
#permalink

Anywhere, at any time, free solitaire is a fun way to spend the time. Play a game of solitaire to liven up your day, whether you're at home on a leisurely day, taking a break at work, or sitting outdoors with your laptop in the sunshine. Get out of a boring chore with a swift victory that will lift your spirits!


Автор: BaileyDaugherty, дата: 18 ноября, 2022 - 06:25
#permalink

Interesting, and thank you; I appreciate the insight. geometry dash


Автор: BaileyDaugherty, дата: 18 ноября, 2022 - 06:25
#permalink

Interesting, and thank you; I appreciate the insight.


Отправить комментарий

Приветствуются комментарии:
  • Полезные.
  • Дополняющие прочитанное.
  • Вопросы по прочитанному. Именно по прочитанному, чтобы ответ на него помог другим разобраться в предмете статьи. Другие вопросы могут быть удалены.
    Для остальных вопросов и обсуждений есть форум.
P.S. Лучшее "спасибо" - не комментарий, как все здорово, а рекомендация или ссылка на статью.
Содержание этого поля является приватным и не предназначено к показу.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Разрешены HTML-таги: <strike> <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <u> <i> <b> <pre> <img> <abbr> <blockquote> <h1> <h2> <h3> <h4> <h5> <p> <div> <span> <sub> <sup>
  • Строки и параграфы переносятся автоматически.
  • Текстовые смайлы будут заменены на графические.

Подробнее о форматировании

CAPTCHA
Антиспам
1 + 4 =
Введите результат. Например, для 1+3, введите 4.
 
Текущий раздел
Поиск по сайту
Содержание

Учебник javascript

Основные элементы языка

Сундучок с инструментами

Интерфейсы

Все об AJAX

Оптимизация

Разное

Дерево всех статей

Последние комментарии
Последние темы на форуме
Forum