Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : /* JSClass definition and its component types, plus related interfaces. */
8 :
9 : #ifndef js_Class_h
10 : #define js_Class_h
11 :
12 : #include "jstypes.h"
13 :
14 : #include "js/CallArgs.h"
15 : #include "js/Id.h"
16 : #include "js/TypeDecls.h"
17 :
18 : /*
19 : * A JSClass acts as a vtable for JS objects that allows JSAPI clients to
20 : * control various aspects of the behavior of an object like property lookup.
21 : * js::Class is an engine-private extension that allows more control over
22 : * object behavior and, e.g., allows custom slow layout.
23 : */
24 :
25 : struct JSAtomState;
26 : struct JSFreeOp;
27 : struct JSFunctionSpec;
28 :
29 : namespace js {
30 :
31 : struct Class;
32 : class FreeOp;
33 : class Shape;
34 :
35 : // This is equal to JSFunction::class_. Use it in places where you don't want
36 : // to #include jsfun.h.
37 : extern JS_FRIEND_DATA(const js::Class* const) FunctionClassPtr;
38 :
39 : } // namespace js
40 :
41 : namespace JS {
42 :
43 : class AutoIdVector;
44 :
45 : /**
46 : * The answer to a successful query as to whether an object is an Array per
47 : * ES6's internal |IsArray| operation (as exposed by |Array.isArray|).
48 : */
49 : enum class IsArrayAnswer
50 : {
51 : Array,
52 : NotArray,
53 : RevokedProxy
54 : };
55 :
56 : /**
57 : * ES6 7.2.2.
58 : *
59 : * Returns false on failure, otherwise returns true and sets |*isArray|
60 : * indicating whether the object passes ECMAScript's IsArray test. This is the
61 : * same test performed by |Array.isArray|.
62 : *
63 : * This is NOT the same as asking whether |obj| is an Array or a wrapper around
64 : * one. If |obj| is a proxy created by |Proxy.revocable()| and has been
65 : * revoked, or if |obj| is a proxy whose target (at any number of hops) is a
66 : * revoked proxy, this method throws a TypeError and returns false.
67 : */
68 : extern JS_PUBLIC_API(bool)
69 : IsArray(JSContext* cx, HandleObject obj, bool* isArray);
70 :
71 : /**
72 : * Identical to IsArray above, but the nature of the object (if successfully
73 : * determined) is communicated via |*answer|. In particular this method
74 : * returns true and sets |*answer = IsArrayAnswer::RevokedProxy| when called on
75 : * a revoked proxy.
76 : *
77 : * Most users will want the overload above, not this one.
78 : */
79 : extern JS_PUBLIC_API(bool)
80 : IsArray(JSContext* cx, HandleObject obj, IsArrayAnswer* answer);
81 :
82 : /**
83 : * Per ES6, the [[DefineOwnProperty]] internal method has three different
84 : * possible outcomes:
85 : *
86 : * - It can throw an exception (which we indicate by returning false).
87 : *
88 : * - It can return true, indicating unvarnished success.
89 : *
90 : * - It can return false, indicating "strict failure". The property could
91 : * not be defined. It's an error, but no exception was thrown.
92 : *
93 : * It's not just [[DefineOwnProperty]]: all the mutating internal methods have
94 : * the same three outcomes. (The other affected internal methods are [[Set]],
95 : * [[Delete]], [[SetPrototypeOf]], and [[PreventExtensions]].)
96 : *
97 : * If you think this design is awful, you're not alone. But as it's the
98 : * standard, we must represent these boolean "success" values somehow.
99 : * ObjectOpSuccess is the class for this. It's like a bool, but when it's false
100 : * it also stores an error code.
101 : *
102 : * Typical usage:
103 : *
104 : * ObjectOpResult result;
105 : * if (!DefineProperty(cx, obj, id, ..., result))
106 : * return false;
107 : * if (!result)
108 : * return result.reportError(cx, obj, id);
109 : *
110 : * Users don't have to call `result.report()`; another possible ending is:
111 : *
112 : * argv.rval().setBoolean(bool(result));
113 : * return true;
114 : */
115 : class ObjectOpResult
116 : {
117 : private:
118 : /**
119 : * code_ is either one of the special codes OkCode or Uninitialized, or
120 : * an error code. For now the error codes are private to the JS engine;
121 : * they're defined in js/src/js.msg.
122 : *
123 : * code_ is uintptr_t (rather than uint32_t) for the convenience of the
124 : * JITs, which would otherwise have to deal with either padding or stack
125 : * alignment on 64-bit platforms.
126 : */
127 : uintptr_t code_;
128 :
129 : public:
130 : enum SpecialCodes : uintptr_t {
131 : OkCode = 0,
132 : Uninitialized = uintptr_t(-1)
133 : };
134 :
135 142171 : ObjectOpResult() : code_(Uninitialized) {}
136 :
137 : /* Return true if succeed() was called. */
138 139593 : bool ok() const {
139 139593 : MOZ_ASSERT(code_ != Uninitialized);
140 139593 : return code_ == OkCode;
141 : }
142 :
143 124890 : explicit operator bool() const { return ok(); }
144 :
145 : /* Set this ObjectOpResult to true and return true. */
146 140768 : bool succeed() {
147 140768 : code_ = OkCode;
148 140768 : return true;
149 : }
150 :
151 : /*
152 : * Set this ObjectOpResult to false with an error code.
153 : *
154 : * Always returns true, as a convenience. Typical usage will be:
155 : *
156 : * if (funny condition)
157 : * return result.fail(JSMSG_CANT_DO_THE_THINGS);
158 : *
159 : * The true return value indicates that no exception is pending, and it
160 : * would be OK to ignore the failure and continue.
161 : */
162 11 : bool fail(uint32_t msg) {
163 11 : MOZ_ASSERT(msg != OkCode);
164 11 : code_ = msg;
165 11 : return true;
166 : }
167 :
168 : JS_PUBLIC_API(bool) failCantRedefineProp();
169 : JS_PUBLIC_API(bool) failReadOnly();
170 : JS_PUBLIC_API(bool) failGetterOnly();
171 : JS_PUBLIC_API(bool) failCantDelete();
172 :
173 : JS_PUBLIC_API(bool) failCantSetInterposed();
174 : JS_PUBLIC_API(bool) failCantDefineWindowElement();
175 : JS_PUBLIC_API(bool) failCantDeleteWindowElement();
176 : JS_PUBLIC_API(bool) failCantDeleteWindowNamedProperty();
177 : JS_PUBLIC_API(bool) failCantPreventExtensions();
178 : JS_PUBLIC_API(bool) failCantSetProto();
179 : JS_PUBLIC_API(bool) failNoNamedSetter();
180 : JS_PUBLIC_API(bool) failNoIndexedSetter();
181 :
182 0 : uint32_t failureCode() const {
183 0 : MOZ_ASSERT(!ok());
184 0 : return uint32_t(code_);
185 : }
186 :
187 : /*
188 : * Report an error or warning if necessary; return true to proceed and
189 : * false if an error was reported. Call this when failure should cause
190 : * a warning if extraWarnings are enabled.
191 : *
192 : * The precise rules are like this:
193 : *
194 : * - If ok(), then we succeeded. Do nothing and return true.
195 : * - Otherwise, if |strict| is true, or if cx has both extraWarnings and
196 : * werrorOption enabled, throw a TypeError and return false.
197 : * - Otherwise, if cx has extraWarnings enabled, emit a warning and
198 : * return true.
199 : * - Otherwise, do nothing and return true.
200 : */
201 10783 : bool checkStrictErrorOrWarning(JSContext* cx, HandleObject obj, HandleId id, bool strict) {
202 10783 : if (ok())
203 10783 : return true;
204 0 : return reportStrictErrorOrWarning(cx, obj, id, strict);
205 : }
206 :
207 : /*
208 : * The same as checkStrictErrorOrWarning(cx, id, strict), except the
209 : * operation is not associated with a particular property id. This is
210 : * used for [[PreventExtensions]] and [[SetPrototypeOf]]. failureCode()
211 : * must not be an error that has "{0}" in the error message.
212 : */
213 3399 : bool checkStrictErrorOrWarning(JSContext* cx, HandleObject obj, bool strict) {
214 3399 : return ok() || reportStrictErrorOrWarning(cx, obj, strict);
215 : }
216 :
217 : /* Throw a TypeError. Call this only if !ok(). */
218 0 : bool reportError(JSContext* cx, HandleObject obj, HandleId id) {
219 0 : return reportStrictErrorOrWarning(cx, obj, id, true);
220 : }
221 :
222 : /*
223 : * The same as reportError(cx, obj, id), except the operation is not
224 : * associated with a particular property id.
225 : */
226 : bool reportError(JSContext* cx, HandleObject obj) {
227 : return reportStrictErrorOrWarning(cx, obj, true);
228 : }
229 :
230 : /* Helper function for checkStrictErrorOrWarning's slow path. */
231 : JS_PUBLIC_API(bool) reportStrictErrorOrWarning(JSContext* cx, HandleObject obj, HandleId id, bool strict);
232 : JS_PUBLIC_API(bool) reportStrictErrorOrWarning(JSContext* cx, HandleObject obj, bool strict);
233 :
234 : /*
235 : * Convenience method. Return true if ok() or if strict is false; otherwise
236 : * throw a TypeError and return false.
237 : */
238 2944 : bool checkStrict(JSContext* cx, HandleObject obj, HandleId id) {
239 2944 : return checkStrictErrorOrWarning(cx, obj, id, true);
240 : }
241 :
242 : /*
243 : * Convenience method. The same as checkStrict(cx, id), except the
244 : * operation is not associated with a particular property id.
245 : */
246 3399 : bool checkStrict(JSContext* cx, HandleObject obj) {
247 3399 : return checkStrictErrorOrWarning(cx, obj, true);
248 : }
249 : };
250 :
251 : class PropertyResult
252 : {
253 : union {
254 : js::Shape* shape_;
255 : uintptr_t bits_;
256 : };
257 :
258 : static const uintptr_t NotFound = 0;
259 : static const uintptr_t NonNativeProperty = 1;
260 : static const uintptr_t DenseOrTypedArrayElement = 1;
261 :
262 : public:
263 259710 : PropertyResult() : bits_(NotFound) {}
264 :
265 725 : explicit PropertyResult(js::Shape* propertyShape)
266 725 : : shape_(propertyShape)
267 : {
268 725 : MOZ_ASSERT(!isFound() || isNativeProperty());
269 725 : }
270 :
271 433336 : explicit operator bool() const {
272 433336 : return isFound();
273 : }
274 :
275 666551 : bool isFound() const {
276 666551 : return bits_ != NotFound;
277 : }
278 :
279 227795 : bool isNonNativeProperty() const {
280 227795 : return bits_ == NonNativeProperty;
281 : }
282 :
283 87503 : bool isDenseOrTypedArrayElement() const {
284 87503 : return bits_ == DenseOrTypedArrayElement;
285 : }
286 :
287 223368 : bool isNativeProperty() const {
288 223368 : return isFound() && !isNonNativeProperty();
289 : }
290 :
291 3915 : js::Shape* maybeShape() const {
292 3915 : MOZ_ASSERT(!isNonNativeProperty());
293 3915 : return isFound() ? shape_ : nullptr;
294 : }
295 :
296 126610 : js::Shape* shape() const {
297 126610 : MOZ_ASSERT(isNativeProperty());
298 126610 : return shape_;
299 : }
300 :
301 258120 : void setNotFound() {
302 258120 : bits_ = NotFound;
303 258120 : }
304 :
305 95971 : void setNativeProperty(js::Shape* propertyShape) {
306 95971 : shape_ = propertyShape;
307 95971 : MOZ_ASSERT(isNativeProperty());
308 95971 : }
309 :
310 59 : void setNonNativeProperty() {
311 59 : bits_ = NonNativeProperty;
312 59 : }
313 :
314 4267 : void setDenseOrTypedArrayElement() {
315 4267 : bits_ = DenseOrTypedArrayElement;
316 4267 : }
317 :
318 : void trace(JSTracer* trc);
319 : };
320 :
321 : } // namespace JS
322 :
323 : namespace js {
324 :
325 : template <class Wrapper>
326 562479 : class WrappedPtrOperations<JS::PropertyResult, Wrapper>
327 : {
328 530417 : const JS::PropertyResult& value() const { return static_cast<const Wrapper*>(this)->get(); }
329 :
330 : public:
331 5060 : bool isFound() const { return value().isFound(); }
332 358668 : explicit operator bool() const { return bool(value()); }
333 : js::Shape* maybeShape() const { return value().maybeShape(); }
334 92927 : js::Shape* shape() const { return value().shape(); }
335 0 : bool isNativeProperty() const { return value().isNativeProperty(); }
336 44 : bool isNonNativeProperty() const { return value().isNonNativeProperty(); }
337 73710 : bool isDenseOrTypedArrayElement() const { return value().isDenseOrTypedArrayElement(); }
338 : js::Shape* asTaggedShape() const { return value().asTaggedShape(); }
339 : };
340 :
341 : template <class Wrapper>
342 545122 : class MutableWrappedPtrOperations<JS::PropertyResult, Wrapper>
343 : : public WrappedPtrOperations<JS::PropertyResult, Wrapper>
344 : {
345 350521 : JS::PropertyResult& value() { return static_cast<Wrapper*>(this)->get(); }
346 :
347 : public:
348 254791 : void setNotFound() {
349 254791 : value().setNotFound();
350 254791 : }
351 91414 : void setNativeProperty(js::Shape* shape) {
352 91414 : value().setNativeProperty(shape);
353 91414 : }
354 51 : void setNonNativeProperty() {
355 51 : value().setNonNativeProperty();
356 51 : }
357 4267 : void setDenseOrTypedArrayElement() {
358 4267 : value().setDenseOrTypedArrayElement();
359 4267 : }
360 : };
361 :
362 : } // namespace js
363 :
364 : // JSClass operation signatures.
365 :
366 : /**
367 : * Get a property named by id in obj. Note the jsid id type -- id may
368 : * be a string (Unicode property identifier) or an int (element index). The
369 : * *vp out parameter, on success, is the new property value after the action.
370 : */
371 : typedef bool
372 : (* JSGetterOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
373 : JS::MutableHandleValue vp);
374 :
375 : /** Add a property named by id to obj. */
376 : typedef bool
377 : (* JSAddPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue v);
378 :
379 : /**
380 : * Set a property named by id in obj, treating the assignment as strict
381 : * mode code if strict is true. Note the jsid id type -- id may be a string
382 : * (Unicode property identifier) or an int (element index). The *vp out
383 : * parameter, on success, is the new property value after the
384 : * set.
385 : */
386 : typedef bool
387 : (* JSSetterOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
388 : JS::MutableHandleValue vp, JS::ObjectOpResult& result);
389 :
390 : /**
391 : * Delete a property named by id in obj.
392 : *
393 : * If an error occurred, return false as per normal JSAPI error practice.
394 : *
395 : * If no error occurred, but the deletion attempt wasn't allowed (perhaps
396 : * because the property was non-configurable), call result.fail() and
397 : * return true. This will cause |delete obj[id]| to evaluate to false in
398 : * non-strict mode code, and to throw a TypeError in strict mode code.
399 : *
400 : * If no error occurred and the deletion wasn't disallowed (this is *not* the
401 : * same as saying that a deletion actually occurred -- deleting a non-existent
402 : * property, or an inherited property, is allowed -- it's just pointless),
403 : * call result.succeed() and return true.
404 : */
405 : typedef bool
406 : (* JSDeletePropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
407 : JS::ObjectOpResult& result);
408 :
409 : /**
410 : * The type of ObjectOps::enumerate. This callback overrides a portion of
411 : * SpiderMonkey's default [[Enumerate]] internal method. When an ordinary object
412 : * is enumerated, that object and each object on its prototype chain is tested
413 : * for an enumerate op, and those ops are called in order. The properties each
414 : * op adds to the 'properties' vector are added to the set of values the for-in
415 : * loop will iterate over. All of this is nonstandard.
416 : *
417 : * An object is "enumerated" when it's the target of a for-in loop or
418 : * JS_Enumerate(). The callback's job is to populate 'properties' with the
419 : * object's property keys. If `enumerableOnly` is true, the callback should only
420 : * add enumerable properties.
421 : */
422 : typedef bool
423 : (* JSNewEnumerateOp)(JSContext* cx, JS::HandleObject obj, JS::AutoIdVector& properties,
424 : bool enumerableOnly);
425 :
426 : /**
427 : * The old-style JSClass.enumerate op should define all lazy properties not
428 : * yet reflected in obj.
429 : */
430 : typedef bool
431 : (* JSEnumerateOp)(JSContext* cx, JS::HandleObject obj);
432 :
433 : /**
434 : * The type of ObjectOps::funToString. This callback allows an object to
435 : * provide a custom string to use when Function.prototype.toString is invoked on
436 : * that object. A null return value means OOM.
437 : */
438 : typedef JSString*
439 : (* JSFunToStringOp)(JSContext* cx, JS::HandleObject obj, unsigned indent);
440 :
441 : /**
442 : * Resolve a lazy property named by id in obj by defining it directly in obj.
443 : * Lazy properties are those reflected from some peer native property space
444 : * (e.g., the DOM attributes for a given node reflected as obj) on demand.
445 : *
446 : * JS looks for a property in an object, and if not found, tries to resolve
447 : * the given id. *resolvedp should be set to true iff the property was defined
448 : * on |obj|.
449 : */
450 : typedef bool
451 : (* JSResolveOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
452 : bool* resolvedp);
453 :
454 : /**
455 : * A class with a resolve hook can optionally have a mayResolve hook. This hook
456 : * must have no side effects and must return true for a given id if the resolve
457 : * hook may resolve this id. This is useful when we're doing a "pure" lookup: if
458 : * mayResolve returns false, we know we don't have to call the effectful resolve
459 : * hook.
460 : *
461 : * maybeObj, if non-null, is the object on which we're doing the lookup. This
462 : * can be nullptr: during JIT compilation we sometimes know the Class but not
463 : * the object.
464 : */
465 : typedef bool
466 : (* JSMayResolveOp)(const JSAtomState& names, jsid id, JSObject* maybeObj);
467 :
468 : /**
469 : * Finalize obj, which the garbage collector has determined to be unreachable
470 : * from other live objects or from GC roots. Obviously, finalizers must never
471 : * store a reference to obj.
472 : */
473 : typedef void
474 : (* JSFinalizeOp)(JSFreeOp* fop, JSObject* obj);
475 :
476 : /**
477 : * Finalizes external strings created by JS_NewExternalString. The finalizer
478 : * can be called off the main thread.
479 : */
480 : struct JSStringFinalizer {
481 : void (*finalize)(const JSStringFinalizer* fin, char16_t* chars);
482 : };
483 :
484 : /**
485 : * Check whether v is an instance of obj. Return false on error or exception,
486 : * true on success with true in *bp if v is an instance of obj, false in
487 : * *bp otherwise.
488 : */
489 : typedef bool
490 : (* JSHasInstanceOp)(JSContext* cx, JS::HandleObject obj, JS::MutableHandleValue vp,
491 : bool* bp);
492 :
493 : /**
494 : * Function type for trace operation of the class called to enumerate all
495 : * traceable things reachable from obj's private data structure. For each such
496 : * thing, a trace implementation must call JS::TraceEdge on the thing's
497 : * location.
498 : *
499 : * JSTraceOp implementation can assume that no other threads mutates object
500 : * state. It must not change state of the object or corresponding native
501 : * structures. The only exception for this rule is the case when the embedding
502 : * needs a tight integration with GC. In that case the embedding can check if
503 : * the traversal is a part of the marking phase through calling
504 : * JS_IsGCMarkingTracer and apply a special code like emptying caches or
505 : * marking its native structures.
506 : */
507 : typedef void
508 : (* JSTraceOp)(JSTracer* trc, JSObject* obj);
509 :
510 : typedef JSObject*
511 : (* JSWeakmapKeyDelegateOp)(JSObject* obj);
512 :
513 : typedef void
514 : (* JSObjectMovedOp)(JSObject* obj, const JSObject* old);
515 :
516 : /* js::Class operation signatures. */
517 :
518 : namespace js {
519 :
520 : typedef bool
521 : (* LookupPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
522 : JS::MutableHandleObject objp, JS::MutableHandle<JS::PropertyResult> propp);
523 : typedef bool
524 : (* DefinePropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
525 : JS::Handle<JS::PropertyDescriptor> desc,
526 : JS::ObjectOpResult& result);
527 : typedef bool
528 : (* HasPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* foundp);
529 : typedef bool
530 : (* GetPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleValue receiver, JS::HandleId id,
531 : JS::MutableHandleValue vp);
532 : typedef bool
533 : (* SetPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue v,
534 : JS::HandleValue receiver, JS::ObjectOpResult& result);
535 : typedef bool
536 : (* GetOwnPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
537 : JS::MutableHandle<JS::PropertyDescriptor> desc);
538 : typedef bool
539 : (* DeletePropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
540 : JS::ObjectOpResult& result);
541 :
542 : typedef bool
543 : (* WatchOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable);
544 :
545 : typedef bool
546 : (* UnwatchOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id);
547 :
548 35 : class JS_FRIEND_API(ElementAdder)
549 : {
550 : public:
551 : enum GetBehavior {
552 : // Check if the element exists before performing the Get and preserve
553 : // holes.
554 : CheckHasElemPreserveHoles,
555 :
556 : // Perform a Get operation, like obj[index] in JS.
557 : GetElement
558 : };
559 :
560 : private:
561 : // Only one of these is used.
562 : JS::RootedObject resObj_;
563 : JS::Value* vp_;
564 :
565 : uint32_t index_;
566 : #ifdef DEBUG
567 : uint32_t length_;
568 : #endif
569 : GetBehavior getBehavior_;
570 :
571 : public:
572 0 : ElementAdder(JSContext* cx, JSObject* obj, uint32_t length, GetBehavior behavior)
573 0 : : resObj_(cx, obj), vp_(nullptr), index_(0),
574 : #ifdef DEBUG
575 : length_(length),
576 : #endif
577 0 : getBehavior_(behavior)
578 0 : {}
579 35 : ElementAdder(JSContext* cx, JS::Value* vp, uint32_t length, GetBehavior behavior)
580 35 : : resObj_(cx), vp_(vp), index_(0),
581 : #ifdef DEBUG
582 : length_(length),
583 : #endif
584 35 : getBehavior_(behavior)
585 35 : {}
586 :
587 132 : GetBehavior getBehavior() const { return getBehavior_; }
588 :
589 : bool append(JSContext* cx, JS::HandleValue v);
590 : void appendHole();
591 : };
592 :
593 : typedef bool
594 : (* GetElementsOp)(JSContext* cx, JS::HandleObject obj, uint32_t begin, uint32_t end,
595 : ElementAdder* adder);
596 :
597 : typedef void
598 : (* FinalizeOp)(FreeOp* fop, JSObject* obj);
599 :
600 : // The special treatment of |finalize| and |trace| is necessary because if we
601 : // assign either of those hooks to a local variable and then call it -- as is
602 : // done with the other hooks -- the GC hazard analysis gets confused.
603 : #define JS_CLASS_MEMBERS(ClassOpsType, FreeOpType) \
604 : const char* name; \
605 : uint32_t flags; \
606 : const ClassOpsType* cOps; \
607 : \
608 : JSAddPropertyOp getAddProperty() const { return cOps ? cOps->addProperty : nullptr; } \
609 : JSDeletePropertyOp getDelProperty() const { return cOps ? cOps->delProperty : nullptr; } \
610 : JSGetterOp getGetProperty() const { return cOps ? cOps->getProperty : nullptr; } \
611 : JSSetterOp getSetProperty() const { return cOps ? cOps->setProperty : nullptr; } \
612 : JSEnumerateOp getEnumerate() const { return cOps ? cOps->enumerate : nullptr; } \
613 : JSNewEnumerateOp getNewEnumerate()const { return cOps ? cOps->newEnumerate: nullptr; } \
614 : JSResolveOp getResolve() const { return cOps ? cOps->resolve : nullptr; } \
615 : JSMayResolveOp getMayResolve() const { return cOps ? cOps->mayResolve : nullptr; } \
616 : JSNative getCall() const { return cOps ? cOps->call : nullptr; } \
617 : JSHasInstanceOp getHasInstance() const { return cOps ? cOps->hasInstance : nullptr; } \
618 : JSNative getConstruct() const { return cOps ? cOps->construct : nullptr; } \
619 : \
620 : bool hasFinalize() const { return cOps && cOps->finalize; } \
621 : bool hasTrace() const { return cOps && cOps->trace; } \
622 : \
623 : bool isTrace(JSTraceOp trace) const { return cOps && cOps->trace == trace; } \
624 : \
625 : void doFinalize(FreeOpType* fop, JSObject* obj) const { \
626 : MOZ_ASSERT(cOps && cOps->finalize); \
627 : cOps->finalize(fop, obj); \
628 : } \
629 : void doTrace(JSTracer* trc, JSObject* obj) const { \
630 : MOZ_ASSERT(cOps && cOps->trace); \
631 : cOps->trace(trc, obj); \
632 : }
633 :
634 : // XXX: MOZ_NONHEAP_CLASS allows objects to be created statically or on the
635 : // stack. We actually want to ban stack objects too, but that's currently not
636 : // possible. So we define JS_STATIC_CLASS to make the intention clearer.
637 : #define JS_STATIC_CLASS MOZ_NONHEAP_CLASS
638 :
639 : struct JS_STATIC_CLASS ClassOps
640 : {
641 : /* Function pointer members (may be null). */
642 : JSAddPropertyOp addProperty;
643 : JSDeletePropertyOp delProperty;
644 : JSGetterOp getProperty;
645 : JSSetterOp setProperty;
646 : JSEnumerateOp enumerate;
647 : JSNewEnumerateOp newEnumerate;
648 : JSResolveOp resolve;
649 : JSMayResolveOp mayResolve;
650 : FinalizeOp finalize;
651 : JSNative call;
652 : JSHasInstanceOp hasInstance;
653 : JSNative construct;
654 : JSTraceOp trace;
655 : };
656 :
657 : /** Callback for the creation of constructor and prototype objects. */
658 : typedef JSObject* (*ClassObjectCreationOp)(JSContext* cx, JSProtoKey key);
659 :
660 : /** Callback for custom post-processing after class initialization via ClassSpec. */
661 : typedef bool (*FinishClassInitOp)(JSContext* cx, JS::HandleObject ctor,
662 : JS::HandleObject proto);
663 :
664 : const size_t JSCLASS_CACHED_PROTO_WIDTH = 6;
665 :
666 : struct JS_STATIC_CLASS ClassSpec
667 : {
668 : ClassObjectCreationOp createConstructor;
669 : ClassObjectCreationOp createPrototype;
670 : const JSFunctionSpec* constructorFunctions;
671 : const JSPropertySpec* constructorProperties;
672 : const JSFunctionSpec* prototypeFunctions;
673 : const JSPropertySpec* prototypeProperties;
674 : FinishClassInitOp finishInit;
675 : uintptr_t flags;
676 :
677 : static const size_t ProtoKeyWidth = JSCLASS_CACHED_PROTO_WIDTH;
678 :
679 : static const uintptr_t ProtoKeyMask = (1 << ProtoKeyWidth) - 1;
680 : static const uintptr_t DontDefineConstructor = 1 << ProtoKeyWidth;
681 :
682 3023 : bool defined() const { return !!createConstructor; }
683 :
684 : // The ProtoKey this class inherits from.
685 71 : JSProtoKey inheritanceProtoKey() const {
686 71 : MOZ_ASSERT(defined());
687 : static_assert(JSProto_Null == 0, "zeroed key must be null");
688 :
689 : // Default: Inherit from Object.
690 71 : if (!(flags & ProtoKeyMask))
691 71 : return JSProto_Object;
692 :
693 0 : return JSProtoKey(flags & ProtoKeyMask);
694 : }
695 :
696 1403 : bool shouldDefineConstructor() const {
697 1403 : MOZ_ASSERT(defined());
698 1403 : return !(flags & DontDefineConstructor);
699 : }
700 : };
701 :
702 : struct JS_STATIC_CLASS ClassExtension
703 : {
704 : /**
705 : * If an object is used as a key in a weakmap, it may be desirable for the
706 : * garbage collector to keep that object around longer than it otherwise
707 : * would. A common case is when the key is a wrapper around an object in
708 : * another compartment, and we want to avoid collecting the wrapper (and
709 : * removing the weakmap entry) as long as the wrapped object is alive. In
710 : * that case, the wrapped object is returned by the wrapper's
711 : * weakmapKeyDelegateOp hook. As long as the wrapper is used as a weakmap
712 : * key, it will not be collected (and remain in the weakmap) until the
713 : * wrapped object is collected.
714 : */
715 : JSWeakmapKeyDelegateOp weakmapKeyDelegateOp;
716 :
717 : /**
718 : * Optional hook called when an object is moved by a compacting GC.
719 : *
720 : * There may exist weak pointers to an object that are not traced through
721 : * when the normal trace APIs are used, for example objects in the wrapper
722 : * cache. This hook allows these pointers to be updated.
723 : *
724 : * Note that this hook can be called before JS_NewObject() returns if a GC
725 : * is triggered during construction of the object. This can happen for
726 : * global objects for example.
727 : */
728 : JSObjectMovedOp objectMovedOp;
729 : };
730 :
731 : #define JS_NULL_CLASS_SPEC nullptr
732 : #define JS_NULL_CLASS_EXT nullptr
733 :
734 : struct JS_STATIC_CLASS ObjectOps
735 : {
736 : LookupPropertyOp lookupProperty;
737 : DefinePropertyOp defineProperty;
738 : HasPropertyOp hasProperty;
739 : GetPropertyOp getProperty;
740 : SetPropertyOp setProperty;
741 : GetOwnPropertyOp getOwnPropertyDescriptor;
742 : DeletePropertyOp deleteProperty;
743 : WatchOp watch;
744 : UnwatchOp unwatch;
745 : GetElementsOp getElements;
746 : JSFunToStringOp funToString;
747 : };
748 :
749 : #define JS_NULL_OBJECT_OPS nullptr
750 :
751 : } // namespace js
752 :
753 : // Classes, objects, and properties.
754 :
755 : typedef void (*JSClassInternal)();
756 :
757 : struct JS_STATIC_CLASS JSClassOps
758 : {
759 : /* Function pointer members (may be null). */
760 : JSAddPropertyOp addProperty;
761 : JSDeletePropertyOp delProperty;
762 : JSGetterOp getProperty;
763 : JSSetterOp setProperty;
764 : JSEnumerateOp enumerate;
765 : JSNewEnumerateOp newEnumerate;
766 : JSResolveOp resolve;
767 : JSMayResolveOp mayResolve;
768 : JSFinalizeOp finalize;
769 : JSNative call;
770 : JSHasInstanceOp hasInstance;
771 : JSNative construct;
772 : JSTraceOp trace;
773 : };
774 :
775 : #define JS_NULL_CLASS_OPS nullptr
776 :
777 : struct JSClass {
778 10960 : JS_CLASS_MEMBERS(JSClassOps, JSFreeOp);
779 :
780 : void* reserved[3];
781 : };
782 :
783 : // Objects have private slot.
784 : static const uint32_t JSCLASS_HAS_PRIVATE = 1 << 0;
785 :
786 : // Class's initialization code will call `SetNewObjectMetadata` itself.
787 : static const uint32_t JSCLASS_DELAY_METADATA_BUILDER = 1 << 1;
788 :
789 : // Class is an XPCWrappedNative. WeakMaps use this to override the wrapper
790 : // disposal mechanism.
791 : static const uint32_t JSCLASS_IS_WRAPPED_NATIVE = 1 << 2;
792 :
793 : // Private is `nsISupports*`.
794 : static const uint32_t JSCLASS_PRIVATE_IS_NSISUPPORTS = 1 << 3;
795 :
796 : // Objects are DOM.
797 : static const uint32_t JSCLASS_IS_DOMJSCLASS = 1 << 4;
798 :
799 : // If wrapped by an xray wrapper, the builtin class's constructor won't be
800 : // unwrapped and invoked. Instead, the constructor is resolved in the caller's
801 : // compartment and invoked with a wrapped newTarget. The constructor has to
802 : // detect and handle this situation. See PromiseConstructor for details.
803 : static const uint32_t JSCLASS_HAS_XRAYED_CONSTRUCTOR = 1 << 5;
804 :
805 : // Objects of this class act like the value undefined, in some contexts.
806 : static const uint32_t JSCLASS_EMULATES_UNDEFINED = 1 << 6;
807 :
808 : // Reserved for embeddings.
809 : static const uint32_t JSCLASS_USERBIT1 = 1 << 7;
810 :
811 : // To reserve slots fetched and stored via JS_Get/SetReservedSlot, bitwise-or
812 : // JSCLASS_HAS_RESERVED_SLOTS(n) into the initializer for JSClass.flags, where n
813 : // is a constant in [1, 255]. Reserved slots are indexed from 0 to n-1.
814 :
815 : // Room for 8 flags below ...
816 : static const uintptr_t JSCLASS_RESERVED_SLOTS_SHIFT = 8;
817 : // ... and 16 above this field.
818 : static const uint32_t JSCLASS_RESERVED_SLOTS_WIDTH = 8;
819 :
820 : static const uint32_t JSCLASS_RESERVED_SLOTS_MASK = JS_BITMASK(JSCLASS_RESERVED_SLOTS_WIDTH);
821 :
822 : #define JSCLASS_HAS_RESERVED_SLOTS(n) (((n) & JSCLASS_RESERVED_SLOTS_MASK) \
823 : << JSCLASS_RESERVED_SLOTS_SHIFT)
824 : #define JSCLASS_RESERVED_SLOTS(clasp) (((clasp)->flags \
825 : >> JSCLASS_RESERVED_SLOTS_SHIFT) \
826 : & JSCLASS_RESERVED_SLOTS_MASK)
827 :
828 : #define JSCLASS_HIGH_FLAGS_SHIFT (JSCLASS_RESERVED_SLOTS_SHIFT + \
829 : JSCLASS_RESERVED_SLOTS_WIDTH)
830 :
831 : static const uint32_t JSCLASS_IS_ANONYMOUS = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 0);
832 : static const uint32_t JSCLASS_IS_GLOBAL = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 1);
833 : static const uint32_t JSCLASS_INTERNAL_FLAG2 = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 2);
834 : static const uint32_t JSCLASS_INTERNAL_FLAG3 = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 3);
835 : static const uint32_t JSCLASS_IS_PROXY = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 4);
836 : static const uint32_t JSCLASS_SKIP_NURSERY_FINALIZE = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 5);
837 :
838 : // Reserved for embeddings.
839 : static const uint32_t JSCLASS_USERBIT2 = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 6);
840 : static const uint32_t JSCLASS_USERBIT3 = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 7);
841 :
842 : static const uint32_t JSCLASS_BACKGROUND_FINALIZE = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 8);
843 : static const uint32_t JSCLASS_FOREGROUND_FINALIZE = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 9);
844 :
845 : // Bits 26 through 31 are reserved for the CACHED_PROTO_KEY mechanism, see
846 : // below.
847 :
848 : // ECMA-262 requires that most constructors used internally create objects
849 : // with "the original Foo.prototype value" as their [[Prototype]] (__proto__)
850 : // member initial value. The "original ... value" verbiage is there because
851 : // in ECMA-262, global properties naming class objects are read/write and
852 : // deleteable, for the most part.
853 : //
854 : // Implementing this efficiently requires that global objects have classes
855 : // with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
856 : // previously allowed, but is now an ES5 violation and thus unsupported.
857 : //
858 : // JSCLASS_GLOBAL_APPLICATION_SLOTS is the number of slots reserved at
859 : // the beginning of every global object's slots for use by the
860 : // application.
861 : static const uint32_t JSCLASS_GLOBAL_APPLICATION_SLOTS = 5;
862 : static const uint32_t JSCLASS_GLOBAL_SLOT_COUNT =
863 : JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 37;
864 :
865 : #define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
866 : (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
867 : #define JSCLASS_GLOBAL_FLAGS \
868 : JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0)
869 : #define JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(clasp) \
870 : (((clasp)->flags & JSCLASS_IS_GLOBAL) \
871 : && JSCLASS_RESERVED_SLOTS(clasp) >= JSCLASS_GLOBAL_SLOT_COUNT)
872 :
873 : // Fast access to the original value of each standard class's prototype.
874 : static const uint32_t JSCLASS_CACHED_PROTO_SHIFT = JSCLASS_HIGH_FLAGS_SHIFT + 10;
875 : static const uint32_t JSCLASS_CACHED_PROTO_MASK = JS_BITMASK(js::JSCLASS_CACHED_PROTO_WIDTH);
876 :
877 : #define JSCLASS_HAS_CACHED_PROTO(key) (uint32_t(key) << JSCLASS_CACHED_PROTO_SHIFT)
878 : #define JSCLASS_CACHED_PROTO_KEY(clasp) ((JSProtoKey) \
879 : (((clasp)->flags \
880 : >> JSCLASS_CACHED_PROTO_SHIFT) \
881 : & JSCLASS_CACHED_PROTO_MASK))
882 :
883 : // Initializer for unused members of statically initialized JSClass structs.
884 : #define JSCLASS_NO_INTERNAL_MEMBERS {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
885 : #define JSCLASS_NO_OPTIONAL_MEMBERS 0,0,0,0,0,JSCLASS_NO_INTERNAL_MEMBERS
886 :
887 : namespace js {
888 :
889 : struct JS_STATIC_CLASS Class
890 : {
891 1094886 : JS_CLASS_MEMBERS(js::ClassOps, FreeOp);
892 : const ClassSpec* spec;
893 : const ClassExtension* ext;
894 : const ObjectOps* oOps;
895 :
896 : /*
897 : * Objects of this class aren't native objects. They don't have Shapes that
898 : * describe their properties and layout. Classes using this flag must
899 : * provide their own property behavior, either by being proxy classes (do
900 : * this) or by overriding all the ObjectOps except getElements, watch and
901 : * unwatch (don't do this).
902 : */
903 : static const uint32_t NON_NATIVE = JSCLASS_INTERNAL_FLAG2;
904 :
905 3584655 : bool isNative() const {
906 3584655 : return !(flags & NON_NATIVE);
907 : }
908 :
909 168273 : bool hasPrivate() const {
910 168273 : return !!(flags & JSCLASS_HAS_PRIVATE);
911 : }
912 :
913 13788 : bool emulatesUndefined() const {
914 13788 : return flags & JSCLASS_EMULATES_UNDEFINED;
915 : }
916 :
917 131379 : bool isJSFunction() const {
918 131379 : return this == js::FunctionClassPtr;
919 : }
920 :
921 1 : bool nonProxyCallable() const {
922 1 : MOZ_ASSERT(!isProxy());
923 1 : return isJSFunction() || getCall();
924 : }
925 :
926 8312126 : bool isProxy() const {
927 8312126 : return flags & JSCLASS_IS_PROXY;
928 : }
929 :
930 9584 : bool isDOMClass() const {
931 9584 : return flags & JSCLASS_IS_DOMJSCLASS;
932 : }
933 :
934 178162 : bool shouldDelayMetadataBuilder() const {
935 178162 : return flags & JSCLASS_DELAY_METADATA_BUILDER;
936 : }
937 :
938 127538 : bool isWrappedNative() const {
939 127538 : return flags & JSCLASS_IS_WRAPPED_NATIVE;
940 : }
941 :
942 126 : static size_t offsetOfFlags() { return offsetof(Class, flags); }
943 :
944 1796 : bool specDefined() const { return spec ? spec->defined() : false; }
945 71 : JSProtoKey specInheritanceProtoKey()
946 71 : const { return spec ? spec->inheritanceProtoKey() : JSProto_Null; }
947 1403 : bool specShouldDefineConstructor()
948 1403 : const { return spec ? spec->shouldDefineConstructor() : true; }
949 1414 : ClassObjectCreationOp specCreateConstructorHook()
950 1414 : const { return spec ? spec->createConstructor : nullptr; }
951 1414 : ClassObjectCreationOp specCreatePrototypeHook()
952 1414 : const { return spec ? spec->createPrototype : nullptr; }
953 1405 : const JSFunctionSpec* specConstructorFunctions()
954 1405 : const { return spec ? spec->constructorFunctions : nullptr; }
955 1405 : const JSPropertySpec* specConstructorProperties()
956 1405 : const { return spec ? spec->constructorProperties : nullptr; }
957 1448 : const JSFunctionSpec* specPrototypeFunctions()
958 1448 : const { return spec ? spec->prototypeFunctions : nullptr; }
959 1448 : const JSPropertySpec* specPrototypeProperties()
960 1448 : const { return spec ? spec->prototypeProperties : nullptr; }
961 1403 : FinishClassInitOp specFinishInitHook()
962 1403 : const { return spec ? spec->finishInit : nullptr; }
963 :
964 117 : JSWeakmapKeyDelegateOp extWeakmapKeyDelegateOp()
965 117 : const { return ext ? ext->weakmapKeyDelegateOp : nullptr; }
966 25276 : JSObjectMovedOp extObjectMovedOp()
967 25276 : const { return ext ? ext->objectMovedOp : nullptr; }
968 :
969 49334 : LookupPropertyOp getOpsLookupProperty() const { return oOps ? oOps->lookupProperty : nullptr; }
970 98392 : DefinePropertyOp getOpsDefineProperty() const { return oOps ? oOps->defineProperty : nullptr; }
971 20665 : HasPropertyOp getOpsHasProperty() const { return oOps ? oOps->hasProperty : nullptr; }
972 98787 : GetPropertyOp getOpsGetProperty() const { return oOps ? oOps->getProperty : nullptr; }
973 10540 : SetPropertyOp getOpsSetProperty() const { return oOps ? oOps->setProperty : nullptr; }
974 20160 : GetOwnPropertyOp getOpsGetOwnPropertyDescriptor()
975 20160 : const { return oOps ? oOps->getOwnPropertyDescriptor
976 20160 : : nullptr; }
977 874 : DeletePropertyOp getOpsDeleteProperty() const { return oOps ? oOps->deleteProperty : nullptr; }
978 0 : WatchOp getOpsWatch() const { return oOps ? oOps->watch : nullptr; }
979 0 : UnwatchOp getOpsUnwatch() const { return oOps ? oOps->unwatch : nullptr; }
980 84 : GetElementsOp getOpsGetElements() const { return oOps ? oOps->getElements : nullptr; }
981 0 : JSFunToStringOp getOpsFunToString() const { return oOps ? oOps->funToString : nullptr; }
982 : };
983 :
984 : static_assert(offsetof(JSClassOps, addProperty) == offsetof(ClassOps, addProperty),
985 : "ClassOps and JSClassOps must be consistent");
986 : static_assert(offsetof(JSClassOps, delProperty) == offsetof(ClassOps, delProperty),
987 : "ClassOps and JSClassOps must be consistent");
988 : static_assert(offsetof(JSClassOps, getProperty) == offsetof(ClassOps, getProperty),
989 : "ClassOps and JSClassOps must be consistent");
990 : static_assert(offsetof(JSClassOps, setProperty) == offsetof(ClassOps, setProperty),
991 : "ClassOps and JSClassOps must be consistent");
992 : static_assert(offsetof(JSClassOps, enumerate) == offsetof(ClassOps, enumerate),
993 : "ClassOps and JSClassOps must be consistent");
994 : static_assert(offsetof(JSClassOps, newEnumerate) == offsetof(ClassOps, newEnumerate),
995 : "ClassOps and JSClassOps must be consistent");
996 : static_assert(offsetof(JSClassOps, resolve) == offsetof(ClassOps, resolve),
997 : "ClassOps and JSClassOps must be consistent");
998 : static_assert(offsetof(JSClassOps, mayResolve) == offsetof(ClassOps, mayResolve),
999 : "ClassOps and JSClassOps must be consistent");
1000 : static_assert(offsetof(JSClassOps, finalize) == offsetof(ClassOps, finalize),
1001 : "ClassOps and JSClassOps must be consistent");
1002 : static_assert(offsetof(JSClassOps, call) == offsetof(ClassOps, call),
1003 : "ClassOps and JSClassOps must be consistent");
1004 : static_assert(offsetof(JSClassOps, construct) == offsetof(ClassOps, construct),
1005 : "ClassOps and JSClassOps must be consistent");
1006 : static_assert(offsetof(JSClassOps, hasInstance) == offsetof(ClassOps, hasInstance),
1007 : "ClassOps and JSClassOps must be consistent");
1008 : static_assert(offsetof(JSClassOps, trace) == offsetof(ClassOps, trace),
1009 : "ClassOps and JSClassOps must be consistent");
1010 : static_assert(sizeof(JSClassOps) == sizeof(ClassOps),
1011 : "ClassOps and JSClassOps must be consistent");
1012 :
1013 : static_assert(offsetof(JSClass, name) == offsetof(Class, name),
1014 : "Class and JSClass must be consistent");
1015 : static_assert(offsetof(JSClass, flags) == offsetof(Class, flags),
1016 : "Class and JSClass must be consistent");
1017 : static_assert(offsetof(JSClass, cOps) == offsetof(Class, cOps),
1018 : "Class and JSClass must be consistent");
1019 : static_assert(sizeof(JSClass) == sizeof(Class),
1020 : "Class and JSClass must be consistent");
1021 :
1022 : static MOZ_ALWAYS_INLINE const JSClass*
1023 65702 : Jsvalify(const Class* c)
1024 : {
1025 65702 : return (const JSClass*)c;
1026 : }
1027 :
1028 : static MOZ_ALWAYS_INLINE const Class*
1029 9199 : Valueify(const JSClass* c)
1030 : {
1031 9199 : return (const Class*)c;
1032 : }
1033 :
1034 : /**
1035 : * Enumeration describing possible values of the [[Class]] internal property
1036 : * value of objects.
1037 : */
1038 : enum class ESClass {
1039 : Object,
1040 : Array,
1041 : Number,
1042 : String,
1043 : Boolean,
1044 : RegExp,
1045 : ArrayBuffer,
1046 : SharedArrayBuffer,
1047 : Date,
1048 : Set,
1049 : Map,
1050 : Promise,
1051 : MapIterator,
1052 : SetIterator,
1053 : Arguments,
1054 : Error,
1055 :
1056 : /** None of the above. */
1057 : Other
1058 : };
1059 :
1060 : /* Fills |vp| with the unboxed value for boxed types, or undefined otherwise. */
1061 : bool
1062 : Unbox(JSContext* cx, JS::HandleObject obj, JS::MutableHandleValue vp);
1063 :
1064 : #ifdef DEBUG
1065 : JS_FRIEND_API(bool)
1066 : HasObjectMovedOp(JSObject* obj);
1067 : #endif
1068 :
1069 : } /* namespace js */
1070 :
1071 : #endif /* js_Class_h */
|