Jerry API implementation.

This commit is contained in:
Ruben Ayrapetyan 2015-03-31 13:56:40 +03:00
commit ef2265ee41
4 changed files with 494 additions and 41 deletions

View File

@ -440,6 +440,18 @@ ecma_op_extension_object_get_own_property (ecma_object_t *obj_p, /**< the extens
switch (field_p->type)
{
case JERRY_API_DATA_TYPE_UNDEFINED:
{
value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED);
break;
}
case JERRY_API_DATA_TYPE_NULL:
{
value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_NULL);
break;
}
case JERRY_API_DATA_TYPE_BOOLEAN:
{
value = ecma_make_simple_value (field_p->v_bool ? ECMA_SIMPLE_VALUE_TRUE : ECMA_SIMPLE_VALUE_FALSE);

View File

@ -36,6 +36,8 @@
*/
typedef enum
{
JERRY_API_DATA_TYPE_UNDEFINED, /**< undefined */
JERRY_API_DATA_TYPE_NULL, /**< null */
JERRY_API_DATA_TYPE_BOOLEAN, /**< bool */
JERRY_API_DATA_TYPE_FLOAT32, /**< 32-bit float */
JERRY_API_DATA_TYPE_FLOAT64, /**< 64-bit float */
@ -57,9 +59,9 @@ typedef struct ecma_object_t jerry_api_object_t;
/**
* Description of an extension function's argument
*/
typedef struct
typedef struct jerry_api_value_t
{
const jerry_api_data_type_t type; /**< argument data type */
jerry_api_data_type_t type; /**< argument data type */
union
{
@ -93,11 +95,10 @@ extern EXTERN_C
void jerry_api_release_object (jerry_api_object_t *object_p);
extern EXTERN_C
bool jerry_api_call_function (jerry_api_object_t *function_object_p,
jerry_api_value_t *retval_p,
const jerry_api_value_t args_p [],
uint32_t args_count);
void jerry_api_release_value (jerry_api_value_t *value_p);
extern EXTERN_C
jerry_api_string_t* jerry_api_create_string (const char *v);
extern EXTERN_C
jerry_api_object_t* jerry_api_create_object (void);
@ -118,6 +119,15 @@ bool jerry_api_set_object_field_value (jerry_api_object_t *object_p,
const char *field_name_p,
const jerry_api_value_t *field_value_p);
extern EXTERN_C
bool jerry_api_call_function (jerry_api_object_t *function_object_p,
jerry_api_value_t *retval_p,
const jerry_api_value_t args_p [],
uint16_t args_count);
extern EXTERN_C
jerry_api_object_t* jerry_api_get_global (void);
/**
* @}
*/

View File

@ -16,10 +16,14 @@
#include <stdio.h>
#include "deserializer.h"
#include "ecma-alloc.h"
#include "ecma-builtins.h"
#include "ecma-extension.h"
#include "ecma-function-object.h"
#include "ecma-gc.h"
#include "ecma-helpers.h"
#include "ecma-init-finalize.h"
#include "ecma-objects.h"
#include "ecma-objects-general.h"
#include "jerry.h"
#include "jrt.h"
@ -56,6 +60,152 @@ static jerry_flag_t jerry_flags;
*/
char jerry_extension_characters_buffer [CONFIG_EXTENSION_CHAR_BUFFER_SIZE];
/**
* Convert ecma-value to Jerry API value representation
*
* Note:
* if the output value contains string / object, it should be freed
* with jerry_api_release_string / jerry_api_release_object,
* just when it becomes unnecessary.
*/
static void
jerry_api_convert_ecma_value_to_api_value (jerry_api_value_t *out_value_p, /**< out: api value */
const ecma_value_t& value) /**< ecma-value (undefined,
* null, boolean, number,
* string or object */
{
JERRY_ASSERT (out_value_p != NULL);
if (ecma_is_value_undefined (value))
{
out_value_p->type = JERRY_API_DATA_TYPE_UNDEFINED;
}
if (ecma_is_value_null (value))
{
out_value_p->type = JERRY_API_DATA_TYPE_NULL;
}
else if (ecma_is_value_boolean (value))
{
out_value_p->type = JERRY_API_DATA_TYPE_BOOLEAN;
out_value_p->v_bool = ecma_is_value_true (value);
}
else if (ecma_is_value_number (value))
{
ecma_number_t *num = ecma_get_number_from_value (value);
#if CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT32
out_value_p->type = JERRY_API_DATA_TYPE_FLOAT32;
out_value_p->v_float32 = *num;
#elif CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT64
out_value_p->type = JERRY_API_DATA_TYPE_FLOAT64;
out_value_p->v_float64 = *num;
#endif /* CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT64 */
}
else if (ecma_is_value_string (value))
{
ecma_string_t *str = ecma_get_string_from_value (value);
out_value_p->type = JERRY_API_DATA_TYPE_STRING;
out_value_p->v_string = ecma_copy_or_ref_ecma_string (str);
}
else if (ecma_is_value_object (value))
{
ecma_object_t *obj = ecma_get_object_from_value (value);
ecma_ref_object (obj);
out_value_p->type = JERRY_API_DATA_TYPE_OBJECT;
out_value_p->v_object = obj;
}
else
{
/* Impossible type of conversion from ecma_value to api_value */
JERRY_UNREACHABLE ();
}
} /* jerry_api_convert_ecma_value_to_api_value */
/**
* Convert value, represented in Jerry API format, to ecma-value.
*
* Note:
* the output ecma-value should be freed with ecma_free_value when it becomes unnecessary.
*/
static void
jerry_api_convert_api_value_to_ecma_value (ecma_value_t *out_value_p, /**< out: ecma-value */
const jerry_api_value_t* api_value_p) /**< value in Jerry API format */
{
switch (api_value_p->type)
{
case JERRY_API_DATA_TYPE_UNDEFINED:
{
*out_value_p = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED);
break;
}
case JERRY_API_DATA_TYPE_NULL:
{
*out_value_p = ecma_make_simple_value (ECMA_SIMPLE_VALUE_NULL);
break;
}
case JERRY_API_DATA_TYPE_BOOLEAN:
{
*out_value_p = ecma_make_simple_value (api_value_p->v_bool ? ECMA_SIMPLE_VALUE_TRUE : ECMA_SIMPLE_VALUE_FALSE);
break;
}
case JERRY_API_DATA_TYPE_FLOAT32:
{
ecma_number_t *num = ecma_alloc_number ();
*num = static_cast<ecma_number_t> (api_value_p->v_float32);
*out_value_p = ecma_make_number_value (num);
break;
}
case JERRY_API_DATA_TYPE_FLOAT64:
{
ecma_number_t *num = ecma_alloc_number ();
*num = static_cast<ecma_number_t> (api_value_p->v_float64);
*out_value_p = ecma_make_number_value (num);
break;
}
case JERRY_API_DATA_TYPE_UINT32:
{
ecma_number_t *num = ecma_alloc_number ();
*num = static_cast<ecma_number_t> (api_value_p->v_uint32);
*out_value_p = ecma_make_number_value (num);
break;
}
case JERRY_API_DATA_TYPE_STRING:
{
ecma_string_t *str_p = ecma_copy_or_ref_ecma_string (api_value_p->v_string);
*out_value_p = ecma_make_string_value (str_p);
break;
}
case JERRY_API_DATA_TYPE_OBJECT:
{
ecma_object_t *obj_p = api_value_p->v_object;
ecma_ref_object (obj_p);
*out_value_p = ecma_make_object_value (obj_p);
break;
}
default:
{
JERRY_UNREACHABLE ();
}
}
} /* jerry_api_convert_api_value_to_ecma_value */
/**
* Extend Global scope with specified extension object
*
@ -148,34 +298,34 @@ jerry_api_release_object (jerry_api_object_t *object_p) /**< pointer acquired th
} /* jerry_api_release_object */
/**
* Call function specified by a function object
* Release specified Jerry API value
*/
void
jerry_api_release_value (jerry_api_value_t *value_p) /**< API value */
{
if (value_p->type == JERRY_API_DATA_TYPE_STRING)
{
jerry_api_release_string (value_p->v_string);
}
else if (value_p->type == JERRY_API_DATA_TYPE_OBJECT)
{
jerry_api_release_object (value_p->v_object);
}
} /* jerry_api_release_value */
/**
* Create a string
*
* Note:
* if call was performed successfully and returned value of type string or object, then caller
* should release the string / object with corresponding jerry_api_release_string / jerry_api_release_object,
* just when the value becomes unnecessary.
* caller should release the string with jerry_api_release_string, just when the value becomes unnecessary.
*
* @return true, if call was performed successfully, i.e.:
* - arguments number equals to the function's length property;
* - no unhandled exceptions were thrown;
* - returned value type is corresponding to one of jerry_api_data_type_t (if retval_p is not NULL)
* or is 'undefined' (if retval_p is NULL);
* false - otherwise.
* @return pointer to created string
*/
bool
jerry_api_call_function (jerry_api_object_t *function_object_p, /**< function object to call */
jerry_api_value_t *retval_p, /**< place for function's return value (if it is required)
* or NULL (if it should be 'undefined') */
const jerry_api_value_t args_p [], /**< function's call arguments
* (NULL if arguments number is zero) */
uint32_t args_count) /**< number of the arguments */
jerry_api_string_t*
jerry_api_create_string (const char *v) /**< string value */
{
JERRY_ASSERT (args_count == 0
|| args_p != NULL);
JERRY_UNIMPLEMENTED_REF_UNUSED_VARS ("API routine is not implemented",
function_object_p, retval_p, args_p, args_count);
} /* jerry_api_call_function */
return ecma_new_ecma_string ((const ecma_char_t*) v);
} /* jerry_api_create_string */
/**
* Create an object
@ -205,8 +355,35 @@ jerry_api_add_object_field (jerry_api_object_t *object_p, /**< object to add fie
const jerry_api_value_t *field_value_p, /**< value of the field */
bool is_writable) /**< flag indicating whether the created field should be writable */
{
JERRY_UNIMPLEMENTED_REF_UNUSED_VARS ("API routine is not implemented",
object_p, field_name_p, field_value_p, is_writable);
bool is_successful = false;
if (ecma_get_object_extensible (object_p))
{
ecma_string_t* field_name_str_p = ecma_new_ecma_string ((const ecma_char_t*) field_name_p);
ecma_property_t *prop_p = ecma_op_object_get_own_property (object_p, field_name_str_p);
if (prop_p == NULL)
{
is_successful = true;
ecma_value_t value_to_put;
jerry_api_convert_api_value_to_ecma_value (&value_to_put, field_value_p);
prop_p = ecma_create_named_data_property (object_p,
field_name_str_p,
is_writable,
true,
true);
ecma_named_data_property_assign_value (object_p, prop_p, value_to_put);
ecma_free_value (value_to_put, true);
}
ecma_deref_ecma_string (field_name_str_p);
}
return is_successful;
} /* jerry_api_add_object_field */
/**
@ -220,21 +397,37 @@ bool
jerry_api_delete_object_field (jerry_api_object_t *object_p, /**< object to delete field at */
const char *field_name_p) /**< name of the field */
{
JERRY_UNIMPLEMENTED_REF_UNUSED_VARS ("API routine is not implemented",
object_p, field_name_p);
bool is_successful = true;
ecma_string_t* field_name_str_p = ecma_new_ecma_string ((const ecma_char_t*) field_name_p);
ecma_completion_value_t delete_completion = ecma_op_object_delete (object_p,
field_name_str_p,
true);
if (!ecma_is_completion_value_normal (delete_completion))
{
JERRY_ASSERT (ecma_is_completion_value_throw (delete_completion));
is_successful = false;
}
ecma_free_completion_value (delete_completion);
ecma_deref_ecma_string (field_name_str_p);
return is_successful;
} /* jerry_api_delete_object_field */
/**
* Get value of field in the specified object
*
* Note:
* if value was retrieved successfully and it is of type string or object, then caller
* should release the string / object with corresponding jerry_api_release_string / jerry_api_release_object,
* just when the value becomes unnecessary.
* if value was retrieved successfully, it should be freed
* with jerry_api_release_value just when it becomes unnecessary.
*
* @return true, if field value was retrieved successfully, i.e. upon the call:
* - there is field with specified name in the object;
* - field value is not undefined nor null;
* false - otherwise.
*/
bool
@ -242,8 +435,31 @@ jerry_api_get_object_field_value (jerry_api_object_t *object_p, /**< object */
const char *field_name_p, /**< name of the field */
jerry_api_value_t *field_value_p) /**< out: field value, if retrieved successfully */
{
JERRY_UNIMPLEMENTED_REF_UNUSED_VARS ("API routine is not implemented",
object_p, field_name_p, field_value_p);
bool is_successful = true;
ecma_string_t* field_name_str_p = ecma_new_ecma_string ((const ecma_char_t*) field_name_p);
ecma_completion_value_t get_completion = ecma_op_object_get (object_p,
field_name_str_p);
if (ecma_is_completion_value_normal (get_completion))
{
ecma_value_t val = ecma_get_completion_value_value (get_completion);
jerry_api_convert_ecma_value_to_api_value (field_value_p, val);
}
else
{
JERRY_ASSERT (ecma_is_completion_value_throw (get_completion));
is_successful = false;
}
ecma_free_completion_value (get_completion);
ecma_deref_ecma_string (field_name_str_p);
return is_successful;
} /* jerry_api_get_object_field_value */
/**
@ -259,10 +475,114 @@ jerry_api_set_object_field_value (jerry_api_object_t *object_p, /**< object */
const char *field_name_p, /**< name of the field */
const jerry_api_value_t *field_value_p) /**< field value to set */
{
JERRY_UNIMPLEMENTED_REF_UNUSED_VARS ("API routine is not implemented",
object_p, field_name_p, field_value_p);
bool is_successful = true;
ecma_string_t* field_name_str_p = ecma_new_ecma_string ((const ecma_char_t*) field_name_p);
ecma_value_t value_to_put;
jerry_api_convert_api_value_to_ecma_value (&value_to_put, field_value_p);
ecma_completion_value_t set_completion = ecma_op_object_put (object_p,
field_name_str_p,
value_to_put,
true);
if (!ecma_is_completion_value_normal (set_completion))
{
JERRY_ASSERT (ecma_is_completion_value_throw (set_completion));
is_successful = false;
}
ecma_free_completion_value (set_completion);
ecma_free_value (value_to_put, true);
ecma_deref_ecma_string (field_name_str_p);
return is_successful;
} /* jerry_api_set_object_field_value */
/**
* Call function specified by a function object
*
* Note:
* if call was performed successfully, returned value should be freed
* with jerry_api_release_value just when the value becomes unnecessary.
*
* @return true, if call was performed successfully, i.e.:
* - no unhandled exceptions were thrown in connection with the call;
* false - otherwise.
*/
bool
jerry_api_call_function (jerry_api_object_t *function_object_p, /**< function object to call */
jerry_api_value_t *retval_p, /**< place for function's return value (if it is required)
* or NULL (if it should be 'undefined') */
const jerry_api_value_t args_p [], /**< function's call arguments
* (NULL if arguments number is zero) */
uint16_t args_count) /**< number of the arguments */
{
JERRY_ASSERT (args_count == 0 || args_p != NULL);
JERRY_STATIC_ASSERT (sizeof (args_count) == sizeof (ecma_length_t));
bool is_successful = true;
MEM_DEFINE_LOCAL_ARRAY (arg_values, args_count, ecma_value_t);
for (uint32_t i = 0; i < args_count; ++i)
{
jerry_api_convert_api_value_to_ecma_value (&arg_values [i], &args_p [i]);
}
ecma_completion_value_t call_completion;
call_completion = ecma_op_function_call (function_object_p,
ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED),
arg_values,
args_count);
if (ecma_is_completion_value_normal (call_completion))
{
if (retval_p != NULL)
{
jerry_api_convert_ecma_value_to_api_value (retval_p,
ecma_get_completion_value_value (call_completion));
}
}
else
{
/* unhandled exception during the function call */
JERRY_ASSERT (ecma_is_completion_value_throw (call_completion));
is_successful = false;
}
ecma_free_completion_value (call_completion);
for (uint32_t i = 0; i < args_count; i++)
{
ecma_free_value (arg_values [i], true);
}
MEM_FINALIZE_LOCAL_ARRAY (arg_values);
return is_successful;
} /* jerry_api_call_function */
/**
* Get global object
*
* Note:
* caller should release the object with jerry_api_release_object, just when the value becomes unnecessary.
*
* @return pointer to the global object
*/
jerry_api_object_t*
jerry_api_get_global (void)
{
return ecma_builtin_get (ECMA_BUILTIN_ID_GLOBAL);
} /* jerry_api_get_global */
/**
* Jerry engine initialization
*/

