Exemplo n.º 1
0
/*JSON{
  "type" : "method",
  "class" : "Array",
  "name" : "reduce",
  "ifndef" : "SAVE_ON_FLASH",
  "generate" : "jswrap_array_reduce",
  "params" : [
    ["callback","JsVar","Function used to reduce the array"],
    ["initialValue","JsVar","if specified, the initial value to pass to the function"]
  ],
  "return" : ["JsVar","The value returned by the last function called"]
}
Execute `previousValue=initialValue` and then `previousValue = callback(previousValue, currentValue, index, array)` for each element in the array, and finally return previousValue.
*/
JsVar *jswrap_array_reduce(JsVar *parent, JsVar *funcVar, JsVar *initialValue) {
  const char *name = "reduce";
  if (!jsvIsIterable(parent)) {
    jsExceptionHere(JSET_ERROR, "Array.%s can only be called on something iterable", name);
    return 0;
  }
  if (!jsvIsFunction(funcVar)) {
    jsExceptionHere(JSET_ERROR, "Array.%s's first argument should be a function", name);
    return 0;
  }
  JsVar *previousValue = initialValue ? jsvLockAgain(initialValue) : 0;
  JsvIterator it;
  jsvIteratorNew(&it, parent);
  if (!previousValue) {
    bool isDone = false;
    while (!isDone && jsvIteratorHasElement(&it)) {
      JsVar *index = jsvIteratorGetKey(&it);
      if (jsvIsInt(index)) {
        previousValue = jsvIteratorGetValue(&it);
        isDone = true;
      }
      jsvUnLock(index);
      jsvIteratorNext(&it);
    }
    if (!previousValue) {
      jsExceptionHere(JSET_ERROR, "Array.%s without initial value required non-empty array", name);
    }
  }
  while (jsvIteratorHasElement(&it)) {
    JsVar *index = jsvIteratorGetKey(&it);
    if (jsvIsInt(index)) {
      JsVarInt idxValue = jsvGetInteger(index);

      JsVar *args[4];
      args[0] = previousValue;
      args[1] = jsvIteratorGetValue(&it);
      args[2] = jsvNewFromInteger(idxValue); // child is a variable name, create a new variable for the index
      args[3] = parent;
      previousValue = jspeFunctionCall(funcVar, 0, 0, false, 4, args);
      jsvUnLock(args[0]);
      jsvUnLock(args[1]);
      jsvUnLock(args[2]);
    }
    jsvUnLock(index);
    jsvIteratorNext(&it);
  }
  jsvIteratorFree(&it);

  return previousValue;
}
Exemplo n.º 2
0
/*JSON{ "type":"staticmethod", "class": "Object", "name" : "keys",
         "description" : "Return all enumerable keys of the given object",
         "generate" : "jswrap_object_keys",
         "params" : [ [ "object", "JsVar", "The object to return keys for"] ],
         "return" : ["JsVar", "An array of strings - one for each key on the given object"]
}*/
JsVar *jswrap_object_keys(JsVar *obj) {
  if (jsvIsIterable(obj)) {
    bool (*checkerFunction)(JsVar*) = 0;
    if (jsvIsFunction(obj)) checkerFunction = jsvIsInternalFunctionKey;
    else if (jsvIsObject(obj)) checkerFunction = jsvIsInternalObjectKey;

    JsVar *arr = jsvNewWithFlags(JSV_ARRAY);
    if (!arr) return 0;
    JsvIterator it;
    jsvIteratorNew(&it, obj);
    while (jsvIteratorHasElement(&it)) {
      JsVar *key = jsvIteratorGetKey(&it);
      if (!(checkerFunction && checkerFunction(key))) {
        JsVar *name = jsvCopyNameOnly(key,false,false);
        if (name) {
          jsvArrayPushAndUnLock(arr, name);
        }
      }
      jsvUnLock(key);
      jsvIteratorNext(&it);
    }
    jsvIteratorFree(&it);
    return arr;
  } else {
    jsWarn("Object.keys called on non-object");
    return 0;
  }
}
Exemplo n.º 3
0
/*JSON{
  "type" : "method",
  "class" : "Function",
  "name" : "apply",
  "generate" : "jswrap_function_apply_or_call",
  "params" : [
    ["this","JsVar","The value to use as the 'this' argument when executing the function"],
    ["args","JsVar","Optional Array of Arguments"]
  ],
  "return" : ["JsVar","The return value of executing this function"]
}
This executes the function with the supplied 'this' argument and parameters
 */
