@@ -199,7 +199,7 @@ v8::Intercepted GenericInterceptorGetter(
199199 Local<String> name = generic_name.As <String>();
200200 String::Utf8Value utf8 (isolate, name);
201201 char * name_str = *utf8;
202- if (*name_str == ' _ ' ) return v8::Intercepted::kNo ;
202+ if (*name_str != ' $ ' ) return v8::Intercepted::kNo ;
203203 str = String::Concat (isolate, v8_str (" _str_" ), name);
204204 }
205205
@@ -209,6 +209,35 @@ v8::Intercepted GenericInterceptorGetter(
209209 return v8::Intercepted::kYes ;
210210}
211211
212+ v8::Intercepted GenericInterceptorQuery (
213+ Local<Name> generic_name,
214+ const v8::PropertyCallbackInfo<v8::Integer>& info) {
215+ v8::Isolate* isolate = info.GetIsolate ();
216+ Local<String> str;
217+ if (generic_name->IsSymbol ()) {
218+ Local<Value> name = generic_name.As <Symbol>()->Description (isolate);
219+ if (name->IsUndefined ()) return v8::Intercepted::kNo ;
220+ str = String::Concat (isolate, v8_str (" _sym_" ), name.As <String>());
221+ } else {
222+ Local<String> name = generic_name.As <String>();
223+ String::Utf8Value utf8 (isolate, name);
224+ char * name_str = *utf8;
225+ if (*name_str != ' $' ) return v8::Intercepted::kNo ;
226+ str = String::Concat (isolate, v8_str (" _str_" ), name);
227+ }
228+
229+ Local<Object> self = info.HolderV2 ();
230+ v8::PropertyAttribute attributes;
231+ bool exists;
232+ if (self->GetPropertyAttributes (isolate->GetCurrentContext (), str,
233+ &attributes)
234+ .To (&exists)) {
235+ if (!exists) return v8::Intercepted::kNo ;
236+ info.GetReturnValue ().Set (attributes);
237+ }
238+ return v8::Intercepted::kYes ;
239+ }
240+
212241v8::Intercepted GenericInterceptorSetter (
213242 Local<Name> generic_name, Local<Value> value,
214243 const v8::PropertyCallbackInfo<void >& info) {
@@ -222,7 +251,7 @@ v8::Intercepted GenericInterceptorSetter(
222251 Local<String> name = generic_name.As <String>();
223252 String::Utf8Value utf8 (info.GetIsolate (), name);
224253 char * name_str = *utf8;
225- if (*name_str == ' _ ' ) return v8::Intercepted::kNo ;
254+ if (*name_str != ' $ ' ) return v8::Intercepted::kNo ;
226255 str = String::Concat (info.GetIsolate (), v8_str (" _str_" ), name);
227256 }
228257
@@ -1632,9 +1661,9 @@ void InterceptorLoadICGlobalWithInterceptor(bool masking) {
16321661 ExpectInt32 (
16331662 " (function() {"
16341663 " var f = function(obj) { "
1635- " return obj.foo;"
1664+ " return obj.$ foo;"
16361665 " };"
1637- " this._str_foo = 42;"
1666+ " this._str_$foo = 42;"
16381667 " var obj = { __proto__: this };"
16391668 " for (var i = 0; i < 1500; i++) obj['p' + i] = 0;"
16401669 " /* Ensure that |obj| is in dictionary mode. */"
@@ -2061,6 +2090,138 @@ THREADED_TEST(EmptyInterceptorVsStoreGlobalICs) {
20612090 120 );
20622091}
20632092
2093+ namespace {
2094+
2095+ void CheckGlobalInterceptorIC (v8::NamedPropertyGetterCallback getter,
2096+ v8::NamedPropertySetterCallback setter,
2097+ v8::NamedPropertyQueryCallback query,
2098+ v8::PropertyHandlerFlags flags,
2099+ const char * source, std::optional<int > expected) {
2100+ v8::Isolate* isolate = CcTest::isolate ();
2101+ v8::HandleScope scope (isolate);
2102+ v8::Local<v8::ObjectTemplate> templ_global = v8::ObjectTemplate::New (isolate);
2103+ v8::NamedPropertyHandlerConfiguration config (
2104+ getter, setter, query, nullptr /* deleter */ , nullptr /* enumerator */ ,
2105+ nullptr /* definer */ , nullptr /* descriptor */ , {}, flags);
2106+ templ_global->SetHandler (config);
2107+
2108+ LocalContext context (nullptr , templ_global);
2109+ i::DirectHandle<i::JSReceiver> global_proxy =
2110+ v8::Utils::OpenDirectHandle<Object, i::JSReceiver>(context->Global ());
2111+ CHECK (IsJSGlobalProxy (*global_proxy));
2112+ i::DirectHandle<i::JSGlobalObject> global (
2113+ i::Cast<i::JSGlobalObject>(global_proxy->map ()->prototype ()),
2114+ CcTest::i_isolate ());
2115+ CHECK (global->map ()->has_named_interceptor ());
2116+
2117+ v8::Local<Value> value = CompileRun (source);
2118+ if (expected) {
2119+ CHECK_EQ (*expected, value->Int32Value (context.local ()).FromJust ());
2120+ } else {
2121+ CHECK (value.IsEmpty ());
2122+ }
2123+ }
2124+
2125+ void StoreGlobalICWithGlobalInterceptor (bool masking) {
2126+ v8::PropertyHandlerFlags flags = v8::PropertyHandlerFlags::kNone ;
2127+ if (!masking) {
2128+ flags = v8::PropertyHandlerFlags::kNonMasking ;
2129+ }
2130+
2131+ // In sloppy mode storing to global must succeed.
2132+ CheckGlobalInterceptorIC ( //
2133+ GenericInterceptorGetter, //
2134+ GenericInterceptorSetter, //
2135+ GenericInterceptorQuery, //
2136+ flags, //
2137+ R"(
2138+ // Make interceptor behave like it has a read-only property "$y".
2139+ Object.defineProperty(globalThis, '_str_$y',
2140+ {value: 153, writable: false});
2141+
2142+ let result = 0;
2143+
2144+ result += (typeof($x) === 'undefined' ? 0 : 10000);
2145+ result += ($y === 153 ? 0 : 10000);
2146+
2147+ for (var i = 0; i < 20; i++) {
2148+ try {
2149+ $x = i; // not intercepted, absent variable
2150+ result++;
2151+ } catch (e) {
2152+ }
2153+ }
2154+ for (var i = 0; i < 20; i++) {
2155+ try {
2156+ $y = i; // intercepted, read only property
2157+ result++;
2158+ } catch (e) {
2159+ }
2160+ }
2161+
2162+ // Check contextual stores succeeded.
2163+ result += ($x === 19 ? 0 : 100);
2164+ result += ($y === 153 ? 0 : 1000);
2165+
2166+ result
2167+ )" ,
2168+ 40 );
2169+
2170+ // In strict mode storing to global must throw.
2171+ CheckGlobalInterceptorIC ( //
2172+ GenericInterceptorGetter, //
2173+ GenericInterceptorSetter, //
2174+ GenericInterceptorQuery, //
2175+ flags,
2176+ R"(
2177+ 'use strict';
2178+
2179+ // Make interceptor behave like it has a read-only property "$y".
2180+ Object.defineProperty(globalThis, '_str_$y',
2181+ {value: 153, writable: false});
2182+
2183+ let result = 0;
2184+
2185+ result += (typeof($x) === 'undefined' ? 0 : 10000);
2186+ result += ($y === 153 ? 0 : 10000);
2187+
2188+ for (var i = 0; i < 20; i++) {
2189+ try {
2190+ $x = i; // not intercepted, absent variable
2191+ } catch (e) {
2192+ result++;
2193+ }
2194+ }
2195+ for (var i = 0; i < 20; i++) {
2196+ try {
2197+ $y = i; // intercepted, read only property
2198+ } catch (e) {
2199+ result++;
2200+ }
2201+ }
2202+
2203+ // Check contextual stores did not happen.
2204+ result += (typeof($x) === 'undefined' ? 0 : 100);
2205+ result += ($y === 153 ? 0 : 1000);
2206+
2207+ result
2208+ )" ,
2209+ 40 );
2210+ }
2211+
2212+ } // namespace
2213+
2214+ // Checks correctness of global objects with interceptor vs contextual stores.
2215+ THREADED_TEST (StoreGlobalICWithGlobalNonMaskingInterceptor) {
2216+ const bool masking = false ;
2217+ StoreGlobalICWithGlobalInterceptor (masking);
2218+ }
2219+
2220+ THREADED_TEST (StoreGlobalICWithGlobalMaskingInterceptor) {
2221+ const bool masking = true ;
2222+ StoreGlobalICWithGlobalInterceptor (masking);
2223+ }
2224+
20642225THREADED_TEST (LegacyInterceptorDoesNotSeeSymbols) {
20652226 LocalContext env;
20662227 v8::Isolate* isolate = CcTest::isolate ();
@@ -2113,9 +2274,9 @@ THREADED_TEST(GenericInterceptorDoesSeeSymbols) {
21132274 ExpectInt32 (" child._sym_age" , 10 );
21142275
21152276 // Check that it also sees strings.
2116- CompileRun (" child.foo = 47" );
2117- ExpectInt32 (" child.foo" , 47 );
2118- ExpectInt32 (" child._str_foo " , 47 );
2277+ CompileRun (" child.$ foo = 47" );
2278+ ExpectInt32 (" child.$ foo" , 47 );
2279+ ExpectInt32 (" child._str_$foo " , 47 );
21192280
21202281 // Check that the interceptor can punt (in this case, on anonymous symbols).
21212282 CompileRun (" child[anon] = 31337" );
0 commit comments