Skip to content

Latest commit

 

History

History
600 lines (455 loc) · 15.5 KB

03.API-EXAMPLE.md

File metadata and controls

600 lines (455 loc) · 15.5 KB

JerryScript Engine can be embedded into any application, providing the way to run JavaScript in a large range of environments - from desktops to low-memory microcontrollers.

This guide is intended to introduce you to JerryScript embedding API through creation of simple JavaScript shell.

Step 1. Execute JavaScript from your application

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

int
main (void)
{
  const jerry_char_t script[] = "var str = 'Hello, World!';";
  size_t script_size = strlen ((const char *) script);

  bool ret_value = jerry_run_simple (script, script_size, JERRY_INIT_EMPTY);

  return (ret_value ? 0 : 1);
}

The application will return with zero exit code.

Step 2. Split engine initialization and script execution

Here we perform the same actions, as jerry_run_simple, while splitting into several steps:

  • engine initialization
  • script code setup
  • script execution
  • engine cleanup
#include <string.h>
#include "jerryscript.h"
#include "jerryscript-ext/handler.h"

int
main (void)
{
  const jerry_char_t script[] = "print ('Hello, World!');";
  size_t script_size = strlen ((const char *) script);

  /* Initialize engine */
  jerry_init (JERRY_INIT_EMPTY);

  /* Register 'print' function from the extensions */
  jerryx_handler_register_global ((const jerry_char_t *) "print",
                                  jerryx_handler_print);

  /* Setup Global scope code */
  jerry_value_t parsed_code = jerry_parse (script, script_size, false);

  if (!jerry_value_has_error_flag (parsed_code))
  {
    /* Execute the parsed source code in the Global scope */
    jerry_value_t ret_value = jerry_run (parsed_code);

    /* Returned value must be freed */
    jerry_release_value (ret_value);
  }

  /* Parsed source code must be freed */
  jerry_release_value (parsed_code);

  /* Cleanup engine */
  jerry_cleanup ();

  return 0;
}

Our code is more complex now, but it introduces possibilities to interact with JerryScript step-by-step: setup native objects, call JavaScript functions, etc.

Step 3. Execution in 'eval'-mode

#include <string.h>
#include "jerryscript.h"
#include "jerryscript-ext/handler.h"

int
main (void)
{
  const jerry_char_t script_1[] = "var s = 'Hello, World!';";
  const jerry_char_t script_2[] = "print (s);";

  /* Initialize engine */
  jerry_init (JERRY_INIT_EMPTY);

  /* Register 'print' function from the extensions */
  jerryx_handler_register_global ((const jerry_char_t *) "print",
                                  jerryx_handler_print);

  jerry_value_t eval_ret;

  /* Evaluate script1 */
  eval_ret = jerry_eval (script_1,
                         strlen ((const char *) script_1),
                         false);

  /* Free JavaScript value, returned by eval */
  jerry_release_value (eval_ret);

  /* Evaluate script2 */
  eval_ret = jerry_eval (script_2,
                         strlen ((const char *) script_2),
                         false);

  /* Free JavaScript value, returned by eval */
  jerry_release_value (eval_ret);

  /* Cleanup engine */
  jerry_cleanup ();

  return 0;
}

This way, we execute two independent script parts in one execution environment. The first part initializes string variable, and the second outputs the variable.

Step 4. Interaction with JavaScript environment

#include <string.h>
#include "jerryscript.h"
#include "jerryscript-ext/handler.h"

int
main (void)
{
  const jerry_char_t str[] = "Hello, World!";
  const jerry_char_t script[] = "print (s);";

  /* Initializing JavaScript environment */
  jerry_init (JERRY_INIT_EMPTY);

  /* Register 'print' function from the extensions */
  jerryx_handler_register_global ((const jerry_char_t *) "print",
                                  jerryx_handler_print);

  /* Getting pointer to the Global object */
  jerry_value_t global_object = jerry_get_global_object ();

  /* Constructing strings */
  jerry_value_t prop_name = jerry_create_string ((const jerry_char_t *) "s");
  jerry_value_t prop_value = jerry_create_string (str);

  /* Setting the string value as a property of the Global object */
  jerry_set_property (global_object, prop_name, prop_value);

  /* Releasing string values, as it is no longer necessary outside of engine */
  jerry_release_value (prop_name);
  jerry_release_value (prop_value);

  /* Releasing the Global object */
  jerry_release_value (global_object);

  /* Now starting script that would output value of just initialized field */
  jerry_value_t eval_ret = jerry_eval (script,
                                       strlen ((const char *) script),
                                       false);

  /* Free JavaScript value, returned by eval */
  jerry_release_value (eval_ret);

  /* Freeing engine */
  jerry_cleanup ();

  return 0;
}

The sample will also output 'Hello, World!'. However, now it is not just a part of the source script, but the value, dynamically supplied to the engine.

Step 5. Description of JerryScript value descriptors

JerryScript value can be a boolean, number, null, object, string or undefined. The value has an error flag, that indicates whether is an error or not. Every type has an error flag not only objects. The error flag should be cleared before the value is passed as an argument, otherwise it can lead to a type error. The error objects created by API functions has the error flag set.

