Version 0.17 : Now we don't deepCopy the parent object of the class

Added JSON.stringify and eval()
                   Nicer JSON indenting
                   Fixed function output in JSON
                   Added evaluateComplex
                   Fixed some reentrancy issues with evaluate/execute
master
pur3mail 14 years ago
parent e04177f08e
commit c32659c207

@ -56,12 +56,16 @@
Changed Callbacks to include user data pointer
Added some support for objects
Added more Java-esque builtin functions
Version 0.17 : Now we don't deepCopy the parent object of the class
Added JSON.stringify and eval()
Nicer JSON indenting
Fixed function output in JSON
Added evaluateComplex
Fixed some reentrancy issues with evaluate/execute
NOTE: This doesn't support constructors for objects
Recursive loops of data such as a.foo = a; fail to be garbage collected
*/
#include "TinyJS.h"
@ -252,7 +256,7 @@ void CScriptLex::match(int expected_tk) {
if (tk!=expected_tk) {
ostringstream errorString;
errorString << "Got " << getTokenStr(tk) << " expected " << getTokenStr(expected_tk)
<< " at " << getPosition(tokenStart) << "in '" << data << "'";
<< " at " << getPosition(tokenStart) << " in '" << data << "'";
throw new CScriptException(errorString.str());
}
getNextToken();
@ -531,6 +535,18 @@ CScriptVarLink::~CScriptVarLink() {
var->unref();
}
CScriptVarLink::CScriptVarLink(const CScriptVarLink &link) {
// Copy constructor
#if DEBUG_MEMORY
mark_allocated(this);
#endif
this->name = link.name;
this->nextSibling = 0;
this->prevSibling = 0;
this->var = link.var->ref();
this->owned = false;
}
void CScriptVarLink::replaceWith(CScriptVar *newVar) {
CScriptVar *oldVar = var;
var = newVar->ref();
@ -593,6 +609,11 @@ CScriptVar *CScriptVar::getReturnVar() {
return getParameter(TINYJS_RETURN_VAR);
}
void CScriptVar::setReturnVar(CScriptVar *var) {
findChildOrCreate(TINYJS_RETURN_VAR)->replaceWith(var);
}
CScriptVar *CScriptVar::getParameter(const std::string &name) {
return findChildOrCreate(name)->var;
}
@ -855,8 +876,16 @@ void CScriptVar::copyValue(CScriptVar *val) {
// copy children of 'val'
CScriptVarLink *child = val->firstChild;
while (child) {
addChild(child->name, child->var->deepCopy());
child = child->nextSibling;
CScriptVar *copied;
// don't copy the 'parent' object...
if (child->name != TINYJS_PARENT_CLASS)
copied = child->var->deepCopy();
else
copied = child->var;
addChild(child->name, copied);
child = child->nextSibling;
}
} else {
setUndefined();
@ -868,7 +897,14 @@ CScriptVar *CScriptVar::deepCopy() {
// copy children
CScriptVarLink *child = firstChild;
while (child) {
newVar->addChild(child->name, child->var->deepCopy());
CScriptVar *copied;
// don't copy the 'parent' object...
if (child->name != TINYJS_PARENT_CLASS)
copied = child->var->deepCopy();
else
copied = child->var;
newVar->addChild(child->name, copied);
child = child->nextSibling;
}
return newVar;
@ -908,14 +944,13 @@ string CScriptVar::getParsableString() {
funcStr << "function (";
// get list of parameters
CScriptVarLink *link = firstChild;
bool first = true;
while (link) {
if (!first) { funcStr << ","; first = false; }
funcStr << link->name;
if (link->nextSibling) funcStr << ",";
link = link->nextSibling;
}
// add function body
funcStr << ")" << getString();
funcStr << ") " << getString();
return funcStr.str();
}
// if it is a string then we quote it
@ -926,28 +961,29 @@ string CScriptVar::getParsableString() {
return "undefined";
}
void CScriptVar::getJSON(ostringstream &destination) {
if (firstChild) {
void CScriptVar::getJSON(ostringstream &destination, const string linePrefix) {
if (!firstChild || isFunction()) {
// no children or a function... just write value directly
destination << getParsableString();
} else {
string indentedLinePrefix = linePrefix+" ";
// children - handle with bracketed list
destination << " { \n";
destination << "{ \n";
CScriptVarLink *link = firstChild;
while (link) {
destination << indentedLinePrefix;
if (isAlphaNum(link->name))
destination << link->name;
else
destination << getJSString(link->name);
destination << " : ";
link->var->getJSON(destination);
link = link->nextSibling;
if (link)
destination << ",\n";
else
destination << "\n";
link->var->getJSON(destination, indentedLinePrefix);
link = link->nextSibling;
if (link) {
destination << ",\n";
}
}
destination << " }\n";
} else {
// no children, just write value directly
destination << getParsableString();
destination << "\n" << linePrefix << "}";
}
}
@ -1004,6 +1040,7 @@ void CTinyJS::trace() {
void CTinyJS::execute(const string &code) {
CScriptLex *oldLex = l;
vector<CScriptVar*> oldScopes = scopes;
l = new CScriptLex(code);
scopes.clear();
scopes.push_back(root);
@ -1019,10 +1056,13 @@ void CTinyJS::execute(const string &code) {
}
delete l;
l = oldLex;
scopes = oldScopes;
}
string CTinyJS::evaluate(const string &code) {
CScriptVarLink CTinyJS::evaluateComplex(const string &code) {
CScriptLex *oldLex = l;
vector<CScriptVar*> oldScopes = scopes;
l = new CScriptLex(code);
scopes.clear();
scopes.push_back(root);
@ -1039,11 +1079,15 @@ string CTinyJS::evaluate(const string &code) {
}
delete l;
l = oldLex;
scopes = oldScopes;
string result = v ? v->var->getString() : "";
CLEAN(v);
if (v) return *v;
// return undefined...
return CScriptVarLink(new CScriptVar());
}
return result;
string CTinyJS::evaluate(const string &code) {
return evaluateComplex(code).var->getString();
}
void CTinyJS::parseFunctionArguments(CScriptVar *funcVar) {

@ -154,6 +154,7 @@ public:
bool owned;
CScriptVarLink(CScriptVar *var, const std::string &name = TINYJS_TEMP_NAME);
CScriptVarLink(const CScriptVarLink &link); ///< Copy constructor
~CScriptVarLink();
void replaceWith(CScriptVar *newVar); ///< Replace the Variable pointed to
void replaceWith(CScriptVarLink *newVar); ///< Replace the Variable pointed to (just dereferences)
@ -169,6 +170,7 @@ public:
~CScriptVar(void);
CScriptVar *getReturnVar(); ///< If this is a function, get the result value (for use by native functions)
void setReturnVar(CScriptVar *var); ///< Set the result value. Use this when setting complex return data as it avoids a deepCopy()
CScriptVar *getParameter(const std::string &name); ///< If this is a function, get the parameter with the given name (for use by native functions)
CScriptVarLink *findChild(const std::string &childName); ///< Tries to find a child with the given name, may return 0
@ -210,7 +212,7 @@ public:
void trace(std::string indentStr = "", const std::string &name = ""); ///< Dump out the contents of this using trace
std::string getFlagsAsString();
void getJSON(std::ostringstream &destination); ///< Write out all the JS code needed to recreate this script variable to the stream (as JSON)
void getJSON(std::ostringstream &destination, const std::string linePrefix=""); ///< Write out all the JS code needed to recreate this script variable to the stream (as JSON)
void setCallback(JSCallback callback, void *userdata);
CScriptVarLink *firstChild;
@ -239,6 +241,14 @@ public:
~CTinyJS();
void execute(const std::string &code);
/** Evaluate the given code and return a link to a javascript object,
* useful for (dangerous) JSON parsing. If nothing to return, will return
* 'undefined' variable type. CScriptVarLink is returned as this will
* automatically unref the result as it goes out of scope. If you want to
* keep it, you must use ref() and unref() */
CScriptVarLink evaluateComplex(const std::string &code);
/** Evaluate the given code and return a string. If nothing to return, will return
* 'undefined' */
std::string evaluate(const std::string &code);
/// add a native function to be called from TinyJS

@ -26,6 +26,7 @@
#include "TinyJS_Functions.h"
#include <math.h>
#include <cstdlib>
#include <sstream>
using namespace std;
// ----------------------------------------------- Actual Functions
@ -114,8 +115,21 @@ void scIntegerValueOf(CScriptVar *c, void *) {
c->getReturnVar()->setInt(val);
}
void scJSONStringify(CScriptVar *c, void *) {
std::ostringstream result;
c->getParameter("obj")->getJSON(result);
c->getReturnVar()->setString(result.str());
}
void scEval(CScriptVar *c, void *data) {
CTinyJS *tinyJS = (CTinyJS *)data;
std::string str = c->getParameter("jsCode")->getString();
c->setReturnVar(tinyJS->evaluateComplex(str).var);
}
// ----------------------------------------------- Register Functions
void registerFunctions(CTinyJS *tinyJS) {
tinyJS->addNative("function eval(jsCode)", scEval, tinyJS); // execute the given string and return the result
tinyJS->addNative("function trace()", scTrace, tinyJS);
tinyJS->addNative("function Object.dump()", scObjectDump, 0);
tinyJS->addNative("function Object.clone()", scObjectClone, 0);
@ -128,4 +142,7 @@ void registerFunctions(CTinyJS *tinyJS) {
tinyJS->addNative("function String.charAt(pos)", scStringCharAt, 0);
tinyJS->addNative("function Integer.parseInt(str)", scIntegerParseInt, 0); // string to int
tinyJS->addNative("function Integer.valueOf(str)", scIntegerValueOf, 0); // value of a single character
tinyJS->addNative("function JSON.stringify(obj, replacer)", scJSONStringify, 0); // convert to JSON. replacer is ignored at the moment
// JSON.parse is left out as you can (unsafely!) use eval instead
}

@ -0,0 +1,5 @@
/* Javascript eval */
myfoo = eval("{ foo: 42 }");
result = eval("4*10+2")==42 && myfoo.foo==42;

@ -0,0 +1,9 @@
/* Javascript eval */
mystructure = { a:39, b:3, addStuff : function(c,d) { return c+d; } };
mystring = JSON.stringify(mystructure, undefined);
mynewstructure = eval(mystring);
result = mynewstructure.addStuff(mynewstructure.a, mynewstructure.b);
Loading…
Cancel
Save