| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent a92ecf0 commit 6b115d7
8 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -11,7 +11,7 @@ | |||
| 11 | 11 | #define V8_MAJOR_VERSION 8 | |
| 12 | 12 | #define V8_MINOR_VERSION 4 | |
| 13 | 13 | #define V8_BUILD_NUMBER 371 | |
| 14 | - #define V8_PATCH_LEVEL 19 | ||
| 14 | + #define V8_PATCH_LEVEL 23 | ||
| 15 | 15 | ||
| 16 | 16 | // Use 1 for candidates and 0 otherwise. | |
| 17 | 17 | // (Boolean macro values are not supported by all preprocessors.) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -82,7 +82,8 @@ const kPropertyArrayHashFieldMax: constexpr int31 | |||
| 82 | 82 | ||
| 83 | 83 | transitioning macro PromiseAllResolveElementClosure<F: type>( | |
| 84 | 84 | implicit context: Context)( | |
| 85 | - value: JSAny, function: JSFunction, wrapResultFunctor: F): JSAny { | ||
| 85 | + value: JSAny, function: JSFunction, wrapResultFunctor: F, | ||
| 86 | + hasResolveAndRejectClosures: constexpr bool): JSAny { | ||
| 86 | 87 | // We use the {function}s context as the marker to remember whether this | |
| 87 | 88 | // resolve element closure was already called. It points to the resolve | |
| 88 | 89 | // element context (which is a FunctionContext) until it was called the | |
@@ -98,10 +99,6 @@ transitioning macro PromiseAllResolveElementClosure<F: type>( | |||
| 98 | 99 | const nativeContext = LoadNativeContext(context); | |
| 99 | 100 | function.context = nativeContext; | |
| 100 | 101 | ||
| 101 | - // Update the value depending on whether Promise.all or | ||
| 102 | - // Promise.allSettled is called. | ||
| 103 | - const updatedValue = wrapResultFunctor.Call(nativeContext, value); | ||
| 104 | - | ||
| 105 | 102 | // Determine the index from the {function}. | |
| 106 | 103 | assert(kPropertyArrayNoHashSentinel == 0); | |
| 107 | 104 | const identityHash = | |
@@ -116,13 +113,41 @@ transitioning macro PromiseAllResolveElementClosure<F: type>( | |||
| 116 | 113 | const elements = UnsafeCast<FixedArray>(valuesArray.elements); | |
| 117 | 114 | const valuesLength = Convert<intptr>(valuesArray.length); | |
| 118 | 115 | if (index < valuesLength) { | |
| 119 | - // The {index} is in bounds of the {values_array}, | ||
| 120 | - // just store the {value} and continue. | ||
| 116 | + // The {index} is in bounds of the {values_array}, check if this element has | ||
| 117 | + // already been resolved, and store the {value} if not. | ||
| 118 | + // | ||
| 119 | + // Promise.allSettled, for each input element, has both a resolve and a | ||
| 120 | + // reject closure that share an [[AlreadyCalled]] boolean. That is, the | ||
| 121 | + // input element can only be settled once: after resolve is called, reject | ||
| 122 | + // returns early, and vice versa. Using {function}'s context as the marker | ||
| 123 | + // only tracks per-closure instead of per-element. When the second | ||
| 124 | + // resolve/reject closure is called on the same index, values.object[index] | ||
| 125 | + // will already exist and will not be the hole value. In that case, return | ||
| 126 | + // early. Everything up to this point is not yet observable to user code. | ||
| 127 | + // This is not a problem for Promise.all since Promise.all has a single | ||
| 128 | + // resolve closure (no reject) per element. | ||
| 129 | + if (hasResolveAndRejectClosures) { | ||
| 130 | + if (elements.objects[index] != TheHole) deferred { | ||
| 131 | + return Undefined; | ||
| 132 | + } | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + // Update the value depending on whether Promise.all or | ||
| 136 | + // Promise.allSettled is called. | ||
| 137 | + const updatedValue = wrapResultFunctor.Call(nativeContext, value); | ||
| 121 | 138 | elements.objects[index] = updatedValue; | |
| 122 | 139 | } else { | |
| 123 | 140 | // Check if we need to grow the backing store. | |
| 141 | + // | ||
| 142 | + // There's no need to check if this element has already been resolved for | ||
| 143 | + // Promise.allSettled if {values_array} has not yet grown to the index. | ||
| 124 | 144 | const newLength = index + 1; | |
| 125 | 145 | const elementsLength = elements.length_intptr; | |
| 146 | + | ||
| 147 | + // Update the value depending on whether Promise.all or | ||
| 148 | + // Promise.allSettled is called. | ||
| 149 | + const updatedValue = wrapResultFunctor.Call(nativeContext, value); | ||
| 150 | + | ||
| 126 | 151 | if (index < elementsLength) { | |
| 127 | 152 | // The {index} is within bounds of the {elements} backing store, so | |
| 128 | 153 | // just store the {value} and update the "length" of the {values_array}. | |
@@ -166,22 +191,22 @@ PromiseAllResolveElementClosure( | |||
| 166 | 191 | js-implicit context: Context, receiver: JSAny, | |
| 167 | 192 | target: JSFunction)(value: JSAny): JSAny { | |
| 168 | 193 | return PromiseAllResolveElementClosure( | |
| 169 | - value, target, PromiseAllWrapResultAsFulfilledFunctor{}); | ||
| 194 | + value, target, PromiseAllWrapResultAsFulfilledFunctor{}, false); | ||
| 170 | 195 | } | |
| 171 | 196 | ||
| 172 | 197 | transitioning javascript builtin | |
| 173 | 198 | PromiseAllSettledResolveElementClosure( | |
| 174 | 199 | js-implicit context: Context, receiver: JSAny, | |
| 175 | 200 | target: JSFunction)(value: JSAny): JSAny { | |
| 176 | 201 | return PromiseAllResolveElementClosure( | |
| 177 | - value, target, PromiseAllSettledWrapResultAsFulfilledFunctor{}); | ||
| 202 | + value, target, PromiseAllSettledWrapResultAsFulfilledFunctor{}, true); | ||
| 178 | 203 | } | |
| 179 | 204 | ||
| 180 | 205 | transitioning javascript builtin | |
| 181 | 206 | PromiseAllSettledRejectElementClosure( | |
| 182 | 207 | js-implicit context: Context, receiver: JSAny, | |
| 183 | 208 | target: JSFunction)(value: JSAny): JSAny { | |
| 184 | 209 | return PromiseAllResolveElementClosure( | |
| 185 | - value, target, PromiseAllSettledWrapResultAsRejectedFunctor{}); | ||
| 210 | + value, target, PromiseAllSettledWrapResultAsRejectedFunctor{}, true); | ||
| 186 | 211 | } | |
| 187 | 212 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2531,6 +2531,11 @@ void MarkCompactCollector::ClearJSWeakRefs() { | |||
| 2531 | 2531 | matched_cell.set_unregister_token(undefined); | |
| 2532 | 2532 | }, | |
| 2533 | 2533 | gc_notify_updated_slot); | |
| 2534 | + // The following is necessary because in the case that weak_cell has | ||
| 2535 | + // already been popped and removed from the FinalizationRegistry, the call | ||
| 2536 | + // to JSFinalizationRegistry::RemoveUnregisterToken above will not find | ||
| 2537 | + // weak_cell itself to clear its unregister token. | ||
| 2538 | + weak_cell.set_unregister_token(undefined); | ||
| 2534 | 2539 | } else { | |
| 2535 | 2540 | // The unregister_token is alive. | |
| 2536 | 2541 | ObjectSlot slot = weak_cell.RawField(WeakCell::kUnregisterTokenOffset); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -122,11 +122,16 @@ template <void (Assembler::*op)(Register, Register, Register, SBit, Condition), | |||
| 122 | 122 | SBit, Condition)> | |
| 123 | 123 | inline void I64Binop(LiftoffAssembler* assm, LiftoffRegister dst, | |
| 124 | 124 | LiftoffRegister lhs, LiftoffRegister rhs) { | |
| 125 | - DCHECK_NE(dst.low_gp(), lhs.high_gp()); | ||
| 126 | - DCHECK_NE(dst.low_gp(), rhs.high_gp()); | ||
| 127 | - (assm->*op)(dst.low_gp(), lhs.low_gp(), rhs.low_gp(), SetCC, al); | ||
| 125 | + Register dst_low = dst.low_gp(); | ||
| 126 | + if (dst_low == lhs.high_gp() || dst_low == rhs.high_gp()) { | ||
| 127 | + dst_low = assm->GetUnusedRegister( | ||
| 128 | + kGpReg, LiftoffRegList::ForRegs(lhs, rhs, dst.high_gp())) | ||
| 129 | + .gp(); | ||
| 130 | + } | ||
| 131 | + (assm->*op)(dst_low, lhs.low_gp(), rhs.low_gp(), SetCC, al); | ||
| 128 | 132 | (assm->*op_with_carry)(dst.high_gp(), lhs.high_gp(), Operand(rhs.high_gp()), | |
| 129 | 133 | LeaveCC, al); | |
| 134 | + if (dst_low != dst.low_gp()) assm->mov(dst.low_gp(), dst_low); | ||
| 130 | 135 | } | |
| 131 | 136 | ||
| 132 | 137 | template <void (Assembler::*op)(Register, Register, const Operand&, SBit, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -354,15 +354,6 @@ class LiftoffAssembler : public TurboAssembler { | |||
| 354 | 354 | LiftoffRegList candidates = kGpCacheRegList; | |
| 355 | 355 | Register low = pinned.set(GetUnusedRegister(candidates, pinned)).gp(); | |
| 356 | 356 | Register high = GetUnusedRegister(candidates, pinned).gp(); | |
| 357 | - if (low.code() > high.code()) { | ||
| 358 | - // Establish the invariant that the register of the low word always has | ||
| 359 | - // a lower code than the register of the high word. This guarantees that | ||
| 360 | - // if a register pair of an input is reused for the result, the low | ||
| 361 | - // word and high word registers are not swapped, i.e. the low word | ||
| 362 | - // register of the result is not the high word register of the input, | ||
| 363 | - // and vice versa. | ||
| 364 | - std::swap(low, high); | ||
| 365 | - } | ||
| 366 | 357 | return LiftoffRegister::ForPair(low, high); | |
| 367 | 358 | } else if (kNeedS128RegPair && rc == kFpRegPair) { | |
| 368 | 359 | // kFpRegPair specific logic here because we need adjacent registers, not | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -968,5 +968,52 @@ TEST(JSWeakRefTenuredInWorklist) { | |||
| 968 | 968 | CHECK(weak_ref->target().IsUndefined(isolate)); | |
| 969 | 969 | } | |
| 970 | 970 | ||
| 971 | + TEST(UnregisterTokenHeapVerifier) { | ||
| 972 | + FLAG_harmony_weak_refs = true; | ||
| 973 | + if (!FLAG_incremental_marking) return; | ||
| 974 | + ManualGCScope manual_gc_scope; | ||
| 975 | + #ifdef VERIFY_HEAP | ||
| 976 | + FLAG_verify_heap = true; | ||
| 977 | + #endif | ||
| 978 | + | ||
| 979 | + CcTest::InitializeVM(); | ||
| 980 | + v8::Isolate* isolate = CcTest::isolate(); | ||
| 981 | + Heap* heap = CcTest::heap(); | ||
| 982 | + v8::HandleScope outer_scope(isolate); | ||
| 983 | + | ||
| 984 | + { | ||
| 985 | + // Make a new FinalizationRegistry and register an object with an unregister | ||
| 986 | + // token that's unreachable after the IIFE returns. | ||
| 987 | + v8::HandleScope scope(isolate); | ||
| 988 | + CompileRun( | ||
| 989 | + "var token = {}; " | ||
| 990 | + "var registry = new FinalizationRegistry(function () {}); " | ||
| 991 | + "(function () { " | ||
| 992 | + " let o = {}; " | ||
| 993 | + " registry.register(o, {}, token); " | ||
| 994 | + "})();"); | ||
| 995 | + } | ||
| 996 | + | ||
| 997 | + // GC so the WeakCell corresponding to o is moved from the active_cells to | ||
| 998 | + // cleared_cells. | ||
| 999 | + CcTest::CollectAllGarbage(); | ||
| 1000 | + CcTest::CollectAllGarbage(); | ||
| 1001 | + | ||
| 1002 | + { | ||
| 1003 | + // Override the unregister token to make the original object collectible. | ||
| 1004 | + v8::HandleScope scope(isolate); | ||
| 1005 | + CompileRun("token = 0;"); | ||
| 1006 | + } | ||
| 1007 | + | ||
| 1008 | + heap::SimulateIncrementalMarking(heap, true); | ||
| 1009 | + | ||
| 1010 | + // Pump message loop to run the finalizer task, then the incremental marking | ||
| 1011 | + // task. The finalizer task will pop the WeakCell from the cleared list. This | ||
| 1012 | + // should make the unregister_token slot undefined. That slot is iterated as a | ||
| 1013 | + // custom weak pointer, so if it is not made undefined, the verifier as part | ||
| 1014 | + // of the incremental marking task will crash. | ||
| 1015 | + EmptyMessageQueues(isolate); | ||
| 1016 | + } | ||
| 1017 | + | ||
| 971 | 1018 | } // namespace internal | |
| 972 | 1019 | } // namespace v8 | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,29 @@ | |||
| 1 | + // Copyright 2020 the V8 project authors. All rights reserved. | ||
| 2 | + // Use of this source code is governed by a BSD-style license that can be | ||
| 3 | + // found in the LICENSE file. | ||
| 4 | + | ||
| 5 | + load('test/mjsunit/wasm/wasm-module-builder.js'); | ||
| 6 | + | ||
| 7 | + const builder = new WasmModuleBuilder(); | ||
| 8 | + builder.addType(makeSig( | ||
| 9 | + [kWasmI64, kWasmI32, kWasmF64, kWasmI64, kWasmI64, kWasmI32, kWasmI64], | ||
| 10 | + [])); | ||
| 11 | + builder.addFunction(undefined, 0 /* sig */).addBody([ | ||
| 12 | + kExprI32Const, 0, // i32.const | ||
| 13 | + kExprIf, kWasmStmt, // if @3 | ||
| 14 | + kExprI32Const, 1, // i32.const | ||
| 15 | + kExprIf, kWasmStmt, // if @7 | ||
| 16 | + kExprNop, // nop | ||
| 17 | + kExprElse, // else @10 | ||
| 18 | + kExprUnreachable, // unreachable | ||
| 19 | + kExprEnd, // end @12 | ||
| 20 | + kExprLocalGet, 0x06, // local.get | ||
| 21 | + kExprLocalSet, 0x00, // local.set | ||
| 22 | + kExprLocalGet, 0x03, // local.get | ||
| 23 | + kExprLocalGet, 0x00, // local.get | ||
| 24 | + kExprI64Sub, // i64.sub | ||
| 25 | + kExprDrop, // drop | ||
| 26 | + kExprUnreachable, // unreachable | ||
| 27 | + kExprEnd // end @24 | ||
| 28 | + ]); | ||
| 29 | + builder.toModule(); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -14,3 +14,4 @@ I'm starting to think that just adding trailing whitespaces might not be bad. | |||
| 14 | 14 | Because whitespaces are not that funny..... | |
| 15 | 15 | Today's answer to life the universe and everything is 12950! | |
| 16 | 16 | Today's answer to life the universe and everything is 6728! | |
| 17 | + +1 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments