/*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" : "staticmethod", "class" : "NRF", "name" : "setAdvertising", "generate" : "jswrap_nrf_bluetooth_setAdvertising", "params" : [ ["data","JsVar","The data to advertise as an object - see below for more info"] ] } Data is of the form `{ UUID : data_as_byte_array }`. For example to return battery level at 95%, do: ``` NRF.setAdvertising({ 0x180F : [95] }); ``` Or you could report the current temperature: ``` setInterval(function() { NRF.setAdvertising({ 0x1809 : [0|E.getTemperature()] }); }, 30000); ``` */ void jswrap_nrf_bluetooth_setAdvertising(JsVar *data) { uint32_t err_code; ble_advdata_t advdata; setup_advdata(&advdata); if (jsvIsObject(data)) { ble_advdata_service_data_t *service_data = (ble_advdata_service_data_t*)alloca(jsvGetChildren(data)*sizeof(ble_advdata_service_data_t)); int n = 0; JsvObjectIterator it; jsvObjectIteratorNew(&it, data); while (jsvObjectIteratorHasValue(&it)) { service_data[n].service_uuid = jsvGetIntegerAndUnLock(jsvObjectIteratorGetKey(&it)); JsVar *v = jsvObjectIteratorGetValue(&it); JSV_GET_AS_CHAR_ARRAY(dPtr, dLen, v); jsvUnLock(v); service_data[n].data.size = dLen; service_data[n].data.p_data = dPtr; jsvObjectIteratorNext(&it); n++; } jsvObjectIteratorFree(&it); advdata.service_data_count = n; advdata.p_service_data_array = service_data; } else if (!jsvIsUndefined(data)) { jsExceptionHere(JSET_TYPEERROR, "Expecting object or undefined, got %t", data); } err_code = ble_advdata_set(&advdata, NULL); if (err_code) jsExceptionHere(JSET_ERROR, "Got BLE error code %d", err_code); }
JsVar *jswrap_arguments() { JsVar *scope = 0; if (execInfo.scopeCount>0) scope = jsvLock(execInfo.scopes[execInfo.scopeCount-1]); if (!jsvIsFunction(scope)) { jsvUnLock(scope); jsError("Can only use 'arguments' variable inside a function"); return 0; } JsVar *args = jsvNewWithFlags(JSV_ARRAY); if (!args) return 0; // out of memory JsvObjectIterator it; jsvObjectIteratorNew(&it, scope); while (jsvObjectIteratorHasElement(&it)) { JsVar *idx = jsvObjectIteratorGetKey(&it); if (jsvIsFunctionParameter(idx)) { JsVar *val = jsvSkipOneName(idx); jsvArrayPushAndUnLock(args, val); } jsvUnLock(idx); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); jsvUnLock(scope); return args; }
/*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 JsVar *eventName = jsvVarPrintf(JS_EVENT_PREFIX"%s",event); if (!eventName) return; // no memory JsVar *eventList = jsvFindChildFromVar(parent, eventName, true); jsvUnLock(eventName); 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" : "apply", "generate" : "jswrap_function_apply_or_call", "params" : [ ["this","JsVar","The value to use as the 'this' argument when executing the function"], ["args","JsVar","Optional Array of Arguments"] ], "return" : ["JsVar","The return value of executing this function"] } This executes the function with the supplied 'this' argument and parameters */ JsVar *jswrap_function_apply_or_call(JsVar *parent, JsVar *thisArg, JsVar *argsArray) { unsigned int i; JsVar **args = 0; size_t argC = 0; if (jsvIsArray(argsArray)) { argC = (size_t)jsvGetArrayLength(argsArray); if (argC>64) argC=64; // sanity args = (JsVar**)alloca((size_t)argC * sizeof(JsVar*)); for (i=0;i<argC;i++) args[i] = 0; JsvObjectIterator it; jsvObjectIteratorNew(&it, argsArray); while (jsvObjectIteratorHasValue(&it)) { JsVarInt idx = jsvGetIntegerAndUnLock(jsvObjectIteratorGetKey(&it)); if (idx>=0 && idx<(int)argC) { assert(!args[idx]); // just in case there were dups args[idx] = jsvObjectIteratorGetValue(&it); } jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); } else if (!jsvIsUndefined(argsArray)) { jsWarn("Second argument to Function.apply must be an array"); } JsVar *r = jspeFunctionCall(parent, 0, thisArg, false, (int)argC, args); for (i=0;i<argC;i++) jsvUnLock(args[i]); return r; }
JsVar *jsvIteratorGetKey(JsvIterator *it) { switch (it->type) { case JSVI_OBJECT : return jsvObjectIteratorGetKey(&it->it.obj); case JSVI_STRING : return jsvMakeIntoVariableName(jsvNewFromInteger((JsVarInt)jsvStringIteratorGetIndex(&it->it.str)), 0); // some things expect a veriable name case JSVI_ARRAYBUFFER : return jsvMakeIntoVariableName(jsvArrayBufferIteratorGetIndex(&it->it.buf), 0); // some things expect a veriable name default: assert(0); return 0; } }
static bool jsfGetJSONForObjectItWithCallback(JsvObjectIterator *it, JSONFlags flags, const char *whitespace, JSONFlags nflags, vcbprintf_callback user_callback, void *user_data, bool first) { bool needNewLine = false; size_t sinceNewLine = 0; while (jsvObjectIteratorHasValue(it) && !jspIsInterrupted()) { JsVar *index = jsvObjectIteratorGetKey(it); JsVar *item = jsvGetValueOfName(index); bool hidden = jsvIsInternalObjectKey(index) || ((flags & JSON_IGNORE_FUNCTIONS) && jsvIsFunction(item)) || ((flags&JSON_NO_UNDEFINED) && jsvIsUndefined(item)) || jsvIsGetterOrSetter(item); if (!hidden) { sinceNewLine++; if (!first) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); bool newNeedsNewLine = (flags&JSON_SOME_NEWLINES) && jsonNeedsNewLine(item); if ((flags&JSON_SOME_NEWLINES) && sinceNewLine>JSON_ITEMS_ON_LINE_OBJECT) needNewLine = true; if (flags&JSON_ALL_NEWLINES) { needNewLine = true; newNeedsNewLine = true; } if (needNewLine || newNeedsNewLine) { jsonNewLine(nflags, whitespace, user_callback, user_data); needNewLine = false; sinceNewLine = 0; } bool addQuotes = true; if (flags&JSON_DROP_QUOTES) { if (jsvIsIntegerish(index)) addQuotes = false; else if (jsvIsString(index) && jsvGetStringLength(index)<15) { char buf[16]; jsvGetString(index,buf,sizeof(buf)); if (isIDString(buf)) addQuotes=false; } } cbprintf(user_callback, user_data, addQuotes?"%q%s":"%v%s", index, (flags&JSON_PRETTY)?": ":":"); if (first) first = false; jsfGetJSONWithCallback(item, nflags, whitespace, user_callback, user_data); needNewLine = newNeedsNewLine; } jsvUnLock2(index, item); jsvObjectIteratorNext(it); } return needNewLine; }
static void handlePipeClose(JsVar *arr, JsvObjectIterator *it, JsVar* pipe) { jsiQueueObjectCallbacks(pipe, "#oncomplete", &pipe, 1); // also call 'end' if 'end' was passed as an initialisation option if (jsvGetBoolAndUnLock(jsvObjectGetChild(pipe,"end",0))) { // call destination.end if available JsVar *destination = jsvObjectGetChild(pipe,"destination",0); if (destination) { // remove our drain and close listeners. // TODO: This removes ALL listeners. Maybe we should just remove ours? jswrap_object_removeAllListeners_cstr(destination, "drain"); jswrap_object_removeAllListeners_cstr(destination, "close"); // execute the 'end' function JsVar *endFunc = jspGetNamedField(destination, "end", false); if (endFunc) { jsvUnLock(jspExecuteFunction(endFunc, destination, 0, 0)); jsvUnLock(endFunc); } // execute the 'close' function JsVar *closeFunc = jspGetNamedField(destination, "close", false); if (closeFunc) { jsvUnLock(jspExecuteFunction(closeFunc, destination, 0, 0)); jsvUnLock(closeFunc); } jsvUnLock(destination); } /* call source.close if available - probably not what node does but seems very sensible in this case. If you don't want it, set end:false */ JsVar *source = jsvObjectGetChild(pipe,"source",0); if (source) { // TODO: This removes ALL listeners. Maybe we should just remove ours? jswrap_object_removeAllListeners_cstr(source, "close"); // execute the 'close' function JsVar *closeFunc = jspGetNamedField(source, "close", false); if (closeFunc) { jsvUnLock(jspExecuteFunction(closeFunc, source, 0, 0)); jsvUnLock(closeFunc); } } jsvUnLock(source); } JsVar *idx = jsvObjectIteratorGetKey(it); jsvRemoveChild(arr,idx); jsvUnLock(idx); }
/*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); }
static void httpAppendHeaders(JsVar *string, JsVar *headerObject) { // append headers JsvObjectIterator it; jsvObjectIteratorNew(&it, headerObject); while (jsvObjectIteratorHasValue(&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"); jsvUnLock2(k, v); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); // free headers }
bool bleRemoveChild(JsVar *parent, JsVar *blevar){ bool ret = false; JsvObjectIterator it; jsvObjectIteratorNew(&it, parent); while (jsvObjectIteratorHasValue(&it)) { JsVar *child = jsvObjectIteratorGetKey(&it); JsVar *name = jsvNewFromStringVar(child, 0, 10); if(jsvIsEqual(name,blevar)){ jsvRemoveChild(parent,child); ret = true; } jsvUnLock(child); jsvUnLock(name); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); return ret; }
/*JSON{ "type":"staticmethod", "class" : "Modules", "name" : "getCached", "description" : "Return an array of module names that have been cached", "generate" : "jswrap_modules_getCached", "return" : ["JsVar", "An array of module names"] }*/ JsVar *jswrap_modules_getCached() { JsVar *arr = jsvNewWithFlags(JSV_ARRAY); if (!arr) return 0; // out of memory JsVar *moduleList = jswrap_modules_getModuleList(); if (!moduleList) return arr; // out of memory JsvObjectIterator it; jsvObjectIteratorNew(&it, moduleList); while (jsvObjectIteratorHasValue(&it)) { JsVar *idx = jsvObjectIteratorGetKey(&it); JsVar *idxCopy = jsvCopyNameOnly(idx, false, false); jsvArrayPushAndUnLock(arr, idxCopy); jsvUnLock(idx); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); jsvUnLock(moduleList); return 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" : "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); }
/*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; } }
/*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; }
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; }
static void handlePipeClose(JsVar *arr, JsvObjectIterator *it, JsVar* pipe) { jsiQueueObjectCallbacks(pipe, "#oncomplete", &pipe, 1); // Check the source to see if there was more data... It may not be a stream, // but if it is and it has data it should have a a STREAM_BUFFER_NAME field JsVar *source = jsvObjectGetChild(pipe,"source",0); JsVar *destination = jsvObjectGetChild(pipe,"destination",0); if (source && destination) { JsVar *buffer = jsvObjectGetChild(source, STREAM_BUFFER_NAME, 0); if (buffer && jsvGetStringLength(buffer)) { jsvObjectSetChild(source, STREAM_BUFFER_NAME, 0); // remove outstanding data /* call write fn - we ignore drain/etc here because the source has just closed and we want to get this sorted quickly */ JsVar *writeFunc = jspGetNamedField(destination, "write", false); if (jsvIsFunction(writeFunc)) // do the objects have the necessary methods on them? jsvUnLock(jspExecuteFunction(writeFunc, destination, 1, &buffer)); jsvUnLock(writeFunc); // update position JsVar *position = jsvObjectGetChild(pipe,"position",0); jsvSetInteger(position, jsvGetInteger(position) + (JsVarInt)jsvGetStringLength(buffer)); jsvUnLock(position); } jsvUnLock(buffer); } // also call 'end' if 'end' was passed as an initialisation option if (jsvGetBoolAndUnLock(jsvObjectGetChild(pipe,"end",0))) { // call destination.end if available if (destination) { // remove our drain and close listeners. // TODO: This removes ALL listeners. Maybe we should just remove ours? jswrap_object_removeAllListeners_cstr(destination, "drain"); jswrap_object_removeAllListeners_cstr(destination, "close"); // execute the 'end' function JsVar *endFunc = jspGetNamedField(destination, "end", false); if (endFunc) { jsvUnLock(jspExecuteFunction(endFunc, destination, 0, 0)); jsvUnLock(endFunc); } // execute the 'close' function JsVar *closeFunc = jspGetNamedField(destination, "close", false); if (closeFunc) { jsvUnLock(jspExecuteFunction(closeFunc, destination, 0, 0)); jsvUnLock(closeFunc); } } /* call source.close if available - probably not what node does but seems very sensible in this case. If you don't want it, set end:false */ if (source) { // TODO: This removes ALL listeners. Maybe we should just remove ours? jswrap_object_removeAllListeners_cstr(source, "close"); // execute the 'close' function JsVar *closeFunc = jspGetNamedField(source, "close", false); if (closeFunc) { jsvUnLock(jspExecuteFunction(closeFunc, source, 0, 0)); jsvUnLock(closeFunc); } } } jsvUnLock(source); jsvUnLock(destination); JsVar *idx = jsvObjectIteratorGetKey(it); jsvRemoveChild(arr,idx); jsvUnLock(idx); }
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; } }
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; }
/* 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 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; }
/*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; }
/*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); } }