1/* Copyright JS Foundation and other contributors, http://js.foundation
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "ecma-alloc.h"
17#include "ecma-array-object.h"
18#include "ecma-globals.h"
19#include "ecma-objects.h"
20#include "ecma-objects-general.h"
21#include "ecma-helpers.h"
22
23/** \addtogroup ecma ECMA
24 * @{
25 *
26 * \addtogroup ecmahelpers Helpers for operations with ECMA data types
27 * @{
28 */
29
30/**
31 * Create a native pointer property to store the native pointer and its type info.
32 *
33 * @return true - if property was just created with specified value,
34 *         false - otherwise, if property existed before the call, it's value was updated
35 */
36bool
37ecma_create_native_pointer_property (ecma_object_t *obj_p, /**< object to create property in */
38                                     void *native_p, /**< native pointer */
39                                     void *info_p) /**< native pointer's type info */
40{
41  ecma_string_t *name_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER);
42
43  if (ecma_op_object_is_fast_array (obj_p))
44  {
45    ecma_fast_array_convert_to_normal (obj_p);
46  }
47
48  ecma_property_t *property_p = ecma_find_named_property (obj_p, name_p);
49
50  bool is_new = (property_p == NULL);
51
52  ecma_native_pointer_t *native_pointer_p;
53
54  if (property_p == NULL)
55  {
56    ecma_property_value_t *value_p;
57    value_p = ecma_create_named_data_property (obj_p, name_p, ECMA_PROPERTY_CONFIGURABLE_WRITABLE, &property_p);
58
59    ECMA_CONVERT_DATA_PROPERTY_TO_INTERNAL_PROPERTY (property_p);
60
61    native_pointer_p = jmem_heap_alloc_block (sizeof (ecma_native_pointer_t));
62
63    ECMA_SET_INTERNAL_VALUE_POINTER (value_p->value, native_pointer_p);
64  }
65  else
66  {
67    ecma_property_value_t *value_p = ECMA_PROPERTY_VALUE_PTR (property_p);
68
69    ecma_native_pointer_t *iter_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_native_pointer_t, value_p->value);
70
71    /* There should be at least 1 native pointer in the chain */
72    JERRY_ASSERT (iter_p != NULL);
73
74    while (true)
75    {
76      if (iter_p->info_p == info_p)
77      {
78        /* The native info already exists -> update the corresponding data */
79        iter_p->data_p = native_p;
80        return false;
81      }
82
83      if (iter_p->next_p == NULL)
84      {
85        /* The native info does not exist -> append a new element to the chain */
86        break;
87      }
88
89      iter_p = iter_p->next_p;
90    }
91
92    native_pointer_p = jmem_heap_alloc_block (sizeof (ecma_native_pointer_t));
93
94    iter_p->next_p = native_pointer_p;
95  }
96
97  native_pointer_p->data_p = native_p;
98  native_pointer_p->info_p = info_p;
99  native_pointer_p->next_p = NULL;
100
101  return is_new;
102} /* ecma_create_native_pointer_property */
103
104/**
105 * Get value of native package stored in the object's property with specified identifier
106 *
107 * Note:
108 *      property identifier should be one of the following:
109 *        - LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER
110 *
111 * @return native pointer data if property exists
112 *         NULL otherwise
113 */
114ecma_native_pointer_t *
115ecma_get_native_pointer_value (ecma_object_t *obj_p, /**< object to get property value from */
116                               void *info_p) /**< native pointer's type info */
117{
118  if (ecma_op_object_is_fast_array (obj_p))
119  {
120    /* Fast access mode array can not have native pointer properties */
121    return NULL;
122  }
123
124  ecma_string_t *name_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER);
125  ecma_property_t *property_p = ecma_find_named_property (obj_p, name_p);
126
127  if (property_p == NULL)
128  {
129    return NULL;
130  }
131
132  ecma_property_value_t *value_p = ECMA_PROPERTY_VALUE_PTR (property_p);
133
134  ecma_native_pointer_t *native_pointer_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_native_pointer_t,
135                                                                             value_p->value);
136
137  JERRY_ASSERT (native_pointer_p != NULL);
138
139  while (native_pointer_p != NULL)
140  {
141    if (native_pointer_p->info_p == info_p)
142    {
143      return native_pointer_p;
144    }
145
146    native_pointer_p = native_pointer_p->next_p;
147  }
148
149  return NULL;
150} /* ecma_get_native_pointer_value */
151
152/**
153 * Delete the previously set native pointer by the native type info from the specified object.
154 *
155 * Note:
156 *      If the specified object has no matching native pointer for the given native type info
157 *      the function has no effect.
158 *
159 * @return true - if the native pointer has been deleted succesfully
160 *         false - otherwise
161 */
162bool
163ecma_delete_native_pointer_property (ecma_object_t *obj_p, /**< object to delete property from */
164                                     void *info_p) /**< native pointer's type info */
165{
166  if (ecma_op_object_is_fast_array (obj_p))
167  {
168    /* Fast access mode array can not have native pointer properties */
169    return false;
170  }
171
172  ecma_string_t *name_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER);
173  ecma_property_t *property_p = ecma_find_named_property (obj_p, name_p);
174
175  if (property_p == NULL)
176  {
177    return false;
178  }
179
180  ecma_property_value_t *value_p = ECMA_PROPERTY_VALUE_PTR (property_p);
181
182  ecma_native_pointer_t *native_pointer_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_native_pointer_t,
183                                                                             value_p->value);
184  ecma_native_pointer_t *prev_p = NULL;
185
186  JERRY_ASSERT (native_pointer_p != NULL);
187
188  while (native_pointer_p != NULL)
189  {
190    if (native_pointer_p->info_p == info_p)
191    {
192      if (prev_p == NULL)
193      {
194        if (native_pointer_p->next_p == NULL)
195        {
196          /* Only one native pointer property exists, so the property can be deleted as well. */
197          ecma_op_general_object_delete (obj_p, name_p, false);
198
199          jmem_heap_free_block (native_pointer_p, sizeof (ecma_native_pointer_t));
200          return true;
201        }
202        else
203        {
204          /* There are at least two native pointers and the first one should be deleted.
205             In this case the second element's data is copied to the head of the chain, and freed as well. */
206          ecma_native_pointer_t *next_p = native_pointer_p->next_p;
207          memcpy (native_pointer_p, next_p, sizeof (ecma_native_pointer_t));
208          jmem_heap_free_block (next_p, sizeof (ecma_native_pointer_t));
209          return true;
210        }
211      }
212      else
213      {
214        /* There are at least two native pointers and not the first element should be deleted.
215           In this case the current element's next element reference is copied to the previous element. */
216        prev_p->next_p = native_pointer_p->next_p;
217        jmem_heap_free_block (native_pointer_p, sizeof (ecma_native_pointer_t));
218        return true;
219      }
220    }
221
222    prev_p = native_pointer_p;
223    native_pointer_p = native_pointer_p->next_p;
224  }
225
226  return false;
227} /* ecma_delete_native_pointer_property */
228
229/**
230 * @}
231 * @}
232 */
233