/* * Copyright (c) 2020-2021, Linus Groh * Copyright (c) 2021, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace JS { // Used in various abstract operations to make it obvious when a non-optional return value must be discarded. static constexpr double INVALID { 0 }; // 7.2.1 RequireObjectCoercible ( argument ), https://tc39.es/ecma262/#sec-requireobjectcoercible Value require_object_coercible(GlobalObject& global_object, Value value) { auto& vm = global_object.vm(); if (value.is_nullish()) { vm.throw_exception(global_object, ErrorType::NotObjectCoercible, value.to_string_without_side_effects()); return {}; } return value; } // 7.3.18 LengthOfArrayLike ( obj ), https://tc39.es/ecma262/#sec-lengthofarraylike size_t length_of_array_like(GlobalObject& global_object, Object const& object) { auto& vm = global_object.vm(); auto result = object.get(vm.names.length).value_or(js_undefined()); if (vm.exception()) return INVALID; return result.to_length(global_object); } // 7.3.19 CreateListFromArrayLike ( obj [ , elementTypes ] ), https://tc39.es/ecma262/#sec-createlistfromarraylike MarkedValueList create_list_from_array_like(GlobalObject& global_object, Value value, Function(Value)> check_value) { auto& vm = global_object.vm(); auto& heap = global_object.heap(); if (!value.is_object()) { vm.throw_exception(global_object, ErrorType::NotAnObject, value.to_string_without_side_effects()); return MarkedValueList { heap }; } auto& array_like = value.as_object(); auto length = length_of_array_like(global_object, array_like); if (vm.exception()) return MarkedValueList { heap }; auto list = MarkedValueList { heap }; for (size_t i = 0; i < length; ++i) { auto index_name = String::number(i); auto next = array_like.get(index_name).value_or(js_undefined()); if (vm.exception()) return MarkedValueList { heap }; if (check_value) { auto result = check_value(next); if (result.is_error()) { vm.throw_exception(global_object, result.release_error()); return MarkedValueList { heap }; } } list.append(next); } return list; } // 7.3.22 SpeciesConstructor ( O, defaultConstructor ), https://tc39.es/ecma262/#sec-speciesconstructor FunctionObject* species_constructor(GlobalObject& global_object, Object const& object, FunctionObject& default_constructor) { auto& vm = global_object.vm(); auto constructor = object.get(vm.names.constructor).value_or(js_undefined()); if (vm.exception()) return nullptr; if (constructor.is_undefined()) return &default_constructor; if (!constructor.is_object()) { vm.throw_exception(global_object, ErrorType::NotAConstructor, constructor.to_string_without_side_effects()); return nullptr; } auto species = constructor.as_object().get(*vm.well_known_symbol_species()).value_or(js_undefined()); if (species.is_nullish()) return &default_constructor; if (species.is_constructor()) return &species.as_function(); vm.throw_exception(global_object, ErrorType::NotAConstructor, species.to_string_without_side_effects()); return nullptr; } // 7.3.24 GetFunctionRealm ( obj ), https://tc39.es/ecma262/#sec-getfunctionrealm GlobalObject* get_function_realm(GlobalObject& global_object, FunctionObject const& function) { auto& vm = global_object.vm(); if (function.realm()) return function.realm(); if (is(function)) { auto& bound_function = static_cast(function); auto& target = bound_function.target_function(); return get_function_realm(global_object, target); } if (is(function)) { auto& proxy = static_cast(function); if (proxy.is_revoked()) { vm.throw_exception(global_object, ErrorType::ProxyRevoked); return nullptr; } auto& proxy_target = proxy.target(); VERIFY(proxy_target.is_function()); return get_function_realm(global_object, static_cast(proxy_target)); } // 5. Return the current Realm Record. return &global_object; } // 10.1.14 GetPrototypeFromConstructor ( constructor, intrinsicDefaultProto ) Object* get_prototype_from_constructor(GlobalObject& global_object, FunctionObject const& constructor, Object* (GlobalObject::*intrinsic_default_prototype)()) { auto& vm = global_object.vm(); auto prototype = constructor.get(vm.names.prototype); if (vm.exception()) return nullptr; if (!prototype.is_object()) { auto* realm = get_function_realm(global_object, constructor); if (vm.exception()) return nullptr; prototype = (realm->*intrinsic_default_prototype)(); } return &prototype.as_object(); } // 9.1.2.2 NewDeclarativeEnvironment ( E ), https://tc39.es/ecma262/#sec-newdeclarativeenvironment DeclarativeEnvironment* new_declarative_environment(Environment& environment) { auto& global_object = environment.global_object(); return global_object.heap().allocate(global_object, &environment); } // 9.1.2.3 NewObjectEnvironment ( O, W, E ), https://tc39.es/ecma262/#sec-newobjectenvironment ObjectEnvironment* new_object_environment(Object& object, bool is_with_environment, Environment* environment) { auto& global_object = object.global_object(); return global_object.heap().allocate(global_object, object, is_with_environment ? ObjectEnvironment::IsWithEnvironment::Yes : ObjectEnvironment::IsWithEnvironment::No, environment); } // 9.4.3 GetThisEnvironment ( ), https://tc39.es/ecma262/#sec-getthisenvironment Environment& get_this_environment(VM& vm) { for (auto* env = vm.lexical_environment(); env; env = env->outer_environment()) { if (env->has_this_binding()) return *env; } VERIFY_NOT_REACHED(); } // 13.3.7.2 GetSuperConstructor ( ), https://tc39.es/ecma262/#sec-getsuperconstructor Object* get_super_constructor(VM& vm) { auto& env = get_this_environment(vm); auto& active_function = verify_cast(env).function_object(); auto* super_constructor = active_function.prototype(); return super_constructor; } // 13.3.7.3 MakeSuperPropertyReference ( actualThis, propertyKey, strict ) Reference make_super_property_reference(GlobalObject& global_object, Value actual_this, StringOrSymbol const& property_key, bool strict) { auto& vm = global_object.vm(); // 1. Let env be GetThisEnvironment(). auto& env = verify_cast(get_this_environment(vm)); // 2. Assert: env.HasSuperBinding() is true. VERIFY(env.has_super_binding()); // 3. Let baseValue be ? env.GetSuperBase(). auto base_value = env.get_super_base(); // 4. Let bv be ? RequireObjectCoercible(baseValue). auto bv = require_object_coercible(global_object, base_value); if (vm.exception()) return {}; // 5. Return the Reference Record { [[Base]]: bv, [[ReferencedName]]: propertyKey, [[Strict]]: strict, [[ThisValue]]: actualThis }. // 6. NOTE: This returns a Super Reference Record. return Reference { bv, property_key, actual_this, strict }; } // 19.2.1.1 PerformEval ( x, callerRealm, strictCaller, direct ), https://tc39.es/ecma262/#sec-performeval Value perform_eval(Value x, GlobalObject& caller_realm, CallerMode strict_caller, EvalMode direct) { VERIFY(direct == EvalMode::Direct || strict_caller == CallerMode::NonStrict); if (!x.is_string()) return x; auto& vm = caller_realm.vm(); auto& code_string = x.as_string(); Parser parser { Lexer { code_string.string() } }; auto program = parser.parse_program(strict_caller == CallerMode::Strict); if (parser.has_errors()) { auto& error = parser.errors()[0]; vm.throw_exception(caller_realm, error.to_string()); return {}; } auto& interpreter = vm.interpreter(); if (direct == EvalMode::Direct) return interpreter.execute_statement(caller_realm, program).value_or(js_undefined()); TemporaryChange scope_change(vm.running_execution_context().lexical_environment, static_cast(&caller_realm.environment())); return interpreter.execute_statement(caller_realm, program).value_or(js_undefined()); } // 10.4.4.6 CreateUnmappedArgumentsObject ( argumentsList ), https://tc39.es/ecma262/#sec-createunmappedargumentsobject Object* create_unmapped_arguments_object(GlobalObject& global_object, Vector const& arguments) { auto& vm = global_object.vm(); auto* object = Object::create(global_object, global_object.object_prototype()); if (vm.exception()) return nullptr; for (auto& argument : arguments) object->indexed_properties().append(argument); // 4. Perform DefinePropertyOrThrow(obj, "length", PropertyDescriptor { [[Value]]: 𝔽(len), [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }). auto length = arguments.size(); object->define_property(vm.names.length, Value(length), Attribute::Writable | Attribute::Configurable); if (vm.exception()) return nullptr; object->define_property(*vm.well_known_symbol_iterator(), global_object.array_prototype()->get(vm.names.values), Attribute::Writable | Attribute::Configurable); // 8. Perform ! DefinePropertyOrThrow(obj, "callee", PropertyDescriptor { [[Get]]: %ThrowTypeError%, [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, [[Configurable]]: false }). object->define_accessor(vm.names.callee, global_object.throw_type_error_function(), global_object.throw_type_error_function(), 0); if (vm.exception()) return nullptr; return object; } // 10.4.4.7 CreateMappedArgumentsObject ( func, formals, argumentsList, env ), https://tc39.es/ecma262/#sec-createmappedargumentsobject Object* create_mapped_arguments_object(GlobalObject& global_object, FunctionObject& function, Vector const& formals, Vector const& arguments, Environment&) { // FIXME: This implementation is incomplete and doesn't support the actual identifier mappings yet. (void)formals; auto& vm = global_object.vm(); auto* object = vm.heap().allocate(global_object, global_object); if (vm.exception()) return nullptr; // 14. Let index be 0. // 15. Repeat, while index < len, // a. Let val be argumentsList[index]. // b . Perform ! CreateDataPropertyOrThrow(obj, ! ToString(𝔽(index)), val). // c. Set index to index + 1. for (auto& argument : arguments) object->indexed_properties().append(argument); // 16. Perform ! DefinePropertyOrThrow(obj, "length", PropertyDescriptor { [[Value]]: 𝔽(len), [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }). auto length = arguments.size(); object->define_property(vm.names.length, Value(length), Attribute::Writable | Attribute::Configurable); if (vm.exception()) return nullptr; // 20. Perform ! DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor { [[Value]]: %Array.prototype.values%, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }). object->define_property(*vm.well_known_symbol_iterator(), global_object.array_prototype()->get(vm.names.values), Attribute::Writable | Attribute::Configurable); // 21. Perform ! DefinePropertyOrThrow(obj, "callee", PropertyDescriptor { [[Value]]: func, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }). object->define_property(vm.names.callee, Value(&function), Attribute::Writable | Attribute::Configurable); if (vm.exception()) return nullptr; return object; } }