1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/openharmony-third_party_jerryscript

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
04.INTERNALS.md 35 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 22.04.2025 23:18 3cdd483

Высокий Уровень Проектирования

Высокий Уровень Проектирования

Схема выше показывает взаимодействие между основными компонентами JerryScript: Парсер и Виртуальная Машина (VM). Парсер выполняет перевод входного приложения ECMAScript в байт-код с указанным форматом (см. Байт-код и Парсер для подробностей). Подготовленный байт-код выполняется Виртуальной Машиной, которая выполняет интерпретацию (см. Виртуальная Машина и ECMA для подробностей).

Парсер

Парсер реализован как рекурсивный спуск парсер. Парсер прямым образом переводит исходный код JavaScript в байт-код без построения абстрактного синтаксического дерева. Парсер зависит от следующих подкомпонентов.

Лексер

Лексер разбивает входную строку (программу ECMAScript) на последовательность токенов. Он способен сканировать входную строку не только вперед, но и перемещаться в произвольную позицию. Структура токена описана структурой lexer_token_t в ./jerry-core/parser/js/js-lexer.h.

Сканер

Сканер (./jerry-core/parser/js/js-parser-scanner.c) предварительно сканирует входную строку для поиска определенных токенов. Например, сканер определяет, является ли ключевое слово for общим циклом или циклом for-in. Чтение токенов в цикле while недостаточно, так как слеш (/) может указывать на начало регулярного выражения или быть оператором деления.## Парсер Выражений

Парсер выражений отвечает за разбор выражений JavaScript. Он реализован в ./jerry-core/parser/js/js-parser-expr.c.

Парсер Инструкций

Инструкции JavaScript разбираются этим компонентом. Он использует Парсер выражений для разбора составных выражений. Реализация Парсера инструкций находится в ./jerry-core/parser/js/js-parser-statm.c.

Функция parser_parse_source выполняет разбор и компиляцию входного исходного кода ECMAScript. Когда появляется функция в исходном коде, parser_parse_source вызывает parser_parse_function, который отвечает за обработку исходного кода функций рекурсивно, включая разбор аргументов и обработку контекста. После разбора функция parser_post_processing записывает созданные опкоды и возвращает указатель ecma_compiled_code_t* на скомпилированный байт-код. Взаимодействия между основными компонентами, показанные на следующем рисунке.

Parser dependency

Байт-код

В этом разделе описывается компактное представление байт-кода (CBC). Основное внимание уделяется уменьшению потребления памяти байт-кодом без значительного ущерба для производительности. Другие представления байт-кода часто фокусируются только на производительности, поэтому создание этого представления является оригинальным исследованием.CBC — это набор инструкций в стиле CISC, который присваивает более короткие инструкции для часто используемых операций. Многие инструкции представляют собой несколько атомарных задач, что уменьшает размер байт-кода. Эта техника в основном является методом сжатия данных.## Формат скомпилированного кода

Планирование памяти скомпилированного байт-кода следующее.

CBC layout

Заголовок — это структура cbc_compiled_code с несколькими полями. Эти поля содержат ключевые свойства скомпилированного кода.

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

Список инструкций CBC — это последовательность инструкций байт-кода, которая представляет собой скомпилированный код.

Формат байт-кода

Планирование памяти байт-кода следующее:

byte-code layout

Каждый байт-код начинается с опкода. Опкод имеет длину одного байта для часто используемых инструкций и двух байтов для редко используемых. Первый байт редко используемых инструкций всегда равен нулю (CBC_EXT_OPCODE), а второй байт представляет собой расширенный опкод. Имена часто используемых и редко используемых инструкций начинаются с префиксов CBC_ и CBC_EXT_ соответственно.

Максимальное количество опкодов — 511, так как можно определить 255 часто используемых (исключая нулевое значение) и 256 редко используемых инструкций. В настоящее время доступно около 215 часто используемых и 70 редко используемых инструкций.В CBC есть три типа аргументов байт-кода:

  • Байтовый аргумент: Значение от 0 до 255, которое часто представляет количество аргументов для инструкций типа вызова (вызов функции, new, eval и т.д.).

  • Литеральный аргумент: Целочисленный индекс, который больше или равен нулю и меньше значения поля literal_end заголовка. Для дополнительной информации см. следующий раздел Литералы (следующий).

  • Относительная ветвь: Смещение длиной от 1 до 3 байт. Аргумент ветви также может представлять конец диапазона инструкций. Например, аргумент ветви CBC_EXT_WITH_CREATE_CONTEXT показывает конец инструкции with. Более точно, это позиция после последней инструкции в блоке with.

Комбинации аргументов ограничены следующими семью формами:

  • без аргументов
  • литеральный аргумент
  • байтовый аргумент
  • аргумент ветви
  • байтовый и литеральный аргументы
  • два литеральных аргумента
  • три литеральных аргумента

