É “chamar site” algum código automático gerado pelo compilador – encontro esse termo com frequência e parece que o método de chamada é simplesmente referido como “chamar site” – o que literalmente soa bem, mas acredito que tenha algumas complexidades mais profundas. E se “call site” for um código gerado pelo compilador – em que circunstâncias isso é necessário?
Embora eu esteja falando no contexto do C # .Net, mas parece ser a terminologia padrão. Gostaria de receber qualquer explicação clara / precisa com alguns exemplos.
Comentários
- Há uma noção de site de chamada tanto no código-fonte quanto no auto código-objeto gerado.
- Você pode querer observar como os sites de chamada são gerados para uma operação dinâmica em C # 4 e superior. Eles são bastante interessantes.
- @EricLippert Obrigado. Felizmente, encontrei o link onde você o descreveu muito bem. No entanto, não consegui encontrar nenhum blog com detalhes específicos sobre a geração de sites de chamadas.
Resposta
“Ligue para o site ”Refere-se ao local onde uma função ou método é chamado. É um termo comum na construção do compilador, e não específico para C #.
foo(); // call site void foo() { ... } // function definition
O site de chamada não é nenhum código especial gerado. Mas no site da chamada, o compilador emite instruções para chamar a função. Normalmente, estes:
- Salve o contexto atual em um quadro de pilha.
- Armazene todos os argumentos de acordo com a convenção de chamada da função de destino.
- Chame a função de destino, que pode exigir o envio do método.
- Recupere o valor de retorno de acordo com a convenção de chamada.
- Restaure o contexto do quadro de pilha.
O envio de métodos pode exigir a execução de um código complicado para encontrar a função que realmente deve ser chamada. Isso é necessário ao chamar métodos virtual
, especialmente métodos por meio de uma interface. O procedimento de despacho de método completo pode ser bastante caro (geralmente O (n) no número de interfaces implementadas, ou O (n) no número de métodos disponíveis). Mas na maioria das vezes (normalmente 70–90%), um site de chamada verá objetos do mesmo tipo. Então, faz sentido armazenar em cache o resultado da pesquisa de método. O compilador pode então gerar um código como este no site da chamada:
static lastType = null; static lastMethod = null; if (type(instance) != lastType) { lastType = type(instance); lastMethod = performMethodLookup(instance, methodName); } result = lastMethod(instance, args);
Isso é conhecido como um monomórfico cache embutido . O .NET CLR desenvolveu uma abordagem um pouco mais complicada chamada Virtual Stub Dispatch . Em vez de fazer o despacho no local da chamada, o JIT-runtime compila métodos stub. Existem diferentes stubs para fazer o procedimento de despacho do método completo e para usar um método em cache. No site da chamada, teremos apenas uma chamada para o stub, e o stub encaminhará a chamada para o destino real. Dependendo das estatísticas para este site de chamada, o código de máquina no site de chamada será corrigido para usar um esboço diferente.
Resposta
O uso mais típico de call-site é provavelmente como atualmente declarado na Wikipedia : não é nada mais do que o lugar em algum código onde uma chamada para um função ou sub-rotina é feita. É um termo totalmente geral que pode ser aplicado a todos os tipos de linguagens (e certamente nada a ver com programação dinâmica!).
Costumo ouvir o termo usado (em oposição apenas ao termo “chamada”) para deixar claro que se está discutindo a sintaxe e a semântica do código onde uma função / método / sub-rotina é chamada, em oposição à semântica subjacente da chamada, ou a semântica na definição da função / método: em desta forma, absolutamente não sobre o compilador ou qualquer código gerado (embora o termo ainda se aplique ao código gerado pelo compilador / tempo de execução, como eu acho que amon já discutiu), é sobre trazer o código específico que se traduz em uma chamada no contexto e, às vezes, o código nas imediações.
Este é o tipo de imagem que tenho na minha cabeça:
call-site call call-target/method definition --> --> int.TryParse(str, out i); (runtime) public static bool TryPars(...
O” call-site “é um termo útil para falar sobre a sintaxe de fazer uma chamada e a semântica que afeta o chamador (detalhado por amon). Considere, por exemplo, o novo modificador in
em C #, uma “chamada” que passa um argumento como “em” tem certa semântica associada a ele (é passada por ref, possivelmente depois uma cópia), mas isso não é “necessariamente claro na sintaxe” no local da chamada “, onde in
não é obrigatório. A ideia é que a semântica do site de chamada (ou seja, o comportamento que influencia o chamador / site de chamada) de passar um argumento por referência com in
está próxima o suficiente para aqueles que passam por valor que não precisam ser eliminados pela sintaxe no site de chamada .Eu discordo dessa afirmação, e eu teria dificuldade em discuti-la sem um termo como “call-site”!
Outro exemplo: uma chamada dinâmica (por exemplo, para um método virtual / interface) tem algo comportamentos de tempo de execução complicados (novamente, detalhados por amon) e não se sabe no momento da compilação qual método será chamado, mas tudo o que o “call-site” se preocupa é com a semântica de despachar essa chamada: se você estiver chamando ToString()
em um object
, você não realmente se importa se é realmente um string
no site de chamada; você apenas se preocupa se object
expõe um método ToString()
virtual (depende do tempo de execução para descobrir qual método chamar de fato). Na verdade, no call-site (por exemplo, em C #) não é necessariamente claro a partir da sintaxe se esta é uma chamada virtual: a distinção entre uma chamada virtual e não virtual é frequentemente considerada sem importância o suficiente na chamada site que não precisa ser eliminado para o programador no site de chamada (embora seja de vital importância para o compilador / tempo de execução fazer uma chamada significativa)
Um último exemplo: considere C # “s ref
e C ++” s &
: a semântica no call-site em ambos language são praticamente iguais em ambos os idiomas: uma referência é passada em vez de um valor, mas cada idioma tem uma sintaxe diferente no site de chamada, onde C # requer ref
e C ++ não requer &
(ou algo parecido). Novamente, os designers de linguagem tomaram algumas decisões de sintaxe sobre como as chamadas são representadas no site da chamada, informadas pela semântica no site da chamada. (Eu prefiro a sintaxe do site de chamada C # porque ela expõe a semântica do site de chamada, que acredito que eu, como um programador, deveria reconhecer quando fiz essa chamada). É óbvio que a definição do método precisa saber que está recebendo um parâmetro por referência, porque muda o comportamento de modificá-lo (por exemplo, com =
). Você poderia argumentar, no entanto, que o tempo de execução não se importa (em tais linguagens de tipo estático, onde o runtime-dispatch é nominalmente informado): ele apenas precisa obter esse valor do chamador para o receptor , não importa se é uma referência ou não, isso é uma preocupação para o site da chamada e o alvo da chamada.
Mais vagamente, “site da chamada” pode se referir a o código que indica uma chamada, bem como o código “associado” ao seu redor (em oposição a apenas a “chamada”). Em C #, por exemplo, pode-se referir-se à definição de uma variável fictícia “em” um site de chamada, por exemplo, ao chamar um método TryParse
:
int dummy; bool looksLikeAnInt = int.TryParse(str, out dummy);
Todo esse “bloco” pode ser considerado parte do call-site. Eu consideraria este um significado menos “preciso”, mas isso não me impede de usá-lo.