Merge remote-tracking branch 'farmboy0/master'

This commit is contained in:
Adrian Siekierka
2022-09-04 02:01:30 +02:00
413 changed files with 50531 additions and 21882 deletions

25
luaj-core/pom.xml Normal file
View File

@@ -0,0 +1,25 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.luaj</groupId>
<artifactId>luaj-parent</artifactId>
<version>3.0-SNAPSHOT</version>
</parent>
<artifactId>luaj-core</artifactId>
<name>luaj-core</name>
<description>Core code for LuaJ</description>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,280 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
/**
* String buffer for use in string library methods, optimized for production of
* StrValue instances.
* <p>
* The buffer can begin initially as a wrapped {@link LuaValue} and only when
* concatenation actually occurs are the bytes first copied.
* <p>
* To convert back to a {@link LuaValue} again, the function
* {@link Buffer#value()} is used.
*
* @see LuaValue
* @see LuaValue#buffer()
* @see LuaString
*/
public final class Buffer {
/** Default capacity for a buffer: 64 */
private static final int DEFAULT_CAPACITY = 64;
/** Shared static array with no bytes */
private static final byte[] NOBYTES = {};
/** Bytes in this buffer */
private byte[] bytes;
/** Length of this buffer */
private int length;
/** Offset into the byte array */
private int offset;
/** Value of this buffer, when not represented in bytes */
private LuaValue value;
/**
* Create buffer with default capacity
*
* @see #DEFAULT_CAPACITY
*/
public Buffer() {
this(DEFAULT_CAPACITY);
}
/**
* Create buffer with specified initial capacity
*
* @param initialCapacity the initial capacity
*/
public Buffer(int initialCapacity) {
bytes = new byte[initialCapacity];
length = 0;
offset = 0;
value = null;
}
/**
* Create buffer with specified initial value
*
* @param value the initial value
*/
public Buffer(LuaValue value) {
bytes = NOBYTES;
length = offset = 0;
this.value = value;
}
/**
* Get buffer contents as a {@link LuaValue}
*
* @return value as a {@link LuaValue}, converting as necessary
*/
public LuaValue value() {
return value != null? value: this.tostring();
}
/**
* Set buffer contents as a {@link LuaValue}
*
* @param value value to set
*/
public Buffer setvalue(LuaValue value) {
bytes = NOBYTES;
offset = length = 0;
this.value = value;
return this;
}
/**
* Convert the buffer to a {@link LuaString}
*
* @return the value as a {@link LuaString}
*/
public LuaString tostring() {
realloc(length, 0);
return LuaString.valueOf(bytes, offset, length);
}
/**
* Convert the buffer to a Java String
*
* @return the value as a Java String
*/
public String tojstring() {
return value().tojstring();
}
/**
* Convert the buffer to a Java String
*
* @return the value as a Java String
*/
@Override
public String toString() {
return tojstring();
}
/**
* Append a single byte to the buffer.
*
* @return {@code this} to allow call chaining
*/
public Buffer append(byte b) {
makeroom(0, 1);
bytes[offset+length++] = b;
return this;
}
/**
* Append a {@link LuaValue} to the buffer.
*
* @return {@code this} to allow call chaining
*/
public Buffer append(LuaValue val) {
append(val.strvalue());
return this;
}
/**
* Append a {@link LuaString} to the buffer.
*
* @return {@code this} to allow call chaining
*/
public Buffer append(LuaString str) {
final int n = str.m_length;
makeroom(0, n);
str.copyInto(0, bytes, offset+length, n);
length += n;
return this;
}
/**
* Append a Java String to the buffer. The Java string will be converted to
* bytes using the UTF8 encoding.
*
* @return {@code this} to allow call chaining
* @see LuaString#encodeToUtf8(char[], int, byte[], int)
*/
public Buffer append(String str) {
char[] c = str.toCharArray();
final int n = LuaString.lengthAsUtf8(c);
makeroom(0, n);
LuaString.encodeToUtf8(c, c.length, bytes, offset+length);
length += n;
return this;
}
/**
* Concatenate this buffer onto a {@link LuaValue}
*
* @param lhs the left-hand-side value onto which we are concatenating
* {@code this}
* @return {@link Buffer} for use in call chaining.
*/
public Buffer concatTo(LuaValue lhs) {
return setvalue(lhs.concat(value()));
}
/**
* Concatenate this buffer onto a {@link LuaString}
*
* @param lhs the left-hand-side value onto which we are concatenating
* {@code this}
* @return {@link Buffer} for use in call chaining.
*/
public Buffer concatTo(LuaString lhs) {
return value != null && !value.isstring()? setvalue(lhs.concat(value)): prepend(lhs);
}
/**
* Concatenate this buffer onto a {@link LuaNumber}
* <p>
* The {@link LuaNumber} will be converted to a string before concatenating.
*
* @param lhs the left-hand-side value onto which we are concatenating
* {@code this}
* @return {@link Buffer} for use in call chaining.
*/
public Buffer concatTo(LuaNumber lhs) {
return value != null && !value.isstring()? setvalue(lhs.concat(value)): prepend(lhs.strvalue());
}
/**
* Concatenate bytes from a {@link LuaString} onto the front of this buffer
*
* @param s the left-hand-side value which we will concatenate onto the
* front of {@code this}
* @return {@link Buffer} for use in call chaining.
*/
public Buffer prepend(LuaString s) {
int n = s.m_length;
makeroom(n, 0);
System.arraycopy(s.m_bytes, s.m_offset, bytes, offset-n, n);
offset -= n;
length += n;
value = null;
return this;
}
/**
* Ensure there is enough room before and after the bytes.
*
* @param nbefore number of unused bytes which must precede the data after
* this completes
* @param nafter number of unused bytes which must follow the data after
* this completes
*/
public void makeroom(int nbefore, int nafter) {
if (value != null) {
LuaString s = value.strvalue();
value = null;
length = s.m_length;
offset = nbefore;
bytes = new byte[nbefore+length+nafter];
System.arraycopy(s.m_bytes, s.m_offset, bytes, offset, length);
} else if (offset+length+nafter > bytes.length || offset < nbefore) {
int n = nbefore+length+nafter;
int m = n < 32? 32: n < length*2? length*2: n;
realloc(m, nbefore == 0? 0: m-length-nafter);
}
}
/**
* Reallocate the internal storage for the buffer
*
* @param newSize the size of the buffer to use
* @param newOffset the offset to use
*/
private void realloc(int newSize, int newOffset) {
if (newSize != bytes.length) {
byte[] newBytes = new byte[newSize];
System.arraycopy(bytes, offset, newBytes, newOffset, length);
bytes = newBytes;
offset = newOffset;
}
}
}

View File

@@ -0,0 +1,590 @@
/*******************************************************************************
* Copyright (c) 2012 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.Reader;
import org.luaj.vm2.lib.BaseLib;
import org.luaj.vm2.lib.DebugLib;
import org.luaj.vm2.lib.IoLib;
import org.luaj.vm2.lib.PackageLib;
import org.luaj.vm2.lib.ResourceFinder;
/**
* Global environment used by luaj. Contains global variables referenced by
* executing lua.
* <p>
*
* <h3>Constructing and Initializing Instances</h3> Typically, this is
* constructed indirectly by a call to
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}, and then used to
* load lua scripts for execution as in the following example.
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* globals.load(new StringReader("print 'hello'"), "main.lua").call();
* }
* </pre>
*
* The creates a complete global environment with the standard libraries loaded.
* <p>
* For specialized circumstances, the Globals may be constructed directly and
* loaded with only those libraries that are needed, for example.
*
* <pre>
* {
* &#64;code
* Globals globals = new Globals();
* globals.load(new BaseLib());
* }
* </pre>
*
* <h3>Loading and Executing Lua Code</h3> Globals contains convenience
* functions to load and execute lua source code given a Reader. A simple
* example is:
*
* <pre>
* {@code
* globals.load( new StringReader("print 'hello'"), "main.lua" ).call();
* }
* </pre>
*
* <h3>Fine-Grained Control of Compiling and Loading Lua</h3> Executable
* LuaFunctions are created from lua code in several steps
* <ul>
* <li>find the resource using the platform's {@link ResourceFinder}
* <li>compile lua to lua bytecode using {@link Compiler}
* <li>load lua bytecode to a {@link Prototype} using {@link Undumper}
* <li>construct {@link LuaClosure} from {@link Prototype} with {@link Globals}
* using {@link Loader}
* </ul>
* <p>
* There are alternate flows when the direct lua-to-Java bytecode compiling
* {@link org.luaj.vm2.luajc.LuaJC} is used.
* <ul>
* <li>compile lua to lua bytecode using {@link Compiler} or load precompiled
* code using {@link Undumper}
* <li>convert lua bytecode to equivalent Java bytecode using
* {@link org.luaj.vm2.luajc.LuaJC} that implements {@link Loader} directly
* </ul>
*
* <h3>Java Field</h3> Certain public fields are provided that contain the
* current values of important global state:
* <ul>
* <li>{@link #STDIN} Current value for standard input in the laaded
* {@link IoLib}, if any.
* <li>{@link #STDOUT} Current value for standard output in the loaded
* {@link IoLib}, if any.
* <li>{@link #STDERR} Current value for standard error in the loaded
* {@link IoLib}, if any.
* <li>{@link #finder} Current loaded {@link ResourceFinder}, if any.
* <li>{@link #compiler} Current loaded {@link Compiler}, if any.
* <li>{@link #undumper} Current loaded {@link Undumper}, if any.
* <li>{@link #loader} Current loaded {@link Loader}, if any.
* </ul>
*
* <h3>Lua Environment Variables</h3> When using
* {@link org.luaj.vm2.lib.jse.JsePlatform} or
* {@link org.luaj.vm2.lib.jme.JmePlatform}, these environment variables are
* created within the Globals.
* <ul>
* <li>"_G" Pointer to this Globals.
* <li>"_VERSION" String containing the version of luaj.
* </ul>
*
* <h3>Use in Multithreaded Environments</h3> In a multi-threaded server
* environment, each server thread should create one Globals instance, which
* will be logically distinct and not interfere with each other, but share
* certain static immutable resources such as class data and string data.
* <p>
*
* @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform
* @see LuaValue
* @see Compiler
* @see Loader
* @see Undumper
* @see ResourceFinder
* @see org.luaj.vm2.compiler.LuaC
* @see org.luaj.vm2.luajc.LuaJC
*/
public class Globals extends LuaTable {
/** The current default input stream. */
public InputStream STDIN = null;
/** The current default output stream. */
public PrintStream STDOUT = System.out;
/** The current default error stream. */
public PrintStream STDERR = System.err;
/** The installed ResourceFinder for looking files by name. */
public ResourceFinder finder;
/**
* The currently running thread. Should not be changed by non-library code.
*/
public LuaThread running = new LuaThread(this);
/** The BaseLib instance loaded into this Globals */
public BaseLib baselib;
/** The PackageLib instance loaded into this Globals */
public PackageLib package_;
/**
* The DebugLib instance loaded into this Globals, or null if debugging is
* not enabled
*/
public DebugLib debuglib;
/**
* Interface for module that converts a Prototype into a LuaFunction with an
* environment.
*/
public interface Loader {
/**
* Convert the prototype into a LuaFunction with the supplied
* environment.
*/
LuaFunction load(Prototype prototype, String chunkname, LuaValue env) throws IOException;
}
/** Interface for module that converts lua source text into a prototype. */
public interface Compiler {
/**
* Compile lua source into a Prototype. The InputStream is assumed to be
* in UTF-8.
*/
Prototype compile(InputStream stream, String chunkname) throws IOException;
}
/** Interface for module that loads lua binary chunk into a prototype. */
public interface Undumper {
/** Load the supplied input stream into a prototype. */
Prototype undump(InputStream stream, String chunkname) throws IOException;
}
/**
* Check that this object is a Globals object, and return it, otherwise
* throw an error.
*/
@Override
public Globals checkglobals() {
return this;
}
/**
* The installed loader.
*
* @see Loader
*/
public Loader loader;
/**
* The installed compiler.
*
* @see Compiler
*/
public Compiler compiler;
/**
* The installed undumper.
*
* @see Undumper
*/
public Undumper undumper;
/**
* Convenience function for loading a file that is either binary lua or lua
* source.
*
* @param filename Name of the file to load.
* @return LuaValue that can be call()'ed or invoke()'ed.
* @throws LuaError if the file could not be loaded.
*/
public LuaValue loadfile(String filename) {
try {
return load(finder.findResource(filename), "@" + filename, "bt", this);
} catch (Exception e) {
return error("load " + filename + ": " + e);
}
}
/**
* Convenience function to load a string value as a script. Must be lua
* source.
*
* @param script Contents of a lua script, such as "print 'hello,
* world.'"
* @param chunkname Name that will be used within the chunk as the source.
* @return LuaValue that may be executed via .call(), .invoke(), or
* .method() calls.
* @throws LuaError if the script could not be compiled.
*/
public LuaValue load(String script, String chunkname) {
return load(new StrReader(script), chunkname);
}
/**
* Convenience function to load a string value as a script. Must be lua
* source.
*
* @param script Contents of a lua script, such as "print 'hello, world.'"
* @return LuaValue that may be executed via .call(), .invoke(), or
* .method() calls.
* @throws LuaError if the script could not be compiled.
*/
public LuaValue load(String script) {
return load(new StrReader(script), script);
}
/**
* Convenience function to load a string value as a script with a custom
* environment. Must be lua source.
*
* @param script Contents of a lua script, such as "print 'hello,
* world.'"
* @param chunkname Name that will be used within the chunk as the source.
* @param environment LuaTable to be used as the environment for the loaded
* function.
* @return LuaValue that may be executed via .call(), .invoke(), or
* .method() calls.
* @throws LuaError if the script could not be compiled.
*/
public LuaValue load(String script, String chunkname, LuaTable environment) {
return load(new StrReader(script), chunkname, environment);
}
/**
* Load the content form a reader as a text file. Must be lua source. The
* source is converted to UTF-8, so any characters appearing in quoted
* literals above the range 128 will be converted into multiple bytes.
*
* @param reader Reader containing text of a lua script, such as "print
* 'hello, world.'"
* @param chunkname Name that will be used within the chunk as the source.
* @return LuaValue that may be executed via .call(), .invoke(), or
* .method() calls.
* @throws LuaError if the script could not be compiled.
*/
public LuaValue load(Reader reader, String chunkname) {
return load(new UTF8Stream(reader), chunkname, "t", this);
}
/**
* Load the content form a reader as a text file, supplying a custom
* environment. Must be lua source. The source is converted to UTF-8, so any
* characters appearing in quoted literals above the range 128 will be
* converted into multiple bytes.
*
* @param reader Reader containing text of a lua script, such as "print
* 'hello, world.'"
* @param chunkname Name that will be used within the chunk as the source.
* @param environment LuaTable to be used as the environment for the loaded
* function.
* @return LuaValue that may be executed via .call(), .invoke(), or
* .method() calls.
* @throws LuaError if the script could not be compiled.
*/
public LuaValue load(Reader reader, String chunkname, LuaTable environment) {
return load(new UTF8Stream(reader), chunkname, "t", environment);
}
/**
* Load the content form an input stream as a binary chunk or text file.
*
* @param is InputStream containing a lua script or compiled lua"
* @param chunkname Name that will be used within the chunk as the source.
* @param mode String containing 'b' or 't' or both to control
* loading as binary or text or either.
* @param environment LuaTable to be used as the environment for the loaded
* function.
*/
public LuaValue load(InputStream is, String chunkname, String mode, LuaValue environment) {
try {
Prototype p = loadPrototype(is, chunkname, mode);
return loader.load(p, chunkname, environment);
} catch (LuaError l) {
throw l;
} catch (Exception e) {
return error("load " + chunkname + ": " + e);
}
}
/**
* Load lua source or lua binary from an input stream into a Prototype. The
* InputStream is either a binary lua chunk starting with the lua binary
* chunk signature, or a text input file. If it is a text input file, it is
* interpreted as a UTF-8 byte sequence.
*
* @param is Input stream containing a lua script or compiled lua"
* @param chunkname Name that will be used within the chunk as the source.
* @param mode String containing 'b' or 't' or both to control loading
* as binary or text or either.
*/
public Prototype loadPrototype(InputStream is, String chunkname, String mode) throws IOException {
if (mode.indexOf('b') >= 0) {
if (undumper == null)
error("No undumper.");
if (!is.markSupported())
is = new BufferedStream(is);
is.mark(4);
final Prototype p = undumper.undump(is, chunkname);
if (p != null)
return p;
is.reset();
}
if (mode.indexOf('t') >= 0) {
return compilePrototype(is, chunkname);
}
error("Failed to load prototype " + chunkname + " using mode '" + mode + "'");
return null;
}
/**
* Compile lua source from a Reader into a Prototype. The characters in the
* reader are converted to bytes using the UTF-8 encoding, so a string
* literal containing characters with codepoints 128 or above will be
* converted into multiple bytes.
*/
public Prototype compilePrototype(Reader reader, String chunkname) throws IOException {
return compilePrototype(new UTF8Stream(reader), chunkname);
}
/**
* Compile lua source from an InputStream into a Prototype. The input is
* assumed to be UTf-8, but since bytes in the range 128-255 are passed
* along as literal bytes, any ASCII-compatible encoding such as ISO 8859-1
* may also be used.
*/
public Prototype compilePrototype(InputStream stream, String chunkname) throws IOException {
if (compiler == null)
error("No compiler.");
return compiler.compile(stream, chunkname);
}
/**
* Function which yields the current thread.
*
* @param args Arguments to supply as return values in the resume function
* of the resuming thread.
* @return Values supplied as arguments to the resume() call that
* reactivates this thread.
*/
public Varargs yield(Varargs args) {
if (running == null || running.isMainThread())
throw new LuaError("cannot yield main thread");
final LuaThread.State s = running.state;
return s.lua_yield(args);
}
/** Reader implementation to read chars from a String in JME or JSE. */
static class StrReader extends Reader {
final String s;
int i = 0;
final int n;
StrReader(String s) {
this.s = s;
n = s.length();
}
@Override
public void close() throws IOException {
i = n;
}
@Override
public int read() throws IOException {
return i < n? s.charAt(i++): -1;
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
int j = 0;
for (; j < len && i < n; ++j, ++i)
cbuf[off+j] = s.charAt(i);
return j > 0 || len == 0? j: -1;
}
}
/* Abstract base class to provide basic buffered input storage and delivery.
* This class may be moved to its own package in the future.
*/
abstract static class AbstractBufferedStream extends InputStream {
protected byte[] b;
protected int i = 0, j = 0;
protected AbstractBufferedStream(int buflen) {
this.b = new byte[buflen];
}
abstract protected int avail() throws IOException;
@Override
public int read() throws IOException {
int a = avail();
return a <= 0? -1: 0xff & b[i++];
}
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int i0, int n) throws IOException {
int a = avail();
if (a <= 0)
return -1;
final int n_read = Math.min(a, n);
System.arraycopy(this.b, i, b, i0, n_read);
i += n_read;
return n_read;
}
@Override
public long skip(long n) throws IOException {
final long k = Math.min(n, j-i);
i += k;
return k;
}
@Override
public int available() throws IOException {
return j-i;
}
}
/**
* Simple converter from Reader to InputStream using UTF8 encoding that will
* work on both JME and JSE. This class may be moved to its own package in
* the future.
*/
static class UTF8Stream extends AbstractBufferedStream {
private final char[] c = new char[32];
private final Reader r;
UTF8Stream(Reader r) {
super(96);
this.r = r;
}
@Override
protected int avail() throws IOException {
if (i < j)
return j-i;
int n = r.read(c);
if (n < 0)
return -1;
if (n == 0) {
int u = r.read();
if (u < 0)
return -1;
c[0] = (char) u;
n = 1;
}
j = LuaString.encodeToUtf8(c, n, b, i = 0);
return j;
}
@Override
public void close() throws IOException {
r.close();
}
}
/**
* Simple buffered InputStream that supports mark. Used to examine an
* InputStream for a 4-byte binary lua signature, and fall back to text
* input when the signature is not found, as well as speed up normal
* compilation and reading of lua scripts. This class may be moved to its
* own package in the future.
*/
static class BufferedStream extends AbstractBufferedStream {
private final InputStream s;
public BufferedStream(InputStream s) {
this(128, s);
}
BufferedStream(int buflen, InputStream s) {
super(buflen);
this.s = s;
}
@Override
protected int avail() throws IOException {
if (i < j)
return j-i;
if (j >= b.length)
i = j = 0;
// leave previous bytes in place to implement mark()/reset().
int n = s.read(b, j, b.length-j);
if (n < 0)
return -1;
if (n == 0) {
int u = s.read();
if (u < 0)
return -1;
b[j] = (byte) u;
n = 1;
}
j += n;
return n;
}
@Override
public void close() throws IOException {
s.close();
}
@Override
public synchronized void mark(int n) {
if (i > 0 || n > b.length) {
byte[] dest = n > b.length? new byte[n]: b;
System.arraycopy(b, i, dest, 0, j-i);
j -= i;
i = 0;
b = dest;
}
}
@Override
public boolean markSupported() {
return true;
}
@Override
public synchronized void reset() throws IOException {
i = 0;
}
}
}

View File

@@ -0,0 +1,486 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Class to undump compiled lua bytecode into a {@link Prototype} instances.
* <p>
* 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
* {@link org.luaj.vm2.compiler.LuaC} compiler.
* <p>
* The canonical method to load and execute code is done indirectly using the
* Globals:
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* LuaValue chunk = globasl.load("print('hello, world')", "main.lua");
* chunk.call();
* }
* </pre>
*
* This should work regardless of which {@link Globals.Compiler} or
* {@link Globals.Undumper} have been installed.
* <p>
* 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}.
* <p>
*
* A lua binary file is created via the {@link org.luaj.vm2.compiler.DumpState}
* class :
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* Prototype p = globals.compilePrototype(new StringReader("print('hello, world')"), "main.lua");
* ByteArrayOutputStream o = new ByteArrayOutputStream();
* org.luaj.vm2.compiler.DumpState.dump(p, o, false);
* byte[] lua_binary_file_bytes = o.toByteArray();
* }
* </pre>
*
* The {@link LoadState}'s default undumper {@link #instance} may be used
* directly to undump these bytes:
*
* <pre>
* {@code
* Prototypep = LoadState.instance.undump(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua");
* LuaClosure c = new LuaClosure(p, globals);
* c.call();
* }
* </pre>
*
*
* More commonly, the {@link Globals.Undumper} may be used to undump them:
*
* <pre>
* {
* &#64;code
* Prototype p = globals.loadPrototype(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua", "b");
* LuaClosure c = new LuaClosure(p, globals);
* c.call();
* }
* </pre>
*
* @see Globals.Compiler
* @see Globals.Undumper
* @see LuaClosure
* @see LuaFunction
* @see org.luaj.vm2.compiler.LuaC
* @see org.luaj.vm2.luajc.LuaJC
* @see Globals#compiler
* @see Globals#load(InputStream, String, LuaValue)
*/
public class LoadState {
/**
* Shared instance of Globals.Undumper to use loading prototypes from binary
* lua files
*/
public static final Globals.Undumper instance = new GlobalsUndumper();
/**
* format corresponding to non-number-patched lua, all numbers are floats or
* doubles
*/
public static final int NUMBER_FORMAT_FLOATS_OR_DOUBLES = 0;
/** format corresponding to non-number-patched lua, all numbers are ints */
public static final int NUMBER_FORMAT_INTS_ONLY = 1;
/**
* format corresponding to number-patched lua, all numbers are 32-bit (4
* byte) ints
*/
public static final int NUMBER_FORMAT_NUM_PATCH_INT32 = 4;
// type constants
public static final int LUA_TINT = -2;
public static final int LUA_TNONE = -1;
public static final int LUA_TNIL = 0;
public static final int LUA_TBOOLEAN = 1;
public static final int LUA_TLIGHTUSERDATA = 2;
public static final int LUA_TNUMBER = 3;
public static final int LUA_TSTRING = 4;
public static final int LUA_TTABLE = 5;
public static final int LUA_TFUNCTION = 6;
public static final int LUA_TUSERDATA = 7;
public static final int LUA_TTHREAD = 8;
public static final int LUA_TVALUE = 9;
/**
* The character encoding to use for file encoding. Null means the default
* encoding
*/
public static String encoding = null;
/** Signature byte indicating the file is a compiled binary chunk */
public static final byte[] LUA_SIGNATURE = { '\033', 'L', 'u', 'a' };
/** Data to catch conversion errors */
public static final byte[] LUAC_TAIL = { (byte) 0x19, (byte) 0x93, '\r', '\n', (byte) 0x1a, '\n', };
/** Name for compiled chunks */
public static final String SOURCE_BINARY_STRING = "binary string";
/** for header of binary files -- this is Lua 5.2 */
public static final int LUAC_VERSION = 0x52;
/** for header of binary files -- this is the official format */
public static final int LUAC_FORMAT = 0;
/** size of header of binary files */
public static final int LUAC_HEADERSIZE = 12;
// values read from the header
private int luacVersion;
private int luacFormat;
private boolean luacLittleEndian;
private int luacSizeofInt;
private int luacSizeofSizeT;
private int luacSizeofInstruction;
private int luacSizeofLuaNumber;
private int luacNumberFormat;
/** input stream from which we are loading */
public final DataInputStream is;
/** Name of what is being loaded? */
String name;
private static final LuaValue[] NOVALUES = {};
private static final Prototype[] NOPROTOS = {};
private static final LocVars[] NOLOCVARS = {};
private static final Upvaldesc[] NOUPVALDESCS = {};
private static final int[] NOINTS = {};
/** Read buffer */
private byte[] buf = new byte[512];
/**
* Install this class as the standard Globals.Undumper for the supplied
* Globals
*/
public static void install(Globals globals) {
globals.undumper = instance;
}
/**
* Load a 4-byte int value from the input stream
*
* @return the int value laoded.
**/
int loadInt() throws IOException {
is.readFully(buf, 0, 4);
return luacLittleEndian? buf[3]<<24 | (0xff & buf[2])<<16 | (0xff & buf[1])<<8 | 0xff & buf[0]
: buf[0]<<24 | (0xff & buf[1])<<16 | (0xff & buf[2])<<8 | 0xff & buf[3];
}
/**
* Load an array of int values from the input stream
*
* @return the array of int values laoded.
**/
int[] loadIntArray() throws IOException {
int n = loadInt();
if (n == 0)
return NOINTS;
// read all data at once
int m = n<<2;
if (buf.length < m)
buf = new byte[m];
is.readFully(buf, 0, m);
int[] array = new int[n];
for (int i = 0, j = 0; i < n; ++i, j += 4)
array[i] = luacLittleEndian? buf[j+3]<<24 | (0xff & buf[j+2])<<16 | (0xff & buf[j+1])<<8 | 0xff & buf[j+0]
: buf[j+0]<<24 | (0xff & buf[j+1])<<16 | (0xff & buf[j+2])<<8 | 0xff & buf[j+3];
return array;
}
/**
* Load a long value from the input stream
*
* @return the long value laoded.
**/
long loadInt64() throws IOException {
int a, b;
if (this.luacLittleEndian) {
a = loadInt();
b = loadInt();
} else {
b = loadInt();
a = loadInt();
}
return (long) b<<32 | a & 0xffffffffL;
}
/**
* Load a lua strin gvalue from the input stream
*
* @return the {@link LuaString} value laoded.
**/
LuaString loadString() throws IOException {
int size = this.luacSizeofSizeT == 8? (int) loadInt64(): loadInt();
if (size == 0)
return null;
byte[] bytes = new byte[size];
is.readFully(bytes, 0, size);
return LuaString.valueUsing(bytes, 0, bytes.length-1);
}
/**
* Convert bits in a long value to a {@link LuaValue}.
*
* @param bits long value containing the bits
* @return {@link LuaInteger} or {@link LuaDouble} whose value corresponds
* to the bits provided.
*/
public static LuaValue longBitsToLuaNumber(long bits) {
if ((bits & (1L<<63)-1) == 0L) {
return LuaValue.ZERO;
}
int e = (int) (bits>>52 & 0x7ffL)-1023;
if (e >= 0 && e < 31) {
long f = bits & 0xFFFFFFFFFFFFFL;
int shift = 52-e;
long intPrecMask = (1L<<shift)-1;
if ((f & intPrecMask) == 0) {
int intValue = (int) (f>>shift) | 1<<e;
return LuaInteger.valueOf(bits>>63 != 0? -intValue: intValue);
}
}
return LuaValue.valueOf(Double.longBitsToDouble(bits));
}
/**
* Load a number from a binary chunk
*
* @return the {@link LuaValue} loaded
* @throws IOException if an i/o exception occurs
*/
LuaValue loadNumber() throws IOException {
if (luacNumberFormat == NUMBER_FORMAT_INTS_ONLY) {
return LuaInteger.valueOf(loadInt());
} else {
return longBitsToLuaNumber(loadInt64());
}
}
/**
* Load a list of constants from a binary chunk
*
* @param f the function prototype
* @throws IOException if an i/o exception occurs
*/
void loadConstants(Prototype f) throws IOException {
int n = loadInt();
LuaValue[] values = n > 0? new LuaValue[n]: NOVALUES;
for (int i = 0; i < n; i++) {
switch (is.readByte()) {
case LUA_TNIL:
values[i] = LuaValue.NIL;
break;
case LUA_TBOOLEAN:
values[i] = 0 != is.readUnsignedByte()? LuaValue.TRUE: LuaValue.FALSE;
break;
case LUA_TINT:
values[i] = LuaInteger.valueOf(loadInt());
break;
case LUA_TNUMBER:
values[i] = loadNumber();
break;
case LUA_TSTRING:
values[i] = loadString();
break;
default:
throw new IllegalStateException("bad constant");
}
}
f.k = values;
n = loadInt();
Prototype[] protos = n > 0? new Prototype[n]: NOPROTOS;
for (int i = 0; i < n; i++)
protos[i] = loadFunction(f.source);
f.p = protos;
}
void loadUpvalues(Prototype f) throws IOException {
int n = loadInt();
f.upvalues = n > 0? new Upvaldesc[n]: NOUPVALDESCS;
for (int i = 0; i < n; i++) {
boolean instack = is.readByte() != 0;
int idx = is.readByte() & 0xff;
f.upvalues[i] = new Upvaldesc(null, instack, idx);
}
}
/**
* Load the debug info for a function prototype
*
* @param f the function Prototype
* @throws IOException if there is an i/o exception
*/
void loadDebug(Prototype f) throws IOException {
f.source = loadString();
f.lineinfo = loadIntArray();
int n = loadInt();
f.locvars = n > 0? new LocVars[n]: NOLOCVARS;
for (int i = 0; i < n; i++) {
LuaString varname = loadString();
int startpc = loadInt();
int endpc = loadInt();
f.locvars[i] = new LocVars(varname, startpc, endpc);
}
n = loadInt();
for (int i = 0; i < n; i++)
f.upvalues[i].name = loadString();
}
/**
* Load a function prototype from the input stream
*
* @param p name of the source
* @return {@link Prototype} instance that was loaded
* @throws IOException
*/
public Prototype loadFunction(LuaString p) throws IOException {
Prototype f = new Prototype();
//// this.L.push(f);
// f.source = loadString();
// if ( f.source == null )
// f.source = p;
f.linedefined = loadInt();
f.lastlinedefined = loadInt();
f.numparams = is.readUnsignedByte();
f.is_vararg = is.readUnsignedByte();
f.maxstacksize = is.readUnsignedByte();
f.code = loadIntArray();
loadConstants(f);
loadUpvalues(f);
loadDebug(f);
// TODO: add check here, for debugging purposes, I believe
// see ldebug.c
// IF (!luaG_checkcode(f), "bad code");
// this.L.pop();
return f;
}
/**
* Load the lua chunk header values.
*
* @throws IOException if an i/o exception occurs.
*/
public void loadHeader() throws IOException {
luacVersion = is.readByte();
luacFormat = is.readByte();
luacLittleEndian = 0 != is.readByte();
luacSizeofInt = is.readByte();
luacSizeofSizeT = is.readByte();
luacSizeofInstruction = is.readByte();
luacSizeofLuaNumber = is.readByte();
luacNumberFormat = is.readByte();
for (int i = 0; i < LUAC_TAIL.length; ++i)
if (is.readByte() != LUAC_TAIL[i])
throw new LuaError("Unexpeted byte in luac tail of header, index=" + i);
}
/**
* Load input stream as a lua binary chunk if the first 4 bytes are the lua
* binary signature.
*
* @param stream InputStream to read, after having read the first byte
* already
* @param chunkname Name to apply to the loaded chunk
* @return {@link Prototype} that was loaded, or null if the first 4 bytes
* were not the lua signature.
* @throws IOException if an IOException occurs
*/
public static Prototype undump(InputStream stream, String chunkname) throws IOException {
// check rest of signature
if (stream.read() != LUA_SIGNATURE[0] || stream.read() != LUA_SIGNATURE[1] || stream.read() != LUA_SIGNATURE[2]
|| stream.read() != LUA_SIGNATURE[3])
return null;
// load file as a compiled chunk
String sname = getSourceName(chunkname);
LoadState s = new LoadState(stream, sname);
s.loadHeader();
// check format
switch (s.luacNumberFormat) {
case NUMBER_FORMAT_FLOATS_OR_DOUBLES:
case NUMBER_FORMAT_INTS_ONLY:
case NUMBER_FORMAT_NUM_PATCH_INT32:
break;
default:
throw new LuaError("unsupported int size");
}
return s.loadFunction(LuaString.valueOf(sname));
}
/**
* Construct a source name from a supplied chunk name
*
* @param name String name that appears in the chunk
* @return source file name
*/
public static String getSourceName(String name) {
String sname = name;
if (name.startsWith("@") || name.startsWith("="))
sname = name.substring(1);
else if (name.startsWith("\033"))
sname = SOURCE_BINARY_STRING;
return sname;
}
/** Private constructor for create a load state */
private LoadState(InputStream stream, String name) {
this.name = name;
this.is = new DataInputStream(stream);
}
private static final class GlobalsUndumper implements Globals.Undumper {
@Override
public Prototype undump(InputStream stream, String chunkname) throws IOException {
return LoadState.undump(stream, chunkname);
}
}
}

View File

@@ -0,0 +1,54 @@
/*******************************************************************************
* Copyright (c) 2009-2011 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
/**
* Data class to hold debug information relating to local variables for a
* {@link Prototype}
*/
public class LocVars {
/** The local variable name */
public LuaString varname;
/** The instruction offset when the variable comes into scope */
public int startpc;
/** The instruction offset when the variable goes out of scope */
public int endpc;
/**
* Construct a LocVars instance.
*
* @param varname The local variable name
* @param startpc The instruction offset when the variable comes into scope
* @param endpc The instruction offset when the variable goes out of scope
*/
public LocVars(LuaString varname, int startpc, int endpc) {
this.varname = varname;
this.startpc = startpc;
this.endpc = endpc;
}
public String tojstring() {
return varname + " " + startpc + "-" + endpc;
}
}

View File

@@ -0,0 +1,357 @@
/*******************************************************************************
* Copyright (c) 2009-2011 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
/**
* Constants for lua limits and opcodes.
* <p>
* This is a direct translation of C lua distribution header file constants for
* bytecode creation and processing.
*/
public class Lua {
/** version is supplied by ant build task */
public static final String _VERSION = "Luaj 0.0";
/** use return values from previous op */
public static final int LUA_MULTRET = -1;
// from lopcodes.h
/*===========================================================================
We assume that instructions are unsigned numbers.
All instructions have an opcode in the first 6 bits.
Instructions can have the following fields:
`A' : 8 bits
`B' : 9 bits
`C' : 9 bits
`Bx' : 18 bits (`B' and `C' together)
`sBx' : signed Bx
A signed argument is represented in excess K; that is, the number
value is the unsigned value minus K. K is exactly the maximum value
for that argument (so that -max is represented by 0, and +max is
represented by 2*max), which is half the maximum for the corresponding
unsigned argument.
===========================================================================*/
/* basic instruction format */
public static final int iABC = 0;
public static final int iABx = 1;
public static final int iAsBx = 2;
public static final int iAx = 3;
/*
** size and position of opcode arguments.
*/
public static final int SIZE_C = 9;
public static final int SIZE_B = 9;
public static final int SIZE_Bx = SIZE_C+SIZE_B;
public static final int SIZE_A = 8;
public static final int SIZE_Ax = SIZE_C+SIZE_B+SIZE_A;
public static final int SIZE_OP = 6;
public static final int POS_OP = 0;
public static final int POS_A = POS_OP+SIZE_OP;
public static final int POS_C = POS_A+SIZE_A;
public static final int POS_B = POS_C+SIZE_C;
public static final int POS_Bx = POS_C;
public static final int POS_Ax = POS_A;
public static final int MAX_OP = (1<<SIZE_OP)-1;
public static final int MAXARG_A = (1<<SIZE_A)-1;
public static final int MAXARG_B = (1<<SIZE_B)-1;
public static final int MAXARG_C = (1<<SIZE_C)-1;
public static final int MAXARG_Bx = (1<<SIZE_Bx)-1;
public static final int MAXARG_sBx = MAXARG_Bx>>1; /* `sBx' is signed */
public static final int MAXARG_Ax = (1<<SIZE_Ax)-1;
public static final int MASK_OP = (1<<SIZE_OP)-1<<POS_OP;
public static final int MASK_A = (1<<SIZE_A)-1<<POS_A;
public static final int MASK_B = (1<<SIZE_B)-1<<POS_B;
public static final int MASK_C = (1<<SIZE_C)-1<<POS_C;
public static final int MASK_Bx = (1<<SIZE_Bx)-1<<POS_Bx;
public static final int MASK_Ax = (1<<SIZE_Ax)-1<<POS_Ax;
public static final int MASK_NOT_OP = ~MASK_OP;
public static final int MASK_NOT_A = ~MASK_A;
public static final int MASK_NOT_B = ~MASK_B;
public static final int MASK_NOT_C = ~MASK_C;
public static final int MASK_NOT_Bx = ~MASK_Bx;
/*
** the following macros help to manipulate instructions
*/
public static int GET_OPCODE(int i) {
return i>>POS_OP & MAX_OP;
}
public static int GETARG_A(int i) {
return i>>POS_A & MAXARG_A;
}
public static int GETARG_Ax(int i) {
return i>>POS_Ax & MAXARG_Ax;
}
public static int GETARG_B(int i) {
return i>>POS_B & MAXARG_B;
}
public static int GETARG_C(int i) {
return i>>POS_C & MAXARG_C;
}
public static int GETARG_Bx(int i) {
return i>>POS_Bx & MAXARG_Bx;
}
public static int GETARG_sBx(int i) {
return (i>>POS_Bx & MAXARG_Bx)-MAXARG_sBx;
}
/*
** Macros to operate RK indices
*/
/** this bit 1 means constant (0 means register) */
public static final int BITRK = 1<<SIZE_B-1;
/** test whether value is a constant */
public static boolean ISK(int x) {
return 0 != (x & BITRK);
}
/** gets the index of the constant */
public static int INDEXK(int r) {
return r & ~BITRK;
}
public static final int MAXINDEXRK = BITRK-1;
/** code a constant index as a RK value */
public static int RKASK(int x) {
return x | BITRK;
}
/**
** invalid register that fits in 8 bits
*/
public static final int NO_REG = MAXARG_A;
/*
** R(x) - register
** Kst(x) - constant (in constant table)
** RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x)
*/
/*
** grep "ORDER OP" if you change these enums
*/
/*----------------------------------------------------------------------
name args description
------------------------------------------------------------------------*/
public static final int OP_MOVE = 0; /* A B R(A) := R(B) */
public static final int OP_LOADK = 1; /* A Bx R(A) := Kst(Bx) */
public static final int OP_LOADKX = 2; /* A R(A) := Kst(extra arg) */
public static final int OP_LOADBOOL = 3; /* A B C R(A) := (Bool)B; if (C) pc++ */
public static final int OP_LOADNIL = 4; /* A B R(A) := ... := R(A+B) := nil */
public static final int OP_GETUPVAL = 5; /* A B R(A) := UpValue[B] */
public static final int OP_GETTABUP = 6; /* A B C R(A) := UpValue[B][RK(C)] */
public static final int OP_GETTABLE = 7; /* A B C R(A) := R(B)[RK(C)] */
public static final int OP_SETTABUP = 8; /* A B C UpValue[A][RK(B)] := RK(C) */
public static final int OP_SETUPVAL = 9; /* A B UpValue[B] := R(A) */
public static final int OP_SETTABLE = 10; /* A B C R(A)[RK(B)] := RK(C) */
public static final int OP_NEWTABLE = 11; /* A B C R(A) := {} (size = B,C) */
public static final int OP_SELF = 12; /* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
public static final int OP_ADD = 13; /* A B C R(A) := RK(B) + RK(C) */
public static final int OP_SUB = 14; /* A B C R(A) := RK(B) - RK(C) */
public static final int OP_MUL = 15; /* A B C R(A) := RK(B) * RK(C) */
public static final int OP_DIV = 16; /* A B C R(A) := RK(B) / RK(C) */
public static final int OP_MOD = 17; /* A B C R(A) := RK(B) % RK(C) */
public static final int OP_POW = 18; /* A B C R(A) := RK(B) ^ RK(C) */
public static final int OP_UNM = 19; /* A B R(A) := -R(B) */
public static final int OP_NOT = 20; /* A B R(A) := not R(B) */
public static final int OP_LEN = 21; /* A B R(A) := length of R(B) */
public static final int OP_CONCAT = 22; /* A B C R(A) := R(B).. ... ..R(C) */
public static final int OP_JMP = 23; /* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */
public static final int OP_EQ = 24; /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
public static final int OP_LT = 25; /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
public static final int OP_LE = 26; /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
public static final int OP_TEST = 27; /* A C if not (R(A) <=> C) then pc++ */
public static final int OP_TESTSET = 28; /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
public static final int OP_CALL = 29; /* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
public static final int OP_TAILCALL = 30; /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
public static final int OP_RETURN = 31; /* A B return R(A), ... ,R(A+B-2) (see note) */
public static final int OP_FORLOOP = 32; /* A sBx R(A)+=R(A+2);
if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
public static final int OP_FORPREP = 33; /* A sBx R(A)-=R(A+2); pc+=sBx */
public static final int OP_TFORCALL = 34; /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
public static final int OP_TFORLOOP = 35; /* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx } */
public static final int OP_SETLIST = 36; /* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
public static final int OP_CLOSURE = 37; /* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */
public static final int OP_VARARG = 38; /* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
public static final int OP_EXTRAARG = 39; /* Ax extra (larger) argument for previous opcode */
public static final int NUM_OPCODES = OP_EXTRAARG+1;
/* pseudo-opcodes used in parsing only. */
public static final int OP_GT = 63; // >
public static final int OP_GE = 62; // >=
public static final int OP_NEQ = 61; // ~=
public static final int OP_AND = 60; // and
public static final int OP_OR = 59; // or
/*===========================================================================
Notes:
(*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1,
and can be 0: OP_CALL then sets `top' to last_result+1, so
next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'.
(*) In OP_VARARG, if (B == 0) then use actual number of varargs and
set top (like in OP_CALL with C == 0).
(*) In OP_RETURN, if (B == 0) then return up to `top'
(*) In OP_SETLIST, if (B == 0) then B = `top';
if (C == 0) then next `instruction' is real C
(*) For comparisons, A specifies what condition the test should accept
(true or false).
(*) All `skips' (pc++) assume that next instruction is a jump
===========================================================================*/
/*
** masks for instruction properties. The format is:
** bits 0-1: op mode
** bits 2-3: C arg mode
** bits 4-5: B arg mode
** bit 6: instruction set register A
** bit 7: operator is a test
*/
public static final int OpArgN = 0; /* argument is not used */
public static final int OpArgU = 1; /* argument is used */
public static final int OpArgR = 2; /* argument is a register or a jump offset */
public static final int OpArgK = 3; /* argument is a constant or register/constant */
public static final int[] luaP_opmodes = {
/* T A B C mode opcode */
0<<7 | 1<<6 | OpArgR<<4 | OpArgN<<2 | iABC, /* OP_MOVE */
0<<7 | 1<<6 | OpArgK<<4 | OpArgN<<2 | iABx, /* OP_LOADK */
0<<7 | 1<<6 | OpArgN<<4 | OpArgN<<2 | iABx, /* OP_LOADKX */
0<<7 | 1<<6 | OpArgU<<4 | OpArgU<<2 | iABC, /* OP_LOADBOOL */
0<<7 | 1<<6 | OpArgU<<4 | OpArgN<<2 | iABC, /* OP_LOADNIL */
0<<7 | 1<<6 | OpArgU<<4 | OpArgN<<2 | iABC, /* OP_GETUPVAL */
0<<7 | 1<<6 | OpArgU<<4 | OpArgK<<2 | iABC, /* OP_GETTABUP */
0<<7 | 1<<6 | OpArgR<<4 | OpArgK<<2 | iABC, /* OP_GETTABLE */
0<<7 | 0<<6 | OpArgK<<4 | OpArgK<<2 | iABC, /* OP_SETTABUP */
0<<7 | 0<<6 | OpArgU<<4 | OpArgN<<2 | iABC, /* OP_SETUPVAL */
0<<7 | 0<<6 | OpArgK<<4 | OpArgK<<2 | iABC, /* OP_SETTABLE */
0<<7 | 1<<6 | OpArgU<<4 | OpArgU<<2 | iABC, /* OP_NEWTABLE */
0<<7 | 1<<6 | OpArgR<<4 | OpArgK<<2 | iABC, /* OP_SELF */
0<<7 | 1<<6 | OpArgK<<4 | OpArgK<<2 | iABC, /* OP_ADD */
0<<7 | 1<<6 | OpArgK<<4 | OpArgK<<2 | iABC, /* OP_SUB */
0<<7 | 1<<6 | OpArgK<<4 | OpArgK<<2 | iABC, /* OP_MUL */
0<<7 | 1<<6 | OpArgK<<4 | OpArgK<<2 | iABC, /* OP_DIV */
0<<7 | 1<<6 | OpArgK<<4 | OpArgK<<2 | iABC, /* OP_MOD */
0<<7 | 1<<6 | OpArgK<<4 | OpArgK<<2 | iABC, /* OP_POW */
0<<7 | 1<<6 | OpArgR<<4 | OpArgN<<2 | iABC, /* OP_UNM */
0<<7 | 1<<6 | OpArgR<<4 | OpArgN<<2 | iABC, /* OP_NOT */
0<<7 | 1<<6 | OpArgR<<4 | OpArgN<<2 | iABC, /* OP_LEN */
0<<7 | 1<<6 | OpArgR<<4 | OpArgR<<2 | iABC, /* OP_CONCAT */
0<<7 | 0<<6 | OpArgR<<4 | OpArgN<<2 | iAsBx, /* OP_JMP */
1<<7 | 0<<6 | OpArgK<<4 | OpArgK<<2 | iABC, /* OP_EQ */
1<<7 | 0<<6 | OpArgK<<4 | OpArgK<<2 | iABC, /* OP_LT */
1<<7 | 0<<6 | OpArgK<<4 | OpArgK<<2 | iABC, /* OP_LE */
1<<7 | 0<<6 | OpArgN<<4 | OpArgU<<2 | iABC, /* OP_TEST */
1<<7 | 1<<6 | OpArgR<<4 | OpArgU<<2 | iABC, /* OP_TESTSET */
0<<7 | 1<<6 | OpArgU<<4 | OpArgU<<2 | iABC, /* OP_CALL */
0<<7 | 1<<6 | OpArgU<<4 | OpArgU<<2 | iABC, /* OP_TAILCALL */
0<<7 | 0<<6 | OpArgU<<4 | OpArgN<<2 | iABC, /* OP_RETURN */
0<<7 | 1<<6 | OpArgR<<4 | OpArgN<<2 | iAsBx, /* OP_FORLOOP */
0<<7 | 1<<6 | OpArgR<<4 | OpArgN<<2 | iAsBx, /* OP_FORPREP */
0<<7 | 0<<6 | OpArgN<<4 | OpArgU<<2 | iABC, /* OP_TFORCALL */
1<<7 | 1<<6 | OpArgR<<4 | OpArgN<<2 | iAsBx, /* OP_TFORLOOP */
0<<7 | 0<<6 | OpArgU<<4 | OpArgU<<2 | iABC, /* OP_SETLIST */
0<<7 | 1<<6 | OpArgU<<4 | OpArgN<<2 | iABx, /* OP_CLOSURE */
0<<7 | 1<<6 | OpArgU<<4 | OpArgN<<2 | iABC, /* OP_VARARG */
0<<7 | 0<<6 | OpArgU<<4 | OpArgU<<2 | iAx, /* OP_EXTRAARG */
};
public static int getOpMode(int m) {
return luaP_opmodes[m] & 3;
}
public static int getBMode(int m) {
return luaP_opmodes[m]>>4 & 3;
}
public static int getCMode(int m) {
return luaP_opmodes[m]>>2 & 3;
}
public static boolean testAMode(int m) {
return 0 != (luaP_opmodes[m] & 1<<6);
}
public static boolean testTMode(int m) {
return 0 != (luaP_opmodes[m] & 1<<7);
}
/* number of list items to accumulate before a SETLIST instruction */
public static final int LFIELDS_PER_FLUSH = 50;
private static final int MAXSRC = 80;
public static String chunkid(String source) {
if (source.startsWith("="))
return source.substring(1);
String end = "";
if (source.startsWith("@")) {
source = source.substring(1);
} else {
source = "[string \"" + source;
end = "\"]";
}
int n = source.length()+end.length();
if (n > MAXSRC)
source = source.substring(0, MAXSRC-end.length()-3) + "...";
return source+end;
}
}

View File

@@ -0,0 +1,113 @@
/*******************************************************************************
* Copyright (c) 2009-2011 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
/**
* Extension of {@link LuaValue} which can hold a Java boolean as its value.
* <p>
* These instance are not instantiated directly by clients. Instead, there are
* exactly twon instances of this class, {@link LuaValue#TRUE} and
* {@link LuaValue#FALSE} representing the lua values {@code true} and
* {@code false}. The function {@link LuaValue#valueOf(boolean)} will always
* return one of these two values.
* <p>
* Any {@link LuaValue} can be converted to its equivalent boolean
* representation using {@link LuaValue#toboolean()}
* <p>
*
* @see LuaValue
* @see LuaValue#valueOf(boolean)
* @see LuaValue#TRUE
* @see LuaValue#FALSE
*/
public final class LuaBoolean extends LuaValue {
/** The singleton instance representing lua {@code true} */
static final LuaBoolean _TRUE = new LuaBoolean(true);
/** The singleton instance representing lua {@code false} */
static final LuaBoolean _FALSE = new LuaBoolean(false);
/** Shared static metatable for boolean values represented in lua. */
public static LuaValue s_metatable;
/** The value of the boolean */
public final boolean v;
LuaBoolean(boolean b) {
this.v = b;
}
@Override
public int type() {
return LuaValue.TBOOLEAN;
}
@Override
public String typename() {
return "boolean";
}
@Override
public boolean isboolean() {
return true;
}
@Override
public LuaValue not() {
return v? FALSE: LuaValue.TRUE;
}
/**
* Return the boolean value for this boolean
*
* @return value as a Java boolean
*/
public boolean booleanValue() {
return v;
}
@Override
public boolean toboolean() {
return v;
}
@Override
public String tojstring() {
return v? "true": "false";
}
@Override
public boolean optboolean(boolean defval) {
return this.v;
}
@Override
public boolean checkboolean() {
return v;
}
@Override
public LuaValue getmetatable() {
return s_metatable;
}
}

View File

@@ -0,0 +1,680 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
import org.luaj.vm2.lib.DebugLib.CallFrame;
/**
* Extension of {@link LuaFunction} which executes lua bytecode.
* <p>
* A {@link LuaClosure} is a combination of a {@link Prototype} and a
* {@link LuaValue} to use as an environment for execution. Normally the
* {@link LuaValue} is a {@link Globals} in which case the environment will
* contain standard lua libraries.
*
* <p>
* There are three main ways {@link LuaClosure} instances are created:
* <ul>
* <li>Construct an instance using {@link #LuaClosure(Prototype, LuaValue)}</li>
* <li>Construct it indirectly by loading a chunk via
* {@link Globals#load(java.io.Reader, String)}
* <li>Execute the lua bytecode {@link Lua#OP_CLOSURE} as part of bytecode
* processing
* </ul>
* <p>
* To construct it directly, the {@link Prototype} is typically created via a
* compiler such as {@link org.luaj.vm2.compiler.LuaC}:
*
* <pre>
* {
* &#64;code
* String script = "print( 'hello, world' )";
* InputStream is = new ByteArrayInputStream(script.getBytes());
* Prototype p = LuaC.instance.compile(is, "script");
* LuaValue globals = JsePlatform.standardGlobals();
* LuaClosure f = new LuaClosure(p, globals);
* f.call();
* }
* </pre>
* <p>
* To construct it indirectly, the {@link Globals#load(java.io.Reader, String)}
* method may be used:
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* LuaFunction f = globals.load(new StringReader(script), "script");
* LuaClosure c = f.checkclosure(); // This may fail if LuaJC is installed.
* c.call();
* }
* </pre>
* <p>
* In this example, the "checkclosure()" may fail if direct lua-to-java-bytecode
* compiling using LuaJC is installed, because no LuaClosure is created in that
* case and the value returned is a {@link LuaFunction} but not a
* {@link LuaClosure}.
* <p>
* Since a {@link LuaClosure} is a {@link LuaFunction} which is a
* {@link LuaValue}, all the value operations can be used directly such as:
* <ul>
* <li>{@link LuaValue#call()}</li>
* <li>{@link LuaValue#call(LuaValue)}</li>
* <li>{@link LuaValue#invoke()}</li>
* <li>{@link LuaValue#invoke(Varargs)}</li>
* <li>{@link LuaValue#method(String)}</li>
* <li>{@link LuaValue#method(String,LuaValue)}</li>
* <li>{@link LuaValue#invokemethod(String)}</li>
* <li>{@link LuaValue#invokemethod(String,Varargs)}</li>
* <li>...</li>
* </ul>
*
* @see LuaValue
* @see LuaFunction
* @see LuaValue#isclosure()
* @see LuaValue#checkclosure()
* @see LuaValue#optclosure(LuaClosure)
* @see LoadState
* @see Globals#compiler
*/
public class LuaClosure extends LuaFunction {
private static final UpValue[] NOUPVALUES = {};
public final Prototype p;
public UpValue[] upValues;
final Globals globals;
/**
* Create a closure around a Prototype with a specific environment. If the
* prototype has upvalues, the environment will be written into the first
* upvalue.
*
* @param p the Prototype to construct this Closure for.
* @param env the environment to associate with the closure.
*/
public LuaClosure(Prototype p, LuaValue env) {
this.p = p;
this.initupvalue1(env);
globals = env instanceof Globals? (Globals) env: null;
}
@Override
public void initupvalue1(LuaValue env) {
if (p.upvalues == null || p.upvalues.length == 0)
this.upValues = NOUPVALUES;
else {
this.upValues = new UpValue[p.upvalues.length];
this.upValues[0] = new UpValue(new LuaValue[] { env }, 0);
}
}
@Override
public boolean isclosure() {
return true;
}
@Override
public LuaClosure optclosure(LuaClosure defval) {
return this;
}
@Override
public LuaClosure checkclosure() {
return this;
}
@Override
public String tojstring() {
return "function: " + p.toString();
}
private LuaValue[] getNewStack() {
int max = p.maxstacksize;
LuaValue[] stack = new LuaValue[max];
System.arraycopy(NILS, 0, stack, 0, max);
return stack;
}
@Override
public final LuaValue call() {
LuaValue[] stack = getNewStack();
return execute(stack, NONE).arg1();
}
@Override
public final LuaValue call(LuaValue arg) {
LuaValue[] stack = getNewStack();
switch (p.numparams) {
default:
stack[0] = arg;
return execute(stack, NONE).arg1();
case 0:
return execute(stack, arg).arg1();
}
}
@Override
public final LuaValue call(LuaValue arg1, LuaValue arg2) {
LuaValue[] stack = getNewStack();
switch (p.numparams) {
default:
stack[0] = arg1;
stack[1] = arg2;
return execute(stack, NONE).arg1();
case 1:
stack[0] = arg1;
return execute(stack, arg2).arg1();
case 0:
return execute(stack, p.is_vararg != 0? varargsOf(arg1, arg2): NONE).arg1();
}
}
@Override
public final LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
LuaValue[] stack = getNewStack();
switch (p.numparams) {
default:
stack[0] = arg1;
stack[1] = arg2;
stack[2] = arg3;
return execute(stack, NONE).arg1();
case 2:
stack[0] = arg1;
stack[1] = arg2;
return execute(stack, arg3).arg1();
case 1:
stack[0] = arg1;
return execute(stack, p.is_vararg != 0? varargsOf(arg2, arg3): NONE).arg1();
case 0:
return execute(stack, p.is_vararg != 0? varargsOf(arg1, arg2, arg3): NONE).arg1();
}
}
@Override
public final Varargs invoke(Varargs varargs) {
return onInvoke(varargs).eval();
}
@Override
public final Varargs onInvoke(Varargs varargs) {
LuaValue[] stack = getNewStack();
for (int i = 0; i < p.numparams; i++)
stack[i] = varargs.arg(i+1);
return execute(stack, p.is_vararg != 0? varargs.subargs(p.numparams+1): NONE);
}
protected Varargs execute(LuaValue[] stack, Varargs varargs) {
// loop through instructions
int i, a, b, c, pc = 0, top = 0;
LuaValue o;
Varargs v = NONE;
int[] code = p.code;
LuaValue[] k = p.k;
// upvalues are only possible when closures create closures
// TODO: use linked list.
UpValue[] openups = p.p.length > 0? new UpValue[stack.length]: null;
// allow for debug hooks
if (globals != null && globals.debuglib != null)
globals.debuglib.onCall(this, varargs, stack);
// process instructions
try {
for (; true; ++pc) {
if (globals != null && globals.debuglib != null)
globals.debuglib.onInstruction(pc, v, top);
// pull out instruction
i = code[pc];
a = i>>6 & 0xff;
// process the op code
switch (i & 0x3f) {
case Lua.OP_MOVE:/* A B R(A):= R(B) */
stack[a] = stack[i>>>23];
continue;
case Lua.OP_LOADK:/* A Bx R(A):= Kst(Bx) */
stack[a] = k[i>>>14];
continue;
case Lua.OP_LOADKX:/* A R(A) := Kst(extra arg) */
++pc;
i = code[pc];
if ((i & 0x3f) != Lua.OP_EXTRAARG) {
int op = i & 0x3f;
throw new LuaError("OP_EXTRAARG expected after OP_LOADKX, got "
+ (op < Print.OPNAMES.length-1? Print.OPNAMES[op]: "UNKNOWN_OP_" + op));
}
stack[a] = k[i>>>6];
continue;
case Lua.OP_LOADBOOL:/* A B C R(A):= (Bool)B: if (C) pc++ */
stack[a] = i>>>23 != 0? LuaValue.TRUE: LuaValue.FALSE;
if ((i & 0x1ff<<14) != 0)
++pc; /* skip next instruction (if C) */
continue;
case Lua.OP_LOADNIL: /* A B R(A):= ...:= R(A+B):= nil */
for (b = i>>>23; b-- >= 0;)
stack[a++] = LuaValue.NIL;
continue;
case Lua.OP_GETUPVAL: /* A B R(A):= UpValue[B] */
stack[a] = upValues[i>>>23].getValue();
continue;
case Lua.OP_GETTABUP: /* A B C R(A) := UpValue[B][RK(C)] */
stack[a] = upValues[i>>>23].getValue().get((c = i>>14 & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue;
case Lua.OP_GETTABLE: /* A B C R(A):= R(B)[RK(C)] */
stack[a] = stack[i>>>23].get((c = i>>14 & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue;
case Lua.OP_SETTABUP: /* A B C UpValue[A][RK(B)] := RK(C) */
upValues[a].getValue().set((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b],
(c = i>>14 & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue;
case Lua.OP_SETUPVAL: /* A B UpValue[B]:= R(A) */
upValues[i>>>23].setValue(stack[a]);
continue;
case Lua.OP_SETTABLE: /* A B C R(A)[RK(B)]:= RK(C) */
stack[a].set((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b],
(c = i>>14 & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue;
case Lua.OP_NEWTABLE: /* A B C R(A):= {} (size = B,C) */
stack[a] = new LuaTable(i>>>23, i>>14 & 0x1ff);
continue;
case Lua.OP_SELF: /* A B C R(A+1):= R(B): R(A):= R(B)[RK(C)] */
stack[a+1] = o = stack[i>>>23];
stack[a] = o.get((c = i>>14 & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue;
case Lua.OP_ADD: /* A B C R(A):= RK(B) + RK(C) */
stack[a] = ((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.add((c = i>>14 & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue;
case Lua.OP_SUB: /* A B C R(A):= RK(B) - RK(C) */
stack[a] = ((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.sub((c = i>>14 & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue;
case Lua.OP_MUL: /* A B C R(A):= RK(B) * RK(C) */
stack[a] = ((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.mul((c = i>>14 & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue;
case Lua.OP_DIV: /* A B C R(A):= RK(B) / RK(C) */
stack[a] = ((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.div((c = i>>14 & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue;
case Lua.OP_MOD: /* A B C R(A):= RK(B) % RK(C) */
stack[a] = ((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.mod((c = i>>14 & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue;
case Lua.OP_POW: /* A B C R(A):= RK(B) ^ RK(C) */
stack[a] = ((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.pow((c = i>>14 & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue;
case Lua.OP_UNM: /* A B R(A):= -R(B) */
stack[a] = stack[i>>>23].neg();
continue;
case Lua.OP_NOT: /* A B R(A):= not R(B) */
stack[a] = stack[i>>>23].not();
continue;
case Lua.OP_LEN: /* A B R(A):= length of R(B) */
stack[a] = stack[i>>>23].len();
continue;
case Lua.OP_CONCAT: /* A B C R(A):= R(B).. ... ..R(C) */
b = i>>>23;
c = i>>14 & 0x1ff; {
if (c > b+1) {
Buffer sb = stack[c].buffer();
while ( --c >= b )
sb.concatTo(stack[c]);
stack[a] = sb.value();
} else {
stack[a] = stack[c-1].concat(stack[c]);
}
}
continue;
case Lua.OP_JMP: /* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */
pc += (i>>>14)-0x1ffff;
if (a > 0) {
for (--a, b = openups.length; --b >= 0;)
if (openups[b] != null && openups[b].index >= a) {
openups[b].close();
openups[b] = null;
}
}
continue;
case Lua.OP_EQ: /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
if (((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.eq_b((c = i>>14 & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]) != (a != 0))
++pc;
continue;
case Lua.OP_LT: /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
if (((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.lt_b((c = i>>14 & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]) != (a != 0))
++pc;
continue;
case Lua.OP_LE: /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
if (((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.lteq_b((c = i>>14 & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]) != (a != 0))
++pc;
continue;
case Lua.OP_TEST: /* A C if not (R(A) <=> C) then pc++ */
if (stack[a].toboolean() != ((i & 0x1ff<<14) != 0))
++pc;
continue;
case Lua.OP_TESTSET: /* A B C if (R(B) <=> C) then R(A):= R(B) else pc++ */
/* note: doc appears to be reversed */
if ((o = stack[i>>>23]).toboolean() != ((i & 0x1ff<<14) != 0))
++pc;
else
stack[a] = o; // TODO: should be sBx?
continue;
case Lua.OP_CALL: /* A B C R(A), ... ,R(A+C-2):= R(A)(R(A+1), ... ,R(A+B-1)) */
switch (i & (Lua.MASK_B | Lua.MASK_C)) {
case 1<<Lua.POS_B | 0<<Lua.POS_C:
v = stack[a].invoke(NONE);
top = a+v.narg();
continue;
case 2<<Lua.POS_B | 0<<Lua.POS_C:
v = stack[a].invoke(stack[a+1]);
top = a+v.narg();
continue;
case 1<<Lua.POS_B | 1<<Lua.POS_C:
stack[a].call();
continue;
case 2<<Lua.POS_B | 1<<Lua.POS_C:
stack[a].call(stack[a+1]);
continue;
case 3<<Lua.POS_B | 1<<Lua.POS_C:
stack[a].call(stack[a+1], stack[a+2]);
continue;
case 4<<Lua.POS_B | 1<<Lua.POS_C:
stack[a].call(stack[a+1], stack[a+2], stack[a+3]);
continue;
case 1<<Lua.POS_B | 2<<Lua.POS_C:
stack[a] = stack[a].call();
continue;
case 2<<Lua.POS_B | 2<<Lua.POS_C:
stack[a] = stack[a].call(stack[a+1]);
continue;
case 3<<Lua.POS_B | 2<<Lua.POS_C:
stack[a] = stack[a].call(stack[a+1], stack[a+2]);
continue;
case 4<<Lua.POS_B | 2<<Lua.POS_C:
stack[a] = stack[a].call(stack[a+1], stack[a+2], stack[a+3]);
continue;
default:
b = i>>>23;
c = i>>14 & 0x1ff;
v = stack[a].invoke(b > 0? varargsOf(stack, a+1, b-1): // exact arg count
varargsOf(stack, a+1, top-v.narg()-(a+1), v)); // from prev top
if (c > 0) {
v.copyto(stack, a, c-1);
v = NONE;
} else {
top = a+v.narg();
v = v.dealias();
}
continue;
}
case Lua.OP_TAILCALL: /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
switch (i & Lua.MASK_B) {
case 1<<Lua.POS_B:
return new TailcallVarargs(stack[a], NONE);
case 2<<Lua.POS_B:
return new TailcallVarargs(stack[a], stack[a+1]);
case 3<<Lua.POS_B:
return new TailcallVarargs(stack[a], varargsOf(stack[a+1], stack[a+2]));
case 4<<Lua.POS_B:
return new TailcallVarargs(stack[a], varargsOf(stack[a+1], stack[a+2], stack[a+3]));
default:
b = i>>>23;
v = b > 0? varargsOf(stack, a+1, b-1): // exact arg count
varargsOf(stack, a+1, top-v.narg()-(a+1), v); // from prev top
return new TailcallVarargs(stack[a], v);
}
case Lua.OP_RETURN: /* A B return R(A), ... ,R(A+B-2) (see note) */
b = i>>>23;
switch (b) {
case 0:
return varargsOf(stack, a, top-v.narg()-a, v);
case 1:
return NONE;
case 2:
return stack[a];
default:
return varargsOf(stack, a, b-1);
}
case Lua.OP_FORLOOP: /* A sBx R(A)+=R(A+2): if R(A) <?= R(A+1) then { pc+=sBx: R(A+3)=R(A) }*/
{
LuaValue limit = stack[a+1];
LuaValue step = stack[a+2];
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;
pc += (i>>>14)-0x1ffff;
}
}
continue;
case Lua.OP_FORPREP: /* A sBx R(A)-=R(A+2): pc+=sBx */
{
LuaValue init = stack[a].checknumber("'for' initial value must be a number");
LuaValue limit = stack[a+1].checknumber("'for' limit must be a number");
LuaValue step = stack[a+2].checknumber("'for' step must be a number");
stack[a] = init.sub(step);
stack[a+1] = limit;
stack[a+2] = step;
pc += (i>>>14)-0x1ffff;
}
continue;
case Lua.OP_TFORCALL: /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
v = stack[a].invoke(varargsOf(stack[a+1], stack[a+2]));
c = i>>14 & 0x1ff;
while ( --c >= 0 )
stack[a+3+c] = v.arg(c+1);
v = NONE;
continue;
case Lua.OP_TFORLOOP: /* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx */
if (!stack[a+1].isnil()) { /* continue loop? */
stack[a] = stack[a+1]; /* save control varible. */
pc += (i>>>14)-0x1ffff;
}
continue;
case Lua.OP_SETLIST: /* A B C R(A)[(C-1)*FPF+i]:= R(A+i), 1 <= i <= B */
{
if ((c = i>>14 & 0x1ff) == 0)
c = code[++pc];
int offset = (c-1)*Lua.LFIELDS_PER_FLUSH;
o = stack[a];
if ((b = i>>>23) == 0) {
b = top-a-1;
int m = b-v.narg();
int j = 1;
for (; j <= m; j++)
o.set(offset+j, stack[a+j]);
for (; j <= b; j++)
o.set(offset+j, v.arg(j-m));
} else {
o.presize(offset+b);
for (int j = 1; j <= b; j++)
o.set(offset+j, stack[a+j]);
}
}
continue;
case Lua.OP_CLOSURE: /* A Bx R(A):= closure(KPROTO[Bx]) */
{
Prototype newp = p.p[i>>>14];
LuaClosure ncl = new LuaClosure(newp, globals);
Upvaldesc[] uv = newp.upvalues;
for (int j = 0, nup = uv.length; j < nup; ++j) {
if (uv[j].instack) /* upvalue refes to local variable? */
ncl.upValues[j] = findupval(stack, uv[j].idx, openups);
else /* get upvalue from enclosing function */
ncl.upValues[j] = upValues[uv[j].idx];
}
stack[a] = ncl;
}
continue;
case Lua.OP_VARARG: /* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
b = i>>>23;
if (b == 0) {
top = a+(b = varargs.narg());
v = varargs;
} else {
for (int j = 1; j < b; ++j)
stack[a+j-1] = varargs.arg(j);
}
continue;
case Lua.OP_EXTRAARG:
throw new java.lang.IllegalArgumentException("Uexecutable opcode: OP_EXTRAARG");
default:
throw new java.lang.IllegalArgumentException("Illegal opcode: " + (i & 0x3f));
}
}
} catch (LuaError le) {
if (le.traceback == null)
processErrorHooks(le, p, pc);
throw le;
} catch (Exception e) {
LuaError le = new LuaError(e);
processErrorHooks(le, p, pc);
throw le;
} finally {
if (openups != null)
for (int u = openups.length; --u >= 0;)
if (openups[u] != null)
openups[u].close();
if (globals != null && globals.debuglib != null)
globals.debuglib.onReturn();
}
}
/**
* Run the error hook if there is one
*
* @param msg the message to use in error hook processing.
*/
String errorHook(String msg, int level) {
if (globals == null)
return msg;
final LuaThread r = globals.running;
if (r.errorfunc == null)
return globals.debuglib != null? msg + "\n" + globals.debuglib.traceback(level): msg;
final LuaValue e = r.errorfunc;
r.errorfunc = null;
try {
return e.call(LuaValue.valueOf(msg)).tojstring();
} catch (Throwable t) {
return "error in error handling";
} finally {
r.errorfunc = e;
}
}
private void processErrorHooks(LuaError le, Prototype p, int 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);
}
private UpValue findupval(LuaValue[] stack, short idx, UpValue[] openups) {
final int n = openups.length;
for (int i = 0; i < n; ++i)
if (openups[i] != null && openups[i].index == idx)
return openups[i];
for (int i = 0; i < n; ++i)
if (openups[i] == null)
return openups[i] = new UpValue(stack, idx);
error("No space for upvalue");
return null;
}
protected LuaValue getUpvalue(int i) {
return upValues[i].getValue();
}
protected void setUpvalue(int i, LuaValue v) {
upValues[i].setValue(v);
}
@Override
public String name() {
return "<" + p.shortsource() + ":" + p.linedefined + ">";
}
}

View File

@@ -0,0 +1,303 @@
/*******************************************************************************
* Copyright (c) 2009-2011 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
import org.luaj.vm2.compat.JavaCompat;
import org.luaj.vm2.lib.MathLib;
/**
* Extension of {@link LuaNumber} which can hold a Java double as its value.
* <p>
* 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}.
* <p>
* Almost all API's implemented in LuaDouble are defined and documented in {@link LuaValue}.
* <p>
* However the constants {@link #NAN}, {@link #NEGNAN}, {@link #POSINF}, {@link #NEGINF},
* {@link #JSTR_NAN}, {@link #JSTR_NEGNAN}, {@link #JSTR_POSINF}, and {@link #JSTR_NEGINF} may be useful
* when dealing with Nan or Infinite values.
* <p>
* LuaDouble also defines functions for handling the unique math rules of lua devision and modulo in
* <ul>
* <li>{@link #ddiv(double, double)}</li>
* <li>{@link #ddiv_d(double, double)}</li>
* <li>{@link #dmod(double, double)}</li>
* <li>{@link #dmod_d(double, double)}</li>
* </ul>
* <p>
* @see LuaValue
* @see LuaNumber
* @see LuaInteger
* @see LuaValue#valueOf(int)
* @see LuaValue#valueOf(double)
*/
public class LuaDouble extends LuaNumber {
/** Constant LuaDouble representing NaN (not a number) */
public static final LuaDouble NAN = new LuaDouble( Double.NaN );
/** Constant LuaDouble representing negative NaN (not a number) */
public static final LuaDouble NEGNAN = new LuaDouble( -Double.NaN );
/** Constant LuaDouble representing positive infinity */
public static final LuaDouble POSINF = new LuaDouble( Double.POSITIVE_INFINITY );
/** Constant LuaDouble representing negative infinity */
public static final LuaDouble NEGINF = new LuaDouble( Double.NEGATIVE_INFINITY );
/** Constant String representation for NaN (not a number), "nan" */
public static final String JSTR_NAN = "nan";
/** Constant String representation for negative NaN (not a number), "-nan" */
public static final String JSTR_NEGNAN = "-nan";
/** Constant String representation for positive infinity, "inf" */
public static final String JSTR_POSINF = "inf";
/** Constant String representation for negative infinity, "-inf" */
public static final String JSTR_NEGINF = "-inf";
/** The value being held by this instance. */
final double v;
public static LuaNumber valueOf(double d) {
int id = (int) d;
return d==id? (LuaNumber) LuaInteger.valueOf(id): (LuaNumber) new LuaDouble(d);
}
/** Don't allow ints to be boxed by DoubleValues */
private LuaDouble(double d) {
this.v = d;
}
public int hashCode() {
long l = Double.doubleToLongBits(v + 1);
return ((int)(l>>32)) + (int) l;
}
public boolean islong() {
return v == (long) v;
}
public byte tobyte() { return (byte) (long) v; }
public char tochar() { return (char) (long) v; }
public double todouble() { return v; }
public float tofloat() { return (float) v; }
public int toint() { return (int) (long) v; }
public long tolong() { return (long) v; }
public short toshort() { return (short) (long) v; }
public double optdouble(double defval) { return v; }
public int optint(int defval) { return (int) (long) v; }
public LuaInteger optinteger(LuaInteger defval) { return LuaInteger.valueOf((int) (long)v); }
public long optlong(long defval) { return (long) v; }
public LuaInteger checkinteger() { return LuaInteger.valueOf( (int) (long) v ); }
// unary operators
public LuaValue neg() { return valueOf(-v); }
// object equality, used for key comparison
public boolean equals(Object o) { return o instanceof LuaDouble? ((LuaDouble)o).v == v: false; }
// equality w/ metatable processing
public LuaValue eq( LuaValue val ) { return val.raweq(v)? TRUE: FALSE; }
public boolean eq_b( LuaValue val ) { return val.raweq(v); }
// equality w/o metatable processing
public boolean raweq( LuaValue val ) { return val.raweq(v); }
public boolean raweq( double val ) { return v == val; }
public boolean raweq( int val ) { return v == val; }
// basic binary arithmetic
public LuaValue add( LuaValue rhs ) { return rhs.add(v); }
public LuaValue add( double lhs ) { return LuaDouble.valueOf(lhs + v); }
public LuaValue sub( LuaValue rhs ) { return rhs.subFrom(v); }
public LuaValue sub( double rhs ) { return LuaDouble.valueOf(v - rhs); }
public LuaValue sub( int rhs ) { return LuaDouble.valueOf(v - rhs); }
public LuaValue subFrom( double lhs ) { return LuaDouble.valueOf(lhs - v); }
public LuaValue mul( LuaValue rhs ) { return rhs.mul(v); }
public LuaValue mul( double lhs ) { return LuaDouble.valueOf(lhs * v); }
public LuaValue mul( int lhs ) { return LuaDouble.valueOf(lhs * v); }
public LuaValue pow( LuaValue rhs ) { return rhs.powWith(v); }
public LuaValue pow( double rhs ) { return MathLib.dpow(v,rhs); }
public LuaValue pow( int rhs ) { return MathLib.dpow(v,rhs); }
public LuaValue powWith( double lhs ) { return MathLib.dpow(lhs,v); }
public LuaValue powWith( int lhs ) { return MathLib.dpow(lhs,v); }
public LuaValue div( LuaValue rhs ) { return rhs.divInto(v); }
public LuaValue div( double rhs ) { return LuaDouble.ddiv(v,rhs); }
public LuaValue div( int rhs ) { return LuaDouble.ddiv(v,rhs); }
public LuaValue divInto( double lhs ) { return LuaDouble.ddiv(lhs,v); }
public LuaValue mod( LuaValue rhs ) { return rhs.modFrom(v); }
public LuaValue mod( double rhs ) { return LuaDouble.dmod(v,rhs); }
public LuaValue mod( int rhs ) { return LuaDouble.dmod(v,rhs); }
public LuaValue modFrom( double lhs ) { return LuaDouble.dmod(lhs,v); }
/** 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,
* taking into account positive and negiative infinity, and Nan
* @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;
}
/** Divide two double numbers according to lua math, and return a double result.
* @param lhs Left-hand-side of the division.
* @param rhs Right-hand-side of the division.
* @return Value of the division, taking into account positive and negative infinity, and Nan
* @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;
}
/** 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,
* using lua's rules for modulo
* @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;
if (rhs == Double.POSITIVE_INFINITY) {
return lhs < 0 ? POSINF : valueOf(lhs);
}
if (rhs == Double.NEGATIVE_INFINITY) {
return lhs > 0 ? NEGINF : valueOf(lhs);
}
return valueOf( lhs-rhs*Math.floor(lhs/rhs) );
}
/** 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,
* using lua's rules for modulo
* @see #dmod(double, double)
*/
public static double dmod_d(double lhs, double rhs) {
if (rhs == 0 || lhs == Double.POSITIVE_INFINITY || lhs == Double.NEGATIVE_INFINITY) return Double.NaN;
if (rhs == Double.POSITIVE_INFINITY) {
return lhs < 0 ? Double.POSITIVE_INFINITY : lhs;
}
if (rhs == Double.NEGATIVE_INFINITY) {
return lhs > 0 ? Double.NEGATIVE_INFINITY : lhs;
}
return lhs-rhs*Math.floor(lhs/rhs);
}
// relational operators
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 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 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 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 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 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 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 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 String tojstring() {
if ( v == 0.0 ) // never occurs on J2ME
return (JavaCompat.INSTANCE.doubleToRawLongBits(v)<0? "-0": "0");
long l = (long) v;
if ( l == v )
return Long.toString(l);
if ( Double.isNaN(v) )
return (JavaCompat.INSTANCE.doubleToRawLongBits(v)<0? JSTR_NEGNAN: JSTR_NAN);
if ( Double.isInfinite(v) )
return (v<0? JSTR_NEGINF: JSTR_POSINF);
return Float.toString((float)v);
}
public LuaString strvalue() {
return LuaString.valueOf(tojstring());
}
public LuaString optstring(LuaString defval) {
return LuaString.valueOf(tojstring());
}
public LuaValue tostring() {
return LuaString.valueOf(tojstring());
}
public String optjstring(String defval) {
return tojstring();
}
public LuaNumber optnumber(LuaNumber defval) {
return this;
}
public boolean isnumber() {
return true;
}
public boolean isstring() {
return true;
}
public LuaValue tonumber() {
return this;
}
public int checkint() { return (int) (long) v; }
public long checklong() { return (long) v; }
public LuaNumber checknumber() { return this; }
public double checkdouble() { return v; }
public String checkjstring() {
return tojstring();
}
public LuaString checkstring() {
return LuaString.valueOf(tojstring());
}
public boolean isvalidkey() {
return !Double.isNaN(v);
}
}

View File

@@ -0,0 +1,136 @@
/*******************************************************************************
* Copyright (c) 2009-2011 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
/**
* RuntimeException that is thrown and caught in response to a lua error.
* <p>
* {@link LuaError} is used wherever a lua call to {@code error()} would be used
* within a script.
* <p>
* Since it is an unchecked exception inheriting from {@link RuntimeException},
* Java method signatures do notdeclare this exception, althoug it can be thrown
* on almost any luaj Java operation. This is analagous to the fact that any lua
* script can throw a lua error at any time.
* <p>
* The LuaError may be constructed with a message object, in which case the
* message is the string representation of that object. getMessageObject will
* get the object supplied at construct time, or a LuaString containing the
* message of an object was not supplied.
*/
public class LuaError extends RuntimeException {
private static final long serialVersionUID = 1L;
protected int level;
protected String fileline;
protected String traceback;
protected Throwable cause;
private LuaValue object;
/**
* Get the string message if it was supplied, or a string representation of
* the message object if that was supplied.
*/
@Override
public String getMessage() {
if (traceback != null)
return traceback;
String m = super.getMessage();
if (m == null)
return null;
if (fileline != null)
return fileline + m;
return m;
}
/**
* Get the LuaValue that was provided in the constructor, or a LuaString
* containing the message if it was a string error argument.
*
* @return LuaValue which was used in the constructor, or a LuaString
* containing the message.
*/
public LuaValue getMessageObject() {
if (object != null)
return object;
String m = getMessage();
return m != null? LuaValue.valueOf(m): null;
}
/**
* Construct LuaError when a program exception occurs.
* <p>
* All errors generated from lua code should throw LuaError(String) instead.
*
* @param cause the Throwable that caused the error, if known.
*/
public LuaError(Throwable cause) {
super("vm error: " + cause);
this.cause = cause;
this.level = 1;
}
/**
* Construct a LuaError with a specific message.
*
* @param message message to supply
*/
public LuaError(String message) {
super(message);
this.level = 1;
}
/**
* Construct a LuaError with a message, and level to draw line number
* information from.
*
* @param message message to supply
* @param level where to supply line info from in call stack
*/
public LuaError(String message, int level) {
super(message);
this.level = level;
}
/**
* Construct a LuaError with a LuaValue as the message object, and level to
* draw line number information from.
*
* @param message_object message string or object to supply
*/
public LuaError(LuaValue message_object) {
super(message_object.tojstring());
this.object = message_object;
this.level = 1;
}
/**
* Get the cause, if any.
*/
@Override
public Throwable getCause() { return cause; }
}

View File

@@ -0,0 +1,107 @@
/*******************************************************************************
* Copyright (c) 2009-2011 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
/**
* Base class for functions implemented in Java.
* <p>
* 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
*/
abstract public class LuaFunction extends LuaValue {
/** Shared static metatable for all functions and closures. */
public static LuaValue s_metatable;
@Override
public int type() {
return TFUNCTION;
}
@Override
public String typename() {
return "function";
}
@Override
public boolean isfunction() {
return true;
}
@Override
public LuaFunction checkfunction() {
return this;
}
@Override
public LuaFunction optfunction(LuaFunction defval) {
return this;
}
@Override
public LuaValue getmetatable() {
return s_metatable;
}
@Override
public String tojstring() {
return "function: " + classnamestub();
}
@Override
public LuaString strvalue() {
return valueOf(tojstring());
}
/**
* 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();
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. Is overridden by LuaClosure to return the
* source file and line, and by LibFunctions to return the name.
*
* @return common name for this function.
*/
public String name() {
return classnamestub();
}
}

View File

@@ -0,0 +1,372 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
import org.luaj.vm2.lib.MathLib;
/**
* Extension of {@link LuaNumber} which can hold a Java int as its value.
* <p>
* 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.
* <p>
* There are no API's specific to LuaInteger that are useful beyond what is
* already exposed in {@link LuaValue}.
*
* @see LuaValue
* @see LuaNumber
* @see LuaDouble
* @see LuaValue#valueOf(int)
* @see LuaValue#valueOf(double)
*/
public class LuaInteger extends LuaNumber {
private static final LuaInteger[] intValues = new LuaInteger[512];
static {
for (int i = 0; i < 512; i++)
intValues[i] = new LuaInteger(i-256);
}
public static LuaInteger valueOf(int i) {
return i <= 255 && i >= -256? intValues[i+256]: new LuaInteger(i);
}
// TODO consider moving this to LuaValue
/**
* Return a LuaNumber that represents the value provided
*
* @param l long value to represent.
* @return LuaNumber that is eithe LuaInteger or LuaDouble representing l
* @see LuaValue#valueOf(int)
* @see LuaValue#valueOf(double)
*/
public static LuaNumber valueOf(long l) {
int i = (int) l;
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.
*
* @see LuaValue#valueOf(int)
**/
LuaInteger(int i) {
this.v = i;
}
@Override
public boolean isint() { return true; }
@Override
public boolean isinttype() { return true; }
@Override
public boolean islong() { return true; }
@Override
public byte tobyte() { return (byte) v; }
@Override
public char tochar() { return (char) v; }
@Override
public double todouble() { return v; }
@Override
public float tofloat() { return v; }
@Override
public int toint() { return v; }
@Override
public long tolong() { return v; }
@Override
public short toshort() { return (short) v; }
@Override
public double optdouble(double defval) { return v; }
@Override
public int optint(int defval) { return v; }
@Override
public LuaInteger optinteger(LuaInteger defval) { return this; }
@Override
public long optlong(long defval) { return v; }
@Override
public String tojstring() {
return Integer.toString(v);
}
@Override
public LuaString strvalue() {
return LuaString.valueOf(Integer.toString(v));
}
@Override
public LuaString optstring(LuaString defval) {
return LuaString.valueOf(Integer.toString(v));
}
@Override
public LuaValue tostring() {
return LuaString.valueOf(Integer.toString(v));
}
@Override
public String optjstring(String defval) {
return Integer.toString(v);
}
@Override
public LuaInteger checkinteger() {
return this;
}
@Override
public boolean isstring() {
return true;
}
@Override
public int hashCode() {
return v;
}
public static int hashCode(int x) {
return x;
}
// unary operators
@Override
public LuaValue neg() { return valueOf(-(long) v); }
// object equality, used for key comparison
@Override
public boolean equals(Object o) { return o instanceof LuaInteger? ((LuaInteger) o).v == v: false; }
// equality w/ metatable processing
@Override
public LuaValue eq(LuaValue val) { return val.raweq(v)? TRUE: FALSE; }
@Override
public boolean eq_b(LuaValue val) { return val.raweq(v); }
// equality w/o metatable processing
@Override
public boolean raweq(LuaValue val) { return val.raweq(v); }
@Override
public boolean raweq(double val) { return v == val; }
@Override
public boolean raweq(int val) { return v == val; }
// arithmetic operators
@Override
public LuaValue add(LuaValue rhs) { return rhs.add(v); }
@Override
public LuaValue add(double lhs) { return LuaDouble.valueOf(lhs+v); }
@Override
public LuaValue add(int lhs) { return LuaInteger.valueOf(lhs+(long) v); }
@Override
public LuaValue sub(LuaValue rhs) { return rhs.subFrom(v); }
@Override
public LuaValue sub(double rhs) { return LuaDouble.valueOf(v-rhs); }
@Override
public LuaValue sub(int rhs) { return LuaValue.valueOf(v-rhs); }
@Override
public LuaValue subFrom(double lhs) { return LuaDouble.valueOf(lhs-v); }
@Override
public LuaValue subFrom(int lhs) { return LuaInteger.valueOf(lhs-(long) v); }
@Override
public LuaValue mul(LuaValue rhs) { return rhs.mul(v); }
@Override
public LuaValue mul(double lhs) { return LuaDouble.valueOf(lhs*v); }
@Override
public LuaValue mul(int lhs) { return LuaInteger.valueOf(lhs*(long) v); }
@Override
public LuaValue pow(LuaValue rhs) { return rhs.powWith(v); }
@Override
public LuaValue pow(double rhs) { return MathLib.dpow(v, rhs); }
@Override
public LuaValue pow(int rhs) { return MathLib.dpow(v, rhs); }
@Override
public LuaValue powWith(double lhs) { return MathLib.dpow(lhs, v); }
@Override
public LuaValue powWith(int lhs) { return MathLib.dpow(lhs, v); }
@Override
public LuaValue div(LuaValue rhs) { return rhs.divInto(v); }
@Override
public LuaValue div(double rhs) { return LuaDouble.ddiv(v, rhs); }
@Override
public LuaValue div(int rhs) { return LuaDouble.ddiv(v, rhs); }
@Override
public LuaValue divInto(double lhs) { return LuaDouble.ddiv(lhs, v); }
@Override
public LuaValue mod(LuaValue rhs) { return rhs.modFrom(v); }
@Override
public LuaValue mod(double rhs) { return LuaDouble.dmod(v, rhs); }
@Override
public LuaValue mod(int rhs) { return LuaDouble.dmod(v, rhs); }
@Override
public LuaValue modFrom(double lhs) { return LuaDouble.dmod(lhs, v); }
// relational operators
@Override
public LuaValue lt(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.gt_b(v)? TRUE: FALSE: super.lt(rhs); }
@Override
public LuaValue lt(double rhs) { return v < rhs? TRUE: FALSE; }
@Override
public LuaValue lt(int rhs) { return v < rhs? TRUE: FALSE; }
@Override
public boolean lt_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.gt_b(v): super.lt_b(rhs); }
@Override
public boolean lt_b(int rhs) { return v < rhs; }
@Override
public boolean lt_b(double rhs) { return v < rhs; }
@Override
public LuaValue lteq(LuaValue rhs) {
return rhs instanceof LuaNumber? rhs.gteq_b(v)? TRUE: FALSE: super.lteq(rhs);
}
@Override
public LuaValue lteq(double rhs) { return v <= rhs? TRUE: FALSE; }
@Override
public LuaValue lteq(int rhs) { return v <= rhs? TRUE: FALSE; }
@Override
public boolean lteq_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.gteq_b(v): super.lteq_b(rhs); }
@Override
public boolean lteq_b(int rhs) { return v <= rhs; }
@Override
public boolean lteq_b(double rhs) { return v <= rhs; }
@Override
public LuaValue gt(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.lt_b(v)? TRUE: FALSE: super.gt(rhs); }
@Override
public LuaValue gt(double rhs) { return v > rhs? TRUE: FALSE; }
@Override
public LuaValue gt(int rhs) { return v > rhs? TRUE: FALSE; }
@Override
public boolean gt_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.lt_b(v): super.gt_b(rhs); }
@Override
public boolean gt_b(int rhs) { return v > rhs; }
@Override
public boolean gt_b(double rhs) { return v > rhs; }
@Override
public LuaValue gteq(LuaValue rhs) {
return rhs instanceof LuaNumber? rhs.lteq_b(v)? TRUE: FALSE: super.gteq(rhs);
}
@Override
public LuaValue gteq(double rhs) { return v >= rhs? TRUE: FALSE; }
@Override
public LuaValue gteq(int rhs) { return v >= rhs? TRUE: FALSE; }
@Override
public boolean gteq_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.lteq_b(v): super.gteq_b(rhs); }
@Override
public boolean gteq_b(int rhs) { return v >= rhs; }
@Override
public boolean gteq_b(double rhs) { return v >= rhs; }
// string comparison
@Override
public int strcmp(LuaString rhs) { typerror("attempt to compare number with string"); return 0; }
@Override
public int checkint() {
return v;
}
@Override
public long checklong() {
return v;
}
@Override
public double checkdouble() {
return v;
}
@Override
public String checkjstring() {
return String.valueOf(v);
}
@Override
public LuaString checkstring() {
return valueOf(String.valueOf(v));
}
}

View File

@@ -0,0 +1,148 @@
/*******************************************************************************
* Copyright (c) 2009-2011 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
/**
* Class to encapsulate behavior of the singleton instance {@code nil}
* <p>
* There will be one instance of this class, {@link LuaValue#NIL}, per Java
* virtual machine. However, the {@link Varargs} instance {@link LuaValue#NONE}
* which is the empty list, is also considered treated as a nil value by
* default.
* <p>
* Although it is possible to test for nil using Java == operator, the
* recommended approach is to use the method {@link LuaValue#isnil()} instead.
* By using that any ambiguities between {@link LuaValue#NIL} and
* {@link LuaValue#NONE} are avoided.
*
* @see LuaValue
* @see LuaValue#NIL
*/
public class LuaNil extends LuaValue {
static final LuaNil _NIL = new LuaNil();
public static LuaValue s_metatable;
LuaNil() {}
@Override
public int type() {
return LuaValue.TNIL;
}
@Override
public String toString() {
return "nil";
}
@Override
public String typename() {
return "nil";
}
@Override
public String tojstring() {
return "nil";
}
@Override
public LuaValue not() {
return LuaValue.TRUE;
}
@Override
public boolean toboolean() {
return false;
}
@Override
public boolean isnil() {
return true;
}
@Override
public LuaValue getmetatable() {
return s_metatable;
}
@Override
public boolean equals(Object o) {
return o instanceof LuaNil;
}
@Override
public LuaValue checknotnil() {
return argerror("value");
}
@Override
public boolean isvalidkey() {
return false;
}
// optional argument conversions - nil alwas falls badk to default value
@Override
public boolean optboolean(boolean defval) { return defval; }
@Override
public LuaClosure optclosure(LuaClosure defval) { return defval; }
@Override
public double optdouble(double defval) { return defval; }
@Override
public LuaFunction optfunction(LuaFunction defval) { return defval; }
@Override
public int optint(int defval) { return defval; }
@Override
public LuaInteger optinteger(LuaInteger defval) { return defval; }
@Override
public long optlong(long defval) { return defval; }
@Override
public LuaNumber optnumber(LuaNumber defval) { return defval; }
@Override
public LuaTable opttable(LuaTable defval) { return defval; }
@Override
public LuaThread optthread(LuaThread defval) { return defval; }
@Override
public String optjstring(String defval) { return defval; }
@Override
public LuaString optstring(LuaString defval) { return defval; }
@Override
public Object optuserdata(Object defval) { return defval; }
@Override
public Object optuserdata(Class c, Object defval) { return defval; }
@Override
public LuaValue optvalue(LuaValue defval) { return defval; }
}

View File

@@ -0,0 +1,97 @@
/*******************************************************************************
* Copyright (c) 2009-2011 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
/**
* Base class for representing numbers as lua values directly.
* <p>
* The main subclasses are {@link LuaInteger} which holds values that fit in a
* java int, and {@link LuaDouble} which holds all other number values.
*
* @see LuaInteger
* @see LuaDouble
* @see LuaValue
*
*/
abstract public class LuaNumber extends LuaValue {
/** Shared static metatable for all number values represented in lua. */
public static LuaValue s_metatable;
@Override
public int type() {
return TNUMBER;
}
@Override
public String typename() {
return "number";
}
@Override
public LuaNumber checknumber() {
return this;
}
@Override
public LuaNumber checknumber(String errmsg) {
return this;
}
@Override
public LuaNumber optnumber(LuaNumber defval) {
return this;
}
@Override
public LuaValue tonumber() {
return this;
}
@Override
public boolean isnumber() {
return true;
}
@Override
public boolean isstring() {
return true;
}
@Override
public LuaValue getmetatable() {
return s_metatable;
}
@Override
public LuaValue concat(LuaValue rhs) { return rhs.concatTo(this); }
@Override
public Buffer concat(Buffer rhs) { return rhs.concatTo(this); }
@Override
public LuaValue concatTo(LuaNumber lhs) { return strvalue().concatTo(lhs.strvalue()); }
@Override
public LuaValue concatTo(LuaString lhs) { return strvalue().concatTo(lhs); }
}

View File

@@ -0,0 +1,949 @@
/*******************************************************************************
* Copyright (c) 2009-2011 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
import java.io.ByteArrayInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import org.luaj.vm2.lib.MathLib;
/**
* Subclass of {@link LuaValue} for representing lua strings.
* <p>
* 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.
* <p>
* {@link LuaString} values are not considered mutable once constructed,
* so multiple {@link LuaString} values can chare a single byte array.
* <p>
* 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,
* instance construction should be via {@link LuaValue#valueOf(byte[])} or similar API.
* <p>
* Because of this pooling, users of LuaString <em>must not directly alter the
* bytes in a LuaString</em>, or undefined behavior will result.
* <p>
* 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)}
* are used to convert back and forth between UTF8 byte arrays and character arrays.
*
* @see LuaValue
* @see LuaValue#valueOf(String)
* @see LuaValue#valueOf(byte[])
*/
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
* 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 <em><b>must not be mutated directly</b></em> because
* 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;
/** The offset into the byte array, 0 means start at the first byte */
public final int m_offset;
/** The number of bytes that comprise this string */
public final int m_length;
/** 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
* 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.
* 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.
* 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
* to the same value. */
private static final class RecentShortStrings {
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.
* @param string Java String containing characters to encode as UTF8
* @return {@link LuaString} with UTF8 bytes corresponding to the supplied String
*/
public static LuaString valueOf(String string) {
char[] c = string.toCharArray();
byte[] b = new byte[lengthAsUtf8(c)];
encodeToUtf8(c, c.length, b, 0);
return valueUsing(b, 0, b.length);
}
/** Construct a {@link LuaString} for a portion of a byte array.
* <p>
* 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
* 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.
* <p>
* @param bytes byte buffer
* @param off offset into the byte buffer
* @param len length of the byte buffer
* @return {@link LuaString} wrapping the byte buffer
*/
public static LuaString valueOf(byte[] bytes, int off, int len) {
if (len > RECENT_STRINGS_MAX_LENGTH)
return valueFromCopy(bytes, off, len);
final int hash = hashCode(bytes, off, len);
final int bucket = hash & (RECENT_STRINGS_CACHE_SIZE - 1);
final LuaString t = RecentShortStrings.recent_short_strings[bucket];
if (t != null && t.m_hashcode == hash && t.byteseq(bytes, off, len)) return t;
final LuaString s = valueFromCopy(bytes, off, len);
RecentShortStrings.recent_short_strings[bucket] = s;
return s;
}
/** Construct a new LuaString using a copy of the bytes array supplied */
private static LuaString valueFromCopy(byte[] bytes, int off, int len) {
final byte[] copy = new byte[len];
System.arraycopy(bytes, off, copy, 0, len);
return new LuaString(copy, 0, len);
}
/** Construct a {@link LuaString} around, possibly using the the supplied
* byte array as the backing store.
* <p>
* The caller must ensure that the array is not mutated after the call.
* However, if the string is short enough the short-string cache is checked
* for a match which may be used instead of the supplied byte array.
* <p>
* @param bytes byte buffer
* @return {@link LuaString} wrapping the byte buffer, or an equivalent string.
*/
static public LuaString valueUsing(byte[] bytes, int off, int len) {
if (bytes.length > RECENT_STRINGS_MAX_LENGTH)
return new LuaString(bytes, off, len);
final int hash = hashCode(bytes, off, len);
final int bucket = hash & (RECENT_STRINGS_CACHE_SIZE - 1);
final LuaString t = RecentShortStrings.recent_short_strings[bucket];
if (t != null && t.m_hashcode == hash && t.byteseq(bytes, off, len)) return t;
final LuaString s = new LuaString(bytes, off, len);
RecentShortStrings.recent_short_strings[bucket] = s;
return s;
}
/** Construct a {@link LuaString} using the supplied characters as byte values.
* <p>
* Only the low-order 8-bits of each character are used, the remainder is ignored.
* <p>
* 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);
}
/** Construct a {@link LuaString} using the supplied characters as byte values.
* <p>
* Only the low-order 8-bits of each character are used, the remainder is ignored.
* <p>
* 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];
for ( int i=0; i<len; i++ )
b[i] = (byte) bytes[i + off];
return valueUsing(b, 0, len);
}
/** Construct a {@link LuaString} for all the bytes in a byte array.
* <p>
* The LuaString returned will either be a new LuaString containing a copy
* of the bytes array, or be an existing LuaString used already having the same value.
* <p>
* @param bytes byte buffer
* @return {@link LuaString} wrapping the byte buffer
*/
public static LuaString valueOf(byte[] bytes) {
return valueOf(bytes, 0, bytes.length);
}
/** Construct a {@link LuaString} for all the bytes in a byte array, possibly using
* the supplied array as the backing store.
* <p>
* The LuaString returned will either be a new LuaString containing the byte array,
* or be an existing LuaString used already having the same value.
* <p>
* 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
*/
public static LuaString valueUsing(byte[] bytes) {
return valueUsing(bytes, 0, bytes.length);
}
/** Construct a {@link LuaString} around a byte array without copying the contents.
* <p>
* The array is used directly after this is called, so clients must not change contents.
* <p>
* @param bytes byte buffer
* @param offset offset into the byte buffer
* @param length length of the byte buffer
* @return {@link LuaString} wrapping the byte buffer
*/
private LuaString(byte[] bytes, int offset, int length) {
this.m_bytes = bytes;
this.m_offset = offset;
this.m_length = length;
this.m_hashcode = hashCode(bytes, offset, length);
}
public boolean isstring() {
return true;
}
public LuaValue getmetatable() {
return s_metatable;
}
public int type() {
return LuaValue.TSTRING;
}
public String typename() {
return "string";
}
public String tojstring() {
return decodeAsUtf8(m_bytes, m_offset, m_length);
}
// unary operators
public LuaValue neg() { double d = scannumber(); return Double.isNaN(d)? super.neg(): valueOf(-d); }
// basic binary arithmetic
public LuaValue add( LuaValue rhs ) { double d = scannumber(); return Double.isNaN(d)? arithmt(ADD,rhs): rhs.add(d); }
public LuaValue add( double rhs ) { return valueOf( checkarith() + rhs ); }
public LuaValue add( int rhs ) { return valueOf( checkarith() + rhs ); }
public LuaValue sub( LuaValue rhs ) { double d = scannumber(); return Double.isNaN(d)? arithmt(SUB,rhs): rhs.subFrom(d); }
public LuaValue sub( double rhs ) { return valueOf( checkarith() - rhs ); }
public LuaValue sub( int rhs ) { return valueOf( checkarith() - rhs ); }
public LuaValue subFrom( double lhs ) { return valueOf( lhs - checkarith() ); }
public LuaValue mul( LuaValue rhs ) { double d = scannumber(); return Double.isNaN(d)? arithmt(MUL,rhs): rhs.mul(d); }
public LuaValue mul( double rhs ) { return valueOf( checkarith() * rhs ); }
public LuaValue mul( int rhs ) { return valueOf( checkarith() * rhs ); }
public LuaValue pow( LuaValue rhs ) { double d = scannumber(); return Double.isNaN(d)? arithmt(POW,rhs): rhs.powWith(d); }
public LuaValue pow( double rhs ) { return MathLib.dpow(checkarith(),rhs); }
public LuaValue pow( int rhs ) { return MathLib.dpow(checkarith(),rhs); }
public LuaValue powWith( double lhs ) { return MathLib.dpow(lhs, checkarith()); }
public LuaValue powWith( int lhs ) { return MathLib.dpow(lhs, checkarith()); }
public LuaValue div( LuaValue rhs ) { double d = scannumber(); return Double.isNaN(d)? arithmt(DIV,rhs): rhs.divInto(d); }
public LuaValue div( double rhs ) { return LuaDouble.ddiv(checkarith(),rhs); }
public LuaValue div( int rhs ) { return LuaDouble.ddiv(checkarith(),rhs); }
public LuaValue divInto( double lhs ) { return LuaDouble.ddiv(lhs, checkarith()); }
public LuaValue mod( LuaValue rhs ) { double d = scannumber(); return Double.isNaN(d)? arithmt(MOD,rhs): rhs.modFrom(d); }
public LuaValue mod( double rhs ) { return LuaDouble.dmod(checkarith(), rhs); }
public LuaValue mod( int rhs ) { return LuaDouble.dmod(checkarith(), rhs); }
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.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.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.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.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; }
// concatenation
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) {
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
public int strcmp(LuaValue lhs) { return -lhs.strcmp(this); }
public int strcmp(LuaString rhs) {
for ( int i=0, j=0; i<m_length && j<rhs.m_length; ++i, ++j ) {
if ( m_bytes[m_offset+i] != rhs.m_bytes[rhs.m_offset+j] ) {
return ((int)m_bytes[m_offset+i]) - ((int) rhs.m_bytes[rhs.m_offset+j]);
}
}
return m_length - rhs.m_length;
}
/** Check for number in arithmetic, or throw aritherror */
private double checkarith() {
double d = scannumber();
if ( Double.isNaN(d) )
aritherror();
return d;
}
public int checkint() {
return (int) (long) checkdouble();
}
public LuaInteger checkinteger() {
return valueOf(checkint());
}
public long checklong() {
return (long) checkdouble();
}
public double checkdouble() {
double d = scannumber();
if ( Double.isNaN(d) )
argerror("number");
return d;
}
public LuaNumber checknumber() {
return valueOf(checkdouble());
}
public LuaNumber checknumber(String msg) {
double d = scannumber();
if ( Double.isNaN(d) )
error(msg);
return valueOf(d);
}
public boolean isnumber() {
double d = scannumber();
return ! Double.isNaN(d);
}
public boolean isint() {
double d = scannumber();
if ( Double.isNaN(d) )
return false;
int i = (int) d;
return i == d;
}
public boolean islong() {
double d = scannumber();
if ( Double.isNaN(d) )
return false;
long l = (long) d;
return l == d;
}
public byte tobyte() { return (byte) toint(); }
public char tochar() { return (char) toint(); }
public double todouble() { double d=scannumber(); return Double.isNaN(d)? 0: d; }
public float tofloat() { return (float) todouble(); }
public int toint() { return (int) tolong(); }
public long tolong() { return (long) todouble(); }
public short toshort() { return (short) toint(); }
public double optdouble(double defval) {
return checkdouble();
}
public int optint(int defval) {
return checkint();
}
public LuaInteger optinteger(LuaInteger defval) {
return checkinteger();
}
public long optlong(long defval) {
return checklong();
}
public LuaNumber optnumber(LuaNumber defval) {
return checknumber();
}
public LuaString optstring(LuaString defval) {
return this;
}
public LuaValue tostring() {
return this;
}
public String optjstring(String defval) {
return tojstring();
}
public LuaString strvalue() {
return this;
}
/** Take a substring using Java zero-based indexes for begin and end or range.
* @param beginIndex The zero-based index of the first character to include.
* @param endIndex The zero-based index of position after the last character.
* @return LuaString which is a substring whose first character is at offset
* beginIndex and extending for (endIndex - beginIndex ) characters.
*/
public LuaString substring( int beginIndex, int endIndex ) {
final int off = m_offset + beginIndex;
final int len = endIndex - beginIndex;
return len >= m_length / 2?
valueUsing(m_bytes, off, len):
valueOf(m_bytes, off, len);
}
public int hashCode() {
return m_hashcode;
}
/** Compute the hash code of a sequence of bytes within a byte array using
* lua's rules for string hashes. For long strings, not all bytes are hashed.
* @param bytes byte array containing the bytes.
* @param offset offset into the hash for the first byte.
* @param length number of bytes starting with offset that are part of the string.
* @return hash for the string defined by bytes, offset, and length.
*/
public static int hashCode(byte[] bytes, int offset, int length) {
int h = length; /* seed */
int step = (length>>5)+1; /* if string is too long, don't hash all its chars */
for (int l1=length; l1>=step; l1-=step) /* compute hash */
h = h ^ ((h<<5)+(h>>2)+(((int) bytes[offset+l1-1] ) & 0x0FF ));
return h;
}
// object comparison, used in key comparison
public boolean equals( Object o ) {
if ( o instanceof LuaString ) {
return raweq( (LuaString) o );
}
return false;
}
// equality w/ metatable processing
public LuaValue eq( LuaValue val ) { return val.raweq(this)? TRUE: FALSE; }
public boolean eq_b( LuaValue val ) { return val.raweq(this); }
// equality w/o metatable processing
public boolean raweq( LuaValue val ) {
return val.raweq(this);
}
public boolean raweq( LuaString s ) {
if ( this == s )
return true;
if ( s.m_length != m_length )
return false;
if ( s.m_bytes == m_bytes && s.m_offset == m_offset )
return true;
if ( s.hashCode() != hashCode() )
return false;
for ( int i=0; i<m_length; i++ )
if ( s.m_bytes[s.m_offset+i] != m_bytes[m_offset+i] )
return false;
return true;
}
public static boolean equals( LuaString a, int i, LuaString b, int j, int n ) {
return equals( a.m_bytes, a.m_offset + i, b.m_bytes, b.m_offset + j, n );
}
/** Return true if the bytes in the supplied range match this LuaStrings bytes. */
private boolean byteseq(byte[] bytes, int off, int len) {
return (m_length == len && equals(m_bytes, m_offset, bytes, off, len));
}
public static boolean equals( byte[] a, int i, byte[] b, int j, int n ) {
if ( a.length < i + n || b.length < j + n )
return false;
while ( --n>=0 )
if ( a[i++]!=b[j++] )
return false;
return true;
}
public void write(DataOutputStream writer, int i, int len) throws IOException {
writer.write(m_bytes,m_offset+i,len);
}
public LuaValue len() {
return LuaInteger.valueOf(m_length);
}
public int length() {
return m_length;
}
public int rawlen() {
return m_length;
}
public int luaByte(int index) {
return m_bytes[m_offset + index] & 0x0FF;
}
public int charAt( int index ) {
if ( index < 0 || index >= m_length )
throw new IndexOutOfBoundsException();
return luaByte( index );
}
public String checkjstring() {
return tojstring();
}
public LuaString checkstring() {
return this;
}
/** Convert value to an input stream.
*
* @return {@link InputStream} whose data matches the bytes in this {@link LuaString}
*/
public InputStream toInputStream() {
return new ByteArrayInputStream(m_bytes, m_offset, m_length);
}
/**
* 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
* @param len number of bytes to copy
*/
public void copyInto( int strOffset, byte[] bytes, int arrayOffset, int len ) {
System.arraycopy( m_bytes, m_offset+strOffset, bytes, arrayOffset, len );
}
/** Java version of strpbrk - find index of any byte that in an accept string.
* @param accept {@link LuaString} containing characters to look for.
* @return index of first match in the {@code accept} string, or -1 if not found.
*/
public int indexOfAny( LuaString accept ) {
final int ilimit = m_offset + m_length;
final int jlimit = accept.m_offset + accept.m_length;
for ( int i = m_offset; i < ilimit; ++i ) {
for ( int j = accept.m_offset; j < jlimit; ++j ) {
if ( m_bytes[i] == accept.m_bytes[j] ) {
return i - m_offset;
}
}
}
return -1;
}
/**
* Find the index of a byte starting at a point in this string
* @param b the byte to look for
* @param start the first index in the string
* @return index of first match found, or -1 if not found.
*/
public int indexOf( byte b, int start ) {
for ( int i=start; i < m_length; ++i ) {
if ( m_bytes[m_offset+i] == b )
return i;
}
return -1;
}
/**
* Find the index of a string starting at a point in this string
* @param s the string to search for
* @param start the first index in the string
* @return index of first match found, or -1 if not found.
*/
public int indexOf( LuaString s, int start ) {
final int slen = s.length();
final int limit = m_length - slen;
for ( int i=start; i <= limit; ++i ) {
if ( equals( m_bytes, m_offset+i, s.m_bytes, s.m_offset, slen ) )
return i;
}
return -1;
}
/**
* Find the last index of a string in this string
* @param s the string to search for
* @return index of last match found, or -1 if not found.
*/
public int lastIndexOf( LuaString s ) {
final int slen = s.length();
final int limit = m_length - slen;
for ( int i=limit; i >= 0; --i ) {
if ( equals( m_bytes, m_offset+i, s.m_bytes, s.m_offset, slen ) )
return i;
}
return -1;
}
/**
* 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
* @see #lengthAsUtf8(char[])
* @see #encodeToUtf8(char[], int, byte[], int)
* @see #isValidUtf8()
*/
public static String decodeAsUtf8(byte[] bytes, int offset, int length) {
int i,j,n,b;
for ( i=offset,j=offset+length,n=0; i<j; ++n ) {
byte v = bytes[i++];
if ((v & 0xC0) == 0xC0) {
++i;
if ((v & 0xE0) == 0xE0) {
++i;
if ((v & 0xF0) == 0xF0) {
++i;
}
}
}
}
char[] chars=new char[n];
for ( i=offset,j=offset+length,n=0; i<j; ) {
chars[n++] = (char) (
((b=bytes[i++])>=0||i>=j)? b:
(b<-32||i+1>=j)? (((b&0x3f) << 6) | (bytes[i++]&0x3f)):
(((b&0xf) << 12) | ((bytes[i++]&0x3f)<<6) | (bytes[i++]&0x3f)));
}
return new String(chars);
}
/**
* Count the number of bytes required to encode the string as UTF-8.
* @param chars Array of unicode characters to be encoded as UTF-8
* @return count of bytes needed to encode using UTF-8
* @see #encodeToUtf8(char[], int, byte[], int)
* @see #decodeAsUtf8(byte[], int, int)
* @see #isValidUtf8()
*/
public static int lengthAsUtf8(char[] chars) {
int i, b;
char c;
for (i = 0, b = 0; i < chars.length; i++) {
if ((c = chars[i]) < 0x80 || (c >= 0xdc00 && c < 0xe000)) {
b += 1;
} else if (c < 0x800) {
b += 2;
} else if (c >= 0xd800 && c < 0xdc00) {
if (i + 1 < chars.length && chars[i+1] >= 0xdc00 && chars[i+1] < 0xe000) {
b += 4;
i++;
} else {
b += 1;
}
} else {
b += 3;
}
}
return b;
}
/**
* Encode the given Java string as UTF-8 bytes, writing the result to bytes
* starting at offset.
* <p>
* The string should be measured first with lengthAsUtf8
* to make sure the given byte array is large enough.
* @param chars Array of unicode characters to be encoded as UTF-8
* @param nchars Number of characters in the array to convert.
* @param bytes byte array to hold the result
* @param off offset into the byte array to start writing
* @return number of bytes converted.
* @see #lengthAsUtf8(char[])
* @see #decodeAsUtf8(byte[], int, int)
* @see #isValidUtf8()
*/
public static int encodeToUtf8(char[] chars, int nchars, byte[] bytes, int off) {
char c;
int j = off;
for (int i = 0; i < nchars; i++) {
if ((c = chars[i]) < 0x80) {
bytes[j++] = (byte) c;
} else if (c < 0x800) {
bytes[j++] = (byte) (0xC0 | ((c >> 6)));
bytes[j++] = (byte) (0x80 | (c & 0x3f));
} else if (c >= 0xd800 && c < 0xdc00) {
if (i + 1 < nchars && chars[i+1] >= 0xdc00 && chars[i+1] < 0xe000) {
int uc = 0x10000 + (((c & 0x3ff) << 10) | (chars[++i] & 0x3ff));
bytes[j++] = (byte) (0xF0 | ((uc >> 18)));
bytes[j++] = (byte) (0x80 | ((uc >> 12) & 0x3f));
bytes[j++] = (byte) (0x80 | ((uc >> 6) & 0x3f));
bytes[j++] = (byte) (0x80 | (uc & 0x3f));
} else {
bytes[j++] = (byte) '?';
}
} else if (c >= 0xdc00 && c < 0xe000) {
bytes[j++] = (byte) '?';
} else {
bytes[j++] = (byte) (0xE0 | ((c >> 12)));
bytes[j++] = (byte) (0x80 | ((c >> 6) & 0x3f));
bytes[j++] = (byte) (0x80 | (c & 0x3f));
}
}
return j - off;
}
/** Check that a byte sequence is valid UTF-8
* @return true if it is valid UTF-8, otherwise false
* @see #lengthAsUtf8(char[])
* @see #encodeToUtf8(char[], int, byte[], int)
* @see #decodeAsUtf8(byte[], int, int)
*/
public boolean isValidUtf8() {
for (int i = m_offset, j = m_offset + m_length; i < j;) {
int c = m_bytes[i++];
if (c >= 0)
continue;
if (((c & 0xE0) == 0xC0) && i < j && (m_bytes[i++] & 0xC0) == 0x80)
continue;
if (((c & 0xF0) == 0xE0) && i + 1 < j && (m_bytes[i++] & 0xC0) == 0x80 && (m_bytes[i++] & 0xC0) == 0x80)
continue;
if (((c & 0xF8) == 0xF0) && i + 2 < j && (m_bytes[i++] & 0xC0) == 0x80 && (m_bytes[i++] & 0xC0) == 0x80 && (m_bytes[i++] & 0xC0) == 0x80)
continue;
return false;
}
return true;
}
// --------------------- number conversion -----------------------
/**
* 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()
*/
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()
*/
public LuaValue tonumber( int base ) {
double d = scannumber( base );
return Double.isNaN(d)? NIL: valueOf(d);
}
private boolean isspace(byte c) {
return c == ' ' || (c >= '\t' && c <= '\r');
}
private boolean isdigit(byte c) {
return (c >= '0' && c <= '9');
}
private boolean isxdigit(byte c) {
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}
private int hexvalue(byte c) {
return c <= '9' ? c - '0' : c <= 'F' ? c + 10 - 'A' : c + 10 - 'a';
}
/**
* 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
*/
public double scannumber() {
int i = m_offset, j = m_offset + m_length;
while (i < j && isspace(m_bytes[i]))
++i;
while (i < j && isspace(m_bytes[j - 1]))
--j;
if (i >= j)
return Double.NaN;
if (indexOf((byte) 'x', i - m_offset) != -1 || indexOf((byte) 'X', i - m_offset) != -1)
return strx2number(i, j);
return scandouble(i, j);
}
private double strx2number(int start, int end) {
double sgn = (m_bytes[start] == '-') ? -1.0 : 1.0;
if (sgn == -1.0 || m_bytes[start] == '+')
++start;
if (start + 2 >= end)
return Double.NaN;
if (m_bytes[start++] != '0')
return Double.NaN;
if (m_bytes[start] != 'x' && m_bytes[start] != 'X')
return Double.NaN;
++start;
double m = 0;
int e = 0;
boolean i = isxdigit(m_bytes[start]);
while (start < end && isxdigit(m_bytes[start]))
m = (m * 16) + hexvalue(m_bytes[start++]);
if (start < end && m_bytes[start] == '.') {
++start;
while (start < end && isxdigit(m_bytes[start])) {
m = (m * 16) + hexvalue(m_bytes[start++]);
e -= 4;
}
}
if (!i && e == 0)
return Double.NaN;
if (start < end && (m_bytes[start] == 'p' || m_bytes[start] == 'P')) {
++start;
int exp1 = 0;
boolean neg1 = false;
if (start < end) {
if (m_bytes[start] == '-')
neg1 = true;
if (neg1 || m_bytes[start] == '+')
++start;
}
if (start >= end || !isdigit(m_bytes[start]))
return Double.NaN;
while (start < end && isdigit(m_bytes[start]))
exp1 = exp1 * 10 + m_bytes[start++] - '0';
if (neg1)
exp1 = -exp1;
e += exp1;
}
if (start != end)
return Double.NaN;
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
*/
public double scannumber(int base) {
if ( base < 2 || base > 36 )
return Double.NaN;
int i=m_offset,j=m_offset+m_length;
while ( i<j && isspace(m_bytes[i]) ) ++i;
while ( i<j && isspace(m_bytes[j-1]) ) --j;
if ( i>=j )
return Double.NaN;
return scanlong( base, i, j );
}
/**
* Scan and convert a long value, or return Double.NaN if not found.
* @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,
* or Double.NaN if not
*/
private double scanlong( int base, int start, int end ) {
long x = 0;
boolean neg = (m_bytes[start] == '-');
if (neg || m_bytes[start] == '+') start++;
for ( int i=start; i<end; i++ ) {
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;
x = x * base + digit;
if ( x < 0 )
return Double.NaN; // overflow
}
return neg? -x: x;
}
/**
* 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,
* or Double.NaN if not
*/
private double scandouble(int start, int end) {
if ( end>start+64 ) end=start+64;
for ( int i=start; i<end; i++ ) {
switch ( m_bytes[i] ) {
case '-':
case '+':
case '.':
case 'e': case 'E':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
break;
default:
return Double.NaN;
}
}
char [] c = new char[end-start];
for ( int i=start; i<end; i++ )
c[i-start] = (char) m_bytes[i];
try {
return Double.parseDouble(new String(c));
} catch ( Exception e ) {
return Double.NaN;
}
}
/**
* Print the bytes of the LuaString to a PrintStream as if it were
* an ASCII string, quoting and escaping control characters.
* @param ps PrintStream to print to.
*/
public void printToStream(PrintStream ps) {
for (int i = 0, n = m_length; i < n; i++) {
int c = m_bytes[m_offset+i];
ps.print((char) c);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,259 @@
/*******************************************************************************
* Copyright (c) 2007-2012 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.
******************************************************************************/
package org.luaj.vm2;
import java.lang.ref.WeakReference;
/**
* Subclass of {@link LuaValue} that implements a lua coroutine thread using
* Java Threads.
* <p>
* A LuaThread is typically created in response to a scripted call to
* {@code coroutine.create()}
* <p>
* The threads must be initialized with the globals, so that the global
* environment may be passed along according to rules of lua. This is done via
* the constructor arguments {@link #LuaThread(Globals)} or
* {@link #LuaThread(Globals, LuaValue)}.
* <p>
* The utility classes {@link org.luaj.vm2.lib.jse.JsePlatform} and
* {@link org.luaj.vm2.lib.jme.JmePlatform} see to it that this {@link Globals}
* are initialized properly.
* <p>
* The behavior of coroutine threads matches closely the behavior of C coroutine
* library. However, because of the use of Java threads to manage call state, it
* is possible to yield from anywhere in luaj.
* <p>
* Each Java thread wakes up at regular intervals and checks a weak reference to
* determine if it can ever be resumed. If not, it throws {@link OrphanedThread}
* which is an {@link java.lang.Error}. Applications should not catch
* {@link OrphanedThread}, because it can break the thread safety of luaj. The
* value controlling the polling interval is
* {@link #thread_orphan_check_interval} and may be set by the user.
* <p>
* There are two main ways to abandon a coroutine. The first is to call
* {@code yield()} from lua, or equivalently {@link Globals#yield(Varargs)}, and
* arrange to have it never resumed possibly by values passed to yield. The
* second is to throw {@link OrphanedThread}, which should put the thread in a
* dead state. In either case all references to the thread must be dropped, and
* the garbage collector must run for the thread to be garbage collected.
*
*
* @see LuaValue
* @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform
* @see org.luaj.vm2.lib.CoroutineLib
*/
public class LuaThread extends LuaValue {
/** Shared metatable for lua threads. */
public static LuaValue s_metatable;
/** The current number of coroutines. Should not be set. */
public static int coroutine_count = 0;
/**
* Polling interval, in milliseconds, which each thread uses while waiting
* to return from a yielded state to check if the lua threads is no longer
* referenced and therefore should be garbage collected. A short polling
* interval for many threads will consume server resources. Orphaned threads
* cannot be detected and collected unless garbage collection is run. This
* can be changed by Java startup code if desired.
*/
public static long thread_orphan_check_interval = 5000;
public static final int STATUS_INITIAL = 0;
public static final int STATUS_SUSPENDED = 1;
public static final int STATUS_RUNNING = 2;
public static final int STATUS_NORMAL = 3;
public static final int STATUS_DEAD = 4;
public static final String[] STATUS_NAMES = { "suspended", "suspended", "running", "normal", "dead", };
public final State state;
public static final int MAX_CALLSTACK = 256;
/**
* Thread-local used by DebugLib to store debugging state. This is an opaque
* value that should not be modified by applications.
*/
public Object callstack;
public final Globals globals;
/** Error message handler for this thread, if any. */
public LuaValue errorfunc;
/** Private constructor for main thread only */
public LuaThread(Globals globals) {
state = new State(globals, this, null);
state.status = STATUS_RUNNING;
this.globals = globals;
}
/**
* Create a LuaThread around a function and environment
*
* @param func The function to execute
*/
public LuaThread(Globals globals, LuaValue func) {
LuaValue.assert_(func != null, "function cannot be null");
state = new State(globals, this, func);
this.globals = globals;
}
@Override
public int type() {
return LuaValue.TTHREAD;
}
@Override
public String typename() {
return "thread";
}
@Override
public boolean isthread() {
return true;
}
@Override
public LuaThread optthread(LuaThread defval) {
return this;
}
@Override
public LuaThread checkthread() {
return this;
}
@Override
public LuaValue getmetatable() {
return s_metatable;
}
public String getStatus() { return STATUS_NAMES[state.status]; }
public boolean isMainThread() { return this.state.function == null; }
public Varargs resume(Varargs args) {
final LuaThread.State s = this.state;
if (s.status > LuaThread.STATUS_SUSPENDED)
return LuaValue.varargsOf(LuaValue.FALSE, LuaValue.valueOf(
"cannot resume " + (s.status == LuaThread.STATUS_DEAD? "dead": "non-suspended") + " coroutine"));
return s.lua_resume(this, args);
}
public static class State implements Runnable {
private final Globals globals;
final WeakReference lua_thread;
public final LuaValue function;
Varargs args = LuaValue.NONE;
Varargs result = LuaValue.NONE;
String error = null;
/** Hook function control state used by debug lib. */
public LuaValue hookfunc;
public boolean hookline;
public boolean hookcall;
public boolean hookrtrn;
public int hookcount;
public boolean inhook;
public int lastline;
public int bytecodes;
public int status = LuaThread.STATUS_INITIAL;
State(Globals globals, LuaThread lua_thread, LuaValue function) {
this.globals = globals;
this.lua_thread = new WeakReference(lua_thread);
this.function = function;
}
@Override
public synchronized void run() {
try {
Varargs a = this.args;
this.args = LuaValue.NONE;
this.result = function.invoke(a);
} catch (Throwable t) {
this.error = t.getMessage();
} finally {
this.status = LuaThread.STATUS_DEAD;
this.notify();
}
}
public synchronized Varargs lua_resume(LuaThread new_thread, Varargs args) {
LuaThread previous_thread = globals.running;
try {
globals.running = new_thread;
this.args = args;
if (this.status == STATUS_INITIAL) {
this.status = STATUS_RUNNING;
new Thread(this, "Coroutine-" + (++coroutine_count)).start();
} else {
this.notify();
}
if (previous_thread != null)
previous_thread.state.status = STATUS_NORMAL;
this.status = STATUS_RUNNING;
this.wait();
return this.error != null? LuaValue.varargsOf(LuaValue.FALSE, LuaValue.valueOf(this.error))
: LuaValue.varargsOf(LuaValue.TRUE, this.result);
} catch (InterruptedException ie) {
throw new OrphanedThread();
} finally {
this.args = LuaValue.NONE;
this.result = LuaValue.NONE;
this.error = null;
globals.running = previous_thread;
if (previous_thread != null)
globals.running.state.status = STATUS_RUNNING;
}
}
public synchronized Varargs lua_yield(Varargs args) {
try {
this.result = args;
this.status = STATUS_SUSPENDED;
this.notify();
do {
this.wait(thread_orphan_check_interval);
if (this.lua_thread.get() == null) {
this.status = STATUS_DEAD;
throw new OrphanedThread();
}
} while ( this.status == STATUS_SUSPENDED );
return this.args;
} catch (InterruptedException ie) {
this.status = STATUS_DEAD;
throw new OrphanedThread();
} finally {
this.args = LuaValue.NONE;
this.result = LuaValue.NONE;
}
}
}
}

View File

@@ -0,0 +1,156 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
public class LuaUserdata extends LuaValue {
public Object m_instance;
public LuaValue m_metatable;
public LuaUserdata(Object obj) {
m_instance = obj;
}
public LuaUserdata(Object obj, LuaValue metatable) {
m_instance = obj;
m_metatable = metatable;
}
@Override
public String tojstring() {
return String.valueOf(m_instance);
}
@Override
public int type() {
return LuaValue.TUSERDATA;
}
@Override
public String typename() {
return "userdata";
}
@Override
public int hashCode() {
return m_instance.hashCode();
}
public Object userdata() {
return m_instance;
}
@Override
public boolean isuserdata() { return true; }
@Override
public boolean isuserdata(Class c) { return c.isAssignableFrom(m_instance.getClass()); }
@Override
public Object touserdata() { return m_instance; }
@Override
public Object touserdata(Class c) { return c.isAssignableFrom(m_instance.getClass())? m_instance: null; }
@Override
public Object optuserdata(Object defval) { return m_instance; }
@Override
public Object optuserdata(Class c, Object defval) {
if (!c.isAssignableFrom(m_instance.getClass()))
typerror(c.getName());
return m_instance;
}
@Override
public LuaValue getmetatable() {
return m_metatable;
}
@Override
public LuaValue setmetatable(LuaValue metatable) {
this.m_metatable = metatable;
return this;
}
@Override
public Object checkuserdata() {
return m_instance;
}
@Override
public Object checkuserdata(Class c) {
if (c.isAssignableFrom(m_instance.getClass()))
return m_instance;
return typerror(c.getName());
}
@Override
public LuaValue get(LuaValue key) {
return m_metatable != null? gettable(this, key): NIL;
}
@Override
public void set(LuaValue key, LuaValue value) {
if (m_metatable == null || !settable(this, key, value))
error("cannot set " + key + " for userdata");
}
@Override
public boolean equals(Object val) {
if (this == val)
return true;
if (!(val instanceof LuaUserdata))
return false;
LuaUserdata u = (LuaUserdata) val;
return m_instance.equals(u.m_instance);
}
// equality w/ metatable processing
@Override
public LuaValue eq(LuaValue val) { return eq_b(val)? TRUE: FALSE; }
@Override
public boolean eq_b(LuaValue val) {
if (val.raweq(this))
return true;
if (m_metatable == null || !val.isuserdata())
return false;
LuaValue valmt = val.getmetatable();
return valmt != null && LuaValue.eqmtcall(this, m_metatable, val, valmt);
}
// equality w/o metatable processing
@Override
public boolean raweq(LuaValue val) { return val.raweq(this); }
@Override
public boolean raweq(LuaUserdata val) {
return this == val || m_metatable == val.m_metatable && m_instance.equals(val.m_instance);
}
// __eq metatag processing
public boolean eqmt(LuaValue val) {
return m_metatable != null && val.isuserdata()? LuaValue.eqmtcall(this, m_metatable, val, val.getmetatable())
: false;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,51 @@
/*******************************************************************************
* Copyright (c) 2013 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
import org.luaj.vm2.LuaTable.Slot;
/**
* Provides operations that depend on the __mode key of the metatable.
*/
interface Metatable {
/** Return whether or not this table's keys are weak. */
boolean useWeakKeys();
/** Return whether or not this table's values are weak. */
boolean useWeakValues();
/** Return this metatable as a LuaValue. */
LuaValue toLuaValue();
/** Return an instance of Slot appropriate for the given key and value. */
Slot entry(LuaValue key, LuaValue value);
/** Returns the given value wrapped in a weak reference if appropriate. */
LuaValue wrap(LuaValue value);
/**
* Returns the value at the given index in the array, or null if it is a
* weak reference that has been dropped.
*/
LuaValue arrayget(LuaValue[] array, int index);
}

View File

@@ -0,0 +1,42 @@
package org.luaj.vm2;
import org.luaj.vm2.LuaTable.Slot;
class NonTableMetatable implements Metatable {
private final LuaValue value;
public NonTableMetatable(LuaValue value) {
this.value = value;
}
@Override
public boolean useWeakKeys() {
return false;
}
@Override
public boolean useWeakValues() {
return false;
}
@Override
public LuaValue toLuaValue() {
return value;
}
@Override
public Slot entry(LuaValue key, LuaValue value) {
return LuaTable.defaultEntry(key, value);
}
@Override
public LuaValue wrap(LuaValue value) {
return value;
}
@Override
public LuaValue arrayget(LuaValue[] array, int index) {
return array[index];
}
}

View File

@@ -0,0 +1,45 @@
/*******************************************************************************
* Copyright (c) 2012 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
/**
* {@link java.lang.Error} sublcass that indicates a lua thread that is no
* longer referenced has been detected.
* <p>
* The java thread in which this is thrown should correspond to a
* {@link LuaThread} being used as a coroutine that could not possibly be
* resumed again because there are no more references to the LuaThread with
* which it is associated. Rather than locking up resources forever, this error
* is thrown, and should fall through all the way to the thread's
* {@link Thread#run()} method.
* <p>
* Java code mixed with the luaj vm should not catch this error because it may
* occur when the coroutine is not running, so any processing done during error
* handling could break the thread-safety of the application because other lua
* processing could be going on in a different thread.
*/
public class OrphanedThread extends Error {
public OrphanedThread() {
super("orphaned thread");
}
}

View File

@@ -0,0 +1,445 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
/**
* Debug helper class to pretty-print lua bytecodes.
*
* @see Prototype
* @see LuaClosure
*/
public class Print extends Lua {
/** opcode names */
private static final String STRING_FOR_NULL = "null";
public static PrintStream ps = System.out;
/** String names for each lua opcode value. */
public static final String[] OPNAMES = { "MOVE", "LOADK", "LOADKX", "LOADBOOL", "LOADNIL", "GETUPVAL", "GETTABUP",
"GETTABLE", "SETTABUP", "SETUPVAL", "SETTABLE", "NEWTABLE", "SELF", "ADD", "SUB", "MUL", "DIV", "MOD",
"POW", "UNM", "NOT", "LEN", "CONCAT", "JMP", "EQ", "LT", "LE", "TEST", "TESTSET", "CALL", "TAILCALL",
"RETURN", "FORLOOP", "FORPREP", "TFORCALL", "TFORLOOP", "SETLIST", "CLOSURE", "VARARG", "EXTRAARG", null, };
static void printString(PrintStream ps, final LuaString s) {
ps.print('"');
for (int i = 0, n = s.m_length; i < n; i++) {
int c = s.m_bytes[s.m_offset+i];
if (c >= ' ' && c <= '~' && c != '\"' && c != '\\')
ps.print((char) c);
else {
switch (c) {
case '"':
ps.print("\\\"");
break;
case '\\':
ps.print("\\\\");
break;
case 0x0007: /* bell */
ps.print("\\a");
break;
case '\b': /* backspace */
ps.print("\\b");
break;
case '\f': /* form feed */
ps.print("\\f");
break;
case '\t': /* tab */
ps.print("\\t");
break;
case '\r': /* carriage return */
ps.print("\\r");
break;
case '\n': /* newline */
ps.print("\\n");
break;
case 0x000B: /* vertical tab */
ps.print("\\v");
break;
default:
ps.print('\\');
ps.print(Integer.toString(1000+0xff & c).substring(1));
break;
}
}
}
ps.print('"');
}
static void printValue(PrintStream ps, LuaValue v) {
if (v == null) {
ps.print("null");
return;
}
switch (v.type()) {
case LuaValue.TSTRING:
printString(ps, (LuaString) v);
break;
default:
ps.print(v.tojstring());
}
}
static void printConstant(PrintStream ps, Prototype f, int i) {
printValue(ps, i < f.k.length? f.k[i]: LuaValue.valueOf("UNKNOWN_CONST_" + i));
}
static void printUpvalue(PrintStream ps, Upvaldesc u) {
ps.print(u.idx + " ");
printValue(ps, u.name);
}
/**
* Print the code in a prototype
*
* @param f the {@link Prototype}
*/
public static void printCode(Prototype f) {
int[] code = f.code;
int pc, n = code.length;
for (pc = 0; pc < n; pc++) {
pc = printOpCode(f, pc);
ps.println();
}
}
/**
* Print an opcode in a prototype
*
* @param f the {@link Prototype}
* @param pc the program counter to look up and print
* @return pc same as above or changed
*/
public static int printOpCode(Prototype f, int pc) {
return printOpCode(ps, f, pc);
}
/**
* Print an opcode in a prototype
*
* @param ps the {@link PrintStream} to print to
* @param f the {@link Prototype}
* @param pc the program counter to look up and print
* @return pc same as above or changed
*/
public static int printOpCode(PrintStream ps, Prototype f, int pc) {
int[] code = f.code;
int i = code[pc];
int o = GET_OPCODE(i);
int a = GETARG_A(i);
int b = GETARG_B(i);
int c = GETARG_C(i);
int bx = GETARG_Bx(i);
int sbx = GETARG_sBx(i);
int line = getline(f, pc);
ps.print(" " + (pc+1) + " ");
if (line > 0)
ps.print("[" + line + "] ");
else
ps.print("[-] ");
if (o >= OPNAMES.length-1) {
ps.print("UNKNOWN_OP_" + o + " ");
} else {
ps.print(OPNAMES[o] + " ");
switch (getOpMode(o)) {
case iABC:
ps.print(a);
if (getBMode(o) != OpArgN)
ps.print(" " + (ISK(b)? -1-INDEXK(b): b));
if (getCMode(o) != OpArgN)
ps.print(" " + (ISK(c)? -1-INDEXK(c): c));
break;
case iABx:
if (getBMode(o) == OpArgK) {
ps.print(a + " " + (-1-bx));
} else {
ps.print(a + " " + bx);
}
break;
case iAsBx:
if (o == OP_JMP)
ps.print(sbx);
else
ps.print(a + " " + sbx);
break;
}
switch (o) {
case OP_LOADK:
ps.print(" ; ");
printConstant(ps, f, bx);
break;
case OP_GETUPVAL:
case OP_SETUPVAL:
ps.print(" ; ");
if (b < f.upvalues.length) {
printUpvalue(ps, f.upvalues[b]);
} else {
ps.print("UNKNOWN_UPVALUE_" + b);
}
break;
case OP_GETTABUP:
ps.print(" ; ");
if (b < f.upvalues.length) {
printUpvalue(ps, f.upvalues[b]);
} else {
ps.print("UNKNOWN_UPVALUE_" + b);
}
ps.print(" ");
if (ISK(c))
printConstant(ps, f, INDEXK(c));
else
ps.print("-");
break;
case OP_SETTABUP:
ps.print(" ; ");
if (a < f.upvalues.length) {
printUpvalue(ps, f.upvalues[a]);
} else {
ps.print("UNKNOWN_UPVALUE_" + a);
}
ps.print(" ");
if (ISK(b))
printConstant(ps, f, INDEXK(b));
else
ps.print("-");
ps.print(" ");
if (ISK(c))
printConstant(ps, f, INDEXK(c));
else
ps.print("-");
break;
case OP_GETTABLE:
case OP_SELF:
if (ISK(c)) {
ps.print(" ; ");
printConstant(ps, f, INDEXK(c));
}
break;
case OP_SETTABLE:
case OP_ADD:
case OP_SUB:
case OP_MUL:
case OP_DIV:
case OP_POW:
case OP_EQ:
case OP_LT:
case OP_LE:
if (ISK(b) || ISK(c)) {
ps.print(" ; ");
if (ISK(b))
printConstant(ps, f, INDEXK(b));
else
ps.print("-");
ps.print(" ");
if (ISK(c))
printConstant(ps, f, INDEXK(c));
else
ps.print("-");
}
break;
case OP_JMP:
case OP_FORLOOP:
case OP_FORPREP:
ps.print(" ; to " + (sbx+pc+2));
break;
case OP_CLOSURE:
if (bx < f.p.length) {
ps.print(" ; " + f.p[bx].getClass().getName());
} else {
ps.print(" ; UNKNOWN_PROTYPE_" + bx);
}
break;
case OP_SETLIST:
if (c == 0)
ps.print(" ; " + code[++pc] + " (stored in the next OP)");
else
ps.print(" ; " + c);
break;
case OP_VARARG:
ps.print(" ; is_vararg=" + f.is_vararg);
break;
default:
break;
}
}
return pc;
}
private static int getline(Prototype f, int pc) {
return pc > 0 && f.lineinfo != null && pc < f.lineinfo.length? f.lineinfo[pc]: -1;
}
static void printHeader(Prototype f) {
String s = String.valueOf(f.source);
if (s.startsWith("@") || s.startsWith("="))
s = s.substring(1);
else if ("\033Lua".equals(s))
s = "(bstring)";
else
s = "(string)";
String a = f.linedefined == 0? "main": "function";
ps.print("\n%" + a + " <" + s + ":" + f.linedefined + "," + f.lastlinedefined + "> (" + f.code.length
+ " instructions, " + f.code.length*4 + " bytes at " + id(f) + ")\n");
ps.print(f.numparams + " param, " + f.maxstacksize + " slot, " + f.upvalues.length + " upvalue, ");
ps.print(f.locvars.length + " local, " + f.k.length + " constant, " + f.p.length + " function\n");
}
static void printConstants(Prototype f) {
int i, n = f.k.length;
ps.print("constants (" + n + ") for " + id(f) + ":\n");
for (i = 0; i < n; i++) {
ps.print(" " + (i+1) + " ");
printValue(ps, f.k[i]);
ps.print("\n");
}
}
static void printLocals(Prototype f) {
int i, n = f.locvars.length;
ps.print("locals (" + n + ") for " + id(f) + ":\n");
for (i = 0; i < n; i++) {
ps.println(
" " + i + " " + f.locvars[i].varname + " " + (f.locvars[i].startpc+1) + " " + (f.locvars[i].endpc+1));
}
}
static void printUpValues(Prototype f) {
int i, n = f.upvalues.length;
ps.print("upvalues (" + n + ") for " + id(f) + ":\n");
for (i = 0; i < n; i++) {
ps.print(" " + i + " " + f.upvalues[i] + "\n");
}
}
/**
* Pretty-prints contents of a Prototype.
*
* @param prototype Prototype to print.
*/
public static void print(Prototype prototype) {
printFunction(prototype, true);
}
/**
* Pretty-prints contents of a Prototype in short or long form.
*
* @param prototype Prototype to print.
* @param full true to print all fields, false to print short form.
*/
public static void printFunction(Prototype prototype, boolean full) {
int i, n = prototype.p.length;
printHeader(prototype);
printCode(prototype);
if (full) {
printConstants(prototype);
printLocals(prototype);
printUpValues(prototype);
}
for (i = 0; i < n; i++)
printFunction(prototype.p[i], full);
}
private static void format(String s, int maxcols) {
int n = s.length();
if (n > maxcols)
ps.print(s.substring(0, maxcols));
else {
ps.print(s);
for (int i = maxcols-n; --i >= 0;)
ps.print(' ');
}
}
private static String id(Prototype f) {
return "Proto";
}
private void _assert(boolean b) {
if (!b)
throw new NullPointerException("_assert failed");
}
/**
* Print the state of a {@link LuaClosure} that is being executed
*
* @param cl the {@link LuaClosure}
* @param pc the program counter
* @param stack the stack of {@link LuaValue}
* @param top the top of the stack
* @param varargs any {@link Varargs} value that may apply
*/
public static void printState(LuaClosure cl, int pc, LuaValue[] stack, int top, Varargs varargs) {
// print opcode into buffer
PrintStream previous = ps;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ps = new PrintStream(baos);
printOpCode(cl.p, pc);
ps.flush();
ps.close();
ps = previous;
format(baos.toString(), 50);
printStack(stack, top, varargs);
ps.println();
}
public static void printStack(LuaValue[] stack, int top, Varargs varargs) {
// print stack
ps.print('[');
for (int i = 0; i < stack.length; i++) {
LuaValue v = stack[i];
if (v == null)
ps.print(STRING_FOR_NULL);
else
switch (v.type()) {
case LuaValue.TSTRING:
LuaString s = v.checkstring();
ps.print(s.length() < 48? s.tojstring()
: s.substring(0, 32).tojstring() + "...+" + (s.length()-32) + "b");
break;
case LuaValue.TFUNCTION:
ps.print(v.tojstring());
break;
case LuaValue.TUSERDATA:
Object o = v.touserdata();
if (o != null) {
String n = o.getClass().getName();
n = n.substring(n.lastIndexOf('.')+1);
ps.print(n + ": " + Integer.toHexString(o.hashCode()));
} else {
ps.print(v.toString());
}
break;
default:
ps.print(v.tojstring());
}
if (i+1 == top)
ps.print(']');
ps.print(" | ");
}
ps.print(varargs);
}
}

View File

@@ -0,0 +1,180 @@
/*******************************************************************************
* Copyright (c) 2009-2011 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
/**
* Prototype representing compiled lua code.
*
* <p>
* This is both a straight translation of the corresponding C type, and the main
* data structure for execution of compiled lua bytecode.
*
* <p>
* Generally, the {@link Prototype} is not constructed directly is an
* intermediate result as lua code is loaded using
* {@link Globals#load(java.io.Reader, String)}:
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* globals.load(new StringReader("print 'hello'"), "main.lua").call();
* }
* </pre>
*
* <p>
* To create a {@link Prototype} directly, a compiler such as
* {@link org.luaj.vm2.compiler.LuaC} may be used:
*
* <pre>
* {
* &#64;code
* InputStream is = new ByteArrayInputStream("print('hello,world')".getBytes());
* Prototype p = LuaC.instance.compile(is, "script");
* }
* </pre>
*
* To simplify loading, the
* {@link Globals#compilePrototype(java.io.InputStream, String)} method may be
* used:
*
* <pre>
* {
* &#64;code
* Prototype p = globals.compileProtoytpe(is, "script");
* }
* </pre>
*
* It may also be loaded from a {@link java.io.Reader} via
* {@link Globals#compilePrototype(java.io.Reader, String)}:
*
* <pre>
* {
* &#64;code
* Prototype p = globals.compileProtoytpe(new StringReader(script), "script");
* }
* </pre>
*
* To un-dump a binary file known to be a binary lua file that has been dumped
* to a string, the {@link Globals.Undumper} interface may be used:
*
* <pre>
* {
* &#64;code
* FileInputStream lua_binary_file = new FileInputStream("foo.lc"); // Known to be compiled lua.
* Prototype p = globals.undumper.undump(lua_binary_file, "foo.lua");
* }
* </pre>
*
* To execute the code represented by the {@link Prototype} it must be supplied
* to the constructor of a {@link LuaClosure}:
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* LuaClosure f = new LuaClosure(p, globals);
* f.call();
* }
* </pre>
*
* To simplify the debugging of prototype values, the contents may be printed
* using {@link Print#print}:
*
* <pre>
* {@code
* Print.print(p);
* }
* </pre>
* <p>
*
* @see LuaClosure
* @see Globals
* @see Globals#undumper
* @see Globals#compiler
* @see Print#print
*/
public class Prototype {
/* constants used by the function */
public LuaValue[] k;
public int[] code;
/* functions defined inside the function */
public Prototype[] p;
/* map from opcodes to source lines */
public int[] lineinfo;
/* information about local variables */
public LocVars[] locvars;
/* upvalue information */
public Upvaldesc[] upvalues;
public LuaString source;
public int linedefined;
public int lastlinedefined;
public int numparams;
public int is_vararg;
public int maxstacksize;
private static final Upvaldesc[] NOUPVALUES = {};
private static final Prototype[] NOSUBPROTOS = {};
public Prototype() {
p = NOSUBPROTOS;
upvalues = NOUPVALUES;
}
public Prototype(int n_upvalues) {
p = NOSUBPROTOS;
upvalues = new Upvaldesc[n_upvalues];
}
@Override
public String toString() {
return source + ":" + linedefined + "-" + lastlinedefined;
}
/**
* Get the name of a local variable.
*
* @param number the local variable number to look up
* @param pc the program counter
* @return the name, or null if not found
*/
public LuaString getlocalname(int number, int pc) {
int i;
for (i = 0; i < locvars.length && locvars[i].startpc <= pc; i++) {
if (pc < locvars[i].endpc) { /* is variable active? */
number--;
if (number == 0)
return locvars[i].varname;
}
}
return null; /* not found */
}
public String shortsource() {
String name = source.tojstring();
if (name.startsWith("@") || name.startsWith("="))
name = name.substring(1);
else if (name.startsWith("\033"))
name = "binary string";
return name;
}
}

View File

@@ -0,0 +1,105 @@
/*******************************************************************************
* Copyright (c) 2010-2011 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
/**
* Subclass of {@link Varargs} that represents a lua tail call in a Java library
* function execution environment.
* <p>
* Since Java doesn't have direct support for tail calls, any lua function whose
* {@link Prototype} contains the {@link Lua#OP_TAILCALL} bytecode needs a
* mechanism for tail calls when converting lua-bytecode to java-bytecode.
* <p>
* The tail call holds the next function and arguments, and the client a call to
* {@link #eval()} executes the function repeatedly until the tail calls are
* completed.
* <p>
* Normally, users of luaj need not concern themselves with the details of this
* mechanism, as it is built into the core execution framework.
*
* @see Prototype
* @see org.luaj.vm2.luajc.LuaJC
*/
public class TailcallVarargs extends Varargs {
private LuaValue func;
private Varargs args;
private Varargs result;
public TailcallVarargs(LuaValue f, Varargs args) {
this.func = f;
this.args = args;
}
public TailcallVarargs(LuaValue object, LuaValue methodname, Varargs args) {
this.func = object.get(methodname);
this.args = LuaValue.varargsOf(object, args);
}
@Override
public boolean isTailcall() { return true; }
@Override
public Varargs eval() {
while ( result == null ) {
Varargs r = func.onInvoke(args);
if (r.isTailcall()) {
TailcallVarargs t = (TailcallVarargs) r;
func = t.func;
args = t.args;
} else {
result = r;
func = null;
args = null;
}
}
return result;
}
@Override
public LuaValue arg(int i) {
if (result == null)
eval();
return result.arg(i);
}
@Override
public LuaValue arg1() {
if (result == null)
eval();
return result.arg1();
}
@Override
public int narg() {
if (result == null)
eval();
return result.narg();
}
@Override
public Varargs subargs(int start) {
if (result == null)
eval();
return result.subargs(start);
}
}

View File

@@ -0,0 +1,85 @@
/*******************************************************************************
* Copyright (c) 2009-2011 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
/**
* Upvalue used with Closure formulation
* <p>
*
* @see LuaClosure
* @see Prototype
*/
public final class UpValue {
LuaValue[] array; // initially the stack, becomes a holder
int index;
/**
* Create an upvalue relative to a stack
*
* @param stack the stack
* @param index the index on the stack for the upvalue
*/
public UpValue(LuaValue[] stack, int index) {
this.array = stack;
this.index = index;
}
@Override
public String toString() {
return index + "/" + array.length + " " + array[index];
}
/**
* Convert this upvalue to a Java String
*
* @return the Java String for this upvalue.
* @see LuaValue#tojstring()
*/
public String tojstring() {
return array[index].tojstring();
}
/**
* Get the value of the upvalue
*
* @return the {@link LuaValue} for this upvalue
*/
public LuaValue getValue() { return array[index]; }
/**
* Set the value of the upvalue
*
* @param value the {@link LuaValue} to set it to
*/
public void setValue(LuaValue value) { array[index] = value; }
/**
* Close this upvalue so it is no longer on the stack
*/
public void close() {
LuaValue[] old = array;
array = new LuaValue[] { old[index] };
old[index] = null;
index = 0;
}
}

View File

@@ -0,0 +1,45 @@
/*******************************************************************************
* Copyright (c) 2012 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
public class Upvaldesc {
/* upvalue name (for debug information) */
public LuaString name;
/* whether it is in stack */
public final boolean instack;
/* index of upvalue (in stack or in outer function's list) */
public final short idx;
public Upvaldesc(LuaString name, boolean instack, int idx) {
this.name = name;
this.instack = instack;
this.idx = (short) idx;
}
@Override
public String toString() {
return idx+(instack? " instack ": " closed ")+String.valueOf(name);
}
}

View File

@@ -0,0 +1,971 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
/**
* Class to encapsulate varargs values, either as part of a variable argument
* list, or multiple return values.
* <p>
* To construct varargs, use one of the static methods such as
* {@code LuaValue.varargsOf(LuaValue,LuaValue)}
* <p>
* <p>
* 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.
* <p>
* 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)}
* <p>
*
* @see LuaValue#varargsOf(LuaValue[])
* @see LuaValue#varargsOf(LuaValue, Varargs)
* @see LuaValue#varargsOf(LuaValue[], Varargs)
* @see LuaValue#varargsOf(LuaValue, LuaValue, Varargs)
* @see LuaValue#varargsOf(LuaValue[], int, int)
* @see LuaValue#varargsOf(LuaValue[], int, int, Varargs)
* @see LuaValue#subargs(int)
*/
public abstract class Varargs {
/**
* Get the n-th argument value (1-based).
*
* @param i the index of the argument to get, 1 is the first argument
* @return Value at position i, or LuaValue.NIL if there is none.
* @see Varargs#arg1()
* @see LuaValue#NIL
*/
abstract public LuaValue arg(int i);
/**
* 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.
*
* @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
*/
public Varargs eval() { return this; }
/**
* Return true if this is a TailcallVarargs
*
* @return true if a tail call, false otherwise
*/
public boolean isTailcall() { return false; }
// -----------------------------------------------------------------------
// utilities to get specific arguments and type-check them.
// -----------------------------------------------------------------------
/**
* 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
* @see LuaValue#TBOOLEAN
* @see LuaValue#TNUMBER
* @see LuaValue#TSTRING
* @see LuaValue#TTABLE
* @see LuaValue#TFUNCTION
* @see LuaValue#TUSERDATA
* @see LuaValue#TTHREAD
*/
public int type(int i) { return arg(i).type(); }
/**
* Tests if argument i is nil.
*
* @param i the index of the argument to test, 1 is the first argument
* @return true if the argument is nil or does not exist, false otherwise
* @see LuaValue#TNIL
*/
public boolean isnil(int i) { return arg(i).isnil(); }
/**
* Tests if argument i is a function.
*
* @param i the index of the argument to test, 1 is the first argument
* @return true if the argument exists and is a function or closure, false
* otherwise
* @see LuaValue#TFUNCTION
*/
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.
*
* @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 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.
*
* @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
* @see LuaValue#TSTRING
*/
public boolean isstring(int i) { return arg(i).isstring(); }
/**
* Tests if argument i is a table.
*
* @param i the index of the argument to test, 1 is the first argument
* @return true if the argument exists and is a lua table, false otherwise
* @see LuaValue#TTABLE
*/
public boolean istable(int i) { return arg(i).istable(); }
/**
* Tests if argument i is a thread.
*
* @param i the index of the argument to test, 1 is the first argument
* @return true if the argument exists and is a lua thread, false otherwise
* @see LuaValue#TTHREAD
*/
public boolean isthread(int i) { return arg(i).isthread(); }
/**
* Tests if argument i is a userdata.
*
* @param i the index of the argument to test, 1 is the first argument
* @return true if the argument exists and is a userdata, false otherwise
* @see LuaValue#TUSERDATA
*/
public boolean isuserdata(int i) { return arg(i).isuserdata(); }
/**
* Tests if a value exists at argument i.
*
* @param i the index of the argument to test, 1 is the first argument
* @return true if the argument exists, false otherwise
*/
public boolean isvalue(int i) { return i > 0 && i <= narg(); }
/**
* 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
* @exception LuaError if the argument is not a lua boolean
*/
public boolean optboolean(int i, boolean defval) { return arg(i).optboolean(defval); }
/**
* Return argument i as a closure, {@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 LuaClosure if argument i is a closure, or defval if not supplied
* or nil
* @exception LuaError if the argument is not a lua closure
*/
public LuaClosure optclosure(int i, LuaClosure defval) { return arg(i).optclosure(defval); }
/**
* Return argument i as a double, {@code defval} if nil, or throw a LuaError
* if it cannot be converted to one.
*
* @param i the index of the argument to test, 1 is the first argument
* @return java double value if argument i is a number or string that
* converts to a number, or defval if not supplied or nil
* @exception LuaError if the argument is not a number
*/
public double optdouble(int i, double defval) { return arg(i).optdouble(defval); }
/**
* Return argument i as a function, {@code defval} if nil, or throw a
* LuaError if an incompatible type.
*
* @param i the index of the argument to test, 1 is the first argument
* @return LuaValue that can be called if argument i is lua function or
* closure, or defval if not supplied or nil
* @exception LuaError if the argument is not a lua function or closure
*/
public LuaFunction optfunction(int i, LuaFunction defval) { return arg(i).optfunction(defval); }
/**
* Return argument i as a java int value, discarding any fractional part,
* {@code defval} if nil, or throw a LuaError if not a number.
*
* @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, or defval if not supplied or nil
* @exception LuaError if the argument is not a number
*/
public int optint(int i, int defval) { return arg(i).optint(defval); }
/**
* Return argument i as a java int value, {@code defval} if nil, or throw a
* LuaError 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
* @return LuaInteger value that fits in a java int without rounding, or
* defval if not supplied or nil
* @exception LuaError if the argument cannot be represented by a java int
* value
*/
public LuaInteger optinteger(int i, LuaInteger defval) { return arg(i).optinteger(defval); }
/**
* Return argument i as a java long value, discarding any fractional part,
* {@code defval} if nil, or throw a LuaError if not a number.
*
* @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, or defval if not supplied or nil
* @exception LuaError if the argument is not a number
*/
public long optlong(int i, long defval) { return arg(i).optlong(defval); }
/**
* Return argument i as a LuaNumber, {@code defval} if nil, or throw a
* LuaError 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, or
* defval if not supplied or nil
* @return LuaNumber if argument i is number or can be converted to a number
* @exception LuaError if the argument is not a number
*/
public LuaNumber optnumber(int i, LuaNumber defval) { return arg(i).optnumber(defval); }
/**
* Return argument i as a java String if a string or number, {@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 String value if argument i is a string or number, or defval if
* not supplied or nil
* @exception LuaError if the argument is not a string or number
*/
public String optjstring(int i, String defval) { return arg(i).optjstring(defval); }
/**
* Return argument i as a LuaString if a string or number, {@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 LuaString value if argument i is a string or number, or defval if
* not supplied or nil
* @exception LuaError if the argument is not a string or number
*/
public LuaString optstring(int i, LuaString defval) { return arg(i).optstring(defval); }
/**
* Return argument i as a LuaTable if a lua table, {@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 LuaTable value if a table, or defval if not supplied or nil
* @exception LuaError if the argument is not a lua table
*/
public LuaTable opttable(int i, LuaTable defval) { return arg(i).opttable(defval); }
/**
* Return argument i as a LuaThread if a lua thread, {@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 LuaThread value if a thread, or defval if not supplied or nil
* @exception LuaError if the argument is not a lua thread
*/
public LuaThread optthread(int i, LuaThread defval) { return arg(i).optthread(defval); }
/**
* Return argument i as a java Object if a userdata, {@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 java Object value if argument i is a userdata, or defval if not
* supplied or nil
* @exception LuaError if the argument is not a userdata
*/
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, {@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
* @return java Object value if argument i is a userdata whose instance
* Class c or a subclass, or defval if not supplied or nil
* @exception LuaError if the argument is not a userdata or from whose
* instance c is not assignable
*/
public Object optuserdata(int i, Class c, Object defval) { return arg(i).optuserdata(c, defval); }
/**
* Return argument i as a LuaValue if it exists, or {@code defval}.
*
* @param i the index of the argument to test, 1 is the first argument
* @return LuaValue value if the argument exists, defval if not
* @exception LuaError if the argument does not exist.
*/
public LuaValue optvalue(int i, LuaValue defval) { return i > 0 && i <= narg()? arg(i): defval; }
/**
* Return argument i as a boolean value, or throw an error 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
* @exception LuaError if the argument is not a lua boolean
*/
public boolean checkboolean(int i) { return arg(i).checkboolean(); }
/**
* Return argument i as a closure, or throw an error if any other type.
*
* @param i the index of the argument to test, 1 is the first argument
* @return LuaClosure if argument i is a closure.
* @exception LuaError if the argument is not a lua closure
*/
public LuaClosure checkclosure(int i) { return arg(i).checkclosure(); }
/**
* Return argument i as a double, 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 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).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
* @return LuaValue that can be called if argument i is lua function or
* closure
* @exception LuaError if the argument is not a lua function or closure
*/
public LuaFunction checkfunction(int i) { return arg(i).checkfunction(); }
/**
* 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 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).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
* @return LuaInteger value that fits in a java int without rounding
* @exception LuaError if the argument cannot be represented by a java int
* value
*/
public LuaInteger checkinteger(int i) { return arg(i).checkinteger(); }
/**
* 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 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).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
* @return LuaNumber if argument i is number or can be converted to a number
* @exception LuaError if the argument is not a number
*/
public LuaNumber checknumber(int i) { return arg(i).checknumber(); }
/**
* Return argument i as a java String if a string or number, or throw an
* error if any other type
*
* @param i the index of the argument to test, 1 is the first argument
* @return String value if argument i is a string or number
* @exception LuaError if the argument is not a string or number
*/
public String checkjstring(int i) { return arg(i).checkjstring(); }
/**
* Return argument i as a LuaString if a string or number, or throw an error
* if any other type
*
* @param i the index of the argument to test, 1 is the first argument
* @return LuaString value if argument i is a string or number
* @exception LuaError if the argument is not a string or number
*/
public LuaString checkstring(int i) { return arg(i).checkstring(); }
/**
* Return argument i as a LuaTable if a lua table, or throw an error if any
* other type.
*
* @param i the index of the argument to test, 1 is the first argument
* @return LuaTable value if a table
* @exception LuaError if the argument is not a lua table
*/
public LuaTable checktable(int i) { return arg(i).checktable(); }
/**
* Return argument i as a LuaThread if a lua thread, or throw an error if
* any other type.
*
* @param i the index of the argument to test, 1 is the first argument
* @return LuaThread value if a thread
* @exception LuaError if the argument is not a lua thread
*/
public LuaThread checkthread(int i) { return arg(i).checkthread(); }
/**
* Return argument i as a java Object if a userdata, or throw an error if
* any other type.
*
* @param i the index of the argument to test, 1 is the first argument
* @return java Object value if argument i is a userdata
* @exception LuaError if the argument is not a userdata
*/
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, 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
* @return java Object value if argument i is a userdata whose instance
* Class c or a subclass
* @exception LuaError if the argument is not a userdata or from whose
* instance c is not assignable
*/
public Object checkuserdata(int i, Class c) { return arg(i).checkuserdata(c); }
/**
* Return argument i as a LuaValue if it exists, or throw an error.
*
* @param i the index of the argument to test, 1 is the first argument
* @return LuaValue value if the argument exists
* @exception LuaError if the argument does not exist.
*/
public LuaValue checkvalue(int i) { return i <= narg()? arg(i): LuaValue.argerror(i, "value expected"); }
/**
* Return argument i as a LuaValue if it is not nil, or throw an error if it
* is nil.
*
* @param i the index of the argument to test, 1 is the first argument
* @return LuaValue value if the argument is not nil
* @exception LuaError if the argument doesn't exist or evaluates to nil.
*/
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 the supplied
* message, {@code msg}.
*
* @param test user supplied assertion to test against
* @param i the index to report in any error message
* @param msg the error message to use when the test fails
* @exception LuaError if the the value of {@code test} is {@code false}
*/
public void argcheck(boolean test, int i, String msg) {
if (!test)
LuaValue.argerror(i, msg);
}
/**
* Return true if there is no argument or nil at argument i.
*
* @param i the index of the argument to test, 1 is the first argument
* @return true if argument i contains either no argument or nil
*/
public boolean isnoneornil(int i) {
return i > narg() || arg(i).isnil();
}
/**
* 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, 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, 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
*/
public char tochar(int i) { return arg(i).tochar(); }
/**
* Return argument i as a java double value or 0 if not a number.
*
* @param i the index of the argument to convert, 1 is the first argument
* @return double value if argument i is number, otherwise 0
*/
public double todouble(int i) { return arg(i).todouble(); }
/**
* 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, 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, 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
*/
public long tolong(int i) { return arg(i).tolong(); }
/**
* Return argument i as a java String based on the type of the argument.
*
* @param i the index of the argument to convert, 1 is the first argument
* @return String value representing the type
*/
public String tojstring(int i) { return arg(i).tojstring(); }
/**
* 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
*/
public short toshort(int i) { return arg(i).toshort(); }
/**
* Return argument i as a java Object if a userdata, or null.
*
* @param i the index of the argument to convert, 1 is the first argument
* @return java Object value if argument i is a userdata, otherwise null
*/
public Object touserdata(int i) { return arg(i).touserdata(); }
/**
* Return argument i as a java Object if it is a userdata whose instance
* Class c or a subclass, or null.
*
* @param i the index of the argument to convert, 1 is the first argument
* @param c the class to which the userdata instance must be assignable
* @return java Object value if argument i is a userdata whose instance
* Class c or a subclass, otherwise null
*/
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}.
*/
public String tojstring() {
Buffer sb = new Buffer();
sb.append("(");
for (int i = 1, n = narg(); i <= n; i++) {
if (i > 1)
sb.append(",");
sb.append(arg(i).tojstring());
}
sb.append(")");
return sb.tojstring();
}
/**
* Convert the value or values to a java String using Varargs.tojstring()
*
* @return String value in human readable form.
* @see Varargs#tojstring()
*/
@Override
public String toString() { return tojstring(); }
/**
* Create a {@code Varargs} instance containing arguments starting at index
* {@code start}
*
* @param start the index from which to include arguments, where 1 is the
* first argument.
* @return Varargs containing argument { start, start+1, ... , narg-start-1
* }
*/
abstract public Varargs subargs(final int start);
/**
* Implementation of Varargs for use in the Varargs.subargs() function.
*
* @see Varargs#subargs(int)
*/
static class SubVarargs extends Varargs {
private final Varargs v;
private final int start;
private final int end;
public SubVarargs(Varargs varargs, int start, int end) {
this.v = varargs;
this.start = start;
this.end = end;
}
@Override
public LuaValue arg(int i) {
i += start-1;
return i >= start && i <= end? v.arg(i): LuaValue.NIL;
}
@Override
public LuaValue arg1() {
return v.arg(start);
}
@Override
public int narg() {
return end+1-start;
}
@Override
public Varargs subargs(final int start) {
if (start == 1)
return this;
final int newstart = this.start+start-1;
if (start > 0) {
if (newstart >= this.end)
return LuaValue.NONE;
if (newstart == this.end)
return v.arg(this.end);
if (newstart == this.end-1)
return new Varargs.PairVarargs(v.arg(this.end-1), v.arg(this.end));
return new SubVarargs(v, newstart, this.end);
}
return new SubVarargs(v, newstart, this.end);
}
}
/**
* Varargs implemenation backed by two values.
* <p>
* This is an internal class not intended to be used directly. 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.
* <p>
* This is an internal class not intended to be used directly. Instead
* use the corresponding static method on LuaValue.
*
* @see LuaValue#varargsOf(LuaValue, Varargs)
*/
PairVarargs(LuaValue v1, Varargs v2) {
this.v1 = v1;
this.v2 = v2;
}
@Override
public LuaValue arg(int i) {
return i == 1? v1: v2.arg(i-1);
}
@Override
public int narg() {
return 1+v2.narg();
}
@Override
public LuaValue arg1() {
return v1;
}
@Override
public Varargs subargs(final int start) {
if (start == 1)
return this;
if (start == 2)
return v2;
if (start > 2)
return v2.subargs(start-1);
return LuaValue.argerror(1, "start must be > 0");
}
}
/**
* Varargs implemenation backed by an array of LuaValues
* <p>
* This is an internal class not intended to be used directly. 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.
* <p>
* This is an internal class not intended to be used directly. Instead
* use the corresponding static methods on LuaValue.
*
* @see LuaValue#varargsOf(LuaValue[])
* @see LuaValue#varargsOf(LuaValue[], Varargs)
*/
ArrayVarargs(LuaValue[] v, Varargs r) {
this.v = v;
this.r = r;
}
@Override
public LuaValue arg(int i) {
return i < 1? LuaValue.NIL: i <= v.length? v[i-1]: r.arg(i-v.length);
}
@Override
public int narg() {
return v.length+r.narg();
}
@Override
public LuaValue arg1() { return v.length > 0? v[0]: r.arg1(); }
@Override
public Varargs subargs(int start) {
if (start <= 0)
LuaValue.argerror(1, "start must be > 0");
if (start == 1)
return this;
if (start > v.length)
return r.subargs(start-v.length);
return LuaValue.varargsOf(v, start-1, v.length-(start-1), r);
}
@Override
void copyto(LuaValue[] dest, int offset, int length) {
int n = Math.min(v.length, length);
System.arraycopy(v, 0, dest, offset, n);
r.copyto(dest, offset+n, length-n);
}
}
/**
* Varargs implemenation backed by an array of LuaValues
* <p>
* This is an internal class not intended to be used directly. Instead use
* the corresponding static methods on LuaValue.
*
* @see LuaValue#varargsOf(LuaValue[], int, int)
* @see LuaValue#varargsOf(LuaValue[], int, int, Varargs)
*/
static final class ArrayPartVarargs extends Varargs {
private final int offset;
private final LuaValue[] v;
private final int length;
private final Varargs more;
/**
* Construct a Varargs from an array of LuaValue.
* <p>
* This is an internal class not intended to be used directly. Instead
* use the corresponding static methods on LuaValue.
*
* @see LuaValue#varargsOf(LuaValue[], int, int)
*/
ArrayPartVarargs(LuaValue[] v, int offset, int length) {
this.v = v;
this.offset = offset;
this.length = length;
this.more = LuaValue.NONE;
}
/**
* Construct a Varargs from an array of LuaValue and additional
* arguments.
* <p>
* This is an internal class not intended to be used directly. 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) {
this.v = v;
this.offset = offset;
this.length = length;
this.more = more;
}
@Override
public LuaValue arg(final int i) {
return i < 1? LuaValue.NIL: i <= length? v[offset+i-1]: more.arg(i-length);
}
@Override
public int narg() {
return length+more.narg();
}
@Override
public LuaValue arg1() {
return length > 0? v[offset]: more.arg1();
}
@Override
public Varargs subargs(int start) {
if (start <= 0)
LuaValue.argerror(1, "start must be > 0");
if (start == 1)
return this;
if (start > length)
return more.subargs(start-length);
return LuaValue.varargsOf(v, offset+start-1, length-(start-1), more);
}
@Override
void copyto(LuaValue[] dest, int offset, int length) {
int n = Math.min(this.length, length);
System.arraycopy(this.v, this.offset, dest, offset, n);
more.copyto(dest, offset+n, length-n);
}
}
/**
* Copy values in a varargs into a destination array. Internal utility
* method not intended to be called directly from user code.
*
* @return Varargs containing same values, but flattened.
*/
void copyto(LuaValue[] dest, int offset, int length) {
for (int i = 0; i < length; ++i)
dest[offset+i] = arg(i+1);
}
/**
* 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
*/
public Varargs dealias() {
int n = narg();
switch (n) {
case 0:
return LuaValue.NONE;
case 1:
return arg1();
case 2:
return new PairVarargs(arg1(), arg(2));
default:
LuaValue[] v = new LuaValue[n];
copyto(v, 0, n);
return new ArrayVarargs(v, LuaValue.NONE);
}
}
}

View File

@@ -0,0 +1,456 @@
/*******************************************************************************
* Copyright (c) 2009-2011, 2013 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
import java.lang.ref.WeakReference;
import org.luaj.vm2.LuaTable.Slot;
import org.luaj.vm2.LuaTable.StrongSlot;
/**
* Subclass of {@link LuaTable} that provides weak key and weak value semantics.
* <p>
* Normally these are not created directly, but indirectly when changing the
* mode of a {@link LuaTable} as lua script executes.
* <p>
* However, calling the constructors directly when weak tables are required from
* Java will reduce overhead.
*/
public class WeakTable implements Metatable {
private final boolean weakkeys, weakvalues;
private final LuaValue backing;
public static LuaTable make(boolean weakkeys, boolean weakvalues) {
LuaString mode;
if (weakkeys && weakvalues) {
mode = LuaString.valueOf("kv");
} else if (weakkeys) {
mode = LuaString.valueOf("k");
} else if (weakvalues) {
mode = LuaString.valueOf("v");
} else {
return LuaValue.tableOf();
}
LuaTable table = LuaValue.tableOf();
LuaTable mt = LuaValue.tableOf(new LuaValue[] { LuaValue.MODE, mode });
table.setmetatable(mt);
return table;
}
/**
* Construct a table with weak keys, weak values, or both
*
* @param weakkeys true to let the table have weak keys
* @param weakvalues true to let the table have weak values
*/
public WeakTable(boolean weakkeys, boolean weakvalues, LuaValue backing) {
this.weakkeys = weakkeys;
this.weakvalues = weakvalues;
this.backing = backing;
}
@Override
public boolean useWeakKeys() {
return weakkeys;
}
@Override
public boolean useWeakValues() {
return weakvalues;
}
@Override
public LuaValue toLuaValue() {
return backing;
}
@Override
public Slot entry(LuaValue key, LuaValue value) {
value = value.strongvalue();
if (value == null)
return null;
if (weakkeys && !(key.isnumber() || key.isstring() || key.isboolean())) {
if (weakvalues && !(value.isnumber() || value.isstring() || value.isboolean())) {
return new WeakKeyAndValueSlot(key, value, null);
} else {
return new WeakKeySlot(key, value, null);
}
}
if (weakvalues && !(value.isnumber() || value.isstring() || value.isboolean())) {
return new WeakValueSlot(key, value, null);
}
return LuaTable.defaultEntry(key, value);
}
public static abstract class WeakSlot implements Slot {
protected Object key;
protected Object value;
protected Slot next;
protected WeakSlot(Object key, Object value, Slot next) {
this.key = key;
this.value = value;
this.next = next;
}
@Override
public abstract int keyindex(int hashMask);
public abstract Slot set(LuaValue value);
@Override
public StrongSlot first() {
LuaValue key = strongkey();
LuaValue value = strongvalue();
if (key != null && value != null) {
return new LuaTable.NormalEntry(key, value);
} else {
this.key = null;
this.value = null;
return null;
}
}
@Override
public StrongSlot find(LuaValue key) {
StrongSlot first = first();
return first != null? first.find(key): null;
}
@Override
public boolean keyeq(LuaValue key) {
StrongSlot first = first();
return first != null && first.keyeq(key);
}
@Override
public Slot rest() {
return next;
}
@Override
public int arraykey(int max) {
// Integer keys can never be weak.
return 0;
}
@Override
public Slot set(StrongSlot target, LuaValue value) {
LuaValue key = strongkey();
if (key != null && target.find(key) != null) {
return set(value);
} else if (key != null) {
// Our key is still good.
next = next.set(target, value);
return this;
} else {
// our key was dropped, remove ourselves from the chain.
return next.set(target, value);
}
}
@Override
public Slot add(Slot entry) {
next = next != null? next.add(entry): entry;
if (strongkey() != null && strongvalue() != null) {
return this;
} else {
return next;
}
}
@Override
public Slot remove(StrongSlot target) {
LuaValue key = strongkey();
if (key == null) {
return next.remove(target);
} else if (target.keyeq(key)) {
this.value = null;
return this;
} else {
next = next.remove(target);
return this;
}
}
@Override
public Slot relink(Slot rest) {
if (strongkey() != null && strongvalue() != null) {
if (rest == null && this.next == null) {
return this;
} else {
return copy(rest);
}
} else {
return rest;
}
}
public LuaValue strongkey() {
return (LuaValue) key;
}
public LuaValue strongvalue() {
return (LuaValue) value;
}
protected abstract WeakSlot copy(Slot next);
}
static class WeakKeySlot extends WeakSlot {
private final int keyhash;
protected WeakKeySlot(LuaValue key, LuaValue value, Slot next) {
super(weaken(key), value, next);
keyhash = key.hashCode();
}
protected WeakKeySlot(WeakKeySlot copyFrom, Slot next) {
super(copyFrom.key, copyFrom.value, next);
this.keyhash = copyFrom.keyhash;
}
@Override
public int keyindex(int mask) {
return LuaTable.hashmod(keyhash, mask);
}
@Override
public Slot set(LuaValue value) {
this.value = value;
return this;
}
@Override
public LuaValue strongkey() {
return strengthen(key);
}
@Override
protected WeakSlot copy(Slot rest) {
return new WeakKeySlot(this, rest);
}
}
static class WeakValueSlot extends WeakSlot {
protected WeakValueSlot(LuaValue key, LuaValue value, Slot next) {
super(key, weaken(value), next);
}
protected WeakValueSlot(WeakValueSlot copyFrom, Slot next) {
super(copyFrom.key, copyFrom.value, next);
}
@Override
public int keyindex(int mask) {
return LuaTable.hashSlot(strongkey(), mask);
}
@Override
public Slot set(LuaValue value) {
this.value = weaken(value);
return this;
}
@Override
public LuaValue strongvalue() {
return strengthen(value);
}
@Override
protected WeakSlot copy(Slot next) {
return new WeakValueSlot(this, next);
}
}
static class WeakKeyAndValueSlot extends WeakSlot {
private final int keyhash;
protected WeakKeyAndValueSlot(LuaValue key, LuaValue value, Slot next) {
super(weaken(key), weaken(value), next);
keyhash = key.hashCode();
}
protected WeakKeyAndValueSlot(WeakKeyAndValueSlot copyFrom, Slot next) {
super(copyFrom.key, copyFrom.value, next);
keyhash = copyFrom.keyhash;
}
@Override
public int keyindex(int hashMask) {
return LuaTable.hashmod(keyhash, hashMask);
}
@Override
public Slot set(LuaValue value) {
this.value = weaken(value);
return this;
}
@Override
public LuaValue strongkey() {
return strengthen(key);
}
@Override
public LuaValue strongvalue() {
return strengthen(value);
}
@Override
protected WeakSlot copy(Slot next) {
return new WeakKeyAndValueSlot(this, next);
}
}
/**
* Self-sent message to convert a value to its weak counterpart
*
* @param value value to convert
* @return {@link LuaValue} that is a strong or weak reference, depending on
* type of {@code value}
*/
protected static LuaValue weaken(LuaValue value) {
switch (value.type()) {
case LuaValue.TFUNCTION:
case LuaValue.TTHREAD:
case LuaValue.TTABLE:
return new WeakValue(value);
case LuaValue.TUSERDATA:
return new WeakUserdata(value);
default:
return value;
}
}
/**
* Unwrap a LuaValue from a WeakReference and/or WeakUserdata.
*
* @param ref reference to convert
* @return LuaValue or null
* @see #weaken(LuaValue)
*/
protected static LuaValue strengthen(Object ref) {
if (ref instanceof WeakReference) {
ref = ((WeakReference) ref).get();
}
if (ref instanceof WeakValue) {
return ((WeakValue) ref).strongvalue();
}
return (LuaValue) ref;
}
/**
* Internal class to implement weak values.
*
* @see WeakTable
*/
static class WeakValue extends LuaValue {
WeakReference ref;
protected WeakValue(LuaValue value) {
ref = new WeakReference(value);
}
@Override
public int type() {
illegal("type", "weak value");
return 0;
}
@Override
public String typename() {
illegal("typename", "weak value");
return null;
}
@Override
public String toString() {
return "weak<" + ref.get() + ">";
}
@Override
public LuaValue strongvalue() {
Object o = ref.get();
return (LuaValue) o;
}
@Override
public boolean raweq(LuaValue rhs) {
Object o = ref.get();
return o != null && rhs.raweq((LuaValue) o);
}
}
/**
* Internal class to implement weak userdata values.
*
* @see WeakTable
*/
static final class WeakUserdata extends WeakValue {
private final WeakReference ob;
private final LuaValue mt;
private WeakUserdata(LuaValue value) {
super(value);
ob = new WeakReference(value.touserdata());
mt = value.getmetatable();
}
@Override
public LuaValue strongvalue() {
Object u = ref.get();
if (u != null)
return (LuaValue) u;
Object o = ob.get();
if (o != null) {
LuaValue ud = LuaValue.userdataOf(o, mt);
ref = new WeakReference(ud);
return ud;
} else {
return null;
}
}
}
@Override
public LuaValue wrap(LuaValue value) {
return weakvalues? weaken(value): value;
}
@Override
public LuaValue arrayget(LuaValue[] array, int index) {
LuaValue value = array[index];
if (value != null) {
value = strengthen(value);
if (value == null) {
array[index] = null;
}
}
return value;
}
}

View File

@@ -0,0 +1,19 @@
package org.luaj.vm2.compat;
public class JavaCompat {
public static final JavaCompat INSTANCE;
static {
JavaCompat instance;
try {
instance = (JavaCompat) Class.forName("org.luaj.vm2.lib.jse.JavaCompatJSE").newInstance();
} catch (Throwable t) {
instance = new JavaCompat();
}
INSTANCE = instance;
}
public long doubleToRawLongBits(double x) {
return Double.doubleToLongBits(x);
}
}

View File

@@ -0,0 +1,178 @@
/*******************************************************************************
* Copyright (c) 2015 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.compiler;
import org.luaj.vm2.LocVars;
import org.luaj.vm2.Lua;
import org.luaj.vm2.LuaError;
import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Prototype;
import org.luaj.vm2.Upvaldesc;
/**
* Constants used by the LuaC compiler and related classes.
*
* @see LuaC
* @see FuncState
*/
public class Constants extends Lua {
/** Maximum stack size of a luaj vm interpreter instance. */
public static final int MAXSTACK = 250;
static final int LUAI_MAXUPVAL = 0xff;
static final int LUAI_MAXVARS = 200;
static final int NO_REG = MAXARG_A;
/* OpMode - basic instruction format */
static final int iABC = 0, iABx = 1, iAsBx = 2;
/* OpArgMask */
static final int OpArgN = 0, /* argument is not used */
OpArgU = 1, /* argument is used */
OpArgR = 2, /* argument is a register or a jump offset */
OpArgK = 3; /* argument is a constant or register/constant */
protected static void _assert(boolean b) {
if (!b)
throw new LuaError("compiler assert failed");
}
static void SET_OPCODE(InstructionPtr i, int o) {
i.set(i.get() & MASK_NOT_OP | o<<POS_OP & MASK_OP);
}
static void SETARG_A(int[] code, int index, int u) {
code[index] = code[index] & MASK_NOT_A | u<<POS_A & MASK_A;
}
static void SETARG_A(InstructionPtr i, int u) {
i.set(i.get() & MASK_NOT_A | u<<POS_A & MASK_A);
}
static void SETARG_B(InstructionPtr i, int u) {
i.set(i.get() & MASK_NOT_B | u<<POS_B & MASK_B);
}
static void SETARG_C(InstructionPtr i, int u) {
i.set(i.get() & MASK_NOT_C | u<<POS_C & MASK_C);
}
static void SETARG_Bx(InstructionPtr i, int u) {
i.set(i.get() & MASK_NOT_Bx | u<<POS_Bx & MASK_Bx);
}
static void SETARG_sBx(InstructionPtr i, int u) {
SETARG_Bx(i, u+MAXARG_sBx);
}
static int CREATE_ABC(int o, int a, int b, int c) {
return o<<POS_OP & MASK_OP | a<<POS_A & MASK_A | b<<POS_B & MASK_B | c<<POS_C & MASK_C;
}
static int CREATE_ABx(int o, int a, int bc) {
return o<<POS_OP & MASK_OP | a<<POS_A & MASK_A | bc<<POS_Bx & MASK_Bx;
}
static int CREATE_Ax(int o, int a) {
return o<<POS_OP & MASK_OP | a<<POS_Ax & MASK_Ax;
}
// vector reallocation
static LuaValue[] realloc(LuaValue[] v, int n) {
LuaValue[] a = new LuaValue[n];
if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a;
}
static Prototype[] realloc(Prototype[] v, int n) {
Prototype[] a = new Prototype[n];
if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a;
}
static LuaString[] realloc(LuaString[] v, int n) {
LuaString[] a = new LuaString[n];
if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a;
}
static LocVars[] realloc(LocVars[] v, int n) {
LocVars[] a = new LocVars[n];
if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a;
}
static Upvaldesc[] realloc(Upvaldesc[] v, int n) {
Upvaldesc[] a = new Upvaldesc[n];
if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a;
}
static LexState.Vardesc[] realloc(LexState.Vardesc[] v, int n) {
LexState.Vardesc[] a = new LexState.Vardesc[n];
if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a;
}
static LexState.Labeldesc[] grow(LexState.Labeldesc[] v, int min_n) {
return v == null? new LexState.Labeldesc[2]: v.length < min_n? realloc(v, v.length*2): v;
}
static LexState.Labeldesc[] realloc(LexState.Labeldesc[] v, int n) {
LexState.Labeldesc[] a = new LexState.Labeldesc[n];
if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a;
}
static int[] realloc(int[] v, int n) {
int[] a = new int[n];
if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a;
}
static byte[] realloc(byte[] v, int n) {
byte[] a = new byte[n];
if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a;
}
static char[] realloc(char[] v, int n) {
char[] a = new char[n];
if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a;
}
protected Constants() {}
}

View File

@@ -0,0 +1,320 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.compiler;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LoadState;
import org.luaj.vm2.LocVars;
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.
* <p>
* Generally, this class is not used directly, but rather indirectly via a
* command line interface tool such as {@link luac}.
* <p>
* A lua binary file is created via {@link DumpState#dump}:
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* Prototype p = globals.compilePrototype(new StringReader("print('hello, world')"), "main.lua");
* ByteArrayOutputStream o = new ByteArrayOutputStream();
* DumpState.dump(p, o, false);
* byte[] lua_binary_file_bytes = o.toByteArray();
* }
* </pre>
*
* The {@link LoadState} may be used directly to undump these bytes:
*
* <pre>
* {@code
* Prototypep = LoadState.instance.undump(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua");
* LuaClosure c = new LuaClosure(p, globals);
* c.call();
* }
* </pre>
*
*
* More commonly, the {@link Globals#undumper} may be used to undump them:
*
* <pre>
* {
* &#64;code
* Prototype p = globals.loadPrototype(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua", "b");
* LuaClosure c = new LuaClosure(p, globals);
* c.call();
* }
* </pre>
*
* @see luac
* @see LoadState
* @see Globals
* @see Prototype
*/
public class DumpState {
/** set true to allow integer compilation */
public static boolean ALLOW_INTEGER_CASTING = false;
/**
* format corresponding to non-number-patched lua, all numbers are floats or
* doubles
*/
public static final int NUMBER_FORMAT_FLOATS_OR_DOUBLES = 0;
/** format corresponding to non-number-patched lua, all numbers are ints */
public static final int NUMBER_FORMAT_INTS_ONLY = 1;
/**
* format corresponding to number-patched lua, all numbers are 32-bit (4
* byte) ints
*/
public static final int NUMBER_FORMAT_NUM_PATCH_INT32 = 4;
/** default number format */
public static final int NUMBER_FORMAT_DEFAULT = NUMBER_FORMAT_FLOATS_OR_DOUBLES;
// header fields
private boolean IS_LITTLE_ENDIAN = true;
private int NUMBER_FORMAT = NUMBER_FORMAT_DEFAULT;
private int SIZEOF_LUA_NUMBER = 8;
private static final int SIZEOF_INT = 4;
private static final int SIZEOF_SIZET = 4;
private static final int SIZEOF_INSTRUCTION = 4;
DataOutputStream writer;
boolean strip;
int status;
public DumpState(OutputStream w, boolean strip) {
this.writer = new DataOutputStream(w);
this.strip = strip;
this.status = 0;
}
void dumpBlock(final byte[] b, int size) throws IOException {
writer.write(b, 0, size);
}
void dumpChar(int b) throws IOException {
writer.write(b);
}
void dumpInt(int x) throws IOException {
if (IS_LITTLE_ENDIAN) {
writer.writeByte(x & 0xff);
writer.writeByte(x>>8 & 0xff);
writer.writeByte(x>>16 & 0xff);
writer.writeByte(x>>24 & 0xff);
} else {
writer.writeInt(x);
}
}
void dumpString(LuaString s) throws IOException {
final int len = s.len().toint();
dumpInt(len+1);
s.write(writer, 0, len);
writer.write(0);
}
void dumpDouble(double d) throws IOException {
long l = Double.doubleToLongBits(d);
if (IS_LITTLE_ENDIAN) {
dumpInt((int) l);
dumpInt((int) (l>>32));
} else {
writer.writeLong(l);
}
}
void dumpCode(final Prototype f) throws IOException {
final int[] code = f.code;
int n = code.length;
dumpInt(n);
for (int i = 0; i < n; i++)
dumpInt(code[i]);
}
void dumpConstants(final Prototype f) throws IOException {
final LuaValue[] k = f.k;
int i, n = k.length;
dumpInt(n);
for (i = 0; i < n; i++) {
final LuaValue o = k[i];
switch (o.type()) {
case LuaValue.TNIL:
writer.write(LuaValue.TNIL);
break;
case LuaValue.TBOOLEAN:
writer.write(LuaValue.TBOOLEAN);
dumpChar(o.toboolean()? 1: 0);
break;
case LuaValue.TNUMBER:
switch (NUMBER_FORMAT) {
case NUMBER_FORMAT_FLOATS_OR_DOUBLES:
writer.write(LuaValue.TNUMBER);
dumpDouble(o.todouble());
break;
case NUMBER_FORMAT_INTS_ONLY:
if (!ALLOW_INTEGER_CASTING && !o.isint())
throw new java.lang.IllegalArgumentException("not an integer: " + o);
writer.write(LuaValue.TNUMBER);
dumpInt(o.toint());
break;
case NUMBER_FORMAT_NUM_PATCH_INT32:
if (o.isint()) {
writer.write(LuaValue.TINT);
dumpInt(o.toint());
} else {
writer.write(LuaValue.TNUMBER);
dumpDouble(o.todouble());
}
break;
default:
throw new IllegalArgumentException("number format not supported: " + NUMBER_FORMAT);
}
break;
case LuaValue.TSTRING:
writer.write(LuaValue.TSTRING);
dumpString((LuaString) o);
break;
default:
throw new IllegalArgumentException("bad type for " + o);
}
}
n = f.p.length;
dumpInt(n);
for (i = 0; i < n; i++)
dumpFunction(f.p[i]);
}
void dumpUpvalues(final Prototype f) throws IOException {
int n = f.upvalues.length;
dumpInt(n);
for (int i = 0; i < n; i++) {
writer.writeByte(f.upvalues[i].instack? 1: 0);
writer.writeByte(f.upvalues[i].idx);
}
}
void dumpDebug(final Prototype f) throws IOException {
int i, n;
if (strip)
dumpInt(0);
else
dumpString(f.source);
n = strip? 0: f.lineinfo.length;
dumpInt(n);
for (i = 0; i < n; i++)
dumpInt(f.lineinfo[i]);
n = strip? 0: f.locvars.length;
dumpInt(n);
for (i = 0; i < n; i++) {
LocVars lvi = f.locvars[i];
dumpString(lvi.varname);
dumpInt(lvi.startpc);
dumpInt(lvi.endpc);
}
n = strip? 0: f.upvalues.length;
dumpInt(n);
for (i = 0; i < n; i++)
dumpString(f.upvalues[i].name);
}
void dumpFunction(final Prototype f) throws IOException {
dumpInt(f.linedefined);
dumpInt(f.lastlinedefined);
dumpChar(f.numparams);
dumpChar(f.is_vararg);
dumpChar(f.maxstacksize);
dumpCode(f);
dumpConstants(f);
dumpUpvalues(f);
dumpDebug(f);
}
void dumpHeader() throws IOException {
writer.write(LoadState.LUA_SIGNATURE);
writer.write(LoadState.LUAC_VERSION);
writer.write(LoadState.LUAC_FORMAT);
writer.write(IS_LITTLE_ENDIAN? 1: 0);
writer.write(SIZEOF_INT);
writer.write(SIZEOF_SIZET);
writer.write(SIZEOF_INSTRUCTION);
writer.write(SIZEOF_LUA_NUMBER);
writer.write(NUMBER_FORMAT);
writer.write(LoadState.LUAC_TAIL);
}
/*
** dump Lua function as precompiled chunk
*/
public static int dump(Prototype f, OutputStream w, boolean strip) throws IOException {
DumpState D = new DumpState(w, strip);
D.dumpHeader();
D.dumpFunction(f);
return D.status;
}
/**
*
* @param f the function to dump
* @param w the output stream to dump to
* @param stripDebug true to strip debugging info, false otherwise
* @param numberFormat one of NUMBER_FORMAT_FLOATS_OR_DOUBLES,
* NUMBER_FORMAT_INTS_ONLY,
* NUMBER_FORMAT_NUM_PATCH_INT32
* @param littleendian true to use little endian for numbers, false for big
* endian
* @return 0 if dump succeeds
* @throws IOException
* @throws IllegalArgumentException if the number format it not supported
*/
public static int dump(Prototype f, OutputStream w, boolean stripDebug, int numberFormat, boolean littleendian)
throws IOException {
switch (numberFormat) {
case NUMBER_FORMAT_FLOATS_OR_DOUBLES:
case NUMBER_FORMAT_INTS_ONLY:
case NUMBER_FORMAT_NUM_PATCH_INT32:
break;
default:
throw new IllegalArgumentException("number format not supported: " + numberFormat);
}
DumpState D = new DumpState(w, stripDebug);
D.IS_LITTLE_ENDIAN = littleendian;
D.NUMBER_FORMAT = numberFormat;
D.SIZEOF_LUA_NUMBER = numberFormat == NUMBER_FORMAT_INTS_ONLY? 4: 8;
D.dumpHeader();
D.dumpFunction(f);
return D.status;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,40 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.compiler;
class InstructionPtr {
final int[] code;
final int idx;
InstructionPtr(int[] code, int idx) {
this.code = code;
this.idx = idx;
}
int get() {
return code[idx];
}
void set(int value) {
code[idx] = value;
}
}

View File

@@ -0,0 +1,33 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.compiler;
public class IntPtr {
int i;
IntPtr() {
}
IntPtr(int value) {
this.i = value;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,175 @@
/*******************************************************************************
* Copyright (c) 2009-2011 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.compiler;
import java.io.IOException;
import java.io.InputStream;
import java.util.Hashtable;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaClosure;
import org.luaj.vm2.LuaFunction;
import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Prototype;
import org.luaj.vm2.lib.BaseLib;
/**
* Compiler for Lua.
*
* <p>
* Compiles lua source files into lua bytecode within a {@link Prototype}, loads
* lua binary files directly into a {@link Prototype}, and optionaly
* instantiates a {@link LuaClosure} around the result using a user-supplied
* environment.
*
* <p>
* Implements the {@link org.luaj.vm2.Globals.Compiler} interface for loading
* initialized chunks, which is an interface common to lua bytecode compiling
* and java bytecode compiling.
*
* <p>
* The {@link LuaC} compiler is installed by default by both the
* {@link org.luaj.vm2.lib.jse.JsePlatform} and
* {@link org.luaj.vm2.lib.jme.JmePlatform} classes, so in the following
* example, the default {@link LuaC} compiler will be used:
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* globals.load(new StringReader("print 'hello'"), "main.lua").call();
* }
* </pre>
*
* To load the LuaC compiler manually, use the install method:
*
* <pre>
* {@code
* LuaC.install(globals);
* }
* </pre>
*
* @see #install(Globals)
* @see Globals#compiler
* @see Globals#loader
* @see org.luaj.vm2.luajc.LuaJC
* @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform
* @see BaseLib
* @see LuaValue
* @see Prototype
*/
public class LuaC extends Constants implements Globals.Compiler, Globals.Loader {
/** A sharable instance of the LuaC compiler. */
public static final LuaC instance = new LuaC();
/**
* Install the compiler so that LoadState will first try to use it when
* handed bytes that are not already a compiled lua chunk.
*
* @param globals the Globals into which this is to be installed.
*/
public static void install(Globals globals) {
globals.compiler = instance;
globals.loader = instance;
}
protected LuaC() {}
/**
* Compile lua source into a Prototype.
*
* @param stream InputStream representing the text source conforming to
* lua source syntax.
* @param chunkname String name of the chunk to use.
* @return Prototype representing the lua chunk for this source.
* @throws IOException
*/
@Override
public Prototype compile(InputStream stream, String chunkname) throws IOException {
return new CompileState().luaY_parser(stream, chunkname);
}
@Override
public LuaFunction load(Prototype prototype, String chunkname, LuaValue env) throws IOException {
return new LuaClosure(prototype, env);
}
/**
* @deprecated Use Globals.load(InputString, String, String) instead, or
* LuaC.compile(InputStream, String) and construct LuaClosure
* directly.
*/
@Deprecated
public LuaValue load(InputStream stream, String chunkname, Globals globals) throws IOException {
return new LuaClosure(compile(stream, chunkname), globals);
}
static class CompileState {
int nCcalls = 0;
private final Hashtable strings = new Hashtable();
protected CompileState() {}
/** Parse the input */
Prototype luaY_parser(InputStream z, String name) throws IOException {
LexState lexstate = new LexState(this, z);
FuncState funcstate = new FuncState();
// lexstate.buff = buff;
lexstate.fs = funcstate;
lexstate.setinput(this, z.read(), z, LuaValue.valueOf(name));
/* main func. is always vararg */
funcstate.f = new Prototype();
funcstate.f.source = LuaValue.valueOf(name);
lexstate.mainfunc(funcstate);
Constants._assert(funcstate.prev == null);
/* all scopes should be correctly finished */
Constants._assert(lexstate.dyd == null
|| lexstate.dyd.n_actvar == 0 && lexstate.dyd.n_gt == 0 && lexstate.dyd.n_label == 0);
return funcstate.f;
}
// look up and keep at most one copy of each string
public LuaString newTString(String s) {
return cachedLuaString(LuaString.valueOf(s));
}
// look up and keep at most one copy of each string
public LuaString newTString(LuaString s) {
return cachedLuaString(s);
}
public LuaString cachedLuaString(LuaString s) {
LuaString c = (LuaString) strings.get(s);
if (c != null)
return c;
strings.put(s, s);
return s;
}
public String pushfstring(String string) {
return string;
}
}
}

View File

@@ -0,0 +1,513 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.lib;
import java.io.IOException;
import java.io.InputStream;
import org.luaj.vm2.Globals;
import org.luaj.vm2.Lua;
import org.luaj.vm2.LuaError;
import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaThread;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LibFunction} which implements the lua basic library
* functions.
* <p>
* This contains all library functions listed as "basic functions" in the lua
* documentation for JME. The functions dofile and loadfile use the
* {@link Globals#finder} instance to find resource files. Since JME has no file
* system by default, {@link BaseLib} implements {@link ResourceFinder} using
* {@link Class#getResource(String)}, which is the closest equivalent on JME.
* The default loader chain in {@link PackageLib} will use these as well.
* <p>
* To use basic library functions that include a {@link ResourceFinder} based on
* directory lookup, use {@link org.luaj.vm2.lib.jse.JseBaseLib} instead.
* <p>
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* globals.get("print").call(LuaValue.valueOf("hello, world"));
* }
* </pre>
* <p>
* 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:
*
* <pre>
* {
* &#64;code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.get("print").call(LuaValue.valueOf("hello, world"));
* }
* </pre>
*
* Doing so will ensure the library is properly initialized and loaded into the
* globals table.
* <p>
* This is a direct port of the corresponding library in C.
*
* @see org.luaj.vm2.lib.jse.JseBaseLib
* @see ResourceFinder
* @see Globals#finder
* @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.1">Lua 5.2 Base Lib
* Reference</a>
*/
public class BaseLib extends TwoArgFunction implements ResourceFinder {
Globals globals;
/**
* Perform one-time initialization on the library by adding base functions
* to the supplied environment, and returning it as the return value.
*
* @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, which must be a Globals
* instance.
*/
@Override
public LuaValue call(LuaValue modname, LuaValue env) {
globals = env.checkglobals();
globals.finder = this;
globals.baselib = this;
env.set("_G", env);
env.set("_VERSION", Lua._VERSION);
env.set("assert", new _assert());
env.set("collectgarbage", new collectgarbage());
env.set("dofile", new dofile());
env.set("error", new error());
env.set("getmetatable", new getmetatable());
env.set("load", new load());
env.set("loadfile", new loadfile());
env.set("pcall", new pcall());
env.set("print", new print(this));
env.set("rawequal", new rawequal());
env.set("rawget", new rawget());
env.set("rawlen", new rawlen());
env.set("rawset", new rawset());
env.set("select", new select());
env.set("setmetatable", new setmetatable());
env.set("tonumber", new tonumber());
env.set("tostring", new tostring());
env.set("type", new type());
env.set("xpcall", new xpcall());
next next;
env.set("next", next = new next());
env.set("pairs", new pairsbase(PAIRS, NIL, next));
env.set("ipairs", new pairsbase(IPAIRS, ZERO, new inext()));
return env;
}
/** ResourceFinder implementation
*
* Tries to open the file as a resource, which can work for JSE and JME.
*/
public InputStream findResource(String filename) {
return getClass().getResourceAsStream(filename.startsWith("/")? filename: "/"+filename);
}
// "assert", // ( v [,message] ) -> v, message | ERR
static final class _assert extends VarArgFunction {
public Varargs invoke(Varargs args) {
if ( !args.arg1().toboolean() )
error( args.narg()>1? args.optjstring(2,"assertion failed!"): "assertion failed!" );
return args;
}
}
// "collectgarbage", // ( opt [,arg] ) -> value
static final class collectgarbage extends VarArgFunction {
public Varargs invoke(Varargs args) {
String s = args.optjstring(1, "collect");
if ( "collect".equals(s) ) {
System.gc();
return ZERO;
} else if ( "count".equals(s) ) {
Runtime rt = Runtime.getRuntime();
long used = rt.totalMemory() - rt.freeMemory();
return varargsOf(valueOf(used/1024.), valueOf(used%1024));
} else if ( "step".equals(s) ) {
System.gc();
return LuaValue.TRUE;
} else {
argerror(1, "invalid option '" + s + "'");
}
return NIL;
}
}
// "dofile", // ( filename ) -> result1, ...
final class dofile extends VarArgFunction {
public Varargs invoke(Varargs args) {
args.argcheck(args.isstring(1) || args.isnil(1), 1, "filename must be string or nil");
String filename = args.isstring(1)? args.tojstring(1): null;
Varargs v = filename == null?
loadStream( globals.STDIN, "=stdin", "bt", globals ):
loadFile( args.checkjstring(1), "bt", globals );
return v.isnil(1)? error(v.tojstring(2)): v.arg1().invoke();
}
}
// "error", // ( message [,level] ) -> ERR
static final class error extends TwoArgFunction {
public LuaValue call(LuaValue arg1, LuaValue arg2) {
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 expected");
}
public LuaValue call(LuaValue arg) {
LuaValue mt = arg.getmetatable();
return mt!=null? mt.rawget(METATABLE).optvalue(mt): NIL;
}
}
// "load", // ( ld [, source [, mode [, env]]] ) -> chunk | nil, msg
final class load extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaValue ld = args.arg1();
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);
return loadStream(ld.isstring()? ld.strvalue().toInputStream():
new StringInputStream(ld.checkfunction()), source, mode, env);
}
}
// "loadfile", // ( [filename [, mode [, env]]] ) -> chunk | nil, msg
final class loadfile extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) {
args.argcheck(args.isstring(1) || args.isnil(1), 1, "filename must be string or nil");
String filename = args.isstring(1)? args.tojstring(1): null;
String mode = args.optjstring(2, "bt");
LuaValue env = args.optvalue(3, globals);
return filename == null?
loadStream( globals.STDIN, "=stdin", mode, env ):
loadFile( filename, mode, env );
}
}
// "pcall", // (f, arg1, ...) -> status, result1, ...
final class pcall extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) {
LuaValue func = args.checkvalue(1);
if (globals != null && globals.debuglib != null)
globals.debuglib.onCall(this);
try {
return varargsOf(TRUE, func.invoke(args.subargs(2)));
} catch ( LuaError le ) {
final LuaValue m = le.getMessageObject();
return varargsOf(FALSE, m!=null? m: NIL);
} catch ( Exception e ) {
final String m = e.getMessage();
return varargsOf(FALSE, valueOf(m!=null? m: e.toString()));
} finally {
if (globals != null && globals.debuglib != null)
globals.debuglib.onReturn();
}
}
}
// "print", // (...) -> void
final class print extends VarArgFunction {
final BaseLib baselib;
print(BaseLib baselib) {
this.baselib = baselib;
}
@Override
public Varargs invoke(Varargs args) {
LuaValue tostring = globals.get("tostring");
for ( int i=1, n=args.narg(); i<=n; i++ ) {
if ( i>1 ) globals.STDOUT.print( '\t' );
LuaString s = tostring.call( args.arg(i) ).strvalue();
globals.STDOUT.print(s.tojstring());
}
globals.STDOUT.print('\n');
return NONE;
}
}
// "rawequal", // (v1, v2) -> boolean
static final class rawequal extends LibFunction {
public LuaValue call() {
return argerror(1, "value expected");
}
public LuaValue call(LuaValue arg) {
return argerror(2, "value expected");
}
public LuaValue call(LuaValue arg1, LuaValue arg2) {
return valueOf(arg1.raweq(arg2));
}
}
// "rawget", // (table, index) -> value
static final class rawget extends TableLibFunction {
public LuaValue call() {
return argerror(1, "value expected");
}
public LuaValue call(LuaValue arg) {
return argerror(2, "value expected");
}
public LuaValue call(LuaValue arg1, LuaValue arg2) {
return arg1.checktable().rawget(arg2);
}
}
// "rawlen", // (v) -> value
static final class rawlen extends LibFunction {
public LuaValue call(LuaValue arg) {
return valueOf(arg.rawlen());
}
}
// "rawset", // (table, index, value) -> table
static final class rawset extends TableLibFunction {
public LuaValue call(LuaValue table) {
return argerror(2,"value expected");
}
public LuaValue call(LuaValue table, LuaValue index) {
return argerror(3,"value expected");
}
public LuaValue call(LuaValue table, LuaValue index, LuaValue value) {
LuaTable t = table.checktable();
if (!index.isvalidkey()) argerror(2, "table index is nil");
t.rawset(index, value);
return t;
}
}
// "select", // (f, ...) -> value1, ...
static final class select extends VarArgFunction {
public Varargs invoke(Varargs args) {
int n = args.narg()-1;
if ( args.arg1().equals(valueOf("#")) )
return valueOf(n);
int i = args.checkint(1);
if ( i == 0 || i < -n )
argerror(1,"index out of range");
return args.subargs(i<0? n+i+2: i+1);
}
}
// "setmetatable", // (table, metatable) -> table
static final class setmetatable extends TableLibFunction {
public LuaValue call(LuaValue table) {
return argerror(2,"nil or table expected");
}
public LuaValue call(LuaValue table, LuaValue metatable) {
final LuaValue mt0 = table.checktable().getmetatable();
if ( mt0!=null && !mt0.rawget(METATABLE).isnil() )
error("cannot change a protected metatable");
return table.setmetatable(metatable.isnil()? null: metatable.checktable());
}
}
// "tonumber", // (e [,base]) -> value
static final class tonumber extends LibFunction {
public LuaValue call(LuaValue e) {
return e.tonumber();
}
public LuaValue call(LuaValue e, LuaValue base) {
if (base.isnil())
return e.tonumber();
final int b = base.checkint();
if (b < 2 || b > 36)
argerror(2, "base out of range");
return e.checkstring().tonumber(b);
}
}
// "tostring", // (e) -> value
static final class tostring extends LibFunction {
public LuaValue call(LuaValue arg) {
LuaValue h = arg.metatag(TOSTRING);
if ( ! h.isnil() ) {
LuaValue v = h.call(arg);
LuaValue vs = v.tostring();
return !vs.isnil() ? vs : v;
}
LuaValue v = arg.tostring();
if ( ! v.isnil() )
return v;
return valueOf(arg.tojstring());
}
}
// "type", // (v) -> value
static final class type extends LibFunction {
@Override
public LuaValue call(LuaValue arg) {
return valueOf(arg.typename());
}
}
// "xpcall", // (f, err) -> result1, ...
final class xpcall extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) {
final LuaThread t = globals.running;
final LuaValue preverror = t.errorfunc;
t.errorfunc = args.checkvalue(2);
try {
if (globals != null && globals.debuglib != null)
globals.debuglib.onCall(this);
try {
return varargsOf(TRUE, args.arg1().invoke(args.subargs(3)));
} catch ( LuaError le ) {
final LuaValue m = le.getMessageObject();
return varargsOf(FALSE, m!=null? m: NIL);
} catch ( Exception e ) {
final String m = e.getMessage();
return varargsOf(FALSE, valueOf(m!=null? m: e.toString()));
} finally {
if (globals != null && globals.debuglib != null)
globals.debuglib.onReturn();
}
} finally {
t.errorfunc = preverror;
}
}
}
// pairsbase, // (t) -> iter-func, t, initial
static final class pairsbase extends VarArgFunction {
final LuaString method;
final LuaValue initial;
final VarArgFunction iter;
pairsbase(LuaString method, LuaValue initial, VarArgFunction iter) {
this.method = method;
this.initial = initial;
this.iter = iter;
}
public Varargs invoke(Varargs args) {
LuaValue arg = args.arg1();
LuaValue t = arg.metatag(method);
if (!t.isnil())
// TODO: This can return more than 3 results.
return t.invoke(args.isvalue(1) ? arg : t);
return varargsOf(iter, args.checktable(1), initial);
}
}
// "next" ( table, [index] ) -> next-index, next-value
static final class next extends VarArgFunction {
public Varargs invoke(Varargs args) {
return args.checktable(1).next(args.arg(2));
}
}
// "inext" ( table, [int-index] ) -> next-index, next-value
static final class inext extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) {
return args.checktable(1).inext(args.arg(2));
}
}
/**
* Load from a named file, returning the chunk or nil,error of can't load
* @param env
* @param mode
* @return Varargs containing chunk, or NIL,error-text on error
*/
public Varargs loadFile(String filename, String mode, LuaValue env) {
InputStream is = globals.finder.findResource(filename);
if ( is == null )
return varargsOf(NIL, valueOf("cannot open "+filename+": No such file or directory"));
try {
return loadStream(is, "@"+filename, mode, env);
} finally {
try {
is.close();
} catch ( Exception e ) {
e.printStackTrace();
}
}
}
public Varargs loadStream(InputStream is, String chunkname, String mode, LuaValue env) {
try {
if ( is == null )
return varargsOf(NIL, valueOf("not found: "+chunkname));
return globals.load(is, chunkname, mode, env);
} catch (Exception e) {
return varargsOf(NIL, valueOf(e.getMessage()));
}
}
private static class StringInputStream extends InputStream {
final LuaValue func;
byte[] bytes;
int offset, remaining = 0;
StringInputStream(LuaValue func) {
this.func = func;
}
public int read() throws IOException {
if ( remaining < 0 )
return -1;
if ( remaining == 0 ) {
LuaValue s = func.call();
if ( s.isnil() )
return remaining = -1;
LuaString ls = s.strvalue();
bytes = ls.m_bytes;
offset = ls.m_offset;
remaining = ls.m_length;
if (remaining <= 0)
return -1;
}
--remaining;
return 0xFF & bytes[offset++];
}
}
}

View File

@@ -0,0 +1,250 @@
/*******************************************************************************
* Copyright (c) 2012 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.lib;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of LibFunction that implements the Lua standard {@code bit32}
* library.
* <p>
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println(globals.get("bit32").get("bnot").call(LuaValue.valueOf(2)));
* }
* </pre>
* <p>
* To instantiate and use it directly, link it into your globals table via
* {@link LuaValue#load(LuaValue)} using code such as:
*
* <pre>
* {
* &#64;code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* globals.load(new Bit32Lib());
* System.out.println(globals.get("bit32").get("bnot").call(LuaValue.valueOf(2)));
* }
* </pre>
* <p>
* This has been implemented to match as closely as possible the behavior in the
* corresponding library in C.
*
* @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.7">Lua 5.2 Bitwise
* Operation Lib Reference</a>
*/
public class Bit32Lib extends TwoArgFunction {
public Bit32Lib() {
}
/**
* Perform one-time initialization on the library by creating a table
* containing the library functions, adding that table to the supplied
* environment, adding the table to package.loaded, and returning table as
* the return value.
*
* @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, which must be a Globals
* instance.
*/
@Override
public LuaValue call(LuaValue modname, LuaValue env) {
LuaTable t = new LuaTable();
bind(t, Bit32LibV.class, new String[] { "band", "bnot", "bor", "btest", "bxor", "extract", "replace" });
bind(t, Bit32Lib2.class, new String[] { "arshift", "lrotate", "lshift", "rrotate", "rshift" });
env.set("bit32", t);
if (!env.get("package").isnil())
env.get("package").get("loaded").set("bit32", t);
return t;
}
static final class Bit32LibV extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) {
switch (opcode) {
case 0:
return Bit32Lib.band(args);
case 1:
return Bit32Lib.bnot(args);
case 2:
return Bit32Lib.bor(args);
case 3:
return Bit32Lib.btest(args);
case 4:
return Bit32Lib.bxor(args);
case 5:
return Bit32Lib.extract(args.checkint(1), args.checkint(2), args.optint(3, 1));
case 6:
return Bit32Lib.replace(args.checkint(1), args.checkint(2), args.checkint(3), args.optint(4, 1));
}
return NIL;
}
}
static final class Bit32Lib2 extends TwoArgFunction {
@Override
public LuaValue call(LuaValue arg1, LuaValue arg2) {
switch (opcode) {
case 0:
return Bit32Lib.arshift(arg1.checkint(), arg2.checkint());
case 1:
return Bit32Lib.lrotate(arg1.checkint(), arg2.checkint());
case 2:
return Bit32Lib.lshift(arg1.checkint(), arg2.checkint());
case 3:
return Bit32Lib.rrotate(arg1.checkint(), arg2.checkint());
case 4:
return Bit32Lib.rshift(arg1.checkint(), arg2.checkint());
}
return NIL;
}
}
static LuaValue arshift(int x, int disp) {
if (disp >= 0) {
return bitsToValue(x>>disp);
} else {
return bitsToValue(x<<-disp);
}
}
static LuaValue rshift(int x, int disp) {
if (disp >= 32 || disp <= -32) {
return ZERO;
} else if (disp >= 0) {
return bitsToValue(x>>>disp);
} else {
return bitsToValue(x<<-disp);
}
}
static LuaValue lshift(int x, int disp) {
if (disp >= 32 || disp <= -32) {
return ZERO;
} else if (disp >= 0) {
return bitsToValue(x<<disp);
} else {
return bitsToValue(x>>>-disp);
}
}
static Varargs band(Varargs args) {
int result = -1;
for (int i = 1; i <= args.narg(); i++) {
result &= args.checkint(i);
}
return bitsToValue(result);
}
static Varargs bnot(Varargs args) {
return bitsToValue(~args.checkint(1));
}
static Varargs bor(Varargs args) {
int result = 0;
for (int i = 1; i <= args.narg(); i++) {
result |= args.checkint(i);
}
return bitsToValue(result);
}
static Varargs btest(Varargs args) {
int bits = -1;
for (int i = 1; i <= args.narg(); i++) {
bits &= args.checkint(i);
}
return valueOf(bits != 0);
}
static Varargs bxor(Varargs args) {
int result = 0;
for (int i = 1; i <= args.narg(); i++) {
result ^= args.checkint(i);
}
return bitsToValue(result);
}
static LuaValue lrotate(int x, int disp) {
if (disp < 0) {
return rrotate(x, -disp);
} else {
disp = disp & 31;
return bitsToValue(x<<disp | x>>>32-disp);
}
}
static LuaValue rrotate(int x, int disp) {
if (disp < 0) {
return lrotate(x, -disp);
} else {
disp = disp & 31;
return bitsToValue(x>>>disp | x<<32-disp);
}
}
static LuaValue extract(int n, int field, int width) {
if (field < 0) {
argerror(2, "field cannot be negative");
}
if (width < 0) {
argerror(3, "width must be postive");
}
if (field+width > 32) {
error("trying to access non-existent bits");
}
return bitsToValue(n>>>field & -1>>>32-width);
}
static LuaValue replace(int n, int v, int field, int width) {
if (field < 0) {
argerror(3, "field cannot be negative");
}
if (width < 0) {
argerror(4, "width must be postive");
}
if (field+width > 32) {
error("trying to access non-existent bits");
}
int mask = -1>>>32-width<<field;
n = n & ~mask | v<<field & mask;
return bitsToValue(n);
}
private static LuaValue bitsToValue(int x) {
return x < 0? valueOf(x & 0xFFFFFFFFL): valueOf(x);
}
}

View File

@@ -0,0 +1,170 @@
/*******************************************************************************
* Copyright (c) 2007-2011 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.
******************************************************************************/
package org.luaj.vm2.lib;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaThread;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LibFunction} which implements the lua standard
* {@code coroutine} library.
* <p>
* The coroutine library in luaj has the same behavior as the coroutine library
* in C, but is implemented using Java Threads to maintain the call state
* between invocations. Therefore it can be yielded from anywhere, similar to
* the "Coco" yield-from-anywhere patch available for C-based lua. However,
* coroutines that are yielded but never resumed to complete their execution may
* not be collected by the garbage collector.
* <p>
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println(globals.get("coroutine").get("running").call());
* }
* </pre>
* <p>
* To instantiate and use it directly, link it into your globals table via
* {@link LuaValue#load(LuaValue)} using code such as:
*
* <pre>
* {
* &#64;code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* globals.load(new CoroutineLib());
* System.out.println(globals.get("coroutine").get("running").call());
* }
* </pre>
* <p>
*
* @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.2">Lua 5.2
* Coroutine Lib Reference</a>
*/
public class CoroutineLib extends TwoArgFunction {
static int coroutine_count = 0;
Globals globals;
/**
* Perform one-time initialization on the library by creating a table
* containing the library functions, adding that table to the supplied
* environment, adding the table to package.loaded, and returning table as
* the return value.
*
* @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, which must be a Globals
* instance.
*/
@Override
public LuaValue call(LuaValue modname, LuaValue env) {
globals = env.checkglobals();
LuaTable coroutine = new LuaTable();
coroutine.set("create", new create());
coroutine.set("resume", new resume());
coroutine.set("running", new running());
coroutine.set("status", new status());
coroutine.set("yield", new yield());
coroutine.set("wrap", new wrap());
env.set("coroutine", coroutine);
if (!env.get("package").isnil())
env.get("package").get("loaded").set("coroutine", coroutine);
return coroutine;
}
final class create extends LibFunction {
@Override
public LuaValue call(LuaValue f) {
return new LuaThread(globals, f.checkfunction());
}
}
static final class resume extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) {
final LuaThread t = args.checkthread(1);
return t.resume(args.subargs(2));
}
}
final class running extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) {
final LuaThread r = globals.running;
return varargsOf(r, valueOf(r.isMainThread()));
}
}
static final class status extends LibFunction {
@Override
public LuaValue call(LuaValue t) {
LuaThread lt = t.checkthread();
return valueOf(lt.getStatus());
}
}
final class yield extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) {
return globals.yield(args);
}
}
final class wrap extends LibFunction {
@Override
public LuaValue call(LuaValue f) {
final LuaValue func = f.checkfunction();
final LuaThread thread = new LuaThread(globals, func);
return new wrapper(thread);
}
}
static final class wrapper extends VarArgFunction {
final LuaThread luathread;
wrapper(LuaThread luathread) {
this.luathread = luathread;
}
@Override
public Varargs invoke(Varargs args) {
final Varargs result = luathread.resume(args);
if (result.arg1().toboolean()) {
return result.subargs(2);
} else {
return error(result.arg(2).tojstring());
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,748 @@
/*******************************************************************************
* Copyright (c) 2009-2011 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.lib;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Abstract base class extending {@link LibFunction} which implements the core
* of the lua standard {@code io} library.
* <p>
* It contains the implementation of the io library support that is common to
* the JSE and JME platforms. In practice on of the concrete IOLib subclasses is
* chosen: {@link org.luaj.vm2.lib.jse.JseIoLib} for the JSE platform, and
* {@link org.luaj.vm2.lib.jme.JmeIoLib} for the JME platform.
* <p>
* The JSE implementation conforms almost completely to the C-based lua library,
* while the JME implementation follows closely except in the area of
* random-access files, which are difficult to support properly on JME.
* <p>
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
* }
* </pre>
*
* In this example the platform-specific {@link org.luaj.vm2.lib.jse.JseIoLib}
* library will be loaded, which will include the base functionality provided by
* this class, whereas the {@link org.luaj.vm2.lib.jse.JsePlatform} would load
* the {@link org.luaj.vm2.lib.jse.JseIoLib}.
* <p>
* To instantiate and use it directly, link it into your globals table via
* {@link LuaValue#load(LuaValue)} using code such as:
*
* <pre>
* {
* &#64;code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* globals.load(new OsLib());
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
* }
* </pre>
* <p>
* This has been implemented to match as closely as possible the behavior in the
* corresponding library in C.
*
* @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform
* @see org.luaj.vm2.lib.jse.JseIoLib
* @see org.luaj.vm2.lib.jme.JmeIoLib
* @see <a href=
* "http://www.lua.org/manual/5.1/manual.html#5.7">http://www.lua.org/manual/5.1/manual.html#5.7</a>
*/
public abstract class IoLib extends TwoArgFunction {
protected abstract class File extends LuaValue {
public abstract void write(LuaString string) throws IOException;
public abstract void flush() throws IOException;
public abstract boolean isstdfile();
public abstract void close() throws IOException;
public abstract boolean isclosed();
// returns new position
public abstract int seek(String option, int bytecount) throws IOException;
public abstract void setvbuf(String mode, int size);
// get length remaining to read
public abstract int remaining() throws IOException;
// peek ahead one character
public abstract int peek() throws IOException, EOFException;
// return char if read, -1 if eof, throw IOException on other exception
public abstract int read() throws IOException, EOFException;
// return number of bytes read if positive, false if eof, throw IOException on other exception
public abstract 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
@Override
public LuaValue get(LuaValue key) {
return filemethods.get(key);
}
// essentially a userdata instance
@Override
public int type() {
return LuaValue.TUSERDATA;
}
@Override
public String typename() {
return "userdata";
}
// displays as "file" type
@Override
public String tojstring() {
return "file: " + Integer.toHexString(hashCode());
}
@Override
public void finalize() {
if (!isclosed()) {
try {
close();
} catch (IOException ignore) {
}
}
}
}
/** Enumerated value representing stdin */
protected static final int FTYPE_STDIN = 0;
/** Enumerated value representing stdout */
protected static final int FTYPE_STDOUT = 1;
/** Enumerated value representing stderr */
protected static final int FTYPE_STDERR = 2;
/** Enumerated value representing a file type for a named file */
protected static final int FTYPE_NAMED = 3;
/**
* Wrap the standard input.
*
* @return File
* @throws IOException
*/
abstract protected File wrapStdin() throws IOException;
/**
* Wrap the standard output.
*
* @return File
* @throws IOException
*/
abstract protected File wrapStdout() throws IOException;
/**
* Wrap the standard error output.
*
* @return File
* @throws IOException
*/
abstract protected File wrapStderr() throws IOException;
/**
* Open a file in a particular mode.
*
* @param filename
* @param readMode true if opening in read mode
* @param appendMode true if opening in append mode
* @param updateMode true if opening in update mode
* @param binaryMode true if opening in binary mode
* @return File object if successful
* @throws IOException if could not be opened
*/
abstract protected File openFile(String filename, boolean readMode, boolean appendMode, boolean updateMode,
boolean binaryMode) throws IOException;
/**
* Open a temporary file.
*
* @return File object if successful
* @throws IOException if could not be opened
*/
abstract protected File tmpFile() throws IOException;
/**
* Start a new process and return a file for input or output
*
* @param prog the program to execute
* @param mode "r" to read, "w" to write
* @return File to read to or write from
* @throws IOException if an i/o exception occurs
*/
abstract protected File openProgram(String prog, String mode) throws IOException;
private File infile = null;
private File outfile = null;
private File errfile = null;
private static final LuaValue STDIN = valueOf("stdin");
private static final LuaValue STDOUT = valueOf("stdout");
private static final LuaValue STDERR = valueOf("stderr");
private static final LuaValue FILE = valueOf("file");
private static final LuaValue CLOSED_FILE = valueOf("closed file");
private static final int IO_CLOSE = 0;
private static final int IO_FLUSH = 1;
private static final int IO_INPUT = 2;
private static final int IO_LINES = 3;
private static final int IO_OPEN = 4;
private static final int IO_OUTPUT = 5;
private static final int IO_POPEN = 6;
private static final int IO_READ = 7;
private static final int IO_TMPFILE = 8;
private static final int IO_TYPE = 9;
private static final int IO_WRITE = 10;
private static final int FILE_CLOSE = 11;
private static final int FILE_FLUSH = 12;
private static final int FILE_LINES = 13;
private static final int FILE_READ = 14;
private static final int FILE_SEEK = 15;
private static final int FILE_SETVBUF = 16;
private static final int FILE_WRITE = 17;
private static final int IO_INDEX = 18;
private static final int LINES_ITER = 19;
public static final String[] IO_NAMES = { "close", "flush", "input", "lines", "open", "output", "popen", "read",
"tmpfile", "type", "write", };
public static final String[] FILE_NAMES = { "close", "flush", "lines", "read", "seek", "setvbuf", "write", };
LuaTable filemethods;
protected Globals globals;
@Override
public LuaValue call(LuaValue modname, LuaValue env) {
globals = env.checkglobals();
// io lib functions
LuaTable t = new LuaTable();
bind(t, IoLibV.class, IO_NAMES);
// create file methods table
filemethods = new LuaTable();
bind(filemethods, IoLibV.class, FILE_NAMES, FILE_CLOSE);
// set up file metatable
LuaTable mt = new LuaTable();
bind(mt, IoLibV.class, new String[] { "__index" }, IO_INDEX);
t.setmetatable(mt);
// all functions link to library instance
setLibInstance(t);
setLibInstance(filemethods);
setLibInstance(mt);
// return the table
env.set("io", t);
if (!env.get("package").isnil())
env.get("package").get("loaded").set("io", t);
return t;
}
private void setLibInstance(LuaTable t) {
LuaValue[] k = t.keys();
for (int i = 0, n = k.length; i < n; i++)
((IoLibV) t.get(k[i])).iolib = this;
}
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) {
this.f = f;
this.name = name;
this.opcode = opcode;
this.iolib = iolib;
}
@Override
public Varargs invoke(Varargs args) {
try {
switch (opcode) {
case IO_FLUSH:
return iolib._io_flush();
case IO_TMPFILE:
return iolib._io_tmpfile();
case IO_CLOSE:
return iolib._io_close(args.arg1());
case IO_INPUT:
return iolib._io_input(args.arg1());
case IO_OUTPUT:
return iolib._io_output(args.arg1());
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);
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, 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, 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;
}
}
private File input() {
return infile != null? infile: (infile = ioopenfile(FTYPE_STDIN, "-", "r"));
}
// io.flush() -> bool
public Varargs _io_flush() throws IOException {
checkopen(output());
outfile.flush();
return LuaValue.TRUE;
}
// io.tmpfile() -> file
public Varargs _io_tmpfile() throws IOException {
return tmpFile();
}
// io.close([file]) -> void
public Varargs _io_close(LuaValue file) throws IOException {
File f = file.isnil()? output(): checkfile(file);
checkopen(f);
return ioclose(f);
}
// io.input([file]) -> file
public Varargs _io_input(LuaValue file) {
infile = file.isnil()? input()
: file.isstring()? ioopenfile(FTYPE_NAMED, file.checkjstring(), "r"): checkfile(file);
return infile;
}
// io.output(filename) -> file
public Varargs _io_output(LuaValue filename) {
outfile = filename.isnil()? output()
: filename.isstring()? ioopenfile(FTYPE_NAMED, filename.checkjstring(), "w"): checkfile(filename);
return outfile;
}
// io.type(obj) -> "file" | "closed file" | nil
public Varargs _io_type(LuaValue obj) {
File f = optfile(obj);
return f != null? f.isclosed()? CLOSED_FILE: FILE: NIL;
}
// 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);
}
// io.open(filename, [mode]) -> file | nil,err
public Varargs _io_open(String filename, String mode) throws IOException {
return rawopenfile(FTYPE_NAMED, filename, mode);
}
// 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, filename != null, args.subargs(2));
}
// io.read(...) -> (...)
public Varargs _io_read(Varargs args) throws IOException {
checkopen(input());
return ioread(infile, args);
}
// io.write(...) -> void
public Varargs _io_write(Varargs args) throws IOException {
checkopen(output());
return iowrite(outfile, args);
}
// file:close() -> void
public Varargs _file_close(LuaValue file) throws IOException {
return ioclose(checkfile(file));
}
// file:flush() -> void
public Varargs _file_flush(LuaValue file) throws IOException {
checkfile(file).flush();
return LuaValue.TRUE;
}
// 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(Varargs args) {
return lines(checkfile(args.arg1()), false, args.subargs(2));
}
// file:read(...) -> (...)
public Varargs _file_read(LuaValue file, Varargs subargs) throws IOException {
return ioread(checkfile(file), subargs);
}
// 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));
}
// file:write(...) -> void
public Varargs _file_write(LuaValue file, Varargs subargs) throws IOException {
return iowrite(checkfile(file), subargs);
}
// __index, returns a field
public Varargs _io_index(LuaValue v) {
return v.equals(STDOUT)? output(): v.equals(STDIN)? input(): v.equals(STDERR)? errput(): NIL;
}
// lines iterator(s,var) -> var'
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() {
return outfile != null? outfile: (outfile = ioopenfile(FTYPE_STDOUT, "-", "w"));
}
private File errput() {
return errfile != null? errfile: (errfile = ioopenfile(FTYPE_STDERR, "-", "w"));
}
private File ioopenfile(int filetype, String filename, String mode) {
try {
return rawopenfile(filetype, filename, mode);
} catch (Exception e) {
error("io error: " + e.getMessage());
return null;
}
}
private static Varargs ioclose(File f) throws IOException {
if (f.isstdfile())
return errorresult("cannot close standard file");
else {
f.close();
return successresult();
}
}
private static Varargs successresult() {
return LuaValue.TRUE;
}
static Varargs errorresult(Exception ioe) {
String s = ioe.getMessage();
return errorresult("io error: " + (s != null? s: ioe.toString()));
}
private static Varargs errorresult(String errortext) {
return varargsOf(NIL, valueOf(errortext));
}
private Varargs lines(final File f, boolean toclose, Varargs args) {
try {
return new IoLibV(f, "lnext", LINES_ITER, this, toclose, args);
} catch (Exception e) {
return error("lines: " + e);
}
}
private static Varargs iowrite(File f, Varargs args) throws IOException {
for (int i = 1, n = args.narg(); i <= n; i++)
f.write(args.checkstring(i));
return f;
}
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;
for (i = 0; i < n;) {
item: switch ((ai = args.arg(i+1)).type()) {
case LuaValue.TNUMBER:
vi = freadbytes(f, ai.toint());
break item;
case LuaValue.TSTRING:
fmt = ai.checkstring();
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, false);
break item;
case 'L':
vi = freadline(f, true);
break item;
case 'a':
vi = freadall(f);
break item;
}
}
default:
return argerror(i+1, "(invalid format)");
}
if ((v[i++] = vi).isnil())
break;
}
return i == 0? NIL: varargsOf(v, 0, i);
}
private static File checkfile(LuaValue val) {
File f = optfile(val);
if (f == null)
argerror(1, "file");
checkopen(f);
return f;
}
private static File optfile(LuaValue val) {
return (val instanceof File)? (File) val: null;
}
private static File checkopen(File file) {
if (file.isclosed())
error("attempt to use a closed file");
return file;
}
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) || (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();
case FTYPE_STDERR:
return wrapStderr();
}
boolean isreadmode = mode.startsWith("r");
boolean isappend = mode.startsWith("a");
boolean isupdate = mode.indexOf('+') > 0;
boolean isbinary = mode.endsWith("b");
return openFile(filename, isreadmode, isappend, isupdate, isbinary);
}
// ------------- 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, boolean withend) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int c;
try {
if (lineonly) {
loop: while ( (c = f.read()) >= 0 ) {
switch (c) {
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 )
baos.write(c);
}
} catch (EOFException e) {
c = -1;
}
return (c < 0 && baos.size() == 0)? (LuaValue) NIL: (LuaValue) LuaString.valueUsing(baos.toByteArray());
}
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 n == 0? EMPTYSTRING: freadbytes(f, n);
} else {
return freaduntil(f, false, false);
}
}
public static LuaValue freadnumber(File f) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
freadchars(f, " \t\r\n", null);
freadchars(f, "-+", baos);
//freadchars(f,"0",baos);
//freadchars(f,"xX",baos);
freadchars(f, "0123456789", baos);
freadchars(f, ".", baos);
freadchars(f, "0123456789", baos);
//freadchars(f,"eEfFgG",baos);
// freadchars(f,"+-",baos);
//freadchars(f,"0123456789",baos);
String s = baos.toString();
return s.length() > 0? valueOf(Double.parseDouble(s)): NIL;
}
private static void freadchars(File f, String chars, ByteArrayOutputStream baos) throws IOException {
int c;
while ( true ) {
c = f.peek();
if (chars.indexOf(c) < 0) {
return;
}
f.read();
if (baos != null)
baos.write(c);
}
}
}

View File

@@ -0,0 +1,263 @@
/*******************************************************************************
* Copyright (c) 2009-2011 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.lib;
import org.luaj.vm2.LuaError;
import org.luaj.vm2.LuaFunction;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LuaFunction} common to Java functions exposed to lua.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <ul>
* <li>{@link ZeroArgFunction}</li>
* <li>{@link OneArgFunction}</li>
* <li>{@link TwoArgFunction}</li>
* <li>{@link ThreeArgFunction}</li>
* <li>{@link VarArgFunction}</li>
* </ul>
* <p>
* 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.
* <p>
* For example, the following code will implement a library called "hyperbolic"
* with two functions, "sinh", and "cosh":
*
* <pre>
* {@code
* import org.luaj.vm2.LuaValue;
* import org.luaj.vm2.lib.*;
*
* public class hyperbolic extends TwoArgFunction {
*
* public hyperbolic() {}
*
* public LuaValue call(LuaValue modname, LuaValue env) {
* LuaValue library = tableOf();
* library.set( "sinh", new sinh() );
* library.set( "cosh", new cosh() );
* env.set( "hyperbolic", library );
* return library;
* }
*
* static class sinh extends OneArgFunction {
* public LuaValue call(LuaValue x) {
* return LuaValue.valueOf(Math.sinh(x.checkdouble()));
* }
* }
*
* static class cosh extends OneArgFunction {
* public LuaValue call(LuaValue x) {
* return LuaValue.valueOf(Math.cosh(x.checkdouble()));
* }
* }
*}
*}
* </pre>
*
* The default constructor is used to instantiate the library in response to
* {@code require 'hyperbolic'} statement, provided it is on Java&quot;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.
* <p>
* To test it, a script such as this can be used:
*
* <pre>
* {@code
* local t = require('hyperbolic')
* print( 't', t )
* print( 'hyperbolic', hyperbolic )
* for k,v in pairs(t) do
* print( 'k,v', k,v )
* end
* print( 'sinh(.5)', hyperbolic.sinh(.5) )
* print( 'cosh(.5)', hyperbolic.cosh(.5) )
* }
* </pre>
* <p>
* It should produce something like:
*
* <pre>
* {@code
* t table: 3dbbd23f
* hyperbolic table: 3dbbd23f
* k,v cosh function: 3dbbd128
* k,v sinh function: 3dbbd242
* sinh(.5) 0.5210953
* cosh(.5) 1.127626
* }
* </pre>
* <p>
* 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.
* <p>
* Subclass will typicall switch on this value to provide the specific
* behavior for each function.
*/
protected int opcode;
/**
* The common name for this function, useful for debugging.
* <p>
* Binding functions initialize this to the name to which it is bound.
*/
protected String name;
/** Default constructor for use by subclasses */
protected LibFunction() {
}
@Override
public String tojstring() {
return name != null? "function: " + name: super.tojstring();
}
/**
* Bind a set of library functions.
* <p>
* 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)
*/
protected void bind(LuaValue env, Class factory, String[] names) {
bind(env, factory, names, 0);
}
/**
* Bind a set of library functions, with an offset
* <p>
* 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[])
*/
protected void bind(LuaValue env, Class factory, String[] names, int firstopcode) {
try {
for (int i = 0, n = names.length; i < n; i++) {
LibFunction f = (LibFunction) factory.newInstance();
f.opcode = firstopcode+i;
f.name = names[i];
env.set(f.name, f);
}
} 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() {
return new LuaValue[1];
}
/**
* Java code generation utility to allocate storage for upvalue, initialize
* with nil
*/
protected static LuaValue[] newupn() {
return new LuaValue[] { NIL };
}
/**
* Java code generation utility to allocate storage for upvalue, initialize
* with value
*/
protected static LuaValue[] newupl(LuaValue v) {
return new LuaValue[] { v };
}
@Override
public LuaValue call() {
return argerror(1, "value expected");
}
@Override
public LuaValue call(LuaValue a) {
return call();
}
@Override
public LuaValue call(LuaValue a, LuaValue b) {
return call(a);
}
@Override
public LuaValue call(LuaValue a, LuaValue b, LuaValue c) {
return call(a, b);
}
public LuaValue call(LuaValue a, LuaValue b, LuaValue c, LuaValue d) {
return call(a, b, c);
}
@Override
public Varargs invoke(Varargs args) {
switch (args.narg()) {
case 0:
return call();
case 1:
return call(args.arg1());
case 2:
return call(args.arg1(), args.arg(2));
case 3:
return call(args.arg1(), args.arg(2), args.arg(3));
default:
return call(args.arg1(), args.arg(2), args.arg(3), args.arg(4));
}
}
}

View File

@@ -0,0 +1,396 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.lib;
import java.util.Random;
import org.luaj.vm2.LuaDouble;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LibFunction} which implements the lua standard
* {@code math} library.
* <p>
* It contains only the math library support that is possible on JME. For a more
* complete implementation based on math functions specific to JSE use
* {@link org.luaj.vm2.lib.jse.JseMathLib}. In Particular the following math
* functions are <b>not</b> implemented by this library:
* <ul>
* <li>acos</li>
* <li>asin</li>
* <li>atan</li>
* <li>cosh</li>
* <li>log</li>
* <li>sinh</li>
* <li>tanh</li>
* <li>atan2</li>
* </ul>
* <p>
* The implementations of {@code exp()} and {@code pow()} are constructed by
* hand for JME, so will be slower and less accurate than when executed on the
* JSE platform.
* <p>
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println(globals.get("math").get("sqrt").call(LuaValue.valueOf(2)));
* }
* </pre>
*
* When using {@link org.luaj.vm2.lib.jse.JsePlatform} as in this example, the
* subclass {@link org.luaj.vm2.lib.jse.JseMathLib} will be included, which also
* includes this base functionality.
* <p>
* To instantiate and use it directly, link it into your globals table via
* {@link LuaValue#load(LuaValue)} using code such as:
*
* <pre>
* {
* &#64;code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* globals.load(new MathLib());
* System.out.println(globals.get("math").get("sqrt").call(LuaValue.valueOf(2)));
* }
* </pre>
*
* Doing so will ensure the library is properly initialized and loaded into the
* globals table.
* <p>
* This has been implemented to match as closely as possible the behavior in the
* corresponding library in C.
*
* @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform
* @see org.luaj.vm2.lib.jse.JseMathLib
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.6">Lua 5.2 Math Lib
* Reference</a>
*/
public class MathLib extends TwoArgFunction {
/**
* Pointer to the latest MathLib instance, used only to dispatch math.exp to
* tha correct platform math library.
*/
public static MathLib MATHLIB = null;
/**
* Construct a MathLib, which can be initialized by calling it with a
* modname string, and a global environment table as arguments using
* {@link #call(LuaValue, LuaValue)}.
*/
public MathLib() {
MATHLIB = this;
}
/**
* Perform one-time initialization on the library by creating a table
* containing the library functions, adding that table to the supplied
* environment, adding the table to package.loaded, and returning table as
* the return value.
*
* @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, typically a Globals
* instance.
*/
@Override
public LuaValue call(LuaValue modname, LuaValue env) {
LuaTable math = new LuaTable(0, 30);
math.set("abs", new abs());
math.set("ceil", new ceil());
math.set("cos", new cos());
math.set("deg", new deg());
math.set("exp", new exp(this));
math.set("floor", new floor());
math.set("fmod", new fmod());
math.set("frexp", new frexp());
math.set("huge", LuaDouble.POSINF);
math.set("ldexp", new ldexp());
math.set("max", new max());
math.set("min", new min());
math.set("modf", new modf());
math.set("pi", Math.PI);
math.set("pow", new pow());
random r;
math.set("random", r = new random());
math.set("randomseed", new randomseed(r));
math.set("rad", new rad());
math.set("sin", new sin());
math.set("sqrt", new sqrt());
math.set("tan", new tan());
env.set("math", math);
if (!env.get("package").isnil())
env.get("package").get("loaded").set("math", math);
return math;
}
abstract protected static class UnaryOp extends OneArgFunction {
@Override
public LuaValue call(LuaValue arg) {
return valueOf(call(arg.checkdouble()));
}
abstract protected double call(double d);
}
abstract protected static class BinaryOp extends TwoArgFunction {
@Override
public LuaValue call(LuaValue x, LuaValue y) {
return valueOf(call(x.checkdouble(), y.checkdouble()));
}
abstract protected double call(double x, double y);
}
static final class abs extends UnaryOp {
@Override
protected double call(double d) { return Math.abs(d); }
}
static final class ceil extends UnaryOp {
@Override
protected double call(double d) { return Math.ceil(d); }
}
static final class cos extends UnaryOp {
@Override
protected double call(double d) { return Math.cos(d); }
}
static final class deg extends UnaryOp {
@Override
protected double call(double d) { return Math.toDegrees(d); }
}
static final class floor extends UnaryOp {
@Override
protected double call(double d) { return Math.floor(d); }
}
static final class rad extends UnaryOp {
@Override
protected double call(double d) { return Math.toRadians(d); }
}
static final class sin extends UnaryOp {
@Override
protected double call(double d) { return Math.sin(d); }
}
static final class sqrt extends UnaryOp {
@Override
protected double call(double d) { return Math.sqrt(d); }
}
static final class tan extends UnaryOp {
@Override
protected double call(double d) { return Math.tan(d); }
}
static final class exp extends UnaryOp {
final MathLib mathlib;
exp(MathLib mathlib) {
this.mathlib = mathlib;
}
@Override
protected double call(double d) {
return mathlib.dpow_lib(Math.E, d);
}
}
static final class fmod extends TwoArgFunction {
@Override
public LuaValue call(LuaValue xv, LuaValue yv) {
if (yv.checkdouble() == 0.0d)
return LuaDouble.NAN;
if (xv.islong() && yv.islong()) {
return valueOf(xv.tolong()%yv.tolong());
}
return valueOf(xv.checkdouble()%yv.checkdouble());
}
}
static final class ldexp extends BinaryOp {
@Override
protected double call(double x, double y) {
// This is the behavior on os-x, windows differs in rounding behavior.
return x*Double.longBitsToDouble((long) y+1023<<52);
}
}
static final class pow extends BinaryOp {
@Override
protected double call(double x, double y) {
return MathLib.dpow_default(x, y);
}
}
static class frexp extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) {
double x = args.checkdouble(1);
if (x == 0)
return varargsOf(ZERO, ZERO);
long bits = Double.doubleToLongBits(x);
double m = ((bits & ~(-1L<<52))+(1L<<52))*(bits >= 0? .5/(1L<<52): -.5/(1L<<52));
double e = ((int) (bits>>52) & 0x7ff)-1022;
return varargsOf(valueOf(m), valueOf(e));
}
}
static class max extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) {
LuaValue m = args.checknumber(1);
for (int i = 2, n = args.narg(); i <= n; ++i) {
LuaValue v = args.checknumber(i);
if (m.lt_b(v))
m = v;
}
return m;
}
}
static class min extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) {
LuaValue m = args.checknumber(1);
for (int i = 2, n = args.narg(); i <= n; ++i) {
LuaValue v = args.checknumber(i);
if (v.lt_b(m))
m = v;
}
return m;
}
}
static class modf extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) {
LuaValue n = args.arg1();
/* number is its own integer part, no fractional part */
if (n.islong())
return varargsOf(n.tonumber(), valueOf(0.0));
double x = n.checkdouble();
/* integer part (rounds toward zero) */
double intPart = x > 0? Math.floor(x): Math.ceil(x);
/* fractional part (test needed for inf/-inf) */
double fracPart = x == intPart? 0.0: x-intPart;
return varargsOf(valueOf(intPart), valueOf(fracPart));
}
}
static class random extends LibFunction {
Random random = new Random();
@Override
public LuaValue call() {
return valueOf(random.nextDouble());
}
@Override
public LuaValue call(LuaValue a) {
int m = a.checkint();
if (m < 1)
argerror(1, "interval is empty");
return valueOf(1+random.nextInt(m));
}
@Override
public LuaValue call(LuaValue a, LuaValue b) {
int m = a.checkint();
int n = b.checkint();
if (n < m)
argerror(2, "interval is empty");
return valueOf(m+random.nextInt(n+1-m));
}
}
static class randomseed extends OneArgFunction {
final random random;
randomseed(random random) {
this.random = random;
}
@Override
public LuaValue call(LuaValue arg) {
long seed = arg.checklong();
random.random = new Random(seed);
return NONE;
}
}
/**
* compute power using installed math library, or default if there is no
* math library installed
*/
public static LuaValue dpow(double a, double b) {
return LuaDouble.valueOf(MATHLIB != null? MATHLIB.dpow_lib(a, b): dpow_default(a, b));
}
public static double dpow_d(double a, double b) {
return MATHLIB != null? MATHLIB.dpow_lib(a, b): dpow_default(a, b);
}
/**
* Hook to override default dpow behavior with faster implementation.
*/
public double dpow_lib(double a, double b) {
return dpow_default(a, b);
}
/**
* Default JME version computes using longhand heuristics.
*/
protected static double dpow_default(double a, double b) {
if (b < 0)
return 1/dpow_default(a, -b);
double p = 1;
int whole = (int) b;
for (double v = a; whole > 0; whole >>= 1, v *= v)
if ((whole & 1) != 0)
p *= v;
if ((b -= whole) > 0) {
int frac = (int) (0x10000*b);
for (; (frac & 0xffff) != 0; frac <<= 1) {
a = Math.sqrt(a);
if ((frac & 0x8000) != 0)
p *= a;
}
}
return p;
}
}

View File

@@ -0,0 +1,79 @@
/*******************************************************************************
* Copyright (c) 2009-2011 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.lib;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Abstract base class for Java function implementations that take one argument
* and return one value.
* <p>
* Subclasses need only implement {@link LuaValue#call(LuaValue)} to complete
* this class, simplifying development. All other uses of {@link #call()},
* {@link #invoke(Varargs)},etc, are routed through this method by this class,
* dropping or extending arguments with {@code nil} values as required.
* <p>
* If more than one argument are required, or no arguments are required, or
* variable argument or variable return values, then use one of the related
* function {@link ZeroArgFunction}, {@link TwoArgFunction},
* {@link ThreeArgFunction}, or {@link VarArgFunction}.
* <p>
* See {@link LibFunction} for more information on implementation libraries and
* library functions.
*
* @see #call(LuaValue)
* @see LibFunction
* @see ZeroArgFunction
* @see TwoArgFunction
* @see ThreeArgFunction
* @see VarArgFunction
*/
abstract public class OneArgFunction extends LibFunction {
@Override
abstract public LuaValue call(LuaValue arg);
/** Default constructor */
public OneArgFunction() {
}
@Override
public final LuaValue call() {
return call(NIL);
}
@Override
public final LuaValue call(LuaValue arg1, LuaValue arg2) {
return call(arg1);
}
@Override
public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
return call(arg1);
}
@Override
public Varargs invoke(Varargs varargs) {
return call(varargs.arg1());
}
}

View File

@@ -0,0 +1,522 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.lib;
import java.io.IOException;
import java.time.format.TextStyle;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.luaj.vm2.Buffer;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LibFunction} which implements the standard lua {@code os}
* library.
* <p>
* It is a usable base with simplified stub functions for library functions that
* cannot be implemented uniformly on Jse and Jme.
* <p>
* This can be installed as-is on either platform, or extended and refined to be
* used in a complete Jse implementation.
* <p>
* 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.
* <p>
* The following functions have limited implementations of features that are not
* supported well on Jme:
* <ul>
* <li>{@code execute()}</li>
* <li>{@code remove()}</li>
* <li>{@code rename()}</li>
* <li>{@code tmpname()}</li>
* </ul>
* <p>
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println(globals.get("os").get("time").call());
* }
* </pre>
*
* In this example the platform-specific {@link org.luaj.vm2.lib.jse.JseOsLib}
* library will be loaded, which will include the base functionality provided by
* this class.
* <p>
* To instantiate and use it directly, link it into your globals table via
* {@link LuaValue#load(LuaValue)} using code such as:
*
* <pre>
* {
* &#64;code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* globals.load(new OsLib());
* System.out.println(globals.get("os").get("time").call());
* }
* </pre>
* <p>
*
* @see LibFunction
* @see org.luaj.vm2.lib.jse.JseOsLib
* @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform
* @see <a href=
* "http://www.lua.org/manual/5.1/manual.html#5.8">http://www.lua.org/manual/5.1/manual.html#5.8</a>
*/
public class OsLib extends TwoArgFunction {
public static final String TMP_PREFIX = ".luaj";
public static final String TMP_SUFFIX = "tmp";
private static final int CLOCK = 0;
private static final int DATE = 1;
private static final int DIFFTIME = 2;
private static final int EXECUTE = 3;
private static final int EXIT = 4;
private static final int GETENV = 5;
private static final int REMOVE = 6;
private static final int RENAME = 7;
private static final int SETLOCALE = 8;
private static final int TIME = 9;
private static final int TMPNAME = 10;
private static final String[] NAMES = { "clock", "date", "difftime", "execute", "exit", "getenv", "remove",
"rename", "setlocale", "time", "tmpname", };
private static final long t0 = System.currentTimeMillis();
private static long tmpnames = t0;
protected Globals globals;
/**
* Create and OsLib instance.
*/
public OsLib() {
}
/**
* Perform one-time initialization on the library by creating a table
* containing the library functions, adding that table to the supplied
* environment, adding the table to package.loaded, and returning table as
* the return value.
*
* @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, typically a Globals
* instance.
*/
@Override
public LuaValue call(LuaValue modname, LuaValue env) {
globals = env.checkglobals();
LuaTable os = new LuaTable();
for (int i = 0; i < NAMES.length; ++i)
os.set(NAMES[i], new OsLibFunc(i, NAMES[i]));
env.set("os", os);
if (!env.get("package").isnil())
env.get("package").get("loaded").set("os", os);
return os;
}
class OsLibFunc extends VarArgFunction {
public OsLibFunc(int opcode, String name) {
this.opcode = opcode;
this.name = name;
}
@Override
public Varargs invoke(Varargs args) {
try {
switch (opcode) {
case CLOCK:
return valueOf(clock());
case DATE: {
String s = args.optjstring(1, "%c");
long t = args.isnumber(2)? args.tolong(2): time(null);
if (s.equals("*t")) {
Calendar d = Calendar.getInstance();
d.setTime(new Date(t*1000));
LuaTable tbl = LuaValue.tableOf();
tbl.set("year", LuaValue.valueOf(d.get(Calendar.YEAR)));
tbl.set("month", LuaValue.valueOf(d.get(Calendar.MONTH)+1));
tbl.set("day", LuaValue.valueOf(d.get(Calendar.DAY_OF_MONTH)));
tbl.set("hour", LuaValue.valueOf(d.get(Calendar.HOUR_OF_DAY)));
tbl.set("min", LuaValue.valueOf(d.get(Calendar.MINUTE)));
tbl.set("sec", LuaValue.valueOf(d.get(Calendar.SECOND)));
tbl.set("wday", LuaValue.valueOf(d.get(Calendar.DAY_OF_WEEK)));
tbl.set("yday", LuaValue.valueOf(d.get(0x6))); // Day of year
tbl.set("isdst", LuaValue.valueOf(isDaylightSavingsTime(d)));
return tbl;
}
return valueOf(date(s, t == -1? time(null): t));
}
case DIFFTIME:
return valueOf(difftime(args.checkdouble(1), args.checkdouble(2)));
case EXECUTE:
return execute(args.optjstring(1, null));
case EXIT:
exit(args.optint(1, 0));
return NONE;
case GETENV: {
final String val = getenv(args.checkjstring(1));
return val != null? valueOf(val): NIL;
}
case REMOVE:
remove(args.checkjstring(1));
return LuaValue.TRUE;
case RENAME:
rename(args.checkjstring(1), args.checkjstring(2));
return LuaValue.TRUE;
case SETLOCALE: {
String s = setlocale(args.optjstring(1, null), args.optjstring(2, "all"));
return s != null? valueOf(s): NIL;
}
case TIME:
return valueOf(time(args.opttable(1, null)));
case TMPNAME:
return valueOf(tmpname());
}
return NONE;
} catch (IOException e) {
return varargsOf(NIL, valueOf(e.getMessage()));
}
}
}
/**
* @return an approximation of the amount in seconds of CPU time used by the
* program. For luaj this simple returns the elapsed time since the
* OsLib class was loaded.
*/
protected double clock() {
return (System.currentTimeMillis()-t0)/1000.;
}
/**
* Returns the number of seconds from time t1 to time t2. In POSIX, Windows,
* and some other systems, this value is exactly t2-t1.
*
* @param t2
* @param t1
* @return diffeence in time values, in seconds
*/
protected double difftime(double t2, double t1) {
return t2-t1;
}
/**
* If the time argument is present, this is the time to be formatted (see
* the os.time function for a description of this value). Otherwise, date
* formats the current time.
*
* Date returns the date as a string, formatted according to the same rules
* as ANSII strftime, but without support for %g, %G, or %V.
*
* When called without arguments, date returns a reasonable date and time
* representation that depends on the host system and on the current locale
* (that is, os.date() is equivalent to os.date("%c")).
*
* @param format
* @param timeInSec time since epoch, or -1 if not supplied
* @return a LString or a LTable containing date and time, formatted
* according to the given string format.
*/
private static String date(String format, long timeInSec) {
Calendar d = Calendar.getInstance();
d.setTime(new Date(timeInSec*1000));
if (format.startsWith("!")) {
timeInSec -= timeZoneOffset(d);
d.setTime(new Date(timeInSec*1000));
format = format.substring(1);
}
byte[] fmt = format.getBytes();
final int n = fmt.length;
Buffer result = new Buffer(n);
for (int i = 0; i < n; i++) {
byte c = fmt[i];
switch (c) {
case '\n':
result.append("\n");
break;
case '%':
if (++i >= n)
break;
String conv = Character.toString((char) fmt[i]);
if (CONVERTERS.containsKey(conv)) {
result.append(CONVERTERS.get(conv).convert(d));
} else {
LuaValue.argerror(1, "invalid conversion specifier '%" + conv + "'");
}
break;
default:
result.append(c);
break;
}
}
return result.tojstring();
}
private static final String[] WeekdayNameAbbrev = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
private static final String[] WeekdayName = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
"Friday", "Saturday" };
private static final String[] MonthNameAbbrev = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
"Oct", "Nov", "Dec" };
private static final String[] MonthName = { "January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December" };
private static interface DateConversion {
public String convert(Calendar d);
}
private static final Map<String, DateConversion> CONVERTERS = new HashMap<>();
static {
CONVERTERS.put("%", d -> "%");
CONVERTERS.put("a", d -> WeekdayNameAbbrev[d.get(Calendar.DAY_OF_WEEK)-1]);
CONVERTERS.put("A", d -> WeekdayName[d.get(Calendar.DAY_OF_WEEK)-1]);
CONVERTERS.put("b", d -> MonthNameAbbrev[d.get(Calendar.MONTH)]);
CONVERTERS.put("B", d -> MonthName[d.get(Calendar.MONTH)]);
CONVERTERS.put("c", d -> date("%a %b %e %H:%M:%S %Y", d.getTimeInMillis()/1000L));
CONVERTERS.put("C", d -> String.valueOf(d.get(Calendar.YEAR)).substring(0, 2));
CONVERTERS.put("d", d -> String.valueOf(100+d.get(Calendar.DAY_OF_MONTH)).substring(1));
CONVERTERS.put("D", d -> date("%m/%d/%y", d.getTimeInMillis()/1000L));
CONVERTERS.put("e", d -> String.format("%2d", d.get(Calendar.DAY_OF_MONTH)));
CONVERTERS.put("F", d -> date("%Y-%m-%d", d.getTimeInMillis()/1000L));
CONVERTERS.put("g", d -> String.valueOf(d.get(Calendar.YEAR)).substring(2));
CONVERTERS.put("G", d -> String.valueOf(d.get(Calendar.YEAR)));
CONVERTERS.put("h", d -> MonthNameAbbrev[d.get(Calendar.MONTH)]);
CONVERTERS.put("H", d -> String.valueOf(100+d.get(Calendar.HOUR_OF_DAY)).substring(1));
CONVERTERS.put("I", d -> String.valueOf(100+d.get(Calendar.HOUR_OF_DAY)%12).substring(1));
// day of year
CONVERTERS.put("j", d -> {
Calendar y0 = beginningOfYear(d);
int dayOfYear = (int) ((d.getTimeInMillis()-y0.getTimeInMillis())/(24*3600L*1000L));
return String.valueOf(1001+dayOfYear).substring(1);
});
CONVERTERS.put("m", d -> String.valueOf(101+d.get(Calendar.MONTH)).substring(1));
CONVERTERS.put("M", d -> String.valueOf(100+d.get(Calendar.MINUTE)).substring(1));
CONVERTERS.put("n", d -> "\n");
CONVERTERS.put("p", d -> d.get(Calendar.HOUR_OF_DAY) < 12? "AM": "PM");
CONVERTERS.put("r", d -> date("%I:%M:%S %p", d.getTimeInMillis()/1000L));
CONVERTERS.put("R", d -> date("%H:%M", d.getTimeInMillis()/1000L));
CONVERTERS.put("S", d -> String.valueOf(100+d.get(Calendar.SECOND)).substring(1));
CONVERTERS.put("t", d -> "\t");
CONVERTERS.put("T", d -> date("%H:%M:%S", d.getTimeInMillis()/1000L));
CONVERTERS.put("u", d -> String.valueOf((d.get(Calendar.DAY_OF_WEEK)+6)%7));
CONVERTERS.put("U", d -> String.valueOf(weekNumber(d, 0)));
CONVERTERS.put("V", d -> String.valueOf(weekNumber(d, 0)));
CONVERTERS.put("w", d -> String.valueOf((d.get(Calendar.DAY_OF_WEEK)+6)%7));
CONVERTERS.put("W", d -> String.valueOf(weekNumber(d, 1)));
CONVERTERS.put("x", d -> date("%m/%d/%y", d.getTimeInMillis()/1000L));
CONVERTERS.put("X", d -> date("%H:%M:%S", d.getTimeInMillis()/1000L));
CONVERTERS.put("y", d -> String.valueOf(d.get(Calendar.YEAR)).substring(2));
CONVERTERS.put("Y", d -> String.valueOf(d.get(Calendar.YEAR)));
CONVERTERS.put("z", d -> {
final int tzo = timeZoneOffset(d)/60;
final int a = Math.abs(tzo);
final String h = String.valueOf(100+a/60).substring(1);
final String m = String.valueOf(100+a%60).substring(1);
return (tzo >= 0? "+": "-")+h+m;
});
CONVERTERS.put("Z", d -> d.getTimeZone().toZoneId().getDisplayName(TextStyle.SHORT, Locale.getDefault()));
}
private static Calendar beginningOfYear(Calendar d) {
Calendar y0 = Calendar.getInstance();
y0.setTime(d.getTime());
y0.set(Calendar.MONTH, 0);
y0.set(Calendar.DAY_OF_MONTH, 1);
y0.set(Calendar.HOUR_OF_DAY, 0);
y0.set(Calendar.MINUTE, 0);
y0.set(Calendar.SECOND, 0);
y0.set(Calendar.MILLISECOND, 0);
return y0;
}
private static int weekNumber(Calendar d, int startDay) {
Calendar y0 = beginningOfYear(d);
y0.set(Calendar.DAY_OF_MONTH, 1+(startDay+8-y0.get(Calendar.DAY_OF_WEEK))%7);
if (y0.after(d)) {
y0.set(Calendar.YEAR, y0.get(Calendar.YEAR)-1);
y0.set(Calendar.DAY_OF_MONTH, 1+(startDay+8-y0.get(Calendar.DAY_OF_WEEK))%7);
}
long dt = d.getTime().getTime()-y0.getTime().getTime();
return 1+(int) (dt/(7L*24L*3600L*1000L));
}
private static int timeZoneOffset(Calendar d) {
int localStandarTimeMillis = (d.get(Calendar.HOUR_OF_DAY)*3600+d.get(Calendar.MINUTE)*60+d.get(Calendar.SECOND))
*1000;
return d.getTimeZone().getOffset(1, d.get(Calendar.YEAR), d.get(Calendar.MONTH), d.get(Calendar.DAY_OF_MONTH),
d.get(Calendar.DAY_OF_WEEK), localStandarTimeMillis)/1000;
}
private boolean isDaylightSavingsTime(Calendar d) {
return timeZoneOffset(d) != d.getTimeZone().getRawOffset()/1000;
}
/**
* This function is equivalent to the C function system. It passes command
* to be executed by an operating system shell. It returns a status code,
* which is system-dependent. If command is absent, then it returns nonzero
* if a shell is available and zero otherwise.
*
* @param command command to pass to the system
*/
protected Varargs execute(String command) {
return varargsOf(NIL, valueOf("exit"), ONE);
}
/**
* Calls the C function exit, with an optional code, to terminate the host
* program.
*
* @param code
*/
protected void exit(int code) {
System.exit(code);
}
/**
* Returns the value of the process environment variable varname, or the
* System property value for varname, or null if the variable is not defined
* in either environment.
*
* The default implementation, which is used by the JmePlatform, only
* queryies System.getProperty().
*
* The JsePlatform overrides this behavior and returns the environment
* variable value using System.getenv() if it exists, or the System property
* value if it does not.
*
* A SecurityException may be thrown if access is not allowed for 'varname'.
*
* @param varname
* @return String value, or null if not defined
*/
protected String getenv(String varname) {
return System.getProperty(varname);
}
/**
* Deletes the file or directory with the given name. Directories must be
* empty to be removed. If this function fails, it throws and IOException
*
* @param filename
* @throws IOException if it fails
*/
protected void remove(String filename) throws IOException {
throw new IOException("not implemented");
}
/**
* Renames file or directory named oldname to newname. If this function
* fails,it throws and IOException
*
* @param oldname old file name
* @param newname new file name
* @throws IOException if it fails
*/
protected void rename(String oldname, String newname) throws IOException {
throw new IOException("not implemented");
}
/**
* Sets the current locale of the program. locale is a string specifying a
* locale; category is an optional string describing which category to
* change: "all", "collate", "ctype", "monetary", "numeric", or "time"; the
* default category is "all".
*
* If locale is the empty string, the current locale is set to an
* implementation- defined native locale. If locale is the string "C", the
* current locale is set to the standard C locale.
*
* When called with null as the first argument, this function only returns
* the name of the current locale for the given category.
*
* @param locale
* @param category
* @return the name of the new locale, or null if the request cannot be
* honored.
*/
protected String setlocale(String locale, String category) {
return "C";
}
/**
* Returns the current time when called without arguments, or a time
* representing the date and time specified by the given table. This table
* must have fields year, month, and day, and may have fields hour, min,
* sec, and isdst (for a description of these fields, see the os.date
* function).
*
* @param table
* @return long value for the time
*/
protected long time(LuaTable table) {
java.util.Date d;
if (table == null) {
d = new java.util.Date();
} else {
Calendar c = Calendar.getInstance();
c.set(Calendar.YEAR, table.get("year").checkint());
c.set(Calendar.MONTH, table.get("month").checkint()-1);
c.set(Calendar.DAY_OF_MONTH, table.get("day").checkint());
c.set(Calendar.HOUR_OF_DAY, table.get("hour").optint(12));
c.set(Calendar.MINUTE, table.get("min").optint(0));
c.set(Calendar.SECOND, table.get("sec").optint(0));
c.set(Calendar.MILLISECOND, 0);
d = c.getTime();
}
return d.getTime()/1000L;
}
/**
* Returns a string with a file name that can be used for a temporary file.
* The file must be explicitly opened before its use and explicitly removed
* when no longer needed.
*
* On some systems (POSIX), this function also creates a file with that
* name, to avoid security risks. (Someone else might create the file with
* wrong permissions in the time between getting the name and creating the
* file.) You still have to open the file to use it and to remove it (even
* if you do not use it).
*
* @return String filename to use
*/
protected String tmpname() {
synchronized (OsLib.class) {
return TMP_PREFIX+tmpnames+++TMP_SUFFIX;
}
}
}

View File

@@ -0,0 +1,433 @@
/*******************************************************************************
* Copyright (c) 2010-2011 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.lib;
import java.io.InputStream;
import java.nio.file.FileSystems;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaFunction;
import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LibFunction} which implements the lua standard package and
* module library functions.
*
* <h3>Lua Environment Variables</h3> The following variables are available to
* lua scrips when this library has been loaded:
* <ul>
* <li><code>"package.loaded"</code> Lua table of loaded modules.
* <li><code>"package.path"</code> Search path for lua scripts.
* <li><code>"package.preload"</code> Lua table of uninitialized preload
* functions.
* <li><code>"package.searchers"</code> Lua table of functions that search for
* object to load.
* </ul>
*
* <h3>Java Environment Variables</h3> These Java environment variables affect
* the library behavior:
* <ul>
* <li><code>"luaj.package.path"</code> Initial value for
* <code>"package.path"</code>. Default value is <code>"?.lua"</code>
* </ul>
*
* <h3>Loading</h3> Typically, this library is included as part of a call to
* either {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
*
* <pre>
* {@code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("require").call"foo") );
* }
* </pre>
* <p>
* To instantiate and use it directly, link it into your globals table via
* {@link LuaValue#load(LuaValue)} using code such as:
*
* <pre>
* {
* &#64;code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* System.out.println(globals.get("require").call("foo"));
* }
* </pre>
*
* <h3>Limitations</h3> This library has been implemented to match as closely as
* possible the behavior in the corresponding library in C. However, the default
* filesystem search semantics are different and delegated to the bas library as
* outlined in the {@link BaseLib} and {@link org.luaj.vm2.lib.jse.JseBaseLib}
* documentation.
* <p>
*
* @see LibFunction
* @see BaseLib
* @see org.luaj.vm2.lib.jse.JseBaseLib
* @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.3">Lua 5.2 Package
* Lib Reference</a>
*/
public class PackageLib extends TwoArgFunction {
/**
* The default value to use for package.path. This can be set with the
* system property <code>"luaj.package.path"</code>, and is
* <code>"?.lua"</code> by default.
*/
public static final String DEFAULT_LUA_PATH;
static {
String path = null;
try {
path = System.getProperty("luaj.package.path");
} catch (Exception e) {
System.out.println(e.toString());
}
if (path == null) {
path = "?.lua";
}
DEFAULT_LUA_PATH = path;
}
static final LuaString _LOADED = valueOf("loaded");
private static final LuaString _LOADLIB = valueOf("loadlib");
static final LuaString _PRELOAD = valueOf("preload");
static final LuaString _PATH = valueOf("path");
static final LuaString _SEARCHPATH = valueOf("searchpath");
static final LuaString _SEARCHERS = valueOf("searchers");
static final LuaString _SEEALL = valueOf("seeall");
/** The globals that were used to load this library. */
Globals globals;
/** The table for this package. */
LuaTable package_;
/** Loader that loads from {@code preload} table if found there */
public preload_searcher preload_searcher;
/**
* Loader that loads as a lua script using the lua path currently in
* {@link path}
*/
public lua_searcher lua_searcher;
/**
* Loader that loads as a Java class. Class must have public constructor and
* be a LuaValue.
*/
public java_searcher java_searcher;
private static final LuaString _SENTINEL = valueOf("\u0001");
private static final String FILE_SEP = FileSystems.getDefault().getSeparator();
public PackageLib() {}
/**
* Perform one-time initialization on the library by adding package
* functions to the supplied environment, and returning it as the return
* value. It also creates the package.preload and package.loaded tables for
* use by other libraries.
*
* @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, typically a Globals
* instance.
*/
@Override
public LuaValue call(LuaValue modname, LuaValue env) {
globals = env.checkglobals();
globals.set("require", new require());
package_ = new LuaTable();
package_.set(_LOADED, new LuaTable());
package_.set(_PRELOAD, new LuaTable());
package_.set(_PATH, LuaValue.valueOf(DEFAULT_LUA_PATH));
package_.set(_LOADLIB, new loadlib());
package_.set(_SEARCHPATH, new searchpath());
package_.set(_SEEALL, new seeall());
LuaTable searchers = new LuaTable();
searchers.set(1, preload_searcher = new preload_searcher());
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;
return env;
}
/** Allow packages to mark themselves as loaded */
public void setIsLoaded(String name, LuaTable value) {
package_.get(_LOADED).set(name, value);
}
/**
* Set the lua path used by this library instance to a new value. Merely
* sets the value of {@link path} to be used in subsequent searches.
*/
public void setLuaPath(String newLuaPath) {
package_.set(_PATH, LuaValue.valueOf(newLuaPath));
}
@Override
public String tojstring() {
return "package";
}
// ======================== Package loading =============================
/**
* require (modname)
*
* Loads the given module. The function starts by looking into the
* package.loaded table to determine whether modname is already loaded. If
* it is, then require returns the value stored at package.loaded[modname].
* Otherwise, it tries to find a loader for the module.
*
* To find a loader, require is guided by the package.searchers sequence. By
* changing this sequence, we can change how require looks for a module. The
* following explanation is based on the default configuration for
* package.searchers.
*
* First require queries package.preload[modname]. If it has a value, this
* value (which should be a function) is the loader. Otherwise require
* searches for a Lua loader using the path stored in package.path. If that
* also fails, it searches for a Java loader using the classpath, using the
* public default constructor, and casting the instance to LuaFunction.
*
* Once a loader is found, require calls the loader with two arguments:
* modname and an extra value dependent on how it got the loader. If the
* loader came from a file, this extra value is the file name. If the loader
* is a Java instance of LuaFunction, this extra value is the environment.
* If the loader returns any non-nil value, require assigns the returned
* value to package.loaded[modname]. If the loader does not return a non-nil
* value and has not assigned any value to package.loaded[modname], then
* require assigns true to this entry. In any case, require returns the
* final value of package.loaded[modname].
*
* If there is any error loading or running the module, or if it cannot find
* any loader for the module, then require raises an error.
*/
public class require extends OneArgFunction {
@Override
public LuaValue call(LuaValue arg) {
LuaString name = arg.checkstring();
LuaValue loaded = package_.get(_LOADED);
LuaValue result = loaded.get(name);
if (result.toboolean()) {
if (result == _SENTINEL)
error("loop or previous error loading module '" + name + "'");
return result;
}
/* else must load it; iterate over available loaders */
LuaTable tbl = package_.get(_SEARCHERS).checktable();
StringBuffer sb = new StringBuffer();
Varargs loader = null;
for (int i = 1; true; i++) {
LuaValue searcher = tbl.get(i);
if (searcher.isnil()) {
error("module '" + name + "' not found: " + name + sb);
}
/* call loader with module name as argument */
loader = searcher.invoke(name);
if (loader.isfunction(1))
break;
if (loader.isstring(1))
sb.append(loader.tojstring(1));
}
// load the module using the loader
loaded.set(name, _SENTINEL);
result = loader.arg1().call(name, loader.arg(2));
if (!result.isnil())
loaded.set(name, result);
else if ((result = loaded.get(name)) == _SENTINEL)
loaded.set(name, result = LuaValue.TRUE);
return result;
}
}
public static class loadlib extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) {
args.checkstring(1);
return varargsOf(NIL, valueOf("dynamic libraries not enabled"), valueOf("absent"));
}
}
public class preload_searcher extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) {
LuaString name = args.checkstring(1);
LuaValue val = package_.get(_PRELOAD).get(name);
return val.isnil()? valueOf("\n\tno field package.preload['" + name + "']"): val;
}
}
public class lua_searcher extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) {
LuaString name = args.checkstring(1);
// get package path
LuaValue path = package_.get(_PATH);
if (!path.isstring())
return valueOf("package.path is not a string");
// get the searchpath function.
Varargs v = package_.get(_SEARCHPATH).invoke(varargsOf(name, path));
// Did we get a result?
if (!v.isstring(1))
return v.arg(2).tostring();
LuaString filename = v.arg1().strvalue();
// Try to load the file.
v = globals.loadfile(filename.tojstring());
if (v.arg1().isfunction())
return LuaValue.varargsOf(v.arg1(), filename);
// report error
return varargsOf(NIL, valueOf("'" + filename + "': " + v.arg(2).tojstring()));
}
}
public class searchpath extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) {
String name = args.checkjstring(1);
String path = args.checkjstring(2);
String sep = args.optjstring(3, ".");
String rep = args.optjstring(4, FILE_SEP);
// check the path elements
int e = -1;
int n = path.length();
StringBuffer sb = null;
name = name.replace(sep.charAt(0), rep.charAt(0));
while ( e < n ) {
// find next template
int b = e+1;
e = path.indexOf(';', b);
if (e < 0)
e = path.length();
String template = path.substring(b, e);
// create filename
int q = template.indexOf('?');
String filename = template;
if (q >= 0) {
filename = template.substring(0, q)+name+template.substring(q+1);
}
// try opening the file
InputStream is = globals.finder.findResource(filename);
if (is != null) {
try {
is.close();
} catch (java.io.IOException ioe) {
}
return valueOf(filename);
}
// report error
if (sb == null)
sb = new StringBuffer();
sb.append("\n\t" + filename);
}
return varargsOf(NIL, valueOf(sb.toString()));
}
}
public class seeall extends OneArgFunction {
@Override
public LuaValue call(LuaValue arg) {
LuaTable mt = new LuaTable();
mt.set(LuaValue.INDEX, globals);
arg.checktable().setmetatable(mt);
return LuaValue.NONE;
}
}
public class java_searcher extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) {
String name = args.checkjstring(1);
String classname = toClassname(name);
Class c = null;
LuaValue v = null;
try {
c = Class.forName(classname);
v = (LuaValue) c.newInstance();
if (v.isfunction())
((LuaFunction) v).initupvalue1(globals);
return varargsOf(v, globals);
} catch (ClassNotFoundException cnfe) {
return valueOf("\n\tno class '" + classname + "'");
} catch (Exception e) {
return valueOf("\n\tjava load failed on '" + classname + "', " + e);
}
}
}
/** Convert lua filename to valid class name */
public static final String toClassname(String filename) {
int n = filename.length();
int j = n;
if (filename.endsWith(".lua"))
j -= 4;
for (int k = 0; k < j; k++) {
char c = filename.charAt(k);
if (!isClassnamePart(c) || c == '/' || c == '\\') {
StringBuffer sb = new StringBuffer(j);
for (int i = 0; i < j; i++) {
c = filename.charAt(i);
sb.append(isClassnamePart(c)? c: c == '/' || c == '\\'? '.': '_');
}
return sb.toString();
}
}
return n == j? filename: filename.substring(0, j);
}
private static final boolean isClassnamePart(char c) {
if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9')
return true;
switch (c) {
case '.':
case '$':
case '_':
return true;
default:
return false;
}
}
}

View File

@@ -0,0 +1,60 @@
/*******************************************************************************
* Copyright (c) 2009-2011 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.lib;
import java.io.InputStream;
import org.luaj.vm2.Globals;
/**
* Interface for opening application resource files such as scripts sources.
* <p>
* This is used by required to load files that are part of the application, and
* implemented by BaseLib for both the Jme and Jse platforms.
* <p>
* The Jme version of base lib {@link BaseLib} implements {@link Globals#finder}
* via {@link Class#getResourceAsStream(String)}, while the Jse version
* {@link org.luaj.vm2.lib.jse.JseBaseLib} implements it using
* {@link java.io.File#File(String)}.
* <p>
* The io library does not use this API for file manipulation.
* <p>
*
* @see BaseLib
* @see Globals#finder
* @see org.luaj.vm2.lib.jse.JseBaseLib
* @see org.luaj.vm2.lib.jme.JmePlatform
* @see org.luaj.vm2.lib.jse.JsePlatform
*/
public interface ResourceFinder {
/**
* Try to open a file, or return null if not found.
*
* @see org.luaj.vm2.lib.BaseLib
* @see org.luaj.vm2.lib.jse.JseBaseLib
*
* @param filename
* @return InputStream, or null if not found.
*/
InputStream findResource(String filename);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,187 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.lib;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LibFunction} which implements the lua standard
* {@code table} library.
*
* <p>
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println(globals.get("table").get("length").call(LuaValue.tableOf()));
* }
* </pre>
* <p>
* To instantiate and use it directly, link it into your globals table via
* {@link LuaValue#load(LuaValue)} using code such as:
*
* <pre>
* {
* &#64;code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* globals.load(new TableLib());
* System.out.println(globals.get("table").get("length").call(LuaValue.tableOf()));
* }
* </pre>
* <p>
* This has been implemented to match as closely as possible the behavior in the
* corresponding library in C.
*
* @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.5">Lua 5.2 Table
* Lib Reference</a>
*/
public class TableLib extends TwoArgFunction {
/**
* Perform one-time initialization on the library by creating a table
* containing the library functions, adding that table to the supplied
* environment, adding the table to package.loaded, and returning table as
* the return value.
*
* @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, typically a Globals
* instance.
*/
@Override
public LuaValue call(LuaValue modname, LuaValue env) {
LuaTable table = new LuaTable();
table.set("concat", new concat());
table.set("insert", new insert());
table.set("pack", new pack());
table.set("remove", new remove());
table.set("sort", new sort());
table.set("unpack", new unpack());
env.set("table", table);
if (!env.get("package").isnil())
env.get("package").get("loaded").set("table", table);
return NIL;
}
// "concat" (table [, sep [, i [, j]]]) -> string
static class concat extends TableLibFunction {
@Override
public LuaValue call(LuaValue list) {
return list.checktable().concat(EMPTYSTRING, 1, list.length());
}
@Override
public LuaValue call(LuaValue list, LuaValue sep) {
return list.checktable().concat(sep.checkstring(), 1, list.length());
}
@Override
public LuaValue call(LuaValue list, LuaValue sep, LuaValue i) {
return list.checktable().concat(sep.checkstring(), i.checkint(), list.length());
}
@Override
public LuaValue call(LuaValue list, LuaValue sep, LuaValue i, LuaValue j) {
return list.checktable().concat(sep.checkstring(), i.checkint(), j.checkint());
}
}
// "insert" (table, [pos,] value)
static class insert extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) {
switch (args.narg()) {
case 2: {
LuaTable table = args.checktable(1);
table.insert(table.length()+1, args.arg(2));
return NONE;
}
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)");
}
}
}
}
// "pack" (...) -> table
static class pack extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) {
LuaValue t = tableOf(args, 1);
t.set("n", args.narg());
return t;
}
}
// "remove" (table [, pos]) -> removed-ele
static class remove extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) {
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 {
@Override
public Varargs invoke(Varargs args) {
args.checktable(1).sort(args.isnil(2)? NIL: args.checkfunction(2));
return NONE;
}
}
// "unpack", // (list [,i [,j]]) -> result1, ...
static class unpack extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) {
LuaTable t = args.checktable(1);
// 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));
}
}
}

View File

@@ -0,0 +1,10 @@
package org.luaj.vm2.lib;
import org.luaj.vm2.LuaValue;
class TableLibFunction extends LibFunction {
@Override
public LuaValue call() {
return argerror(1, "table expected, got no value");
}
}

View File

@@ -0,0 +1,81 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.lib;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Abstract base class for Java function implementations that take two arguments
* and return one value.
* <p>
* Subclasses need only implement
* {@link LuaValue#call(LuaValue,LuaValue,LuaValue)} to complete this class,
* simplifying development. All other uses of {@link #call()},
* {@link #invoke(Varargs)},etc, are routed through this method by this class,
* dropping or extending arguments with {@code nil} values as required.
* <p>
* If more or less than three arguments are required, or variable argument or
* variable return values, then use one of the related function
* {@link ZeroArgFunction}, {@link OneArgFunction}, {@link TwoArgFunction}, or
* {@link VarArgFunction}.
* <p>
* See {@link LibFunction} for more information on implementation libraries and
* library functions.
*
* @see #call(LuaValue,LuaValue,LuaValue)
* @see LibFunction
* @see ZeroArgFunction
* @see OneArgFunction
* @see TwoArgFunction
* @see VarArgFunction
*/
abstract public class ThreeArgFunction extends LibFunction {
@Override
abstract public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3);
/** Default constructor */
public ThreeArgFunction() {
}
@Override
public final LuaValue call() {
return call(NIL, NIL, NIL);
}
@Override
public final LuaValue call(LuaValue arg) {
return call(arg, NIL, NIL);
}
@Override
public LuaValue call(LuaValue arg1, LuaValue arg2) {
return call(arg1, arg2, NIL);
}
@Override
public Varargs invoke(Varargs varargs) {
return call(varargs.arg1(), varargs.arg(2), varargs.arg(3));
}
}

View File

@@ -0,0 +1,81 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.lib;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Abstract base class for Java function implementations that take two arguments
* and return one value.
* <p>
* Subclasses need only implement {@link LuaValue#call(LuaValue,LuaValue)} to
* complete this class, simplifying development. All other uses of
* {@link #call()}, {@link #invoke(Varargs)},etc, are routed through this method
* by this class, dropping or extending arguments with {@code nil} values as
* required.
* <p>
* If more or less than two arguments are required, or variable argument or
* variable return values, then use one of the related function
* {@link ZeroArgFunction}, {@link OneArgFunction}, {@link ThreeArgFunction}, or
* {@link VarArgFunction}.
* <p>
* See {@link LibFunction} for more information on implementation libraries and
* library functions.
*
* @see #call(LuaValue,LuaValue)
* @see LibFunction
* @see ZeroArgFunction
* @see OneArgFunction
* @see ThreeArgFunction
* @see VarArgFunction
*/
abstract public class TwoArgFunction extends LibFunction {
@Override
abstract public LuaValue call(LuaValue arg1, LuaValue arg2);
/** Default constructor */
public TwoArgFunction() {
}
@Override
public final LuaValue call() {
return call(NIL, NIL);
}
@Override
public final LuaValue call(LuaValue arg) {
return call(arg, NIL);
}
@Override
public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
return call(arg1, arg2);
}
@Override
public Varargs invoke(Varargs varargs) {
return call(varargs.arg1(), varargs.arg(2));
}
}

View File

@@ -0,0 +1,92 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.lib;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Abstract base class for Java function implementations that takes varaiable
* arguments and returns multiple return values.
* <p>
* Subclasses need only implement {@link LuaValue#invoke(Varargs)} to complete
* this class, simplifying development. All other uses of
* {@link #call(LuaValue)}, {@link #invoke()},etc, are routed through this
* method by this class, converting arguments to {@link Varargs} and dropping or
* extending return values with {@code nil} values as required.
* <p>
* If between one and three arguments are required, and only one return value is
* returned, {@link ZeroArgFunction}, {@link OneArgFunction},
* {@link TwoArgFunction}, or {@link ThreeArgFunction}.
* <p>
* See {@link LibFunction} for more information on implementation libraries and
* library functions.
*
* @see #invoke(Varargs)
* @see LibFunction
* @see ZeroArgFunction
* @see OneArgFunction
* @see TwoArgFunction
* @see ThreeArgFunction
*/
abstract public class VarArgFunction extends LibFunction {
public VarArgFunction() {
}
@Override
public LuaValue call() {
return invoke(NONE).arg1();
}
@Override
public LuaValue call(LuaValue arg) {
return invoke(arg).arg1();
}
@Override
public LuaValue call(LuaValue arg1, LuaValue arg2) {
return invoke(varargsOf(arg1, arg2)).arg1();
}
@Override
public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
return invoke(varargsOf(arg1, arg2, arg3)).arg1();
}
/**
* Subclass responsibility. May not have expected behavior for tail calls.
* Should not be used if: - function has a possibility of returning a
* TailcallVarargs
*
* @param args the arguments to the function call.
*/
@Override
public Varargs invoke(Varargs args) {
return onInvoke(args).eval();
}
@Override
public Varargs onInvoke(Varargs args) {
return invoke(args);
}
}

View File

@@ -0,0 +1,77 @@
/*******************************************************************************
* Copyright (c) 2009-2011 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.lib;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Abstract base class for Java function implementations that take no arguments
* and return one value.
* <p>
* Subclasses need only implement {@link LuaValue#call()} to complete this
* class, simplifying development. All other uses of {@link #call(LuaValue)},
* {@link #invoke(Varargs)},etc, are routed through this method by this class.
* <p>
* If one or more arguments are required, or variable argument or variable
* return values, then use one of the related function {@link OneArgFunction},
* {@link TwoArgFunction}, {@link ThreeArgFunction}, or {@link VarArgFunction}.
* <p>
* See {@link LibFunction} for more information on implementation libraries and
* library functions.
*
* @see #call()
* @see LibFunction
* @see OneArgFunction
* @see TwoArgFunction
* @see ThreeArgFunction
* @see VarArgFunction
*/
abstract public class ZeroArgFunction extends LibFunction {
@Override
abstract public LuaValue call();
/** Default constructor */
public ZeroArgFunction() {
}
@Override
public LuaValue call(LuaValue arg) {
return call();
}
@Override
public LuaValue call(LuaValue arg1, LuaValue arg2) {
return call();
}
@Override
public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
return call();
}
@Override
public Varargs invoke(Varargs varargs) {
return call();
}
}

View File

View File

@@ -0,0 +1,115 @@
/*******************************************************************************
* Copyright (c) 2014 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.ByteArrayInputStream;
import org.junit.jupiter.api.Test;
import org.luaj.vm2.Globals.BufferedStream;
class BufferedStreamTest {
private BufferedStream NewBufferedStream(int buflen, String contents) {
return new BufferedStream(buflen, new ByteArrayInputStream(contents.getBytes()));
}
@Test
void testReadEmptyStream() throws java.io.IOException {
BufferedStream bs = NewBufferedStream(4, "");
assertEquals(-1, bs.read());
assertEquals(-1, bs.read(new byte[10]));
assertEquals(-1, bs.read(new byte[10], 0, 10));
}
@Test
void testReadByte() throws java.io.IOException {
BufferedStream bs = NewBufferedStream(2, "abc");
assertEquals('a', bs.read());
assertEquals('b', bs.read());
assertEquals('c', bs.read());
assertEquals(-1, bs.read());
}
@Test
void testReadByteArray() throws java.io.IOException {
byte[] array = new byte[3];
BufferedStream bs = NewBufferedStream(4, "abcdef");
assertEquals(3, bs.read(array));
assertEquals("abc", new String(array));
assertEquals(1, bs.read(array));
assertEquals("d", new String(array, 0, 1));
assertEquals(2, bs.read(array));
assertEquals("ef", new String(array, 0, 2));
assertEquals(-1, bs.read());
}
@Test
void testReadByteArrayOffsetLength() throws java.io.IOException {
byte[] array = new byte[10];
BufferedStream bs = NewBufferedStream(8, "abcdefghijklmn");
assertEquals(4, bs.read(array, 0, 4));
assertEquals("abcd", new String(array, 0, 4));
assertEquals(4, bs.read(array, 2, 8));
assertEquals("efgh", new String(array, 2, 4));
assertEquals(6, bs.read(array, 0, 10));
assertEquals("ijklmn", new String(array, 0, 6));
assertEquals(-1, bs.read());
}
@Test
void testMarkOffsetBeginningOfStream() throws java.io.IOException {
byte[] array = new byte[4];
BufferedStream bs = NewBufferedStream(8, "abcdefghijkl");
assertEquals(true, bs.markSupported());
bs.mark(4);
assertEquals(4, bs.read(array));
assertEquals("abcd", new String(array));
bs.reset();
assertEquals(4, bs.read(array));
assertEquals("abcd", new String(array));
assertEquals(4, bs.read(array));
assertEquals("efgh", new String(array));
assertEquals(4, bs.read(array));
assertEquals("ijkl", new String(array));
assertEquals(-1, bs.read());
}
@Test
void testMarkOffsetMiddleOfStream() throws java.io.IOException {
byte[] array = new byte[4];
BufferedStream bs = NewBufferedStream(8, "abcdefghijkl");
assertEquals(true, bs.markSupported());
assertEquals(4, bs.read(array));
assertEquals("abcd", new String(array));
bs.mark(4);
assertEquals(4, bs.read(array));
assertEquals("efgh", new String(array));
bs.reset();
assertEquals(4, bs.read(array));
assertEquals("efgh", new String(array));
assertEquals(4, bs.read(array));
assertEquals("ijkl", new String(array));
assertEquals(-1, bs.read());
}
}

View File

@@ -0,0 +1,135 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import java.lang.reflect.InvocationTargetException;
import org.junit.jupiter.api.Test;
import org.luaj.vm2.TypeTest.MyData;
import org.luaj.vm2.lib.ZeroArgFunction;
class LuaOperationsTest {
private final int sampleint = 77;
private final long samplelong = 123400000000L;
private final double sampledouble = 55.25;
private final String samplestringstring = "abcdef";
private final String samplestringint = String.valueOf(sampleint);
private final String samplestringlong = String.valueOf(samplelong);
private final String samplestringdouble = String.valueOf(sampledouble);
private final Object sampleobject = new Object();
private final MyData sampledata = new MyData();
private final LuaValue somenil = LuaValue.NIL;
private final LuaValue sometrue = LuaValue.TRUE;
private final LuaValue somefalse = LuaValue.FALSE;
private final LuaValue zero = LuaValue.ZERO;
private final LuaValue intint = LuaValue.valueOf(sampleint);
private final LuaValue longdouble = LuaValue.valueOf(samplelong);
private final LuaValue doubledouble = LuaValue.valueOf(sampledouble);
private final LuaValue stringstring = LuaValue.valueOf(samplestringstring);
private final LuaValue stringint = LuaValue.valueOf(samplestringint);
private final LuaValue stringlong = LuaValue.valueOf(samplestringlong);
private final LuaValue stringdouble = LuaValue.valueOf(samplestringdouble);
private final LuaTable table = LuaValue
.listOf(new LuaValue[] { LuaValue.valueOf("aaa"), LuaValue.valueOf("bbb") });
private final LuaValue somefunc = new ZeroArgFunction() {
@Override
public LuaValue call() { return NONE; }
};
private final LuaThread thread = new LuaThread(new Globals(), somefunc);
private final Prototype proto = new Prototype(1);
private final LuaClosure someclosure = new LuaClosure(proto, table);
private final LuaUserdata userdataobj = LuaValue.userdataOf(sampleobject);
private final LuaUserdata userdatacls = LuaValue.userdataOf(sampledata);
private void throwsLuaError(String methodName, Object obj) {
try {
LuaValue.class.getMethod(methodName).invoke(obj);
fail("failed to throw LuaError as required");
} catch (InvocationTargetException e) {
if (!(e.getTargetException() instanceof LuaError))
fail("not a LuaError: " + e.getTargetException());
return; // pass
} catch (Exception e) {
fail("bad exception: " + e);
}
}
private void throwsLuaError(String methodName, Object obj, Object arg) {
try {
LuaValue.class.getMethod(methodName, LuaValue.class).invoke(obj, arg);
fail("failed to throw LuaError as required");
} catch (InvocationTargetException e) {
if (!(e.getTargetException() instanceof LuaError))
fail("not a LuaError: " + e.getTargetException());
return; // pass
} catch (Exception e) {
fail("bad exception: " + e);
}
}
@Test
void testLen() {
throwsLuaError("len", somenil);
throwsLuaError("len", sometrue);
throwsLuaError("len", somefalse);
throwsLuaError("len", zero);
throwsLuaError("len", intint);
throwsLuaError("len", longdouble);
throwsLuaError("len", doubledouble);
assertEquals(LuaInteger.valueOf(samplestringstring.length()), stringstring.len());
assertEquals(LuaInteger.valueOf(samplestringint.length()), stringint.len());
assertEquals(LuaInteger.valueOf(samplestringlong.length()), stringlong.len());
assertEquals(LuaInteger.valueOf(samplestringdouble.length()), stringdouble.len());
assertEquals(LuaInteger.valueOf(2), table.len());
throwsLuaError("len", somefunc);
throwsLuaError("len", thread);
throwsLuaError("len", someclosure);
throwsLuaError("len", userdataobj);
throwsLuaError("len", userdatacls);
}
@Test
void testLength() {
throwsLuaError("length", somenil);
throwsLuaError("length", sometrue);
throwsLuaError("length", somefalse);
throwsLuaError("length", zero);
throwsLuaError("length", intint);
throwsLuaError("length", longdouble);
throwsLuaError("length", doubledouble);
assertEquals(samplestringstring.length(), stringstring.length());
assertEquals(samplestringint.length(), stringint.length());
assertEquals(samplestringlong.length(), stringlong.length());
assertEquals(samplestringdouble.length(), stringdouble.length());
assertEquals(2, table.length());
throwsLuaError("length", somefunc);
throwsLuaError("length", thread);
throwsLuaError("length", someclosure);
throwsLuaError("length", userdataobj);
throwsLuaError("length", userdatacls);
}
}

View File

@@ -0,0 +1,372 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.luaj.vm2.TypeTest.MyData;
import org.luaj.vm2.lib.StringLib;
import org.luaj.vm2.lib.ThreeArgFunction;
import org.luaj.vm2.lib.TwoArgFunction;
import org.luaj.vm2.lib.ZeroArgFunction;
class MetatableTest {
private final String samplestring = "abcdef";
private final Object sampleobject = new Object();
private final MyData sampledata = new MyData();
private final LuaValue string = LuaValue.valueOf(samplestring);
private final LuaTable table = LuaValue.tableOf();
private final LuaFunction function = new ZeroArgFunction() {
@Override
public LuaValue call() { return NONE; }
};
private final LuaThread thread = new LuaThread(new Globals(), function);
private final LuaClosure closure = new LuaClosure(new Prototype(), new LuaTable());
private final LuaUserdata userdata = LuaValue.userdataOf(sampleobject);
private final LuaUserdata userdatamt = LuaValue.userdataOf(sampledata, table);
@BeforeEach
protected void setUp() throws Exception {
// needed for metatable ops to work on strings
new StringLib();
}
@AfterEach
protected void tearDown() throws Exception {
LuaBoolean.s_metatable = null;
LuaFunction.s_metatable = null;
LuaNil.s_metatable = null;
LuaNumber.s_metatable = null;
// LuaString.s_metatable = null;
LuaThread.s_metatable = null;
}
@Test
void testGetMetatable() {
assertEquals(null, LuaValue.NIL.getmetatable());
assertEquals(null, LuaValue.TRUE.getmetatable());
assertEquals(null, LuaValue.ONE.getmetatable());
// assertEquals( null, string.getmetatable() );
assertEquals(null, table.getmetatable());
assertEquals(null, function.getmetatable());
assertEquals(null, thread.getmetatable());
assertEquals(null, closure.getmetatable());
assertEquals(null, userdata.getmetatable());
assertEquals(table, userdatamt.getmetatable());
}
@Test
void testSetMetatable() {
LuaValue mt = LuaValue.tableOf();
assertEquals(null, table.getmetatable());
assertEquals(null, userdata.getmetatable());
assertEquals(table, userdatamt.getmetatable());
assertEquals(table, table.setmetatable(mt));
assertEquals(userdata, userdata.setmetatable(mt));
assertEquals(userdatamt, userdatamt.setmetatable(mt));
assertEquals(mt, table.getmetatable());
assertEquals(mt, userdata.getmetatable());
assertEquals(mt, userdatamt.getmetatable());
// these all get metatable behind-the-scenes
assertEquals(null, LuaValue.NIL.getmetatable());
assertEquals(null, LuaValue.TRUE.getmetatable());
assertEquals(null, LuaValue.ONE.getmetatable());
// assertEquals( null, string.getmetatable() );
assertEquals(null, function.getmetatable());
assertEquals(null, thread.getmetatable());
assertEquals(null, closure.getmetatable());
LuaNil.s_metatable = mt;
assertEquals(mt, LuaValue.NIL.getmetatable());
assertEquals(null, LuaValue.TRUE.getmetatable());
assertEquals(null, LuaValue.ONE.getmetatable());
// assertEquals( null, string.getmetatable() );
assertEquals(null, function.getmetatable());
assertEquals(null, thread.getmetatable());
assertEquals(null, closure.getmetatable());
LuaBoolean.s_metatable = mt;
assertEquals(mt, LuaValue.TRUE.getmetatable());
assertEquals(null, LuaValue.ONE.getmetatable());
// assertEquals( null, string.getmetatable() );
assertEquals(null, function.getmetatable());
assertEquals(null, thread.getmetatable());
assertEquals(null, closure.getmetatable());
LuaNumber.s_metatable = mt;
assertEquals(mt, LuaValue.ONE.getmetatable());
assertEquals(mt, LuaValue.valueOf(1.25).getmetatable());
// assertEquals( null, string.getmetatable() );
assertEquals(null, function.getmetatable());
assertEquals(null, thread.getmetatable());
assertEquals(null, closure.getmetatable());
// LuaString.s_metatable = mt;
// assertEquals( mt, string.getmetatable() );
assertEquals(null, function.getmetatable());
assertEquals(null, thread.getmetatable());
assertEquals(null, closure.getmetatable());
LuaFunction.s_metatable = mt;
assertEquals(mt, function.getmetatable());
assertEquals(null, thread.getmetatable());
LuaThread.s_metatable = mt;
assertEquals(mt, thread.getmetatable());
}
@Test
void testMetatableIndex() {
assertEquals(table, table.setmetatable(null));
assertEquals(userdata, userdata.setmetatable(null));
assertEquals(userdatamt, userdatamt.setmetatable(null));
assertEquals(LuaValue.NIL, table.get(1));
assertEquals(LuaValue.NIL, userdata.get(1));
assertEquals(LuaValue.NIL, userdatamt.get(1));
// empty metatable
LuaValue mt = LuaValue.tableOf();
assertEquals(table, table.setmetatable(mt));
assertEquals(userdata, userdata.setmetatable(mt));
LuaBoolean.s_metatable = mt;
LuaFunction.s_metatable = mt;
LuaNil.s_metatable = mt;
LuaNumber.s_metatable = mt;
// LuaString.s_metatable = mt;
LuaThread.s_metatable = mt;
assertEquals(mt, table.getmetatable());
assertEquals(mt, userdata.getmetatable());
assertEquals(mt, LuaValue.NIL.getmetatable());
assertEquals(mt, LuaValue.TRUE.getmetatable());
assertEquals(mt, LuaValue.ONE.getmetatable());
// assertEquals( StringLib.instance, string.getmetatable() );
assertEquals(mt, function.getmetatable());
assertEquals(mt, thread.getmetatable());
// plain metatable
LuaValue abc = LuaValue.valueOf("abc");
mt.set(LuaValue.INDEX, LuaValue.listOf(new LuaValue[] { abc }));
assertEquals(abc, table.get(1));
assertEquals(abc, userdata.get(1));
assertEquals(abc, LuaValue.NIL.get(1));
assertEquals(abc, LuaValue.TRUE.get(1));
assertEquals(abc, LuaValue.ONE.get(1));
// assertEquals( abc, string.get(1) );
assertEquals(abc, function.get(1));
assertEquals(abc, thread.get(1));
// plain metatable
mt.set(LuaValue.INDEX, new TwoArgFunction() {
@Override
public LuaValue call(LuaValue arg1, LuaValue arg2) {
return LuaValue.valueOf(arg1.typename() + "[" + arg2.tojstring() + "]=xyz");
}
});
assertEquals("table[1]=xyz", table.get(1).tojstring());
assertEquals("userdata[1]=xyz", userdata.get(1).tojstring());
assertEquals("nil[1]=xyz", LuaValue.NIL.get(1).tojstring());
assertEquals("boolean[1]=xyz", LuaValue.TRUE.get(1).tojstring());
assertEquals("number[1]=xyz", LuaValue.ONE.get(1).tojstring());
// assertEquals( "string[1]=xyz", string.get(1).tojstring() );
assertEquals("function[1]=xyz", function.get(1).tojstring());
assertEquals("thread[1]=xyz", thread.get(1).tojstring());
}
@Test
void testMetatableNewIndex() {
// empty metatable
LuaValue mt = LuaValue.tableOf();
assertEquals(table, table.setmetatable(mt));
assertEquals(userdata, userdata.setmetatable(mt));
LuaBoolean.s_metatable = mt;
LuaFunction.s_metatable = mt;
LuaNil.s_metatable = mt;
LuaNumber.s_metatable = mt;
// LuaString.s_metatable = mt;
LuaThread.s_metatable = mt;
// plain metatable
final LuaValue fallback = LuaValue.tableOf();
LuaValue abc = LuaValue.valueOf("abc");
mt.set(LuaValue.NEWINDEX, fallback);
table.set(2, abc);
userdata.set(3, abc);
LuaValue.NIL.set(4, abc);
LuaValue.TRUE.set(5, abc);
LuaValue.ONE.set(6, abc);
// string.set(7,abc);
function.set(8, abc);
thread.set(9, abc);
assertEquals(abc, fallback.get(2));
assertEquals(abc, fallback.get(3));
assertEquals(abc, fallback.get(4));
assertEquals(abc, fallback.get(5));
assertEquals(abc, fallback.get(6));
// assertEquals( abc, StringLib.instance.get(7) );
assertEquals(abc, fallback.get(8));
assertEquals(abc, fallback.get(9));
// metatable with function call
mt.set(LuaValue.NEWINDEX, new ThreeArgFunction() {
@Override
public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
fallback.rawset(arg2, LuaValue.valueOf("via-func-" + arg3));
return NONE;
}
});
table.set(12, abc);
userdata.set(13, abc);
LuaValue.NIL.set(14, abc);
LuaValue.TRUE.set(15, abc);
LuaValue.ONE.set(16, abc);
// string.set(17,abc);
function.set(18, abc);
thread.set(19, abc);
LuaValue via = LuaValue.valueOf("via-func-abc");
assertEquals(via, fallback.get(12));
assertEquals(via, fallback.get(13));
assertEquals(via, fallback.get(14));
assertEquals(via, fallback.get(15));
assertEquals(via, fallback.get(16));
// assertEquals( via, StringLib.instance.get(17) );
assertEquals(via, fallback.get(18));
assertEquals(via, fallback.get(19));
}
private void checkTable(LuaValue t, LuaValue aa, LuaValue bb, LuaValue cc, LuaValue dd, LuaValue ee, LuaValue ff,
LuaValue gg, LuaValue ra, LuaValue rb, LuaValue rc, LuaValue rd, LuaValue re, LuaValue rf, LuaValue rg) {
assertEquals(aa, t.get("aa"));
assertEquals(bb, t.get("bb"));
assertEquals(cc, t.get("cc"));
assertEquals(dd, t.get("dd"));
assertEquals(ee, t.get("ee"));
assertEquals(ff, t.get("ff"));
assertEquals(gg, t.get("gg"));
assertEquals(ra, t.rawget("aa"));
assertEquals(rb, t.rawget("bb"));
assertEquals(rc, t.rawget("cc"));
assertEquals(rd, t.rawget("dd"));
assertEquals(re, t.rawget("ee"));
assertEquals(rf, t.rawget("ff"));
assertEquals(rg, t.rawget("gg"));
}
private LuaValue makeTable(String key1, String val1, String key2, String val2) {
return LuaValue.tableOf(new LuaValue[] { LuaValue.valueOf(key1), LuaValue.valueOf(val1), LuaValue.valueOf(key2),
LuaValue.valueOf(val2), });
}
@Test
void testRawsetMetatableSet() {
// set up tables
LuaValue m = makeTable("aa", "aaa", "bb", "bbb");
m.set(LuaValue.INDEX, m);
m.set(LuaValue.NEWINDEX, m);
LuaValue s = makeTable("cc", "ccc", "dd", "ddd");
LuaValue t = makeTable("cc", "ccc", "dd", "ddd");
t.setmetatable(m);
LuaValue aaa = LuaValue.valueOf("aaa");
LuaValue bbb = LuaValue.valueOf("bbb");
LuaValue ccc = LuaValue.valueOf("ccc");
LuaValue ddd = LuaValue.valueOf("ddd");
LuaValue ppp = LuaValue.valueOf("ppp");
LuaValue qqq = LuaValue.valueOf("qqq");
LuaValue rrr = LuaValue.valueOf("rrr");
LuaValue sss = LuaValue.valueOf("sss");
LuaValue ttt = LuaValue.valueOf("ttt");
LuaValue www = LuaValue.valueOf("www");
LuaValue xxx = LuaValue.valueOf("xxx");
LuaValue yyy = LuaValue.valueOf("yyy");
LuaValue zzz = LuaValue.valueOf("zzz");
LuaValue nil = LuaValue.NIL;
// check initial values
// values via "bet()" values via "rawget()"
checkTable(s, nil, nil, ccc, ddd, nil, nil, nil, nil, nil, ccc, ddd, nil, nil, nil);
checkTable(t, aaa, bbb, ccc, ddd, nil, nil, nil, nil, nil, ccc, ddd, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, nil, nil, aaa, bbb, nil, nil, nil, nil, nil);
// rawset()
s.rawset("aa", www);
checkTable(s, www, nil, ccc, ddd, nil, nil, nil, www, nil, ccc, ddd, nil, nil, nil);
checkTable(t, aaa, bbb, ccc, ddd, nil, nil, nil, nil, nil, ccc, ddd, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, nil, nil, aaa, bbb, nil, nil, nil, nil, nil);
s.rawset("cc", xxx);
checkTable(s, www, nil, xxx, ddd, nil, nil, nil, www, nil, xxx, ddd, nil, nil, nil);
checkTable(t, aaa, bbb, ccc, ddd, nil, nil, nil, nil, nil, ccc, ddd, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, nil, nil, aaa, bbb, nil, nil, nil, nil, nil);
t.rawset("bb", yyy);
checkTable(s, www, nil, xxx, ddd, nil, nil, nil, www, nil, xxx, ddd, nil, nil, nil);
checkTable(t, aaa, yyy, ccc, ddd, nil, nil, nil, nil, yyy, ccc, ddd, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, nil, nil, aaa, bbb, nil, nil, nil, nil, nil);
t.rawset("dd", zzz);
checkTable(s, www, nil, xxx, ddd, nil, nil, nil, www, nil, xxx, ddd, nil, nil, nil);
checkTable(t, aaa, yyy, ccc, zzz, nil, nil, nil, nil, yyy, ccc, zzz, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, nil, nil, aaa, bbb, nil, nil, nil, nil, nil);
// set() invoking metatables
s.set("ee", ppp);
checkTable(s, www, nil, xxx, ddd, ppp, nil, nil, www, nil, xxx, ddd, ppp, nil, nil);
checkTable(t, aaa, yyy, ccc, zzz, nil, nil, nil, nil, yyy, ccc, zzz, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, nil, nil, aaa, bbb, nil, nil, nil, nil, nil);
s.set("cc", qqq);
checkTable(s, www, nil, qqq, ddd, ppp, nil, nil, www, nil, qqq, ddd, ppp, nil, nil);
checkTable(t, aaa, yyy, ccc, zzz, nil, nil, nil, nil, yyy, ccc, zzz, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, nil, nil, aaa, bbb, nil, nil, nil, nil, nil);
t.set("ff", rrr);
checkTable(s, www, nil, qqq, ddd, ppp, nil, nil, www, nil, qqq, ddd, ppp, nil, nil);
checkTable(t, aaa, yyy, ccc, zzz, nil, rrr, nil, nil, yyy, ccc, zzz, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, rrr, nil, aaa, bbb, nil, nil, nil, rrr, nil);
t.set("dd", sss);
checkTable(s, www, nil, qqq, ddd, ppp, nil, nil, www, nil, qqq, ddd, ppp, nil, nil);
checkTable(t, aaa, yyy, ccc, sss, nil, rrr, nil, nil, yyy, ccc, sss, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, rrr, nil, aaa, bbb, nil, nil, nil, rrr, nil);
m.set("gg", ttt);
checkTable(s, www, nil, qqq, ddd, ppp, nil, nil, www, nil, qqq, ddd, ppp, nil, nil);
checkTable(t, aaa, yyy, ccc, sss, nil, rrr, ttt, nil, yyy, ccc, sss, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, rrr, ttt, aaa, bbb, nil, nil, nil, rrr, ttt);
// make s fall back to t
s.setmetatable(LuaValue.tableOf(new LuaValue[] { LuaValue.INDEX, t, LuaValue.NEWINDEX, t }));
checkTable(s, www, yyy, qqq, ddd, ppp, rrr, ttt, www, nil, qqq, ddd, ppp, nil, nil);
checkTable(t, aaa, yyy, ccc, sss, nil, rrr, ttt, nil, yyy, ccc, sss, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, rrr, ttt, aaa, bbb, nil, nil, nil, rrr, ttt);
s.set("aa", www);
checkTable(s, www, yyy, qqq, ddd, ppp, rrr, ttt, www, nil, qqq, ddd, ppp, nil, nil);
checkTable(t, aaa, yyy, ccc, sss, nil, rrr, ttt, nil, yyy, ccc, sss, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, rrr, ttt, aaa, bbb, nil, nil, nil, rrr, ttt);
s.set("bb", zzz);
checkTable(s, www, zzz, qqq, ddd, ppp, rrr, ttt, www, nil, qqq, ddd, ppp, nil, nil);
checkTable(t, aaa, zzz, ccc, sss, nil, rrr, ttt, nil, zzz, ccc, sss, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, rrr, ttt, aaa, bbb, nil, nil, nil, rrr, ttt);
s.set("ee", xxx);
checkTable(s, www, zzz, qqq, ddd, xxx, rrr, ttt, www, nil, qqq, ddd, xxx, nil, nil);
checkTable(t, aaa, zzz, ccc, sss, nil, rrr, ttt, nil, zzz, ccc, sss, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, rrr, ttt, aaa, bbb, nil, nil, nil, rrr, ttt);
s.set("ff", yyy);
checkTable(s, www, zzz, qqq, ddd, xxx, yyy, ttt, www, nil, qqq, ddd, xxx, nil, nil);
checkTable(t, aaa, zzz, ccc, sss, nil, yyy, ttt, nil, zzz, ccc, sss, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, yyy, ttt, aaa, bbb, nil, nil, nil, yyy, ttt);
}
}

View File

@@ -0,0 +1,368 @@
package org.luaj.vm2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import org.junit.jupiter.api.Test;
class StringTest {
@Test
void testToInputStream() throws IOException {
LuaString str = LuaString.valueOf("Hello");
InputStream is = str.toInputStream();
assertEquals('H', is.read());
assertEquals('e', is.read());
assertEquals(2, is.skip(2));
assertEquals('o', is.read());
assertEquals(-1, is.read());
assertTrue(is.markSupported());
is.reset();
assertEquals('H', is.read());
is.mark(4);
assertEquals('e', is.read());
is.reset();
assertEquals('e', is.read());
LuaString substr = str.substring(1, 4);
assertEquals(3, substr.length());
is.close();
is = substr.toInputStream();
assertEquals('e', is.read());
assertEquals('l', is.read());
assertEquals('l', is.read());
assertEquals(-1, is.read());
is = substr.toInputStream();
is.reset();
assertEquals('e', is.read());
}
private static final String userFriendly(String s) {
StringBuffer sb = new StringBuffer();
for (int i = 0, n = s.length(); i < n; i++) {
int c = s.charAt(i);
if (c < ' ' || c >= 0x80) {
sb.append("\\u" + Integer.toHexString(0x10000+c).substring(1));
} else {
sb.append((char) c);
}
}
return sb.toString();
}
@Test
void testUtf820482051() throws UnsupportedEncodingException {
int i = 2048;
char[] c = { (char) (i+0), (char) (i+1), (char) (i+2), (char) (i+3) };
String before = new String(c) + " " + i + "-" + (i+4);
LuaString ls = LuaString.valueOf(before);
String after = ls.tojstring();
assertEquals(userFriendly(before), userFriendly(after));
}
@Test
void testUtf8() {
for (int i = 4; i < 0xffff; i += 4) {
char[] c = { (char) (i+0), (char) (i+1), (char) (i+2), (char) (i+3) };
String before = new String(c) + " " + i + "-" + (i+4);
LuaString ls = LuaString.valueOf(before);
String after = ls.tojstring();
assertEquals(userFriendly(before), userFriendly(after));
}
char[] c = { (char) (1), (char) (2), (char) (3) };
String before = new String(c) + " 1-3";
LuaString ls = LuaString.valueOf(before);
String after = ls.tojstring();
assertEquals(userFriendly(before), userFriendly(after));
}
@Test
void testSpotCheckUtf8() throws UnsupportedEncodingException {
byte[] bytes = { (byte) 194, (byte) 160, (byte) 194, (byte) 161, (byte) 194, (byte) 162, (byte) 194, (byte) 163,
(byte) 194, (byte) 164 };
String expected = new String(bytes, "UTF8");
String actual = LuaString.valueOf(bytes).tojstring();
char[] d = actual.toCharArray();
assertEquals(160, d[0]);
assertEquals(161, d[1]);
assertEquals(162, d[2]);
assertEquals(163, d[3]);
assertEquals(164, d[4]);
assertEquals(expected, actual);
}
@Test
void testNullTerminated() {
char[] c = { 'a', 'b', 'c', '\0', 'd', 'e', 'f' };
String before = new String(c);
LuaString ls = LuaString.valueOf(before);
String after = ls.tojstring();
assertEquals(userFriendly("abc\0def"), userFriendly(after));
}
@Test
void testRecentStringsCacheDifferentHashcodes() {
final byte[] abc = { 'a', 'b', 'c' };
final byte[] xyz = { 'x', 'y', 'z' };
final LuaString abc1 = LuaString.valueOf(abc);
final LuaString xyz1 = LuaString.valueOf(xyz);
final LuaString abc2 = LuaString.valueOf(abc);
final LuaString xyz2 = LuaString.valueOf(xyz);
final int mod = LuaString.RECENT_STRINGS_CACHE_SIZE;
assertTrue(abc1.hashCode()%mod != xyz1.hashCode()%mod);
assertSame(abc1, abc2);
assertSame(xyz1, xyz2);
}
@Test
void testRecentStringsCacheHashCollisionCacheHit() {
final byte[] abc = { 'a', 'b', 'c' };
final byte[] lyz = { 'l', 'y', 'z' }; // chosen to have hash collision with 'abc'
final LuaString abc1 = LuaString.valueOf(abc);
final LuaString abc2 = LuaString.valueOf(abc); // in cache: 'abc'
final LuaString lyz1 = LuaString.valueOf(lyz);
final LuaString lyz2 = LuaString.valueOf(lyz); // in cache: 'lyz'
final int mod = LuaString.RECENT_STRINGS_CACHE_SIZE;
assertEquals(abc1.hashCode()%mod, lyz1.hashCode()%mod);
assertNotSame(abc1, lyz1);
assertFalse(abc1.equals(lyz1));
assertSame(abc1, abc2);
assertSame(lyz1, lyz2);
}
@Test
void testRecentStringsCacheHashCollisionCacheMiss() {
final byte[] abc = { 'a', 'b', 'c' };
final byte[] lyz = { 'l', 'y', 'z' }; // chosen to have hash collision with 'abc'
final LuaString abc1 = LuaString.valueOf(abc);
final LuaString lyz1 = LuaString.valueOf(lyz); // in cache: 'abc'
final LuaString abc2 = LuaString.valueOf(abc); // in cache: 'lyz'
final LuaString lyz2 = LuaString.valueOf(lyz); // in cache: 'abc'
final int mod = LuaString.RECENT_STRINGS_CACHE_SIZE;
assertEquals(abc1.hashCode()%mod, lyz1.hashCode()%mod);
assertNotSame(abc1, lyz1);
assertFalse(abc1.equals(lyz1));
assertNotSame(abc1, abc2);
assertNotSame(lyz1, lyz2);
}
@Test
void testRecentStringsLongStrings() {
byte[] abc = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes();
assertTrue(abc.length > LuaString.RECENT_STRINGS_MAX_LENGTH);
LuaString abc1 = LuaString.valueOf(abc);
LuaString abc2 = LuaString.valueOf(abc);
assertNotSame(abc1, abc2);
}
@Test
void testRecentStringsUsingJavaStrings() {
final String abc = "abc";
final String lyz = "lyz"; // chosen to have hash collision with 'abc'
final String xyz = "xyz";
final LuaString abc1 = LuaString.valueOf(abc);
final LuaString abc2 = LuaString.valueOf(abc);
final LuaString lyz1 = LuaString.valueOf(lyz);
final LuaString lyz2 = LuaString.valueOf(lyz);
final LuaString xyz1 = LuaString.valueOf(xyz);
final LuaString xyz2 = LuaString.valueOf(xyz);
final int mod = LuaString.RECENT_STRINGS_CACHE_SIZE;
assertEquals(abc1.hashCode()%mod, lyz1.hashCode()%mod);
assertFalse(abc1.hashCode()%mod == xyz1.hashCode()%mod);
assertSame(abc1, abc2);
assertSame(lyz1, lyz2);
assertSame(xyz1, xyz2);
final LuaString abc3 = LuaString.valueOf(abc);
final LuaString lyz3 = LuaString.valueOf(lyz);
final LuaString xyz3 = LuaString.valueOf(xyz);
final LuaString abc4 = LuaString.valueOf(abc);
final LuaString lyz4 = LuaString.valueOf(lyz);
final LuaString xyz4 = LuaString.valueOf(xyz);
assertNotSame(abc3, abc4); // because of hash collision
assertNotSame(lyz3, lyz4); // because of hash collision
assertSame(xyz3, xyz4); // because hashes do not collide
}
@Test
void testLongSubstringGetsOldBacking() {
LuaString src = LuaString.valueOf("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
LuaString sub1 = src.substring(10, 40);
assertSame(src.m_bytes, sub1.m_bytes);
assertEquals(sub1.m_offset, 10);
assertEquals(sub1.m_length, 30);
}
@Test
void testShortSubstringGetsNewBacking() {
LuaString src = LuaString.valueOf("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
LuaString sub1 = src.substring(10, 20);
LuaString sub2 = src.substring(10, 20);
assertEquals(sub1.m_offset, 0);
assertEquals(sub1.m_length, 10);
assertSame(sub1, sub2);
assertFalse(src.m_bytes == sub1.m_bytes);
}
@Test
void testShortSubstringOfVeryLongStringGetsNewBacking() {
LuaString src = LuaString.valueOf("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
LuaString sub1 = src.substring(10, 50);
LuaString sub2 = src.substring(10, 50);
assertEquals(sub1.m_offset, 0);
assertEquals(sub1.m_length, 40);
assertFalse(sub1 == sub2);
assertFalse(src.m_bytes == sub1.m_bytes);
}
@Test
void testIndexOfByteInSubstring() {
LuaString str = LuaString.valueOf("abcdef:ghi");
LuaString sub = str.substring(2, 10);
assertEquals(10, str.m_length);
assertEquals(8, sub.m_length);
assertEquals(0, str.m_offset);
assertEquals(2, sub.m_offset);
assertEquals(6, str.indexOf((byte) ':', 0));
assertEquals(6, str.indexOf((byte) ':', 2));
assertEquals(6, str.indexOf((byte) ':', 6));
assertEquals(-1, str.indexOf((byte) ':', 7));
assertEquals(-1, str.indexOf((byte) ':', 9));
assertEquals(9, str.indexOf((byte) 'i', 0));
assertEquals(9, str.indexOf((byte) 'i', 2));
assertEquals(9, str.indexOf((byte) 'i', 9));
assertEquals(-1, str.indexOf((byte) 'z', 0));
assertEquals(-1, str.indexOf((byte) 'z', 2));
assertEquals(-1, str.indexOf((byte) 'z', 9));
assertEquals(4, sub.indexOf((byte) ':', 0));
assertEquals(4, sub.indexOf((byte) ':', 2));
assertEquals(4, sub.indexOf((byte) ':', 4));
assertEquals(-1, sub.indexOf((byte) ':', 5));
assertEquals(-1, sub.indexOf((byte) ':', 7));
assertEquals(7, sub.indexOf((byte) 'i', 0));
assertEquals(7, sub.indexOf((byte) 'i', 2));
assertEquals(7, sub.indexOf((byte) 'i', 7));
assertEquals(-1, sub.indexOf((byte) 'z', 0));
assertEquals(-1, sub.indexOf((byte) 'z', 2));
assertEquals(-1, sub.indexOf((byte) 'z', 7));
}
@Test
void testIndexOfPatternInSubstring() {
LuaString str = LuaString.valueOf("abcdef:ghi");
LuaString sub = str.substring(2, 10);
assertEquals(10, str.m_length);
assertEquals(8, sub.m_length);
assertEquals(0, str.m_offset);
assertEquals(2, sub.m_offset);
LuaString pat = LuaString.valueOf(":");
LuaString i = LuaString.valueOf("i");
LuaString xyz = LuaString.valueOf("xyz");
assertEquals(6, str.indexOf(pat, 0));
assertEquals(6, str.indexOf(pat, 2));
assertEquals(6, str.indexOf(pat, 6));
assertEquals(-1, str.indexOf(pat, 7));
assertEquals(-1, str.indexOf(pat, 9));
assertEquals(9, str.indexOf(i, 0));
assertEquals(9, str.indexOf(i, 2));
assertEquals(9, str.indexOf(i, 9));
assertEquals(-1, str.indexOf(xyz, 0));
assertEquals(-1, str.indexOf(xyz, 2));
assertEquals(-1, str.indexOf(xyz, 9));
assertEquals(4, sub.indexOf(pat, 0));
assertEquals(4, sub.indexOf(pat, 2));
assertEquals(4, sub.indexOf(pat, 4));
assertEquals(-1, sub.indexOf(pat, 5));
assertEquals(-1, sub.indexOf(pat, 7));
assertEquals(7, sub.indexOf(i, 0));
assertEquals(7, sub.indexOf(i, 2));
assertEquals(7, sub.indexOf(i, 7));
assertEquals(-1, sub.indexOf(xyz, 0));
assertEquals(-1, sub.indexOf(xyz, 2));
assertEquals(-1, sub.indexOf(xyz, 7));
}
@Test
void testLastIndexOfPatternInSubstring() {
LuaString str = LuaString.valueOf("abcdef:ghi");
LuaString sub = str.substring(2, 10);
assertEquals(10, str.m_length);
assertEquals(8, sub.m_length);
assertEquals(0, str.m_offset);
assertEquals(2, sub.m_offset);
LuaString pat = LuaString.valueOf(":");
LuaString i = LuaString.valueOf("i");
LuaString xyz = LuaString.valueOf("xyz");
assertEquals(6, str.lastIndexOf(pat));
assertEquals(9, str.lastIndexOf(i));
assertEquals(-1, str.lastIndexOf(xyz));
assertEquals(4, sub.lastIndexOf(pat));
assertEquals(7, sub.lastIndexOf(i));
assertEquals(-1, sub.lastIndexOf(xyz));
}
@Test
void testIndexOfAnyInSubstring() {
LuaString str = LuaString.valueOf("abcdef:ghi");
LuaString sub = str.substring(2, 10);
assertEquals(10, str.m_length);
assertEquals(8, sub.m_length);
assertEquals(0, str.m_offset);
assertEquals(2, sub.m_offset);
LuaString ghi = LuaString.valueOf("ghi");
LuaString ihg = LuaString.valueOf("ihg");
LuaString ijk = LuaString.valueOf("ijk");
LuaString kji = LuaString.valueOf("kji");
LuaString xyz = LuaString.valueOf("xyz");
LuaString ABCdEFGHIJKL = LuaString.valueOf("ABCdEFGHIJKL");
LuaString EFGHIJKL = ABCdEFGHIJKL.substring(4, 12);
LuaString CdEFGHIJ = ABCdEFGHIJKL.substring(2, 10);
assertEquals(4, EFGHIJKL.m_offset);
assertEquals(2, CdEFGHIJ.m_offset);
assertEquals(7, str.indexOfAny(ghi));
assertEquals(7, str.indexOfAny(ihg));
assertEquals(9, str.indexOfAny(ijk));
assertEquals(9, str.indexOfAny(kji));
assertEquals(-1, str.indexOfAny(xyz));
assertEquals(3, str.indexOfAny(CdEFGHIJ));
assertEquals(-1, str.indexOfAny(EFGHIJKL));
assertEquals(5, sub.indexOfAny(ghi));
assertEquals(5, sub.indexOfAny(ihg));
assertEquals(7, sub.indexOfAny(ijk));
assertEquals(7, sub.indexOfAny(kji));
assertEquals(-1, sub.indexOfAny(xyz));
assertEquals(1, sub.indexOfAny(CdEFGHIJ));
assertEquals(-1, sub.indexOfAny(EFGHIJKL));
}
}

View File

@@ -0,0 +1,325 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import org.luaj.vm2.lib.TwoArgFunction;
/**
* Tests for tables used as lists.
*/
public class TableHashTest {
protected LuaTable new_Table() {
return new LuaTable();
}
protected LuaTable new_Table(int n, int m) {
return new LuaTable(n, m);
}
@Test
void testSetRemove() {
LuaTable t = new_Table();
assertEquals(0, t.getHashLength());
assertEquals(0, t.length());
assertEquals(0, t.keyCount());
String[] keys = { "abc", "def", "ghi", "jkl", "mno", "pqr", "stu", "wxy", "z01", "cd", "ef", "g", "hi", "jk",
"lm", "no", "pq", "rs", };
int[] capacities = { 0, 2, 2, 4, 4, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, 16, 32, 32, 32 };
for (int i = 0; i < keys.length; ++i) {
assertEquals(capacities[i], t.getHashLength());
String si = "Test Value! " + i;
t.set(keys[i], si);
assertEquals(0, t.length());
assertEquals(i+1, t.keyCount());
}
assertEquals(capacities[keys.length], t.getHashLength());
for (int i = 0; i < keys.length; ++i) {
LuaValue vi = LuaString.valueOf("Test Value! " + i);
assertEquals(vi, t.get(keys[i]));
assertEquals(vi, t.get(LuaString.valueOf(keys[i])));
assertEquals(vi, t.rawget(keys[i]));
assertEquals(vi, t.rawget(keys[i]));
}
// replace with new values
for (int i = 0; i < keys.length; ++i) {
t.set(keys[i], LuaString.valueOf("Replacement Value! " + i));
assertEquals(0, t.length());
assertEquals(keys.length, t.keyCount());
assertEquals(capacities[keys.length], t.getHashLength());
}
for (int i = 0; i < keys.length; ++i) {
LuaValue vi = LuaString.valueOf("Replacement Value! " + i);
assertEquals(vi, t.get(keys[i]));
}
// remove
for (int i = 0; i < keys.length; ++i) {
t.set(keys[i], LuaValue.NIL);
assertEquals(0, t.length());
assertEquals(keys.length-i-1, t.keyCount());
if (i < keys.length-1)
assertEquals(capacities[keys.length], t.getHashLength());
else
assertTrue(0 <= t.getHashLength());
}
for (int i = 0; i < keys.length; ++i) {
assertEquals(LuaValue.NIL, t.get(keys[i]));
}
}
@Test
void testIndexMetatag() {
LuaTable t = new_Table();
LuaTable mt = new_Table();
LuaTable fb = new_Table();
// set basic values
t.set("ppp", "abc");
t.set(123, "def");
mt.set(LuaValue.INDEX, fb);
fb.set("qqq", "ghi");
fb.set(456, "jkl");
// check before setting metatable
assertEquals("abc", t.get("ppp").tojstring());
assertEquals("def", t.get(123).tojstring());
assertEquals("nil", t.get("qqq").tojstring());
assertEquals("nil", t.get(456).tojstring());
assertEquals("nil", fb.get("ppp").tojstring());
assertEquals("nil", fb.get(123).tojstring());
assertEquals("ghi", fb.get("qqq").tojstring());
assertEquals("jkl", fb.get(456).tojstring());
assertEquals("nil", mt.get("ppp").tojstring());
assertEquals("nil", mt.get(123).tojstring());
assertEquals("nil", mt.get("qqq").tojstring());
assertEquals("nil", mt.get(456).tojstring());
// check before setting metatable
t.setmetatable(mt);
assertEquals(mt, t.getmetatable());
assertEquals("abc", t.get("ppp").tojstring());
assertEquals("def", t.get(123).tojstring());
assertEquals("ghi", t.get("qqq").tojstring());
assertEquals("jkl", t.get(456).tojstring());
assertEquals("nil", fb.get("ppp").tojstring());
assertEquals("nil", fb.get(123).tojstring());
assertEquals("ghi", fb.get("qqq").tojstring());
assertEquals("jkl", fb.get(456).tojstring());
assertEquals("nil", mt.get("ppp").tojstring());
assertEquals("nil", mt.get(123).tojstring());
assertEquals("nil", mt.get("qqq").tojstring());
assertEquals("nil", mt.get(456).tojstring());
// set metatable to metatable without values
t.setmetatable(fb);
assertEquals("abc", t.get("ppp").tojstring());
assertEquals("def", t.get(123).tojstring());
assertEquals("nil", t.get("qqq").tojstring());
assertEquals("nil", t.get(456).tojstring());
// set metatable to null
t.setmetatable(null);
assertEquals("abc", t.get("ppp").tojstring());
assertEquals("def", t.get(123).tojstring());
assertEquals("nil", t.get("qqq").tojstring());
assertEquals("nil", t.get(456).tojstring());
}
@Test
void testIndexFunction() {
final LuaTable t = new_Table();
final LuaTable mt = new_Table();
final TwoArgFunction fb = new TwoArgFunction() {
@Override
public LuaValue call(LuaValue tbl, LuaValue key) {
assertEquals(tbl, t);
return valueOf("from mt: " + key);
}
};
// set basic values
t.set("ppp", "abc");
t.set(123, "def");
mt.set(LuaValue.INDEX, fb);
// check before setting metatable
assertEquals("abc", t.get("ppp").tojstring());
assertEquals("def", t.get(123).tojstring());
assertEquals("nil", t.get("qqq").tojstring());
assertEquals("nil", t.get(456).tojstring());
// check before setting metatable
t.setmetatable(mt);
assertEquals(mt, t.getmetatable());
assertEquals("abc", t.get("ppp").tojstring());
assertEquals("def", t.get(123).tojstring());
assertEquals("from mt: qqq", t.get("qqq").tojstring());
assertEquals("from mt: 456", t.get(456).tojstring());
// use raw set
t.rawset("qqq", "alt-qqq");
t.rawset(456, "alt-456");
assertEquals("abc", t.get("ppp").tojstring());
assertEquals("def", t.get(123).tojstring());
assertEquals("alt-qqq", t.get("qqq").tojstring());
assertEquals("alt-456", t.get(456).tojstring());
// remove using raw set
t.rawset("qqq", LuaValue.NIL);
t.rawset(456, LuaValue.NIL);
assertEquals("abc", t.get("ppp").tojstring());
assertEquals("def", t.get(123).tojstring());
assertEquals("from mt: qqq", t.get("qqq").tojstring());
assertEquals("from mt: 456", t.get(456).tojstring());
// set metatable to null
t.setmetatable(null);
assertEquals("abc", t.get("ppp").tojstring());
assertEquals("def", t.get(123).tojstring());
assertEquals("nil", t.get("qqq").tojstring());
assertEquals("nil", t.get(456).tojstring());
}
@Test
void testNext() {
final LuaTable t = new_Table();
assertEquals(LuaValue.NIL, t.next(LuaValue.NIL));
// insert array elements
t.set(1, "one");
assertEquals(LuaValue.valueOf(1), t.next(LuaValue.NIL).arg(1));
assertEquals(LuaValue.valueOf("one"), t.next(LuaValue.NIL).arg(2));
assertEquals(LuaValue.NIL, t.next(LuaValue.ONE));
t.set(2, "two");
assertEquals(LuaValue.valueOf(1), t.next(LuaValue.NIL).arg(1));
assertEquals(LuaValue.valueOf("one"), t.next(LuaValue.NIL).arg(2));
assertEquals(LuaValue.valueOf(2), t.next(LuaValue.ONE).arg(1));
assertEquals(LuaValue.valueOf("two"), t.next(LuaValue.ONE).arg(2));
assertEquals(LuaValue.NIL, t.next(LuaValue.valueOf(2)));
// insert hash elements
t.set("aa", "aaa");
assertEquals(LuaValue.valueOf(1), t.next(LuaValue.NIL).arg(1));
assertEquals(LuaValue.valueOf("one"), t.next(LuaValue.NIL).arg(2));
assertEquals(LuaValue.valueOf(2), t.next(LuaValue.ONE).arg(1));
assertEquals(LuaValue.valueOf("two"), t.next(LuaValue.ONE).arg(2));
assertEquals(LuaValue.valueOf("aa"), t.next(LuaValue.valueOf(2)).arg(1));
assertEquals(LuaValue.valueOf("aaa"), t.next(LuaValue.valueOf(2)).arg(2));
assertEquals(LuaValue.NIL, t.next(LuaValue.valueOf("aa")));
t.set("bb", "bbb");
assertEquals(LuaValue.valueOf(1), t.next(LuaValue.NIL).arg(1));
assertEquals(LuaValue.valueOf("one"), t.next(LuaValue.NIL).arg(2));
assertEquals(LuaValue.valueOf(2), t.next(LuaValue.ONE).arg(1));
assertEquals(LuaValue.valueOf("two"), t.next(LuaValue.ONE).arg(2));
assertEquals(LuaValue.valueOf("aa"), t.next(LuaValue.valueOf(2)).arg(1));
assertEquals(LuaValue.valueOf("aaa"), t.next(LuaValue.valueOf(2)).arg(2));
assertEquals(LuaValue.valueOf("bb"), t.next(LuaValue.valueOf("aa")).arg(1));
assertEquals(LuaValue.valueOf("bbb"), t.next(LuaValue.valueOf("aa")).arg(2));
assertEquals(LuaValue.NIL, t.next(LuaValue.valueOf("bb")));
}
@Test
void testLoopWithRemoval() {
final LuaTable t = new_Table();
t.set(LuaValue.valueOf(1), LuaValue.valueOf("1"));
t.set(LuaValue.valueOf(3), LuaValue.valueOf("3"));
t.set(LuaValue.valueOf(8), LuaValue.valueOf("4"));
t.set(LuaValue.valueOf(17), LuaValue.valueOf("5"));
t.set(LuaValue.valueOf(26), LuaValue.valueOf("6"));
t.set(LuaValue.valueOf(35), LuaValue.valueOf("7"));
t.set(LuaValue.valueOf(42), LuaValue.valueOf("8"));
t.set(LuaValue.valueOf(60), LuaValue.valueOf("10"));
t.set(LuaValue.valueOf(63), LuaValue.valueOf("11"));
Varargs entry = t.next(LuaValue.NIL);
while ( !entry.isnil(1) ) {
LuaValue k = entry.arg1();
LuaValue v = entry.arg(2);
if ((k.toint() & 1) == 0) {
t.set(k, LuaValue.NIL);
}
entry = t.next(k);
}
int numEntries = 0;
entry = t.next(LuaValue.NIL);
while ( !entry.isnil(1) ) {
LuaValue k = entry.arg1();
// Only odd keys should remain
assertTrue((k.toint() & 1) == 1);
numEntries++;
entry = t.next(k);
}
assertEquals(5, numEntries);
}
@Test
void testLoopWithRemovalAndSet() {
final LuaTable t = new_Table();
t.set(LuaValue.valueOf(1), LuaValue.valueOf("1"));
t.set(LuaValue.valueOf(3), LuaValue.valueOf("3"));
t.set(LuaValue.valueOf(8), LuaValue.valueOf("4"));
t.set(LuaValue.valueOf(17), LuaValue.valueOf("5"));
t.set(LuaValue.valueOf(26), LuaValue.valueOf("6"));
t.set(LuaValue.valueOf(35), LuaValue.valueOf("7"));
t.set(LuaValue.valueOf(42), LuaValue.valueOf("8"));
t.set(LuaValue.valueOf(60), LuaValue.valueOf("10"));
t.set(LuaValue.valueOf(63), LuaValue.valueOf("11"));
Varargs entry = t.next(LuaValue.NIL);
Varargs entry2 = entry;
while ( !entry.isnil(1) ) {
LuaValue k = entry.arg1();
LuaValue v = entry.arg(2);
if ((k.toint() & 1) == 0) {
t.set(k, LuaValue.NIL);
} else {
t.set(k, v.tonumber());
entry2 = t.next(entry2.arg1());
}
entry = t.next(k);
}
int numEntries = 0;
entry = t.next(LuaValue.NIL);
while ( !entry.isnil(1) ) {
LuaValue k = entry.arg1();
// Only odd keys should remain
assertTrue((k.toint() & 1) == 1);
assertTrue(entry.arg(2).type() == LuaValue.TNUMBER);
numEntries++;
entry = t.next(k);
}
assertEquals(5, numEntries);
}
}

View File

@@ -0,0 +1,437 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import org.junit.jupiter.api.Test;
class TableTest {
protected LuaTable new_Table() {
return new LuaTable();
}
protected LuaTable new_Table(int n, int m) {
return new LuaTable(n, m);
}
private int keyCount(LuaTable t) {
return keys(t).length;
}
private LuaValue[] keys(LuaTable t) {
ArrayList<LuaValue> l = new ArrayList<LuaValue>();
LuaValue k = LuaValue.NIL;
while ( true ) {
Varargs n = t.next(k);
if ((k = n.arg1()).isnil())
break;
l.add(k);
}
return l.toArray(new LuaValue[t.length()]);
}
@Test
void testInOrderIntegerKeyInsertion() {
LuaTable t = new_Table();
for (int i = 1; i <= 32; ++i) {
t.set(i, LuaValue.valueOf("Test Value! " + i));
}
// Ensure all keys are still there.
for (int i = 1; i <= 32; ++i) {
assertEquals("Test Value! " + i, t.get(i).tojstring());
}
// Ensure capacities make sense
assertEquals(0, t.getHashLength());
assertTrue(t.getArrayLength() >= 32);
assertTrue(t.getArrayLength() <= 64);
}
@Test
void testRekeyCount() {
LuaTable t = new_Table();
// NOTE: This order of insertion is important.
t.set(3, LuaInteger.valueOf(3));
t.set(1, LuaInteger.valueOf(1));
t.set(5, LuaInteger.valueOf(5));
t.set(4, LuaInteger.valueOf(4));
t.set(6, LuaInteger.valueOf(6));
t.set(2, LuaInteger.valueOf(2));
for (int i = 1; i < 6; ++i) {
assertEquals(LuaInteger.valueOf(i), t.get(i));
}
assertTrue(t.getArrayLength() >= 3);
assertTrue(t.getArrayLength() <= 12);
assertTrue(t.getHashLength() <= 3);
}
@Test
void testOutOfOrderIntegerKeyInsertion() {
LuaTable t = new_Table();
for (int i = 32; i > 0; --i) {
t.set(i, LuaValue.valueOf("Test Value! " + i));
}
// Ensure all keys are still there.
for (int i = 1; i <= 32; ++i) {
assertEquals("Test Value! " + i, t.get(i).tojstring());
}
// Ensure capacities make sense
assertEquals(32, t.getArrayLength());
assertEquals(0, t.getHashLength());
}
@Test
void testStringAndIntegerKeys() {
LuaTable t = new_Table();
for (int i = 0; i < 10; ++i) {
LuaString str = LuaValue.valueOf(String.valueOf(i));
t.set(i, str);
t.set(str, LuaInteger.valueOf(i));
}
assertTrue(t.getArrayLength() >= 8); // 1, 2, ..., 9
assertTrue(t.getArrayLength() <= 16);
assertTrue(t.getHashLength() >= 11); // 0, "0", "1", ..., "9"
assertTrue(t.getHashLength() <= 33);
LuaValue[] keys = keys(t);
int intKeys = 0;
int stringKeys = 0;
assertEquals(20, keys.length);
for (int i = 0; i < keys.length; ++i) {
LuaValue k = keys[i];
if (k instanceof LuaInteger) {
final int ik = k.toint();
assertTrue(ik >= 0 && ik < 10);
final int mask = 1<<ik;
assertTrue((intKeys & mask) == 0);
intKeys |= mask;
} else if (k instanceof LuaString) {
final int ik = Integer.parseInt(k.strvalue().tojstring());
assertEquals(String.valueOf(ik), k.strvalue().tojstring());
assertTrue(ik >= 0 && ik < 10);
final int mask = 1<<ik;
assertTrue((stringKeys & mask) == 0, "Key \"" + ik + "\" found more than once");
stringKeys |= mask;
} else {
fail("Unexpected type of key found");
}
}
assertEquals(0x03FF, intKeys);
assertEquals(0x03FF, stringKeys);
}
@Test
void testBadInitialCapacity() {
LuaTable t = new_Table(0, 1);
t.set("test", LuaValue.valueOf("foo"));
t.set("explode", LuaValue.valueOf("explode"));
assertEquals(2, keyCount(t));
}
@Test
void testRemove0() {
LuaTable t = new_Table(2, 0);
t.set(1, LuaValue.valueOf("foo"));
t.set(2, LuaValue.valueOf("bah"));
assertNotSame(LuaValue.NIL, t.get(1));
assertNotSame(LuaValue.NIL, t.get(2));
assertEquals(LuaValue.NIL, t.get(3));
t.set(1, LuaValue.NIL);
t.set(2, LuaValue.NIL);
t.set(3, LuaValue.NIL);
assertEquals(LuaValue.NIL, t.get(1));
assertEquals(LuaValue.NIL, t.get(2));
assertEquals(LuaValue.NIL, t.get(3));
}
@Test
void testRemove1() {
LuaTable t = new_Table(0, 1);
t.set("test", LuaValue.valueOf("foo"));
t.set("explode", LuaValue.NIL);
t.set(42, LuaValue.NIL);
t.set(new_Table(), LuaValue.NIL);
t.set("test", LuaValue.NIL);
assertEquals(0, keyCount(t));
t.set(10, LuaInteger.valueOf(5));
t.set(10, LuaValue.NIL);
assertEquals(0, keyCount(t));
}
@Test
void testRemove2() {
LuaTable t = new_Table(0, 1);
t.set("test", LuaValue.valueOf("foo"));
t.set("string", LuaInteger.valueOf(10));
assertEquals(2, keyCount(t));
t.set("string", LuaValue.NIL);
t.set("three", LuaValue.valueOf(3.14));
assertEquals(2, keyCount(t));
t.set("test", LuaValue.NIL);
assertEquals(1, keyCount(t));
t.set(10, LuaInteger.valueOf(5));
assertEquals(2, keyCount(t));
t.set(10, LuaValue.NIL);
assertEquals(1, keyCount(t));
t.set("three", LuaValue.NIL);
assertEquals(0, keyCount(t));
}
@Test
void testShrinkNonPowerOfTwoArray() {
LuaTable t = new_Table(6, 2);
t.set(1, "one");
t.set(2, "two");
t.set(3, "three");
t.set(4, "four");
t.set(5, "five");
t.set(6, "six");
t.set("aa", "aaa");
t.set("bb", "bbb");
t.set(3, LuaValue.NIL);
t.set(4, LuaValue.NIL);
t.set(6, LuaValue.NIL);
t.set("cc", "ccc");
t.set("dd", "ddd");
assertEquals(4, t.getArrayLength());
assertTrue(t.getHashLength() < 10);
assertEquals(5, t.hashEntries);
assertEquals("one", t.get(1).tojstring());
assertEquals("two", t.get(2).tojstring());
assertEquals(LuaValue.NIL, t.get(3));
assertEquals(LuaValue.NIL, t.get(4));
assertEquals("five", t.get(5).tojstring());
assertEquals(LuaValue.NIL, t.get(6));
assertEquals("aaa", t.get("aa").tojstring());
assertEquals("bbb", t.get("bb").tojstring());
assertEquals("ccc", t.get("cc").tojstring());
assertEquals("ddd", t.get("dd").tojstring());
}
@Test
void testInOrderLuaLength() {
LuaTable t = new_Table();
for (int i = 1; i <= 32; ++i) {
t.set(i, LuaValue.valueOf("Test Value! " + i));
assertEquals(i, t.length());
}
}
@Test
void testOutOfOrderLuaLength() {
LuaTable t = new_Table();
for (int j = 8; j < 32; j += 8) {
for (int i = j; i > 0; --i) {
t.set(i, LuaValue.valueOf("Test Value! " + i));
}
assertEquals(j, t.length());
}
}
@Test
void testStringKeysLuaLength() {
LuaTable t = new_Table();
for (int i = 1; i <= 32; ++i) {
t.set("str-" + i, LuaValue.valueOf("String Key Test Value! " + i));
assertEquals(0, t.length());
}
}
@Test
void testMixedKeysLuaLength() {
LuaTable t = new_Table();
for (int i = 1; i <= 32; ++i) {
t.set("str-" + i, LuaValue.valueOf("String Key Test Value! " + i));
t.set(i, LuaValue.valueOf("Int Key Test Value! " + i));
assertEquals(i, t.length());
}
}
private static final void compareLists(LuaTable t, Vector<LuaString> v) {
int n = v.size();
assertEquals(v.size(), t.length());
for (int j = 0; j < n; j++) {
LuaString vj = v.elementAt(j);
String tj = t.get(j+1).tojstring();
assertEquals(vj.tojstring(), tj);
}
}
@Test
void testInsertBeginningOfList() {
LuaTable t = new_Table();
Vector<LuaString> v = new Vector<>();
for (int i = 1; i <= 32; ++i) {
LuaString test = LuaValue.valueOf("Test Value! " + i);
t.insert(1, test);
v.insertElementAt(test, 0);
compareLists(t, v);
}
}
@Test
void testInsertEndOfList() {
LuaTable t = new_Table();
Vector<LuaString> v = new Vector<>();
for (int i = 1; i <= 32; ++i) {
LuaString test = LuaValue.valueOf("Test Value! " + i);
t.insert(0, test);
v.insertElementAt(test, v.size());
compareLists(t, v);
}
}
@Test
void testInsertMiddleOfList() {
LuaTable t = new_Table();
Vector<LuaString> v = new Vector<>();
for (int i = 1; i <= 32; ++i) {
LuaString test = LuaValue.valueOf("Test Value! " + i);
int m = i/2;
t.insert(m+1, test);
v.insertElementAt(test, m);
compareLists(t, v);
}
}
private static final void prefillLists(LuaTable t, Vector<LuaString> v) {
for (int i = 1; i <= 32; ++i) {
LuaString test = LuaValue.valueOf("Test Value! " + i);
t.insert(0, test);
v.insertElementAt(test, v.size());
}
}
@Test
void testRemoveBeginningOfList() {
LuaTable t = new_Table();
Vector<LuaString> v = new Vector<>();
prefillLists(t, v);
for (int i = 1; i <= 32; ++i) {
t.remove(1);
v.removeElementAt(0);
compareLists(t, v);
}
}
@Test
void testRemoveEndOfList() {
LuaTable t = new_Table();
Vector<LuaString> v = new Vector<>();
prefillLists(t, v);
for (int i = 1; i <= 32; ++i) {
t.remove(0);
v.removeElementAt(v.size()-1);
compareLists(t, v);
}
}
@Test
void testRemoveMiddleOfList() {
LuaTable t = new_Table();
Vector<LuaString> v = new Vector<>();
prefillLists(t, v);
for (int i = 1; i <= 32; ++i) {
int m = v.size()/2;
t.remove(m+1);
v.removeElementAt(m);
compareLists(t, v);
}
}
@Test
void testRemoveWhileIterating() {
LuaTable t = LuaValue.tableOf(
new LuaValue[] { LuaValue.valueOf("a"), LuaValue.valueOf("aa"), LuaValue.valueOf("b"),
LuaValue.valueOf("bb"), LuaValue.valueOf("c"), LuaValue.valueOf("cc"), LuaValue.valueOf("d"),
LuaValue.valueOf("dd"), LuaValue.valueOf("e"), LuaValue.valueOf("ee"), },
new LuaValue[] { LuaValue.valueOf("11"), LuaValue.valueOf("22"), LuaValue.valueOf("33"),
LuaValue.valueOf("44"), LuaValue.valueOf("55"), });
// Find expected order after removal.
List<String> expected = new ArrayList<>();
Varargs n;
int i;
for (n = t.next(LuaValue.NIL), i = 0; !n.arg1().isnil(); n = t.next(n.arg1()), ++i) {
if (i%2 == 0)
expected.add(n.arg1() + "=" + n.arg(2));
}
// Remove every other key while iterating over the table.
for (n = t.next(LuaValue.NIL), i = 0; !n.arg1().isnil(); n = t.next(n.arg1()), ++i) {
if (i%2 != 0)
t.set(n.arg1(), LuaValue.NIL);
}
// Iterate over remaining table, and form list of entries still in table.
List<String> actual = new ArrayList<>();
for (n = t.next(LuaValue.NIL); !n.arg1().isnil(); n = t.next(n.arg1())) {
actual.add(n.arg1() + "=" + n.arg(2));
}
assertEquals(expected, actual);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,235 @@
/*******************************************************************************
* Copyright (c) 2012 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import org.junit.jupiter.api.Test;
/**
* Tests of basic unary and binary operators on main value types.
*/
class VarargsTest {
static LuaValue A = LuaValue.valueOf("a");
static LuaValue B = LuaValue.valueOf("b");
static LuaValue C = LuaValue.valueOf("c");
static LuaValue D = LuaValue.valueOf("d");
static LuaValue E = LuaValue.valueOf("e");
static LuaValue F = LuaValue.valueOf("f");
static LuaValue G = LuaValue.valueOf("g");
static LuaValue H = LuaValue.valueOf("h");
static LuaValue Z = LuaValue.valueOf("z");
static LuaValue NIL = LuaValue.NIL;
static Varargs A_G = LuaValue.varargsOf(new LuaValue[] { A, B, C, D, E, F, G });
static Varargs B_E = LuaValue.varargsOf(new LuaValue[] { B, C, D, E });
static Varargs C_G = LuaValue.varargsOf(new LuaValue[] { C, D, E, F, G });
static Varargs C_E = LuaValue.varargsOf(new LuaValue[] { C, D, E });
static Varargs DE = LuaValue.varargsOf(new LuaValue[] { D, E });
static Varargs E_G = LuaValue.varargsOf(new LuaValue[] { E, F, G });
static Varargs FG = LuaValue.varargsOf(new LuaValue[] { F, G });
static LuaValue[] Z_H_array = { Z, A, B, C, D, E, F, G, H };
static Varargs A_G_alt = new Varargs.ArrayPartVarargs(Z_H_array, 1, 7);
static Varargs B_E_alt = new Varargs.ArrayPartVarargs(Z_H_array, 2, 4);
static Varargs C_G_alt = new Varargs.ArrayPartVarargs(Z_H_array, 3, 5);
static Varargs C_E_alt = new Varargs.ArrayPartVarargs(Z_H_array, 3, 3);
static Varargs C_E_alt2 = LuaValue.varargsOf(C, D, E);
static Varargs DE_alt = new Varargs.PairVarargs(D, E);
static Varargs DE_alt2 = LuaValue.varargsOf(D, E);
static Varargs E_G_alt = new Varargs.ArrayPartVarargs(Z_H_array, 5, 3);
static Varargs FG_alt = new Varargs.PairVarargs(F, G);
static Varargs NONE = LuaValue.NONE;
private void expectEquals(Varargs x, Varargs y) {
assertEquals(x.narg(), y.narg());
assertEquals(x.arg1(), y.arg1());
assertEquals(x.arg(0), y.arg(0));
assertEquals(x.arg(-1), y.arg(-1));
assertEquals(x.arg(2), y.arg(2));
assertEquals(x.arg(3), y.arg(3));
for (int i = 4; i < x.narg()+2; ++i)
assertEquals(x.arg(i), y.arg(i));
}
@Test
void testSanity() {
expectEquals(A_G, A_G);
expectEquals(A_G_alt, A_G_alt);
expectEquals(A_G, A_G_alt);
expectEquals(B_E, B_E_alt);
expectEquals(C_G, C_G_alt);
expectEquals(C_E, C_E_alt);
expectEquals(C_E, C_E_alt2);
expectEquals(DE, DE_alt);
expectEquals(DE, DE_alt2);
expectEquals(E_G, E_G_alt);
expectEquals(FG, FG_alt);
expectEquals(FG_alt, FG_alt);
expectEquals(A, A);
expectEquals(NONE, NONE);
expectEquals(NIL, NIL);
}
@Test
void testNegativeIndices() {
expectNegSubargsError(A_G);
expectNegSubargsError(A_G_alt);
expectNegSubargsError(B_E);
expectNegSubargsError(B_E_alt);
expectNegSubargsError(C_G);
expectNegSubargsError(C_G_alt);
expectNegSubargsError(C_E);
expectNegSubargsError(C_E_alt);
expectNegSubargsError(C_E_alt2);
expectNegSubargsError(DE);
expectNegSubargsError(DE_alt);
expectNegSubargsError(DE_alt2);
expectNegSubargsError(E_G);
expectNegSubargsError(FG);
expectNegSubargsError(A);
expectNegSubargsError(NONE);
expectNegSubargsError(NIL);
}
private void standardTestsA_G(Varargs a_g) {
expectEquals(A_G, a_g);
expectEquals(A_G, a_g.subargs(1));
expectEquals(C_G, a_g.subargs(3).subargs(1));
expectEquals(E_G, a_g.subargs(5));
expectEquals(E_G, a_g.subargs(5).subargs(1));
expectEquals(FG, a_g.subargs(6));
expectEquals(FG, a_g.subargs(6).subargs(1));
expectEquals(G, a_g.subargs(7));
expectEquals(G, a_g.subargs(7).subargs(1));
expectEquals(NONE, a_g.subargs(8));
expectEquals(NONE, a_g.subargs(8).subargs(1));
standardTestsC_G(A_G.subargs(3));
}
private void standardTestsC_G(Varargs c_g) {
expectEquals(C_G, c_g.subargs(1));
expectEquals(E_G, c_g.subargs(3));
expectEquals(E_G, c_g.subargs(3).subargs(1));
expectEquals(FG, c_g.subargs(4));
expectEquals(FG, c_g.subargs(4).subargs(1));
expectEquals(G, c_g.subargs(5));
expectEquals(G, c_g.subargs(5).subargs(1));
expectEquals(NONE, c_g.subargs(6));
expectEquals(NONE, c_g.subargs(6).subargs(1));
standardTestsE_G(c_g.subargs(3));
}
private void standardTestsE_G(Varargs e_g) {
expectEquals(E_G, e_g.subargs(1));
expectEquals(FG, e_g.subargs(2));
expectEquals(FG, e_g.subargs(2).subargs(1));
expectEquals(G, e_g.subargs(3));
expectEquals(G, e_g.subargs(3).subargs(1));
expectEquals(NONE, e_g.subargs(4));
expectEquals(NONE, e_g.subargs(4).subargs(1));
standardTestsFG(e_g.subargs(2));
}
private void standardTestsFG(Varargs fg) {
expectEquals(FG, fg.subargs(1));
expectEquals(G, fg.subargs(2));
expectEquals(G, fg.subargs(2).subargs(1));
expectEquals(NONE, fg.subargs(3));
expectEquals(NONE, fg.subargs(3).subargs(1));
}
private void standardTestsNone(Varargs none) {
expectEquals(NONE, none.subargs(1));
expectEquals(NONE, none.subargs(2));
}
@Test
void testVarargsSubargs() {
standardTestsA_G(A_G);
standardTestsA_G(A_G_alt);
standardTestsC_G(C_G);
standardTestsC_G(C_G_alt);
standardTestsE_G(E_G);
standardTestsE_G(E_G_alt);
standardTestsFG(FG);
standardTestsFG(FG_alt);
standardTestsNone(NONE);
}
@Test
void testVarargsMore() {
Varargs a_g;
a_g = LuaValue.varargsOf(new LuaValue[] { A, }, LuaValue.varargsOf(new LuaValue[] { B, C, D, E, F, G }));
standardTestsA_G(a_g);
a_g = LuaValue.varargsOf(new LuaValue[] { A, B, }, LuaValue.varargsOf(new LuaValue[] { C, D, E, F, G }));
standardTestsA_G(a_g);
a_g = LuaValue.varargsOf(new LuaValue[] { A, B, C, }, LuaValue.varargsOf(new LuaValue[] { D, E, F, G }));
standardTestsA_G(a_g);
a_g = LuaValue.varargsOf(new LuaValue[] { A, B, C, D, }, LuaValue.varargsOf(E, F, G));
standardTestsA_G(a_g);
a_g = LuaValue.varargsOf(new LuaValue[] { A, B, C, D, E }, LuaValue.varargsOf(F, G));
standardTestsA_G(a_g);
a_g = LuaValue.varargsOf(new LuaValue[] { A, B, C, D, E, F, }, G);
standardTestsA_G(a_g);
}
@Test
void testPairVarargsMore() {
Varargs a_g = new Varargs.PairVarargs(A, new Varargs.PairVarargs(B, new Varargs.PairVarargs(C,
new Varargs.PairVarargs(D, new Varargs.PairVarargs(E, new Varargs.PairVarargs(F, G))))));
standardTestsA_G(a_g);
}
@Test
void testArrayPartMore() {
Varargs a_g;
a_g = new Varargs.ArrayPartVarargs(Z_H_array, 1, 1, new Varargs.ArrayPartVarargs(Z_H_array, 2, 6));
standardTestsA_G(a_g);
a_g = new Varargs.ArrayPartVarargs(Z_H_array, 1, 2, new Varargs.ArrayPartVarargs(Z_H_array, 3, 5));
standardTestsA_G(a_g);
a_g = new Varargs.ArrayPartVarargs(Z_H_array, 1, 3, new Varargs.ArrayPartVarargs(Z_H_array, 4, 4));
standardTestsA_G(a_g);
a_g = new Varargs.ArrayPartVarargs(Z_H_array, 1, 4, new Varargs.ArrayPartVarargs(Z_H_array, 5, 3));
standardTestsA_G(a_g);
a_g = new Varargs.ArrayPartVarargs(Z_H_array, 1, 5, new Varargs.ArrayPartVarargs(Z_H_array, 6, 2));
standardTestsA_G(a_g);
a_g = new Varargs.ArrayPartVarargs(Z_H_array, 1, 6, new Varargs.ArrayPartVarargs(Z_H_array, 7, 1));
standardTestsA_G(a_g);
}
private void expectNegSubargsError(Varargs v) {
String expected_msg = "bad argument #1: start must be > 0";
try {
v.subargs(0);
fail("Failed to throw exception for index 0");
} catch (LuaError e) {
assertEquals(expected_msg, e.getMessage());
}
try {
v.subargs(-1);
fail("Failed to throw exception for index -1");
} catch (LuaError e) {
assertEquals(expected_msg, e.getMessage());
}
}
}

View File

@@ -0,0 +1,262 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.lang.ref.WeakReference;
import org.junit.jupiter.api.Test;
class WeakTableTest {
@Test
void testWeakValuesTable() {
LuaTable t = WeakTable.make(false, true);
Object obj = new Object();
LuaTable tableValue = new LuaTable();
LuaString stringValue = LuaString.valueOf("this is a test");
LuaTable tableValue2 = new LuaTable();
t.set("table", tableValue);
t.set("userdata", LuaValue.userdataOf(obj, null));
t.set("string", stringValue);
t.set("string2", LuaValue.valueOf("another string"));
t.set(1, tableValue2);
assertTrue(t.getHashLength() >= 4, "table must have at least 4 elements");
// TODO fix assert
// assertTrue(t.getArrayLength() >= 1, "array part must have 1 element");
// check that table can be used to get elements
assertEquals(tableValue, t.get("table"));
assertEquals(stringValue, t.get("string"));
assertEquals(obj, t.get("userdata").checkuserdata());
assertEquals(tableValue2, t.get(1));
// nothing should be collected, since we have strong references here
collectGarbage();
// check that elements are still there
assertEquals(tableValue, t.get("table"));
assertEquals(stringValue, t.get("string"));
assertEquals(obj, t.get("userdata").checkuserdata());
assertEquals(tableValue2, t.get(1));
// drop our strong references
obj = null;
tableValue = null;
tableValue2 = null;
stringValue = null;
// Garbage collection should cause weak entries to be dropped.
collectGarbage();
// check that they are dropped
assertEquals(LuaValue.NIL, t.get("table"));
assertEquals(LuaValue.NIL, t.get("userdata"));
assertEquals(LuaValue.NIL, t.get(1));
assertFalse(t.get("string").isnil(), "strings should not be in weak references");
}
@Test
void testWeakKeysTable() {
LuaTable t = WeakTable.make(true, false);
LuaValue key = LuaValue.userdataOf(new MyData(111));
LuaValue val = LuaValue.userdataOf(new MyData(222));
// set up the table
t.set(key, val);
assertEquals(val, t.get(key));
System.gc();
assertEquals(val, t.get(key));
// drop key and value references, replace them with new ones
WeakReference<LuaValue> origkey = new WeakReference<>(key);
WeakReference<LuaValue> origval = new WeakReference<>(val);
key = LuaValue.userdataOf(new MyData(111));
val = LuaValue.userdataOf(new MyData(222));
// new key and value should be interchangeable (feature of this test class)
assertEquals(key, origkey.get());
assertEquals(val, origval.get());
assertEquals(val, t.get(key));
assertEquals(val, t.get(origkey.get()));
assertEquals(origval.get(), t.get(key));
// value should not be reachable after gc
collectGarbage();
assertEquals(null, origkey.get());
assertEquals(LuaValue.NIL, t.get(key));
collectGarbage();
assertEquals(null, origval.get());
}
@Test
void testNext() {
LuaTable t = WeakTable.make(true, true);
LuaValue key = LuaValue.userdataOf(new MyData(111));
LuaValue val = LuaValue.userdataOf(new MyData(222));
LuaValue key2 = LuaValue.userdataOf(new MyData(333));
LuaValue val2 = LuaValue.userdataOf(new MyData(444));
LuaValue key3 = LuaValue.userdataOf(new MyData(555));
LuaValue val3 = LuaValue.userdataOf(new MyData(666));
// set up the table
t.set(key, val);
t.set(key2, val2);
t.set(key3, val3);
// forget one of the keys
key2 = null;
val2 = null;
collectGarbage();
// table should have 2 entries
int size = 0;
for (LuaValue k = t.next(LuaValue.NIL).arg1(); !k.isnil(); k = t.next(k).arg1()) {
size++;
}
assertEquals(2, size);
}
@Test
void testWeakKeysValuesTable() {
LuaTable t = WeakTable.make(true, true);
LuaValue key = LuaValue.userdataOf(new MyData(111));
LuaValue val = LuaValue.userdataOf(new MyData(222));
LuaValue key2 = LuaValue.userdataOf(new MyData(333));
LuaValue val2 = LuaValue.userdataOf(new MyData(444));
LuaValue key3 = LuaValue.userdataOf(new MyData(555));
LuaValue val3 = LuaValue.userdataOf(new MyData(666));
// set up the table
t.set(key, val);
t.set(key2, val2);
t.set(key3, val3);
assertEquals(val, t.get(key));
assertEquals(val2, t.get(key2));
assertEquals(val3, t.get(key3));
System.gc();
assertEquals(val, t.get(key));
assertEquals(val2, t.get(key2));
assertEquals(val3, t.get(key3));
// drop key and value references, replace them with new ones
WeakReference<LuaValue> origkey = new WeakReference<>(key);
WeakReference<LuaValue> origval = new WeakReference<>(val);
WeakReference<LuaValue> origkey2 = new WeakReference<>(key2);
WeakReference<LuaValue> origval2 = new WeakReference<>(val2);
WeakReference<LuaValue> origkey3 = new WeakReference<>(key3);
WeakReference<LuaValue> origval3 = new WeakReference<>(val3);
key = LuaValue.userdataOf(new MyData(111));
val = LuaValue.userdataOf(new MyData(222));
key2 = LuaValue.userdataOf(new MyData(333));
// don't drop val2, or key3
val3 = LuaValue.userdataOf(new MyData(666));
// no values should be reachable after gc
collectGarbage();
assertEquals(null, origkey.get());
assertEquals(null, origval.get());
assertEquals(null, origkey2.get());
assertEquals(null, origval3.get());
assertEquals(LuaValue.NIL, t.get(key));
assertEquals(LuaValue.NIL, t.get(key2));
assertEquals(LuaValue.NIL, t.get(key3));
// all originals should be gone after gc, then access
val2 = null;
key3 = null;
collectGarbage();
assertEquals(null, origval2.get());
assertEquals(null, origkey3.get());
}
@Test
void testReplace() {
LuaTable t = WeakTable.make(true, true);
LuaValue key = LuaValue.userdataOf(new MyData(111));
LuaValue val = LuaValue.userdataOf(new MyData(222));
LuaValue key2 = LuaValue.userdataOf(new MyData(333));
LuaValue val2 = LuaValue.userdataOf(new MyData(444));
LuaValue key3 = LuaValue.userdataOf(new MyData(555));
LuaValue val3 = LuaValue.userdataOf(new MyData(666));
// set up the table
t.set(key, val);
t.set(key2, val2);
t.set(key3, val3);
LuaValue val4 = LuaValue.userdataOf(new MyData(777));
t.set(key2, val4);
// table should have 3 entries
int size = 0;
for (LuaValue k = t.next(LuaValue.NIL).arg1(); !k.isnil() && size < 1000; k = t.next(k).arg1()) {
size++;
}
assertEquals(3, size);
}
public static class MyData {
public final int value;
public MyData(int value) {
this.value = value;
}
@Override
public int hashCode() {
return value;
}
@Override
public boolean equals(Object o) {
return (o instanceof MyData) && ((MyData) o).value == value;
}
@Override
public String toString() {
return "mydata-" + value;
}
}
static void collectGarbage() {
Runtime rt = Runtime.getRuntime();
rt.gc();
try {
Thread.sleep(20);
rt.gc();
Thread.sleep(20);
} catch (Exception e) {
e.printStackTrace();
}
rt.gc();
}
}

View File