Move sources into maven modules
This commit is contained in:
60
luaj-core/pom.xml
Normal file
60
luaj-core/pom.xml
Normal file
@@ -0,0 +1,60 @@
|
||||
<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>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.helger.maven</groupId>
|
||||
<artifactId>ph-javacc-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>generate-grammar</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>javacc</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<jdkVersion>1.8</jdkVersion>
|
||||
<javadocFriendlyComments>true</javadocFriendlyComments>
|
||||
<packageName>org.luaj.vm2.parser</packageName>
|
||||
<sourceDirectory>src/main/javacc</sourceDirectory>
|
||||
<outputDirectory>${project.build.directory}/generated-sources/javacc</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>add-source</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>add-source</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sources>
|
||||
<source>${project.build.directory}/generated-sources/javacc</source>
|
||||
</sources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
249
luaj-core/src/main/java/org/luaj/vm2/Buffer.java
Normal file
249
luaj-core/src/main/java/org/luaj/vm2/Buffer.java
Normal file
@@ -0,0 +1,249 @@
|
||||
/*******************************************************************************
|
||||
* 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 final 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
|
||||
*/
|
||||
public String toString() {
|
||||
return tojstring();
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a single byte to the buffer.
|
||||
* @return {@code this} to allow call chaining
|
||||
*/
|
||||
public final 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 final Buffer append( LuaValue val ) {
|
||||
append( val.strvalue() );
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a {@link LuaString} to the buffer.
|
||||
* @return {@code this} to allow call chaining
|
||||
*/
|
||||
public final 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 final 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 final 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 final 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
458
luaj-core/src/main/java/org/luaj/vm2/Globals.java
Normal file
458
luaj-core/src/main/java/org/luaj/vm2/Globals.java
Normal file
@@ -0,0 +1,458 @@
|
||||
/*******************************************************************************
|
||||
* 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> {@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> {@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. */
|
||||
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();
|
||||
}
|
||||
public void close() throws IOException {
|
||||
i = n;
|
||||
}
|
||||
public int read() throws IOException {
|
||||
return i < n ? s.charAt(i++) : -1;
|
||||
}
|
||||
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;
|
||||
public int read() throws IOException {
|
||||
int a = avail();
|
||||
return (a <= 0 ? -1 : 0xff & b[i++]);
|
||||
}
|
||||
public int read(byte[] b) throws IOException {
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
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;
|
||||
}
|
||||
public long skip(long n) throws IOException {
|
||||
final long k = Math.min(n, j - i);
|
||||
i += k;
|
||||
return k;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
public void close() throws IOException {
|
||||
s.close();
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
public boolean markSupported() {
|
||||
return true;
|
||||
}
|
||||
public synchronized void reset() throws IOException {
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
443
luaj-core/src/main/java/org/luaj/vm2/LoadState.java
Normal file
443
luaj-core/src/main/java/org/luaj/vm2/LoadState.java
Normal file
@@ -0,0 +1,443 @@
|
||||
/*******************************************************************************
|
||||
* 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> {@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> {@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> {@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) | (((long)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 = ((int) 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 {
|
||||
public Prototype undump(InputStream stream, String chunkname)
|
||||
throws IOException {
|
||||
return LoadState.undump(stream, chunkname);
|
||||
}
|
||||
}
|
||||
}
|
||||
52
luaj-core/src/main/java/org/luaj/vm2/LocVars.java
Normal file
52
luaj-core/src/main/java/org/luaj/vm2/LocVars.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
361
luaj-core/src/main/java/org/luaj/vm2/Lua.java
Normal file
361
luaj-core/src/main/java/org/luaj/vm2/Lua.java
Normal file
@@ -0,0 +1,361 @@
|
||||
/*******************************************************************************
|
||||
* 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 ((int)(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;
|
||||
}
|
||||
}
|
||||
103
luaj-core/src/main/java/org/luaj/vm2/LuaBoolean.java
Normal file
103
luaj-core/src/main/java/org/luaj/vm2/LuaBoolean.java
Normal file
@@ -0,0 +1,103 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
}
|
||||
|
||||
public int type() {
|
||||
return LuaValue.TBOOLEAN;
|
||||
}
|
||||
|
||||
public String typename() {
|
||||
return "boolean";
|
||||
}
|
||||
|
||||
public boolean isboolean() {
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public boolean toboolean() {
|
||||
return v;
|
||||
}
|
||||
|
||||
public String tojstring() {
|
||||
return v ? "true" : "false";
|
||||
}
|
||||
|
||||
public boolean optboolean(boolean defval) {
|
||||
return this.v;
|
||||
}
|
||||
|
||||
public boolean checkboolean() {
|
||||
return v;
|
||||
}
|
||||
|
||||
public LuaValue getmetatable() {
|
||||
return s_metatable;
|
||||
}
|
||||
}
|
||||
598
luaj-core/src/main/java/org/luaj/vm2/LuaClosure.java
Normal file
598
luaj-core/src/main/java/org/luaj/vm2/LuaClosure.java
Normal file
@@ -0,0 +1,598 @@
|
||||
/*******************************************************************************
|
||||
* 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> {@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> {@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 = new UpValue[0];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean isclosure() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public LuaClosure optclosure(LuaClosure defval) {
|
||||
return this;
|
||||
}
|
||||
|
||||
public LuaClosure checkclosure() {
|
||||
return this;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public final LuaValue call() {
|
||||
LuaValue[] stack = getNewStack();
|
||||
return execute(stack,NONE).arg1();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
public final Varargs invoke(Varargs varargs) {
|
||||
return onInvoke(varargs).eval();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return "<"+p.shortsource()+":"+p.linedefined+">";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
300
luaj-core/src/main/java/org/luaj/vm2/LuaDouble.java
Normal file
300
luaj-core/src/main/java/org/luaj/vm2/LuaDouble.java
Normal file
@@ -0,0 +1,300 @@
|
||||
/*******************************************************************************
|
||||
* 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.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 #POSINF}, {@link #NEGINF},
|
||||
* {@link #JSTR_NAN}, {@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 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 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 in J2me
|
||||
long bits = Double.doubleToLongBits( v );
|
||||
return ( bits >> 63 == 0 ) ? "0" : "-0";
|
||||
}
|
||||
*/
|
||||
long l = (long) v;
|
||||
if ( l == v )
|
||||
return Long.toString(l);
|
||||
if ( Double.isNaN(v) )
|
||||
return 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);
|
||||
}
|
||||
}
|
||||
130
luaj-core/src/main/java/org/luaj/vm2/LuaError.java
Normal file
130
luaj-core/src/main/java/org/luaj/vm2/LuaError.java
Normal file
@@ -0,0 +1,130 @@
|
||||
/*******************************************************************************
|
||||
* 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
public Throwable getCause() {
|
||||
return cause;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
92
luaj-core/src/main/java/org/luaj/vm2/LuaFunction.java
Normal file
92
luaj-core/src/main/java/org/luaj/vm2/LuaFunction.java
Normal file
@@ -0,0 +1,92 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
|
||||
public int type() {
|
||||
return TFUNCTION;
|
||||
}
|
||||
|
||||
public String typename() {
|
||||
return "function";
|
||||
}
|
||||
|
||||
public boolean isfunction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public LuaFunction checkfunction() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public LuaFunction optfunction(LuaFunction defval) {
|
||||
return this;
|
||||
}
|
||||
|
||||
public LuaValue getmetatable() {
|
||||
return s_metatable;
|
||||
}
|
||||
|
||||
public String tojstring() {
|
||||
return "function: " + classnamestub();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
219
luaj-core/src/main/java/org/luaj/vm2/LuaInteger.java
Normal file
219
luaj-core/src/main/java/org/luaj/vm2/LuaInteger.java
Normal file
@@ -0,0 +1,219 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
}
|
||||
|
||||
public boolean isint() { return true; }
|
||||
public boolean isinttype() { return true; }
|
||||
public boolean islong() { return true; }
|
||||
|
||||
public byte tobyte() { return (byte) v; }
|
||||
public char tochar() { return (char) v; }
|
||||
public double todouble() { return v; }
|
||||
public float tofloat() { return v; }
|
||||
public int toint() { return v; }
|
||||
public long tolong() { return v; }
|
||||
public short toshort() { return (short) v; }
|
||||
|
||||
public double optdouble(double defval) { return v; }
|
||||
public int optint(int defval) { return v; }
|
||||
public LuaInteger optinteger(LuaInteger defval) { return this; }
|
||||
public long optlong(long defval) { return v; }
|
||||
|
||||
public String tojstring() {
|
||||
return Integer.toString(v);
|
||||
}
|
||||
|
||||
public LuaString strvalue() {
|
||||
return LuaString.valueOf(Integer.toString(v));
|
||||
}
|
||||
|
||||
public LuaString optstring(LuaString defval) {
|
||||
return LuaString.valueOf(Integer.toString(v));
|
||||
}
|
||||
|
||||
public LuaValue tostring() {
|
||||
return LuaString.valueOf(Integer.toString(v));
|
||||
}
|
||||
|
||||
public String optjstring(String defval) {
|
||||
return Integer.toString(v);
|
||||
}
|
||||
|
||||
public LuaInteger checkinteger() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isstring() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return v;
|
||||
}
|
||||
|
||||
public static int hashCode(int x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
// unary operators
|
||||
public LuaValue neg() { return valueOf(-(long)v); }
|
||||
|
||||
// object equality, used for key comparison
|
||||
public boolean equals(Object o) { return o instanceof LuaInteger? ((LuaInteger)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; }
|
||||
|
||||
// arithmetic operators
|
||||
public LuaValue add( LuaValue rhs ) { return rhs.add(v); }
|
||||
public LuaValue add( double lhs ) { return LuaDouble.valueOf(lhs + v); }
|
||||
public LuaValue add( int lhs ) { return LuaInteger.valueOf(lhs + (long)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 subFrom( int lhs ) { return LuaInteger.valueOf(lhs - (long)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 LuaInteger.valueOf(lhs * (long)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); }
|
||||
|
||||
// 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 int checkint() {
|
||||
return v;
|
||||
}
|
||||
public long checklong() {
|
||||
return v;
|
||||
}
|
||||
public double checkdouble() {
|
||||
return v;
|
||||
}
|
||||
public String checkjstring() {
|
||||
return String.valueOf(v);
|
||||
}
|
||||
public LuaString checkstring() {
|
||||
return valueOf( String.valueOf(v) );
|
||||
}
|
||||
|
||||
}
|
||||
108
luaj-core/src/main/java/org/luaj/vm2/LuaNil.java
Normal file
108
luaj-core/src/main/java/org/luaj/vm2/LuaNil.java
Normal file
@@ -0,0 +1,108 @@
|
||||
/*******************************************************************************
|
||||
* 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() {}
|
||||
|
||||
public int type() {
|
||||
return LuaValue.TNIL;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "nil";
|
||||
}
|
||||
|
||||
public String typename() {
|
||||
return "nil";
|
||||
}
|
||||
|
||||
public String tojstring() {
|
||||
return "nil";
|
||||
}
|
||||
|
||||
public LuaValue not() {
|
||||
return LuaValue.TRUE;
|
||||
}
|
||||
|
||||
public boolean toboolean() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isnil() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public LuaValue getmetatable() {
|
||||
return s_metatable;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof LuaNil;
|
||||
}
|
||||
|
||||
public LuaValue checknotnil() {
|
||||
return argerror("value");
|
||||
}
|
||||
|
||||
public boolean isvalidkey() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// optional argument conversions - nil alwas falls badk to default value
|
||||
public boolean optboolean(boolean defval) { return defval; }
|
||||
public LuaClosure optclosure(LuaClosure defval) { return defval; }
|
||||
public double optdouble(double defval) { return defval; }
|
||||
public LuaFunction optfunction(LuaFunction defval) { return defval; }
|
||||
public int optint(int defval) { return defval; }
|
||||
public LuaInteger optinteger(LuaInteger defval) { return defval; }
|
||||
public long optlong(long defval) { return defval; }
|
||||
public LuaNumber optnumber(LuaNumber defval) { return defval; }
|
||||
public LuaTable opttable(LuaTable defval) { return defval; }
|
||||
public LuaThread optthread(LuaThread defval) { return defval; }
|
||||
public String optjstring(String defval) { return defval; }
|
||||
public LuaString optstring(LuaString defval) { return defval; }
|
||||
public Object optuserdata(Object defval) { return defval; }
|
||||
public Object optuserdata(Class c, Object defval) { return defval; }
|
||||
public LuaValue optvalue(LuaValue defval) { return defval; }
|
||||
}
|
||||
81
luaj-core/src/main/java/org/luaj/vm2/LuaNumber.java
Normal file
81
luaj-core/src/main/java/org/luaj/vm2/LuaNumber.java
Normal file
@@ -0,0 +1,81 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
|
||||
public int type() {
|
||||
return TNUMBER;
|
||||
}
|
||||
|
||||
public String typename() {
|
||||
return "number";
|
||||
}
|
||||
|
||||
public LuaNumber checknumber() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public LuaNumber checknumber(String errmsg) {
|
||||
return this;
|
||||
}
|
||||
|
||||
public LuaNumber optnumber(LuaNumber defval) {
|
||||
return this;
|
||||
}
|
||||
|
||||
public LuaValue tonumber() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isnumber() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isstring() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public LuaValue getmetatable() {
|
||||
return s_metatable;
|
||||
}
|
||||
|
||||
public LuaValue concat(LuaValue rhs) { return rhs.concatTo(this); }
|
||||
public Buffer concat(Buffer rhs) { return rhs.concatTo(this); }
|
||||
public LuaValue concatTo(LuaNumber lhs) { return strvalue().concatTo(lhs.strvalue()); }
|
||||
public LuaValue concatTo(LuaString lhs) { return strvalue().concatTo(lhs); }
|
||||
|
||||
}
|
||||
852
luaj-core/src/main/java/org/luaj/vm2/LuaString.java
Normal file
852
luaj-core/src/main/java/org/luaj/vm2/LuaString.java
Normal file
@@ -0,0 +1,852 @@
|
||||
/*******************************************************************************
|
||||
* 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 ) {
|
||||
switch ( 0xE0 & bytes[i++] ) {
|
||||
case 0xE0: ++i;
|
||||
case 0xC0: ++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=b=chars.length; --i>=0; )
|
||||
if ( (c=chars[i]) >=0x80 )
|
||||
b += (c>=0x800)? 2: 1;
|
||||
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) & 0x1f));
|
||||
bytes[j++] = (byte) (0x80 | ( c & 0x3f));
|
||||
} else {
|
||||
bytes[j++] = (byte) (0xE0 | ((c>>12) & 0x0f));
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 && m_bytes[i]==' ' ) ++i;
|
||||
while ( i<j && m_bytes[j-1]==' ' ) --j;
|
||||
if ( i>=j )
|
||||
return Double.NaN;
|
||||
if ( m_bytes[i]=='0' && i+1<j && (m_bytes[i+1]=='x'||m_bytes[i+1]=='X'))
|
||||
return scanlong(16, i+2, j);
|
||||
double l = scanlong(10, i, j);
|
||||
return Double.isNaN(l)? scandouble(i,j): l;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 && m_bytes[i]==' ' ) ++i;
|
||||
while ( i<j && 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] == '-');
|
||||
for ( int i=(neg?start+1: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);
|
||||
}
|
||||
}
|
||||
}
|
||||
1371
luaj-core/src/main/java/org/luaj/vm2/LuaTable.java
Normal file
1371
luaj-core/src/main/java/org/luaj/vm2/LuaTable.java
Normal file
File diff suppressed because it is too large
Load Diff
260
luaj-core/src/main/java/org/luaj/vm2/LuaThread.java
Normal file
260
luaj-core/src/main/java/org/luaj/vm2/LuaThread.java
Normal file
@@ -0,0 +1,260 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
}
|
||||
|
||||
public int type() {
|
||||
return LuaValue.TTHREAD;
|
||||
}
|
||||
|
||||
public String typename() {
|
||||
return "thread";
|
||||
}
|
||||
|
||||
public boolean isthread() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public LuaThread optthread(LuaThread defval) {
|
||||
return this;
|
||||
}
|
||||
|
||||
public LuaThread checkthread() {
|
||||
return this;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
126
luaj-core/src/main/java/org/luaj/vm2/LuaUserdata.java
Normal file
126
luaj-core/src/main/java/org/luaj/vm2/LuaUserdata.java
Normal file
@@ -0,0 +1,126 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
}
|
||||
|
||||
public String tojstring() {
|
||||
return String.valueOf(m_instance);
|
||||
}
|
||||
|
||||
public int type() {
|
||||
return LuaValue.TUSERDATA;
|
||||
}
|
||||
|
||||
public String typename() {
|
||||
return "userdata";
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return m_instance.hashCode();
|
||||
}
|
||||
|
||||
public Object userdata() {
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
public boolean isuserdata() { return true; }
|
||||
public boolean isuserdata(Class c) { return c.isAssignableFrom(m_instance.getClass()); }
|
||||
public Object touserdata() { return m_instance; }
|
||||
public Object touserdata(Class c) { return c.isAssignableFrom(m_instance.getClass())? m_instance: null; }
|
||||
public Object optuserdata(Object defval) { return m_instance; }
|
||||
public Object optuserdata(Class c, Object defval) {
|
||||
if (!c.isAssignableFrom(m_instance.getClass()))
|
||||
typerror(c.getName());
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
public LuaValue getmetatable() {
|
||||
return m_metatable;
|
||||
}
|
||||
|
||||
public LuaValue setmetatable(LuaValue metatable) {
|
||||
this.m_metatable = metatable;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Object checkuserdata() {
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
public Object checkuserdata(Class c) {
|
||||
if ( c.isAssignableFrom(m_instance.getClass()) )
|
||||
return m_instance;
|
||||
return typerror(c.getName());
|
||||
}
|
||||
|
||||
public LuaValue get( LuaValue key ) {
|
||||
return m_metatable!=null? gettable(this,key): NIL;
|
||||
}
|
||||
|
||||
public void set( LuaValue key, LuaValue value ) {
|
||||
if ( m_metatable==null || ! settable(this,key,value) )
|
||||
error( "cannot set "+key+" for userdata" );
|
||||
}
|
||||
|
||||
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
|
||||
public LuaValue eq( LuaValue val ) { return eq_b(val)? TRUE: FALSE; }
|
||||
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
|
||||
public boolean raweq( LuaValue val ) { return val.raweq(this); }
|
||||
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;
|
||||
}
|
||||
}
|
||||
3585
luaj-core/src/main/java/org/luaj/vm2/LuaValue.java
Normal file
3585
luaj-core/src/main/java/org/luaj/vm2/LuaValue.java
Normal file
File diff suppressed because it is too large
Load Diff
51
luaj-core/src/main/java/org/luaj/vm2/Metatable.java
Normal file
51
luaj-core/src/main/java/org/luaj/vm2/Metatable.java
Normal 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. */
|
||||
public boolean useWeakKeys();
|
||||
|
||||
/** Return whether or not this table's values are weak. */
|
||||
public boolean useWeakValues();
|
||||
|
||||
/** Return this metatable as a LuaValue. */
|
||||
public LuaValue toLuaValue();
|
||||
|
||||
/** Return an instance of Slot appropriate for the given key and value. */
|
||||
public Slot entry( LuaValue key, LuaValue value );
|
||||
|
||||
/** Returns the given value wrapped in a weak reference if appropriate. */
|
||||
public 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.
|
||||
*/
|
||||
public LuaValue arrayget(LuaValue[] array, int index);
|
||||
}
|
||||
36
luaj-core/src/main/java/org/luaj/vm2/NonTableMetatable.java
Normal file
36
luaj-core/src/main/java/org/luaj/vm2/NonTableMetatable.java
Normal file
@@ -0,0 +1,36 @@
|
||||
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;
|
||||
}
|
||||
|
||||
public boolean useWeakKeys() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean useWeakValues() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public LuaValue toLuaValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public Slot entry(LuaValue key, LuaValue value) {
|
||||
return LuaTable.defaultEntry(key, value);
|
||||
}
|
||||
|
||||
public LuaValue wrap(LuaValue value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
public LuaValue arrayget(LuaValue[] array, int index) {
|
||||
return array[index];
|
||||
}
|
||||
}
|
||||
44
luaj-core/src/main/java/org/luaj/vm2/OrphanedThread.java
Normal file
44
luaj-core/src/main/java/org/luaj/vm2/OrphanedThread.java
Normal file
@@ -0,0 +1,44 @@
|
||||
/*******************************************************************************
|
||||
* 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");
|
||||
}
|
||||
}
|
||||
476
luaj-core/src/main/java/org/luaj/vm2/Print.java
Normal file
476
luaj-core/src/main/java/org/luaj/vm2/Print.java
Normal file
@@ -0,0 +1,476 @@
|
||||
/*******************************************************************************
|
||||
* 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(" ; " + ((int) code[++pc]) + " (stored in the next OP)");
|
||||
else
|
||||
ps.print(" ; " + ((int) 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);
|
||||
}
|
||||
|
||||
}
|
||||
146
luaj-core/src/main/java/org/luaj/vm2/Prototype.java
Normal file
146
luaj-core/src/main/java/org/luaj/vm2/Prototype.java
Normal file
@@ -0,0 +1,146 @@
|
||||
/*******************************************************************************
|
||||
* 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> {@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> {@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> {@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> {@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> {@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> {@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];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
103
luaj-core/src/main/java/org/luaj/vm2/TailcallVarargs.java
Normal file
103
luaj-core/src/main/java/org/luaj/vm2/TailcallVarargs.java
Normal file
@@ -0,0 +1,103 @@
|
||||
/*******************************************************************************
|
||||
* 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);
|
||||
}
|
||||
|
||||
public boolean isTailcall() {
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public LuaValue arg( int i ) {
|
||||
if ( result == null )
|
||||
eval();
|
||||
return result.arg(i);
|
||||
}
|
||||
|
||||
public LuaValue arg1() {
|
||||
if (result == null)
|
||||
eval();
|
||||
return result.arg1();
|
||||
}
|
||||
|
||||
public int narg() {
|
||||
if (result == null)
|
||||
eval();
|
||||
return result.narg();
|
||||
}
|
||||
|
||||
public Varargs subargs(int start) {
|
||||
if (result == null)
|
||||
eval();
|
||||
return result.subargs(start);
|
||||
}
|
||||
}
|
||||
83
luaj-core/src/main/java/org/luaj/vm2/UpValue.java
Normal file
83
luaj-core/src/main/java/org/luaj/vm2/UpValue.java
Normal file
@@ -0,0 +1,83 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
}
|
||||
|
||||
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 final LuaValue getValue() {
|
||||
return array[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of the upvalue
|
||||
* @param value the {@link LuaValue} to set it to
|
||||
*/
|
||||
public final void setValue( LuaValue value ) {
|
||||
array[index] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close this upvalue so it is no longer on the stack
|
||||
*/
|
||||
public final void close() {
|
||||
LuaValue[] old = array;
|
||||
array = new LuaValue[] { old[index] };
|
||||
old[index] = null;
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
44
luaj-core/src/main/java/org/luaj/vm2/Upvaldesc.java
Normal file
44
luaj-core/src/main/java/org/luaj/vm2/Upvaldesc.java
Normal file
@@ -0,0 +1,44 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return idx + (instack? " instack ": " closed ") + String.valueOf(name);
|
||||
}
|
||||
}
|
||||
725
luaj-core/src/main/java/org/luaj/vm2/Varargs.java
Normal file
725
luaj-core/src/main/java/org/luaj/vm2/Varargs.java
Normal file
@@ -0,0 +1,725 @@
|
||||
/*******************************************************************************
|
||||
* 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()
|
||||
*/
|
||||
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;
|
||||
}
|
||||
public LuaValue arg(int i) {
|
||||
i += start-1;
|
||||
return i>=start && i<=end? v.arg(i): LuaValue.NIL;
|
||||
}
|
||||
public LuaValue arg1() {
|
||||
return v.arg(start);
|
||||
}
|
||||
public int narg() {
|
||||
return end+1-start;
|
||||
}
|
||||
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;
|
||||
}
|
||||
public LuaValue arg(int i) {
|
||||
return i==1? v1: v2.arg(i-1);
|
||||
}
|
||||
public int narg() {
|
||||
return 1+v2.narg();
|
||||
}
|
||||
public LuaValue arg1() {
|
||||
return v1;
|
||||
}
|
||||
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 ;
|
||||
}
|
||||
public LuaValue arg(int i) {
|
||||
return i < 1 ? LuaValue.NIL: i <= v.length? v[i - 1]: r.arg(i-v.length);
|
||||
}
|
||||
public int narg() {
|
||||
return v.length+r.narg();
|
||||
}
|
||||
public LuaValue arg1() { return v.length>0? v[0]: r.arg1(); }
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
public LuaValue arg(final int i) {
|
||||
return i < 1? LuaValue.NIL: i <= length? v[offset+i-1]: more.arg(i-length);
|
||||
}
|
||||
public int narg() {
|
||||
return length + more.narg();
|
||||
}
|
||||
public LuaValue arg1() {
|
||||
return length>0? v[offset]: more.arg1();
|
||||
}
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
413
luaj-core/src/main/java/org/luaj/vm2/WeakTable.java
Normal file
413
luaj-core/src/main/java/org/luaj/vm2/WeakTable.java
Normal file
@@ -0,0 +1,413 @@
|
||||
/*******************************************************************************
|
||||
* 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 boolean weakkeys, weakvalues;
|
||||
private 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 LuaTable.tableOf();
|
||||
}
|
||||
LuaTable table = LuaTable.tableOf();
|
||||
LuaTable mt = LuaTable.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;
|
||||
}
|
||||
|
||||
public boolean useWeakKeys() {
|
||||
return weakkeys;
|
||||
}
|
||||
|
||||
public boolean useWeakValues() {
|
||||
return weakvalues;
|
||||
}
|
||||
|
||||
public LuaValue toLuaValue() {
|
||||
return backing;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public abstract int keyindex( int hashMask );
|
||||
|
||||
public abstract Slot set(LuaValue value);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public StrongSlot find(LuaValue key) {
|
||||
StrongSlot first = first();
|
||||
return ( first != null ) ? first.find( key ) : null;
|
||||
}
|
||||
|
||||
public boolean keyeq(LuaValue key) {
|
||||
StrongSlot first = first();
|
||||
return ( first != null ) && first.keyeq( key );
|
||||
}
|
||||
|
||||
public Slot rest() {
|
||||
return next;
|
||||
}
|
||||
|
||||
public int arraykey(int max) {
|
||||
// Integer keys can never be weak.
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
public Slot add( Slot entry ) {
|
||||
next = ( next != null ) ? next.add( entry ) : entry;
|
||||
if ( strongkey() != null && strongvalue() != null ) {
|
||||
return this;
|
||||
} else {
|
||||
return next;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public int keyindex( int mask ) {
|
||||
return LuaTable.hashmod( keyhash, mask );
|
||||
}
|
||||
|
||||
public Slot set(LuaValue value) {
|
||||
this.value = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LuaValue strongkey() {
|
||||
return strengthen( key );
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
public int keyindex( int mask ) {
|
||||
return LuaTable.hashSlot( strongkey(), mask );
|
||||
}
|
||||
|
||||
public Slot set(LuaValue value) {
|
||||
this.value = weaken(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public LuaValue strongvalue() {
|
||||
return strengthen( value );
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public int keyindex( int hashMask ) {
|
||||
return LuaTable.hashmod( keyhash, hashMask );
|
||||
}
|
||||
|
||||
public Slot set(LuaValue value) {
|
||||
this.value = weaken(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public LuaValue strongkey() {
|
||||
return strengthen( key );
|
||||
}
|
||||
|
||||
public LuaValue strongvalue() {
|
||||
return strengthen( value );
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public int type() {
|
||||
illegal("type","weak value");
|
||||
return 0;
|
||||
}
|
||||
|
||||
public String typename() {
|
||||
illegal("typename","weak value");
|
||||
return null;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "weak<"+ref.get()+">";
|
||||
}
|
||||
|
||||
public LuaValue strongvalue() {
|
||||
Object o = ref.get();
|
||||
return (LuaValue)o;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public LuaValue wrap(LuaValue value) {
|
||||
return weakvalues ? weaken( value ) : value;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
190
luaj-core/src/main/java/org/luaj/vm2/compiler/Constants.java
Normal file
190
luaj-core/src/main/java/org/luaj/vm2/compiler/Constants.java
Normal file
@@ -0,0 +1,190 @@
|
||||
/*******************************************************************************
|
||||
* 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() {}
|
||||
}
|
||||
298
luaj-core/src/main/java/org/luaj/vm2/compiler/DumpState.java
Normal file
298
luaj-core/src/main/java/org/luaj/vm2/compiler/DumpState.java
Normal file
@@ -0,0 +1,298 @@
|
||||
/*******************************************************************************
|
||||
* 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> {@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> {@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;
|
||||
}
|
||||
}
|
||||
1147
luaj-core/src/main/java/org/luaj/vm2/compiler/FuncState.java
Normal file
1147
luaj-core/src/main/java/org/luaj/vm2/compiler/FuncState.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,37 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
31
luaj-core/src/main/java/org/luaj/vm2/compiler/IntPtr.java
Normal file
31
luaj-core/src/main/java/org/luaj/vm2/compiler/IntPtr.java
Normal file
@@ -0,0 +1,31 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
2153
luaj-core/src/main/java/org/luaj/vm2/compiler/LexState.java
Normal file
2153
luaj-core/src/main/java/org/luaj/vm2/compiler/LexState.java
Normal file
File diff suppressed because it is too large
Load Diff
159
luaj-core/src/main/java/org/luaj/vm2/compiler/LuaC.java
Normal file
159
luaj-core/src/main/java/org/luaj/vm2/compiler/LuaC.java
Normal file
@@ -0,0 +1,159 @@
|
||||
/*******************************************************************************
|
||||
* 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> {@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
|
||||
*/
|
||||
public Prototype compile(InputStream stream, String chunkname) throws IOException {
|
||||
return (new CompileState()).luaY_parser(stream, chunkname);
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
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 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, (LuaString) LuaValue.valueOf(name) );
|
||||
/* main func. is always vararg */
|
||||
funcstate.f = new Prototype();
|
||||
funcstate.f.source = (LuaString) LuaValue.valueOf(name);
|
||||
lexstate.mainfunc(funcstate);
|
||||
LuaC._assert (funcstate.prev == null);
|
||||
/* all scopes should be correctly finished */
|
||||
LuaC._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;
|
||||
}
|
||||
}
|
||||
}
|
||||
485
luaj-core/src/main/java/org/luaj/vm2/lib/BaseLib.java
Normal file
485
luaj-core/src/main/java/org/luaj/vm2/lib/BaseLib.java
Normal file
@@ -0,0 +1,485 @@
|
||||
/*******************************************************************************
|
||||
* 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> {@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> {@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.
|
||||
*/
|
||||
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 pairs(next));
|
||||
env.set("ipairs", new ipairs());
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
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(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() )
|
||||
return h.call(arg);
|
||||
LuaValue v = arg.tostring();
|
||||
if ( ! v.isnil() )
|
||||
return v;
|
||||
return valueOf(arg.tojstring());
|
||||
}
|
||||
}
|
||||
|
||||
// "type", // (v) -> value
|
||||
static final class type extends LibFunction {
|
||||
public LuaValue call(LuaValue arg) {
|
||||
return valueOf(arg.typename());
|
||||
}
|
||||
}
|
||||
|
||||
// "xpcall", // (f, err) -> result1, ...
|
||||
final class xpcall extends VarArgFunction {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "pairs" (t) -> iter-func, t, nil
|
||||
static final class pairs extends VarArgFunction {
|
||||
final next next;
|
||||
pairs(next next) {
|
||||
this.next = next;
|
||||
}
|
||||
public Varargs invoke(Varargs args) {
|
||||
return varargsOf( next, args.checktable(1), NIL );
|
||||
}
|
||||
}
|
||||
|
||||
// // "ipairs", // (t) -> iter-func, t, 0
|
||||
static final class ipairs extends VarArgFunction {
|
||||
inext inext = new inext();
|
||||
public Varargs invoke(Varargs args) {
|
||||
return varargsOf( inext, args.checktable(1), ZERO );
|
||||
}
|
||||
}
|
||||
|
||||
// "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 {
|
||||
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++];
|
||||
}
|
||||
}
|
||||
}
|
||||
224
luaj-core/src/main/java/org/luaj/vm2/lib/Bit32Lib.java
Normal file
224
luaj-core/src/main/java/org/luaj/vm2/lib/Bit32Lib.java
Normal file
@@ -0,0 +1,224 @@
|
||||
/*******************************************************************************
|
||||
* 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> {@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> {@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.
|
||||
*/
|
||||
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 {
|
||||
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 {
|
||||
|
||||
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((double) ((long) x & 0xFFFFFFFFL)) : valueOf(x);
|
||||
}
|
||||
}
|
||||
144
luaj-core/src/main/java/org/luaj/vm2/lib/CoroutineLib.java
Normal file
144
luaj-core/src/main/java/org/luaj/vm2/lib/CoroutineLib.java
Normal file
@@ -0,0 +1,144 @@
|
||||
/*******************************************************************************
|
||||
* 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> {@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> {@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.
|
||||
*/
|
||||
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 {
|
||||
public LuaValue call(LuaValue f) {
|
||||
return new LuaThread(globals, f.checkfunction());
|
||||
}
|
||||
}
|
||||
|
||||
static final class resume extends VarArgFunction {
|
||||
public Varargs invoke(Varargs args) {
|
||||
final LuaThread t = args.checkthread(1);
|
||||
return t.resume( args.subargs(2) );
|
||||
}
|
||||
}
|
||||
|
||||
final class running extends VarArgFunction {
|
||||
public Varargs invoke(Varargs args) {
|
||||
final LuaThread r = globals.running;
|
||||
return varargsOf(r, valueOf(r.isMainThread()));
|
||||
}
|
||||
}
|
||||
|
||||
static final class status extends LibFunction {
|
||||
public LuaValue call(LuaValue t) {
|
||||
LuaThread lt = t.checkthread();
|
||||
return valueOf( lt.getStatus() );
|
||||
}
|
||||
}
|
||||
|
||||
final class yield extends VarArgFunction {
|
||||
public Varargs invoke(Varargs args) {
|
||||
return globals.yield( args );
|
||||
}
|
||||
}
|
||||
|
||||
final class wrap extends LibFunction {
|
||||
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;
|
||||
}
|
||||
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() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
899
luaj-core/src/main/java/org/luaj/vm2/lib/DebugLib.java
Normal file
899
luaj-core/src/main/java/org/luaj/vm2/lib/DebugLib.java
Normal file
@@ -0,0 +1,899 @@
|
||||
/*******************************************************************************
|
||||
* 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.Globals;
|
||||
import org.luaj.vm2.Lua;
|
||||
import org.luaj.vm2.LuaBoolean;
|
||||
import org.luaj.vm2.LuaClosure;
|
||||
import org.luaj.vm2.LuaError;
|
||||
import org.luaj.vm2.LuaFunction;
|
||||
import org.luaj.vm2.LuaNil;
|
||||
import org.luaj.vm2.LuaNumber;
|
||||
import org.luaj.vm2.LuaString;
|
||||
import org.luaj.vm2.LuaTable;
|
||||
import org.luaj.vm2.LuaThread;
|
||||
import org.luaj.vm2.LuaUserdata;
|
||||
import org.luaj.vm2.LuaValue;
|
||||
import org.luaj.vm2.Print;
|
||||
import org.luaj.vm2.Prototype;
|
||||
import org.luaj.vm2.Varargs;
|
||||
|
||||
/**
|
||||
* Subclass of {@link LibFunction} which implements the lua standard {@code debug}
|
||||
* library.
|
||||
* <p>
|
||||
* The debug library in luaj tries to emulate the behavior of the corresponding C-based lua library.
|
||||
* To do this, it must maintain a separate stack of calls to {@link LuaClosure} and {@link LibFunction}
|
||||
* instances.
|
||||
* Especially when lua-to-java bytecode compiling is being used
|
||||
* via a {@link org.luaj.vm2.Globals.Compiler} such as {@link org.luaj.vm2.luajc.LuaJC},
|
||||
* this cannot be done in all cases.
|
||||
* <p>
|
||||
* Typically, this library is included as part of a call to either
|
||||
* {@link org.luaj.vm2.lib.jse.JsePlatform#debugGlobals()} or
|
||||
* {@link org.luaj.vm2.lib.jme.JmePlatform#debugGlobals()}
|
||||
* <pre> {@code
|
||||
* Globals globals = JsePlatform.debugGlobals();
|
||||
* System.out.println( globals.get("debug").get("traceback").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> {@code
|
||||
* Globals globals = new Globals();
|
||||
* globals.load(new JseBaseLib());
|
||||
* globals.load(new PackageLib());
|
||||
* globals.load(new DebugLib());
|
||||
* System.out.println( globals.get("debug").get("traceback").call() );
|
||||
* } </pre>
|
||||
* <p>
|
||||
* This library exposes the entire state of lua code, and provides method to see and modify
|
||||
* all underlying lua values within a Java VM so should not be exposed to client code
|
||||
* in a shared server environment.
|
||||
*
|
||||
* @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.10">Lua 5.2 Debug Lib Reference</a>
|
||||
*/
|
||||
public class DebugLib extends TwoArgFunction {
|
||||
public static boolean CALLS;
|
||||
public static boolean TRACE;
|
||||
static {
|
||||
try { CALLS = (null != System.getProperty("CALLS")); } catch (Exception e) {}
|
||||
try { TRACE = (null != System.getProperty("TRACE")); } catch (Exception e) {}
|
||||
}
|
||||
|
||||
static final LuaString LUA = valueOf("Lua");
|
||||
private static final LuaString QMARK = valueOf("?");
|
||||
private static final LuaString CALL = valueOf("call");
|
||||
private static final LuaString LINE = valueOf("line");
|
||||
private static final LuaString COUNT = valueOf("count");
|
||||
private static final LuaString RETURN = valueOf("return");
|
||||
|
||||
static final LuaString FUNC = valueOf("func");
|
||||
static final LuaString ISTAILCALL = valueOf("istailcall");
|
||||
static final LuaString ISVARARG = valueOf("isvararg");
|
||||
static final LuaString NUPS = valueOf("nups");
|
||||
static final LuaString NPARAMS = valueOf("nparams");
|
||||
static final LuaString NAME = valueOf("name");
|
||||
static final LuaString NAMEWHAT = valueOf("namewhat");
|
||||
static final LuaString WHAT = valueOf("what");
|
||||
static final LuaString SOURCE = valueOf("source");
|
||||
static final LuaString SHORT_SRC = valueOf("short_src");
|
||||
static final LuaString LINEDEFINED = valueOf("linedefined");
|
||||
static final LuaString LASTLINEDEFINED = valueOf("lastlinedefined");
|
||||
static final LuaString CURRENTLINE = valueOf("currentline");
|
||||
static final LuaString ACTIVELINES = valueOf("activelines");
|
||||
|
||||
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.
|
||||
*/
|
||||
public LuaValue call(LuaValue modname, LuaValue env) {
|
||||
globals = env.checkglobals();
|
||||
globals.debuglib = this;
|
||||
LuaTable debug = new LuaTable();
|
||||
debug.set("debug", new debug());
|
||||
debug.set("gethook", new gethook());
|
||||
debug.set("getinfo", new getinfo());
|
||||
debug.set("getlocal", new getlocal());
|
||||
debug.set("getmetatable", new getmetatable());
|
||||
debug.set("getregistry", new getregistry());
|
||||
debug.set("getupvalue", new getupvalue());
|
||||
debug.set("getuservalue", new getuservalue());
|
||||
debug.set("sethook", new sethook());
|
||||
debug.set("setlocal", new setlocal());
|
||||
debug.set("setmetatable", new setmetatable());
|
||||
debug.set("setupvalue", new setupvalue());
|
||||
debug.set("setuservalue", new setuservalue());
|
||||
debug.set("traceback", new traceback());
|
||||
debug.set("upvalueid", new upvalueid());
|
||||
debug.set("upvaluejoin", new upvaluejoin());
|
||||
env.set("debug", debug);
|
||||
if (!env.get("package").isnil()) env.get("package").get("loaded").set("debug", debug);
|
||||
return debug;
|
||||
}
|
||||
|
||||
// debug.debug()
|
||||
static final class debug extends ZeroArgFunction {
|
||||
public LuaValue call() {
|
||||
return NONE;
|
||||
}
|
||||
}
|
||||
|
||||
// debug.gethook ([thread])
|
||||
final class gethook extends VarArgFunction {
|
||||
public Varargs invoke(Varargs args) {
|
||||
LuaThread t = args.narg() > 0 ? args.checkthread(1): globals.running;
|
||||
LuaThread.State s = t.state;
|
||||
return varargsOf(
|
||||
s.hookfunc != null? s.hookfunc: NIL,
|
||||
valueOf((s.hookcall?"c":"")+(s.hookline?"l":"")+(s.hookrtrn?"r":"")),
|
||||
valueOf(s.hookcount));
|
||||
}
|
||||
}
|
||||
|
||||
// debug.getinfo ([thread,] f [, what])
|
||||
final class getinfo extends VarArgFunction {
|
||||
public Varargs invoke(Varargs args) {
|
||||
int a=1;
|
||||
LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running;
|
||||
LuaValue func = args.arg(a++);
|
||||
String what = args.optjstring(a++, "flnStu");
|
||||
DebugLib.CallStack callstack = callstack(thread);
|
||||
|
||||
// find the stack info
|
||||
DebugLib.CallFrame frame;
|
||||
if ( func.isnumber() ) {
|
||||
frame = callstack.getCallFrame(func.toint());
|
||||
if (frame == null)
|
||||
return NONE;
|
||||
func = frame.f;
|
||||
} else if ( func.isfunction() ) {
|
||||
frame = callstack.findCallFrame(func);
|
||||
} else {
|
||||
return argerror(a-2, "function or level");
|
||||
}
|
||||
|
||||
// start a table
|
||||
DebugInfo ar = callstack.auxgetinfo(what, (LuaFunction) func, frame);
|
||||
LuaTable info = new LuaTable();
|
||||
if (what.indexOf('S') >= 0) {
|
||||
info.set(WHAT, LUA);
|
||||
info.set(SOURCE, valueOf(ar.source));
|
||||
info.set(SHORT_SRC, valueOf(ar.short_src));
|
||||
info.set(LINEDEFINED, valueOf(ar.linedefined));
|
||||
info.set(LASTLINEDEFINED, valueOf(ar.lastlinedefined));
|
||||
}
|
||||
if (what.indexOf('l') >= 0) {
|
||||
info.set( CURRENTLINE, valueOf(ar.currentline) );
|
||||
}
|
||||
if (what.indexOf('u') >= 0) {
|
||||
info.set(NUPS, valueOf(ar.nups));
|
||||
info.set(NPARAMS, valueOf(ar.nparams));
|
||||
info.set(ISVARARG, ar.isvararg? ONE: ZERO);
|
||||
}
|
||||
if (what.indexOf('n') >= 0) {
|
||||
info.set(NAME, LuaValue.valueOf(ar.name!=null? ar.name: "?"));
|
||||
info.set(NAMEWHAT, LuaValue.valueOf(ar.namewhat));
|
||||
}
|
||||
if (what.indexOf('t') >= 0) {
|
||||
info.set(ISTAILCALL, ZERO);
|
||||
}
|
||||
if (what.indexOf('L') >= 0) {
|
||||
LuaTable lines = new LuaTable();
|
||||
info.set(ACTIVELINES, lines);
|
||||
DebugLib.CallFrame cf;
|
||||
for (int l = 1; (cf=callstack.getCallFrame(l)) != null; ++l)
|
||||
if (cf.f == func)
|
||||
lines.insert(-1, valueOf(cf.currentline()));
|
||||
}
|
||||
if (what.indexOf('f') >= 0) {
|
||||
if (func != null)
|
||||
info.set( FUNC, func );
|
||||
}
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
// debug.getlocal ([thread,] f, local)
|
||||
final class getlocal extends VarArgFunction {
|
||||
public Varargs invoke(Varargs args) {
|
||||
int a=1;
|
||||
LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running;
|
||||
int level = args.checkint(a++);
|
||||
int local = args.checkint(a++);
|
||||
CallFrame f = callstack(thread).getCallFrame(level);
|
||||
return f != null? f.getLocal(local): NONE;
|
||||
}
|
||||
}
|
||||
|
||||
// debug.getmetatable (value)
|
||||
static final class getmetatable extends LibFunction {
|
||||
public LuaValue call(LuaValue v) {
|
||||
LuaValue mt = v.getmetatable();
|
||||
return mt != null? mt: NIL;
|
||||
}
|
||||
}
|
||||
|
||||
// debug.getregistry ()
|
||||
final class getregistry extends ZeroArgFunction {
|
||||
public LuaValue call() {
|
||||
return globals;
|
||||
}
|
||||
}
|
||||
|
||||
// debug.getupvalue (f, up)
|
||||
static final class getupvalue extends VarArgFunction {
|
||||
public Varargs invoke(Varargs args) {
|
||||
LuaValue func = args.checkfunction(1);
|
||||
int up = args.checkint(2);
|
||||
if ( func instanceof LuaClosure ) {
|
||||
LuaClosure c = (LuaClosure) func;
|
||||
LuaString name = findupvalue(c, up);
|
||||
if ( name != null ) {
|
||||
return varargsOf(name, c.upValues[up-1].getValue() );
|
||||
}
|
||||
}
|
||||
return NIL;
|
||||
}
|
||||
}
|
||||
|
||||
// debug.getuservalue (u)
|
||||
static final class getuservalue extends LibFunction {
|
||||
public LuaValue call(LuaValue u) {
|
||||
return u.isuserdata()? u: NIL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// debug.sethook ([thread,] hook, mask [, count])
|
||||
final class sethook extends VarArgFunction {
|
||||
public Varargs invoke(Varargs args) {
|
||||
int a=1;
|
||||
LuaThread t = args.isthread(a)? args.checkthread(a++): globals.running;
|
||||
LuaValue func = args.optfunction(a++, null);
|
||||
String str = args.optjstring(a++,"");
|
||||
int count = args.optint(a++,0);
|
||||
boolean call=false,line=false,rtrn=false;
|
||||
for ( int i=0; i<str.length(); i++ )
|
||||
switch ( str.charAt(i) ) {
|
||||
case 'c': call=true; break;
|
||||
case 'l': line=true; break;
|
||||
case 'r': rtrn=true; break;
|
||||
}
|
||||
LuaThread.State s = t.state;
|
||||
s.hookfunc = func;
|
||||
s.hookcall = call;
|
||||
s.hookline = line;
|
||||
s.hookcount = count;
|
||||
s.hookrtrn = rtrn;
|
||||
return NONE;
|
||||
}
|
||||
}
|
||||
|
||||
// debug.setlocal ([thread,] level, local, value)
|
||||
final class setlocal extends VarArgFunction {
|
||||
public Varargs invoke(Varargs args) {
|
||||
int a=1;
|
||||
LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running;
|
||||
int level = args.checkint(a++);
|
||||
int local = args.checkint(a++);
|
||||
LuaValue value = args.arg(a++);
|
||||
CallFrame f = callstack(thread).getCallFrame(level);
|
||||
return f != null? f.setLocal(local, value): NONE;
|
||||
}
|
||||
}
|
||||
|
||||
// debug.setmetatable (value, table)
|
||||
static final class setmetatable extends TwoArgFunction {
|
||||
public LuaValue call(LuaValue value, LuaValue table) {
|
||||
LuaValue mt = table.opttable(null);
|
||||
switch ( value.type() ) {
|
||||
case TNIL: LuaNil.s_metatable = mt; break;
|
||||
case TNUMBER: LuaNumber.s_metatable = mt; break;
|
||||
case TBOOLEAN: LuaBoolean.s_metatable = mt; break;
|
||||
case TSTRING: LuaString.s_metatable = mt; break;
|
||||
case TFUNCTION: LuaFunction.s_metatable = mt; break;
|
||||
case TTHREAD: LuaThread.s_metatable = mt; break;
|
||||
default: value.setmetatable( mt );
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
// debug.setupvalue (f, up, value)
|
||||
static final class setupvalue extends VarArgFunction {
|
||||
public Varargs invoke(Varargs args) {
|
||||
LuaValue func = args.checkfunction(1);
|
||||
int up = args.checkint(2);
|
||||
LuaValue value = args.arg(3);
|
||||
if ( func instanceof LuaClosure ) {
|
||||
LuaClosure c = (LuaClosure) func;
|
||||
LuaString name = findupvalue(c, up);
|
||||
if ( name != null ) {
|
||||
c.upValues[up-1].setValue(value);
|
||||
return name;
|
||||
}
|
||||
}
|
||||
return NIL;
|
||||
}
|
||||
}
|
||||
|
||||
// debug.setuservalue (udata, value)
|
||||
static final class setuservalue extends VarArgFunction {
|
||||
public Varargs invoke(Varargs args) {
|
||||
Object o = args.checkuserdata(1);
|
||||
LuaValue v = args.checkvalue(2);
|
||||
LuaUserdata u = (LuaUserdata) args.arg1();
|
||||
u.m_instance = v.checkuserdata();
|
||||
u.m_metatable = v.getmetatable();
|
||||
return NONE;
|
||||
}
|
||||
}
|
||||
|
||||
// debug.traceback ([thread,] [message [, level]])
|
||||
final class traceback extends VarArgFunction {
|
||||
public Varargs invoke(Varargs args) {
|
||||
int a=1;
|
||||
LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running;
|
||||
String message = args.optjstring(a++, null);
|
||||
int level = args.optint(a++,1);
|
||||
String tb = callstack(thread).traceback(level);
|
||||
return valueOf(message!=null? message+"\n"+tb: tb);
|
||||
}
|
||||
}
|
||||
|
||||
// debug.upvalueid (f, n)
|
||||
static final class upvalueid extends VarArgFunction {
|
||||
public Varargs invoke(Varargs args) {
|
||||
LuaValue func = args.checkfunction(1);
|
||||
int up = args.checkint(2);
|
||||
if ( func instanceof LuaClosure ) {
|
||||
LuaClosure c = (LuaClosure) func;
|
||||
if ( c.upValues != null && up > 0 && up <= c.upValues.length ) {
|
||||
return valueOf(c.upValues[up-1].hashCode());
|
||||
}
|
||||
}
|
||||
return NIL;
|
||||
}
|
||||
}
|
||||
|
||||
// debug.upvaluejoin (f1, n1, f2, n2)
|
||||
static final class upvaluejoin extends VarArgFunction {
|
||||
public Varargs invoke(Varargs args) {
|
||||
LuaClosure f1 = args.checkclosure(1);
|
||||
int n1 = args.checkint(2);
|
||||
LuaClosure f2 = args.checkclosure(3);
|
||||
int n2 = args.checkint(4);
|
||||
if (n1 < 1 || n1 > f1.upValues.length)
|
||||
argerror("index out of range");
|
||||
if (n2 < 1 || n2 > f2.upValues.length)
|
||||
argerror("index out of range");
|
||||
f1.upValues[n1-1] = f2.upValues[n2-1];
|
||||
return NONE;
|
||||
}
|
||||
}
|
||||
|
||||
public void onCall(LuaFunction f) {
|
||||
LuaThread.State s = globals.running.state;
|
||||
if (s.inhook) return;
|
||||
callstack().onCall(f);
|
||||
if (s.hookcall) callHook(s, CALL, NIL);
|
||||
}
|
||||
|
||||
public void onCall(LuaClosure c, Varargs varargs, LuaValue[] stack) {
|
||||
LuaThread.State s = globals.running.state;
|
||||
if (s.inhook) return;
|
||||
callstack().onCall(c, varargs, stack);
|
||||
if (s.hookcall) callHook(s, CALL, NIL);
|
||||
}
|
||||
|
||||
public void onInstruction(int pc, Varargs v, int top) {
|
||||
LuaThread.State s = globals.running.state;
|
||||
if (s.inhook) return;
|
||||
callstack().onInstruction(pc, v, top);
|
||||
if (s.hookfunc == null) return;
|
||||
if (s.hookcount > 0)
|
||||
if (++s.bytecodes % s.hookcount == 0)
|
||||
callHook(s, COUNT, NIL);
|
||||
if (s.hookline) {
|
||||
int newline = callstack().currentline();
|
||||
if ( newline != s.lastline ) {
|
||||
s.lastline = newline;
|
||||
callHook(s, LINE, LuaValue.valueOf(newline));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onReturn() {
|
||||
LuaThread.State s = globals.running.state;
|
||||
if (s.inhook) return;
|
||||
callstack().onReturn();
|
||||
if (s.hookrtrn) callHook(s, RETURN, NIL);
|
||||
}
|
||||
|
||||
public String traceback(int level) {
|
||||
return callstack().traceback(level);
|
||||
}
|
||||
|
||||
public CallFrame getCallFrame(int level) {
|
||||
return callstack().getCallFrame(level);
|
||||
}
|
||||
|
||||
void callHook(LuaThread.State s, LuaValue type, LuaValue arg) {
|
||||
if (s.inhook || s.hookfunc == null) return;
|
||||
s.inhook = true;
|
||||
try {
|
||||
s.hookfunc.call(type, arg);
|
||||
} catch (LuaError e) {
|
||||
throw e;
|
||||
} catch (RuntimeException e) {
|
||||
throw new LuaError(e);
|
||||
} finally {
|
||||
s.inhook = false;
|
||||
}
|
||||
}
|
||||
|
||||
CallStack callstack() {
|
||||
return callstack(globals.running);
|
||||
}
|
||||
|
||||
CallStack callstack(LuaThread t) {
|
||||
if (t.callstack == null)
|
||||
t.callstack = new CallStack();
|
||||
return (CallStack) t.callstack;
|
||||
}
|
||||
|
||||
static class DebugInfo {
|
||||
String name; /* (n) */
|
||||
String namewhat; /* (n) 'global', 'local', 'field', 'method' */
|
||||
String what; /* (S) 'Lua', 'C', 'main', 'tail' */
|
||||
String source; /* (S) */
|
||||
int currentline; /* (l) */
|
||||
int linedefined; /* (S) */
|
||||
int lastlinedefined; /* (S) */
|
||||
short nups; /* (u) number of upvalues */
|
||||
short nparams;/* (u) number of parameters */
|
||||
boolean isvararg; /* (u) */
|
||||
boolean istailcall; /* (t) */
|
||||
String short_src; /* (S) */
|
||||
CallFrame cf; /* active function */
|
||||
|
||||
public void funcinfo(LuaFunction f) {
|
||||
if (f.isclosure()) {
|
||||
Prototype p = f.checkclosure().p;
|
||||
this.source = p.source != null ? p.source.tojstring() : "=?";
|
||||
this.linedefined = p.linedefined;
|
||||
this.lastlinedefined = p.lastlinedefined;
|
||||
this.what = (this.linedefined == 0) ? "main" : "Lua";
|
||||
this.short_src = p.shortsource();
|
||||
} else {
|
||||
this.source = "=[Java]";
|
||||
this.linedefined = -1;
|
||||
this.lastlinedefined = -1;
|
||||
this.what = "Java";
|
||||
this.short_src = f.name();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class CallStack {
|
||||
final static CallFrame[] EMPTY = {};
|
||||
CallFrame[] frame = EMPTY;
|
||||
int calls = 0;
|
||||
|
||||
CallStack() {}
|
||||
|
||||
synchronized int currentline() {
|
||||
return calls > 0? frame[calls-1].currentline(): -1;
|
||||
}
|
||||
|
||||
private synchronized CallFrame pushcall() {
|
||||
if (calls >= frame.length) {
|
||||
int n = Math.max(4, frame.length * 3 / 2);
|
||||
CallFrame[] f = new CallFrame[n];
|
||||
System.arraycopy(frame, 0, f, 0, frame.length);
|
||||
for (int i = frame.length; i < n; ++i)
|
||||
f[i] = new CallFrame();
|
||||
frame = f;
|
||||
for (int i = 1; i < n; ++i)
|
||||
f[i].previous = f[i-1];
|
||||
}
|
||||
return frame[calls++];
|
||||
}
|
||||
|
||||
final synchronized void onCall(LuaFunction function) {
|
||||
pushcall().set(function);
|
||||
}
|
||||
|
||||
final synchronized void onCall(LuaClosure function, Varargs varargs, LuaValue[] stack) {
|
||||
pushcall().set(function, varargs, stack);
|
||||
}
|
||||
|
||||
final synchronized void onReturn() {
|
||||
if (calls > 0)
|
||||
frame[--calls].reset();
|
||||
}
|
||||
|
||||
final synchronized void onInstruction(int pc, Varargs v, int top) {
|
||||
if (calls > 0)
|
||||
frame[calls-1].instr(pc, v, top);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the traceback starting at a specific level.
|
||||
* @param level
|
||||
* @return String containing the traceback.
|
||||
*/
|
||||
synchronized String traceback(int level) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append( "stack traceback:" );
|
||||
for (DebugLib.CallFrame c; (c = getCallFrame(level++)) != null; ) {
|
||||
sb.append("\n\t");
|
||||
sb.append( c.shortsource() );
|
||||
sb.append( ':' );
|
||||
if (c.currentline() > 0)
|
||||
sb.append( c.currentline()+":" );
|
||||
sb.append( " in " );
|
||||
DebugInfo ar = auxgetinfo("n", c.f, c);
|
||||
if (c.linedefined() == 0)
|
||||
sb.append("main chunk");
|
||||
else if ( ar.name != null ) {
|
||||
sb.append( "function '" );
|
||||
sb.append( ar.name );
|
||||
sb.append( '\'' );
|
||||
} else {
|
||||
sb.append( "function <" );
|
||||
sb.append( c.shortsource() );
|
||||
sb.append( ':' );
|
||||
sb.append( c.linedefined() );
|
||||
sb.append( '>' );
|
||||
}
|
||||
}
|
||||
sb.append("\n\t[Java]: in ?");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
synchronized DebugLib.CallFrame getCallFrame(int level) {
|
||||
if (level < 1 || level > calls)
|
||||
return null;
|
||||
return frame[calls-level];
|
||||
}
|
||||
|
||||
synchronized DebugLib.CallFrame findCallFrame(LuaValue func) {
|
||||
for (int i = 1; i <= calls; ++i)
|
||||
if (frame[calls-i].f == func)
|
||||
return frame[i];
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
synchronized DebugInfo auxgetinfo(String what, LuaFunction f, CallFrame ci) {
|
||||
DebugInfo ar = new DebugInfo();
|
||||
for (int i = 0, n = what.length(); i < n; ++i) {
|
||||
switch (what.charAt(i)) {
|
||||
case 'S':
|
||||
ar.funcinfo(f);
|
||||
break;
|
||||
case 'l':
|
||||
ar.currentline = ci != null && ci.f.isclosure()? ci.currentline(): -1;
|
||||
break;
|
||||
case 'u':
|
||||
if (f != null && f.isclosure()) {
|
||||
Prototype p = f.checkclosure().p;
|
||||
ar.nups = (short) p.upvalues.length;
|
||||
ar.nparams = (short) p.numparams;
|
||||
ar.isvararg = p.is_vararg != 0;
|
||||
} else {
|
||||
ar.nups = 0;
|
||||
ar.isvararg = true;
|
||||
ar.nparams = 0;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
ar.istailcall = false;
|
||||
break;
|
||||
case 'n': {
|
||||
/* calling function is a known Lua function? */
|
||||
if (ci != null && ci.previous != null) {
|
||||
if (ci.previous.f.isclosure()) {
|
||||
NameWhat nw = getfuncname(ci.previous);
|
||||
if (nw != null) {
|
||||
ar.name = nw.name;
|
||||
ar.namewhat = nw.namewhat;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ar.namewhat == null) {
|
||||
ar.namewhat = ""; /* not found */
|
||||
ar.name = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'L':
|
||||
case 'f':
|
||||
break;
|
||||
default:
|
||||
// TODO: return bad status.
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ar;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class CallFrame {
|
||||
LuaFunction f;
|
||||
int pc;
|
||||
int top;
|
||||
Varargs v;
|
||||
LuaValue[] stack;
|
||||
CallFrame previous;
|
||||
void set(LuaClosure function, Varargs varargs, LuaValue[] stack) {
|
||||
this.f = function;
|
||||
this.v = varargs;
|
||||
this.stack = stack;
|
||||
}
|
||||
public String shortsource() {
|
||||
return f.isclosure()? f.checkclosure().p.shortsource(): "[Java]";
|
||||
}
|
||||
void set(LuaFunction function) {
|
||||
this.f = function;
|
||||
}
|
||||
void reset() {
|
||||
this.f = null;
|
||||
this.v = null;
|
||||
this.stack = null;
|
||||
}
|
||||
void instr(int pc, Varargs v, int top) {
|
||||
this.pc = pc;
|
||||
this.v = v;
|
||||
this.top = top;
|
||||
if (TRACE)
|
||||
Print.printState(f.checkclosure(), pc, stack, top, v);
|
||||
}
|
||||
Varargs getLocal(int i) {
|
||||
LuaString name = getlocalname(i);
|
||||
if ( i >= 1 && i <= stack.length && stack[i-1] != null )
|
||||
return varargsOf( name == null ? NIL : name, stack[i-1] );
|
||||
else
|
||||
return NIL;
|
||||
}
|
||||
Varargs setLocal(int i, LuaValue value) {
|
||||
LuaString name = getlocalname(i);
|
||||
if ( i >= 1 && i <= stack.length && stack[i-1] != null ) {
|
||||
stack[i-1] = value;
|
||||
return name == null ? NIL : name;
|
||||
} else {
|
||||
return NIL;
|
||||
}
|
||||
}
|
||||
public int currentline() {
|
||||
if ( !f.isclosure() ) return -1;
|
||||
int[] li = f.checkclosure().p.lineinfo;
|
||||
return li==null || pc<0 || pc>=li.length? -1: li[pc];
|
||||
}
|
||||
String sourceline() {
|
||||
if ( !f.isclosure() ) return f.tojstring();
|
||||
return f.checkclosure().p.shortsource() + ":" + currentline();
|
||||
}
|
||||
int linedefined() {
|
||||
return f.isclosure()? f.checkclosure().p.linedefined: -1;
|
||||
}
|
||||
LuaString getlocalname(int index) {
|
||||
if ( !f.isclosure() ) return null;
|
||||
return f.checkclosure().p.getlocalname(index, pc);
|
||||
}
|
||||
}
|
||||
|
||||
static LuaString findupvalue(LuaClosure c, int up) {
|
||||
if ( c.upValues != null && up > 0 && up <= c.upValues.length ) {
|
||||
if ( c.p.upvalues != null && up <= c.p.upvalues.length )
|
||||
return c.p.upvalues[up-1].name;
|
||||
else
|
||||
return LuaString.valueOf( "."+up );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static void lua_assert(boolean x) {
|
||||
if (!x) throw new RuntimeException("lua_assert failed");
|
||||
}
|
||||
|
||||
static class NameWhat {
|
||||
final String name;
|
||||
final String namewhat;
|
||||
NameWhat(String name, String namewhat) {
|
||||
this.name = name;
|
||||
this.namewhat = namewhat;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the name info if found, or null if no useful information could be found.
|
||||
static NameWhat getfuncname(DebugLib.CallFrame frame) {
|
||||
if (!frame.f.isclosure())
|
||||
return new NameWhat(frame.f.classnamestub(), "Java");
|
||||
Prototype p = frame.f.checkclosure().p;
|
||||
int pc = frame.pc;
|
||||
int i = p.code[pc]; /* calling instruction */
|
||||
LuaString tm;
|
||||
switch (Lua.GET_OPCODE(i)) {
|
||||
case Lua.OP_CALL:
|
||||
case Lua.OP_TAILCALL: /* get function name */
|
||||
return getobjname(p, pc, Lua.GETARG_A(i));
|
||||
case Lua.OP_TFORCALL: /* for iterator */
|
||||
return new NameWhat("(for iterator)", "(for iterator");
|
||||
/* all other instructions can call only through metamethods */
|
||||
case Lua.OP_SELF:
|
||||
case Lua.OP_GETTABUP:
|
||||
case Lua.OP_GETTABLE: tm = LuaValue.INDEX; break;
|
||||
case Lua.OP_SETTABUP:
|
||||
case Lua.OP_SETTABLE: tm = LuaValue.NEWINDEX; break;
|
||||
case Lua.OP_EQ: tm = LuaValue.EQ; break;
|
||||
case Lua.OP_ADD: tm = LuaValue.ADD; break;
|
||||
case Lua.OP_SUB: tm = LuaValue.SUB; break;
|
||||
case Lua.OP_MUL: tm = LuaValue.MUL; break;
|
||||
case Lua.OP_DIV: tm = LuaValue.DIV; break;
|
||||
case Lua.OP_MOD: tm = LuaValue.MOD; break;
|
||||
case Lua.OP_POW: tm = LuaValue.POW; break;
|
||||
case Lua.OP_UNM: tm = LuaValue.UNM; break;
|
||||
case Lua.OP_LEN: tm = LuaValue.LEN; break;
|
||||
case Lua.OP_LT: tm = LuaValue.LT; break;
|
||||
case Lua.OP_LE: tm = LuaValue.LE; break;
|
||||
case Lua.OP_CONCAT: tm = LuaValue.CONCAT; break;
|
||||
default:
|
||||
return null; /* else no useful name can be found */
|
||||
}
|
||||
return new NameWhat( tm.tojstring(), "metamethod" );
|
||||
}
|
||||
|
||||
// return NameWhat if found, null if not
|
||||
public static NameWhat getobjname(Prototype p, int lastpc, int reg) {
|
||||
int pc = lastpc; // currentpc(L, ci);
|
||||
LuaString name = p.getlocalname(reg + 1, pc);
|
||||
if (name != null) /* is a local? */
|
||||
return new NameWhat( name.tojstring(), "local" );
|
||||
|
||||
/* else try symbolic execution */
|
||||
pc = findsetreg(p, lastpc, reg);
|
||||
if (pc != -1) { /* could find instruction? */
|
||||
int i = p.code[pc];
|
||||
switch (Lua.GET_OPCODE(i)) {
|
||||
case Lua.OP_MOVE: {
|
||||
int a = Lua.GETARG_A(i);
|
||||
int b = Lua.GETARG_B(i); /* move from `b' to `a' */
|
||||
if (b < a)
|
||||
return getobjname(p, pc, b); /* get name for `b' */
|
||||
break;
|
||||
}
|
||||
case Lua.OP_GETTABUP:
|
||||
case Lua.OP_GETTABLE: {
|
||||
int k = Lua.GETARG_C(i); /* key index */
|
||||
int t = Lua.GETARG_B(i); /* table index */
|
||||
LuaString vn = (Lua.GET_OPCODE(i) == Lua.OP_GETTABLE) /* name of indexed variable */
|
||||
? p.getlocalname(t + 1, pc)
|
||||
: (t < p.upvalues.length ? p.upvalues[t].name : QMARK);
|
||||
String jname = kname(p, pc, k);
|
||||
return new NameWhat( jname, vn != null && vn.eq_b(ENV)? "global": "field" );
|
||||
}
|
||||
case Lua.OP_GETUPVAL: {
|
||||
int u = Lua.GETARG_B(i); /* upvalue index */
|
||||
name = u < p.upvalues.length ? p.upvalues[u].name : QMARK;
|
||||
return name == null ? null : new NameWhat( name.tojstring(), "upvalue" );
|
||||
}
|
||||
case Lua.OP_LOADK:
|
||||
case Lua.OP_LOADKX: {
|
||||
int b = (Lua.GET_OPCODE(i) == Lua.OP_LOADK) ? Lua.GETARG_Bx(i)
|
||||
: Lua.GETARG_Ax(p.code[pc + 1]);
|
||||
if (p.k[b].isstring()) {
|
||||
name = p.k[b].strvalue();
|
||||
return new NameWhat( name.tojstring(), "constant" );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Lua.OP_SELF: {
|
||||
int k = Lua.GETARG_C(i); /* key index */
|
||||
String jname = kname(p, pc, k);
|
||||
return new NameWhat( jname, "method" );
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return null; /* no useful name found */
|
||||
}
|
||||
|
||||
static String kname(Prototype p, int pc, int c) {
|
||||
if (Lua.ISK(c)) { /* is 'c' a constant? */
|
||||
LuaValue k = p.k[Lua.INDEXK(c)];
|
||||
if (k.isstring()) { /* literal constant? */
|
||||
return k.tojstring(); /* it is its own name */
|
||||
} /* else no reasonable name found */
|
||||
} else { /* 'c' is a register */
|
||||
NameWhat what = getobjname(p, pc, c); /* search for 'c' */
|
||||
if (what != null && "constant".equals(what.namewhat)) { /* found a constant name? */
|
||||
return what.name; /* 'name' already filled */
|
||||
}
|
||||
/* else no reasonable name found */
|
||||
}
|
||||
return "?"; /* no reasonable name found */
|
||||
}
|
||||
|
||||
/*
|
||||
** try to find last instruction before 'lastpc' that modified register 'reg'
|
||||
*/
|
||||
static int findsetreg (Prototype p, int lastpc, int reg) {
|
||||
int pc;
|
||||
int setreg = -1; /* keep last instruction that changed 'reg' */
|
||||
for (pc = 0; pc < lastpc; pc++) {
|
||||
int i = p.code[pc];
|
||||
int op = Lua.GET_OPCODE(i);
|
||||
int a = Lua.GETARG_A(i);
|
||||
switch (op) {
|
||||
case Lua.OP_LOADNIL: {
|
||||
int b = Lua.GETARG_B(i);
|
||||
if (a <= reg && reg <= a + b) /* set registers from 'a' to 'a+b' */
|
||||
setreg = pc;
|
||||
break;
|
||||
}
|
||||
case Lua.OP_TFORCALL: {
|
||||
if (reg >= a + 2) setreg = pc; /* affect all regs above its base */
|
||||
break;
|
||||
}
|
||||
case Lua.OP_CALL:
|
||||
case Lua.OP_TAILCALL: {
|
||||
if (reg >= a) setreg = pc; /* affect all registers above base */
|
||||
break;
|
||||
}
|
||||
case Lua.OP_JMP: {
|
||||
int b = Lua.GETARG_sBx(i);
|
||||
int dest = pc + 1 + b;
|
||||
/* jump is forward and do not skip `lastpc'? */
|
||||
if (pc < dest && dest <= lastpc)
|
||||
pc += b; /* do the jump */
|
||||
break;
|
||||
}
|
||||
case Lua.OP_TEST: {
|
||||
if (reg == a) setreg = pc; /* jumped code can change 'a' */
|
||||
break;
|
||||
}
|
||||
case Lua.OP_SETLIST: { // Lua.testAMode(Lua.OP_SETLIST) == false
|
||||
if ( ((i>>14)&0x1ff) == 0 ) pc++; // if c == 0 then c stored in next op -> skip
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (Lua.testAMode(op) && reg == a) /* any instruction that set A */
|
||||
setreg = pc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return setreg;
|
||||
}
|
||||
}
|
||||
684
luaj-core/src/main/java/org/luaj/vm2/lib/IoLib.java
Normal file
684
luaj-core/src/main/java/org/luaj/vm2/lib/IoLib.java
Normal file
@@ -0,0 +1,684 @@
|
||||
/*******************************************************************************
|
||||
* 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> {@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> {@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>
|
||||
*/
|
||||
abstract
|
||||
public class IoLib extends TwoArgFunction {
|
||||
|
||||
abstract
|
||||
protected class File extends LuaValue{
|
||||
abstract public void write( LuaString string ) throws IOException;
|
||||
abstract public void flush() throws IOException;
|
||||
abstract public boolean isstdfile();
|
||||
abstract public void close() throws IOException;
|
||||
abstract public boolean isclosed();
|
||||
// returns new position
|
||||
abstract public int seek(String option, int bytecount) throws IOException;
|
||||
abstract public void setvbuf(String mode, int size);
|
||||
// get length remaining to read
|
||||
abstract public int remaining() throws IOException;
|
||||
// peek ahead one character
|
||||
abstract public int peek() throws IOException, EOFException;
|
||||
// return char if read, -1 if eof, throw IOException on other exception
|
||||
abstract public int read() throws IOException, EOFException;
|
||||
// return number of bytes read if positive, false if eof, throw IOException on other exception
|
||||
abstract public int read(byte[] bytes, int offset, int length) throws IOException;
|
||||
|
||||
public boolean eof() throws IOException {
|
||||
try {
|
||||
return peek() < 0;
|
||||
} catch (EOFException e) { return true; }
|
||||
}
|
||||
|
||||
// delegate method access to file methods table
|
||||
public LuaValue get( LuaValue key ) {
|
||||
return filemethods.get(key);
|
||||
}
|
||||
|
||||
// essentially a userdata instance
|
||||
public int type() {
|
||||
return LuaValue.TUSERDATA;
|
||||
}
|
||||
public String typename() {
|
||||
return "userdata";
|
||||
}
|
||||
|
||||
// displays as "file" type
|
||||
public String tojstring() {
|
||||
return "file: " + Integer.toHexString(hashCode());
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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) {
|
||||
super();
|
||||
this.f = f;
|
||||
this.name = name;
|
||||
this.opcode = opcode;
|
||||
this.iolib = iolib;
|
||||
}
|
||||
|
||||
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) continue;
|
||||
if (i == 1 && ch == '+') continue;
|
||||
if (i >= 1 && ch == 'b') continue;
|
||||
len = -1;
|
||||
break;
|
||||
}
|
||||
if (len <= 0) argerror(2, "invalid mode: '" + mode + "'");
|
||||
|
||||
switch (filetype) {
|
||||
case FTYPE_STDIN: return wrapStdin();
|
||||
case FTYPE_STDOUT: return wrapStdout();
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
222
luaj-core/src/main/java/org/luaj/vm2/lib/LibFunction.java
Normal file
222
luaj-core/src/main/java/org/luaj/vm2/lib/LibFunction.java
Normal file
@@ -0,0 +1,222 @@
|
||||
/*******************************************************************************
|
||||
* 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"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() {
|
||||
}
|
||||
|
||||
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 };
|
||||
}
|
||||
|
||||
public LuaValue call() {
|
||||
return argerror(1,"value expected");
|
||||
}
|
||||
public LuaValue call(LuaValue a) {
|
||||
return call();
|
||||
}
|
||||
public LuaValue call(LuaValue a, LuaValue b) {
|
||||
return call(a);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
306
luaj-core/src/main/java/org/luaj/vm2/lib/MathLib.java
Normal file
306
luaj-core/src/main/java/org/luaj/vm2/lib/MathLib.java
Normal file
@@ -0,0 +1,306 @@
|
||||
/*******************************************************************************
|
||||
* 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> {@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> {@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.
|
||||
*/
|
||||
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 {
|
||||
public LuaValue call(LuaValue arg) {
|
||||
return valueOf(call(arg.checkdouble()));
|
||||
}
|
||||
abstract protected double call(double d);
|
||||
}
|
||||
|
||||
abstract protected static class BinaryOp extends TwoArgFunction {
|
||||
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 { protected double call(double d) { return Math.abs(d); } }
|
||||
static final class ceil extends UnaryOp { protected double call(double d) { return Math.ceil(d); } }
|
||||
static final class cos extends UnaryOp { protected double call(double d) { return Math.cos(d); } }
|
||||
static final class deg extends UnaryOp { protected double call(double d) { return Math.toDegrees(d); } }
|
||||
static final class floor extends UnaryOp { protected double call(double d) { return Math.floor(d); } }
|
||||
static final class rad extends UnaryOp { protected double call(double d) { return Math.toRadians(d); } }
|
||||
static final class sin extends UnaryOp { protected double call(double d) { return Math.sin(d); } }
|
||||
static final class sqrt extends UnaryOp { protected double call(double d) { return Math.sqrt(d); } }
|
||||
static final class tan extends UnaryOp { protected double call(double d) { return Math.tan(d); } }
|
||||
|
||||
static final class exp extends UnaryOp {
|
||||
final MathLib mathlib;
|
||||
exp(MathLib mathlib) {
|
||||
this.mathlib = mathlib;
|
||||
}
|
||||
protected double call(double d) {
|
||||
return mathlib.dpow_lib(Math.E,d);
|
||||
}
|
||||
}
|
||||
|
||||
static final class fmod extends TwoArgFunction {
|
||||
public LuaValue call(LuaValue xv, LuaValue yv) {
|
||||
if (xv.islong() && yv.islong()) {
|
||||
return valueOf(xv.tolong() % yv.tolong());
|
||||
}
|
||||
return valueOf(xv.checkdouble() % yv.checkdouble());
|
||||
}
|
||||
}
|
||||
static final class ldexp extends BinaryOp {
|
||||
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 {
|
||||
protected double call(double x, double y) {
|
||||
return MathLib.dpow_default(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
static class frexp extends VarArgFunction {
|
||||
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 {
|
||||
public Varargs invoke(Varargs args) {
|
||||
LuaValue m = args.checkvalue(1);
|
||||
for ( int i=2,n=args.narg(); i<=n; ++i ) {
|
||||
LuaValue v = args.checkvalue(i);
|
||||
if (m.lt_b(v)) m = v;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
||||
static class min extends VarArgFunction {
|
||||
public Varargs invoke(Varargs args) {
|
||||
LuaValue m = args.checkvalue(1);
|
||||
for ( int i=2,n=args.narg(); i<=n; ++i ) {
|
||||
LuaValue v = args.checkvalue(i);
|
||||
if (v.lt_b(m)) m = v;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
||||
static class modf extends VarArgFunction {
|
||||
public Varargs invoke(Varargs args) {
|
||||
LuaValue n = args.arg1();
|
||||
/* number is its own integer part, no fractional part */
|
||||
if (n.islong()) return varargsOf(n, 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();
|
||||
public LuaValue call() {
|
||||
return valueOf( random.nextDouble() );
|
||||
}
|
||||
public LuaValue call(LuaValue a) {
|
||||
int m = a.checkint();
|
||||
if (m<1) argerror(1, "interval is empty");
|
||||
return valueOf( 1 + random.nextInt(m) );
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
72
luaj-core/src/main/java/org/luaj/vm2/lib/OneArgFunction.java
Normal file
72
luaj-core/src/main/java/org/luaj/vm2/lib/OneArgFunction.java
Normal file
@@ -0,0 +1,72 @@
|
||||
/*******************************************************************************
|
||||
* 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 {
|
||||
|
||||
abstract public LuaValue call(LuaValue arg);
|
||||
|
||||
/** Default constructor */
|
||||
public OneArgFunction() {
|
||||
}
|
||||
|
||||
public final LuaValue call() {
|
||||
return call(NIL);
|
||||
}
|
||||
|
||||
public final LuaValue call(LuaValue arg1, LuaValue arg2) {
|
||||
return call(arg1);
|
||||
}
|
||||
|
||||
public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
|
||||
return call(arg1);
|
||||
}
|
||||
|
||||
public Varargs invoke(Varargs varargs) {
|
||||
return call(varargs.arg1());
|
||||
}
|
||||
}
|
||||
524
luaj-core/src/main/java/org/luaj/vm2/lib/OsLib.java
Normal file
524
luaj-core/src/main/java/org/luaj/vm2/lib/OsLib.java
Normal file
@@ -0,0 +1,524 @@
|
||||
/*******************************************************************************
|
||||
* 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.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
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> {@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> {@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.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
public Varargs invoke(Varargs args) {
|
||||
try {
|
||||
switch ( opcode ) {
|
||||
case CLOCK:
|
||||
return valueOf(clock());
|
||||
case DATE: {
|
||||
String s = args.optjstring(1, "%c");
|
||||
double t = args.isnumber(2)? args.todouble(2): time(null);
|
||||
if (s.equals("*t")) {
|
||||
Calendar d = Calendar.getInstance();
|
||||
d.setTime(new Date((long)(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 time 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.
|
||||
*/
|
||||
public String date(String format, double time) {
|
||||
Calendar d = Calendar.getInstance();
|
||||
d.setTime(new Date((long)(time*1000)));
|
||||
if (format.startsWith("!")) {
|
||||
time -= timeZoneOffset(d);
|
||||
d.setTime(new Date((long)(time*1000)));
|
||||
format = format.substring(1);
|
||||
}
|
||||
byte[] fmt = format.getBytes();
|
||||
final int n = fmt.length;
|
||||
Buffer result = new Buffer(n);
|
||||
byte c;
|
||||
for ( int i = 0; i < n; ) {
|
||||
switch ( c = fmt[i++ ] ) {
|
||||
case '\n':
|
||||
result.append( "\n" );
|
||||
break;
|
||||
default:
|
||||
result.append( c );
|
||||
break;
|
||||
case '%':
|
||||
if (i >= n) break;
|
||||
switch ( c = fmt[i++ ] ) {
|
||||
default:
|
||||
LuaValue.argerror(1, "invalid conversion specifier '%"+c+"'");
|
||||
break;
|
||||
case '%':
|
||||
result.append( (byte)'%' );
|
||||
break;
|
||||
case 'a':
|
||||
result.append(WeekdayNameAbbrev[d.get(Calendar.DAY_OF_WEEK)-1]);
|
||||
break;
|
||||
case 'A':
|
||||
result.append(WeekdayName[d.get(Calendar.DAY_OF_WEEK)-1]);
|
||||
break;
|
||||
case 'b':
|
||||
result.append(MonthNameAbbrev[d.get(Calendar.MONTH)]);
|
||||
break;
|
||||
case 'B':
|
||||
result.append(MonthName[d.get(Calendar.MONTH)]);
|
||||
break;
|
||||
case 'c':
|
||||
result.append(date("%a %b %d %H:%M:%S %Y", time));
|
||||
break;
|
||||
case 'd':
|
||||
result.append(String.valueOf(100+d.get(Calendar.DAY_OF_MONTH)).substring(1));
|
||||
break;
|
||||
case 'H':
|
||||
result.append(String.valueOf(100+d.get(Calendar.HOUR_OF_DAY)).substring(1));
|
||||
break;
|
||||
case 'I':
|
||||
result.append(String.valueOf(100+(d.get(Calendar.HOUR_OF_DAY)%12)).substring(1));
|
||||
break;
|
||||
case 'j': { // day of year.
|
||||
Calendar y0 = beginningOfYear(d);
|
||||
int dayOfYear = (int) ((d.getTime().getTime() - y0.getTime().getTime()) / (24 * 3600L * 1000L));
|
||||
result.append(String.valueOf(1001+dayOfYear).substring(1));
|
||||
break;
|
||||
}
|
||||
case 'm':
|
||||
result.append(String.valueOf(101+d.get(Calendar.MONTH)).substring(1));
|
||||
break;
|
||||
case 'M':
|
||||
result.append(String.valueOf(100+d.get(Calendar.MINUTE)).substring(1));
|
||||
break;
|
||||
case 'p':
|
||||
result.append(d.get(Calendar.HOUR_OF_DAY) < 12? "AM": "PM");
|
||||
break;
|
||||
case 'S':
|
||||
result.append(String.valueOf(100+d.get(Calendar.SECOND)).substring(1));
|
||||
break;
|
||||
case 'U':
|
||||
result.append(String.valueOf(weekNumber(d, 0)));
|
||||
break;
|
||||
case 'w':
|
||||
result.append(String.valueOf((d.get(Calendar.DAY_OF_WEEK)+6)%7));
|
||||
break;
|
||||
case 'W':
|
||||
result.append(String.valueOf(weekNumber(d, 1)));
|
||||
break;
|
||||
case 'x':
|
||||
result.append(date("%m/%d/%y", time));
|
||||
break;
|
||||
case 'X':
|
||||
result.append(date("%H:%M:%S", time));
|
||||
break;
|
||||
case 'y':
|
||||
result.append(String.valueOf(d.get(Calendar.YEAR)).substring(2));
|
||||
break;
|
||||
case 'Y':
|
||||
result.append(String.valueOf(d.get(Calendar.YEAR)));
|
||||
break;
|
||||
case 'z': {
|
||||
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);
|
||||
result.append((tzo>=0? "+": "-") + h + m);
|
||||
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 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 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 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 double 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() / 1000.;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
381
luaj-core/src/main/java/org/luaj/vm2/lib/PackageLib.java
Normal file
381
luaj-core/src/main/java/org/luaj/vm2/lib/PackageLib.java
Normal file
@@ -0,0 +1,381 @@
|
||||
/*******************************************************************************
|
||||
* 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 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> {@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");
|
||||
|
||||
/** 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 = System.getProperty("file.separator");
|
||||
|
||||
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.
|
||||
*/
|
||||
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());
|
||||
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));
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
public Varargs invoke( Varargs args ) {
|
||||
args.checkstring(1);
|
||||
return varargsOf(NIL, valueOf("dynamic libraries not enabled"), valueOf("absent"));
|
||||
}
|
||||
}
|
||||
|
||||
public class preload_searcher extends VarArgFunction {
|
||||
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 {
|
||||
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 {
|
||||
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 java_searcher extends VarArgFunction {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
59
luaj-core/src/main/java/org/luaj/vm2/lib/ResourceFinder.java
Normal file
59
luaj-core/src/main/java/org/luaj/vm2/lib/ResourceFinder.java
Normal file
@@ -0,0 +1,59 @@
|
||||
/*******************************************************************************
|
||||
* 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.
|
||||
*/
|
||||
public InputStream findResource( String filename );
|
||||
}
|
||||
1223
luaj-core/src/main/java/org/luaj/vm2/lib/StringLib.java
Normal file
1223
luaj-core/src/main/java/org/luaj/vm2/lib/StringLib.java
Normal file
File diff suppressed because it is too large
Load Diff
158
luaj-core/src/main/java/org/luaj/vm2/lib/TableLib.java
Normal file
158
luaj-core/src/main/java/org/luaj/vm2/lib/TableLib.java
Normal file
@@ -0,0 +1,158 @@
|
||||
/*******************************************************************************
|
||||
* 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> {@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> {@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.
|
||||
*/
|
||||
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 {
|
||||
public LuaValue call(LuaValue list) {
|
||||
return list.checktable().concat(EMPTYSTRING,1,list.length());
|
||||
}
|
||||
public LuaValue call(LuaValue list, LuaValue sep) {
|
||||
return list.checktable().concat(sep.checkstring(),1,list.length());
|
||||
}
|
||||
public LuaValue call(LuaValue list, LuaValue sep, LuaValue i) {
|
||||
return list.checktable().concat(sep.checkstring(),i.checkint(),list.length());
|
||||
}
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.luaj.vm2.lib;
|
||||
|
||||
import org.luaj.vm2.LuaValue;
|
||||
|
||||
class TableLibFunction extends LibFunction {
|
||||
public LuaValue call() {
|
||||
return argerror(1, "table expected, got no value");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*******************************************************************************
|
||||
* 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 {
|
||||
|
||||
abstract public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3);
|
||||
|
||||
/** Default constructor */
|
||||
public ThreeArgFunction() {
|
||||
}
|
||||
|
||||
public final LuaValue call() {
|
||||
return call(NIL, NIL, NIL);
|
||||
}
|
||||
|
||||
public final LuaValue call(LuaValue arg) {
|
||||
return call(arg, NIL, NIL);
|
||||
}
|
||||
|
||||
public LuaValue call(LuaValue arg1, LuaValue arg2) {
|
||||
return call(arg1, arg2, NIL);
|
||||
}
|
||||
|
||||
public Varargs invoke(Varargs varargs) {
|
||||
return call(varargs.arg1(),varargs.arg(2),varargs.arg(3));
|
||||
}
|
||||
|
||||
}
|
||||
73
luaj-core/src/main/java/org/luaj/vm2/lib/TwoArgFunction.java
Normal file
73
luaj-core/src/main/java/org/luaj/vm2/lib/TwoArgFunction.java
Normal file
@@ -0,0 +1,73 @@
|
||||
/*******************************************************************************
|
||||
* 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 {
|
||||
|
||||
abstract public LuaValue call(LuaValue arg1, LuaValue arg2);
|
||||
|
||||
/** Default constructor */
|
||||
public TwoArgFunction() {
|
||||
}
|
||||
|
||||
public final LuaValue call() {
|
||||
return call(NIL, NIL);
|
||||
}
|
||||
|
||||
public final LuaValue call(LuaValue arg) {
|
||||
return call(arg, NIL);
|
||||
}
|
||||
|
||||
public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
|
||||
return call(arg1, arg2);
|
||||
}
|
||||
|
||||
public Varargs invoke(Varargs varargs) {
|
||||
return call(varargs.arg1(),varargs.arg(2));
|
||||
}
|
||||
|
||||
}
|
||||
83
luaj-core/src/main/java/org/luaj/vm2/lib/VarArgFunction.java
Normal file
83
luaj-core/src/main/java/org/luaj/vm2/lib/VarArgFunction.java
Normal file
@@ -0,0 +1,83 @@
|
||||
/*******************************************************************************
|
||||
* 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() {
|
||||
}
|
||||
|
||||
public LuaValue call() {
|
||||
return invoke(NONE).arg1();
|
||||
}
|
||||
|
||||
public LuaValue call(LuaValue arg) {
|
||||
return invoke(arg).arg1();
|
||||
}
|
||||
|
||||
public LuaValue call(LuaValue arg1, LuaValue arg2) {
|
||||
return invoke(varargsOf(arg1,arg2)).arg1();
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
public Varargs invoke(Varargs args) {
|
||||
return onInvoke(args).eval();
|
||||
}
|
||||
|
||||
public Varargs onInvoke(Varargs args) {
|
||||
return invoke(args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*******************************************************************************
|
||||
* 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 {
|
||||
|
||||
abstract public LuaValue call();
|
||||
|
||||
/** Default constructor */
|
||||
public ZeroArgFunction() {
|
||||
}
|
||||
|
||||
public LuaValue call(LuaValue arg) {
|
||||
return call();
|
||||
}
|
||||
|
||||
public LuaValue call(LuaValue arg1, LuaValue arg2) {
|
||||
return call();
|
||||
}
|
||||
|
||||
public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
|
||||
return call();
|
||||
}
|
||||
|
||||
public Varargs invoke(Varargs varargs) {
|
||||
return call();
|
||||
}
|
||||
}
|
||||
349
luaj-core/src/main/javacc/Lua51.jj
Normal file
349
luaj-core/src/main/javacc/Lua51.jj
Normal file
@@ -0,0 +1,349 @@
|
||||
/**
|
||||
* Javacc grammar for lua language version 5.1
|
||||
*
|
||||
* Originally created for use in luaj, a Java implementation of the lua language
|
||||
* @see http://sourceforge.net/projects/luaj/
|
||||
*
|
||||
* For documentation on the lua language
|
||||
* @see http://www.lua.org/manual/5.1/
|
||||
*
|
||||
* Empty grammar that validates syntax without producing a parse tree.
|
||||
*
|
||||
* @author Jim Roseborough
|
||||
* @date June 21, 2010
|
||||
*/
|
||||
|
||||
options {
|
||||
STATIC = false;
|
||||
JDK_VERSION = "1.3";
|
||||
ERROR_REPORTING = false;
|
||||
DEBUG_LOOKAHEAD = false;
|
||||
DEBUG_PARSER = false;
|
||||
DEBUG_TOKEN_MANAGER = false;
|
||||
OUTPUT_DIRECTORY = "org/luaj/vm2/parser";
|
||||
}
|
||||
|
||||
PARSER_BEGIN(LuaParser)
|
||||
package org.luaj.vm2.parser;
|
||||
|
||||
public class LuaParser {
|
||||
|
||||
public static void main(String args[]) throws ParseException {
|
||||
LuaParser parser = new LuaParser(System.in);
|
||||
parser.Chunk();
|
||||
}
|
||||
|
||||
public static final int VAR = 0;
|
||||
public static final int CALL = 1;
|
||||
}
|
||||
|
||||
PARSER_END(LuaParser)
|
||||
|
||||
/* WHITE SPACE */
|
||||
|
||||
SKIP :
|
||||
{
|
||||
" " | "\t" | "\n" | "\r" | "\f"
|
||||
}
|
||||
|
||||
/* COMMENTS and LONG STRINGS */
|
||||
|
||||
MORE :
|
||||
{
|
||||
"--[[": IN_LC0
|
||||
| "--[=[": IN_LC1
|
||||
| "--[==[": IN_LC2
|
||||
| "--[===[": IN_LC3
|
||||
| < "--[====" ("=")* "[" > : IN_LCN
|
||||
| "[[" : IN_LS0
|
||||
| "[=[" : IN_LS1
|
||||
| "[==[" : IN_LS2
|
||||
| "[===[" : IN_LS3
|
||||
| < "[====" ("=")* "[" > : IN_LSN
|
||||
| "--" : IN_COMMENT
|
||||
}
|
||||
|
||||
<IN_COMMENT> SPECIAL_TOKEN :
|
||||
{
|
||||
<COMMENT: (~["\n","\r"])* ("\n"|"\r"|"\r\n")? > : DEFAULT
|
||||
}
|
||||
|
||||
<IN_LC0> SPECIAL_TOKEN : { <LONGCOMMENT0: "]]" > : DEFAULT }
|
||||
<IN_LC1> SPECIAL_TOKEN : { <LONGCOMMENT1: "]=]" > : DEFAULT }
|
||||
<IN_LC2> SPECIAL_TOKEN : { <LONGCOMMENT2: "]==]" > : DEFAULT }
|
||||
<IN_LC3> SPECIAL_TOKEN : { <LONGCOMMENT3: "]===]" > : DEFAULT }
|
||||
<IN_LCN> SPECIAL_TOKEN : { <LONGCOMMENTN: "]====" ("=")* "]" > : DEFAULT }
|
||||
|
||||
<IN_LS0> TOKEN : { <LONGSTRING0: "]]" > : DEFAULT }
|
||||
<IN_LS1> TOKEN : { <LONGSTRING1: "]=]" > : DEFAULT }
|
||||
<IN_LS2> TOKEN : { <LONGSTRING2: "]==]" > : DEFAULT }
|
||||
<IN_LS3> TOKEN : { <LONGSTRING3: "]===]" > : DEFAULT }
|
||||
<IN_LSN> TOKEN : { <LONGSTRINGN: "]====" ("=")* "]" > : DEFAULT }
|
||||
|
||||
<IN_LC0,IN_LC1,IN_LC2,IN_LC3,IN_LCN,IN_LS0,IN_LS1,IN_LS2,IN_LS3,IN_LSN> MORE :
|
||||
{
|
||||
< ~[] >
|
||||
}
|
||||
|
||||
|
||||
/* RESERVED WORDS AND LITERALS */
|
||||
|
||||
TOKEN :
|
||||
{
|
||||
<AND: "and">
|
||||
| <BREAK: "break">
|
||||
| <DO: "do">
|
||||
| <ELSE: "else">
|
||||
| <ELSEIF: "elseif">
|
||||
| <END: "end">
|
||||
| <FALSE: "false">
|
||||
| <FOR: "for">
|
||||
| <FUNCTION: "function">
|
||||
| <IF: "if">
|
||||
| <IN: "in">
|
||||
| <LOCAL: "local">
|
||||
| <NIL: "nil">
|
||||
| <NOT: "not">
|
||||
| <OR: "or">
|
||||
| <RETURN: "return">
|
||||
| <REPEAT: "repeat">
|
||||
| <THEN: "then">
|
||||
| <TRUE: "true">
|
||||
| <UNTIL: "until">
|
||||
| <WHILE: "while">
|
||||
}
|
||||
|
||||
/* LITERALS */
|
||||
|
||||
TOKEN :
|
||||
{
|
||||
< NAME: ["a"-"z", "A"-"Z", "_"] (["a"-"z", "A"-"Z", "_", "0"-"9"])* >
|
||||
| < NUMBER: <HEX> | <FLOAT> >
|
||||
| < #FLOAT: (<DIGIT>)+ "." (<DIGIT>)* (<EXP>)? | "." (<DIGIT>)+ (<EXP>)? | (<DIGIT>)+ (<EXP>)? >
|
||||
| < #DIGIT: ["0"-"9"] >
|
||||
| < #EXP: ["e","E"] (["+","-"])? (<DIGIT>)+ >
|
||||
| < #HEX: "0" ["x","X"] (<HEXDIGIT>)+ >
|
||||
| < #HEXDIGIT: ["0"-"9","a"-"f","A"-"F"] >
|
||||
| < STRING: "\"" (<QUOTED> | ~["\\","\""])* "\"" >
|
||||
| < CHARSTRING: "'" (<QUOTED> | ~["\\","'"])* "'" >
|
||||
| < #QUOTED: <DECIMAL> | <UNICODE> | <CHAR> >
|
||||
| < #DECIMAL: "\\" ["0"-"9"] (["0"-"9"])? (["0"-"9"])? >
|
||||
| < #UNICODE: "\\" "u" <HEXDIGIT> <HEXDIGIT> <HEXDIGIT> <HEXDIGIT> >
|
||||
//| < #CHAR: "\\" ("a"|"b"|"f"|"n"|"r"|"t"|"v"|"["|"]"|"'"|"\""|"\\"|"0"|<LF>) >
|
||||
| < #CHAR: "\\" (~[]) >
|
||||
| < #LF: ("\n" | "\r" | "\r\n") >
|
||||
}
|
||||
|
||||
/** Root production. */
|
||||
void Chunk():
|
||||
{}
|
||||
{
|
||||
Block() <EOF>
|
||||
}
|
||||
|
||||
void Block():
|
||||
{}
|
||||
{
|
||||
(Stat() (";")? )* (LastStat() (";")? )?
|
||||
}
|
||||
|
||||
void Stat():
|
||||
{}
|
||||
{
|
||||
<DO> Block() <END>
|
||||
| <WHILE> Exp() <DO> Block() <END>
|
||||
| <REPEAT> Block() <UNTIL> Exp()
|
||||
| <IF> Exp() <THEN> Block() (<ELSEIF> Exp() <THEN> Block())* (<ELSE> Block())? <END>
|
||||
| LOOKAHEAD(3) <FOR> <NAME> "=" Exp() "," Exp() ( "," Exp() )? <DO> Block() <END>
|
||||
| <FOR> NameList() <IN> ExpList() <DO> Block() <END>
|
||||
| <FUNCTION> FuncName() FuncBody()
|
||||
| LOOKAHEAD(2) <LOCAL> <FUNCTION> <NAME> FuncBody()
|
||||
| <LOCAL> NameList() ( "=" ExpList() )?
|
||||
| ExprStat()
|
||||
}
|
||||
|
||||
void LastStat():
|
||||
{}
|
||||
{
|
||||
<BREAK> | <RETURN> ( ExpList() )?
|
||||
}
|
||||
|
||||
void ExprStat():
|
||||
{ int type,need=CALL; }
|
||||
{
|
||||
type=PrimaryExp() ( Assign() { need=VAR; } )?
|
||||
{ if ( type!=need ) throw new ParseException("expected function call or assignment"); }
|
||||
}
|
||||
|
||||
void Assign():
|
||||
{}
|
||||
{
|
||||
( "," VarExp() )* "=" ExpList()
|
||||
}
|
||||
|
||||
void VarExp():
|
||||
{ int type; }
|
||||
{
|
||||
type=PrimaryExp()
|
||||
{ if ( type!=VAR ) throw new ParseException("expected variable expression"); }
|
||||
}
|
||||
|
||||
void FuncName():
|
||||
{}
|
||||
{
|
||||
<NAME> ( "." <NAME> )* ( ":" <NAME> )?
|
||||
}
|
||||
|
||||
void PrefixExp():
|
||||
{}
|
||||
{
|
||||
<NAME>
|
||||
| ParenExp()
|
||||
}
|
||||
|
||||
void ParenExp():
|
||||
{}
|
||||
{
|
||||
"(" Exp() ")"
|
||||
}
|
||||
|
||||
int PrimaryExp():
|
||||
{ int type=VAR; }
|
||||
{
|
||||
PrefixExp() ( LOOKAHEAD(2) type=PostfixOp() )* { return type; }
|
||||
}
|
||||
|
||||
int PostfixOp():
|
||||
{}
|
||||
{
|
||||
FieldOp() { return VAR; }
|
||||
| FuncOp() { return CALL; }
|
||||
}
|
||||
|
||||
void FieldOp():
|
||||
{}
|
||||
{
|
||||
"." <NAME>
|
||||
| "[" Exp() "]"
|
||||
}
|
||||
|
||||
void FuncOp():
|
||||
{}
|
||||
{
|
||||
":" <NAME> FuncArgs()
|
||||
| FuncArgs()
|
||||
}
|
||||
|
||||
void FuncArgs():
|
||||
{}
|
||||
{
|
||||
"(" ( ExpList() )? ")"
|
||||
| TableConstructor()
|
||||
| Str()
|
||||
}
|
||||
|
||||
void NameList():
|
||||
{}
|
||||
{
|
||||
<NAME> ( LOOKAHEAD(2) "," <NAME> )*
|
||||
}
|
||||
|
||||
void ExpList():
|
||||
{}
|
||||
{
|
||||
Exp() ( "," Exp() )*
|
||||
}
|
||||
|
||||
void SimpleExp():
|
||||
{}
|
||||
{
|
||||
<NIL>
|
||||
| <TRUE>
|
||||
| <FALSE>
|
||||
| <NUMBER>
|
||||
| Str()
|
||||
| "..."
|
||||
| TableConstructor()
|
||||
| Function()
|
||||
| PrimaryExp()
|
||||
}
|
||||
|
||||
void Str():
|
||||
{}
|
||||
{
|
||||
<STRING>
|
||||
| <CHARSTRING>
|
||||
| <LONGSTRING0>
|
||||
| <LONGSTRING1>
|
||||
| <LONGSTRING2>
|
||||
| <LONGSTRING3>
|
||||
| <LONGSTRINGN>
|
||||
}
|
||||
|
||||
void Exp():
|
||||
{}
|
||||
{
|
||||
SubExp()
|
||||
}
|
||||
|
||||
void SubExp():
|
||||
{}
|
||||
{
|
||||
( SimpleExp() | Unop() SubExp() ) (LOOKAHEAD(2) Binop() SubExp())*
|
||||
}
|
||||
|
||||
void Function():
|
||||
{}
|
||||
{
|
||||
<FUNCTION> FuncBody()
|
||||
}
|
||||
|
||||
void FuncBody():
|
||||
{}
|
||||
{
|
||||
"(" ( ParList() )? ")" Block() <END>
|
||||
}
|
||||
|
||||
void ParList():
|
||||
{}
|
||||
{
|
||||
NameList() ( "," "..." )? | "..."
|
||||
}
|
||||
|
||||
void TableConstructor():
|
||||
{}
|
||||
{
|
||||
"{" ( FieldList() )? "}"
|
||||
}
|
||||
|
||||
void FieldList():
|
||||
{}
|
||||
{
|
||||
Field() (LOOKAHEAD(2) FieldSep() Field())* (FieldSep())?
|
||||
}
|
||||
|
||||
void Field():
|
||||
{}
|
||||
{
|
||||
"[" Exp() "]" "=" Exp()
|
||||
| LOOKAHEAD(2) <NAME> "=" Exp()
|
||||
| Exp()
|
||||
}
|
||||
|
||||
void FieldSep():
|
||||
{}
|
||||
{
|
||||
"," | ";"
|
||||
}
|
||||
|
||||
void Binop():
|
||||
{}
|
||||
{
|
||||
"+" | "-" | "*" | "/" | "^" | "%" | ".." | "<" | "<=" | ">" | ">=" | "==" | "~=" | <AND> | <OR>
|
||||
}
|
||||
|
||||
void Unop():
|
||||
{}
|
||||
{
|
||||
"-" | <NOT> | "#"
|
||||
}
|
||||
363
luaj-core/src/main/javacc/Lua52.jj
Normal file
363
luaj-core/src/main/javacc/Lua52.jj
Normal file
@@ -0,0 +1,363 @@
|
||||
/**
|
||||
* Javacc grammar for lua language version 5.2
|
||||
*
|
||||
* Originally created for use in luaj, a Java implementation of the lua language
|
||||
* @see http://sourceforge.net/projects/luaj/
|
||||
*
|
||||
* For documentation on the lua language
|
||||
* @see http://www.lua.org/manual/5.2/
|
||||
*
|
||||
* Empty grammar that validates syntax without producing a parse tree.
|
||||
*
|
||||
* @author Jim Roseborough
|
||||
* @date August 30, 2012
|
||||
*/
|
||||
|
||||
options {
|
||||
STATIC = false;
|
||||
JDK_VERSION = "1.3";
|
||||
ERROR_REPORTING = false;
|
||||
DEBUG_LOOKAHEAD = false;
|
||||
DEBUG_PARSER = false;
|
||||
DEBUG_TOKEN_MANAGER = false;
|
||||
OUTPUT_DIRECTORY = "org/luaj/vm2/parser";
|
||||
}
|
||||
|
||||
PARSER_BEGIN(LuaParser)
|
||||
package org.luaj.vm2.parser;
|
||||
|
||||
public class LuaParser {
|
||||
|
||||
public static void main(String args[]) throws ParseException {
|
||||
LuaParser parser = new LuaParser(System.in);
|
||||
parser.Chunk();
|
||||
}
|
||||
|
||||
public static final int VAR = 0;
|
||||
public static final int CALL = 1;
|
||||
}
|
||||
|
||||
PARSER_END(LuaParser)
|
||||
|
||||
/* WHITE SPACE */
|
||||
|
||||
SKIP :
|
||||
{
|
||||
" " | "\t" | "\n" | "\r" | "\f"
|
||||
}
|
||||
|
||||
/* COMMENTS and LONG STRINGS */
|
||||
|
||||
MORE :
|
||||
{
|
||||
"--[[": IN_LC0
|
||||
| "--[=[": IN_LC1
|
||||
| "--[==[": IN_LC2
|
||||
| "--[===[": IN_LC3
|
||||
| < "--[====" ("=")* "[" > : IN_LCN
|
||||
| "[[" : IN_LS0
|
||||
| "[=[" : IN_LS1
|
||||
| "[==[" : IN_LS2
|
||||
| "[===[" : IN_LS3
|
||||
| < "[====" ("=")* "[" > : IN_LSN
|
||||
| "--" : IN_COMMENT
|
||||
}
|
||||
|
||||
<IN_COMMENT> SPECIAL_TOKEN :
|
||||
{
|
||||
<COMMENT: (~["\n","\r"])* ("\n"|"\r"|"\r\n")? > : DEFAULT
|
||||
}
|
||||
|
||||
<IN_LC0> SPECIAL_TOKEN : { <LONGCOMMENT0: "]]" > : DEFAULT }
|
||||
<IN_LC1> SPECIAL_TOKEN : { <LONGCOMMENT1: "]=]" > : DEFAULT }
|
||||
<IN_LC2> SPECIAL_TOKEN : { <LONGCOMMENT2: "]==]" > : DEFAULT }
|
||||
<IN_LC3> SPECIAL_TOKEN : { <LONGCOMMENT3: "]===]" > : DEFAULT }
|
||||
<IN_LCN> SPECIAL_TOKEN : { <LONGCOMMENTN: "]====" ("=")* "]" > : DEFAULT }
|
||||
|
||||
<IN_LS0> TOKEN : { <LONGSTRING0: "]]" > : DEFAULT }
|
||||
<IN_LS1> TOKEN : { <LONGSTRING1: "]=]" > : DEFAULT }
|
||||
<IN_LS2> TOKEN : { <LONGSTRING2: "]==]" > : DEFAULT }
|
||||
<IN_LS3> TOKEN : { <LONGSTRING3: "]===]" > : DEFAULT }
|
||||
<IN_LSN> TOKEN : { <LONGSTRINGN: "]====" ("=")* "]" > : DEFAULT }
|
||||
|
||||
<IN_LC0,IN_LC1,IN_LC2,IN_LC3,IN_LCN,IN_LS0,IN_LS1,IN_LS2,IN_LS3,IN_LSN> MORE :
|
||||
{
|
||||
< ~[] >
|
||||
}
|
||||
|
||||
|
||||
/* RESERVED WORDS AND LITERALS */
|
||||
|
||||
TOKEN :
|
||||
{
|
||||
<AND: "and">
|
||||
| <BREAK: "break">
|
||||
| <DO: "do">
|
||||
| <ELSE: "else">
|
||||
| <ELSEIF: "elseif">
|
||||
| <END: "end">
|
||||
| <FALSE: "false">
|
||||
| <FOR: "for">
|
||||
| <FUNCTION: "function">
|
||||
| <GOTO: "goto">
|
||||
| <IF: "if">
|
||||
| <IN: "in">
|
||||
| <LOCAL: "local">
|
||||
| <NIL: "nil">
|
||||
| <NOT: "not">
|
||||
| <OR: "or">
|
||||
| <RETURN: "return">
|
||||
| <REPEAT: "repeat">
|
||||
| <THEN: "then">
|
||||
| <TRUE: "true">
|
||||
| <UNTIL: "until">
|
||||
| <WHILE: "while">
|
||||
}
|
||||
|
||||
/* LITERALS */
|
||||
|
||||
TOKEN :
|
||||
{
|
||||
< NAME: ["a"-"z", "A"-"Z", "_"] (["a"-"z", "A"-"Z", "_", "0"-"9"])* >
|
||||
| < NUMBER: <HEX> | <FLOAT> >
|
||||
| < #FLOAT: <FNUM> (<EXP>)? >
|
||||
| < #FNUM: (<DIGIT>)+ "." (<DIGIT>)* | "." (<DIGIT>)+ | (<DIGIT>)+ >
|
||||
| < #DIGIT: ["0"-"9"] >
|
||||
| < #EXP: ["e","E"] (["+","-"])? (<DIGIT>)+ >
|
||||
| < #HEX: "0" ["x","X"] <HEXNUM> (<HEXEXP>)? >
|
||||
| < #HEXNUM: (<HEXDIGIT>)+ "." (<HEXDIGIT>)* | "." (<HEXDIGIT>)+ | (<HEXDIGIT>)+ >
|
||||
| < #HEXDIGIT: ["0"-"9","a"-"f","A"-"F"] >
|
||||
| < #HEXEXP: ["e","E","p","P"] (["+","-"])? (<DIGIT>)+ >
|
||||
| < STRING: "\"" (<QUOTED> | ~["\\","\""])* "\"" >
|
||||
| < CHARSTRING: "'" (<QUOTED> | ~["\\","'"])* "'" >
|
||||
| < #QUOTED: <DECIMAL> | <UNICODE> | <CHAR> >
|
||||
| < #DECIMAL: "\\" ["0"-"9"] (["0"-"9"])? (["0"-"9"])? >
|
||||
| < DBCOLON: "::" >
|
||||
| < #UNICODE: "\\" "u" <HEXDIGIT> <HEXDIGIT> <HEXDIGIT> <HEXDIGIT> >
|
||||
| < #CHAR: "\\" (~[]) >
|
||||
| < #LF: ("\n" | "\r" | "\r\n") >
|
||||
}
|
||||
|
||||
/** Root production. */
|
||||
void Chunk():
|
||||
{}
|
||||
{
|
||||
( "#" { token_source.SwitchTo(IN_COMMENT); } )? Block() <EOF>
|
||||
}
|
||||
|
||||
void Block():
|
||||
{}
|
||||
{
|
||||
( Stat() )* ( ReturnStat() )?
|
||||
}
|
||||
|
||||
void Stat():
|
||||
{}
|
||||
{
|
||||
";"
|
||||
| Label()
|
||||
| <BREAK>
|
||||
| <GOTO> <NAME>
|
||||
| <DO> Block() <END>
|
||||
| <WHILE> Exp() <DO> Block() <END>
|
||||
| <REPEAT> Block() <UNTIL> Exp()
|
||||
| <IF> Exp() <THEN> Block() (<ELSEIF> Exp() <THEN> Block())* (<ELSE> Block())? <END>
|
||||
| LOOKAHEAD(3) <FOR> <NAME> "=" Exp() "," Exp() ( "," Exp() )? <DO> Block() <END>
|
||||
| <FOR> NameList() <IN> ExpList() <DO> Block() <END>
|
||||
| <FUNCTION> FuncName() FuncBody()
|
||||
| LOOKAHEAD(2) <LOCAL> <FUNCTION> <NAME> FuncBody()
|
||||
| <LOCAL> NameList() ( "=" ExpList() )?
|
||||
| ExprStat()
|
||||
}
|
||||
|
||||
void ReturnStat():
|
||||
{}
|
||||
{
|
||||
<RETURN> ( ExpList() )? ( ";" )?
|
||||
}
|
||||
|
||||
void Label():
|
||||
{}
|
||||
{
|
||||
<DBCOLON> <NAME> <DBCOLON>
|
||||
}
|
||||
|
||||
void ExprStat():
|
||||
{ int type,need=CALL; }
|
||||
{
|
||||
type=PrimaryExp() ( Assign() { need=VAR; } )?
|
||||
{ if ( type!=need ) throw new ParseException("expected function call or assignment"); }
|
||||
}
|
||||
|
||||
void Assign():
|
||||
{}
|
||||
{
|
||||
( "," VarExp() )* "=" ExpList()
|
||||
}
|
||||
|
||||
void VarExp():
|
||||
{ int type; }
|
||||
{
|
||||
type=PrimaryExp()
|
||||
{ if ( type!=VAR ) throw new ParseException("expected variable expression"); }
|
||||
}
|
||||
|
||||
void FuncName():
|
||||
{}
|
||||
{
|
||||
<NAME> ( "." <NAME> )* ( ":" <NAME> )?
|
||||
}
|
||||
|
||||
void PrefixExp():
|
||||
{}
|
||||
{
|
||||
<NAME>
|
||||
| ParenExp()
|
||||
}
|
||||
|
||||
void ParenExp():
|
||||
{}
|
||||
{
|
||||
"(" Exp() ")"
|
||||
}
|
||||
|
||||
int PrimaryExp():
|
||||
{ int type=VAR; }
|
||||
{
|
||||
PrefixExp() ( LOOKAHEAD(2) type=PostfixOp() )* { return type; }
|
||||
}
|
||||
|
||||
int PostfixOp():
|
||||
{}
|
||||
{
|
||||
FieldOp() { return VAR; }
|
||||
| FuncOp() { return CALL; }
|
||||
}
|
||||
|
||||
void FieldOp():
|
||||
{}
|
||||
{
|
||||
"." <NAME>
|
||||
| "[" Exp() "]"
|
||||
}
|
||||
|
||||
void FuncOp():
|
||||
{}
|
||||
{
|
||||
":" <NAME> FuncArgs()
|
||||
| FuncArgs()
|
||||
}
|
||||
|
||||
void FuncArgs():
|
||||
{}
|
||||
{
|
||||
"(" ( ExpList() )? ")"
|
||||
| TableConstructor()
|
||||
| Str()
|
||||
}
|
||||
|
||||
void NameList():
|
||||
{}
|
||||
{
|
||||
<NAME> ( LOOKAHEAD(2) "," <NAME> )*
|
||||
}
|
||||
|
||||
void ExpList():
|
||||
{}
|
||||
{
|
||||
Exp() ( "," Exp() )*
|
||||
}
|
||||
|
||||
void SimpleExp():
|
||||
{}
|
||||
{
|
||||
<NIL>
|
||||
| <TRUE>
|
||||
| <FALSE>
|
||||
| <NUMBER>
|
||||
| Str()
|
||||
| "..."
|
||||
| TableConstructor()
|
||||
| FunctionCall()
|
||||
| PrimaryExp()
|
||||
}
|
||||
|
||||
void Str():
|
||||
{}
|
||||
{
|
||||
<STRING>
|
||||
| <CHARSTRING>
|
||||
| <LONGSTRING0>
|
||||
| <LONGSTRING1>
|
||||
| <LONGSTRING2>
|
||||
| <LONGSTRING3>
|
||||
| <LONGSTRINGN>
|
||||
}
|
||||
|
||||
void Exp():
|
||||
{}
|
||||
{
|
||||
SubExp()
|
||||
}
|
||||
|
||||
void SubExp():
|
||||
{}
|
||||
{
|
||||
( SimpleExp() | Unop() SubExp() ) (LOOKAHEAD(2) Binop() SubExp())*
|
||||
}
|
||||
|
||||
void FunctionCall():
|
||||
{}
|
||||
{
|
||||
<FUNCTION> FuncBody()
|
||||
}
|
||||
|
||||
void FuncBody():
|
||||
{}
|
||||
{
|
||||
"(" ( ParList() )? ")" Block() <END>
|
||||
}
|
||||
|
||||
void ParList():
|
||||
{}
|
||||
{
|
||||
NameList() ( "," "..." )? | "..."
|
||||
}
|
||||
|
||||
void TableConstructor():
|
||||
{}
|
||||
{
|
||||
"{" ( FieldList() )? "}"
|
||||
}
|
||||
|
||||
void FieldList():
|
||||
{}
|
||||
{
|
||||
Field() (LOOKAHEAD(2) FieldSep() Field())* (FieldSep())?
|
||||
}
|
||||
|
||||
void Field():
|
||||
{}
|
||||
{
|
||||
"[" Exp() "]" "=" Exp()
|
||||
| LOOKAHEAD(2) <NAME> "=" Exp()
|
||||
| Exp()
|
||||
}
|
||||
|
||||
void FieldSep():
|
||||
{}
|
||||
{
|
||||
"," | ";"
|
||||
}
|
||||
|
||||
void Binop():
|
||||
{}
|
||||
{
|
||||
"+" | "-" | "*" | "/" | "^" | "%" | ".." | "<" | "<=" | ">" | ">=" | "==" | "~=" | <AND> | <OR>
|
||||
}
|
||||
|
||||
void Unop():
|
||||
{}
|
||||
{
|
||||
"-" | <NOT> | "#"
|
||||
}
|
||||
0
luaj-core/src/main/resources/.keep
Normal file
0
luaj-core/src/main/resources/.keep
Normal file
0
luaj-core/src/test/java/.keep
Normal file
0
luaj-core/src/test/java/.keep
Normal file
0
luaj-core/src/test/resources/.keep
Normal file
0
luaj-core/src/test/resources/.keep
Normal file
Reference in New Issue
Block a user