int nativeCallGetCType(JsLex *lex) { if (lex->tk == LEX_R_VOID) { jslMatch(lex, LEX_R_VOID); return JSWAT_VOID; } if (lex->tk == LEX_ID) { int t = -1; char *name = jslGetTokenValueAsString(lex); if (strcmp(name,"int")==0) t=JSWAT_INT32; if (strcmp(name,"double")==0) t=JSWAT_JSVARFLOAT; if (strcmp(name,"bool")==0) t=JSWAT_BOOL; if (strcmp(name,"Pin")==0) t=JSWAT_PIN; if (strcmp(name,"JsVar")==0) t=JSWAT_JSVAR; jslMatch(lex, LEX_ID); return t; } return -1; // unknown }
/*JSON{ "type" : "staticmethod", "ifndef" : "SAVE_ON_FLASH", "class" : "E", "name" : "nativeCall", "generate" : "jswrap_espruino_nativeCall", "params" : [ ["addr","int","The address in memory of the function"], ["sig","JsVar","The signature of the call, `returnType (arg1,arg2,...)`. Allowed types are `void`,`bool`,`int`,`double`,`Pin`,`JsVar`"] ], "return" : ["JsVar","The native function"] } ADVANCED: This is a great way to crash Espruino if you're not sure what you are doing Create a native function that executes the code at the given address. Eg. `E.nativeCall(0x08012345,'double (double,double)')(1.1, 2.2)` If you're executing a thumb function, you'll almost certainly need to set the bottom bit of the address to 1. Note it's not guaranteed that the call signature you provide can be used - it has to be something that a function in Espruino already uses. */ JsVar *jswrap_espruino_nativeCall(JsVarInt addr, JsVar *signature) { unsigned int argTypes = 0; if (jsvIsUndefined(signature)) { // Nothing to do } else if (jsvIsString(signature)) { JsLex lex; jslInit(&lex, signature); int argType; bool ok = true; int argNumber = 0; argType = nativeCallGetCType(&lex); if (argType>=0) argTypes |= (unsigned)argType << (JSWAT_BITS * argNumber++); else ok = false; if (ok) ok = jslMatch(&lex, '('); while (ok && lex.tk!=LEX_EOF && lex.tk!=')') { argType = nativeCallGetCType(&lex); if (argType>=0) { argTypes |= (unsigned)argType << (JSWAT_BITS * argNumber++); if (lex.tk!=')') ok = jslMatch(&lex, ','); } else ok = false; } if (ok) ok = jslMatch(&lex, ')'); jslKill(&lex); if (argTypes & (unsigned int)~0xFFFF) ok = false; if (!ok) { jsExceptionHere(JSET_ERROR, "Error Parsing signature at argument number %d", argNumber); return 0; } } else { jsExceptionHere(JSET_ERROR, "Invalid Signature"); return 0; } return jsvNewNativeFunction((void *)(size_t)addr, (unsigned short)argTypes); }
JsVar *jswrap_json_parse_internal() { 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 '-': { jslGetNextToken(lex); if (lex->tk!=LEX_INT && lex->tk!=LEX_FLOAT) return 0; JsVar *v = jswrap_json_parse_internal(lex); JsVar *zero = jsvNewFromInteger(0); JsVar *r = jsvMathsOp(zero, v, '-'); jsvUnLock2(v, zero); return r; } 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 = jsvNewEmptyArray(); if (!arr) return 0; jslGetNextToken(lex); // [ while (lex->tk != ']' && !jspHasError()) { JsVar *value = jswrap_json_parse_internal(lex); if (!value || (lex->tk!=']' && !jslMatch(','))) { jsvUnLock2(value, arr); return 0; } jsvArrayPush(arr, value); jsvUnLock(value); } if (!jslMatch(']')) { jsvUnLock(arr); return 0; } return arr; } case '{': { JsVar *obj = jsvNewObject(); if (!obj) return 0; jslGetNextToken(lex); // { while (lex->tk == LEX_STR && !jspHasError()) { JsVar *key = jsvAsArrayIndexAndUnLock(jslGetTokenValueAsVar(lex)); jslGetNextToken(lex); JsVar *value = 0; if (!jslMatch(':') || !(value=jswrap_json_parse_internal(lex)) || (lex->tk!='}' && !jslMatch(','))) { jsvUnLock3(key, value, obj); return 0; } jsvAddName(obj, jsvMakeIntoVariableName(key, value)); jsvUnLock2(value, key); } if (!jslMatch('}')) { jsvUnLock(obj); return 0; } return obj; } default: { char buf[32]; jslTokenAsString(lex->tk, buf, 32); jsExceptionHere(JSET_SYNTAXERROR, "Expecting a valid value, got %s", buf); return 0; // undefined = error } } }
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 '-': { jslGetNextToken(lex); if (lex->tk!=LEX_INT && lex->tk!=LEX_FLOAT) return 0; JsVar *v = jswrap_json_parse_internal(lex); JsVar *zero = jsvNewFromInteger(0); JsVar *r = jsvMathsOp(zero, v, '-'); jsvUnLock(v); jsvUnLock(zero); return r; } 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 } }