← All reports

[JSC] String#match implemented in C++

Component: JSC DFG and FTL JIT | 1440f86

Source/JavaScriptCore/dfg/DFGFixupPhase.cpp

+void FixupPhase::addStringMatchPrimordialChecks(Node* node)
+{
+ // Single CheckStructure on the primordial RegExp structure instead of
+ // TryGetById chain, since fixup-inserted TryGetByIds never specialize.
+ ...
+}

Source/JavaScriptCore/runtime/RegExpObjectInlines.h

+bool RegExpObject::isSymbolMatchFastAndNonObservable()
+{
+ // mirrors the replace variant; lastIndex must be a number since
+ // RegExp.prototype[@@match] reads it. No species watchpoint needed.
+ ...
+}

Source/JavaScriptCore/runtime/StringPrototype.cpp

+JSC_DEFINE_HOST_FUNCTION(stringProtoFuncMatch, ...)
+{
+ // re-validate after ToString(this): user toString() can mutate the
+ // regexp before RegExp.prototype[@@match] reads flags/exec.
+ ...
+}

For the JIT to emit specialized code for a built-in method, it must verify at runtime that the object's prototype chain has not been tampered with — that nobody has overridden RegExp.prototype[Symbol.match] or reassigned lastIndex — before taking the fast path. Those are primordial checks. This commit rewrites String.prototype.match as a C++ host function rather than a JS builtin, adds a DFG StringMatch node converted to RegExpMatchFast during the fixup phase, and installs the primordial checks via addStringMatchPrimordialChecks, using a single CheckStructure guard (stricter than the TryGetById chain used for String#replace) combined with a watchpoint-based purity check in isSymbolMatchFastAndNonObservable(). Violate any assumption and the JIT must OSR-exit to the spec-compliant slow path.

Up to ~1.4x on match-heavy code by removing the builtin call overhead, and the DFG/FTL fast-path surface for RegExp intrinsics widens accordingly.

Narrow: check whether addStringMatchPrimordialChecks' CheckStructure-plus-watchpoint combination can be invalidated mid-execution — a toString or getter side effect that mutates the RegExp's flags or prototype after the guard but before RegExpMatchFast executes — and whether OSR exit correctly reconstructs state when isSymbolMatchFastAndNonObservable()'s assumptions are violated post-speculation. Wider: this fix is the third member of a family (String#split, String#replace, now String#match), each with its own isSymbol*FastAndNonObservable() predicate; diff the three predicates against each other and against the spec's observable-operation list for the corresponding @@ method — a condition present in two of the three and absent from the fourth is exactly the variant to chase. Match tell in review: a primordial-check helper whose set of watchpoints differs from its siblings' without a comment explaining why.