1 # vim: set ts=8 sts=4 et sw=4 tw=99:
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/.
6 # ----------------------------------------------------------------------------
7 # This script checks various aspects of SpiderMonkey code style. The current checks are as
10 # We check the following things in headers.
12 # - No cyclic dependencies.
14 # - No normal header should #include a inlines.h/-inl.h file.
16 # - #ifndef wrappers should have the right form. (XXX: not yet implemented)
17 # - Every header file should have one.
18 # - The guard name used should be appropriate for the filename.
20 # We check the following things in all files.
22 # - #includes should have full paths, e.g. "jit/Ion.h", not "Ion.h".
24 # - #includes should use the appropriate form for system headers (<...>) and
25 # local headers ("...").
27 # - #includes should be ordered correctly.
28 # - Each one should be in the correct section.
29 # - Alphabetical order should be used within sections.
30 # - Sections should be in the right order.
31 # Note that the presence of #if/#endif blocks complicates things, to the
32 # point that it's not always clear where a conditionally-compiled #include
33 # statement should go, even to a human. Therefore, we check the #include
34 # statements within each #if/#endif block (including nested ones) in
35 # isolation, but don't try to do any order checking between such blocks.
36 # ----------------------------------------------------------------------------
38 from __future__
import absolute_import
, print_function
45 # We don't bother checking files in these directories, because they're (a) auxiliary or (b)
46 # imported code that doesn't follow our coding style.
47 ignored_js_src_dirs
= [
48 "js/src/config/", # auxiliary stuff
49 "js/src/ctypes/libffi/", # imported code
50 "js/src/devtools/", # auxiliary stuff
51 "js/src/editline/", # imported code
52 "js/src/gdb/", # auxiliary stuff
53 "js/src/vtune/", # imported code
54 "js/src/zydis/", # imported code
57 # We ignore #includes of these files, because they don't follow the usual rules.
58 included_inclnames_to_ignore
= set(
60 "ffi.h", # generated in ctypes/libffi/
61 "devtools/Instruments.h", # we ignore devtools/ in general
62 "double-conversion/double-conversion.h", # strange MFBT case
63 "javascript-trace.h", # generated in $OBJDIR if HAVE_DTRACE is defined
64 "frontend/ReservedWordsGenerated.h", # generated in $OBJDIR
65 "frontend/smoosh_generated.h", # generated in $OBJDIR
66 "gc/StatsPhasesGenerated.h", # generated in $OBJDIR
67 "gc/StatsPhasesGenerated.inc", # generated in $OBJDIR
68 "jit/CacheIROpsGenerated.h", # generated in $OBJDIR
69 "jit/LOpcodesGenerated.h", # generated in $OBJDIR
70 "jit/MOpcodesGenerated.h", # generated in $OBJDIR
71 "js/ProfilingCategoryList.h", # comes from mozglue/baseprofiler
72 "jscustomallocator.h", # provided by embedders; allowed to be missing
73 "js-config.h", # generated in $OBJDIR
75 "FuzzerDefs.h", # included without a path
76 "FuzzingInterface.h", # included without a path
77 "mozmemory.h", # included without a path
83 "private/pprio.h", # NSPR
89 "selfhosted.out.h", # generated in $OBJDIR
90 "shellmoduleloader.out.h", # generated in $OBJDIR
91 "unicode/basictz.h", # ICU
92 "unicode/locid.h", # ICU
93 "unicode/plurrule.h", # ICU
94 "unicode/putil.h", # ICU
95 "unicode/timezone.h", # ICU
96 "unicode/ucal.h", # ICU
97 "unicode/uchar.h", # ICU
98 "unicode/uclean.h", # ICU
99 "unicode/ucol.h", # ICU
100 "unicode/ucurr.h", # ICU
101 "unicode/udat.h", # ICU
102 "unicode/udata.h", # ICU
103 "unicode/udateintervalformat.h", # ICU
104 "unicode/udatpg.h", # ICU
105 "unicode/udisplaycontext.h", # ICU
106 "unicode/uenum.h", # ICU
107 "unicode/ufieldpositer.h", # ICU
108 "unicode/uformattedvalue.h", # ICU
109 "unicode/ulistformatter.h", # ICU
110 "unicode/uldnames.h", # ICU
111 "unicode/uloc.h", # ICU
112 "unicode/umachine.h", # ICU
113 "unicode/uniset.h", # ICU
114 "unicode/unistr.h", # ICU
115 "unicode/unorm2.h", # ICU
116 "unicode/unum.h", # ICU
117 "unicode/unumberformatter.h", # ICU
118 "unicode/unumsys.h", # ICU
119 "unicode/upluralrules.h", # ICU
120 "unicode/ureldatefmt.h", # ICU
121 "unicode/ures.h", # ICU
122 "unicode/ustring.h", # ICU
123 "unicode/utypes.h", # ICU
124 "unicode/uversion.h", # ICU
125 "vtune/VTuneWrapper.h", # VTune
126 "zydis/ZydisAPI.h", # Zydis
130 # These files have additional constraints on where they are #included, so we
131 # ignore #includes of them when checking #include ordering.
132 oddly_ordered_inclnames
= set(
134 "ctypes/typedefs.h", # Included multiple times in the body of ctypes/CTypes.h
135 # Included in the body of frontend/TokenStream.h
136 "frontend/ReservedWordsGenerated.h",
137 "gc/StatsPhasesGenerated.h", # Included in the body of gc/Statistics.h
138 "gc/StatsPhasesGenerated.inc", # Included in the body of gc/Statistics.cpp
139 "psapi.h", # Must be included after "util/Windows.h" on Windows
140 "machine/endian.h", # Must be included after <sys/types.h> on BSD
141 "winbase.h", # Must precede other system headers(?)
142 "windef.h", # Must precede other system headers(?)
146 # The files in tests/style/ contain code that fails this checking in various
147 # ways. Here is the output we expect. If the actual output differs from
148 # this, one of the following must have happened.
149 # - New SpiderMonkey code violates one of the checked rules.
150 # - The tests/style/ files have changed without expected_output being changed
152 # - This script has been broken somehow.
154 expected_output
= """\
155 js/src/tests/style/BadIncludes.h:3: error:
156 the file includes itself
158 js/src/tests/style/BadIncludes.h:6: error:
159 "BadIncludes2.h" is included using the wrong path;
160 did you forget a prefix, or is the file not yet committed?
162 js/src/tests/style/BadIncludes.h:8: error:
163 <tests/style/BadIncludes2.h> should be included using
164 the #include "..." form
166 js/src/tests/style/BadIncludes.h:10: error:
167 "stdio.h" is included using the wrong path;
168 did you forget a prefix, or is the file not yet committed?
170 js/src/tests/style/BadIncludes2.h:1: error:
171 vanilla header includes an inline-header file "tests/style/BadIncludes2-inl.h"
173 js/src/tests/style/BadIncludesOrder-inl.h:5:6: error:
174 "vm/JSScript-inl.h" should be included after "vm/Interpreter-inl.h"
176 js/src/tests/style/BadIncludesOrder-inl.h:6:7: error:
177 "vm/Interpreter-inl.h" should be included after "js/Value.h"
179 js/src/tests/style/BadIncludesOrder-inl.h:7:8: error:
180 "js/Value.h" should be included after "ds/LifoAlloc.h"
182 js/src/tests/style/BadIncludesOrder-inl.h:8:9: error:
183 "ds/LifoAlloc.h" should be included after "jsapi.h"
185 js/src/tests/style/BadIncludesOrder-inl.h:9:10: error:
186 "jsapi.h" should be included after <stdio.h>
188 js/src/tests/style/BadIncludesOrder-inl.h:10:11: error:
189 <stdio.h> should be included after "mozilla/HashFunctions.h"
191 js/src/tests/style/BadIncludesOrder-inl.h:28:29: error:
192 "vm/JSScript.h" should be included after "vm/JSFunction.h"
194 (multiple files): error:
195 header files form one or more cycles
197 tests/style/HeaderCycleA1.h
198 -> tests/style/HeaderCycleA2.h
199 -> tests/style/HeaderCycleA3.h
200 -> tests/style/HeaderCycleA1.h
202 tests/style/HeaderCycleB1-inl.h
203 -> tests/style/HeaderCycleB2-inl.h
204 -> tests/style/HeaderCycleB3-inl.h
205 -> tests/style/HeaderCycleB4-inl.h
206 -> tests/style/HeaderCycleB1-inl.h
207 -> tests/style/jsheadercycleB5inlines.h
208 -> tests/style/HeaderCycleB1-inl.h
209 -> tests/style/HeaderCycleB4-inl.h
220 actual_output
.append(line
+ "\n")
223 def error(filename
, linenum
, *lines
):
225 if linenum
is not None:
226 location
+= ":" + str(linenum
)
227 out(location
+ ": error:")
233 class FileKind(object):
243 if filename
.endswith(".c"):
246 if filename
.endswith(".cpp"):
249 if filename
.endswith(("inlines.h", "-inl.h")):
250 return FileKind
.INL_H
252 if filename
.endswith(".h"):
255 if filename
.endswith(".tbl"):
258 if filename
.endswith(".msg"):
261 error(filename
, None, "unknown file kind")
264 def check_style(enable_fixup
):
265 # We deal with two kinds of name.
266 # - A "filename" is a full path to a file from the repository root.
267 # - An "inclname" is how a file is referred to in a #include statement.
269 # Examples (filename -> inclname)
270 # - "mfbt/Attributes.h" -> "mozilla/Attributes.h"
271 # - "mozglue/misc/TimeStamp.h -> "mozilla/TimeStamp.h"
272 # - "memory/mozalloc/mozalloc.h -> "mozilla/mozalloc.h"
273 # - "js/public/Vector.h" -> "js/Vector.h"
274 # - "js/src/vm/String.h" -> "vm/String.h"
276 non_js_dirnames
= ("mfbt/", "memory/mozalloc/", "mozglue/") # type: tuple(str)
277 non_js_inclnames
= set() # type: set(inclname)
278 js_names
= dict() # type: dict(filename, inclname)
280 # Process files in js/src.
281 js_src_root
= os
.path
.join("js", "src")
282 for dirpath
, dirnames
, filenames
in os
.walk(js_src_root
):
283 if dirpath
== js_src_root
:
284 # Skip any subdirectories that contain a config.status file
287 for dirname
in dirnames
:
288 path
= os
.path
.join(dirpath
, dirname
, "config.status")
289 if os
.path
.isfile(path
):
290 builddirs
.append(dirname
)
291 for dirname
in builddirs
:
292 dirnames
.remove(dirname
)
293 for filename
in filenames
:
294 filepath
= os
.path
.join(dirpath
, filename
).replace("\\", "/")
295 if not filepath
.startswith(
296 tuple(ignored_js_src_dirs
)
297 ) and filepath
.endswith((".c", ".cpp", ".h", ".tbl", ".msg")):
298 inclname
= filepath
[len("js/src/") :]
299 js_names
[filepath
] = inclname
301 # Look for header files in directories in non_js_dirnames.
302 for non_js_dir
in non_js_dirnames
:
303 for dirpath
, dirnames
, filenames
in os
.walk(non_js_dir
):
304 for filename
in filenames
:
305 if filename
.endswith(".h"):
306 inclname
= "mozilla/" + filename
307 non_js_inclnames
.add(inclname
)
309 # Look for header files in js/public.
310 js_public_root
= os
.path
.join("js", "public")
311 for dirpath
, dirnames
, filenames
in os
.walk(js_public_root
):
312 for filename
in filenames
:
313 if filename
.endswith((".h", ".msg")):
314 filepath
= os
.path
.join(dirpath
, filename
).replace("\\", "/")
315 inclname
= "js/" + filepath
[len("js/public/") :]
316 js_names
[filepath
] = inclname
318 all_inclnames
= non_js_inclnames |
set(js_names
.values())
320 edges
= dict() # type: dict(inclname, set(inclname))
322 # We don't care what's inside the MFBT and MOZALLOC files, but because they
323 # are #included from JS files we have to add them to the inclusion graph.
324 for inclname
in non_js_inclnames
:
325 edges
[inclname
] = set()
327 # Process all the JS files.
328 for filename
in sorted(js_names
.keys()):
329 inclname
= js_names
[filename
]
330 file_kind
= FileKind
.get(filename
)
332 file_kind
== FileKind
.C
333 or file_kind
== FileKind
.CPP
334 or file_kind
== FileKind
.H
335 or file_kind
== FileKind
.INL_H
337 included_h_inclnames
= set() # type: set(inclname)
339 with
open(filename
, encoding
="utf-8") as f
:
343 code
= code
.sorted(inclname
)
344 with
open(filename
, "w") as f
:
345 f
.write(code
.to_source())
348 filename
, inclname
, file_kind
, code
, all_inclnames
, included_h_inclnames
351 edges
[inclname
] = included_h_inclnames
353 find_cycles(all_inclnames
, edges
)
355 # Compare expected and actual output.
356 difflines
= difflib
.unified_diff(
359 fromfile
="check_spidermonkey_style.py expected output",
360 tofile
="check_spidermonkey_style.py actual output",
363 for diffline
in difflines
:
365 print(diffline
, end
="")
370 def module_name(name
):
371 """Strip the trailing .cpp, .h, inlines.h or -inl.h from a filename."""
374 name
.replace("inlines.h", "")
375 .replace("-inl.h", "")
381 def is_module_header(enclosing_inclname
, header_inclname
):
382 """Determine if an included name is the "module header", i.e. should be
383 first in the file."""
385 module
= module_name(enclosing_inclname
)
387 # Normal case, for example:
388 # module == "vm/Runtime", header_inclname == "vm/Runtime.h".
389 if module
== module_name(header_inclname
):
392 # A public header, for example:
394 # module == "vm/CharacterEncoding",
395 # header_inclname == "js/CharacterEncoding.h"
397 # or (for implementation files for js/public/*/*.h headers)
399 # module == "vm/SourceHook",
400 # header_inclname == "js/experimental/SourceHook.h"
401 m
= re
.match(r
"js\/.*?([^\/]+)\.h", header_inclname
)
402 if m
is not None and module
.endswith("/" + m
.group(1)):
408 class Include(object):
409 """Important information for a single #include statement."""
411 def __init__(self
, include_prefix
, inclname
, line_suffix
, linenum
, is_system
):
412 self
.include_prefix
= include_prefix
413 self
.line_suffix
= line_suffix
414 self
.inclname
= inclname
415 self
.linenum
= linenum
416 self
.is_system
= is_system
418 def is_style_relevant(self
):
419 # Includes are style-relevant; that is, they're checked by the pairwise
420 # style-checking algorithm in check_file.
423 def section(self
, enclosing_inclname
):
424 """Identify which section inclname belongs to.
426 The section numbers are as follows.
427 0. Module header (e.g. jsfoo.h or jsfooinlines.h within jsfoo.cpp)
430 3. jsfoo.h, prmjtime.h, etc
434 7. non-.h, e.g. *.tbl, *.msg (these can be scattered throughout files)
440 if not self
.inclname
.endswith(".h"):
443 # A couple of modules have the .h file in js/ and the .cpp file elsewhere and so need
445 if is_module_header(enclosing_inclname
, self
.inclname
):
448 if "/" in self
.inclname
:
449 if self
.inclname
.startswith("mozilla/"):
452 if self
.inclname
.endswith("-inl.h"):
457 if self
.inclname
.endswith("inlines.h"):
464 return "<" + self
.inclname
+ ">"
466 return '"' + self
.inclname
+ '"'
468 def sort_key(self
, enclosing_inclname
):
469 return (self
.section(enclosing_inclname
), self
.inclname
.lower())
472 return self
.include_prefix
+ self
.quote() + self
.line_suffix
+ "\n"
475 class CppBlock(object):
476 """C preprocessor block: a whole file or a single #if/#elif/#else block.
478 A #if/#endif block is the contents of a #if/#endif (or similar) section.
479 The top-level block, which is not within a #if/#endif pair, is also
482 Each kid is either an Include (representing a #include), OrdinaryCode, or
483 a nested CppBlock."""
485 def __init__(self
, start_line
=""):
486 self
.start
= start_line
490 def is_style_relevant(self
):
493 def append_ordinary_line(self
, line
):
494 if len(self
.kids
) == 0 or not isinstance(self
.kids
[-1], OrdinaryCode
):
495 self
.kids
.append(OrdinaryCode())
496 self
.kids
[-1].lines
.append(line
)
498 def style_relevant_kids(self
):
499 """ Return a list of kids in this block that are style-relevant. """
500 return [kid
for kid
in self
.kids
if kid
.is_style_relevant()]
502 def sorted(self
, enclosing_inclname
):
503 """Return a hopefully-sorted copy of this block. Implements --fixup.
505 When in doubt, this leaves the code unchanged.
508 def pretty_sorted_includes(includes
):
509 """Return a new list containing the given includes, in order,
510 with blank lines separating sections."""
511 keys
= [inc
.sort_key(enclosing_inclname
) for inc
in includes
]
512 if sorted(keys
) == keys
:
513 return includes
# if nothing is out of order, don't touch anything
516 current_section
= None
517 for (section
, _
), inc
in sorted(zip(keys
, includes
)):
518 if current_section
is not None and section
!= current_section
:
519 output
.append(OrdinaryCode(["\n"])) # blank line
521 current_section
= section
524 def should_try_to_sort(includes
):
525 if "tests/style/BadIncludes" in enclosing_inclname
:
526 return False # don't straighten the counterexample
527 if any(inc
.inclname
in oddly_ordered_inclnames
for inc
in includes
):
528 return False # don't sort batches containing odd includes
529 if includes
== sorted(
530 includes
, key
=lambda inc
: inc
.sort_key(enclosing_inclname
)
532 return False # it's already sorted, avoid whitespace-only fixups
535 # The content of the eventual output of this method.
538 # The current batch of includes to sort. This list only ever contains Include objects
539 # and whitespace OrdinaryCode objects.
543 """Sort the contents of `batch` and move it to `output`."""
546 isinstance(item
, Include
)
547 or (isinstance(item
, OrdinaryCode
) and "".join(item
.lines
).isspace())
551 # Here we throw away the blank lines.
552 # `pretty_sorted_includes` puts them back.
554 last_include_index
= -1
555 for i
, item
in enumerate(batch
):
556 if isinstance(item
, Include
):
557 includes
.append(item
)
558 last_include_index
= i
559 cutoff
= last_include_index
+ 1
561 if should_try_to_sort(includes
):
562 output
.extend(pretty_sorted_includes(includes
) + batch
[cutoff
:])
567 for kid
in self
.kids
:
568 if isinstance(kid
, CppBlock
):
570 output
.append(kid
.sorted(enclosing_inclname
))
571 elif isinstance(kid
, Include
):
574 assert isinstance(kid
, OrdinaryCode
)
575 if kid
.to_source().isspace():
583 result
.start
= self
.start
584 result
.end
= self
.end
589 return self
.start
+ "".join(kid
.to_source() for kid
in self
.kids
) + self
.end
592 class OrdinaryCode(object):
593 """ A list of lines of code that aren't #include/#if/#else/#endif lines. """
595 def __init__(self
, lines
=None):
596 self
.lines
= lines
if lines
is not None else []
598 def is_style_relevant(self
):
602 return "".join(self
.lines
)
605 # A "snippet" is one of:
607 # * Include - representing an #include line
608 # * CppBlock - a whole file or #if/#elif/#else block; contains a list of snippets
609 # * OrdinaryCode - representing lines of non-#include-relevant code
613 block_stack
= [CppBlock()]
615 # Extract the #include statements as a tree of snippets.
616 for linenum
, line
in enumerate(f
, start
=1):
617 if line
.lstrip().startswith("#"):
618 # Look for a |#include "..."| line.
619 m
= re
.match(r
'(\s*#\s*include\s+)"([^"]*)"(.*)', line
)
621 prefix
, inclname
, suffix
= m
.groups()
622 block_stack
[-1].kids
.append(
623 Include(prefix
, inclname
, suffix
, linenum
, is_system
=False)
627 # Look for a |#include <...>| line.
628 m
= re
.match(r
"(\s*#\s*include\s+)<([^>]*)>(.*)", line
)
630 prefix
, inclname
, suffix
= m
.groups()
631 block_stack
[-1].kids
.append(
632 Include(prefix
, inclname
, suffix
, linenum
, is_system
=True)
636 # Look for a |#{if,ifdef,ifndef}| line.
637 m
= re
.match(r
"\s*#\s*(if|ifdef|ifndef)\b", line
)
640 new_block
= CppBlock(line
)
641 block_stack
[-1].kids
.append(new_block
)
642 block_stack
.append(new_block
)
645 # Look for a |#{elif,else}| line.
646 m
= re
.match(r
"\s*#\s*(elif|else)\b", line
)
648 # Close the current block, and open an adjacent one.
650 new_block
= CppBlock(line
)
651 block_stack
[-1].kids
.append(new_block
)
652 block_stack
.append(new_block
)
655 # Look for a |#endif| line.
656 m
= re
.match(r
"\s*#\s*endif\b", line
)
658 # Close the current block.
659 block_stack
.pop().end
= line
660 if len(block_stack
) == 0:
661 raise ValueError("#endif without #if at line " + str(linenum
))
664 # Otherwise, we have an ordinary line.
665 block_stack
[-1].append_ordinary_line(line
)
667 if len(block_stack
) > 1:
668 raise ValueError("unmatched #if")
669 return block_stack
[-1]
673 filename
, inclname
, file_kind
, code
, all_inclnames
, included_h_inclnames
675 def check_include_statement(include
):
676 """Check the style of a single #include statement."""
678 if include
.is_system
:
679 # Check it is not a known local file (in which case it's probably a system header).
681 include
.inclname
in included_inclnames_to_ignore
682 or include
.inclname
in all_inclnames
687 include
.quote() + " should be included using",
688 'the #include "..." form',
692 if include
.inclname
not in included_inclnames_to_ignore
:
693 included_kind
= FileKind
.get(include
.inclname
)
695 # Check the #include path has the correct form.
696 if include
.inclname
not in all_inclnames
:
700 include
.quote() + " is included using the wrong path;",
701 "did you forget a prefix, or is the file not yet committed?",
704 # Record inclusions of .h files for cycle detection later.
705 # (Exclude .tbl and .msg files.)
706 elif included_kind
== FileKind
.H
or included_kind
== FileKind
.INL_H
:
707 included_h_inclnames
.add(include
.inclname
)
709 # Check a H file doesn't #include an INL_H file.
710 if file_kind
== FileKind
.H
and included_kind
== FileKind
.INL_H
:
714 "vanilla header includes an inline-header file "
718 # Check a file doesn't #include itself. (We do this here because the cycle
719 # detection below doesn't detect this case.)
720 if inclname
== include
.inclname
:
721 error(filename
, include
.linenum
, "the file includes itself")
723 def check_includes_order(include1
, include2
):
724 """Check the ordering of two #include statements."""
727 include1
.inclname
in oddly_ordered_inclnames
728 or include2
.inclname
in oddly_ordered_inclnames
732 section1
= include1
.section(inclname
)
733 section2
= include2
.section(inclname
)
734 if (section1
> section2
) or (
735 (section1
== section2
)
736 and (include1
.inclname
.lower() > include2
.inclname
.lower())
740 str(include1
.linenum
) + ":" + str(include2
.linenum
),
741 include1
.quote() + " should be included after " + include2
.quote(),
744 # Check the extracted #include statements, both individually, and the ordering of
745 # adjacent pairs that live in the same block.
746 def pair_traverse(prev
, this
):
747 if isinstance(this
, Include
):
748 check_include_statement(this
)
749 if isinstance(prev
, Include
):
750 check_includes_order(prev
, this
)
752 kids
= this
.style_relevant_kids()
753 for prev2
, this2
in zip([None] + kids
[0:-1], kids
):
754 pair_traverse(prev2
, this2
)
756 pair_traverse(None, code
)
759 def find_cycles(all_inclnames
, edges
):
760 """Find and draw any cycles."""
762 SCCs
= tarjan(all_inclnames
, edges
)
764 # The various sorted() calls below ensure the output is deterministic.
771 out(" " * indent
+ ("-> " if indent
else " ") + v
)
775 for succ
in sorted(edges
[v
]):
777 draw(succ
, indent
+ 1)
779 draw(sorted(c
)[0], 0)
782 have_drawn_an_SCC
= False
783 for scc
in sorted(SCCs
):
785 if not have_drawn_an_SCC
:
786 error("(multiple files)", None, "header files form one or more cycles")
787 have_drawn_an_SCC
= True
792 # Tarjan's algorithm for finding the strongly connected components (SCCs) of a graph.
793 # https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
801 def strongconnect(v
, index
):
802 # Set the depth index for v to the smallest unused index
803 vertex_index
[v
] = index
804 vertex_lowlink
[v
] = index
808 # Consider successors of v
810 if w
not in vertex_index
:
811 # Successor w has not yet been visited; recurse on it
812 index
= strongconnect(w
, index
)
813 vertex_lowlink
[v
] = min(vertex_lowlink
[v
], vertex_lowlink
[w
])
815 # Successor w is in stack S and hence in the current SCC
816 vertex_lowlink
[v
] = min(vertex_lowlink
[v
], vertex_index
[w
])
818 # If v is a root node, pop the stack and generate an SCC
819 if vertex_lowlink
[v
] == vertex_index
[v
]:
828 if v
not in vertex_index
:
829 index
= strongconnect(v
, index
)
835 if sys
.argv
[1:] == ["--fixup"]:
836 # Sort #include directives in-place. Fixup mode doesn't solve
837 # all possible silliness that the script checks for; it's just a
838 # hack for the common case where renaming a header causes style
841 elif sys
.argv
[1:] == []:
845 "TEST-UNEXPECTED-FAIL | check_spidermonkey_style.py | unexpected command "
846 "line options: " + repr(sys
.argv
[1:])
850 ok
= check_style(fixup
)
853 print("TEST-PASS | check_spidermonkey_style.py | ok")
856 "TEST-UNEXPECTED-FAIL | check_spidermonkey_style.py | "
857 + "actual output does not match expected output; diff is above."
860 "TEST-UNEXPECTED-FAIL | check_spidermonkey_style.py | "
861 + "Hint: If the problem is that you renamed a header, and many #includes "
862 + "are no longer in alphabetical order, commit your work and then try "
863 + "`check_spidermonkey_style.py --fixup`. "
864 + "You need to commit first because --fixup modifies your files in place."
867 sys
.exit(0 if ok
else 1)
870 if __name__
== "__main__":