jerryscript/docs/06.REFERENCE-COUNTING.md
Dániel Bátyai 9860d66a56
Rework the public API (#4829)
Related to #4186.

Some notable changes:
  - The term 'Error' now strictly refers to native Error objects defined in
    the ECMA standard, which are ordinary objects. All other uses of
    'error' or 'error reference' where the term refers to a thrown value is
    now called 'exception'.

  - Simplified the naming scheme of many String API functions. These functions
    will now also take an 'encoding' argument to specify the desired
    encoding in which to operate.

  - Removed the substring-copy-to-buffer functions. These functions
    behaved awkwardly, as they use character index to specify the
    start/end positions, and were mostly used incorrectly with byte
    offsets instead. The functionality can still be replicated with
    other functions if necessary.

  - String-to-buffer functions will no longer fail if the buffer is not
    sufficiently large, the string will instead be cropped.

  - Fixed the usage of the '_sz' prefix in many API functions. The term
    'sz' means zero-terminated string in hungarian notation, this was
    used incorrectly in many cases.

  - Renamed most of the public API functions to have shorter, more on-point
    names, rather than the often too long descriptive names. Functions are now
    also grouped by the type of value they operate on, where this makes
    sense.

JerryScript-DCO-1.0-Signed-off-by: Dániel Bátyai dbatyai@inf.u-szeged.hu
2021-12-06 10:20:09 +01:00

5.5 KiB

Reference counting in JerryScript

In JerryScript all jerry_value_t values are independent references to internal objects. Values returned by JerryScript API functions are always live references and must be released by jerry_value_free.

  jerry_value_t global = jerry_current_realm ();

  /* The value stored in the 'global' variable contains a live
   * reference to the global object. The system also keeps its
   * own live reference to the global object. These two references
   * are independent, and both must be destroyed before the global
   * object can be freed. */

  jerry_value_free (global);

  /* Without jerry_value_free() the global object will not
   * be freed even by jerry_cleanup(). After the reference
   * is released it becomes a dead reference and cannot be
   * used anymore. */

Multiple references might refer to the same internal object even though their jerry_value_t representation might be different.

  jerry_value_t pi_ref1 = jerry_number (3.14);
  jerry_value_t pi_ref2 = jerry_value_copy (pi_ref1);

  /* Both pi_ref1 and pi_ref2 refer to the same 3.14 value
   * although they might not be equal in C (pi_ref1 != pi_ref2). */

  /* Both references must be released. */
  jerry_value_free (pi_ref1);
  jerry_value_free (pi_ref2);

Releasing the same jerry_value_t twice to release two live references is not allowed and it might cause crashes. Hence the following code is an INCORRECT WAY of releasing the 3.14 value.

  jerry_value_free (pi_ref1);
  jerry_value_free (pi_ref1);

JerryScript API functions returning with a jerry_value_t always return with a new live reference. Passing a jerry_value_t to an API function never releases its reference (unless explicitly stated in the documentation). The next example shows this behaviour through property getting and setting.

  jerry_value_t prop_value = jerry_object_get (...);

  /* The prop_value must be released later because both the base
   * object and the prop_value have an independent reference to
   * the same JavaScript value. When the operation fails, the
   * prop_value contains a live reference to an error object.
   * This reference must be released as well. */

  if (jerry_value_is_exception (prop_value))
  {
    /* Errors can be handled here. */
  }
  else
  {
    /* The application has a live reference to the property
     * value even if the base object is freed by the garbage
     * collector. */
  }

  /* The prop_value must be released. */
  jerry_value_free (prop_value);

  /* Property setting is the same. */

  jerry_value_t new_prop_value = jerry_number (2.718);
  jerry_value_t result = jerry_object_set (..., new_prop_value);

  /* If the property set is successful, a new reference is created
   * for the value referenced by new_prop_value. The new_prop_value
   * reference must be released regardless of whether the operation
   * is successful. */

  /* The new_prop_value can be passed to other JerryScript API
   * functions before the jerry_value_free () call. */

  jerry_value_free (new_prop_value);

  /* The reference stored in the 'result' variable is live whether
   * the operation is successful or not, and must also be freed. */

  if (jerry_value_is_exception (result))
  {
    /* Errors can be handled here. */
  }
  else
  {
    /* A reference to a true primitive value is returned. */
  }

  jerry_value_free (result);

The simplest form of setting a property without error checking is the following:

  /* There are no 'ifs' in this snippet. */
  jerry_value_free (jerry_object_set (..., new_prop_value));
  jerry_value_free (new_prop_value);

The reference returned by a jerry_external_handler_t callback transfers the ownership of the live reference. Otherwise the referenced object could be freed by the garbage collector.

jerry_value_t my_external_handler (const jerry_value_t function_obj,
                                   const jerry_value_t this_val,
                                   const jerry_value_t args_p[],
                                   const jerry_length_t args_count
{
  /* Do not release function_obj, this_val, and args_p because
   * these references are automatically released after the handler
   * is returned. This approach reduces code size which is useful
   * on embedded systems. However you can create other references
   * to them by calling jerry_value_copy () if needed. */

  /* Since the ownership of the reference is transferred to the
   * caller the following snippet is valid. */

  /* If the value to be returned is needed for other purposes the
   * jerry_value_copy () can be used to create new references. */
  return jerry_string (...);
}

Duplicating a jerry_value_t in C does not create another live reference.

  jerry_value_t undef = jerry_undefined ();
  jerry_value_t undef2 = undef;

  /* Releasing either undef or undef2 is valid but not both.
   * After the release both references become dead (invalid). */
  jerry_value_free (undef2);

  /* Dead references can be reassigned again. */
  undef = jerry_boolean (true);

References can be duplicated in C as long as only one of them is freed.

  jerry_value_t a = jerry_boolean (true);

  jerry_value_t b = a;
  jerry_value_t c = a;

  /* A new reference is assigned to 'a'. */
  a = jerry_boolean (false);

  [...]

  jerry_value_free (a);
  /* The 'a' (boolean false) reference becomes dead (invalid). */

  jerry_value_free (c);
  /* Both 'b' and 'c' (boolean true) references become dead. */

  /* Since all references are released, no memory leak occurs. */