Skip to content

FunctionHasExecutedCache: expose function names in getFunctionRanges#178

Open
bilby91 wants to merge 4 commits intooven-sh:mainfrom
crunchloop:function-has-executed-cache-names
Open

FunctionHasExecutedCache: expose function names in getFunctionRanges#178
bilby91 wants to merge 4 commits intooven-sh:mainfrom
crunchloop:function-has-executed-cache-names

Conversation

@bilby91
Copy link

@bilby91 bilby91 commented Mar 23, 2026

Summary

  • Extend FunctionHasExecutedCache to store and return function names alongside execution status and byte ranges
  • Add RangeValue struct holding hasExecuted bool + function name String, replacing the bare bool in RangeMap
  • Extend insertUnexecutedRange() to accept an optional function name parameter (default empty, backward compatible)
  • Extend getFunctionRanges() return type to include the function name as a 4th tuple element
  • Update all call sites (CodeBlock.cpp, ProgramExecutable.cpp, JSModuleRecord.cpp) to pass UnlinkedFunctionExecutable::ecmaName()
  • Add $vm.getFunctionRanges() test helper in JSDollarVM.cpp
  • Add JSTests/controlFlowProfiler/function-names.js test

Motivation

Coverage tools (e.g., Bun's LCOV reporter) need per-function names to emit standard FN: and FNDA: records. Currently, getFunctionRanges() only returns (hasExecuted, startOffset, endOffset), forcing consumers to parse function names from source text heuristically. Since UnlinkedFunctionExecutable::ecmaName() is already available at all insertUnexecutedRange() call sites, this is a minimal change to expose it through the existing API.

Test plan

  • All existing JSTests/controlFlowProfiler/ tests pass (8/8)
  • New function-names.js test verifies named functions, arrow functions, and execution status
  • Bun coverage tests pass with the extended API (12/12)

🤖 Generated with Claude Code

Extend FunctionHasExecutedCache to store and return function names
alongside execution status and byte ranges. This enables coverage
tools to report per-function data (FN/FNDA records in LCOV) using
real function names from JSC rather than heuristic source text parsing.

Changes:
- Add RangeValue struct holding hasExecuted bool + function name String
- Change RangeMap value type from bool to RangeValue
- Extend insertUnexecutedRange() to accept an optional function name
- Extend getFunctionRanges() return type to include the name
- Update all call sites to pass UnlinkedFunctionExecutable::ecmaName()
- Add $vm.getFunctionRanges() test helper in JSDollarVM
- Add JSTests/controlFlowProfiler/function-names.js test

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@bilby91
Copy link
Author

bilby91 commented Mar 23, 2026

I haven't submitted the PR on the Bun side yet. I'm looking to get similar coverage characteristics than V8.

@coderabbitai
Copy link

coderabbitai bot commented Mar 23, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 07a70564-7f08-4138-abfc-c808f71ec1a6

📥 Commits

Reviewing files that changed from the base of the PR and between c433d0e and 9d2e644.

📒 Files selected for processing (1)
  • Source/JavaScriptCore/tools/JSDollarVM.cpp

Walkthrough

Per-range execution entries now include function names alongside execution flags. Call sites were updated to supply ECMA names when inserting unexecuted ranges. A $vm.getFunctionRanges host API and a JS test were added to expose and validate ranges as { hasExecuted, start, end, name }.

Changes

Cohort / File(s) Summary
Function Execution Cache
Source/JavaScriptCore/runtime/FunctionHasExecutedCache.h, Source/JavaScriptCore/runtime/FunctionHasExecutedCache.cpp
Replaced stored per-range value bool with RangeValue { bool m_hasExecuted; String m_functionName }. insertUnexecutedRange gains an additional functionName parameter (default empty). getFunctionRanges now returns tuples (hasExecuted, start, end, name). Adjusted callers to read/write m_hasExecuted and m_functionName.
Callsites Passing Names
Source/JavaScriptCore/bytecode/CodeBlock.cpp, Source/JavaScriptCore/runtime/JSModuleRecord.cpp, Source/JavaScriptCore/runtime/ProgramExecutable.cpp
Updated calls to insertUnexecutedRange(...) to include unlinked*Executable->ecmaName().string() as the extra functionName argument. No other logic changes in these call sites.
Control Flow Profiler
Source/JavaScriptCore/runtime/ControlFlowProfiler.cpp
Local variable holding results of getFunctionRanges(sourceID) changed to auto for deduction; iteration and tuple field access remain the same.
VM Host API
Source/JavaScriptCore/tools/JSDollarVM.cpp
Added $vm.getFunctionRanges(sourceFunction) host function (arity 1) that returns an array of objects { hasExecuted, start, end, name } for the sourceID of the provided JSFunction.
Tests
JSTests/controlFlowProfiler/function-names.js
Added test that defines two named functions and an arrow function, invokes one, calls $vm.getFunctionRanges(namedFunction), and asserts presence of ranges, correct name fields, execution flags (hasExecuted), and valid start/end ordering.
🚥 Pre-merge checks | ✅ 1 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The PR description provides a comprehensive summary, clear motivation, and test plan, but does not follow the WebKit template format (missing bug reference and Bugzilla link). Consider adding a Bugzilla bug reference link at the top and using the standard WebKit PR template format with 'Reviewed by' section for consistency with project guidelines.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: exposing function names in the getFunctionRanges API of FunctionHasExecutedCache.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@JSTests/controlFlowProfiler/function-names.js`:
- Around line 36-42: Add the missing assertions for arrowFuncRange: verify its
execution status with an assertion on arrowFuncRange.hasExecuted (match the
expected execution like namedFunction, e.g., === true) and verify the range
validity with an assertion that arrowFuncRange.start < arrowFuncRange.end; place
these alongside the existing checks for namedFunctionRange and
anotherFunctionRange so arrowFuncRange has the same coverage.

In `@Source/JavaScriptCore/tools/JSDollarVM.cpp`:
- Around line 3675-3683: The loop that builds entries for functionRanges must
check for exceptions after calling result->putDirectIndex(globalObject, i,
entry) because putDirectIndex can throw (e.g., allocation during array growth);
update the for-loop in JSDollarVM.cpp to test vm->exception() (or the
appropriate exception-check API used elsewhere in this file) immediately after
each putDirectIndex call and return/propagate the exception or break out as the
surrounding function's error-handling convention requires, keeping all other
entry construction (JSFinalObject::create, putDirect for fields, and use of
result and functionRanges) unchanged.
- Around line 3666-3668: The code assumes a callable is a JSFunction and
directly calls jsExecutable() on the result of jsDynamicCast<JSFunction*>, which
can be null for non-JSFunction callables; modify the logic around JSValue
functionValue / jsDynamicCast<JSFunction*> to null-check the result of
jsDynamicCast<JSFunction*> before calling ->jsExecutable(), and handle the
nullptr case the same way as similar checks in this file (e.g., return an
appropriate exception or take the non-JSFunction path as done near lines
2541-2544), referencing the symbols JSValue, jsDynamicCast<JSFunction*>, and
jsExecutable() to locate and update the code.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 08ef7d16-cf5c-49ba-a913-a582cabc9bf6

📥 Commits

Reviewing files that changed from the base of the PR and between fc9f2fa and ecf6b4a.

📒 Files selected for processing (8)
  • JSTests/controlFlowProfiler/function-names.js
  • Source/JavaScriptCore/bytecode/CodeBlock.cpp
  • Source/JavaScriptCore/runtime/ControlFlowProfiler.cpp
  • Source/JavaScriptCore/runtime/FunctionHasExecutedCache.cpp
  • Source/JavaScriptCore/runtime/FunctionHasExecutedCache.h
  • Source/JavaScriptCore/runtime/JSModuleRecord.cpp
  • Source/JavaScriptCore/runtime/ProgramExecutable.cpp
  • Source/JavaScriptCore/tools/JSDollarVM.cpp

- Add null check for jsDynamicCast<JSFunction*> in $vm.getFunctionRanges
  to handle non-JSFunction callables (BoundFunction, Proxy, etc.)
- Add RETURN_IF_EXCEPTION after putDirectIndex in the loop
- Add missing arrowFuncRange assertions in function-names.js test

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Source/JavaScriptCore/tools/JSDollarVM.cpp`:
- Around line 3679-3687: The loop repeatedly calls
JSFinalObject::createStructure(vm, globalObject,
globalObject->objectPrototype(), 4) which allocates a new Structure each
iteration; move that call out of the loop into a local Structure* (e.g.
reuseStructure) before iterating over functionRanges and use
JSFinalObject::create(vm, reuseStructure) inside the loop to reduce allocation
churn; keep existing uses of entry, functionRanges, result->putDirectIndex(...)
and RETURN_IF_EXCEPTION(...) unchanged except for replacing the inline
createStructure call with the reused Structure*.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 06b08c67-9f8c-402e-a16f-f9c2cdcce6f3

📥 Commits

Reviewing files that changed from the base of the PR and between ecf6b4a and 96d85c6.

📒 Files selected for processing (2)
  • JSTests/controlFlowProfiler/function-names.js
  • Source/JavaScriptCore/tools/JSDollarVM.cpp

The entry Structure is identical for every iteration, so create it
once before the loop to reduce allocation churn.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Source/JavaScriptCore/tools/JSDollarVM.cpp`:
- Around line 3658-3659: Update the comment for $vm.getFunctionRanges to match
the actual emitted object field order: currently the code emits objects in the
order hasExecuted, start, end, name, so change the documented shape from "{
name, hasExecuted, start, end }" to "{ hasExecuted, start, end, name }" (or
reorder the emitted properties in the function to match the comment) — locate
the comment near the $vm.getFunctionRanges implementation in JSDollarVM.cpp and
make the comment and emitted field order consistent.
- Around line 3669-3673: The code calls JSFunction::jsExecutable() unguarded,
which asserts for host/native functions; change it to retrieve the generic
Executable (via function->executable() or equivalent non-asserting accessor) and
then perform a dynamic cast to FunctionExecutable* using
jsDynamicCast<FunctionExecutable*>(...), and if the cast returns null call
throwVMTypeError(globalObject, scope, "Expected argument to be a source
JSFunction"_s) to reject non-source (host) functions before proceeding.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: dd1e1b70-4c20-4ca3-a749-dd553475a461

📥 Commits

Reviewing files that changed from the base of the PR and between 96d85c6 and c433d0e.

📒 Files selected for processing (1)
  • Source/JavaScriptCore/tools/JSDollarVM.cpp

- Use jsDynamicCast<FunctionExecutable*>(function->executable()) instead
  of function->jsExecutable() to avoid assert on host/native functions
- Fix comment to match actual field emission order: hasExecuted, start,
  end, name

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant