Skip to content
33 changes: 24 additions & 9 deletions src/jsc/bindings/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6774,7 +6774,14 @@
size_t prefixLen)
{
auto& vm = JSC::getVM(globalObject);
auto scope = DECLARE_THROW_SCOPE(vm);
// This is an FFI boundary called from Rust (repl.rs) without JSError
// propagation, so use a top-level scope and swallow any exception locally
// rather than letting it escape to a caller that has no scope to check it.
Comment thread
robobun marked this conversation as resolved.
Outdated
auto scope = DECLARE_TOP_EXCEPTION_SCOPE(vm);
auto clearAndEncode = [&](JSC::JSValue v) {
scope.clearException();
return JSC::JSValue::encode(v);
};

JSC::JSValue target = JSC::JSValue::decode(targetValue);
if (!target || target.isUndefined() || target.isNull()) {
Expand All @@ -6783,7 +6790,8 @@

if (!target.isObject()) {
JSObject* boxed = target.toObject(globalObject);
RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined()));
if (scope.exception()) [[unlikely]]
return clearAndEncode(JSC::jsUndefined());
target = boxed;
}

Expand All @@ -6794,40 +6802,47 @@
JSC::JSObject* object = target.getObject();
JSC::PropertyNameArrayBuilder propertyNames(vm, JSC::PropertyNameMode::Strings, JSC::PrivateSymbolMode::Exclude);
object->getPropertyNames(globalObject, propertyNames, DontEnumPropertiesMode::Include);
RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined()));
if (scope.exception()) [[unlikely]]
return clearAndEncode(JSC::jsUndefined());

JSC::JSArray* completions = JSC::constructEmptyArray(globalObject, nullptr, 0);
RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined()));
if (scope.exception()) [[unlikely]]
return clearAndEncode(JSC::jsUndefined());

unsigned completionIndex = 0;
for (const auto& propertyName : propertyNames) {
WTF::String name = propertyName.string();
if (prefix.isEmpty() || name.startsWith(prefix)) {
completions->putDirectIndex(globalObject, completionIndex++, JSC::jsString(vm, name));
RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined()));
if (scope.exception()) [[unlikely]]
return clearAndEncode(JSC::jsUndefined());
}
}

// Also check the prototype chain
JSC::JSValue proto = object->getPrototype(globalObject);
RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(completions));
if (scope.exception()) [[unlikely]]
return clearAndEncode(completions);

while (proto && proto.isObject()) {
JSC::JSObject* protoObj = proto.getObject();
JSC::PropertyNameArrayBuilder protoNames(vm, JSC::PropertyNameMode::Strings, JSC::PrivateSymbolMode::Exclude);
protoObj->getPropertyNames(globalObject, protoNames, DontEnumPropertiesMode::Include);
RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(completions));
if (scope.exception()) [[unlikely]]
return clearAndEncode(completions);

for (const auto& propertyName : protoNames) {
WTF::String name = propertyName.string();
if (prefix.isEmpty() || name.startsWith(prefix)) {
completions->putDirectIndex(globalObject, completionIndex++, JSC::jsString(vm, name));
RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(completions));
if (scope.exception()) [[unlikely]]
return clearAndEncode(completions);
}
}

proto = protoObj->getPrototype(globalObject);
RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(completions));
if (scope.exception()) [[unlikely]]
return clearAndEncode(completions);

Check warning on line 6845 in src/jsc/bindings/bindings.cpp

View check run for this annotation

Claude / Claude Code Review

Unbounded prototype-chain walk hangs on cyclic Proxy now that user objects reach getCompletions

The manual prototype-chain walk here has no depth cap or visited-set, so a Proxy whose `getPrototypeOf` trap returns itself makes it spin forever — and this PR newly hands user objects (via `resolve_object_expr`) to this loop on every keystroke. Since `getPropertyNames` at :6804 already walks the prototype chain (per the comment at :1096-1097), this outer loop is also redundant and duplicates every inherited name; consider dropping it entirely, or capping it (e.g. `for (unsigned depth = 0; proto
Comment thread
claude[bot] marked this conversation as resolved.
Outdated
}

return JSC::JSValue::encode(completions);
Expand Down
Loading