void httpServerResponseData(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 = jsvNewFromEmptyString(); jsvAppendPrintf(sendData, "HTTP/1.0 %d OK\r\nServer: Espruino "JS_VERSION"\r\n", jsvGetIntegerAndUnLock(jsvObjectSetChild(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 { // we have already sent headers 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); }
// returns 0 on success and a (negative) error number on failure int socketSendData(JsNetwork *net, JsVar *connection, int sckt, JsVar **sendData) { char *buf = alloca(net->chunkSize); // allocate on stack assert(!jsvIsEmptyString(*sendData)); size_t bufLen = httpStringGet(*sendData, buf, net->chunkSize); int num = netSend(net, sckt, buf, bufLen); if (num < 0) return num; // an error occurred // Now cut what we managed to send off the beginning of sendData if (num > 0) { JsVar *newSendData = 0; if (num < (int)jsvGetStringLength(*sendData)) { // we didn't send all of it... cut out what we did send newSendData = jsvNewFromStringVar(*sendData, (size_t)num, JSVAPPENDSTRINGVAR_MAXLENGTH); } else { // we sent all of it! Issue a drain event, unless we want to close, then we shouldn't // callback for more data bool wantClose = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_CLOSE,0)); if (!wantClose) { jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_DRAIN, &connection, 1); } newSendData = jsvNewFromEmptyString(); } jsvUnLock(*sendData); *sendData = newSendData; } return 0; }
/*JSON{ "type":"method", "class": "String", "name" : "split", "description" : "Return an array made by splitting this string up by the separator. eg. ```'1,2,3'.split(',')==[1,2,3]```", "generate" : "jswrap_string_split", "params" : [ [ "separator", "JsVar", "The start character index"] ], "return" : ["JsVar", "Part of this string from start for len characters"] }*/ JsVar *jswrap_string_split(JsVar *parent, JsVar *split) { JsVar *array; int last, idx, arraylen=0; int splitlen = (int)jsvGetStringLength(split); int l = (int)jsvGetStringLength(parent) - splitlen; last = 0; array = jsvNewWithFlags(JSV_ARRAY); if (!array) return 0; // out of memory for (idx=0;idx<=l;idx++) { if (idx==l || jsvCompareString(parent, split, idx, 0, true)==0) { JsVar *part = jsvNewFromEmptyString(); if (!part) break; // out of memory JsVar *idxvar = jsvMakeIntoVariableName(jsvNewFromInteger(arraylen++), part); if (idxvar) { // could be out of memory if (idx==l) idx=l+splitlen; // if the last element, do to the end of the string jsvAppendStringVar(part, parent, last, idx-last); jsvAddName(array, idxvar); last = idx+splitlen; jsvUnLock(idxvar); } jsvUnLock(part); } } return array; }
JsVar *jslNewStringFromLexer(JslCharPos *charFrom, size_t charTo) { // Original method - just copy it verbatim size_t maxLength = charTo + 1 - jsvStringIteratorGetIndex(&charFrom->it); assert(maxLength>0); // will fail if 0 // Try and create a flat string first JsVar *var = 0; if (maxLength > JSV_FLAT_STRING_BREAK_EVEN) { var = jsvNewFlatStringOfLength((unsigned int)maxLength); if (var) { // Flat string char *flatPtr = jsvGetFlatStringPointer(var); *(flatPtr++) = charFrom->currCh; JsvStringIterator it = jsvStringIteratorClone(&charFrom->it); while (jsvStringIteratorHasChar(&it) && (--maxLength>0)) { *(flatPtr++) = jsvStringIteratorGetChar(&it); jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); return var; } } // Non-flat string... var = jsvNewFromEmptyString(); if (!var) { // out of memory return 0; } //jsvAppendStringVar(var, lex->sourceVar, charFrom->it->index, (int)(charTo-charFrom)); JsVar *block = jsvLockAgain(var); block->varData.str[0] = charFrom->currCh; size_t blockChars = 1; size_t l = maxLength; // now start appending JsvStringIterator it = jsvStringIteratorClone(&charFrom->it); while (jsvStringIteratorHasChar(&it) && (--maxLength>0)) { char ch = jsvStringIteratorGetChar(&it); if (blockChars >= jsvGetMaxCharactersInVar(block)) { jsvSetCharactersInVar(block, blockChars); JsVar *next = jsvNewWithFlags(JSV_STRING_EXT_0); if (!next) break; // out of memory // we don't ref, because StringExts are never reffed as they only have one owner (and ALWAYS have an owner) jsvSetLastChild(block, jsvGetRef(next)); jsvUnLock(block); block = next; blockChars=0; // it's new, so empty } block->varData.str[blockChars++] = ch; jsvStringIteratorNext(&it); } jsvSetCharactersInVar(block, blockChars); jsvUnLock(block); // Just make sure we only assert if there's a bug here. If we just ran out of memory or at end of string it's ok assert((l == jsvGetStringLength(var)) || (jsErrorFlags&JSERR_MEMORY) || !jsvStringIteratorHasChar(&it)); jsvStringIteratorFree(&it); return var; }
/*JSON{ "type":"method", "class": "String", "name" : "charAt", "description" : "Return a single character at the given position in the String.", "generate" : "jswrap_string_charAt", "params" : [ [ "pos", "int", "The character number in the string. Negative values return characters from end of string (-1 = last char)"] ], "return" : ["JsVar", "The character in the string"] }*/ JsVar *jswrap_string_charAt(JsVar *parent, JsVarInt idx) { // We do this so we can handle '/0' in a string JsVar *r = jsvNewFromEmptyString(); if (r) { // out of mem? char ch = jsvGetCharInString(parent, (int)idx); jsvAppendStringBuf(r, &ch, 1); } return r; }
/*JSON{ "type":"staticmethod", "class": "String", "name" : "fromCharCode", "description" : "Return the character represented by the given character code.", "generate" : "jswrap_string_fromCharCode", "params" : [ [ "code", "int", "The character code to create a character from (range 0-255)"] ], "return" : ["JsVar", "The character"] }*/ JsVar *jswrap_string_fromCharCode(JsVarInt code) { // We do this so we can handle '/0' in a string JsVar *r = jsvNewFromEmptyString(); if (r) { // out of mem? char ch = code; jsvAppendStringBuf(r, &ch, 1); } return r; }
/*JSON{ "type" : "method", "class" : "String", "name" : "repeat", "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_string_repeat", "params" : [ ["count","int","An integer with the amount of times to repeat this String"] ], "return" : ["JsVar","A string containing repetitions of this string"], "return_object" : "String" } Repeat this string the given number of times. */ JsVar *jswrap_string_repeat(JsVar *parent, int count) { if (count<0) { jsExceptionHere(JSET_ERROR, "Invalid count value"); return 0; } JsVar *result = jsvNewFromEmptyString(); while (count-- && !jspIsInterrupted()) jsvAppendStringVarComplete(result, parent); return result; }
/*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; }
/*JSON{ "type":"function", "name" : "edit", "description" : ["Fill the console with the contents of the given function, so you can edit it.", "NOTE: This is a convenience function - it will not edit 'inner functions'. For that, you must edit the 'outer function' and re-execute it."], "generate" : "jswrap_interface_edit", "params" : [ [ "funcName", "JsVar", "The name of the function to edit (either a string or just the unquoted name)"] ] }*/ void jswrap_interface_edit(JsVar *funcName) { JsVar *func = 0; if (jsvIsString(funcName)) { funcName = jsvLockAgain(funcName); func = jsvSkipNameAndUnLock(jsvFindChildFromVar(execInfo.root, funcName, 0)); } else { func = funcName; funcName = jsvGetPathTo(execInfo.root, func, 2); } if (jsvIsString(funcName)) { if (jsvIsFunction(func)) { JsVar *scopeVar = jsvFindChildFromString(func, JSPARSE_FUNCTION_SCOPE_NAME, false); JsVar *inRoot = jsvGetArrayIndexOf(execInfo.root, func, true); bool normalDecl = scopeVar==0 && inRoot!=0; jsvUnLock(inRoot); jsvUnLock(scopeVar); JsVar *newLine = jsvNewFromEmptyString(); if (newLine) { // could be out of memory /* normalDecl: * * function foo() { ... } * * NOT normalDecl: * * foo.replaceWith(function() { ... }); * */ JsVar *funcData = jsvAsString(func, false); if (normalDecl) { jsvAppendString(newLine, "function "); jsvAppendStringVarComplete(newLine, funcName); jsvAppendStringVar(newLine, funcData, 9, JSVAPPENDSTRINGVAR_MAXLENGTH); } else { jsvAppendStringVarComplete(newLine, funcName); jsvAppendString(newLine, ".replaceWith("); jsvAppendStringVarComplete(newLine, funcData); jsvAppendString(newLine, ");"); } jsvUnLock(funcData); jsiReplaceInputLine(newLine); jsvUnLock(newLine); } } else { jsError("Edit should be called with the name of a function"); } } else { jsError("Edit should be called with edit(funcName) or edit('funcName')"); } jsvUnLock(func); jsvUnLock(funcName); }
/*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; }
/*JSON{ "type" : "method", "class" : "File", "name" : "read", "generate" : "jswrap_file_read", "description" : [ "Read data in a file in byte size chunks"], "params" : [ ["length", "int32", "is an integer specifying the number of bytes to read."] ], "return" : [ "JsVar", "A string containing the characters that were read" ] }*/ JsVar *jswrap_file_read(JsVar* parent, int length) { JsVar *buffer = 0; JsvStringIterator it; FRESULT res = 0; size_t bytesRead = 0; if (jsfsInit()) { JsFile file; if (fileGetFromVar(&file, parent)) { if(file.data.mode == FM_READ || file.data.mode == FM_READ_WRITE) { char buf[32]; size_t actual = 0; while (bytesRead < (size_t)length) { size_t requested = (size_t)length - bytesRead; if (requested > sizeof( buf )) requested = sizeof( buf ); actual = 0; #ifndef LINUX res = f_read(&file.data.handle, buf, requested, &actual); if(res) break; #else actual = fread(buf, 1, requested, file.data.handle); #endif if (actual>0) { if (!buffer) { buffer = jsvNewFromEmptyString(); if (!buffer) return 0; // out of memory jsvStringIteratorNew(&it, buffer, 0); } size_t i; for (i=0;i<actual;i++) jsvStringIteratorAppend(&it, buf[i]); } bytesRead += actual; if(actual != requested) break; } fileSetVar(&file); } } } if (res) jsfsReportError("Unable to read file", res); if (buffer) jsvStringIteratorFree(&it); // automatically close this file if we're at the end of it if (bytesRead!=(size_t)length) jswrap_file_close(parent); return buffer; }
/*JSON{ "type" : "method", "class" : "String", "name" : "charAt", "generate" : "jswrap_string_charAt", "params" : [ ["pos","int","The character number in the string. Negative values return characters from end of string (-1 = last char)"] ], "return" : ["JsVar","The character in the string"] } Return a single character at the given position in the String. */ JsVar *jswrap_string_charAt(JsVar *parent, JsVarInt idx) { // We do this so we can handle '/0' in a string JsVar *r = jsvNewFromEmptyString(); if (r && jsvIsString(parent) && idx>=0) { JsvStringIterator it; jsvStringIteratorNew(&it, parent, (size_t)idx); if (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); jsvAppendStringBuf(r, &ch, 1); } jsvStringIteratorFree(&it); } return r; }
/*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" : "function", "name" : "getSerial", "generate" : "jswrap_interface_getSerial", "return" : ["JsVar","The board's serial number"] } Get the serial number of this board */ JsVar *jswrap_interface_getSerial() { char buf[8]; unsigned char serial[32]; int i, serialSize = jshGetSerialNumber(serial, sizeof(serial)); JsVar *str = jsvNewFromEmptyString(); if (!str) return 0; for (i=0;i<serialSize;i++) { if ((i&3)==0 && i) jsvAppendString(str, "-"); itostr(serial[i] | 0x100, buf, 16); jsvAppendString(str, &buf[1]); } return str; }
/*JSON{ "type" : "staticmethod", "class" : "String", "name" : "fromCharCode", "generate" : "jswrap_string_fromCharCode", "params" : [ ["code","JsVarArray","One or more character codes to create a string from (range 0-255)."] ], "return" : ["JsVar","The character"] } Return the character(s) represented by the given character code(s). */ JsVar *jswrap_string_fromCharCode(JsVar *arr) { assert(jsvIsArray(arr)); JsVar *r = jsvNewFromEmptyString(); if (!r) return 0; JsvObjectIterator it; jsvObjectIteratorNew(&it, arr); while (jsvObjectIteratorHasValue(&it)) { char ch = (char)jsvGetIntegerAndUnLock(jsvObjectIteratorGetValue(&it)); jsvAppendStringBuf(r, &ch, 1); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); return r; }
static void jslLexRegex() { lex->tokenValue = jsvNewFromEmptyString(); if (!lex->tokenValue) { lex->tk = LEX_EOF; return; } JsvStringIterator it; jsvStringIteratorNew(&it, lex->tokenValue, 0); jsvStringIteratorAppend(&it, '/'); // strings... jslGetNextCh(); while (lex->currCh && lex->currCh!='/') { if (lex->currCh == '\\') { jsvStringIteratorAppend(&it, lex->currCh); jslGetNextCh(); } else if (lex->currCh=='\n') { /* Was a newline - this is now allowed * unless we're a template string */ break; } jsvStringIteratorAppend(&it, lex->currCh); jslGetNextCh(); } lex->tk = LEX_REGEX; if (lex->currCh!='/') { lex->tk++; // +1 gets you to 'unfinished X' } else { jsvStringIteratorAppend(&it, '/'); jslGetNextCh(); // regex modifiers while (lex->currCh=='g' || lex->currCh=='i' || lex->currCh=='m' || lex->currCh=='y' || lex->currCh=='u') { jslTokenAppendChar(lex->currCh); jsvStringIteratorAppend(&it, lex->currCh); jslGetNextCh(); } } jsvStringIteratorFree(&it); }
bool _http_send(SOCKET sckt, JsVar **sendData) { char buf[64]; int a=1; int len = (int)jsvGetStringLength(*sendData); if (len>0) { size_t bufLen = httpStringGet(*sendData, buf, sizeof(buf)); a = (int)send(sckt,buf,bufLen, MSG_NOSIGNAL); JsVar *newSendData = 0; if (a!=len) { newSendData = jsvNewFromEmptyString(); jsvAppendStringVar(newSendData, *sendData, a, JSVAPPENDSTRINGVAR_MAXLENGTH); } jsvUnLock(*sendData); *sendData = newSendData; } if (a<=0) { httpError("Socket error while sending"); return false; } return true; }
/*JSON{ "type" : "staticmethod", "class" : "JSON", "name" : "stringify", "generate" : "jswrap_json_stringify", "params" : [ ["data","JsVar","The data to be converted to a JSON string"], ["replacer","JsVar","This value is ignored"], ["space","JsVar","The number of spaces to use for padding, a string, or null/undefined for no whitespace "] ], "return" : ["JsVar","A JSON string"] } Convert the given object into a JSON string which can subsequently be parsed with JSON.parse or eval. **Note:** This differs from JavaScript's standard `JSON.stringify` in that: * The `replacer` argument is ignored * Typed arrays like `new Uint8Array(5)` will be dumped as if they were arrays, not as if they were objects (since it is more compact) */ JsVar *jswrap_json_stringify(JsVar *v, JsVar *replacer, JsVar *space) { NOT_USED(replacer); JSONFlags flags = JSON_IGNORE_FUNCTIONS|JSON_NO_UNDEFINED|JSON_ARRAYBUFFER_AS_ARRAY; JsVar *result = jsvNewFromEmptyString(); if (result) {// could be out of memory char whitespace[11] = ""; if (jsvIsUndefined(space) || jsvIsNull(space)) { // nothing } else if (jsvIsNumeric(space)) { unsigned int s = (unsigned int)jsvGetInteger(space); if (s>10) s=10; whitespace[s] = 0; while (s) whitespace[--s]=' '; } else { jsvGetString(space, whitespace, sizeof(whitespace)); } if (strlen(whitespace)) flags |= JSON_ALL_NEWLINES|JSON_PRETTY; jsfGetJSONWhitespace(v, result, flags, whitespace); } return result; }
/*JSON{ "type":"function", "name" : "edit", "description" : ["Fill the console with the contents of the given function, so you can edit it.", "NOTE: This is a convenience function - it will not edit 'inner functions'. For that, you must edit the 'outer function' and re-execute it."], "generate" : "jswrap_interface_edit", "params" : [ [ "funcName", "JsVarName", "The name of the function to edit (either a string or just the unquoted name)"] ] }*/ void jswrap_interface_edit(JsVar *funcName) { if (jsvIsString(funcName)) { JsVar *func = 0; if (jsvIsName(funcName)) func = jsvSkipName(funcName); else func = jsvSkipNameAndUnLock(jsvFindChildFromVar(jsiGetParser()->root, funcName, 0)); if (jsvIsFunction(func)) { JsVar *scopeVar = jsvFindChildFromString(func, JSPARSE_FUNCTION_SCOPE_NAME, false); JsVarRef scope = jsvGetRef(scopeVar); jsvUnLock(scopeVar); JsVar *newLine = jsvNewFromEmptyString(); if (newLine) { // could be out of memory jsvAppendStringVarComplete(newLine, funcName); if (scope) { // If we have a scope, it's an internal function so we will need to write different code jsvAppendString(newLine, ".replaceWith("); } else { jsvAppendString(newLine, " = "); } JsVar *funcData = jsvAsString(func, false); if (funcData) jsvAppendStringVarComplete(newLine, funcData); jsvUnLock(funcData); if (scope) { jsvAppendString(newLine, ");"); } else { jsvAppendString(newLine, ";"); } jsiReplaceInputLine(newLine); jsvUnLock(newLine); } } else { jsError("Edit should be called with the name of a function"); } jsvUnLock(func); } else { jsError("Edit should be called with edit(funcName) or edit('funcName')"); } }
/*JSON{ "type" : "method", "class" : "String", "name" : "toUpperCase", "generate_full" : "jswrap_string_toUpperLowerCase(parent, true)", "params" : [ ], "return" : ["JsVar","The uppercase version of this string"] }*/ JsVar *jswrap_string_toUpperLowerCase(JsVar *parent, bool upper) { JsVar *res = jsvNewFromEmptyString(); if (!res) return 0; // out of memory JsVar *parentStr = jsvAsString(parent); JsvStringIterator itsrc, itdst; jsvStringIteratorNew(&itsrc, parentStr, 0); jsvStringIteratorNew(&itdst, res, 0); while (jsvStringIteratorHasChar(&itsrc)) { char ch = jsvStringIteratorGetChar(&itsrc); ch = upper ? jsvStringCharToUpper(ch) : jsvStringCharToLower(ch); jsvStringIteratorAppend(&itdst, ch); jsvStringIteratorNext(&itsrc); } jsvStringIteratorFree(&itsrc); jsvStringIteratorFree(&itdst); jsvUnLock(parentStr); return res; }
// Return a string containing 'chars' bytes. If chars<=0 the string will be all available data JsVar *jswrap_stream_read(JsVar *parent, JsVarInt chars) { if (!jsvIsObject(parent)) return 0; JsVar *buf = jsvObjectGetChild(parent, STREAM_BUFFER_NAME, 0); JsVar *data = 0; if (jsvIsString(buf)) { size_t len = jsvGetStringLength(buf); if (chars <= 0 || (size_t)chars>=len) { // return the whole buffer and remove it data = buf; buf = 0; jsvRemoveNamedChild(parent, STREAM_BUFFER_NAME); } else { // return just part of the buffer, and shorten it accordingly data = jsvNewFromStringVar(buf, 0, (size_t)chars); JsVar *newBuf = jsvNewFromStringVar(buf, (size_t)chars, JSVAPPENDSTRINGVAR_MAXLENGTH); jsvUnLock(jsvObjectSetChild(parent, STREAM_BUFFER_NAME, newBuf)); } } else data = jsvNewFromEmptyString(); jsvUnLock(buf); return data; }
/*JSON{ "type" : "staticmethod", "class" : "fs", "name" : "readFileSync", "ifndef" : "SAVE_ON_FLASH", "generate" : "wrap_fat_readFile", "description" : [ "Read all data from a file and return as a string" ], "params" : [ [ "path", "JsVar", "The path of the file to read" ] ], "return" : [ "JsVar", "A string containing the contents of the file" ] }*/ JsVar *wrap_fat_readFile(JsVar *path) { char pathStr[JS_DIR_BUF_SIZE] = ""; if (!jsvIsUndefined(path)) jsvGetString(path, pathStr, JS_DIR_BUF_SIZE); JsVar *result = jsvNewFromEmptyString(); if (!result) return 0; // out of memory FRESULT res = 0; if (jsfsInit()) { #ifndef LINUX FIL file; if ((res=f_open(&file, pathStr, FA_READ)) == FR_OK) { #else FILE *file = fopen(pathStr, "r"); if (file) { #endif // re-use pathStr buffer size_t bytesRead = JS_DIR_BUF_SIZE; while (res==FR_OK && bytesRead==JS_DIR_BUF_SIZE) { #ifndef LINUX res = f_read (&file, pathStr, JS_DIR_BUF_SIZE, &bytesRead); #else bytesRead = fread(pathStr,1,JS_DIR_BUF_SIZE,file); #endif jsvAppendStringBuf(result, pathStr, (int)bytesRead); } #ifndef LINUX f_close(&file); #else fclose(file); #endif } } if (res) jsfsReportError("Unable to read file", res); return result; }
JsVar *jslNewFromLexer(struct JsLex *lex, JslCharPos *charFrom, size_t charTo) { // Create a var JsVar *var = jsvNewFromEmptyString(); if (!var) { // out of memory return 0; } //jsvAppendStringVar(var, lex->sourceVar, charFrom->it->index, (int)(charTo-charFrom)); size_t maxLength = charTo - jsvStringIteratorGetIndex(&charFrom->it); JsVar *block = jsvLockAgain(var); block->varData.str[0] = charFrom->currCh; size_t blockChars = 1; // now start appending JsvStringIterator it = jsvStringIteratorClone(&charFrom->it); while (jsvStringIteratorHasChar(&it) && (maxLength-->0)) { char ch = jsvStringIteratorGetChar(&it); if (blockChars >= jsvGetMaxCharactersInVar(block)) { jsvSetCharactersInVar(block, blockChars); JsVar *next = jsvNewWithFlags(JSV_STRING_EXT); if (!next) break; // out of memory // we don't ref, because StringExts are never reffed as they only have one owner (and ALWAYS have an owner) block->lastChild = jsvGetRef(next); jsvUnLock(block); block = next; blockChars=0; // it's new, so empty } block->varData.str[blockChars++] = ch; jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); jsvSetCharactersInVar(block, blockChars); jsvUnLock(block); return var; }
/*JSON{ "type" : "method", "class" : "String", "name" : "toUpperCase", "generate_full" : "jswrap_string_toUpperLowerCase(parent, true)", "params" : [ ], "return" : ["JsVar","The uppercase version of this string"] }*/ JsVar *jswrap_string_toUpperLowerCase(JsVar *parent, bool upper) { JsVar *res = jsvNewFromEmptyString(); if (!res) return 0; // out of memory JsvStringIterator itsrc, itdst; jsvStringIteratorNew(&itsrc, parent, 0); jsvStringIteratorNew(&itdst, res, 0); while (jsvStringIteratorHasChar(&itsrc)) { char ch = jsvStringIteratorGetChar(&itsrc); if (upper) { if (ch >= 97 && ch <= 122) ch = (char)(ch - 32); } else { if (ch >= 65 && ch <= 90) ch = (char)(ch + 32); // A-Z } jsvStringIteratorAppend(&itdst, ch); jsvStringIteratorNext(&itsrc); } jsvStringIteratorFree(&itsrc); jsvStringIteratorFree(&itdst); return res; }
/*JSON{ "type" : "method", "class" : "Function", "name" : "bind", "generate" : "jswrap_function_bind", "params" : [ ["this","JsVar","The value to use as the 'this' argument when executing the function"], ["params","JsVarArray","Optional Default parameters that are prepended to the call"] ], "return" : ["JsVar","The 'bound' function"] } This executes the function with the supplied 'this' argument and parameters */ JsVar *jswrap_function_bind(JsVar *parent, JsVar *thisArg, JsVar *argsArray) { if (!jsvIsFunction(parent)) { jsExceptionHere(JSET_TYPEERROR, "Function.bind expects to be called on function, got %t", parent); return 0; } JsVar *fn; if (jsvIsNativeFunction(parent)) fn = jsvNewNativeFunction(parent->varData.native.ptr, parent->varData.native.argTypes); else fn = jsvNewWithFlags(jsvIsFunctionReturn(parent) ? JSV_FUNCTION_RETURN : JSV_FUNCTION); if (!fn) return 0; // Old function info JsvObjectIterator fnIt; jsvObjectIteratorNew(&fnIt, parent); // add previously bound arguments while (jsvObjectIteratorHasValue(&fnIt)) { JsVar *param = jsvObjectIteratorGetKey(&fnIt); JsVar *defaultValue = jsvObjectIteratorGetValue(&fnIt); bool wasBound = jsvIsFunctionParameter(param) && defaultValue; if (wasBound) { JsVar *newParam = jsvCopy(param); if (newParam) { // could be out of memory jsvAddName(fn, newParam); jsvUnLock(newParam); } } jsvUnLock2(param, defaultValue); if (!wasBound) break; jsvObjectIteratorNext(&fnIt); } // add bound arguments JsvObjectIterator argIt; jsvObjectIteratorNew(&argIt, argsArray); while (jsvObjectIteratorHasValue(&argIt)) { JsVar *defaultValue = jsvObjectIteratorGetValue(&argIt); bool addedParam = false; while (!addedParam && jsvObjectIteratorHasValue(&fnIt)) { JsVar *param = jsvObjectIteratorGetKey(&fnIt); if (!jsvIsFunctionParameter(param)) { jsvUnLock(param); break; } JsVar *newParam = jsvCopyNameOnly(param, false, true); jsvSetValueOfName(newParam, defaultValue); jsvAddName(fn, newParam); addedParam = true; jsvUnLock2(param, newParam); jsvObjectIteratorNext(&fnIt); } if (!addedParam) { JsVar *paramName = jsvNewFromEmptyString(); if (paramName) { jsvMakeFunctionParameter(paramName); // force this to be called a function parameter jsvSetValueOfName(paramName, defaultValue); jsvAddName(fn, paramName); jsvUnLock(paramName); } } jsvUnLock(defaultValue); jsvObjectIteratorNext(&argIt); } jsvObjectIteratorFree(&argIt); // Copy the rest of the old function's info while (jsvObjectIteratorHasValue(&fnIt)) { JsVar *param = jsvObjectIteratorGetKey(&fnIt); JsVar *newParam = jsvCopyNameOnly(param, true, true); if (newParam) { // could be out of memory jsvAddName(fn, newParam); jsvUnLock(newParam); } jsvUnLock(param); jsvObjectIteratorNext(&fnIt); } jsvObjectIteratorFree(&fnIt); // Add 'this' jsvObjectSetChild(fn, JSPARSE_FUNCTION_THIS_NAME, thisArg); // no unlock needed return fn; }
/*JSON{ "type" : "staticmethod", "class" : "JSON", "name" : "stringify", "generate" : "jswrap_json_stringify", "params" : [ ["data","JsVar","The data to be converted to a JSON string"] ], "return" : ["JsVar","A JSON string"] } Convert the given object into a JSON string which can subsequently be parsed with JSON.parse or eval */ JsVar *jswrap_json_stringify(JsVar *v) { JsVar *result = jsvNewFromEmptyString(); if (result) // could be out of memory jsfGetJSON(v, result, JSON_IGNORE_FUNCTIONS|JSON_NO_UNDEFINED); return result; }
/*JSON{ "type" : "constructor", "class" : "String", "name" : "String", "generate" : "jswrap_string_constructor", "params" : [ ["str","JsVar","A value to turn into a string. If undefined or not supplied, an empty String is created."] ], "return" : ["JsVar","A String"] } Create a new String */ JsVar *jswrap_string_constructor(JsVar *a) { if (!a) return jsvNewFromEmptyString(); // no argument - return an empty string return jsvAsString(a, false); }
/*JSON{ "type" : "staticmethod", "class" : "url", "name" : "parse", "generate" : "jswrap_url_parse", "params" : [ ["urlStr","JsVar","A URL to be parsed"], ["parseQuery","bool","Whether to parse the query string into an object not (default = false)"] ], "return" : ["JsVar","An object containing options for ```http.request``` or ```http.get```. Contains `method`, `host`, `path`, `pathname`, `search`, `port` and `query`"] } A utility function to split a URL into parts This is useful in web servers for instance when handling a request. For instance `url.parse("/a?b=c&d=e",true)` returns `{"method":"GET","host":"","path":"/a?b=c&d=e","pathname":"/a","search":"?b=c&d=e","port":80,"query":{"b":"c","d":"e"}}` */ JsVar *jswrap_url_parse(JsVar *url, bool parseQuery) { if (!jsvIsString(url)) return 0; JsVar *obj = jsvNewWithFlags(JSV_OBJECT); if (!obj) return 0; // out of memory // scan string to try and pick stuff out JsvStringIterator it; jsvStringIteratorNew(&it, url, 0); int slashes = 0; int colons = 0; int addrStart = -1; int portStart = -1; int pathStart = -1; int searchStart = -1; int charIdx = 0; int portNumber = 0; while (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); if (ch == '/') { slashes++; if (pathStart<0) pathStart = charIdx; if (colons==1 && slashes==2 && addrStart<0) { addrStart = charIdx; pathStart = -1; searchStart = -1; } } if (ch == ':') { colons++; if (addrStart>=0 && pathStart<0) portStart = charIdx; } if (portStart>=0 && charIdx>portStart && pathStart<0 && ch >= '0' && ch <= '9') { portNumber = portNumber*10 + (ch-'0'); } if (ch == '?' && pathStart>=0) { searchStart = charIdx; } jsvStringIteratorNext(&it); charIdx++; } jsvStringIteratorFree(&it); // try and sort stuff out if (pathStart<0) pathStart = charIdx; if (pathStart<0) pathStart = charIdx; int addrEnd = (portStart>=0) ? portStart : pathStart; // pull out details if (addrStart>0) jsvObjectSetChildAndUnLock(obj, "protocol", jsvNewFromStringVar(url, 0, (size_t)addrStart-1)); jsvObjectSetChildAndUnLock(obj, "method", jsvNewFromString("GET")); jsvObjectSetChildAndUnLock(obj, "host", jsvNewFromStringVar(url, (size_t)(addrStart+1), (size_t)(addrEnd-(addrStart+1)))); JsVar *v; v = jsvNewFromStringVar(url, (size_t)pathStart, JSVAPPENDSTRINGVAR_MAXLENGTH); if (jsvGetStringLength(v)==0) jsvAppendString(v, "/"); jsvObjectSetChildAndUnLock(obj, "path", v); v = jsvNewFromStringVar(url, (size_t)pathStart, (size_t)((searchStart>=0)?(searchStart-pathStart):JSVAPPENDSTRINGVAR_MAXLENGTH)); if (jsvGetStringLength(v)==0) jsvAppendString(v, "/"); jsvObjectSetChildAndUnLock(obj, "pathname", v); jsvObjectSetChildAndUnLock(obj, "search", (searchStart>=0)?jsvNewFromStringVar(url, (size_t)searchStart, JSVAPPENDSTRINGVAR_MAXLENGTH):jsvNewNull()); jsvObjectSetChildAndUnLock(obj, "port", (portNumber<=0 || portNumber>65535) ? jsvNewWithFlags(JSV_NULL) : jsvNewFromInteger(portNumber)); JsVar *query = (searchStart>=0)?jsvNewFromStringVar(url, (size_t)(searchStart+1), JSVAPPENDSTRINGVAR_MAXLENGTH):jsvNewNull(); if (parseQuery && !jsvIsNull(query)) { JsVar *queryStr = query; jsvStringIteratorNew(&it, query, 0); query = jsvNewWithFlags(JSV_OBJECT); JsVar *key = jsvNewFromEmptyString(); JsVar *val = jsvNewFromEmptyString(); bool hadEquals = false; while (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); if (ch=='&') { if (jsvGetStringLength(key)>0 || jsvGetStringLength(val)>0) { key = jsvAsArrayIndexAndUnLock(key); // make sure "0" gets made into 0 jsvMakeIntoVariableName(key, val); jsvAddName(query, key); jsvUnLock2(key, val); key = jsvNewFromEmptyString(); val = jsvNewFromEmptyString(); hadEquals = false; } } else if (!hadEquals && ch=='=') { hadEquals = true; } else { // decode percent escape chars if (ch=='%') { jsvStringIteratorNext(&it); ch = jsvStringIteratorGetChar(&it); jsvStringIteratorNext(&it); ch = (char)((chtod(ch)<<4) | chtod(jsvStringIteratorGetChar(&it))); } if (hadEquals) jsvAppendCharacter(val, ch); else jsvAppendCharacter(key, ch); } jsvStringIteratorNext(&it); charIdx++; } jsvStringIteratorFree(&it); jsvUnLock(queryStr); if (jsvGetStringLength(key)>0 || jsvGetStringLength(val)>0) { key = jsvAsArrayIndexAndUnLock(key); // make sure "0" gets made into 0 jsvMakeIntoVariableName(key, val); jsvAddName(query, key); } jsvUnLock2(key, val); } jsvObjectSetChildAndUnLock(obj, "query", query); return obj; }
/*JSON{ "type" : "staticmethod", "class" : "fs", "name" : "readFileSync", "ifndef" : "SAVE_ON_FLASH", "generate" : "wrap_fat_readFile", "description" : [ "Read all data from a file and return as a string" ], "params" : [ [ "path", "JsVar", "The path of the file to read" ] ], "return" : [ "JsVar", "A string containing the contents of the file (or undefined if the file doesn't exist)" ] }*/ JsVar *wrap_fat_readFile(JsVar *path) { char pathStr[JS_DIR_BUF_SIZE] = ""; if (!jsvIsUndefined(path)) jsvGetString(path, pathStr, JS_DIR_BUF_SIZE); JsVar *result = 0; // undefined unless we read the file FRESULT res = 0; if (jsfsInit()) { #ifndef LINUX FIL file; if ((res=f_open(&file, pathStr, FA_READ)) == FR_OK) { #else FILE *file = fopen(pathStr, "r"); if (file) { #endif result = jsvNewFromEmptyString(); if (result) { // out of memory? // re-use pathStr buffer size_t bytesRead = JS_DIR_BUF_SIZE; while (res==FR_OK && bytesRead==JS_DIR_BUF_SIZE) { #ifndef LINUX res = f_read (&file, pathStr, JS_DIR_BUF_SIZE, &bytesRead); #else bytesRead = fread(pathStr,1,JS_DIR_BUF_SIZE,file); #endif if (!jsvAppendStringBuf(result, pathStr, (int)bytesRead)) break; // was out of memory } } #ifndef LINUX f_close(&file); #else fclose(file); #endif } } if (res) jsfsReportError("Unable to read file", res); return result; } /*JSON{ "type" : "staticmethod", "class" : "fs", "name" : "unlink", "generate" : "wrap_fat_unlink", "description" : [ "Delete the given file", "NOTE: Espruino does not yet support Async file IO, so this function behaves like the 'Sync' version." ], "params" : [ [ "path", "JsVar", "The path of the file to delete" ] ], "return" : [ "bool", "True on success, or false on failure" ] }*/ /*JSON{ "type" : "staticmethod", "class" : "fs", "name" : "unlinkSync", "ifndef" : "SAVE_ON_FLASH", "generate" : "wrap_fat_unlink", "description" : [ "Delete the given file" ], "params" : [ [ "path", "JsVar", "The path of the file to delete" ] ], "return" : [ "bool", "True on success, or false on failure" ] }*/ bool wrap_fat_unlink(JsVar *path) { char pathStr[JS_DIR_BUF_SIZE] = ""; if (!jsvIsUndefined(path)) jsvGetString(path, pathStr, JS_DIR_BUF_SIZE); #ifndef LINUX FRESULT res = 0; if (jsfsInit()) { f_unlink(pathStr); } #else FRESULT res = remove(pathStr); #endif if (res) { jsfsReportError("Unable to delete file", res); return false; } return true; }
/*JSON{ "type" : "method", "class" : "File", "name" : "read", "generate" : "jswrap_file_read", "params" : [ ["length","int32","is an integer specifying the number of bytes to read."] ], "return" : ["JsVar","A string containing the characters that were read"] } Read data in a file in byte size chunks */ JsVar *jswrap_file_read(JsVar* parent, int length) { if (length<0) length=0; JsVar *buffer = 0; JsvStringIterator it; FRESULT res = 0; size_t bytesRead = 0; if (jsfsInit()) { JsFile file; if (fileGetFromVar(&file, parent)) { if(file.data->mode == FM_READ || file.data->mode == FM_READ_WRITE) { size_t actual = 0; #ifndef LINUX // if we're able to load this into a flat string, do it! size_t len = f_size(&file.data->handle)-f_tell(&file.data->handle); if ( len == 0 ) { // file all read return 0; // if called from a pipe signal end callback } if (len > (size_t)length) len = (size_t)length; buffer = jsvNewFlatStringOfLength(len); if (buffer) { res = f_read(&file.data->handle, jsvGetFlatStringPointer(buffer), len, &actual); if (res) jsfsReportError("Unable to read file", res); return buffer; } #endif char buf[32]; while (bytesRead < (size_t)length) { size_t requested = (size_t)length - bytesRead; if (requested > sizeof( buf )) requested = sizeof( buf ); actual = 0; #ifndef LINUX res = f_read(&file.data->handle, buf, requested, &actual); if(res) break; #else actual = fread(buf, 1, requested, file.data->handle); #endif if (actual>0) { if (!buffer) { buffer = jsvNewFromEmptyString(); if (!buffer) return 0; // out of memory jsvStringIteratorNew(&it, buffer, 0); } size_t i; for (i=0;i<actual;i++) jsvStringIteratorAppend(&it, buf[i]); } bytesRead += actual; if(actual != requested) break; } } } } if (res) jsfsReportError("Unable to read file", res); if (buffer) jsvStringIteratorFree(&it); return buffer; }