1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 /* Property descriptors and flags. */
7
8 #ifndef js_PropertySpec_h
9 #define js_PropertySpec_h
10
11 #include "mozilla/Assertions.h" // MOZ_ASSERT{,_IF}
12
13 #include <stddef.h> // size_t
14 #include <stdint.h> // uint8_t, uint16_t, int32_t, uint32_t, uintptr_t
15 #include <type_traits> // std::enable_if
16
17 #include "jstypes.h" // JS_PUBLIC_API
18
19 #include "js/CallArgs.h" // JSNative
20 #include "js/PropertyDescriptor.h" // JSPROP_*
21 #include "js/RootingAPI.h" // JS::MutableHandle
22 #include "js/Symbol.h" // JS::SymbolCode, PropertySpecNameIsSymbol
23 #include "js/Value.h" // JS::Value
24
25 struct JS_PUBLIC_API JSContext;
26 class JSJitInfo;
27
28 /**
29 * Wrapper to relace JSNative for JSPropertySpecs and JSFunctionSpecs. This will
30 * allow us to pass one JSJitInfo per function with the property/function spec,
31 * without additional field overhead.
32 */
33 struct JSNativeWrapper {
34 JSNative op = nullptr;
35 const JSJitInfo* info = nullptr;
36
37 JSNativeWrapper() = default;
38
39 JSNativeWrapper(const JSNativeWrapper& other) = default;
40
JSNativeWrapperJSNativeWrapper41 constexpr JSNativeWrapper(JSNative op, const JSJitInfo* info)
42 : op(op), info(info) {}
43 };
44
45 /**
46 * Description of a property. JS_DefineProperties and JS_InitClass take arrays
47 * of these and define many properties at once. JS_PSG, JS_PSGS and JS_PS_END
48 * are helper macros for defining such arrays.
49 */
50 struct JSPropertySpec {
51 struct SelfHostedWrapper {
52 // The same type as JSNativeWrapper's first field, so that the access in
53 // JSPropertySpec::checkAccessorsAreSelfHosted become valid.
54 JSNative unused = nullptr;
55
56 const char* funname;
57
58 SelfHostedWrapper() = delete;
59
SelfHostedWrapperJSPropertySpec::SelfHostedWrapper60 explicit constexpr SelfHostedWrapper(const char* funname)
61 : funname(funname) {}
62 };
63
64 struct ValueWrapper {
65 enum class Type : uint8_t { String, Int32, Double };
66 Type type;
67 union {
68 const char* string;
69 int32_t int32;
70 double double_;
71 };
72
73 private:
74 ValueWrapper() = delete;
75
ValueWrapperJSPropertySpec::ValueWrapper76 explicit constexpr ValueWrapper(int32_t n) : type(Type::Int32), int32(n) {}
77
ValueWrapperJSPropertySpec::ValueWrapper78 explicit constexpr ValueWrapper(const char* s)
79 : type(Type::String), string(s) {}
80
ValueWrapperJSPropertySpec::ValueWrapper81 explicit constexpr ValueWrapper(double d)
82 : type(Type::Double), double_(d) {}
83
84 public:
85 ValueWrapper(const ValueWrapper& other) = default;
86
int32ValueJSPropertySpec::ValueWrapper87 static constexpr ValueWrapper int32Value(int32_t n) {
88 return ValueWrapper(n);
89 }
90
stringValueJSPropertySpec::ValueWrapper91 static constexpr ValueWrapper stringValue(const char* s) {
92 return ValueWrapper(s);
93 }
94
doubleValueJSPropertySpec::ValueWrapper95 static constexpr ValueWrapper doubleValue(double d) {
96 return ValueWrapper(d);
97 }
98 };
99
100 union Accessor {
101 JSNativeWrapper native;
102 SelfHostedWrapper selfHosted;
103
104 private:
105 Accessor() = delete;
106
Accessor(JSNative op,const JSJitInfo * info)107 constexpr Accessor(JSNative op, const JSJitInfo* info) : native(op, info) {}
108
Accessor(const char * funname)109 explicit constexpr Accessor(const char* funname) : selfHosted(funname) {}
110
111 public:
112 Accessor(const Accessor& other) = default;
113
114 static constexpr Accessor nativeAccessor(JSNative op,
115 const JSJitInfo* info = nullptr) {
116 return Accessor(op, info);
117 }
118
selfHostedAccessor(const char * funname)119 static constexpr Accessor selfHostedAccessor(const char* funname) {
120 return Accessor(funname);
121 }
122
noAccessor()123 static constexpr Accessor noAccessor() {
124 return Accessor(nullptr, nullptr);
125 }
126 };
127
128 union AccessorsOrValue {
129 struct Accessors {
130 Accessor getter;
131 Accessor setter;
132
AccessorsJSPropertySpec::AccessorsOrValue::Accessors133 constexpr Accessors(Accessor getter, Accessor setter)
134 : getter(getter), setter(setter) {}
135 } accessors;
136 ValueWrapper value;
137
138 private:
139 AccessorsOrValue() = delete;
140
AccessorsOrValue(Accessor getter,Accessor setter)141 constexpr AccessorsOrValue(Accessor getter, Accessor setter)
142 : accessors(getter, setter) {}
143
AccessorsOrValue(ValueWrapper value)144 explicit constexpr AccessorsOrValue(ValueWrapper value) : value(value) {}
145
146 public:
147 AccessorsOrValue(const AccessorsOrValue& other) = default;
148
fromAccessors(Accessor getter,Accessor setter)149 static constexpr AccessorsOrValue fromAccessors(Accessor getter,
150 Accessor setter) {
151 return AccessorsOrValue(getter, setter);
152 }
153
fromValue(ValueWrapper value)154 static constexpr AccessorsOrValue fromValue(ValueWrapper value) {
155 return AccessorsOrValue(value);
156 }
157 };
158
159 union Name {
160 private:
161 const char* string_;
162 uintptr_t symbol_;
163
164 public:
165 Name() = delete;
166
Name(const char * str)167 explicit constexpr Name(const char* str) : string_(str) {}
Name(JS::SymbolCode symbol)168 explicit constexpr Name(JS::SymbolCode symbol)
169 : symbol_(uint32_t(symbol) + 1) {}
170
171 explicit operator bool() const { return !!symbol_; }
172
isSymbol()173 bool isSymbol() const { return JS::PropertySpecNameIsSymbol(symbol_); }
symbol()174 JS::SymbolCode symbol() const {
175 MOZ_ASSERT(isSymbol());
176 return JS::SymbolCode(symbol_ - 1);
177 }
178
isString()179 bool isString() const { return !isSymbol(); }
string()180 const char* string() const {
181 MOZ_ASSERT(isString());
182 return string_;
183 }
184 };
185
186 Name name;
187
188 private:
189 // JSPROP_* property attributes as defined in PropertyDescriptor.h.
190 uint8_t attributes_;
191
192 // Whether AccessorsOrValue below stores a value, JSNative accessors, or
193 // self-hosted accessors.
194 enum class Kind : uint8_t { Value, SelfHostedAccessor, NativeAccessor };
195 Kind kind_;
196
197 public:
198 AccessorsOrValue u;
199
200 private:
201 JSPropertySpec() = delete;
202
JSPropertySpecJSPropertySpec203 constexpr JSPropertySpec(const char* name, uint8_t attributes, Kind kind,
204 AccessorsOrValue u)
205 : name(name), attributes_(attributes), kind_(kind), u(u) {}
JSPropertySpecJSPropertySpec206 constexpr JSPropertySpec(JS::SymbolCode name, uint8_t attributes, Kind kind,
207 AccessorsOrValue u)
208 : name(name), attributes_(attributes), kind_(kind), u(u) {}
209
210 public:
211 JSPropertySpec(const JSPropertySpec& other) = default;
212
213 static constexpr JSPropertySpec nativeAccessors(
214 const char* name, uint8_t attributes, JSNative getter,
215 const JSJitInfo* getterInfo, JSNative setter = nullptr,
216 const JSJitInfo* setterInfo = nullptr) {
217 return JSPropertySpec(
218 name, attributes, Kind::NativeAccessor,
219 AccessorsOrValue::fromAccessors(
220 JSPropertySpec::Accessor::nativeAccessor(getter, getterInfo),
221 JSPropertySpec::Accessor::nativeAccessor(setter, setterInfo)));
222 }
223
224 static constexpr JSPropertySpec nativeAccessors(
225 JS::SymbolCode name, uint8_t attributes, JSNative getter,
226 const JSJitInfo* getterInfo, JSNative setter = nullptr,
227 const JSJitInfo* setterInfo = nullptr) {
228 return JSPropertySpec(
229 name, attributes, Kind::NativeAccessor,
230 AccessorsOrValue::fromAccessors(
231 JSPropertySpec::Accessor::nativeAccessor(getter, getterInfo),
232 JSPropertySpec::Accessor::nativeAccessor(setter, setterInfo)));
233 }
234
235 static constexpr JSPropertySpec selfHostedAccessors(
236 const char* name, uint8_t attributes, const char* getterName,
237 const char* setterName = nullptr) {
238 return JSPropertySpec(
239 name, attributes, Kind::SelfHostedAccessor,
240 AccessorsOrValue::fromAccessors(
241 JSPropertySpec::Accessor::selfHostedAccessor(getterName),
242 setterName
243 ? JSPropertySpec::Accessor::selfHostedAccessor(setterName)
244 : JSPropertySpec::Accessor::noAccessor()));
245 }
246
247 static constexpr JSPropertySpec selfHostedAccessors(
248 JS::SymbolCode name, uint8_t attributes, const char* getterName,
249 const char* setterName = nullptr) {
250 return JSPropertySpec(
251 name, attributes, Kind::SelfHostedAccessor,
252 AccessorsOrValue::fromAccessors(
253 JSPropertySpec::Accessor::selfHostedAccessor(getterName),
254 setterName
255 ? JSPropertySpec::Accessor::selfHostedAccessor(setterName)
256 : JSPropertySpec::Accessor::noAccessor()));
257 }
258
int32ValueJSPropertySpec259 static constexpr JSPropertySpec int32Value(const char* name,
260 uint8_t attributes, int32_t n) {
261 return JSPropertySpec(name, attributes, Kind::Value,
262 AccessorsOrValue::fromValue(
263 JSPropertySpec::ValueWrapper::int32Value(n)));
264 }
265
int32ValueJSPropertySpec266 static constexpr JSPropertySpec int32Value(JS::SymbolCode name,
267 uint8_t attributes, int32_t n) {
268 return JSPropertySpec(name, attributes, Kind::Value,
269 AccessorsOrValue::fromValue(
270 JSPropertySpec::ValueWrapper::int32Value(n)));
271 }
272
stringValueJSPropertySpec273 static constexpr JSPropertySpec stringValue(const char* name,
274 uint8_t attributes,
275 const char* s) {
276 return JSPropertySpec(name, attributes, Kind::Value,
277 AccessorsOrValue::fromValue(
278 JSPropertySpec::ValueWrapper::stringValue(s)));
279 }
280
stringValueJSPropertySpec281 static constexpr JSPropertySpec stringValue(JS::SymbolCode name,
282 uint8_t attributes,
283 const char* s) {
284 return JSPropertySpec(name, attributes, Kind::Value,
285 AccessorsOrValue::fromValue(
286 JSPropertySpec::ValueWrapper::stringValue(s)));
287 }
288
doubleValueJSPropertySpec289 static constexpr JSPropertySpec doubleValue(const char* name,
290 uint8_t attributes, double d) {
291 return JSPropertySpec(name, attributes, Kind::Value,
292 AccessorsOrValue::fromValue(
293 JSPropertySpec::ValueWrapper::doubleValue(d)));
294 }
295
sentinelJSPropertySpec296 static constexpr JSPropertySpec sentinel() {
297 return JSPropertySpec(nullptr, 0, Kind::NativeAccessor,
298 AccessorsOrValue::fromAccessors(
299 JSPropertySpec::Accessor::noAccessor(),
300 JSPropertySpec::Accessor::noAccessor()));
301 }
302
attributesJSPropertySpec303 unsigned attributes() const { return attributes_; }
304
isAccessorJSPropertySpec305 bool isAccessor() const {
306 return (kind_ == Kind::NativeAccessor || kind_ == Kind::SelfHostedAccessor);
307 }
308
309 JS_PUBLIC_API bool getValue(JSContext* cx,
310 JS::MutableHandle<JS::Value> value) const;
311
isSelfHostedJSPropertySpec312 bool isSelfHosted() const {
313 MOZ_ASSERT(isAccessor());
314 #ifdef DEBUG
315 // Verify that our accessors match our Kind.
316 if (kind_ == Kind::SelfHostedAccessor) {
317 checkAccessorsAreSelfHosted();
318 } else {
319 checkAccessorsAreNative();
320 }
321 #endif
322 return kind_ == Kind::SelfHostedAccessor;
323 }
324
325 static_assert(sizeof(SelfHostedWrapper) == sizeof(JSNativeWrapper),
326 "JSPropertySpec::getter/setter must be compact");
327 static_assert(offsetof(SelfHostedWrapper, unused) ==
328 offsetof(JSNativeWrapper, op) &&
329 offsetof(SelfHostedWrapper, funname) ==
330 offsetof(JSNativeWrapper, info),
331 "checkAccessorsAreNative below require that "
332 "SelfHostedWrapper::funname overlay "
333 "JSNativeWrapper::info and "
334 "SelfHostedWrapper::unused overlay "
335 "JSNativeWrapper::op");
336
337 private:
checkAccessorsAreNativeJSPropertySpec338 void checkAccessorsAreNative() const {
339 // We may have a getter or a setter or both. And whichever ones we have
340 // should not have a SelfHostedWrapper for the accessor.
341 MOZ_ASSERT_IF(u.accessors.getter.native.info, u.accessors.getter.native.op);
342 MOZ_ASSERT_IF(u.accessors.setter.native.info, u.accessors.setter.native.op);
343 }
344
checkAccessorsAreSelfHostedJSPropertySpec345 void checkAccessorsAreSelfHosted() const {
346 MOZ_ASSERT(!u.accessors.getter.selfHosted.unused);
347 MOZ_ASSERT(!u.accessors.setter.selfHosted.unused);
348 }
349 };
350
351 // There can be many JSPropertySpec instances so verify the size is what we
352 // expect:
353 //
354 // - Name (1 word)
355 // - attributes_ + isAccessor_ (1 word)
356 // - AccessorsOrValue (4 words, native + JSJitInfo for both getter and setter)
357 static_assert(sizeof(JSPropertySpec) == 6 * sizeof(uintptr_t));
358
359 template <unsigned Attributes>
CheckAccessorAttrs()360 constexpr uint8_t CheckAccessorAttrs() {
361 static_assert((Attributes & ~(JSPROP_ENUMERATE | JSPROP_PERMANENT)) == 0,
362 "Unexpected flag (not JSPROP_ENUMERATE or JSPROP_PERMANENT)");
363 return uint8_t(Attributes);
364 }
365
366 #define JS_PSG(name, getter, attributes) \
367 JSPropertySpec::nativeAccessors(name, CheckAccessorAttrs<attributes>(), \
368 getter, nullptr)
369 #define JS_PSGS(name, getter, setter, attributes) \
370 JSPropertySpec::nativeAccessors(name, CheckAccessorAttrs<attributes>(), \
371 getter, nullptr, setter, nullptr)
372 #define JS_SYM_GET(symbol, getter, attributes) \
373 JSPropertySpec::nativeAccessors(::JS::SymbolCode::symbol, \
374 CheckAccessorAttrs<attributes>(), getter, \
375 nullptr)
376 #define JS_SELF_HOSTED_GET(name, getterName, attributes) \
377 JSPropertySpec::selfHostedAccessors(name, CheckAccessorAttrs<attributes>(), \
378 getterName)
379 #define JS_SELF_HOSTED_GETSET(name, getterName, setterName, attributes) \
380 JSPropertySpec::selfHostedAccessors(name, CheckAccessorAttrs<attributes>(), \
381 getterName, setterName)
382 #define JS_SELF_HOSTED_SYM_GET(symbol, getterName, attributes) \
383 JSPropertySpec::selfHostedAccessors( \
384 ::JS::SymbolCode::symbol, CheckAccessorAttrs<attributes>(), getterName)
385 #define JS_STRING_PS(name, string, attributes) \
386 JSPropertySpec::stringValue(name, attributes, string)
387 #define JS_STRING_SYM_PS(symbol, string, attributes) \
388 JSPropertySpec::stringValue(::JS::SymbolCode::symbol, attributes, string)
389 #define JS_INT32_PS(name, value, attributes) \
390 JSPropertySpec::int32Value(name, attributes, value)
391 #define JS_DOUBLE_PS(name, value, attributes) \
392 JSPropertySpec::doubleValue(name, attributes, value)
393 #define JS_PS_END JSPropertySpec::sentinel()
394
395 /**
396 * To define a native function, set call to a JSNativeWrapper. To define a
397 * self-hosted function, set selfHostedName to the name of a function
398 * compiled during JSRuntime::initSelfHosting.
399 */
400 struct JSFunctionSpec {
401 using Name = JSPropertySpec::Name;
402
403 Name name;
404 JSNativeWrapper call;
405 uint16_t nargs;
406 uint16_t flags;
407 const char* selfHostedName;
408
409 // JSPROP_* property attributes as defined in PropertyDescriptor.h
attributesJSFunctionSpec410 unsigned attributes() const { return flags; }
411 };
412
413 /*
414 * Terminating sentinel initializer to put at the end of a JSFunctionSpec array
415 * that's passed to JS_DefineFunctions or JS_InitClass.
416 */
417 #define JS_FS_END JS_FN(nullptr, nullptr, 0, 0)
418
419 /*
420 * Initializer macros for a JSFunctionSpec array element. JS_FNINFO allows the
421 * simple adding of JSJitInfos. JS_SELF_HOSTED_FN declares a self-hosted
422 * function. JS_INLINABLE_FN allows specifying an InlinableNative enum value for
423 * natives inlined or specialized by the JIT. Finally JS_FNSPEC has slots for
424 * all the fields.
425 *
426 * The _SYM variants allow defining a function with a symbol key rather than a
427 * string key. For example, use JS_SYM_FN(iterator, ...) to define an
428 * @@iterator method.
429 */
430 #define JS_FN(name, call, nargs, flags) \
431 JS_FNSPEC(name, call, nullptr, nargs, flags, nullptr)
432 #define JS_INLINABLE_FN(name, call, nargs, flags, native) \
433 JS_FNSPEC(name, call, &js::jit::JitInfo_##native, nargs, flags, nullptr)
434 #define JS_SYM_FN(symbol, call, nargs, flags) \
435 JS_SYM_FNSPEC(symbol, call, nullptr, nargs, flags, nullptr)
436 #define JS_FNINFO(name, call, info, nargs, flags) \
437 JS_FNSPEC(name, call, info, nargs, flags, nullptr)
438 #define JS_SELF_HOSTED_FN(name, selfHostedName, nargs, flags) \
439 JS_FNSPEC(name, nullptr, nullptr, nargs, flags, selfHostedName)
440 #define JS_SELF_HOSTED_SYM_FN(symbol, selfHostedName, nargs, flags) \
441 JS_SYM_FNSPEC(symbol, nullptr, nullptr, nargs, flags, selfHostedName)
442 #define JS_SYM_FNSPEC(symbol, call, info, nargs, flags, selfHostedName) \
443 JS_FNSPEC(::JS::SymbolCode::symbol, call, info, nargs, flags, selfHostedName)
444 #define JS_FNSPEC(name, call, info, nargs, flags, selfHostedName) \
445 { JSFunctionSpec::Name(name), {call, info}, nargs, flags, selfHostedName }
446
447 #endif // js_PropertySpec_h
448