static void httpAppendHeaders(JsVar *string, JsVar *headerObject) { // append headers JsObjectIterator it; jsvObjectIteratorNew(&it, headerObject); while (jsvObjectIteratorHasElement(&it)) { JsVar *k = jsvAsString(jsvObjectIteratorGetKey(&it), true); JsVar *v = jsvAsString(jsvObjectIteratorGetValue(&it), true); jsvAppendStringVarComplete(string, k); jsvAppendString(string, ": "); jsvAppendStringVarComplete(string, v); jsvAppendString(string, "\r\n"); jsvUnLock(k); jsvUnLock(v); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); // free headers }
/*JSON{ "type" : "staticmethod", "class" : "console", "name" : "log", "generate" : "jswrap_interface_print", "params" : [ ["text","JsVarArray","One or more arguments to print"] ] } Print the supplied string(s) to the console **Note:** If you're connected to a computer (not a wall adaptor) via USB but **you are not running a terminal app** then when you print data Espruino may pause execution and wait until the computer requests the data it is trying to print. */ void jswrap_interface_print(JsVar *v) { assert(jsvIsArray(v)); jsiConsoleRemoveInputLine(); JsvObjectIterator it; jsvObjectIteratorNew(&it, v); while (jsvObjectIteratorHasValue(&it)) { JsVar *v = jsvObjectIteratorGetValue(&it); if (jsvIsString(v)) jsiConsolePrintStringVar(v); else jsfPrintJSON(v, JSON_PRETTY | JSON_NEWLINES); jsvUnLock(v); jsvObjectIteratorNext(&it); if (jsvObjectIteratorHasValue(&it)) jsiConsolePrint(" "); } jsvObjectIteratorFree(&it); jsiConsolePrint("\n"); }
/*JSON{ "type" : "staticmethod", "class" : "Object", "name" : "defineProperties", "generate" : "jswrap_object_defineProperties", "params" : [ ["obj","JsVar","An object"], ["props","JsVar","An object whose fields represent property names, and whose values are property descriptors."] ], "return" : ["JsVar","The object, obj."] } Adds new properties to the Object. See `Object.defineProperty` for more information */ JsVar *jswrap_object_defineProperties(JsVar *parent, JsVar *props) { if (!jsvIsObject(parent)) { jsExceptionHere(JSET_ERROR, "First argument must be an object, got %t\n", parent); return 0; } if (!jsvIsObject(props)) { jsExceptionHere(JSET_ERROR, "Second argument must be an object, got %t\n", props); return 0; } JsvObjectIterator it; jsvObjectIteratorNew(&it, props); while (jsvObjectIteratorHasValue(&it)) { JsVar *name = jsvObjectIteratorGetKey(&it); JsVar *desc = jsvObjectIteratorGetValue(&it); jsvUnLock3(jswrap_object_defineProperty(parent, name, desc), name, desc); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); return jsvLockAgain(parent); }
/** This gets called when a pipe destination drains itself */ static void jswrap_pipe_drain_listener(JsVar *destination) { if (!jsvIsObject(destination)) return; // try and find it... JsVar *arr = pipeGetArray(false); if (arr) { JsvObjectIterator it; jsvObjectIteratorNew(&it, arr); while (jsvObjectIteratorHasValue(&it)) { JsVar *pipe = jsvObjectIteratorGetValue(&it); JsVar *dst = jsvObjectGetChild(pipe,"destination",0); if (dst == destination) { // found it! said wait to false jsvUnLock(jsvObjectSetChild(pipe,"drainWait",jsvNewFromBool(false))); } jsvUnLock(dst); jsvUnLock(pipe); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); jsvUnLock(arr); } }
/** This gets called when the destination closes and we need to clean up */ static void jswrap_pipe_close_listener(JsVar *destination, const char *name) { if (!jsvIsObject(destination)) return; // try and find it... JsVar *arr = pipeGetArray(false); if (arr) { JsvObjectIterator it; jsvObjectIteratorNew(&it, arr); while (jsvObjectIteratorHasValue(&it)) { JsVar *pipe = jsvObjectIteratorGetValue(&it); JsVar *dst = jsvObjectGetChild(pipe,name,0); if (dst == destination) { // found it! said wait to false handlePipeClose(arr, &it, pipe); } jsvUnLock(dst); jsvUnLock(pipe); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); jsvUnLock(arr); } }
/* This is like jsfGetJSONWithCallback, but handles ONLY functions (and does not print the initial 'function' text) */ void jsfGetJSONForFunctionWithCallback(JsVar *var, JSONFlags flags, vcbprintf_callback user_callback, void *user_data) { assert(jsvIsFunction(var)); JsVar *codeVar = 0; // TODO: this should really be in jsvAsString JsvObjectIterator it; jsvObjectIteratorNew(&it, var); bool firstParm = true; cbprintf(user_callback, user_data, "("); while (jsvObjectIteratorHasValue(&it)) { JsVar *child = jsvObjectIteratorGetKey(&it); if (jsvIsFunctionParameter(child)) { if (firstParm) firstParm=false; else cbprintf(user_callback, user_data, ","); cbprintf(user_callback, user_data, "%v", child); } else if (jsvIsString(child) && jsvIsStringEqual(child, JSPARSE_FUNCTION_CODE_NAME)) { codeVar = jsvObjectIteratorGetValue(&it); } jsvUnLock(child); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); cbprintf(user_callback, user_data, ") "); if (jsvIsNative(var)) { cbprintf(user_callback, user_data, "{ [native code] }"); } else { if (codeVar) { if (flags & JSON_LIMIT) { cbprintf(user_callback, user_data, "{%s}", JSON_LIMIT_TEXT); } else { cbprintf(user_callback, user_data, "%v", codeVar); } } else cbprintf(user_callback, user_data, "{}"); } jsvUnLock(codeVar); }
/*JSON{ "type" : "kill", "generate" : "jswrap_waveform_kill", "ifndef" : "SAVE_ON_FLASH" }*/ void jswrap_waveform_kill() { // be sure to remove all waveforms... JsVar *waveforms = jsvObjectGetChild(execInfo.hiddenRoot, JSI_WAVEFORM_NAME, 0); if (waveforms) { JsvObjectIterator it; jsvObjectIteratorNew(&it, waveforms); while (jsvObjectIteratorHasValue(&it)) { JsVar *waveform = jsvObjectIteratorGetValue(&it); bool running = jsvGetBoolAndUnLock(jsvObjectGetChild(waveform, "running", 0)); if (running) { JsVar *buffer = jswrap_waveform_getBuffer(waveform,0,0); if (!jstStopBufferTimerTask(buffer)) { jsExceptionHere(JSET_ERROR, "Waveform couldn't be stopped"); } jsvUnLock(buffer); } jsvUnLock(waveform); // if not running, remove waveform from this list jsvObjectIteratorRemoveAndGotoNext(&it, waveforms); } jsvObjectIteratorFree(&it); jsvUnLock(waveforms); } }
/*JSON{ "type" : "method", "class" : "Object", "name" : "emit", "generate" : "jswrap_object_emit", "params" : [ ["event","JsVar","The name of the event, for instance 'data'"], ["args","JsVarArray","Optional arguments"] ] } Call the event listeners for this object, for instance ```http.emit('data', 'Foo')```. See Node.js's EventEmitter. */ void jswrap_object_emit(JsVar *parent, JsVar *event, JsVar *argArray) { if (!jsvHasChildren(parent)) { jsWarn("Parent must be an object - not a String, Integer, etc."); return; } if (!jsvIsString(event)) { jsWarn("First argument to EventEmitter.emit(..) must be a string"); return; } JsVar *eventName = jsvVarPrintf(JS_EVENT_PREFIX"%s",event); if (!eventName) return; // no memory // extract data const unsigned int MAX_ARGS = 4; JsVar *args[MAX_ARGS]; unsigned int n = 0; JsvObjectIterator it; jsvObjectIteratorNew(&it, argArray); while (jsvObjectIteratorHasValue(&it)) { if (n>=MAX_ARGS) { jsWarn("Too many arguments"); break; } args[n++] = jsvObjectIteratorGetValue(&it); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); JsVar *callback = jsvSkipNameAndUnLock(jsvFindChildFromVar(parent, eventName, 0)); jsvUnLock(eventName); if (callback) jsiQueueEvents(parent, callback, args, (int)n); jsvUnLock(callback); // unlock jsvUnLockMany(n, args); }
/*JSON{ "type" : "method", "class" : "Array", "name" : "splice", "generate" : "jswrap_array_splice", "params" : [ ["index","int","Index at which to start changing the array. If negative, will begin that many elements from the end"], ["howMany","JsVar","An integer indicating the number of old array elements to remove. If howMany is 0, no elements are removed."], ["elements","JsVarArray","One or more items to add to the array"] ], "return" : ["JsVar","An array containing the removed elements. If only one element is removed, an array of one element is returned."] } Both remove and add items to an array */ JsVar *jswrap_array_splice(JsVar *parent, JsVarInt index, JsVar *howManyVar, JsVar *elements) { if (!jsvIsArray(parent)) return 0; JsVarInt len = jsvGetArrayLength(parent); if (index<0) index+=len; if (index<0) index=0; if (index>len) index=len; JsVarInt howMany = len; // how many to delete! if (jsvIsInt(howManyVar)) howMany = jsvGetInteger(howManyVar); if (howMany > len-index) howMany = len-index; JsVarInt newItems = jsvGetArrayLength(elements); JsVarInt shift = newItems-howMany; bool needToAdd = false; JsVar *result = jsvNewEmptyArray(); JsvObjectIterator it; jsvObjectIteratorNew(&it, parent); while (jsvObjectIteratorHasValue(&it) && !needToAdd) { bool goToNext = true; JsVar *idxVar = jsvObjectIteratorGetKey(&it); if (idxVar && jsvIsInt(idxVar)) { JsVarInt idx = jsvGetInteger(idxVar); if (idx<index) { // do nothing... } else if (idx<index+howMany) { // must delete if (result) { // append to result array JsVar *el = jsvObjectIteratorGetValue(&it); jsvArrayPushAndUnLock(result, el); } // delete goToNext = false; JsVar *toRemove = jsvObjectIteratorGetKey(&it); jsvObjectIteratorNext(&it); jsvRemoveChild(parent, toRemove); jsvUnLock(toRemove); } else { // we're greater than the amount we need to remove now needToAdd = true; goToNext = false; } } jsvUnLock(idxVar); if (goToNext) jsvObjectIteratorNext(&it); } // now we add everything JsVar *beforeIndex = jsvObjectIteratorGetKey(&it); JsvObjectIterator itElement; jsvObjectIteratorNew(&itElement, elements); while (jsvObjectIteratorHasValue(&itElement)) { JsVar *element = jsvObjectIteratorGetValue(&itElement); jsvArrayInsertBefore(parent, beforeIndex, element); jsvUnLock(element); jsvObjectIteratorNext(&itElement); } jsvObjectIteratorFree(&itElement); jsvUnLock(beforeIndex); // And finally renumber while (jsvObjectIteratorHasValue(&it)) { JsVar *idxVar = jsvObjectIteratorGetKey(&it); if (idxVar && jsvIsInt(idxVar)) { jsvSetInteger(idxVar, jsvGetInteger(idxVar)+shift); } jsvUnLock(idxVar); jsvObjectIteratorNext(&it); } // free jsvObjectIteratorFree(&it); // and reset array size jsvSetArrayLength(parent, len + shift, false); return result; }
void jsfGetJSONWithCallback(JsVar *var, JSONFlags flags, const char *whitespace, vcbprintf_callback user_callback, void *user_data) { JSONFlags nflags = flags + JSON_INDENT; // if we add a newline, make sure we indent any subsequent JSON more if (!whitespace) whitespace=" "; if (jsvIsUndefined(var)) { cbprintf(user_callback, user_data, "undefined"); } else { // Use IS_RECURSING flag to stop recursion if (var->flags & JSV_IS_RECURSING) { cbprintf(user_callback, user_data, " ... "); return; } var->flags |= JSV_IS_RECURSING; if (jsvIsArray(var)) { JsVarInt length = jsvGetArrayLength(var); bool limited = (flags&JSON_LIMIT) && (length>(JsVarInt)JSON_LIMIT_AMOUNT); bool needNewLine = false; cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"[ ":"["); JsVarInt lastIndex = -1; bool numeric = true; bool first = true; JsvObjectIterator it; jsvObjectIteratorNew(&it, var); while (lastIndex+1<length && numeric && !jspIsInterrupted()) { JsVar *key = jsvObjectIteratorGetKey(&it); if (!jsvObjectIteratorHasValue(&it) || jsvIsNumeric(key)) { JsVarInt index = jsvObjectIteratorHasValue(&it) ? jsvGetInteger(key) : length-1; JsVar *item = jsvObjectIteratorGetValue(&it); while (lastIndex < index) { lastIndex++; if (!limited || lastIndex<(JsVarInt)JSON_LIMITED_AMOUNT || lastIndex>=length-(JsVarInt)JSON_LIMITED_AMOUNT) { if (!first) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); first = false; if (limited && lastIndex==length-(JsVarInt)JSON_LIMITED_AMOUNT) cbprintf(user_callback, user_data, JSON_LIMIT_TEXT); bool newNeedsNewLine = ((flags&JSON_SOME_NEWLINES) && jsonNeedsNewLine(item)); if (flags&JSON_ALL_NEWLINES) { needNewLine = true; newNeedsNewLine = true; } if (needNewLine || newNeedsNewLine) { jsonNewLine(nflags, whitespace, user_callback, user_data); needNewLine = false; } if (lastIndex == index) jsfGetJSONWithCallback(item, nflags, whitespace, user_callback, user_data); else cbprintf(user_callback, user_data, (flags&JSON_NO_UNDEFINED)?"null":"undefined"); needNewLine = newNeedsNewLine; } } jsvUnLock(item); jsvObjectIteratorNext(&it); } else { numeric = false; } jsvUnLock(key); } // non-numeric - but NOT for standard JSON if ((flags&JSON_PRETTY)) jsfGetJSONForObjectItWithCallback(&it, flags, whitespace, nflags, user_callback, user_data, first); jsvObjectIteratorFree(&it); if (needNewLine) jsonNewLine(flags, whitespace, user_callback, user_data); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?" ]":"]"); } else if (jsvIsArrayBuffer(var)) { JsvArrayBufferIterator it; bool allZero = true; jsvArrayBufferIteratorNew(&it, var, 0); while (jsvArrayBufferIteratorHasElement(&it)) { if (jsvArrayBufferIteratorGetFloatValue(&it)!=0) allZero = false; jsvArrayBufferIteratorNext(&it); } jsvArrayBufferIteratorFree(&it); bool asArray = flags&JSON_ARRAYBUFFER_AS_ARRAY; if (allZero && !asArray) { cbprintf(user_callback, user_data, "new %s(%d)", jswGetBasicObjectName(var), jsvGetArrayBufferLength(var)); } else { const char *aname = jswGetBasicObjectName(var); /* You can't do `new ArrayBuffer([1,2,3])` so we have to output * `new Uint8Array([1,2,3]).buffer`! */ bool isBasicArrayBuffer = strcmp(aname,"ArrayBuffer")==0; if (isBasicArrayBuffer) { aname="Uint8Array"; } cbprintf(user_callback, user_data, asArray?"[":"new %s([", aname); if (flags&JSON_ALL_NEWLINES) jsonNewLine(nflags, whitespace, user_callback, user_data); size_t length = jsvGetArrayBufferLength(var); bool limited = (flags&JSON_LIMIT) && (length>JSON_LIMIT_AMOUNT); // no newlines needed for array buffers as they only contain simple stuff jsvArrayBufferIteratorNew(&it, var, 0); while (jsvArrayBufferIteratorHasElement(&it) && !jspIsInterrupted()) { if (!limited || it.index<JSON_LIMITED_AMOUNT || it.index>=length-JSON_LIMITED_AMOUNT) { if (it.index>0) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); if (flags&JSON_ALL_NEWLINES) jsonNewLine(nflags, whitespace, user_callback, user_data); if (limited && it.index==length-JSON_LIMITED_AMOUNT) cbprintf(user_callback, user_data, JSON_LIMIT_TEXT); JsVar *item = jsvArrayBufferIteratorGetValue(&it); jsfGetJSONWithCallback(item, nflags, whitespace, user_callback, user_data); jsvUnLock(item); } jsvArrayBufferIteratorNext(&it); } if (flags&JSON_ALL_NEWLINES) jsonNewLine(flags, whitespace, user_callback, user_data); jsvArrayBufferIteratorFree(&it); cbprintf(user_callback, user_data, asArray?"]":"])"); if (isBasicArrayBuffer && !asArray) cbprintf(user_callback, user_data, ".buffer"); } } else if (jsvIsObject(var)) { IOEventFlags device = (flags & JSON_SHOW_DEVICES) ? jsiGetDeviceFromClass(var) : EV_NONE; if (device!=EV_NONE) { cbprintf(user_callback, user_data, "%s", jshGetDeviceString(device)); } else { bool showContents = true; if (flags & JSON_SHOW_OBJECT_NAMES) { JsVar *proto = jsvObjectGetChild(var, JSPARSE_INHERITS_VAR, 0); if (jsvHasChildren(proto)) { JsVar *constr = jsvObjectGetChild(proto, JSPARSE_CONSTRUCTOR_VAR, 0); if (constr) { JsVar *p = jsvGetIndexOf(execInfo.root, constr, true); if (p) cbprintf(user_callback, user_data, "%v: ", p); jsvUnLock2(p,constr); /* We had the constructor - now if there was a non-default toString function * we'll execute it and print the result */ JsVar *toStringFn = jspGetNamedField(var, "toString", false); if (toStringFn && toStringFn->varData.native.ptr != (void (*)(void))jswrap_object_toString) { // Function found and it's not the default one - execute it JsVar *result = jspExecuteFunction(toStringFn,var,0,0); cbprintf(user_callback, user_data, "%v", result); jsvUnLock(result); showContents = false; // we already printed something } jsvUnLock(toStringFn); } } jsvUnLock(proto); } if (showContents) { JsvObjectIterator it; jsvObjectIteratorNew(&it, var); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"{ ":"{"); bool needNewLine = jsfGetJSONForObjectItWithCallback(&it, flags, whitespace, nflags, user_callback, user_data, true); jsvObjectIteratorFree(&it); if (needNewLine) jsonNewLine(flags, whitespace, user_callback, user_data); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?" }":"}"); } } } else if (jsvIsFunction(var)) { if (flags & JSON_IGNORE_FUNCTIONS) { cbprintf(user_callback, user_data, "undefined"); } else { cbprintf(user_callback, user_data, "function "); jsfGetJSONForFunctionWithCallback(var, nflags, user_callback, user_data); } } else if (jsvIsString(var) && !jsvIsName(var)) { if ((flags&JSON_LIMIT) && jsvGetStringLength(var)>JSON_LIMIT_STRING_AMOUNT) { // if the string is too big, split it and put dots in the middle JsVar *var1 = jsvNewFromStringVar(var, 0, JSON_LIMITED_STRING_AMOUNT); JsVar *var2 = jsvNewFromStringVar(var, jsvGetStringLength(var)-JSON_LIMITED_STRING_AMOUNT, JSON_LIMITED_STRING_AMOUNT); cbprintf(user_callback, user_data, "%q%s%q", var1, JSON_LIMIT_TEXT, var2); jsvUnLock2(var1, var2); } else { cbprintf(user_callback, user_data, "%q", var); } } else { cbprintf(user_callback, user_data, "%v", var); } var->flags &= ~JSV_IS_RECURSING; } }
/* This is like jsfGetJSONWithCallback, but handles ONLY functions (and does not print the initial 'function' text) */ void jsfGetJSONForFunctionWithCallback(JsVar *var, JSONFlags flags, vcbprintf_callback user_callback, void *user_data) { assert(jsvIsFunction(var)); JsVar *codeVar = 0; // TODO: this should really be in jsvAsString JsvObjectIterator it; jsvObjectIteratorNew(&it, var); bool firstParm = true; cbprintf(user_callback, user_data, "("); while (jsvObjectIteratorHasValue(&it)) { JsVar *child = jsvObjectIteratorGetKey(&it); if (jsvIsFunctionParameter(child)) { if (firstParm) firstParm=false; else cbprintf(user_callback, user_data, ","); JsVar *name = jsvNewFromStringVar(child, 1, JSVAPPENDSTRINGVAR_MAXLENGTH); cbprintf(user_callback, user_data, "%v", name); jsvUnLock(name); } else if (jsvIsString(child) && jsvIsStringEqual(child, JSPARSE_FUNCTION_CODE_NAME)) { codeVar = jsvObjectIteratorGetValue(&it); } jsvUnLock(child); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); cbprintf(user_callback, user_data, ") "); if (jsvIsNative(var)) { cbprintf(user_callback, user_data, "{ [native code] }"); } else { if (codeVar) { if (flags & JSON_LIMIT) { cbprintf(user_callback, user_data, "{%s}", JSON_LIMIT_TEXT); } else { bool hasNewLine = jsvGetStringIndexOf(codeVar,'\n')>=0; user_callback(hasNewLine?"{\n ":"{", user_data); if (jsvIsFunctionReturn(var)) user_callback("return ", user_data); // reconstruct the tokenised output into something more readable char buf[32]; unsigned char lastch = 0; JsvStringIterator it; jsvStringIteratorNew(&it, codeVar, 0); while (jsvStringIteratorHasChar(&it)) { unsigned char ch = (unsigned char)jsvStringIteratorGetChar(&it); if (jslNeedSpaceBetween(lastch, ch)) user_callback(" ", user_data); jslFunctionCharAsString(ch, buf, sizeof(buf)); user_callback(buf, user_data); jsvStringIteratorNext(&it); lastch = ch; } jsvStringIteratorFree(&it); user_callback(hasNewLine?"\n}":"}", user_data); } } else cbprintf(user_callback, user_data, "{}"); } jsvUnLock(codeVar); }
void jsfGetJSONWithCallback(JsVar *var, JSONFlags flags, vcbprintf_callback user_callback, void *user_data) { JSONFlags nflags = flags + JSON_INDENT; // if we add a newline, make sure we indent any subsequent JSON more if (jsvIsUndefined(var)) { cbprintf(user_callback, user_data, "undefined"); } else { // Use IS_RECURSING flag to stop recursion if (var->flags & JSV_IS_RECURSING) { cbprintf(user_callback, user_data, " ... "); return; } var->flags |= JSV_IS_RECURSING; if (jsvIsArray(var)) { size_t length = (size_t)jsvGetArrayLength(var); bool limited = (flags&JSON_LIMIT) && (length>JSON_LIMIT_AMOUNT); bool needNewLine = false; size_t i; cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"[ ":"["); for (i=0;i<length && !jspIsInterrupted();i++) { if (!limited || i<JSON_LIMITED_AMOUNT || i>=length-JSON_LIMITED_AMOUNT) { if (i>0) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); if (limited && i==length-JSON_LIMITED_AMOUNT) { if (needNewLine) jsonNewLine(nflags, user_callback, user_data); cbprintf(user_callback, user_data, JSON_LIMIT_TEXT); } JsVar *item = jsvGetArrayItem(var, (JsVarInt)i); if (jsvIsUndefined(item) && (flags&JSON_NO_UNDEFINED)) item = jsvNewWithFlags(JSV_NULL); bool newNeedsNewLine = (flags&JSON_NEWLINES) && jsonNeedsNewLine(item); if (needNewLine || newNeedsNewLine) { jsonNewLine(nflags, user_callback, user_data); needNewLine = false; } jsfGetJSONWithCallback(item, nflags, user_callback, user_data); needNewLine = newNeedsNewLine; jsvUnLock(item); } } if (needNewLine) jsonNewLine(flags, user_callback, user_data); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?" ]":"]"); } else if (jsvIsArrayBuffer(var)) { JsvArrayBufferIterator it; bool allZero = true; jsvArrayBufferIteratorNew(&it, var, 0); while (jsvArrayBufferIteratorHasElement(&it)) { if (jsvArrayBufferIteratorGetFloatValue(&it)!=0) allZero = false; jsvArrayBufferIteratorNext(&it); } jsvArrayBufferIteratorFree(&it); if (allZero) { cbprintf(user_callback, user_data, "new %s(%d)", jswGetBasicObjectName(var), jsvGetArrayBufferLength(var)); } else { cbprintf(user_callback, user_data, "new %s([", jswGetBasicObjectName(var)); size_t length = jsvGetArrayBufferLength(var); bool limited = (flags&JSON_LIMIT) && (length>JSON_LIMIT_AMOUNT); // no newlines needed for array buffers as they only contain simple stuff jsvArrayBufferIteratorNew(&it, var, 0); while (jsvArrayBufferIteratorHasElement(&it) && !jspIsInterrupted()) { if (!limited || it.index<JSON_LIMITED_AMOUNT || it.index>=length-JSON_LIMITED_AMOUNT) { if (it.index>0) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); if (limited && it.index==length-JSON_LIMITED_AMOUNT) cbprintf(user_callback, user_data, JSON_LIMIT_TEXT); JsVar *item = jsvArrayBufferIteratorGetValue(&it); jsfGetJSONWithCallback(item, nflags, user_callback, user_data); jsvUnLock(item); } jsvArrayBufferIteratorNext(&it); } jsvArrayBufferIteratorFree(&it); cbprintf(user_callback, user_data, "])"); } } else if (jsvIsObject(var)) { IOEventFlags device = (flags & JSON_SHOW_DEVICES) ? jsiGetDeviceFromClass(var) : EV_NONE; if (device!=EV_NONE) { cbprintf(user_callback, user_data, "%s", jshGetDeviceString(device)); } else { bool first = true; bool needNewLine = false; size_t sinceNewLine = 0; JsvObjectIterator it; jsvObjectIteratorNew(&it, var); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"{ ":"{"); while (jsvObjectIteratorHasValue(&it) && !jspIsInterrupted()) { JsVar *index = jsvObjectIteratorGetKey(&it); JsVar *item = jsvObjectIteratorGetValue(&it); bool hidden = jsvIsInternalObjectKey(index) || ((flags & JSON_IGNORE_FUNCTIONS) && jsvIsFunction(item)) || ((flags&JSON_NO_UNDEFINED) && jsvIsUndefined(item)); if (!hidden) { sinceNewLine++; if (!first) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); bool newNeedsNewLine = (flags&JSON_NEWLINES) && jsonNeedsNewLine(item); if ((flags&JSON_NEWLINES) && sinceNewLine>JSON_ITEMS_ON_LINE_OBJECT) needNewLine = true; if (needNewLine || newNeedsNewLine) { jsonNewLine(nflags, user_callback, user_data); needNewLine = false; sinceNewLine = 0; } cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"%q: ":"%q:", index); if (first) first = false; jsfGetJSONWithCallback(item, nflags, user_callback, user_data); needNewLine = newNeedsNewLine; } jsvUnLock(index); jsvUnLock(item); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); if (needNewLine) jsonNewLine(flags, user_callback, user_data); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?" }":"}"); } } else if (jsvIsFunction(var)) { if (flags & JSON_IGNORE_FUNCTIONS) { cbprintf(user_callback, user_data, "undefined"); } else { cbprintf(user_callback, user_data, "function "); jsfGetJSONForFunctionWithCallback(var, nflags, user_callback, user_data); } } else if (jsvIsString(var) && !jsvIsName(var)) { if ((flags&JSON_LIMIT) && jsvGetStringLength(var)>JSON_LIMIT_STRING_AMOUNT) { // if the string is too big, split it and put dots in the middle JsVar *var1 = jsvNewFromStringVar(var, 0, JSON_LIMITED_STRING_AMOUNT); JsVar *var2 = jsvNewFromStringVar(var, jsvGetStringLength(var)-JSON_LIMITED_STRING_AMOUNT, JSON_LIMITED_STRING_AMOUNT); cbprintf(user_callback, user_data, "%q%s%q", var1, JSON_LIMIT_TEXT, var2); jsvUnLock(var1); jsvUnLock(var2); } else { cbprintf(user_callback, user_data, "%q", var); } } else { cbprintf(user_callback, user_data, "%v", var); } var->flags &= ~JSV_IS_RECURSING; } }
bool socketServerConnectionsIdle(JsNetwork *net) { char buf[64]; JsVar *arr = socketGetArray(HTTP_ARRAY_HTTP_SERVER_CONNECTIONS,false); if (!arr) return false; bool hadSockets = false; JsvObjectIterator it; jsvObjectIteratorNew(&it, arr); while (jsvObjectIteratorHasValue(&it)) { hadSockets = true; // Get connection, socket, and socket type // For normal sockets, socket==connection, but for HTTP we split it into a request and a response JsVar *connection = jsvObjectIteratorGetValue(&it); SocketType socketType = socketGetType(connection); JsVar *socket = (socketType==ST_HTTP) ? jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0) : jsvLockAgain(connection); int sckt = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_SOCKET,0))-1; // so -1 if undefined bool closeConnectionNow = jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CLOSENOW, false)); if (!closeConnectionNow) { int num = net->recv(net, sckt, buf,sizeof(buf)); if (num<0) { // we probably disconnected so just get rid of this closeConnectionNow = true; } else { // add it to our request string if (num>0) { JsVar *receiveData = jsvObjectGetChild(connection,HTTP_NAME_RECEIVE_DATA,0); JsVar *oldReceiveData = receiveData; if (!receiveData) receiveData = jsvNewFromEmptyString(); if (receiveData) { jsvAppendStringBuf(receiveData, buf, (size_t)num); bool hadHeaders = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_HAD_HEADERS,0)); if (!hadHeaders && httpParseHeaders(&receiveData, connection, true)) { hadHeaders = true; jsvUnLock(jsvObjectSetChild(connection, HTTP_NAME_HAD_HEADERS, jsvNewFromBool(hadHeaders))); JsVar *server = jsvObjectGetChild(connection,HTTP_NAME_SERVER_VAR,0); JsVar *args[2] = { connection, socket }; jsiQueueObjectCallbacks(server, HTTP_NAME_ON_CONNECT, args, (socketType==ST_HTTP) ? 2 : 1); jsvUnLock(server); } if (hadHeaders && !jsvIsEmptyString(receiveData)) { // execute 'data' callback or save data jswrap_stream_pushData(connection, receiveData); // clear received data jsvUnLock(receiveData); receiveData = 0; } // if received data changed, update it if (receiveData != oldReceiveData) jsvObjectSetChild(connection,HTTP_NAME_RECEIVE_DATA,receiveData); jsvUnLock(receiveData); } } } // send data if possible JsVar *sendData = jsvObjectGetChild(socket,HTTP_NAME_SEND_DATA,0); if (sendData) { if (!socketSendData(net, socket, sckt, &sendData)) closeConnectionNow = true; jsvObjectSetChild(socket, HTTP_NAME_SEND_DATA, sendData); // socketSendData prob updated sendData } // only close if we want to close, have no data to send, and aren't receiving data if (jsvGetBoolAndUnLock(jsvObjectGetChild(socket,HTTP_NAME_CLOSE,0)) && !sendData && num<=0) closeConnectionNow = true; jsvUnLock(sendData); } if (closeConnectionNow) { // send out any data that we were POSTed JsVar *receiveData = jsvObjectGetChild(connection,HTTP_NAME_RECEIVE_DATA,0); bool hadHeaders = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_HAD_HEADERS,0)); if (hadHeaders && !jsvIsEmptyString(receiveData)) { // execute 'data' callback or save data jswrap_stream_pushData(connection, receiveData); } jsvUnLock(receiveData); // fire the close listeners jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_CLOSE, 0, 0); jsiQueueObjectCallbacks(socket, HTTP_NAME_ON_CLOSE, 0, 0); _socketConnectionKill(net, connection); JsVar *connectionName = jsvObjectIteratorGetKey(&it); jsvObjectIteratorNext(&it); jsvRemoveChild(arr, connectionName); jsvUnLock(connectionName); } else jsvObjectIteratorNext(&it); jsvUnLock(connection); jsvUnLock(socket); } jsvObjectIteratorFree(&it); jsvUnLock(arr); return hadSockets; }
bool socketIdle(JsNetwork *net) { if (networkState != NETWORKSTATE_ONLINE) { // clear all clients and servers _socketCloseAllConnections(net); return false; } bool hadSockets = false; JsVar *arr = socketGetArray(HTTP_ARRAY_HTTP_SERVERS,false); if (arr) { JsvObjectIterator it; jsvObjectIteratorNew(&it, arr); while (jsvObjectIteratorHasValue(&it)) { hadSockets = true; JsVar *server = jsvObjectIteratorGetValue(&it); int sckt = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(server,HTTP_NAME_SOCKET,0))-1; // so -1 if undefined int theClient = netAccept(net, sckt); if (theClient >= 0) { SocketType socketType = socketGetType(server); if ((socketType&ST_TYPE_MASK) == ST_HTTP) { JsVar *req = jspNewObject(0, "httpSRq"); JsVar *res = jspNewObject(0, "httpSRs"); if (res && req) { // out of memory? socketSetType(req, ST_HTTP); JsVar *arr = socketGetArray(HTTP_ARRAY_HTTP_SERVER_CONNECTIONS, true); if (arr) { jsvArrayPush(arr, req); jsvUnLock(arr); } jsvObjectSetChild(req, HTTP_NAME_RESPONSE_VAR, res); jsvObjectSetChild(req, HTTP_NAME_SERVER_VAR, server); jsvObjectSetChildAndUnLock(req, HTTP_NAME_SOCKET, jsvNewFromInteger(theClient+1)); } jsvUnLock2(req, res); } else { // Normal sockets JsVar *sock = jspNewObject(0, "Socket"); if (sock) { // out of memory? socketSetType(sock, ST_NORMAL); JsVar *arr = socketGetArray(HTTP_ARRAY_HTTP_CLIENT_CONNECTIONS, true); if (arr) { jsvArrayPush(arr, sock); jsvUnLock(arr); } jsvObjectSetChildAndUnLock(sock, HTTP_NAME_SOCKET, jsvNewFromInteger(theClient+1)); jsiQueueObjectCallbacks(server, HTTP_NAME_ON_CONNECT, &sock, 1); jsvUnLock(sock); } } } jsvUnLock(server); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); jsvUnLock(arr); } if (socketServerConnectionsIdle(net)) hadSockets = true; if (socketClientConnectionsIdle(net)) hadSockets = true; netCheckError(net); return hadSockets; }
bool socketClientConnectionsIdle(JsNetwork *net) { char *buf = alloca(net->chunkSize); // allocate on stack JsVar *arr = socketGetArray(HTTP_ARRAY_HTTP_CLIENT_CONNECTIONS,false); if (!arr) return false; bool hadSockets = false; JsvObjectIterator it; jsvObjectIteratorNew(&it, arr); while (jsvObjectIteratorHasValue(&it)) { hadSockets = true; // Get connection, socket, and socket type // For normal sockets, socket==connection, but for HTTP connection is httpCRq and socket is httpCRs JsVar *connection = jsvObjectIteratorGetValue(&it); SocketType socketType = socketGetType(connection); JsVar *socket = ((socketType&ST_TYPE_MASK)==ST_HTTP) ? jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0) : jsvLockAgain(connection); bool socketClosed = false; JsVar *receiveData = 0; bool hadHeaders = false; int error = 0; // error code received from netXxxx functions bool isHttp = (socketType&ST_TYPE_MASK) == ST_HTTP; bool closeConnectionNow = jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CLOSENOW, false)); bool alreadyConnected = jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CONNECTED, false)); int sckt = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_SOCKET,0))-1; // so -1 if undefined if (sckt>=0) { if (isHttp) hadHeaders = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_HAD_HEADERS,0)); else hadHeaders = true; receiveData = jsvObjectGetChild(connection,HTTP_NAME_RECEIVE_DATA,0); /* We do this up here because we want to wait until we have been once * around the idle loop (=callbacks have been executed) before we run this */ if (hadHeaders) socketClientPushReceiveData(connection, socket, &receiveData); JsVar *sendData = jsvObjectGetChild(connection,HTTP_NAME_SEND_DATA,0); if (!closeConnectionNow) { // send data if possible if (sendData && !jsvIsEmptyString(sendData)) { // don't try to send if we're already in error state int num = 0; if (error == 0) num = socketSendData(net, connection, sckt, &sendData); if (num > 0 && !alreadyConnected && !isHttp) { // whoa, we sent something, must be connected! jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_CONNECT, &connection, 1); jsvObjectSetChildAndUnLock(connection, HTTP_NAME_CONNECTED, jsvNewFromBool(true)); alreadyConnected = true; } if (num < 0) { closeConnectionNow = true; error = num; } jsvObjectSetChild(connection, HTTP_NAME_SEND_DATA, sendData); // _http_send prob updated sendData } else { // no data to send, do we want to close? do so. if (jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CLOSE, false))) closeConnectionNow = true; } // Now read data if possible (and we have space for it) if (!receiveData || !hadHeaders) { int num = netRecv(net, sckt, buf, net->chunkSize); //if (num != 0) printf("recv returned %d\r\n", num); if (!alreadyConnected && num == SOCKET_ERR_NO_CONN) { ; // ignore... it's just telling us we're not connected yet } else if (num < 0) { closeConnectionNow = true; error = num; // disconnected without headers? error. if (!hadHeaders && error == SOCKET_ERR_CLOSED) error = SOCKET_ERR_NO_RESP; } else { // did we just get connected? if (!alreadyConnected && !isHttp) { jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_CONNECT, &connection, 1); jsvObjectSetChildAndUnLock(connection, HTTP_NAME_CONNECTED, jsvNewFromBool(true)); alreadyConnected = true; // if we do not have any data to send, issue a drain event if (!sendData || (int)jsvGetStringLength(sendData) == 0) jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_DRAIN, &connection, 1); } // got data add it to our receive buffer if (num > 0) { if (!receiveData) { receiveData = jsvNewFromEmptyString(); jsvObjectSetChild(connection, HTTP_NAME_RECEIVE_DATA, receiveData); } if (receiveData) { // could be out of memory jsvAppendStringBuf(receiveData, buf, (size_t)num); if ((socketType&ST_TYPE_MASK)==ST_HTTP && !hadHeaders) { // for HTTP see whether we now have full response headers JsVar *resVar = jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0); if (httpParseHeaders(&receiveData, resVar, false)) { hadHeaders = true; jsvObjectSetChildAndUnLock(connection, HTTP_NAME_HAD_HEADERS, jsvNewFromBool(hadHeaders)); jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_CONNECT, &resVar, 1); } jsvUnLock(resVar); jsvObjectSetChild(connection, HTTP_NAME_RECEIVE_DATA, receiveData); } } } } } jsvUnLock(sendData); } } if (closeConnectionNow) { socketClientPushReceiveData(connection, socket, &receiveData); if (!receiveData) { if ((socketType&ST_TYPE_MASK) != ST_HTTP) jsiQueueObjectCallbacks(socket, HTTP_NAME_ON_END, &socket, 1); // If we had data to send but the socket closed, this is an error JsVar *sendData = jsvObjectGetChild(connection,HTTP_NAME_SEND_DATA,0); if (sendData && jsvGetStringLength(sendData) > 0 && error == SOCKET_ERR_CLOSED) error = SOCKET_ERR_UNSENT_DATA; jsvUnLock(sendData); _socketConnectionKill(net, connection); JsVar *connectionName = jsvObjectIteratorGetKey(&it); jsvObjectIteratorNext(&it); jsvRemoveChild(arr, connectionName); jsvUnLock(connectionName); socketClosed = true; // fire error event, if there is an error bool hadError = fireErrorEvent(error, connection, NULL); // close callback must happen after error callback JsVar *params[1] = { jsvNewFromBool(hadError) }; jsiQueueObjectCallbacks(socket, HTTP_NAME_ON_CLOSE, params, 1); jsvUnLock(params[0]); } } if (!socketClosed) { jsvObjectIteratorNext(&it); } jsvUnLock3(receiveData, connection, socket); } jsvUnLock(arr); return hadSockets; }
bool socketServerConnectionsIdle(JsNetwork *net) { char *buf = alloca(net->chunkSize); // allocate on stack JsVar *arr = socketGetArray(HTTP_ARRAY_HTTP_SERVER_CONNECTIONS,false); if (!arr) return false; bool hadSockets = false; JsvObjectIterator it; jsvObjectIteratorNew(&it, arr); while (jsvObjectIteratorHasValue(&it)) { hadSockets = true; // Get connection, socket, and socket type // For normal sockets, socket==connection, but for HTTP we split it into a request and a response JsVar *connection = jsvObjectIteratorGetValue(&it); SocketType socketType = socketGetType(connection); JsVar *socket = ((socketType&ST_TYPE_MASK)==ST_HTTP) ? jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0) : jsvLockAgain(connection); int sckt = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_SOCKET,0))-1; // so -1 if undefined bool closeConnectionNow = jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CLOSENOW, false)); int error = 0; if (!closeConnectionNow) { int num = netRecv(net, sckt, buf, net->chunkSize); if (num<0) { // we probably disconnected so just get rid of this closeConnectionNow = true; error = num; } else { // add it to our request string if (num>0) { JsVar *receiveData = jsvObjectGetChild(connection,HTTP_NAME_RECEIVE_DATA,0); JsVar *oldReceiveData = receiveData; if (!receiveData) receiveData = jsvNewFromEmptyString(); if (receiveData) { jsvAppendStringBuf(receiveData, buf, (size_t)num); bool hadHeaders = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_HAD_HEADERS,0)); if (!hadHeaders && httpParseHeaders(&receiveData, connection, true)) { hadHeaders = true; jsvObjectSetChildAndUnLock(connection, HTTP_NAME_HAD_HEADERS, jsvNewFromBool(hadHeaders)); JsVar *server = jsvObjectGetChild(connection,HTTP_NAME_SERVER_VAR,0); JsVar *args[2] = { connection, socket }; jsiQueueObjectCallbacks(server, HTTP_NAME_ON_CONNECT, args, ((socketType&ST_TYPE_MASK)==ST_HTTP) ? 2 : 1); jsvUnLock(server); } if (hadHeaders && !jsvIsEmptyString(receiveData)) { // Keep track of how much we received (so we can close once we have it) if ((socketType&ST_TYPE_MASK)==ST_HTTP) { jsvObjectSetChildAndUnLock(connection, HTTP_NAME_RECEIVE_COUNT, jsvNewFromInteger( jsvGetIntegerAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_RECEIVE_COUNT, JSV_INTEGER)) + jsvGetStringLength(receiveData) )); } // execute 'data' callback or save data if (jswrap_stream_pushData(connection, receiveData, false)) { // clear received data jsvUnLock(receiveData); receiveData = 0; } } // if received data changed, update it if (receiveData != oldReceiveData) jsvObjectSetChild(connection,HTTP_NAME_RECEIVE_DATA,receiveData); jsvUnLock(receiveData); } } } // send data if possible JsVar *sendData = jsvObjectGetChild(socket,HTTP_NAME_SEND_DATA,0); if (sendData && !jsvIsEmptyString(sendData)) { int sent = socketSendData(net, socket, sckt, &sendData); // FIXME? checking for errors is a bit iffy. With the esp8266 network that returns // varied error codes we'd want to skip SOCKET_ERR_CLOSED and let the recv side deal // with normal closing so we don't miss the tail of what's received, but other drivers // return -1 (which is the same value) for all errors. So we rely on the check ~12 lines // down if(num>0)closeConnectionNow=false instead. if (sent < 0) { closeConnectionNow = true; error = sent; } jsvObjectSetChild(socket, HTTP_NAME_SEND_DATA, sendData); // socketSendData prob updated sendData } // only close if we want to close, have no data to send, and aren't receiving data bool wantClose = jsvGetBoolAndUnLock(jsvObjectGetChild(socket,HTTP_NAME_CLOSE,0)); if (wantClose && (!sendData || jsvIsEmptyString(sendData)) && num<=0) { bool reallyCloseNow = true; if ((socketType&ST_TYPE_MASK)==ST_HTTP) { // Check if we had a Content-Length header - if so, we need to wait until we have received that amount JsVar *headers = jsvObjectGetChild(connection,"headers",0); if (headers) { JsVarInt contentLength = jsvGetIntegerAndUnLock(jsvObjectGetChild(headers,"Content-Length",0)); JsVarInt contentReceived = jsvGetIntegerAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_RECEIVE_COUNT, 0)); if (contentLength > contentReceived) { reallyCloseNow = false; } jsvUnLock(headers); } } closeConnectionNow = reallyCloseNow; } else if (num > 0) closeConnectionNow = false; // guarantee that anything received is processed jsvUnLock(sendData); } if (closeConnectionNow) { // send out any data that we were POSTed JsVar *receiveData = jsvObjectGetChild(connection,HTTP_NAME_RECEIVE_DATA,0); bool hadHeaders = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_HAD_HEADERS,0)); if (hadHeaders && !jsvIsEmptyString(receiveData)) { // execute 'data' callback or save data jswrap_stream_pushData(connection, receiveData, true); } jsvUnLock(receiveData); // fire error events bool hadError = fireErrorEvent(error, connection, socket); // fire the close listeners JsVar *params[1] = { jsvNewFromBool(hadError) }; jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_CLOSE, params, 1); jsiQueueObjectCallbacks(socket, HTTP_NAME_ON_CLOSE, params, 1); jsvUnLock(params[0]); _socketConnectionKill(net, connection); JsVar *connectionName = jsvObjectIteratorGetKey(&it); jsvObjectIteratorNext(&it); jsvRemoveChild(arr, connectionName); jsvUnLock(connectionName); } else jsvObjectIteratorNext(&it); jsvUnLock2(connection, socket); } jsvObjectIteratorFree(&it); jsvUnLock(arr); return hadSockets; }
bool socketClientConnectionsIdle(JsNetwork *net) { char buf[64]; JsVar *arr = socketGetArray(HTTP_ARRAY_HTTP_CLIENT_CONNECTIONS,false); if (!arr) return false; bool hadSockets = false; JsvObjectIterator it; jsvObjectIteratorNew(&it, arr); while (jsvObjectIteratorHasValue(&it)) { hadSockets = true; // Get connection, socket, and socket type // For normal sockets, socket==connection, but for HTTP we split it into a request and a response JsVar *connection = jsvObjectIteratorGetValue(&it); SocketType socketType = socketGetType(connection); JsVar *socket = (socketType==ST_HTTP) ? jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0) : jsvLockAgain(connection); bool closeConnectionNow = jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CLOSENOW, false)); int sckt = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_SOCKET,0))-1; // so -1 if undefined if (sckt<0) closeConnectionNow = true; bool hadHeaders = true; if (socketType==ST_HTTP) hadHeaders = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_HAD_HEADERS,0)); JsVar *receiveData = jsvObjectGetChild(connection,HTTP_NAME_RECEIVE_DATA,0); /* We do this up here because we want to wait until we have been once * around the idle loop (=callbacks have been executed) before we run this */ if (hadHeaders) socketClientPushReceiveData(connection, socket, &receiveData); if (!closeConnectionNow) { JsVar *sendData = jsvObjectGetChild(connection,HTTP_NAME_SEND_DATA,0); // send data if possible if (sendData) { bool b = socketSendData(net, connection, sckt, &sendData); if (!b) closeConnectionNow = true; jsvObjectSetChild(connection, HTTP_NAME_SEND_DATA, sendData); // _http_send prob updated sendData } else { if (jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CLOSE, false))) closeConnectionNow = true; } // Now read data if possible int num = net->recv(net, sckt, buf, sizeof(buf)); if (num<0) { // we probably disconnected so just get rid of this closeConnectionNow = true; } else { // add it to our request string if (num>0) { if (!receiveData) { receiveData = jsvNewFromEmptyString(); jsvObjectSetChild(connection, HTTP_NAME_RECEIVE_DATA, receiveData); } if (receiveData) { // could be out of memory jsvAppendStringBuf(receiveData, buf, (size_t)num); if (socketType==ST_HTTP && !hadHeaders) { JsVar *resVar = jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0); if (httpParseHeaders(&receiveData, resVar, false)) { hadHeaders = true; jsvUnLock(jsvObjectSetChild(connection, HTTP_NAME_HAD_HEADERS, jsvNewFromBool(hadHeaders))); jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_CONNECT, &resVar, 1); } jsvUnLock(resVar); jsvObjectSetChild(connection, HTTP_NAME_RECEIVE_DATA, receiveData); } } } } jsvUnLock(sendData); } if (closeConnectionNow) { socketClientPushReceiveData(connection, socket, &receiveData); if (socketType != ST_HTTP) jsiQueueObjectCallbacks(socket, HTTP_NAME_ON_END, 0, 0); jsiQueueObjectCallbacks(socket, HTTP_NAME_ON_CLOSE, 0, 0); _socketConnectionKill(net, connection); JsVar *connectionName = jsvObjectIteratorGetKey(&it); jsvObjectIteratorNext(&it); jsvRemoveChild(arr, connectionName); jsvUnLock(connectionName); } else { jsvObjectIteratorNext(&it); } jsvUnLock(receiveData); jsvUnLock(connection); jsvUnLock(socket); } jsvUnLock(arr); return hadSockets; }
/*JSON{ "type" : "method", "class" : "Function", "name" : "bind", "generate" : "jswrap_function_bind", "params" : [ ["this","JsVar","The value to use as the 'this' argument when executing the function"], ["params","JsVarArray","Optional Default parameters that are prepended to the call"] ], "return" : ["JsVar","The 'bound' function"] } This executes the function with the supplied 'this' argument and parameters */ JsVar *jswrap_function_bind(JsVar *parent, JsVar *thisArg, JsVar *argsArray) { if (!jsvIsFunction(parent)) { jsExceptionHere(JSET_TYPEERROR, "Function.bind expects to be called on function, got %t", parent); return 0; } JsVar *fn; if (jsvIsNativeFunction(parent)) fn = jsvNewNativeFunction(parent->varData.native.ptr, parent->varData.native.argTypes); else fn = jsvNewWithFlags(jsvIsFunctionReturn(parent) ? JSV_FUNCTION_RETURN : JSV_FUNCTION); if (!fn) return 0; // Old function info JsvObjectIterator fnIt; jsvObjectIteratorNew(&fnIt, parent); // add previously bound arguments while (jsvObjectIteratorHasValue(&fnIt)) { JsVar *param = jsvObjectIteratorGetKey(&fnIt); JsVar *defaultValue = jsvObjectIteratorGetValue(&fnIt); bool wasBound = jsvIsFunctionParameter(param) && defaultValue; if (wasBound) { JsVar *newParam = jsvCopy(param); if (newParam) { // could be out of memory jsvAddName(fn, newParam); jsvUnLock(newParam); } } jsvUnLock2(param, defaultValue); if (!wasBound) break; jsvObjectIteratorNext(&fnIt); } // add bound arguments JsvObjectIterator argIt; jsvObjectIteratorNew(&argIt, argsArray); while (jsvObjectIteratorHasValue(&argIt)) { JsVar *defaultValue = jsvObjectIteratorGetValue(&argIt); bool addedParam = false; while (!addedParam && jsvObjectIteratorHasValue(&fnIt)) { JsVar *param = jsvObjectIteratorGetKey(&fnIt); if (!jsvIsFunctionParameter(param)) { jsvUnLock(param); break; } JsVar *newParam = jsvCopyNameOnly(param, false, true); jsvSetValueOfName(newParam, defaultValue); jsvAddName(fn, newParam); addedParam = true; jsvUnLock2(param, newParam); jsvObjectIteratorNext(&fnIt); } if (!addedParam) { JsVar *paramName = jsvNewFromEmptyString(); if (paramName) { jsvMakeFunctionParameter(paramName); // force this to be called a function parameter jsvSetValueOfName(paramName, defaultValue); jsvAddName(fn, paramName); jsvUnLock(paramName); } } jsvUnLock(defaultValue); jsvObjectIteratorNext(&argIt); } jsvObjectIteratorFree(&argIt); // Copy the rest of the old function's info while (jsvObjectIteratorHasValue(&fnIt)) { JsVar *param = jsvObjectIteratorGetKey(&fnIt); JsVar *newParam = jsvCopyNameOnly(param, true, true); if (newParam) { // could be out of memory jsvAddName(fn, newParam); jsvUnLock(newParam); } jsvUnLock(param); jsvObjectIteratorNext(&fnIt); } jsvObjectIteratorFree(&fnIt); // Add 'this' jsvObjectSetChild(fn, JSPARSE_FUNCTION_THIS_NAME, thisArg); // no unlock needed return fn; }
/*JSON{ "type" : "staticmethod", "class" : "NRF", "name" : "setServices", "generate" : "jswrap_nrf_bluetooth_setServices", "params" : [ ["data","JsVar","The service (and characteristics) to advertise"] ] } BETA: This only partially works at the moment Change the services and characteristics Espruino advertises. ``` NRF.setServices({ 0xBCDE : { 0xABCD : { value : "Hello", // optional maxLen : 5, // optional (otherwise is length of initial value) broadcast : false, // optional, default is false readable : true, // optional, default is false writable : true, // optional, default is false onWrite : function(evt) { // optional console.log("Got ", evt.data); } } // more characteristics allowed } // more services allowed }); ``` */ void jswrap_nrf_bluetooth_setServices(JsVar *data) { uint32_t err_code; // TODO: Reset services if (jsvIsObject(data)) { JsvObjectIterator it; jsvObjectIteratorNew(&it, data); while (jsvObjectIteratorHasValue(&it)) { ble_uuid_t ble_uuid; uint16_t service_handle; // Add the service BLE_UUID_BLE_ASSIGN(ble_uuid, jsvGetIntegerAndUnLock(jsvObjectIteratorGetKey(&it))); err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &service_handle); if (err_code) { jsExceptionHere(JSET_ERROR, "Got BLE error code %d in gatts_service_add", err_code); break; } // sd_ble_gatts_include_add ? // Now add characteristics JsVar *serviceVar = jsvObjectIteratorGetValue(&it); JsvObjectIterator serviceit; jsvObjectIteratorNew(&serviceit, serviceVar); while (jsvObjectIteratorHasValue(&serviceit)) { ble_uuid_t char_uuid; ble_gatts_char_md_t char_md; ble_gatts_attr_t attr_char_value; ble_gatts_attr_md_t attr_md; ble_gatts_char_handles_t characteristic_handles; BLE_UUID_BLE_ASSIGN(char_uuid, jsvGetIntegerAndUnLock(jsvObjectIteratorGetKey(&serviceit))); JsVar *charVar = jsvObjectIteratorGetValue(&serviceit); memset(&char_md, 0, sizeof(char_md)); if (jsvGetBoolAndUnLock(jsvObjectGetChild(charVar, "broadcast", 0))) char_md.char_props.broadcast = 1; if (jsvGetBoolAndUnLock(jsvObjectGetChild(charVar, "readable", 0))) char_md.char_props.read = 1; if (jsvGetBoolAndUnLock(jsvObjectGetChild(charVar, "writable", 0))) { char_md.char_props.write = 1; char_md.char_props.write_wo_resp = 1; } char_md.p_char_user_desc = NULL; char_md.p_char_pf = NULL; char_md.p_user_desc_md = NULL; char_md.p_cccd_md = NULL; char_md.p_sccd_md = NULL; memset(&attr_md, 0, sizeof(attr_md)); BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm); BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm); attr_md.vloc = BLE_GATTS_VLOC_STACK; attr_md.rd_auth = 0; attr_md.wr_auth = 0; attr_md.vlen = 1; // TODO: variable length? memset(&attr_char_value, 0, sizeof(attr_char_value)); attr_char_value.p_uuid = &char_uuid; attr_char_value.p_attr_md = &attr_md; attr_char_value.init_len = 0; attr_char_value.init_offs = 0; attr_char_value.p_value = 0; attr_char_value.max_len = (uint16_t)jsvGetIntegerAndUnLock(jsvObjectGetChild(charVar, "maxLen", 0)); if (attr_char_value.max_len==0) attr_char_value.max_len=1; // get initial data JsVar *charValue = jsvObjectGetChild(charVar, "value", 0); if (charValue) { JSV_GET_AS_CHAR_ARRAY(vPtr, vLen, charValue); if (vPtr && vLen) { attr_char_value.p_value = (uint8_t*)vPtr; attr_char_value.init_len = vLen; if (attr_char_value.init_len > attr_char_value.max_len) attr_char_value.max_len = attr_char_value.init_len; } } err_code = sd_ble_gatts_characteristic_add(service_handle, &char_md, &attr_char_value, &characteristic_handles); jsvUnLock(charValue); // unlock here in case we were storing data in a flat string if (err_code) { jsExceptionHere(JSET_ERROR, "Got BLE error code %d in gatts_characteristic_add", err_code); break; } // Add Write callback JsVar *writeCb = jsvObjectGetChild(charVar, "onWrite", 0); if (writeCb) { char eventName[12]; ble_handle_to_write_event_name(eventName, characteristic_handles.value_handle); jsvObjectSetChildAndUnLock(execInfo.root, eventName, writeCb); } jsvUnLock(charVar); /* We'd update the characteristic with: memset(&hvx_params, 0, sizeof(hvx_params)); hvx_params.handle = characteristic_handle.value_handle; hvx_params.p_data = p_string; hvx_params.p_len = &length; hvx_params.type = BLE_GATT_HVX_NOTIFICATION; return sd_ble_gatts_hvx(p_nus->conn_handle, &hvx_params); Maybe we could find the handle out based on characteristic UUID, rather than having to store it? */ jsvObjectIteratorNext(&serviceit); } jsvObjectIteratorFree(&serviceit); jsvUnLock(serviceVar); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); } else if (!jsvIsUndefined(data)) { jsExceptionHere(JSET_TYPEERROR, "Expecting object or undefined, got %t", data); } }