В большинстве статей о внутреннем устройстве Nodejs всегда говорится о границах C ++ / JS и их пересечении. Но большинство из них обычно не углубляется в объяснение того, что на самом деле означало пересечение границы C ++ / JS и что такое пересечение.

В этой статье мы подробно рассмотрим границы C ++ / JS, чтобы понять, что crossing это влечет за собой.

Что мы извлечем из этой статьи: Node.js поддерживает более миллиона стартапов и компаний. Это наиболее часто используемый бэкэнд-фреймворк. Мы пользуемся им каждый день. Разработчики, обладающие навыками Node.js, пользуются большим спросом, поэтому глубокое изучение того, как работает Node.js, значительно расширит наш кругозор по Nodejs и будет уверенно создавать приложения Nodejs.

Совет. При создании приложений на Node.js вы можете совместно использовать общий код и управлять им в любом масштабе с помощью Bit. Вместо того, чтобы дублировать код, просто поделитесь им и синхронизируйте его. Попробуйте сами.



C ++ / JS: граница между двумя мирами

Все начинается с компилятора.

Во-первых, что такое компилятор?

Компилятор - это программа, которая переводит фрагмент кода в машинный код.

Большой!! into machine code. Помни это.

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

source code
    |
    v
lexical analyzer
    |
    v
  parser
    |
    v
code generation

лексический анализатор: генерирует токены из исходного кода.

синтаксический анализатор: генерирует дерево AST (абстрактное синтаксическое дерево) из токенов.

генерация кода: генерирует машинный / ассемблерный код из AST.

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

Nodejs использует движок v8 JS для компиляции и выполнения кода JS. Надеюсь, мы знаем, что такое v8.

в противном случае v8 - это высокопроизводительный компилятор JavaScript с открытым исходным кодом от Google.

Каждый раз, когда мы запускаем js-файл в Node.js следующим образом:

node script.js

Nodejs передает скрипт в v8, используя свои API. v8 компилирует код JS в предоставленном сценарии (script.js) и возвращает эквивалент сборки. Затем Node.js использует другой API v8 для запуска сгенерированного кода сборки.

Компилятор компилирует код в сборку и копирует его в память.

Глядя на изображение выше, код JS компилируется в код сборки.

Чтобы запустить скомпилированный код, он выделяет место в памяти, перемещает код в выделенное пространство и переходит в пространство памяти.

Теперь при переходе выполнение начинается с скомпилированного кода. Следовательно, он перешел границу.

Выполняемый сейчас код - это не код C ++, а код JS. Все сейчас в сборке.

Если выполнение скомпилированного JS-кода завершается, он возвращается к коду C ++.

Код C ++ и код JS здесь не означают исходный код C ++ или исходный код JS. Нет. Это ассемблерный код, сгенерированный компилятором из их исходных кодов, и это то, что выполняется. Вы можете произнести код C ++ и код JS, чтобы различать, какой ассемблерный код запускается.

Вызов из JS в C ++

Функции в исходном коде C ++ можно вызывать из JS-кода.

Пример:

// script.js
let f = 90
function send() {
    var f= 100
}
send()
sendMe()

В этом коде мы определили функцию send, но функции sendMe нет.

Функция sendMe будет определена в нашем приложении на C ++:

// v8_demo.cpp
include "v8.h";
void sendMe() {
    cout << "Greetings from C++ land";
}
int main() {
    Maybe<Local> result = v8::Script::Compile('script.js');
    result->Run();
}

Примечание: приведенные выше фрагменты не запускаются. Это просто для демонстрационных целей.

Здесь у нас есть функция sendMe.

Посмотри, что получится. При исполнении наша v8_demo.cpp работает так:

MEMORY
    ; v8_demo.cpp
x00    sendMe:
x01        push ""Greetings from C++ land""
x02        call cout
x03        ret
x04
x05    main:
x06        push "script.js"
x07        call Compile
x08        push eax
x09        call Run
x10
x11
x12
x13
x14
x15
x16
x17
x18
x19
x20