The following example function will output a JavaScript value:

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

static void
print_value (const jerry_value_t value)
{
  if (jerry_value_is_undefined (value))
  {
    printf ("undefined");
  }
  else if (jerry_value_is_null (value))
  {
    printf ("null");
  }
  else if (jerry_value_is_boolean (value))
  {
    if (jerry_get_boolean_value (value))
    {
      printf ("true");
    }
    else
    {
      printf ("false");
    }
  }
  /* Float value */
  else if (jerry_value_is_number (value))
  {
    printf ("number");
  }
  /* String value */
  else if (jerry_value_is_string (value))
  {
    /* Determining required buffer size */
    jerry_size_t req_sz = jerry_get_string_size (value);
    jerry_char_t str_buf_p[req_sz + 1];

    jerry_string_to_char_buffer (value, str_buf_p, req_sz);
    str_buf_p[req_sz] = '\0';

    printf ("%s", (const char *) str_buf_p);
  }
  /* Object reference */
  else if (jerry_value_is_object (value))
  {
    printf ("[JS object]");
  }

  printf ("\n");
}

Simple JavaScript shell

Now all building blocks, necessary to construct JavaScript shell, are ready.

Shell operation can be described with the following loop:

  • read command;
  • if command is 'quit'
    • exit loop;
  • else
    • eval (command);
    • print result of eval;
    • loop.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jerryscript.h"
#include "jerryscript-ext/handler.h"

void print_value (const jerry_value_t);

int
main (void)
{
  bool is_done = false;

  /* Initialize engine */
  jerry_init (JERRY_INIT_EMPTY);

  /* Register 'print' function from the extensions */
  jerryx_handler_register_global ((const jerry_char_t *) "print",
                                  jerryx_handler_print);

  while (!is_done)
  {
    char cmd[256];
    char *cmd_tail = cmd;
    size_t len = 0;

    printf ("> ");

    /* Read next command */
    while (true)
    {
      if (fread (cmd_tail, 1, 1, stdin) != 1 && len == 0)
      {
        is_done = true;
        break;
      }
      if (*cmd_tail == '\n')
      {
        break;
      }

      cmd_tail++;
      len++;
    }

    /* If the command is "quit", break the loop */
    if (!strncmp (cmd, "quit\n", strlen ("quit\n")))
    {
      break;
    }

    jerry_value_t ret_val;

    /* Evaluate entered command */
    ret_val = jerry_eval ((const jerry_char_t *) cmd,
                          len,
                          false);

    /* If command evaluated successfully, print value, returned by eval */
    if (jerry_value_has_error_flag (ret_val))
    {
      /* Evaluated JS code thrown an exception
       *  and didn't handle it with try-catch-finally */
      printf ("Unhandled JS exception occurred: ");
    }

    print_value (ret_val);
    jerry_release_value (ret_val);
  }

  /* Cleanup engine */
  jerry_cleanup ();

  return 0;
}

The application inputs commands and evaluates them, one after another.

Step 6. Creating JS object in global context

In this example we demonstrate how to use native function and structures in JavaScript.

#include <string.h>
#include "jerryscript.h"
#include "jerryscript-ext/handler.h"

struct my_struct
{
  const char *msg;
} my_struct;

/**
 * Get a string from a native object
 */
static jerry_value_t
get_msg_handler (const jerry_value_t func_value, /**< function object */
                 const jerry_value_t this_value, /**< this arg */
                 const jerry_value_t *args_p, /**< function arguments */
                 const jerry_length_t args_cnt) /**< number of function arguments */
{
  return jerry_create_string ((const jerry_char_t *) my_struct.msg);
} /* get_msg_handler */

int
main (void)
{
  /* Initialize engine */
  jerry_init (JERRY_INIT_EMPTY);

  /* Register 'print' function from the extensions */
  jerryx_handler_register_global ((const jerry_char_t *) "print",
                                  jerryx_handler_print);

  /* Do something with the native object */
  my_struct.msg = "Hello, World!";

  /* Create an empty JS object */
  jerry_value_t object = jerry_create_object ();

  /* Create a JS function object and wrap into a jerry value */
  jerry_value_t func_obj = jerry_create_external_function (get_msg_handler);

  /* Set the native function as a property of the empty JS object */
  jerry_value_t prop_name = jerry_create_string ((const jerry_char_t *) "myFunc");
  jerry_set_property (object, prop_name, func_obj);
  jerry_release_value (prop_name);
  jerry_release_value (func_obj);

  /* Wrap the JS object (not empty anymore) into a jerry api value */
  jerry_value_t global_object = jerry_get_global_object ();

  /* Add the JS object to the global context */
  prop_name = jerry_create_string ((const jerry_char_t *) "MyObject");
  jerry_set_property (global_object, prop_name, object);
  jerry_release_value (prop_name);
  jerry_release_value (object);
  jerry_release_value (global_object);

  /* Now we have a "builtin" object called MyObject with a function called myFunc()
   *
   * Equivalent JS code:
   *                    var MyObject = { myFunc : function () { return "some string value"; } }
   */
  const jerry_char_t script[] = " \
    var str = MyObject.myFunc (); \
    print (str); \
  ";
  size_t script_size = strlen ((const char *) script);

  /* Evaluate script */
  jerry_value_t eval_ret = jerry_eval (script, script_size, false);

  /* Free JavaScript value, returned by eval */
  jerry_release_value (eval_ret);

  /* Cleanup engine */
  jerry_cleanup ();

  return 0;
}

