diff --git a/.gitignore b/.gitignore index d7642856..df07b267 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ bin/ target/ build/ -lib/ jit/ *.ser *.gz diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..d74b1d54 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2007 LuaJ. 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. diff --git a/README.md b/README.md index d0ca2241..30f17dbd 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ James Roseborough, Ian Farmer, Version 3.0.2 Copyright © 2009-2014 Luaj.org. Freely available under the terms of the -Luaj license. +Luaj license.
@@ -412,7 +412,7 @@ and the math operations include all those supported by Java SE. Android applications should use the JsePlatform, and can include the Luajava library to simplify access to underlying Android APIs. A specialized Globals.finder should be provided to find scripts and data for loading. -See examples/android/src/android/LuajView +See examples/android/src/android/LuajView.java for an example that loads from the "res" Android project directory. The ant build script is examples/android/build.xml. diff --git a/src/core/org/luaj/vm2/LoadState.java b/src/core/org/luaj/vm2/LoadState.java index ece3f885..8d71a74a 100644 --- a/src/core/org/luaj/vm2/LoadState.java +++ b/src/core/org/luaj/vm2/LoadState.java @@ -31,10 +31,10 @@ import java.io.InputStream; *
* The {@link LoadState} class provides the default {@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 +* using either the C-based lua compiler, or luaj's * {@link org.luaj.vm2.compiler.LuaC} compiler. *
-* The canonical method to load and execute code is done +* The canonical method to load and execute code is done * indirectly using the Globals: *
{@code
* Globals globals = JsePlatform.standardGlobals();
@@ -44,10 +44,10 @@ import java.io.InputStream;
* This should work regardless of which {@link Globals.Compiler} or {@link Globals.Undumper}
* have been installed.
*
-* By default, when using {@link org.luaj.vm2.lib.jse.JsePlatform} or
+* By default, when using {@link org.luaj.vm2.lib.jse.JsePlatform} or
* {@link org.luaj.vm2.lib.jme.JmePlatform}
* to construct globals, the {@link LoadState} default undumper is installed
-* as the default {@link Globals.Undumper}.
+* as the default {@link Globals.Undumper}.
*
*
* A lua binary file is created via the {@link org.luaj.vm2.compiler.DumpState} class
@@ -60,7 +60,7 @@ import java.io.InputStream;
* byte[] lua_binary_file_bytes = o.toByteArray();
* }
*
-* The {@link LoadState}'s default undumper {@link #instance}
+* The {@link LoadState}'s default undumper {@link #instance}
* may be used directly to undump these bytes:
* {@code
* Prototypep = LoadState.instance.undump(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua");
@@ -99,7 +99,7 @@ public class LoadState {
/** 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
+ // type constants
public static final int LUA_TINT = (-2);
public static final int LUA_TNONE = (-1);
public static final int LUA_TNIL = 0;
@@ -155,7 +155,6 @@ public class LoadState {
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 = {};
@@ -168,17 +167,17 @@ public class LoadState {
}
/** Load a 4-byte int value from the input stream
- * @return the int value laoded.
+ * @return the int value laoded.
**/
int loadInt() throws IOException {
is.readFully(buf,0,4);
- return luacLittleEndian?
+ 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.
+ * @return the array of int values laoded.
**/
int[] loadIntArray() throws IOException {
int n = loadInt();
@@ -192,7 +191,7 @@ public class LoadState {
is.readFully(buf,0,m);
int[] array = new int[n];
for ( int i=0, j=0; i
@@ -415,7 +417,7 @@ public class LuaClosure extends LuaFunction {
{
LuaValue limit = stack[a + 1];
LuaValue step = stack[a + 2];
- LuaValue idx = step.add(stack[a]);
+ LuaValue idx = stack[a].add(step);
if (step.gt_b(0)? idx.lteq_b(limit): idx.gteq_b(limit)) {
stack[a] = idx;
stack[a + 3] = idx;
@@ -547,8 +549,24 @@ public class LuaClosure extends LuaFunction {
}
private void processErrorHooks(LuaError le, Prototype p, int pc) {
- le.fileline = (p.source != null? p.source.tojstring(): "?") + ":"
- + (p.lineinfo != null && pc >= 0 && pc < p.lineinfo.length? String.valueOf(p.lineinfo[pc]): "?") + ": ";
+ String file = "?";
+ int line = -1;
+ {
+ CallFrame frame = null;
+ if (globals != null && globals.debuglib != null) {
+ frame = globals.debuglib.getCallFrame(le.level);
+ if (frame != null) {
+ String src = frame.shortsource();
+ file = src != null ? src : "?";
+ line = frame.currentline();
+ }
+ }
+ if (frame == null) {
+ file = p.source != null? p.source.tojstring(): "?";
+ line = p.lineinfo != null && pc >= 0 && pc < p.lineinfo.length ? p.lineinfo[pc] : -1;
+ }
+ }
+ le.fileline = file + ": " + line;
le.traceback = errorHook(le.getMessage(), le.level);
}
diff --git a/src/core/org/luaj/vm2/LuaDouble.java b/src/core/org/luaj/vm2/LuaDouble.java
index 67b05996..c23e7849 100644
--- a/src/core/org/luaj/vm2/LuaDouble.java
+++ b/src/core/org/luaj/vm2/LuaDouble.java
@@ -25,12 +25,12 @@ import org.luaj.vm2.compat.JavaCompat;
import org.luaj.vm2.lib.MathLib;
/**
- * Extension of {@link LuaNumber} which can hold a Java double as its value.
+ * Extension of {@link LuaNumber} which can hold a Java double as its value.
*
- * These instance are not instantiated directly by clients, but indirectly
+ * These instance are not instantiated directly by clients, but indirectly
* via the static functions {@link LuaValue#valueOf(int)} or {@link LuaValue#valueOf(double)}
- * functions. This ensures that values which can be represented as int
- * are wrapped in {@link LuaInteger} instead of {@link LuaDouble}.
+ * functions. This ensures that values which can be represented as int
+ * are wrapped in {@link LuaInteger} instead of {@link LuaDouble}.
*
* Almost all API's implemented in LuaDouble are defined and documented in {@link LuaValue}.
*
@@ -44,7 +44,7 @@ import org.luaj.vm2.lib.MathLib;
*
{@link #ddiv_d(double, double)}
* {@link #dmod(double, double)}
* {@link #dmod_d(double, double)}
- *
+ *
*
* @see LuaValue
* @see LuaNumber
@@ -97,7 +97,7 @@ public class LuaDouble extends LuaNumber {
}
public boolean islong() {
- return v == (long) v;
+ return v == (long) v;
}
public byte tobyte() { return (byte) (long) v; }
@@ -158,12 +158,12 @@ public class LuaDouble extends LuaNumber {
/** Divide two double numbers according to lua math, and return a {@link LuaValue} result.
* @param lhs Left-hand-side of the division.
* @param rhs Right-hand-side of the division.
- * @return {@link LuaValue} for the result of the division,
+ * @return {@link LuaValue} for the result of the division,
* taking into account positive and negiative infinity, and Nan
- * @see #ddiv_d(double, double)
+ * @see #ddiv_d(double, double)
*/
public static LuaValue ddiv(double lhs, double rhs) {
- return rhs!=0? valueOf( lhs / rhs ): lhs>0? POSINF: lhs==0? NAN: NEGINF;
+ return rhs!=0? valueOf( lhs / rhs ): lhs>0? POSINF: lhs==0? NAN: NEGINF;
}
/** Divide two double numbers according to lua math, and return a double result.
@@ -173,15 +173,15 @@ public class LuaDouble extends LuaNumber {
* @see #ddiv(double, double)
*/
public static double ddiv_d(double lhs, double rhs) {
- return rhs!=0? lhs / rhs: lhs>0? Double.POSITIVE_INFINITY: lhs==0? Double.NaN: Double.NEGATIVE_INFINITY;
+ return rhs!=0? lhs / rhs: lhs>0? Double.POSITIVE_INFINITY: lhs==0? Double.NaN: Double.NEGATIVE_INFINITY;
}
/** Take modulo double numbers according to lua math, and return a {@link LuaValue} result.
* @param lhs Left-hand-side of the modulo.
* @param rhs Right-hand-side of the modulo.
- * @return {@link LuaValue} for the result of the modulo,
+ * @return {@link LuaValue} for the result of the modulo,
* using lua's rules for modulo
- * @see #dmod_d(double, double)
+ * @see #dmod_d(double, double)
*/
public static LuaValue dmod(double lhs, double rhs) {
if (rhs == 0 || lhs == Double.POSITIVE_INFINITY || lhs == Double.NEGATIVE_INFINITY) return NAN;
@@ -197,7 +197,7 @@ public class LuaDouble extends LuaNumber {
/** Take modulo for double numbers according to lua math, and return a double result.
* @param lhs Left-hand-side of the modulo.
* @param rhs Right-hand-side of the modulo.
- * @return double value for the result of the modulo,
+ * @return double value for the result of the modulo,
* using lua's rules for modulo
* @see #dmod(double, double)
*/
@@ -213,28 +213,28 @@ public class LuaDouble extends LuaNumber {
}
// relational operators
- public LuaValue lt( LuaValue rhs ) { return rhs.gt_b(v)? LuaValue.TRUE: FALSE; }
+ public LuaValue lt( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.gt_b(v)? TRUE: FALSE) : super.lt(rhs); }
public LuaValue lt( double rhs ) { return v < rhs? TRUE: FALSE; }
public LuaValue lt( int rhs ) { return v < rhs? TRUE: FALSE; }
- public boolean lt_b( LuaValue rhs ) { return rhs.gt_b(v); }
+ public boolean lt_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.gt_b(v) : super.lt_b(rhs); }
public boolean lt_b( int rhs ) { return v < rhs; }
public boolean lt_b( double rhs ) { return v < rhs; }
- public LuaValue lteq( LuaValue rhs ) { return rhs.gteq_b(v)? LuaValue.TRUE: FALSE; }
+ public LuaValue lteq( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.gteq_b(v)? TRUE: FALSE) : super.lteq(rhs); }
public LuaValue lteq( double rhs ) { return v <= rhs? TRUE: FALSE; }
public LuaValue lteq( int rhs ) { return v <= rhs? TRUE: FALSE; }
- public boolean lteq_b( LuaValue rhs ) { return rhs.gteq_b(v); }
+ public boolean lteq_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.gteq_b(v) : super.lteq_b(rhs); }
public boolean lteq_b( int rhs ) { return v <= rhs; }
public boolean lteq_b( double rhs ) { return v <= rhs; }
- public LuaValue gt( LuaValue rhs ) { return rhs.lt_b(v)? LuaValue.TRUE: FALSE; }
+ public LuaValue gt( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.lt_b(v)? TRUE: FALSE) : super.gt(rhs); }
public LuaValue gt( double rhs ) { return v > rhs? TRUE: FALSE; }
public LuaValue gt( int rhs ) { return v > rhs? TRUE: FALSE; }
- public boolean gt_b( LuaValue rhs ) { return rhs.lt_b(v); }
+ public boolean gt_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.lt_b(v) : super.gt_b(rhs); }
public boolean gt_b( int rhs ) { return v > rhs; }
public boolean gt_b( double rhs ) { return v > rhs; }
- public LuaValue gteq( LuaValue rhs ) { return rhs.lteq_b(v)? LuaValue.TRUE: FALSE; }
+ public LuaValue gteq( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.lteq_b(v)? TRUE: FALSE) : super.gteq(rhs); }
public LuaValue gteq( double rhs ) { return v >= rhs? TRUE: FALSE; }
public LuaValue gteq( int rhs ) { return v >= rhs? TRUE: FALSE; }
- public boolean gteq_b( LuaValue rhs ) { return rhs.lteq_b(v); }
+ public boolean gteq_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.lteq_b(v) : super.gteq_b(rhs); }
public boolean gteq_b( int rhs ) { return v >= rhs; }
public boolean gteq_b( double rhs ) { return v >= rhs; }
@@ -245,7 +245,7 @@ public class LuaDouble extends LuaNumber {
if ( v == 0.0 ) // never occurs on J2ME
return (JavaCompat.INSTANCE.doubleToRawLongBits(v)<0? "-0": "0");
long l = (long) v;
- if ( l == v )
+ if ( l == v )
return Long.toString(l);
if ( Double.isNaN(v) )
return (JavaCompat.INSTANCE.doubleToRawLongBits(v)<0? JSTR_NEGNAN: JSTR_NAN);
@@ -271,11 +271,11 @@ public class LuaDouble extends LuaNumber {
}
public LuaNumber optnumber(LuaNumber defval) {
- return this;
+ return this;
}
public boolean isnumber() {
- return true;
+ return true;
}
public boolean isstring() {
@@ -290,14 +290,14 @@ public class LuaDouble extends LuaNumber {
public LuaNumber checknumber() { return this; }
public double checkdouble() { return v; }
- public String checkjstring() {
+ public String checkjstring() {
return tojstring();
}
- public LuaString checkstring() {
+ public LuaString checkstring() {
return LuaString.valueOf(tojstring());
}
public boolean isvalidkey() {
return !Double.isNaN(v);
- }
+ }
}
diff --git a/src/core/org/luaj/vm2/LuaFunction.java b/src/core/org/luaj/vm2/LuaFunction.java
index 2730952e..83bee86d 100644
--- a/src/core/org/luaj/vm2/LuaFunction.java
+++ b/src/core/org/luaj/vm2/LuaFunction.java
@@ -22,14 +22,14 @@
package org.luaj.vm2;
-/**
- * Base class for functions implemented in Java.
+/**
+ * Base class for functions implemented in Java.
*
- * Direct subclass include {@link org.luaj.vm2.lib.LibFunction}
- * which is the base class for
- * all built-in library functions coded in Java,
- * and {@link LuaClosure}, which represents a lua closure
- * whose bytecode is interpreted when the function is invoked.
+ * Direct subclass include {@link org.luaj.vm2.lib.LibFunction}
+ * which is the base class for
+ * all built-in library functions coded in Java,
+ * and {@link LuaClosure}, which represents a lua closure
+ * whose bytecode is interpreted when the function is invoked.
* @see LuaValue
* @see LuaClosure
* @see org.luaj.vm2.lib.LibFunction
@@ -57,11 +57,11 @@ public class LuaFunction extends LuaValue {
}
public LuaFunction optfunction(LuaFunction defval) {
- return this;
+ return this;
}
- public LuaValue getmetatable() {
- return s_metatable;
+ public LuaValue getmetatable() {
+ return s_metatable;
}
public String tojstring() {
@@ -72,12 +72,15 @@ public class LuaFunction extends LuaValue {
return valueOf(tojstring());
}
- /** Return the last part of the class name, to be used as a function name in tojstring and elsewhere.
+ /** Return the last part of the class name, to be used as a function name in tojstring and elsewhere.
* @return String naming the last part of the class name after the last dot (.) or dollar sign ($).
+ * If the first character is '_', it is skipped.
*/
public String classnamestub() {
String s = getClass().getName();
- return s.substring(Math.max(s.lastIndexOf('.'),s.lastIndexOf('$'))+1);
+ int offset = Math.max(s.lastIndexOf('.'), s.lastIndexOf('$')) + 1;
+ if (s.charAt(offset) == '_') offset++;
+ return s.substring(offset);
}
/** Return a human-readable name for this function. Returns the last part of the class name by default.
diff --git a/src/core/org/luaj/vm2/LuaInteger.java b/src/core/org/luaj/vm2/LuaInteger.java
index 6c4cb7ba..e5e651dc 100644
--- a/src/core/org/luaj/vm2/LuaInteger.java
+++ b/src/core/org/luaj/vm2/LuaInteger.java
@@ -24,14 +24,14 @@ package org.luaj.vm2;
import org.luaj.vm2.lib.MathLib;
/**
- * Extension of {@link LuaNumber} which can hold a Java int as its value.
+ * Extension of {@link LuaNumber} which can hold a Java int as its value.
*
- * These instance are not instantiated directly by clients, but indirectly
+ * These instance are not instantiated directly by clients, but indirectly
* via the static functions {@link LuaValue#valueOf(int)} or {@link LuaValue#valueOf(double)}
- * functions. This ensures that policies regarding pooling of instances are
- * encapsulated.
+ * functions. This ensures that policies regarding pooling of instances are
+ * encapsulated.
*
- * There are no API's specific to LuaInteger that are useful beyond what is already
+ * There are no API's specific to LuaInteger that are useful beyond what is already
* exposed in {@link LuaValue}.
*
* @see LuaValue
@@ -61,16 +61,16 @@ public class LuaInteger extends LuaNumber {
*/
public static LuaNumber valueOf(long l) {
int i = (int) l;
- return l==i? (i<=255 && i>=-256? intValues[i+256]:
- (LuaNumber) new LuaInteger(i)):
+ return l==i? (i<=255 && i>=-256? intValues[i+256]:
+ (LuaNumber) new LuaInteger(i)):
(LuaNumber) LuaDouble.valueOf(l);
}
/** The value being held by this instance. */
public final int v;
- /**
- * Package protected constructor.
+ /**
+ * Package protected constructor.
* @see LuaValue#valueOf(int)
**/
LuaInteger(int i) {
@@ -103,15 +103,15 @@ public class LuaInteger extends LuaNumber {
}
public LuaString optstring(LuaString defval) {
- return LuaString.valueOf(Integer.toString(v));
+ return LuaString.valueOf(Integer.toString(v));
}
public LuaValue tostring() {
- return LuaString.valueOf(Integer.toString(v));
+ return LuaString.valueOf(Integer.toString(v));
}
- public String optjstring(String defval) {
- return Integer.toString(v);
+ public String optjstring(String defval) {
+ return Integer.toString(v);
}
public LuaInteger checkinteger() {
@@ -172,48 +172,48 @@ public class LuaInteger extends LuaNumber {
public LuaValue modFrom( double lhs ) { return LuaDouble.dmod(lhs,v); }
// relational operators
- public LuaValue lt( LuaValue rhs ) { return rhs.gt_b(v)? TRUE: FALSE; }
+ public LuaValue lt( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.gt_b(v)? TRUE: FALSE) : super.lt(rhs); }
public LuaValue lt( double rhs ) { return v < rhs? TRUE: FALSE; }
public LuaValue lt( int rhs ) { return v < rhs? TRUE: FALSE; }
- public boolean lt_b( LuaValue rhs ) { return rhs.gt_b(v); }
+ public boolean lt_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.gt_b(v) : super.lt_b(rhs); }
public boolean lt_b( int rhs ) { return v < rhs; }
public boolean lt_b( double rhs ) { return v < rhs; }
- public LuaValue lteq( LuaValue rhs ) { return rhs.gteq_b(v)? TRUE: FALSE; }
+ public LuaValue lteq( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.gteq_b(v)? TRUE: FALSE) : super.lteq(rhs); }
public LuaValue lteq( double rhs ) { return v <= rhs? TRUE: FALSE; }
public LuaValue lteq( int rhs ) { return v <= rhs? TRUE: FALSE; }
- public boolean lteq_b( LuaValue rhs ) { return rhs.gteq_b(v); }
+ public boolean lteq_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.gteq_b(v) : super.lteq_b(rhs); }
public boolean lteq_b( int rhs ) { return v <= rhs; }
public boolean lteq_b( double rhs ) { return v <= rhs; }
- public LuaValue gt( LuaValue rhs ) { return rhs.lt_b(v)? TRUE: FALSE; }
+ public LuaValue gt( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.lt_b(v)? TRUE: FALSE) : super.gt(rhs); }
public LuaValue gt( double rhs ) { return v > rhs? TRUE: FALSE; }
public LuaValue gt( int rhs ) { return v > rhs? TRUE: FALSE; }
- public boolean gt_b( LuaValue rhs ) { return rhs.lt_b(v); }
+ public boolean gt_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.lt_b(v) : super.gt_b(rhs); }
public boolean gt_b( int rhs ) { return v > rhs; }
public boolean gt_b( double rhs ) { return v > rhs; }
- public LuaValue gteq( LuaValue rhs ) { return rhs.lteq_b(v)? TRUE: FALSE; }
+ public LuaValue gteq( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.lteq_b(v)? TRUE: FALSE) : super.gteq(rhs); }
public LuaValue gteq( double rhs ) { return v >= rhs? TRUE: FALSE; }
public LuaValue gteq( int rhs ) { return v >= rhs? TRUE: FALSE; }
- public boolean gteq_b( LuaValue rhs ) { return rhs.lteq_b(v); }
+ public boolean gteq_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.lteq_b(v) : super.gteq_b(rhs); }
public boolean gteq_b( int rhs ) { return v >= rhs; }
public boolean gteq_b( double rhs ) { return v >= rhs; }
// string comparison
public int strcmp( LuaString rhs ) { typerror("attempt to compare number with string"); return 0; }
- public int checkint() {
- return v;
+ public int checkint() {
+ return v;
}
public long checklong() {
- return v;
+ return v;
}
public double checkdouble() {
return v;
}
- public String checkjstring() {
- return String.valueOf(v);
+ public String checkjstring() {
+ return String.valueOf(v);
}
- public LuaString checkstring() {
- return valueOf( String.valueOf(v) );
+ public LuaString checkstring() {
+ return valueOf( String.valueOf(v) );
}
}
diff --git a/src/core/org/luaj/vm2/LuaString.java b/src/core/org/luaj/vm2/LuaString.java
index e9cf8ef0..6c64d559 100644
--- a/src/core/org/luaj/vm2/LuaString.java
+++ b/src/core/org/luaj/vm2/LuaString.java
@@ -31,28 +31,28 @@ import java.io.PrintStream;
import org.luaj.vm2.lib.MathLib;
/**
- * Subclass of {@link LuaValue} for representing lua strings.
+ * Subclass of {@link LuaValue} for representing lua strings.
*
- * Because lua string values are more nearly sequences of bytes than
+ * Because lua string values are more nearly sequences of bytes than
* sequences of characters or unicode code points, the {@link LuaString}
- * implementation holds the string value in an internal byte array.
+ * implementation holds the string value in an internal byte array.
*
- * {@link LuaString} values are not considered mutable once constructed,
+ * {@link LuaString} values are not considered mutable once constructed,
* so multiple {@link LuaString} values can chare a single byte array.
*
* Currently {@link LuaString}s are pooled via a centrally managed weak table.
- * To ensure that as many string values as possible take advantage of this,
- * Constructors are not exposed directly. As with number, booleans, and nil,
+ * To ensure that as many string values as possible take advantage of this,
+ * Constructors are not exposed directly. As with number, booleans, and nil,
* instance construction should be via {@link LuaValue#valueOf(byte[])} or similar API.
*
* Because of this pooling, users of LuaString must not directly alter the
* bytes in a LuaString, or undefined behavior will result.
*
- * When Java Strings are used to initialize {@link LuaString} data, the UTF8 encoding is assumed.
- * The functions
+ * When Java Strings are used to initialize {@link LuaString} data, the UTF8 encoding is assumed.
+ * The functions
* {@link #lengthAsUtf8(char[])},
- * {@link #encodeToUtf8(char[], int, byte[], int)}, and
- * {@link #decodeAsUtf8(byte[], int, int)}
+ * {@link #encodeToUtf8(char[], int, byte[], int)}, and
+ * {@link #decodeAsUtf8(byte[], int, int)}
* are used to convert back and forth between UTF8 byte arrays and character arrays.
*
* @see LuaValue
@@ -63,15 +63,15 @@ public class LuaString extends LuaValue {
/** The singleton instance for string metatables that forwards to the string functions.
* Typically, this is set to the string metatable as a side effect of loading the string
- * library, and is read-write to provide flexible behavior by default. When used in a
+ * library, and is read-write to provide flexible behavior by default. When used in a
* server environment where there may be roge scripts, this should be replaced with a
* read-only table since it is shared across all lua code in this Java VM.
*/
public static LuaValue s_metatable;
/** The bytes for the string. These must not be mutated directly because
- * the backing may be shared by multiple LuaStrings, and the hash code is
- * computed only at construction time.
+ * the backing may be shared by multiple LuaStrings, and the hash code is
+ * computed only at construction time.
* It is exposed only for performance and legacy reasons. */
public final byte[] m_bytes;
@@ -84,29 +84,29 @@ public class LuaString extends LuaValue {
/** The hashcode for this string. Computed at construct time. */
private final int m_hashcode;
- /** Size of cache of recent short strings. This is the maximum number of LuaStrings that
+ /** Size of cache of recent short strings. This is the maximum number of LuaStrings that
* will be retained in the cache of recent short strings. Exposed to package for testing. */
static final int RECENT_STRINGS_CACHE_SIZE = 128;
- /** Maximum length of a string to be considered for recent short strings caching.
+ /** Maximum length of a string to be considered for recent short strings caching.
* This effectively limits the total memory that can be spent on the recent strings cache,
- * because no LuaString whose backing exceeds this length will be put into the cache.
+ * because no LuaString whose backing exceeds this length will be put into the cache.
* Exposed to package for testing. */
static final int RECENT_STRINGS_MAX_LENGTH = 32;
- /** Simple cache of recently created strings that are short.
- * This is simply a list of strings, indexed by their hash codes modulo the cache size
- * that have been recently constructed. If a string is being constructed frequently
- * from different contexts, it will generally show up as a cache hit and resolve
+ /** Simple cache of recently created strings that are short.
+ * This is simply a list of strings, indexed by their hash codes modulo the cache size
+ * that have been recently constructed. If a string is being constructed frequently
+ * from different contexts, it will generally show up as a cache hit and resolve
* to the same value. */
private static final class RecentShortStrings {
- private static final LuaString recent_short_strings[] =
+ private static final LuaString recent_short_strings[] =
new LuaString[RECENT_STRINGS_CACHE_SIZE];
}
/**
- * Get a {@link LuaString} instance whose bytes match
- * the supplied Java String using the UTF8 encoding.
+ * Get a {@link LuaString} instance whose bytes match
+ * the supplied Java String using the UTF8 encoding.
* @param string Java String containing characters to encode as UTF8
* @return {@link LuaString} with UTF8 bytes corresponding to the supplied String
*/
@@ -120,7 +120,7 @@ public class LuaString extends LuaValue {
/** Construct a {@link LuaString} for a portion of a byte array.
*
* The array is first be used as the backing for this object, so clients must not change contents.
- * If the supplied value for 'len' is more than half the length of the container, the
+ * If the supplied value for 'len' is more than half the length of the container, the
* supplied byte array will be used as the backing, otherwise the bytes will be copied to a
* new byte array, and cache lookup may be performed.
*
@@ -172,11 +172,11 @@ public class LuaString extends LuaValue {
/** Construct a {@link LuaString} using the supplied characters as byte values.
*
- * Only the low-order 8-bits of each character are used, the remainder is ignored.
+ * Only the low-order 8-bits of each character are used, the remainder is ignored.
*
- * This is most useful for constructing byte sequences that do not conform to UTF8.
- * @param bytes array of char, whose values are truncated at 8-bits each and put into a byte array.
- * @return {@link LuaString} wrapping a copy of the byte buffer
+ * This is most useful for constructing byte sequences that do not conform to UTF8.
+ * @param bytes array of char, whose values are truncated at 8-bits each and put into a byte array.
+ * @return {@link LuaString} wrapping a copy of the byte buffer
*/
public static LuaString valueOf(char[] bytes) {
return valueOf(bytes, 0, bytes.length);
@@ -184,11 +184,11 @@ public class LuaString extends LuaValue {
/** Construct a {@link LuaString} using the supplied characters as byte values.
*
- * Only the low-order 8-bits of each character are used, the remainder is ignored.
+ * Only the low-order 8-bits of each character are used, the remainder is ignored.
*
- * This is most useful for constructing byte sequences that do not conform to UTF8.
- * @param bytes array of char, whose values are truncated at 8-bits each and put into a byte array.
- * @return {@link LuaString} wrapping a copy of the byte buffer
+ * This is most useful for constructing byte sequences that do not conform to UTF8.
+ * @param bytes array of char, whose values are truncated at 8-bits each and put into a byte array.
+ * @return {@link LuaString} wrapping a copy of the byte buffer
*/
public static LuaString valueOf(char[] bytes, int off, int len) {
byte[] b = new byte[len];
@@ -215,7 +215,7 @@ public class LuaString extends LuaValue {
* The LuaString returned will either be a new LuaString containing the byte array,
* or be an existing LuaString used already having the same value.
*
- * The caller must not mutate the contents of the byte array after this call, as
+ * The caller must not mutate the contents of the byte array after this call, as
* it may be used elsewhere due to recent short string caching.
* @param bytes byte buffer
* @return {@link LuaString} wrapping the byte buffer
@@ -241,11 +241,11 @@ public class LuaString extends LuaValue {
}
public boolean isstring() {
- return true;
+ return true;
}
- public LuaValue getmetatable() {
- return s_metatable;
+ public LuaValue getmetatable() {
+ return s_metatable;
}
public int type() {
@@ -289,20 +289,20 @@ public class LuaString extends LuaValue {
public LuaValue modFrom( double lhs ) { return LuaDouble.dmod(lhs, checkarith()); }
// relational operators, these only work with other strings
- public LuaValue lt( LuaValue rhs ) { return rhs.strcmp(this)>0? LuaValue.TRUE: FALSE; }
- public boolean lt_b( LuaValue rhs ) { return rhs.strcmp(this)>0; }
+ public LuaValue lt( LuaValue rhs ) { return rhs.isstring() ? (rhs.strcmp(this)>0? LuaValue.TRUE: FALSE) : super.lt(rhs); }
+ public boolean lt_b( LuaValue rhs ) { return rhs.isstring() ? rhs.strcmp(this)>0 : super.lt_b(rhs); }
public boolean lt_b( int rhs ) { typerror("attempt to compare string with number"); return false; }
public boolean lt_b( double rhs ) { typerror("attempt to compare string with number"); return false; }
- public LuaValue lteq( LuaValue rhs ) { return rhs.strcmp(this)>=0? LuaValue.TRUE: FALSE; }
- public boolean lteq_b( LuaValue rhs ) { return rhs.strcmp(this)>=0; }
+ public LuaValue lteq( LuaValue rhs ) { return rhs.isstring() ? (rhs.strcmp(this)>=0? LuaValue.TRUE: FALSE) : super.lteq(rhs); }
+ public boolean lteq_b( LuaValue rhs ) { return rhs.isstring() ? rhs.strcmp(this)>=0 : super.lteq_b(rhs); }
public boolean lteq_b( int rhs ) { typerror("attempt to compare string with number"); return false; }
public boolean lteq_b( double rhs ) { typerror("attempt to compare string with number"); return false; }
- public LuaValue gt( LuaValue rhs ) { return rhs.strcmp(this)<0? LuaValue.TRUE: FALSE; }
- public boolean gt_b( LuaValue rhs ) { return rhs.strcmp(this)<0; }
+ public LuaValue gt( LuaValue rhs ) { return rhs.isstring() ? (rhs.strcmp(this)<0? LuaValue.TRUE: FALSE) : super.gt(rhs); }
+ public boolean gt_b( LuaValue rhs ) { return rhs.isstring() ? rhs.strcmp(this)<0 : super.gt_b(rhs); }
public boolean gt_b( int rhs ) { typerror("attempt to compare string with number"); return false; }
public boolean gt_b( double rhs ) { typerror("attempt to compare string with number"); return false; }
- public LuaValue gteq( LuaValue rhs ) { return rhs.strcmp(this)<=0? LuaValue.TRUE: FALSE; }
- public boolean gteq_b( LuaValue rhs ) { return rhs.strcmp(this)<=0; }
+ public LuaValue gteq( LuaValue rhs ) { return rhs.isstring() ? (rhs.strcmp(this)<=0? LuaValue.TRUE: FALSE) : super.gteq(rhs); }
+ public boolean gteq_b( LuaValue rhs ) { return rhs.isstring() ? rhs.strcmp(this)<=0 : super.gteq_b(rhs); }
public boolean gteq_b( int rhs ) { typerror("attempt to compare string with number"); return false; }
public boolean gteq_b( double rhs ) { typerror("attempt to compare string with number"); return false; }
@@ -310,14 +310,14 @@ public class LuaString extends LuaValue {
public LuaValue concat(LuaValue rhs) { return rhs.concatTo(this); }
public Buffer concat(Buffer rhs) { return rhs.concatTo(this); }
public LuaValue concatTo(LuaNumber lhs) { return concatTo(lhs.strvalue()); }
- public LuaValue concatTo(LuaString lhs) {
+ public LuaValue concatTo(LuaString lhs) {
byte[] b = new byte[lhs.m_length+this.m_length];
System.arraycopy(lhs.m_bytes, lhs.m_offset, b, 0, lhs.m_length);
System.arraycopy(this.m_bytes, this.m_offset, b, lhs.m_length, this.m_length);
return valueUsing(b, 0, b.length);
}
- // string comparison
+ // string comparison
public int strcmp(LuaValue lhs) { return -lhs.strcmp(this); }
public int strcmp(LuaString rhs) {
for ( int i=0, j=0; i=0 )
+ while ( --n>=0 )
if ( a[i++]!=b[j++] )
return false;
return true;
@@ -535,8 +535,8 @@ public class LuaString extends LuaValue {
return luaByte( index );
}
- public String checkjstring() {
- return tojstring();
+ public String checkjstring() {
+ return tojstring();
}
public LuaString checkstring() {
@@ -552,7 +552,7 @@ public class LuaString extends LuaValue {
}
/**
- * Copy the bytes of the string into the given byte array.
+ * Copy the bytes of the string into the given byte array.
* @param strOffset offset from which to copy
* @param bytes destination byte array
* @param arrayOffset offset in destination
@@ -626,12 +626,12 @@ public class LuaString extends LuaValue {
/**
- * Convert to Java String interpreting as utf8 characters.
+ * Convert to Java String interpreting as utf8 characters.
*
* @param bytes byte array in UTF8 encoding to convert
* @param offset starting index in byte array
* @param length number of bytes to convert
- * @return Java String corresponding to the value of bytes interpreted using UTF8
+ * @return Java String corresponding to the value of bytes interpreted using UTF8
* @see #lengthAsUtf8(char[])
* @see #encodeToUtf8(char[], int, byte[], int)
* @see #isValidUtf8()
@@ -692,7 +692,7 @@ public class LuaString extends LuaValue {
/**
* Encode the given Java string as UTF-8 bytes, writing the result to bytes
- * starting at offset.
+ * starting at offset.
*
* The string should be measured first with lengthAsUtf8
* to make sure the given byte array is large enough.
@@ -759,22 +759,22 @@ public class LuaString extends LuaValue {
// --------------------- number conversion -----------------------
- /**
- * convert to a number using baee 10 or base 16 if it starts with '0x',
+ /**
+ * convert to a number using baee 10 or base 16 if it starts with '0x',
* or NIL if it can't be converted
* @return IntValue, DoubleValue, or NIL depending on the content of the string.
- * @see LuaValue#tonumber()
+ * @see LuaValue#tonumber()
*/
public LuaValue tonumber() {
double d = scannumber();
return Double.isNaN(d)? NIL: valueOf(d);
}
- /**
+ /**
* convert to a number using a supplied base, or NIL if it can't be converted
* @param base the base to use, such as 10
* @return IntValue, DoubleValue, or NIL depending on the content of the string.
- * @see LuaValue#tonumber()
+ * @see LuaValue#tonumber()
*/
public LuaValue tonumber( int base ) {
double d = scannumber( base );
@@ -800,7 +800,7 @@ public class LuaString extends LuaValue {
/**
* Convert to a number in base 10, or base 16 if the string starts with '0x',
* or return Double.NaN if it cannot be converted to a number.
- * @return double value if conversion is valid, or Double.NaN if not
+ * @return double value if conversion is valid, or Double.NaN if not
*/
public double scannumber() {
int i = m_offset, j = m_offset + m_length;
@@ -863,10 +863,10 @@ public class LuaString extends LuaValue {
return sgn * m * MathLib.dpow_d(2.0, e);
}
- /**
+ /**
* Convert to a number in a base, or return Double.NaN if not a number.
* @param base the base to use between 2 and 36
- * @return double value if conversion is valid, or Double.NaN if not
+ * @return double value if conversion is valid, or Double.NaN if not
*/
public double scannumber(int base) {
if ( base < 2 || base > 36 )
@@ -884,7 +884,7 @@ public class LuaString extends LuaValue {
* @param base the base to use, such as 10
* @param start the index to start searching from
* @param end the first index beyond the search range
- * @return double value if conversion is valid,
+ * @return double value if conversion is valid,
* or Double.NaN if not
*/
private double scanlong( int base, int start, int end ) {
@@ -895,7 +895,7 @@ public class LuaString extends LuaValue {
int digit = m_bytes[i] - (base<=10||(m_bytes[i]>='0'&&m_bytes[i]<='9')? '0':
m_bytes[i]>='A'&&m_bytes[i]<='Z'? ('A'-10): ('a'-10));
if ( digit < 0 || digit >= base )
- return Double.NaN;
+ return Double.NaN;
x = x * base + digit;
if ( x < 0 )
return Double.NaN; // overflow
@@ -907,7 +907,7 @@ public class LuaString extends LuaValue {
* Scan and convert a double value, or return Double.NaN if not a double.
* @param start the index to start searching from
* @param end the first index beyond the search range
- * @return double value if conversion is valid,
+ * @return double value if conversion is valid,
* or Double.NaN if not
*/
private double scandouble(int start, int end) {
@@ -930,7 +930,7 @@ public class LuaString extends LuaValue {
c[i-start] = (char) m_bytes[i];
try {
return Double.parseDouble(new String(c));
- } catch ( Exception e ) {
+ } catch ( Exception e ) {
return Double.NaN;
}
}
diff --git a/src/core/org/luaj/vm2/LuaTable.java b/src/core/org/luaj/vm2/LuaTable.java
index d95e261c..a35dcc3f 100644
--- a/src/core/org/luaj/vm2/LuaTable.java
+++ b/src/core/org/luaj/vm2/LuaTable.java
@@ -25,23 +25,23 @@ import java.lang.ref.WeakReference;
import java.util.Vector;
/**
- * Subclass of {@link LuaValue} for representing lua tables.
+ * Subclass of {@link LuaValue} for representing lua tables.
*
* Almost all API's implemented in {@link LuaTable} are defined and documented in {@link LuaValue}.
*
* If a table is needed, the one of the type-checking functions can be used such as
- * {@link #istable()},
+ * {@link #istable()},
* {@link #checktable()}, or
- * {@link #opttable(LuaTable)}
+ * {@link #opttable(LuaTable)}
*
- * The main table operations are defined on {@link LuaValue}
+ * The main table operations are defined on {@link LuaValue}
* for getting and setting values with and without metatag processing:
*
- * - {@link #get(LuaValue)}
- * - {@link #set(LuaValue,LuaValue)}
- * - {@link #rawget(LuaValue)}
+ * - {@link #get(LuaValue)}
+ * - {@link #set(LuaValue,LuaValue)}
+ * - {@link #rawget(LuaValue)}
* - {@link #rawset(LuaValue,LuaValue)}
- * - plus overloads such as {@link #get(String)}, {@link #get(int)}, and so on
+ * - plus overloads such as {@link #get(String)}, {@link #get(int)}, and so on
*
*
* To iterate over key-value pairs from Java, use
@@ -56,7 +56,7 @@ import java.util.Vector;
* }}
*
* - * As with other types, {@link LuaTable} instances should be constructed via one of the table constructor + * As with other types, {@link LuaTable} instances should be constructed via one of the table constructor * methods on {@link LuaValue}: *
- * To construct varargs, use one of the static methods such as + * To construct varargs, use one of the static methods such as * {@code LuaValue.varargsOf(LuaValue,LuaValue)} *
*
- * Any LuaValue can be used as a stand-in for Varargs, for both calls and return values. - * When doing so, nargs() will return 1 and arg1() or arg(1) will return this. - * This simplifies the case when calling or implementing varargs functions with only - * 1 argument or 1 return value. + * Any LuaValue can be used as a stand-in for Varargs, for both calls and return values. + * When doing so, nargs() will return 1 and arg1() or arg(1) will return this. + * This simplifies the case when calling or implementing varargs functions with only + * 1 argument or 1 return value. *
- * Varargs can also be derived from other varargs by appending to the front with a call + * Varargs can also be derived from other varargs by appending to the front with a call * such as {@code LuaValue.varargsOf(LuaValue,Varargs)} * or by taking a portion of the args using {@code Varargs.subargs(int start)} *
@@ -57,22 +57,22 @@ public abstract class Varargs { abstract public LuaValue arg( int i ); /** - * Get the number of arguments, or 0 if there are none. - * @return number of arguments. + * Get the number of arguments, or 0 if there are none. + * @return number of arguments. */ abstract public int narg(); /** - * Get the first argument in the list. + * Get the first argument in the list. * @return LuaValue which is first in the list, or LuaValue.NIL if there are no values. * @see Varargs#arg(int) * @see LuaValue#NIL */ abstract public LuaValue arg1(); - /** + /** * Evaluate any pending tail call and return result. - * @return the evaluated tail call result + * @return the evaluated tail call result */ public Varargs eval() { return this; } @@ -88,7 +88,7 @@ public abstract class Varargs { // utilities to get specific arguments and type-check them. // ----------------------------------------------------------------------- - /** Gets the type of argument {@code i} + /** Gets the type of argument {@code i} * @param i the index of the argument to convert, 1 is the first argument * @return int value corresponding to one of the LuaValue integer type values * @see LuaValue#TNIL @@ -117,20 +117,20 @@ public abstract class Varargs { public boolean isfunction(int i) { return arg(i).isfunction(); } /** Tests if argument i is a number. - * Since anywhere a number is required, a string can be used that - * is a number, this will return true for both numbers and - * strings that can be interpreted as numbers. + * Since anywhere a number is required, a string can be used that + * is a number, this will return true for both numbers and + * strings that can be interpreted as numbers. * @param i the index of the argument to test, 1 is the first argument - * @return true if the argument exists and is a number or + * @return true if the argument exists and is a number or * string that can be interpreted as a number, false otherwise * @see LuaValue#TNUMBER * @see LuaValue#TSTRING * */ public boolean isnumber(int i) { return arg(i).isnumber(); } - /** Tests if argument i is a string. - * Since all lua numbers can be used where strings are used, - * this will return true for both strings and numbers. + /** Tests if argument i is a string. + * Since all lua numbers can be used where strings are used, + * this will return true for both strings and numbers. * @param i the index of the argument to test, 1 is the first argument * @return true if the argument exists and is a string or number, false otherwise * @see LuaValue#TNUMBER @@ -167,7 +167,7 @@ public abstract class Varargs { /** Return argument i as a boolean value, {@code defval} if nil, or throw a LuaError if any other type. * @param i the index of the argument to test, 1 is the first argument - * @return true if argument i is boolean true, false if it is false, or defval if not supplied or nil + * @return true if argument i is boolean true, false if it is false, or defval if not supplied or nil * @exception LuaError if the argument is not a lua boolean * */ public boolean optboolean(int i, boolean defval) { return arg(i).optboolean(defval); } @@ -256,7 +256,7 @@ public abstract class Varargs { * */ public Object optuserdata(int i, Object defval) { return arg(i).optuserdata(defval); } - /** Return argument i as a java Object if it is a userdata whose instance Class c or a subclass, + /** Return argument i as a java Object if it is a userdata whose instance Class c or a subclass, * {@code defval} if nil, or throw a LuaError if any other type. * @param i the index of the argument to test, 1 is the first argument * @param c the class to which the userdata instance must be assignable @@ -291,7 +291,7 @@ public abstract class Varargs { * @return java double value if argument i is a number or string that converts to a number * @exception LuaError if the argument is not a number * */ - public double checkdouble(int i) { return arg(i).checknumber().todouble(); } + public double checkdouble(int i) { return arg(i).checkdouble(); } /** Return argument i as a function, or throw an error if an incompatible type. * @param i the index of the argument to test, 1 is the first argument @@ -300,12 +300,12 @@ public abstract class Varargs { * */ public LuaFunction checkfunction(int i) { return arg(i).checkfunction(); } - /** Return argument i as a java int value, discarding any fractional part, or throw an error if not a number. + /** Return argument i as a java int value, or throw an error if it cannot be converted to one. * @param i the index of the argument to test, 1 is the first argument - * @return int value with fraction discarded and truncated if necessary if argument i is number - * @exception LuaError if the argument is not a number + * @return int value if argument i is a number or string that converts to a number + * @exception LuaError if the argument cannot be represented by a java int value * */ - public int checkint(int i) { return arg(i).checknumber().toint(); } + public int checkint(int i) { return arg(i).checkint(); } /** Return argument i as a java int value, or throw an error if not a number or is not representable by a java int. * @param i the index of the argument to test, 1 is the first argument @@ -314,12 +314,12 @@ public abstract class Varargs { * */ public LuaInteger checkinteger(int i) { return arg(i).checkinteger(); } - /** Return argument i as a java long value, discarding any fractional part, or throw an error if not a number. + /** Return argument i as a java long value, or throw an error if it cannot be converted to one. * @param i the index of the argument to test, 1 is the first argument - * @return long value with fraction discarded and truncated if necessary if argument i is number - * @exception LuaError if the argument is not a number + * @return long value if argument i is a number or string that converts to a number + * @exception LuaError if the argument cannot be represented by a java long value * */ - public long checklong(int i) { return arg(i).checknumber().tolong(); } + public long checklong(int i) { return arg(i).checklong(); } /** Return argument i as a LuaNumber, or throw an error if not a number or string that can be converted to a number. * @param i the index of the argument to test, 1 is the first argument @@ -363,7 +363,7 @@ public abstract class Varargs { * */ public Object checkuserdata(int i) { return arg(i).checkuserdata(); } - /** Return argument i as a java Object if it is a userdata whose instance Class c or a subclass, + /** Return argument i as a java Object if it is a userdata whose instance Class c or a subclass, * or throw an error if any other type. * @param i the index of the argument to test, 1 is the first argument * @param c the class to which the userdata instance must be assignable @@ -387,7 +387,7 @@ public abstract class Varargs { public LuaValue checknotnil(int i) { return arg(i).checknotnil(); } /** Performs test on argument i as a LuaValue when a user-supplied assertion passes, or throw an error. - * Returns normally if the value of {@code test} is {@code true}, otherwise throws and argument error with + * Returns normally if the value of {@code test} is {@code true}, otherwise throws and argument error with * the supplied message, {@code msg}. * @param test user supplied assertion to test against * @param i the index to report in any error message @@ -404,20 +404,20 @@ public abstract class Varargs { return i>narg() || arg(i).isnil(); } - /** Convert argument {@code i} to java boolean based on lua rules for boolean evaluation. + /** Convert argument {@code i} to java boolean based on lua rules for boolean evaluation. * @param i the index of the argument to convert, 1 is the first argument * @return {@code false} if argument i is nil or false, otherwise {@code true} * */ public boolean toboolean(int i) { return arg(i).toboolean(); } - /** Return argument i as a java byte value, discarding any fractional part and truncating, + /** Return argument i as a java byte value, discarding any fractional part and truncating, * or 0 if not a number. * @param i the index of the argument to convert, 1 is the first argument * @return byte value with fraction discarded and truncated if necessary if argument i is number, otherwise 0 * */ public byte tobyte(int i) { return arg(i).tobyte(); } - /** Return argument i as a java char value, discarding any fractional part and truncating, + /** Return argument i as a java char value, discarding any fractional part and truncating, * or 0 if not a number. * @param i the index of the argument to convert, 1 is the first argument * @return char value with fraction discarded and truncated if necessary if argument i is number, otherwise 0 @@ -430,21 +430,21 @@ public abstract class Varargs { * */ public double todouble(int i) { return arg(i).todouble(); } - /** Return argument i as a java float value, discarding excess fractional part and truncating, + /** Return argument i as a java float value, discarding excess fractional part and truncating, * or 0 if not a number. * @param i the index of the argument to convert, 1 is the first argument * @return float value with excess fraction discarded and truncated if necessary if argument i is number, otherwise 0 * */ public float tofloat(int i) { return arg(i).tofloat(); } - /** Return argument i as a java int value, discarding any fractional part and truncating, + /** Return argument i as a java int value, discarding any fractional part and truncating, * or 0 if not a number. * @param i the index of the argument to convert, 1 is the first argument * @return int value with fraction discarded and truncated if necessary if argument i is number, otherwise 0 * */ public int toint(int i) { return arg(i).toint(); } - /** Return argument i as a java long value, discarding any fractional part and truncating, + /** Return argument i as a java long value, discarding any fractional part and truncating, * or 0 if not a number. * @param i the index of the argument to convert, 1 is the first argument * @return long value with fraction discarded and truncated if necessary if argument i is number, otherwise 0 @@ -457,7 +457,7 @@ public abstract class Varargs { * */ public String tojstring(int i) { return arg(i).tojstring(); } - /** Return argument i as a java short value, discarding any fractional part and truncating, + /** Return argument i as a java short value, discarding any fractional part and truncating, * or 0 if not a number. * @param i the index of the argument to convert, 1 is the first argument * @return short value with fraction discarded and truncated if necessary if argument i is number, otherwise 0 @@ -477,8 +477,8 @@ public abstract class Varargs { * */ public Object touserdata(int i,Class c) { return arg(i).touserdata(c); } - /** Convert the list of varargs values to a human readable java String. - * @return String value in human readable form such as {1,2}. + /** Convert the list of varargs values to a human readable java String. + * @return String value in human readable form such as {1,2}. */ public String tojstring() { Buffer sb = new Buffer(); @@ -491,8 +491,8 @@ public abstract class Varargs { return sb.tojstring(); } - /** Convert the value or values to a java String using Varargs.tojstring() - * @return String value in human readable form. + /** Convert the value or values to a java String using Varargs.tojstring() + * @return String value in human readable form. * @see Varargs#tojstring() */ public String toString() { return tojstring(); } @@ -544,21 +544,21 @@ public abstract class Varargs { } } - /** Varargs implemenation backed by two values. + /** Varargs implemenation backed by two values. *
* This is an internal class not intended to be used directly. - * Instead use the corresponding static method on LuaValue. - * + * Instead use the corresponding static method on LuaValue. + * * @see LuaValue#varargsOf(LuaValue, Varargs) */ static final class PairVarargs extends Varargs { private final LuaValue v1; private final Varargs v2; - /** Construct a Varargs from an two LuaValue. + /** Construct a Varargs from an two LuaValue. *
* This is an internal class not intended to be used directly. - * Instead use the corresponding static method on LuaValue. - * + * Instead use the corresponding static method on LuaValue. + * * @see LuaValue#varargsOf(LuaValue, Varargs) */ PairVarargs(LuaValue v1, Varargs v2) { @@ -571,8 +571,8 @@ public abstract class Varargs { public int narg() { return 1+v2.narg(); } - public LuaValue arg1() { - return v1; + public LuaValue arg1() { + return v1; } public Varargs subargs(final int start) { if (start == 1) @@ -585,22 +585,22 @@ public abstract class Varargs { } } - /** Varargs implemenation backed by an array of LuaValues + /** Varargs implemenation backed by an array of LuaValues *
* This is an internal class not intended to be used directly. - * Instead use the corresponding static methods on LuaValue. - * + * Instead use the corresponding static methods on LuaValue. + * * @see LuaValue#varargsOf(LuaValue[]) * @see LuaValue#varargsOf(LuaValue[], Varargs) */ static final class ArrayVarargs extends Varargs { private final LuaValue[] v; private final Varargs r; - /** Construct a Varargs from an array of LuaValue. + /** Construct a Varargs from an array of LuaValue. *
* This is an internal class not intended to be used directly. - * Instead use the corresponding static methods on LuaValue. - * + * Instead use the corresponding static methods on LuaValue. + * * @see LuaValue#varargsOf(LuaValue[]) * @see LuaValue#varargsOf(LuaValue[], Varargs) */ @@ -631,11 +631,11 @@ public abstract class Varargs { } } - /** Varargs implemenation backed by an array of LuaValues + /** Varargs implemenation backed by an array of LuaValues *
* This is an internal class not intended to be used directly. - * Instead use the corresponding static methods on LuaValue. - * + * Instead use the corresponding static methods on LuaValue. + * * @see LuaValue#varargsOf(LuaValue[], int, int) * @see LuaValue#varargsOf(LuaValue[], int, int, Varargs) */ @@ -644,11 +644,11 @@ public abstract class Varargs { private final LuaValue[] v; private final int length; private final Varargs more; - /** Construct a Varargs from an array of LuaValue. + /** Construct a Varargs from an array of LuaValue. *
* This is an internal class not intended to be used directly. - * Instead use the corresponding static methods on LuaValue. - * + * Instead use the corresponding static methods on LuaValue. + * * @see LuaValue#varargsOf(LuaValue[], int, int) */ ArrayPartVarargs(LuaValue[] v, int offset, int length) { @@ -657,11 +657,11 @@ public abstract class Varargs { this.length = length; this.more = LuaValue.NONE; } - /** Construct a Varargs from an array of LuaValue and additional arguments. + /** Construct a Varargs from an array of LuaValue and additional arguments. *
* This is an internal class not intended to be used directly. - * Instead use the corresponding static method on LuaValue. - * + * Instead use the corresponding static method on LuaValue. + * * @see LuaValue#varargsOf(LuaValue[], int, int, Varargs) */ public ArrayPartVarargs(LuaValue[] v, int offset, int length, Varargs more) { @@ -676,8 +676,8 @@ public abstract class Varargs { public int narg() { return length + more.narg(); } - public LuaValue arg1() { - return length>0? v[offset]: more.arg1(); + public LuaValue arg1() { + return length>0? v[offset]: more.arg1(); } public Varargs subargs(int start) { if (start <= 0) @@ -707,8 +707,10 @@ public abstract class Varargs { /** Return Varargs that cannot be using a shared array for the storage, and is flattened. * Internal utility method not intended to be called directly from user code. * @return Varargs containing same values, but flattened and with a new array if needed. + * @exclude + * @hide */ - Varargs dealias() { + public Varargs dealias() { int n = narg(); switch (n) { case 0: return LuaValue.NONE; diff --git a/src/core/org/luaj/vm2/compiler/DumpState.java b/src/core/org/luaj/vm2/compiler/DumpState.java index cc213315..3f92ba63 100644 --- a/src/core/org/luaj/vm2/compiler/DumpState.java +++ b/src/core/org/luaj/vm2/compiler/DumpState.java @@ -28,14 +28,14 @@ import java.io.OutputStream; import org.luaj.vm2.Globals; import org.luaj.vm2.LoadState; import org.luaj.vm2.LocVars; -import org.luaj.vm2.Prototype; import org.luaj.vm2.LuaString; import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Prototype; /** Class to dump a {@link Prototype} into an output stream, as part of compiling. *
- * Generally, this class is not used directly, but rather indirectly via a command + * Generally, this class is not used directly, but rather indirectly via a command * line interface tool such as {@link luac}. *
* A lua binary file is created via {@link DumpState#dump}: @@ -85,7 +85,7 @@ public class DumpState { public static final int NUMBER_FORMAT_DEFAULT = NUMBER_FORMAT_FLOATS_OR_DOUBLES; // header fields - private boolean IS_LITTLE_ENDIAN = false; + private boolean IS_LITTLE_ENDIAN = true; private int NUMBER_FORMAT = NUMBER_FORMAT_DEFAULT; private int SIZEOF_LUA_NUMBER = 8; private static final int SIZEOF_INT = 4; @@ -190,7 +190,7 @@ public class DumpState { dumpString((LuaString)o); break; default: - throw new IllegalArgumentException("bad type for " + o); + throw new IllegalArgumentException("bad type for " + o); } } n = f.p.length; diff --git a/src/core/org/luaj/vm2/compiler/LexState.java b/src/core/org/luaj/vm2/compiler/LexState.java index a02e465b..9972cd3e 100644 --- a/src/core/org/luaj/vm2/compiler/LexState.java +++ b/src/core/org/luaj/vm2/compiler/LexState.java @@ -198,7 +198,7 @@ public class LexState extends Constants { } private boolean isspace(int c) { - return (c <= ' '); + return (c >= 0 && c <= ' '); } @@ -388,8 +388,13 @@ public class LexState extends Constants { seminfo.r = LuaValue.ZERO; else if (str.indexOf('x')>=0 || str.indexOf('X')>=0) seminfo.r = strx2number(str, seminfo); - else - seminfo.r = LuaValue.valueOf(Double.parseDouble(str.trim())); + else { + try { + seminfo.r = LuaValue.valueOf(Double.parseDouble(str.trim())); + } catch (NumberFormatException e) { + lexerror("malformed number (" + e.getMessage() + ")", TK_NUMBER); + } + } return true; } @@ -408,7 +413,6 @@ public class LexState extends Constants { else break; } - save('\0'); String str = new String(buff, 0, nbuff); str2d(str, seminfo); } @@ -585,6 +589,13 @@ public class LexState extends Constants { inclinenumber(); continue; } + case ' ': + case '\f': + case '\t': + case 0x0B: /* \v */ { + nextChar(); + continue; + } case '-': { nextChar(); if (current != '-') @@ -688,19 +699,12 @@ public class LexState extends Constants { return TK_EOF; } default: { - if (isspace(current)) { - _assert (!currIsNewline()); - nextChar(); - continue; - } else if (isdigit(current)) { - read_numeral(seminfo); - return TK_NUMBER; - } else if (isalpha(current) || current == '_') { + if (isalpha(current) || current == '_') { /* identifier or reserved word */ LuaString ts; do { save_and_next(); - } while (isalnum(current) || current == '_'); + } while (isalnum(current)); ts = newstring(buff, 0, nbuff); if ( RESERVED.containsKey(ts) ) return ((Integer)RESERVED.get(ts)).intValue(); @@ -1744,7 +1748,7 @@ public class LexState extends Constants { fs.checkrepeated(dyd.label, dyd.n_label, label); /* check for repeated labels */ checknext(TK_DBCOLON); /* skip double colon */ /* create new entry for this label */ - l = newlabelentry(dyd.label=grow(dyd.label, dyd.n_label+1), dyd.n_label++, label, line, fs.pc); + l = newlabelentry(dyd.label=grow(dyd.label, dyd.n_label+1), dyd.n_label++, label, line, fs.getlabel()); skipnoopstat(); /* skip other no-op statements */ if (block_follow(false)) { /* label is last no-op statement in the block? */ /* assume that locals are already out of scope */ diff --git a/src/core/org/luaj/vm2/lib/BaseLib.java b/src/core/org/luaj/vm2/lib/BaseLib.java index bd63e4c1..035e7323 100644 --- a/src/core/org/luaj/vm2/lib/BaseLib.java +++ b/src/core/org/luaj/vm2/lib/BaseLib.java @@ -151,7 +151,7 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder { System.gc(); return LuaValue.TRUE; } else { - this.argerror("gc op"); + argerror(1, "invalid option '" + s + "'"); } return NIL; } @@ -172,16 +172,16 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder { // "error", // ( message [,level] ) -> ERR static final class error extends TwoArgFunction { public LuaValue call(LuaValue arg1, LuaValue arg2) { - throw arg1.isnil()? new LuaError(null, arg2.optint(1)): - arg1.isstring()? new LuaError(arg1.tojstring(), arg2.optint(1)): - new LuaError(arg1); + if (arg1.isnil()) throw new LuaError(NIL); + if (!arg1.isstring() || arg2.optint(1) == 0) throw new LuaError(arg1); + throw new LuaError(arg1.tojstring(), arg2.optint(1)); } } // "getmetatable", // ( object ) -> table static final class getmetatable extends LibFunction { public LuaValue call() { - return argerror(1, "value"); + return argerror(1, "value expected"); } public LuaValue call(LuaValue arg) { LuaValue mt = arg.getmetatable(); @@ -192,7 +192,9 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder { final class load extends VarArgFunction { public Varargs invoke(Varargs args) { LuaValue ld = args.arg1(); - args.argcheck(ld.isstring() || ld.isfunction(), 1, "ld must be string or function"); + if (!ld.isstring() && !ld.isfunction()) { + throw new LuaError("bad argument #1 to 'load' (string or function expected, got " + ld.typename() + ")"); + } String source = args.optjstring(2, ld.isstring()? ld.tojstring(): "=(load)"); String mode = args.optjstring(3, "bt"); LuaValue env = args.optvalue(4, globals); @@ -257,10 +259,10 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder { // "rawequal", // (v1, v2) -> boolean static final class rawequal extends LibFunction { public LuaValue call() { - return argerror(1, "value"); + return argerror(1, "value expected"); } public LuaValue call(LuaValue arg) { - return argerror(2, "value"); + return argerror(2, "value expected"); } public LuaValue call(LuaValue arg1, LuaValue arg2) { return valueOf(arg1.raweq(arg2)); @@ -268,12 +270,12 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder { } // "rawget", // (table, index) -> value - static final class rawget extends LibFunction { + static final class rawget extends TableLibFunction { public LuaValue call() { - return argerror(1, "value"); + return argerror(1, "value expected"); } public LuaValue call(LuaValue arg) { - return argerror(2, "value"); + return argerror(2, "value expected"); } public LuaValue call(LuaValue arg1, LuaValue arg2) { return arg1.checktable().rawget(arg2); @@ -289,16 +291,16 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder { } // "rawset", // (table, index, value) -> table - static final class rawset extends LibFunction { + static final class rawset extends TableLibFunction { public LuaValue call(LuaValue table) { - return argerror(2,"value"); + return argerror(2,"value expected"); } public LuaValue call(LuaValue table, LuaValue index) { - return argerror(3,"value"); + return argerror(3,"value expected"); } public LuaValue call(LuaValue table, LuaValue index, LuaValue value) { LuaTable t = table.checktable(); - if (!index.isvalidkey()) argerror(2, "value"); + if (!index.isvalidkey()) argerror(2, "table index is nil"); t.rawset(index, value); return t; } @@ -318,9 +320,9 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder { } // "setmetatable", // (table, metatable) -> table - static final class setmetatable extends LibFunction { + static final class setmetatable extends TableLibFunction { public LuaValue call(LuaValue table) { - return argerror(2,"value"); + return argerror(2,"nil or table expected"); } public LuaValue call(LuaValue table, LuaValue metatable) { final LuaValue mt0 = table.checktable().getmetatable(); @@ -471,10 +473,12 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder { this.func = func; } public int read() throws IOException { - if ( remaining <= 0 ) { + if ( remaining < 0 ) + return -1; + if ( remaining == 0 ) { LuaValue s = func.call(); if ( s.isnil() ) - return -1; + return remaining = -1; LuaString ls = s.strvalue(); bytes = ls.m_bytes; offset = ls.m_offset; diff --git a/src/core/org/luaj/vm2/lib/DebugLib.java b/src/core/org/luaj/vm2/lib/DebugLib.java index fd7c4c3c..167ea36c 100644 --- a/src/core/org/luaj/vm2/lib/DebugLib.java +++ b/src/core/org/luaj/vm2/lib/DebugLib.java @@ -442,6 +442,10 @@ public class DebugLib extends TwoArgFunction { return callstack().traceback(level); } + public CallFrame getCallFrame(int level) { + return callstack().getCallFrame(level); + } + void callHook(LuaThread.State s, LuaValue type, LuaValue arg) { if (s.inhook || s.hookfunc == null) return; s.inhook = true; @@ -645,7 +649,7 @@ public class DebugLib extends TwoArgFunction { } - static class CallFrame { + public static class CallFrame { LuaFunction f; int pc; int top; @@ -691,7 +695,7 @@ public class DebugLib extends TwoArgFunction { return NIL; } } - int currentline() { + public int currentline() { if ( !f.isclosure() ) return -1; int[] li = f.checkclosure().p.lineinfo; return li==null || pc<0 || pc>=li.length? -1: li[pc]; @@ -796,13 +800,13 @@ public class DebugLib extends TwoArgFunction { LuaString vn = (Lua.GET_OPCODE(i) == Lua.OP_GETTABLE) /* name of indexed variable */ ? p.getlocalname(t + 1, pc) : (t < p.upvalues.length ? p.upvalues[t].name : QMARK); - name = kname(p, k); - return new NameWhat( name.tojstring(), vn != null && vn.eq_b(ENV)? "global": "field" ); + String jname = kname(p, pc, k); + return new NameWhat( jname, vn != null && vn.eq_b(ENV)? "global": "field" ); } case Lua.OP_GETUPVAL: { int u = Lua.GETARG_B(i); /* upvalue index */ name = u < p.upvalues.length ? p.upvalues[u].name : QMARK; - return new NameWhat( name.tojstring(), "upvalue" ); + return name == null ? null : new NameWhat( name.tojstring(), "upvalue" ); } case Lua.OP_LOADK: case Lua.OP_LOADKX: { @@ -816,8 +820,8 @@ public class DebugLib extends TwoArgFunction { } case Lua.OP_SELF: { int k = Lua.GETARG_C(i); /* key index */ - name = kname(p, k); - return new NameWhat( name.tojstring(), "method" ); + String jname = kname(p, pc, k); + return new NameWhat( jname, "method" ); } default: break; @@ -826,11 +830,20 @@ public class DebugLib extends TwoArgFunction { return null; /* no useful name found */ } - static LuaString kname(Prototype p, int c) { - if (Lua.ISK(c) && p.k[Lua.INDEXK(c)].isstring()) - return p.k[Lua.INDEXK(c)].strvalue(); - else - return QMARK; + static String kname(Prototype p, int pc, int c) { + if (Lua.ISK(c)) { /* is 'c' a constant? */ + LuaValue k = p.k[Lua.INDEXK(c)]; + if (k.isstring()) { /* literal constant? */ + return k.tojstring(); /* it is its own name */ + } /* else no reasonable name found */ + } else { /* 'c' is a register */ + NameWhat what = getobjname(p, pc, c); /* search for 'c' */ + if (what != null && "constant".equals(what.namewhat)) { /* found a constant name? */ + return what.name; /* 'name' already filled */ + } + /* else no reasonable name found */ + } + return "?"; /* no reasonable name found */ } /* diff --git a/src/core/org/luaj/vm2/lib/IoLib.java b/src/core/org/luaj/vm2/lib/IoLib.java index fa41b7bf..13f6f986 100644 --- a/src/core/org/luaj/vm2/lib/IoLib.java +++ b/src/core/org/luaj/vm2/lib/IoLib.java @@ -95,6 +95,12 @@ public class IoLib extends TwoArgFunction { // return number of bytes read if positive, false if eof, throw IOException on other exception abstract public int read(byte[] bytes, int offset, int length) throws IOException; + public boolean eof() throws IOException { + try { + return peek() < 0; + } catch (EOFException e) { return true; } + } + // delegate method access to file methods table public LuaValue get( LuaValue key ) { return filemethods.get(key); @@ -112,6 +118,14 @@ public class IoLib extends TwoArgFunction { public String tojstring() { return "file: " + Integer.toHexString(hashCode()); } + + public void finalize() { + if (!isclosed()) { + try { + close(); + } catch (IOException ignore) {} + } + } } /** Enumerated value representing stdin */ @@ -269,8 +283,15 @@ public class IoLib extends TwoArgFunction { static final class IoLibV extends VarArgFunction { private File f; public IoLib iolib; + private boolean toclose; + private Varargs args; public IoLibV() { } + public IoLibV(File f, String name, int opcode, IoLib iolib, boolean toclose, Varargs args) { + this(f, name, opcode, iolib); + this.toclose = toclose; + this.args = args.dealias(); + } public IoLibV(File f, String name, int opcode, IoLib iolib) { super(); this.f = f; @@ -290,22 +311,26 @@ public class IoLib extends TwoArgFunction { case IO_TYPE: return iolib._io_type(args.arg1()); case IO_POPEN: return iolib._io_popen(args.checkjstring(1),args.optjstring(2,"r")); case IO_OPEN: return iolib._io_open(args.checkjstring(1), args.optjstring(2,"r")); - case IO_LINES: return iolib._io_lines(args.isvalue(1)? args.checkjstring(1): null); + case IO_LINES: return iolib._io_lines(args); case IO_READ: return iolib._io_read(args); case IO_WRITE: return iolib._io_write(args); case FILE_CLOSE: return iolib._file_close(args.arg1()); case FILE_FLUSH: return iolib._file_flush(args.arg1()); - case FILE_SETVBUF: return iolib._file_setvbuf(args.arg1(),args.checkjstring(2),args.optint(3,1024)); - case FILE_LINES: return iolib._file_lines(args.arg1()); + case FILE_SETVBUF: return iolib._file_setvbuf(args.arg1(),args.checkjstring(2),args.optint(3,8192)); + case FILE_LINES: return iolib._file_lines(args); case FILE_READ: return iolib._file_read(args.arg1(),args.subargs(2)); case FILE_SEEK: return iolib._file_seek(args.arg1(),args.optjstring(2,"cur"),args.optint(3,0)); case FILE_WRITE: return iolib._file_write(args.arg1(),args.subargs(2)); case IO_INDEX: return iolib._io_index(args.arg(2)); - case LINES_ITER: return iolib._lines_iter(f); + case LINES_ITER: return iolib._lines_iter(f, toclose, this.args); } } catch ( IOException ioe ) { + if (opcode == LINES_ITER) { + String s = ioe.getMessage(); + error(s != null ? s : ioe.toString()); + } return errorresult(ioe); } return NONE; @@ -361,6 +386,7 @@ public class IoLib extends TwoArgFunction { // io.popen(prog, [mode]) -> file public Varargs _io_popen(String prog, String mode) throws IOException { + if (!"r".equals(mode) && !"w".equals(mode)) argerror(2, "invalid value: '" + mode + "'; must be one of 'r' or 'w'"); return openProgram(prog, mode); } @@ -369,11 +395,12 @@ public class IoLib extends TwoArgFunction { return rawopenfile(FTYPE_NAMED, filename, mode); } - // io.lines(filename) -> iterator - public Varargs _io_lines(String filename) { - infile = filename==null? input(): ioopenfile(FTYPE_NAMED, filename,"r"); + // io.lines(filename, ...) -> iterator + public Varargs _io_lines(Varargs args) { + String filename = args.optjstring(1, null); + File infile = filename==null? input(): ioopenfile(FTYPE_NAMED, filename,"r"); checkopen(infile); - return lines(infile); + return lines(infile, filename != null, args.subargs(2)); } // io.read(...) -> (...) @@ -401,13 +428,19 @@ public class IoLib extends TwoArgFunction { // file:setvbuf(mode,[size]) -> void public Varargs _file_setvbuf(LuaValue file, String mode, int size) { + if ("no".equals(mode)) { + } else if ("full".equals(mode)) { + } else if ("line".equals(mode)) { + } else { + argerror(1, "invalid value: '" + mode + "'; must be one of 'no', 'full' or 'line'"); + } checkfile(file).setvbuf(mode,size); return LuaValue.TRUE; } - // file:lines() -> iterator - public Varargs _file_lines(LuaValue file) { - return lines(checkfile(file)); + // file:lines(...) -> iterator + public Varargs _file_lines(Varargs args) { + return lines(checkfile(args.arg1()), false, args.subargs(2)); } // file:read(...) -> (...) @@ -417,6 +450,12 @@ public class IoLib extends TwoArgFunction { // file:seek([whence][,offset]) -> pos | nil,error public Varargs _file_seek(LuaValue file, String whence, int offset) throws IOException { + if ("set".equals(whence)) { + } else if ("end".equals(whence)) { + } else if ("cur".equals(whence)) { + } else { + argerror(1, "invalid value: '" + whence + "'; must be one of 'set', 'cur' or 'end'"); + } return valueOf( checkfile(file).seek(whence,offset) ); } @@ -433,8 +472,13 @@ public class IoLib extends TwoArgFunction { } // lines iterator(s,var) -> var' - public Varargs _lines_iter(LuaValue file) throws IOException { - return freadline(checkfile(file)); + public Varargs _lines_iter(LuaValue file, boolean toclose, Varargs args) throws IOException { + File f = optfile(file); + if ( f == null ) argerror(1, "not a file: " + file); + if ( f.isclosed() ) error("file is already closed"); + Varargs ret = ioread(f, args); + if (toclose && ret.isnil(1) && f.eof()) f.close(); + return ret; } private File output() { @@ -476,9 +520,9 @@ public class IoLib extends TwoArgFunction { return varargsOf(NIL, valueOf(errortext)); } - private Varargs lines(final File f) { + private Varargs lines(final File f, boolean toclose, Varargs args) { try { - return new IoLibV(f,"lnext",LINES_ITER,this); + return new IoLibV(f,"lnext",LINES_ITER,this,toclose,args); } catch ( Exception e ) { return error("lines: "+e); } @@ -492,6 +536,7 @@ public class IoLib extends TwoArgFunction { private Varargs ioread(File f, Varargs args) throws IOException { int i,n=args.narg(); + if (n == 0) return freadline(f,false); LuaValue[] v = new LuaValue[n]; LuaValue ai,vi; LuaString fmt; @@ -502,10 +547,11 @@ public class IoLib extends TwoArgFunction { break item; case LuaValue.TSTRING: fmt = ai.checkstring(); - if ( fmt.m_length == 2 && fmt.m_bytes[fmt.m_offset] == '*' ) { + if ( fmt.m_length >= 2 && fmt.m_bytes[fmt.m_offset] == '*' ) { switch ( fmt.m_bytes[fmt.m_offset+1] ) { case 'n': vi = freadnumber(f); break item; - case 'l': vi = freadline(f); break item; + case 'l': vi = freadline(f,false); break item; + case 'L': vi = freadline(f,true); break item; case 'a': vi = freadall(f); break item; } } @@ -537,6 +583,17 @@ public class IoLib extends TwoArgFunction { } private File rawopenfile(int filetype, String filename, String mode) throws IOException { + int len = mode.length(); + for (int i = 0; i < len; i++) { // [rwa][+]?b* + char ch = mode.charAt(i); + if (i == 0 && "rwa".indexOf(ch) >= 0) continue; + if (i == 1 && ch == '+') continue; + if (i >= 1 && ch == 'b') continue; + len = -1; + break; + } + if (len <= 0) argerror(2, "invalid mode: '" + mode + "'"); + switch (filetype) { case FTYPE_STDIN: return wrapStdin(); case FTYPE_STDOUT: return wrapStdout(); @@ -553,26 +610,27 @@ public class IoLib extends TwoArgFunction { // ------------- file reading utilitied ------------------ public static LuaValue freadbytes(File f, int count) throws IOException { + if (count == 0) return f.eof() ? NIL : EMPTYSTRING; byte[] b = new byte[count]; int r; if ( ( r = f.read(b,0,b.length) ) < 0 ) return NIL; return LuaString.valueUsing(b, 0, r); } - public static LuaValue freaduntil(File f,boolean lineonly) throws IOException { + public static LuaValue freaduntil(File f,boolean lineonly,boolean withend) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); int c; try { if ( lineonly ) { - loop: while ( (c = f.read()) > 0 ) { + loop: while ( (c = f.read()) >= 0 ) { switch ( c ) { - case '\r': break; - case '\n': break loop; + case '\r': if (withend) baos.write(c); break; + case '\n': if (withend) baos.write(c); break loop; default: baos.write(c); break; } } } else { - while ( (c = f.read()) > 0 ) + while ( (c = f.read()) >= 0 ) baos.write(c); } } catch ( EOFException e ) { @@ -582,15 +640,15 @@ public class IoLib extends TwoArgFunction { (LuaValue) NIL: (LuaValue) LuaString.valueUsing(baos.toByteArray()); } - public static LuaValue freadline(File f) throws IOException { - return freaduntil(f,true); + public static LuaValue freadline(File f,boolean withend) throws IOException { + return freaduntil(f,true,withend); } public static LuaValue freadall(File f) throws IOException { int n = f.remaining(); if ( n >= 0 ) { - return freadbytes(f, n); + return n == 0 ? EMPTYSTRING : freadbytes(f, n); } else { - return freaduntil(f,false); + return freaduntil(f,false,false); } } public static LuaValue freadnumber(File f) throws IOException { diff --git a/src/core/org/luaj/vm2/lib/LibFunction.java b/src/core/org/luaj/vm2/lib/LibFunction.java index 5a654170..eba56072 100644 --- a/src/core/org/luaj/vm2/lib/LibFunction.java +++ b/src/core/org/luaj/vm2/lib/LibFunction.java @@ -27,21 +27,21 @@ import org.luaj.vm2.LuaValue; import org.luaj.vm2.Varargs; /** - * Subclass of {@link LuaFunction} common to Java functions exposed to lua. + * Subclass of {@link LuaFunction} common to Java functions exposed to lua. *
- * To provide for common implementations in JME and JSE, + * To provide for common implementations in JME and JSE, * library functions are typically grouped on one or more library classes * and an opcode per library function is defined and used to key the switch - * to the correct function within the library. + * to the correct function within the library. *
- * Since lua functions can be called with too few or too many arguments, - * and there are overloaded {@link LuaValue#call()} functions with varying + * Since lua functions can be called with too few or too many arguments, + * and there are overloaded {@link LuaValue#call()} functions with varying * number of arguments, a Java function exposed in lua needs to handle the - * argument fixup when a function is called with a number of arguments - * differs from that expected. + * argument fixup when a function is called with a number of arguments + * differs from that expected. *
- * To simplify the creation of library functions, - * there are 5 direct subclasses to handle common cases based on number of + * To simplify the creation of library functions, + * there are 5 direct subclasses to handle common cases based on number of * argument values and number of return return values. *
- * To be a Java library that can be loaded via {@code require}, it should have - * a public constructor that returns a {@link LuaValue} that, when executed, - * initializes the library. - *
- * For example, the following code will implement a library called "hyperbolic" + * To be a Java library that can be loaded via {@code require}, it should have + * a public constructor that returns a {@link LuaValue} that, when executed, + * initializes the library. + *
+ * For example, the following code will implement a library called "hyperbolic" * with two functions, "sinh", and "cosh": -
{@code
+ {@code
* import org.luaj.vm2.LuaValue;
* import org.luaj.vm2.lib.*;
*
@@ -86,13 +86,13 @@ import org.luaj.vm2.Varargs;
* }
*}
*}
- * The default constructor is used to instantiate the library
+ * The default constructor is used to instantiate the library
* in response to {@code require 'hyperbolic'} statement,
- * provided it is on Java"s class path.
- * This instance is then invoked with 2 arguments: the name supplied to require(),
- * and the environment for this function. The library may ignore these, or use
- * them to leave side effects in the global environment, for example.
- * In the previous example, two functions are created, 'sinh', and 'cosh', and placed
+ * provided it is on Java"s class path.
+ * This instance is then invoked with 2 arguments: the name supplied to require(),
+ * and the environment for this function. The library may ignore these, or use
+ * them to leave side effects in the global environment, for example.
+ * In the previous example, two functions are created, 'sinh', and 'cosh', and placed
* into a global table called 'hyperbolic' using the supplied 'env' argument.
*
* To test it, a script such as this can be used:
@@ -105,7 +105,7 @@ import org.luaj.vm2.Varargs;
* end
* print( 'sinh(.5)', hyperbolic.sinh(.5) )
* print( 'cosh(.5)', hyperbolic.cosh(.5) )
- * }
+ * }
* * It should produce something like: *
{@code
@@ -115,16 +115,16 @@ import org.luaj.vm2.Varargs;
* k,v sinh function: 3dbbd242
* sinh(.5) 0.5210953
* cosh(.5) 1.127626
- * }
- * - * See the source code in any of the library functions - * such as {@link BaseLib} or {@link TableLib} for other examples. + * } + *
+ * See the source code in any of the library functions + * such as {@link BaseLib} or {@link TableLib} for other examples. */ abstract public class LibFunction extends LuaFunction { - /** User-defined opcode to differentiate between instances of the library function class. + /** User-defined opcode to differentiate between instances of the library function class. *
- * Subclass will typicall switch on this value to provide the specific behavior for each function. + * Subclass will typicall switch on this value to provide the specific behavior for each function. */ protected int opcode; @@ -135,37 +135,37 @@ abstract public class LibFunction extends LuaFunction { protected String name; /** Default constructor for use by subclasses */ - protected LibFunction() { + protected LibFunction() { } public String tojstring() { return name != null ? "function: " + name : super.tojstring(); } - /** - * Bind a set of library functions. + /** + * Bind a set of library functions. *
- * An array of names is provided, and the first name is bound - * with opcode = 0, second with 1, etc. - * @param env The environment to apply to each bound function + * An array of names is provided, and the first name is bound + * with opcode = 0, second with 1, etc. + * @param env The environment to apply to each bound function * @param factory the Class to instantiate for each bound function * @param names array of String names, one for each function. - * @see #bind(LuaValue, Class, String[], int) + * @see #bind(LuaValue, Class, String[], int) */ protected void bind(LuaValue env, Class factory, String[] names ) { bind( env, factory, names, 0 ); } - /** - * Bind a set of library functions, with an offset + /** + * Bind a set of library functions, with an offset *
- * An array of names is provided, and the first name is bound - * with opcode = {@code firstopcode}, second with {@code firstopcode+1}, etc. - * @param env The environment to apply to each bound function + * An array of names is provided, and the first name is bound + * with opcode = {@code firstopcode}, second with {@code firstopcode+1}, etc. + * @param env The environment to apply to each bound function * @param factory the Class to instantiate for each bound function * @param names array of String names, one for each function. - * @param firstopcode the first opcode to use - * @see #bind(LuaValue, Class, String[]) + * @param firstopcode the first opcode to use + * @see #bind(LuaValue, Class, String[]) */ protected void bind(LuaValue env, Class factory, String[] names, int firstopcode ) { try { @@ -178,7 +178,7 @@ abstract public class LibFunction extends LuaFunction { } catch ( Exception e ) { throw new LuaError( "bind failed: "+e ); } - } + } /** Java code generation utility to allocate storage for upvalue, leave it empty */ protected static LuaValue[] newupe() { @@ -219,4 +219,4 @@ abstract public class LibFunction extends LuaFunction { default: return call(args.arg1(),args.arg(2),args.arg(3),args.arg(4)); } } -} +} diff --git a/src/core/org/luaj/vm2/lib/PackageLib.java b/src/core/org/luaj/vm2/lib/PackageLib.java index db344818..4c2147c9 100644 --- a/src/core/org/luaj/vm2/lib/PackageLib.java +++ b/src/core/org/luaj/vm2/lib/PackageLib.java @@ -144,6 +144,7 @@ public class PackageLib extends TwoArgFunction { searchers.set(2, lua_searcher = new lua_searcher()); searchers.set(3, java_searcher = new java_searcher()); package_.set(_SEARCHERS, searchers); + package_.set("config", FILE_SEP + "\n;\n?\n!\n-\n"); package_.get(_LOADED).set("package", package_); env.set("package", package_); globals.package_ = this; diff --git a/src/core/org/luaj/vm2/lib/StringLib.java b/src/core/org/luaj/vm2/lib/StringLib.java index 58d24438..44919817 100644 --- a/src/core/org/luaj/vm2/lib/StringLib.java +++ b/src/core/org/luaj/vm2/lib/StringLib.java @@ -79,8 +79,8 @@ public class StringLib extends TwoArgFunction { */ public LuaValue call(LuaValue modname, LuaValue env) { LuaTable string = new LuaTable(); - string.set("byte", new byte_()); - string.set("char", new char_()); + string.set("byte", new _byte()); + string.set("char", new _char()); string.set("dump", new dump()); string.set("find", new find()); string.set("format", new format()); @@ -113,7 +113,7 @@ public class StringLib extends TwoArgFunction { * * @param args the calling args */ - static final class byte_ extends VarArgFunction { + static final class _byte extends VarArgFunction { public Varargs invoke(Varargs args) { LuaString s = args.checkstring(1); int l = s.m_length; @@ -144,7 +144,7 @@ public class StringLib extends TwoArgFunction { * * @param args the calling VM */ - static final class char_ extends VarArgFunction { + static final class _char extends VarArgFunction { public Varargs invoke(Varargs args) { int n = args.narg(); byte[] bytes = new byte[n]; @@ -624,19 +624,20 @@ public class StringLib extends TwoArgFunction { private final int srclen; private final MatchState ms; private int soffset; + private int lastmatch; public GMatchAux(Varargs args, LuaString src, LuaString pat) { this.srclen = src.length(); this.ms = new MatchState(args, src, pat); this.soffset = 0; + this.lastmatch = -1; } public Varargs invoke(Varargs args) { for ( ; soffset<=srclen; soffset++ ) { ms.reset(); int res = ms.match(soffset, 0); - if ( res >=0 ) { + if ( res >=0 && res != lastmatch ) { int soff = soffset; - soffset = res; - if (soff == res) soffset++; /* empty match? go at least one position */ + lastmatch = soffset = res; return ms.push_captures( true, soff, res ); } } @@ -695,6 +696,7 @@ public class StringLib extends TwoArgFunction { LuaString src = args.checkstring( 1 ); final int srclen = src.length(); LuaString p = args.checkstring( 2 ); + int lastmatch = -1; /* end of last match */ LuaValue repl = args.arg( 3 ); int max_s = args.optint( 4, srclen + 1 ); final boolean anchor = p.length() > 0 && p.charAt( 0 ) == '^'; @@ -707,18 +709,15 @@ public class StringLib extends TwoArgFunction { while ( n < max_s ) { ms.reset(); int res = ms.match( soffset, anchor ? 1 : 0 ); - if ( res != -1 ) { + if ( res != -1 && res != lastmatch ) { /* match? */ n++; - ms.add_value( lbuf, soffset, res, repl ); + ms.add_value( lbuf, soffset, res, repl ); /* add replacement to buffer */ + soffset = lastmatch = res; } - if ( res != -1 && res > soffset ) - soffset = res; - else if ( soffset < srclen ) + else if ( soffset < srclen ) /* otherwise, skip one character */ lbuf.append( (byte) src.luaByte( soffset++ ) ); - else - break; - if ( anchor ) - break; + else break; /* end of subject */ + if ( anchor ) break; } lbuf.append( src.substring( soffset, srclen ) ); return varargsOf(lbuf.tostring(), valueOf(n)); @@ -914,6 +913,8 @@ public class StringLib extends TwoArgFunction { private static final LuaString SPECIALS = valueOf("^$*+?.([%-"); private static final int MAX_CAPTURES = 32; + private static final int MAXCCALLS = 200; + private static final int CAP_UNFINISHED = -1; private static final int CAP_POSITION = -2; @@ -931,7 +932,7 @@ public class StringLib extends TwoArgFunction { static { CHAR_TABLE = new byte[256]; - for ( int i = 0; i < 256; ++i ) { + for ( int i = 0; i < 128; ++i ) { final char c = (char) i; CHAR_TABLE[i] = (byte)( ( Character.isDigit( c ) ? MASK_DIGIT : 0 ) | ( Character.isLowerCase( c ) ? MASK_LOWERCASE : 0 ) | @@ -940,7 +941,7 @@ public class StringLib extends TwoArgFunction { if ( ( c >= 'a' && c <= 'f' ) || ( c >= 'A' && c <= 'F' ) || ( c >= '0' && c <= '9' ) ) { CHAR_TABLE[i] |= MASK_HEXDIGIT; } - if ( ( c >= '!' && c <= '/' ) || ( c >= ':' && c <= '@' ) ) { + if ( ( c >= '!' && c <= '/' ) || ( c >= ':' && c <= '@' ) || ( c >= '[' && c <= '`' ) || ( c >= '{' && c <= '~' ) ) { CHAR_TABLE[i] |= MASK_PUNCT; } if ( ( CHAR_TABLE[i] & ( MASK_LOWERCASE | MASK_UPPERCASE ) ) != 0 ) { @@ -952,11 +953,12 @@ public class StringLib extends TwoArgFunction { CHAR_TABLE['\r'] |= MASK_SPACE; CHAR_TABLE['\n'] |= MASK_SPACE; CHAR_TABLE['\t'] |= MASK_SPACE; - CHAR_TABLE[0x0C /* '\v' */ ] |= MASK_SPACE; + CHAR_TABLE[0x0B /* '\v' */ ] |= MASK_SPACE; CHAR_TABLE['\f'] |= MASK_SPACE; }; static class MatchState { + int matchdepth; /* control for recursive depth (to avoid C stack overflow) */ final LuaString s; final LuaString p; final Varargs args; @@ -971,10 +973,12 @@ public class StringLib extends TwoArgFunction { this.level = 0; this.cinit = new int[ MAX_CAPTURES ]; this.clen = new int[ MAX_CAPTURES ]; + this.matchdepth = MAXCCALLS; } void reset() { level = 0; + this.matchdepth = MAXCCALLS; } private void add_s( Buffer lbuf, LuaString news, int soff, int e ) { @@ -1049,7 +1053,7 @@ public class StringLib extends TwoArgFunction { if ( i == 0 ) { return s.substring( soff, end ); } else { - return error( "invalid capture index" ); + return error( "invalid capture index %" + (i + 1) ); } } else { int l = clen[i]; @@ -1068,7 +1072,7 @@ public class StringLib extends TwoArgFunction { private int check_capture( int l ) { l -= '1'; if ( l < 0 || l >= level || this.clen[l] == CAP_UNFINISHED ) { - error("invalid capture index"); + error("invalid capture index %" + (l + 1)); } return l; } @@ -1118,9 +1122,10 @@ public class StringLib extends TwoArgFunction { case 'c': res = ( cdata & MASK_CONTROL ) != 0; break; case 'p': res = ( cdata & MASK_PUNCT ) != 0; break; case 's': res = ( cdata & MASK_SPACE ) != 0; break; + case 'g': res = ( cdata & ( MASK_ALPHA | MASK_DIGIT | MASK_PUNCT ) ) != 0; break; case 'w': res = ( cdata & ( MASK_ALPHA | MASK_DIGIT ) ) != 0; break; case 'x': res = ( cdata & MASK_HEXDIGIT ) != 0; break; - case 'z': res = ( c == 0 ); break; + case 'z': res = ( c == 0 ); break; /* deprecated option */ default: return cl == c; } return ( lcl == cl ) ? res : !res; @@ -1162,81 +1167,86 @@ public class StringLib extends TwoArgFunction { * where match ends, otherwise returns -1. */ int match( int soffset, int poffset ) { - while ( true ) { - // Check if we are at the end of the pattern - - // equivalent to the '\0' case in the C version, but our pattern - // string is not NUL-terminated. - if ( poffset == p.length() ) - return soffset; - switch ( p.luaByte( poffset ) ) { - case '(': - if ( ++poffset < p.length() && p.luaByte( poffset ) == ')' ) - return start_capture( soffset, poffset + 1, CAP_POSITION ); - else - return start_capture( soffset, poffset, CAP_UNFINISHED ); - case ')': - return end_capture( soffset, poffset + 1 ); - case L_ESC: - if ( poffset + 1 == p.length() ) - error("malformed pattern (ends with '%')"); - switch ( p.luaByte( poffset + 1 ) ) { - case 'b': - soffset = matchbalance( soffset, poffset + 2 ); - if ( soffset == -1 ) return -1; - poffset += 4; - continue; - case 'f': { - poffset += 2; - if ( poffset == p.length() || p.luaByte( poffset ) != '[' ) { - error("Missing '[' after '%f' in pattern"); + if (matchdepth-- == 0) error("pattern too complex"); + try { + while ( true ) { + // Check if we are at the end of the pattern - + // equivalent to the '\0' case in the C version, but our pattern + // string is not NUL-terminated. + if ( poffset == p.length() ) + return soffset; + switch ( p.luaByte( poffset ) ) { + case '(': + if ( ++poffset < p.length() && p.luaByte( poffset ) == ')' ) + return start_capture( soffset, poffset + 1, CAP_POSITION ); + else + return start_capture( soffset, poffset, CAP_UNFINISHED ); + case ')': + return end_capture( soffset, poffset + 1 ); + case L_ESC: + if ( poffset + 1 == p.length() ) + error("malformed pattern (ends with '%')"); + switch ( p.luaByte( poffset + 1 ) ) { + case 'b': + soffset = matchbalance( soffset, poffset + 2 ); + if ( soffset == -1 ) return -1; + poffset += 4; + continue; + case 'f': { + poffset += 2; + if ( poffset == p.length() || p.luaByte( poffset ) != '[' ) { + error("missing '[' after '%f' in pattern"); + } + int ep = classend( poffset ); + int previous = ( soffset == 0 ) ? '\0' : s.luaByte( soffset - 1 ); + int next = ( soffset == s.length() ) ? '\0' : s.luaByte( soffset ); + if ( matchbracketclass( previous, poffset, ep - 1 ) || + !matchbracketclass( next, poffset, ep - 1 ) ) + return -1; + poffset = ep; + continue; } - int ep = classend( poffset ); - int previous = ( soffset == 0 ) ? '\0' : s.luaByte( soffset - 1 ); - int next = ( soffset == s.length() ) ? '\0' : s.luaByte( soffset ); - if ( matchbracketclass( previous, poffset, ep - 1 ) || - !matchbracketclass( next, poffset, ep - 1 ) ) + default: { + int c = p.luaByte( poffset + 1 ); + if ( Character.isDigit( (char) c ) ) { + soffset = match_capture( soffset, c ); + if ( soffset == -1 ) + return -1; + return match( soffset, poffset + 2 ); + } + } + } + case '$': + if ( poffset + 1 == p.length() ) + return ( soffset == s.length() ) ? soffset : -1; + } + int ep = classend( poffset ); + boolean m = soffset < s.length() && singlematch( s.luaByte( soffset ), poffset, ep ); + int pc = ( ep < p.length() ) ? p.luaByte( ep ) : '\0'; + + switch ( pc ) { + case '?': + int res; + if ( m && ( ( res = match( soffset + 1, ep + 1 ) ) != -1 ) ) + return res; + poffset = ep + 1; + continue; + case '*': + return max_expand( soffset, poffset, ep ); + case '+': + return ( m ? max_expand( soffset + 1, poffset, ep ) : -1 ); + case '-': + return min_expand( soffset, poffset, ep ); + default: + if ( !m ) return -1; + soffset++; poffset = ep; continue; } - default: { - int c = p.luaByte( poffset + 1 ); - if ( Character.isDigit( (char) c ) ) { - soffset = match_capture( soffset, c ); - if ( soffset == -1 ) - return -1; - return match( soffset, poffset + 2 ); - } - } - } - case '$': - if ( poffset + 1 == p.length() ) - return ( soffset == s.length() ) ? soffset : -1; - } - int ep = classend( poffset ); - boolean m = soffset < s.length() && singlematch( s.luaByte( soffset ), poffset, ep ); - int pc = ( ep < p.length() ) ? p.luaByte( ep ) : '\0'; - - switch ( pc ) { - case '?': - int res; - if ( m && ( ( res = match( soffset + 1, ep + 1 ) ) != -1 ) ) - return res; - poffset = ep + 1; - continue; - case '*': - return max_expand( soffset, poffset, ep ); - case '+': - return ( m ? max_expand( soffset + 1, poffset, ep ) : -1 ); - case '-': - return min_expand( soffset, poffset, ep ); - default: - if ( !m ) - return -1; - soffset++; - poffset = ep; - continue; } + } finally { + matchdepth++; } } @@ -1301,7 +1311,7 @@ public class StringLib extends TwoArgFunction { int matchbalance( int soff, int poff ) { final int plen = p.length(); if ( poff == plen || poff + 1 == plen ) { - error( "unbalanced pattern" ); + error( "malformed pattern (missing arguments to '%b')" ); } final int slen = s.length(); if ( soff >= slen ) diff --git a/src/core/org/luaj/vm2/lib/TableLib.java b/src/core/org/luaj/vm2/lib/TableLib.java index 74456952..64959277 100644 --- a/src/core/org/luaj/vm2/lib/TableLib.java +++ b/src/core/org/luaj/vm2/lib/TableLib.java @@ -73,12 +73,6 @@ public class TableLib extends TwoArgFunction { if (!env.get("package").isnil()) env.get("package").get("loaded").set("table", table); return NIL; } - - static class TableLibFunction extends LibFunction { - public LuaValue call() { - return argerror(1, "table expected, got no value"); - } - } // "concat" (table [, sep [, i [, j]]]) -> string static class concat extends TableLibFunction { @@ -100,18 +94,22 @@ public class TableLib extends TwoArgFunction { static class insert extends VarArgFunction { public Varargs invoke(Varargs args) { switch (args.narg()) { - case 0: case 1: { - return argerror(2, "value expected"); - } case 2: { - LuaTable table = args.arg1().checktable(); + LuaTable table = args.checktable(1); table.insert(table.length()+1,args.arg(2)); return NONE; } - default: { - args.arg1().checktable().insert(args.checkint(2),args.arg(3)); + case 3: { + LuaTable table = args.checktable(1); + int pos = args.checkint(2); + int max = table.length() + 1; + if (pos < 1 || pos > max) argerror(2, "position out of bounds: " + pos + " not between 1 and " + max); + table.insert(pos, args.arg(3)); return NONE; } + default: { + return error("wrong number of arguments to 'table.insert': " + args.narg() + " (must be 2 or 3)"); + } } } } @@ -128,15 +126,21 @@ public class TableLib extends TwoArgFunction { // "remove" (table [, pos]) -> removed-ele static class remove extends VarArgFunction { public Varargs invoke(Varargs args) { - return args.arg1().checktable().remove(args.optint(2, 0)); + LuaTable table = args.checktable(1); + int size = table.length(); + int pos = args.optint(2, size); + if (pos != size && (pos < 1 || pos > size + 1)) { + argerror(2, "position out of bounds: " + pos + " not between 1 and " + (size + 1)); + } + return table.remove(pos); } } // "sort" (table [, comp]) static class sort extends VarArgFunction { public Varargs invoke(Varargs args) { - args.arg1().checktable().sort( - args.arg(2).isnil()? NIL: args.arg(2).checkfunction()); + args.checktable(1).sort( + args.isnil(2)? NIL: args.checkfunction(2)); return NONE; } } @@ -146,11 +150,9 @@ public class TableLib extends TwoArgFunction { static class unpack extends VarArgFunction { public Varargs invoke(Varargs args) { LuaTable t = args.checktable(1); - switch (args.narg()) { - case 1: return t.unpack(); - case 2: return t.unpack(args.checkint(2)); - default: return t.unpack(args.checkint(2), args.checkint(3)); - } + // do not waste resource for calc rawlen if arg3 is not nil + int len = args.arg(3).isnil() ? t.length() : 0; + return t.unpack(args.optint(2, 1), args.optint(3, len)); } } } diff --git a/src/core/org/luaj/vm2/lib/TableLibFunction.java b/src/core/org/luaj/vm2/lib/TableLibFunction.java new file mode 100644 index 00000000..5fabef7e --- /dev/null +++ b/src/core/org/luaj/vm2/lib/TableLibFunction.java @@ -0,0 +1,9 @@ +package org.luaj.vm2.lib; + +import org.luaj.vm2.LuaValue; + +class TableLibFunction extends LibFunction { + public LuaValue call() { + return argerror(1, "table expected, got no value"); + } +} diff --git a/src/jse/org/luaj/vm2/lib/jse/JseIoLib.java b/src/jse/org/luaj/vm2/lib/jse/JseIoLib.java index 9655de26..cfaf6f4a 100644 --- a/src/jse/org/luaj/vm2/lib/jse/JseIoLib.java +++ b/src/jse/org/luaj/vm2/lib/jse/JseIoLib.java @@ -36,20 +36,20 @@ import org.luaj.vm2.LuaValue; import org.luaj.vm2.lib.IoLib; import org.luaj.vm2.lib.LibFunction; -/** - * Subclass of {@link IoLib} and therefore {@link LibFunction} which implements the lua standard {@code io} - * library for the JSE platform. - *
- * It uses RandomAccessFile to implement seek on files. +/** + * Subclass of {@link IoLib} and therefore {@link LibFunction} which implements the lua standard {@code io} + * library for the JSE platform. *
- * Typically, this library is included as part of a call to + * It uses RandomAccessFile to implement seek on files. + *
+ * Typically, this library is included as part of a call to * {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} *
{@code
* Globals globals = JsePlatform.standardGlobals();
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
* }
* - * For special cases where the smallest possible footprint is desired, + * For special cases where the smallest possible footprint is desired, * a minimal set of libraries could be loaded * directly via {@link Globals#load(LuaValue)} using code such as: *
{@code
@@ -96,9 +96,9 @@ public class JseIoLib extends IoLib {
protected File openProgram(String prog, String mode) throws IOException {
final Process p = Runtime.getRuntime().exec(prog);
- return "w".equals(mode)?
- new FileImpl( p.getOutputStream() ):
- new FileImpl( p.getInputStream() );
+ return "w".equals(mode)?
+ new FileImpl( p.getOutputStream() ):
+ new FileImpl( p.getInputStream() );
}
protected File tmpFile() throws IOException {
@@ -133,7 +133,7 @@ public class JseIoLib extends IoLib {
this( null, null, o );
}
public String tojstring() {
- return "file ("+this.hashCode()+")";
+ return "file (" + (this.closed ? "closed" : String.valueOf(this.hashCode())) + ")";
}
public boolean isstdfile() {
return file == null;
@@ -199,11 +199,11 @@ public class JseIoLib extends IoLib {
}
notimplemented();
return 0;
- }
+ }
- // return char if read, -1 if eof, throw IOException on other exception
+ // return char if read, -1 if eof, throw IOException on other exception
public int read() throws IOException {
- if ( is != null )
+ if ( is != null )
return is.read();
else if ( file != null ) {
return file.read();
@@ -321,7 +321,7 @@ public class JseIoLib extends IoLib {
}
public int remaining() throws IOException {
- return 0;
+ return -1;
}
public int peek() throws IOException, EOFException {
diff --git a/src/jse/org/luaj/vm2/lib/jse/JseOsLib.java b/src/jse/org/luaj/vm2/lib/jse/JseOsLib.java
index 79d67d9f..af63c8af 100644
--- a/src/jse/org/luaj/vm2/lib/jse/JseOsLib.java
+++ b/src/jse/org/luaj/vm2/lib/jse/JseOsLib.java
@@ -33,8 +33,8 @@ import org.luaj.vm2.lib.OsLib;
/**
* Subclass of {@link LibFunction} which implements the standard lua {@code os} library.
*
- * This contains more complete implementations of the following functions
- * using features that are specific to JSE:
+ * This contains more complete implementations of the following functions
+ * using features that are specific to JSE:
*
- * Because the nature of the {@code os} library is to encapsulate - * os-specific features, the behavior of these functions varies considerably - * from their counterparts in the C platform. + * Because the nature of the {@code os} library is to encapsulate + * os-specific features, the behavior of these functions varies considerably + * from their counterparts in the C platform. *
- * Typically, this library is included as part of a call to + * Typically, this library is included as part of a call to * {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} *
{@code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("os").get("time").call() );
* }
* - * For special cases where the smallest possible footprint is desired, + * For special cases where the smallest possible footprint is desired, * a minimal set of libraries could be loaded * directly via {@link Globals#load(LuaValue)} using code such as: *
{@code
@@ -126,7 +126,7 @@ public class JseOsLib extends org.luaj.vm2.lib.OsLib {
protected String tmpname() {
try {
java.io.File f = java.io.File.createTempFile(TMP_PREFIX ,TMP_SUFFIX);
- return f.getName();
+ return f.getAbsolutePath();
} catch ( IOException ioe ) {
return super.tmpname();
}