Vai al contenuto principale

Contribuire a Babel: Tre Lezioni da Ricordare

· Lettura di 8 min
Traduzione Beta Non Ufficiale

Questa pagina è stata tradotta da PageTurner AI (beta). Non ufficialmente approvata dal progetto. Hai trovato un errore? Segnala problema →

Prender confidenza con una nuova codebase presenta sempre le sue sfide, e Babel non ha fatto eccezione.

Ho lavorato con Babel nell'ambito del programma Google Summer of Code 2017, aggiornando le trasformazioni di Babel e il parser Babylon per adeguarli ai cambiamenti delle specifiche e implementando nuove funzionalità.

Ecco alcune cose che ho imparato durante questa avventura.

1. Sì, la comunicazione è importante

Per iniziare a familiarizzare meglio con la codebase, ho esaminato la lista delle issue aperte su Babel e ne ho trovata una relativamente semplice da affrontare (issue #5728).

Per assicurarmi di capire cosa stavo facendo, ho posto una domanda rapida nella discussione:

My question asking for clarification

Dopo aver ricevuto chiarimenti, ho iniziato a modificare il plugin per non generare errori "runtime" durante la transpilazione, ma solo quando il codice viene effettivamente eseguito. Un frammento di codice sospetto ha attirato la mia attenzione:

JavaScript
for (const violation of (binding.constantViolations: Array)) {
throw violation.buildCodeFrameError(messages.get("readOnly", name));
}

Qui bisognava inserire un'istruzione throw nel codice generato, cosa che non si è rivelata troppo difficile. Tuttavia, rimanevano alcuni casi in cui errori runtime venivano generati altrove da codice non direttamente correlato a questo file.

Desideroso di esplorare altre parti della codebase di Babel, ho rimandato quel lavoro a più tardi.

Poco dopo, ho ricevuto un aggiornamento piuttosto... interessante sull'issue. Aspetta, cosa?

Someone else had claimed the issue.

Non avevo mai dichiarato esplicitamente che stavo lavorando alla risoluzione del problema, ma avevo dato per scontato che il mio intervento implicasse che ci avrei lavorato.

Ops.

2. Quando i test snapshot non bastano

Riprendendo la ricerca, mi sono imbattuto nella issue #5656:

Arguments deoptimized when shadowed in nested function

This is a feature request (I think). Arguments are not optimized if an inner function shadows the name with a parameter (or rest parameters in my case).

Input code

JavaScript
const log = (...args) => console.log(...args);

function test_opt(...args) {
log(...args);
}

function test_deopt(...args) {
const fn = (...args) => log(...args);
fn(...args);
}

...

Expected vs. Current Behavior

I’d expect the code to be optimizable to use .apply( thisArg, arguments ) throughout. However, in test_deopt the outer ...args gets copied just to be passed into the inner fn. I can verify that the problem disappears if I rename either the ...args of test_deopt or of the fn arrow function.

Cosa succede qui?

Il problema era che questo codice generava il seguente output:

JavaScript
var log = function log() {
var _console;

return (_console = console).log.apply(_console, arguments);
};

function test_opt() {
log.apply(undefined, arguments);
}

function test_deopt() {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { // unnecessary loop
args[_key] = arguments[_key];
}

var fn = function fn() {
return log.apply(undefined, arguments);
};
fn.apply(undefined, args);
}

Vedete quella sezione for? Normalmente è necessaria perché l'oggetto arguments non è un array vero e proprio. Ad esempio, se provaste a eseguire arguments.slice(), fallirebbe miseramente. Tuttavia, in questo caso viene passato solo a Function.prototype.apply. Sorprendentemente, Babel già si preoccupa di ottimizzare questo caso specifico, come nell'esempio test_opt sopra.

Tentativo di correzione

Cosa ho fatto? Ho aggiunto il file problematico come nuovo test case, cercando di ottenere un output che rispecchiasse il comportamento desiderato.

Test failure of modified code

"Perché il test fallisce? Sicuramente se modifico qualcosa si risolverà da solo."

Nonostante abbia ripetutamente eseguito make test-only e modificato le trasformazioni degli identificatori referenziati nel codice, ogni modifica si traduceva semplicemente in un diverso insieme di test falliti.

Il debugger di Chromium è "divertente"

Sconsolato, irritato e confuso, mi sono deciso ad avviare l'inspector di Node.js per analizzare passo passo cosa stesse succedendo.

Using the Chromium debugger

Dopo essere tornato al computer da una pausa per bere, sono stato accolto dalla luce del disco rigido che lampeggiava freneticamente e un computer praticamente bloccato.

Chromium process using more than 3GB of memory

Holding my computer together with judicious applications of Alt + SysRq + F, I managed to work through the flow of things¹ and figure out how exactly the code worked.

Nonostante tutto, non riuscivo ancora a capire perché stesse decidendo di rimuovere quel codice "necessario" (così pensavo) che veniva eliminato con la mia correzione iniziale.

Il problema effettivo?

Vedi l'errore mostrato sopra? Tutto quel codice in verde non doveva essere lì, anche se era "previsto".

In sostanza: il test era rotto. Ottimo. :/

La correzione effettiva ha comportato la creazione di una funzione referencesRest per assicurarsi che l'operatore spread fosse applicato al parametro originale, anziché a una variabile in un altro scope che lo mascherava.

¹: Si è scoperto che aggiungere una cartella di grandi dimensioni all'area di lavoro di DevTools causava perdite di memoria fino a provocare un OOM (bug che ho segnalato).

Allora perché usiamo lo snapshot testing?!

Innanzitutto, è molto più semplice creare test quando tutto ciò che devi fare è chiedere a Babel di eseguire il tuo caso di test per generare il file atteso. Questo ci offre un'opzione a basso costo temporale proteggendo da una porzione significativa di potenziali errori.

Inoltre, specialmente per il tipo di programma che è Babel, sarebbe molto più difficile testare in altri modi. Ad esempio, potremmo verificare nodi specifici dell'AST, ma questo richiede molto più tempo per essere scritto ed è anche soggetto a rotture non ovvie quando il codice tenta di modificare il modo in cui viene eseguita la trasformazione.

Quindi, in definitiva, ecco alcune lezioni:

  1. Assicurati che i tuoi test siano corretti fin dall'inizio—non essere compiacente!

  2. Sì, il debugger è effettivamente utile per capire cosa succede.

  3. A volte le cose richiedono tempo per essere risolte—se non stai ottenendo risultati, fai una pausa o lavora a qualcos'altro.

3. Riunioni di squadra!

So che questo allarga un po' il concetto di "problema", ma comunque :)

Quando lavori a un progetto con altre persone, è sempre utile confrontarsi e discutere le aree su cui dobbiamo lavorare.

Quindi come possiamo organizzare tutto ciò?!

Ugh, riunioni.

Quando hai persone sparse in tutto il mondo, trovare modi per comunicare non è mai semplice, ma comunque dobbiamo arrangiarci con i nostri tentativi in questa impresa.

Fusi orari

Quando hai a che fare con un progetto open source che abbraccia tutto il globo, scegliere un orario appropriato si trasforma rapidamente in un complesso esercizio di bikeshedding.

World map of people who’ve attended our meetings

Nonostante l'ampia distribuzione geografica tra di noi, sembrava che riuscissimo a organizzare qualcosa.

Time zones discussed in 31 May 2017 meeting

Ahimè, non è durato. Alla fine, abbiamo dovuto alternare due orari ogni settimana per accogliere altri utenti (13:00 e 16:00 UTC), il che significava che potevo partecipare solo una volta ogni due settimane.

Nonostante ciò, siamo riusciti a fare progressi significativi nel coordinare correzioni a varie parti che costituiscono cambiamenti chiave per Babel, inclusi il supporto per TypeScript, modifiche all'ordine di esecuzione dei plugin di trasformazione e l'aggiornamento continuo alle specifiche TC39.

Quali sono i prossimi passi?

Stiamo continuando a perfezionare Babel 7 per il rilascio pubblico, con numerose nuove funzionalità in arrivo.

Sto collaborando con altri per integrare in Babel il supporto all'aggiornata proposta di specifica per i Class Fields, permettendo agli sviluppatori di testarla e fornire feedback.

Approfitto inoltre per ringraziare tutti i mentor e i contributor di Babel che mi hanno aiutato con revisioni tra pari e indicazioni sulle proposte, fin dal primo contatto fino ad oggi.


Vuoi saperne di più su Babel? Visita la nostra pagina per contribuire e unisciti alla community su Slack!

Maggiori informazioni su Karl

Karl Cheng è uno studente GSoC 2017 proveniente da Sydney, Australia. Scopri di più su di lui su GitHub (Qantas94Heavy) e Twitter (@Qantas94Heavy)!

Consulta il nostro primo post sul Summer of Code per maggiori informazioni!