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