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-exceptions.h"
17#include "ecma-function-object.h"
18#include "ecma-gc.h"
19#include "ecma-globals.h"
20#include "ecma-helpers.h"
21#include "ecma-lcache.h"
22#include "ecma-lex-env.h"
23#include "ecma-objects.h"
24#include "ecma-proxy-object.h"
25#include "ecma-reference.h"
26#include "jrt.h"
27
28/** \addtogroup ecma ECMA
29 * @{
30 *
31 * \addtogroup references ECMA-Reference
32 * @{
33 */
34
35/**
36 * Resolve syntactic reference.
37 *
38 * @return ECMA_OBJECT_POINTER_ERROR - if the operation fails
39 *         pointer to lexical environment - if the reference's base is resolved sucessfully,
40 *         NULL - otherwise.
41 */
42ecma_object_t *
43ecma_op_resolve_reference_base (ecma_object_t *lex_env_p, /**< starting lexical environment */
44                                ecma_string_t *name_p) /**< identifier's name */
45{
46  JERRY_ASSERT (lex_env_p != NULL);
47
48  while (true)
49  {
50#if ENABLED (JERRY_ES2015)
51    if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_HOME_OBJECT_BOUND)
52    {
53      JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL);
54      lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp);
55    }
56#endif /* ENABLED (JERRY_ES2015) */
57
58    ecma_value_t has_binding = ecma_op_has_binding (lex_env_p, name_p);
59
60#if ENABLED (JERRY_ES2015_BUILTIN_PROXY)
61    if (ECMA_IS_VALUE_ERROR (has_binding))
62    {
63      return ECMA_OBJECT_POINTER_ERROR;
64    }
65#endif /* ENABLED (JERRY_ES2015_BUILTIN_PROXY) */
66
67    if (ecma_is_value_true (has_binding))
68    {
69      return lex_env_p;
70    }
71
72    if (lex_env_p->u2.outer_reference_cp == JMEM_CP_NULL)
73    {
74      return NULL;
75    }
76
77    lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp);
78  }
79} /* ecma_op_resolve_reference_base */
80
81#if ENABLED (JERRY_ES2015)
82/**
83 * Perform GetThisEnvironment and GetSuperBase operations
84 *
85 * See also: ECMAScript v6, 8.1.1.3.5
86 *
87 * @return ECMA_VALUE_ERROR - if the operation fails
88 *         ECMA_VALUE_UNDEFINED - if the home object is null
89 *         value of the [[HomeObject]].[[Prototype]] internal slot - otherwise
90 */
91ecma_value_t
92ecma_op_resolve_super_base (ecma_object_t *lex_env_p) /**< starting lexical environment */
93{
94  JERRY_ASSERT (lex_env_p != NULL);
95
96  while (true)
97  {
98    if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_HOME_OBJECT_BOUND)
99    {
100      ecma_object_t *home_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u1.home_object_cp);
101
102#if ENABLED (JERRY_ES2015_BUILTIN_PROXY)
103      if (ECMA_OBJECT_IS_PROXY (home_p))
104      {
105        return ecma_proxy_object_get_prototype_of (home_p);
106      }
107#endif /* ENABLED (JERRY_ES2015_BUILTIN_PROXY) */
108
109      jmem_cpointer_t proto_cp = ecma_op_ordinary_object_get_prototype_of (home_p);
110
111      if (proto_cp == JMEM_CP_NULL)
112      {
113        return ECMA_VALUE_NULL;
114      }
115
116      ecma_object_t *proto_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, proto_cp);
117      ecma_ref_object (proto_p);
118
119      return ecma_make_object_value (proto_p);
120    }
121
122    if (lex_env_p->u2.outer_reference_cp == JMEM_CP_NULL)
123    {
124      break;
125    }
126
127    lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp);
128  }
129
130  return ECMA_VALUE_UNDEFINED;
131} /* ecma_op_resolve_super_base */
132
133/**
134 * Helper method for HasBindig operation
135 *
136 * See also:
137 *         ECMA-262 v6, 8.1.1.2.1 steps 7-9;
138 *
139 * @return ECMA_VALUE_TRUE - if the property is unscopable
140 *         ECMA_VALUE_FALSE - if a the property is not unscopable
141 *         ECMA_VALUE_ERROR - otherwise
142 */
143static ecma_value_t
144ecma_op_is_prop_unscopable (ecma_object_t *binding_obj_p, /**< binding object */
145                            ecma_string_t *prop_name_p) /**< property's name */
146{
147  ecma_value_t unscopables = ecma_op_object_get_by_symbol_id (binding_obj_p, LIT_GLOBAL_SYMBOL_UNSCOPABLES);
148
149  if (ECMA_IS_VALUE_ERROR (unscopables))
150  {
151    return unscopables;
152  }
153
154  if (ecma_is_value_object (unscopables))
155  {
156    ecma_object_t *unscopables_obj_p = ecma_get_object_from_value (unscopables);
157    ecma_value_t get_unscopables_value = ecma_op_object_get (unscopables_obj_p, prop_name_p);
158    ecma_deref_object (unscopables_obj_p);
159
160    if (ECMA_IS_VALUE_ERROR (get_unscopables_value))
161    {
162      return get_unscopables_value;
163    }
164
165    bool is_blocked = ecma_op_to_boolean (get_unscopables_value);
166
167    ecma_free_value (get_unscopables_value);
168
169    return ecma_make_boolean_value (is_blocked);
170  }
171
172  ecma_free_value (unscopables);
173
174  return ECMA_VALUE_FALSE;
175} /* ecma_op_is_prop_unscopable */
176#endif /* ENABLED (JERRY_ES2015) */
177
178/**
179 * Helper method for HasBindig operation
180 *
181 * See also:
182 *         ECMA-262 v6, 8.1.1.2.1 steps 7-9;
183 *
184 * @return ECMA_VALUE_TRUE - if the property is unscopable
185 *         ECMA_VALUE_FALSE - if a the property is not unscopable
186 *         ECMA_VALUE_ERROR - otherwise
187 */
188
189/**
190 * Resolve value corresponding to the given object environment reference.
191 *
192 * Note: the steps are already include the HasBindig operation steps
193 *
194 *  See also:
195 *         ECMA-262 v6, 8.1.1.2.1
196 *
197 * @return ECMA_VALUE_ERROR - if the operation fails
198 *         ECMA_VALUE_NOT_FOUND - if the binding not exists or blocked via @@unscopables
199 *         result of the binding - otherwise
200 */
201ecma_value_t
202ecma_op_object_bound_environment_resolve_reference_value (ecma_object_t *lex_env_p, /**< lexical environment */
203                                                          ecma_string_t *name_p) /**< variable name */
204{
205  ecma_object_t *binding_obj_p = ecma_get_lex_env_binding_object (lex_env_p);
206  ecma_value_t found_binding;
207
208#if ENABLED (JERRY_ES2015_BUILTIN_PROXY)
209  if (ECMA_OBJECT_IS_PROXY (binding_obj_p))
210  {
211    found_binding = ecma_proxy_object_has (binding_obj_p, name_p);
212
213    if (!ecma_is_value_true (found_binding))
214    {
215      return ECMA_IS_VALUE_ERROR (found_binding) ? found_binding : ECMA_VALUE_NOT_FOUND;
216    }
217  }
218  else
219  {
220#endif /* ENABLED (JERRY_ES2015_BUILTIN_PROXY) */
221    found_binding = ecma_op_object_find (binding_obj_p, name_p);
222
223    if (ECMA_IS_VALUE_ERROR (found_binding) || !ecma_is_value_found (found_binding))
224    {
225      return found_binding;
226    }
227
228#if ENABLED (JERRY_ES2015)
229    if (JERRY_LIKELY (lex_env_p == ecma_get_global_scope ()))
230#endif /* ENABLED (JERRY_ES2015) */
231    {
232      return found_binding;
233    }
234#if ENABLED (JERRY_ES2015_BUILTIN_PROXY)
235  }
236#endif /* ENABLED (JERRY_ES2015_BUILTIN_PROXY) */
237
238#if ENABLED (JERRY_ES2015)
239  ecma_value_t blocked = ecma_op_is_prop_unscopable (binding_obj_p, name_p);
240
241  if (ecma_is_value_false (blocked))
242  {
243#if ENABLED (JERRY_ES2015_BUILTIN_PROXY)
244    if (ECMA_OBJECT_IS_PROXY (binding_obj_p))
245    {
246      return ecma_proxy_object_get (binding_obj_p, name_p, ecma_make_object_value (binding_obj_p));
247    }
248#endif /* ENABLED (JERRY_ES2015_BUILTIN_PROXY) */
249    return found_binding;
250  }
251
252#if ENABLED (JERRY_ES2015_BUILTIN_PROXY)
253  if (!ECMA_OBJECT_IS_PROXY (binding_obj_p))
254  {
255    ecma_free_value (found_binding);
256  }
257#endif /* ENABLED (JERRY_ES2015_BUILTIN_PROXY) */
258
259  return ECMA_IS_VALUE_ERROR (blocked) ? blocked : ECMA_VALUE_NOT_FOUND;
260#endif /* ENABLED (JERRY_ES2015) */
261} /* ecma_op_object_bound_environment_resolve_reference_value */
262
263/**
264 * Resolve value corresponding to reference.
265 *
266 * @return value of the reference
267 */
268ecma_value_t
269ecma_op_resolve_reference_value (ecma_object_t *lex_env_p, /**< starting lexical environment */
270                                 ecma_string_t *name_p) /**< identifier's name */
271{
272  JERRY_ASSERT (lex_env_p != NULL);
273
274  while (true)
275  {
276    ecma_lexical_environment_type_t lex_env_type = ecma_get_lex_env_type (lex_env_p);
277
278    if (lex_env_type == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE)
279    {
280      ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p);
281
282      if (property_p != NULL)
283      {
284        ecma_property_value_t *property_value_p = ECMA_PROPERTY_VALUE_PTR (property_p);
285
286#if ENABLED (JERRY_ES2015)
287        if (JERRY_UNLIKELY (property_value_p->value == ECMA_VALUE_UNINITIALIZED))
288        {
289          return ecma_raise_reference_error (ECMA_ERR_MSG ("Variables declared by let/const must be"
290                                                           " initialized before reading their value."));
291        }
292#endif /* ENABLED (JERRY_ES2015) */
293
294        return ecma_fast_copy_value (property_value_p->value);
295      }
296    }
297    else if (lex_env_type == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND)
298    {
299#if ENABLED (JERRY_ES2015)
300      bool lcache_lookup_allowed = (lex_env_p == ecma_get_global_environment ());
301#else /* !ENABLED (JERRY_ES2015)*/
302      bool lcache_lookup_allowed = true;
303#endif /* ENABLED (JERRY_ES2015) */
304
305      if (lcache_lookup_allowed)
306      {
307#if ENABLED (JERRY_LCACHE)
308        ecma_object_t *binding_obj_p = ecma_get_lex_env_binding_object (lex_env_p);
309        ecma_property_t *property_p = ecma_lcache_lookup (binding_obj_p, name_p);
310
311        if (property_p != NULL)
312        {
313          ecma_property_value_t *prop_value_p = ECMA_PROPERTY_VALUE_PTR (property_p);
314
315          if (ECMA_PROPERTY_GET_TYPE (*property_p) == ECMA_PROPERTY_TYPE_NAMEDDATA)
316          {
317            return ecma_fast_copy_value (prop_value_p->value);
318          }
319
320          JERRY_ASSERT (ECMA_PROPERTY_GET_TYPE (*property_p) == ECMA_PROPERTY_TYPE_NAMEDACCESSOR);
321
322          ecma_getter_setter_pointers_t *get_set_pair_p = ecma_get_named_accessor_property (prop_value_p);
323
324          if (get_set_pair_p->getter_cp == JMEM_CP_NULL)
325          {
326            return ECMA_VALUE_UNDEFINED;
327          }
328
329          ecma_object_t *getter_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, get_set_pair_p->getter_cp);
330
331          ecma_value_t base_value = ecma_make_object_value (binding_obj_p);
332          return ecma_op_function_call (getter_p, base_value, NULL, 0);
333        }
334#endif /* ENABLED (JERRY_LCACHE) */
335      }
336
337      ecma_value_t result = ecma_op_object_bound_environment_resolve_reference_value (lex_env_p, name_p);
338
339      if (ecma_is_value_found (result))
340      {
341        /* Note: the result may contains ECMA_VALUE_ERROR */
342        return result;
343      }
344    }
345    else
346    {
347#if ENABLED (JERRY_ES2015)
348      JERRY_ASSERT (lex_env_type == ECMA_LEXICAL_ENVIRONMENT_HOME_OBJECT_BOUND);
349#else /* !ENABLED (JERRY_ES2015) */
350      JERRY_UNREACHABLE ();
351#endif /* ENABLED (JERRY_ES2015) */
352    }
353
354    if (lex_env_p->u2.outer_reference_cp == JMEM_CP_NULL)
355    {
356      break;
357    }
358
359    lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp);
360  }
361
362#if ENABLED (JERRY_ERROR_MESSAGES)
363  ecma_value_t name_val = ecma_make_string_value (name_p);
364  ecma_value_t error_value = ecma_raise_standard_error_with_format (ECMA_ERROR_REFERENCE,
365                                                                    "% is not defined",
366                                                                    name_val);
367#else /* ENABLED (JERRY_ERROR_MESSAGES) */
368  ecma_value_t error_value = ecma_raise_reference_error (NULL);
369#endif /* !ENABLED (JERRY_ERROR_MESSAGES) */
370  return error_value;
371} /* ecma_op_resolve_reference_value */
372
373/**
374 * @}
375 * @}
376 */
377