835 lines
21 KiB
C++
835 lines
21 KiB
C++
#ifndef _WIN32
|
|
#include <unistd.h>
|
|
#ifndef EELSCRIPT_NO_LICE
|
|
#include "../swell/swell.h"
|
|
#endif
|
|
#endif
|
|
|
|
#include "../wdltypes.h"
|
|
#include "../ptrlist.h"
|
|
#include "../wdlstring.h"
|
|
#include "../assocarray.h"
|
|
#include "../queue.h"
|
|
#include "../mutex.h"
|
|
#include "../win32_utf8.h"
|
|
#include "ns-eel.h"
|
|
|
|
|
|
#ifndef EELSCRIPT_MAX_FILE_HANDLES
|
|
#define EELSCRIPT_MAX_FILE_HANDLES 512
|
|
#endif
|
|
#ifndef EELSCRIPT_FILE_HANDLE_INDEX_BASE
|
|
#define EELSCRIPT_FILE_HANDLE_INDEX_BASE 1000000
|
|
#endif
|
|
#ifndef EEL_STRING_MAXUSERSTRING_LENGTH_HINT
|
|
#define EEL_STRING_MAXUSERSTRING_LENGTH_HINT (1<<16) // 64KB per string max
|
|
#endif
|
|
#ifndef EEL_STRING_MAX_USER_STRINGS
|
|
#define EEL_STRING_MAX_USER_STRINGS 32768
|
|
#endif
|
|
#ifndef EEL_STRING_LITERAL_BASE
|
|
#define EEL_STRING_LITERAL_BASE 2000000
|
|
#endif
|
|
#ifndef EELSCRIPT_LICE_MAX_IMAGES
|
|
#define EELSCRIPT_LICE_MAX_IMAGES 1024
|
|
#endif
|
|
|
|
#ifndef EELSCRIPT_LICE_MAX_FONTS
|
|
#define EELSCRIPT_LICE_MAX_FONTS 128
|
|
#endif
|
|
|
|
#ifndef EELSCRIPT_NET_MAXCON
|
|
#define EELSCRIPT_NET_MAXCON 4096
|
|
#endif
|
|
|
|
#ifndef EELSCRIPT_LICE_CLASSNAME
|
|
#define EELSCRIPT_LICE_CLASSNAME "eelscript_gfx"
|
|
#endif
|
|
|
|
|
|
// #define EELSCRIPT_NO_NET
|
|
// #define EELSCRIPT_NO_LICE
|
|
// #define EELSCRIPT_NO_FILE
|
|
// #define EELSCRIPT_NO_FFT
|
|
// #define EELSCRIPT_NO_MDCT
|
|
// #define EELSCRIPT_NO_EVAL
|
|
|
|
class eel_string_context_state;
|
|
#ifndef EELSCRIPT_NO_NET
|
|
class eel_net_state;
|
|
#endif
|
|
#ifndef EELSCRIPT_NO_LICE
|
|
class eel_lice_state;
|
|
#endif
|
|
|
|
#ifndef EELSCRIPT_NO_PREPROC
|
|
#include "eel_pproc.h"
|
|
#endif
|
|
|
|
class eelScriptInst {
|
|
public:
|
|
|
|
static int init();
|
|
|
|
eelScriptInst();
|
|
virtual ~eelScriptInst();
|
|
|
|
NSEEL_CODEHANDLE compile_code(const char *code, const char **err);
|
|
int runcode(const char *code, int showerr, const char *showerrfn, bool canfree, bool ignoreEndOfInputChk, bool doExec);
|
|
int loadfile(const char *fn, const char *callerfn, bool allowstdin);
|
|
|
|
NSEEL_VMCTX m_vm;
|
|
|
|
WDL_PtrList<void> m_code_freelist;
|
|
|
|
#ifndef EELSCRIPT_NO_FILE
|
|
FILE *m_handles[EELSCRIPT_MAX_FILE_HANDLES];
|
|
virtual EEL_F OpenFile(const char *fn, const char *mode)
|
|
{
|
|
if (!*fn || !*mode) return 0.0;
|
|
#ifndef EELSCRIPT_NO_STDIO
|
|
if (!strcmp(fn,"stdin")) return 1;
|
|
if (!strcmp(fn,"stdout")) return 2;
|
|
if (!strcmp(fn,"stderr")) return 3;
|
|
#endif
|
|
|
|
WDL_FastString fnstr(fn);
|
|
if (!translateFilename(&fnstr,mode)) return 0.0;
|
|
|
|
int x;
|
|
for (x=0;x<EELSCRIPT_MAX_FILE_HANDLES && m_handles[x];x++);
|
|
if (x>= EELSCRIPT_MAX_FILE_HANDLES) return 0.0;
|
|
|
|
FILE *fp = fopenUTF8(fnstr.Get(),mode);
|
|
if (!fp) return 0.0;
|
|
m_handles[x]=fp;
|
|
return x + EELSCRIPT_FILE_HANDLE_INDEX_BASE;
|
|
}
|
|
virtual EEL_F CloseFile(int fp_idx)
|
|
{
|
|
fp_idx-=EELSCRIPT_FILE_HANDLE_INDEX_BASE;
|
|
if (fp_idx>=0 && fp_idx<EELSCRIPT_MAX_FILE_HANDLES && m_handles[fp_idx])
|
|
{
|
|
fclose(m_handles[fp_idx]);
|
|
m_handles[fp_idx]=0;
|
|
return 0.0;
|
|
}
|
|
return -1.0;
|
|
}
|
|
virtual FILE *GetFileFP(int fp_idx)
|
|
{
|
|
#ifndef EELSCRIPT_NO_STDIO
|
|
if (fp_idx==1) return stdin;
|
|
if (fp_idx==2) return stdout;
|
|
if (fp_idx==3) return stderr;
|
|
#endif
|
|
fp_idx-=EELSCRIPT_FILE_HANDLE_INDEX_BASE;
|
|
if (fp_idx>=0 && fp_idx<EELSCRIPT_MAX_FILE_HANDLES) return m_handles[fp_idx];
|
|
return NULL;
|
|
}
|
|
|
|
#endif
|
|
|
|
virtual bool translateFilename(WDL_FastString *fs, const char *mode) { return true; }
|
|
|
|
virtual bool GetFilenameForParameter(EEL_F idx, WDL_FastString *fs, int iswrite);
|
|
|
|
eel_string_context_state *m_string_context;
|
|
#ifndef EELSCRIPT_NO_NET
|
|
eel_net_state *m_net_state;
|
|
#endif
|
|
#ifndef EELSCRIPT_NO_LICE
|
|
eel_lice_state *m_gfx_state;
|
|
#endif
|
|
|
|
#ifndef EELSCRIPT_NO_EVAL
|
|
struct evalCacheEnt {
|
|
char *str;
|
|
NSEEL_CODEHANDLE ch;
|
|
};
|
|
int m_eval_depth;
|
|
WDL_TypedBuf<evalCacheEnt> m_eval_cache;
|
|
virtual char *evalCacheGet(const char *str, NSEEL_CODEHANDLE *ch);
|
|
virtual void evalCacheDispose(char *key, NSEEL_CODEHANDLE ch);
|
|
WDL_Queue m_defer_eval, m_atexit_eval;
|
|
void runCodeQ(WDL_Queue *q, const char *fname);
|
|
void runAtExitCode()
|
|
{
|
|
runCodeQ(&m_atexit_eval,"atexit");
|
|
m_atexit_eval.Clear(); // make sure nothing gets added in atexit(), in case the user called runAtExitCode before destroying
|
|
}
|
|
#endif
|
|
virtual bool run_deferred(); // requires eval support to be useful
|
|
virtual bool has_deferred();
|
|
|
|
|
|
WDL_StringKeyedArray<bool> m_loaded_fnlist; // imported file list (to avoid repeats)
|
|
|
|
#ifndef EELSCRIPT_NO_PREPROC
|
|
EEL2_PreProcessor m_preproc;
|
|
#endif
|
|
};
|
|
|
|
//#define EEL_STRINGS_MUTABLE_LITERALS
|
|
//#define EEL_STRING_WANT_MUTEX
|
|
|
|
|
|
#define EEL_STRING_GET_CONTEXT_POINTER(opaque) (((eelScriptInst *)opaque)->m_string_context)
|
|
#ifndef EEL_STRING_STDOUT_WRITE
|
|
#ifndef EELSCRIPT_NO_STDIO
|
|
#define EEL_STRING_STDOUT_WRITE(x,len) { fwrite(x,len,1,stdout); fflush(stdout); }
|
|
#endif
|
|
#endif
|
|
#include "eel_strings.h"
|
|
|
|
#include "eel_misc.h"
|
|
|
|
|
|
#ifndef EELSCRIPT_NO_FILE
|
|
#define EEL_FILE_OPEN(fn,mode) ((eelScriptInst*)opaque)->OpenFile(fn,mode)
|
|
#define EEL_FILE_GETFP(fp) ((eelScriptInst*)opaque)->GetFileFP(fp)
|
|
#define EEL_FILE_CLOSE(fpindex) ((eelScriptInst*)opaque)->CloseFile(fpindex)
|
|
|
|
#include "eel_files.h"
|
|
#endif
|
|
|
|
#ifndef EELSCRIPT_NO_FFT
|
|
#include "eel_fft.h"
|
|
#endif
|
|
|
|
#ifndef EELSCRIPT_NO_MDCT
|
|
#include "eel_mdct.h"
|
|
#endif
|
|
|
|
#ifndef EELSCRIPT_NO_NET
|
|
#define EEL_NET_GET_CONTEXT(opaque) (((eelScriptInst *)opaque)->m_net_state)
|
|
#include "eel_net.h"
|
|
#endif
|
|
|
|
#ifndef EELSCRIPT_NO_LICE
|
|
#ifndef EEL_LICE_WANT_STANDALONE
|
|
#define EEL_LICE_WANT_STANDALONE
|
|
#endif
|
|
#ifndef EELSCRIPT_LICE_NOUPDATE
|
|
#define EEL_LICE_WANT_STANDALONE_UPDATE // gfx_update() which runs message pump and updates screen etc
|
|
#endif
|
|
|
|
#define EEL_LICE_GET_FILENAME_FOR_STRING(idx, fs, p) (((eelScriptInst*)opaque)->GetFilenameForParameter(idx,fs,p))
|
|
#define EEL_LICE_GET_CONTEXT(opaque) ((opaque) ? (((eelScriptInst *)opaque)->m_gfx_state) : NULL)
|
|
#include "eel_lice.h"
|
|
#endif
|
|
|
|
#ifndef EELSCRIPT_NO_EVAL
|
|
#define EEL_EVAL_GET_CACHED(str, ch) ((eelScriptInst *)opaque)->evalCacheGet(str,&(ch))
|
|
#define EEL_EVAL_SET_CACHED(str, ch) ((eelScriptInst *)opaque)->evalCacheDispose(str,ch)
|
|
#define EEL_EVAL_GET_VMCTX(opaque) (((eelScriptInst *)opaque)->m_vm)
|
|
#define EEL_EVAL_SCOPE_ENTER (((eelScriptInst *)opaque)->m_eval_depth < 3 ? \
|
|
++((eelScriptInst *)opaque)->m_eval_depth : 0)
|
|
#define EEL_EVAL_SCOPE_LEAVE ((eelScriptInst *)opaque)->m_eval_depth--;
|
|
#include "eel_eval.h"
|
|
|
|
static EEL_F NSEEL_CGEN_CALL _eel_defer(void *opaque, EEL_F *s)
|
|
{
|
|
EEL_STRING_MUTEXLOCK_SCOPE
|
|
const char *str=EEL_STRING_GET_FOR_INDEX(*s,NULL);
|
|
if (str && *str && *s >= EEL_STRING_MAX_USER_STRINGS) // don't allow defer(0) etc
|
|
{
|
|
eelScriptInst *inst = (eelScriptInst *)opaque;
|
|
if (inst->m_defer_eval.Available() < EEL_STRING_MAXUSERSTRING_LENGTH_HINT)
|
|
{
|
|
inst->m_defer_eval.Add(str,strlen(str)+1);
|
|
return 1.0;
|
|
}
|
|
#ifdef EEL_STRING_DEBUGOUT
|
|
EEL_STRING_DEBUGOUT("defer(): too much defer() code already added, ignoring");
|
|
#endif
|
|
}
|
|
#ifdef EEL_STRING_DEBUGOUT
|
|
else if (!str)
|
|
{
|
|
EEL_STRING_DEBUGOUT("defer(): invalid string identifier specified %f",*s);
|
|
}
|
|
else if (*s < EEL_STRING_MAX_USER_STRINGS)
|
|
{
|
|
EEL_STRING_DEBUGOUT("defer(): user string identifier %f specified but not allowed",*s);
|
|
}
|
|
#endif
|
|
return 0.0;
|
|
}
|
|
static EEL_F NSEEL_CGEN_CALL _eel_atexit(void *opaque, EEL_F *s)
|
|
{
|
|
EEL_STRING_MUTEXLOCK_SCOPE
|
|
const char *str=EEL_STRING_GET_FOR_INDEX(*s,NULL);
|
|
if (str && *str && *s >= EEL_STRING_MAX_USER_STRINGS) // don't allow atexit(0) etc
|
|
{
|
|
eelScriptInst *inst = (eelScriptInst *)opaque;
|
|
if (inst->m_atexit_eval.Available() < EEL_STRING_MAXUSERSTRING_LENGTH_HINT)
|
|
{
|
|
inst->m_atexit_eval.Add(str,strlen(str)+1);
|
|
return 1.0;
|
|
}
|
|
#ifdef EEL_STRING_DEBUGOUT
|
|
EEL_STRING_DEBUGOUT("atexit(): too much atexit() code already added, ignoring");
|
|
#endif
|
|
}
|
|
#ifdef EEL_STRING_DEBUGOUT
|
|
else if (!str)
|
|
{
|
|
EEL_STRING_DEBUGOUT("atexit(): invalid string identifier specified %f",*s);
|
|
}
|
|
else if (*s < EEL_STRING_MAX_USER_STRINGS)
|
|
{
|
|
EEL_STRING_DEBUGOUT("atexit(): user string identifier %f specified but not allowed",*s);
|
|
}
|
|
#endif
|
|
return 0.0;
|
|
}
|
|
#endif
|
|
|
|
|
|
#define opaque ((void *)this)
|
|
|
|
eelScriptInst::eelScriptInst() : m_loaded_fnlist(false)
|
|
{
|
|
#ifndef EELSCRIPT_NO_FILE
|
|
memset(m_handles,0,sizeof(m_handles));
|
|
#endif
|
|
m_vm = NSEEL_VM_alloc();
|
|
#ifdef EEL_STRING_DEBUGOUT
|
|
if (!m_vm) EEL_STRING_DEBUGOUT("NSEEL_VM_alloc(): failed");
|
|
#endif
|
|
NSEEL_VM_SetCustomFuncThis(m_vm,this);
|
|
#ifdef NSEEL_ADDFUNC_DESTINATION
|
|
NSEEL_VM_SetFunctionTable(m_vm,NSEEL_ADDFUNC_DESTINATION);
|
|
#endif
|
|
|
|
m_string_context = new eel_string_context_state;
|
|
eel_string_initvm(m_vm);
|
|
#ifndef EELSCRIPT_NO_NET
|
|
m_net_state = new eel_net_state(EELSCRIPT_NET_MAXCON,NULL);
|
|
#endif
|
|
#ifndef EELSCRIPT_NO_LICE
|
|
m_gfx_state = new eel_lice_state(m_vm,this,EELSCRIPT_LICE_MAX_IMAGES,EELSCRIPT_LICE_MAX_FONTS);
|
|
|
|
m_gfx_state->resetVarsToStock();
|
|
#endif
|
|
#ifndef EELSCRIPT_NO_EVAL
|
|
m_eval_depth=0;
|
|
#endif
|
|
}
|
|
|
|
eelScriptInst::~eelScriptInst()
|
|
{
|
|
#ifndef EELSCRIPT_NO_EVAL
|
|
if (m_atexit_eval.GetSize()>0) runAtExitCode();
|
|
#endif
|
|
int x;
|
|
m_code_freelist.Empty((void (*)(void *))NSEEL_code_free);
|
|
#ifndef EELSCRIPT_NO_EVAL
|
|
for (x=0;x<m_eval_cache.GetSize();x++)
|
|
{
|
|
free(m_eval_cache.Get()[x].str);
|
|
NSEEL_code_free(m_eval_cache.Get()[x].ch);
|
|
}
|
|
#endif
|
|
|
|
if (m_vm) NSEEL_VM_free(m_vm);
|
|
|
|
#ifndef EELSCRIPT_NO_FILE
|
|
for (x=0;x<EELSCRIPT_MAX_FILE_HANDLES;x++)
|
|
{
|
|
if (m_handles[x]) fclose(m_handles[x]);
|
|
m_handles[x]=0;
|
|
}
|
|
#endif
|
|
delete m_string_context;
|
|
#ifndef EELSCRIPT_NO_NET
|
|
delete m_net_state;
|
|
#endif
|
|
#ifndef EELSCRIPT_NO_LICE
|
|
delete m_gfx_state;
|
|
#endif
|
|
}
|
|
|
|
bool eelScriptInst::GetFilenameForParameter(EEL_F idx, WDL_FastString *fs, int iswrite)
|
|
{
|
|
const char *fmt = EEL_STRING_GET_FOR_INDEX(idx,NULL);
|
|
if (!fmt) return false;
|
|
fs->Set(fmt);
|
|
return translateFilename(fs,iswrite?"w":"r");
|
|
}
|
|
|
|
NSEEL_CODEHANDLE eelScriptInst::compile_code(const char *code, const char **err)
|
|
{
|
|
if (!m_vm)
|
|
{
|
|
*err = "EEL VM not initialized";
|
|
return NULL;
|
|
}
|
|
|
|
#ifndef EELSCRIPT_NO_PREPROC
|
|
WDL_FastString str;
|
|
if (strstr(code,EEL2_PREPROCESS_OPEN_TOKEN))
|
|
{
|
|
const char *pperr = m_preproc.preprocess(code,&str);
|
|
if (pperr)
|
|
{
|
|
*err = pperr;
|
|
return NULL;
|
|
}
|
|
code = str.Get();
|
|
}
|
|
else
|
|
m_preproc.clear_line_info();
|
|
#endif
|
|
|
|
NSEEL_CODEHANDLE ch = NSEEL_code_compile_ex(m_vm, code, 0, NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS);
|
|
if (ch)
|
|
{
|
|
m_string_context->update_named_vars(m_vm);
|
|
m_code_freelist.Add((void*)ch);
|
|
return ch;
|
|
}
|
|
*err = NSEEL_code_getcodeerror(m_vm);
|
|
#ifndef EELSCRIPT_NO_PREPROC
|
|
if (*err) *err = m_preproc.translate_error_line(*err);
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
int eelScriptInst::runcode(const char *codeptr, int showerr, const char *showerrfn, bool canfree, bool ignoreEndOfInputChk, bool doExec)
|
|
{
|
|
if (m_vm)
|
|
{
|
|
const char *err = NULL;
|
|
NSEEL_CODEHANDLE code = NULL;
|
|
#ifndef EELSCRIPT_NO_PREPROC
|
|
WDL_FastString str;
|
|
if (strstr(codeptr,EEL2_PREPROCESS_OPEN_TOKEN))
|
|
{
|
|
err = m_preproc.preprocess(codeptr,&str);
|
|
if (err) goto on_preproc_error;
|
|
codeptr = str.Get();
|
|
}
|
|
else
|
|
m_preproc.clear_line_info();
|
|
#endif
|
|
|
|
code = NSEEL_code_compile_ex(m_vm,codeptr,0,canfree ? 0 : NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS);
|
|
if (code) m_string_context->update_named_vars(m_vm);
|
|
|
|
if (!code && (err=NSEEL_code_getcodeerror(m_vm)))
|
|
{
|
|
if (!ignoreEndOfInputChk && (NSEEL_code_geterror_flag(m_vm)&1)) return 1;
|
|
#ifndef EELSCRIPT_NO_PREPROC
|
|
err = m_preproc.translate_error_line(err);
|
|
on_preproc_error:
|
|
#endif
|
|
if (showerr)
|
|
{
|
|
#ifdef EEL_STRING_DEBUGOUT
|
|
if (showerr==2)
|
|
{
|
|
EEL_STRING_DEBUGOUT("Warning: %s:%s",WDL_get_filepart(showerrfn),err);
|
|
}
|
|
else
|
|
{
|
|
EEL_STRING_DEBUGOUT("%s:%s",WDL_get_filepart(showerrfn),err);
|
|
}
|
|
#endif
|
|
}
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
if (code)
|
|
{
|
|
#ifdef EELSCRIPT_DO_DISASSEMBLE
|
|
codeHandleType *p = (codeHandleType*)code;
|
|
|
|
char buf[512];
|
|
buf[0]=0;
|
|
#ifdef _WIN32
|
|
GetTempPath(sizeof(buf)-64,buf);
|
|
lstrcatn(buf,"jsfx-out",sizeof(buf));
|
|
#else
|
|
lstrcpyn_safe(buf,"/tmp/jsfx-out",sizeof(buf));
|
|
#endif
|
|
FILE *fp = fopenUTF8(buf,"wb");
|
|
if (fp)
|
|
{
|
|
fwrite(p->code,1,p->code_size,fp);
|
|
fclose(fp);
|
|
char buf2[2048];
|
|
#ifdef _WIN32
|
|
snprintf(buf2,sizeof(buf2),"disasm \"%s\"",buf);
|
|
#else
|
|
#ifdef __aarch64__
|
|
snprintf(buf2,sizeof(buf2), "objdump -D -b binary -maarch64 \"%s\"",buf);
|
|
#elif defined(__arm__)
|
|
snprintf(buf2,sizeof(buf2), "objdump -D -b binary -m arm \"%s\"",buf);
|
|
#elif defined(__LP64__)
|
|
#ifdef __APPLE__
|
|
snprintf(buf2,sizeof(buf2),"distorm3 --b64 \"%s\"",buf);
|
|
#else
|
|
snprintf(buf2,sizeof(buf2),"objdump -D -b binary -m i386:x86-64 \"%s\"",buf);
|
|
#endif
|
|
#else
|
|
snprintf(buf2,sizeof(buf2),"distorm3 --b32 \"%s\"",buf);
|
|
#endif
|
|
#endif
|
|
system(buf2);
|
|
}
|
|
#endif
|
|
|
|
if (doExec) NSEEL_code_execute(code);
|
|
if (canfree) NSEEL_code_free(code);
|
|
else m_code_freelist.Add((void*)code);
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
FILE *eelscript_resolvePath(WDL_FastString &usefn, const char *fn, const char *callerfn)
|
|
{
|
|
// resolve path relative to current
|
|
int x;
|
|
bool had_abs=false;
|
|
for (x=0;x<2; x ++)
|
|
{
|
|
#ifdef _WIN32
|
|
if (!x && ((fn[0] == '\\' && fn[1] == '\\') || (fn[0] && fn[1] == ':')))
|
|
#else
|
|
if (!x && fn[0] == '/')
|
|
#endif
|
|
{
|
|
usefn.Set(fn);
|
|
had_abs=true;
|
|
}
|
|
else
|
|
{
|
|
const char *fnu = fn;
|
|
if (x)
|
|
{
|
|
while (*fnu) fnu++;
|
|
while (fnu >= fn && *fnu != '\\' && *fnu != '/') fnu--;
|
|
if (fnu < fn) break;
|
|
fnu++;
|
|
}
|
|
|
|
usefn.Set(callerfn);
|
|
int l=usefn.GetLength();
|
|
while (l > 0 && usefn.Get()[l-1] != '\\' && usefn.Get()[l-1] != '/') l--;
|
|
if (l > 0)
|
|
{
|
|
usefn.SetLen(l);
|
|
usefn.Append(fnu);
|
|
}
|
|
else
|
|
{
|
|
usefn.Set(fnu);
|
|
}
|
|
int last_slash_pos=-1;
|
|
for (l = 0; l < usefn.GetLength(); l ++)
|
|
{
|
|
if (usefn.Get()[l] == '/' || usefn.Get()[l] == '\\')
|
|
{
|
|
if (usefn.Get()[l+1] == '.' && usefn.Get()[l+2] == '.' &&
|
|
(usefn.Get()[l+3] == '/' || usefn.Get()[l+3] == '\\'))
|
|
{
|
|
if (last_slash_pos >= 0)
|
|
usefn.DeleteSub(last_slash_pos, l+3-last_slash_pos);
|
|
else
|
|
usefn.DeleteSub(0,l+3+1);
|
|
}
|
|
else
|
|
{
|
|
last_slash_pos=l;
|
|
}
|
|
}
|
|
// take currentfn, remove filename part, add fnu
|
|
}
|
|
}
|
|
|
|
FILE *fp = fopenUTF8(usefn.Get(),"r");
|
|
if (fp) return fp;
|
|
}
|
|
if (had_abs) usefn.Set(fn);
|
|
return NULL;
|
|
}
|
|
|
|
int eelScriptInst::loadfile(const char *fn, const char *callerfn, bool allowstdin)
|
|
{
|
|
WDL_FastString usefn;
|
|
FILE *fp = NULL;
|
|
if (!strcmp(fn,"-"))
|
|
{
|
|
if (callerfn)
|
|
{
|
|
#ifdef EEL_STRING_DEBUGOUT
|
|
EEL_STRING_DEBUGOUT("@import: can't import \"-\" (stdin)");
|
|
#endif
|
|
return -1;
|
|
}
|
|
if (allowstdin)
|
|
{
|
|
fp = stdin;
|
|
fn = "(stdin)";
|
|
}
|
|
}
|
|
else if (!callerfn)
|
|
{
|
|
fp = fopenUTF8(fn,"r");
|
|
if (fp) m_loaded_fnlist.Insert(fn,true);
|
|
}
|
|
else
|
|
{
|
|
fp = eelscript_resolvePath(usefn,fn,callerfn);
|
|
if (fp)
|
|
{
|
|
if (m_loaded_fnlist.Get(usefn.Get()))
|
|
{
|
|
fclose(fp);
|
|
return 0;
|
|
}
|
|
m_loaded_fnlist.Insert(usefn.Get(),true);
|
|
fn = usefn.Get();
|
|
}
|
|
}
|
|
|
|
if (!fp)
|
|
{
|
|
#ifdef EEL_STRING_DEBUGOUT
|
|
if (callerfn)
|
|
EEL_STRING_DEBUGOUT("Warning: @import could not open '%s'",fn);
|
|
else
|
|
EEL_STRING_DEBUGOUT("Error opening %s",fn);
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
#ifndef EELSCRIPT_NO_PREPROC
|
|
WDL_FastString incpath(fn);
|
|
incpath.remove_filepart();
|
|
m_preproc.m_include_paths.Add(incpath.Get());
|
|
#endif
|
|
|
|
WDL_FastString code;
|
|
char line[4096];
|
|
for (;;)
|
|
{
|
|
line[0]=0;
|
|
fgets(line,sizeof(line),fp);
|
|
if (!line[0]) break;
|
|
if (!strnicmp(line,"@import",7) && isspace((unsigned char)line[7]))
|
|
{
|
|
char *p=line+7;
|
|
while (isspace((unsigned char)*p)) p++;
|
|
|
|
char *ep=p;
|
|
while (*ep) ep++;
|
|
while (ep>p && isspace((unsigned char)ep[-1])) ep--;
|
|
*ep=0;
|
|
|
|
if (*p) loadfile(p,fn,false);
|
|
}
|
|
else
|
|
{
|
|
code.Append(line);
|
|
}
|
|
}
|
|
if (fp != stdin) fclose(fp);
|
|
|
|
int rv = runcode(code.Get(),callerfn ? 2 : 1, fn,false,true,!callerfn);
|
|
|
|
#ifndef EELSCRIPT_NO_PREPROC
|
|
m_preproc.m_include_paths.Delete(m_preproc.m_include_paths.GetSize()-1);
|
|
#endif
|
|
|
|
return rv;
|
|
}
|
|
|
|
char *eelScriptInst::evalCacheGet(const char *str, NSEEL_CODEHANDLE *ch)
|
|
{
|
|
// should mutex protect if multiple threads access this eelScriptInst context
|
|
int x=m_eval_cache.GetSize();
|
|
while (--x >= 0)
|
|
{
|
|
char *ret;
|
|
if (!strcmp(ret=m_eval_cache.Get()[x].str, str))
|
|
{
|
|
*ch = m_eval_cache.Get()[x].ch;
|
|
m_eval_cache.Delete(x);
|
|
return ret;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void eelScriptInst::evalCacheDispose(char *key, NSEEL_CODEHANDLE ch)
|
|
{
|
|
// should mutex protect if multiple threads access this eelScriptInst context
|
|
evalCacheEnt ecc;
|
|
ecc.str= key;
|
|
ecc.ch = ch;
|
|
if (m_eval_cache.GetSize() > 1024)
|
|
{
|
|
NSEEL_code_free(m_eval_cache.Get()->ch);
|
|
free(m_eval_cache.Get()->str);
|
|
m_eval_cache.Delete(0);
|
|
}
|
|
m_eval_cache.Add(ecc);
|
|
}
|
|
|
|
int eelScriptInst::init()
|
|
{
|
|
EEL_string_register();
|
|
#ifndef EELSCRIPT_NO_FILE
|
|
EEL_file_register();
|
|
#endif
|
|
#ifndef EELSCRIPT_NO_FFT
|
|
EEL_fft_register();
|
|
#endif
|
|
#ifndef EELSCRIPT_NO_MDCT
|
|
EEL_mdct_register();
|
|
#endif
|
|
EEL_misc_register();
|
|
#ifndef EELSCRIPT_NO_EVAL
|
|
EEL_eval_register();
|
|
NSEEL_addfunc_retval("defer",1,NSEEL_PProc_THIS,&_eel_defer);
|
|
NSEEL_addfunc_retval("runloop", 1, NSEEL_PProc_THIS, &_eel_defer);
|
|
NSEEL_addfunc_retval("atexit",1,NSEEL_PProc_THIS,&_eel_atexit);
|
|
#endif
|
|
#ifndef EELSCRIPT_NO_NET
|
|
EEL_tcp_register();
|
|
#endif
|
|
#ifndef EELSCRIPT_NO_LICE
|
|
eel_lice_register();
|
|
#ifdef _WIN32
|
|
eel_lice_register_standalone(GetModuleHandle(NULL),EELSCRIPT_LICE_CLASSNAME,NULL,NULL);
|
|
#else
|
|
eel_lice_register_standalone(NULL,EELSCRIPT_LICE_CLASSNAME,NULL,NULL);
|
|
#endif
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
bool eelScriptInst::has_deferred()
|
|
{
|
|
#ifndef EELSCRIPT_NO_EVAL
|
|
return m_defer_eval.Available() && m_vm;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
#ifndef EELSCRIPT_NO_EVAL
|
|
void eelScriptInst::runCodeQ(WDL_Queue *q, const char *callername)
|
|
{
|
|
const int endptr = q->Available();
|
|
int offs = 0;
|
|
while (offs < endptr)
|
|
{
|
|
if (q->Available() < endptr) break; // should never happen, but safety first!
|
|
|
|
const char *ptr = (const char *)q->Get() + offs;
|
|
offs += strlen(ptr)+1;
|
|
|
|
NSEEL_CODEHANDLE ch=NULL;
|
|
char *sv=evalCacheGet(ptr,&ch);
|
|
|
|
if (!sv) sv=strdup(ptr);
|
|
if (!ch) ch=NSEEL_code_compile(m_vm,sv,0);
|
|
if (!ch)
|
|
{
|
|
free(sv);
|
|
#ifdef EEL_STRING_DEBUGOUT
|
|
const char *err = NSEEL_code_getcodeerror(m_vm);
|
|
if (err) EEL_STRING_DEBUGOUT("%s: error in code: %s",callername,err);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
NSEEL_code_execute(ch);
|
|
evalCacheDispose(sv,ch);
|
|
}
|
|
}
|
|
q->Advance(endptr);
|
|
}
|
|
#endif
|
|
|
|
bool eelScriptInst::run_deferred()
|
|
{
|
|
#ifndef EELSCRIPT_NO_EVAL
|
|
if (!m_defer_eval.Available()||!m_vm) return false;
|
|
|
|
runCodeQ(&m_defer_eval,"defer");
|
|
m_defer_eval.Compact();
|
|
return m_defer_eval.Available()>0;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
#ifdef EEL_WANT_DOCUMENTATION
|
|
#include "ns-eel-func-ref.h"
|
|
|
|
void EELScript_GenerateFunctionList(WDL_PtrList<const char> *fs)
|
|
{
|
|
const char *p = nseel_builtin_function_reference;
|
|
while (*p) { fs->Add(p); p += strlen(p) + 1; }
|
|
p = eel_strings_function_reference;
|
|
while (*p) { fs->Add(p); p += strlen(p) + 1; }
|
|
p = eel_misc_function_reference;
|
|
while (*p) { fs->Add(p); p += strlen(p) + 1; }
|
|
#ifndef EELSCRIPT_NO_EVAL
|
|
fs->Add("atexit\t\"code\"\t"
|
|
#ifndef EELSCRIPT_HELP_NO_DEFER_DESC
|
|
"Adds code to be executed when the script finishes."
|
|
#endif
|
|
);
|
|
fs->Add("defer\t\"code\"\t"
|
|
#ifndef EELSCRIPT_HELP_NO_DEFER_DESC
|
|
"Adds code which will be executed some small amount of time after the current code finishes. Identical to runloop()"
|
|
#endif
|
|
);
|
|
fs->Add("runloop\t\"code\"\t"
|
|
#ifndef EELSCRIPT_HELP_NO_DEFER_DESC
|
|
"Adds code which will be executed some small amount of time after the current code finishes. Identical to defer()"
|
|
#endif
|
|
);
|
|
|
|
p = eel_eval_function_reference;
|
|
while (*p) { fs->Add(p); p += strlen(p) + 1; }
|
|
#endif
|
|
#ifndef EELSCRIPT_NO_NET
|
|
p = eel_net_function_reference;
|
|
while (*p) { fs->Add(p); p += strlen(p) + 1; }
|
|
#endif
|
|
#ifndef EELSCRIPT_NO_FFT
|
|
p = eel_fft_function_reference;
|
|
while (*p) { fs->Add(p); p += strlen(p) + 1; }
|
|
#endif
|
|
#ifndef EELSCRIPT_NO_FILE
|
|
p = eel_file_function_reference;
|
|
while (*p) { fs->Add(p); p += strlen(p) + 1; }
|
|
#endif
|
|
#ifndef EELSCRIPT_NO_MDCT
|
|
p = eel_mdct_function_reference;
|
|
while (*p) { fs->Add(p); p += strlen(p) + 1; }
|
|
#endif
|
|
#ifndef EELSCRIPT_NO_LICE
|
|
p = eel_lice_function_reference;
|
|
while (*p) { fs->Add(p); p += strlen(p) + 1; }
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
#undef opaque
|