UNPKG

@designliquido/delegua

Version:

Linguagem de programação simples e moderna usando português estruturado.

711 lines (708 loc) 27.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TradutorAssemblyARM = void 0; const construtos_1 = require("../construtos"); const declaracoes_1 = require("../declaracoes"); class TradutorAssemblyARM { constructor(alvo = 'linux-arm') { this.alvo = alvo; this.indentacao = 0; this.contadorLabels = 0; this.variaveis = new Map(); this.registradoresDisponiveis = ['r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'r10']; this.pilhaRegistradores = []; this.bss = '.bss\n'; this.data = '.data\n'; this.dicionarioConstrutos = { AcessoIndiceVariavel: this.traduzirAcessoIndiceVariavel.bind(this), AcessoMetodoOuPropriedade: this.trazudirConstrutoAcessoMetodo.bind(this), Agrupamento: this.traduzirConstrutoAgrupamento.bind(this), AtribuicaoPorIndice: this.traduzirConstrutoAtribuicaoPorIndice.bind(this), Atribuir: this.traduzirConstrutoAtribuir.bind(this), Binario: this.traduzirConstrutoBinario.bind(this), Chamada: this.traduzirConstrutoChamada.bind(this), DefinirValor: this.traduzirConstrutoDefinirValor.bind(this), FuncaoConstruto: this.traduzirFuncaoConstruto.bind(this), Isto: () => 'this', Literal: this.traduzirConstrutoLiteral.bind(this), Logico: this.traduzirConstrutoLogico.bind(this), TipoDe: this.traduzirConstrutoTipoDe.bind(this), Unario: this.traduzirConstrutoUnario.bind(this), Variavel: this.traduzirConstrutoVariavel.bind(this), Vetor: this.traduzirConstrutoVetor.bind(this), }; this.dicionarioDeclaracoes = { Bloco: this.traduzirDeclaracaoBloco.bind(this), Enquanto: this.traduzirDeclaracaoEnquanto.bind(this), Continua: () => 'b .continue_label', Escolha: this.traduzirDeclaracaoEscolha.bind(this), Escreva: this.traduzirDeclaracaoEscreva.bind(this), Expressao: this.traduzirDeclaracaoExpressao.bind(this), Fazer: this.traduzirDeclaracaoFazer.bind(this), Falhar: this.traduzirDeclaracaoFalhar.bind(this), FuncaoDeclaracao: this.traduzirDeclaracaoFuncao.bind(this), Importar: this.traduzirDeclaracaoImportar.bind(this), Leia: this.traduzirDeclaracaoLeia.bind(this), Para: this.traduzirDeclaracaoPara.bind(this), ParaCada: this.traduzirDeclaracaoParaCada.bind(this), Retorna: this.traduzirDeclaracaoRetorna.bind(this), Se: this.traduzirDeclaracaoSe.bind(this), Sustar: () => 'b .break_label', Classe: this.traduzirDeclaracaoClasse.bind(this), Tente: this.traduzirDeclaracaoTente.bind(this), Const: this.traduzirDeclaracaoConst.bind(this), Var: this.traduzirDeclaracaoVar.bind(this), }; this.indentacao = 0; // Seleciona o símbolo de entrada conforme a plataforma alvo. // Para linux-arm: programa standalone com _start. // Para android: ainda é um binário standalone, mas com rótulo Delegua_main // para deixar claro que não é o _start do CRT padrão. const entryLabel = this.alvo === 'android' ? 'Delegua_main' : '_start'; this.text = ` .text .global ${entryLabel} ${entryLabel}:`; } gerarDigitoAleatorio() { let result = ''; const digits = '0123456789'; for (let i = 0; i < 5; i++) { const randomIndex = Math.floor(Math.random() * digits.length); result += digits.charAt(randomIndex); } return result; } gerarLabel() { return `.L${this.contadorLabels++}`; } obterRegistrador() { if (this.registradoresDisponiveis.length > 0) { const reg = this.registradoresDisponiveis.pop(); this.pilhaRegistradores.push(reg); return reg; } return 'r0'; // fallback } liberarRegistrador(reg) { const index = this.pilhaRegistradores.indexOf(reg); if (index > -1) { this.pilhaRegistradores.splice(index, 1); this.registradoresDisponiveis.push(reg); } } // Implementação dos Construtos traduzirAcessoIndiceVariavel(construto) { let nomeVar; if (construto.entidadeChamada instanceof construtos_1.Variavel) { nomeVar = construto.entidadeChamada.simbolo?.lexema; } if (!nomeVar) { nomeVar = 'unknown'; } const indice = this.dicionarioConstrutos[construto.indice.constructor.name](construto.indice); const reg = this.obterRegistrador(); this.text += ` ldr ${reg}, =${nomeVar} ldr r0, =${indice} lsl r0, r0, #2 @ multiply index by 4 (word size) add ${reg}, ${reg}, r0 ldr r0, [${reg}]`; this.liberarRegistrador(reg); return 'r0'; } trazudirConstrutoAcessoMetodo(construto) { const objeto = this.dicionarioConstrutos[construto.objeto.constructor.name](construto.objeto); return `${objeto}_${construto.nomeMetodo}`; } traduzirConstrutoAgrupamento(construto) { return this.dicionarioConstrutos[construto.expressao.constructor.name](construto.expressao); } traduzirConstrutoAtribuicaoPorIndice(construto) { let nomeVar; if (construto.objeto instanceof construtos_1.Variavel) { nomeVar = construto.objeto.simbolo?.lexema; } if (!nomeVar) { nomeVar = 'unknown'; } const indice = this.dicionarioConstrutos[construto.indice.constructor.name](construto.indice); const valor = this.dicionarioConstrutos[construto.valor.constructor.name](construto.valor); const reg = this.obterRegistrador(); this.text += ` ldr ${reg}, =${nomeVar} ldr r1, =${indice} lsl r1, r1, #2 @ multiply by 4 add ${reg}, ${reg}, r1 ldr r1, =${valor} str r1, [${reg}]`; this.liberarRegistrador(reg); } traduzirConstrutoAtribuir(construto) { let nomeVar; if (construto.alvo instanceof construtos_1.Variavel) { nomeVar = construto.alvo.simbolo?.lexema; } if (!nomeVar) { return; } const valor = this.dicionarioConstrutos[construto.valor.constructor.name](construto.valor); if (!this.variaveis.has(nomeVar)) { const varLabel = `var_${nomeVar}`; this.bss += ` ${varLabel}: .space 4\n`; this.variaveis.set(nomeVar, varLabel); } this.text += ` ldr r0, =${valor} ldr r1, =${this.variaveis.get(nomeVar)} str r0, [r1]`; } traduzirConstrutoBinario(construto) { const esquerda = this.dicionarioConstrutos[construto.esquerda.constructor.name](construto.esquerda); const direita = this.dicionarioConstrutos[construto.direita.constructor.name](construto.direita); const operador = construto.operador.lexema; const reg = this.obterRegistrador(); // Load left operand into r0 if (esquerda !== 'r0') { this.text += ` ldr r0, =${esquerda}`; } // Load right operand into reg this.text += ` ldr ${reg}, =${direita}`; switch (operador) { case '+': this.text += ` add r0, r0, ${reg}`; break; case '-': this.text += ` sub r0, r0, ${reg}`; break; case '*': this.text += ` mul r0, r0, ${reg}`; break; case '/': this.text += ` sdiv r0, r0, ${reg}`; break; case '%': this.text += ` sdiv r1, r0, ${reg} mul r1, r1, ${reg} sub r0, r0, r1 @ r0 = r0 - (r0/reg)*reg`; break; case '<': this.text += ` cmp r0, ${reg} movlt r0, #1 movge r0, #0`; break; case '>': this.text += ` cmp r0, ${reg} movgt r0, #1 movle r0, #0`; break; case '<=': this.text += ` cmp r0, ${reg} movle r0, #1 movgt r0, #0`; break; case '>=': this.text += ` cmp r0, ${reg} movge r0, #1 movlt r0, #0`; break; case '==': case '===': this.text += ` cmp r0, ${reg} moveq r0, #1 movne r0, #0`; break; case '!=': case '!==': this.text += ` cmp r0, ${reg} movne r0, #1 moveq r0, #0`; break; default: this.text += ` @ Operador ${operador} não implementado`; } this.liberarRegistrador(reg); return 'r0'; } traduzirConstrutoChamada(construto) { let nomeFuncao = 'funcao'; if (construto.entidadeChamada instanceof construtos_1.Variavel) { nomeFuncao = construto.entidadeChamada.simbolo?.lexema || 'funcao'; } // ARM calling convention: r0-r3 for first 4 args, rest on stack const registrosArgs = ['r0', 'r1', 'r2', 'r3']; construto.argumentos.forEach((argumento, indice) => { if (indice < registrosArgs.length) { const valorArg = this.dicionarioConstrutos[argumento.constructor.name](argumento); if (valorArg !== registrosArgs[indice]) { this.text += ` ldr ${registrosArgs[indice]}, =${valorArg}`; } } else { // Push extra args on stack const valorArg = this.dicionarioConstrutos[argumento.constructor.name](argumento); this.text += ` ldr r0, =${valorArg} push {r0}`; } }); this.text += ` bl ${nomeFuncao}`; } traduzirConstrutoDefinirValor(construto) { const objeto = this.dicionarioConstrutos[construto.objeto.constructor.name](construto.objeto); const valor = this.dicionarioConstrutos[construto.valor.constructor.name](construto.valor); this.text += ` ldr r0, =${valor} ldr r1, =${objeto} str r0, [r1]`; } traduzirFuncaoConstruto(construto) { const labelFuncao = `func_${this.gerarDigitoAleatorio()}`; this.text += ` ${labelFuncao}: push {fp, lr} mov fp, sp`; // Traduzir corpo da função if (construto.corpo && Array.isArray(construto.corpo)) { construto.corpo.forEach((declaracao) => { if (this.dicionarioDeclaracoes[declaracao.constructor.name]) { this.dicionarioDeclaracoes[declaracao.constructor.name](declaracao); } }); } this.text += ` mov sp, fp pop {fp, pc}`; } traduzirConstrutoLiteral(construto) { if (typeof construto.valor === 'string') { return this.criaStringLiteral(construto); } return String(construto.valor); } traduzirConstrutoLogico(construto) { const esquerda = this.dicionarioConstrutos[construto.esquerda.constructor.name](construto.esquerda); const direita = this.dicionarioConstrutos[construto.direita.constructor.name](construto.direita); const operador = construto.operador.lexema; const labelVerdadeiro = this.gerarLabel(); const labelFim = this.gerarLabel(); this.text += ` ldr r0, =${esquerda} cmp r0, #0`; if (operador === 'e' || operador === '&&') { this.text += ` beq ${labelFim} ldr r0, =${direita} cmp r0, #0 beq ${labelFim} ${labelVerdadeiro}: mov r0, #1 ${labelFim}:`; } else if (operador === 'ou' || operador === '||') { this.text += ` bne ${labelVerdadeiro} ldr r0, =${direita} cmp r0, #0 bne ${labelVerdadeiro} mov r0, #0 b ${labelFim} ${labelVerdadeiro}: mov r0, #1 ${labelFim}:`; } return 'r0'; } traduzirConstrutoTipoDe(construto) { const expressao = this.dicionarioConstrutos[construto.valor.constructor.name](construto.valor); return expressao; } traduzirConstrutoUnario(construto) { const operando = this.dicionarioConstrutos[construto.operando.constructor.name](construto.operando); const operador = construto.operador.lexema; this.text += ` ldr r0, =${operando}`; if (operador === '-') { this.text += ` neg r0, r0`; } else if (operador === '!' || operador === 'nao') { this.text += ` cmp r0, #0 moveq r0, #1 movne r0, #0`; } return 'r0'; } traduzirConstrutoVariavel(construto) { const nomeVar = construto.simbolo?.lexema; if (nomeVar && this.variaveis.has(nomeVar)) { const varLabel = this.variaveis.get(nomeVar); this.text += ` ldr r0, =${varLabel} ldr r0, [r0]`; return 'r0'; } return nomeVar || 'unknown'; } traduzirConstrutoVetor(construto) { const labelVetor = `vetor_${this.gerarDigitoAleatorio()}`; const tamanho = construto.valores?.length || 0; this.bss += ` ${labelVetor}: .space ${tamanho * 4}\n`; if (construto.valores && Array.isArray(construto.valores)) { construto.valores.forEach((valor, index) => { if (this.dicionarioConstrutos[valor.constructor.name]) { const valorTraduzido = this.dicionarioConstrutos[valor.constructor.name](valor); this.text += ` ldr r0, =${valorTraduzido} ldr r1, =${labelVetor} str r0, [r1, #${index * 4}]`; } }); } return labelVetor; } // Implementação das Declarações traduzirDeclaracaoBloco(declaracao) { if (declaracao.declaracoes && Array.isArray(declaracao.declaracoes)) { declaracao.declaracoes.forEach((decl) => { if (this.dicionarioDeclaracoes[decl.constructor.name]) { this.dicionarioDeclaracoes[decl.constructor.name](decl); } }); } } traduzirDeclaracaoEnquanto(declaracao) { const labelInicio = this.gerarLabel(); const labelFim = this.gerarLabel(); this.text += ` ${labelInicio}:`; const condicao = this.dicionarioConstrutos[declaracao.condicao.constructor.name](declaracao.condicao); this.text += ` cmp ${condicao}, #0 beq ${labelFim}`; if (this.dicionarioDeclaracoes[declaracao.corpo.constructor.name]) { this.dicionarioDeclaracoes[declaracao.corpo.constructor.name](declaracao.corpo); } this.text += ` b ${labelInicio} ${labelFim}:`; } traduzirDeclaracaoEscolha(declaracao) { const labelFim = this.gerarLabel(); const valorEscolha = this.dicionarioConstrutos[declaracao.identificadorOuLiteral.constructor.name](declaracao.identificadorOuLiteral); if (declaracao.caminhos && Array.isArray(declaracao.caminhos)) { declaracao.caminhos.forEach((caminho) => { const labelProximo = this.gerarLabel(); if (caminho.condicoes && caminho.condicoes[0]) { const valorCaso = this.dicionarioConstrutos[caminho.condicoes[0].constructor.name](caminho.condicoes[0]); this.text += ` ldr r0, =${valorEscolha} ldr r1, =${valorCaso} cmp r0, r1 bne ${labelProximo}`; if (caminho.declaracoes && Array.isArray(caminho.declaracoes)) { caminho.declaracoes.forEach((decl) => { if (this.dicionarioDeclaracoes[decl.constructor.name]) { this.dicionarioDeclaracoes[decl.constructor.name](decl); } }); } this.text += ` b ${labelFim} ${labelProximo}:`; } }); } this.text += ` ${labelFim}:`; } traduzirDeclaracaoExpressao(declaracao) { if (declaracao.expressao && this.dicionarioConstrutos[declaracao.expressao.constructor.name]) { this.dicionarioConstrutos[declaracao.expressao.constructor.name](declaracao.expressao); } } traduzirDeclaracaoFazer(declaracao) { const labelInicio = this.gerarLabel(); this.text += ` ${labelInicio}:`; if (declaracao.caminhoFazer && declaracao.caminhoFazer.declaracoes) { declaracao.caminhoFazer.declaracoes.forEach((decl) => { if (this.dicionarioDeclaracoes[decl.constructor.name]) { this.dicionarioDeclaracoes[decl.constructor.name](decl); } }); } if (declaracao.condicaoEnquanto) { const condicao = this.dicionarioConstrutos[declaracao.condicaoEnquanto.constructor.name](declaracao.condicaoEnquanto); this.text += ` cmp ${condicao}, #0 bne ${labelInicio}`; } } traduzirDeclaracaoFalhar(declaracao) { let mensagem = '"Erro"'; if (declaracao.explicacao && typeof declaracao.explicacao === 'object' && 'constructor' in declaracao.explicacao) { const explicacao = declaracao.explicacao; if (explicacao.constructor && this.dicionarioConstrutos[explicacao.constructor.name]) { mensagem = this.dicionarioConstrutos[explicacao.constructor.name](explicacao); } } this.text += ` @ Falhar com mensagem: ${mensagem} mov r0, #1 mov r7, #1 @ sys_exit swi 0`; } traduzirDeclaracaoFuncao(declaracao) { const nomeFuncao = declaracao.simbolo?.lexema || 'funcao'; this.text += ` ${nomeFuncao}: push {fp, lr} mov fp, sp`; if (declaracao.funcao && declaracao.funcao.corpo && Array.isArray(declaracao.funcao.corpo)) { declaracao.funcao.corpo.forEach((decl) => { if (this.dicionarioDeclaracoes[decl.constructor.name]) { this.dicionarioDeclaracoes[decl.constructor.name](decl); } }); } this.text += ` mov sp, fp pop {fp, pc}`; } traduzirDeclaracaoImportar(declaracao) { this.text += ` @ Importar: ${declaracao.caminho || 'unknown'}`; } traduzirDeclaracaoLeia(declaracao) { let nomeVar; if (declaracao.argumentos && declaracao.argumentos[0] && declaracao.argumentos[0] instanceof construtos_1.Variavel) { nomeVar = declaracao.argumentos[0].simbolo?.lexema; } if (!nomeVar) return; if (!this.variaveis.has(nomeVar)) { const varLabel = `var_${nomeVar}`; this.bss += ` ${varLabel}: .space 256\n`; this.variaveis.set(nomeVar, varLabel); } this.text += ` ldr r1, =${this.variaveis.get(nomeVar)} mov r2, #256 mov r0, #0 @ stdin mov r7, #3 @ sys_read swi 0`; } traduzirDeclaracaoPara(declaracao) { const labelInicio = this.gerarLabel(); const labelFim = this.gerarLabel(); if (declaracao.inicializador) { const tipoInicializador = declaracao.inicializador.constructor.name; if (this.dicionarioDeclaracoes[tipoInicializador]) { this.dicionarioDeclaracoes[tipoInicializador](declaracao.inicializador); } else if (this.dicionarioConstrutos[tipoInicializador]) { this.dicionarioConstrutos[tipoInicializador](declaracao.inicializador); } } this.text += ` ${labelInicio}:`; if (declaracao.condicao) { const condicao = this.dicionarioConstrutos[declaracao.condicao.constructor.name](declaracao.condicao); this.text += ` cmp ${condicao}, #0 beq ${labelFim}`; } if (this.dicionarioDeclaracoes[declaracao.corpo.constructor.name]) { this.dicionarioDeclaracoes[declaracao.corpo.constructor.name](declaracao.corpo); } if (declaracao.incrementar) { if (this.dicionarioConstrutos[declaracao.incrementar.constructor.name]) { this.dicionarioConstrutos[declaracao.incrementar.constructor.name](declaracao.incrementar); } } this.text += ` b ${labelInicio} ${labelFim}:`; } traduzirDeclaracaoParaCada(declaracao) { const labelInicio = this.gerarLabel(); const labelFim = this.gerarLabel(); let nomeVar; if (declaracao.variavelIteracao instanceof construtos_1.Variavel) { nomeVar = declaracao.variavelIteracao.simbolo?.lexema; } const vetor = declaracao.vetorOuDicionario; let tamanhoVetor = 0; if (vetor instanceof construtos_1.Vetor) { tamanhoVetor = vetor.tamanho || 0; } this.text += ` mov r4, #0 @ counter ${labelInicio}: cmp r4, #${tamanhoVetor} bge ${labelFim}`; if (this.dicionarioDeclaracoes[declaracao.corpo.constructor.name]) { this.dicionarioDeclaracoes[declaracao.corpo.constructor.name](declaracao.corpo); } this.text += ` add r4, r4, #1 b ${labelInicio} ${labelFim}:`; } traduzirDeclaracaoRetorna(declaracao) { if (declaracao.valor) { const valor = this.dicionarioConstrutos[declaracao.valor.constructor.name](declaracao.valor); this.text += ` ldr r0, =${valor}`; } this.text += ` mov sp, fp pop {fp, pc}`; } traduzirDeclaracaoSe(declaracao) { const labelSenao = this.gerarLabel(); const labelFim = this.gerarLabel(); const condicao = this.dicionarioConstrutos[declaracao.condicao.constructor.name](declaracao.condicao); this.text += ` cmp ${condicao}, #0 beq ${labelSenao}`; if (this.dicionarioDeclaracoes[declaracao.caminhoEntao.constructor.name]) { this.dicionarioDeclaracoes[declaracao.caminhoEntao.constructor.name](declaracao.caminhoEntao); } this.text += ` b ${labelFim} ${labelSenao}:`; if (declaracao.caminhoSenao && this.dicionarioDeclaracoes[declaracao.caminhoSenao.constructor.name]) { this.dicionarioDeclaracoes[declaracao.caminhoSenao.constructor.name](declaracao.caminhoSenao); } this.text += ` ${labelFim}:`; } traduzirDeclaracaoClasse(declaracao) { this.text += ` @ Classe: ${declaracao.simbolo?.lexema || 'unknown'}`; } traduzirDeclaracaoTente(declaracao) { this.text += ` @ Tente-pegue`; if (declaracao.caminhoTente && Array.isArray(declaracao.caminhoTente)) { declaracao.caminhoTente.forEach((decl) => { if (this.dicionarioDeclaracoes[decl.constructor.name]) { this.dicionarioDeclaracoes[decl.constructor.name](decl); } }); } } traduzirDeclaracaoConst(declaracao) { const nomeVar = declaracao.simbolo?.lexema; if (!nomeVar) return; const valor = this.dicionarioConstrutos[declaracao.inicializador.constructor.name](declaracao.inicializador); const varLabel = `const_${nomeVar}`; this.data += ` ${varLabel}: .word ${valor}\n`; this.variaveis.set(nomeVar, varLabel); } traduzirDeclaracaoVar(declaracao) { const nomeVar = declaracao.simbolo?.lexema; if (!nomeVar) return; const varLabel = `var_${nomeVar}`; this.bss += ` ${varLabel}: .space 4\n`; this.variaveis.set(nomeVar, varLabel); if (declaracao.inicializador && declaracao.inicializador.valor !== null) { const tipoInicializador = declaracao.inicializador.constructor.name; // Verificar se é um vetor if (declaracao.inicializador instanceof construtos_1.Vetor) { // Vetor precisa de tratamento especial const labelVetor = this.traduzirConstrutoVetor(declaracao.inicializador); // Associar o nome da variável com o label do vetor this.variaveis.set(nomeVar, labelVetor); } else if (this.dicionarioConstrutos[tipoInicializador]) { const valor = this.dicionarioConstrutos[tipoInicializador](declaracao.inicializador); this.text += ` ldr r0, =${valor} ldr r1, =${varLabel} str r0, [r1]`; } } } criaStringLiteral(literal) { const varLiteral = `Delegua_${this.gerarDigitoAleatorio()}`; this.data += ` ${varLiteral}: .asciz "${literal.valor}"\n`; return varLiteral; } criaTamanhoNaMemoriaReferenteAVar(nomeStringLiteral) { const varTamanho = `tam_${nomeStringLiteral}`; // Em ARM, calculamos o tamanho de forma diferente // Podemos usar uma diretiva ou calcular em tempo de execução return varTamanho; } traduzirDeclaracaoEscreva(declaracaoEscreva) { let tam_string_literal = ''; let nome_string_literal = ''; if (declaracaoEscreva.argumentos[0] instanceof construtos_1.Literal) { nome_string_literal = this.criaStringLiteral(declaracaoEscreva.argumentos[0]); const stringValue = declaracaoEscreva.argumentos[0].valor; tam_string_literal = String(stringValue.length); } // Para ambos linux-arm e android, continuamos usando a convenção // de syscall Linux ARM (write). Em Android típico, esse binário // seria executado via adb/Termux, onde essa convenção ainda é válida. this.text += ` ldr r1, =${nome_string_literal} mov r2, #${tam_string_literal} mov r0, #1 @ fd stdout mov r7, #4 @ sys_write swi 0`; } saidaSistema() { // Mesmo comentário da função Escreva: usamos sys_exit Linux. // Em um futuro modo Android "NDK/JNI", esta função deveria // apenas retornar ao chamador em vez de fazer syscall direta. this.text += ` mov r0, #1 @ exit status mov r7, #1 @ sys_exit swi 0`; } traduzir(declaracoes) { let resultado = ''; this.declaracoesDeClasses = declaracoes.filter((declaracao) => declaracao instanceof declaracoes_1.Classe); for (const declaracao of declaracoes) { if (this.dicionarioDeclaracoes[declaracao.constructor.name]) { this.dicionarioDeclaracoes[declaracao.constructor.name](declaracao); } } this.saidaSistema(); resultado += this.bss + '\n' + this.data + '\n' + this.text; return resultado; } } exports.TradutorAssemblyARM = TradutorAssemblyARM; //# sourceMappingURL=tradutor-assembly-arm.js.map