javascriptlint-commit Mailing List for JavaScript Lint (Page 10)
Status: Beta
Brought to you by:
matthiasmiller
You can subscribe to this list here.
2008 |
Jan
|
Feb
|
Mar
(42) |
Apr
(15) |
May
(2) |
Jun
|
Jul
|
Aug
(33) |
Sep
(3) |
Oct
|
Nov
|
Dec
|
---|---|---|---|---|---|---|---|---|---|---|---|---|
2009 |
Jan
|
Feb
|
Mar
(5) |
Apr
|
May
(2) |
Jun
|
Jul
|
Aug
(2) |
Sep
|
Oct
(43) |
Nov
(4) |
Dec
(1) |
2010 |
Jan
|
Feb
|
Mar
|
Apr
(6) |
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
(1) |
Nov
|
Dec
|
2011 |
Jan
|
Feb
|
Mar
|
Apr
(1) |
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(1) |
2013 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
(20) |
Oct
(23) |
Nov
|
Dec
(1) |
2014 |
Jan
(1) |
Feb
(2) |
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
(4) |
Nov
|
Dec
|
2016 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(18) |
2018 |
Jan
(7) |
Feb
|
Mar
|
Apr
|
May
|
Jun
(8) |
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: <mat...@us...> - 2008-03-04 03:10:55
|
Revision: 162 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=162&view=rev Author: matthiasmiller Date: 2008-03-03 19:10:46 -0800 (Mon, 03 Mar 2008) Log Message: ----------- actually set the eol-style property Modified Paths: -------------- trunk/pyjsl/jsparse.py trunk/pyjsl/util.py Property Changed: ---------------- trunk/COPYING trunk/DEVELOPMENT trunk/INSTALL trunk/jsl.py trunk/pyjsl/__init__.py trunk/pyjsl/conf.py trunk/pyjsl/jsparse.py trunk/pyjsl/lint.py trunk/pyjsl/util.py trunk/pyjsl/visitation.py trunk/pyjsl/warnings.py trunk/pyspidermonkey/pyspidermonkey.c trunk/pyspidermonkey/tokens.tbl trunk/setup.py trunk/test.py Property changes on: trunk/COPYING ___________________________________________________________________ Name: eol-style - native Name: svn:eol-style + native Property changes on: trunk/DEVELOPMENT ___________________________________________________________________ Name: eol-style - native Name: svn:eol-style + native Property changes on: trunk/INSTALL ___________________________________________________________________ Name: eol-style - native Name: svn:eol-style + native Property changes on: trunk/jsl.py ___________________________________________________________________ Name: eol-style - native Name: svn:eol-style + native Property changes on: trunk/pyjsl/__init__.py ___________________________________________________________________ Name: eol-style - native Name: svn:eol-style + native Property changes on: trunk/pyjsl/conf.py ___________________________________________________________________ Name: eol-style - native Name: svn:eol-style + native Modified: trunk/pyjsl/jsparse.py =================================================================== --- trunk/pyjsl/jsparse.py 2008-03-04 01:34:39 UTC (rev 161) +++ trunk/pyjsl/jsparse.py 2008-03-04 03:10:46 UTC (rev 162) @@ -1,319 +1,319 @@ -#!/usr/bin/python -""" Parses a script into nodes. """ -import bisect -import re -import unittest - -import pyspidermonkey -from pyspidermonkey import tok, op - -_tok_names = dict(zip( - [getattr(tok, prop) for prop in dir(tok)], - ['tok.%s' % prop for prop in dir(tok)] -)) - -class NodePos(): - def __init__(self, line, col): - self.line = line - self.col = col - def __cmp__(self, other): - if self.line < other.line: - return -1 - if self.line > other.line: - return 1 - if self.col < other.col: - return -1 - if self.col > other.col: - return 1 - return 0 - def __str__(self): - return '(line %i, col %i)' % (self.line+1, self.col+1) - -class NodePositions(): - " Given a string, allows [x] lookups for NodePos line and column numbers." - def __init__(self, text): - # Find the length of each line and incrementally sum all of the lengths - # to determine the ending position of each line. - self._lines = text.splitlines(True) - lines = [0] + [len(x) for x in self._lines] - for x in range(1, len(lines)): - lines[x] += lines[x-1] - self._line_offsets = lines - def from_offset(self, offset): - line = bisect.bisect(self._line_offsets, offset)-1 - col = offset - self._line_offsets[line] - return NodePos(line, col) - def to_offset(self, pos): - offset = self._line_offsets[pos.line] + pos.col - assert offset <= self._line_offsets[pos.line+1] # out-of-bounds col num - return offset - def text(self, start, end): - assert start <= end - # Trim the ending first in case it's a single line. - lines = self._lines[start.line:end.line+1] - lines[-1] = lines[-1][:end.col+1] - lines[0] = lines[0][start.col:] - return ''.join(lines) - -class NodeRanges(): - def __init__(self): - self._offsets = [] - def add(self, start, end): - i = bisect.bisect_left(self._offsets, start) - if i % 2 == 1: - i -= 1 - start = self._offsets[i] - - end = end + 1 - j = bisect.bisect_left(self._offsets, end) - if j % 2 == 1: - end = self._offsets[j] - j += 1 - - self._offsets[i:j] = [start,end] - def has(self, pos): - return bisect.bisect_right(self._offsets, pos) % 2 == 1 - -class _Node(): - def add_child(self, node): - if node: - node.node_index = len(self.kids) - node.parent = self - self.kids.append(node) - - def start_pos(self): - try: - return self._start_pos - except AttributeError: - self._start_pos = NodePos(self._start_line, self._start_col) - return self._start_pos - - def end_pos(self): - try: - return self._end_pos - except AttributeError: - self._end_pos = NodePos(self._end_line, self._end_col) - return self._end_pos - - def __str__(self): - kind = self.kind - if not kind: - kind = '(none)' - return '%s>%s' % (_tok_names[kind], str(self.kids)) - - def is_equivalent(self, other, are_functions_equiv=False): - if not other: - return False - - # Bail out for functions - if not are_functions_equiv: - if self.kind == tok.FUNCTION: - return False - if self.kind == tok.LP and self.opcode == op.CALL: - return False - - if self.kind != other.kind: - return False - if self.opcode != other.opcode: - return False - - # Check atoms on names, properties, and string constants - if self.kind in (tok.NAME, tok.DOT, tok.STRING) and self.atom != other.atom: - return False - - # Check values on numbers - if self.kind == tok.NUMBER and self.dval != other.dval: - return False - - # Compare child nodes - if len(self.kids) != len(other.kids): - return False - for i in range(0, len(self.kids)): - # Watch for dead nodes - if not self.kids[i]: - if not other.kids[i]: return True - else: return False - if not self.kids[i].is_equivalent(other.kids[i]): - return False - - return True - -def _parse_comments(script, root, node_positions, ignore_ranges): - pos = 0 - single_line_re = r"//[^\r\n]*" - multi_line_re = r"/\*(.*?)\*/" - full_re = "(%s)|(%s)" % (single_line_re, multi_line_re) - comment_re = re.compile(full_re, re.DOTALL) - - comments = [] - while True: - match = comment_re.search(script, pos) - if not match: - return comments - - # Get the comment text - comment_text = script[match.start():match.end()] - if comment_text.startswith('/*'): - comment_text = comment_text[2:-2] - opcode = 'JSOP_C_COMMENT' - else: - comment_text = comment_text[2:] - opcode = 'JSOP_CPP_COMMENT' - opcode = opcode[5:].lower() - - start_offset = match.start()+1 - end_offset = match.end() - - # Make sure it doesn't start in a string or regexp - if not ignore_ranges.has(start_offset): - start_pos = node_positions.from_offset(start_offset) - end_pos = node_positions.from_offset(end_offset) - kwargs = { - 'type': 'COMMENT', - 'atom': comment_text, - 'opcode': opcode, - '_start_line': start_pos.line, - '_start_col': start_pos.col, - '_end_line': end_pos.line, - '_end_col': end_pos.col, - 'parent': None, - 'kids': [], - 'node_index': None - } - comment_node = _Node() - comment_node.__dict__.update(kwargs) - comments.append(comment_node) - pos = match.end() - else: - pos = match.start()+1 - -def parse(script, error_callback): - def _wrapped_callback(line, col, msg): - assert msg.startswith('JSMSG_') - msg = msg[6:].lower() - error_callback(line, col, msg) - - positions = NodePositions(script) - - roots = [] - nodes = [] - comment_ignore_ranges = NodeRanges() - def process(node): - if node.kind == tok.NUMBER: - node.atom = positions.text(node.start_pos(), node.end_pos()) - elif node.kind == tok.STRING or \ - (node.kind == tok.OBJECT and node.opcode == op.REGEXP): - start_offset = positions.to_offset(node.start_pos()) - end_offset = positions.to_offset(node.end_pos()) - comment_ignore_ranges.add(start_offset, end_offset) - for kid in node.kids: - if kid: - process(kid) - def pop(): - nodes.pop() - - roots = pyspidermonkey.traverse(script, _Node, _wrapped_callback) - assert len(roots) == 1 - root_node = roots[0] - process(root_node) - - comments = _parse_comments(script, root_node, positions, comment_ignore_ranges) - return root_node, comments - -def _dump_node(node, depth=0): - print '. '*depth, - if node is None: - print '(none)' - else: - print '%s\t%s, %s' % (_tok_names[node.kind], node.start_pos(), node.end_pos()) - for node in node.kids: - _dump_node(node, depth+1) - -def dump_tree(script): - def error_callback(line, col, msg): - print '(%i, %i): %s', (line, col, msg) - node, comments = parse(script, error_callback) - _dump_node(node) - -class TestComments(unittest.TestCase): - def _test(self, script, expected_comments): - root, comments = parse(script, lambda line, col, msg: None) - encountered_comments = [node.atom for node in comments] - self.assertEquals(encountered_comments, list(expected_comments)) - def testSimpleComments(self): - self._test('re = /\//g', ()) - self._test('re = /\///g', ()) - self._test('re = /\////g', ('g',)) - def testCComments(self): - self._test('/*a*//*b*/', ('a', 'b')) - self._test('/*a\r\na*//*b\r\nb*/', ('a\r\na', 'b\r\nb')) - self._test('a//*b*/c', ('*b*/c',)) - self._test('a///*b*/c', ('/*b*/c',)) - self._test('a/*//*/;', ('//',)) - self._test('a/*b*/+/*c*/d', ('b', 'c')) - -class TestNodePositions(unittest.TestCase): - def _test(self, text, expected_lines, expected_cols): - # Get a NodePos list - positions = NodePositions(text) - positions = [positions.from_offset(i) for i in range(0, len(text))] - encountered_lines = ''.join([str(x.line) for x in positions]) - encountered_cols = ''.join([str(x.col) for x in positions]) - self.assertEquals(encountered_lines, expected_lines.replace(' ', '')) - self.assertEquals(encountered_cols, expected_cols.replace(' ', '')) - def testSimple(self): - self._test( - 'abc\r\ndef\nghi\n\nj', - '0000 0 1111 2222 3 4', - '0123 4 0123 0123 0 0' - ) - self._test( - '\rabc', - '0 111', - '0 012' - ) - def testText(self): - pos = NodePositions('abc\r\ndef\n\nghi') - self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 0)), 'a') - self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 2)), 'abc') - self.assertEquals(pos.text(NodePos(0, 2), NodePos(1, 2)), 'c\r\ndef') - def testOffset(self): - pos = NodePositions('abc\r\ndef\n\nghi') - self.assertEquals(pos.to_offset(NodePos(0, 2)), 2) - self.assertEquals(pos.to_offset(NodePos(1, 0)), 5) - self.assertEquals(pos.to_offset(NodePos(3, 1)), 11) - -class TestNodeRanges(unittest.TestCase): - def testAdd(self): - r = NodeRanges() - r.add(5, 10) - self.assertEquals(r._offsets, [5,11]) - r.add(15, 20) - self.assertEquals(r._offsets, [5,11,15,21]) - r.add(21,22) - self.assertEquals(r._offsets, [5,11,15,23]) - r.add(4,5) - self.assertEquals(r._offsets, [4,11,15,23]) - r.add(9,11) - self.assertEquals(r._offsets, [4,12,15,23]) - r.add(10,20) - self.assertEquals(r._offsets, [4,23]) - r.add(4,22) - self.assertEquals(r._offsets, [4,23]) - r.add(30,30) - self.assertEquals(r._offsets, [4,23,30,31]) - def testHas(self): - r = NodeRanges() - r.add(5, 10) - r.add(15, 15) - assert not r.has(4) - assert r.has(5) - assert r.has(6) - assert r.has(9) - assert r.has(10) - assert not r.has(14) - assert r.has(15) - assert not r.has(16) -if __name__ == '__main__': - unittest.main() - +#!/usr/bin/python +""" Parses a script into nodes. """ +import bisect +import re +import unittest + +import pyspidermonkey +from pyspidermonkey import tok, op + +_tok_names = dict(zip( + [getattr(tok, prop) for prop in dir(tok)], + ['tok.%s' % prop for prop in dir(tok)] +)) + +class NodePos(): + def __init__(self, line, col): + self.line = line + self.col = col + def __cmp__(self, other): + if self.line < other.line: + return -1 + if self.line > other.line: + return 1 + if self.col < other.col: + return -1 + if self.col > other.col: + return 1 + return 0 + def __str__(self): + return '(line %i, col %i)' % (self.line+1, self.col+1) + +class NodePositions(): + " Given a string, allows [x] lookups for NodePos line and column numbers." + def __init__(self, text): + # Find the length of each line and incrementally sum all of the lengths + # to determine the ending position of each line. + self._lines = text.splitlines(True) + lines = [0] + [len(x) for x in self._lines] + for x in range(1, len(lines)): + lines[x] += lines[x-1] + self._line_offsets = lines + def from_offset(self, offset): + line = bisect.bisect(self._line_offsets, offset)-1 + col = offset - self._line_offsets[line] + return NodePos(line, col) + def to_offset(self, pos): + offset = self._line_offsets[pos.line] + pos.col + assert offset <= self._line_offsets[pos.line+1] # out-of-bounds col num + return offset + def text(self, start, end): + assert start <= end + # Trim the ending first in case it's a single line. + lines = self._lines[start.line:end.line+1] + lines[-1] = lines[-1][:end.col+1] + lines[0] = lines[0][start.col:] + return ''.join(lines) + +class NodeRanges(): + def __init__(self): + self._offsets = [] + def add(self, start, end): + i = bisect.bisect_left(self._offsets, start) + if i % 2 == 1: + i -= 1 + start = self._offsets[i] + + end = end + 1 + j = bisect.bisect_left(self._offsets, end) + if j % 2 == 1: + end = self._offsets[j] + j += 1 + + self._offsets[i:j] = [start,end] + def has(self, pos): + return bisect.bisect_right(self._offsets, pos) % 2 == 1 + +class _Node(): + def add_child(self, node): + if node: + node.node_index = len(self.kids) + node.parent = self + self.kids.append(node) + + def start_pos(self): + try: + return self._start_pos + except AttributeError: + self._start_pos = NodePos(self._start_line, self._start_col) + return self._start_pos + + def end_pos(self): + try: + return self._end_pos + except AttributeError: + self._end_pos = NodePos(self._end_line, self._end_col) + return self._end_pos + + def __str__(self): + kind = self.kind + if not kind: + kind = '(none)' + return '%s>%s' % (_tok_names[kind], str(self.kids)) + + def is_equivalent(self, other, are_functions_equiv=False): + if not other: + return False + + # Bail out for functions + if not are_functions_equiv: + if self.kind == tok.FUNCTION: + return False + if self.kind == tok.LP and self.opcode == op.CALL: + return False + + if self.kind != other.kind: + return False + if self.opcode != other.opcode: + return False + + # Check atoms on names, properties, and string constants + if self.kind in (tok.NAME, tok.DOT, tok.STRING) and self.atom != other.atom: + return False + + # Check values on numbers + if self.kind == tok.NUMBER and self.dval != other.dval: + return False + + # Compare child nodes + if len(self.kids) != len(other.kids): + return False + for i in range(0, len(self.kids)): + # Watch for dead nodes + if not self.kids[i]: + if not other.kids[i]: return True + else: return False + if not self.kids[i].is_equivalent(other.kids[i]): + return False + + return True + +def _parse_comments(script, root, node_positions, ignore_ranges): + pos = 0 + single_line_re = r"//[^\r\n]*" + multi_line_re = r"/\*(.*?)\*/" + full_re = "(%s)|(%s)" % (single_line_re, multi_line_re) + comment_re = re.compile(full_re, re.DOTALL) + + comments = [] + while True: + match = comment_re.search(script, pos) + if not match: + return comments + + # Get the comment text + comment_text = script[match.start():match.end()] + if comment_text.startswith('/*'): + comment_text = comment_text[2:-2] + opcode = 'JSOP_C_COMMENT' + else: + comment_text = comment_text[2:] + opcode = 'JSOP_CPP_COMMENT' + opcode = opcode[5:].lower() + + start_offset = match.start()+1 + end_offset = match.end() + + # Make sure it doesn't start in a string or regexp + if not ignore_ranges.has(start_offset): + start_pos = node_positions.from_offset(start_offset) + end_pos = node_positions.from_offset(end_offset) + kwargs = { + 'type': 'COMMENT', + 'atom': comment_text, + 'opcode': opcode, + '_start_line': start_pos.line, + '_start_col': start_pos.col, + '_end_line': end_pos.line, + '_end_col': end_pos.col, + 'parent': None, + 'kids': [], + 'node_index': None + } + comment_node = _Node() + comment_node.__dict__.update(kwargs) + comments.append(comment_node) + pos = match.end() + else: + pos = match.start()+1 + +def parse(script, error_callback): + def _wrapped_callback(line, col, msg): + assert msg.startswith('JSMSG_') + msg = msg[6:].lower() + error_callback(line, col, msg) + + positions = NodePositions(script) + + roots = [] + nodes = [] + comment_ignore_ranges = NodeRanges() + def process(node): + if node.kind == tok.NUMBER: + node.atom = positions.text(node.start_pos(), node.end_pos()) + elif node.kind == tok.STRING or \ + (node.kind == tok.OBJECT and node.opcode == op.REGEXP): + start_offset = positions.to_offset(node.start_pos()) + end_offset = positions.to_offset(node.end_pos()) + comment_ignore_ranges.add(start_offset, end_offset) + for kid in node.kids: + if kid: + process(kid) + def pop(): + nodes.pop() + + roots = pyspidermonkey.traverse(script, _Node, _wrapped_callback) + assert len(roots) == 1 + root_node = roots[0] + process(root_node) + + comments = _parse_comments(script, root_node, positions, comment_ignore_ranges) + return root_node, comments + +def _dump_node(node, depth=0): + print '. '*depth, + if node is None: + print '(none)' + else: + print '%s\t%s, %s' % (_tok_names[node.kind], node.start_pos(), node.end_pos()) + for node in node.kids: + _dump_node(node, depth+1) + +def dump_tree(script): + def error_callback(line, col, msg): + print '(%i, %i): %s', (line, col, msg) + node, comments = parse(script, error_callback) + _dump_node(node) + +class TestComments(unittest.TestCase): + def _test(self, script, expected_comments): + root, comments = parse(script, lambda line, col, msg: None) + encountered_comments = [node.atom for node in comments] + self.assertEquals(encountered_comments, list(expected_comments)) + def testSimpleComments(self): + self._test('re = /\//g', ()) + self._test('re = /\///g', ()) + self._test('re = /\////g', ('g',)) + def testCComments(self): + self._test('/*a*//*b*/', ('a', 'b')) + self._test('/*a\r\na*//*b\r\nb*/', ('a\r\na', 'b\r\nb')) + self._test('a//*b*/c', ('*b*/c',)) + self._test('a///*b*/c', ('/*b*/c',)) + self._test('a/*//*/;', ('//',)) + self._test('a/*b*/+/*c*/d', ('b', 'c')) + +class TestNodePositions(unittest.TestCase): + def _test(self, text, expected_lines, expected_cols): + # Get a NodePos list + positions = NodePositions(text) + positions = [positions.from_offset(i) for i in range(0, len(text))] + encountered_lines = ''.join([str(x.line) for x in positions]) + encountered_cols = ''.join([str(x.col) for x in positions]) + self.assertEquals(encountered_lines, expected_lines.replace(' ', '')) + self.assertEquals(encountered_cols, expected_cols.replace(' ', '')) + def testSimple(self): + self._test( + 'abc\r\ndef\nghi\n\nj', + '0000 0 1111 2222 3 4', + '0123 4 0123 0123 0 0' + ) + self._test( + '\rabc', + '0 111', + '0 012' + ) + def testText(self): + pos = NodePositions('abc\r\ndef\n\nghi') + self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 0)), 'a') + self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 2)), 'abc') + self.assertEquals(pos.text(NodePos(0, 2), NodePos(1, 2)), 'c\r\ndef') + def testOffset(self): + pos = NodePositions('abc\r\ndef\n\nghi') + self.assertEquals(pos.to_offset(NodePos(0, 2)), 2) + self.assertEquals(pos.to_offset(NodePos(1, 0)), 5) + self.assertEquals(pos.to_offset(NodePos(3, 1)), 11) + +class TestNodeRanges(unittest.TestCase): + def testAdd(self): + r = NodeRanges() + r.add(5, 10) + self.assertEquals(r._offsets, [5,11]) + r.add(15, 20) + self.assertEquals(r._offsets, [5,11,15,21]) + r.add(21,22) + self.assertEquals(r._offsets, [5,11,15,23]) + r.add(4,5) + self.assertEquals(r._offsets, [4,11,15,23]) + r.add(9,11) + self.assertEquals(r._offsets, [4,12,15,23]) + r.add(10,20) + self.assertEquals(r._offsets, [4,23]) + r.add(4,22) + self.assertEquals(r._offsets, [4,23]) + r.add(30,30) + self.assertEquals(r._offsets, [4,23,30,31]) + def testHas(self): + r = NodeRanges() + r.add(5, 10) + r.add(15, 15) + assert not r.has(4) + assert r.has(5) + assert r.has(6) + assert r.has(9) + assert r.has(10) + assert not r.has(14) + assert r.has(15) + assert not r.has(16) +if __name__ == '__main__': + unittest.main() + Property changes on: trunk/pyjsl/jsparse.py ___________________________________________________________________ Name: eol-style - native Name: svn:eol-style + native Property changes on: trunk/pyjsl/lint.py ___________________________________________________________________ Name: eol-style - native Name: svn:eol-style + native Modified: trunk/pyjsl/util.py =================================================================== --- trunk/pyjsl/util.py 2008-03-04 01:34:39 UTC (rev 161) +++ trunk/pyjsl/util.py 2008-03-04 03:10:46 UTC (rev 162) @@ -1,16 +1,16 @@ -import codecs -import os.path - -def readfile(path): - file = codecs.open(path, 'r', 'utf-8') - contents = file.read() - if contents[0] == unicode(codecs.BOM_UTF8, 'utf8'): - contents = contents[1:] - return contents - -def normpath(path): - path = os.path.abspath(path) - path = os.path.normcase(path) - path = os.path.normpath(path) - return path - +import codecs +import os.path + +def readfile(path): + file = codecs.open(path, 'r', 'utf-8') + contents = file.read() + if contents[0] == unicode(codecs.BOM_UTF8, 'utf8'): + contents = contents[1:] + return contents + +def normpath(path): + path = os.path.abspath(path) + path = os.path.normcase(path) + path = os.path.normpath(path) + return path + Property changes on: trunk/pyjsl/util.py ___________________________________________________________________ Name: eol-style - native Name: svn:eol-style + native Property changes on: trunk/pyjsl/visitation.py ___________________________________________________________________ Name: eol-style - native Name: svn:eol-style + native Property changes on: trunk/pyjsl/warnings.py ___________________________________________________________________ Name: eol-style - native Name: svn:eol-style + native Property changes on: trunk/pyspidermonkey/pyspidermonkey.c ___________________________________________________________________ Name: eol-style - native Property changes on: trunk/pyspidermonkey/tokens.tbl ___________________________________________________________________ Name: eol-style - native Name: svn:eol-style + native Property changes on: trunk/setup.py ___________________________________________________________________ Name: eol-style - native Name: svn:eol-style + native Property changes on: trunk/test.py ___________________________________________________________________ Name: eol-style - native Name: svn:eol-style + native This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2008-03-04 01:34:41
|
Revision: 161 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=161&view=rev Author: matthiasmiller Date: 2008-03-03 17:34:39 -0800 (Mon, 03 Mar 2008) Log Message: ----------- rename pyjsl.parse to pyjsl.htmlparse Modified Paths: -------------- trunk/pyjsl/lint.py Added Paths: ----------- trunk/pyjsl/jsparse.py Removed Paths: ------------- trunk/pyjsl/parse.py Copied: trunk/pyjsl/jsparse.py (from rev 157, trunk/pyjsl/parse.py) =================================================================== --- trunk/pyjsl/jsparse.py (rev 0) +++ trunk/pyjsl/jsparse.py 2008-03-04 01:34:39 UTC (rev 161) @@ -0,0 +1,319 @@ +#!/usr/bin/python +""" Parses a script into nodes. """ +import bisect +import re +import unittest + +import pyspidermonkey +from pyspidermonkey import tok, op + +_tok_names = dict(zip( + [getattr(tok, prop) for prop in dir(tok)], + ['tok.%s' % prop for prop in dir(tok)] +)) + +class NodePos(): + def __init__(self, line, col): + self.line = line + self.col = col + def __cmp__(self, other): + if self.line < other.line: + return -1 + if self.line > other.line: + return 1 + if self.col < other.col: + return -1 + if self.col > other.col: + return 1 + return 0 + def __str__(self): + return '(line %i, col %i)' % (self.line+1, self.col+1) + +class NodePositions(): + " Given a string, allows [x] lookups for NodePos line and column numbers." + def __init__(self, text): + # Find the length of each line and incrementally sum all of the lengths + # to determine the ending position of each line. + self._lines = text.splitlines(True) + lines = [0] + [len(x) for x in self._lines] + for x in range(1, len(lines)): + lines[x] += lines[x-1] + self._line_offsets = lines + def from_offset(self, offset): + line = bisect.bisect(self._line_offsets, offset)-1 + col = offset - self._line_offsets[line] + return NodePos(line, col) + def to_offset(self, pos): + offset = self._line_offsets[pos.line] + pos.col + assert offset <= self._line_offsets[pos.line+1] # out-of-bounds col num + return offset + def text(self, start, end): + assert start <= end + # Trim the ending first in case it's a single line. + lines = self._lines[start.line:end.line+1] + lines[-1] = lines[-1][:end.col+1] + lines[0] = lines[0][start.col:] + return ''.join(lines) + +class NodeRanges(): + def __init__(self): + self._offsets = [] + def add(self, start, end): + i = bisect.bisect_left(self._offsets, start) + if i % 2 == 1: + i -= 1 + start = self._offsets[i] + + end = end + 1 + j = bisect.bisect_left(self._offsets, end) + if j % 2 == 1: + end = self._offsets[j] + j += 1 + + self._offsets[i:j] = [start,end] + def has(self, pos): + return bisect.bisect_right(self._offsets, pos) % 2 == 1 + +class _Node(): + def add_child(self, node): + if node: + node.node_index = len(self.kids) + node.parent = self + self.kids.append(node) + + def start_pos(self): + try: + return self._start_pos + except AttributeError: + self._start_pos = NodePos(self._start_line, self._start_col) + return self._start_pos + + def end_pos(self): + try: + return self._end_pos + except AttributeError: + self._end_pos = NodePos(self._end_line, self._end_col) + return self._end_pos + + def __str__(self): + kind = self.kind + if not kind: + kind = '(none)' + return '%s>%s' % (_tok_names[kind], str(self.kids)) + + def is_equivalent(self, other, are_functions_equiv=False): + if not other: + return False + + # Bail out for functions + if not are_functions_equiv: + if self.kind == tok.FUNCTION: + return False + if self.kind == tok.LP and self.opcode == op.CALL: + return False + + if self.kind != other.kind: + return False + if self.opcode != other.opcode: + return False + + # Check atoms on names, properties, and string constants + if self.kind in (tok.NAME, tok.DOT, tok.STRING) and self.atom != other.atom: + return False + + # Check values on numbers + if self.kind == tok.NUMBER and self.dval != other.dval: + return False + + # Compare child nodes + if len(self.kids) != len(other.kids): + return False + for i in range(0, len(self.kids)): + # Watch for dead nodes + if not self.kids[i]: + if not other.kids[i]: return True + else: return False + if not self.kids[i].is_equivalent(other.kids[i]): + return False + + return True + +def _parse_comments(script, root, node_positions, ignore_ranges): + pos = 0 + single_line_re = r"//[^\r\n]*" + multi_line_re = r"/\*(.*?)\*/" + full_re = "(%s)|(%s)" % (single_line_re, multi_line_re) + comment_re = re.compile(full_re, re.DOTALL) + + comments = [] + while True: + match = comment_re.search(script, pos) + if not match: + return comments + + # Get the comment text + comment_text = script[match.start():match.end()] + if comment_text.startswith('/*'): + comment_text = comment_text[2:-2] + opcode = 'JSOP_C_COMMENT' + else: + comment_text = comment_text[2:] + opcode = 'JSOP_CPP_COMMENT' + opcode = opcode[5:].lower() + + start_offset = match.start()+1 + end_offset = match.end() + + # Make sure it doesn't start in a string or regexp + if not ignore_ranges.has(start_offset): + start_pos = node_positions.from_offset(start_offset) + end_pos = node_positions.from_offset(end_offset) + kwargs = { + 'type': 'COMMENT', + 'atom': comment_text, + 'opcode': opcode, + '_start_line': start_pos.line, + '_start_col': start_pos.col, + '_end_line': end_pos.line, + '_end_col': end_pos.col, + 'parent': None, + 'kids': [], + 'node_index': None + } + comment_node = _Node() + comment_node.__dict__.update(kwargs) + comments.append(comment_node) + pos = match.end() + else: + pos = match.start()+1 + +def parse(script, error_callback): + def _wrapped_callback(line, col, msg): + assert msg.startswith('JSMSG_') + msg = msg[6:].lower() + error_callback(line, col, msg) + + positions = NodePositions(script) + + roots = [] + nodes = [] + comment_ignore_ranges = NodeRanges() + def process(node): + if node.kind == tok.NUMBER: + node.atom = positions.text(node.start_pos(), node.end_pos()) + elif node.kind == tok.STRING or \ + (node.kind == tok.OBJECT and node.opcode == op.REGEXP): + start_offset = positions.to_offset(node.start_pos()) + end_offset = positions.to_offset(node.end_pos()) + comment_ignore_ranges.add(start_offset, end_offset) + for kid in node.kids: + if kid: + process(kid) + def pop(): + nodes.pop() + + roots = pyspidermonkey.traverse(script, _Node, _wrapped_callback) + assert len(roots) == 1 + root_node = roots[0] + process(root_node) + + comments = _parse_comments(script, root_node, positions, comment_ignore_ranges) + return root_node, comments + +def _dump_node(node, depth=0): + print '. '*depth, + if node is None: + print '(none)' + else: + print '%s\t%s, %s' % (_tok_names[node.kind], node.start_pos(), node.end_pos()) + for node in node.kids: + _dump_node(node, depth+1) + +def dump_tree(script): + def error_callback(line, col, msg): + print '(%i, %i): %s', (line, col, msg) + node, comments = parse(script, error_callback) + _dump_node(node) + +class TestComments(unittest.TestCase): + def _test(self, script, expected_comments): + root, comments = parse(script, lambda line, col, msg: None) + encountered_comments = [node.atom for node in comments] + self.assertEquals(encountered_comments, list(expected_comments)) + def testSimpleComments(self): + self._test('re = /\//g', ()) + self._test('re = /\///g', ()) + self._test('re = /\////g', ('g',)) + def testCComments(self): + self._test('/*a*//*b*/', ('a', 'b')) + self._test('/*a\r\na*//*b\r\nb*/', ('a\r\na', 'b\r\nb')) + self._test('a//*b*/c', ('*b*/c',)) + self._test('a///*b*/c', ('/*b*/c',)) + self._test('a/*//*/;', ('//',)) + self._test('a/*b*/+/*c*/d', ('b', 'c')) + +class TestNodePositions(unittest.TestCase): + def _test(self, text, expected_lines, expected_cols): + # Get a NodePos list + positions = NodePositions(text) + positions = [positions.from_offset(i) for i in range(0, len(text))] + encountered_lines = ''.join([str(x.line) for x in positions]) + encountered_cols = ''.join([str(x.col) for x in positions]) + self.assertEquals(encountered_lines, expected_lines.replace(' ', '')) + self.assertEquals(encountered_cols, expected_cols.replace(' ', '')) + def testSimple(self): + self._test( + 'abc\r\ndef\nghi\n\nj', + '0000 0 1111 2222 3 4', + '0123 4 0123 0123 0 0' + ) + self._test( + '\rabc', + '0 111', + '0 012' + ) + def testText(self): + pos = NodePositions('abc\r\ndef\n\nghi') + self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 0)), 'a') + self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 2)), 'abc') + self.assertEquals(pos.text(NodePos(0, 2), NodePos(1, 2)), 'c\r\ndef') + def testOffset(self): + pos = NodePositions('abc\r\ndef\n\nghi') + self.assertEquals(pos.to_offset(NodePos(0, 2)), 2) + self.assertEquals(pos.to_offset(NodePos(1, 0)), 5) + self.assertEquals(pos.to_offset(NodePos(3, 1)), 11) + +class TestNodeRanges(unittest.TestCase): + def testAdd(self): + r = NodeRanges() + r.add(5, 10) + self.assertEquals(r._offsets, [5,11]) + r.add(15, 20) + self.assertEquals(r._offsets, [5,11,15,21]) + r.add(21,22) + self.assertEquals(r._offsets, [5,11,15,23]) + r.add(4,5) + self.assertEquals(r._offsets, [4,11,15,23]) + r.add(9,11) + self.assertEquals(r._offsets, [4,12,15,23]) + r.add(10,20) + self.assertEquals(r._offsets, [4,23]) + r.add(4,22) + self.assertEquals(r._offsets, [4,23]) + r.add(30,30) + self.assertEquals(r._offsets, [4,23,30,31]) + def testHas(self): + r = NodeRanges() + r.add(5, 10) + r.add(15, 15) + assert not r.has(4) + assert r.has(5) + assert r.has(6) + assert r.has(9) + assert r.has(10) + assert not r.has(14) + assert r.has(15) + assert not r.has(16) +if __name__ == '__main__': + unittest.main() + Modified: trunk/pyjsl/lint.py =================================================================== --- trunk/pyjsl/lint.py 2008-03-03 22:53:12 UTC (rev 160) +++ trunk/pyjsl/lint.py 2008-03-04 01:34:39 UTC (rev 161) @@ -3,7 +3,7 @@ import re import conf -import parse +import jsparse import visitation import warnings import util @@ -149,7 +149,7 @@ def _lint_script(script, script_cache, lint_error, conf, import_callback): def parse_error(row, col, msg): if not msg in ('redeclared_var', 'var_hides_arg'): - parse_errors.append((parse.NodePos(row, col), msg)) + parse_errors.append((jsparse.NodePos(row, col), msg)) def report(node, errname): _report(node.start_pos(), errname, True) @@ -169,7 +169,7 @@ return lint_error(pos.line, pos.col, errname) parse_errors = [] - root, comments = parse.parse(script, parse_error) + root, comments = jsparse.parse(script, parse_error) ignores = [] start_ignore = None declares = [] Deleted: trunk/pyjsl/parse.py =================================================================== --- trunk/pyjsl/parse.py 2008-03-03 22:53:12 UTC (rev 160) +++ trunk/pyjsl/parse.py 2008-03-04 01:34:39 UTC (rev 161) @@ -1,321 +0,0 @@ -#!/usr/bin/python -""" Parses a script into nodes. """ -import bisect -import re -import unittest - -import pyspidermonkey -from pyspidermonkey import tok, op - -_tok_names = dict(zip( - [getattr(tok, prop) for prop in dir(tok)], - ['tok.%s' % prop for prop in dir(tok)] -)) -_op_names = dict(zip( - [getattr(op, prop) for prop in dir(op)], - ['op.%s' % prop for prop in dir(op)] -)) - -class NodePos(): - def __init__(self, line, col): - self.line = line - self.col = col - def __cmp__(self, other): - if self.line < other.line: - return -1 - if self.line > other.line: - return 1 - if self.col < other.col: - return -1 - if self.col > other.col: - return 1 - return 0 - def __str__(self): - return '(line %i, col %i)' % (self.line+1, self.col+1) - -class NodePositions(): - " Given a string, allows [x] lookups for NodePos line and column numbers." - def __init__(self, text): - # Find the length of each line and incrementally sum all of the lengths - # to determine the ending position of each line. - self._lines = text.splitlines(True) - lines = [0] + [len(x) for x in self._lines] - for x in range(1, len(lines)): - lines[x] += lines[x-1] - self._line_offsets = lines - def from_offset(self, offset): - line = bisect.bisect(self._line_offsets, offset)-1 - col = offset - self._line_offsets[line] - return NodePos(line, col) - def to_offset(self, pos): - offset = self._line_offsets[pos.line] + pos.col - assert offset <= self._line_offsets[pos.line+1] # out-of-bounds col num - return offset - def text(self, start, end): - assert start <= end - # Trim the ending first in case it's a single line. - lines = self._lines[start.line:end.line+1] - lines[-1] = lines[-1][:end.col+1] - lines[0] = lines[0][start.col:] - return ''.join(lines) - -class NodeRanges(): - def __init__(self): - self._offsets = [] - def add(self, start, end): - i = bisect.bisect_left(self._offsets, start) - if i % 2 == 1: - i -= 1 - start = self._offsets[i] - - end = end + 1 - j = bisect.bisect_left(self._offsets, end) - if j % 2 == 1: - end = self._offsets[j] - j += 1 - - self._offsets[i:j] = [start,end] - def has(self, pos): - return bisect.bisect_right(self._offsets, pos) % 2 == 1 - -class _Node(): - def add_child(self, node): - if node: - node.node_index = len(self.kids) - node.parent = self - self.kids.append(node) - - def start_pos(self): - try: - return self._start_pos - except AttributeError: - self._start_pos = NodePos(self._start_line, self._start_col) - return self._start_pos - - def end_pos(self): - try: - return self._end_pos - except AttributeError: - self._end_pos = NodePos(self._end_line, self._end_col) - return self._end_pos - - def __str__(self): - kind = self.kind - if not kind: - kind = '(none)' - return '%s>%s' % (_tok_names[kind], str(self.kids)) - - def is_equivalent(self, other, are_functions_equiv=False): - if not other: - return False - - # Bail out for functions - if not are_functions_equiv: - if self.kind == tok.FUNCTION: - return False - if self.kind == tok.LP and self.opcode == op.CALL: - return False - - if self.kind != other.kind: - return False - if self.opcode != other.opcode: - return False - - # Check atoms on names, properties, and string constants - if self.kind in (tok.NAME, tok.DOT, tok.STRING) and self.atom != other.atom: - return False - - # Check values on numbers - if self.kind == tok.NUMBER and self.dval != other.dval: - return False - - # Compare child nodes - if len(self.kids) != len(other.kids): - return False - for i in range(0, len(self.kids)): - # Watch for dead nodes - if not self.kids[i]: - if not other.kids[i]: return True - else: return False - if not self.kids[i].is_equivalent(other.kids[i]): - return False - - return True - -def _parse_comments(script, root, node_positions, ignore_ranges): - pos = 0 - single_line_re = r"//[^\r\n]*" - multi_line_re = r"/\*(.*?)\*/" - full_re = "(%s)|(%s)" % (single_line_re, multi_line_re) - comment_re = re.compile(full_re, re.DOTALL) - - comments = [] - while True: - match = comment_re.search(script, pos) - if not match: - return comments - - # Get the comment text - comment_text = script[match.start():match.end()] - if comment_text.startswith('/*'): - comment_text = comment_text[2:-2] - opcode = 'JSOP_C_COMMENT' - else: - comment_text = comment_text[2:] - opcode = 'JSOP_CPP_COMMENT' - opcode = opcode[5:].lower() - - start_offset = match.start()+1 - end_offset = match.end() - - # Make sure it doesn't start in a string or regexp - if not ignore_ranges.has(start_offset): - start_pos = node_positions.from_offset(start_offset) - end_pos = node_positions.from_offset(end_offset) - kwargs = { - 'type': 'COMMENT', - 'atom': comment_text, - 'opcode': opcode, - '_start_line': start_pos.line, - '_start_col': start_pos.col, - '_end_line': end_pos.line, - '_end_col': end_pos.col, - 'parent': None, - 'kids': [], - 'node_index': None - } - comment_node = _Node() - comment_node.__dict__.update(kwargs) - comments.append(comment_node) - pos = match.end() - else: - pos = match.start()+1 - -def parse(script, error_callback): - def _wrapped_callback(line, col, msg): - assert msg.startswith('JSMSG_') - msg = msg[6:].lower() - error_callback(line, col, msg) - - positions = NodePositions(script) - - roots = [] - nodes = [] - comment_ignore_ranges = NodeRanges() - def process(node): - if node.kind == tok.NUMBER: - node.atom = positions.text(node.start_pos(), node.end_pos()) - elif node.kind == tok.STRING or \ - (node.kind == tok.OBJECT and node.opcode == op.REGEXP): - start_offset = positions.to_offset(node.start_pos()) - end_offset = positions.to_offset(node.end_pos()) - comment_ignore_ranges.add(start_offset, end_offset) - for kid in node.kids: - if kid: - process(kid) - - roots = pyspidermonkey.traverse(script, _Node, _wrapped_callback) - assert len(roots) == 1 - root_node = roots[0] - process(root_node) - - comments = _parse_comments(script, root_node, positions, comment_ignore_ranges) - return root_node, comments - -def _dump_node(node, depth=0): - print '. '*depth, - if node is None: - print '(none)' - else: - print '%s, %s\tfrom %s to %s' % (_tok_names[node.kind], _op_names[node.opcode], node.start_pos(), node.end_pos()) - for node in node.kids: - _dump_node(node, depth+1) - -def dump_tree(script): - def error_callback(line, col, msg): - print '(%i, %i): %s', (line, col, msg) - node, comments = parse(script, error_callback) - _dump_node(node) - -class TestComments(unittest.TestCase): - def _test(self, script, expected_comments): - root, comments = parse(script, lambda line, col, msg: None) - encountered_comments = [node.atom for node in comments] - self.assertEquals(encountered_comments, list(expected_comments)) - def testSimpleComments(self): - self._test('re = /\//g', ()) - self._test('re = /\///g', ()) - self._test('re = /\////g', ('g',)) - def testCComments(self): - self._test('/*a*//*b*/', ('a', 'b')) - self._test('/*a\r\na*//*b\r\nb*/', ('a\r\na', 'b\r\nb')) - self._test('a//*b*/c', ('*b*/c',)) - self._test('a///*b*/c', ('/*b*/c',)) - self._test('a/*//*/;', ('//',)) - self._test('a/*b*/+/*c*/d', ('b', 'c')) - -class TestNodePositions(unittest.TestCase): - def _test(self, text, expected_lines, expected_cols): - # Get a NodePos list - positions = NodePositions(text) - positions = [positions.from_offset(i) for i in range(0, len(text))] - encountered_lines = ''.join([str(x.line) for x in positions]) - encountered_cols = ''.join([str(x.col) for x in positions]) - self.assertEquals(encountered_lines, expected_lines.replace(' ', '')) - self.assertEquals(encountered_cols, expected_cols.replace(' ', '')) - def testSimple(self): - self._test( - 'abc\r\ndef\nghi\n\nj', - '0000 0 1111 2222 3 4', - '0123 4 0123 0123 0 0' - ) - self._test( - '\rabc', - '0 111', - '0 012' - ) - def testText(self): - pos = NodePositions('abc\r\ndef\n\nghi') - self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 0)), 'a') - self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 2)), 'abc') - self.assertEquals(pos.text(NodePos(0, 2), NodePos(1, 2)), 'c\r\ndef') - def testOffset(self): - pos = NodePositions('abc\r\ndef\n\nghi') - self.assertEquals(pos.to_offset(NodePos(0, 2)), 2) - self.assertEquals(pos.to_offset(NodePos(1, 0)), 5) - self.assertEquals(pos.to_offset(NodePos(3, 1)), 11) - -class TestNodeRanges(unittest.TestCase): - def testAdd(self): - r = NodeRanges() - r.add(5, 10) - self.assertEquals(r._offsets, [5,11]) - r.add(15, 20) - self.assertEquals(r._offsets, [5,11,15,21]) - r.add(21,22) - self.assertEquals(r._offsets, [5,11,15,23]) - r.add(4,5) - self.assertEquals(r._offsets, [4,11,15,23]) - r.add(9,11) - self.assertEquals(r._offsets, [4,12,15,23]) - r.add(10,20) - self.assertEquals(r._offsets, [4,23]) - r.add(4,22) - self.assertEquals(r._offsets, [4,23]) - r.add(30,30) - self.assertEquals(r._offsets, [4,23,30,31]) - def testHas(self): - r = NodeRanges() - r.add(5, 10) - r.add(15, 15) - assert not r.has(4) - assert r.has(5) - assert r.has(6) - assert r.has(9) - assert r.has(10) - assert not r.has(14) - assert r.has(15) - assert not r.has(16) -if __name__ == '__main__': - unittest.main() - This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2008-03-03 22:53:16
|
Revision: 160 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=160&view=rev Author: matthiasmiller Date: 2008-03-03 14:53:12 -0800 (Mon, 03 Mar 2008) Log Message: ----------- change --dump to output opcodes Modified Paths: -------------- trunk/pyjsl/parse.py Modified: trunk/pyjsl/parse.py =================================================================== --- trunk/pyjsl/parse.py 2008-03-03 22:49:59 UTC (rev 159) +++ trunk/pyjsl/parse.py 2008-03-03 22:53:12 UTC (rev 160) @@ -11,6 +11,10 @@ [getattr(tok, prop) for prop in dir(tok)], ['tok.%s' % prop for prop in dir(tok)] )) +_op_names = dict(zip( + [getattr(op, prop) for prop in dir(op)], + ['op.%s' % prop for prop in dir(op)] +)) class NodePos(): def __init__(self, line, col): @@ -223,7 +227,7 @@ if node is None: print '(none)' else: - print '%s\t%s, %s' % (_tok_names[node.kind], node.start_pos(), node.end_pos()) + print '%s, %s\tfrom %s to %s' % (_tok_names[node.kind], _op_names[node.opcode], node.start_pos(), node.end_pos()) for node in node.kids: _dump_node(node, depth+1) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2008-03-03 22:50:06
|
Revision: 159 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=159&view=rev Author: matthiasmiller Date: 2008-03-03 14:49:59 -0800 (Mon, 03 Mar 2008) Log Message: ----------- remove obsolete code Modified Paths: -------------- trunk/pyjsl/parse.py Modified: trunk/pyjsl/parse.py =================================================================== --- trunk/pyjsl/parse.py 2008-03-03 22:46:15 UTC (rev 158) +++ trunk/pyjsl/parse.py 2008-03-03 22:49:59 UTC (rev 159) @@ -209,8 +209,6 @@ for kid in node.kids: if kid: process(kid) - def pop(): - nodes.pop() roots = pyspidermonkey.traverse(script, _Node, _wrapped_callback) assert len(roots) == 1 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2008-03-03 22:46:17
|
Revision: 158 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=158&view=rev Author: matthiasmiller Date: 2008-03-03 14:46:15 -0800 (Mon, 03 Mar 2008) Log Message: ----------- pyspidermonkey.c: rename kw to the more descriptive pynode Modified Paths: -------------- trunk/pyspidermonkey/pyspidermonkey.c Modified: trunk/pyspidermonkey/pyspidermonkey.c =================================================================== --- trunk/pyspidermonkey/pyspidermonkey.c 2008-03-03 22:43:57 UTC (rev 157) +++ trunk/pyspidermonkey/pyspidermonkey.c 2008-03-03 22:46:15 UTC (rev 158) @@ -157,7 +157,7 @@ static int traverse_node(JSContext* context, JSParseNode* jsnode, PyObject* parent, PyObject* tuple, int node_offset) { JSContextData* data = JS_GetContextPrivate(context); - PyObject* kw = NULL; + PyObject* pynode = NULL; PyObject* kids = NULL; /* TODO: make sure no tuple item already exists */ @@ -169,42 +169,42 @@ } /* pass in a dictionary of options */ - kw = PyInstance_New(data->node_class, NULL, NULL); - if (!kw) + pynode = PyInstance_New(data->node_class, NULL, NULL); + if (!pynode) goto fail; - PyTuple_SET_ITEM(tuple, node_offset, kw); + PyTuple_SET_ITEM(tuple, node_offset, pynode); Py_INCREF(parent); - if (PyObject_SetAttrString(kw, "parent", parent) == -1) + if (PyObject_SetAttrString(pynode, "parent", parent) == -1) goto fail; - if (PyObject_SetAttrString(kw, "kind", Py_BuildValue("i", TOK_TO_NUM(jsnode->pn_type))) == -1) + if (PyObject_SetAttrString(pynode, "kind", Py_BuildValue("i", TOK_TO_NUM(jsnode->pn_type))) == -1) goto fail; - if (PyObject_SetAttrString(kw, "node_index", Py_BuildValue("i", node_offset)) == -1) + if (PyObject_SetAttrString(pynode, "node_index", Py_BuildValue("i", node_offset)) == -1) goto fail; /* pass the position */ - if (PyObject_SetAttrString(kw, "_start_line", Py_BuildValue("i", jsnode->pn_pos.begin.lineno-1)) == -1) + if (PyObject_SetAttrString(pynode, "_start_line", Py_BuildValue("i", jsnode->pn_pos.begin.lineno-1)) == -1) goto fail; - if (PyObject_SetAttrString(kw, "_start_col", Py_BuildValue("i", jsnode->pn_pos.begin.index)) == -1) + if (PyObject_SetAttrString(pynode, "_start_col", Py_BuildValue("i", jsnode->pn_pos.begin.index)) == -1) goto fail; - if (PyObject_SetAttrString(kw, "_end_line", Py_BuildValue("i", jsnode->pn_pos.end.lineno-1)) == -1) + if (PyObject_SetAttrString(pynode, "_end_line", Py_BuildValue("i", jsnode->pn_pos.end.lineno-1)) == -1) goto fail; - if (PyObject_SetAttrString(kw, "_end_col", Py_BuildValue("i", jsnode->pn_pos.end.index)) == -1) + if (PyObject_SetAttrString(pynode, "_end_col", Py_BuildValue("i", jsnode->pn_pos.end.index)) == -1) goto fail; if ((jsnode->pn_type == TOK_NAME || jsnode->pn_type == TOK_DOT || jsnode->pn_type == TOK_STRING) && ATOM_IS_STRING(jsnode->pn_atom)) { /* Convert the atom to a string. */ - if (PyObject_SetAttrString(kw, "atom", atom_to_string(jsnode->pn_atom)) == -1) + if (PyObject_SetAttrString(pynode, "atom", atom_to_string(jsnode->pn_atom)) == -1) goto fail; } - if (PyObject_SetAttrString(kw, "opcode", Py_BuildValue("i", OPCODE_TO_NUM(jsnode->pn_op))) == -1) + if (PyObject_SetAttrString(pynode, "opcode", Py_BuildValue("i", OPCODE_TO_NUM(jsnode->pn_op))) == -1) goto fail; if (jsnode->pn_type == TOK_NUMBER) { - if (PyObject_SetAttrString(kw, "dval", Py_BuildValue("d", jsnode->pn_dval)) == -1) + if (PyObject_SetAttrString(pynode, "dval", Py_BuildValue("d", jsnode->pn_dval)) == -1) goto fail; } @@ -226,7 +226,7 @@ Py_INCREF(Py_None); fn_name = Py_None; } - if (PyObject_SetAttrString(kw, "fn_name", fn_name) == -1) + if (PyObject_SetAttrString(pynode, "fn_name", fn_name) == -1) goto fail; /* get the function arguments */ @@ -256,19 +256,19 @@ name = atom_to_string(JSID_TO_ATOM(scope_property->id)); PyTuple_SET_ITEM(fn_args, (uint16)scope_property->shortid, name); } - if (PyObject_SetAttrString(kw, "fn_args", fn_args) == -1) + if (PyObject_SetAttrString(pynode, "fn_args", fn_args) == -1) goto fail; } else if (jsnode->pn_type == TOK_RB) { PyObject* end_comma = PyBool_FromLong(jsnode->pn_extra & PNX_ENDCOMMA); - if (PyObject_SetAttrString(kw, "end_comma", end_comma) == -1) + if (PyObject_SetAttrString(pynode, "end_comma", end_comma) == -1) goto fail; } switch (jsnode->pn_arity) { case PN_FUNC: kids = PyTuple_New(1); - if (traverse_node(context, jsnode->pn_body, kw, kids, 0) == -1) + if (traverse_node(context, jsnode->pn_body, pynode, kids, 0) == -1) return -1; break; @@ -277,7 +277,7 @@ int i; kids = PyTuple_New(jsnode->pn_count); for (i = 0, p = jsnode->pn_head; p; p = p->pn_next, i++) { - if (traverse_node(context, p, kw, kids, i) == -1) + if (traverse_node(context, p, pynode, kids, i) == -1) return -1; } } @@ -285,31 +285,31 @@ case PN_TERNARY: kids = PyTuple_New(3); - if (traverse_node(context, jsnode->pn_kid1, kw, kids, 0) == -1) + if (traverse_node(context, jsnode->pn_kid1, pynode, kids, 0) == -1) return -1; - if (traverse_node(context, jsnode->pn_kid2, kw, kids, 1) == -1) + if (traverse_node(context, jsnode->pn_kid2, pynode, kids, 1) == -1) return -1; - if (traverse_node(context, jsnode->pn_kid3, kw, kids, 2) == -1) + if (traverse_node(context, jsnode->pn_kid3, pynode, kids, 2) == -1) return -1; break; case PN_BINARY: kids = PyTuple_New(2); - if (traverse_node(context, jsnode->pn_left, kw, kids, 0) == -1) + if (traverse_node(context, jsnode->pn_left, pynode, kids, 0) == -1) return -1; - if (traverse_node(context, jsnode->pn_right, kw, kids, 1) == -1) + if (traverse_node(context, jsnode->pn_right, pynode, kids, 1) == -1) return -1; break; case PN_UNARY: kids = PyTuple_New(1); - if (traverse_node(context, jsnode->pn_kid, kw, kids, 0) == -1) + if (traverse_node(context, jsnode->pn_kid, pynode, kids, 0) == -1) return -1; break; case PN_NAME: kids = PyTuple_New(1); - if (traverse_node(context, jsnode->pn_expr, kw, kids, 0) == -1) + if (traverse_node(context, jsnode->pn_expr, pynode, kids, 0) == -1) return -1; break; @@ -318,14 +318,14 @@ break; } - if (PyObject_SetAttrString(kw, "kids", kids) == -1) + if (PyObject_SetAttrString(pynode, "kids", kids) == -1) goto fail; return 0; fail: - if (kw) { - Py_XDECREF(kw); + if (pynode) { + Py_XDECREF(pynode); } return -1; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2008-03-03 22:43:59
|
Revision: 157 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=157&view=rev Author: matthiasmiller Date: 2008-03-03 14:43:57 -0800 (Mon, 03 Mar 2008) Log Message: ----------- change the c module to instantiate the Node objects and set the node's attributes to speed up parsing Modified Paths: -------------- trunk/pyjsl/parse.py trunk/pyspidermonkey/pyspidermonkey.c Modified: trunk/pyjsl/parse.py =================================================================== --- trunk/pyjsl/parse.py 2008-03-03 18:41:11 UTC (rev 156) +++ trunk/pyjsl/parse.py 2008-03-03 22:43:57 UTC (rev 157) @@ -75,13 +75,6 @@ return bisect.bisect_right(self._offsets, pos) % 2 == 1 class _Node(): - def __init__(self, kids, parent=None, **kwargs): - _to_node = lambda kid: kid and _Node(parent=self, **kid) - self.__dict__.update(kwargs) - self.args = kwargs - self.parent = parent - self.kids = map(_to_node, kids) - def add_child(self, node): if node: node.node_index = len(self.kids) @@ -187,7 +180,8 @@ 'kids': [], 'node_index': None } - comment_node = _Node(**kwargs) + comment_node = _Node() + comment_node.__dict__.update(kwargs) comments.append(comment_node) pos = match.end() else: @@ -218,9 +212,9 @@ def pop(): nodes.pop() - roots = pyspidermonkey.traverse(script, _wrapped_callback) + roots = pyspidermonkey.traverse(script, _Node, _wrapped_callback) assert len(roots) == 1 - root_node = _Node(**roots[0]) + root_node = roots[0] process(root_node) comments = _parse_comments(script, root_node, positions, comment_ignore_ranges) @@ -231,7 +225,7 @@ if node is None: print '(none)' else: - print _tok_names[node.kind], '\t', node.args + print '%s\t%s, %s' % (_tok_names[node.kind], node.start_pos(), node.end_pos()) for node in node.kids: _dump_node(node, depth+1) Modified: trunk/pyspidermonkey/pyspidermonkey.c =================================================================== --- trunk/pyspidermonkey/pyspidermonkey.c 2008-03-03 18:41:11 UTC (rev 156) +++ trunk/pyspidermonkey/pyspidermonkey.c 2008-03-03 22:43:57 UTC (rev 157) @@ -112,6 +112,7 @@ */ typedef struct JSContextData { + PyObject* node_class; PyObject* error_callback; } JSContextData; @@ -154,7 +155,8 @@ /* returns 0 on success and -1 on failure */ static int -traverse_node(JSContext* context, JSParseNode* jsnode, PyObject* tuple, int node_offset) { +traverse_node(JSContext* context, JSParseNode* jsnode, PyObject* parent, PyObject* tuple, int node_offset) { + JSContextData* data = JS_GetContextPrivate(context); PyObject* kw = NULL; PyObject* kids = NULL; @@ -167,39 +169,42 @@ } /* pass in a dictionary of options */ - kw = PyDict_New(); + kw = PyInstance_New(data->node_class, NULL, NULL); if (!kw) goto fail; PyTuple_SET_ITEM(tuple, node_offset, kw); - if (PyDict_SetItemString(kw, "kind", Py_BuildValue("i", TOK_TO_NUM(jsnode->pn_type))) == -1) + Py_INCREF(parent); + if (PyObject_SetAttrString(kw, "parent", parent) == -1) goto fail; - if (PyDict_SetItemString(kw, "node_index", Py_BuildValue("i", node_offset)) == -1) + if (PyObject_SetAttrString(kw, "kind", Py_BuildValue("i", TOK_TO_NUM(jsnode->pn_type))) == -1) goto fail; + if (PyObject_SetAttrString(kw, "node_index", Py_BuildValue("i", node_offset)) == -1) + goto fail; /* pass the position */ - if (PyDict_SetItemString(kw, "_start_line", Py_BuildValue("i", jsnode->pn_pos.begin.lineno-1)) == -1) + if (PyObject_SetAttrString(kw, "_start_line", Py_BuildValue("i", jsnode->pn_pos.begin.lineno-1)) == -1) goto fail; - if (PyDict_SetItemString(kw, "_start_col", Py_BuildValue("i", jsnode->pn_pos.begin.index)) == -1) + if (PyObject_SetAttrString(kw, "_start_col", Py_BuildValue("i", jsnode->pn_pos.begin.index)) == -1) goto fail; - if (PyDict_SetItemString(kw, "_end_line", Py_BuildValue("i", jsnode->pn_pos.end.lineno-1)) == -1) + if (PyObject_SetAttrString(kw, "_end_line", Py_BuildValue("i", jsnode->pn_pos.end.lineno-1)) == -1) goto fail; - if (PyDict_SetItemString(kw, "_end_col", Py_BuildValue("i", jsnode->pn_pos.end.index)) == -1) + if (PyObject_SetAttrString(kw, "_end_col", Py_BuildValue("i", jsnode->pn_pos.end.index)) == -1) goto fail; if ((jsnode->pn_type == TOK_NAME || jsnode->pn_type == TOK_DOT || jsnode->pn_type == TOK_STRING) && ATOM_IS_STRING(jsnode->pn_atom)) { /* Convert the atom to a string. */ - if (PyDict_SetItemString(kw, "atom", atom_to_string(jsnode->pn_atom)) == -1) + if (PyObject_SetAttrString(kw, "atom", atom_to_string(jsnode->pn_atom)) == -1) goto fail; } - if (PyDict_SetItemString(kw, "opcode", Py_BuildValue("i", OPCODE_TO_NUM(jsnode->pn_op))) == -1) + if (PyObject_SetAttrString(kw, "opcode", Py_BuildValue("i", OPCODE_TO_NUM(jsnode->pn_op))) == -1) goto fail; if (jsnode->pn_type == TOK_NUMBER) { - if (PyDict_SetItemString(kw, "dval", Py_BuildValue("d", jsnode->pn_dval)) == -1) + if (PyObject_SetAttrString(kw, "dval", Py_BuildValue("d", jsnode->pn_dval)) == -1) goto fail; } @@ -221,7 +226,7 @@ Py_INCREF(Py_None); fn_name = Py_None; } - if (PyDict_SetItemString(kw, "fn_name", fn_name) == -1) + if (PyObject_SetAttrString(kw, "fn_name", fn_name) == -1) goto fail; /* get the function arguments */ @@ -251,19 +256,19 @@ name = atom_to_string(JSID_TO_ATOM(scope_property->id)); PyTuple_SET_ITEM(fn_args, (uint16)scope_property->shortid, name); } - if (PyDict_SetItemString(kw, "fn_args", fn_args) == -1) + if (PyObject_SetAttrString(kw, "fn_args", fn_args) == -1) goto fail; } else if (jsnode->pn_type == TOK_RB) { PyObject* end_comma = PyBool_FromLong(jsnode->pn_extra & PNX_ENDCOMMA); - if (PyDict_SetItemString(kw, "end_comma", end_comma) == -1) + if (PyObject_SetAttrString(kw, "end_comma", end_comma) == -1) goto fail; } switch (jsnode->pn_arity) { case PN_FUNC: kids = PyTuple_New(1); - if (traverse_node(context, jsnode->pn_body, kids, 0) == -1) + if (traverse_node(context, jsnode->pn_body, kw, kids, 0) == -1) return -1; break; @@ -272,7 +277,7 @@ int i; kids = PyTuple_New(jsnode->pn_count); for (i = 0, p = jsnode->pn_head; p; p = p->pn_next, i++) { - if (traverse_node(context, p, kids, i) == -1) + if (traverse_node(context, p, kw, kids, i) == -1) return -1; } } @@ -280,31 +285,31 @@ case PN_TERNARY: kids = PyTuple_New(3); - if (traverse_node(context, jsnode->pn_kid1, kids, 0) == -1) + if (traverse_node(context, jsnode->pn_kid1, kw, kids, 0) == -1) return -1; - if (traverse_node(context, jsnode->pn_kid2, kids, 1) == -1) + if (traverse_node(context, jsnode->pn_kid2, kw, kids, 1) == -1) return -1; - if (traverse_node(context, jsnode->pn_kid3, kids, 2) == -1) + if (traverse_node(context, jsnode->pn_kid3, kw, kids, 2) == -1) return -1; break; case PN_BINARY: kids = PyTuple_New(2); - if (traverse_node(context, jsnode->pn_left, kids, 0) == -1) + if (traverse_node(context, jsnode->pn_left, kw, kids, 0) == -1) return -1; - if (traverse_node(context, jsnode->pn_right, kids, 1) == -1) + if (traverse_node(context, jsnode->pn_right, kw, kids, 1) == -1) return -1; break; case PN_UNARY: kids = PyTuple_New(1); - if (traverse_node(context, jsnode->pn_kid, kids, 0) == -1) + if (traverse_node(context, jsnode->pn_kid, kw, kids, 0) == -1) return -1; break; case PN_NAME: kids = PyTuple_New(1); - if (traverse_node(context, jsnode->pn_expr, kids, 0) == -1) + if (traverse_node(context, jsnode->pn_expr, kw, kids, 0) == -1) return -1; break; @@ -313,7 +318,7 @@ break; } - if (PyDict_SetItemString(kw, "kids", kids) == -1) + if (PyObject_SetAttrString(kw, "kids", kids) == -1) goto fail; return 0; @@ -346,9 +351,14 @@ error = "encountered an unknown error"; /* validate arguments */ - if (!PyArg_ParseTuple(args, "sO", &m.script, &m.ctx_data.error_callback)) + if (!PyArg_ParseTuple(args, "sOO", &m.script, &m.ctx_data.node_class, &m.ctx_data.error_callback)) return NULL; + if (!PyCallable_Check(m.ctx_data.node_class)) { + PyErr_SetString(PyExc_ValueError, "\"node_class\" must be callable"); + return NULL; + } + if (!PyCallable_Check(m.ctx_data.error_callback)) { PyErr_SetString(PyExc_ValueError, "\"error\" must be callable"); return NULL; @@ -399,7 +409,7 @@ } m.kids = PyTuple_New(1); - if (traverse_node(m.context, m.jsnode, m.kids, 0) == -1) { + if (traverse_node(m.context, m.jsnode, Py_None, m.kids, 0) == -1) { error = ""; goto cleanup; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2008-03-03 18:41:16
|
Revision: 156 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=156&view=rev Author: matthiasmiller Date: 2008-03-03 10:41:11 -0800 (Mon, 03 Mar 2008) Log Message: ----------- use __str__ instead of __repr__ Modified Paths: -------------- trunk/pyjsl/parse.py Modified: trunk/pyjsl/parse.py =================================================================== --- trunk/pyjsl/parse.py 2008-03-03 18:31:17 UTC (rev 155) +++ trunk/pyjsl/parse.py 2008-03-03 18:41:11 UTC (rev 156) @@ -26,7 +26,7 @@ if self.col > other.col: return 1 return 0 - def __repr__(self): + def __str__(self): return '(line %i, col %i)' % (self.line+1, self.col+1) class NodePositions(): @@ -102,7 +102,7 @@ self._end_pos = NodePos(self._end_line, self._end_col) return self._end_pos - def __repr__(self): + def __str__(self): kind = self.kind if not kind: kind = '(none)' This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2008-03-03 18:31:32
|
Revision: 155 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=155&view=rev Author: matthiasmiller Date: 2008-03-03 10:31:17 -0800 (Mon, 03 Mar 2008) Log Message: ----------- set eol styles Property Changed: ---------------- trunk/COPYING trunk/DEVELOPMENT trunk/INSTALL trunk/jsl.py trunk/pyjsl/__init__.py trunk/pyjsl/conf.py trunk/pyjsl/lint.py trunk/pyjsl/parse.py trunk/pyjsl/util.py trunk/pyjsl/visitation.py trunk/pyjsl/warnings.py trunk/pyspidermonkey/pyspidermonkey.c trunk/pyspidermonkey/tokens.tbl trunk/setup.py trunk/test.py Property changes on: trunk/COPYING ___________________________________________________________________ Name: eol-style + native Property changes on: trunk/DEVELOPMENT ___________________________________________________________________ Name: eol-style + native Property changes on: trunk/INSTALL ___________________________________________________________________ Name: eol-style + native Property changes on: trunk/jsl.py ___________________________________________________________________ Name: eol-style + native Property changes on: trunk/pyjsl/__init__.py ___________________________________________________________________ Name: eol-style + native Property changes on: trunk/pyjsl/conf.py ___________________________________________________________________ Name: eol-style + native Property changes on: trunk/pyjsl/lint.py ___________________________________________________________________ Name: eol-style + native Property changes on: trunk/pyjsl/parse.py ___________________________________________________________________ Name: eol-style + native Property changes on: trunk/pyjsl/util.py ___________________________________________________________________ Name: eol-style + native Property changes on: trunk/pyjsl/visitation.py ___________________________________________________________________ Name: eol-style + native Property changes on: trunk/pyjsl/warnings.py ___________________________________________________________________ Name: eol-style + native Property changes on: trunk/pyspidermonkey/pyspidermonkey.c ___________________________________________________________________ Name: eol-style + native Property changes on: trunk/pyspidermonkey/tokens.tbl ___________________________________________________________________ Name: eol-style + native Property changes on: trunk/setup.py ___________________________________________________________________ Name: eol-style + native Property changes on: trunk/test.py ___________________________________________________________________ Name: eol-style + native This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2008-03-03 18:26:20
|
Revision: 154 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=154&view=rev Author: matthiasmiller Date: 2008-03-03 10:26:12 -0800 (Mon, 03 Mar 2008) Log Message: ----------- optimize _Node construction Modified Paths: -------------- trunk/pyjsl/parse.py trunk/pyspidermonkey/pyspidermonkey.c Modified: trunk/pyjsl/parse.py =================================================================== --- trunk/pyjsl/parse.py 2008-03-02 03:30:04 UTC (rev 153) +++ trunk/pyjsl/parse.py 2008-03-03 18:26:12 UTC (rev 154) @@ -1,341 +1,325 @@ -#!/usr/bin/python -""" Parses a script into nodes. """ -import bisect -import re -import unittest - -import pyspidermonkey -from pyspidermonkey import tok, op - -_tok_names = dict(zip( - [getattr(tok, prop) for prop in dir(tok)], - ['tok.%s' % prop for prop in dir(tok)] -)) - -class NodePos(): - def __init__(self, line, col): - self.line = line - self.col = col - def __cmp__(self, other): - if self.line < other.line: - return -1 - if self.line > other.line: - return 1 - if self.col < other.col: - return -1 - if self.col > other.col: - return 1 - return 0 - def __repr__(self): - return '(line %i, col %i)' % (self.line+1, self.col+1) - -class NodePositions(): - " Given a string, allows [x] lookups for NodePos line and column numbers." - def __init__(self, text): - # Find the length of each line and incrementally sum all of the lengths - # to determine the ending position of each line. - self._lines = text.splitlines(True) - lines = [0] + [len(x) for x in self._lines] - for x in range(1, len(lines)): - lines[x] += lines[x-1] - self._line_offsets = lines - def from_offset(self, offset): - line = bisect.bisect(self._line_offsets, offset)-1 - col = offset - self._line_offsets[line] - return NodePos(line, col) - def to_offset(self, pos): - offset = self._line_offsets[pos.line] + pos.col - assert offset <= self._line_offsets[pos.line+1] # out-of-bounds col num - return offset - def text(self, start, end): - assert start <= end - # Trim the ending first in case it's a single line. - lines = self._lines[start.line:end.line+1] - lines[-1] = lines[-1][:end.col+1] - lines[0] = lines[0][start.col:] - return ''.join(lines) - -class NodeRanges(): - def __init__(self): - self._offsets = [] - def add(self, start, end): - i = bisect.bisect_left(self._offsets, start) - if i % 2 == 1: - i -= 1 - start = self._offsets[i] - - end = end + 1 - j = bisect.bisect_left(self._offsets, end) - if j % 2 == 1: - end = self._offsets[j] - j += 1 - - self._offsets[i:j] = [start,end] - def has(self, pos): - return bisect.bisect_right(self._offsets, pos) % 2 == 1 - -class _Node(): - def __init__(self, kwargs): - def _to_node(kid): - if kid: - return _Node(kid) - self.kind = kwargs['type'] - kwargs['opcode'] = kwargs['opcode'] - self.opcode = kwargs['opcode'] - self.kids = tuple([_to_node(kid) for kid in kwargs['kids']]) - for kid in self.kids: - if kid: - kid.parent = self - if 'atom' in kwargs: - self.atom = kwargs['atom'] - if 'dval' in kwargs: - self.dval = kwargs['dval'] - if 'fn_name' in kwargs: - self.fn_name = kwargs['fn_name'] - if 'fn_args' in kwargs: - self.fn_args = kwargs['fn_args'] - if 'end_comma' in kwargs: - self.end_comma = kwargs['end_comma'] - self.args = kwargs - self.node_index = kwargs['node_index'] - self.parent = None - self.start_line = kwargs['start_row'] - self._start_pos = None - self._end_pos = None - - def add_child(self, node): - if node: - node.node_index = len(self.kids) - node.parent = self - self.kids.append(node) - - def start_pos(self): - self._start_pos = self._start_pos or \ - NodePos(self.args['start_row'], self.args['start_col']) - return self._start_pos - - def end_pos(self): - self._end_pos = self._end_pos or \ - NodePos(self.args['end_row'], self.args['end_col']) - return self._end_pos - - def __repr__(self): - kind = self.kind - if not kind: - kind = '(none)' - return '%s>%s' % (_tok_names[kind], str(self.kids)) - - def is_equivalent(self, other, are_functions_equiv=False): - if not other: - return False - - # Bail out for functions - if not are_functions_equiv: - if self.kind == tok.FUNCTION: - return False - if self.kind == tok.LP and self.opcode == op.CALL: - return False - - if self.kind != other.kind: - return False - if self.opcode != other.opcode: - return False - - # Check atoms on names, properties, and string constants - if self.kind in (tok.NAME, tok.DOT, tok.STRING) and self.atom != other.atom: - return False - - # Check values on numbers - if self.kind == tok.NUMBER and self.dval != other.dval: - return False - - # Compare child nodes - if len(self.kids) != len(other.kids): - return False - for i in range(0, len(self.kids)): - # Watch for dead nodes - if not self.kids[i]: - if not other.kids[i]: return True - else: return False - if not self.kids[i].is_equivalent(other.kids[i]): - return False - - return True - -def _parse_comments(script, root, node_positions, ignore_ranges): - pos = 0 - single_line_re = r"//[^\r\n]*" - multi_line_re = r"/\*(.*?)\*/" - full_re = "(%s)|(%s)" % (single_line_re, multi_line_re) - comment_re = re.compile(full_re, re.DOTALL) - - comments = [] - while True: - match = comment_re.search(script, pos) - if not match: - return comments - - # Get the comment text - comment_text = script[match.start():match.end()] - if comment_text.startswith('/*'): - comment_text = comment_text[2:-2] - opcode = 'JSOP_C_COMMENT' - else: - comment_text = comment_text[2:] - opcode = 'JSOP_CPP_COMMENT' - opcode = opcode[5:].lower() - - start_offset = match.start()+1 - end_offset = match.end() - - # Make sure it doesn't start in a string or regexp - if not ignore_ranges.has(start_offset): - start_pos = node_positions.from_offset(start_offset) - end_pos = node_positions.from_offset(end_offset) - kwargs = { - 'type': 'COMMENT', - 'atom': comment_text, - 'opcode': opcode, - 'start_row': start_pos.line, - 'start_col': start_pos.col, - 'end_row': end_pos.line, - 'end_col': end_pos.col, - 'kids': [], - 'node_index': None - } - comment_node = _Node(kwargs) - comments.append(comment_node) - pos = match.end() - else: - pos = match.start()+1 - -def parse(script, error_callback): - def _wrapped_callback(line, col, msg): - assert msg.startswith('JSMSG_') - msg = msg[6:].lower() - error_callback(line, col, msg) - - positions = NodePositions(script) - - roots = [] - nodes = [] - comment_ignore_ranges = NodeRanges() - def process(node): - if node.kind == tok.NUMBER: - node.atom = positions.text(node.start_pos(), node.end_pos()) - elif node.kind == tok.STRING or \ - (node.kind == tok.OBJECT and node.opcode == op.REGEXP): - start_offset = positions.to_offset(node.start_pos()) - end_offset = positions.to_offset(node.end_pos()) - comment_ignore_ranges.add(start_offset, end_offset) - for kid in node.kids: - if kid: - process(kid) - def pop(): - nodes.pop() - - roots = pyspidermonkey.traverse(script, _wrapped_callback) - assert len(roots) == 1 - root_node = _Node(roots[0]) - process(root_node) - - comments = _parse_comments(script, root_node, positions, comment_ignore_ranges) - return root_node, comments - -def _dump_node(node, depth=0): - print '. '*depth, - if node is None: - print '(none)' - else: - print _tok_names[node.kind], '\t', node.args - for node in node.kids: - _dump_node(node, depth+1) - -def dump_tree(script): - def error_callback(line, col, msg): - print '(%i, %i): %s', (line, col, msg) - node, comments = parse(script, error_callback) - _dump_node(node) - -class TestComments(unittest.TestCase): - def _test(self, script, expected_comments): - root, comments = parse(script, lambda line, col, msg: None) - encountered_comments = [node.atom for node in comments] - self.assertEquals(encountered_comments, list(expected_comments)) - def testSimpleComments(self): - self._test('re = /\//g', ()) - self._test('re = /\///g', ()) - self._test('re = /\////g', ('g',)) - def testCComments(self): - self._test('/*a*//*b*/', ('a', 'b')) - self._test('/*a\r\na*//*b\r\nb*/', ('a\r\na', 'b\r\nb')) - self._test('a//*b*/c', ('*b*/c',)) - self._test('a///*b*/c', ('/*b*/c',)) - self._test('a/*//*/;', ('//',)) - self._test('a/*b*/+/*c*/d', ('b', 'c')) - -class TestNodePositions(unittest.TestCase): - def _test(self, text, expected_lines, expected_cols): - # Get a NodePos list - positions = NodePositions(text) - positions = [positions.from_offset(i) for i in range(0, len(text))] - encountered_lines = ''.join([str(x.line) for x in positions]) - encountered_cols = ''.join([str(x.col) for x in positions]) - self.assertEquals(encountered_lines, expected_lines.replace(' ', '')) - self.assertEquals(encountered_cols, expected_cols.replace(' ', '')) - def testSimple(self): - self._test( - 'abc\r\ndef\nghi\n\nj', - '0000 0 1111 2222 3 4', - '0123 4 0123 0123 0 0' - ) - self._test( - '\rabc', - '0 111', - '0 012' - ) - def testText(self): - pos = NodePositions('abc\r\ndef\n\nghi') - self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 0)), 'a') - self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 2)), 'abc') - self.assertEquals(pos.text(NodePos(0, 2), NodePos(1, 2)), 'c\r\ndef') - def testOffset(self): - pos = NodePositions('abc\r\ndef\n\nghi') - self.assertEquals(pos.to_offset(NodePos(0, 2)), 2) - self.assertEquals(pos.to_offset(NodePos(1, 0)), 5) - self.assertEquals(pos.to_offset(NodePos(3, 1)), 11) - -class TestNodeRanges(unittest.TestCase): - def testAdd(self): - r = NodeRanges() - r.add(5, 10) - self.assertEquals(r._offsets, [5,11]) - r.add(15, 20) - self.assertEquals(r._offsets, [5,11,15,21]) - r.add(21,22) - self.assertEquals(r._offsets, [5,11,15,23]) - r.add(4,5) - self.assertEquals(r._offsets, [4,11,15,23]) - r.add(9,11) - self.assertEquals(r._offsets, [4,12,15,23]) - r.add(10,20) - self.assertEquals(r._offsets, [4,23]) - r.add(4,22) - self.assertEquals(r._offsets, [4,23]) - r.add(30,30) - self.assertEquals(r._offsets, [4,23,30,31]) - def testHas(self): - r = NodeRanges() - r.add(5, 10) - r.add(15, 15) - assert not r.has(4) - assert r.has(5) - assert r.has(6) - assert r.has(9) - assert r.has(10) - assert not r.has(14) - assert r.has(15) - assert not r.has(16) -if __name__ == '__main__': - unittest.main() - +#!/usr/bin/python +""" Parses a script into nodes. """ +import bisect +import re +import unittest + +import pyspidermonkey +from pyspidermonkey import tok, op + +_tok_names = dict(zip( + [getattr(tok, prop) for prop in dir(tok)], + ['tok.%s' % prop for prop in dir(tok)] +)) + +class NodePos(): + def __init__(self, line, col): + self.line = line + self.col = col + def __cmp__(self, other): + if self.line < other.line: + return -1 + if self.line > other.line: + return 1 + if self.col < other.col: + return -1 + if self.col > other.col: + return 1 + return 0 + def __repr__(self): + return '(line %i, col %i)' % (self.line+1, self.col+1) + +class NodePositions(): + " Given a string, allows [x] lookups for NodePos line and column numbers." + def __init__(self, text): + # Find the length of each line and incrementally sum all of the lengths + # to determine the ending position of each line. + self._lines = text.splitlines(True) + lines = [0] + [len(x) for x in self._lines] + for x in range(1, len(lines)): + lines[x] += lines[x-1] + self._line_offsets = lines + def from_offset(self, offset): + line = bisect.bisect(self._line_offsets, offset)-1 + col = offset - self._line_offsets[line] + return NodePos(line, col) + def to_offset(self, pos): + offset = self._line_offsets[pos.line] + pos.col + assert offset <= self._line_offsets[pos.line+1] # out-of-bounds col num + return offset + def text(self, start, end): + assert start <= end + # Trim the ending first in case it's a single line. + lines = self._lines[start.line:end.line+1] + lines[-1] = lines[-1][:end.col+1] + lines[0] = lines[0][start.col:] + return ''.join(lines) + +class NodeRanges(): + def __init__(self): + self._offsets = [] + def add(self, start, end): + i = bisect.bisect_left(self._offsets, start) + if i % 2 == 1: + i -= 1 + start = self._offsets[i] + + end = end + 1 + j = bisect.bisect_left(self._offsets, end) + if j % 2 == 1: + end = self._offsets[j] + j += 1 + + self._offsets[i:j] = [start,end] + def has(self, pos): + return bisect.bisect_right(self._offsets, pos) % 2 == 1 + +class _Node(): + def __init__(self, kids, parent=None, **kwargs): + _to_node = lambda kid: kid and _Node(parent=self, **kid) + self.__dict__.update(kwargs) + self.args = kwargs + self.parent = parent + self.kids = map(_to_node, kids) + + def add_child(self, node): + if node: + node.node_index = len(self.kids) + node.parent = self + self.kids.append(node) + + def start_pos(self): + try: + return self._start_pos + except AttributeError: + self._start_pos = NodePos(self._start_line, self._start_col) + return self._start_pos + + def end_pos(self): + try: + return self._end_pos + except AttributeError: + self._end_pos = NodePos(self._end_line, self._end_col) + return self._end_pos + + def __repr__(self): + kind = self.kind + if not kind: + kind = '(none)' + return '%s>%s' % (_tok_names[kind], str(self.kids)) + + def is_equivalent(self, other, are_functions_equiv=False): + if not other: + return False + + # Bail out for functions + if not are_functions_equiv: + if self.kind == tok.FUNCTION: + return False + if self.kind == tok.LP and self.opcode == op.CALL: + return False + + if self.kind != other.kind: + return False + if self.opcode != other.opcode: + return False + + # Check atoms on names, properties, and string constants + if self.kind in (tok.NAME, tok.DOT, tok.STRING) and self.atom != other.atom: + return False + + # Check values on numbers + if self.kind == tok.NUMBER and self.dval != other.dval: + return False + + # Compare child nodes + if len(self.kids) != len(other.kids): + return False + for i in range(0, len(self.kids)): + # Watch for dead nodes + if not self.kids[i]: + if not other.kids[i]: return True + else: return False + if not self.kids[i].is_equivalent(other.kids[i]): + return False + + return True + +def _parse_comments(script, root, node_positions, ignore_ranges): + pos = 0 + single_line_re = r"//[^\r\n]*" + multi_line_re = r"/\*(.*?)\*/" + full_re = "(%s)|(%s)" % (single_line_re, multi_line_re) + comment_re = re.compile(full_re, re.DOTALL) + + comments = [] + while True: + match = comment_re.search(script, pos) + if not match: + return comments + + # Get the comment text + comment_text = script[match.start():match.end()] + if comment_text.startswith('/*'): + comment_text = comment_text[2:-2] + opcode = 'JSOP_C_COMMENT' + else: + comment_text = comment_text[2:] + opcode = 'JSOP_CPP_COMMENT' + opcode = opcode[5:].lower() + + start_offset = match.start()+1 + end_offset = match.end() + + # Make sure it doesn't start in a string or regexp + if not ignore_ranges.has(start_offset): + start_pos = node_positions.from_offset(start_offset) + end_pos = node_positions.from_offset(end_offset) + kwargs = { + 'type': 'COMMENT', + 'atom': comment_text, + 'opcode': opcode, + '_start_line': start_pos.line, + '_start_col': start_pos.col, + '_end_line': end_pos.line, + '_end_col': end_pos.col, + 'parent': None, + 'kids': [], + 'node_index': None + } + comment_node = _Node(**kwargs) + comments.append(comment_node) + pos = match.end() + else: + pos = match.start()+1 + +def parse(script, error_callback): + def _wrapped_callback(line, col, msg): + assert msg.startswith('JSMSG_') + msg = msg[6:].lower() + error_callback(line, col, msg) + + positions = NodePositions(script) + + roots = [] + nodes = [] + comment_ignore_ranges = NodeRanges() + def process(node): + if node.kind == tok.NUMBER: + node.atom = positions.text(node.start_pos(), node.end_pos()) + elif node.kind == tok.STRING or \ + (node.kind == tok.OBJECT and node.opcode == op.REGEXP): + start_offset = positions.to_offset(node.start_pos()) + end_offset = positions.to_offset(node.end_pos()) + comment_ignore_ranges.add(start_offset, end_offset) + for kid in node.kids: + if kid: + process(kid) + def pop(): + nodes.pop() + + roots = pyspidermonkey.traverse(script, _wrapped_callback) + assert len(roots) == 1 + root_node = _Node(**roots[0]) + process(root_node) + + comments = _parse_comments(script, root_node, positions, comment_ignore_ranges) + return root_node, comments + +def _dump_node(node, depth=0): + print '. '*depth, + if node is None: + print '(none)' + else: + print _tok_names[node.kind], '\t', node.args + for node in node.kids: + _dump_node(node, depth+1) + +def dump_tree(script): + def error_callback(line, col, msg): + print '(%i, %i): %s', (line, col, msg) + node, comments = parse(script, error_callback) + _dump_node(node) + +class TestComments(unittest.TestCase): + def _test(self, script, expected_comments): + root, comments = parse(script, lambda line, col, msg: None) + encountered_comments = [node.atom for node in comments] + self.assertEquals(encountered_comments, list(expected_comments)) + def testSimpleComments(self): + self._test('re = /\//g', ()) + self._test('re = /\///g', ()) + self._test('re = /\////g', ('g',)) + def testCComments(self): + self._test('/*a*//*b*/', ('a', 'b')) + self._test('/*a\r\na*//*b\r\nb*/', ('a\r\na', 'b\r\nb')) + self._test('a//*b*/c', ('*b*/c',)) + self._test('a///*b*/c', ('/*b*/c',)) + self._test('a/*//*/;', ('//',)) + self._test('a/*b*/+/*c*/d', ('b', 'c')) + +class TestNodePositions(unittest.TestCase): + def _test(self, text, expected_lines, expected_cols): + # Get a NodePos list + positions = NodePositions(text) + positions = [positions.from_offset(i) for i in range(0, len(text))] + encountered_lines = ''.join([str(x.line) for x in positions]) + encountered_cols = ''.join([str(x.col) for x in positions]) + self.assertEquals(encountered_lines, expected_lines.replace(' ', '')) + self.assertEquals(encountered_cols, expected_cols.replace(' ', '')) + def testSimple(self): + self._test( + 'abc\r\ndef\nghi\n\nj', + '0000 0 1111 2222 3 4', + '0123 4 0123 0123 0 0' + ) + self._test( + '\rabc', + '0 111', + '0 012' + ) + def testText(self): + pos = NodePositions('abc\r\ndef\n\nghi') + self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 0)), 'a') + self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 2)), 'abc') + self.assertEquals(pos.text(NodePos(0, 2), NodePos(1, 2)), 'c\r\ndef') + def testOffset(self): + pos = NodePositions('abc\r\ndef\n\nghi') + self.assertEquals(pos.to_offset(NodePos(0, 2)), 2) + self.assertEquals(pos.to_offset(NodePos(1, 0)), 5) + self.assertEquals(pos.to_offset(NodePos(3, 1)), 11) + +class TestNodeRanges(unittest.TestCase): + def testAdd(self): + r = NodeRanges() + r.add(5, 10) + self.assertEquals(r._offsets, [5,11]) + r.add(15, 20) + self.assertEquals(r._offsets, [5,11,15,21]) + r.add(21,22) + self.assertEquals(r._offsets, [5,11,15,23]) + r.add(4,5) + self.assertEquals(r._offsets, [4,11,15,23]) + r.add(9,11) + self.assertEquals(r._offsets, [4,12,15,23]) + r.add(10,20) + self.assertEquals(r._offsets, [4,23]) + r.add(4,22) + self.assertEquals(r._offsets, [4,23]) + r.add(30,30) + self.assertEquals(r._offsets, [4,23,30,31]) + def testHas(self): + r = NodeRanges() + r.add(5, 10) + r.add(15, 15) + assert not r.has(4) + assert r.has(5) + assert r.has(6) + assert r.has(9) + assert r.has(10) + assert not r.has(14) + assert r.has(15) + assert not r.has(16) +if __name__ == '__main__': + unittest.main() + Modified: trunk/pyspidermonkey/pyspidermonkey.c =================================================================== --- trunk/pyspidermonkey/pyspidermonkey.c 2008-03-02 03:30:04 UTC (rev 153) +++ trunk/pyspidermonkey/pyspidermonkey.c 2008-03-03 18:26:12 UTC (rev 154) @@ -173,19 +173,19 @@ PyTuple_SET_ITEM(tuple, node_offset, kw); - if (PyDict_SetItemString(kw, "type", Py_BuildValue("i", TOK_TO_NUM(jsnode->pn_type))) == -1) + if (PyDict_SetItemString(kw, "kind", Py_BuildValue("i", TOK_TO_NUM(jsnode->pn_type))) == -1) goto fail; if (PyDict_SetItemString(kw, "node_index", Py_BuildValue("i", node_offset)) == -1) goto fail; /* pass the position */ - if (PyDict_SetItemString(kw, "start_row", Py_BuildValue("i", jsnode->pn_pos.begin.lineno-1)) == -1) + if (PyDict_SetItemString(kw, "_start_line", Py_BuildValue("i", jsnode->pn_pos.begin.lineno-1)) == -1) goto fail; - if (PyDict_SetItemString(kw, "start_col", Py_BuildValue("i", jsnode->pn_pos.begin.index)) == -1) + if (PyDict_SetItemString(kw, "_start_col", Py_BuildValue("i", jsnode->pn_pos.begin.index)) == -1) goto fail; - if (PyDict_SetItemString(kw, "end_row", Py_BuildValue("i", jsnode->pn_pos.end.lineno-1)) == -1) + if (PyDict_SetItemString(kw, "_end_line", Py_BuildValue("i", jsnode->pn_pos.end.lineno-1)) == -1) goto fail; - if (PyDict_SetItemString(kw, "end_col", Py_BuildValue("i", jsnode->pn_pos.end.index)) == -1) + if (PyDict_SetItemString(kw, "_end_col", Py_BuildValue("i", jsnode->pn_pos.end.index)) == -1) goto fail; if ((jsnode->pn_type == TOK_NAME || jsnode->pn_type == TOK_DOT || This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2008-03-02 03:30:08
|
Revision: 153 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=153&view=rev Author: matthiasmiller Date: 2008-03-01 19:30:04 -0800 (Sat, 01 Mar 2008) Log Message: ----------- show tok.TOKENNAME when printing tokens Modified Paths: -------------- trunk/pyjsl/parse.py Modified: trunk/pyjsl/parse.py =================================================================== --- trunk/pyjsl/parse.py 2008-03-01 19:05:07 UTC (rev 152) +++ trunk/pyjsl/parse.py 2008-03-02 03:30:04 UTC (rev 153) @@ -7,6 +7,11 @@ import pyspidermonkey from pyspidermonkey import tok, op +_tok_names = dict(zip( + [getattr(tok, prop) for prop in dir(tok)], + ['tok.%s' % prop for prop in dir(tok)] +)) + class NodePos(): def __init__(self, line, col): self.line = line @@ -118,7 +123,7 @@ kind = self.kind if not kind: kind = '(none)' - return '%s>%s' % (kind, str(self.kids)) + return '%s>%s' % (_tok_names[kind], str(self.kids)) def is_equivalent(self, other, are_functions_equiv=False): if not other: @@ -242,7 +247,7 @@ if node is None: print '(none)' else: - print node.kind, '\t', node.args + print _tok_names[node.kind], '\t', node.args for node in node.kids: _dump_node(node, depth+1) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2008-03-01 19:05:09
|
Revision: 152 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=152&view=rev Author: matthiasmiller Date: 2008-03-01 11:05:07 -0800 (Sat, 01 Mar 2008) Log Message: ----------- fix crash when functions have too many parameters Modified Paths: -------------- trunk/spidermonkey/src/jsdbgapi.c Modified: trunk/spidermonkey/src/jsdbgapi.c =================================================================== --- trunk/spidermonkey/src/jsdbgapi.c 2008-03-01 19:04:30 UTC (rev 151) +++ trunk/spidermonkey/src/jsdbgapi.c 2008-03-01 19:05:07 UTC (rev 152) @@ -1187,8 +1187,10 @@ } n = scope->entryCount; +#if 0 if (n > scope->map.nslots) n = scope->map.nslots; +#endif pd = (JSPropertyDesc *) JS_malloc(cx, (size_t)n * sizeof(JSPropertyDesc)); if (!pd) return JS_FALSE; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2008-03-01 19:04:32
|
Revision: 151 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=151&view=rev Author: matthiasmiller Date: 2008-03-01 11:04:30 -0800 (Sat, 01 Mar 2008) Log Message: ----------- fix Makefile for Windows Modified Paths: -------------- trunk/Makefile.SpiderMonkey Modified: trunk/Makefile.SpiderMonkey =================================================================== --- trunk/Makefile.SpiderMonkey 2008-03-01 18:45:07 UTC (rev 150) +++ trunk/Makefile.SpiderMonkey 2008-03-01 19:04:30 UTC (rev 151) @@ -53,7 +53,10 @@ $(COPY_LIB): $(BUILD_DIR) $(ORIG_LIB) cp $(ORIG_LIB) $(COPY_LIB) -$(COPY_DLL): $(BUILD_DIR) $(ORIG_LIB) +$(DISTUTILS_DIR): $(DISTUTILS_DIR_MAKEFILE) + mkdir -p $(DISTUTILS_DIR) + +$(COPY_DLL): $(DISTUTILS_DIR) $(ORIG_LIB) cp $(ORIG_DLL) $(COPY_DLL) $(OS_HEADER): $(BUILD_DIR) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2008-03-01 18:45:09
|
Revision: 150 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=150&view=rev Author: matthiasmiller Date: 2008-03-01 10:45:07 -0800 (Sat, 01 Mar 2008) Log Message: ----------- add svn_load_dirs conf file Added Paths: ----------- trunk/svn_load_dirs.conf Added: trunk/svn_load_dirs.conf =================================================================== --- trunk/svn_load_dirs.conf (rev 0) +++ trunk/svn_load_dirs.conf 2008-03-01 18:45:07 UTC (rev 150) @@ -0,0 +1 @@ +.* cont svn:eol-style native This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2008-03-01 18:44:55
|
Revision: 149 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=149&view=rev Author: matthiasmiller Date: 2008-03-01 10:44:53 -0800 (Sat, 01 Mar 2008) Log Message: ----------- use numbers instead of strings for Node.kind and Node.opcode Modified Paths: -------------- trunk/pyjsl/lint.py trunk/pyjsl/parse.py trunk/pyjsl/warnings.py trunk/pyspidermonkey/pyspidermonkey.c Modified: trunk/pyjsl/lint.py =================================================================== --- trunk/pyjsl/lint.py 2008-03-01 17:32:55 UTC (rev 148) +++ trunk/pyjsl/lint.py 2008-03-01 18:44:53 UTC (rev 149) @@ -8,6 +8,8 @@ import warnings import util +from pyspidermonkey import tok, op + _newline_kinds = ( 'eof', 'comma', 'dot', 'semi', 'colon', 'lc', 'rc', 'lp', 'rb', 'assign', 'relop', 'hook', 'plus', 'minus', 'star', 'divop', 'eqop', 'shop', 'or', @@ -29,14 +31,14 @@ _identifier = re.compile('^[A-Za-z_$][A-Za-z0-9_$]*$') def _find_function(node): - while node and node.kind != 'function': + while node and node.kind != tok.FUNCTION: node = node.parent return node def _find_functions(node): functions = [] while node: - if node.kind == 'function': + if node.kind == tok.FUNCTION: functions.append(node) node = node.parent return functions @@ -70,7 +72,7 @@ class Scope(): def __init__(self, node): - self._is_with_scope = node.kind == 'with' + self._is_with_scope = node.kind == tok.WITH self._parent = None self._kids = [] self._identifiers = {} @@ -244,7 +246,7 @@ def warn_or_declare(name, node): other = scope.get_identifier(name) - if other and other.kind == 'function' and name in other.fn_args: + if other and other.kind == tok.FUNCTION and name in other.fn_args: report(node, 'var_hides_arg') elif other: report(node, 'redeclared_var') @@ -252,34 +254,34 @@ scope.add_declaration(name, node) # Let the visitors warn. - for kind in (node.kind, '%s:%s' % (node.kind, node.opcode)): + for kind in (node.kind, (node.kind, node.opcode)): if kind in visitors: for visitor in visitors[kind]: warning_node = visitor(node) if warning_node: report(warning_node, visitor.im_class.__name__) - if node.kind == 'name': - if node.node_index == 0 and node.parent.kind == 'colon' and node.parent.parent.kind == 'rc': + if node.kind == tok.NAME: + if node.node_index == 0 and node.parent.kind == tok.COLON and node.parent.parent.kind == tok.RC: pass # left side of object literal - elif node.parent.kind == 'catch': + elif node.parent.kind == tok.CATCH: scope.add_declaration(node.atom, node) else: scope.add_reference(node.atom, node) # Push function identifiers - if node.kind == 'function': + if node.kind == tok.FUNCTION: if node.fn_name: warn_or_declare(node.fn_name, node) scope = scope.add_scope(node) for var_name in node.fn_args: scope.add_declaration(var_name, node) - elif node.kind == 'lexicalscope': + elif node.kind == tok.LEXICALSCOPE: scope = scope.add_scope(node) - elif node.kind == 'with': + elif node.kind == tok.WITH: scope = scope.add_scope(node) - if node.parent and node.parent.kind == 'var': + if node.parent and node.parent.kind == tok.VAR: warn_or_declare(node.atom, node) for child in node.kids: Modified: trunk/pyjsl/parse.py =================================================================== --- trunk/pyjsl/parse.py 2008-03-01 17:32:55 UTC (rev 148) +++ trunk/pyjsl/parse.py 2008-03-01 18:44:53 UTC (rev 149) @@ -5,6 +5,7 @@ import unittest import pyspidermonkey +from pyspidermonkey import tok, op class NodePos(): def __init__(self, line, col): @@ -73,10 +74,8 @@ def _to_node(kid): if kid: return _Node(kid) - kwargs['type'] = kwargs['type'].lower() self.kind = kwargs['type'] - assert kwargs['opcode'].startswith('JSOP_') - kwargs['opcode'] = kwargs['opcode'][5:].lower() + kwargs['opcode'] = kwargs['opcode'] self.opcode = kwargs['opcode'] self.kids = tuple([_to_node(kid) for kid in kwargs['kids']]) for kid in self.kids: @@ -127,9 +126,9 @@ # Bail out for functions if not are_functions_equiv: - if self.kind == 'function': + if self.kind == tok.FUNCTION: return False - if self.kind == 'lp' and self.opcode == 'call': + if self.kind == tok.LP and self.opcode == op.CALL: return False if self.kind != other.kind: @@ -138,11 +137,11 @@ return False # Check atoms on names, properties, and string constants - if self.kind in ('name', 'dot', 'string') and self.atom != other.atom: + if self.kind in (tok.NAME, tok.DOT, tok.STRING) and self.atom != other.atom: return False # Check values on numbers - if self.kind == 'number' and self.dval != other.dval: + if self.kind == tok.NUMBER and self.dval != other.dval: return False # Compare child nodes @@ -179,6 +178,7 @@ else: comment_text = comment_text[2:] opcode = 'JSOP_CPP_COMMENT' + opcode = opcode[5:].lower() start_offset = match.start()+1 end_offset = match.end() @@ -216,10 +216,10 @@ nodes = [] comment_ignore_ranges = NodeRanges() def process(node): - if node.kind == 'number': + if node.kind == tok.NUMBER: node.atom = positions.text(node.start_pos(), node.end_pos()) - elif node.kind == 'string' or \ - (node.kind == 'object' and node.opcode == 'regexp'): + elif node.kind == tok.STRING or \ + (node.kind == tok.OBJECT and node.opcode == op.REGEXP): start_offset = positions.to_offset(node.start_pos()) end_offset = positions.to_offset(node.end_pos()) comment_ignore_ranges.add(start_offset, end_offset) Modified: trunk/pyjsl/warnings.py =================================================================== --- trunk/pyjsl/warnings.py 2008-03-01 17:32:55 UTC (rev 148) +++ trunk/pyjsl/warnings.py 2008-03-01 18:44:53 UTC (rev 149) @@ -4,7 +4,7 @@ The class can have one more more member functions to inspect nodes. The function should be decorated with a @lookat call specifying the nodes it -wants to examine. The node names may be in the 'kind' or 'kind:opcode' +wants to examine. The node names may be in the tok.KIND or (tok.KIND, op.OPCODE) format. To report a warning, the function should return the node causing the warning. @@ -12,7 +12,7 @@ class warning_name: 'questionable JavaScript coding style' - @lookat('nodekind', 'nodekind:opcode') + @lookat(tok.NODEKIND, (tok.NODEKIND, op.OPCODE)) def _lint(self, node): if questionable: return node @@ -22,17 +22,19 @@ import types from visitation import visit as lookat +from pyspidermonkey import tok, op + # TODO: document inspect, node:opcode, etc def _get_branch_in_for(node): " Returns None if this is not one of the branches in a 'for' " - if node.parent and node.parent.kind == 'reserved' and \ - node.parent.parent.kind == 'for': + if node.parent and node.parent.kind == tok.RESERVED and \ + node.parent.parent.kind == tok.FOR: return node.node_index return None def _get_exit_points(node): - if node.kind == 'lc': + if node.kind == tok.LC: # Only if the last child contains it exit_points = set([None]) for kid in node.kids: @@ -41,13 +43,13 @@ exit_points.remove(None) if kid: exit_points |= _get_exit_points(kid) - elif node.kind == 'if': + elif node.kind == tok.IF: # Only if both branches have an exit point cond_, if_, else_ = node.kids exit_points = _get_exit_points(if_) if else_: exit_points |= _get_exit_points(else_) - elif node.kind == 'switch': + elif node.kind == tok.SWITCH: exit_points = set([None]) switch_has_default = False @@ -57,7 +59,7 @@ for node in switch_stmts.kids: case_val, case_stmt = node.kids case_exit_points = _get_exit_points(case_stmt) - switch_has_default = switch_has_default or node.kind == 'default' + switch_has_default = switch_has_default or node.kind == tok.DEFAULT switch_has_final_fallthru = None in case_exit_points exit_points |= case_exit_points @@ -65,8 +67,8 @@ exit_points.remove(None) # Check if the switch contained any break - if 'break' in exit_points: - exit_points.remove('break') + if tok.BREAK in exit_points: + exit_points.remove(tok.BREAK) exit_points.add(None) # Check if the switch had a default case @@ -76,15 +78,15 @@ # Check if the final case statement had a fallthru if switch_has_final_fallthru: exit_points.add(None) - elif node.kind == 'break': - exit_points = set(['break']) - elif node.kind == 'with': + elif node.kind == tok.BREAK: + exit_points = set([tok.BREAK]) + elif node.kind == tok.WITH: exit_points = _get_exit_points(node.kids[-1]) - elif node.kind == 'return': - exit_points = set(['return']) - elif node.kind == 'throw': - exit_points = set(['throw']) - elif node.kind == 'try': + elif node.kind == tok.RETURN: + exit_points = set([tok.RETURN]) + elif node.kind == tok.THROW: + exit_points = set([tok.THROW]) + elif node.kind == tok.TRY: try_, catch_, finally_ = node.kids exit_points = _get_exit_points(try_) | _get_exit_points(catch_) @@ -100,24 +102,24 @@ class comparison_type_conv: 'comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==)' - @lookat('eqop:eq') + @lookat((tok.EQOP, op.EQ)) def _lint(self, node): lvalue, rvalue = node.kids if not self._allow_coercive_compare(lvalue) or \ not self._allow_coercive_compare(rvalue): return node def _allow_coercive_compare(self, node): - if node.kind == 'primary' and node.opcode in ('null', 'true', 'false'): + if node.kind == tok.PRIMARY and node.opcode in (op.NULL, op.TRUE, op.FALSE): return False - if node.kind == 'number' and not node.dval: + if node.kind == tok.NUMBER and not node.dval: return False - if node.kind == 'string' and not node.atom: + if node.kind == tok.STRING and not node.atom: return False return True class default_not_at_end: 'the default case is not at the end of the switch statement' - @lookat('default') + @lookat(tok.DEFAULT) def _lint(self, node): siblings = node.parent.kids if node.node_index != len(siblings)-1: @@ -125,7 +127,7 @@ class duplicate_case_in_switch: 'duplicate case in switch statement' - @lookat('case') + @lookat(tok.CASE) def _lint(self, node): # Only look at previous siblings siblings = node.parent.kids @@ -133,30 +135,30 @@ # Compare values (first kid) node_value = node.kids[0] for sibling in siblings: - if sibling.kind == 'case': + if sibling.kind == tok.CASE: sibling_value = sibling.kids[0] if node_value.is_equivalent(sibling_value, True): return node class missing_default_case: 'missing default case in switch statement' - @lookat('switch') + @lookat(tok.SWITCH) def _lint(self, node): value, cases = node.kids for case in cases.kids: - if case.kind == 'default': + if case.kind == tok.DEFAULT: return return node class with_statement: 'with statement hides undeclared variables; use temporary variable instead' - @lookat('with') + @lookat(tok.WITH) def _lint(self, node): return node class useless_comparison: 'useless comparison; comparing identical expressions' - @lookat('eqop','relop') + @lookat(tok.EQOP,tok.RELOP) def _lint(self, node): lvalue, rvalue = node.kids if lvalue.is_equivalent(rvalue): @@ -164,45 +166,45 @@ class use_of_label: 'use of label' - @lookat('colon:name') + @lookat((tok.COLON, op.NAME)) def _lint(self, node): return node class meaningless_block: 'meaningless block; curly braces have no impact' - @lookat('lc') + @lookat(tok.LC) def _lint(self, node): - if node.parent and node.parent.kind == 'lc': + if node.parent and node.parent.kind == tok.LC: return node class misplaced_regex: 'regular expressions should be preceded by a left parenthesis, assignment, colon, or comma' - @lookat('object:regexp') + @lookat((tok.OBJECT, op.REGEXP)) def _lint(self, node): - if node.parent.kind == 'name' and node.parent.opcode == 'setname': + if node.parent.kind == tok.NAME and node.parent.opcode == op.SETNAME: return # Allow in var statements - if node.parent.kind == 'assign' and node.parent.opcode == 'nop': + if node.parent.kind == tok.ASSIGN and node.parent.opcode == op.NOP: return # Allow in assigns - if node.parent.kind == 'colon' and node.parent.parent.kind == 'rc': + if node.parent.kind == tok.COLON and node.parent.parent.kind == tok.RC: return # Allow in object literals - if node.parent.kind == 'lp' and node.parent.opcode == 'call': + if node.parent.kind == tok.LP and node.parent.opcode == op.CALL: return # Allow in parameters - if node.parent.kind == 'dot' and node.parent.opcode == 'getprop': + if node.parent.kind == tok.DOT and node.parent.opcode == op.GETPROP: return # Allow in /re/.property - if node.parent.kind == 'return': + if node.parent.kind == tok.RETURN: return # Allow for return values return node class assign_to_function_call: 'assignment to a function call' - @lookat('assign') + @lookat(tok.ASSIGN) def _lint(self, node): - if node.kids[0].kind == 'lp': + if node.kids[0].kind == tok.LP: return node class ambiguous_else_stmt: 'the else statement could be matched with one of multiple if statements (use curly braces to indicate intent' - @lookat('if') + @lookat(tok.IF) def _lint(self, node): # Only examine this node if it has an else statement. condition, if_, else_ = node.kids @@ -212,79 +214,79 @@ tmp = node while tmp: # Curly braces always clarify if statements. - if tmp.kind == 'lc': + if tmp.kind == tok.LC: return # Else is only ambiguous in the first branch of an if statement. - if tmp.parent.kind == 'if' and tmp.node_index == 1: + if tmp.parent.kind == tok.IF and tmp.node_index == 1: return else_ tmp = tmp.parent class block_without_braces: 'block statement without curly braces' - @lookat('if', 'while', 'do', 'for', 'with') + @lookat(tok.IF, tok.WHILE, tok.DO, tok.FOR, tok.WITH) def _lint(self, node): - if node.kids[1].kind != 'lc': + if node.kids[1].kind != tok.LC: return node.kids[1] class ambiguous_nested_stmt: 'block statements containing block statements should use curly braces to resolve ambiguity' - _block_nodes = ('if', 'while', 'do', 'for', 'with') + _block_nodes = (tok.IF, tok.WHILE, tok.DO, tok.FOR, tok.WITH) @lookat(*_block_nodes) def _lint(self, node): # Ignore "else if" - if node.kind == 'if' and node.node_index == 2 and node.parent.kind == 'if': + if node.kind == tok.IF and node.node_index == 2 and node.parent.kind == tok.IF: return # If the parent is a block, it means a block statement # was inside a block statement without clarifying curlies. - # (Otherwise, the node type would be 'lc'.) + # (Otherwise, the node type would be tok.LC.) if node.parent.kind in self._block_nodes: return node class inc_dec_within_stmt: 'increment (++) and decrement (--) operators used as part of greater statement' - @lookat('inc', 'dec') + @lookat(tok.INC, tok.DEC) def _lint(self, node): - if node.parent.kind == 'semi': + if node.parent.kind == tok.SEMI: return None # Allow within the third part of the "for" tmp = node - while tmp and tmp.parent and tmp.parent.kind == 'comma': + while tmp and tmp.parent and tmp.parent.kind == tok.COMMA: tmp = tmp.parent if tmp and tmp.node_index == 2 and \ - tmp.parent.kind == 'reserved' and \ - tmp.parent.parent.kind == 'for': + tmp.parent.kind == tok.RESERVED and \ + tmp.parent.parent.kind == tok.FOR: return None return node def _is_for_ternary_stmt(self, node, branch=None): - if node.parent and node.parent.kind == 'comma': + if node.parent and node.parent.kind == tok.COMMA: return _is_for_ternary_stmt(node.parent, branch) return node.node_index == branch and \ node.parent and \ - node.parent.kind == 'reserved' and \ - node.parent.parent.kind == 'for' + node.parent.kind == tok.RESERVED and \ + node.parent.parent.kind == tok.FOR class comma_separated_stmts: 'multiple statements separated by commas (use semicolons?)' - @lookat('comma') + @lookat(tok.COMMA) def _lint(self, node): # Allow within the first and third part of "for(;;)" if _get_branch_in_for(node) in (0, 2): return # This is an array - if node.parent.kind == 'rb': + if node.parent.kind == tok.RB: return return node class empty_statement: 'empty statement or extra semicolon' - @lookat('semi') + @lookat(tok.SEMI) def _semi(self, node): if not node.kids[0]: return node - @lookat('lc') + @lookat(tok.LC) def _lc(self, node): if node.kids: return @@ -292,19 +294,19 @@ if not node.parent: return # Some empty blocks are meaningful. - if node.parent.kind in ('catch', 'case', 'default', 'switch', 'function'): + if node.parent.kind in (tok.CATCH, tok.CASE, tok.DEFAULT, tok.SWITCH, tok.FUNCTION): return return node class missing_break: 'missing break statement' - @lookat('case', 'default') + @lookat(tok.CASE, tok.DEFAULT) def _lint(self, node): # The last item is handled separately if node.node_index == len(node.parent.kids)-1: return case_contents = node.kids[1] - assert case_contents.kind == 'lc' + assert case_contents.kind == tok.LC # Ignore empty case statements if not case_contents.kids: return @@ -313,88 +315,88 @@ class missing_break_for_last_case: 'missing break statement for last case in switch' - @lookat('case', 'default') + @lookat(tok.CASE, tok.DEFAULT) def _lint(self, node): if node.node_index < len(node.parent.kids)-1: return case_contents = node.kids[1] - assert case_contents.kind == 'lc' + assert case_contents.kind == tok.LC if None in _get_exit_points(case_contents): return node class multiple_plus_minus: 'unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs' - @lookat('inc') + @lookat(tok.INC) def _inc(self, node): - if node.node_index == 0 and node.parent.kind == 'plus': + if node.node_index == 0 and node.parent.kind == tok.PLUS: return node - @lookat('dec') + @lookat(tok.DEC) def _dec(self, node): - if node.node_index == 0 and node.parent.kind == 'minus': + if node.node_index == 0 and node.parent.kind == tok.MINUS: return node class useless_assign: 'useless assignment' - @lookat('name:setname') + @lookat((tok.NAME, op.SETNAME)) def _lint(self, node): - if node.parent.kind == 'assign': + if node.parent.kind == tok.ASSIGN: assert node.node_index == 0 value = node.parent.kids[1] - elif node.parent.kind == 'var': + elif node.parent.kind == tok.VAR: value = node.kids[0] - if value and value.kind == 'name' and node.atom == value.atom: + if value and value.kind == tok.NAME and node.atom == value.atom: return node class unreachable_code: 'unreachable code' - @lookat('break', 'continue', 'return', 'throw') + @lookat(tok.BREAK, tok.CONTINUE, tok.RETURN, tok.THROW) def _lint(self, node): - if node.parent.kind == 'lc' and \ + if node.parent.kind == tok.LC and \ node.node_index != len(node.parent.kids)-1: return node.parent.kids[node.node_index+1] class meaningless_block: 'meaningless block; curly braces have no impact' - #TODO: @lookat('if') + #TODO: @lookat(tok.IF) def _lint(self, node): condition, if_, else_ = node.kids - if condition.kind == 'primary' and condition.opcode in ('true', 'false', 'null'): + if condition.kind == tok.PRIMARY and condition.opcode in (op.TRUE, op.FALSE, op.NULL): return condition - #TODO: @lookat('while') + #TODO: @lookat(tok.WHILE) def _lint(self, node): condition = node.kids[0] - if condition.kind == 'primary' and condition.opcode in ('false', 'null'): + if condition.kind == tok.PRIMARY and condition.opcode in (op.FALSE, op.NULL): return condition - @lookat('lc') + @lookat(tok.LC) def _lint(self, node): - if node.parent and node.parent.kind == 'lc': + if node.parent and node.parent.kind == tok.LC: return node class useless_void: 'use of the void type may be unnecessary (void is always undefined)' - @lookat('unaryop:void') + @lookat((tok.UNARYOP, op.VOID)) def _lint(self, node): return node class parseint_missing_radix: 'parseInt missing radix parameter' - @lookat('lp:call') + @lookat((tok.LP, op.CALL)) def _lint(self, node): - if node.kids[0].kind == 'name' and node.kids[0].atom == 'parseInt' and len(node.kids) <= 2: + if node.kids[0].kind == tok.NAME and node.kids[0].atom == 'parseInt' and len(node.kids) <= 2: return node class leading_decimal_point: 'leading decimal point may indicate a number or an object member' - @lookat('number') + @lookat(tok.NUMBER) def _lint(self, node): if node.atom.startswith('.'): return node class trailing_decimal_point: 'trailing decimal point may indicate a number or an object member' - @lookat('number') + @lookat(tok.NUMBER) def _lint(self, node): - if node.parent.kind == 'dot': + if node.parent.kind == tok.DOT: return node if node.atom.endswith('.'): return node @@ -402,14 +404,14 @@ class octal_number: 'leading zeros make an octal number' _regexp = re.compile('^0[0-9]') - @lookat('number') + @lookat(tok.NUMBER) def _line(self, node): if self._regexp.match(node.atom): return node class trailing_comma_in_array: 'extra comma is not recommended in array initializers' - @lookat('rb') + @lookat(tok.RB) def _line(self, node): if node.end_comma: return node Modified: trunk/pyspidermonkey/pyspidermonkey.c =================================================================== --- trunk/pyspidermonkey/pyspidermonkey.c 2008-03-01 17:32:55 UTC (rev 148) +++ trunk/pyspidermonkey/pyspidermonkey.c 2008-03-01 18:44:53 UTC (rev 149) @@ -41,7 +41,11 @@ }; JS_STATIC_ASSERT(ARRAY_COUNT(error_names) == JSErr_Limit); +/* Use different numeric ranges to avoid accidental confusion. */ +#define TOK_TO_NUM(tok) (tok+1000) +#define OPCODE_TO_NUM(op) (op+2000) + /** MODULE INITIALIZATION */ @@ -57,7 +61,45 @@ PyMODINIT_FUNC initpyspidermonkey() { - (void)Py_InitModule("pyspidermonkey", module_methods); + PyObject* module; + PyObject* class; + PyObject* tok; + PyObject* op; + int i; + + module = Py_InitModule("pyspidermonkey", module_methods); + if (!module) + return; + + class = PyClass_New(NULL, PyDict_New(), PyString_FromString("spidermonkey_constants")); + if (!class) + return; + + /* set up tokens */ + tok = PyInstance_New(class, NULL, NULL); + if (!tok) + return; + if (PyObject_SetAttrString(module, "tok", tok) == -1) + return; + for (i = 0; i < ARRAY_COUNT(tokens); i++) { + if (PyObject_SetAttrString(tok, tokens[i], PyLong_FromLong(TOK_TO_NUM(i))) == -1) + return; + } + + /* set up opcodes */ + op = PyInstance_New(class, NULL, NULL); + if (!op) + return; + if (PyObject_SetAttrString(module, "op", op) == -1) + return; + for (i = 0; i < ARRAY_COUNT(opcodes); i++) { + /* yank off the JSOP prefix */ + const char* opcode = opcodes[i]; + if (strlen(opcode) > 5) + opcode += 5; + if (PyObject_SetAttrString(op, opcode, PyLong_FromLong(OPCODE_TO_NUM(i))) == -1) + return; + } } PyMODINIT_FUNC @@ -131,7 +173,7 @@ PyTuple_SET_ITEM(tuple, node_offset, kw); - if (PyDict_SetItemString(kw, "type", PyString_FromString(tokens[jsnode->pn_type])) == -1) + if (PyDict_SetItemString(kw, "type", Py_BuildValue("i", TOK_TO_NUM(jsnode->pn_type))) == -1) goto fail; if (PyDict_SetItemString(kw, "node_index", Py_BuildValue("i", node_offset)) == -1) goto fail; @@ -153,7 +195,7 @@ goto fail; } - if (PyDict_SetItemString(kw, "opcode", PyString_FromString(opcodes[jsnode->pn_op])) == -1) + if (PyDict_SetItemString(kw, "opcode", Py_BuildValue("i", OPCODE_TO_NUM(jsnode->pn_op))) == -1) goto fail; if (jsnode->pn_type == TOK_NUMBER) { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2008-03-01 17:32:58
|
Revision: 148 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=148&view=rev Author: matthiasmiller Date: 2008-03-01 09:32:55 -0800 (Sat, 01 Mar 2008) Log Message: ----------- include some notes on style guidelines and upgrading SpiderMonkey Added Paths: ----------- trunk/DEVELOPMENT Removed Paths: ------------- trunk/TODO Copied: trunk/DEVELOPMENT (from rev 147, trunk/TODO) =================================================================== --- trunk/DEVELOPMENT (rev 0) +++ trunk/DEVELOPMENT 2008-03-01 17:32:55 UTC (rev 148) @@ -0,0 +1,31 @@ +** STYLE GUIDELINES + +> Use tabs instead of spaces (for now) +> All lines should be 79 characters or less +> For everything else, follow http://www.python.org/dev/peps/pep-0008/ as much + as possible. + + +** TODO + +> implement conf file +> support HTML parsing +> support JScript extensions +> implement semicolons warning +> implement line break warning +> add test for syntax error + + +** UPGRADING SPIDERMONKEY + +Use the following command to upgrade SpiderMonkey. Replace X.X.X with the +version number. js-X.X.X is the directory containing the new version of +SpiderMonkey. Use a relative path for pretty commit messages. + +svn_load_dirs.pl \ + -t X.X.X \ + -p svn_load_dirs.conf \ + https://javascriptlint.svn.sourceforge.net/svnroot/javascriptlint/vendorsrc/Mozilla.org/js \ + current \ + js-X.X.X + Deleted: trunk/TODO =================================================================== --- trunk/TODO 2008-03-01 17:21:01 UTC (rev 147) +++ trunk/TODO 2008-03-01 17:32:55 UTC (rev 148) @@ -1,13 +0,0 @@ - -> implement conf file - -> support HTML parsing - -> support JScript extensions - -> implement semicolons warning - -> implement line break warning - -> add test for syntax error - This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2008-03-01 17:21:12
|
Revision: 147 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=147&view=rev Author: matthiasmiller Date: 2008-03-01 09:21:01 -0800 (Sat, 01 Mar 2008) Log Message: ----------- initial import of pyjsl Added Paths: ----------- trunk/COPYING trunk/INSTALL trunk/Makefile.SpiderMonkey trunk/TODO trunk/jsl.py trunk/pyjsl/ trunk/pyjsl/__init__.py trunk/pyjsl/conf.py trunk/pyjsl/lint.py trunk/pyjsl/parse.py trunk/pyjsl/util.py trunk/pyjsl/visitation.py trunk/pyjsl/warnings.py trunk/pyspidermonkey/ trunk/pyspidermonkey/pyspidermonkey.c trunk/pyspidermonkey/tokens.tbl trunk/setup.py trunk/test.py Property Changed: ---------------- trunk/ Property changes on: trunk ___________________________________________________________________ Name: svn:ignore + dist build *.pyc Added: trunk/COPYING =================================================================== --- trunk/COPYING (rev 0) +++ trunk/COPYING 2008-03-01 17:21:01 UTC (rev 147) @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. Added: trunk/INSTALL =================================================================== --- trunk/INSTALL (rev 0) +++ trunk/INSTALL 2008-03-01 17:21:01 UTC (rev 147) @@ -0,0 +1,14 @@ + +BUILDING FROM THE SUBVERSION TRUNK +* Windows Prequisites: + * Visual Studio 2003 + * Python + * MozillaBuild (http://developer.mozilla.org/en/docs/Windows_Build_Prerequisites) + + Launch the MozillaBuild 7.1 batch file. (You may have to run this as an Administrator + on Windows Vista.) Run the commands in that shell. + +On all platforms: + $ make -f Makefile.SpiderMonkey + $ python setup.py build + Added: trunk/Makefile.SpiderMonkey =================================================================== --- trunk/Makefile.SpiderMonkey (rev 0) +++ trunk/Makefile.SpiderMonkey 2008-03-01 17:21:01 UTC (rev 147) @@ -0,0 +1,67 @@ + +SPIDERMONKEY_SRC=spidermonkey/src + +# Load the SpiderMonkey config to find the OS define +# Also use this for the SO_SUFFIX +BUILD_OPT=1 +DEPTH=$(SPIDERMONKEY_SRC) +include $(SPIDERMONKEY_SRC)/config.mk +SPIDERMONKEY_OS=$(firstword $(patsubst -D%, %, $(filter -DXP_%, $(OS_CFLAGS)))) + +ifdef USE_MSVC +JS_LIB=js32.lib +else +JS_LIB=libjs.a +endif + +BUILD_DIR=build/spidermonkey + +# Use a dynamically-created makefile to determine the distutils output dir +DISTUTILS_DIR_MAKEFILE=$(BUILD_DIR)/Makefile-distutils +include $(DISTUTILS_DIR_MAKEFILE) + +ORIG_LIB=$(SPIDERMONKEY_SRC)/$(OBJDIR)/$(JS_LIB) +COPY_LIB=$(BUILD_DIR)/$(JS_LIB) +ORIG_DLL=$(SPIDERMONKEY_SRC)/$(OBJDIR)/js32.dll +COPY_DLL=$(DISTUTILS_DIR)/js32.dll +OS_HEADER=$(BUILD_DIR)/js_operating_system.h +ORIG_JSAUTOCFG_H=$(SPIDERMONKEY_SRC)/$(OBJDIR)/jsautocfg.h +COPY_JSAUTOCFG_H=$(BUILD_DIR)/jsautocfg.h + +ALL_TARGETS=$(COPY_LIB) $(OS_HEADER) +ifndef PREBUILT_CPUCFG +ALL_TARGETS+=$(COPY_JSAUTOCFG_H) +endif + +ifeq ($(SPIDERMONKEY_OS),XP_WIN) +ALL_TARGETS+=$(COPY_DLL) +endif + +all: $(ALL_TARGETS) + +clean: + make -f Makefile.ref -C $(SPIDERMONKEY_SRC) BUILD_OPT=$(BUILD_OPT) clean + rm $(ORIG_LIB) + rm -R $(BUILD_DIR) + +$(DISTUTILS_DIR_MAKEFILE): Makefile.SpiderMonkey $(BUILD_DIR) + python -c "import setup; print 'DISTUTILS_DIR='+setup.get_lib_path()" >> $(DISTUTILS_DIR_MAKEFILE) + +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + +$(COPY_LIB): $(BUILD_DIR) $(ORIG_LIB) + cp $(ORIG_LIB) $(COPY_LIB) + +$(COPY_DLL): $(BUILD_DIR) $(ORIG_LIB) + cp $(ORIG_DLL) $(COPY_DLL) + +$(OS_HEADER): $(BUILD_DIR) + echo "#define $(SPIDERMONKEY_OS)" > $(OS_HEADER) + +$(COPY_JSAUTOCFG_H): $(ORIG_JSAUTOCFG_H) + cp $(ORIG_JSAUTOCFG_H) $(COPY_JSAUTOCFG_H) + +$(ORIG_LIB): + make -f Makefile.ref -C $(SPIDERMONKEY_SRC) BUILD_OPT=$(BUILD_OPT) + Added: trunk/TODO =================================================================== --- trunk/TODO (rev 0) +++ trunk/TODO 2008-03-01 17:21:01 UTC (rev 147) @@ -0,0 +1,13 @@ + +> implement conf file + +> support HTML parsing + +> support JScript extensions + +> implement semicolons warning + +> implement line break warning + +> add test for syntax error + Added: trunk/jsl.py =================================================================== --- trunk/jsl.py (rev 0) +++ trunk/jsl.py 2008-03-01 17:21:01 UTC (rev 147) @@ -0,0 +1,147 @@ +#!/usr/bin/python +import codecs +import getopt +import glob +import os +import sys +import unittest + +try: + import setup +except ImportError: + pass +else: + sys.path.append(setup.get_lib_path()) + +import pyjsl.conf +import pyjsl.parse +import pyjsl.util +import test + +_lint_results = { + 'warnings': 0, + 'errors': 0 +} + +def get_test_files(): + # Get a list of test files. + dir_ = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tests') + + all_files = [] + for root, dirs, files in os.walk(dir_): + all_files += [os.path.join(dir_, root, file) for file in files] + if '.svn' in dirs: + dirs.remove('.svn') + # TODO + if 'conf' in dirs: + dirs.remove('conf') + all_files.sort() + return all_files + +def run_tests(): + for file in get_test_files(): + if file.endswith('.htm') or file.endswith('.html'): + continue #TODO + elif file.endswith('.js'): + print file + try: + test.run(file) + except test.TestError, error: + print error + +def _dump(paths): + for path in paths: + script = pyjsl.util.readfile(path) + pyjsl.parse.dump_tree(script) + +def _lint(paths, conf): + def lint_error(path, line, col, errname): + _lint_results['warnings'] = _lint_results['warnings'] + 1 + print '%s(%i): %s' % (path, line, errname) + pyjsl.lint.lint_files(paths, lint_error, conf=conf) + +def _resolve_paths(path, recurse): + if os.path.isfile(path): + return [path] + elif os.path.isdir(path): + dir = path + pattern = '*' + else: + dir, pattern = os.path.split(path) + + # Build a list of directories + dirs = [dir] + if recurse: + for cur_root, cur_dirs, cur_files in os.walk(dir): + for name in cur_dirs: + dirs.append(os.path.join(cur_root, name)) + + # Glob all files. + paths = [] + for dir in dirs: + paths.extend(glob.glob(os.path.join(dir, pattern))) + return paths + +def profile_enabled(func, *args, **kwargs): + import tempfile + import hotshot + import hotshot.stats + handle, filename = tempfile.mkstemp() + profile = hotshot.Profile(filename) + profile.runcall(func, *args, **kwargs) + profile.close() + stats = hotshot.stats.load(filename) + stats = stats.sort_stats("time") + stats.print_stats() +def profile_disabled(func, *args, **kwargs): + func(*args, **kwargs) + +def usage(): + print """ +Usage: + jsl [files] + --help (-h) print this help + --test (-t) run tests + --dump= dump this script +""" + +if __name__ == '__main__': + try: + opts, args = getopt.getopt(sys.argv[1:], 'ht:v', ['conf=', 'help', 'test', 'dump', 'unittest', 'profile']) + except getopt.GetoptError: + usage() + sys.exit(2) + + dump = False + conf = pyjsl.conf.Conf() + profile_func = profile_disabled + for opt, val in opts: + if opt in ('-h', '--help'): + usage() + sys.exit() + if opt in ('--dump',): + dump = True + if opt in ('-t', '--test'): + profile_func(run_tests) + if opt in ('--unittest',): + unittest.main(pyjsl.parse, argv=sys.argv[:1]) + if opt in ('--profile',): + profile_func = profile_enabled + if opt in ('--conf',): + conf.loadfile(val) + + paths = [] + for recurse, path in conf['paths']: + paths.extend(_resolve_paths(path, recurse)) + for arg in args: + paths.extend(_resolve_paths(arg, False)) + if dump: + profile_func(_dump, paths) + else: + profile_func(_lint, paths, conf) + + if _lint_results['errors']: + sys.exit(3) + if _lint_results['warnings']: + sys.exit(1) + Property changes on: trunk/jsl.py ___________________________________________________________________ Name: svn:executable + * Property changes on: trunk/pyjsl ___________________________________________________________________ Name: svn:ignore + *.pyc Added: trunk/pyjsl/__init__.py =================================================================== Added: trunk/pyjsl/conf.py =================================================================== --- trunk/pyjsl/conf.py (rev 0) +++ trunk/pyjsl/conf.py 2008-03-01 17:21:01 UTC (rev 147) @@ -0,0 +1,134 @@ +import os + +import warnings + +class ConfError(Exception): + def __init__(self, error): + Exception.__init__(error) + self.lineno = None + self.path = None + +class Setting(): + wants_parm = False + wants_dir = False + +class BooleanSetting(Setting): + wants_parm = False + def __init__(self, default): + self.value = default + def load(self, enabled): + self.value = enabled + +class StringSetting(Setting): + wants_parm = True + def __init__(self, default): + self.value = default + def load(self, enabled, parm): + if not enabled: + raise ConfError, 'Expected +.' + self.value = parm + +class DeclareSetting(Setting): + wants_parm = True + def __init__(self): + self.value = [] + def load(self, enabled, parm): + if not enabled: + raise ConfError, 'Expected +.' + self.value.append(parm) + +class ProcessSetting(Setting): + wants_parm = True + wants_dir = True + def __init__(self, recurse_setting): + self.value = [] + self._recurse = recurse_setting + def load(self, enabled, parm, dir): + if dir: + parm = os.path.join(dir, parm) + self.value.append((self._recurse.value, parm)) + +class Conf(): + def __init__(self): + recurse = BooleanSetting(False) + self._settings = { + 'recurse': recurse, + 'show_context': BooleanSetting(False), + 'output-format': StringSetting('TODO'), + 'lambda_assign_requires_semicolon': BooleanSetting(False), + 'legacy_control_comments': BooleanSetting(True), + 'jscript_function_extensions': BooleanSetting(False), + 'always_use_option_explicit': BooleanSetting(False), + 'define': DeclareSetting(), + 'context': BooleanSetting(False), + 'process': ProcessSetting(recurse), + # SpiderMonkey warnings + 'no_return_value': BooleanSetting(True), + 'equal_as_assign': BooleanSetting(True), + 'anon_no_return_value': BooleanSetting(True) + } + for klass in warnings.klasses: + self._settings[klass.__name__] = BooleanSetting(True) + self.loadline('-block_without_braces') + + def loadfile(self, path): + path = os.path.abspath(path) + conf = open(path, 'r').read() + try: + self.loadtext(conf, dir=os.path.dirname(path)) + except ConfError, error: + error.path = path + raise + + def loadtext(self, conf, dir=None): + lines = conf.splitlines() + for lineno in range(0, len(lines)): + try: + self.loadline(lines[lineno], dir) + except ConfError, error: + error.lineno = lineno + raise + + def loadline(self, line, dir=None): + assert not '\r' in line + assert not '\n' in line + + # Allow comments + line = line.partition('#')[0] + line = line.rstrip() + if not line: + return + + # Parse the +/- + if line.startswith('+'): + enabled = True + elif line.startswith('-'): + enabled = False + else: + raise ConfError, 'Expected + or -.' + line = line[1:] + + # Parse the key/parms + name = line.split()[0].lower() + parm = line[len(name):].lstrip() + + # Load the setting + setting = self._settings[name] + args = { + 'enabled': enabled + } + if setting.wants_parm: + args['parm'] = parm + elif parm: + raise ConfError, 'The %s setting does not expect a parameter.' % name + if setting.wants_dir: + args['dir'] = dir + setting.load(**args) + + def __getitem__(self, name): + if name == 'paths': + name = 'process' + elif name == 'declarations': + name = 'define' + return self._settings[name].value + Added: trunk/pyjsl/lint.py =================================================================== --- trunk/pyjsl/lint.py (rev 0) +++ trunk/pyjsl/lint.py 2008-03-01 17:21:01 UTC (rev 147) @@ -0,0 +1,288 @@ +#!/usr/bin/python +import os.path +import re + +import conf +import parse +import visitation +import warnings +import util + +_newline_kinds = ( + 'eof', 'comma', 'dot', 'semi', 'colon', 'lc', 'rc', 'lp', 'rb', 'assign', + 'relop', 'hook', 'plus', 'minus', 'star', 'divop', 'eqop', 'shop', 'or', + 'and', 'bitor', 'bitxor', 'bitand', 'else', 'try' +) + +_globals = frozenset([ + 'Array', 'Boolean', 'Math', 'Number', 'String', 'RegExp', 'Script', 'Date', + 'isNaN', 'isFinite', 'parseFloat', 'parseInt', + 'eval', 'NaN', 'Infinity', + 'escape', 'unescape', 'uneval', + 'decodeURI', 'encodeURI', 'decodeURIComponent', 'encodeURIComponent', + 'Function', 'Object', + 'Error', 'InternalError', 'EvalError', 'RangeError', 'ReferenceError', + 'SyntaxError', 'TypeError', 'URIError', + 'arguments', 'undefined' +]) + +_identifier = re.compile('^[A-Za-z_$][A-Za-z0-9_$]*$') + +def _find_function(node): + while node and node.kind != 'function': + node = node.parent + return node + +def _find_functions(node): + functions = [] + while node: + if node.kind == 'function': + functions.append(node) + node = node.parent + return functions + +def _parse_control_comment(comment): + """ Returns None or (keyword, parms) """ + if comment.atom.lower().startswith('jsl:'): + control_comment = comment.atom[4:] + elif comment.atom.startswith('@') and comment.atom.endswith('@'): + control_comment = comment.atom[1:-1] + else: + return None + + control_comments = { + 'ignoreall': (False), + 'ignore': (False), + 'end': (False), + 'option explicit': (False), + 'import': (True), + 'fallthru': (False), + 'pass': (False), + 'declare': (True) + } + if control_comment.lower() in control_comments: + keyword = control_comment.lower() + else: + keyword = control_comment.lower().split()[0] + + parms = control_comment[len(keyword):].strip() + return (comment, keyword, parms) + +class Scope(): + def __init__(self, node): + self._is_with_scope = node.kind == 'with' + self._parent = None + self._kids = [] + self._identifiers = {} + self._references = [] + self._node = node + def add_scope(self, node): + self._kids.append(Scope(node)) + self._kids[-1]._parent = self + if self._is_with_scope: + self._kids[-1]._is_with_scope = True + return self._kids[-1] + def add_declaration(self, name, node): + if not self._is_with_scope: + self._identifiers[name] = node + def add_reference(self, name, node): + if not self._is_with_scope: + self._references.append((name, node)) + def get_identifier(self, name): + if name in self._identifiers: + return self._identifiers[name] + else: + return None + def get_identifiers(self): + "returns a list of names" + return self._identifiers.keys() + def resolve_identifier(self, name): + if name in self._identifiers: + return self, self._identifiers[name] + if self._parent: + return self._parent.resolve_identifier(name) + return None + def get_undeclared_identifiers(self): + identifiers = [] + for child in self._kids: + identifiers += child.get_undeclared_identifiers() + for name, node in self._references: + if not self.resolve_identifier(name): + identifiers.append(node) + return identifiers + def find_scope(self, node): + for kid in self._kids: + scope = kid.find_scope(node) + if scope: + return scope + + # Always add it to the outer scope. + if not self._parent or \ + (node.start_pos() >= self._node.start_pos() and \ + node.end_pos() <= self._node.end_pos()): + return self + + return None + +def lint_files(paths, lint_error, conf=conf.Conf()): + def lint_file(path): + def import_script(import_path): + import_path = os.path.join(os.path.dirname(path), import_path) + return lint_file(import_path) + def _lint_error(*args): + return lint_error(normpath, *args) + + normpath = util.normpath(path) + if not normpath in lint_cache: + lint_cache[normpath] = {} + script = util.readfile(path) + print normpath + _lint_script(script, lint_cache[normpath], _lint_error, conf, import_script) + return lint_cache[normpath] + + lint_cache = {} + for path in paths: + lint_file(path) + +def _lint_script(script, script_cache, lint_error, conf, import_callback): + def parse_error(row, col, msg): + if not msg in ('redeclared_var', 'var_hides_arg'): + parse_errors.append((parse.NodePos(row, col), msg)) + + def report(node, errname): + _report(node.start_pos(), errname, True) + + def _report(pos, errname, require_key): + try: + if not conf[errname]: + return + except KeyError, err: + if require_key: + raise + + for start, end in ignores: + if pos >= start and pos <= end: + return + + return lint_error(pos.line, pos.col, errname) + + parse_errors = [] + root, comments = parse.parse(script, parse_error) + ignores = [] + start_ignore = None + declares = [] + import_paths = [] + for comment in comments: + cc = _parse_control_comment(comment) + if cc: + node, keyword, parms = cc + if keyword == 'declare': + if not _identifier.match(parms): + report(node, 'jsl_cc_not_understood') + else: + declares.append((parms, node)) + elif keyword == 'ignore': + if start_ignore: + report(node, 'mismatch_ctrl_comments') + else: + start_ignore = node + elif keyword == 'end': + if start_ignore: + ignores.append((start_ignore.start_pos(), node.end_pos())) + start_ignore = None + else: + report(node, 'mismatch_ctrl_comments') + elif keyword == 'import': + if not parms: + report(node, 'jsl_cc_not_understood') + else: + import_paths.append(parms) + else: + if comment.opcode == 'c_comment': + if '/*' in comment.atom or comment.atom.endswith('/'): + report(comment, 'nested_comment') + if comment.atom.lower().startswith('jsl:'): + report(comment, 'jsl_cc_not_understood') + elif comment.atom.startswith('@'): + report(comment, 'legacy_cc_not_understood') + if start_ignore: + report(start_ignore, 'mismatch_ctrl_comments') + + # Wait to report parse errors until loading jsl:ignore directives. + for pos, msg in parse_errors: + _report(pos, msg, False) + + visitors = visitation.make_visitors(warnings.klasses) + + assert not script_cache + imports = script_cache['imports'] = set() + scope = script_cache['scope'] = Scope(root) + + # kickoff! + _lint_node(root, visitors, report, scope) + + # Process imports by copying global declarations into the universal scope. + imports |= set(conf['declarations']) + imports |= _globals + for path in import_paths: + cache = import_callback(path) + imports |= cache['imports'] + imports |= set(cache['scope'].get_identifiers()) + + for name, node in declares: + declare_scope = scope.find_scope(node) + if declare_scope.get_identifier(name): + report(node, 'redeclared_var') + else: + declare_scope.add_declaration(name, node) + + for node in scope.get_undeclared_identifiers(): + if not node.atom in imports: + report(node, 'undeclared_identifier') + +def _lint_node(node, visitors, report, scope): + + def warn_or_declare(name, node): + other = scope.get_identifier(name) + if other and other.kind == 'function' and name in other.fn_args: + report(node, 'var_hides_arg') + elif other: + report(node, 'redeclared_var') + else: + scope.add_declaration(name, node) + + # Let the visitors warn. + for kind in (node.kind, '%s:%s' % (node.kind, node.opcode)): + if kind in visitors: + for visitor in visitors[kind]: + warning_node = visitor(node) + if warning_node: + report(warning_node, visitor.im_class.__name__) + + if node.kind == 'name': + if node.node_index == 0 and node.parent.kind == 'colon' and node.parent.parent.kind == 'rc': + pass # left side of object literal + elif node.parent.kind == 'catch': + scope.add_declaration(node.atom, node) + else: + scope.add_reference(node.atom, node) + + # Push function identifiers + if node.kind == 'function': + if node.fn_name: + warn_or_declare(node.fn_name, node) + scope = scope.add_scope(node) + for var_name in node.fn_args: + scope.add_declaration(var_name, node) + elif node.kind == 'lexicalscope': + scope = scope.add_scope(node) + elif node.kind == 'with': + scope = scope.add_scope(node) + + if node.parent and node.parent.kind == 'var': + warn_or_declare(node.atom, node) + + for child in node.kids: + if child: + _lint_node(child, visitors, report, scope) + Added: trunk/pyjsl/parse.py =================================================================== --- trunk/pyjsl/parse.py (rev 0) +++ trunk/pyjsl/parse.py 2008-03-01 17:21:01 UTC (rev 147) @@ -0,0 +1,336 @@ +#!/usr/bin/python +""" Parses a script into nodes. """ +import bisect +import re +import unittest + +import pyspidermonkey + +class NodePos(): + def __init__(self, line, col): + self.line = line + self.col = col + def __cmp__(self, other): + if self.line < other.line: + return -1 + if self.line > other.line: + return 1 + if self.col < other.col: + return -1 + if self.col > other.col: + return 1 + return 0 + def __repr__(self): + return '(line %i, col %i)' % (self.line+1, self.col+1) + +class NodePositions(): + " Given a string, allows [x] lookups for NodePos line and column numbers." + def __init__(self, text): + # Find the length of each line and incrementally sum all of the lengths + # to determine the ending position of each line. + self._lines = text.splitlines(True) + lines = [0] + [len(x) for x in self._lines] + for x in range(1, len(lines)): + lines[x] += lines[x-1] + self._line_offsets = lines + def from_offset(self, offset): + line = bisect.bisect(self._line_offsets, offset)-1 + col = offset - self._line_offsets[line] + return NodePos(line, col) + def to_offset(self, pos): + offset = self._line_offsets[pos.line] + pos.col + assert offset <= self._line_offsets[pos.line+1] # out-of-bounds col num + return offset + def text(self, start, end): + assert start <= end + # Trim the ending first in case it's a single line. + lines = self._lines[start.line:end.line+1] + lines[-1] = lines[-1][:end.col+1] + lines[0] = lines[0][start.col:] + return ''.join(lines) + +class NodeRanges(): + def __init__(self): + self._offsets = [] + def add(self, start, end): + i = bisect.bisect_left(self._offsets, start) + if i % 2 == 1: + i -= 1 + start = self._offsets[i] + + end = end + 1 + j = bisect.bisect_left(self._offsets, end) + if j % 2 == 1: + end = self._offsets[j] + j += 1 + + self._offsets[i:j] = [start,end] + def has(self, pos): + return bisect.bisect_right(self._offsets, pos) % 2 == 1 + +class _Node(): + def __init__(self, kwargs): + def _to_node(kid): + if kid: + return _Node(kid) + kwargs['type'] = kwargs['type'].lower() + self.kind = kwargs['type'] + assert kwargs['opcode'].startswith('JSOP_') + kwargs['opcode'] = kwargs['opcode'][5:].lower() + self.opcode = kwargs['opcode'] + self.kids = tuple([_to_node(kid) for kid in kwargs['kids']]) + for kid in self.kids: + if kid: + kid.parent = self + if 'atom' in kwargs: + self.atom = kwargs['atom'] + if 'dval' in kwargs: + self.dval = kwargs['dval'] + if 'fn_name' in kwargs: + self.fn_name = kwargs['fn_name'] + if 'fn_args' in kwargs: + self.fn_args = kwargs['fn_args'] + if 'end_comma' in kwargs: + self.end_comma = kwargs['end_comma'] + self.args = kwargs + self.node_index = kwargs['node_index'] + self.parent = None + self.start_line = kwargs['start_row'] + self._start_pos = None + self._end_pos = None + + def add_child(self, node): + if node: + node.node_index = len(self.kids) + node.parent = self + self.kids.append(node) + + def start_pos(self): + self._start_pos = self._start_pos or \ + NodePos(self.args['start_row'], self.args['start_col']) + return self._start_pos + + def end_pos(self): + self._end_pos = self._end_pos or \ + NodePos(self.args['end_row'], self.args['end_col']) + return self._end_pos + + def __repr__(self): + kind = self.kind + if not kind: + kind = '(none)' + return '%s>%s' % (kind, str(self.kids)) + + def is_equivalent(self, other, are_functions_equiv=False): + if not other: + return False + + # Bail out for functions + if not are_functions_equiv: + if self.kind == 'function': + return False + if self.kind == 'lp' and self.opcode == 'call': + return False + + if self.kind != other.kind: + return False + if self.opcode != other.opcode: + return False + + # Check atoms on names, properties, and string constants + if self.kind in ('name', 'dot', 'string') and self.atom != other.atom: + return False + + # Check values on numbers + if self.kind == 'number' and self.dval != other.dval: + return False + + # Compare child nodes + if len(self.kids) != len(other.kids): + return False + for i in range(0, len(self.kids)): + # Watch for dead nodes + if not self.kids[i]: + if not other.kids[i]: return True + else: return False + if not self.kids[i].is_equivalent(other.kids[i]): + return False + + return True + +def _parse_comments(script, root, node_positions, ignore_ranges): + pos = 0 + single_line_re = r"//[^\r\n]*" + multi_line_re = r"/\*(.*?)\*/" + full_re = "(%s)|(%s)" % (single_line_re, multi_line_re) + comment_re = re.compile(full_re, re.DOTALL) + + comments = [] + while True: + match = comment_re.search(script, pos) + if not match: + return comments + + # Get the comment text + comment_text = script[match.start():match.end()] + if comment_text.startswith('/*'): + comment_text = comment_text[2:-2] + opcode = 'JSOP_C_COMMENT' + else: + comment_text = comment_text[2:] + opcode = 'JSOP_CPP_COMMENT' + + start_offset = match.start()+1 + end_offset = match.end() + + # Make sure it doesn't start in a string or regexp + if not ignore_ranges.has(start_offset): + start_pos = node_positions.from_offset(start_offset) + end_pos = node_positions.from_offset(end_offset) + kwargs = { + 'type': 'COMMENT', + 'atom': comment_text, + 'opcode': opcode, + 'start_row': start_pos.line, + 'start_col': start_pos.col, + 'end_row': end_pos.line, + 'end_col': end_pos.col, + 'kids': [], + 'node_index': None + } + comment_node = _Node(kwargs) + comments.append(comment_node) + pos = match.end() + else: + pos = match.start()+1 + +def parse(script, error_callback): + def _wrapped_callback(line, col, msg): + assert msg.startswith('JSMSG_') + msg = msg[6:].lower() + error_callback(line, col, msg) + + positions = NodePositions(script) + + roots = [] + nodes = [] + comment_ignore_ranges = NodeRanges() + def process(node): + if node.kind == 'number': + node.atom = positions.text(node.start_pos(), node.end_pos()) + elif node.kind == 'string' or \ + (node.kind == 'object' and node.opcode == 'regexp'): + start_offset = positions.to_offset(node.start_pos()) + end_offset = positions.to_offset(node.end_pos()) + comment_ignore_ranges.add(start_offset, end_offset) + for kid in node.kids: + if kid: + process(kid) + def pop(): + nodes.pop() + + roots = pyspidermonkey.traverse(script, _wrapped_callback) + assert len(roots) == 1 + root_node = _Node(roots[0]) + process(root_node) + + comments = _parse_comments(script, root_node, positions, comment_ignore_ranges) + return root_node, comments + +def _dump_node(node, depth=0): + print '. '*depth, + if node is None: + print '(none)' + else: + print node.kind, '\t', node.args + for node in node.kids: + _dump_node(node, depth+1) + +def dump_tree(script): + def error_callback(line, col, msg): + print '(%i, %i): %s', (line, col, msg) + node, comments = parse(script, error_callback) + _dump_node(node) + +class TestComments(unittest.TestCase): + def _test(self, script, expected_comments): + root, comments = parse(script, lambda line, col, msg: None) + encountered_comments = [node.atom for node in comments] + self.assertEquals(encountered_comments, list(expected_comments)) + def testSimpleComments(self): + self._test('re = /\//g', ()) + self._test('re = /\///g', ()) + self._test('re = /\////g', ('g',)) + def testCComments(self): + self._test('/*a*//*b*/', ('a', 'b')) + self._test('/*a\r\na*//*b\r\nb*/', ('a\r\na', 'b\r\nb')) + self._test('a//*b*/c', ('*b*/c',)) + self._test('a///*b*/c', ('/*b*/c',)) + self._test('a/*//*/;', ('//',)) + self._test('a/*b*/+/*c*/d', ('b', 'c')) + +class TestNodePositions(unittest.TestCase): + def _test(self, text, expected_lines, expected_cols): + # Get a NodePos list + positions = NodePositions(text) + positions = [positions.from_offset(i) for i in range(0, len(text))] + encountered_lines = ''.join([str(x.line) for x in positions]) + encountered_cols = ''.join([str(x.col) for x in positions]) + self.assertEquals(encountered_lines, expected_lines.replace(' ', '')) + self.assertEquals(encountered_cols, expected_cols.replace(' ', '')) + def testSimple(self): + self._test( + 'abc\r\ndef\nghi\n\nj', + '0000 0 1111 2222 3 4', + '0123 4 0123 0123 0 0' + ) + self._test( + '\rabc', + '0 111', + '0 012' + ) + def testText(self): + pos = NodePositions('abc\r\ndef\n\nghi') + self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 0)), 'a') + self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 2)), 'abc') + self.assertEquals(pos.text(NodePos(0, 2), NodePos(1, 2)), 'c\r\ndef') + def testOffset(self): + pos = NodePositions('abc\r\ndef\n\nghi') + self.assertEquals(pos.to_offset(NodePos(0, 2)), 2) + self.assertEquals(pos.to_offset(NodePos(1, 0)), 5) + self.assertEquals(pos.to_offset(NodePos(3, 1)), 11) + +class TestNodeRanges(unittest.TestCase): + def testAdd(self): + r = NodeRanges() + r.add(5, 10) + self.assertEquals(r._offsets, [5,11]) + r.add(15, 20) + self.assertEquals(r._offsets, [5,11,15,21]) + r.add(21,22) + self.assertEquals(r._offsets, [5,11,15,23]) + r.add(4,5) + self.assertEquals(r._offsets, [4,11,15,23]) + r.add(9,11) + self.assertEquals(r._offsets, [4,12,15,23]) + r.add(10,20) + self.assertEquals(r._offsets, [4,23]) + r.add(4,22) + self.assertEquals(r._offsets, [4,23]) + r.add(30,30) + self.assertEquals(r._offsets, [4,23,30,31]) + def testHas(self): + r = NodeRanges() + r.add(5, 10) + r.add(15, 15) + assert not r.has(4) + assert r.has(5) + assert r.has(6) + assert r.has(9) + assert r.has(10) + assert not r.has(14) + assert r.has(15) + assert not r.has(16) +if __name__ == '__main__': + unittest.main() + Added: trunk/pyjsl/util.py =================================================================== --- trunk/pyjsl/util.py (rev 0) +++ trunk/pyjsl/util.py 2008-03-01 17:21:01 UTC (rev 147) @@ -0,0 +1,16 @@ +import codecs +import os.path + +def readfile(path): + file = codecs.open(path, 'r', 'utf-8') + contents = file.read() + if contents[0] == unicode(codecs.BOM_UTF8, 'utf8'): + contents = contents[1:] + return contents + +def normpath(path): + path = os.path.abspath(path) + path = os.path.normcase(path) + path = os.path.normpath(path) + return path + Added: trunk/pyjsl/visitation.py =================================================================== --- trunk/pyjsl/visitation.py (rev 0) +++ trunk/pyjsl/visitation.py 2008-03-01 17:21:01 UTC (rev 147) @@ -0,0 +1,35 @@ +""" This is an abstract module for visiting specific nodes. This is useed to +traverse the tree to generate warnings. +""" + +def visit(*args): + """ This decorator is used to indicate which nodes the function should + examine. The function should accept (self, node) and return the relevant + node or None. """ + def _decorate(fn): + fn._visit_nodes = args + return fn + return _decorate + +def make_visitors(klasses): + """ Searches klasses for all member functions decorated with @visit and + returns a dictionary that maps from node type to visitor function. """ + visitors = {} + + # Intantiate an instance of each class + for klass in klasses: + if klass.__name__.lower() != klass.__name__: + raise ValueError, 'class names must be lowercase' + if not klass.__doc__: + raise ValueError, 'missing docstring on class' + + # Look for functions with the "_visit_nodes" property. + visitor = klass() + for func in [getattr(visitor, name) for name in dir(visitor)]: + for node_kind in getattr(func, '_visit_nodes', ()): + # Map from node_kind to the function + if not node_kind in visitors: + visitors[node_kind] = [] + visitors[node_kind].append(func) + return visitors + Added: trunk/pyjsl/warnings.py =================================================================== --- trunk/pyjsl/warnings.py (rev 0) +++ trunk/pyjsl/warnings.py 2008-03-01 17:21:01 UTC (rev 147) @@ -0,0 +1,475 @@ +""" This module contains all the warnings. To add a new warning, define a +class. Its name should be in lowercase and words should be separated by +underscores. Its docstring should be the warning message. + +The class can have one more more member functions to inspect nodes. The +function should be decorated with a @lookat call specifying the nodes it +wants to examine. The node names may be in the 'kind' or 'kind:opcode' +format. To report a warning, the function should return the node causing +the warning. + +For example: + + class warning_name: + 'questionable JavaScript coding style' + @lookat('nodekind', 'nodekind:opcode') + def _lint(self, node): + if questionable: + return node +""" +import re +import sys +import types + +from visitation import visit as lookat +# TODO: document inspect, node:opcode, etc + +def _get_branch_in_for(node): + " Returns None if this is not one of the branches in a 'for' " + if node.parent and node.parent.kind == 'reserved' and \ + node.parent.parent.kind == 'for': + return node.node_index + return None + +def _get_exit_points(node): + if node.kind == 'lc': + # Only if the last child contains it + exit_points = set([None]) + for kid in node.kids: + # "None" is only a valid exit point for the last statement. + if None in exit_points: + exit_points.remove(None) + if kid: + exit_points |= _get_exit_points(kid) + elif node.kind == 'if': + # Only if both branches have an exit point + cond_, if_, else_ = node.kids + exit_points = _get_exit_points(if_) + if else_: + exit_points |= _get_exit_points(else_) + elif node.kind == 'switch': + exit_points = set([None]) + + switch_has_default = False + switch_has_final_fallthru = True + + switch_var, switch_stmts = node.kids + for node in switch_stmts.kids: + case_val, case_stmt = node.kids + case_exit_points = _get_exit_points(case_stmt) + switch_has_default = switch_has_default or node.kind == 'default' + switch_has_final_fallthru = None in case_exit_points + exit_points |= case_exit_points + + # Correct the "None" exit point. + exit_points.remove(None) + + # Check if the switch contained any break + if 'break' in exit_points: + exit_points.remove('break') + exit_points.add(None) + + # Check if the switch had a default case + if not switch_has_default: + exit_points.add(None) + + # Check if the final case statement had a fallthru + if switch_has_final_fallthru: + exit_points.add(None) + elif node.kind == 'break': + exit_points = set(['break']) + elif node.kind == 'with': + exit_points = _get_exit_points(node.kids[-1]) + elif node.kind == 'return': + exit_points = set(['return']) + elif node.kind == 'throw': + exit_points = set(['throw']) + elif node.kind == 'try': + try_, catch_, finally_ = node.kids + + exit_points = _get_exit_points(try_) | _get_exit_points(catch_) + if finally_: + # Always if the finally has an exit point + if None in exit_points: + exit_points.remove(None) + exit_points |= _get_exit_points(finally_) + else: + exit_points = set([None]) + + return exit_points + +class comparison_type_conv: + 'comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==)' + @lookat('eqop:eq') + def _lint(self, node): + lvalue, rvalue = node.kids + if not self._allow_coercive_compare(lvalue) or \ + not self._allow_coercive_compare(rvalue): + return node + def _allow_coercive_compare(self, node): + if node.kind == 'primary' and node.opcode in ('null', 'true', 'false'): + return False + if node.kind == 'number' and not node.dval: + return False + if node.kind == 'string' and not node.atom: + return False + return True + +class default_not_at_end: + 'the default case is not at the end of the switch statement' + @lookat('default') + def _lint(self, node): + siblings = node.parent.kids + if node.node_index != len(siblings)-1: + return siblings[node.node_index+1] + +class duplicate_case_in_switch: + 'duplicate case in switch statement' + @lookat('case') + def _lint(self, node): + # Only look at previous siblings + siblings = node.parent.kids + siblings = siblings[:node.node_index] + # Compare values (first kid) + node_value = node.kids[0] + for sibling in siblings: + if sibling.kind == 'case': + sibling_value = sibling.kids[0] + if node_value.is_equivalent(sibling_value, True): + return node + +class missing_default_case: + 'missing default case in switch statement' + @lookat('switch') + def _lint(self, node): + value, cases = node.kids + for case in cases.kids: + if case.kind == 'default': + return + return node + +class with_statement: + 'with statement hides undeclared variables; use temporary variable instead' + @lookat('with') + def _lint(self, node): + return node + +class useless_comparison: + 'useless comparison; comparing identical expressions' + @lookat('eqop','relop') + def _lint(self, node): + lvalue, rvalue = node.kids + if lvalue.is_equivalent(rvalue): + return node + +class use_of_label: + 'use of label' + @lookat('colon:name') + def _lint(self, node): + return node + +class meaningless_block: + 'meaningless block; curly braces have no impact' + @lookat('lc') + def _lint(self, node): + if node.parent and node.parent.kind == 'lc': + return node + +class misplaced_regex: + 'regular expressions should be preceded by a left parenthesis, assignment, colon, or comma' + @lookat('object:regexp') + def _lint(self, node): + if node.parent.kind == 'name' and node.parent.opcode == 'setname': + return # Allow in var statements + if node.parent.kind == 'assign' and node.parent.opcode == 'nop': + return # Allow in assigns + if node.parent.kind == 'colon' and node.parent.parent.kind == 'rc': + return # Allow in object literals + if node.parent.kind == 'lp' and node.parent.opcode == 'call': + return # Allow in parameters + if node.parent.kind == 'dot' and node.parent.opcode == 'getprop': + return # Allow in /re/.property + if node.parent.kind == 'return': + return # Allow for return values + return node + +class assign_to_function_call: + 'assignment to a function call' + @lookat('assign') + def _lint(self, node): + if node.kids[0].kind == 'lp': + return node + +class ambiguous_else_stmt: + 'the else statement could be matched with one of multiple if statements (use curly braces to indicate intent' + @lookat('if') + def _lint(self, node): + # Only examine this node if it has an else statement. + condition, if_, else_ = node.kids + if not else_: + return + + tmp = node + while tmp: + # Curly braces always clarify if statements. + if tmp.kind == 'lc': + return + # Else is only ambiguous in the first branch of an if statement. + if tmp.parent.kind == 'if' and tmp.node_index == 1: + return else_ + tmp = tmp.parent + +class block_without_braces: + 'block statement without curly braces' + @lookat('if', 'while', 'do', 'for', 'with') + def _lint(self, node): + if node.kids[1].kind != 'lc': + return node.kids[1] + +class ambiguous_nested_stmt: + 'block statements containing block statements should use curly braces to resolve ambiguity' + _block_nodes = ('if', 'while', 'do', 'for', 'with') + @lookat(*_block_nodes) + def _lint(self, node): + # Ignore "else if" + if node.kind == 'if' and node.node_index == 2 and node.parent.kind == 'if': + return + + # If the parent is a block, it means a block statement + # was inside a block statement without clarifying curlies. + # (Otherwise, the node type would be 'lc'.) + if node.parent.kind in self._block_nodes: + return node + +class inc_dec_within_stmt: + 'increment (++) and decrement (--) operators used as part of greater statement' + @lookat('inc', 'dec') + def _lint(self, node): + if node.parent.kind == 'semi': + return None + + # Allow within the third part of the "for" + tmp = node + while tmp and tmp.parent and tmp.parent.kind == 'comma': + tmp = tmp.parent + if tmp and tmp.node_index == 2 and \ + tmp.parent.kind == 'reserved' and \ + tmp.parent.parent.kind == 'for': + return None + + return node + def _is_for_ternary_stmt(self, node, branch=None): + if node.parent and node.parent.kind == 'comma': + return _is_for_ternary_stmt(node.parent, branch) + return node.node_index == branch and \ + node.parent and \ + node.parent.kind == 'reserved' and \ + node.parent.parent.kind == 'for' + +class comma_separated_stmts: + 'multiple statements separated by commas (use semicolons?)' + @lookat('comma') + def _lint(self, node): + # Allow within the first and third part of "for(;;)" + if _get_branch_in_for(node) in (0, 2): + return + # This is an array + if node.parent.kind == 'rb': + return + return node + +class empty_statement: + 'empty statement or extra semicolon' + @lookat('semi') + def _semi(self, node): + if not node.kids[0]: + return node + @lookat('lc') + def _lc(self, node): + if node.kids: + return + # Ignore the outermost block. + if not node.parent: + return + # Some empty blocks are meaningful. + if node.parent.kind in ('catch', 'case', 'default', 'switch', 'function'): + return + return node + +class missing_break: + 'missing break statement' + @lookat('case', 'default') + def _lint(self, node): + # The last item is handled separately + if node.node_index == len(node.parent.kids)-1: + return + case_contents = node.kids[1] + assert case_contents.kind == 'lc' + # Ignore empty case statements + if not case_contents.kids: + return + if None in _get_exit_points(case_contents): + return node + +class missing_break_for_last_case: + 'missing break statement for last case in switch' + @lookat('case', 'default') + def _lint(self, node): + if node.node_index < len(node.parent.kids)-1: + return + case_contents = node.kids[1] + assert case_c... [truncated message content] |
From: <mat...@us...> - 2008-03-01 17:17:52
|
Revision: 146 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=146&view=rev Author: matthiasmiller Date: 2008-03-01 09:17:49 -0800 (Sat, 01 Mar 2008) Log Message: ----------- copy in a fresh copy of SpiderMonkey Added Paths: ----------- trunk/spidermonkey/ Copied: trunk/spidermonkey (from rev 145, vendorsrc/Mozilla.org/js/current) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2008-03-01 17:11:12
|
Revision: 145 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=145&view=rev Author: matthiasmiller Date: 2008-03-01 09:11:09 -0800 (Sat, 01 Mar 2008) Log Message: ----------- move the C version of jsl into a branch Added Paths: ----------- branches/cjsl/ branches/cjsl/src/ Removed Paths: ------------- trunk/src/ Copied: branches/cjsl/src (from rev 144, trunk/src) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2008-03-01 17:04:39
|
Revision: 144 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=144&view=rev Author: matthiasmiller Date: 2008-03-01 09:04:35 -0800 (Sat, 01 Mar 2008) Log Message: ----------- Tag vendorsrc/Mozilla.org/js/current as vendorsrc/Mozilla.org/js/1.7.0. Added Paths: ----------- vendorsrc/Mozilla.org/js/1.7.0/ Copied: vendorsrc/Mozilla.org/js/1.7.0 (from rev 143, vendorsrc/Mozilla.org/js/current) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2008-03-01 17:04:27
|
Revision: 143 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=143&view=rev Author: matthiasmiller Date: 2008-03-01 09:04:20 -0800 (Sat, 01 Mar 2008) Log Message: ----------- Load js-1.7.0 into vendorsrc/Mozilla.org/js/current. Modified Paths: -------------- vendorsrc/Mozilla.org/js/current/src/jsapi.c vendorsrc/Mozilla.org/js/current/src/jsarray.c vendorsrc/Mozilla.org/js/current/src/jsemit.c vendorsrc/Mozilla.org/js/current/src/jsgc.c vendorsrc/Mozilla.org/js/current/src/jsobj.c vendorsrc/Mozilla.org/js/current/src/jsopcode.c vendorsrc/Mozilla.org/js/current/src/jsscript.c vendorsrc/Mozilla.org/js/current/src/jsxdrapi.h Added Paths: ----------- vendorsrc/Mozilla.org/js/current/README vendorsrc/Mozilla.org/js/current/jsd/ vendorsrc/Mozilla.org/js/current/jsd/.cvsignore vendorsrc/Mozilla.org/js/current/jsd/CVS/ vendorsrc/Mozilla.org/js/current/jsd/CVS/Entries vendorsrc/Mozilla.org/js/current/jsd/CVS/Entries.Log vendorsrc/Mozilla.org/js/current/jsd/CVS/Repository vendorsrc/Mozilla.org/js/current/jsd/CVS/Root vendorsrc/Mozilla.org/js/current/jsd/CVS/Tag vendorsrc/Mozilla.org/js/current/jsd/Makefile.in vendorsrc/Mozilla.org/js/current/jsd/README vendorsrc/Mozilla.org/js/current/jsd/idl/ vendorsrc/Mozilla.org/js/current/jsd/idl/.cvsignore vendorsrc/Mozilla.org/js/current/jsd/idl/CVS/ vendorsrc/Mozilla.org/js/current/jsd/idl/CVS/Entries vendorsrc/Mozilla.org/js/current/jsd/idl/CVS/Repository vendorsrc/Mozilla.org/js/current/jsd/idl/CVS/Root vendorsrc/Mozilla.org/js/current/jsd/idl/CVS/Tag vendorsrc/Mozilla.org/js/current/jsd/idl/Makefile.in vendorsrc/Mozilla.org/js/current/jsd/idl/jsdIDebuggerService.idl vendorsrc/Mozilla.org/js/current/jsd/jsd.h vendorsrc/Mozilla.org/js/current/jsd/jsd.mak vendorsrc/Mozilla.org/js/current/jsd/jsd.pkg vendorsrc/Mozilla.org/js/current/jsd/jsd1640.def vendorsrc/Mozilla.org/js/current/jsd/jsd1640.rc vendorsrc/Mozilla.org/js/current/jsd/jsd3240.rc vendorsrc/Mozilla.org/js/current/jsd/jsd_atom.c vendorsrc/Mozilla.org/js/current/jsd/jsd_high.c vendorsrc/Mozilla.org/js/current/jsd/jsd_hook.c vendorsrc/Mozilla.org/js/current/jsd/jsd_java.c vendorsrc/Mozilla.org/js/current/jsd/jsd_lock.c vendorsrc/Mozilla.org/js/current/jsd/jsd_lock.h vendorsrc/Mozilla.org/js/current/jsd/jsd_obj.c vendorsrc/Mozilla.org/js/current/jsd/jsd_scpt.c vendorsrc/Mozilla.org/js/current/jsd/jsd_stak.c vendorsrc/Mozilla.org/js/current/jsd/jsd_step.c vendorsrc/Mozilla.org/js/current/jsd/jsd_text.c vendorsrc/Mozilla.org/js/current/jsd/jsd_val.c vendorsrc/Mozilla.org/js/current/jsd/jsd_xpc.cpp vendorsrc/Mozilla.org/js/current/jsd/jsd_xpc.h vendorsrc/Mozilla.org/js/current/jsd/jsdebug.c vendorsrc/Mozilla.org/js/current/jsd/jsdebug.h vendorsrc/Mozilla.org/js/current/jsd/jsdshell.mak vendorsrc/Mozilla.org/js/current/jsd/jsdstubs.c vendorsrc/Mozilla.org/js/current/jsd/mkshell.bat vendorsrc/Mozilla.org/js/current/jsd/resource.h vendorsrc/Mozilla.org/js/current/src/CVS/ vendorsrc/Mozilla.org/js/current/src/CVS/Entries vendorsrc/Mozilla.org/js/current/src/CVS/Repository vendorsrc/Mozilla.org/js/current/src/CVS/Root vendorsrc/Mozilla.org/js/current/src/CVS/Tag vendorsrc/Mozilla.org/js/current/src/config/CVS/ vendorsrc/Mozilla.org/js/current/src/config/CVS/Entries vendorsrc/Mozilla.org/js/current/src/config/CVS/Repository vendorsrc/Mozilla.org/js/current/src/config/CVS/Root vendorsrc/Mozilla.org/js/current/src/config/CVS/Tag vendorsrc/Mozilla.org/js/current/src/editline/CVS/ vendorsrc/Mozilla.org/js/current/src/editline/CVS/Entries vendorsrc/Mozilla.org/js/current/src/editline/CVS/Repository vendorsrc/Mozilla.org/js/current/src/editline/CVS/Root vendorsrc/Mozilla.org/js/current/src/editline/CVS/Tag vendorsrc/Mozilla.org/js/current/src/fdlibm/CVS/ vendorsrc/Mozilla.org/js/current/src/fdlibm/CVS/Entries vendorsrc/Mozilla.org/js/current/src/fdlibm/CVS/Repository vendorsrc/Mozilla.org/js/current/src/fdlibm/CVS/Root vendorsrc/Mozilla.org/js/current/src/fdlibm/CVS/Tag vendorsrc/Mozilla.org/js/current/src/liveconnect/ vendorsrc/Mozilla.org/js/current/src/liveconnect/.cvsignore vendorsrc/Mozilla.org/js/current/src/liveconnect/CVS/ vendorsrc/Mozilla.org/js/current/src/liveconnect/CVS/Entries vendorsrc/Mozilla.org/js/current/src/liveconnect/CVS/Repository vendorsrc/Mozilla.org/js/current/src/liveconnect/CVS/Root vendorsrc/Mozilla.org/js/current/src/liveconnect/CVS/Tag vendorsrc/Mozilla.org/js/current/src/liveconnect/LiveConnect.dsp vendorsrc/Mozilla.org/js/current/src/liveconnect/LiveConnectShell.dsp vendorsrc/Mozilla.org/js/current/src/liveconnect/LiveConnectShell.dsw vendorsrc/Mozilla.org/js/current/src/liveconnect/Makefile.in vendorsrc/Mozilla.org/js/current/src/liveconnect/Makefile.ref vendorsrc/Mozilla.org/js/current/src/liveconnect/README.html vendorsrc/Mozilla.org/js/current/src/liveconnect/_jni/ vendorsrc/Mozilla.org/js/current/src/liveconnect/_jni/CVS/ vendorsrc/Mozilla.org/js/current/src/liveconnect/_jni/CVS/Entries vendorsrc/Mozilla.org/js/current/src/liveconnect/_jni/CVS/Repository vendorsrc/Mozilla.org/js/current/src/liveconnect/_jni/CVS/Root vendorsrc/Mozilla.org/js/current/src/liveconnect/_jni/CVS/Tag vendorsrc/Mozilla.org/js/current/src/liveconnect/_jni/netscape_javascript_JSException.h vendorsrc/Mozilla.org/js/current/src/liveconnect/_jni/netscape_javascript_JSObject.h vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/ vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/.cvsignore vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/CVS/ vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/CVS/Entries vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/CVS/Repository vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/CVS/Root vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/CVS/Tag vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/Makefile.in vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/Makefile.ref vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/netscape/ vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/netscape/CVS/ vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/netscape/CVS/Entries vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/netscape/CVS/Repository vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/netscape/CVS/Root vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/netscape/CVS/Tag vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/netscape/Makefile.ref vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/netscape/javascript/ vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/netscape/javascript/CVS/ vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/netscape/javascript/CVS/Entries vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/netscape/javascript/CVS/Repository vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/netscape/javascript/CVS/Root vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/netscape/javascript/CVS/Tag vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/netscape/javascript/JSException.java vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/netscape/javascript/JSObject.java vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/netscape/javascript/JSProxy.java vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/netscape/javascript/JSRunnable.java vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/netscape/javascript/JSUtil.java vendorsrc/Mozilla.org/js/current/src/liveconnect/classes/netscape/javascript/Makefile.ref vendorsrc/Mozilla.org/js/current/src/liveconnect/config/ vendorsrc/Mozilla.org/js/current/src/liveconnect/config/AIX4.1.mk vendorsrc/Mozilla.org/js/current/src/liveconnect/config/AIX4.2.mk vendorsrc/Mozilla.org/js/current/src/liveconnect/config/AIX4.3.mk vendorsrc/Mozilla.org/js/current/src/liveconnect/config/CVS/ vendorsrc/Mozilla.org/js/current/src/liveconnect/config/CVS/Entries vendorsrc/Mozilla.org/js/current/src/liveconnect/config/CVS/Repository vendorsrc/Mozilla.org/js/current/src/liveconnect/config/CVS/Root vendorsrc/Mozilla.org/js/current/src/liveconnect/config/CVS/Tag vendorsrc/Mozilla.org/js/current/src/liveconnect/config/HP-UXB.10.10.mk vendorsrc/Mozilla.org/js/current/src/liveconnect/config/HP-UXB.10.20.mk vendorsrc/Mozilla.org/js/current/src/liveconnect/config/HP-UXB.11.00.mk vendorsrc/Mozilla.org/js/current/src/liveconnect/config/IRIX6.2.mk vendorsrc/Mozilla.org/js/current/src/liveconnect/config/IRIX6.3.mk vendorsrc/Mozilla.org/js/current/src/liveconnect/config/IRIX6.5.mk vendorsrc/Mozilla.org/js/current/src/liveconnect/config/Linux_All.mk vendorsrc/Mozilla.org/js/current/src/liveconnect/config/OSF1V4.0.mk vendorsrc/Mozilla.org/js/current/src/liveconnect/config/OSF1V5.0.mk vendorsrc/Mozilla.org/js/current/src/liveconnect/config/SunOS5.5.1.mk vendorsrc/Mozilla.org/js/current/src/liveconnect/config/SunOS5.6.mk vendorsrc/Mozilla.org/js/current/src/liveconnect/config/SunOS5.7.mk vendorsrc/Mozilla.org/js/current/src/liveconnect/config/SunOS5.8.mk vendorsrc/Mozilla.org/js/current/src/liveconnect/config/WINNT4.0.mk vendorsrc/Mozilla.org/js/current/src/liveconnect/jsj.c vendorsrc/Mozilla.org/js/current/src/liveconnect/jsj.msg vendorsrc/Mozilla.org/js/current/src/liveconnect/jsj_JSObject.c vendorsrc/Mozilla.org/js/current/src/liveconnect/jsj_JavaArray.c vendorsrc/Mozilla.org/js/current/src/liveconnect/jsj_JavaClass.c vendorsrc/Mozilla.org/js/current/src/liveconnect/jsj_JavaMember.c vendorsrc/Mozilla.org/js/current/src/liveconnect/jsj_JavaObject.c vendorsrc/Mozilla.org/js/current/src/liveconnect/jsj_JavaPackage.c vendorsrc/Mozilla.org/js/current/src/liveconnect/jsj_array.c vendorsrc/Mozilla.org/js/current/src/liveconnect/jsj_class.c vendorsrc/Mozilla.org/js/current/src/liveconnect/jsj_convert.c vendorsrc/Mozilla.org/js/current/src/liveconnect/jsj_field.c vendorsrc/Mozilla.org/js/current/src/liveconnect/jsj_hash.c vendorsrc/Mozilla.org/js/current/src/liveconnect/jsj_hash.h vendorsrc/Mozilla.org/js/current/src/liveconnect/jsj_method.c vendorsrc/Mozilla.org/js/current/src/liveconnect/jsj_nodl.c vendorsrc/Mozilla.org/js/current/src/liveconnect/jsj_private.h vendorsrc/Mozilla.org/js/current/src/liveconnect/jsj_simpleapi.c vendorsrc/Mozilla.org/js/current/src/liveconnect/jsj_utils.c vendorsrc/Mozilla.org/js/current/src/liveconnect/jsjava.h vendorsrc/Mozilla.org/js/current/src/liveconnect/liveconnect.pkg vendorsrc/Mozilla.org/js/current/src/liveconnect/netscape_javascript_JSObject.h vendorsrc/Mozilla.org/js/current/src/liveconnect/nsCLiveconnect.cpp vendorsrc/Mozilla.org/js/current/src/liveconnect/nsCLiveconnect.h vendorsrc/Mozilla.org/js/current/src/liveconnect/nsCLiveconnectFactory.cpp vendorsrc/Mozilla.org/js/current/src/liveconnect/nsCLiveconnectFactory.h vendorsrc/Mozilla.org/js/current/src/liveconnect/nsILiveconnect.h vendorsrc/Mozilla.org/js/current/src/liveconnect/nsISecureLiveconnect.h vendorsrc/Mozilla.org/js/current/src/liveconnect/nsISecurityContext.h vendorsrc/Mozilla.org/js/current/src/liveconnect/win32.order Property Changed: ---------------- vendorsrc/Mozilla.org/js/current/src/config/Darwin1.3.mk vendorsrc/Mozilla.org/js/current/src/config/Darwin1.4.mk vendorsrc/Mozilla.org/js/current/src/config/Darwin5.2.mk vendorsrc/Mozilla.org/js/current/src/config/Mac_OS10.0.mk Added: vendorsrc/Mozilla.org/js/current/README =================================================================== --- vendorsrc/Mozilla.org/js/current/README (rev 0) +++ vendorsrc/Mozilla.org/js/current/README 2008-03-01 17:04:20 UTC (rev 143) @@ -0,0 +1,7 @@ +1. The latest release notes for SpiderMonkey can be found at: + + http://www.mozilla.org/js/spidermonkey/release-notes/ + + +2. js/jsd contains code for debugging support for the C-based JavaScript engine in js/src. + Property changes on: vendorsrc/Mozilla.org/js/current/README ___________________________________________________________________ Name: svn:eol-style + native Added: vendorsrc/Mozilla.org/js/current/jsd/.cvsignore =================================================================== --- vendorsrc/Mozilla.org/js/current/jsd/.cvsignore (rev 0) +++ vendorsrc/Mozilla.org/js/current/jsd/.cvsignore 2008-03-01 17:04:20 UTC (rev 143) @@ -0,0 +1 @@ +Makefile Property changes on: vendorsrc/Mozilla.org/js/current/jsd/.cvsignore ___________________________________________________________________ Name: svn:eol-style + native Added: vendorsrc/Mozilla.org/js/current/jsd/CVS/Entries =================================================================== --- vendorsrc/Mozilla.org/js/current/jsd/CVS/Entries (rev 0) +++ vendorsrc/Mozilla.org/js/current/jsd/CVS/Entries 2008-03-01 17:04:20 UTC (rev 143) @@ -0,0 +1,30 @@ +/.cvsignore/1.1/Sat Dec 5 09:02:27 1998//TJS_170 +/Makefile.in/1.26/Tue Apr 5 18:26:06 2005//TJS_170 +/README/3.3/Sun Sep 30 08:52:04 2001//TJS_170 +/jsd.h/3.19.2.1/Fri Jul 7 02:12:01 2006//TJS_170 +/jsd.mak/3.2/Thu Apr 3 22:42:02 2003//TJS_170 +/jsd.pkg/1.2/Wed Jan 7 01:21:43 2004//TJS_170 +/jsd1640.def/3.3/Sun Apr 18 21:57:30 2004//TJS_170 +/jsd1640.rc/3.6/Sun Apr 18 21:57:30 2004//TJS_170 +/jsd3240.rc/3.6/Sun Apr 18 21:57:30 2004//TJS_170 +/jsd_atom.c/3.6/Sun Apr 18 21:57:30 2004//TJS_170 +/jsd_high.c/3.12/Sun Apr 18 21:57:30 2004//TJS_170 +/jsd_hook.c/3.10/Sun Apr 18 21:57:30 2004//TJS_170 +/jsd_java.c/3.8/Thu Jul 7 22:35:38 2005//TJS_170 +/jsd_lock.c/3.8/Sun Apr 18 21:57:30 2004//TJS_170 +/jsd_lock.h/3.7/Sun Apr 18 21:57:30 2004//TJS_170 +/jsd_obj.c/3.8/Sun Apr 18 21:57:30 2004//TJS_170 +/jsd_scpt.c/3.13/Tue Aug 2 15:53:59 2005//TJS_170 +/jsd_stak.c/3.21.20.1/Fri Aug 25 14:02:55 2006//TJS_170 +/jsd_step.c/3.16/Tue Aug 2 18:46:14 2005//TJS_170 +/jsd_text.c/3.8/Sun Apr 18 21:57:30 2004//TJS_170 +/jsd_val.c/3.9.28.1/Fri Jul 7 02:12:01 2006//TJS_170 +/jsd_xpc.cpp/1.72.2.1/Tue Jun 20 15:54:58 2006//TJS_170 +/jsd_xpc.h/1.21/Sun Apr 18 21:57:31 2004//TJS_170 +/jsdebug.c/3.15/Tue Aug 2 15:53:59 2005//TJS_170 +/jsdebug.h/3.19/Tue Aug 2 15:53:59 2005//TJS_170 +/jsdshell.mak/3.2/Thu Apr 3 22:42:02 2003//TJS_170 +/jsdstubs.c/3.8/Thu Jul 7 22:35:38 2005//TJS_170 +/mkshell.bat/3.1/Thu Nov 5 08:57:03 1998//TJS_170 +/resource.h/3.6/Sun Apr 18 21:57:31 2004//TJS_170 +D Property changes on: vendorsrc/Mozilla.org/js/current/jsd/CVS/Entries ___________________________________________________________________ Name: svn:eol-style + native Added: vendorsrc/Mozilla.org/js/current/jsd/CVS/Entries.Log =================================================================== --- vendorsrc/Mozilla.org/js/current/jsd/CVS/Entries.Log (rev 0) +++ vendorsrc/Mozilla.org/js/current/jsd/CVS/Entries.Log 2008-03-01 17:04:20 UTC (rev 143) @@ -0,0 +1,13 @@ +A D/classes//// +A D/corba//// +A D/idl//// +A D/java//// +A D/javawrap//// +A D/jsdb//// +A D/macbuild//// +R D/macbuild//// +R D/jsdb//// +R D/javawrap//// +R D/java//// +R D/corba//// +R D/classes//// Property changes on: vendorsrc/Mozilla.org/js/current/jsd/CVS/Entries.Log ___________________________________________________________________ Name: svn:eol-style + native Added: vendorsrc/Mozilla.org/js/current/jsd/CVS/Repository =================================================================== --- vendorsrc/Mozilla.org/js/current/jsd/CVS/Repository (rev 0) +++ vendorsrc/Mozilla.org/js/current/jsd/CVS/Repository 2008-03-01 17:04:20 UTC (rev 143) @@ -0,0 +1 @@ +mozilla/js/jsd Property changes on: vendorsrc/Mozilla.org/js/current/jsd/CVS/Repository ___________________________________________________________________ Name: svn:eol-style + native Added: vendorsrc/Mozilla.org/js/current/jsd/CVS/Root =================================================================== --- vendorsrc/Mozilla.org/js/current/jsd/CVS/Root (rev 0) +++ vendorsrc/Mozilla.org/js/current/jsd/CVS/Root 2008-03-01 17:04:20 UTC (rev 143) @@ -0,0 +1 @@ +:pserver:ano...@cv...:/cvsroot Property changes on: vendorsrc/Mozilla.org/js/current/jsd/CVS/Root ___________________________________________________________________ Name: svn:eol-style + native Added: vendorsrc/Mozilla.org/js/current/jsd/CVS/Tag =================================================================== --- vendorsrc/Mozilla.org/js/current/jsd/CVS/Tag (rev 0) +++ vendorsrc/Mozilla.org/js/current/jsd/CVS/Tag 2008-03-01 17:04:20 UTC (rev 143) @@ -0,0 +1 @@ +NJS_170 Property changes on: vendorsrc/Mozilla.org/js/current/jsd/CVS/Tag ___________________________________________________________________ Name: svn:eol-style + native Added: vendorsrc/Mozilla.org/js/current/jsd/Makefile.in =================================================================== --- vendorsrc/Mozilla.org/js/current/jsd/Makefile.in (rev 0) +++ vendorsrc/Mozilla.org/js/current/jsd/Makefile.in 2008-03-01 17:04:20 UTC (rev 143) @@ -0,0 +1,110 @@ +#!gmake +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + + + +DEPTH = ../.. +topsrcdir = @top_srcdir@ +VPATH = @srcdir@ +srcdir = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = jsdebug +LIBRARY_NAME = jsd +FORCE_SHARED_LIB= 1 +ifeq ($(OS_ARCH)$(MOZ_ENABLE_LIBXUL),WINNT) +LIBRARY_NAME = jsd$(MOZ_BITS)$(VERSION_NUMBER) +endif + +# REQUIRES = java js +REQUIRES = js + +ifndef JSD_STANDALONE +REQUIRES += \ + string \ + xpcom \ + xpconnect \ + widget \ + dom \ + $(NULL) + +PACKAGE_FILE = jsd.pkg +endif + +EXTRA_DSO_LDOPTS += \ + $(MOZ_COMPONENT_LIBS) \ + $(MOZ_JS_LIBS) \ + $(NULL) +EXPORTS = jsdebug.h + +ifdef JS_THREADSAFE +DEFINES += -DJS_THREADSAFE +endif + +CSRCS = \ + jsdebug.c \ + jsd_atom.c \ + jsd_high.c \ + jsd_hook.c \ + jsd_lock.c \ + jsd_obj.c \ + jsd_scpt.c \ + jsd_stak.c \ + jsd_step.c \ + jsd_text.c \ + jsd_val.c \ + $(NULL) + +ifdef JSD_STANDALONE +DIRS += jsdb +else +DIRS += idl +CPPSRCS = jsd_xpc.cpp +IS_COMPONENT = 1 +LIBXUL_LIBRARY = 1 + +ifdef MOZ_ENABLE_LIBXUL +FORCE_SHARED_LIB= +MODULE_NAME = JavaScript_Debugger +endif +endif + +include $(topsrcdir)/config/rules.mk + +DEFINES += -DEXPORT_JSD_API Property changes on: vendorsrc/Mozilla.org/js/current/jsd/Makefile.in ___________________________________________________________________ Name: svn:eol-style + native Added: vendorsrc/Mozilla.org/js/current/jsd/README =================================================================== --- vendorsrc/Mozilla.org/js/current/jsd/README (rev 0) +++ vendorsrc/Mozilla.org/js/current/jsd/README 2008-03-01 17:04:20 UTC (rev 143) @@ -0,0 +1,6 @@ +js/jsd contains code for debugging support for the C-based JavaScript engine +in js/src. jsd_xpc.cpp provides an XPCOM binding for the library. + +js/jsd/jsdb is a console debugger using only native code (see README in that +directory.) This debugger is no longer being actively developed, though it +should work. Property changes on: vendorsrc/Mozilla.org/js/current/jsd/README ___________________________________________________________________ Name: svn:eol-style + native Added: vendorsrc/Mozilla.org/js/current/jsd/idl/.cvsignore =================================================================== --- vendorsrc/Mozilla.org/js/current/jsd/idl/.cvsignore (rev 0) +++ vendorsrc/Mozilla.org/js/current/jsd/idl/.cvsignore 2008-03-01 17:04:20 UTC (rev 143) @@ -0,0 +1 @@ +Makefile Property changes on: vendorsrc/Mozilla.org/js/current/jsd/idl/.cvsignore ___________________________________________________________________ Name: svn:eol-style + native Added: vendorsrc/Mozilla.org/js/current/jsd/idl/CVS/Entries =================================================================== --- vendorsrc/Mozilla.org/js/current/jsd/idl/CVS/Entries (rev 0) +++ vendorsrc/Mozilla.org/js/current/jsd/idl/CVS/Entries 2008-03-01 17:04:20 UTC (rev 143) @@ -0,0 +1,4 @@ +/.cvsignore/1.1/Fri Jul 6 02:36:12 2001//TJS_170 +/Makefile.in/1.3/Sun Apr 18 21:57:31 2004//TJS_170 +/jsdIDebuggerService.idl/1.32/Tue Aug 2 15:53:59 2005//TJS_170 +D Property changes on: vendorsrc/Mozilla.org/js/current/jsd/idl/CVS/Entries ___________________________________________________________________ Name: svn:eol-style + native Added: vendorsrc/Mozilla.org/js/current/jsd/idl/CVS/Repository =================================================================== --- vendorsrc/Mozilla.org/js/current/jsd/idl/CVS/Repository (rev 0) +++ vendorsrc/Mozilla.org/js/current/jsd/idl/CVS/Repository 2008-03-01 17:04:20 UTC (rev 143) @@ -0,0 +1 @@ +mozilla/js/jsd/idl Property changes on: vendorsrc/Mozilla.org/js/current/jsd/idl/CVS/Repository ___________________________________________________________________ Name: svn:eol-style + native Added: vendorsrc/Mozilla.org/js/current/jsd/idl/CVS/Root =================================================================== --- vendorsrc/Mozilla.org/js/current/jsd/idl/CVS/Root (rev 0) +++ vendorsrc/Mozilla.org/js/current/jsd/idl/CVS/Root 2008-03-01 17:04:20 UTC (rev 143) @@ -0,0 +1 @@ +:pserver:ano...@cv...:/cvsroot Property changes on: vendorsrc/Mozilla.org/js/current/jsd/idl/CVS/Root ___________________________________________________________________ Name: svn:eol-style + native Added: vendorsrc/Mozilla.org/js/current/jsd/idl/CVS/Tag =================================================================== --- vendorsrc/Mozilla.org/js/current/jsd/idl/CVS/Tag (rev 0) +++ vendorsrc/Mozilla.org/js/current/jsd/idl/CVS/Tag 2008-03-01 17:04:20 UTC (rev 143) @@ -0,0 +1 @@ +NJS_170 Property changes on: vendorsrc/Mozilla.org/js/current/jsd/idl/CVS/Tag ___________________________________________________________________ Name: svn:eol-style + native Added: vendorsrc/Mozilla.org/js/current/jsd/idl/Makefile.in =================================================================== --- vendorsrc/Mozilla.org/js/current/jsd/idl/Makefile.in (rev 0) +++ vendorsrc/Mozilla.org/js/current/jsd/idl/Makefile.in 2008-03-01 17:04:20 UTC (rev 143) @@ -0,0 +1,52 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = jsdebug +XPIDL_MODULE = jsdservice + +XPIDLSRCS = \ + jsdIDebuggerService.idl \ + $(NULL) + +include $(topsrcdir)/config/rules.mk Property changes on: vendorsrc/Mozilla.org/js/current/jsd/idl/Makefile.in ___________________________________________________________________ Name: svn:eol-style + native Added: vendorsrc/Mozilla.org/js/current/jsd/idl/jsdIDebuggerService.idl =================================================================== --- vendorsrc/Mozilla.org/js/current/jsd/idl/jsdIDebuggerService.idl (rev 0) +++ vendorsrc/Mozilla.org/js/current/jsd/idl/jsdIDebuggerService.idl 2008-03-01 17:04:20 UTC (rev 143) @@ -0,0 +1,1255 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Robert Ginda, <rg...@ne...> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsISupports.idl" + +%{ C++ +#include "jsdebug.h" +#include "nsAString.h" +%} + +[ptr] native JSDContext(JSDContext); +[ptr] native JSDObject(JSDObject); +[ptr] native JSDProperty(JSDProperty); +[ptr] native JSDScript(JSDScript); +[ptr] native JSDStackFrameInfo(JSDStackFrameInfo); +[ptr] native JSDThreadState(JSDThreadState); +[ptr] native JSDValue(JSDValue); +[ptr] native JSRuntime(JSRuntime); +[ptr] native JSContext(JSContext); + +/* interfaces we declare in this file */ +interface jsdIDebuggerService; +interface jsdIFilter; +interface jsdINestCallback; +interface jsdIFilterEnumerator; +interface jsdIContextEnumerator; +interface jsdIScriptEnumerator; +interface jsdIScriptHook; +interface jsdIErrorHook; +interface jsdIExecutionHook; +interface jsdICallHook; +interface jsdIEphemeral; +interface jsdIPC; +interface jsdIContext; +interface jsdIStackFrame; +interface jsdIScript; +interface jsdIValue; +interface jsdIObject; +interface jsdIProperty; + +/** + * Debugger service. It's not a good idea to have more than one active client of + * the debugger service. + */ +[scriptable, uuid(9dd9006a-4e5e-4a80-ac3d-007fb7335ca4)] +interface jsdIDebuggerService : nsISupports +{ + /** Internal use only. */ + [noscript] readonly attribute JSDContext JSDContext; + + /** + * Called when an error or warning occurs. + */ + attribute jsdIErrorHook errorHook; + /** + * Called when a jsdIScript is created or destroyed. + */ + attribute jsdIScriptHook scriptHook; + /** + * Called when the engine encounters a breakpoint. + */ + attribute jsdIExecutionHook breakpointHook; + /** + * Called when the engine encounters the debugger keyword. + */ + attribute jsdIExecutionHook debuggerHook; + /** + * Called when the errorHook returns false. + */ + attribute jsdIExecutionHook debugHook; + /** + * Called before the next PC is executed. + */ + attribute jsdIExecutionHook interruptHook; + /** + * Called when an exception is thrown (even if it will be caught.) + */ + attribute jsdIExecutionHook throwHook; + /** + * Called before and after a toplevel script is evaluated. + */ + attribute jsdICallHook topLevelHook; + /** + * Called before and after a function is called. + */ + attribute jsdICallHook functionHook; + + + /** + * VERSION_* values must be kept in sync with the JSVersion enumeration in + * jspubtd.h. + */ + + /** + * Possible values for jsdIScript::version and jsdIContext::version. + */ + const long VERSION_1_0 = 100; + const long VERSION_1_1 = 110; + const long VERSION_1_2 = 120; + const long VERSION_1_3 = 130; + const long VERSION_1_4 = 140; + const long VERSION_1_5 = 150; + const long VERSION_DEFAULT = 0; + const long VERSION_UNKNOWN = -1; + + /** + * These flags need to be kept in sync with the context flags defined in + * jsdebug.h + */ + + /** + * Link native frames in call stacks. + */ + const unsigned long ENABLE_NATIVE_FRAMES = 0x01; + /** + * Normally, if a script has a 0 in JSD_SCRIPT_PROFILE_BIT it is included in + * profile data, otherwise it is not profiled. Setting the + * PROFILE_WHEN_SET flag reverses this convention. + */ + const unsigned long PROFILE_WHEN_SET = 0x02; + /** + * Normally, when the script in the top frame of a thread state has a 1 in + * JSD_SCRIPT_DEBUG_BIT, the execution hook is ignored. Setting the + * DEBUG_WHEN_SET flag reverses this convention. + */ + const unsigned long DEBUG_WHEN_SET = 0x04; + /** + * When this flag is set the internal call hook will collect profile data. + */ + const unsigned long COLLECT_PROFILE_DATA = 0x08; + /** + * When this flag is set, stack frames that are disabled for debugging + * will not appear in the call stack chain. + */ + const unsigned long HIDE_DISABLED_FRAMES = 0x10; + /** + * When this flag is set, the debugger will only check the + * JSD_SCRIPT_DEBUG_BIT on the top (most recent) stack frame. This + * makes it possible to stop in an enabled frame which was called from + * a stack that contains a disabled frame. + * + * When this flag is *not* set, any stack that contains a disabled frame + * will not be debugged (the execution hook will not be invoked.) + * + * This only applies when the reason for calling the hook would have + * been TYPE_INTERRUPTED or TYPE_THROW. TYPE_BREAKPOINT, + * TYPE_DEBUG_REQUESTED, and TYPE_DEBUGGER_KEYWORD always stop, regardless + * of this setting, as long as the top frame is not disabled. + * + * If HIDE_DISABLED_FRAMES is set, this is effectively set as well. + */ + const unsigned long MASK_TOP_FRAME_ONLY = 0x20; + /** + * When this flag is set, object creation will not be tracked. This will + * reduce the performance price you pay by enabling the debugger. + */ + const unsigned long DISABLE_OBJECT_TRACE = 0x40; + + /** + * Debugger service flags. + */ + attribute unsigned long flags; + + /** + * Major version number of implementation. + */ + readonly attribute unsigned long implementationMajor; + /** + * Minor version number of implementation. + */ + readonly attribute unsigned long implementationMinor; + /** + * Free form string identifier for implementation. + */ + readonly attribute string implementationString; + + /** + * |true| if the debugger should register an app-start observer in order + * to begin collecting debug information when mozilla is launched. + */ + attribute boolean initAtStartup; + + /** + * |true| if the debugger service has been turned on. This does not + * necessarily mean another app is actively using the service, as the + * autostart pref may have turned the service on. + */ + readonly attribute boolean isOn; + + /** + * Turn on the debugger. This function should only be called from JavaScript + * code. The debugger will be enabled on the runtime the call is made on, + * as determined by nsIXPCNativeCallContext. + */ + void on (); + /** + * Turn on the debugger for a given runtime. + * + * @param rt The runtime you want to debug. You cannot turn the debugger + * on for multiple runtimes. + */ + [noscript] void onForRuntime (in JSRuntime rt); + /** + * Turn the debugger off. This will invalidate all of your jsdIEphemeral + * derived objects, and clear all of your breakpoints. In theory you + * should be able to turn the debugger back on at some later time without + * any problems. + */ + void off (); + + /** + * Peek at the current pause depth of the debugger. + * + * @return depth Number of pause() calls still waiting to be unPause()d. + */ + readonly attribute unsigned long pauseDepth; + /** + * Temporarily disable the debugger. Hooks will not be called while the + * debugger is paused. Multiple calls to pause will increase the "pause + * depth", and equal number of unPause calles must be made to resume + * normal debugging. + * + * @return depth Number of times pause has been called since the debugger + * has been unpaused. + */ + unsigned long pause(); + /** + * Undo a pause. + * + * @return depth The number of remaining pending pause calls. + */ + unsigned long unPause(); + + /** + * Force the engine to perform garbage collection. + */ + void GC(); + + /** + * Clear profile data for all scripts. + */ + void clearProfileData(); + + /** + * Adds an execution hook filter. These filters are consulted each time one + * of the jsdIExecutionHooks is about to be called. Filters are matched in + * a first in, first compared fashion. The first filter to match determines + * whether or not the hook is called. Use swapFilter to reorder existing + * filters, and removeFilter to remove them. + * + * If |filter| is already present this method throws NS_ERROR_INVALID_ARG. + * + * @param filter Object representing the filter to add. + * @param after Insert |filter| after this one. Pass null to insert at + * the beginning. + */ + void insertFilter (in jsdIFilter filter, in jsdIFilter after); + /** + * Same as insertFilter, except always add to the end of the list. + */ + void appendFilter (in jsdIFilter filter); + /** + * Remove a filter. + * + * If |filter| is not present this method throws NS_ERROR_INVALID_ARG. + * + * @param filter Object representing the filter to remove. Must be the exact + * object passed to addFilter, not just a new object with the same + * properties. + */ + void removeFilter (in jsdIFilter filter); + /** + * Swap position of two filters. + * + * If |filter_a| is not present, this method throws NS_ERROR_INVALID_ARG. + * If |filter_b| is not present, filter_a is replaced by filter_b. + * If |filter_a| == |filter_b|, then filter is refreshed. + */ + void swapFilters (in jsdIFilter filter_a, in jsdIFilter filter_b); + /** + * Enumerate registered filters. This routine refreshes each filter before + * passing them on to the enumeration function. Calling this with a null + * |enumerator| is equivalent to jsdIService::refreshFilters. + * + * @param enumerator jsdIFilterEnumerator instance to be called back for the + * enumeration. + */ + void enumerateFilters (in jsdIFilterEnumerator enumerator); + /** + * Force the debugger to resync its internal filter cache with the + * actual values in the jsdIFilter objects. To refresh a single filter + * use jsdIService::swapFilters. This method is equivalent to + * jsdIService::enumerateFilters with a null enumerator. + */ + void refreshFilters (); + /** + * Clear the list of filters. + */ + void clearFilters(); + + /** + * Enumerate all known contexts. + */ + void enumerateContexts (in jsdIContextEnumerator enumerator); + + /** + * Enumerate all scripts the debugger knows about. Any scripts created + * before you turned the debugger on, or after turning the debugger off + * will not be available unless the autostart perf is set. + * + * @param enumerator jsdIScriptEnumerator instance to be called back for + * the enumeration. + */ + void enumerateScripts (in jsdIScriptEnumerator enumerator); + /** + * Clear all breakpoints in all scripts. + */ + void clearAllBreakpoints (); + + /** + * When called from JavaScript, this method returns the jsdIValue wrapper + * for the given value. If a wrapper does not exist one will be created. + * When called from another language this method returns an xpconnect + * defined error code. + */ + jsdIValue wrapValue (/*in jsvalue value*/); + + /* XXX these two routines are candidates for refactoring. The only problem + * is that it is not clear where and how they should land. + */ + + /** + * Push a new network queue, and enter a new UI event loop. + * @param callback jsdINestCallback instance to be called back after the + * network queue has been pushed, but before the + * UI loop starts. + * @return depth returns the current number of times the event loop has been + * nested. your code can use it for sanity checks. + */ + unsigned long enterNestedEventLoop (in jsdINestCallback callback); + /** + * Exit the current nested event loop after the current iteration completes, + * and pop the network event queue. + * + * @return depth returns the current number of times the event loop has been + * nested. your code can use it for sanity checks. + */ + unsigned long exitNestedEventLoop (); +}; + +/* callback interfaces */ + +/** + * Object representing a pattern of global object and/or url the debugger should + * ignore. The debugger service itself will not modify properties of these + * objects. + */ +[scriptable, uuid(05593438-1b83-4517-864f-3cea3d37a266)] +interface jsdIFilter : nsISupports +{ + /** + * These two bytes of the flags attribute are reserved for interpretation + * by the jsdService implementation. You can do what you like with the + * remaining flags. + */ + const unsigned long FLAG_RESERVED_MASK = 0xFF; + /** + * Filters without this flag set are ignored. + */ + const unsigned long FLAG_ENABLED = 0x01; + /** + * Filters with this flag set are "pass" filters, they allow matching hooks + * to continue. Filters without this flag block matching hooks. + */ + const unsigned long FLAG_PASS = 0x02; + + /** + * FLAG_* values from above, OR'd together. + */ + attribute unsigned long flags; + + /** + * An nsISupports version of the global object to be filtered. A null glob + * matches all hooks. This attribute must be QI'able to the + * (non-scriptable) nsIScriptGlobalObject interface. + * + * The jsdIService caches this value internally, to if it changes you must + * swap the filter with itself using jsdIService::swapFilters. + */ + attribute nsISupports globalObject; + + /** + * String representing the url pattern to be filtered. Supports limited + * glob matching, at the beginning and end of the pattern only. For example, + * "chrome://venkman*" filters all urls that start with chrome/venkman, + * "*.cgi" filters all cgi's, and "http://myserver/utils.js" filters only + * the utils.js file on "myserver". A null urlPattern matches all urls. + * + * The jsdIService caches this value internally, to if it changes you must + * swap the filter with itself using jsdIService::swapFilters. + */ + attribute string urlPattern; + + /** + * Line number for the start of this filter. Line numbers are one based. + * Assigning a 0 to this attribute will tell the debugger to ignore the + * entire file. + */ + attribute unsigned long startLine; + + /** + * Line number for the end of this filter. Line numbers are one based. + * Assigning a 0 to this attribute will tell the debugger to ignore from + * |startLine| to the end of the file. + */ + attribute unsigned long endLine; +}; + +/** + * Pass an instance of one of these to jsdIDebuggerService::enterNestedEventLoop. + */ +[scriptable, uuid(88bea60f-9b5d-4b39-b08b-1c3a278782c6)] +interface jsdINestCallback : nsISupports +{ + /** + * This method will be called after pre-nesting work has completed, such + * as pushing the js context and network event queue, but before the new + * event loop starts. + */ + void onNest (); +}; + +/** + * Pass an instance of one of these to jsdIDebuggerService::enumerateFilters. + */ +[scriptable, uuid(54382875-ed12-4f90-9a63-1f0498d0a3f2)] +interface jsdIFilterEnumerator : nsISupports +{ + /** + * The enumerateFilter method will be called once for every filter the + * debugger knows about. + */ + void enumerateFilter (in jsdIFilter filter); +}; + +/** + * Pass an instance of one of these to jsdIDebuggerService::enumerateScripts. + */ +[scriptable, uuid(4c2f706e-1dd2-11b2-9ebc-85a06e948830)] +interface jsdIScriptEnumerator : nsISupports +{ + /** + * The enumerateScript method will be called once for every script the + * debugger knows about. + */ + void enumerateScript (in jsdIScript script); +}; + +/** + * Pass an instance of one of these to jsdIDebuggerService::enumerateContexts. + */ +[scriptable, uuid(912e342a-1dd2-11b2-b09f-cf3af38c15f0)] +interface jsdIContextEnumerator : nsISupports +{ + /** + * The enumerateContext method will be called once for every context + * currently in use. + */ + void enumerateContext (in jsdIContext executionContext); +}; + +/** + * Set jsdIDebuggerService::scriptHook to an instance of one of these. + */ +[scriptable, uuid(ae89a7e2-1dd1-11b2-8c2f-af82086291a5)] +interface jsdIScriptHook : nsISupports +{ + /** + * Called when scripts are created. + */ + void onScriptCreated (in jsdIScript script); + /** + * Called when the JavaScript engine destroys a script. The jsdIScript + * object passed in will already be invalidated. + */ + void onScriptDestroyed (in jsdIScript script); +}; + +/** + * Hook instances of this interface up to the + * jsdIDebuggerService::functionHook and toplevelHook properties. + */ +[scriptable, uuid(f102caf6-1dd1-11b2-bd43-c1dbacb95a98)] +interface jsdICallHook : nsISupports +{ + /** + * TYPE_* values must be kept in sync with the JSD_HOOK_* #defines + * in jsdebug.h. + */ + + /** + * Toplevel script is starting. + */ + const unsigned long TYPE_TOPLEVEL_START = 0; + /** + * Toplevel script has completed. + */ + const unsigned long TYPE_TOPLEVEL_END = 1; + /** + * Function is being called. + */ + const unsigned long TYPE_FUNCTION_CALL = 2; + /** + * Function is returning. + */ + const unsigned long TYPE_FUNCTION_RETURN = 3; + + /** + * Called before the JavaScript engine executes a top level script or calls + * a function. + */ + void onCall (in jsdIStackFrame frame, in unsigned long type); +}; + +[scriptable, uuid(b7dd3c1c-1dd1-11b2-83eb-8a857d199e0f)] +interface jsdIErrorHook : nsISupports +{ + /** + * REPORT_* values must be kept in sync with JSREPORT_* #defines in + * jsapi.h + */ + + /** + * Report is an error. + */ + const unsigned long REPORT_ERROR = 0x00; + /** + * Report is only a warning. + */ + const unsigned long REPORT_WARNING = 0x01; + /** + * Report represents an uncaught exception. + */ + const unsigned long REPORT_EXCEPTION = 0x02; + /** + * Report is due to strict mode. + */ + const unsigned long REPORT_STRICT = 0x04; + + /** + * Called when the JavaScript engine encounters an error. Return |true| + * to pass the error along, |false| to invoke the debugHook. + */ + boolean onError (in string message, in string fileName, + in unsigned long line, in unsigned long pos, + in unsigned long flags, in unsigned long errnum, + in jsdIValue exc); +}; + +/** + * Hook instances of this interface up to the + * jsdIDebuggerService::breakpointHook, debuggerHook, errorHook, interruptHook, + * and throwHook properties. + */ +[scriptable, uuid(9a7b6ad0-1dd1-11b2-a789-fcfae96356a2)] +interface jsdIExecutionHook : nsISupports +{ + /** + * TYPE_* values must be kept in sync with JSD_HOOK_* #defines in jsdebug.h. + */ + + /** + * Execution stopped because we're in single step mode. + */ + const unsigned long TYPE_INTERRUPTED = 0; + /** + * Execution stopped by a trap instruction (i.e. breakoint.) + */ + const unsigned long TYPE_BREAKPOINT = 1; + /** + * Error handler returned an "invoke debugger" value. + */ + const unsigned long TYPE_DEBUG_REQUESTED = 2; + /** + * Debugger keyword encountered. + */ + const unsigned long TYPE_DEBUGGER_KEYWORD = 3; + /** + * Exception was thrown. + */ + const unsigned long TYPE_THROW = 4; + + /** + * RETURN_* values must be kept in sync with JSD_HOOK_RETURN_* #defines in + * jsdebug.h. + */ + + /** + * Indicates unrecoverable error processing the hook. This will cause + * the script being executed to be aborted without raising a JavaScript + * exception. + */ + const unsigned long RETURN_HOOK_ERROR = 0; + /** + * Continue processing normally. This is the "do nothing special" return + * value for all hook types *except* TYPE_THROW. Returning RETURN_CONTINUE + * from TYPE_THROW cause the exception to be ignored. Return + * RETURN_CONTINUE_THROW to continue exception processing from TYPE_THROW + * hooks. + */ + const unsigned long RETURN_CONTINUE = 1; + /** + * Same effect as RETURN_HOOK_ERROR. + */ + const unsigned long RETURN_ABORT = 2; + /** + * Return the value of the |val| parameter. + */ + const unsigned long RETURN_RET_WITH_VAL = 3; + /** + * Throw the value of the |val| parameter. + */ + const unsigned long RETURN_THROW_WITH_VAL = 4; + /** + * Continue the current throw. + */ + const unsigned long RETURN_CONTINUE_THROW = 5; + + /** + * @param frame A jsdIStackFrame object representing the bottom stack frame. + * @param type One of the jsdIExecutionHook::TYPE_ constants. + * @param val in - Current exception (if any) when this method is called. + * out - If you return RETURN_THROW_WITH_VAL, value to be + * thrown. + * If you return RETURN_RET_WITH_VAL, value to return. + * All other return values, not significant. + * @retval One of the jsdIExecutionHook::RETURN_* constants. + */ + unsigned long onExecute (in jsdIStackFrame frame, + in unsigned long type, inout jsdIValue val); +}; + +/** + * Objects which inherit this interface may go away, with (jsdIScript) or + * without (all others) notification. These objects are generally wrappers + * around JSD structures that go away when you call jsdService::Off(). + */ +[scriptable, uuid(46f1e23e-1dd2-11b2-9ceb-8285f2e95e69)] +interface jsdIEphemeral : nsISupports +{ + /** + * |true| if this object is still valid. If not, many or all of the methods + * and/or properties of the inheritor may no longer be callable. + */ + readonly attribute boolean isValid; + /** + * Mark this instance as invalid. + */ + [noscript] void invalidate(); +}; + +/* handle objects */ + +/** + * Context object. Only context's which are also nsISupports objects can be + * reflected by this interface. + */ +[scriptable, uuid(a2dd25a4-1dd1-11b2-bda6-ed525acd4c35)] +interface jsdIContext : jsdIEphemeral +{ + /* Internal use only. */ + [noscript] readonly attribute JSContext JSContext; + + /** + * OPT_* values must be kept in sync with JSOPTION_* #defines in jsapi.h. + */ + + /** + * Strict mode is on. + */ + const long OPT_STRICT = 0x01; + /** + * Warnings reported as errors. + */ + const long OPT_WERR = 0x02; + /** + * Makes eval() use the last object on its 'obj' param's scope chain as the + * ECMA 'variables object'. + */ + const long OPT_VAROBJFIX = 0x04; + /** + * Private data for this object is an nsISupports object. Attempting to + * alter this bit will result in an NS_ERROR_ILLEGAL_VALUE. + */ + const long OPT_ISUPPORTS = 0x08; + /** + * OPT_* values above, OR'd together. + */ + attribute unsigned long options; + + /** + * Last version set on this context. + * Scripts typically select this with the "language" attribute. + * See the VERSION_* consts on jsdIDebuggerService. + */ + attribute long version; + + /** + * Unique tag among all valid jsdIContext objects, useful as a hash key. + */ + readonly attribute unsigned long tag; + + /** + * Private data for this context, if it is an nsISupports, |null| otherwise. + */ + readonly attribute nsISupports privateData; + + /** + * Retrieve the underlying context wrapped by this jsdIContext. + */ + readonly attribute nsISupports wrappedContext; + + /** + * Top of the scope chain for this context. + */ + readonly attribute jsdIValue globalObject; + + /** + * |true| if this context should be allowed to run scripts, |false| + * otherwise. This attribute is only valid for contexts which implement + * nsIScriptContext. Setting or getting this attribute on any other + * context will throw a NS_ERROR_NO_INTERFACE exception. + */ + attribute boolean scriptsEnabled; +}; + +/** + * Stack frame objects. These are only valid inside the jsdIExecutionHook which + * gave it to you. After you return from that handler the bottom frame, and any + * frame you found attached through it, are invalidated via the jsdIEphemeral + * interface. Once a jsdIStackFrame has been invalidated all method and + * property accesses will throw a NS_ERROR_NOT_AVAILABLE exception. + */ +[scriptable, uuid(b6d50784-1dd1-11b2-a932-882246c6fe45)] +interface jsdIStackFrame : jsdIEphemeral +{ + /** Internal use only. */ + [noscript] readonly attribute JSDContext JSDContext; + /** Internal use only. */ + [noscript] readonly attribute JSDThreadState JSDThreadState; + /** Internal use only. */ + [noscript] readonly attribute JSDStackFrameInfo JSDStackFrameInfo; + + /** + * True if stack frame represents a native frame. + */ + readonly attribute boolean isNative; + /** + * True if stack frame represents a frame created as a result of a debugger + * evaluation. + */ + readonly attribute boolean isDebugger; + /** + * True if stack frame is constructing a new object. + */ + readonly attribute boolean isConstructing; + + /** + * Link to the caller's stack frame. + */ + readonly attribute jsdIStackFrame callingFrame; + /** + * Executon context. + */ + readonly attribute jsdIContext executionContext; + /** + * Function name executing in this stack frame. + */ + readonly attribute string functionName; + /** + * Script running in this stack frame, null for native frames. + */ + readonly attribute jsdIScript script; + /** + * Current program counter in this stack frame. + */ + readonly attribute unsigned long pc; + /** + * Current line number (using the script's pc to line map.) + */ + readonly attribute unsigned long line; + /** + * Function object running in this stack frame. + */ + readonly attribute jsdIValue callee; + /** + * Top object in the scope chain. + */ + readonly attribute jsdIValue scope; + /** + * |this| object for this stack frame. + */ + readonly attribute jsdIValue thisValue; + /** + * Evaluate arbitrary JavaScript in this stack frame. + * @param bytes Script to be evaluated. + * @param fileName Filename to compile this script under. This is the + * filename you'll see in error messages, etc. + * @param line Starting line number for this script. One based. + * @retval Result of evaluating the script. + */ + boolean eval (in AString bytes, in string fileName, + in unsigned long line, out jsdIValue result); + +}; + +/** + * Script object. In JavaScript engine terms, there's a single script for each + * function, and one for the top level script. + */ +[scriptable, uuid(a38f65ca-1dd1-11b2-95d5-ff2947e9c920)] +interface jsdIScript : jsdIEphemeral +{ + /** Internal use only. */ + [noscript] readonly attribute JSDContext JSDContext; + /** Internal use only. */ + [noscript] readonly attribute JSDScript JSDScript; + + /** + * Last version set on this context. + * Scripts typically select this with the "language" attribute. + * See the VERSION_* consts on jsdIDebuggerService. + */ + readonly attribute long version; + + /** + * Tag value guaranteed unique among jsdIScript objects. Useful as a + * hash key in script. + */ + readonly attribute unsigned long tag; + + /** + * FLAG_* values need to be kept in sync with JSD_SCRIPT_* #defines in + * jsdebug.h. + */ + + /** + * Determines whether or not to collect profile information for this + * script. The context flag FLAG_PROFILE_WHEN_SET decides the logic. + */ + const unsigned long FLAG_PROFILE = 0x01; + /** + * Determines whether or not to ignore breakpoints, etc. in this script. + * The context flag JSD_DEBUG_WHEN_SET decides the logic. + */ + const unsigned long FLAG_DEBUG = 0x02; + + /** + * FLAG_* attributes from above, OR'd together. + */ + attribute unsigned long flags; + + /** + * Filename given for this script when it was compiled. + * This data is copied from the underlying structure when the jsdIScript + * instance is created and is therefore available even after the script is + * invalidated. + */ + readonly attribute string fileName; + /** + * Function name for this script. "anonymous" for unnamed functions (or + * a function actually named anonymous), empty for top level scripts. + * This data is copied from the underlying structure when the jsdIScript + * instance is created and is therefore available even after the script is + * invalidated. + */ + readonly attribute string functionName; + /** + * Fetch the function object as a jsdIValue. + */ + readonly attribute jsdIValue functionObject; + /** + * Source code for this script, without function declaration. + */ + readonly attribute AString functionSource; + /** + * Line number in source file containing the first line of this script. + * This data is copied from the underlying structure when the jsdIScript + * instance is created and is therefore available even after the script is + * invalidated. + */ + readonly attribute unsigned long baseLineNumber; + /** + * Total number of lines in this script. + * This data is copied from the underlying structure when the jsdIScript + * instance is created and is therefore available even after the script is + * invalidated. + */ + readonly attribute unsigned long lineExtent; + + /** + * Number of times this script has been called. + */ + readonly attribute unsigned long callCount; + /** + * Number of times this script called itself, directly or indirectly. + */ + readonly attribute unsigned long maxRecurseDepth; + /** + * Shortest execution time recorded, in milliseconds. + */ + readonly attribute double minExecutionTime; + /** + * Longest execution time recorded, in milliseconds. + */ + readonly attribute double maxExecutionTime; + /** + * Total time spent in this function, in milliseconds. + */ + readonly attribute double totalExecutionTime; + /** + * Shortest execution time recorded, in milliseconds, excluding time spent + * in other called code. + */ + readonly attribute double minOwnExecutionTime; + /** + * Longest execution time recorded, in milliseconds, excluding time spent + * in other called code. + */ + readonly attribute double maxOwnExecutionTime; + /** + * Total time spent in this function, in milliseconds, excluding time spent + * in other called code. + */ + readonly attribute double totalOwnExecutionTime; + + /** + * Clear profile data for this script. + */ + void clearProfileData(); + ... [truncated message content] |