-
Notifications
You must be signed in to change notification settings - Fork 28.2k
/
promise-all-element-closure.tq
212 lines (188 loc) Β· 8.61 KB
/
promise-all-element-closure.tq
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include 'src/builtins/builtins-promise.h'
#include 'src/builtins/builtins-promise-gen.h'
#include 'src/objects/property-array.h'
namespace promise {
struct PromiseAllWrapResultAsFulfilledFunctor {
macro Call(_nativeContext: NativeContext, value: JSAny): JSAny {
return value;
}
}
struct PromiseAllSettledWrapResultAsFulfilledFunctor {
transitioning
macro Call(implicit context: Context)(
nativeContext: NativeContext, value: JSAny): JSAny {
// TODO(gsathya): Optimize the creation using a cached map to
// prevent transitions here.
// 9. Let obj be ! ObjectCreate(%ObjectPrototype%).
const objectFunction = UnsafeCast<JSFunction>(
nativeContext[NativeContextSlot::OBJECT_FUNCTION_INDEX]);
const objectFunctionMap =
UnsafeCast<Map>(objectFunction.prototype_or_initial_map);
const obj = AllocateJSObjectFromMap(objectFunctionMap);
// 10. Perform ! CreateDataProperty(obj, "status", "fulfilled").
FastCreateDataProperty(
obj, StringConstant('status'), StringConstant('fulfilled'));
// 11. Perform ! CreateDataProperty(obj, "value", x).
FastCreateDataProperty(obj, StringConstant('value'), value);
return obj;
}
}
struct PromiseAllSettledWrapResultAsRejectedFunctor {
transitioning
macro Call(implicit context: Context)(
nativeContext: NativeContext, value: JSAny): JSAny {
// TODO(gsathya): Optimize the creation using a cached map to
// prevent transitions here.
// 9. Let obj be ! ObjectCreate(%ObjectPrototype%).
const objectFunction = UnsafeCast<JSFunction>(
nativeContext[NativeContextSlot::OBJECT_FUNCTION_INDEX]);
const objectFunctionMap =
UnsafeCast<Map>(objectFunction.prototype_or_initial_map);
const obj = AllocateJSObjectFromMap(objectFunctionMap);
// 10. Perform ! CreateDataProperty(obj, "status", "rejected").
FastCreateDataProperty(
obj, StringConstant('status'), StringConstant('rejected'));
// 11. Perform ! CreateDataProperty(obj, "reason", x).
FastCreateDataProperty(obj, StringConstant('reason'), value);
return obj;
}
}
extern macro LoadJSReceiverIdentityHash(Object): intptr labels IfNoHash;
extern enum PromiseAllResolveElementContextSlots extends int31
constexpr 'PromiseBuiltins::PromiseAllResolveElementContextSlots' {
kPromiseAllResolveElementRemainingSlot,
kPromiseAllResolveElementCapabilitySlot,
kPromiseAllResolveElementValuesArraySlot,
kPromiseAllResolveElementLength
}
extern operator '[]=' macro StoreContextElement(
Context, constexpr PromiseAllResolveElementContextSlots, Object): void;
extern operator '[]' macro LoadContextElement(
Context, constexpr PromiseAllResolveElementContextSlots): Object;
const kPropertyArrayNoHashSentinel: constexpr int31
generates 'PropertyArray::kNoHashSentinel';
const kPropertyArrayHashFieldMax: constexpr int31
generates 'PropertyArray::HashField::kMax';
transitioning macro PromiseAllResolveElementClosure<F: type>(
implicit context: Context)(
value: JSAny, function: JSFunction, wrapResultFunctor: F,
hasResolveAndRejectClosures: constexpr bool): JSAny {
// We use the {function}s context as the marker to remember whether this
// resolve element closure was already called. It points to the resolve
// element context (which is a FunctionContext) until it was called the
// first time, in which case we make it point to the native context here
// to mark this resolve element closure as done.
if (IsNativeContext(context)) deferred {
return Undefined;
}
assert(
context.length ==
PromiseAllResolveElementContextSlots::kPromiseAllResolveElementLength);
const nativeContext = LoadNativeContext(context);
function.context = nativeContext;
// Determine the index from the {function}.
assert(kPropertyArrayNoHashSentinel == 0);
const identityHash =
LoadJSReceiverIdentityHash(function) otherwise unreachable;
assert(identityHash > 0);
const index = identityHash - 1;
// Check if we need to grow the [[ValuesArray]] to store {value} at {index}.
const valuesArray = UnsafeCast<JSArray>(
context[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementValuesArraySlot]);
const elements = UnsafeCast<FixedArray>(valuesArray.elements);
const valuesLength = Convert<intptr>(valuesArray.length);
if (index < valuesLength) {
// The {index} is in bounds of the {values_array}, check if this element has
// already been resolved, and store the {value} if not.
//
// Promise.allSettled, for each input element, has both a resolve and a
// reject closure that share an [[AlreadyCalled]] boolean. That is, the
// input element can only be settled once: after resolve is called, reject
// returns early, and vice versa. Using {function}'s context as the marker
// only tracks per-closure instead of per-element. When the second
// resolve/reject closure is called on the same index, values.object[index]
// will already exist and will not be the hole value. In that case, return
// early. Everything up to this point is not yet observable to user code.
// This is not a problem for Promise.all since Promise.all has a single
// resolve closure (no reject) per element.
if (hasResolveAndRejectClosures) {
if (elements.objects[index] != TheHole) deferred {
return Undefined;
}
}
// Update the value depending on whether Promise.all or
// Promise.allSettled is called.
const updatedValue = wrapResultFunctor.Call(nativeContext, value);
elements.objects[index] = updatedValue;
} else {
// Check if we need to grow the backing store.
//
// There's no need to check if this element has already been resolved for
// Promise.allSettled if {values_array} has not yet grown to the index.
const newLength = index + 1;
const elementsLength = elements.length_intptr;
// Update the value depending on whether Promise.all or
// Promise.allSettled is called.
const updatedValue = wrapResultFunctor.Call(nativeContext, value);
if (index < elementsLength) {
// The {index} is within bounds of the {elements} backing store, so
// just store the {value} and update the "length" of the {values_array}.
valuesArray.length = Convert<Smi>(newLength);
elements.objects[index] = updatedValue;
} else
deferred {
// We need to grow the backing store to fit the {index} as well.
const newElementsLength = IntPtrMin(
CalculateNewElementsCapacity(newLength),
kPropertyArrayHashFieldMax + 1);
assert(index < newElementsLength);
assert(elementsLength < newElementsLength);
const newElements =
ExtractFixedArray(elements, 0, elementsLength, newElementsLength);
newElements.objects[index] = updatedValue;
// Update backing store and "length" on {values_array}.
valuesArray.elements = newElements;
valuesArray.length = Convert<Smi>(newLength);
}
}
let remainingElementsCount =
UnsafeCast<Smi>(context[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementRemainingSlot]);
remainingElementsCount = remainingElementsCount - 1;
context[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementRemainingSlot] = remainingElementsCount;
if (remainingElementsCount == 0) {
const capability = UnsafeCast<PromiseCapability>(
context[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementCapabilitySlot]);
const resolve = UnsafeCast<JSAny>(capability.resolve);
Call(context, resolve, Undefined, valuesArray);
}
return Undefined;
}
transitioning javascript builtin
PromiseAllResolveElementClosure(
js-implicit context: Context, receiver: JSAny,
target: JSFunction)(value: JSAny): JSAny {
return PromiseAllResolveElementClosure(
value, target, PromiseAllWrapResultAsFulfilledFunctor{}, false);
}
transitioning javascript builtin
PromiseAllSettledResolveElementClosure(
js-implicit context: Context, receiver: JSAny,
target: JSFunction)(value: JSAny): JSAny {
return PromiseAllResolveElementClosure(
value, target, PromiseAllSettledWrapResultAsFulfilledFunctor{}, true);
}
transitioning javascript builtin
PromiseAllSettledRejectElementClosure(
js-implicit context: Context, receiver: JSAny,
target: JSFunction)(value: JSAny): JSAny {
return PromiseAllResolveElementClosure(
value, target, PromiseAllSettledWrapResultAsRejectedFunctor{}, true);
}
}