/** Push data into a stream. To be used by Espruino (not a user). * This either calls the on('data') handler if it exists, or it * puts the data in a buffer. This MAY CLAIM the string that is * passed in. * * This will return true on success, or false if the buffer is * full. Setting force=true will attempt to fill the buffer as * full as possible, and will raise an error flag if data is lost. */ bool jswrap_stream_pushData(JsVar *parent, JsVar *dataString, bool force) { assert(jsvIsObject(parent)); assert(jsvIsString(dataString)); bool ok = true; JsVar *callback = jsvFindChildFromString(parent, STREAM_CALLBACK_NAME, false); if (callback) { if (!jsiExecuteEventCallback(parent, callback, dataString, 0)) { jsError("Error processing Serial data handler - removing it."); jsErrorFlags |= JSERR_CALLBACK; jsvRemoveNamedChild(parent, STREAM_CALLBACK_NAME); } jsvUnLock(callback); } else { // No callback - try and add buffer JsVar *buf = jsvObjectGetChild(parent, STREAM_BUFFER_NAME, 0); if (!jsvIsString(buf)) { // no buffer, just set this one up jsvObjectSetChild(parent, STREAM_BUFFER_NAME, dataString); } else { // append (if there is room!) size_t bufLen = jsvGetStringLength(buf); size_t dataLen = jsvGetStringLength(dataString); if (bufLen + dataLen > STREAM_MAX_BUFFER_SIZE) { if (force) jsErrorFlags |= JSERR_BUFFER_FULL; // jsWarn("String buffer overflowed maximum size (%d)", STREAM_MAX_BUFFER_SIZE); ok = false; } if ((ok || force) && (bufLen < STREAM_MAX_BUFFER_SIZE)) jsvAppendStringVar(buf, dataString, 0, STREAM_MAX_BUFFER_SIZE-bufLen); jsvUnLock(buf); } } return ok; }
/*JSON{ "type" : "method", "class" : "Object", "name" : "removeAllListeners", "generate" : "jswrap_object_removeAllListeners", "params" : [ ["event","JsVar","The name of the event, for instance 'data'"] ] } Removes all listeners, or those of the specified event. */ void jswrap_object_removeAllListeners(JsVar *parent, JsVar *event) { if (!jsvHasChildren(parent)) { jsWarn("Parent must be an object - not a String, Integer, etc."); return; } if (jsvIsString(event)) { // remove the whole child containing listeners char eventName[16]; if (!jswrap_object_get_event_name(eventName, event)) return; JsVar *eventList = jsvFindChildFromString(parent, eventName, true); if (eventList) { jsvRemoveChild(parent, eventList); jsvUnLock(eventList); } } else if (jsvIsUndefined(event)) { // Eep. We must remove everything beginning with '#on' (JS_EVENT_PREFIX) JsvObjectIterator it; jsvObjectIteratorNew(&it, parent); while (jsvObjectIteratorHasValue(&it)) { JsVar *key = jsvObjectIteratorGetKey(&it); jsvObjectIteratorNext(&it); if (jsvIsStringEqualOrStartsWith(key, JS_EVENT_PREFIX, true)) { // begins with #on - we must kill it jsvRemoveChild(parent, key); } jsvUnLock(key); } jsvObjectIteratorFree(&it); } else { jsWarn("First argument to EventEmitter.removeAllListeners(..) must be a string, or undefined"); return; } }
/*JSON{ "type":"method", "class": "Function", "name" : "replaceWith", "description" : ["This replaces the function with the one in the argument - while keeping the old function's scope. This allows inner functions to be edited, and is used when edit() is called on an inner function."], "generate" : "jswrap_function_replaceWith", "params" : [ [ "newFunc", "JsVar", "The new function to replace this function with"] ] }*/ void jswrap_function_replaceWith(JsVar *oldFunc, JsVar *newFunc) { if (!jsvIsFunction(newFunc)) { jsWarn("First argument of replaceWith should be a function - ignoring"); return; } // Grab scope - the one thing we want to keep JsVar *scope = jsvFindChildFromString(oldFunc, JSPARSE_FUNCTION_SCOPE_NAME, false); // so now remove all existing entries jsvRemoveAllChildren(oldFunc); // now re-add scope jsvAddName(oldFunc, scope); jsvUnLock(scope); // now re-add other entries JsObjectIterator it; jsvObjectIteratorNew(&it, newFunc); while (jsvObjectIteratorHasElement(&it)) { JsVar *el = jsvObjectIteratorGetKey(&it); jsvObjectIteratorNext(&it); if (!jsvIsStringEqual(el, JSPARSE_FUNCTION_SCOPE_NAME)) { JsVar *copy = jsvCopy(el); if (copy) { jsvAddName(oldFunc, copy); jsvUnLock(copy); } } } jsvObjectIteratorFree(&it); }
/*JSON{ "type":"function", "name" : "analogWrite", "description" : "Set the analog Value of a pin. It will be output using PWM", "generate" : "jswrap_io_analogWrite", "params" : [ [ "pin", "pin", "The pin to use"], [ "value", "float", "A value between 0 and 1"], [ "options", "JsVar", ["An object containing options.", "Currently only freq (pulse frequency in Hz) is available: ```analogWrite(LED1,0.5,{ freq : 10 });``` ", "Note that specifying a frequency will force PWM output, even if the pin has a DAC"] ] ] }*/ void jswrap_io_analogWrite(Pin pin, JsVarFloat value, JsVar *options) { JsVarFloat freq = 0; if (jsvIsObject(options)) { freq = jsvGetFloatAndUnLock(jsvSkipNameAndUnLock(jsvFindChildFromString(options, "freq", false))); } jshPinAnalogOutput(pin, value, freq); }
static JsVar* fsGetArray(const char *name, bool create) { JsVar *arrayName = jsvFindChildFromString(execInfo.root, name, create); JsVar *arr = jsvSkipName(arrayName); if (!arr && create) { arr = jsvNewWithFlags(JSV_ARRAY); jsvSetValueOfName(arrayName, arr); } jsvUnLock(arrayName); return arr; }
/*JSON{ "type":"function", "name" : "edit", "description" : ["Fill the console with the contents of the given function, so you can edit it.", "NOTE: This is a convenience function - it will not edit 'inner functions'. For that, you must edit the 'outer function' and re-execute it."], "generate" : "jswrap_interface_edit", "params" : [ [ "funcName", "JsVar", "The name of the function to edit (either a string or just the unquoted name)"] ] }*/ void jswrap_interface_edit(JsVar *funcName) { JsVar *func = 0; if (jsvIsString(funcName)) { funcName = jsvLockAgain(funcName); func = jsvSkipNameAndUnLock(jsvFindChildFromVar(execInfo.root, funcName, 0)); } else { func = funcName; funcName = jsvGetPathTo(execInfo.root, func, 2); } if (jsvIsString(funcName)) { if (jsvIsFunction(func)) { JsVar *scopeVar = jsvFindChildFromString(func, JSPARSE_FUNCTION_SCOPE_NAME, false); JsVar *inRoot = jsvGetArrayIndexOf(execInfo.root, func, true); bool normalDecl = scopeVar==0 && inRoot!=0; jsvUnLock(inRoot); jsvUnLock(scopeVar); JsVar *newLine = jsvNewFromEmptyString(); if (newLine) { // could be out of memory /* normalDecl: * * function foo() { ... } * * NOT normalDecl: * * foo.replaceWith(function() { ... }); * */ JsVar *funcData = jsvAsString(func, false); if (normalDecl) { jsvAppendString(newLine, "function "); jsvAppendStringVarComplete(newLine, funcName); jsvAppendStringVar(newLine, funcData, 9, JSVAPPENDSTRINGVAR_MAXLENGTH); } else { jsvAppendStringVarComplete(newLine, funcName); jsvAppendString(newLine, ".replaceWith("); jsvAppendStringVarComplete(newLine, funcData); jsvAppendString(newLine, ");"); } jsvUnLock(funcData); jsiReplaceInputLine(newLine); jsvUnLock(newLine); } } else { jsError("Edit should be called with the name of a function"); } } else { jsError("Edit should be called with edit(funcName) or edit('funcName')"); } jsvUnLock(func); jsvUnLock(funcName); }
static void fileSetVar(JsFile *file) { JsVar *fHandle = jsvFindChildFromString(file->fileVar, JS_FS_DATA_NAME, true); JsVar *data = jsvSkipName(fHandle); if (!data) { data = jsvNewStringOfLength(sizeof(JsFileData)); jsvSetValueOfName(fHandle, data); } jsvUnLock(fHandle); assert(data); jsvSetString(data, (char*)&file->data, sizeof(JsFileData)); jsvUnLock(data); }
void graphicsSetVar(JsGraphics *gfx) { JsVar *dataname = jsvFindChildFromString(gfx->graphicsVar, JS_HIDDEN_CHAR_STR"gfx", true); JsVar *data = jsvSkipName(dataname); if (!data) { data = jsvNewStringOfLength(sizeof(JsGraphicsData)); jsvSetValueOfName(dataname, data); } jsvUnLock(dataname); assert(data); jsvSetString(data, (char*)&gfx->data, sizeof(JsGraphicsData)); jsvUnLock(data); }
static JsVar *jswrap_modules_getModuleList() { JsVar *moduleListName = jsvFindChildFromString(jsiGetParser()->root, JSPARSE_MODULE_CACHE_NAME, true); if (!moduleListName) return 0; // out of memory JsVar *moduleList = jsvSkipName(moduleListName); if (!moduleList) { moduleList = jsvNewWithFlags(JSV_OBJECT); if (!moduleList) { jsvUnLock(moduleListName); return 0; } // out of memory jsvSetValueOfName(moduleListName, moduleList); // no need to unlock } jsvUnLock(moduleListName); return moduleList; }
/*JSON{ "type":"method", "class": "Serial", "name" : "onData", "description" : ["When a character is received on this serial port, the function supplied to onData gets called.", "Only one function can ever be supplied, so calling onData(undefined) will stop any function being called"], "generate" : "jswrap_serial_onData", "params" : [ [ "function", "JsVarName", "A function to call when data arrives. It takes one argument, which is an object with a 'data' field"] ] }*/ void jswrap_serial_onData(JsVar *parent, JsVar *funcVar) { JsVar *skippedFunc = jsvSkipName(funcVar); if (!jsvIsFunction(skippedFunc) && !jsvIsString(skippedFunc)) { jsiConsolePrint("Function or String not supplied - removing onData handler.\n"); JsVar *handler = jsvFindChildFromString(parent, USART_CALLBACK_NAME, false); if (handler) { jsvRemoveChild(parent, handler); jsvUnLock(handler); } } else { jsvUnLock(jsvSetNamedChild(parent, funcVar, USART_CALLBACK_NAME)); } jsvUnLock(skippedFunc); }
/*JSON{ "type" : "method", "class" : "Object", "name" : "on", "generate" : "jswrap_object_on", "params" : [ ["event","JsVar","The name of the event, for instance 'data'"], ["listener","JsVar","The listener to call when this event is received"] ] } Register an event listener for this object, for instance ```http.on('data', function(d) {...})```. See Node.js's EventEmitter. */ void jswrap_object_on(JsVar *parent, JsVar *event, JsVar *listener) { if (!jsvIsObject(parent)) { jsWarn("Parent must be a proper object - not a String, Integer, etc."); return; } if (!jsvIsString(event)) { jsWarn("First argument to EventEmitter.on(..) must be a string"); return; } if (!jsvIsFunction(listener) && !jsvIsString(listener)) { jsWarn("Second argument to EventEmitter.on(..) must be a function or a String (containing code)"); return; } char eventName[16] = "#on"; jsvGetString(event, &eventName[3], sizeof(eventName)-4); JsVar *eventList = jsvFindChildFromString(parent, eventName, true); JsVar *eventListeners = jsvSkipName(eventList); if (jsvIsUndefined(eventListeners)) { // just add jsvSetValueOfName(eventList, listener); } else { if (jsvIsArray(eventListeners)) { // we already have an array, just add to it jsvArrayPush(eventListeners, listener); } else { // not an array - we need to make it an array JsVar *arr = jsvNewWithFlags(JSV_ARRAY); jsvArrayPush(arr, eventListeners); jsvArrayPush(arr, listener); jsvSetValueOfName(eventList, arr); jsvUnLock(arr); } } jsvUnLock(eventListeners); jsvUnLock(eventList); /* Special case if we're a data listener and data has already arrived then * we queue an event immediately. */ if (jsvIsStringEqual(event, "data")) { JsVar *buf = jsvObjectGetChild(parent, STREAM_BUFFER_NAME, 0); if (jsvIsString(buf)) { jsiQueueObjectCallbacks(parent, "#ondata", &buf, 1); jsvRemoveNamedChild(parent, STREAM_BUFFER_NAME); } jsvUnLock(buf); } }
/*JSON{ "type" : "method", "class" : "Function", "name" : "replaceWith", "generate" : "jswrap_function_replaceWith", "params" : [ ["newFunc","JsVar","The new function to replace this function with"] ] } This replaces the function with the one in the argument - while keeping the old function's scope. This allows inner functions to be edited, and is used when edit() is called on an inner function. */ void jswrap_function_replaceWith(JsVar *oldFunc, JsVar *newFunc) { if (!jsvIsFunction(newFunc)) { jsWarn("First argument of replaceWith should be a function - ignoring"); return; } // If old was native or vice versa... if (jsvIsNativeFunction(oldFunc) != jsvIsNativeFunction(newFunc)) { if (jsvIsNativeFunction(newFunc)) oldFunc->flags |= JSV_NATIVE; else oldFunc->flags &= ~JSV_NATIVE; } // If old fn started with 'return' or vice versa... if (jsvIsFunctionReturn(oldFunc) != jsvIsFunctionReturn(newFunc)) { if (jsvIsFunctionReturn(newFunc)) oldFunc->flags = (oldFunc->flags&~JSV_VARTYPEMASK) |JSV_FUNCTION_RETURN; else oldFunc->flags = (oldFunc->flags&~JSV_VARTYPEMASK) |JSV_FUNCTION; } // Grab scope - the one thing we want to keep JsVar *scope = jsvFindChildFromString(oldFunc, JSPARSE_FUNCTION_SCOPE_NAME, false); // so now remove all existing entries jsvRemoveAllChildren(oldFunc); // now re-add scope if (scope) jsvAddName(oldFunc, scope); jsvUnLock(scope); // now re-add other entries JsvObjectIterator it; jsvObjectIteratorNew(&it, newFunc); while (jsvObjectIteratorHasValue(&it)) { JsVar *el = jsvObjectIteratorGetKey(&it); jsvObjectIteratorNext(&it); if (!jsvIsStringEqual(el, JSPARSE_FUNCTION_SCOPE_NAME)) { JsVar *copy = jsvCopy(el); if (copy) { jsvAddName(oldFunc, copy); jsvUnLock(copy); } } jsvUnLock(el); } jsvObjectIteratorFree(&it); }
bool run_test(const char *filename) { printf("----------------------------------\r\n"); printf("----------------------------- TEST %s \r\n", filename); char *buffer = read_file(filename); jshInit(); jsiInit(false /* do not autoload!!! */); jspAddNativeFunction(jsiGetParser(), "function quit()", nativeQuit); jspAddNativeFunction(jsiGetParser(), "function interrupt()", nativeInterrupt); jsvUnLock(jspEvaluate(jsiGetParser(), buffer )); isRunning = true; while (isRunning && jsiHasTimers()) jsiLoop(); JsVar *result = jsvSkipNameAndUnLock(jsvFindChildFromString(jsiGetParser()->root, "result", false/*no create*/)); bool pass = jsvGetBool(result); jsvUnLock(result); if (pass) printf("----------------------------- PASS %s\r\n", filename); else { printf("----------------------------------\r\n"); printf("----------------------------- FAIL %s <-------\r\n", filename); jsvTrace(jsvGetRef(jsiGetParser()->root), 0); printf("----------------------------- FAIL %s <-------\r\n", filename); printf("----------------------------------\r\n"); } printf("BEFORE: %d Memory Records Used\r\n", jsvGetMemoryUsage()); // jsvTrace(jsiGetParser()->root, 0); jsiKill(); printf("AFTER: %d Memory Records Used\r\n", jsvGetMemoryUsage()); jsvGarbageCollect(); printf("AFTER GC: %d Memory Records Used (should be 0!)\r\n", jsvGetMemoryUsage()); jsvShowAllocated(); jshKill(); //jsvDottyOutput(); printf("\r\n"); free(buffer); return pass; }
void lcdSetPixel_JS(JsGraphics *gfx, short x, short y, unsigned int col) { // look up setPixel and execute it! // JsVar *lcdProto = jsvSkipNameAndUnLock(jsvFindChildFromString(gfx->graphicsVar, JSPARSE_PROTOTYPE_VAR, false)); // if (lcdProto) { JsVar *setPixel = jsvSkipNameAndUnLock(jsvFindChildFromString(gfx->graphicsVar/*lcdProto*/, "setPixel", false)); if (setPixel) { JsVar *args[3]; args[0] = jsvNewFromInteger(x); args[1] = jsvNewFromInteger(y); args[2] = jsvNewFromInteger(col); jspExecuteFunction(jsiGetParser(), setPixel, gfx->graphicsVar, 3, args); jsvUnLock(args[0]); jsvUnLock(args[1]); jsvUnLock(args[2]); jsvUnLock(setPixel); } // jsvUnLock(lcdProto); // } }
/*JSON{ "type":"function", "name" : "edit", "description" : ["Fill the console with the contents of the given function, so you can edit it.", "NOTE: This is a convenience function - it will not edit 'inner functions'. For that, you must edit the 'outer function' and re-execute it."], "generate" : "jswrap_interface_edit", "params" : [ [ "funcName", "JsVarName", "The name of the function to edit (either a string or just the unquoted name)"] ] }*/ void jswrap_interface_edit(JsVar *funcName) { if (jsvIsString(funcName)) { JsVar *func = 0; if (jsvIsName(funcName)) func = jsvSkipName(funcName); else func = jsvSkipNameAndUnLock(jsvFindChildFromVar(jsiGetParser()->root, funcName, 0)); if (jsvIsFunction(func)) { JsVar *scopeVar = jsvFindChildFromString(func, JSPARSE_FUNCTION_SCOPE_NAME, false); JsVarRef scope = jsvGetRef(scopeVar); jsvUnLock(scopeVar); JsVar *newLine = jsvNewFromEmptyString(); if (newLine) { // could be out of memory jsvAppendStringVarComplete(newLine, funcName); if (scope) { // If we have a scope, it's an internal function so we will need to write different code jsvAppendString(newLine, ".replaceWith("); } else { jsvAppendString(newLine, " = "); } JsVar *funcData = jsvAsString(func, false); if (funcData) jsvAppendStringVarComplete(newLine, funcData); jsvUnLock(funcData); if (scope) { jsvAppendString(newLine, ");"); } else { jsvAppendString(newLine, ";"); } jsiReplaceInputLine(newLine); jsvUnLock(newLine); } } else { jsError("Edit should be called with the name of a function"); } jsvUnLock(func); } else { jsError("Edit should be called with edit(funcName) or edit('funcName')"); } }
/*JSON{ "type":"method", "class": "Object", "name" : "hasOwnProperty", "description" : ["Return true if the object (not its prototype) has the given property.","NOTE: This currently returns false-positives for built-in functions in prototypes"], "generate" : "jswrap_object_hasOwnProperty", "params" : [ [ "name", "JsVar", "The name of the property to search for"] ], "return" : ["bool", "True if it exists, false if it doesn't"] }*/ bool jswrap_object_hasOwnProperty(JsVar *parent, JsVar *name) { char str[32]; jsvGetString(name, str, sizeof(str)); bool contains = false; if (jsvHasChildren(parent)) { JsVar *foundVar = jsvFindChildFromString(parent, str, false); if (foundVar) { contains = true; jsvUnLock(foundVar); } } if (!contains) { // search builtins JsVar *foundVar = jswFindBuiltInFunction(parent, str); if (foundVar) { contains = true; jsvUnLock(foundVar); } } return contains; }
/*JSON{ "type":"method", "class": "Object", "name" : "on", "description" : ["Register an event listener for this object, for instance ```http.on('data', function(d) {...})```. See Node.js's EventEmitter."], "generate" : "jswrap_object_on", "params" : [ [ "event", "JsVar", "The name of the event, for instance 'data'"], [ "listener", "JsVar", "The listener to call when this event is received"] ] }*/ void jswrap_object_on(JsVar *parent, JsVar *event, JsVar *listener) { if (!jsvIsObject(parent)) { jsWarn("Parent must be a proper object - not a String, Integer, etc."); return; } if (!jsvIsString(event)) { jsWarn("First argument to EventEmitter.on(..) must be a string"); return; } if (!jsvIsFunction(listener) && !jsvIsString(listener)) { jsWarn("Second argument to EventEmitter.on(..) must be a function or a String (containing code)"); return; } char eventName[16] = "#on"; jsvGetString(event, &eventName[3], sizeof(eventName)-4); JsVar *eventList = jsvFindChildFromString(parent, eventName, true); JsVar *eventListeners = jsvSkipName(eventList); if (jsvIsUndefined(eventListeners)) { // just add jsvSetValueOfName(eventList, listener); } else { if (jsvIsArray(eventListeners)) { // we already have an array, just add to it jsvArrayPush(eventListeners, listener); } else { // not an array - we need to make it an array JsVar *arr = jsvNewWithFlags(JSV_ARRAY); jsvArrayPush(arr, eventListeners); jsvArrayPush(arr, listener); jsvSetValueOfName(eventList, arr); jsvUnLock(arr); } } jsvUnLock(eventListeners); jsvUnLock(eventList); }
/*JSON{ "type":"method", "class": "Object", "name" : "removeAllListeners", "description" : ["Removes all listeners, or those of the specified event."], "generate" : "jswrap_object_removeAllListeners", "params" : [ [ "event", "JsVar", "The name of the event, for instance 'data'"] ] }*/ void jswrap_object_removeAllListeners(JsVar *parent, JsVar *event) { if (!jsvIsObject(parent)) { jsWarn("Parent must be a proper object - not a String, Integer, etc."); return; } if (jsvIsString(event)) { // remove the whole child containing listeners char eventName[16] = "#on"; jsvGetString(event, &eventName[3], sizeof(eventName)-4); JsVar *eventList = jsvFindChildFromString(parent, eventName, true); if (eventList) { jsvRemoveChild(parent, eventList); jsvUnLock(eventList); } } else if (jsvIsUndefined(event)) { // Eep. We must remove everything beginning with '#on' JsObjectIterator it; jsvObjectIteratorNew(&it, parent); while (jsvObjectIteratorHasElement(&it)) { JsVar *key = jsvObjectIteratorGetKey(&it); jsvObjectIteratorNext(&it); if (jsvIsString(key) && key->varData.str[0]=='#' && key->varData.str[1]=='o' && key->varData.str[2]=='n') { // begins with #on - we must kill it jsvRemoveChild(parent, key); } jsvUnLock(key); } jsvObjectIteratorFree(&it); } else { jsWarn("First argument to EventEmitter.removeAllListeners(..) must be a string, or undefined"); return; } }
void _wlan_getIP_get_address(JsVar *object, const char *name, unsigned char *ip, int nBytes, int base, char separator) { char data[64] = ""; int i, l = 0; for (i=nBytes-1;i>=0;i--) { itoa(ip[i], &data[l], base); l = strlen(data); if (i>0 && separator) { data[l++] = separator; data[l] = 0; } } JsVar *dataVar = jsvNewFromString(data); if (!dataVar) return; JsVar *v = jsvFindChildFromString(object, name, true); if (!v) { jsvUnLock(dataVar); return; // out of memory } jsvSetValueOfName(v, dataVar); jsvUnLock(dataVar); jsvUnLock(v); }
static void NO_INLINE _eth_getIP_get_address(JsVar *object, const char *name, unsigned char *ip, int nBytes, unsigned int base, char separator) { char data[64] = ""; int i, l = 0; for (i=0;i<nBytes;i++) { itoa((int)ip[i], &data[l], base); l = (int)strlen(data); if (i<nBytes-1 && separator) { data[l++] = separator; data[l] = 0; } } JsVar *dataVar = jsvNewFromString(data); if (!dataVar) return; JsVar *v = jsvFindChildFromString(object, name, true); if (!v) { jsvUnLock(dataVar); return; // out of memory } jsvSetValueOfName(v, dataVar); jsvUnLock(dataVar); jsvUnLock(v); }