Node.js è sviluppato in JavaScript e C++. In questo articolo vedremo come nel codice sorgente avviene il collegamento tra questi due componenti dell'applicativo.

Il codice sorgente di Node è ospitato su GitHub. Collegandoci all'indirizzo della repository vedremo il listato completo dei contenuti del codice sorgente.

All'interno, le directory srce lib contengono rispettivamente i file C++ e JavaScript che implementano i vari moduli e funzionalità di cui è composto Node.

Se apriamo il file lib/fs.js, che corrisponde al modulo fs, all'inizio del file troveremo questa dichiarazione:

const binding = internalBinding('fs');

Tale dichiarazione effettua il collegamento (binding) interno tra il modulo corrente ed il codice C++ che implementa le funzionalità per la gestione del file system.

Più avanti nel codice troveremo infatti l'implementazione del metodo access().

function access(path, mode, callback) {
  if (typeof mode === 'function') {
    callback = mode;
    mode = F_OK;
  }

  path = getValidatedPath(path);

  mode = mode | 0;
  const req = new FSReqCallback();
  req.oncomplete = makeCallback(callback);
  binding.access(pathModule.toNamespacedPath(path), mode, req);
}

Come si può notare, l'ultima istruzione richiama l'oggetto collegato al codice C++ che effettua l'accesso al file specificato nel percorso passato come primo argomento al metodo.

All'interno quindi del file C++ src/node_file.cc troveremo l'implementazione del metodo access().

void Access(const FunctionCallbackInfo<Value>& args) {
  Environment* env = Environment::GetCurrent(args);
  Isolate* isolate = env->isolate();
  HandleScope scope(isolate);

  const int argc = args.Length();
  CHECK_GE(argc, 2);

  CHECK(args[1]->IsInt32());
  int mode = args[1].As<Int32>()->Value();

  BufferValue path(isolate, args[0]);
  CHECK_NOT_NULL(*path);

  FSReqBase* req_wrap_async = GetReqWrap(env, args[2]);
  if (req_wrap_async != nullptr) {  // access(path, mode, req)
    AsyncCall(env, req_wrap_async, args, "access", UTF8, AfterNoArgs,
              uv_fs_access, *path, mode);
  } else {  // access(path, mode, undefined, ctx)
    CHECK_EQ(argc, 4);
    FSReqWrapSync req_wrap_sync;
    FS_SYNC_TRACE_BEGIN(access);
    SyncCall(env, args[3], &req_wrap_sync, "access", uv_fs_access, *path, mode);
    FS_SYNC_TRACE_END(access);
  }
}

Alla fine del modulo C++ avverrà l'esportazione del metodo sopra definito in modo che possa essere richiamato dall'esterno come abbiamo visto in precedenza nel codice JavaScript.

env->SetMethod(target, "access", Access);

Nel codice C++ le funzioni con prefisso uv_ fanno riferimento alla libreria libuv che permette a Node di avere accesso al file system e a tutte le altre operazioni legate all'interazione col sistema operativo. La differenza in questo caso è che le operazioni sul file system avvengono all'interno del thread pool di libuv, mentre le altre operazioni legate al sistema operativo (ad esempio le connessioni di rete) vengono svolte al di fuori del thread pool.