mirror of
https://github.com/LuaJIT/LuaJIT.git
synced 2025-02-07 23:24:09 +00:00
81a797373f
Note: this is not exploitable! minilua is only used during the LuaJIT build process. It only runs controlled and static Lua code (DynASM), which is entirely contained within this repo. LuaJIT itself has completely different stack handling code and is not affected either. This change is solely for the benefit of others, who might possibly use minilua for purposes other than running DynASM.
7771 lines
170 KiB
C
7771 lines
170 KiB
C
/* This is a heavily customized and minimized copy of Lua 5.1.5. */
|
|
/* It's only used to build LuaJIT. It does NOT have all standard functions! */
|
|
/******************************************************************************
|
|
* Copyright (C) 1994-2012 Lua.org, PUC-Rio. 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.
|
|
******************************************************************************/
|
|
#ifdef _MSC_VER
|
|
typedef unsigned __int64 U64;
|
|
#else
|
|
typedef unsigned long long U64;
|
|
#endif
|
|
int _CRT_glob = 0;
|
|
#include <stddef.h>
|
|
#include <stdarg.h>
|
|
#include <limits.h>
|
|
#include <math.h>
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <setjmp.h>
|
|
#include <errno.h>
|
|
#include <time.h>
|
|
typedef enum{
|
|
TM_INDEX,
|
|
TM_NEWINDEX,
|
|
TM_GC,
|
|
TM_MODE,
|
|
TM_EQ,
|
|
TM_ADD,
|
|
TM_SUB,
|
|
TM_MUL,
|
|
TM_DIV,
|
|
TM_MOD,
|
|
TM_POW,
|
|
TM_UNM,
|
|
TM_LEN,
|
|
TM_LT,
|
|
TM_LE,
|
|
TM_CONCAT,
|
|
TM_CALL,
|
|
TM_N
|
|
}TMS;
|
|
enum OpMode{iABC,iABx,iAsBx};
|
|
typedef enum{
|
|
OP_MOVE,
|
|
OP_LOADK,
|
|
OP_LOADBOOL,
|
|
OP_LOADNIL,
|
|
OP_GETUPVAL,
|
|
OP_GETGLOBAL,
|
|
OP_GETTABLE,
|
|
OP_SETGLOBAL,
|
|
OP_SETUPVAL,
|
|
OP_SETTABLE,
|
|
OP_NEWTABLE,
|
|
OP_SELF,
|
|
OP_ADD,
|
|
OP_SUB,
|
|
OP_MUL,
|
|
OP_DIV,
|
|
OP_MOD,
|
|
OP_POW,
|
|
OP_UNM,
|
|
OP_NOT,
|
|
OP_LEN,
|
|
OP_CONCAT,
|
|
OP_JMP,
|
|
OP_EQ,
|
|
OP_LT,
|
|
OP_LE,
|
|
OP_TEST,
|
|
OP_TESTSET,
|
|
OP_CALL,
|
|
OP_TAILCALL,
|
|
OP_RETURN,
|
|
OP_FORLOOP,
|
|
OP_FORPREP,
|
|
OP_TFORLOOP,
|
|
OP_SETLIST,
|
|
OP_CLOSE,
|
|
OP_CLOSURE,
|
|
OP_VARARG
|
|
}OpCode;
|
|
enum OpArgMask{
|
|
OpArgN,
|
|
OpArgU,
|
|
OpArgR,
|
|
OpArgK
|
|
};
|
|
typedef enum{
|
|
VVOID,
|
|
VNIL,
|
|
VTRUE,
|
|
VFALSE,
|
|
VK,
|
|
VKNUM,
|
|
VLOCAL,
|
|
VUPVAL,
|
|
VGLOBAL,
|
|
VINDEXED,
|
|
VJMP,
|
|
VRELOCABLE,
|
|
VNONRELOC,
|
|
VCALL,
|
|
VVARARG
|
|
}expkind;
|
|
enum RESERVED{
|
|
TK_AND=257,TK_BREAK,
|
|
TK_DO,TK_ELSE,TK_ELSEIF,TK_END,TK_FALSE,TK_FOR,TK_FUNCTION,
|
|
TK_IF,TK_IN,TK_LOCAL,TK_NIL,TK_NOT,TK_OR,TK_REPEAT,
|
|
TK_RETURN,TK_THEN,TK_TRUE,TK_UNTIL,TK_WHILE,
|
|
TK_CONCAT,TK_DOTS,TK_EQ,TK_GE,TK_LE,TK_NE,TK_NUMBER,
|
|
TK_NAME,TK_STRING,TK_EOS
|
|
};
|
|
typedef enum BinOpr{
|
|
OPR_ADD,OPR_SUB,OPR_MUL,OPR_DIV,OPR_MOD,OPR_POW,
|
|
OPR_CONCAT,
|
|
OPR_NE,OPR_EQ,
|
|
OPR_LT,OPR_LE,OPR_GT,OPR_GE,
|
|
OPR_AND,OPR_OR,
|
|
OPR_NOBINOPR
|
|
}BinOpr;
|
|
typedef enum UnOpr{OPR_MINUS,OPR_NOT,OPR_LEN,OPR_NOUNOPR}UnOpr;
|
|
#define LUA_QL(x)"'"x"'"
|
|
#define luai_apicheck(L,o){(void)L;}
|
|
#define lua_number2str(s,n)sprintf((s),"%.14g",(n))
|
|
#define lua_str2number(s,p)strtod((s),(p))
|
|
#define luai_numadd(a,b)((a)+(b))
|
|
#define luai_numsub(a,b)((a)-(b))
|
|
#define luai_nummul(a,b)((a)*(b))
|
|
#define luai_numdiv(a,b)((a)/(b))
|
|
#define luai_nummod(a,b)((a)-floor((a)/(b))*(b))
|
|
#define luai_numpow(a,b)(pow(a,b))
|
|
#define luai_numunm(a)(-(a))
|
|
#define luai_numeq(a,b)((a)==(b))
|
|
#define luai_numlt(a,b)((a)<(b))
|
|
#define luai_numle(a,b)((a)<=(b))
|
|
#define luai_numisnan(a)(!luai_numeq((a),(a)))
|
|
#define lua_number2int(i,d)((i)=(int)(d))
|
|
#define lua_number2integer(i,d)((i)=(lua_Integer)(d))
|
|
#define LUAI_THROW(L,c)longjmp((c)->b,1)
|
|
#define LUAI_TRY(L,c,a)if(setjmp((c)->b)==0){a}
|
|
#define lua_pclose(L,file)((void)((void)L,file),0)
|
|
#define lua_upvalueindex(i)((-10002)-(i))
|
|
typedef struct lua_State lua_State;
|
|
typedef int(*lua_CFunction)(lua_State*L);
|
|
typedef const char*(*lua_Reader)(lua_State*L,void*ud,size_t*sz);
|
|
typedef void*(*lua_Alloc)(void*ud,void*ptr,size_t osize,size_t nsize);
|
|
typedef double lua_Number;
|
|
typedef ptrdiff_t lua_Integer;
|
|
static void lua_settop(lua_State*L,int idx);
|
|
static int lua_type(lua_State*L,int idx);
|
|
static const char* lua_tolstring(lua_State*L,int idx,size_t*len);
|
|
static size_t lua_objlen(lua_State*L,int idx);
|
|
static void lua_pushlstring(lua_State*L,const char*s,size_t l);
|
|
static void lua_pushcclosure(lua_State*L,lua_CFunction fn,int n);
|
|
static void lua_createtable(lua_State*L,int narr,int nrec);
|
|
static void lua_setfield(lua_State*L,int idx,const char*k);
|
|
#define lua_pop(L,n)lua_settop(L,-(n)-1)
|
|
#define lua_newtable(L)lua_createtable(L,0,0)
|
|
#define lua_pushcfunction(L,f)lua_pushcclosure(L,(f),0)
|
|
#define lua_strlen(L,i)lua_objlen(L,(i))
|
|
#define lua_isfunction(L,n)(lua_type(L,(n))==6)
|
|
#define lua_istable(L,n)(lua_type(L,(n))==5)
|
|
#define lua_isnil(L,n)(lua_type(L,(n))==0)
|
|
#define lua_isboolean(L,n)(lua_type(L,(n))==1)
|
|
#define lua_isnone(L,n)(lua_type(L,(n))==(-1))
|
|
#define lua_isnoneornil(L,n)(lua_type(L,(n))<=0)
|
|
#define lua_pushliteral(L,s)lua_pushlstring(L,""s,(sizeof(s)/sizeof(char))-1)
|
|
#define lua_setglobal(L,s)lua_setfield(L,(-10002),(s))
|
|
#define lua_tostring(L,i)lua_tolstring(L,(i),NULL)
|
|
typedef struct lua_Debug lua_Debug;
|
|
typedef void(*lua_Hook)(lua_State*L,lua_Debug*ar);
|
|
struct lua_Debug{
|
|
int event;
|
|
const char*name;
|
|
const char*namewhat;
|
|
const char*what;
|
|
const char*source;
|
|
int currentline;
|
|
int nups;
|
|
int linedefined;
|
|
int lastlinedefined;
|
|
char short_src[60];
|
|
int i_ci;
|
|
};
|
|
typedef unsigned int lu_int32;
|
|
typedef size_t lu_mem;
|
|
typedef ptrdiff_t l_mem;
|
|
typedef unsigned char lu_byte;
|
|
#define IntPoint(p)((unsigned int)(lu_mem)(p))
|
|
typedef union{double u;void*s;long l;}L_Umaxalign;
|
|
typedef double l_uacNumber;
|
|
#define check_exp(c,e)(e)
|
|
#define UNUSED(x)((void)(x))
|
|
#define cast(t,exp)((t)(exp))
|
|
#define cast_byte(i)cast(lu_byte,(i))
|
|
#define cast_num(i)cast(lua_Number,(i))
|
|
#define cast_int(i)cast(int,(i))
|
|
typedef lu_int32 Instruction;
|
|
#define condhardstacktests(x)((void)0)
|
|
typedef union GCObject GCObject;
|
|
typedef struct GCheader{
|
|
GCObject*next;lu_byte tt;lu_byte marked;
|
|
}GCheader;
|
|
typedef union{
|
|
GCObject*gc;
|
|
void*p;
|
|
lua_Number n;
|
|
int b;
|
|
}Value;
|
|
typedef struct lua_TValue{
|
|
Value value;int tt;
|
|
}TValue;
|
|
#define ttisnil(o)(ttype(o)==0)
|
|
#define ttisnumber(o)(ttype(o)==3)
|
|
#define ttisstring(o)(ttype(o)==4)
|
|
#define ttistable(o)(ttype(o)==5)
|
|
#define ttisfunction(o)(ttype(o)==6)
|
|
#define ttisboolean(o)(ttype(o)==1)
|
|
#define ttisuserdata(o)(ttype(o)==7)
|
|
#define ttisthread(o)(ttype(o)==8)
|
|
#define ttislightuserdata(o)(ttype(o)==2)
|
|
#define ttype(o)((o)->tt)
|
|
#define gcvalue(o)check_exp(iscollectable(o),(o)->value.gc)
|
|
#define pvalue(o)check_exp(ttislightuserdata(o),(o)->value.p)
|
|
#define nvalue(o)check_exp(ttisnumber(o),(o)->value.n)
|
|
#define rawtsvalue(o)check_exp(ttisstring(o),&(o)->value.gc->ts)
|
|
#define tsvalue(o)(&rawtsvalue(o)->tsv)
|
|
#define rawuvalue(o)check_exp(ttisuserdata(o),&(o)->value.gc->u)
|
|
#define uvalue(o)(&rawuvalue(o)->uv)
|
|
#define clvalue(o)check_exp(ttisfunction(o),&(o)->value.gc->cl)
|
|
#define hvalue(o)check_exp(ttistable(o),&(o)->value.gc->h)
|
|
#define bvalue(o)check_exp(ttisboolean(o),(o)->value.b)
|
|
#define thvalue(o)check_exp(ttisthread(o),&(o)->value.gc->th)
|
|
#define l_isfalse(o)(ttisnil(o)||(ttisboolean(o)&&bvalue(o)==0))
|
|
#define checkconsistency(obj)
|
|
#define checkliveness(g,obj)
|
|
#define setnilvalue(obj)((obj)->tt=0)
|
|
#define setnvalue(obj,x){TValue*i_o=(obj);i_o->value.n=(x);i_o->tt=3;}
|
|
#define setbvalue(obj,x){TValue*i_o=(obj);i_o->value.b=(x);i_o->tt=1;}
|
|
#define setsvalue(L,obj,x){TValue*i_o=(obj);i_o->value.gc=cast(GCObject*,(x));i_o->tt=4;checkliveness(G(L),i_o);}
|
|
#define setuvalue(L,obj,x){TValue*i_o=(obj);i_o->value.gc=cast(GCObject*,(x));i_o->tt=7;checkliveness(G(L),i_o);}
|
|
#define setthvalue(L,obj,x){TValue*i_o=(obj);i_o->value.gc=cast(GCObject*,(x));i_o->tt=8;checkliveness(G(L),i_o);}
|
|
#define setclvalue(L,obj,x){TValue*i_o=(obj);i_o->value.gc=cast(GCObject*,(x));i_o->tt=6;checkliveness(G(L),i_o);}
|
|
#define sethvalue(L,obj,x){TValue*i_o=(obj);i_o->value.gc=cast(GCObject*,(x));i_o->tt=5;checkliveness(G(L),i_o);}
|
|
#define setptvalue(L,obj,x){TValue*i_o=(obj);i_o->value.gc=cast(GCObject*,(x));i_o->tt=(8+1);checkliveness(G(L),i_o);}
|
|
#define setobj(L,obj1,obj2){const TValue*o2=(obj2);TValue*o1=(obj1);o1->value=o2->value;o1->tt=o2->tt;checkliveness(G(L),o1);}
|
|
#define setttype(obj,tt)(ttype(obj)=(tt))
|
|
#define iscollectable(o)(ttype(o)>=4)
|
|
typedef TValue*StkId;
|
|
typedef union TString{
|
|
L_Umaxalign dummy;
|
|
struct{
|
|
GCObject*next;lu_byte tt;lu_byte marked;
|
|
lu_byte reserved;
|
|
unsigned int hash;
|
|
size_t len;
|
|
}tsv;
|
|
}TString;
|
|
#define getstr(ts)cast(const char*,(ts)+1)
|
|
#define svalue(o)getstr(rawtsvalue(o))
|
|
typedef union Udata{
|
|
L_Umaxalign dummy;
|
|
struct{
|
|
GCObject*next;lu_byte tt;lu_byte marked;
|
|
struct Table*metatable;
|
|
struct Table*env;
|
|
size_t len;
|
|
}uv;
|
|
}Udata;
|
|
typedef struct Proto{
|
|
GCObject*next;lu_byte tt;lu_byte marked;
|
|
TValue*k;
|
|
Instruction*code;
|
|
struct Proto**p;
|
|
int*lineinfo;
|
|
struct LocVar*locvars;
|
|
TString**upvalues;
|
|
TString*source;
|
|
int sizeupvalues;
|
|
int sizek;
|
|
int sizecode;
|
|
int sizelineinfo;
|
|
int sizep;
|
|
int sizelocvars;
|
|
int linedefined;
|
|
int lastlinedefined;
|
|
GCObject*gclist;
|
|
lu_byte nups;
|
|
lu_byte numparams;
|
|
lu_byte is_vararg;
|
|
lu_byte maxstacksize;
|
|
}Proto;
|
|
typedef struct LocVar{
|
|
TString*varname;
|
|
int startpc;
|
|
int endpc;
|
|
}LocVar;
|
|
typedef struct UpVal{
|
|
GCObject*next;lu_byte tt;lu_byte marked;
|
|
TValue*v;
|
|
union{
|
|
TValue value;
|
|
struct{
|
|
struct UpVal*prev;
|
|
struct UpVal*next;
|
|
}l;
|
|
}u;
|
|
}UpVal;
|
|
typedef struct CClosure{
|
|
GCObject*next;lu_byte tt;lu_byte marked;lu_byte isC;lu_byte nupvalues;GCObject*gclist;struct Table*env;
|
|
lua_CFunction f;
|
|
TValue upvalue[1];
|
|
}CClosure;
|
|
typedef struct LClosure{
|
|
GCObject*next;lu_byte tt;lu_byte marked;lu_byte isC;lu_byte nupvalues;GCObject*gclist;struct Table*env;
|
|
struct Proto*p;
|
|
UpVal*upvals[1];
|
|
}LClosure;
|
|
typedef union Closure{
|
|
CClosure c;
|
|
LClosure l;
|
|
}Closure;
|
|
#define iscfunction(o)(ttype(o)==6&&clvalue(o)->c.isC)
|
|
typedef union TKey{
|
|
struct{
|
|
Value value;int tt;
|
|
struct Node*next;
|
|
}nk;
|
|
TValue tvk;
|
|
}TKey;
|
|
typedef struct Node{
|
|
TValue i_val;
|
|
TKey i_key;
|
|
}Node;
|
|
typedef struct Table{
|
|
GCObject*next;lu_byte tt;lu_byte marked;
|
|
lu_byte flags;
|
|
lu_byte lsizenode;
|
|
struct Table*metatable;
|
|
TValue*array;
|
|
Node*node;
|
|
Node*lastfree;
|
|
GCObject*gclist;
|
|
int sizearray;
|
|
}Table;
|
|
#define lmod(s,size)(check_exp((size&(size-1))==0,(cast(int,(s)&((size)-1)))))
|
|
#define twoto(x)((size_t)1<<(x))
|
|
#define sizenode(t)(twoto((t)->lsizenode))
|
|
static const TValue luaO_nilobject_;
|
|
#define ceillog2(x)(luaO_log2((x)-1)+1)
|
|
static int luaO_log2(unsigned int x);
|
|
#define gfasttm(g,et,e)((et)==NULL?NULL:((et)->flags&(1u<<(e)))?NULL:luaT_gettm(et,e,(g)->tmname[e]))
|
|
#define fasttm(l,et,e)gfasttm(G(l),et,e)
|
|
static const TValue*luaT_gettm(Table*events,TMS event,TString*ename);
|
|
#define luaM_reallocv(L,b,on,n,e)((cast(size_t,(n)+1)<=((size_t)(~(size_t)0)-2)/(e))?luaM_realloc_(L,(b),(on)*(e),(n)*(e)):luaM_toobig(L))
|
|
#define luaM_freemem(L,b,s)luaM_realloc_(L,(b),(s),0)
|
|
#define luaM_free(L,b)luaM_realloc_(L,(b),sizeof(*(b)),0)
|
|
#define luaM_freearray(L,b,n,t)luaM_reallocv(L,(b),n,0,sizeof(t))
|
|
#define luaM_malloc(L,t)luaM_realloc_(L,NULL,0,(t))
|
|
#define luaM_new(L,t)cast(t*,luaM_malloc(L,sizeof(t)))
|
|
#define luaM_newvector(L,n,t)cast(t*,luaM_reallocv(L,NULL,0,n,sizeof(t)))
|
|
#define luaM_growvector(L,v,nelems,size,t,limit,e)if((nelems)+1>(size))((v)=cast(t*,luaM_growaux_(L,v,&(size),sizeof(t),limit,e)))
|
|
#define luaM_reallocvector(L,v,oldn,n,t)((v)=cast(t*,luaM_reallocv(L,v,oldn,n,sizeof(t))))
|
|
static void*luaM_realloc_(lua_State*L,void*block,size_t oldsize,
|
|
size_t size);
|
|
static void*luaM_toobig(lua_State*L);
|
|
static void*luaM_growaux_(lua_State*L,void*block,int*size,
|
|
size_t size_elem,int limit,
|
|
const char*errormsg);
|
|
typedef struct Zio ZIO;
|
|
#define char2int(c)cast(int,cast(unsigned char,(c)))
|
|
#define zgetc(z)(((z)->n--)>0?char2int(*(z)->p++):luaZ_fill(z))
|
|
typedef struct Mbuffer{
|
|
char*buffer;
|
|
size_t n;
|
|
size_t buffsize;
|
|
}Mbuffer;
|
|
#define luaZ_initbuffer(L,buff)((buff)->buffer=NULL,(buff)->buffsize=0)
|
|
#define luaZ_buffer(buff)((buff)->buffer)
|
|
#define luaZ_sizebuffer(buff)((buff)->buffsize)
|
|
#define luaZ_bufflen(buff)((buff)->n)
|
|
#define luaZ_resetbuffer(buff)((buff)->n=0)
|
|
#define luaZ_resizebuffer(L,buff,size)(luaM_reallocvector(L,(buff)->buffer,(buff)->buffsize,size,char),(buff)->buffsize=size)
|
|
#define luaZ_freebuffer(L,buff)luaZ_resizebuffer(L,buff,0)
|
|
struct Zio{
|
|
size_t n;
|
|
const char*p;
|
|
lua_Reader reader;
|
|
void*data;
|
|
lua_State*L;
|
|
};
|
|
static int luaZ_fill(ZIO*z);
|
|
struct lua_longjmp;
|
|
#define gt(L)(&L->l_gt)
|
|
#define registry(L)(&G(L)->l_registry)
|
|
typedef struct stringtable{
|
|
GCObject**hash;
|
|
lu_int32 nuse;
|
|
int size;
|
|
}stringtable;
|
|
typedef struct CallInfo{
|
|
StkId base;
|
|
StkId func;
|
|
StkId top;
|
|
const Instruction*savedpc;
|
|
int nresults;
|
|
int tailcalls;
|
|
}CallInfo;
|
|
#define curr_func(L)(clvalue(L->ci->func))
|
|
#define ci_func(ci)(clvalue((ci)->func))
|
|
#define f_isLua(ci)(!ci_func(ci)->c.isC)
|
|
#define isLua(ci)(ttisfunction((ci)->func)&&f_isLua(ci))
|
|
typedef struct global_State{
|
|
stringtable strt;
|
|
lua_Alloc frealloc;
|
|
void*ud;
|
|
lu_byte currentwhite;
|
|
lu_byte gcstate;
|
|
int sweepstrgc;
|
|
GCObject*rootgc;
|
|
GCObject**sweepgc;
|
|
GCObject*gray;
|
|
GCObject*grayagain;
|
|
GCObject*weak;
|
|
GCObject*tmudata;
|
|
Mbuffer buff;
|
|
lu_mem GCthreshold;
|
|
lu_mem totalbytes;
|
|
lu_mem estimate;
|
|
lu_mem gcdept;
|
|
int gcpause;
|
|
int gcstepmul;
|
|
lua_CFunction panic;
|
|
TValue l_registry;
|
|
struct lua_State*mainthread;
|
|
UpVal uvhead;
|
|
struct Table*mt[(8+1)];
|
|
TString*tmname[TM_N];
|
|
}global_State;
|
|
struct lua_State{
|
|
GCObject*next;lu_byte tt;lu_byte marked;
|
|
lu_byte status;
|
|
StkId top;
|
|
StkId base;
|
|
global_State*l_G;
|
|
CallInfo*ci;
|
|
const Instruction*savedpc;
|
|
StkId stack_last;
|
|
StkId stack;
|
|
CallInfo*end_ci;
|
|
CallInfo*base_ci;
|
|
int stacksize;
|
|
int size_ci;
|
|
unsigned short nCcalls;
|
|
unsigned short baseCcalls;
|
|
lu_byte hookmask;
|
|
lu_byte allowhook;
|
|
int basehookcount;
|
|
int hookcount;
|
|
lua_Hook hook;
|
|
TValue l_gt;
|
|
TValue env;
|
|
GCObject*openupval;
|
|
GCObject*gclist;
|
|
struct lua_longjmp*errorJmp;
|
|
ptrdiff_t errfunc;
|
|
};
|
|
#define G(L)(L->l_G)
|
|
union GCObject{
|
|
GCheader gch;
|
|
union TString ts;
|
|
union Udata u;
|
|
union Closure cl;
|
|
struct Table h;
|
|
struct Proto p;
|
|
struct UpVal uv;
|
|
struct lua_State th;
|
|
};
|
|
#define rawgco2ts(o)check_exp((o)->gch.tt==4,&((o)->ts))
|
|
#define gco2ts(o)(&rawgco2ts(o)->tsv)
|
|
#define rawgco2u(o)check_exp((o)->gch.tt==7,&((o)->u))
|
|
#define gco2u(o)(&rawgco2u(o)->uv)
|
|
#define gco2cl(o)check_exp((o)->gch.tt==6,&((o)->cl))
|
|
#define gco2h(o)check_exp((o)->gch.tt==5,&((o)->h))
|
|
#define gco2p(o)check_exp((o)->gch.tt==(8+1),&((o)->p))
|
|
#define gco2uv(o)check_exp((o)->gch.tt==(8+2),&((o)->uv))
|
|
#define ngcotouv(o)check_exp((o)==NULL||(o)->gch.tt==(8+2),&((o)->uv))
|
|
#define gco2th(o)check_exp((o)->gch.tt==8,&((o)->th))
|
|
#define obj2gco(v)(cast(GCObject*,(v)))
|
|
static void luaE_freethread(lua_State*L,lua_State*L1);
|
|
#define pcRel(pc,p)(cast(int,(pc)-(p)->code)-1)
|
|
#define getline_(f,pc)(((f)->lineinfo)?(f)->lineinfo[pc]:0)
|
|
#define resethookcount(L)(L->hookcount=L->basehookcount)
|
|
static void luaG_typeerror(lua_State*L,const TValue*o,
|
|
const char*opname);
|
|
static void luaG_runerror(lua_State*L,const char*fmt,...);
|
|
#define luaD_checkstack(L,n)if((char*)L->stack_last-(char*)L->top<=(n)*(int)sizeof(TValue))luaD_growstack(L,n);else condhardstacktests(luaD_reallocstack(L,L->stacksize-5-1));
|
|
#define incr_top(L){luaD_checkstack(L,1);L->top++;}
|
|
#define savestack(L,p)((char*)(p)-(char*)L->stack)
|
|
#define restorestack(L,n)((TValue*)((char*)L->stack+(n)))
|
|
#define saveci(L,p)((char*)(p)-(char*)L->base_ci)
|
|
#define restoreci(L,n)((CallInfo*)((char*)L->base_ci+(n)))
|
|
typedef void(*Pfunc)(lua_State*L,void*ud);
|
|
static int luaD_poscall(lua_State*L,StkId firstResult);
|
|
static void luaD_reallocCI(lua_State*L,int newsize);
|
|
static void luaD_reallocstack(lua_State*L,int newsize);
|
|
static void luaD_growstack(lua_State*L,int n);
|
|
static void luaD_throw(lua_State*L,int errcode);
|
|
static void*luaM_growaux_(lua_State*L,void*block,int*size,size_t size_elems,
|
|
int limit,const char*errormsg){
|
|
void*newblock;
|
|
int newsize;
|
|
if(*size>=limit/2){
|
|
if(*size>=limit)
|
|
luaG_runerror(L,errormsg);
|
|
newsize=limit;
|
|
}
|
|
else{
|
|
newsize=(*size)*2;
|
|
if(newsize<4)
|
|
newsize=4;
|
|
}
|
|
newblock=luaM_reallocv(L,block,*size,newsize,size_elems);
|
|
*size=newsize;
|
|
return newblock;
|
|
}
|
|
static void*luaM_toobig(lua_State*L){
|
|
luaG_runerror(L,"memory allocation error: block too big");
|
|
return NULL;
|
|
}
|
|
static void*luaM_realloc_(lua_State*L,void*block,size_t osize,size_t nsize){
|
|
global_State*g=G(L);
|
|
block=(*g->frealloc)(g->ud,block,osize,nsize);
|
|
if(block==NULL&&nsize>0)
|
|
luaD_throw(L,4);
|
|
g->totalbytes=(g->totalbytes-osize)+nsize;
|
|
return block;
|
|
}
|
|
#define resetbits(x,m)((x)&=cast(lu_byte,~(m)))
|
|
#define setbits(x,m)((x)|=(m))
|
|
#define testbits(x,m)((x)&(m))
|
|
#define bitmask(b)(1<<(b))
|
|
#define bit2mask(b1,b2)(bitmask(b1)|bitmask(b2))
|
|
#define l_setbit(x,b)setbits(x,bitmask(b))
|
|
#define resetbit(x,b)resetbits(x,bitmask(b))
|
|
#define testbit(x,b)testbits(x,bitmask(b))
|
|
#define set2bits(x,b1,b2)setbits(x,(bit2mask(b1,b2)))
|
|
#define reset2bits(x,b1,b2)resetbits(x,(bit2mask(b1,b2)))
|
|
#define test2bits(x,b1,b2)testbits(x,(bit2mask(b1,b2)))
|
|
#define iswhite(x)test2bits((x)->gch.marked,0,1)
|
|
#define isblack(x)testbit((x)->gch.marked,2)
|
|
#define isgray(x)(!isblack(x)&&!iswhite(x))
|
|
#define otherwhite(g)(g->currentwhite^bit2mask(0,1))
|
|
#define isdead(g,v)((v)->gch.marked&otherwhite(g)&bit2mask(0,1))
|
|
#define changewhite(x)((x)->gch.marked^=bit2mask(0,1))
|
|
#define gray2black(x)l_setbit((x)->gch.marked,2)
|
|
#define valiswhite(x)(iscollectable(x)&&iswhite(gcvalue(x)))
|
|
#define luaC_white(g)cast(lu_byte,(g)->currentwhite&bit2mask(0,1))
|
|
#define luaC_checkGC(L){condhardstacktests(luaD_reallocstack(L,L->stacksize-5-1));if(G(L)->totalbytes>=G(L)->GCthreshold)luaC_step(L);}
|
|
#define luaC_barrier(L,p,v){if(valiswhite(v)&&isblack(obj2gco(p)))luaC_barrierf(L,obj2gco(p),gcvalue(v));}
|
|
#define luaC_barriert(L,t,v){if(valiswhite(v)&&isblack(obj2gco(t)))luaC_barrierback(L,t);}
|
|
#define luaC_objbarrier(L,p,o){if(iswhite(obj2gco(o))&&isblack(obj2gco(p)))luaC_barrierf(L,obj2gco(p),obj2gco(o));}
|
|
#define luaC_objbarriert(L,t,o){if(iswhite(obj2gco(o))&&isblack(obj2gco(t)))luaC_barrierback(L,t);}
|
|
static void luaC_step(lua_State*L);
|
|
static void luaC_link(lua_State*L,GCObject*o,lu_byte tt);
|
|
static void luaC_linkupval(lua_State*L,UpVal*uv);
|
|
static void luaC_barrierf(lua_State*L,GCObject*o,GCObject*v);
|
|
static void luaC_barrierback(lua_State*L,Table*t);
|
|
#define sizestring(s)(sizeof(union TString)+((s)->len+1)*sizeof(char))
|
|
#define sizeudata(u)(sizeof(union Udata)+(u)->len)
|
|
#define luaS_new(L,s)(luaS_newlstr(L,s,strlen(s)))
|
|
#define luaS_newliteral(L,s)(luaS_newlstr(L,""s,(sizeof(s)/sizeof(char))-1))
|
|
#define luaS_fix(s)l_setbit((s)->tsv.marked,5)
|
|
static TString*luaS_newlstr(lua_State*L,const char*str,size_t l);
|
|
#define tostring(L,o)((ttype(o)==4)||(luaV_tostring(L,o)))
|
|
#define tonumber(o,n)(ttype(o)==3||(((o)=luaV_tonumber(o,n))!=NULL))
|
|
#define equalobj(L,o1,o2)(ttype(o1)==ttype(o2)&&luaV_equalval(L,o1,o2))
|
|
static int luaV_equalval(lua_State*L,const TValue*t1,const TValue*t2);
|
|
static const TValue*luaV_tonumber(const TValue*obj,TValue*n);
|
|
static int luaV_tostring(lua_State*L,StkId obj);
|
|
static void luaV_execute(lua_State*L,int nexeccalls);
|
|
static void luaV_concat(lua_State*L,int total,int last);
|
|
static const TValue luaO_nilobject_={{NULL},0};
|
|
static int luaO_int2fb(unsigned int x){
|
|
int e=0;
|
|
while(x>=16){
|
|
x=(x+1)>>1;
|
|
e++;
|
|
}
|
|
if(x<8)return x;
|
|
else return((e+1)<<3)|(cast_int(x)-8);
|
|
}
|
|
static int luaO_fb2int(int x){
|
|
int e=(x>>3)&31;
|
|
if(e==0)return x;
|
|
else return((x&7)+8)<<(e-1);
|
|
}
|
|
static int luaO_log2(unsigned int x){
|
|
static const lu_byte log_2[256]={
|
|
0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
|
|
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
|
|
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
|
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
|
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
|
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
|
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
|
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
|
|
};
|
|
int l=-1;
|
|
while(x>=256){l+=8;x>>=8;}
|
|
return l+log_2[x];
|
|
}
|
|
static int luaO_rawequalObj(const TValue*t1,const TValue*t2){
|
|
if(ttype(t1)!=ttype(t2))return 0;
|
|
else switch(ttype(t1)){
|
|
case 0:
|
|
return 1;
|
|
case 3:
|
|
return luai_numeq(nvalue(t1),nvalue(t2));
|
|
case 1:
|
|
return bvalue(t1)==bvalue(t2);
|
|
case 2:
|
|
return pvalue(t1)==pvalue(t2);
|
|
default:
|
|
return gcvalue(t1)==gcvalue(t2);
|
|
}
|
|
}
|
|
static int luaO_str2d(const char*s,lua_Number*result){
|
|
char*endptr;
|
|
*result=lua_str2number(s,&endptr);
|
|
if(endptr==s)return 0;
|
|
if(*endptr=='x'||*endptr=='X')
|
|
*result=cast_num(strtoul(s,&endptr,16));
|
|
if(*endptr=='\0')return 1;
|
|
while(isspace(cast(unsigned char,*endptr)))endptr++;
|
|
if(*endptr!='\0')return 0;
|
|
return 1;
|
|
}
|
|
static void pushstr(lua_State*L,const char*str){
|
|
setsvalue(L,L->top,luaS_new(L,str));
|
|
incr_top(L);
|
|
}
|
|
static const char*luaO_pushvfstring(lua_State*L,const char*fmt,va_list argp){
|
|
int n=1;
|
|
pushstr(L,"");
|
|
for(;;){
|
|
const char*e=strchr(fmt,'%');
|
|
if(e==NULL)break;
|
|
setsvalue(L,L->top,luaS_newlstr(L,fmt,e-fmt));
|
|
incr_top(L);
|
|
switch(*(e+1)){
|
|
case's':{
|
|
const char*s=va_arg(argp,char*);
|
|
if(s==NULL)s="(null)";
|
|
pushstr(L,s);
|
|
break;
|
|
}
|
|
case'c':{
|
|
char buff[2];
|
|
buff[0]=cast(char,va_arg(argp,int));
|
|
buff[1]='\0';
|
|
pushstr(L,buff);
|
|
break;
|
|
}
|
|
case'd':{
|
|
setnvalue(L->top,cast_num(va_arg(argp,int)));
|
|
incr_top(L);
|
|
break;
|
|
}
|
|
case'f':{
|
|
setnvalue(L->top,cast_num(va_arg(argp,l_uacNumber)));
|
|
incr_top(L);
|
|
break;
|
|
}
|
|
case'p':{
|
|
char buff[4*sizeof(void*)+8];
|
|
sprintf(buff,"%p",va_arg(argp,void*));
|
|
pushstr(L,buff);
|
|
break;
|
|
}
|
|
case'%':{
|
|
pushstr(L,"%");
|
|
break;
|
|
}
|
|
default:{
|
|
char buff[3];
|
|
buff[0]='%';
|
|
buff[1]=*(e+1);
|
|
buff[2]='\0';
|
|
pushstr(L,buff);
|
|
break;
|
|
}
|
|
}
|
|
n+=2;
|
|
fmt=e+2;
|
|
}
|
|
pushstr(L,fmt);
|
|
luaV_concat(L,n+1,cast_int(L->top-L->base)-1);
|
|
L->top-=n;
|
|
return svalue(L->top-1);
|
|
}
|
|
static const char*luaO_pushfstring(lua_State*L,const char*fmt,...){
|
|
const char*msg;
|
|
va_list argp;
|
|
va_start(argp,fmt);
|
|
msg=luaO_pushvfstring(L,fmt,argp);
|
|
va_end(argp);
|
|
return msg;
|
|
}
|
|
static void luaO_chunkid(char*out,const char*source,size_t bufflen){
|
|
if(*source=='='){
|
|
strncpy(out,source+1,bufflen);
|
|
out[bufflen-1]='\0';
|
|
}
|
|
else{
|
|
if(*source=='@'){
|
|
size_t l;
|
|
source++;
|
|
bufflen-=sizeof(" '...' ");
|
|
l=strlen(source);
|
|
strcpy(out,"");
|
|
if(l>bufflen){
|
|
source+=(l-bufflen);
|
|
strcat(out,"...");
|
|
}
|
|
strcat(out,source);
|
|
}
|
|
else{
|
|
size_t len=strcspn(source,"\n\r");
|
|
bufflen-=sizeof(" [string \"...\"] ");
|
|
if(len>bufflen)len=bufflen;
|
|
strcpy(out,"[string \"");
|
|
if(source[len]!='\0'){
|
|
strncat(out,source,len);
|
|
strcat(out,"...");
|
|
}
|
|
else
|
|
strcat(out,source);
|
|
strcat(out,"\"]");
|
|
}
|
|
}
|
|
}
|
|
#define gnode(t,i)(&(t)->node[i])
|
|
#define gkey(n)(&(n)->i_key.nk)
|
|
#define gval(n)(&(n)->i_val)
|
|
#define gnext(n)((n)->i_key.nk.next)
|
|
#define key2tval(n)(&(n)->i_key.tvk)
|
|
static TValue*luaH_setnum(lua_State*L,Table*t,int key);
|
|
static const TValue*luaH_getstr(Table*t,TString*key);
|
|
static TValue*luaH_set(lua_State*L,Table*t,const TValue*key);
|
|
static const char*const luaT_typenames[]={
|
|
"nil","boolean","userdata","number",
|
|
"string","table","function","userdata","thread",
|
|
"proto","upval"
|
|
};
|
|
static void luaT_init(lua_State*L){
|
|
static const char*const luaT_eventname[]={
|
|
"__index","__newindex",
|
|
"__gc","__mode","__eq",
|
|
"__add","__sub","__mul","__div","__mod",
|
|
"__pow","__unm","__len","__lt","__le",
|
|
"__concat","__call"
|
|
};
|
|
int i;
|
|
for(i=0;i<TM_N;i++){
|
|
G(L)->tmname[i]=luaS_new(L,luaT_eventname[i]);
|
|
luaS_fix(G(L)->tmname[i]);
|
|
}
|
|
}
|
|
static const TValue*luaT_gettm(Table*events,TMS event,TString*ename){
|
|
const TValue*tm=luaH_getstr(events,ename);
|
|
if(ttisnil(tm)){
|
|
events->flags|=cast_byte(1u<<event);
|
|
return NULL;
|
|
}
|
|
else return tm;
|
|
}
|
|
static const TValue*luaT_gettmbyobj(lua_State*L,const TValue*o,TMS event){
|
|
Table*mt;
|
|
switch(ttype(o)){
|
|
case 5:
|
|
mt=hvalue(o)->metatable;
|
|
break;
|
|
case 7:
|
|
mt=uvalue(o)->metatable;
|
|
break;
|
|
default:
|
|
mt=G(L)->mt[ttype(o)];
|
|
}
|
|
return(mt?luaH_getstr(mt,G(L)->tmname[event]):(&luaO_nilobject_));
|
|
}
|
|
#define sizeCclosure(n)(cast(int,sizeof(CClosure))+cast(int,sizeof(TValue)*((n)-1)))
|
|
#define sizeLclosure(n)(cast(int,sizeof(LClosure))+cast(int,sizeof(TValue*)*((n)-1)))
|
|
static Closure*luaF_newCclosure(lua_State*L,int nelems,Table*e){
|
|
Closure*c=cast(Closure*,luaM_malloc(L,sizeCclosure(nelems)));
|
|
luaC_link(L,obj2gco(c),6);
|
|
c->c.isC=1;
|
|
c->c.env=e;
|
|
c->c.nupvalues=cast_byte(nelems);
|
|
return c;
|
|
}
|
|
static Closure*luaF_newLclosure(lua_State*L,int nelems,Table*e){
|
|
Closure*c=cast(Closure*,luaM_malloc(L,sizeLclosure(nelems)));
|
|
luaC_link(L,obj2gco(c),6);
|
|
c->l.isC=0;
|
|
c->l.env=e;
|
|
c->l.nupvalues=cast_byte(nelems);
|
|
while(nelems--)c->l.upvals[nelems]=NULL;
|
|
return c;
|
|
}
|
|
static UpVal*luaF_newupval(lua_State*L){
|
|
UpVal*uv=luaM_new(L,UpVal);
|
|
luaC_link(L,obj2gco(uv),(8+2));
|
|
uv->v=&uv->u.value;
|
|
setnilvalue(uv->v);
|
|
return uv;
|
|
}
|
|
static UpVal*luaF_findupval(lua_State*L,StkId level){
|
|
global_State*g=G(L);
|
|
GCObject**pp=&L->openupval;
|
|
UpVal*p;
|
|
UpVal*uv;
|
|
while(*pp!=NULL&&(p=ngcotouv(*pp))->v>=level){
|
|
if(p->v==level){
|
|
if(isdead(g,obj2gco(p)))
|
|
changewhite(obj2gco(p));
|
|
return p;
|
|
}
|
|
pp=&p->next;
|
|
}
|
|
uv=luaM_new(L,UpVal);
|
|
uv->tt=(8+2);
|
|
uv->marked=luaC_white(g);
|
|
uv->v=level;
|
|
uv->next=*pp;
|
|
*pp=obj2gco(uv);
|
|
uv->u.l.prev=&g->uvhead;
|
|
uv->u.l.next=g->uvhead.u.l.next;
|
|
uv->u.l.next->u.l.prev=uv;
|
|
g->uvhead.u.l.next=uv;
|
|
return uv;
|
|
}
|
|
static void unlinkupval(UpVal*uv){
|
|
uv->u.l.next->u.l.prev=uv->u.l.prev;
|
|
uv->u.l.prev->u.l.next=uv->u.l.next;
|
|
}
|
|
static void luaF_freeupval(lua_State*L,UpVal*uv){
|
|
if(uv->v!=&uv->u.value)
|
|
unlinkupval(uv);
|
|
luaM_free(L,uv);
|
|
}
|
|
static void luaF_close(lua_State*L,StkId level){
|
|
UpVal*uv;
|
|
global_State*g=G(L);
|
|
while(L->openupval!=NULL&&(uv=ngcotouv(L->openupval))->v>=level){
|
|
GCObject*o=obj2gco(uv);
|
|
L->openupval=uv->next;
|
|
if(isdead(g,o))
|
|
luaF_freeupval(L,uv);
|
|
else{
|
|
unlinkupval(uv);
|
|
setobj(L,&uv->u.value,uv->v);
|
|
uv->v=&uv->u.value;
|
|
luaC_linkupval(L,uv);
|
|
}
|
|
}
|
|
}
|
|
static Proto*luaF_newproto(lua_State*L){
|
|
Proto*f=luaM_new(L,Proto);
|
|
luaC_link(L,obj2gco(f),(8+1));
|
|
f->k=NULL;
|
|
f->sizek=0;
|
|
f->p=NULL;
|
|
f->sizep=0;
|
|
f->code=NULL;
|
|
f->sizecode=0;
|
|
f->sizelineinfo=0;
|
|
f->sizeupvalues=0;
|
|
f->nups=0;
|
|
f->upvalues=NULL;
|
|
f->numparams=0;
|
|
f->is_vararg=0;
|
|
f->maxstacksize=0;
|
|
f->lineinfo=NULL;
|
|
f->sizelocvars=0;
|
|
f->locvars=NULL;
|
|
f->linedefined=0;
|
|
f->lastlinedefined=0;
|
|
f->source=NULL;
|
|
return f;
|
|
}
|
|
static void luaF_freeproto(lua_State*L,Proto*f){
|
|
luaM_freearray(L,f->code,f->sizecode,Instruction);
|
|
luaM_freearray(L,f->p,f->sizep,Proto*);
|
|
luaM_freearray(L,f->k,f->sizek,TValue);
|
|
luaM_freearray(L,f->lineinfo,f->sizelineinfo,int);
|
|
luaM_freearray(L,f->locvars,f->sizelocvars,struct LocVar);
|
|
luaM_freearray(L,f->upvalues,f->sizeupvalues,TString*);
|
|
luaM_free(L,f);
|
|
}
|
|
static void luaF_freeclosure(lua_State*L,Closure*c){
|
|
int size=(c->c.isC)?sizeCclosure(c->c.nupvalues):
|
|
sizeLclosure(c->l.nupvalues);
|
|
luaM_freemem(L,c,size);
|
|
}
|
|
#define MASK1(n,p)((~((~(Instruction)0)<<n))<<p)
|
|
#define MASK0(n,p)(~MASK1(n,p))
|
|
#define GET_OPCODE(i)(cast(OpCode,((i)>>0)&MASK1(6,0)))
|
|
#define SET_OPCODE(i,o)((i)=(((i)&MASK0(6,0))|((cast(Instruction,o)<<0)&MASK1(6,0))))
|
|
#define GETARG_A(i)(cast(int,((i)>>(0+6))&MASK1(8,0)))
|
|
#define SETARG_A(i,u)((i)=(((i)&MASK0(8,(0+6)))|((cast(Instruction,u)<<(0+6))&MASK1(8,(0+6)))))
|
|
#define GETARG_B(i)(cast(int,((i)>>(((0+6)+8)+9))&MASK1(9,0)))
|
|
#define SETARG_B(i,b)((i)=(((i)&MASK0(9,(((0+6)+8)+9)))|((cast(Instruction,b)<<(((0+6)+8)+9))&MASK1(9,(((0+6)+8)+9)))))
|
|
#define GETARG_C(i)(cast(int,((i)>>((0+6)+8))&MASK1(9,0)))
|
|
#define SETARG_C(i,b)((i)=(((i)&MASK0(9,((0+6)+8)))|((cast(Instruction,b)<<((0+6)+8))&MASK1(9,((0+6)+8)))))
|
|
#define GETARG_Bx(i)(cast(int,((i)>>((0+6)+8))&MASK1((9+9),0)))
|
|
#define SETARG_Bx(i,b)((i)=(((i)&MASK0((9+9),((0+6)+8)))|((cast(Instruction,b)<<((0+6)+8))&MASK1((9+9),((0+6)+8)))))
|
|
#define GETARG_sBx(i)(GETARG_Bx(i)-(((1<<(9+9))-1)>>1))
|
|
#define SETARG_sBx(i,b)SETARG_Bx((i),cast(unsigned int,(b)+(((1<<(9+9))-1)>>1)))
|
|
#define CREATE_ABC(o,a,b,c)((cast(Instruction,o)<<0)|(cast(Instruction,a)<<(0+6))|(cast(Instruction,b)<<(((0+6)+8)+9))|(cast(Instruction,c)<<((0+6)+8)))
|
|
#define CREATE_ABx(o,a,bc)((cast(Instruction,o)<<0)|(cast(Instruction,a)<<(0+6))|(cast(Instruction,bc)<<((0+6)+8)))
|
|
#define ISK(x)((x)&(1<<(9-1)))
|
|
#define INDEXK(r)((int)(r)&~(1<<(9-1)))
|
|
#define RKASK(x)((x)|(1<<(9-1)))
|
|
static const lu_byte luaP_opmodes[(cast(int,OP_VARARG)+1)];
|
|
#define getBMode(m)(cast(enum OpArgMask,(luaP_opmodes[m]>>4)&3))
|
|
#define getCMode(m)(cast(enum OpArgMask,(luaP_opmodes[m]>>2)&3))
|
|
#define testTMode(m)(luaP_opmodes[m]&(1<<7))
|
|
typedef struct expdesc{
|
|
expkind k;
|
|
union{
|
|
struct{int info,aux;}s;
|
|
lua_Number nval;
|
|
}u;
|
|
int t;
|
|
int f;
|
|
}expdesc;
|
|
typedef struct upvaldesc{
|
|
lu_byte k;
|
|
lu_byte info;
|
|
}upvaldesc;
|
|
struct BlockCnt;
|
|
typedef struct FuncState{
|
|
Proto*f;
|
|
Table*h;
|
|
struct FuncState*prev;
|
|
struct LexState*ls;
|
|
struct lua_State*L;
|
|
struct BlockCnt*bl;
|
|
int pc;
|
|
int lasttarget;
|
|
int jpc;
|
|
int freereg;
|
|
int nk;
|
|
int np;
|
|
short nlocvars;
|
|
lu_byte nactvar;
|
|
upvaldesc upvalues[60];
|
|
unsigned short actvar[200];
|
|
}FuncState;
|
|
static Proto*luaY_parser(lua_State*L,ZIO*z,Mbuffer*buff,
|
|
const char*name);
|
|
struct lua_longjmp{
|
|
struct lua_longjmp*previous;
|
|
jmp_buf b;
|
|
volatile int status;
|
|
};
|
|
static void luaD_seterrorobj(lua_State*L,int errcode,StkId oldtop){
|
|
switch(errcode){
|
|
case 4:{
|
|
setsvalue(L,oldtop,luaS_newliteral(L,"not enough memory"));
|
|
break;
|
|
}
|
|
case 5:{
|
|
setsvalue(L,oldtop,luaS_newliteral(L,"error in error handling"));
|
|
break;
|
|
}
|
|
case 3:
|
|
case 2:{
|
|
setobj(L,oldtop,L->top-1);
|
|
break;
|
|
}
|
|
}
|
|
L->top=oldtop+1;
|
|
}
|
|
static void restore_stack_limit(lua_State*L){
|
|
if(L->size_ci>20000){
|
|
int inuse=cast_int(L->ci-L->base_ci);
|
|
if(inuse+1<20000)
|
|
luaD_reallocCI(L,20000);
|
|
}
|
|
}
|
|
static void resetstack(lua_State*L,int status){
|
|
L->ci=L->base_ci;
|
|
L->base=L->ci->base;
|
|
luaF_close(L,L->base);
|
|
luaD_seterrorobj(L,status,L->base);
|
|
L->nCcalls=L->baseCcalls;
|
|
L->allowhook=1;
|
|
restore_stack_limit(L);
|
|
L->errfunc=0;
|
|
L->errorJmp=NULL;
|
|
}
|
|
static void luaD_throw(lua_State*L,int errcode){
|
|
if(L->errorJmp){
|
|
L->errorJmp->status=errcode;
|
|
LUAI_THROW(L,L->errorJmp);
|
|
}
|
|
else{
|
|
L->status=cast_byte(errcode);
|
|
if(G(L)->panic){
|
|
resetstack(L,errcode);
|
|
G(L)->panic(L);
|
|
}
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
static int luaD_rawrunprotected(lua_State*L,Pfunc f,void*ud){
|
|
struct lua_longjmp lj;
|
|
lj.status=0;
|
|
lj.previous=L->errorJmp;
|
|
L->errorJmp=&lj;
|
|
LUAI_TRY(L,&lj,
|
|
(*f)(L,ud);
|
|
);
|
|
L->errorJmp=lj.previous;
|
|
return lj.status;
|
|
}
|
|
static void correctstack(lua_State*L,TValue*oldstack){
|
|
CallInfo*ci;
|
|
GCObject*up;
|
|
L->top=(L->top-oldstack)+L->stack;
|
|
for(up=L->openupval;up!=NULL;up=up->gch.next)
|
|
gco2uv(up)->v=(gco2uv(up)->v-oldstack)+L->stack;
|
|
for(ci=L->base_ci;ci<=L->ci;ci++){
|
|
ci->top=(ci->top-oldstack)+L->stack;
|
|
ci->base=(ci->base-oldstack)+L->stack;
|
|
ci->func=(ci->func-oldstack)+L->stack;
|
|
}
|
|
L->base=(L->base-oldstack)+L->stack;
|
|
}
|
|
static void luaD_reallocstack(lua_State*L,int newsize){
|
|
TValue*oldstack=L->stack;
|
|
int realsize=newsize+1+5;
|
|
luaM_reallocvector(L,L->stack,L->stacksize,realsize,TValue);
|
|
L->stacksize=realsize;
|
|
L->stack_last=L->stack+newsize;
|
|
correctstack(L,oldstack);
|
|
}
|
|
static void luaD_reallocCI(lua_State*L,int newsize){
|
|
CallInfo*oldci=L->base_ci;
|
|
luaM_reallocvector(L,L->base_ci,L->size_ci,newsize,CallInfo);
|
|
L->size_ci=newsize;
|
|
L->ci=(L->ci-oldci)+L->base_ci;
|
|
L->end_ci=L->base_ci+L->size_ci-1;
|
|
}
|
|
static void luaD_growstack(lua_State*L,int n){
|
|
if(n<=L->stacksize)
|
|
luaD_reallocstack(L,2*L->stacksize);
|
|
else
|
|
luaD_reallocstack(L,L->stacksize+n);
|
|
}
|
|
static CallInfo*growCI(lua_State*L){
|
|
if(L->size_ci>20000)
|
|
luaD_throw(L,5);
|
|
else{
|
|
luaD_reallocCI(L,2*L->size_ci);
|
|
if(L->size_ci>20000)
|
|
luaG_runerror(L,"stack overflow");
|
|
}
|
|
return++L->ci;
|
|
}
|
|
static StkId adjust_varargs(lua_State*L,Proto*p,int actual){
|
|
int i;
|
|
int nfixargs=p->numparams;
|
|
Table*htab=NULL;
|
|
StkId base,fixed;
|
|
for(;actual<nfixargs;++actual)
|
|
setnilvalue(L->top++);
|
|
fixed=L->top-actual;
|
|
base=L->top;
|
|
for(i=0;i<nfixargs;i++){
|
|
setobj(L,L->top++,fixed+i);
|
|
setnilvalue(fixed+i);
|
|
}
|
|
if(htab){
|
|
sethvalue(L,L->top++,htab);
|
|
}
|
|
return base;
|
|
}
|
|
static StkId tryfuncTM(lua_State*L,StkId func){
|
|
const TValue*tm=luaT_gettmbyobj(L,func,TM_CALL);
|
|
StkId p;
|
|
ptrdiff_t funcr=savestack(L,func);
|
|
if(!ttisfunction(tm))
|
|
luaG_typeerror(L,func,"call");
|
|
for(p=L->top;p>func;p--)setobj(L,p,p-1);
|
|
incr_top(L);
|
|
func=restorestack(L,funcr);
|
|
setobj(L,func,tm);
|
|
return func;
|
|
}
|
|
#define inc_ci(L)((L->ci==L->end_ci)?growCI(L):(condhardstacktests(luaD_reallocCI(L,L->size_ci)),++L->ci))
|
|
static int luaD_precall(lua_State*L,StkId func,int nresults){
|
|
LClosure*cl;
|
|
ptrdiff_t funcr;
|
|
if(!ttisfunction(func))
|
|
func=tryfuncTM(L,func);
|
|
funcr=savestack(L,func);
|
|
cl=&clvalue(func)->l;
|
|
L->ci->savedpc=L->savedpc;
|
|
if(!cl->isC){
|
|
CallInfo*ci;
|
|
StkId st,base;
|
|
Proto*p=cl->p;
|
|
luaD_checkstack(L,p->maxstacksize+p->numparams);
|
|
func=restorestack(L,funcr);
|
|
if(!p->is_vararg){
|
|
base=func+1;
|
|
if(L->top>base+p->numparams)
|
|
L->top=base+p->numparams;
|
|
}
|
|
else{
|
|
int nargs=cast_int(L->top-func)-1;
|
|
base=adjust_varargs(L,p,nargs);
|
|
func=restorestack(L,funcr);
|
|
}
|
|
ci=inc_ci(L);
|
|
ci->func=func;
|
|
L->base=ci->base=base;
|
|
ci->top=L->base+p->maxstacksize;
|
|
L->savedpc=p->code;
|
|
ci->tailcalls=0;
|
|
ci->nresults=nresults;
|
|
for(st=L->top;st<ci->top;st++)
|
|
setnilvalue(st);
|
|
L->top=ci->top;
|
|
return 0;
|
|
}
|
|
else{
|
|
CallInfo*ci;
|
|
int n;
|
|
luaD_checkstack(L,20);
|
|
ci=inc_ci(L);
|
|
ci->func=restorestack(L,funcr);
|
|
L->base=ci->base=ci->func+1;
|
|
ci->top=L->top+20;
|
|
ci->nresults=nresults;
|
|
n=(*curr_func(L)->c.f)(L);
|
|
if(n<0)
|
|
return 2;
|
|
else{
|
|
luaD_poscall(L,L->top-n);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
static int luaD_poscall(lua_State*L,StkId firstResult){
|
|
StkId res;
|
|
int wanted,i;
|
|
CallInfo*ci;
|
|
ci=L->ci--;
|
|
res=ci->func;
|
|
wanted=ci->nresults;
|
|
L->base=(ci-1)->base;
|
|
L->savedpc=(ci-1)->savedpc;
|
|
for(i=wanted;i!=0&&firstResult<L->top;i--)
|
|
setobj(L,res++,firstResult++);
|
|
while(i-->0)
|
|
setnilvalue(res++);
|
|
L->top=res;
|
|
return(wanted-(-1));
|
|
}
|
|
static void luaD_call(lua_State*L,StkId func,int nResults){
|
|
if(++L->nCcalls>=200){
|
|
if(L->nCcalls==200)
|
|
luaG_runerror(L,"C stack overflow");
|
|
else if(L->nCcalls>=(200+(200>>3)))
|
|
luaD_throw(L,5);
|
|
}
|
|
if(luaD_precall(L,func,nResults)==0)
|
|
luaV_execute(L,1);
|
|
L->nCcalls--;
|
|
luaC_checkGC(L);
|
|
}
|
|
static int luaD_pcall(lua_State*L,Pfunc func,void*u,
|
|
ptrdiff_t old_top,ptrdiff_t ef){
|
|
int status;
|
|
unsigned short oldnCcalls=L->nCcalls;
|
|
ptrdiff_t old_ci=saveci(L,L->ci);
|
|
lu_byte old_allowhooks=L->allowhook;
|
|
ptrdiff_t old_errfunc=L->errfunc;
|
|
L->errfunc=ef;
|
|
status=luaD_rawrunprotected(L,func,u);
|
|
if(status!=0){
|
|
StkId oldtop=restorestack(L,old_top);
|
|
luaF_close(L,oldtop);
|
|
luaD_seterrorobj(L,status,oldtop);
|
|
L->nCcalls=oldnCcalls;
|
|
L->ci=restoreci(L,old_ci);
|
|
L->base=L->ci->base;
|
|
L->savedpc=L->ci->savedpc;
|
|
L->allowhook=old_allowhooks;
|
|
restore_stack_limit(L);
|
|
}
|
|
L->errfunc=old_errfunc;
|
|
return status;
|
|
}
|
|
struct SParser{
|
|
ZIO*z;
|
|
Mbuffer buff;
|
|
const char*name;
|
|
};
|
|
static void f_parser(lua_State*L,void*ud){
|
|
int i;
|
|
Proto*tf;
|
|
Closure*cl;
|
|
struct SParser*p=cast(struct SParser*,ud);
|
|
luaC_checkGC(L);
|
|
tf=luaY_parser(L,p->z,
|
|
&p->buff,p->name);
|
|
cl=luaF_newLclosure(L,tf->nups,hvalue(gt(L)));
|
|
cl->l.p=tf;
|
|
for(i=0;i<tf->nups;i++)
|
|
cl->l.upvals[i]=luaF_newupval(L);
|
|
setclvalue(L,L->top,cl);
|
|
incr_top(L);
|
|
}
|
|
static int luaD_protectedparser(lua_State*L,ZIO*z,const char*name){
|
|
struct SParser p;
|
|
int status;
|
|
p.z=z;p.name=name;
|
|
luaZ_initbuffer(L,&p.buff);
|
|
status=luaD_pcall(L,f_parser,&p,savestack(L,L->top),L->errfunc);
|
|
luaZ_freebuffer(L,&p.buff);
|
|
return status;
|
|
}
|
|
static void luaS_resize(lua_State*L,int newsize){
|
|
GCObject**newhash;
|
|
stringtable*tb;
|
|
int i;
|
|
if(G(L)->gcstate==2)
|
|
return;
|
|
newhash=luaM_newvector(L,newsize,GCObject*);
|
|
tb=&G(L)->strt;
|
|
for(i=0;i<newsize;i++)newhash[i]=NULL;
|
|
for(i=0;i<tb->size;i++){
|
|
GCObject*p=tb->hash[i];
|
|
while(p){
|
|
GCObject*next=p->gch.next;
|
|
unsigned int h=gco2ts(p)->hash;
|
|
int h1=lmod(h,newsize);
|
|
p->gch.next=newhash[h1];
|
|
newhash[h1]=p;
|
|
p=next;
|
|
}
|
|
}
|
|
luaM_freearray(L,tb->hash,tb->size,TString*);
|
|
tb->size=newsize;
|
|
tb->hash=newhash;
|
|
}
|
|
static TString*newlstr(lua_State*L,const char*str,size_t l,
|
|
unsigned int h){
|
|
TString*ts;
|
|
stringtable*tb;
|
|
if(l+1>(((size_t)(~(size_t)0)-2)-sizeof(TString))/sizeof(char))
|
|
luaM_toobig(L);
|
|
ts=cast(TString*,luaM_malloc(L,(l+1)*sizeof(char)+sizeof(TString)));
|
|
ts->tsv.len=l;
|
|
ts->tsv.hash=h;
|
|
ts->tsv.marked=luaC_white(G(L));
|
|
ts->tsv.tt=4;
|
|
ts->tsv.reserved=0;
|
|
memcpy(ts+1,str,l*sizeof(char));
|
|
((char*)(ts+1))[l]='\0';
|
|
tb=&G(L)->strt;
|
|
h=lmod(h,tb->size);
|
|
ts->tsv.next=tb->hash[h];
|
|
tb->hash[h]=obj2gco(ts);
|
|
tb->nuse++;
|
|
if(tb->nuse>cast(lu_int32,tb->size)&&tb->size<=(INT_MAX-2)/2)
|
|
luaS_resize(L,tb->size*2);
|
|
return ts;
|
|
}
|
|
static TString*luaS_newlstr(lua_State*L,const char*str,size_t l){
|
|
GCObject*o;
|
|
unsigned int h=cast(unsigned int,l);
|
|
size_t step=(l>>5)+1;
|
|
size_t l1;
|
|
for(l1=l;l1>=step;l1-=step)
|
|
h=h^((h<<5)+(h>>2)+cast(unsigned char,str[l1-1]));
|
|
for(o=G(L)->strt.hash[lmod(h,G(L)->strt.size)];
|
|
o!=NULL;
|
|
o=o->gch.next){
|
|
TString*ts=rawgco2ts(o);
|
|
if(ts->tsv.len==l&&(memcmp(str,getstr(ts),l)==0)){
|
|
if(isdead(G(L),o))changewhite(o);
|
|
return ts;
|
|
}
|
|
}
|
|
return newlstr(L,str,l,h);
|
|
}
|
|
static Udata*luaS_newudata(lua_State*L,size_t s,Table*e){
|
|
Udata*u;
|
|
if(s>((size_t)(~(size_t)0)-2)-sizeof(Udata))
|
|
luaM_toobig(L);
|
|
u=cast(Udata*,luaM_malloc(L,s+sizeof(Udata)));
|
|
u->uv.marked=luaC_white(G(L));
|
|
u->uv.tt=7;
|
|
u->uv.len=s;
|
|
u->uv.metatable=NULL;
|
|
u->uv.env=e;
|
|
u->uv.next=G(L)->mainthread->next;
|
|
G(L)->mainthread->next=obj2gco(u);
|
|
return u;
|
|
}
|
|
#define hashpow2(t,n)(gnode(t,lmod((n),sizenode(t))))
|
|
#define hashstr(t,str)hashpow2(t,(str)->tsv.hash)
|
|
#define hashboolean(t,p)hashpow2(t,p)
|
|
#define hashmod(t,n)(gnode(t,((n)%((sizenode(t)-1)|1))))
|
|
#define hashpointer(t,p)hashmod(t,IntPoint(p))
|
|
static const Node dummynode_={
|
|
{{NULL},0},
|
|
{{{NULL},0,NULL}}
|
|
};
|
|
static Node*hashnum(const Table*t,lua_Number n){
|
|
unsigned int a[cast_int(sizeof(lua_Number)/sizeof(int))];
|
|
int i;
|
|
if(luai_numeq(n,0))
|
|
return gnode(t,0);
|
|
memcpy(a,&n,sizeof(a));
|
|
for(i=1;i<cast_int(sizeof(lua_Number)/sizeof(int));i++)a[0]+=a[i];
|
|
return hashmod(t,a[0]);
|
|
}
|
|
static Node*mainposition(const Table*t,const TValue*key){
|
|
switch(ttype(key)){
|
|
case 3:
|
|
return hashnum(t,nvalue(key));
|
|
case 4:
|
|
return hashstr(t,rawtsvalue(key));
|
|
case 1:
|
|
return hashboolean(t,bvalue(key));
|
|
case 2:
|
|
return hashpointer(t,pvalue(key));
|
|
default:
|
|
return hashpointer(t,gcvalue(key));
|
|
}
|
|
}
|
|
static int arrayindex(const TValue*key){
|
|
if(ttisnumber(key)){
|
|
lua_Number n=nvalue(key);
|
|
int k;
|
|
lua_number2int(k,n);
|
|
if(luai_numeq(cast_num(k),n))
|
|
return k;
|
|
}
|
|
return-1;
|
|
}
|
|
static int findindex(lua_State*L,Table*t,StkId key){
|
|
int i;
|
|
if(ttisnil(key))return-1;
|
|
i=arrayindex(key);
|
|
if(0<i&&i<=t->sizearray)
|
|
return i-1;
|
|
else{
|
|
Node*n=mainposition(t,key);
|
|
do{
|
|
if(luaO_rawequalObj(key2tval(n),key)||
|
|
(ttype(gkey(n))==(8+3)&&iscollectable(key)&&
|
|
gcvalue(gkey(n))==gcvalue(key))){
|
|
i=cast_int(n-gnode(t,0));
|
|
return i+t->sizearray;
|
|
}
|
|
else n=gnext(n);
|
|
}while(n);
|
|
luaG_runerror(L,"invalid key to "LUA_QL("next"));
|
|
return 0;
|
|
}
|
|
}
|
|
static int luaH_next(lua_State*L,Table*t,StkId key){
|
|
int i=findindex(L,t,key);
|
|
for(i++;i<t->sizearray;i++){
|
|
if(!ttisnil(&t->array[i])){
|
|
setnvalue(key,cast_num(i+1));
|
|
setobj(L,key+1,&t->array[i]);
|
|
return 1;
|
|
}
|
|
}
|
|
for(i-=t->sizearray;i<(int)sizenode(t);i++){
|
|
if(!ttisnil(gval(gnode(t,i)))){
|
|
setobj(L,key,key2tval(gnode(t,i)));
|
|
setobj(L,key+1,gval(gnode(t,i)));
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
static int computesizes(int nums[],int*narray){
|
|
int i;
|
|
int twotoi;
|
|
int a=0;
|
|
int na=0;
|
|
int n=0;
|
|
for(i=0,twotoi=1;twotoi/2<*narray;i++,twotoi*=2){
|
|
if(nums[i]>0){
|
|
a+=nums[i];
|
|
if(a>twotoi/2){
|
|
n=twotoi;
|
|
na=a;
|
|
}
|
|
}
|
|
if(a==*narray)break;
|
|
}
|
|
*narray=n;
|
|
return na;
|
|
}
|
|
static int countint(const TValue*key,int*nums){
|
|
int k=arrayindex(key);
|
|
if(0<k&&k<=(1<<(32-2))){
|
|
nums[ceillog2(k)]++;
|
|
return 1;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
static int numusearray(const Table*t,int*nums){
|
|
int lg;
|
|
int ttlg;
|
|
int ause=0;
|
|
int i=1;
|
|
for(lg=0,ttlg=1;lg<=(32-2);lg++,ttlg*=2){
|
|
int lc=0;
|
|
int lim=ttlg;
|
|
if(lim>t->sizearray){
|
|
lim=t->sizearray;
|
|
if(i>lim)
|
|
break;
|
|
}
|
|
for(;i<=lim;i++){
|
|
if(!ttisnil(&t->array[i-1]))
|
|
lc++;
|
|
}
|
|
nums[lg]+=lc;
|
|
ause+=lc;
|
|
}
|
|
return ause;
|
|
}
|
|
static int numusehash(const Table*t,int*nums,int*pnasize){
|
|
int totaluse=0;
|
|
int ause=0;
|
|
int i=sizenode(t);
|
|
while(i--){
|
|
Node*n=&t->node[i];
|
|
if(!ttisnil(gval(n))){
|
|
ause+=countint(key2tval(n),nums);
|
|
totaluse++;
|
|
}
|
|
}
|
|
*pnasize+=ause;
|
|
return totaluse;
|
|
}
|
|
static void setarrayvector(lua_State*L,Table*t,int size){
|
|
int i;
|
|
luaM_reallocvector(L,t->array,t->sizearray,size,TValue);
|
|
for(i=t->sizearray;i<size;i++)
|
|
setnilvalue(&t->array[i]);
|
|
t->sizearray=size;
|
|
}
|
|
static void setnodevector(lua_State*L,Table*t,int size){
|
|
int lsize;
|
|
if(size==0){
|
|
t->node=cast(Node*,(&dummynode_));
|
|
lsize=0;
|
|
}
|
|
else{
|
|
int i;
|
|
lsize=ceillog2(size);
|
|
if(lsize>(32-2))
|
|
luaG_runerror(L,"table overflow");
|
|
size=twoto(lsize);
|
|
t->node=luaM_newvector(L,size,Node);
|
|
for(i=0;i<size;i++){
|
|
Node*n=gnode(t,i);
|
|
gnext(n)=NULL;
|
|
setnilvalue(gkey(n));
|
|
setnilvalue(gval(n));
|
|
}
|
|
}
|
|
t->lsizenode=cast_byte(lsize);
|
|
t->lastfree=gnode(t,size);
|
|
}
|
|
static void resize(lua_State*L,Table*t,int nasize,int nhsize){
|
|
int i;
|
|
int oldasize=t->sizearray;
|
|
int oldhsize=t->lsizenode;
|
|
Node*nold=t->node;
|
|
if(nasize>oldasize)
|
|
setarrayvector(L,t,nasize);
|
|
setnodevector(L,t,nhsize);
|
|
if(nasize<oldasize){
|
|
t->sizearray=nasize;
|
|
for(i=nasize;i<oldasize;i++){
|
|
if(!ttisnil(&t->array[i]))
|
|
setobj(L,luaH_setnum(L,t,i+1),&t->array[i]);
|
|
}
|
|
luaM_reallocvector(L,t->array,oldasize,nasize,TValue);
|
|
}
|
|
for(i=twoto(oldhsize)-1;i>=0;i--){
|
|
Node*old=nold+i;
|
|
if(!ttisnil(gval(old)))
|
|
setobj(L,luaH_set(L,t,key2tval(old)),gval(old));
|
|
}
|
|
if(nold!=(&dummynode_))
|
|
luaM_freearray(L,nold,twoto(oldhsize),Node);
|
|
}
|
|
static void luaH_resizearray(lua_State*L,Table*t,int nasize){
|
|
int nsize=(t->node==(&dummynode_))?0:sizenode(t);
|
|
resize(L,t,nasize,nsize);
|
|
}
|
|
static void rehash(lua_State*L,Table*t,const TValue*ek){
|
|
int nasize,na;
|
|
int nums[(32-2)+1];
|
|
int i;
|
|
int totaluse;
|
|
for(i=0;i<=(32-2);i++)nums[i]=0;
|
|
nasize=numusearray(t,nums);
|
|
totaluse=nasize;
|
|
totaluse+=numusehash(t,nums,&nasize);
|
|
nasize+=countint(ek,nums);
|
|
totaluse++;
|
|
na=computesizes(nums,&nasize);
|
|
resize(L,t,nasize,totaluse-na);
|
|
}
|
|
static Table*luaH_new(lua_State*L,int narray,int nhash){
|
|
Table*t=luaM_new(L,Table);
|
|
luaC_link(L,obj2gco(t),5);
|
|
t->metatable=NULL;
|
|
t->flags=cast_byte(~0);
|
|
t->array=NULL;
|
|
t->sizearray=0;
|
|
t->lsizenode=0;
|
|
t->node=cast(Node*,(&dummynode_));
|
|
setarrayvector(L,t,narray);
|
|
setnodevector(L,t,nhash);
|
|
return t;
|
|
}
|
|
static void luaH_free(lua_State*L,Table*t){
|
|
if(t->node!=(&dummynode_))
|
|
luaM_freearray(L,t->node,sizenode(t),Node);
|
|
luaM_freearray(L,t->array,t->sizearray,TValue);
|
|
luaM_free(L,t);
|
|
}
|
|
static Node*getfreepos(Table*t){
|
|
while(t->lastfree-->t->node){
|
|
if(ttisnil(gkey(t->lastfree)))
|
|
return t->lastfree;
|
|
}
|
|
return NULL;
|
|
}
|
|
static TValue*newkey(lua_State*L,Table*t,const TValue*key){
|
|
Node*mp=mainposition(t,key);
|
|
if(!ttisnil(gval(mp))||mp==(&dummynode_)){
|
|
Node*othern;
|
|
Node*n=getfreepos(t);
|
|
if(n==NULL){
|
|
rehash(L,t,key);
|
|
return luaH_set(L,t,key);
|
|
}
|
|
othern=mainposition(t,key2tval(mp));
|
|
if(othern!=mp){
|
|
while(gnext(othern)!=mp)othern=gnext(othern);
|
|
gnext(othern)=n;
|
|
*n=*mp;
|
|
gnext(mp)=NULL;
|
|
setnilvalue(gval(mp));
|
|
}
|
|
else{
|
|
gnext(n)=gnext(mp);
|
|
gnext(mp)=n;
|
|
mp=n;
|
|
}
|
|
}
|
|
gkey(mp)->value=key->value;gkey(mp)->tt=key->tt;
|
|
luaC_barriert(L,t,key);
|
|
return gval(mp);
|
|
}
|
|
static const TValue*luaH_getnum(Table*t,int key){
|
|
if(cast(unsigned int,key)-1<cast(unsigned int,t->sizearray))
|
|
return&t->array[key-1];
|
|
else{
|
|
lua_Number nk=cast_num(key);
|
|
Node*n=hashnum(t,nk);
|
|
do{
|
|
if(ttisnumber(gkey(n))&&luai_numeq(nvalue(gkey(n)),nk))
|
|
return gval(n);
|
|
else n=gnext(n);
|
|
}while(n);
|
|
return(&luaO_nilobject_);
|
|
}
|
|
}
|
|
static const TValue*luaH_getstr(Table*t,TString*key){
|
|
Node*n=hashstr(t,key);
|
|
do{
|
|
if(ttisstring(gkey(n))&&rawtsvalue(gkey(n))==key)
|
|
return gval(n);
|
|
else n=gnext(n);
|
|
}while(n);
|
|
return(&luaO_nilobject_);
|
|
}
|
|
static const TValue*luaH_get(Table*t,const TValue*key){
|
|
switch(ttype(key)){
|
|
case 0:return(&luaO_nilobject_);
|
|
case 4:return luaH_getstr(t,rawtsvalue(key));
|
|
case 3:{
|
|
int k;
|
|
lua_Number n=nvalue(key);
|
|
lua_number2int(k,n);
|
|
if(luai_numeq(cast_num(k),nvalue(key)))
|
|
return luaH_getnum(t,k);
|
|
}
|
|
default:{
|
|
Node*n=mainposition(t,key);
|
|
do{
|
|
if(luaO_rawequalObj(key2tval(n),key))
|
|
return gval(n);
|
|
else n=gnext(n);
|
|
}while(n);
|
|
return(&luaO_nilobject_);
|
|
}
|
|
}
|
|
}
|
|
static TValue*luaH_set(lua_State*L,Table*t,const TValue*key){
|
|
const TValue*p=luaH_get(t,key);
|
|
t->flags=0;
|
|
if(p!=(&luaO_nilobject_))
|
|
return cast(TValue*,p);
|
|
else{
|
|
if(ttisnil(key))luaG_runerror(L,"table index is nil");
|
|
else if(ttisnumber(key)&&luai_numisnan(nvalue(key)))
|
|
luaG_runerror(L,"table index is NaN");
|
|
return newkey(L,t,key);
|
|
}
|
|
}
|
|
static TValue*luaH_setnum(lua_State*L,Table*t,int key){
|
|
const TValue*p=luaH_getnum(t,key);
|
|
if(p!=(&luaO_nilobject_))
|
|
return cast(TValue*,p);
|
|
else{
|
|
TValue k;
|
|
setnvalue(&k,cast_num(key));
|
|
return newkey(L,t,&k);
|
|
}
|
|
}
|
|
static TValue*luaH_setstr(lua_State*L,Table*t,TString*key){
|
|
const TValue*p=luaH_getstr(t,key);
|
|
if(p!=(&luaO_nilobject_))
|
|
return cast(TValue*,p);
|
|
else{
|
|
TValue k;
|
|
setsvalue(L,&k,key);
|
|
return newkey(L,t,&k);
|
|
}
|
|
}
|
|
static int unbound_search(Table*t,unsigned int j){
|
|
unsigned int i=j;
|
|
j++;
|
|
while(!ttisnil(luaH_getnum(t,j))){
|
|
i=j;
|
|
j*=2;
|
|
if(j>cast(unsigned int,(INT_MAX-2))){
|
|
i=1;
|
|
while(!ttisnil(luaH_getnum(t,i)))i++;
|
|
return i-1;
|
|
}
|
|
}
|
|
while(j-i>1){
|
|
unsigned int m=(i+j)/2;
|
|
if(ttisnil(luaH_getnum(t,m)))j=m;
|
|
else i=m;
|
|
}
|
|
return i;
|
|
}
|
|
static int luaH_getn(Table*t){
|
|
unsigned int j=t->sizearray;
|
|
if(j>0&&ttisnil(&t->array[j-1])){
|
|
unsigned int i=0;
|
|
while(j-i>1){
|
|
unsigned int m=(i+j)/2;
|
|
if(ttisnil(&t->array[m-1]))j=m;
|
|
else i=m;
|
|
}
|
|
return i;
|
|
}
|
|
else if(t->node==(&dummynode_))
|
|
return j;
|
|
else return unbound_search(t,j);
|
|
}
|
|
#define makewhite(g,x)((x)->gch.marked=cast_byte(((x)->gch.marked&cast_byte(~(bitmask(2)|bit2mask(0,1))))|luaC_white(g)))
|
|
#define white2gray(x)reset2bits((x)->gch.marked,0,1)
|
|
#define black2gray(x)resetbit((x)->gch.marked,2)
|
|
#define stringmark(s)reset2bits((s)->tsv.marked,0,1)
|
|
#define isfinalized(u)testbit((u)->marked,3)
|
|
#define markfinalized(u)l_setbit((u)->marked,3)
|
|
#define markvalue(g,o){checkconsistency(o);if(iscollectable(o)&&iswhite(gcvalue(o)))reallymarkobject(g,gcvalue(o));}
|
|
#define markobject(g,t){if(iswhite(obj2gco(t)))reallymarkobject(g,obj2gco(t));}
|
|
#define setthreshold(g)(g->GCthreshold=(g->estimate/100)*g->gcpause)
|
|
static void removeentry(Node*n){
|
|
if(iscollectable(gkey(n)))
|
|
setttype(gkey(n),(8+3));
|
|
}
|
|
static void reallymarkobject(global_State*g,GCObject*o){
|
|
white2gray(o);
|
|
switch(o->gch.tt){
|
|
case 4:{
|
|
return;
|
|
}
|
|
case 7:{
|
|
Table*mt=gco2u(o)->metatable;
|
|
gray2black(o);
|
|
if(mt)markobject(g,mt);
|
|
markobject(g,gco2u(o)->env);
|
|
return;
|
|
}
|
|
case(8+2):{
|
|
UpVal*uv=gco2uv(o);
|
|
markvalue(g,uv->v);
|
|
if(uv->v==&uv->u.value)
|
|
gray2black(o);
|
|
return;
|
|
}
|
|
case 6:{
|
|
gco2cl(o)->c.gclist=g->gray;
|
|
g->gray=o;
|
|
break;
|
|
}
|
|
case 5:{
|
|
gco2h(o)->gclist=g->gray;
|
|
g->gray=o;
|
|
break;
|
|
}
|
|
case 8:{
|
|
gco2th(o)->gclist=g->gray;
|
|
g->gray=o;
|
|
break;
|
|
}
|
|
case(8+1):{
|
|
gco2p(o)->gclist=g->gray;
|
|
g->gray=o;
|
|
break;
|
|
}
|
|
default:;
|
|
}
|
|
}
|
|
static void marktmu(global_State*g){
|
|
GCObject*u=g->tmudata;
|
|
if(u){
|
|
do{
|
|
u=u->gch.next;
|
|
makewhite(g,u);
|
|
reallymarkobject(g,u);
|
|
}while(u!=g->tmudata);
|
|
}
|
|
}
|
|
static size_t luaC_separateudata(lua_State*L,int all){
|
|
global_State*g=G(L);
|
|
size_t deadmem=0;
|
|
GCObject**p=&g->mainthread->next;
|
|
GCObject*curr;
|
|
while((curr=*p)!=NULL){
|
|
if(!(iswhite(curr)||all)||isfinalized(gco2u(curr)))
|
|
p=&curr->gch.next;
|
|
else if(fasttm(L,gco2u(curr)->metatable,TM_GC)==NULL){
|
|
markfinalized(gco2u(curr));
|
|
p=&curr->gch.next;
|
|
}
|
|
else{
|
|
deadmem+=sizeudata(gco2u(curr));
|
|
markfinalized(gco2u(curr));
|
|
*p=curr->gch.next;
|
|
if(g->tmudata==NULL)
|
|
g->tmudata=curr->gch.next=curr;
|
|
else{
|
|
curr->gch.next=g->tmudata->gch.next;
|
|
g->tmudata->gch.next=curr;
|
|
g->tmudata=curr;
|
|
}
|
|
}
|
|
}
|
|
return deadmem;
|
|
}
|
|
static int traversetable(global_State*g,Table*h){
|
|
int i;
|
|
int weakkey=0;
|
|
int weakvalue=0;
|
|
const TValue*mode;
|
|
if(h->metatable)
|
|
markobject(g,h->metatable);
|
|
mode=gfasttm(g,h->metatable,TM_MODE);
|
|
if(mode&&ttisstring(mode)){
|
|
weakkey=(strchr(svalue(mode),'k')!=NULL);
|
|
weakvalue=(strchr(svalue(mode),'v')!=NULL);
|
|
if(weakkey||weakvalue){
|
|
h->marked&=~(bitmask(3)|bitmask(4));
|
|
h->marked|=cast_byte((weakkey<<3)|
|
|
(weakvalue<<4));
|
|
h->gclist=g->weak;
|
|
g->weak=obj2gco(h);
|
|
}
|
|
}
|
|
if(weakkey&&weakvalue)return 1;
|
|
if(!weakvalue){
|
|
i=h->sizearray;
|
|
while(i--)
|
|
markvalue(g,&h->array[i]);
|
|
}
|
|
i=sizenode(h);
|
|
while(i--){
|
|
Node*n=gnode(h,i);
|
|
if(ttisnil(gval(n)))
|
|
removeentry(n);
|
|
else{
|
|
if(!weakkey)markvalue(g,gkey(n));
|
|
if(!weakvalue)markvalue(g,gval(n));
|
|
}
|
|
}
|
|
return weakkey||weakvalue;
|
|
}
|
|
static void traverseproto(global_State*g,Proto*f){
|
|
int i;
|
|
if(f->source)stringmark(f->source);
|
|
for(i=0;i<f->sizek;i++)
|
|
markvalue(g,&f->k[i]);
|
|
for(i=0;i<f->sizeupvalues;i++){
|
|
if(f->upvalues[i])
|
|
stringmark(f->upvalues[i]);
|
|
}
|
|
for(i=0;i<f->sizep;i++){
|
|
if(f->p[i])
|
|
markobject(g,f->p[i]);
|
|
}
|
|
for(i=0;i<f->sizelocvars;i++){
|
|
if(f->locvars[i].varname)
|
|
stringmark(f->locvars[i].varname);
|
|
}
|
|
}
|
|
static void traverseclosure(global_State*g,Closure*cl){
|
|
markobject(g,cl->c.env);
|
|
if(cl->c.isC){
|
|
int i;
|
|
for(i=0;i<cl->c.nupvalues;i++)
|
|
markvalue(g,&cl->c.upvalue[i]);
|
|
}
|
|
else{
|
|
int i;
|
|
markobject(g,cl->l.p);
|
|
for(i=0;i<cl->l.nupvalues;i++)
|
|
markobject(g,cl->l.upvals[i]);
|
|
}
|
|
}
|
|
static void checkstacksizes(lua_State*L,StkId max){
|
|
int ci_used=cast_int(L->ci-L->base_ci);
|
|
int s_used=cast_int(max-L->stack);
|
|
if(L->size_ci>20000)
|
|
return;
|
|
if(4*ci_used<L->size_ci&&2*8<L->size_ci)
|
|
luaD_reallocCI(L,L->size_ci/2);
|
|
condhardstacktests(luaD_reallocCI(L,ci_used+1));
|
|
if(4*s_used<L->stacksize&&
|
|
2*((2*20)+5)<L->stacksize)
|
|
luaD_reallocstack(L,L->stacksize/2);
|
|
condhardstacktests(luaD_reallocstack(L,s_used));
|
|
}
|
|
static void traversestack(global_State*g,lua_State*l){
|
|
StkId o,lim;
|
|
CallInfo*ci;
|
|
markvalue(g,gt(l));
|
|
lim=l->top;
|
|
for(ci=l->base_ci;ci<=l->ci;ci++){
|
|
if(lim<ci->top)lim=ci->top;
|
|
}
|
|
for(o=l->stack;o<l->top;o++)
|
|
markvalue(g,o);
|
|
for(;o<=lim;o++)
|
|
setnilvalue(o);
|
|
checkstacksizes(l,lim);
|
|
}
|
|
static l_mem propagatemark(global_State*g){
|
|
GCObject*o=g->gray;
|
|
gray2black(o);
|
|
switch(o->gch.tt){
|
|
case 5:{
|
|
Table*h=gco2h(o);
|
|
g->gray=h->gclist;
|
|
if(traversetable(g,h))
|
|
black2gray(o);
|
|
return sizeof(Table)+sizeof(TValue)*h->sizearray+
|
|
sizeof(Node)*sizenode(h);
|
|
}
|
|
case 6:{
|
|
Closure*cl=gco2cl(o);
|
|
g->gray=cl->c.gclist;
|
|
traverseclosure(g,cl);
|
|
return(cl->c.isC)?sizeCclosure(cl->c.nupvalues):
|
|
sizeLclosure(cl->l.nupvalues);
|
|
}
|
|
case 8:{
|
|
lua_State*th=gco2th(o);
|
|
g->gray=th->gclist;
|
|
th->gclist=g->grayagain;
|
|
g->grayagain=o;
|
|
black2gray(o);
|
|
traversestack(g,th);
|
|
return sizeof(lua_State)+sizeof(TValue)*th->stacksize+
|
|
sizeof(CallInfo)*th->size_ci;
|
|
}
|
|
case(8+1):{
|
|
Proto*p=gco2p(o);
|
|
g->gray=p->gclist;
|
|
traverseproto(g,p);
|
|
return sizeof(Proto)+sizeof(Instruction)*p->sizecode+
|
|
sizeof(Proto*)*p->sizep+
|
|
sizeof(TValue)*p->sizek+
|
|
sizeof(int)*p->sizelineinfo+
|
|
sizeof(LocVar)*p->sizelocvars+
|
|
sizeof(TString*)*p->sizeupvalues;
|
|
}
|
|
default:return 0;
|
|
}
|
|
}
|
|
static size_t propagateall(global_State*g){
|
|
size_t m=0;
|
|
while(g->gray)m+=propagatemark(g);
|
|
return m;
|
|
}
|
|
static int iscleared(const TValue*o,int iskey){
|
|
if(!iscollectable(o))return 0;
|
|
if(ttisstring(o)){
|
|
stringmark(rawtsvalue(o));
|
|
return 0;
|
|
}
|
|
return iswhite(gcvalue(o))||
|
|
(ttisuserdata(o)&&(!iskey&&isfinalized(uvalue(o))));
|
|
}
|
|
static void cleartable(GCObject*l){
|
|
while(l){
|
|
Table*h=gco2h(l);
|
|
int i=h->sizearray;
|
|
if(testbit(h->marked,4)){
|
|
while(i--){
|
|
TValue*o=&h->array[i];
|
|
if(iscleared(o,0))
|
|
setnilvalue(o);
|
|
}
|
|
}
|
|
i=sizenode(h);
|
|
while(i--){
|
|
Node*n=gnode(h,i);
|
|
if(!ttisnil(gval(n))&&
|
|
(iscleared(key2tval(n),1)||iscleared(gval(n),0))){
|
|
setnilvalue(gval(n));
|
|
removeentry(n);
|
|
}
|
|
}
|
|
l=h->gclist;
|
|
}
|
|
}
|
|
static void freeobj(lua_State*L,GCObject*o){
|
|
switch(o->gch.tt){
|
|
case(8+1):luaF_freeproto(L,gco2p(o));break;
|
|
case 6:luaF_freeclosure(L,gco2cl(o));break;
|
|
case(8+2):luaF_freeupval(L,gco2uv(o));break;
|
|
case 5:luaH_free(L,gco2h(o));break;
|
|
case 8:{
|
|
luaE_freethread(L,gco2th(o));
|
|
break;
|
|
}
|
|
case 4:{
|
|
G(L)->strt.nuse--;
|
|
luaM_freemem(L,o,sizestring(gco2ts(o)));
|
|
break;
|
|
}
|
|
case 7:{
|
|
luaM_freemem(L,o,sizeudata(gco2u(o)));
|
|
break;
|
|
}
|
|
default:;
|
|
}
|
|
}
|
|
#define sweepwholelist(L,p)sweeplist(L,p,((lu_mem)(~(lu_mem)0)-2))
|
|
static GCObject**sweeplist(lua_State*L,GCObject**p,lu_mem count){
|
|
GCObject*curr;
|
|
global_State*g=G(L);
|
|
int deadmask=otherwhite(g);
|
|
while((curr=*p)!=NULL&&count-->0){
|
|
if(curr->gch.tt==8)
|
|
sweepwholelist(L,&gco2th(curr)->openupval);
|
|
if((curr->gch.marked^bit2mask(0,1))&deadmask){
|
|
makewhite(g,curr);
|
|
p=&curr->gch.next;
|
|
}
|
|
else{
|
|
*p=curr->gch.next;
|
|
if(curr==g->rootgc)
|
|
g->rootgc=curr->gch.next;
|
|
freeobj(L,curr);
|
|
}
|
|
}
|
|
return p;
|
|
}
|
|
static void checkSizes(lua_State*L){
|
|
global_State*g=G(L);
|
|
if(g->strt.nuse<cast(lu_int32,g->strt.size/4)&&
|
|
g->strt.size>32*2)
|
|
luaS_resize(L,g->strt.size/2);
|
|
if(luaZ_sizebuffer(&g->buff)>32*2){
|
|
size_t newsize=luaZ_sizebuffer(&g->buff)/2;
|
|
luaZ_resizebuffer(L,&g->buff,newsize);
|
|
}
|
|
}
|
|
static void GCTM(lua_State*L){
|
|
global_State*g=G(L);
|
|
GCObject*o=g->tmudata->gch.next;
|
|
Udata*udata=rawgco2u(o);
|
|
const TValue*tm;
|
|
if(o==g->tmudata)
|
|
g->tmudata=NULL;
|
|
else
|
|
g->tmudata->gch.next=udata->uv.next;
|
|
udata->uv.next=g->mainthread->next;
|
|
g->mainthread->next=o;
|
|
makewhite(g,o);
|
|
tm=fasttm(L,udata->uv.metatable,TM_GC);
|
|
if(tm!=NULL){
|
|
lu_byte oldah=L->allowhook;
|
|
lu_mem oldt=g->GCthreshold;
|
|
L->allowhook=0;
|
|
g->GCthreshold=2*g->totalbytes;
|
|
setobj(L,L->top,tm);
|
|
setuvalue(L,L->top+1,udata);
|
|
L->top+=2;
|
|
luaD_call(L,L->top-2,0);
|
|
L->allowhook=oldah;
|
|
g->GCthreshold=oldt;
|
|
}
|
|
}
|
|
static void luaC_callGCTM(lua_State*L){
|
|
while(G(L)->tmudata)
|
|
GCTM(L);
|
|
}
|
|
static void luaC_freeall(lua_State*L){
|
|
global_State*g=G(L);
|
|
int i;
|
|
g->currentwhite=bit2mask(0,1)|bitmask(6);
|
|
sweepwholelist(L,&g->rootgc);
|
|
for(i=0;i<g->strt.size;i++)
|
|
sweepwholelist(L,&g->strt.hash[i]);
|
|
}
|
|
static void markmt(global_State*g){
|
|
int i;
|
|
for(i=0;i<(8+1);i++)
|
|
if(g->mt[i])markobject(g,g->mt[i]);
|
|
}
|
|
static void markroot(lua_State*L){
|
|
global_State*g=G(L);
|
|
g->gray=NULL;
|
|
g->grayagain=NULL;
|
|
g->weak=NULL;
|
|
markobject(g,g->mainthread);
|
|
markvalue(g,gt(g->mainthread));
|
|
markvalue(g,registry(L));
|
|
markmt(g);
|
|
g->gcstate=1;
|
|
}
|
|
static void remarkupvals(global_State*g){
|
|
UpVal*uv;
|
|
for(uv=g->uvhead.u.l.next;uv!=&g->uvhead;uv=uv->u.l.next){
|
|
if(isgray(obj2gco(uv)))
|
|
markvalue(g,uv->v);
|
|
}
|
|
}
|
|
static void atomic(lua_State*L){
|
|
global_State*g=G(L);
|
|
size_t udsize;
|
|
remarkupvals(g);
|
|
propagateall(g);
|
|
g->gray=g->weak;
|
|
g->weak=NULL;
|
|
markobject(g,L);
|
|
markmt(g);
|
|
propagateall(g);
|
|
g->gray=g->grayagain;
|
|
g->grayagain=NULL;
|
|
propagateall(g);
|
|
udsize=luaC_separateudata(L,0);
|
|
marktmu(g);
|
|
udsize+=propagateall(g);
|
|
cleartable(g->weak);
|
|
g->currentwhite=cast_byte(otherwhite(g));
|
|
g->sweepstrgc=0;
|
|
g->sweepgc=&g->rootgc;
|
|
g->gcstate=2;
|
|
g->estimate=g->totalbytes-udsize;
|
|
}
|
|
static l_mem singlestep(lua_State*L){
|
|
global_State*g=G(L);
|
|
switch(g->gcstate){
|
|
case 0:{
|
|
markroot(L);
|
|
return 0;
|
|
}
|
|
case 1:{
|
|
if(g->gray)
|
|
return propagatemark(g);
|
|
else{
|
|
atomic(L);
|
|
return 0;
|
|
}
|
|
}
|
|
case 2:{
|
|
lu_mem old=g->totalbytes;
|
|
sweepwholelist(L,&g->strt.hash[g->sweepstrgc++]);
|
|
if(g->sweepstrgc>=g->strt.size)
|
|
g->gcstate=3;
|
|
g->estimate-=old-g->totalbytes;
|
|
return 10;
|
|
}
|
|
case 3:{
|
|
lu_mem old=g->totalbytes;
|
|
g->sweepgc=sweeplist(L,g->sweepgc,40);
|
|
if(*g->sweepgc==NULL){
|
|
checkSizes(L);
|
|
g->gcstate=4;
|
|
}
|
|
g->estimate-=old-g->totalbytes;
|
|
return 40*10;
|
|
}
|
|
case 4:{
|
|
if(g->tmudata){
|
|
GCTM(L);
|
|
if(g->estimate>100)
|
|
g->estimate-=100;
|
|
return 100;
|
|
}
|
|
else{
|
|
g->gcstate=0;
|
|
g->gcdept=0;
|
|
return 0;
|
|
}
|
|
}
|
|
default:return 0;
|
|
}
|
|
}
|
|
static void luaC_step(lua_State*L){
|
|
global_State*g=G(L);
|
|
l_mem lim=(1024u/100)*g->gcstepmul;
|
|
if(lim==0)
|
|
lim=(((lu_mem)(~(lu_mem)0)-2)-1)/2;
|
|
g->gcdept+=g->totalbytes-g->GCthreshold;
|
|
do{
|
|
lim-=singlestep(L);
|
|
if(g->gcstate==0)
|
|
break;
|
|
}while(lim>0);
|
|
if(g->gcstate!=0){
|
|
if(g->gcdept<1024u)
|
|
g->GCthreshold=g->totalbytes+1024u;
|
|
else{
|
|
g->gcdept-=1024u;
|
|
g->GCthreshold=g->totalbytes;
|
|
}
|
|
}
|
|
else{
|
|
setthreshold(g);
|
|
}
|
|
}
|
|
static void luaC_barrierf(lua_State*L,GCObject*o,GCObject*v){
|
|
global_State*g=G(L);
|
|
if(g->gcstate==1)
|
|
reallymarkobject(g,v);
|
|
else
|
|
makewhite(g,o);
|
|
}
|
|
static void luaC_barrierback(lua_State*L,Table*t){
|
|
global_State*g=G(L);
|
|
GCObject*o=obj2gco(t);
|
|
black2gray(o);
|
|
t->gclist=g->grayagain;
|
|
g->grayagain=o;
|
|
}
|
|
static void luaC_link(lua_State*L,GCObject*o,lu_byte tt){
|
|
global_State*g=G(L);
|
|
o->gch.next=g->rootgc;
|
|
g->rootgc=o;
|
|
o->gch.marked=luaC_white(g);
|
|
o->gch.tt=tt;
|
|
}
|
|
static void luaC_linkupval(lua_State*L,UpVal*uv){
|
|
global_State*g=G(L);
|
|
GCObject*o=obj2gco(uv);
|
|
o->gch.next=g->rootgc;
|
|
g->rootgc=o;
|
|
if(isgray(o)){
|
|
if(g->gcstate==1){
|
|
gray2black(o);
|
|
luaC_barrier(L,uv,uv->v);
|
|
}
|
|
else{
|
|
makewhite(g,o);
|
|
}
|
|
}
|
|
}
|
|
typedef union{
|
|
lua_Number r;
|
|
TString*ts;
|
|
}SemInfo;
|
|
typedef struct Token{
|
|
int token;
|
|
SemInfo seminfo;
|
|
}Token;
|
|
typedef struct LexState{
|
|
int current;
|
|
int linenumber;
|
|
int lastline;
|
|
Token t;
|
|
Token lookahead;
|
|
struct FuncState*fs;
|
|
struct lua_State*L;
|
|
ZIO*z;
|
|
Mbuffer*buff;
|
|
TString*source;
|
|
char decpoint;
|
|
}LexState;
|
|
static void luaX_init(lua_State*L);
|
|
static void luaX_lexerror(LexState*ls,const char*msg,int token);
|
|
#define state_size(x)(sizeof(x)+0)
|
|
#define fromstate(l)(cast(lu_byte*,(l))-0)
|
|
#define tostate(l)(cast(lua_State*,cast(lu_byte*,l)+0))
|
|
typedef struct LG{
|
|
lua_State l;
|
|
global_State g;
|
|
}LG;
|
|
static void stack_init(lua_State*L1,lua_State*L){
|
|
L1->base_ci=luaM_newvector(L,8,CallInfo);
|
|
L1->ci=L1->base_ci;
|
|
L1->size_ci=8;
|
|
L1->end_ci=L1->base_ci+L1->size_ci-1;
|
|
L1->stack=luaM_newvector(L,(2*20)+5,TValue);
|
|
L1->stacksize=(2*20)+5;
|
|
L1->top=L1->stack;
|
|
L1->stack_last=L1->stack+(L1->stacksize-5)-1;
|
|
L1->ci->func=L1->top;
|
|
setnilvalue(L1->top++);
|
|
L1->base=L1->ci->base=L1->top;
|
|
L1->ci->top=L1->top+20;
|
|
}
|
|
static void freestack(lua_State*L,lua_State*L1){
|
|
luaM_freearray(L,L1->base_ci,L1->size_ci,CallInfo);
|
|
luaM_freearray(L,L1->stack,L1->stacksize,TValue);
|
|
}
|
|
static void f_luaopen(lua_State*L,void*ud){
|
|
global_State*g=G(L);
|
|
UNUSED(ud);
|
|
stack_init(L,L);
|
|
sethvalue(L,gt(L),luaH_new(L,0,2));
|
|
sethvalue(L,registry(L),luaH_new(L,0,2));
|
|
luaS_resize(L,32);
|
|
luaT_init(L);
|
|
luaX_init(L);
|
|
luaS_fix(luaS_newliteral(L,"not enough memory"));
|
|
g->GCthreshold=4*g->totalbytes;
|
|
}
|
|
static void preinit_state(lua_State*L,global_State*g){
|
|
G(L)=g;
|
|
L->stack=NULL;
|
|
L->stacksize=0;
|
|
L->errorJmp=NULL;
|
|
L->hook=NULL;
|
|
L->hookmask=0;
|
|
L->basehookcount=0;
|
|
L->allowhook=1;
|
|
resethookcount(L);
|
|
L->openupval=NULL;
|
|
L->size_ci=0;
|
|
L->nCcalls=L->baseCcalls=0;
|
|
L->status=0;
|
|
L->base_ci=L->ci=NULL;
|
|
L->savedpc=NULL;
|
|
L->errfunc=0;
|
|
setnilvalue(gt(L));
|
|
}
|
|
static void close_state(lua_State*L){
|
|
global_State*g=G(L);
|
|
luaF_close(L,L->stack);
|
|
luaC_freeall(L);
|
|
luaM_freearray(L,G(L)->strt.hash,G(L)->strt.size,TString*);
|
|
luaZ_freebuffer(L,&g->buff);
|
|
freestack(L,L);
|
|
(*g->frealloc)(g->ud,fromstate(L),state_size(LG),0);
|
|
}
|
|
static void luaE_freethread(lua_State*L,lua_State*L1){
|
|
luaF_close(L1,L1->stack);
|
|
freestack(L,L1);
|
|
luaM_freemem(L,fromstate(L1),state_size(lua_State));
|
|
}
|
|
static lua_State*lua_newstate(lua_Alloc f,void*ud){
|
|
int i;
|
|
lua_State*L;
|
|
global_State*g;
|
|
void*l=(*f)(ud,NULL,0,state_size(LG));
|
|
if(l==NULL)return NULL;
|
|
L=tostate(l);
|
|
g=&((LG*)L)->g;
|
|
L->next=NULL;
|
|
L->tt=8;
|
|
g->currentwhite=bit2mask(0,5);
|
|
L->marked=luaC_white(g);
|
|
set2bits(L->marked,5,6);
|
|
preinit_state(L,g);
|
|
g->frealloc=f;
|
|
g->ud=ud;
|
|
g->mainthread=L;
|
|
g->uvhead.u.l.prev=&g->uvhead;
|
|
g->uvhead.u.l.next=&g->uvhead;
|
|
g->GCthreshold=0;
|
|
g->strt.size=0;
|
|
g->strt.nuse=0;
|
|
g->strt.hash=NULL;
|
|
setnilvalue(registry(L));
|
|
luaZ_initbuffer(L,&g->buff);
|
|
g->panic=NULL;
|
|
g->gcstate=0;
|
|
g->rootgc=obj2gco(L);
|
|
g->sweepstrgc=0;
|
|
g->sweepgc=&g->rootgc;
|
|
g->gray=NULL;
|
|
g->grayagain=NULL;
|
|
g->weak=NULL;
|
|
g->tmudata=NULL;
|
|
g->totalbytes=sizeof(LG);
|
|
g->gcpause=200;
|
|
g->gcstepmul=200;
|
|
g->gcdept=0;
|
|
for(i=0;i<(8+1);i++)g->mt[i]=NULL;
|
|
if(luaD_rawrunprotected(L,f_luaopen,NULL)!=0){
|
|
close_state(L);
|
|
L=NULL;
|
|
}
|
|
else
|
|
{}
|
|
return L;
|
|
}
|
|
static void callallgcTM(lua_State*L,void*ud){
|
|
UNUSED(ud);
|
|
luaC_callGCTM(L);
|
|
}
|
|
static void lua_close(lua_State*L){
|
|
L=G(L)->mainthread;
|
|
luaF_close(L,L->stack);
|
|
luaC_separateudata(L,1);
|
|
L->errfunc=0;
|
|
do{
|
|
L->ci=L->base_ci;
|
|
L->base=L->top=L->ci->base;
|
|
L->nCcalls=L->baseCcalls=0;
|
|
}while(luaD_rawrunprotected(L,callallgcTM,NULL)!=0);
|
|
close_state(L);
|
|
}
|
|
#define getcode(fs,e)((fs)->f->code[(e)->u.s.info])
|
|
#define luaK_codeAsBx(fs,o,A,sBx)luaK_codeABx(fs,o,A,(sBx)+(((1<<(9+9))-1)>>1))
|
|
#define luaK_setmultret(fs,e)luaK_setreturns(fs,e,(-1))
|
|
static int luaK_codeABx(FuncState*fs,OpCode o,int A,unsigned int Bx);
|
|
static int luaK_codeABC(FuncState*fs,OpCode o,int A,int B,int C);
|
|
static void luaK_setreturns(FuncState*fs,expdesc*e,int nresults);
|
|
static void luaK_patchtohere(FuncState*fs,int list);
|
|
static void luaK_concat(FuncState*fs,int*l1,int l2);
|
|
static int currentpc(lua_State*L,CallInfo*ci){
|
|
if(!isLua(ci))return-1;
|
|
if(ci==L->ci)
|
|
ci->savedpc=L->savedpc;
|
|
return pcRel(ci->savedpc,ci_func(ci)->l.p);
|
|
}
|
|
static int currentline(lua_State*L,CallInfo*ci){
|
|
int pc=currentpc(L,ci);
|
|
if(pc<0)
|
|
return-1;
|
|
else
|
|
return getline_(ci_func(ci)->l.p,pc);
|
|
}
|
|
static int lua_getstack(lua_State*L,int level,lua_Debug*ar){
|
|
int status;
|
|
CallInfo*ci;
|
|
for(ci=L->ci;level>0&&ci>L->base_ci;ci--){
|
|
level--;
|
|
if(f_isLua(ci))
|
|
level-=ci->tailcalls;
|
|
}
|
|
if(level==0&&ci>L->base_ci){
|
|
status=1;
|
|
ar->i_ci=cast_int(ci-L->base_ci);
|
|
}
|
|
else if(level<0){
|
|
status=1;
|
|
ar->i_ci=0;
|
|
}
|
|
else status=0;
|
|
return status;
|
|
}
|
|
static Proto*getluaproto(CallInfo*ci){
|
|
return(isLua(ci)?ci_func(ci)->l.p:NULL);
|
|
}
|
|
static void funcinfo(lua_Debug*ar,Closure*cl){
|
|
if(cl->c.isC){
|
|
ar->source="=[C]";
|
|
ar->linedefined=-1;
|
|
ar->lastlinedefined=-1;
|
|
ar->what="C";
|
|
}
|
|
else{
|
|
ar->source=getstr(cl->l.p->source);
|
|
ar->linedefined=cl->l.p->linedefined;
|
|
ar->lastlinedefined=cl->l.p->lastlinedefined;
|
|
ar->what=(ar->linedefined==0)?"main":"Lua";
|
|
}
|
|
luaO_chunkid(ar->short_src,ar->source,60);
|
|
}
|
|
static void info_tailcall(lua_Debug*ar){
|
|
ar->name=ar->namewhat="";
|
|
ar->what="tail";
|
|
ar->lastlinedefined=ar->linedefined=ar->currentline=-1;
|
|
ar->source="=(tail call)";
|
|
luaO_chunkid(ar->short_src,ar->source,60);
|
|
ar->nups=0;
|
|
}
|
|
static void collectvalidlines(lua_State*L,Closure*f){
|
|
if(f==NULL||f->c.isC){
|
|
setnilvalue(L->top);
|
|
}
|
|
else{
|
|
Table*t=luaH_new(L,0,0);
|
|
int*lineinfo=f->l.p->lineinfo;
|
|
int i;
|
|
for(i=0;i<f->l.p->sizelineinfo;i++)
|
|
setbvalue(luaH_setnum(L,t,lineinfo[i]),1);
|
|
sethvalue(L,L->top,t);
|
|
}
|
|
incr_top(L);
|
|
}
|
|
static int auxgetinfo(lua_State*L,const char*what,lua_Debug*ar,
|
|
Closure*f,CallInfo*ci){
|
|
int status=1;
|
|
if(f==NULL){
|
|
info_tailcall(ar);
|
|
return status;
|
|
}
|
|
for(;*what;what++){
|
|
switch(*what){
|
|
case'S':{
|
|
funcinfo(ar,f);
|
|
break;
|
|
}
|
|
case'l':{
|
|
ar->currentline=(ci)?currentline(L,ci):-1;
|
|
break;
|
|
}
|
|
case'u':{
|
|
ar->nups=f->c.nupvalues;
|
|
break;
|
|
}
|
|
case'n':{
|
|
ar->namewhat=(ci)?NULL:NULL;
|
|
if(ar->namewhat==NULL){
|
|
ar->namewhat="";
|
|
ar->name=NULL;
|
|
}
|
|
break;
|
|
}
|
|
case'L':
|
|
case'f':
|
|
break;
|
|
default:status=0;
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
static int lua_getinfo(lua_State*L,const char*what,lua_Debug*ar){
|
|
int status;
|
|
Closure*f=NULL;
|
|
CallInfo*ci=NULL;
|
|
if(*what=='>'){
|
|
StkId func=L->top-1;
|
|
luai_apicheck(L,ttisfunction(func));
|
|
what++;
|
|
f=clvalue(func);
|
|
L->top--;
|
|
}
|
|
else if(ar->i_ci!=0){
|
|
ci=L->base_ci+ar->i_ci;
|
|
f=clvalue(ci->func);
|
|
}
|
|
status=auxgetinfo(L,what,ar,f,ci);
|
|
if(strchr(what,'f')){
|
|
if(f==NULL)setnilvalue(L->top);
|
|
else setclvalue(L,L->top,f);
|
|
incr_top(L);
|
|
}
|
|
if(strchr(what,'L'))
|
|
collectvalidlines(L,f);
|
|
return status;
|
|
}
|
|
static int isinstack(CallInfo*ci,const TValue*o){
|
|
StkId p;
|
|
for(p=ci->base;p<ci->top;p++)
|
|
if(o==p)return 1;
|
|
return 0;
|
|
}
|
|
static void luaG_typeerror(lua_State*L,const TValue*o,const char*op){
|
|
const char*name=NULL;
|
|
const char*t=luaT_typenames[ttype(o)];
|
|
const char*kind=(isinstack(L->ci,o))?
|
|
NULL:
|
|
NULL;
|
|
if(kind)
|
|
luaG_runerror(L,"attempt to %s %s "LUA_QL("%s")" (a %s value)",
|
|
op,kind,name,t);
|
|
else
|
|
luaG_runerror(L,"attempt to %s a %s value",op,t);
|
|
}
|
|
static void luaG_concaterror(lua_State*L,StkId p1,StkId p2){
|
|
if(ttisstring(p1)||ttisnumber(p1))p1=p2;
|
|
luaG_typeerror(L,p1,"concatenate");
|
|
}
|
|
static void luaG_aritherror(lua_State*L,const TValue*p1,const TValue*p2){
|
|
TValue temp;
|
|
if(luaV_tonumber(p1,&temp)==NULL)
|
|
p2=p1;
|
|
luaG_typeerror(L,p2,"perform arithmetic on");
|
|
}
|
|
static int luaG_ordererror(lua_State*L,const TValue*p1,const TValue*p2){
|
|
const char*t1=luaT_typenames[ttype(p1)];
|
|
const char*t2=luaT_typenames[ttype(p2)];
|
|
if(t1[2]==t2[2])
|
|
luaG_runerror(L,"attempt to compare two %s values",t1);
|
|
else
|
|
luaG_runerror(L,"attempt to compare %s with %s",t1,t2);
|
|
return 0;
|
|
}
|
|
static void addinfo(lua_State*L,const char*msg){
|
|
CallInfo*ci=L->ci;
|
|
if(isLua(ci)){
|
|
char buff[60];
|
|
int line=currentline(L,ci);
|
|
luaO_chunkid(buff,getstr(getluaproto(ci)->source),60);
|
|
luaO_pushfstring(L,"%s:%d: %s",buff,line,msg);
|
|
}
|
|
}
|
|
static void luaG_errormsg(lua_State*L){
|
|
if(L->errfunc!=0){
|
|
StkId errfunc=restorestack(L,L->errfunc);
|
|
if(!ttisfunction(errfunc))luaD_throw(L,5);
|
|
setobj(L,L->top,L->top-1);
|
|
setobj(L,L->top-1,errfunc);
|
|
incr_top(L);
|
|
luaD_call(L,L->top-2,1);
|
|
}
|
|
luaD_throw(L,2);
|
|
}
|
|
static void luaG_runerror(lua_State*L,const char*fmt,...){
|
|
va_list argp;
|
|
va_start(argp,fmt);
|
|
addinfo(L,luaO_pushvfstring(L,fmt,argp));
|
|
va_end(argp);
|
|
luaG_errormsg(L);
|
|
}
|
|
static int luaZ_fill(ZIO*z){
|
|
size_t size;
|
|
lua_State*L=z->L;
|
|
const char*buff;
|
|
buff=z->reader(L,z->data,&size);
|
|
if(buff==NULL||size==0)return(-1);
|
|
z->n=size-1;
|
|
z->p=buff;
|
|
return char2int(*(z->p++));
|
|
}
|
|
static void luaZ_init(lua_State*L,ZIO*z,lua_Reader reader,void*data){
|
|
z->L=L;
|
|
z->reader=reader;
|
|
z->data=data;
|
|
z->n=0;
|
|
z->p=NULL;
|
|
}
|
|
static char*luaZ_openspace(lua_State*L,Mbuffer*buff,size_t n){
|
|
if(n>buff->buffsize){
|
|
if(n<32)n=32;
|
|
luaZ_resizebuffer(L,buff,n);
|
|
}
|
|
return buff->buffer;
|
|
}
|
|
#define opmode(t,a,b,c,m)(((t)<<7)|((a)<<6)|((b)<<4)|((c)<<2)|(m))
|
|
static const lu_byte luaP_opmodes[(cast(int,OP_VARARG)+1)]={
|
|
opmode(0,1,OpArgR,OpArgN,iABC)
|
|
,opmode(0,1,OpArgK,OpArgN,iABx)
|
|
,opmode(0,1,OpArgU,OpArgU,iABC)
|
|
,opmode(0,1,OpArgR,OpArgN,iABC)
|
|
,opmode(0,1,OpArgU,OpArgN,iABC)
|
|
,opmode(0,1,OpArgK,OpArgN,iABx)
|
|
,opmode(0,1,OpArgR,OpArgK,iABC)
|
|
,opmode(0,0,OpArgK,OpArgN,iABx)
|
|
,opmode(0,0,OpArgU,OpArgN,iABC)
|
|
,opmode(0,0,OpArgK,OpArgK,iABC)
|
|
,opmode(0,1,OpArgU,OpArgU,iABC)
|
|
,opmode(0,1,OpArgR,OpArgK,iABC)
|
|
,opmode(0,1,OpArgK,OpArgK,iABC)
|
|
,opmode(0,1,OpArgK,OpArgK,iABC)
|
|
,opmode(0,1,OpArgK,OpArgK,iABC)
|
|
,opmode(0,1,OpArgK,OpArgK,iABC)
|
|
,opmode(0,1,OpArgK,OpArgK,iABC)
|
|
,opmode(0,1,OpArgK,OpArgK,iABC)
|
|
,opmode(0,1,OpArgR,OpArgN,iABC)
|
|
,opmode(0,1,OpArgR,OpArgN,iABC)
|
|
,opmode(0,1,OpArgR,OpArgN,iABC)
|
|
,opmode(0,1,OpArgR,OpArgR,iABC)
|
|
,opmode(0,0,OpArgR,OpArgN,iAsBx)
|
|
,opmode(1,0,OpArgK,OpArgK,iABC)
|
|
,opmode(1,0,OpArgK,OpArgK,iABC)
|
|
,opmode(1,0,OpArgK,OpArgK,iABC)
|
|
,opmode(1,1,OpArgR,OpArgU,iABC)
|
|
,opmode(1,1,OpArgR,OpArgU,iABC)
|
|
,opmode(0,1,OpArgU,OpArgU,iABC)
|
|
,opmode(0,1,OpArgU,OpArgU,iABC)
|
|
,opmode(0,0,OpArgU,OpArgN,iABC)
|
|
,opmode(0,1,OpArgR,OpArgN,iAsBx)
|
|
,opmode(0,1,OpArgR,OpArgN,iAsBx)
|
|
,opmode(1,0,OpArgN,OpArgU,iABC)
|
|
,opmode(0,0,OpArgU,OpArgU,iABC)
|
|
,opmode(0,0,OpArgN,OpArgN,iABC)
|
|
,opmode(0,1,OpArgU,OpArgN,iABx)
|
|
,opmode(0,1,OpArgU,OpArgN,iABC)
|
|
};
|
|
#define next(ls)(ls->current=zgetc(ls->z))
|
|
#define currIsNewline(ls)(ls->current=='\n'||ls->current=='\r')
|
|
static const char*const luaX_tokens[]={
|
|
"and","break","do","else","elseif",
|
|
"end","false","for","function","if",
|
|
"in","local","nil","not","or","repeat",
|
|
"return","then","true","until","while",
|
|
"..","...","==",">=","<=","~=",
|
|
"<number>","<name>","<string>","<eof>",
|
|
NULL
|
|
};
|
|
#define save_and_next(ls)(save(ls,ls->current),next(ls))
|
|
static void save(LexState*ls,int c){
|
|
Mbuffer*b=ls->buff;
|
|
if(b->n+1>b->buffsize){
|
|
size_t newsize;
|
|
if(b->buffsize>=((size_t)(~(size_t)0)-2)/2)
|
|
luaX_lexerror(ls,"lexical element too long",0);
|
|
newsize=b->buffsize*2;
|
|
luaZ_resizebuffer(ls->L,b,newsize);
|
|
}
|
|
b->buffer[b->n++]=cast(char,c);
|
|
}
|
|
static void luaX_init(lua_State*L){
|
|
int i;
|
|
for(i=0;i<(cast(int,TK_WHILE-257+1));i++){
|
|
TString*ts=luaS_new(L,luaX_tokens[i]);
|
|
luaS_fix(ts);
|
|
ts->tsv.reserved=cast_byte(i+1);
|
|
}
|
|
}
|
|
static const char*luaX_token2str(LexState*ls,int token){
|
|
if(token<257){
|
|
return(iscntrl(token))?luaO_pushfstring(ls->L,"char(%d)",token):
|
|
luaO_pushfstring(ls->L,"%c",token);
|
|
}
|
|
else
|
|
return luaX_tokens[token-257];
|
|
}
|
|
static const char*txtToken(LexState*ls,int token){
|
|
switch(token){
|
|
case TK_NAME:
|
|
case TK_STRING:
|
|
case TK_NUMBER:
|
|
save(ls,'\0');
|
|
return luaZ_buffer(ls->buff);
|
|
default:
|
|
return luaX_token2str(ls,token);
|
|
}
|
|
}
|
|
static void luaX_lexerror(LexState*ls,const char*msg,int token){
|
|
char buff[80];
|
|
luaO_chunkid(buff,getstr(ls->source),80);
|
|
msg=luaO_pushfstring(ls->L,"%s:%d: %s",buff,ls->linenumber,msg);
|
|
if(token)
|
|
luaO_pushfstring(ls->L,"%s near "LUA_QL("%s"),msg,txtToken(ls,token));
|
|
luaD_throw(ls->L,3);
|
|
}
|
|
static void luaX_syntaxerror(LexState*ls,const char*msg){
|
|
luaX_lexerror(ls,msg,ls->t.token);
|
|
}
|
|
static TString*luaX_newstring(LexState*ls,const char*str,size_t l){
|
|
lua_State*L=ls->L;
|
|
TString*ts=luaS_newlstr(L,str,l);
|
|
TValue*o=luaH_setstr(L,ls->fs->h,ts);
|
|
if(ttisnil(o)){
|
|
setbvalue(o,1);
|
|
luaC_checkGC(L);
|
|
}
|
|
return ts;
|
|
}
|
|
static void inclinenumber(LexState*ls){
|
|
int old=ls->current;
|
|
next(ls);
|
|
if(currIsNewline(ls)&&ls->current!=old)
|
|
next(ls);
|
|
if(++ls->linenumber>=(INT_MAX-2))
|
|
luaX_syntaxerror(ls,"chunk has too many lines");
|
|
}
|
|
static void luaX_setinput(lua_State*L,LexState*ls,ZIO*z,TString*source){
|
|
ls->decpoint='.';
|
|
ls->L=L;
|
|
ls->lookahead.token=TK_EOS;
|
|
ls->z=z;
|
|
ls->fs=NULL;
|
|
ls->linenumber=1;
|
|
ls->lastline=1;
|
|
ls->source=source;
|
|
luaZ_resizebuffer(ls->L,ls->buff,32);
|
|
next(ls);
|
|
}
|
|
static int check_next(LexState*ls,const char*set){
|
|
if(!strchr(set,ls->current))
|
|
return 0;
|
|
save_and_next(ls);
|
|
return 1;
|
|
}
|
|
static void buffreplace(LexState*ls,char from,char to){
|
|
size_t n=luaZ_bufflen(ls->buff);
|
|
char*p=luaZ_buffer(ls->buff);
|
|
while(n--)
|
|
if(p[n]==from)p[n]=to;
|
|
}
|
|
static void read_numeral(LexState*ls,SemInfo*seminfo){
|
|
do{
|
|
save_and_next(ls);
|
|
}while(isdigit(ls->current)||ls->current=='.');
|
|
if(check_next(ls,"Ee"))
|
|
check_next(ls,"+-");
|
|
while(isalnum(ls->current)||ls->current=='_')
|
|
save_and_next(ls);
|
|
save(ls,'\0');
|
|
buffreplace(ls,'.',ls->decpoint);
|
|
if(!luaO_str2d(luaZ_buffer(ls->buff),&seminfo->r))
|
|
luaX_lexerror(ls,"malformed number",TK_NUMBER);
|
|
}
|
|
static int skip_sep(LexState*ls){
|
|
int count=0;
|
|
int s=ls->current;
|
|
save_and_next(ls);
|
|
while(ls->current=='='){
|
|
save_and_next(ls);
|
|
count++;
|
|
}
|
|
return(ls->current==s)?count:(-count)-1;
|
|
}
|
|
static void read_long_string(LexState*ls,SemInfo*seminfo,int sep){
|
|
int cont=0;
|
|
(void)(cont);
|
|
save_and_next(ls);
|
|
if(currIsNewline(ls))
|
|
inclinenumber(ls);
|
|
for(;;){
|
|
switch(ls->current){
|
|
case(-1):
|
|
luaX_lexerror(ls,(seminfo)?"unfinished long string":
|
|
"unfinished long comment",TK_EOS);
|
|
break;
|
|
case']':{
|
|
if(skip_sep(ls)==sep){
|
|
save_and_next(ls);
|
|
goto endloop;
|
|
}
|
|
break;
|
|
}
|
|
case'\n':
|
|
case'\r':{
|
|
save(ls,'\n');
|
|
inclinenumber(ls);
|
|
if(!seminfo)luaZ_resetbuffer(ls->buff);
|
|
break;
|
|
}
|
|
default:{
|
|
if(seminfo)save_and_next(ls);
|
|
else next(ls);
|
|
}
|
|
}
|
|
}endloop:
|
|
if(seminfo)
|
|
seminfo->ts=luaX_newstring(ls,luaZ_buffer(ls->buff)+(2+sep),
|
|
luaZ_bufflen(ls->buff)-2*(2+sep));
|
|
}
|
|
static void read_string(LexState*ls,int del,SemInfo*seminfo){
|
|
save_and_next(ls);
|
|
while(ls->current!=del){
|
|
switch(ls->current){
|
|
case(-1):
|
|
luaX_lexerror(ls,"unfinished string",TK_EOS);
|
|
continue;
|
|
case'\n':
|
|
case'\r':
|
|
luaX_lexerror(ls,"unfinished string",TK_STRING);
|
|
continue;
|
|
case'\\':{
|
|
int c;
|
|
next(ls);
|
|
switch(ls->current){
|
|
case'a':c='\a';break;
|
|
case'b':c='\b';break;
|
|
case'f':c='\f';break;
|
|
case'n':c='\n';break;
|
|
case'r':c='\r';break;
|
|
case't':c='\t';break;
|
|
case'v':c='\v';break;
|
|
case'\n':
|
|
case'\r':save(ls,'\n');inclinenumber(ls);continue;
|
|
case(-1):continue;
|
|
default:{
|
|
if(!isdigit(ls->current))
|
|
save_and_next(ls);
|
|
else{
|
|
int i=0;
|
|
c=0;
|
|
do{
|
|
c=10*c+(ls->current-'0');
|
|
next(ls);
|
|
}while(++i<3&&isdigit(ls->current));
|
|
if(c>UCHAR_MAX)
|
|
luaX_lexerror(ls,"escape sequence too large",TK_STRING);
|
|
save(ls,c);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
save(ls,c);
|
|
next(ls);
|
|
continue;
|
|
}
|
|
default:
|
|
save_and_next(ls);
|
|
}
|
|
}
|
|
save_and_next(ls);
|
|
seminfo->ts=luaX_newstring(ls,luaZ_buffer(ls->buff)+1,
|
|
luaZ_bufflen(ls->buff)-2);
|
|
}
|
|
static int llex(LexState*ls,SemInfo*seminfo){
|
|
luaZ_resetbuffer(ls->buff);
|
|
for(;;){
|
|
switch(ls->current){
|
|
case'\n':
|
|
case'\r':{
|
|
inclinenumber(ls);
|
|
continue;
|
|
}
|
|
case'-':{
|
|
next(ls);
|
|
if(ls->current!='-')return'-';
|
|
next(ls);
|
|
if(ls->current=='['){
|
|
int sep=skip_sep(ls);
|
|
luaZ_resetbuffer(ls->buff);
|
|
if(sep>=0){
|
|
read_long_string(ls,NULL,sep);
|
|
luaZ_resetbuffer(ls->buff);
|
|
continue;
|
|
}
|
|
}
|
|
while(!currIsNewline(ls)&&ls->current!=(-1))
|
|
next(ls);
|
|
continue;
|
|
}
|
|
case'[':{
|
|
int sep=skip_sep(ls);
|
|
if(sep>=0){
|
|
read_long_string(ls,seminfo,sep);
|
|
return TK_STRING;
|
|
}
|
|
else if(sep==-1)return'[';
|
|
else luaX_lexerror(ls,"invalid long string delimiter",TK_STRING);
|
|
}
|
|
case'=':{
|
|
next(ls);
|
|
if(ls->current!='=')return'=';
|
|
else{next(ls);return TK_EQ;}
|
|
}
|
|
case'<':{
|
|
next(ls);
|
|
if(ls->current!='=')return'<';
|
|
else{next(ls);return TK_LE;}
|
|
}
|
|
case'>':{
|
|
next(ls);
|
|
if(ls->current!='=')return'>';
|
|
else{next(ls);return TK_GE;}
|
|
}
|
|
case'~':{
|
|
next(ls);
|
|
if(ls->current!='=')return'~';
|
|
else{next(ls);return TK_NE;}
|
|
}
|
|
case'"':
|
|
case'\'':{
|
|
read_string(ls,ls->current,seminfo);
|
|
return TK_STRING;
|
|
}
|
|
case'.':{
|
|
save_and_next(ls);
|
|
if(check_next(ls,".")){
|
|
if(check_next(ls,"."))
|
|
return TK_DOTS;
|
|
else return TK_CONCAT;
|
|
}
|
|
else if(!isdigit(ls->current))return'.';
|
|
else{
|
|
read_numeral(ls,seminfo);
|
|
return TK_NUMBER;
|
|
}
|
|
}
|
|
case(-1):{
|
|
return TK_EOS;
|
|
}
|
|
default:{
|
|
if(isspace(ls->current)){
|
|
next(ls);
|
|
continue;
|
|
}
|
|
else if(isdigit(ls->current)){
|
|
read_numeral(ls,seminfo);
|
|
return TK_NUMBER;
|
|
}
|
|
else if(isalpha(ls->current)||ls->current=='_'){
|
|
TString*ts;
|
|
do{
|
|
save_and_next(ls);
|
|
}while(isalnum(ls->current)||ls->current=='_');
|
|
ts=luaX_newstring(ls,luaZ_buffer(ls->buff),
|
|
luaZ_bufflen(ls->buff));
|
|
if(ts->tsv.reserved>0)
|
|
return ts->tsv.reserved-1+257;
|
|
else{
|
|
seminfo->ts=ts;
|
|
return TK_NAME;
|
|
}
|
|
}
|
|
else{
|
|
int c=ls->current;
|
|
next(ls);
|
|
return c;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
static void luaX_next(LexState*ls){
|
|
ls->lastline=ls->linenumber;
|
|
if(ls->lookahead.token!=TK_EOS){
|
|
ls->t=ls->lookahead;
|
|
ls->lookahead.token=TK_EOS;
|
|
}
|
|
else
|
|
ls->t.token=llex(ls,&ls->t.seminfo);
|
|
}
|
|
static void luaX_lookahead(LexState*ls){
|
|
ls->lookahead.token=llex(ls,&ls->lookahead.seminfo);
|
|
}
|
|
#define hasjumps(e)((e)->t!=(e)->f)
|
|
static int isnumeral(expdesc*e){
|
|
return(e->k==VKNUM&&e->t==(-1)&&e->f==(-1));
|
|
}
|
|
static void luaK_nil(FuncState*fs,int from,int n){
|
|
Instruction*previous;
|
|
if(fs->pc>fs->lasttarget){
|
|
if(fs->pc==0){
|
|
if(from>=fs->nactvar)
|
|
return;
|
|
}
|
|
else{
|
|
previous=&fs->f->code[fs->pc-1];
|
|
if(GET_OPCODE(*previous)==OP_LOADNIL){
|
|
int pfrom=GETARG_A(*previous);
|
|
int pto=GETARG_B(*previous);
|
|
if(pfrom<=from&&from<=pto+1){
|
|
if(from+n-1>pto)
|
|
SETARG_B(*previous,from+n-1);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
luaK_codeABC(fs,OP_LOADNIL,from,from+n-1,0);
|
|
}
|
|
static int luaK_jump(FuncState*fs){
|
|
int jpc=fs->jpc;
|
|
int j;
|
|
fs->jpc=(-1);
|
|
j=luaK_codeAsBx(fs,OP_JMP,0,(-1));
|
|
luaK_concat(fs,&j,jpc);
|
|
return j;
|
|
}
|
|
static void luaK_ret(FuncState*fs,int first,int nret){
|
|
luaK_codeABC(fs,OP_RETURN,first,nret+1,0);
|
|
}
|
|
static int condjump(FuncState*fs,OpCode op,int A,int B,int C){
|
|
luaK_codeABC(fs,op,A,B,C);
|
|
return luaK_jump(fs);
|
|
}
|
|
static void fixjump(FuncState*fs,int pc,int dest){
|
|
Instruction*jmp=&fs->f->code[pc];
|
|
int offset=dest-(pc+1);
|
|
if(abs(offset)>(((1<<(9+9))-1)>>1))
|
|
luaX_syntaxerror(fs->ls,"control structure too long");
|
|
SETARG_sBx(*jmp,offset);
|
|
}
|
|
static int luaK_getlabel(FuncState*fs){
|
|
fs->lasttarget=fs->pc;
|
|
return fs->pc;
|
|
}
|
|
static int getjump(FuncState*fs,int pc){
|
|
int offset=GETARG_sBx(fs->f->code[pc]);
|
|
if(offset==(-1))
|
|
return(-1);
|
|
else
|
|
return(pc+1)+offset;
|
|
}
|
|
static Instruction*getjumpcontrol(FuncState*fs,int pc){
|
|
Instruction*pi=&fs->f->code[pc];
|
|
if(pc>=1&&testTMode(GET_OPCODE(*(pi-1))))
|
|
return pi-1;
|
|
else
|
|
return pi;
|
|
}
|
|
static int need_value(FuncState*fs,int list){
|
|
for(;list!=(-1);list=getjump(fs,list)){
|
|
Instruction i=*getjumpcontrol(fs,list);
|
|
if(GET_OPCODE(i)!=OP_TESTSET)return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
static int patchtestreg(FuncState*fs,int node,int reg){
|
|
Instruction*i=getjumpcontrol(fs,node);
|
|
if(GET_OPCODE(*i)!=OP_TESTSET)
|
|
return 0;
|
|
if(reg!=((1<<8)-1)&®!=GETARG_B(*i))
|
|
SETARG_A(*i,reg);
|
|
else
|
|
*i=CREATE_ABC(OP_TEST,GETARG_B(*i),0,GETARG_C(*i));
|
|
return 1;
|
|
}
|
|
static void removevalues(FuncState*fs,int list){
|
|
for(;list!=(-1);list=getjump(fs,list))
|
|
patchtestreg(fs,list,((1<<8)-1));
|
|
}
|
|
static void patchlistaux(FuncState*fs,int list,int vtarget,int reg,
|
|
int dtarget){
|
|
while(list!=(-1)){
|
|
int next=getjump(fs,list);
|
|
if(patchtestreg(fs,list,reg))
|
|
fixjump(fs,list,vtarget);
|
|
else
|
|
fixjump(fs,list,dtarget);
|
|
list=next;
|
|
}
|
|
}
|
|
static void dischargejpc(FuncState*fs){
|
|
patchlistaux(fs,fs->jpc,fs->pc,((1<<8)-1),fs->pc);
|
|
fs->jpc=(-1);
|
|
}
|
|
static void luaK_patchlist(FuncState*fs,int list,int target){
|
|
if(target==fs->pc)
|
|
luaK_patchtohere(fs,list);
|
|
else{
|
|
patchlistaux(fs,list,target,((1<<8)-1),target);
|
|
}
|
|
}
|
|
static void luaK_patchtohere(FuncState*fs,int list){
|
|
luaK_getlabel(fs);
|
|
luaK_concat(fs,&fs->jpc,list);
|
|
}
|
|
static void luaK_concat(FuncState*fs,int*l1,int l2){
|
|
if(l2==(-1))return;
|
|
else if(*l1==(-1))
|
|
*l1=l2;
|
|
else{
|
|
int list=*l1;
|
|
int next;
|
|
while((next=getjump(fs,list))!=(-1))
|
|
list=next;
|
|
fixjump(fs,list,l2);
|
|
}
|
|
}
|
|
static void luaK_checkstack(FuncState*fs,int n){
|
|
int newstack=fs->freereg+n;
|
|
if(newstack>fs->f->maxstacksize){
|
|
if(newstack>=250)
|
|
luaX_syntaxerror(fs->ls,"function or expression too complex");
|
|
fs->f->maxstacksize=cast_byte(newstack);
|
|
}
|
|
}
|
|
static void luaK_reserveregs(FuncState*fs,int n){
|
|
luaK_checkstack(fs,n);
|
|
fs->freereg+=n;
|
|
}
|
|
static void freereg(FuncState*fs,int reg){
|
|
if(!ISK(reg)&®>=fs->nactvar){
|
|
fs->freereg--;
|
|
}
|
|
}
|
|
static void freeexp(FuncState*fs,expdesc*e){
|
|
if(e->k==VNONRELOC)
|
|
freereg(fs,e->u.s.info);
|
|
}
|
|
static int addk(FuncState*fs,TValue*k,TValue*v){
|
|
lua_State*L=fs->L;
|
|
TValue*idx=luaH_set(L,fs->h,k);
|
|
Proto*f=fs->f;
|
|
int oldsize=f->sizek;
|
|
if(ttisnumber(idx)){
|
|
return cast_int(nvalue(idx));
|
|
}
|
|
else{
|
|
setnvalue(idx,cast_num(fs->nk));
|
|
luaM_growvector(L,f->k,fs->nk,f->sizek,TValue,
|
|
((1<<(9+9))-1),"constant table overflow");
|
|
while(oldsize<f->sizek)setnilvalue(&f->k[oldsize++]);
|
|
setobj(L,&f->k[fs->nk],v);
|
|
luaC_barrier(L,f,v);
|
|
return fs->nk++;
|
|
}
|
|
}
|
|
static int luaK_stringK(FuncState*fs,TString*s){
|
|
TValue o;
|
|
setsvalue(fs->L,&o,s);
|
|
return addk(fs,&o,&o);
|
|
}
|
|
static int luaK_numberK(FuncState*fs,lua_Number r){
|
|
TValue o;
|
|
setnvalue(&o,r);
|
|
return addk(fs,&o,&o);
|
|
}
|
|
static int boolK(FuncState*fs,int b){
|
|
TValue o;
|
|
setbvalue(&o,b);
|
|
return addk(fs,&o,&o);
|
|
}
|
|
static int nilK(FuncState*fs){
|
|
TValue k,v;
|
|
setnilvalue(&v);
|
|
sethvalue(fs->L,&k,fs->h);
|
|
return addk(fs,&k,&v);
|
|
}
|
|
static void luaK_setreturns(FuncState*fs,expdesc*e,int nresults){
|
|
if(e->k==VCALL){
|
|
SETARG_C(getcode(fs,e),nresults+1);
|
|
}
|
|
else if(e->k==VVARARG){
|
|
SETARG_B(getcode(fs,e),nresults+1);
|
|
SETARG_A(getcode(fs,e),fs->freereg);
|
|
luaK_reserveregs(fs,1);
|
|
}
|
|
}
|
|
static void luaK_setoneret(FuncState*fs,expdesc*e){
|
|
if(e->k==VCALL){
|
|
e->k=VNONRELOC;
|
|
e->u.s.info=GETARG_A(getcode(fs,e));
|
|
}
|
|
else if(e->k==VVARARG){
|
|
SETARG_B(getcode(fs,e),2);
|
|
e->k=VRELOCABLE;
|
|
}
|
|
}
|
|
static void luaK_dischargevars(FuncState*fs,expdesc*e){
|
|
switch(e->k){
|
|
case VLOCAL:{
|
|
e->k=VNONRELOC;
|
|
break;
|
|
}
|
|
case VUPVAL:{
|
|
e->u.s.info=luaK_codeABC(fs,OP_GETUPVAL,0,e->u.s.info,0);
|
|
e->k=VRELOCABLE;
|
|
break;
|
|
}
|
|
case VGLOBAL:{
|
|
e->u.s.info=luaK_codeABx(fs,OP_GETGLOBAL,0,e->u.s.info);
|
|
e->k=VRELOCABLE;
|
|
break;
|
|
}
|
|
case VINDEXED:{
|
|
freereg(fs,e->u.s.aux);
|
|
freereg(fs,e->u.s.info);
|
|
e->u.s.info=luaK_codeABC(fs,OP_GETTABLE,0,e->u.s.info,e->u.s.aux);
|
|
e->k=VRELOCABLE;
|
|
break;
|
|
}
|
|
case VVARARG:
|
|
case VCALL:{
|
|
luaK_setoneret(fs,e);
|
|
break;
|
|
}
|
|
default:break;
|
|
}
|
|
}
|
|
static int code_label(FuncState*fs,int A,int b,int jump){
|
|
luaK_getlabel(fs);
|
|
return luaK_codeABC(fs,OP_LOADBOOL,A,b,jump);
|
|
}
|
|
static void discharge2reg(FuncState*fs,expdesc*e,int reg){
|
|
luaK_dischargevars(fs,e);
|
|
switch(e->k){
|
|
case VNIL:{
|
|
luaK_nil(fs,reg,1);
|
|
break;
|
|
}
|
|
case VFALSE:case VTRUE:{
|
|
luaK_codeABC(fs,OP_LOADBOOL,reg,e->k==VTRUE,0);
|
|
break;
|
|
}
|
|
case VK:{
|
|
luaK_codeABx(fs,OP_LOADK,reg,e->u.s.info);
|
|
break;
|
|
}
|
|
case VKNUM:{
|
|
luaK_codeABx(fs,OP_LOADK,reg,luaK_numberK(fs,e->u.nval));
|
|
break;
|
|
}
|
|
case VRELOCABLE:{
|
|
Instruction*pc=&getcode(fs,e);
|
|
SETARG_A(*pc,reg);
|
|
break;
|
|
}
|
|
case VNONRELOC:{
|
|
if(reg!=e->u.s.info)
|
|
luaK_codeABC(fs,OP_MOVE,reg,e->u.s.info,0);
|
|
break;
|
|
}
|
|
default:{
|
|
return;
|
|
}
|
|
}
|
|
e->u.s.info=reg;
|
|
e->k=VNONRELOC;
|
|
}
|
|
static void discharge2anyreg(FuncState*fs,expdesc*e){
|
|
if(e->k!=VNONRELOC){
|
|
luaK_reserveregs(fs,1);
|
|
discharge2reg(fs,e,fs->freereg-1);
|
|
}
|
|
}
|
|
static void exp2reg(FuncState*fs,expdesc*e,int reg){
|
|
discharge2reg(fs,e,reg);
|
|
if(e->k==VJMP)
|
|
luaK_concat(fs,&e->t,e->u.s.info);
|
|
if(hasjumps(e)){
|
|
int final;
|
|
int p_f=(-1);
|
|
int p_t=(-1);
|
|
if(need_value(fs,e->t)||need_value(fs,e->f)){
|
|
int fj=(e->k==VJMP)?(-1):luaK_jump(fs);
|
|
p_f=code_label(fs,reg,0,1);
|
|
p_t=code_label(fs,reg,1,0);
|
|
luaK_patchtohere(fs,fj);
|
|
}
|
|
final=luaK_getlabel(fs);
|
|
patchlistaux(fs,e->f,final,reg,p_f);
|
|
patchlistaux(fs,e->t,final,reg,p_t);
|
|
}
|
|
e->f=e->t=(-1);
|
|
e->u.s.info=reg;
|
|
e->k=VNONRELOC;
|
|
}
|
|
static void luaK_exp2nextreg(FuncState*fs,expdesc*e){
|
|
luaK_dischargevars(fs,e);
|
|
freeexp(fs,e);
|
|
luaK_reserveregs(fs,1);
|
|
exp2reg(fs,e,fs->freereg-1);
|
|
}
|
|
static int luaK_exp2anyreg(FuncState*fs,expdesc*e){
|
|
luaK_dischargevars(fs,e);
|
|
if(e->k==VNONRELOC){
|
|
if(!hasjumps(e))return e->u.s.info;
|
|
if(e->u.s.info>=fs->nactvar){
|
|
exp2reg(fs,e,e->u.s.info);
|
|
return e->u.s.info;
|
|
}
|
|
}
|
|
luaK_exp2nextreg(fs,e);
|
|
return e->u.s.info;
|
|
}
|
|
static void luaK_exp2val(FuncState*fs,expdesc*e){
|
|
if(hasjumps(e))
|
|
luaK_exp2anyreg(fs,e);
|
|
else
|
|
luaK_dischargevars(fs,e);
|
|
}
|
|
static int luaK_exp2RK(FuncState*fs,expdesc*e){
|
|
luaK_exp2val(fs,e);
|
|
switch(e->k){
|
|
case VKNUM:
|
|
case VTRUE:
|
|
case VFALSE:
|
|
case VNIL:{
|
|
if(fs->nk<=((1<<(9-1))-1)){
|
|
e->u.s.info=(e->k==VNIL)?nilK(fs):
|
|
(e->k==VKNUM)?luaK_numberK(fs,e->u.nval):
|
|
boolK(fs,(e->k==VTRUE));
|
|
e->k=VK;
|
|
return RKASK(e->u.s.info);
|
|
}
|
|
else break;
|
|
}
|
|
case VK:{
|
|
if(e->u.s.info<=((1<<(9-1))-1))
|
|
return RKASK(e->u.s.info);
|
|
else break;
|
|
}
|
|
default:break;
|
|
}
|
|
return luaK_exp2anyreg(fs,e);
|
|
}
|
|
static void luaK_storevar(FuncState*fs,expdesc*var,expdesc*ex){
|
|
switch(var->k){
|
|
case VLOCAL:{
|
|
freeexp(fs,ex);
|
|
exp2reg(fs,ex,var->u.s.info);
|
|
return;
|
|
}
|
|
case VUPVAL:{
|
|
int e=luaK_exp2anyreg(fs,ex);
|
|
luaK_codeABC(fs,OP_SETUPVAL,e,var->u.s.info,0);
|
|
break;
|
|
}
|
|
case VGLOBAL:{
|
|
int e=luaK_exp2anyreg(fs,ex);
|
|
luaK_codeABx(fs,OP_SETGLOBAL,e,var->u.s.info);
|
|
break;
|
|
}
|
|
case VINDEXED:{
|
|
int e=luaK_exp2RK(fs,ex);
|
|
luaK_codeABC(fs,OP_SETTABLE,var->u.s.info,var->u.s.aux,e);
|
|
break;
|
|
}
|
|
default:{
|
|
break;
|
|
}
|
|
}
|
|
freeexp(fs,ex);
|
|
}
|
|
static void luaK_self(FuncState*fs,expdesc*e,expdesc*key){
|
|
int func;
|
|
luaK_exp2anyreg(fs,e);
|
|
freeexp(fs,e);
|
|
func=fs->freereg;
|
|
luaK_reserveregs(fs,2);
|
|
luaK_codeABC(fs,OP_SELF,func,e->u.s.info,luaK_exp2RK(fs,key));
|
|
freeexp(fs,key);
|
|
e->u.s.info=func;
|
|
e->k=VNONRELOC;
|
|
}
|
|
static void invertjump(FuncState*fs,expdesc*e){
|
|
Instruction*pc=getjumpcontrol(fs,e->u.s.info);
|
|
SETARG_A(*pc,!(GETARG_A(*pc)));
|
|
}
|
|
static int jumponcond(FuncState*fs,expdesc*e,int cond){
|
|
if(e->k==VRELOCABLE){
|
|
Instruction ie=getcode(fs,e);
|
|
if(GET_OPCODE(ie)==OP_NOT){
|
|
fs->pc--;
|
|
return condjump(fs,OP_TEST,GETARG_B(ie),0,!cond);
|
|
}
|
|
}
|
|
discharge2anyreg(fs,e);
|
|
freeexp(fs,e);
|
|
return condjump(fs,OP_TESTSET,((1<<8)-1),e->u.s.info,cond);
|
|
}
|
|
static void luaK_goiftrue(FuncState*fs,expdesc*e){
|
|
int pc;
|
|
luaK_dischargevars(fs,e);
|
|
switch(e->k){
|
|
case VK:case VKNUM:case VTRUE:{
|
|
pc=(-1);
|
|
break;
|
|
}
|
|
case VJMP:{
|
|
invertjump(fs,e);
|
|
pc=e->u.s.info;
|
|
break;
|
|
}
|
|
default:{
|
|
pc=jumponcond(fs,e,0);
|
|
break;
|
|
}
|
|
}
|
|
luaK_concat(fs,&e->f,pc);
|
|
luaK_patchtohere(fs,e->t);
|
|
e->t=(-1);
|
|
}
|
|
static void luaK_goiffalse(FuncState*fs,expdesc*e){
|
|
int pc;
|
|
luaK_dischargevars(fs,e);
|
|
switch(e->k){
|
|
case VNIL:case VFALSE:{
|
|
pc=(-1);
|
|
break;
|
|
}
|
|
case VJMP:{
|
|
pc=e->u.s.info;
|
|
break;
|
|
}
|
|
default:{
|
|
pc=jumponcond(fs,e,1);
|
|
break;
|
|
}
|
|
}
|
|
luaK_concat(fs,&e->t,pc);
|
|
luaK_patchtohere(fs,e->f);
|
|
e->f=(-1);
|
|
}
|
|
static void codenot(FuncState*fs,expdesc*e){
|
|
luaK_dischargevars(fs,e);
|
|
switch(e->k){
|
|
case VNIL:case VFALSE:{
|
|
e->k=VTRUE;
|
|
break;
|
|
}
|
|
case VK:case VKNUM:case VTRUE:{
|
|
e->k=VFALSE;
|
|
break;
|
|
}
|
|
case VJMP:{
|
|
invertjump(fs,e);
|
|
break;
|
|
}
|
|
case VRELOCABLE:
|
|
case VNONRELOC:{
|
|
discharge2anyreg(fs,e);
|
|
freeexp(fs,e);
|
|
e->u.s.info=luaK_codeABC(fs,OP_NOT,0,e->u.s.info,0);
|
|
e->k=VRELOCABLE;
|
|
break;
|
|
}
|
|
default:{
|
|
break;
|
|
}
|
|
}
|
|
{int temp=e->f;e->f=e->t;e->t=temp;}
|
|
removevalues(fs,e->f);
|
|
removevalues(fs,e->t);
|
|
}
|
|
static void luaK_indexed(FuncState*fs,expdesc*t,expdesc*k){
|
|
t->u.s.aux=luaK_exp2RK(fs,k);
|
|
t->k=VINDEXED;
|
|
}
|
|
static int constfolding(OpCode op,expdesc*e1,expdesc*e2){
|
|
lua_Number v1,v2,r;
|
|
if(!isnumeral(e1)||!isnumeral(e2))return 0;
|
|
v1=e1->u.nval;
|
|
v2=e2->u.nval;
|
|
switch(op){
|
|
case OP_ADD:r=luai_numadd(v1,v2);break;
|
|
case OP_SUB:r=luai_numsub(v1,v2);break;
|
|
case OP_MUL:r=luai_nummul(v1,v2);break;
|
|
case OP_DIV:
|
|
if(v2==0)return 0;
|
|
r=luai_numdiv(v1,v2);break;
|
|
case OP_MOD:
|
|
if(v2==0)return 0;
|
|
r=luai_nummod(v1,v2);break;
|
|
case OP_POW:r=luai_numpow(v1,v2);break;
|
|
case OP_UNM:r=luai_numunm(v1);break;
|
|
case OP_LEN:return 0;
|
|
default:r=0;break;
|
|
}
|
|
if(luai_numisnan(r))return 0;
|
|
e1->u.nval=r;
|
|
return 1;
|
|
}
|
|
static void codearith(FuncState*fs,OpCode op,expdesc*e1,expdesc*e2){
|
|
if(constfolding(op,e1,e2))
|
|
return;
|
|
else{
|
|
int o2=(op!=OP_UNM&&op!=OP_LEN)?luaK_exp2RK(fs,e2):0;
|
|
int o1=luaK_exp2RK(fs,e1);
|
|
if(o1>o2){
|
|
freeexp(fs,e1);
|
|
freeexp(fs,e2);
|
|
}
|
|
else{
|
|
freeexp(fs,e2);
|
|
freeexp(fs,e1);
|
|
}
|
|
e1->u.s.info=luaK_codeABC(fs,op,0,o1,o2);
|
|
e1->k=VRELOCABLE;
|
|
}
|
|
}
|
|
static void codecomp(FuncState*fs,OpCode op,int cond,expdesc*e1,
|
|
expdesc*e2){
|
|
int o1=luaK_exp2RK(fs,e1);
|
|
int o2=luaK_exp2RK(fs,e2);
|
|
freeexp(fs,e2);
|
|
freeexp(fs,e1);
|
|
if(cond==0&&op!=OP_EQ){
|
|
int temp;
|
|
temp=o1;o1=o2;o2=temp;
|
|
cond=1;
|
|
}
|
|
e1->u.s.info=condjump(fs,op,cond,o1,o2);
|
|
e1->k=VJMP;
|
|
}
|
|
static void luaK_prefix(FuncState*fs,UnOpr op,expdesc*e){
|
|
expdesc e2;
|
|
e2.t=e2.f=(-1);e2.k=VKNUM;e2.u.nval=0;
|
|
switch(op){
|
|
case OPR_MINUS:{
|
|
if(!isnumeral(e))
|
|
luaK_exp2anyreg(fs,e);
|
|
codearith(fs,OP_UNM,e,&e2);
|
|
break;
|
|
}
|
|
case OPR_NOT:codenot(fs,e);break;
|
|
case OPR_LEN:{
|
|
luaK_exp2anyreg(fs,e);
|
|
codearith(fs,OP_LEN,e,&e2);
|
|
break;
|
|
}
|
|
default:;
|
|
}
|
|
}
|
|
static void luaK_infix(FuncState*fs,BinOpr op,expdesc*v){
|
|
switch(op){
|
|
case OPR_AND:{
|
|
luaK_goiftrue(fs,v);
|
|
break;
|
|
}
|
|
case OPR_OR:{
|
|
luaK_goiffalse(fs,v);
|
|
break;
|
|
}
|
|
case OPR_CONCAT:{
|
|
luaK_exp2nextreg(fs,v);
|
|
break;
|
|
}
|
|
case OPR_ADD:case OPR_SUB:case OPR_MUL:case OPR_DIV:
|
|
case OPR_MOD:case OPR_POW:{
|
|
if(!isnumeral(v))luaK_exp2RK(fs,v);
|
|
break;
|
|
}
|
|
default:{
|
|
luaK_exp2RK(fs,v);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
static void luaK_posfix(FuncState*fs,BinOpr op,expdesc*e1,expdesc*e2){
|
|
switch(op){
|
|
case OPR_AND:{
|
|
luaK_dischargevars(fs,e2);
|
|
luaK_concat(fs,&e2->f,e1->f);
|
|
*e1=*e2;
|
|
break;
|
|
}
|
|
case OPR_OR:{
|
|
luaK_dischargevars(fs,e2);
|
|
luaK_concat(fs,&e2->t,e1->t);
|
|
*e1=*e2;
|
|
break;
|
|
}
|
|
case OPR_CONCAT:{
|
|
luaK_exp2val(fs,e2);
|
|
if(e2->k==VRELOCABLE&&GET_OPCODE(getcode(fs,e2))==OP_CONCAT){
|
|
freeexp(fs,e1);
|
|
SETARG_B(getcode(fs,e2),e1->u.s.info);
|
|
e1->k=VRELOCABLE;e1->u.s.info=e2->u.s.info;
|
|
}
|
|
else{
|
|
luaK_exp2nextreg(fs,e2);
|
|
codearith(fs,OP_CONCAT,e1,e2);
|
|
}
|
|
break;
|
|
}
|
|
case OPR_ADD:codearith(fs,OP_ADD,e1,e2);break;
|
|
case OPR_SUB:codearith(fs,OP_SUB,e1,e2);break;
|
|
case OPR_MUL:codearith(fs,OP_MUL,e1,e2);break;
|
|
case OPR_DIV:codearith(fs,OP_DIV,e1,e2);break;
|
|
case OPR_MOD:codearith(fs,OP_MOD,e1,e2);break;
|
|
case OPR_POW:codearith(fs,OP_POW,e1,e2);break;
|
|
case OPR_EQ:codecomp(fs,OP_EQ,1,e1,e2);break;
|
|
case OPR_NE:codecomp(fs,OP_EQ,0,e1,e2);break;
|
|
case OPR_LT:codecomp(fs,OP_LT,1,e1,e2);break;
|
|
case OPR_LE:codecomp(fs,OP_LE,1,e1,e2);break;
|
|
case OPR_GT:codecomp(fs,OP_LT,0,e1,e2);break;
|
|
case OPR_GE:codecomp(fs,OP_LE,0,e1,e2);break;
|
|
default:;
|
|
}
|
|
}
|
|
static void luaK_fixline(FuncState*fs,int line){
|
|
fs->f->lineinfo[fs->pc-1]=line;
|
|
}
|
|
static int luaK_code(FuncState*fs,Instruction i,int line){
|
|
Proto*f=fs->f;
|
|
dischargejpc(fs);
|
|
luaM_growvector(fs->L,f->code,fs->pc,f->sizecode,Instruction,
|
|
(INT_MAX-2),"code size overflow");
|
|
f->code[fs->pc]=i;
|
|
luaM_growvector(fs->L,f->lineinfo,fs->pc,f->sizelineinfo,int,
|
|
(INT_MAX-2),"code size overflow");
|
|
f->lineinfo[fs->pc]=line;
|
|
return fs->pc++;
|
|
}
|
|
static int luaK_codeABC(FuncState*fs,OpCode o,int a,int b,int c){
|
|
return luaK_code(fs,CREATE_ABC(o,a,b,c),fs->ls->lastline);
|
|
}
|
|
static int luaK_codeABx(FuncState*fs,OpCode o,int a,unsigned int bc){
|
|
return luaK_code(fs,CREATE_ABx(o,a,bc),fs->ls->lastline);
|
|
}
|
|
static void luaK_setlist(FuncState*fs,int base,int nelems,int tostore){
|
|
int c=(nelems-1)/50+1;
|
|
int b=(tostore==(-1))?0:tostore;
|
|
if(c<=((1<<9)-1))
|
|
luaK_codeABC(fs,OP_SETLIST,base,b,c);
|
|
else{
|
|
luaK_codeABC(fs,OP_SETLIST,base,b,0);
|
|
luaK_code(fs,cast(Instruction,c),fs->ls->lastline);
|
|
}
|
|
fs->freereg=base+1;
|
|
}
|
|
#define hasmultret(k)((k)==VCALL||(k)==VVARARG)
|
|
#define getlocvar(fs,i)((fs)->f->locvars[(fs)->actvar[i]])
|
|
#define luaY_checklimit(fs,v,l,m)if((v)>(l))errorlimit(fs,l,m)
|
|
typedef struct BlockCnt{
|
|
struct BlockCnt*previous;
|
|
int breaklist;
|
|
lu_byte nactvar;
|
|
lu_byte upval;
|
|
lu_byte isbreakable;
|
|
}BlockCnt;
|
|
static void chunk(LexState*ls);
|
|
static void expr(LexState*ls,expdesc*v);
|
|
static void anchor_token(LexState*ls){
|
|
if(ls->t.token==TK_NAME||ls->t.token==TK_STRING){
|
|
TString*ts=ls->t.seminfo.ts;
|
|
luaX_newstring(ls,getstr(ts),ts->tsv.len);
|
|
}
|
|
}
|
|
static void error_expected(LexState*ls,int token){
|
|
luaX_syntaxerror(ls,
|
|
luaO_pushfstring(ls->L,LUA_QL("%s")" expected",luaX_token2str(ls,token)));
|
|
}
|
|
static void errorlimit(FuncState*fs,int limit,const char*what){
|
|
const char*msg=(fs->f->linedefined==0)?
|
|
luaO_pushfstring(fs->L,"main function has more than %d %s",limit,what):
|
|
luaO_pushfstring(fs->L,"function at line %d has more than %d %s",
|
|
fs->f->linedefined,limit,what);
|
|
luaX_lexerror(fs->ls,msg,0);
|
|
}
|
|
static int testnext(LexState*ls,int c){
|
|
if(ls->t.token==c){
|
|
luaX_next(ls);
|
|
return 1;
|
|
}
|
|
else return 0;
|
|
}
|
|
static void check(LexState*ls,int c){
|
|
if(ls->t.token!=c)
|
|
error_expected(ls,c);
|
|
}
|
|
static void checknext(LexState*ls,int c){
|
|
check(ls,c);
|
|
luaX_next(ls);
|
|
}
|
|
#define check_condition(ls,c,msg){if(!(c))luaX_syntaxerror(ls,msg);}
|
|
static void check_match(LexState*ls,int what,int who,int where){
|
|
if(!testnext(ls,what)){
|
|
if(where==ls->linenumber)
|
|
error_expected(ls,what);
|
|
else{
|
|
luaX_syntaxerror(ls,luaO_pushfstring(ls->L,
|
|
LUA_QL("%s")" expected (to close "LUA_QL("%s")" at line %d)",
|
|
luaX_token2str(ls,what),luaX_token2str(ls,who),where));
|
|
}
|
|
}
|
|
}
|
|
static TString*str_checkname(LexState*ls){
|
|
TString*ts;
|
|
check(ls,TK_NAME);
|
|
ts=ls->t.seminfo.ts;
|
|
luaX_next(ls);
|
|
return ts;
|
|
}
|
|
static void init_exp(expdesc*e,expkind k,int i){
|
|
e->f=e->t=(-1);
|
|
e->k=k;
|
|
e->u.s.info=i;
|
|
}
|
|
static void codestring(LexState*ls,expdesc*e,TString*s){
|
|
init_exp(e,VK,luaK_stringK(ls->fs,s));
|
|
}
|
|
static void checkname(LexState*ls,expdesc*e){
|
|
codestring(ls,e,str_checkname(ls));
|
|
}
|
|
static int registerlocalvar(LexState*ls,TString*varname){
|
|
FuncState*fs=ls->fs;
|
|
Proto*f=fs->f;
|
|
int oldsize=f->sizelocvars;
|
|
luaM_growvector(ls->L,f->locvars,fs->nlocvars,f->sizelocvars,
|
|
LocVar,SHRT_MAX,"too many local variables");
|
|
while(oldsize<f->sizelocvars)f->locvars[oldsize++].varname=NULL;
|
|
f->locvars[fs->nlocvars].varname=varname;
|
|
luaC_objbarrier(ls->L,f,varname);
|
|
return fs->nlocvars++;
|
|
}
|
|
#define new_localvarliteral(ls,v,n)new_localvar(ls,luaX_newstring(ls,""v,(sizeof(v)/sizeof(char))-1),n)
|
|
static void new_localvar(LexState*ls,TString*name,int n){
|
|
FuncState*fs=ls->fs;
|
|
luaY_checklimit(fs,fs->nactvar+n+1,200,"local variables");
|
|
fs->actvar[fs->nactvar+n]=cast(unsigned short,registerlocalvar(ls,name));
|
|
}
|
|
static void adjustlocalvars(LexState*ls,int nvars){
|
|
FuncState*fs=ls->fs;
|
|
fs->nactvar=cast_byte(fs->nactvar+nvars);
|
|
for(;nvars;nvars--){
|
|
getlocvar(fs,fs->nactvar-nvars).startpc=fs->pc;
|
|
}
|
|
}
|
|
static void removevars(LexState*ls,int tolevel){
|
|
FuncState*fs=ls->fs;
|
|
while(fs->nactvar>tolevel)
|
|
getlocvar(fs,--fs->nactvar).endpc=fs->pc;
|
|
}
|
|
static int indexupvalue(FuncState*fs,TString*name,expdesc*v){
|
|
int i;
|
|
Proto*f=fs->f;
|
|
int oldsize=f->sizeupvalues;
|
|
for(i=0;i<f->nups;i++){
|
|
if(fs->upvalues[i].k==v->k&&fs->upvalues[i].info==v->u.s.info){
|
|
return i;
|
|
}
|
|
}
|
|
luaY_checklimit(fs,f->nups+1,60,"upvalues");
|
|
luaM_growvector(fs->L,f->upvalues,f->nups,f->sizeupvalues,
|
|
TString*,(INT_MAX-2),"");
|
|
while(oldsize<f->sizeupvalues)f->upvalues[oldsize++]=NULL;
|
|
f->upvalues[f->nups]=name;
|
|
luaC_objbarrier(fs->L,f,name);
|
|
fs->upvalues[f->nups].k=cast_byte(v->k);
|
|
fs->upvalues[f->nups].info=cast_byte(v->u.s.info);
|
|
return f->nups++;
|
|
}
|
|
static int searchvar(FuncState*fs,TString*n){
|
|
int i;
|
|
for(i=fs->nactvar-1;i>=0;i--){
|
|
if(n==getlocvar(fs,i).varname)
|
|
return i;
|
|
}
|
|
return-1;
|
|
}
|
|
static void markupval(FuncState*fs,int level){
|
|
BlockCnt*bl=fs->bl;
|
|
while(bl&&bl->nactvar>level)bl=bl->previous;
|
|
if(bl)bl->upval=1;
|
|
}
|
|
static int singlevaraux(FuncState*fs,TString*n,expdesc*var,int base){
|
|
if(fs==NULL){
|
|
init_exp(var,VGLOBAL,((1<<8)-1));
|
|
return VGLOBAL;
|
|
}
|
|
else{
|
|
int v=searchvar(fs,n);
|
|
if(v>=0){
|
|
init_exp(var,VLOCAL,v);
|
|
if(!base)
|
|
markupval(fs,v);
|
|
return VLOCAL;
|
|
}
|
|
else{
|
|
if(singlevaraux(fs->prev,n,var,0)==VGLOBAL)
|
|
return VGLOBAL;
|
|
var->u.s.info=indexupvalue(fs,n,var);
|
|
var->k=VUPVAL;
|
|
return VUPVAL;
|
|
}
|
|
}
|
|
}
|
|
static void singlevar(LexState*ls,expdesc*var){
|
|
TString*varname=str_checkname(ls);
|
|
FuncState*fs=ls->fs;
|
|
if(singlevaraux(fs,varname,var,1)==VGLOBAL)
|
|
var->u.s.info=luaK_stringK(fs,varname);
|
|
}
|
|
static void adjust_assign(LexState*ls,int nvars,int nexps,expdesc*e){
|
|
FuncState*fs=ls->fs;
|
|
int extra=nvars-nexps;
|
|
if(hasmultret(e->k)){
|
|
extra++;
|
|
if(extra<0)extra=0;
|
|
luaK_setreturns(fs,e,extra);
|
|
if(extra>1)luaK_reserveregs(fs,extra-1);
|
|
}
|
|
else{
|
|
if(e->k!=VVOID)luaK_exp2nextreg(fs,e);
|
|
if(extra>0){
|
|
int reg=fs->freereg;
|
|
luaK_reserveregs(fs,extra);
|
|
luaK_nil(fs,reg,extra);
|
|
}
|
|
}
|
|
}
|
|
static void enterlevel(LexState*ls){
|
|
if(++ls->L->nCcalls>200)
|
|
luaX_lexerror(ls,"chunk has too many syntax levels",0);
|
|
}
|
|
#define leavelevel(ls)((ls)->L->nCcalls--)
|
|
static void enterblock(FuncState*fs,BlockCnt*bl,lu_byte isbreakable){
|
|
bl->breaklist=(-1);
|
|
bl->isbreakable=isbreakable;
|
|
bl->nactvar=fs->nactvar;
|
|
bl->upval=0;
|
|
bl->previous=fs->bl;
|
|
fs->bl=bl;
|
|
}
|
|
static void leaveblock(FuncState*fs){
|
|
BlockCnt*bl=fs->bl;
|
|
fs->bl=bl->previous;
|
|
removevars(fs->ls,bl->nactvar);
|
|
if(bl->upval)
|
|
luaK_codeABC(fs,OP_CLOSE,bl->nactvar,0,0);
|
|
fs->freereg=fs->nactvar;
|
|
luaK_patchtohere(fs,bl->breaklist);
|
|
}
|
|
static void pushclosure(LexState*ls,FuncState*func,expdesc*v){
|
|
FuncState*fs=ls->fs;
|
|
Proto*f=fs->f;
|
|
int oldsize=f->sizep;
|
|
int i;
|
|
luaM_growvector(ls->L,f->p,fs->np,f->sizep,Proto*,
|
|
((1<<(9+9))-1),"constant table overflow");
|
|
while(oldsize<f->sizep)f->p[oldsize++]=NULL;
|
|
f->p[fs->np++]=func->f;
|
|
luaC_objbarrier(ls->L,f,func->f);
|
|
init_exp(v,VRELOCABLE,luaK_codeABx(fs,OP_CLOSURE,0,fs->np-1));
|
|
for(i=0;i<func->f->nups;i++){
|
|
OpCode o=(func->upvalues[i].k==VLOCAL)?OP_MOVE:OP_GETUPVAL;
|
|
luaK_codeABC(fs,o,0,func->upvalues[i].info,0);
|
|
}
|
|
}
|
|
static void open_func(LexState*ls,FuncState*fs){
|
|
lua_State*L=ls->L;
|
|
Proto*f=luaF_newproto(L);
|
|
fs->f=f;
|
|
fs->prev=ls->fs;
|
|
fs->ls=ls;
|
|
fs->L=L;
|
|
ls->fs=fs;
|
|
fs->pc=0;
|
|
fs->lasttarget=-1;
|
|
fs->jpc=(-1);
|
|
fs->freereg=0;
|
|
fs->nk=0;
|
|
fs->np=0;
|
|
fs->nlocvars=0;
|
|
fs->nactvar=0;
|
|
fs->bl=NULL;
|
|
f->source=ls->source;
|
|
f->maxstacksize=2;
|
|
fs->h=luaH_new(L,0,0);
|
|
sethvalue(L,L->top,fs->h);
|
|
incr_top(L);
|
|
setptvalue(L,L->top,f);
|
|
incr_top(L);
|
|
}
|
|
static void close_func(LexState*ls){
|
|
lua_State*L=ls->L;
|
|
FuncState*fs=ls->fs;
|
|
Proto*f=fs->f;
|
|
removevars(ls,0);
|
|
luaK_ret(fs,0,0);
|
|
luaM_reallocvector(L,f->code,f->sizecode,fs->pc,Instruction);
|
|
f->sizecode=fs->pc;
|
|
luaM_reallocvector(L,f->lineinfo,f->sizelineinfo,fs->pc,int);
|
|
f->sizelineinfo=fs->pc;
|
|
luaM_reallocvector(L,f->k,f->sizek,fs->nk,TValue);
|
|
f->sizek=fs->nk;
|
|
luaM_reallocvector(L,f->p,f->sizep,fs->np,Proto*);
|
|
f->sizep=fs->np;
|
|
luaM_reallocvector(L,f->locvars,f->sizelocvars,fs->nlocvars,LocVar);
|
|
f->sizelocvars=fs->nlocvars;
|
|
luaM_reallocvector(L,f->upvalues,f->sizeupvalues,f->nups,TString*);
|
|
f->sizeupvalues=f->nups;
|
|
ls->fs=fs->prev;
|
|
if(fs)anchor_token(ls);
|
|
L->top-=2;
|
|
}
|
|
static Proto*luaY_parser(lua_State*L,ZIO*z,Mbuffer*buff,const char*name){
|
|
struct LexState lexstate;
|
|
struct FuncState funcstate;
|
|
lexstate.buff=buff;
|
|
luaX_setinput(L,&lexstate,z,luaS_new(L,name));
|
|
open_func(&lexstate,&funcstate);
|
|
funcstate.f->is_vararg=2;
|
|
luaX_next(&lexstate);
|
|
chunk(&lexstate);
|
|
check(&lexstate,TK_EOS);
|
|
close_func(&lexstate);
|
|
return funcstate.f;
|
|
}
|
|
static void field(LexState*ls,expdesc*v){
|
|
FuncState*fs=ls->fs;
|
|
expdesc key;
|
|
luaK_exp2anyreg(fs,v);
|
|
luaX_next(ls);
|
|
checkname(ls,&key);
|
|
luaK_indexed(fs,v,&key);
|
|
}
|
|
static void yindex(LexState*ls,expdesc*v){
|
|
luaX_next(ls);
|
|
expr(ls,v);
|
|
luaK_exp2val(ls->fs,v);
|
|
checknext(ls,']');
|
|
}
|
|
struct ConsControl{
|
|
expdesc v;
|
|
expdesc*t;
|
|
int nh;
|
|
int na;
|
|
int tostore;
|
|
};
|
|
static void recfield(LexState*ls,struct ConsControl*cc){
|
|
FuncState*fs=ls->fs;
|
|
int reg=ls->fs->freereg;
|
|
expdesc key,val;
|
|
int rkkey;
|
|
if(ls->t.token==TK_NAME){
|
|
luaY_checklimit(fs,cc->nh,(INT_MAX-2),"items in a constructor");
|
|
checkname(ls,&key);
|
|
}
|
|
else
|
|
yindex(ls,&key);
|
|
cc->nh++;
|
|
checknext(ls,'=');
|
|
rkkey=luaK_exp2RK(fs,&key);
|
|
expr(ls,&val);
|
|
luaK_codeABC(fs,OP_SETTABLE,cc->t->u.s.info,rkkey,luaK_exp2RK(fs,&val));
|
|
fs->freereg=reg;
|
|
}
|
|
static void closelistfield(FuncState*fs,struct ConsControl*cc){
|
|
if(cc->v.k==VVOID)return;
|
|
luaK_exp2nextreg(fs,&cc->v);
|
|
cc->v.k=VVOID;
|
|
if(cc->tostore==50){
|
|
luaK_setlist(fs,cc->t->u.s.info,cc->na,cc->tostore);
|
|
cc->tostore=0;
|
|
}
|
|
}
|
|
static void lastlistfield(FuncState*fs,struct ConsControl*cc){
|
|
if(cc->tostore==0)return;
|
|
if(hasmultret(cc->v.k)){
|
|
luaK_setmultret(fs,&cc->v);
|
|
luaK_setlist(fs,cc->t->u.s.info,cc->na,(-1));
|
|
cc->na--;
|
|
}
|
|
else{
|
|
if(cc->v.k!=VVOID)
|
|
luaK_exp2nextreg(fs,&cc->v);
|
|
luaK_setlist(fs,cc->t->u.s.info,cc->na,cc->tostore);
|
|
}
|
|
}
|
|
static void listfield(LexState*ls,struct ConsControl*cc){
|
|
expr(ls,&cc->v);
|
|
luaY_checklimit(ls->fs,cc->na,(INT_MAX-2),"items in a constructor");
|
|
cc->na++;
|
|
cc->tostore++;
|
|
}
|
|
static void constructor(LexState*ls,expdesc*t){
|
|
FuncState*fs=ls->fs;
|
|
int line=ls->linenumber;
|
|
int pc=luaK_codeABC(fs,OP_NEWTABLE,0,0,0);
|
|
struct ConsControl cc;
|
|
cc.na=cc.nh=cc.tostore=0;
|
|
cc.t=t;
|
|
init_exp(t,VRELOCABLE,pc);
|
|
init_exp(&cc.v,VVOID,0);
|
|
luaK_exp2nextreg(ls->fs,t);
|
|
checknext(ls,'{');
|
|
do{
|
|
if(ls->t.token=='}')break;
|
|
closelistfield(fs,&cc);
|
|
switch(ls->t.token){
|
|
case TK_NAME:{
|
|
luaX_lookahead(ls);
|
|
if(ls->lookahead.token!='=')
|
|
listfield(ls,&cc);
|
|
else
|
|
recfield(ls,&cc);
|
|
break;
|
|
}
|
|
case'[':{
|
|
recfield(ls,&cc);
|
|
break;
|
|
}
|
|
default:{
|
|
listfield(ls,&cc);
|
|
break;
|
|
}
|
|
}
|
|
}while(testnext(ls,',')||testnext(ls,';'));
|
|
check_match(ls,'}','{',line);
|
|
lastlistfield(fs,&cc);
|
|
SETARG_B(fs->f->code[pc],luaO_int2fb(cc.na));
|
|
SETARG_C(fs->f->code[pc],luaO_int2fb(cc.nh));
|
|
}
|
|
static void parlist(LexState*ls){
|
|
FuncState*fs=ls->fs;
|
|
Proto*f=fs->f;
|
|
int nparams=0;
|
|
f->is_vararg=0;
|
|
if(ls->t.token!=')'){
|
|
do{
|
|
switch(ls->t.token){
|
|
case TK_NAME:{
|
|
new_localvar(ls,str_checkname(ls),nparams++);
|
|
break;
|
|
}
|
|
case TK_DOTS:{
|
|
luaX_next(ls);
|
|
f->is_vararg|=2;
|
|
break;
|
|
}
|
|
default:luaX_syntaxerror(ls,"<name> or "LUA_QL("...")" expected");
|
|
}
|
|
}while(!f->is_vararg&&testnext(ls,','));
|
|
}
|
|
adjustlocalvars(ls,nparams);
|
|
f->numparams=cast_byte(fs->nactvar-(f->is_vararg&1));
|
|
luaK_reserveregs(fs,fs->nactvar);
|
|
}
|
|
static void body(LexState*ls,expdesc*e,int needself,int line){
|
|
FuncState new_fs;
|
|
open_func(ls,&new_fs);
|
|
new_fs.f->linedefined=line;
|
|
checknext(ls,'(');
|
|
if(needself){
|
|
new_localvarliteral(ls,"self",0);
|
|
adjustlocalvars(ls,1);
|
|
}
|
|
parlist(ls);
|
|
checknext(ls,')');
|
|
chunk(ls);
|
|
new_fs.f->lastlinedefined=ls->linenumber;
|
|
check_match(ls,TK_END,TK_FUNCTION,line);
|
|
close_func(ls);
|
|
pushclosure(ls,&new_fs,e);
|
|
}
|
|
static int explist1(LexState*ls,expdesc*v){
|
|
int n=1;
|
|
expr(ls,v);
|
|
while(testnext(ls,',')){
|
|
luaK_exp2nextreg(ls->fs,v);
|
|
expr(ls,v);
|
|
n++;
|
|
}
|
|
return n;
|
|
}
|
|
static void funcargs(LexState*ls,expdesc*f){
|
|
FuncState*fs=ls->fs;
|
|
expdesc args;
|
|
int base,nparams;
|
|
int line=ls->linenumber;
|
|
switch(ls->t.token){
|
|
case'(':{
|
|
if(line!=ls->lastline)
|
|
luaX_syntaxerror(ls,"ambiguous syntax (function call x new statement)");
|
|
luaX_next(ls);
|
|
if(ls->t.token==')')
|
|
args.k=VVOID;
|
|
else{
|
|
explist1(ls,&args);
|
|
luaK_setmultret(fs,&args);
|
|
}
|
|
check_match(ls,')','(',line);
|
|
break;
|
|
}
|
|
case'{':{
|
|
constructor(ls,&args);
|
|
break;
|
|
}
|
|
case TK_STRING:{
|
|
codestring(ls,&args,ls->t.seminfo.ts);
|
|
luaX_next(ls);
|
|
break;
|
|
}
|
|
default:{
|
|
luaX_syntaxerror(ls,"function arguments expected");
|
|
return;
|
|
}
|
|
}
|
|
base=f->u.s.info;
|
|
if(hasmultret(args.k))
|
|
nparams=(-1);
|
|
else{
|
|
if(args.k!=VVOID)
|
|
luaK_exp2nextreg(fs,&args);
|
|
nparams=fs->freereg-(base+1);
|
|
}
|
|
init_exp(f,VCALL,luaK_codeABC(fs,OP_CALL,base,nparams+1,2));
|
|
luaK_fixline(fs,line);
|
|
fs->freereg=base+1;
|
|
}
|
|
static void prefixexp(LexState*ls,expdesc*v){
|
|
switch(ls->t.token){
|
|
case'(':{
|
|
int line=ls->linenumber;
|
|
luaX_next(ls);
|
|
expr(ls,v);
|
|
check_match(ls,')','(',line);
|
|
luaK_dischargevars(ls->fs,v);
|
|
return;
|
|
}
|
|
case TK_NAME:{
|
|
singlevar(ls,v);
|
|
return;
|
|
}
|
|
default:{
|
|
luaX_syntaxerror(ls,"unexpected symbol");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
static void primaryexp(LexState*ls,expdesc*v){
|
|
FuncState*fs=ls->fs;
|
|
prefixexp(ls,v);
|
|
for(;;){
|
|
switch(ls->t.token){
|
|
case'.':{
|
|
field(ls,v);
|
|
break;
|
|
}
|
|
case'[':{
|
|
expdesc key;
|
|
luaK_exp2anyreg(fs,v);
|
|
yindex(ls,&key);
|
|
luaK_indexed(fs,v,&key);
|
|
break;
|
|
}
|
|
case':':{
|
|
expdesc key;
|
|
luaX_next(ls);
|
|
checkname(ls,&key);
|
|
luaK_self(fs,v,&key);
|
|
funcargs(ls,v);
|
|
break;
|
|
}
|
|
case'(':case TK_STRING:case'{':{
|
|
luaK_exp2nextreg(fs,v);
|
|
funcargs(ls,v);
|
|
break;
|
|
}
|
|
default:return;
|
|
}
|
|
}
|
|
}
|
|
static void simpleexp(LexState*ls,expdesc*v){
|
|
switch(ls->t.token){
|
|
case TK_NUMBER:{
|
|
init_exp(v,VKNUM,0);
|
|
v->u.nval=ls->t.seminfo.r;
|
|
break;
|
|
}
|
|
case TK_STRING:{
|
|
codestring(ls,v,ls->t.seminfo.ts);
|
|
break;
|
|
}
|
|
case TK_NIL:{
|
|
init_exp(v,VNIL,0);
|
|
break;
|
|
}
|
|
case TK_TRUE:{
|
|
init_exp(v,VTRUE,0);
|
|
break;
|
|
}
|
|
case TK_FALSE:{
|
|
init_exp(v,VFALSE,0);
|
|
break;
|
|
}
|
|
case TK_DOTS:{
|
|
FuncState*fs=ls->fs;
|
|
check_condition(ls,fs->f->is_vararg,
|
|
"cannot use "LUA_QL("...")" outside a vararg function");
|
|
fs->f->is_vararg&=~4;
|
|
init_exp(v,VVARARG,luaK_codeABC(fs,OP_VARARG,0,1,0));
|
|
break;
|
|
}
|
|
case'{':{
|
|
constructor(ls,v);
|
|
return;
|
|
}
|
|
case TK_FUNCTION:{
|
|
luaX_next(ls);
|
|
body(ls,v,0,ls->linenumber);
|
|
return;
|
|
}
|
|
default:{
|
|
primaryexp(ls,v);
|
|
return;
|
|
}
|
|
}
|
|
luaX_next(ls);
|
|
}
|
|
static UnOpr getunopr(int op){
|
|
switch(op){
|
|
case TK_NOT:return OPR_NOT;
|
|
case'-':return OPR_MINUS;
|
|
case'#':return OPR_LEN;
|
|
default:return OPR_NOUNOPR;
|
|
}
|
|
}
|
|
static BinOpr getbinopr(int op){
|
|
switch(op){
|
|
case'+':return OPR_ADD;
|
|
case'-':return OPR_SUB;
|
|
case'*':return OPR_MUL;
|
|
case'/':return OPR_DIV;
|
|
case'%':return OPR_MOD;
|
|
case'^':return OPR_POW;
|
|
case TK_CONCAT:return OPR_CONCAT;
|
|
case TK_NE:return OPR_NE;
|
|
case TK_EQ:return OPR_EQ;
|
|
case'<':return OPR_LT;
|
|
case TK_LE:return OPR_LE;
|
|
case'>':return OPR_GT;
|
|
case TK_GE:return OPR_GE;
|
|
case TK_AND:return OPR_AND;
|
|
case TK_OR:return OPR_OR;
|
|
default:return OPR_NOBINOPR;
|
|
}
|
|
}
|
|
static const struct{
|
|
lu_byte left;
|
|
lu_byte right;
|
|
}priority[]={
|
|
{6,6},{6,6},{7,7},{7,7},{7,7},
|
|
{10,9},{5,4},
|
|
{3,3},{3,3},
|
|
{3,3},{3,3},{3,3},{3,3},
|
|
{2,2},{1,1}
|
|
};
|
|
static BinOpr subexpr(LexState*ls,expdesc*v,unsigned int limit){
|
|
BinOpr op;
|
|
UnOpr uop;
|
|
enterlevel(ls);
|
|
uop=getunopr(ls->t.token);
|
|
if(uop!=OPR_NOUNOPR){
|
|
luaX_next(ls);
|
|
subexpr(ls,v,8);
|
|
luaK_prefix(ls->fs,uop,v);
|
|
}
|
|
else simpleexp(ls,v);
|
|
op=getbinopr(ls->t.token);
|
|
while(op!=OPR_NOBINOPR&&priority[op].left>limit){
|
|
expdesc v2;
|
|
BinOpr nextop;
|
|
luaX_next(ls);
|
|
luaK_infix(ls->fs,op,v);
|
|
nextop=subexpr(ls,&v2,priority[op].right);
|
|
luaK_posfix(ls->fs,op,v,&v2);
|
|
op=nextop;
|
|
}
|
|
leavelevel(ls);
|
|
return op;
|
|
}
|
|
static void expr(LexState*ls,expdesc*v){
|
|
subexpr(ls,v,0);
|
|
}
|
|
static int block_follow(int token){
|
|
switch(token){
|
|
case TK_ELSE:case TK_ELSEIF:case TK_END:
|
|
case TK_UNTIL:case TK_EOS:
|
|
return 1;
|
|
default:return 0;
|
|
}
|
|
}
|
|
static void block(LexState*ls){
|
|
FuncState*fs=ls->fs;
|
|
BlockCnt bl;
|
|
enterblock(fs,&bl,0);
|
|
chunk(ls);
|
|
leaveblock(fs);
|
|
}
|
|
struct LHS_assign{
|
|
struct LHS_assign*prev;
|
|
expdesc v;
|
|
};
|
|
static void check_conflict(LexState*ls,struct LHS_assign*lh,expdesc*v){
|
|
FuncState*fs=ls->fs;
|
|
int extra=fs->freereg;
|
|
int conflict=0;
|
|
for(;lh;lh=lh->prev){
|
|
if(lh->v.k==VINDEXED){
|
|
if(lh->v.u.s.info==v->u.s.info){
|
|
conflict=1;
|
|
lh->v.u.s.info=extra;
|
|
}
|
|
if(lh->v.u.s.aux==v->u.s.info){
|
|
conflict=1;
|
|
lh->v.u.s.aux=extra;
|
|
}
|
|
}
|
|
}
|
|
if(conflict){
|
|
luaK_codeABC(fs,OP_MOVE,fs->freereg,v->u.s.info,0);
|
|
luaK_reserveregs(fs,1);
|
|
}
|
|
}
|
|
static void assignment(LexState*ls,struct LHS_assign*lh,int nvars){
|
|
expdesc e;
|
|
check_condition(ls,VLOCAL<=lh->v.k&&lh->v.k<=VINDEXED,
|
|
"syntax error");
|
|
if(testnext(ls,',')){
|
|
struct LHS_assign nv;
|
|
nv.prev=lh;
|
|
primaryexp(ls,&nv.v);
|
|
if(nv.v.k==VLOCAL)
|
|
check_conflict(ls,lh,&nv.v);
|
|
luaY_checklimit(ls->fs,nvars,200-ls->L->nCcalls,
|
|
"variables in assignment");
|
|
assignment(ls,&nv,nvars+1);
|
|
}
|
|
else{
|
|
int nexps;
|
|
checknext(ls,'=');
|
|
nexps=explist1(ls,&e);
|
|
if(nexps!=nvars){
|
|
adjust_assign(ls,nvars,nexps,&e);
|
|
if(nexps>nvars)
|
|
ls->fs->freereg-=nexps-nvars;
|
|
}
|
|
else{
|
|
luaK_setoneret(ls->fs,&e);
|
|
luaK_storevar(ls->fs,&lh->v,&e);
|
|
return;
|
|
}
|
|
}
|
|
init_exp(&e,VNONRELOC,ls->fs->freereg-1);
|
|
luaK_storevar(ls->fs,&lh->v,&e);
|
|
}
|
|
static int cond(LexState*ls){
|
|
expdesc v;
|
|
expr(ls,&v);
|
|
if(v.k==VNIL)v.k=VFALSE;
|
|
luaK_goiftrue(ls->fs,&v);
|
|
return v.f;
|
|
}
|
|
static void breakstat(LexState*ls){
|
|
FuncState*fs=ls->fs;
|
|
BlockCnt*bl=fs->bl;
|
|
int upval=0;
|
|
while(bl&&!bl->isbreakable){
|
|
upval|=bl->upval;
|
|
bl=bl->previous;
|
|
}
|
|
if(!bl)
|
|
luaX_syntaxerror(ls,"no loop to break");
|
|
if(upval)
|
|
luaK_codeABC(fs,OP_CLOSE,bl->nactvar,0,0);
|
|
luaK_concat(fs,&bl->breaklist,luaK_jump(fs));
|
|
}
|
|
static void whilestat(LexState*ls,int line){
|
|
FuncState*fs=ls->fs;
|
|
int whileinit;
|
|
int condexit;
|
|
BlockCnt bl;
|
|
luaX_next(ls);
|
|
whileinit=luaK_getlabel(fs);
|
|
condexit=cond(ls);
|
|
enterblock(fs,&bl,1);
|
|
checknext(ls,TK_DO);
|
|
block(ls);
|
|
luaK_patchlist(fs,luaK_jump(fs),whileinit);
|
|
check_match(ls,TK_END,TK_WHILE,line);
|
|
leaveblock(fs);
|
|
luaK_patchtohere(fs,condexit);
|
|
}
|
|
static void repeatstat(LexState*ls,int line){
|
|
int condexit;
|
|
FuncState*fs=ls->fs;
|
|
int repeat_init=luaK_getlabel(fs);
|
|
BlockCnt bl1,bl2;
|
|
enterblock(fs,&bl1,1);
|
|
enterblock(fs,&bl2,0);
|
|
luaX_next(ls);
|
|
chunk(ls);
|
|
check_match(ls,TK_UNTIL,TK_REPEAT,line);
|
|
condexit=cond(ls);
|
|
if(!bl2.upval){
|
|
leaveblock(fs);
|
|
luaK_patchlist(ls->fs,condexit,repeat_init);
|
|
}
|
|
else{
|
|
breakstat(ls);
|
|
luaK_patchtohere(ls->fs,condexit);
|
|
leaveblock(fs);
|
|
luaK_patchlist(ls->fs,luaK_jump(fs),repeat_init);
|
|
}
|
|
leaveblock(fs);
|
|
}
|
|
static int exp1(LexState*ls){
|
|
expdesc e;
|
|
int k;
|
|
expr(ls,&e);
|
|
k=e.k;
|
|
luaK_exp2nextreg(ls->fs,&e);
|
|
return k;
|
|
}
|
|
static void forbody(LexState*ls,int base,int line,int nvars,int isnum){
|
|
BlockCnt bl;
|
|
FuncState*fs=ls->fs;
|
|
int prep,endfor;
|
|
adjustlocalvars(ls,3);
|
|
checknext(ls,TK_DO);
|
|
prep=isnum?luaK_codeAsBx(fs,OP_FORPREP,base,(-1)):luaK_jump(fs);
|
|
enterblock(fs,&bl,0);
|
|
adjustlocalvars(ls,nvars);
|
|
luaK_reserveregs(fs,nvars);
|
|
block(ls);
|
|
leaveblock(fs);
|
|
luaK_patchtohere(fs,prep);
|
|
endfor=(isnum)?luaK_codeAsBx(fs,OP_FORLOOP,base,(-1)):
|
|
luaK_codeABC(fs,OP_TFORLOOP,base,0,nvars);
|
|
luaK_fixline(fs,line);
|
|
luaK_patchlist(fs,(isnum?endfor:luaK_jump(fs)),prep+1);
|
|
}
|
|
static void fornum(LexState*ls,TString*varname,int line){
|
|
FuncState*fs=ls->fs;
|
|
int base=fs->freereg;
|
|
new_localvarliteral(ls,"(for index)",0);
|
|
new_localvarliteral(ls,"(for limit)",1);
|
|
new_localvarliteral(ls,"(for step)",2);
|
|
new_localvar(ls,varname,3);
|
|
checknext(ls,'=');
|
|
exp1(ls);
|
|
checknext(ls,',');
|
|
exp1(ls);
|
|
if(testnext(ls,','))
|
|
exp1(ls);
|
|
else{
|
|
luaK_codeABx(fs,OP_LOADK,fs->freereg,luaK_numberK(fs,1));
|
|
luaK_reserveregs(fs,1);
|
|
}
|
|
forbody(ls,base,line,1,1);
|
|
}
|
|
static void forlist(LexState*ls,TString*indexname){
|
|
FuncState*fs=ls->fs;
|
|
expdesc e;
|
|
int nvars=0;
|
|
int line;
|
|
int base=fs->freereg;
|
|
new_localvarliteral(ls,"(for generator)",nvars++);
|
|
new_localvarliteral(ls,"(for state)",nvars++);
|
|
new_localvarliteral(ls,"(for control)",nvars++);
|
|
new_localvar(ls,indexname,nvars++);
|
|
while(testnext(ls,','))
|
|
new_localvar(ls,str_checkname(ls),nvars++);
|
|
checknext(ls,TK_IN);
|
|
line=ls->linenumber;
|
|
adjust_assign(ls,3,explist1(ls,&e),&e);
|
|
luaK_checkstack(fs,3);
|
|
forbody(ls,base,line,nvars-3,0);
|
|
}
|
|
static void forstat(LexState*ls,int line){
|
|
FuncState*fs=ls->fs;
|
|
TString*varname;
|
|
BlockCnt bl;
|
|
enterblock(fs,&bl,1);
|
|
luaX_next(ls);
|
|
varname=str_checkname(ls);
|
|
switch(ls->t.token){
|
|
case'=':fornum(ls,varname,line);break;
|
|
case',':case TK_IN:forlist(ls,varname);break;
|
|
default:luaX_syntaxerror(ls,LUA_QL("=")" or "LUA_QL("in")" expected");
|
|
}
|
|
check_match(ls,TK_END,TK_FOR,line);
|
|
leaveblock(fs);
|
|
}
|
|
static int test_then_block(LexState*ls){
|
|
int condexit;
|
|
luaX_next(ls);
|
|
condexit=cond(ls);
|
|
checknext(ls,TK_THEN);
|
|
block(ls);
|
|
return condexit;
|
|
}
|
|
static void ifstat(LexState*ls,int line){
|
|
FuncState*fs=ls->fs;
|
|
int flist;
|
|
int escapelist=(-1);
|
|
flist=test_then_block(ls);
|
|
while(ls->t.token==TK_ELSEIF){
|
|
luaK_concat(fs,&escapelist,luaK_jump(fs));
|
|
luaK_patchtohere(fs,flist);
|
|
flist=test_then_block(ls);
|
|
}
|
|
if(ls->t.token==TK_ELSE){
|
|
luaK_concat(fs,&escapelist,luaK_jump(fs));
|
|
luaK_patchtohere(fs,flist);
|
|
luaX_next(ls);
|
|
block(ls);
|
|
}
|
|
else
|
|
luaK_concat(fs,&escapelist,flist);
|
|
luaK_patchtohere(fs,escapelist);
|
|
check_match(ls,TK_END,TK_IF,line);
|
|
}
|
|
static void localfunc(LexState*ls){
|
|
expdesc v,b;
|
|
FuncState*fs=ls->fs;
|
|
new_localvar(ls,str_checkname(ls),0);
|
|
init_exp(&v,VLOCAL,fs->freereg);
|
|
luaK_reserveregs(fs,1);
|
|
adjustlocalvars(ls,1);
|
|
body(ls,&b,0,ls->linenumber);
|
|
luaK_storevar(fs,&v,&b);
|
|
getlocvar(fs,fs->nactvar-1).startpc=fs->pc;
|
|
}
|
|
static void localstat(LexState*ls){
|
|
int nvars=0;
|
|
int nexps;
|
|
expdesc e;
|
|
do{
|
|
new_localvar(ls,str_checkname(ls),nvars++);
|
|
}while(testnext(ls,','));
|
|
if(testnext(ls,'='))
|
|
nexps=explist1(ls,&e);
|
|
else{
|
|
e.k=VVOID;
|
|
nexps=0;
|
|
}
|
|
adjust_assign(ls,nvars,nexps,&e);
|
|
adjustlocalvars(ls,nvars);
|
|
}
|
|
static int funcname(LexState*ls,expdesc*v){
|
|
int needself=0;
|
|
singlevar(ls,v);
|
|
while(ls->t.token=='.')
|
|
field(ls,v);
|
|
if(ls->t.token==':'){
|
|
needself=1;
|
|
field(ls,v);
|
|
}
|
|
return needself;
|
|
}
|
|
static void funcstat(LexState*ls,int line){
|
|
int needself;
|
|
expdesc v,b;
|
|
luaX_next(ls);
|
|
needself=funcname(ls,&v);
|
|
body(ls,&b,needself,line);
|
|
luaK_storevar(ls->fs,&v,&b);
|
|
luaK_fixline(ls->fs,line);
|
|
}
|
|
static void exprstat(LexState*ls){
|
|
FuncState*fs=ls->fs;
|
|
struct LHS_assign v;
|
|
primaryexp(ls,&v.v);
|
|
if(v.v.k==VCALL)
|
|
SETARG_C(getcode(fs,&v.v),1);
|
|
else{
|
|
v.prev=NULL;
|
|
assignment(ls,&v,1);
|
|
}
|
|
}
|
|
static void retstat(LexState*ls){
|
|
FuncState*fs=ls->fs;
|
|
expdesc e;
|
|
int first,nret;
|
|
luaX_next(ls);
|
|
if(block_follow(ls->t.token)||ls->t.token==';')
|
|
first=nret=0;
|
|
else{
|
|
nret=explist1(ls,&e);
|
|
if(hasmultret(e.k)){
|
|
luaK_setmultret(fs,&e);
|
|
if(e.k==VCALL&&nret==1){
|
|
SET_OPCODE(getcode(fs,&e),OP_TAILCALL);
|
|
}
|
|
first=fs->nactvar;
|
|
nret=(-1);
|
|
}
|
|
else{
|
|
if(nret==1)
|
|
first=luaK_exp2anyreg(fs,&e);
|
|
else{
|
|
luaK_exp2nextreg(fs,&e);
|
|
first=fs->nactvar;
|
|
}
|
|
}
|
|
}
|
|
luaK_ret(fs,first,nret);
|
|
}
|
|
static int statement(LexState*ls){
|
|
int line=ls->linenumber;
|
|
switch(ls->t.token){
|
|
case TK_IF:{
|
|
ifstat(ls,line);
|
|
return 0;
|
|
}
|
|
case TK_WHILE:{
|
|
whilestat(ls,line);
|
|
return 0;
|
|
}
|
|
case TK_DO:{
|
|
luaX_next(ls);
|
|
block(ls);
|
|
check_match(ls,TK_END,TK_DO,line);
|
|
return 0;
|
|
}
|
|
case TK_FOR:{
|
|
forstat(ls,line);
|
|
return 0;
|
|
}
|
|
case TK_REPEAT:{
|
|
repeatstat(ls,line);
|
|
return 0;
|
|
}
|
|
case TK_FUNCTION:{
|
|
funcstat(ls,line);
|
|
return 0;
|
|
}
|
|
case TK_LOCAL:{
|
|
luaX_next(ls);
|
|
if(testnext(ls,TK_FUNCTION))
|
|
localfunc(ls);
|
|
else
|
|
localstat(ls);
|
|
return 0;
|
|
}
|
|
case TK_RETURN:{
|
|
retstat(ls);
|
|
return 1;
|
|
}
|
|
case TK_BREAK:{
|
|
luaX_next(ls);
|
|
breakstat(ls);
|
|
return 1;
|
|
}
|
|
default:{
|
|
exprstat(ls);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
static void chunk(LexState*ls){
|
|
int islast=0;
|
|
enterlevel(ls);
|
|
while(!islast&&!block_follow(ls->t.token)){
|
|
islast=statement(ls);
|
|
testnext(ls,';');
|
|
ls->fs->freereg=ls->fs->nactvar;
|
|
}
|
|
leavelevel(ls);
|
|
}
|
|
static const TValue*luaV_tonumber(const TValue*obj,TValue*n){
|
|
lua_Number num;
|
|
if(ttisnumber(obj))return obj;
|
|
if(ttisstring(obj)&&luaO_str2d(svalue(obj),&num)){
|
|
setnvalue(n,num);
|
|
return n;
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
static int luaV_tostring(lua_State*L,StkId obj){
|
|
if(!ttisnumber(obj))
|
|
return 0;
|
|
else{
|
|
char s[32];
|
|
lua_Number n=nvalue(obj);
|
|
lua_number2str(s,n);
|
|
setsvalue(L,obj,luaS_new(L,s));
|
|
return 1;
|
|
}
|
|
}
|
|
static void callTMres(lua_State*L,StkId res,const TValue*f,
|
|
const TValue*p1,const TValue*p2){
|
|
ptrdiff_t result=savestack(L,res);
|
|
setobj(L,L->top,f);
|
|
setobj(L,L->top+1,p1);
|
|
setobj(L,L->top+2,p2);
|
|
luaD_checkstack(L,3);
|
|
L->top+=3;
|
|
luaD_call(L,L->top-3,1);
|
|
res=restorestack(L,result);
|
|
L->top--;
|
|
setobj(L,res,L->top);
|
|
}
|
|
static void callTM(lua_State*L,const TValue*f,const TValue*p1,
|
|
const TValue*p2,const TValue*p3){
|
|
setobj(L,L->top,f);
|
|
setobj(L,L->top+1,p1);
|
|
setobj(L,L->top+2,p2);
|
|
setobj(L,L->top+3,p3);
|
|
luaD_checkstack(L,4);
|
|
L->top+=4;
|
|
luaD_call(L,L->top-4,0);
|
|
}
|
|
static void luaV_gettable(lua_State*L,const TValue*t,TValue*key,StkId val){
|
|
int loop;
|
|
for(loop=0;loop<100;loop++){
|
|
const TValue*tm;
|
|
if(ttistable(t)){
|
|
Table*h=hvalue(t);
|
|
const TValue*res=luaH_get(h,key);
|
|
if(!ttisnil(res)||
|
|
(tm=fasttm(L,h->metatable,TM_INDEX))==NULL){
|
|
setobj(L,val,res);
|
|
return;
|
|
}
|
|
}
|
|
else if(ttisnil(tm=luaT_gettmbyobj(L,t,TM_INDEX)))
|
|
luaG_typeerror(L,t,"index");
|
|
if(ttisfunction(tm)){
|
|
callTMres(L,val,tm,t,key);
|
|
return;
|
|
}
|
|
t=tm;
|
|
}
|
|
luaG_runerror(L,"loop in gettable");
|
|
}
|
|
static void luaV_settable(lua_State*L,const TValue*t,TValue*key,StkId val){
|
|
int loop;
|
|
TValue temp;
|
|
for(loop=0;loop<100;loop++){
|
|
const TValue*tm;
|
|
if(ttistable(t)){
|
|
Table*h=hvalue(t);
|
|
TValue*oldval=luaH_set(L,h,key);
|
|
if(!ttisnil(oldval)||
|
|
(tm=fasttm(L,h->metatable,TM_NEWINDEX))==NULL){
|
|
setobj(L,oldval,val);
|
|
h->flags=0;
|
|
luaC_barriert(L,h,val);
|
|
return;
|
|
}
|
|
}
|
|
else if(ttisnil(tm=luaT_gettmbyobj(L,t,TM_NEWINDEX)))
|
|
luaG_typeerror(L,t,"index");
|
|
if(ttisfunction(tm)){
|
|
callTM(L,tm,t,key,val);
|
|
return;
|
|
}
|
|
setobj(L,&temp,tm);
|
|
t=&temp;
|
|
}
|
|
luaG_runerror(L,"loop in settable");
|
|
}
|
|
static int call_binTM(lua_State*L,const TValue*p1,const TValue*p2,
|
|
StkId res,TMS event){
|
|
const TValue*tm=luaT_gettmbyobj(L,p1,event);
|
|
if(ttisnil(tm))
|
|
tm=luaT_gettmbyobj(L,p2,event);
|
|
if(ttisnil(tm))return 0;
|
|
callTMres(L,res,tm,p1,p2);
|
|
return 1;
|
|
}
|
|
static const TValue*get_compTM(lua_State*L,Table*mt1,Table*mt2,
|
|
TMS event){
|
|
const TValue*tm1=fasttm(L,mt1,event);
|
|
const TValue*tm2;
|
|
if(tm1==NULL)return NULL;
|
|
if(mt1==mt2)return tm1;
|
|
tm2=fasttm(L,mt2,event);
|
|
if(tm2==NULL)return NULL;
|
|
if(luaO_rawequalObj(tm1,tm2))
|
|
return tm1;
|
|
return NULL;
|
|
}
|
|
static int call_orderTM(lua_State*L,const TValue*p1,const TValue*p2,
|
|
TMS event){
|
|
const TValue*tm1=luaT_gettmbyobj(L,p1,event);
|
|
const TValue*tm2;
|
|
if(ttisnil(tm1))return-1;
|
|
tm2=luaT_gettmbyobj(L,p2,event);
|
|
if(!luaO_rawequalObj(tm1,tm2))
|
|
return-1;
|
|
callTMres(L,L->top,tm1,p1,p2);
|
|
return!l_isfalse(L->top);
|
|
}
|
|
static int l_strcmp(const TString*ls,const TString*rs){
|
|
const char*l=getstr(ls);
|
|
size_t ll=ls->tsv.len;
|
|
const char*r=getstr(rs);
|
|
size_t lr=rs->tsv.len;
|
|
for(;;){
|
|
int temp=strcoll(l,r);
|
|
if(temp!=0)return temp;
|
|
else{
|
|
size_t len=strlen(l);
|
|
if(len==lr)
|
|
return(len==ll)?0:1;
|
|
else if(len==ll)
|
|
return-1;
|
|
len++;
|
|
l+=len;ll-=len;r+=len;lr-=len;
|
|
}
|
|
}
|
|
}
|
|
static int luaV_lessthan(lua_State*L,const TValue*l,const TValue*r){
|
|
int res;
|
|
if(ttype(l)!=ttype(r))
|
|
return luaG_ordererror(L,l,r);
|
|
else if(ttisnumber(l))
|
|
return luai_numlt(nvalue(l),nvalue(r));
|
|
else if(ttisstring(l))
|
|
return l_strcmp(rawtsvalue(l),rawtsvalue(r))<0;
|
|
else if((res=call_orderTM(L,l,r,TM_LT))!=-1)
|
|
return res;
|
|
return luaG_ordererror(L,l,r);
|
|
}
|
|
static int lessequal(lua_State*L,const TValue*l,const TValue*r){
|
|
int res;
|
|
if(ttype(l)!=ttype(r))
|
|
return luaG_ordererror(L,l,r);
|
|
else if(ttisnumber(l))
|
|
return luai_numle(nvalue(l),nvalue(r));
|
|
else if(ttisstring(l))
|
|
return l_strcmp(rawtsvalue(l),rawtsvalue(r))<=0;
|
|
else if((res=call_orderTM(L,l,r,TM_LE))!=-1)
|
|
return res;
|
|
else if((res=call_orderTM(L,r,l,TM_LT))!=-1)
|
|
return!res;
|
|
return luaG_ordererror(L,l,r);
|
|
}
|
|
static int luaV_equalval(lua_State*L,const TValue*t1,const TValue*t2){
|
|
const TValue*tm;
|
|
switch(ttype(t1)){
|
|
case 0:return 1;
|
|
case 3:return luai_numeq(nvalue(t1),nvalue(t2));
|
|
case 1:return bvalue(t1)==bvalue(t2);
|
|
case 2:return pvalue(t1)==pvalue(t2);
|
|
case 7:{
|
|
if(uvalue(t1)==uvalue(t2))return 1;
|
|
tm=get_compTM(L,uvalue(t1)->metatable,uvalue(t2)->metatable,
|
|
TM_EQ);
|
|
break;
|
|
}
|
|
case 5:{
|
|
if(hvalue(t1)==hvalue(t2))return 1;
|
|
tm=get_compTM(L,hvalue(t1)->metatable,hvalue(t2)->metatable,TM_EQ);
|
|
break;
|
|
}
|
|
default:return gcvalue(t1)==gcvalue(t2);
|
|
}
|
|
if(tm==NULL)return 0;
|
|
callTMres(L,L->top,tm,t1,t2);
|
|
return!l_isfalse(L->top);
|
|
}
|
|
static void luaV_concat(lua_State*L,int total,int last){
|
|
do{
|
|
StkId top=L->base+last+1;
|
|
int n=2;
|
|
if(!(ttisstring(top-2)||ttisnumber(top-2))||!tostring(L,top-1)){
|
|
if(!call_binTM(L,top-2,top-1,top-2,TM_CONCAT))
|
|
luaG_concaterror(L,top-2,top-1);
|
|
}else if(tsvalue(top-1)->len==0)
|
|
(void)tostring(L,top-2);
|
|
else{
|
|
size_t tl=tsvalue(top-1)->len;
|
|
char*buffer;
|
|
int i;
|
|
for(n=1;n<total&&tostring(L,top-n-1);n++){
|
|
size_t l=tsvalue(top-n-1)->len;
|
|
if(l>=((size_t)(~(size_t)0)-2)-tl)luaG_runerror(L,"string length overflow");
|
|
tl+=l;
|
|
}
|
|
buffer=luaZ_openspace(L,&G(L)->buff,tl);
|
|
tl=0;
|
|
for(i=n;i>0;i--){
|
|
size_t l=tsvalue(top-i)->len;
|
|
memcpy(buffer+tl,svalue(top-i),l);
|
|
tl+=l;
|
|
}
|
|
setsvalue(L,top-n,luaS_newlstr(L,buffer,tl));
|
|
}
|
|
total-=n-1;
|
|
last-=n-1;
|
|
}while(total>1);
|
|
}
|
|
static void Arith(lua_State*L,StkId ra,const TValue*rb,
|
|
const TValue*rc,TMS op){
|
|
TValue tempb,tempc;
|
|
const TValue*b,*c;
|
|
if((b=luaV_tonumber(rb,&tempb))!=NULL&&
|
|
(c=luaV_tonumber(rc,&tempc))!=NULL){
|
|
lua_Number nb=nvalue(b),nc=nvalue(c);
|
|
switch(op){
|
|
case TM_ADD:setnvalue(ra,luai_numadd(nb,nc));break;
|
|
case TM_SUB:setnvalue(ra,luai_numsub(nb,nc));break;
|
|
case TM_MUL:setnvalue(ra,luai_nummul(nb,nc));break;
|
|
case TM_DIV:setnvalue(ra,luai_numdiv(nb,nc));break;
|
|
case TM_MOD:setnvalue(ra,luai_nummod(nb,nc));break;
|
|
case TM_POW:setnvalue(ra,luai_numpow(nb,nc));break;
|
|
case TM_UNM:setnvalue(ra,luai_numunm(nb));break;
|
|
default:break;
|
|
}
|
|
}
|
|
else if(!call_binTM(L,rb,rc,ra,op))
|
|
luaG_aritherror(L,rb,rc);
|
|
}
|
|
#define runtime_check(L,c){if(!(c))break;}
|
|
#define RA(i)(base+GETARG_A(i))
|
|
#define RB(i)check_exp(getBMode(GET_OPCODE(i))==OpArgR,base+GETARG_B(i))
|
|
#define RKB(i)check_exp(getBMode(GET_OPCODE(i))==OpArgK,ISK(GETARG_B(i))?k+INDEXK(GETARG_B(i)):base+GETARG_B(i))
|
|
#define RKC(i)check_exp(getCMode(GET_OPCODE(i))==OpArgK,ISK(GETARG_C(i))?k+INDEXK(GETARG_C(i)):base+GETARG_C(i))
|
|
#define KBx(i)check_exp(getBMode(GET_OPCODE(i))==OpArgK,k+GETARG_Bx(i))
|
|
#define dojump(L,pc,i){(pc)+=(i);}
|
|
#define Protect(x){L->savedpc=pc;{x;};base=L->base;}
|
|
#define arith_op(op,tm){TValue*rb=RKB(i);TValue*rc=RKC(i);if(ttisnumber(rb)&&ttisnumber(rc)){lua_Number nb=nvalue(rb),nc=nvalue(rc);setnvalue(ra,op(nb,nc));}else Protect(Arith(L,ra,rb,rc,tm));}
|
|
static void luaV_execute(lua_State*L,int nexeccalls){
|
|
LClosure*cl;
|
|
StkId base;
|
|
TValue*k;
|
|
const Instruction*pc;
|
|
reentry:
|
|
pc=L->savedpc;
|
|
cl=&clvalue(L->ci->func)->l;
|
|
base=L->base;
|
|
k=cl->p->k;
|
|
for(;;){
|
|
const Instruction i=*pc++;
|
|
StkId ra;
|
|
ra=RA(i);
|
|
switch(GET_OPCODE(i)){
|
|
case OP_MOVE:{
|
|
setobj(L,ra,RB(i));
|
|
continue;
|
|
}
|
|
case OP_LOADK:{
|
|
setobj(L,ra,KBx(i));
|
|
continue;
|
|
}
|
|
case OP_LOADBOOL:{
|
|
setbvalue(ra,GETARG_B(i));
|
|
if(GETARG_C(i))pc++;
|
|
continue;
|
|
}
|
|
case OP_LOADNIL:{
|
|
TValue*rb=RB(i);
|
|
do{
|
|
setnilvalue(rb--);
|
|
}while(rb>=ra);
|
|
continue;
|
|
}
|
|
case OP_GETUPVAL:{
|
|
int b=GETARG_B(i);
|
|
setobj(L,ra,cl->upvals[b]->v);
|
|
continue;
|
|
}
|
|
case OP_GETGLOBAL:{
|
|
TValue g;
|
|
TValue*rb=KBx(i);
|
|
sethvalue(L,&g,cl->env);
|
|
Protect(luaV_gettable(L,&g,rb,ra));
|
|
continue;
|
|
}
|
|
case OP_GETTABLE:{
|
|
Protect(luaV_gettable(L,RB(i),RKC(i),ra));
|
|
continue;
|
|
}
|
|
case OP_SETGLOBAL:{
|
|
TValue g;
|
|
sethvalue(L,&g,cl->env);
|
|
Protect(luaV_settable(L,&g,KBx(i),ra));
|
|
continue;
|
|
}
|
|
case OP_SETUPVAL:{
|
|
UpVal*uv=cl->upvals[GETARG_B(i)];
|
|
setobj(L,uv->v,ra);
|
|
luaC_barrier(L,uv,ra);
|
|
continue;
|
|
}
|
|
case OP_SETTABLE:{
|
|
Protect(luaV_settable(L,ra,RKB(i),RKC(i)));
|
|
continue;
|
|
}
|
|
case OP_NEWTABLE:{
|
|
int b=GETARG_B(i);
|
|
int c=GETARG_C(i);
|
|
sethvalue(L,ra,luaH_new(L,luaO_fb2int(b),luaO_fb2int(c)));
|
|
Protect(luaC_checkGC(L));
|
|
continue;
|
|
}
|
|
case OP_SELF:{
|
|
StkId rb=RB(i);
|
|
setobj(L,ra+1,rb);
|
|
Protect(luaV_gettable(L,rb,RKC(i),ra));
|
|
continue;
|
|
}
|
|
case OP_ADD:{
|
|
arith_op(luai_numadd,TM_ADD);
|
|
continue;
|
|
}
|
|
case OP_SUB:{
|
|
arith_op(luai_numsub,TM_SUB);
|
|
continue;
|
|
}
|
|
case OP_MUL:{
|
|
arith_op(luai_nummul,TM_MUL);
|
|
continue;
|
|
}
|
|
case OP_DIV:{
|
|
arith_op(luai_numdiv,TM_DIV);
|
|
continue;
|
|
}
|
|
case OP_MOD:{
|
|
arith_op(luai_nummod,TM_MOD);
|
|
continue;
|
|
}
|
|
case OP_POW:{
|
|
arith_op(luai_numpow,TM_POW);
|
|
continue;
|
|
}
|
|
case OP_UNM:{
|
|
TValue*rb=RB(i);
|
|
if(ttisnumber(rb)){
|
|
lua_Number nb=nvalue(rb);
|
|
setnvalue(ra,luai_numunm(nb));
|
|
}
|
|
else{
|
|
Protect(Arith(L,ra,rb,rb,TM_UNM));
|
|
}
|
|
continue;
|
|
}
|
|
case OP_NOT:{
|
|
int res=l_isfalse(RB(i));
|
|
setbvalue(ra,res);
|
|
continue;
|
|
}
|
|
case OP_LEN:{
|
|
const TValue*rb=RB(i);
|
|
switch(ttype(rb)){
|
|
case 5:{
|
|
setnvalue(ra,cast_num(luaH_getn(hvalue(rb))));
|
|
break;
|
|
}
|
|
case 4:{
|
|
setnvalue(ra,cast_num(tsvalue(rb)->len));
|
|
break;
|
|
}
|
|
default:{
|
|
Protect(
|
|
if(!call_binTM(L,rb,(&luaO_nilobject_),ra,TM_LEN))
|
|
luaG_typeerror(L,rb,"get length of");
|
|
)
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
case OP_CONCAT:{
|
|
int b=GETARG_B(i);
|
|
int c=GETARG_C(i);
|
|
Protect(luaV_concat(L,c-b+1,c);luaC_checkGC(L));
|
|
setobj(L,RA(i),base+b);
|
|
continue;
|
|
}
|
|
case OP_JMP:{
|
|
dojump(L,pc,GETARG_sBx(i));
|
|
continue;
|
|
}
|
|
case OP_EQ:{
|
|
TValue*rb=RKB(i);
|
|
TValue*rc=RKC(i);
|
|
Protect(
|
|
if(equalobj(L,rb,rc)==GETARG_A(i))
|
|
dojump(L,pc,GETARG_sBx(*pc));
|
|
)
|
|
pc++;
|
|
continue;
|
|
}
|
|
case OP_LT:{
|
|
Protect(
|
|
if(luaV_lessthan(L,RKB(i),RKC(i))==GETARG_A(i))
|
|
dojump(L,pc,GETARG_sBx(*pc));
|
|
)
|
|
pc++;
|
|
continue;
|
|
}
|
|
case OP_LE:{
|
|
Protect(
|
|
if(lessequal(L,RKB(i),RKC(i))==GETARG_A(i))
|
|
dojump(L,pc,GETARG_sBx(*pc));
|
|
)
|
|
pc++;
|
|
continue;
|
|
}
|
|
case OP_TEST:{
|
|
if(l_isfalse(ra)!=GETARG_C(i))
|
|
dojump(L,pc,GETARG_sBx(*pc));
|
|
pc++;
|
|
continue;
|
|
}
|
|
case OP_TESTSET:{
|
|
TValue*rb=RB(i);
|
|
if(l_isfalse(rb)!=GETARG_C(i)){
|
|
setobj(L,ra,rb);
|
|
dojump(L,pc,GETARG_sBx(*pc));
|
|
}
|
|
pc++;
|
|
continue;
|
|
}
|
|
case OP_CALL:{
|
|
int b=GETARG_B(i);
|
|
int nresults=GETARG_C(i)-1;
|
|
if(b!=0)L->top=ra+b;
|
|
L->savedpc=pc;
|
|
switch(luaD_precall(L,ra,nresults)){
|
|
case 0:{
|
|
nexeccalls++;
|
|
goto reentry;
|
|
}
|
|
case 1:{
|
|
if(nresults>=0)L->top=L->ci->top;
|
|
base=L->base;
|
|
continue;
|
|
}
|
|
default:{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
case OP_TAILCALL:{
|
|
int b=GETARG_B(i);
|
|
if(b!=0)L->top=ra+b;
|
|
L->savedpc=pc;
|
|
switch(luaD_precall(L,ra,(-1))){
|
|
case 0:{
|
|
CallInfo*ci=L->ci-1;
|
|
int aux;
|
|
StkId func=ci->func;
|
|
StkId pfunc=(ci+1)->func;
|
|
if(L->openupval)luaF_close(L,ci->base);
|
|
L->base=ci->base=ci->func+((ci+1)->base-pfunc);
|
|
for(aux=0;pfunc+aux<L->top;aux++)
|
|
setobj(L,func+aux,pfunc+aux);
|
|
ci->top=L->top=func+aux;
|
|
ci->savedpc=L->savedpc;
|
|
ci->tailcalls++;
|
|
L->ci--;
|
|
goto reentry;
|
|
}
|
|
case 1:{
|
|
base=L->base;
|
|
continue;
|
|
}
|
|
default:{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
case OP_RETURN:{
|
|
int b=GETARG_B(i);
|
|
if(b!=0)L->top=ra+b-1;
|
|
if(L->openupval)luaF_close(L,base);
|
|
L->savedpc=pc;
|
|
b=luaD_poscall(L,ra);
|
|
if(--nexeccalls==0)
|
|
return;
|
|
else{
|
|
if(b)L->top=L->ci->top;
|
|
goto reentry;
|
|
}
|
|
}
|
|
case OP_FORLOOP:{
|
|
lua_Number step=nvalue(ra+2);
|
|
lua_Number idx=luai_numadd(nvalue(ra),step);
|
|
lua_Number limit=nvalue(ra+1);
|
|
if(luai_numlt(0,step)?luai_numle(idx,limit)
|
|
:luai_numle(limit,idx)){
|
|
dojump(L,pc,GETARG_sBx(i));
|
|
setnvalue(ra,idx);
|
|
setnvalue(ra+3,idx);
|
|
}
|
|
continue;
|
|
}
|
|
case OP_FORPREP:{
|
|
const TValue*init=ra;
|
|
const TValue*plimit=ra+1;
|
|
const TValue*pstep=ra+2;
|
|
L->savedpc=pc;
|
|
if(!tonumber(init,ra))
|
|
luaG_runerror(L,LUA_QL("for")" initial value must be a number");
|
|
else if(!tonumber(plimit,ra+1))
|
|
luaG_runerror(L,LUA_QL("for")" limit must be a number");
|
|
else if(!tonumber(pstep,ra+2))
|
|
luaG_runerror(L,LUA_QL("for")" step must be a number");
|
|
setnvalue(ra,luai_numsub(nvalue(ra),nvalue(pstep)));
|
|
dojump(L,pc,GETARG_sBx(i));
|
|
continue;
|
|
}
|
|
case OP_TFORLOOP:{
|
|
StkId cb=ra+3;
|
|
setobj(L,cb+2,ra+2);
|
|
setobj(L,cb+1,ra+1);
|
|
setobj(L,cb,ra);
|
|
L->top=cb+3;
|
|
Protect(luaD_call(L,cb,GETARG_C(i)));
|
|
L->top=L->ci->top;
|
|
cb=RA(i)+3;
|
|
if(!ttisnil(cb)){
|
|
setobj(L,cb-1,cb);
|
|
dojump(L,pc,GETARG_sBx(*pc));
|
|
}
|
|
pc++;
|
|
continue;
|
|
}
|
|
case OP_SETLIST:{
|
|
int n=GETARG_B(i);
|
|
int c=GETARG_C(i);
|
|
int last;
|
|
Table*h;
|
|
if(n==0){
|
|
n=cast_int(L->top-ra)-1;
|
|
L->top=L->ci->top;
|
|
}
|
|
if(c==0)c=cast_int(*pc++);
|
|
runtime_check(L,ttistable(ra));
|
|
h=hvalue(ra);
|
|
last=((c-1)*50)+n;
|
|
if(last>h->sizearray)
|
|
luaH_resizearray(L,h,last);
|
|
for(;n>0;n--){
|
|
TValue*val=ra+n;
|
|
setobj(L,luaH_setnum(L,h,last--),val);
|
|
luaC_barriert(L,h,val);
|
|
}
|
|
continue;
|
|
}
|
|
case OP_CLOSE:{
|
|
luaF_close(L,ra);
|
|
continue;
|
|
}
|
|
case OP_CLOSURE:{
|
|
Proto*p;
|
|
Closure*ncl;
|
|
int nup,j;
|
|
p=cl->p->p[GETARG_Bx(i)];
|
|
nup=p->nups;
|
|
ncl=luaF_newLclosure(L,nup,cl->env);
|
|
ncl->l.p=p;
|
|
for(j=0;j<nup;j++,pc++){
|
|
if(GET_OPCODE(*pc)==OP_GETUPVAL)
|
|
ncl->l.upvals[j]=cl->upvals[GETARG_B(*pc)];
|
|
else{
|
|
ncl->l.upvals[j]=luaF_findupval(L,base+GETARG_B(*pc));
|
|
}
|
|
}
|
|
setclvalue(L,ra,ncl);
|
|
Protect(luaC_checkGC(L));
|
|
continue;
|
|
}
|
|
case OP_VARARG:{
|
|
int b=GETARG_B(i)-1;
|
|
int j;
|
|
CallInfo*ci=L->ci;
|
|
int n=cast_int(ci->base-ci->func)-cl->p->numparams-1;
|
|
if(b==(-1)){
|
|
Protect(luaD_checkstack(L,n));
|
|
ra=RA(i);
|
|
b=n;
|
|
L->top=ra+n;
|
|
}
|
|
for(j=0;j<b;j++){
|
|
if(j<n){
|
|
setobj(L,ra+j,ci->base-n+j);
|
|
}
|
|
else{
|
|
setnilvalue(ra+j);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#define api_checknelems(L,n)luai_apicheck(L,(n)<=(L->top-L->base))
|
|
#define api_checkvalidindex(L,i)luai_apicheck(L,(i)!=(&luaO_nilobject_))
|
|
#define api_incr_top(L){luai_apicheck(L,L->top<L->ci->top);L->top++;}
|
|
static TValue*index2adr(lua_State*L,int idx){
|
|
if(idx>0){
|
|
TValue*o=L->base+(idx-1);
|
|
luai_apicheck(L,idx<=L->ci->top-L->base);
|
|
if(o>=L->top)return cast(TValue*,(&luaO_nilobject_));
|
|
else return o;
|
|
}
|
|
else if(idx>(-10000)){
|
|
luai_apicheck(L,idx!=0&&-idx<=L->top-L->base);
|
|
return L->top+idx;
|
|
}
|
|
else switch(idx){
|
|
case(-10000):return registry(L);
|
|
case(-10001):{
|
|
Closure*func=curr_func(L);
|
|
sethvalue(L,&L->env,func->c.env);
|
|
return&L->env;
|
|
}
|
|
case(-10002):return gt(L);
|
|
default:{
|
|
Closure*func=curr_func(L);
|
|
idx=(-10002)-idx;
|
|
return(idx<=func->c.nupvalues)
|
|
?&func->c.upvalue[idx-1]
|
|
:cast(TValue*,(&luaO_nilobject_));
|
|
}
|
|
}
|
|
}
|
|
static Table*getcurrenv(lua_State*L){
|
|
if(L->ci==L->base_ci)
|
|
return hvalue(gt(L));
|
|
else{
|
|
Closure*func=curr_func(L);
|
|
return func->c.env;
|
|
}
|
|
}
|
|
static int lua_checkstack(lua_State*L,int size){
|
|
int res=1;
|
|
if(size>8000||(L->top-L->base+size)>8000)
|
|
res=0;
|
|
else if(size>0){
|
|
luaD_checkstack(L,size);
|
|
if(L->ci->top<L->top+size)
|
|
L->ci->top=L->top+size;
|
|
}
|
|
return res;
|
|
}
|
|
static lua_CFunction lua_atpanic(lua_State*L,lua_CFunction panicf){
|
|
lua_CFunction old;
|
|
old=G(L)->panic;
|
|
G(L)->panic=panicf;
|
|
return old;
|
|
}
|
|
static int lua_gettop(lua_State*L){
|
|
return cast_int(L->top-L->base);
|
|
}
|
|
static void lua_settop(lua_State*L,int idx){
|
|
if(idx>=0){
|
|
luai_apicheck(L,idx<=L->stack_last-L->base);
|
|
while(L->top<L->base+idx)
|
|
setnilvalue(L->top++);
|
|
L->top=L->base+idx;
|
|
}
|
|
else{
|
|
luai_apicheck(L,-(idx+1)<=(L->top-L->base));
|
|
L->top+=idx+1;
|
|
}
|
|
}
|
|
static void lua_remove(lua_State*L,int idx){
|
|
StkId p;
|
|
p=index2adr(L,idx);
|
|
api_checkvalidindex(L,p);
|
|
while(++p<L->top)setobj(L,p-1,p);
|
|
L->top--;
|
|
}
|
|
static void lua_insert(lua_State*L,int idx){
|
|
StkId p;
|
|
StkId q;
|
|
p=index2adr(L,idx);
|
|
api_checkvalidindex(L,p);
|
|
for(q=L->top;q>p;q--)setobj(L,q,q-1);
|
|
setobj(L,p,L->top);
|
|
}
|
|
static void lua_replace(lua_State*L,int idx){
|
|
StkId o;
|
|
if(idx==(-10001)&&L->ci==L->base_ci)
|
|
luaG_runerror(L,"no calling environment");
|
|
api_checknelems(L,1);
|
|
o=index2adr(L,idx);
|
|
api_checkvalidindex(L,o);
|
|
if(idx==(-10001)){
|
|
Closure*func=curr_func(L);
|
|
luai_apicheck(L,ttistable(L->top-1));
|
|
func->c.env=hvalue(L->top-1);
|
|
luaC_barrier(L,func,L->top-1);
|
|
}
|
|
else{
|
|
setobj(L,o,L->top-1);
|
|
if(idx<(-10002))
|
|
luaC_barrier(L,curr_func(L),L->top-1);
|
|
}
|
|
L->top--;
|
|
}
|
|
static void lua_pushvalue(lua_State*L,int idx){
|
|
setobj(L,L->top,index2adr(L,idx));
|
|
api_incr_top(L);
|
|
}
|
|
static int lua_type(lua_State*L,int idx){
|
|
StkId o=index2adr(L,idx);
|
|
return(o==(&luaO_nilobject_))?(-1):ttype(o);
|
|
}
|
|
static const char*lua_typename(lua_State*L,int t){
|
|
UNUSED(L);
|
|
return(t==(-1))?"no value":luaT_typenames[t];
|
|
}
|
|
static int lua_iscfunction(lua_State*L,int idx){
|
|
StkId o=index2adr(L,idx);
|
|
return iscfunction(o);
|
|
}
|
|
static int lua_isnumber(lua_State*L,int idx){
|
|
TValue n;
|
|
const TValue*o=index2adr(L,idx);
|
|
return tonumber(o,&n);
|
|
}
|
|
static int lua_isstring(lua_State*L,int idx){
|
|
int t=lua_type(L,idx);
|
|
return(t==4||t==3);
|
|
}
|
|
static int lua_rawequal(lua_State*L,int index1,int index2){
|
|
StkId o1=index2adr(L,index1);
|
|
StkId o2=index2adr(L,index2);
|
|
return(o1==(&luaO_nilobject_)||o2==(&luaO_nilobject_))?0
|
|
:luaO_rawequalObj(o1,o2);
|
|
}
|
|
static int lua_lessthan(lua_State*L,int index1,int index2){
|
|
StkId o1,o2;
|
|
int i;
|
|
o1=index2adr(L,index1);
|
|
o2=index2adr(L,index2);
|
|
i=(o1==(&luaO_nilobject_)||o2==(&luaO_nilobject_))?0
|
|
:luaV_lessthan(L,o1,o2);
|
|
return i;
|
|
}
|
|
static lua_Number lua_tonumber(lua_State*L,int idx){
|
|
TValue n;
|
|
const TValue*o=index2adr(L,idx);
|
|
if(tonumber(o,&n))
|
|
return nvalue(o);
|
|
else
|
|
return 0;
|
|
}
|
|
static lua_Integer lua_tointeger(lua_State*L,int idx){
|
|
TValue n;
|
|
const TValue*o=index2adr(L,idx);
|
|
if(tonumber(o,&n)){
|
|
lua_Integer res;
|
|
lua_Number num=nvalue(o);
|
|
lua_number2integer(res,num);
|
|
return res;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
static int lua_toboolean(lua_State*L,int idx){
|
|
const TValue*o=index2adr(L,idx);
|
|
return!l_isfalse(o);
|
|
}
|
|
static const char*lua_tolstring(lua_State*L,int idx,size_t*len){
|
|
StkId o=index2adr(L,idx);
|
|
if(!ttisstring(o)){
|
|
if(!luaV_tostring(L,o)){
|
|
if(len!=NULL)*len=0;
|
|
return NULL;
|
|
}
|
|
luaC_checkGC(L);
|
|
o=index2adr(L,idx);
|
|
}
|
|
if(len!=NULL)*len=tsvalue(o)->len;
|
|
return svalue(o);
|
|
}
|
|
static size_t lua_objlen(lua_State*L,int idx){
|
|
StkId o=index2adr(L,idx);
|
|
switch(ttype(o)){
|
|
case 4:return tsvalue(o)->len;
|
|
case 7:return uvalue(o)->len;
|
|
case 5:return luaH_getn(hvalue(o));
|
|
case 3:{
|
|
size_t l;
|
|
l=(luaV_tostring(L,o)?tsvalue(o)->len:0);
|
|
return l;
|
|
}
|
|
default:return 0;
|
|
}
|
|
}
|
|
static lua_CFunction lua_tocfunction(lua_State*L,int idx){
|
|
StkId o=index2adr(L,idx);
|
|
return(!iscfunction(o))?NULL:clvalue(o)->c.f;
|
|
}
|
|
static void*lua_touserdata(lua_State*L,int idx){
|
|
StkId o=index2adr(L,idx);
|
|
switch(ttype(o)){
|
|
case 7:return(rawuvalue(o)+1);
|
|
case 2:return pvalue(o);
|
|
default:return NULL;
|
|
}
|
|
}
|
|
static void lua_pushnil(lua_State*L){
|
|
setnilvalue(L->top);
|
|
api_incr_top(L);
|
|
}
|
|
static void lua_pushnumber(lua_State*L,lua_Number n){
|
|
setnvalue(L->top,n);
|
|
api_incr_top(L);
|
|
}
|
|
static void lua_pushinteger(lua_State*L,lua_Integer n){
|
|
setnvalue(L->top,cast_num(n));
|
|
api_incr_top(L);
|
|
}
|
|
static void lua_pushlstring(lua_State*L,const char*s,size_t len){
|
|
luaC_checkGC(L);
|
|
setsvalue(L,L->top,luaS_newlstr(L,s,len));
|
|
api_incr_top(L);
|
|
}
|
|
static void lua_pushstring(lua_State*L,const char*s){
|
|
if(s==NULL)
|
|
lua_pushnil(L);
|
|
else
|
|
lua_pushlstring(L,s,strlen(s));
|
|
}
|
|
static const char*lua_pushvfstring(lua_State*L,const char*fmt,
|
|
va_list argp){
|
|
const char*ret;
|
|
luaC_checkGC(L);
|
|
ret=luaO_pushvfstring(L,fmt,argp);
|
|
return ret;
|
|
}
|
|
static const char*lua_pushfstring(lua_State*L,const char*fmt,...){
|
|
const char*ret;
|
|
va_list argp;
|
|
luaC_checkGC(L);
|
|
va_start(argp,fmt);
|
|
ret=luaO_pushvfstring(L,fmt,argp);
|
|
va_end(argp);
|
|
return ret;
|
|
}
|
|
static void lua_pushcclosure(lua_State*L,lua_CFunction fn,int n){
|
|
Closure*cl;
|
|
luaC_checkGC(L);
|
|
api_checknelems(L,n);
|
|
cl=luaF_newCclosure(L,n,getcurrenv(L));
|
|
cl->c.f=fn;
|
|
L->top-=n;
|
|
while(n--)
|
|
setobj(L,&cl->c.upvalue[n],L->top+n);
|
|
setclvalue(L,L->top,cl);
|
|
api_incr_top(L);
|
|
}
|
|
static void lua_pushboolean(lua_State*L,int b){
|
|
setbvalue(L->top,(b!=0));
|
|
api_incr_top(L);
|
|
}
|
|
static int lua_pushthread(lua_State*L){
|
|
setthvalue(L,L->top,L);
|
|
api_incr_top(L);
|
|
return(G(L)->mainthread==L);
|
|
}
|
|
static void lua_gettable(lua_State*L,int idx){
|
|
StkId t;
|
|
t=index2adr(L,idx);
|
|
api_checkvalidindex(L,t);
|
|
luaV_gettable(L,t,L->top-1,L->top-1);
|
|
}
|
|
static void lua_getfield(lua_State*L,int idx,const char*k){
|
|
StkId t;
|
|
TValue key;
|
|
t=index2adr(L,idx);
|
|
api_checkvalidindex(L,t);
|
|
setsvalue(L,&key,luaS_new(L,k));
|
|
luaV_gettable(L,t,&key,L->top);
|
|
api_incr_top(L);
|
|
}
|
|
static void lua_rawget(lua_State*L,int idx){
|
|
StkId t;
|
|
t=index2adr(L,idx);
|
|
luai_apicheck(L,ttistable(t));
|
|
setobj(L,L->top-1,luaH_get(hvalue(t),L->top-1));
|
|
}
|
|
static void lua_rawgeti(lua_State*L,int idx,int n){
|
|
StkId o;
|
|
o=index2adr(L,idx);
|
|
luai_apicheck(L,ttistable(o));
|
|
setobj(L,L->top,luaH_getnum(hvalue(o),n));
|
|
api_incr_top(L);
|
|
}
|
|
static void lua_createtable(lua_State*L,int narray,int nrec){
|
|
luaC_checkGC(L);
|
|
sethvalue(L,L->top,luaH_new(L,narray,nrec));
|
|
api_incr_top(L);
|
|
}
|
|
static int lua_getmetatable(lua_State*L,int objindex){
|
|
const TValue*obj;
|
|
Table*mt=NULL;
|
|
int res;
|
|
obj=index2adr(L,objindex);
|
|
switch(ttype(obj)){
|
|
case 5:
|
|
mt=hvalue(obj)->metatable;
|
|
break;
|
|
case 7:
|
|
mt=uvalue(obj)->metatable;
|
|
break;
|
|
default:
|
|
mt=G(L)->mt[ttype(obj)];
|
|
break;
|
|
}
|
|
if(mt==NULL)
|
|
res=0;
|
|
else{
|
|
sethvalue(L,L->top,mt);
|
|
api_incr_top(L);
|
|
res=1;
|
|
}
|
|
return res;
|
|
}
|
|
static void lua_getfenv(lua_State*L,int idx){
|
|
StkId o;
|
|
o=index2adr(L,idx);
|
|
api_checkvalidindex(L,o);
|
|
switch(ttype(o)){
|
|
case 6:
|
|
sethvalue(L,L->top,clvalue(o)->c.env);
|
|
break;
|
|
case 7:
|
|
sethvalue(L,L->top,uvalue(o)->env);
|
|
break;
|
|
case 8:
|
|
setobj(L,L->top,gt(thvalue(o)));
|
|
break;
|
|
default:
|
|
setnilvalue(L->top);
|
|
break;
|
|
}
|
|
api_incr_top(L);
|
|
}
|
|
static void lua_settable(lua_State*L,int idx){
|
|
StkId t;
|
|
api_checknelems(L,2);
|
|
t=index2adr(L,idx);
|
|
api_checkvalidindex(L,t);
|
|
luaV_settable(L,t,L->top-2,L->top-1);
|
|
L->top-=2;
|
|
}
|
|
static void lua_setfield(lua_State*L,int idx,const char*k){
|
|
StkId t;
|
|
TValue key;
|
|
api_checknelems(L,1);
|
|
t=index2adr(L,idx);
|
|
api_checkvalidindex(L,t);
|
|
setsvalue(L,&key,luaS_new(L,k));
|
|
luaV_settable(L,t,&key,L->top-1);
|
|
L->top--;
|
|
}
|
|
static void lua_rawset(lua_State*L,int idx){
|
|
StkId t;
|
|
api_checknelems(L,2);
|
|
t=index2adr(L,idx);
|
|
luai_apicheck(L,ttistable(t));
|
|
setobj(L,luaH_set(L,hvalue(t),L->top-2),L->top-1);
|
|
luaC_barriert(L,hvalue(t),L->top-1);
|
|
L->top-=2;
|
|
}
|
|
static void lua_rawseti(lua_State*L,int idx,int n){
|
|
StkId o;
|
|
api_checknelems(L,1);
|
|
o=index2adr(L,idx);
|
|
luai_apicheck(L,ttistable(o));
|
|
setobj(L,luaH_setnum(L,hvalue(o),n),L->top-1);
|
|
luaC_barriert(L,hvalue(o),L->top-1);
|
|
L->top--;
|
|
}
|
|
static int lua_setmetatable(lua_State*L,int objindex){
|
|
TValue*obj;
|
|
Table*mt;
|
|
api_checknelems(L,1);
|
|
obj=index2adr(L,objindex);
|
|
api_checkvalidindex(L,obj);
|
|
if(ttisnil(L->top-1))
|
|
mt=NULL;
|
|
else{
|
|
luai_apicheck(L,ttistable(L->top-1));
|
|
mt=hvalue(L->top-1);
|
|
}
|
|
switch(ttype(obj)){
|
|
case 5:{
|
|
hvalue(obj)->metatable=mt;
|
|
if(mt)
|
|
luaC_objbarriert(L,hvalue(obj),mt);
|
|
break;
|
|
}
|
|
case 7:{
|
|
uvalue(obj)->metatable=mt;
|
|
if(mt)
|
|
luaC_objbarrier(L,rawuvalue(obj),mt);
|
|
break;
|
|
}
|
|
default:{
|
|
G(L)->mt[ttype(obj)]=mt;
|
|
break;
|
|
}
|
|
}
|
|
L->top--;
|
|
return 1;
|
|
}
|
|
static int lua_setfenv(lua_State*L,int idx){
|
|
StkId o;
|
|
int res=1;
|
|
api_checknelems(L,1);
|
|
o=index2adr(L,idx);
|
|
api_checkvalidindex(L,o);
|
|
luai_apicheck(L,ttistable(L->top-1));
|
|
switch(ttype(o)){
|
|
case 6:
|
|
clvalue(o)->c.env=hvalue(L->top-1);
|
|
break;
|
|
case 7:
|
|
uvalue(o)->env=hvalue(L->top-1);
|
|
break;
|
|
case 8:
|
|
sethvalue(L,gt(thvalue(o)),hvalue(L->top-1));
|
|
break;
|
|
default:
|
|
res=0;
|
|
break;
|
|
}
|
|
if(res)luaC_objbarrier(L,gcvalue(o),hvalue(L->top-1));
|
|
L->top--;
|
|
return res;
|
|
}
|
|
#define adjustresults(L,nres){if(nres==(-1)&&L->top>=L->ci->top)L->ci->top=L->top;}
|
|
#define checkresults(L,na,nr)luai_apicheck(L,(nr)==(-1)||(L->ci->top-L->top>=(nr)-(na)))
|
|
static void lua_call(lua_State*L,int nargs,int nresults){
|
|
StkId func;
|
|
api_checknelems(L,nargs+1);
|
|
checkresults(L,nargs,nresults);
|
|
func=L->top-(nargs+1);
|
|
luaD_call(L,func,nresults);
|
|
adjustresults(L,nresults);
|
|
}
|
|
struct CallS{
|
|
StkId func;
|
|
int nresults;
|
|
};
|
|
static void f_call(lua_State*L,void*ud){
|
|
struct CallS*c=cast(struct CallS*,ud);
|
|
luaD_call(L,c->func,c->nresults);
|
|
}
|
|
static int lua_pcall(lua_State*L,int nargs,int nresults,int errfunc){
|
|
struct CallS c;
|
|
int status;
|
|
ptrdiff_t func;
|
|
api_checknelems(L,nargs+1);
|
|
checkresults(L,nargs,nresults);
|
|
if(errfunc==0)
|
|
func=0;
|
|
else{
|
|
StkId o=index2adr(L,errfunc);
|
|
api_checkvalidindex(L,o);
|
|
func=savestack(L,o);
|
|
}
|
|
c.func=L->top-(nargs+1);
|
|
c.nresults=nresults;
|
|
status=luaD_pcall(L,f_call,&c,savestack(L,c.func),func);
|
|
adjustresults(L,nresults);
|
|
return status;
|
|
}
|
|
static int lua_load(lua_State*L,lua_Reader reader,void*data,
|
|
const char*chunkname){
|
|
ZIO z;
|
|
int status;
|
|
if(!chunkname)chunkname="?";
|
|
luaZ_init(L,&z,reader,data);
|
|
status=luaD_protectedparser(L,&z,chunkname);
|
|
return status;
|
|
}
|
|
static int lua_error(lua_State*L){
|
|
api_checknelems(L,1);
|
|
luaG_errormsg(L);
|
|
return 0;
|
|
}
|
|
static int lua_next(lua_State*L,int idx){
|
|
StkId t;
|
|
int more;
|
|
t=index2adr(L,idx);
|
|
luai_apicheck(L,ttistable(t));
|
|
more=luaH_next(L,hvalue(t),L->top-1);
|
|
if(more){
|
|
api_incr_top(L);
|
|
}
|
|
else
|
|
L->top-=1;
|
|
return more;
|
|
}
|
|
static void lua_concat(lua_State*L,int n){
|
|
api_checknelems(L,n);
|
|
if(n>=2){
|
|
luaC_checkGC(L);
|
|
luaV_concat(L,n,cast_int(L->top-L->base)-1);
|
|
L->top-=(n-1);
|
|
}
|
|
else if(n==0){
|
|
setsvalue(L,L->top,luaS_newlstr(L,"",0));
|
|
api_incr_top(L);
|
|
}
|
|
}
|
|
static void*lua_newuserdata(lua_State*L,size_t size){
|
|
Udata*u;
|
|
luaC_checkGC(L);
|
|
u=luaS_newudata(L,size,getcurrenv(L));
|
|
setuvalue(L,L->top,u);
|
|
api_incr_top(L);
|
|
return u+1;
|
|
}
|
|
#define luaL_getn(L,i)((int)lua_objlen(L,i))
|
|
#define luaL_setn(L,i,j)((void)0)
|
|
typedef struct luaL_Reg{
|
|
const char*name;
|
|
lua_CFunction func;
|
|
}luaL_Reg;
|
|
static void luaI_openlib(lua_State*L,const char*libname,
|
|
const luaL_Reg*l,int nup);
|
|
static int luaL_argerror(lua_State*L,int numarg,const char*extramsg);
|
|
static const char* luaL_checklstring(lua_State*L,int numArg,
|
|
size_t*l);
|
|
static const char* luaL_optlstring(lua_State*L,int numArg,
|
|
const char*def,size_t*l);
|
|
static lua_Integer luaL_checkinteger(lua_State*L,int numArg);
|
|
static lua_Integer luaL_optinteger(lua_State*L,int nArg,
|
|
lua_Integer def);
|
|
static int luaL_error(lua_State*L,const char*fmt,...);
|
|
static const char* luaL_findtable(lua_State*L,int idx,
|
|
const char*fname,int szhint);
|
|
#define luaL_argcheck(L,cond,numarg,extramsg)((void)((cond)||luaL_argerror(L,(numarg),(extramsg))))
|
|
#define luaL_checkstring(L,n)(luaL_checklstring(L,(n),NULL))
|
|
#define luaL_optstring(L,n,d)(luaL_optlstring(L,(n),(d),NULL))
|
|
#define luaL_checkint(L,n)((int)luaL_checkinteger(L,(n)))
|
|
#define luaL_optint(L,n,d)((int)luaL_optinteger(L,(n),(d)))
|
|
#define luaL_typename(L,i)lua_typename(L,lua_type(L,(i)))
|
|
#define luaL_getmetatable(L,n)(lua_getfield(L,(-10000),(n)))
|
|
#define luaL_opt(L,f,n,d)(lua_isnoneornil(L,(n))?(d):f(L,(n)))
|
|
typedef struct luaL_Buffer{
|
|
char*p;
|
|
int lvl;
|
|
lua_State*L;
|
|
char buffer[BUFSIZ];
|
|
}luaL_Buffer;
|
|
#define luaL_addchar(B,c)((void)((B)->p<((B)->buffer+BUFSIZ)||luaL_prepbuffer(B)),(*(B)->p++=(char)(c)))
|
|
#define luaL_addsize(B,n)((B)->p+=(n))
|
|
static char* luaL_prepbuffer(luaL_Buffer*B);
|
|
static int luaL_argerror(lua_State*L,int narg,const char*extramsg){
|
|
lua_Debug ar;
|
|
if(!lua_getstack(L,0,&ar))
|
|
return luaL_error(L,"bad argument #%d (%s)",narg,extramsg);
|
|
lua_getinfo(L,"n",&ar);
|
|
if(strcmp(ar.namewhat,"method")==0){
|
|
narg--;
|
|
if(narg==0)
|
|
return luaL_error(L,"calling "LUA_QL("%s")" on bad self (%s)",
|
|
ar.name,extramsg);
|
|
}
|
|
if(ar.name==NULL)
|
|
ar.name="?";
|
|
return luaL_error(L,"bad argument #%d to "LUA_QL("%s")" (%s)",
|
|
narg,ar.name,extramsg);
|
|
}
|
|
static int luaL_typerror(lua_State*L,int narg,const char*tname){
|
|
const char*msg=lua_pushfstring(L,"%s expected, got %s",
|
|
tname,luaL_typename(L,narg));
|
|
return luaL_argerror(L,narg,msg);
|
|
}
|
|
static void tag_error(lua_State*L,int narg,int tag){
|
|
luaL_typerror(L,narg,lua_typename(L,tag));
|
|
}
|
|
static void luaL_where(lua_State*L,int level){
|
|
lua_Debug ar;
|
|
if(lua_getstack(L,level,&ar)){
|
|
lua_getinfo(L,"Sl",&ar);
|
|
if(ar.currentline>0){
|
|
lua_pushfstring(L,"%s:%d: ",ar.short_src,ar.currentline);
|
|
return;
|
|
}
|
|
}
|
|
lua_pushliteral(L,"");
|
|
}
|
|
static int luaL_error(lua_State*L,const char*fmt,...){
|
|
va_list argp;
|
|
va_start(argp,fmt);
|
|
luaL_where(L,1);
|
|
lua_pushvfstring(L,fmt,argp);
|
|
va_end(argp);
|
|
lua_concat(L,2);
|
|
return lua_error(L);
|
|
}
|
|
static int luaL_newmetatable(lua_State*L,const char*tname){
|
|
lua_getfield(L,(-10000),tname);
|
|
if(!lua_isnil(L,-1))
|
|
return 0;
|
|
lua_pop(L,1);
|
|
lua_newtable(L);
|
|
lua_pushvalue(L,-1);
|
|
lua_setfield(L,(-10000),tname);
|
|
return 1;
|
|
}
|
|
static void*luaL_checkudata(lua_State*L,int ud,const char*tname){
|
|
void*p=lua_touserdata(L,ud);
|
|
if(p!=NULL){
|
|
if(lua_getmetatable(L,ud)){
|
|
lua_getfield(L,(-10000),tname);
|
|
if(lua_rawequal(L,-1,-2)){
|
|
lua_pop(L,2);
|
|
return p;
|
|
}
|
|
}
|
|
}
|
|
luaL_typerror(L,ud,tname);
|
|
return NULL;
|
|
}
|
|
static void luaL_checkstack(lua_State*L,int space,const char*mes){
|
|
if(!lua_checkstack(L,space))
|
|
luaL_error(L,"stack overflow (%s)",mes);
|
|
}
|
|
static void luaL_checktype(lua_State*L,int narg,int t){
|
|
if(lua_type(L,narg)!=t)
|
|
tag_error(L,narg,t);
|
|
}
|
|
static void luaL_checkany(lua_State*L,int narg){
|
|
if(lua_type(L,narg)==(-1))
|
|
luaL_argerror(L,narg,"value expected");
|
|
}
|
|
static const char*luaL_checklstring(lua_State*L,int narg,size_t*len){
|
|
const char*s=lua_tolstring(L,narg,len);
|
|
if(!s)tag_error(L,narg,4);
|
|
return s;
|
|
}
|
|
static const char*luaL_optlstring(lua_State*L,int narg,
|
|
const char*def,size_t*len){
|
|
if(lua_isnoneornil(L,narg)){
|
|
if(len)
|
|
*len=(def?strlen(def):0);
|
|
return def;
|
|
}
|
|
else return luaL_checklstring(L,narg,len);
|
|
}
|
|
static lua_Number luaL_checknumber(lua_State*L,int narg){
|
|
lua_Number d=lua_tonumber(L,narg);
|
|
if(d==0&&!lua_isnumber(L,narg))
|
|
tag_error(L,narg,3);
|
|
return d;
|
|
}
|
|
static lua_Integer luaL_checkinteger(lua_State*L,int narg){
|
|
lua_Integer d=lua_tointeger(L,narg);
|
|
if(d==0&&!lua_isnumber(L,narg))
|
|
tag_error(L,narg,3);
|
|
return d;
|
|
}
|
|
static lua_Integer luaL_optinteger(lua_State*L,int narg,
|
|
lua_Integer def){
|
|
return luaL_opt(L,luaL_checkinteger,narg,def);
|
|
}
|
|
static int luaL_getmetafield(lua_State*L,int obj,const char*event){
|
|
if(!lua_getmetatable(L,obj))
|
|
return 0;
|
|
lua_pushstring(L,event);
|
|
lua_rawget(L,-2);
|
|
if(lua_isnil(L,-1)){
|
|
lua_pop(L,2);
|
|
return 0;
|
|
}
|
|
else{
|
|
lua_remove(L,-2);
|
|
return 1;
|
|
}
|
|
}
|
|
static void luaL_register(lua_State*L,const char*libname,
|
|
const luaL_Reg*l){
|
|
luaI_openlib(L,libname,l,0);
|
|
}
|
|
static int libsize(const luaL_Reg*l){
|
|
int size=0;
|
|
for(;l->name;l++)size++;
|
|
return size;
|
|
}
|
|
static void luaI_openlib(lua_State*L,const char*libname,
|
|
const luaL_Reg*l,int nup){
|
|
if(libname){
|
|
int size=libsize(l);
|
|
luaL_findtable(L,(-10000),"_LOADED",1);
|
|
lua_getfield(L,-1,libname);
|
|
if(!lua_istable(L,-1)){
|
|
lua_pop(L,1);
|
|
if(luaL_findtable(L,(-10002),libname,size)!=NULL)
|
|
luaL_error(L,"name conflict for module "LUA_QL("%s"),libname);
|
|
lua_pushvalue(L,-1);
|
|
lua_setfield(L,-3,libname);
|
|
}
|
|
lua_remove(L,-2);
|
|
lua_insert(L,-(nup+1));
|
|
}
|
|
for(;l->name;l++){
|
|
int i;
|
|
for(i=0;i<nup;i++)
|
|
lua_pushvalue(L,-nup);
|
|
lua_pushcclosure(L,l->func,nup);
|
|
lua_setfield(L,-(nup+2),l->name);
|
|
}
|
|
lua_pop(L,nup);
|
|
}
|
|
static const char*luaL_findtable(lua_State*L,int idx,
|
|
const char*fname,int szhint){
|
|
const char*e;
|
|
lua_pushvalue(L,idx);
|
|
do{
|
|
e=strchr(fname,'.');
|
|
if(e==NULL)e=fname+strlen(fname);
|
|
lua_pushlstring(L,fname,e-fname);
|
|
lua_rawget(L,-2);
|
|
if(lua_isnil(L,-1)){
|
|
lua_pop(L,1);
|
|
lua_createtable(L,0,(*e=='.'?1:szhint));
|
|
lua_pushlstring(L,fname,e-fname);
|
|
lua_pushvalue(L,-2);
|
|
lua_settable(L,-4);
|
|
}
|
|
else if(!lua_istable(L,-1)){
|
|
lua_pop(L,2);
|
|
return fname;
|
|
}
|
|
lua_remove(L,-2);
|
|
fname=e+1;
|
|
}while(*e=='.');
|
|
return NULL;
|
|
}
|
|
#define bufflen(B)((B)->p-(B)->buffer)
|
|
#define bufffree(B)((size_t)(BUFSIZ-bufflen(B)))
|
|
static int emptybuffer(luaL_Buffer*B){
|
|
size_t l=bufflen(B);
|
|
if(l==0)return 0;
|
|
else{
|
|
lua_pushlstring(B->L,B->buffer,l);
|
|
B->p=B->buffer;
|
|
B->lvl++;
|
|
return 1;
|
|
}
|
|
}
|
|
static void adjuststack(luaL_Buffer*B){
|
|
if(B->lvl>1){
|
|
lua_State*L=B->L;
|
|
int toget=1;
|
|
size_t toplen=lua_strlen(L,-1);
|
|
do{
|
|
size_t l=lua_strlen(L,-(toget+1));
|
|
if(B->lvl-toget+1>=(20/2)||toplen>l){
|
|
toplen+=l;
|
|
toget++;
|
|
}
|
|
else break;
|
|
}while(toget<B->lvl);
|
|
lua_concat(L,toget);
|
|
B->lvl=B->lvl-toget+1;
|
|
}
|
|
}
|
|
static char*luaL_prepbuffer(luaL_Buffer*B){
|
|
if(emptybuffer(B))
|
|
adjuststack(B);
|
|
return B->buffer;
|
|
}
|
|
static void luaL_addlstring(luaL_Buffer*B,const char*s,size_t l){
|
|
while(l--)
|
|
luaL_addchar(B,*s++);
|
|
}
|
|
static void luaL_pushresult(luaL_Buffer*B){
|
|
emptybuffer(B);
|
|
lua_concat(B->L,B->lvl);
|
|
B->lvl=1;
|
|
}
|
|
static void luaL_addvalue(luaL_Buffer*B){
|
|
lua_State*L=B->L;
|
|
size_t vl;
|
|
const char*s=lua_tolstring(L,-1,&vl);
|
|
if(vl<=bufffree(B)){
|
|
memcpy(B->p,s,vl);
|
|
B->p+=vl;
|
|
lua_pop(L,1);
|
|
}
|
|
else{
|
|
if(emptybuffer(B))
|
|
lua_insert(L,-2);
|
|
B->lvl++;
|
|
adjuststack(B);
|
|
}
|
|
}
|
|
static void luaL_buffinit(lua_State*L,luaL_Buffer*B){
|
|
B->L=L;
|
|
B->p=B->buffer;
|
|
B->lvl=0;
|
|
}
|
|
typedef struct LoadF{
|
|
int extraline;
|
|
FILE*f;
|
|
char buff[BUFSIZ];
|
|
}LoadF;
|
|
static const char*getF(lua_State*L,void*ud,size_t*size){
|
|
LoadF*lf=(LoadF*)ud;
|
|
(void)L;
|
|
if(lf->extraline){
|
|
lf->extraline=0;
|
|
*size=1;
|
|
return"\n";
|
|
}
|
|
if(feof(lf->f))return NULL;
|
|
*size=fread(lf->buff,1,sizeof(lf->buff),lf->f);
|
|
return(*size>0)?lf->buff:NULL;
|
|
}
|
|
static int errfile(lua_State*L,const char*what,int fnameindex){
|
|
const char*serr=strerror(errno);
|
|
const char*filename=lua_tostring(L,fnameindex)+1;
|
|
lua_pushfstring(L,"cannot %s %s: %s",what,filename,serr);
|
|
lua_remove(L,fnameindex);
|
|
return(5+1);
|
|
}
|
|
static int luaL_loadfile(lua_State*L,const char*filename){
|
|
LoadF lf;
|
|
int status,readstatus;
|
|
int c;
|
|
int fnameindex=lua_gettop(L)+1;
|
|
lf.extraline=0;
|
|
if(filename==NULL){
|
|
lua_pushliteral(L,"=stdin");
|
|
lf.f=stdin;
|
|
}
|
|
else{
|
|
lua_pushfstring(L,"@%s",filename);
|
|
lf.f=fopen(filename,"r");
|
|
if(lf.f==NULL)return errfile(L,"open",fnameindex);
|
|
}
|
|
c=getc(lf.f);
|
|
if(c=='#'){
|
|
lf.extraline=1;
|
|
while((c=getc(lf.f))!=EOF&&c!='\n');
|
|
if(c=='\n')c=getc(lf.f);
|
|
}
|
|
if(c=="\033Lua"[0]&&filename){
|
|
lf.f=freopen(filename,"rb",lf.f);
|
|
if(lf.f==NULL)return errfile(L,"reopen",fnameindex);
|
|
while((c=getc(lf.f))!=EOF&&c!="\033Lua"[0]);
|
|
lf.extraline=0;
|
|
}
|
|
ungetc(c,lf.f);
|
|
status=lua_load(L,getF,&lf,lua_tostring(L,-1));
|
|
readstatus=ferror(lf.f);
|
|
if(filename)fclose(lf.f);
|
|
if(readstatus){
|
|
lua_settop(L,fnameindex);
|
|
return errfile(L,"read",fnameindex);
|
|
}
|
|
lua_remove(L,fnameindex);
|
|
return status;
|
|
}
|
|
typedef struct LoadS{
|
|
const char*s;
|
|
size_t size;
|
|
}LoadS;
|
|
static const char*getS(lua_State*L,void*ud,size_t*size){
|
|
LoadS*ls=(LoadS*)ud;
|
|
(void)L;
|
|
if(ls->size==0)return NULL;
|
|
*size=ls->size;
|
|
ls->size=0;
|
|
return ls->s;
|
|
}
|
|
static int luaL_loadbuffer(lua_State*L,const char*buff,size_t size,
|
|
const char*name){
|
|
LoadS ls;
|
|
ls.s=buff;
|
|
ls.size=size;
|
|
return lua_load(L,getS,&ls,name);
|
|
}
|
|
static void*l_alloc(void*ud,void*ptr,size_t osize,size_t nsize){
|
|
(void)ud;
|
|
(void)osize;
|
|
if(nsize==0){
|
|
free(ptr);
|
|
return NULL;
|
|
}
|
|
else
|
|
return realloc(ptr,nsize);
|
|
}
|
|
static int panic(lua_State*L){
|
|
(void)L;
|
|
fprintf(stderr,"PANIC: unprotected error in call to Lua API (%s)\n",
|
|
lua_tostring(L,-1));
|
|
return 0;
|
|
}
|
|
static lua_State*luaL_newstate(void){
|
|
lua_State*L=lua_newstate(l_alloc,NULL);
|
|
if(L)lua_atpanic(L,&panic);
|
|
return L;
|
|
}
|
|
static int luaB_tonumber(lua_State*L){
|
|
int base=luaL_optint(L,2,10);
|
|
if(base==10){
|
|
luaL_checkany(L,1);
|
|
if(lua_isnumber(L,1)){
|
|
lua_pushnumber(L,lua_tonumber(L,1));
|
|
return 1;
|
|
}
|
|
}
|
|
else{
|
|
const char*s1=luaL_checkstring(L,1);
|
|
char*s2;
|
|
unsigned long n;
|
|
luaL_argcheck(L,2<=base&&base<=36,2,"base out of range");
|
|
n=strtoul(s1,&s2,base);
|
|
if(s1!=s2){
|
|
while(isspace((unsigned char)(*s2)))s2++;
|
|
if(*s2=='\0'){
|
|
lua_pushnumber(L,(lua_Number)n);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
static int luaB_error(lua_State*L){
|
|
int level=luaL_optint(L,2,1);
|
|
lua_settop(L,1);
|
|
if(lua_isstring(L,1)&&level>0){
|
|
luaL_where(L,level);
|
|
lua_pushvalue(L,1);
|
|
lua_concat(L,2);
|
|
}
|
|
return lua_error(L);
|
|
}
|
|
static int luaB_setmetatable(lua_State*L){
|
|
int t=lua_type(L,2);
|
|
luaL_checktype(L,1,5);
|
|
luaL_argcheck(L,t==0||t==5,2,
|
|
"nil or table expected");
|
|
if(luaL_getmetafield(L,1,"__metatable"))
|
|
luaL_error(L,"cannot change a protected metatable");
|
|
lua_settop(L,2);
|
|
lua_setmetatable(L,1);
|
|
return 1;
|
|
}
|
|
static void getfunc(lua_State*L,int opt){
|
|
if(lua_isfunction(L,1))lua_pushvalue(L,1);
|
|
else{
|
|
lua_Debug ar;
|
|
int level=opt?luaL_optint(L,1,1):luaL_checkint(L,1);
|
|
luaL_argcheck(L,level>=0,1,"level must be non-negative");
|
|
if(lua_getstack(L,level,&ar)==0)
|
|
luaL_argerror(L,1,"invalid level");
|
|
lua_getinfo(L,"f",&ar);
|
|
if(lua_isnil(L,-1))
|
|
luaL_error(L,"no function environment for tail call at level %d",
|
|
level);
|
|
}
|
|
}
|
|
static int luaB_setfenv(lua_State*L){
|
|
luaL_checktype(L,2,5);
|
|
getfunc(L,0);
|
|
lua_pushvalue(L,2);
|
|
if(lua_isnumber(L,1)&&lua_tonumber(L,1)==0){
|
|
lua_pushthread(L);
|
|
lua_insert(L,-2);
|
|
lua_setfenv(L,-2);
|
|
return 0;
|
|
}
|
|
else if(lua_iscfunction(L,-2)||lua_setfenv(L,-2)==0)
|
|
luaL_error(L,
|
|
LUA_QL("setfenv")" cannot change environment of given object");
|
|
return 1;
|
|
}
|
|
static int luaB_rawget(lua_State*L){
|
|
luaL_checktype(L,1,5);
|
|
luaL_checkany(L,2);
|
|
lua_settop(L,2);
|
|
lua_rawget(L,1);
|
|
return 1;
|
|
}
|
|
static int luaB_type(lua_State*L){
|
|
luaL_checkany(L,1);
|
|
lua_pushstring(L,luaL_typename(L,1));
|
|
return 1;
|
|
}
|
|
static int luaB_next(lua_State*L){
|
|
luaL_checktype(L,1,5);
|
|
lua_settop(L,2);
|
|
if(lua_next(L,1))
|
|
return 2;
|
|
else{
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
}
|
|
static int luaB_pairs(lua_State*L){
|
|
luaL_checktype(L,1,5);
|
|
lua_pushvalue(L,lua_upvalueindex(1));
|
|
lua_pushvalue(L,1);
|
|
lua_pushnil(L);
|
|
return 3;
|
|
}
|
|
static int ipairsaux(lua_State*L){
|
|
int i=luaL_checkint(L,2);
|
|
luaL_checktype(L,1,5);
|
|
i++;
|
|
lua_pushinteger(L,i);
|
|
lua_rawgeti(L,1,i);
|
|
return(lua_isnil(L,-1))?0:2;
|
|
}
|
|
static int luaB_ipairs(lua_State*L){
|
|
luaL_checktype(L,1,5);
|
|
lua_pushvalue(L,lua_upvalueindex(1));
|
|
lua_pushvalue(L,1);
|
|
lua_pushinteger(L,0);
|
|
return 3;
|
|
}
|
|
static int load_aux(lua_State*L,int status){
|
|
if(status==0)
|
|
return 1;
|
|
else{
|
|
lua_pushnil(L);
|
|
lua_insert(L,-2);
|
|
return 2;
|
|
}
|
|
}
|
|
static int luaB_loadstring(lua_State*L){
|
|
size_t l;
|
|
const char*s=luaL_checklstring(L,1,&l);
|
|
const char*chunkname=luaL_optstring(L,2,s);
|
|
return load_aux(L,luaL_loadbuffer(L,s,l,chunkname));
|
|
}
|
|
static int luaB_loadfile(lua_State*L){
|
|
const char*fname=luaL_optstring(L,1,NULL);
|
|
return load_aux(L,luaL_loadfile(L,fname));
|
|
}
|
|
static int luaB_assert(lua_State*L){
|
|
luaL_checkany(L,1);
|
|
if(!lua_toboolean(L,1))
|
|
return luaL_error(L,"%s",luaL_optstring(L,2,"assertion failed!"));
|
|
return lua_gettop(L);
|
|
}
|
|
static int luaB_unpack(lua_State*L){
|
|
int i,e,n;
|
|
luaL_checktype(L,1,5);
|
|
i=luaL_optint(L,2,1);
|
|
e=luaL_opt(L,luaL_checkint,3,luaL_getn(L,1));
|
|
if(i>e)return 0;
|
|
n=e-i+1;
|
|
if(n<=0||!lua_checkstack(L,n))
|
|
return luaL_error(L,"too many results to unpack");
|
|
lua_rawgeti(L,1,i);
|
|
while(i++<e)
|
|
lua_rawgeti(L,1,i);
|
|
return n;
|
|
}
|
|
static int luaB_pcall(lua_State*L){
|
|
int status;
|
|
luaL_checkany(L,1);
|
|
status=lua_pcall(L,lua_gettop(L)-1,(-1),0);
|
|
lua_pushboolean(L,(status==0));
|
|
lua_insert(L,1);
|
|
return lua_gettop(L);
|
|
}
|
|
static int luaB_newproxy(lua_State*L){
|
|
lua_settop(L,1);
|
|
lua_newuserdata(L,0);
|
|
if(lua_toboolean(L,1)==0)
|
|
return 1;
|
|
else if(lua_isboolean(L,1)){
|
|
lua_newtable(L);
|
|
lua_pushvalue(L,-1);
|
|
lua_pushboolean(L,1);
|
|
lua_rawset(L,lua_upvalueindex(1));
|
|
}
|
|
else{
|
|
int validproxy=0;
|
|
if(lua_getmetatable(L,1)){
|
|
lua_rawget(L,lua_upvalueindex(1));
|
|
validproxy=lua_toboolean(L,-1);
|
|
lua_pop(L,1);
|
|
}
|
|
luaL_argcheck(L,validproxy,1,"boolean or proxy expected");
|
|
lua_getmetatable(L,1);
|
|
}
|
|
lua_setmetatable(L,2);
|
|
return 1;
|
|
}
|
|
static const luaL_Reg base_funcs[]={
|
|
{"assert",luaB_assert},
|
|
{"error",luaB_error},
|
|
{"loadfile",luaB_loadfile},
|
|
{"loadstring",luaB_loadstring},
|
|
{"next",luaB_next},
|
|
{"pcall",luaB_pcall},
|
|
{"rawget",luaB_rawget},
|
|
{"setfenv",luaB_setfenv},
|
|
{"setmetatable",luaB_setmetatable},
|
|
{"tonumber",luaB_tonumber},
|
|
{"type",luaB_type},
|
|
{"unpack",luaB_unpack},
|
|
{NULL,NULL}
|
|
};
|
|
static void auxopen(lua_State*L,const char*name,
|
|
lua_CFunction f,lua_CFunction u){
|
|
lua_pushcfunction(L,u);
|
|
lua_pushcclosure(L,f,1);
|
|
lua_setfield(L,-2,name);
|
|
}
|
|
static void base_open(lua_State*L){
|
|
lua_pushvalue(L,(-10002));
|
|
lua_setglobal(L,"_G");
|
|
luaL_register(L,"_G",base_funcs);
|
|
lua_pushliteral(L,"Lua 5.1");
|
|
lua_setglobal(L,"_VERSION");
|
|
auxopen(L,"ipairs",luaB_ipairs,ipairsaux);
|
|
auxopen(L,"pairs",luaB_pairs,luaB_next);
|
|
lua_createtable(L,0,1);
|
|
lua_pushvalue(L,-1);
|
|
lua_setmetatable(L,-2);
|
|
lua_pushliteral(L,"kv");
|
|
lua_setfield(L,-2,"__mode");
|
|
lua_pushcclosure(L,luaB_newproxy,1);
|
|
lua_setglobal(L,"newproxy");
|
|
}
|
|
static int luaopen_base(lua_State*L){
|
|
base_open(L);
|
|
return 1;
|
|
}
|
|
#define aux_getn(L,n)(luaL_checktype(L,n,5),luaL_getn(L,n))
|
|
static int tinsert(lua_State*L){
|
|
int e=aux_getn(L,1)+1;
|
|
int pos;
|
|
switch(lua_gettop(L)){
|
|
case 2:{
|
|
pos=e;
|
|
break;
|
|
}
|
|
case 3:{
|
|
int i;
|
|
pos=luaL_checkint(L,2);
|
|
if(pos>e)e=pos;
|
|
for(i=e;i>pos;i--){
|
|
lua_rawgeti(L,1,i-1);
|
|
lua_rawseti(L,1,i);
|
|
}
|
|
break;
|
|
}
|
|
default:{
|
|
return luaL_error(L,"wrong number of arguments to "LUA_QL("insert"));
|
|
}
|
|
}
|
|
luaL_setn(L,1,e);
|
|
lua_rawseti(L,1,pos);
|
|
return 0;
|
|
}
|
|
static int tremove(lua_State*L){
|
|
int e=aux_getn(L,1);
|
|
int pos=luaL_optint(L,2,e);
|
|
if(!(1<=pos&&pos<=e))
|
|
return 0;
|
|
luaL_setn(L,1,e-1);
|
|
lua_rawgeti(L,1,pos);
|
|
for(;pos<e;pos++){
|
|
lua_rawgeti(L,1,pos+1);
|
|
lua_rawseti(L,1,pos);
|
|
}
|
|
lua_pushnil(L);
|
|
lua_rawseti(L,1,e);
|
|
return 1;
|
|
}
|
|
static void addfield(lua_State*L,luaL_Buffer*b,int i){
|
|
lua_rawgeti(L,1,i);
|
|
if(!lua_isstring(L,-1))
|
|
luaL_error(L,"invalid value (%s) at index %d in table for "
|
|
LUA_QL("concat"),luaL_typename(L,-1),i);
|
|
luaL_addvalue(b);
|
|
}
|
|
static int tconcat(lua_State*L){
|
|
luaL_Buffer b;
|
|
size_t lsep;
|
|
int i,last;
|
|
const char*sep=luaL_optlstring(L,2,"",&lsep);
|
|
luaL_checktype(L,1,5);
|
|
i=luaL_optint(L,3,1);
|
|
last=luaL_opt(L,luaL_checkint,4,luaL_getn(L,1));
|
|
luaL_buffinit(L,&b);
|
|
for(;i<last;i++){
|
|
addfield(L,&b,i);
|
|
luaL_addlstring(&b,sep,lsep);
|
|
}
|
|
if(i==last)
|
|
addfield(L,&b,i);
|
|
luaL_pushresult(&b);
|
|
return 1;
|
|
}
|
|
static void set2(lua_State*L,int i,int j){
|
|
lua_rawseti(L,1,i);
|
|
lua_rawseti(L,1,j);
|
|
}
|
|
static int sort_comp(lua_State*L,int a,int b){
|
|
if(!lua_isnil(L,2)){
|
|
int res;
|
|
lua_pushvalue(L,2);
|
|
lua_pushvalue(L,a-1);
|
|
lua_pushvalue(L,b-2);
|
|
lua_call(L,2,1);
|
|
res=lua_toboolean(L,-1);
|
|
lua_pop(L,1);
|
|
return res;
|
|
}
|
|
else
|
|
return lua_lessthan(L,a,b);
|
|
}
|
|
static void auxsort(lua_State*L,int l,int u){
|
|
while(l<u){
|
|
int i,j;
|
|
lua_rawgeti(L,1,l);
|
|
lua_rawgeti(L,1,u);
|
|
if(sort_comp(L,-1,-2))
|
|
set2(L,l,u);
|
|
else
|
|
lua_pop(L,2);
|
|
if(u-l==1)break;
|
|
i=(l+u)/2;
|
|
lua_rawgeti(L,1,i);
|
|
lua_rawgeti(L,1,l);
|
|
if(sort_comp(L,-2,-1))
|
|
set2(L,i,l);
|
|
else{
|
|
lua_pop(L,1);
|
|
lua_rawgeti(L,1,u);
|
|
if(sort_comp(L,-1,-2))
|
|
set2(L,i,u);
|
|
else
|
|
lua_pop(L,2);
|
|
}
|
|
if(u-l==2)break;
|
|
lua_rawgeti(L,1,i);
|
|
lua_pushvalue(L,-1);
|
|
lua_rawgeti(L,1,u-1);
|
|
set2(L,i,u-1);
|
|
i=l;j=u-1;
|
|
for(;;){
|
|
while(lua_rawgeti(L,1,++i),sort_comp(L,-1,-2)){
|
|
if(i>u)luaL_error(L,"invalid order function for sorting");
|
|
lua_pop(L,1);
|
|
}
|
|
while(lua_rawgeti(L,1,--j),sort_comp(L,-3,-1)){
|
|
if(j<l)luaL_error(L,"invalid order function for sorting");
|
|
lua_pop(L,1);
|
|
}
|
|
if(j<i){
|
|
lua_pop(L,3);
|
|
break;
|
|
}
|
|
set2(L,i,j);
|
|
}
|
|
lua_rawgeti(L,1,u-1);
|
|
lua_rawgeti(L,1,i);
|
|
set2(L,u-1,i);
|
|
if(i-l<u-i){
|
|
j=l;i=i-1;l=i+2;
|
|
}
|
|
else{
|
|
j=i+1;i=u;u=j-2;
|
|
}
|
|
auxsort(L,j,i);
|
|
}
|
|
}
|
|
static int sort(lua_State*L){
|
|
int n=aux_getn(L,1);
|
|
luaL_checkstack(L,40,"");
|
|
if(!lua_isnoneornil(L,2))
|
|
luaL_checktype(L,2,6);
|
|
lua_settop(L,2);
|
|
auxsort(L,1,n);
|
|
return 0;
|
|
}
|
|
static const luaL_Reg tab_funcs[]={
|
|
{"concat",tconcat},
|
|
{"insert",tinsert},
|
|
{"remove",tremove},
|
|
{"sort",sort},
|
|
{NULL,NULL}
|
|
};
|
|
static int luaopen_table(lua_State*L){
|
|
luaL_register(L,"table",tab_funcs);
|
|
return 1;
|
|
}
|
|
static const char*const fnames[]={"input","output"};
|
|
static int pushresult(lua_State*L,int i,const char*filename){
|
|
int en=errno;
|
|
if(i){
|
|
lua_pushboolean(L,1);
|
|
return 1;
|
|
}
|
|
else{
|
|
lua_pushnil(L);
|
|
if(filename)
|
|
lua_pushfstring(L,"%s: %s",filename,strerror(en));
|
|
else
|
|
lua_pushfstring(L,"%s",strerror(en));
|
|
lua_pushinteger(L,en);
|
|
return 3;
|
|
}
|
|
}
|
|
static void fileerror(lua_State*L,int arg,const char*filename){
|
|
lua_pushfstring(L,"%s: %s",filename,strerror(errno));
|
|
luaL_argerror(L,arg,lua_tostring(L,-1));
|
|
}
|
|
#define tofilep(L)((FILE**)luaL_checkudata(L,1,"FILE*"))
|
|
static int io_type(lua_State*L){
|
|
void*ud;
|
|
luaL_checkany(L,1);
|
|
ud=lua_touserdata(L,1);
|
|
lua_getfield(L,(-10000),"FILE*");
|
|
if(ud==NULL||!lua_getmetatable(L,1)||!lua_rawequal(L,-2,-1))
|
|
lua_pushnil(L);
|
|
else if(*((FILE**)ud)==NULL)
|
|
lua_pushliteral(L,"closed file");
|
|
else
|
|
lua_pushliteral(L,"file");
|
|
return 1;
|
|
}
|
|
static FILE*tofile(lua_State*L){
|
|
FILE**f=tofilep(L);
|
|
if(*f==NULL)
|
|
luaL_error(L,"attempt to use a closed file");
|
|
return*f;
|
|
}
|
|
static FILE**newfile(lua_State*L){
|
|
FILE**pf=(FILE**)lua_newuserdata(L,sizeof(FILE*));
|
|
*pf=NULL;
|
|
luaL_getmetatable(L,"FILE*");
|
|
lua_setmetatable(L,-2);
|
|
return pf;
|
|
}
|
|
static int io_noclose(lua_State*L){
|
|
lua_pushnil(L);
|
|
lua_pushliteral(L,"cannot close standard file");
|
|
return 2;
|
|
}
|
|
static int io_pclose(lua_State*L){
|
|
FILE**p=tofilep(L);
|
|
int ok=lua_pclose(L,*p);
|
|
*p=NULL;
|
|
return pushresult(L,ok,NULL);
|
|
}
|
|
static int io_fclose(lua_State*L){
|
|
FILE**p=tofilep(L);
|
|
int ok=(fclose(*p)==0);
|
|
*p=NULL;
|
|
return pushresult(L,ok,NULL);
|
|
}
|
|
static int aux_close(lua_State*L){
|
|
lua_getfenv(L,1);
|
|
lua_getfield(L,-1,"__close");
|
|
return(lua_tocfunction(L,-1))(L);
|
|
}
|
|
static int io_close(lua_State*L){
|
|
if(lua_isnone(L,1))
|
|
lua_rawgeti(L,(-10001),2);
|
|
tofile(L);
|
|
return aux_close(L);
|
|
}
|
|
static int io_gc(lua_State*L){
|
|
FILE*f=*tofilep(L);
|
|
if(f!=NULL)
|
|
aux_close(L);
|
|
return 0;
|
|
}
|
|
static int io_open(lua_State*L){
|
|
const char*filename=luaL_checkstring(L,1);
|
|
const char*mode=luaL_optstring(L,2,"r");
|
|
FILE**pf=newfile(L);
|
|
*pf=fopen(filename,mode);
|
|
return(*pf==NULL)?pushresult(L,0,filename):1;
|
|
}
|
|
static FILE*getiofile(lua_State*L,int findex){
|
|
FILE*f;
|
|
lua_rawgeti(L,(-10001),findex);
|
|
f=*(FILE**)lua_touserdata(L,-1);
|
|
if(f==NULL)
|
|
luaL_error(L,"standard %s file is closed",fnames[findex-1]);
|
|
return f;
|
|
}
|
|
static int g_iofile(lua_State*L,int f,const char*mode){
|
|
if(!lua_isnoneornil(L,1)){
|
|
const char*filename=lua_tostring(L,1);
|
|
if(filename){
|
|
FILE**pf=newfile(L);
|
|
*pf=fopen(filename,mode);
|
|
if(*pf==NULL)
|
|
fileerror(L,1,filename);
|
|
}
|
|
else{
|
|
tofile(L);
|
|
lua_pushvalue(L,1);
|
|
}
|
|
lua_rawseti(L,(-10001),f);
|
|
}
|
|
lua_rawgeti(L,(-10001),f);
|
|
return 1;
|
|
}
|
|
static int io_input(lua_State*L){
|
|
return g_iofile(L,1,"r");
|
|
}
|
|
static int io_output(lua_State*L){
|
|
return g_iofile(L,2,"w");
|
|
}
|
|
static int io_readline(lua_State*L);
|
|
static void aux_lines(lua_State*L,int idx,int toclose){
|
|
lua_pushvalue(L,idx);
|
|
lua_pushboolean(L,toclose);
|
|
lua_pushcclosure(L,io_readline,2);
|
|
}
|
|
static int f_lines(lua_State*L){
|
|
tofile(L);
|
|
aux_lines(L,1,0);
|
|
return 1;
|
|
}
|
|
static int io_lines(lua_State*L){
|
|
if(lua_isnoneornil(L,1)){
|
|
lua_rawgeti(L,(-10001),1);
|
|
return f_lines(L);
|
|
}
|
|
else{
|
|
const char*filename=luaL_checkstring(L,1);
|
|
FILE**pf=newfile(L);
|
|
*pf=fopen(filename,"r");
|
|
if(*pf==NULL)
|
|
fileerror(L,1,filename);
|
|
aux_lines(L,lua_gettop(L),1);
|
|
return 1;
|
|
}
|
|
}
|
|
static int read_number(lua_State*L,FILE*f){
|
|
lua_Number d;
|
|
if(fscanf(f,"%lf",&d)==1){
|
|
lua_pushnumber(L,d);
|
|
return 1;
|
|
}
|
|
else{
|
|
lua_pushnil(L);
|
|
return 0;
|
|
}
|
|
}
|
|
static int test_eof(lua_State*L,FILE*f){
|
|
int c=getc(f);
|
|
ungetc(c,f);
|
|
lua_pushlstring(L,NULL,0);
|
|
return(c!=EOF);
|
|
}
|
|
static int read_line(lua_State*L,FILE*f){
|
|
luaL_Buffer b;
|
|
luaL_buffinit(L,&b);
|
|
for(;;){
|
|
size_t l;
|
|
char*p=luaL_prepbuffer(&b);
|
|
if(fgets(p,BUFSIZ,f)==NULL){
|
|
luaL_pushresult(&b);
|
|
return(lua_objlen(L,-1)>0);
|
|
}
|
|
l=strlen(p);
|
|
if(l==0||p[l-1]!='\n')
|
|
luaL_addsize(&b,l);
|
|
else{
|
|
luaL_addsize(&b,l-1);
|
|
luaL_pushresult(&b);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
static int read_chars(lua_State*L,FILE*f,size_t n){
|
|
size_t rlen;
|
|
size_t nr;
|
|
luaL_Buffer b;
|
|
luaL_buffinit(L,&b);
|
|
rlen=BUFSIZ;
|
|
do{
|
|
char*p=luaL_prepbuffer(&b);
|
|
if(rlen>n)rlen=n;
|
|
nr=fread(p,sizeof(char),rlen,f);
|
|
luaL_addsize(&b,nr);
|
|
n-=nr;
|
|
}while(n>0&&nr==rlen);
|
|
luaL_pushresult(&b);
|
|
return(n==0||lua_objlen(L,-1)>0);
|
|
}
|
|
static int g_read(lua_State*L,FILE*f,int first){
|
|
int nargs=lua_gettop(L)-1;
|
|
int success;
|
|
int n;
|
|
clearerr(f);
|
|
if(nargs==0){
|
|
success=read_line(L,f);
|
|
n=first+1;
|
|
}
|
|
else{
|
|
luaL_checkstack(L,nargs+20,"too many arguments");
|
|
success=1;
|
|
for(n=first;nargs--&&success;n++){
|
|
if(lua_type(L,n)==3){
|
|
size_t l=(size_t)lua_tointeger(L,n);
|
|
success=(l==0)?test_eof(L,f):read_chars(L,f,l);
|
|
}
|
|
else{
|
|
const char*p=lua_tostring(L,n);
|
|
luaL_argcheck(L,p&&p[0]=='*',n,"invalid option");
|
|
switch(p[1]){
|
|
case'n':
|
|
success=read_number(L,f);
|
|
break;
|
|
case'l':
|
|
success=read_line(L,f);
|
|
break;
|
|
case'a':
|
|
read_chars(L,f,~((size_t)0));
|
|
success=1;
|
|
break;
|
|
default:
|
|
return luaL_argerror(L,n,"invalid format");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(ferror(f))
|
|
return pushresult(L,0,NULL);
|
|
if(!success){
|
|
lua_pop(L,1);
|
|
lua_pushnil(L);
|
|
}
|
|
return n-first;
|
|
}
|
|
static int io_read(lua_State*L){
|
|
return g_read(L,getiofile(L,1),1);
|
|
}
|
|
static int f_read(lua_State*L){
|
|
return g_read(L,tofile(L),2);
|
|
}
|
|
static int io_readline(lua_State*L){
|
|
FILE*f=*(FILE**)lua_touserdata(L,lua_upvalueindex(1));
|
|
int sucess;
|
|
if(f==NULL)
|
|
luaL_error(L,"file is already closed");
|
|
sucess=read_line(L,f);
|
|
if(ferror(f))
|
|
return luaL_error(L,"%s",strerror(errno));
|
|
if(sucess)return 1;
|
|
else{
|
|
if(lua_toboolean(L,lua_upvalueindex(2))){
|
|
lua_settop(L,0);
|
|
lua_pushvalue(L,lua_upvalueindex(1));
|
|
aux_close(L);
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
static int g_write(lua_State*L,FILE*f,int arg){
|
|
int nargs=lua_gettop(L)-1;
|
|
int status=1;
|
|
for(;nargs--;arg++){
|
|
if(lua_type(L,arg)==3){
|
|
status=status&&
|
|
fprintf(f,"%.14g",lua_tonumber(L,arg))>0;
|
|
}
|
|
else{
|
|
size_t l;
|
|
const char*s=luaL_checklstring(L,arg,&l);
|
|
status=status&&(fwrite(s,sizeof(char),l,f)==l);
|
|
}
|
|
}
|
|
return pushresult(L,status,NULL);
|
|
}
|
|
static int io_write(lua_State*L){
|
|
return g_write(L,getiofile(L,2),1);
|
|
}
|
|
static int f_write(lua_State*L){
|
|
return g_write(L,tofile(L),2);
|
|
}
|
|
static int io_flush(lua_State*L){
|
|
return pushresult(L,fflush(getiofile(L,2))==0,NULL);
|
|
}
|
|
static int f_flush(lua_State*L){
|
|
return pushresult(L,fflush(tofile(L))==0,NULL);
|
|
}
|
|
static const luaL_Reg iolib[]={
|
|
{"close",io_close},
|
|
{"flush",io_flush},
|
|
{"input",io_input},
|
|
{"lines",io_lines},
|
|
{"open",io_open},
|
|
{"output",io_output},
|
|
{"read",io_read},
|
|
{"type",io_type},
|
|
{"write",io_write},
|
|
{NULL,NULL}
|
|
};
|
|
static const luaL_Reg flib[]={
|
|
{"close",io_close},
|
|
{"flush",f_flush},
|
|
{"lines",f_lines},
|
|
{"read",f_read},
|
|
{"write",f_write},
|
|
{"__gc",io_gc},
|
|
{NULL,NULL}
|
|
};
|
|
static void createmeta(lua_State*L){
|
|
luaL_newmetatable(L,"FILE*");
|
|
lua_pushvalue(L,-1);
|
|
lua_setfield(L,-2,"__index");
|
|
luaL_register(L,NULL,flib);
|
|
}
|
|
static void createstdfile(lua_State*L,FILE*f,int k,const char*fname){
|
|
*newfile(L)=f;
|
|
if(k>0){
|
|
lua_pushvalue(L,-1);
|
|
lua_rawseti(L,(-10001),k);
|
|
}
|
|
lua_pushvalue(L,-2);
|
|
lua_setfenv(L,-2);
|
|
lua_setfield(L,-3,fname);
|
|
}
|
|
static void newfenv(lua_State*L,lua_CFunction cls){
|
|
lua_createtable(L,0,1);
|
|
lua_pushcfunction(L,cls);
|
|
lua_setfield(L,-2,"__close");
|
|
}
|
|
static int luaopen_io(lua_State*L){
|
|
createmeta(L);
|
|
newfenv(L,io_fclose);
|
|
lua_replace(L,(-10001));
|
|
luaL_register(L,"io",iolib);
|
|
newfenv(L,io_noclose);
|
|
createstdfile(L,stdin,1,"stdin");
|
|
createstdfile(L,stdout,2,"stdout");
|
|
createstdfile(L,stderr,0,"stderr");
|
|
lua_pop(L,1);
|
|
lua_getfield(L,-1,"popen");
|
|
newfenv(L,io_pclose);
|
|
lua_setfenv(L,-2);
|
|
lua_pop(L,1);
|
|
return 1;
|
|
}
|
|
static int os_pushresult(lua_State*L,int i,const char*filename){
|
|
int en=errno;
|
|
if(i){
|
|
lua_pushboolean(L,1);
|
|
return 1;
|
|
}
|
|
else{
|
|
lua_pushnil(L);
|
|
lua_pushfstring(L,"%s: %s",filename,strerror(en));
|
|
lua_pushinteger(L,en);
|
|
return 3;
|
|
}
|
|
}
|
|
static int os_remove(lua_State*L){
|
|
const char*filename=luaL_checkstring(L,1);
|
|
return os_pushresult(L,remove(filename)==0,filename);
|
|
}
|
|
static int os_exit(lua_State*L){
|
|
exit(luaL_optint(L,1,EXIT_SUCCESS));
|
|
}
|
|
static const luaL_Reg syslib[]={
|
|
{"exit",os_exit},
|
|
{"remove",os_remove},
|
|
{NULL,NULL}
|
|
};
|
|
static int luaopen_os(lua_State*L){
|
|
luaL_register(L,"os",syslib);
|
|
return 1;
|
|
}
|
|
#define uchar(c)((unsigned char)(c))
|
|
static ptrdiff_t posrelat(ptrdiff_t pos,size_t len){
|
|
if(pos<0)pos+=(ptrdiff_t)len+1;
|
|
return(pos>=0)?pos:0;
|
|
}
|
|
static int str_sub(lua_State*L){
|
|
size_t l;
|
|
const char*s=luaL_checklstring(L,1,&l);
|
|
ptrdiff_t start=posrelat(luaL_checkinteger(L,2),l);
|
|
ptrdiff_t end=posrelat(luaL_optinteger(L,3,-1),l);
|
|
if(start<1)start=1;
|
|
if(end>(ptrdiff_t)l)end=(ptrdiff_t)l;
|
|
if(start<=end)
|
|
lua_pushlstring(L,s+start-1,end-start+1);
|
|
else lua_pushliteral(L,"");
|
|
return 1;
|
|
}
|
|
static int str_lower(lua_State*L){
|
|
size_t l;
|
|
size_t i;
|
|
luaL_Buffer b;
|
|
const char*s=luaL_checklstring(L,1,&l);
|
|
luaL_buffinit(L,&b);
|
|
for(i=0;i<l;i++)
|
|
luaL_addchar(&b,tolower(uchar(s[i])));
|
|
luaL_pushresult(&b);
|
|
return 1;
|
|
}
|
|
static int str_upper(lua_State*L){
|
|
size_t l;
|
|
size_t i;
|
|
luaL_Buffer b;
|
|
const char*s=luaL_checklstring(L,1,&l);
|
|
luaL_buffinit(L,&b);
|
|
for(i=0;i<l;i++)
|
|
luaL_addchar(&b,toupper(uchar(s[i])));
|
|
luaL_pushresult(&b);
|
|
return 1;
|
|
}
|
|
static int str_rep(lua_State*L){
|
|
size_t l;
|
|
luaL_Buffer b;
|
|
const char*s=luaL_checklstring(L,1,&l);
|
|
int n=luaL_checkint(L,2);
|
|
luaL_buffinit(L,&b);
|
|
while(n-->0)
|
|
luaL_addlstring(&b,s,l);
|
|
luaL_pushresult(&b);
|
|
return 1;
|
|
}
|
|
static int str_byte(lua_State*L){
|
|
size_t l;
|
|
const char*s=luaL_checklstring(L,1,&l);
|
|
ptrdiff_t posi=posrelat(luaL_optinteger(L,2,1),l);
|
|
ptrdiff_t pose=posrelat(luaL_optinteger(L,3,posi),l);
|
|
int n,i;
|
|
if(posi<=0)posi=1;
|
|
if((size_t)pose>l)pose=l;
|
|
if(posi>pose)return 0;
|
|
n=(int)(pose-posi+1);
|
|
if(posi+n<=pose)
|
|
luaL_error(L,"string slice too long");
|
|
luaL_checkstack(L,n,"string slice too long");
|
|
for(i=0;i<n;i++)
|
|
lua_pushinteger(L,uchar(s[posi+i-1]));
|
|
return n;
|
|
}
|
|
static int str_char(lua_State*L){
|
|
int n=lua_gettop(L);
|
|
int i;
|
|
luaL_Buffer b;
|
|
luaL_buffinit(L,&b);
|
|
for(i=1;i<=n;i++){
|
|
int c=luaL_checkint(L,i);
|
|
luaL_argcheck(L,uchar(c)==c,i,"invalid value");
|
|
luaL_addchar(&b,uchar(c));
|
|
}
|
|
luaL_pushresult(&b);
|
|
return 1;
|
|
}
|
|
typedef struct MatchState{
|
|
const char*src_init;
|
|
const char*src_end;
|
|
lua_State*L;
|
|
int level;
|
|
struct{
|
|
const char*init;
|
|
ptrdiff_t len;
|
|
}capture[32];
|
|
}MatchState;
|
|
static int check_capture(MatchState*ms,int l){
|
|
l-='1';
|
|
if(l<0||l>=ms->level||ms->capture[l].len==(-1))
|
|
return luaL_error(ms->L,"invalid capture index");
|
|
return l;
|
|
}
|
|
static int capture_to_close(MatchState*ms){
|
|
int level=ms->level;
|
|
for(level--;level>=0;level--)
|
|
if(ms->capture[level].len==(-1))return level;
|
|
return luaL_error(ms->L,"invalid pattern capture");
|
|
}
|
|
static const char*classend(MatchState*ms,const char*p){
|
|
switch(*p++){
|
|
case'%':{
|
|
if(*p=='\0')
|
|
luaL_error(ms->L,"malformed pattern (ends with "LUA_QL("%%")")");
|
|
return p+1;
|
|
}
|
|
case'[':{
|
|
if(*p=='^')p++;
|
|
do{
|
|
if(*p=='\0')
|
|
luaL_error(ms->L,"malformed pattern (missing "LUA_QL("]")")");
|
|
if(*(p++)=='%'&&*p!='\0')
|
|
p++;
|
|
}while(*p!=']');
|
|
return p+1;
|
|
}
|
|
default:{
|
|
return p;
|
|
}
|
|
}
|
|
}
|
|
static int match_class(int c,int cl){
|
|
int res;
|
|
switch(tolower(cl)){
|
|
case'a':res=isalpha(c);break;
|
|
case'c':res=iscntrl(c);break;
|
|
case'd':res=isdigit(c);break;
|
|
case'l':res=islower(c);break;
|
|
case'p':res=ispunct(c);break;
|
|
case's':res=isspace(c);break;
|
|
case'u':res=isupper(c);break;
|
|
case'w':res=isalnum(c);break;
|
|
case'x':res=isxdigit(c);break;
|
|
case'z':res=(c==0);break;
|
|
default:return(cl==c);
|
|
}
|
|
return(islower(cl)?res:!res);
|
|
}
|
|
static int matchbracketclass(int c,const char*p,const char*ec){
|
|
int sig=1;
|
|
if(*(p+1)=='^'){
|
|
sig=0;
|
|
p++;
|
|
}
|
|
while(++p<ec){
|
|
if(*p=='%'){
|
|
p++;
|
|
if(match_class(c,uchar(*p)))
|
|
return sig;
|
|
}
|
|
else if((*(p+1)=='-')&&(p+2<ec)){
|
|
p+=2;
|
|
if(uchar(*(p-2))<=c&&c<=uchar(*p))
|
|
return sig;
|
|
}
|
|
else if(uchar(*p)==c)return sig;
|
|
}
|
|
return!sig;
|
|
}
|
|
static int singlematch(int c,const char*p,const char*ep){
|
|
switch(*p){
|
|
case'.':return 1;
|
|
case'%':return match_class(c,uchar(*(p+1)));
|
|
case'[':return matchbracketclass(c,p,ep-1);
|
|
default:return(uchar(*p)==c);
|
|
}
|
|
}
|
|
static const char*match(MatchState*ms,const char*s,const char*p);
|
|
static const char*matchbalance(MatchState*ms,const char*s,
|
|
const char*p){
|
|
if(*p==0||*(p+1)==0)
|
|
luaL_error(ms->L,"unbalanced pattern");
|
|
if(*s!=*p)return NULL;
|
|
else{
|
|
int b=*p;
|
|
int e=*(p+1);
|
|
int cont=1;
|
|
while(++s<ms->src_end){
|
|
if(*s==e){
|
|
if(--cont==0)return s+1;
|
|
}
|
|
else if(*s==b)cont++;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
static const char*max_expand(MatchState*ms,const char*s,
|
|
const char*p,const char*ep){
|
|
ptrdiff_t i=0;
|
|
while((s+i)<ms->src_end&&singlematch(uchar(*(s+i)),p,ep))
|
|
i++;
|
|
while(i>=0){
|
|
const char*res=match(ms,(s+i),ep+1);
|
|
if(res)return res;
|
|
i--;
|
|
}
|
|
return NULL;
|
|
}
|
|
static const char*min_expand(MatchState*ms,const char*s,
|
|
const char*p,const char*ep){
|
|
for(;;){
|
|
const char*res=match(ms,s,ep+1);
|
|
if(res!=NULL)
|
|
return res;
|
|
else if(s<ms->src_end&&singlematch(uchar(*s),p,ep))
|
|
s++;
|
|
else return NULL;
|
|
}
|
|
}
|
|
static const char*start_capture(MatchState*ms,const char*s,
|
|
const char*p,int what){
|
|
const char*res;
|
|
int level=ms->level;
|
|
if(level>=32)luaL_error(ms->L,"too many captures");
|
|
ms->capture[level].init=s;
|
|
ms->capture[level].len=what;
|
|
ms->level=level+1;
|
|
if((res=match(ms,s,p))==NULL)
|
|
ms->level--;
|
|
return res;
|
|
}
|
|
static const char*end_capture(MatchState*ms,const char*s,
|
|
const char*p){
|
|
int l=capture_to_close(ms);
|
|
const char*res;
|
|
ms->capture[l].len=s-ms->capture[l].init;
|
|
if((res=match(ms,s,p))==NULL)
|
|
ms->capture[l].len=(-1);
|
|
return res;
|
|
}
|
|
static const char*match_capture(MatchState*ms,const char*s,int l){
|
|
size_t len;
|
|
l=check_capture(ms,l);
|
|
len=ms->capture[l].len;
|
|
if((size_t)(ms->src_end-s)>=len&&
|
|
memcmp(ms->capture[l].init,s,len)==0)
|
|
return s+len;
|
|
else return NULL;
|
|
}
|
|
static const char*match(MatchState*ms,const char*s,const char*p){
|
|
init:
|
|
switch(*p){
|
|
case'(':{
|
|
if(*(p+1)==')')
|
|
return start_capture(ms,s,p+2,(-2));
|
|
else
|
|
return start_capture(ms,s,p+1,(-1));
|
|
}
|
|
case')':{
|
|
return end_capture(ms,s,p+1);
|
|
}
|
|
case'%':{
|
|
switch(*(p+1)){
|
|
case'b':{
|
|
s=matchbalance(ms,s,p+2);
|
|
if(s==NULL)return NULL;
|
|
p+=4;goto init;
|
|
}
|
|
case'f':{
|
|
const char*ep;char previous;
|
|
p+=2;
|
|
if(*p!='[')
|
|
luaL_error(ms->L,"missing "LUA_QL("[")" after "
|
|
LUA_QL("%%f")" in pattern");
|
|
ep=classend(ms,p);
|
|
previous=(s==ms->src_init)?'\0':*(s-1);
|
|
if(matchbracketclass(uchar(previous),p,ep-1)||
|
|
!matchbracketclass(uchar(*s),p,ep-1))return NULL;
|
|
p=ep;goto init;
|
|
}
|
|
default:{
|
|
if(isdigit(uchar(*(p+1)))){
|
|
s=match_capture(ms,s,uchar(*(p+1)));
|
|
if(s==NULL)return NULL;
|
|
p+=2;goto init;
|
|
}
|
|
goto dflt;
|
|
}
|
|
}
|
|
}
|
|
case'\0':{
|
|
return s;
|
|
}
|
|
case'$':{
|
|
if(*(p+1)=='\0')
|
|
return(s==ms->src_end)?s:NULL;
|
|
else goto dflt;
|
|
}
|
|
default:dflt:{
|
|
const char*ep=classend(ms,p);
|
|
int m=s<ms->src_end&&singlematch(uchar(*s),p,ep);
|
|
switch(*ep){
|
|
case'?':{
|
|
const char*res;
|
|
if(m&&((res=match(ms,s+1,ep+1))!=NULL))
|
|
return res;
|
|
p=ep+1;goto init;
|
|
}
|
|
case'*':{
|
|
return max_expand(ms,s,p,ep);
|
|
}
|
|
case'+':{
|
|
return(m?max_expand(ms,s+1,p,ep):NULL);
|
|
}
|
|
case'-':{
|
|
return min_expand(ms,s,p,ep);
|
|
}
|
|
default:{
|
|
if(!m)return NULL;
|
|
s++;p=ep;goto init;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
static const char*lmemfind(const char*s1,size_t l1,
|
|
const char*s2,size_t l2){
|
|
if(l2==0)return s1;
|
|
else if(l2>l1)return NULL;
|
|
else{
|
|
const char*init;
|
|
l2--;
|
|
l1=l1-l2;
|
|
while(l1>0&&(init=(const char*)memchr(s1,*s2,l1))!=NULL){
|
|
init++;
|
|
if(memcmp(init,s2+1,l2)==0)
|
|
return init-1;
|
|
else{
|
|
l1-=init-s1;
|
|
s1=init;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
static void push_onecapture(MatchState*ms,int i,const char*s,
|
|
const char*e){
|
|
if(i>=ms->level){
|
|
if(i==0)
|
|
lua_pushlstring(ms->L,s,e-s);
|
|
else
|
|
luaL_error(ms->L,"invalid capture index");
|
|
}
|
|
else{
|
|
ptrdiff_t l=ms->capture[i].len;
|
|
if(l==(-1))luaL_error(ms->L,"unfinished capture");
|
|
if(l==(-2))
|
|
lua_pushinteger(ms->L,ms->capture[i].init-ms->src_init+1);
|
|
else
|
|
lua_pushlstring(ms->L,ms->capture[i].init,l);
|
|
}
|
|
}
|
|
static int push_captures(MatchState*ms,const char*s,const char*e){
|
|
int i;
|
|
int nlevels=(ms->level==0&&s)?1:ms->level;
|
|
luaL_checkstack(ms->L,nlevels,"too many captures");
|
|
for(i=0;i<nlevels;i++)
|
|
push_onecapture(ms,i,s,e);
|
|
return nlevels;
|
|
}
|
|
static int str_find_aux(lua_State*L,int find){
|
|
size_t l1,l2;
|
|
const char*s=luaL_checklstring(L,1,&l1);
|
|
const char*p=luaL_checklstring(L,2,&l2);
|
|
ptrdiff_t init=posrelat(luaL_optinteger(L,3,1),l1)-1;
|
|
if(init<0)init=0;
|
|
else if((size_t)(init)>l1)init=(ptrdiff_t)l1;
|
|
if(find&&(lua_toboolean(L,4)||
|
|
strpbrk(p,"^$*+?.([%-")==NULL)){
|
|
const char*s2=lmemfind(s+init,l1-init,p,l2);
|
|
if(s2){
|
|
lua_pushinteger(L,s2-s+1);
|
|
lua_pushinteger(L,s2-s+l2);
|
|
return 2;
|
|
}
|
|
}
|
|
else{
|
|
MatchState ms;
|
|
int anchor=(*p=='^')?(p++,1):0;
|
|
const char*s1=s+init;
|
|
ms.L=L;
|
|
ms.src_init=s;
|
|
ms.src_end=s+l1;
|
|
do{
|
|
const char*res;
|
|
ms.level=0;
|
|
if((res=match(&ms,s1,p))!=NULL){
|
|
if(find){
|
|
lua_pushinteger(L,s1-s+1);
|
|
lua_pushinteger(L,res-s);
|
|
return push_captures(&ms,NULL,0)+2;
|
|
}
|
|
else
|
|
return push_captures(&ms,s1,res);
|
|
}
|
|
}while(s1++<ms.src_end&&!anchor);
|
|
}
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
static int str_find(lua_State*L){
|
|
return str_find_aux(L,1);
|
|
}
|
|
static int str_match(lua_State*L){
|
|
return str_find_aux(L,0);
|
|
}
|
|
static int gmatch_aux(lua_State*L){
|
|
MatchState ms;
|
|
size_t ls;
|
|
const char*s=lua_tolstring(L,lua_upvalueindex(1),&ls);
|
|
const char*p=lua_tostring(L,lua_upvalueindex(2));
|
|
const char*src;
|
|
ms.L=L;
|
|
ms.src_init=s;
|
|
ms.src_end=s+ls;
|
|
for(src=s+(size_t)lua_tointeger(L,lua_upvalueindex(3));
|
|
src<=ms.src_end;
|
|
src++){
|
|
const char*e;
|
|
ms.level=0;
|
|
if((e=match(&ms,src,p))!=NULL){
|
|
lua_Integer newstart=e-s;
|
|
if(e==src)newstart++;
|
|
lua_pushinteger(L,newstart);
|
|
lua_replace(L,lua_upvalueindex(3));
|
|
return push_captures(&ms,src,e);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
static int gmatch(lua_State*L){
|
|
luaL_checkstring(L,1);
|
|
luaL_checkstring(L,2);
|
|
lua_settop(L,2);
|
|
lua_pushinteger(L,0);
|
|
lua_pushcclosure(L,gmatch_aux,3);
|
|
return 1;
|
|
}
|
|
static void add_s(MatchState*ms,luaL_Buffer*b,const char*s,
|
|
const char*e){
|
|
size_t l,i;
|
|
const char*news=lua_tolstring(ms->L,3,&l);
|
|
for(i=0;i<l;i++){
|
|
if(news[i]!='%')
|
|
luaL_addchar(b,news[i]);
|
|
else{
|
|
i++;
|
|
if(!isdigit(uchar(news[i])))
|
|
luaL_addchar(b,news[i]);
|
|
else if(news[i]=='0')
|
|
luaL_addlstring(b,s,e-s);
|
|
else{
|
|
push_onecapture(ms,news[i]-'1',s,e);
|
|
luaL_addvalue(b);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
static void add_value(MatchState*ms,luaL_Buffer*b,const char*s,
|
|
const char*e){
|
|
lua_State*L=ms->L;
|
|
switch(lua_type(L,3)){
|
|
case 3:
|
|
case 4:{
|
|
add_s(ms,b,s,e);
|
|
return;
|
|
}
|
|
case 6:{
|
|
int n;
|
|
lua_pushvalue(L,3);
|
|
n=push_captures(ms,s,e);
|
|
lua_call(L,n,1);
|
|
break;
|
|
}
|
|
case 5:{
|
|
push_onecapture(ms,0,s,e);
|
|
lua_gettable(L,3);
|
|
break;
|
|
}
|
|
}
|
|
if(!lua_toboolean(L,-1)){
|
|
lua_pop(L,1);
|
|
lua_pushlstring(L,s,e-s);
|
|
}
|
|
else if(!lua_isstring(L,-1))
|
|
luaL_error(L,"invalid replacement value (a %s)",luaL_typename(L,-1));
|
|
luaL_addvalue(b);
|
|
}
|
|
static int str_gsub(lua_State*L){
|
|
size_t srcl;
|
|
const char*src=luaL_checklstring(L,1,&srcl);
|
|
const char*p=luaL_checkstring(L,2);
|
|
int tr=lua_type(L,3);
|
|
int max_s=luaL_optint(L,4,srcl+1);
|
|
int anchor=(*p=='^')?(p++,1):0;
|
|
int n=0;
|
|
MatchState ms;
|
|
luaL_Buffer b;
|
|
luaL_argcheck(L,tr==3||tr==4||
|
|
tr==6||tr==5,3,
|
|
"string/function/table expected");
|
|
luaL_buffinit(L,&b);
|
|
ms.L=L;
|
|
ms.src_init=src;
|
|
ms.src_end=src+srcl;
|
|
while(n<max_s){
|
|
const char*e;
|
|
ms.level=0;
|
|
e=match(&ms,src,p);
|
|
if(e){
|
|
n++;
|
|
add_value(&ms,&b,src,e);
|
|
}
|
|
if(e&&e>src)
|
|
src=e;
|
|
else if(src<ms.src_end)
|
|
luaL_addchar(&b,*src++);
|
|
else break;
|
|
if(anchor)break;
|
|
}
|
|
luaL_addlstring(&b,src,ms.src_end-src);
|
|
luaL_pushresult(&b);
|
|
lua_pushinteger(L,n);
|
|
return 2;
|
|
}
|
|
static void addquoted(lua_State*L,luaL_Buffer*b,int arg){
|
|
size_t l;
|
|
const char*s=luaL_checklstring(L,arg,&l);
|
|
luaL_addchar(b,'"');
|
|
while(l--){
|
|
switch(*s){
|
|
case'"':case'\\':case'\n':{
|
|
luaL_addchar(b,'\\');
|
|
luaL_addchar(b,*s);
|
|
break;
|
|
}
|
|
case'\r':{
|
|
luaL_addlstring(b,"\\r",2);
|
|
break;
|
|
}
|
|
case'\0':{
|
|
luaL_addlstring(b,"\\000",4);
|
|
break;
|
|
}
|
|
default:{
|
|
luaL_addchar(b,*s);
|
|
break;
|
|
}
|
|
}
|
|
s++;
|
|
}
|
|
luaL_addchar(b,'"');
|
|
}
|
|
static const char*scanformat(lua_State*L,const char*strfrmt,char*form){
|
|
const char*p=strfrmt;
|
|
while(*p!='\0'&&strchr("-+ #0",*p)!=NULL)p++;
|
|
if((size_t)(p-strfrmt)>=sizeof("-+ #0"))
|
|
luaL_error(L,"invalid format (repeated flags)");
|
|
if(isdigit(uchar(*p)))p++;
|
|
if(isdigit(uchar(*p)))p++;
|
|
if(*p=='.'){
|
|
p++;
|
|
if(isdigit(uchar(*p)))p++;
|
|
if(isdigit(uchar(*p)))p++;
|
|
}
|
|
if(isdigit(uchar(*p)))
|
|
luaL_error(L,"invalid format (width or precision too long)");
|
|
*(form++)='%';
|
|
strncpy(form,strfrmt,p-strfrmt+1);
|
|
form+=p-strfrmt+1;
|
|
*form='\0';
|
|
return p;
|
|
}
|
|
static void addintlen(char*form){
|
|
size_t l=strlen(form);
|
|
char spec=form[l-1];
|
|
strcpy(form+l-1,"l");
|
|
form[l+sizeof("l")-2]=spec;
|
|
form[l+sizeof("l")-1]='\0';
|
|
}
|
|
static int str_format(lua_State*L){
|
|
int top=lua_gettop(L);
|
|
int arg=1;
|
|
size_t sfl;
|
|
const char*strfrmt=luaL_checklstring(L,arg,&sfl);
|
|
const char*strfrmt_end=strfrmt+sfl;
|
|
luaL_Buffer b;
|
|
luaL_buffinit(L,&b);
|
|
while(strfrmt<strfrmt_end){
|
|
if(*strfrmt!='%')
|
|
luaL_addchar(&b,*strfrmt++);
|
|
else if(*++strfrmt=='%')
|
|
luaL_addchar(&b,*strfrmt++);
|
|
else{
|
|
char form[(sizeof("-+ #0")+sizeof("l")+10)];
|
|
char buff[512];
|
|
if(++arg>top)
|
|
luaL_argerror(L,arg,"no value");
|
|
strfrmt=scanformat(L,strfrmt,form);
|
|
switch(*strfrmt++){
|
|
case'c':{
|
|
sprintf(buff,form,(int)luaL_checknumber(L,arg));
|
|
break;
|
|
}
|
|
case'd':case'i':{
|
|
addintlen(form);
|
|
sprintf(buff,form,(long)luaL_checknumber(L,arg));
|
|
break;
|
|
}
|
|
case'o':case'u':case'x':case'X':{
|
|
addintlen(form);
|
|
sprintf(buff,form,(unsigned long)luaL_checknumber(L,arg));
|
|
break;
|
|
}
|
|
case'e':case'E':case'f':
|
|
case'g':case'G':{
|
|
sprintf(buff,form,(double)luaL_checknumber(L,arg));
|
|
break;
|
|
}
|
|
case'q':{
|
|
addquoted(L,&b,arg);
|
|
continue;
|
|
}
|
|
case's':{
|
|
size_t l;
|
|
const char*s=luaL_checklstring(L,arg,&l);
|
|
if(!strchr(form,'.')&&l>=100){
|
|
lua_pushvalue(L,arg);
|
|
luaL_addvalue(&b);
|
|
continue;
|
|
}
|
|
else{
|
|
sprintf(buff,form,s);
|
|
break;
|
|
}
|
|
}
|
|
default:{
|
|
return luaL_error(L,"invalid option "LUA_QL("%%%c")" to "
|
|
LUA_QL("format"),*(strfrmt-1));
|
|
}
|
|
}
|
|
luaL_addlstring(&b,buff,strlen(buff));
|
|
}
|
|
}
|
|
luaL_pushresult(&b);
|
|
return 1;
|
|
}
|
|
static const luaL_Reg strlib[]={
|
|
{"byte",str_byte},
|
|
{"char",str_char},
|
|
{"find",str_find},
|
|
{"format",str_format},
|
|
{"gmatch",gmatch},
|
|
{"gsub",str_gsub},
|
|
{"lower",str_lower},
|
|
{"match",str_match},
|
|
{"rep",str_rep},
|
|
{"sub",str_sub},
|
|
{"upper",str_upper},
|
|
{NULL,NULL}
|
|
};
|
|
static void createmetatable(lua_State*L){
|
|
lua_createtable(L,0,1);
|
|
lua_pushliteral(L,"");
|
|
lua_pushvalue(L,-2);
|
|
lua_setmetatable(L,-2);
|
|
lua_pop(L,1);
|
|
lua_pushvalue(L,-2);
|
|
lua_setfield(L,-2,"__index");
|
|
lua_pop(L,1);
|
|
}
|
|
static int luaopen_string(lua_State*L){
|
|
luaL_register(L,"string",strlib);
|
|
createmetatable(L);
|
|
return 1;
|
|
}
|
|
static const luaL_Reg lualibs[]={
|
|
{"",luaopen_base},
|
|
{"table",luaopen_table},
|
|
{"io",luaopen_io},
|
|
{"os",luaopen_os},
|
|
{"string",luaopen_string},
|
|
{NULL,NULL}
|
|
};
|
|
static void luaL_openlibs(lua_State*L){
|
|
const luaL_Reg*lib=lualibs;
|
|
for(;lib->func;lib++){
|
|
lua_pushcfunction(L,lib->func);
|
|
lua_pushstring(L,lib->name);
|
|
lua_call(L,1,0);
|
|
}
|
|
}
|
|
typedef unsigned int UB;
|
|
static UB barg(lua_State*L,int idx){
|
|
union{lua_Number n;U64 b;}bn;
|
|
bn.n=lua_tonumber(L,idx)+6755399441055744.0;
|
|
if(bn.n==0.0&&!lua_isnumber(L,idx))luaL_typerror(L,idx,"number");
|
|
return(UB)bn.b;
|
|
}
|
|
#define BRET(b)lua_pushnumber(L,(lua_Number)(int)(b));return 1;
|
|
static int tobit(lua_State*L){
|
|
BRET(barg(L,1))}
|
|
static int bnot(lua_State*L){
|
|
BRET(~barg(L,1))}
|
|
static int band(lua_State*L){
|
|
int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b&=barg(L,i);BRET(b)}
|
|
static int bor(lua_State*L){
|
|
int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b|=barg(L,i);BRET(b)}
|
|
static int bxor(lua_State*L){
|
|
int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b^=barg(L,i);BRET(b)}
|
|
static int lshift(lua_State*L){
|
|
UB b=barg(L,1),n=barg(L,2)&31;BRET(b<<n)}
|
|
static int rshift(lua_State*L){
|
|
UB b=barg(L,1),n=barg(L,2)&31;BRET(b>>n)}
|
|
static int arshift(lua_State*L){
|
|
UB b=barg(L,1),n=barg(L,2)&31;BRET((int)b>>n)}
|
|
static int rol(lua_State*L){
|
|
UB b=barg(L,1),n=barg(L,2)&31;BRET((b<<n)|(b>>(32-n)))}
|
|
static int ror(lua_State*L){
|
|
UB b=barg(L,1),n=barg(L,2)&31;BRET((b>>n)|(b<<(32-n)))}
|
|
static int bswap(lua_State*L){
|
|
UB b=barg(L,1);b=(b>>24)|((b>>8)&0xff00)|((b&0xff00)<<8)|(b<<24);BRET(b)}
|
|
static int tohex(lua_State*L){
|
|
UB b=barg(L,1);
|
|
int n=lua_isnone(L,2)?8:(int)barg(L,2);
|
|
const char*hexdigits="0123456789abcdef";
|
|
char buf[8];
|
|
int i;
|
|
if(n<0){n=-n;hexdigits="0123456789ABCDEF";}
|
|
if(n>8)n=8;
|
|
for(i=(int)n;--i>=0;){buf[i]=hexdigits[b&15];b>>=4;}
|
|
lua_pushlstring(L,buf,(size_t)n);
|
|
return 1;
|
|
}
|
|
static const struct luaL_Reg bitlib[]={
|
|
{"tobit",tobit},
|
|
{"bnot",bnot},
|
|
{"band",band},
|
|
{"bor",bor},
|
|
{"bxor",bxor},
|
|
{"lshift",lshift},
|
|
{"rshift",rshift},
|
|
{"arshift",arshift},
|
|
{"rol",rol},
|
|
{"ror",ror},
|
|
{"bswap",bswap},
|
|
{"tohex",tohex},
|
|
{NULL,NULL}
|
|
};
|
|
int main(int argc,char**argv){
|
|
lua_State*L=luaL_newstate();
|
|
int i;
|
|
luaL_openlibs(L);
|
|
luaL_register(L,"bit",bitlib);
|
|
if(argc<2)return sizeof(void*);
|
|
lua_createtable(L,0,1);
|
|
lua_pushstring(L,argv[1]);
|
|
lua_rawseti(L,-2,0);
|
|
lua_setglobal(L,"arg");
|
|
if(luaL_loadfile(L,argv[1]))
|
|
goto err;
|
|
for(i=2;i<argc;i++)
|
|
lua_pushstring(L,argv[i]);
|
|
if(lua_pcall(L,argc-2,0,0)){
|
|
err:
|
|
fprintf(stderr,"Error: %s\n",lua_tostring(L,-1));
|
|
return 1;
|
|
}
|
|
lua_close(L);
|
|
return 0;
|
|
}
|