/*
** Load and dump code.
** Copyright (C) 2005-2014 Mike Pall. See Copyright Notice in luajit.h
*/

#include <errno.h>
#include <stdio.h>

#define lj_load_c
#define LUA_CORE

#include "lua.h"
#include "lauxlib.h"

#include "lj_obj.h"
#include "lj_gc.h"
#include "lj_err.h"
#include "lj_str.h"
#include "lj_func.h"
#include "lj_frame.h"
#include "lj_vm.h"
#include "lj_lex.h"
#include "lj_bcdump.h"
#include "lj_parse.h"

/* -- Load Lua source code and bytecode ----------------------------------- */

static TValue *cpparser(lua_State *L, lua_CFunction dummy, void *ud)
{
    LexState *ls = (LexState *)ud;
    GCproto *pt;
    GCfunc *fn;
    int bc;
    UNUSED(dummy);
    cframe_errfunc(L->cframe) = -1; /* Inherit error function. */
    bc = lj_lex_setup(L, ls);
    if (ls->mode && !strchr(ls->mode, bc ? 'b' : 't')) {
        setstrV(L, L->top++, lj_err_str(L, LJ_ERR_XMODE));
        lj_err_throw(L, LUA_ERRSYNTAX);
    }
    pt = bc ? lj_bcread(ls) : lj_parse(ls);
    fn = lj_func_newL_empty(L, pt, tabref(L->env));
    /* Don't combine above/below into one statement. */
    setfuncV(L, L->top++, fn);
    return NULL;
}

LUA_API int lua_loadx(lua_State *L, lua_Reader reader, void *data,
                      const char *chunkname, const char *mode)
{
    LexState ls;
    int status;
    ls.rfunc = reader;
    ls.rdata = data;
    ls.chunkarg = chunkname ? chunkname : "?";
    ls.mode = mode;
    lj_str_initbuf(&ls.sb);
    status = lj_vm_cpcall(L, NULL, &ls, cpparser);
    lj_lex_cleanup(L, &ls);
    lj_gc_check(L);
    return status;
}

LUA_API int lua_load(lua_State *L, lua_Reader reader, void *data,
                     const char *chunkname)
{
    return lua_loadx(L, reader, data, chunkname, NULL);
}

typedef struct FileReaderCtx {
    FILE *fp;
    char buf[LUAL_BUFFERSIZE];
} FileReaderCtx;

static const char *reader_file(lua_State *L, void *ud, size_t *size)
{
    FileReaderCtx *ctx = (FileReaderCtx *)ud;
    UNUSED(L);
    if (feof(ctx->fp))
        return NULL;
    *size = fread(ctx->buf, 1, sizeof(ctx->buf), ctx->fp);
    return *size > 0 ? ctx->buf : NULL;
}

LUALIB_API int luaL_loadfilex(lua_State *L, const char *filename,
                              const char *mode)
{
    FileReaderCtx ctx;
    int status;
    const char *chunkname;
    if (filename) {
        ctx.fp = fopen(filename, "rb");
        if (ctx.fp == NULL) {
            lua_pushfstring(L, "cannot open %s: %s", filename, strerror(errno));
            return LUA_ERRFILE;
        }
        chunkname = lua_pushfstring(L, "@%s", filename);
    }
    else {
        ctx.fp = stdin;
        chunkname = "=stdin";
    }
    status = lua_loadx(L, reader_file, &ctx, chunkname, mode);
    if (ferror(ctx.fp)) {
        L->top -= filename ? 2 : 1;
        lua_pushfstring(L, "cannot read %s: %s", chunkname + 1,
                        strerror(errno));
        if (filename)
            fclose(ctx.fp);
        return LUA_ERRFILE;
    }
    if (filename) {
        L->top--;
        copyTV(L, L->top - 1, L->top);
        fclose(ctx.fp);
    }
    return status;
}

LUALIB_API int luaL_loadfile(lua_State *L, const char *filename)
{
    return luaL_loadfilex(L, filename, NULL);
}

typedef struct StringReaderCtx {
    const char *str;
    size_t size;
} StringReaderCtx;

static const char *reader_string(lua_State *L, void *ud, size_t *size)
{
    StringReaderCtx *ctx = (StringReaderCtx *)ud;
    UNUSED(L);
    if (ctx->size == 0)
        return NULL;
    *size = ctx->size;
    ctx->size = 0;
    return ctx->str;
}

LUALIB_API int luaL_loadbufferx(lua_State *L, const char *buf, size_t size,
                                const char *name, const char *mode)
{
    StringReaderCtx ctx;
    ctx.str = buf;
    ctx.size = size;
    return lua_loadx(L, reader_string, &ctx, name, mode);
}

LUALIB_API int luaL_loadbuffer(lua_State *L, const char *buf, size_t size,
                               const char *name)
{
    return luaL_loadbufferx(L, buf, size, name, NULL);
}

LUALIB_API int luaL_loadstring(lua_State *L, const char *s)
{
    return luaL_loadbuffer(L, s, strlen(s), s);
}

/* -- Dump bytecode ------------------------------------------------------- */

LUA_API int lua_dump(lua_State *L, lua_Writer writer, void *data)
{
    cTValue *o = L->top - 1;
    api_check(L, L->top > L->base);
    if (tvisfunc(o) && isluafunc(funcV(o)))
        return lj_bcwrite(L, funcproto(funcV(o)), writer, data, 0);
    else
        return 1;
}
