Implemented Support of Java 21 VirtualThread

This commit is contained in:
UnlegitDqrk
2026-03-01 12:39:42 +01:00
parent 40831d0f2d
commit f40e89e19c
216 changed files with 2172 additions and 16083 deletions

134
jse/pom.xml Normal file
View File

@@ -0,0 +1,134 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openautonomousconnection.luaj</groupId>
<artifactId>parent</artifactId>
<version>3.0.2</version>
</parent>
<artifactId>jse</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.openautonomousconnection.luaj</groupId>
<artifactId>core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.bcel</groupId>
<artifactId>bcel</artifactId>
<version>6.12.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
<executions>
<execution>
<id>copy-sources</id>
<phase>generate-sources</phase>
<goals><goal>copy-resources</goal></goals>
<configuration>
<outputDirectory>${project.build.directory}/generated-sources/luaj-jse</outputDirectory>
<resources>
<resource>
<directory>${project.basedir}/../luaj-core/src/main/java</directory>
<includes><include>**/*.java</include></includes>
</resource>
<resource>
<directory>${project.basedir}/src/main/java</directory>
<includes><include>**/*.java</include></includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.google.code.maven-replacer-plugin</groupId>
<artifactId>replacer</artifactId>
<executions>
<execution>
<id>rewrite-branding-and-cleanup</id>
<phase>generate-sources</phase>
<goals><goal>replace</goal></goals>
<configuration>
<basedir>${project.build.directory}/generated-sources/luaj-jse</basedir>
<includes>
<include>**/*.java</include>
</includes>
<replacements>
<replacement>
<token>"Luaj 0.0"</token>
<value>"${luaj.brand.jse}"</value>
</replacement>
<replacement><token>&lt;String&gt;</token><value></value></replacement>
<replacement><token>&lt;Stat&gt;</token><value></value></replacement>
<replacement><token>&lt;Exp&gt;</token><value></value></replacement>
<replacement><token>&lt;Name&gt;</token><value></value></replacement>
<replacement><token>&lt;Block&gt;</token><value></value></replacement>
<replacement><token>&lt;TableField&gt;</token><value></value></replacement>
<replacement><token>&lt;VarExp&gt;</token><value></value></replacement>
<replacement><token>&lt;Exp.VarExp&gt;</token><value></value></replacement>
<replacement><token>&lt;Object,String&gt;</token><value></value></replacement>
<replacement><token>&lt;Double,String&gt;</token><value></value></replacement>
<replacement><token>&lt;Integer,Integer&gt;</token><value></value></replacement>
<replacement><token>&lt;Integer,LocalVariableGen&gt;</token><value></value></replacement>
<replacement><token>&lt;Exp,Integer&gt;</token><value></value></replacement>
<replacement><token>&lt;String,byte[]&gt;</token><value></value></replacement>
<replacement><token>&lt;String,Variable&gt;</token><value></value></replacement>
<replacement><token>&lt;LuaValue,String&gt;</token><value></value></replacement>
<replacement><token>&lt;LuaString,String&gt;</token><value></value></replacement>
</replacements>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add-generated-sources</id>
<phase>generate-sources</phase>
<goals><goal>add-source</goal></goals>
<configuration>
<sources>
<source>${project.build.directory}/generated-sources/luaj-jse</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.4.2</version>
<configuration>
<includes>
<include>**/*</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals><goal>jar</goal></goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

238
jse/src/main/java/lua.java Normal file
View File

@@ -0,0 +1,238 @@
/*******************************************************************************
* Copyright (c) 2009-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.
******************************************************************************/
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Vector;
import org.luaj.vm2.Globals;
import org.luaj.vm2.Lua;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Print;
import org.luaj.vm2.Varargs;
import org.luaj.vm2.lib.jse.JsePlatform;
import org.luaj.vm2.luajc.LuaJC;
/**
* lua command for use in JSE environments.
*/
public class lua {
private static final String version = Lua._VERSION + " Copyright (c) 2012 Luaj.org.org";
private static final String usage =
"usage: java -cp luaj-jse.jar lua [options] [script [args]].\n" +
"Available options are:\n" +
" -e stat execute string 'stat'\n" +
" -l name require library 'name'\n" +
" -i enter interactive mode after executing 'script'\n" +
" -v show version information\n" +
" -b use luajc bytecode-to-bytecode compiler (requires bcel on class path)\n" +
" -n nodebug - do not load debug library by default\n" +
" -p print the prototype\n" +
" -c enc use the supplied encoding 'enc' for input files\n" +
" -- stop handling options\n" +
" - execute stdin and stop handling options";
private static void usageExit() {
System.out.println(usage);
System.exit(-1);
}
private static Globals globals;
private static boolean print = false;
private static String encoding = null;
public static void main( String[] args ) throws IOException {
// process args
boolean interactive = (args.length == 0);
boolean versioninfo = false;
boolean processing = true;
boolean nodebug = false;
boolean luajc = false;
Vector libs = null;
try {
// stateful argument processing
for ( int i=0; i<args.length; i++ ) {
if ( ! processing || ! args[i].startsWith("-") ) {
// input file - defer to last stage
break;
} else if ( args[i].length() <= 1 ) {
// input file - defer to last stage
break;
} else {
switch ( args[i].charAt(1) ) {
case 'e':
if ( ++i >= args.length )
usageExit();
// input script - defer to last stage
break;
case 'b':
luajc = true;
break;
case 'l':
if ( ++i >= args.length )
usageExit();
libs = libs!=null? libs: new Vector();
libs.addElement( args[i] );
break;
case 'i':
interactive = true;
break;
case 'v':
versioninfo = true;
break;
case 'n':
nodebug = true;
break;
case 'p':
print = true;
break;
case 'c':
if ( ++i >= args.length )
usageExit();
encoding = args[i];
break;
case '-':
if ( args[i].length() > 2 )
usageExit();
processing = false;
break;
default:
usageExit();
break;
}
}
}
// echo version
if ( versioninfo )
System.out.println(version);
// new lua state
globals = nodebug? JsePlatform.standardGlobals(): JsePlatform.debugGlobals();
if ( luajc ) LuaJC.install(globals);
for ( int i=0, n=libs!=null? libs.size(): 0; i<n; i++ )
loadLibrary( (String) libs.elementAt(i) );
// input script processing
processing = true;
for ( int i=0; i<args.length; i++ ) {
if ( ! processing || ! args[i].startsWith("-") ) {
processScript( new FileInputStream(args[i]), args[i], args, i );
break;
} else if ( "-".equals( args[i] ) ) {
processScript( System.in, "=stdin", args, i );
break;
} else {
switch ( args[i].charAt(1) ) {
case 'l':
case 'c':
++i;
break;
case 'e':
++i;
processScript( new ByteArrayInputStream(args[i].getBytes()), "string", args, i );
break;
case '-':
processing = false;
break;
}
}
}
if ( interactive )
interactiveMode();
} catch ( IOException ioe ) {
System.err.println( ioe.toString() );
System.exit(-2);
}
}
private static void loadLibrary( String libname ) throws IOException {
LuaValue slibname =LuaValue.valueOf(libname);
try {
// load via plain require
globals.get("require").call(slibname);
} catch ( Exception e ) {
try {
// load as java class
LuaValue v = (LuaValue) Class.forName(libname).newInstance();
v.call(slibname, globals);
} catch ( Exception f ) {
throw new IOException("loadLibrary("+libname+") failed: "+e+","+f );
}
}
}
private static void processScript( InputStream script, String chunkname, String[] args, int firstarg ) throws IOException {
try {
LuaValue c;
try {
script = new BufferedInputStream(script);
c = encoding != null?
globals.load(new InputStreamReader(script, encoding), chunkname):
globals.load(script, chunkname, "bt", globals);
} finally {
script.close();
}
if (print && c.isclosure())
Print.print(c.checkclosure().p);
Varargs scriptargs = setGlobalArg(chunkname, args, firstarg, globals);
c.invoke( scriptargs );
} catch ( Exception e ) {
e.printStackTrace( System.err );
}
}
private static Varargs setGlobalArg(String chunkname, String[] args, int i, LuaValue globals) {
if (args == null)
return LuaValue.NONE;
LuaTable arg = LuaValue.tableOf();
for ( int j=0; j<args.length; j++ )
arg.set( j-i, LuaValue.valueOf(args[j]) );
arg.set(0, LuaValue.valueOf(chunkname));
arg.set(-1, LuaValue.valueOf("luaj"));
globals.set("arg", arg);
return arg.unpack();
}
private static void interactiveMode( ) throws IOException {
BufferedReader reader = new BufferedReader( new InputStreamReader( System.in ) );
while ( true ) {
System.out.print("> ");
System.out.flush();
String line = reader.readLine();
if ( line == null )
return;
processScript( new ByteArrayInputStream(line.getBytes()), "=stdin", null, 0 );
}
}
}

194
jse/src/main/java/luac.java Normal file
View File

@@ -0,0 +1,194 @@
/*******************************************************************************
* 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.
******************************************************************************/
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import org.luaj.vm2.Globals;
import org.luaj.vm2.Lua;
import org.luaj.vm2.Print;
import org.luaj.vm2.Prototype;
import org.luaj.vm2.compiler.DumpState;
import org.luaj.vm2.lib.jse.JsePlatform;
/**
* Compiler for lua files to lua bytecode.
*/
public class luac {
private static final String version = Lua._VERSION + "Copyright (C) 2009 luaj.org";
private static final String usage =
"usage: java -cp luaj-jse.jar luac [options] [filenames].\n" +
"Available options are:\n" +
" - process stdin\n" +
" -l list\n" +
" -o name output to file 'name' (default is \"luac.out\")\n" +
" -p parse only\n" +
" -s strip debug information\n" +
" -e little endian format for numbers\n" +
" -i<n> number format 'n', (n=0,1 or 4, default="+DumpState.NUMBER_FORMAT_DEFAULT+")\n" +
" -v show version information\n" +
" -c enc use the supplied encoding 'enc' for input files\n" +
" -- stop handling options\n";
private static void usageExit() {
System.out.println(usage);
System.exit(-1);
}
private boolean list = false;
private String output = "luac.out";
private boolean parseonly = false;
private boolean stripdebug = false;
private boolean littleendian = false;
private int numberformat = DumpState.NUMBER_FORMAT_DEFAULT;
private boolean versioninfo = false;
private boolean processing = true;
private String encoding = null;
public static void main( String[] args ) throws IOException {
new luac( args );
}
private luac( String[] args ) throws IOException {
// process args
try {
// get stateful args
for ( int i=0; i<args.length; i++ ) {
if ( ! processing || ! args[i].startsWith("-") ) {
// input file - defer to next stage
} else if ( args[i].length() <= 1 ) {
// input file - defer to next stage
} else {
switch ( args[i].charAt(1) ) {
case 'l':
list = true;
break;
case 'o':
if ( ++i >= args.length )
usageExit();
output = args[i];
break;
case 'p':
parseonly = true;
break;
case 's':
stripdebug = true;
break;
case 'e':
littleendian = true;
break;
case 'i':
if ( args[i].length() <= 2 )
usageExit();
numberformat = Integer.parseInt(args[i].substring(2));
break;
case 'v':
versioninfo = true;
break;
case 'c':
if ( ++i >= args.length )
usageExit();
encoding = args[i];
break;
case '-':
if ( args[i].length() > 2 )
usageExit();
processing = false;
break;
default:
usageExit();
break;
}
}
}
// echo version
if ( versioninfo )
System.out.println(version);
// open output file
OutputStream fos = new FileOutputStream( output );
// process input files
try {
Globals globals = JsePlatform.standardGlobals();
processing = true;
for ( int i=0; i<args.length; i++ ) {
if ( ! processing || ! args[i].startsWith("-") ) {
String chunkname = args[i].substring(0,args[i].length()-4);
processScript( globals, new FileInputStream(args[i]), chunkname, fos );
} else if ( args[i].length() <= 1 ) {
processScript( globals, System.in, "=stdin", fos );
} else {
switch ( args[i].charAt(1) ) {
case 'o':
case 'c':
++i;
break;
case '-':
processing = false;
break;
}
}
}
} finally {
fos.close();
}
} catch ( IOException ioe ) {
System.err.println( ioe.toString() );
System.exit(-2);
}
}
private void processScript( Globals globals, InputStream script, String chunkname, OutputStream out ) throws IOException {
try {
// create the chunk
script = new BufferedInputStream(script);
Prototype chunk = encoding != null?
globals.compilePrototype(new InputStreamReader(script, encoding), chunkname):
globals.compilePrototype(script, chunkname);
// list the chunk
if (list)
Print.printCode(chunk);
// write out the chunk
if (!parseonly) {
DumpState.dump(chunk, out, stripdebug, numberformat, littleendian);
}
} catch ( Exception e ) {
e.printStackTrace( System.err );
} finally {
script.close();
}
}
}

View File

@@ -0,0 +1,270 @@
/*******************************************************************************
* Copyright (c) 2009-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.
******************************************************************************/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import org.luaj.vm2.Globals;
import org.luaj.vm2.Lua;
import org.luaj.vm2.lib.jse.JsePlatform;
import org.luaj.vm2.luajc.LuaJC;
/**
* Compiler for lua files to compile lua sources or lua binaries into java classes.
*/
public class luajc {
private static final String version = Lua._VERSION + " Copyright (C) 2012 luaj.org";
private static final String usage =
"usage: java -cp luaj-jse.jar,bcel-5.2.jar luajc [options] fileordir [, fileordir ...]\n" +
"Available options are:\n" +
" - process stdin\n" +
" -s src source directory\n" +
" -d dir destination directory\n" +
" -p pkg package prefix to apply to all classes\n" +
" -m generate main(String[]) function for JSE\n" +
" -r recursively compile all\n" +
" -l load classes to verify generated bytecode\n" +
" -c enc use the supplied encoding 'enc' for input files\n" +
" -v verbose\n";
private static void usageExit() {
System.out.println(usage);
System.exit(-1);
}
private String srcdir = ".";
private String destdir = ".";
private boolean genmain = false;
private boolean recurse = false;
private boolean verbose = false;
private boolean loadclasses = false;
private String encoding = null;
private String pkgprefix = null;
private List files = new ArrayList();
private Globals globals;
public static void main( String[] args ) throws IOException {
new luajc( args );
}
private luajc( String[] args ) throws IOException {
// process args
List seeds = new ArrayList ();
// get stateful args
for ( int i=0; i<args.length; i++ ) {
if ( ! args[i].startsWith("-") ) {
seeds.add(args[i]);
} else {
switch ( args[i].charAt(1) ) {
case 's':
if ( ++i >= args.length )
usageExit();
srcdir = args[i];
break;
case 'd':
if ( ++i >= args.length )
usageExit();
destdir = args[i];
break;
case 'l':
loadclasses = true;
break;
case 'p':
if ( ++i >= args.length )
usageExit();
pkgprefix = args[i];
break;
case 'm':
genmain = true;
break;
case 'r':
recurse = true;
break;
case 'c':
if ( ++i >= args.length )
usageExit();
encoding = args[i];
break;
case 'v':
verbose = true;
break;
default:
usageExit();
break;
}
}
}
// echo version
if ( verbose ) {
System.out.println(version);
System.out.println("srcdir: "+srcdir);
System.out.println("destdir: "+destdir);
System.out.println("files: "+seeds);
System.out.println("recurse: "+recurse);
}
// need at least one seed
if ( seeds.size() <= 0 ) {
System.err.println(usage);
System.exit(-1);
}
// collect up files to process
for ( int i=0; i<seeds.size(); i++ )
collectFiles( srcdir+"/"+seeds.get(i) );
// check for at least one file
if ( files.size() <= 0 ) {
System.err.println("no files found in "+seeds);
System.exit(-1);
}
// process input files
globals = JsePlatform.standardGlobals();
for ( int i=0,n=files.size(); i<n; i++ )
processFile( (InputFile) files.get(i) );
}
private void collectFiles(String path) {
File f = new File(path);
if ( f.isDirectory() && recurse )
scandir(f,pkgprefix);
else if ( f.isFile() ) {
File dir = f.getAbsoluteFile().getParentFile();
if ( dir != null )
scanfile( dir, f, pkgprefix );
}
}
private void scandir(File dir, String javapackage) {
File[] f = dir.listFiles();
for ( int i=0; i<f.length; i++ )
scanfile( dir, f[i], javapackage );
}
private void scanfile(File dir, File f, String javapackage) {
if ( f.exists() ) {
if ( f.isDirectory() && recurse )
scandir( f, (javapackage!=null? javapackage+"."+f.getName(): f.getName()) );
else if ( f.isFile() && f.getName().endsWith(".lua") )
files.add( new InputFile(dir,f,javapackage) );
}
}
private static final class LocalClassLoader extends ClassLoader {
private final Hashtable t;
private LocalClassLoader(Hashtable t) {
this.t = t;
}
public Class findClass(String classname) throws ClassNotFoundException {
byte[] bytes = (byte[]) t.get(classname);
if ( bytes != null )
return defineClass(classname, bytes, 0, bytes.length);
return super.findClass(classname);
}
}
class InputFile {
public String luachunkname;
public String srcfilename;
public File infile;
public File outdir;
public String javapackage;
public InputFile(File dir, File f, String javapackage) {
this.infile = f;
String subdir = javapackage!=null? javapackage.replace('.', '/'): null;
String outdirpath = subdir!=null? destdir+"/"+subdir: destdir;
this.javapackage = javapackage;
this.srcfilename = (subdir!=null? subdir+"/": "")+infile.getName();
this.luachunkname = (subdir!=null? subdir+"/": "")+infile.getName().substring( 0, infile.getName().lastIndexOf('.') );
this.infile = f;
this.outdir = new File(outdirpath);
}
}
private void processFile( InputFile inf ) {
inf.outdir.mkdirs();
try {
if ( verbose )
System.out.println("chunk="+inf.luachunkname+" srcfile="+inf.srcfilename);
// create the chunk
FileInputStream fis = new FileInputStream( inf.infile );
final Hashtable t = encoding != null?
LuaJC.instance.compileAll( new InputStreamReader(fis, encoding), inf.luachunkname, inf.srcfilename, globals, genmain):
LuaJC.instance.compileAll( fis, inf.luachunkname, inf.srcfilename, globals, genmain);
fis.close();
// write out the chunk
for ( Enumeration e = t.keys(); e.hasMoreElements(); ) {
String key = (String) e.nextElement();
byte[] bytes = (byte[]) t.get(key);
if ( key.indexOf('/')>=0 ) {
String d = (destdir!=null? destdir+"/": "")+key.substring(0,key.lastIndexOf('/'));
new File(d).mkdirs();
}
String destpath = (destdir!=null? destdir+"/": "") + key + ".class";
if ( verbose )
System.out.println( " "+destpath +" ("+bytes.length+" bytes)");
FileOutputStream fos = new FileOutputStream( destpath );
fos.write( bytes );
fos.close();
}
// try to load the files
if ( loadclasses ) {
ClassLoader loader = new LocalClassLoader(t);
for ( Enumeration e = t.keys(); e.hasMoreElements(); ) {
String classname = (String) e.nextElement();
try {
Class c = loader.loadClass(classname);
Object o = c.newInstance();
if ( verbose )
System.out.println(" loaded "+classname+" as "+o );
} catch ( Exception ex ) {
System.out.flush();
System.err.println(" failed to load "+classname+": "+ex );
System.err.flush();
}
}
}
} catch ( Exception e ) {
System.err.println(" failed to load "+inf.srcfilename+": "+e );
e.printStackTrace( System.err );
System.err.flush();
}
}
}

View File

@@ -0,0 +1,41 @@
/*******************************************************************************
* Copyright (c) 2010 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.ast;
import java.util.ArrayList;
import java.util.List;
public class Block extends Stat {
public List<Stat> stats = new ArrayList<Stat>();
public NameScope scope;
public void add(Stat s) {
if ( s == null )
return;
stats.add(s);
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}

View File

@@ -0,0 +1,34 @@
/*******************************************************************************
* Copyright (c) 2010 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.ast;
public class Chunk extends SyntaxElement {
public final Block block;
public Chunk(Block b) {
this.block = b;
}
public void accept( Visitor visitor ) {
visitor.visit( this );
}
}

View File

@@ -0,0 +1,313 @@
/*******************************************************************************
* Copyright (c) 2010 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.ast;
import org.luaj.vm2.Lua;
import org.luaj.vm2.LuaValue;
abstract
public class Exp extends SyntaxElement {
abstract public void accept(Visitor visitor);
public static Exp constant(LuaValue value) {
return new Constant(value);
}
public static Exp numberconstant(String token) {
return new Constant( LuaValue.valueOf(token).tonumber() );
}
public static Exp varargs() {
return new VarargsExp();
}
public static Exp tableconstructor(TableConstructor tc) {
return tc;
}
public static Exp unaryexp(int op, Exp rhs) {
if ( rhs instanceof BinopExp ) {
BinopExp b = (BinopExp) rhs;
if ( precedence(op) > precedence(b.op) )
return binaryexp( unaryexp(op, b.lhs), b.op, b.rhs );
}
return new UnopExp(op, rhs);
}
public static Exp binaryexp(Exp lhs, int op, Exp rhs) {
if ( lhs instanceof UnopExp ) {
UnopExp u = (UnopExp) lhs;
if ( precedence(op) > precedence(u.op) )
return unaryexp( u.op, binaryexp( u.rhs, op, rhs ) );
}
// TODO: cumulate string concatenations together
// TODO: constant folding
if ( lhs instanceof BinopExp ) {
BinopExp b = (BinopExp) lhs;
if ( (precedence(op) > precedence(b.op)) ||
((precedence(op) == precedence(b.op)) && isrightassoc(op)) )
return binaryexp( b.lhs, b.op, binaryexp( b.rhs, op, rhs ) );
}
if ( rhs instanceof BinopExp ) {
BinopExp b = (BinopExp) rhs;
if ( (precedence(op) > precedence(b.op)) ||
((precedence(op) == precedence(b.op)) && ! isrightassoc(op)) )
return binaryexp( binaryexp( lhs, op, b.lhs ), b.op, b.rhs );
}
return new BinopExp(lhs, op, rhs);
}
static boolean isrightassoc(int op) {
switch ( op ) {
case Lua.OP_CONCAT:
case Lua.OP_POW: return true;
default: return false;
}
}
static int precedence(int op) {
switch ( op ) {
case Lua.OP_OR: return 0;
case Lua.OP_AND: return 1;
case Lua.OP_LT: case Lua.OP_GT: case Lua.OP_LE: case Lua.OP_GE: case Lua.OP_NEQ: case Lua.OP_EQ: return 2;
case Lua.OP_CONCAT: return 3;
case Lua.OP_ADD: case Lua.OP_SUB: return 4;
case Lua.OP_MUL: case Lua.OP_DIV: case Lua.OP_MOD: return 5;
case Lua.OP_NOT: case Lua.OP_UNM: case Lua.OP_LEN: return 6;
case Lua.OP_POW: return 7;
default: throw new IllegalStateException("precedence of bad op "+op);
}
}
public static Exp anonymousfunction(FuncBody funcbody) {
return new AnonFuncDef(funcbody);
}
/** foo */
public static NameExp nameprefix(String name) {
return new NameExp(name);
}
/** ( foo.bar ) */
public static ParensExp parensprefix(Exp exp) {
return new ParensExp(exp);
}
/** foo[exp] */
public static IndexExp indexop(PrimaryExp lhs, Exp exp) {
return new IndexExp(lhs, exp);
}
/** foo.bar */
public static FieldExp fieldop(PrimaryExp lhs, String name) {
return new FieldExp(lhs, name);
}
/** foo(2,3) */
public static FuncCall functionop(PrimaryExp lhs, FuncArgs args) {
return new FuncCall(lhs, args);
}
/** foo:bar(4,5) */
public static MethodCall methodop(PrimaryExp lhs, String name, FuncArgs args) {
return new MethodCall(lhs, name, args);
}
public boolean isvarexp() {
return false;
}
public boolean isfunccall() {
return false;
}
public boolean isvarargexp() {
return false;
}
abstract public static class PrimaryExp extends Exp {
public boolean isvarexp() {
return false;
}
public boolean isfunccall() {
return false;
}
}
abstract public static class VarExp extends PrimaryExp {
public boolean isvarexp() {
return true;
}
public void markHasAssignment() {
}
}
public static class NameExp extends VarExp {
public final Name name;
public NameExp(String name) {
this.name = new Name(name);
}
public void markHasAssignment() {
name.variable.hasassignments = true;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public static class ParensExp extends PrimaryExp {
public final Exp exp;
public ParensExp(Exp exp) {
this.exp = exp;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public static class FieldExp extends VarExp {
public final PrimaryExp lhs;
public final Name name;
public FieldExp(PrimaryExp lhs, String name) {
this.lhs = lhs;
this.name = new Name(name);
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public static class IndexExp extends VarExp {
public final PrimaryExp lhs;
public final Exp exp;
public IndexExp(PrimaryExp lhs, Exp exp) {
this.lhs = lhs;
this.exp = exp;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public static class FuncCall extends PrimaryExp {
public final PrimaryExp lhs;
public final FuncArgs args;
public FuncCall(PrimaryExp lhs, FuncArgs args) {
this.lhs = lhs;
this.args = args;
}
public boolean isfunccall() {
return true;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
public boolean isvarargexp() {
return true;
}
}
public static class MethodCall extends FuncCall {
public final String name;
public MethodCall(PrimaryExp lhs, String name, FuncArgs args) {
super(lhs, args);
this.name = new String(name);
}
public boolean isfunccall() {
return true;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public static class Constant extends Exp {
public final LuaValue value;
public Constant(LuaValue value) {
this.value = value;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public static class VarargsExp extends Exp {
public void accept(Visitor visitor) {
visitor.visit(this);
}
public boolean isvarargexp() {
return true;
}
}
public static class UnopExp extends Exp {
public final int op;
public final Exp rhs;
public UnopExp(int op, Exp rhs) {
this.op = op;
this.rhs = rhs;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public static class BinopExp extends Exp {
public final Exp lhs,rhs;
public final int op;
public BinopExp(Exp lhs, int op, Exp rhs) {
this.lhs = lhs;
this.op = op;
this.rhs = rhs;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public static class AnonFuncDef extends Exp {
public final FuncBody body;
public AnonFuncDef(FuncBody funcbody) {
this.body = funcbody;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
}

View File

@@ -0,0 +1,66 @@
/*******************************************************************************
* Copyright (c) 2010 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.ast;
import java.util.ArrayList;
import java.util.List;
import org.luaj.vm2.LuaString;
public class FuncArgs extends SyntaxElement {
public final List<Exp> exps;
/** exp1,exp2... */
public static FuncArgs explist(List<Exp> explist) {
return new FuncArgs(explist);
}
/** {...} */
public static FuncArgs tableconstructor(TableConstructor table) {
return new FuncArgs(table);
}
/** "mylib" */
public static FuncArgs string(LuaString string) {
return new FuncArgs(string);
}
public FuncArgs(List<Exp> exps) {
this.exps = exps;
}
public FuncArgs(LuaString string) {
this.exps = new ArrayList<Exp>();
this.exps.add( Exp.constant(string) );
}
public FuncArgs(TableConstructor table) {
this.exps = new ArrayList<Exp>();
this.exps.add( table );
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}

View File

@@ -0,0 +1,36 @@
/*******************************************************************************
* Copyright (c) 2010 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.ast;
public class FuncBody extends SyntaxElement {
public ParList parlist;
public Block block;
public NameScope scope;
public FuncBody(ParList parlist, Block block) {
this.parlist = parlist!=null? parlist: ParList.EMPTY_PARLIST;
this.block = block;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}

View File

@@ -0,0 +1,49 @@
/*******************************************************************************
* Copyright (c) 2010 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.ast;
import java.util.ArrayList;
import java.util.List;
public class FuncName extends SyntaxElement {
// example: a.b.c.d:e
// initial base name: "a"
public final Name name;
// intermediate field accesses: "b", "c", "d"
public List<String> dots;
// optional final method name: "e"
public String method;
public FuncName( String name ) {
this.name = new Name(name);
}
public void adddot(String dot) {
if ( dots == null )
dots = new ArrayList<String>();
dots.add(dot);
}
}

View File

@@ -0,0 +1,31 @@
/*******************************************************************************
* Copyright (c) 2010 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.ast;
public class Name {
public final String name;
public Variable variable;
public Name(String name) {
this.name = name;
}
}

View File

@@ -0,0 +1,127 @@
package org.luaj.vm2.ast;
import java.util.List;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.ast.Exp.Constant;
import org.luaj.vm2.ast.Exp.NameExp;
import org.luaj.vm2.ast.Exp.VarExp;
import org.luaj.vm2.ast.Stat.Assign;
import org.luaj.vm2.ast.Stat.FuncDef;
import org.luaj.vm2.ast.Stat.GenericFor;
import org.luaj.vm2.ast.Stat.LocalAssign;
import org.luaj.vm2.ast.Stat.LocalFuncDef;
import org.luaj.vm2.ast.Stat.NumericFor;
/**
* Visitor that resolves names to scopes.
* Each Name is resolved to a NamedVarible, possibly in a NameScope
* if it is a local, or in no named scope if it is a global.
*/
public class NameResolver extends Visitor {
private NameScope scope = null;
private void pushScope() {
scope = new NameScope(scope);
}
private void popScope() {
scope = scope.outerScope;
}
public void visit(NameScope scope) {
}
public void visit(Block block) {
pushScope();
block.scope = scope;
super.visit(block);
popScope();
}
public void visit(FuncBody body) {
pushScope();
scope.functionNestingCount++;
body.scope = scope;
super.visit(body);
popScope();
}
public void visit(LocalFuncDef stat) {
defineLocalVar(stat.name);
super.visit(stat);
}
public void visit(NumericFor stat) {
pushScope();
stat.scope = scope;
defineLocalVar(stat.name);
super.visit(stat);
popScope();
}
public void visit(GenericFor stat) {
pushScope();
stat.scope = scope;
defineLocalVars( stat.names );
super.visit(stat);
popScope();
}
public void visit(NameExp exp) {
exp.name.variable = resolveNameReference(exp.name);
super.visit(exp);
}
public void visit(FuncDef stat) {
stat.name.name.variable = resolveNameReference(stat.name.name);
stat.name.name.variable.hasassignments = true;
super.visit(stat);
}
public void visit(Assign stat) {
super.visit(stat);
for ( int i=0, n=stat.vars.size(); i<n; i++ ) {
VarExp v = (VarExp) stat.vars.get(i);
v.markHasAssignment();
}
}
public void visit(LocalAssign stat) {
visitExps(stat.values);
defineLocalVars( stat.names );
int n = stat.names.size();
int m = stat.values!=null? stat.values.size(): 0;
boolean isvarlist = m>0 && m<n && ((Exp)stat.values.get(m-1)).isvarargexp();
for ( int i=0; i<n && i<(isvarlist?m-1:m); i++ )
if ( stat.values.get(i) instanceof Constant )
((Name)stat.names.get(i)).variable.initialValue = ((Constant) stat.values.get(i)).value;
if ( !isvarlist )
for ( int i=m; i<n; i++ )
((Name)stat.names.get(i)).variable.initialValue = LuaValue.NIL;
}
public void visit(ParList pars) {
if ( pars.names != null )
defineLocalVars(pars.names);
if ( pars.isvararg )
scope.define("arg");
super.visit(pars);
}
protected void defineLocalVars(List<Name> names) {
for ( int i=0, n=names.size(); i<n; i++ )
defineLocalVar((Name) names.get(i));
}
protected void defineLocalVar(Name name) {
name.variable = scope.define(name.name);
}
protected Variable resolveNameReference(Name name) {
Variable v = scope.find(name.name);
if ( v.isLocal() && scope.functionNestingCount != v.definingScope.functionNestingCount )
v.isupvalue = true;
return v;
}
}

View File

@@ -0,0 +1,85 @@
/*******************************************************************************
* Copyright (c) 2010 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.ast;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class NameScope {
private static final Set<String> LUA_KEYWORDS = new HashSet<String>();
static {
String[] k = new String[] {
"and", "break", "do", "else", "elseif", "end",
"false", "for", "function", "if", "in", "local",
"nil", "not", "or", "repeat", "return",
"then", "true", "until", "while" };
for ( int i=0; i<k.length; i++ )
LUA_KEYWORDS.add( k[i] );
}
public final Map<String,Variable> namedVariables = new HashMap<String,Variable>();
public final NameScope outerScope;
public int functionNestingCount;
/** Construct default names scope */
public NameScope() {
this.outerScope = null;
this.functionNestingCount = 0;
}
/** Construct name scope within another scope*/
public NameScope(NameScope outerScope) {
this.outerScope = outerScope;
this.functionNestingCount = outerScope!=null? outerScope.functionNestingCount: 0;
}
/** Look up a name. If it is a global name, then throw IllegalArgumentException. */
public Variable find( String name ) throws IllegalArgumentException {
validateIsNotKeyword(name);
for ( NameScope n = this; n!=null; n=n.outerScope )
if ( n.namedVariables.containsKey(name) )
return (Variable)n.namedVariables.get(name);
Variable value = new Variable(name);
this.namedVariables.put(name, value);
return value;
}
/** Define a name in this scope. If it is a global name, then throw IllegalArgumentException. */
public Variable define( String name ) throws IllegalStateException, IllegalArgumentException {
validateIsNotKeyword(name);
Variable value = new Variable(name, this);
this.namedVariables.put(name, value);
return value;
}
private void validateIsNotKeyword(String name) {
if ( LUA_KEYWORDS.contains(name) )
throw new IllegalArgumentException("name is a keyword: '"+name+"'");
}
}

View File

@@ -0,0 +1,42 @@
/*******************************************************************************
* Copyright (c) 2010 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.ast;
import java.util.ArrayList;
import java.util.List;
public class ParList extends SyntaxElement {
public static final List<Name> EMPTY_NAMELIST = new ArrayList<Name>();
public static final ParList EMPTY_PARLIST = new ParList(EMPTY_NAMELIST,false);
public final List<Name> names;
public final boolean isvararg;
public ParList(List<Name> names, boolean isvararg) {
this.names = names;
this.isvararg = isvararg;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}

View File

@@ -0,0 +1,278 @@
/*******************************************************************************
* Copyright (c) 2010 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.ast;
import java.util.List;
import org.luaj.vm2.ast.Exp.VarExp;
abstract
public class Stat extends SyntaxElement {
public abstract void accept(Visitor visitor);
public static Stat block(Block block) {
return block;
}
public static Stat whiledo(Exp exp, Block block) {
return new WhileDo(exp, block);
}
public static Stat repeatuntil(Block block, Exp exp) {
return new RepeatUntil(block, exp);
}
public static Stat breakstat() {
return new Break();
}
public static Stat returnstat(List<Exp> exps) {
return new Return(exps);
}
public static Stat assignment(List<VarExp> vars, List<Exp> exps) {
return new Assign(vars,exps);
}
public static Stat functioncall(Exp.FuncCall funccall) {
return new FuncCallStat(funccall);
}
public static Stat localfunctiondef(String name, FuncBody funcbody) {
return new LocalFuncDef(name, funcbody);
}
public static Stat fornumeric(String name, Exp initial, Exp limit, Exp step, Block block) {
return new NumericFor(name, initial, limit, step, block);
}
public static Stat functiondef(FuncName funcname, FuncBody funcbody) {
return new FuncDef( funcname, funcbody );
}
public static Stat forgeneric(List<Name> names, List<Exp> exps, Block block) {
return new GenericFor(names, exps, block);
}
public static Stat localassignment(List<Name> names, List<Exp> values) {
return new LocalAssign(names, values);
}
public static Stat ifthenelse(Exp ifexp, Block ifblock, List<Exp> elseifexps, List<Block> elseifblocks, Block elseblock) {
return new IfThenElse(ifexp, ifblock, elseifexps, elseifblocks, elseblock);
}
public static Stat gotostat(String name) {
return new Goto(name);
}
public static Stat labelstat(String name) {
return new Label(name);
}
public static class Goto extends Stat {
public final String name;
public Goto(String name) {
this.name = name;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public static class Label extends Stat {
public final String name;
public Label(String name) {
this.name = name;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public static class Assign extends Stat {
public final List<VarExp> vars;
public final List<Exp> exps;
public Assign(List<VarExp> vars, List<Exp> exps) {
this.vars = vars;
this.exps = exps;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public static class WhileDo extends Stat {
public final Exp exp;
public final Block block;
public WhileDo( Exp exp, Block block ) {
this.exp = exp;
this.block = block;
}
public void accept(Visitor visitor) {
visitor.visit( this );
}
}
public static class RepeatUntil extends Stat {
public final Block block;
public final Exp exp;
public RepeatUntil( Block block, Exp exp ) {
this.block = block;
this.exp = exp;
}
public void accept(Visitor visitor) {
visitor.visit( this );
}
}
public static class Break extends Stat {
public void accept(Visitor visitor) {
visitor.visit( this );
}
}
public static class Return extends Stat {
public final List<Exp> values;
public Return(List<Exp> values) {
this.values = values;
}
public void accept(Visitor visitor) {
visitor.visit( this );
}
public int nreturns() {
int n = values!=null? values.size(): 0;
if ( n>0 && ((Exp)values.get(n-1)).isvarargexp() )
n = -1;
return n;
}
}
public static class FuncCallStat extends Stat {
public final Exp.FuncCall funccall;
public FuncCallStat(Exp.FuncCall funccall) {
this.funccall = funccall;
}
public void accept(Visitor visitor) {
visitor.visit( this );
}
}
public static class LocalFuncDef extends Stat {
public final Name name;
public final FuncBody body;
public LocalFuncDef(String name, FuncBody body) {
this.name = new Name(name);
this.body = body;
}
public void accept(Visitor visitor) {
visitor.visit( this );
}
}
public static class FuncDef extends Stat {
public final FuncName name;
public final FuncBody body;
public FuncDef(FuncName name, FuncBody body) {
this.name = name;
this.body = body;
}
public void accept(Visitor visitor) {
visitor.visit( this );
}
}
public static class GenericFor extends Stat {
public List<Name> names;
public List<Exp> exps;
public Block block;
public NameScope scope;
public GenericFor(List<Name> names, List<Exp> exps, Block block) {
this.names = names;
this.exps = exps;
this.block = block;
}
public void accept(Visitor visitor) {
visitor.visit( this );
}
}
public static class NumericFor extends Stat {
public final Name name;
public final Exp initial,limit,step;
public final Block block;
public NameScope scope;
public NumericFor(String name, Exp initial, Exp limit, Exp step, Block block) {
this.name = new Name(name);
this.initial = initial;
this.limit = limit;
this.step = step;
this.block = block;
}
public void accept(Visitor visitor) {
visitor.visit( this );
}
}
public static class LocalAssign extends Stat {
public final List<Name> names;
public final List<Exp> values;
public LocalAssign(List<Name> names, List<Exp> values) {
this.names = names;
this.values = values;
}
public void accept(Visitor visitor) {
visitor.visit( this );
}
}
public static class IfThenElse extends Stat {
public final Exp ifexp;
public final Block ifblock;
public final List<Exp> elseifexps;
public final List<Block> elseifblocks;
public final Block elseblock;
public IfThenElse(Exp ifexp, Block ifblock, List<Exp> elseifexps,
List<Block> elseifblocks, Block elseblock) {
this.ifexp = ifexp;
this.ifblock = ifblock;
this.elseifexps = elseifexps;
this.elseifblocks = elseifblocks;
this.elseblock = elseblock;
}
public void accept(Visitor visitor) {
visitor.visit( this );
}
}
}

View File

@@ -0,0 +1,93 @@
/*******************************************************************************
* Copyright (c) 2010 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.ast;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import org.luaj.vm2.LuaString;
public class Str {
private Str() {}
public static LuaString quoteString(String image) {
String s = image.substring(1, image.length()-1);
byte[] bytes = unquote(s);
return LuaString.valueUsing(bytes);
}
public static LuaString charString(String image) {
String s = image.substring(1, image.length()-1);
byte[] bytes = unquote(s);
return LuaString.valueUsing(bytes);
}
public static LuaString longString(String image) {
int i = image.indexOf('[', image.indexOf('[')+1)+1;
String s = image.substring(i,image.length()-i);
byte[] b = iso88591bytes(s);
return LuaString.valueUsing(b);
}
public static byte[] iso88591bytes( String s ) {
try {
return s.getBytes("ISO8859-1");
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("ISO8859-1 not supported");
}
}
public static byte[] unquote(String s) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
char[] c = s.toCharArray();
int n = c.length;
for ( int i=0; i<n; i++ ) {
if ( c[i] == '\\' && i<n ) {
switch ( c[++i] ) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
int d=(int) (c[i++]-'0');
for ( int j=0; i<n && j<2 && c[i]>='0' && c[i]<='9'; i++, j++ )
d = d * 10 + (int) (c[i]-'0');
baos.write( (byte) d );
--i;
continue;
case 'a': baos.write( (byte) 7 ); continue;
case 'b': baos.write( (byte) '\b' ); continue;
case 'f': baos.write( (byte) '\f' ); continue;
case 'n': baos.write( (byte) '\n' ); continue;
case 'r': baos.write( (byte) '\r' ); continue;
case 't': baos.write( (byte) '\t' ); continue;
case 'v': baos.write( (byte) 11 ); continue;
case '"': baos.write( (byte) '"' ); continue;
case '\'': baos.write( (byte) '\'' ); continue;
case '\\': baos.write( (byte) '\\' ); continue;
default: baos.write( (byte) c[i] ); break;
}
} else {
baos.write( (byte) c[i] );
}
}
return baos.toByteArray();
}
}

View File

@@ -0,0 +1,40 @@
/*******************************************************************************
* 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.ast;
/** Base class for syntax elements of the parse tree that appear in source files.
* The LuaParser class will fill these values out during parsing for use in
* syntax highlighting, for example.
*/
public class SyntaxElement {
/** The line number on which the element begins. */
public int beginLine;
/** The column at which the element begins. */
public short beginColumn;
/** The line number on which the element ends. */
public int endLine;
/** The column at which the element ends. */
public short endColumn;
}

View File

@@ -0,0 +1,32 @@
/*******************************************************************************
* Copyright (c) 2010 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.ast;
import java.util.List;
public class TableConstructor extends Exp {
public List<TableField> fields;
public void accept(Visitor visitor) {
visitor.visit(this);
}
}

View File

@@ -0,0 +1,51 @@
/*******************************************************************************
* Copyright (c) 2010 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.ast;
public class TableField extends SyntaxElement {
public final Exp index;
public final String name;
public final Exp rhs;
public TableField(Exp index, String name, Exp rhs) {
this.index = index;
this.name = name;
this.rhs = rhs;
}
public static TableField keyedField(Exp index, Exp rhs) {
return new TableField(index, null, rhs);
}
public static TableField namedField(String name, Exp rhs) {
return new TableField(null, name, rhs);
}
public static TableField listField(Exp rhs) {
return new TableField(null, null, rhs);
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}

View File

@@ -0,0 +1,62 @@
/*******************************************************************************
* Copyright (c) 2010 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.ast;
import org.luaj.vm2.LuaValue;
/** Variable is created lua name scopes, and is a named, lua variable that
* either refers to a lua local, global, or upvalue storage location.
*/
public class Variable {
/** The name as it appears in lua source code */
public final String name;
/** The lua scope in which this variable is defined. */
public final NameScope definingScope;
/** true if this variable is an upvalue */
public boolean isupvalue;
/** true if there are assignments made to this variable */
public boolean hasassignments;
/** When hasassignments == false, and the initial value is a constant, this is the initial value */
public LuaValue initialValue;
/** Global is named variable not associated with a defining scope */
public Variable(String name) {
this.name = name;
this.definingScope = null;
}
public Variable(String name, NameScope definingScope) {
/** Local variable is defined in a particular scope. */
this.name = name;
this.definingScope = definingScope;
}
public boolean isLocal() {
return this.definingScope != null;
}
public boolean isConstant() {
return ! hasassignments && initialValue != null;
}
}

View File

@@ -0,0 +1,180 @@
/*******************************************************************************
* Copyright (c) 2010 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.ast;
import java.util.List;
import org.luaj.vm2.ast.Exp.VarExp;
abstract public class Visitor {
public void visit(Chunk chunk) {
chunk.block.accept(this);
};
public void visit(Block block) {
visit(block.scope);
if ( block.stats != null )
for ( int i=0, n=block.stats.size(); i<n; i++ )
((Stat)block.stats.get(i)).accept(this);
};
public void visit(Stat.Assign stat) {
visitVars(stat.vars);
visitExps(stat.exps);
}
public void visit(Stat.Break breakstat) {
}
public void visit(Stat.FuncCallStat stat) {
stat.funccall.accept(this);
}
public void visit(Stat.FuncDef stat) {
stat.body.accept(this);
}
public void visit(Stat.GenericFor stat) {
visit(stat.scope);
visitNames(stat.names);
visitExps(stat.exps);
stat.block.accept(this);
}
public void visit(Stat.IfThenElse stat) {
stat.ifexp.accept(this);
stat.ifblock.accept(this);
if ( stat.elseifblocks != null )
for ( int i=0, n=stat.elseifblocks.size(); i<n; i++ ) {
((Exp)stat.elseifexps.get(i)).accept(this);
((Block)stat.elseifblocks.get(i)).accept(this);
}
if ( stat.elseblock != null )
visit( stat.elseblock );
}
public void visit(Stat.LocalAssign stat) {
visitNames(stat.names);
visitExps(stat.values);
}
public void visit(Stat.LocalFuncDef stat) {
visit(stat.name);
stat.body.accept(this);
}
public void visit(Stat.NumericFor stat) {
visit(stat.scope);
visit(stat.name);
stat.initial.accept(this);
stat.limit.accept(this);
if ( stat.step != null )
stat.step.accept(this);
stat.block.accept(this);
}
public void visit(Stat.RepeatUntil stat) {
stat.block.accept(this);
stat.exp.accept(this);
}
public void visit(Stat.Return stat) {
visitExps(stat.values);
}
public void visit(Stat.WhileDo stat) {
stat.exp.accept(this);
stat.block.accept(this);
}
public void visit(FuncBody body) {
visit(body.scope);
body.parlist.accept(this);
body.block.accept(this);
}
public void visit(FuncArgs args) {
visitExps(args.exps);
}
public void visit(TableField field) {
if ( field.name != null )
visit( field.name );
if ( field.index != null )
field.index.accept(this);
field.rhs.accept(this);
}
public void visit(Exp.AnonFuncDef exp) {
exp.body.accept(this);
}
public void visit(Exp.BinopExp exp) {
exp.lhs.accept(this);
exp.rhs.accept(this);
}
public void visit(Exp.Constant exp) {
}
public void visit(Exp.FieldExp exp) {
exp.lhs.accept(this);
visit(exp.name);
}
public void visit(Exp.FuncCall exp) {
exp.lhs.accept(this);
exp.args.accept(this);
}
public void visit(Exp.IndexExp exp) {
exp.lhs.accept(this);
exp.exp.accept(this);
}
public void visit(Exp.MethodCall exp) {
exp.lhs.accept(this);
visit(exp.name);
exp.args.accept(this);
}
public void visit(Exp.NameExp exp) {
visit(exp.name);
}
public void visit(Exp.ParensExp exp) {
exp.exp.accept(this);
}
public void visit(Exp.UnopExp exp) {
exp.rhs.accept(this);
}
public void visit(Exp.VarargsExp exp) {
}
public void visit(ParList pars) {
visitNames(pars.names);
}
public void visit(TableConstructor table) {
if( table.fields != null)
for ( int i=0, n=table.fields.size(); i<n; i++ )
((TableField)table.fields.get(i)).accept(this);
}
public void visitVars(List<VarExp> vars) {
if ( vars != null )
for ( int i=0, n=vars.size(); i<n; i++ )
((VarExp)vars.get(i)).accept(this);
}
public void visitExps(List<Exp> exps) {
if ( exps != null )
for ( int i=0, n=exps.size(); i<n; i++ )
((Exp)exps.get(i)).accept(this);
}
public void visitNames(List<Name> names) {
if ( names != null )
for ( int i=0, n=names.size(); i<n; i++ )
visit((Name) names.get(i));
}
public void visit(Name name) {
}
public void visit(String name) {
}
public void visit(NameScope scope) {
}
public void visit(Stat.Goto gotostat) {
}
public void visit(Stat.Label label) {
}
}

View File

@@ -0,0 +1,215 @@
/**
*
*/
package org.luaj.vm2.luajc;
import java.util.Vector;
import org.luaj.vm2.Lua;
import org.luaj.vm2.Prototype;
public class BasicBlock {
int pc0,pc1; // range of program counter values for the block
BasicBlock[] prev; // previous basic blocks (0-n of these)
BasicBlock[] next; // next basic blocks (0, 1, or 2 of these)
boolean islive; // true if this block is used
public BasicBlock(Prototype p, int pc0) {
this.pc0 = this.pc1 = pc0;
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append( (pc0+1)+"-"+(pc1+1)
+(prev!=null? " prv: "+str(prev,1): "")
+(next!=null? " nxt: "+str(next,0): "")
+"\n" );
return sb.toString();
}
private String str(BasicBlock[] b, int p) {
if ( b == null )
return "";
StringBuffer sb = new StringBuffer();
sb.append("(");
for ( int i=0, n=b.length; i<n; i++ ) {
if ( i > 0 )
sb.append( "," );
sb.append( String.valueOf( p==1? b[i].pc1+1: b[i].pc0+1 ) );
}
sb.append(")");
return sb.toString();
}
public static BasicBlock[] findBasicBlocks(Prototype p) {
// mark beginnings, endings
final int n = p.code.length;
final boolean[] isbeg = new boolean[n];
final boolean[] isend = new boolean[n];
isbeg[0] = true;
BranchVisitor bv = new MarkAndMergeVisitor(isbeg, isend);
visitBranches(p, bv); // 1st time to mark branches
visitBranches(p, bv); // 2nd time to catch merges
// create basic blocks
final BasicBlock[] blocks = new BasicBlock[n];
for ( int i=0; i<n; i++ ) {
isbeg[i] = true;
BasicBlock b = new BasicBlock(p,i);
blocks[i] = b;
while ( !isend[i] && i+1<n && !isbeg[i+1] )
blocks[b.pc1=++i] = b;
}
// count previous, next
final int[] nnext = new int[n];
final int[] nprev = new int[n];
visitBranches(p, new CountPrevNextVistor(isbeg, nnext, nprev));
// allocate and cross-reference
visitBranches( p, new AllocAndXRefVisitor(isbeg, nnext, nprev, blocks));
return blocks;
}
private static final class AllocAndXRefVisitor extends BranchVisitor {
private final int[] nnext;
private final int[] nprev;
private final BasicBlock[] blocks;
private AllocAndXRefVisitor(boolean[] isbeg, int[] nnext, int[] nprev,
BasicBlock[] blocks) {
super(isbeg);
this.nnext = nnext;
this.nprev = nprev;
this.blocks = blocks;
}
public void visitBranch(int pc0, int pc1) {
if ( blocks[pc0].next == null ) blocks[pc0].next = new BasicBlock[nnext[pc0]];
if ( blocks[pc1].prev == null ) blocks[pc1].prev = new BasicBlock[nprev[pc1]];
blocks[pc0].next[--nnext[pc0]] = blocks[pc1];
blocks[pc1].prev[--nprev[pc1]] = blocks[pc0];
}
}
private static final class CountPrevNextVistor extends BranchVisitor {
private final int[] nnext;
private final int[] nprev;
private CountPrevNextVistor(boolean[] isbeg, int[] nnext, int[] nprev) {
super(isbeg);
this.nnext = nnext;
this.nprev = nprev;
}
public void visitBranch(int pc0, int pc1) {
nnext[pc0]++;
nprev[pc1]++;
}
}
private static final class MarkAndMergeVisitor extends BranchVisitor {
private final boolean[] isend;
private MarkAndMergeVisitor(boolean[] isbeg, boolean[] isend) {
super(isbeg);
this.isend = isend;
}
public void visitBranch(int pc0, int pc1) {
isend[pc0] = true;
isbeg[pc1] = true;
}
public void visitReturn(int pc) {
isend[pc] = true;
}
}
abstract public static class BranchVisitor {
final boolean[] isbeg;
public BranchVisitor(boolean[] isbeg) {
this.isbeg = isbeg;
}
public void visitBranch( int frompc, int topc ) {}
public void visitReturn( int atpc ) {}
}
public static void visitBranches( Prototype p, BranchVisitor visitor ) {
int sbx,j,c;
int[] code = p.code;
int n = code.length;
for ( int i=0; i<n; i++ ) {
int ins = code[i];
switch ( Lua.GET_OPCODE( ins ) ) {
case Lua.OP_LOADBOOL:
if ( 0 == Lua.GETARG_C(ins) )
break;
if ( Lua.GET_OPCODE(code[i+1]) == Lua.OP_JMP )
throw new IllegalArgumentException("OP_LOADBOOL followed by jump at "+i);
visitor.visitBranch( i, i+2 );
continue;
case Lua.OP_EQ:
case Lua.OP_LT:
case Lua.OP_LE:
case Lua.OP_TEST:
case Lua.OP_TESTSET:
if ( Lua.GET_OPCODE(code[i+1]) != Lua.OP_JMP )
throw new IllegalArgumentException("test not followed by jump at "+i);
sbx = Lua.GETARG_sBx(code[i+1]);
++i;
j = i + sbx + 1;
visitor.visitBranch( i, j );
visitor.visitBranch( i, i+1 );
continue;
case Lua.OP_TFORLOOP:
case Lua.OP_FORLOOP:
sbx = Lua.GETARG_sBx(ins);
j = i + sbx + 1;
visitor.visitBranch( i, j );
visitor.visitBranch( i, i+1 );
continue;
case Lua.OP_JMP:
case Lua.OP_FORPREP:
sbx = Lua.GETARG_sBx(ins);
j = i + sbx + 1;
visitor.visitBranch( i, j );
continue;
case Lua.OP_TAILCALL:
case Lua.OP_RETURN:
visitor.visitReturn( i );
continue;
}
if ( i+1<n && visitor.isbeg[i+1] )
visitor.visitBranch( i, i+1 );
}
}
public static BasicBlock[] findLiveBlocks(BasicBlock[] blocks) {
// add reachable blocks
Vector next = new Vector ();
next.addElement( blocks[0] );
while ( ! next.isEmpty() ) {
BasicBlock b = (BasicBlock) next.elementAt(0);
next.removeElementAt(0);
if ( ! b.islive ) {
b.islive = true;
for ( int i=0, n=b.next!=null? b.next.length: 0; i<n; i++ )
if ( ! b.next[i].islive )
next.addElement( b.next[i] );
}
}
// create list in natural order
Vector list = new Vector();
for ( int i=0; i<blocks.length; i=blocks[i].pc1+1 )
if ( blocks[i].islive )
list.addElement(blocks[i]);
// convert to array
BasicBlock[] array = new BasicBlock[list.size()];
list.copyInto(array);
return array;
}
}

View File

@@ -0,0 +1,853 @@
/*******************************************************************************
* Copyright (c) 2010 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.luajc;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.apache.bcel.Constants;
import org.apache.bcel.generic.AASTORE;
import org.apache.bcel.generic.ALOAD;
import org.apache.bcel.generic.ANEWARRAY;
import org.apache.bcel.generic.ASTORE;
import org.apache.bcel.generic.ArrayType;
import org.apache.bcel.generic.BranchInstruction;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.CompoundInstruction;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.FieldGen;
import org.apache.bcel.generic.GOTO;
import org.apache.bcel.generic.IFEQ;
import org.apache.bcel.generic.IFNE;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionConstants;
import org.apache.bcel.generic.InstructionFactory;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.LocalVariableGen;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.PUSH;
import org.apache.bcel.generic.Type;
import org.luaj.vm2.Buffer;
import org.luaj.vm2.Lua;
import org.luaj.vm2.LuaBoolean;
import org.luaj.vm2.LuaInteger;
import org.luaj.vm2.LuaNumber;
import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Prototype;
import org.luaj.vm2.Varargs;
import org.luaj.vm2.lib.OneArgFunction;
import org.luaj.vm2.lib.ThreeArgFunction;
import org.luaj.vm2.lib.TwoArgFunction;
import org.luaj.vm2.lib.VarArgFunction;
import org.luaj.vm2.lib.ZeroArgFunction;
public class JavaBuilder {
private static final String STR_VARARGS = Varargs.class.getName();
private static final String STR_LUAVALUE = LuaValue.class.getName();
private static final String STR_LUASTRING = LuaString.class.getName();
private static final String STR_LUAINTEGER = LuaInteger.class.getName();
private static final String STR_LUANUMBER = LuaNumber.class.getName();
private static final String STR_LUABOOLEAN = LuaBoolean.class.getName();
private static final String STR_LUATABLE = LuaTable.class.getName();
private static final String STR_BUFFER = Buffer.class.getName();
private static final String STR_STRING = String.class.getName();
private static final String STR_JSEPLATFORM = "org.luaj.vm2.lib.jse.JsePlatform";
private static final ObjectType TYPE_VARARGS = new ObjectType(STR_VARARGS);
private static final ObjectType TYPE_LUAVALUE = new ObjectType(STR_LUAVALUE);
private static final ObjectType TYPE_LUASTRING = new ObjectType(STR_LUASTRING);
private static final ObjectType TYPE_LUAINTEGER = new ObjectType(STR_LUAINTEGER);
private static final ObjectType TYPE_LUANUMBER = new ObjectType(STR_LUANUMBER);
private static final ObjectType TYPE_LUABOOLEAN = new ObjectType(STR_LUABOOLEAN);
private static final ObjectType TYPE_LUATABLE = new ObjectType(STR_LUATABLE);
private static final ObjectType TYPE_BUFFER = new ObjectType(STR_BUFFER);
private static final ObjectType TYPE_STRING = new ObjectType(STR_STRING);
private static final ArrayType TYPE_LOCALUPVALUE = new ArrayType( TYPE_LUAVALUE, 1 );
private static final ArrayType TYPE_CHARARRAY = new ArrayType( Type.CHAR, 1 );
private static final ArrayType TYPE_STRINGARRAY = new ArrayType( TYPE_STRING, 1 );
private static final String STR_FUNCV = VarArgFunction.class.getName();
private static final String STR_FUNC0 = ZeroArgFunction.class.getName();
private static final String STR_FUNC1 = OneArgFunction.class.getName();
private static final String STR_FUNC2 = TwoArgFunction.class.getName();
private static final String STR_FUNC3 = ThreeArgFunction.class.getName();
// argument list types
private static final Type[] ARG_TYPES_NONE = {};
private static final Type[] ARG_TYPES_INT = { Type.INT };
private static final Type[] ARG_TYPES_DOUBLE = { Type.DOUBLE };
private static final Type[] ARG_TYPES_STRING = { Type.STRING };
private static final Type[] ARG_TYPES_CHARARRAY = { TYPE_CHARARRAY };
private static final Type[] ARG_TYPES_INT_LUAVALUE = { Type.INT, TYPE_LUAVALUE };
private static final Type[] ARG_TYPES_INT_VARARGS = { Type.INT, TYPE_VARARGS };
private static final Type[] ARG_TYPES_LUAVALUE_VARARGS = { TYPE_LUAVALUE, TYPE_VARARGS };
private static final Type[] ARG_TYPES_LUAVALUE_LUAVALUE_VARARGS = { TYPE_LUAVALUE, TYPE_LUAVALUE, TYPE_VARARGS };
private static final Type[] ARG_TYPES_LUAVALUEARRAY = { new ArrayType( TYPE_LUAVALUE, 1 ) };
private static final Type[] ARG_TYPES_LUAVALUEARRAY_VARARGS = { new ArrayType( TYPE_LUAVALUE, 1 ), TYPE_VARARGS };
private static final Type[] ARG_TYPES_LUAVALUE_LUAVALUE_LUAVALUE = { TYPE_LUAVALUE, TYPE_LUAVALUE, TYPE_LUAVALUE };
private static final Type[] ARG_TYPES_VARARGS = { TYPE_VARARGS };
private static final Type[] ARG_TYPES_LUAVALUE_LUAVALUE = { TYPE_LUAVALUE, TYPE_LUAVALUE };
private static final Type[] ARG_TYPES_INT_INT = { Type.INT, Type.INT };
private static final Type[] ARG_TYPES_LUAVALUE = { TYPE_LUAVALUE };
private static final Type[] ARG_TYPES_BUFFER = { TYPE_BUFFER };
private static final Type[] ARG_TYPES_STRINGARRAY = { TYPE_STRINGARRAY };
private static final Type[] ARG_TYPES_LUAVALUE_STRINGARRAY = { TYPE_LUAVALUE, TYPE_STRINGARRAY };
// names, arg types for main prototype classes
private static final String[] SUPER_NAME_N = { STR_FUNC0, STR_FUNC1, STR_FUNC2, STR_FUNC3, STR_FUNCV, };
private static final ObjectType[] RETURN_TYPE_N = { TYPE_LUAVALUE, TYPE_LUAVALUE, TYPE_LUAVALUE, TYPE_LUAVALUE, TYPE_VARARGS, };
private static final Type[][] ARG_TYPES_N = { ARG_TYPES_NONE, ARG_TYPES_LUAVALUE, ARG_TYPES_LUAVALUE_LUAVALUE, ARG_TYPES_LUAVALUE_LUAVALUE_LUAVALUE, ARG_TYPES_VARARGS, };
private static final String[][] ARG_NAMES_N = { {}, {"arg"}, {"arg1","arg2"}, {"arg1","arg2","arg3"}, {"args"}, };
private static final String[] METH_NAME_N = { "call", "call", "call", "call", "onInvoke", };
// varable naming
private static final String PREFIX_CONSTANT = "k";
private static final String PREFIX_UPVALUE = "u";
private static final String PREFIX_PLAIN_SLOT = "s";
private static final String PREFIX_UPVALUE_SLOT = "a";
private static final String NAME_VARRESULT = "v";
// basic info
private final ProtoInfo pi;
private final Prototype p;
private final String classname;
// bcel variables
private final ClassGen cg;
private final ConstantPoolGen cp;
private final InstructionFactory factory;
// main instruction list for the main function of this class
private final InstructionList init;
private final InstructionList main;
private final MethodGen mg;
// the superclass arg count, 0-3 args, 4=varargs
private int superclassType;
private static int SUPERTYPE_VARARGS = 4;
// storage for goto locations
private final int[] targets;
private final BranchInstruction[] branches;
private final InstructionHandle[] branchDestHandles;
private final InstructionHandle[] lastInstrHandles;
private InstructionHandle beginningOfLuaInstruction;
// hold vararg result
private LocalVariableGen varresult = null;
private int prev_line = -1;
public JavaBuilder(ProtoInfo pi, String classname, String filename) {
this.pi = pi;
this.p = pi.prototype;
this.classname = classname;
// what class to inherit from
superclassType = p.numparams;
if ( p.is_vararg != 0 || superclassType >= SUPERTYPE_VARARGS )
superclassType = SUPERTYPE_VARARGS;
for ( int i=0, n=p.code.length; i<n; i++ ) {
int inst = p.code[i];
int o = Lua.GET_OPCODE(inst);
if ( (o == Lua.OP_TAILCALL) ||
((o == Lua.OP_RETURN) && (Lua.GETARG_B(inst) < 1 || Lua.GETARG_B(inst) > 2)) ) {
superclassType = SUPERTYPE_VARARGS;
break;
}
}
// create class generator
cg = new ClassGen(classname, SUPER_NAME_N[superclassType], filename,
Constants.ACC_PUBLIC | Constants.ACC_SUPER, null);
cp = cg.getConstantPool(); // cg creates constant pool
// main instruction lists
factory = new InstructionFactory(cg);
init = new InstructionList();
main = new InstructionList();
// create the fields
for ( int i=0; i<p.upvalues.length; i++ ) {
boolean isrw = pi.isReadWriteUpvalue( pi.upvals[i] );
Type uptype = isrw? (Type) TYPE_LOCALUPVALUE: (Type) TYPE_LUAVALUE;
FieldGen fg = new FieldGen(0, uptype, upvalueName(i), cp);
cg.addField(fg.getField());
}
// create the method
mg = new MethodGen( Constants.ACC_PUBLIC | Constants.ACC_FINAL, // access flags
RETURN_TYPE_N[superclassType], // return type
ARG_TYPES_N[superclassType], // argument types
ARG_NAMES_N[superclassType], // arg names
METH_NAME_N[superclassType],
STR_LUAVALUE, // method, defining class
main, cp);
// initialize the values in the slots
initializeSlots();
// initialize branching
int nc = p.code.length;
targets = new int[nc];
branches = new BranchInstruction[nc];
branchDestHandles = new InstructionHandle[nc];
lastInstrHandles = new InstructionHandle[nc];
}
public void initializeSlots() {
int slot = 0;
createUpvalues(-1, 0, p.maxstacksize);
if ( superclassType == SUPERTYPE_VARARGS ) {
for ( slot=0; slot<p.numparams; slot++ ) {
if ( pi.isInitialValueUsed(slot) ) {
append(new ALOAD(1));
append(new PUSH(cp, slot+1));
append(factory.createInvoke(STR_VARARGS, "arg", TYPE_LUAVALUE, ARG_TYPES_INT, Constants.INVOKEVIRTUAL));
storeLocal(-1, slot);
}
}
append(new ALOAD(1));
append(new PUSH(cp, 1 + p.numparams));
append(factory.createInvoke(STR_VARARGS, "subargs", TYPE_VARARGS, ARG_TYPES_INT, Constants.INVOKEVIRTUAL));
append(new ASTORE(1));
} else {
// fixed arg function between 0 and 3 arguments
for ( slot=0; slot<p.numparams; slot++ ) {
this.plainSlotVars.put( Integer.valueOf(slot), Integer.valueOf(1+slot) );
if ( pi.isUpvalueCreate(-1, slot) ) {
append(new ALOAD(1+slot));
storeLocal(-1, slot);
}
}
}
// nil parameters
// TODO: remove this for lua 5.2, not needed
for ( ; slot<p.maxstacksize; slot++ ) {
if ( pi.isInitialValueUsed(slot) ) {
loadNil();
storeLocal(-1, slot);
}
}
}
public byte[] completeClass(boolean genmain) {
// add class initializer
if ( ! init.isEmpty() ) {
MethodGen mg = new MethodGen(Constants.ACC_STATIC, Type.VOID,
ARG_TYPES_NONE, new String[] {}, "<clinit>",
cg.getClassName(), init, cg.getConstantPool());
init.append(InstructionConstants.RETURN);
mg.setMaxStack();
cg.addMethod(mg.getMethod());
init.dispose();
}
// add default constructor
cg.addEmptyConstructor(Constants.ACC_PUBLIC);
// gen method
resolveBranches();
mg.setMaxStack();
cg.addMethod(mg.getMethod());
main.dispose();
// add initupvalue1(LuaValue env) to initialize environment for main chunk
if (p.upvalues.length == 1 && superclassType == SUPERTYPE_VARARGS) {
MethodGen mg = new MethodGen( Constants.ACC_PUBLIC | Constants.ACC_FINAL, // access flags
Type.VOID, // return type
ARG_TYPES_LUAVALUE, // argument types
new String[] { "env" }, // arg names
"initupvalue1",
STR_LUAVALUE, // method, defining class
main, cp);
boolean isrw = pi.isReadWriteUpvalue( pi.upvals[0] );
append(InstructionConstants.THIS);
append(new ALOAD(1));
if ( isrw ) {
append(factory.createInvoke(classname, "newupl", TYPE_LOCALUPVALUE, ARG_TYPES_LUAVALUE, Constants.INVOKESTATIC));
append(factory.createFieldAccess(classname, upvalueName(0), TYPE_LOCALUPVALUE, Constants.PUTFIELD));
} else {
append(factory.createFieldAccess(classname, upvalueName(0), TYPE_LUAVALUE, Constants.PUTFIELD));
}
append(InstructionConstants.RETURN);
mg.setMaxStack();
cg.addMethod(mg.getMethod());
main.dispose();
}
// add main function so class is invokable from the java command line
if (genmain) {
MethodGen mg = new MethodGen( Constants.ACC_PUBLIC | Constants.ACC_STATIC, // access flags
Type.VOID, // return type
ARG_TYPES_STRINGARRAY, // argument types
new String[] { "arg" }, // arg names
"main",
classname, // method, defining class
main, cp);
append(factory.createNew(classname));
append(InstructionConstants.DUP);
append(factory.createInvoke(classname, Constants.CONSTRUCTOR_NAME, Type.VOID, ARG_TYPES_NONE, Constants.INVOKESPECIAL));
append(new ALOAD(0));
append(factory.createInvoke(STR_JSEPLATFORM, "luaMain", Type.VOID, ARG_TYPES_LUAVALUE_STRINGARRAY, Constants.INVOKESTATIC));
append(InstructionConstants.RETURN);
mg.setMaxStack();
cg.addMethod(mg.getMethod());
main.dispose();
}
// convert to class bytes
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
cg.getJavaClass().dump(baos);
return baos.toByteArray();
} catch ( IOException ioe ) {
throw new RuntimeException("JavaClass.dump() threw "+ioe);
}
}
public void dup() {
append(InstructionConstants.DUP);
}
public void pop() {
append(InstructionConstants.POP);
}
public void loadNil() {
append(factory.createFieldAccess(STR_LUAVALUE, "NIL", TYPE_LUAVALUE, Constants.GETSTATIC));
}
public void loadNone() {
append(factory.createFieldAccess(STR_LUAVALUE, "NONE", TYPE_LUAVALUE, Constants.GETSTATIC));
}
public void loadBoolean(boolean b) {
String field = (b? "TRUE": "FALSE");
append(factory.createFieldAccess(STR_LUAVALUE, field, TYPE_LUABOOLEAN, Constants.GETSTATIC));
}
private Map<Integer,Integer> plainSlotVars = new HashMap<Integer,Integer>();
private Map<Integer,Integer> upvalueSlotVars = new HashMap<Integer,Integer>();
private Map<Integer,LocalVariableGen> localVarGenBySlot = new HashMap<Integer,LocalVariableGen>();
private int findSlot( int slot, Map<Integer,Integer> map, String prefix, Type type ) {
Integer islot = Integer.valueOf(slot);
if ( map.containsKey(islot) )
return ((Integer)map.get(islot)).intValue();
String name = prefix+slot;
LocalVariableGen local = mg.addLocalVariable(name, type, null, null);
int index = local.getIndex();
map.put(islot, Integer.valueOf(index));
localVarGenBySlot.put(islot, local);
return index;
}
private int findSlotIndex( int slot, boolean isupvalue ) {
return isupvalue?
findSlot( slot, upvalueSlotVars, PREFIX_UPVALUE_SLOT, TYPE_LOCALUPVALUE ):
findSlot( slot, plainSlotVars, PREFIX_PLAIN_SLOT, TYPE_LUAVALUE );
}
public void loadLocal(int pc, int slot) {
boolean isupval = pi.isUpvalueRefer(pc, slot);
int index = findSlotIndex( slot, isupval );
append(new ALOAD(index));
if (isupval) {
append(new PUSH(cp, 0));
append(InstructionConstants.AALOAD);
}
}
public void storeLocal(int pc, int slot) {
boolean isupval = pi.isUpvalueAssign(pc, slot);
int index = findSlotIndex( slot, isupval );
if (isupval) {
boolean isupcreate = pi.isUpvalueCreate(pc, slot);
if ( isupcreate ) {
append(factory.createInvoke(classname, "newupe", TYPE_LOCALUPVALUE, ARG_TYPES_NONE, Constants.INVOKESTATIC));
append(InstructionConstants.DUP);
append(new ASTORE(index));
} else {
append(new ALOAD(index));
}
append(InstructionConstants.SWAP);
append(new PUSH(cp, 0));
append(InstructionConstants.SWAP);
append(InstructionConstants.AASTORE);
} else {
append(new ASTORE(index));
}
}
public void createUpvalues(int pc, int firstslot, int numslots) {
for ( int i=0; i<numslots; i++ ) {
int slot = firstslot + i;
boolean isupcreate = pi.isUpvalueCreate(pc, slot);
if ( isupcreate ) {
int index = findSlotIndex( slot, true );
append(factory.createInvoke(classname, "newupn", TYPE_LOCALUPVALUE, ARG_TYPES_NONE, Constants.INVOKESTATIC));
append(new ASTORE(index));
}
}
}
public void convertToUpvalue(int pc, int slot) {
boolean isupassign = pi.isUpvalueAssign(pc, slot);
if ( isupassign ) {
int index = findSlotIndex( slot, false );
append(new ALOAD(index));
append(factory.createInvoke(classname, "newupl", TYPE_LOCALUPVALUE, ARG_TYPES_LUAVALUE, Constants.INVOKESTATIC));
int upindex = findSlotIndex( slot, true );
append(new ASTORE(upindex));
}
}
private static String upvalueName(int upindex) {
return PREFIX_UPVALUE+upindex;
}
public void loadUpvalue(int upindex) {
boolean isrw = pi.isReadWriteUpvalue( pi.upvals[upindex] );
append(InstructionConstants.THIS);
if ( isrw ) {
append(factory.createFieldAccess(classname, upvalueName(upindex), TYPE_LOCALUPVALUE, Constants.GETFIELD));
append(new PUSH(cp,0));
append(InstructionConstants.AALOAD);
} else {
append(factory.createFieldAccess(classname, upvalueName(upindex), TYPE_LUAVALUE, Constants.GETFIELD));
}
}
public void storeUpvalue(int pc, int upindex, int slot) {
boolean isrw = pi.isReadWriteUpvalue( pi.upvals[upindex] );
append(InstructionConstants.THIS);
if ( isrw ) {
append(factory.createFieldAccess(classname, upvalueName(upindex), TYPE_LOCALUPVALUE, Constants.GETFIELD));
append(new PUSH(cp,0));
loadLocal(pc, slot);
append(InstructionConstants.AASTORE);
} else {
loadLocal(pc, slot);
append(factory.createFieldAccess(classname, upvalueName(upindex), TYPE_LUAVALUE, Constants.PUTFIELD));
}
}
public void newTable( int b, int c ) {
append(new PUSH(cp, b));
append(new PUSH(cp, c));
append(factory.createInvoke(STR_LUAVALUE, "tableOf", TYPE_LUATABLE, ARG_TYPES_INT_INT, Constants.INVOKESTATIC));
}
public void loadVarargs() {
append(new ALOAD(1));
}
public void loadVarargs(int argindex) {
loadVarargs();
arg(argindex);
}
public void arg(int argindex) {
if ( argindex == 1 ) {
append(factory.createInvoke(STR_VARARGS, "arg1", TYPE_LUAVALUE, ARG_TYPES_NONE, Constants.INVOKEVIRTUAL));
} else {
append(new PUSH(cp, argindex));
append(factory.createInvoke(STR_VARARGS, "arg", TYPE_LUAVALUE, ARG_TYPES_INT, Constants.INVOKEVIRTUAL));
}
}
private int getVarresultIndex() {
if ( varresult == null )
varresult = mg.addLocalVariable(NAME_VARRESULT, TYPE_VARARGS, null, null);
return varresult.getIndex();
}
public void loadVarresult() {
append(new ALOAD(getVarresultIndex()));
}
public void storeVarresult() {
append(new ASTORE(getVarresultIndex()));
}
public void subargs(int firstarg) {
append(new PUSH(cp, firstarg));
append(factory.createInvoke(STR_VARARGS, "subargs", TYPE_VARARGS, ARG_TYPES_INT, Constants.INVOKEVIRTUAL));
}
public void getTable() {
append(factory.createInvoke(STR_LUAVALUE, "get", TYPE_LUAVALUE, ARG_TYPES_LUAVALUE, Constants.INVOKEVIRTUAL));
}
public void setTable() {
append(factory.createInvoke(STR_LUAVALUE, "set", Type.VOID, ARG_TYPES_LUAVALUE_LUAVALUE, Constants.INVOKEVIRTUAL));
}
public void unaryop(int o) {
String op;
switch (o) {
default:
case Lua.OP_UNM: op = "neg"; break;
case Lua.OP_NOT: op = "not"; break;
case Lua.OP_LEN: op = "len"; break;
}
append(factory.createInvoke(STR_LUAVALUE, op, TYPE_LUAVALUE, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
}
public void binaryop(int o) {
String op;
switch (o) {
default:
case Lua.OP_ADD: op = "add"; break;
case Lua.OP_SUB: op = "sub"; break;
case Lua.OP_MUL: op = "mul"; break;
case Lua.OP_DIV: op = "div"; break;
case Lua.OP_MOD: op = "mod"; break;
case Lua.OP_POW: op = "pow"; break;
}
append(factory.createInvoke(STR_LUAVALUE, op, TYPE_LUAVALUE, ARG_TYPES_LUAVALUE, Constants.INVOKEVIRTUAL));
}
public void compareop(int o) {
String op;
switch (o) {
default:
case Lua.OP_EQ: op = "eq_b"; break;
case Lua.OP_LT: op = "lt_b"; break;
case Lua.OP_LE: op = "lteq_b"; break;
}
append(factory.createInvoke(STR_LUAVALUE, op, Type.BOOLEAN, ARG_TYPES_LUAVALUE, Constants.INVOKEVIRTUAL));
}
public void areturn() {
append(InstructionConstants.ARETURN);
}
public void toBoolean() {
append(factory.createInvoke(STR_LUAVALUE, "toboolean", Type.BOOLEAN, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
}
public void tostring() {
append(factory.createInvoke(STR_BUFFER, "tostring", TYPE_LUASTRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
}
public void isNil() {
append(factory.createInvoke(STR_LUAVALUE, "isnil", Type.BOOLEAN, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
}
public void testForLoop() {
append(factory.createInvoke(STR_LUAVALUE, "testfor_b", Type.BOOLEAN, ARG_TYPES_LUAVALUE_LUAVALUE, Constants.INVOKEVIRTUAL));
}
public void loadArrayArgs(int pc, int firstslot, int nargs) {
append(new PUSH(cp, nargs));
append(new ANEWARRAY(cp.addClass(STR_LUAVALUE)));
for ( int i=0; i<nargs; i++ ) {
append(InstructionConstants.DUP);
append(new PUSH(cp, i));
loadLocal(pc, firstslot++);
append(new AASTORE());
}
}
public void newVarargs(int pc, int firstslot, int nargs) {
switch ( nargs ) {
case 0: loadNone();
break;
case 1: loadLocal(pc, firstslot);
break;
case 2: loadLocal(pc, firstslot); loadLocal(pc, firstslot+1);
append(factory.createInvoke(STR_LUAVALUE, "varargsOf", TYPE_VARARGS, ARG_TYPES_LUAVALUE_VARARGS, Constants.INVOKESTATIC));
break;
case 3: loadLocal(pc, firstslot); loadLocal(pc, firstslot+1); loadLocal(pc, firstslot+2);
append(factory.createInvoke(STR_LUAVALUE, "varargsOf", TYPE_VARARGS, ARG_TYPES_LUAVALUE_LUAVALUE_VARARGS, Constants.INVOKESTATIC));
break;
default:
loadArrayArgs(pc, firstslot, nargs);
append(factory.createInvoke(STR_LUAVALUE, "varargsOf", TYPE_VARARGS, ARG_TYPES_LUAVALUEARRAY, Constants.INVOKESTATIC));
break;
}
}
public void newVarargsVarresult(int pc, int firstslot, int nslots) {
loadArrayArgs(pc, firstslot, nslots );
loadVarresult();
append(factory.createInvoke(STR_LUAVALUE, "varargsOf", TYPE_VARARGS, ARG_TYPES_LUAVALUEARRAY_VARARGS, Constants.INVOKESTATIC));
}
public void call(int nargs) {
switch ( nargs ) {
case 0: append(factory.createInvoke(STR_LUAVALUE, "call", TYPE_LUAVALUE, ARG_TYPES_NONE, Constants.INVOKEVIRTUAL)); break;
case 1: append(factory.createInvoke(STR_LUAVALUE, "call", TYPE_LUAVALUE, ARG_TYPES_LUAVALUE, Constants.INVOKEVIRTUAL)); break;
case 2: append(factory.createInvoke(STR_LUAVALUE, "call", TYPE_LUAVALUE, ARG_TYPES_LUAVALUE_LUAVALUE, Constants.INVOKEVIRTUAL)); break;
case 3: append(factory.createInvoke(STR_LUAVALUE, "call", TYPE_LUAVALUE, ARG_TYPES_LUAVALUE_LUAVALUE_LUAVALUE, Constants.INVOKEVIRTUAL)); break;
default: throw new IllegalArgumentException("can't call with "+nargs+" args");
}
}
public void newTailcallVarargs() {
append(factory.createInvoke(STR_LUAVALUE, "tailcallOf", TYPE_VARARGS, ARG_TYPES_LUAVALUE_VARARGS, Constants.INVOKESTATIC));
}
public void invoke(int nargs) {
switch ( nargs ) {
case -1: append(factory.createInvoke(STR_LUAVALUE, "invoke", TYPE_VARARGS, ARG_TYPES_VARARGS, Constants.INVOKEVIRTUAL)); break;
case 0: append(factory.createInvoke(STR_LUAVALUE, "invoke", TYPE_VARARGS, ARG_TYPES_NONE, Constants.INVOKEVIRTUAL)); break;
case 1: append(factory.createInvoke(STR_LUAVALUE, "invoke", TYPE_VARARGS, ARG_TYPES_VARARGS, Constants.INVOKEVIRTUAL)); break;
case 2: append(factory.createInvoke(STR_LUAVALUE, "invoke", TYPE_VARARGS, ARG_TYPES_LUAVALUE_VARARGS, Constants.INVOKEVIRTUAL)); break;
case 3: append(factory.createInvoke(STR_LUAVALUE, "invoke", TYPE_VARARGS, ARG_TYPES_LUAVALUE_LUAVALUE_VARARGS, Constants.INVOKEVIRTUAL)); break;
default: throw new IllegalArgumentException("can't invoke with "+nargs+" args");
}
}
// ------------------------ closures ------------------------
public void closureCreate(String protoname) {
append(factory.createNew(new ObjectType(protoname)));
append(InstructionConstants.DUP);
append(factory.createInvoke(protoname, "<init>", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL));
}
public void closureInitUpvalueFromUpvalue(String protoname, int newup, int upindex) {
boolean isrw = pi.isReadWriteUpvalue( pi.upvals[upindex] );
Type uptype = isrw? (Type) TYPE_LOCALUPVALUE: (Type) TYPE_LUAVALUE;
String srcname = upvalueName(upindex);
String destname = upvalueName(newup);
append(InstructionConstants.THIS);
append(factory.createFieldAccess(classname, srcname, uptype, Constants.GETFIELD));
append(factory.createFieldAccess(protoname, destname, uptype, Constants.PUTFIELD));
}
public void closureInitUpvalueFromLocal(String protoname, int newup, int pc, int srcslot) {
boolean isrw = pi.isReadWriteUpvalue( pi.vars[srcslot][pc].upvalue );
Type uptype = isrw? (Type) TYPE_LOCALUPVALUE: (Type) TYPE_LUAVALUE;
String destname = upvalueName(newup);
int index = findSlotIndex( srcslot, isrw );
append(new ALOAD(index));
append(factory.createFieldAccess(protoname, destname, uptype, Constants.PUTFIELD));
}
private Map<LuaValue,String> constants = new HashMap<LuaValue,String>();
public void loadConstant(LuaValue value) {
switch ( value.type() ) {
case LuaValue.TNIL:
loadNil();
break;
case LuaValue.TBOOLEAN:
loadBoolean( value.toboolean() );
break;
case LuaValue.TNUMBER:
case LuaValue.TSTRING:
String name = (String) constants.get(value);
if ( name == null ) {
name = value.type() == LuaValue.TNUMBER?
value.isinttype()?
createLuaIntegerField(value.checkint()):
createLuaDoubleField(value.checkdouble()):
createLuaStringField(value.checkstring());
constants.put(value, name);
}
append(factory.createGetStatic(classname, name, TYPE_LUAVALUE));
break;
default:
throw new IllegalArgumentException("bad constant type: "+value.type());
}
}
private String createLuaIntegerField(int value) {
String name = PREFIX_CONSTANT+constants.size();
FieldGen fg = new FieldGen(Constants.ACC_STATIC | Constants.ACC_FINAL,
TYPE_LUAVALUE, name, cp);
cg.addField(fg.getField());
init.append(new PUSH(cp, value));
init.append(factory.createInvoke(STR_LUAVALUE, "valueOf",
TYPE_LUAINTEGER, ARG_TYPES_INT, Constants.INVOKESTATIC));
init.append(factory.createPutStatic(classname, name, TYPE_LUAVALUE));
return name;
}
private String createLuaDoubleField(double value) {
String name = PREFIX_CONSTANT+constants.size();
FieldGen fg = new FieldGen(Constants.ACC_STATIC | Constants.ACC_FINAL,
TYPE_LUAVALUE, name, cp);
cg.addField(fg.getField());
init.append(new PUSH(cp, value));
init.append(factory.createInvoke(STR_LUAVALUE, "valueOf",
TYPE_LUANUMBER, ARG_TYPES_DOUBLE, Constants.INVOKESTATIC));
init.append(factory.createPutStatic(classname, name, TYPE_LUAVALUE));
return name;
}
private String createLuaStringField(LuaString value) {
String name = PREFIX_CONSTANT+constants.size();
FieldGen fg = new FieldGen(Constants.ACC_STATIC | Constants.ACC_FINAL,
TYPE_LUAVALUE, name, cp);
cg.addField(fg.getField());
LuaString ls = value.checkstring();
if ( ls.isValidUtf8() ) {
init.append(new PUSH(cp, value.tojstring()));
init.append(factory.createInvoke(STR_LUASTRING, "valueOf",
TYPE_LUASTRING, ARG_TYPES_STRING, Constants.INVOKESTATIC));
} else {
char[] c = new char[ls.m_length];
for ( int j=0; j<ls.m_length; j++ )
c[j] = (char) (0xff & (int) (ls.m_bytes[ls.m_offset+j]));
init.append(new PUSH(cp, new String(c)));
init.append(factory.createInvoke(STR_STRING, "toCharArray",
TYPE_CHARARRAY, Type.NO_ARGS,
Constants.INVOKEVIRTUAL));
init.append(factory.createInvoke(STR_LUASTRING, "valueOf",
TYPE_LUASTRING, ARG_TYPES_CHARARRAY,
Constants.INVOKESTATIC));
}
init.append(factory.createPutStatic(classname, name, TYPE_LUAVALUE));
return name;
}
// --------------------- branching support -------------------------
public static final int BRANCH_GOTO = 1;
public static final int BRANCH_IFNE = 2;
public static final int BRANCH_IFEQ = 3;
public void addBranch( int pc, int branchType, int targetpc ) {
switch ( branchType ) {
default:
case BRANCH_GOTO: branches[pc] = new GOTO(null); break;
case BRANCH_IFNE: branches[pc] = new IFNE(null); break;
case BRANCH_IFEQ: branches[pc] = new IFEQ(null); break;
}
targets[pc] = targetpc;
append(branches[pc]);
}
private void append( Instruction i ) {
conditionalSetBeginningOfLua( main.append(i) );
}
private void append( CompoundInstruction i ) {
conditionalSetBeginningOfLua( main.append(i) );
}
private void append( BranchInstruction i ) {
conditionalSetBeginningOfLua( main.append(i) );
}
private void conditionalSetBeginningOfLua(InstructionHandle ih) {
if ( beginningOfLuaInstruction == null )
beginningOfLuaInstruction = ih;
}
public void onEndOfLuaInstruction(int pc, int line) {
branchDestHandles[pc] = beginningOfLuaInstruction;
lastInstrHandles[pc] = main.getEnd();
if (line != prev_line)
mg.addLineNumber(beginningOfLuaInstruction, prev_line = line);
beginningOfLuaInstruction = null;
}
public void setVarStartEnd(int slot, int start_pc, int end_pc, String name) {
Integer islot = Integer.valueOf(slot);
if (localVarGenBySlot.containsKey(islot)) {
name = name.replaceAll("[^a-zA-Z0-9]", "_");
LocalVariableGen l = (LocalVariableGen)localVarGenBySlot.get(islot);
l.setEnd(lastInstrHandles[end_pc-1]);
if (start_pc > 1)
l.setStart(lastInstrHandles[start_pc-2]);
l.setName(name);
}
}
private void resolveBranches() {
int nc = p.code.length;
for (int pc = 0; pc < nc; pc++) {
if (branches[pc] != null) {
int t=targets[pc];
while ( t<branchDestHandles.length && branchDestHandles[t] == null )
t++;
if ( t>= branchDestHandles.length )
throw new IllegalArgumentException("no target at or after "+targets[pc]+" op="+Lua.GET_OPCODE(p.code[targets[pc]]));
branches[pc].setTarget(branchDestHandles[t]);
}
}
}
public void setlistStack(int pc, int a0, int index0, int nvals) {
for ( int i=0; i<nvals; i++ ) {
dup();
append(new PUSH(cp, index0+i));
loadLocal( pc, a0+i );
append(factory.createInvoke(STR_LUAVALUE, "rawset", Type.VOID, ARG_TYPES_INT_LUAVALUE, Constants.INVOKEVIRTUAL));
}
}
public void setlistVarargs(int index0, int vresultbase) {
append(new PUSH(cp, index0));
loadVarresult();
append(factory.createInvoke(STR_LUAVALUE, "rawsetlist", Type.VOID, ARG_TYPES_INT_VARARGS, Constants.INVOKEVIRTUAL));
}
public void concatvalue() {
append(factory.createInvoke(STR_LUAVALUE, "concat", TYPE_LUAVALUE, ARG_TYPES_LUAVALUE, Constants.INVOKEVIRTUAL));
}
public void concatbuffer() {
append(factory.createInvoke(STR_LUAVALUE, "concat", TYPE_BUFFER, ARG_TYPES_BUFFER, Constants.INVOKEVIRTUAL));
}
public void tobuffer() {
append(factory.createInvoke(STR_LUAVALUE, "buffer", TYPE_BUFFER, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
}
public void tovalue() {
append(factory.createInvoke(STR_BUFFER, "value", TYPE_LUAVALUE, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
}
public void closeUpvalue(int pc, int upindex) {
// TODO: assign the upvalue location the value null;
/*
boolean isrw = pi.isReadWriteUpvalue( pi.upvals[upindex] );
append(InstructionConstants.THIS);
append(InstructionConstants.ACONST_NULL);
if ( isrw ) {
append(factory.createFieldAccess(classname, upvalueName(upindex), TYPE_LUAVALUEARRAY, Constants.PUTFIELD));
} else {
append(factory.createFieldAccess(classname, upvalueName(upindex), TYPE_LUAVALUE, Constants.PUTFIELD));
}
*/
}
}

View File

@@ -0,0 +1,447 @@
/*******************************************************************************
* Copyright (c) 2010 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.luajc;
import org.luaj.vm2.LocVars;
import org.luaj.vm2.Lua;
import org.luaj.vm2.Prototype;
import org.luaj.vm2.Upvaldesc;
/**
* TODO:
* propogate constants
* loader can find inner classes
*/
public class JavaGen {
public final String classname;
public final byte[] bytecode;
public final JavaGen[] inners;
public JavaGen( Prototype p, String classname, String filename, boolean genmain ) {
this( new ProtoInfo(p,classname), classname, filename, genmain );
}
private JavaGen( ProtoInfo pi, String classname, String filename, boolean genmain ) {
this.classname = classname;
// build this class
JavaBuilder builder = new JavaBuilder(pi, classname, filename);
scanInstructions(pi, classname, builder);
for (int i = 0; i < pi.prototype.locvars.length; ++i) {
LocVars l = pi.prototype.locvars[i];
builder.setVarStartEnd(i, l.startpc, l.endpc, l.varname.tojstring());
}
this.bytecode = builder.completeClass(genmain);
// build sub-prototypes
if ( pi.subprotos != null ) {
int n = pi.subprotos.length;
inners = new JavaGen[n];
for ( int i=0; i<n; i++ )
inners[i] = new JavaGen(pi.subprotos[i], pi.subprotos[i].name, filename, false);
} else {
inners = null;
}
}
private void scanInstructions(ProtoInfo pi, String classname, JavaBuilder builder) {
Prototype p = pi.prototype;
int vresultbase = -1;
for ( int bi=0; bi<pi.blocklist.length; bi++ ) {
BasicBlock b0 = pi.blocklist[bi];
// convert upvalues that are phi-variables
for ( int slot=0; slot<p.maxstacksize; slot++ ) {
int pc = b0.pc0;
boolean c = pi.isUpvalueCreate(pc, slot);
if ( c && pi.vars[slot][pc].isPhiVar() )
builder.convertToUpvalue(pc, slot);
}
for ( int pc=b0.pc0; pc<=b0.pc1; pc++ ) {
final int pc0 = pc; // closure changes pc
final int ins = p.code[pc];
final int line = pc < p.lineinfo.length? p.lineinfo[pc]: -1;
final int o = Lua.GET_OPCODE(ins);
int a = Lua.GETARG_A(ins);
int b = Lua.GETARG_B(ins);
int bx = Lua.GETARG_Bx(ins);
int sbx = Lua.GETARG_sBx(ins);
int c = Lua.GETARG_C(ins);
switch ( o ) {
case Lua.OP_GETUPVAL: /* A B R(A):= UpValue[B] */
builder.loadUpvalue( b );
builder.storeLocal( pc, a );
break;
case Lua.OP_SETUPVAL: /* A B UpValue[B]:= R(A) */
builder.storeUpvalue( pc, b, a );
break;
case Lua.OP_NEWTABLE: /* A B C R(A):= {} (size = B,C) */
builder.newTable( b, c );
builder.storeLocal( pc, a );
break;
case Lua.OP_MOVE:/* A B R(A):= R(B) */
builder.loadLocal( pc, b );
builder.storeLocal( pc, a );
break;
case Lua.OP_UNM: /* A B R(A):= -R(B) */
case Lua.OP_NOT: /* A B R(A):= not R(B) */
case Lua.OP_LEN: /* A B R(A):= length of R(B) */
builder.loadLocal( pc, b );
builder.unaryop( o );
builder.storeLocal( pc, a );
break;
case Lua.OP_LOADK:/* A Bx R(A):= Kst(Bx) */
builder.loadConstant( p.k[bx] );
builder.storeLocal( pc, a );
break;
case Lua.OP_LOADNIL: /* A B R(A):= ...:= R(A+B):= nil */
builder.loadNil();
for ( ; b>=0; a++, b-- ) {
if ( b > 0 )
builder.dup();
builder.storeLocal( pc, a );
}
break;
case Lua.OP_GETTABUP: /* A B C R(A) := UpValue[B][RK(C)] */
builder.loadUpvalue( b );
loadLocalOrConstant( p, builder, pc, c );
builder.getTable();
builder.storeLocal( pc, a );
break;
case Lua.OP_GETTABLE: /* A B C R(A):= R(B)[RK(C)] */
builder.loadLocal( pc, b );
loadLocalOrConstant( p, builder, pc, c );
builder.getTable();
builder.storeLocal( pc, a );
break;
case Lua.OP_SETTABUP: /* A B C UpValue[A][RK(B)] := RK(C) */
builder.loadUpvalue( a );
loadLocalOrConstant( p, builder, pc, b );
loadLocalOrConstant( p, builder, pc, c );
builder.setTable();
break;
case Lua.OP_SETTABLE: /* A B C R(A)[RK(B)]:= RK(C) */
builder.loadLocal( pc, a );
loadLocalOrConstant( p, builder, pc, b );
loadLocalOrConstant( p, builder, pc, c );
builder.setTable();
break;
case Lua.OP_ADD: /* A B C R(A):= RK(B) + RK(C) */
case Lua.OP_SUB: /* A B C R(A):= RK(B) - RK(C) */
case Lua.OP_MUL: /* A B C R(A):= RK(B) * RK(C) */
case Lua.OP_DIV: /* A B C R(A):= RK(B) / RK(C) */
case Lua.OP_MOD: /* A B C R(A):= RK(B) % RK(C) */
case Lua.OP_POW: /* A B C R(A):= RK(B) ^ RK(C) */
loadLocalOrConstant( p, builder, pc, b );
loadLocalOrConstant( p, builder, pc, c );
builder.binaryop( o );
builder.storeLocal( pc, a );
break;
case Lua.OP_SELF: /* A B C R(A+1):= R(B): R(A):= R(B)[RK(C)] */
builder.loadLocal(pc,b);
builder.dup();
builder.storeLocal(pc, a+1);
loadLocalOrConstant( p, builder, pc, c );
builder.getTable();
builder.storeLocal(pc, a);
break;
case Lua.OP_CONCAT: /* A B C R(A):= R(B).. ... ..R(C) */
for ( int k=b; k<=c; k++ )
builder.loadLocal(pc, k);
if ( c > b+1 ) {
builder.tobuffer();
for ( int k=c; --k>=b; )
builder.concatbuffer();
builder.tovalue();
} else {
builder.concatvalue();
}
builder.storeLocal(pc, a);
break;
case Lua.OP_LOADBOOL:/* A B C R(A):= (Bool)B: if (C) pc++ */
builder.loadBoolean( b!=0 );
builder.storeLocal( pc, a );
if ( c!=0 )
builder.addBranch(pc, JavaBuilder.BRANCH_GOTO, pc+2);
break;
case Lua.OP_JMP: /* sBx pc+=sBx */
if (a > 0) {
for (int i = a-1; i < pi.openups.length; ++i) {
builder.closeUpvalue(pc, i);
}
}
builder.addBranch(pc, JavaBuilder.BRANCH_GOTO, pc+1+sbx);
break;
case Lua.OP_EQ: /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
case Lua.OP_LT: /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
case Lua.OP_LE: /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
loadLocalOrConstant( p, builder, pc, b );
loadLocalOrConstant( p, builder, pc, c );
builder.compareop(o);
builder.addBranch(pc, (a!=0? JavaBuilder.BRANCH_IFEQ: JavaBuilder.BRANCH_IFNE), pc+2);
break;
case Lua.OP_TEST: /* A C if not (R(A) <=> C) then pc++ */
builder.loadLocal( pc, a );
builder.toBoolean();
builder.addBranch(pc, (c!=0? JavaBuilder.BRANCH_IFEQ: JavaBuilder.BRANCH_IFNE), pc+2);
break;
case Lua.OP_TESTSET: /* A B C if (R(B) <=> C) then R(A):= R(B) else pc++ */
builder.loadLocal( pc, b );
builder.toBoolean();
builder.addBranch(pc, (c!=0? JavaBuilder.BRANCH_IFEQ: JavaBuilder.BRANCH_IFNE), pc+2);
builder.loadLocal( pc, b );
builder.storeLocal( pc, a );
break;
case Lua.OP_CALL: { /* A B C R(A), ... ,R(A+C-2):= R(A)(R(A+1), ... ,R(A+B-1)) */
// load function
builder.loadLocal(pc, a);
// load args
int narg = b - 1;
switch ( narg ) {
case 0: case 1: case 2: case 3:
for ( int i=1; i<b; i++ )
builder.loadLocal(pc, a+i);
break;
default: // fixed arg count > 3
builder.newVarargs( pc, a+1, b-1 );
narg = -1;
break;
case -1: // prev vararg result
loadVarargResults( builder, pc, a+1, vresultbase );
narg = -1;
break;
}
// call or invoke
boolean useinvoke = narg<0 || c<1 || c>2;
if ( useinvoke )
builder.invoke(narg);
else
builder.call(narg);
// handle results
switch ( c ) {
case 1:
builder.pop();
break;
case 2:
if ( useinvoke )
builder.arg( 1 );
builder.storeLocal(pc, a);
break;
default: // fixed result count - unpack args
for ( int i=1; i<c; i++ ) {
if ( i+1 < c )
builder.dup();
builder.arg( i );
builder.storeLocal(pc, a+i-1);
}
break;
case 0: // vararg result
vresultbase = a;
builder.storeVarresult();
break;
}
}
break;
case Lua.OP_TAILCALL: /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
// load function
builder.loadLocal(pc, a);
// load args
switch ( b ) {
case 1:
builder.loadNone();
break;
case 2:
builder.loadLocal(pc, a+1);
break;
default: // fixed arg count > 1
builder.newVarargs( pc, a+1, b-1 );
break;
case 0: // prev vararg result
loadVarargResults( builder, pc, a+1, vresultbase );
break;
}
builder.newTailcallVarargs();
builder.areturn();
break;
case Lua.OP_RETURN: /* A B return R(A), ... ,R(A+B-2) (see note) */
if ( c == 1 ) {
builder.loadNone();
} else {
switch ( b ) {
case 0: loadVarargResults( builder, pc, a, vresultbase ); break;
case 1: builder.loadNone(); break;
case 2: builder.loadLocal(pc, a); break;
default: builder.newVarargs(pc, a, b-1); break;
}
}
builder.areturn();
break;
case Lua.OP_FORPREP: /* A sBx R(A)-=R(A+2): pc+=sBx */
builder.loadLocal(pc, a);
builder.loadLocal(pc, a+2);
builder.binaryop( Lua.OP_SUB );
builder.storeLocal(pc, a);
builder.addBranch(pc, JavaBuilder.BRANCH_GOTO, pc+1+sbx);
break;
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) }*/
builder.loadLocal(pc, a);
builder.loadLocal(pc, a+2);
builder.binaryop( Lua.OP_ADD );
builder.dup();
builder.dup();
builder.storeLocal(pc, a);
builder.storeLocal(pc, a+3);
builder.loadLocal(pc, a+1); // limit
builder.loadLocal(pc, a+2); // step
builder.testForLoop();
builder.addBranch(pc, JavaBuilder.BRANCH_IFNE, pc+1+sbx);
break;
case Lua.OP_TFORCALL: /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
builder.loadLocal(pc, a);
builder.loadLocal(pc, a+1);
builder.loadLocal(pc, a+2);
builder.invoke(2);
for ( int i=1; i<=c; i++ ) {
if ( i < c )
builder.dup();
builder.arg( i );
builder.storeLocal(pc, a+2+i);
}
break;
case Lua.OP_TFORLOOP:/* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx } */
builder.loadLocal(pc, a+1);
builder.dup();
builder.storeLocal(pc, a);
builder.isNil();
builder.addBranch(pc, JavaBuilder.BRANCH_IFEQ, pc+1+sbx);
break;
case Lua.OP_SETLIST: /* A B C R(A)[(C-1)*FPF+i]:= R(A+i), 1 <= i <= B */
int index0 = (c-1)*Lua.LFIELDS_PER_FLUSH + 1;
builder.loadLocal( pc, a );
if ( b == 0 ) {
int nstack = vresultbase - (a+1);
if ( nstack > 0 ) {
builder.setlistStack( pc, a+1, index0, nstack );
index0 += nstack;
}
builder.setlistVarargs( index0, vresultbase );
} else {
builder.setlistStack( pc, a+1, index0, b );
builder.pop();
}
break;
case Lua.OP_CLOSURE: /* A Bx R(A):= closure(KPROTO[Bx], R(A), ... ,R(A+n)) */
{
Prototype newp = p.p[bx];
int nup = newp.upvalues.length;
String protoname = pi.subprotos[bx].name;
builder.closureCreate( protoname );
if ( nup > 0 )
builder.dup();
builder.storeLocal( pc, a );
for ( int up=0; up<nup; ++up ) {
if ( up+1 < nup )
builder.dup();
Upvaldesc u = newp.upvalues[up];
if (u.instack)
builder.closureInitUpvalueFromLocal( protoname, up, pc, u.idx );
else
builder.closureInitUpvalueFromUpvalue( protoname, up, u.idx );
}
break;
}
case Lua.OP_VARARG: /* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
if ( b == 0 ) {
builder.loadVarargs();
builder.storeVarresult();
vresultbase = a;
} else {
for ( int i=1; i<b; ++a, ++i ) {
builder.loadVarargs( i );
builder.storeLocal(pc, a);
}
}
break;
}
// let builder process branch instructions
builder.onEndOfLuaInstruction( pc0, line );
}
}
}
private void loadVarargResults(JavaBuilder builder, int pc, int a, int vresultbase) {
if ( vresultbase <= a ) {
builder.loadVarresult();
builder.subargs( a+1-vresultbase );
} else if ( vresultbase == a ) {
builder.loadVarresult();
} else {
builder.newVarargsVarresult(pc, a, vresultbase-a);
}
}
private void loadLocalOrConstant(Prototype p, JavaBuilder builder, int pc, int borc) {
if ( borc<=0xff )
builder.loadLocal( pc, borc );
else
builder.loadConstant( p.k[borc&0xff] );
}
}

View File

@@ -0,0 +1,73 @@
package org.luaj.vm2.luajc;
import java.util.HashMap;
import java.util.Map;
import org.luaj.vm2.LuaFunction;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Prototype;
/*******************************************************************************
* Copyright (c) 2010 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.
******************************************************************************/
public class JavaLoader extends ClassLoader {
private Map<String,byte[]> unloaded = new HashMap<String,byte[]>();
public JavaLoader() {
}
public LuaFunction load( Prototype p, String classname, String filename, LuaValue env ) {
JavaGen jg = new JavaGen( p, classname, filename, false );
return load( jg, env );
}
public LuaFunction load( JavaGen jg, LuaValue env ) {
include( jg );
return load( jg.classname, env );
}
public LuaFunction load(String classname, LuaValue env) {
try {
Class c = loadClass( classname );
LuaFunction v = (LuaFunction) c.newInstance();
v.initupvalue1(env);
return v;
} catch ( Exception e ) {
e.printStackTrace();
throw new IllegalStateException("bad class gen: "+e);
}
}
public void include( JavaGen jg ) {
unloaded.put( jg.classname, jg.bytecode );
for ( int i=0, n=jg.inners!=null? jg.inners.length: 0; i<n; i++ )
include( jg.inners[i] );
}
public Class findClass(String classname) throws ClassNotFoundException {
byte[] bytes = (byte[]) unloaded.get(classname);
if ( bytes != null )
return defineClass(classname, bytes, 0, bytes.length);
return super.findClass(classname);
}
}

View File

@@ -0,0 +1,132 @@
/*******************************************************************************
* Copyright (c) 2010 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.luajc;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Hashtable;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaClosure;
import org.luaj.vm2.LuaFunction;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Prototype;
import org.luaj.vm2.compiler.LuaC;
/**
* Implementation of {@link Globals.Compiler} which does direct
* lua-to-java-bytecode compiling.
* <p>
* By default, when using {@link org.luaj.vm2.lib.jse.JsePlatform} or
* {@link org.luaj.vm2.lib.jme.JmePlatform}
* to construct globals, the plain compiler {@link LuaC} is installed and lua code
* will only be compiled into lua bytecode and execute as {@link LuaClosure}.
* <p>
* To override the default compiling behavior with {@link LuaJC}
* lua-to-java bytecode compiler, install it before undumping code,
* for example:
* <pre> {@code
* LuaValue globals = JsePlatform.standardGlobals();
* LuaJC.install(globals);
* LuaValue chunk = globals.load( "print('hello, world'), "main.lua");
* System.out.println(chunk.isclosure()); // Will be false when LuaJC is working.
* chunk.call();
* } </pre>
* <p>
* This requires the bcel library to be on the class path to work as expected.
* If the library is not found, the default {@link LuaC} lua-to-lua-bytecode
* compiler will be used.
*
* @see Globals#compiler
* @see #install(Globals)
* @see LuaC
* @see LuaValue
*/
public class LuaJC implements Globals.Loader {
public static final LuaJC instance = new LuaJC();
/**
* Install the compiler as the main Globals.Loader to use in a set of globals.
* Will fall back to the LuaC prototype compiler.
*/
public static final void install(Globals G) {
G.loader = instance;
}
protected LuaJC() {}
public Hashtable compileAll(InputStream script, String chunkname, String filename, Globals globals, boolean genmain) throws IOException {
final String classname = toStandardJavaClassName( chunkname );
final Prototype p = globals.loadPrototype(script, classname, "bt");
return compileProtoAndSubProtos(p, classname, filename, genmain);
}
public Hashtable compileAll(Reader script, String chunkname, String filename, Globals globals, boolean genmain) throws IOException {
final String classname = toStandardJavaClassName( chunkname );
final Prototype p = globals.compilePrototype(script, classname);
return compileProtoAndSubProtos(p, classname, filename, genmain);
}
private Hashtable compileProtoAndSubProtos(Prototype p, String classname, String filename, boolean genmain) throws IOException {
final String luaname = toStandardLuaFileName( filename );
final Hashtable h = new Hashtable();
final JavaGen gen = new JavaGen(p, classname, luaname, genmain);
insert( h, gen );
return h;
}
private void insert(Hashtable h, JavaGen gen) {
h.put(gen.classname, gen.bytecode);
for ( int i=0, n=gen.inners!=null? gen.inners.length: 0; i<n; i++ )
insert(h, gen.inners[i]);
}
public LuaFunction load(Prototype p, String name, LuaValue globals) throws IOException {
String luaname = toStandardLuaFileName( name );
String classname = toStandardJavaClassName( luaname );
JavaLoader loader = new JavaLoader();
return loader.load(p, classname, luaname, globals);
}
private static String toStandardJavaClassName( String luachunkname ) {
String stub = toStub( luachunkname );
StringBuffer classname = new StringBuffer();
for (int i = 0, n = stub.length(); i < n; ++i) {
final char c = stub.charAt(i);
classname.append((((i == 0) && Character.isJavaIdentifierStart(c)) || ((i > 0) && Character.isJavaIdentifierPart(c)))? c: '_');
}
return classname.toString();
}
private static String toStandardLuaFileName( String luachunkname ) {
String stub = toStub( luachunkname );
String filename = stub.replace('.','/')+".lua";
return filename.startsWith("@")? filename.substring(1): filename;
}
private static String toStub( String s ) {
String stub = s.endsWith(".lua")? s.substring(0,s.length()-4): s;
return stub;
}
}

View File

@@ -0,0 +1,534 @@
package org.luaj.vm2.luajc;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.Hashtable;
import org.luaj.vm2.Lua;
import org.luaj.vm2.LuaString;
import org.luaj.vm2.Print;
import org.luaj.vm2.Prototype;
import org.luaj.vm2.Upvaldesc;
/**
* Prototype information for static single-assignment analysis
*/
public class ProtoInfo {
public final String name;
public final Prototype prototype; // the prototype that this info is about
public final ProtoInfo[] subprotos; // one per enclosed prototype, or null
public final BasicBlock[] blocks; // basic block analysis of code branching
public final BasicBlock[] blocklist; // blocks in breadth-first order
public final VarInfo[] params; // Parameters and initial values of stack variables
public final VarInfo[][] vars; // Each variable
public final UpvalInfo[] upvals; // from outer scope
public final UpvalInfo[][] openups; // per slot, upvalues allocated by this prototype
// A main chunk proto info.
public ProtoInfo(Prototype p, String name) {
// For the outer chunk, we have one upvalue which is the environment.
this(p,name,null);
}
private ProtoInfo(Prototype p, String name, UpvalInfo[] u) {
this.name = name;
this.prototype = p;
this.upvals = u != null? u: new UpvalInfo[] { new UpvalInfo(this) };
this.subprotos = p.p!=null&&p.p.length>0? new ProtoInfo[p.p.length]: null;
// find basic blocks
this.blocks = BasicBlock.findBasicBlocks(p);
this.blocklist = BasicBlock.findLiveBlocks(blocks);
// params are inputs to first block
this.params = new VarInfo[p.maxstacksize];
for ( int slot=0; slot<p.maxstacksize; slot++ ) {
VarInfo v = VarInfo.PARAM(slot);
params[slot] = v;
}
// find variables
this.vars = findVariables();
replaceTrivialPhiVariables();
// find upvalues, create sub-prototypes
this.openups = new UpvalInfo[p.maxstacksize][];
findUpvalues();
}
public String toString() {
StringBuffer sb = new StringBuffer();
// prototpye name
sb.append( "proto '"+name+"'\n" );
// upvalues from outer scopes
for ( int i=0, n=(upvals!=null? upvals.length: 0); i<n; i++ )
sb.append( " up["+i+"]: "+upvals[i]+"\n" );
// basic blocks
for ( int i=0; i<blocklist.length; i++ ) {
BasicBlock b = blocklist[i];
int pc0 = b.pc0;
sb.append( " block "+b.toString() );
appendOpenUps( sb, -1 );
// instructions
for ( int pc=pc0; pc<=b.pc1; pc++ ) {
// open upvalue storage
appendOpenUps( sb, pc );
// opcode
sb.append( " " );
for ( int j=0; j<prototype.maxstacksize; j++ ) {
VarInfo v = vars[j][pc];
String u = (v==null? "": v.upvalue!=null? !v.upvalue.rw? "[C] ": (v.allocupvalue&&v.pc==pc? "[*] ": "[] "): " ");
String s = v==null? "null ": String.valueOf(v);
sb.append( s+u);
}
sb.append( " " );
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ops = Print.ps;
Print.ps = new PrintStream(baos);
try {
Print.printOpCode(prototype, pc);
} finally {
Print.ps.close();
Print.ps = ops;
}
sb.append( baos.toString() );
sb.append( "\n" );
}
}
// nested functions
for ( int i=0, n=subprotos!=null? subprotos.length: 0; i<n; i++ ) {
sb.append( subprotos[i].toString() );
}
return sb.toString();
}
private void appendOpenUps(StringBuffer sb, int pc) {
for ( int j=0; j<prototype.maxstacksize; j++ ) {
VarInfo v = (pc<0? params[j]: vars[j][pc]);
if ( v != null && v.pc == pc && v.allocupvalue ) {
sb.append( " open: "+v.upvalue+"\n" );
}
}
}
private VarInfo[][] findVariables() {
// create storage for variables.
int n = prototype.code.length;
int m = prototype.maxstacksize;
VarInfo[][] v = new VarInfo[m][];
for ( int i=0; i<v.length; i++ )
v[i] = new VarInfo[n];
// process instructions
for ( int bi=0; bi<blocklist.length; bi++ ) {
BasicBlock b0 = blocklist[bi];
// input from previous blocks
int nprev = b0.prev!=null? b0.prev.length: 0;
for ( int slot=0; slot<m; slot++ ) {
VarInfo var = null;
if ( nprev == 0 )
var = params[slot];
else if ( nprev == 1 )
var = v[slot][b0.prev[0].pc1];
else {
for ( int i=0; i<nprev; i++ ) {
BasicBlock bp = b0.prev[i];
if ( v[slot][bp.pc1] == VarInfo.INVALID )
var = VarInfo.INVALID;
}
}
if ( var == null )
var = VarInfo.PHI(this, slot, b0.pc0);
v[slot][b0.pc0] = var;
}
// process instructions for this basic block
for ( int pc=b0.pc0; pc<=b0.pc1; pc++ ) {
// propogate previous values except at block boundaries
if ( pc > b0.pc0 )
propogateVars( v, pc-1, pc );
int a,b,c;
int ins = prototype.code[pc];
int op = Lua.GET_OPCODE(ins);
// account for assignments, references and invalidations
switch ( op ) {
case Lua.OP_LOADK:/* A Bx R(A) := Kst(Bx) */
case Lua.OP_LOADBOOL:/* A B C R(A) := (Bool)B; if (C) pc++ */
case Lua.OP_GETUPVAL: /* A B R(A) := UpValue[B] */
case Lua.OP_NEWTABLE: /* A B C R(A) := {} (size = B,C) */
a = Lua.GETARG_A( ins );
v[a][pc] = new VarInfo(a,pc);
break;
case Lua.OP_MOVE:/* A B R(A) := R(B) */
case Lua.OP_UNM: /* A B R(A) := -R(B) */
case Lua.OP_NOT: /* A B R(A) := not R(B) */
case Lua.OP_LEN: /* A B R(A) := length of R(B) */
case Lua.OP_TESTSET: /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
a = Lua.GETARG_A( ins );
b = Lua.GETARG_B( ins );
v[b][pc].isreferenced = true;
v[a][pc] = new VarInfo(a,pc);
break;
case Lua.OP_ADD: /* A B C R(A) := RK(B) + RK(C) */
case Lua.OP_SUB: /* A B C R(A) := RK(B) - RK(C) */
case Lua.OP_MUL: /* A B C R(A) := RK(B) * RK(C) */
case Lua.OP_DIV: /* A B C R(A) := RK(B) / RK(C) */
case Lua.OP_MOD: /* A B C R(A) := RK(B) % RK(C) */
case Lua.OP_POW: /* A B C R(A) := RK(B) ^ RK(C) */
a = Lua.GETARG_A( ins );
b = Lua.GETARG_B( ins );
c = Lua.GETARG_C( ins );
if (!Lua.ISK(b)) v[b][pc].isreferenced = true;
if (!Lua.ISK(c)) v[c][pc].isreferenced = true;
v[a][pc] = new VarInfo(a,pc);
break;
case Lua.OP_SETTABLE: /* A B C R(A)[RK(B)]:= RK(C) */
a = Lua.GETARG_A( ins );
b = Lua.GETARG_B( ins );
c = Lua.GETARG_C( ins );
v[a][pc].isreferenced = true;
if (!Lua.ISK(b)) v[b][pc].isreferenced = true;
if (!Lua.ISK(c)) v[c][pc].isreferenced = true;
break;
case Lua.OP_SETTABUP: /* A B C UpValue[A][RK(B)] := RK(C) */
b = Lua.GETARG_B( ins );
c = Lua.GETARG_C( ins );
if (!Lua.ISK(b)) v[b][pc].isreferenced = true;
if (!Lua.ISK(c)) v[c][pc].isreferenced = true;
break;
case Lua.OP_CONCAT: /* A B C R(A) := R(B).. ... ..R(C) */
a = Lua.GETARG_A( ins );
b = Lua.GETARG_B( ins );
c = Lua.GETARG_C( ins );
for ( ; b<=c; b++ )
v[b][pc].isreferenced = true;
v[a][pc] = new VarInfo(a,pc);
break;
case Lua.OP_FORPREP: /* A sBx R(A)-=R(A+2); pc+=sBx */
a = Lua.GETARG_A( ins );
v[a+2][pc].isreferenced = true;
v[a][pc] = new VarInfo(a,pc);
break;
case Lua.OP_GETTABLE: /* A B C R(A) := R(B)[RK(C)] */
a = Lua.GETARG_A( ins );
b = Lua.GETARG_B( ins );
c = Lua.GETARG_C( ins );
v[b][pc].isreferenced = true;
if (!Lua.ISK(c)) v[c][pc].isreferenced = true;
v[a][pc] = new VarInfo(a,pc);
break;
case Lua.OP_GETTABUP: /* A B C R(A) := UpValue[B][RK(C)] */
a = Lua.GETARG_A( ins );
c = Lua.GETARG_C( ins );
if (!Lua.ISK(c)) v[c][pc].isreferenced = true;
v[a][pc] = new VarInfo(a,pc);
break;
case Lua.OP_SELF: /* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
a = Lua.GETARG_A( ins );
b = Lua.GETARG_B( ins );
c = Lua.GETARG_C( ins );
v[b][pc].isreferenced = true;
if (!Lua.ISK(c)) v[c][pc].isreferenced = true;
v[a][pc] = new VarInfo(a,pc);
v[a+1][pc] = new VarInfo(a+1,pc);
break;
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) }*/
a = Lua.GETARG_A( ins );
v[a][pc].isreferenced = true;
v[a+2][pc].isreferenced = true;
v[a][pc] = new VarInfo(a,pc);
v[a][pc].isreferenced = true;
v[a+1][pc].isreferenced = true;
v[a+3][pc] = new VarInfo(a+3,pc);
break;
case Lua.OP_LOADNIL: /* A B R(A) := ... := R(A+B) := nil */
a = Lua.GETARG_A( ins );
b = Lua.GETARG_B( ins );
for ( ; b-->=0; a++ )
v[a][pc] = new VarInfo(a,pc);
break;
case Lua.OP_VARARG: /* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
a = Lua.GETARG_A( ins );
b = Lua.GETARG_B( ins );
for ( int j=1; j<b; j++, a++ )
v[a][pc] = new VarInfo(a,pc);
if ( b == 0 )
for ( ; a<m; a++ )
v[a][pc] = VarInfo.INVALID;
break;
case Lua.OP_CALL: /* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
a = Lua.GETARG_A( ins );
b = Lua.GETARG_B( ins );
c = Lua.GETARG_C( ins );
v[a][pc].isreferenced = true;
v[a][pc].isreferenced = true;
for ( int i=1; i<=b-1; i++ )
v[a+i][pc].isreferenced = true;
for ( int j=0; j<=c-2; j++, a++ )
v[a][pc] = new VarInfo(a,pc);
for ( ; a<m; a++ )
v[a][pc] = VarInfo.INVALID;
break;
case Lua.OP_TFORCALL: /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
a = Lua.GETARG_A( ins );
c = Lua.GETARG_C( ins );
v[a++][pc].isreferenced = true;
v[a++][pc].isreferenced = true;
v[a++][pc].isreferenced = true;
for ( int j=0; j<c; j++, a++ )
v[a][pc] = new VarInfo(a,pc);
for ( ; a<m; a++ )
v[a][pc] = VarInfo.INVALID;
break;
case Lua.OP_TFORLOOP: /* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx */
a = Lua.GETARG_A( ins );
v[a+1][pc].isreferenced = true;
v[a][pc] = new VarInfo(a,pc);
break;
case Lua.OP_TAILCALL: /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
a = Lua.GETARG_A( ins );
b = Lua.GETARG_B( ins );
v[a][pc].isreferenced = true;
for ( int i=1; i<=b-1; i++ )
v[a+i][pc].isreferenced = true;
break;
case Lua.OP_RETURN: /* A B return R(A), ... ,R(A+B-2) (see note) */
a = Lua.GETARG_A( ins );
b = Lua.GETARG_B( ins );
for ( int i=0; i<=b-2; i++ )
v[a+i][pc].isreferenced = true;
break;
case Lua.OP_CLOSURE: { /* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */
a = Lua.GETARG_A( ins );
b = Lua.GETARG_Bx( ins );
Upvaldesc[] upvalues = prototype.p[b].upvalues;
for (int k = 0, nups = upvalues.length; k < nups; ++k)
if (upvalues[k].instack)
v[upvalues[k].idx][pc].isreferenced = true;
v[a][pc] = new VarInfo(a,pc);
break;
}
case Lua.OP_SETLIST: /* A B C R(A)[(C-1)*FPF+i]:= R(A+i), 1 <= i <= B */
a = Lua.GETARG_A( ins );
b = Lua.GETARG_B( ins );
v[a][pc].isreferenced = true;
for ( int i=1; i<=b; i++ )
v[a+i][pc].isreferenced = true;
break;
case Lua.OP_SETUPVAL: /* A B UpValue[B]:= R(A) */
case Lua.OP_TEST: /* A C if not (R(A) <=> C) then pc++ */
a = Lua.GETARG_A( ins );
v[a][pc].isreferenced = true;
break;
case Lua.OP_EQ: /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
case Lua.OP_LT: /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
case Lua.OP_LE: /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
b = Lua.GETARG_B( ins );
c = Lua.GETARG_C( ins );
if (!Lua.ISK(b)) v[b][pc].isreferenced = true;
if (!Lua.ISK(c)) v[c][pc].isreferenced = true;
break;
case Lua.OP_JMP: /* sBx pc+=sBx */
a = Lua.GETARG_A( ins );
if (a > 0)
for ( --a; a<m; a++ )
v[a][pc] = VarInfo.INVALID;
break;
default:
throw new IllegalStateException("unhandled opcode: "+ins);
}
}
}
return v;
}
private static void propogateVars(VarInfo[][] v, int pcfrom, int pcto) {
for ( int j=0, m=v.length; j<m; j++ )
v[j][pcto] = v[j][pcfrom];
}
private void replaceTrivialPhiVariables() {
for ( int i=0; i<blocklist.length; i++ ) {
BasicBlock b0 = blocklist[i];
for ( int slot=0; slot<prototype.maxstacksize; slot++ ) {
VarInfo vold = vars[slot][b0.pc0];
VarInfo vnew = vold.resolvePhiVariableValues();
if ( vnew != null )
substituteVariable( slot, vold, vnew );
}
}
}
private void substituteVariable(int slot, VarInfo vold, VarInfo vnew) {
for ( int i=0, n=prototype.code.length; i<n; i++ )
replaceAll( vars[slot], vars[slot].length, vold, vnew );
}
private void replaceAll(VarInfo[] v, int n, VarInfo vold, VarInfo vnew) {
for ( int i=0; i<n; i++ )
if ( v[i] == vold )
v[i] = vnew;
}
private void findUpvalues() {
int[] code = prototype.code;
int n = code.length;
// propogate to inner prototypes
String[] names = findInnerprotoNames();
for ( int pc=0; pc<n; pc++ ) {
if ( Lua.GET_OPCODE(code[pc]) == Lua.OP_CLOSURE ) {
int bx = Lua.GETARG_Bx(code[pc]);
Prototype newp = prototype.p[bx];
UpvalInfo[] newu = new UpvalInfo[newp.upvalues.length];
String newname = name + "$" + names[bx];
for ( int j=0; j<newp.upvalues.length; ++j ) {
Upvaldesc u = newp.upvalues[j];
newu[j] = u.instack? findOpenUp(pc,u.idx) : upvals[u.idx];
}
subprotos[bx] = new ProtoInfo(newp, newname, newu);
}
}
// mark all upvalues that are written locally as read/write
for ( int pc=0; pc<n; pc++ ) {
if ( Lua.GET_OPCODE(code[pc]) == Lua.OP_SETUPVAL )
upvals[Lua.GETARG_B(code[pc])].rw = true;
}
}
private UpvalInfo findOpenUp(int pc, int slot) {
if ( openups[slot] == null )
openups[slot] = new UpvalInfo[prototype.code.length];
if ( openups[slot][pc] != null )
return openups[slot][pc];
UpvalInfo u = new UpvalInfo(this, pc, slot);
for ( int i=0, n=prototype.code.length; i<n; ++i )
if ( vars[slot][i] != null && vars[slot][i].upvalue == u )
openups[slot][i] = u;
return u;
}
public boolean isUpvalueAssign(int pc, int slot) {
VarInfo v = pc<0? params[slot]: vars[slot][pc];
return v != null && v.upvalue != null && v.upvalue.rw;
}
public boolean isUpvalueCreate(int pc, int slot) {
VarInfo v = pc<0? params[slot]: vars[slot][pc];
return v != null && v.upvalue != null && v.upvalue.rw && v.allocupvalue && pc == v.pc;
}
public boolean isUpvalueRefer(int pc, int slot) {
// special case when both refer and assign in same instruction
if ( pc > 0 && vars[slot][pc] != null && vars[slot][pc].pc == pc && vars[slot][pc-1] != null )
pc -= 1;
VarInfo v = pc<0? params[slot]: vars[slot][pc];
return v != null && v.upvalue != null && v.upvalue.rw;
}
public boolean isInitialValueUsed(int slot) {
VarInfo v = params[slot];
return v.isreferenced;
}
public boolean isReadWriteUpvalue(UpvalInfo u) {
return u.rw;
}
private String[] findInnerprotoNames() {
if (prototype.p.length <= 0)
return null;
// find all the prototype names
String[] names = new String[prototype.p.length];
Hashtable used = new Hashtable();
int[] code = prototype.code;
int n = code.length;
for ( int pc=0; pc<n; pc++ ) {
if ( Lua.GET_OPCODE(code[pc]) == Lua.OP_CLOSURE ) {
int bx = Lua.GETARG_Bx(code[pc]);
String name = null;
final int i = code[pc+1];
switch (Lua.GET_OPCODE(i)) {
case Lua.OP_SETTABLE:
case Lua.OP_SETTABUP: {
final int b = Lua.GETARG_B(i);
if (Lua.ISK(b))
name = prototype.k[b&0x0ff].tojstring();
break;
}
case Lua.OP_SETUPVAL: {
final int b = Lua.GETARG_B(i);
final LuaString s = prototype.upvalues[b].name;
if (s != null)
name = s.tojstring();
break;
}
default: // Local variable
final int a = Lua.GETARG_A(code[pc]);
final LuaString s = prototype.getlocalname(a+1, pc+1);
if (s != null)
name = s.tojstring();
break;
}
name = name != null? toJavaClassPart(name): String.valueOf(bx);
if (used.containsKey(name)) {
String basename = name;
int count = 1;
do {
name = basename + '$' + count++;
} while (used.containsKey(name));
}
used.put(name, Boolean.TRUE);
names[bx] = name;
}
}
return names;
}
private static String toJavaClassPart(String s) {
final int n = s.length();
StringBuffer sb = new StringBuffer(n);
for (int i = 0; i < n; ++i)
sb.append( Character.isJavaIdentifierPart(s.charAt(i)) ? s.charAt(i): '_' );
return sb.toString();
}
}

View File

@@ -0,0 +1,153 @@
/**
*
*/
package org.luaj.vm2.luajc;
import org.luaj.vm2.Lua;
public class UpvalInfo {
ProtoInfo pi; // where defined
int slot; // where defined
int nvars; // number of vars involved
VarInfo var[]; // list of vars
boolean rw; // read-write
// Upval info representing the implied context containing only the environment.
public UpvalInfo(ProtoInfo pi) {
this.pi = pi;
this.slot = 0;
this.nvars = 1;
this.var = new VarInfo[] { VarInfo.PARAM(0) };
this.rw = false;
}
public UpvalInfo(ProtoInfo pi, int pc, int slot) {
this.pi = pi;
this.slot = slot;
this.nvars = 0;
this.var = null;
includeVarAndPosteriorVars( pi.vars[slot][pc] );
for ( int i=0; i<nvars; i++ )
var[i].allocupvalue = testIsAllocUpvalue( var[i] );
this.rw = nvars > 1;
}
private boolean includeVarAndPosteriorVars( VarInfo var ) {
if ( var == null || var == VarInfo.INVALID )
return false;
if ( var.upvalue == this )
return true;
var.upvalue = this;
appendVar( var );
if ( isLoopVariable( var ) )
return false;
boolean loopDetected = includePosteriorVarsCheckLoops( var );
if ( loopDetected )
includePriorVarsIgnoreLoops( var );
return loopDetected;
}
private boolean isLoopVariable(VarInfo var) {
if ( var.pc >= 0 ) {
switch ( Lua.GET_OPCODE(pi.prototype.code[var.pc]) ) {
case Lua.OP_TFORLOOP:
case Lua.OP_FORLOOP:
return true;
}
}
return false;
}
private boolean includePosteriorVarsCheckLoops( VarInfo prior ) {
boolean loopDetected = false;
for ( int i=0, n=pi.blocklist.length; i<n; i++ ) {
BasicBlock b = pi.blocklist[i];
VarInfo v = pi.vars[slot][b.pc1];
if ( v == prior ) {
for ( int j=0, m=b.next!=null? b.next.length: 0; j<m; j++ ) {
BasicBlock b1 = b.next[j];
VarInfo v1 = pi.vars[slot][b1.pc0];
if ( v1 != prior ) {
loopDetected |= includeVarAndPosteriorVars( v1 );
if ( v1.isPhiVar() )
includePriorVarsIgnoreLoops( v1 );
}
}
} else {
for ( int pc=b.pc1-1; pc>=b.pc0; pc-- ) {
if ( pi.vars[slot][pc] == prior ) {
loopDetected |= includeVarAndPosteriorVars( pi.vars[slot][pc+1] );
break;
}
}
}
}
return loopDetected;
}
private void includePriorVarsIgnoreLoops(VarInfo poster) {
for ( int i=0, n=pi.blocklist.length; i<n; i++ ) {
BasicBlock b = pi.blocklist[i];
VarInfo v = pi.vars[slot][b.pc0];
if ( v == poster ) {
for ( int j=0, m=b.prev!=null? b.prev.length: 0; j<m; j++ ) {
BasicBlock b0 = b.prev[j];
VarInfo v0 = pi.vars[slot][b0.pc1];
if ( v0 != poster )
includeVarAndPosteriorVars( v0 );
}
} else {
for ( int pc=b.pc0+1; pc<=b.pc1; pc++ ) {
if ( pi.vars[slot][pc] == poster ) {
includeVarAndPosteriorVars( pi.vars[slot][pc-1] );
break;
}
}
}
}
}
private void appendVar(VarInfo v) {
if ( nvars == 0 ) {
var = new VarInfo[1];
} else if ( nvars+1 >= var.length ) {
VarInfo[] s = var;
var = new VarInfo[nvars*2+1];
System.arraycopy(s, 0, var, 0, nvars);
}
var[nvars++] = v;
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append( pi.name );
for ( int i=0; i<nvars; i++ ) {
sb.append( i>0? ",": " " );
sb.append( String.valueOf(var[i]));
}
if ( rw )
sb.append( "(rw)" );
return sb.toString();
}
private boolean testIsAllocUpvalue(VarInfo v) {
if ( v.pc < 0 )
return true;
BasicBlock b = pi.blocks[v.pc];
if ( v.pc > b.pc0 )
return pi.vars[slot][v.pc-1].upvalue != this;
if ( b.prev == null ) {
v = pi.params[slot];
if ( v != null && v.upvalue != this )
return true;
} else {
for ( int i=0, n=b.prev.length; i<n; i++ ) {
v = pi.vars[slot][b.prev[i].pc1];
if ( v != null && v.upvalue != this )
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,144 @@
/**
*
*/
package org.luaj.vm2.luajc;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class VarInfo {
public static VarInfo INVALID = new VarInfo(-1, -1);
public static VarInfo PARAM(int slot) {
return new ParamVarInfo(slot, -1);
}
public static VarInfo NIL(final int slot) {
return new NilVarInfo(slot, -1);
}
public static VarInfo PHI(final ProtoInfo pi, final int slot, final int pc) {
return new PhiVarInfo(pi, slot, pc);
}
public final int slot; // where assigned
public final int pc; // where assigned, or -1 if for block inputs
public UpvalInfo upvalue; // not null if this var is an upvalue
public boolean allocupvalue; // true if this variable allocates r/w upvalue
// storage
public boolean isreferenced; // true if this variable is refenced by some
// opcode
public VarInfo(int slot, int pc) {
this.slot = slot;
this.pc = pc;
}
public String toString() {
return slot < 0 ? "x.x" : (slot + "." + pc);
}
/** Return replacement variable if there is exactly one value possible,
* otherwise compute entire collection of variables and return null.
* Computes the list of aall variable values, and saves it for the future.
*
* @return new Variable to replace with if there is only one value, or null to leave alone.
*/
public VarInfo resolvePhiVariableValues() {
return null;
}
protected void collectUniqueValues(Set visitedBlocks, Set vars) {
vars.add(this);
}
public boolean isPhiVar() {
return false;
}
private static final class ParamVarInfo extends VarInfo {
private ParamVarInfo(int slot, int pc) {
super(slot, pc);
}
public String toString() {
return slot + ".p";
}
}
private static final class NilVarInfo extends VarInfo {
private NilVarInfo(int slot, int pc) {
super(slot, pc);
}
public String toString() {
return "nil";
}
}
private static final class PhiVarInfo extends VarInfo {
private final ProtoInfo pi;
VarInfo[] values;
private PhiVarInfo(ProtoInfo pi, int slot, int pc) {
super(slot, pc);
this.pi = pi;
}
public boolean isPhiVar() {
return true;
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append( super.toString() );
sb.append("={");
for (int i=0, n=(values!=null? values.length : 0); i<n; i++) {
if ( i>0 )
sb.append( "," );
sb.append(String.valueOf(values[i]));
}
sb.append("}");
return sb.toString();
}
public VarInfo resolvePhiVariableValues() {
Set visitedBlocks = new HashSet();
Set vars = new HashSet();
this.collectUniqueValues(visitedBlocks, vars);
if (vars.contains(INVALID))
return INVALID;
int n = vars.size();
Iterator it = vars.iterator();
if (n == 1) {
VarInfo v = (VarInfo) it.next();
v.isreferenced |= this.isreferenced;
return v;
}
this.values = new VarInfo[n];
for ( int i=0; i<n; i++ ) {
this.values[i] = (VarInfo) it.next();
this.values[i].isreferenced |= this.isreferenced;
}
return null;
}
protected void collectUniqueValues(Set visitedBlocks, Set vars) {
BasicBlock b = pi.blocks[pc];
if ( pc == 0 )
vars.add(pi.params[slot]);
for (int i = 0, n = b.prev != null ? b.prev.length : 0; i < n; i++) {
BasicBlock bp = b.prev[i];
if (!visitedBlocks.contains(bp)) {
visitedBlocks.add(bp);
VarInfo v = pi.vars[slot][bp.pc1];
if ( v != null )
v.collectUniqueValues(visitedBlocks, vars);
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,240 @@
/* Generated By:JavaCC: Do not edit this line. LuaParserConstants.java */
package org.luaj.vm2.parser;
/**
* Token literal values and constants.
* Generated by org.javacc.parser.OtherFilesGen#start()
*/
public interface LuaParserConstants {
/** End of File. */
int EOF = 0;
/** RegularExpression Id. */
int COMMENT = 17;
/** RegularExpression Id. */
int LONGCOMMENT0 = 18;
/** RegularExpression Id. */
int LONGCOMMENT1 = 19;
/** RegularExpression Id. */
int LONGCOMMENT2 = 20;
/** RegularExpression Id. */
int LONGCOMMENT3 = 21;
/** RegularExpression Id. */
int LONGCOMMENTN = 22;
/** RegularExpression Id. */
int LONGSTRING0 = 23;
/** RegularExpression Id. */
int LONGSTRING1 = 24;
/** RegularExpression Id. */
int LONGSTRING2 = 25;
/** RegularExpression Id. */
int LONGSTRING3 = 26;
/** RegularExpression Id. */
int LONGSTRINGN = 27;
/** RegularExpression Id. */
int AND = 29;
/** RegularExpression Id. */
int BREAK = 30;
/** RegularExpression Id. */
int DO = 31;
/** RegularExpression Id. */
int ELSE = 32;
/** RegularExpression Id. */
int ELSEIF = 33;
/** RegularExpression Id. */
int END = 34;
/** RegularExpression Id. */
int FALSE = 35;
/** RegularExpression Id. */
int FOR = 36;
/** RegularExpression Id. */
int FUNCTION = 37;
/** RegularExpression Id. */
int GOTO = 38;
/** RegularExpression Id. */
int IF = 39;
/** RegularExpression Id. */
int IN = 40;
/** RegularExpression Id. */
int LOCAL = 41;
/** RegularExpression Id. */
int NIL = 42;
/** RegularExpression Id. */
int NOT = 43;
/** RegularExpression Id. */
int OR = 44;
/** RegularExpression Id. */
int RETURN = 45;
/** RegularExpression Id. */
int REPEAT = 46;
/** RegularExpression Id. */
int THEN = 47;
/** RegularExpression Id. */
int TRUE = 48;
/** RegularExpression Id. */
int UNTIL = 49;
/** RegularExpression Id. */
int WHILE = 50;
/** RegularExpression Id. */
int NAME = 51;
/** RegularExpression Id. */
int NUMBER = 52;
/** RegularExpression Id. */
int FLOAT = 53;
/** RegularExpression Id. */
int FNUM = 54;
/** RegularExpression Id. */
int DIGIT = 55;
/** RegularExpression Id. */
int EXP = 56;
/** RegularExpression Id. */
int HEX = 57;
/** RegularExpression Id. */
int HEXNUM = 58;
/** RegularExpression Id. */
int HEXDIGIT = 59;
/** RegularExpression Id. */
int HEXEXP = 60;
/** RegularExpression Id. */
int STRING = 61;
/** RegularExpression Id. */
int CHARSTRING = 62;
/** RegularExpression Id. */
int QUOTED = 63;
/** RegularExpression Id. */
int DECIMAL = 64;
/** RegularExpression Id. */
int DBCOLON = 65;
/** RegularExpression Id. */
int UNICODE = 66;
/** RegularExpression Id. */
int CHAR = 67;
/** RegularExpression Id. */
int LF = 68;
/** Lexical state. */
int DEFAULT = 0;
/** Lexical state. */
int IN_COMMENT = 1;
/** Lexical state. */
int IN_LC0 = 2;
/** Lexical state. */
int IN_LC1 = 3;
/** Lexical state. */
int IN_LC2 = 4;
/** Lexical state. */
int IN_LC3 = 5;
/** Lexical state. */
int IN_LCN = 6;
/** Lexical state. */
int IN_LS0 = 7;
/** Lexical state. */
int IN_LS1 = 8;
/** Lexical state. */
int IN_LS2 = 9;
/** Lexical state. */
int IN_LS3 = 10;
/** Lexical state. */
int IN_LSN = 11;
/** Literal token values. */
String[] tokenImage = {
"<EOF>",
"\" \"",
"\"\\t\"",
"\"\\n\"",
"\"\\r\"",
"\"\\f\"",
"\"--[[\"",
"\"--[=[\"",
"\"--[==[\"",
"\"--[===[\"",
"<token of kind 10>",
"\"[[\"",
"\"[=[\"",
"\"[==[\"",
"\"[===[\"",
"<token of kind 15>",
"\"--\"",
"<COMMENT>",
"\"]]\"",
"\"]=]\"",
"\"]==]\"",
"\"]===]\"",
"<LONGCOMMENTN>",
"\"]]\"",
"\"]=]\"",
"\"]==]\"",
"\"]===]\"",
"<LONGSTRINGN>",
"<token of kind 28>",
"\"and\"",
"\"break\"",
"\"do\"",
"\"else\"",
"\"elseif\"",
"\"end\"",
"\"false\"",
"\"for\"",
"\"function\"",
"\"goto\"",
"\"if\"",
"\"in\"",
"\"local\"",
"\"nil\"",
"\"not\"",
"\"or\"",
"\"return\"",
"\"repeat\"",
"\"then\"",
"\"true\"",
"\"until\"",
"\"while\"",
"<NAME>",
"<NUMBER>",
"<FLOAT>",
"<FNUM>",
"<DIGIT>",
"<EXP>",
"<HEX>",
"<HEXNUM>",
"<HEXDIGIT>",
"<HEXEXP>",
"<STRING>",
"<CHARSTRING>",
"<QUOTED>",
"<DECIMAL>",
"\"::\"",
"<UNICODE>",
"<CHAR>",
"<LF>",
"\"#\"",
"\";\"",
"\"=\"",
"\",\"",
"\".\"",
"\":\"",
"\"(\"",
"\")\"",
"\"[\"",
"\"]\"",
"\"...\"",
"\"{\"",
"\"}\"",
"\"+\"",
"\"-\"",
"\"*\"",
"\"/\"",
"\"^\"",
"\"%\"",
"\"..\"",
"\"<\"",
"\"<=\"",
"\">\"",
"\">=\"",
"\"==\"",
"\"~=\"",
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,187 @@
/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 5.0 */
/* JavaCCOptions:KEEP_LINE_COL=null */
package org.luaj.vm2.parser;
/**
* This exception is thrown when parse errors are encountered.
* You can explicitly create objects of this exception type by
* calling the method generateParseException in the generated
* parser.
*
* You can modify this class to customize your error reporting
* mechanisms so long as you retain the public fields.
*/
public class ParseException extends Exception {
/**
* The version identifier for this Serializable class.
* Increment only if the <i>serialized</i> form of the
* class changes.
*/
private static final long serialVersionUID = 1L;
/**
* This constructor is used by the method "generateParseException"
* in the generated parser. Calling this constructor generates
* a new object of this type with the fields "currentToken",
* "expectedTokenSequences", and "tokenImage" set.
*/
public ParseException(Token currentTokenVal,
int[][] expectedTokenSequencesVal,
String[] tokenImageVal
)
{
super(initialise(currentTokenVal, expectedTokenSequencesVal, tokenImageVal));
currentToken = currentTokenVal;
expectedTokenSequences = expectedTokenSequencesVal;
tokenImage = tokenImageVal;
}
/**
* The following constructors are for use by you for whatever
* purpose you can think of. Constructing the exception in this
* manner makes the exception behave in the normal way - i.e., as
* documented in the class "Throwable". The fields "errorToken",
* "expectedTokenSequences", and "tokenImage" do not contain
* relevant information. The JavaCC generated code does not use
* these constructors.
*/
public ParseException() {
super();
}
/** Constructor with message. */
public ParseException(String message) {
super(message);
}
/**
* This is the last token that has been consumed successfully. If
* this object has been created due to a parse error, the token
* followng this token will (therefore) be the first error token.
*/
public Token currentToken;
/**
* Each entry in this array is an array of integers. Each array
* of integers represents a sequence of tokens (by their ordinal
* values) that is expected at this point of the parse.
*/
public int[][] expectedTokenSequences;
/**
* This is a reference to the "tokenImage" array of the generated
* parser within which the parse error occurred. This array is
* defined in the generated ...Constants interface.
*/
public String[] tokenImage;
/**
* It uses "currentToken" and "expectedTokenSequences" to generate a parse
* error message and returns it. If this object has been created
* due to a parse error, and you do not catch it (it gets thrown
* from the parser) the correct error message
* gets displayed.
*/
private static String initialise(Token currentToken,
int[][] expectedTokenSequences,
String[] tokenImage) {
String eol = System.getProperty("line.separator", "\n");
StringBuffer expected = new StringBuffer();
int maxSize = 0;
for (int i = 0; i < expectedTokenSequences.length; i++) {
if (maxSize < expectedTokenSequences[i].length) {
maxSize = expectedTokenSequences[i].length;
}
for (int j = 0; j < expectedTokenSequences[i].length; j++) {
expected.append(tokenImage[expectedTokenSequences[i][j]]).append(' ');
}
if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) {
expected.append("...");
}
expected.append(eol).append(" ");
}
String retval = "Encountered \"";
Token tok = currentToken.next;
for (int i = 0; i < maxSize; i++) {
if (i != 0) retval += " ";
if (tok.kind == 0) {
retval += tokenImage[0];
break;
}
retval += " " + tokenImage[tok.kind];
retval += " \"";
retval += add_escapes(tok.image);
retval += " \"";
tok = tok.next;
}
retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
retval += "." + eol;
if (expectedTokenSequences.length == 1) {
retval += "Was expecting:" + eol + " ";
} else {
retval += "Was expecting one of:" + eol + " ";
}
retval += expected.toString();
return retval;
}
/**
* The end of line string for this machine.
*/
protected String eol = System.getProperty("line.separator", "\n");
/**
* Used to convert raw characters to their escaped version
* when these raw version cannot be used as part of an ASCII
* string literal.
*/
static String add_escapes(String str) {
StringBuffer retval = new StringBuffer();
char ch;
for (int i = 0; i < str.length(); i++) {
switch (str.charAt(i))
{
case 0 :
continue;
case '\b':
retval.append("\\b");
continue;
case '\t':
retval.append("\\t");
continue;
case '\n':
retval.append("\\n");
continue;
case '\f':
retval.append("\\f");
continue;
case '\r':
retval.append("\\r");
continue;
case '\"':
retval.append("\\\"");
continue;
case '\'':
retval.append("\\\'");
continue;
case '\\':
retval.append("\\\\");
continue;
default:
if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
String s = "0000" + Integer.toString(ch, 16);
retval.append("\\u" + s.substring(s.length() - 4, s.length()));
} else {
retval.append(ch);
}
continue;
}
}
return retval.toString();
}
}
/* JavaCC - OriginalChecksum=ef246095a930e4915c0d4bbf4c9880ad (do not edit this line) */

View File

@@ -0,0 +1,469 @@
/* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 5.0 */
/* JavaCCOptions:STATIC=false,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package org.luaj.vm2.parser;
/**
* An implementation of interface CharStream, where the stream is assumed to
* contain only ASCII characters (without unicode processing).
*/
public class SimpleCharStream
{
/** Whether parser is static. */
public static final boolean staticFlag = false;
int bufsize;
int available;
int tokenBegin;
/** Position in buffer. */
public int bufpos = -1;
protected int bufline[];
protected int bufcolumn[];
protected int column = 0;
protected int line = 1;
protected boolean prevCharIsCR = false;
protected boolean prevCharIsLF = false;
protected java.io.Reader inputStream;
protected char[] buffer;
protected int maxNextCharInd = 0;
protected int inBuf = 0;
protected int tabSize = 1;
public void setTabSize(int i) { tabSize = i; }
public int getTabSize(int i) { return tabSize; }
protected void ExpandBuff(boolean wrapAround)
{
char[] newbuffer = new char[bufsize + 2048];
int newbufline[] = new int[bufsize + 2048];
int newbufcolumn[] = new int[bufsize + 2048];
try
{
if (wrapAround)
{
System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
System.arraycopy(buffer, 0, newbuffer, bufsize - tokenBegin, bufpos);
buffer = newbuffer;
System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos);
bufline = newbufline;
System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos);
bufcolumn = newbufcolumn;
maxNextCharInd = (bufpos += (bufsize - tokenBegin));
}
else
{
System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
buffer = newbuffer;
System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
bufline = newbufline;
System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
bufcolumn = newbufcolumn;
maxNextCharInd = (bufpos -= tokenBegin);
}
}
catch (Throwable t)
{
throw new Error(t.getMessage());
}
bufsize += 2048;
available = bufsize;
tokenBegin = 0;
}
protected void FillBuff() throws java.io.IOException
{
if (maxNextCharInd == available)
{
if (available == bufsize)
{
if (tokenBegin > 2048)
{
bufpos = maxNextCharInd = 0;
available = tokenBegin;
}
else if (tokenBegin < 0)
bufpos = maxNextCharInd = 0;
else
ExpandBuff(false);
}
else if (available > tokenBegin)
available = bufsize;
else if ((tokenBegin - available) < 2048)
ExpandBuff(true);
else
available = tokenBegin;
}
int i;
try {
if ((i = inputStream.read(buffer, maxNextCharInd, available - maxNextCharInd)) == -1)
{
inputStream.close();
throw new java.io.IOException();
}
else
maxNextCharInd += i;
return;
}
catch(java.io.IOException e) {
--bufpos;
backup(0);
if (tokenBegin == -1)
tokenBegin = bufpos;
throw e;
}
}
/** Start. */
public char BeginToken() throws java.io.IOException
{
tokenBegin = -1;
char c = readChar();
tokenBegin = bufpos;
return c;
}
protected void UpdateLineColumn(char c)
{
column++;
if (prevCharIsLF)
{
prevCharIsLF = false;
line += (column = 1);
}
else if (prevCharIsCR)
{
prevCharIsCR = false;
if (c == '\n')
{
prevCharIsLF = true;
}
else
line += (column = 1);
}
switch (c)
{
case '\r' :
prevCharIsCR = true;
break;
case '\n' :
prevCharIsLF = true;
break;
case '\t' :
column--;
column += (tabSize - (column % tabSize));
break;
default :
break;
}
bufline[bufpos] = line;
bufcolumn[bufpos] = column;
}
/** Read a character. */
public char readChar() throws java.io.IOException
{
if (inBuf > 0)
{
--inBuf;
if (++bufpos == bufsize)
bufpos = 0;
return buffer[bufpos];
}
if (++bufpos >= maxNextCharInd)
FillBuff();
char c = buffer[bufpos];
UpdateLineColumn(c);
return c;
}
/**
* @deprecated
* @see #getEndColumn
*/
public int getColumn() {
return bufcolumn[bufpos];
}
/**
* @deprecated
* @see #getEndLine
*/
public int getLine() {
return bufline[bufpos];
}
/** Get token end column number. */
public int getEndColumn() {
return bufcolumn[bufpos];
}
/** Get token end line number. */
public int getEndLine() {
return bufline[bufpos];
}
/** Get token beginning column number. */
public int getBeginColumn() {
return bufcolumn[tokenBegin];
}
/** Get token beginning line number. */
public int getBeginLine() {
return bufline[tokenBegin];
}
/** Backup a number of characters. */
public void backup(int amount) {
inBuf += amount;
if ((bufpos -= amount) < 0)
bufpos += bufsize;
}
/** Constructor. */
public SimpleCharStream(java.io.Reader dstream, int startline,
int startcolumn, int buffersize)
{
inputStream = dstream;
line = startline;
column = startcolumn - 1;
available = bufsize = buffersize;
buffer = new char[buffersize];
bufline = new int[buffersize];
bufcolumn = new int[buffersize];
}
/** Constructor. */
public SimpleCharStream(java.io.Reader dstream, int startline,
int startcolumn)
{
this(dstream, startline, startcolumn, 4096);
}
/** Constructor. */
public SimpleCharStream(java.io.Reader dstream)
{
this(dstream, 1, 1, 4096);
}
/** Reinitialise. */
public void ReInit(java.io.Reader dstream, int startline,
int startcolumn, int buffersize)
{
inputStream = dstream;
line = startline;
column = startcolumn - 1;
if (buffer == null || buffersize != buffer.length)
{
available = bufsize = buffersize;
buffer = new char[buffersize];
bufline = new int[buffersize];
bufcolumn = new int[buffersize];
}
prevCharIsLF = prevCharIsCR = false;
tokenBegin = inBuf = maxNextCharInd = 0;
bufpos = -1;
}
/** Reinitialise. */
public void ReInit(java.io.Reader dstream, int startline,
int startcolumn)
{
ReInit(dstream, startline, startcolumn, 4096);
}
/** Reinitialise. */
public void ReInit(java.io.Reader dstream)
{
ReInit(dstream, 1, 1, 4096);
}
/** Constructor. */
public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline,
int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException
{
this(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize);
}
/** Constructor. */
public SimpleCharStream(java.io.InputStream dstream, int startline,
int startcolumn, int buffersize)
{
this(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
}
/** Constructor. */
public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline,
int startcolumn) throws java.io.UnsupportedEncodingException
{
this(dstream, encoding, startline, startcolumn, 4096);
}
/** Constructor. */
public SimpleCharStream(java.io.InputStream dstream, int startline,
int startcolumn)
{
this(dstream, startline, startcolumn, 4096);
}
/** Constructor. */
public SimpleCharStream(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException
{
this(dstream, encoding, 1, 1, 4096);
}
/** Constructor. */
public SimpleCharStream(java.io.InputStream dstream)
{
this(dstream, 1, 1, 4096);
}
/** Reinitialise. */
public void ReInit(java.io.InputStream dstream, String encoding, int startline,
int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException
{
ReInit(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize);
}
/** Reinitialise. */
public void ReInit(java.io.InputStream dstream, int startline,
int startcolumn, int buffersize)
{
ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
}
/** Reinitialise. */
public void ReInit(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException
{
ReInit(dstream, encoding, 1, 1, 4096);
}
/** Reinitialise. */
public void ReInit(java.io.InputStream dstream)
{
ReInit(dstream, 1, 1, 4096);
}
/** Reinitialise. */
public void ReInit(java.io.InputStream dstream, String encoding, int startline,
int startcolumn) throws java.io.UnsupportedEncodingException
{
ReInit(dstream, encoding, startline, startcolumn, 4096);
}
/** Reinitialise. */
public void ReInit(java.io.InputStream dstream, int startline,
int startcolumn)
{
ReInit(dstream, startline, startcolumn, 4096);
}
/** Get token literal value. */
public String GetImage()
{
if (bufpos >= tokenBegin)
return new String(buffer, tokenBegin, bufpos - tokenBegin + 1);
else
return new String(buffer, tokenBegin, bufsize - tokenBegin) +
new String(buffer, 0, bufpos + 1);
}
/** Get the suffix. */
public char[] GetSuffix(int len)
{
char[] ret = new char[len];
if ((bufpos + 1) >= len)
System.arraycopy(buffer, bufpos - len + 1, ret, 0, len);
else
{
System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0,
len - bufpos - 1);
System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1);
}
return ret;
}
/** Reset buffer when finished. */
public void Done()
{
buffer = null;
bufline = null;
bufcolumn = null;
}
/**
* Method to adjust line and column numbers for the start of a token.
*/
public void adjustBeginLineColumn(int newLine, int newCol)
{
int start = tokenBegin;
int len;
if (bufpos >= tokenBegin)
{
len = bufpos - tokenBegin + inBuf + 1;
}
else
{
len = bufsize - tokenBegin + bufpos + 1 + inBuf;
}
int i = 0, j = 0, k = 0;
int nextColDiff = 0, columnDiff = 0;
while (i < len && bufline[j = start % bufsize] == bufline[k = ++start % bufsize])
{
bufline[j] = newLine;
nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j];
bufcolumn[j] = newCol + columnDiff;
columnDiff = nextColDiff;
i++;
}
if (i < len)
{
bufline[j] = newLine++;
bufcolumn[j] = newCol + columnDiff;
while (i++ < len)
{
if (bufline[j = start % bufsize] != bufline[++start % bufsize])
bufline[j] = newLine++;
else
bufline[j] = newLine;
}
}
line = bufline[j];
column = bufcolumn[j];
}
}
/* JavaCC - OriginalChecksum=ab0c629eabd887d4c88cec51eb5e6477 (do not edit this line) */

View File

@@ -0,0 +1,131 @@
/* Generated By:JavaCC: Do not edit this line. Token.java Version 5.0 */
/* JavaCCOptions:TOKEN_EXTENDS=,KEEP_LINE_COL=null,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package org.luaj.vm2.parser;
/**
* Describes the input token stream.
*/
public class Token implements java.io.Serializable {
/**
* The version identifier for this Serializable class.
* Increment only if the <i>serialized</i> form of the
* class changes.
*/
private static final long serialVersionUID = 1L;
/**
* An integer that describes the kind of this token. This numbering
* system is determined by JavaCCParser, and a table of these numbers is
* stored in the file ...Constants.java.
*/
public int kind;
/** The line number of the first character of this Token. */
public int beginLine;
/** The column number of the first character of this Token. */
public int beginColumn;
/** The line number of the last character of this Token. */
public int endLine;
/** The column number of the last character of this Token. */
public int endColumn;
/**
* The string image of the token.
*/
public String image;
/**
* A reference to the next regular (non-special) token from the input
* stream. If this is the last token from the input stream, or if the
* token manager has not read tokens beyond this one, this field is
* set to null. This is true only if this token is also a regular
* token. Otherwise, see below for a description of the contents of
* this field.
*/
public Token next;
/**
* This field is used to access special tokens that occur prior to this
* token, but after the immediately preceding regular (non-special) token.
* If there are no such special tokens, this field is set to null.
* When there are more than one such special token, this field refers
* to the last of these special tokens, which in turn refers to the next
* previous special token through its specialToken field, and so on
* until the first special token (whose specialToken field is null).
* The next fields of special tokens refer to other special tokens that
* immediately follow it (without an intervening regular token). If there
* is no such token, this field is null.
*/
public Token specialToken;
/**
* An optional attribute value of the Token.
* Tokens which are not used as syntactic sugar will often contain
* meaningful values that will be used later on by the compiler or
* interpreter. This attribute value is often different from the image.
* Any subclass of Token that actually wants to return a non-null value can
* override this method as appropriate.
*/
public Object getValue() {
return null;
}
/**
* No-argument constructor
*/
public Token() {}
/**
* Constructs a new token for the specified Image.
*/
public Token(int kind)
{
this(kind, null);
}
/**
* Constructs a new token for the specified Image and Kind.
*/
public Token(int kind, String image)
{
this.kind = kind;
this.image = image;
}
/**
* Returns the image.
*/
public String toString()
{
return image;
}
/**
* Returns a new Token object, by default. However, if you want, you
* can create and return subclass objects based on the value of ofKind.
* Simply add the cases to the switch for all those special cases.
* For example, if you have a subclass of Token called IDToken that
* you want to create if ofKind is ID, simply add something like :
*
* case MyParserConstants.ID : return new IDToken(ofKind, image);
*
* to the following switch statement. Then you can cast matchedToken
* variable to the appropriate type and use sit in your lexical actions.
*/
public static Token newToken(int ofKind, String image)
{
switch(ofKind)
{
default : return new Token(ofKind, image);
}
}
public static Token newToken(int ofKind)
{
return newToken(ofKind, null);
}
}
/* JavaCC - OriginalChecksum=70d73add5771158f10d1ae81755e7cfc (do not edit this line) */

View File

@@ -0,0 +1,147 @@
/* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 5.0 */
/* JavaCCOptions: */
package org.luaj.vm2.parser;
/** Token Manager Error. */
public class TokenMgrError extends Error
{
/**
* The version identifier for this Serializable class.
* Increment only if the <i>serialized</i> form of the
* class changes.
*/
private static final long serialVersionUID = 1L;
/*
* Ordinals for various reasons why an Error of this type can be thrown.
*/
/**
* Lexical error occurred.
*/
static final int LEXICAL_ERROR = 0;
/**
* An attempt was made to create a second instance of a static token manager.
*/
static final int STATIC_LEXER_ERROR = 1;
/**
* Tried to change to an invalid lexical state.
*/
static final int INVALID_LEXICAL_STATE = 2;
/**
* Detected (and bailed out of) an infinite loop in the token manager.
*/
static final int LOOP_DETECTED = 3;
/**
* Indicates the reason why the exception is thrown. It will have
* one of the above 4 values.
*/
int errorCode;
/**
* Replaces unprintable characters by their escaped (or unicode escaped)
* equivalents in the given string
*/
protected static final String addEscapes(String str) {
StringBuffer retval = new StringBuffer();
char ch;
for (int i = 0; i < str.length(); i++) {
switch (str.charAt(i))
{
case 0 :
continue;
case '\b':
retval.append("\\b");
continue;
case '\t':
retval.append("\\t");
continue;
case '\n':
retval.append("\\n");
continue;
case '\f':
retval.append("\\f");
continue;
case '\r':
retval.append("\\r");
continue;
case '\"':
retval.append("\\\"");
continue;
case '\'':
retval.append("\\\'");
continue;
case '\\':
retval.append("\\\\");
continue;
default:
if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
String s = "0000" + Integer.toString(ch, 16);
retval.append("\\u" + s.substring(s.length() - 4, s.length()));
} else {
retval.append(ch);
}
continue;
}
}
return retval.toString();
}
/**
* Returns a detailed message for the Error when it is thrown by the
* token manager to indicate a lexical error.
* Parameters :
* EOFSeen : indicates if EOF caused the lexical error
* curLexState : lexical state in which this error occurred
* errorLine : line number when the error occurred
* errorColumn : column number when the error occurred
* errorAfter : prefix that was seen before this error occurred
* curchar : the offending character
* Note: You can customize the lexical error message by modifying this method.
*/
protected static String LexicalError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar) {
return("Lexical error at line " +
errorLine + ", column " +
errorColumn + ". Encountered: " +
(EOFSeen ? "<EOF> " : ("\"" + addEscapes(String.valueOf(curChar)) + "\"") + " (" + (int)curChar + "), ") +
"after : \"" + addEscapes(errorAfter) + "\"");
}
/**
* You can also modify the body of this method to customize your error messages.
* For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not
* of end-users concern, so you can return something like :
*
* "Internal Error : Please file a bug report .... "
*
* from this method for such cases in the release version of your parser.
*/
public String getMessage() {
return super.getMessage();
}
/*
* Constructors of various flavors follow.
*/
/** No arg constructor. */
public TokenMgrError() {
}
/** Constructor with message and reason. */
public TokenMgrError(String message, int reason) {
super(message);
errorCode = reason;
}
/** Full Constructor. */
public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar, int reason) {
this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason);
}
}
/* JavaCC - OriginalChecksum=bd3720425dc7b44a5223b12676db358c (do not edit this line) */

View File

@@ -0,0 +1,266 @@
/*******************************************************************************
* Copyright (c) 2008-2013 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.script;
import java.io.*;
import javax.script.*;
import org.luaj.vm2.*;
import org.luaj.vm2.lib.ThreeArgFunction;
import org.luaj.vm2.lib.TwoArgFunction;
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
/**
* Implementation of the ScriptEngine interface which can compile and execute
* scripts using luaj.
*
* <p>
* This engine requires the types of the Bindings and ScriptContext to be
* compatible with the engine. For creating new client context use
* ScriptEngine.createContext() which will return {@link LuajContext},
* and for client bindings use the default engine scoped bindings or
* construct a {@link LuajBindings} directly.
*/
public class LuaScriptEngine extends AbstractScriptEngine implements ScriptEngine, Compilable {
private static final String __ENGINE_VERSION__ = Lua._VERSION;
private static final String __NAME__ = "Luaj";
private static final String __SHORT_NAME__ = "Luaj";
private static final String __LANGUAGE__ = "lua";
private static final String __LANGUAGE_VERSION__ = "5.2";
private static final String __ARGV__ = "arg";
private static final String __FILENAME__ = "?";
private static final ScriptEngineFactory myFactory = new LuaScriptEngineFactory();
private LuajContext context;
public LuaScriptEngine() {
// set up context
context = new LuajContext();
context.setBindings(createBindings(), ScriptContext.ENGINE_SCOPE);
setContext(context);
// set special values
put(LANGUAGE_VERSION, __LANGUAGE_VERSION__);
put(LANGUAGE, __LANGUAGE__);
put(ENGINE, __NAME__);
put(ENGINE_VERSION, __ENGINE_VERSION__);
put(ARGV, __ARGV__);
put(FILENAME, __FILENAME__);
put(NAME, __SHORT_NAME__);
put("THREADING", null);
}
@Override
public CompiledScript compile(String script) throws ScriptException {
return compile(new StringReader(script));
}
@Override
public CompiledScript compile(Reader script) throws ScriptException {
try {
InputStream is = new Utf8Encoder(script);
try {
final Globals g = context.globals;
final LuaFunction f = g.load(script, "script").checkfunction();
return new LuajCompiledScript(f, g);
} catch ( LuaError lee ) {
throw new ScriptException(lee.getMessage() );
} finally {
is.close();
}
} catch ( Exception e ) {
throw new ScriptException("eval threw "+e.toString());
}
}
@Override
public Object eval(Reader reader, Bindings bindings) throws ScriptException {
return ((LuajCompiledScript) compile(reader)).eval(context.globals, bindings);
}
@Override
public Object eval(String script, Bindings bindings) throws ScriptException {
return eval(new StringReader(script), bindings);
}
@Override
protected ScriptContext getScriptContext(Bindings nn) {
throw new IllegalStateException("LuajScriptEngine should not be allocating contexts.");
}
@Override
public Bindings createBindings() {
return new SimpleBindings();
}
@Override
public Object eval(String script, ScriptContext context)
throws ScriptException {
return eval(new StringReader(script), context);
}
@Override
public Object eval(Reader reader, ScriptContext context)
throws ScriptException {
return compile(reader).eval(context);
}
@Override
public ScriptEngineFactory getFactory() {
return myFactory;
}
class LuajCompiledScript extends CompiledScript {
final LuaFunction function;
final Globals compiling_globals;
LuajCompiledScript(LuaFunction function, Globals compiling_globals) {
this.function = function;
this.compiling_globals = compiling_globals;
}
public ScriptEngine getEngine() {
return LuaScriptEngine.this;
}
public Object eval() throws ScriptException {
return eval(getContext());
}
public Object eval(Bindings bindings) throws ScriptException {
return eval(((LuajContext) getContext()).globals, bindings);
}
public Object eval(ScriptContext context) throws ScriptException {
return eval(((LuajContext) context).globals, context.getBindings(ScriptContext.ENGINE_SCOPE));
}
Object eval(Globals g, Bindings b) throws ScriptException {
g.setmetatable(new BindingsMetatable(b));
LuaFunction f = function;
if (f.isclosure())
f = new LuaClosure(f.checkclosure().p, g);
else {
try {
f = f.getClass().newInstance();
} catch (Exception e) {
throw new ScriptException(e);
}
f.initupvalue1(g);
}
return toJava(f.invoke(LuaValue.NONE));
}
}
// ------ convert char stream to byte stream for lua compiler -----
private final class Utf8Encoder extends InputStream {
private final Reader r;
private final int[] buf = new int[2];
private int n;
private Utf8Encoder(Reader r) {
this.r = r;
}
public int read() throws IOException {
if ( n > 0 )
return buf[--n];
int c = r.read();
if ( c < 0x80 )
return c;
n = 0;
if ( c < 0x800 ) {
buf[n++] = (0x80 | ( c & 0x3f));
return (0xC0 | ((c>>6) & 0x1f));
} else {
buf[n++] = (0x80 | ( c & 0x3f));
buf[n++] = (0x80 | ((c>>6) & 0x3f));
return (0xE0 | ((c>>12) & 0x0f));
}
}
}
static class BindingsMetatable extends LuaTable {
BindingsMetatable(final Bindings bindings) {
this.rawset(LuaValue.INDEX, new TwoArgFunction() {
public LuaValue call(LuaValue table, LuaValue key) {
if (key.isstring())
return toLua(bindings.get(key.tojstring()));
else
return this.rawget(key);
}
});
this.rawset(LuaValue.NEWINDEX, new ThreeArgFunction() {
public LuaValue call(LuaValue table, LuaValue key, LuaValue value) {
if (key.isstring()) {
final String k = key.tojstring();
final Object v = toJava(value);
if (v == null)
bindings.remove(k);
else
bindings.put(k, v);
} else {
this.rawset(key, value);
}
return LuaValue.NONE;
}
});
}
}
static private LuaValue toLua(Object javaValue) {
return javaValue == null? LuaValue.NIL:
javaValue instanceof LuaValue? (LuaValue) javaValue:
CoerceJavaToLua.coerce(javaValue);
}
static private Object toJava(LuaValue luajValue) {
switch ( luajValue.type() ) {
case LuaValue.TNIL: return null;
case LuaValue.TSTRING: return luajValue.tojstring();
case LuaValue.TUSERDATA: return luajValue.checkuserdata(Object.class);
case LuaValue.TNUMBER: return luajValue.isinttype()?
luajValue.toint():
luajValue.todouble();
default: return luajValue;
}
}
static private Object toJava(Varargs v) {
final int n = v.narg();
switch (n) {
case 0: return null;
case 1: return toJava(v.arg1());
default:
Object[] o = new Object[n];
for (int i=0; i<n; ++i)
o[i] = toJava(v.arg(i+1));
return o;
}
}
}

View File

@@ -0,0 +1,128 @@
/*******************************************************************************
* Copyright (c) 2008 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.script;
import java.util.Arrays;
import java.util.List;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
/**
* Jsr 223 scripting engine factory.
*
* Exposes metadata to support the lua language, and constructs
* instances of LuaScriptEngine to handl lua scripts.
*/
public class LuaScriptEngineFactory implements ScriptEngineFactory {
private static final String [] EXTENSIONS = {
"lua",
".lua",
};
private static final String [] MIMETYPES = {
"text/lua",
"application/lua"
};
private static final String [] NAMES = {
"lua",
"luaj",
};
private List<String> extensions;
private List<String> mimeTypes;
private List<String> names;
public LuaScriptEngineFactory() {
extensions = Arrays.asList(EXTENSIONS);
mimeTypes = Arrays.asList(MIMETYPES);
names = Arrays.asList(NAMES);
}
public String getEngineName() {
return getScriptEngine().get(ScriptEngine.ENGINE).toString();
}
public String getEngineVersion() {
return getScriptEngine().get(ScriptEngine.ENGINE_VERSION).toString();
}
public List<String> getExtensions() {
return extensions;
}
public List<String> getMimeTypes() {
return mimeTypes;
}
public List<String> getNames() {
return names;
}
public String getLanguageName() {
return getScriptEngine().get(ScriptEngine.LANGUAGE).toString();
}
public String getLanguageVersion() {
return getScriptEngine().get(ScriptEngine.LANGUAGE_VERSION).toString();
}
public Object getParameter(String key) {
return getScriptEngine().get(key).toString();
}
public String getMethodCallSyntax(String obj, String m, String... args) {
StringBuffer sb = new StringBuffer();
sb.append(obj + ":" + m + "(");
int len = args.length;
for (int i = 0; i < len; i++) {
if (i > 0) {
sb.append(',');
}
sb.append(args[i]);
}
sb.append(")");
return sb.toString();
}
public String getOutputStatement(String toDisplay) {
return "print(" + toDisplay + ")";
}
public String getProgram(String ... statements) {
StringBuffer sb = new StringBuffer();
int len = statements.length;
for (int i = 0; i < len; i++) {
if (i > 0) {
sb.append('\n');
}
sb.append(statements[i]);
}
return sb.toString();
}
public ScriptEngine getScriptEngine() {
return new LuaScriptEngine();
}
}

View File

@@ -0,0 +1,144 @@
/*******************************************************************************
* Copyright (c) 2013 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.script;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.io.Writer;
import javax.script.ScriptContext;
import javax.script.SimpleScriptContext;
import org.luaj.vm2.Globals;
import org.luaj.vm2.lib.jse.JsePlatform;
import org.luaj.vm2.luajc.LuaJC;
/**
* Context for LuaScriptEngine execution which maintains its own Globals,
* and manages the input and output redirection.
*/
public class LuajContext extends SimpleScriptContext implements ScriptContext {
/** Globals for this context instance. */
public final Globals globals;
/** The initial value of globals.STDIN */
private final InputStream stdin;
/** The initial value of globals.STDOUT */
private final PrintStream stdout;
/** The initial value of globals.STDERR */
private final PrintStream stderr;
/** Construct a LuajContext with its own globals which may
* be debug globals depending on the value of the system
* property 'org.luaj.debug'
* <p>
* If the system property 'org.luaj.debug' is set, the globals
* created will be a debug globals that includes the debug
* library. This may provide better stack traces, but may
* have negative impact on performance.
*/
public LuajContext() {
this("true".equals(System.getProperty("org.luaj.debug")),
"true".equals(System.getProperty("org.luaj.luajc")));
}
/** Construct a LuajContext with its own globals, which
* which optionally are debug globals, and optionally use the
* luajc direct lua to java bytecode compiler.
* <p>
* If createDebugGlobals is set, the globals
* created will be a debug globals that includes the debug
* library. This may provide better stack traces, but may
* have negative impact on performance.
* @param createDebugGlobals true to create debug globals,
* false for standard globals.
* @param useLuaJCCompiler true to use the luajc compiler,
* reqwuires bcel to be on the class path.
*/
public LuajContext(boolean createDebugGlobals, boolean useLuaJCCompiler) {
globals = createDebugGlobals?
JsePlatform.debugGlobals():
JsePlatform.standardGlobals();
if (useLuaJCCompiler)
LuaJC.install(globals);
stdin = globals.STDIN;
stdout = globals.STDOUT;
stderr = globals.STDERR;
}
@Override
public void setErrorWriter(Writer writer) {
globals.STDERR = writer != null?
new PrintStream(new WriterOutputStream(writer)):
stderr;
}
@Override
public void setReader(Reader reader) {
globals.STDIN = reader != null?
new ReaderInputStream(reader):
stdin;
}
@Override
public void setWriter(Writer writer) {
globals.STDOUT = writer != null?
new PrintStream(new WriterOutputStream(writer), true):
stdout;
}
static final class WriterOutputStream extends OutputStream {
final Writer w;
WriterOutputStream(Writer w) {
this.w = w;
}
public void write(int b) throws IOException {
w.write(new String(new byte[] {(byte)b}));
}
public void write(byte[] b, int o, int l) throws IOException {
w.write(new String(b, o, l));
}
public void write(byte[] b) throws IOException {
w.write(new String(b));
}
public void close() throws IOException {
w.close();
}
public void flush() throws IOException {
w.flush();
}
}
static final class ReaderInputStream extends InputStream {
final Reader r;
ReaderInputStream(Reader r) {
this.r = r;
}
public int read() throws IOException {
return r.read();
}
}
}

View File

@@ -0,0 +1,105 @@
/*******************************************************************************
* 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.server;
import java.io.InputStream;
import java.io.Reader;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
import org.luaj.vm2.lib.jse.JsePlatform;
/**
* Default {@link Launcher} instance that creates standard globals
* and runs the supplied scripts with chunk name 'main'.
* <P>
* Arguments are coerced into lua using {@link CoerceJavaToLua#coerce(Object)}.
* <P>
* Return values with simple types are coerced into Java simple types.
* Tables, threads, and functions are returned as lua objects.
*
* @see Launcher
* @see LuajClassLoader
* @see LuajClassLoader#NewLauncher()
* @see LuajClassLoader#NewLauncher(Class)
* @since luaj 3.0.1
*/
public class DefaultLauncher implements Launcher {
protected Globals g;
public DefaultLauncher() {
g = JsePlatform.standardGlobals();
}
/** Launches the script with chunk name 'main' */
public Object[] launch(String script, Object[] arg) {
return launchChunk(g.load(script, "main"), arg);
}
/** Launches the script with chunk name 'main' and loading using modes 'bt' */
public Object[] launch(InputStream script, Object[] arg) {
return launchChunk(g.load(script, "main", "bt", g), arg);
}
/** Launches the script with chunk name 'main' */
public Object[] launch(Reader script, Object[] arg) {
return launchChunk(g.load(script, "main"), arg);
}
private Object[] launchChunk(LuaValue chunk, Object[] arg) {
LuaValue args[] = new LuaValue[arg.length];
for (int i = 0; i < args.length; ++i)
args[i] = CoerceJavaToLua.coerce(arg[i]);
Varargs results = chunk.invoke(LuaValue.varargsOf(args));
final int n = results.narg();
Object return_values[] = new Object[n];
for (int i = 0; i < n; ++i) {
LuaValue r = results.arg(i+1);
switch (r.type()) {
case LuaValue.TBOOLEAN:
return_values[i] = r.toboolean();
break;
case LuaValue.TNUMBER:
return_values[i] = r.todouble();
break;
case LuaValue.TINT:
return_values[i] = r.toint();
break;
case LuaValue.TNIL:
return_values[i] = null;
break;
case LuaValue.TSTRING:
return_values[i] = r.tojstring();
break;
case LuaValue.TUSERDATA:
return_values[i] = r.touserdata();
break;
default:
return_values[i] = r;
}
}
return return_values;
}
}

View File

@@ -0,0 +1,70 @@
/*******************************************************************************
* 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.server;
import java.io.InputStream;
import java.io.Reader;
/** Interface to launch lua scripts using the {@link LuajClassLoader}.
* <P>
* <em>Note: This class is experimental and subject to change in future versions.</em>
* <P>
* This interface is purposely genericized to defer class loading so that
* luaj classes can come from the class loader.
* <P>
* The implementation should be acquired using {@link LuajClassLoader#NewLauncher()}
* or {@link LuajClassLoader#NewLauncher(Class)} which ensure that the classes are
* loaded to give each Launcher instance a pristine set of Globals, including
* the shared metatables.
*
* @see LuajClassLoader
* @see LuajClassLoader#NewLauncher()
* @see LuajClassLoader#NewLauncher(Class)
* @see DefaultLauncher
* @since luaj 3.0.1
*/
public interface Launcher {
/** Launch a script contained in a String.
*
* @param script The script contents.
* @param arg Optional arguments supplied to the script.
* @return return values from the script.
*/
public Object[] launch(String script, Object[] arg);
/** Launch a script from an InputStream.
*
* @param script The script as an InputStream.
* @param arg Optional arguments supplied to the script.
* @return return values from the script.
*/
public Object[] launch(InputStream script, Object[] arg);
/** Launch a script from a Reader.
*
* @param script The script as a Reader.
* @param arg Optional arguments supplied to the script.
* @return return values from the script.
*/
public Object[] launch(Reader script, Object[] arg);
}

View File

@@ -0,0 +1,157 @@
/*******************************************************************************
* 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.server;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
/**
* Class loader that can be used to launch a lua script in a Java VM that has a
* unique set of classes for org.luaj classes.
* <P>
* <em>Note: This class is experimental and subject to change in future versions.</em>
* <P>
* By using a custom class loader per script, it allows the script to have
* its own set of globals, including static values such as shared metatables
* that cannot access lua values from other scripts because their classes are
* loaded from different class loaders. Thus normally unsafe libraries such
* as luajava can be exposed to scripts in a server environment using these
* techniques.
* <P>
* All classes in the package "org.luaj.vm2." are considered user classes, and
* loaded into this class loader from their bytes in the class path. Other
* classes are considered systemc classes and loaded via the system loader. This
* class set can be extended by overriding {@link #isUserClass(String)}.
* <P>
* The {@link Launcher} interface is loaded as a system class by exception so
* that the caller may use it to launch lua scripts.
* <P>
* By default {@link #NewLauncher()} creates a subclass of {@link Launcher} of
* type {@link DefaultLauncher} which creates debug globals, runs the script,
* and prints the return values. This behavior can be changed by supplying a
* different implementation class to {@link #NewLauncher(Class)} which must
* extend {@link Launcher}.
*
* @see Launcher
* @see #NewLauncher()
* @see #NewLauncher(Class)
* @see DefaultLauncher
* @since luaj 3.0.1
*/
public class LuajClassLoader extends ClassLoader {
/** String describing the luaj packages to consider part of the user classes */
static final String luajPackageRoot = "org.luaj.vm2.";
/** String describing the Launcher interface to be considered a system class */
static final String launcherInterfaceRoot = Launcher.class.getName();
/** Local cache of classes loaded by this loader. */
Map<String, Class<?>> classes = new HashMap<String, Class<?>>();
/**
* Construct a default {@link Launcher} instance that will load classes in
* its own {@link LuajClassLoader} using the default implementation class
* {@link DefaultLauncher}.
* <P>
* The {@link Launcher} that is returned will be a pristine luaj vm
* whose classes are loaded into this loader including static variables
* such as shared metatables, and should not be able to directly access
* variables from other Launcher instances.
*
* @return {@link Launcher} instance that can be used to launch scripts.
* @throws InstantiationException
* @throws IllegalAccessException
* @throws ClassNotFoundException
*/
public static Launcher NewLauncher() throws InstantiationException,
IllegalAccessException, ClassNotFoundException {
return NewLauncher(DefaultLauncher.class);
}
/**
* Construct a {@link Launcher} instance that will load classes in
* its own {@link LuajClassLoader} using a user-supplied implementation class
* that implements {@link Launcher}.
* <P>
* The {@link Launcher} that is returned will be a pristine luaj vm
* whose classes are loaded into this loader including static variables
* such as shared metatables, and should not be able to directly access
* variables from other Launcher instances.
*
* @return instance of type 'launcher_class' that can be used to launch scripts.
* @throws InstantiationException
* @throws IllegalAccessException
* @throws ClassNotFoundException
*/
public static Launcher NewLauncher(Class<? extends Launcher> launcher_class)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
final LuajClassLoader loader = new LuajClassLoader();
final Object instance = loader.loadAsUserClass(launcher_class.getName())
.newInstance();
return (Launcher) instance;
}
/**
* Test if a class name should be considered a user class and loaded
* by this loader, or a system class and loaded by the system loader.
* @param classname Class name to test.
* @return true if this should be loaded into this class loader.
*/
public static boolean isUserClass(String classname) {
return classname.startsWith(luajPackageRoot)
&& !classname.startsWith(launcherInterfaceRoot);
}
public Class<?> loadClass(String classname) throws ClassNotFoundException {
if (classes.containsKey(classname))
return classes.get(classname);
if (!isUserClass(classname))
return super.findSystemClass(classname);
return loadAsUserClass(classname);
}
private Class<?> loadAsUserClass(String classname) throws ClassNotFoundException {
final String path = classname.replace('.', '/').concat(".class");
InputStream is = getResourceAsStream(path);
if (is != null) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
for (int n = 0; (n = is.read(b)) >= 0;)
baos.write(b, 0, n);
byte[] bytes = baos.toByteArray();
Class<?> result = super.defineClass(classname, bytes, 0,
bytes.length);
classes.put(classname, result);
return result;
} catch (java.io.IOException e) {
throw new ClassNotFoundException("Read failed: " + classname
+ ": " + e);
}
}
throw new ClassNotFoundException("Not found: " + classname);
}
}

View File

@@ -0,0 +1 @@
org.luaj.vm2.script.LuaScriptEngineFactory

View File

@@ -0,0 +1,61 @@
package org.luaj.luajc;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
import org.luaj.vm2.lib.TwoArgFunction;
import org.luaj.vm2.lib.VarArgFunction;
public class SampleMainChunk extends VarArgFunction {
static final LuaValue $print = valueOf("print");
static final LuaValue $foo = valueOf("foo");
LuaValue[] rw_ENV; // The environment when it is read-write
// LuaValue ro_ENV; // The environment when it is read-only in all sub-functions
LuaValue[] rw_openup1; // upvalue that we create and modify in "slot" 1, passed to sub-function in initer.
LuaValue[] rw_openup2; // array is instantiated on first set or before supply to closure, after that value is get, set.
LuaValue[] rw_openup3; // closing these nulls them out, sub-functions still retain references to array & can use
LuaValue ro_openup4; // open upvalue that is read-only once it is supplied to an inner function.
LuaValue ro_openup5; // closing this also nulls it out.
// Must have this in the main chunk so it can be loaded and instantiated on all platforms.
public SampleMainChunk() {
}
public void initupvalue1(LuaValue[] v) {
this.rw_ENV = v;
}
public Varargs invoke(Varargs args) {
rw_ENV[0].get($print).call($foo);
rw_ENV[0].set($print, new InnerFunction(rw_openup3, rw_openup1, ro_openup5));
return null;
}
static class InnerFunction extends TwoArgFunction {
static final LuaValue $print = valueOf("print"); // A constant, named for what it is.
static final LuaValue $foo = valueOf("foo");
final LuaValue[] rw_upvalue1; // from enclosing function, corresponds to upvaldesc not instack.
final LuaValue[] rw_upvalue2; // from enclosing function, corresponds to upvaldesc not instack.
final LuaValue ro_upvalue3; // from enclosing function, but read-only everywhere.
LuaValue[] rw_openup1; // closing these nulls them out, sub-functions still retain references to array & can use
LuaValue ro_openup2; // open upvalue that is read-only once it is supplied to an inner function.
InnerFunction(LuaValue[] rw_upvalue1, LuaValue[] rw_upvalue2, LuaValue ro_upvalue3) {
this.rw_upvalue1 = rw_upvalue1;
this.rw_upvalue2 = rw_upvalue2;
this.ro_upvalue3 = ro_upvalue3;
}
public LuaValue call(LuaValue arg1, LuaValue arg2) {
return NIL;
}
}
}

View File

@@ -0,0 +1,70 @@
/*******************************************************************************
* 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.luajc;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Print;
import org.luaj.vm2.Prototype;
import org.luaj.vm2.lib.jse.JsePlatform;
/** Test the plain old bytecode interpreter */
public class TestLuaJ {
// create the script
public static String name = "script";
public static String script =
"function r(q,...)\n"+
" local a=arg\n"+
" return a and a[2]\n"+
"end\n" +
"function s(q,...)\n"+
" local a=arg\n"+
" local b=...\n"+
" return a and a[2],b\n"+
"end\n" +
"print( r(111,222,333),s(111,222,333) )";
public static void main(String[] args) throws Exception {
System.out.println(script);
// create an environment to run in
Globals globals = JsePlatform.standardGlobals();
// compile into a chunk, or load as a class
LuaValue chunk = globals.load(script, "script");
// The loaded chunk should be a closure, which contains the prototype.
print( chunk.checkclosure().p );
// The chunk can be called with arguments as desired.
chunk.call(LuaValue.ZERO, LuaValue.ONE);
}
private static void print(Prototype p) {
System.out.println("--- "+p);
Print.printCode(p);
if (p.p!=null)
for ( int i=0,n=p.p.length; i<n; i++ )
print( p.p[i] );
}
}

View File

@@ -0,0 +1,102 @@
/*******************************************************************************
* 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.luajc;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Print;
import org.luaj.vm2.Prototype;
import org.luaj.vm2.Varargs;
import org.luaj.vm2.lib.jse.JsePlatform;
import org.luaj.vm2.luajc.LuaJC;
public class TestLuaJC {
// This file will be loaded using the finder as a resource, provided it is in the
// build path. This allows the debugger to find the file when stepping into the function.
public static String filename = "perf/nsieve.lua";
static Globals globals;
public static void main(String[] args) throws Exception {
if (args.length > 0)
filename = args[0];
System.out.println("filename: "+filename);
try {
// create an environment to run in
globals = JsePlatform.standardGlobals();
// print the chunk as a closure, and pretty-print the closure.
LuaValue f = globals.loadfile(filename).arg1();
Prototype p = f.checkclosure().p;
Print.print(p);
// load into a luajc java-bytecode based chunk by installing the LuaJC compiler first
if ( ! (args.length>0 && args[0].equals("nocompile")) ) {
LuaJC.install(globals);
f = globals.loadfile(filename).arg1();
}
// call with arguments
Varargs v = f.invoke(LuaValue.NONE);
// print the result
System.out.println("result: "+v);
// Write out the files.
// saveClasses();
} catch ( Throwable e ) {
e.printStackTrace();
}
}
private static void saveClasses() throws Exception {
// create the chunk
String destdir = ".";
InputStream is = globals.finder.findResource(filename);
Hashtable t = LuaJC.instance.compileAll(is, filename, filename, globals, true);
// write out the chunk
for ( Enumeration e = t.keys(); e.hasMoreElements(); ) {
String key = (String) e.nextElement();
byte[] bytes = (byte[]) t.get(key);
String destpath = (destdir!=null? destdir+"/": "") + key + ".class";
System.out.println(
"chunk "+filename+
" from "+filename+
" written to "+destpath
+" length="+bytes.length+" bytes");
FileOutputStream fos = new FileOutputStream( destpath );
fos.write( bytes );
fos.close();
}
}
}

View File

@@ -0,0 +1,109 @@
/*******************************************************************************
* 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 junit.framework.Test;
import junit.framework.TestSuite;
import org.luaj.vm2.WeakTableTest.WeakKeyTableTest;
import org.luaj.vm2.WeakTableTest.WeakKeyValueTableTest;
import org.luaj.vm2.WeakTableTest.WeakValueTableTest;
import org.luaj.vm2.compiler.CompilerUnitTests;
import org.luaj.vm2.compiler.DumpLoadEndianIntTest;
import org.luaj.vm2.compiler.LuaParserTests;
import org.luaj.vm2.compiler.RegressionTests;
import org.luaj.vm2.compiler.SimpleTests;
import org.luaj.vm2.lib.jse.JsePlatformTest;
import org.luaj.vm2.lib.jse.LuaJavaCoercionTest;
import org.luaj.vm2.lib.jse.LuajavaAccessibleMembersTest;
import org.luaj.vm2.lib.jse.LuajavaClassMembersTest;
import org.luaj.vm2.lib.jse.OsLibTest;
import org.luaj.vm2.script.ScriptEngineTests;
public class AllTests {
public static Test suite() {
TestSuite suite = new TestSuite("All Tests for Luaj-vm2");
// vm tests
TestSuite vm = new TestSuite("VM Tests");
vm.addTestSuite(TypeTest.class);
vm.addTestSuite(UnaryBinaryOperatorsTest.class);
vm.addTestSuite(MetatableTest.class);
vm.addTestSuite(LuaOperationsTest.class);
vm.addTestSuite(StringTest.class);
vm.addTestSuite(OrphanedThreadTest.class);
vm.addTestSuite(VarargsTest.class);
vm.addTestSuite(LoadOrderTest.class);
suite.addTest(vm);
// table tests
TestSuite table = new TestSuite("Table Tests");
table.addTestSuite(TableTest.class);
table.addTestSuite(TableHashTest.class);
table.addTestSuite(WeakValueTableTest.class);
table.addTestSuite(WeakKeyTableTest.class);
table.addTestSuite(WeakKeyValueTableTest.class);
suite.addTest(table);
// bytecode compilers regression tests
TestSuite bytecodetests = FragmentsTest.suite();
suite.addTest(bytecodetests);
// I/O tests
TestSuite io = new TestSuite("I/O Tests");
io.addTestSuite(BufferedStreamTest.class);
io.addTestSuite(UTF8StreamTest.class);
suite.addTest(io);
// prototype compiler
TestSuite compiler = new TestSuite("Lua Compiler Tests");
compiler.addTestSuite(CompilerUnitTests.class);
compiler.addTestSuite(DumpLoadEndianIntTest.class);
compiler.addTestSuite(LuaParserTests.class);
compiler.addTestSuite(RegressionTests.class);
compiler.addTestSuite(SimpleTests.class);
suite.addTest(compiler);
// library tests
TestSuite lib = new TestSuite("Library Tests");
lib.addTestSuite(JsePlatformTest.class);
lib.addTestSuite(LuajavaAccessibleMembersTest.class);
lib.addTestSuite(LuajavaClassMembersTest.class);
lib.addTestSuite(LuaJavaCoercionTest.class);
lib.addTestSuite(RequireClassTest.class);
lib.addTestSuite(OsLibTest.class);
suite.addTest(lib);
// Script engine tests.
TestSuite script = ScriptEngineTests.suite();
suite.addTest(script);
// compatiblity tests
TestSuite compat = CompatibiltyTest.suite();
suite.addTest(compat);
compat.addTestSuite(ErrorsTest.class);
return suite;
}
}

View File

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

View File

@@ -0,0 +1,115 @@
/*******************************************************************************
* 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 junit.framework.TestSuite;
import org.luaj.vm2.luajc.LuaJC;
/**
* Compatibility tests for the Luaj VM
*
* Results are compared for exact match with
* the installed C-based lua environment.
*/
public class CompatibiltyTest extends TestSuite {
private static final String dir = "";
abstract protected static class CompatibiltyTestSuite extends ScriptDrivenTest {
LuaValue savedStringMetatable;
protected CompatibiltyTestSuite(PlatformType platform) {
super(platform,dir);
}
protected void setUp() throws Exception {
savedStringMetatable = LuaString.s_metatable;
super.setUp();
}
protected void tearDown() throws Exception {
super.tearDown();
LuaNil.s_metatable = null;
LuaBoolean.s_metatable = null;
LuaNumber.s_metatable = null;
LuaFunction.s_metatable = null;
LuaThread.s_metatable = null;
LuaString.s_metatable = savedStringMetatable;
}
public void testBaseLib() { runTest("baselib"); }
public void testCoroutineLib() { runTest("coroutinelib"); }
public void testDebugLib() { runTest("debuglib"); }
public void testErrors() { runTest("errors"); }
public void testFunctions() { runTest("functions"); }
public void testIoLib() { runTest("iolib"); }
public void testManyUpvals() { runTest("manyupvals"); }
public void testMathLib() { runTest("mathlib"); }
public void testMetatags() { runTest("metatags"); }
public void testOsLib() { runTest("oslib"); }
public void testStringLib() { runTest("stringlib"); }
public void testTableLib() { runTest("tablelib"); }
public void testTailcalls() { runTest("tailcalls"); }
public void testUpvalues() { runTest("upvalues"); }
public void testVm() { runTest("vm"); }
}
public static TestSuite suite() {
TestSuite suite = new TestSuite("Compatibility Tests");
suite.addTest( new TestSuite( JseCompatibilityTest.class, "JSE Compatibility Tests" ) );
suite.addTest( new TestSuite( JmeCompatibilityTest.class, "JME Compatibility Tests" ) );
suite.addTest( new TestSuite( LuaJCCompatibilityTest.class, "LuaJC Compatibility Tests" ) );
return suite;
}
public static class JmeCompatibilityTest extends CompatibiltyTestSuite {
public JmeCompatibilityTest() {
super(PlatformType.JME);
}
protected void setUp() throws Exception {
System.setProperty("JME", "true");
super.setUp();
}
}
public static class JseCompatibilityTest extends CompatibiltyTestSuite {
public JseCompatibilityTest() {
super(PlatformType.JSE);
}
protected void setUp() throws Exception {
super.setUp();
System.setProperty("JME", "false");
}
}
public static class LuaJCCompatibilityTest extends CompatibiltyTestSuite {
public LuaJCCompatibilityTest() {
super(PlatformType.LUAJIT);
}
protected void setUp() throws Exception {
super.setUp();
System.setProperty("JME", "false");
LuaJC.install(globals);
}
// not supported on this platform - don't test
public void testDebugLib() {}
}
}

View File

@@ -0,0 +1,63 @@
/*******************************************************************************
* 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.IOException;
import java.io.InputStream;
/**
* Test argument type check errors
*
* Results are compared for exact match with
* the installed C-based lua environment.
*/
public class ErrorsTest extends ScriptDrivenTest {
private static final String dir = "errors/";
public ErrorsTest() {
super(PlatformType.JSE, dir);
}
protected void setUp() throws Exception {
super.setUp();
}
public void testBaseLibArgs() {
globals.STDIN = new InputStream() {
public int read() throws IOException {
return -1;
}
};
runTest("baselibargs");
}
public void testCoroutineLibArgs() { runTest("coroutinelibargs"); }
public void testDebugLibArgs() { runTest("debuglibargs"); }
public void testIoLibArgs() { runTest("iolibargs"); }
public void testMathLibArgs() { runTest("mathlibargs"); }
public void testModuleLibArgs() { runTest("modulelibargs"); }
public void testOperators() { runTest("operators"); }
public void testStringLibArgs() { runTest("stringlibargs"); }
public void testTableLibArgs() { runTest("tablelibargs"); }
}

View File

@@ -0,0 +1,612 @@
/*******************************************************************************
* 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.Reader;
import java.io.StringReader;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.luaj.vm2.lib.jse.JsePlatform;
import org.luaj.vm2.luajc.LuaJC;
/**
* Test compilation of various fragments that have
* caused problems for jit compiling during development.
*
*/
public class FragmentsTest extends TestSuite {
static final int TEST_TYPE_LUAC = 0;
static final int TEST_TYPE_LUAJC = 1;
public static class JseFragmentsTest extends FragmentsTestCase {
public JseFragmentsTest() { super( TEST_TYPE_LUAC ); }
}
public static class LuaJCFragmentsTest extends FragmentsTestCase {
public LuaJCFragmentsTest() { super( TEST_TYPE_LUAJC ); }
}
public static TestSuite suite() {
TestSuite suite = new TestSuite("Compiler Fragments Tests");
suite.addTest( new TestSuite( JseFragmentsTest.class, "JSE Fragments Tests" ) );
suite.addTest( new TestSuite( LuaJCFragmentsTest.class, "LuaJC Fragments Tests" ) );
return suite;
}
abstract protected static class FragmentsTestCase extends TestCase {
final int TEST_TYPE;
protected FragmentsTestCase(int testType) {
this.TEST_TYPE = testType;
}
public void runFragment( Varargs expected, String script ) {
try {
String name = getName();
Globals globals = JsePlatform.debugGlobals();
Reader reader = new StringReader(script);
LuaValue chunk ;
switch ( TEST_TYPE ) {
case TEST_TYPE_LUAJC:
LuaJC.install(globals);
chunk = globals.load(reader, name);
break;
default:
Prototype p = globals.compilePrototype(reader, name);
chunk = new LuaClosure(p, globals);
Print.print(p);
break;
}
Varargs actual = chunk.invoke();
assertEquals( expected.narg(), actual.narg() );
for ( int i=1; i<=actual.narg(); i++ )
assertEquals( expected.arg(i), actual.arg(i) );
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
fail(e.toString());
}
}
public void testFirstArgNilExtended() {
runFragment( LuaValue.NIL,
"function f1(a) print( 'f1:', a ) return a end\n" +
"b = f1()\n" +
"return b" );
}
public void testSimpleForloop() {
runFragment( LuaValue.valueOf(77),
"for n,p in ipairs({77}) do\n"+
" print('n,p',n,p)\n"+
" return p\n"+
"end\n");
}
public void testForloopParamUpvalues() {
runFragment( LuaValue.varargsOf(new LuaValue[] {
LuaValue.valueOf(77),
LuaValue.valueOf(1) } ),
"for n,p in ipairs({77}) do\n"+
" print('n,p',n,p)\n"+
" foo = function()\n"+
" return p,n\n"+
" end\n"+
" return foo()\n"+
"end\n");
}
public void testArgVarargsUseBoth() {
runFragment( LuaValue.varargsOf( new LuaValue[] {
LuaValue.valueOf("a"),
LuaValue.valueOf("b"),
LuaValue.valueOf("c")}),
"function v(arg,...)\n" +
" return arg,...\n" +
"end\n" +
"return v('a','b','c')\n" );
}
public void testArgParamUseNone() {
runFragment( LuaValue.valueOf("string"),
"function v(arg,...)\n" +
" return type(arg)\n" +
"end\n" +
"return v('abc')\n" );
}
public void testSetlistVarargs() {
runFragment( LuaValue.valueOf("abc"),
"local f = function() return 'abc' end\n" +
"local g = { f() }\n" +
"return g[1]\n" );
}
public void testSelfOp() {
runFragment( LuaValue.valueOf("bcd"),
"local s = 'abcde'\n"+
"return s:sub(2,4)\n" );
}
public void testSetListWithOffsetAndVarargs() {
runFragment( LuaValue.valueOf(1003),
"local bar = {1000, math.sqrt(9)}\n"+
"return bar[1]+bar[2]\n" );
}
public void testMultiAssign() {
// arargs evaluations are all done before assignments
runFragment( LuaValue.varargsOf(new LuaValue[]{
LuaValue.valueOf(111),
LuaValue.valueOf(111),
LuaValue.valueOf(111)}),
"a,b,c = 1,10,100\n" +
"a,b,c = a+b+c, a+b+c, a+b+c\n" +
"return a,b,c\n" );
}
public void testUpvalues() {
runFragment( LuaValue.valueOf(999),
"local a = function(x)\n" +
" return function(y)\n" +
" return x + y\n" +
" end\n" +
"end\n" +
"local b = a(222)\n" +
"local c = b(777)\n" +
"print( 'c=', c )\n" +
"return c\n" );
}
public void testNonAsciiStringLiterals() {
runFragment( LuaValue.valueOf("7,8,12,10,9,11,133,222"),
"local a='\\a\\b\\f\\n\\t\\v\\133\\222'\n"+
"local t={string.byte(a,1,#a)}\n"+
"return table.concat(t,',')\n" );
}
public void testControlCharStringLiterals() {
runFragment( LuaValue.valueOf("97,0,98,18,99,18,100,18,48,101"),
"local a='a\\0b\\18c\\018d\\0180e'\n"+
"local t={string.byte(a,1,#a)}\n"+
"return table.concat(t,',')\n" );
}
public void testLoopVarNames() {
runFragment( LuaValue.valueOf(" 234,1,aa 234,2,bb"),
"local w = ''\n"+
"function t()\n"+
" for f,var in ipairs({'aa','bb'}) do\n"+
" local s = 234\n"+
" w = w..' '..s..','..f..','..var\n"+
" end\n"+
"end\n" +
"t()\n" +
"return w\n" );
}
public void testForLoops() {
runFragment( LuaValue.valueOf("12345 357 963"),
"local s,t,u = '','',''\n"+
"for m=1,5 do\n"+
" s = s..m\n"+
"end\n"+
"for m=3,7,2 do\n"+
" t = t..m\n"+
"end\n"+
"for m=9,3,-3 do\n"+
" u = u..m\n"+
"end\n"+
"return s..' '..t..' '..u\n" );
}
public void testLocalFunctionDeclarations() {
runFragment( LuaValue.varargsOf(LuaValue.valueOf("function"),LuaValue.valueOf("nil")),
"local function aaa()\n"+
" return type(aaa)\n"+
"end\n"+
"local bbb = function()\n"+
" return type(bbb)\n"+
"end\n"+
"return aaa(),bbb()\n" );
}
public void testNilsInTableConstructor() {
runFragment( LuaValue.valueOf("1=111 2=222 3=333 "),
"local t = { 111, 222, 333, nil, nil }\n"+
"local s = ''\n"+
"for i,v in ipairs(t) do \n" +
" s=s..tostring(i)..'='..tostring(v)..' '\n" +
"end\n"+
"return s\n" );
}
public void testUnreachableCode() {
runFragment( LuaValue.valueOf(66),
"local function foo(x) return x * 2 end\n" +
"local function bar(x, y)\n" +
" if x==y then\n" +
" return y\n" +
" else\n" +
" return foo(x)\n" +
" end\n" +
"end\n" +
"return bar(33,44)\n" );
}
public void testVarargsWithParameters() {
runFragment( LuaValue.valueOf(222),
"local func = function(t,...)\n"+
" return (...)\n"+
"end\n"+
"return func(111,222,333)\n" );
}
public void testNoReturnValuesPlainCall() {
runFragment( LuaValue.TRUE,
"local testtable = {}\n"+
"return pcall( function() testtable[1]=2 end )\n" );
}
public void testVarargsInTableConstructor() {
runFragment( LuaValue.valueOf(222),
"local function foo() return 111,222,333 end\n"+
"local t = {'a','b',c='c',foo()}\n"+
"return t[4]\n" );
}
public void testVarargsInFirstArg() {
runFragment( LuaValue.valueOf(123),
"function aaa(x) return x end\n" +
"function bbb(y) return y end\n" +
"function ccc(z) return z end\n" +
"return ccc( aaa(bbb(123)), aaa(456) )\n" );
}
public void testSetUpvalueTableInitializer() {
runFragment( LuaValue.valueOf("b"),
"local aliases = {a='b'}\n" +
"local foo = function()\n" +
" return aliases\n" +
"end\n" +
"return foo().a\n" );
}
public void testLoadNilUpvalue() {
runFragment( LuaValue.NIL,
"tostring = function() end\n" +
"local pc \n" +
"local pcall = function(...)\n" +
" pc(...)\n" +
"end\n" +
"return NIL\n" );
}
public void testUpvalueClosure() {
runFragment( LuaValue.NIL,
"print()\n"+
"local function f2() end\n"+
"local function f3()\n"+
" return f3\n"+
"end\n" +
"return NIL\n" );
}
public void testUninitializedUpvalue() {
runFragment( LuaValue.NIL,
"local f\n"+
"do\n"+
" function g()\n"+
" print(f())\n"+
" end\n"+
"end\n" +
"return NIL\n" );
}
public void testTestOpUpvalues() {
runFragment( LuaValue.varargsOf(LuaValue.valueOf(1),LuaValue.valueOf(2),LuaValue.valueOf(3)),
"print( nil and 'T' or 'F' )\n"+
"local a,b,c = 1,2,3\n"+
"function foo()\n"+
" return a,b,c\n"+
"end\n" +
"return foo()\n" );
}
public void testTestSimpleBinops() {
runFragment( LuaValue.varargsOf(new LuaValue[] {
LuaValue.FALSE, LuaValue.FALSE, LuaValue.TRUE, LuaValue.TRUE, LuaValue.FALSE }),
"local a,b,c = 2,-2.5,0\n" +
"return (a==c), (b==c), (a==a), (a>c), (b>0)\n" );
}
public void testNumericForUpvalues() {
runFragment( LuaValue.valueOf(8),
"for i = 3,4 do\n"+
" i = i + 5\n"+
" local a = function()\n"+
" return i\n"+
" end\n" +
" return a()\n"+
"end\n");
}
public void testNumericForUpvalues2() {
runFragment( LuaValue.valueOf("222 222"),
"local t = {}\n"+
"local template = [[123 456]]\n"+
"for i = 1,2 do\n"+
" t[i] = template:gsub('%d', function(s)\n"+
" return i\n"+
" end)\n"+
"end\n" +
"return t[2]\n");
}
public void testReturnUpvalue() {
runFragment( LuaValue.varargsOf(new LuaValue[] { LuaValue.ONE, LuaValue.valueOf(5), }),
"local a = 1\n"+
"local b\n"+
"function c()\n"+
" b=5\n" +
" return a\n"+
"end\n"+
"return c(),b\n" );
}
public void testUninitializedAroundBranch() {
runFragment( LuaValue.valueOf(333),
"local state\n"+
"if _G then\n"+
" state = 333\n"+
"end\n"+
"return state\n" );
}
public void testLoadedNilUpvalue() {
runFragment( LuaValue.NIL,
"local a = print()\n"+
"local b = c and { d = e }\n"+
"local f\n"+
"local function g()\n"+
" return f\n"+
"end\n" +
"return g()\n" );
}
public void testUpvalueInFirstSlot() {
runFragment( LuaValue.valueOf("foo"),
"local p = {'foo'}\n"+
"bar = function()\n"+
" return p \n"+
"end\n"+
"for i,key in ipairs(p) do\n"+
" print()\n"+
"end\n" +
"return bar()[1]");
}
public void testReadOnlyAndReadWriteUpvalues() {
runFragment( LuaValue.varargsOf( new LuaValue[] { LuaValue.valueOf(333), LuaValue.valueOf(222) } ),
"local a = 111\n" +
"local b = 222\n" +
"local c = function()\n"+
" a = a + b\n" +
" return a,b\n"+
"end\n" +
"return c()\n" );
}
public void testNestedUpvalues() {
runFragment( LuaValue.varargsOf( new LuaValue[] { LuaValue.valueOf(5), LuaValue.valueOf(8), LuaValue.valueOf(9) } ),
"local x = 3\n"+
"local y = 5\n"+
"local function f()\n"+
" return y\n"+
"end\n"+
"local function g(x1, y1)\n"+
" x = x1\n"+
" y = y1\n" +
" return x,y\n"+
"end\n"+
"return f(), g(8,9)\n"+
"\n" );
}
public void testLoadBool() {
runFragment( LuaValue.NONE,
"print( type(foo)=='string' )\n"+
"local a,b\n"+
"if print() then\n"+
" b = function()\n"+
" return a\n"+
" end\n"+
"end\n" );
}
public void testBasicForLoop() {
runFragment( LuaValue.valueOf(2),
"local data\n"+
"for i = 1, 2 do\n"+
" data = i\n"+
"end\n"+
"local bar = function()\n"+
" return data\n"+
"end\n" +
"return bar()\n" );
}
public void testGenericForMultipleValues() {
runFragment( LuaValue.varargsOf(LuaValue.valueOf(3),LuaValue.valueOf(2),LuaValue.valueOf(1)),
"local iter = function() return 1,2,3,4 end\n" +
"local foo = function() return iter,5 end\n" +
"for a,b,c in foo() do\n" +
" return c,b,a\n" +
"end\n" );
}
public void testPhiUpvalue() {
runFragment( LuaValue.valueOf(6),
"local a = foo or 0\n"+
"local function b(c)\n"+
" if c > a then a = c end\n" +
" return a\n"+
"end\n" +
"b(6)\n" +
"return a\n" );
}
public void testAssignReferUpvalues() {
runFragment( LuaValue.valueOf(123),
"local entity = 234\n" +
"local function c()\n" +
" return entity\n" +
"end\n" +
"entity = (a == b) and 123\n" +
"if entity then\n" +
" return entity\n" +
"end\n" );
}
public void testSimpleRepeatUntil() {
runFragment( LuaValue.valueOf(5),
"local a\n"+
"local w\n"+
"repeat\n"+
" a = w\n"+
"until not a\n" +
"return 5\n" );
}
public void testLoopVarUpvalues() {
runFragment( LuaValue.valueOf("b"),
"local env = {}\n" +
"for a,b in pairs(_G) do\n" +
" c = function()\n" +
" return b\n" +
" end\n" +
"end\n" +
"local e = env\n" +
"local f = {a='b'}\n" +
"for k,v in pairs(f) do\n" +
" return env[k] or v\n" +
"end\n");
}
public void testPhiVarUpvalue() {
runFragment( LuaValue.valueOf(2),
"local a = 1\n"+
"local function b()\n"+
" a = a + 1\n"+
" return function() end\n"+
"end\n"+
"for i in b() do\n"+
" a = 3\n"+
"end\n" +
"return a\n");
}
public void testUpvaluesInElseClauses() {
runFragment( LuaValue.valueOf(111),
"if a then\n" +
" foo(bar)\n" +
"elseif _G then\n" +
" local x = 111\n" +
" if d then\n" +
" foo(bar)\n" +
" else\n" +
" local y = function()\n" +
" return x\n" +
" end\n" +
" return y()\n" +
" end\n" +
"end\n");
}
public void testUpvalueInDoBlock() {
runFragment( LuaValue.NONE, "do\n"+
" local x = 10\n"+
" function g()\n"+
" return x\n"+
" end\n"+
"end\n"+
"g()\n");
}
public void testNullError() {
runFragment( LuaValue.varargsOf(LuaValue.FALSE, LuaValue.NIL),
"return pcall(error)\n");
}
public void testFindWithOffset() {
runFragment(LuaValue.varargsOf(LuaValue.valueOf(8), LuaValue.valueOf(5)),
"string = \"abcdef:ghi\"\n" +
"substring = string:sub(3)\n" +
"idx = substring:find(\":\")\n" +
"return #substring, idx\n");
}
public void testErrorArgIsString() {
runFragment(LuaValue.varargsOf(LuaValue.valueOf("string"), LuaValue.valueOf("c")),
"a,b = pcall(error, 'c'); return type(b), b\n");
}
public void testErrorArgIsNil() {
runFragment(LuaValue.varargsOf(LuaValue.valueOf("nil"), LuaValue.NIL),
"a,b = pcall(error); return type(b), b\n");
}
public void testErrorArgIsTable() {
runFragment(LuaValue.varargsOf(LuaValue.valueOf("table"), LuaValue.valueOf("d")),
"a,b = pcall(error, {c='d'}); return type(b), b.c\n");
}
public void testErrorArgIsNumber() {
runFragment(LuaValue.varargsOf(LuaValue.valueOf("string"), LuaValue.valueOf("1")),
"a,b = pcall(error, 1); return type(b), b\n");
}
public void testErrorArgIsBool() {
runFragment(LuaValue.varargsOf(LuaValue.valueOf("boolean"), LuaValue.TRUE),
"a,b = pcall(error, true); return type(b), b\n");
}
public void testBalancedMatchOnEmptyString() {
runFragment(LuaValue.NIL, "return (\"\"):match(\"%b''\")\n");
}
public void testReturnValueForTableRemove() {
runFragment(LuaValue.NONE, "return table.remove({ })");
}
public void testTypeOfTableRemoveReturnValue() {
runFragment(LuaValue.valueOf("nil"), "local k = table.remove({ }) return type(k)");
}
public void testVarargBugReport() {
runFragment(LuaValue.varargsOf(new LuaValue[] {
LuaValue.valueOf(1), LuaValue.valueOf(2), LuaValue.valueOf(3) }),
"local i = function(...) return ... end\n"
+ "local v1, v2, v3 = i(1, 2, 3)\n"
+ "return v1, v2, v3");
}
}
}

View File

@@ -0,0 +1,70 @@
/*******************************************************************************
* 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;
import java.io.InputStream;
import java.io.Reader;
import junit.framework.TestCase;
import org.luaj.vm2.lib.jse.JsePlatform;
import org.luaj.vm2.server.Launcher;
import org.luaj.vm2.server.LuajClassLoader;
// Tests using class loading orders that have caused problems for some use cases.
public class LoadOrderTest extends TestCase {
public void testLoadGlobalsFirst() {
Globals g = JsePlatform.standardGlobals();
assertNotNull(g);
}
public void testLoadStringFirst() {
LuaString BAR = LuaString.valueOf("bar");
assertNotNull(BAR);
}
public static class TestLauncherLoadStringFirst implements Launcher {
// Static initializer that causes LuaString->LuaValue->LuaString
private static final LuaString FOO = LuaString.valueOf("foo");
public Object[] launch(String script, Object[] arg) {
return new Object[] { FOO };
}
public Object[] launch(InputStream script, Object[] arg) {
return null;
}
public Object[] launch(Reader script, Object[] arg) {
return null;
}
}
public void testClassLoadsStringFirst() throws Exception {
Launcher launcher = LuajClassLoader
.NewLauncher(TestLauncherLoadStringFirst.class);
Object[] results = launcher.launch("foo", null);
assertNotNull(results);
}
}

View File

@@ -0,0 +1,176 @@
/*******************************************************************************
* 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.Reader;
import java.io.StringReader;
import java.lang.reflect.InvocationTargetException;
import junit.framework.TestCase;
import org.luaj.vm2.TypeTest.MyData;
import org.luaj.vm2.compiler.LuaC;
import org.luaj.vm2.lib.ZeroArgFunction;
public class LuaOperationsTest extends TestCase {
private final int sampleint = 77;
private final long samplelong = 123400000000L;
private final double sampledouble = 55.25;
private final String samplestringstring = "abcdef";
private final String samplestringint = String.valueOf(sampleint);
private final String samplestringlong = String.valueOf(samplelong);
private final String samplestringdouble = String.valueOf(sampledouble);
private final Object sampleobject = new Object();
private final MyData sampledata = new MyData();
private final LuaValue somenil = LuaValue.NIL;
private final LuaValue sometrue = LuaValue.TRUE;
private final LuaValue somefalse = LuaValue.FALSE;
private final LuaValue zero = LuaValue.ZERO;
private final LuaValue intint = LuaValue.valueOf(sampleint);
private final LuaValue longdouble = LuaValue.valueOf(samplelong);
private final LuaValue doubledouble = LuaValue.valueOf(sampledouble);
private final LuaValue stringstring = LuaValue.valueOf(samplestringstring);
private final LuaValue stringint = LuaValue.valueOf(samplestringint);
private final LuaValue stringlong = LuaValue.valueOf(samplestringlong);
private final LuaValue stringdouble = LuaValue.valueOf(samplestringdouble);
private final LuaTable table = LuaValue.listOf( new LuaValue[] { LuaValue.valueOf("aaa"), LuaValue.valueOf("bbb") } );
private final LuaValue somefunc = new ZeroArgFunction() { public LuaValue call() { return NONE;}};
private final LuaThread thread = new LuaThread(new Globals(), somefunc);
private final Prototype proto = new Prototype(1);
private final LuaClosure someclosure = new LuaClosure(proto,table);
private final LuaUserdata userdataobj = LuaValue.userdataOf(sampleobject);
private final LuaUserdata userdatacls = LuaValue.userdataOf(sampledata);
private void throwsLuaError(String methodName, Object obj) {
try {
LuaValue.class.getMethod(methodName).invoke(obj);
fail("failed to throw LuaError as required");
} catch (InvocationTargetException e) {
if ( ! (e.getTargetException() instanceof LuaError) )
fail("not a LuaError: "+e.getTargetException());
return; // pass
} catch ( Exception e ) {
fail( "bad exception: "+e );
}
}
private void throwsLuaError(String methodName, Object obj, Object arg) {
try {
LuaValue.class.getMethod(methodName,LuaValue.class).invoke(obj,arg);
fail("failed to throw LuaError as required");
} catch (InvocationTargetException e) {
if ( ! (e.getTargetException() instanceof LuaError) )
fail("not a LuaError: "+e.getTargetException());
return; // pass
} catch ( Exception e ) {
fail( "bad exception: "+e );
}
}
public void testLen() {
throwsLuaError( "len", somenil );
throwsLuaError( "len", sometrue );
throwsLuaError( "len", somefalse );
throwsLuaError( "len", zero );
throwsLuaError( "len", intint );
throwsLuaError( "len", longdouble );
throwsLuaError( "len", doubledouble );
assertEquals( LuaInteger.valueOf(samplestringstring.length()), stringstring.len() );
assertEquals( LuaInteger.valueOf(samplestringint.length()), stringint.len() );
assertEquals( LuaInteger.valueOf(samplestringlong.length()), stringlong.len() );
assertEquals( LuaInteger.valueOf(samplestringdouble.length()), stringdouble.len() );
assertEquals( LuaInteger.valueOf(2), table.len() );
throwsLuaError( "len", somefunc );
throwsLuaError( "len", thread );
throwsLuaError( "len", someclosure );
throwsLuaError( "len", userdataobj );
throwsLuaError( "len", userdatacls );
}
public void testLength() {
throwsLuaError( "length", somenil );
throwsLuaError( "length", sometrue );
throwsLuaError( "length", somefalse );
throwsLuaError( "length", zero );
throwsLuaError( "length", intint );
throwsLuaError( "length", longdouble );
throwsLuaError( "length", doubledouble );
assertEquals( samplestringstring.length(), stringstring.length() );
assertEquals( samplestringint.length(), stringint.length() );
assertEquals( samplestringlong.length(), stringlong.length() );
assertEquals( samplestringdouble.length(), stringdouble.length() );
assertEquals( 2, table.length() );
throwsLuaError( "length", somefunc );
throwsLuaError( "length", thread );
throwsLuaError( "length", someclosure );
throwsLuaError( "length", userdataobj );
throwsLuaError( "length", userdatacls );
}
public Prototype createPrototype( String script, String name ) {
try {
Globals globals = org.luaj.vm2.lib.jse.JsePlatform.standardGlobals();
Reader reader = new StringReader(script);
return globals.compilePrototype(reader, name);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
fail(e.toString());
return null;
}
}
public void testFunctionClosureThreadEnv() {
// set up suitable environments for execution
LuaValue aaa = LuaValue.valueOf("aaa");
LuaValue eee = LuaValue.valueOf("eee");
final Globals globals = org.luaj.vm2.lib.jse.JsePlatform.standardGlobals();
LuaTable newenv = LuaValue.tableOf( new LuaValue[] {
LuaValue.valueOf("a"), LuaValue.valueOf("aaa"),
LuaValue.valueOf("b"), LuaValue.valueOf("bbb"), } );
LuaTable mt = LuaValue.tableOf( new LuaValue[] { LuaValue.INDEX, globals } );
newenv.setmetatable(mt);
globals.set("a", aaa);
newenv.set("a", eee);
// function tests
{
LuaFunction f = new ZeroArgFunction() { public LuaValue call() { return globals.get("a");}};
assertEquals( aaa, f.call() );
}
// closure tests
{
Prototype p = createPrototype( "return a\n", "closuretester" );
LuaClosure c = new LuaClosure(p, globals);
// Test that a clusure with a custom enviroment uses that environment.
assertEquals( aaa, c.call() );
c = new LuaClosure(p, newenv);
assertEquals( newenv, c.upValues[0].getValue() );
assertEquals( eee, c.call() );
}
}
}

View File

@@ -0,0 +1,232 @@
package org.luaj.vm2;
import junit.framework.TestCase;
import org.luaj.vm2.lib.jme.JmePlatform;
import org.luaj.vm2.lib.jse.JsePlatform;
public class MathLibTest extends TestCase {
private LuaValue j2se;
private LuaValue j2me;
private boolean supportedOnJ2me;
public MathLibTest() {
j2se = JsePlatform.standardGlobals().get("math");
j2me = JmePlatform.standardGlobals().get("math");
}
protected void setUp() throws Exception {
supportedOnJ2me = true;
}
public void testMathDPow() {
assertEquals( 1, j2mepow(2, 0), 0 );
assertEquals( 2, j2mepow(2, 1), 0 );
assertEquals( 8, j2mepow(2, 3), 0 );
assertEquals( -8, j2mepow(-2, 3), 0 );
assertEquals( 1/8., j2mepow(2, -3), 0 );
assertEquals( -1/8., j2mepow(-2, -3), 0 );
assertEquals( 16, j2mepow(256, .5), 0 );
assertEquals( 4, j2mepow(256, .25), 0 );
assertEquals( 64, j2mepow(256, .75), 0 );
assertEquals( 1./16, j2mepow(256, - .5), 0 );
assertEquals( 1./ 4, j2mepow(256, -.25), 0 );
assertEquals( 1./64, j2mepow(256, -.75), 0 );
assertEquals( Double.NaN, j2mepow(-256, .5), 0 );
assertEquals( 1, j2mepow(.5, 0), 0 );
assertEquals( .5, j2mepow(.5, 1), 0 );
assertEquals(.125, j2mepow(.5, 3), 0 );
assertEquals( 2, j2mepow(.5, -1), 0 );
assertEquals( 8, j2mepow(.5, -3), 0 );
assertEquals(1, j2mepow(0.0625, 0), 0 );
assertEquals(0.00048828125, j2mepow(0.0625, 2.75), 0 );
}
private double j2mepow(double x, double y) {
return j2me.get("pow").call(LuaValue.valueOf(x),LuaValue.valueOf(y)).todouble();
}
public void testAbs() {
tryMathOp( "abs", 23.45 );
tryMathOp( "abs", -23.45 );
}
public void testCos() {
tryTrigOps( "cos" );
}
public void testCosh() {
supportedOnJ2me = false;
tryTrigOps( "cosh" );
}
public void testDeg() {
tryTrigOps( "deg" );
}
public void testExp() {
//supportedOnJ2me = false;
tryMathOp( "exp", 0 );
tryMathOp( "exp", 0.1 );
tryMathOp( "exp", .9 );
tryMathOp( "exp", 1. );
tryMathOp( "exp", 9 );
tryMathOp( "exp", -.1 );
tryMathOp( "exp", -.9 );
tryMathOp( "exp", -1. );
tryMathOp( "exp", -9 );
}
public void testLog() {
supportedOnJ2me = false;
tryMathOp( "log", 0.1 );
tryMathOp( "log", .9 );
tryMathOp( "log", 1. );
tryMathOp( "log", 9 );
tryMathOp( "log", -.1 );
tryMathOp( "log", -.9 );
tryMathOp( "log", -1. );
tryMathOp( "log", -9 );
}
public void testRad() {
tryMathOp( "rad", 0 );
tryMathOp( "rad", 0.1 );
tryMathOp( "rad", .9 );
tryMathOp( "rad", 1. );
tryMathOp( "rad", 9 );
tryMathOp( "rad", 10 );
tryMathOp( "rad", 100 );
tryMathOp( "rad", -.1 );
tryMathOp( "rad", -.9 );
tryMathOp( "rad", -1. );
tryMathOp( "rad", -9 );
tryMathOp( "rad", -10 );
tryMathOp( "rad", -100 );
}
public void testSin() {
tryTrigOps( "sin" );
}
public void testSinh() {
supportedOnJ2me = false;
tryTrigOps( "sinh" );
}
public void testSqrt() {
tryMathOp( "sqrt", 0 );
tryMathOp( "sqrt", 0.1 );
tryMathOp( "sqrt", .9 );
tryMathOp( "sqrt", 1. );
tryMathOp( "sqrt", 9 );
tryMathOp( "sqrt", 10 );
tryMathOp( "sqrt", 100 );
}
public void testTan() {
tryTrigOps( "tan" );
}
public void testTanh() {
supportedOnJ2me = false;
tryTrigOps( "tanh" );
}
public void testAtan2() {
supportedOnJ2me = false;
tryDoubleOps( "atan2", false );
}
public void testFmod() {
tryDoubleOps( "fmod", false );
}
public void testPow() {
tryDoubleOps( "pow", true );
}
private void tryDoubleOps( String op, boolean positiveOnly ) {
// y>0, x>0
tryMathOp( op, 0.1, 4.0 );
tryMathOp( op, .9, 4.0 );
tryMathOp( op, 1., 4.0 );
tryMathOp( op, 9, 4.0 );
tryMathOp( op, 10, 4.0 );
tryMathOp( op, 100, 4.0 );
// y>0, x<0
tryMathOp( op, 0.1, -4.0 );
tryMathOp( op, .9, -4.0 );
tryMathOp( op, 1., -4.0 );
tryMathOp( op, 9, -4.0 );
tryMathOp( op, 10, -4.0 );
tryMathOp( op, 100, -4.0 );
if ( ! positiveOnly ) {
// y<0, x>0
tryMathOp( op, -0.1, 4.0 );
tryMathOp( op, -.9, 4.0 );
tryMathOp( op, -1., 4.0 );
tryMathOp( op, -9, 4.0 );
tryMathOp( op, -10, 4.0 );
tryMathOp( op, -100, 4.0 );
// y<0, x<0
tryMathOp( op, -0.1, -4.0 );
tryMathOp( op, -.9, -4.0 );
tryMathOp( op, -1., -4.0 );
tryMathOp( op, -9, -4.0 );
tryMathOp( op, -10, -4.0 );
tryMathOp( op, -100, -4.0 );
}
// degenerate cases
tryMathOp( op, 0, 1 );
tryMathOp( op, 1, 0 );
tryMathOp( op, -1, 0 );
tryMathOp( op, 0, -1 );
tryMathOp( op, 0, 0 );
}
private void tryTrigOps(String op) {
tryMathOp( op, 0 );
tryMathOp( op, Math.PI/8 );
tryMathOp( op, Math.PI*7/8 );
tryMathOp( op, Math.PI*8/8 );
tryMathOp( op, Math.PI*9/8 );
tryMathOp( op, -Math.PI/8 );
tryMathOp( op, -Math.PI*7/8 );
tryMathOp( op, -Math.PI*8/8 );
tryMathOp( op, -Math.PI*9/8 );
}
private void tryMathOp(String op, double x) {
try {
double expected = j2se.get(op).call( LuaValue.valueOf(x)).todouble();
double actual = j2me.get(op).call( LuaValue.valueOf(x)).todouble();
if ( supportedOnJ2me )
assertEquals( expected, actual, 1.e-4 );
else
fail("j2me should throw exception for math."+op+" but returned "+actual);
} catch ( LuaError lee ) {
if ( supportedOnJ2me )
throw lee;
}
}
private void tryMathOp(String op, double a, double b) {
try {
double expected = j2se.get(op).call( LuaValue.valueOf(a), LuaValue.valueOf(b)).todouble();
double actual = j2me.get(op).call( LuaValue.valueOf(a), LuaValue.valueOf(b)).todouble();
if ( supportedOnJ2me )
assertEquals( expected, actual, 1.e-5 );
else
fail("j2me should throw exception for math."+op+" but returned "+actual);
} catch ( LuaError lee ) {
if ( supportedOnJ2me )
throw lee;
}
}
}

View File

@@ -0,0 +1,366 @@
/*******************************************************************************
* 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 junit.framework.TestCase;
import org.luaj.vm2.TypeTest.MyData;
import org.luaj.vm2.lib.StringLib;
import org.luaj.vm2.lib.ThreeArgFunction;
import org.luaj.vm2.lib.TwoArgFunction;
import org.luaj.vm2.lib.ZeroArgFunction;
public class MetatableTest extends TestCase {
private final String samplestring = "abcdef";
private final Object sampleobject = new Object();
private final MyData sampledata = new MyData();
private final LuaValue string = LuaValue.valueOf(samplestring);
private final LuaTable table = LuaValue.tableOf();
private final LuaFunction function = new ZeroArgFunction() { public LuaValue call() { return NONE;}};
private final LuaThread thread = new LuaThread(new Globals(), function);
private final LuaClosure closure = new LuaClosure(new Prototype(), new LuaTable());
private final LuaUserdata userdata = LuaValue.userdataOf(sampleobject);
private final LuaUserdata userdatamt = LuaValue.userdataOf(sampledata,table);
protected void setUp() throws Exception {
// needed for metatable ops to work on strings
new StringLib();
}
protected void tearDown() throws Exception {
super.tearDown();
LuaBoolean.s_metatable = null;
LuaFunction.s_metatable = null;
LuaNil.s_metatable = null;
LuaNumber.s_metatable = null;
// LuaString.s_metatable = null;
LuaThread.s_metatable = null;
}
public void testGetMetatable() {
assertEquals( null, LuaValue.NIL.getmetatable() );
assertEquals( null, LuaValue.TRUE.getmetatable() );
assertEquals( null, LuaValue.ONE.getmetatable() );
// assertEquals( null, string.getmetatable() );
assertEquals( null, table.getmetatable() );
assertEquals( null, function.getmetatable() );
assertEquals( null, thread.getmetatable() );
assertEquals( null, closure.getmetatable() );
assertEquals( null, userdata.getmetatable() );
assertEquals( table, userdatamt.getmetatable() );
}
public void testSetMetatable() {
LuaValue mt = LuaValue.tableOf();
assertEquals( null, table.getmetatable() );
assertEquals( null, userdata.getmetatable() );
assertEquals( table, userdatamt.getmetatable() );
assertEquals( table, table.setmetatable(mt) );
assertEquals( userdata, userdata.setmetatable(mt) );
assertEquals( userdatamt, userdatamt.setmetatable(mt) );
assertEquals( mt, table.getmetatable() );
assertEquals( mt, userdata.getmetatable() );
assertEquals( mt, userdatamt.getmetatable() );
// these all get metatable behind-the-scenes
assertEquals( null, LuaValue.NIL.getmetatable() );
assertEquals( null, LuaValue.TRUE.getmetatable() );
assertEquals( null, LuaValue.ONE.getmetatable() );
// assertEquals( null, string.getmetatable() );
assertEquals( null, function.getmetatable() );
assertEquals( null, thread.getmetatable() );
assertEquals( null, closure.getmetatable() );
LuaNil.s_metatable = mt;
assertEquals( mt, LuaValue.NIL.getmetatable() );
assertEquals( null, LuaValue.TRUE.getmetatable() );
assertEquals( null, LuaValue.ONE.getmetatable() );
// assertEquals( null, string.getmetatable() );
assertEquals( null, function.getmetatable() );
assertEquals( null, thread.getmetatable() );
assertEquals( null, closure.getmetatable() );
LuaBoolean.s_metatable = mt;
assertEquals( mt, LuaValue.TRUE.getmetatable() );
assertEquals( null, LuaValue.ONE.getmetatable() );
// assertEquals( null, string.getmetatable() );
assertEquals( null, function.getmetatable() );
assertEquals( null, thread.getmetatable() );
assertEquals( null, closure.getmetatable() );
LuaNumber.s_metatable = mt;
assertEquals( mt, LuaValue.ONE.getmetatable() );
assertEquals( mt, LuaValue.valueOf(1.25).getmetatable() );
// assertEquals( null, string.getmetatable() );
assertEquals( null, function.getmetatable() );
assertEquals( null, thread.getmetatable() );
assertEquals( null, closure.getmetatable() );
// LuaString.s_metatable = mt;
// assertEquals( mt, string.getmetatable() );
assertEquals( null, function.getmetatable() );
assertEquals( null, thread.getmetatable() );
assertEquals( null, closure.getmetatable() );
LuaFunction.s_metatable = mt;
assertEquals( mt, function.getmetatable() );
assertEquals( null, thread.getmetatable() );
LuaThread.s_metatable = mt;
assertEquals( mt, thread.getmetatable() );
}
public void testMetatableIndex() {
assertEquals( table, table.setmetatable(null) );
assertEquals( userdata, userdata.setmetatable(null) );
assertEquals( userdatamt, userdatamt.setmetatable(null) );
assertEquals( LuaValue.NIL, table.get(1) );
assertEquals( LuaValue.NIL, userdata.get(1) );
assertEquals( LuaValue.NIL, userdatamt.get(1) );
// empty metatable
LuaValue mt = LuaValue.tableOf();
assertEquals( table, table.setmetatable(mt) );
assertEquals( userdata, userdata.setmetatable(mt) );
LuaBoolean.s_metatable = mt;
LuaFunction.s_metatable = mt;
LuaNil.s_metatable = mt;
LuaNumber.s_metatable = mt;
// LuaString.s_metatable = mt;
LuaThread.s_metatable = mt;
assertEquals( mt, table.getmetatable() );
assertEquals( mt, userdata.getmetatable() );
assertEquals( mt, LuaValue.NIL.getmetatable() );
assertEquals( mt, LuaValue.TRUE.getmetatable() );
assertEquals( mt, LuaValue.ONE.getmetatable() );
// assertEquals( StringLib.instance, string.getmetatable() );
assertEquals( mt, function.getmetatable() );
assertEquals( mt, thread.getmetatable() );
// plain metatable
LuaValue abc = LuaValue.valueOf("abc");
mt.set( LuaValue.INDEX, LuaValue.listOf(new LuaValue[] { abc } ) );
assertEquals( abc, table.get(1) );
assertEquals( abc, userdata.get(1) );
assertEquals( abc, LuaValue.NIL.get(1) );
assertEquals( abc, LuaValue.TRUE.get(1) );
assertEquals( abc, LuaValue.ONE.get(1) );
// assertEquals( abc, string.get(1) );
assertEquals( abc, function.get(1) );
assertEquals( abc, thread.get(1) );
// plain metatable
mt.set( LuaValue.INDEX, new TwoArgFunction() {
public LuaValue call(LuaValue arg1, LuaValue arg2) {
return LuaValue.valueOf( arg1.typename()+"["+arg2.tojstring()+"]=xyz" );
}
});
assertEquals( "table[1]=xyz", table.get(1).tojstring() );
assertEquals( "userdata[1]=xyz", userdata.get(1).tojstring() );
assertEquals( "nil[1]=xyz", LuaValue.NIL.get(1).tojstring() );
assertEquals( "boolean[1]=xyz", LuaValue.TRUE.get(1).tojstring() );
assertEquals( "number[1]=xyz", LuaValue.ONE.get(1).tojstring() );
// assertEquals( "string[1]=xyz", string.get(1).tojstring() );
assertEquals( "function[1]=xyz", function.get(1).tojstring() );
assertEquals( "thread[1]=xyz", thread.get(1).tojstring() );
}
public void testMetatableNewIndex() {
// empty metatable
LuaValue mt = LuaValue.tableOf();
assertEquals( table, table.setmetatable(mt) );
assertEquals( userdata, userdata.setmetatable(mt) );
LuaBoolean.s_metatable = mt;
LuaFunction.s_metatable = mt;
LuaNil.s_metatable = mt;
LuaNumber.s_metatable = mt;
// LuaString.s_metatable = mt;
LuaThread.s_metatable = mt;
// plain metatable
final LuaValue fallback = LuaValue.tableOf();
LuaValue abc = LuaValue.valueOf("abc");
mt.set( LuaValue.NEWINDEX, fallback );
table.set(2,abc);
userdata.set(3,abc);
LuaValue.NIL.set(4,abc);
LuaValue.TRUE.set(5,abc);
LuaValue.ONE.set(6,abc);
// string.set(7,abc);
function.set(8,abc);
thread.set(9,abc);
assertEquals( abc, fallback.get(2) );
assertEquals( abc, fallback.get(3) );
assertEquals( abc, fallback.get(4) );
assertEquals( abc, fallback.get(5) );
assertEquals( abc, fallback.get(6) );
// assertEquals( abc, StringLib.instance.get(7) );
assertEquals( abc, fallback.get(8) );
assertEquals( abc, fallback.get(9) );
// metatable with function call
mt.set( LuaValue.NEWINDEX, new ThreeArgFunction() {
public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
fallback.rawset(arg2, LuaValue.valueOf( "via-func-"+arg3 ));
return NONE;
}
});
table.set(12,abc);
userdata.set(13,abc);
LuaValue.NIL.set(14,abc);
LuaValue.TRUE.set(15,abc);
LuaValue.ONE.set(16,abc);
// string.set(17,abc);
function.set(18,abc);
thread.set(19,abc);
LuaValue via = LuaValue.valueOf( "via-func-abc" );
assertEquals( via, fallback.get(12) );
assertEquals( via, fallback.get(13) );
assertEquals( via, fallback.get(14) );
assertEquals( via, fallback.get(15) );
assertEquals( via, fallback.get(16) );
// assertEquals( via, StringLib.instance.get(17) );
assertEquals( via, fallback.get(18) );
assertEquals( via, fallback.get(19) );
}
private void checkTable( LuaValue t,
LuaValue aa, LuaValue bb, LuaValue cc, LuaValue dd, LuaValue ee, LuaValue ff, LuaValue gg,
LuaValue ra, LuaValue rb, LuaValue rc, LuaValue rd, LuaValue re, LuaValue rf, LuaValue rg ) {
assertEquals( aa, t.get("aa") );
assertEquals( bb, t.get("bb") );
assertEquals( cc, t.get("cc") );
assertEquals( dd, t.get("dd") );
assertEquals( ee, t.get("ee") );
assertEquals( ff, t.get("ff") );
assertEquals( gg, t.get("gg") );
assertEquals( ra, t.rawget("aa") );
assertEquals( rb, t.rawget("bb") );
assertEquals( rc, t.rawget("cc") );
assertEquals( rd, t.rawget("dd") );
assertEquals( re, t.rawget("ee") );
assertEquals( rf, t.rawget("ff") );
assertEquals( rg, t.rawget("gg") );
}
private LuaValue makeTable( String key1, String val1, String key2, String val2 ) {
return LuaValue.tableOf( new LuaValue[] {
LuaValue.valueOf(key1), LuaValue.valueOf(val1),
LuaValue.valueOf(key2), LuaValue.valueOf(val2),
} );
}
public void testRawsetMetatableSet() {
// set up tables
LuaValue m = makeTable( "aa", "aaa", "bb", "bbb" );
m.set(LuaValue.INDEX, m);
m.set(LuaValue.NEWINDEX, m);
LuaValue s = makeTable( "cc", "ccc", "dd", "ddd" );
LuaValue t = makeTable( "cc", "ccc", "dd", "ddd" );
t.setmetatable(m);
LuaValue aaa = LuaValue.valueOf("aaa");
LuaValue bbb = LuaValue.valueOf("bbb");
LuaValue ccc = LuaValue.valueOf("ccc");
LuaValue ddd = LuaValue.valueOf("ddd");
LuaValue ppp = LuaValue.valueOf("ppp");
LuaValue qqq = LuaValue.valueOf("qqq");
LuaValue rrr = LuaValue.valueOf("rrr");
LuaValue sss = LuaValue.valueOf("sss");
LuaValue ttt = LuaValue.valueOf("ttt");
LuaValue www = LuaValue.valueOf("www");
LuaValue xxx = LuaValue.valueOf("xxx");
LuaValue yyy = LuaValue.valueOf("yyy");
LuaValue zzz = LuaValue.valueOf("zzz");
LuaValue nil = LuaValue.NIL;
// check initial values
// values via "bet()" values via "rawget()"
checkTable( s, nil,nil,ccc,ddd,nil,nil,nil, nil,nil,ccc,ddd,nil,nil,nil );
checkTable( t, aaa,bbb,ccc,ddd,nil,nil,nil, nil,nil,ccc,ddd,nil,nil,nil );
checkTable( m, aaa,bbb,nil,nil,nil,nil,nil, aaa,bbb,nil,nil,nil,nil,nil );
// rawset()
s.rawset("aa", www);
checkTable( s, www,nil,ccc,ddd,nil,nil,nil, www,nil,ccc,ddd,nil,nil,nil );
checkTable( t, aaa,bbb,ccc,ddd,nil,nil,nil, nil,nil,ccc,ddd,nil,nil,nil );
checkTable( m, aaa,bbb,nil,nil,nil,nil,nil, aaa,bbb,nil,nil,nil,nil,nil );
s.rawset("cc", xxx);
checkTable( s, www,nil,xxx,ddd,nil,nil,nil, www,nil,xxx,ddd,nil,nil,nil );
checkTable( t, aaa,bbb,ccc,ddd,nil,nil,nil, nil,nil,ccc,ddd,nil,nil,nil );
checkTable( m, aaa,bbb,nil,nil,nil,nil,nil, aaa,bbb,nil,nil,nil,nil,nil );
t.rawset("bb", yyy);
checkTable( s, www,nil,xxx,ddd,nil,nil,nil, www,nil,xxx,ddd,nil,nil,nil );
checkTable( t, aaa,yyy,ccc,ddd,nil,nil,nil, nil,yyy,ccc,ddd,nil,nil,nil );
checkTable( m, aaa,bbb,nil,nil,nil,nil,nil, aaa,bbb,nil,nil,nil,nil,nil );
t.rawset("dd", zzz);
checkTable( s, www,nil,xxx,ddd,nil,nil,nil, www,nil,xxx,ddd,nil,nil,nil );
checkTable( t, aaa,yyy,ccc,zzz,nil,nil,nil, nil,yyy,ccc,zzz,nil,nil,nil );
checkTable( m, aaa,bbb,nil,nil,nil,nil,nil, aaa,bbb,nil,nil,nil,nil,nil );
// set() invoking metatables
s.set("ee", ppp);
checkTable( s, www,nil,xxx,ddd,ppp,nil,nil, www,nil,xxx,ddd,ppp,nil,nil );
checkTable( t, aaa,yyy,ccc,zzz,nil,nil,nil, nil,yyy,ccc,zzz,nil,nil,nil );
checkTable( m, aaa,bbb,nil,nil,nil,nil,nil, aaa,bbb,nil,nil,nil,nil,nil );
s.set("cc", qqq);
checkTable( s, www,nil,qqq,ddd,ppp,nil,nil, www,nil,qqq,ddd,ppp,nil,nil );
checkTable( t, aaa,yyy,ccc,zzz,nil,nil,nil, nil,yyy,ccc,zzz,nil,nil,nil );
checkTable( m, aaa,bbb,nil,nil,nil,nil,nil, aaa,bbb,nil,nil,nil,nil,nil );
t.set("ff", rrr);
checkTable( s, www,nil,qqq,ddd,ppp,nil,nil, www,nil,qqq,ddd,ppp,nil,nil );
checkTable( t, aaa,yyy,ccc,zzz,nil,rrr,nil, nil,yyy,ccc,zzz,nil,nil,nil );
checkTable( m, aaa,bbb,nil,nil,nil,rrr,nil, aaa,bbb,nil,nil,nil,rrr,nil );
t.set("dd", sss);
checkTable( s, www,nil,qqq,ddd,ppp,nil,nil, www,nil,qqq,ddd,ppp,nil,nil );
checkTable( t, aaa,yyy,ccc,sss,nil,rrr,nil, nil,yyy,ccc,sss,nil,nil,nil );
checkTable( m, aaa,bbb,nil,nil,nil,rrr,nil, aaa,bbb,nil,nil,nil,rrr,nil );
m.set("gg", ttt);
checkTable( s, www,nil,qqq,ddd,ppp,nil,nil, www,nil,qqq,ddd,ppp,nil,nil );
checkTable( t, aaa,yyy,ccc,sss,nil,rrr,ttt, nil,yyy,ccc,sss,nil,nil,nil );
checkTable( m, aaa,bbb,nil,nil,nil,rrr,ttt, aaa,bbb,nil,nil,nil,rrr,ttt );
// make s fall back to t
s.setmetatable(LuaValue.tableOf(new LuaValue[] {LuaValue.INDEX,t,LuaValue.NEWINDEX,t}));
checkTable( s, www,yyy,qqq,ddd,ppp,rrr,ttt, www,nil,qqq,ddd,ppp,nil,nil );
checkTable( t, aaa,yyy,ccc,sss,nil,rrr,ttt, nil,yyy,ccc,sss,nil,nil,nil );
checkTable( m, aaa,bbb,nil,nil,nil,rrr,ttt, aaa,bbb,nil,nil,nil,rrr,ttt );
s.set("aa", www);
checkTable( s, www,yyy,qqq,ddd,ppp,rrr,ttt, www,nil,qqq,ddd,ppp,nil,nil );
checkTable( t, aaa,yyy,ccc,sss,nil,rrr,ttt, nil,yyy,ccc,sss,nil,nil,nil );
checkTable( m, aaa,bbb,nil,nil,nil,rrr,ttt, aaa,bbb,nil,nil,nil,rrr,ttt );
s.set("bb", zzz);
checkTable( s, www,zzz,qqq,ddd,ppp,rrr,ttt, www,nil,qqq,ddd,ppp,nil,nil );
checkTable( t, aaa,zzz,ccc,sss,nil,rrr,ttt, nil,zzz,ccc,sss,nil,nil,nil );
checkTable( m, aaa,bbb,nil,nil,nil,rrr,ttt, aaa,bbb,nil,nil,nil,rrr,ttt );
s.set("ee", xxx);
checkTable( s, www,zzz,qqq,ddd,xxx,rrr,ttt, www,nil,qqq,ddd,xxx,nil,nil );
checkTable( t, aaa,zzz,ccc,sss,nil,rrr,ttt, nil,zzz,ccc,sss,nil,nil,nil );
checkTable( m, aaa,bbb,nil,nil,nil,rrr,ttt, aaa,bbb,nil,nil,nil,rrr,ttt );
s.set("ff", yyy);
checkTable( s, www,zzz,qqq,ddd,xxx,yyy,ttt, www,nil,qqq,ddd,xxx,nil,nil );
checkTable( t, aaa,zzz,ccc,sss,nil,yyy,ttt, nil,zzz,ccc,sss,nil,nil,nil );
checkTable( m, aaa,bbb,nil,nil,nil,yyy,ttt, aaa,bbb,nil,nil,nil,yyy,ttt );
}
}

View File

@@ -0,0 +1,178 @@
/*******************************************************************************
* 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.lang.ref.WeakReference;
import junit.framework.TestCase;
import org.luaj.vm2.lib.OneArgFunction;
import org.luaj.vm2.lib.jse.JsePlatform;
public class OrphanedThreadTest extends TestCase {
Globals globals;
LuaThread luathread;
WeakReference luathr_ref;
LuaValue function;
WeakReference func_ref;
protected void setUp() throws Exception {
LuaThread.thread_orphan_check_interval = 5;
globals = JsePlatform.standardGlobals();
}
protected void tearDown() {
LuaThread.thread_orphan_check_interval = 30000;
}
public void testCollectOrphanedNormalThread() throws Exception {
function = new NormalFunction(globals);
doTest(LuaValue.TRUE, LuaValue.ZERO);
}
public void testCollectOrphanedEarlyCompletionThread() throws Exception {
function = new EarlyCompletionFunction(globals);
doTest(LuaValue.TRUE, LuaValue.ZERO);
}
public void testCollectOrphanedAbnormalThread() throws Exception {
function = new AbnormalFunction(globals);
doTest(LuaValue.FALSE, LuaValue.valueOf("abnormal condition"));
}
public void testCollectOrphanedClosureThread() throws Exception {
String script =
"print('in closure, arg is '..(...))\n" +
"arg = coroutine.yield(1)\n" +
"print('in closure.2, arg is '..arg)\n" +
"arg = coroutine.yield(0)\n" +
"print('leakage in closure.3, arg is '..arg)\n" +
"return 'done'\n";
function = globals.load(script, "script");
doTest(LuaValue.TRUE, LuaValue.ZERO);
}
public void testCollectOrphanedPcallClosureThread() throws Exception {
String script =
"f = function(x)\n" +
" print('in pcall-closure, arg is '..(x))\n" +
" arg = coroutine.yield(1)\n" +
" print('in pcall-closure.2, arg is '..arg)\n" +
" arg = coroutine.yield(0)\n" +
" print('leakage in pcall-closure.3, arg is '..arg)\n" +
" return 'done'\n" +
"end\n" +
"print( 'pcall-closre.result:', pcall( f, ... ) )\n";
function = globals.load(script, "script");
doTest(LuaValue.TRUE, LuaValue.ZERO);
}
public void testCollectOrphanedLoadCloasureThread() throws Exception {
String script =
"t = { \"print \", \"'hello, \", \"world'\", }\n" +
"i = 0\n" +
"arg = ...\n" +
"f = function()\n" +
" i = i + 1\n" +
" print('in load-closure, arg is', arg, 'next is', t[i])\n" +
" arg = coroutine.yield(1)\n" +
" return t[i]\n" +
"end\n" +
"load(f)()\n";
function = globals.load(script, "script");
doTest(LuaValue.TRUE, LuaValue.ONE);
}
private void doTest(LuaValue status2, LuaValue value2) throws Exception {
luathread = new LuaThread(globals, function);
luathr_ref = new WeakReference(luathread);
func_ref = new WeakReference(function);
assertNotNull(luathr_ref.get());
// resume two times
Varargs a = luathread.resume(LuaValue.valueOf("foo"));
assertEquals(LuaValue.ONE, a.arg(2));
assertEquals(LuaValue.TRUE, a.arg1());
a = luathread.resume(LuaValue.valueOf("bar"));
assertEquals(value2, a.arg(2));
assertEquals(status2, a.arg1());
// drop strong references
luathread = null;
function = null;
// gc
for (int i=0; i<100 && (luathr_ref.get() != null || func_ref.get() != null); i++) {
Runtime.getRuntime().gc();
Thread.sleep(5);
}
// check reference
assertNull(luathr_ref.get());
assertNull(func_ref.get());
}
static class NormalFunction extends OneArgFunction {
final Globals globals;
public NormalFunction(Globals globals) {
this.globals = globals;
}
public LuaValue call(LuaValue arg) {
System.out.println("in normal.1, arg is "+arg);
arg = globals.yield(ONE).arg1();
System.out.println("in normal.2, arg is "+arg);
arg = globals.yield(ZERO).arg1();
System.out.println("in normal.3, arg is "+arg);
return NONE;
}
}
static class EarlyCompletionFunction extends OneArgFunction {
final Globals globals;
public EarlyCompletionFunction(Globals globals) {
this.globals = globals;
}
public LuaValue call(LuaValue arg) {
System.out.println("in early.1, arg is "+arg);
arg = globals.yield(ONE).arg1();
System.out.println("in early.2, arg is "+arg);
return ZERO;
}
}
static class AbnormalFunction extends OneArgFunction {
final Globals globals;
public AbnormalFunction(Globals globals) {
this.globals = globals;
}
public LuaValue call(LuaValue arg) {
System.out.println("in abnormal.1, arg is "+arg);
arg = globals.yield(ONE).arg1();
System.out.println("in abnormal.2, arg is "+arg);
error("abnormal condition");
return ZERO;
}
}
}

View File

@@ -0,0 +1,89 @@
package org.luaj.vm2;
import junit.framework.TestCase;
import org.luaj.vm2.lib.jse.JsePlatform;
import org.luaj.vm2.require.RequireSampleClassCastExcep;
import org.luaj.vm2.require.RequireSampleLoadLuaError;
import org.luaj.vm2.require.RequireSampleLoadRuntimeExcep;
public class RequireClassTest extends TestCase {
private LuaTable globals;
private LuaValue require;
public void setUp() {
globals = JsePlatform.standardGlobals();
require = globals.get("require");
}
public void testLoadClass() {
LuaValue result = globals.load(new org.luaj.vm2.require.RequireSampleSuccess());
assertEquals( "require-sample-success-", result.tojstring() );
}
public void testRequireClassSuccess() {
LuaValue result = require.call( LuaValue.valueOf("org.luaj.vm2.require.RequireSampleSuccess") );
assertEquals( "require-sample-success-org.luaj.vm2.require.RequireSampleSuccess", result.tojstring() );
result = require.call( LuaValue.valueOf("org.luaj.vm2.require.RequireSampleSuccess") );
assertEquals( "require-sample-success-org.luaj.vm2.require.RequireSampleSuccess", result.tojstring() );
}
public void testRequireClassLoadLuaError() {
try {
LuaValue result = require.call( LuaValue.valueOf(RequireSampleLoadLuaError.class.getName()) );
fail( "incorrectly loaded class that threw lua error");
} catch ( LuaError le ) {
assertEquals(
"sample-load-lua-error",
le.getMessage() );
}
try {
LuaValue result = require.call( LuaValue.valueOf(RequireSampleLoadLuaError.class.getName()) );
fail( "incorrectly loaded class that threw lua error");
} catch ( LuaError le ) {
assertEquals(
"loop or previous error loading module '"+RequireSampleLoadLuaError.class.getName()+"'",
le.getMessage() );
}
}
public void testRequireClassLoadRuntimeException() {
try {
LuaValue result = require.call( LuaValue.valueOf(RequireSampleLoadRuntimeExcep.class.getName()) );
fail( "incorrectly loaded class that threw runtime exception");
} catch ( RuntimeException le ) {
assertEquals(
"sample-load-runtime-exception",
le.getMessage() );
}
try {
LuaValue result = require.call( LuaValue.valueOf(RequireSampleLoadRuntimeExcep.class.getName()) );
fail( "incorrectly loaded class that threw runtime exception");
} catch ( LuaError le ) {
assertEquals(
"loop or previous error loading module '"+RequireSampleLoadRuntimeExcep.class.getName()+"'",
le.getMessage() );
}
}
public void testRequireClassClassCastException() {
try {
LuaValue result = require.call( LuaValue.valueOf(RequireSampleClassCastExcep.class.getName()) );
fail( "incorrectly loaded class that threw class cast exception");
} catch ( LuaError le ) {
String msg = le.getMessage();
if ( msg.indexOf("not found") < 0 )
fail( "expected 'not found' message but got "+msg );
}
try {
LuaValue result = require.call( LuaValue.valueOf(RequireSampleClassCastExcep.class.getName()) );
fail( "incorrectly loaded class that threw class cast exception");
} catch ( LuaError le ) {
String msg = le.getMessage();
if ( msg.indexOf("not found") < 0 )
fail( "expected 'not found' message but got "+msg );
}
}
}

View File

@@ -0,0 +1,257 @@
/*******************************************************************************
* 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.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.MalformedURLException;
import java.net.URL;
import junit.framework.TestCase;
import org.luaj.vm2.lib.ResourceFinder;
import org.luaj.vm2.lib.jse.JseProcess;
import org.luaj.vm2.luajc.LuaJC;
abstract
public class ScriptDrivenTest extends TestCase implements ResourceFinder {
public static final boolean nocompile = "true".equals(System.getProperty("nocompile"));
public enum PlatformType {
JME, JSE, LUAJIT,
}
private final PlatformType platform;
private final String subdir;
protected Globals globals;
static final String zipdir = "test/lua/";
static final String zipfile = "luaj3.0-tests.zip";
protected ScriptDrivenTest( PlatformType platform, String subdir ) {
this.platform = platform;
this.subdir = subdir;
initGlobals();
}
private void initGlobals() {
switch ( platform ) {
default:
case JSE:
case LUAJIT:
globals = org.luaj.vm2.lib.jse.JsePlatform.debugGlobals();
break;
case JME:
globals = org.luaj.vm2.lib.jme.JmePlatform.debugGlobals();
break;
}
}
protected void setUp() throws Exception {
super.setUp();
initGlobals();
globals.finder = this;
}
// ResourceFinder implementation.
public InputStream findResource(String filename) {
InputStream is = findInPlainFile(filename);
if (is != null) return is;
is = findInPlainFileAsResource("",filename);
if (is != null) return is;
is = findInPlainFileAsResource("/",filename);
if (is != null) return is;
is = findInZipFileAsPlainFile(filename);
if (is != null) return is;
is = findInZipFileAsResource("",filename);
if (is != null) return is;
is = findInZipFileAsResource("/",filename);
return is;
}
private InputStream findInPlainFileAsResource(String prefix, String filename) {
return getClass().getResourceAsStream(prefix + subdir + filename);
}
private InputStream findInPlainFile(String filename) {
try {
File f = new File(zipdir+subdir+filename);
if (f.exists())
return new FileInputStream(f);
} catch ( IOException ioe ) {
ioe.printStackTrace();
}
return null;
}
private InputStream findInZipFileAsPlainFile(String filename) {
URL zip;
File file = new File(zipdir+zipfile);
try {
if ( file.exists() ) {
zip = file.toURI().toURL();
String path = "jar:"+zip.toExternalForm()+ "!/"+subdir+filename;
URL url = new URL(path);
return url.openStream();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
// Ignore and return null.
} catch (IOException ioe) {
ioe.printStackTrace();
}
return null;
}
private InputStream findInZipFileAsResource(String prefix, String filename) {
URL zip = null;
zip = getClass().getResource(zipfile);
if ( zip != null )
try {
String path = "jar:"+zip.toExternalForm()+ "!/"+subdir+filename;
URL url = new URL(path);
return url.openStream();
} catch (IOException ioe) {
ioe.printStackTrace();
}
return null;
}
// */
protected void runTest(String testName) {
try {
// override print()
final ByteArrayOutputStream output = new ByteArrayOutputStream();
final PrintStream oldps = globals.STDOUT;
final PrintStream ps = new PrintStream( output );
globals.STDOUT = ps;
// run the script
try {
LuaValue chunk = loadScript(testName, globals);
chunk.call(LuaValue.valueOf(platform.toString()));
ps.flush();
String actualOutput = new String(output.toByteArray());
String expectedOutput = getExpectedOutput(testName);
actualOutput = actualOutput.replaceAll("\r\n", "\n");
expectedOutput = expectedOutput.replaceAll("\r\n", "\n");
assertEquals(expectedOutput, actualOutput);
} finally {
globals.STDOUT = oldps;
ps.close();
}
} catch ( IOException ioe ) {
throw new RuntimeException(ioe.toString());
} catch ( InterruptedException ie ) {
throw new RuntimeException(ie.toString());
}
}
protected LuaValue loadScript(String name, Globals globals) throws IOException {
InputStream script = this.findResource(name+".lua");
if ( script == null )
fail("Could not load script for test case: " + name);
try {
switch ( this.platform ) {
case LUAJIT:
if ( nocompile ) {
LuaValue c = (LuaValue) Class.forName(name).newInstance();
return c;
} else {
LuaJC.install(globals);
return globals.load(script, name, "bt", globals);
}
default:
return globals.load(script, "@"+name+".lua", "bt", globals);
}
} catch ( Exception e ) {
e.printStackTrace();
throw new IOException( e.toString() );
} finally {
script.close();
}
}
private String getExpectedOutput(final String name) throws IOException,
InterruptedException {
InputStream output = this.findResource(name+".out");
if (output != null)
try {
return readString(output);
} finally {
output.close();
}
String expectedOutput = executeLuaProcess(name);
if (expectedOutput == null)
throw new IOException("Failed to get comparison output or run process for "+name);
return expectedOutput;
}
private String executeLuaProcess(String name) throws IOException, InterruptedException {
InputStream script = findResource(name+".lua");
if ( script == null )
throw new IOException("Failed to find source file "+script);
try {
String luaCommand = System.getProperty("LUA_COMMAND");
if ( luaCommand == null )
luaCommand = "lua";
String[] args = new String[] { luaCommand, "-", platform.toString() };
return collectProcessOutput(args, script);
} finally {
script.close();
}
}
public static String collectProcessOutput(String[] cmd, final InputStream input)
throws IOException, InterruptedException {
Runtime r = Runtime.getRuntime();
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
new JseProcess(cmd, input, baos, System.err).waitFor();
return new String(baos.toByteArray());
}
private String readString(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
copy(is, baos);
return new String(baos.toByteArray());
}
private static void copy(InputStream is, OutputStream os) throws IOException {
byte[] buf = new byte[1024];
int r;
while ((r = is.read(buf)) >= 0) {
os.write(buf, 0, r);
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,419 @@
/*******************************************************************************
* 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.util.ArrayList;
import java.util.Vector;
import junit.framework.TestCase;
public class TableTest extends TestCase {
protected LuaTable new_Table() {
return new LuaTable();
}
protected LuaTable new_Table(int n,int m) {
return new LuaTable(n,m);
}
private int keyCount(LuaTable t) {
return keys(t).length;
}
private LuaValue[] keys(LuaTable t) {
ArrayList<LuaValue> l = new ArrayList<LuaValue>();
LuaValue k = LuaValue.NIL;
while ( true ) {
Varargs n = t.next(k);
if ( (k = n.arg1()).isnil() )
break;
l.add( k );
}
return l.toArray(new LuaValue[t.length()]);
}
public void testInOrderIntegerKeyInsertion() {
LuaTable t = new_Table();
for ( int i = 1; i <= 32; ++i ) {
t.set( i, LuaValue.valueOf( "Test Value! "+i ) );
}
// Ensure all keys are still there.
for ( int i = 1; i <= 32; ++i ) {
assertEquals( "Test Value! " + i, t.get( i ).tojstring() );
}
// Ensure capacities make sense
assertEquals( 0, t.getHashLength() );
assertTrue( t.getArrayLength() >= 32 );
assertTrue( t.getArrayLength() <= 64 );
}
public void testRekeyCount() {
LuaTable t = new_Table();
// NOTE: This order of insertion is important.
t.set(3, LuaInteger.valueOf(3));
t.set(1, LuaInteger.valueOf(1));
t.set(5, LuaInteger.valueOf(5));
t.set(4, LuaInteger.valueOf(4));
t.set(6, LuaInteger.valueOf(6));
t.set(2, LuaInteger.valueOf(2));
for ( int i = 1; i < 6; ++i ) {
assertEquals(LuaInteger.valueOf(i), t.get(i));
}
assertTrue( t.getArrayLength() >= 3 );
assertTrue( t.getArrayLength() <= 12 );
assertTrue( t.getHashLength() <= 3 );
}
public void testOutOfOrderIntegerKeyInsertion() {
LuaTable t = new_Table();
for ( int i = 32; i > 0; --i ) {
t.set( i, LuaValue.valueOf( "Test Value! "+i ) );
}
// Ensure all keys are still there.
for ( int i = 1; i <= 32; ++i ) {
assertEquals( "Test Value! "+i, t.get( i ).tojstring() );
}
// Ensure capacities make sense
assertEquals( 32, t.getArrayLength() );
assertEquals( 0, t.getHashLength() );
}
public void testStringAndIntegerKeys() {
LuaTable t = new_Table();
for ( int i = 0; i < 10; ++i ) {
LuaString str = LuaValue.valueOf( String.valueOf( i ) );
t.set( i, str );
t.set( str, LuaInteger.valueOf( i ) );
}
assertTrue( t.getArrayLength() >= 8 ); // 1, 2, ..., 9
assertTrue( t.getArrayLength() <= 16 );
assertTrue( t.getHashLength() >= 11 ); // 0, "0", "1", ..., "9"
assertTrue( t.getHashLength() <= 33 );
LuaValue[] keys = keys(t);
int intKeys = 0;
int stringKeys = 0;
assertEquals( 20, keys.length );
for ( int i = 0; i < keys.length; ++i ) {
LuaValue k = keys[i];
if ( k instanceof LuaInteger ) {
final int ik = k.toint();
assertTrue( ik >= 0 && ik < 10 );
final int mask = 1 << ik;
assertTrue( ( intKeys & mask ) == 0 );
intKeys |= mask;
} else if ( k instanceof LuaString ) {
final int ik = Integer.parseInt( k.strvalue().tojstring() );
assertEquals( String.valueOf( ik ), k.strvalue().tojstring() );
assertTrue( ik >= 0 && ik < 10 );
final int mask = 1 << ik;
assertTrue( "Key \""+ik+"\" found more than once", ( stringKeys & mask ) == 0 );
stringKeys |= mask;
} else {
fail( "Unexpected type of key found" );
}
}
assertEquals( 0x03FF, intKeys );
assertEquals( 0x03FF, stringKeys );
}
public void testBadInitialCapacity() {
LuaTable t = new_Table(0, 1);
t.set( "test", LuaValue.valueOf("foo") );
t.set( "explode", LuaValue.valueOf("explode") );
assertEquals( 2, keyCount(t) );
}
public void testRemove0() {
LuaTable t = new_Table(2, 0);
t.set( 1, LuaValue.valueOf("foo") );
t.set( 2, LuaValue.valueOf("bah") );
assertNotSame(LuaValue.NIL, t.get(1));
assertNotSame(LuaValue.NIL, t.get(2));
assertEquals(LuaValue.NIL, t.get(3));
t.set( 1, LuaValue.NIL );
t.set( 2, LuaValue.NIL );
t.set( 3, LuaValue.NIL );
assertEquals(LuaValue.NIL, t.get(1));
assertEquals(LuaValue.NIL, t.get(2));
assertEquals(LuaValue.NIL, t.get(3));
}
public void testRemove1() {
LuaTable t = new_Table(0, 1);
t.set( "test", LuaValue.valueOf("foo") );
t.set( "explode", LuaValue.NIL );
t.set( 42, LuaValue.NIL );
t.set( new_Table(), LuaValue.NIL );
t.set( "test", LuaValue.NIL );
assertEquals( 0, keyCount(t) );
t.set( 10, LuaInteger.valueOf( 5 ) );
t.set( 10, LuaValue.NIL );
assertEquals( 0, keyCount(t) );
}
public void testRemove2() {
LuaTable t = new_Table(0, 1);
t.set( "test", LuaValue.valueOf("foo") );
t.set( "string", LuaInteger.valueOf( 10 ) );
assertEquals( 2, keyCount(t) );
t.set( "string", LuaValue.NIL );
t.set( "three", LuaValue.valueOf( 3.14 ) );
assertEquals( 2, keyCount(t) );
t.set( "test", LuaValue.NIL );
assertEquals( 1, keyCount(t) );
t.set( 10, LuaInteger.valueOf( 5 ) );
assertEquals( 2, keyCount(t) );
t.set( 10, LuaValue.NIL );
assertEquals( 1, keyCount(t) );
t.set( "three", LuaValue.NIL );
assertEquals( 0, keyCount(t) );
}
public void testShrinkNonPowerOfTwoArray() {
LuaTable t = new_Table(6, 2);
t.set(1, "one");
t.set(2, "two");
t.set(3, "three");
t.set(4, "four");
t.set(5, "five");
t.set(6, "six");
t.set("aa", "aaa");
t.set("bb", "bbb");
t.set(3, LuaValue.NIL);
t.set(4, LuaValue.NIL);
t.set(6, LuaValue.NIL);
t.set("cc", "ccc");
t.set("dd", "ddd");
assertEquals(4, t.getArrayLength());
assertTrue(t.getHashLength() < 10);
assertEquals(5, t.hashEntries);
assertEquals("one", t.get(1).tojstring());
assertEquals("two", t.get(2).tojstring());
assertEquals(LuaValue.NIL, t.get(3));
assertEquals(LuaValue.NIL, t.get(4));
assertEquals("five", t.get(5).tojstring());
assertEquals(LuaValue.NIL, t.get(6));
assertEquals("aaa", t.get("aa").tojstring());
assertEquals("bbb", t.get("bb").tojstring());
assertEquals("ccc", t.get("cc").tojstring());
assertEquals("ddd", t.get("dd").tojstring());
}
public void testInOrderLuaLength() {
LuaTable t = new_Table();
for ( int i = 1; i <= 32; ++i ) {
t.set( i, LuaValue.valueOf( "Test Value! "+i ) );
assertEquals( i, t.length() );
}
}
public void testOutOfOrderLuaLength() {
LuaTable t = new_Table();
for ( int j=8; j<32; j+=8 ) {
for ( int i = j; i > 0; --i ) {
t.set( i, LuaValue.valueOf( "Test Value! "+i ) );
}
assertEquals( j, t.length() );
}
}
public void testStringKeysLuaLength() {
LuaTable t = new_Table();
for ( int i = 1; i <= 32; ++i ) {
t.set( "str-"+i, LuaValue.valueOf( "String Key Test Value! "+i ) );
assertEquals( 0, t.length() );
}
}
public void testMixedKeysLuaLength() {
LuaTable t = new_Table();
for ( int i = 1; i <= 32; ++i ) {
t.set( "str-"+i, LuaValue.valueOf( "String Key Test Value! "+i ) );
t.set( i, LuaValue.valueOf( "Int Key Test Value! "+i ) );
assertEquals( i, t.length() );
}
}
private static final void compareLists(LuaTable t,Vector v) {
int n = v.size();
assertEquals(v.size(),t.length());
for ( int j=0; j<n; j++ ) {
Object vj = v.elementAt(j);
Object tj = t.get(j+1).tojstring();
vj = ((LuaString)vj).tojstring();
assertEquals(vj,tj);
}
}
public void testInsertBeginningOfList() {
LuaTable t = new_Table();
Vector v = new Vector();
for ( int i = 1; i <= 32; ++i ) {
LuaString test = LuaValue.valueOf("Test Value! "+i);
t.insert(1, test);
v.insertElementAt(test, 0);
compareLists(t,v);
}
}
public void testInsertEndOfList() {
LuaTable t = new_Table();
Vector v = new Vector();
for ( int i = 1; i <= 32; ++i ) {
LuaString test = LuaValue.valueOf("Test Value! "+i);
t.insert(0, test);
v.insertElementAt(test, v.size());
compareLists(t,v);
}
}
public void testInsertMiddleOfList() {
LuaTable t = new_Table();
Vector v = new Vector();
for ( int i = 1; i <= 32; ++i ) {
LuaString test = LuaValue.valueOf("Test Value! "+i);
int m = i / 2;
t.insert(m+1, test);
v.insertElementAt(test, m);
compareLists(t,v);
}
}
private static final void prefillLists(LuaTable t,Vector v) {
for ( int i = 1; i <= 32; ++i ) {
LuaString test = LuaValue.valueOf("Test Value! "+i);
t.insert(0, test);
v.insertElementAt(test, v.size());
}
}
public void testRemoveBeginningOfList() {
LuaTable t = new_Table();
Vector v = new Vector();
prefillLists(t,v);
for ( int i = 1; i <= 32; ++i ) {
t.remove(1);
v.removeElementAt(0);
compareLists(t,v);
}
}
public void testRemoveEndOfList() {
LuaTable t = new_Table();
Vector v = new Vector();
prefillLists(t,v);
for ( int i = 1; i <= 32; ++i ) {
t.remove(0);
v.removeElementAt(v.size()-1);
compareLists(t,v);
}
}
public void testRemoveMiddleOfList() {
LuaTable t = new_Table();
Vector v = new Vector();
prefillLists(t,v);
for ( int i = 1; i <= 32; ++i ) {
int m = v.size() / 2;
t.remove(m+1);
v.removeElementAt(m);
compareLists(t,v);
}
}
public void testRemoveWhileIterating() {
LuaTable t = LuaValue.tableOf(new LuaValue[] {
LuaValue.valueOf("a"), LuaValue.valueOf("aa"),
LuaValue.valueOf("b"), LuaValue.valueOf("bb"),
LuaValue.valueOf("c"), LuaValue.valueOf("cc"),
LuaValue.valueOf("d"), LuaValue.valueOf("dd"),
LuaValue.valueOf("e"), LuaValue.valueOf("ee"),
}, new LuaValue[] {
LuaValue.valueOf("11"),
LuaValue.valueOf("22"),
LuaValue.valueOf("33"),
LuaValue.valueOf("44"),
LuaValue.valueOf("55"),
});
// Find expected order after removal.
java.util.List<String> expected = new ArrayList<String>();
Varargs n;
int i;
for (n = t.next(LuaValue.NIL), i = 0; !n.arg1().isnil(); n = t.next(n.arg1()), ++i) {
if (i % 2 == 0)
expected.add(n.arg1() + "=" + n.arg(2));
}
// Remove every other key while iterating over the table.
for (n = t.next(LuaValue.NIL), i = 0; !n.arg1().isnil(); n = t.next(n.arg1()), ++i) {
if (i % 2 != 0)
t.set(n.arg1(), LuaValue.NIL);
}
// Iterate over remaining table, and form list of entries still in table.
java.util.List<String> actual = new ArrayList<String>();
for (n = t.next(LuaValue.NIL); !n.arg1().isnil(); n = t.next(n.arg1())) {
actual.add(n.arg1() + "=" + n.arg(2));
}
assertEquals(expected, actual);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
/*******************************************************************************
* Copyright (c) 2014 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
import junit.framework.TestCase;
import org.luaj.vm2.lib.jse.JsePlatform;
public class UTF8StreamTest extends TestCase {
public void testUtf8CharsInStream() {
String script = "x = \"98\u00b0: today's temp!\"\n"
+ "print('x = ', x)\n"
+ "return x";
Globals globals = JsePlatform.standardGlobals();
LuaValue chunk = globals.load(script);
LuaValue result = chunk.call();
String str = result.tojstring();
assertEquals("98\u00b0: today's temp!", str);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,230 @@
/*******************************************************************************
* 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 junit.framework.TestCase;
/**
* Tests of basic unary and binary operators on main value types.
*/
public class VarargsTest extends TestCase {
static LuaValue A = LuaValue.valueOf("a");
static LuaValue B = LuaValue.valueOf("b");
static LuaValue C = LuaValue.valueOf("c");
static LuaValue D = LuaValue.valueOf("d");
static LuaValue E = LuaValue.valueOf("e");
static LuaValue F = LuaValue.valueOf("f");
static LuaValue G = LuaValue.valueOf("g");
static LuaValue H = LuaValue.valueOf("h");
static LuaValue Z = LuaValue.valueOf("z");
static LuaValue NIL = LuaValue.NIL;
static Varargs A_G = LuaValue.varargsOf(new LuaValue[] { A, B, C, D, E, F, G });
static Varargs B_E = LuaValue.varargsOf(new LuaValue[] { B, C, D, E });
static Varargs C_G = LuaValue.varargsOf(new LuaValue[] { C, D, E, F, G });
static Varargs C_E = LuaValue.varargsOf(new LuaValue[] { C, D, E });
static Varargs DE = LuaValue.varargsOf(new LuaValue[] { D, E });
static Varargs E_G = LuaValue.varargsOf(new LuaValue[] { E, F, G });
static Varargs FG = LuaValue.varargsOf(new LuaValue[] { F, G });
static LuaValue[] Z_H_array = {Z, A, B, C, D, E, F, G, H };
static Varargs A_G_alt = new Varargs.ArrayPartVarargs(Z_H_array, 1, 7);
static Varargs B_E_alt = new Varargs.ArrayPartVarargs(Z_H_array, 2, 4);
static Varargs C_G_alt = new Varargs.ArrayPartVarargs(Z_H_array, 3, 5);
static Varargs C_E_alt = new Varargs.ArrayPartVarargs(Z_H_array, 3, 3);
static Varargs C_E_alt2 = LuaValue.varargsOf(C, D, E);
static Varargs DE_alt = new Varargs.PairVarargs(D,E);
static Varargs DE_alt2 = LuaValue.varargsOf(D,E);
static Varargs E_G_alt = new Varargs.ArrayPartVarargs(Z_H_array, 5, 3);
static Varargs FG_alt = new Varargs.PairVarargs(F, G);
static Varargs NONE = LuaValue.NONE;
static void expectEquals(Varargs x, Varargs y) {
assertEquals(x.narg(), y.narg());
assertEquals(x.arg1(), y.arg1());
assertEquals(x.arg(0), y.arg(0));
assertEquals(x.arg(-1), y.arg(-1));
assertEquals(x.arg(2), y.arg(2));
assertEquals(x.arg(3), y.arg(3));
for (int i = 4; i < x.narg() + 2; ++i)
assertEquals(x.arg(i), y.arg(i));
}
public void testSanity() {
expectEquals(A_G, A_G);
expectEquals(A_G_alt, A_G_alt);
expectEquals(A_G, A_G_alt);
expectEquals(B_E, B_E_alt);
expectEquals(C_G, C_G_alt);
expectEquals(C_E, C_E_alt);
expectEquals(C_E, C_E_alt2);
expectEquals(DE, DE_alt);
expectEquals(DE, DE_alt2);
expectEquals(E_G, E_G_alt);
expectEquals(FG, FG_alt);
expectEquals(FG_alt, FG_alt);
expectEquals(A, A);
expectEquals(NONE, NONE);
expectEquals(NIL, NIL);
}
public void testNegativeIndices() {
expectNegSubargsError(A_G);
expectNegSubargsError(A_G_alt);
expectNegSubargsError(B_E);
expectNegSubargsError(B_E_alt);
expectNegSubargsError(C_G);
expectNegSubargsError(C_G_alt);
expectNegSubargsError(C_E);
expectNegSubargsError(C_E_alt);
expectNegSubargsError(C_E_alt2);
expectNegSubargsError(DE);
expectNegSubargsError(DE_alt);
expectNegSubargsError(DE_alt2);
expectNegSubargsError(E_G);
expectNegSubargsError(FG);
expectNegSubargsError(A);
expectNegSubargsError(NONE);
expectNegSubargsError(NIL);
}
static void standardTestsA_G(Varargs a_g) {
expectEquals(A_G, a_g);
expectEquals(A_G, a_g.subargs(1));
expectEquals(C_G, a_g.subargs(3).subargs(1));
expectEquals(E_G, a_g.subargs(5));
expectEquals(E_G, a_g.subargs(5).subargs(1));
expectEquals(FG, a_g.subargs(6));
expectEquals(FG, a_g.subargs(6).subargs(1));
expectEquals(G, a_g.subargs(7));
expectEquals(G, a_g.subargs(7).subargs(1));
expectEquals(NONE, a_g.subargs(8));
expectEquals(NONE, a_g.subargs(8).subargs(1));
standardTestsC_G(A_G.subargs(3));
}
static void standardTestsC_G(Varargs c_g) {
expectEquals(C_G, c_g.subargs(1));
expectEquals(E_G, c_g.subargs(3));
expectEquals(E_G, c_g.subargs(3).subargs(1));
expectEquals(FG, c_g.subargs(4));
expectEquals(FG, c_g.subargs(4).subargs(1));
expectEquals(G, c_g.subargs(5));
expectEquals(G, c_g.subargs(5).subargs(1));
expectEquals(NONE, c_g.subargs(6));
expectEquals(NONE, c_g.subargs(6).subargs(1));
standardTestsE_G(c_g.subargs(3));
}
static void standardTestsE_G(Varargs e_g) {
expectEquals(E_G, e_g.subargs(1));
expectEquals(FG, e_g.subargs(2));
expectEquals(FG, e_g.subargs(2).subargs(1));
expectEquals(G, e_g.subargs(3));
expectEquals(G, e_g.subargs(3).subargs(1));
expectEquals(NONE, e_g.subargs(4));
expectEquals(NONE, e_g.subargs(4).subargs(1));
standardTestsFG(e_g.subargs(2));
}
static void standardTestsFG(Varargs fg) {
expectEquals(FG, fg.subargs(1));
expectEquals(G, fg.subargs(2));
expectEquals(G, fg.subargs(2).subargs(1));
expectEquals(NONE, fg.subargs(3));
expectEquals(NONE, fg.subargs(3).subargs(1));
}
static void standardTestsNone(Varargs none) {
expectEquals(NONE, none.subargs(1));
expectEquals(NONE, none.subargs(2));
}
public void testVarargsSubargs() {
standardTestsA_G(A_G);
standardTestsA_G(A_G_alt);
standardTestsC_G(C_G);
standardTestsC_G(C_G_alt);
standardTestsE_G(E_G);
standardTestsE_G(E_G_alt);
standardTestsFG(FG);
standardTestsFG(FG_alt);
standardTestsNone(NONE);
}
public void testVarargsMore() {
Varargs a_g;
a_g = LuaValue.varargsOf(new LuaValue[] { A, }, LuaValue.varargsOf( new LuaValue[] { B, C, D, E, F, G }));
standardTestsA_G(a_g);
a_g = LuaValue.varargsOf(new LuaValue[] { A, B, }, LuaValue.varargsOf( new LuaValue[] { C, D, E, F, G }));
standardTestsA_G(a_g);
a_g = LuaValue.varargsOf(new LuaValue[] { A, B, C, }, LuaValue.varargsOf( new LuaValue[] { D, E, F, G }));
standardTestsA_G(a_g);
a_g = LuaValue.varargsOf(new LuaValue[] { A, B, C, D, }, LuaValue.varargsOf(E, F, G));
standardTestsA_G(a_g);
a_g = LuaValue.varargsOf(new LuaValue[] { A, B, C, D, E }, LuaValue.varargsOf( F, G ));
standardTestsA_G(a_g);
a_g = LuaValue.varargsOf(new LuaValue[] { A, B, C, D, E, F, }, G );
standardTestsA_G(a_g);
}
public void testPairVarargsMore() {
Varargs a_g = new Varargs.PairVarargs(A,
new Varargs.PairVarargs(B,
new Varargs.PairVarargs(C,
new Varargs.PairVarargs(D,
new Varargs.PairVarargs(E,
new Varargs.PairVarargs(F, G))))));
standardTestsA_G(a_g);
}
public void testArrayPartMore() {
Varargs a_g;
a_g = new Varargs.ArrayPartVarargs(Z_H_array, 1, 1, new Varargs.ArrayPartVarargs(Z_H_array, 2, 6));
standardTestsA_G(a_g);
a_g = new Varargs.ArrayPartVarargs(Z_H_array, 1, 2, new Varargs.ArrayPartVarargs(Z_H_array, 3, 5));
standardTestsA_G(a_g);
a_g = new Varargs.ArrayPartVarargs(Z_H_array, 1, 3, new Varargs.ArrayPartVarargs(Z_H_array, 4, 4));
standardTestsA_G(a_g);
a_g = new Varargs.ArrayPartVarargs(Z_H_array, 1, 4, new Varargs.ArrayPartVarargs(Z_H_array, 5, 3));
standardTestsA_G(a_g);
a_g = new Varargs.ArrayPartVarargs(Z_H_array, 1, 5, new Varargs.ArrayPartVarargs(Z_H_array, 6, 2));
standardTestsA_G(a_g);
a_g = new Varargs.ArrayPartVarargs(Z_H_array, 1, 6, new Varargs.ArrayPartVarargs(Z_H_array, 7, 1));
standardTestsA_G(a_g);
}
static void expectNegSubargsError(Varargs v) {
String expected_msg = "bad argument #1: start must be > 0";
try {
v.subargs(0);
fail("Failed to throw exception for index 0");
} catch ( LuaError e ) {
assertEquals(expected_msg, e.getMessage());
}
try {
v.subargs(-1);
fail("Failed to throw exception for index -1");
} catch ( LuaError e ) {
assertEquals(expected_msg, e.getMessage());
}
}
}

View File

@@ -0,0 +1,261 @@
/*******************************************************************************
* 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.lang.ref.WeakReference;
abstract public class WeakTableTest extends TableTest {
public static class MyData {
public final int value;
public MyData( int value ) {
this.value = value;
}
public int hashCode() {
return value;
}
public boolean equals( Object o ) {
return (o instanceof MyData) && ((MyData)o).value == value;
}
public String toString() {
return "mydata-"+value;
}
}
static void collectGarbage() {
Runtime rt = Runtime.getRuntime();
rt.gc();
try {
Thread.sleep(20);
rt.gc();
Thread.sleep(20);
} catch ( Exception e ) {
e.printStackTrace();
}
rt.gc();
}
public static class WeakValueTableTest extends WeakTableTest {
protected LuaTable new_Table() { return WeakTable.make(false, true); }
protected LuaTable new_Table(int n,int m) { return WeakTable.make(false, true); }
public void testWeakValuesTable() {
LuaTable t = new_Table();
Object obj = new Object();
LuaTable tableValue = new LuaTable();
LuaString stringValue = LuaString.valueOf("this is a test");
LuaTable tableValue2 = new LuaTable();
t.set("table", tableValue);
t.set("userdata", LuaValue.userdataOf(obj, null));
t.set("string", stringValue);
t.set("string2", LuaValue.valueOf("another string"));
t.set(1, tableValue2);
assertTrue("table must have at least 4 elements", t.getHashLength() >= 4);
assertTrue("array part must have 1 element", t.getArrayLength() >= 1);
// check that table can be used to get elements
assertEquals(tableValue, t.get("table"));
assertEquals(stringValue, t.get("string"));
assertEquals(obj, t.get("userdata").checkuserdata());
assertEquals(tableValue2, t.get(1));
// nothing should be collected, since we have strong references here
collectGarbage();
// check that elements are still there
assertEquals(tableValue, t.get("table"));
assertEquals(stringValue, t.get("string"));
assertEquals(obj, t.get("userdata").checkuserdata());
assertEquals(tableValue2, t.get(1));
// drop our strong references
obj = null;
tableValue = null;
tableValue2 = null;
stringValue = null;
// Garbage collection should cause weak entries to be dropped.
collectGarbage();
// check that they are dropped
assertEquals(LuaValue.NIL, t.get("table"));
assertEquals(LuaValue.NIL, t.get("userdata"));
assertEquals(LuaValue.NIL, t.get(1));
assertFalse("strings should not be in weak references", t.get("string").isnil());
}
}
public static class WeakKeyTableTest extends WeakTableTest {
protected LuaTable new_Table() { return WeakTable.make(true, false); }
protected LuaTable new_Table(int n,int m) { return WeakTable.make(true, false); }
public void testWeakKeysTable() {
LuaTable t = WeakTable.make(true, false);
LuaValue key = LuaValue.userdataOf(new MyData(111));
LuaValue val = LuaValue.userdataOf(new MyData(222));
// set up the table
t.set( key, val );
assertEquals( val, t.get(key) );
System.gc();
assertEquals( val, t.get(key) );
// drop key and value references, replace them with new ones
WeakReference origkey = new WeakReference(key);
WeakReference origval = new WeakReference(val);
key = LuaValue.userdataOf(new MyData(111));
val = LuaValue.userdataOf(new MyData(222));
// new key and value should be interchangeable (feature of this test class)
assertEquals( key, origkey.get() );
assertEquals( val, origval.get() );
assertEquals( val, t.get(key) );
assertEquals( val, t.get((LuaValue) origkey.get()) );
assertEquals( origval.get(), t.get(key) );
// value should not be reachable after gc
collectGarbage();
assertEquals( null, origkey.get() );
assertEquals( LuaValue.NIL, t.get(key) );
collectGarbage();
assertEquals( null, origval.get() );
}
public void testNext() {
LuaTable t = WeakTable.make(true, true);
LuaValue key = LuaValue.userdataOf(new MyData(111));
LuaValue val = LuaValue.userdataOf(new MyData(222));
LuaValue key2 = LuaValue.userdataOf(new MyData(333));
LuaValue val2 = LuaValue.userdataOf(new MyData(444));
LuaValue key3 = LuaValue.userdataOf(new MyData(555));
LuaValue val3 = LuaValue.userdataOf(new MyData(666));
// set up the table
t.set( key, val );
t.set( key2, val2 );
t.set( key3, val3 );
// forget one of the keys
key2 = null;
val2 = null;
collectGarbage();
// table should have 2 entries
int size = 0;
for ( LuaValue k = t.next(LuaValue.NIL).arg1(); !k.isnil();
k = t.next(k).arg1() ) {
size++;
}
assertEquals(2, size);
}
}
public static class WeakKeyValueTableTest extends WeakTableTest {
protected LuaTable new_Table() { return WeakTable.make(true, true); }
protected LuaTable new_Table(int n,int m) { return WeakTable.make(true, true); }
public void testWeakKeysValuesTable() {
LuaTable t = WeakTable.make(true, true);
LuaValue key = LuaValue.userdataOf(new MyData(111));
LuaValue val = LuaValue.userdataOf(new MyData(222));
LuaValue key2 = LuaValue.userdataOf(new MyData(333));
LuaValue val2 = LuaValue.userdataOf(new MyData(444));
LuaValue key3 = LuaValue.userdataOf(new MyData(555));
LuaValue val3 = LuaValue.userdataOf(new MyData(666));
// set up the table
t.set( key, val );
t.set( key2, val2 );
t.set( key3, val3 );
assertEquals( val, t.get(key) );
assertEquals( val2, t.get(key2) );
assertEquals( val3, t.get(key3) );
System.gc();
assertEquals( val, t.get(key) );
assertEquals( val2, t.get(key2) );
assertEquals( val3, t.get(key3) );
// drop key and value references, replace them with new ones
WeakReference origkey = new WeakReference(key);
WeakReference origval = new WeakReference(val);
WeakReference origkey2 = new WeakReference(key2);
WeakReference origval2 = new WeakReference(val2);
WeakReference origkey3 = new WeakReference(key3);
WeakReference origval3 = new WeakReference(val3);
key = LuaValue.userdataOf(new MyData(111));
val = LuaValue.userdataOf(new MyData(222));
key2 = LuaValue.userdataOf(new MyData(333));
// don't drop val2, or key3
val3 = LuaValue.userdataOf(new MyData(666));
// no values should be reachable after gc
collectGarbage();
assertEquals( null, origkey.get() );
assertEquals( null, origval.get() );
assertEquals( null, origkey2.get() );
assertEquals( null, origval3.get() );
assertEquals( LuaValue.NIL, t.get(key) );
assertEquals( LuaValue.NIL, t.get(key2) );
assertEquals( LuaValue.NIL, t.get(key3) );
// all originals should be gone after gc, then access
val2 = null;
key3 = null;
collectGarbage();
assertEquals( null, origval2.get() );
assertEquals( null, origkey3.get() );
}
public void testReplace() {
LuaTable t = WeakTable.make(true, true);
LuaValue key = LuaValue.userdataOf(new MyData(111));
LuaValue val = LuaValue.userdataOf(new MyData(222));
LuaValue key2 = LuaValue.userdataOf(new MyData(333));
LuaValue val2 = LuaValue.userdataOf(new MyData(444));
LuaValue key3 = LuaValue.userdataOf(new MyData(555));
LuaValue val3 = LuaValue.userdataOf(new MyData(666));
// set up the table
t.set( key, val );
t.set( key2, val2 );
t.set( key3, val3 );
LuaValue val4 = LuaValue.userdataOf(new MyData(777));
t.set( key2, val4 );
// table should have 3 entries
int size = 0;
for ( LuaValue k = t.next(LuaValue.NIL).arg1();
!k.isnil() && size < 1000;
k = t.next(k).arg1() ) {
size++;
}
assertEquals(3, size);
}
}
}

View File

@@ -0,0 +1,117 @@
package org.luaj.vm2.compiler;
import junit.framework.TestCase;
import org.luaj.vm2.Globals;
import org.luaj.vm2.Print;
import org.luaj.vm2.Prototype;
import org.luaj.vm2.compiler.DumpState;
import org.luaj.vm2.lib.jse.JsePlatform;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
abstract public class AbstractUnitTests extends TestCase {
private final String dir;
private final String jar;
private Globals globals;
public AbstractUnitTests(String zipdir, String zipfile, String dir) {
URL zip = null;
zip = getClass().getResource(zipfile);
if ( zip == null ) {
File file = new File(zipdir+"/"+zipfile);
try {
if ( file.exists() )
zip = file.toURI().toURL();
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
if ( zip == null )
throw new RuntimeException("not found: "+zipfile);
this.jar = "jar:" + zip.toExternalForm()+ "!/";
this.dir = dir;
}
protected void setUp() throws Exception {
super.setUp();
globals = JsePlatform.standardGlobals();
}
protected String pathOfFile(String file) {
return jar + dir + "/" + file;
}
protected InputStream inputStreamOfPath(String path) throws IOException {
URL url = new URL(path);
return url.openStream();
}
protected InputStream inputStreamOfFile(String file) throws IOException {
return inputStreamOfPath(pathOfFile(file));
}
protected void doTest(String file) {
try {
// load source from jar
String path = pathOfFile(file);
byte[] lua = bytesFromJar(path);
// compile in memory
InputStream is = new ByteArrayInputStream(lua);
Prototype p = globals.loadPrototype(is, "@" + file, "bt");
String actual = protoToString(p);
// load expected value from jar
byte[] luac = bytesFromJar(path.substring(0, path.length()-4)+".lc");
Prototype e = loadFromBytes(luac, file);
String expected = protoToString(e);
// compare results
assertEquals(expected, actual);
// dump into memory
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DumpState.dump(p, baos, false);
byte[] dumped = baos.toByteArray();
// re-undump
Prototype p2 = loadFromBytes(dumped, file);
String actual2 = protoToString(p2);
// compare again
assertEquals(actual, actual2);
} catch (IOException e) {
fail(e.toString());
}
}
protected byte[] bytesFromJar(String path) throws IOException {
InputStream is = inputStreamOfPath(path);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[2048];
int n;
while ((n = is.read(buffer)) >= 0)
baos.write(buffer, 0, n);
is.close();
return baos.toByteArray();
}
protected Prototype loadFromBytes(byte[] bytes, String script)
throws IOException {
InputStream is = new ByteArrayInputStream(bytes);
return globals.loadPrototype(is, script, "b");
}
protected String protoToString(Prototype p) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
Print.ps = ps;
new Print().printFunction(p, true);
return baos.toString();
}
}

View File

@@ -0,0 +1,38 @@
package org.luaj.vm2.compiler;
public class CompilerUnitTests extends AbstractUnitTests {
public CompilerUnitTests() {
super("test/lua", "luaj3.0-tests.zip", "lua5.2.1-tests");
}
public void testAll() { doTest("all.lua"); }
public void testApi() { doTest("api.lua"); }
public void testAttrib() { doTest("attrib.lua"); }
public void testBig() { doTest("big.lua"); }
public void testBitwise() { doTest("bitwise.lua"); }
public void testCalls() { doTest("calls.lua"); }
public void testChecktable() { doTest("checktable.lua"); }
public void testClosure() { doTest("closure.lua"); }
public void testCode() { doTest("code.lua"); }
public void testConstruct() { doTest("constructs.lua"); }
public void testCoroutine() { doTest("coroutine.lua"); }
public void testDb() { doTest("db.lua"); }
public void testErrors() { doTest("errors.lua"); }
public void testEvents() { doTest("events.lua"); }
public void testFiles() { doTest("files.lua"); }
public void testGc() { doTest("gc.lua"); }
public void testGoto() { doTest("goto.lua"); }
public void testLiterals() { doTest("literals.lua"); }
public void testLocals() { doTest("locals.lua"); }
public void testMain() { doTest("main.lua"); }
public void testMath() { doTest("math.lua"); }
public void testNextvar() { doTest("nextvar.lua"); }
public void testPm() { doTest("pm.lua"); }
public void testSort() { doTest("sort.lua"); }
public void testStrings() { doTest("strings.lua"); }
public void testVararg() { doTest("vararg.lua"); }
public void testVerybig() { doTest("verybig.lua"); }
}

View File

@@ -0,0 +1,125 @@
package org.luaj.vm2.compiler;
import junit.framework.TestCase;
import org.luaj.vm2.*;
import org.luaj.vm2.compiler.DumpState;
import org.luaj.vm2.lib.jse.JsePlatform;
import java.io.*;
public class DumpLoadEndianIntTest extends TestCase {
private static final String SAVECHUNKS = "SAVECHUNKS";
private static final boolean SHOULDPASS = true;
private static final boolean SHOULDFAIL = false;
private static final String mixedscript = "return tostring(1234)..'-#!-'..tostring(23.75)";
private static final String intscript = "return tostring(1234)..'-#!-'..tostring(23)";
private static final String withdoubles = "1234-#!-23.75";
private static final String withints = "1234-#!-23";
private Globals globals;
protected void setUp() throws Exception {
super.setUp();
globals = JsePlatform.standardGlobals();
DumpState.ALLOW_INTEGER_CASTING = false;
}
public void testBigDoubleCompile() {
doTest( false, DumpState.NUMBER_FORMAT_FLOATS_OR_DOUBLES, false, mixedscript, withdoubles, withdoubles, SHOULDPASS );
doTest( false, DumpState.NUMBER_FORMAT_FLOATS_OR_DOUBLES, true, mixedscript, withdoubles, withdoubles, SHOULDPASS );
}
public void testLittleDoubleCompile() {
doTest( true, DumpState.NUMBER_FORMAT_FLOATS_OR_DOUBLES, false, mixedscript, withdoubles, withdoubles, SHOULDPASS );
doTest( true, DumpState.NUMBER_FORMAT_FLOATS_OR_DOUBLES, true, mixedscript, withdoubles, withdoubles, SHOULDPASS );
}
public void testBigIntCompile() {
DumpState.ALLOW_INTEGER_CASTING = true;
doTest( false, DumpState.NUMBER_FORMAT_INTS_ONLY, false, mixedscript, withdoubles, withints, SHOULDPASS );
doTest( false, DumpState.NUMBER_FORMAT_INTS_ONLY, true, mixedscript, withdoubles, withints, SHOULDPASS );
DumpState.ALLOW_INTEGER_CASTING = false;
doTest( false, DumpState.NUMBER_FORMAT_INTS_ONLY, false, mixedscript, withdoubles, withints, SHOULDFAIL );
doTest( false, DumpState.NUMBER_FORMAT_INTS_ONLY, true, mixedscript, withdoubles, withints, SHOULDFAIL );
doTest( false, DumpState.NUMBER_FORMAT_INTS_ONLY, false, intscript, withints, withints, SHOULDPASS );
doTest( false, DumpState.NUMBER_FORMAT_INTS_ONLY, true, intscript, withints, withints, SHOULDPASS );
}
public void testLittleIntCompile() {
DumpState.ALLOW_INTEGER_CASTING = true;
doTest( true, DumpState.NUMBER_FORMAT_INTS_ONLY, false, mixedscript, withdoubles, withints, SHOULDPASS );
doTest( true, DumpState.NUMBER_FORMAT_INTS_ONLY, true, mixedscript, withdoubles, withints, SHOULDPASS );
DumpState.ALLOW_INTEGER_CASTING = false;
doTest( true, DumpState.NUMBER_FORMAT_INTS_ONLY, false, mixedscript, withdoubles, withints, SHOULDFAIL );
doTest( true, DumpState.NUMBER_FORMAT_INTS_ONLY, true, mixedscript, withdoubles, withints, SHOULDFAIL );
doTest( true, DumpState.NUMBER_FORMAT_INTS_ONLY, false, intscript, withints, withints, SHOULDPASS );
doTest( true, DumpState.NUMBER_FORMAT_INTS_ONLY, true, intscript, withints, withints, SHOULDPASS );
}
public void testBigNumpatchCompile() {
doTest( false, DumpState.NUMBER_FORMAT_NUM_PATCH_INT32, false, mixedscript, withdoubles, withdoubles, SHOULDPASS );
doTest( false, DumpState.NUMBER_FORMAT_NUM_PATCH_INT32, true, mixedscript, withdoubles, withdoubles, SHOULDPASS );
}
public void testLittleNumpatchCompile() {
doTest( true, DumpState.NUMBER_FORMAT_NUM_PATCH_INT32, false, mixedscript, withdoubles, withdoubles, SHOULDPASS );
doTest( true, DumpState.NUMBER_FORMAT_NUM_PATCH_INT32, true, mixedscript, withdoubles, withdoubles, SHOULDPASS );
}
public void doTest( boolean littleEndian, int numberFormat, boolean stripDebug,
String script, String expectedPriorDump, String expectedPostDump, boolean shouldPass ) {
try {
// compile into prototype
Reader reader = new StringReader(script);
Prototype p = globals.compilePrototype(reader, "script");
// double check script result before dumping
LuaFunction f = new LuaClosure(p, globals);
LuaValue r = f.call();
String actual = r.tojstring();
assertEquals( expectedPriorDump, actual );
// dump into bytes
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
DumpState.dump(p, baos, stripDebug, numberFormat, littleEndian);
if ( ! shouldPass )
fail( "dump should not have succeeded" );
} catch ( Exception e ) {
if ( shouldPass )
fail( "dump threw "+e );
else
return;
}
byte[] dumped = baos.toByteArray();
// load again using compiler
InputStream is = new ByteArrayInputStream(dumped);
f = globals.load(is, "dumped", "b", globals).checkfunction();
r = f.call();
actual = r.tojstring();
assertEquals( expectedPostDump, actual );
// write test chunk
if ( System.getProperty(SAVECHUNKS) != null && script.equals(mixedscript) ) {
new File("build").mkdirs();
String filename = "build/test-"
+(littleEndian? "little-": "big-")
+(numberFormat==DumpState.NUMBER_FORMAT_FLOATS_OR_DOUBLES? "double-":
numberFormat==DumpState.NUMBER_FORMAT_INTS_ONLY? "int-":
numberFormat==DumpState.NUMBER_FORMAT_NUM_PATCH_INT32? "numpatch4-": "???-")
+(stripDebug? "nodebug-": "debug-")
+"bin.lua";
FileOutputStream fos = new FileOutputStream(filename);
fos.write( dumped );
fos.close();
}
} catch (IOException e) {
fail(e.toString());
}
}
}

View File

@@ -0,0 +1,29 @@
package org.luaj.vm2.compiler;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.compiler.CompilerUnitTests;
import org.luaj.vm2.parser.LuaParser;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
public class LuaParserTests extends CompilerUnitTests {
protected void setUp() throws Exception {
super.setUp();
LuaValue.valueOf(true);
}
protected void doTest(String file) {
try {
InputStream is = inputStreamOfFile(file);
Reader r = new InputStreamReader(is, "ISO-8859-1");
LuaParser parser = new LuaParser(r);
parser.Chunk();
} catch (Exception e) {
fail(e.getMessage());
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,30 @@
package org.luaj.vm2.compiler;
/**
* Framework to add regression tests as problem areas are found.
*
* To add a new regression test:
* 1) run "unpack.sh" in the project root
* 2) add a new "lua" file in the "regressions" subdirectory
* 3) run "repack.sh" in the project root
* 4) add a line to the source file naming the new test
*
* After adding a test, check in the zip file
* rather than the individual regression test files.
*
* @author jrosebor
*/
public class RegressionTests extends AbstractUnitTests {
public RegressionTests() {
super( "test/lua", "luaj3.0-tests.zip", "regressions" );
}
public void testModulo() { doTest("modulo.lua"); }
public void testConstruct() { doTest("construct.lua"); }
public void testBigAttrs() { doTest("bigattr.lua"); }
public void testControlChars() { doTest("controlchars.lua"); }
public void testComparators() { doTest("comparators.lua"); }
public void testMathRandomseed() { doTest("mathrandomseed.lua"); }
public void testVarargs() { doTest("varargs.lua"); }
}

View File

@@ -0,0 +1,95 @@
package org.luaj.vm2.compiler;
import junit.framework.TestCase;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaDouble;
import org.luaj.vm2.LuaInteger;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.jse.JsePlatform;
public class SimpleTests extends TestCase {
private Globals globals;
protected void setUp() throws Exception {
super.setUp();
globals = JsePlatform.standardGlobals();
}
private void doTest( String script ) {
try {
LuaValue c = globals.load(script, "script");
c.call();
} catch ( Exception e ) {
fail("i/o exception: "+e );
}
}
public void testTrivial() {
String s = "print( 2 )\n";
doTest( s );
}
public void testAlmostTrivial() {
String s = "print( 2 )\n" +
"print( 3 )\n";
doTest( s );
}
public void testSimple() {
String s = "print( 'hello, world' )\n"+
"for i = 2,4 do\n" +
" print( 'i', i )\n" +
"end\n";
doTest( s );
}
public void testBreak() {
String s = "a=1\n"+
"while true do\n"+
" if a>10 then\n"+
" break\n"+
" end\n"+
" a=a+1\n"+
" print( a )\n"+
"end\n";
doTest( s );
}
public void testShebang() {
String s = "#!../lua\n"+
"print( 2 )\n";
doTest( s );
}
public void testInlineTable() {
String s = "A = {g=10}\n"+
"print( A )\n";
doTest( s );
}
public void testEqualsAnd() {
String s = "print( 1 == b and b )\n";
doTest( s );
}
private static final int [] samehash = { 0, 1, -1, 2, -2, 4, 8, 16, 32, Integer.MAX_VALUE, Integer.MIN_VALUE };
private static final double [] diffhash = { .5, 1, 1.5, 1, .5, 1.5, 1.25, 2.5 };
public void testDoubleHashCode() {
for ( int i=0; i<samehash.length; i++ ) {
LuaValue j = LuaInteger.valueOf(samehash[i]);
LuaValue d = LuaDouble.valueOf(samehash[i]);
int hj = j.hashCode();
int hd = d.hashCode();
assertEquals(hj, hd);
}
for ( int i=0; i<diffhash.length; i+=2 ) {
LuaValue c = LuaValue.valueOf(diffhash[i+0]);
LuaValue d = LuaValue.valueOf(diffhash[i+1]);
int hc = c.hashCode();
int hd = d.hashCode();
assertTrue("hash codes are same: "+hc,hc!=hd);
}
}
}

View File

@@ -0,0 +1,17 @@
package org.luaj.vm2.require;
import org.luaj.vm2.LuaValue;
/**
* This should fail while trying to load via "require() because it is not a LibFunction"
*
*/
public class RequireSampleClassCastExcep {
public RequireSampleClassCastExcep() {
}
public LuaValue call() {
return LuaValue.valueOf("require-sample-class-cast-excep");
}
}

View File

@@ -0,0 +1,20 @@
package org.luaj.vm2.require;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.ZeroArgFunction;
/**
* This should fail while trying to load via
* "require()" because it throws a LuaError
*
*/
public class RequireSampleLoadLuaError extends ZeroArgFunction {
public RequireSampleLoadLuaError() {
}
public LuaValue call() {
error("sample-load-lua-error");
return LuaValue.valueOf("require-sample-load-lua-error");
}
}

View File

@@ -0,0 +1,18 @@
package org.luaj.vm2.require;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.ZeroArgFunction;
/**
* This should fail while trying to load via "require()" because it throws a RuntimeException
*
*/
public class RequireSampleLoadRuntimeExcep extends ZeroArgFunction {
public RequireSampleLoadRuntimeExcep() {
}
public LuaValue call() {
throw new RuntimeException("sample-load-runtime-exception");
}
}

View File

@@ -0,0 +1,18 @@
package org.luaj.vm2.require;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.TwoArgFunction;
/**
* This should succeed as a library that can be loaded dynamically via "require()"
*/
public class RequireSampleSuccess extends TwoArgFunction {
public RequireSampleSuccess() {
}
public LuaValue call(LuaValue modname, LuaValue env) {
env.checkglobals();
return LuaValue.valueOf("require-sample-success-"+modname.tojstring());
}
}

View File

@@ -0,0 +1,311 @@
/*******************************************************************************
* 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.script;
import java.io.CharArrayReader;
import java.io.CharArrayWriter;
import java.io.Reader;
import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.luaj.vm2.LuaFunction;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.OneArgFunction;
public class ScriptEngineTests extends TestSuite {
public static TestSuite suite() {
TestSuite suite = new TestSuite("Script Engine Tests");
suite.addTest( new TestSuite( LookupEngineTestCase.class, "Lookup Engine" ) );
suite.addTest( new TestSuite( DefaultBindingsTest.class, "Default Bindings" ) );
suite.addTest( new TestSuite( SimpleBindingsTest.class, "Simple Bindings" ) );
suite.addTest( new TestSuite( CompileClosureTest.class, "Compile Closure" ) );
suite.addTest( new TestSuite( CompileNonClosureTest.class, "Compile NonClosure" ) );
suite.addTest( new TestSuite( UserContextTest.class, "User Context" ) );
suite.addTest( new TestSuite( WriterTest.class, "Writer" ) );
return suite;
}
public static class LookupEngineTestCase extends TestCase {
public void testGetEngineByExtension() {
ScriptEngine e = new ScriptEngineManager().getEngineByExtension(".lua");
assertNotNull(e);
assertEquals(LuaScriptEngine.class, e.getClass());
}
public void testGetEngineByName() {
ScriptEngine e = new ScriptEngineManager().getEngineByName("luaj");
assertNotNull(e);
assertEquals(LuaScriptEngine.class, e.getClass());
}
public void testGetEngineByMimeType() {
ScriptEngine e = new ScriptEngineManager().getEngineByMimeType("text/lua");
assertNotNull(e);
assertEquals(LuaScriptEngine.class, e.getClass());
}
public void testFactoryMetadata() {
ScriptEngine e = new ScriptEngineManager().getEngineByName("luaj");
ScriptEngineFactory f = e.getFactory();
assertEquals("Luaj", f.getEngineName());
assertEquals("Luaj 0.0", f.getEngineVersion());
assertEquals("lua", f.getLanguageName());
assertEquals("5.2", f.getLanguageVersion());
}
}
public static class DefaultBindingsTest extends EngineTestCase {
protected Bindings createBindings() {
return e.createBindings();
}
}
public static class SimpleBindingsTest extends EngineTestCase {
protected Bindings createBindings() {
return new SimpleBindings();
}
}
public static class CompileClosureTest extends DefaultBindingsTest {
protected void setUp() throws Exception {
System.setProperty("org.luaj.luajc", "false");
super.setUp();
}
public void testCompiledFunctionIsClosure() throws ScriptException {
CompiledScript cs = ((Compilable)e).compile("return 'foo'");
LuaValue value = ((LuaScriptEngine.LuajCompiledScript)cs).function;
assertTrue(value.isclosure());
}
}
public static class CompileNonClosureTest extends DefaultBindingsTest {
protected void setUp() throws Exception {
System.setProperty("org.luaj.luajc", "true");
super.setUp();
}
public void testCompiledFunctionIsNotClosure() throws ScriptException {
CompiledScript cs = ((Compilable)e).compile("return 'foo'");
LuaValue value = ((LuaScriptEngine.LuajCompiledScript)cs).function;
assertFalse(value.isclosure());
}
}
abstract public static class EngineTestCase extends TestCase {
protected ScriptEngine e;
protected Bindings b;
abstract protected Bindings createBindings();
protected void setUp() throws Exception {
this.e = new ScriptEngineManager().getEngineByName("luaj");
this.b = createBindings();
}
public void testSqrtIntResult() throws ScriptException {
e.put("x", 25);
e.eval("y = math.sqrt(x)");
Object y = e.get("y");
assertEquals(5, y);
}
public void testOneArgFunction() throws ScriptException {
e.put("x", 25);
e.eval("y = math.sqrt(x)");
Object y = e.get("y");
assertEquals(5, y);
e.put("f", new OneArgFunction() {
public LuaValue call(LuaValue arg) {
return LuaValue.valueOf(arg.toString()+"123");
}
});
Object r = e.eval("return f('abc')");
assertEquals("abc123", r);
}
public void testCompiledScript() throws ScriptException {
CompiledScript cs = ((Compilable)e).compile("y = math.sqrt(x); return y");
b.put("x", 144);
assertEquals(12, cs.eval(b));
}
public void testBuggyLuaScript() {
try {
e.eval("\n\nbuggy lua code\n\n");
} catch ( ScriptException se ) {
assertEquals("eval threw javax.script.ScriptException: [string \"script\"]:3: syntax error", se.getMessage());
return;
}
fail("buggy script did not throw ScriptException as expected.");
}
public void testScriptRedirection() throws ScriptException {
Reader input = new CharArrayReader("abcdefg\nhijk".toCharArray());
CharArrayWriter output = new CharArrayWriter();
CharArrayWriter errors = new CharArrayWriter();
String script =
"print(\"string written using 'print'\")\n" +
"io.write(\"string written using 'io.write()'\\n\")\n" +
"io.stdout:write(\"string written using 'io.stdout:write()'\\n\")\n" +
"io.stderr:write(\"string written using 'io.stderr:write()'\\n\")\n" +
"io.write([[string read using 'io.stdin:read(\"*l\")':]]..io.stdin:read(\"*l\")..\"\\n\")\n";
// Evaluate script with redirection set
e.getContext().setReader(input);
e.getContext().setWriter(output);
e.getContext().setErrorWriter(errors);
e.eval(script);
final String expectedOutput = "string written using 'print'\n"+
"string written using 'io.write()'\n"+
"string written using 'io.stdout:write()'\n"+
"string read using 'io.stdin:read(\"*l\")':abcdefg\n";
assertEquals(expectedOutput, output.toString());
final String expectedErrors = "string written using 'io.stderr:write()'\n";
assertEquals(expectedErrors, errors.toString());
// Evaluate script with redirection reset
output.reset();
errors.reset();
// e.getContext().setReader(null); // This will block if using actual STDIN
e.getContext().setWriter(null);
e.getContext().setErrorWriter(null);
e.eval(script);
assertEquals("", output.toString());
assertEquals("", errors.toString());
}
public void testBindingJavaInt() throws ScriptException {
CompiledScript cs = ((Compilable)e).compile("y = x; return 'x '..type(x)..' '..tostring(x)\n");
b.put("x", 111);
assertEquals("x number 111", cs.eval(b));
assertEquals(111, b.get("y"));
}
public void testBindingJavaDouble() throws ScriptException {
CompiledScript cs = ((Compilable)e).compile("y = x; return 'x '..type(x)..' '..tostring(x)\n");
b.put("x", 125.125);
assertEquals("x number 125.125", cs.eval(b));
assertEquals(125.125, b.get("y"));
}
public void testBindingJavaString() throws ScriptException {
CompiledScript cs = ((Compilable)e).compile("y = x; return 'x '..type(x)..' '..tostring(x)\n");
b.put("x", "foo");
assertEquals("x string foo", cs.eval(b));
assertEquals("foo", b.get("y"));
}
public void testBindingJavaObject() throws ScriptException {
CompiledScript cs = ((Compilable)e).compile("y = x; return 'x '..type(x)..' '..tostring(x)\n");
b.put("x", new SomeUserClass());
assertEquals("x userdata some-user-value", cs.eval(b));
assertEquals(SomeUserClass.class, b.get("y").getClass());
}
public void testBindingJavaArray() throws ScriptException {
CompiledScript cs = ((Compilable)e).compile("y = x; return 'x '..type(x)..' '..#x..' '..x[1]..' '..x[2]\n");
b.put("x", new int[] { 777, 888 });
assertEquals("x userdata 2 777 888", cs.eval(b));
assertEquals(int[].class, b.get("y").getClass());
}
public void testBindingLuaFunction() throws ScriptException {
CompiledScript cs = ((Compilable)e).compile("y = function(x) return 678 + x end; return 'foo'");
assertEquals("foo", cs.eval(b).toString());
assertTrue(b.get("y") instanceof LuaFunction);
assertEquals(LuaValue.valueOf(801), ((LuaFunction) b.get("y")).call(LuaValue.valueOf(123)));
}
public void testUserClasses() throws ScriptException {
CompiledScript cs = ((Compilable)e).compile(
"x = x or luajava.newInstance('java.lang.String', 'test')\n" +
"return 'x ' .. type(x) .. ' ' .. tostring(x)\n");
assertEquals("x string test", cs.eval(b));
b.put("x", new SomeUserClass());
assertEquals("x userdata some-user-value", cs.eval(b));
}
public void testReturnMultipleValues() throws ScriptException {
CompiledScript cs = ((Compilable)e).compile("return 'foo', 'bar'\n");
Object o = cs.eval();
assertEquals(Object[].class, o.getClass());
Object[] array = (Object[]) o;
assertEquals(2, array.length);
assertEquals("foo", array[0]);
assertEquals("bar", array[1]);
}
}
public static class SomeUserClass {
public String toString() {
return "some-user-value";
}
}
public static class UserContextTest extends TestCase {
protected ScriptEngine e;
protected Bindings b;
protected ScriptContext c;
public void setUp() {
this.e = new ScriptEngineManager().getEngineByName("luaj");
this.c = new LuajContext();
this.b = c.getBindings(ScriptContext.ENGINE_SCOPE);
}
public void testUncompiledScript() throws ScriptException {
b.put("x", 144);
assertEquals(12, e.eval("z = math.sqrt(x); return z", b));
assertEquals(12, b.get("z"));
assertEquals(null, e.getBindings(ScriptContext.ENGINE_SCOPE).get("z"));
assertEquals(null, e.getBindings(ScriptContext.GLOBAL_SCOPE).get("z"));
b.put("x", 25);
assertEquals(5, e.eval("z = math.sqrt(x); return z", c));
assertEquals(5, b.get("z"));
assertEquals(null, e.getBindings(ScriptContext.ENGINE_SCOPE).get("z"));
assertEquals(null, e.getBindings(ScriptContext.GLOBAL_SCOPE).get("z"));
}
public void testCompiledScript() throws ScriptException {
CompiledScript cs = ((Compilable)e).compile("z = math.sqrt(x); return z");
b.put("x", 144);
assertEquals(12, cs.eval(b));
assertEquals(12, b.get("z"));
b.put("x", 25);
assertEquals(5, cs.eval(c));
assertEquals(5, b.get("z"));
}
}
public static class WriterTest extends TestCase {
protected ScriptEngine e;
protected Bindings b;
public void setUp() {
this.e = new ScriptEngineManager().getEngineByName("luaj");
this.b = e.getBindings(ScriptContext.ENGINE_SCOPE);
}
public void testWriter() throws ScriptException {
CharArrayWriter output = new CharArrayWriter();
CharArrayWriter errors = new CharArrayWriter();
e.getContext().setWriter(output);
e.getContext().setErrorWriter(errors);
e.eval("io.write( [[line]] )");
assertEquals("line", output.toString());
e.eval("io.write( [[ one\nline two\n]] )");
assertEquals("line one\nline two\n", output.toString());
output.reset();
}
}
}