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.OutputStream;
import org.luaj.vm.LBoolean;
import org.luaj.vm.LNil;
import org.luaj.vm.LNumber;
import org.luaj.vm.LPrototype;
import org.luaj.vm.LString;
import org.luaj.vm.LValue;
import org.luaj.vm.LocVars;
import org.luaj.vm.Lua;
import org.luaj.vm.LPrototype;
public class DumpState {
@@ -54,9 +53,21 @@ public class DumpState {
/** set true to allow integer compilation */
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
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 static final int SIZEOF_INT = 4;
private static final int SIZEOF_SIZET = 4;
@@ -98,49 +109,69 @@ public class DumpState {
writer.write( 0 );
}
void dumpNumber(double d) throws IOException {
if ( IS_NUMBER_INTEGRAL ) {
int i = (int) d;
if ( (! ALLOW_INTEGER_CASTING) && (i != d) )
throw new java.lang.IllegalArgumentException("not an integer: "+d);
dumpInt( i );
void dumpDouble(double d) throws IOException {
long l = Double.doubleToLongBits(d);
if ( IS_LITTLE_ENDIAN ) {
dumpInt( (int) l );
dumpInt( (int) (l>>32) );
} else {
long l = Double.doubleToLongBits(d);
if ( IS_LITTLE_ENDIAN ) {
dumpInt( (int) l );
dumpInt( (int) (l>>32) );
} else {
writer.writeLong(l);
}
writer.writeLong(l);
}
}
void dumpCode( final LPrototype f ) throws IOException {
int n = f.code.length;
final int[] code = f.code;
int n = code.length;
dumpInt( n );
for ( int i=0; i<n; i++ )
dumpInt( f.code[i] );
dumpInt( code[i] );
}
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);
for (i = 0; i < n; i++) {
final LValue o = f.k[i];
if (o.isNil()) {
final LValue o = k[i];
switch ( o.luaGetType() ) {
case Lua.LUA_TNIL:
writer.write(Lua.LUA_TNIL);
// do nothing more
} else if (o instanceof LBoolean) {
break;
case Lua.LUA_TBOOLEAN:
writer.write(Lua.LUA_TBOOLEAN);
dumpChar(o.toJavaBoolean() ? 1 : 0);
} else if (o instanceof LNumber) {
writer.write(Lua.LUA_TNUMBER);
dumpNumber(o.toJavaDouble());
} else if (o instanceof LString) {
break;
case Lua.LUA_TNUMBER:
switch (NUMBER_FORMAT) {
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);
dumpString((LString) o);
} else {
throw new IllegalArgumentException("bad type for " + o);
dumpString(o.luaAsString());
break;
default:
throw new IllegalArgumentException("bad type for " + o);
}
}
n = f.p.length;
@@ -158,9 +189,10 @@ public class DumpState {
n = (strip) ? 0 : f.locvars.length;
dumpInt(n);
for (i = 0; i < n; i++) {
dumpString(f.locvars[i].varname);
dumpInt(f.locvars[i].startpc);
dumpInt(f.locvars[i].endpc);
LocVars lvi = f.locvars[i];
dumpString(lvi.varname);
dumpInt(lvi.startpc);
dumpInt(lvi.endpc);
}
n = (strip) ? 0 : f.upvalues.length;
dumpInt(n);
@@ -193,7 +225,7 @@ public class DumpState {
writer.write( SIZEOF_SIZET );
writer.write( SIZEOF_INSTRUCTION );
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;
}
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_NUMBER_INTEGRAL = intonly;
D.SIZEOF_LUA_NUMBER = (intonly? 4: 8);
D.NUMBER_FORMAT = numberFormat;
D.SIZEOF_LUA_NUMBER = (numberFormat==NUMBER_FORMAT_INTS_ONLY? 4: 8);
D.dumpHeader();
D.dumpFunction(f,null);
return D.status;

View File

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

View File

@@ -327,6 +327,7 @@ public class Lua {
// 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;

View File

@@ -51,7 +51,7 @@ public class luac {
" -p parse only\n" +
" -s strip debug information\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" +
" -- stop handling options\n";
@@ -65,7 +65,7 @@ public class luac {
private boolean parseonly = false;
private boolean stripdebug = false;
private boolean littleendian = false;
private boolean intsonly = false;
private int numberformat = DumpState.NUMBER_FORMAT_DEFAULT;
private boolean versioninfo = false;
private boolean processing = true;
@@ -107,7 +107,9 @@ public class luac {
littleendian = true;
break;
case 'i':
intsonly = true;
if ( args[i].length() <= 2 )
usageExit();
numberformat = Integer.parseInt(args[i].substring(2));
break;
case 'v':
versioninfo = true;
@@ -171,7 +173,7 @@ public class luac {
// write out the chunk
if (!parseonly) {
DumpState.dump(chunk, out, stripdebug, intsonly, littleendian);
DumpState.dump(chunk, out, stripdebug, numberformat, littleendian);
}
} catch ( Throwable t ) {

View File

@@ -2,6 +2,7 @@ package org.luaj.compiler;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -15,38 +16,65 @@ import org.luaj.vm.LuaState;
import org.luaj.vm.Platform;
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 withints = "1234-#!-23";
protected void setUp() throws Exception {
super.setUp();
Platform.setInstance(new J2sePlatform());
DumpState.ALLOW_INTEGER_CASTING = true;
DumpState.ALLOW_INTEGER_CASTING = false;
}
public void testBidDoubleCompile() {
doTest( false, false, false, withdoubles );
doTest( false, false, true, withdoubles );
public void testBigDoubleCompile() {
doTest( false, DumpState.NUMBER_FORMAT_FLOATS_OR_DOUBLES, false, mixedscript, withdoubles, withdoubles, SHOULDPASS );
doTest( false, DumpState.NUMBER_FORMAT_FLOATS_OR_DOUBLES, true, mixedscript, withdoubles, withdoubles, SHOULDPASS );
}
public void testLittleDoubleCompile() {
doTest( true, false, false, withdoubles );
doTest( true, false, true, withdoubles );
doTest( true, DumpState.NUMBER_FORMAT_FLOATS_OR_DOUBLES, false, mixedscript, withdoubles, withdoubles, SHOULDPASS );
doTest( true, DumpState.NUMBER_FORMAT_FLOATS_OR_DOUBLES, true, mixedscript, withdoubles, withdoubles, SHOULDPASS );
}
public void testBigIntCompile() {
doTest( false, true, false, withints );
doTest( false, true, true, withints );
DumpState.ALLOW_INTEGER_CASTING = true;
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() {
doTest( true, true, false, withints );
doTest( true, true, true, withints );
DumpState.ALLOW_INTEGER_CASTING = true;
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 {
LuaState vm = Platform.newLuaState();
@@ -59,11 +87,20 @@ public class DumpLoadEndianIntTest extends TestCase {
vm.pushfunction(f);
vm.call(0,1);
String actual = vm.poplvalue().toJavaString();
assertEquals( withdoubles, actual );
assertEquals( expectedPriorDump, actual );
// dump into bytes
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();
// load again using compiler
@@ -71,13 +108,16 @@ public class DumpLoadEndianIntTest extends TestCase {
vm.load(is, "dumped");
vm.call(0,1);
actual = vm.poplvalue().toJavaString();
assertEquals( expected, actual );
assertEquals( expectedPostDump, actual );
// write test chunk
if ( System.getProperty("SAVECHUNKS") != null ) {
String filename = "test-"
if ( System.getProperty(SAVECHUNKS) != null && script.equals(mixedscript) ) {
new File("build").mkdirs();
String filename = "build/test-"
+(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-")
+"bin.lua";
FileOutputStream fos = new FileOutputStream(filename);

View File

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