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
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Stream teeing state. */
#ifndef builtin_streams_TeeState_h
#define builtin_streams_TeeState_h
#include "mozilla/Assertions.h" // MOZ_ASSERT
#include <stdint.h> // uint32_t
#include "builtin/streams/ReadableStreamController.h" // js::ReadableStreamDefaultController
#include "js/Class.h" // JSClass
#include "js/Value.h" // JS::{Int32,Object}Value
#include "vm/NativeObject.h" // js::NativeObject
#include "vm/PromiseObject.h" // js::PromiseObject
namespace js {
/**
* TeeState objects implement the local variables in Streams spec 3.3.9
* ReadableStreamTee, which are accessed by several algorithms.
*/
class TeeState : public NativeObject {
public:
/**
* Memory layout for TeeState instances.
*
* The Reason1 and Reason2 slots store opaque values, which might be
* wrapped objects from other compartments. Since we don't treat them as
* objects in Streams-specific code, we don't have to worry about that
* apart from ensuring that the values are properly wrapped before storing
* them.
*
* CancelPromise is always created in TeeState::create below, so is
* guaranteed to be in the same compartment as the TeeState instance
* itself.
*
* Stream can be from another compartment. It is automatically wrapped
* before storing it and unwrapped upon retrieval. That means that
* TeeState consumers need to be able to deal with unwrapped
* ReadableStream instances from non-current compartments.
*
* Branch1 and Branch2 are always created in the same compartment as the
* TeeState instance, so cannot be from another compartment.
*/
enum Slots {
Slot_Flags = 0,
Slot_Reason1,
Slot_Reason2,
Slot_CancelPromise,
Slot_Stream,
Slot_Branch1,
Slot_Branch2,
SlotCount
};
private:
enum Flags {
Flag_Reading = 1 << 0,
Flag_Canceled1 = 1 << 1,
Flag_Canceled2 = 1 << 2,
// No internal user ever sets the cloneForBranch2 flag to true, and the
// streams spec doesn't expose a way to set the flag to true. So for the
// moment, don't even reserve flag-space to store it.
// Flag_CloneForBranch2 = 1 << 3,
};
uint32_t flags() const { return getFixedSlot(Slot_Flags).toInt32(); }
void setFlags(uint32_t flags) {
setFixedSlot(Slot_Flags, JS::Int32Value(flags));
}
public:
static const JSClass class_;
// Consistent with not even storing this always-false flag, expose it as
// compile-time constant false.
static constexpr bool cloneForBranch2() { return false; }
bool reading() const { return flags() & Flag_Reading; }
void setReading() {
MOZ_ASSERT(!(flags() & Flag_Reading));
setFlags(flags() | Flag_Reading);
}
void unsetReading() {
MOZ_ASSERT(flags() & Flag_Reading);
setFlags(flags() & ~Flag_Reading);
}
bool canceled1() const { return flags() & Flag_Canceled1; }
void setCanceled1(HandleValue reason) {
MOZ_ASSERT(!(flags() & Flag_Canceled1));
setFlags(flags() | Flag_Canceled1);
setFixedSlot(Slot_Reason1, reason);
}
bool canceled2() const { return flags() & Flag_Canceled2; }
void setCanceled2(HandleValue reason) {
MOZ_ASSERT(!(flags() & Flag_Canceled2));
setFlags(flags() | Flag_Canceled2);
setFixedSlot(Slot_Reason2, reason);
}
JS::Value reason1() const {
MOZ_ASSERT(canceled1());
return getFixedSlot(Slot_Reason1);
}
JS::Value reason2() const {
MOZ_ASSERT(canceled2());
return getFixedSlot(Slot_Reason2);
}
PromiseObject* cancelPromise() {
return &getFixedSlot(Slot_CancelPromise).toObject().as<PromiseObject>();
}
ReadableStreamDefaultController* branch1() {
ReadableStreamDefaultController* controller =
&getFixedSlot(Slot_Branch1)
.toObject()
.as<ReadableStreamDefaultController>();
MOZ_ASSERT(controller->isTeeBranch1());
return controller;
}
void setBranch1(ReadableStreamDefaultController* controller) {
MOZ_ASSERT(controller->isTeeBranch1());
setFixedSlot(Slot_Branch1, JS::ObjectValue(*controller));
}
ReadableStreamDefaultController* branch2() {
ReadableStreamDefaultController* controller =
&getFixedSlot(Slot_Branch2)
.toObject()
.as<ReadableStreamDefaultController>();
MOZ_ASSERT(controller->isTeeBranch2());
return controller;
}
void setBranch2(ReadableStreamDefaultController* controller) {
MOZ_ASSERT(controller->isTeeBranch2());
setFixedSlot(Slot_Branch2, JS::ObjectValue(*controller));
}
static TeeState* create(JSContext* cx,
Handle<ReadableStream*> unwrappedStream);
};
} // namespace js
#endif // builtin_streams_TeeState_h
|