Operações de Tabela na VM do Lua: Criação, Acesso e Metatabelas

Criação de Tabelas e a Instrução NEWTABLE

Uma tabela vazia em Lua é implementada internamente usando a instrução OP_NEWTABLE. O seguinte código Lua demonstra esta operação:

local tabela = {}

Ao analisar o bytecode gerado com ferramentas como o ChunkSpy, observa-se a emissão da instrução newtable. Esta instrução utiliza três operandos: A (o registro de destino para a tabela), B (tamanho do array) e C (tamanho da hash). A aálise sintática segue uma cadeia de chamadas partindo de chunk até constructor, que é a função responsável por processar literais de tabela ({...}).

Durante a construção de uma tabela, o parser mantém o estado em uma estrutura ConsControl, que rastreia o número de elementos (na para array, nh para hash) e um buffer (tostore) para otimização.

struct ControleConstrutor {
  expdesc v;
  expdesc *t;
  int num_hash;
  int num_array;
  int pendentes;
};

A função principal constructor emite a instrução OP_NEWTABLE inicial e, em seguida, itera sobre os campos definidos na tabela.

Populando a Parte Array

Para uma tabela com elementos de array, como local t = {10, 20}, além de newtable, são emitidas instruções para carregar os valores constantes (loadk) e finalmente uma instrução OP_SETLIST. Esta última transfere um bloco de valores do topo da pilha de reigstros para dentro da tabela, dentro do array.

A instrução OP_SETLIST possui o formato A B C. Onde A é o registro da tabela, B é o número de itens a serem transferidos e C é um fator de bloco calculado a partir da constante LFIELDS_PER_FLUSH (tipicamente 50). Isso limita o número de registros utilizados durante a inicialização do array.

A função auxiliar listfield analisa cada elemento do array, enquanto closelistfield verifica se o buffer de itens pendentes atingiu o limite (LFIELDS_PER_FLUSH) para emitir uma instrução setlist.

Populando a Parte Hash

Campos chave-valor, como em local t = {["chave"] = 42}, são tratados pela instrução OP_SETTABLE. Seu formato é A B C, onde A é o registro da tabela, B é a posição da chave (RK) e C é a posição do valor (RK). RK (Register or Constant) é uma representação compacta que pode referenciar um registro na pilha ou uma constante no array de constantes da função.

Se a chave for uma variável local (carregada em um registro) em vez de uma constante literal, a instrução loadk correspondente é emitida para carregá-la antes do newtable ou settable, garantindo que o registro correto seja referenciado.

Acessando Elementos de uma Tabela

A instrução OP_GETTABLE é usada para recuperar valores de uma tabela. Seu formato é A B C, onde A é o registro de destino, B é o registro que contém a tabela e C é a posição da chave (RK).

-- Exemplo de acesso
local valor = tabela["minhaChave"]

A compilação desse código resulta em uma instrução gettable após a tabela e a chave estarem disponíveis.

Metatabelas e o Protocolo __index

O mecanismo de metatabelas é implementado durante a inicialização da VM na função luaT_init, onde os nomes dos métodos mágicos (como "__index", "__newindex") são criados como strings fixas na memória.

Quando a instrução OP_GETTABLE (ou similar) encontra uma chave não existente na tabela, a VM segue um protocolo. A função luaV_gettable verifica se a tabela possui uma metatabela com o método TM_INDEX. Se o método for uma função, ela é chamada. Se for outra tabela, a busca é repetida nessa nova tabela, até atingir um limite de iteração (MAXTAGLOOP) para prevenir loops infinitos.

-- Pseudocódigo do mecanismo de busca com metatabela
function buscarNaTabela(t, chave)
    local valor = t[chave]
    if valor ~= nil then
        return valor
    end
    local mt = obterMetatabela(t)
    if mt and mt.__index then
        if tipo(mt.__index) == "funcao" then
            return mt.__index(t, chave)
        elseif tipo(mt.__index) == "tabela" then
            return buscarNaTabela(mt.__index, chave) -- recursão
        end
    end
    return nil
end

Este processo, embora apresentado de forma simplificada, é fundamental para a flexibilidade do sistema de objetos em Lua.

Tags: Lua VM opcodes metatables table Bytecode

Publicado em 6-6 20:43 por Thomas