Beispiel #1
0
/*JSON{
  "type" : "method",
  "ifndef" : "SAVE_ON_FLASH",
  "class" : "RegExp",
  "name" : "exec",
  "params" : [
    ["str","JsVar","A string to match on"]
  ],
  "generate" : "jswrap_regexp_exec",
  "return" : ["JsVar","A result array, or null"]
}
Test this regex on a string - returns a result array on success, or `null` otherwise.


`/Wo/.exec("Hello World")` will return:

```
[
 "Wo",
 "index": 6,
 "input": "Hello World"
]
```

Or with groups `/W(o)rld/.exec("Hello World")` returns:

```
[
 "World",
 "o", "index": 6,
 "input": "Hello World"
]
```

 */
JsVar *jswrap_regexp_exec(JsVar *parent, JsVar *arg) {
  JsVar *str = jsvAsString(arg);
  JsVarInt lastIndex = jsvGetIntegerAndUnLock(jsvObjectGetChild(parent, "lastIndex", 0));
  JsVar *regex = jsvObjectGetChild(parent, "source", 0);
  if (!jsvIsString(regex)) {
    jsvUnLock2(str,regex);
    return 0;
  }
  size_t regexLen = jsvGetStringLength(regex);
  char *regexPtr = (char *)alloca(regexLen+1);
  if (!regexPtr) {
    jsvUnLock2(str,regex);
    return 0;
  }
  jsvGetString(regex, regexPtr, regexLen+1);
  jsvUnLock(regex);
  JsVar *rmatch = match(regexPtr, str, (size_t)lastIndex, jswrap_regexp_hasFlag(parent,'i'));
  jsvUnLock(str);
  if (!rmatch) {
    rmatch = jsvNewWithFlags(JSV_NULL);
    lastIndex = 0;
  } else {
    // if it's global, set lastIndex
    if (jswrap_regexp_hasFlag(parent,'g')) {
      JsVar *matchStr = jsvGetArrayItem(rmatch,0);
      lastIndex = jsvGetIntegerAndUnLock(jsvObjectGetChild(rmatch, "index", 0)) +
                  (JsVarInt)jsvGetStringLength(matchStr);
      jsvUnLock(matchStr);
    } else
      lastIndex = 0;
  }
  jsvObjectSetChildAndUnLock(parent, "lastIndex", jsvNewFromInteger(lastIndex));
  return rmatch;
}
Beispiel #2
0
/*JSON{
  "type" : "function",
  "name" : "clearTimeout",
  "generate" : "jswrap_interface_clearTimeout",
  "params" : [
    ["id","JsVar","The id returned by a previous call to setTimeout"]
  ]
}
Clear the Timeout that was created with setTimeout, for example:

```var id = setTimeout(function () { print('foo'); }, 1000);```

```clearTimeout(id);```

If no argument is supplied, all timers and intervals are stopped
 */
void _jswrap_interface_clearTimeoutOrInterval(JsVar *idVar, bool isTimeout) {
  JsVar *timerArrayPtr = jsvLock(timerArray);
  if (jsvIsUndefined(idVar)) {
    /* Delete all timers EXCEPT those with a 'watch' field,
     as those were generated by jsinteractive.c for debouncing watches */
    JsvObjectIterator it;
    jsvObjectIteratorNew(&it, timerArrayPtr);
    while (jsvObjectIteratorHasValue(&it)) {
      JsVar *timerPtr = jsvObjectIteratorGetValue(&it);
      JsVar *watchPtr = jsvObjectGetChild(timerPtr, "watch", 0);
      if (!watchPtr)
        jsvObjectIteratorRemoveAndGotoNext(&it, timerArrayPtr);
      else
        jsvObjectIteratorNext(&it); 
      jsvUnLock2(watchPtr, timerPtr);
    }
    jsvObjectIteratorFree(&it);
  } else {
    JsVar *child = jsvIsBasic(idVar) ? jsvFindChildFromVar(timerArrayPtr, idVar, false) : 0;
    if (child) {
      JsVar *timerArrayPtr = jsvLock(timerArray);
      jsvRemoveChild(timerArrayPtr, child);
      jsvUnLock2(child, timerArrayPtr);
    } else {
      if (isTimeout)
        jsExceptionHere(JSET_ERROR, "Unknown Timeout");
      else
        jsExceptionHere(JSET_ERROR, "Unknown Interval");
    }
  }
  jsvUnLock(timerArrayPtr);
  jsiTimersChanged(); // mark timers as changed
}
Beispiel #3
0
/*JSON{
  "type" : "function",
  "name" : "edit",
  "generate" : "jswrap_interface_edit",
  "params" : [
    ["funcName","JsVar","The name of the function to edit (either a string or just the unquoted name)"]
  ]
}
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.
 */
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, 0);
  }

  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;
      jsvUnLock2(inRoot, 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 {
      jsExceptionHere(JSET_ERROR, "Edit should be called with the name of a function");
    }
  } else {
    jsExceptionHere(JSET_ERROR, "Edit should be called with edit(funcName) or edit('funcName')");
  }
  jsvUnLock2(func, funcName);
}
Beispiel #4
0
/*JSON{
  "type" : "method",
  "class" : "Array",
  "name" : "unshift",
  "ifndef" : "SAVE_ON_FLASH",
  "generate" : "jswrap_array_unshift",
  "params" : [
    ["elements","JsVarArray","One or more items to add to the beginning of the array"]
  ],
  "return" : ["int","The new array length"]
}
Add one or more items to the start of the array, and return its new length.

This is the opposite of `[1,2,3].push(4)`, which puts one or more elements on the end.
 */
