Browse Source

LibJS: Improve ResolveBinding + add GetIdentifierReference

ResolveBinding now matches the spec, while the non-conforming parts
are moved to GetIdentifierReference.

Implementing this properly requires variable bindings.
Andreas Kling 4 years ago
parent
commit
fd43d1e205

+ 2 - 2
Userland/Libraries/LibJS/AST.cpp

@@ -713,9 +713,9 @@ Reference Expression::to_reference(Interpreter&, GlobalObject&) const
     return {};
 }
 
-Reference Identifier::to_reference(Interpreter& interpreter, GlobalObject& global_object) const
+Reference Identifier::to_reference(Interpreter& interpreter, GlobalObject&) const
 {
-    return interpreter.vm().resolve_binding(global_object, string());
+    return interpreter.vm().resolve_binding(string());
 }
 
 Reference MemberExpression::to_reference(Interpreter& interpreter, GlobalObject& global_object) const

+ 32 - 6
Userland/Libraries/LibJS/Runtime/VM.cpp

@@ -392,17 +392,43 @@ Value VM::get_variable(const FlyString& name, GlobalObject& global_object)
     return value;
 }
 
-// 9.4.2 ResolveBinding ( name [ , env ] ), https://tc39.es/ecma262/#sec-resolvebinding
-Reference VM::resolve_binding(GlobalObject& global_object, FlyString const& name, Environment*)
+// 9.1.2.1 GetIdentifierReference ( env, name, strict ), https://tc39.es/ecma262/#sec-getidentifierreference
+Reference VM::get_identifier_reference(Environment* environment, FlyString const& name, bool strict)
 {
-    // FIXME: This implementation of ResolveBinding is non-conforming.
+    // 1. If env is the value null, then
+    if (!environment) {
+        // a. Return the Reference Record { [[Base]]: unresolvable, [[ReferencedName]]: name, [[Strict]]: strict, [[ThisValue]]: empty }.
+        return Reference { Reference::BaseType::Unresolvable, name, strict };
+    }
+
+    // FIXME: The remainder of this function is non-conforming.
 
-    for (auto* environment = lexical_environment(); environment && environment->outer_environment(); environment = environment->outer_environment()) {
+    auto& global_object = environment->global_object();
+    for (; environment && environment->outer_environment(); environment = environment->outer_environment()) {
         auto possible_match = environment->get_from_environment(name);
         if (possible_match.has_value())
-            return Reference { *environment, name, in_strict_mode() };
+            return Reference { *environment, name, strict };
     }
-    return Reference { global_object.environment(), name, in_strict_mode() };
+    return Reference { global_object.environment(), name, strict };
+}
+
+// 9.4.2 ResolveBinding ( name [ , env ] ), https://tc39.es/ecma262/#sec-resolvebinding
+Reference VM::resolve_binding(FlyString const& name, Environment* environment)
+{
+    // 1. If env is not present or if env is undefined, then
+    if (!environment) {
+        // a. Set env to the running execution context's LexicalEnvironment.
+        environment = running_execution_context().lexical_environment;
+    }
+
+    // 2. Assert: env is an Environment Record.
+    VERIFY(environment);
+
+    // 3. If the code matching the syntactic production that is being evaluated is contained in strict mode code, let strict be true; else let strict be false.
+    bool strict = in_strict_mode();
+
+    // 4. Return ? GetIdentifierReference(env, name, strict).
+    return get_identifier_reference(environment, name, strict);
 }
 
 Value VM::construct(FunctionObject& function, FunctionObject& new_target, Optional<MarkedValueList> arguments)

+ 2 - 1
Userland/Libraries/LibJS/Runtime/VM.h

@@ -204,7 +204,8 @@ public:
     void assign(const FlyString& target, Value, GlobalObject&, bool first_assignment = false, Environment* specific_scope = nullptr);
     void assign(const NonnullRefPtr<BindingPattern>& target, Value, GlobalObject&, bool first_assignment = false, Environment* specific_scope = nullptr);
 
-    Reference resolve_binding(GlobalObject&, FlyString const&, Environment* = nullptr);
+    Reference resolve_binding(FlyString const&, Environment* = nullptr);
+    Reference get_identifier_reference(Environment*, FlyString const&, bool strict);
 
     template<typename T, typename... Args>
     void throw_exception(GlobalObject& global_object, Args&&... args)