2011-09-29 15 views
15

Quali sono gli errori più comuni associati con il Perl di eval, che potrebbero rendere si sceglie di utilizzare un modulo come il Try::Tiny?Quali sono le più comuni insidie ​​quando si utilizza la valutazione di Perl?

+0

possibile duplicato del [Perché '$ @ 'inaffidabile?] (http://stackoverflow.com/questions/3686857/why-is-untrustworthy), [cosa è rotto sulle eccezioni in Perl?] (http://stackoverflow.com/questions/2165161/) – mob

+2

L'unica ri a meno che tu non usassi il builtin, non stai facendo girare una versione corrente di Perl. – tchrist

+0

@mob - sì, sembra essere la stessa domanda. – Hugh

risposta

25

di Perl disponibile in due versioni, stringa eval e il blocco eval. String eval richiama il compilatore per eseguire il codice sorgente. Il blocco eval circonda il codice già compilato in un wrapper che catturerà l'eccezione die. (stringa eval cattura anche l'eccezione die, così come eventuali errori di compilazione).

Prova :: Piccolo vale solo per la forma di blocco eval, ma si applica quanto segue per entrambe le forme.

Ogni volta che si chiama eval, verrà modificato il valore di [email protected]. Sarà '' se la valutazione è riuscita o l'errore è stato rilevato dalla valutazione.

Questo significa che ogni volta che si chiama un eval, si cancellerà eventuali messaggi di errore precedenti. Try::Tiny localizza automaticamente la variabile [email protected], in modo che una valutazione di successo non cancellerà il messaggio di una valutazione precedente non riuscita.

L'altra insidia viene dal usando [email protected] come il controllo per determinare se l'eval riuscito. Un modello comune è:

eval {...}; 
if ([email protected]) { 
    # deal with error here 
} 

Questo si basa su due presupposti, prima che qualsiasi messaggio di errore [email protected] potrebbe contenere un valore vero (generalmente vero), e che non esiste un codice tra il blocco eval e l'istruzione if.

Visivamente, naturalmente, la seconda è vero, ma se il blocco eval ha creato un oggetto e l'oggetto è andato fuori del campo di applicazione dopo l'eval fallito, allora il metodo dell'oggetto DESTROY sarà chiamato prima dell'istruzione if. Se DESTROY accade di chiamare eval senza localizzazione [email protected] e riesce, quindi per il momento la sua dichiarazione if viene eseguito, la variabile [email protected] verrà cancellata.

La soluzione a questi problemi è:

my $return = do { 
    local [email protected]; 
    my $ret; 
    eval {$ret = this_could_fail(); 1} or die "eval failed: [email protected]"; 
    $ret 
}; 

rompendo quella riga per riga a parte, il local [email protected] crea un nuovo [email protected] per il blocco che impedisce do clobbering valori precedenti. my $ret sarà il valore restituito del codice valutato. Nel blocco eval, viene assegnato $ret e il blocco restituisce 1. In questo modo, indipendentemente da cosa, se la valutazione ha esito positivo, verrà restituito true e, in caso di esito negativo, restituirà false. Spetta a voi cosa fare in caso di fallimento. Il codice sopra appena muore, ma potresti facilmente utilizzare il valore di ritorno del blocco eval per decidere di eseguire altro codice.

Poiché l'incantesimo di cui sopra è un po 'noioso, diventa soggetto a errori.L'utilizzo di un modulo come Try::Tiny ti isola da quei potenziali errori, al costo di alcune chiamate di funzione in più per valutazione. È importante sapere come utilizzare eval correttamente, perché Try::Tiny non ti aiuterà se devi usare una valutazione di stringa.

+4

Risolto nella versione corrente. – tchrist

+0

Piccola correzione - '$ @' sarà in realtà una stringa vuota anziché undef se non viene lanciata alcuna eccezione. –

+1

@Grant McLean => grazie, ho dovuto ricordare, perché è così che le mie solite offerte Repl con errori: 'perl -Abbiamo 'dire eval, $ @ mentre <>'' –

6

Oltre alle risposte di cui sopra, vorrei aggiungere ...

  • eval è influenzato dal $SIG{__DIE__} gestore globale di azione causando a distanza.
  • è facile per un novizio di confondere eval BLOCK e eval STRING, dal momento che sembrano fare la stessa cosa, ma uno è un buco di sicurezza.

Prova :: Tiny ha le sue insidie, il più grande è che mentre sembra un blocco in realtà è una chiamata di subroutine. Ciò significa che questo:

eval { 
    ...blah blah... 
    return $foo; 
}; 

e questo:

try { 
    ...blah blah... 
    return $foo; 
}; 

non fare la stessa cosa. Questi sono disposti nel CAVEATS section of the Try::Tiny docs. Detto questo, lo raccomanderei su eval.

+2

Sta dicendo che 'eval' è * ancora * insolitamente rotto in 5.14? ** Davvero? ** Sarebbe * estremamente * deludente, perché so che ci è voluto molto lavoro per cercare di rendere 'Try :: Tiny' obsoleto risolvendo qualunque bug sottostante afflitto' eval'.Se questo tentativo fallisce, allora c'è un problema orribile perché 'Try :: Tiny' è ancora semplicemente un modulo CPAN, non core. Se non puoi fare un lavoro vero e affidabile, con il nucleo, allora questa è una situazione inaccettabile. – tchrist

+1

@tchrist Hai dimenticato. A quanto ho capito, 5.14.0 ha risolto una classe di bug che aveva a che fare con le interazioni tra $ @ e la distruzione degli oggetti e in genere reso 'eval {...}; se ($ @) {...} 'più affidabile. Credo che risolva 2 delle 3 cose Try :: Tiny fixes ... e il terzo (un falso $ @) sia piuttosto improbabile. Lascia ancora i punti che ho menzionato. Sarebbe una bella funzionalità 5.16 per fermare '$ SIG {__ DIE __}' da sparare all'interno di un'eval. E chiama il dramma, amico. – Schwern

+0

Chiamare "eval STRING" un "problema di sicurezza" non è solo eccessivamente drammatico; non è nemmeno vero Ho usato 'eval STRING' da quando è apparso perl2 per la prima volta circa ventitré anni fa, posso assicurarti che non ho mai sperimentato alcun cosiddetto" problema di sicurezza "con esso. Certo, i programmatori stupidi possono fare cose stupide con esso, ma questo è vero per quasi tutto. Se esisti in un mondo patologicamente paranoico, dovresti usare la modalità contaminazione e/o compartimenti sicuri. In un mondo normale, 'eval STRING' ottiene molto lavoro utile; guarda il classico programma * rename *. – tchrist

0

Utilizzare eval sulla funzione X11 potrebbe ancora non è riuscito a mantenere in vita.

Il codice è come

eval {  
    @win_arrays = GetWindowsFromPid($pid); 
}; 

Lo script viene abbandonato da

X errore di richiesta non riuscita: ...

Problemi correlati