Дополнения V8
V8 реализует большую часть встроенных объектов и функций языка JavaScript непосредственно в самом JavaScript. Например, наша реализация промисов написана на JavaScript. Такие встроенные функции называются автономными. Эти реализации включены в наш снимок при загрузке, чтобы новые контексты можно было быстро создавать без необходимости настройки и инициализации автономных встроенных функций во время выполнения.
Встраиваемые системы V8, такие как Chromium, иногда хотят писать API на JavaScript тоже. Это особенно хорошо подходит для функций платформы, которые являются самодостаточными, например, потоки, или для функций, которые являются частью «слоистой платформы» высокоуровневых возможностей, построенных поверх уже существующих низкоуровневых. Хотя всегда можно выполнить дополнительный код при запуске для инициализации API встроенных систем (как это делается, например, в Node.js), желательно, чтобы встроенные системы могли получать такие же преимущества скорости для своих автономных API, как и V8.
Дополнения V8 — это новая функция V8, начиная с нашего выпуска v4.8, разработанная с целью позволить встроенным системам писать высокопроизводительные автономные API через простой интерфейс. Дополнения — это предоставляемые встроенной системой JavaScript-файлы, которые компилируются непосредственно в снимок V8. Они также имеют доступ к нескольким вспомогательным утилитам, которые упрощают написание безопасных API на JavaScript.
Пример
Файл дополнения V8 — это просто JavaScript-файл с определенной структурой:
(function(global, binding, v8) {
'use strict';
const Object = global.Object;
const x = v8.createPrivateSymbol('x');
const y = v8.createPrivateSymbol('y');
class Vec2 {
constructor(theX, theY) {
this[x] = theX;
this[y] = theY;
}
norm() {
return binding.computeNorm(this[x], this[y]);
}
}
Object.defineProperty(global, 'Vec2', {
value: Vec2,
enumerable: false,
configurable: true,
writable: true
});
binding.Vec2 = Vec2;
});
Несколько вещей, которые стоит отметить здесь:
- Объект
global
отсутствует в цепочке областей видимости, поэтому доступ к нему (например, кObject
) необходимо выполнять явно через предоставленный аргументglobal
. - Объект
binding
— место для хранения или получения значений встроенной системы. C++ APIv8::Context::GetExtrasBindingObject()
предоставляет доступ к объектуbinding
со стороны встроенной системы. В нашем игрушечном примере мы позволяем встроенной системе выполнять вычисление нормы; в реальном примере вы могли бы делегировать встроенной системе что-то более сложное, например, разрешение URL. Мы также добавляем конструкторVec2
в объектbinding
, чтобы код встроенной системы мог создавать экземплярыVec2
, не обращаясь к потенциально изменяемому объектуglobal
. - Объект
v8
предоставляет небольшое количество API для написания безопасного кода. Здесь мы создаем частные символы для хранения нашего внутреннего состояния таким образом, чтобы оно не могло быть изменено извне. (Частные символы — это внутренняя концепция V8 и не имеют смысла в стандартном JavaScript-коде.) Встроенные функции V8 часто используют «%-вызовы функций» для таких вещей, но дополнения V8 не могут использовать %-функции, так как они являются внутренней деталью реализации V8 и не подходят для зависимостей встроенных систем.
Вам может быть интересно, откуда берутся эти объекты. Все три из них инициализируются в загрузчике V8, который устанавливает некоторые основные свойства, но преимущественно оставляет инициализацию автономному JavaScript V8. Например, почти каждый .js файл в V8 что-то устанавливает на global
; см. например promise.js или uri.js. И мы устанавливаем API на объект v8
во многих местах. (Объект binding
пуст до тех пор, пока дополнение или встроенная система не изменят его, поэтому единственный относящийся к V8 код — это когда загрузчик создает его.)
Наконец, чтобы сообщить V8, что мы будем компилировать дополнение, мы добавляем строку в файл gyp нашего проекта:
'v8_extra_library_files': ['./Vec2.js']
(Вы можете увидеть реальный пример этого в gyp-файле V8.)
Практическое применение V8 extras
V8 extras предоставляют новый и легковесный способ для встраиваемых приложений реализовывать функции. Код на JavaScript может легче работать с такими встроенными объектами JavaScript, как массивы, карты или промисы; он может вызывать другие функции JavaScript без дополнительных усилий; и он может обрабатывать исключения в идеоматическом стиле. В отличие от реализаций на C++, функции, реализованные на JavaScript через V8 extras, могут воспользоваться встроением, и их вызов не несет затрат на пересечение границ. Эти преимущества особенно заметны, если сравнивать с традиционной системой связывания, такой как Web IDL bindings в Chromium.
V8 extras были введены и доработаны за последний год, и в настоящее время Chromium использует их для реализации потоков. Chromium также рассматривает V8 extras для реализации кастомизации прокрутки и эффективных API геометрии.
V8 extras всё ещё находятся в стадии разработки, и интерфейс имеет некоторые шероховатости и недостатки, которые мы надеемся устранить со временем. Основная область, требующая улучшений, это отладка: ошибки сложно отследить, а отладка на этапе выполнения чаще всего проводится с помощью выводов print. В будущем мы надеемся интегрировать V8 extras в инструменты разработчика и систему трассировки Chromium, как для самого Chromium, так и для любых встраиваемых решений, поддерживающих тот же протокол.
Еще одна причина быть осторожным при использовании V8 extras — это дополнительное усилие разработчика, необходимое для написания безопасного и надежного кода. Код V8 extras работает напрямую с снапшотом, как и код для встроенных функций V8. Он обращается к тем же объектам, что и пользовательский JavaScript, без какого-либо слоя связывания или отдельного контекста, предотвращающего доступ. Например, нечто такое простое, как global.Object.prototype.hasOwnProperty.call(obj, 5)
, имеет шесть потенциальных способов, при которых это может не сработать из-за изменения встроенных объектов пользовательским кодом (посчитайте их!). Встраиваемые решения, такие как Chromium, должны быть устойчивы к любому пользовательскому коду, независимо от его поведения, и поэтому в таких средах требуется больше осторожности при написании extras, чем при написании функций, реализованных традиционно на C++.
Если вы хотите узнать больше о V8 extras, ознакомьтесь с нашим дизайн-документом, в котором приводится гораздо больше деталей. Мы с нетерпением ждем улучшений V8 extras и добавления новых функций, которые позволят разработчикам и встраиваемым приложениям писать выразительные и высокопроизводительные дополнения к среде выполнения V8.