mirror of
https://github.com/zebrajr/ladybird.git
synced 2026-01-15 12:15:15 +00:00
LibJS: Skip generic call when using regexp builtins in StringPrototype
For StringPrototype functions that defer to RegExpPrototype builtins, we can skip the generic call stuff (eliding the execution context etc) and just call the builtin directly. 1.03x speedup on Octane/regexp.js
This commit is contained in:
committed by
Andreas Kling
parent
add8402536
commit
54b755126c
@@ -26,6 +26,9 @@ namespace JS::Bytecode {
|
||||
O(MathSin, math_sin, Math, sin, 1) \
|
||||
O(MathCos, math_cos, Math, cos, 1) \
|
||||
O(MathTan, math_tan, Math, tan, 1) \
|
||||
O(RegExpPrototypeExec, regexp_prototype_exec, RegExpPrototype, exec, 1) \
|
||||
O(RegExpPrototypeReplace, regexp_prototype_replace, RegExpPrototype, replace, 2) \
|
||||
O(RegExpPrototypeSplit, regexp_prototype_split, RegExpPrototype, split, 2) \
|
||||
O(OrdinaryHasInstance, ordinary_has_instance, InternalBuiltin, ordinary_has_instance, 1) \
|
||||
O(ArrayIteratorPrototypeNext, array_iterator_prototype_next, ArrayIteratorPrototype, next, 0) \
|
||||
O(MapIteratorPrototypeNext, map_iterator_prototype_next, MapIteratorPrototype, next, 0) \
|
||||
|
||||
@@ -2417,6 +2417,9 @@ static ThrowCompletionOr<Value> dispatch_builtin_call(Bytecode::Interpreter& int
|
||||
return TRY(MathObject::cos_impl(interpreter.vm(), interpreter.get(arguments[0])));
|
||||
case Builtin::MathTan:
|
||||
return TRY(MathObject::tan_impl(interpreter.vm(), interpreter.get(arguments[0])));
|
||||
case Builtin::RegExpPrototypeExec:
|
||||
case Builtin::RegExpPrototypeReplace:
|
||||
case Builtin::RegExpPrototypeSplit:
|
||||
case Builtin::ArrayIteratorPrototypeNext:
|
||||
case Builtin::MapIteratorPrototypeNext:
|
||||
case Builtin::SetIteratorPrototypeNext:
|
||||
|
||||
@@ -39,14 +39,14 @@ void RegExpPrototype::initialize(Realm& realm)
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||
define_native_function(realm, vm.names.toString, to_string, 0, attr);
|
||||
define_native_function(realm, vm.names.test, test, 1, attr);
|
||||
define_native_function(realm, vm.names.exec, exec, 1, attr);
|
||||
define_native_function(realm, vm.names.exec, exec, 1, attr, Bytecode::Builtin::RegExpPrototypeExec);
|
||||
define_native_function(realm, vm.names.compile, compile, 2, attr);
|
||||
|
||||
define_native_function(realm, vm.well_known_symbol_match(), symbol_match, 1, attr);
|
||||
define_native_function(realm, vm.well_known_symbol_match_all(), symbol_match_all, 1, attr);
|
||||
define_native_function(realm, vm.well_known_symbol_replace(), symbol_replace, 2, attr);
|
||||
define_native_function(realm, vm.well_known_symbol_replace(), symbol_replace, 2, attr, Bytecode::Builtin::RegExpPrototypeReplace);
|
||||
define_native_function(realm, vm.well_known_symbol_search(), symbol_search, 1, attr);
|
||||
define_native_function(realm, vm.well_known_symbol_split(), symbol_split, 2, attr);
|
||||
define_native_function(realm, vm.well_known_symbol_split(), symbol_split, 2, attr, Bytecode::Builtin::RegExpPrototypeSplit);
|
||||
|
||||
define_native_accessor(realm, vm.names.flags, flags, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.source, source, {}, Attribute::Configurable);
|
||||
@@ -459,8 +459,12 @@ ThrowCompletionOr<Value> regexp_exec(VM& vm, Object& regexp_object, GC::Ref<Prim
|
||||
|
||||
// 2. If IsCallable(exec) is true, then
|
||||
if (exec.is_function()) {
|
||||
auto& exec_function = exec.as_function();
|
||||
if (&exec_function == vm.current_realm()->get_builtin_value(Bytecode::Builtin::RegExpPrototypeExec))
|
||||
return regexp_builtin_exec(vm, static_cast<RegExpObject&>(regexp_object), string);
|
||||
|
||||
// a. Let result be ? Call(exec, R, « S »).
|
||||
auto result = TRY(call(vm, exec.as_function(), ®exp_object, string));
|
||||
auto result = TRY(call(vm, exec_function, ®exp_object, string));
|
||||
|
||||
// b. If Type(result) is neither Object nor Null, throw a TypeError exception.
|
||||
if (!result.is_object() && !result.is_null())
|
||||
@@ -718,6 +722,11 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_replace)
|
||||
// 3. Let S be ? ToString(string).
|
||||
auto string = TRY(string_value.to_primitive_string(vm));
|
||||
|
||||
return symbol_replace_impl(vm, *regexp_object, string, replace_value);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<Value> RegExpPrototype::symbol_replace_impl(VM& vm, Object& regexp_object, GC::Ref<PrimitiveString> string, Value replace_value)
|
||||
{
|
||||
// 4. Let lengthS be the number of code unit elements in S.
|
||||
// 5. Let functionalReplace be IsCallable(replaceValue).
|
||||
|
||||
@@ -730,7 +739,7 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_replace)
|
||||
|
||||
// 7. Let flags be ? ToString(? Get(rx, "flags")).
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto flags_value = TRY(regexp_object->get(vm.names.flags, cache));
|
||||
auto flags_value = TRY(regexp_object.get(vm.names.flags, cache));
|
||||
auto flags = TRY(flags_value.to_string(vm));
|
||||
|
||||
// 8. If flags contains "g", let global be true. Otherwise, let global be false.
|
||||
@@ -740,7 +749,7 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_replace)
|
||||
if (global) {
|
||||
// a. Perform ? Set(rx, "lastIndex", +0𝔽, true).
|
||||
static Bytecode::PropertyLookupCache cache2;
|
||||
TRY(regexp_object->set(vm.names.lastIndex, Value(0), cache2));
|
||||
TRY(regexp_object.set(vm.names.lastIndex, Value(0), cache2));
|
||||
}
|
||||
|
||||
// 10. Let results be a new empty List.
|
||||
@@ -969,8 +978,6 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::source)
|
||||
// 22.2.6.14 RegExp.prototype [ @@split ] ( string, limit ), https://tc39.es/ecma262/#sec-regexp.prototype-@@split
|
||||
JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_split)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
// 1. Let rx be the this value.
|
||||
// 2. If Type(rx) is not Object, throw a TypeError exception.
|
||||
auto regexp_object = TRY(this_object(vm));
|
||||
@@ -978,12 +985,19 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_split)
|
||||
// 3. Let S be ? ToString(string).
|
||||
auto string = TRY(vm.argument(0).to_primitive_string(vm));
|
||||
|
||||
return symbol_split_impl(vm, *regexp_object, string, vm.argument(1));
|
||||
}
|
||||
|
||||
ThrowCompletionOr<Value> RegExpPrototype::symbol_split_impl(VM& vm, Object& regexp_object, GC::Ref<PrimitiveString> string, Value limit_value)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
// 4. Let C be ? SpeciesConstructor(rx, %RegExp%).
|
||||
auto* constructor = TRY(species_constructor(vm, regexp_object, realm.intrinsics().regexp_constructor()));
|
||||
|
||||
// 5. Let flags be ? ToString(? Get(rx, "flags")).
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto flags_value = TRY(regexp_object->get(vm.names.flags, cache));
|
||||
auto flags_value = TRY(regexp_object.get(vm.names.flags, cache));
|
||||
auto flags = TRY(flags_value.to_string(vm));
|
||||
|
||||
// 6. If flags contains "u" or flags contains "v", let unicodeMatching be true.
|
||||
@@ -995,7 +1009,7 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_split)
|
||||
auto new_flags = flags.bytes_as_string_view().find('y').has_value() ? move(flags) : MUST(String::formatted("{}y", flags));
|
||||
|
||||
// 10. Let splitter be ? Construct(C, « rx, newFlags »).
|
||||
auto splitter = TRY(construct(vm, *constructor, regexp_object, PrimitiveString::create(vm, move(new_flags))));
|
||||
auto splitter = TRY(construct(vm, *constructor, ®exp_object, PrimitiveString::create(vm, move(new_flags))));
|
||||
|
||||
// 11. Let A be ! ArrayCreate(0).
|
||||
auto array = MUST(Array::create(realm, 0));
|
||||
@@ -1005,8 +1019,8 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_split)
|
||||
|
||||
// 13. If limit is undefined, let lim be 2^32 - 1; else let lim be ℝ(? ToUint32(limit)).
|
||||
auto limit = NumericLimits<u32>::max();
|
||||
if (!vm.argument(1).is_undefined())
|
||||
limit = TRY(vm.argument(1).to_u32(vm));
|
||||
if (!limit_value.is_undefined())
|
||||
limit = TRY(limit_value.to_u32(vm));
|
||||
|
||||
// 14. If lim is 0, return A.
|
||||
if (limit == 0)
|
||||
|
||||
@@ -22,6 +22,9 @@ public:
|
||||
virtual void initialize(Realm&) override;
|
||||
virtual ~RegExpPrototype() override = default;
|
||||
|
||||
static ThrowCompletionOr<Value> symbol_replace_impl(VM&, Object& regexp_object, GC::Ref<PrimitiveString> string, Value replace_value);
|
||||
static ThrowCompletionOr<Value> symbol_split_impl(VM&, Object& regexp_object, GC::Ref<PrimitiveString> string, Value limit_value);
|
||||
|
||||
private:
|
||||
explicit RegExpPrototype(Realm&);
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <LibJS/Runtime/Intl/CollatorConstructor.h>
|
||||
#include <LibJS/Runtime/PrimitiveString.h>
|
||||
#include <LibJS/Runtime/RegExpObject.h>
|
||||
#include <LibJS/Runtime/RegExpPrototype.h>
|
||||
#include <LibJS/Runtime/StringIterator.h>
|
||||
#include <LibJS/Runtime/StringObject.h>
|
||||
#include <LibJS/Runtime/StringPrototype.h>
|
||||
@@ -876,6 +877,12 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::replace)
|
||||
|
||||
// b. If replacer is not undefined, then
|
||||
if (replacer) {
|
||||
if (replacer == vm.current_realm()->get_builtin_value(Bytecode::Builtin::RegExpPrototypeReplace)) {
|
||||
// OPTIMIZATION: The common case of RegExp.prototype[@@replace]
|
||||
auto& rx = search_value.as_object();
|
||||
auto string = TRY(this_object.to_primitive_string(vm));
|
||||
return TRY(RegExpPrototype::symbol_replace_impl(vm, rx, *string, replace_value));
|
||||
}
|
||||
// i. Return ? Call(replacer, searchValue, « O, replaceValue »).
|
||||
return TRY(call(vm, *replacer, search_value, this_object, replace_value));
|
||||
}
|
||||
@@ -972,6 +979,12 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::replace_all)
|
||||
|
||||
// d. If replacer is not undefined, then
|
||||
if (replacer) {
|
||||
if (replacer == vm.current_realm()->get_builtin_value(Bytecode::Builtin::RegExpPrototypeReplace)) {
|
||||
// OPTIMIZATION: The common case of RegExp.prototype[@@replace]
|
||||
auto& rx = search_value.as_object();
|
||||
auto string = TRY(this_object.to_primitive_string(vm));
|
||||
return TRY(RegExpPrototype::symbol_replace_impl(vm, rx, *string, replace_value));
|
||||
}
|
||||
// i. Return ? Call(replacer, searchValue, « O, replaceValue »).
|
||||
return TRY(call(vm, *replacer, search_value, this_object, replace_value));
|
||||
}
|
||||
@@ -1154,6 +1167,12 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::split)
|
||||
auto splitter = TRY(separator_argument.get_method(vm, vm.well_known_symbol_split(), cache));
|
||||
// b. If splitter is not undefined, then
|
||||
if (splitter) {
|
||||
if (splitter == realm.get_builtin_value(Bytecode::Builtin::RegExpPrototypeSplit)) {
|
||||
// OPTIMIZATION: The common case of RegExp.prototype[@@split]
|
||||
auto& rx = separator_argument.as_object();
|
||||
auto string = TRY(object.to_primitive_string(vm));
|
||||
return TRY(RegExpPrototype::symbol_split_impl(vm, rx, *string, limit_argument));
|
||||
}
|
||||
// i. Return ? Call(splitter, separator, « O, limit »).
|
||||
return TRY(call(vm, *splitter, separator_argument, object, limit_argument));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user