From c8e4bea43d4e4f4527a6e4dca73be059478af8ef Mon Sep 17 00:00:00 2001 From: James Roseborough Date: Mon, 9 Mar 2015 06:32:54 +0000 Subject: [PATCH] Improve string byte backing ownership, add gradle file, up version, improve build packaging rules. --- README.html | 3 + build.xml | 14 +- examples/android/build.gradle | 37 +++ examples/maven/pom.xml | 2 +- src/core/org/luaj/vm2/LoadState.java | 2 +- src/core/org/luaj/vm2/LuaString.java | 182 +++++++----- src/core/org/luaj/vm2/compiler/Constants.java | 185 +++++++++++++ src/core/org/luaj/vm2/compiler/FuncState.java | 6 +- src/core/org/luaj/vm2/compiler/LexState.java | 75 +++-- src/core/org/luaj/vm2/compiler/LuaC.java | 260 ++++-------------- src/core/org/luaj/vm2/lib/IoLib.java | 4 +- src/core/org/luaj/vm2/lib/StringLib.java | 8 +- src/jse/org/luaj/vm2/ast/Str.java | 6 +- version.properties | 2 +- 14 files changed, 465 insertions(+), 321 deletions(-) create mode 100644 examples/android/build.gradle create mode 100644 src/core/org/luaj/vm2/compiler/Constants.java diff --git a/README.html b/README.html index e477ccb8..b4cd8401 100644 --- a/README.html +++ b/README.html @@ -968,6 +968,9 @@ Files are no longer hosted at LuaForge.
  • Add fallback to __lt when pocessing __le metatag.
  • Convert anonymous classes to inner classes (gradle build support).
  • Allow error() function to pass any lua object including non-strings.
  • +
  • Fix string backing ownership issue when compiling many scripts.
  • +
  • Make LuaC compile state explicit and improve factoring.
  • +
  • Add sample build.gradle file for Android example.
  • diff --git a/build.xml b/build.xml index 686e0216..f8dc2590 100644 --- a/build.xml +++ b/build.xml @@ -13,6 +13,11 @@ + + + + + @@ -150,7 +155,14 @@ - + + + + + + + + diff --git a/examples/android/build.gradle b/examples/android/build.gradle new file mode 100644 index 00000000..0ce835d6 --- /dev/null +++ b/examples/android/build.gradle @@ -0,0 +1,37 @@ +buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:1.0.1' + } +} + +apply plugin: 'com.android.application' + +android { + compileSdkVersion 20 + buildToolsVersion "20.0.0" + defaultConfig { + minSdkVersion 13 + targetSdkVersion 20 + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src'] + res.srcDirs = ['res'] + assets.srcDirs = ['assets'] + } + } +} + +repositories { + mavenCentral() +} + +dependencies { + compile 'org.luaj:luaj-jse:3.0.1' +} diff --git a/examples/maven/pom.xml b/examples/maven/pom.xml index 44a15ce4..85025c43 100644 --- a/examples/maven/pom.xml +++ b/examples/maven/pom.xml @@ -11,7 +11,7 @@ org.luaj luaj-jse - 3.0 + 3.0.1 junit diff --git a/src/core/org/luaj/vm2/LoadState.java b/src/core/org/luaj/vm2/LoadState.java index 992d887b..8554b0dd 100644 --- a/src/core/org/luaj/vm2/LoadState.java +++ b/src/core/org/luaj/vm2/LoadState.java @@ -220,7 +220,7 @@ public class LoadState { return null; byte[] bytes = new byte[size]; is.readFully( bytes, 0, size ); - return LuaString.valueOf( bytes, 0, bytes.length - 1 ); + return LuaString.valueUsing( bytes, 0, bytes.length - 1 ); } /** diff --git a/src/core/org/luaj/vm2/LuaString.java b/src/core/org/luaj/vm2/LuaString.java index fbd9a08a..f1d73bfc 100644 --- a/src/core/org/luaj/vm2/LuaString.java +++ b/src/core/org/luaj/vm2/LuaString.java @@ -38,7 +38,7 @@ import org.luaj.vm2.lib.StringLib; * sequences of characters or unicode code points, the {@link LuaString} * implementation holds the string value in an internal byte array. *

    - * {@link LuaString} values are generally not mutable once constructed, + * {@link LuaString} values are not considered mutable once constructed, * so multiple {@link LuaString} values can chare a single byte array. *

    * Currently {@link LuaString}s are pooled via a centrally managed weak table. @@ -46,6 +46,9 @@ import org.luaj.vm2.lib.StringLib; * Constructors are not exposed directly. As with number, booleans, and nil, * instance construction should be via {@link LuaValue#valueOf(byte[])} or similar API. *

    + * Because of this pooling, users of LuaString must not directly alter the + * bytes in a LuaString, or undefined behavior will result. + *

    * When Java Strings are used to initialize {@link LuaString} data, the UTF8 encoding is assumed. * The functions * {@link LuaString#lengthAsUtf8(char[]), @@ -59,47 +62,42 @@ import org.luaj.vm2.lib.StringLib; */ public class LuaString extends LuaValue { - /** Size of cache of recent short strings. This is the maximum number of LuaStrings that - * will be retained in the cache of recent short strings. */ - public static final int RECENT_STRINGS_CACHE_SIZE = 128; - - /** Maximum length of a string to be considered for recent short strings caching. - * This effectively limits the total memory that can be spent on the recent strings cache, - * ecause no LuaString whose backing exceeds this length will be put into the cache. */ - public static final int RECENT_STRINGS_MAX_LENGTH = 32; - /** The singleton instance representing lua {@code true} */ public static LuaValue s_metatable; - /** The bytes for the string */ + /** The bytes for the string. These must not be mutated directly because + * the backing may be shared by multiple LuaStrings, and the hash code is + * computed only at construction time. + * It is exposed only for performance and legacy reasons. */ public final byte[] m_bytes; /** The offset into the byte array, 0 means start at the first byte */ - public final int m_offset; + public final int m_offset; /** The number of bytes that comprise this string */ - public final int m_length; - - private static class Cache { - /** Simple cache of recently created strings that are short. - * This is simply a list of strings, indexed by their hash codes modulo the cache size - * that have been recently constructed. If a string is being constructed frequently - * from different contexts, it will generally may show up as a cache hit and resolve - * to the same value. */ - public final LuaString recent_short_strings[] = new LuaString[RECENT_STRINGS_CACHE_SIZE]; - - public LuaString get(LuaString s) { - final int index = s.hashCode() & (RECENT_STRINGS_CACHE_SIZE - 1); - final LuaString cached = (LuaString) recent_short_strings[index]; - if (cached != null && s.raweq(cached)) - return cached; - recent_short_strings[index] = s; - return s; - } - - static final Cache instance = new Cache(); - } + public final int m_length; + /** The hashcode for this string. Computed at construct time. */ + private final int m_hashcode; + + /** Size of cache of recent short strings. This is the maximum number of LuaStrings that + * will be retained in the cache of recent short strings. Exposed to package for testing. */ + static final int RECENT_STRINGS_CACHE_SIZE = 128; + + /** Maximum length of a string to be considered for recent short strings caching. + * This effectively limits the total memory that can be spent on the recent strings cache, + * because no LuaString whose backing exceeds this length will be put into the cache. + * Exposed to package for testing. */ + static final int RECENT_STRINGS_MAX_LENGTH = 32; + + /** Simple cache of recently created strings that are short. + * This is simply a list of strings, indexed by their hash codes modulo the cache size + * that have been recently constructed. If a string is being constructed frequently + * from different contexts, it will generally show up as a cache hit and resolve + * to the same value. */ + private static final LuaString recent_short_strings[] = + new LuaString[RECENT_STRINGS_CACHE_SIZE]; + /** * Get a {@link LuaString} instance whose bytes match * the supplied Java String using the UTF8 encoding. @@ -110,13 +108,12 @@ public class LuaString extends LuaValue { char[] c = string.toCharArray(); byte[] b = new byte[lengthAsUtf8(c)]; encodeToUtf8(c, c.length, b, 0); - return valueOf(b, 0, b.length); + return valueUsing(b, 0, b.length); } - // TODO: should this be deprecated or made private? - /** Construct a {@link LuaString} around a byte array that may be used directly as the backing. + /** Construct a {@link LuaString} for a portion of a byte array. *

    - * The array may be used as the backing for this object, so clients must not change contents. + * The array is first be used as the backing for this object, so clients must not change contents. * If the supplied value for 'len' is more than half the length of the container, the * supplied byte array will be used as the backing, otherwise the bytes will be copied to a * new byte array, and cache lookup may be performed. @@ -127,21 +124,46 @@ public class LuaString extends LuaValue { * @return {@link LuaString} wrapping the byte buffer */ public static LuaString valueOf(byte[] bytes, int off, int len) { - if (bytes.length < RECENT_STRINGS_MAX_LENGTH) { - // Short string. Reuse the backing and check the cache of recent strings before returning. - final LuaString s = new LuaString(bytes, off, len); - return Cache.instance.get( s ); - } else if (len >= bytes.length / 2) { - // Reuse backing only when more than half the bytes are part of the result. - return new LuaString(bytes, off, len); - } else { - // Short result relative to the source. Copy only the bytes that are actually to be used. - final byte[] b = new byte[len]; - System.arraycopy(bytes, off, b, 0, len); - return valueOf(b, 0, len); // To possibly use cached version. - } + if (len > RECENT_STRINGS_MAX_LENGTH) + return valueFromCopy(bytes, off, len); + final int hash = hashCode(bytes, off, len); + final int bucket = hash & (RECENT_STRINGS_CACHE_SIZE - 1); + final LuaString t = recent_short_strings[bucket]; + if (t != null && t.m_hashcode == hash && t.byteseq(bytes, off, len)) return t; + final LuaString s = valueFromCopy(bytes, off, len); + recent_short_strings[bucket] = s; + return s; } - + + /** Construct a new LuaString using a copy of the bytes array supplied */ + private static LuaString valueFromCopy(byte[] bytes, int off, int len) { + final byte[] copy = new byte[len]; + for (int i=0; i + * The caller must ensure that the array is not mutated after the call. + * However, if the string is short enough the short-string cache is checked + * for a match which may be used instead of the supplied byte array. + *

    + * @param bytes byte buffer + * @return {@link LuaString} wrapping the byte buffer, or an equivalent string. + */ + static public LuaString valueUsing(byte[] bytes, int off, int len) { + if (bytes.length > RECENT_STRINGS_MAX_LENGTH) + return new LuaString(bytes, off, len); + final int hash = hashCode(bytes, off, len); + final int bucket = hash & (RECENT_STRINGS_CACHE_SIZE - 1); + final LuaString t = recent_short_strings[bucket]; + if (t != null && t.m_hashcode == hash && t.byteseq(bytes, off, len)) return t; + final LuaString s = new LuaString(bytes, off, len); + recent_short_strings[bucket] = s; + return s; + } + /** Construct a {@link LuaString} using the supplied characters as byte values. *

    * Only the low-order 8-bits of each character are used, the remainder is ignored. @@ -166,13 +188,13 @@ public class LuaString extends LuaValue { byte[] b = new byte[len]; for ( int i=0; i - * The array may be used directly as the backing, so clients must not change contents. + * The LuaString returned will either be a new LuaString containing a copy + * of the bytes array, or be an existing LuaString used already having the same value. *

    * @param bytes byte buffer * @return {@link LuaString} wrapping the byte buffer @@ -181,6 +203,21 @@ public class LuaString extends LuaValue { return valueOf(bytes, 0, bytes.length); } + /** Construct a {@link LuaString} for all the bytes in a byte array, possibly using + * the supplied array as the backing store. + *

    + * The LuaString returned will either be a new LuaString containing the byte array, + * or be an existing LuaString used already having the same value. + *

    + * The caller must not mutate the contents of the byte array after this call, as + * it may be used elsewhere due to recent short string caching. + * @param bytes byte buffer + * @return {@link LuaString} wrapping the byte buffer + */ + public static LuaString valueUsing(byte[] bytes) { + return valueUsing(bytes, 0, bytes.length); + } + /** Construct a {@link LuaString} around a byte array without copying the contents. *

    * The array is used directly after this is called, so clients must not change contents. @@ -194,6 +231,7 @@ public class LuaString extends LuaValue { this.m_bytes = bytes; this.m_offset = offset; this.m_length = length; + this.m_hashcode = hashCode(bytes, offset, length); } public boolean isstring() { @@ -275,7 +313,7 @@ public class LuaString extends LuaValue { byte[] b = new byte[lhs.m_length+this.m_length]; System.arraycopy(lhs.m_bytes, lhs.m_offset, b, 0, lhs.m_length); System.arraycopy(this.m_bytes, this.m_offset, b, lhs.m_length, this.m_length); - return valueOf(b, 0, b.length); + return valueUsing(b, 0, b.length); } // string comparison @@ -394,17 +432,32 @@ public class LuaString extends LuaValue { * beginIndex and extending for (endIndex - beginIndex ) characters. */ public LuaString substring( int beginIndex, int endIndex ) { - return valueOf( m_bytes, m_offset + beginIndex, endIndex - beginIndex ); + final int off = m_offset + beginIndex; + final int len = endIndex - beginIndex; + return len >= m_length / 2? + valueUsing(m_bytes, off, len): + valueOf(m_bytes, off, len); } public int hashCode() { - int h = m_length; /* seed */ - int step = (m_length>>5)+1; /* if string is too long, don't hash all its chars */ - for (int l1=m_length; l1>=step; l1-=step) /* compute hash */ - h = h ^ ((h<<5)+(h>>2)+(((int) m_bytes[m_offset+l1-1] ) & 0x0FF )); - return h; + return m_hashcode; } + /** Compute the hash code of a sequence of bytes within a byte array using + * lua's rules for string hashes. For long strings, not all bytes are hashed. + * @param bytes byte array containing the bytes. + * @param offset offset into the hash for the first byte. + * @param length number of bytes starting with offset that are part of the string. + * @return hash for the string defined by bytes, offset, and length. + */ + public static int hashCode(byte[] bytes, int offset, int length) { + int h = length; /* seed */ + int step = (length>>5)+1; /* if string is too long, don't hash all its chars */ + for (int l1=length; l1>=step; l1-=step) /* compute hash */ + h = h ^ ((h<<5)+(h>>2)+(((int) bytes[offset+l1-1] ) & 0x0FF )); + return h; + } + // object comparison, used in key comparison public boolean equals( Object o ) { if ( o instanceof LuaString ) { @@ -441,6 +494,11 @@ public class LuaString extends LuaValue { return equals( a.m_bytes, a.m_offset + i, b.m_bytes, b.m_offset + j, n ); } + /** Return true if the bytes in the supplied range match this LuaStrings bytes. */ + private boolean byteseq(byte[] bytes, int off, int len) { + return (m_length == len && equals(m_bytes, m_offset, bytes, off, len)); + } + public static boolean equals( byte[] a, int i, byte[] b, int j, int n ) { if ( a.length < i + n || b.length < j + n ) return false; diff --git a/src/core/org/luaj/vm2/compiler/Constants.java b/src/core/org/luaj/vm2/compiler/Constants.java new file mode 100644 index 00000000..50f2c2eb --- /dev/null +++ b/src/core/org/luaj/vm2/compiler/Constants.java @@ -0,0 +1,185 @@ +/******************************************************************************* +* Copyright (c) 2015 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.compiler; + +import org.luaj.vm2.LocVars; +import org.luaj.vm2.Lua; +import org.luaj.vm2.LuaError; +import org.luaj.vm2.LuaString; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Prototype; +import org.luaj.vm2.Upvaldesc; + +/** + * Constants used by the LuaC compiler and related classes. + * + * @see LuaC + * @see FuncState + */ +public class Constants extends Lua { + + /** Maximum stack size of a luaj vm interpreter instance. */ + public static final int MAXSTACK = 250; + + static final int LUAI_MAXUPVAL = 0xff; + static final int LUAI_MAXVARS = 200; + static final int NO_REG = MAXARG_A; + + + /* OpMode - basic instruction format */ + static final int + iABC = 0, + iABx = 1, + iAsBx = 2; + + /* OpArgMask */ + static final int + OpArgN = 0, /* argument is not used */ + OpArgU = 1, /* argument is used */ + OpArgR = 2, /* argument is a register or a jump offset */ + OpArgK = 3; /* argument is a constant or register/constant */ + + + protected static void _assert(boolean b) { + if (!b) + throw new LuaError("compiler assert failed"); + } + + static void SET_OPCODE(InstructionPtr i,int o) { + i.set( ( i.get() & (MASK_NOT_OP)) | ((o << POS_OP) & MASK_OP) ); + } + + static void SETARG_A(int[] code, int index, int u) { + code[index] = (code[index] & (MASK_NOT_A)) | ((u << POS_A) & MASK_A); + } + + static void SETARG_A(InstructionPtr i,int u) { + i.set( ( i.get() & (MASK_NOT_A)) | ((u << POS_A) & MASK_A) ); + } + + static void SETARG_B(InstructionPtr i,int u) { + i.set( ( i.get() & (MASK_NOT_B)) | ((u << POS_B) & MASK_B) ); + } + + static void SETARG_C(InstructionPtr i,int u) { + i.set( ( i.get() & (MASK_NOT_C)) | ((u << POS_C) & MASK_C) ); + } + + static void SETARG_Bx(InstructionPtr i,int u) { + i.set( ( i.get() & (MASK_NOT_Bx)) | ((u << POS_Bx) & MASK_Bx) ); + } + + static void SETARG_sBx(InstructionPtr i,int u) { + SETARG_Bx( i, u + MAXARG_sBx ); + } + + static int CREATE_ABC(int o, int a, int b, int c) { + return ((o << POS_OP) & MASK_OP) | + ((a << POS_A) & MASK_A) | + ((b << POS_B) & MASK_B) | + ((c << POS_C) & MASK_C) ; + } + + static int CREATE_ABx(int o, int a, int bc) { + return ((o << POS_OP) & MASK_OP) | + ((a << POS_A) & MASK_A) | + ((bc << POS_Bx) & MASK_Bx) ; + } + + // vector reallocation + + static LuaValue[] realloc(LuaValue[] v, int n) { + LuaValue[] a = new LuaValue[n]; + if ( v != null ) + System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); + return a; + } + + static Prototype[] realloc(Prototype[] v, int n) { + Prototype[] a = new Prototype[n]; + if ( v != null ) + System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); + return a; + } + + static LuaString[] realloc(LuaString[] v, int n) { + LuaString[] a = new LuaString[n]; + if ( v != null ) + System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); + return a; + } + + static LocVars[] realloc(LocVars[] v, int n) { + LocVars[] a = new LocVars[n]; + if ( v != null ) + System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); + return a; + } + + static Upvaldesc[] realloc(Upvaldesc[] v, int n) { + Upvaldesc[] a = new Upvaldesc[n]; + if ( v != null ) + System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); + return a; + } + + static LexState.Vardesc[] realloc(LexState.Vardesc[] v, int n) { + LexState.Vardesc[] a = new LexState.Vardesc[n]; + if ( v != null ) + System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); + return a; + } + + static LexState.Labeldesc[] grow(LexState.Labeldesc[] v, int min_n) { + return v == null ? new LexState.Labeldesc[2] : v.length < min_n ? realloc(v, v.length*2) : v; + } + + static LexState.Labeldesc[] realloc(LexState.Labeldesc[] v, int n) { + LexState.Labeldesc[] a = new LexState.Labeldesc[n]; + if ( v != null ) + System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); + return a; + } + + static int[] realloc(int[] v, int n) { + int[] a = new int[n]; + if ( v != null ) + System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); + return a; + } + + static byte[] realloc(byte[] v, int n) { + byte[] a = new byte[n]; + if ( v != null ) + System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); + return a; + } + + static char[] realloc(char[] v, int n) { + char[] a = new char[n]; + if ( v != null ) + System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); + return a; + } + + protected Constants() {} +} diff --git a/src/core/org/luaj/vm2/compiler/FuncState.java b/src/core/org/luaj/vm2/compiler/FuncState.java index bc7c31a8..2717f369 100644 --- a/src/core/org/luaj/vm2/compiler/FuncState.java +++ b/src/core/org/luaj/vm2/compiler/FuncState.java @@ -28,8 +28,6 @@ import org.luaj.vm2.Lua; import org.luaj.vm2.LuaDouble; import org.luaj.vm2.LuaInteger; import org.luaj.vm2.LuaString; -import org.luaj.vm2.LuaTable; -import org.luaj.vm2.LuaUserdata; import org.luaj.vm2.LuaValue; import org.luaj.vm2.Prototype; import org.luaj.vm2.Upvaldesc; @@ -37,7 +35,7 @@ import org.luaj.vm2.compiler.LexState.ConsControl; import org.luaj.vm2.compiler.LexState.expdesc; -public class FuncState extends LuaC { +public class FuncState extends Constants { static class BlockCnt { BlockCnt previous; /* chain */ @@ -52,7 +50,7 @@ public class FuncState extends LuaC { Hashtable h; /* table to find (and reuse) elements in `k' */ FuncState prev; /* enclosing function */ LexState ls; /* lexical state */ - LuaC L; /* compiler being invoked */ + LuaC.CompileState L; /* compiler being invoked */ BlockCnt bl; /* chain of current blocks */ int pc; /* next position to code (equivalent to `ncode') */ int lasttarget; /* `pc' of last `jump target' */ diff --git a/src/core/org/luaj/vm2/compiler/LexState.java b/src/core/org/luaj/vm2/compiler/LexState.java index bc29e27d..c85cf68e 100644 --- a/src/core/org/luaj/vm2/compiler/LexState.java +++ b/src/core/org/luaj/vm2/compiler/LexState.java @@ -23,7 +23,6 @@ package org.luaj.vm2.compiler; import java.io.IOException; import java.io.InputStream; -import java.io.Reader; import java.util.Hashtable; import org.luaj.vm2.LocVars; @@ -37,7 +36,7 @@ import org.luaj.vm2.compiler.FuncState.BlockCnt; import org.luaj.vm2.lib.MathLib; -public class LexState { +public class LexState extends Constants { protected static final String RESERVED_LOCAL_VAR_FOR_CONTROL = "(for control)"; protected static final String RESERVED_LOCAL_VAR_FOR_STATE = "(for state)"; @@ -135,7 +134,7 @@ public class LexState { final Token t = new Token(); /* current token */ final Token lookahead = new Token(); /* look ahead token */ FuncState fs; /* `FuncState' is private to the parser */ - LuaC L; + LuaC.CompileState L; InputStream z; /* input stream */ char[] buff; /* buffer for tokens */ int nbuff; /* length of buffer */ @@ -203,7 +202,7 @@ public class LexState { } - public LexState(LuaC state, InputStream stream) { + public LexState(LuaC.CompileState state, InputStream stream) { this.z = stream; this.buff = new char[32]; this.L = state; @@ -229,7 +228,7 @@ public class LexState { void save(int c) { if ( buff == null || nbuff + 1 > buff.length ) - buff = LuaC.realloc( buff, nbuff*2+1 ); + buff = realloc( buff, nbuff*2+1 ); buff[nbuff++] = (char) c; } @@ -282,7 +281,7 @@ public class LexState { void inclinenumber() { int old = current; - LuaC._assert( currIsNewline() ); + _assert( currIsNewline() ); nextChar(); /* skip '\n' or '\r' */ if ( currIsNewline() && current != old ) nextChar(); /* skip '\n\r' or '\r\n' */ @@ -290,7 +289,7 @@ public class LexState { syntaxerror("chunk has too many lines"); } - void setinput( LuaC L, int firstByte, InputStream z, LuaString source ) { + void setinput(LuaC.CompileState L, int firstByte, InputStream z, LuaString source) { this.decpoint = '.'; this.L = L; this.lookahead.token = TK_EOS; /* no look-ahead token */ @@ -397,7 +396,7 @@ public class LexState { void read_numeral(SemInfo seminfo) { String expo = "Ee"; int first = current; - LuaC._assert (isdigit(current)); + _assert (isdigit(current)); save_and_next(); if (first == '0' && check_next("Xx")) expo = "Pp"; @@ -417,7 +416,7 @@ public class LexState { int skip_sep() { int count = 0; int s = current; - LuaC._assert (s == '[' || s == ']'); + _assert (s == '[' || s == ']'); save_and_next(); while (current == '=') { save_and_next(); @@ -690,7 +689,7 @@ public class LexState { } default: { if (isspace(current)) { - LuaC._assert (!currIsNewline()); + _assert (!currIsNewline()); nextChar(); continue; } else if (isdigit(current)) { @@ -729,7 +728,7 @@ public class LexState { } void lookahead() { - LuaC._assert (lookahead.token == TK_EOS); + _assert (lookahead.token == TK_EOS); lookahead.token = llex(lookahead.seminfo); } @@ -841,7 +840,7 @@ public class LexState { void anchor_token () { /* last token from outer function must be EOS */ - LuaC._assert(fs != null || t.token == TK_EOS); + _assert(fs != null || t.token == TK_EOS); if (t.token == TK_NAME || t.token == TK_STRING) { LuaString ts = t.seminfo.ts; // TODO: is this necessary? @@ -916,7 +915,7 @@ public class LexState { FuncState fs = this.fs; Prototype f = fs.f; if (f.locvars == null || fs.nlocvars + 1 > f.locvars.length) - f.locvars = LuaC.realloc( f.locvars, fs.nlocvars*2+1 ); + f.locvars = realloc( f.locvars, fs.nlocvars*2+1 ); f.locvars[fs.nlocvars] = new LocVars(varname,0,0); return fs.nlocvars++; } @@ -925,7 +924,7 @@ public class LexState { int reg = registerlocalvar(name); fs.checklimit(dyd.n_actvar + 1, FuncState.LUAI_MAXVARS, "local variables"); if (dyd.actvar == null || dyd.n_actvar + 1 > dyd.actvar.length) - dyd.actvar = LuaC.realloc(dyd.actvar, Math.max(1, dyd.n_actvar * 2)); + dyd.actvar = realloc(dyd.actvar, Math.max(1, dyd.n_actvar * 2)); dyd.actvar[dyd.n_actvar++] = new Vardesc(reg); } @@ -954,7 +953,7 @@ public class LexState { if (FuncState.singlevaraux(fs, varname, var, 1) == VVOID) { /* global name? */ expdesc key = new expdesc(); FuncState.singlevaraux(fs, this.envn, var, 1); /* get environment variable */ - LuaC._assert(var.k == VLOCAL || var.k == VUPVAL); + _assert(var.k == VLOCAL || var.k == VUPVAL); this.codestring(key, varname); /* key is variable name */ fs.indexed(var, key); /* env[varname] */ } @@ -997,7 +996,7 @@ public class LexState { FuncState fs = this.fs; Labeldesc[] gl = this.dyd.gt; Labeldesc gt = gl[g]; - LuaC._assert(gt.name.eq_b(label.name)); + _assert(gt.name.eq_b(label.name)); if (gt.nactvar < label.nactvar) { LuaString vname = fs.getlocvar(gt.nactvar).varname; String msg = L.pushfstring(" at line " @@ -1033,7 +1032,7 @@ public class LexState { return false; /* label not found; cannot close goto */ } - /* Caller must LuaC.grow() the vector before calling this. */ + /* Caller must grow() the vector before calling this. */ int newlabelentry(Labeldesc[] l, int index, LuaString name, int line, int pc) { l[index] = new Labeldesc(name, pc, line, fs.nactvar); return index; @@ -1060,7 +1059,7 @@ public class LexState { */ void breaklabel () { LuaString n = LuaString.valueOf("break"); - int l = newlabelentry(dyd.label=LuaC.grow(dyd.label, dyd.n_label+1), dyd.n_label++, n, 0, fs.pc); + int l = newlabelentry(dyd.label=grow(dyd.label, dyd.n_label+1), dyd.n_label++, n, 0, fs.pc); findgotos(dyd.label[l]); } @@ -1079,7 +1078,7 @@ public class LexState { Prototype clp; Prototype f = fs.f; /* prototype of current function */ if (f.p == null || fs.np >= f.p.length) { - f.p = LuaC.realloc(f.p, Math.max(1, fs.np * 2)); + f.p = realloc(f.p, Math.max(1, fs.np * 2)); } f.p[fs.np++] = clp = new Prototype(); return clp; @@ -1087,7 +1086,7 @@ public class LexState { void codeclosure (expdesc v) { FuncState fs = this.fs.prev; - v.init(VRELOCABLE, fs.codeABx(LuaC.OP_CLOSURE, 0, fs.np - 1)); + v.init(VRELOCABLE, fs.codeABx(OP_CLOSURE, 0, fs.np - 1)); fs.exp2nextreg(v); /* fix it at stack top (for GC) */ } @@ -1116,13 +1115,13 @@ public class LexState { Prototype f = fs.f; fs.ret(0, 0); /* final return */ fs.leaveblock(); - f.code = LuaC.realloc(f.code, fs.pc); - f.lineinfo = LuaC.realloc(f.lineinfo, fs.pc); - f.k = LuaC.realloc(f.k, fs.nk); - f.p = LuaC.realloc(f.p, fs.np); - f.locvars = LuaC.realloc(f.locvars, fs.nlocvars); - f.upvalues = LuaC.realloc(f.upvalues, fs.nups); - LuaC._assert (fs.bl == null); + f.code = realloc(f.code, fs.pc); + f.lineinfo = realloc(f.lineinfo, fs.pc); + f.k = realloc(f.k, fs.nk); + f.p = realloc(f.p, fs.np); + f.locvars = realloc(f.locvars, fs.nlocvars); + f.upvalues = realloc(f.upvalues, fs.nups); + _assert (fs.bl == null); this.fs = fs.prev; // last token read was anchored in defunct function; must reanchor it // ls.anchor_token(); @@ -1209,7 +1208,7 @@ public class LexState { fs.exp2nextreg(t); /* fix it at stack top (for gc) */ this.checknext('{'); do { - LuaC._assert (cc.v.k == VVOID || cc.tostore > 0); + _assert (cc.v.k == VVOID || cc.tostore > 0); if (this.t.token == '}') break; fs.closelistfield(cc); @@ -1235,8 +1234,8 @@ public class LexState { this.check_match('}', '{', line); fs.lastlistfield(cc); InstructionPtr i = new InstructionPtr(fs.f.code, pc); - LuaC.SETARG_B(i, luaO_int2fb(cc.na)); /* set initial array size */ - LuaC.SETARG_C(i, luaO_int2fb(cc.nh)); /* set initial table size */ + SETARG_B(i, luaO_int2fb(cc.na)); /* set initial array size */ + SETARG_C(i, luaO_int2fb(cc.nh)); /* set initial table size */ } /* @@ -1350,7 +1349,7 @@ public class LexState { return; } } - LuaC._assert (f.k == VNONRELOC); + _assert (f.k == VNONRELOC); base = f.u.info; /* base register for call */ if (hasmultret(args.k)) nparams = Lua.LUA_MULTRET; /* open call */ @@ -1727,7 +1726,7 @@ public class LexState { next(); /* skip break */ label = LuaString.valueOf("break"); } - g = newlabelentry(dyd.gt =LuaC.grow(dyd.gt, dyd.n_gt+1), dyd.n_gt++, label, line, pc); + g = newlabelentry(dyd.gt =grow(dyd.gt, dyd.n_gt+1), dyd.n_gt++, label, line, pc); findlabel(g); /* close it if label already defined */ } @@ -1745,7 +1744,7 @@ public class LexState { fs.checkrepeated(dyd.label, dyd.n_label, label); /* check for repeated labels */ checknext(TK_DBCOLON); /* skip double colon */ /* create new entry for this label */ - l = newlabelentry(dyd.label=LuaC.grow(dyd.label, dyd.n_label+1), dyd.n_label++, label, line, fs.pc); + l = newlabelentry(dyd.label=grow(dyd.label, dyd.n_label+1), dyd.n_label++, label, line, fs.pc); skipnoopstat(); /* skip other no-op statements */ if (block_follow(false)) { /* label is last no-op statement in the block? */ /* assume that locals are already out of scope */ @@ -2014,7 +2013,7 @@ public class LexState { } else { /* stat -> func */ check_condition(v.v.k == VCALL, "syntax error"); - LuaC.SETARG_C(fs.getcodePtr(v.v), 1); /* call statement uses no results */ + SETARG_C(fs.getcodePtr(v.v), 1); /* call statement uses no results */ } } @@ -2030,8 +2029,8 @@ public class LexState { if (hasmultret(e.k)) { fs.setmultret(e); if (e.k == VCALL && nret == 1) { /* tail call? */ - LuaC.SET_OPCODE(fs.getcodePtr(e), Lua.OP_TAILCALL); - LuaC._assert (Lua.GETARG_A(fs.getcode(e)) == fs.nactvar); + SET_OPCODE(fs.getcodePtr(e), Lua.OP_TAILCALL); + _assert (Lua.GETARG_A(fs.getcode(e)) == fs.nactvar); } first = fs.nactvar; nret = Lua.LUA_MULTRET; /* return all values */ @@ -2041,7 +2040,7 @@ public class LexState { else { fs.exp2nextreg(e); /* values must go to the `stack' */ first = fs.nactvar; /* return all `active' values */ - LuaC._assert (nret == fs.freereg - first); + _assert (nret == fs.freereg - first); } } } @@ -2111,7 +2110,7 @@ public class LexState { break; } } - LuaC._assert(fs.f.maxstacksize >= fs.freereg + _assert(fs.f.maxstacksize >= fs.freereg && fs.freereg >= fs.nactvar); fs.freereg = fs.nactvar; /* free registers */ leavelevel(); diff --git a/src/core/org/luaj/vm2/compiler/LuaC.java b/src/core/org/luaj/vm2/compiler/LuaC.java index da5f959f..d255cd6a 100644 --- a/src/core/org/luaj/vm2/compiler/LuaC.java +++ b/src/core/org/luaj/vm2/compiler/LuaC.java @@ -26,15 +26,11 @@ import java.io.InputStream; import java.util.Hashtable; import org.luaj.vm2.Globals; -import org.luaj.vm2.LocVars; -import org.luaj.vm2.Lua; import org.luaj.vm2.LuaClosure; -import org.luaj.vm2.LuaError; import org.luaj.vm2.LuaFunction; import org.luaj.vm2.LuaString; import org.luaj.vm2.LuaValue; import org.luaj.vm2.Prototype; -import org.luaj.vm2.Upvaldesc; import org.luaj.vm2.lib.BaseLib; /** @@ -77,7 +73,7 @@ import org.luaj.vm2.lib.BaseLib; * @see LuaCompiler * @see Prototype */ -public class LuaC extends Lua implements Globals.Compiler, Globals.Loader { +public class LuaC extends Constants implements Globals.Compiler, Globals.Loader { /** A sharable instance of the LuaC compiler. */ public static final LuaC instance = new LuaC(); @@ -92,156 +88,7 @@ public class LuaC extends Lua implements Globals.Compiler, Globals.Loader { globals.loader = instance; } - protected static void _assert(boolean b) { - if (!b) - throw new LuaError("compiler assert failed"); - } - - public static final int MAXSTACK = 250; - static final int LUAI_MAXUPVAL = 0xff; - static final int LUAI_MAXVARS = 200; - static final int NO_REG = MAXARG_A; - - - /* OpMode - basic instruction format */ - static final int - iABC = 0, - iABx = 1, - iAsBx = 2; - - /* OpArgMask */ - static final int - OpArgN = 0, /* argument is not used */ - OpArgU = 1, /* argument is used */ - OpArgR = 2, /* argument is a register or a jump offset */ - OpArgK = 3; /* argument is a constant or register/constant */ - - - static void SET_OPCODE(InstructionPtr i,int o) { - i.set( ( i.get() & (MASK_NOT_OP)) | ((o << POS_OP) & MASK_OP) ); - } - - static void SETARG_A(int[] code, int index, int u) { - code[index] = (code[index] & (MASK_NOT_A)) | ((u << POS_A) & MASK_A); - } - - static void SETARG_A(InstructionPtr i,int u) { - i.set( ( i.get() & (MASK_NOT_A)) | ((u << POS_A) & MASK_A) ); - } - - static void SETARG_B(InstructionPtr i,int u) { - i.set( ( i.get() & (MASK_NOT_B)) | ((u << POS_B) & MASK_B) ); - } - - static void SETARG_C(InstructionPtr i,int u) { - i.set( ( i.get() & (MASK_NOT_C)) | ((u << POS_C) & MASK_C) ); - } - - static void SETARG_Bx(InstructionPtr i,int u) { - i.set( ( i.get() & (MASK_NOT_Bx)) | ((u << POS_Bx) & MASK_Bx) ); - } - - static void SETARG_sBx(InstructionPtr i,int u) { - SETARG_Bx( i, u + MAXARG_sBx ); - } - - static int CREATE_ABC(int o, int a, int b, int c) { - return ((o << POS_OP) & MASK_OP) | - ((a << POS_A) & MASK_A) | - ((b << POS_B) & MASK_B) | - ((c << POS_C) & MASK_C) ; - } - - static int CREATE_ABx(int o, int a, int bc) { - return ((o << POS_OP) & MASK_OP) | - ((a << POS_A) & MASK_A) | - ((bc << POS_Bx) & MASK_Bx) ; - } - - // vector reallocation - - static LuaValue[] realloc(LuaValue[] v, int n) { - LuaValue[] a = new LuaValue[n]; - if ( v != null ) - System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); - return a; - } - - static Prototype[] realloc(Prototype[] v, int n) { - Prototype[] a = new Prototype[n]; - if ( v != null ) - System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); - return a; - } - - static LuaString[] realloc(LuaString[] v, int n) { - LuaString[] a = new LuaString[n]; - if ( v != null ) - System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); - return a; - } - - static LocVars[] realloc(LocVars[] v, int n) { - LocVars[] a = new LocVars[n]; - if ( v != null ) - System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); - return a; - } - - static Upvaldesc[] realloc(Upvaldesc[] v, int n) { - Upvaldesc[] a = new Upvaldesc[n]; - if ( v != null ) - System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); - return a; - } - - static LexState.Vardesc[] realloc(LexState.Vardesc[] v, int n) { - LexState.Vardesc[] a = new LexState.Vardesc[n]; - if ( v != null ) - System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); - return a; - } - - static LexState.Labeldesc[] grow(LexState.Labeldesc[] v, int min_n) { - return v == null ? new LexState.Labeldesc[2] : v.length < min_n ? realloc(v, v.length*2) : v; - } - - static LexState.Labeldesc[] realloc(LexState.Labeldesc[] v, int n) { - LexState.Labeldesc[] a = new LexState.Labeldesc[n]; - if ( v != null ) - System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); - return a; - } - - static int[] realloc(int[] v, int n) { - int[] a = new int[n]; - if ( v != null ) - System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); - return a; - } - - static byte[] realloc(byte[] v, int n) { - byte[] a = new byte[n]; - if ( v != null ) - System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); - return a; - } - - static char[] realloc(char[] v, int n) { - char[] a = new char[n]; - if ( v != null ) - System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); - return a; - } - - public int nCcalls; - Hashtable strings; - protected LuaC() {} - - private LuaC(Hashtable strings) { - this.strings = strings; - } /** Compile lua source into a Prototype. * @param stream InputStream representing the text source conforming to lua source syntax. @@ -250,59 +97,64 @@ public class LuaC extends Lua implements Globals.Compiler, Globals.Loader { * @throws IOException */ public Prototype compile(InputStream stream, String chunkname) throws IOException { - return (new LuaC(new Hashtable())).luaY_parser(stream, chunkname); - } - - /** @deprecated - * Use Globals.load(InputString, String, String) instead, - * or LuaC.compil(InputStream, String) and construct LuaClosure directly. - */ - public LuaValue load(InputStream stream, String chunkname, Globals globals) throws IOException { - return new LuaClosure(compile(stream, chunkname), globals); - } - - - /** Parse the input */ - private Prototype luaY_parser(InputStream z, String name) throws IOException{ - LexState lexstate = new LexState(this, z); - FuncState funcstate = new FuncState(); - // lexstate.buff = buff; - lexstate.fs = funcstate; - lexstate.setinput( this, z.read(), z, (LuaString) LuaValue.valueOf(name) ); - /* main func. is always vararg */ - funcstate.f = new Prototype(); - funcstate.f.source = (LuaString) LuaValue.valueOf(name); - lexstate.mainfunc(funcstate); - LuaC._assert (funcstate.prev == null); - /* all scopes should be correctly finished */ - LuaC._assert (lexstate.dyd == null - || (lexstate.dyd.n_actvar == 0 && lexstate.dyd.n_gt == 0 && lexstate.dyd.n_label == 0)); - return funcstate.f; - } - - // look up and keep at most one copy of each string - public LuaString newTString(String s) { - return cachedLuaString(LuaString.valueOf(s)); - } - - // look up and keep at most one copy of each string - public LuaString newTString(LuaString s) { - return cachedLuaString(s); - } - - public LuaString cachedLuaString(LuaString s) { - LuaString c = (LuaString) strings.get(s); - if (c != null) - return c; - strings.put(s, s); - return s; - } - - public String pushfstring(String string) { - return string; + return (new CompileState()).luaY_parser(stream, chunkname); } public LuaFunction load(Prototype prototype, String chunkname, LuaValue env) throws IOException { return new LuaClosure(prototype, env); } + + /** @deprecated + * Use Globals.load(InputString, String, String) instead, + * or LuaC.compile(InputStream, String) and construct LuaClosure directly. + */ + public LuaValue load(InputStream stream, String chunkname, Globals globals) throws IOException { + return new LuaClosure(compile(stream, chunkname), globals); + } + + static class CompileState { + int nCcalls = 0; + private Hashtable strings = new Hashtable(); + protected CompileState() {} + + /** Parse the input */ + private Prototype luaY_parser(InputStream z, String name) throws IOException{ + LexState lexstate = new LexState(this, z); + FuncState funcstate = new FuncState(); + // lexstate.buff = buff; + lexstate.fs = funcstate; + lexstate.setinput(this, z.read(), z, (LuaString) LuaValue.valueOf(name) ); + /* main func. is always vararg */ + funcstate.f = new Prototype(); + funcstate.f.source = (LuaString) LuaValue.valueOf(name); + lexstate.mainfunc(funcstate); + LuaC._assert (funcstate.prev == null); + /* all scopes should be correctly finished */ + LuaC._assert (lexstate.dyd == null + || (lexstate.dyd.n_actvar == 0 && lexstate.dyd.n_gt == 0 && lexstate.dyd.n_label == 0)); + return funcstate.f; + } + + // look up and keep at most one copy of each string + public LuaString newTString(String s) { + return cachedLuaString(LuaString.valueOf(s)); + } + + // look up and keep at most one copy of each string + public LuaString newTString(LuaString s) { + return cachedLuaString(s); + } + + public LuaString cachedLuaString(LuaString s) { + LuaString c = (LuaString) strings.get(s); + if (c != null) + return c; + strings.put(s, s); + return s; + } + + public String pushfstring(String string) { + return string; + } + } } diff --git a/src/core/org/luaj/vm2/lib/IoLib.java b/src/core/org/luaj/vm2/lib/IoLib.java index 68319b9c..5913c049 100644 --- a/src/core/org/luaj/vm2/lib/IoLib.java +++ b/src/core/org/luaj/vm2/lib/IoLib.java @@ -557,7 +557,7 @@ public class IoLib extends TwoArgFunction { int r; if ( ( r = f.read(b,0,b.length) ) < 0 ) return NIL; - return LuaString.valueOf(b, 0, r); + return LuaString.valueUsing(b, 0, r); } public static LuaValue freaduntil(File f,boolean lineonly) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -580,7 +580,7 @@ public class IoLib extends TwoArgFunction { } return ( c < 0 && baos.size() == 0 )? (LuaValue) NIL: - (LuaValue) LuaString.valueOf(baos.toByteArray()); + (LuaValue) LuaString.valueUsing(baos.toByteArray()); } public static LuaValue freadline(File f) throws IOException { return freaduntil(f,true); diff --git a/src/core/org/luaj/vm2/lib/StringLib.java b/src/core/org/luaj/vm2/lib/StringLib.java index f92a99b5..80f47377 100644 --- a/src/core/org/luaj/vm2/lib/StringLib.java +++ b/src/core/org/luaj/vm2/lib/StringLib.java @@ -160,7 +160,7 @@ public class StringLib extends TwoArgFunction { if (c<0 || c>=256) argerror(a, "invalid value"); bytes[i] = (byte) c; } - return LuaString.valueOf( bytes ); + return LuaString.valueUsing( bytes ); } /** @@ -177,7 +177,7 @@ public class StringLib extends TwoArgFunction { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { DumpState.dump( ((LuaClosure)f).p, baos, true ); - return LuaString.valueOf(baos.toByteArray()); + return LuaString.valueUsing(baos.toByteArray()); } catch (IOException e) { return error( e.getMessage() ); } @@ -658,7 +658,7 @@ public class StringLib extends TwoArgFunction { for ( int offset = 0; offset < bytes.length; offset += len ) { s.copyInto( 0, bytes, offset, len ); } - return LuaString.valueOf( bytes ); + return LuaString.valueUsing( bytes ); } /** @@ -672,7 +672,7 @@ public class StringLib extends TwoArgFunction { byte[] b = new byte[n]; for ( int i=0, j=n-1; i