version 0.50 optional dump and load chunks using number-patch format extensions.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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) );
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 ) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1 +1 @@
|
||||
version: 0.46
|
||||
version: 0.50
|
||||
|
||||
Reference in New Issue
Block a user