void serverResponseWrite(JsVar *httpServerResponseVar, JsVar *data) { // Append data to sendData JsVar *sendData = jsvObjectGetChild(httpServerResponseVar, HTTP_NAME_SEND_DATA, 0); if (!sendData) { // no sendData, so no headers - add them! JsVar *sendHeaders = jsvObjectGetChild(httpServerResponseVar, HTTP_NAME_HEADERS, 0); if (sendHeaders) { sendData = jsvVarPrintf("HTTP/1.0 %d OK\r\nServer: Espruino "JS_VERSION"\r\n", jsvGetIntegerAndUnLock(jsvObjectGetChild(httpServerResponseVar, HTTP_NAME_CODE, 0))); httpAppendHeaders(sendData, sendHeaders); jsvObjectSetChild(httpServerResponseVar, HTTP_NAME_HEADERS, 0); jsvUnLock(sendHeaders); // finally add ending newline jsvAppendString(sendData, "\r\n"); } else if (!jsvIsUndefined(data)) { // we have already sent headers, but want to send more sendData = jsvNewFromEmptyString(); } jsvObjectSetChild(httpServerResponseVar, HTTP_NAME_SEND_DATA, sendData); } if (sendData && !jsvIsUndefined(data)) { JsVar *s = jsvAsString(data, false); if (s) jsvAppendStringVarComplete(sendData,s); jsvUnLock(s); } jsvUnLock(sendData); }
/*JSON{ "type" : "method", "class" : "String", "name" : "split", "generate" : "jswrap_string_split", "params" : [ ["separator","JsVar","The start character index"] ], "return" : ["JsVar","Part of this string from start for len characters"] } Return an array made by splitting this string up by the separator. eg. ```'1,2,3'.split(',')==[1,2,3]``` */ JsVar *jswrap_string_split(JsVar *parent, JsVar *split) { JsVar *array = jsvNewWithFlags(JSV_ARRAY); if (!array) return 0; // out of memory if (jsvIsUndefined(split)) { jsvArrayPush(array, parent); return array; } split = jsvAsString(split, false); int idx, last = 0; int splitlen = jsvIsUndefined(split) ? 0 : (int)jsvGetStringLength(split); int l = (int)jsvGetStringLength(parent) + 1 - splitlen; for (idx=0;idx<=l;idx++) { if (splitlen==0 && idx==0) continue; // special case for where split string is "" if (idx==l || splitlen==0 || jsvCompareString(parent, split, (size_t)idx, 0, true)==0) { if (idx==l) { idx=l+splitlen; // if the last element, do to the end of the string if (splitlen==0) break; } JsVar *part = jsvNewFromStringVar(parent, (size_t)last, (size_t)(idx-last)); if (!part) break; // out of memory jsvArrayPush(array, part); jsvUnLock(part); last = idx+splitlen; } } jsvUnLock(split); return array; }
/*JSON{ "type" : "staticmethod", "class" : "crypto", "name" : "PBKDF2", "generate" : "jswrap_crypto_PBKDF2", "params" : [ ["passphrase","JsVar","Passphrase"], ["salt","JsVar","Salt for turning passphrase into a key"], ["options","JsVar","Object of Options, `{ keySize: 8 (in 32 bit words), iterations: 10, hasher: 'SHA1'/'SHA224'/'SHA256'/'SHA384'/'SHA512' }`"] ], "return" : ["JsVar","Returns an ArrayBuffer"], "return_object" : "ArrayBuffer", "ifdef" : "USE_TLS" } Password-Based Key Derivation Function 2 algorithm, using SHA512 */ JsVar *jswrap_crypto_PBKDF2(JsVar *passphrase, JsVar *salt, JsVar *options) { int iterations = 1; int keySize = 128/32; mbedtls_md_type_t hasher = MBEDTLS_MD_SHA1; if (jsvIsObject(options)) { keySize = jsvGetIntegerAndUnLock(jsvObjectGetChild(options, "keySize", 0)); if (keySize<=0) keySize=128/32; iterations = jsvGetIntegerAndUnLock(jsvObjectGetChild(options, "iterations", 0)); if (iterations<1) iterations = 1; JsVar *hashVar = jsvObjectGetChild(options, "hasher", 0); if (!jsvIsUndefined(hashVar)) hasher = jswrap_crypto_getHasher(hashVar); jsvUnLock(hashVar); } else if (!jsvIsUndefined(options)) jsError("Options should be an object or undefined, got %t", options); if (hasher == MBEDTLS_MD_NONE) return 0; // already shown an error JSV_GET_AS_CHAR_ARRAY(passPtr, passLen, passphrase); if (!passPtr) return 0; JSV_GET_AS_CHAR_ARRAY(saltPtr, saltLen, salt); if (!saltPtr) return 0; int err; mbedtls_md_context_t ctx; mbedtls_md_init( &ctx ); err = mbedtls_md_setup( &ctx, mbedtls_md_info_from_type( hasher ), 1 ); assert(err==0); char *keyPtr = 0; JsVar *keyArr = jsvNewArrayBufferWithPtr((unsigned)keySize*4, &keyPtr); if (!keyPtr) { jsError("Not enough memory for result"); return 0; } err = mbedtls_pkcs5_pbkdf2_hmac( &ctx, (unsigned char*)passPtr, passLen, (unsigned char*)saltPtr, saltLen, (unsigned)iterations, (unsigned)keySize*4, (unsigned char*)keyPtr ); mbedtls_md_free( &ctx ); if (!err) { return keyArr; } else { jswrap_crypto_error(err); jsvUnLock(keyArr); return 0; } }
/*JSON{ "type" : "staticmethod", "class" : "Trig", "name" : "setup", "generate" : "jswrap_trig_setup", "params" : [ ["pin","pin","The pin to use for triggering"], ["options","JsVar","Additional options as an object. defaults are: ```{teethTotal:60,teethMissing:2,minRPM:30,keyPosition:0}```"] ] } Initialise the trigger class */ void jswrap_trig_setup(Pin pin, JsVar *options) { if (!jshIsPinValid(pin)) { jsError("Invalid pin supplied as an argument to Trig.setup"); return; } TriggerStruct *trig = &mainTrigger; // static info trig->teethMissing = 2; trig->teethTotal = 60; trig->keyPosition = 0; JsVarFloat minRPM = 30; if (jsvIsObject(options)) { JsVar *v; v = jsvObjectGetChild(options, "teethMissing", 0); if (!jsvIsUndefined(v)) trig->teethMissing = (unsigned char)jsvGetInteger(v); jsvUnLock(v); v = jsvObjectGetChild(options, "teethTotal", 0); if (!jsvIsUndefined(v)) trig->teethTotal = (unsigned char)jsvGetInteger(v); jsvUnLock(v); v = jsvObjectGetChild(options, "minRPM", 0); if (!jsvIsUndefined(v)) minRPM = jsvGetFloat(v); jsvUnLock(v); v = jsvObjectGetChild(options, "keyPosition", 0); if (!jsvIsUndefined(v)) trig->keyPosition = jsvGetFloat(v); jsvUnLock(v); } trig->maxTooth = (unsigned int)jshGetTimeFromMilliseconds(60000 / (JsVarFloat)(trig->teethTotal * minRPM)); // semi-static info int i; for (i=0;i<TRIGGER_TRIGGERS_COUNT;i++) { trig->triggers[i].tooth = TRIGGERPOINT_TOOTH_DISABLE; trig->triggers[i].newTooth = TRIGGERPOINT_TOOTH_DISABLE; } // dynamic info trig->lastTime = jshGetSystemTime(); trig->avrTrigger = (unsigned int)jshGetTimeFromMilliseconds(10); // average time for a trigger pulse trig->avrTooth = (unsigned int)jshGetTimeFromMilliseconds(10); // average time for a tooth trig->currTooth = 0; trig->teethSinceStart = 0; trig->wrongTriggerTeeth = 0; // finally set up the watch! if (jshIsPinValid(trig->sensorPin)) jshPinWatch(trig->sensorPin, false); trig->sensorPin = pin; jshPinWatch(trig->sensorPin, true); }
/*JSON{ "type" : "constructor", "class" : "Number", "name" : "Number", "generate" : "jswrap_number_constructor", "params" : [ ["value","JsVarArray","A single value to be converted to a number"] ], "return" : ["JsVar","A Number object"] } Creates a number */ JsVar *jswrap_number_constructor(JsVar *args) { if (jsvGetArrayLength(args)==0) return jsvNewFromInteger(0); JsVar *val = jsvGetArrayItem(args, 0); JsVar *result = 0; if (jsvIsArray(val)) { JsVarInt l = jsvGetArrayLength(val); if (l==0) result = jsvNewFromInteger(0); else if (l==1) { JsVar *n = jsvGetArrayItem(val, 0); if (jsvIsString(n) && jsvIsEmptyString(n)) result = jsvNewFromInteger(0); else if (!jsvIsBoolean(n)) result=jsvAsNumber(n); jsvUnLock(n); } // else NaN } else if (jsvIsUndefined(val) || jsvIsObject(val)) result = 0; else { if (jsvIsString(val) && jsvIsEmptyString(val)) { result = jsvNewFromInteger(0); } else result = jsvAsNumber(val); } jsvUnLock(val); if (result) return result; return jsvNewFromFloat(NAN); }
/*JSON{ "type":"method", "class": "Function", "name" : "apply", "description" : ["This executes the function with the supplied 'this' argument and parameters"], "generate" : "jswrap_function_apply", "params" : [ [ "this", "JsVar", "The value to use as the 'this' argument when executing the function"], [ "args", "JsVar", "Optional Array of Aruments"] ], "return" : [ "JsVar", "The return value of executing this function" ] }*/ JsVar *jswrap_function_apply(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; JsvArrayIterator it; jsvArrayIteratorNew(&it, argsArray); while (jsvArrayIteratorHasElement(&it)) { JsVarInt idx = jsvGetIntegerAndUnLock(jsvArrayIteratorGetIndex(&it)); if (idx>=0 && idx<(int)argC) { assert(!args[idx]); // just in case there were dups args[idx] = jsvArrayIteratorGetElement(&it); } jsvArrayIteratorNext(&it); } jsvArrayIteratorFree(&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; }
/*JSON{ "type" : "function", "name" : "trace", "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_interface_trace", "params" : [ ["root","JsVar","The symbol to output (optional). If nothing is specified, everything will be output"] ] } Output debugging information */ void jswrap_interface_trace(JsVar *root) { if (jsvIsUndefined(root)) { jsvTrace(execInfo.root, 0); } else { jsvTrace(root, 0); } }
/*JSON{ "type" : "function", "name" : "clearTimeout", "generate" : "jswrap_interface_clearTimeout", "params" : [ ["id","JsVar","The id returned by a previous call to setTimeout"] ] } Clear the Timeout that was created with setTimeout, for example: ```var id = setTimeout(function () { print('foo'); }, 1000);``` ```clearTimeout(id);``` If no argument is supplied, all timers and intervals are stopped */ void _jswrap_interface_clearTimeoutOrInterval(JsVar *idVar, bool isTimeout) { JsVar *timerArrayPtr = jsvLock(timerArray); if (jsvIsUndefined(idVar)) { /* Delete all timers EXCEPT those with a 'watch' field, as those were generated by jsinteractive.c for debouncing watches */ JsvObjectIterator it; jsvObjectIteratorNew(&it, timerArrayPtr); while (jsvObjectIteratorHasValue(&it)) { JsVar *timerPtr = jsvObjectIteratorGetValue(&it); JsVar *watchPtr = jsvObjectGetChild(timerPtr, "watch", 0); if (!watchPtr) jsvObjectIteratorRemoveAndGotoNext(&it, timerArrayPtr); else jsvObjectIteratorNext(&it); jsvUnLock2(watchPtr, timerPtr); } jsvObjectIteratorFree(&it); } else { JsVar *child = jsvIsBasic(idVar) ? jsvFindChildFromVar(timerArrayPtr, idVar, false) : 0; if (child) { JsVar *timerArrayPtr = jsvLock(timerArray); jsvRemoveChild(timerArrayPtr, child); jsvUnLock2(child, timerArrayPtr); } else { if (isTimeout) jsExceptionHere(JSET_ERROR, "Unknown Timeout"); else jsExceptionHere(JSET_ERROR, "Unknown Interval"); } } jsvUnLock(timerArrayPtr); jsiTimersChanged(); // mark timers as changed }
/*JSON{ "type":"method", "class": "Serial", "name" : "setup", "description" : ["Setup this Serial port with the given baud rate and options.", "If not specified in options, the default pins are used (usually the lowest numbered pins on the lowest port that supports this peripheral)"], "generate" : "jswrap_serial_setup", "params" : [ [ "baudrate", "JsVar", "The baud rate - the default is 9600"], [ "options", "JsVar", ["An optional structure containing extra information on initialising the serial port.", "```{rx:pin,tx:pin,bytesize:8,parity:null/'none'/'o'/'odd'/'e'/'even',stopbits:1}```", "You can find out which pins to use by looking at [your board's reference page](#boards) and searching for pins with the `UART`/`USART` markers.", "Note that even after changing the RX and TX pins, if you have called setup before then the previous RX and TX pins will still be connected to the Serial port as well - until you set them to something else using digitalWrite" ] ] ] }*/ void jswrap_serial_setup(JsVar *parent, JsVar *baud, JsVar *options) { IOEventFlags device = jsiGetDeviceFromClass(parent); if (!DEVICE_IS_USART(device)) return; JshUSARTInfo inf; jshUSARTInitInfo(&inf); if (!jsvIsUndefined(baud)) { int b = (int)jsvGetInteger(baud); if (b<=100 || b > 10000000) jsExceptionHere(JSET_ERROR, "Invalid baud rate specified"); else inf.baudRate = b; } if (jsvIsObject(options)) { inf.pinRX = jshGetPinFromVarAndUnLock(jsvObjectGetChild(options, "rx", 0)); inf.pinTX = jshGetPinFromVarAndUnLock(jsvObjectGetChild(options, "tx", 0)); JsVar *v; v = jsvObjectGetChild(options, "bytesize", 0); if (jsvIsInt(v)) inf.bytesize = (unsigned char)jsvGetInteger(v); jsvUnLock(v); inf.parity = 0; v = jsvObjectGetChild(options, "parity", 0); if(jsvIsString(v)) { if(jsvIsStringEqual(v, "o") || jsvIsStringEqual(v, "odd")) inf.parity = 1; else if(jsvIsStringEqual(v, "e") || jsvIsStringEqual(v, "even")) inf.parity = 2; } else if(jsvIsInt(v)) { inf.parity = (unsigned char)jsvGetInteger(v); } jsvUnLock(v); if (inf.parity>2) { jsExceptionHere(JSET_ERROR, "Invalid parity %d", inf.parity); return; } v = jsvObjectGetChild(options, "stopbits", 0); if (jsvIsInt(v)) inf.stopbits = (unsigned char)jsvGetInteger(v); jsvUnLock(v); } jshUSARTSetup(device, &inf); // Set baud rate in object, so we can initialise it on startup if (inf.baudRate != DEFAULT_BAUD_RATE) { jsvUnLock(jsvObjectSetChild(parent, USART_BAUDRATE_NAME, jsvNewFromInteger(inf.baudRate))); } else jsvRemoveNamedChild(parent, USART_BAUDRATE_NAME); // Do the same for options if (options) jsvUnLock(jsvSetNamedChild(parent, options, DEVICE_OPTIONS_NAME)); else jsvRemoveNamedChild(parent, DEVICE_OPTIONS_NAME); }
/*JSON{ "type":"function", "name" : "trace", "ifndef" : "SAVE_ON_FLASH", "description" : "Output debugging information", "generate" : "jswrap_interface_trace", "params" : [ [ "root", "JsVarName", "The symbol to output (optional). If nothing is specified, everything will be output"] ] }*/ void jswrap_interface_trace(JsVar *root) { if (jsvIsUndefined(root)) { jsvTrace(jsvGetRef(jsiGetParser()->root), 0); } else { jsvTrace(jsvGetRef(root), 0); } }
/*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; unsigned int argC = 0; if (jsvIsIterable(argsArray)) { argC = (unsigned int)jsvGetLength(argsArray); if (argC>64) { jsExceptionHere(JSET_ERROR, "Array passed to Function.apply is too big! Maximum 64 arguments, got %d", argC); return 0; } args = (JsVar**)alloca((size_t)argC * sizeof(JsVar*)); for (i=0;i<argC;i++) args[i] = 0; // TODO: Use jsvGetArrayItems? JsvIterator it; jsvIteratorNew(&it, argsArray); while (jsvIteratorHasElement(&it)) { JsVarInt idx = jsvGetIntegerAndUnLock(jsvIteratorGetKey(&it)); if (idx>=0 && idx<(int)argC) { assert(!args[idx]); // just in case there were dups args[idx] = jsvIteratorGetValue(&it); } jsvIteratorNext(&it); } jsvIteratorFree(&it); } else if (!jsvIsUndefined(argsArray)) { jsExceptionHere(JSET_ERROR, "Second argument to Function.apply must be iterable, got %t", argsArray); return 0; } JsVar *r = jspeFunctionCall(parent, 0, thisArg, false, (int)argC, args); jsvUnLockMany(argC, args); return r; }
/*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); }
/*JSON{ "type" : "method", "class" : "Socket", "name" : "end", "generate" : "jswrap_net_socket_end", "params" : [ ["data","JsVar","A string containing data to send"] ] } Close this socket - optional data to append as an argument */ void jswrap_net_socket_end(JsVar *parent, JsVar *data) { JsNetwork net; if (!networkGetFromVarIfOnline(&net)) return; if (!jsvIsUndefined(data)) jswrap_net_socket_write(parent, data); clientRequestEnd(&net, parent); networkFree(&net); }
/*JSON{ "type" : "staticmethod", "class" : "fs", "name" : "pipe", "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_pipe", "params" : [ ["source","JsVar","The source file/stream that will send content."], ["destination","JsVar","The destination file/stream that will receive content from the source."], ["options","JsVar",["An optional object `{ chunkSize : int=64, end : bool=true, complete : function }`","chunkSize : The amount of data to pipe from source to destination at a time","complete : a function to call when the pipe activity is complete","end : call the 'end' function on the destination when the source is finished"]] ] }*/ void jswrap_pipe(JsVar* source, JsVar* dest, JsVar* options) { if (!source || !dest) return; JsVar *pipe = jspNewObject(0, "Pipe"); JsVar *arr = pipeGetArray(true); JsVar* position = jsvNewFromInteger(0); if (pipe && arr && position) {// out of memory? JsVar *readFunc = jspGetNamedField(source, "read", false); JsVar *writeFunc = jspGetNamedField(dest, "write", false); if(jsvIsFunction(readFunc)) { if(jsvIsFunction(writeFunc)) { JsVarInt chunkSize = 64; bool callEnd = true; // parse Options Object if (jsvIsObject(options)) { JsVar *c; c = jsvObjectGetChild(options, "complete", false); if (c) { jsvObjectSetChild(pipe, "#oncomplete", c); jsvUnLock(c); } c = jsvObjectGetChild(options, "end", false); if (c) callEnd = jsvGetBoolAndUnLock(c); c = jsvObjectGetChild(options, "chunkSize", false); if (c) { if (jsvIsNumeric(c) && jsvGetInteger(c)>0) chunkSize = jsvGetInteger(c); else jsWarn("chunkSize must be an integer > 0"); jsvUnLock(c); } } else if (!jsvIsUndefined(options)) { jsWarn("'options' must be an object, or undefined"); } // set up our event listeners jswrap_object_addEventListener(source, "close", jswrap_pipe_src_close_listener, JSWAT_VOID | (JSWAT_JSVAR << (JSWAT_BITS*1))); jswrap_object_addEventListener(dest, "drain", jswrap_pipe_drain_listener, JSWAT_VOID | (JSWAT_JSVAR << (JSWAT_BITS*1))); jswrap_object_addEventListener(dest, "close", jswrap_pipe_dst_close_listener, JSWAT_VOID | (JSWAT_JSVAR << (JSWAT_BITS*1))); // set up the rest of the pipe jsvUnLock(jsvObjectSetChild(pipe, "chunkSize", jsvNewFromInteger(chunkSize))); jsvUnLock(jsvObjectSetChild(pipe, "end", jsvNewFromBool(callEnd))); jsvUnLock(jsvAddNamedChild(pipe, position, "position")); jsvUnLock(jsvAddNamedChild(pipe, source, "source")); jsvUnLock(jsvAddNamedChild(pipe, dest, "destination")); // add the pipe to our list jsvArrayPush(arr, pipe); } else { jsExceptionHere(JSET_ERROR, "Destination object does not implement the required write(buffer, length, position) method."); } } else { jsExceptionHere(JSET_ERROR, "Source object does not implement the required read(buffer, length, position) method."); } jsvUnLock(readFunc); jsvUnLock(writeFunc); } jsvUnLock(arr); jsvUnLock(pipe); jsvUnLock(position); }
void serverResponseWriteHead(JsVar *httpServerResponseVar, int statusCode, JsVar *headers) { if (!jsvIsUndefined(headers) && !jsvIsObject(headers)) { jsError("Headers sent to writeHead should be an object"); return; } jsvUnLock(jsvObjectSetChild(httpServerResponseVar, HTTP_NAME_CODE, jsvNewFromInteger(statusCode))); JsVar *sendHeaders = jsvObjectGetChild(httpServerResponseVar, HTTP_NAME_HEADERS, 0); if (sendHeaders) { if (!jsvIsUndefined(headers)) { jsvObjectSetChild(httpServerResponseVar, HTTP_NAME_HEADERS, headers); } jsvUnLock(sendHeaders); } else { // headers are set to 0 when they are sent jsError("Headers have already been sent"); } }
/*JSON{ "type":"method", "class": "String", "name" : "substr", "generate" : "jswrap_string_substr", "params" : [ [ "start", "int", "The start character index"], [ "len", "JsVar", "The number of characters"] ], "return" : ["JsVar", "Part of this string from start for len characters"] }*/ JsVar *jswrap_string_substr(JsVar *parent, JsVarInt pStart, JsVar *vLen) { JsVar *res; JsVarInt pLen = jsvIsUndefined(vLen) ? JSVAPPENDSTRINGVAR_MAXLENGTH : (int)jsvGetInteger(vLen); if (pLen<0) pLen=0; res = jsvNewWithFlags(JSV_STRING); if (!res) return 0; // out of memory jsvAppendStringVar(res, parent, (int)pStart, (int)pLen); return res; }
/*JSON{ "type":"method", "class": "Array", "name" : "join", "description" : "Join all elements of this array together into one string, using 'separator' between them. eg. ```[1,2,3].join(' ')=='1 2 3'```", "generate" : "jswrap_array_join", "params" : [ [ "separator", "JsVar", "The separator"] ], "return" : ["JsVar", "A String representing the Joined array"] }*/ JsVar *jswrap_array_join(JsVar *parent, JsVar *filler) { if (jsvIsUndefined(filler)) filler = jsvNewFromString(","); // the default it seems else filler = jsvAsString(filler, false); if (!filler) return 0; // out of memory JsVar *str = jsvArrayJoin(parent, filler); jsvUnLock(filler); return str; }
static void jswrap_waveform_start(JsVar *waveform, Pin pin, JsVarFloat freq, JsVar *options, bool isWriting) { bool running = jsvGetBoolAndUnLock(jsvObjectGetChild(waveform, "running", 0)); if (running) { jsExceptionHere(JSET_ERROR, "Waveform is already running"); return; } if (!jshIsPinValid(pin)) { jsExceptionHere(JSET_ERROR, "Invalid pin"); return; } if (!isfinite(freq) || freq<0.001) { jsExceptionHere(JSET_ERROR, "Frequency must be above 0.001Hz"); return; } JsSysTime startTime = jshGetSystemTime(); bool repeat = false; if (jsvIsObject(options)) { JsVarFloat t = jsvGetFloatAndUnLock(jsvObjectGetChild(options, "time", 0)); if (isfinite(t) && t>0) startTime = jshGetTimeFromMilliseconds(t*1000); repeat = jsvGetBoolAndUnLock(jsvObjectGetChild(options, "repeat", 0)); } else if (!jsvIsUndefined(options)) { jsExceptionHere(JSET_ERROR, "Expecting options to be undefined or an Object, not %t", options); } bool is16Bit = false; JsVar *buffer = jswrap_waveform_getBuffer(waveform,0, &is16Bit); JsVar *buffer2 = jswrap_waveform_getBuffer(waveform,1,0); UtilTimerEventType eventType; if (is16Bit) { eventType = isWriting ? UET_WRITE_SHORT : UET_READ_SHORT; } else { eventType = isWriting ? UET_WRITE_BYTE : UET_READ_BYTE; } // And finally set it up if (!jstStartSignal(startTime, jshGetTimeFromMilliseconds(1000.0 / freq), pin, buffer, repeat?(buffer2?buffer2:buffer):0, eventType)) jsWarn("Unable to schedule a timer"); jsvUnLock(buffer); jsvUnLock(buffer2); jsvUnLock(jsvObjectSetChild(waveform, "running", jsvNewFromBool(true))); jsvUnLock(jsvObjectSetChild(waveform, "freq", jsvNewFromFloat(freq))); // Add to our list of active waveforms JsVar *waveforms = jsvObjectGetChild(execInfo.hiddenRoot, JSI_WAVEFORM_NAME, JSV_ARRAY); if (waveforms) { jsvArrayPush(waveforms, waveform); jsvUnLock(waveforms); } }
/*JSON{ "type" : "method", "class" : "String", "name" : "substr", "generate" : "jswrap_string_substr", "params" : [ ["start","int","The start character index"], ["len","JsVar","The number of characters"] ], "return" : ["JsVar","Part of this string from start for len characters"] }*/ JsVar *jswrap_string_substr(JsVar *parent, JsVarInt pStart, JsVar *vLen) { JsVar *res; JsVarInt pLen = jsvIsUndefined(vLen) ? JSVAPPENDSTRINGVAR_MAXLENGTH : (int)jsvGetInteger(vLen); if (pLen<0) pLen = 0; if (pStart<0) pStart += (JsVarInt)jsvGetStringLength(parent); if (pStart<0) pStart = 0; res = jsvNewFromEmptyString(); if (!res) return 0; // out of memory jsvAppendStringVar(res, parent, (size_t)pStart, (size_t)pLen); return res; }
// Connect this connection/socket void clientRequestConnect(JsNetwork *net, JsVar *httpClientReqVar) { // Have we already connected? If so, don't go further if (jsvGetIntegerAndUnLock(jsvObjectGetChild(httpClientReqVar, HTTP_NAME_SOCKET, 0))>0) return; SocketType socketType = socketGetType(httpClientReqVar); JsVar *options = jsvObjectGetChild(httpClientReqVar, HTTP_NAME_OPTIONS_VAR, false); unsigned short port = (unsigned short)jsvGetIntegerAndUnLock(jsvObjectGetChild(options, "port", 0)); char hostName[128]; JsVar *hostNameVar = jsvObjectGetChild(options, "host", 0); if (jsvIsUndefined(hostNameVar)) strncpy(hostName, "localhost", sizeof(hostName)); else jsvGetString(hostNameVar, hostName, sizeof(hostName)); jsvUnLock(hostNameVar); uint32_t host_addr = 0; networkGetHostByName(net, hostName, &host_addr); if(!host_addr) { jsError("Unable to locate host\n"); // As this is already in the list of connections, an error will be thrown on idle anyway jsvObjectSetChildAndUnLock(httpClientReqVar, HTTP_NAME_CLOSENOW, jsvNewFromBool(true)); jsvUnLock(options); netCheckError(net); return; } NetCreateFlags flags = NCF_NORMAL; #ifdef USE_TLS if (socketType & ST_TLS) { flags |= NCF_TLS; if (port==0) port = 443; } #endif if (port==0) port = 80; int sckt = netCreateSocket(net, host_addr, port, flags, options); if (sckt<0) { jsError("Unable to create socket\n"); // As this is already in the list of connections, an error will be thrown on idle anyway jsvObjectSetChildAndUnLock(httpClientReqVar, HTTP_NAME_CLOSENOW, jsvNewFromBool(true)); } else { jsvObjectSetChildAndUnLock(httpClientReqVar, HTTP_NAME_SOCKET, jsvNewFromInteger(sckt+1)); } jsvUnLock(options); netCheckError(net); }
/*JSON{ "type" : "function", "name" : "trace", "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_interface_trace", "params" : [ ["root","JsVar","The symbol to output (optional). If nothing is specified, everything will be output"] ] } Output debugging information Note: This is not included on boards with low amounts of flash memory, or the Espruino board. */ void jswrap_interface_trace(JsVar *root) { #ifdef ESPRUINOBOARD // leave this function out on espruino board - we need to save as much flash as possible jsiConsolePrintf("Trace not included on this board"); #else if (jsvIsUndefined(root)) { jsvTrace(execInfo.root, 0); } else { jsvTrace(root, 0); } #endif }
/*JSON{ "type" : "method", "class" : "ArrayBufferView", "name" : "map", "generate" : "jswrap_arraybufferview_map", "params" : [ ["function","JsVar","Function used to map one item to another"], ["thisArg","JsVar","if specified, the function is called with 'this' set to thisArg (optional)"] ], "return" : ["JsVar","An array containing the results"], "return_object" : "ArrayBufferView" } Return an array which is made from the following: ```A.map(function) = [function(A[0]), function(A[1]), ...]``` **Note:** This returns an ArrayBuffer of the same type it was called on. To get an Array, use `Array.prototype.map` */ JsVar *jswrap_arraybufferview_map(JsVar *parent, JsVar *funcVar, JsVar *thisVar) { if (!jsvIsArrayBuffer(parent)) { jsExceptionHere(JSET_ERROR, "ArrayBufferView.map can only be called on an ArrayBufferView"); return 0; } if (!jsvIsFunction(funcVar)) { jsExceptionHere(JSET_ERROR, "ArrayBufferView.map's first argument should be a function"); return 0; } if (!jsvIsUndefined(thisVar) && !jsvIsObject(thisVar)) { jsExceptionHere(JSET_ERROR, "ArrayBufferView.map's second argument should be undefined, or an object"); return 0; } // create ArrayBuffer result JsVarDataArrayBufferViewType arrayBufferType = parent->varData.arraybuffer.type; JsVar *arrayBufferLength = jsvNewFromInteger((JsVarInt)jsvGetArrayBufferLength(parent)); JsVar *array = jswrap_typedarray_constructor(arrayBufferType, arrayBufferLength, 0, 0); jsvUnLock(arrayBufferLength); if (!array) return 0; // now iterate JsvIterator it; // TODO: if we really are limited to ArrayBuffers, this could be an ArrayBufferIterator. jsvIteratorNew(&it, parent); JsvArrayBufferIterator itdst; jsvArrayBufferIteratorNew(&itdst, array, 0); while (jsvIteratorHasElement(&it)) { JsVar *index = jsvIteratorGetKey(&it); if (jsvIsInt(index)) { JsVarInt idxValue = jsvGetInteger(index); JsVar *args[3], *mapped; args[0] = jsvIteratorGetValue(&it); args[1] = jsvNewFromInteger(idxValue); // child is a variable name, create a new variable for the index args[2] = parent; mapped = jspeFunctionCall(funcVar, 0, thisVar, false, 3, args); jsvUnLock(args[0]); jsvUnLock(args[1]); if (mapped) { jsvArrayBufferIteratorSetValue(&itdst, mapped); jsvUnLock(mapped); } } jsvUnLock(index); jsvIteratorNext(&it); jsvArrayBufferIteratorNext(&itdst); } jsvIteratorFree(&it); jsvArrayBufferIteratorFree(&itdst); return array; }
/*JSON{ "type" : "method", "class" : "String", "name" : "match", "generate" : "jswrap_string_match", "params" : [ ["subStr","JsVar","Substring or RegExp to match"] ], "return" : ["JsVar","This match array"] } Matches `subStr` occurrence in the string. */ JsVar *jswrap_string_match(JsVar *parent, JsVar *subStr) { if (!jsvIsString(parent)) return 0; if (jsvIsUndefined(subStr)) return 0; #ifndef SAVE_ON_FLASH // Use RegExp if one is passed in if (jsvIsInstanceOf(subStr, "RegExp")) { jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(0)); JsVar *match; match = jswrap_regexp_exec(subStr, parent); if (!jswrap_regexp_hasFlag(subStr,'g')) { return match; } // global JsVar *array = jsvNewEmptyArray(); if (!array) return 0; // out of memory while (match && !jsvIsNull(match)) { // get info about match JsVar *matchStr = jsvGetArrayItem(match,0); JsVarInt idx = jsvGetIntegerAndUnLock(jsvObjectGetChild(match,"index",0)); JsVarInt len = (JsVarInt)jsvGetStringLength(matchStr); int last = idx+len; jsvArrayPushAndUnLock(array, matchStr); // search again jsvUnLock(match); jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(last)); match = jswrap_regexp_exec(subStr, parent); } jsvUnLock(match); jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(0)); return array; } #endif subStr = jsvAsString(subStr); int idx = jswrap_string_indexOf(parent, subStr, 0, false); if (idx>=0) { JsVar *array = jsvNewEmptyArray(); if (!array) { jsvUnLock(subStr); return 0; // out of memory } jsvArrayPush(array, subStr); jsvObjectSetChildAndUnLock(array, "index", jsvNewFromInteger(idx)); jsvObjectSetChildAndUnLock(array, "input", subStr); return array; } jsvUnLock(subStr); return NULL; }
/*JSON{ "type":"method", "class": "Array", "name" : "slice", "description" : "Return a copy of a portion of the calling array", "generate" : "jswrap_array_slice", "params" : [ [ "start", "JsVar", "Start index"], [ "end", "JsVar", "End index (optional)"] ], "return" : ["JsVar", "A new array"] }*/ JsVar *jswrap_array_slice(JsVar *parent, JsVar *startVar, JsVar *endVar) { JsVarInt len = jsvGetLength(parent); JsVarInt start = 0; JsVarInt end = len; if (!jsvIsUndefined(startVar)) start = jsvGetInteger(startVar); if (!jsvIsUndefined(endVar)) end = jsvGetInteger(endVar); JsVarInt k = 0; JsVarInt final = len; JsVar *array = jsvNewWithFlags(JSV_ARRAY); if (!array) return 0; if (start<0) k = max((len + start), 0); else k = min(start, len); if (end<0) final = max((len + end), 0); else final = min(end, len);
/*JSON{ "type" : "method", "class" : "String", "name" : "slice", "generate" : "jswrap_string_slice", "params" : [ ["start","int","The start character index, if negative it is from the end of the string"], ["end","JsVar","The end character index, if negative it is from the end of the string, and if omitted it is the end of the string"] ], "return" : ["JsVar","Part of this string from start for len characters"] }*/ JsVar *jswrap_string_slice(JsVar *parent, JsVarInt pStart, JsVar *vEnd) { JsVar *res; JsVarInt pEnd = jsvIsUndefined(vEnd) ? JSVAPPENDSTRINGVAR_MAXLENGTH : (int)jsvGetInteger(vEnd); if (pStart<0) pStart += (JsVarInt)jsvGetStringLength(parent); if (pEnd<0) pEnd += (JsVarInt)jsvGetStringLength(parent); if (pStart<0) pStart = 0; if (pEnd<0) pEnd = 0; res = jsvNewFromEmptyString(); if (!res) return 0; // out of memory if (pEnd>pStart) jsvAppendStringVar(res, parent, (size_t)pStart, (size_t)(pEnd-pStart)); return res; }
JsVar *_jswrap_array_map_or_forEach(JsVar *parent, JsVar *funcVar, JsVar *thisVar, bool isMap) { const char *name = isMap ? "map":"forEach"; if (!jsvIsIterable(parent)) { jsError("Array.%s can only be called on something iterable", name); return 0; } if (!jsvIsFunction(funcVar)) { jsError("Array.%s's first argument should be a function", name); return 0; } if (!jsvIsUndefined(thisVar) && !jsvIsObject(thisVar)) { jsError("Array.%s's second argument should be undefined, or an object", name); return 0; } JsVar *array = 0; if (isMap) array = jsvNewWithFlags(JSV_ARRAY); if (array || !isMap) { JsvIterator it; jsvIteratorNew(&it, parent); while (jsvIteratorHasElement(&it)) { JsVar *index = jsvIteratorGetKey(&it); if (jsvIsInt(index)) { JsVarInt idxValue = jsvGetInteger(index); JsVar *args[3], *mapped; args[0] = jsvIteratorGetValue(&it); args[1] = jsvNewFromInteger(idxValue); // child is a variable name, create a new variable for the index args[2] = parent; mapped = jspeFunctionCall(funcVar, 0, thisVar, false, 3, args); jsvUnLock(args[0]); jsvUnLock(args[1]); if (mapped) { if (isMap) { JsVar *name = jsvNewFromInteger(idxValue); if (name) { // out of memory? jsvMakeIntoVariableName(name, mapped); jsvAddName(array, name); jsvUnLock(name); } } jsvUnLock(mapped); } } jsvUnLock(index); jsvIteratorNext(&it); } jsvIteratorFree(&it); } return array; }
/*JSON{ "type" : "staticmethod", "class" : "ESP8266WiFi", "name" : "ping", "generate" : "jswrap_ESP8266WiFi_ping", "params" : [ ["ipAddr","JsVar","A string or integer representation of an IP address."], ["pingCallback", "JsVar", "Optional callback function."] ] }*/ void jswrap_ESP8266WiFi_ping( JsVar *ipAddr, //!< A string or integer representation of an IP address. JsVar *pingCallback //!< Optional callback function. ) { // If the parameter is a string, get the IP address from the string // representation. if (jsvIsString(ipAddr)) { char ipString[20]; int len = jsvGetString(ipAddr, ipString, sizeof(ipString)-1); ipString[len] = '\0'; pingOpt.ip = networkParseIPAddress(ipString); if (pingOpt.ip == 0) { jsExceptionHere(JSET_ERROR, "Not a valid IP address."); return; } } else // If the parameter is an integer, treat it as an IP address. if (jsvIsInt(ipAddr)) { pingOpt.ip = jsvGetInteger(ipAddr); } else // The parameter was neither a string nor an IP address and hence we don't // know how to get the IP address of the partner to ping so throw an // exception. { jsExceptionHere(JSET_ERROR, "IP address must be string or integer."); return; } if (jsvIsUndefined(pingCallback) || jsvIsNull(pingCallback)) { if (g_jsPingCallback != NULL) { jsvUnLock(g_jsPingCallback); } g_jsPingCallback = NULL; } else if (!jsvIsFunction(pingCallback)) { jsExceptionHere(JSET_ERROR, "Callback is not a function."); return; } else { if (g_jsPingCallback != NULL) { jsvUnLock(g_jsPingCallback); } g_jsPingCallback = pingCallback; jsvLockAgainSafe(g_jsPingCallback); } // We now have an IP address to ping ... so ping. memset(&pingOpt, 0, sizeof(pingOpt)); pingOpt.count = 5; pingOpt.recv_function = pingRecvCB; ping_start(&pingOpt); }
/*JSON{ "type" : "method", "class" : "String", "name" : "substring", "generate" : "jswrap_string_substring", "params" : [ ["start","int","The start character index"], ["end","JsVar","The end character index"] ], "return" : ["JsVar","The part of this string between start and end"] }*/ JsVar *jswrap_string_substring(JsVar *parent, JsVarInt pStart, JsVar *vEnd) { JsVar *res; JsVarInt pEnd = jsvIsUndefined(vEnd) ? JSVAPPENDSTRINGVAR_MAXLENGTH : (int)jsvGetInteger(vEnd); if (pStart<0) pStart=0; if (pEnd<0) pEnd=0; if (pEnd<pStart) { JsVarInt l = pStart; pStart = pEnd; pEnd = l; } res = jsvNewFromEmptyString(); if (!res) return 0; // out of memory jsvAppendStringVar(res, parent, (size_t)pStart, (size_t)(pEnd-pStart)); return res; }
/*JSON{ "type" : "method", "class" : "Object", "name" : "on", "generate" : "jswrap_object_on", "params" : [ ["event","JsVar","The name of the event, for instance 'data'"], ["listener","JsVar","The listener to call when this event is received"] ] } Register an event listener for this object, for instance ```http.on('data', function(d) {...})```. See Node.js's EventEmitter. */ void jswrap_object_on(JsVar *parent, JsVar *event, JsVar *listener) { if (!jsvHasChildren(parent)) { jsWarn("Parent must be an object - not a String, Integer, etc."); return; } if (!jsvIsString(event)) { jsWarn("First argument to EventEmitter.on(..) must be a string"); return; } if (!jsvIsFunction(listener) && !jsvIsString(listener)) { jsWarn("Second argument to EventEmitter.on(..) must be a function or a String (containing code)"); return; } JsVar *eventName = jsvVarPrintf(JS_EVENT_PREFIX"%s",event); if (!eventName) return; // no memory JsVar *eventList = jsvFindChildFromVar(parent, eventName, true); jsvUnLock(eventName); JsVar *eventListeners = jsvSkipName(eventList); if (jsvIsUndefined(eventListeners)) { // just add jsvSetValueOfName(eventList, listener); } else { if (jsvIsArray(eventListeners)) { // we already have an array, just add to it jsvArrayPush(eventListeners, listener); } else { // not an array - we need to make it an array JsVar *arr = jsvNewEmptyArray(); jsvArrayPush(arr, eventListeners); jsvArrayPush(arr, listener); jsvSetValueOfName(eventList, arr); jsvUnLock(arr); } } jsvUnLock2(eventListeners, eventList); /* Special case if we're a data listener and data has already arrived then * we queue an event immediately. */ if (jsvIsStringEqual(event, "data")) { JsVar *buf = jsvObjectGetChild(parent, STREAM_BUFFER_NAME, 0); if (jsvIsString(buf)) { jsiQueueObjectCallbacks(parent, STREAM_CALLBACK_NAME, &buf, 1); jsvRemoveNamedChild(parent, STREAM_BUFFER_NAME); } jsvUnLock(buf); } }