Compilar (um subconjunto de) JavaScript ou AS2 para ActionScript 2 (Flash Player 8 Compatível) Assembléia de Bytecode compilável pelo FLASM. Extremamente WIP.
Estou modificando um jogo flash antigo e me cansei de escrever o ActionScript Bytecode manualmente. Felizmente, o ActionScript 2 é extremamente semelhante ao JavaScript (já que o AS2 está parcialmente em conformidade com o ECMAScript 4 Spec) para que eu possa usar @babel/parser para analisar minha fonte como JavaScript em um AST e depois emitir o bytecode do AST.
O compilador suporta apenas um subconjunto de recursos JS/AS2 de que preciso, e o projeto se reserva o direito de açougues semântica do idioma para facilitar a implementação. O desenvolvimento também progredirá apenas quando eu encontrar os recursos que quero implementar para facilitar minha vida.
O compilador também simula a pilha para documentar a pilha atual como comentários à direita de cada linha para facilitar a verificação manual. Se uma filial ou o código de filial for atingido (um if-else ou um loop), o simulador pulará o resto da função/contexto. As funções internas são simuladas com suas próprias pilhas. As declarações de código também são adicionadas como comentários antes do bytecode para o comunicado.
@js2f/push-register-context , @js2f/pop-register-contextFonte de amostra atual de trabalho em andamento:
outsideGlobalVar = globalVar2 = 123 ;
function gatherStats ( velocity ) {
var emptyLocal , emptyLocal2 , nonEmptyLocal3 ;
var localVar = 123 ;
globalVar = 5432 ;
globalVar = localVar = 1111 ;
globalVar = globalVar2 = 1111 ;
globalVar = globalVar2 = undefined ;
velocity = atv . velocity ;
globalVelocity = atv . velocity ;
atv . bar = 1 ;
this . foo = this . bar + 1 ;
atv . x = atv . velocityX - atv . x ;
localVar = "foo\nbar" ;
return '{"type":"velocity","data":' + ( velocity + 1 ) + "}" ;
}
global . enqueueStats ( gatherStats ( atvMC . velocity ) , 1 ) ;
enqueueStats ( gatherStats ( atvMC . velocity ) ) ;
emptyFunction ( ) ;
global . emptyFunction ( ) ;Emite:
// -- outsideGlobalVar = globalVar2 = 123;
// -- outsideGlobalVar = globalVar2 = 123
push ' outsideGlobalVar ' // ' outsideGlobalVar '
// -- globalVar2 = 123
push ' globalVar2 ' , 123 // ' outsideGlobalVar ' | ' globalVar2 ' | 123
setVariable // ' outsideGlobalVar '
push 123 // ' outsideGlobalVar ' | 123
setVariable // -- <empty>
function2 ' gatherStats ' ( r : 2 = ' velocity ' ) ( r : 1 = ' this ' )
// -- var emptyLocal, emptyLocal2, nonEmptyLocal3;
// -- var localVar = 123;
push 123 // 123
setRegister r : 6 /* local:localVar */ // 123
pop // -- <empty>
// -- globalVar = 5432;
// -- globalVar = 5432
push ' globalVar ' , 5432 // ' globalVar ' | 5432
setVariable // -- <empty>
// -- globalVar = localVar = 1111;
// -- globalVar = localVar = 1111
push ' globalVar ' // ' globalVar '
// -- localVar = 1111
push 1111 // ' globalVar ' | 1111
setRegister r : 6 /* local:localVar */ // ' globalVar ' | 1111
setVariable // -- <empty>
// -- globalVar = globalVar2 = 1111;
// -- globalVar = globalVar2 = 1111
push ' globalVar ' // ' globalVar '
// -- globalVar2 = 1111
push ' globalVar2 ' , 1111 // ' globalVar ' | ' globalVar2 ' | 1111
setVariable // ' globalVar '
push 1111 // ' globalVar ' | 1111
setVariable // -- <empty>
// -- globalVar = globalVar2 = undefined;
// -- globalVar = globalVar2 = undefined
push ' globalVar ' // ' globalVar '
// -- globalVar2 = undefined
push ' globalVar2 ' , UNDEF // ' globalVar ' | ' globalVar2 ' | UNDEF
setVariable // ' globalVar '
push UNDEF // ' globalVar ' | UNDEF
setVariable // -- <empty>
// -- velocity = atv.velocity;
// -- velocity = atv.velocity
push ' atv ' // ' atv '
getVariable // atv
push ' velocity ' // atv | ' velocity '
getMember // atv . velocity
setRegister r : velocity // atv . velocity
pop // -- <empty>
// -- globalVelocity = atv.velocity;
// -- globalVelocity = atv.velocity
push ' globalVelocity ' , ' atv ' // ' globalVelocity ' | ' atv '
getVariable // ' globalVelocity ' | atv
push ' velocity ' // ' globalVelocity ' | atv | ' velocity '
getMember // ' globalVelocity ' | atv . velocity
setVariable // -- <empty>
// -- atv.bar = 1;
// -- atv.bar = 1
push ' atv ' // ' atv '
getVariable // atv
push ' bar ' , 1 // atv | ' bar ' | 1
setMember // -- <empty>
// -- this.foo = this.bar + 1;
// -- this.foo = this.bar + 1
push r : this , ' foo ' , r : this , ' bar ' // r : this | ' foo ' | r : this | ' bar '
getMember // r : this | ' foo ' | r : this . bar
push 1 // r : this | ' foo ' | r : this . bar | 1
add // r : this | ' foo ' | r : this . bar + 1
setMember // -- <empty>
// -- atv.x = atv.velocityX - atv.x;
// -- atv.x = atv.velocityX - atv.x
push ' atv ' // ' atv '
getVariable // atv
push ' x ' , ' atv ' // atv | ' x ' | ' atv '
getVariable // atv | ' x ' | atv
push ' velocityX ' // atv | ' x ' | atv | ' velocityX '
getMember // atv | ' x ' | atv . velocityX
push ' atv ' // atv | ' x ' | atv . velocityX | ' atv '
getVariable // atv | ' x ' | atv . velocityX | atv
push ' x ' // atv | ' x ' | atv . velocityX | atv | ' x '
getMember // atv | ' x ' | atv . velocityX | atv . x
subtract // atv | ' x ' | atv . velocityX - atv . x
setMember // -- <empty>
// -- localVar = "foo\nbar";
// -- localVar = "foo\nbar"
push ' foo n bar ' // ' foo n bar '
setRegister r : 6 /* local:localVar */ // ' foo n bar '
pop // -- <empty>
// -- return '{"type":"velocity","data":' + (velocity + 1) + "}";
push ' {"type":"velocity","data": ' , r : velocity , 1 // ' {"type":"velocity","data": ' | r : velocity | 1
add // ' {"type":"velocity","data": ' | r : velocity + 1
add // ' {"type":"velocity","data": ' + ( r : velocity + 1 )
push ' } ' // ' {"type":"velocity","data": ' + ( r : velocity + 1 ) | ' } '
add // ( ' {"type":"velocity","data": ' + ( r : velocity + 1 )) + ' } '
return // ( ' {"type":"velocity","data": ' + ( r : velocity + 1 )) + ' } '
end // of function gatherStats
// -- global.enqueueStats(gatherStats(atvMC.velocity), 1);
push 1, ' atvMC ' // 1 | ' atvMC '
getVariable // 1 | atvMC
push ' velocity ' // 1 | atvMC | ' velocity '
getMember // 1 | atvMC . velocity
push 1 , ' gatherStats ' // 1 | atvMC . velocity | 1 | ' gatherStats '
callFunction // 1 | gatherStats ( atvMC . velocity )
push 2 , ' global ' // 1 | gatherStats ( atvMC . velocity ) | 2 | ' global '
getVariable // 1 | gatherStats ( atvMC . velocity ) | 2 | global
push ' enqueueStats ' // 1 | gatherStats ( atvMC . velocity ) | 2 | global | ' enqueueStats '
callMethod // global . enqueueStats ( gatherStats ( atvMC . velocity ), 1 )
pop // -- <empty>
// -- enqueueStats(gatherStats(atvMC.velocity));
push ' atvMC ' // ' atvMC '
getVariable // atvMC
push ' velocity ' // atvMC | ' velocity '
getMember // atvMC . velocity
push 1 , ' gatherStats ' // atvMC . velocity | 1 | ' gatherStats '
callFunction // gatherStats ( atvMC . velocity )
push 1 , ' enqueueStats ' // gatherStats ( atvMC . velocity ) | 1 | ' enqueueStats '
callFunction // enqueueStats ( gatherStats ( atvMC . velocity ))
pop // -- <empty>
// -- emptyFunction();
push 0 , ' emptyFunction ' // 0 | ' emptyFunction '
callFunction // emptyFunction ()
pop // -- <empty>
// -- global.emptyFunction();
push 0 , ' global ' // 0 | ' global '
getVariable // 0 | global
push ' emptyFunction ' // 0 | global | ' emptyFunction '
callMethod // global . emptyFunction ()
pop // -- <empty> Clone o repositório e execute npm i . Em seguida, execute npm start ou instale-o como um módulo NPM relativo local e execute-o como js-to-flasm-as2-compiler Inside package.json
js-to-flasm-as2-compiler [configFilePath]
$ npm start
> [email protected] start D: P rojects j s-to-flasm-compiler
> node ./bin/index.js
samples A .js - > dist A .flm
samples B .js - > dist B .flm
empty file samples C a.js
samples C aa.js - > dist C aa.flm
samples h ooks f oobar i nitialize-socket-hook.js - > dist h ooks f oobar i nitialize-socket-hook.flmSe você estiver no Linux, provavelmente pode apenas
chmod u+x bin/index.js
ln -s $PWD /bin/index.js ./js-to-flasm-as2-compiler
./js-to-flasm-as2-compiler O compilador compilará os arquivos de origem JS em samples/ , e emitirá saída para o diretório dist/ .
O compilador trava se você usar os recursos JS que eu não implementei.
Os comentários também são //-- e /*--[[ --]]*/ porque os comentários do FLASM são // e /* */ , mas eu uso a sintaxe de Lua destacando para o FLM (é bom o suficiente!) E Lua são -- e --[[ --]] .
var é suportado (sem let ou const )if não suporta operadores binários lógicos&& ou || ), no entanto ! éclass não é implementada, use a programação baseada em protótipo (faça uma função que seja um construtor e atribua funções ao cTor.prototype)int() em vez de parseInt()var foo = parseInt(bar) do var foo = int(bar)intfoo[bar] ou foo['bar'] ou foo[x + y + 1 + true + 0] está bom, mas foo[bar++] ou foo[selector()] não conseguirá compilar++variable funciona, mas ++foo.bar ou ++foo[bar]variable++ ou variable++ não é implementado O compilador procura um arquivo de configuração chamado js-to-flasm.config.json no diretório atual, ou de um caminho especificado pelo primeiro argumento que passou para ele.
Por exemplo
js-to-flasm-as2-compiler ./config/js-to-flasm.config.json
Exemplo de arquivo de configuração:
{
"dist" : " dist/ " ,
"sourceRoot" : " samples/ "
} Nenhuma outra chaves é suportada e ambas são necessárias. Ambos os caminhos de arquivo podem ser qualquer coisa que fs.readdir() do Node entenda. Terminando / é provavelmente opcional.
As diretrizes do compilador são implementadas por meio de comentários de linha única. As diretivas não são pesquisadas por comentários internos do bloco ou comentários JSDOC. Uma diretiva é de forma:
// directive-name
ou se aceitar argumentos, é de forma:
// directive-with-args: arg1 arg2 arg3 argn
As diretivas atuais são:
@js2f/push-register-context , @js2f/pop-register-context Permite informar ao compilador sobre associações de variáveis Register <->. Por exemplo, se você está compilando o código que fica com #include 'D dentro de uma função que você não controla (ou seja, é um código desmontado), e você deseja acessar this dentro da função, você pode instruir o compilador que ele está no registro n.
Prenda o contexto assim que não precisar mais dele.
Exemplo 1
// @js2f/push-register-context: r:1=this
enqueueSocketJob ( '{"type": "cash", "data":' + this . cash + "}" ) ;
// @js2f/pop-register-contextExemplo 2
// @js2f/push-register-context: r:3=socket
socket . onConnect = function ( ) {
// can use local variables here
var foobar = 123 ;
} ;
// @js2f/pop-register-context MIT, consulte a licença.