diff --git a/docs/06.REFERENCE-COUNTING.md b/docs/06.REFERENCE-COUNTING.md new file mode 100644 index 000000000..720c7f23d --- /dev/null +++ b/docs/06.REFERENCE-COUNTING.md @@ -0,0 +1,174 @@ +## 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_release_value`. + +```c + jerry_value_t global = jerry_get_global_object (); + + /* 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 indepent, and both must be destroyed before the global + * object can be freed. */ + + jerry_release_value (global); + + /* Without jerry_release_value() 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. + +```c + jerry_value_t pi_ref1 = jerry_create_number (3.14); + jerry_value_t pi_ref2 = jerry_acquire_value (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_release_value (pi_ref1); + jerry_release_value (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. + +```c + jerry_release_value (pi_ref1); + jerry_release_value (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. The next example +shows this behaviour through property getting and setting. + +```c + jerry_value_t prop_value = jerry_get_property (...); + + /* 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 is failed + * the prop_value contains a live reference to an error object. + * This reference must be released as well. */ + + if (jerry_value_has_error_flag (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_release_value (prop_value); + + /* Property setting is the same. */ + + jerry_value_t result = jerry_set_property (..., 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 the operation is + * successful. */ + + /* The new_prop_value can be passed to other JerryScript API + * functions before the jerry_release_value () call. */ + + jerry_release_value (new_prop_value); + + /* The reference stored in 'result' variable contains whether + * the operation is successful and must also be freed. */ + + if (jerry_value_has_error_flag (result)) + { + /* Errors can be handled here. */ + } + else + { + /* A reference to a true primitive value is returned. */ + } + + jerry_release_value (result); +``` + +The simplest form of setting a property without error checking +is the following: + +```c + /* There are no 'ifs' in this snippet. */ + jerry_release_value (jerry_set_property (..., new_prop_value)); + jerry_release_value (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. + +```c +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_acquire_value () 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_acquire_value () can be used to create new references. */ + return jerry_create_string (...); +} +``` + +Duplicating a `jerry_value_t` in C does not create another live reference. + +```c + jerry_value_t undef = jerry_create_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_release_value (undef2); + + /* Dead references can be reassigned again. */ + undef = jerry_create_boolean (true); +``` + +References can be duplicated in C as long as only one of them is freed. + +```c + jerry_value_t a = jerry_create_boolean (true); + + jerry_value_t b = a; + jerry_value_t c = a; + + /* A new reference is assigned to 'a'. */ + a = jerry_create_boolean (false); + + [...] + + jerry_release_value (a); + /* The 'a' (boolean false) reference becomes dead (invalid). */ + + jerry_release_value (c); + /* Both 'b' and 'c' (boolean true) references become dead. */ + + /* Since all references are released no memory leak is possible. */ +```