jerryscript-project / jerryscript

Ultra-lightweight JavaScript engine for the Internet of Things.
https://jerryscript.net
Apache License 2.0
6.91k stars 669 forks source link

jerry_call with Jerry_Array argument will causing JERRY_FATAL_REF_COUNT_LIMIT randomly #4974

Closed weixiongmei closed 2 years ago

weixiongmei commented 2 years ago
JerryScript revision

d00f4810b0e8eaa6a6755e2d0ecbd1713ecfca49

Test case

Error Code

//periodically calling the following code every 1 second will causing JERRY_FATAL_REF_COUNT_LIMIT randomly
jerry_value_t jerry_arguments[2];
jerry_arguments[0] = jerry_number(0);
jerry_arguments[1] = jerry_array(1);
jerry_value_t element_0 = jerry_number(0);
jerry_value_free(jerry_object_set_index(jerry_arguments[1], 0,
    element_0
));
//jerry_value_free(element_0); //Also tried with this line un-commended, but having the same result
jerry_call(jerry_function, jerry_undefined(), jerry_arguments, 2);
jerry_value_free(jerry_arguments[0]);
jerry_value_free(jerry_arguments[1]);

Good Code

//periodically calling the following code every 1 second will NOT causing JERRY_FATAL_REF_COUNT_LIMIT
jerry_value_t jerry_arguments[2];
jerry_arguments[0] = jerry_number(0);
jerry_arguments[1] = jerry_number(1);
jerry_call(jerry_function, jerry_undefined(), jerry_arguments, 2);
jerry_value_free(jerry_arguments[0]);
jerry_value_free(jerry_arguments[1]);
Execution steps

Call the above code in the Error Section every 1 second, it will randomly causing JERRY_FATAL_REF_COUNT_LIMIT within an hour.

dbatyai commented 2 years ago

It would be good to know what the function that you're calling does. jerry_call returns with the value returned by the function, it's possible that needs to be freed as well.

Based on the error message, the most likely cause is that the Array object is leaking at some point, however based on the above code alone it's not possible to tell where exactly.

weixiongmei commented 2 years ago

Hi, it's just an empty javascript function, and has not return. Here's the code.

let test_func = function(arg0, arg1) { };
galpeter commented 2 years ago

Hi,

I've created a simple example application based on the details you provided (with minor modifications), see:

#include "jerryscript.h"

int main (void)
{ 
  jerry_init (JERRY_INIT_EMPTY);

  const jerry_char_t script[] = "function test_func(arg0, arg1) { };";
  jerry_value_t parsed_code = jerry_parse(script, sizeof (script) - 1, NULL);
  if (!jerry_value_is_error(parsed_code))
  { 
    jerry_value_t ret_value = jerry_run(parsed_code);
    jerry_value_free(ret_value);
  }
  jerry_value_free(parsed_code);

  // Get the test function
  jerry_value_t global = jerry_current_realm();
  jerry_value_t jerry_function_demo = jerry_object_get_sz (global, "test_func");
  jerry_value_free (global);

  for (int i = 0; i < 1000000; i++) {
    jerry_value_t jerry_arguments[2];
    jerry_arguments[0] = jerry_number(12321211414.3);
    jerry_arguments[1] = jerry_array(1);
    jerry_value_t element_0 = jerry_number(3.1231231212);
    jerry_value_free( jerry_object_set_index(jerry_arguments[1], 0, element_0) );

    jerry_value_free(element_0); // This is required

    jerry_value_t result = jerry_call(jerry_function_demo, jerry_undefined(), jerry_arguments, 2);
    jerry_value_free(result); // result must be freed

    jerry_value_free(jerry_arguments[0]);
    jerry_value_free(jerry_arguments[1]);
  }

  jerry_value_free(jerry_function_demo);

  jerry_cleanup();

  return 0;
}

Using this, I was unable to reproduce the ref count limit error. Still a few important key points:

How do you load thetest_func js function's value into a jerry_value_t? (which API calls are you using?)

weixiongmei commented 2 years ago

Hi @galpeter, thank you very much, seems like my problem is half way solved, i didn't freed the returned value of jerry_call, and didn't freed all the items of an array, but still having problem if the array contains jerry_string like the code below, would you mind to test it with a jerry_string item in the array? Thanks

char* test_string = "jerry test string";
jerry_value_t element_1 = jerry_string((jerry_char_t*)test_string, strlen(test_string), JERRY_ENCODING_UTF8);
jerry_value_free( jerry_object_set_index(jerry_arguments[1], 1, element_1));
jerry_value_free(element_1);
galpeter commented 2 years ago

I've added the code part you mentioned to the example above (just before the jerry_call) and it works without any errors. I think there the problem lies somewhere else.

weixiongmei commented 2 years ago

@galpeter I think I found the issue, would you mind to try one more thing please? Thank you~

Javascript

class Item {
    constructor(d0 = 0, l0 = false, d1 = 0, l1 = false) {
        this.da0 = d0;
        this.le0 = l0;
        this.da1 = d1;
        this.le1 = l1;

        Object.seal(this);
    }
}

let items = new Array(24);
for (let i = 0; i < 24; i++) {
    items[i] = new Item();
}

while(true)
{
  native_c_function(items.map(item => Object.values(item)))
}

C

static jerry_value_t native_c_function(const jerry_call_info_t* call_info_p, const jerry_value_t arguments[], const jerry_length_t arguments_count)
{
  return jerry_undefined();
}
galpeter commented 2 years ago

@galpeter I think I found the issue, would you mind to try one more thing please? Thank you~

Just to make sure, I've tried out this one also. There were no errors (excluding the fact that the while(true) never exited). The code I've used is:

#include "jerryscript.h"
#include <string.h>

#define XSTR(s) #s
#define STR(s) XSTR(s)

static jerry_value_t
demo_handler (const jerry_call_info_t *call_info_p, const jerry_value_t args_p[], const jerry_length_t args_cnt) {
  (void) call_info_p;
  (void) args_p;
  (void) args_cnt;

  return jerry_undefined();
}

int main (void) {
  const jerry_char_t script[] = STR(
class Item {
    constructor(d0 = 0, l0 = false, d1 = 0, l1 = false) {
        this.da0 = d0;
        this.le0 = l0;
        this.da1 = d1;
        this.le1 = l1;

        Object.seal(this);
    }
}

let items = new Array(24);
for (let i = 0; i < 24; i++) {
    items[i] = new Item();
};

while(true)
{
  native_c_function(items.map(item => Object.values(item)));
}

);

  jerry_init (JERRY_INIT_EMPTY);

  jerry_value_t global = jerry_current_realm ();
  jerry_value_t func_handler = jerry_function_external (demo_handler);
  jerry_value_free (jerry_object_set_sz (global, "native_c_function", func_handler));
  jerry_value_free (func_handler);
  jerry_value_free (global);

  jerry_value_t parsed_code = jerry_parse (script, sizeof (script) - 1, NULL);
  if (!jerry_value_is_error (parsed_code))
  {
    jerry_value_t ret_value = jerry_run (parsed_code); // This will never return
    jerry_value_free (ret_value);
  }

  jerry_value_free (parsed_code);

  jerry_cleanup ();

  return 0;
}