JsVar *jswrap_function_apply_or_call(JsVar *parent, JsVar *thisArg, JsVar *argsArray) {
  unsigned int i;
  JsVar **args = 0;
  unsigned int argC = 0;

  if (jsvIsIterable(argsArray)) {
    argC = (unsigned int)jsvGetLength(argsArray);
    if (argC>64) {
      jsExceptionHere(JSET_ERROR, "Array passed to Function.apply is too big! Maximum 64 arguments, got %d", argC);
      return 0;
    }
    args = (JsVar**)alloca((size_t)argC * sizeof(JsVar*));
    for (i=0;i<argC;i++) args[i] = 0;
    // TODO: Use jsvGetArrayItems?
    JsvIterator it;
    jsvIteratorNew(&it, argsArray);
    while (jsvIteratorHasElement(&it)) {
      JsVarInt idx = jsvGetIntegerAndUnLock(jsvIteratorGetKey(&it));
      if (idx>=0 && idx<(int)argC) {
        assert(!args[idx]); // just in case there were dups
        args[idx] = jsvIteratorGetValue(&it);
      }
      jsvIteratorNext(&it);
    }
    jsvIteratorFree(&it);
  } else if (!jsvIsUndefined(argsArray)) {
    jsExceptionHere(JSET_ERROR, "Second argument to Function.apply must be iterable, got %t", argsArray);
    return 0;
  }

  JsVar *r = jspeFunctionCall(parent, 0, thisArg, false, (int)argC, args);
  jsvUnLockMany(argC, args);
  return r;
}
Exemplo n.º 4
0
// This is for Object.keys and Object.
JsVar *jswrap_object_keys_or_property_names(JsVar *obj, bool includeNonEnumerable) {
  // strings are iterable, but we shouldn't try and show keys for them
  if (jsvIsIterable(obj) && !jsvIsString(obj)) {
    JsvIsInternalChecker checkerFunction = jsvGetInternalFunctionCheckerFor(obj);

    JsVar *arr = jsvNewWithFlags(JSV_ARRAY);
    if (!arr) return 0;
    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) {
        symbols = jswGetSymbolListForObjectProto(protoOwner);
        jsvUnLock(protoOwner);
      } else if (!jsvIsObject(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 (jsvIsArray(obj) || jsvIsString(obj)) {
        jsvArrayAddString(arr, "length");
      }
    }

    return arr;
  } else {
    jsWarn("Object.keys called on non-object");
    return 0;
  }
}
Exemplo n.º 5
0
/*JSON{
  "type" : "method",
  "class" : "ArrayBufferView",
  "name" : "map",
  "generate" : "jswrap_arraybufferview_map",
  "params" : [
    ["function","JsVar","Function used to map one item to another"],
    ["thisArg","JsVar","if specified, the function is called with 'this' set to thisArg (optional)"]
  ],
  "return" : ["JsVar","An array containing the results"],
  "return_object" : "ArrayBufferView"
}
Return an array which is made from the following: ```A.map(function) = [function(A[0]), function(A[1]), ...]```

**Note:** This returns an ArrayBuffer of the same type it was called on. To get an Array, use `Array.prototype.map`
*/
JsVar *jswrap_arraybufferview_map(JsVar *parent, JsVar *funcVar, JsVar *thisVar) {
  if (!jsvIsArrayBuffer(parent)) {
    jsExceptionHere(JSET_ERROR, "ArrayBufferView.map can only be called on an ArrayBufferView");
    return 0;
  }
  if (!jsvIsFunction(funcVar)) {
    jsExceptionHere(JSET_ERROR, "ArrayBufferView.map's first argument should be a function");
    return 0;
  }
  if (!jsvIsUndefined(thisVar) && !jsvIsObject(thisVar)) {
    jsExceptionHere(JSET_ERROR, "ArrayBufferView.map's second argument should be undefined, or an object");
    return 0;
  }

  // create ArrayBuffer result
  JsVarDataArrayBufferViewType arrayBufferType = parent->varData.arraybuffer.type;
  JsVar *arrayBufferLength = jsvNewFromInteger((JsVarInt)jsvGetArrayBufferLength(parent));
  JsVar *array = jswrap_typedarray_constructor(arrayBufferType, arrayBufferLength, 0, 0);
  jsvUnLock(arrayBufferLength);
  if (!array) return 0;

  // now iterate
  JsvIterator it; // TODO: if we really are limited to ArrayBuffers, this could be an ArrayBufferIterator.
  jsvIteratorNew(&it, parent);
  JsvArrayBufferIterator itdst;
  jsvArrayBufferIteratorNew(&itdst, array, 0);

  while (jsvIteratorHasElement(&it)) {
    JsVar *index = jsvIteratorGetKey(&it);
    if (jsvIsInt(index)) {
      JsVarInt idxValue = jsvGetInteger(index);

      JsVar *args[3], *mapped;
      args[0] = jsvIteratorGetValue(&it);
      args[1] = jsvNewFromInteger(idxValue); // child is a variable name, create a new variable for the index
      args[2] = parent;
      mapped = jspeFunctionCall(funcVar, 0, thisVar, false, 3, args);
      jsvUnLock(args[0]);
      jsvUnLock(args[1]);
      if (mapped) {
        jsvArrayBufferIteratorSetValue(&itdst, mapped);
        jsvUnLock(mapped);
      }
    }
    jsvUnLock(index);
    jsvIteratorNext(&it);
    jsvArrayBufferIteratorNext(&itdst);
  }
  jsvIteratorFree(&it);
  jsvArrayBufferIteratorFree(&itdst);

  return array;
}
Exemplo n.º 6
0
JsVar *jswrap_typedarray_constructor(JsVarDataArrayBufferViewType type, JsVar *arr, JsVarInt byteOffset, JsVarInt length) {
  JsVar *arrayBuffer = 0;
  // Only allow use of byteOffset/length if we're passing an ArrayBuffer - NOT A VIEW.
  bool copyData = false;
  if (jsvIsArrayBuffer(arr) && arr->varData.arraybuffer.type==ARRAYBUFFERVIEW_ARRAYBUFFER) {
    arrayBuffer = jsvLockAgain(arr);
  } else if (jsvIsNumeric(arr)) {
    length = jsvGetInteger(arr);
    byteOffset = 0;
    arrayBuffer = jswrap_arraybuffer_constructor((int)JSV_ARRAYBUFFER_GET_SIZE(type)*length);
  } else if (jsvIsArray(arr) || jsvIsArrayBuffer(arr)) {
    length = (JsVarInt)jsvGetLength(arr);
    byteOffset = 0;
    arrayBuffer = jswrap_arraybuffer_constructor((int)JSV_ARRAYBUFFER_GET_SIZE(type)*length);
    copyData = true; // so later on we'll populate this
  }
  if (!arrayBuffer) {
    jsExceptionHere(JSET_ERROR, "Unsupported first argument of type %t\n", arr);
    return 0;
  }
  if (length==0) {
    length = ((JsVarInt)jsvGetArrayBufferLength(arrayBuffer)-byteOffset) / (JsVarInt)JSV_ARRAYBUFFER_GET_SIZE(type); 
    if (length<0) length=0;
  }
  JsVar *typedArr = jsvNewWithFlags(JSV_ARRAYBUFFER);
  if (typedArr) {
    typedArr->varData.arraybuffer.type = type;
    typedArr->varData.arraybuffer.byteOffset = (unsigned short)byteOffset;
    typedArr->varData.arraybuffer.length = (unsigned short)length;
    jsvSetFirstChild(typedArr, jsvGetRef(jsvRef(arrayBuffer)));

    if (copyData) {
      // if we were given an array, populate this ArrayBuffer
      JsvIterator it;
      jsvIteratorNew(&it, arr, JSIF_DEFINED_ARRAY_ElEMENTS);
      while (jsvIteratorHasElement(&it)) {
        JsVar *idx = jsvIteratorGetKey(&it);
        if (jsvIsInt(idx)) {
          JsVar *val = jsvIteratorGetValue(&it);
          // TODO: This is horrible! We need to try and iterate properly...
          jsvArrayBufferSet(typedArr, (size_t)jsvGetInteger(idx), val);
          jsvUnLock(val);
        }
        jsvUnLock(idx);
        jsvIteratorNext(&it);
      }
      jsvIteratorFree(&it);
    }
  }
  jsvUnLock(arrayBuffer);
  return typedArr;
}
Exemplo n.º 7
0
JsVar *_jswrap_array_map_or_forEach(JsVar *parent, JsVar *funcVar, JsVar *thisVar, bool isMap) {
  const char *name = isMap ? "map":"forEach";
  if (!jsvIsIterable(parent)) {
    jsError("Array.%s can only be called on something iterable", name);
    return 0;
  }
  if (!jsvIsFunction(funcVar)) {
    jsError("Array.%s's first argument should be a function", name);
    return 0;
  }
  if (!jsvIsUndefined(thisVar) && !jsvIsObject(thisVar)) {
    jsError("Array.%s's second argument should be undefined, or an object", name);
    return 0;
  }
  JsVar *array = 0;
  if (isMap)
    array = jsvNewWithFlags(JSV_ARRAY);
  if (array || !isMap) {
    JsvIterator it;
    jsvIteratorNew(&it, parent);
    while (jsvIteratorHasElement(&it)) {
      JsVar *index = jsvIteratorGetKey(&it);
      if (jsvIsInt(index)) {
        JsVarInt idxValue = jsvGetInteger(index);

        JsVar *args[3], *mapped;
        args[0] = jsvIteratorGetValue(&it);
        args[1] = jsvNewFromInteger(idxValue); // child is a variable name, create a new variable for the index
        args[2] = parent;
        mapped = jspeFunctionCall(funcVar, 0, thisVar, false, 3, args);
        jsvUnLock(args[0]);
        jsvUnLock(args[1]);
        if (mapped) {
          if (isMap) {
            JsVar *name = jsvNewFromInteger(idxValue);
            if (name) { // out of memory?
              jsvMakeIntoVariableName(name, mapped);
              jsvAddName(array, name);
              jsvUnLock(name);
            }
          }
          jsvUnLock(mapped);
        }
      }
      jsvUnLock(index);
      jsvIteratorNext(&it);
    }
    jsvIteratorFree(&it);
  }
  return array;
}
Exemplo n.º 8
0
/** This is for Object.keys and Object. However it uses a callback so doesn't allocate anything */
void jswrap_object_keys_or_property_names_cb(
    JsVar *obj,
    bool includeNonEnumerable,  ///< include 'hidden' items
    bool includePrototype, ///< include items for the prototype too (for autocomplete)
    void (*callback)(void *data, JsVar *name),
    void *data
) {
  // 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) {
          callback(data, name);
          jsvUnLock(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);
    }

    while (symbols) {
      unsigned int i;
      unsigned char symbolCount = READ_FLASH_UINT8(&symbols->symbolCount);
      for (i=0;i<symbolCount;i++) {
        unsigned short strOffset = READ_FLASH_UINT16(&symbols->symbols[i].strOffset);
#ifndef USE_FLASH_MEMORY
        JsVar *name = jsvNewFromString(&symbols->symbolChars[strOffset]);
#else
        // On the esp8266 the string is in flash, so we have to copy it to RAM first
        // We can't use flash_strncpy here because it assumes that strings start on a word
        // boundary and that's not the case here.
        char buf[64], *b = buf, c; const char *s = &symbols->symbolChars[strOffset];
        do { c = READ_FLASH_UINT8(s++); *b++ = c; } while (c && b != buf+64);
        JsVar *name = jsvNewFromString(buf);
#endif
        //os_printf_plus("OBJ cb %s\n", buf);
        callback(data, name);
        jsvUnLock(name);
      }

      symbols = 0;
      if (includePrototype) {
        includePrototype = false;
        symbols = jswGetSymbolListForObjectProto(obj);
      }
    }

    if (jsvIsArray(obj) || jsvIsString(obj)) {
      JsVar *name = jsvNewFromString("length");
      callback(data, name);
      jsvUnLock(name);
    }
  }
}
Exemplo n.º 9
0
JsVar *_jswrap_array_iterate_with_callback(const char *name, JsVar *parent, JsVar *funcVar, JsVar *thisVar, bool wantArray, bool isBoolCallback, bool expectedValue) {
  if (!jsvIsIterable(parent)) {
    jsExceptionHere(JSET_ERROR, "Array.%s can only be called on something iterable", name);
    return 0;
  }
  if (!jsvIsFunction(funcVar)) {
    jsExceptionHere(JSET_ERROR, "Array.%s's first argument should be a function", name);
    return 0;
  }
  if (!jsvIsUndefined(thisVar) && !jsvIsObject(thisVar)) {
    jsExceptionHere(JSET_ERROR, "Array.%s's second argument should be undefined, or an object", name);
    return 0;
  }
  JsVar *result = 0;
  if (wantArray)
    result = jsvNewEmptyArray();
  bool isDone = false;
  if (result || !wantArray) {
    JsvIterator it;
    jsvIteratorNew(&it, parent);
    while (jsvIteratorHasElement(&it) && !isDone) {
      JsVar *index = jsvIteratorGetKey(&it);
      if (jsvIsInt(index)) {
        JsVarInt idxValue = jsvGetInteger(index);

        JsVar *args[3], *cb_result;
        args[0] = jsvIteratorGetValue(&it);
        args[1] = jsvNewFromInteger(idxValue); // child is a variable name, create a new variable for the index
        args[2] = parent;
        cb_result = jspeFunctionCall(funcVar, 0, thisVar, false, 3, args);
        jsvUnLockMany(2,args);
        if (cb_result) {
          bool matched;
          if (isBoolCallback)
            matched = (jsvGetBool(cb_result) == expectedValue);
          if (wantArray) {
            if (isBoolCallback) { // filter
              if (matched) {
                jsvArrayPushAndUnLock(result, jsvIteratorGetValue(&it));
              }
            } else { // map
              JsVar *name = jsvNewFromInteger(idxValue);
              if (name) { // out of memory?
                jsvMakeIntoVariableName(name, cb_result);
                jsvAddName(result, name);
                jsvUnLock(name);
              }
            }
          } else {
            // break the loop early if expecting a particular value and didn't get it
            if (isBoolCallback && !matched)
              isDone = true;
          }
          jsvUnLock(cb_result);
        }
      }
      jsvUnLock(index);
      jsvIteratorNext(&it);
    }
    jsvIteratorFree(&it);
  }
  /* boolean result depends on whether the loop terminated
     early for 'some' or completed for 'every' */
  if (!wantArray && isBoolCallback) {
    result = jsvNewFromBool(isDone != expectedValue);
  }
  return result;
}
Exemplo n.º 10
0
/** This is for Object.keys and Object. However it uses a callback so doesn't allocate anything */
void jswrap_object_keys_or_property_names_cb(
    JsVar *obj,
    bool includeNonEnumerable,  ///< include 'hidden' items
    bool includePrototype, ///< include items for the prototype too (for autocomplete)
    void (*callback)(void *data, JsVar *name),
    void *data
) {
  // 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) {
          callback(data, name);
          jsvUnLock(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) {
    JsVar *protoOwner = jspGetPrototypeOwner(obj);
    if (protoOwner) {
      // If protoOwner then this is the prototype (protoOwner is the object)
      const JswSymList *symbols = jswGetSymbolListForObjectProto(protoOwner);
      jsvUnLock(protoOwner);
      _jswrap_object_keys_or_property_names_iterator(symbols, callback, data);
    } else if (!jsvIsObject(obj) || jsvIsRoot(obj)) {
      // get symbols, but only if we're not doing it on a basic object
       const JswSymList *symbols = jswGetSymbolListForObject(obj);
      _jswrap_object_keys_or_property_names_iterator(symbols, callback, data);
    }

    if (includePrototype) {
      if (jsvIsObject(obj)) {
        JsVar *proto = jsvObjectGetChild(obj, JSPARSE_INHERITS_VAR, 0);
        while (jsvIsObject(proto)) {
          const JswSymList *symbols = jswGetSymbolListForObjectProto(proto);
          _jswrap_object_keys_or_property_names_iterator(symbols, callback, data);
          JsVar *p2 = jsvObjectGetChild(proto, JSPARSE_INHERITS_VAR, 0);
          jsvUnLock(proto);
          proto = p2;
        }
      }
      // finally include Object/String/etc
      const JswSymList *symbols = jswGetSymbolListForObjectProto(obj);
      _jswrap_object_keys_or_property_names_iterator(symbols, callback, data);
    }

    if (jsvIsArray(obj) || jsvIsString(obj)) {
      JsVar *name = jsvNewFromString("length");
      callback(data, name);
      jsvUnLock(name);
    }
  }
}