JavaScript:SpiderMonkey:C Coding Style

From MozillaWiki
Revision as of 07:27, 29 April 2010 by Cdleary (talk | contribs) (→‎Control Flow: Fix inconsistency with previous style guideline.)
Jump to navigation Jump to search

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 in switch 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 from switch 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

  • There used to be a strict 80-column limit per line. The hard limit is now 100 columns, a change that dates to 2008. 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 the case, the statement, and the break; all on one line.
  • Comment /* FALL THROUGH */ in place of missing break when intentionally falling through from one case-controlled statement sequence into another, or into the default 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);
}

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.