Thu, 15 Jan 2015 15:55:04 +0100
Back out 97036ab72558 which inappropriately compared turds to third parties.
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/. */
7 #ifndef jsfun_h
8 #define jsfun_h
10 /*
11 * JS function definitions.
12 */
14 #include "jsobj.h"
15 #include "jsscript.h"
16 #include "jstypes.h"
18 namespace js {
19 class FunctionExtended;
21 typedef JSNative Native;
22 typedef JSParallelNative ParallelNative;
23 typedef JSThreadSafeNative ThreadSafeNative;
24 }
26 struct JSAtomState;
28 class JSFunction : public JSObject
29 {
30 public:
31 static const js::Class class_;
33 enum Flags {
34 INTERPRETED = 0x0001, /* function has a JSScript and environment. */
35 NATIVE_CTOR = 0x0002, /* native that can be called as a constructor */
36 EXTENDED = 0x0004, /* structure is FunctionExtended */
37 IS_FUN_PROTO = 0x0010, /* function is Function.prototype for some global object */
38 EXPR_CLOSURE = 0x0020, /* expression closure: function(x) x*x */
39 HAS_GUESSED_ATOM = 0x0040, /* function had no explicit name, but a
40 name was guessed for it anyway */
41 LAMBDA = 0x0080, /* function comes from a FunctionExpression, ArrowFunction, or
42 Function() call (not a FunctionDeclaration or nonstandard
43 function-statement) */
44 SELF_HOSTED = 0x0100, /* function is self-hosted builtin and must not be
45 decompilable nor constructible. */
46 SELF_HOSTED_CTOR = 0x0200, /* function is self-hosted builtin constructor and
47 must be constructible but not decompilable. */
48 HAS_REST = 0x0400, /* function has a rest (...) parameter */
49 // 0x0800 is available
50 INTERPRETED_LAZY = 0x1000, /* function is interpreted but doesn't have a script yet */
51 ARROW = 0x2000, /* ES6 '(args) => body' syntax */
53 /* Derived Flags values for convenience: */
54 NATIVE_FUN = 0,
55 NATIVE_LAMBDA_FUN = NATIVE_FUN | LAMBDA,
56 INTERPRETED_LAMBDA = INTERPRETED | LAMBDA,
57 INTERPRETED_LAMBDA_ARROW = INTERPRETED | LAMBDA | ARROW
58 };
60 static void staticAsserts() {
61 JS_STATIC_ASSERT(INTERPRETED == JS_FUNCTION_INTERPRETED_BIT);
62 static_assert(sizeof(JSFunction) == sizeof(js::shadow::Function),
63 "shadow interface must match actual interface");
64 }
66 private:
67 uint16_t nargs_; /* number of formal arguments
68 (including defaults and the rest parameter unlike f.length) */
69 uint16_t flags_; /* bitfield composed of the above Flags enum */
70 union U {
71 class Native {
72 friend class JSFunction;
73 js::Native native; /* native method pointer or null */
74 const JSJitInfo *jitinfo; /* Information about this function to be
75 used by the JIT;
76 use the accessor! */
77 } n;
78 struct Scripted {
79 union {
80 JSScript *script_; /* interpreted bytecode descriptor or null;
81 use the accessor! */
82 js::LazyScript *lazy_; /* lazily compiled script, or nullptr */
83 } s;
84 JSObject *env_; /* environment for new activations;
85 use the accessor! */
86 } i;
87 void *nativeOrScript;
88 } u;
89 js::HeapPtrAtom atom_; /* name for diagnostics and decompiling */
91 public:
93 /* Call objects must be created for each invocation of a heavyweight function. */
94 bool isHeavyweight() const {
95 JS_ASSERT(!isInterpretedLazy());
97 if (isNative())
98 return false;
100 // Note: this should be kept in sync with FunctionBox::isHeavyweight().
101 return nonLazyScript()->hasAnyAliasedBindings() ||
102 nonLazyScript()->funHasExtensibleScope() ||
103 nonLazyScript()->funNeedsDeclEnvObject() ||
104 isGenerator();
105 }
107 size_t nargs() const {
108 return nargs_;
109 }
111 uint16_t flags() const {
112 return flags_;
113 }
115 /* A function can be classified as either native (C++) or interpreted (JS): */
116 bool isInterpreted() const { return flags() & (INTERPRETED | INTERPRETED_LAZY); }
117 bool isNative() const { return !isInterpreted(); }
119 /* Possible attributes of a native function: */
120 bool isNativeConstructor() const { return flags() & NATIVE_CTOR; }
122 /* Possible attributes of an interpreted function: */
123 bool isFunctionPrototype() const { return flags() & IS_FUN_PROTO; }
124 bool isExprClosure() const { return flags() & EXPR_CLOSURE; }
125 bool hasGuessedAtom() const { return flags() & HAS_GUESSED_ATOM; }
126 bool isLambda() const { return flags() & LAMBDA; }
127 bool isSelfHostedBuiltin() const { return flags() & SELF_HOSTED; }
128 bool isSelfHostedConstructor() const { return flags() & SELF_HOSTED_CTOR; }
129 bool hasRest() const { return flags() & HAS_REST; }
131 bool isInterpretedLazy() const {
132 return flags() & INTERPRETED_LAZY;
133 }
134 bool hasScript() const {
135 return flags() & INTERPRETED;
136 }
138 bool hasJITCode() const {
139 if (!hasScript())
140 return false;
142 return nonLazyScript()->hasBaselineScript() || nonLazyScript()->hasIonScript();
143 }
145 // Arrow functions store their lexical |this| in the first extended slot.
146 bool isArrow() const { return flags() & ARROW; }
148 /* Compound attributes: */
149 bool isBuiltin() const {
150 return isNative() || isSelfHostedBuiltin();
151 }
152 bool isInterpretedConstructor() const {
153 // Note: the JITs inline this check, so be careful when making changes
154 // here. See IonMacroAssembler::branchIfNotInterpretedConstructor.
155 return isInterpreted() && !isFunctionPrototype() && !isArrow() &&
156 (!isSelfHostedBuiltin() || isSelfHostedConstructor());
157 }
158 bool isNamedLambda() const {
159 return isLambda() && displayAtom() && !hasGuessedAtom();
160 }
161 bool hasParallelNative() const {
162 return isNative() && jitInfo() && jitInfo()->hasParallelNative();
163 }
165 bool isBuiltinFunctionConstructor();
167 /* Returns the strictness of this function, which must be interpreted. */
168 bool strict() const {
169 return nonLazyScript()->strict();
170 }
172 void setFlags(uint16_t flags) {
173 this->flags_ = flags;
174 }
176 // Can be called multiple times by the parser.
177 void setArgCount(uint16_t nargs) {
178 this->nargs_ = nargs;
179 }
181 // Can be called multiple times by the parser.
182 void setHasRest() {
183 flags_ |= HAS_REST;
184 }
186 void setIsSelfHostedBuiltin() {
187 JS_ASSERT(!isSelfHostedBuiltin());
188 flags_ |= SELF_HOSTED;
189 }
191 void setIsSelfHostedConstructor() {
192 JS_ASSERT(!isSelfHostedConstructor());
193 flags_ |= SELF_HOSTED_CTOR;
194 }
196 void setIsFunctionPrototype() {
197 JS_ASSERT(!isFunctionPrototype());
198 flags_ |= IS_FUN_PROTO;
199 }
201 // Can be called multiple times by the parser.
202 void setIsExprClosure() {
203 flags_ |= EXPR_CLOSURE;
204 }
206 void setArrow() {
207 flags_ |= ARROW;
208 }
210 JSAtom *atom() const { return hasGuessedAtom() ? nullptr : atom_.get(); }
211 js::PropertyName *name() const { return hasGuessedAtom() || !atom_ ? nullptr : atom_->asPropertyName(); }
212 void initAtom(JSAtom *atom) { atom_.init(atom); }
214 JSAtom *displayAtom() const {
215 return atom_;
216 }
218 void setGuessedAtom(JSAtom *atom) {
219 JS_ASSERT(atom_ == nullptr);
220 JS_ASSERT(atom != nullptr);
221 JS_ASSERT(!hasGuessedAtom());
222 atom_ = atom;
223 flags_ |= HAS_GUESSED_ATOM;
224 }
226 /* uint16_t representation bounds number of call object dynamic slots. */
227 enum { MAX_ARGS_AND_VARS = 2 * ((1U << 16) - 1) };
229 /*
230 * For an interpreted function, accessors for the initial scope object of
231 * activations (stack frames) of the function.
232 */
233 JSObject *environment() const {
234 JS_ASSERT(isInterpreted());
235 return u.i.env_;
236 }
238 void setEnvironment(JSObject *obj) {
239 JS_ASSERT(isInterpreted());
240 *(js::HeapPtrObject *)&u.i.env_ = obj;
241 }
243 void initEnvironment(JSObject *obj) {
244 JS_ASSERT(isInterpreted());
245 ((js::HeapPtrObject *)&u.i.env_)->init(obj);
246 }
248 static inline size_t offsetOfNargs() { return offsetof(JSFunction, nargs_); }
249 static inline size_t offsetOfFlags() { return offsetof(JSFunction, flags_); }
250 static inline size_t offsetOfEnvironment() { return offsetof(JSFunction, u.i.env_); }
251 static inline size_t offsetOfAtom() { return offsetof(JSFunction, atom_); }
253 static bool createScriptForLazilyInterpretedFunction(JSContext *cx, js::HandleFunction fun);
254 void relazify(JSTracer *trc);
256 // Function Scripts
257 //
258 // Interpreted functions may either have an explicit JSScript (hasScript())
259 // or be lazy with sufficient information to construct the JSScript if
260 // necessary (isInterpretedLazy()).
261 //
262 // A lazy function will have a LazyScript if the function came from parsed
263 // source, or nullptr if the function is a clone of a self hosted function.
264 //
265 // There are several methods to get the script of an interpreted function:
266 //
267 // - For all interpreted functions, getOrCreateScript() will get the
268 // JSScript, delazifying the function if necessary. This is the safest to
269 // use, but has extra checks, requires a cx and may trigger a GC.
270 //
271 // - For inlined functions which may have a LazyScript but whose JSScript
272 // is known to exist, existingScriptForInlinedFunction() will get the
273 // script and delazify the function if necessary.
274 //
275 // - For functions known to have a JSScript, nonLazyScript() will get it.
277 JSScript *getOrCreateScript(JSContext *cx) {
278 JS_ASSERT(isInterpreted());
279 JS_ASSERT(cx);
280 if (isInterpretedLazy()) {
281 JS::RootedFunction self(cx, this);
282 if (!createScriptForLazilyInterpretedFunction(cx, self))
283 return nullptr;
284 return self->nonLazyScript();
285 }
286 return nonLazyScript();
287 }
289 JSScript *existingScriptForInlinedFunction() {
290 MOZ_ASSERT(isInterpreted());
291 if (isInterpretedLazy()) {
292 // Get the script from the canonical function. Ion used the
293 // canonical function to inline the script and because it has
294 // Baseline code it has not been relazified. Note that we can't
295 // use lazyScript->script_ here as it may be null in some cases,
296 // see bug 976536.
297 js::LazyScript *lazy = lazyScript();
298 JSFunction *fun = lazy->functionNonDelazifying();
299 MOZ_ASSERT(fun);
300 JSScript *script = fun->nonLazyScript();
302 if (shadowZone()->needsBarrier())
303 js::LazyScript::writeBarrierPre(lazy);
305 flags_ &= ~INTERPRETED_LAZY;
306 flags_ |= INTERPRETED;
307 initScript(script);
308 }
309 return nonLazyScript();
310 }
312 JSScript *nonLazyScript() const {
313 JS_ASSERT(hasScript());
314 JS_ASSERT(u.i.s.script_);
315 return u.i.s.script_;
316 }
318 // Returns non-callsited-clone version of this. Use when return
319 // value can flow to arbitrary JS (see Bug 944975).
320 JSFunction* originalFunction() {
321 if (this->hasScript() && this->nonLazyScript()->isCallsiteClone()) {
322 return this->nonLazyScript()->donorFunction();
323 } else {
324 return this;
325 }
326 }
328 js::HeapPtrScript &mutableScript() {
329 JS_ASSERT(isInterpreted());
330 return *(js::HeapPtrScript *)&u.i.s.script_;
331 }
333 js::LazyScript *lazyScript() const {
334 JS_ASSERT(isInterpretedLazy() && u.i.s.lazy_);
335 return u.i.s.lazy_;
336 }
338 js::LazyScript *lazyScriptOrNull() const {
339 JS_ASSERT(isInterpretedLazy());
340 return u.i.s.lazy_;
341 }
343 js::GeneratorKind generatorKind() const {
344 if (!isInterpreted())
345 return js::NotGenerator;
346 if (hasScript())
347 return nonLazyScript()->generatorKind();
348 if (js::LazyScript *lazy = lazyScriptOrNull())
349 return lazy->generatorKind();
350 JS_ASSERT(isSelfHostedBuiltin());
351 return js::NotGenerator;
352 }
354 bool isGenerator() const { return generatorKind() != js::NotGenerator; }
356 bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; }
358 bool isStarGenerator() const { return generatorKind() == js::StarGenerator; }
360 void setScript(JSScript *script_) {
361 JS_ASSERT(hasScript());
362 mutableScript() = script_;
363 }
365 void initScript(JSScript *script_) {
366 JS_ASSERT(hasScript());
367 mutableScript().init(script_);
368 }
370 void setUnlazifiedScript(JSScript *script) {
371 // Note: createScriptForLazilyInterpretedFunction triggers a barrier on
372 // lazy script before it is overwritten here.
373 JS_ASSERT(isInterpretedLazy());
374 if (!lazyScript()->maybeScript())
375 lazyScript()->initScript(script);
376 flags_ &= ~INTERPRETED_LAZY;
377 flags_ |= INTERPRETED;
378 initScript(script);
379 }
381 void initLazyScript(js::LazyScript *lazy) {
382 JS_ASSERT(isInterpreted());
383 flags_ &= ~INTERPRETED;
384 flags_ |= INTERPRETED_LAZY;
385 u.i.s.lazy_ = lazy;
386 }
388 JSNative native() const {
389 JS_ASSERT(isNative());
390 return u.n.native;
391 }
393 JSNative maybeNative() const {
394 return isInterpreted() ? nullptr : native();
395 }
397 JSParallelNative parallelNative() const {
398 JS_ASSERT(hasParallelNative());
399 return jitInfo()->parallelNative;
400 }
402 JSParallelNative maybeParallelNative() const {
403 return hasParallelNative() ? parallelNative() : nullptr;
404 }
406 void initNative(js::Native native, const JSJitInfo *jitinfo) {
407 JS_ASSERT(native);
408 u.n.native = native;
409 u.n.jitinfo = jitinfo;
410 }
412 const JSJitInfo *jitInfo() const {
413 JS_ASSERT(isNative());
414 return u.n.jitinfo;
415 }
417 void setJitInfo(const JSJitInfo *data) {
418 JS_ASSERT(isNative());
419 u.n.jitinfo = data;
420 }
422 static unsigned offsetOfNativeOrScript() {
423 JS_STATIC_ASSERT(offsetof(U, n.native) == offsetof(U, i.s.script_));
424 JS_STATIC_ASSERT(offsetof(U, n.native) == offsetof(U, nativeOrScript));
425 return offsetof(JSFunction, u.nativeOrScript);
426 }
428 #if JS_BITS_PER_WORD == 32
429 static const js::gc::AllocKind FinalizeKind = js::gc::FINALIZE_OBJECT2_BACKGROUND;
430 static const js::gc::AllocKind ExtendedFinalizeKind = js::gc::FINALIZE_OBJECT4_BACKGROUND;
431 #else
432 static const js::gc::AllocKind FinalizeKind = js::gc::FINALIZE_OBJECT4_BACKGROUND;
433 static const js::gc::AllocKind ExtendedFinalizeKind = js::gc::FINALIZE_OBJECT8_BACKGROUND;
434 #endif
436 inline void trace(JSTracer *trc);
438 /* Bound function accessors. */
440 inline bool initBoundFunction(JSContext *cx, js::HandleValue thisArg,
441 const js::Value *args, unsigned argslen);
443 JSObject *getBoundFunctionTarget() const {
444 JS_ASSERT(isBoundFunction());
446 /* Bound functions abuse |parent| to store their target function. */
447 return getParent();
448 }
450 const js::Value &getBoundFunctionThis() const;
451 const js::Value &getBoundFunctionArgument(unsigned which) const;
452 size_t getBoundFunctionArgumentCount() const;
454 private:
455 inline js::FunctionExtended *toExtended();
456 inline const js::FunctionExtended *toExtended() const;
458 public:
459 inline bool isExtended() const {
460 JS_STATIC_ASSERT(FinalizeKind != ExtendedFinalizeKind);
461 JS_ASSERT_IF(isTenured(), !!(flags() & EXTENDED) == (tenuredGetAllocKind() == ExtendedFinalizeKind));
462 return !!(flags() & EXTENDED);
463 }
465 /*
466 * Accessors for data stored in extended functions. Use setExtendedSlot if
467 * the function has already been initialized. Otherwise use
468 * initExtendedSlot.
469 */
470 inline void initializeExtended();
471 inline void initExtendedSlot(size_t which, const js::Value &val);
472 inline void setExtendedSlot(size_t which, const js::Value &val);
473 inline const js::Value &getExtendedSlot(size_t which) const;
475 /* Constructs a new type for the function if necessary. */
476 static bool setTypeForScriptedFunction(js::ExclusiveContext *cx, js::HandleFunction fun,
477 bool singleton = false);
479 /* GC support. */
480 js::gc::AllocKind getAllocKind() const {
481 js::gc::AllocKind kind = FinalizeKind;
482 if (isExtended())
483 kind = ExtendedFinalizeKind;
484 JS_ASSERT_IF(isTenured(), kind == tenuredGetAllocKind());
485 return kind;
486 }
487 };
489 extern JSString *
490 fun_toStringHelper(JSContext *cx, js::HandleObject obj, unsigned indent);
492 inline JSFunction::Flags
493 JSAPIToJSFunctionFlags(unsigned flags)
494 {
495 return (flags & JSFUN_CONSTRUCTOR)
496 ? JSFunction::NATIVE_CTOR
497 : JSFunction::NATIVE_FUN;
498 }
500 namespace js {
502 extern bool
503 Function(JSContext *cx, unsigned argc, Value *vp);
505 extern bool
506 Generator(JSContext *cx, unsigned argc, Value *vp);
508 extern JSFunction *
509 NewFunction(ExclusiveContext *cx, HandleObject funobj, JSNative native, unsigned nargs,
510 JSFunction::Flags flags, HandleObject parent, HandleAtom atom,
511 gc::AllocKind allocKind = JSFunction::FinalizeKind,
512 NewObjectKind newKind = GenericObject);
514 // If proto is nullptr, Function.prototype is used instead.
515 extern JSFunction *
516 NewFunctionWithProto(ExclusiveContext *cx, HandleObject funobj, JSNative native, unsigned nargs,
517 JSFunction::Flags flags, HandleObject parent, HandleAtom atom,
518 JSObject *proto, gc::AllocKind allocKind = JSFunction::FinalizeKind,
519 NewObjectKind newKind = GenericObject);
521 extern JSFunction *
522 DefineFunction(JSContext *cx, HandleObject obj, HandleId id, JSNative native,
523 unsigned nargs, unsigned flags,
524 gc::AllocKind allocKind = JSFunction::FinalizeKind,
525 NewObjectKind newKind = GenericObject);
527 bool
528 FunctionHasResolveHook(const JSAtomState &atomState, PropertyName *name);
530 extern bool
531 fun_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp);
533 // ES6 9.2.5 IsConstructor
534 bool IsConstructor(const Value &v);
536 /*
537 * Function extended with reserved slots for use by various kinds of functions.
538 * Most functions do not have these extensions, but enough do that efficient
539 * storage is required (no malloc'ed reserved slots).
540 */
541 class FunctionExtended : public JSFunction
542 {
543 public:
544 static const unsigned NUM_EXTENDED_SLOTS = 2;
546 /* Arrow functions store their lexical |this| in the first extended slot. */
547 static const unsigned ARROW_THIS_SLOT = 0;
549 static inline size_t offsetOfExtendedSlot(unsigned which) {
550 MOZ_ASSERT(which < NUM_EXTENDED_SLOTS);
551 return offsetof(FunctionExtended, extendedSlots) + which * sizeof(HeapValue);
552 }
553 static inline size_t offsetOfArrowThisSlot() {
554 return offsetOfExtendedSlot(ARROW_THIS_SLOT);
555 }
557 private:
558 friend class JSFunction;
560 /* Reserved slots available for storage by particular native functions. */
561 HeapValue extendedSlots[NUM_EXTENDED_SLOTS];
562 };
564 extern JSFunction *
565 CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
566 gc::AllocKind kind = JSFunction::FinalizeKind,
567 NewObjectKind newKindArg = GenericObject);
570 extern bool
571 FindBody(JSContext *cx, HandleFunction fun, ConstTwoByteChars chars, size_t length,
572 size_t *bodyStart, size_t *bodyEnd);
574 } // namespace js
576 inline js::FunctionExtended *
577 JSFunction::toExtended()
578 {
579 JS_ASSERT(isExtended());
580 return static_cast<js::FunctionExtended *>(this);
581 }
583 inline const js::FunctionExtended *
584 JSFunction::toExtended() const
585 {
586 JS_ASSERT(isExtended());
587 return static_cast<const js::FunctionExtended *>(this);
588 }
590 inline void
591 JSFunction::initializeExtended()
592 {
593 JS_ASSERT(isExtended());
595 JS_ASSERT(mozilla::ArrayLength(toExtended()->extendedSlots) == 2);
596 toExtended()->extendedSlots[0].init(js::UndefinedValue());
597 toExtended()->extendedSlots[1].init(js::UndefinedValue());
598 }
600 inline void
601 JSFunction::initExtendedSlot(size_t which, const js::Value &val)
602 {
603 JS_ASSERT(which < mozilla::ArrayLength(toExtended()->extendedSlots));
604 toExtended()->extendedSlots[which].init(val);
605 }
607 inline void
608 JSFunction::setExtendedSlot(size_t which, const js::Value &val)
609 {
610 JS_ASSERT(which < mozilla::ArrayLength(toExtended()->extendedSlots));
611 toExtended()->extendedSlots[which] = val;
612 }
614 inline const js::Value &
615 JSFunction::getExtendedSlot(size_t which) const
616 {
617 JS_ASSERT(which < mozilla::ArrayLength(toExtended()->extendedSlots));
618 return toExtended()->extendedSlots[which];
619 }
621 namespace js {
623 JSString *FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lambdaParen);
625 template<XDRMode mode>
626 bool
627 XDRInterpretedFunction(XDRState<mode> *xdr, HandleObject enclosingScope,
628 HandleScript enclosingScript, MutableHandleObject objp);
630 extern JSObject *
631 CloneFunctionAndScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun);
633 /*
634 * Report an error that call.thisv is not compatible with the specified class,
635 * assuming that the method (clasp->name).prototype.<name of callee function>
636 * is what was called.
637 */
638 extern void
639 ReportIncompatibleMethod(JSContext *cx, CallReceiver call, const Class *clasp);
641 /*
642 * Report an error that call.thisv is not an acceptable this for the callee
643 * function.
644 */
645 extern void
646 ReportIncompatible(JSContext *cx, CallReceiver call);
648 bool
649 CallOrConstructBoundFunction(JSContext *, unsigned, js::Value *);
651 extern const JSFunctionSpec function_methods[];
653 } /* namespace js */
655 extern bool
656 js_fun_apply(JSContext *cx, unsigned argc, js::Value *vp);
658 extern bool
659 js_fun_call(JSContext *cx, unsigned argc, js::Value *vp);
661 extern JSObject*
662 js_fun_bind(JSContext *cx, js::HandleObject target, js::HandleValue thisArg,
663 js::Value *boundArgs, unsigned argslen);
665 #ifdef DEBUG
666 namespace JS {
667 namespace detail {
669 JS_PUBLIC_API(void)
670 CheckIsValidConstructible(Value calleev);
672 } // namespace detail
673 } // namespace JS
674 #endif
676 #endif /* jsfun_h */