LibJS: Include identifier information in nullish property read access

When a GetById / GetByValue bytecode operation results in accessing a
nullish object, we now include the name of the property and the object
being accessed in the exception message (if available). This should make
it easier to debug live websites.

For example, the following errors would all previously produce a generic
error message of "ToObject on null or undefined":

  > foo = null
  > foo.bar
  Uncaught exception:
  [TypeError] Cannot access property "bar" on null object "foo"
      at <unknown>

  > foo = { bar: undefined }
  > foo.bar.baz
  Uncaught exception:
  [TypeError] Cannot access property "baz" on undefined object "foo.bar"
      at <unknown>

Note we certainly don't capture all possible nullish property read
accesses here. This just covers cases I've seen most on live websites;
we can cover more cases as they arise.
This commit is contained in:
Timothy Flynn 2024-03-29 11:26:10 -04:00 committed by Andreas Kling
commit 9bbd3103a8
Notes: sideshowbarker 2024-07-16 23:03:06 +09:00
9 changed files with 137 additions and 17 deletions

View file

@ -1083,9 +1083,13 @@ ThrowCompletionOr<void> SetLocal::execute_impl(Bytecode::Interpreter&) const
ThrowCompletionOr<void> GetById::execute_impl(Bytecode::Interpreter& interpreter) const
{
auto base_identifier = interpreter.current_executable().get_identifier(m_base_identifier);
auto const& property_identifier = interpreter.current_executable().get_identifier(m_property);
auto base_value = interpreter.get(base());
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index];
interpreter.set(dst(), TRY(get_by_id(interpreter.vm(), interpreter.current_executable().get_identifier(m_property), base_value, base_value, cache)));
interpreter.set(dst(), TRY(get_by_id(interpreter.vm(), base_identifier, property_identifier, base_value, base_value, cache)));
return {};
}
@ -1094,7 +1098,7 @@ ThrowCompletionOr<void> GetByIdWithThis::execute_impl(Bytecode::Interpreter& int
auto base_value = interpreter.get(m_base);
auto this_value = interpreter.get(m_this_value);
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index];
interpreter.set(dst(), TRY(get_by_id(interpreter.vm(), interpreter.current_executable().get_identifier(m_property), base_value, this_value, cache)));
interpreter.set(dst(), TRY(get_by_id(interpreter.vm(), {}, interpreter.current_executable().get_identifier(m_property), base_value, this_value, cache)));
return {};
}
@ -1499,7 +1503,9 @@ ThrowCompletionOr<void> Await::execute_impl(Bytecode::Interpreter& interpreter)
ThrowCompletionOr<void> GetByValue::execute_impl(Bytecode::Interpreter& interpreter) const
{
interpreter.set(dst(), TRY(get_by_value(interpreter.vm(), interpreter.get(m_base), interpreter.get(m_property))));
auto base_identifier = interpreter.current_executable().get_identifier(m_base_identifier);
interpreter.set(dst(), TRY(get_by_value(interpreter.vm(), base_identifier, interpreter.get(m_base), interpreter.get(m_property))));
return {};
}