Литералы

Литералы организованы в группы, которые представляют различные типы литералов. Имея эти группы, можно сэкономить место, чем если бы присваивать флаги для каждого литерала.(В следующих описаниях упомянутые диапазоны представляют индексы, которые больше или равны левой стороне диапазона и меньше правой стороны диапазона. Например, диапазон между полями ident_end и literal_end заголовка байт-кода содержит те индексы, которые больше или равны ident_end и меньше literal_end. Если ident_end равен literal_end, диапазон пуст.)Две основные группы литералов — это идентификаторы и значения.

  • идентификатор: Названный ссылочный объект для переменной. Литералы между нулем и ident_end заголовка относятся к этой группе. Все эти литералы должны быть строками или неопределенными. Неопределенное значение может использоваться только для тех литералов, которые не могут быть доступны по имени. Например, в функции function (arg, arg) есть два аргумента, но идентификатор arg ссылается только на второй аргумент. В таких случаях имя первого аргумента неопределено. Кроме того, оптимизации, такие как CSE, могут также вводить литералы без имени.

  • значение: Ссылка на немедленное значение. Литералы между ident_end и const_literal_end являются постоянными значениями, такими как числа или строки. Эти литералы могут быть использованы напрямую виртуальной машиной. Литералы между const_literal_end и literal_end являются шаблонными литералами. Новый объект должен создаваться каждый раз, когда их значение используется. Эти литералы являются функциями и регулярными выражениями.

Ещё есть две подгруппы идентификаторов. Регистры — это идентификаторы, которые хранятся в стеке вызова функции. Аргументы — это регистры, которые передаются вызывающей функцией. В CBC используется два типа кодирования литералов. Оба они переменной длины, где длина может быть от одного до двух байт. * малый: максимальное количество кодируемых литералов — 511.

Одно байтовое кодирование для литералов 0 - 254.

byte[0] = literal_index

Два байтовое кодирование для литералов 255 - 510.

byte[0] = 0xff
byte[1] = literal_index - 0xff
  • полный: максимальное количество кодируемых литералов — 32767.

Одно байтовое кодирование для литералов 0 - 127.

byte[0] = literal_index

Два байтовое кодирование для литералов 128 - 32767.

byte[0] = (literal_index >> 8) | 0x80
byte[1] = (literal_index & 0xff)

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

Хранилище литералов

JerryScript не имеет глобальной таблицы строк для литералов, но хранит их в хранилище литералов. В процессе парсинга, когда новый литерал появляется с тем же идентификатором, который уже появлялся ранее, строка не будет заново сохранена, а будет использован идентификатор из хранилища литералов. Если новый литерал ещё не находится в хранилище литералов, он будет вставлен.

Категории байт-кодов

Байт-коды могут быть разделены на четыре основные категории.

Байт-коды для помещения на стекБайт-коды этой категории используются для помещения объектов на стек. Поскольку в CBC существует множество инструкций, представляющих множественные атомарные задачи, существует также множество инструкций для помещения объектов на стек в зависимости от количества и типа аргументов. Следующая таблица содержит несколько из этих опкодов с кратким описанием.

байт-код описание
CBC_PUSH_LITERAL Помещает значение данного литерального аргумента.
CBC_PUSH_TWO_LITERALS Помещает значения двух данного литеральных аргументов.
CBC_PUSH_UNDEFINED Помещает неопределенное значение.
CBC_PUSH_TRUE Помещает логическое значение true.
CBC_PUSH_PROP_LITERAL Помещает свойство, базовый объект которого снят с стека, а имя свойства передано как литеральный аргумент.

Байт-коды вызова

Байт-коды этой категории выполняют вызовы различными способами.

| байт-код | описание | | --------------------- | --------------------------------------------------------------------------------- | | CBC_CALL0 | Вызывает функцию без аргументов. Значение возврата не будет помещено на стек. | | CBC_CALL1 | Вызывает функцию с одним аргументом. Значение возврата не будет помещено на стек. | | CBC_CALL | Вызывает функцию с n аргументами. n передается как байт-аргумент. Значение возврата не будет помещено на стек. | | CBC_CALL0_PUSH_RESULT | Вызывает функцию без аргументов. Значение возврата будет помещено на стек. | | CBC_CALL1_PUSH_RESULT | Вызывает функцию с одним аргументом. Значение возврата будет помещено на стек. | | CBC_CALL2_PROP | Вызывает функцию свойства с двумя аргументами. Основной объект, имя свойства и два аргумента находятся на стеке. |

Арифметические, логические, побитовые и операции присваивания

Опкоды этой категории выполняют арифметические, логические, побитовые и операции присваивания.