The application will generate the following output:

Hello World

Step 7. Extending JS Objects with native functions

Here we create a JS Object with jerry_eval, then extend it with a native function. This function shows how to get a property value from the object and how to manipulate it.

#include <string.h>
#include "jerryscript.h"
#include "jerryscript-ext/handler.h"

/**
 * Add param to 'this.x'
 */
static jerry_value_t
add_handler (const jerry_value_t func_value, /**< function object */
             const jerry_value_t this_val, /**< this arg */
             const jerry_value_t *args_p, /**< function arguments */
             const jerry_length_t args_cnt) /**< number of function arguments */
{
  /* Get 'this.x' */
  jerry_value_t prop_name = jerry_create_string ((const jerry_char_t *) "x");
  jerry_value_t x_val = jerry_get_property (this_val, prop_name);

  if (!jerry_value_has_error_flag (x_val))
  {
    /* Convert Jerry API values to double */
    double x = jerry_get_number_value (x_val);
    double d = jerry_get_number_value (*args_p);

    /* Add the parameter to 'x' */
    jerry_value_t res_val = jerry_create_number (x + d);

    /* Set the new value of 'this.x' */
    jerry_set_property (this_val, prop_name, res_val);
    jerry_release_value (res_val);
  }

  jerry_release_value (x_val);
  jerry_release_value (prop_name);

  return jerry_create_undefined ();
} /* add_handler */

int
main (void)
{
  /* Initialize engine */
  jerry_init (JERRY_INIT_EMPTY);

  /* Register 'print' function from the extensions */
  jerryx_handler_register_global ((const jerry_char_t *) "print",
                                  jerryx_handler_print);

  /* Create a JS object */
  const jerry_char_t my_js_object[] = " \
    MyObject = \
    { x : 12, \
      y : 'Value of x is ', \
      foo: function () \
      { \
        return this.y + this.x; \
      } \
    } \
  ";

  jerry_value_t my_js_obj_val;

  /* Evaluate script */
  my_js_obj_val = jerry_eval (my_js_object,
                              strlen ((const char *) my_js_object),
                              false);

  /* Create a JS function object and wrap into a jerry value */
  jerry_value_t add_func_obj = jerry_create_external_function (add_handler);

  /* Set the native function as a property of previously created MyObject */
  jerry_value_t prop_name = jerry_create_string ((const jerry_char_t *) "add2x");
  jerry_set_property (my_js_obj_val, prop_name, add_func_obj);
  jerry_release_value (add_func_obj);
  jerry_release_value (prop_name);

  /* Free JavaScript value, returned by eval (my_js_object) */
  jerry_release_value (my_js_obj_val);

  const jerry_char_t script[] = " \
    var str = MyObject.foo (); \
    print (str); \
    MyObject.add2x (5); \
    print (MyObject.foo ()); \
  ";
  size_t script_size = strlen ((const char *) script);

  /* Evaluate script */
  jerry_value_t eval_ret = jerry_eval (script, script_size, false);

  /* Free JavaScript value, returned by eval */
  jerry_release_value (eval_ret);

  /* Cleanup engine */
  jerry_cleanup ();

  return 0;
}

The application will generate the following output:

Value of x is 12
Value of x is 17

Step 8. Changing the seed of pseudorandom generated numbers

If you want to change the seed of Math.random() generated numbers, you have to initialize the seed value with srand. A recommended method is using jerry_port_get_current_time() or something based on a constantly changing value, therefore every run produces truly random numbers.

#include <string.h>
#include <stdlib.h>
#include "jerryscript.h"
#include "jerryscript-port.h"
#include "jerryscript-ext/handler.h"

int
main (void)
{
  /* Initialize srand value */
  srand ((unsigned) jerry_port_get_current_time ());

  /* Generate a random number, and print it */
  const jerry_char_t script[] = "var a = Math.random (); print(a)";
  size_t script_size = strlen ((const char *) script);

  /* Initialize the engine */
  jerry_init (JERRY_INIT_EMPTY);

  /* Register the print function */
  jerryx_handler_register_global ((const jerry_char_t *) "print",
                                  jerryx_handler_print);

  /* Evaluate the script */
  jerry_value_t eval_ret = jerry_eval (script, script_size, false);

  /* Free the JavaScript value returned by eval */
  jerry_release_value (eval_ret);

  /* Cleanup the engine */
  jerry_cleanup ();

  return 0;
}

Further steps

For further API description, please visit API Reference page on JerryScript home page.