JsVarInt jswrap_array_unshift(JsVar *parent, JsVar *elements) {
  // just use splice, as this does all the hard work for us
  JsVar *nRemove = jsvNewFromInteger(0);
  jsvUnLock2(jswrap_array_splice(parent, 0, nRemove, elements), nRemove);
  // return new length
  return jsvGetLength(parent);
}
Beispiel #5
0
JsVar *clientRequestNew(SocketType socketType, JsVar *options, JsVar *callback) {
    JsVar *arr = socketGetArray(HTTP_ARRAY_HTTP_CLIENT_CONNECTIONS,true);
    if (!arr) return 0;
    JsVar *req, *res = 0;
    if ((socketType&ST_TYPE_MASK)==ST_HTTP) {
        res = jspNewObject(0, "httpCRs");
        if (!res) {
            jsvUnLock(arr);    // out of memory?
            return 0;
        }
        req = jspNewObject(0, "httpCRq");
    } else {
        req = jspNewObject(0, "Socket");
    }
    if (req) { // out of memory?
        socketSetType(req, socketType);
        if (callback != NULL)
            jsvUnLock(jsvAddNamedChild(req, callback, HTTP_NAME_ON_CONNECT));

        jsvArrayPush(arr, req);
        if (res)
            jsvObjectSetChild(req, HTTP_NAME_RESPONSE_VAR, res);
        jsvObjectSetChild(req, HTTP_NAME_OPTIONS_VAR, options);
    }
    jsvUnLock2(res, arr);
    return req;
}
Beispiel #6
0
/*JSON{
  "type" : "staticmethod",
  "class" : "Object",
  "name" : "getOwnPropertyDescriptor",
  "generate" : "jswrap_object_getOwnPropertyDescriptor",
  "params" : [
    ["obj","JsVar","The object"],
    ["name","JsVar","The name of the property"]
  ],
  "return" : ["JsVar","An object with a description of the property. The values of writable/enumerable/configurable may not be entirely correct due to Espruino's implementation."]
}
Get information on the given property in the object, or undefined
 */
JsVar *jswrap_object_getOwnPropertyDescriptor(JsVar *parent, JsVar *name) {
  if (!jswrap_object_hasOwnProperty(parent, name))
    return 0;

  JsVar *propName = jsvAsArrayIndex(name);
  JsVar *varName = jspGetVarNamedField(parent, propName, true);
  jsvUnLock(propName);

  assert(varName); // we had it! (apparently)
  if (!varName) return 0;

  JsVar *obj = jsvNewObject();
  if (!obj) {
    jsvUnLock(varName);
    return 0;
  }

  //jsvTrace(varName, 5);
  JsVar *var = jsvSkipName(varName);

  bool isBuiltIn = jsvIsNewChild(varName);
  JsvIsInternalChecker checkerFunction = jsvGetInternalFunctionCheckerFor(parent);

  jsvObjectSetChild(obj, "value", var);
  jsvObjectSetChildAndUnLock(obj, "writable", jsvNewFromBool(true));
  jsvObjectSetChildAndUnLock(obj, "enumerable", jsvNewFromBool(!checkerFunction || !checkerFunction(varName)));
  jsvObjectSetChildAndUnLock(obj, "configurable", jsvNewFromBool(!isBuiltIn));

  jsvUnLock2(var, varName);
  return obj;
}
Beispiel #7
0
/*JSON{
  "type" : "staticmethod",
  "class" : "Promise",
  "name" : "all",
  "generate" : "jswrap_promise_all",
  "params" : [
    ["promises","JsVar","An array of promises"]
  ],
  "return" : ["JsVar","A new Promise"]
}
Return a new promise that is resolved when all promises in the supplied
array are resolved.
*/
JsVar *jswrap_promise_all(JsVar *arr) {
  if (!jsvIsIterable(arr)) {
    jsExceptionHere(JSET_TYPEERROR, "Expecting something iterable, got %t", arr);
    return 0;
  }
  JsVar *promise = jspNewObject(0, "Promise");
  if (!promise) return 0;
  JsVar *resolve = jsvNewNativeFunction((void (*)(void))jswrap_promise_all_resolve, JSWAT_VOID|JSWAT_THIS_ARG|(JSWAT_JSVAR<<JSWAT_BITS));
  JsVar *reject = jsvNewNativeFunction((void (*)(void))jswrap_promise_all_reject, JSWAT_VOID|JSWAT_THIS_ARG|(JSWAT_JSVAR<<JSWAT_BITS));
  if (resolve && reject) {
    jsvObjectSetChild(resolve, JSPARSE_FUNCTION_THIS_NAME, promise);
    jsvObjectSetChild(reject, JSPARSE_FUNCTION_THIS_NAME, promise);
    int promises = 0;
    JsvObjectIterator it;
    jsvObjectIteratorNew(&it, arr);
    while (jsvObjectIteratorHasValue(&it)) {
      JsVar *p = jsvObjectIteratorGetValue(&it);
      jsvUnLock(jswrap_promise_then(p, resolve));
      jsvUnLock(jswrap_promise_catch(p, reject));
      jsvUnLock(p);
      promises++;
      jsvObjectIteratorNext(&it);
    }
    jsvObjectIteratorFree(&it);

    jsvObjectSetChildAndUnLock(promise, JS_PROMISE_COUNT_NAME, jsvNewFromInteger(promises));
    jsvObjectSetChildAndUnLock(promise, JS_PROMISE_RESULT_NAME, jsvNewEmptyArray());
  }
  jsvUnLock2(resolve, reject);
  return promise;
}
Beispiel #8
0
/*JSON{
  "type" : "constructor",
  "class" : "Object",
  "name" : "Object",
  "generate" : "jswrap_object_constructor",
  "params" : [
    ["value","JsVar","A single value to be converted to an object"]
  ],
  "return" : ["JsVar","An Object"]
}
Creates an Object from the supplied argument
 */