| байт-код | описание | | ----------------------- | --------------------------------------------------------------------------------------------------- | | CBC_LOGICAL_NOT | Негативирует логическое значение, которое было выведено с вершины стека. Результат помещается обратно на вершину стека. | | CBC_LOGICAL_NOT_LITERAL | Негативирует логическое значение, указанное в литеральном аргументе. Результат помещается обратно на вершину стека. | | CBC_ADD | Складывает два значения, которые были выведены с вершины стека. Результат помещается обратно на вершину стека. | | CBC_ADD_RIGHT_LITERAL | Складывает два значения. Левое значение выведено с вершины стека, правое значение указано в литеральном аргументе. | | CBC_ADD_TWO_LITERALS | Складывает два значения. Оба значения указаны в литеральных аргументах. | | CBC_ASSIGN | Присваивает значение свойству. Имеет три аргумента: базовый объект, имя свойства, значение для присваивания. | | CBC_ASSIGN_PUSH_RESULT | Присваивает значение свойству. Имеет три аргумента: базовый объект, имя свойства, значение для присваивания. Результат помещается обратно на вершину стека. |### Ветвные байт-коды

Ветвные байт-коды используются для выполнения условных и безусловных прыжков в байт-коде. Аргументы этих инструкций имеют длину от 1 до 3 байт относительных смещений. Количество байт входит в состав опкода, поэтому каждая инструкция с аргументом ветвления имеет три формы. Направление (вперед, назад) также определяется опкодом, так как смещение является беззнаковым значением. Таким образом, некоторые инструкции ветвления имеют шесть форм. Некоторые примеры можно найти в следующей таблице.

байт-код описание
CBC_JUMP_FORWARD Прыгает вперед на относительное смещение длиной 1 байт.
CBC_JUMP_FORWARD_2 Прыгает вперед на относительное смещение длиной 2 байта.
CBC_JUMP_FORWARD_3 Прыгает вперед на относительное смещение длиной 3 байта.
CBC_JUMP_BACKWARD Прыгает назад на относительное смещение длиной 1 байт.
CBC_JUMP_BACKWARD_2 Прыгает назад на относительное смещение длиной 2 байта.
CBC_JUMP_BACKWARD_3 Прыгает назад на относительное смещение длиной 3 байта.
CBC_BRANCH_IF_TRUE_FORWARD Если значение на вершине стека истинно, прыгает вперед на относительное смещение длиной 1 байт.

СнимокСкомпилированный байт-код может быть сохранён в снимке, который также может быть загружен обратно для выполнения. Прямое выполнение снимка позволяет сэкономить затраты на парсинг исходного кода в терминах потребления памяти и производительности. Снимок также может быть выполнен из ROM, в этом случае можно также сэкономить перенос его в память.# Виртуальная машина

Виртуальная машина — это интерпретатор, который выполняет инструкции байт-кода по одному. Функция, которая запускает интерпретацию, это vm_run в ./jerry-core/vm/vm.c. vm_loop — это основной цикл виртуальной машины, который имеет особенность быть нерекурсивным. Это означает, что в случае вызова функций она не вызывает себя рекурсивно, а возвращается, что имеет преимущество в том, что она не нагружает стек, как рекурсивная реализация.

ЭКМА

ЭКМА-компонента движка отвечает за следующие понятия:

  • Представление данных
  • Временное представление
  • Сборка мусора (GC)

Представление данных

Основная структура для представления данных — это ECMA_value. Нижние три бита этой структуры кодируют тег значения, который определяет тип значения:

  • простое
  • число
  • строка
  • объект
  • символ
  • ошибка

Представление значения ЭКМА

В случае чисел, строк и объектов значение содержит закодированный указатель, а простое значение — это заранее определенная константа, которая может быть:

  • неопределенное
  • null
  • true
  • false
  • пустое (неразмещённое значение)

Сжатые указатели

Сжатые указатели были введены для экономии пространства кучи.

Сжатый указательЭти указатели — это 16-битные указатели, выравненные по 8 байтам, которые могут адресовать 512 Кб памяти, что также является максимальным размером кучи JerryScript. Для поддержки ещё большего объёма памяти размер сжатых указателей может быть расширен до 32 бит, чтобы охватить весь адресный пространство 32-битной системы, передавая "--cpointer_method 32_bit_on" в систему сборки. Эти "нераспакованные указатели" увеличивают потребление памяти примерно на 20%.### Числа

Согласно стандарту IEEE 754 существует два возможных представления чисел: по умолчанию это 8-байтовое (double), но движок поддерживает представление 4-байтовое (одинарной точности) путём установки JERRY_NUMBER_TYPE_FLOAT64 в 0.

Число

Несколько ссылок на одно выделенное число не поддерживаются. Каждая ссылка хранит собственную копию числа.

Строки

