/* * 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 #else Hash #end; var strings : Array; 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(); 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(); } }