404 lines
9.7 KiB
Haxe
404 lines
9.7 KiB
Haxe
/*
|
|
* Copyright (C)2008-2017 Haxe Foundation
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
package hscript;
|
|
import hscript.Expr;
|
|
|
|
class Bytes {
|
|
|
|
var bin : haxe.io.Bytes;
|
|
var bout : haxe.io.BytesBuffer;
|
|
var pin : Int;
|
|
var hstrings : #if haxe3 Map<String,Int> #else Hash<Int> #end;
|
|
var strings : Array<String>;
|
|
var nstrings : Int;
|
|
|
|
function new( ?bin ) {
|
|
this.bin = bin;
|
|
pin = 0;
|
|
bout = new haxe.io.BytesBuffer();
|
|
hstrings = #if haxe3 new Map() #else new Hash() #end;
|
|
strings = [null];
|
|
nstrings = 1;
|
|
}
|
|
|
|
function doEncodeString( v : String ) {
|
|
var vid = hstrings.get(v);
|
|
if( vid == null ) {
|
|
if( nstrings == 256 ) {
|
|
hstrings = #if haxe3 new Map() #else new Hash() #end;
|
|
nstrings = 1;
|
|
}
|
|
hstrings.set(v,nstrings);
|
|
bout.addByte(0);
|
|
var vb = haxe.io.Bytes.ofString(v);
|
|
bout.addByte(vb.length);
|
|
bout.add(vb);
|
|
nstrings++;
|
|
} else
|
|
bout.addByte(vid);
|
|
}
|
|
|
|
function doDecodeString() {
|
|
var id = bin.get(pin++);
|
|
if( id == 0 ) {
|
|
var len = bin.get(pin);
|
|
var str = #if (haxe_ver < 3.103) bin.readString(pin+1,len); #else bin.getString(pin+1,len); #end
|
|
pin += len + 1;
|
|
if( strings.length == 255 )
|
|
strings = [null];
|
|
strings.push(str);
|
|
return str;
|
|
}
|
|
return strings[id];
|
|
}
|
|
|
|
function doEncodeInt(v: Int) {
|
|
bout.addInt32(v);
|
|
}
|
|
|
|
function doEncodeConst( c : Const ) {
|
|
switch( c ) {
|
|
case CInt(v):
|
|
if( v >= 0 && v <= 255 ) {
|
|
bout.addByte(0);
|
|
bout.addByte(v);
|
|
} else {
|
|
bout.addByte(1);
|
|
doEncodeInt(v);
|
|
}
|
|
#if !haxe3
|
|
case CInt32(v):
|
|
bout.addByte(4);
|
|
var mid = haxe.Int32.toInt(haxe.Int32.and(v,haxe.Int32.ofInt(0xFFFFFF)));
|
|
bout.addByte(mid & 0xFF);
|
|
bout.addByte((mid >> 8) & 0xFF);
|
|
bout.addByte(mid >> 16);
|
|
bout.addByte(haxe.Int32.toInt(haxe.Int32.ushr(v, 24)));
|
|
#end
|
|
case CFloat(f):
|
|
bout.addByte(2);
|
|
doEncodeString(Std.string(f));
|
|
case CString(s):
|
|
bout.addByte(3);
|
|
doEncodeString(s);
|
|
}
|
|
}
|
|
|
|
function doDecodeInt() {
|
|
var i = bin.getInt32(pin);
|
|
pin += 4;
|
|
return i;
|
|
}
|
|
|
|
function doDecodeConst() {
|
|
return switch( bin.get(pin++) ) {
|
|
case 0:
|
|
CInt(bin.get(pin++));
|
|
case 1:
|
|
var i = doDecodeInt();
|
|
CInt(i);
|
|
case 2:
|
|
CFloat( Std.parseFloat(doDecodeString()) );
|
|
case 3:
|
|
CString( doDecodeString() );
|
|
#if !haxe3
|
|
case 4:
|
|
var i = bin.get(pin) | (bin.get(pin+1) << 8) | (bin.get(pin+2) << 16);
|
|
var j = bin.get(pin+3);
|
|
pin += 4;
|
|
CInt32(haxe.Int32.or(haxe.Int32.ofInt(i), haxe.Int32.shl(haxe.Int32.ofInt(j), 24)));
|
|
#end
|
|
default:
|
|
throw "Invalid code "+bin.get(pin-1);
|
|
}
|
|
}
|
|
|
|
function doEncode( e : Expr ) {
|
|
#if hscriptPos
|
|
doEncodeString(e.origin);
|
|
doEncodeInt(e.line);
|
|
var e = e.e;
|
|
#end
|
|
bout.addByte(Type.enumIndex(e));
|
|
switch( e ) {
|
|
case EImport(c):
|
|
// TODO
|
|
case EClass(_, _, _, _):
|
|
// TODO
|
|
case EConst(c):
|
|
doEncodeConst(c);
|
|
case EIdent(v):
|
|
doEncodeString(v);
|
|
case EVar(n,_,e):
|
|
doEncodeString(n);
|
|
if( e == null )
|
|
bout.addByte(255);
|
|
else
|
|
doEncode(e);
|
|
case EParent(e):
|
|
doEncode(e);
|
|
case EBlock(el):
|
|
bout.addByte(el.length);
|
|
for( e in el )
|
|
doEncode(e);
|
|
case EField(e,f):
|
|
doEncode(e);
|
|
doEncodeString(f);
|
|
case EBinop(op,e1,e2):
|
|
doEncodeString(op);
|
|
doEncode(e1);
|
|
doEncode(e2);
|
|
case EUnop(op,prefix,e):
|
|
doEncodeString(op);
|
|
bout.addByte(prefix?1:0);
|
|
doEncode(e);
|
|
case ECall(e,el):
|
|
doEncode(e);
|
|
bout.addByte(el.length);
|
|
for( e in el )
|
|
doEncode(e);
|
|
case EIf(cond,e1,e2):
|
|
doEncode(cond);
|
|
doEncode(e1);
|
|
if( e2 == null )
|
|
bout.addByte(255);
|
|
else
|
|
doEncode(e2);
|
|
case EWhile(cond,e):
|
|
doEncode(cond);
|
|
doEncode(e);
|
|
case EDoWhile(cond,e):
|
|
doEncode(cond);
|
|
doEncode(e);
|
|
case EFor(v,it,e):
|
|
doEncodeString(v);
|
|
doEncode(it);
|
|
doEncode(e);
|
|
case EBreak, EContinue:
|
|
case EFunction(params,e,name,_):
|
|
bout.addByte(params.length);
|
|
for( p in params )
|
|
doEncodeString(p.name);
|
|
doEncode(e);
|
|
doEncodeString(name == null?"":name);
|
|
case EReturn(e):
|
|
if( e == null )
|
|
bout.addByte(255);
|
|
else
|
|
doEncode(e);
|
|
case EArray(e,index):
|
|
doEncode(e);
|
|
doEncode(index);
|
|
case EArrayDecl(el):
|
|
if( el.length >= 255 ) throw "assert";
|
|
bout.addByte(el.length);
|
|
for( e in el )
|
|
doEncode(e);
|
|
case ENew(cl,params):
|
|
doEncodeString(cl);
|
|
bout.addByte(params.length);
|
|
for( e in params )
|
|
doEncode(e);
|
|
case EThrow(e):
|
|
doEncode(e);
|
|
case ETry(e,v,_,ecatch):
|
|
doEncode(e);
|
|
doEncodeString(v);
|
|
doEncode(ecatch);
|
|
case EObject(fl):
|
|
bout.addByte(fl.length);
|
|
for( f in fl ) {
|
|
doEncodeString(f.name);
|
|
doEncode(f.e);
|
|
}
|
|
case ETernary(cond, e1, e2):
|
|
doEncode(cond);
|
|
doEncode(e1);
|
|
doEncode(e2);
|
|
case ESwitch(e, cases, def):
|
|
doEncode(e);
|
|
for( c in cases ) {
|
|
if( c.values.length == 0 ) throw "assert";
|
|
for( v in c.values )
|
|
doEncode(v);
|
|
bout.addByte(255);
|
|
doEncode(c.expr);
|
|
}
|
|
bout.addByte(255);
|
|
if( def == null ) bout.addByte(255) else doEncode(def);
|
|
case EMeta(name,args,e):
|
|
doEncodeString(name);
|
|
bout.addByte(args == null ? 0 : args.length + 1);
|
|
if( args != null ) for( e in args ) doEncode(e);
|
|
doEncode(e);
|
|
case ECheckType(e,_):
|
|
doEncode(e);
|
|
}
|
|
}
|
|
|
|
function doDecode() : Expr {
|
|
#if hscriptPos
|
|
if (bin.get(pin) == 255) {
|
|
pin++;
|
|
return null;
|
|
}
|
|
var origin = doDecodeString();
|
|
var line = doDecodeInt();
|
|
return { e : _doDecode(), pmin : 0, pmax : 0, origin : origin, line : line };
|
|
}
|
|
function _doDecode() : ExprDef {
|
|
#end
|
|
return switch( bin.get(pin++) ) {
|
|
case 0:
|
|
EConst( doDecodeConst() );
|
|
case 1:
|
|
EIdent( doDecodeString() );
|
|
case 2:
|
|
var v = doDecodeString();
|
|
EVar(v,doDecode());
|
|
case 3:
|
|
EParent(doDecode());
|
|
case 4:
|
|
var a = new Array();
|
|
for( i in 0...bin.get(pin++) )
|
|
a.push(doDecode());
|
|
EBlock(a);
|
|
case 5:
|
|
var e = doDecode();
|
|
EField(e,doDecodeString());
|
|
case 6:
|
|
var op = doDecodeString();
|
|
var e1 = doDecode();
|
|
EBinop(op,e1,doDecode());
|
|
case 7:
|
|
var op = doDecodeString();
|
|
var prefix = bin.get(pin++) != 0;
|
|
EUnop(op,prefix,doDecode());
|
|
case 8:
|
|
var e = doDecode();
|
|
var params = new Array();
|
|
for( i in 0...bin.get(pin++) )
|
|
params.push(doDecode());
|
|
ECall(e,params);
|
|
case 9:
|
|
var cond = doDecode();
|
|
var e1 = doDecode();
|
|
EIf(cond,e1,doDecode());
|
|
case 10:
|
|
var cond = doDecode();
|
|
EWhile(cond,doDecode());
|
|
case 11:
|
|
var v = doDecodeString();
|
|
var it = doDecode();
|
|
EFor(v,it,doDecode());
|
|
case 12:
|
|
EBreak;
|
|
case 13:
|
|
EContinue;
|
|
case 14:
|
|
var params = new Array<Argument>();
|
|
for( i in 0...bin.get(pin++) )
|
|
params.push({ name : doDecodeString() });
|
|
var e = doDecode();
|
|
var name = doDecodeString();
|
|
EFunction(params,e,(name == "") ? null: name);
|
|
case 15:
|
|
EReturn(doDecode());
|
|
case 16:
|
|
var e = doDecode();
|
|
EArray(e,doDecode());
|
|
case 17:
|
|
var el = new Array();
|
|
for( i in 0...bin.get(pin++) )
|
|
el.push(doDecode());
|
|
EArrayDecl(el);
|
|
case 18:
|
|
var cl = doDecodeString();
|
|
var el = new Array();
|
|
for( i in 0...bin.get(pin++) )
|
|
el.push(doDecode());
|
|
ENew(cl,el);
|
|
case 19:
|
|
EThrow(doDecode());
|
|
case 20:
|
|
var e = doDecode();
|
|
var v = doDecodeString();
|
|
ETry(e,v,null,doDecode());
|
|
case 21:
|
|
var fl = new Array();
|
|
for( i in 0...bin.get(pin++) ) {
|
|
var name = doDecodeString();
|
|
var e = doDecode();
|
|
fl.push({ name : name, e : e });
|
|
}
|
|
EObject(fl);
|
|
case 22:
|
|
var cond = doDecode();
|
|
var e1 = doDecode();
|
|
var e2 = doDecode();
|
|
ETernary(cond, e1, e2);
|
|
case 23:
|
|
var e = doDecode();
|
|
var cases = [];
|
|
while( true ) {
|
|
var v = doDecode();
|
|
if( v == null ) break;
|
|
var values = [v];
|
|
while( true ) {
|
|
v = doDecode();
|
|
if( v == null ) break;
|
|
values.push(v);
|
|
}
|
|
cases.push( { values : values, expr : doDecode() } );
|
|
}
|
|
var def = doDecode();
|
|
ESwitch(e, cases, def);
|
|
case 24:
|
|
var cond = doDecode();
|
|
EDoWhile(cond,doDecode());
|
|
case 25:
|
|
var name = doDecodeString();
|
|
var count = bin.get(pin++);
|
|
var args = count == 0 ? null : [for( i in 0...count - 1 ) doDecode()];
|
|
EMeta(name, args, doDecode());
|
|
case 26:
|
|
ECheckType(doDecode(), CTPath(["Void"]));
|
|
case 255:
|
|
null;
|
|
default:
|
|
throw "Invalid code "+bin.get(pin - 1);
|
|
}
|
|
}
|
|
|
|
public static function encode( e : Expr ) : haxe.io.Bytes {
|
|
var b = new Bytes();
|
|
b.doEncode(e);
|
|
return b.bout.getBytes();
|
|
}
|
|
|
|
public static function decode( bytes : haxe.io.Bytes ) : Expr {
|
|
var b = new Bytes(bytes);
|
|
return b.doDecode();
|
|
}
|
|
|
|
}
|