111
tests/unit/test_api.cpp Normal file
View File

@ -0,0 +1,111 @@
/* Copyright 2015 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "jerry.h"
#include "jerry-api.h"
const char *test_source = "var t = 1;\nfunction f () {\n return t;\n}\nthis.foo = f;\n";
/**
* Initialize Jerry API value with specified float64 number
*/
static void
test_api_init_api_value_float64 (jerry_api_value_t *out_value_p, /**< out: API value */
double v) /**< float64 value to initialize with */
{
out_value_p->type = JERRY_API_DATA_TYPE_FLOAT64;
out_value_p->v_float64 = v;
} /* test_api_init_api_value_float64 */
/**
* Initialize Jerry API value with specified float64 number
*/
static void
test_api_init_api_value_string (jerry_api_value_t *out_value_p, /**< out: API value */
const char* v) /**< string value to initialize with */
{
out_value_p->type = JERRY_API_DATA_TYPE_STRING;
out_value_p->v_string = jerry_api_create_string (v);
} /* test_api_init_api_value_string */
int
main (void)
{
jerry_init (JERRY_FLAG_EMPTY);
bool is_ok;
ssize_t sz;
jerry_api_value_t val_t, val_foo;
jerry_api_object_t* global_obj_p;
jerry_api_value_t res, args [2];
char buffer [16];
is_ok = jerry_parse (NULL, test_source, strlen (test_source));
assert (is_ok);
is_ok = (jerry_run (NULL) == JERRY_COMPLETION_CODE_OK);
assert (is_ok);
global_obj_p = jerry_api_get_global ();
is_ok = jerry_api_get_object_field_value (global_obj_p, "t", &val_t);
assert (is_ok
&& val_t.type == JERRY_API_DATA_TYPE_FLOAT64
&& val_t.v_float64 == 1.0);
jerry_api_release_value (&val_t);
is_ok = jerry_api_get_object_field_value (global_obj_p, "foo", &val_foo);
assert (is_ok
&& val_foo.type == JERRY_API_DATA_TYPE_OBJECT);
test_api_init_api_value_float64 (&args[0], 4);
test_api_init_api_value_float64 (&args[1], 2);
is_ok = jerry_api_call_function (val_foo.v_object, &res, args, 2);
assert (is_ok
&& res.type == JERRY_API_DATA_TYPE_FLOAT64
&& res.v_float64 == 1.0);
jerry_api_release_value (&res);
test_api_init_api_value_string (&args[0], "abcd");
is_ok = jerry_api_set_object_field_value (global_obj_p,
"t",
&args[0]);
assert (is_ok);
jerry_api_release_value (&args[0]);
is_ok = jerry_api_call_function (val_foo.v_object, &res, args, 2);
assert (is_ok
&& res.type == JERRY_API_DATA_TYPE_STRING);
sz = jerry_api_string_to_char_buffer (res.v_string, NULL, 0);
assert (sz == -5);
sz = jerry_api_string_to_char_buffer (res.v_string, buffer, -sz);
assert (sz == 5);
jerry_api_release_value (&res);
assert (!strcmp (buffer, "abcd"));
jerry_api_release_value (&val_foo);
jerry_api_release_object (global_obj_p);
jerry_cleanup ();
return 0;
}