编译JavaScript或AS2的(一个子集)到ActionScript 2(Flash Player 8兼容)字节码组件可通过FLASM编译。极度wip。
我正在修改一个旧的Flash游戏,我厌倦了手工编写ActionScript字节码。幸运的是,ActionScript 2与JavaScript极为相似(因为AS2部分符合Ecmascript 4 Spec),因此我可以使用@babel/parser将我的源作为JavaScript将我的源解析为AST,然后从AST中发出字节码。
编译器仅支持我需要的JS/AS2功能的子集,该项目保留屠宰该语言语义的权利,以使其更易于实现。只有当我找到要实施的功能以使自己的生活更轻松时,开发也会进步。
编译器还模拟堆栈以记录当前堆栈作为每行右侧的注释,以使手动验证更容易。如果达到分支或支枝opcode(IF-ELSE或循环),则模拟器跳过函数/上下文的其余部分。内部功能是用自己的堆栈模拟的。代码语句也将作为注释添加到该语句的字节码之前。
@js2f/push-register-context , @js2f/pop-register-context当前正在进行的工作样本来源:
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 ( ) ;发射:
// -- 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> 克隆回购,并运行npm i 。然后,运行npm start ,或将其作为本地相对NPM模块安装,并将其作为js-to-flasm-as2-compiler在package.json中运行。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.flm如果您在Linux上,您可能只能
chmod u+x bin/index.js
ln -s $PWD /bin/index.js ./js-to-flasm-as2-compiler
./js-to-flasm-as2-compiler编译器将在samples/中编译JS源文件,并发出输出到dist/ DIRECTORY。
如果您使用我尚未实现的JS功能,则编译器会崩溃。
评论也为//--和/*--[[ --]]*/因为Flasm的评论为//和/* */ ,但是我使用lua的语法突出显示了FLM(足够好!),而lua则是--和--[[ --]] 。
var (没有let或const )if不支持逻辑二进制操作员&&或|| ) !是class未实现,而是使用基于原型的编程(制作一个构造函数,并将函数分配给ctor.prototype)int()而不是parseInt()var foo = parseInt(bar) do var foo = int(bar)int OPCODE中foo[bar]或foo['bar']或foo[x + y + 1 + true + 0]可以,但是foo[bar++]或foo[selector()]将无法编译++variable有效,但是++foo.bar或++foo[bar]不variable++或variable++未实现编译器在当前目录中搜索名为js-to-flasm.config.json的配置文件,或从传递给它的第一个参数指定的路径中。
例如
js-to-flasm-as2-compiler ./config/js-to-flasm.config.json
示例配置文件:
{
"dist" : " dist/ " ,
"sourceRoot" : " samples/ "
}不支持其他密钥,并且都需要两者。两个文件路径都可以是Node fs.readdir()理解的任何内容。终止/可能是可选的。
编译器指令通过单线注释实施。指令未搜索内部块注释或JSDOC评论。指令是形式:
// directive-name
或者,如果接受参数,则是形式:
// directive-with-args: arg1 arg2 arg3 argn
当前指令是:
@js2f/push-register-context , @js2f/pop-register-context允许您告诉编译器有关寄存器<->变量关联。例如,如果您要编译的代码在您无法控制的函数中获得#include include'd(即它是拆卸的代码),并且您想在函数内访问this函数,则可以指示编译器在寄存器n中。
一旦您不再需要上下文,就可以弹出上下文。
示例1
// @js2f/push-register-context: r:1=this
enqueueSocketJob ( '{"type": "cash", "data":' + this . cash + "}" ) ;
// @js2f/pop-register-context示例2
// @js2f/push-register-context: r:3=socket
socket . onConnect = function ( ) {
// can use local variables here
var foobar = 123 ;
} ;
// @js2f/pop-register-context 麻省理工学院,请参阅许可证。