Version 0.22 : First part of ardi's changes:

sprintf -> sprintf_s
                       extra tokens parsed
                       array memory leak fixed
                   Fixed memory leak in evaluateComplex
                   Fixed memory leak in FOR loops
                   Fixed memory leak for unary minus
master
pur3mail 14 years ago
parent 19c8b52b47
commit edc1c42c2e

@ -27,10 +27,11 @@
* This is a simple program showing how to use TinyJS
*/
#include <assert.h>
#include <stdio.h>
#include "TinyJS.h"
#include "TinyJS_Functions.h"
#include <assert.h>
#include <stdio.h>
//const char *code = "var a = 5; if (a==5) a=4; else a=3;";
//const char *code = "{ var a = 4; var b = 1; while (a>0) { b = b * 2; a = a - 1; } var c = 5; }";
@ -49,29 +50,35 @@ void js_dump(CScriptVar *v, void *userdata) {
int main(int argc, char **argv)
{
CTinyJS js;
CTinyJS *js = new CTinyJS();
/* add the functions from TinyJS_Functions.cpp */
registerFunctions(&js);
registerFunctions(js);
/* Add a native function */
js.addNative("function print(text)", &js_print, 0);
js.addNative("function dump()", &js_dump, &js);
js->addNative("function print(text)", &js_print, 0);
js->addNative("function dump()", &js_dump, js);
/* Execute out bit of code - we could call 'evaluate' here if
we wanted something returned */
try {
js.execute("var lets_quit = 0; function quit() { lets_quit = 1; }");
js.execute("print(\"Interactive mode... Type quit(); to exit, or print(...); to print something, or dump() to dump the symbol table!\");");
js->execute("var lets_quit = 0; function quit() { lets_quit = 1; }");
js->execute("print(\"Interactive mode... Type quit(); to exit, or print(...); to print something, or dump() to dump the symbol table!\");");
} catch (CScriptException *e) {
printf("ERROR: %s\n", e->text.c_str());
}
while (js.evaluate("lets_quit") == "0") {
while (js->evaluate("lets_quit") == "0") {
char buffer[2048];
fgets ( buffer, sizeof(buffer), stdin );
try {
js.execute(buffer);
js->execute(buffer);
} catch (CScriptException *e) {
printf("ERROR: %s\n", e->text.c_str());
}
}
delete js;
#ifdef _WIN32
#ifdef _DEBUG
_CrtDumpMemoryLeaks();
#endif
#endif
return 0;
}

@ -70,12 +70,23 @@
String.length() no more - now String.length
Added extra constructors to reduce confusion
Fixed checks against undefined
Version 0.22 : First part of ardi's changes:
sprintf -> sprintf_s
extra tokens parsed
array memory leak fixed
Fixed memory leak in evaluateComplex
Fixed memory leak in FOR loops
Fixed memory leak for unary minus
NOTE: This doesn't support constructors for objects
Recursive loops of data such as a.foo = a; fail to be garbage collected
'length' cannot be set
There is no ternary operator implemented yet
The postfix increment operator returns the current value, not the previous as it should.
TODO:
Utility va-args style function in TinyJS for executing a function directly
*/
#include "TinyJS.h"
@ -96,14 +107,24 @@
using namespace std;
#ifdef _WIN32
#ifdef _DEBUG
#ifndef DBG_NEW
#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
#define new DBG_NEW
#endif
#endif
#endif
#ifdef __GNUC__
#define vsprintf_s vsnprintf
#define sprintf_s snprintf
#define _strdup strdup
#endif
// ----------------------------------------------------------------------------------- Memory Debug
//#define DEBUG_MEMORY 1
#define DEBUG_MEMORY 0
#if DEBUG_MEMORY
@ -148,7 +169,6 @@ void show_allocated() {
allocatedVars.clear();
allocatedLinks.clear();
}
#endif
// ----------------------------------------------------------------------------------- Utils
@ -234,7 +254,7 @@ CScriptException::CScriptException(const string &exceptionText) {
// ----------------------------------------------------------------------------------- CSCRIPTLEX
CScriptLex::CScriptLex(const string &input) {
data = strdup(input.c_str());
data = _strdup(input.c_str());
dataOwned = true;
dataStart = 0;
dataEnd = strlen(data);
@ -280,7 +300,7 @@ void CScriptLex::match(int expected_tk) {
string CScriptLex::getTokenStr(int token) {
if (token>32 && token<128) {
char buf[4] = "' '";
buf[1] = token;
buf[1] = (char)token;
return buf;
}
switch (token) {
@ -294,18 +314,28 @@ string CScriptLex::getTokenStr(int token) {
case LEX_NEQUAL : return "!=";
case LEX_NTYPEEQUAL : return "!==";
case LEX_LEQUAL : return "<=";
case LEX_LSHIFT : return "<<";
case LEX_LSHIFTEQUAL : return "<<=";
case LEX_GEQUAL : return ">=";
case LEX_RSHIFT : return ">>";
case LEX_RSHIFTEQUAL : return ">>=";
case LEX_PLUSEQUAL : return "+=";
case LEX_MINUSEQUAL : return "-=";
case LEX_PLUSPLUS : return "++";
case LEX_MINUSMINUS : return "--";
case LEX_ANDEQUAL : return "&=";
case LEX_ANDAND : return "&&";
case LEX_OREQUAL : return "|=";
case LEX_OROR : return "||";
case LEX_XOREQUAL : return "^=";
// reserved words
case LEX_R_IF : return "if";
case LEX_R_ELSE : return "else";
case LEX_R_DO : return "do";
case LEX_R_WHILE : return "while";
case LEX_R_FOR : return "for";
case LEX_R_BREAK : return "break";
case LEX_R_CONTINUE : return "continue";
case LEX_R_FUNCTION : return "function";
case LEX_R_RETURN : return "return";
case LEX_R_VAR : return "var";
@ -360,8 +390,11 @@ void CScriptLex::getNextToken() {
tk = LEX_ID;
if (tkStr=="if") tk = LEX_R_IF;
else if (tkStr=="else") tk = LEX_R_ELSE;
else if (tkStr=="do") tk = LEX_R_DO;
else if (tkStr=="while") tk = LEX_R_WHILE;
else if (tkStr=="for") tk = LEX_R_FOR;
else if (tkStr=="break") tk = LEX_R_BREAK;
else if (tkStr=="continue") tk = LEX_R_CONTINUE;
else if (tkStr=="function") tk = LEX_R_FUNCTION;
else if (tkStr=="return") tk = LEX_R_RETURN;
else if (tkStr=="var") tk = LEX_R_VAR;
@ -459,9 +492,23 @@ void CScriptLex::getNextToken() {
} else if (tk=='<' && currCh=='=') {
tk = LEX_LEQUAL;
getNextCh();
} else if (tk=='<' && currCh=='<') {
tk = LEX_LSHIFT;
getNextCh();
if (currCh=='=') { // <<=
tk = LEX_LSHIFTEQUAL;
getNextCh();
}
} else if (tk=='>' && currCh=='=') {
tk = LEX_GEQUAL;
getNextCh();
} else if (tk=='>' && currCh=='>') {
tk = LEX_RSHIFT;
getNextCh();
if (currCh=='=') { // <<=
tk = LEX_RSHIFTEQUAL;
getNextCh();
}
} else if (tk=='+' && currCh=='=') {
tk = LEX_PLUSEQUAL;
getNextCh();
@ -474,12 +521,21 @@ void CScriptLex::getNextToken() {
} else if (tk=='-' && currCh=='-') {
tk = LEX_MINUSMINUS;
getNextCh();
} else if (tk=='&' && currCh=='=') {
tk = LEX_ANDEQUAL;
getNextCh();
} else if (tk=='&' && currCh=='&') {
tk = LEX_ANDAND;
getNextCh();
} else if (tk=='|' && currCh=='=') {
tk = LEX_OREQUAL;
getNextCh();
} else if (tk=='|' && currCh=='|') {
tk = LEX_OROR;
getNextCh();
} else if (tk=='^' && currCh=='=') {
tk = LEX_XOREQUAL;
getNextCh();
}
}
/* This isn't quite right yet */
@ -512,6 +568,7 @@ CScriptLex *CScriptLex::getSubLex(int lastPosition) {
}
string CScriptLex::getPosition(int pos) {
if (pos<0) pos=tokenLastEnd;
int line = 1,col = 1;
for (int i=0;i<pos;i++) {
char ch;
@ -543,13 +600,6 @@ CScriptVarLink::CScriptVarLink(CScriptVar *var, const std::string &name) {
this->owned = false;
}
CScriptVarLink::~CScriptVarLink() {
#if DEBUG_MEMORY
mark_deallocated(this);
#endif
var->unref();
}
CScriptVarLink::CScriptVarLink(const CScriptVarLink &link) {
// Copy constructor
#if DEBUG_MEMORY
@ -562,6 +612,13 @@ CScriptVarLink::CScriptVarLink(const CScriptVarLink &link) {
this->owned = false;
}
CScriptVarLink::~CScriptVarLink() {
#if DEBUG_MEMORY
mark_deallocated(this);
#endif
var->unref();
}
void CScriptVarLink::replaceWith(CScriptVar *newVar) {
CScriptVar *oldVar = var;
var = newVar->ref();
@ -753,7 +810,7 @@ void CScriptVar::removeAllChildren() {
CScriptVar *CScriptVar::getArrayIndex(int idx) {
char sIdx[64];
sprintf(sIdx, "%d", idx);
sprintf_s(sIdx, sizeof(sIdx), "%d", idx);
CScriptVarLink *link = findChild(sIdx);
if (link) return link->var;
else return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NULL); // undefined
@ -761,7 +818,7 @@ CScriptVar *CScriptVar::getArrayIndex(int idx) {
void CScriptVar::setArrayIndex(int idx, CScriptVar *value) {
char sIdx[64];
sprintf(sIdx, "%d", idx);
sprintf_s(sIdx, sizeof(sIdx), "%d", idx);
CScriptVarLink *link = findChild(sIdx);
if (link) {
@ -829,14 +886,14 @@ const string &CScriptVar::getString() {
void CScriptVar::setInt(int val) {
char buf[256];
sprintf(buf, "%d", val);
sprintf_s(buf, sizeof(buf), "%d", val);
flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_INTEGER;
data = buf;
}
void CScriptVar::setDouble(double val) {
char buf[256];
sprintf(buf, "%lf", val);
sprintf_s(buf, sizeof(buf), "%lf", val);
flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_DOUBLE;
data = buf;
}
@ -1107,6 +1164,7 @@ CTinyJS::~CTinyJS() {
ASSERT(!l);
scopes.clear();
stringClass->unref();
arrayClass->unref();
objectClass->unref();
root->unref();
@ -1130,7 +1188,7 @@ void CTinyJS::execute(const string &code) {
while (l->tk) statement(execute);
} catch (CScriptException *e) {
ostringstream msg;
msg << "Error " << e->text << " at " << l->getPosition(l->tokenLastEnd);
msg << "Error " << e->text << " at " << l->getPosition();
delete l;
l = oldLex;
throw new CScriptException(msg.str());
@ -1153,7 +1211,7 @@ CScriptVarLink CTinyJS::evaluateComplex(const string &code) {
v = base(execute);
} catch (CScriptException *e) {
ostringstream msg;
msg << "Error " << e->text << " at " << l->getPosition(l->tokenLastEnd);
msg << "Error " << e->text << " at " << l->getPosition();
delete l;
l = oldLex;
throw new CScriptException(msg.str());
@ -1162,7 +1220,11 @@ CScriptVarLink CTinyJS::evaluateComplex(const string &code) {
l = oldLex;
scopes = oldScopes;
if (v) return *v;
if (v) {
CScriptVarLink r = *v;
CLEAN(v);
return r;
}
// return undefined...
return CScriptVarLink(new CScriptVar());
}
@ -1253,6 +1315,7 @@ CScriptVarLink *CTinyJS::factor(bool &execute) {
}
if (l->tk==LEX_ID) {
CScriptVarLink *a = execute ? findInScopes(l->tkStr) : new CScriptVarLink(new CScriptVar());
//printf("0x%08X for %s at %s\n", (unsigned int)a, l->tkStr.c_str(), l->getPosition().c_str());
/* The parent if we're executing a method call */
CScriptVar *parent = 0;
@ -1425,7 +1488,7 @@ CScriptVarLink *CTinyJS::factor(bool &execute) {
while (l->tk != ']') {
if (execute) {
char idx_str[16]; // big enough for 2^32
sprintf(idx_str,"%d",idx);
sprintf_s(idx_str, sizeof(idx_str), "%d",idx);
CScriptVarLink *a = base(execute);
contents->addChild(idx_str, a->var);
@ -1514,8 +1577,8 @@ CScriptVarLink *CTinyJS::expression(bool &execute) {
}
CScriptVarLink *a = term(execute);
if (negate) {
CScriptVar *zero = new CScriptVar(0);
CScriptVar *res = zero->mathsOp(a->var, '-');
CScriptVar zero(0);
CScriptVar *res = zero.mathsOp(a->var, '-');
CREATE_LINK(a, res);
}
@ -1633,13 +1696,13 @@ CScriptVarLink *CTinyJS::base(bool &execute) {
}
void CTinyJS::block(bool &execute) {
// TODO: fast skip of blocks
l->match('{');
if (execute) {
while (l->tk && l->tk!='}')
statement(execute);
l->match('}');
} else {
// fast skip of blocks
int brackets = 1;
while (l->tk && brackets) {
if (l->tk == '{') brackets++;
@ -1740,7 +1803,7 @@ void CTinyJS::statement(bool &execute) {
if (loopCount<=0) {
root->trace();
TRACE("WHILE Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str());
TRACE("WHILE Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition().c_str());
throw new CScriptException("LOOP_ERROR");
}
} else if (l->tk==LEX_R_FOR) {
@ -1756,7 +1819,7 @@ void CTinyJS::statement(bool &execute) {
CScriptLex *forCond = l->getSubLex(forCondStart);
l->match(';');
int forIterStart = l->tokenStart;
base(noexecute); // iterator
CLEAN(base(noexecute)); // iterator
CScriptLex *forIter = l->getSubLex(forIterStart);
l->match(')');
int forBodyStart = l->tokenStart;
@ -1766,7 +1829,7 @@ void CTinyJS::statement(bool &execute) {
if (loopCond) {
forIter->reset();
l = forIter;
base(execute);
CLEAN(base(execute));
}
int loopCount = TINYJS_LOOP_MAX_ITERATIONS;
while (execute && loopCond && loopCount-->0) {
@ -1783,7 +1846,7 @@ void CTinyJS::statement(bool &execute) {
if (execute && loopCond) {
forIter->reset();
l = forIter;
base(execute);
CLEAN(base(execute));
}
}
l = oldLex;
@ -1792,7 +1855,7 @@ void CTinyJS::statement(bool &execute) {
delete forBody;
if (loopCount<=0) {
root->trace();
TRACE("FOR Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition(l->tokenLastEnd).c_str());
TRACE("FOR Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition().c_str());
throw new CScriptException("LOOP_ERROR");
}
} else if (l->tk==LEX_R_RETURN) {

@ -26,6 +26,13 @@
#ifndef TINYJS_H
#define TINYJS_H
#ifdef _WIN32
#ifdef _DEBUG
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#endif
#endif
#include <string>
#include <vector>
@ -47,18 +54,29 @@ enum LEX_TYPES {
LEX_NEQUAL,
LEX_NTYPEEQUAL,
LEX_LEQUAL,
LEX_LSHIFT,
LEX_LSHIFTEQUAL,
LEX_GEQUAL,
LEX_RSHIFT,
LEX_RSHIFTEQUAL,
LEX_PLUSEQUAL,
LEX_MINUSEQUAL,
LEX_PLUSPLUS,
LEX_MINUSMINUS,
LEX_ANDEQUAL,
LEX_ANDAND,
LEX_OREQUAL,
LEX_OROR,
LEX_XOREQUAL,
// reserved words
#define LEX_R_LIST_START LEX_R_IF
LEX_R_IF,
LEX_R_ELSE,
LEX_R_DO,
LEX_R_WHILE,
LEX_R_FOR,
LEX_R_BREAK,
LEX_R_CONTINUE,
LEX_R_FUNCTION,
LEX_R_RETURN,
LEX_R_VAR,
@ -67,6 +85,8 @@ enum LEX_TYPES {
LEX_R_NULL,
LEX_R_UNDEFINED,
LEX_R_NEW,
LEX_R_LIST_END /* always the last entry */
};
enum SCRIPTVAR_FLAGS {
@ -122,13 +142,13 @@ public:
std::string tkStr; ///< Data contained in the token we have here
void match(int expected_tk); ///< Lexical match wotsit
std::string getTokenStr(int token); ///< Get the string representation of the given token
static std::string getTokenStr(int token); ///< Get the string representation of the given token
void reset(); ///< Reset this lex so we can start again
std::string getSubString(int pos); ///< Return a sub-string from the given position up until right now
CScriptLex *getSubLex(int lastPosition); ///< Return a sub-lexer from the given position up until right now
std::string getPosition(int pos); ///< Return a string representing the position in lines and columns of the character pos given
std::string getPosition(int pos=-1); ///< Return a string representing the position in lines and columns of the character pos given
protected:
/* When we go into a loop, we use getSubLex to get a lexer for just the sub-part of the

@ -27,13 +27,14 @@
* This is a program to run all the tests in the tests folder...
*/
#include "TinyJS.h"
#include "TinyJS_Functions.h"
#include <assert.h>
#include <sys/stat.h>
#include <string>
#include <sstream>
#include <stdio.h>
#include "TinyJS.h"
#include "TinyJS_Functions.h"
//#define INSANE_MEMORY_DEBUG
@ -267,6 +268,11 @@ int main(int argc, char **argv)
printf("Done. %d tests, %d pass, %d fail\n", count, passed, count-passed);
#ifdef INSANE_MEMORY_DEBUG
memtracing_kill();
#endif
#ifdef _WIN32
#ifdef _DEBUG
_CrtDumpMemoryLeaks();
#endif
#endif
return 0;
}

Loading…
Cancel
Save