JsVar *jswrap_object_constructor(JsVar *value) {
  if (jsvIsObject(value) || jsvIsArray(value) || jsvIsFunction(value))
    return jsvLockAgain(value);
  const char *objName = jswGetBasicObjectName(value);
  JsVar *funcName = objName ? jspGetNamedVariable(objName) : 0;
  if (!funcName) return jsvNewObject();
  JsVar *func = jsvSkipName(funcName);
  JsVar *result = jspeFunctionCall(func, funcName, 0, false, 1, &value);
  jsvUnLock2(funcName, func);
  return result;
}
Beispiel #9
0
static void jswrap_waveform_start(JsVar *waveform, Pin pin, JsVarFloat freq, JsVar *options, bool isWriting) {
  bool running = jsvGetBoolAndUnLock(jsvObjectGetChild(waveform, "running", 0));
  if (running) {
    jsExceptionHere(JSET_ERROR, "Waveform is already running");
    return;
  }
  if (!jshIsPinValid(pin)) {
    jsExceptionHere(JSET_ERROR, "Invalid pin");
    return;
  }
  if (!isfinite(freq) || freq<0.001) {
    jsExceptionHere(JSET_ERROR, "Frequency must be above 0.001Hz");
    return;
  }

  JsSysTime startTime = jshGetSystemTime();
  bool repeat = false;
  if (jsvIsObject(options)) {
    JsVarFloat t = jsvGetFloatAndUnLock(jsvObjectGetChild(options, "time", 0));
    if (isfinite(t) && t>0)
      startTime = jshGetTimeFromMilliseconds(t*1000);
    repeat = jsvGetBoolAndUnLock(jsvObjectGetChild(options, "repeat", 0));
  } else if (!jsvIsUndefined(options)) {
    jsExceptionHere(JSET_ERROR, "Expecting options to be undefined or an Object, not %t", options);
  }

  bool is16Bit = false;
  JsVar *buffer = jswrap_waveform_getBuffer(waveform,0, &is16Bit);
  JsVar *buffer2 = jswrap_waveform_getBuffer(waveform,1,0);

  UtilTimerEventType eventType;

  if (is16Bit) {
    eventType = isWriting ? UET_WRITE_SHORT : UET_READ_SHORT;
  } else {
    eventType = isWriting ? UET_WRITE_BYTE : UET_READ_BYTE;
  }


  // And finally set it up
  if (!jstStartSignal(startTime, jshGetTimeFromMilliseconds(1000.0 / freq), pin, buffer, repeat?(buffer2?buffer2:buffer):0, eventType))
    jsWarn("Unable to schedule a timer");
  jsvUnLock2(buffer,buffer2);

  jsvObjectSetChildAndUnLock(waveform, "running", jsvNewFromBool(true));
  jsvObjectSetChildAndUnLock(waveform, "freq", jsvNewFromFloat(freq));
  // Add to our list of active waveforms
  JsVar *waveforms = jsvObjectGetChild(execInfo.hiddenRoot, JSI_WAVEFORM_NAME, JSV_ARRAY);
  if (waveforms) {
    jsvArrayPush(waveforms, waveform);
    jsvUnLock(waveforms);
  }
}
Beispiel #10
0
// Get the path associated with a device. Returns false on failure.
bool jshGetDevicePath(IOEventFlags device, char *buf, size_t bufSize) {
  JsVar *obj = jshGetDeviceObject(device);
  if (!obj) return false;

  bool success = false;
  JsVar *str = jsvObjectGetChild(obj, "path", 0);
  if (jsvIsString(str)) {
    jsvGetString(str, buf, bufSize);
    success = true;
  }
  jsvUnLock2(str, obj);
  return success;
}
Beispiel #11
0
/*JSON{
  "type" : "method",
  "class" : "Array",
  "name" : "shift",
  "ifndef" : "SAVE_ON_FLASH",
  "generate" : "jswrap_array_shift",
  "params" : [

  ],
  "return" : ["JsVar","The element that was removed"]
}
Remove and return the first element of the array.

This is the opposite of `[1,2,3].pop()`, which takes an element off the end.
 */
JsVar *jswrap_array_shift(JsVar *parent) {
  // just use splice, as this does all the hard work for us
  JsVar *nRemove = jsvNewFromInteger(1);
  JsVar *elements = jsvNewEmptyArray();
  JsVar *arr = jswrap_array_splice(parent, 0, nRemove, elements);
  jsvUnLock2(elements, nRemove);
  // unpack element from the array
  JsVar *el = 0;
  if (jsvIsArray(arr))
    el = jsvArrayPop(arr);
  jsvUnLock(arr);
  return el;
}
Beispiel #12
0
/*JSON{
  "type" : "method",
  "class" : "ReferenceError",
  "name" : "toString",
  "generate" : "jswrap_error_toString",
  "return" : ["JsVar","A String"]
}*/
JsVar *jswrap_error_toString(JsVar *parent) {
  JsVar *str = jsvObjectGetChild(parent, "type", 0);
  if (!str) str = jsvNewFromString("Error");
  if (!str) return 0;

  JsVar *msg = jsvObjectGetChild(parent, "msg", 0);
  if (msg) {
    JsVar *newStr = jsvVarPrintf("%v: %v", str, msg);
    jsvUnLock2(msg, str);
    str = newStr;
  }

  return str;
}
Beispiel #13
0
/*JSON{
  "type" : "method",
  "class" : "Object",
  "name" : "on",
  "generate" : "jswrap_object_on",
  "params" : [
    ["event","JsVar","The name of the event, for instance 'data'"],
    ["listener","JsVar","The listener to call when this event is received"]
  ]
}
Register an event listener for this object, for instance ```http.on('data', function(d) {...})```. See Node.js's EventEmitter.
 */
void jswrap_object_on(JsVar *parent, JsVar *event, JsVar *listener) {
  if (!jsvHasChildren(parent)) {
    jsWarn("Parent must be an object - not a String, Integer, etc.");
    return;
  }
  if (!jsvIsString(event)) {
    jsWarn("First argument to EventEmitter.on(..) must be a string");
    return;
  }
  if (!jsvIsFunction(listener) && !jsvIsString(listener)) {
    jsWarn("Second argument to EventEmitter.on(..) must be a function or a String (containing code)");
    return;
  }

  JsVar *eventName = jsvVarPrintf(JS_EVENT_PREFIX"%s",event);
  if (!eventName) return; // no memory

  JsVar *eventList = jsvFindChildFromVar(parent, eventName, true);
  jsvUnLock(eventName);
  JsVar *eventListeners = jsvSkipName(eventList);
  if (jsvIsUndefined(eventListeners)) {
    // just add
    jsvSetValueOfName(eventList, listener);
  } else {
    if (jsvIsArray(eventListeners)) {
      // we already have an array, just add to it
      jsvArrayPush(eventListeners, listener);
    } else {
      // not an array - we need to make it an array
      JsVar *arr = jsvNewEmptyArray();
      jsvArrayPush(arr, eventListeners);
      jsvArrayPush(arr, listener);
      jsvSetValueOfName(eventList, arr);
      jsvUnLock(arr);
    }
  }
  jsvUnLock2(eventListeners, eventList);
  /* Special case if we're a data listener and data has already arrived then
   * we queue an event immediately. */
  if (jsvIsStringEqual(event, "data")) {
    JsVar *buf = jsvObjectGetChild(parent, STREAM_BUFFER_NAME, 0);
    if (jsvIsString(buf)) {
      jsiQueueObjectCallbacks(parent, STREAM_CALLBACK_NAME, &buf, 1);
      jsvRemoveNamedChild(parent, STREAM_BUFFER_NAME);
    }
    jsvUnLock(buf);
  }
}
Beispiel #14
0
/*JSON{
  "type" : "method",
  "class" : "Object",
  "name" : "on",
  "generate" : "jswrap_object_on",
  "params" : [
    ["event","JsVar","The name of the event, for instance 'data'"],
    ["listener","JsVar","The listener to call when this event is received"]
  ]
}
Register an event listener for this object, for instance ```http.on('data', function(d) {...})```. See Node.js's EventEmitter.
 */