увидеть, что функция sendMe присутствует. Исполнение начинается с основного и продолжается вниз. На x07 script.js компилируется и помещается в память.

MEMORY
    ; v8_demo.cpp
x00    sendMe:
x01        push ""Greetings from C++ land""
x02        call cout
x03        ret
x04
x05    main:
x06        push "script.js"
x07 ➥     call Compile
x08        push eax
x09        call Run
x10
x11     ;script.js
x12     mov 90,f
x13     send:
x14         push 100
x15     call send
x16     call sendMe
x17
x18
x19
x20

См. Наш script.js ассемблерный код в памяти. :) Здесь нет кода C ++ или JS. Все в сборке. Как я сказал ранее, мы можем просто разграничить, например, вот код сборки из сценария C ++, а вот код сборки из сценария JS.

Итак, когда выполняется Run x09,

MEMORY
    ; v8_demo.cpp
x00    sendMe:
x01        push ""Greetings from C++ land""
x02        call cout
x03        ret
x04
x05    main:
x06        push "script.js"
x07        call Compile
x08        push eax
x09 ➥     call Run
x10
x11     ;script.js
x12     mov 90,f
x13     send:
x14         push 100
x15     call send
x16     call sendMe
x17
x18
x19
x20

он переходит в область памяти кода сборки JS x12.

Примечание. Run - это API v8 для запуска скомпилированного кода JS после компиляции.

MEMORY
    ; v8_demo.cpp
x00    sendMe:
x01        push ""Greetings from C++ land""
x02        call cout
x03        ret
x04
x05    main:
x06        push "script.js"
x07        call Compile
x08        push eax
x09        call Run
x10
x11     ;script.js
x12 ➥  mov 90,f
x13     send:
x14         push 100
x15     call send
x16     call sendMe
x17
x18
x19
x20

Здесь код, который мы написали в script.js, выполняется в виде сборки.

Когда выполнение достигает x15 call send, он переходит к x13 send: и выполняет свой блок, после чего возвращается и вызывает sendMe.

MEMORY
    ; v8_demo.cpp
x00    sendMe:
x01        push ""Greetings from C++ land""
x02        call cout
x03        ret
x04
x05    main:
x06        push "script.js"
x07        call Compile
x08        push eax
x09        call Run
x10
x11     ;script.js
x12     mov 90,f
x13     send:
x14         push 100
x15     call send
x16 ➥  call sendMe
x17
x18
x19
x20

Эта функция не была определена в нашем script.js, но, поскольку она была определена в v8_demo.cpp, теперь она доступна в памяти. Его можно вызвать, потому что ассемблерная форма script.js и v8_demo.cpp теперь выполняется на одном языке и в одном пространстве памяти.

Итак, выполнение переходит к x00 sendMe:, началу функции sendMe.

MEMORY
    ; v8_demo.cpp
x00 ➥ sendMe:
x01        push ""Greetings from C++ land""
x02        call cout
x03        ret
x04
x05    main:
x06        push "script.js"
x07        call Compile
x08        push eax
x09        call Run
x10
x11     ;script.js
x12     mov 90,f
x13     send:
x14         push 100
x15     call send
x16     call sendMe
x17
x18
x19
x20

:) Отображается Greetings from C++ land !!

Итак, этот вид прыжка называется Crossing the C++/JS boundary.

Аналогичным образом возможен вызов из C ++ в JS при условии, что функция определена в области JS.

Заключение

Понимаете, это довольно просто, никакой магии, ничего страшного. Всякий раз, когда вы слышите "Crossing C++/JS", всегда представляйте, как все работает в сборке и в одном и том же пространстве памяти.

В наших следующих статьях мы подробно рассмотрим, как работают некоторые основные API-интерфейсы Node.js:

  • setTimeout
  • process.nextTick
  • setImmediate
  • Обещать
  • setInterval

Если у вас есть какие-либо вопросы относительно этого или чего-либо, что я должен добавить, исправить или удалить, не стесняйтесь комментировать, писать мне по электронной почте или в прямом сообщении. Спасибо !!! 👍

Учить больше