Строки в JerryScript не являются просто последовательностями символов, но могут содержать числа и так называемые магические идентификаторы. Для обычных последовательностей символов (определённых в ./jerry-core/lit/lit-magic-strings.ini) существует таблица в только для чтения памяти, содержащая пары магических идентификаторов и последовательностей символов. Если строка уже находится в этой таблице, то хранится магический идентификатор строки, а не сама последовательность символов. Использование чисел ускоряет доступ к свойствам. Эти техники экономят память.### Объект / Лексическое окружение

Объект может быть обычным объектом данных или объектом лексической среды. В отличие от других типов данных, объект может иметь ссылки (называемые свойствами) на другие типы данных. Из-за циклических ссылок подсчет ссылок не всегда достаточно для определения мертвых объектов. Поэтому формируется цепочка объектов из всех существующих объектов, которая может использоваться для поиска нессылочных объектов во время сборки мусора. Указатель gc-next каждого объекта указывает на следующий выделенный объект в цепочке.

Лексические окружения реализуются в виде объектов в JerryScript, так как лексические окружения содержат пары ключ-значение (называемые связями) подобно объектам. Это упрощает реализацию и уменьшает размер кода.Структуры объекта/лексического окружения

Объекты представляются следующей структурой:

  • Счетчик ссылок — количество жестких (не-свойственных) ссылок
  • Указатель на следующий объект для сборщика мусора
  • Тип (объект-функция, лексическое окружение и т. д.)

Свойства объектов

Свойства объекта

Объекты имеют связанный список, содержащий их свойства. Этот список фактически содержит пары свойств, чтобы сэкономить память, как описано ниже: Свойство имеет длину 7 бит, а его поле типа — 2 бита, что потребляет 9 бит, которые не помещаются в 1 байт, но потребляют 2 байта. Поэтому, объединение двух свойств (14 бит) с 2-битным полем типа помещается в 2 байта.

Хэш-карта свойств

Если количество пар свойств достигает определенного предела (в настоящее время этот предел определен как 16), вставляется хэш-карта (называемая Хэш-картой свойств), которая вставляется на первое место в списке пар свойств, чтобы найти свойство с ее помощью, а не итерируясь линейно по парам свойств.

Хэш-карта свойств содержит 2n элементов, где 2n больше, чем количество свойств объекта. Каждый элемент может иметь три типа значений:

  • null, указывающее на пустой элемент
  • удалено, указывающее на удаленный свойство, или
  • ссылка на существующее свойствоЭта хэш-карта является кэшем типа must-return, что означает, что каждый свойство, которое имеет объект, может быть найдено с её помощью.

Внутренние свойства

Внутренние свойства — это специальные свойства, которые содержат метаданные, которые не могут быть доступны JavaScript-кодом, но важны для самого движка. Некоторые примеры внутренних свойств приведены ниже:

  • [[Class]] — класс (тип) объекта (определено ECMA)
  • [[Code]] — указывает, где найти байт-код функции
  • native code — указывает, где найти код встроенной функции
  • [[PrimitiveValue]] для Boolean — хранит булево значение объекта Boolean
  • [[PrimitiveValue]] для Number — хранит числовое значение объекта Number

LCache

LCache — это хеш-карта для поиска свойства, указанного объектом и именем свойства. Структура LCache с объектами и именами свойств повторяется несколько раз, как показано на рисунке ниже.

LCache

При обращении к свойству извлекается хеш-значение из требуемого имени свойства, а затем этот хеш используется для индексации LCache. После этого в индексированной строке ищется указанный объект и имя свойства.

Важно отметить, что если указанное свойство не найдено в LCache, это не означает, что оно не существует (т.е. LCache — это кеш, который может вернуть значение). Если свойство не найдено, оно будет искаться в списке свойств объекта, и если оно найдено там, свойство будет помещено в LCache.### Коллекции

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

Обработка исключений

Чтобы реализовать обработку исключений, возвращаемые значения функций JerryScript могут указывать на их сбой или "исключительное" поведение. Возвращаемые значения — это ECMA-значения (см. раздел Представление данных), и если произошла ошибка, возвращается простое значение ECMA_VALUE_ERROR.

Управление значениями и владением

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

Изначально значение выделяется его владельцем (т.е. с владением). Владелец несет ответственность за освобождение выделенного значения. Когда значение передается функции в качестве аргумента, владение им не передается, и вызываемая функция должна сделать свой собственный копию значения. Однако, пока функция возвращает значение, владение передается, и вызывающая функция будет нести ответственность за его освобождение.

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/openharmony-third_party_jerryscript.git
git@api.gitlife.ru:oschina-mirror/openharmony-third_party_jerryscript.git
oschina-mirror
openharmony-third_party_jerryscript
openharmony-third_party_jerryscript
master