API DEX 035
Esta API es experimental (0.2.0-dev) y puede cambiar antes de v1. Los errores se
devuelven explícitamente; una referencia ausente nunca se resuelve de forma
implícita.
import "github.com/wux4an/koru/pkg/dex"
code := dex.NewCode(1).
Const(dex.V(0), 42).
Emit(dex.Instruction{Op: "return", Regs: []dex.Register{dex.V(0)}}).
Build()
program := dex.Program{Classes: []dex.Class{{
Descriptor: "Lexample/Answer;",
Super: "Ljava/lang/Object;",
Access: dex.Public,
Methods: []dex.Method{{
Name: "answer", Descriptor: "()I",
Access: dex.Public | dex.Static, Code: code,
}},
}}}
bytes, err := dex.Build(program)
// Handle err before using bytes.
dex.Build valida las definiciones, genera las tablas y secciones, y valida el
archivo resultante con el parser independiente. dex.Inspect devuelve la
representación decodificada (dex.Inspection) solo si la estructura y el bytecode
pertenecen al subconjunto admitido. Estas comprobaciones no garantizan que ART
resuelva o verifique las clases: para eso hay que ejecutar el archivo en Android.
El runtime incluido se ha ejecutado en ART/API 30; cualquier IR distinto debe
validarse por separado.
En esta página
Registros y parámetros
Code.Locals reserva registros locales contiguos. V(n) identifica un registro
local; P(n), una palabra de parámetro. La API todavía no asigna registros
automáticamente a variables de un lenguaje de alto nivel.
En onCreate(Bundle), si se reservan cuatro locales, se usan seis registros:
v0..v3 son locales, this ocupa v4=P(0) y Bundle, v5=P(1). En un método
estático, P(0) es la primera palabra del primer parámetro. Los tipos long y
double ocupan dos palabras. El ensamblador determina el registro físico de cada
parámetro; no es necesario fijarlo al crear el IR.
En JSON V(n) se escribe como n, y P(n) como -n-1. Véase el constructor en
examples/dex/answer.json. Los flags se escriben como enteros; por ejemplo
Public|Static=9, Public|Constructor=65537. Public|Protected es un error:
no se permite combinar visibilidades contradictorias.
CodeBuilder.Emit y Build copian los registros y las referencias mutables.
Por tanto, puedes modificar cada snapshot sin alterar el builder ni los anteriores.
Instrucciones implementadas
Op del IR |
Operandos |
|---|---|
nop, return-void |
Sin registros |
move, move-object |
[destino, fuente]; elige 12x, 22x o 32x |
move-result, move-result-object |
[destino], inmediatamente después de invoke no void |
return, return-object |
[valor], categoría coherente con el prototipo |
const/4, const/16, const |
[destino], Literal int32; límites explícitos |
const-string |
[destino], String; selecciona jumbo si el índice supera 65535 |
const-class, new-instance, check-cast |
[destino], Type descriptor |
invoke-direct, invoke-virtual, invoke-static, invoke-super, invoke-interface |
Method y lista de palabras de registros, incluido receptor salvo static |
iget, iput |
[valor, objeto], Field |
sget, sput |
[valor], Field |
if-eqz, if-nez |
[registro], Target etiqueta |
instance-of, new-array |
[destino, fuente/tamaño], Type |
aget-object, aput-object |
[valor, array, índice] |
int-to-long |
[destino wide, fuente] |
if-lt, if-ge, if-ne |
[izquierda, derecha], Target |
move-exception |
[destino], al inicio de un handler |
goto |
Target; se emite goto/32 |
label |
Label; pseudo instrucción sin code units |
Los accesos a campos eligen las variantes object, boolean, byte, char y short a
partir del descriptor. Se rechazan campos wide. invoke usa 35c hasta cinco
palabras en registros de cuatro bits; usa 3rc cuando los registros son contiguos
si supera esa forma. Se rechazan rangos no contiguos y conteos incompatibles con
el prototipo. outs_size es el máximo de palabras de todas las invocaciones.
Los desplazamientos de salto se miden desde el opcode en unidades de 16 bits. El
parser rechaza saltos que caen dentro de otra instrucción, destinos fuera del código
o saltos a move-result. También rechaza resultados sin una invocación previa y
flujo alcanzable que salga del método. Todavía no comprueba todos los tipos
propagados por cada registro.
Identificadores y secciones
Strings: se ordenan por unidades UTF-16, no por bytes UTF-8. Se serializan en MUTF-8 con longitud en unidades UTF-16 y un terminador. El emisor acepta strings Go UTF-8 válidos. El decoder conserva las unidades surrogate aisladas necesarias para comparar strings DEX; al representarlas en Go, usa el carácter de reemplazo si no forman un par. Los descriptores y nombres se validan según DEX 035.
Tipos: orden por string index. Prototipos: return type mayor y lista de parámetros lexicográfica menor. Campos: clase, nombre, tipo. Métodos: clase, nombre, prototipo. Se internan referencias repetidas y se rechazan definiciones repetidas.
Las clases se ordenan por dependencias: primero superclases e interfaces definidas en el archivo. Los ciclos son errores. Métodos concretos tienen código; native y abstract no lo tienen. Constructores son direct methods y requieren su flag.
Header e IDs son little endian y de tamaño fijo. Las secciones de datos se emiten contiguas por tipo: strings, type_lists compartidas, code_items, class_data, mapa. Las estructuras que lo requieren se alinean a cuatro bytes. Los campos/métodos en class_data usan índices diferenciales ULEB128 ordenados por grupo.
Cada mapa contiene exactamente una entrada por sección no vacía. SHA-1 cubre
data[32:]; Adler-32 cubre data[12:], después de escribir SHA-1. Son hashes de
integridad del formato DEX, no una firma de identidad del desarrollador.
Los límites actuales son 65.535 tipos y prototipos, 65.536 referencias a campos y métodos, 65.535 registros por método y 255 palabras para la forma invoke/range. El emisor detecta desbordamientos en offsets y tamaños de 32 bits. Para limitar el coste de procesar entradas hostiles, la CLI acepta archivos de hasta 64 MiB.
Excepciones
CodeBuilder.Catch(start, end, handler, typeDescriptor) declara un bloque protegido
delimitado por etiquetas. El final del rango es exclusivo; un descriptor vacío
indica catch-all. Se admite un handler por rango y varios rangos no solapados.
El emisor escribe try_items y encoded_catch_handler_list, incluidos el padding
y los offsets. El parser independiente comprueba los límites, los destinos y
move-exception. Esta versión no admite anotaciones generales ni varios handlers
para un mismo rango.