| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 3839c4a commit 784431d
6 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -38,7 +38,7 @@ | |||
| 38 | 38 | ||
| 39 | 39 | # Reset this number to 0 on major V8 upgrades. | |
| 40 | 40 | # Increment by one for each non-official patch applied to deps/v8. | |
| 41 | - 'v8_embedder_string': '-node.14', | ||
| 41 | + 'v8_embedder_string': '-node.15', | ||
| 42 | 42 | ||
| 43 | 43 | ##### V8 defaults for Node.js ##### | |
| 44 | 44 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -336,9 +336,24 @@ class V8_EXPORT Object : public Value { | |||
| 336 | 336 | * Gets the property attributes of a property which can be None or | |
| 337 | 337 | * any combination of ReadOnly, DontEnum and DontDelete. Returns | |
| 338 | 338 | * None when the property doesn't exist. | |
| 339 | + * | ||
| 340 | + * This method will be deprecated soon, since it doesn't provide a way | ||
| 341 | + * to return "property does not exist" result. Use GetPropertyAttributes with | ||
| 342 | + * PropertyAttribute* instead. | ||
| 339 | 343 | */ | |
| 340 | 344 | V8_WARN_UNUSED_RESULT Maybe<PropertyAttribute> GetPropertyAttributes( | |
| 341 | 345 | Local<Context> context, Local<Value> key); | |
| 346 | + /** | ||
| 347 | + * Gets the property attributes of a property which can be None or | ||
| 348 | + * any combination of ReadOnly, DontEnum and DontDelete. | ||
| 349 | + * | ||
| 350 | + * Returns true and sets *out_attributes if the property exists, false if | ||
| 351 | + * not or empty Maybe if an exception is thrown. In the latter two cases, | ||
| 352 | + * the value of *out_attributes is not modified. | ||
| 353 | + */ | ||
| 354 | + V8_WARN_UNUSED_RESULT Maybe<bool> GetPropertyAttributes( | ||
| 355 | + Local<Context> context, Local<Value> key, | ||
| 356 | + PropertyAttribute* out_attributes); | ||
| 342 | 357 | ||
| 343 | 358 | /** | |
| 344 | 359 | * Implements Object.getOwnPropertyDescriptor(O, P), see | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4752,6 +4752,16 @@ MaybeLocal<Value> v8::Object::GetPrivate(Local<Context> context, | |||
| 4752 | 4752 | ||
| 4753 | 4753 | Maybe<PropertyAttribute> v8::Object::GetPropertyAttributes( | |
| 4754 | 4754 | Local<Context> context, Local<Value> key) { | |
| 4755 | + PropertyAttribute attributes = PropertyAttribute::None; | ||
| 4756 | + auto result = GetPropertyAttributes(context, key, &attributes); | ||
| 4757 | + if (result.IsNothing()) return {}; | ||
| 4758 | + // This will confusingly return None when the property doesn't exist. | ||
| 4759 | + return Just(attributes); | ||
| 4760 | + } | ||
| 4761 | + | ||
| 4762 | + Maybe<bool> v8::Object::GetPropertyAttributes( | ||
| 4763 | + Local<Context> context, Local<Value> key, | ||
| 4764 | + PropertyAttribute* out_attributes) { | ||
| 4755 | 4765 | auto i_isolate = i::Isolate::Current(); | |
| 4756 | 4766 | EnterV8Scope<> api_scope{i_isolate, context, | |
| 4757 | 4767 | RCCId::kAPI_Object_GetPropertyAttributes}; | |
@@ -4764,9 +4774,10 @@ Maybe<PropertyAttribute> v8::Object::GetPropertyAttributes( | |||
| 4764 | 4774 | auto result = i::JSReceiver::GetPropertyAttributes(i_isolate, self, key_name); | |
| 4765 | 4775 | if (result.IsNothing()) return {}; | |
| 4766 | 4776 | if (result.FromJust() == i::ABSENT) { | |
| 4767 | - return Just(static_cast<PropertyAttribute>(i::NONE)); | ||
| 4777 | + return Just(false); | ||
| 4768 | 4778 | } | |
| 4769 | - return Just(static_cast<PropertyAttribute>(result.FromJust())); | ||
| 4779 | + *out_attributes = static_cast<PropertyAttribute>(result.FromJust()); | ||
| 4780 | + return Just(true); | ||
| 4770 | 4781 | } | |
| 4771 | 4782 | ||
| 4772 | 4783 | MaybeLocal<Value> v8::Object::GetOwnPropertyDescriptor(Local<Context> context, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1813,6 +1813,8 @@ bool StoreIC::LookupForWrite(LookupIterator* it, DirectHandle<Object> value, | |||
| 1813 | 1813 | case LookupIterator::NOT_FOUND: | |
| 1814 | 1814 | // If we are in StoreGlobal then check if we should throw on | |
| 1815 | 1815 | // non-existent properties. | |
| 1816 | + // TODO(ishell): make store global ic kind reliable, currently it is | ||
| 1817 | + // not if the feedback vector is not available. | ||
| 1816 | 1818 | if (IsStoreGlobalIC() && | |
| 1817 | 1819 | (GetShouldThrow(it->isolate(), Nothing<ShouldThrow>()) == | |
| 1818 | 1820 | ShouldThrow::kThrowOnError)) { | |
@@ -2084,6 +2086,8 @@ MaybeDirectHandle<Object> StoreIC::Store(Handle<JSAny> object, | |||
| 2084 | 2086 | Nothing<ShouldThrow>(), store_origin)); | |
| 2085 | 2087 | } | |
| 2086 | 2088 | } else { | |
| 2089 | + // TODO(ishell): deduce should throw from IC kind once it's reliable, | ||
| 2090 | + // currently it is not if the feedback vector is not available. | ||
| 2087 | 2091 | MAYBE_RETURN_NULL(Object::SetProperty(&it, value, store_origin)); | |
| 2088 | 2092 | } | |
| 2089 | 2093 | return value; | |
@@ -2166,6 +2170,20 @@ MaybeObjectHandle StoreIC::ComputeHandler(LookupIterator* lookup) { | |||
| 2166 | 2170 | isolate()); | |
| 2167 | 2171 | ||
| 2168 | 2172 | if (lookup->HolderIsReceiverOrHiddenPrototype()) { | |
| 2173 | + // TODO(ishell): make store global ic kind reliable, currently it is | ||
| 2174 | + // not if the feedback vector is not available. | ||
| 2175 | + if (IsStoreGlobalIC() && | ||
| 2176 | + (GetShouldThrow(isolate(), Nothing<ShouldThrow>()) == | ||
| 2177 | + ShouldThrow::kThrowOnError)) { | ||
| 2178 | + DCHECK(IsJSGlobalObject(*lookup->GetHolder<Object>())); | ||
| 2179 | + // This is a contextual store to an interceptor object. Use slow | ||
| 2180 | + // handler because before calling the setter callback we need to | ||
| 2181 | + // check whether the property exists. See | ||
| 2182 | + // https://tc39.es/ecma262/#sec-object-environment-records-setmutablebinding-n-v-s | ||
| 2183 | + Handle<Smi> smi_handler = StoreHandler::StoreSlow(isolate()); | ||
| 2184 | + return MaybeObjectHandle(smi_handler); | ||
| 2185 | + } | ||
| 2186 | + | ||
| 2169 | 2187 | if (info->has_setter() && !IsAnyDefineOwn()) { | |
| 2170 | 2188 | TRACE_HANDLER_STATS(isolate(), StoreIC_StoreInterceptorDH); | |
| 2171 | 2189 | if (info->non_masking()) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2382,6 +2382,28 @@ Maybe<bool> Object::SetPropertyInternal(LookupIterator* it, | |||
| 2382 | 2382 | ||
| 2383 | 2383 | case LookupIterator::INTERCEPTOR: { | |
| 2384 | 2384 | if (it->HolderIsReceiverOrHiddenPrototype()) { | |
| 2385 | + // In case we are executing contextual store to a global object with | ||
| 2386 | + // an interceptor in strict mode, we need to check that the property | ||
| 2387 | + // actually exists before calling the setter. See | ||
| 2388 | + // https://tc39.es/ecma262/#sec-object-environment-records-setmutablebinding-n-v-s | ||
| 2389 | + if (IsJSGlobalObject(*it->GetReceiver())) { | ||
| 2390 | + Isolate* isolate = it->isolate(); | ||
| 2391 | + auto should_throw_value = GetShouldThrow(isolate, should_throw); | ||
| 2392 | + should_throw = Just(should_throw_value); | ||
| 2393 | + if (should_throw_value == kThrowOnError) { | ||
| 2394 | + Maybe<PropertyAttributes> maybe_attributes = | ||
| 2395 | + JSObject::GetPropertyAttributesWithInterceptor(it); | ||
| 2396 | + if (maybe_attributes.IsNothing()) return Nothing<bool>(); | ||
| 2397 | + if ((maybe_attributes.FromJust() & READ_ONLY) != 0) { | ||
| 2398 | + return WriteToReadOnlyProperty(it, value, should_throw); | ||
| 2399 | + } | ||
| 2400 | + if (maybe_attributes.FromJust() == ABSENT) { | ||
| 2401 | + // Interceptor doesn't have the property, continue lookup. | ||
| 2402 | + continue; | ||
| 2403 | + } | ||
| 2404 | + } | ||
| 2405 | + } | ||
| 2406 | + | ||
| 2385 | 2407 | InterceptorResult result; | |
| 2386 | 2408 | if (!JSObject::SetPropertyWithInterceptor(it, should_throw, value) | |
| 2387 | 2409 | .To(&result)) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -199,7 +199,7 @@ v8::Intercepted GenericInterceptorGetter( | |||
| 199 | 199 | Local<String> name = generic_name.As<String>(); | |
| 200 | 200 | String::Utf8Value utf8(isolate, name); | |
| 201 | 201 | char* name_str = *utf8; | |
| 202 | - if (*name_str == '_') return v8::Intercepted::kNo; | ||
| 202 | + if (*name_str != '$') return v8::Intercepted::kNo; | ||
| 203 | 203 | str = String::Concat(isolate, v8_str("_str_"), name); | |
| 204 | 204 | } | |
| 205 | 205 | ||
@@ -209,6 +209,35 @@ v8::Intercepted GenericInterceptorGetter( | |||
| 209 | 209 | return v8::Intercepted::kYes; | |
| 210 | 210 | } | |
| 211 | 211 | ||
| 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 | + | ||
| 212 | 241 | v8::Intercepted GenericInterceptorSetter( | |
| 213 | 242 | Local<Name> generic_name, Local<Value> value, | |
| 214 | 243 | const v8::PropertyCallbackInfo<void>& info) { | |
@@ -222,7 +251,7 @@ v8::Intercepted GenericInterceptorSetter( | |||
| 222 | 251 | Local<String> name = generic_name.As<String>(); | |
| 223 | 252 | String::Utf8Value utf8(info.GetIsolate(), name); | |
| 224 | 253 | char* name_str = *utf8; | |
| 225 | - if (*name_str == '_') return v8::Intercepted::kNo; | ||
| 254 | + if (*name_str != '$') return v8::Intercepted::kNo; | ||
| 226 | 255 | str = String::Concat(info.GetIsolate(), v8_str("_str_"), name); | |
| 227 | 256 | } | |
| 228 | 257 | ||
@@ -1632,9 +1661,9 @@ void InterceptorLoadICGlobalWithInterceptor(bool masking) { | |||
| 1632 | 1661 | ExpectInt32( | |
| 1633 | 1662 | "(function() {" | |
| 1634 | 1663 | " var f = function(obj) { " | |
| 1635 | - " return obj.foo;" | ||
| 1664 | + " return obj.$foo;" | ||
| 1636 | 1665 | " };" | |
| 1637 | - " this._str_foo = 42;" | ||
| 1666 | + " this._str_$foo = 42;" | ||
| 1638 | 1667 | " var obj = { __proto__: this };" | |
| 1639 | 1668 | " for (var i = 0; i < 1500; i++) obj['p' + i] = 0;" | |
| 1640 | 1669 | " /* Ensure that |obj| is in dictionary mode. */" | |
@@ -2061,6 +2090,138 @@ THREADED_TEST(EmptyInterceptorVsStoreGlobalICs) { | |||
| 2061 | 2090 | 120); | |
| 2062 | 2091 | } | |
| 2063 | 2092 | ||
| 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 | + | ||
| 2064 | 2225 | THREADED_TEST(LegacyInterceptorDoesNotSeeSymbols) { | |
| 2065 | 2226 | LocalContext env; | |
| 2066 | 2227 | v8::Isolate* isolate = CcTest::isolate(); | |
@@ -2113,9 +2274,9 @@ THREADED_TEST(GenericInterceptorDoesSeeSymbols) { | |||
| 2113 | 2274 | ExpectInt32("child._sym_age", 10); | |
| 2114 | 2275 | ||
| 2115 | 2276 | // 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); | ||
| 2119 | 2280 | ||
| 2120 | 2281 | // Check that the interceptor can punt (in this case, on anonymous symbols). | |
| 2121 | 2282 | CompileRun("child[anon] = 31337"); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments