[*] Speed glorious speed

[+] Removed isolate entropy from the AstXXX hash seed bases, string inls, and string hash to ensure v8::Names deterministic across instances. Hashes are expected to colloid. Doubt anything depends on isolate entropy.
[+] v8::String::GloballyInternalize(...)
[+] v8::String::Share(...)
[+] ScriptCompiler::EvaluateGlobal

Previous aurora commits: 4c7c7d1a24, c9b547e373, 97450cb8d1, 07e2139b4f, ...
This commit is contained in:
Reece Wilson 2022-11-08 23:37:52 +00:00
parent 4c7c7d1a24
commit f86bf4d785
14 changed files with 386 additions and 108 deletions

View File

@ -223,6 +223,10 @@ class V8_EXPORT String : public Name {
*/ */
bool IsExternalOneByte() const; bool IsExternalOneByte() const;
v8::Local<v8::String> GloballyInternalize();
v8::Local<v8::String> Share(v8::Isolate *pIsolate);
class V8_EXPORT ExternalStringResourceBase { class V8_EXPORT ExternalStringResourceBase {
public: public:
virtual ~ExternalStringResourceBase() = default; virtual ~ExternalStringResourceBase() = default;

View File

@ -597,6 +597,11 @@ class V8_EXPORT ScriptCompiler {
CompileOptions options = kNoCompileOptions, CompileOptions options = kNoCompileOptions,
NoCacheReason no_cache_reason = kNoCacheNoReason); NoCacheReason no_cache_reason = kNoCacheNoReason);
static MaybeLocal<v8::Value> EvaluateGlobal(v8::Isolate* isolate,
v8::Local<v8::String> source,
bool repl);
/** /**
* Compiles the specified script (bound to current context). * Compiles the specified script (bound to current context).
* *

View File

@ -165,6 +165,9 @@ class V8_EXPORT Template : public Data {
using GenericNamedPropertyGetterCallback = using GenericNamedPropertyGetterCallback =
void (*)(Local<Name> property, const PropertyCallbackInfo<Value>& info); void (*)(Local<Name> property, const PropertyCallbackInfo<Value>& info);
using GenericNamedPropertyGetterFastCallback = v8::Local<Value> (*)(
v8::Isolate* pIsolate, Local<Object> that, Local<Name> property, bool *pShouldCheckBase);
/** /**
* Interceptor for set requests on an object. * Interceptor for set requests on an object.
* *
@ -190,6 +193,11 @@ using GenericNamedPropertySetterCallback =
void (*)(Local<Name> property, Local<Value> value, void (*)(Local<Name> property, Local<Value> value,
const PropertyCallbackInfo<Value>& info); const PropertyCallbackInfo<Value>& info);
using GenericNamedPropertySetterFastCallback = bool (*)(v8::Isolate *pIsolate,
Local<Object> that,
Local<Name> property,
Local<Value> &value);
/** /**
* Intercepts all requests that query the attributes of the * Intercepts all requests that query the attributes of the
* property, e.g., getOwnPropertyDescriptor(), propertyIsEnumerable(), and * property, e.g., getOwnPropertyDescriptor(), propertyIsEnumerable(), and
@ -302,6 +310,10 @@ using GenericNamedPropertyDescriptorCallback =
using IndexedPropertyGetterCallback = using IndexedPropertyGetterCallback =
void (*)(uint32_t index, const PropertyCallbackInfo<Value>& info); void (*)(uint32_t index, const PropertyCallbackInfo<Value>& info);
using IndexedPropertyGetterFastCallback =
v8::Local<Value> (*)(v8::Isolate* pIsolate,
Local<Object> that,
uint32_t index);
/** /**
* See `v8::GenericNamedPropertySetterCallback`. * See `v8::GenericNamedPropertySetterCallback`.
*/ */
@ -309,6 +321,11 @@ using IndexedPropertySetterCallback =
void (*)(uint32_t index, Local<Value> value, void (*)(uint32_t index, Local<Value> value,
const PropertyCallbackInfo<Value>& info); const PropertyCallbackInfo<Value>& info);
using IndexedPropertySetterFastCallback = bool (*)(v8::Isolate* pIsolate,
Local<Object> that,
uint32_t index,
v8::Local<Value> value);
/** /**
* See `v8::GenericNamedPropertyQueryCallback`. * See `v8::GenericNamedPropertyQueryCallback`.
*/ */
@ -623,6 +640,11 @@ enum class PropertyHandlerFlags {
* The getter, query, enumerator callbacks do not produce side effects. * The getter, query, enumerator callbacks do not produce side effects.
*/ */
kHasNoSideEffect = 1 << 3, kHasNoSideEffect = 1 << 3,
/**
*
*/
kUseNoProxyNoExternalCallbackInfoFastPath = 1 << 4
}; };
struct NamedPropertyHandlerConfiguration { struct NamedPropertyHandlerConfiguration {

View File

@ -140,6 +140,8 @@
#include "src/wasm/wasm-serialization.h" #include "src/wasm/wasm-serialization.h"
#endif // V8_ENABLE_WEBASSEMBLY #endif // V8_ENABLE_WEBASSEMBLY
#include "src/debug/debug-evaluate.h"
#if V8_OS_LINUX || V8_OS_DARWIN || V8_OS_FREEBSD #if V8_OS_LINUX || V8_OS_DARWIN || V8_OS_FREEBSD
#include <signal.h> #include <signal.h>
@ -1725,6 +1727,8 @@ i::Handle<i::InterceptorInfo> CreateInterceptorInfo(
obj->set_has_no_side_effect( obj->set_has_no_side_effect(
static_cast<int>(flags) & static_cast<int>(flags) &
static_cast<int>(PropertyHandlerFlags::kHasNoSideEffect)); static_cast<int>(PropertyHandlerFlags::kHasNoSideEffect));
obj->set_use_fastpath((static_cast<int>(flags) &
static_cast<int>(PropertyHandlerFlags::kUseNoProxyNoExternalCallbackInfoFastPath)) != 0);
if (data.IsEmpty()) { if (data.IsEmpty()) {
data = v8::Undefined(reinterpret_cast<v8::Isolate*>(i_isolate)); data = v8::Undefined(reinterpret_cast<v8::Isolate*>(i_isolate));
@ -2555,6 +2559,22 @@ MaybeLocal<UnboundScript> ScriptCompiler::CompileUnboundScript(
return CompileUnboundInternal(v8_isolate, source, options, no_cache_reason); return CompileUnboundInternal(v8_isolate, source, options, no_cache_reason);
} }
MaybeLocal<v8::Value> ScriptCompiler::EvaluateGlobal(
v8::Isolate* isolate, v8::Local<v8::String> source,
bool repl) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
PREPARE_FOR_DEBUG_INTERFACE_EXECUTION_WITH_ISOLATE(i_isolate, Value);
i::REPLMode repl_mode = repl ? i::REPLMode::kYes : i::REPLMode::kNo;
Local<Value> result;
has_pending_exception = !ToLocal<Value>(
i::DebugEvaluate::Global(i_isolate, Utils::OpenHandle(*source),
debug::EvaluateGlobalMode::kDefault,
repl_mode),
&result);
RETURN_ON_FAILED_EXECUTION(Value);
RETURN_ESCAPED(result)
}
MaybeLocal<Script> ScriptCompiler::Compile(Local<Context> context, MaybeLocal<Script> ScriptCompiler::Compile(Local<Context> context,
Source* source, Source* source,
CompileOptions options, CompileOptions options,
@ -5871,6 +5891,22 @@ bool v8::String::IsExternalOneByte() const {
return false; return false;
} }
v8::Local<v8::String> v8::String::GloballyInternalize() {
i::DisallowGarbageCollection no_gc;
i::Handle<i::String> str = Utils::OpenHandle(this);
i::Isolate* i_isolate = i::GetIsolateFromWritableObject(*str);
if (!str->IsUniqueName()) {
return Utils::ToLocal(i_isolate->factory()->InternalizeString(str));
} else {
return Utils::ToLocal(str);
}
}
v8::Local<v8::String> v8::String::Share(v8::Isolate* pIsolate) {
i::Handle<i::String> str = Utils::OpenHandle(this);
return Utils::ToLocal(i::String::Share(reinterpret_cast<i::Isolate*>(pIsolate), str));
}
void v8::String::VerifyExternalStringResource( void v8::String::VerifyExternalStringResource(
v8::String::ExternalStringResource* value) const { v8::String::ExternalStringResource* value) const {
i::DisallowGarbageCollection no_gc; i::DisallowGarbageCollection no_gc;
@ -9048,7 +9084,9 @@ void Isolate::GetHeapStatistics(HeapStatistics* heap_statistics) {
// now we just add the values, thereby over-approximating the peak slightly. // now we just add the values, thereby over-approximating the peak slightly.
heap_statistics->malloced_memory_ = heap_statistics->malloced_memory_ =
i_isolate->allocator()->GetCurrentMemoryUsage() + i_isolate->allocator()->GetCurrentMemoryUsage() +
i_isolate->string_table()->GetCurrentMemoryUsage(); 0;
//i_isolate->string_table()->GetCurrentMemoryUsage();
// On 32-bit systems backing_store_bytes() might overflow size_t temporarily // On 32-bit systems backing_store_bytes() might overflow size_t temporarily
// due to concurrent array buffer sweeping. // due to concurrent array buffer sweeping.
heap_statistics->external_memory_ = heap_statistics->external_memory_ =

View File

@ -3314,39 +3314,18 @@ RUNTIME_FUNCTION(Runtime_StoreCallbackProperty) {
return *value; return *value;
} }
#define ASSIGN_RETURN_FAILURE_ON_EXCEPTION2()
#define RETURN_FAILURE_IF_SCHEDULED_EXCEPTION_DETECTOR2()
/** /**
* Loads a property with an interceptor performing post interceptor * Loads a property with an interceptor performing post interceptor
* lookup if interceptor failed. * lookup if interceptor failed.
*/ */
RUNTIME_FUNCTION(Runtime_LoadPropertyWithInterceptor) {
HandleScope scope(isolate);
DCHECK_EQ(5, args.length());
Handle<Name> name = args.at<Name>(0);
Handle<Object> receiver = args.at(1);
Handle<JSObject> holder = args.at<JSObject>(2);
if (!receiver->IsJSReceiver()) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, receiver, Object::ConvertReceiver(isolate, receiver));
}
{
Handle<InterceptorInfo> interceptor(holder->GetNamedInterceptor(), isolate);
PropertyCallbackArguments arguments(isolate, interceptor->data(), *receiver,
*holder, Just(kDontThrow));
Handle<Object> result = arguments.CallNamedGetter(interceptor, name);
RETURN_FAILURE_IF_SCHEDULED_EXCEPTION_DETECTOR(isolate, arguments);
if (!result.is_null()) {
arguments.AcceptSideEffects();
return *result;
}
// If the interceptor didn't handle the request, then there must be no
// side effects.
}
static V8_INLINE Object Runtime_LoadPropertyWithInterceptor_Slow(
RuntimeArguments args, Isolate* isolate, Handle<Object> receiver,
Handle<Name> name, Handle<JSObject> holder) {
LookupIterator it(isolate, receiver, name, holder); LookupIterator it(isolate, receiver, name, holder);
// Skip any lookup work until we hit the (possibly non-masking) interceptor. // Skip any lookup work until we hit the (possibly non-masking) interceptor.
while (it.state() != LookupIterator::INTERCEPTOR || while (it.state() != LookupIterator::INTERCEPTOR ||
@ -3376,37 +3355,176 @@ RUNTIME_FUNCTION(Runtime_LoadPropertyWithInterceptor) {
isolate, NewReferenceError(MessageTemplate::kNotDefined, it.name())); isolate, NewReferenceError(MessageTemplate::kNotDefined, it.name()));
} }
RUNTIME_FUNCTION(Runtime_StorePropertyWithInterceptor) { Address Runtime_LoadPropertyWithInterceptor(int args_length,
Address* args_object,
Isolate* isolate) {
DCHECK(isolate->context().is_null() || isolate->context().IsContext());
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
// Runtime functions don't follow the IC's calling convention.
Handle<Object> value = args.at(0);
Handle<JSObject> receiver = args.at<JSObject>(1);
Handle<Name> name = args.at<Name>(2);
// TODO(ishell): Cache interceptor_holder in the store handler like we do #define RT_STPWI_HOLDER_ADDRESS \
// for LoadHandler::kInterceptor case. (reinterpret_cast<Address*>(reinterpret_cast<Address>(args_object) - \
Handle<JSObject> interceptor_holder = receiver; (2 * kSystemPointerSize)))
if (receiver->IsJSGlobalProxy() &&
(!receiver->HasNamedInterceptor() || #define RT_STPWI_NAME_ADDRESS \
receiver->GetNamedInterceptor().non_masking())) { (reinterpret_cast<Address*>(reinterpret_cast<Address>(args_object) - \
interceptor_holder = (0 * kSystemPointerSize)))
handle(JSObject::cast(receiver->map().prototype()), isolate);
#define RT_STPWI_RECV_ADDRESS \
(reinterpret_cast<Address*>(reinterpret_cast<Address>(args_object) - \
(1 * kSystemPointerSize)))
Handle<Object> receiver = Handle<JSObject>(RT_STPWI_RECV_ADDRESS);
Handle<Name> name = Handle<Name>(RT_STPWI_NAME_ADDRESS);
Handle<JSObject> holder = Handle<JSObject>(RT_STPWI_HOLDER_ADDRESS);
if (!receiver->IsJSReceiver()) {
if (!Object::ConvertReceiver(isolate, receiver).ToHandle(&receiver)) {
return ReadOnlyRoots(isolate).exception().ptr();
}
} }
DCHECK(interceptor_holder->HasNamedInterceptor());
{ {
Handle<InterceptorInfo> interceptor( InterceptorInfo interceptorInfo;
interceptor_holder->GetNamedInterceptor(), isolate); auto handler = holder->map().cached_property_handler();
DCHECK(!interceptor->non_masking()); if (!handler.is_null()) {
PropertyCallbackArguments arguments(isolate, interceptor->data(), *receiver, interceptorInfo = InterceptorInfo::cast(handler);
*receiver, Just(kDontThrow)); } else {
interceptorInfo = holder->GetNamedInterceptor();
holder->map().set_cached_property_handler(interceptorInfo);
}
Handle<Object> result = arguments.CallNamedSetter(interceptor, name, value); if (interceptorInfo.use_fastpath()) {
RETURN_FAILURE_IF_SCHEDULED_EXCEPTION_DETECTOR(isolate, arguments); bool bScan{};
if (!result.is_null()) return *value; auto ret = reinterpret_cast<GenericNamedPropertyGetterFastCallback>(
// If the interceptor didn't handle the request, then there must be no v8::internal::Foreign::cast(interceptorInfo.getter())
// side effects. .foreign_address())((v8::Isolate*)(isolate),
v8::Utils::ToLocal(receiver).As<v8::Object>(),
v8::Utils::ToLocal(name), &bScan);
if (!ret.IsEmpty()) {
return (*(v8::internal::Object*)(*ret)).ptr();
} else if (!bScan) {
return (*isolate->factory()->undefined_value()).ptr();
}
} else {
PropertyCallbackArguments arguments(isolate, interceptorInfo.data(),
*receiver, *holder, Just(kDontThrow));
Handle<Object> result = arguments.CallNamedGetter(
Handle<InterceptorInfo>(interceptorInfo, isolate), name);
if (isolate->has_pending_exception())
{
return ReadOnlyRoots(isolate).exception().ptr();
}
if (!result.is_null()) {
arguments.AcceptSideEffects();
return (*result).ptr();
}
// If the interceptor didn't handle the request, then there must be no
// side effects.
}
}
RuntimeArguments args(args_length, args_object);
return BUILTIN_CONVERT_RESULT(Runtime_LoadPropertyWithInterceptor_Slow(
args, isolate, receiver, name, holder));
}
#undef RT_STPWI_HOLDER_ADDRESS
#undef RT_STPWI_NAME_ADDRESS
#undef RT_STPWI_RECV_ADDRESS
Address Runtime_StorePropertyWithInterceptor(int args_length,
Address* args_object,
Isolate* isolate) {
DCHECK_EQ(3, args_length);
HandleScope scope(isolate);
#define RT_STPWI_VALUE_ADDRESS \
(reinterpret_cast<Address*>(reinterpret_cast<Address>(args_object) - \
(0 * kSystemPointerSize)))
#define RT_STPWI_NAME_ADDRESS \
(reinterpret_cast<Address*>(reinterpret_cast<Address>(args_object) - \
(2 * kSystemPointerSize)))
#define RT_STPWI_RECV_ADDRESS \
(reinterpret_cast<Address*>(reinterpret_cast<Address>(args_object) - \
(1 * kSystemPointerSize)))
Handle<JSObject> receiver = Handle<JSObject>(RT_STPWI_RECV_ADDRESS);
Handle<Name> name = Handle<Name>(RT_STPWI_NAME_ADDRESS);
Handle<Object> value = Handle<Object>(RT_STPWI_VALUE_ADDRESS);
{
bool bCont{};
InterceptorInfo interceptorInfo;
auto handler = receiver->map().cached_property_handler();
if (!handler.is_null()) {
interceptorInfo = InterceptorInfo::cast(handler);
} else {
if (receiver->HasNamedInterceptor()) {
Handle<JSObject> interceptor_holder = receiver;
if (receiver->IsJSGlobalProxy() &&
(!receiver->HasNamedInterceptor() ||
receiver->GetNamedInterceptor().non_masking())) {
interceptor_holder =
handle(JSObject::cast(receiver->map().prototype()), isolate);
}
DCHECK(interceptor_holder->HasNamedInterceptor());
interceptorInfo = interceptor_holder->GetNamedInterceptor();
receiver->map().set_cached_property_handler(interceptorInfo);
} else {
// intentional fallthrough to LookupIterator it [...] without goto
bCont = true;
}
}
if (!bCont) {
DCHECK(!interceptorInfo.non_masking());
if (interceptorInfo.use_fastpath()) {
AssertNoContextChange ncc(isolate);
auto val = v8::Utils::ToLocal(value);
bool bStatus = reinterpret_cast<GenericNamedPropertySetterFastCallback>(
v8::internal::Foreign::cast(interceptorInfo.setter())
.foreign_address())((v8::Isolate*)(isolate),
v8::Utils::ToLocal(receiver),
v8::Utils::ToLocal(name), val);
if (bStatus) {
return v8::Utils::OpenHandle(*val).address(); // 1:
}
if (isolate->has_scheduled_exception()) {
return isolate->PromoteScheduledException().ptr();
}
} else {
PropertyCallbackArguments arguments(isolate, interceptorInfo.data(),
*receiver, *receiver,
Just(kDontThrow));
if (isolate->has_scheduled_exception()) {
arguments.AcceptSideEffects();
return isolate->PromoteScheduledException().ptr();
}
if (!arguments
.CallNamedSetter(
Handle<InterceptorInfo>(interceptorInfo, isolate), name,
value)
.is_null()) {
return *RT_STPWI_VALUE_ADDRESS; // 1:
}
}
// 1: If the interceptor didn't handle the request, then there must be no
// side effects.
}
} }
LookupIterator it(isolate, receiver, name, receiver); LookupIterator it(isolate, receiver, name, receiver);
@ -3419,11 +3537,13 @@ RUNTIME_FUNCTION(Runtime_StorePropertyWithInterceptor) {
DCHECK_EQ(LookupIterator::INTERCEPTOR, it.state()); DCHECK_EQ(LookupIterator::INTERCEPTOR, it.state());
it.Next(); it.Next();
MAYBE_RETURN(Object::SetProperty(&it, value, StoreOrigin::kNamed), if (Object::SetProperty(&it, value, StoreOrigin::kNamed).IsNothing()) {
ReadOnlyRoots(isolate).exception()); return ReadOnlyRoots(isolate).exception().ptr();
return *value; }
return *RT_STPWI_VALUE_ADDRESS;
} }
RUNTIME_FUNCTION(Runtime_LoadElementWithInterceptor) { RUNTIME_FUNCTION(Runtime_LoadElementWithInterceptor) {
// TODO(verwaest): This should probably get the holder and receiver as input. // TODO(verwaest): This should probably get the holder and receiver as input.
HandleScope scope(isolate); HandleScope scope(isolate);
@ -3435,7 +3555,21 @@ RUNTIME_FUNCTION(Runtime_LoadElementWithInterceptor) {
isolate); isolate);
PropertyCallbackArguments arguments(isolate, interceptor->data(), *receiver, PropertyCallbackArguments arguments(isolate, interceptor->data(), *receiver,
*receiver, Just(kDontThrow)); *receiver, Just(kDontThrow));
Handle<Object> result = arguments.CallIndexedGetter(interceptor, index);
Handle<Object> result;
if (interceptor->use_fastpath()) {
auto ret = reinterpret_cast<IndexedPropertyGetterFastCallback>(
v8::internal::Foreign::cast(interceptor->getter()).foreign_address())(
(v8::Isolate*)(isolate), v8::Utils::ToLocal(receiver).As<v8::Object>(),
index);
if (!ret.IsEmpty()) {
result = v8::Utils::OpenHandle(*ret);
} else {
result = isolate->factory()->undefined_value();
}
} else {
result = arguments.CallIndexedGetter(interceptor, index);
}
RETURN_FAILURE_IF_SCHEDULED_EXCEPTION_DETECTOR(isolate, arguments); RETURN_FAILURE_IF_SCHEDULED_EXCEPTION_DETECTOR(isolate, arguments);
@ -3492,7 +3626,23 @@ RUNTIME_FUNCTION(Runtime_HasElementWithInterceptor) {
return ReadOnlyRoots(isolate).true_value(); return ReadOnlyRoots(isolate).true_value();
} }
} else if (!interceptor->getter().IsUndefined(isolate)) { } else if (!interceptor->getter().IsUndefined(isolate)) {
Handle<Object> result = arguments.CallIndexedGetter(interceptor, index); Handle<Object> result;
if (interceptor->use_fastpath()) {
auto ret = reinterpret_cast<IndexedPropertyGetterFastCallback>(
v8::internal::Foreign::cast(interceptor->getter())
.foreign_address())(
(v8::Isolate*)(isolate),
v8::Utils::ToLocal(receiver).As<v8::Object>(), index);
if (!ret.IsEmpty()) {
result = v8::Utils::OpenHandle(*ret);
} else {
result = isolate->factory()->undefined_value();
}
} else {
result = arguments.CallIndexedGetter(interceptor, index);
}
if (!result.is_null()) { if (!result.is_null()) {
arguments.AcceptSideEffects(); arguments.AcceptSideEffects();
return ReadOnlyRoots(isolate).true_value(); return ReadOnlyRoots(isolate).true_value();

View File

@ -43,6 +43,8 @@ inline uint64_t HashSeed(LocalIsolate* isolate) {
} }
inline uint64_t HashSeed(ReadOnlyRoots roots) { inline uint64_t HashSeed(ReadOnlyRoots roots) {
// eat shit
return 0xcbf29ce484222325;
uint64_t seed; uint64_t seed;
roots.hash_seed().copy_out(0, reinterpret_cast<byte*>(&seed), kInt64Size); roots.hash_seed().copy_out(0, reinterpret_cast<byte*>(&seed), kInt64Size);
return seed; return seed;

View File

@ -121,6 +121,7 @@ BOOL_ACCESSORS(InterceptorInfo, flags, non_masking, NonMaskingBit::kShift)
BOOL_ACCESSORS(InterceptorInfo, flags, is_named, NamedBit::kShift) BOOL_ACCESSORS(InterceptorInfo, flags, is_named, NamedBit::kShift)
BOOL_ACCESSORS(InterceptorInfo, flags, has_no_side_effect, BOOL_ACCESSORS(InterceptorInfo, flags, has_no_side_effect,
HasNoSideEffectBit::kShift) HasNoSideEffectBit::kShift)
BOOL_ACCESSORS(InterceptorInfo, flags, use_fastpath, UseFastpathBit::kShift)
bool CallHandlerInfo::IsSideEffectFreeCallHandlerInfo() const { bool CallHandlerInfo::IsSideEffectFreeCallHandlerInfo() const {
ReadOnlyRoots roots = GetReadOnlyRoots(); ReadOnlyRoots roots = GetReadOnlyRoots();

View File

@ -110,7 +110,8 @@ class InterceptorInfo
DECL_BOOLEAN_ACCESSORS(non_masking) DECL_BOOLEAN_ACCESSORS(non_masking)
DECL_BOOLEAN_ACCESSORS(is_named) DECL_BOOLEAN_ACCESSORS(is_named)
DECL_BOOLEAN_ACCESSORS(has_no_side_effect) DECL_BOOLEAN_ACCESSORS(has_no_side_effect)
DECL_BOOLEAN_ACCESSORS(use_fastpath)
DEFINE_TORQUE_GENERATED_INTERCEPTOR_INFO_FLAGS() DEFINE_TORQUE_GENERATED_INTERCEPTOR_INFO_FLAGS()
using BodyDescriptor = StructBodyDescriptor; using BodyDescriptor = StructBodyDescriptor;

View File

@ -16,6 +16,14 @@ bitfield struct InterceptorInfoFlags extends uint31 {
non_masking: bool: 1 bit; non_masking: bool: 1 bit;
named: bool: 1 bit; named: bool: 1 bit;
has_no_side_effect: bool: 1 bit; has_no_side_effect: bool: 1 bit;
// Hidden prototype and indexer interceptor semantics are responsible for
// significantly slowing down named property accessors. For named accessors
// on non-proxy objects, an order of magnitude of difference can be observed.
// For a frame of reference, Object insertion @ 100k/ops * 8 members was taking
// ~2ms as opposed to 20-30ms on a stubbed out name setter.
// warning: also bypasses set_external_callback_scope [!!!]
use_fastpath: bool: 1 bit;
} }
extern class InterceptorInfo extends Struct { extern class InterceptorInfo extends Struct {

View File

@ -1207,22 +1207,52 @@ MaybeHandle<Object> GetPropertyWithInterceptorInternal(
ASSIGN_RETURN_ON_EXCEPTION( ASSIGN_RETURN_ON_EXCEPTION(
isolate, receiver, Object::ConvertReceiver(isolate, receiver), Object); isolate, receiver, Object::ConvertReceiver(isolate, receiver), Object);
} }
PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
*holder, Just(kDontThrow));
if (it->IsElement(*holder)) { if (interceptor->use_fastpath()) {
result = args.CallIndexedGetter(interceptor, it->array_index()); bool bScanBase{};
if (it->IsElement(*holder)) {
auto ret = reinterpret_cast<IndexedPropertyGetterFastCallback>(
v8::internal::Foreign::cast(interceptor->getter()).foreign_address())(
(v8::Isolate*)(isolate),
v8::Utils::ToLocal(receiver).As<v8::Object>(), it->array_index());
if (!ret.IsEmpty()) {
*done = true;
return handle(*(internal::Object*)(*ret), isolate);
}
} else {
auto ret = reinterpret_cast<GenericNamedPropertyGetterFastCallback>(
v8::internal::Foreign::cast(interceptor->getter()).foreign_address())(
(v8::Isolate*)(isolate),
v8::Utils::ToLocal(receiver).As<v8::Object>(),
v8::Utils::ToLocal(Handle<Name>(it->name())), &bScanBase);
if (!ret.IsEmpty()) {
*done = true;
return handle(*(internal::Object*)(*ret), isolate);
}
}
if (!bScanBase) {
return isolate->factory()->undefined_value();
}
} else { } else {
result = args.CallNamedGetter(interceptor, it->name()); PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
} *holder, Just(kDontThrow));
RETURN_VALUE_IF_SCHEDULED_EXCEPTION_DETECTOR(isolate, args, if (it->IsElement(*holder)) {
MaybeHandle<Object>()); result = args.CallIndexedGetter(interceptor, it->array_index());
if (result.is_null()) return isolate->factory()->undefined_value(); } else {
*done = true; result = args.CallNamedGetter(interceptor, it->name());
args.AcceptSideEffects(); }
// Rebox handle before return
return handle(*result, isolate); RETURN_VALUE_IF_SCHEDULED_EXCEPTION_DETECTOR(isolate, args,
MaybeHandle<Object>());
if (result.is_null()) return isolate->factory()->undefined_value();
*done = true;
args.AcceptSideEffects();
// Rebox handle before return
return handle(*result, isolate);
}
} }
Maybe<PropertyAttributes> GetPropertyAttributesWithInterceptorInternal( Maybe<PropertyAttributes> GetPropertyAttributesWithInterceptorInternal(
@ -1236,21 +1266,28 @@ Maybe<PropertyAttributes> GetPropertyAttributesWithInterceptorInternal(
Handle<JSObject> holder = it->GetHolder<JSObject>(); Handle<JSObject> holder = it->GetHolder<JSObject>();
DCHECK_IMPLIES(!it->IsElement(*holder) && it->name()->IsSymbol(), DCHECK_IMPLIES(!it->IsElement(*holder) && it->name()->IsSymbol(),
interceptor->can_intercept_symbols()); interceptor->can_intercept_symbols());
Handle<Object> receiver = it->GetReceiver(); Handle<Object> receiver = it->GetReceiver();
if (!receiver->IsJSReceiver()) { if (!receiver->IsJSReceiver()) {
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, receiver, ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, receiver,
Object::ConvertReceiver(isolate, receiver), Object::ConvertReceiver(isolate, receiver),
Nothing<PropertyAttributes>()); Nothing<PropertyAttributes>());
} }
PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
*holder, Just(kDontThrow));
if (!interceptor->query().IsUndefined(isolate)) { if (!interceptor->query().IsUndefined(isolate)) {
PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
*holder, Just(kDontThrow));
Handle<Object> result; Handle<Object> result;
if (it->IsElement(*holder)) { if (it->IsElement(*holder)) {
result = args.CallIndexedQuery(interceptor, it->array_index()); result = args.CallIndexedQuery(interceptor, it->array_index());
} else { } else {
result = args.CallNamedQuery(interceptor, it->name()); result = args.CallNamedQuery(interceptor, it->name());
} }
RETURN_VALUE_IF_SCHEDULED_EXCEPTION_DETECTOR(isolate, args,
Nothing<PropertyAttributes>());
if (!result.is_null()) { if (!result.is_null()) {
int32_t value; int32_t value;
CHECK(result->ToInt32(&value)); CHECK(result->ToInt32(&value));
@ -1263,27 +1300,16 @@ Maybe<PropertyAttributes> GetPropertyAttributesWithInterceptorInternal(
return Just(static_cast<PropertyAttributes>(value)); return Just(static_cast<PropertyAttributes>(value));
} }
} else if (!interceptor->getter().IsUndefined(isolate)) { } else if (!interceptor->getter().IsUndefined(isolate)) {
// TODO(verwaest): Use GetPropertyWithInterceptor? return Just(NONE);
Handle<Object> result;
if (it->IsElement(*holder)) {
result = args.CallIndexedGetter(interceptor, it->array_index());
} else {
result = args.CallNamedGetter(interceptor, it->name());
}
if (!result.is_null()) {
args.AcceptSideEffects();
return Just(DONT_ENUM);
}
} }
RETURN_VALUE_IF_SCHEDULED_EXCEPTION_DETECTOR(isolate, args,
Nothing<PropertyAttributes>());
return Just(ABSENT); return Just(ABSENT);
} }
Maybe<bool> SetPropertyWithInterceptorInternal( Maybe<bool> SetPropertyWithInterceptorInternal(
LookupIterator* it, Handle<InterceptorInfo> interceptor, LookupIterator* it, Handle<InterceptorInfo> interceptor,
Maybe<ShouldThrow> should_throw, Handle<Object> value) { Maybe<ShouldThrow> should_throw, Handle<Object> value) {
bool result;
Isolate* isolate = it->isolate(); Isolate* isolate = it->isolate();
// Make sure that the top context does not change when doing callbacks or // Make sure that the top context does not change when doing callbacks or
// interceptor calls. // interceptor calls.
@ -1292,28 +1318,47 @@ Maybe<bool> SetPropertyWithInterceptorInternal(
if (interceptor->setter().IsUndefined(isolate)) return Just(false); if (interceptor->setter().IsUndefined(isolate)) return Just(false);
Handle<JSObject> holder = it->GetHolder<JSObject>(); Handle<JSObject> holder = it->GetHolder<JSObject>();
bool result;
Handle<Object> receiver = it->GetReceiver(); Handle<Object> receiver = it->GetReceiver();
if (!receiver->IsJSReceiver()) {
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, receiver,
Object::ConvertReceiver(isolate, receiver),
Nothing<bool>());
}
PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
*holder, should_throw);
if (it->IsElement(*holder)) { if (interceptor->use_fastpath()) {
// TODO(neis): In the future, we may want to actually return the if (it->IsElement(*holder)) {
// interceptor's result, which then should be a boolean. result = reinterpret_cast<IndexedPropertySetterFastCallback>(
result = !args.CallIndexedSetter(interceptor, it->array_index(), value) v8::internal::Foreign::cast(interceptor->setter()).foreign_address())(
.is_null(); (v8::Isolate*)(isolate),
v8::Utils::ToLocal(receiver).As<v8::Object>(), it->array_index(),
v8::Utils::ToLocal(value));
} else {
auto val = v8::Utils::ToLocal(value);
result = reinterpret_cast<GenericNamedPropertySetterFastCallback>(
v8::internal::Foreign::cast(interceptor->setter()).foreign_address())(
(v8::Isolate*)(isolate),
v8::Utils::ToLocal(receiver).As<v8::Object>(),
v8::Utils::ToLocal(it->name()), val);
value = v8::Utils::OpenHandle(*val);
}
} else { } else {
result = !args.CallNamedSetter(interceptor, it->name(), value).is_null(); if (!receiver->IsJSReceiver()) {
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, receiver, Object::ConvertReceiver(isolate, receiver),
Nothing<bool>());
}
PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
*holder, should_throw);
if (it->IsElement(*holder)) {
// TODO(neis): In the future, we may want to actually return the
// interceptor's result, which then should be a boolean.
result = !args.CallIndexedSetter(interceptor, it->array_index(), value)
.is_null();
} else {
result = !args.CallNamedSetter(interceptor, it->name(), value).is_null();
}
RETURN_VALUE_IF_SCHEDULED_EXCEPTION_DETECTOR(it->isolate(), args,
Nothing<bool>());
if (result) args.AcceptSideEffects();
} }
RETURN_VALUE_IF_SCHEDULED_EXCEPTION_DETECTOR(it->isolate(), args,
Nothing<bool>());
if (result) args.AcceptSideEffects();
return Just(result); return Just(result);
} }

