/*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(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" : "init", "generate" : "jswrap_wice_init" }*/ void jswrap_wice_init() { jspCallNamedFunction(execInfo.root, "USB.setConsole", 0, 0); jspEvaluate("USB.setConsole();"); #ifdef WICE_DEBUG WDEBUGLN("USB is console"); #endif /*JsVar *mode = jsvNewFromString("output"); jswrap_io_pinMode(jshGetPinFromString(PIN_DASH7_XTAL_EN), mode); jswrap_io_pinMode(jshGetPinFromString(PIN_DASH7_RST), mode); jsvUnLock(mode);*/ enableDASH(); // Set up the DASH7 USART how we want it JshUSARTInfo inf; jshUSARTInitInfo(&inf); inf.baudRate = 115200; inf.pinRX = jshGetPinFromString(PIN_DASH7_RX); inf.pinTX = jshGetPinFromString(PIN_DASH7_TX); jshUSARTSetup(EV_SERIAL1, &inf); JsVar *serial = jspGetNamedField(execInfo.root, SERIAL1_DASH7, false); jswrap_object_addEventListener(serial, "data", dash7Callback, JSWAT_VOID | (JSWAT_JSVAR<<(JSWAT_BITS))); jsvUnLock(serial); startTime = jshGetSystemTime(); wice_msg_init(&wifiMessage, wifiMessageBuffer, 2048); wice_msg_init(&dash7Message, dash7MessageBuffer, 256); options = jspEvaluate("x = {\"repeat\": \"true\", \"edge\": \"rising\", \"debounce\":\"50\"}"); Pin btn = jshGetPinFromString("B10"); JsVar *btnmode = jsvNewFromString("input"); jswrap_io_pinMode(btn, btnmode); jsvUnLock(btnmode); #ifdef GATEWAY WDEBUGLN("GATEWAY"); blink(PIN_GRN, 50); /// opendrain and digitalwrite 1 will 'opencircuit it' /// http://www.espruino.com/Reference#l__global_pinMode JsVar *opendrain = jsvNewFromString("opendrain"); WDEBUGSTRVAR(opendrain); jswrap_io_pinMode(jshGetPinFromString(PIN_ESP_GPIO_0), opendrain); jswrap_io_pinMode(jshGetPinFromString(PIN_ESP_GPIO_2), opendrain); jshPinOutput(jshGetPinFromString(PIN_ESP_GPIO_0), 1); jshPinOutput(jshGetPinFromString(PIN_ESP_GPIO_2), 1); jsvUnLock(opendrain); /// must opendrain gpio0/2 because of boot modes /// https://github.com/esp8266/esp8266-wiki/wiki/Boot-Process enableWifi(); // Set up the Wifi USART how we want it JshUSARTInfo inf4; jshUSARTInitInfo(&inf4); inf4.baudRate = 115200; inf4.pinRX = jshGetPinFromString(PIN_ESP_RX); inf4.pinTX = jshGetPinFromString(PIN_ESP_TX); jshUSARTSetup(EV_SERIAL4, &inf4); /// make button restart wifi for gateway JsVar *restartWifi_fn = jsvNewNativeFunction(restartWifi, JSWAT_VOID); btnEvent = jswrap_interface_setWatch(restartWifi_fn, btn, options); jsvUnLock(restartWifi_fn); /// configure wifi usart callback JsVar *wifiSerial = jspGetNamedField(execInfo.root, SERIAL4_WIFI, false); jswrap_object_addEventListener(wifiSerial, "data", wifiCallback, JSWAT_VOID | (JSWAT_JSVAR<<(JSWAT_BITS))); jsvUnLock(wifiSerial); doSendSerial(); #else WDEBUGLN("NODE"); blink(PIN_BLUE, 50); /// make button a forced measurement for nodes JsVar *doMeasurementAndWaitForResponse_fn = jsvNewNativeFunction(doMeasurementAndWaitForResponse, JSWAT_VOID); btnEvent = jswrap_interface_setWatch(doMeasurementAndWaitForResponse_fn, btn, options); jsvUnLock(doMeasurementAndWaitForResponse_fn); /// set up main interval callback for nodes JsVar *doMeasurement_fn = jsvNewNativeFunction(doMeasurement, JSWAT_VOID); currentInterval = jswrap_interface_setInterval(doMeasurement_fn, INTERVAL, 0); jsvUnLock(doMeasurement_fn); /// prepare the I2C bus for talking with the Si7050 temp sensor JsVar *s = jspEvaluate("I2C1.setup({scl:B8, sda:B9, bitrate:50000});"); jsvUnLock(s); disableDASH(); jswrap_interface_setDeepSleep(true); //do deep sleep [TODO can we wake on press?] #endif }
int main(int argc, char **argv) { int i; for (i=1;i<argc;i++) { if (argv[i][0]=='-') { // option char *a = argv[i]; if (!strcmp(a,"-h") || !strcmp(a,"--help")) { show_help(); exit(1); } else if (!strcmp(a,"-e") || !strcmp(a,"--eval")) { if (i+1>=argc) die("Expecting an extra argument\n"); jshInit(); jsvInit(); jsiInit(true); addNativeFunction("quit", nativeQuit); jsvUnLock(jspEvaluate(argv[i+1], false)); int errCode = handleErrors(); isRunning = !errCode; bool isBusy = true; while (isRunning && (jsiHasTimers() || isBusy)) isBusy = jsiLoop(); jsiKill(); jsvKill(); jshKill(); exit(errCode); } else if (!strcmp(a,"--test")) { if (i+1>=argc) die("Expecting an extra argument\n"); bool ok = run_test(argv[i+1]); exit(ok ? 0 : 1); } else if (!strcmp(a,"--test-all")) { bool ok = run_all_tests(); exit(ok ? 0 : 1); } else if (!strcmp(a,"--test-mem-all")) { bool ok = run_memory_tests(0); exit(ok ? 0 : 1); } else if (!strcmp(a,"--test-mem")) { if (i+1>=argc) die("Expecting an extra argument\n"); bool ok = run_memory_test(argv[i+1], 0); exit(ok ? 0 : 1); } else if (!strcmp(a,"--test-mem-n")) { if (i+2>=argc) die("Expecting an extra 2 arguments\n"); bool ok = run_memory_test(argv[i+1], atoi(argv[i+2])); exit(ok ? 0 : 1); } else { printf("Unknown Argument %s\n", a); show_help(); exit(1); } } } if (argc==1) { printf("Interactive mode.\n"); } else if (argc==2) { // single file - just run it char *buffer = read_file(argv[1]); if (!buffer) exit(1); // check for '#' as the first char, and if so, skip the first line char *cmd = buffer; if (cmd[0]=='#') { while (cmd[0] && cmd[0]!='\n') cmd++; if (cmd[0]=='\n') cmd++; } jshInit(); jsvInit(); jsiInit(false /* do not autoload!!! */); addNativeFunction("quit", nativeQuit); jsvUnLock(jspEvaluate(cmd, true)); int errCode = handleErrors(); free(buffer); isRunning = !errCode; bool isBusy = true; while (isRunning && (jsiHasTimers() || isBusy)) isBusy = jsiLoop(); jsiKill(); jsvKill(); jshKill(); exit(errCode); } else { printf("Unknown arguments!\n"); show_help(); exit(1); } printf("Size of JsVar is now %d bytes\n", (int)sizeof(JsVar)); printf("Size of JsVarRef is now %d bytes\n", (int)sizeof(JsVarRef)); #ifndef __MINGW32__ struct sigaction sa; sa.sa_handler = sig_handler; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); if (sigaction(SIGINT, &sa, NULL) == -1) printf("Adding SIGINT hook failed\n"); else printf("Added SIGINT hook\n"); if (sigaction(SIGHUP, &sa, NULL) == -1) printf("Adding SIGHUP hook failed\n"); else printf("Added SIGHUP hook\n"); if (sigaction(SIGTERM, &sa, NULL) == -1) printf("Adding SIGTERM hook failed\n"); else printf("Added SIGTERM hook\n"); #endif//!__MINGW32__ jshInit(); jsvInit(); jsiInit(true); addNativeFunction("quit", nativeQuit); addNativeFunction("interrupt", nativeInterrupt); while (isRunning) { jsiLoop(); } jsiConsolePrint("\n"); jsiKill(); jsvGarbageCollect(); jsvShowAllocated(); jsvKill(); jshKill(); return 0; }
/*JSON{ "type":"staticmethod", "class" : "Modules", "name" : "removeAllCached", "description" : "Remove all cached modules", "generate" : "jswrap_modules_removeAllCached" }*/ void jswrap_modules_removeAllCached() { JsVar *moduleList = jswrap_modules_getModuleList(); if (!moduleList) return; // out of memory jsvRemoveAllChildren(moduleList); jsvUnLock(moduleList); }
void getSerial(char *serialBuffer, int size){ JsVar *serialNumberString = jspEvaluate("getSerial().toString();"); jsvGetString(serialNumberString, serialBuffer, size); jsvUnLock(serialNumberString); }
bool socketClientConnectionsIdle(JsNetwork *net) { char buf[CHUNK]; 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_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) printf("send returned %d\r\n", num); 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, sizeof(buf)); //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) { //printf("closing now error=%d\r\n", error); 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 && 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 ther eis 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" : "WLAN", "name" : "disconnect", "generate" : "jswrap_wlan_disconnect", "description" : "Completely uninitialise and power down the CC3000. After this you'll have to use ```require(\"CC3000\").connect()``` again." }*/ void jswrap_wlan_disconnect(JsVar *wlanObj) { jsvUnLock(jsvObjectSetChild(wlanObj,JS_HIDDEN_CHAR_STR"DISC", jsvNewFromBool(true))); networkState = NETWORKSTATE_OFFLINE; // force offline //wlan_disconnect(); wlan_stop(); }
void jslGetNextToken() { jslGetNextToken_start: // Skip whitespace while (isWhitespace(lex->currCh)) jslGetNextCh(); // Search for comments if (lex->currCh=='/') { // newline comments if (jslNextCh()=='/') { while (lex->currCh && lex->currCh!='\n') jslGetNextCh(); jslGetNextCh(); goto jslGetNextToken_start; } // block comments if (jslNextCh()=='*') { jslGetNextCh(); jslGetNextCh(); while (lex->currCh && !(lex->currCh=='*' && jslNextCh()=='/')) jslGetNextCh(); if (!lex->currCh) { lex->tk = LEX_UNFINISHED_COMMENT; return; /* an unfinished multi-line comment. When in interactive console, detect this and make sure we accept new lines */ } jslGetNextCh(); jslGetNextCh(); goto jslGetNextToken_start; } } int lastToken = lex->tk; lex->tk = LEX_EOF; lex->tokenl = 0; // clear token string if (lex->tokenValue) { jsvUnLock(lex->tokenValue); lex->tokenValue = 0; } // record beginning of this token lex->tokenLastStart = jsvStringIteratorGetIndex(&lex->tokenStart.it) - 1; /* we don't lock here, because we know that the string itself will be locked * because of lex->sourceVar */ lex->tokenStart.it = lex->it; lex->tokenStart.currCh = lex->currCh; // tokens if (((unsigned char)lex->currCh) < jslJumpTableStart || ((unsigned char)lex->currCh) > jslJumpTableEnd) { // if unhandled by the jump table, just pass it through as a single character jslSingleChar(); } else { switch(jslJumpTable[((unsigned char)lex->currCh) - jslJumpTableStart]) { case JSLJT_ID: { while (isAlpha(lex->currCh) || isNumeric(lex->currCh) || lex->currCh=='$') { jslTokenAppendChar(lex->currCh); jslGetNextCh(); } lex->tk = LEX_ID; // We do fancy stuff here to reduce number of compares (hopefully GCC creates a jump table) switch (lex->token[0]) { case 'b': if (jslIsToken("break", 1)) lex->tk = LEX_R_BREAK; break; case 'c': if (jslIsToken("case", 1)) lex->tk = LEX_R_CASE; else if (jslIsToken("catch", 1)) lex->tk = LEX_R_CATCH; else if (jslIsToken("class", 1)) lex->tk = LEX_R_CLASS; else if (jslIsToken("const", 1)) lex->tk = LEX_R_CONST; else if (jslIsToken("continue", 1)) lex->tk = LEX_R_CONTINUE; break; case 'd': if (jslIsToken("default", 1)) lex->tk = LEX_R_DEFAULT; else if (jslIsToken("delete", 1)) lex->tk = LEX_R_DELETE; else if (jslIsToken("do", 1)) lex->tk = LEX_R_DO; else if (jslIsToken("debugger", 1)) lex->tk = LEX_R_DEBUGGER; break; case 'e': if (jslIsToken("else", 1)) lex->tk = LEX_R_ELSE; else if (jslIsToken("extends", 1)) lex->tk = LEX_R_EXTENDS; break; case 'f': if (jslIsToken("false", 1)) lex->tk = LEX_R_FALSE; else if (jslIsToken("finally", 1)) lex->tk = LEX_R_FINALLY; else if (jslIsToken("for", 1)) lex->tk = LEX_R_FOR; else if (jslIsToken("function", 1)) lex->tk = LEX_R_FUNCTION; break; case 'i': if (jslIsToken("if", 1)) lex->tk = LEX_R_IF; else if (jslIsToken("in", 1)) lex->tk = LEX_R_IN; else if (jslIsToken("instanceof", 1)) lex->tk = LEX_R_INSTANCEOF; break; case 'l': if (jslIsToken("let", 1)) lex->tk = LEX_R_LET; break; case 'n': if (jslIsToken("new", 1)) lex->tk = LEX_R_NEW; else if (jslIsToken("null", 1)) lex->tk = LEX_R_NULL; break; case 'o': if (jslIsToken("of", 1)) lex->tk = LEX_R_OF; break; case 'r': if (jslIsToken("return", 1)) lex->tk = LEX_R_RETURN; break; case 's': if (jslIsToken("static", 1)) lex->tk = LEX_R_STATIC; else if (jslIsToken("super", 1)) lex->tk = LEX_R_SUPER; else if (jslIsToken("switch", 1)) lex->tk = LEX_R_SWITCH; break; case 't': if (jslIsToken("this", 1)) lex->tk = LEX_R_THIS; else if (jslIsToken("throw", 1)) lex->tk = LEX_R_THROW; else if (jslIsToken("true", 1)) lex->tk = LEX_R_TRUE; else if (jslIsToken("try", 1)) lex->tk = LEX_R_TRY; else if (jslIsToken("typeof", 1)) lex->tk = LEX_R_TYPEOF; break; case 'u': if (jslIsToken("undefined", 1)) lex->tk = LEX_R_UNDEFINED; break; case 'w': if (jslIsToken("while", 1)) lex->tk = LEX_R_WHILE; break; case 'v': if (jslIsToken("var", 1)) lex->tk = LEX_R_VAR; else if (jslIsToken("void", 1)) lex->tk = LEX_R_VOID; break; default: break; } break; case JSLJT_NUMBER: { // TODO: check numbers aren't the wrong format bool canBeFloating = true; if (lex->currCh=='.') { jslGetNextCh(); if (isNumeric(lex->currCh)) { // it is a float lex->tk = LEX_FLOAT; jslTokenAppendChar('.'); } else { // it wasn't a number after all lex->tk = '.'; break; } } else { if (lex->currCh=='0') { jslTokenAppendChar(lex->currCh); jslGetNextCh(); if ((lex->currCh=='x' || lex->currCh=='X') || (lex->currCh=='b' || lex->currCh=='B') || (lex->currCh=='o' || lex->currCh=='O')) { canBeFloating = false; jslTokenAppendChar(lex->currCh); jslGetNextCh(); } } lex->tk = LEX_INT; while (isNumeric(lex->currCh) || (!canBeFloating && isHexadecimal(lex->currCh))) { jslTokenAppendChar(lex->currCh); jslGetNextCh(); } if (canBeFloating && lex->currCh=='.') { lex->tk = LEX_FLOAT; jslTokenAppendChar('.'); jslGetNextCh(); } } // parse fractional part if (lex->tk == LEX_FLOAT) { while (isNumeric(lex->currCh)) { jslTokenAppendChar(lex->currCh); jslGetNextCh(); } } // do fancy e-style floating point if (canBeFloating && (lex->currCh=='e'||lex->currCh=='E')) { lex->tk = LEX_FLOAT; jslTokenAppendChar(lex->currCh); jslGetNextCh(); if (lex->currCh=='-' || lex->currCh=='+') { jslTokenAppendChar(lex->currCh); jslGetNextCh(); } while (isNumeric(lex->currCh)) { jslTokenAppendChar(lex->currCh); jslGetNextCh(); } } } break; case JSLJT_STRING: jslLexString(); break; case JSLJT_EXCLAMATION: jslSingleChar(); if (lex->currCh=='=') { // != lex->tk = LEX_NEQUAL; jslGetNextCh(); if (lex->currCh=='=') { // !== lex->tk = LEX_NTYPEEQUAL; jslGetNextCh(); } } break; case JSLJT_PLUS: jslSingleChar(); if (lex->currCh=='=') { lex->tk = LEX_PLUSEQUAL; jslGetNextCh(); } else if (lex->currCh=='+') { lex->tk = LEX_PLUSPLUS; jslGetNextCh(); } break; case JSLJT_MINUS: jslSingleChar(); if (lex->currCh=='=') { lex->tk = LEX_MINUSEQUAL; jslGetNextCh(); } else if (lex->currCh=='-') { lex->tk = LEX_MINUSMINUS; jslGetNextCh(); } break; case JSLJT_AND: jslSingleChar(); if (lex->currCh=='=') { lex->tk = LEX_ANDEQUAL; jslGetNextCh(); } else if (lex->currCh=='&') { lex->tk = LEX_ANDAND; jslGetNextCh(); } break; case JSLJT_OR: jslSingleChar(); if (lex->currCh=='=') { lex->tk = LEX_OREQUAL; jslGetNextCh(); } else if (lex->currCh=='|') { lex->tk = LEX_OROR; jslGetNextCh(); } break; case JSLJT_TOPHAT: jslSingleChar(); if (lex->currCh=='=') { lex->tk = LEX_XOREQUAL; jslGetNextCh(); } break; case JSLJT_STAR: jslSingleChar(); if (lex->currCh=='=') { lex->tk = LEX_MULEQUAL; jslGetNextCh(); } break; case JSLJT_FORWARDSLASH: // yay! JS is so awesome. if (lastToken==LEX_EOF || lastToken=='!' || lastToken=='%' || lastToken=='&' || lastToken=='*' || lastToken=='+' || lastToken=='-' || lastToken=='/' || lastToken=='<' || lastToken=='=' || lastToken=='>' || lastToken=='?' || (lastToken>=_LEX_OPERATOR_START && lastToken<=_LEX_OPERATOR_END) || (lastToken>=_LEX_R_LIST_START && lastToken<=_LEX_R_LIST_END) || // keywords lastToken==LEX_R_CASE || lastToken==LEX_R_NEW || lastToken=='[' || lastToken=='{' || lastToken=='}' || lastToken=='(' || lastToken==',' || lastToken==';' || lastToken==':' || lastToken==LEX_ARROW_FUNCTION) { // EOF operator keyword case new [ { } ( , ; : => // phew. We're a regex jslLexRegex(); } else { jslSingleChar(); if (lex->currCh=='=') { lex->tk = LEX_DIVEQUAL; jslGetNextCh(); } } break; case JSLJT_PERCENT: jslSingleChar(); if (lex->currCh=='=') { lex->tk = LEX_MODEQUAL; jslGetNextCh(); } break; case JSLJT_EQUAL: jslSingleChar(); if (lex->currCh=='=') { // == lex->tk = LEX_EQUAL; jslGetNextCh(); if (lex->currCh=='=') { // === lex->tk = LEX_TYPEEQUAL; jslGetNextCh(); } } else if (lex->currCh=='>') { // => lex->tk = LEX_ARROW_FUNCTION; jslGetNextCh(); } break; case JSLJT_LESSTHAN: jslSingleChar(); if (lex->currCh=='=') { // <= lex->tk = LEX_LEQUAL; jslGetNextCh(); } else if (lex->currCh=='<') { // << lex->tk = LEX_LSHIFT; jslGetNextCh(); if (lex->currCh=='=') { // <<= lex->tk = LEX_LSHIFTEQUAL; jslGetNextCh(); } } break; case JSLJT_GREATERTHAN: jslSingleChar(); if (lex->currCh=='=') { // >= lex->tk = LEX_GEQUAL; jslGetNextCh(); } else if (lex->currCh=='>') { // >> lex->tk = LEX_RSHIFT; jslGetNextCh(); if (lex->currCh=='=') { // >>= lex->tk = LEX_RSHIFTEQUAL; jslGetNextCh(); } else if (lex->currCh=='>') { // >>> jslGetNextCh(); if (lex->currCh=='=') { // >>>= lex->tk = LEX_RSHIFTUNSIGNEDEQUAL; jslGetNextCh(); } else { lex->tk = LEX_RSHIFTUNSIGNED; } } } break; case JSLJT_SINGLECHAR: jslSingleChar(); break; default: assert(0);break; } } } }
void jsvArrayBufferIteratorSetIntegerValue(JsvArrayBufferIterator *it, JsVarInt value) { // FIXME: Do this without the allocation! JsVar *val = jsvNewFromInteger(value); jsvArrayBufferIteratorSetValue(it, val); jsvUnLock(val); }
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)) { 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 JsvArrayBufferIterator it; 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; } }
JsVar *jswrap_json_parse_internal(JsLex *lex) { switch (lex->tk) { case LEX_R_TRUE: jslGetNextToken(lex); return jsvNewFromBool(true); case LEX_R_FALSE: jslGetNextToken(lex); return jsvNewFromBool(false); case LEX_R_NULL: jslGetNextToken(lex); return jsvNewWithFlags(JSV_NULL); case LEX_INT: { long long v = stringToInt(jslGetTokenValueAsString(lex)); jslGetNextToken(lex); return jsvNewFromLongInteger(v); } case LEX_FLOAT: { JsVarFloat v = stringToFloat(jslGetTokenValueAsString(lex)); jslGetNextToken(lex); return jsvNewFromFloat(v); } case LEX_STR: { JsVar *a = jslGetTokenValueAsVar(lex); jslGetNextToken(lex); return a; } case '[': { JsVar *arr = jsvNewWithFlags(JSV_ARRAY); if (!arr) return 0; jslGetNextToken(lex); // [ while (lex->tk != ']') { JsVar *value = jswrap_json_parse_internal(lex); if (!value || (lex->tk!=']' && !jslMatch(lex, ','))) { jsvUnLock(value); jsvUnLock(arr); return 0; } jsvArrayPush(arr, value); jsvUnLock(value); } if (!jslMatch(lex, ']')) { jsvUnLock(arr); return 0; } return arr; } case '{': { JsVar *obj = jsvNewWithFlags(JSV_OBJECT); if (!obj) return 0; jslGetNextToken(lex); // { while (lex->tk == LEX_STR) { JsVar *key = jsvAsArrayIndexAndUnLock(jslGetTokenValueAsVar(lex)); jslGetNextToken(lex); JsVar *value = 0; if (!jslMatch(lex, ':') || !(value=jswrap_json_parse_internal(lex)) || (lex->tk!='}' && !jslMatch(lex, ','))) { jsvUnLock(key); jsvUnLock(value); jsvUnLock(obj); return 0; } jsvAddName(obj, jsvMakeIntoVariableName(key, value)); jsvUnLock(value); jsvUnLock(key); } if (!jslMatch(lex, '}')) { jsvUnLock(obj); return 0; } return obj; } default: return 0; // undefined = error } }
// httpParseHeaders(&receiveData, reqVar, true) // server // httpParseHeaders(&receiveData, resVar, false) // client bool httpParseHeaders(JsVar **receiveData, JsVar *objectForData, bool isServer) { // find /r/n/r/n int newlineIdx = 0; int strIdx = 0; int headerEnd = -1; JsvStringIterator it; jsvStringIteratorNew(&it, *receiveData, 0); while (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); if (ch == '\r') { if (newlineIdx==0) newlineIdx=1; else if (newlineIdx==2) newlineIdx=3; } else if (ch == '\n') { if (newlineIdx==1) newlineIdx=2; else if (newlineIdx==3) { headerEnd = strIdx+1; } } else newlineIdx=0; jsvStringIteratorNext(&it); strIdx++; } jsvStringIteratorFree(&it); // skip if we have no header if (headerEnd<0) return false; // Now parse the header JsVar *vHeaders = jsvNewWithFlags(JSV_OBJECT); if (!vHeaders) return true; jsvUnLock(jsvAddNamedChild(objectForData, vHeaders, "headers")); strIdx = 0; int firstSpace = -1; int secondSpace = -1; int firstEOL = -1; int lineNumber = 0; int lastLineStart = 0; int colonPos = 0; //jsiConsolePrintStringVar(receiveData); jsvStringIteratorNew(&it, *receiveData, 0); while (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); if (ch==' ' || ch=='\r') { if (firstSpace<0) firstSpace = strIdx; else if (secondSpace<0) secondSpace = strIdx; } if (ch == ':' && colonPos<0) colonPos = strIdx; if (ch == '\r') { if (firstEOL<0) firstEOL=strIdx; if (lineNumber>0 && colonPos>lastLineStart && lastLineStart<strIdx) { JsVar *hVal = jsvNewFromEmptyString(); if (hVal) jsvAppendStringVar(hVal, *receiveData, (size_t)colonPos+2, (size_t)(strIdx-(colonPos+2))); JsVar *hKey = jsvNewFromEmptyString(); if (hKey) { jsvMakeIntoVariableName(hKey, hVal); jsvAppendStringVar(hKey, *receiveData, (size_t)lastLineStart, (size_t)(colonPos-lastLineStart)); jsvAddName(vHeaders, hKey); jsvUnLock(hKey); } jsvUnLock(hVal); } lineNumber++; colonPos=-1; } if (ch == '\r' || ch == '\n') { lastLineStart = strIdx+1; } jsvStringIteratorNext(&it); strIdx++; } jsvStringIteratorFree(&it); jsvUnLock(vHeaders); // try and pull out methods/etc if (isServer) { jsvObjectSetChildAndUnLock(objectForData, "method", jsvNewFromStringVar(*receiveData, 0, (size_t)firstSpace)); jsvObjectSetChildAndUnLock(objectForData, "url", jsvNewFromStringVar(*receiveData, (size_t)(firstSpace+1), (size_t)(secondSpace-(firstSpace+1)))); } else { jsvObjectSetChildAndUnLock(objectForData, "httpVersion", jsvNewFromStringVar(*receiveData, 5, (size_t)firstSpace-5)); jsvObjectSetChildAndUnLock(objectForData, "statusCode", jsvNewFromStringVar(*receiveData, (size_t)(firstSpace+1), (size_t)(secondSpace-(firstSpace+1)))); jsvObjectSetChildAndUnLock(objectForData, "statusMessage", jsvNewFromStringVar(*receiveData, (size_t)(secondSpace+1), (size_t)(firstEOL-(secondSpace+1)))); } // strip out the header JsVar *afterHeaders = jsvNewFromStringVar(*receiveData, (size_t)headerEnd, JSVAPPENDSTRINGVAR_MAXLENGTH); jsvUnLock(*receiveData); *receiveData = afterHeaders; return true; }
void clientRequestWrite(JsNetwork *net, JsVar *httpClientReqVar, JsVar *data) { SocketType socketType = socketGetType(httpClientReqVar); // Append data to sendData JsVar *sendData = jsvObjectGetChild(httpClientReqVar, HTTP_NAME_SEND_DATA, 0); if (!sendData) { JsVar *options = 0; // Only append a header if we're doing HTTP AND we haven't already connected if ((socketType&ST_TYPE_MASK) == ST_HTTP) if (jsvGetIntegerAndUnLock(jsvObjectGetChild(httpClientReqVar, HTTP_NAME_SOCKET, 0))==0) options = jsvObjectGetChild(httpClientReqVar, HTTP_NAME_OPTIONS_VAR, 0); if (options) { // We're an HTTP client - make a header JsVar *method = jsvObjectGetChild(options, "method", 0); JsVar *path = jsvObjectGetChild(options, "path", 0); sendData = jsvVarPrintf("%v %v HTTP/1.0\r\nUser-Agent: Espruino "JS_VERSION"\r\nConnection: close\r\n", method, path); jsvUnLock2(method, path); JsVar *headers = jsvObjectGetChild(options, "headers", 0); bool hasHostHeader = false; if (jsvIsObject(headers)) { JsVar *hostHeader = jsvObjectGetChild(headers, "Host", 0); hasHostHeader = hostHeader!=0; jsvUnLock(hostHeader); httpAppendHeaders(sendData, headers); // if Transfer-Encoding:chunked was set, subsequent writes need to 'chunk' the data that is sent if (jsvIsStringEqualAndUnLock(jsvObjectGetChild(headers, "Transfer-Encoding", 0), "chunked")) { jsvObjectSetChildAndUnLock(httpClientReqVar, HTTP_NAME_CHUNKED, jsvNewFromBool(true)); } } jsvUnLock(headers); if (!hasHostHeader) { JsVar *host = jsvObjectGetChild(options, "host", 0); int port = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(options, "port", 0)); if (port>0 && port!=80) jsvAppendPrintf(sendData, "Host: %v:%d\r\n", host, port); else jsvAppendPrintf(sendData, "Host: %v\r\n", host); jsvUnLock(host); } // finally add ending newline jsvAppendString(sendData, "\r\n"); } else { // !options // We're not HTTP (or were already connected), so don't send any header sendData = jsvNewFromString(""); } jsvObjectSetChild(httpClientReqVar, HTTP_NAME_SEND_DATA, sendData); jsvUnLock(options); } // We have data and aren't out of memory... if (data && sendData) { // append the data to what we want to send JsVar *s = jsvAsString(data, false); if (s) { if ((socketType&ST_TYPE_MASK) == ST_HTTP && jsvGetBoolAndUnLock(jsvObjectGetChild(httpClientReqVar, HTTP_NAME_CHUNKED, 0))) { // If we asked to send 'chunked' data, we need to wrap it up, // prefixed with the length jsvAppendPrintf(sendData, "%x\r\n%v\r\n", jsvGetStringLength(s), s); } else { jsvAppendStringVarComplete(sendData,s); } jsvUnLock(s); } } jsvUnLock(sendData); if ((socketType&ST_TYPE_MASK) == ST_HTTP) { // on HTTP we connect after the first write clientRequestConnect(net, httpClientReqVar); } }
bool socketIdle(JsNetwork *net) { if (networkState != NETWORKSTATE_ONLINE) { // clear all clients and servers _socketCloseAllConnections(net); return false; } bool hadSockets = false; JsVar *arr = socketGetArray(HTTP_ARRAY_HTTP_SERVERS,false); if (arr) { JsvObjectIterator it; jsvObjectIteratorNew(&it, arr); while (jsvObjectIteratorHasValue(&it)) { hadSockets = true; JsVar *server = jsvObjectIteratorGetValue(&it); int sckt = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(server,HTTP_NAME_SOCKET,0))-1; // so -1 if undefined int theClient = netAccept(net, sckt); if (theClient >= 0) { SocketType socketType = socketGetType(server); if ((socketType&ST_TYPE_MASK) == ST_HTTP) { JsVar *req = jspNewObject(0, "httpSRq"); JsVar *res = jspNewObject(0, "httpSRs"); if (res && req) { // out of memory? socketSetType(req, ST_HTTP); JsVar *arr = socketGetArray(HTTP_ARRAY_HTTP_SERVER_CONNECTIONS, true); if (arr) { jsvArrayPush(arr, req); jsvUnLock(arr); } jsvObjectSetChild(req, HTTP_NAME_RESPONSE_VAR, res); jsvObjectSetChild(req, HTTP_NAME_SERVER_VAR, server); jsvObjectSetChildAndUnLock(req, HTTP_NAME_SOCKET, jsvNewFromInteger(theClient+1)); } jsvUnLock2(req, res); } else { // Normal sockets JsVar *sock = jspNewObject(0, "Socket"); if (sock) { // out of memory? socketSetType(sock, ST_NORMAL); JsVar *arr = socketGetArray(HTTP_ARRAY_HTTP_CLIENT_CONNECTIONS, true); if (arr) { jsvArrayPush(arr, sock); jsvUnLock(arr); } jsvObjectSetChildAndUnLock(sock, HTTP_NAME_SOCKET, jsvNewFromInteger(theClient+1)); jsiQueueObjectCallbacks(server, HTTP_NAME_ON_CONNECT, &sock, 1); jsvUnLock(sock); } } } jsvUnLock(server); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); jsvUnLock(arr); } if (socketServerConnectionsIdle(net)) hadSockets = true; if (socketClientConnectionsIdle(net)) hadSockets = true; netCheckError(net); return hadSockets; }
int espruino(int argc, char **argv) { int i; for (i=1;i<argc;i++) { if (argv[i][0]=='-') { // option char *a = argv[i]; if (!strcmp(a,"-h") || !strcmp(a,"--help")) { show_help(); return (1); } else if (!strcmp(a,"-e") || !strcmp(a,"--eval")) { if (i+1>=argc) die("Expecting an extra argument\n"); jshInit(); jsiInit(true); jspAddNativeFunction(jsiGetParser(), "function quit()", nativeQuit); jsvUnLock(jspEvaluate(jsiGetParser(), argv[i+1])); isRunning = true; //while (isRunning && jsiHasTimers()) jsiLoop(); jsiKill(); jshKill(); return (0); } else if (!strcmp(a,"--test")) { if (i+1>=argc) die("Expecting an extra argument\n"); bool ok = run_test(argv[i+1]); return (ok ? 0 : 1); } else if (!strcmp(a,"--test-all")) { bool ok = run_all_tests(); return (ok ? 0 : 1); } else if (!strcmp(a,"--test-mem-all")) { bool ok = run_memory_tests(0); return (ok ? 0 : 1); } else if (!strcmp(a,"--test-mem")) { if (i+1>=argc) die("Expecting an extra argument\n"); bool ok = run_memory_test(argv[i+1], 0); return (ok ? 0 : 1); } else if (!strcmp(a,"--test-mem-n")) { if (i+2>=argc) die("Expecting an extra 2 arguments\n"); bool ok = run_memory_test(argv[i+1], atoi(argv[i+2])); return (ok ? 0 : 1); } else { printf("Unknown Argument %s\n", a); show_help(); return -1; } } } if (argc==1) { printf("Interactive mode.\n"); } else if (argc==2) { // single file - just run it char *buffer = (char *)read_file(argv[1]); if (!buffer) return (1); // check for '#' as the first char, and if so, skip the first line char *cmd = buffer; if (cmd[0]=='#') { while (cmd[0] && cmd[0]!='\n') cmd++; if (cmd[0]=='\n') cmd++; } jshInit(); jsiInit(false /* do not autoload!!! */); jspAddNativeFunction(jsiGetParser(), "function quit()", nativeQuit); jsvUnLock(jspEvaluate(jsiGetParser(), cmd )); free(buffer); //isRunning = true; //while (isRunning && jsiHasTimers()) jsiLoop(); jsiKill(); jshKill(); return -1; } else { printf("Unknown arguments!\n"); show_help(); return -1; } printf("Size of JsVar is now %d bytes\n", (int)sizeof(JsVar)); printf("Size of JsVarRef is now %d bytes\n", (int)sizeof(JsVarRef)); jshInit(); jsiInit(true); jspAddNativeFunction(jsiGetParser(), "function quit()", nativeQuit); jspAddNativeFunction(jsiGetParser(), "function interrupt()", nativeInterrupt); while (isRunning) { jsiLoop(); } jsiConsolePrint("\n"); jsiKill(); jsvShowAllocated(); jshKill(); return 0; }
/*JSON{ "type":"staticmethod", "class" : "url", "name" : "parse", "generate" : "jswrap_url_parse", "description" : ["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\"}}`"], "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`" ] }*/ 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 jsvUnLock(jsvObjectSetChild(obj, "method", jsvNewFromString("GET"))); jsvUnLock(jsvObjectSetChild(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, "/"); jsvUnLock(jsvObjectSetChild(obj, "path", v)); v = jsvNewFromStringVar(url, (size_t)pathStart, (size_t)((searchStart>=0)?(searchStart-pathStart):JSVAPPENDSTRINGVAR_MAXLENGTH)); if (jsvGetStringLength(v)==0) jsvAppendString(v, "/"); jsvUnLock(jsvObjectSetChild(obj, "pathname", v)); jsvUnLock(jsvObjectSetChild(obj, "search", (searchStart>=0)?jsvNewFromStringVar(url, (size_t)searchStart, JSVAPPENDSTRINGVAR_MAXLENGTH):jsvNewNull())); if (portNumber<=0 || portNumber>65535) portNumber=80; jsvUnLock(jsvObjectSetChild(obj, "port", 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) { jsvMakeIntoVariableName(key, val); jsvAddName(query, key); jsvUnLock(key); jsvUnLock(val); key = jsvNewFromEmptyString(); val = jsvNewFromEmptyString(); hadEquals = false; } } else if (!hadEquals && ch=='=') { hadEquals = true; } else { if (hadEquals) jsvAppendCharacter(val, ch); else jsvAppendCharacter(key, ch); } jsvStringIteratorNext(&it); charIdx++; } jsvStringIteratorFree(&it); jsvUnLock(queryStr); if (jsvGetStringLength(key)>0 || jsvGetStringLength(val)>0) { jsvMakeIntoVariableName(key, val); jsvAddName(query, key); } jsvUnLock(key); jsvUnLock(val); } jsvUnLock(jsvObjectSetChild(obj, "query", query)); return obj; }
/** This is for Object.keys and Object. */ JsVar *jswrap_object_keys_or_property_names( JsVar *obj, bool includeNonEnumerable, ///< include 'hidden' items bool includePrototype) { ///< include items for the prototype too (for autocomplete) JsVar *arr = jsvNewWithFlags(JSV_ARRAY); if (!arr) return 0; // strings are iterable, but we shouldn't try and show keys for them if (jsvIsIterable(obj)) { JsvIsInternalChecker checkerFunction = jsvGetInternalFunctionCheckerFor(obj); JsvIterator it; jsvIteratorNew(&it, obj); while (jsvIteratorHasElement(&it)) { JsVar *key = jsvIteratorGetKey(&it); if (!(checkerFunction && checkerFunction(key)) || (jsvIsStringEqual(key, JSPARSE_CONSTRUCTOR_VAR))) { /* Not sure why constructor is included in getOwnPropertyNames, but * not in for (i in ...) but it is, so we must explicitly override the * check in jsvIsInternalObjectKey! */ JsVar *name = jsvAsArrayIndexAndUnLock(jsvCopyNameOnly(key, false, false)); if (name) { jsvArrayPushAndUnLock(arr, name); } } jsvUnLock(key); jsvIteratorNext(&it); } jsvIteratorFree(&it); } /* Search our built-in symbol table Assume that ALL builtins are non-enumerable. This isn't great but seems to work quite well right now! */ if (includeNonEnumerable) { const JswSymList *symbols = 0; JsVar *protoOwner = jspGetPrototypeOwner(obj); if (protoOwner) { // If protoOwner then this is the prototype (protoOwner is the object) symbols = jswGetSymbolListForObjectProto(protoOwner); jsvUnLock(protoOwner); } else if (!jsvIsObject(obj) || jsvIsRoot(obj)) { // get symbols, but only if we're not doing it on a basic object symbols = jswGetSymbolListForObject(obj); } if (symbols) { unsigned int i; for (i=0;i<symbols->symbolCount;i++) jsvArrayAddString(arr, &symbols->symbolChars[symbols->symbols[i].strOffset]); } if (includePrototype) { symbols = jswGetSymbolListForObjectProto(obj); if (symbols) { unsigned int i; for (i=0;i<symbols->symbolCount;i++) jsvArrayAddString(arr, &symbols->symbolChars[symbols->symbols[i].strOffset]); } } if (jsvIsArray(obj) || jsvIsString(obj)) { jsvArrayAddString(arr, "length"); } } return arr; }
bool socketServerConnectionsIdle(JsNetwork *net) { char buf[CHUNK]; 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,sizeof(buf)); 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)) { // 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) closeConnectionNow = true; 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; }