content: /*******************************************************************************
  * Copyright (c) 2009 Luaj.org. All rights reserved.
  *
  * 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 org.luaj.vm2;
  
  import java.io.DataInputStream;
  import java.io.IOException;
  import java.io.InputStream;
  
  import org.luaj.vm2.compiler.DumpState;
  
  
  /**
  * Class to undump compiled lua bytecode into a {@link Prototype} instances.
  * <p>
  * The {@link LoadState} class implements {@link Globals#Undumper}
  * which is used to undump a string of bytes that represent a lua binary file
  * using either the C-based lua compiler, or luaj's {@link DumpState#dump} function.
  * <p>
  * The canonical method to load and execute code is done 
  * indirectly using the Globals:
  * <pre> {@code
  * Globals globals = JsePlatform.standardGlobals();
  * LuaValue chunk = globasl.load("print('hello, world')", "main.lua");
  * chunk.call();
  * } </pre>
  * This should work regardless of which {@link Globals.Compiler}
  * has been installed.
  * <p>
  * By default, when using {@link JsePlatform} or {@JmePlatform}
  * to construct globals, the {@link LoadState} is installed
  * as the default {@link Globals#undumper}. 
  * <p>
  * 
  * A lua binary file is created via {@link DumpState#dump}:
  * <pre> {@code
  * Globals globals = JsePlatform.standardGlobals();
  * Prototype p = globals.compilePrototype(new StringReader("print('hello, world')"), "main.lua");
  * ByteArrayOutputStream o = new ByteArrayOutputStream();
  * DumpState.dump(p, o, false);
  * byte[] lua_binary_file_bytes = o.toByteArray();
  * } </pre>
  * 
  * The {@link LoadState} may be used directly to undump these bytes:
  * <pre> {@code
  * Prototypep = LoadState.instance.undump(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua");
  * LuaClosure c = new LuaClosure(p, globals);
  * c.call();
  * } </pre>
  * 
  * 
  * More commonly, the {@link Globals#undumper} may be used to undump them:
  * <pre> {@code
  * Prototype p = globals.loadPrototype(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua", "b");
  * LuaClosure c = new LuaClosure(p, globals);
  * c.call();
  * } </pre>
  * 
  * @see LuaCompiler
  * @see LuaClosure
  * @see LuaFunction
  * @see LoadState#compiler
  * @see LoadState#load(InputStream, String, LuaValue)
  * @see LuaC
  * @see LuaJC
  */
  public class LoadState implements Globals.Undumper {
  
  	/** Shared instance of Globals.Undumper to use loading prototypes from binary lua files */
  	public static final Globals.Undumper instance = new LoadState();
  	
  	/** format corresponding to non-number-patched lua, all numbers are floats or doubles */
  	public static final int NUMBER_FORMAT_FLOATS_OR_DOUBLES    = 0;
  
  	/** format corresponding to non-number-patched lua, all numbers are ints */
  	public static final int NUMBER_FORMAT_INTS_ONLY            = 1;
  	
  	/** format corresponding to number-patched lua, all numbers are 32-bit (4 byte) ints */
  	public static final int NUMBER_FORMAT_NUM_PATCH_INT32      = 4;
  	
  	// type constants	
  	public static final int LUA_TINT            = (-2);
  	public static final int LUA_TNONE			= (-1);
  	public static final int LUA_TNIL			= 0;
  	public static final int LUA_TBOOLEAN		= 1;
  	public static final int LUA_TLIGHTUSERDATA	= 2;
  	public static final int LUA_TNUMBER			= 3;
  	public static final int LUA_TSTRING			= 4;
  	public static final int LUA_TTABLE			= 5;
  	public static final int LUA_TFUNCTION		= 6;
  	public static final int LUA_TUSERDATA		= 7;
  	public static final int LUA_TTHREAD			= 8;
  	public static final int LUA_TVALUE          = 9;
  	
  	/** The character encoding to use for file encoding.  Null means the default encoding */
  	public static String encoding = null;
  	
  	/** Signature byte indicating the file is a compiled binary chunk */
  	public static final byte[] LUA_SIGNATURE	= { '\033', 'L', 'u', 'a' };
  
  	/** Data to catch conversion errors */
  	public static final byte[] LUAC_TAIL = { (byte) 0x19, (byte) 0x93, '\r', '\n', (byte) 0x1a, '\n', };
  	
  
  	/** Name for compiled chunks */
  	public static final String SOURCE_BINARY_STRING = "binary string";
  
  
  	/** for header of binary files -- this is Lua 5.2 */
  	public static final int LUAC_VERSION		= 0x52;
  
  	/** for header of binary files -- this is the official format */
  	public static final int LUAC_FORMAT		= 0;
  
  	/** size of header of binary files */
  	public static final int LUAC_HEADERSIZE		= 12;
  
  	// values read from the header
  	private int     luacVersion;
  	private int     luacFormat;
  	private boolean luacLittleEndian;
  	private int     luacSizeofInt;
  	private int     luacSizeofSizeT;
  	private int     luacSizeofInstruction;
  	private int     luacSizeofLuaNumber;
  	private int 	luacNumberFormat;
  
  	/** input stream from which we are loading */
  	public final DataInputStream is;
  
  	/** Name of what is being loaded? */
  	String name;
  
  	private static final LuaValue[]     NOVALUES    = {};
  	private static final Prototype[] NOPROTOS    = {};
  	private static final LocVars[]   NOLOCVARS   = {};
  	private static final LuaString[]  NOSTRVALUES = {};
  	private static final Upvaldesc[]  NOUPVALDESCS = {};
  	private static final int[]       NOINTS      = {};
  	
  	/** Read buffer */
  	private byte[] buf = new byte[512];
  
  	/** Install this class as the standard Globals.Undumper for the supplied Globals */
  	public static void install(Globals globals) {
  		globals.undumper = instance;
  	}
  	
  	/** Load a 4-byte int value from the input stream
  	 * @return the int value laoded.  
  	 **/
  	int loadInt() throws IOException {
  		is.readFully(buf,0,4);
  		return luacLittleEndian? 
  				(buf[3] << 24) | ((0xff & buf[2]) << 16) | ((0xff & buf[1]) << 8) | (0xff & buf[0]):
  				(buf[0] << 24) | ((0xff & buf[1]) << 16) | ((0xff & buf[2]) << 8) | (0xff & buf[3]);
  	}
  	
  	/** Load an array of int values from the input stream
  	 * @return the array of int values laoded.  
  	 **/
  	int[] loadIntArray() throws IOException {
  		int n = loadInt();
  		if ( n == 0 )
  			return NOINTS;
  		
  		// read all data at once
  		int m = n << 2;
  		if ( buf.length < m )
  			buf = new byte[m];
  		is.readFully(buf,0,m);
  		int[] array = new int[n];
  		for ( int i=0, j=0; i<n; ++i, j+=4 )
  			array[i] = luacLittleEndian? 
  					(buf[j+3] << 24) | ((0xff & buf[j+2]) << 16) | ((0xff & buf[j+1]) << 8) | (0xff & buf[j+0]):
  					(buf[j+0] << 24) | ((0xff & buf[j+1]) << 16) | ((0xff & buf[j+2]) << 8) | (0xff & buf[j+3]);
  
  		return array;
  	}
  	
  	/** Load a long  value from the input stream
  	 * @return the long value laoded.  
  	 **/
  	long loadInt64() throws IOException {
  		int a,b;
  		if ( this.luacLittleEndian ) {
  			a = loadInt();
  			b = loadInt();
  		} else {
  			b = loadInt();
  			a = loadInt();
  		}
  		return (((long)b)<<32) | (((long)a)&0xffffffffL);
  	}
  
  	/** Load a lua strin gvalue from the input stream
  	 * @return the {@link LuaString} value laoded.  
  	 **/
  	LuaString loadString() throws IOException {
  		int size = this.luacSizeofSizeT == 8? (int) loadInt64(): loadInt();
  		if ( size == 0 )
  			return null;
  		byte[] bytes = new byte[size];
  		is.readFully( bytes, 0, size );
  		return LuaString.valueOf( bytes, 0, bytes.length - 1 );
  	}
  	
  	/**
  	 * Convert bits in a long value to a {@link LuaValue}. 
  	 * @param bits long value containing the bits
  	 * @return {@link LuaInteger} or {@link LuaDouble} whose value corresponds to the bits provided.
  	 */
  	public static LuaValue longBitsToLuaNumber( long bits ) {
  		if ( ( bits & ( ( 1L << 63 ) - 1 ) ) == 0L ) {
  			return LuaValue.ZERO;
  		}
  		
  		int e = (int)((bits >> 52) & 0x7ffL) - 1023;
  		
  		if ( e >= 0 && e < 31 ) {
  			long f = bits & 0xFFFFFFFFFFFFFL;
  			int shift = 52 - e;
  			long intPrecMask = ( 1L << shift ) - 1;
  			if ( ( f & intPrecMask ) == 0 ) {
  				int intValue = (int)( f >> shift ) | ( 1 << e );
  				return LuaInteger.valueOf( ( ( bits >> 63 ) != 0 ) ? -intValue : intValue );
  			}
  		}
  		
  		return LuaValue.valueOf( Double.longBitsToDouble(bits) );
  	}
  	
  	/** 
  	 * Load a number from a binary chunk
  	 * @return the {@link LuaValue} loaded
  	 * @throws IOException if an i/o exception occurs
  	 */
  	LuaValue loadNumber() throws IOException {
  		if ( luacNumberFormat == NUMBER_FORMAT_INTS_ONLY ) {
  			return LuaInteger.valueOf( loadInt() );
  		} else {
  			return longBitsToLuaNumber( loadInt64() );
  		}
  	}
  
  	/**
  	 * Load a list of constants from a binary chunk
  	 * @param f the function prototype
  	 * @throws IOException if an i/o exception occurs
  	 */
  	void loadConstants(Prototype f) throws IOException {
  		int n = loadInt();
  		LuaValue[] values = n>0? new LuaValue[n]: NOVALUES;
  		for ( int i=0; i<n; i++ ) {
  			switch ( is.readByte() ) {
  			case LUA_TNIL:
  				values[i] = LuaValue.NIL;
  				break;
  			case LUA_TBOOLEAN:
  				values[i] = (0 != is.readUnsignedByte()? LuaValue.TRUE: LuaValue.FALSE);
  				break;
  			case LUA_TINT:
  				values[i] = LuaInteger.valueOf( loadInt() );
  				break;
  			case LUA_TNUMBER:
  				values[i] = loadNumber();
  				break;
  			case LUA_TSTRING:
  				values[i] = loadString();
  				break;
  			default:
  				throw new IllegalStateException("bad constant");
  			}
  		}
  		f.k = values;
  		
  		n = loadInt();
  		Prototype[] protos = n>0? new Prototype[n]: NOPROTOS;
  		for ( int i=0; i<n; i++ )
  			protos[i] = loadFunction(f.source);
  		f.p = protos;
  	}
  
  
  	void loadUpvalues(Prototype f) throws IOException {
  		int n = loadInt();
  		f.upvalues = n>0? new Upvaldesc[n]: NOUPVALDESCS;
  		for (int i=0; i<n; i++) {
  			boolean instack = is.readByte() != 0;
  			int idx = ((int) is.readByte()) & 0xff;
  			f.upvalues[i] = new Upvaldesc(null, instack, idx);
  		}
  	}
  
  	/**
  	 * Load the debug info for a function prototype
  	 * @param f the function Prototype
  	 * @throws IOException if there is an i/o exception
  	 */
  	void loadDebug( Prototype f ) throws IOException {
  		f.source = loadString();
  		f.lineinfo = loadIntArray();
  		int n = loadInt();
  		f.locvars = n>0? new LocVars[n]: NOLOCVARS;
  		for ( int i=0; i<n; i++ ) {
  			LuaString varname = loadString();
  			int startpc = loadInt();
  			int endpc = loadInt();
  			f.locvars[i] = new LocVars(varname, startpc, endpc);
  		}
  		
  		n = loadInt();
  		for ( int i=0; i<n; i++ )
  			f.upvalues[i].name = loadString();
  	}
  
  	/** 
  	 * Load a function prototype from the input stream
  	 * @param p name of the source
  	 * @return {@link Prototype} instance that was loaded
  	 * @throws IOException
  	 */
  	public Prototype loadFunction(LuaString p) throws IOException {
  		Prototype f = new Prototype();
  ////		this.L.push(f);
  //		f.source = loadString();
  //		if ( f.source == null )
  //			f.source = p;
  		f.linedefined = loadInt();
  		f.lastlinedefined = loadInt();
  		f.numparams = is.readUnsignedByte();
  		f.is_vararg = is.readUnsignedByte();
  		f.maxstacksize = is.readUnsignedByte();
  		f.code = loadIntArray();
  		loadConstants(f);
  		loadUpvalues(f);
  		loadDebug(f);
  		
  		// TODO: add check here, for debugging purposes, I believe
  		// see ldebug.c
  //		 IF (!luaG_checkcode(f), "bad code");
  		
  //		 this.L.pop();
  		 return f;
  	}
  
  	/**
  	 * Load the lua chunk header values. 
  	 * @throws IOException if an i/o exception occurs. 
  	 */
  	public void loadHeader() throws IOException {
  		luacVersion = is.readByte();
  		luacFormat = is.readByte();
  		luacLittleEndian = (0 != is.readByte());
  		luacSizeofInt = is.readByte();
  		luacSizeofSizeT = is.readByte();
  		luacSizeofInstruction = is.readByte();
  		luacSizeofLuaNumber = is.readByte();
  		luacNumberFormat = is.readByte();
  		for (int i=0; i < LUAC_TAIL.length; ++i)
  			if (is.readByte() != LUAC_TAIL[i])
  				throw new LuaError("Unexpeted byte in luac tail of header, index="+i);
  	}
  
  	/**
  	 * Load input stream as a lua binary chunk if the first 4 bytes are the lua binary signature.
  	 * @param stream InputStream to read, after having read the first byte already
  	 * @param name Name to apply to the loaded chunk
  	 * @return {@link Prototype} that was loaded, or null if the first 4 bytes were not the lua signature.
  	 * @throws IOException if an IOException occurs
  	 */
  	public Prototype undump(InputStream stream, String chunkname) throws IOException {
  		// check rest of signature
  		if ( stream.read() != LUA_SIGNATURE[0] 
  		   || stream.read() != LUA_SIGNATURE[1]
  	       || stream.read() != LUA_SIGNATURE[2]
  		   || stream.read() != LUA_SIGNATURE[3] )
  			return null;
  		
  		// load file as a compiled chunk
  		String sname = getSourceName(chunkname);
  		LoadState s = new LoadState( stream, sname );
  		s.loadHeader();
  
  		// check format
  		switch ( s.luacNumberFormat ) {
  		case NUMBER_FORMAT_FLOATS_OR_DOUBLES:
  		case NUMBER_FORMAT_INTS_ONLY:
  		case NUMBER_FORMAT_NUM_PATCH_INT32:
  			break;
  		default:
  			throw new LuaError("unsupported int size");
  		}
  		return s.loadFunction( LuaString.valueOf(sname) );
  	}
  	
  	/**
  	 * Construct a source name from a supplied chunk name
  	 * @param name String name that appears in the chunk
  	 * @return source file name
  	 */
      public static String getSourceName(String name) {
          String sname = name;
          if ( name.startsWith("@") || name.startsWith("=") )
  			sname = name.substring(1);
  		else if ( name.startsWith("\033") )
  			sname = SOURCE_BINARY_STRING;
          return sname;
      }
  
  	/** Private constructor for create a load state */
  	private LoadState( InputStream stream, String name ) {
  		this.name = name;
  		this.is = new DataInputStream( stream );
  	}
  	
  	private LoadState() {
  		this.name = "";
  		this.is = null;
  	}
  }
  