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