LibJS: Remove callerRealm from HostEnsureCanCompileStrings

This is a normative change in the ecma262 spec.

See: https://github.com/tc39/ecma262/commit/2527be4
This commit is contained in:
Luke Wilde 2022-04-22 19:10:27 +01:00 committed by Linus Groh
parent a0a4d169f4
commit 77ba3d3e3f
Notes: sideshowbarker 2024-07-17 11:08:16 +09:00
7 changed files with 79 additions and 87 deletions

View file

@ -432,7 +432,7 @@ Completion CallExpression::execute(Interpreter& interpreter, GlobalObject& globa
&& callee_reference.name().as_string() == vm.names.eval.as_string()) {
auto script_value = arg_list.size() == 0 ? js_undefined() : arg_list[0];
return perform_eval(script_value, global_object, vm.in_strict_mode() ? CallerMode::Strict : CallerMode::NonStrict, EvalMode::Direct);
return perform_eval(global_object, script_value, vm.in_strict_mode() ? CallerMode::Strict : CallerMode::NonStrict, EvalMode::Direct);
}
return call(global_object, function, this_value, move(arg_list));

View file

@ -490,8 +490,8 @@ ThrowCompletionOr<Reference> make_super_property_reference(GlobalObject& global_
return Reference { bv, property_key, actual_this, strict };
}
// 19.2.1.1 PerformEval ( x, callerRealm, strictCaller, direct ), https://tc39.es/ecma262/#sec-performeval
ThrowCompletionOr<Value> perform_eval(Value x, GlobalObject& caller_realm, CallerMode strict_caller, EvalMode direct)
// 19.2.1.1 PerformEval ( x, strictCaller, direct ), https://tc39.es/ecma262/#sec-performeval
ThrowCompletionOr<Value> perform_eval(GlobalObject& global_object, Value x, CallerMode strict_caller, EvalMode direct)
{
// 1. Assert: If direct is false, then strictCaller is also false.
VERIFY(direct == EvalMode::Direct || strict_caller == CallerMode::NonStrict);
@ -500,27 +500,28 @@ ThrowCompletionOr<Value> perform_eval(Value x, GlobalObject& caller_realm, Calle
if (!x.is_string())
return x;
auto& vm = caller_realm.vm();
auto& vm = global_object.vm();
// 3. Let evalRealm be the current Realm Record.
auto& eval_realm = *vm.running_execution_context().realm;
// 4. Perform ? HostEnsureCanCompileStrings(callerRealm, evalRealm).
TRY(vm.host_ensure_can_compile_strings(*caller_realm.associated_realm(), eval_realm));
// 4. NOTE: In the case of a direct eval, evalRealm is the realm of both the caller of eval and of the eval function itself.
// 5. Perform ? HostEnsureCanCompileStrings(evalRealm).
TRY(vm.host_ensure_can_compile_strings(eval_realm));
// 5. Let inFunction be false.
// 6. Let inFunction be false.
bool in_function = false;
// 6. Let inMethod be false.
// 7. Let inMethod be false.
bool in_method = false;
// 7. Let inDerivedConstructor be false.
// 8. Let inDerivedConstructor be false.
bool in_derived_constructor = false;
// 8. Let inClassFieldInitializer be false.
// 9. Let inClassFieldInitializer be false.
bool in_class_field_initializer = false;
// 9. If direct is true, then
// 10. If direct is true, then
if (direct == EvalMode::Direct) {
// a. Let thisEnvRec be GetThisEnvironment().
auto& this_environment_record = get_this_environment(vm);
@ -551,7 +552,7 @@ ThrowCompletionOr<Value> perform_eval(Value x, GlobalObject& caller_realm, Calle
}
}
// 10. Perform the following substeps in an implementation-defined order, possibly interleaving parsing and error detection:
// 11. Perform the following substeps in an implementation-defined order, possibly interleaving parsing and error detection:
// a. Let script be ParseText(StringToCodePoints(x), Script).
// c. If script Contains ScriptBody is false, return undefined.
// d. Let body be the ScriptBody of script.
@ -575,22 +576,22 @@ ThrowCompletionOr<Value> perform_eval(Value x, GlobalObject& caller_realm, Calle
// b. If script is a List of errors, throw a SyntaxError exception.
if (parser.has_errors()) {
auto& error = parser.errors()[0];
return vm.throw_completion<SyntaxError>(caller_realm, error.to_string());
return vm.throw_completion<SyntaxError>(global_object, error.to_string());
}
auto strict_eval = strict_caller == CallerMode::Strict;
if (program->is_strict_mode())
strict_eval = true;
// 13. Let runningContext be the running execution context.
// 14. NOTE: If direct is true, runningContext will be the execution context that performed the direct eval. If direct is false, runningContext will be the execution context for the invocation of the eval function.
// 14. Let runningContext be the running execution context.
// 15. NOTE: If direct is true, runningContext will be the execution context that performed the direct eval. If direct is false, runningContext will be the execution context for the invocation of the eval function.
auto& running_context = vm.running_execution_context();
Environment* lexical_environment;
Environment* variable_environment;
PrivateEnvironment* private_environment;
// 15. If direct is true, then
// 16. If direct is true, then
if (direct == EvalMode::Direct) {
// a. Let lexEnv be NewDeclarativeEnvironment(runningContext's LexicalEnvironment).
lexical_environment = new_declarative_environment(*running_context.lexical_environment);
@ -601,7 +602,7 @@ ThrowCompletionOr<Value> perform_eval(Value x, GlobalObject& caller_realm, Calle
// c. Let privateEnv be runningContext's PrivateEnvironment.
private_environment = running_context.private_environment;
}
// 16. Else,
// 17. Else,
else {
// a. Let lexEnv be NewDeclarativeEnvironment(evalRealm.[[GlobalEnv]]).
lexical_environment = new_declarative_environment(eval_realm.global_environment());
@ -613,7 +614,7 @@ ThrowCompletionOr<Value> perform_eval(Value x, GlobalObject& caller_realm, Calle
private_environment = nullptr;
}
// 17. If strictEval is true, set varEnv to lexEnv.
// 18. If strictEval is true, set varEnv to lexEnv.
if (strict_eval)
variable_environment = lexical_environment;
@ -624,50 +625,50 @@ ThrowCompletionOr<Value> perform_eval(Value x, GlobalObject& caller_realm, Calle
variable_environment->set_permanently_screwed_by_eval();
}
// 18. If runningContext is not already suspended, suspend runningContext.
// 19. If runningContext is not already suspended, suspend runningContext.
// FIXME: We don't have this concept yet.
// 19. Let evalContext be a new ECMAScript code execution context.
// 20. Let evalContext be a new ECMAScript code execution context.
ExecutionContext eval_context(vm.heap());
// 20. Set evalContext's Function to null.
// 21. Set evalContext's Function to null.
// NOTE: This was done in the construction of eval_context.
// 21. Set evalContext's Realm to evalRealm.
// 22. Set evalContext's Realm to evalRealm.
eval_context.realm = &eval_realm;
// 22. Set evalContext's ScriptOrModule to runningContext's ScriptOrModule.
// 23. Set evalContext's ScriptOrModule to runningContext's ScriptOrModule.
eval_context.script_or_module = running_context.script_or_module;
// 23. Set evalContext's VariableEnvironment to varEnv.
// 24. Set evalContext's VariableEnvironment to varEnv.
eval_context.variable_environment = variable_environment;
// 24. Set evalContext's LexicalEnvironment to lexEnv.
// 25. Set evalContext's LexicalEnvironment to lexEnv.
eval_context.lexical_environment = lexical_environment;
// 25. Set evalContext's PrivateEnvironment to privateEnv.
// 26. Set evalContext's PrivateEnvironment to privateEnv.
eval_context.private_environment = private_environment;
// NOTE: This isn't in the spec, but we require it.
eval_context.is_strict_mode = strict_eval;
// 26. Push evalContext onto the execution context stack; evalContext is now the running execution context.
// 27. Push evalContext onto the execution context stack; evalContext is now the running execution context.
TRY(vm.push_execution_context(eval_context, eval_realm.global_object()));
// NOTE: We use a ScopeGuard to automatically pop the execution context when any of the `TRY`s below return a throw completion.
ScopeGuard pop_guard = [&] {
// FIXME: 30. Suspend evalContext and remove it from the execution context stack.
// FIXME: 31. Suspend evalContext and remove it from the execution context stack.
// 31. Resume the context that is now on the top of the execution context stack as the running execution context.
// 32. Resume the context that is now on the top of the execution context stack as the running execution context.
vm.pop_execution_context();
};
// 27. Let result be Completion(EvalDeclarationInstantiation(body, varEnv, lexEnv, privateEnv, strictEval)).
// 28. Let result be Completion(EvalDeclarationInstantiation(body, varEnv, lexEnv, privateEnv, strictEval)).
TRY(eval_declaration_instantiation(vm, eval_realm.global_object(), program, variable_environment, lexical_environment, private_environment, strict_eval));
Optional<Value> eval_result;
// 28. If result.[[Type]] is normal, then
// 29. If result.[[Type]] is normal, then
// a. Set result to the result of evaluating body.
if (auto* bytecode_interpreter = Bytecode::Interpreter::current()) {
auto executable_result = Bytecode::Generator::generate(program);
@ -684,14 +685,14 @@ ThrowCompletionOr<Value> perform_eval(Value x, GlobalObject& caller_realm, Calle
eval_result = {};
} else {
auto& ast_interpreter = vm.interpreter();
eval_result = TRY(program->execute(ast_interpreter, caller_realm));
eval_result = TRY(program->execute(ast_interpreter, global_object));
}
// 29. If result.[[Type]] is normal and result.[[Value]] is empty, then
// 30. If result.[[Type]] is normal and result.[[Value]] is empty, then
// a. Set result to NormalCompletion(undefined).
// NOTE: Step 30 and 31 is handled by `pop_guard` above.
// 32. Return ? result.
// NOTE: Step 32 is also performed with each use of `TRY` above.
// NOTE: Step 31 and 32 is handled by `pop_guard` above.
// 33. Return ? result.
// NOTE: Step 33 is also performed with each use of `TRY` above.
return eval_result.value_or(js_undefined());
}

View file

@ -56,7 +56,7 @@ enum class EvalMode {
Direct,
Indirect
};
ThrowCompletionOr<Value> perform_eval(Value, GlobalObject&, CallerMode, EvalMode);
ThrowCompletionOr<Value> perform_eval(GlobalObject&, Value, CallerMode, EvalMode);
ThrowCompletionOr<void> eval_declaration_instantiation(VM& vm, GlobalObject& global_object, Program const& program, Environment* variable_environment, Environment* lexical_environment, PrivateEnvironment* private_environment, bool strict);

View file

@ -40,22 +40,13 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> FunctionConstructor::create_dynamic
{
auto& vm = global_object.vm();
// 1. Assert: The execution context stack has at least two elements.
VERIFY(vm.execution_context_stack().size() >= 2);
// 1. Let currentRealm be the current Realm Record.
auto& current_realm = *vm.running_execution_context().realm;
// 2. Let callerContext be the second to top element of the execution context stack.
auto& caller_context = *vm.execution_context_stack().at(vm.execution_context_stack().size() - 2);
// 2. Perform ? HostEnsureCanCompileStrings(currentRealm).
TRY(vm.host_ensure_can_compile_strings(current_realm));
// 3. Let callerRealm be callerContext's Realm.
auto& caller_realm = *caller_context.realm;
// 4. Let calleeRealm be the current Realm Record.
auto& callee_realm = *vm.running_execution_context().realm;
// 5. Perform ? HostEnsureCanCompileStrings(callerRealm, calleeRealm).
TRY(vm.host_ensure_can_compile_strings(caller_realm, callee_realm));
// 6. If newTarget is undefined, set newTarget to constructor.
// 3. If newTarget is undefined, set newTarget to constructor.
if (new_target == nullptr)
new_target = &constructor;
@ -63,7 +54,7 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> FunctionConstructor::create_dynamic
Object* (GlobalObject::*fallback_prototype)() = nullptr;
switch (kind) {
// 7. If kind is normal, then
// 4. If kind is normal, then
case FunctionKind::Normal:
// a. Let prefix be "function".
prefix = "function"sv;
@ -76,7 +67,7 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> FunctionConstructor::create_dynamic
fallback_prototype = &GlobalObject::function_prototype;
break;
// 8. Else if kind is generator, then
// 5. Else if kind is generator, then
case FunctionKind::Generator:
// a. Let prefix be "function*".
prefix = "function*"sv;
@ -89,7 +80,7 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> FunctionConstructor::create_dynamic
fallback_prototype = &GlobalObject::generator_function_prototype;
break;
// 9. Else if kind is async, then
// 6. Else if kind is async, then
case FunctionKind::Async:
// a. Let prefix be "async function".
prefix = "async function"sv;
@ -102,7 +93,7 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> FunctionConstructor::create_dynamic
fallback_prototype = &GlobalObject::async_function_prototype;
break;
// 10. Else,
// 7. Else,
case FunctionKind::AsyncGenerator:
// a. Assert: kind is asyncGenerator.
@ -121,23 +112,23 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> FunctionConstructor::create_dynamic
VERIFY_NOT_REACHED();
}
// 11. Let argCount be the number of elements in args.
// 8. Let argCount be the number of elements in args.
auto arg_count = args.size();
// 12. Let P be the empty String.
// 9. Let P be the empty String.
String parameters_string = "";
Optional<Value> body_arg;
// 13. If argCount = 0, let bodyArg be the empty String.
// 10. If argCount = 0, let bodyArg be the empty String.
if (arg_count == 0) {
// Optimization: Instead of creating a js_string() here, we just check if body_arg is empty in step 16.
}
// 14. Else if argCount = 1, let bodyArg be args[0].
// 11. Else if argCount = 1, let bodyArg be args[0].
else if (arg_count == 1) {
body_arg = args[0];
}
// 15. Else,
// 12. Else,
else {
// a. Assert: argCount > 1.
VERIFY(arg_count > 1);
@ -167,11 +158,11 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> FunctionConstructor::create_dynamic
body_arg = args[k];
}
// 16. Let bodyString be the string-concatenation of 0x000A (LINE FEED), ? ToString(bodyArg), and 0x000A (LINE FEED).
// 13. Let bodyString be the string-concatenation of 0x000A (LINE FEED), ? ToString(bodyArg), and 0x000A (LINE FEED).
auto body_string = String::formatted("\n{}\n", body_arg.has_value() ? TRY(body_arg->to_string(global_object)) : "");
// 17. Let sourceString be the string-concatenation of prefix, " anonymous(", P, 0x000A (LINE FEED), ") {", bodyString, and "}".
// 18. Let sourceText be StringToCodePoints(sourceString).
// 14. Let sourceString be the string-concatenation of prefix, " anonymous(", P, 0x000A (LINE FEED), ") {", bodyString, and "}".
// 15. Let sourceText be StringToCodePoints(sourceString).
auto source_text = String::formatted("{} anonymous({}\n) {{{}}}", prefix, parameters_string, body_string);
u8 parse_options = FunctionNodeParseOptions::CheckForFunctionAndName;
@ -180,18 +171,18 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> FunctionConstructor::create_dynamic
if (kind == FunctionKind::Generator || kind == FunctionKind::AsyncGenerator)
parse_options |= FunctionNodeParseOptions::IsGeneratorFunction;
// 19. Let parameters be ParseText(StringToCodePoints(P), parameterSym).
// 16. Let parameters be ParseText(StringToCodePoints(P), parameterSym).
i32 function_length = 0;
auto parameters_parser = Parser { Lexer { parameters_string } };
auto parameters = parameters_parser.parse_formal_parameters(function_length, parse_options);
// 20. If parameters is a List of errors, throw a SyntaxError exception.
// 17. If parameters is a List of errors, throw a SyntaxError exception.
if (parameters_parser.has_errors()) {
auto error = parameters_parser.errors()[0];
return vm.throw_completion<SyntaxError>(global_object, error.to_string());
}
// 21. Let body be ParseText(StringToCodePoints(bodyString), bodySym).
// 18. Let body be ParseText(StringToCodePoints(bodyString), bodySym).
bool contains_direct_call_to_eval = false;
auto body_parser = Parser { Lexer { body_string } };
// Set up some parser state to accept things like return await, and yield in the plain function body.
@ -202,45 +193,45 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> FunctionConstructor::create_dynamic
body_parser.m_state.in_generator_function_context = true;
(void)body_parser.parse_function_body(parameters, kind, contains_direct_call_to_eval);
// 22. If body is a List of errors, throw a SyntaxError exception.
// 19. If body is a List of errors, throw a SyntaxError exception.
if (body_parser.has_errors()) {
auto error = body_parser.errors()[0];
return vm.throw_completion<SyntaxError>(global_object, error.to_string());
}
// 23. NOTE: The parameters and body are parsed separately to ensure that each is valid alone. For example, new Function("/*", "*/ ) {") is not legal.
// 24. NOTE: If this step is reached, sourceText must have the syntax of exprSym (although the reverse implication does not hold). The purpose of the next two steps is to enforce any Early Error rules which apply to exprSym directly.
// 20. NOTE: The parameters and body are parsed separately to ensure that each is valid alone. For example, new Function("/*", "*/ ) {") is not legal.
// 21. NOTE: If this step is reached, sourceText must have the syntax of exprSym (although the reverse implication does not hold). The purpose of the next two steps is to enforce any Early Error rules which apply to exprSym directly.
// 25. Let expr be ParseText(sourceText, exprSym).
// 22. Let expr be ParseText(sourceText, exprSym).
auto source_parser = Parser { Lexer { source_text } };
// This doesn't need any parse_options, it determines those & the function type based on the tokens that were found.
auto expr = source_parser.parse_function_node<FunctionExpression>();
// 26. If expr is a List of errors, throw a SyntaxError exception.
// 23. If expr is a List of errors, throw a SyntaxError exception.
if (source_parser.has_errors()) {
auto error = source_parser.errors()[0];
return vm.throw_completion<SyntaxError>(global_object, error.to_string());
}
// 27. Let proto be ? GetPrototypeFromConstructor(newTarget, fallbackProto).
// 24. Let proto be ? GetPrototypeFromConstructor(newTarget, fallbackProto).
auto* prototype = TRY(get_prototype_from_constructor(global_object, *new_target, fallback_prototype));
// 28. Let realmF be the current Realm Record.
// 25. Let realmF be the current Realm Record.
auto* realm = vm.current_realm();
// 29. Let env be realmF.[[GlobalEnv]].
// 26. Let env be realmF.[[GlobalEnv]].
auto* environment = &realm->global_environment();
// 30. Let privateEnv be null.
// 27. Let privateEnv be null.
PrivateEnvironment* private_environment = nullptr;
// 31. Let F be OrdinaryFunctionCreate(proto, sourceText, parameters, body, non-lexical-this, env, privateEnv).
// 28. Let F be OrdinaryFunctionCreate(proto, sourceText, parameters, body, non-lexical-this, env, privateEnv).
auto* function = ECMAScriptFunctionObject::create(global_object, "anonymous", *prototype, move(source_text), expr->body(), expr->parameters(), expr->function_length(), environment, private_environment, expr->kind(), expr->is_strict_mode(), expr->might_need_arguments_object(), contains_direct_call_to_eval);
// FIXME: Remove the name argument from create() and do this instead.
// 32. Perform SetFunctionName(F, "anonymous").
// 29. Perform SetFunctionName(F, "anonymous").
// 33. If kind is generator, then
// 30. If kind is generator, then
if (kind == FunctionKind::Generator) {
// a. Let prototype be OrdinaryObjectCreate(%GeneratorFunction.prototype.prototype%).
prototype = Object::create(global_object, global_object.generator_function_prototype_prototype());
@ -248,7 +239,7 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> FunctionConstructor::create_dynamic
// b. Perform ! DefinePropertyOrThrow(F, "prototype", PropertyDescriptor { [[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }).
function->define_direct_property(vm.names.prototype, prototype, Attribute::Writable);
}
// 34. Else if kind is asyncGenerator, then
// 31. Else if kind is asyncGenerator, then
else if (kind == FunctionKind::AsyncGenerator) {
// a. Let prototype be OrdinaryObjectCreate(%AsyncGeneratorFunction.prototype.prototype%).
prototype = Object::create(global_object, global_object.async_generator_function_prototype_prototype());
@ -256,7 +247,7 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> FunctionConstructor::create_dynamic
// b. Perform ! DefinePropertyOrThrow(F, "prototype", PropertyDescriptor { [[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }).
function->define_direct_property(vm.names.prototype, prototype, Attribute::Writable);
}
// 35. Else if kind is normal, perform MakeConstructor(F).
// 32. Else if kind is normal, perform MakeConstructor(F).
else if (kind == FunctionKind::Normal) {
// FIXME: Implement MakeConstructor
prototype = Object::create(global_object, global_object.object_prototype());
@ -264,9 +255,9 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> FunctionConstructor::create_dynamic
function->define_direct_property(vm.names.prototype, prototype, Attribute::Writable);
}
// 36. NOTE: Functions whose kind is async are not constructible and do not have a [[Construct]] internal method or a "prototype" property.
// 33. NOTE: Functions whose kind is async are not constructible and do not have a [[Construct]] internal method or a "prototype" property.
// 37. Return F.
// 34. Return F.
return function;
}

View file

@ -489,7 +489,7 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::parse_int)
// 19.2.1 eval ( x ), https://tc39.es/ecma262/#sec-eval-x
JS_DEFINE_NATIVE_FUNCTION(GlobalObject::eval)
{
return perform_eval(vm.argument(0), global_object, CallerMode::NonStrict, EvalMode::Indirect);
return perform_eval(global_object, vm.argument(0), CallerMode::NonStrict, EvalMode::Indirect);
}
// 19.2.6.1.1 Encode ( string, unescapedSet ), https://tc39.es/ecma262/#sec-encode

View file

@ -132,9 +132,9 @@ VM::VM(OwnPtr<CustomData> custom_data)
};
// 19.2.1.2 HostEnsureCanCompileStrings ( callerRealm, calleeRealm ), https://tc39.es/ecma262/#sec-hostensurecancompilestrings
host_ensure_can_compile_strings = [](Realm&, Realm&) -> ThrowCompletionOr<void> {
// The host-defined abstract operation HostEnsureCanCompileStrings takes arguments callerRealm (a Realm Record) and calleeRealm (a Realm Record)
// and returns either a normal completion containing unused or an abrupt completion.
host_ensure_can_compile_strings = [](Realm&) -> ThrowCompletionOr<void> {
// The host-defined abstract operation HostEnsureCanCompileStrings takes argument calleeRealm (a Realm Record)
// and returns either a normal completion containing unused or a throw completion.
// It allows host environments to block certain ECMAScript functions which allow developers to compile strings into ECMAScript code.
// An implementation of HostEnsureCanCompileStrings must conform to the following requirements:
// - If the returned Completion Record is a normal completion, it must be a normal completion containing unused.

View file

@ -234,7 +234,7 @@ public:
Function<void(Function<ThrowCompletionOr<Value>()>, Realm*)> host_enqueue_promise_job;
Function<JobCallback(FunctionObject&)> host_make_job_callback;
Function<ThrowCompletionOr<HostResizeArrayBufferResult>(GlobalObject&, size_t)> host_resize_array_buffer;
Function<ThrowCompletionOr<void>(Realm&, Realm&)> host_ensure_can_compile_strings;
Function<ThrowCompletionOr<void>(Realm&)> host_ensure_can_compile_strings;
private:
explicit VM(OwnPtr<CustomData>);