1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 13:47:35 +00:00

LibJS: Correct behaviour of direct vs. indirect eval

eval only has direct access to the local scope when accessed through
the name eval. This includes locals named eval, because of course it
does.
This commit is contained in:
Anonymous 2021-06-19 20:13:53 -07:00 committed by Linus Groh
parent 5d24b5f4be
commit 2822da8c8f
10 changed files with 121 additions and 20 deletions

View file

@ -7,12 +7,16 @@
#include <AK/Function.h>
#include <AK/Result.h>
#include <AK/TemporaryChange.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Parser.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/BoundFunction.h>
#include <LibJS/Runtime/DeclarativeEnvironmentRecord.h>
#include <LibJS/Runtime/ErrorTypes.h>
#include <LibJS/Runtime/Function.h>
#include <LibJS/Runtime/FunctionEnvironmentRecord.h>
#include <LibJS/Runtime/GlobalEnvironmentRecord.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/ObjectEnvironmentRecord.h>
@ -198,4 +202,30 @@ Object* get_super_constructor(VM& vm)
return super_constructor;
}
// 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<SyntaxError>(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.call_frame().lexical_environment, static_cast<EnvironmentRecord*>(&caller_realm.environment_record()));
return interpreter.execute_statement(caller_realm, program).value_or(js_undefined());
}
}

View file

@ -25,6 +25,16 @@ Function* species_constructor(GlobalObject&, Object const&, Function& default_co
GlobalObject* get_function_realm(GlobalObject&, Function const&);
Object* get_prototype_from_constructor(GlobalObject&, Function const& constructor, Object* (GlobalObject::*intrinsic_default_prototype)());
enum class CallerMode {
Strict,
NonStrict
};
enum class EvalMode {
Direct,
Indirect
};
Value perform_eval(Value, GlobalObject&, CallerMode, EvalMode);
// 10.1.13 OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto [ , internalSlotsList ] ), https://tc39.es/ecma262/#sec-ordinarycreatefromconstructor
template<typename T, typename... Args>
T* ordinary_create_from_constructor(GlobalObject& global_object, Function const& constructor, Object* (GlobalObject::*intrinsic_default_prototype)(), Args&&... args)

View file

@ -8,13 +8,13 @@
#include <AK/CharacterTypes.h>
#include <AK/Hex.h>
#include <AK/Platform.h>
#include <AK/TemporaryChange.h>
#include <AK/Utf8View.h>
#include <LibJS/Console.h>
#include <LibJS/Heap/DeferGC.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Lexer.h>
#include <LibJS/Parser.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/AggregateErrorConstructor.h>
#include <LibJS/Runtime/AggregateErrorPrototype.h>
#include <LibJS/Runtime/ArrayBufferConstructor.h>
@ -140,6 +140,8 @@ void GlobalObject::initialize_global_object()
define_native_function(vm.names.parseFloat, parse_float, 1, attr);
define_native_function(vm.names.parseInt, parse_int, 2, attr);
define_native_function(vm.names.eval, eval, 1, attr);
m_eval_function = &get_without_side_effects(vm.names.eval).as_function();
define_native_function(vm.names.encodeURI, encode_uri, 1, attr);
define_native_function(vm.names.decodeURI, decode_uri, 1, attr);
define_native_function(vm.names.encodeURIComponent, encode_uri_component, 1, attr);
@ -223,6 +225,8 @@ void GlobalObject::visit_edges(Visitor& visitor)
visitor.visit(m_##snake_name##_prototype);
JS_ENUMERATE_ITERATOR_PROTOTYPES
#undef __JS_ENUMERATE
visitor.visit(m_eval_function);
}
JS_DEFINE_NATIVE_FUNCTION(GlobalObject::gc)
@ -335,23 +339,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)
{
if (!vm.argument(0).is_string())
return vm.argument(0);
auto& code_string = vm.argument(0).as_string();
JS::Parser parser { JS::Lexer { code_string.string() } };
auto program = parser.parse_program();
if (parser.has_errors()) {
auto& error = parser.errors()[0];
vm.throw_exception<SyntaxError>(global_object, error.to_string());
return {};
}
auto& caller_frame = vm.call_stack().at(vm.call_stack().size() - 2);
TemporaryChange scope_change(vm.call_frame().lexical_environment, caller_frame->lexical_environment);
auto& interpreter = vm.interpreter();
return interpreter.execute_statement(global_object, program).value_or(js_undefined());
return perform_eval(vm.argument(0), global_object, CallerMode::NonStrict, EvalMode::Indirect);
}
// 19.2.6.1.1 Encode ( string, unescapedSet ), https://tc39.es/ecma262/#sec-encode

View file

@ -36,6 +36,8 @@ public:
// Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct constructor
GeneratorObjectPrototype* generator_object_prototype() { return m_generator_object_prototype; }
Function* eval_function() const { return m_eval_function; }
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
ConstructorName* snake_name##_constructor() { return m_##snake_name##_constructor; } \
Object* snake_name##_prototype() { return m_##snake_name##_prototype; }
@ -95,6 +97,8 @@ private:
Object* m_##snake_name##_prototype { nullptr };
JS_ENUMERATE_ITERATOR_PROTOTYPES
#undef __JS_ENUMERATE
Function* m_eval_function;
};
template<typename ConstructorType>