JavaScript:SpiderMonkey:C Coding Style
Functions
- Public function names begin with JS_ followed by capitalized "intercaps", e.g. JS_NewObject.
- Extern but library-private function names use a js_ prefix and mixed case, e.g. js_SearchScope.
- Most static function names have unprefixed, mixed-case names: GetChar.
- But static native methods of JS objects have lowercase, underscore-separated or intercaps names, e.g., str_indexOf.
- Function return types are on a separate line preceding the function name.
- Function braces go on the line following the function name.
void DoThis() /* bad */ { ... } void DoThis() /* OK */ { ... }
Other Symbols
- Library-private and static data use underscores, not intercaps (but library-private data do use a js_ prefix).
- Scalar type names are lowercase and js-prefixed: jsdouble.
- Aggregate type names are JS-prefixed and mixed-case: JSObject.
- Macros are generally ALL_CAPS and underscored, to call out potential side effects, multiple uses of a formal argument, etc.
Indentation
- Use spaces, not tabs. There should be no tabs in source files.
- Four spaces of indentation per statement nesting level.
- "
case L:
" labels inswitch
statements count as half of a nesting level, so indent two spaces, with the labeled statements indenting two more for a standard four spaces indentation fromswitch
to a case-controlled statement.
switch (discriminant) { case L1: DoSomething(); . . . }
- Function arguments that overflow the first line of the call expression should be aligned to underhang the first argument (to start in overflow lines in the column after the opening parenthesis).
JS_SetContext(rt, /* bad */ cx); JS_SetContext(rt, /* OK */ cx);
Whitespace in declarations
These rules are inconsistently applied. Be consistent with the code you're editing rather than adhere too closely to these guidelines!
- In a declaration of a pointer, the
*
goes with the variable name:
char* s; /* bad */ char *s; /* OK */
- Even in the return type of a method:
class JSString { ... JSString* dependentBase() const; /* bad */ JSString * dependentBase() const; /* bad */ JSString *dependentBase() const; /* OK */ ... };
- But in top-level function declarations/definitions, put a line break immediately before the name being declared:
extern const char * js_GetStringBytes(JSContext *cx, JSString *str); /* OK */
- And put a space in
*JS_FASTCALL
, because that would be crazy.
extern JSString *JS_FASTCALL /* bad */ js_ConcatStrings(JSContext *cx, JSString *left, JSString *right); extern JSString * JS_FASTCALL /* OK */ js_ConcatStrings(JSContext *cx, JSString *left, JSString *right);
- In C++ method declarations with default arguments, use spaces and comments like so:
static void Frob(JSContext *cx, uint32_t defaultValue = 0); static void Frob(JSContext *cx, uint32_t defaultValue /* = 0 */) { /* ... */ }
Other whitespace
- Code should fit within 99 columns; comments should fit within 80 columns; both figures include indentation. Break down lines that are too long.
- Overlong conditions break after each && or || operator.
- Other binary operators go at the front of the second line.
- Exception: in a
switch
statement where each case is a trivially-short statement, it's ok to put thecase
, the statement, and thebreak;
all on one line. - Comment
/* FALL THROUGH */
in place of missingbreak
when intentionally falling through from one case-controlled statement sequence into another, or into thedefault
statements. - Do not use spaces between a function name and its arguments list, or between an array name and the square bracket. Also, do no use spaces after a bracket. Use a space after a comma to separate arguments.
JS_SetContext ( rt, cx ); /* bad */ JS_SetContext(rt, cx); /* OK */
- Use a space between a C keyword and parentheses.
if(condition) /* bad */ if (condition) /* OK */
- An opening brace is placed on the same line as the preceding statement.
if (condition) /* bad */ { } if (condition) { /* OK */ }
- Do not put compound statements in one line. Indent the controlled statement on the next line, for clarity and break-pointing.
if (condition) break; /* bad */ if (condition) /* OK */ break;
- If a statement or condition covers multiple lines, use braces for all the controlled statements (even if the else part fits on one line, brace it too).
if (condition) /* bad */ CallThisMethod(argument1, argument2); if (condition) { /* OK */ CallThisMethod(argument1, argument2); }
for
loop heads go on one line where possible; when not possible, initializer part, update, and termination parts each go on separate lines
for (int i = 0; i < 5; i++) { /* bad, could all fit on one line */ doStuff(); } for (int i = 0; i < 5; i++) /* OK */ doStuff(); for (size_t ind = JSObject::JSSLOT_DATE_COMPONENTS_START; ind < JSObject::DATE_FIXED_RESERVED_SLOTS; ind++) { /* bad */ obj->setSlot(ind, DoubleValue(utcTime)); } for (size_t ind = JSObject::JSSLOT_DATE_COMPONENTS_START; ind < JSObject::DATE_FIXED_RESERVED_SLOTS; ind++) { /* OK */ obj->setSlot(ind, DoubleValue(utcTime)); }
Control Flow
- Minimize indentation using return, break, and continue where appropriate. Prefer return (break, continue) statements to cast out abnormal cases, instead of nesting "if/else" statements and indenting the common cases.
void MyFunction(int n) { if (n) { /* bad */ ... } } void MyFunction(int n) { if (!n) return; /* OK */ ... }
- If an "if" statement controls a "then" clause ending in a return statement, do not use "else" after return.
if (condition) { /* bad */ DoThis(); return; } else { DoThat(); }
if (condition) { /* OK */ DoThis(); return; } DoThat();
- Avoid similar arbitrary patterns and non-sequiturs:
if (condition) { /* bad */ DoThis(); DoThat(); } else { CleanUp(); return; } DoTheOther(); if (!condition) { /* OK */ CleanUp(); return; } DoThis(); DoThat(); DoTheOther();
Comments
- Always use C style comments. Never use C++ style comments.
- Terminate a comment with a period (so try to make comments be complete sentences).
/* This is a good comment. */
- Align multiline comments with any indentation, and start every line with an asterisk. Asterisks stack in the same column. Precede the multiline comment with one empty line unless the prior line ends in a left brace. The first line of the comment contains only leading space followed by
/*
. Multiline comments should also be bracketed.
if (condition) { /* * This is a lengthy * multiline comment. */ DoYourStuff(); }
Entry Points and Callbacks
- DLL entry points have their return type expanded within a JS_PUBLIC_API() macro call, to get the right Windows secret type qualifiers in the right places for all build variants.
- Callback functions that might be called from a DLL are similarly macroized with JS_STATIC_DLL_CALLBACK (if the function otherwise would be static to hide its name) or JS_DLL_CALLBACK (this macro takes no type argument; it should be used after the return type and before the function name).
Data Types and Alignments
- As with all Mozilla code, SpiderMonkey needs to compile and execute correctly on many platforms, including 64-bits systems.
- JS and NSPR have common roots in the dawn of time (Netscape 2), and the
JS_THREADSAFE
mode of building SpiderMonkey depends on NSPR, so reading the NSPR documentation is well worth your while. - Not all 64-bit systems use the same integer type model: some are "LP64" (long and pointer are 64 bits, int is 32 bits), while others are "LLP64" (only long long and pointer are 64 bits; long and int are 32 bits).
- Use size_t for unsigned number of bytes variables, ptrdiff_t for signed pointer subtraction results. In particular, do not use uintN, which is just shorthand for unsigned int, and so may not be big enough.
C++
Whitespace
In comments, use one space, not two, between sentences and after a colon.
If an expression is too long to fit on a line, split it after a binary operator.
Includes
The following order is used for includes: the module's .h (this ensures it includes all the headers it needs itself), mozilla/*.h; <*.h>, js*.h; */*.h; js*inlines.h and js*-inl.h; */*-inl.h. The public JSAPI headers in js/public/*.h should be included as js/*.h in the */*.h block.
Example for jsfoo.cpp:
#include "jsfoo.h" #include "mozilla/StdInt.h" #include <string.h> #include "jsbar.h" #include "jsbaz.h" #include "ds/Baz.h" #include "js/Bar.h" #include "vm/Bat.h" #include "jssquee-inl.h" #include "jswoot-inl.h" #include "frontend/Thingamabob-inl.h" #include "vm/VirtualReality-inl.h"
Including X-inl.h implies that X.h is included as well. If possible, keep lexicographic order.
Conditionals
The following style is used for bracing conditionals:
If the consequent (and, if present, the alternate) is a single statement, no braces are used.
if (today == "Tuesday") puts("I don't have my wallet on me."); else puts("I would gladly pay you on Tuesday for a hamburger today.");
However, if either the consequent or alternate is a block of multiple statements, braces are used on both.
if (canSwingFromWeb) { p->swingFromWeb(); } else { JS_ASSERT(p->isSpiderPig()); p->doWhateverSpiderPigDoes(); }
Conditions with multi-line tests should put the brace on the new line to provide a visual separation between the condition and the body.
types::TypeSet *types = frame.extra(lhs).types; if (JSOp(*PC) == JSOP_SETPROP && id == types::MakeTypeId(cx, id) && types && !types->unknownObject() && types->getObjectCount() == 1 && types->getTypeObject(0) != NULL && !types->getTypeObject(0)->unknownProperties()) { JS_ASSERT(usePropCache); types::TypeObject *object = types->getTypeObject(0); types::TypeSet *propertyTypes = object->getProperty(cx, id, false); ... } else { ... }
However, if there is already a visual separation between the condition and the body, putting the { on a new line isn't necessary:
if (forHead->pn_kid1 && NewSrcNote2(cx, cg, SRC_DECL, (forHead->pn_kid1->isOp(JSOP_DEFVAR)) ? SRC_DECL_VAR : SRC_DECL_LET) < 0) { return false; }
Namespaces
- Instead of JS/js and JS_ prefixing for public function and type names, respectively, use
namespace JS { ... }
- Instead of js_ prefixing for library-private and "friend" functions,
namespace js { ... }
- Compile-time-evaluated functions (i.e., template meta-functions) are kept in the "template library" namespace to avoid collision with their runtime counterparts.
namespace js { namespace tl { ... } }
- Avoid unnamed namespaces unless they are necessary to give a class's members internal linkage. Although the C++ standard officially deprecates 'static' on functions, 'static' has the following advantages:
- It is difficult to name functions in unnamed namespaces in gdb.
- Static says "this is a translation-unit-local helper function" on the function, without having to look around for an enclosing "namespace {".
- Other namespaces? Are these names too short and likely to collide?
Macros
- Line continuation characters should all line up, in column 79 if that exceeds the width of all the macro text.
- Macro parameters should be of the form name_ (instead of something like __name).
Enums
Older code uses SHOUT_REALLY_LOUD for enum values, newer code uses InterCaps. Enums should be preferred to boolean arguments for ease of understanding at the invocation site.
Classical OOP
- Most structs can become classes, with associated functions becoming methods. This already started for the upvar2 bug.
- Style detailing: mrbkap suggests left brace before class body on its own line in column 1 to match function style and facilitate vim navigation. People do this already when inheriting, since the left brace can get lost in the superclass(es).
class JSObject { ... }
- Member variable names, private or public, are not decorated. Examples of improper decoration include:
class Fail { size_t capacity_; // unsightly T *mBegin; // makes babies cry T *m_end; // much typing }
Sometimes a canonical argument name may conflict with a member name. In this case, one can disambiguate with "this->", although such explicit qualification should only be added when necessary:
class C { size_t count; bool fresh; void change(size_t count) { this->count = count; this->fresh = false; // verbose and unnecessary } };
- Most macros become inline helpers. With LiveConnect gone on mozilla-central, we can make the helpers methods of relevant structs or classes finally, instead of static inline functions.
- Based on 20+ years of bad experiences, we are going to go slow and resist virtual methods and bases, MI, and the like. Function pointer tables for now, as before -- see JSObjectOps needs a make-over.
- Templates are good, Mozilla has positive experience and the portability is there for the kind of RAII helpers we need.
- No exceptions, so std is hard to use. There is initial work underway to make STL-like containers that mesh well with the rest of the JS engine (see js::Vector, js::HashMap, js::Pool).
- There are still improvements to be made to the new hash table: double-hash implementation; improve bit-mixing into multiplicative hash if the cycle costs can be supported (measurement is required and we should understand down to the bits what is going on); a js::HashSet or js::HashMap<T,void> specialization such that set-like use of hashtables do not waste any space on values.
Initializer lists
Initializer lists can break in one of two ways. The first may be preferable when constructors take few arguments:
class Ninja : public WeaponWeilder, public Camouflagible, public Assassinatable, public ShapeShiftable { Ninja() : WeaponWeilder(Weapons::SHURIKEN), Camouflagible(Garments::SHINOBI_SHOZOKU), Assassinatable(AssassinationDifficulty::HIGHLY_DIFFICULT), ShapeShiftable(MPCost(512)) {} }
The other permitted style mitigates longer-identifiers-squishing-text-against-the-right-side-of-the-screen-syndrome by using a half-indented colon:
class Ninja : public WeaponWeilder, public Camouflagible, public Assassinatable, public ShapeShiftable { Ninja() : WeaponWeilder(Weapons::SHURIKEN), Camouflagible(Garments::SHINOBI_SHOZOKU), Assassinatable(AssassinationDifficulty::HIGHLY_DIFFICULT), ShapeShiftable(MPCost(512)) {} }
Inline methods
If the method in question fits on one line and is branch free, do a one liner:
class Eater { void eat(Eaten &other) { other.setConsumed(); } };
If it's too long, put the type, declarator including formals (unless they overflow), and left brace all on the first line:
class Eater { Food *obtainFoodFromEatery(Eatery &eatery) { if (!eatery.hasFood()) return NULL; return eatery.purchaseFood(); } };
For out-of-line inlines (when the definitions are too unwieldy to place in the class definition) use the inline keyword as an indicator that there's an out-of-line inline definition:
class SpaceGoo { inline BlobbyWrapper *enblob(Entity &other); }; inline BlobbyWrapper * SpaceGoo::enblob(Entity &other) { /* ... */ }
Boolean Types
- We would like to use the C++ 'bool' type wherever possible, but we must avoid breaking public and 'friend' APIs that use JSBool. So public header files (and semi-public headers, like 'jsopcodes.h') should continue to use 'JSBool', 'JS_TRUE', and 'JS_FALSE'. In all SpiderMonkey '.cpp' files, use 'true' and 'false', never 'JS_TRUE' or 'JS_FALSE'. Use 'bool' whenever doing so would not conflict with a declaration in a public or semi-public header.