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 */
42 ecma_object_t *
ecma_op_resolve_reference_base(ecma_object_t * lex_env_p,ecma_string_t * name_p)43 ecma_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 */
91 ecma_value_t
ecma_op_resolve_super_base(ecma_object_t * lex_env_p)92 ecma_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 */
143 static ecma_value_t
ecma_op_is_prop_unscopable(ecma_object_t * binding_obj_p,ecma_string_t * prop_name_p)144 ecma_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 */
201 ecma_value_t
ecma_op_object_bound_environment_resolve_reference_value(ecma_object_t * lex_env_p,ecma_string_t * name_p)202 ecma_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 */
268 ecma_value_t
ecma_op_resolve_reference_value(ecma_object_t * lex_env_p,ecma_string_t * name_p)269 ecma_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