/* ** $Id: lobject.c $ ** Some generic functions over Lua objects ** See Copyright Notice in lua.h */ #define lobject_c #define LUA_CORE #include "lprefix.h" #include #include #include #include #include #include #include "lua.h" #include "lctype.h" #include "ldebug.h" #include "ldo.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" #include "lstring.h" #include "lvm.h" /* ** Computes ceil(log2(x)) */ int luaO_ceillog2 (unsigned int x) { static const lu_byte log_2[256] = { /* log_2[i] = ceil(log2(i - 1)) */ 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 = 0; x--; while (x >= 256) { l += 8; x >>= 8; } return l + log_2[x]; } static lua_Integer intarith (lua_State *L, int op, lua_Integer v1, lua_Integer v2) { switch (op) { case LUA_OPADD: return intop(+, v1, v2); case LUA_OPSUB:return intop(-, v1, v2); case LUA_OPMUL:return intop(*, v1, v2); case LUA_OPMOD: return luaV_mod(L, v1, v2); case LUA_OPIDIV: return luaV_idiv(L, v1, v2); case LUA_OPBAND: return intop(&, v1, v2); case LUA_OPBOR: return intop(|, v1, v2); case LUA_OPBXOR: return intop(^, v1, v2); case LUA_OPSHL: return luaV_shiftl(v1, v2); case LUA_OPSHR: return luaV_shiftl(v1, -v2); case LUA_OPUNM: return intop(-, 0, v1); case LUA_OPBNOT: return intop(^, ~l_castS2U(0), v1); default: lua_assert(0); return 0; } } static lua_Number numarith (lua_State *L, int op, lua_Number v1, lua_Number v2) { switch (op) { case LUA_OPADD: return luai_numadd(L, v1, v2); case LUA_OPSUB: return luai_numsub(L, v1, v2); case LUA_OPMUL: return luai_nummul(L, v1, v2); case LUA_OPDIV: return luai_numdiv(L, v1, v2); case LUA_OPPOW: return luai_numpow(L, v1, v2); case LUA_OPIDIV: return luai_numidiv(L, v1, v2); case LUA_OPUNM: return luai_numunm(L, v1); case LUA_OPMOD: return luaV_modf(L, v1, v2); default: lua_assert(0); return 0; } } int luaO_rawarith (lua_State *L, int op, const TValue *p1, const TValue *p2, TValue *res) { switch (op) { case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR: case LUA_OPSHL: case LUA_OPSHR: case LUA_OPBNOT: { /* operate only on integers */ lua_Integer i1; lua_Integer i2; if (tointegerns(p1, &i1) && tointegerns(p2, &i2)) { setivalue(res, intarith(L, op, i1, i2)); return 1; } else return 0; /* fail */ } case LUA_OPDIV: case LUA_OPPOW: { /* operate only on floats */ lua_Number n1; lua_Number n2; if (tonumberns(p1, n1) && tonumberns(p2, n2)) { setfltvalue(res, numarith(L, op, n1, n2)); return 1; } else return 0; /* fail */ } default: { /* other operations */ lua_Number n1; lua_Number n2; if (ttisinteger(p1) && ttisinteger(p2)) { setivalue(res, intarith(L, op, ivalue(p1), ivalue(p2))); return 1; } else if (tonumberns(p1, n1) && tonumberns(p2, n2)) { setfltvalue(res, numarith(L, op, n1, n2)); return 1; } else return 0; /* fail */ } } } void luaO_arith (lua_State *L, int op, const TValue *p1, const TValue *p2, StkId res) { if (!luaO_rawarith(L, op, p1, p2, s2v(res))) { /* could not perform raw operation; try metamethod */ luaT_trybinTM(L, p1, p2, res, cast(TMS, (op - LUA_OPADD) + TM_ADD)); } } int luaO_hexavalue (int c) { if (lisdigit(c)) return c - '0'; else return (ltolower(c) - 'a') + 10; } static int isneg (const char **s) { if (**s == '-') { (*s)++; return 1; } else if (**s == '+') (*s)++; return 0; } /* ** {================================================================== ** Lua's implementation for 'lua_strx2number' ** =================================================================== */ #if !defined(lua_strx2number) /* maximum number of significant digits to read (to avoid overflows even with single floats) */ #define MAXSIGDIG 30 /* ** convert a hexadecimal numeric string to a number, following ** C99 specification for 'strtod' */ static lua_Number lua_strx2number (const char *s, char **endptr) { int dot = lua_getlocaledecpoint(); lua_Number r = 0.0; /* result (accumulator) */ int sigdig = 0; /* number of significant digits */ int nosigdig = 0; /* number of non-significant digits */ int e = 0; /* exponent correction */ int neg; /* 1 if number is negative */ int hasdot = 0; /* true after seen a dot */ *endptr = cast_charp(s); /* nothing is valid yet */ while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */ neg = isneg(&s); /* check sign */ if (!(*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X'))) /* check '0x' */ return 0.0; /* invalid format (no '0x') */ for (s += 2; ; s++) { /* skip '0x' and read numeral */ if (*s == dot) { if (hasdot) break; /* second dot? stop loop */ else hasdot = 1; } else if (lisxdigit(cast_uchar(*s))) { if (sigdig == 0 && *s == '0') /* non-significant digit (zero)? */ nosigdig++; else if (++sigdig <= MAXSIGDIG) /* can read it without overflow? */ r = (r * cast_num(16.0)) + luaO_hexavalue(*s); else e++; /* too many digits; ignore, but still count for exponent */ if (hasdot) e--; /* decimal digit? correct exponent */ } else break; /* neither a dot nor a digit */ } if (nosigdig + sigdig == 0) /* no digits? */ return 0.0; /* invalid format */ *endptr = cast_charp(s); /* valid up to here */ e *= 4; /* each digit multiplies/divides value by 2^4 */ if (*s == 'p' || *s == 'P') { /* exponent part? */ int exp1 = 0; /* exponent value */ int neg1; /* exponent sign */ s++; /* skip 'p' */ neg1 = isneg(&s); /* sign */ if (!lisdigit(cast_uchar(*s))) return 0.0; /* invalid; must have at least one digit */ while (lisdigit(cast_uchar(*s))) /* read exponent */ exp1 = exp1 * 10 + *(s++) - '0'; if (neg1) exp1 = -exp1; e += exp1; *endptr = cast_charp(s); /* valid up to here */ } if (neg) r = -r; return l_mathop(ldexp)(r, e); } #endif /* }====================================================== */ /* maximum length of a numeral */ #if !defined (L_MAXLENNUM) #define L_MAXLENNUM 200 #endif static const char *l_str2dloc (const char *s, lua_Number *result, int mode) { char *endptr; *result = (mode == 'x') ? lua_strx2number(s, &endptr) /* try to convert */ : lua_str2number(s, &endptr); if (endptr == s) return NULL; /* nothing recognized? */ while (lisspace(cast_uchar(*endptr))) endptr++; /* skip trailing spaces */ return (*endptr == '\0') ? endptr : NULL; /* OK if no trailing characters */ } /* ** Convert string 's' to a Lua number (put in 'result'). Return NULL ** on fail or the address of the ending '\0' on success. ** 'pmode' points to (and 'mode' contains) special things in the string: ** - 'x'/'X' means a hexadecimal numeral ** - 'n'/'N' means 'inf' or 'nan' (which should be rejected) ** - '.' just optimizes the search for the common case (nothing special) ** This function accepts both the current locale or a dot as the radix ** mark. If the conversion fails, it may mean number has a dot but ** locale accepts something else. In that case, the code copies 's' ** to a buffer (because 's' is read-only), changes the dot to the ** current locale radix mark, and tries to convert again. */ static const char *l_str2d (const char *s, lua_Number *result) { const char *endptr; const char *pmode = strpbrk(s, ".xXnN"); int mode = pmode ? ltolower(cast_uchar(*pmode)) : 0; if (mode == 'n') /* reject 'inf' and 'nan' */ return NULL; endptr = l_str2dloc(s, result, mode); /* try to convert */ if (endptr == NULL) { /* failed? may be a different locale */ char buff[L_MAXLENNUM + 1]; const char *pdot = strchr(s, '.'); if (strlen(s) > L_MAXLENNUM || pdot == NULL) return NULL; /* string too long or no dot; fail */ strcpy(buff, s); /* copy string to buffer */ buff[pdot - s] = lua_getlocaledecpoint(); /* correct decimal point */ endptr = l_str2dloc(buff, result, mode); /* try again */ if (endptr != NULL) endptr = s + (endptr - buff); /* make relative to 's' */ } return endptr; } #define MAXBY10 cast(lua_Unsigned, LUA_MAXINTEGER / 10) #define MAXLASTD cast_int(LUA_MAXINTEGER % 10) static const char *l_str2int (const char *s, lua_Integer *result) { lua_Unsigned a = 0; int empty = 1; int neg; while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */ neg = isneg(&s); if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { /* hex? */ s += 2; /* skip '0x' */ for (; lisxdigit(cast_uchar(*s)); s++) { a = a * 16 + luaO_hexavalue(*s); empty = 0; } } else { /* decimal */ for (; lisdigit(cast_uchar(*s)); s++) { int d = *s - '0'; if (a >= MAXBY10 && (a > MAXBY10 || d > MAXLASTD + neg)) /* overflow? */ return NULL; /* do not accept it (as integer) */ a = a * 10 + d; empty = 0; } } while (lisspace(cast_uchar(*s))) s++; /* skip trailing spaces */ if (empty || *s != '\0') return NULL; /* something wrong in the numeral */ else { *result = l_castU2S((neg) ? 0u - a : a); return s; } } size_t luaO_str2num (const char *s, TValue *o) { lua_Integer i; lua_Number n; const char *e; if ((e = l_str2int(s, &i)) != NULL) { /* try as an integer */ setivalue(o, i); } else if ((e = l_str2d(s, &n)) != NULL) { /* else try as a float */ setfltvalue(o, n); } else return 0; /* conversion failed */ return (e - s) + 1; /* success; return string size */ } int luaO_utf8esc (char *buff, unsigned long x) { int n = 1; /* number of bytes put in buffer (backwards) */ lua_assert(x <= 0x7FFFFFFFu); if (x < 0x80) /* ascii? */ buff[UTF8BUFFSZ - 1] = cast_char(x); else { /* need continuation bytes */ unsigned int mfb = 0x3f; /* maximum that fits in first byte */ do { /* add continuation bytes */ buff[UTF8BUFFSZ - (n++)] = cast_char(0x80 | (x & 0x3f)); x >>= 6; /* remove added bits */ mfb >>= 1; /* now there is one less bit available in first byte */ } while (x > mfb); /* still needs continuation byte? */ buff[UTF8BUFFSZ - n] = cast_char((~mfb << 1) | x); /* add first byte */ } return n; } /* maximum length of the conversion of a number to a string */ #define MAXNUMBER2STR 50 /* ** Convert a number object to a string, adding it to a buffer */ static int tostringbuff (TValue *obj, char *buff) { int len; lua_assert(ttisnumber(obj)); if (ttisinteger(obj)) len = lua_integer2str(buff, MAXNUMBER2STR, ivalue(obj)); else { len = lua_number2str(buff, MAXNUMBER2STR, fltvalue(obj)); if (buff[strspn(buff, "-0123456789")] == '\0') { /* looks like an int? */ buff[len++] = lua_getlocaledecpoint(); buff[len++] = '0'; /* adds '.0' to result */ } } return len; } /* ** Convert a number object to a Lua string, replacing the value at 'obj' */ void luaO_tostring (lua_State *L, TValue *obj) { char buff[MAXNUMBER2STR]; int len = tostringbuff(obj, buff); setsvalue(L, obj, luaS_newlstr(L, buff, len)); } /* ** {================================================================== ** 'luaO_pushvfstring' ** =================================================================== */ /* size for buffer space used by 'luaO_pushvfstring' */ #define BUFVFS 400 /* buffer used by 'luaO_pushvfstring' */ typedef struct BuffFS { lua_State *L; int pushed; /* number of string pieces already on the stack */ int blen; /* length of partial string in 'space' */ char space[BUFVFS]; /* holds last part of the result */ } BuffFS; /* ** Push given string to the stack, as part of the buffer. If the stack ** is almost full, join all partial strings in the stack into one. */ static void pushstr (BuffFS *buff, const char *str, size_t l) { lua_State *L = buff->L; setsvalue2s(L, L->top, luaS_newlstr(L, str, l)); L->top++; /* may use one extra slot */ buff->pushed++; if (buff->pushed > 1 && L->top + 1 >= L->stack_last) { luaV_concat(L, buff->pushed); /* join all partial results into one */ buff->pushed = 1; } } /* ** empty the buffer space into the stack */ static void clearbuff (BuffFS *buff) { pushstr(buff, buff->space, buff->blen); /* push buffer contents */ buff->blen = 0; /* space now is empty */ } /* ** Get a space of size 'sz' in the buffer. If buffer has not enough ** space, empty it. 'sz' must fit in an empty buffer. */ static char *getbuff (BuffFS *buff, int sz) { lua_assert(buff->blen <= BUFVFS); lua_assert(sz <= BUFVFS); if (sz > BUFVFS - buff->blen) /* not enough space? */ clearbuff(buff); return buff->space + buff->blen; } #define addsize(b,sz) ((b)->blen += (sz)) /* ** Add 'str' to the buffer. If string is larger than the buffer space, ** push the string directly to the stack. */ static void addstr2buff (BuffFS *buff, const char *str, size_t slen) { if (slen <= BUFVFS) { /* does string fit into buffer? */ char *bf = getbuff(buff, cast_int(slen)); memcpy(bf, str, slen); /* add string to buffer */ addsize(buff, cast_int(slen)); } else { /* string larger than buffer */ clearbuff(buff); /* string comes after buffer's content */ pushstr(buff, str, slen); /* push string */ } } /* ** Add a number to the buffer. */ static void addnum2buff (BuffFS *buff, TValue *num) { char *numbuff = getbuff(buff, MAXNUMBER2STR); int len = tostringbuff(num, numbuff); /* format number into 'numbuff' */ addsize(buff, len); } /* ** this function handles only '%d', '%c', '%f', '%p', '%s', and '%%' conventional formats, plus Lua-specific '%I' and '%U' */ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { BuffFS buff; /* holds last part of the result */ const char *e; /* points to next '%' */ buff.pushed = buff.blen = 0; buff.L = L; while ((e = strchr(fmt, '%')) != NULL) { addstr2buff(&buff, fmt, e - fmt); /* add 'fmt' up to '%' */ switch (*(e + 1)) { /* conversion specifier */ case 's': { /* zero-terminated string */ const char *s = va_arg(argp, char *); if (s == NULL) s = "(null)"; addstr2buff(&buff, s, strlen(s)); break; } case 'c': { /* an 'int' as a character */ char c = cast_uchar(va_arg(argp, int)); addstr2buff(&buff, &c, sizeof(char)); break; } case 'd': { /* an 'int' */ TValue num; setivalue(&num, va_arg(argp, int)); addnum2buff(&buff, &num); break; } case 'I': { /* a 'lua_Integer' */ TValue num; setivalue(&num, cast(lua_Integer, va_arg(argp, l_uacInt))); addnum2buff(&buff, &num); break; } case 'f': { /* a 'lua_Number' */ TValue num; setfltvalue(&num, cast_num(va_arg(argp, l_uacNumber))); addnum2buff(&buff, &num); break; } case 'p': { /* a pointer */ const int sz = 3 * sizeof(void*) + 8; /* enough space for '%p' */ char *bf = getbuff(&buff, sz); void *p = va_arg(argp, void *); int len = lua_pointer2str(bf, sz, p); addsize(&buff, len); break; } case 'U': { /* a 'long' as a UTF-8 sequence */ char bf[UTF8BUFFSZ]; int len = luaO_utf8esc(bf, va_arg(argp, long)); addstr2buff(&buff, bf + UTF8BUFFSZ - len, len); break; } case '%': { addstr2buff(&buff, "%", 1); break; } default: { luaG_runerror(L, "invalid option '%%%c' to 'lua_pushfstring'", *(e + 1)); } } fmt = e + 2; /* skip '%' and the specifier */ } addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */ clearbuff(&buff); /* empty buffer into the stack */ if (buff.pushed > 1) luaV_concat(L, buff.pushed); /* join all partial results */ return svalue(s2v(L->top - 1)); } 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; } /* }================================================================== */ #define RETS "..." #define PRE "[string \"" #define POS "\"]" #define addstr(a,b,l) ( memcpy(a,b,(l) * sizeof(char)), a += (l) ) void luaO_chunkid (char *out, const char *source, size_t srclen) { size_t bufflen = LUA_IDSIZE; /* free space in buffer */ if (*source == '=') { /* 'literal' source */ if (srclen <= bufflen) /* small enough? */ memcpy(out, source + 1, srclen * sizeof(char)); else { /* truncate it */ addstr(out, source + 1, bufflen - 1); *out = '\0'; } } else if (*source == '@') { /* file name */ if (srclen <= bufflen) /* small enough? */ memcpy(out, source + 1, srclen * sizeof(char)); else { /* add '...' before rest of name */ addstr(out, RETS, LL(RETS)); bufflen -= LL(RETS); memcpy(out, source + 1 + srclen - bufflen, bufflen * sizeof(char)); } } else { /* string; format as [string "source"] */ const char *nl = strchr(source, '\n'); /* find first new line (if any) */ addstr(out, PRE, LL(PRE)); /* add prefix */ bufflen -= LL(PRE RETS POS) + 1; /* save space for prefix+suffix+'\0' */ if (srclen < bufflen && nl == NULL) { /* small one-line source? */ addstr(out, source, srclen); /* keep it */ } else { if (nl != NULL) srclen = nl - source; /* stop at first newline */ if (srclen > bufflen) srclen = bufflen; addstr(out, source, srclen); addstr(out, RETS, LL(RETS)); } memcpy(out, POS, (LL(POS) + 1) * sizeof(char)); } }