Kompilieren Sie (eine Teilmenge von) JavaScript oder AS2 zu ActionScript 2 (Flash -Player 8 -kompatibel) Bytecode -Assemblierung, die mit Flasm kompiliert werden kann. Extrem WIP.
Ich modiere ein altes Flash -Spiel und habe es satt, ActionScript -Bytecode von Hand zu schreiben. Glücklicherweise ist ActionScript 2 JavaScript sehr ähnlich (da AS2 teilweise der Spezifikation der ECMascript 4 entspricht), sodass ich einfach @babel/parser verwenden kann, um meine Quelle als JavaScript in einen AST zu analysieren und dann den Bytecode aus dem AST auszugeben.
Der Compiler unterstützt nur eine Untergruppe von JS/AS2 -Funktionen, die ich benötige, und das Projekt behält sich das Recht vor, die Sprachsemantik zu schließen, um die Implementierung zu erleichtern. Die Entwicklung wird auch weiterentwickeln, wenn ich Funktionen finde, die ich implementieren möchte, um mein Leben zu erleichtern.
Der Compiler simuliert auch den Stapel, um den aktuellen Stapel rechts neben jeder Zeile als Kommentare zu dokumentieren, um die manuelle Überprüfung zu vereinfachen. Wenn ein Zweig- oder Zweig-Opcode erreicht ist (ein IF-ELSE oder eine Schleife), überspringt der Simulator die Ruhe der Funktion/des Kontextes. Innenfunktionen werden mit ihren eigenen Stapeln simuliert. Code -Anweisungen werden auch als Kommentare vor der Bytecode für diese Erklärung hinzugefügt.
@js2f/push-register-context , @js2f/pop-register-contextAktuelle Arbeitenquelle in der Progress:
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 ( ) ;Emittiert:
// -- 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> Klonen Sie das Repo und rennen Sie npm i . Führen js-to-flasm-as2-compiler dann npm start oder installieren package.json es als lokales relatives NPM
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.flmWenn Sie unter Linux sind, können Sie wahrscheinlich nur gerecht
chmod u+x bin/index.js
ln -s $PWD /bin/index.js ./js-to-flasm-as2-compiler
./js-to-flasm-as2-compiler Der Compiler kompiliert die JS -Quelldateien in samples/ und emittiert die Ausgabe in das dist/ Verzeichnis.
Der Compiler stürzt ab, wenn Sie JS -Funktionen verwenden, die ich nicht implementiert habe.
Die Kommentare sind auch //-- und /*--[[ --]]*/ Weil Flasms Kommentare // und /* */ sind, aber ich verwende Luas Syntax-Hervorhebung für FLM (es ist gut genug!) Und Lua's sind -- und --[[ --]] .
var unterstützt (kein let oder const )if logische binäre Operatoren nicht unterstützt werden&& oder || ) ! Istclass wird nicht implementiert, verwenden Sie prototypbasierte Programmierung (erstellenint() anstelle von parseInt()var foo = parseInt(bar) do var foo = int (bange) do var foo = int(bar)int opcodefoo[bar] oder foo['bar'] oder foo[x + y + 1 + true + 0] ist in Ordnung, aber foo[bar++] oder foo[selector()] wird nicht kompiliert++variable , aber ++foo.bar oder ++foo[bar] nichtvariable++ oder variable++ wird nicht implementiert Der Compiler sucht nach einer Konfigurationsdatei mit dem Namen js-to-flasm.config.json im aktuellen Verzeichnis oder aus einem durch das an ihn übergebenen Argument angegebenen Pfad.
Z.B
js-to-flasm-as2-compiler ./config/js-to-flasm.config.json
Beispielkonfigurationsdatei:
{
"dist" : " dist/ " ,
"sourceRoot" : " samples/ "
} Es werden keine anderen Schlüssel unterstützt und beide sind erforderlich. Beide Dateipfade können alles sein, was das Knoten fs.readdir() versteht. Beenden / ist wahrscheinlich optional.
Compiler-Direktiven werden über Einzellinien-Kommentare implementiert. Anweisungen werden in Block -Kommentaren oder JSDOC -Kommentaren nicht gesucht. Eine Richtlinie ist von Form:
// directive-name
oder wenn es Argumente akzeptiert, ist es von Form:
// directive-with-args: arg1 arg2 arg3 argn
Aktuelle Richtlinien sind:
@js2f/push-register-context , @js2f/pop-register-context Ermöglicht Ihnen den Compiler über Register <--> variable Assoziationen. Wenn Sie beispielsweise Code kompilieren, der #include 'D in einer Funktion erhält, die Sie nicht kontrollieren (dh es handelt sich um den zerlegten Code), und Sie in der Funktion this zugreifen möchten, können Sie den Compiler anweisen, dass er sich in Register n.
Pop den Kontext, sobald Sie ihn nicht mehr brauchen.
Beispiel 1
// @js2f/push-register-context: r:1=this
enqueueSocketJob ( '{"type": "cash", "data":' + this . cash + "}" ) ;
// @js2f/pop-register-contextBeispiel 2
// @js2f/push-register-context: r:3=socket
socket . onConnect = function ( ) {
// can use local variables here
var foobar = 123 ;
} ;
// @js2f/pop-register-context MIT, siehe Lizenz.