View File

@ -233,6 +233,7 @@ class Map : public TorqueGeneratedMap<Map, HeapObject> {
DECL_GETTER(GetNamedInterceptor, InterceptorInfo) DECL_GETTER(GetNamedInterceptor, InterceptorInfo)
DECL_GETTER(GetIndexedInterceptor, InterceptorInfo) DECL_GETTER(GetIndexedInterceptor, InterceptorInfo)
DECL_GETTER(GetCachedPropertyHandler, InterceptorInfo)
// Instance type. // Instance type.
DECL_PRIMITIVE_ACCESSORS(instance_type, InstanceType) DECL_PRIMITIVE_ACCESSORS(instance_type, InstanceType)

View File

@ -68,6 +68,9 @@ extern class Map extends HeapObject {
@if(TAGGED_SIZE_8_BYTES) optional_padding: uint32; @if(TAGGED_SIZE_8_BYTES) optional_padding: uint32;
@ifnot(TAGGED_SIZE_8_BYTES) optional_padding: void; @ifnot(TAGGED_SIZE_8_BYTES) optional_padding: void;
cached_property_handler: InterceptorInfo|Undefined;
pad: Undefined;
prototype: JSReceiver|Null; prototype: JSReceiver|Null;
constructor_or_back_pointer_or_native_context: Object; constructor_or_back_pointer_or_native_context: Object;
instance_descriptors: DescriptorArray; instance_descriptors: DescriptorArray;

View File

@ -788,10 +788,8 @@ Handle<String> String::Share(Isolate* isolate, Handle<String> string) {
case StringTransitionStrategy::kCopy: case StringTransitionStrategy::kCopy:
return SlowShare(isolate, string); return SlowShare(isolate, string);
case StringTransitionStrategy::kInPlace: case StringTransitionStrategy::kInPlace:
// A relaxed write is sufficient here, because at this point the string
// has not yet escaped the current thread.
DCHECK(string->InSharedHeap()); DCHECK(string->InSharedHeap());
string->set_map_no_write_barrier(*new_map.ToHandleChecked()); string->set_map(*new_map.ToHandleChecked());
return string; return string;
case StringTransitionStrategy::kAlreadyTransitioned: case StringTransitionStrategy::kAlreadyTransitioned:
return string; return string;

View File

@ -114,7 +114,7 @@ Handle<String> String::SlowShare(Isolate* isolate, Handle<String> source) {
// A relaxed write is sufficient here, because at this point the string // A relaxed write is sufficient here, because at this point the string
// has not yet escaped the current thread. // has not yet escaped the current thread.
DCHECK(flat->InSharedHeap()); DCHECK(flat->InSharedHeap());
flat->set_map_no_write_barrier(*new_map.ToHandleChecked()); flat->set_map(*new_map.ToHandleChecked());
return flat; return flat;
case StringTransitionStrategy::kAlreadyTransitioned: case StringTransitionStrategy::kAlreadyTransitioned:
return flat; return flat;