|
|
@@ -0,0 +1,471 @@
|
|
|
+/*
|
|
|
+** $Id: lundump.c,v 2.44.1.1 2017/04/19 17:20:42 roberto Exp $
|
|
|
+** load precompiled Lua chunks
|
|
|
+** See Copyright Notice in lua.h
|
|
|
+*/
|
|
|
+
|
|
|
+#define lundump_c
|
|
|
+#define LUA_CORE
|
|
|
+
|
|
|
+#include "lprefix.h"
|
|
|
+
|
|
|
+
|
|
|
+#include <string.h>
|
|
|
+
|
|
|
+#include "lua.h"
|
|
|
+
|
|
|
+#include "ldebug.h"
|
|
|
+#include "ldo.h"
|
|
|
+#include "lfunc.h"
|
|
|
+#include "lmem.h"
|
|
|
+#include "lobject.h"
|
|
|
+#include "lstring.h"
|
|
|
+#include "lundump.h"
|
|
|
+#include "lzio.h"
|
|
|
+#include "ltable.h"
|
|
|
+
|
|
|
+#include "luat_base.h"
|
|
|
+#include "luat_fs.h"
|
|
|
+
|
|
|
+#define LUAT_LOG_TAG "undump"
|
|
|
+#include "luat_log.h"
|
|
|
+
|
|
|
+#define LUF_SIGNATURE "\x1cLUF"
|
|
|
+
|
|
|
+
|
|
|
+#if !defined(luai_verifycode)
|
|
|
+#define luai_verifycode(L,b,f) /* empty */
|
|
|
+#endif
|
|
|
+
|
|
|
+typedef struct {
|
|
|
+ lua_State *L;
|
|
|
+ ZIO *Z;
|
|
|
+ const char *name;
|
|
|
+} LoadState;
|
|
|
+
|
|
|
+static void dumpHex(const char* tag, void* ptr, size_t len) {
|
|
|
+ uint8_t* c = (uint8_t*)ptr;
|
|
|
+ for (size_t i = 0; i < len / 8; i++)
|
|
|
+ {
|
|
|
+ LLOGD("%s %p %02X %02X %02X %02X %02X %02X %02X %02X", tag, &c[i*8], c[i*8], c[i*8+1], c[i*8+2], c[i*8+3],
|
|
|
+ c[i*8+4], c[i*8+5], c[i*8+6], c[i*8+7]);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static l_noret error(LoadState *S, const char *why) {
|
|
|
+ luaO_pushfstring(S->L, "luf %s: %s precompiled chunk", S->name, why);
|
|
|
+ luaD_throw(S->L, LUA_ERRSYNTAX);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/*
|
|
|
+** All high-level loads go through LoadVector; you can change it to
|
|
|
+** adapt to the endianness of the input
|
|
|
+*/
|
|
|
+#define LoadVector(S,b,n) LoadBlock(S,b,(n)*sizeof((b)[0]))
|
|
|
+
|
|
|
+static void LoadBlock (LoadState *S, void *b, size_t size) {
|
|
|
+ if (luaZ_read(S->Z, b, size) != 0)
|
|
|
+ error(S, "truncated");
|
|
|
+}
|
|
|
+
|
|
|
+static void* DistBlock (LoadState *S, size_t size) {
|
|
|
+ uint8_t b = 0;
|
|
|
+ const char* p = S->Z->p;
|
|
|
+ for (size_t i = 0; i < size; i++)
|
|
|
+ {
|
|
|
+ if (luaZ_read(S->Z, &b, 1) != 0)
|
|
|
+ error(S, "truncated");
|
|
|
+ }
|
|
|
+ return (void*)p;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#define LoadVar(S,x) LoadVector(S,&x,1)
|
|
|
+
|
|
|
+
|
|
|
+static lu_byte LoadByte (LoadState *S) {
|
|
|
+ lu_byte x;
|
|
|
+ LoadVar(S, x);
|
|
|
+ return x;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static int LoadInt (LoadState *S) {
|
|
|
+ int x;
|
|
|
+ LoadVar(S, x);
|
|
|
+ return x;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static lua_Number LoadNumber (LoadState *S) {
|
|
|
+ lua_Number x;
|
|
|
+ LoadVar(S, x);
|
|
|
+ return x;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static lua_Integer LoadInteger (LoadState *S) {
|
|
|
+ lua_Integer x;
|
|
|
+ LoadVar(S, x);
|
|
|
+ return x;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// static TString *LoadString (LoadState *S, Proto *p) {
|
|
|
+// lu_byte t = LoadByte(S);
|
|
|
+// if (t == 0)
|
|
|
+// return NULL;
|
|
|
+// TString * ts = (TString*)S->Z->p;
|
|
|
+// // LLOGD("LoadString >> %d %p", tsslen(ts), ts);
|
|
|
+// DistBlock(S, sizeof(TString) + tsslen(ts) + 1);
|
|
|
+// // LLOGD("LoadString >> %s", getstr(ts));
|
|
|
+// return ts;
|
|
|
+// }
|
|
|
+
|
|
|
+static void LoadCode (LoadState *S, Proto *f) {
|
|
|
+ // int n = LoadInt(S);
|
|
|
+ // LLOGD("LoadCode %d %d", n, sizeof(Instruction) * n);
|
|
|
+ // f->sizecode = n;
|
|
|
+ // f->code = luat_heap_malloc(sizeof(Instruction) * f->sizecode);
|
|
|
+ // memcpy(f->code, S->Z->p, sizeof(Instruction) * f->sizecode);
|
|
|
+ f->code = DistBlock(S, sizeof(Instruction) * f->sizecode);
|
|
|
+ //f->code = ((uint8_t*)f->code) + 2;
|
|
|
+ LLOGD("f->code %p", f->code);
|
|
|
+ for (size_t i = 0; i < f->sizecode; i++)
|
|
|
+ {
|
|
|
+ LLOGD("Code %02X -> %08X", i, f->code[i]);
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static void LoadFunction(LoadState *S, Proto *f, TString *psource);
|
|
|
+
|
|
|
+
|
|
|
+static void LoadConstants (LoadState *S, Proto *f) {
|
|
|
+ // int i;
|
|
|
+ // int n = LoadInt(S);
|
|
|
+ // LLOGD("LoadConstants %d %d", n, sizeof(TValue) * n);
|
|
|
+ // f->sizek = n;
|
|
|
+ // 指向常数数组
|
|
|
+ f->k = DistBlock(S, sizeof(TValue) * f->sizek);
|
|
|
+ // 跳过字符串段
|
|
|
+ for (size_t i = 0; i < f->sizek; i++)
|
|
|
+ {
|
|
|
+ TValue *t = &f->k[i];
|
|
|
+ switch (ttype(t))
|
|
|
+ {
|
|
|
+ case LUA_TSHRSTR:
|
|
|
+ case LUA_TLNGSTR:
|
|
|
+ LLOGD("const string %p %s", tsvalue(t), getstr(tsvalue(t)));
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ // LLOGD("1>>LoadConstants %02X %02X %02X %02X", *(S->Z->p), *(S->Z->p + 1), *(S->Z->p + 2), *(S->Z->p + 3));
|
|
|
+ // n = LoadInt(S);
|
|
|
+ // LLOGD("LoadConstants skip Strings %d", n);
|
|
|
+ // DistBlock(S, sizeof(char) * n);
|
|
|
+
|
|
|
+ // LLOGD("2>>LoadConstants %02X %02X %02X %02X", *(S->Z->p), *(S->Z->p + 1), *(S->Z->p + 2), *(S->Z->p + 3));
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static void LoadProtos (LoadState *S, Proto *f) {
|
|
|
+ int i;
|
|
|
+ // int n = LoadInt(S);
|
|
|
+ f->p = luaM_newvector(S->L, f->sizep, Proto *);
|
|
|
+ // f->sizep = n;
|
|
|
+ for (i = 0; i < f->sizep; i++)
|
|
|
+ f->p[i] = NULL;
|
|
|
+ for (i = 0; i < f->sizep; i++) {
|
|
|
+ f->p[i] = luaF_newproto(S->L);
|
|
|
+ luaC_objbarrier(S->L, f, f->p[i]);
|
|
|
+ LoadFunction(S, f->p[i], f->source);
|
|
|
+ }
|
|
|
+ // LLOGD("LoadProtos %d %d", n, sizeof(Proto *) * n);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static void LoadUpvalues (LoadState *S, Proto *f) {
|
|
|
+ int i, n;
|
|
|
+ // n = LoadInt(S);
|
|
|
+ // f->sizeupvalues = n;
|
|
|
+ // LLOGD("LoadUpvalues %d %d", n, sizeof(Upvaldesc) * n);
|
|
|
+ f->upvalues = DistBlock(S, sizeof(Upvaldesc) * f->sizeupvalues);
|
|
|
+ // char* tmp = luaM_newvector(S->L, n, Upvaldesc);
|
|
|
+ // memcpy(tmp, f->upvalues, sizeof(Upvaldesc) * n);
|
|
|
+ // f->upvalues = tmp;
|
|
|
+ // 跳过字符串段
|
|
|
+ // n = LoadInt(S);
|
|
|
+ // LLOGD("LoadUpvalues skip Strings %d", n);
|
|
|
+ // DistBlock(S, sizeof(char) * n);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static void LoadDebug (LoadState *S, Proto *f) {
|
|
|
+ int i, n;
|
|
|
+
|
|
|
+ // n = LoadInt(S);
|
|
|
+ // f->sizelineinfo = n;
|
|
|
+ // LLOGD("LoadDebug sizelineinfo %d %d", n, sizeof(int) * n);
|
|
|
+ f->lineinfo = DistBlock(S, sizeof(int) * f->sizelineinfo);
|
|
|
+
|
|
|
+ // n = LoadInt(S);
|
|
|
+ // f->sizelocvars = n;
|
|
|
+ // LLOGD("LoadDebug sizelocvars %d %d", n, sizeof(LocVar) * n);
|
|
|
+ f->locvars = DistBlock(S, sizeof(LocVar) * f->sizelocvars);
|
|
|
+
|
|
|
+ // n = LoadInt(S);
|
|
|
+ // DistBlock(S, sizeof(char) * n);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static void LoadFunction (LoadState *S, Proto *f, TString *psource) {
|
|
|
+ //LLOGD(">> %02X %02X %02X %02X", *(S->Z->p), *(S->Z->p + 1), *(S->Z->p + 2), *(S->Z->p + 3));
|
|
|
+ f->source = psource; /* reuse parent's source */
|
|
|
+
|
|
|
+ if (f->source)
|
|
|
+ LLOGI("%s %d source %s", __FILE__, __LINE__, getstr(f->source));
|
|
|
+ else
|
|
|
+ LLOGD("no source ?");
|
|
|
+
|
|
|
+ f->linedefined = LoadInt(S);
|
|
|
+ f->lastlinedefined = LoadInt(S);
|
|
|
+ f->numparams = LoadByte(S);
|
|
|
+ f->is_vararg = LoadByte(S);
|
|
|
+ f->maxstacksize = LoadByte(S);
|
|
|
+ LoadByte(S); // f->source != NULL ?
|
|
|
+
|
|
|
+ // LLOGD("linedefined %d", f->linedefined);
|
|
|
+ // LLOGD("lastlinedefined %d", f->lastlinedefined);
|
|
|
+ // LLOGD("numparams %d", f->numparams);
|
|
|
+ // LLOGD("is_vararg %d", f->is_vararg);
|
|
|
+ // LLOGD("maxstacksize %d", f->maxstacksize);
|
|
|
+
|
|
|
+ f->sizecode = LoadInt(S);
|
|
|
+ f->sizek = LoadInt(S);
|
|
|
+ f->sizeupvalues = LoadInt(S);
|
|
|
+ f->sizep = LoadInt(S);
|
|
|
+ f->sizelineinfo = LoadInt(S);
|
|
|
+ f->sizelocvars = LoadInt(S);
|
|
|
+
|
|
|
+ // LLOGD("sizecode %d", f->sizecode);
|
|
|
+ // LLOGD("sizek %d", f->sizek);
|
|
|
+ // LLOGD("sizeupvalues %d", f->sizeupvalues);
|
|
|
+ // LLOGD("sizep %d", f->sizep);
|
|
|
+ // LLOGD("sizelineinfo %d", f->sizelineinfo);
|
|
|
+ // LLOGD("sizelocvars %d", f->sizelocvars);
|
|
|
+
|
|
|
+ LoadCode(S, f);
|
|
|
+ LoadConstants(S, f);
|
|
|
+ LoadUpvalues(S, f);
|
|
|
+ LoadProtos(S, f);
|
|
|
+ LoadDebug(S, f);
|
|
|
+
|
|
|
+ // for (size_t i = 0; i < f->sizelineinfo; i++)
|
|
|
+ // {
|
|
|
+ // LLOGD("lineinfo %d %p", f->lineinfo[i], &f->lineinfo[i]);
|
|
|
+ // }
|
|
|
+ // for (size_t i = 0; i < f->sizek; i++)
|
|
|
+ // {
|
|
|
+ // switch (f->k[i].tt_)
|
|
|
+ // {
|
|
|
+ // case LUA_TSHRSTR:
|
|
|
+ // case LUA_TLNGSTR:
|
|
|
+ // // LLOGD("const string %s", getstr(tsvalue(&f->k[i])));
|
|
|
+ // LLOGD("const string %s", getstr(tsvalue(&f->k[i])));
|
|
|
+ // break;
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ // for (size_t i = 0; i < f->sizelocvars; i++)
|
|
|
+ // {
|
|
|
+ // LLOGD("locval string %s", getstr(f->locvars[i].varname));
|
|
|
+ // }
|
|
|
+ LLOGD("f->upvalues %p %08X", f->upvalues, (uint32_t)f->upvalues);
|
|
|
+ for (size_t i = 0; i < f->sizeupvalues; i++)
|
|
|
+ {
|
|
|
+ // LLOGD("upval string %s", getstr(f->upvalues[i].name));
|
|
|
+ LLOGD("upval string %p", f->upvalues[i].name);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static void checkliteral (LoadState *S, const char *s, const char *msg) {
|
|
|
+ char buff[sizeof(LUA_SIGNATURE) + sizeof(LUAC_DATA)]; /* larger than both */
|
|
|
+ size_t len = strlen(s);
|
|
|
+ // buff[len] = 0;
|
|
|
+ LoadVector(S, buff, len);
|
|
|
+ // LLOGD("buff>> %02X %02X %02X %02X", buff[0], buff[1], buff[2], buff[3]);
|
|
|
+ // LLOGD("s>> %02X %02X %02X %02X", s[0], s[1], s[2], s[3]);
|
|
|
+ if (memcmp(s, buff, len) != 0)
|
|
|
+ error(S, msg);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static void fchecksize (LoadState *S, size_t size, const char *tname) {
|
|
|
+ if (LoadByte(S) != size)
|
|
|
+ error(S, luaO_pushfstring(S->L, "%s size mismatch in", tname));
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#define checksize(S,t) fchecksize(S,sizeof(t),#t)
|
|
|
+
|
|
|
+static void checkHeader (LoadState *S) {
|
|
|
+ checkliteral(S, LUF_SIGNATURE + 1, "not a"); /* 1st char already checked */
|
|
|
+ if (LoadByte(S) != LUAC_VERSION)
|
|
|
+ error(S, "version mismatch in");
|
|
|
+ if (LoadByte(S) != 1)
|
|
|
+ error(S, "format mismatch in");
|
|
|
+ checkliteral(S, LUAC_DATA, "corrupted");
|
|
|
+ checksize(S, int);
|
|
|
+ checksize(S, size_t);
|
|
|
+ checksize(S, Instruction);
|
|
|
+ checksize(S, lua_Integer);
|
|
|
+ checksize(S, lua_Number);
|
|
|
+ if (LoadInteger(S) != LUAC_INT)
|
|
|
+ error(S, "endianness mismatch in");
|
|
|
+ if (LoadNumber(S) != LUAC_NUM)
|
|
|
+ error(S, "float format mismatch in");
|
|
|
+}
|
|
|
+
|
|
|
+extern void luat_os_print_heapinfo(const char* tag);
|
|
|
+
|
|
|
+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; /* not used */
|
|
|
+ if (ls->size == 0) return NULL;
|
|
|
+ *size = ls->size;
|
|
|
+ ls->size = 0;
|
|
|
+ return ls->s;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+** load precompiled chunk
|
|
|
+*/
|
|
|
+LClosure *luat_luf_undump(lua_State *L, const char* ptr, size_t len, const char *name) {
|
|
|
+ LoadState S;
|
|
|
+ LClosure *cl;
|
|
|
+
|
|
|
+ ZIO z;
|
|
|
+ LoadS ls;
|
|
|
+ ls.s = ptr;
|
|
|
+ ls.size = len;
|
|
|
+ luaZ_init(L, &z, getS, &ls);
|
|
|
+
|
|
|
+ zgetc(&z);
|
|
|
+
|
|
|
+ S.name = name;
|
|
|
+ S.L = L;
|
|
|
+ S.Z = &z;
|
|
|
+ checkHeader(&S);
|
|
|
+ cl = luaF_newLclosure(L, LoadByte(&S));
|
|
|
+ setclLvalue(L, L->top, cl);
|
|
|
+ luaD_inctop(L);
|
|
|
+ cl->p = luaF_newproto(L);
|
|
|
+ // LLOGD("sizeupvalues %d", cl->nupvalues);
|
|
|
+ luaC_objbarrier(L, cl, cl->p); // add by wendal, refer: https://github.com/lua/lua/commit/f5eb809d3f1da13683cd02184042e67228206205
|
|
|
+ size_t s = LoadInt(&S);
|
|
|
+ LoadFunction(&S, cl->p, (TString*)s);
|
|
|
+ lua_assert(cl->nupvalues == cl->p->sizeupvalues);
|
|
|
+ luai_verifycode(L, buff, cl->p);
|
|
|
+ luaF_initupvals(L, cl);
|
|
|
+
|
|
|
+ //-----------------
|
|
|
+ // from lua_load
|
|
|
+ LClosure *f = cl;
|
|
|
+ if (f->nupvalues >= 1) { /* does it have an upvalue? */
|
|
|
+ /* get global table from registry */
|
|
|
+ Table *reg = hvalue(&G(L)->l_registry);
|
|
|
+ const TValue *gt = luaH_getint(reg, LUA_RIDX_GLOBALS);
|
|
|
+ /* set global table as 1st upvalue of 'f' (may be LUA_ENV) */
|
|
|
+ setobj(L, f->upvals[0]->v, gt);
|
|
|
+ luaC_upvalbarrier(L, f->upvals[0]);
|
|
|
+ }
|
|
|
+ //-----------------
|
|
|
+ //sizeof(LClosure) + sizeof(Proto) + sizeof(UpVal);
|
|
|
+ return cl;
|
|
|
+}
|
|
|
+
|
|
|
+#ifndef LoadF
|
|
|
+typedef struct LoadF {
|
|
|
+ int n; /* number of pre-read characters */
|
|
|
+ FILE *f; /* file being read */
|
|
|
+ char buff[BUFSIZ]; /* area for reading file */
|
|
|
+} LoadF;
|
|
|
+#endif
|
|
|
+
|
|
|
+LClosure *luat_luf_undump2(lua_State *L, ZIO *Z, const char *name) {
|
|
|
+ LoadState S;
|
|
|
+ LClosure *cl;
|
|
|
+
|
|
|
+ S.name = name;
|
|
|
+ S.L = L;
|
|
|
+ S.Z = Z;
|
|
|
+
|
|
|
+#ifdef LUAT_USE_FS_VFS
|
|
|
+ LLOGD("try mmap");
|
|
|
+ char* ptr = (char*)luat_vfs_mmap(((LoadF*)Z->data)->f);
|
|
|
+ if (ptr != NULL) {
|
|
|
+ LLOGD("found mmap %p", ptr);
|
|
|
+ ZIO z;
|
|
|
+ LoadS ls;
|
|
|
+ ls.s = ptr;
|
|
|
+ ls.size = 64*1024;
|
|
|
+ luaZ_init(L, &z, getS, &ls);
|
|
|
+ zgetc(&z);
|
|
|
+ S.Z = &z;
|
|
|
+ // LLOGD(">> %02X %02X %02X %02X", S.Z->p[0], S.Z->p[1], S.Z->p[2], S.Z->p[3]);
|
|
|
+ // LLOGD(">> %02X %02X %02X %02X", S.Z->p[4], S.Z->p[5], S.Z->p[6], S.Z->p[7]);
|
|
|
+ // LLOGD(">> %02X %02X %02X %02X", S.Z->p[8], S.Z->p[9], S.Z->p[10], S.Z->p[11]);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+ // LLOGD("LClosure %d Proto %d Upvaldesc %d LocVal %d",
|
|
|
+ // sizeof(LClosure), sizeof(Proto), sizeof(Upvaldesc), sizeof(LocVar));
|
|
|
+
|
|
|
+ checkHeader(&S);
|
|
|
+ cl = luaF_newLclosure(L, LoadByte(&S));
|
|
|
+ // 有几个对齐用的字节
|
|
|
+ size_t fd_offset = (size_t)S.Z->p;
|
|
|
+ if (fd_offset % 0x04 != 0) {
|
|
|
+ LLOGD("skip %d 0x00", fd_offset % 0x04);
|
|
|
+ for (size_t i = 0; i < (4 - (fd_offset % 0x04)); i++)
|
|
|
+ {
|
|
|
+ LoadByte(&S);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //
|
|
|
+ setclLvalue(L, L->top, cl);
|
|
|
+ luaD_inctop(L);
|
|
|
+ cl->p = luaF_newproto(L);
|
|
|
+ luaC_objbarrier(L, cl, cl->p); // add by wendal, refer: https://github.com/lua/lua/commit/f5eb809d3f1da13683cd02184042e67228206205
|
|
|
+ size_t s = LoadInt(&S);
|
|
|
+ LoadFunction(&S, cl->p, (TString*)s);
|
|
|
+ lua_assert(cl->nupvalues == cl->p->sizeupvalues);
|
|
|
+ luai_verifycode(L, buff, cl->p);
|
|
|
+
|
|
|
+ // dumpHex("& upvalues", &cl->p->upvalues[0], 8);
|
|
|
+ // dumpHex("& upvalues[0].name", &cl->p->upvalues[0].name, 8);
|
|
|
+ // dumpHex("> upvalues[0].name", cl->p->upvalues[0].name, 8);
|
|
|
+ // LLOGD("> getstr(upvalues[0].name) %p", getstr(cl->p->upvalues[0].name));
|
|
|
+ // LLOGD("> getstr(upvalues[0].name) %s", getstr(cl->p->upvalues[0].name));
|
|
|
+ // dumpHex("head", (char*)0x080E0000, 8);
|
|
|
+ //dumpHex("lineinfo", cl->p->lineinfo, 8);
|
|
|
+ //LLOGD("lineinfo %d %d", cl->p->lineinfo[0], cl->p->lineinfo[1]);
|
|
|
+
|
|
|
+ return cl;
|
|
|
+}
|