void jswrap_object_on(JsVar *parent, JsVar *event, JsVar *listener) {
  if (!jsvIsObject(parent)) {
    jsWarn("Parent must be a proper object - not a String, Integer, etc.");
    return;
  }
  if (!jsvIsString(event)) {
    jsWarn("First argument to EventEmitter.on(..) must be a string");
    return;
  }
  if (!jsvIsFunction(listener) && !jsvIsString(listener)) {
    jsWarn("Second argument to EventEmitter.on(..) must be a function or a String (containing code)");
    return;
  }
  char eventName[16];
  if (!jswrap_object_get_event_name(eventName, event)) return;

  JsVar *eventList = jsvFindChildFromString(parent, eventName, true);
  JsVar *eventListeners = jsvSkipName(eventList);
  if (jsvIsUndefined(eventListeners)) {
    // just add
    jsvSetValueOfName(eventList, listener);
  } else {
    if (jsvIsArray(eventListeners)) {
      // we already have an array, just add to it
      jsvArrayPush(eventListeners, listener);
    } else {
      // not an array - we need to make it an array
      JsVar *arr = jsvNewWithFlags(JSV_ARRAY);
      jsvArrayPush(arr, eventListeners);
      jsvArrayPush(arr, listener);
      jsvSetValueOfName(eventList, arr);
      jsvUnLock(arr);
    }
  }
  jsvUnLock2(eventListeners, eventList);
  /* Special case if we're a data listener and data has already arrived then
   * we queue an event immediately. */
  if (jsvIsStringEqual(event, "data")) {
    JsVar *buf = jsvObjectGetChild(parent, STREAM_BUFFER_NAME, 0);
    if (jsvIsString(buf)) {
      jsiQueueObjectCallbacks(parent, "#ondata", &buf, 1);
      jsvRemoveNamedChild(parent, STREAM_BUFFER_NAME);
    }
    jsvUnLock(buf);
  }
}
Beispiel #15
0
/*JSON{
  "type" : "function",
  "name" : "clearTimeout",
  "generate" : "jswrap_interface_clearTimeout",
  "params" : [
    ["id","JsVar","The id returned by a previous call to setTimeout"]
  ]
}
Clear the Timeout that was created with setTimeout, for example:

```var id = setTimeout(function () { print('foo'); }, 1000);```

```clearTimeout(id);```

If no argument is supplied, all timers and intervals are stopped
 */
void _jswrap_interface_clearTimeoutOrInterval(JsVar *idVar, bool isTimeout) {
  JsVar *timerArrayPtr = jsvLock(timerArray);
  if (jsvIsUndefined(idVar)) {
    jsvRemoveAllChildren(timerArrayPtr);
  } else {
    JsVar *child = jsvIsBasic(idVar) ? jsvFindChildFromVar(timerArrayPtr, idVar, false) : 0;
    if (child) {
      JsVar *timerArrayPtr = jsvLock(timerArray);
      jsvRemoveChild(timerArrayPtr, child);
      jsvUnLock2(child, timerArrayPtr);
    } else {
      jsExceptionHere(JSET_ERROR, isTimeout ? "Unknown Timeout" : "Unknown Interval");
    }
  }
  jsvUnLock(timerArrayPtr);
  jsiTimersChanged(); // mark timers as changed
}
Beispiel #16
0
/*JSON{
  "type" : "method",
  "class" : "String",
  "name" : "replace",
  "generate" : "jswrap_string_replace",
  "params" : [
    ["subStr","JsVar","The string to search for"],
    ["newSubStr","JsVar","The string to replace it with"]
  ],
  "return" : ["JsVar","This string with `subStr` replaced"]
}
Search and replace ONE occurrance of `subStr` with `newSubStr` and return the result. This doesn't alter the original string. Regular expressions not supported.
 */
JsVar *jswrap_string_replace(JsVar *parent, JsVar *subStr, JsVar *newSubStr) {
  JsVar *str = jsvAsString(parent, false);
  subStr = jsvAsString(subStr, false);
  newSubStr = jsvAsString(newSubStr, false);

  int idx = jswrap_string_indexOf(parent, subStr, 0, false);
  if (idx>=0) {
    JsVar *newStr = jsvNewFromStringVar(str, 0, (size_t)idx);
    jsvAppendStringVar(newStr, newSubStr, 0, JSVAPPENDSTRINGVAR_MAXLENGTH);
    jsvAppendStringVar(newStr, str, (size_t)idx+jsvGetStringLength(subStr), JSVAPPENDSTRINGVAR_MAXLENGTH);
    jsvUnLock(str);
    str = newStr;
  }

  jsvUnLock2(subStr, newSubStr);
  return str;
}
Beispiel #17
0
static bool jsfGetJSONForObjectItWithCallback(JsvObjectIterator *it, JSONFlags flags, const char *whitespace, JSONFlags nflags, vcbprintf_callback user_callback, void *user_data, bool first) {
  bool needNewLine = false;
  size_t sinceNewLine = 0;
  while (jsvObjectIteratorHasValue(it) && !jspIsInterrupted()) {
    JsVar *index = jsvObjectIteratorGetKey(it);
    JsVar *item = jsvGetValueOfName(index);
    bool hidden = jsvIsInternalObjectKey(index) ||
        ((flags & JSON_IGNORE_FUNCTIONS) && jsvIsFunction(item)) ||
        ((flags&JSON_NO_UNDEFINED) && jsvIsUndefined(item)) ||
        jsvIsGetterOrSetter(item);
    if (!hidden) {
      sinceNewLine++;
      if (!first) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":",");
      bool newNeedsNewLine = (flags&JSON_SOME_NEWLINES) && jsonNeedsNewLine(item);
      if ((flags&JSON_SOME_NEWLINES) && sinceNewLine>JSON_ITEMS_ON_LINE_OBJECT)
        needNewLine = true;
      if (flags&JSON_ALL_NEWLINES) {
        needNewLine = true;
        newNeedsNewLine = true;
      }
      if (needNewLine || newNeedsNewLine) {
        jsonNewLine(nflags, whitespace, user_callback, user_data);
        needNewLine = false;
        sinceNewLine = 0;
      }
      bool addQuotes = true;
      if (flags&JSON_DROP_QUOTES) {
        if (jsvIsIntegerish(index)) addQuotes = false;
        else if (jsvIsString(index) && jsvGetStringLength(index)<15) {
          char buf[16];
          jsvGetString(index,buf,sizeof(buf));
          if (isIDString(buf)) addQuotes=false;
        }
      }
      cbprintf(user_callback, user_data, addQuotes?"%q%s":"%v%s", index, (flags&JSON_PRETTY)?": ":":");
      if (first)
        first = false;
      jsfGetJSONWithCallback(item, nflags, whitespace, user_callback, user_data);
      needNewLine = newNeedsNewLine;
    }
    jsvUnLock2(index, item);
    jsvObjectIteratorNext(it);
  }
  return needNewLine;
}
Beispiel #18
0
static void httpAppendHeaders(JsVar *string, JsVar *headerObject) {
    // append headers
    JsvObjectIterator it;
    jsvObjectIteratorNew(&it, headerObject);
    while (jsvObjectIteratorHasValue(&it)) {
        JsVar *k = jsvAsString(jsvObjectIteratorGetKey(&it), true);
        JsVar *v = jsvAsString(jsvObjectIteratorGetValue(&it), true);
        jsvAppendStringVarComplete(string, k);
        jsvAppendString(string, ": ");
        jsvAppendStringVarComplete(string, v);
        jsvAppendString(string, "\r\n");
        jsvUnLock2(k, v);
        jsvObjectIteratorNext(&it);
    }
    jsvObjectIteratorFree(&it);

    // free headers
}
Beispiel #19
0
/*JSON{
  "type" : "function",
  "name" : "changeInterval",
  "generate" : "jswrap_interface_changeInterval",
  "params" : [
    ["id","JsVar","The id returned by a previous call to setInterval"],
    ["time","float","The new time period in ms"]
  ]
}
Change the Interval on a callback created with setInterval, for example:

```var id = setInterval(function () { print('foo'); }, 1000); // every second```

```changeInterval(id, 1500); // now runs every 1.5 seconds```

This takes effect the next time the callback is called (so it is not immediate).
 */
