Pergunta

Eu tenho usado o recurso eval de rubi vezes muitos um. pessoas, mas eu ouvi dizendo evals são desagradáveis. Quando perguntado, por que e como, eu nunca poderia ter uma razão convincente para não usá-lo. eles são realmente desagradável? Se sim, de que maneira? Quais são as possíveis opções "seguras" para eval?

Foi útil?

Solução

Se você está evaling uma string apresentado por, ou modificável pelo usuário, isso equivale a permitir a execução de código arbitrário. Imagine-se a cadeia continha uma chamada de sistema operacional para rm -rf / ou similar. Dito isto, em situações onde você sabe que as cordas são apropriadamente restrita, ou o seu interpretador Ruby está no modo seguro de forma adequada, ou, idealmente ambos, eval pode ser extremamente poderosa.

O problema é análogo ao SQL injeção , se você está familiarizado. A solução aqui é semelhante à solução para o problema de injecção (consultas parametrizadas). Ou seja, se as declarações que você gostaria de eval são conhecidos por serem de uma forma muito específica, e não todas da declaração precisa ser enviadas pelo usuário, apenas algumas variáveis, uma expressão matemática, ou similar, você pode tomar nestes pequenos pedaços do usuário, higienizar-los se necessário, em seguida, avaliar a declaração modelo de seguro com a entrada do usuário conectado nos lugares apropriados.

Outras dicas

Em Ruby existem vários truques que podem ser mais apropriado do que eval():

  1. #send que lhe permite chamar um método cujo nome você tem como cordas e passar parâmetros para ele.
  2. yield permite que você passe um bloco de código a um método que será executado no contexto do método de recepção.
  3. Muitas vezes, o Kernel.const_get("String") simples é suficiente para obter a classe, cujo nome você tem como string.

Eu acho que eu não sou capaz de explicá-los adequadamente em detalhes, então eu apenas dei-lhe as dicas, se você estiver interessado você google.

eval não só é inseguro (como tem sido apontado em outro lugar), também é lento. Cada vez que é executado, o AST das necessidades de código evaled a ser analisado (e para, por exemplo JRuby, virou-se para bytecode) de novo, que é uma operação corda-pesado e também é provavelmente ruim para localidade cache (sob a suposição de que uma corrida programa não eval muito, e as partes correspondentes do intérprete são, portanto, cache-frio, além de ser grande).

Por que há eval em tudo em Ruby, você pergunta? "Porque nós podemos" principalmente - Na verdade, quando eval foi inventado (para a linguagem de programação LISP), foi principalmente para a mostra ! Mais ao ponto, usando eval é a coisa certa quando você quiser "adicionar um intérprete em seu intérprete", para metaprogramação tarefas como escrever um pré-processador, um depurador ou um motor de templates. A idéia comum para tais aplicações é a massagem algum código e chamada eval Ruby on-la, e com certeza é melhor reinventar e implementar uma linguagem de brinquedo de domínio específico, uma armadilha também conhecido como regra décimo de Greenspun . As advertências são: cuidado com os custos, por exemplo, para um motor de templates, fazer todas as suas evaling no momento da inicialização não executar tempo; e fazer código não eval não confiável se você não sabe como "domar"-lo, ou seja, selecionar e aplicar um subconjunto segura do idioma de acordo com a teoria de capacidade de disciplina . O último é uma muito de trabalho realmente difícil (ver, por exemplo como isso foi feito para Java , eu não estou ciente de qualquer esforço para ruby ??infelizmente)

.

Ele torna a depuração difícil. Faz otimização difícil. Mas acima de tudo, é geralmente um sinal de que há uma maneira melhor de fazer o que você está tentando fazer.

Se você nos diga o que você está tentando realizar com eval, você pode obter algumas respostas mais relevantes relacionados com a sua situação específica.

Eval é um recurso incrivelmente poderoso que deve ser usado com cuidado. Além das questões de segurança apontado por Matt J, você também vai descobrir que a depuração do código de tempo de execução avaliado é extremamente difícil. Um problema em um bloco de código de tempo de execução avaliada será difícil para o intérprete de expressar -. Modo procurando será difícil

Dito isto, se você está confortável com essa questão, e não estão preocupados com a questão da segurança, então você não deve evitar o uso de uma das características que torna ruby ??tão atraente como ela é.

Em certas situações, uma eval bem colocado é inteligente e reduz a quantidade de código necessário. Além das preocupações de segurança que foram mencionados por Matt J, você também precisa perguntar a si mesmo uma pergunta muito simples:

Quando está tudo dito e feito, lata qualquer outra pessoa ler o seu código e entender o que você fez?

Se a resposta é não, então o que você ganhou com um eval é abandonado para manutenção. Esta questão não só é aplicável se você trabalhar em uma equipe, mas também é aplicável a você -. Você quer ser capaz de olhar para trás em seus meses de código, se não anos a partir de agora, e sabe o que fez

Se você estiver passando qualquer coisa que você começa a partir do "fora" para eval, você está fazendo algo errado, e é muito desagradável. É muito difícil escapar o suficiente código para que ele seja seguro, então eu considero que é bastante inseguro. No entanto, se você estiver usando eval para evitar duplicação ou outras coisas semelhantes, como exemplo de código a seguir, que é ok para usá-lo.

class Foo
  def self.define_getters(*symbols)
    symbols.each do |symbol|
      eval "def #{symbol}; @#{symbol}; end"
    end
  end

  define_getters :foo, :bar, :baz
end

No entanto, pelo menos em Ruby 1.9.1, Ruby tem realmente poderosos métodos de meta-programação, e você pode fazer o seguinte em vez disso:

class Foo
  def self.define_getters(*symbols)
    symbols.each do |symbol|
      define_method(symbol) { instance_variable_get(symbol) }
    end
  end

  define_getters :foo, :bar, :baz
end

Na maioria dos casos, você quer usar esses métodos, e não há como escapar é necessário.

A outra coisa ruim sobre eval é o fato de que (pelo menos em Ruby), é bastante lento, como o intérprete precisa analisar a cadeia, e em seguida, executar o código dentro a ligação atual. Os outros métodos chama a função C diretamente, e, portanto, você deve obter um bom aumento de velocidade.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top