version 0.50 optional dump and load chunks using number-patch format extensions.

This commit is contained in:
James Roseborough
2008-08-21 21:57:52 +00:00
parent 83eedc10a5
commit 9fe29f4d15
6 changed files with 177 additions and 68 deletions

View File

@@ -25,13 +25,12 @@ import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import org.luaj.vm.LBoolean;
import org.luaj.vm.LNil;
import org.luaj.vm.LNumber; import org.luaj.vm.LNumber;
import org.luaj.vm.LPrototype;
import org.luaj.vm.LString; import org.luaj.vm.LString;
import org.luaj.vm.LValue; import org.luaj.vm.LValue;
import org.luaj.vm.LocVars;
import org.luaj.vm.Lua; import org.luaj.vm.Lua;
import org.luaj.vm.LPrototype;
public class DumpState { public class DumpState {
@@ -54,9 +53,21 @@ public class DumpState {
/** set true to allow integer compilation */ /** set true to allow integer compilation */
public static boolean ALLOW_INTEGER_CASTING = false; public static boolean ALLOW_INTEGER_CASTING = false;
/** 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;
/** default number format */
public static final int NUMBER_FORMAT_DEFAULT = NUMBER_FORMAT_FLOATS_OR_DOUBLES;
// header fields // header fields
private boolean IS_LITTLE_ENDIAN = false; private boolean IS_LITTLE_ENDIAN = false;
private boolean IS_NUMBER_INTEGRAL = false; private int NUMBER_FORMAT = NUMBER_FORMAT_DEFAULT;
private int SIZEOF_LUA_NUMBER = 8; private int SIZEOF_LUA_NUMBER = 8;
private static final int SIZEOF_INT = 4; private static final int SIZEOF_INT = 4;
private static final int SIZEOF_SIZET = 4; private static final int SIZEOF_SIZET = 4;
@@ -98,49 +109,69 @@ public class DumpState {
writer.write( 0 ); writer.write( 0 );
} }
void dumpNumber(double d) throws IOException { void dumpDouble(double d) throws IOException {
if ( IS_NUMBER_INTEGRAL ) { long l = Double.doubleToLongBits(d);
int i = (int) d; if ( IS_LITTLE_ENDIAN ) {
if ( (! ALLOW_INTEGER_CASTING) && (i != d) ) dumpInt( (int) l );
throw new java.lang.IllegalArgumentException("not an integer: "+d); dumpInt( (int) (l>>32) );
dumpInt( i );
} else { } else {
long l = Double.doubleToLongBits(d); writer.writeLong(l);
if ( IS_LITTLE_ENDIAN ) {
dumpInt( (int) l );
dumpInt( (int) (l>>32) );
} else {
writer.writeLong(l);
}
} }
} }
void dumpCode( final LPrototype f ) throws IOException { void dumpCode( final LPrototype f ) throws IOException {
int n = f.code.length; final int[] code = f.code;
int n = code.length;
dumpInt( n ); dumpInt( n );
for ( int i=0; i<n; i++ ) for ( int i=0; i<n; i++ )
dumpInt( f.code[i] ); dumpInt( code[i] );
} }
void dumpConstants(final LPrototype f) throws IOException { void dumpConstants(final LPrototype f) throws IOException {
int i, n = f.k.length; final LValue[] k = f.k;
int i, n = k.length;
dumpInt(n); dumpInt(n);
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
final LValue o = f.k[i]; final LValue o = k[i];
if (o.isNil()) { switch ( o.luaGetType() ) {
case Lua.LUA_TNIL:
writer.write(Lua.LUA_TNIL); writer.write(Lua.LUA_TNIL);
// do nothing more break;
} else if (o instanceof LBoolean) { case Lua.LUA_TBOOLEAN:
writer.write(Lua.LUA_TBOOLEAN); writer.write(Lua.LUA_TBOOLEAN);
dumpChar(o.toJavaBoolean() ? 1 : 0); dumpChar(o.toJavaBoolean() ? 1 : 0);
} else if (o instanceof LNumber) { break;
writer.write(Lua.LUA_TNUMBER); case Lua.LUA_TNUMBER:
dumpNumber(o.toJavaDouble()); switch (NUMBER_FORMAT) {
} else if (o instanceof LString) { case NUMBER_FORMAT_FLOATS_OR_DOUBLES:
writer.write(Lua.LUA_TNUMBER);
dumpDouble(o.toJavaDouble());
break;
case NUMBER_FORMAT_INTS_ONLY:
if ( ! ALLOW_INTEGER_CASTING && ! o.isInteger() )
throw new java.lang.IllegalArgumentException("not an integer: "+o);
writer.write(Lua.LUA_TNUMBER);
dumpInt(o.toJavaInt());
break;
case NUMBER_FORMAT_NUM_PATCH_INT32:
if ( o.isInteger() ) {
writer.write(Lua.LUA_TINT);
dumpInt(o.toJavaInt());
} else {
writer.write(Lua.LUA_TNUMBER);
dumpDouble(o.toJavaDouble());
}
break;
default:
throw new IllegalArgumentException("number format not supported: "+NUMBER_FORMAT);
}
break;
case Lua.LUA_TSTRING:
writer.write(Lua.LUA_TSTRING); writer.write(Lua.LUA_TSTRING);
dumpString((LString) o); dumpString(o.luaAsString());
} else { break;
throw new IllegalArgumentException("bad type for " + o); default:
throw new IllegalArgumentException("bad type for " + o);
} }
} }
n = f.p.length; n = f.p.length;
@@ -158,9 +189,10 @@ public class DumpState {
n = (strip) ? 0 : f.locvars.length; n = (strip) ? 0 : f.locvars.length;
dumpInt(n); dumpInt(n);
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
dumpString(f.locvars[i].varname); LocVars lvi = f.locvars[i];
dumpInt(f.locvars[i].startpc); dumpString(lvi.varname);
dumpInt(f.locvars[i].endpc); dumpInt(lvi.startpc);
dumpInt(lvi.endpc);
} }
n = (strip) ? 0 : f.upvalues.length; n = (strip) ? 0 : f.upvalues.length;
dumpInt(n); dumpInt(n);
@@ -193,7 +225,7 @@ public class DumpState {
writer.write( SIZEOF_SIZET ); writer.write( SIZEOF_SIZET );
writer.write( SIZEOF_INSTRUCTION ); writer.write( SIZEOF_INSTRUCTION );
writer.write( SIZEOF_LUA_NUMBER ); writer.write( SIZEOF_LUA_NUMBER );
writer.write( IS_NUMBER_INTEGRAL? 1: 0 ); writer.write( NUMBER_FORMAT );
} }
/* /*
@@ -206,11 +238,30 @@ public class DumpState {
return D.status; return D.status;
} }
public static int dump(LPrototype f, OutputStream w, boolean strip, boolean intonly, boolean littleendian) throws IOException { /**
DumpState D = new DumpState(w,strip); *
* @param f the function to dump
* @param w the output stream to dump to
* @param stripDebug true to strip debugging info, false otherwise
* @param numberFormat one of NUMBER_FORMAT_FLOATS_OR_DOUBLES, NUMBER_FORMAT_INTS_ONLY, NUMBER_FORMAT_NUM_PATCH_INT32
* @param littleendian true to use little endian for numbers, false for big endian
* @return 0 if dump succeeds
* @throws IOException
* @throws IllegalArgumentException if the number format it not supported
*/
public static int dump(LPrototype f, OutputStream w, boolean stripDebug, int numberFormat, boolean littleendian) throws IOException {
switch ( numberFormat ) {
case NUMBER_FORMAT_FLOATS_OR_DOUBLES:
case NUMBER_FORMAT_INTS_ONLY:
case NUMBER_FORMAT_NUM_PATCH_INT32:
break;
default:
throw new IllegalArgumentException("number format not supported: "+numberFormat);
}
DumpState D = new DumpState(w,stripDebug);
D.IS_LITTLE_ENDIAN = littleendian; D.IS_LITTLE_ENDIAN = littleendian;
D.IS_NUMBER_INTEGRAL = intonly; D.NUMBER_FORMAT = numberFormat;
D.SIZEOF_LUA_NUMBER = (intonly? 4: 8); D.SIZEOF_LUA_NUMBER = (numberFormat==NUMBER_FORMAT_INTS_ONLY? 4: 8);
D.dumpHeader(); D.dumpHeader();
D.dumpFunction(f,null); D.dumpFunction(f,null);
return D.status; return D.status;

View File

@@ -25,6 +25,8 @@ import java.io.DataInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import org.luaj.compiler.DumpState;
@@ -65,7 +67,7 @@ public class LoadState {
private int luacSizeofSizeT; private int luacSizeofSizeT;
private int luacSizeofInstruction; private int luacSizeofInstruction;
private int luacSizeofLuaNumber; private int luacSizeofLuaNumber;
private boolean luacIsNumberIntegral; private int luacNumberFormat;
/** input stream from which we are loading */ /** input stream from which we are loading */
private DataInputStream is; private DataInputStream is;
@@ -151,9 +153,8 @@ public class LoadState {
} }
LNumber loadNumber() throws IOException { LNumber loadNumber() throws IOException {
if ( this.luacIsNumberIntegral ) { if ( luacNumberFormat == DumpState.NUMBER_FORMAT_INTS_ONLY ) {
int value = loadInt(); return LInteger.valueOf( loadInt() );
return LInteger.valueOf( value );
} else { } else {
return longBitsToLuaNumber( loadInt64() ); return longBitsToLuaNumber( loadInt64() );
} }
@@ -163,13 +164,16 @@ public class LoadState {
int n = loadInt(); int n = loadInt();
LValue[] values = new LValue[n]; LValue[] values = new LValue[n];
for ( int i=0; i<n; i++ ) { for ( int i=0; i<n; i++ ) {
switch ( is.readUnsignedByte() ) { switch ( is.readByte() ) {
case Lua.LUA_TNIL: case Lua.LUA_TNIL:
values[i] = LNil.NIL; values[i] = LNil.NIL;
break; break;
case Lua.LUA_TBOOLEAN: case Lua.LUA_TBOOLEAN:
values[i] = (0 != is.readUnsignedByte()? LBoolean.TRUE: LBoolean.FALSE); values[i] = (0 != is.readUnsignedByte()? LBoolean.TRUE: LBoolean.FALSE);
break; break;
case Lua.LUA_TINT:
values[i] = LInteger.valueOf( loadInt() );
break;
case Lua.LUA_TNUMBER: case Lua.LUA_TNUMBER:
values[i] = loadNumber(); values[i] = loadNumber();
break; break;
@@ -239,7 +243,7 @@ public class LoadState {
luacSizeofSizeT = is.readByte(); luacSizeofSizeT = is.readByte();
luacSizeofInstruction = is.readByte(); luacSizeofInstruction = is.readByte();
luacSizeofLuaNumber = is.readByte(); luacSizeofLuaNumber = is.readByte();
luacIsNumberIntegral = (0 != is.readByte()); luacNumberFormat = is.readByte();
} }
public static LPrototype undump( LuaState L, InputStream stream, String name ) throws IOException { public static LPrototype undump( LuaState L, InputStream stream, String name ) throws IOException {
@@ -262,6 +266,17 @@ public class LoadState {
String sname = getSourceName(name); String sname = getSourceName(name);
LoadState s = new LoadState( L, stream, sname ); LoadState s = new LoadState( L, stream, sname );
s.loadHeader(); s.loadHeader();
// check format
switch ( s.luacNumberFormat ) {
case DumpState.NUMBER_FORMAT_FLOATS_OR_DOUBLES:
case DumpState.NUMBER_FORMAT_INTS_ONLY:
case DumpState.NUMBER_FORMAT_NUM_PATCH_INT32:
break;
default:
L.error("unsupported int size");
}
return s.loadFunction( LString.valueOf(sname) ); return s.loadFunction( LString.valueOf(sname) );
} }

View File

@@ -327,6 +327,7 @@ public class Lua {
// type constants // type constants
public static final int LUA_TINT = (-2);
public static final int LUA_TNONE = (-1); public static final int LUA_TNONE = (-1);
public static final int LUA_TNIL = 0; public static final int LUA_TNIL = 0;
public static final int LUA_TBOOLEAN = 1; public static final int LUA_TBOOLEAN = 1;

View File

@@ -51,7 +51,7 @@ public class luac {
" -p parse only\n" + " -p parse only\n" +
" -s strip debug information\n" + " -s strip debug information\n" +
" -e little endian format for numbers\n" + " -e little endian format for numbers\n" +
" -i int32 format for all numbers\n" + " -i<n> number format 'n', (n=0,1 or 4, default="+DumpState.NUMBER_FORMAT_DEFAULT+")\n" +
" -v show version information\n" + " -v show version information\n" +
" -- stop handling options\n"; " -- stop handling options\n";
@@ -65,7 +65,7 @@ public class luac {
private boolean parseonly = false; private boolean parseonly = false;
private boolean stripdebug = false; private boolean stripdebug = false;
private boolean littleendian = false; private boolean littleendian = false;
private boolean intsonly = false; private int numberformat = DumpState.NUMBER_FORMAT_DEFAULT;
private boolean versioninfo = false; private boolean versioninfo = false;
private boolean processing = true; private boolean processing = true;
@@ -107,7 +107,9 @@ public class luac {
littleendian = true; littleendian = true;
break; break;
case 'i': case 'i':
intsonly = true; if ( args[i].length() <= 2 )
usageExit();
numberformat = Integer.parseInt(args[i].substring(2));
break; break;
case 'v': case 'v':
versioninfo = true; versioninfo = true;
@@ -171,7 +173,7 @@ public class luac {
// write out the chunk // write out the chunk
if (!parseonly) { if (!parseonly) {
DumpState.dump(chunk, out, stripdebug, intsonly, littleendian); DumpState.dump(chunk, out, stripdebug, numberformat, littleendian);
} }
} catch ( Throwable t ) { } catch ( Throwable t ) {

View File

@@ -2,6 +2,7 @@ package org.luaj.compiler;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -15,38 +16,65 @@ import org.luaj.vm.LuaState;
import org.luaj.vm.Platform; import org.luaj.vm.Platform;
public class DumpLoadEndianIntTest extends TestCase { public class DumpLoadEndianIntTest extends TestCase {
private static final String SAVECHUNKS = "SAVECHUNKS";
private static final String script = "return tostring(1234)..'-#!-'..tostring(23.75)"; private static final boolean SHOULDPASS = true;
private static final boolean SHOULDFAIL = false;
private static final String mixedscript = "return tostring(1234)..'-#!-'..tostring(23.75)";
private static final String intscript = "return tostring(1234)..'-#!-'..tostring(23)";
private static final String withdoubles = "1234-#!-23.75"; private static final String withdoubles = "1234-#!-23.75";
private static final String withints = "1234-#!-23"; private static final String withints = "1234-#!-23";
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp(); super.setUp();
Platform.setInstance(new J2sePlatform()); Platform.setInstance(new J2sePlatform());
DumpState.ALLOW_INTEGER_CASTING = true; DumpState.ALLOW_INTEGER_CASTING = false;
} }
public void testBidDoubleCompile() { public void testBigDoubleCompile() {
doTest( false, false, false, withdoubles ); doTest( false, DumpState.NUMBER_FORMAT_FLOATS_OR_DOUBLES, false, mixedscript, withdoubles, withdoubles, SHOULDPASS );
doTest( false, false, true, withdoubles ); doTest( false, DumpState.NUMBER_FORMAT_FLOATS_OR_DOUBLES, true, mixedscript, withdoubles, withdoubles, SHOULDPASS );
} }
public void testLittleDoubleCompile() { public void testLittleDoubleCompile() {
doTest( true, false, false, withdoubles ); doTest( true, DumpState.NUMBER_FORMAT_FLOATS_OR_DOUBLES, false, mixedscript, withdoubles, withdoubles, SHOULDPASS );
doTest( true, false, true, withdoubles ); doTest( true, DumpState.NUMBER_FORMAT_FLOATS_OR_DOUBLES, true, mixedscript, withdoubles, withdoubles, SHOULDPASS );
} }
public void testBigIntCompile() { public void testBigIntCompile() {
doTest( false, true, false, withints ); DumpState.ALLOW_INTEGER_CASTING = true;
doTest( false, true, true, withints ); doTest( false, DumpState.NUMBER_FORMAT_INTS_ONLY, false, mixedscript, withdoubles, withints, SHOULDPASS );
doTest( false, DumpState.NUMBER_FORMAT_INTS_ONLY, true, mixedscript, withdoubles, withints, SHOULDPASS );
DumpState.ALLOW_INTEGER_CASTING = false;
doTest( false, DumpState.NUMBER_FORMAT_INTS_ONLY, false, mixedscript, withdoubles, withints, SHOULDFAIL );
doTest( false, DumpState.NUMBER_FORMAT_INTS_ONLY, true, mixedscript, withdoubles, withints, SHOULDFAIL );
doTest( false, DumpState.NUMBER_FORMAT_INTS_ONLY, false, intscript, withints, withints, SHOULDPASS );
doTest( false, DumpState.NUMBER_FORMAT_INTS_ONLY, true, intscript, withints, withints, SHOULDPASS );
} }
public void testLittleIntCompile() { public void testLittleIntCompile() {
doTest( true, true, false, withints ); DumpState.ALLOW_INTEGER_CASTING = true;
doTest( true, true, true, withints ); doTest( true, DumpState.NUMBER_FORMAT_INTS_ONLY, false, mixedscript, withdoubles, withints, SHOULDPASS );
doTest( true, DumpState.NUMBER_FORMAT_INTS_ONLY, true, mixedscript, withdoubles, withints, SHOULDPASS );
DumpState.ALLOW_INTEGER_CASTING = false;
doTest( true, DumpState.NUMBER_FORMAT_INTS_ONLY, false, mixedscript, withdoubles, withints, SHOULDFAIL );
doTest( true, DumpState.NUMBER_FORMAT_INTS_ONLY, true, mixedscript, withdoubles, withints, SHOULDFAIL );
doTest( true, DumpState.NUMBER_FORMAT_INTS_ONLY, false, intscript, withints, withints, SHOULDPASS );
doTest( true, DumpState.NUMBER_FORMAT_INTS_ONLY, true, intscript, withints, withints, SHOULDPASS );
} }
public void doTest( boolean littleEndian, boolean intNumbers, boolean stripDebug, String expected ) { public void testBigNumpatchCompile() {
doTest( false, DumpState.NUMBER_FORMAT_NUM_PATCH_INT32, false, mixedscript, withdoubles, withdoubles, SHOULDPASS );
doTest( false, DumpState.NUMBER_FORMAT_NUM_PATCH_INT32, true, mixedscript, withdoubles, withdoubles, SHOULDPASS );
}
public void testLittleNumpatchCompile() {
doTest( true, DumpState.NUMBER_FORMAT_NUM_PATCH_INT32, false, mixedscript, withdoubles, withdoubles, SHOULDPASS );
doTest( true, DumpState.NUMBER_FORMAT_NUM_PATCH_INT32, true, mixedscript, withdoubles, withdoubles, SHOULDPASS );
}
public void doTest( boolean littleEndian, int numberFormat, boolean stripDebug,
String script, String expectedPriorDump, String expectedPostDump, boolean shouldPass ) {
try { try {
LuaState vm = Platform.newLuaState(); LuaState vm = Platform.newLuaState();
@@ -59,11 +87,20 @@ public class DumpLoadEndianIntTest extends TestCase {
vm.pushfunction(f); vm.pushfunction(f);
vm.call(0,1); vm.call(0,1);
String actual = vm.poplvalue().toJavaString(); String actual = vm.poplvalue().toJavaString();
assertEquals( withdoubles, actual ); assertEquals( expectedPriorDump, actual );
// dump into bytes // dump into bytes
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
DumpState.dump(p, baos, stripDebug, intNumbers, littleEndian); try {
DumpState.dump(p, baos, stripDebug, numberFormat, littleEndian);
if ( ! shouldPass )
fail( "dump should not have succeeded" );
} catch ( Exception e ) {
if ( shouldPass )
fail( "dump threw "+e );
else
return;
}
byte[] dumped = baos.toByteArray(); byte[] dumped = baos.toByteArray();
// load again using compiler // load again using compiler
@@ -71,13 +108,16 @@ public class DumpLoadEndianIntTest extends TestCase {
vm.load(is, "dumped"); vm.load(is, "dumped");
vm.call(0,1); vm.call(0,1);
actual = vm.poplvalue().toJavaString(); actual = vm.poplvalue().toJavaString();
assertEquals( expected, actual ); assertEquals( expectedPostDump, actual );
// write test chunk // write test chunk
if ( System.getProperty("SAVECHUNKS") != null ) { if ( System.getProperty(SAVECHUNKS) != null && script.equals(mixedscript) ) {
String filename = "test-" new File("build").mkdirs();
String filename = "build/test-"
+(littleEndian? "little-": "big-") +(littleEndian? "little-": "big-")
+(intNumbers? "int-": "double-") +(numberFormat==DumpState.NUMBER_FORMAT_FLOATS_OR_DOUBLES? "double-":
numberFormat==DumpState.NUMBER_FORMAT_INTS_ONLY? "int-":
numberFormat==DumpState.NUMBER_FORMAT_NUM_PATCH_INT32? "numpatch4-": "???-")
+(stripDebug? "nodebug-": "debug-") +(stripDebug? "nodebug-": "debug-")
+"bin.lua"; +"bin.lua";
FileOutputStream fos = new FileOutputStream(filename); FileOutputStream fos = new FileOutputStream(filename);

View File

@@ -1 +1 @@
version: 0.46 version: 0.50