void jswrap_interface_changeInterval(JsVar *idVar, JsVarFloat interval) {
  JsVar *timerArrayPtr = jsvLock(timerArray);
  if (interval<TIMER_MIN_INTERVAL) interval=TIMER_MIN_INTERVAL;
  JsVar *timerName = jsvIsBasic(idVar) ? jsvFindChildFromVar(timerArrayPtr, idVar, false) : 0;
  if (timerName) {
    JsVar *timer = jsvSkipNameAndUnLock(timerName);
    JsVar *v;
    JsVarInt intervalInt = (JsVarInt)jshGetTimeFromMilliseconds(interval);
    v = jsvNewFromInteger(intervalInt);
    jsvUnLock2(jsvSetNamedChild(timer, v, "interval"), v);
    v = jsvNewFromInteger((JsVarInt)(jshGetSystemTime()-jsiLastIdleTime) + intervalInt);
    jsvUnLock3(jsvSetNamedChild(timer, v, "time"), v, timer);
    // timerName already unlocked
    jsiTimersChanged(); // mark timers as changed
  } else {
    jsExceptionHere(JSET_ERROR, "Unknown Interval");
  }
  jsvUnLock(timerArrayPtr);
}
Beispiel #20
0
/*JSON{
  "type" : "staticmethod",
  "class" : "Object",
  "name" : "defineProperty",
  "generate" : "jswrap_object_defineProperty",
  "params" : [
    ["obj","JsVar","An object"],
    ["name","JsVar","The name of the property"],
    ["desc","JsVar","The property descriptor"]
  ],
  "return" : ["JsVar","The object, obj."]
}
Add a new property to the Object. 'Desc' is an object with the following fields:

* `configurable` (bool = false) - can this property be changed/deleted
* `enumerable` (bool = false) - can this property be enumerated
* `value` (anything) - the value of this property
* `writable` (bool = false) - can the value be changed with the assignment operator?
* `get` (function) - the getter function, or undefined if no getter
* `set` (function) - the setter function, or undefined if no setter
*
**Note:** `configurable`, `enumerable`, `writable`, `get`, and `set` are not implemented and will be ignored.
 */
JsVar *jswrap_object_defineProperty(JsVar *parent, JsVar *propName, JsVar *desc) {
  if (!jsvIsObject(parent)) {
    jsExceptionHere(JSET_ERROR, "First argument must be an object, got %t", parent);
    return 0;
  }
  if (!jsvIsObject(desc)) {
    jsExceptionHere(JSET_ERROR, "Property description must be an object, got %t", desc);
    return 0;
  }

  JsVar *name = jsvAsArrayIndex(propName);
  JsVar *value = jsvObjectGetChild(desc, "value", 0);
  JsVar *property = jsvFindChildFromVar(parent, name, true);
  jsvUnLock(name);
  if (property && value)
    jsvSetValueOfName(property, value);
  jsvUnLock2(property, value);

  return jsvLockAgain(parent);
}
Beispiel #21
0
/*JSON{
  "type" : "staticmethod",
  "class" : "http",
  "name" : "get",
  "generate" : "jswrap_http_get",
  "params" : [
    ["options","JsVar","An object containing host,port,path,method fields"],
    ["callback","JsVar","A function(res) that will be called when a connection is made. You can then call `res.on('data', function(data) { ... })` and `res.on('close', function() { ... })` to deal with the response."]
  ],
  "return" : ["JsVar","Returns a new httpCRq object"],
  "return_object" : "httpCRq"
}
Create an HTTP Request - convenience function for ```http.request()```. `options.method` is set to 'get', and end is called automatically. See [the Internet page](/Internet) for more usage examples.
*/
JsVar *jswrap_http_get(JsVar *options, JsVar *callback) {
  JsNetwork net;
  if (!networkGetFromVarIfOnline(&net)) return 0;

  if (jsvIsObject(options)) {
    // if options is a string - it will be parsed, and GET will be set automatically
    JsVar *method = jsvNewFromString("GET");
    jsvUnLock2(jsvAddNamedChild(options, method, "method"), method);
  }
  JsVar *skippedCallback = jsvSkipName(callback);
  if (!jsvIsUndefined(skippedCallback) && !jsvIsFunction(skippedCallback)) {
    jsError("Expecting Callback Function but got %t", skippedCallback);
    jsvUnLock(skippedCallback);
    return 0;
  }
  jsvUnLock(skippedCallback);
  JsVar *cliReq = jswrap_net_connect(options, callback, ST_HTTP);
  if (cliReq) clientRequestEnd(&net, cliReq);
  networkFree(&net);
  return cliReq;
}
Beispiel #22
0
/**
 * \brief Iterate over the contents of the content of a variable, calling callback for each.
 * Contents may be:
 * * numeric -> output
 * * a string -> output each character
 * * array/arraybuffer -> call itself on each element
 * object -> call itself object.count times, on object.data
 */
bool jsvIterateCallback(
    JsVar *data,                                    // The data to iterate over.
	void (*callback)(int item, void *callbackData), // The callback function invoke.
	void *callbackData                              // Data to be passed to the callback function
  ) {
  bool ok = true;
  // Handle the data being a single numeric.
  if (jsvIsNumeric(data)) {
    callback((int)jsvGetInteger(data), callbackData);
  }
  // Handle the data being an object.
  else if (jsvIsObject(data)) {
    JsVar *countVar = jsvObjectGetChild(data, "count", 0);
    JsVar *dataVar = jsvObjectGetChild(data, "data", 0);
    if (countVar && dataVar && jsvIsNumeric(countVar)) {
      int n = (int)jsvGetInteger(countVar);
      while (ok && n-- > 0) {
        ok = jsvIterateCallback(dataVar, callback, callbackData);
      }
    } else {
      jsWarn("If specifying an object, it must be of the form {data : ..., count : N}");
    }
    jsvUnLock2(countVar, dataVar);
  }
  // Handle the data being a string
  else if (jsvIsString(data)) {
    JsvStringIterator it;
    jsvStringIteratorNew(&it, data, 0);
    while (jsvStringIteratorHasChar(&it) && ok) {
      char ch = jsvStringIteratorGetChar(&it);
      callback(ch, callbackData);
      jsvStringIteratorNext(&it);
    }
    jsvStringIteratorFree(&it);
  }
  // Handle the data being an array buffer
  else if (jsvIsArrayBuffer(data)) {
    JsvArrayBufferIterator it;
    jsvArrayBufferIteratorNew(&it, data, 0);
    if (JSV_ARRAYBUFFER_GET_SIZE(it.type) == 1 && !JSV_ARRAYBUFFER_IS_SIGNED(it.type)) {
      // faster for single byte arrays.
      while (jsvArrayBufferIteratorHasElement(&it)) {
        callback((int)(unsigned char)jsvStringIteratorGetChar(&it.it), callbackData);
        jsvArrayBufferIteratorNext(&it);
      }
    } else {
      while (jsvArrayBufferIteratorHasElement(&it)) {
        callback((int)jsvArrayBufferIteratorGetIntegerValue(&it), callbackData);
        jsvArrayBufferIteratorNext(&it);
      }
    }
    jsvArrayBufferIteratorFree(&it);
  }
  // Handle the data being iterable
  else if (jsvIsIterable(data)) {
    JsvIterator it;
    jsvIteratorNew(&it, data);
    while (jsvIteratorHasElement(&it) && ok) {
      JsVar *el = jsvIteratorGetValue(&it);
      ok = jsvIterateCallback(el, callback, callbackData);
      jsvUnLock(el);
      jsvIteratorNext(&it);
    }
    jsvIteratorFree(&it);
  } else {
    jsWarn("Expecting a number or something iterable, got %t", data);
    ok = false;
  }
  return ok;
}
Beispiel #23
0
/*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;
}
Beispiel #24
0
bool socketServerConnectionsIdle(JsNetwork *net) {
    char *buf = alloca(net->chunkSize); // allocate on stack

    JsVar *arr = socketGetArray(HTTP_ARRAY_HTTP_SERVER_CONNECTIONS,false);
    if (!arr) return false;

    bool hadSockets = false;
    JsvObjectIterator it;
    jsvObjectIteratorNew(&it, arr);
    while (jsvObjectIteratorHasValue(&it)) {
        hadSockets = true;
        // Get connection, socket, and socket type
        // For normal sockets, socket==connection, but for HTTP we split it into a request and a response
        JsVar *connection = jsvObjectIteratorGetValue(&it);
        SocketType socketType = socketGetType(connection);
        JsVar *socket = ((socketType&ST_TYPE_MASK)==ST_HTTP) ? jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0) : jsvLockAgain(connection);

        int sckt = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_SOCKET,0))-1; // so -1 if undefined
        bool closeConnectionNow = jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CLOSENOW, false));
        int error = 0;

        if (!closeConnectionNow) {
            int num = netRecv(net, sckt, buf, net->chunkSize);
            if (num<0) {
                // we probably disconnected so just get rid of this
                closeConnectionNow = true;
                error = num;
            } else {
                // add it to our request string
                if (num>0) {
                    JsVar *receiveData = jsvObjectGetChild(connection,HTTP_NAME_RECEIVE_DATA,0);
                    JsVar *oldReceiveData = receiveData;
                    if (!receiveData) receiveData = jsvNewFromEmptyString();
                    if (receiveData) {
                        jsvAppendStringBuf(receiveData, buf, (size_t)num);
                        bool hadHeaders = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_HAD_HEADERS,0));
                        if (!hadHeaders && httpParseHeaders(&receiveData, connection, true)) {
                            hadHeaders = true;
                            jsvObjectSetChildAndUnLock(connection, HTTP_NAME_HAD_HEADERS, jsvNewFromBool(hadHeaders));
                            JsVar *server = jsvObjectGetChild(connection,HTTP_NAME_SERVER_VAR,0);
                            JsVar *args[2] = { connection, socket };
                            jsiQueueObjectCallbacks(server, HTTP_NAME_ON_CONNECT, args, ((socketType&ST_TYPE_MASK)==ST_HTTP) ? 2 : 1);
                            jsvUnLock(server);
                        }
                        if (hadHeaders && !jsvIsEmptyString(receiveData)) {
                            // Keep track of how much we received (so we can close once we have it)
                            if ((socketType&ST_TYPE_MASK)==ST_HTTP) {
                                jsvObjectSetChildAndUnLock(connection, HTTP_NAME_RECEIVE_COUNT,
                                                           jsvNewFromInteger(
                                                               jsvGetIntegerAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_RECEIVE_COUNT, JSV_INTEGER)) +
                                                               jsvGetStringLength(receiveData)
                                                           ));
                            }
                            // execute 'data' callback or save data
                            if (jswrap_stream_pushData(connection, receiveData, false)) {
                                // clear received data
                                jsvUnLock(receiveData);
                                receiveData = 0;
                            }
                        }
                        // if received data changed, update it
                        if (receiveData != oldReceiveData)
                            jsvObjectSetChild(connection,HTTP_NAME_RECEIVE_DATA,receiveData);
                        jsvUnLock(receiveData);
                    }
                }
            }

            // send data if possible
            JsVar *sendData = jsvObjectGetChild(socket,HTTP_NAME_SEND_DATA,0);
            if (sendData && !jsvIsEmptyString(sendData)) {
                int sent = socketSendData(net, socket, sckt, &sendData);
                // FIXME? checking for errors is a bit iffy. With the esp8266 network that returns
                // varied error codes we'd want to skip SOCKET_ERR_CLOSED and let the recv side deal
                // with normal closing so we don't miss the tail of what's received, but other drivers
                // return -1 (which is the same value) for all errors. So we rely on the check ~12 lines
                // down if(num>0)closeConnectionNow=false instead.
                if (sent < 0) {
                    closeConnectionNow = true;
                    error = sent;
                }
                jsvObjectSetChild(socket, HTTP_NAME_SEND_DATA, sendData); // socketSendData prob updated sendData
            }
            // only close if we want to close, have no data to send, and aren't receiving data
            bool wantClose = jsvGetBoolAndUnLock(jsvObjectGetChild(socket,HTTP_NAME_CLOSE,0));
            if (wantClose && (!sendData || jsvIsEmptyString(sendData)) && num<=0) {
                bool reallyCloseNow = true;
                if ((socketType&ST_TYPE_MASK)==ST_HTTP) {
                    // Check if we had a Content-Length header - if so, we need to wait until we have received that amount
                    JsVar *headers = jsvObjectGetChild(connection,"headers",0);
                    if (headers) {
                        JsVarInt contentLength = jsvGetIntegerAndUnLock(jsvObjectGetChild(headers,"Content-Length",0));
                        JsVarInt contentReceived = jsvGetIntegerAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_RECEIVE_COUNT, 0));
                        if (contentLength > contentReceived) {
                            reallyCloseNow = false;
                        }
                        jsvUnLock(headers);
                    }
                }
                closeConnectionNow = reallyCloseNow;
            } else if (num > 0)
                closeConnectionNow = false; // guarantee that anything received is processed
            jsvUnLock(sendData);
        }
        if (closeConnectionNow) {
            // send out any data that we were POSTed
            JsVar *receiveData = jsvObjectGetChild(connection,HTTP_NAME_RECEIVE_DATA,0);
            bool hadHeaders = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_HAD_HEADERS,0));
            if (hadHeaders && !jsvIsEmptyString(receiveData)) {
                // execute 'data' callback or save data
                jswrap_stream_pushData(connection, receiveData, true);
            }
            jsvUnLock(receiveData);

            // fire error events
            bool hadError = fireErrorEvent(error, connection, socket);

            // fire the close listeners
            JsVar *params[1] = { jsvNewFromBool(hadError) };
            jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_CLOSE, params, 1);
            jsiQueueObjectCallbacks(socket, HTTP_NAME_ON_CLOSE, params, 1);
            jsvUnLock(params[0]);

            _socketConnectionKill(net, connection);
            JsVar *connectionName = jsvObjectIteratorGetKey(&it);
            jsvObjectIteratorNext(&it);
            jsvRemoveChild(arr, connectionName);
            jsvUnLock(connectionName);
        } else
            jsvObjectIteratorNext(&it);
        jsvUnLock2(connection, socket);
    }
    jsvObjectIteratorFree(&it);
    jsvUnLock(arr);

    return hadSockets;
}
Beispiel #25
0
/** A convenience function for adding event listeners */
void jswrap_object_addEventListener(JsVar *parent, const char *eventName, void (*callback)(), JsnArgumentType argTypes) {
  JsVar *n = jsvNewFromString(eventName);
  JsVar *cb = jsvNewNativeFunction(callback, argTypes);
  jswrap_object_on(parent, n, cb);
  jsvUnLock2(cb, n);
}
Beispiel #26
0
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;
}
Beispiel #27
0
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
  }
  }
}
Beispiel #28
0
void jsfGetJSONWithCallback(JsVar *var, JSONFlags flags, const char *whitespace, vcbprintf_callback user_callback, void *user_data) {
  JSONFlags nflags = flags + JSON_INDENT; // if we add a newline, make sure we indent any subsequent JSON more
  if (!whitespace) whitespace="  ";

  if (jsvIsUndefined(var)) {
    cbprintf(user_callback, user_data, "undefined");
  } else {
    // Use IS_RECURSING  flag to stop recursion
    if (var->flags & JSV_IS_RECURSING) {
      cbprintf(user_callback, user_data, " ... ");
      return;
    }
    var->flags |= JSV_IS_RECURSING;

    if (jsvIsArray(var)) {
      JsVarInt length = jsvGetArrayLength(var);
      bool limited = (flags&JSON_LIMIT) && (length>(JsVarInt)JSON_LIMIT_AMOUNT);
      bool needNewLine = false;
      cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"[ ":"[");
      JsVarInt lastIndex = -1;
      bool numeric = true;
      bool first = true;
      JsvObjectIterator it;
      jsvObjectIteratorNew(&it, var);
      while (lastIndex+1<length && numeric && !jspIsInterrupted()) {
        JsVar *key = jsvObjectIteratorGetKey(&it);
        if (!jsvObjectIteratorHasValue(&it) || jsvIsNumeric(key)) {
          JsVarInt index = jsvObjectIteratorHasValue(&it) ? jsvGetInteger(key) : length-1;
          JsVar *item = jsvObjectIteratorGetValue(&it);
          while (lastIndex < index) {
            lastIndex++;
            if (!limited || lastIndex<(JsVarInt)JSON_LIMITED_AMOUNT || lastIndex>=length-(JsVarInt)JSON_LIMITED_AMOUNT) {
              if (!first) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":",");
              first = false;
              if (limited && lastIndex==length-(JsVarInt)JSON_LIMITED_AMOUNT) cbprintf(user_callback, user_data, JSON_LIMIT_TEXT);
              bool newNeedsNewLine = ((flags&JSON_SOME_NEWLINES) && jsonNeedsNewLine(item));
              if (flags&JSON_ALL_NEWLINES) {
                needNewLine = true;
                newNeedsNewLine = true;
              }
              if (needNewLine || newNeedsNewLine) {
                jsonNewLine(nflags, whitespace, user_callback, user_data);
                needNewLine = false;
              }
              if (lastIndex == index)
                jsfGetJSONWithCallback(item, nflags, whitespace, user_callback, user_data);
              else
                cbprintf(user_callback, user_data, (flags&JSON_NO_UNDEFINED)?"null":"undefined");
              needNewLine = newNeedsNewLine;
            }
          }
          jsvUnLock(item);
          jsvObjectIteratorNext(&it);
        } else {
          numeric = false;
        }
        jsvUnLock(key);
      }

      // non-numeric  - but NOT for standard JSON
      if ((flags&JSON_PRETTY))
        jsfGetJSONForObjectItWithCallback(&it, flags, whitespace, nflags, user_callback, user_data, first);
      jsvObjectIteratorFree(&it);
      if (needNewLine) jsonNewLine(flags, whitespace, user_callback, user_data);
      cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?" ]":"]");
    } else if (jsvIsArrayBuffer(var)) {
      JsvArrayBufferIterator it;
      bool allZero = true;
      jsvArrayBufferIteratorNew(&it, var, 0);
      while (jsvArrayBufferIteratorHasElement(&it)) {
        if (jsvArrayBufferIteratorGetFloatValue(&it)!=0)
          allZero = false;
        jsvArrayBufferIteratorNext(&it);
      }
      jsvArrayBufferIteratorFree(&it);
      bool asArray = flags&JSON_ARRAYBUFFER_AS_ARRAY;

      if (allZero && !asArray) {
        cbprintf(user_callback, user_data, "new %s(%d)", jswGetBasicObjectName(var), jsvGetArrayBufferLength(var));
      } else {
        const char *aname = jswGetBasicObjectName(var);
        /* You can't do `new ArrayBuffer([1,2,3])` so we have to output
         * `new Uint8Array([1,2,3]).buffer`! */
        bool isBasicArrayBuffer = strcmp(aname,"ArrayBuffer")==0;
        if (isBasicArrayBuffer) {
          aname="Uint8Array";
        }
        cbprintf(user_callback, user_data, asArray?"[":"new %s([", aname);
        if (flags&JSON_ALL_NEWLINES) jsonNewLine(nflags, whitespace, user_callback, user_data);
        size_t length = jsvGetArrayBufferLength(var);
        bool limited = (flags&JSON_LIMIT) && (length>JSON_LIMIT_AMOUNT);
        // no newlines needed for array buffers as they only contain simple stuff

        jsvArrayBufferIteratorNew(&it, var, 0);
        while (jsvArrayBufferIteratorHasElement(&it) && !jspIsInterrupted()) {
          if (!limited || it.index<JSON_LIMITED_AMOUNT || it.index>=length-JSON_LIMITED_AMOUNT) {
            if (it.index>0) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":",");
            if (flags&JSON_ALL_NEWLINES) jsonNewLine(nflags, whitespace, user_callback, user_data);
            if (limited && it.index==length-JSON_LIMITED_AMOUNT) cbprintf(user_callback, user_data, JSON_LIMIT_TEXT);
            JsVar *item = jsvArrayBufferIteratorGetValue(&it);
            jsfGetJSONWithCallback(item, nflags, whitespace, user_callback, user_data);
            jsvUnLock(item);
          }
          jsvArrayBufferIteratorNext(&it);
        }
        if (flags&JSON_ALL_NEWLINES) jsonNewLine(flags, whitespace, user_callback, user_data);
        jsvArrayBufferIteratorFree(&it);
        cbprintf(user_callback, user_data, asArray?"]":"])");
        if (isBasicArrayBuffer && !asArray) cbprintf(user_callback, user_data, ".buffer");
      }
    } else if (jsvIsObject(var)) {
      IOEventFlags device = (flags & JSON_SHOW_DEVICES) ? jsiGetDeviceFromClass(var) : EV_NONE;
      if (device!=EV_NONE) {
        cbprintf(user_callback, user_data, "%s", jshGetDeviceString(device));
      } else {
        bool showContents = true;
        if (flags & JSON_SHOW_OBJECT_NAMES) {
          JsVar *proto = jsvObjectGetChild(var, JSPARSE_INHERITS_VAR, 0);
          if (jsvHasChildren(proto)) {
            JsVar *constr = jsvObjectGetChild(proto, JSPARSE_CONSTRUCTOR_VAR, 0);
            if (constr) {
              JsVar *p = jsvGetIndexOf(execInfo.root, constr, true);
              if (p) cbprintf(user_callback, user_data, "%v: ", p);
              jsvUnLock2(p,constr);
              /* We had the constructor - now if there was a non-default toString function
               * we'll execute it and print the result */
              JsVar *toStringFn = jspGetNamedField(var, "toString", false);
              if (toStringFn && toStringFn->varData.native.ptr != (void (*)(void))jswrap_object_toString) {
                // Function found and it's not the default one - execute it
                JsVar *result = jspExecuteFunction(toStringFn,var,0,0);
                cbprintf(user_callback, user_data, "%v", result);
                jsvUnLock(result);
                showContents = false; // we already printed something
              }
              jsvUnLock(toStringFn);
            }
          }
          jsvUnLock(proto);
        }
        if (showContents) {
          JsvObjectIterator it;
          jsvObjectIteratorNew(&it, var);
          cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"{ ":"{");
          bool needNewLine = jsfGetJSONForObjectItWithCallback(&it, flags, whitespace, nflags, user_callback, user_data, true);
          jsvObjectIteratorFree(&it);
          if (needNewLine) jsonNewLine(flags, whitespace, user_callback, user_data);
          cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?" }":"}");
        }
      }
    } else if (jsvIsFunction(var)) {
      if (flags & JSON_IGNORE_FUNCTIONS) {
        cbprintf(user_callback, user_data, "undefined");
      } else {
        cbprintf(user_callback, user_data, "function ");
        jsfGetJSONForFunctionWithCallback(var, nflags, user_callback, user_data);
      }
    } else if (jsvIsString(var) && !jsvIsName(var)) {
      if ((flags&JSON_LIMIT) && jsvGetStringLength(var)>JSON_LIMIT_STRING_AMOUNT) {
        // if the string is too big, split it and put dots in the middle
        JsVar *var1 = jsvNewFromStringVar(var, 0, JSON_LIMITED_STRING_AMOUNT);
        JsVar *var2 = jsvNewFromStringVar(var, jsvGetStringLength(var)-JSON_LIMITED_STRING_AMOUNT, JSON_LIMITED_STRING_AMOUNT);
        cbprintf(user_callback, user_data, "%q%s%q", var1, JSON_LIMIT_TEXT, var2);
        jsvUnLock2(var1, var2);
      } else {
        cbprintf(user_callback, user_data, "%q", var);
      }
    } else {
      cbprintf(user_callback, user_data, "%v", var);
    }

    var->flags &= ~JSV_IS_RECURSING;
  }
}
Beispiel #29
0
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);
    }
}
Beispiel #30
0
/*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;
}