From 76680b0fc461b8cab77a11c822e8cc6a55a74563 Mon Sep 17 00:00:00 2001 From: Cody Peter Mello Date: Fri, 6 May 2016 16:17:18 -0700 Subject: [PATCH 01/59] Import tests from subversion repository --- test.py | 107 ++++++++++++++++++ tests/bugs/sf-2890901-unicode.js | 4 + .../bugs/sf-2891908-finally_without_catch.js | 8 ++ tests/conf/always_use_option_explicit.js | 4 + tests/conf/define.js | 8 ++ tests/conf/jscript_function_extensions-1.js | 7 ++ tests/conf/jscript_function_extensions-2.js | 8 ++ tests/conf/jscript_function_extensions-3.js | 27 +++++ tests/conf/jscript_function_extensions-4.js | 4 + tests/conf/legacy_control_comments.js | 8 ++ tests/control_comments/conf-version-2.js | 7 ++ tests/control_comments/conf-version.js | 4 + tests/control_comments/control_comments.js | 33 ++++++ tests/control_comments/cpp_style_comments.js | 9 ++ tests/control_comments/declare.js | 27 +++++ tests/control_comments/import-slashes.js | 5 + tests/control_comments/import.js | 5 + tests/control_comments/import2.js | 2 + tests/control_comments/invalid_fallthru.js | 13 +++ tests/control_comments/invalid_pass.js | 2 + .../control_comments/option_explicit-with.js | 12 ++ tests/control_comments/option_explicit.js | 64 +++++++++++ tests/errors/syntax_error.js | 4 + tests/errors/syntax_error_2.js | 9 ++ tests/errors/syntax_error_3.js | 8 ++ tests/errors/unterminated_comment.js | 8 ++ tests/html/e4x.html | 28 +++++ tests/html/include.ecmascript | 10 ++ tests/html/include.html | 21 ++++ tests/html/script_tag_in_js_comment.html | 12 ++ tests/html/script_tag_in_js_literal.html | 14 +++ tests/html/unsupported_version.html | 19 ++++ tests/path_resolution/asterisk.js | 2 + tests/path_resolution/file | 0 tests/path_resolution/is_a_dir.js | 4 + tests/path_resolution/is_a_dir_not_file.js | 4 + tests/path_resolution/is_a_file.js | 4 + tests/path_resolution/is_a_file_not_dir.js | 2 + tests/path_resolution/minus_process.js | 15 +++ tests/path_resolution/not_a_dir.js | 2 + tests/path_resolution/not_a_file.js | 2 + .../process/error-2.ecmascript | 3 + .../path_resolution/process/error.ecmascript | 3 + .../process/ignore-a.ecmascript | 3 + .../process/ignore-b.ecmascript | 3 + .../process/included.ecmascript | 3 + tests/path_resolution/question_mark.js | 2 + tests/run_tests.pl | 106 +++++++++++++++++ tests/warnings/ambiguous_else_stmt.js | 21 ++++ tests/warnings/ambiguous_nested_stmt.js | 66 +++++++++++ tests/warnings/anon_no_return_value.js | 26 +++++ tests/warnings/assign_to_function_call.js | 16 +++ tests/warnings/block_without_braces.js | 13 +++ tests/warnings/comma_separated_stmts.js | 17 +++ tests/warnings/comparison_type_conv.js | 44 +++++++ tests/warnings/default_not_at_end.js | 15 +++ tests/warnings/dup_option_explicit.js | 5 + tests/warnings/duplicate_case_in_switch.js | 62 ++++++++++ tests/warnings/duplicate_formal.js | 5 + tests/warnings/empty_statement.js | 29 +++++ tests/warnings/equal_as_assign.js | 7 ++ tests/warnings/identifier_hides_another.js | 43 +++++++ tests/warnings/inc_dec_within_stmt-ignore.js | 20 ++++ tests/warnings/inc_dec_within_stmt.js | 62 ++++++++++ tests/warnings/jsl_cc_not_understood.js | 5 + tests/warnings/leading_decimal_point.js | 7 ++ tests/warnings/legacy_cc_not_understood.js | 9 ++ tests/warnings/meaningless_block.js | 12 ++ tests/warnings/misplaced_regex.js | 26 +++++ tests/warnings/missing_break.js | 106 +++++++++++++++++ tests/warnings/missing_break_for_last_case.js | 19 ++++ tests/warnings/missing_default_case.js | 51 +++++++++ tests/warnings/missing_option_explicit.js | 5 + tests/warnings/missing_semicolon.js | 23 ++++ .../warnings/missing_semicolon_for_lambda.js | 43 +++++++ tests/warnings/multiple_plus_minus.js | 11 ++ tests/warnings/nested_comment.js | 6 + tests/warnings/no_return_value.js | 31 +++++ tests/warnings/octal_number.js | 5 + tests/warnings/parseint_missing_radix.js | 15 +++ tests/warnings/partial_option_explicit.js | 5 + tests/warnings/redeclared_var.js | 10 ++ tests/warnings/spidermonkey/bad_backref.js | 5 + .../warnings/spidermonkey/deprecated_usage.js | 11 ++ .../warnings/spidermonkey/invalid_backref.js | 5 + tests/warnings/spidermonkey/trailing_comma.js | 5 + tests/warnings/trailing_comma_in_array.js | 8 ++ tests/warnings/trailing_decimal_point.js | 7 ++ tests/warnings/unreachable_code.js | 64 +++++++++++ tests/warnings/unreachable_code_2.js | 33 ++++++ tests/warnings/unreferenced_identifier.js | 102 +++++++++++++++++ tests/warnings/use_of_label.js | 19 ++++ tests/warnings/useless_assign.js | 20 ++++ tests/warnings/useless_comparison.js | 55 +++++++++ tests/warnings/useless_quotes.js | 11 ++ tests/warnings/useless_void.js | 6 + tests/warnings/var_hides_arg.js | 7 ++ tests/warnings/want_assign_or_call.js | 30 +++++ tests/warnings/with_statement.js | 7 ++ 99 files changed, 1894 insertions(+) create mode 100755 test.py create mode 100644 tests/bugs/sf-2890901-unicode.js create mode 100644 tests/bugs/sf-2891908-finally_without_catch.js create mode 100644 tests/conf/always_use_option_explicit.js create mode 100644 tests/conf/define.js create mode 100644 tests/conf/jscript_function_extensions-1.js create mode 100644 tests/conf/jscript_function_extensions-2.js create mode 100644 tests/conf/jscript_function_extensions-3.js create mode 100644 tests/conf/jscript_function_extensions-4.js create mode 100644 tests/conf/legacy_control_comments.js create mode 100644 tests/control_comments/conf-version-2.js create mode 100644 tests/control_comments/conf-version.js create mode 100644 tests/control_comments/control_comments.js create mode 100644 tests/control_comments/cpp_style_comments.js create mode 100644 tests/control_comments/declare.js create mode 100644 tests/control_comments/import-slashes.js create mode 100644 tests/control_comments/import.js create mode 100644 tests/control_comments/import2.js create mode 100644 tests/control_comments/invalid_fallthru.js create mode 100644 tests/control_comments/invalid_pass.js create mode 100644 tests/control_comments/option_explicit-with.js create mode 100644 tests/control_comments/option_explicit.js create mode 100644 tests/errors/syntax_error.js create mode 100644 tests/errors/syntax_error_2.js create mode 100644 tests/errors/syntax_error_3.js create mode 100644 tests/errors/unterminated_comment.js create mode 100644 tests/html/e4x.html create mode 100644 tests/html/include.ecmascript create mode 100644 tests/html/include.html create mode 100644 tests/html/script_tag_in_js_comment.html create mode 100644 tests/html/script_tag_in_js_literal.html create mode 100644 tests/html/unsupported_version.html create mode 100644 tests/path_resolution/asterisk.js create mode 100644 tests/path_resolution/file create mode 100644 tests/path_resolution/is_a_dir.js create mode 100644 tests/path_resolution/is_a_dir_not_file.js create mode 100644 tests/path_resolution/is_a_file.js create mode 100644 tests/path_resolution/is_a_file_not_dir.js create mode 100644 tests/path_resolution/minus_process.js create mode 100644 tests/path_resolution/not_a_dir.js create mode 100644 tests/path_resolution/not_a_file.js create mode 100644 tests/path_resolution/process/error-2.ecmascript create mode 100644 tests/path_resolution/process/error.ecmascript create mode 100644 tests/path_resolution/process/ignore-a.ecmascript create mode 100644 tests/path_resolution/process/ignore-b.ecmascript create mode 100644 tests/path_resolution/process/included.ecmascript create mode 100644 tests/path_resolution/question_mark.js create mode 100755 tests/run_tests.pl create mode 100644 tests/warnings/ambiguous_else_stmt.js create mode 100644 tests/warnings/ambiguous_nested_stmt.js create mode 100644 tests/warnings/anon_no_return_value.js create mode 100644 tests/warnings/assign_to_function_call.js create mode 100644 tests/warnings/block_without_braces.js create mode 100644 tests/warnings/comma_separated_stmts.js create mode 100644 tests/warnings/comparison_type_conv.js create mode 100644 tests/warnings/default_not_at_end.js create mode 100644 tests/warnings/dup_option_explicit.js create mode 100644 tests/warnings/duplicate_case_in_switch.js create mode 100644 tests/warnings/duplicate_formal.js create mode 100644 tests/warnings/empty_statement.js create mode 100644 tests/warnings/equal_as_assign.js create mode 100644 tests/warnings/identifier_hides_another.js create mode 100644 tests/warnings/inc_dec_within_stmt-ignore.js create mode 100644 tests/warnings/inc_dec_within_stmt.js create mode 100644 tests/warnings/jsl_cc_not_understood.js create mode 100644 tests/warnings/leading_decimal_point.js create mode 100644 tests/warnings/legacy_cc_not_understood.js create mode 100644 tests/warnings/meaningless_block.js create mode 100644 tests/warnings/misplaced_regex.js create mode 100644 tests/warnings/missing_break.js create mode 100644 tests/warnings/missing_break_for_last_case.js create mode 100644 tests/warnings/missing_default_case.js create mode 100644 tests/warnings/missing_option_explicit.js create mode 100644 tests/warnings/missing_semicolon.js create mode 100644 tests/warnings/missing_semicolon_for_lambda.js create mode 100644 tests/warnings/multiple_plus_minus.js create mode 100644 tests/warnings/nested_comment.js create mode 100644 tests/warnings/no_return_value.js create mode 100644 tests/warnings/octal_number.js create mode 100644 tests/warnings/parseint_missing_radix.js create mode 100644 tests/warnings/partial_option_explicit.js create mode 100644 tests/warnings/redeclared_var.js create mode 100644 tests/warnings/spidermonkey/bad_backref.js create mode 100644 tests/warnings/spidermonkey/deprecated_usage.js create mode 100644 tests/warnings/spidermonkey/invalid_backref.js create mode 100644 tests/warnings/spidermonkey/trailing_comma.js create mode 100644 tests/warnings/trailing_comma_in_array.js create mode 100644 tests/warnings/trailing_decimal_point.js create mode 100644 tests/warnings/unreachable_code.js create mode 100644 tests/warnings/unreachable_code_2.js create mode 100644 tests/warnings/unreferenced_identifier.js create mode 100644 tests/warnings/use_of_label.js create mode 100644 tests/warnings/useless_assign.js create mode 100644 tests/warnings/useless_comparison.js create mode 100644 tests/warnings/useless_quotes.js create mode 100644 tests/warnings/useless_void.js create mode 100644 tests/warnings/var_hides_arg.js create mode 100644 tests/warnings/want_assign_or_call.js create mode 100644 tests/warnings/with_statement.js diff --git a/test.py b/test.py new file mode 100755 index 0000000..23a0432 --- /dev/null +++ b/test.py @@ -0,0 +1,107 @@ +#!/usr/bin/python +# vim: ts=4 sw=4 expandtab +import os +import re +import sys + +import javascriptlint.conf +import javascriptlint.lint + +_DEFAULT_CONF = """ +# This warning triggers a lot of warnings in many of the tests, so only enable +# it when specifically testing it. +-unreferenced_argument +-unreferenced_function +-unreferenced_variable +""" + +class TestError(Exception): + pass + +def _get_conf(script): + regexp = re.compile(r"/\*conf:([^*]*)\*/") + text = '\n'.join(regexp.findall(script)) + conf = javascriptlint.conf.Conf() + conf.loadtext(_DEFAULT_CONF) + conf.loadtext(text) + return conf + +def _get_expected_warnings(script): + "returns an array of tuples -- line, warning" + warnings = [] + + regexp = re.compile(r"/\*warning:([^*]*)\*/") + + lines = script.splitlines() + for i in range(0, len(lines)): + for warning in regexp.findall(lines[i]): + # TODO: implement these + unimpl_warnings = ('dup_option_explicit',) + if not warning in unimpl_warnings: + warnings.append((i, warning)) + return warnings + +def _testfile(path): + # Parse the script and find the expected warnings. + script = open(path).read() + expected_warnings = _get_expected_warnings(script) + unexpected_warnings = [] + conf = _get_conf(script) + + def lint_error(path, line, col, errname, errdesc): + warning = (line, errname) + + # Bad hack to fix line numbers on ambiguous else statements + # TODO: Fix tests. + if errname == 'ambiguous_else_stmt' and not warning in expected_warnings: + warning = (line-1, errname) + + if warning in expected_warnings: + expected_warnings.remove(warning) + else: + unexpected_warnings.append(warning) + + javascriptlint.lint.lint_files([path], lint_error, conf=conf) + + errors = [] + if expected_warnings: + errors.append('Expected warnings:') + for line, warning in expected_warnings: + errors.append('\tline %i: %s' % (line+1, warning)) + if unexpected_warnings: + errors.append('Unexpected warnings:') + for line, warning in unexpected_warnings: + errors.append('\tline %i: %s' % (line+1, warning)) + if errors: + raise TestError, '\n'.join(errors) + +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 main(): + haderrors = False + for file in _get_test_files(): + ext = os.path.splitext(file)[1] + if ext in ('.htm', '.html', '.js'): + try: + _testfile(file) + except TestError, error: + haderrors = True + print error + sys.exit(haderrors) + +if __name__ == '__main__': + main() + diff --git a/tests/bugs/sf-2890901-unicode.js b/tests/bugs/sf-2890901-unicode.js new file mode 100644 index 0000000..201143e --- /dev/null +++ b/tests/bugs/sf-2890901-unicode.js @@ -0,0 +1,4 @@ +function unicode() { + return 'รถ'; +} + diff --git a/tests/bugs/sf-2891908-finally_without_catch.js b/tests/bugs/sf-2891908-finally_without_catch.js new file mode 100644 index 0000000..a2ed744 --- /dev/null +++ b/tests/bugs/sf-2891908-finally_without_catch.js @@ -0,0 +1,8 @@ +function f() { + try { + /*jsl:pass*/ + } + finally { + /*jsl:pass*/ + } +} diff --git a/tests/conf/always_use_option_explicit.js b/tests/conf/always_use_option_explicit.js new file mode 100644 index 0000000..827bf7e --- /dev/null +++ b/tests/conf/always_use_option_explicit.js @@ -0,0 +1,4 @@ +/*conf:+always_use_option_explicit*/ +function always_use_option_explicit() { + x++; /*warning:undeclared_identifier*/ +} diff --git a/tests/conf/define.js b/tests/conf/define.js new file mode 100644 index 0000000..2d1850f --- /dev/null +++ b/tests/conf/define.js @@ -0,0 +1,8 @@ +/*conf:+define window*/ +/*jsl:option explicit*/ +function define() { + window.alert('http://www.javascriptlint.com/'); + + /* cannot use document, however */ + document.write('JavaScript Lint'); /*warning:undeclared_identifier*/ +} diff --git a/tests/conf/jscript_function_extensions-1.js b/tests/conf/jscript_function_extensions-1.js new file mode 100644 index 0000000..7290090 --- /dev/null +++ b/tests/conf/jscript_function_extensions-1.js @@ -0,0 +1,7 @@ +/*conf:-jscript_function_extensions*/ +function conf() { + this.settings = {}; +} +function conf.jscript_function_extensions(val) { /*warning:redeclared_var*//*warning:paren_before_formal*/ + this.settings.jscript_function_extensions = val; +} diff --git a/tests/conf/jscript_function_extensions-2.js b/tests/conf/jscript_function_extensions-2.js new file mode 100644 index 0000000..a5e6548 --- /dev/null +++ b/tests/conf/jscript_function_extensions-2.js @@ -0,0 +1,8 @@ +/*conf:-jscript_function_extensions*/ + +function conf() { + this.settings = {}; +} + +function conf::jscript_function_extensions(val) { /*warning:redeclared_var*//*warning:paren_before_formal*/ +} diff --git a/tests/conf/jscript_function_extensions-3.js b/tests/conf/jscript_function_extensions-3.js new file mode 100644 index 0000000..a72154c --- /dev/null +++ b/tests/conf/jscript_function_extensions-3.js @@ -0,0 +1,27 @@ +/*conf:+jscript_function_extensions*/ +function conf() { + this.settings = {}; +} + +function conf.jscript_function_extensions(val) { + this.settings.jscript_function_extensions = val; +} + +function conf.jscript_function_extensions(val) { + /* cannot warn about redeclaration */ + return val; +} + +function conf.jscript_function_extensions.can.have.infinite.depth() { +} + +function conf::jscript_function_extensions(val) { + this.val = val; +} + +function conf.jscript_function_extensions::onunload() { + this.val = null; +} + +function conf.jscript_function_extensions..ok(val) { /*warning:syntax_error*/ +} diff --git a/tests/conf/jscript_function_extensions-4.js b/tests/conf/jscript_function_extensions-4.js new file mode 100644 index 0000000..72dff27 --- /dev/null +++ b/tests/conf/jscript_function_extensions-4.js @@ -0,0 +1,4 @@ +/*conf:+jscript_function_extensions*/ + +function conf.jscript_function_extensions:onunload(val) { /*warning:syntax_error*/ +} diff --git a/tests/conf/legacy_control_comments.js b/tests/conf/legacy_control_comments.js new file mode 100644 index 0000000..adc975d --- /dev/null +++ b/tests/conf/legacy_control_comments.js @@ -0,0 +1,8 @@ +/*conf:-legacy_control_comments*/ + +/* Make sure that legacy control comments aren't respected */ +function legacy_control_comments() { + /*@ignore@*/ + ; /*warning:empty_statement*/ + /*@end@*/ +} diff --git a/tests/control_comments/conf-version-2.js b/tests/control_comments/conf-version-2.js new file mode 100644 index 0000000..242f9c8 --- /dev/null +++ b/tests/control_comments/conf-version-2.js @@ -0,0 +1,7 @@ +/*conf:+default-version text/javascript;version=1.7*/ +/*jsl:content-type text/javascript;version=1.6*/ + +/* Make sure that the control comment overrides the config file. */ +function default_version() { + yield true; /*warning:semi_before_stmnt*/ +} diff --git a/tests/control_comments/conf-version.js b/tests/control_comments/conf-version.js new file mode 100644 index 0000000..f15dddb --- /dev/null +++ b/tests/control_comments/conf-version.js @@ -0,0 +1,4 @@ +/*conf:+default-version text/javascript;version=1.7*/ +function default_version() { + yield true; +} diff --git a/tests/control_comments/control_comments.js b/tests/control_comments/control_comments.js new file mode 100644 index 0000000..93585a6 --- /dev/null +++ b/tests/control_comments/control_comments.js @@ -0,0 +1,33 @@ +/*jsl:option explicit*/ +function control_comments() { + /* "legal" - can do anything */ + /*jsl:ignore*/ + var a; + if (a); + var b = a = b+++a; + var a = b; + /*jsl:end*/ + /*@ignore@*/ + var a; + if (a); + var b = a = b+++a; + var a = b; + /*@end@*/ + + /* legal - case doesn't matter */ + /*Jsl:IGNORE*/ + asdf = asdf; + /*JSL:End*/ + + /* illegal - not ending anything */ + /*jsl:end*/ /*warning:mismatch_ctrl_comments*/ + + /* illegal - can't start twice */ + /*jsl:ignore*/ + /*jsl:ignore*/ /*warning:mismatch_ctrl_comments*/ + /*jsl:end*/ + + /* illegal - don't forget to end */ + /*jsl:ignore*/ /*warning:mismatch_ctrl_comments*/ +} + diff --git a/tests/control_comments/cpp_style_comments.js b/tests/control_comments/cpp_style_comments.js new file mode 100644 index 0000000..c8b2143 --- /dev/null +++ b/tests/control_comments/cpp_style_comments.js @@ -0,0 +1,9 @@ +/* Allow C++ style control comments. +https://sourceforge.net/tracker/?func=detail&aid=1534046&group_id=168518&atid=847185 +*/ +function control_comments() { + //jsl:declare undeclared_a + undeclared_a = 1; + undeclared_b = 2; /*warning:undeclared_identifier*/ +} + diff --git a/tests/control_comments/declare.js b/tests/control_comments/declare.js new file mode 100644 index 0000000..0dfa6e5 --- /dev/null +++ b/tests/control_comments/declare.js @@ -0,0 +1,27 @@ +/*jsl:option explicit*/ +/*conf:-identifier_hides_another*/ +function declare() { + window.alert('http://www.javascriptlint.com/'); + /*jsl:declare window*/ /*warning:redeclared_var*/ + + /* redeclaration only at local scope */ + var window; + var document; + /*jsl:declare document*//*warning:redeclared_var*/ +} + +var i = 10 /*warning:missing_semicolon*/ +/*jsl:declare sample*/ + +/* declare was scoped */ +window.alert('JavaScript Lint');/*warning:undeclared_identifier*/ + +document.write('JavaScript Lint'); + +/*jsl:declare document*/ /*warning:redeclared_var*/ +function document() +{ +} + +/*jsl:declare*//*warning:jsl_cc_not_understood*/ +/*jsl:declare variable?*//*warning:jsl_cc_not_understood*/ diff --git a/tests/control_comments/import-slashes.js b/tests/control_comments/import-slashes.js new file mode 100644 index 0000000..6948bf4 --- /dev/null +++ b/tests/control_comments/import-slashes.js @@ -0,0 +1,5 @@ +/*jsl:option explicit*/ + +/* Try importing using backslashes instead of forward slashes. */ +/*jsl:import ..\control_comments\import-slashes.js */ + diff --git a/tests/control_comments/import.js b/tests/control_comments/import.js new file mode 100644 index 0000000..06a6eb6 --- /dev/null +++ b/tests/control_comments/import.js @@ -0,0 +1,5 @@ +/*jsl:option explicit*/ +function cc_import() { + /*jsl:import import2.js */ + return cc_imported; +} diff --git a/tests/control_comments/import2.js b/tests/control_comments/import2.js new file mode 100644 index 0000000..07222cd --- /dev/null +++ b/tests/control_comments/import2.js @@ -0,0 +1,2 @@ +/*jsl:option explicit*/ +var cc_imported = true; diff --git a/tests/control_comments/invalid_fallthru.js b/tests/control_comments/invalid_fallthru.js new file mode 100644 index 0000000..33de673 --- /dev/null +++ b/tests/control_comments/invalid_fallthru.js @@ -0,0 +1,13 @@ +/*jsl:option explicit*/ +function invalid_fallthru() { + /* mistake - invalid use of fallthru */ + /*jsl:fallthru*/ /*warning:invalid_fallthru*/ + var i; + switch (i) { + /*jsl:fallthru*/ /*warning:invalid_fallthru*/ + case /*jsl:fallthru*/1: /*warning:invalid_fallthru*/ + break; + default /*jsl:fallthru*/: /*warning:invalid_fallthru*/ + break; + } +} diff --git a/tests/control_comments/invalid_pass.js b/tests/control_comments/invalid_pass.js new file mode 100644 index 0000000..9601663 --- /dev/null +++ b/tests/control_comments/invalid_pass.js @@ -0,0 +1,2 @@ +/*jsl:option explicit*/ +/*jsl:pass*/ /*warning:invalid_pass*/ diff --git a/tests/control_comments/option_explicit-with.js b/tests/control_comments/option_explicit-with.js new file mode 100644 index 0000000..a69e2fa --- /dev/null +++ b/tests/control_comments/option_explicit-with.js @@ -0,0 +1,12 @@ +/*jsl:option explicit*/ +function option_explicit() { + var o = {}; + + with (o) { /*warning:with_statement*/ + /* should not warn about undeclared identifier */ + some_variable = another_variable; + if (o) { + second_value = first_variable; + } + } +} diff --git a/tests/control_comments/option_explicit.js b/tests/control_comments/option_explicit.js new file mode 100644 index 0000000..bba2875 --- /dev/null +++ b/tests/control_comments/option_explicit.js @@ -0,0 +1,64 @@ +/*jsl:option explicit*/ +var g; +function option_explicit(parm) { + /* legal - j is declared */ + g = j; + var j; + + var s; + + /* legal - function referencing parameter in parent */ + var fn = function() { return parm; }; + + /* legal - function referening variable in parent */ + var fn2 = function() { return s; }; + + /* legal - defined below */ + var o = new Child(); + + /* legal - function referencing variable in grandparent */ + function Child() { + function Grandchild() { + if (parm) { + return s; + } + return null; + } + } + + /* legal - catch variable */ + try { + throw null; + } + catch (err) { + return err; + } + + /* legal - recursion */ + option_explicit(parm); + + /* legal - this is a property, not a variable */ + this.q = -1; + + /* legal - global */ + g++; + + /* legal - ignore undeclared identifier */ + /*jsl:ignore*/ + g = undefined_var; + /*jsl:end*/ + + /* illegal - undeclared global */ + z--; /*warning:undeclared_identifier*/ + + /* illegal - undeclared global */ + y(); /*warning:undeclared_identifier*/ + + /* illegal */ + x = 14; /*warning:undeclared_identifier*/ + + /* illegal */ + y(); /*warning:undeclared_identifier*/ + + return ""; +} diff --git a/tests/errors/syntax_error.js b/tests/errors/syntax_error.js new file mode 100644 index 0000000..3733fd1 --- /dev/null +++ b/tests/errors/syntax_error.js @@ -0,0 +1,4 @@ +function syntax_error() { + &; /*warning:syntax_error*/ +} + diff --git a/tests/errors/syntax_error_2.js b/tests/errors/syntax_error_2.js new file mode 100644 index 0000000..5bdcccf --- /dev/null +++ b/tests/errors/syntax_error_2.js @@ -0,0 +1,9 @@ +/* +This failed in cjsl but is working in pyjsl. +http://sourceforge.net/tracker/?func=detail&aid=2861271&group_id=168518&atid=847185 +*/ +function syntax_error_2() { + var o = {}; + o.for = 10; +} + diff --git a/tests/errors/syntax_error_3.js b/tests/errors/syntax_error_3.js new file mode 100644 index 0000000..43a6eeb --- /dev/null +++ b/tests/errors/syntax_error_3.js @@ -0,0 +1,8 @@ +/* +This failed in cjsl but is working in pyjsl. +https://sourceforge.net/tracker/?func=detail&aid=2146544&group_id=168518&atid=847185 +*/ +function syntax_error_3() { + return /^{([^}]*)}$/; +} + diff --git a/tests/errors/unterminated_comment.js b/tests/errors/unterminated_comment.js new file mode 100644 index 0000000..976fea5 --- /dev/null +++ b/tests/errors/unterminated_comment.js @@ -0,0 +1,8 @@ +function unterminated_comment() { + /* + This should not produce a syntax error + when ending a multiline-comment like this: + ////////////////////////////////////////////////////////*/ /*warning:nested_comment*/ + + return true; +} diff --git a/tests/html/e4x.html b/tests/html/e4x.html new file mode 100644 index 0000000..0a9ac69 --- /dev/null +++ b/tests/html/e4x.html @@ -0,0 +1,28 @@ + + + + JavaScript Lint Test Page + + + + + + + + diff --git a/tests/html/include.ecmascript b/tests/html/include.ecmascript new file mode 100644 index 0000000..ff323b8 --- /dev/null +++ b/tests/html/include.ecmascript @@ -0,0 +1,10 @@ +var global_var = 42; +function GlobalFromInclude() { + var value; + this.set = function(newvalue) { + value = newvalue; + }; + this.get = function() { + return value; + }; +} diff --git a/tests/html/include.html b/tests/html/include.html new file mode 100644 index 0000000..a1388ed --- /dev/null +++ b/tests/html/include.html @@ -0,0 +1,21 @@ + + + + JavaScript Lint Test Page + + + + + + + diff --git a/tests/html/script_tag_in_js_comment.html b/tests/html/script_tag_in_js_comment.html new file mode 100644 index 0000000..f4b07b7 --- /dev/null +++ b/tests/html/script_tag_in_js_comment.html @@ -0,0 +1,12 @@ + + + + JavaScript Lint Test Page + end tag */ /*warning:unterminated_comment*/ + //--> + + + + + diff --git a/tests/html/script_tag_in_js_literal.html b/tests/html/script_tag_in_js_literal.html new file mode 100644 index 0000000..66fdae5 --- /dev/null +++ b/tests/html/script_tag_in_js_literal.html @@ -0,0 +1,14 @@ + + + + JavaScript Lint Test Page + "; + window.alert(x); + //--> + + + + + diff --git a/tests/html/unsupported_version.html b/tests/html/unsupported_version.html new file mode 100644 index 0000000..f79d8d5 --- /dev/null +++ b/tests/html/unsupported_version.html @@ -0,0 +1,19 @@ + + + + JavaScript Lint Test Page + + + + + + + diff --git a/tests/path_resolution/asterisk.js b/tests/path_resolution/asterisk.js new file mode 100644 index 0000000..096ea60 --- /dev/null +++ b/tests/path_resolution/asterisk.js @@ -0,0 +1,2 @@ +/*conf:+process unknown_dir/a*b */ +/*conf_error:unable to resolve path: unknown_dir/a*b*/ diff --git a/tests/path_resolution/file b/tests/path_resolution/file new file mode 100644 index 0000000..e69de29 diff --git a/tests/path_resolution/is_a_dir.js b/tests/path_resolution/is_a_dir.js new file mode 100644 index 0000000..8e12ba7 --- /dev/null +++ b/tests/path_resolution/is_a_dir.js @@ -0,0 +1,4 @@ +/*conf:+process dir/ */ +function zero() { + return 0; +} diff --git a/tests/path_resolution/is_a_dir_not_file.js b/tests/path_resolution/is_a_dir_not_file.js new file mode 100644 index 0000000..f60d745 --- /dev/null +++ b/tests/path_resolution/is_a_dir_not_file.js @@ -0,0 +1,4 @@ +/*conf:+process dir*/ +function zero() { + return 0; +} diff --git a/tests/path_resolution/is_a_file.js b/tests/path_resolution/is_a_file.js new file mode 100644 index 0000000..ced6da1 --- /dev/null +++ b/tests/path_resolution/is_a_file.js @@ -0,0 +1,4 @@ +/*conf:+process file*/ +function zero() { + return 0; +} diff --git a/tests/path_resolution/is_a_file_not_dir.js b/tests/path_resolution/is_a_file_not_dir.js new file mode 100644 index 0000000..865d142 --- /dev/null +++ b/tests/path_resolution/is_a_file_not_dir.js @@ -0,0 +1,2 @@ +/*conf:+process file/ */ +/*conf_error:unable to resolve path: file/*/ /*warning:nested_comment*/ diff --git a/tests/path_resolution/minus_process.js b/tests/path_resolution/minus_process.js new file mode 100644 index 0000000..12716d2 --- /dev/null +++ b/tests/path_resolution/minus_process.js @@ -0,0 +1,15 @@ +/* try skipping some files */ +/*conf:+process process/i* */ +/*conf:-process process/ignore* */ + +/* try skipping a file that was never added */ +/*conf:-process process/error.ecmascript*/ + +/* try skipping a file that was added multiple times */ +/*conf:+process process/error-2.ecmascript*/ +/*conf:+process process/error-2.ecmascript*/ +/*conf:-process process/error-2.ecmascript*/ + +function zero() { + return 0; +} diff --git a/tests/path_resolution/not_a_dir.js b/tests/path_resolution/not_a_dir.js new file mode 100644 index 0000000..8a1f7fb --- /dev/null +++ b/tests/path_resolution/not_a_dir.js @@ -0,0 +1,2 @@ +/*conf:+process not_a_dir*/ +/*conf_error:unable to resolve path: not_a_dir*/ diff --git a/tests/path_resolution/not_a_file.js b/tests/path_resolution/not_a_file.js new file mode 100644 index 0000000..d3d3646 --- /dev/null +++ b/tests/path_resolution/not_a_file.js @@ -0,0 +1,2 @@ +/*conf:+process not_a_file*/ +/*conf_error:unable to resolve path: not_a_file*/ diff --git a/tests/path_resolution/process/error-2.ecmascript b/tests/path_resolution/process/error-2.ecmascript new file mode 100644 index 0000000..77de5d9 --- /dev/null +++ b/tests/path_resolution/process/error-2.ecmascript @@ -0,0 +1,3 @@ +function process() { + error! +} diff --git a/tests/path_resolution/process/error.ecmascript b/tests/path_resolution/process/error.ecmascript new file mode 100644 index 0000000..77de5d9 --- /dev/null +++ b/tests/path_resolution/process/error.ecmascript @@ -0,0 +1,3 @@ +function process() { + error! +} diff --git a/tests/path_resolution/process/ignore-a.ecmascript b/tests/path_resolution/process/ignore-a.ecmascript new file mode 100644 index 0000000..77de5d9 --- /dev/null +++ b/tests/path_resolution/process/ignore-a.ecmascript @@ -0,0 +1,3 @@ +function process() { + error! +} diff --git a/tests/path_resolution/process/ignore-b.ecmascript b/tests/path_resolution/process/ignore-b.ecmascript new file mode 100644 index 0000000..77de5d9 --- /dev/null +++ b/tests/path_resolution/process/ignore-b.ecmascript @@ -0,0 +1,3 @@ +function process() { + error! +} diff --git a/tests/path_resolution/process/included.ecmascript b/tests/path_resolution/process/included.ecmascript new file mode 100644 index 0000000..bb2d86f --- /dev/null +++ b/tests/path_resolution/process/included.ecmascript @@ -0,0 +1,3 @@ +function zero() { + return 0; +} diff --git a/tests/path_resolution/question_mark.js b/tests/path_resolution/question_mark.js new file mode 100644 index 0000000..5a7b001 --- /dev/null +++ b/tests/path_resolution/question_mark.js @@ -0,0 +1,2 @@ +/*conf:+process unknown_dir/a?b */ +/*conf_error:unable to resolve path: unknown_dir/a?b*/ diff --git a/tests/run_tests.pl b/tests/run_tests.pl new file mode 100755 index 0000000..cb35ec8 --- /dev/null +++ b/tests/run_tests.pl @@ -0,0 +1,106 @@ +#!/usr/bin/perl + +use strict; +use File::Find; +use File::Spec; +use FindBin; + +# require a path to jsl +# +if (scalar(@ARGV) != 1) { + die("Usage: run_tests.pl \n"); +} +my $jsl_path = File::Spec->rel2abs($ARGV[0]); +my $tests_path = $FindBin::Bin; + +my $num_tests = 0; +my $num_passed = 0; +sub TestFile { + /\.(js|htm|html)$/ or return; + my $filename = $_; + my $pretty_name = $File::Find::name; + + my $conf_file = ".jsl.conf"; + + # open the path being validated + open(FILE, $filename) or die("Could not open $filename: $!"); + my @contents = ; + + # look for special configuration directives + my @conf = grep(s/\/\*conf:(([^*]|(\*[^\/]))*)\*\//\1\n/g, @contents); + open(FILE, ">$conf_file") or die("Could not open configuration file $conf_file: $!"); + print FILE join("",@conf); + close FILE; + + my $this_passed = 1; + + # look for expected configuration error + my @all_conf_errors = grep(s/\/\*conf_error:(([^*]|(\*[^\/]))*)\*\//\1/g, @contents); + my $conf_error; + if (scalar(@all_conf_errors) > 1) { + print "Only one conf_error allowed per script."; + $this_passed = 0; + } + elsif (scalar(@all_conf_errors) == 1) { + $conf_error = $all_conf_errors[0]; + unless ($conf_error) { + print "Missing conf_error text."; + $this_passed = 0; + } + } + + # run the lint + print "Testing $pretty_name...\n"; + my $results = `$jsl_path --conf $conf_file --process $filename --nologo --nofilelisting --nocontext --nosummary -output-format __LINE__,__ERROR_NAME__`; + my $exit_code = $? >> 8; + unlink $conf_file; + die "Error executing $jsl_path" unless defined $results; + + if ($conf_error) { + unless ($exit_code == 2) { + print "Expected exit code: $exit_code\n"; + $this_passed = 0; + } + unless (index($results, "configuration error: $conf_error") > 0) { + print "Expected configuration error: $conf_error"; + print "Got configuration error: $results"; + $this_passed = 0; + } + } + elsif ($exit_code == 2) { + print "Usage or configuration error:\n$results"; + $this_passed = 0; + } + + foreach my $result (split("\n", $results)) { + my ($line, $error) = split(",", $result); + next unless $error; # for now, skip blank errors (such as inability to open file) + + # some warnings point beyond the end of the file + $line = scalar(@contents) if $line > scalar(@contents); + + unless ($contents[$line-1] =~ s/\/\*warning:$error\*\///) { + print "Error in $filename, line $line: $error\n"; + $this_passed = 0; + } + } + for (my $i = 1; $i <= scalar(@contents); $i++) { + if ($contents[$i-1] =~ /\/\*warning:([^*]*)\*\//) { + print "Error in $filename, line $i: no $1 warning\n"; + $this_passed = 0; + } + } + close(FILE); + + $num_tests++; + $num_passed++ if $this_passed; +} + +# locate all files in the test folder +# +my @dirs; +push(@dirs, $tests_path); +print "Searching $tests_path...\n"; +find( sub{TestFile}, '.'); + +print "Passed $num_passed of $num_tests tests\n"; diff --git a/tests/warnings/ambiguous_else_stmt.js b/tests/warnings/ambiguous_else_stmt.js new file mode 100644 index 0000000..4f91dda --- /dev/null +++ b/tests/warnings/ambiguous_else_stmt.js @@ -0,0 +1,21 @@ +/*jsl:option explicit*/ +function ambiguous_else_stmt() { + var i, j, y; + + if (i) + if (j) { /*warning:ambiguous_nested_stmt*/ + j++; + } + else if (j) { /*warning:ambiguous_else_stmt*/ + i--; + } + + if (j) + if (i) /*warning:ambiguous_nested_stmt*/ + for (;;) /*warning:ambiguous_nested_stmt*/ + while (j) /*warning:ambiguous_nested_stmt*/ + if (y) /*warning:ambiguous_nested_stmt*/ + y--; + else /*warning:ambiguous_else_stmt*/ + y++; +} diff --git a/tests/warnings/ambiguous_nested_stmt.js b/tests/warnings/ambiguous_nested_stmt.js new file mode 100644 index 0000000..5a2bbc8 --- /dev/null +++ b/tests/warnings/ambiguous_nested_stmt.js @@ -0,0 +1,66 @@ +/*jsl:option explicit*/ +function ambiguous_nested_stmt() { + var a, i, s; + a = new Array(1, 2, 3); + + /* legal: else if */ + if (s == "false") { + i = 0; + } + else if (s == "true") { + i = 1; + } + + /* if, else */ + if (true) + s = "A"; + else + s = "B"; + + /* skip with */ + + /* try, catch, finally always require braces */ + + /* do...while */ + do s += "."; + while (false); + + /* for */ + for (i = 0; i < 20; i += 1) + s += i; + + /* for...in */ + for (i in a) + s += a[i]; + + /* while */ + while (i > 0) + s += "~"; + + /* illegal */ + if (i) + if (s) { /*warning:ambiguous_nested_stmt*/ + i = s; + } + else { /*warning:ambiguous_else_stmt*/ + s = i; + } + + /* illegal */ + if (i) + while (s) { /*warning:ambiguous_nested_stmt*/ + i = s; + } + + /* illegal */ + if (i) + do { /*warning:ambiguous_nested_stmt*/ + i = s; + } while (s); + + /* illegal */ + if (i) + for (i = 0; i < 1; i++) { /*warning:ambiguous_nested_stmt*/ + i++; + } +} diff --git a/tests/warnings/anon_no_return_value.js b/tests/warnings/anon_no_return_value.js new file mode 100644 index 0000000..9715800 --- /dev/null +++ b/tests/warnings/anon_no_return_value.js @@ -0,0 +1,26 @@ +/*jsl:option explicit*/ +function anon_no_return_value() { + var error1 = function(b) { + if (b) + return true; + else + return; /*warning:anon_no_return_value*/ + }; + + var error2 = function(b) { + if (b) { + return; /*warning:anon_no_return_value*/ + } + else { + return ""; + } + }; + + + var correct = function(b) { + if (b) + return; + else + return; + }; +} diff --git a/tests/warnings/assign_to_function_call.js b/tests/warnings/assign_to_function_call.js new file mode 100644 index 0000000..189f429 --- /dev/null +++ b/tests/warnings/assign_to_function_call.js @@ -0,0 +1,16 @@ +/*jsl:option explicit*/ +function assign_to_function_call() { + var o; + var s; + + o = {}; + s = 'prop'; + + o.prop = []; + o['prop'] = []; + + o.getThis = function() { return this; }; + o.getThis() = {}; /*warning:assign_to_function_call*/ + + ((function(){return 0;})()) = []; /*warning:assign_to_function_call*/ +} diff --git a/tests/warnings/block_without_braces.js b/tests/warnings/block_without_braces.js new file mode 100644 index 0000000..d039b4a --- /dev/null +++ b/tests/warnings/block_without_braces.js @@ -0,0 +1,13 @@ +/*jsl:option explicit*/ +/*conf:+block_without_braces*/ +function block_without_braces() { + var i; + if (i) + i++; /*warning:block_without_braces*/ + + do i--; + while (i); /*warning:block_without_braces*/ + + for (i = 0; i < 10; i++) + i *= 2; /*warning:block_without_braces*/ +} diff --git a/tests/warnings/comma_separated_stmts.js b/tests/warnings/comma_separated_stmts.js new file mode 100644 index 0000000..a0d55ca --- /dev/null +++ b/tests/warnings/comma_separated_stmts.js @@ -0,0 +1,17 @@ +/*jsl:option explicit*/ +function comma_separated_stmts() { + var b, i, j; + + /* comma (legit) */ + for (i = 0, j = 0; i < 10; i += 2, j += 4) { + b = ((i + j) / 2 == i - j); + } + + /* comma (unclear) */ + for (i = 0; i < 10, j > 20; i++) { /*warning:comma_separated_stmts*/ + j = i; + } + + /* comma (unclear) */ + b = false, i = 0, j = 0; /*warning:comma_separated_stmts*/ +} diff --git a/tests/warnings/comparison_type_conv.js b/tests/warnings/comparison_type_conv.js new file mode 100644 index 0000000..b31642e --- /dev/null +++ b/tests/warnings/comparison_type_conv.js @@ -0,0 +1,44 @@ +/*jsl:option explicit*/ +function comparison_type_conv() { + var a, b, c; + + /* comparison against null */ + if (a == null || b < c) { /*warning:comparison_type_conv*/ + a = b; + } + if (a === null || b < c) { + a = b; + } + + /* comparison against zero */ + if (c > a && a + b == 0) { /*warning:comparison_type_conv*/ + c = -c; + } + if (c > a && a + b === 0) { + c = -c; + } + + /* comparison against blank string */ + if (a == "") { /*warning:comparison_type_conv*/ + b = c; + } + if (a === "") { + b = c; + } + + /* comparison against true */ + if (a == true) { /*warning:comparison_type_conv*/ + c = b; + } + if (a === true) { + c = b; + } + + /* comparison against false */ + if (a == false) { /*warning:comparison_type_conv*/ + c = a; + } + if (a === false) { + c = a; + } +} diff --git a/tests/warnings/default_not_at_end.js b/tests/warnings/default_not_at_end.js new file mode 100644 index 0000000..7e5a5a8 --- /dev/null +++ b/tests/warnings/default_not_at_end.js @@ -0,0 +1,15 @@ +/*jsl:option explicit*/ +function default_not_at_end() { + var i; + + /*default case at top*/ + switch (i) { + default: + i++; + break; + case 1: /*warning:default_not_at_end*/ + return 1; + } + + return 0; +} diff --git a/tests/warnings/dup_option_explicit.js b/tests/warnings/dup_option_explicit.js new file mode 100644 index 0000000..8d10d45 --- /dev/null +++ b/tests/warnings/dup_option_explicit.js @@ -0,0 +1,5 @@ +/*jsl:option explicit*/ +function dup_option_explicit() { + /*@option explicit@*/ /*warning:dup_option_explicit*/ + return null; +} diff --git a/tests/warnings/duplicate_case_in_switch.js b/tests/warnings/duplicate_case_in_switch.js new file mode 100644 index 0000000..0d89fe6 --- /dev/null +++ b/tests/warnings/duplicate_case_in_switch.js @@ -0,0 +1,62 @@ +/*jsl:option explicit*/ +function duplicate_case_in_switch() { + var i, o, s; + + switch (i) { + case i: + s += "..."; + break; + case -1: + s = ""; + break; + case duplicate_case_in_switch(): + s = "0"; + break; + case o.prop: + i = 4; + break; + case "\"str1'": + case "str2": + i = null; + break; + + /* mistake - duplicated */ + case i: /*warning:duplicate_case_in_switch*/ + s = "~"; + break; + + /* mistake - duplicated */ + case -1: /*warning:duplicate_case_in_switch*/ + s = "!"; + break; + + /* mistake - duplicated */ + case duplicate_case_in_switch(): /*warning:duplicate_case_in_switch*/ + s = ""; + break; + + /* mistake - duplicated */ + case o['prop']: /*warning:duplicate_case_in_switch*/ + s = i; + break; + + /* mistake - duplicated */ + case '"str1\'': /*warning:duplicate_case_in_switch*/ + s = 0; + break; + + /* ok - not duplicated */ + case 100000000: + case 100000001: + s = 1; + break; + + /* mistake - duplicated */ + case 100000000: /*warning:duplicate_case_in_switch*/ + s = -1; + break; + + default: + break; + } +} diff --git a/tests/warnings/duplicate_formal.js b/tests/warnings/duplicate_formal.js new file mode 100644 index 0000000..9b31467 --- /dev/null +++ b/tests/warnings/duplicate_formal.js @@ -0,0 +1,5 @@ +/*jsl:option explicit*/ +function duplicate_formal(duplicate, + duplicate) { /*warning:duplicate_formal*/ + return duplicate; +} diff --git a/tests/warnings/empty_statement.js b/tests/warnings/empty_statement.js new file mode 100644 index 0000000..4d7922d --- /dev/null +++ b/tests/warnings/empty_statement.js @@ -0,0 +1,29 @@ +/*jsl:option explicit*/ +function empty_statement() { + var i; + i = 0; + + /* empty statement within while; useless expression */ + while (false); /*warning:empty_statement*/ + while (false) /*jsl:pass*/; /*warning:invalid_pass*//*warning:empty_statement*/ + + while (false) { /*warning:empty_statement*/ + } + while (false) { + /*jsl:pass*/ + } + + /* empty block within for; useless expression */ + for (i = 0; i < 2; i += 1) { /*warning:empty_statement*/ + } + for (i = 0; i < 2; i += 1) { + /*jsl:pass*/ + } + + /* legal: empty catch statement */ + try { + i++; + } + catch (err) { + } +} diff --git a/tests/warnings/equal_as_assign.js b/tests/warnings/equal_as_assign.js new file mode 100644 index 0000000..df43778 --- /dev/null +++ b/tests/warnings/equal_as_assign.js @@ -0,0 +1,7 @@ +/*jsl:option explicit*/ +function equal_as_assign() { + var a, b; + while (a = b) { /*warning:equal_as_assign*/ + a++; + } +} diff --git a/tests/warnings/identifier_hides_another.js b/tests/warnings/identifier_hides_another.js new file mode 100644 index 0000000..1c85929 --- /dev/null +++ b/tests/warnings/identifier_hides_another.js @@ -0,0 +1,43 @@ +// Test each combination of arg, var, function, and jsl:declare. +function identifier_hides_another(arg_hides_arg, + function_hides_arg, + var_hides_arg, + declare_hides_arg) { + + function arg_hides_function() { return true; } + function function_hides_function() { return true; } + function var_hides_function() { return true; } + function declare_hides_function() { return true; } + + var arg_hides_var; + var function_hides_var; + var var_hides_var; + var declare_hides_var; + + /*jsl:declare arg_hides_declare*/ + /*jsl:declare function_hides_declare*/ + /*jsl:declare var_hides_declare*/ + /*jsl:declare declare_hides_declare*/ + + function inner(arg_hides_arg, + arg_hides_function, + arg_hides_var, + arg_hides_declare) { /*warning:identifier_hides_another*//*warning:identifier_hides_another*//*warning:identifier_hides_another*//*warning:identifier_hides_another*/ + + function function_hides_arg() { return true; } /*warning:identifier_hides_another*/ + function function_hides_function() { return true; } /*warning:identifier_hides_another*/ + function function_hides_var() { return true; } /*warning:identifier_hides_another*/ + function function_hides_declare() { return true; } /*warning:identifier_hides_another*/ + + var var_hides_arg; /*warning:identifier_hides_another*/ + var var_hides_function; /*warning:identifier_hides_another*/ + var var_hides_var; /*warning:identifier_hides_another*/ + var var_hides_declare; /*warning:identifier_hides_another*/ + + /*jsl:declare declare_hides_arg*/ /*warning:identifier_hides_another*/ + /*jsl:declare declare_hides_function*/ /*warning:identifier_hides_another*/ + /*jsl:declare declare_hides_var*/ /*warning:identifier_hides_another*/ + /*jsl:declare declare_hides_declare*/ /*warning:identifier_hides_another*/ + } +} + diff --git a/tests/warnings/inc_dec_within_stmt-ignore.js b/tests/warnings/inc_dec_within_stmt-ignore.js new file mode 100644 index 0000000..29cbd03 --- /dev/null +++ b/tests/warnings/inc_dec_within_stmt-ignore.js @@ -0,0 +1,20 @@ +/*jsl:option explicit*/ +function inc_dec_within_stmt() { + var x; + do { + var y = x--; /*warning:inc_dec_within_stmt*/ + } while (x > 0); + + do y = x--; /*warning:inc_dec_within_stmt*/ + while (x > 0); + + do { + /*jsl:ignore*/ + var y = x--; + /*jsl:end*/ + } while (x > 0); + + do { + x++; + } while (x < 0); +} diff --git a/tests/warnings/inc_dec_within_stmt.js b/tests/warnings/inc_dec_within_stmt.js new file mode 100644 index 0000000..950fb54 --- /dev/null +++ b/tests/warnings/inc_dec_within_stmt.js @@ -0,0 +1,62 @@ +/*jsl:option explicit*/ +function inc_dec_within_stmt() { + var i, s; + + function statements() { + i++; + i--; + ++i; + --i; + } + + function for_loops() { + for (i = 0; i < 10; i++) { + s = i; + } + for (i = 10; i > 0; i--) { + s = i; + } + for(i = 0; i < 5; i++, i--) { + i++; + } + for(i = 0; i < 5; ) { + i++; + } + + for (i = 0; i < 5; i = ++i) { /*warning:inc_dec_within_stmt*/ + /*jsl:pass*/ + } + } + + function expressions() { + switch (i--) /*warning:inc_dec_within_stmt*/ + { + default: + break; + } + + s = new String(i++); /*warning:inc_dec_within_stmt*/ + + s = --i; /*warning:inc_dec_within_stmt*/ + } + + function jsl_ignore() { + var x; + do { + var y = x--; /*warning:inc_dec_within_stmt*/ + } while (x > 0); + + do y = x--; /*warning:inc_dec_within_stmt*/ + while (x > 0); + + do { + /*jsl:ignore*/ + var y = x--; + /*jsl:end*/ + } while (x > 0); + + do { + x++; + } while (x < 0); + } +} diff --git a/tests/warnings/jsl_cc_not_understood.js b/tests/warnings/jsl_cc_not_understood.js new file mode 100644 index 0000000..ca80df5 --- /dev/null +++ b/tests/warnings/jsl_cc_not_understood.js @@ -0,0 +1,5 @@ +/*jsl:option explicit*/ +function jsl_cc_not_understood() { + /*jsl:bogon*/ /*warning:jsl_cc_not_understood*/ + return; +} diff --git a/tests/warnings/leading_decimal_point.js b/tests/warnings/leading_decimal_point.js new file mode 100644 index 0000000..b93c770 --- /dev/null +++ b/tests/warnings/leading_decimal_point.js @@ -0,0 +1,7 @@ +/*jsl:option explicit*/ +function leading_decimal_point() { + var i; + + /* leading decimal point; should have zero */ + i = .12; /*warning:leading_decimal_point*/ +} diff --git a/tests/warnings/legacy_cc_not_understood.js b/tests/warnings/legacy_cc_not_understood.js new file mode 100644 index 0000000..6a53883 --- /dev/null +++ b/tests/warnings/legacy_cc_not_understood.js @@ -0,0 +1,9 @@ +/*jsl:option explicit*/ +function legacy_cc_not_understood() { + /* illegal - missing end */ + /*@control comment starts but doesn't end*/ /*warning:legacy_cc_not_understood*/ + + /* illegal - unrecognized */ + /*@bogon@*/ /*warning:legacy_cc_not_understood*/ + return; +} diff --git a/tests/warnings/meaningless_block.js b/tests/warnings/meaningless_block.js new file mode 100644 index 0000000..45bb2f3 --- /dev/null +++ b/tests/warnings/meaningless_block.js @@ -0,0 +1,12 @@ +/*jsl:option explicit*/ +function meaningless_block() { + var i; + + /* meaningless block */ + { /*warning:meaningless_block*/ + var s; + s = i + "%"; + } + + return s; +} diff --git a/tests/warnings/misplaced_regex.js b/tests/warnings/misplaced_regex.js new file mode 100644 index 0000000..6474b53 --- /dev/null +++ b/tests/warnings/misplaced_regex.js @@ -0,0 +1,26 @@ +/*jsl:option explicit*/ +function misplaced_regex() { + var i, re; + + /* legal usage: regex in assignment */ + re = /\/\./; + + /* legal usage: regex in object definition */ + var o = { test : /\/\./ }; + + /* legal usage: regex as first parameter */ + new String().replace(/\/\./, ""); + + /* legal usage: regex as parameter (besides first) */ + misplaced_regex(re, /\/\./); + + /* legal usage: regex in property */ + var b = /\/\./.test(new String()); + + /* illegal usage: anything else */ + i += /\/\./; /*warning:misplaced_regex*/ + i = -/.*/; /*warning:misplaced_regex*/ + + /* legal usage: return */ + return /\/\./; +} diff --git a/tests/warnings/missing_break.js b/tests/warnings/missing_break.js new file mode 100644 index 0000000..4488a1e --- /dev/null +++ b/tests/warnings/missing_break.js @@ -0,0 +1,106 @@ +/*jsl:option explicit*/ +function missing_break() { + var i, o, s; + + switch (i) { + /* okay because of return */ + default: + return ""; + } + + switch (i) { + /* okay because of throw */ + default: + throw s; + } + + switch (i) { + case 1: + s += "."; + /*missing break*/ + + case 2: /*warning:missing_break*/ + /*okay because of return*/ + s += ","; + return s; + + case 3: + /*okay because of throw*/ + s += ";"; + throw s; + + case 4: + /*okay because of break/throw*/ + if (s) { + break; + } + else { + throw i; + } + + case 5: + /*missing break in catch*/ + try { + i--; + break; + } + catch (err) { + s = null; + } + finally { + i++; + } + + case 6: /*warning:missing_break*/ + /*ok; finally statement does not affect it */ + try { + i--; + break; + } + catch (err) { + s = null; + break; + } + finally { + i++; + } + + case 7: + /*ok; break statement in catch and finally*/ + try { + i--; + } + catch (err) { + s = null; + break; + } + finally { + i++; + break; + } + + case 8: + /*ok; return statement in finally*/ + try { + i--; + } + catch (err) { + s = null; + } + finally { + i++; + return i; + } + + case 9: + /* test a break inside a loop */ + for (;;) { + break; + } + + default: /*warning:missing_break*/ + break; + } + + return ""; +} diff --git a/tests/warnings/missing_break_for_last_case.js b/tests/warnings/missing_break_for_last_case.js new file mode 100644 index 0000000..deb9fbc --- /dev/null +++ b/tests/warnings/missing_break_for_last_case.js @@ -0,0 +1,19 @@ +/*jsl:option explicit*/ +function missing_break_for_last_case(i) { + switch (i) { + default: /*warning:missing_break_for_last_case*/ + /*missing break at end of switch (without code)*/ + } + + /*missing break at end of switch (with code)*/ + switch (i) { + default: /*warning:missing_break_for_last_case*/ + i++; + } + + /*ok because of fallthru*/ + switch (i) { + default: + /*jsl:fallthru*/ + } +} diff --git a/tests/warnings/missing_default_case.js b/tests/warnings/missing_default_case.js new file mode 100644 index 0000000..0977dca --- /dev/null +++ b/tests/warnings/missing_default_case.js @@ -0,0 +1,51 @@ +/*jsl:option explicit*/ +function missing_default_case() { + var i, s; + + /*missing default case*/ + switch (i) { /*warning:missing_default_case*/ + case 1: + return 1; + } + + /* ambivalence - fallthru is meaningless */ + switch (i) { + case 2: + /*jsl:fallthru*/ /*warning:invalid_fallthru*/ + case 3: + s += 1; + break; + default: + break; + } + + /* ok - intended use of fallthru */ + switch (i) { + case 0: + s += "?"; + /*jsl:fallthru*/ + case 1: + s += "!"; + break; + default: + break; + } + + /* ok - intended use of fallthru */ + switch(i) { + case 1: + try { + i++; + } + catch(e) + {} + /*jsl:fallthru*/ + case 2: + i--; + break; + default: + break; + } + + return ""; +} diff --git a/tests/warnings/missing_option_explicit.js b/tests/warnings/missing_option_explicit.js new file mode 100644 index 0000000..69e3a43 --- /dev/null +++ b/tests/warnings/missing_option_explicit.js @@ -0,0 +1,5 @@ +/*jsl:option explicit*/ +function missing_option_explicit() { + /* nothing to see here; move along */ + return null; +} diff --git a/tests/warnings/missing_semicolon.js b/tests/warnings/missing_semicolon.js new file mode 100644 index 0000000..9334cd5 --- /dev/null +++ b/tests/warnings/missing_semicolon.js @@ -0,0 +1,23 @@ +/*jsl:option explicit*/ + +function missing_semicolon() { + /* missing semicolon after return */ + function MissingSemicolonOnReturnStatement() { + return 0 /*warning:missing_semicolon*/ + } + function MissingSemicolonOnReturnStatement2() { return 0 } /*warning:missing_semicolon*/ + + /* missing semicolon after lambda */ + function x() { + this.y = function() { return 0; } /*warning:missing_semicolon_for_lambda*/ + } + + /* missing semicolon after lambda */ + x.prototype.z = function() { + return 1; + } /*warning:missing_semicolon_for_lambda*/ + + do x++; + while (x < 10) /*warning:missing_semicolon*/ +} + diff --git a/tests/warnings/missing_semicolon_for_lambda.js b/tests/warnings/missing_semicolon_for_lambda.js new file mode 100644 index 0000000..69293ef --- /dev/null +++ b/tests/warnings/missing_semicolon_for_lambda.js @@ -0,0 +1,43 @@ +/*jsl:option explicit*/ +/*jsl:declare global*/ + +/* Test with a simple variable. */ +var x = function() { + return {}; +} /*warning:missing_semicolon_for_lambda*/ +x(); + +var a, b = function() { }, c /*warning:missing_semicolon*/ +b(); +var d, e = function() { } /*warning:missing_semicolon_for_lambda*/ +e(); + +var y; +y = function() { + return []; +} /*warning:missing_semicolon_for_lambda*/ +y(); + +global = function() { + return null; +} /*warning:missing_semicolon_for_lambda*/ +global(); + +function Foo() +{ + this.bar = 10; + + /* Test an assignment to a member. */ + this.setBar = function(bar) { + this.bar = bar; + } /*warning:missing_semicolon_for_lambda*/ + + this.setBar(this.bar * 2); +} + +/* Test an assignment to a prototype. */ +Foo.prototype.getBar = function() { + return this.bar; +} /*warning:missing_semicolon_for_lambda*/ + +var foo = new Foo(); diff --git a/tests/warnings/multiple_plus_minus.js b/tests/warnings/multiple_plus_minus.js new file mode 100644 index 0000000..4f5ecc6 --- /dev/null +++ b/tests/warnings/multiple_plus_minus.js @@ -0,0 +1,11 @@ +/*jsl:option explicit*/ +/*conf:-want_assign_or_call*/ +function multiple_plus_minus() { + var i, j; + i = 0; + j = 0; + + /* disallow confusing +/- */ + i+++j; /*warning:multiple_plus_minus*//*warning:inc_dec_within_stmt*/ + j---i; /*warning:multiple_plus_minus*//*warning:inc_dec_within_stmt*/ +} diff --git a/tests/warnings/nested_comment.js b/tests/warnings/nested_comment.js new file mode 100644 index 0000000..c0a876f --- /dev/null +++ b/tests/warnings/nested_comment.js @@ -0,0 +1,6 @@ +/*jsl:option explicit*/ +function nested_comment() { + /* nested comment */ + /* /* */ /*warning:nested_comment*/ + return ""; +} diff --git a/tests/warnings/no_return_value.js b/tests/warnings/no_return_value.js new file mode 100644 index 0000000..6d25c32 --- /dev/null +++ b/tests/warnings/no_return_value.js @@ -0,0 +1,31 @@ +/*jsl:option explicit*/ +function no_return_value() { + function error1(b) { + if (b) + return true; + else + return; /*warning:no_return_value*/ + } + + function error2(b) { + if (b) { + return; /*warning:no_return_value*/ + } + else { + return ""; + } + } + + function error3(b) { /*warning:no_return_value*/ + if (b) { + return ""; + } + } + + function correct(b) { + if (b) + return; + else + return; + } +} diff --git a/tests/warnings/octal_number.js b/tests/warnings/octal_number.js new file mode 100644 index 0000000..82585bc --- /dev/null +++ b/tests/warnings/octal_number.js @@ -0,0 +1,5 @@ +/*jsl:option explicit*/ +function octal_number() { + var i; + i = 010; /*warning:octal_number*/ +} diff --git a/tests/warnings/parseint_missing_radix.js b/tests/warnings/parseint_missing_radix.js new file mode 100644 index 0000000..37d234a --- /dev/null +++ b/tests/warnings/parseint_missing_radix.js @@ -0,0 +1,15 @@ +/*jsl:option explicit*/ +function parseint_missing_radix() { + var i; + + i = parseInt();/*warning:parseint_missing_radix*/ + + i = parseInt('061');/*warning:parseint_missing_radix*/ + i = parseInt('061', 8); + + i = parseInt('0xA');/*warning:parseint_missing_radix*/ + i = parseInt('0xA', 16); + + i = parseInt('81');/*warning:parseint_missing_radix*/ + i = parseInt('81', 10); +} diff --git a/tests/warnings/partial_option_explicit.js b/tests/warnings/partial_option_explicit.js new file mode 100644 index 0000000..0330656 --- /dev/null +++ b/tests/warnings/partial_option_explicit.js @@ -0,0 +1,5 @@ +/*jsl:option explicit*/ +function partial_option_explicit() { + /* nothing to see here; move along */ + return null; +} diff --git a/tests/warnings/redeclared_var.js b/tests/warnings/redeclared_var.js new file mode 100644 index 0000000..e790d1e --- /dev/null +++ b/tests/warnings/redeclared_var.js @@ -0,0 +1,10 @@ +/*jsl:option explicit*/ +function redeclared_var() { + var duplicate; + var duplicate; /*warning:redeclared_var*/ + + function myFunction() { + return; + } + var myFunction; /*warning:redeclared_var*/ +} diff --git a/tests/warnings/spidermonkey/bad_backref.js b/tests/warnings/spidermonkey/bad_backref.js new file mode 100644 index 0000000..5822b98 --- /dev/null +++ b/tests/warnings/spidermonkey/bad_backref.js @@ -0,0 +1,5 @@ +/*jsl:option explicit*/ +function bad_backref() { + /* illegal - one 1 backreference */ + return /(.)\2/; /*warning:bad_backref*/ +} diff --git a/tests/warnings/spidermonkey/deprecated_usage.js b/tests/warnings/spidermonkey/deprecated_usage.js new file mode 100644 index 0000000..b66f422 --- /dev/null +++ b/tests/warnings/spidermonkey/deprecated_usage.js @@ -0,0 +1,11 @@ +/*jsl:option explicit*/ +function deprecated_usage() { + /* illegal - getter/setter is deprecated */ + + Array.bogon getter = function () { /*warning:deprecated_usage*/ + return ""; + }; + Array.bogon setter = function (o) { /*warning:deprecated_usage*/ + this.push(o); + }; +} diff --git a/tests/warnings/spidermonkey/invalid_backref.js b/tests/warnings/spidermonkey/invalid_backref.js new file mode 100644 index 0000000..9da228b --- /dev/null +++ b/tests/warnings/spidermonkey/invalid_backref.js @@ -0,0 +1,5 @@ +/*jsl:option explicit*/ +function invalid_backref() { + /* illegal - \0 is not a valid regex backreference */ + return /\0/; /*warning:invalid_backref*/ +} diff --git a/tests/warnings/spidermonkey/trailing_comma.js b/tests/warnings/spidermonkey/trailing_comma.js new file mode 100644 index 0000000..28a1fe2 --- /dev/null +++ b/tests/warnings/spidermonkey/trailing_comma.js @@ -0,0 +1,5 @@ +/*jsl:option explicit*/ +function trailing_comma() { + /* illegal - trailing comma */ + return { name: 'value', }; /*warning:trailing_comma*/ +} diff --git a/tests/warnings/trailing_comma_in_array.js b/tests/warnings/trailing_comma_in_array.js new file mode 100644 index 0000000..1659649 --- /dev/null +++ b/tests/warnings/trailing_comma_in_array.js @@ -0,0 +1,8 @@ +/*jsl:option explicit*/ +function trailing_comma_in_array() { + var a; + + a = [1,,2]; + a = [1,]; /*warning:trailing_comma_in_array*/ + a = [1,,]; /*warning:trailing_comma_in_array*/ +} diff --git a/tests/warnings/trailing_decimal_point.js b/tests/warnings/trailing_decimal_point.js new file mode 100644 index 0000000..9f75f59 --- /dev/null +++ b/tests/warnings/trailing_decimal_point.js @@ -0,0 +1,7 @@ +/*jsl:option explicit*/ +function trailing_decimal_point() { + var i; + + /* trailing decimal point; should have zero or no decimal*/ + i = 12.0.floor(); /*warning:trailing_decimal_point*/ +} diff --git a/tests/warnings/unreachable_code.js b/tests/warnings/unreachable_code.js new file mode 100644 index 0000000..edf6a1b --- /dev/null +++ b/tests/warnings/unreachable_code.js @@ -0,0 +1,64 @@ +/*jsl:option explicit*/ +function unreachable_code() { + var i; + i = 0; + + /* unreachable because of break */ + while (i < 100) { + break; + i += 1; /*warning:unreachable_code*/ + } + + /* unreachable because of continue */ + while (i > 100) { + continue; + i += 1; /*warning:unreachable_code*/ + } + + /* unreachable because of return */ + if (i + i < 0) { + return; + i = -i; /*warning:unreachable_code*/ + } + + /* unreachable because of throw */ + if (i == 14) { + throw i; + i -= 1; /*warning:unreachable_code*/ + } + + function var_test() { + return undef; + var undef; + } + + function func_test() { + return fortytwo(); + function fortytwo() { + return 42; + } + } + + /* test unreachable statements in for loops */ + for (i = 0; i < 10; i++) { /*warning:unreachable_code*/ + if (i) + break; + else + return; + } + for (i = 0; i < 10; ) { + if (i) + break; + else + return; + } + + /* test unreachable statements in do..while loops. */ + do { + if (i) + break; + else + return; + } while (i); /*warning:unreachable_code*/ + +} diff --git a/tests/warnings/unreachable_code_2.js b/tests/warnings/unreachable_code_2.js new file mode 100644 index 0000000..903eafb --- /dev/null +++ b/tests/warnings/unreachable_code_2.js @@ -0,0 +1,33 @@ +function unreachable_code_2() { + + // Function declarations are never unreachable. + function scope_a() + { + return inner(); + function inner() { + return 10; + } + } + + // Variable declarations are never unreachable. + function scope_b() + { + return value; + var value; + } + + // Variable assignments are, however. + function scope_c() + { + return value_a; + var value_a = 10; /*warning:unreachable_code*/ + } + + // Test multiple variables. + function scope_d() + { + return value_a; + var value_a, value_b = 10, value_c; /*warning:unreachable_code*/ + } +} + diff --git a/tests/warnings/unreferenced_identifier.js b/tests/warnings/unreferenced_identifier.js new file mode 100644 index 0000000..1ef098a --- /dev/null +++ b/tests/warnings/unreferenced_identifier.js @@ -0,0 +1,102 @@ +/* The tests disable this warning by default becaues of noise. Enable it. */ +/*conf:+unreferenced_argument*/ +/*conf:+unreferenced_function*/ +/*conf:+unreferenced_variable*/ + +/* outer-level functions shouldn't warn */ +var unreferenced_global; +function unreferenced_identifier() { + /* Test an unreferenced function. */ + function unreferenced_func() { /*warning:unreferenced_function*/ + return true; + } + function referenced_func() { + } + var referenced_var = referenced_func; + referenced_var(); + + /* Test an unreferenced parameter. */ + var z = new function(unreferenced_parm) { /*warning:unreferenced_argument*/ + }; + z.prop = 42; + + /* Test an unreferenced variable. */ + var unreferenced_variable = 100; /*warning:unreferenced_variable*/ + + /* An unreferenced duplicate parameter should give one warning. */ + function func_with_dup(unref_dup_parm, unref_dup_parm) { /*warning:unreferenced_argument*/ /*warning:duplicate_formal*/ + } + func_with_dup(); + + /* An unreferenced duplicate variable should give one warning. */ + var unref_dup_var; /*warning:unreferenced_variable*/ + var unref_dup_var; /*warning:redeclared_var*/ + + /* Test a try/catch. The error doesn't need to be referenced. */ + var can; + try { + can = true; /* we think we can... */ + } + catch(err) { + can = false; /* ...but maybe not! */ + } + can = !can; + + /* Test a with statement. */ + var withobj = {}; + var withval = 42; + with (withobj) /*warning:with_statement*/ + { + prop_a = withval; + var innerval = '42'; + prop_b = innerval; + } + + /* Test assignments. */ + var assigned_but_unref; /*warning:unreferenced_variable*/ + assigned_but_unref = 42; + + function callback() { + } + var assigned_but_ref; + (assigned_but_ref = callback)(); + + /* Test increment and decrement. */ + var unref_inc; /*warning:unreferenced_variable*/ + unref_inc++; + var unref_dec; /*warning:unreferenced_variable*/ + unref_dec--; + + var tmp; + var ref_inc; + tmp = ref_inc++; /*warning:inc_dec_within_stmt*/ + var ref_dec; + tmp = ref_dec--; /*warning:inc_dec_within_stmt*/ + tmp = -tmp; + + /* Test named functions as references. */ + var fn = function ref_func() { return 42; }; /*warning:unreferenced_function*/ + fn(); + + /* Test nested scopes. */ + function get_callback(parm) { + return function() { + return parm; + }; + } + + function test_unused(parm) { /*warning:unreferenced_function*/ + /*jsl:unused parm*/ + /*jsl:unused bogus_outer*/ /*warning:undeclared_identifier*/ + + var unused_var; + /*jsl:unused unused_var*/ + + with (parm) { /*warning:with_statement*/ + /*jsl:unused bogus_inner*/ /*warning:undeclared_identifier*/ + x = 42; + } + } + + return get_callback(42); +} diff --git a/tests/warnings/use_of_label.js b/tests/warnings/use_of_label.js new file mode 100644 index 0000000..8379927 --- /dev/null +++ b/tests/warnings/use_of_label.js @@ -0,0 +1,19 @@ +/*jsl:option explicit*/ +function use_of_label() { + var o; + + /* label disallowed */ + MyWhile: /*warning:use_of_label*/ + while (true) { + /* label disallowed */ + MyFor: /*warning:use_of_label*/ + for (var x in o) { + if (x) { + break MyWhile; + } + else { + continue MyWhile; + } + } + } +} diff --git a/tests/warnings/useless_assign.js b/tests/warnings/useless_assign.js new file mode 100644 index 0000000..1d143f6 --- /dev/null +++ b/tests/warnings/useless_assign.js @@ -0,0 +1,20 @@ +/*jsl:option explicit*/ +function useless_assign() { + var s = s; /*warning:useless_assign*/ + + var o; + o = o; /*warning:useless_assign*/ + + var i; + for (i = i; ; ) { /*warning:useless_assign*/ + i++; + } + + for (; i = i; ) { /*warning:useless_assign*/ + i++; + } + + for (; ; i = i) { /*warning:useless_assign*/ + i++; + } +} diff --git a/tests/warnings/useless_comparison.js b/tests/warnings/useless_comparison.js new file mode 100644 index 0000000..20a928e --- /dev/null +++ b/tests/warnings/useless_comparison.js @@ -0,0 +1,55 @@ +/*jsl:option explicit*/ +function useless_comparison() { + var i, j, o; + + /* Test expressions */ + if (i+2 < i+2) { /*warning:useless_comparison*/ + return; + } + if (j != j) { /*warning:useless_comparison*/ + i++; + } + if ((14 * i) / (j - 2) >= (14 * i) / (j - 2)) { /*warning:useless_comparison*/ + return; + } + + /* Test properties */ + if (o.left == o.left) { /*warning:useless_comparison*/ + return; + } + if (o.left == o['left']) { /*warning:useless_comparison*/ + return; + } + if (o['left'] == o['left']) { /*warning:useless_comparison*/ + return; + } + if (o[i] == o[i]) { /*warning:useless_comparison*/ + return; + } + + if (o.left == o.right) { + return; + } + if (o['left'] == o.right) { + return; + } + if (o['left'] == o['right']) { + return; + } + if (o[i] == o[j]) { + return; + } + if (o[i] == o.right) { + return; + } + + /* Complex expressions not caught because of slight differences */ + if ((14 * i) / (j - 2) == (i * 14) / (j - 2)) { + return; + } + + /* allowed since function may have side affects */ + if (useless_comparison() == useless_comparison()) { + return; + } +} diff --git a/tests/warnings/useless_quotes.js b/tests/warnings/useless_quotes.js new file mode 100644 index 0000000..321dc24 --- /dev/null +++ b/tests/warnings/useless_quotes.js @@ -0,0 +1,11 @@ +function useless_quotes() { + var o = { + 'key': 1 /*warning:useless_quotes*/ + }; + o = { + 'key with space': false + }; + o = { + key: '1' + }; +} diff --git a/tests/warnings/useless_void.js b/tests/warnings/useless_void.js new file mode 100644 index 0000000..dda2f2a --- /dev/null +++ b/tests/warnings/useless_void.js @@ -0,0 +1,6 @@ +/*jsl:option explicit*/ +function useless_void() { + var z; + z = void 0; /*warning:useless_void*/ + z(); +} diff --git a/tests/warnings/var_hides_arg.js b/tests/warnings/var_hides_arg.js new file mode 100644 index 0000000..e1e1743 --- /dev/null +++ b/tests/warnings/var_hides_arg.js @@ -0,0 +1,7 @@ +/*jsl:option explicit*/ +function var_hides_arg(duplicate1, duplicate2) { + var duplicate1; /*warning:var_hides_arg*/ + function inner() { + var duplicate2; /*warning:identifier_hides_another*/ + } +} diff --git a/tests/warnings/want_assign_or_call.js b/tests/warnings/want_assign_or_call.js new file mode 100644 index 0000000..42bf5b8 --- /dev/null +++ b/tests/warnings/want_assign_or_call.js @@ -0,0 +1,30 @@ +function want_assign_or_call() { + var a; + a; /*warning:want_assign_or_call*/ + a++; + a--; + a /= 2; + a %= 4; + a >>= 3; + a << 2; /*warning:want_assign_or_call*/ + + new function() { + }; + + function test() { + } + + function() { /*warning:want_assign_or_call*/ + return 42; + } + + delete a; + + a.b(); + + /* Test with arguments to the constructor. */ + new function(x) { + this.x = x; + }(42); +} + diff --git a/tests/warnings/with_statement.js b/tests/warnings/with_statement.js new file mode 100644 index 0000000..49156f7 --- /dev/null +++ b/tests/warnings/with_statement.js @@ -0,0 +1,7 @@ +function with_statement() { + var o = {}; + + with (o) { /*warning:with_statement*/ + this.x = this.y; + } +} From 968c3e0b5a17c04c8870e14e8b953bb142e248de Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Fri, 2 Dec 2011 18:49:10 +0000 Subject: [PATCH 02/59] r303: Fix --dump command line option. --- javascriptlint/jsl.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/javascriptlint/jsl.py b/javascriptlint/jsl.py index 4eee0b6..10d69d8 100755 --- a/javascriptlint/jsl.py +++ b/javascriptlint/jsl.py @@ -9,6 +9,7 @@ from optparse import OptionParser import conf +import fs import htmlparse import jsparse import lint @@ -21,7 +22,7 @@ def _dump(paths): for path in paths: - script = util.readfile(path) + script = fs.readfile(path) jsparse.dump_tree(script) def _lint(paths, conf_, printpaths): From 14b8ba5466caf228b1e96f6e53c1a31c13b46856 Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Sat, 28 Sep 2013 03:06:19 +0000 Subject: [PATCH 03/59] r304: Replace SpiderMonkey with a JavaScript parser written purely in JavaScript. --- Makefile | 45 +- javascriptlint/jsparse.py | 208 +- javascriptlint/lint.py | 26 +- javascriptlint/pyspidermonkey/nodepos.c | 117 - javascriptlint/pyspidermonkey/nodepos.h | 10 - .../pyspidermonkey/pyspidermonkey.c | 596 -- javascriptlint/pyspidermonkey/tokens.tbl | 86 - javascriptlint/pyspidermonkey_/__init__.py | 21 - javascriptlint/spidermonkey.py | 10 - javascriptlint/warnings.py | 37 +- jsengine/__init__.py | 21 + jsengine/parser/__init__.py | 924 ++ jsengine/parser/_constants_kind.py | 79 + jsengine/parser/_constants_op.py | 85 + jsengine/structs.py | 196 + jsengine/tokenizer/__init__.py | 430 + spidermonkey/README | 7 - spidermonkey/src/Makefile | 70 - spidermonkey/src/README.html | 826 -- spidermonkey/src/SpiderMonkey.rsp | 12 - spidermonkey/src/Y.js | 19 - spidermonkey/src/js.c | 3181 ------- spidermonkey/src/js.mdp | Bin 17922 -> 0 bytes spidermonkey/src/js.msg | 301 - spidermonkey/src/js.pkg | 2 - spidermonkey/src/jsOS240.def | 654 -- spidermonkey/src/jsapi.c | 5023 ---------- spidermonkey/src/jsapi.h | 2224 ----- spidermonkey/src/jsarena.c | 502 - spidermonkey/src/jsarena.h | 303 - spidermonkey/src/jsarray.c | 1864 ---- spidermonkey/src/jsarray.h | 95 - spidermonkey/src/jsatom.c | 999 -- spidermonkey/src/jsatom.h | 456 - spidermonkey/src/jsbit.h | 195 - spidermonkey/src/jsbool.c | 227 - spidermonkey/src/jsbool.h | 76 - spidermonkey/src/jsclist.h | 139 - spidermonkey/src/jscntxt.c | 1229 --- spidermonkey/src/jscntxt.h | 1013 -- spidermonkey/src/jscompat.h | 57 - spidermonkey/src/jsconfig.h | 208 - spidermonkey/src/jscpucfg.c | 380 - spidermonkey/src/jscpucfg.h | 212 - spidermonkey/src/jsdate.c | 2371 ----- spidermonkey/src/jsdate.h | 120 - spidermonkey/src/jsdbgapi.c | 1441 --- spidermonkey/src/jsdbgapi.h | 406 - spidermonkey/src/jsdhash.c | 826 -- spidermonkey/src/jsdhash.h | 581 -- spidermonkey/src/jsdtoa.c | 3132 ------ spidermonkey/src/jsdtoa.h | 130 - spidermonkey/src/jsemit.c | 6845 -------------- spidermonkey/src/jsemit.h | 743 -- spidermonkey/src/jsexn.c | 1348 --- spidermonkey/src/jsexn.h | 96 - spidermonkey/src/jsfile.c | 2735 ------ spidermonkey/src/jsfile.h | 56 - spidermonkey/src/jsfile.msg | 90 - spidermonkey/src/jsfun.c | 2330 ----- spidermonkey/src/jsfun.h | 170 - spidermonkey/src/jsgc.c | 3201 ------- spidermonkey/src/jsgc.h | 368 - spidermonkey/src/jshash.c | 483 - spidermonkey/src/jshash.h | 151 - spidermonkey/src/jsify.pl | 485 - spidermonkey/src/jsinterp.c | 6216 ------------ spidermonkey/src/jsinterp.h | 361 - spidermonkey/src/jsiter.c | 1080 --- spidermonkey/src/jsiter.h | 114 - spidermonkey/src/jskeyword.tbl | 124 - spidermonkey/src/jskwgen.c | 460 - spidermonkey/src/jslibmath.h | 266 - spidermonkey/src/jslock.c | 1303 --- spidermonkey/src/jslock.h | 266 - spidermonkey/src/jslocko.asm | 60 - spidermonkey/src/jslog2.c | 94 - spidermonkey/src/jslong.c | 281 - spidermonkey/src/jslong.h | 437 - spidermonkey/src/jsmath.c | 514 - spidermonkey/src/jsmath.h | 57 - spidermonkey/src/jsnum.c | 1147 --- spidermonkey/src/jsnum.h | 268 - spidermonkey/src/jsobj.c | 5035 ---------- spidermonkey/src/jsobj.h | 596 -- spidermonkey/src/jsopcode.c | 4794 ---------- spidermonkey/src/jsopcode.h | 318 - spidermonkey/src/jsopcode.tbl | 478 - spidermonkey/src/jsosdep.h | 115 - spidermonkey/src/jsotypes.h | 202 - spidermonkey/src/jsparse.c | 6556 ------------- spidermonkey/src/jsparse.h | 439 - spidermonkey/src/jsprf.c | 1266 --- spidermonkey/src/jsprf.h | 150 - spidermonkey/src/jsproto.tbl | 116 - spidermonkey/src/jsprvtd.h | 202 - spidermonkey/src/jspubtd.h | 667 -- spidermonkey/src/jsregexp.c | 4206 --------- spidermonkey/src/jsregexp.h | 183 - spidermonkey/src/jsscan.c | 2101 ----- spidermonkey/src/jsscan.h | 389 - spidermonkey/src/jsscope.c | 1776 ---- spidermonkey/src/jsscope.h | 407 - spidermonkey/src/jsscript.c | 1717 ---- spidermonkey/src/jsscript.h | 225 - spidermonkey/src/jsshell.msg | 50 - spidermonkey/src/jsstddef.h | 83 - spidermonkey/src/jsstr.c | 4818 ---------- spidermonkey/src/jsstr.h | 500 - spidermonkey/src/jstypes.h | 464 - spidermonkey/src/jsutil.c | 198 - spidermonkey/src/jsutil.h | 106 - spidermonkey/src/jsxdrapi.c | 835 -- spidermonkey/src/jsxdrapi.h | 223 - spidermonkey/src/jsxml.c | 8357 ----------------- spidermonkey/src/jsxml.h | 332 - spidermonkey/src/perfect.js | 39 - spidermonkey/src/plify_jsdhash.sed | 33 - spidermonkey/src/prmjtime.c | 440 - spidermonkey/src/prmjtime.h | 95 - spidermonkey/src/resource.h | 15 - spidermonkey/src/win32.order | 391 - test.py | 6 +- tests/control_comments/conf-version.js | 3 +- tests/html/e4x.html | 6 +- tests/html/script_tag_in_js_comment.html | 2 +- tests/warnings/identifier_hides_another.js | 8 +- tests/warnings/spidermonkey/bad_backref.js | 2 +- .../warnings/spidermonkey/deprecated_usage.js | 11 - .../warnings/spidermonkey/invalid_backref.js | 2 +- tests/warnings/want_assign_or_call.js | 2 +- 131 files changed, 1829 insertions(+), 111451 deletions(-) delete mode 100644 javascriptlint/pyspidermonkey/nodepos.c delete mode 100644 javascriptlint/pyspidermonkey/nodepos.h delete mode 100644 javascriptlint/pyspidermonkey/pyspidermonkey.c delete mode 100644 javascriptlint/pyspidermonkey/tokens.tbl delete mode 100644 javascriptlint/pyspidermonkey_/__init__.py delete mode 100644 javascriptlint/spidermonkey.py create mode 100644 jsengine/__init__.py create mode 100644 jsengine/parser/__init__.py create mode 100644 jsengine/parser/_constants_kind.py create mode 100644 jsengine/parser/_constants_op.py create mode 100644 jsengine/structs.py create mode 100644 jsengine/tokenizer/__init__.py delete mode 100644 spidermonkey/README delete mode 100644 spidermonkey/src/Makefile delete mode 100644 spidermonkey/src/README.html delete mode 100644 spidermonkey/src/SpiderMonkey.rsp delete mode 100644 spidermonkey/src/Y.js delete mode 100644 spidermonkey/src/js.c delete mode 100644 spidermonkey/src/js.mdp delete mode 100644 spidermonkey/src/js.msg delete mode 100644 spidermonkey/src/js.pkg delete mode 100644 spidermonkey/src/jsOS240.def delete mode 100644 spidermonkey/src/jsapi.c delete mode 100644 spidermonkey/src/jsapi.h delete mode 100644 spidermonkey/src/jsarena.c delete mode 100644 spidermonkey/src/jsarena.h delete mode 100644 spidermonkey/src/jsarray.c delete mode 100644 spidermonkey/src/jsarray.h delete mode 100644 spidermonkey/src/jsatom.c delete mode 100644 spidermonkey/src/jsatom.h delete mode 100644 spidermonkey/src/jsbit.h delete mode 100644 spidermonkey/src/jsbool.c delete mode 100644 spidermonkey/src/jsbool.h delete mode 100644 spidermonkey/src/jsclist.h delete mode 100644 spidermonkey/src/jscntxt.c delete mode 100644 spidermonkey/src/jscntxt.h delete mode 100644 spidermonkey/src/jscompat.h delete mode 100644 spidermonkey/src/jsconfig.h delete mode 100644 spidermonkey/src/jscpucfg.c delete mode 100644 spidermonkey/src/jscpucfg.h delete mode 100644 spidermonkey/src/jsdate.c delete mode 100644 spidermonkey/src/jsdate.h delete mode 100644 spidermonkey/src/jsdbgapi.c delete mode 100644 spidermonkey/src/jsdbgapi.h delete mode 100644 spidermonkey/src/jsdhash.c delete mode 100644 spidermonkey/src/jsdhash.h delete mode 100644 spidermonkey/src/jsdtoa.c delete mode 100644 spidermonkey/src/jsdtoa.h delete mode 100644 spidermonkey/src/jsemit.c delete mode 100644 spidermonkey/src/jsemit.h delete mode 100644 spidermonkey/src/jsexn.c delete mode 100644 spidermonkey/src/jsexn.h delete mode 100644 spidermonkey/src/jsfile.c delete mode 100644 spidermonkey/src/jsfile.h delete mode 100644 spidermonkey/src/jsfile.msg delete mode 100644 spidermonkey/src/jsfun.c delete mode 100644 spidermonkey/src/jsfun.h delete mode 100644 spidermonkey/src/jsgc.c delete mode 100644 spidermonkey/src/jsgc.h delete mode 100644 spidermonkey/src/jshash.c delete mode 100644 spidermonkey/src/jshash.h delete mode 100644 spidermonkey/src/jsify.pl delete mode 100644 spidermonkey/src/jsinterp.c delete mode 100644 spidermonkey/src/jsinterp.h delete mode 100644 spidermonkey/src/jsiter.c delete mode 100644 spidermonkey/src/jsiter.h delete mode 100644 spidermonkey/src/jskeyword.tbl delete mode 100644 spidermonkey/src/jskwgen.c delete mode 100644 spidermonkey/src/jslibmath.h delete mode 100644 spidermonkey/src/jslock.c delete mode 100644 spidermonkey/src/jslock.h delete mode 100644 spidermonkey/src/jslocko.asm delete mode 100644 spidermonkey/src/jslog2.c delete mode 100644 spidermonkey/src/jslong.c delete mode 100644 spidermonkey/src/jslong.h delete mode 100644 spidermonkey/src/jsmath.c delete mode 100644 spidermonkey/src/jsmath.h delete mode 100644 spidermonkey/src/jsnum.c delete mode 100644 spidermonkey/src/jsnum.h delete mode 100644 spidermonkey/src/jsobj.c delete mode 100644 spidermonkey/src/jsobj.h delete mode 100644 spidermonkey/src/jsopcode.c delete mode 100644 spidermonkey/src/jsopcode.h delete mode 100644 spidermonkey/src/jsopcode.tbl delete mode 100644 spidermonkey/src/jsosdep.h delete mode 100644 spidermonkey/src/jsotypes.h delete mode 100644 spidermonkey/src/jsparse.c delete mode 100644 spidermonkey/src/jsparse.h delete mode 100644 spidermonkey/src/jsprf.c delete mode 100644 spidermonkey/src/jsprf.h delete mode 100644 spidermonkey/src/jsproto.tbl delete mode 100644 spidermonkey/src/jsprvtd.h delete mode 100644 spidermonkey/src/jspubtd.h delete mode 100644 spidermonkey/src/jsregexp.c delete mode 100644 spidermonkey/src/jsregexp.h delete mode 100644 spidermonkey/src/jsscan.c delete mode 100644 spidermonkey/src/jsscan.h delete mode 100644 spidermonkey/src/jsscope.c delete mode 100644 spidermonkey/src/jsscope.h delete mode 100644 spidermonkey/src/jsscript.c delete mode 100644 spidermonkey/src/jsscript.h delete mode 100644 spidermonkey/src/jsshell.msg delete mode 100644 spidermonkey/src/jsstddef.h delete mode 100644 spidermonkey/src/jsstr.c delete mode 100644 spidermonkey/src/jsstr.h delete mode 100644 spidermonkey/src/jstypes.h delete mode 100644 spidermonkey/src/jsutil.c delete mode 100644 spidermonkey/src/jsutil.h delete mode 100644 spidermonkey/src/jsxdrapi.c delete mode 100644 spidermonkey/src/jsxdrapi.h delete mode 100644 spidermonkey/src/jsxml.c delete mode 100644 spidermonkey/src/jsxml.h delete mode 100644 spidermonkey/src/perfect.js delete mode 100644 spidermonkey/src/plify_jsdhash.sed delete mode 100644 spidermonkey/src/prmjtime.c delete mode 100644 spidermonkey/src/prmjtime.h delete mode 100644 spidermonkey/src/resource.h delete mode 100644 spidermonkey/src/win32.order delete mode 100644 tests/warnings/spidermonkey/deprecated_usage.js diff --git a/Makefile b/Makefile index 521952e..8f4f97a 100644 --- a/Makefile +++ b/Makefile @@ -4,59 +4,20 @@ INSTALLDIRS = \ $(BUILDDIR)/install \ $(BUILDDIR)/install/javascriptlint \ -CSRCS = \ - nodepos.c \ - pyspidermonkey.c - -OBJECTS = $(CSRCS:%.c=$(BUILDDIR)/%.o) -CFLAGS += -fno-strict-aliasing -O -fPIC - -SOLDFLAGS += -shared -CPPFLAGS += -DNDEBUG -D_REENTRANT \ - -Ispidermonkey/src -Ispidermonkey/src/build \ - -I/usr/include \ - - -PY_PYTHON=$(shell python -c "import sys; print(sys.executable)") +PY_PYTHON=$(shell python2.7 -c "import sys; print(sys.executable)") PY_PREFIX=$(shell $(PY_PYTHON) -c "import sys; print(sys.real_prefix)" || $(PY_PYTHON) -c "import sys; print(sys.prefix)") PY_VERSION=$(shell $(PY_PYTHON) -c "import sys; print('.'.join(map(str, sys.version_info[:2])))") -ifeq ($(BUILDOS),Darwin) - PY_ARCH=$(shell $(PY_PYTHON) -c 'import sys; print (sys.maxint > 2**32 and "x86_64" or "i386")') - SOLDFLAGS += $(PY_PREFIX)/Python - CC=gcc -arch $(PY_ARCH) -else - PY_BIT=$(shell $(PY_PYTHON) -c 'import sys; print (sys.maxint > 2**32 and "64" or "32")') - CFLAGS += -m$(PY_BIT) -endif CPPFLAGS += -I$(PY_PREFIX)/include/python$(PY_VERSION) -SOFILE = $(BUILDDIR)/pyspidermonkey.so - -all: $(SOFILE) $(BUILDDIR) $(INSTALLDIRS): mkdir -p $@ -$(OBJECTS): spidermonkey/src/build/libjs.a spidermonkey/src/build/js_operating_system.h - -$(SOFILE): $(OBJECTS) - $(CC) $(CFLAGS) $(SOLDFLAGS) $(LDFLAGS) $(OBJECTS) -Lspidermonkey/src/build -ljs -o $@ - -$(BUILDDIR)/%.o: javascriptlint/pyspidermonkey/%.c | $(BUILDDIR) - $(CC) -o $@ -c $(CFLAGS) $(CPPFLAGS) $< - -spidermonkey/src/build/libjs.a: - (cd spidermonkey/src && CC="$(CC)" CFLAGS="$(CFLAGS)" $(MAKE)) - -spidermonkey/src/build/js_operating_system.h: - echo "#define XP_UNIX" > $@ - clean: -rm -rf $(BUILDDIR) $(INSTALLDIRS) - -(cd spidermonkey/src && $(MAKE) clean) -install: $(SOFILE) javascriptlint/jsl javascriptlint/jsl | $(INSTALLDIRS) - cp $(SOFILE) build/install +install: javascriptlint/jsl javascriptlint/jsl | $(INSTALLDIRS) + cp -r jsengine build/install cp javascriptlint/*.py build/install/javascriptlint sed -e "1s:#\!/usr/bin/env python:#\!$(PY_PYTHON):" javascriptlint/jsl >build/install/jsl chmod +x build/install/jsl diff --git a/javascriptlint/jsparse.py b/javascriptlint/jsparse.py index eec82a4..a1fc1c8 100644 --- a/javascriptlint/jsparse.py +++ b/javascriptlint/jsparse.py @@ -1,155 +1,20 @@ #!/usr/bin/env python # vim: ts=4 sw=4 expandtab """ Parses a script into nodes. """ -import bisect import re import unittest -import spidermonkey -from spidermonkey import tok, op -from util import JSVersion +import jsengine.parser +from jsengine.parser import kind as tok +from jsengine.parser import op +from jsengine.structs import * -_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)] -)) - -NodePos = spidermonkey.NodePos - -class NodePositions: - " Given a string, allows [x] lookups for NodePos line and column numbers." - def __init__(self, text, start_pos=None): - # Find the length of each line and incrementally sum all of the lengths - # to determine the ending position of each line. - self._start_pos = start_pos - 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] - if self._start_pos: - if line == 0: - col += self._start_pos.col - line += self._start_pos.line - return NodePos(line, col) - def to_offset(self, pos): - pos = self._to_rel_pos(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 - start, end = self._to_rel_pos(start), self._to_rel_pos(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) - def _to_rel_pos(self, pos): - " converts a position to a position relative to self._start_pos " - if not self._start_pos: - return pos - line, col = pos.line, pos.col - line -= self._start_pos.line - if line == 0: - col -= self._start_pos.col - assert line >= 0 and col >= 0 # out-of-bounds node position - return NodePos(line, col) - -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 +from .util import JSVersion def isvalidversion(jsversion): if jsversion is None: return True - return spidermonkey.is_valid_version(jsversion.version) + return jsengine.parser.is_valid_version(jsversion.version) def findpossiblecomments(script, node_positions): pos = 0 @@ -168,31 +33,18 @@ def findpossiblecomments(script, node_positions): comment_text = script[match.start():match.end()] if comment_text.startswith('/*'): comment_text = comment_text[2:-2] - opcode = 'JSOP_C_COMMENT' + opcode = op.C_COMMENT else: comment_text = comment_text[2:] - opcode = 'JSOP_CPP_COMMENT' - opcode = opcode[5:].lower() + opcode = op.CPP_COMMENT start_offset = match.start() end_offset = match.end()-1 start_pos = node_positions.from_offset(start_offset) end_pos = node_positions.from_offset(end_offset) - kwargs = { - 'kind': '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) + comment_node = ParseNode(kind.COMMENT, opcode, start_pos, end_pos, + comment_text, []) comments.append(comment_node) # Start searching immediately after the start of the comment in case @@ -203,28 +55,23 @@ def parse(script, jsversion, error_callback, startpos=None): """ All node positions will be relative to startpos. This allows scripts to be embedded in a file (for example, HTML). """ - def _wrapped_callback(line, col, msg): - assert msg.startswith('JSMSG_') - msg = msg[6:].lower() - error_callback(line, col, msg) - startpos = startpos or NodePos(0,0) jsversion = jsversion or JSVersion.default() - assert isvalidversion(jsversion) - return spidermonkey.parse(script, jsversion.version, jsversion.e4x, - _Node, _wrapped_callback, - startpos.line, startpos.col) + assert isvalidversion(jsversion), jsversion + if jsversion.e4x: + error_callback(startpos.line, startpos.col, 'e4x_deprecated', {}) + return jsengine.parser.parse(script, jsversion.version, + error_callback, + startpos) def filtercomments(possible_comments, node_positions, root_node): comment_ignore_ranges = NodeRanges() def process(node): - if node.kind == tok.NUMBER: - node.atom = node_positions.text(node.start_pos(), node.end_pos()) - elif node.kind == tok.STRING or \ + if node.kind == tok.STRING or \ (node.kind == tok.OBJECT and node.opcode == op.REGEXP): start_offset = node_positions.to_offset(node.start_pos()) - end_offset = node_positions.to_offset(node.end_pos()) - 1 + end_offset = node_positions.to_offset(node.end_pos()) comment_ignore_ranges.add(start_offset, end_offset) for kid in node.kids: if kid: @@ -249,7 +96,7 @@ def findcomments(script, root_node, start_pos=None): def is_compilable_unit(script, jsversion): jsversion = jsversion or JSVersion.default() assert isvalidversion(jsversion) - return spidermonkey.is_compilable_unit(script, jsversion.version, jsversion.e4x) + return jsengine.parser.is_compilable_unit(script, jsversion.version) def _dump_node(node, depth=0): if node is None: @@ -258,7 +105,7 @@ def _dump_node(node, depth=0): print else: print ' '*depth, - print '%s, %s' % (_tok_names[node.kind], _op_names[node.opcode]) + print '%s, %s' % (repr(node.kind), repr(node.opcode)) print ' '*depth, print '%s - %s' % (node.start_pos(), node.end_pos()) if hasattr(node, 'atom'): @@ -379,22 +226,21 @@ def test(self): for text, expected in tests: encountered = is_compilable_unit(text, JSVersion.default()) self.assertEquals(encountered, expected) - # NOTE: This seems like a bug. - self.assert_(is_compilable_unit("/* test", JSVersion.default())) + self.assert_(not is_compilable_unit("/* test", JSVersion.default())) class TestLineOffset(unittest.TestCase): def testErrorPos(self): def geterror(script, startpos): errors = [] - def onerror(line, col, msg): - errors.append((line, col, msg)) + def onerror(line, col, msg, msg_args): + errors.append((line, col, msg, msg_args)) parse(script, None, onerror, startpos) self.assertEquals(len(errors), 1) return errors[0] - self.assertEquals(geterror(' ?', None), (0, 1, 'syntax_error')) - self.assertEquals(geterror('\n ?', None), (1, 1, 'syntax_error')) - self.assertEquals(geterror(' ?', NodePos(1,1)), (1, 2, 'syntax_error')) - self.assertEquals(geterror('\n ?', NodePos(1,1)), (2, 1, 'syntax_error')) + self.assertEquals(geterror(' ?', None), (0, 1, 'syntax_error', {})) + self.assertEquals(geterror('\n ?', None), (1, 1, 'syntax_error', {})) + self.assertEquals(geterror(' ?', NodePos(1,1)), (1, 2, 'syntax_error', {})) + self.assertEquals(geterror('\n ?', NodePos(1,1)), (2, 1, 'syntax_error', {})) def testNodePos(self): def getnodepos(script, startpos): root = parse(script, None, None, startpos) diff --git a/javascriptlint/lint.py b/javascriptlint/lint.py index f2967a4..3966539 100644 --- a/javascriptlint/lint.py +++ b/javascriptlint/lint.py @@ -12,7 +12,8 @@ import unittest import util -from spidermonkey import tok, op +from jsengine.parser import kind as tok +from jsengine.parser import op _newline_kinds = ( 'eof', 'comma', 'dot', 'semi', 'colon', 'lc', 'rc', 'lp', 'rb', 'assign', @@ -98,6 +99,7 @@ def add_scope(self, node): def add_declaration(self, name, node, type_): assert type_ in ('arg', 'function', 'var'), \ 'Unrecognized identifier type: %s' % type_ + assert isinstance(name, basestring) self._identifiers[name] = { 'node': node, 'type': type_ @@ -341,10 +343,10 @@ def _lint_error(*args): def _lint_script_part(scriptpos, jsversion, script, script_cache, conf, ignores, report_native, report_lint, import_callback): - def parse_error(row, col, msg): + def parse_error(row, col, msg, msg_args): if not msg in ('anon_no_return_value', 'no_return_value', 'redeclared_var', 'var_hides_arg'): - parse_errors.append((jsparse.NodePos(row, col), msg)) + parse_errors.append((jsparse.NodePos(row, col), msg, msg_args)) def report(node, errname, pos=None, **errargs): if errname == 'empty_statement' and node.kind == tok.LC: @@ -413,8 +415,8 @@ def report(node, errname, pos=None, **errargs): root = jsparse.parse(script, jsversion, parse_error, scriptpos) if not root: # Report errors and quit. - for pos, msg in parse_errors: - report_native(pos, msg) + for pos, msg, msg_args in parse_errors: + report_native(pos, msg, msg_args) return comments = jsparse.filtercomments(possible_comments, node_positions, root) @@ -459,7 +461,7 @@ def report(node, errname, pos=None, **errargs): elif keyword == 'pass': passes.append(node) else: - if comment.opcode == 'c_comment': + if comment.opcode == op.C_COMMENT: # Look for nested C-style comments. nested_comment = comment.atom.find('/*') if nested_comment < 0 and comment.atom.endswith('/'): @@ -516,9 +518,9 @@ def report_lint(node, errname, pos=None, **errargs): errdesc = warnings.format_error(errname, **errargs) _report(pos or node.start_pos(), errname, errdesc, True) - def report_native(pos, errname): - # TODO: Format the error. - _report(pos, errname, errname, False) + def report_native(pos, errname, errargs): + errdesc = warnings.format_error(errname, **errargs) + _report(pos, errname, errdesc, False) def _report(pos, errname, errdesc, require_key): try: @@ -583,7 +585,7 @@ def _warn_or_declare(scope, name, type_, node, report): if other and parent_scope == scope: # Only warn about duplications in this scope. # Other scopes will be checked later. - if other.kind == tok.FUNCTION and name in other.fn_args: + if other.kind == tok.NAME and other.opcode == op.ARGNAME: report(node, 'var_hides_arg', name=name) else: report(node, 'redeclared_var', name=name) @@ -614,7 +616,9 @@ def _push_func(self, node): _warn_or_declare(scopes[-1], node.fn_name, 'function', node, report) self._push_scope(node) for var_name in node.fn_args: - scopes[-1].add_declaration(var_name, node, 'arg') + if scopes[-1].get_identifier(var_name.atom): + report(var_name, 'duplicate_formal', name=var_name.atom) + scopes[-1].add_declaration(var_name.atom, var_name, 'arg') @visitation.visit('push', tok.LEXICALSCOPE, tok.WITH) def _push_scope(self, node): diff --git a/javascriptlint/pyspidermonkey/nodepos.c b/javascriptlint/pyspidermonkey/nodepos.c deleted file mode 100644 index 3dec061..0000000 --- a/javascriptlint/pyspidermonkey/nodepos.c +++ /dev/null @@ -1,117 +0,0 @@ -/* vim: ts=4 sw=4 expandtab - */ -#include -#include "structmember.h" - -#include "nodepos.h" - -typedef struct { - PyObject_HEAD - int line; - int col; -} NodePosObject; - -static PyObject* -NodePos_new(PyTypeObject* type, PyObject* args, PyObject* kwds) -{ - NodePosObject* self; - - self = (NodePosObject*)type->tp_alloc(type, 0); - if (self == NULL) - return NULL; - - self->line = -1; - self->col = -1; - - return (PyObject*)self; -} - -static int -NodePos_init(NodePosObject* self, PyObject* args, PyObject* kwds) -{ - static char* kwlist[] = {"line", "col", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "ii", kwlist, &self->line, &self->col)) - return -1; - - return 0; -} - -static PyObject* -NodePos_str(NodePosObject* self) -{ - return PyString_FromFormat("(line %i, col %i)", self->line+1, self->col+1); -} - -static int -NodePos_compare(NodePosObject* left, NodePosObject* right) -{ - if (left->line < right->line) - return -1; - if (left->line > right->line) - return 1; - if (left->col < right->col) - return -1; - if (left->col > right->col) - return 1; - return 0; -} - -static PyMemberDef -NodePos_members[] = { - {"line", T_INT, offsetof(NodePosObject, line), 0, "zero-based line number"}, - {"col", T_INT, offsetof(NodePosObject, col), 0, "zero-based column number"}, - {NULL} /* Sentinel */ -}; - -PyTypeObject NodePosType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "pyspidermonkey.NodePos", /*tp_name*/ - sizeof(NodePosObject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - 0, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - (cmpfunc)NodePos_compare, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - (reprfunc)NodePos_str, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - "Represents zero-based line and column number.", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - NodePos_members, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)NodePos_init, /* tp_init */ - 0, /* tp_alloc */ - NodePos_new, /* tp_new */ -}; - -void -RegisterNodePosType(PyObject* module) -{ - if (PyType_Ready(&NodePosType) < 0) - return; - - Py_INCREF(&NodePosType); - PyModule_AddObject(module, "NodePos", (PyObject*)&NodePosType); -} - diff --git a/javascriptlint/pyspidermonkey/nodepos.h b/javascriptlint/pyspidermonkey/nodepos.h deleted file mode 100644 index 0189eb0..0000000 --- a/javascriptlint/pyspidermonkey/nodepos.h +++ /dev/null @@ -1,10 +0,0 @@ -/* vim: ts=4 sw=4 expandtab - */ -#ifndef NODEPOS_H -#define NODEPOS_H - -void -RegisterNodePosType(PyObject* module); - -#endif - diff --git a/javascriptlint/pyspidermonkey/pyspidermonkey.c b/javascriptlint/pyspidermonkey/pyspidermonkey.c deleted file mode 100644 index 11baf2d..0000000 --- a/javascriptlint/pyspidermonkey/pyspidermonkey.c +++ /dev/null @@ -1,596 +0,0 @@ -/* vim: ts=4 sw=4 expandtab - */ -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "nodepos.h" - -#define ARRAY_COUNT(a) (sizeof(a) / sizeof(a[0])) - -/** CONSTANTS - */ -static const char* tokens[] = { - #define TOKEN(name) #name, - #include "tokens.tbl" - #undef TOKEN -}; -JS_STATIC_ASSERT(ARRAY_COUNT(tokens) == TOK_LIMIT); - -static const char* opcodes[] = { - #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) #op, - #include - #undef OPDEF -}; -JS_STATIC_ASSERT(ARRAY_COUNT(opcodes) == JSOP_LIMIT); - -static const char *error_names[] = { - #define MSG_DEF(name, number, count, exception, format) #name, - #include - #undef MSG_DEF -}; -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) - -static jschar* -tojschar(const char* buf) { - return (jschar*)buf; -} - -static int -tojscharlen(int buflen) { - /* The buffer length shouldn't be an odd number, buf if it is, the buffer - will be truncated to exclude it. - */ - JS_STATIC_ASSERT(sizeof(char) == 1); - JS_STATIC_ASSERT(sizeof(jschar) == 2); - return buflen / 2; -} - -/** MODULE INITIALIZATION - */ - -static PyObject* -module_parse(PyObject *self, PyObject *args); - -static PyObject* -is_compilable_unit(PyObject *self, PyObject *args); - -static PyObject* -is_valid_version(PyObject *self, PyObject *args); - -static PyMethodDef module_methods[] = { - {"parse", module_parse, METH_VARARGS, - "Parses \"script\" and returns a tree of \"node_class\"."}, - - {"is_compilable_unit", is_compilable_unit, METH_VARARGS, - "Returns True if \"script\" is a compilable unit."}, - - {"is_valid_version", is_valid_version, METH_VARARGS, - "Returns True if \"strversion\" is a valid version."}, - - {NULL, NULL, 0, NULL} /* Sentinel */ -}; - -PyMODINIT_FUNC -initpyspidermonkey(void) { - 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; - } - - RegisterNodePosType(module); -} - -PyMODINIT_FUNC -initpyspidermonkey_d(void) { - initpyspidermonkey(); -} - - -/** MODULE IMPLEMENTATION - */ - -typedef struct JSContextData { - PyObject* node_class; - PyObject* error_callback; - long int first_lineno; - long int first_index; -} JSContextData; - -static long int -to_pyjsl_lineno(JSContextData* data, long int lineno) { - /* SpiderMonkey uses 1-based line numbers. */ - return lineno + data->first_lineno - 1; -} - -static long int -to_pyjsl_index(JSContextData* data, long int lineno, long int index) { - /* SpiderMonkey uses 1-based line numbers. */ - if (lineno - 1 == 0) - return index + data->first_index; - else - return index; -} - -static JSTokenPtr -to_pyjsl_pos(JSContextData* data, JSTokenPtr ptr) { - JSTokenPtr newptr = ptr; - newptr.index = to_pyjsl_index(data, ptr.lineno, ptr.index); - newptr.lineno = to_pyjsl_lineno(data, ptr.lineno); - return newptr; -} - -static void -error_reporter(JSContext* cx, const char* message, JSErrorReport* report) -{ - JSContextData* data = JS_GetContextPrivate(cx); - long int line = to_pyjsl_lineno(data, report->lineno); - long int col = -1; - - if (report->uclinebuf) { - col = report->uctokenptr - report->uclinebuf; - col = to_pyjsl_index(data, report->lineno, col); - } - - // TODO: Check return value - (void)PyObject_CallFunction(data->error_callback, "lls", - line, col, error_names[report->errorNumber]); -} - -static PyObject* -jsstring_to_py(JSString* jsstr) { - PyObject* pystr; - size_t i; - - pystr = PyUnicode_FromUnicode(NULL, jsstr->length); - if (pystr) { - for (i = 0; i < jsstr->length; i++) - PyUnicode_AS_UNICODE(pystr)[i] = jsstr->chars[i]; - } - - return pystr; -} - -static PyObject* -atom_to_string(JSAtom* atom) { - if (!ATOM_IS_STRING(atom)) - return NULL; - - return jsstring_to_py(ATOM_TO_STRING(atom)); -} - -/* returns 0 on success and -1 on failure */ -static PyObject* -jsnode_to_pynode(JSContext* context, JSParseNode* jsnode) { - JSContextData* data = JS_GetContextPrivate(context); - PyObject* pynode = NULL; - PyObject* kids = NULL; - JSTokenPtr tokenptr; - - /* TODO: make sure no tuple item already exists */ - - if (!jsnode) { - Py_INCREF(Py_None); - return Py_None; - } - - /* pass in a dictionary of options */ - pynode = PyInstance_New(data->node_class, NULL, NULL); - if (!pynode) - goto fail; - - Py_INCREF(Py_None); - if (PyObject_SetAttrString(pynode, "parent", Py_None) == -1) - goto fail; - Py_INCREF(Py_None); - if (PyObject_SetAttrString(pynode, "node_index", Py_None) == -1) - goto fail; - if (PyObject_SetAttrString(pynode, "kind", Py_BuildValue("i", TOK_TO_NUM(jsnode->pn_type))) == -1) - goto fail; - - /* pass the position */ - tokenptr = to_pyjsl_pos(data, jsnode->pn_pos.begin); - if (PyObject_SetAttrString(pynode, "_start_line", Py_BuildValue("i", tokenptr.lineno)) == -1) - goto fail; - if (PyObject_SetAttrString(pynode, "_start_col", Py_BuildValue("i", tokenptr.index)) == -1) - goto fail; - tokenptr = to_pyjsl_pos(data, jsnode->pn_pos.end); - if (PyObject_SetAttrString(pynode, "_end_line", Py_BuildValue("i", tokenptr.lineno)) == -1) - goto fail; - if (PyObject_SetAttrString(pynode, "_end_col", Py_BuildValue("i", tokenptr.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(pynode, "atom", atom_to_string(jsnode->pn_atom)) == -1) - goto fail; - } - - 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(pynode, "dval", Py_BuildValue("d", jsnode->pn_dval)) == -1) - goto fail; - } - - if (jsnode->pn_type == TOK_FUNCTION) { - JSObject* object = ATOM_TO_OBJECT(jsnode->pn_funAtom); - JSFunction* function = (JSFunction *) JS_GetPrivate(context, object); - JSScope* scope = OBJ_SCOPE(object); - JSScopeProperty* scope_property; - PyObject* fn_name; - PyObject* fn_args; - uint32 i; - JSPropertyDescArray props = {0, NULL}; - - /* get the function name */ - if (function->atom) { - fn_name = atom_to_string(function->atom); - } - else { - Py_INCREF(Py_None); - fn_name = Py_None; - } - if (PyObject_SetAttrString(pynode, "fn_name", fn_name) == -1) - goto fail; - - /* get the function arguments */ - if (!JS_GetPropertyDescArray(context, object, &props)) - props.length = 0; - - fn_args = PyTuple_New(function->nargs); - for (i = 0; i < props.length; i++) { - PyObject* name; - if ((props.array[i].flags & JSPD_ARGUMENT) == 0) - continue; - name = jsstring_to_py(JSVAL_TO_STRING(props.array[i].id)); - PyTuple_SET_ITEM(fn_args, props.array[i].slot, name); - } - - /* Duplicate parameters are not included in the desc array. Go back and add them in. */ - for (scope_property = SCOPE_LAST_PROP(scope); - scope_property != NULL; - scope_property = scope_property->parent) { - PyObject* name; - - if ((scope_property->flags & SPROP_IS_DUPLICATE) == 0) - continue; - if (PyTuple_GET_ITEM(fn_args, scope_property->shortid) != NULL) - continue; - - name = atom_to_string(JSID_TO_ATOM(scope_property->id)); - PyTuple_SET_ITEM(fn_args, (uint16)scope_property->shortid, name); - } - 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(pynode, "end_comma", end_comma) == -1) - goto fail; - } - - if (PyObject_SetAttrString(pynode, "no_semi", PyBool_FromLong(jsnode->pn_no_semi)) == -1) - goto fail; - - switch (jsnode->pn_arity) { - case PN_FUNC: - kids = PyTuple_New(1); - PyTuple_SET_ITEM(kids, 0, jsnode_to_pynode(context, jsnode->pn_body)); - break; - - case PN_LIST: { - JSParseNode* p; - int i; - kids = PyTuple_New(jsnode->pn_count); - for (i = 0, p = jsnode->pn_head; p; p = p->pn_next, i++) { - PyTuple_SET_ITEM(kids, i, jsnode_to_pynode(context, p)); - } - } - break; - - case PN_TERNARY: - kids = PyTuple_New(3); - PyTuple_SET_ITEM(kids, 0, jsnode_to_pynode(context, jsnode->pn_kid1)); - PyTuple_SET_ITEM(kids, 1, jsnode_to_pynode(context, jsnode->pn_kid2)); - PyTuple_SET_ITEM(kids, 2, jsnode_to_pynode(context, jsnode->pn_kid3)); - break; - - case PN_BINARY: - kids = PyTuple_New(2); - PyTuple_SET_ITEM(kids, 0, jsnode_to_pynode(context, jsnode->pn_left)); - PyTuple_SET_ITEM(kids, 1, jsnode_to_pynode(context, jsnode->pn_right)); - break; - - case PN_UNARY: - kids = PyTuple_New(1); - PyTuple_SET_ITEM(kids, 0, jsnode_to_pynode(context, jsnode->pn_kid)); - break; - - case PN_NAME: - kids = PyTuple_New(1); - PyTuple_SET_ITEM(kids, 0, jsnode_to_pynode(context, jsnode->pn_expr)); - break; - - case PN_NULLARY: - kids = PyTuple_New(0); - break; - } - - if (!kids) - goto fail; - - if (PyObject_SetAttrString(pynode, "kids", kids) == -1) - goto fail; - - { - int i; - for (i = 0; i < PyTuple_GET_SIZE(kids); i++) { - PyObject* kid = PyTuple_GET_ITEM(kids, i); - if (!kid) - goto fail; - if (kid == Py_None) - continue; - - Py_INCREF(pynode); - if (PyObject_SetAttrString(kid, "parent", pynode) == -1) - goto fail; - if (PyObject_SetAttrString(kid, "node_index", Py_BuildValue("i", i)) == -1) - goto fail; - } - } - - return pynode; - -fail: - if (pynode) { - Py_XDECREF(pynode); - } - return NULL; -} - -/* Returns NULL on success. Otherwise, it returns an error. - * If the error is blank, an exception will be set. - */ -static const char* create_jscontext(const char* strversion, PyObject* is_e4x, - void* ctx_data, - JSRuntime** runtime, JSContext** context, - JSObject** global) -{ - JSVersion jsversion; - - jsversion = JS_StringToVersion(strversion); - if (jsversion == JSVERSION_UNKNOWN) { - PyErr_SetString(PyExc_ValueError, "\"version\" is invalid"); - return ""; - } - - *runtime = JS_NewRuntime(8L * 1024L * 1024L); - if (*runtime == NULL) - return "cannot create runtime"; - - *context = JS_NewContext(*runtime, 8192); - if (*context == NULL) - return "cannot create context"; - - JS_SetErrorReporter(*context, error_reporter); - JS_SetContextPrivate(*context, ctx_data); - JS_ToggleOptions(*context, JSOPTION_STRICT); - if (is_e4x == Py_True) - JS_ToggleOptions(*context, JSOPTION_XML); - else if (is_e4x != Py_False) - return "e4x is not a boolean"; - JS_SetVersion(*context, jsversion); - - *global = JS_NewObject(*context, NULL, NULL, NULL); - if (*global == NULL) - return "cannot create global object"; - - if (!JS_InitStandardClasses(*context, *global)) - return "cannot initialize standard classes"; - - return NULL; -} - - -static PyObject* -module_parse(PyObject *self, PyObject *args) { - struct { - char* scriptbuf; - int scriptbuflen; - const char* jsversion; - PyObject* is_e4x; - PyObject* pynode; - - JSRuntime* runtime; - JSContext* context; - JSObject* global; - JSTokenStream* token_stream; - JSParseNode* jsnode; - - JSContextData ctx_data; - } m; - const char* error; - - memset(&m, 0, sizeof(m)); - error = "encountered an unknown error"; - - /* validate arguments */ - if (!PyArg_ParseTuple(args, "es#sO!OOll", "utf16", &m.scriptbuf, - &m.scriptbuflen, &m.jsversion, &PyBool_Type, &m.is_e4x, - &m.ctx_data.node_class, &m.ctx_data.error_callback, - &m.ctx_data.first_lineno, &m.ctx_data.first_index)) { - 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; - } - - error = create_jscontext(m.jsversion, m.is_e4x, &m.ctx_data, - &m.runtime, &m.context, &m.global); - if (error) - goto cleanup; - - m.token_stream = js_NewBufferTokenStream(m.context, tojschar(m.scriptbuf), - tojscharlen(m.scriptbuflen)); - if (!m.token_stream) { - error = "cannot create token stream"; - goto cleanup; - } - - m.jsnode = js_ParseTokenStream(m.context, m.global, m.token_stream); - if (!m.jsnode) { - if (!JS_ReportPendingException(m.context)) { - error = "parse error in file"; - goto cleanup; - } - } - - m.pynode = jsnode_to_pynode(m.context, m.jsnode); - if (!m.pynode) { - error = ""; - goto cleanup; - } - - error = NULL; - -cleanup: - if (m.context) - JS_DestroyContext(m.context); - if (m.runtime) - JS_DestroyRuntime(m.runtime); - if (m.scriptbuf) - PyMem_Free(m.scriptbuf); - - if (error) { - if (*error) { - PyErr_SetString(PyExc_StandardError, error); - } - return NULL; - } - return m.pynode; -} - -static PyObject* -is_compilable_unit(PyObject *self, PyObject *args) { - struct { - char* scriptbuf; - int scriptbuflen; - const char* jsversion; - PyObject* is_e4x; - JSRuntime* runtime; - JSContext* context; - JSObject* global; - JSBool is_compilable; - } m; - const char* error; - - memset(&m, 0, sizeof(m)); - error = "encountered an unknown error"; - - if (!PyArg_ParseTuple(args, "es#sO!", "utf16", &m.scriptbuf, - &m.scriptbuflen, &m.jsversion, &PyBool_Type, &m.is_e4x)) { - return NULL; - } - - error = create_jscontext(m.jsversion, m.is_e4x, NULL, - &m.runtime, &m.context, &m.global); - if (error) - goto cleanup; - - m.is_compilable = JS_UCBufferIsCompilableUnit(m.context, m.global, - tojschar(m.scriptbuf), - tojscharlen(m.scriptbuflen)); - error = NULL; - -cleanup: - if (m.context) - JS_DestroyContext(m.context); - if (m.runtime) - JS_DestroyRuntime(m.runtime); - if (m.scriptbuf) - PyMem_Free(m.scriptbuf); - - if (error) { - if (*error) - PyErr_SetString(PyExc_StandardError, error); - return NULL; - } - if (m.is_compilable) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} - -static PyObject* -is_valid_version(PyObject *self, PyObject *args) { - const char* strversion = NULL; - - if (!PyArg_ParseTuple(args, "s", &strversion)) - return NULL; - - if (JS_StringToVersion(strversion) != JSVERSION_UNKNOWN) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} - diff --git a/javascriptlint/pyspidermonkey/tokens.tbl b/javascriptlint/pyspidermonkey/tokens.tbl deleted file mode 100644 index dfc0c5e..0000000 --- a/javascriptlint/pyspidermonkey/tokens.tbl +++ /dev/null @@ -1,86 +0,0 @@ -TOKEN(EOF) -TOKEN(EOL) -TOKEN(SEMI) -TOKEN(COMMA) -TOKEN(ASSIGN) -TOKEN(HOOK) -TOKEN(COLON) -TOKEN(OR) -TOKEN(AND) -TOKEN(BITOR) -TOKEN(BITXOR) -TOKEN(BITAND) -TOKEN(EQOP) -TOKEN(RELOP) -TOKEN(SHOP) -TOKEN(PLUS) -TOKEN(MINUS) -TOKEN(STAR) -TOKEN(DIVOP) -TOKEN(UNARYOP) -TOKEN(INC) -TOKEN(DEC) -TOKEN(DOT) -TOKEN(LB) -TOKEN(RB) -TOKEN(LC) -TOKEN(RC) -TOKEN(LP) -TOKEN(RP) -TOKEN(NAME) -TOKEN(NUMBER) -TOKEN(STRING) -TOKEN(OBJECT) -TOKEN(PRIMARY) -TOKEN(FUNCTION) -TOKEN(EXPORT) -TOKEN(IMPORT) -TOKEN(IF) -TOKEN(ELSE) -TOKEN(SWITCH) -TOKEN(CASE) -TOKEN(DEFAULT) -TOKEN(WHILE) -TOKEN(DO) -TOKEN(FOR) -TOKEN(BREAK) -TOKEN(CONTINUE) -TOKEN(IN) -TOKEN(VAR) -TOKEN(WITH) -TOKEN(RETURN) -TOKEN(NEW) -TOKEN(DELETE) -TOKEN(DEFSHARP) -TOKEN(USESHARP) -TOKEN(TRY) -TOKEN(CATCH) -TOKEN(FINALLY) -TOKEN(THROW) -TOKEN(INSTANCEOF) -TOKEN(DEBUGGER) -TOKEN(XMLSTAGO) -TOKEN(XMLETAGO) -TOKEN(XMLPTAGC) -TOKEN(XMLTAGC) -TOKEN(XMLNAME) -TOKEN(XMLATTR) -TOKEN(XMLSPACE) -TOKEN(XMLTEXT) -TOKEN(XMLCOMMENT) -TOKEN(XMLCDATA) -TOKEN(XMLPI) -TOKEN(AT) -TOKEN(DBLCOLON) -TOKEN(ANYNAME) -TOKEN(DBLDOT) -TOKEN(FILTER) -TOKEN(XMLELEM) -TOKEN(XMLLIST) -TOKEN(YIELD) -TOKEN(ARRAYCOMP) -TOKEN(ARRAYPUSH) -TOKEN(LEXICALSCOPE) -TOKEN(LET) -TOKEN(BODY) -TOKEN(RESERVED) diff --git a/javascriptlint/pyspidermonkey_/__init__.py b/javascriptlint/pyspidermonkey_/__init__.py deleted file mode 100644 index 658349c..0000000 --- a/javascriptlint/pyspidermonkey_/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# vim: ts=4 sw=4 expandtab -from distutils.core import setup, Extension -import os -import sys - -# Add the bin directory to the module search path -def _get_lib_path(): - import distutils.dist - import distutils.command.build - dist = distutils.dist.Distribution() - build = distutils.command.build.build(dist) - build.finalize_options() - return os.path.join(os.path.dirname(__file__), '..', '..', - build.build_platlib, 'javascriptlint') - -sys.path.insert(0, _get_lib_path()) -try: - from pyspidermonkey import * -finally: - sys.path.pop(0) - diff --git a/javascriptlint/spidermonkey.py b/javascriptlint/spidermonkey.py deleted file mode 100644 index c4b2491..0000000 --- a/javascriptlint/spidermonkey.py +++ /dev/null @@ -1,10 +0,0 @@ -# vim: ts=4 sw=4 expandtab - -# This is a wrapper script to make it easier for development. It tries to -# import the development version first, and if that fails, it goes after the -# real version. -try: - from pyspidermonkey_ import * -except ImportError: - from pyspidermonkey import * - diff --git a/javascriptlint/warnings.py b/javascriptlint/warnings.py index 7f596e9..f69c2b3 100644 --- a/javascriptlint/warnings.py +++ b/javascriptlint/warnings.py @@ -21,9 +21,10 @@ def warning_name(node): import util import visitation -from spidermonkey import tok, op +from jsengine.parser import kind as tok +from jsengine.parser import op -_ALL_TOKENS = tuple(filter(lambda x: x != tok.EOF, tok.__dict__.values())) +_ALL_TOKENS = tok.__dict__.values() def _get_assigned_lambda(node): """ Given a node "x = function() {}", returns "function() {}". @@ -53,6 +54,7 @@ def _get_assigned_lambda(node): 'use_of_label': 'use of label', 'misplaced_regex': 'regular expressions should be preceded by a left parenthesis, assignment, colon, or comma', 'assign_to_function_call': 'assignment to a function call', + 'equal_as_assign': 'test for equality (==) mistyped as assignment (=)?', 'ambiguous_else_stmt': 'the else statement could be matched with one of multiple if statements (use curly braces to indicate intent', 'block_without_braces': 'block statement without curly braces', 'ambiguous_nested_stmt': 'block statements containing block statements should use curly braces to resolve ambiguity', @@ -70,6 +72,7 @@ def _get_assigned_lambda(node): 'leading_decimal_point': 'leading decimal point may indicate a number or an object member', 'trailing_decimal_point': 'trailing decimal point may indicate a number or an object member', 'octal_number': 'leading zeros make an octal number', + 'trailing_comma': 'extra comma is not recommended in object initializers', 'trailing_comma_in_array': 'extra comma is not recommended in array initializers', 'useless_quotes': 'the quotation marks are unnecessary', 'mismatch_ctrl_comments': 'mismatched control comment; "ignore" and "end" control comments must have a one-to-one correspondence', @@ -99,8 +102,20 @@ def _get_assigned_lambda(node): 'incorrect_version': 'Expected /*jsl:content-type*/ control comment. The script was parsed with the wrong version.', } +errors = { + 'e4x_deprecated': 'e4x is deprecated', + 'semi_before_stmnt': 'missing semicolon before statement', + 'syntax_error': 'syntax error', + 'expected_tok': 'expected token: {token}', + 'unexpected_char': 'unexpected character: {char}', +} + def format_error(errname, **errargs): - errdesc = warnings[errname] + if errname in errors: + errdesc = errors[errname] + else: + errdesc = warnings[errname] + try: errdesc = re.sub(r"{(\w+)}", lambda match: errargs[match.group(1)], errdesc) except (TypeError, KeyError): @@ -295,7 +310,16 @@ def misplaced_regex(node): @lookfor(tok.ASSIGN) def assign_to_function_call(node): - if node.kids[0].kind == tok.LP: + kid = node.kids[0] + # Unpack parens. + while kid.kind == tok.RP: + kid, = kid.kids + if kid.kind == tok.LP: + raise LintWarning, node + +@lookfor(tok.ASSIGN) +def equal_as_assign(node): + if not node.parent.kind in (tok.SEMI, tok.RESERVED, tok.RP, tok.COMMA): raise LintWarning, node @lookfor(tok.IF) @@ -492,6 +516,11 @@ def octal_number(node): if _octal_regexp.match(node.atom): raise LintWarning, node +@lookfor(tok.RC) +def trailing_comma(node): + if node.end_comma: + raise LintWarning, node + @lookfor(tok.RB) def trailing_comma_in_array(node): if node.end_comma: diff --git a/jsengine/__init__.py b/jsengine/__init__.py new file mode 100644 index 0000000..b60947b --- /dev/null +++ b/jsengine/__init__.py @@ -0,0 +1,21 @@ +# vim: sw=4 ts=4 et + +_MESSAGES = ( + 'eof', + 'semi_before_stmnt', + 'syntax_error', + 'unterminated_comment', + 'expected_tok', + 'unexpected_char', +) + +class JSSyntaxError(BaseException): + def __init__(self, pos, msg, msg_args=None): + assert msg in _MESSAGES, msg + self.pos = pos + self.msg = msg + self.msg_args = msg_args or {} + def __unicode__(self): + return '%s: %s' % (self.pos, self.msg) + def __repr__(self): + return 'JSSyntaxError(%r, %r, %r)' % (self.pos, self.msg. self.msg_args) diff --git a/jsengine/parser/__init__.py b/jsengine/parser/__init__.py new file mode 100644 index 0000000..e2ca85c --- /dev/null +++ b/jsengine/parser/__init__.py @@ -0,0 +1,924 @@ +# vim: sw=4 ts=4 et +import unittest + +from jsengine.tokenizer import tok +from jsengine import tokenizer + +from jsengine import JSSyntaxError +from _constants_kind import kind +from _constants_op import op + +from jsengine.structs import * + +_VERSIONS = [ + "default", + "1.0", + "1.1", + "1.2", + "1.3", + "1.4", + "1.5", + "1.6", + "1.7", +] + +def _auto_semicolon(t, kind_, op_, startpos, endpos, atom, kids): + nosemi = False + if t.peek_sameline().tok not in (tok.EOF, tok.EOL, tok.RBRACE): + x = t.advance() + if x.tok != tok.SEMI: + raise JSSyntaxError(x.startpos, 'semi_before_stmnt') + endpos = x.endpos + else: + nosemi = True + return ParseNode(kind_, op_, startpos, endpos, atom, kids, nosemi) + +def _function_arglist(t): + fn_args = [] + if t.peek().tok != tok.RPAREN: + while True: + x = t.expect(tok.NAME) + fn_args.append(ParseNode(kind.NAME, op.ARGNAME, + x.startpos, + x.endpos, x.atom, [])) + if t.peek().tok == tok.COMMA: + t.advance() + else: + break + return fn_args + +def _primary_expression(t): + x = t.next_withregexp() + if x.tok == tok.THIS: + return ParseNode(kind.PRIMARY, op.THIS, x.startpos, x.endpos, None, []) + elif x.tok == tok.NAME: + return ParseNode(kind.NAME, op.NAME, x.startpos, x.endpos, x.atom, [None]) + elif x.tok == tok.NULL: + return ParseNode(kind.PRIMARY, op.NULL, x.startpos, x.endpos, None, []) + elif x.tok == tok.TRUE: + return ParseNode(kind.PRIMARY, op.TRUE, x.startpos, x.endpos, None, []) + elif x.tok == tok.FALSE: + return ParseNode(kind.PRIMARY, op.FALSE, x.startpos, x.endpos, None, []) + elif x.tok == tok.STRING: + return ParseNode(kind.STRING, op.STRING, x.startpos, x.endpos, x.atom, []) + elif x.tok == tok.REGEXP: + return ParseNode(kind.OBJECT, op.REGEXP, x.startpos, x.endpos, None, []) + elif x.tok == tok.NUMBER: + return ParseNode(kind.NUMBER, None, x.startpos, x.endpos, x.atom, []) + elif x.tok == tok.LBRACKET: + startpos = x.startpos + items = [] + end_comma = None + if t.peek().tok != tok.RBRACKET: + while True: + # Conditionally add a value. If it isn't followed by a comma, + # quit in order to force an RBRACKET. + if t.peek().tok == tok.COMMA: + items.append(None) + else: + items.append(_assignment_expression(t, True)) + if not t.peek().tok == tok.COMMA: + break + + # Expect a comma and use it if the value was missing. + x = t.expect(tok.COMMA) + comma = ParseNode(kind.COMMA, None, + x.startpos, x.endpos, None, []) + items[-1] = items[-1] or comma + + # Check for the end. + if t.peek().tok == tok.RBRACKET: + end_comma = comma + break + endpos = t.expect(tok.RBRACKET).endpos + return ParseNode(kind.RB, None, startpos, endpos, None, items, + end_comma=end_comma) + elif x.tok == tok.LBRACE: + startpos = x.startpos + kids = [] + # TODO: get/set + end_comma = None + while True: + x = t.peek() + if x.tok == tok.RBRACE: + break + elif x.tok == tok.STRING: + t.expect(tok.STRING) + key = ParseNode(kind.STRING, None, x.startpos, + x.endpos, x.atom, []) + elif x.tok == tok.NUMBER: + t.expect(tok.NUMBER) + key = ParseNode(kind.NUMBER, None, x.startpos, + x.endpos, x.atom, []) + else: + x = t.expect_identifiername() + key = ParseNode(kind.NAME, None, x.startpos, x.endpos, + x.atom, []) + t.expect(tok.COLON) + value = _assignment_expression(t, True) + kids.append(ParseNode(kind.COLON, None, key.startpos, + value.endpos, None, [key, value])) + if t.peek().tok == tok.COMMA: + x = t.advance() + end_comma = ParseNode(kind.COMMA, None, + x.startpos, x.endpos, None, []) + else: + end_comma = None + break + endpos = t.expect(tok.RBRACE).endpos + return ParseNode(kind.RC, None, startpos, endpos, None, kids, + end_comma=end_comma) + elif x.tok == tok.LPAREN: + startpos = x.startpos + kid = _expression(t, True) + endpos = t.expect(tok.RPAREN).endpos + return ParseNode(kind.RP, None, startpos, endpos, None, [kid]) + else: + raise JSSyntaxError(x.startpos, 'syntax_error') + +def _function_declaration(t, named_opcode): + node = _function_expression(t, named_opcode) + + # Convert anonymous functions in expressions. + if node.opcode == op.ANONFUNOBJ: + node = _auto_semicolon(t, kind.SEMI, None, node.startpos, node.endpos, + None, [node]) + return node + + +def _function_expression(t, named_opcode): + startpos = t.expect(tok.FUNCTION).startpos + if t.peek().tok == tok.NAME: + fn_name = t.expect(tok.NAME).atom + opcode = named_opcode + else: + fn_name = None + opcode = op.ANONFUNOBJ + t.expect(tok.LPAREN) + fn_args = _function_arglist(t) + t.expect(tok.RPAREN) + fn_body_startpos = t.expect(tok.LBRACE).startpos + kids = _sourceelements(t, tok.RBRACE) + fn_body_endpos = t.expect(tok.RBRACE).endpos + fn_body = ParseNode(kind.LC, None, fn_body_startpos, + fn_body_endpos, None, kids) + return ParseNode(kind.FUNCTION, + op.ANONFUNOBJ if fn_name is None else op.NAMEDFUNOBJ, + startpos, fn_body.endpos, + fn_name, [fn_body], fn_args=fn_args) + +def _argument_list(t): + args = [] + if t.peek().tok != tok.RPAREN: + while True: + args.append(_assignment_expression(t, True)) + if t.peek().tok == tok.COMMA: + t.advance() + else: + break + return args + +def _new_expression(t): + startpos = t.expect(tok.NEW).startpos + expr = _member_expression(t) + # If no (), this is a variant of the NewExpression + if t.peek().tok == tok.LPAREN: + t.expect(tok.LPAREN) + args = _argument_list(t) + endpos = t.expect(tok.RPAREN).endpos + else: + args = [] + endpos = expr.endpos + return ParseNode(kind.NEW, op.NEW, startpos, endpos, + None, [expr] + args) + +def _member_expression(t, _recurse=True): + x = t.peek() + if x.tok == tok.NEW: + kid = _new_expression(t) + elif x.tok == tok.FUNCTION: + kid = _function_expression(t, op.NAMEDFUNOBJ) + else: + kid = _primary_expression(t) + + while True: + if t.peek().tok == tok.LBRACKET: + t.advance() + expr = _expression(t, True) + endpos = t.expect(tok.RBRACKET).endpos + kid = ParseNode(kind.LB, op.GETELEM, kid.startpos, endpos, + None, [kid, expr]) + elif t.peek().tok == tok.DOT: + t.advance() + expr = t.expect_identifiername() + kid = ParseNode(kind.DOT, op.GETPROP, kid.startpos, expr.endpos, + expr.atom, [kid]) + else: + return kid + +def _call_expression(t): + expr = _member_expression(t) + if t.peek().tok != tok.LPAREN: + return expr + + while True: + x = t.peek() + if x.tok == tok.LPAREN: + t.expect(tok.LPAREN) + args = _argument_list(t) + endpos = t.expect(tok.RPAREN).endpos + expr = ParseNode(kind.LP, op.CALL, expr.startpos, + endpos, None, [expr] + args) + elif x.tok == tok.LBRACKET: + t.expect(tok.LBRACKET) + lookup = _expression(t, True) + endpos = t.expect(tok.RBRACKET).endpos + expr = ParseNode(kind.LB, op.GETELEM, + expr.startpos, endpos, + None, [expr, lookup]) + elif x.tok == tok.DOT: + t.expect(tok.DOT) + lookup = t.expect_identifiername() + expr = ParseNode(kind.DOT, op.GETPROP, + expr.startpos, lookup.endpos, + lookup.atom, [expr]) + else: + return expr + +def _lefthandside_expression(t): + kid = _call_expression(t) + kid._lefthandside = True + return kid + +def _postfix_expression(t): + kid = _lefthandside_expression(t) + if t.peek_sameline().tok == tok.INC: + endpos = t.expect(tok.INC).endpos + if kid.kind == kind.DOT and kid.opcode == op.GETPROP: + opcode = op.PROPINC + else: + opcode = op.NAMEINC + return ParseNode(kind.INC, opcode, + kid.startpos, endpos, None, [kid]) + elif t.peek_sameline().tok == tok.DEC: + endpos = t.expect(tok.DEC).endpos + return ParseNode(kind.DEC, op.NAMEDEC, + kid.startpos, endpos, None, [kid]) + else: + return kid + +_UNARY = { + tok.DELETE: (kind.DELETE, None), + tok.VOID: (kind.UNARYOP, op.VOID), + tok.TYPEOF: (kind.UNARYOP, op.TYPEOF), + tok.INC: (kind.INC, op.INCNAME), + tok.DEC: (kind.DEC, op.DECNAME), + tok.ADD: (kind.UNARYOP, op.POS), + tok.SUB: (kind.UNARYOP, op.NEG), + tok.BIT_NOT: (kind.UNARYOP, op.BITNOT), + tok.LOGICAL_NOT: (kind.UNARYOP, op.NOT), +} +def _unary_expression(t): + x = t.peek() + if x.tok in _UNARY: + kind_, op_ = _UNARY[x.tok] + startpos = t.advance().startpos + kid = _unary_expression(t) + return ParseNode(kind_, op_, startpos, kid.endpos, None, [kid]) + else: + return _postfix_expression(t) + +def _binary_expression(t, dict_, child_expr_callback): + expr = child_expr_callback(t) + while True: + x = t.peek() + try: + kind_, op_ = dict_[x.tok] + except KeyError: + return expr + + kids = [expr] + while t.peek().tok == x.tok: + t.advance() + kids.append(child_expr_callback(t)) + expr = ParseNode(kind_, op_, + kids[0].startpos, kids[1].endpos, + None, kids) + +_MULTIPLICATIVE = { + tok.MUL: (kind.STAR, op.MUL), + tok.DIV: (kind.DIVOP, op.DIV), + tok.MOD: (kind.DIVOP, op.MOD), +} +def _multiplicative_expression(t): + return _binary_expression(t, _MULTIPLICATIVE, _unary_expression) + +_ADDITIVE = { + tok.ADD: (kind.PLUS, op.ADD), + tok.SUB: (kind.MINUS, op.SUB), +} +def _additive_expression(t): + return _binary_expression(t, _ADDITIVE, + _multiplicative_expression) + +_SHIFT = { + tok.LSHIFT: (kind.SHOP, op.LSH), + tok.RSHIFT: (kind.SHOP, op.RSH), + tok.URSHIFT: (kind.SHOP, op.URSH), +} +def _shift_expression(t): + return _binary_expression(t, _SHIFT, + _additive_expression) + +_RELATIONAL_NOIN = { + tok.LT: (kind.RELOP, op.LT), + tok.GT: (kind.RELOP, op.GT), + tok.LE: (kind.RELOP, op.LE), + tok.GE: (kind.RELOP, op.GE), + tok.INSTANCEOF: (kind.INSTANCEOF, op.INSTANCEOF), +} +_RELATIONAL_IN = dict(_RELATIONAL_NOIN) +_RELATIONAL_IN.update({ + tok.IN: (kind.IN, op.IN), +}) +def _relational_expression(t, allowin): + return _binary_expression(t, _RELATIONAL_IN if allowin else _RELATIONAL_NOIN, + _shift_expression) + +_EQUALITY = { + tok.EQ: (kind.EQOP, op.EQ), + tok.NE: (kind.EQOP, op.NE), + tok.EQ_STRICT: (kind.EQOP, op.NEW_EQ), + tok.NE_STRICT: (kind.EQOP, op.NEW_NE), +} +def _equality_expression(t, allowin): + return _binary_expression(t, _EQUALITY, + lambda t: _relational_expression(t, allowin)) + +def _bitwise_and_expression(t, allowin): + left = _equality_expression(t, allowin) + while t.peek().tok == tok.BIT_AND: + t.advance() + right = _equality_expression(t, allowin) + left = ParseNode(kind.BITAND, op.BITAND, + left.startpos, right.endpos, + None, [left, right]) + return left + +def _bitwise_xor_expression(t, allowin): + left = _bitwise_and_expression(t, allowin) + while t.peek().tok == tok.BIT_XOR: + t.advance() + right = _bitwise_and_expression(t, allowin) + left = ParseNode(kind.BITXOR, op.BITXOR, + left.startpos, right.endpos, + None, [left, right]) + return left + +def _bitwise_or_expression(t, allowin): + left = _bitwise_xor_expression(t, allowin) + while t.peek().tok == tok.BIT_OR: + t.advance() + right = _bitwise_xor_expression(t, allowin) + left = ParseNode(kind.BITOR, op.BITOR, + left.startpos, right.endpos, + None, [left, right]) + return left + +def _logical_and_expression(t, allowin): + exprs = [] + while True: + exprs.append(_bitwise_or_expression(t, allowin)) + if t.peek().tok == tok.LOGICAL_AND: + t.expect(tok.LOGICAL_AND) + else: + break + + while len(exprs) > 1: + right = exprs.pop() + left = exprs[-1] + exprs[-1] = ParseNode(kind.AND, op.AND, + left.startpos, right.endpos, + None, [left, right]) + return exprs[0] + +def _logical_or_expression(t, allowin): + exprs = [] + while True: + exprs.append(_logical_and_expression(t, allowin)) + if t.peek().tok == tok.LOGICAL_OR: + t.expect(tok.LOGICAL_OR) + else: + break + + while len(exprs) > 1: + right = exprs.pop() + left = exprs[-1] + exprs[-1] = ParseNode(kind.OR, op.OR, + left.startpos, right.endpos, + None, [left, right]) + return exprs[0] + +def _conditional_expression(t, allowin): + kid = _logical_or_expression(t, allowin) + if t.peek().tok == tok.QUESTION: + t.expect(tok.QUESTION) + if_ = _assignment_expression(t, True) + t.expect(tok.COLON) + else_ = _assignment_expression(t, allowin) + return ParseNode(kind.HOOK, None, + kid.startpos, else_.endpos, + None, [kid, if_, else_]) + else: + return kid + +_ASSIGNS = { + tok.ASSIGN: (kind.ASSIGN, None), + tok.ASSIGN_URSHIFT: (kind.ASSIGN, op.URSH), + tok.ASSIGN_LSHIFT: (kind.ASSIGN, op.LSH), + tok.ASSIGN_RSHIFT: (kind.ASSIGN, op.RSH), + tok.ASSIGN_ADD: (kind.ASSIGN, op.ADD), + tok.ASSIGN_SUB: (kind.ASSIGN, op.SUB), + tok.ASSIGN_MUL: (kind.ASSIGN, op.MUL), + tok.ASSIGN_MOD: (kind.ASSIGN, op.MOD), + tok.ASSIGN_BIT_AND: (kind.ASSIGN, op.BITAND), + tok.ASSIGN_BIT_OR: (kind.ASSIGN, op.BITOR), + tok.ASSIGN_BIT_XOR: (kind.ASSIGN, op.BITXOR), + tok.ASSIGN_DIV: (kind.ASSIGN, op.DIV), +} +def _assignment_expression(t, allowin): + left = _conditional_expression(t, allowin) + if t.peek().tok in _ASSIGNS: + kid = left + while kid.kind == kind.RP: + kid, = kid.kids + if kid.kind == kind.NAME: + assert kid.opcode == op.NAME + kid.opcode = op.SETNAME + elif kid.kind == kind.DOT: + assert kid.opcode == op.GETPROP, left.op + kid.opcode = op.SETPROP + elif kid.kind == kind.LB: + assert kid.opcode == op.GETELEM + kid.opcode = op.SETELEM + elif kid.kind == kind.LP: + assert kid.opcode == op.CALL + kid.opcode = op.SETCALL + else: + raise JSSyntaxError(left.startpos, 'invalid_assign') + kind_, op_ = _ASSIGNS[t.peek().tok] + t.advance() + right = _assignment_expression(t, allowin) + return ParseNode(kind_, op_, + left.startpos, right.endpos, None, [left, right]) + else: + return left + +def _expression(t, allowin): + items = [] + items.append(_assignment_expression(t, allowin)) + while t.peek().tok == tok.COMMA: + t.advance() + items.append(_assignment_expression(t, allowin)) + if len(items) > 1: + return ParseNode(kind.COMMA, None, items[0].startpos, + items[-1].endpos, None, items) + else: + return items[0] + +def _variable_declaration(t, allowin): + nodes = [] + while True: + x = t.expect(tok.NAME) + value = None + if t.peek().tok == tok.ASSIGN: + t.advance() + value = _assignment_expression(t, allowin) + nodes.append(ParseNode(kind.NAME, op.SETNAME if value else op.NAME, + x.startpos, + value.endpos if value else x.endpos, + x.atom, [value])) + + if t.peek().tok == tok.COMMA: + t.advance() + else: + return nodes + +def _block_statement(t): + kids = [] + startpos = t.expect(tok.LBRACE).startpos + while t.peek().tok != tok.RBRACE: + kids.append(_statement(t)) + endpos = t.expect(tok.RBRACE).endpos + return ParseNode(kind.LC, None, startpos, endpos, None, kids) + +def _empty_statement(t): + # EMPTY STATEMENT + x = t.expect(tok.SEMI) + return ParseNode(kind.SEMI, None, x.startpos, x.endpos, None, [None]) + +def _var_statement(t): + # VARIABLE STATEMENT + startpos = t.expect(tok.VAR).startpos + nodes = _variable_declaration(t, True) + return _auto_semicolon(t, kind.VAR, op.DEFVAR, + startpos, nodes[-1].endpos, None, nodes) + +def _if_statement(t): + # IF STATEMENT + startpos = t.expect(tok.IF).startpos + t.expect(tok.LPAREN) + condition = _expression(t, True) + t.expect(tok.RPAREN) + if_body = _statement(t) + if t.peek().tok == tok.ELSE: + t.advance() + else_body = _statement(t) + else: + else_body = None + endpos = else_body.endpos if else_body else if_body.endpos + return ParseNode(kind.IF, None, startpos, + endpos, None, [condition, if_body, else_body]) + +def _do_statement(t): + startpos = t.expect(tok.DO).startpos + code = _statement(t) + t.expect(tok.WHILE) + t.expect(tok.LPAREN) + expr = _expression(t, True) + endtoken = t.expect(tok.RPAREN) + return _auto_semicolon(t, kind.DO, None, + startpos, endtoken.endpos, None, [code, expr]) + +def _while_statement(t): + startpos = t.expect(tok.WHILE).startpos + t.expect(tok.LPAREN) + expr = _expression(t, True) + t.expect(tok.RPAREN) + code = _statement(t) + return ParseNode(kind.WHILE, None, + startpos, code.endpos, None, [expr, code]) + +def _for_statement(t): + for_startpos = t.expect(tok.FOR).startpos + t.expect(tok.LPAREN) + + for_exprs = [] + if t.peek().tok == tok.VAR: + var_startpos = t.advance().startpos + kids = _variable_declaration(t, False) + vars = ParseNode(kind.VAR, op.DEFVAR, var_startpos, kids[-1].endpos, + None, kids) + + if t.peek().tok == tok.IN: + t.advance() + in_ = _expression(t, True) + for_exprs = [vars, in_] + else: + for_exprs = [vars, None, None] + else: + if t.peek().tok != tok.SEMI: + expr = _expression(t, False) + else: + expr = None + + if t.peek().tok == tok.IN: + t.advance() + vars = expr + in_ = _expression(t, True) + for_exprs = [vars, in_] + else: + for_exprs = [expr, None, None] + + if len(for_exprs) == 2: + condition = ParseNode(kind.IN, None, for_exprs[0].startpos, + for_exprs[-1].endpos, None, for_exprs) + else: + x = t.expect(tok.SEMI) + if t.peek().tok != tok.SEMI: + for_exprs[1] = _expression(t, True) + t.expect(tok.SEMI) + if t.peek().tok != tok.RPAREN: + for_exprs[2] = _expression(t, True) + condition = ParseNode(kind.RESERVED, None, None, None, + None, for_exprs) + + t.expect(tok.RPAREN) + body = _statement(t) + return ParseNode(kind.FOR, + op.FORIN if condition.kind == kind.IN else None, + for_startpos, body.endpos, + None, [condition, body]) + +def _continue_statement(t): + endtoken = t.expect(tok.CONTINUE) + startpos = endtoken.startpos + + if t.peek_sameline().tok == tok.NAME: + endtoken = t.expect(tok.NAME) + name = endtoken.atom + else: + name = None + # TODO: Validate Scope Labels + return _auto_semicolon(t, kind.CONTINUE, None, startpos, endtoken.endpos, name, []) + +def _break_statement(t): + endtoken = t.expect(tok.BREAK) + startpos = endtoken.startpos + + if t.peek_sameline().tok == tok.NAME: + endtoken = t.expect(tok.NAME) + name = endtoken.atom + else: + name = None + # TODO: Validate Scope Labels + return _auto_semicolon(t, kind.BREAK, None, startpos, endtoken.endpos, name, []) + +def _return_statement(t): + endtoken = t.expect(tok.RETURN) + startpos = endtoken.startpos + + if t.peek_sameline().tok not in (tok.EOF, tok.EOL, tok.SEMI): + expr = _expression(t, True) + endtoken = expr + else: + expr = None + # TODO: Validate Scope Labels + return _auto_semicolon(t, kind.RETURN, None, startpos, endtoken.endpos, + None, [expr]) + +def _with_statement(t): + startpos = t.expect(tok.WITH).startpos + t.expect(tok.LPAREN) + expr = _expression(t, True) + t.expect(tok.RPAREN) + body = _statement(t) + return ParseNode(kind.WITH, None, startpos, body.endpos, None, [expr, body]) + +def _switch_statement(t): + switch_startpos = t.expect(tok.SWITCH).startpos + t.expect(tok.LPAREN) + expr = _expression(t, True) + t.expect(tok.RPAREN) + lc_startpos = t.expect(tok.LBRACE).startpos + cases = [] + while t.peek().tok != tok.RBRACE: + case_kind = None + case_expr = None + if t.peek().tok == tok.CASE: + case_startpos = t.advance().startpos + case_kind = kind.CASE + case_expr = _expression(t, True) + elif t.peek().tok == tok.DEFAULT: + case_startpos = t.advance().startpos + case_kind = kind.DEFAULT + else: + raise JSSyntaxError(t.peek().startpos, 'invalid_case') + + case_endpos = t.expect(tok.COLON).endpos + + statements = [] + while t.peek().tok not in (tok.DEFAULT, tok.CASE, tok.RBRACE): + statements.append(_statement(t)) + if statements: + statements_startpos = statements[0].startpos + statements_endpos = statements[-1].endpos + case_endpos = statements[-1].endpos + else: + statements_startpos = case_endpos + statements_endpos = case_endpos + + cases.append(ParseNode(case_kind, None, case_startpos, case_endpos, + None, [ + case_expr, + ParseNode(kind.LC, None, statements_startpos, + statements_endpos, None, statements) + ])) + + rc_endpos = t.expect(tok.RBRACE).endpos + return ParseNode(kind.SWITCH, None, switch_startpos, rc_endpos, + None, [expr, + ParseNode(kind.LC, None, lc_startpos, rc_endpos, None, cases)]) + +def _throw_statement(t): + # TODO: Validate Scope + startpos = t.expect(tok.THROW).startpos + if t.peek_sameline().tok == tok.EOL: + raise JSSyntaxError(t.peek_sameline().startpos, 'expected_statement') + expr = _expression(t, True) + return _auto_semicolon(t, kind.THROW, op.THROW, startpos, expr.endpos, + None, [expr]) + +def _try_statement(t): + try_startpos = t.expect(tok.TRY).startpos + + try_node = _block_statement(t) + catch_node = None + finally_node = None + try_endpos = None + + if t.peek().tok == tok.CATCH: + catch_startpos = t.advance().startpos + t.expect(tok.LPAREN) + x = t.expect(tok.NAME) + catch_expr = ParseNode(kind.NAME, None, x.startpos, x.endpos, + x.atom, [None]) + t.expect(tok.RPAREN) + catch_block = _block_statement(t) + catch_endpos = catch_block.endpos + catch_node = \ + ParseNode(kind.RESERVED, None, None, None, None, [ + ParseNode(kind.LEXICALSCOPE, op.LEAVEBLOCK, + catch_startpos, catch_endpos, None, [ + ParseNode(kind.CATCH, None, catch_startpos, + catch_endpos, None, + [catch_expr, None, catch_block]) + ]) + ]) + try_endpos = catch_endpos + + if t.peek().tok == tok.FINALLY: + t.advance() + finally_node = _block_statement(t) + try_endpos = finally_node.endpos + + if not catch_node and not finally_node: + raise JSSyntaxError(try_endpos, 'invalid_catch') + + return ParseNode(kind.TRY, None, try_startpos, try_endpos, + None, + [try_node, catch_node, finally_node]) + +def _statement(t): + # TODO: Labelled Statement + x = t.peek() + if x.tok == tok.LBRACE: + return _block_statement(t) + elif x.tok == tok.SEMI: + return _empty_statement(t) + elif x.tok == tok.VAR: + return _var_statement(t) + elif x.tok == tok.IF: + return _if_statement(t) + elif x.tok == tok.DO: + return _do_statement(t) + elif x.tok == tok.WHILE: + return _while_statement(t) + elif x.tok == tok.FOR: + return _for_statement(t) + elif x.tok == tok.CONTINUE: + return _continue_statement(t) + elif x.tok == tok.BREAK: + return _break_statement(t) + elif x.tok == tok.RETURN: + return _return_statement(t) + elif x.tok == tok.WITH: + return _with_statement(t) + elif x.tok == tok.SWITCH: + return _switch_statement(t) + elif x.tok == tok.THROW: + return _throw_statement(t) + elif x.tok == tok.TRY: + return _try_statement(t) + elif x.tok == tok.EOF: + raise JSSyntaxError(x.startpos, 'eof') + elif x.tok == tok.FUNCTION: + return _function_declaration(t, op.CLOSURE) #TODO: warn, since this is not reliable + + elif x.tok not in (tok.LBRACE, tok.FUNCTION): + expr = _expression(t, True) + if expr.kind == tok.NAME and t.peek().tok == tok.COLON: + t.expect(tok.COLON) + stmt = _statement(t) + return ParseNode(kind.COLON, op.NAME, expr.startpos, + stmt.endpos, expr.atom, [stmt]) + + return _auto_semicolon(t, kind.SEMI, None, expr.startpos, expr.endpos, + None, [expr]) + else: + raise JSSyntaxError(x.startpos, 'syntax_error') + +def _sourceelements(t, end_tok): + nodes = [] + while True: + x = t.peek() + if x.tok == tok.FUNCTION: + nodes.append(_function_declaration(t, None)) + elif x.tok == end_tok: + return nodes + else: + nodes.append(_statement(t)) + +def parsestring(s, startpos=None): + stream = tokenizer.TokenStream(s, startpos) + t = tokenizer.Tokenizer(stream) + nodes = _sourceelements(t, tok.EOF) + lc_endpos = t.expect(tok.EOF).endpos + lc_startpos = nodes[-1].startpos if nodes else lc_endpos + return ParseNode(kind.LC, None, lc_startpos, lc_endpos, None, nodes) + +def is_valid_version(version): + return version in _VERSIONS + +def _validate(node, depth=0): + for kid in node.kids: + if kid: + assert kid.parent is node + _validate(kid, depth+1) + +def parse(script, jsversion, + error_callback, startpos): + # TODO: respect version + assert is_valid_version(jsversion) + try: + root = parsestring(script, startpos) + except JSSyntaxError as error: + error_callback(error.pos.line, error.pos.col, error.msg, error.msg_args) + return None + _validate(root) + return root + +def is_compilable_unit(script, jsversion): + # TODO: respect version + assert is_valid_version(jsversion) + try: + parsestring(script) + except JSSyntaxError as error: + return error.msg not in ('eof', 'unterminated_comment') + return True + +class TestParser(unittest.TestCase): + def testCompilableUnit(self): + self.assert_(is_compilable_unit('', 'default')) + self.assert_(is_compilable_unit('/**/', 'default')) + self.assert_(not is_compilable_unit('/*', 'default')) + def testUnterminatedComment(self): + try: + parsestring('/*') + except JSSyntaxError as error: + self.assertEqual(error.pos, NodePos(0,1)) + else: + self.assert_(False) + def testObjectEndComma(self): + root = parsestring('a={a:1,}') + node, = root.kids + self.assertEquals(node.kind, kind.SEMI) + node, = node.kids + self.assertEquals(node.kind, kind.ASSIGN) + left, right = node.kids + self.assertEquals(left.atom, 'a') + self.assertEquals(right.kind, kind.RC) + node = right.end_comma + self.assertEquals(node.kind, tok.COMMA) + self.assertEquals(node.startpos, NodePos(0, 6)) + self.assertEquals(node.endpos, NodePos(0, 6)) + def _testArrayEndComma(self, script, col): + root = parsestring(script) + node, = root.kids + self.assertEquals(node.kind, kind.SEMI) + node, = node.kids + self.assertEquals(node.kind, kind.ASSIGN) + left, right = node.kids + self.assertEquals(left.atom, 'a') + self.assertEquals(right.kind, kind.RB) + node = right.end_comma + self.assertEquals(node is None, col is None) + if col is None: + self.assert_(node is None) + else: + self.assertEquals(node.kind, tok.COMMA) + self.assertEquals(node.startpos, NodePos(0, col)) + self.assertEquals(node.endpos, NodePos(0, col)) + def testArrayEndComma(self): + self._testArrayEndComma('a=[,]', 3) + self._testArrayEndComma('a=[a,]', 4) + self._testArrayEndComma('a=[a,b,c]', None) + def _testArrayCommas(self, script, items, end_comma): + root = parsestring(script) + node, = root.kids + self.assertEquals(node.kind, kind.SEMI) + node, = node.kids + self.assertEquals(node.kind, kind.ASSIGN) + left, right = node.kids + self.assertEquals(left.atom, 'a') + self.assertEquals(right.kind, kind.RB) + node = right + self.assertEquals(len(node.kids), len(items)) + for kid, item in zip(node.kids, items): + self.assertEquals(kid.atom, item) + self.assertEquals(bool(node.end_comma), end_comma) + def testArrayCommas(self): + self._testArrayCommas('a=[]', [], False) + self._testArrayCommas('a=[,]', [None], True) + self._testArrayCommas('a=[,,]', [None, None], True) + self._testArrayCommas('a=[,1]', [None, '1'], False) + self._testArrayCommas('a=[,,1]', [None, None, '1'], False) + self._testArrayCommas('a=[1,,1]', ['1', None, '1'], False) + self._testArrayCommas('a=[,1,]', [None, '1'], True) + def testParseArray(self): + try: + parsestring('a=[1 1]') + except JSSyntaxError as error: + pass + else: + self.assert_(False) diff --git a/jsengine/parser/_constants_kind.py b/jsengine/parser/_constants_kind.py new file mode 100644 index 0000000..657c5ec --- /dev/null +++ b/jsengine/parser/_constants_kind.py @@ -0,0 +1,79 @@ +# vim: sw=4 ts=4 et + +_KINDS = [ + 'AND', + 'BITAND', + 'BITOR', + 'BITXOR', + 'CATCH', + 'COMMENT', + 'DELETE', + 'DIVOP', + 'DOT', + 'EQ', + 'FINALLY', + 'FUNCTION', + 'HOOK', + 'IF', + 'IN', + 'INC', + 'INSTANCEOF', + 'LB', + 'LC', + 'LEXICALSCOPE', + 'LP', + 'MINUS', + 'NAME', + 'NEW', + 'OBJECT', + 'OR', + 'PLUS', + 'PRIMARY', + 'RB', + 'RC', + 'RELOP', + 'RESERVED', + 'RP', + 'SEMI', + 'SHOP', + 'STAR', + 'TRY', + 'UNARYOP', + 'VAR', + 'ASSIGN', + 'CASE', + 'COLON', + 'DEFAULT', + 'EQOP', + 'OBJECT', + 'RELOP', + 'SWITCH', + 'WITH', + 'WHILE', + 'DO', + 'FOR', + 'COMMA', + 'DEC', + 'BREAK', + 'CONTINUE', + 'THROW', + 'RETURN', + 'UNARYOP', + 'LP', + 'NUMBER', + 'RB', + 'STRING', + 'YIELD', # TODO +] +class _Kind(str): + def __repr__(self): + return 'kind.%s' % self + +class _Kinds: + def __init__(self): + for kind in _KINDS: + setattr(self, kind, _Kind(kind)) + def contains(self, item): + return isinstance(item, _Kind) and \ + getattr(self, item) is item +kind = _Kinds() diff --git a/jsengine/parser/_constants_op.py b/jsengine/parser/_constants_op.py new file mode 100644 index 0000000..7aae168 --- /dev/null +++ b/jsengine/parser/_constants_op.py @@ -0,0 +1,85 @@ +# vim: sw=4 ts=4 et + +_OPS = [ + 'ADD', + 'AND', + 'ANONFUNOBJ', + 'ARGNAME', + 'BITAND', + 'BITNOT', + 'BITOR', + 'BITXOR', + 'CALL', + 'C_COMMENT', + 'CLOSURE', + 'CPP_COMMENT', + 'DECNAME', + 'DEFVAR', + 'DIV', + 'EQOP', + 'FALSE', + 'FORIN', + 'GETELEM', + 'GETPROP', + 'GT', + 'GE', + 'HOOK', + 'HTMLCOMMENT', + 'IN', + 'INCNAME', + 'INSTANCEOF', + 'LEAVEBLOCK', + 'LSH', + 'LT', + 'LE', + 'MOD', + 'MUL', + 'NAME', + 'NAMEDEC', + 'NAMEINC', + 'NAMEDFUNOBJ', + 'NEG', + 'NE', + 'NEW', + 'NEW_EQ', + 'NEW_NE', + 'NOT', + 'NULL', + 'NUMBER', + 'OR', + 'POS', + 'PROPINC', + 'REGEXP', + 'RSH', + 'SETCALL', + 'SETELEM', + 'SETNAME', + 'SETPROP', + 'STRING', + 'SUB', + 'THIS', + 'TRUE', + 'THROW', + 'TYPEOF', + 'URSH', + 'VOID', + 'EQ', + 'NAME', + 'REGEXP', + 'SETNAME', + 'VOID', + 'CALL', +] +class _Op(str): + def __repr__(self): + return 'op.%s' % self + +class _Ops: + NOP = None # TODO! + def __init__(self): + for op in _OPS: + setattr(self, op, _Op(op)) + def contains(self, item): + return isinstance(item, _Op) and \ + getattr(self, item) is item +op = _Ops() diff --git a/jsengine/structs.py b/jsengine/structs.py new file mode 100644 index 0000000..88b14af --- /dev/null +++ b/jsengine/structs.py @@ -0,0 +1,196 @@ +# vim: ts=4 sw=4 expandtab +import bisect +import functools + +from parser._constants_kind import kind +from parser._constants_op import op + +class NodePositions: + " Given a string, allows [x] lookups for NodePos line and column numbers." + def __init__(self, text, start_pos=None): + # Find the length of each line and incrementally sum all of the lengths + # to determine the ending position of each line. + self._start_pos = start_pos + 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] + if self._start_pos: + if line == 0: + col += self._start_pos.col + line += self._start_pos.line + return NodePos(line, col) + def to_offset(self, pos): + pos = self._to_rel_pos(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 + start, end = self._to_rel_pos(start), self._to_rel_pos(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) + def _to_rel_pos(self, pos): + " converts a position to a position relative to self._start_pos " + if not self._start_pos: + return pos + line, col = pos.line, pos.col + line -= self._start_pos.line + if line == 0: + col -= self._start_pos.col + assert line >= 0 and col >= 0 # out-of-bounds node position + return NodePos(line, col) + +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 + +@functools.total_ordering +class NodePos: + def __init__(self, line, col): + self.line = line + self.col = col + + def __repr__(self): + return 'NodePos(%i, %i)' % (self.line, self.col) + + def __unicode__(self): + return '(line %i, col %i)' % \ + (self.line + 1, self.col + 1) + + def __lt__(self, other): + return self.line < other.line or \ + (self.line == other.line and self.col < other.col) + + def __eq__(self, other): + return self.line == other.line and self.col == other.col + +class ParseNode: + node_index = None + parent = None + def __init__(self, kind_, op_, start_pos, end_pos, atom, kids, + no_semi=False, end_comma=None, fn_args=None): + assert not kids is None + assert kind.contains(kind_) + assert op_ is None or op.contains(op_) + if kind_ == kind.RESERVED: + assert start_pos is None + assert end_pos is None + else: + assert isinstance(start_pos, NodePos), repr(start_pos) + assert isinstance(end_pos, NodePos), repr(end_pos) + assert end_comma is None or isinstance(end_comma, ParseNode) + assert (start_pos is None and end_pos is None) or start_pos <= end_pos + self.kind = kind_ + self.opcode = op_ + self.atom = atom + self.kids = kids + self._lefthandside = False + self.startpos = start_pos + self.endpos = end_pos + self.no_semi = no_semi + self.end_comma = end_comma + + for i, kid in enumerate(self.kids): + if kid: + assert isinstance(kid, ParseNode) + kid.node_index = i + kid.parent = self + if kind_ == kind.FUNCTION: #TODO + self.fn_name = self.atom + assert not fn_args is None + self.fn_args = fn_args + else: + assert fn_args is None + if self.kind == kind.NUMBER: + self.dval = float(self.atom) + + def start_pos(self): + return self.startpos + def end_pos(self): + return self.endpos + + def is_equivalent(self, other, are_functions_equiv=False): + if not other: + return False + + # Deal with nested parentheses + if self.kind == kind.RP: + return self.kids[0].is_equivalent(other, are_functions_equiv) + while other.kind == kind.RP: + other, = other.kids + + op_conversion = { + op.SETNAME: op.NAME, + op.SETPROP: op.GETPROP, + op.SETELEM: op.GETELEM, + op.SETCALL: op.CALL, + } + self_opcode = op_conversion.get(self.opcode, self.opcode) + other_opcode = op_conversion.get(other.opcode, other.opcode) + + # Bail out for functions + if not are_functions_equiv: + if self.kind == kind.FUNCTION: + return False + if self.kind == kind.LP and self_opcode == op.CALL: + return False + + if self.kind == kind.DOT and self_opcode == op.GETPROP and \ + other.kind == kind.LB and other_opcode == op.GETELEM and \ + self.atom == other.kids[1].atom and \ + self.kids[0].is_equivalent(other.kids[0], are_functions_equiv): + return True + if other.kind == kind.DOT and other_opcode == op.GETPROP and \ + self.kind == kind.LB and self_opcode == op.GETELEM and \ + self.kids[1].atom == other.atom and \ + self.kids[0].is_equivalent(other.kids[0], are_functions_equiv): + return True + + 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 (kind.NAME, kind.DOT, kind.STRING) and self.atom != other.atom: + return False + + # Check values on numbers + if self.kind == 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 diff --git a/jsengine/tokenizer/__init__.py b/jsengine/tokenizer/__init__.py new file mode 100644 index 0000000..f4e1057 --- /dev/null +++ b/jsengine/tokenizer/__init__.py @@ -0,0 +1,430 @@ +# vim: sw=4 ts=4 et +from jsengine import JSSyntaxError +from jsengine.structs import NodePositions + +_WHITESPACE = u'\u0020\t\u000B\u000C\u00A0\uFFFF' +_LINETERMINATOR = u'\u000A\u000D\u2028\u2029' +_DIGITS = u'0123456789' +_DOT_DIGITS = [u'.%s' % digit for digit in _DIGITS] +_HEX_DIGITS = _DIGITS + u'abcdefABCDEF' +_IDENT = u'abcdefghijklmnopqrstuvwxyz' + \ + u'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + \ + u'$_' + +_PUNCTUATORS = { + "<<<=": "ASSIGN_ULSHIFT", + ">>>=": "ASSIGN_URSHIFT", + "===": "EQ_STRICT", + "!==": "NE_STRICT", + ">>>": "URSHIFT", + "<<=": "ASSIGN_LSHIFT", + ">>=": "ASSIGN_RSHIFT", + "<=": "LE", + ">=": "GE", + "==": "EQ", + "!=": "NE", + "++": "INC", + "--": "DEC", + "<<": "LSHIFT", + ">>": "RSHIFT", + "&&": "LOGICAL_AND", + "||": "LOGICAL_OR", + "+=": "ASSIGN_ADD", + "-=": "ASSIGN_SUB", + "*=": "ASSIGN_MUL", + "%=": "ASSIGN_MOD", + "&=": "ASSIGN_BIT_AND", + "|=": "ASSIGN_BIT_OR", + "^=": "ASSIGN_BIT_XOR", + "/=": "ASSIGN_DIV", + "{": "LBRACE", + "}": "RBRACE", + "(": "LPAREN", + ")": "RPAREN", + "[": "LBRACKET", + "]": "RBRACKET", + ".": "DOT", + ";": "SEMI", + ",": "COMMA", + "<": "LT", + ">": "GT", + "+": "ADD", + "-": "SUB", + "*": "MUL", + "%": "MOD", + "|": "BIT_OR", + "&": "BIT_AND", + "^": "BIT_XOR", + "!": "LOGICAL_NOT", + "~": "BIT_NOT", + "?": "QUESTION", + ":": "COLON", + "=": "ASSIGN", + "/": "DIV", + "!": "LOGICAL_NOT", +} + +_KEYWORDS = dict((keyword, keyword.upper()) for keyword in [ + 'break', + 'case', + 'catch', + 'continue', + 'default', + 'delete', + 'do', + 'else', + 'false', + 'finally', + 'for', + 'function', + 'if', + 'in', + 'instanceof', + 'new', + 'null', + 'return', + 'switch', + 'this', + 'throw', + 'true', + 'typeof', + 'try', + 'var', + 'void', + 'while', + 'with', +]) + +_TOKENS = [ + 'C_COMMENT', + 'CPP_COMMENT', + 'HTML_COMMENT', + 'ERROR', + 'EOF', + 'EOL', + 'NAME', + 'NUMBER', + 'OPERATOR', + 'REGEXP', + 'SPACE', + 'STRING', +] + +class _Token(str): + pass + +class _Tokens: + def __init__(self): + for token in _TOKENS: + setattr(self, token, _Token(token)) + + for key, name in list(_KEYWORDS.items()): + _KEYWORDS[key] = _Token(name) + setattr(self, name, _KEYWORDS[key]) + + for key, name in list(_PUNCTUATORS.items()): + _PUNCTUATORS[key] = _Token(name) + setattr(self, name, _PUNCTUATORS[key]) + +tok = _Tokens() + + +_PUNCTUATOR_TREE = {} +for punctuator in _PUNCTUATORS: + d = _PUNCTUATOR_TREE + for c in punctuator: + d = d.setdefault(c, {}) + assert not None in d + d[None] = _PUNCTUATORS[punctuator] + +class Token: + def __init__(self, tok, atom=None): + self.tok = tok + self.atom = atom + self.startpos = None + self.endpos = None + def setpos(self, startpos, endpos): + self.startpos = startpos + self.endpos = endpos + def __repr__(self): + return 'Token(%r, %r)' % \ + (self.tok, self.atom) + +class TokenStream: + def __init__(self, content, startpos=None): + self._content = content + self._pos = 0 + self._watched_pos = None + self._nodepositions = NodePositions(content, startpos) + + def getpos(self, offset=0): + return self._nodepositions.from_offset(self._pos+offset) + + def watch_reads(self): + self._watched_pos = self._pos + + def get_watched_reads(self): + assert not self._watched_pos == None + s = self._content[self._watched_pos:self._pos] + self._watched_pos = None + return s + + def eof(self): + return self._pos >= len(self._content) + + def readchr(self): + if self._pos < len(self._content): + self._pos += 1 + return self._content[self._pos - 1] + raise JSSyntaxError(self.getpos(-1), 'eof') + + def readif(self, len_, seq): + s = self.peekif(len_, seq) + if s: + assert len(s) == len_ + self._pos += len_ + return s + + def peekchr(self, seq): + if self._pos < len(self._content) and self._content[self._pos] in seq: + return self._content[self._pos] + + def peekif(self, len_, seq): + """ Returns the string if found. Otherwise returns None. + """ + if self._pos + len_ <= len(self._content): + peeked = self._content[self._pos:self._pos+len_] + if peeked in seq: + return peeked + +class Tokenizer: + def __init__(self, stream): + self._stream = stream + self._peeked = [] + self._error = False + + def peek(self): + self._readahead() + return self._peeked[-1] + + def peek_sameline(self): + self._readahead() + for peek in self._peeked: + if peek.tok == tok.EOL: + return peek + else: + return peek + + def advance(self, skipspace=True, skipcomments=True): + assert not self._error + + self._readahead() + for i, peek in enumerate(self._peeked): + if not skipspace and peek.tok in (tok.EOL, tok.SPACE): + self._peeked = self._peeked[i+1:] + return peek + elif not skipcomments and peek.tok in (tok.C_COMMENT, tok.CPP_COMMENT, tok.HTML_COMMENT): + self._peeked = self._peeked[i+1:] + return peek + else: + self._peeked = [] + if peek.tok == tok.ERROR: + self._error = True + raise JSSyntaxError(peek.startpos, peek.atom or 'syntax_error') + return peek + + def next_withregexp(self): + assert not self._error + self._readahead() + if self._peeked[-1].tok == tok.DIV: + token = self._parse_rest_of_regexp() + token.setpos(self._peeked[-1].startpos, self._stream.getpos(-1)) + self._peeked = [] + if token.tok == tok.ERROR: + self._error = True + raise JSSyntaxError(peek.startpos, peek.atom or 'syntax_error') + return token + else: + return self.advance() + + def expect(self, tok): + encountered = self.advance() + if encountered.tok != tok: + raise JSSyntaxError(encountered.startpos, 'expected_tok', + { 'token': tok }) + return encountered + + def expect_identifiername(self): + encountered = self.advance() + if encountered.tok in list(_KEYWORDS.values()): + encountered.tok = tok.NAME + if encountered.tok != tok.NAME: + raise JSSyntaxError(encountered.startpos, 'syntax_error') + return encountered + + def _readahead(self): + """ Always ensure that a valid token is at the end of the queue. + """ + if self._peeked: + assert self._peeked[-1].tok not in (tok.EOL, tok.SPACE, + tok.C_COMMENT, tok.CPP_COMMENT, + tok.HTML_COMMENT) + return + while True: + startpos = self._stream.getpos() + peek = self._next() + endpos = self._stream.getpos(-1) + if peek.tok == tok.ERROR: + peek.setpos(endpos, endpos) + else: + peek.setpos(startpos, endpos) + + self._peeked.append(peek) + assert isinstance(peek.tok, _Token), repr(peek.tok) + if peek.tok not in (tok.EOL, tok.SPACE, + tok.C_COMMENT, tok.CPP_COMMENT, + tok.HTML_COMMENT): + return + + def _parse_rest_of_regexp(self): + stream = self._stream + while True: + c = stream.readchr() + if c == '\\': + c = stream.readchr() + if c == '\n': + return Token(tok.ERROR) + elif c == '[': + while True: + c = stream.readchr() + if c == '\n': + return Token(tok.ERROR) + elif c == ']': + break + elif c == '\n': + return Token(tok.ERROR) + elif c == '/': + break + + # TODO: Validate and save + while True: + c = stream.readif(1, _IDENT) + if not c: + break + + return Token(tok.REGEXP) + + def _next(self, parse_regexp=False): + stream = self._stream + + if stream.eof(): + return Token(tok.EOF) + + c = stream.readchr() + + # WHITESPACE + if c in _WHITESPACE or c in _LINETERMINATOR: + linebreak = c in _LINETERMINATOR + while True: + if stream.readif(1, _LINETERMINATOR): + linebreak = True + elif stream.readif(1, _WHITESPACE): + pass + else: + break + if linebreak: + return Token(tok.EOL) + else: + return Token(tok.SPACE) + + # COMMENTS + if c == '/': + if stream.peekchr("/"): + while not stream.eof() and not stream.peekif(1, _LINETERMINATOR): + stream.readchr() + return Token(tok.CPP_COMMENT) + if stream.peekchr("*"): + linebreak = False + while True: + if stream.eof(): + return Token(tok.ERROR, atom='unterminated_comment') + c = stream.readchr() + if c in _LINETERMINATOR: + linebreak = True + elif c == '*' and stream.readif(1, '/'): + return Token(tok.C_COMMENT) + return Token(tok.EOF) + elif c == '<': + if stream.readif(3, ('!--',)): + while not stream.eof() and not stream.peekif(1, _LINETERMINATOR): + stream.readchr() + return Token(tok.HTML_COMMENT) + + # STRING LITERALS + if c == '"' or c == "'": + # TODO: Decode + s = '' + quote = c + while True: + c = stream.readchr() + if c == '\\': + c = stream.readchr() + elif c == quote: + return Token(tok.STRING, atom=s) + s += c + + # NUMBERS + if c in _DIGITS or (c == '.' and stream.peekchr(_DIGITS)): + s = c # TODO + stream.watch_reads() + if c == '0' and stream.readif(1, 'xX'): + while stream.readif(1, _HEX_DIGITS): + pass + return Token(tok.NUMBER, atom=stream.get_watched_reads()) + + if c != '.': + while stream.readif(1, _DIGITS): + pass + stream.readif(1, '.') + + while stream.readif(1, _DIGITS): + pass + + if stream.readif(1, 'eE'): + stream.readif(1, '+-') + stream.require(_DIGITS) + while stream.readif(1, _DIGITS): + pass + + if stream.peekchr(_IDENT): + return Token(tok.ERROR) + + atom = s + stream.get_watched_reads() + return Token(tok.NUMBER, atom=atom) + + if c in _PUNCTUATOR_TREE: + d = _PUNCTUATOR_TREE[c] + while True: + c = stream.readif(1, list(d.keys())) + if c: + d = d[c] + else: + break + try: + return Token(d[None]) + except KeyError: + print('oops') + raise JSSyntaxError(stream.getpos(), 'syntax_error') + + if c in _IDENT: + s = '' + while c: + s += c + c = stream.readif(1, _IDENT + _DIGITS) + if s in _KEYWORDS: + return Token(_KEYWORDS[s], atom=s) + elif s: + return Token(tok.NAME, atom=s) + + raise JSSyntaxError(stream.getpos(), 'unexpected_char', + { 'char': c }) diff --git a/spidermonkey/README b/spidermonkey/README deleted file mode 100644 index 0e50d96..0000000 --- a/spidermonkey/README +++ /dev/null @@ -1,7 +0,0 @@ -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. - diff --git a/spidermonkey/src/Makefile b/spidermonkey/src/Makefile deleted file mode 100644 index 844d62c..0000000 --- a/spidermonkey/src/Makefile +++ /dev/null @@ -1,70 +0,0 @@ -BUILDDIR = build -CSRCS = \ - jsapi.c \ - jsarena.c \ - jsarray.c \ - jsatom.c \ - jsbool.c \ - jscntxt.c \ - jsdate.c \ - jsdbgapi.c \ - jsdhash.c \ - jsdtoa.c \ - jsemit.c \ - jsexn.c \ - jsfun.c \ - jsgc.c \ - jshash.c \ - jsinterp.c \ - jsiter.c \ - jslock.c \ - jslog2.c \ - jslong.c \ - jsmath.c \ - jsnum.c \ - jsobj.c \ - jsopcode.c \ - jsparse.c \ - jsprf.c \ - jsregexp.c \ - jsscan.c \ - jsscope.c \ - jsscript.c \ - jsstr.c \ - jsutil.c \ - jsxdrapi.c \ - jsxml.c \ - prmjtime.c - - -OBJECTS = $(CSRCS:%.c=$(BUILDDIR)/%.o) -CFLAGS += -Wall -Wno-format -O -CPPFLAGS += -DXP_UNIX -O -DXP_UNIX -DSVR4 -DSYSV -DSOLARIS \ - -DHAVE_LOCALTIME_R -UDEBUG -DNDEBUG -UDEBUG_dap -DEDITLINE \ - -I$(BUILDDIR) - -all: $(BUILDDIR)/libjs.a - -$(BUILDDIR): - mkdir -p $@ - -$(BUILDDIR)/libjs.a: $(BUILDDIR)/jsautocfg.h $(BUILDDIR)/jsautokw.h $(OBJECTS) - $(AR) rv $@ $(OBJECTS) - -$(BUILDDIR)/%.o: %.c | $(BUILDDIR) - $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $^ - -clean: - -rm -rf $(BUILDDIR) - -$(BUILDDIR)/jscpucfg: $(BUILDDIR)/jscpucfg.o - $(CC) $(CFLAGS) -o $@ $^ - -$(BUILDDIR)/jsautocfg.h: $(BUILDDIR)/jscpucfg - $(BUILDDIR)/jscpucfg > $@ - -$(BUILDDIR)/jskwgen: $(BUILDDIR)/jskwgen.o - $(CC) $(CFLAGS) -o $@ $^ - -$(BUILDDIR)/jsautokw.h: $(BUILDDIR)/jskwgen - $(BUILDDIR)/jskwgen > $@ diff --git a/spidermonkey/src/README.html b/spidermonkey/src/README.html deleted file mode 100644 index b2942e3..0000000 --- a/spidermonkey/src/README.html +++ /dev/null @@ -1,826 +0,0 @@ - - - - - - - JavaScript Reference Implementation (JSRef) README - - - -

-Table of Contents

- - - -

-Introduction

-This is the README file for the JavaScript -Reference (JSRef, now better known as SpiderMonkey) implementation. -It consists of build conventions -and instructions, source code conventions, a design walk-through, and a -brief file-by-file description of the source. -

JSRef builds a library or DLL containing the -JavaScript runtime (compiler, interpreter, decompiler, garbage collector, -atom manager, standard classes). It then compiles a small "shell" program -and links that with the library to make an interpreter that can be used -interactively and with test .js files to run scripts.  The code has -no dependencies on the rest of the Mozilla codebase. -

Quick start tip: skip to "Using the JS API" below, build the -js shell, and play with the object named "it" (start by setting 'it.noisy -= true'). -

-Build conventions (standalone JS engine and shell) -(OUT OF DATE!)

-These build directions refer only to building the standalone JavaScript -engine and shell.  To build within the browser, refer to the build -directions on the mozilla.org website. -

By default, all platforms build a version of the JS engine that is not -threadsafe.  If you require thread-safety, you must also populate -the mozilla/dist directory with NSPR -headers and libraries.  (NSPR implements a portable threading library, -among other things.  The source is downloadable via CVS -from mozilla/nsprpub.)  -Next, you must define JS_THREADSAFE when building the JS engine, -either on the command-line (gmake/nmake) or in a universal header file. -

-Windows

- -
    -
  • -Use MSVC 4.2 or 5.0.
  • - -
  • -For building from the IDE use js/src/js.mdp.  (js.mdp -is an MSVC4.2 project file, but if you load it into MSVC5, it will be converted -to the newer project file format.)  NOTE: makefile.win -is an nmake file used only for building the JS-engine in the Mozilla browser.  -Don't attempt to use it to build the standalone JS-engine.
  • - -
  • -If you prefer to build from the command-line, use 'nmake -f js.mak'
  • - -
  • -Executable shell js.exe and runtime library js32.dll -are created in either js/src/Debug or js/src/Release.
  • -
- -

-Macintosh

- -
    -
  • -Use CodeWarrior 3.x
  • - -
  • -Load the project file js:src:macbuild:JSRef.mcp and select "Make" -from the menu.
  • -
- -

-Unix

- -
    -
  • -Use 'gmake -f Makefile.ref' to build. To compile optimized code, -pass BUILD_OPT=1 on the gmake command line or preset it in the -environment or Makefile.refNOTE: -Do not attempt to use Makefile to build the standalone JavaScript engine.  -This file is used only for building the JS-engine in the Mozilla browser.
  • - -
  • -Each platform on which JS is built must have a *.mk -configuration file in the js/src/config directory.  The configuration -file specifies the compiler/linker to be used and allows for customization -of command-line options.  To date, the build system has been tested -on Solaris, AIX, HP/UX, OSF, IRIX, x86 Linux and Windows NT.
  • - -
  • -Most platforms will work with either the vendor compiler -or -gcc.  -(Except that HP builds only work using the native compiler.  gcc won't -link correctly with shared libraries on that platform.  If someone -knows a way to fix this, let us -know.)
  • - -
  • -If you define JS_LIVECONNECT, gmake will -descend into the liveconnect directory and build -LiveConnect -after building the JS engine.
  • - -
  • -To build a binary drop (a zip'ed up file of headers, libraries, binaries), -check out mozilla/config and mozilla/nsprpub/config.  -Use 'gmake -f Makefile.ref nsinstall-target all export ship'
  • -
- -

-Debugging notes

- -
    -
  • -To turn on GC instrumentation, define JS_GCMETER.
  • - -
      -
    • -To turn on GC mark-phase debugging, useful to find leaked objects by their -address, and to dump the GC heap, define GC_MARK_DEBUG. -See the code in jsgc.c around the declaration and use of -js_LiveThingToFind.
    • - -
    • -To turn on the arena package's instrumentation, define JS_ARENAMETER.
    • - -
    • -To turn on the hash table package's metering, define JS_HASHMETER.
    • -
    - -

    -Naming and coding conventions

    - -
      -
    • -Public function names begin with JS_ followed by capitalized "intercaps", -e.g. JS_NewObject.
    • - -
    • -Extern but library-private function names use a js_ prefix and -mixed case, e.g. js_SearchScope.
    • - -
    • -Most static function names have unprefixed, mixed-case names: GetChar.
    • - -
    • -But static native methods of JS objects have lowercase, underscore-separated -or intercaps names, e.g., str_indexOf.
    • - -
    • -And library-private and static data use underscores, not intercaps (but -library-private data do use a js_ prefix).
    • - -
    • -Scalar type names are lowercase and js-prefixed: jsdouble.
    • - -
    • -Aggregate type names are JS-prefixed and mixed-case: JSObject.
    • - -
    • -Macros are generally ALL_CAPS and underscored, to call out potential -side effects, multiple uses of a formal argument, etc.
    • - -
    • -Four spaces of indentation per statement nesting level.
    • - -
    • -Tabs are taken to be eight spaces, and an Emacs magic comment at the top -of each file tries to help. If you're using MSVC or similar, you'll want -to set tab width to 8, and help convert these files to be space-filled. -Do not add hard tabs to source files; do remove them -whenever possible.
    • - -
    • -DLL entry points have their return type expanded within a JS_PUBLIC_API() -macro call, to get the right Windows secret type qualifiers in the right -places for all build variants.
    • - -
    • -Callback functions that might be called from a DLL are similarly macroized -with JS_STATIC_DLL_CALLBACK (if the function otherwise would be -static to hide its name) or JS_DLL_CALLBACK (this macro takes -no type argument; it should be used after the return type and before the -function name).
    • -
    - -

    -Using the JS API

    - -

    -Starting up

    - -
        /*
    -     * Tune this to avoid wasting space for shallow stacks, while saving on
    -     * malloc overhead/fragmentation for deep or highly-variable stacks.
    -     */
    -    #define STACK_CHUNK_SIZE    8192
    -
    -    JSRuntime *rt;
    -    JSContext *cx;
    -
    -    /* You need a runtime and one or more contexts to do anything with JS. */
    -    rt = JS_NewRuntime(0x400000L);
    -    if (!rt)
    -        fail("can't create JavaScript runtime");
    -    cx = JS_NewContext(rt, STACK_CHUNK_SIZE);
    -    if (!cx)
    -        fail("can't create JavaScript context");
    -
    -    /*
    -     * The context definitely wants a global object, in order to have standard
    -     * classes and functions like Date and parseInt.  See below for details on
    -     * JS_NewObject.
    -     */
    -    JSObject *globalObj;
    -
    -    globalObj = JS_NewObject(cx, &my_global_class, 0, 0);
    -    JS_InitStandardClasses(cx, globalObj);
    - -

    -Defining objects and properties

    - -
        /* Statically initialize a class to make "one-off" objects. */
    -    JSClass my_class = {
    -        "MyClass",
    -
    -        /* All of these can be replaced with the corresponding JS_*Stub
    -           function pointers. */
    -        my_addProperty, my_delProperty, my_getProperty, my_setProperty,
    -        my_enumerate,   my_resolve,     my_convert,     my_finalize
    -    };
    -
    -    JSObject *obj;
    -
    -    /*
    -     * Define an object named in the global scope that can be enumerated by
    -     * for/in loops.  The parent object is passed as the second argument, as
    -     * with all other API calls that take an object/name pair.  The prototype
    -     * passed in is null, so the default object prototype will be used.
    -     */
    -    obj = JS_DefineObject(cx, globalObj, "myObject", &my_class, NULL,
    -                          JSPROP_ENUMERATE);
    -
    -    /*
    -     * Define a bunch of properties with a JSPropertySpec array statically
    -     * initialized and terminated with a null-name entry.  Besides its name,
    -     * each property has a "tiny" identifier (MY_COLOR, e.g.) that can be used
    -     * in switch statements (in a common my_getProperty function, for example).
    -     */
    -    enum my_tinyid {
    -        MY_COLOR, MY_HEIGHT, MY_WIDTH, MY_FUNNY, MY_ARRAY, MY_RDONLY
    -    };
    -
    -    static JSPropertySpec my_props[] = {
    -        {"color",       MY_COLOR,       JSPROP_ENUMERATE},
    -        {"height",      MY_HEIGHT,      JSPROP_ENUMERATE},
    -        {"width",       MY_WIDTH,       JSPROP_ENUMERATE},
    -        {"funny",       MY_FUNNY,       JSPROP_ENUMERATE},
    -        {"array",       MY_ARRAY,       JSPROP_ENUMERATE},
    -        {"rdonly",      MY_RDONLY,      JSPROP_READONLY},
    -        {0}
    -    };
    -
    -    JS_DefineProperties(cx, obj, my_props);
    -
    -    /*
    -     * Given the above definitions and call to JS_DefineProperties, obj will
    -     * need this sort of "getter" method in its class (my_class, above).  See
    -     * the example for the "It" class in js.c.
    -     */
    -    static JSBool
    -    my_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
    -    {
    -        if (JSVAL_IS_INT(id)) {
    -            switch (JSVAL_TO_INT(id)) {
    -              case MY_COLOR:  *vp = . . .; break;
    -              case MY_HEIGHT: *vp = . . .; break;
    -              case MY_WIDTH:  *vp = . . .; break;
    -              case MY_FUNNY:  *vp = . . .; break;
    -              case MY_ARRAY:  *vp = . . .; break;
    -              case MY_RDONLY: *vp = . . .; break;
    -            }
    -        }
    -        return JS_TRUE;
    -    }
    - -

    -Defining functions

    - -
        /* Define a bunch of native functions first: */
    -    static JSBool
    -    my_abs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
    -    {
    -        jsdouble x, z;
    -
    -        if (!JS_ValueToNumber(cx, argv[0], &x))
    -            return JS_FALSE;
    -        z = (x < 0) ? -x : x;
    -        return JS_NewDoubleValue(cx, z, rval);
    -    }
    -
    -    . . .
    -
    -    /*
    -     * Use a JSFunctionSpec array terminated with a null name to define a
    -     * bunch of native functions.
    -     */
    -    static JSFunctionSpec my_functions[] = {
    -    /*    name          native          nargs    */
    -        {"abs",         my_abs,         1},
    -        {"acos",        my_acos,        1},
    -        {"asin",        my_asin,        1},
    -        . . .
    -        {0}
    -    };
    -
    -    /*
    -     * Pass a particular object to define methods for it alone.  If you pass
    -     * a prototype object, the methods will apply to all instances past and
    -     * future of the prototype's class (see below for classes).
    -     */
    -    JS_DefineFunctions(cx, globalObj, my_functions);
    - -

    -Defining classes

    - -
        /*
    -     * This pulls together the above API elements by defining a constructor
    -     * function, a prototype object, and properties of the prototype and of
    -     * the constructor, all with one API call.
    -     *
    -     * Initialize a class by defining its constructor function, prototype, and
    -     * per-instance and per-class properties.  The latter are called "static"
    -     * below by analogy to Java.  They are defined in the constructor object's
    -     * scope, so that 'MyClass.myStaticProp' works along with 'new MyClass()'.
    -     *
    -     * JS_InitClass takes a lot of arguments, but you can pass null for any of
    -     * the last four if there are no such properties or methods.
    -     *
    -     * Note that you do not need to call JS_InitClass to make a new instance of
    -     * that class -- otherwise there would be a chicken-and-egg problem making
    -     * the global object -- but you should call JS_InitClass if you require a
    -     * constructor function for script authors to call via new, and/or a class
    -     * prototype object ('MyClass.prototype') for authors to extend with new
    -     * properties at run-time.  In general, if you want to support multiple
    -     * instances that share behavior, use JS_InitClass.
    -     */
    -    protoObj = JS_InitClass(cx, globalObj, NULL, &my_class,
    -
    -                            /* native constructor function and min arg count */
    -                            MyClass, 0,
    -
    -                            /* prototype object properties and methods -- these
    -                               will be "inherited" by all instances through
    -                               delegation up the instance's prototype link. */
    -                            my_props, my_methods,
    -
    -                            /* class constructor properties and methods */
    -                            my_static_props, my_static_methods);
    - -

    -Running scripts

    - -
        /* These should indicate source location for diagnostics. */
    -    char *filename;
    -    uintN lineno;
    -
    -    /*
    -     * The return value comes back here -- if it could be a GC thing, you must
    -     * add it to the GC's "root set" with JS_AddRoot(cx, &thing) where thing
    -     * is a JSString *, JSObject *, or jsdouble *, and remove the root before
    -     * rval goes out of scope, or when rval is no longer needed.
    -     */
    -    jsval rval;
    -    JSBool ok;
    -
    -    /*
    -     * Some example source in a C string.  Larger, non-null-terminated buffers
    -     * can be used, if you pass the buffer length to JS_EvaluateScript.
    -     */
    -    char *source = "x * f(y)";
    -
    -    ok = JS_EvaluateScript(cx, globalObj, source, strlen(source),
    -                           filename, lineno, &rval);
    -
    -    if (ok) {
    -        /* Should get a number back from the example source. */
    -        jsdouble d;
    -
    -        ok = JS_ValueToNumber(cx, rval, &d);
    -        . . .
    -    }
    - -

    -Calling functions

    - -
        /* Call a global function named "foo" that takes no arguments. */
    -    ok = JS_CallFunctionName(cx, globalObj, "foo", 0, 0, &rval);
    -
    -    jsval argv[2];
    -
    -    /* Call a function in obj's scope named "method", passing two arguments. */
    -    argv[0] = . . .;
    -    argv[1] = . . .;
    -    ok = JS_CallFunctionName(cx, obj, "method", 2, argv, &rval);
    - -

    -Shutting down

    - -
        /* For each context you've created: */
    -    JS_DestroyContext(cx);
    -
    -    /* For each runtime: */
    -    JS_DestroyRuntime(rt);
    -
    -    /* And finally: */
    -    JS_ShutDown();
    - -

    -Debugging API

    -See the trap, untrap, watch, unwatch, line2pc, and pc2line -commands in js.c. Also the (scant) comments in jsdbgapi.h. -

    -Design walk-through

    -This section must be brief for now -- it could easily turn into a book. -

    -JS "JavaScript Proper"

    -JS modules declare and implement the JavaScript compiler, interpreter, -decompiler, GC and atom manager, and standard classes. -

    JavaScript uses untyped bytecode and runtime type tagging of data values. -The jsval type is a signed machine word that contains either a -signed integer value (if the low bit is set), or a type-tagged pointer -or boolean value (if the low bit is clear). Tagged pointers all refer to -8-byte-aligned things in the GC heap. -

    Objects consist of a possibly shared structural description, called -the map or scope; and unshared property values in a vector, called the -slots. Object properties are associated with nonnegative integers stored -in jsval's, or with atoms (unique string descriptors) if named -by an identifier or a non-integral index expression. -

    Scripts contain bytecode, source annotations, and a pool of string, -number, and identifier literals. Functions are objects that extend scripts -or native functions with formal parameters, a literal syntax, and a distinct -primitive type ("function"). -

    The compiler consists of a recursive-descent parser and a random-logic -rather than table-driven lexical scanner. Semantic and lexical feedback -are used to disambiguate hard cases such as missing semicolons, assignable -expressions ("lvalues" in C parlance), etc. The parser generates bytecode -as it parses, using fixup lists for downward branches and code buffering -and rewriting for exceptional cases such as for loops. It attempts no error -recovery. The interpreter executes the bytecode of top-level scripts, and -calls itself indirectly to interpret function bodies (which are also scripts). -All state associated with an interpreter instance is passed through formal -parameters to the interpreter entry point; most implicit state is collected -in a type named JSContext. Therefore, all API and almost all other functions -in JSRef take a JSContext pointer as their first argument. -

    The decompiler translates postfix bytecode into infix source by consulting -a separate byte-sized code, called source notes, to disambiguate bytecodes -that result from more than one grammatical production. -

    The GC is a mark-and-sweep, non-conservative (exact) collector. It -can allocate only fixed-sized things -- the current size is two machine -words. It is used to hold JS object and string descriptors (but not property -lists or string bytes), and double-precision floating point numbers. It -runs automatically only when maxbytes (as passed to JS_NewRuntime()) -bytes of GC things have been allocated and another thing-allocation request -is made. JS API users should call JS_GC() or JS_MaybeGC() -between script executions or from the branch callback, as often as necessary. -

    An important point about the GC's "exactness": you must add roots for -new objects created by your native methods if you store references to them -into a non-JS structure in the malloc heap or in static data. Also, if -you make a new object in a native method, but do not store it through the -rval -result parameter (see math_abs in the "Using the JS API" section above) -so that it is in a known root, the object is guaranteed to survive only -until another new object is created. Either lock the first new object when -making two in a row, or store it in a root you've added, or store it via -rval. -See the GC tips -document for more. -

    The atom manager consists of a hash table associating strings uniquely -with scanner/parser information such as keyword type, index in script or -function literal pool, etc. Atoms play three roles in JSRef: as literals -referred to by unaligned 16-bit immediate bytecode operands, as unique -string descriptors for efficient property name hashing, and as members -of the root GC set for exact GC. -

    Native objects and methods for arrays, booleans, dates, functions, numbers, -and strings are implemented using the JS API and certain internal interfaces -used as "fast paths". -

    In general, errors are signaled by false or unoverloaded-null return -values, and are reported using JS_ReportError() or one of its -variants by the lowest level in order to provide the most detail. Client -code can substitute its own error reporting function and suppress errors, -or reflect them into Java or some other runtime system as exceptions, GUI -dialogs, etc.. -

    -File walk-through (OUT OF DATE!)

    - -

    -jsapi.c, jsapi.h

    -The public API to be used by almost all client code.  If your client -code can't make do with jsapi.h, and must reach into a friend -or private js* file, please let us know so we can extend jsapi.h -to include what you need in a fashion that we can support over the long -run. -

    -jspubtd.h, jsprvtd.h

    -These files exist to group struct and scalar typedefs so they can be used -everywhere without dragging in struct definitions from N different files. -The jspubtd.h file contains public typedefs, and is included by -jsapi.h. -The jsprvtd.h file contains private typedefs and is included by -various .h files that need type names, but not type sizes or declarations. -

    -jsdbgapi.c, jsdbgapi.h

    -The Debugging API, still very much under development. Provided so far: -
      -
    • -Traps, with which breakpoints, single-stepping, step over, step out, and -so on can be implemented. The debugger will have to consult jsopcode.def -on its own to figure out where to plant trap instructions to implement -functions like step out, but a future jsdbgapi.h will provide convenience -interfaces to do these things. At most one trap per bytecode can be set. -When a script (JSScript) is destroyed, all traps set in its bytecode -are cleared.
    • - -
    • -Watchpoints, for intercepting set operations on properties and running -a debugger-supplied function that receives the old value and a pointer -to the new one, which it can use to modify the new value being set.
    • - -
    • -Line number to PC and back mapping functions. The line-to-PC direction -"rounds" toward the next bytecode generated from a line greater than or -equal to the input line, and may return the PC of a for-loop update part, -if given the line number of the loop body's closing brace. Any line after -the last one in a script or function maps to a PC one byte beyond the last -bytecode in the script. An example, from perfect.js:
    • - -
      14   function perfect(n)
      -15   {
      -16       print("The perfect numbers up to " +  n + " are:");
      -17
      -18       // We build sumOfDivisors[i] to hold a string expression for
      -19       // the sum of the divisors of i, excluding i itself.
      -20       var sumOfDivisors = new ExprArray(n+1,1);
      -21       for (var divisor = 2; divisor <= n; divisor++) {
      -22           for (var j = divisor + divisor; j <= n; j += divisor) {
      -23               sumOfDivisors[j] += " + " + divisor;
      -24           }
      -25           // At this point everything up to 'divisor' has its sumOfDivisors
      -26           // expression calculated, so we can determine whether it's perfect
      -27           // already by evaluating.
      -28           if (eval(sumOfDivisors[divisor]) == divisor) {
      -29               print("" + divisor + " = " + sumOfDivisors[divisor]);
      -30           }
      -31       }
      -32       delete sumOfDivisors;
      -33       print("That's all.");
      -34   }
      -The line number to PC and back mappings can be tested using the js program -with the following script: -
              load("perfect.js")
      -        print(perfect)
      -        dis(perfect)
      -
      -        print()
      -        for (var ln = 0; ln <= 40; ln++) {
      -            var pc = line2pc(perfect,ln)
      -            var ln2 = pc2line(perfect,pc)
      -            print("\tline " + ln + " => pc " + pc + " => line " + ln2)
      -        }
      -The result of the for loop over lines 0 to 40 inclusive is: -
              line 0 => pc 0 => line 16
      -        line 1 => pc 0 => line 16
      -        line 2 => pc 0 => line 16
      -        line 3 => pc 0 => line 16
      -        line 4 => pc 0 => line 16
      -        line 5 => pc 0 => line 16
      -        line 6 => pc 0 => line 16
      -        line 7 => pc 0 => line 16
      -        line 8 => pc 0 => line 16
      -        line 9 => pc 0 => line 16
      -        line 10 => pc 0 => line 16
      -        line 11 => pc 0 => line 16
      -        line 12 => pc 0 => line 16
      -        line 13 => pc 0 => line 16
      -        line 14 => pc 0 => line 16
      -        line 15 => pc 0 => line 16
      -        line 16 => pc 0 => line 16
      -        line 17 => pc 19 => line 20
      -        line 18 => pc 19 => line 20
      -        line 19 => pc 19 => line 20
      -        line 20 => pc 19 => line 20
      -        line 21 => pc 36 => line 21
      -        line 22 => pc 53 => line 22
      -        line 23 => pc 74 => line 23
      -        line 24 => pc 92 => line 22
      -        line 25 => pc 106 => line 28
      -        line 26 => pc 106 => line 28
      -        line 27 => pc 106 => line 28
      -        line 28 => pc 106 => line 28
      -        line 29 => pc 127 => line 29
      -        line 30 => pc 154 => line 21
      -        line 31 => pc 154 => line 21
      -        line 32 => pc 161 => line 32
      -        line 33 => pc 172 => line 33
      -        line 34 => pc 172 => line 33
      -        line 35 => pc 172 => line 33
      -        line 36 => pc 172 => line 33
      -        line 37 => pc 172 => line 33
      -        line 38 => pc 172 => line 33
      -        line 39 => pc 172 => line 33
      -        line 40 => pc 172 => line 33
      -
    - -

    -jsconfig.h

    -Various configuration macros defined as 0 or 1 depending on how JS_VERSION -is defined (as 10 for JavaScript 1.0, 11 for JavaScript 1.1, etc.). Not -all macros are tested around related code yet. In particular, JS 1.0 support -is missing from JSRef. JS 1.2 support will appear in a future JSRef release. -
      -

    -js.c

    -The "JS shell", a simple interpreter program that uses the JS API and more -than a few internal interfaces (some of these internal interfaces could -be replaced by jsapi.h calls). The js program built from this -source provides a test vehicle for evaluating scripts and calling functions, -trying out new debugger primitives, etc. -

    -jsarray.*, jsbool.*, jdsdate.*, jsfun.*, jsmath.*, jsnum.*, jsstr.*

    -These file pairs implement the standard classes and (where they exist) -their underlying primitive types. They have similar structure, generally -starting with class definitions and continuing with internal constructors, -finalizers, and helper functions. -

    -jsobj.*, jsscope.*

    -These two pairs declare and implement the JS object system. All of the -following happen here: -
      -
    • -creating objects by class and prototype, and finalizing objects;
    • - -
    • -defining, looking up, getting, setting, and deleting properties;
    • - -
    • -creating and destroying properties and binding names to them.
    • -
    -The details of a native object's map (scope) are mostly hidden in -jsscope.[ch]. -

    -jsatom.c, jsatom.h

    -The atom manager. Contains well-known string constants, their atoms, the -global atom hash table and related state, the js_Atomize() function that -turns a counted string of bytes into an atom, and literal pool (JSAtomMap) -methods. -

    -jsgc.c, jsgc.h

    -[TBD] -

    -jsinterp.*, jscntxt.*

    -The bytecode interpreter, and related functions such as Call and AllocStack, -live in jsinterp.c. The JSContext constructor and destructor are -factored out into jscntxt.c for minimal linking when the compiler -part of JS is split from the interpreter part into a separate program. -

    -jsemit.*, jsopcode.tbl, jsopcode.*, jsparse.*, jsscan.*, jsscript.*

    -Compiler and decompiler modules. The jsopcode.tbl file is a C preprocessor -source that defines almost everything there is to know about JS bytecodes. -See its major comment for how to use it. For now, a debugger will use it -and its dependents such as jsopcode.h directly, but over time we -intend to extend jsdbgapi.h to hide uninteresting details and provide -conveniences. The code generator is split across paragraphs of code in -jsparse.c, -and the utility methods called on JSCodeGenerator appear in jsemit.c. -Source notes generated by jsparse.c and -jsemit.c are used -in jsscript.c to map line number to program counter and back. -

    -jstypes.h, jslog2.c

    -Fundamental representation types and utility macros. This file alone among -all .h files in JSRef must be included first by .c files. It is not nested -in .h files, as other prerequisite .h files generally are, since it is -also a direct dependency of most .c files and would be over-included if -nested in addition to being directly included. The one "not-quite-a-macro -macro" is the JS_CeilingLog2() function in jslog2.c. -

    -jsarena.c, jsarena.h

    -Last-In-First-Out allocation macros that amortize malloc costs and allow -for en-masse freeing. See the paper mentioned in prarena.h's major comment. -

    -jsutil.c, jsutil.h

    -The JS_ASSERT macro is used throughout JSRef source as a proof -device to make invariants and preconditions clear to the reader, and to -hold the line during maintenance and evolution against regressions or violations -of assumptions that it would be too expensive to test unconditionally at -run-time. Certain assertions are followed by run-time tests that cope with -assertion failure, but only where I'm too smart or paranoid to believe -the assertion will never fail... -

    -jsclist.h

    -Doubly-linked circular list struct and macros. -

    -jscpucfg.c

    -This standalone program generates jscpucfg.h, a header file containing -bytes per word and other constants that depend on CPU architecture and -C compiler type model. It tries to discover most of these constants by -running its own experiments on the build host, so if you are cross-compiling, -beware. -

    -prdtoa.c, prdtoa.h

    -David Gay's portable double-precision floating point to string conversion -code, with Permission To Use notice included. -

    -prhash.c, prhash.h

    -Portable, extensible hash tables. These use multiplicative hash for strength -reduction over division hash, yet with very good key distribution over -power of two table sizes. Collisions resolve via chaining, so each entry -burns a malloc and can fragment the heap. -

    -prlong.c, prlong.h

    -64-bit integer emulation, and compatible macros that use C's long long -type where it exists (my last company mapped long long to a 128-bit type, -but no real architecture does 128-bit ints yet). -

    -jsosdep.h

    -Annoying OS dependencies rationalized into a few "feature-test" macros -such as JS_HAVE_LONG_LONG. -

    -jsprf.*

    -Portable, buffer-overrun-resistant sprintf and friends. For no good reason -save lack of time, the %e, %f, and %g formats cause your system's native -sprintf, rather than JS_dtoa(), to be used. This bug doesn't affect -JSRef, because it uses its own JS_dtoa() call in jsnum.c -to convert from double to string, but it's a bug that we'll fix later, -and one you should be aware of if you intend to use a JS_*printf()  -function with your own floating type arguments - various vendor sprintf's -mishandle NaN, +/-Inf, and some even print normal floating values inaccurately. -

    -prmjtime.c, prmjtime.h

    -Time functions. These interfaces are named in a way that makes local vs. -universal time confusion likely. Caveat emptor, and we're working on it. -To make matters worse, Java (and therefore JavaScript) uses "local" time -numbers (offsets from the epoch) in its Date class. - - -

    -Additional Resources (links, API docs, and newsgroups)

    - - - - - - diff --git a/spidermonkey/src/SpiderMonkey.rsp b/spidermonkey/src/SpiderMonkey.rsp deleted file mode 100644 index 8025c6c..0000000 --- a/spidermonkey/src/SpiderMonkey.rsp +++ /dev/null @@ -1,12 +0,0 @@ -mozilla/js/src/* -mozilla/js/src/config/* -mozilla/js/src/fdlibm/* -mozilla/js/src/liveconnect/* -mozilla/js/src/liveconnect/_jni/* -mozilla/js/src/liveconnect/classes/* -mozilla/js/src/liveconnect/classes/netscape/* -mozilla/js/src/liveconnect/classes/netscape/javascript/* -mozilla/js/src/liveconnect/config/* -mozilla/js/src/liveconnect/macbuild/* -mozilla/js/src/liveconnect/macbuild/JavaSession/* -mozilla/js/src/macbuild/* diff --git a/spidermonkey/src/Y.js b/spidermonkey/src/Y.js deleted file mode 100644 index e92a65a..0000000 --- a/spidermonkey/src/Y.js +++ /dev/null @@ -1,19 +0,0 @@ -// The Y combinator, applied to the factorial function - -function factorial(proc) { - return function (n) { - return (n <= 1) ? 1 : n * proc(n-1); - } -} - -function Y(outer) { - function inner(proc) { - function apply(arg) { - return proc(proc)(arg); - } - return outer(apply); - } - return inner(inner); -} - -print("5! is " + Y(factorial)(5)); diff --git a/spidermonkey/src/js.c b/spidermonkey/src/js.c deleted file mode 100644 index fb4332f..0000000 --- a/spidermonkey/src/js.c +++ /dev/null @@ -1,3181 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS shell. - */ -#include "jsstddef.h" -#include -#include -#include -#include -#include -#include "jstypes.h" -#include "jsarena.h" -#include "jsutil.h" -#include "jsprf.h" -#include "jsapi.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsdbgapi.h" -#include "jsemit.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jslock.h" -#include "jsobj.h" -#include "jsparse.h" -#include "jsscope.h" -#include "jsscript.h" - -#ifdef PERLCONNECT -#include "perlconnect/jsperl.h" -#endif - -#ifdef LIVECONNECT -#include "jsjava.h" -#endif - -#ifdef JSDEBUGGER -#include "jsdebug.h" -#ifdef JSDEBUGGER_JAVA_UI -#include "jsdjava.h" -#endif /* JSDEBUGGER_JAVA_UI */ -#ifdef JSDEBUGGER_C_UI -#include "jsdb.h" -#endif /* JSDEBUGGER_C_UI */ -#endif /* JSDEBUGGER */ - -#ifdef XP_UNIX -#include -#include -#include -#endif - -#if defined(XP_WIN) || defined(XP_OS2) -#include /* for isatty() */ -#endif - -typedef enum JSShellExitCode { - EXITCODE_RUNTIME_ERROR = 3, - EXITCODE_FILE_NOT_FOUND = 4, - EXITCODE_OUT_OF_MEMORY = 5 -} JSShellExitCode; - -size_t gStackChunkSize = 8192; - -/* Assume that we can not use more than 5e5 bytes of C stack by default. */ -static size_t gMaxStackSize = 500000; - -static jsuword gStackBase; -int gExitCode = 0; -JSBool gQuitting = JS_FALSE; -FILE *gErrFile = NULL; -FILE *gOutFile = NULL; - -#ifdef JSDEBUGGER -static JSDContext *_jsdc; -#ifdef JSDEBUGGER_JAVA_UI -static JSDJContext *_jsdjc; -#endif /* JSDEBUGGER_JAVA_UI */ -#endif /* JSDEBUGGER */ - -static JSBool reportWarnings = JS_TRUE; -static JSBool compileOnly = JS_FALSE; - -typedef enum JSShellErrNum { -#define MSG_DEF(name, number, count, exception, format) \ - name = number, -#include "jsshell.msg" -#undef MSG_DEF - JSShellErr_Limit -#undef MSGDEF -} JSShellErrNum; - -static const JSErrorFormatString * -my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber); -static JSObject * -split_setup(JSContext *cx); - -#ifdef EDITLINE -extern char *readline(const char *prompt); -extern void add_history(char *line); -#endif - -static JSBool -GetLine(JSContext *cx, char *bufp, FILE *file, const char *prompt) { -#ifdef EDITLINE - /* - * Use readline only if file is stdin, because there's no way to specify - * another handle. Are other filehandles interactive? - */ - if (file == stdin) { - char *linep = readline(prompt); - if (!linep) - return JS_FALSE; - if (linep[0] != '\0') - add_history(linep); - strcpy(bufp, linep); - JS_free(cx, linep); - bufp += strlen(bufp); - *bufp++ = '\n'; - *bufp = '\0'; - } else -#endif - { - char line[256]; - fprintf(gOutFile, prompt); - fflush(gOutFile); - if (!fgets(line, sizeof line, file)) - return JS_FALSE; - strcpy(bufp, line); - } - return JS_TRUE; -} - -static void -Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY) -{ - JSBool ok, hitEOF; - JSScript *script; - jsval result; - JSString *str; - char buffer[4096]; - char *bufp; - int lineno; - int startline; - FILE *file; - jsuword stackLimit; - - if (forceTTY || !filename || strcmp(filename, "-") == 0) { - file = stdin; - } else { - file = fopen(filename, "r"); - if (!file) { - JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, - JSSMSG_CANT_OPEN, filename, strerror(errno)); - gExitCode = EXITCODE_FILE_NOT_FOUND; - return; - } - } - - if (gMaxStackSize == 0) { - /* - * Disable checking for stack overflow if limit is zero. - */ - stackLimit = 0; - } else { -#if JS_STACK_GROWTH_DIRECTION > 0 - stackLimit = gStackBase + gMaxStackSize; -#else - stackLimit = gStackBase - gMaxStackSize; -#endif - } - JS_SetThreadStackLimit(cx, stackLimit); - - if (!forceTTY && !isatty(fileno(file))) { - /* - * It's not interactive - just execute it. - * - * Support the UNIX #! shell hack; gobble the first line if it starts - * with '#'. TODO - this isn't quite compatible with sharp variables, - * as a legal js program (using sharp variables) might start with '#'. - * But that would require multi-character lookahead. - */ - int ch = fgetc(file); - if (ch == '#') { - while((ch = fgetc(file)) != EOF) { - if (ch == '\n' || ch == '\r') - break; - } - } - ungetc(ch, file); - script = JS_CompileFileHandle(cx, obj, filename, file); - if (script) { - if (!compileOnly) - (void)JS_ExecuteScript(cx, obj, script, &result); - JS_DestroyScript(cx, script); - } - - return; - } - - /* It's an interactive filehandle; drop into read-eval-print loop. */ - lineno = 1; - hitEOF = JS_FALSE; - do { - bufp = buffer; - *bufp = '\0'; - - /* - * Accumulate lines until we get a 'compilable unit' - one that either - * generates an error (before running out of source) or that compiles - * cleanly. This should be whenever we get a complete statement that - * coincides with the end of a line. - */ - startline = lineno; - do { - if (!GetLine(cx, bufp, file, startline == lineno ? "js> " : "")) { - hitEOF = JS_TRUE; - break; - } - bufp += strlen(bufp); - lineno++; - } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, strlen(buffer))); - - /* Clear any pending exception from previous failed compiles. */ - JS_ClearPendingException(cx); - script = JS_CompileScript(cx, obj, buffer, strlen(buffer), "typein", - startline); - if (script) { - if (!compileOnly) { - ok = JS_ExecuteScript(cx, obj, script, &result); - if (ok && result != JSVAL_VOID) { - str = JS_ValueToString(cx, result); - if (str) - fprintf(gOutFile, "%s\n", JS_GetStringBytes(str)); - else - ok = JS_FALSE; - } - } - JS_DestroyScript(cx, script); - } - } while (!hitEOF && !gQuitting); - fprintf(gOutFile, "\n"); - return; -} - -static int -usage(void) -{ - fprintf(gErrFile, "%s\n", JS_GetImplementationVersion()); - fprintf(gErrFile, "usage: js [-PswWxCi] [-b branchlimit] [-c stackchunksize] [-v version] [-f scriptfile] [-e script] [-S maxstacksize] [scriptfile] [scriptarg...]\n"); - return 2; -} - -static uint32 gBranchCount; -static uint32 gBranchLimit; - -static JSBool -my_BranchCallback(JSContext *cx, JSScript *script) -{ - if (++gBranchCount == gBranchLimit) { - if (script) { - if (script->filename) - fprintf(gErrFile, "%s:", script->filename); - fprintf(gErrFile, "%u: script branch callback (%u callbacks)\n", - script->lineno, gBranchLimit); - } else { - fprintf(gErrFile, "native branch callback (%u callbacks)\n", - gBranchLimit); - } - gBranchCount = 0; - return JS_FALSE; - } - if ((gBranchCount & 0x3fff) == 1) - JS_MaybeGC(cx); - return JS_TRUE; -} - -extern JSClass global_class; - -static int -ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc) -{ - int i, j, length; - JSObject *argsObj; - char *filename = NULL; - JSBool isInteractive = JS_TRUE; - JSBool forceTTY = JS_FALSE; - - /* - * Scan past all optional arguments so we can create the arguments object - * before processing any -f options, which must interleave properly with - * -v and -w options. This requires two passes, and without getopt, we'll - * have to keep the option logic here and in the second for loop in sync. - */ - for (i = 0; i < argc; i++) { - if (argv[i][0] != '-' || argv[i][1] == '\0') { - ++i; - break; - } - switch (argv[i][1]) { - case 'b': - case 'c': - case 'f': - case 'e': - case 'v': - case 'S': - ++i; - break; - default:; - } - } - - /* - * Create arguments early and define it to root it, so it's safe from any - * GC calls nested below, and so it is available to -f arguments. - */ - argsObj = JS_NewArrayObject(cx, 0, NULL); - if (!argsObj) - return 1; - if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(argsObj), - NULL, NULL, 0)) { - return 1; - } - - length = argc - i; - for (j = 0; j < length; j++) { - JSString *str = JS_NewStringCopyZ(cx, argv[i++]); - if (!str) - return 1; - if (!JS_DefineElement(cx, argsObj, j, STRING_TO_JSVAL(str), - NULL, NULL, JSPROP_ENUMERATE)) { - return 1; - } - } - - for (i = 0; i < argc; i++) { - if (argv[i][0] != '-' || argv[i][1] == '\0') { - filename = argv[i++]; - isInteractive = JS_FALSE; - break; - } - - switch (argv[i][1]) { - case 'v': - if (++i == argc) - return usage(); - - JS_SetVersion(cx, (JSVersion) atoi(argv[i])); - break; - - case 'w': - reportWarnings = JS_TRUE; - break; - - case 'W': - reportWarnings = JS_FALSE; - break; - - case 's': - JS_ToggleOptions(cx, JSOPTION_STRICT); - break; - - case 'x': - JS_ToggleOptions(cx, JSOPTION_XML); - break; - - case 'P': - if (JS_GET_CLASS(cx, JS_GetPrototype(cx, obj)) != &global_class) { - JSObject *gobj; - - if (!JS_SealObject(cx, obj, JS_TRUE)) - return JS_FALSE; - gobj = JS_NewObject(cx, &global_class, NULL, NULL); - if (!gobj) - return JS_FALSE; - if (!JS_SetPrototype(cx, gobj, obj)) - return JS_FALSE; - JS_SetParent(cx, gobj, NULL); - JS_SetGlobalObject(cx, gobj); - obj = gobj; - } - break; - - case 'b': - gBranchLimit = atoi(argv[++i]); - JS_SetBranchCallback(cx, my_BranchCallback); - JS_ToggleOptions(cx, JSOPTION_NATIVE_BRANCH_CALLBACK); - break; - - case 'c': - /* set stack chunk size */ - gStackChunkSize = atoi(argv[++i]); - break; - - case 'f': - if (++i == argc) - return usage(); - - Process(cx, obj, argv[i], JS_FALSE); - - /* - * XXX: js -f foo.js should interpret foo.js and then - * drop into interactive mode, but that breaks the test - * harness. Just execute foo.js for now. - */ - isInteractive = JS_FALSE; - break; - - case 'e': - { - jsval rval; - - if (++i == argc) - return usage(); - - /* Pass a filename of -e to imitate PERL */ - JS_EvaluateScript(cx, obj, argv[i], strlen(argv[i]), - "-e", 1, &rval); - - isInteractive = JS_FALSE; - break; - - } - case 'C': - compileOnly = JS_TRUE; - isInteractive = JS_FALSE; - break; - - case 'i': - isInteractive = forceTTY = JS_TRUE; - break; - - case 'S': - if (++i == argc) - return usage(); - - /* Set maximum stack size. */ - gMaxStackSize = atoi(argv[i]); - break; - - case 'z': - obj = split_setup(cx); - break; - - default: - return usage(); - } - } - - if (filename || isInteractive) - Process(cx, obj, filename, forceTTY); - return gExitCode; -} - - -static JSBool -Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - if (argc > 0 && JSVAL_IS_INT(argv[0])) - *rval = INT_TO_JSVAL(JS_SetVersion(cx, (JSVersion) JSVAL_TO_INT(argv[0]))); - else - *rval = INT_TO_JSVAL(JS_GetVersion(cx)); - return JS_TRUE; -} - -static struct { - const char *name; - uint32 flag; -} js_options[] = { - {"strict", JSOPTION_STRICT}, - {"werror", JSOPTION_WERROR}, - {"atline", JSOPTION_ATLINE}, - {"xml", JSOPTION_XML}, - {0, 0} -}; - -static JSBool -Options(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - uint32 optset, flag; - uintN i, j, found; - JSString *str; - const char *opt; - char *names; - - optset = 0; - for (i = 0; i < argc; i++) { - str = JS_ValueToString(cx, argv[i]); - if (!str) - return JS_FALSE; - opt = JS_GetStringBytes(str); - for (j = 0; js_options[j].name; j++) { - if (strcmp(js_options[j].name, opt) == 0) { - optset |= js_options[j].flag; - break; - } - } - } - optset = JS_ToggleOptions(cx, optset); - - names = NULL; - found = 0; - while (optset != 0) { - flag = optset; - optset &= optset - 1; - flag &= ~optset; - for (j = 0; js_options[j].name; j++) { - if (js_options[j].flag == flag) { - names = JS_sprintf_append(names, "%s%s", - names ? "," : "", js_options[j].name); - found++; - break; - } - } - } - if (!found) - names = strdup(""); - if (!names) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - str = JS_NewString(cx, names, strlen(names)); - if (!str) { - free(names); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - uintN i; - JSString *str; - const char *filename; - JSScript *script; - JSBool ok; - jsval result; - uint32 oldopts; - - for (i = 0; i < argc; i++) { - str = JS_ValueToString(cx, argv[i]); - if (!str) - return JS_FALSE; - argv[i] = STRING_TO_JSVAL(str); - filename = JS_GetStringBytes(str); - errno = 0; - oldopts = JS_GetOptions(cx); - JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO); - script = JS_CompileFile(cx, obj, filename); - if (!script) { - ok = JS_FALSE; - } else { - ok = !compileOnly - ? JS_ExecuteScript(cx, obj, script, &result) - : JS_TRUE; - JS_DestroyScript(cx, script); - } - JS_SetOptions(cx, oldopts); - if (!ok) - return JS_FALSE; - } - - return JS_TRUE; -} - -/* - * function readline() - * Provides a hook for scripts to read a line from stdin. - */ -static JSBool -ReadLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ -#define BUFSIZE 256 - FILE *from; - char *buf, *tmp; - size_t bufsize, buflength, gotlength; - JSString *str; - - from = stdin; - buflength = 0; - bufsize = BUFSIZE; - buf = JS_malloc(cx, bufsize); - if (!buf) - return JS_FALSE; - - while ((gotlength = - js_fgets(buf + buflength, bufsize - buflength, from)) > 0) { - buflength += gotlength; - - /* Are we done? */ - if (buf[buflength - 1] == '\n') { - buf[buflength - 1] = '\0'; - break; - } - - /* Else, grow our buffer for another pass. */ - tmp = JS_realloc(cx, buf, bufsize * 2); - if (!tmp) { - JS_free(cx, buf); - return JS_FALSE; - } - - bufsize *= 2; - buf = tmp; - } - - /* Treat the empty string specially. */ - if (buflength == 0) { - *rval = JS_GetEmptyStringValue(cx); - JS_free(cx, buf); - return JS_TRUE; - } - - /* Shrink the buffer to the real size. */ - tmp = JS_realloc(cx, buf, buflength); - if (!tmp) { - JS_free(cx, buf); - return JS_FALSE; - } - - buf = tmp; - - /* - * Turn buf into a JSString. Note that buflength includes the trailing null - * character. - */ - str = JS_NewString(cx, buf, buflength - 1); - if (!str) { - JS_free(cx, buf); - return JS_FALSE; - } - - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -Print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - uintN i, n; - JSString *str; - - for (i = n = 0; i < argc; i++) { - str = JS_ValueToString(cx, argv[i]); - if (!str) - return JS_FALSE; - fprintf(gOutFile, "%s%s", i ? " " : "", JS_GetStringBytes(str)); - } - n++; - if (n) - fputc('\n', gOutFile); - return JS_TRUE; -} - -static JSBool -Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); - -static JSBool -Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ -#ifdef LIVECONNECT - JSJ_SimpleShutdown(); -#endif - - JS_ConvertArguments(cx, argc, argv,"/ i", &gExitCode); - - gQuitting = JS_TRUE; - return JS_FALSE; -} - -static JSBool -GC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSRuntime *rt; - uint32 preBytes; - - rt = cx->runtime; - preBytes = rt->gcBytes; -#ifdef GC_MARK_DEBUG - if (argc && JSVAL_IS_STRING(argv[0])) { - char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); - FILE *file = fopen(name, "w"); - if (!file) { - fprintf(gErrFile, "gc: can't open %s: %s\n", strerror(errno)); - return JS_FALSE; - } - js_DumpGCHeap = file; - } else { - js_DumpGCHeap = stdout; - } -#endif - JS_GC(cx); -#ifdef GC_MARK_DEBUG - if (js_DumpGCHeap != stdout) - fclose(js_DumpGCHeap); - js_DumpGCHeap = NULL; -#endif - fprintf(gOutFile, "before %lu, after %lu, break %08lx\n", - (unsigned long)preBytes, (unsigned long)rt->gcBytes, -#ifdef XP_UNIX - (unsigned long)sbrk(0) -#else - 0 -#endif - ); -#ifdef JS_GCMETER - js_DumpGCStats(rt, stdout); -#endif - return JS_TRUE; -} - -static JSScript * -ValueToScript(JSContext *cx, jsval v) -{ - JSScript *script; - JSFunction *fun; - - if (!JSVAL_IS_PRIMITIVE(v) && - JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass) { - script = (JSScript *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); - } else { - fun = JS_ValueToFunction(cx, v); - if (!fun) - return NULL; - script = FUN_SCRIPT(fun); - } - return script; -} - -static JSBool -GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp, - int32 *ip) -{ - jsval v; - uintN intarg; - JSScript *script; - - *scriptp = cx->fp->down->script; - *ip = 0; - if (argc != 0) { - v = argv[0]; - intarg = 0; - if (!JSVAL_IS_PRIMITIVE(v) && - (JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_FunctionClass || - JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass)) { - script = ValueToScript(cx, v); - if (!script) - return JS_FALSE; - *scriptp = script; - intarg++; - } - if (argc > intarg) { - if (!JS_ValueToInt32(cx, argv[intarg], ip)) - return JS_FALSE; - } - } - return JS_TRUE; -} - -static JSTrapStatus -TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, - void *closure) -{ - JSString *str; - JSStackFrame *caller; - - str = (JSString *) closure; - caller = JS_GetScriptedCaller(cx, NULL); - if (!JS_EvaluateScript(cx, caller->scopeChain, - JS_GetStringBytes(str), JS_GetStringLength(str), - caller->script->filename, caller->script->lineno, - rval)) { - return JSTRAP_ERROR; - } - if (*rval != JSVAL_VOID) - return JSTRAP_RETURN; - return JSTRAP_CONTINUE; -} - -static JSBool -Trap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - JSScript *script; - int32 i; - - if (argc == 0) { - JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_TRAP_USAGE); - return JS_FALSE; - } - argc--; - str = JS_ValueToString(cx, argv[argc]); - if (!str) - return JS_FALSE; - argv[argc] = STRING_TO_JSVAL(str); - if (!GetTrapArgs(cx, argc, argv, &script, &i)) - return JS_FALSE; - return JS_SetTrap(cx, script, script->code + i, TrapHandler, str); -} - -static JSBool -Untrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSScript *script; - int32 i; - - if (!GetTrapArgs(cx, argc, argv, &script, &i)) - return JS_FALSE; - JS_ClearTrap(cx, script, script->code + i, NULL, NULL); - return JS_TRUE; -} - -static JSBool -LineToPC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSScript *script; - int32 i; - uintN lineno; - jsbytecode *pc; - - if (argc == 0) { - JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_LINE2PC_USAGE); - return JS_FALSE; - } - script = cx->fp->down->script; - if (!GetTrapArgs(cx, argc, argv, &script, &i)) - return JS_FALSE; - lineno = (i == 0) ? script->lineno : (uintN)i; - pc = JS_LineNumberToPC(cx, script, lineno); - if (!pc) - return JS_FALSE; - *rval = INT_TO_JSVAL(PTRDIFF(pc, script->code, jsbytecode)); - return JS_TRUE; -} - -static JSBool -PCToLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSScript *script; - int32 i; - uintN lineno; - - if (!GetTrapArgs(cx, argc, argv, &script, &i)) - return JS_FALSE; - lineno = JS_PCToLineNumber(cx, script, script->code + i); - if (!lineno) - return JS_FALSE; - *rval = INT_TO_JSVAL(lineno); - return JS_TRUE; -} - -#ifdef DEBUG - -static void -GetSwitchTableBounds(JSScript *script, uintN offset, - uintN *start, uintN *end) -{ - jsbytecode *pc; - JSOp op; - ptrdiff_t jmplen; - jsint low, high, n; - - pc = script->code + offset; - op = *pc; - switch (op) { - case JSOP_TABLESWITCHX: - jmplen = JUMPX_OFFSET_LEN; - goto jump_table; - case JSOP_TABLESWITCH: - jmplen = JUMP_OFFSET_LEN; - jump_table: - pc += jmplen; - low = GET_JUMP_OFFSET(pc); - pc += JUMP_OFFSET_LEN; - high = GET_JUMP_OFFSET(pc); - pc += JUMP_OFFSET_LEN; - n = high - low + 1; - break; - - case JSOP_LOOKUPSWITCHX: - jmplen = JUMPX_OFFSET_LEN; - goto lookup_table; - default: - JS_ASSERT(op == JSOP_LOOKUPSWITCH); - jmplen = JUMP_OFFSET_LEN; - lookup_table: - pc += jmplen; - n = GET_ATOM_INDEX(pc); - pc += ATOM_INDEX_LEN; - jmplen += ATOM_INDEX_LEN; - break; - } - - *start = (uintN)(pc - script->code); - *end = *start + (uintN)(n * jmplen); -} - - -/* - * SrcNotes assumes that SRC_METHODBASE should be distinguished from SRC_LABEL - * using the bytecode the source note points to. - */ -JS_STATIC_ASSERT(SRC_LABEL == SRC_METHODBASE); - -static void -SrcNotes(JSContext *cx, JSScript *script) -{ - uintN offset, delta, caseOff, switchTableStart, switchTableEnd; - jssrcnote *notes, *sn; - JSSrcNoteType type; - const char *name; - JSOp op; - jsatomid atomIndex; - JSAtom *atom; - - fprintf(gOutFile, "\nSource notes:\n"); - offset = 0; - notes = SCRIPT_NOTES(script); - switchTableEnd = switchTableStart = 0; - for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { - delta = SN_DELTA(sn); - offset += delta; - type = (JSSrcNoteType) SN_TYPE(sn); - name = js_SrcNoteSpec[type].name; - if (type == SRC_LABEL) { - /* Heavily overloaded case. */ - if (switchTableStart <= offset && offset < switchTableEnd) { - name = "case"; - } else { - op = script->code[offset]; - if (op == JSOP_GETMETHOD || op == JSOP_SETMETHOD) { - /* This is SRC_METHODBASE which we print as SRC_PCBASE. */ - type = SRC_PCBASE; - name = "methodbase"; - } else { - JS_ASSERT(op == JSOP_NOP); - } - } - } - fprintf(gOutFile, "%3u: %5u [%4u] %-8s", - PTRDIFF(sn, notes, jssrcnote), offset, delta, name); - switch (type) { - case SRC_SETLINE: - fprintf(gOutFile, " lineno %u", (uintN) js_GetSrcNoteOffset(sn, 0)); - break; - case SRC_FOR: - fprintf(gOutFile, " cond %u update %u tail %u", - (uintN) js_GetSrcNoteOffset(sn, 0), - (uintN) js_GetSrcNoteOffset(sn, 1), - (uintN) js_GetSrcNoteOffset(sn, 2)); - break; - case SRC_IF_ELSE: - fprintf(gOutFile, " else %u elseif %u", - (uintN) js_GetSrcNoteOffset(sn, 0), - (uintN) js_GetSrcNoteOffset(sn, 1)); - break; - case SRC_COND: - case SRC_WHILE: - case SRC_PCBASE: - case SRC_PCDELTA: - case SRC_DECL: - case SRC_BRACE: - fprintf(gOutFile, " offset %u", (uintN) js_GetSrcNoteOffset(sn, 0)); - break; - case SRC_LABEL: - case SRC_LABELBRACE: - case SRC_BREAK2LABEL: - case SRC_CONT2LABEL: - case SRC_FUNCDEF: { - const char *bytes; - JSFunction *fun; - JSString *str; - - atomIndex = (jsatomid) js_GetSrcNoteOffset(sn, 0); - atom = js_GetAtom(cx, &script->atomMap, atomIndex); - if (type != SRC_FUNCDEF) { - bytes = js_AtomToPrintableString(cx, atom); - } else { - fun = (JSFunction *) - JS_GetPrivate(cx, ATOM_TO_OBJECT(atom)); - str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT); - bytes = str ? JS_GetStringBytes(str) : "N/A"; - } - fprintf(gOutFile, " atom %u (%s)", (uintN)atomIndex, bytes); - break; - } - case SRC_SWITCH: - fprintf(gOutFile, " length %u", (uintN) js_GetSrcNoteOffset(sn, 0)); - caseOff = (uintN) js_GetSrcNoteOffset(sn, 1); - if (caseOff) - fprintf(gOutFile, " first case offset %u", caseOff); - GetSwitchTableBounds(script, offset, - &switchTableStart, &switchTableEnd); - break; - case SRC_CATCH: - delta = (uintN) js_GetSrcNoteOffset(sn, 0); - if (delta) { - if (script->main[offset] == JSOP_LEAVEBLOCK) - fprintf(gOutFile, " stack depth %u", delta); - else - fprintf(gOutFile, " guard delta %u", delta); - } - break; - default:; - } - fputc('\n', gOutFile); - } -} - -static JSBool -Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - uintN i; - JSScript *script; - - for (i = 0; i < argc; i++) { - script = ValueToScript(cx, argv[i]); - if (!script) - continue; - - SrcNotes(cx, script); - } - return JS_TRUE; -} - -static JSBool -TryNotes(JSContext *cx, JSScript *script) -{ - JSTryNote *tn = script->trynotes; - - if (!tn) - return JS_TRUE; - fprintf(gOutFile, "\nException table:\nstart\tend\tcatch\n"); - while (tn->start && tn->catchStart) { - fprintf(gOutFile, " %d\t%d\t%d\n", - tn->start, tn->start + tn->length, tn->catchStart); - tn++; - } - return JS_TRUE; -} - -static JSBool -Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSBool lines; - uintN i; - JSScript *script; - - if (argc > 0 && - JSVAL_IS_STRING(argv[0]) && - !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), "-l")) { - lines = JS_TRUE; - argv++, argc--; - } else { - lines = JS_FALSE; - } - for (i = 0; i < argc; i++) { - script = ValueToScript(cx, argv[i]); - if (!script) - return JS_FALSE; - - if (VALUE_IS_FUNCTION(cx, argv[i])) { - JSFunction *fun = JS_ValueToFunction(cx, argv[i]); - if (fun && (fun->flags & JSFUN_FLAGS_MASK)) { - uint16 flags = fun->flags; - fputs("flags:", stdout); - -#define SHOW_FLAG(flag) if (flags & JSFUN_##flag) fputs(" " #flag, stdout); - - SHOW_FLAG(LAMBDA); - SHOW_FLAG(SETTER); - SHOW_FLAG(GETTER); - SHOW_FLAG(BOUND_METHOD); - SHOW_FLAG(HEAVYWEIGHT); - SHOW_FLAG(THISP_STRING); - SHOW_FLAG(THISP_NUMBER); - SHOW_FLAG(THISP_BOOLEAN); - SHOW_FLAG(INTERPRETED); - -#undef SHOW_FLAG - putchar('\n'); - } - } - - if (!js_Disassemble(cx, script, lines, stdout)) - return JS_FALSE; - SrcNotes(cx, script); - TryNotes(cx, script); - } - return JS_TRUE; -} - -static JSBool -DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ -#define LINE_BUF_LEN 512 - uintN i, len, line1, line2, bupline; - JSScript *script; - FILE *file; - char linebuf[LINE_BUF_LEN]; - jsbytecode *pc, *end; - static char sep[] = ";-------------------------"; - - for (i = 0; i < argc; i++) { - script = ValueToScript(cx, argv[i]); - if (!script) - return JS_FALSE; - - if (!script || !script->filename) { - JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, - JSSMSG_FILE_SCRIPTS_ONLY); - return JS_FALSE; - } - - file = fopen(script->filename, "r"); - if (!file) { - JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, - JSSMSG_CANT_OPEN, - script->filename, strerror(errno)); - return JS_FALSE; - } - - pc = script->code; - end = pc + script->length; - - /* burn the leading lines */ - line2 = JS_PCToLineNumber(cx, script, pc); - for (line1 = 0; line1 < line2 - 1; line1++) - fgets(linebuf, LINE_BUF_LEN, file); - - bupline = 0; - while (pc < end) { - line2 = JS_PCToLineNumber(cx, script, pc); - - if (line2 < line1) { - if (bupline != line2) { - bupline = line2; - fprintf(gOutFile, "%s %3u: BACKUP\n", sep, line2); - } - } else { - if (bupline && line1 == line2) - fprintf(gOutFile, "%s %3u: RESTORE\n", sep, line2); - bupline = 0; - while (line1 < line2) { - if (!fgets(linebuf, LINE_BUF_LEN, file)) { - JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, - JSSMSG_UNEXPECTED_EOF, - script->filename); - goto bail; - } - line1++; - fprintf(gOutFile, "%s %3u: %s", sep, line1, linebuf); - } - } - - len = js_Disassemble1(cx, script, pc, - PTRDIFF(pc, script->code, jsbytecode), - JS_TRUE, stdout); - if (!len) - return JS_FALSE; - pc += len; - } - - bail: - fclose(file); - } - return JS_TRUE; -#undef LINE_BUF_LEN -} - -static JSBool -Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSBool bval; - JSString *str; - - if (argc == 0) { - *rval = BOOLEAN_TO_JSVAL(cx->tracefp != 0); - return JS_TRUE; - } - - switch (JS_TypeOfValue(cx, argv[0])) { - case JSTYPE_NUMBER: - bval = JSVAL_IS_INT(argv[0]) - ? JSVAL_TO_INT(argv[0]) - : (jsint) *JSVAL_TO_DOUBLE(argv[0]); - break; - case JSTYPE_BOOLEAN: - bval = JSVAL_TO_BOOLEAN(argv[0]); - break; - default: - str = JS_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - fprintf(gErrFile, "tracing: illegal argument %s\n", - JS_GetStringBytes(str)); - return JS_TRUE; - } - cx->tracefp = bval ? stderr : NULL; - return JS_TRUE; -} - -typedef struct DumpAtomArgs { - JSContext *cx; - FILE *fp; -} DumpAtomArgs; - -static int -DumpAtom(JSHashEntry *he, int i, void *arg) -{ - DumpAtomArgs *args = (DumpAtomArgs *)arg; - FILE *fp = args->fp; - JSAtom *atom = (JSAtom *)he; - - fprintf(fp, "%3d %08x %5lu ", - i, (uintN)he->keyHash, (unsigned long)atom->number); - if (ATOM_IS_STRING(atom)) - fprintf(fp, "\"%s\"\n", js_AtomToPrintableString(args->cx, atom)); - else if (ATOM_IS_INT(atom)) - fprintf(fp, "%ld\n", (long)ATOM_TO_INT(atom)); - else - fprintf(fp, "%.16g\n", *ATOM_TO_DOUBLE(atom)); - return HT_ENUMERATE_NEXT; -} - -static void -DumpScope(JSContext *cx, JSObject *obj, FILE *fp) -{ - uintN i; - JSScope *scope; - JSScopeProperty *sprop; - - i = 0; - scope = OBJ_SCOPE(obj); - for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { - if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop)) - continue; - fprintf(fp, "%3u %p", i, (void *)sprop); - if (JSID_IS_INT(sprop->id)) { - fprintf(fp, " [%ld]", (long)JSVAL_TO_INT(sprop->id)); - } else if (JSID_IS_ATOM(sprop->id)) { - JSAtom *atom = JSID_TO_ATOM(sprop->id); - fprintf(fp, " \"%s\"", js_AtomToPrintableString(cx, atom)); - } else { - jsval v = OBJECT_TO_JSVAL(JSID_TO_OBJECT(sprop->id)); - fprintf(fp, " \"%s\"", js_ValueToPrintableString(cx, v)); - } - -#define DUMP_ATTR(name) if (sprop->attrs & JSPROP_##name) fputs(" " #name, fp) - DUMP_ATTR(ENUMERATE); - DUMP_ATTR(READONLY); - DUMP_ATTR(PERMANENT); - DUMP_ATTR(EXPORTED); - DUMP_ATTR(GETTER); - DUMP_ATTR(SETTER); -#undef DUMP_ATTR - - fprintf(fp, " slot %lu flags %x shortid %d\n", - (unsigned long)sprop->slot, sprop->flags, sprop->shortid); - } -} - -static JSBool -DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - uintN i; - JSString *str; - const char *bytes; - JSAtom *atom; - JSObject *obj2; - JSProperty *prop; - jsval value; - - for (i = 0; i < argc; i++) { - str = JS_ValueToString(cx, argv[i]); - if (!str) - return JS_FALSE; - bytes = JS_GetStringBytes(str); - if (strcmp(bytes, "arena") == 0) { -#ifdef JS_ARENAMETER - JS_DumpArenaStats(stdout); -#endif - } else if (strcmp(bytes, "atom") == 0) { - DumpAtomArgs args; - - fprintf(gOutFile, "\natom table contents:\n"); - args.cx = cx; - args.fp = stdout; - JS_HashTableEnumerateEntries(cx->runtime->atomState.table, - DumpAtom, - &args); -#ifdef HASHMETER - JS_HashTableDumpMeter(cx->runtime->atomState.table, - DumpAtom, - stdout); -#endif - } else if (strcmp(bytes, "global") == 0) { - DumpScope(cx, cx->globalObject, stdout); - } else { - atom = js_Atomize(cx, bytes, JS_GetStringLength(str), 0); - if (!atom) - return JS_FALSE; - if (!js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &obj2, &prop)) - return JS_FALSE; - if (prop) { - OBJ_DROP_PROPERTY(cx, obj2, prop); - if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &value)) - return JS_FALSE; - } - if (!prop || !JSVAL_IS_OBJECT(value)) { - fprintf(gErrFile, "js: invalid stats argument %s\n", - bytes); - continue; - } - obj = JSVAL_TO_OBJECT(value); - if (obj) - DumpScope(cx, obj, stdout); - } - } - return JS_TRUE; -} - -#endif /* DEBUG */ - -#ifdef TEST_EXPORT -static JSBool -DoExport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSAtom *atom; - JSObject *obj2; - JSProperty *prop; - JSBool ok; - uintN attrs; - - if (argc != 2) { - JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_DOEXP_USAGE); - return JS_FALSE; - } - if (!JS_ValueToObject(cx, argv[0], &obj)) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(obj); - atom = js_ValueToStringAtom(cx, argv[1]); - if (!atom) - return JS_FALSE; - if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop)) - return JS_FALSE; - if (!prop) { - ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL, - JSPROP_EXPORTED, NULL); - } else { - ok = OBJ_GET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs); - if (ok) { - attrs |= JSPROP_EXPORTED; - ok = OBJ_SET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs); - } - OBJ_DROP_PROPERTY(cx, obj2, prop); - } - return ok; -} -#endif - -#ifdef TEST_CVTARGS -#include - -static const char * -EscapeWideString(jschar *w) -{ - static char enuf[80]; - static char hex[] = "0123456789abcdef"; - jschar u; - unsigned char b, c; - int i, j; - - if (!w) - return ""; - for (i = j = 0; i < sizeof enuf - 1; i++, j++) { - u = w[j]; - if (u == 0) - break; - b = (unsigned char)(u >> 8); - c = (unsigned char)(u); - if (b) { - if (i >= sizeof enuf - 6) - break; - enuf[i++] = '\\'; - enuf[i++] = 'u'; - enuf[i++] = hex[b >> 4]; - enuf[i++] = hex[b & 15]; - enuf[i++] = hex[c >> 4]; - enuf[i] = hex[c & 15]; - } else if (!isprint(c)) { - if (i >= sizeof enuf - 4) - break; - enuf[i++] = '\\'; - enuf[i++] = 'x'; - enuf[i++] = hex[c >> 4]; - enuf[i] = hex[c & 15]; - } else { - enuf[i] = (char)c; - } - } - enuf[i] = 0; - return enuf; -} - -#include - -static JSBool -ZZ_formatter(JSContext *cx, const char *format, JSBool fromJS, jsval **vpp, - va_list *app) -{ - jsval *vp; - va_list ap; - jsdouble re, im; - - printf("entering ZZ_formatter"); - vp = *vpp; - ap = *app; - if (fromJS) { - if (!JS_ValueToNumber(cx, vp[0], &re)) - return JS_FALSE; - if (!JS_ValueToNumber(cx, vp[1], &im)) - return JS_FALSE; - *va_arg(ap, jsdouble *) = re; - *va_arg(ap, jsdouble *) = im; - } else { - re = va_arg(ap, jsdouble); - im = va_arg(ap, jsdouble); - if (!JS_NewNumberValue(cx, re, &vp[0])) - return JS_FALSE; - if (!JS_NewNumberValue(cx, im, &vp[1])) - return JS_FALSE; - } - *vpp = vp + 2; - *app = ap; - printf("leaving ZZ_formatter"); - return JS_TRUE; -} - -static JSBool -ConvertArgs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSBool b = JS_FALSE; - jschar c = 0; - int32 i = 0, j = 0; - uint32 u = 0; - jsdouble d = 0, I = 0, re = 0, im = 0; - char *s = NULL; - JSString *str = NULL; - jschar *w = NULL; - JSObject *obj2 = NULL; - JSFunction *fun = NULL; - jsval v = JSVAL_VOID; - JSBool ok; - - if (!JS_AddArgumentFormatter(cx, "ZZ", ZZ_formatter)) - return JS_FALSE;; - ok = JS_ConvertArguments(cx, argc, argv, "b/ciujdIsSWofvZZ*", - &b, &c, &i, &u, &j, &d, &I, &s, &str, &w, &obj2, - &fun, &v, &re, &im); - JS_RemoveArgumentFormatter(cx, "ZZ"); - if (!ok) - return JS_FALSE; - fprintf(gOutFile, - "b %u, c %x (%c), i %ld, u %lu, j %ld\n", - b, c, (char)c, i, u, j); - fprintf(gOutFile, - "d %g, I %g, s %s, S %s, W %s, obj %s, fun %s\n" - "v %s, re %g, im %g\n", - d, I, s, str ? JS_GetStringBytes(str) : "", EscapeWideString(w), - JS_GetStringBytes(JS_ValueToString(cx, OBJECT_TO_JSVAL(obj2))), - fun ? JS_GetStringBytes(JS_DecompileFunction(cx, fun, 4)) : "", - JS_GetStringBytes(JS_ValueToString(cx, v)), re, im); - return JS_TRUE; -} -#endif - -static JSBool -BuildDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - char version[20] = "\n"; -#if JS_VERSION < 150 - sprintf(version, " for version %d\n", JS_VERSION); -#endif - fprintf(gOutFile, "built on %s at %s%s", __DATE__, __TIME__, version); - return JS_TRUE; -} - -static JSBool -Clear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - if (argc != 0 && !JS_ValueToObject(cx, argv[0], &obj)) - return JS_FALSE; - JS_ClearScope(cx, obj); - return JS_TRUE; -} - -static JSBool -Intern(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - - str = JS_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - if (!JS_InternUCStringN(cx, JS_GetStringChars(str), - JS_GetStringLength(str))) { - return JS_FALSE; - } - return JS_TRUE; -} - -static JSBool -Clone(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFunction *fun; - JSObject *funobj, *parent, *clone; - - fun = JS_ValueToFunction(cx, argv[0]); - if (!fun) - return JS_FALSE; - funobj = JS_GetFunctionObject(fun); - if (argc > 1) { - if (!JS_ValueToObject(cx, argv[1], &parent)) - return JS_FALSE; - } else { - parent = JS_GetParent(cx, funobj); - } - clone = JS_CloneFunctionObject(cx, funobj, parent); - if (!clone) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(clone); - return JS_TRUE; -} - -static JSBool -Seal(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSObject *target; - JSBool deep = JS_FALSE; - - if (!JS_ConvertArguments(cx, argc, argv, "o/b", &target, &deep)) - return JS_FALSE; - if (!target) - return JS_TRUE; - return JS_SealObject(cx, target, deep); -} - -static JSBool -GetPDA(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSObject *vobj, *aobj, *pdobj; - JSBool ok; - JSPropertyDescArray pda; - JSPropertyDesc *pd; - uint32 i; - jsval v; - - if (!JS_ValueToObject(cx, argv[0], &vobj)) - return JS_FALSE; - if (!vobj) - return JS_TRUE; - - aobj = JS_NewArrayObject(cx, 0, NULL); - if (!aobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(aobj); - - ok = JS_GetPropertyDescArray(cx, vobj, &pda); - if (!ok) - return JS_FALSE; - pd = pda.array; - for (i = 0; i < pda.length; i++) { - pdobj = JS_NewObject(cx, NULL, NULL, NULL); - if (!pdobj) { - ok = JS_FALSE; - break; - } - - ok = JS_SetProperty(cx, pdobj, "id", &pd->id) && - JS_SetProperty(cx, pdobj, "value", &pd->value) && - (v = INT_TO_JSVAL(pd->flags), - JS_SetProperty(cx, pdobj, "flags", &v)) && - (v = INT_TO_JSVAL(pd->slot), - JS_SetProperty(cx, pdobj, "slot", &v)) && - JS_SetProperty(cx, pdobj, "alias", &pd->alias); - if (!ok) - break; - - v = OBJECT_TO_JSVAL(pdobj); - ok = JS_SetElement(cx, aobj, i, &v); - if (!ok) - break; - } - JS_PutPropertyDescArray(cx, &pda); - return ok; -} - -static JSBool -GetSLX(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSScript *script; - - script = ValueToScript(cx, argv[0]); - if (!script) - return JS_FALSE; - *rval = INT_TO_JSVAL(js_GetScriptLineExtent(script)); - return JS_TRUE; -} - -static JSBool -ToInt32(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - int32 i; - - if (!JS_ValueToInt32(cx, argv[0], &i)) - return JS_FALSE; - return JS_NewNumberValue(cx, i, rval); -} - -static JSBool -StringsAreUtf8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - *rval = JS_CStringsAreUTF8() ? JSVAL_TRUE : JSVAL_FALSE; - return JS_TRUE; -} - -static const char* badUtf8 = "...\xC0..."; -static const char* bigUtf8 = "...\xFB\xBF\xBF\xBF\xBF..."; -static const jschar badSurrogate[] = { 'A', 'B', 'C', 0xDEEE, 'D', 'E', 0 }; - -static JSBool -TestUtf8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - intN mode = 1; - jschar chars[20]; - size_t charsLength = 5; - char bytes[20]; - size_t bytesLength = 20; - if (argc && !JS_ValueToInt32(cx, *argv, &mode)) - return JS_FALSE; - - /* The following throw errors if compiled with UTF-8. */ - switch (mode) { - /* mode 1: malformed UTF-8 string. */ - case 1: - JS_NewStringCopyZ(cx, badUtf8); - break; - /* mode 2: big UTF-8 character. */ - case 2: - JS_NewStringCopyZ(cx, bigUtf8); - break; - /* mode 3: bad surrogate character. */ - case 3: - JS_EncodeCharacters(cx, badSurrogate, 6, bytes, &bytesLength); - break; - /* mode 4: use a too small buffer. */ - case 4: - JS_DecodeBytes(cx, "1234567890", 10, chars, &charsLength); - break; - default: - JS_ReportError(cx, "invalid mode parameter"); - return JS_FALSE; - } - return !JS_IsExceptionPending (cx); -} - -static JSBool -ThrowError(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JS_ReportError(cx, "This is an error"); - return JS_FALSE; -} - -#define LAZY_STANDARD_CLASSES - -/* A class for easily testing the inner/outer object callbacks. */ -typedef struct ComplexObject { - JSBool isInner; - JSObject *inner; - JSObject *outer; -} ComplexObject; - -static JSObject * -split_create_outer(JSContext *cx); - -static JSObject * -split_create_inner(JSContext *cx, JSObject *outer); - -static ComplexObject * -split_get_private(JSContext *cx, JSObject *obj); - -JS_STATIC_DLL_CALLBACK(JSBool) -split_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - ComplexObject *cpx; - jsid asId; - - cpx = split_get_private(cx, obj); - if (!cpx) - return JS_TRUE; - if (!cpx->isInner && cpx->inner) { - /* Make sure to define this property on the inner object. */ - if (!JS_ValueToId(cx, *vp, &asId)) - return JS_FALSE; - return OBJ_DEFINE_PROPERTY(cx, cpx->inner, asId, *vp, NULL, NULL, - JSPROP_ENUMERATE, NULL); - } - return JS_TRUE; -} - -JS_STATIC_DLL_CALLBACK(JSBool) -split_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - ComplexObject *cpx; - - cpx = split_get_private(cx, obj); - if (!cpx) - return JS_TRUE; - if (!cpx->isInner && cpx->inner) { - if (JSVAL_IS_STRING(id)) { - JSString *str; - - str = JSVAL_TO_STRING(id); - return JS_GetUCProperty(cx, cpx->inner, JS_GetStringChars(str), - JS_GetStringLength(str), vp); - } - if (JSVAL_IS_INT(id)) - return JS_GetElement(cx, cpx->inner, JSVAL_TO_INT(id), vp); - return JS_TRUE; - } - - return JS_TRUE; -} - -JS_STATIC_DLL_CALLBACK(JSBool) -split_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - ComplexObject *cpx; - - cpx = split_get_private(cx, obj); - if (!cpx) - return JS_TRUE; - if (!cpx->isInner && cpx->inner) { - if (JSVAL_IS_STRING(id)) { - JSString *str; - - str = JSVAL_TO_STRING(id); - return JS_SetUCProperty(cx, cpx->inner, JS_GetStringChars(str), - JS_GetStringLength(str), vp); - } - if (JSVAL_IS_INT(id)) - return JS_SetElement(cx, cpx->inner, JSVAL_TO_INT(id), vp); - return JS_TRUE; - } - - return JS_TRUE; -} - -JS_STATIC_DLL_CALLBACK(JSBool) -split_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - ComplexObject *cpx; - jsid asId; - - cpx = split_get_private(cx, obj); - if (!cpx) - return JS_TRUE; - if (!cpx->isInner && cpx->inner) { - /* Make sure to define this property on the inner object. */ - if (!JS_ValueToId(cx, *vp, &asId)) - return JS_FALSE; - return OBJ_DELETE_PROPERTY(cx, cpx->inner, asId, vp); - } - return JS_TRUE; -} - -JS_STATIC_DLL_CALLBACK(JSBool) -split_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, - jsval *statep, jsid *idp) -{ - ComplexObject *cpx; - JSObject *iterator; - - switch (enum_op) { - case JSENUMERATE_INIT: - cpx = JS_GetPrivate(cx, obj); - - if (!cpx->isInner && cpx->inner) - obj = cpx->inner; - - iterator = JS_NewPropertyIterator(cx, obj); - if (!iterator) - return JS_FALSE; - - *statep = OBJECT_TO_JSVAL(iterator); - if (idp) - *idp = JSVAL_ZERO; - break; - - case JSENUMERATE_NEXT: - iterator = (JSObject*)JSVAL_TO_OBJECT(*statep); - if (!JS_NextProperty(cx, iterator, idp)) - return JS_FALSE; - - if (*idp != JSVAL_VOID) - break; - /* Fall through. */ - - case JSENUMERATE_DESTROY: - /* Let GC at our iterator object. */ - *statep = JSVAL_NULL; - break; - } - - return JS_TRUE; -} - -JS_STATIC_DLL_CALLBACK(JSBool) -split_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, - JSObject **objp) -{ - ComplexObject *cpx; - - cpx = split_get_private(cx, obj); - if (!cpx) - return JS_TRUE; - if (!cpx->isInner && cpx->inner) { - jsid asId; - JSProperty *prop; - - if (!JS_ValueToId(cx, id, &asId)) - return JS_FALSE; - - if (!OBJ_LOOKUP_PROPERTY(cx, cpx->inner, asId, objp, &prop)) - return JS_FALSE; - if (prop) - OBJ_DROP_PROPERTY(cx, cpx->inner, prop); - - return JS_TRUE; - } - -#ifdef LAZY_STANDARD_CLASSES - if (!(flags & JSRESOLVE_ASSIGNING)) { - JSBool resolved; - - if (!JS_ResolveStandardClass(cx, obj, id, &resolved)) - return JS_FALSE; - - if (resolved) { - *objp = obj; - return JS_TRUE; - } - } -#endif - - /* XXX For additional realism, let's resolve some random property here. */ - return JS_TRUE; -} - -JS_STATIC_DLL_CALLBACK(void) -split_finalize(JSContext *cx, JSObject *obj) -{ - JS_free(cx, JS_GetPrivate(cx, obj)); -} - -JS_STATIC_DLL_CALLBACK(uint32) -split_mark(JSContext *cx, JSObject *obj, void *arg) -{ - ComplexObject *cpx; - - cpx = JS_GetPrivate(cx, obj); - - if (!cpx->isInner && cpx->inner) { - /* Mark the inner object. */ - JS_MarkGCThing(cx, cpx->inner, "ComplexObject.inner", arg); - } - - return 0; -} - -JS_STATIC_DLL_CALLBACK(JSObject *) -split_outerObject(JSContext *cx, JSObject *obj) -{ - ComplexObject *cpx; - - cpx = JS_GetPrivate(cx, obj); - return cpx->isInner ? cpx->outer : obj; -} - -JS_STATIC_DLL_CALLBACK(JSObject *) -split_innerObject(JSContext *cx, JSObject *obj) -{ - ComplexObject *cpx; - - cpx = JS_GetPrivate(cx, obj); - return !cpx->isInner ? cpx->inner : obj; -} - -static JSExtendedClass split_global_class = { - {"split_global", - JSCLASS_NEW_RESOLVE | JSCLASS_HAS_PRIVATE | JSCLASS_IS_EXTENDED, - split_addProperty, split_delProperty, - split_getProperty, split_setProperty, - (JSEnumerateOp)split_enumerate, - (JSResolveOp)split_resolve, - JS_ConvertStub, split_finalize, - NULL, NULL, NULL, NULL, NULL, NULL, - split_mark, NULL}, - NULL, split_outerObject, split_innerObject, - NULL, NULL, NULL, NULL, NULL -}; - -JSObject * -split_create_outer(JSContext *cx) -{ - ComplexObject *cpx; - JSObject *obj; - - cpx = JS_malloc(cx, sizeof *obj); - if (!cpx) - return NULL; - cpx->outer = NULL; - cpx->inner = NULL; - cpx->isInner = JS_FALSE; - - obj = JS_NewObject(cx, &split_global_class.base, NULL, NULL); - if (!obj) { - JS_free(cx, cpx); - return NULL; - } - - JS_ASSERT(!JS_GetParent(cx, obj)); - if (!JS_SetPrivate(cx, obj, cpx)) { - JS_free(cx, cpx); - return NULL; - } - - return obj; -} - -static JSObject * -split_create_inner(JSContext *cx, JSObject *outer) -{ - ComplexObject *cpx, *outercpx; - JSObject *obj; - - JS_ASSERT(JS_GET_CLASS(cx, outer) == &split_global_class.base); - - cpx = JS_malloc(cx, sizeof *cpx); - if (!cpx) - return NULL; - cpx->outer = outer; - cpx->inner = NULL; - cpx->isInner = JS_TRUE; - - obj = JS_NewObject(cx, &split_global_class.base, NULL, NULL); - if (!obj || !JS_SetParent(cx, obj, NULL) || !JS_SetPrivate(cx, obj, cpx)) { - JS_free(cx, cpx); - return NULL; - } - - outercpx = JS_GetPrivate(cx, outer); - outercpx->inner = obj; - - return obj; -} - -static ComplexObject * -split_get_private(JSContext *cx, JSObject *obj) -{ - do { - if (JS_GET_CLASS(cx, obj) == &split_global_class.base) - return JS_GetPrivate(cx, obj); - obj = JS_GetParent(cx, obj); - } while (obj); - - return NULL; -} - -static JSBool -sandbox_enumerate(JSContext *cx, JSObject *obj) -{ - jsval v; - JSBool b; - - if (!JS_GetProperty(cx, obj, "lazy", &v) || !JS_ValueToBoolean(cx, v, &b)) - return JS_FALSE; - return !b || JS_EnumerateStandardClasses(cx, obj); -} - -static JSBool -sandbox_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, - JSObject **objp) -{ - jsval v; - JSBool b, resolved; - - if (!JS_GetProperty(cx, obj, "lazy", &v) || !JS_ValueToBoolean(cx, v, &b)) - return JS_FALSE; - if (b && (flags & JSRESOLVE_ASSIGNING) == 0) { - if (!JS_ResolveStandardClass(cx, obj, id, &resolved)) - return JS_FALSE; - if (resolved) { - *objp = obj; - return JS_TRUE; - } - } - *objp = NULL; - return JS_TRUE; -} - -static JSClass sandbox_class = { - "sandbox", - JSCLASS_NEW_RESOLVE, - JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, - sandbox_enumerate, (JSResolveOp)sandbox_resolve, - JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -static JSBool -EvalInContext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - JSObject *sobj; - JSContext *scx; - const jschar *src; - size_t srclen; - JSBool lazy, ok; - jsval v; - JSStackFrame *fp; - - sobj = NULL; - if (!JS_ConvertArguments(cx, argc, argv, "S / o", &str, &sobj)) - return JS_FALSE; - - scx = JS_NewContext(JS_GetRuntime(cx), gStackChunkSize); - if (!scx) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - src = JS_GetStringChars(str); - srclen = JS_GetStringLength(str); - lazy = JS_FALSE; - if (srclen == 4 && - src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') { - lazy = JS_TRUE; - srclen = 0; - } - - if (!sobj) { - sobj = JS_NewObject(scx, &sandbox_class, NULL, NULL); - if (!sobj || (!lazy && !JS_InitStandardClasses(scx, sobj))) { - ok = JS_FALSE; - goto out; - } - v = BOOLEAN_TO_JSVAL(v); - ok = JS_SetProperty(cx, sobj, "lazy", &v); - if (!ok) - goto out; - } - - if (srclen == 0) { - *rval = OBJECT_TO_JSVAL(sobj); - ok = JS_TRUE; - } else { - fp = JS_GetScriptedCaller(cx, NULL); - ok = JS_EvaluateUCScript(scx, sobj, src, srclen, - fp->script->filename, - JS_PCToLineNumber(cx, fp->script, fp->pc), - rval); - } - -out: - JS_DestroyContext(scx); - return ok; -} - -static JSFunctionSpec shell_functions[] = { - {"version", Version, 0,0,0}, - {"options", Options, 0,0,0}, - {"load", Load, 1,0,0}, - {"readline", ReadLine, 0,0,0}, - {"print", Print, 0,0,0}, - {"help", Help, 0,0,0}, - {"quit", Quit, 0,0,0}, - {"gc", GC, 0,0,0}, - {"trap", Trap, 3,0,0}, - {"untrap", Untrap, 2,0,0}, - {"line2pc", LineToPC, 0,0,0}, - {"pc2line", PCToLine, 0,0,0}, - {"stringsAreUtf8", StringsAreUtf8, 0,0,0}, - {"testUtf8", TestUtf8, 1,0,0}, - {"throwError", ThrowError, 0,0,0}, -#ifdef DEBUG - {"dis", Disassemble, 1,0,0}, - {"dissrc", DisassWithSrc, 1,0,0}, - {"notes", Notes, 1,0,0}, - {"tracing", Tracing, 0,0,0}, - {"stats", DumpStats, 1,0,0}, -#endif -#ifdef TEST_EXPORT - {"xport", DoExport, 2,0,0}, -#endif -#ifdef TEST_CVTARGS - {"cvtargs", ConvertArgs, 0,0,12}, -#endif - {"build", BuildDate, 0,0,0}, - {"clear", Clear, 0,0,0}, - {"intern", Intern, 1,0,0}, - {"clone", Clone, 1,0,0}, - {"seal", Seal, 1,0,1}, - {"getpda", GetPDA, 1,0,0}, - {"getslx", GetSLX, 1,0,0}, - {"toint32", ToInt32, 1,0,0}, - {"evalcx", EvalInContext, 1,0,0}, - {NULL,NULL,0,0,0} -}; - -/* NOTE: These must be kept in sync with the above. */ - -static char *shell_help_messages[] = { - "version([number]) Get or set JavaScript version number", - "options([option ...]) Get or toggle JavaScript options", - "load(['foo.js' ...]) Load files named by string arguments", - "readline() Read a single line from stdin", - "print([exp ...]) Evaluate and print expressions", - "help([name ...]) Display usage and help messages", - "quit() Quit the shell", - "gc() Run the garbage collector", - "trap([fun, [pc,]] exp) Trap bytecode execution", - "untrap(fun[, pc]) Remove a trap", - "line2pc([fun,] line) Map line number to PC", - "pc2line(fun[, pc]) Map PC to line number", - "stringsAreUTF8() Check if strings are UTF-8 encoded", - "testUTF8(mode) Perform UTF-8 tests (modes are 1 to 4)", - "throwError() Throw an error from JS_ReportError", -#ifdef DEBUG - "dis([fun]) Disassemble functions into bytecodes", - "dissrc([fun]) Disassemble functions with source lines", - "notes([fun]) Show source notes for functions", - "tracing([toggle]) Turn tracing on or off", - "stats([string ...]) Dump 'arena', 'atom', 'global' stats", -#endif -#ifdef TEST_EXPORT - "xport(obj, id) Export identified property from object", -#endif -#ifdef TEST_CVTARGS - "cvtargs(b, c, ...) Test JS_ConvertArguments", -#endif - "build() Show build date and time", - "clear([obj]) Clear properties of object", - "intern(str) Internalize str in the atom table", - "clone(fun[, scope]) Clone function object", - "seal(obj[, deep]) Seal object, or object graph if deep", - "getpda(obj) Get the property descriptors for obj", - "getslx(obj) Get script line extent", - "toint32(n) Testing hook for JS_ValueToInt32", - "evalcx(s[, o]) Evaluate s in optional sandbox object o\n" - " if (s == '' && !o) return new o with eager standard classes\n" - " if (s == 'lazy' && !o) return new o with lazy standard classes", - 0 -}; - -static void -ShowHelpHeader(void) -{ - fprintf(gOutFile, "%-14s %-22s %s\n", "Command", "Usage", "Description"); - fprintf(gOutFile, "%-14s %-22s %s\n", "=======", "=====", "==========="); -} - -static void -ShowHelpForCommand(uintN n) -{ - fprintf(gOutFile, "%-14.14s %s\n", shell_functions[n].name, shell_help_messages[n]); -} - -static JSObject * -split_setup(JSContext *cx) -{ - JSObject *outer, *inner, *arguments; - - outer = split_create_outer(cx); - if (!outer) - return NULL; - JS_SetGlobalObject(cx, outer); - - inner = split_create_inner(cx, outer); - if (!inner) - return NULL; - - if (!JS_DefineFunctions(cx, inner, shell_functions)) - return NULL; - JS_ClearScope(cx, outer); - - /* Create a dummy arguments object. */ - arguments = JS_NewArrayObject(cx, 0, NULL); - if (!arguments || - !JS_DefineProperty(cx, inner, "arguments", OBJECT_TO_JSVAL(arguments), - NULL, NULL, 0)) { - return NULL; - } - -#ifndef LAZY_STANDARD_CLASSES - if (!JS_InitStandardClasses(cx, inner)) - return NULL; -#endif - - return inner; -} - -static JSBool -Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - uintN i, j; - int did_header, did_something; - JSType type; - JSFunction *fun; - JSString *str; - const char *bytes; - - fprintf(gOutFile, "%s\n", JS_GetImplementationVersion()); - if (argc == 0) { - ShowHelpHeader(); - for (i = 0; shell_functions[i].name; i++) - ShowHelpForCommand(i); - } else { - did_header = 0; - for (i = 0; i < argc; i++) { - did_something = 0; - type = JS_TypeOfValue(cx, argv[i]); - if (type == JSTYPE_FUNCTION) { - fun = JS_ValueToFunction(cx, argv[i]); - str = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL; - } else if (type == JSTYPE_STRING) { - str = JSVAL_TO_STRING(argv[i]); - } else { - str = NULL; - } - if (str) { - bytes = JS_GetStringBytes(str); - for (j = 0; shell_functions[j].name; j++) { - if (!strcmp(bytes, shell_functions[j].name)) { - if (!did_header) { - did_header = 1; - ShowHelpHeader(); - } - did_something = 1; - ShowHelpForCommand(j); - break; - } - } - } - if (!did_something) { - str = JS_ValueToString(cx, argv[i]); - if (!str) - return JS_FALSE; - fprintf(gErrFile, "Sorry, no help for %s\n", - JS_GetStringBytes(str)); - } - } - } - return JS_TRUE; -} - -/* - * Define a JS object called "it". Give it class operations that printf why - * they're being called for tutorial purposes. - */ -enum its_tinyid { - ITS_COLOR, ITS_HEIGHT, ITS_WIDTH, ITS_FUNNY, ITS_ARRAY, ITS_RDONLY -}; - -static JSPropertySpec its_props[] = { - {"color", ITS_COLOR, JSPROP_ENUMERATE, NULL, NULL}, - {"height", ITS_HEIGHT, JSPROP_ENUMERATE, NULL, NULL}, - {"width", ITS_WIDTH, JSPROP_ENUMERATE, NULL, NULL}, - {"funny", ITS_FUNNY, JSPROP_ENUMERATE, NULL, NULL}, - {"array", ITS_ARRAY, JSPROP_ENUMERATE, NULL, NULL}, - {"rdonly", ITS_RDONLY, JSPROP_READONLY, NULL, NULL}, - {NULL,0,0,NULL,NULL} -}; - -static JSBool -its_item(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - *rval = OBJECT_TO_JSVAL(obj); - if (argc != 0) - JS_SetCallReturnValue2(cx, argv[0]); - return JS_TRUE; -} - -static JSBool -its_bindMethod(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - char *name; - JSObject *method; - - if (!JS_ConvertArguments(cx, argc, argv, "so", &name, &method)) - return JS_FALSE; - - *rval = OBJECT_TO_JSVAL(method); - - if (JS_TypeOfValue(cx, *rval) != JSTYPE_FUNCTION) { - JSString *valstr = JS_ValueToString(cx, *rval); - if (valstr) { - JS_ReportError(cx, "can't bind method %s to non-callable object %s", - name, JS_GetStringBytes(valstr)); - } - return JS_FALSE; - } - - if (!JS_DefineProperty(cx, obj, name, *rval, NULL, NULL, JSPROP_ENUMERATE)) - return JS_FALSE; - - return JS_SetParent(cx, method, obj); -} - -static JSFunctionSpec its_methods[] = { - {"item", its_item, 0,0,0}, - {"bindMethod", its_bindMethod, 2,0,0}, - {NULL,NULL,0,0,0} -}; - -#ifdef JSD_LOWLEVEL_SOURCE -/* - * This facilitates sending source to JSD (the debugger system) in the shell - * where the source is loaded using the JSFILE hack in jsscan. The function - * below is used as a callback for the jsdbgapi JS_SetSourceHandler hook. - * A more normal embedding (e.g. mozilla) loads source itself and can send - * source directly to JSD without using this hook scheme. - */ -static void -SendSourceToJSDebugger(const char *filename, uintN lineno, - jschar *str, size_t length, - void **listenerTSData, JSDContext* jsdc) -{ - JSDSourceText *jsdsrc = (JSDSourceText *) *listenerTSData; - - if (!jsdsrc) { - if (!filename) - filename = "typein"; - if (1 == lineno) { - jsdsrc = JSD_NewSourceText(jsdc, filename); - } else { - jsdsrc = JSD_FindSourceForURL(jsdc, filename); - if (jsdsrc && JSD_SOURCE_PARTIAL != - JSD_GetSourceStatus(jsdc, jsdsrc)) { - jsdsrc = NULL; - } - } - } - if (jsdsrc) { - jsdsrc = JSD_AppendUCSourceText(jsdc,jsdsrc, str, length, - JSD_SOURCE_PARTIAL); - } - *listenerTSData = jsdsrc; -} -#endif /* JSD_LOWLEVEL_SOURCE */ - -static JSBool its_noisy; /* whether to be noisy when finalizing it */ - -static JSBool -its_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - if (its_noisy) { - fprintf(gOutFile, "adding its property %s,", - JS_GetStringBytes(JS_ValueToString(cx, id))); - fprintf(gOutFile, " initial value %s\n", - JS_GetStringBytes(JS_ValueToString(cx, *vp))); - } - return JS_TRUE; -} - -static JSBool -its_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - if (its_noisy) { - fprintf(gOutFile, "deleting its property %s,", - JS_GetStringBytes(JS_ValueToString(cx, id))); - fprintf(gOutFile, " current value %s\n", - JS_GetStringBytes(JS_ValueToString(cx, *vp))); - } - return JS_TRUE; -} - -static JSBool -its_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - if (its_noisy) { - fprintf(gOutFile, "getting its property %s,", - JS_GetStringBytes(JS_ValueToString(cx, id))); - fprintf(gOutFile, " current value %s\n", - JS_GetStringBytes(JS_ValueToString(cx, *vp))); - } - return JS_TRUE; -} - -static JSBool -its_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - if (its_noisy) { - fprintf(gOutFile, "setting its property %s,", - JS_GetStringBytes(JS_ValueToString(cx, id))); - fprintf(gOutFile, " new value %s\n", - JS_GetStringBytes(JS_ValueToString(cx, *vp))); - } - if (JSVAL_IS_STRING(id) && - !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(id)), "noisy")) { - return JS_ValueToBoolean(cx, *vp, &its_noisy); - } - return JS_TRUE; -} - -static JSBool -its_enumerate(JSContext *cx, JSObject *obj) -{ - if (its_noisy) - fprintf(gOutFile, "enumerate its properties\n"); - return JS_TRUE; -} - -static JSBool -its_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, - JSObject **objp) -{ - if (its_noisy) { - fprintf(gOutFile, "resolving its property %s, flags {%s,%s,%s}\n", - JS_GetStringBytes(JS_ValueToString(cx, id)), - (flags & JSRESOLVE_QUALIFIED) ? "qualified" : "", - (flags & JSRESOLVE_ASSIGNING) ? "assigning" : "", - (flags & JSRESOLVE_DETECTING) ? "detecting" : ""); - } - return JS_TRUE; -} - -static JSBool -its_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) -{ - if (its_noisy) - fprintf(gOutFile, "converting it to %s type\n", JS_GetTypeName(cx, type)); - return JS_TRUE; -} - -static void -its_finalize(JSContext *cx, JSObject *obj) -{ - if (its_noisy) - fprintf(gOutFile, "finalizing it\n"); -} - -static JSClass its_class = { - "It", JSCLASS_NEW_RESOLVE, - its_addProperty, its_delProperty, its_getProperty, its_setProperty, - its_enumerate, (JSResolveOp)its_resolve, - its_convert, its_finalize, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -JSErrorFormatString jsShell_ErrorFormatString[JSErr_Limit] = { -#define MSG_DEF(name, number, count, exception, format) \ - { format, count, JSEXN_ERR } , -#include "jsshell.msg" -#undef MSG_DEF -}; - -static const JSErrorFormatString * -my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber) -{ - if ((errorNumber > 0) && (errorNumber < JSShellErr_Limit)) - return &jsShell_ErrorFormatString[errorNumber]; - return NULL; -} - -static void -my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) -{ - int i, j, k, n; - char *prefix, *tmp; - const char *ctmp; - - if (!report) { - fprintf(gErrFile, "%s\n", message); - return; - } - - /* Conditionally ignore reported warnings. */ - if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings) - return; - - prefix = NULL; - if (report->filename) - prefix = JS_smprintf("%s:", report->filename); - if (report->lineno) { - tmp = prefix; - prefix = JS_smprintf("%s%u: ", tmp ? tmp : "", report->lineno); - JS_free(cx, tmp); - } - if (JSREPORT_IS_WARNING(report->flags)) { - tmp = prefix; - prefix = JS_smprintf("%s%swarning: ", - tmp ? tmp : "", - JSREPORT_IS_STRICT(report->flags) ? "strict " : ""); - JS_free(cx, tmp); - } - - /* embedded newlines -- argh! */ - while ((ctmp = strchr(message, '\n')) != 0) { - ctmp++; - if (prefix) - fputs(prefix, gErrFile); - fwrite(message, 1, ctmp - message, gErrFile); - message = ctmp; - } - - /* If there were no filename or lineno, the prefix might be empty */ - if (prefix) - fputs(prefix, gErrFile); - fputs(message, gErrFile); - - if (!report->linebuf) { - fputc('\n', gErrFile); - goto out; - } - - /* report->linebuf usually ends with a newline. */ - n = strlen(report->linebuf); - fprintf(gErrFile, ":\n%s%s%s%s", - prefix, - report->linebuf, - (n > 0 && report->linebuf[n-1] == '\n') ? "" : "\n", - prefix); - n = PTRDIFF(report->tokenptr, report->linebuf, char); - for (i = j = 0; i < n; i++) { - if (report->linebuf[i] == '\t') { - for (k = (j + 8) & ~7; j < k; j++) { - fputc('.', gErrFile); - } - continue; - } - fputc('.', gErrFile); - j++; - } - fputs("^\n", gErrFile); - out: - if (!JSREPORT_IS_WARNING(report->flags)) { - if (report->errorNumber == JSMSG_OUT_OF_MEMORY) { - gExitCode = EXITCODE_OUT_OF_MEMORY; - } else { - gExitCode = EXITCODE_RUNTIME_ERROR; - } - } - JS_free(cx, prefix); -} - -#if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX) -static JSBool -Exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFunction *fun; - const char *name, **nargv; - uintN i, nargc; - JSString *str; - pid_t pid; - int status; - - fun = JS_ValueToFunction(cx, argv[-2]); - if (!fun) - return JS_FALSE; - if (!fun->atom) - return JS_TRUE; - name = JS_GetStringBytes(ATOM_TO_STRING(fun->atom)); - nargc = 1 + argc; - nargv = JS_malloc(cx, (nargc + 1) * sizeof(char *)); - if (!nargv) - return JS_FALSE; - nargv[0] = name; - for (i = 1; i < nargc; i++) { - str = JS_ValueToString(cx, argv[i-1]); - if (!str) { - JS_free(cx, nargv); - return JS_FALSE; - } - nargv[i] = JS_GetStringBytes(str); - } - nargv[nargc] = 0; - pid = fork(); - switch (pid) { - case -1: - perror("js"); - break; - case 0: - (void) execvp(name, (char **)nargv); - perror("js"); - exit(127); - default: - while (waitpid(pid, &status, 0) < 0 && errno == EINTR) - continue; - break; - } - JS_free(cx, nargv); - return JS_TRUE; -} -#endif - -static JSBool -global_enumerate(JSContext *cx, JSObject *obj) -{ -#ifdef LAZY_STANDARD_CLASSES - return JS_EnumerateStandardClasses(cx, obj); -#else - return JS_TRUE; -#endif -} - -static JSBool -global_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, - JSObject **objp) -{ -#ifdef LAZY_STANDARD_CLASSES - JSBool resolved; - - if (!JS_ResolveStandardClass(cx, obj, id, &resolved)) - return JS_FALSE; - if (resolved) { - *objp = obj; - return JS_TRUE; - } -#endif - -#if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX) - if ((flags & (JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING)) == 0) { - /* - * Do this expensive hack only for unoptimized Unix builds, which are - * not used for benchmarking. - */ - char *path, *comp, *full; - const char *name; - JSBool ok, found; - JSFunction *fun; - - if (!JSVAL_IS_STRING(id)) - return JS_TRUE; - path = getenv("PATH"); - if (!path) - return JS_TRUE; - path = JS_strdup(cx, path); - if (!path) - return JS_FALSE; - name = JS_GetStringBytes(JSVAL_TO_STRING(id)); - ok = JS_TRUE; - for (comp = strtok(path, ":"); comp; comp = strtok(NULL, ":")) { - if (*comp != '\0') { - full = JS_smprintf("%s/%s", comp, name); - if (!full) { - JS_ReportOutOfMemory(cx); - ok = JS_FALSE; - break; - } - } else { - full = (char *)name; - } - found = (access(full, X_OK) == 0); - if (*comp != '\0') - free(full); - if (found) { - fun = JS_DefineFunction(cx, obj, name, Exec, 0, - JSPROP_ENUMERATE); - ok = (fun != NULL); - if (ok) - *objp = obj; - break; - } - } - JS_free(cx, path); - return ok; - } -#else - return JS_TRUE; -#endif -} - -JSClass global_class = { - "global", JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS, - JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, - global_enumerate, (JSResolveOp) global_resolve, - JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -static JSBool -env_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ -/* XXX porting may be easy, but these don't seem to supply setenv by default */ -#if !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS - JSString *idstr, *valstr; - const char *name, *value; - int rv; - - idstr = JS_ValueToString(cx, id); - valstr = JS_ValueToString(cx, *vp); - if (!idstr || !valstr) - return JS_FALSE; - name = JS_GetStringBytes(idstr); - value = JS_GetStringBytes(valstr); -#if defined XP_WIN || defined HPUX || defined OSF1 || defined IRIX - { - char *waste = JS_smprintf("%s=%s", name, value); - if (!waste) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - rv = putenv(waste); -#ifdef XP_WIN - /* - * HPUX9 at least still has the bad old non-copying putenv. - * - * Per mail from , OSF1 also has a putenv - * that will crash if you pass it an auto char array (so it must place - * its argument directly in the char *environ[] array). - */ - free(waste); -#endif - } -#else - rv = setenv(name, value, 1); -#endif - if (rv < 0) { - JS_ReportError(cx, "can't set envariable %s to %s", name, value); - return JS_FALSE; - } - *vp = STRING_TO_JSVAL(valstr); -#endif /* !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS */ - return JS_TRUE; -} - -static JSBool -env_enumerate(JSContext *cx, JSObject *obj) -{ - static JSBool reflected; - char **evp, *name, *value; - JSString *valstr; - JSBool ok; - - if (reflected) - return JS_TRUE; - - for (evp = (char **)JS_GetPrivate(cx, obj); (name = *evp) != NULL; evp++) { - value = strchr(name, '='); - if (!value) - continue; - *value++ = '\0'; - valstr = JS_NewStringCopyZ(cx, value); - if (!valstr) { - ok = JS_FALSE; - } else { - ok = JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr), - NULL, NULL, JSPROP_ENUMERATE); - } - value[-1] = '='; - if (!ok) - return JS_FALSE; - } - - reflected = JS_TRUE; - return JS_TRUE; -} - -static JSBool -env_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, - JSObject **objp) -{ - JSString *idstr, *valstr; - const char *name, *value; - - if (flags & JSRESOLVE_ASSIGNING) - return JS_TRUE; - - idstr = JS_ValueToString(cx, id); - if (!idstr) - return JS_FALSE; - name = JS_GetStringBytes(idstr); - value = getenv(name); - if (value) { - valstr = JS_NewStringCopyZ(cx, value); - if (!valstr) - return JS_FALSE; - if (!JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr), - NULL, NULL, JSPROP_ENUMERATE)) { - return JS_FALSE; - } - *objp = obj; - } - return JS_TRUE; -} - -static JSClass env_class = { - "environment", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, - JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, env_setProperty, - env_enumerate, (JSResolveOp) env_resolve, - JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -#ifdef NARCISSUS - -static JSBool -defineProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - jsval value; - JSBool dontDelete, readOnly, dontEnum; - const jschar *chars; - size_t length; - uintN attrs; - - dontDelete = readOnly = dontEnum = JS_FALSE; - if (!JS_ConvertArguments(cx, argc, argv, "Sv/bbb", - &str, &value, &dontDelete, &readOnly, &dontEnum)) { - return JS_FALSE; - } - chars = JS_GetStringChars(str); - length = JS_GetStringLength(str); - attrs = dontEnum ? 0 : JSPROP_ENUMERATE; - if (dontDelete) - attrs |= JSPROP_PERMANENT; - if (readOnly) - attrs |= JSPROP_READONLY; - return JS_DefineUCProperty(cx, obj, chars, length, value, NULL, NULL, - attrs); -} - -static JSBool -Evaluate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - /* function evaluate(source, filename, lineno) { ... } */ - JSString *source; - const char *filename = ""; - jsuint lineno = 0; - uint32 oldopts; - JSBool ok; - - if (argc == 0) { - *rval = JSVAL_VOID; - return JS_TRUE; - } - - if (!JS_ConvertArguments(cx, argc, argv, "S/su", - &source, &filename, &lineno)) { - return JS_FALSE; - } - - oldopts = JS_GetOptions(cx); - JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO); - ok = JS_EvaluateUCScript(cx, obj, JS_GetStringChars(source), - JS_GetStringLength(source), filename, - lineno, rval); - JS_SetOptions(cx, oldopts); - - return ok; -} - -#include -#include - -/* - * Returns a JS_malloc'd string (that the caller needs to JS_free) - * containing the directory (non-leaf) part of |from| prepended to |leaf|. - * If |from| is empty or a leaf, MakeAbsolutePathname returns a copy of leaf. - * Returns NULL to indicate an error. - */ -static char * -MakeAbsolutePathname(JSContext *cx, const char *from, const char *leaf) -{ - size_t dirlen; - char *dir; - const char *slash = NULL, *cp; - - cp = from; - while (*cp) { - if (*cp == '/' -#ifdef XP_WIN - || *cp == '\\' -#endif - ) { - slash = cp; - } - - ++cp; - } - - if (!slash) { - /* We were given a leaf or |from| was empty. */ - return JS_strdup(cx, leaf); - } - - /* Else, we were given a real pathname, return that + the leaf. */ - dirlen = slash - from + 1; - dir = JS_malloc(cx, dirlen + strlen(leaf) + 1); - if (!dir) - return NULL; - - strncpy(dir, from, dirlen); - strcpy(dir + dirlen, leaf); /* Note: we can't use strcat here. */ - - return dir; -} - -static JSBool -snarf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - const char *filename; - char *pathname; - JSStackFrame *fp; - JSBool ok; - off_t cc, len; - char *buf; - FILE *file; - - str = JS_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - filename = JS_GetStringBytes(str); - - /* Get the currently executing script's name. */ - fp = JS_GetScriptedCaller(cx, NULL); - JS_ASSERT(fp && fp->script->filename); - pathname = MakeAbsolutePathname(cx, fp->script->filename, filename); - if (!pathname) - return JS_FALSE; - - ok = JS_FALSE; - len = 0; - buf = NULL; - file = fopen(pathname, "rb"); - if (!file) { - JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno)); - } else { - if (fseek(file, 0, SEEK_END) == EOF) { - JS_ReportError(cx, "can't seek end of %s", pathname); - } else { - len = ftell(file); - if (fseek(file, 0, SEEK_SET) == EOF) { - JS_ReportError(cx, "can't seek start of %s", pathname); - } else { - buf = JS_malloc(cx, len + 1); - if (buf) { - cc = fread(buf, 1, len, file); - if (cc != len) { - JS_free(cx, buf); - JS_ReportError(cx, "can't read %s: %s", pathname, - (cc < 0) ? strerror(errno) - : "short read"); - } else { - len = (size_t)cc; - ok = JS_TRUE; - } - } - } - } - fclose(file); - } - JS_free(cx, pathname); - if (!ok) { - JS_free(cx, buf); - return ok; - } - - buf[len] = '\0'; - str = JS_NewString(cx, buf, len); - if (!str) { - JS_free(cx, buf); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -#endif /* NARCISSUS */ - -int -main(int argc, char **argv, char **envp) -{ - int stackDummy; - JSRuntime *rt; - JSContext *cx; - JSObject *glob, *it, *envobj; - int result; -#ifdef LIVECONNECT - JavaVM *java_vm = NULL; -#endif -#ifdef JSDEBUGGER_JAVA_UI - JNIEnv *java_env; -#endif - - gStackBase = (jsuword)&stackDummy; - - setlocale(LC_ALL, ""); - -#ifdef XP_OS2 - /* these streams are normally line buffered on OS/2 and need a \n, * - * so we need to unbuffer then to get a reasonable prompt */ - setbuf(stdout,0); - setbuf(stderr,0); -#endif - - gErrFile = stderr; - gOutFile = stdout; - - argc--; - argv++; - - rt = JS_NewRuntime(64L * 1024L * 1024L); - if (!rt) - return 1; - - cx = JS_NewContext(rt, gStackChunkSize); - if (!cx) - return 1; - JS_SetErrorReporter(cx, my_ErrorReporter); - -#ifdef JS_THREADSAFE - JS_BeginRequest(cx); -#endif - - glob = JS_NewObject(cx, &global_class, NULL, NULL); - if (!glob) - return 1; -#ifdef LAZY_STANDARD_CLASSES - JS_SetGlobalObject(cx, glob); -#else - if (!JS_InitStandardClasses(cx, glob)) - return 1; -#endif - if (!JS_DefineFunctions(cx, glob, shell_functions)) - return 1; - - it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0); - if (!it) - return 1; - if (!JS_DefineProperties(cx, it, its_props)) - return 1; - if (!JS_DefineFunctions(cx, it, its_methods)) - return 1; - -#ifdef PERLCONNECT - if (!JS_InitPerlClass(cx, glob)) - return 1; -#endif - -#ifdef JSDEBUGGER - /* - * XXX A command line option to enable debugging (or not) would be good - */ - _jsdc = JSD_DebuggerOnForUser(rt, NULL, NULL); - if (!_jsdc) - return 1; - JSD_JSContextInUse(_jsdc, cx); -#ifdef JSD_LOWLEVEL_SOURCE - JS_SetSourceHandler(rt, SendSourceToJSDebugger, _jsdc); -#endif /* JSD_LOWLEVEL_SOURCE */ -#ifdef JSDEBUGGER_JAVA_UI - _jsdjc = JSDJ_CreateContext(); - if (! _jsdjc) - return 1; - JSDJ_SetJSDContext(_jsdjc, _jsdc); - java_env = JSDJ_CreateJavaVMAndStartDebugger(_jsdjc); -#ifdef LIVECONNECT - if (java_env) - (*java_env)->GetJavaVM(java_env, &java_vm); -#endif - /* - * XXX This would be the place to wait for the debugger to start. - * Waiting would be nice in general, but especially when a js file - * is passed on the cmd line. - */ -#endif /* JSDEBUGGER_JAVA_UI */ -#ifdef JSDEBUGGER_C_UI - JSDB_InitDebugger(rt, _jsdc, 0); -#endif /* JSDEBUGGER_C_UI */ -#endif /* JSDEBUGGER */ - -#ifdef LIVECONNECT - if (!JSJ_SimpleInit(cx, glob, java_vm, getenv("CLASSPATH"))) - return 1; -#endif - - envobj = JS_DefineObject(cx, glob, "environment", &env_class, NULL, 0); - if (!envobj || !JS_SetPrivate(cx, envobj, envp)) - return 1; - -#ifdef NARCISSUS - { - jsval v; - static const char Object_prototype[] = "Object.prototype"; - - if (!JS_DefineFunction(cx, glob, "snarf", snarf, 1, 0)) - return 1; - if (!JS_DefineFunction(cx, glob, "evaluate", Evaluate, 3, 0)) - return 1; - - if (!JS_EvaluateScript(cx, glob, - Object_prototype, sizeof Object_prototype - 1, - NULL, 0, &v)) { - return 1; - } - if (!JS_DefineFunction(cx, JSVAL_TO_OBJECT(v), "__defineProperty__", - defineProperty, 5, 0)) { - return 1; - } - } -#endif - - result = ProcessArgs(cx, glob, argv, argc); - -#ifdef JSDEBUGGER - if (_jsdc) - JSD_DebuggerOff(_jsdc); -#endif /* JSDEBUGGER */ - -#ifdef JS_THREADSAFE - JS_EndRequest(cx); -#endif - - JS_DestroyContext(cx); - JS_DestroyRuntime(rt); - JS_ShutDown(); - return result; -} diff --git a/spidermonkey/src/js.mdp b/spidermonkey/src/js.mdp deleted file mode 100644 index 8da64fb6b61a5a047ac54c88020cf397fa7eaa25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17922 zcmeI3-A)rh6vxj}3RDD&d|#MMV&sNEPuNv)f8dOnd+p zzn{e$6YhNi?|2EX)acnQgDY|a;b!UCWV@63|IgWTeltmvNxQmvcQFOMum#Wqbafqp z-*j~~frGB@o|<2Sd3X%hU>ZUe!_g;E=^g^HFT9`K(=~N!2=qcf47Th_&0ktvsJALV z_P(WOMQm5lkO&X~B0vO)01+SpM1Tko0U|&IhyW2tJb^Ek)njD=KEonp`@bi?A8O!q z1k%ulCqM&`flDw5mtp91;p9#-48~ysCSmFfIVdEl1TuK`n>sB1>yua)(V<}Uhmc4P zcSRbk+nKzQfmYspKJfu8zqq~EY)EJ*Ka7_jq5K_pU7#=+iY1irQ=5_ zgd-eN#w&D8u6T+EtRSS)b*yH+AvBjqme*(*KeV_l*AcAJW?MollVk6%p(w>Jw*z)x z_)YH!+#57ZbuttWi;dn9FZC^YhC811meAXtdTR2&Lus@lZ3f(~vHXs(Yes#K>PSa@ zAf10BKcF^Ql!2%UjeJfj{v%ql#&Z14pThVJje)pM!Z~T3exYT!Q`Yh5WefIa3Z)W; z;i}*oJ&Se>Tj7}Q2(Rte52nhlt(C8~1D2ng`+uFoJ=%+R-yV9tJ==Y!JgEEm>X5bG% C_`cBq diff --git a/spidermonkey/src/js.msg b/spidermonkey/src/js.msg deleted file mode 100644 index 2686af0..0000000 --- a/spidermonkey/src/js.msg +++ /dev/null @@ -1,301 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * This is the JavaScript error message file. - * - * The format for each JS error message is: - * - * MSG_DEF(, , , , - * ) - * - * where ; - * is a legal C identifer that will be used in the - * JS engine source. - * - * is an unique integral value identifying this error. - * - * is an integer literal specifying the total number of - * replaceable arguments in the following format string. - * - * is an exception index from the enum in jsexn.c; - * JSEXN_NONE for none. The given exception index will be raised by the - * engine when the corresponding error occurs. - * - * is a string literal, optionally containing sequences - * {X} where X is an integer representing the argument number that will - * be replaced with a string value when the error is reported. - * - * e.g. - * - * MSG_DEF(JSMSG_NOT_A_SUBSPECIES, 73, JSEXN_NONE, 2, - * "{0} is not a member of the {1} family") - * - * can be used: - * - * JS_ReportErrorNumber(JSMSG_NOT_A_SUBSPECIES, "Rhino", "Monkey"); - * - * to report: - * - * "Rhino is not a member of the Monkey family" - * - * Before adding a new MSG_DEF at the end, look for JSMSG_UNUSED free - * index placeholders in the middle of the list. - */ - -MSG_DEF(JSMSG_NOT_AN_ERROR, 0, 0, JSEXN_NONE, "") -MSG_DEF(JSMSG_NOT_DEFINED, 1, 1, JSEXN_REFERENCEERR, "{0} is not defined") -MSG_DEF(JSMSG_INACTIVE, 2, 0, JSEXN_INTERNALERR, "nothing active on context") -MSG_DEF(JSMSG_MORE_ARGS_NEEDED, 3, 3, JSEXN_TYPEERR, "{0} requires more than {1} argument{2}") -MSG_DEF(JSMSG_BAD_CHAR, 4, 1, JSEXN_INTERNALERR, "invalid format character {0}") -MSG_DEF(JSMSG_BAD_TYPE, 5, 1, JSEXN_TYPEERR, "unknown type {0}") -MSG_DEF(JSMSG_CANT_LOCK, 6, 0, JSEXN_INTERNALERR, "can't lock memory") -MSG_DEF(JSMSG_CANT_UNLOCK, 7, 0, JSEXN_INTERNALERR, "can't unlock memory") -MSG_DEF(JSMSG_INCOMPATIBLE_PROTO, 8, 3, JSEXN_TYPEERR, "{0}.prototype.{1} called on incompatible {2}") -MSG_DEF(JSMSG_NO_CONSTRUCTOR, 9, 1, JSEXN_TYPEERR, "{0} has no constructor") -MSG_DEF(JSMSG_CANT_ALIAS, 10, 3, JSEXN_TYPEERR, "can't alias {0} to {1} in class {2}") -MSG_DEF(JSMSG_NOT_SCRIPTED_FUNCTION, 11, 1, JSEXN_TYPEERR, "{0} is not a scripted function") -MSG_DEF(JSMSG_BAD_SORT_ARG, 12, 0, JSEXN_TYPEERR, "invalid Array.prototype.sort argument") -MSG_DEF(JSMSG_BAD_ATOMIC_NUMBER, 13, 1, JSEXN_INTERNALERR, "internal error: no index for atom {0}") -MSG_DEF(JSMSG_TOO_MANY_LITERALS, 14, 0, JSEXN_INTERNALERR, "too many literals") -MSG_DEF(JSMSG_CANT_WATCH, 15, 1, JSEXN_TYPEERR, "can't watch non-native objects of class {0}") -MSG_DEF(JSMSG_STACK_UNDERFLOW, 16, 2, JSEXN_INTERNALERR, "internal error compiling {0}: stack underflow at pc {1}") -MSG_DEF(JSMSG_NEED_DIET, 17, 1, JSEXN_INTERNALERR, "{0} too large") -MSG_DEF(JSMSG_TOO_MANY_LOCAL_ROOTS, 18, 0, JSEXN_ERR, "out of local root space") -MSG_DEF(JSMSG_READ_ONLY, 19, 1, JSEXN_ERR, "{0} is read-only") -MSG_DEF(JSMSG_BAD_FORMAL, 20, 0, JSEXN_SYNTAXERR, "malformed formal parameter") -MSG_DEF(JSMSG_BAD_ITERATOR, 21, 3, JSEXN_TYPEERR, "{0} has invalid {1} value {2}") -MSG_DEF(JSMSG_NOT_FUNCTION, 22, 1, JSEXN_TYPEERR, "{0} is not a function") -MSG_DEF(JSMSG_NOT_CONSTRUCTOR, 23, 1, JSEXN_TYPEERR, "{0} is not a constructor") -MSG_DEF(JSMSG_STACK_OVERFLOW, 24, 1, JSEXN_INTERNALERR, "stack overflow in {0}") -MSG_DEF(JSMSG_NOT_EXPORTED, 25, 1, JSEXN_TYPEERR, "{0} is not exported") -MSG_DEF(JSMSG_OVER_RECURSED, 26, 0, JSEXN_INTERNALERR, "too much recursion") -MSG_DEF(JSMSG_IN_NOT_OBJECT, 27, 1, JSEXN_TYPEERR, "invalid 'in' operand {0}") -MSG_DEF(JSMSG_BAD_NEW_RESULT, 28, 1, JSEXN_TYPEERR, "invalid new expression result {0}") -MSG_DEF(JSMSG_BAD_SHARP_DEF, 29, 1, JSEXN_ERR, "invalid sharp variable definition #{0}=") -MSG_DEF(JSMSG_BAD_SHARP_USE, 30, 1, JSEXN_ERR, "invalid sharp variable use #{0}#") -MSG_DEF(JSMSG_BAD_INSTANCEOF_RHS, 31, 1, JSEXN_TYPEERR, "invalid 'instanceof' operand {0}") -MSG_DEF(JSMSG_BAD_BYTECODE, 32, 1, JSEXN_INTERNALERR, "unimplemented JavaScript bytecode {0}") -MSG_DEF(JSMSG_BAD_RADIX, 33, 1, JSEXN_ERR, "illegal radix {0}") -MSG_DEF(JSMSG_PAREN_BEFORE_LET, 34, 0, JSEXN_SYNTAXERR, "missing ( before let head") -MSG_DEF(JSMSG_CANT_CONVERT, 35, 1, JSEXN_ERR, "can't convert {0} to an integer") -MSG_DEF(JSMSG_CYCLIC_VALUE, 36, 1, JSEXN_ERR, "cyclic {0} value") -MSG_DEF(JSMSG_PERMANENT, 37, 1, JSEXN_ERR, "{0} is permanent") -MSG_DEF(JSMSG_CANT_CONVERT_TO, 38, 2, JSEXN_TYPEERR, "can't convert {0} to {1}") -MSG_DEF(JSMSG_NO_PROPERTIES, 39, 1, JSEXN_TYPEERR, "{0} has no properties") -MSG_DEF(JSMSG_CANT_FIND_CLASS, 40, 1, JSEXN_TYPEERR, "can't find class id {0}") -MSG_DEF(JSMSG_CANT_XDR_CLASS, 41, 1, JSEXN_TYPEERR, "can't XDR class {0}") -MSG_DEF(JSMSG_BYTECODE_TOO_BIG, 42, 2, JSEXN_INTERNALERR, "bytecode {0} too large (limit {1})") -MSG_DEF(JSMSG_UNKNOWN_FORMAT, 43, 1, JSEXN_INTERNALERR, "unknown bytecode format {0}") -MSG_DEF(JSMSG_TOO_MANY_CON_ARGS, 44, 0, JSEXN_SYNTAXERR, "too many constructor arguments") -MSG_DEF(JSMSG_TOO_MANY_FUN_ARGS, 45, 0, JSEXN_SYNTAXERR, "too many function arguments") -MSG_DEF(JSMSG_BAD_QUANTIFIER, 46, 1, JSEXN_SYNTAXERR, "invalid quantifier {0}") -MSG_DEF(JSMSG_MIN_TOO_BIG, 47, 1, JSEXN_SYNTAXERR, "overlarge minimum {0}") -MSG_DEF(JSMSG_MAX_TOO_BIG, 48, 1, JSEXN_SYNTAXERR, "overlarge maximum {0}") -MSG_DEF(JSMSG_OUT_OF_ORDER, 49, 1, JSEXN_SYNTAXERR, "maximum {0} less than minimum") -MSG_DEF(JSMSG_BAD_DESTRUCT_DECL, 50, 0, JSEXN_SYNTAXERR, "missing = in destructuring declaration") -MSG_DEF(JSMSG_BAD_DESTRUCT_ASS, 51, 0, JSEXN_SYNTAXERR, "invalid destructuring assignment operator") -MSG_DEF(JSMSG_PAREN_AFTER_LET, 52, 0, JSEXN_SYNTAXERR, "missing ) after let head") -MSG_DEF(JSMSG_CURLY_AFTER_LET, 53, 0, JSEXN_SYNTAXERR, "missing } after let block") -MSG_DEF(JSMSG_MISSING_PAREN, 54, 0, JSEXN_SYNTAXERR, "unterminated parenthetical") -MSG_DEF(JSMSG_UNTERM_CLASS, 55, 1, JSEXN_SYNTAXERR, "unterminated character class {0}") -MSG_DEF(JSMSG_TRAILING_SLASH, 56, 0, JSEXN_SYNTAXERR, "trailing \\ in regular expression") -MSG_DEF(JSMSG_BAD_CLASS_RANGE, 57, 0, JSEXN_SYNTAXERR, "invalid range in character class") -MSG_DEF(JSMSG_BAD_FLAG, 58, 1, JSEXN_SYNTAXERR, "invalid regular expression flag {0}") -MSG_DEF(JSMSG_NO_INPUT, 59, 3, JSEXN_SYNTAXERR, "no input for /{0}/{1}{2}") -MSG_DEF(JSMSG_CANT_OPEN, 60, 2, JSEXN_ERR, "can't open {0}: {1}") -MSG_DEF(JSMSG_BAD_STRING_MASK, 61, 1, JSEXN_ERR, "invalid string escape mask {0}") -MSG_DEF(JSMSG_UNMATCHED_RIGHT_PAREN, 62, 0, JSEXN_SYNTAXERR, "unmatched ) in regular expression") -MSG_DEF(JSMSG_END_OF_DATA, 63, 0, JSEXN_INTERNALERR, "unexpected end of data") -MSG_DEF(JSMSG_SEEK_BEYOND_START, 64, 0, JSEXN_INTERNALERR, "illegal seek beyond start") -MSG_DEF(JSMSG_SEEK_BEYOND_END, 65, 0, JSEXN_INTERNALERR, "illegal seek beyond end") -MSG_DEF(JSMSG_END_SEEK, 66, 0, JSEXN_INTERNALERR, "illegal end-based seek") -MSG_DEF(JSMSG_WHITHER_WHENCE, 67, 1, JSEXN_INTERNALERR, "unknown seek whence: {0}") -MSG_DEF(JSMSG_BAD_SCRIPT_MAGIC, 68, 0, JSEXN_INTERNALERR, "bad script XDR magic number") -MSG_DEF(JSMSG_PAREN_BEFORE_FORMAL, 69, 0, JSEXN_SYNTAXERR, "missing ( before formal parameters") -MSG_DEF(JSMSG_MISSING_FORMAL, 70, 0, JSEXN_SYNTAXERR, "missing formal parameter") -MSG_DEF(JSMSG_PAREN_AFTER_FORMAL, 71, 0, JSEXN_SYNTAXERR, "missing ) after formal parameters") -MSG_DEF(JSMSG_CURLY_BEFORE_BODY, 72, 0, JSEXN_SYNTAXERR, "missing { before function body") -MSG_DEF(JSMSG_CURLY_AFTER_BODY, 73, 0, JSEXN_SYNTAXERR, "missing } after function body") -MSG_DEF(JSMSG_PAREN_BEFORE_COND, 74, 0, JSEXN_SYNTAXERR, "missing ( before condition") -MSG_DEF(JSMSG_PAREN_AFTER_COND, 75, 0, JSEXN_SYNTAXERR, "missing ) after condition") -MSG_DEF(JSMSG_NO_IMPORT_NAME, 76, 0, JSEXN_SYNTAXERR, "missing name in import statement") -MSG_DEF(JSMSG_NAME_AFTER_DOT, 77, 0, JSEXN_SYNTAXERR, "missing name after . operator") -MSG_DEF(JSMSG_BRACKET_IN_INDEX, 78, 0, JSEXN_SYNTAXERR, "missing ] in index expression") -MSG_DEF(JSMSG_NO_EXPORT_NAME, 79, 0, JSEXN_SYNTAXERR, "missing name in export statement") -MSG_DEF(JSMSG_PAREN_BEFORE_SWITCH, 80, 0, JSEXN_SYNTAXERR, "missing ( before switch expression") -MSG_DEF(JSMSG_PAREN_AFTER_SWITCH, 81, 0, JSEXN_SYNTAXERR, "missing ) after switch expression") -MSG_DEF(JSMSG_CURLY_BEFORE_SWITCH, 82, 0, JSEXN_SYNTAXERR, "missing { before switch body") -MSG_DEF(JSMSG_COLON_AFTER_CASE, 83, 0, JSEXN_SYNTAXERR, "missing : after case label") -MSG_DEF(JSMSG_WHILE_AFTER_DO, 84, 0, JSEXN_SYNTAXERR, "missing while after do-loop body") -MSG_DEF(JSMSG_PAREN_AFTER_FOR, 85, 0, JSEXN_SYNTAXERR, "missing ( after for") -MSG_DEF(JSMSG_SEMI_AFTER_FOR_INIT, 86, 0, JSEXN_SYNTAXERR, "missing ; after for-loop initializer") -MSG_DEF(JSMSG_SEMI_AFTER_FOR_COND, 87, 0, JSEXN_SYNTAXERR, "missing ; after for-loop condition") -MSG_DEF(JSMSG_PAREN_AFTER_FOR_CTRL, 88, 0, JSEXN_SYNTAXERR, "missing ) after for-loop control") -MSG_DEF(JSMSG_CURLY_BEFORE_TRY, 89, 0, JSEXN_SYNTAXERR, "missing { before try block") -MSG_DEF(JSMSG_CURLY_AFTER_TRY, 90, 0, JSEXN_SYNTAXERR, "missing } after try block") -MSG_DEF(JSMSG_PAREN_BEFORE_CATCH, 91, 0, JSEXN_SYNTAXERR, "missing ( before catch") -MSG_DEF(JSMSG_CATCH_IDENTIFIER, 92, 0, JSEXN_SYNTAXERR, "missing identifier in catch") -MSG_DEF(JSMSG_PAREN_AFTER_CATCH, 93, 0, JSEXN_SYNTAXERR, "missing ) after catch") -MSG_DEF(JSMSG_CURLY_BEFORE_CATCH, 94, 0, JSEXN_SYNTAXERR, "missing { before catch block") -MSG_DEF(JSMSG_CURLY_AFTER_CATCH, 95, 0, JSEXN_SYNTAXERR, "missing } after catch block") -MSG_DEF(JSMSG_CURLY_BEFORE_FINALLY, 96, 0, JSEXN_SYNTAXERR, "missing { before finally block") -MSG_DEF(JSMSG_CURLY_AFTER_FINALLY, 97, 0, JSEXN_SYNTAXERR, "missing } after finally block") -MSG_DEF(JSMSG_CATCH_OR_FINALLY, 98, 0, JSEXN_SYNTAXERR, "missing catch or finally after try") -MSG_DEF(JSMSG_PAREN_BEFORE_WITH, 99, 0, JSEXN_SYNTAXERR, "missing ( before with-statement object") -MSG_DEF(JSMSG_PAREN_AFTER_WITH, 100, 0, JSEXN_SYNTAXERR, "missing ) after with-statement object") -MSG_DEF(JSMSG_CURLY_IN_COMPOUND, 101, 0, JSEXN_SYNTAXERR, "missing } in compound statement") -MSG_DEF(JSMSG_NO_VARIABLE_NAME, 102, 0, JSEXN_SYNTAXERR, "missing variable name") -MSG_DEF(JSMSG_COLON_IN_COND, 103, 0, JSEXN_SYNTAXERR, "missing : in conditional expression") -MSG_DEF(JSMSG_PAREN_AFTER_ARGS, 104, 0, JSEXN_SYNTAXERR, "missing ) after argument list") -MSG_DEF(JSMSG_BRACKET_AFTER_LIST, 105, 0, JSEXN_SYNTAXERR, "missing ] after element list") -MSG_DEF(JSMSG_COLON_AFTER_ID, 106, 0, JSEXN_SYNTAXERR, "missing : after property id") -MSG_DEF(JSMSG_CURLY_AFTER_LIST, 107, 0, JSEXN_SYNTAXERR, "missing } after property list") -MSG_DEF(JSMSG_PAREN_IN_PAREN, 108, 0, JSEXN_SYNTAXERR, "missing ) in parenthetical") -MSG_DEF(JSMSG_SEMI_BEFORE_STMNT, 109, 0, JSEXN_SYNTAXERR, "missing ; before statement") -MSG_DEF(JSMSG_NO_RETURN_VALUE, 110, 1, JSEXN_TYPEERR, "function {0} does not always return a value") -MSG_DEF(JSMSG_DUPLICATE_FORMAL, 111, 1, JSEXN_TYPEERR, "duplicate formal argument {0}") -MSG_DEF(JSMSG_EQUAL_AS_ASSIGN, 112, 1, JSEXN_SYNTAXERR, "test for equality (==) mistyped as assignment (=)?{0}") -MSG_DEF(JSMSG_BAD_IMPORT, 113, 0, JSEXN_SYNTAXERR, "invalid import expression") -MSG_DEF(JSMSG_TOO_MANY_DEFAULTS, 114, 0, JSEXN_SYNTAXERR, "more than one switch default") -MSG_DEF(JSMSG_TOO_MANY_CASES, 115, 0, JSEXN_INTERNALERR, "too many switch cases") -MSG_DEF(JSMSG_BAD_SWITCH, 116, 0, JSEXN_SYNTAXERR, "invalid switch statement") -MSG_DEF(JSMSG_BAD_FOR_LEFTSIDE, 117, 0, JSEXN_SYNTAXERR, "invalid for/in left-hand side") -MSG_DEF(JSMSG_CATCH_AFTER_GENERAL, 118, 0, JSEXN_SYNTAXERR, "catch after unconditional catch") -MSG_DEF(JSMSG_CATCH_WITHOUT_TRY, 119, 0, JSEXN_SYNTAXERR, "catch without try") -MSG_DEF(JSMSG_FINALLY_WITHOUT_TRY, 120, 0, JSEXN_SYNTAXERR, "finally without try") -MSG_DEF(JSMSG_LABEL_NOT_FOUND, 121, 0, JSEXN_SYNTAXERR, "label not found") -MSG_DEF(JSMSG_TOUGH_BREAK, 122, 0, JSEXN_SYNTAXERR, "invalid break") -MSG_DEF(JSMSG_BAD_CONTINUE, 123, 0, JSEXN_SYNTAXERR, "invalid continue") -MSG_DEF(JSMSG_BAD_RETURN_OR_YIELD, 124, 1, JSEXN_SYNTAXERR, "{0} not in function") -MSG_DEF(JSMSG_BAD_LABEL, 125, 0, JSEXN_SYNTAXERR, "invalid label") -MSG_DEF(JSMSG_DUPLICATE_LABEL, 126, 0, JSEXN_SYNTAXERR, "duplicate label") -MSG_DEF(JSMSG_VAR_HIDES_ARG, 127, 1, JSEXN_TYPEERR, "variable {0} hides argument") -MSG_DEF(JSMSG_BAD_VAR_INIT, 128, 0, JSEXN_SYNTAXERR, "invalid variable initialization") -MSG_DEF(JSMSG_BAD_LEFTSIDE_OF_ASS, 129, 0, JSEXN_SYNTAXERR, "invalid assignment left-hand side") -MSG_DEF(JSMSG_BAD_OPERAND, 130, 1, JSEXN_SYNTAXERR, "invalid {0} operand") -MSG_DEF(JSMSG_BAD_PROP_ID, 131, 0, JSEXN_SYNTAXERR, "invalid property id") -MSG_DEF(JSMSG_RESERVED_ID, 132, 1, JSEXN_SYNTAXERR, "{0} is a reserved identifier") -MSG_DEF(JSMSG_SYNTAX_ERROR, 133, 0, JSEXN_SYNTAXERR, "syntax error") -MSG_DEF(JSMSG_BAD_SHARP_VAR_DEF, 134, 0, JSEXN_SYNTAXERR, "invalid sharp variable definition") -MSG_DEF(JSMSG_BAD_PROTOTYPE, 135, 1, JSEXN_TYPEERR, "'prototype' property of {0} is not an object") -MSG_DEF(JSMSG_MISSING_EXPONENT, 136, 0, JSEXN_SYNTAXERR, "missing exponent") -MSG_DEF(JSMSG_OUT_OF_MEMORY, 137, 0, JSEXN_ERR, "out of memory") -MSG_DEF(JSMSG_UNTERMINATED_STRING, 138, 0, JSEXN_SYNTAXERR, "unterminated string literal") -MSG_DEF(JSMSG_TOO_MANY_PARENS, 139, 0, JSEXN_INTERNALERR, "too many parentheses in regular expression") -MSG_DEF(JSMSG_UNTERMINATED_COMMENT, 140, 0, JSEXN_SYNTAXERR, "unterminated comment") -MSG_DEF(JSMSG_UNTERMINATED_REGEXP, 141, 0, JSEXN_SYNTAXERR, "unterminated regular expression literal") -MSG_DEF(JSMSG_BAD_REGEXP_FLAG, 142, 0, JSEXN_SYNTAXERR, "invalid flag after regular expression") -MSG_DEF(JSMSG_SHARPVAR_TOO_BIG, 143, 0, JSEXN_SYNTAXERR, "overlarge sharp variable number") -MSG_DEF(JSMSG_ILLEGAL_CHARACTER, 144, 0, JSEXN_SYNTAXERR, "illegal character") -MSG_DEF(JSMSG_BAD_OCTAL, 145, 1, JSEXN_SYNTAXERR, "{0} is not a legal ECMA-262 octal constant") -MSG_DEF(JSMSG_BAD_INDIRECT_CALL, 146, 1, JSEXN_EVALERR, "function {0} must be called directly, and not by way of a function of another name") -MSG_DEF(JSMSG_UNCAUGHT_EXCEPTION, 147, 1, JSEXN_INTERNALERR, "uncaught exception: {0}") -MSG_DEF(JSMSG_INVALID_BACKREF, 148, 0, JSEXN_SYNTAXERR, "non-octal digit in an escape sequence that doesn't match a back-reference") -MSG_DEF(JSMSG_BAD_BACKREF, 149, 0, JSEXN_SYNTAXERR, "back-reference exceeds number of capturing parentheses") -MSG_DEF(JSMSG_PRECISION_RANGE, 150, 1, JSEXN_RANGEERR, "precision {0} out of range") -MSG_DEF(JSMSG_BAD_GETTER_OR_SETTER, 151, 1, JSEXN_SYNTAXERR, "invalid {0} usage") -MSG_DEF(JSMSG_BAD_ARRAY_LENGTH, 152, 0, JSEXN_RANGEERR, "invalid array length") -MSG_DEF(JSMSG_CANT_DESCRIBE_PROPS, 153, 1, JSEXN_TYPEERR, "can't describe non-native properties of class {0}") -MSG_DEF(JSMSG_BAD_APPLY_ARGS, 154, 1, JSEXN_TYPEERR, "second argument to Function.prototype.{0} must be an array") -MSG_DEF(JSMSG_REDECLARED_VAR, 155, 2, JSEXN_TYPEERR, "redeclaration of {0} {1}") -MSG_DEF(JSMSG_UNDECLARED_VAR, 156, 1, JSEXN_TYPEERR, "assignment to undeclared variable {0}") -MSG_DEF(JSMSG_ANON_NO_RETURN_VALUE, 157, 0, JSEXN_TYPEERR, "anonymous function does not always return a value") -MSG_DEF(JSMSG_DEPRECATED_USAGE, 158, 1, JSEXN_REFERENCEERR, "deprecated {0} usage") -MSG_DEF(JSMSG_BAD_URI, 159, 0, JSEXN_URIERR, "malformed URI sequence") -MSG_DEF(JSMSG_GETTER_ONLY, 160, 0, JSEXN_TYPEERR, "setting a property that has only a getter") -MSG_DEF(JSMSG_TRAILING_COMMA, 161, 0, JSEXN_SYNTAXERR, "trailing comma is not legal in ECMA-262 object initializers") -MSG_DEF(JSMSG_UNDEFINED_PROP, 162, 1, JSEXN_REFERENCEERR, "reference to undefined property {0}") -MSG_DEF(JSMSG_USELESS_EXPR, 163, 0, JSEXN_TYPEERR, "useless expression") -MSG_DEF(JSMSG_REDECLARED_PARAM, 164, 1, JSEXN_TYPEERR, "redeclaration of formal parameter {0}") -MSG_DEF(JSMSG_NEWREGEXP_FLAGGED, 165, 0, JSEXN_TYPEERR, "can't supply flags when constructing one RegExp from another") -MSG_DEF(JSMSG_RESERVED_SLOT_RANGE, 166, 0, JSEXN_RANGEERR, "reserved slot index out of range") -MSG_DEF(JSMSG_CANT_DECODE_PRINCIPALS, 167, 0, JSEXN_INTERNALERR, "can't decode JSPrincipals") -MSG_DEF(JSMSG_CANT_SEAL_OBJECT, 168, 1, JSEXN_ERR, "can't seal {0} objects") -MSG_DEF(JSMSG_TOO_MANY_CATCH_VARS, 169, 0, JSEXN_SYNTAXERR, "too many catch variables") -MSG_DEF(JSMSG_BAD_XML_MARKUP, 170, 0, JSEXN_SYNTAXERR, "invalid XML markup") -MSG_DEF(JSMSG_BAD_XML_CHARACTER, 171, 0, JSEXN_SYNTAXERR, "illegal XML character") -MSG_DEF(JSMSG_BAD_DEFAULT_XML_NAMESPACE,172,0,JSEXN_SYNTAXERR, "invalid default XML namespace") -MSG_DEF(JSMSG_BAD_XML_NAME_SYNTAX, 173, 0, JSEXN_SYNTAXERR, "invalid XML name") -MSG_DEF(JSMSG_BRACKET_AFTER_ATTR_EXPR,174, 0, JSEXN_SYNTAXERR, "missing ] after attribute expression") -MSG_DEF(JSMSG_NESTING_GENERATOR, 175, 1, JSEXN_TYPEERR, "already executing generator {0}") -MSG_DEF(JSMSG_CURLY_IN_XML_EXPR, 176, 0, JSEXN_SYNTAXERR, "missing } in XML expression") -MSG_DEF(JSMSG_BAD_XML_NAMESPACE, 177, 1, JSEXN_TYPEERR, "invalid XML namespace {0}") -MSG_DEF(JSMSG_BAD_XML_ATTR_NAME, 178, 1, JSEXN_TYPEERR, "invalid XML attribute name {0}") -MSG_DEF(JSMSG_BAD_XML_NAME, 179, 1, JSEXN_TYPEERR, "invalid XML name {0}") -MSG_DEF(JSMSG_BAD_XML_CONVERSION, 180, 1, JSEXN_TYPEERR, "can't convert {0} to XML") -MSG_DEF(JSMSG_BAD_XMLLIST_CONVERSION, 181, 1, JSEXN_TYPEERR, "can't convert {0} to XMLList") -MSG_DEF(JSMSG_BAD_GENERATOR_SEND, 182, 1, JSEXN_TYPEERR, "attempt to send {0} to newborn generator") -MSG_DEF(JSMSG_NO_ASSIGN_IN_XML_ATTR, 183, 0, JSEXN_SYNTAXERR, "missing = in XML attribute") -MSG_DEF(JSMSG_BAD_XML_ATTR_VALUE, 184, 0, JSEXN_SYNTAXERR, "invalid XML attribute value") -MSG_DEF(JSMSG_XML_TAG_NAME_MISMATCH, 185, 1, JSEXN_SYNTAXERR, "XML tag name mismatch (expected {0})") -MSG_DEF(JSMSG_BAD_XML_TAG_SYNTAX, 186, 0, JSEXN_SYNTAXERR, "invalid XML tag syntax") -MSG_DEF(JSMSG_BAD_XML_LIST_SYNTAX, 187, 0, JSEXN_SYNTAXERR, "invalid XML list syntax") -MSG_DEF(JSMSG_INCOMPATIBLE_METHOD, 188, 3, JSEXN_TYPEERR, "{0} {1} called on incompatible {2}") -MSG_DEF(JSMSG_CANT_SET_XML_ATTRS, 189, 0, JSEXN_INTERNALERR, "can't set XML property attributes") -MSG_DEF(JSMSG_END_OF_XML_SOURCE, 190, 0, JSEXN_SYNTAXERR, "unexpected end of XML source") -MSG_DEF(JSMSG_END_OF_XML_ENTITY, 191, 0, JSEXN_SYNTAXERR, "unexpected end of XML entity") -MSG_DEF(JSMSG_BAD_XML_QNAME, 192, 0, JSEXN_SYNTAXERR, "invalid XML qualified name") -MSG_DEF(JSMSG_BAD_FOR_EACH_LOOP, 193, 0, JSEXN_SYNTAXERR, "invalid for each loop") -MSG_DEF(JSMSG_BAD_XMLLIST_PUT, 194, 1, JSEXN_TYPEERR, "can't set property {0} in XMLList") -MSG_DEF(JSMSG_UNKNOWN_XML_ENTITY, 195, 1, JSEXN_TYPEERR, "unknown XML entity {0}") -MSG_DEF(JSMSG_BAD_XML_NCR, 196, 1, JSEXN_TYPEERR, "malformed XML character {0}") -MSG_DEF(JSMSG_UNDEFINED_XML_NAME, 197, 1, JSEXN_REFERENCEERR, "reference to undefined XML name {0}") -MSG_DEF(JSMSG_DUPLICATE_XML_ATTR, 198, 1, JSEXN_TYPEERR, "duplicate XML attribute {0}") -MSG_DEF(JSMSG_TOO_MANY_FUN_VARS, 199, 0, JSEXN_SYNTAXERR, "too many local variables") -MSG_DEF(JSMSG_ARRAY_INIT_TOO_BIG, 200, 0, JSEXN_INTERNALERR, "array initialiser too large") -MSG_DEF(JSMSG_REGEXP_TOO_COMPLEX, 201, 0, JSEXN_INTERNALERR, "regular expression too complex") -MSG_DEF(JSMSG_BUFFER_TOO_SMALL, 202, 0, JSEXN_INTERNALERR, "buffer too small") -MSG_DEF(JSMSG_BAD_SURROGATE_CHAR, 203, 1, JSEXN_TYPEERR, "bad surrogate character {0}") -MSG_DEF(JSMSG_UTF8_CHAR_TOO_LARGE, 204, 1, JSEXN_TYPEERR, "UTF-8 character {0} too large") -MSG_DEF(JSMSG_MALFORMED_UTF8_CHAR, 205, 1, JSEXN_TYPEERR, "malformed UTF-8 character sequence at offset {0}") -MSG_DEF(JSMSG_USER_DEFINED_ERROR, 206, 0, JSEXN_ERR, "JS_ReportError was called") -MSG_DEF(JSMSG_WRONG_CONSTRUCTOR, 207, 1, JSEXN_TYPEERR, "wrong constructor called for {0}") -MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 208, 1, JSEXN_TYPEERR, "generator function {0} returns a value") -MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 209, 0, JSEXN_TYPEERR, "anonymous generator function returns a value") -MSG_DEF(JSMSG_NAME_AFTER_FOR_PAREN, 210, 0, JSEXN_SYNTAXERR, "missing name after for (") -MSG_DEF(JSMSG_IN_AFTER_FOR_NAME, 211, 0, JSEXN_SYNTAXERR, "missing in after for") -MSG_DEF(JSMSG_BAD_ITERATOR_RETURN, 212, 2, JSEXN_TYPEERR, "{0}.{1} returned a primitive value") -MSG_DEF(JSMSG_KEYWORD_NOT_NS, 213, 0, JSEXN_SYNTAXERR, "keyword is used as namespace") -MSG_DEF(JSMSG_BAD_GENERATOR_YIELD, 214, 1, JSEXN_TYPEERR, "yield from closing generator {0}") -MSG_DEF(JSMSG_BAD_YIELD_SYNTAX, 215, 0, JSEXN_SYNTAXERR, "yield expression must be parenthesized") -MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE, 216, 0, JSEXN_SYNTAXERR, "invalid array comprehension left-hand side") -MSG_DEF(JSMSG_YIELD_FROM_FILTER, 217, 0, JSEXN_INTERNALERR, "yield not yet supported from filtering predicate") -MSG_DEF(JSMSG_COMPILE_EXECED_SCRIPT, 218, 0, JSEXN_TYPEERR, "cannot compile over a script that is currently executing") -MSG_DEF(JSMSG_NON_LIST_XML_METHOD, 219, 2, JSEXN_TYPEERR, "cannot call {0} method on an XML list with {1} elements") diff --git a/spidermonkey/src/js.pkg b/spidermonkey/src/js.pkg deleted file mode 100644 index 93185a9..0000000 --- a/spidermonkey/src/js.pkg +++ /dev/null @@ -1,2 +0,0 @@ -[gecko xpi-bootstrap] -dist/bin/@SHARED_LIBRARY@ diff --git a/spidermonkey/src/jsOS240.def b/spidermonkey/src/jsOS240.def deleted file mode 100644 index 8f27d64..0000000 --- a/spidermonkey/src/jsOS240.def +++ /dev/null @@ -1,654 +0,0 @@ -; ***** 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 Communicator client code, released -; March 31, 1998. -; -; 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 of 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 ***** - -LIBRARY JS3240 INITINSTANCE TERMINSTANCE -PROTMODE - -DESCRIPTION 'Netscape OS/2 JavaScript Library' - - -CODE LOADONCALL MOVEABLE DISCARDABLE -DATA PRELOAD MOVEABLE MULTIPLE NONSHARED - - -EXPORTS -;====================== win16 exports these at least... =========== -; JS_Init = JS_Init @2 -; JS_Finish = JS_Finish @3 -; JS_GetNaNValue -; JS_GetNegativeInfinityValue -; JS_GetPositiveInfinityValue -; JS_GetEmptyStringValue -; JS_ConvertValue -; JS_ValueToObject -; JS_ValueToFunction -; JS_ValueToString -; JS_ValueToNumber -; JS_ValueToBoolean -; JS_TypeOfValue -; JS_GetTypeName -; JS_Lock -; JS_Unlock -; JS_NewContext -; JS_DestroyContext -; JS_ContextIterator -; JS_GetGlobalObject -; JS_SetGlobalObject -; JS_InitStandardClasses -;; JS_GetStaticLink -; JS_malloc -; JS_realloc -; JS_free -; JS_strdup -; JS_NewDouble -; JS_NewDoubleValue -; JS_AddRoot -; JS_RemoveRoot -; JS_LockGCThing -; JS_UnlockGCThing -; JS_GC -; JS_PropertyStub -; JS_EnumerateStub -; JS_ResolveStub -; JS_ConvertStub -; JS_FinalizeStub -; JS_InitClass -; JS_GetClass -; JS_InstanceOf -; JS_GetPrivate -; JS_SetPrivate -; JS_GetInstancePrivate -; JS_GetPrototype -; JS_GetParent -; JS_SetParent -; JS_GetConstructor -; JS_NewObject -; JS_DefineObject -; JS_DefineConstDoubles -; JS_DefineProperties -; JS_DefineProperty -; JS_DefinePropertyWithTinyId -; JS_AliasProperty -; JS_LookupProperty -; JS_GetProperty -; JS_SetProperty -; JS_DeleteProperty -; JS_NewArrayObject -; JS_DefineElement -; JS_AliasElement -; JS_LookupElement -; JS_GetElement -; JS_SetElement -; JS_DeleteElement -; JS_ClearScope -; JS_NewFunction -; JS_GetFunctionObject -; JS_GetFunctionName -; JS_DefineFunctions -; JS_DefineFunction -; JS_CompileScript -; JS_DestroyScript -; JS_CompileFunction -; JS_DecompileScript -; JS_DecompileFunction -; JS_DecompileFunctionBody -; JS_ExecuteScript -; JS_EvaluateScript -; JS_CallFunction -; JS_CallFunctionName -; JS_CallFunctionValue -; JS_SetBranchCallback -; JS_IsRunning -; JS_IsConstructing -; JS_SetCallReturnValue2 -; JS_NewString -; JS_NewStringCopyN -; JS_NewStringCopyZ -; JS_InternString -; JS_GetStringBytes -; JS_GetStringLength -; JS_CompareStrings -; JS_ReportError -; JS_ReportOutOfMemory -; JS_SetErrorReporter -; JS_NewRegExpObject -; JS_SetRegExpInput -; JS_ClearRegExpStatics -;================================================= - - -;00001:jsstr (OFFSET:0x00002e17, SIZE:0x0000ae17): -; - Public Definitions: -; js_EmptySubString -; js_CompareStrings -; js_HashString -; js_ValueToString -; js_StringToObject -; js_FinalizeString -; js_NewStringCopyZ -; js_NewString -; js_InitStringClass -; js_NewStringCopyN -; js_BoyerMooreHorspool -; -; -;00002:jsscript (OFFSET:0x0000dc2e, SIZE:0x00003abb): -; - Public Definitions: -; js_LineNumberToPC -; js_PCToLineNumber -; js_GetSrcNote -; js_DestroyScript -; js_NewScript -; -; -;00003:jsscope (OFFSET:0x000116e9, SIZE:0x00004f82): -; - Public Definitions: -; js_hash_scope_ops -; js_list_scope_ops -; js_DestroyProperty -; js_NewProperty -; js_IdToValue -; js_HashValue -; js_DestroyScope -; js_MutateScope -; js_DropScope -; js_HoldScope -; js_NewScope -; js_GetMutableScope -; js_HoldProperty -; js_DropProperty -; -; -;00004:jsscan (OFFSET:0x0001666b, SIZE:0x00008890): -; - Public Definitions: -; js_MatchToken -; js_FlushNewlines -; js_PeekTokenSameLine -; js_UngetToken -; js_GetToken -; js_PeekToken -; js_ReportCompileError - js_CloseTokenStream - js_NewBufferTokenStream -; js_NewTokenStream -; js_InitScanner -; -; -;00005:jsregexp (OFFSET:0x0001eefb, SIZE:0x0000eee4): -; - Public Definitions: -; js_RegExpClass -; reopsize -; js_NewRegExpObject -; js_InitRegExpClass -; js_FreeRegExpStatics -; js_InitRegExpStatics -; js_ExecuteRegExp -; js_NewRegExpOpt -; js_DestroyRegExp -; js_NewRegExp -; -; -;00006:jsparse (OFFSET:0x0002dddf, SIZE:0x00010b71): -; - Public Definitions: -; js_ParseFunctionBody - js_Parse -; -; -;00007:jsopcode (OFFSET:0x0003e950, SIZE:0x0000d362): -; - Public Definitions: -; js_EscapeMap -; js_NumCodeSpecs -; js_CodeSpec -; js_incop_str -; js_true_str -; js_false_str -; js_this_str -; js_null_str -; js_void_str -; js_typeof_str -; js_delete_str -; js_new_str -; js_ValueToSource -; js_DecompileScript -; js_DecompileCode -; js_DecompileFunction -; js_puts -; js_printf -; js_GetPrinterOutput -; js_DestroyPrinter -; js_NewPrinter -; js_EscapeString -; js_Disassemble1 -; js_Disassemble -; -;00008:jsobj (OFFSET:0x0004bcb2, SIZE:0x000090a4): -; - Public Definitions: -; js_WithClass -; js_ObjectClass -; js_TryValueOf -; js_ValueToNonNullObject -; js_TryMethod -; js_ObjectToString -; js_SetClassPrototype -; js_DeleteProperty2 -; js_DeleteProperty -; js_SetProperty -; js_GetProperty -; js_FindVariableScope -; js_FindVariable -; js_FindProperty -; js_LookupProperty -; js_DefineProperty -; js_FreeSlot -; js_AllocSlot -; js_FinalizeObject -; js_GetClassPrototype -; js_NewObject -; js_InitObjectClass -; js_ValueToObject -; js_obj_toString -; js_SetSlot -; js_GetSlot -; -; -;00009:jsnum (OFFSET:0x00054d56, SIZE:0x00004f29): -; - Public Definitions: -; js_ValueToInt32 -; js_NumberToObject -; js_FinalizeDouble -; js_InitNumberClass -; js_NumberToString -; js_NewDoubleValue -; js_NewDouble -; js_ValueToNumber -; -; -;00010:jsmath (OFFSET:0x00059c7f, SIZE:0x000054b6): -; - Public Definitions: -; js_InitMathClass -; -; -;00011:jsjava (OFFSET:0x0005f135, SIZE:0x00022aad): -; - Public Definitions: -; js_Hooks -; MojaSrcLog -; finalizeTask - JSJ_FindCurrentJSContext -; JSJ_GetPrincipals - JSJ_IsSafeMethod - JSJ_InitContext - JSJ_Init - js_JSErrorToJException - js_JavaErrorReporter - js_RemoveReflection - js_ReflectJObjectToJSObject - js_convertJObjectToJSValue - js_convertJSValueToJObject - js_ReflectJSObjectToJObject -; js_ReflectJClassToJSObject - JSJ_ExitJS - JSJ_EnterJS - JSJ_CurrentContext - JSJ_IsEnabled -;added in GA code - DSR70297 - JSJ_Finish - JSJ_IsCalledFromJava - js_GetJSPrincipalsFromJavaCaller - -; -; -;00012:jsinterp (OFFSET:0x00081be2, SIZE:0x00012274): -; - Public Definitions: -; js_Call -; js_Interpret -; js_SetLocalVariable -; js_GetLocalVariable -; js_SetArgument -; js_GetArgument -; js_FlushPropertyCacheByProp -; js_FlushPropertyCache -; -; -;00013:jsgc (OFFSET:0x00093e56, SIZE:0x00004f8d): -; - Public Definitions: -; js_ForceGC -; js_UnlockGCThing -; js_LockGCThing -; js_GC -; js_AllocGCThing -; js_RemoveRoot -; js_AddRoot -; js_FinishGC -; js_InitGC -; -; -;00014:jsfun (OFFSET:0x00098de3, SIZE:0x0000977c): -; - Public Definitions: -; js_FunctionClass -; js_ClosureClass -; js_CallClass -; js_DefineFunction -; js_NewFunction -; js_InitCallAndClosureClasses -; js_InitFunctionClass -; js_ValueToFunction -; js_SetCallVariable -; js_GetCallVariable -; js_PutCallObject -; js_GetCallObject -; -; -;00015:jsemit (OFFSET:0x000a255f, SIZE:0x000077be): -; - Public Definitions: -; js_SrcNoteName -; js_SrcNoteArity - js_FinishTakingSrcNotes -; js_MoveSrcNotes -; js_GetSrcNoteOffset -; js_BumpSrcNoteDelta -; js_NewSrcNote3 -; js_NewSrcNote2 -; js_PopStatement -; js_EmitContinue -; js_EmitBreak -; js_SetSrcNoteOffset -; js_NewSrcNote -; js_PushStatement -; js_MoveCode -; js_SetJumpOffset -; js_Emit3 -; js_Emit2 -; js_Emit1 -; js_UpdateDepth -; js_SrcNoteLength -; js_CancelLastOpcode - js_InitCodeGenerator -; -; -;00016:jsdbgapi (OFFSET:0x000a9d1d, SIZE:0x000057db): -; - Public Definitions: -; js_watchpoint_list -; js_trap_list -; JS_SetAnnotationInFrame -; JS_GetAnnotationFromFrame -; JS_GetJSPrincipalArrayFromFrame -; JS_NextJSFrame -; JS_InitJSFrameIterator - JS_LineNumberToPC - JS_PCToLineNumber - JS_ClearAllWatchPoints - JS_ClearWatchPoint - JS_SetWatchPoint - JS_HandleTrap - JS_ClearAllTraps - JS_ClearScriptTraps - JS_ClearTrap - JS_GetTrapOpcode - JS_SetTrap -;DSR070297 - added in GA code - JS_FrameIterator - JS_GetFrameAnnotation - JS_GetFramePrincipalArray - JS_GetFrameScript - JS_GetScriptFilename - JS_SetFrameAnnotation - JS_GetFramePC - JS_GetFunctionScript - -; -; -;00017:jsdate (OFFSET:0x000af4f8, SIZE:0x00009a8e): -; - Public Definitions: - js_DateGetSeconds - js_DateGetMinutes - js_DateGetHours - js_DateGetDate - js_DateGetMonth - js_DateGetYear - js_NewDateObject -; js_InitDateClass -; -; -;00018:jscntxt (OFFSET:0x000b8f86, SIZE:0x00003732): -; - Public Definitions: -; js_InterpreterHooks -; js_ReportIsNotDefined -; js_ReportErrorAgain -; js_ReportErrorVA -; js_ContextIterator -; js_DestroyContext -; js_NewContext -; js_SetInterpreterHooks -; -; -;00019:jsbool (OFFSET:0x000bc6b8, SIZE:0x00003375): -; - Public Definitions: -; js_BooleanToString -; js_BooleanToObject -; js_InitBooleanClass -; js_ValueToBoolean -; -; -;00020:jsatom (OFFSET:0x000bfa2d, SIZE:0x000058d0): -; - Public Definitions: -; js_valueOf_str -; js_toString_str -; js_length_str -; js_eval_str -; js_constructor_str -; js_class_prototype_str -; js_assign_str -; js_anonymous_str -; js_Object_str -; js_Array_str -; js_type_str -; js_DropUnmappedAtoms - js_FreeAtomMap - js_InitAtomMap -; js_GetAtom -; js_DropAtom -; js_IndexAtom -; js_ValueToStringAtom -; js_AtomizeString -; js_AtomizeDouble -; js_AtomizeInt -; js_AtomizeBoolean -; js_AtomizeObject -; js_HoldAtom -; js_MarkAtomState -; js_FreeAtomState -; js_Atomize -; js_InitAtomState -; -; -;00021:jsarray (OFFSET:0x000c52fd, SIZE:0x00007c86): -; - Public Definitions: -; js_ArrayClass -; js_SetArrayLength -; js_GetArrayLength -; js_InitArrayClass -; js_NewArrayObject -; PR_qsort -; -; -;00022:jsapi (OFFSET:0x000ccf83, SIZE:0x0000de8c): -; - Public Definitions: - JS_ClearRegExpStatics - JS_SetRegExpInput - JS_NewRegExpObject - JS_SetErrorReporter - JS_CompareStrings - JS_GetStringLength - JS_GetStringBytes - JS_InternString - JS_NewStringCopyZ - JS_NewStringCopyN - JS_NewString - JS_IsRunning - JS_SetBranchCallback - JS_CallFunctionValue - JS_CallFunctionName - JS_CallFunction - JS_EvaluateScriptForPrincipals - JS_EvaluateScript - JS_ExecuteScript - JS_DecompileFunctionBody - JS_DecompileFunction - JS_DecompileScript - JS_CompileFunctionForPrincipals - JS_CompileFunction - JS_DestroyScript - JS_CompileScriptForPrincipals - JS_CompileScript - JS_DefineFunction - JS_GetFunctionName - JS_GetFunctionObject - JS_NewFunction - JS_ClearScope - JS_DeleteElement - JS_SetElement - JS_GetElement - JS_LookupElement - JS_AliasElement - JS_DefineElement - JS_SetArrayLength - JS_GetArrayLength - JS_NewArrayObject - JS_DeleteProperty - JS_SetProperty - JS_GetProperty - JS_LookupProperty - JS_AliasProperty - JS_DefinePropertyWithTinyId - JS_DefineProperty - JS_DefineConstDoubles - JS_DefineObject - JS_NewObject - JS_GetConstructor - JS_SetParent - JS_GetParent - JS_SetPrototype - JS_GetPrototype - JS_GetInstancePrivate - JS_SetPrivate - JS_GetPrivate - JS_InstanceOf - JS_GetClass - JS_DefineFunctions - JS_DefineProperties - JS_InitClass - JS_FinalizeStub - JS_ConvertStub - JS_ResolveStub - JS_EnumerateStub - JS_PropertyStub - JS_GC - JS_UnlockGCThing - JS_LockGCThing - JS_RemoveRoot - JS_AddRoot - JS_NewDoubleValue - JS_NewDouble - JS_strdup - JS_free - JS_realloc - JS_ReportOutOfMemory - JS_malloc - JS_GetScopeChain - JS_InitStandardClasses - JS_SetGlobalObject - JS_GetGlobalObject - JS_SetVersion - JS_GetVersion - JS_ContextIterator - JS_GetTaskState - JS_DestroyContext - JS_NewContext - JS_Unlock - JS_Lock - JS_Finish - JS_Init - JS_GetTypeName - JS_TypeOfValue - JS_ValueToBoolean - JS_ValueToInt32 - JS_ValueToNumber - JS_ValueToString - JS_ValueToFunction - JS_ValueToObject - JS_ReportError - JS_ConvertValue - JS_GetEmptyStringValue - JS_GetPositiveInfinityValue - JS_GetNegativeInfinityValue - JS_GetNaNValue -;DSR062897 - added for GA code - JS_MaybeGC - JS_GetScriptPrincipals - JS_IsAssigning - JS_SetCharSetInfo -;brendan@mozilla.org, 2-Sept-2000 - JS_SetCallReturnValue2 - JS_SetGCCallback - JS_SetGCCallbackRT - JS_AddExternalStringFinalizer - JS_RemoveExternalStringFinalizer - JS_NewExternalString -; -; -;00023:prmjtime (OFFSET:0x000dae0f, SIZE:0x00008986): -; - Public Definitions: - PRMJ_FormatTimeUSEnglish - PRMJ_gmtime - PRMJ_FormatTime - PRMJ_mktime - PRMJ_ComputeTime - PRMJ_localtime - PRMJ_ExplodeTime - PRMJ_ToLocal - PRMJ_ToGMT - PRMJ_NowLocal - PRMJ_DSTOffset - PRMJ_NowS - PRMJ_NowMS - PRMJ_Now - PRMJ_ToExtendedTime - PRMJ_ToBaseTime - PRMJ_setDST - PRMJ_LocalGMTDifference - - diff --git a/spidermonkey/src/jsapi.c b/spidermonkey/src/jsapi.c deleted file mode 100644 index 221e4c4..0000000 --- a/spidermonkey/src/jsapi.c +++ /dev/null @@ -1,5023 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JavaScript API. - */ -#include "jsstddef.h" -#include -#include -#include -#include -#include "jstypes.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ -#include "jsclist.h" -#include "jsdhash.h" -#include "jsprf.h" -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jsbool.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsdate.h" -#include "jsdtoa.h" -#include "jsemit.h" -#include "jsexn.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsmath.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsparse.h" -#include "jsregexp.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" -#include "prmjtime.h" - -#if JS_HAS_FILE_OBJECT -#include "jsfile.h" -#endif - -#if JS_HAS_XML_SUPPORT -#include "jsxml.h" -#endif - -#if JS_HAS_GENERATORS -#include "jsiter.h" -#endif - -#ifdef HAVE_VA_LIST_AS_ARRAY -#define JS_ADDRESSOF_VA_LIST(ap) ((va_list *)(ap)) -#else -#define JS_ADDRESSOF_VA_LIST(ap) (&(ap)) -#endif - -#if defined(JS_PARANOID_REQUEST) && defined(JS_THREADSAFE) -#define CHECK_REQUEST(cx) JS_ASSERT(cx->requestDepth) -#else -#define CHECK_REQUEST(cx) ((void)0) -#endif - -JS_PUBLIC_API(int64) -JS_Now() -{ - return PRMJ_Now(); -} - -JS_PUBLIC_API(jsval) -JS_GetNaNValue(JSContext *cx) -{ - return DOUBLE_TO_JSVAL(cx->runtime->jsNaN); -} - -JS_PUBLIC_API(jsval) -JS_GetNegativeInfinityValue(JSContext *cx) -{ - return DOUBLE_TO_JSVAL(cx->runtime->jsNegativeInfinity); -} - -JS_PUBLIC_API(jsval) -JS_GetPositiveInfinityValue(JSContext *cx) -{ - return DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity); -} - -JS_PUBLIC_API(jsval) -JS_GetEmptyStringValue(JSContext *cx) -{ - return STRING_TO_JSVAL(cx->runtime->emptyString); -} - -static JSBool -TryArgumentFormatter(JSContext *cx, const char **formatp, JSBool fromJS, - jsval **vpp, va_list *app) -{ - const char *format; - JSArgumentFormatMap *map; - - format = *formatp; - for (map = cx->argumentFormatMap; map; map = map->next) { - if (!strncmp(format, map->format, map->length)) { - *formatp = format + map->length; - return map->formatter(cx, format, fromJS, vpp, app); - } - } - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CHAR, format); - return JS_FALSE; -} - -JS_PUBLIC_API(JSBool) -JS_ConvertArguments(JSContext *cx, uintN argc, jsval *argv, const char *format, - ...) -{ - va_list ap; - JSBool ok; - - va_start(ap, format); - ok = JS_ConvertArgumentsVA(cx, argc, argv, format, ap); - va_end(ap); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv, - const char *format, va_list ap) -{ - jsval *sp; - JSBool required; - char c; - JSFunction *fun; - jsdouble d; - JSString *str; - JSObject *obj; - - CHECK_REQUEST(cx); - sp = argv; - required = JS_TRUE; - while ((c = *format++) != '\0') { - if (isspace(c)) - continue; - if (c == '/') { - required = JS_FALSE; - continue; - } - if (sp == argv + argc) { - if (required) { - fun = js_ValueToFunction(cx, &argv[-2], 0); - if (fun) { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%u", argc); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_MORE_ARGS_NEEDED, - JS_GetFunctionName(fun), numBuf, - (argc == 1) ? "" : "s"); - } - return JS_FALSE; - } - break; - } - switch (c) { - case 'b': - if (!js_ValueToBoolean(cx, *sp, va_arg(ap, JSBool *))) - return JS_FALSE; - break; - case 'c': - if (!js_ValueToUint16(cx, *sp, va_arg(ap, uint16 *))) - return JS_FALSE; - break; - case 'i': - if (!js_ValueToECMAInt32(cx, *sp, va_arg(ap, int32 *))) - return JS_FALSE; - break; - case 'u': - if (!js_ValueToECMAUint32(cx, *sp, va_arg(ap, uint32 *))) - return JS_FALSE; - break; - case 'j': - if (!js_ValueToInt32(cx, *sp, va_arg(ap, int32 *))) - return JS_FALSE; - break; - case 'd': - if (!js_ValueToNumber(cx, *sp, va_arg(ap, jsdouble *))) - return JS_FALSE; - break; - case 'I': - if (!js_ValueToNumber(cx, *sp, &d)) - return JS_FALSE; - *va_arg(ap, jsdouble *) = js_DoubleToInteger(d); - break; - case 's': - case 'S': - case 'W': - str = js_ValueToString(cx, *sp); - if (!str) - return JS_FALSE; - *sp = STRING_TO_JSVAL(str); - if (c == 's') - *va_arg(ap, char **) = JS_GetStringBytes(str); - else if (c == 'W') - *va_arg(ap, jschar **) = JS_GetStringChars(str); - else - *va_arg(ap, JSString **) = str; - break; - case 'o': - if (!js_ValueToObject(cx, *sp, &obj)) - return JS_FALSE; - *sp = OBJECT_TO_JSVAL(obj); - *va_arg(ap, JSObject **) = obj; - break; - case 'f': - obj = js_ValueToFunctionObject(cx, sp, 0); - if (!obj) - return JS_FALSE; - *va_arg(ap, JSFunction **) = (JSFunction *) JS_GetPrivate(cx, obj); - break; - case 'v': - *va_arg(ap, jsval *) = *sp; - break; - case '*': - break; - default: - format--; - if (!TryArgumentFormatter(cx, &format, JS_TRUE, &sp, - JS_ADDRESSOF_VA_LIST(ap))) { - return JS_FALSE; - } - /* NB: the formatter already updated sp, so we continue here. */ - continue; - } - sp++; - } - return JS_TRUE; -} - -JS_PUBLIC_API(jsval *) -JS_PushArguments(JSContext *cx, void **markp, const char *format, ...) -{ - va_list ap; - jsval *argv; - - va_start(ap, format); - argv = JS_PushArgumentsVA(cx, markp, format, ap); - va_end(ap); - return argv; -} - -JS_PUBLIC_API(jsval *) -JS_PushArgumentsVA(JSContext *cx, void **markp, const char *format, va_list ap) -{ - uintN argc; - jsval *argv, *sp; - char c; - const char *cp; - JSString *str; - JSFunction *fun; - JSStackHeader *sh; - - CHECK_REQUEST(cx); - *markp = NULL; - argc = 0; - for (cp = format; (c = *cp) != '\0'; cp++) { - /* - * Count non-space non-star characters as individual jsval arguments. - * This may over-allocate stack, but we'll fix below. - */ - if (isspace(c) || c == '*') - continue; - argc++; - } - sp = js_AllocStack(cx, argc, markp); - if (!sp) - return NULL; - argv = sp; - while ((c = *format++) != '\0') { - if (isspace(c) || c == '*') - continue; - switch (c) { - case 'b': - *sp = BOOLEAN_TO_JSVAL((JSBool) va_arg(ap, int)); - break; - case 'c': - *sp = INT_TO_JSVAL((uint16) va_arg(ap, unsigned int)); - break; - case 'i': - case 'j': - if (!js_NewNumberValue(cx, (jsdouble) va_arg(ap, int32), sp)) - goto bad; - break; - case 'u': - if (!js_NewNumberValue(cx, (jsdouble) va_arg(ap, uint32), sp)) - goto bad; - break; - case 'd': - case 'I': - if (!js_NewDoubleValue(cx, va_arg(ap, jsdouble), sp)) - goto bad; - break; - case 's': - str = JS_NewStringCopyZ(cx, va_arg(ap, char *)); - if (!str) - goto bad; - *sp = STRING_TO_JSVAL(str); - break; - case 'W': - str = JS_NewUCStringCopyZ(cx, va_arg(ap, jschar *)); - if (!str) - goto bad; - *sp = STRING_TO_JSVAL(str); - break; - case 'S': - str = va_arg(ap, JSString *); - *sp = STRING_TO_JSVAL(str); - break; - case 'o': - *sp = OBJECT_TO_JSVAL(va_arg(ap, JSObject *)); - break; - case 'f': - fun = va_arg(ap, JSFunction *); - *sp = fun ? OBJECT_TO_JSVAL(fun->object) : JSVAL_NULL; - break; - case 'v': - *sp = va_arg(ap, jsval); - break; - default: - format--; - if (!TryArgumentFormatter(cx, &format, JS_FALSE, &sp, - JS_ADDRESSOF_VA_LIST(ap))) { - goto bad; - } - /* NB: the formatter already updated sp, so we continue here. */ - continue; - } - sp++; - } - - /* - * We may have overallocated stack due to a multi-character format code - * handled by a JSArgumentFormatter. Give back that stack space! - */ - JS_ASSERT(sp <= argv + argc); - if (sp < argv + argc) { - /* Return slots not pushed to the current stack arena. */ - cx->stackPool.current->avail = (jsuword)sp; - - /* Reduce the count of slots the GC will scan in this stack segment. */ - sh = cx->stackHeaders; - JS_ASSERT(JS_STACK_SEGMENT(sh) + sh->nslots == argv + argc); - sh->nslots -= argc - (sp - argv); - } - return argv; - -bad: - js_FreeStack(cx, *markp); - return NULL; -} - -JS_PUBLIC_API(void) -JS_PopArguments(JSContext *cx, void *mark) -{ - CHECK_REQUEST(cx); - js_FreeStack(cx, mark); -} - -JS_PUBLIC_API(JSBool) -JS_AddArgumentFormatter(JSContext *cx, const char *format, - JSArgumentFormatter formatter) -{ - size_t length; - JSArgumentFormatMap **mpp, *map; - - length = strlen(format); - mpp = &cx->argumentFormatMap; - while ((map = *mpp) != NULL) { - /* Insert before any shorter string to match before prefixes. */ - if (map->length < length) - break; - if (map->length == length && !strcmp(map->format, format)) - goto out; - mpp = &map->next; - } - map = (JSArgumentFormatMap *) JS_malloc(cx, sizeof *map); - if (!map) - return JS_FALSE; - map->format = format; - map->length = length; - map->next = *mpp; - *mpp = map; -out: - map->formatter = formatter; - return JS_TRUE; -} - -JS_PUBLIC_API(void) -JS_RemoveArgumentFormatter(JSContext *cx, const char *format) -{ - size_t length; - JSArgumentFormatMap **mpp, *map; - - length = strlen(format); - mpp = &cx->argumentFormatMap; - while ((map = *mpp) != NULL) { - if (map->length == length && !strcmp(map->format, format)) { - *mpp = map->next; - JS_free(cx, map); - return; - } - mpp = &map->next; - } -} - -JS_PUBLIC_API(JSBool) -JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp) -{ - JSBool ok, b; - JSObject *obj; - JSString *str; - jsdouble d, *dp; - - CHECK_REQUEST(cx); - switch (type) { - case JSTYPE_VOID: - *vp = JSVAL_VOID; - ok = JS_TRUE; - break; - case JSTYPE_OBJECT: - ok = js_ValueToObject(cx, v, &obj); - if (ok) - *vp = OBJECT_TO_JSVAL(obj); - break; - case JSTYPE_FUNCTION: - *vp = v; - obj = js_ValueToFunctionObject(cx, vp, JSV2F_SEARCH_STACK); - ok = (obj != NULL); - break; - case JSTYPE_STRING: - str = js_ValueToString(cx, v); - ok = (str != NULL); - if (ok) - *vp = STRING_TO_JSVAL(str); - break; - case JSTYPE_NUMBER: - ok = js_ValueToNumber(cx, v, &d); - if (ok) { - dp = js_NewDouble(cx, d, 0); - ok = (dp != NULL); - if (ok) - *vp = DOUBLE_TO_JSVAL(dp); - } - break; - case JSTYPE_BOOLEAN: - ok = js_ValueToBoolean(cx, v, &b); - if (ok) - *vp = BOOLEAN_TO_JSVAL(b); - break; - default: { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%d", (int)type); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_TYPE, - numBuf); - ok = JS_FALSE; - break; - } - } - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_ValueToObject(JSContext *cx, jsval v, JSObject **objp) -{ - CHECK_REQUEST(cx); - return js_ValueToObject(cx, v, objp); -} - -JS_PUBLIC_API(JSFunction *) -JS_ValueToFunction(JSContext *cx, jsval v) -{ - CHECK_REQUEST(cx); - return js_ValueToFunction(cx, &v, JSV2F_SEARCH_STACK); -} - -JS_PUBLIC_API(JSFunction *) -JS_ValueToConstructor(JSContext *cx, jsval v) -{ - CHECK_REQUEST(cx); - return js_ValueToFunction(cx, &v, JSV2F_SEARCH_STACK); -} - -JS_PUBLIC_API(JSString *) -JS_ValueToString(JSContext *cx, jsval v) -{ - CHECK_REQUEST(cx); - return js_ValueToString(cx, v); -} - -JS_PUBLIC_API(JSBool) -JS_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp) -{ - CHECK_REQUEST(cx); - return js_ValueToNumber(cx, v, dp); -} - -JS_PUBLIC_API(JSBool) -JS_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip) -{ - CHECK_REQUEST(cx); - return js_ValueToECMAInt32(cx, v, ip); -} - -JS_PUBLIC_API(JSBool) -JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip) -{ - CHECK_REQUEST(cx); - return js_ValueToECMAUint32(cx, v, ip); -} - -JS_PUBLIC_API(JSBool) -JS_ValueToInt32(JSContext *cx, jsval v, int32 *ip) -{ - CHECK_REQUEST(cx); - return js_ValueToInt32(cx, v, ip); -} - -JS_PUBLIC_API(JSBool) -JS_ValueToUint16(JSContext *cx, jsval v, uint16 *ip) -{ - CHECK_REQUEST(cx); - return js_ValueToUint16(cx, v, ip); -} - -JS_PUBLIC_API(JSBool) -JS_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp) -{ - CHECK_REQUEST(cx); - return js_ValueToBoolean(cx, v, bp); -} - -JS_PUBLIC_API(JSType) -JS_TypeOfValue(JSContext *cx, jsval v) -{ - JSType type; - JSObject *obj; - JSObjectOps *ops; - JSClass *clasp; - - CHECK_REQUEST(cx); - if (JSVAL_IS_OBJECT(v)) { - type = JSTYPE_OBJECT; /* XXXbe JSTYPE_NULL for JS2 */ - obj = JSVAL_TO_OBJECT(v); - if (obj) { - ops = obj->map->ops; -#if JS_HAS_XML_SUPPORT - if (ops == &js_XMLObjectOps.base) { - type = JSTYPE_XML; - } else -#endif - { - /* - * ECMA 262, 11.4.3 says that any native object that implements - * [[Call]] should be of type "function". Note that RegExp and - * Script are both of type "function" for compatibility with - * older SpiderMonkeys. - */ - clasp = OBJ_GET_CLASS(cx, obj); - if ((ops == &js_ObjectOps) - ? (clasp->call - ? (clasp == &js_RegExpClass || clasp == &js_ScriptClass) - : clasp == &js_FunctionClass) - : ops->call != NULL) { - type = JSTYPE_FUNCTION; - } else { -#ifdef NARCISSUS - if (!OBJ_GET_PROPERTY(cx, obj, - ATOM_TO_JSID(cx->runtime->atomState - .callAtom), - &v)) { - JS_ClearPendingException(cx); - } else if (VALUE_IS_FUNCTION(cx, v)) { - type = JSTYPE_FUNCTION; - } -#endif - } - } - } - } else if (JSVAL_IS_NUMBER(v)) { - type = JSTYPE_NUMBER; - } else if (JSVAL_IS_STRING(v)) { - type = JSTYPE_STRING; - } else if (JSVAL_IS_BOOLEAN(v)) { - type = JSTYPE_BOOLEAN; - } else { - type = JSTYPE_VOID; - } - return type; -} - -JS_PUBLIC_API(const char *) -JS_GetTypeName(JSContext *cx, JSType type) -{ - if ((uintN)type >= (uintN)JSTYPE_LIMIT) - return NULL; - return js_type_strs[type]; -} - -/************************************************************************/ - -JS_PUBLIC_API(JSRuntime *) -JS_NewRuntime(uint32 maxbytes) -{ - JSRuntime *rt; - -#ifdef DEBUG - static JSBool didFirstChecks; - - if (!didFirstChecks) { - /* - * This code asserts that the numbers associated with the error names - * in jsmsg.def are monotonically increasing. It uses values for the - * error names enumerated in jscntxt.c. It's not a compile-time check - * but it's better than nothing. - */ - int errorNumber = 0; -#define MSG_DEF(name, number, count, exception, format) \ - JS_ASSERT(name == errorNumber++); -#include "js.msg" -#undef MSG_DEF - -#define MSG_DEF(name, number, count, exception, format) \ - JS_BEGIN_MACRO \ - uintN numfmtspecs = 0; \ - const char *fmt; \ - for (fmt = format; *fmt != '\0'; fmt++) { \ - if (*fmt == '{' && isdigit(fmt[1])) \ - ++numfmtspecs; \ - } \ - JS_ASSERT(count == numfmtspecs); \ - JS_END_MACRO; -#include "js.msg" -#undef MSG_DEF - - didFirstChecks = JS_TRUE; - } -#endif /* DEBUG */ - - rt = (JSRuntime *) malloc(sizeof(JSRuntime)); - if (!rt) - return NULL; - - /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */ - memset(rt, 0, sizeof(JSRuntime)); - JS_INIT_CLIST(&rt->contextList); - JS_INIT_CLIST(&rt->trapList); - JS_INIT_CLIST(&rt->watchPointList); - - if (!js_InitGC(rt, maxbytes)) - goto bad; -#ifdef JS_THREADSAFE - if (PR_FAILURE == PR_NewThreadPrivateIndex(&rt->threadTPIndex, - js_ThreadDestructorCB)) { - goto bad; - } - rt->gcLock = JS_NEW_LOCK(); - if (!rt->gcLock) - goto bad; - rt->gcDone = JS_NEW_CONDVAR(rt->gcLock); - if (!rt->gcDone) - goto bad; - rt->requestDone = JS_NEW_CONDVAR(rt->gcLock); - if (!rt->requestDone) - goto bad; - /* this is asymmetric with JS_ShutDown: */ - if (!js_SetupLocks(8, 16)) - goto bad; - rt->rtLock = JS_NEW_LOCK(); - if (!rt->rtLock) - goto bad; - rt->stateChange = JS_NEW_CONDVAR(rt->gcLock); - if (!rt->stateChange) - goto bad; - rt->setSlotLock = JS_NEW_LOCK(); - if (!rt->setSlotLock) - goto bad; - rt->setSlotDone = JS_NEW_CONDVAR(rt->setSlotLock); - if (!rt->setSlotDone) - goto bad; - rt->scopeSharingDone = JS_NEW_CONDVAR(rt->gcLock); - if (!rt->scopeSharingDone) - goto bad; - rt->scopeSharingTodo = NO_SCOPE_SHARING_TODO; -#endif - rt->propertyCache.empty = JS_TRUE; - if (!js_InitPropertyTree(rt)) - goto bad; - return rt; - -bad: - JS_DestroyRuntime(rt); - return NULL; -} - -JS_PUBLIC_API(void) -JS_DestroyRuntime(JSRuntime *rt) -{ -#ifdef DEBUG - /* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */ - if (!JS_CLIST_IS_EMPTY(&rt->contextList)) { - JSContext *cx, *iter = NULL; - uintN cxcount = 0; - while ((cx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL) - cxcount++; - fprintf(stderr, -"JS API usage error: %u contexts left in runtime upon JS_DestroyRuntime.\n", - cxcount); - } -#endif - - js_FreeRuntimeScriptState(rt); - js_FinishAtomState(&rt->atomState); - js_FinishGC(rt); -#ifdef JS_THREADSAFE - if (rt->gcLock) - JS_DESTROY_LOCK(rt->gcLock); - if (rt->gcDone) - JS_DESTROY_CONDVAR(rt->gcDone); - if (rt->requestDone) - JS_DESTROY_CONDVAR(rt->requestDone); - if (rt->rtLock) - JS_DESTROY_LOCK(rt->rtLock); - if (rt->stateChange) - JS_DESTROY_CONDVAR(rt->stateChange); - if (rt->setSlotLock) - JS_DESTROY_LOCK(rt->setSlotLock); - if (rt->setSlotDone) - JS_DESTROY_CONDVAR(rt->setSlotDone); - if (rt->scopeSharingDone) - JS_DESTROY_CONDVAR(rt->scopeSharingDone); -#else - GSN_CACHE_CLEAR(&rt->gsnCache); -#endif - js_FinishPropertyTree(rt); - free(rt); -} - -JS_PUBLIC_API(void) -JS_ShutDown(void) -{ - js_FinishDtoa(); -#ifdef JS_THREADSAFE - js_CleanupLocks(); -#endif -} - -JS_PUBLIC_API(void *) -JS_GetRuntimePrivate(JSRuntime *rt) -{ - return rt->data; -} - -JS_PUBLIC_API(void) -JS_SetRuntimePrivate(JSRuntime *rt, void *data) -{ - rt->data = data; -} - -#ifdef JS_THREADSAFE - -JS_PUBLIC_API(void) -JS_BeginRequest(JSContext *cx) -{ - JSRuntime *rt; - - JS_ASSERT(cx->thread->id == js_CurrentThreadId()); - if (!cx->requestDepth) { - /* Wait until the GC is finished. */ - rt = cx->runtime; - JS_LOCK_GC(rt); - - /* NB: we use cx->thread here, not js_GetCurrentThread(). */ - if (rt->gcThread != cx->thread) { - while (rt->gcLevel > 0) - JS_AWAIT_GC_DONE(rt); - } - - /* Indicate that a request is running. */ - rt->requestCount++; - cx->requestDepth = 1; - JS_UNLOCK_GC(rt); - return; - } - cx->requestDepth++; -} - -JS_PUBLIC_API(void) -JS_EndRequest(JSContext *cx) -{ - JSRuntime *rt; - JSScope *scope, **todop; - uintN nshares; - - CHECK_REQUEST(cx); - JS_ASSERT(cx->requestDepth > 0); - if (cx->requestDepth == 1) { - /* Lock before clearing to interlock with ClaimScope, in jslock.c. */ - rt = cx->runtime; - JS_LOCK_GC(rt); - cx->requestDepth = 0; - - /* See whether cx has any single-threaded scopes to start sharing. */ - todop = &rt->scopeSharingTodo; - nshares = 0; - while ((scope = *todop) != NO_SCOPE_SHARING_TODO) { - if (scope->ownercx != cx) { - todop = &scope->u.link; - continue; - } - *todop = scope->u.link; - scope->u.link = NULL; /* null u.link for sanity ASAP */ - - /* - * If js_DropObjectMap returns null, we held the last ref to scope. - * The waiting thread(s) must have been killed, after which the GC - * collected the object that held this scope. Unlikely, because it - * requires that the GC ran (e.g., from a branch callback) during - * this request, but possible. - */ - if (js_DropObjectMap(cx, &scope->map, NULL)) { - js_InitLock(&scope->lock); - scope->u.count = 0; /* NULL may not pun as 0 */ - js_FinishSharingScope(rt, scope); /* set ownercx = NULL */ - nshares++; - } - } - if (nshares) - JS_NOTIFY_ALL_CONDVAR(rt->scopeSharingDone); - - /* Give the GC a chance to run if this was the last request running. */ - JS_ASSERT(rt->requestCount > 0); - rt->requestCount--; - if (rt->requestCount == 0) - JS_NOTIFY_REQUEST_DONE(rt); - - JS_UNLOCK_GC(rt); - return; - } - - cx->requestDepth--; -} - -/* Yield to pending GC operations, regardless of request depth */ -JS_PUBLIC_API(void) -JS_YieldRequest(JSContext *cx) -{ - JSRuntime *rt; - - JS_ASSERT(cx->thread); - CHECK_REQUEST(cx); - - rt = cx->runtime; - JS_LOCK_GC(rt); - JS_ASSERT(rt->requestCount > 0); - rt->requestCount--; - if (rt->requestCount == 0) - JS_NOTIFY_REQUEST_DONE(rt); - JS_UNLOCK_GC(rt); - /* XXXbe give the GC or another request calling it a chance to run here? - Assumes FIFO scheduling */ - JS_LOCK_GC(rt); - if (rt->gcThread != cx->thread) { - while (rt->gcLevel > 0) - JS_AWAIT_GC_DONE(rt); - } - rt->requestCount++; - JS_UNLOCK_GC(rt); -} - -JS_PUBLIC_API(jsrefcount) -JS_SuspendRequest(JSContext *cx) -{ - jsrefcount saveDepth = cx->requestDepth; - - while (cx->requestDepth) - JS_EndRequest(cx); - return saveDepth; -} - -JS_PUBLIC_API(void) -JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth) -{ - JS_ASSERT(!cx->requestDepth); - while (--saveDepth >= 0) - JS_BeginRequest(cx); -} - -#endif /* JS_THREADSAFE */ - -JS_PUBLIC_API(void) -JS_Lock(JSRuntime *rt) -{ - JS_LOCK_RUNTIME(rt); -} - -JS_PUBLIC_API(void) -JS_Unlock(JSRuntime *rt) -{ - JS_UNLOCK_RUNTIME(rt); -} - -JS_PUBLIC_API(JSContextCallback) -JS_SetContextCallback(JSRuntime *rt, JSContextCallback cxCallback) -{ - JSContextCallback old; - - old = rt->cxCallback; - rt->cxCallback = cxCallback; - return old; -} - -JS_PUBLIC_API(JSContext *) -JS_NewContext(JSRuntime *rt, size_t stackChunkSize) -{ - return js_NewContext(rt, stackChunkSize); -} - -JS_PUBLIC_API(void) -JS_DestroyContext(JSContext *cx) -{ - js_DestroyContext(cx, JSDCM_FORCE_GC); -} - -JS_PUBLIC_API(void) -JS_DestroyContextNoGC(JSContext *cx) -{ - js_DestroyContext(cx, JSDCM_NO_GC); -} - -JS_PUBLIC_API(void) -JS_DestroyContextMaybeGC(JSContext *cx) -{ - js_DestroyContext(cx, JSDCM_MAYBE_GC); -} - -JS_PUBLIC_API(void *) -JS_GetContextPrivate(JSContext *cx) -{ - return cx->data; -} - -JS_PUBLIC_API(void) -JS_SetContextPrivate(JSContext *cx, void *data) -{ - cx->data = data; -} - -JS_PUBLIC_API(JSRuntime *) -JS_GetRuntime(JSContext *cx) -{ - return cx->runtime; -} - -JS_PUBLIC_API(JSContext *) -JS_ContextIterator(JSRuntime *rt, JSContext **iterp) -{ - return js_ContextIterator(rt, JS_TRUE, iterp); -} - -JS_PUBLIC_API(JSVersion) -JS_GetVersion(JSContext *cx) -{ - return cx->version & JSVERSION_MASK; -} - -JS_PUBLIC_API(JSVersion) -JS_SetVersion(JSContext *cx, JSVersion version) -{ - JSVersion oldVersion; - - JS_ASSERT(version != JSVERSION_UNKNOWN); - JS_ASSERT((version & ~JSVERSION_MASK) == 0); - - oldVersion = cx->version & JSVERSION_MASK; - if (version == oldVersion) - return oldVersion; - - /* We no longer support 1.4 or below. */ - if (version != JSVERSION_DEFAULT && version <= JSVERSION_1_4) - return oldVersion; - - cx->version = (cx->version & ~JSVERSION_MASK) | version; - js_OnVersionChange(cx); - return oldVersion; -} - -static struct v2smap { - JSVersion version; - const char *string; -} v2smap[] = { - {JSVERSION_1_0, "1.0"}, - {JSVERSION_1_1, "1.1"}, - {JSVERSION_1_2, "1.2"}, - {JSVERSION_1_3, "1.3"}, - {JSVERSION_1_4, "1.4"}, - {JSVERSION_ECMA_3, "ECMAv3"}, - {JSVERSION_1_5, "1.5"}, - {JSVERSION_1_6, "1.6"}, - {JSVERSION_1_7, "1.7"}, - {JSVERSION_DEFAULT, js_default_str}, - {JSVERSION_UNKNOWN, NULL}, /* must be last, NULL is sentinel */ -}; - -JS_PUBLIC_API(const char *) -JS_VersionToString(JSVersion version) -{ - int i; - - for (i = 0; v2smap[i].string; i++) - if (v2smap[i].version == version) - return v2smap[i].string; - return "unknown"; -} - -JS_PUBLIC_API(JSVersion) -JS_StringToVersion(const char *string) -{ - int i; - - for (i = 0; v2smap[i].string; i++) - if (strcmp(v2smap[i].string, string) == 0) - return v2smap[i].version; - return JSVERSION_UNKNOWN; -} - -JS_PUBLIC_API(uint32) -JS_GetOptions(JSContext *cx) -{ - return cx->options; -} - -#define SYNC_OPTIONS_TO_VERSION(cx) \ - JS_BEGIN_MACRO \ - if ((cx)->options & JSOPTION_XML) \ - (cx)->version |= JSVERSION_HAS_XML; \ - else \ - (cx)->version &= ~JSVERSION_HAS_XML; \ - JS_END_MACRO - -JS_PUBLIC_API(uint32) -JS_SetOptions(JSContext *cx, uint32 options) -{ - uint32 oldopts = cx->options; - cx->options = options; - SYNC_OPTIONS_TO_VERSION(cx); - return oldopts; -} - -JS_PUBLIC_API(uint32) -JS_ToggleOptions(JSContext *cx, uint32 options) -{ - uint32 oldopts = cx->options; - cx->options ^= options; - SYNC_OPTIONS_TO_VERSION(cx); - return oldopts; -} - -JS_PUBLIC_API(const char *) -JS_GetImplementationVersion(void) -{ - return "JavaScript-C 1.7.0 2007-10-03"; -} - - -JS_PUBLIC_API(JSObject *) -JS_GetGlobalObject(JSContext *cx) -{ - return cx->globalObject; -} - -JS_PUBLIC_API(void) -JS_SetGlobalObject(JSContext *cx, JSObject *obj) -{ - cx->globalObject = obj; - -#if JS_HAS_XML_SUPPORT - cx->xmlSettingFlags = 0; -#endif -} - -JSObject * -js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj) -{ - JSDHashTable *table; - JSBool resolving; - JSRuntime *rt; - JSResolvingKey key; - JSResolvingEntry *entry; - JSObject *fun_proto, *obj_proto; - - /* If cx has no global object, use obj so prototypes can be found. */ - if (!cx->globalObject) - JS_SetGlobalObject(cx, obj); - - /* Record Function and Object in cx->resolvingTable, if we are resolving. */ - table = cx->resolvingTable; - resolving = (table && table->entryCount); - rt = cx->runtime; - key.obj = obj; - if (resolving) { - key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Function]); - entry = (JSResolvingEntry *) - JS_DHashTableOperate(table, &key, JS_DHASH_ADD); - if (entry && entry->key.obj && (entry->flags & JSRESFLAG_LOOKUP)) { - /* Already resolving Function, record Object too. */ - JS_ASSERT(entry->key.obj == obj); - key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); - entry = (JSResolvingEntry *) - JS_DHashTableOperate(table, &key, JS_DHASH_ADD); - } - if (!entry) { - JS_ReportOutOfMemory(cx); - return NULL; - } - JS_ASSERT(!entry->key.obj && entry->flags == 0); - entry->key = key; - entry->flags = JSRESFLAG_LOOKUP; - } else { - key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); - if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) - return NULL; - - key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Function]); - if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) { - key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); - JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE); - return NULL; - } - - table = cx->resolvingTable; - } - - /* Initialize the function class first so constructors can be made. */ - fun_proto = js_InitFunctionClass(cx, obj); - if (!fun_proto) - goto out; - - /* Initialize the object class next so Object.prototype works. */ - obj_proto = js_InitObjectClass(cx, obj); - if (!obj_proto) { - fun_proto = NULL; - goto out; - } - - /* Function.prototype and the global object delegate to Object.prototype. */ - OBJ_SET_PROTO(cx, fun_proto, obj_proto); - if (!OBJ_GET_PROTO(cx, obj)) - OBJ_SET_PROTO(cx, obj, obj_proto); - -out: - /* If resolving, remove the other entry (Object or Function) from table. */ - JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE); - if (!resolving) { - /* If not resolving, remove the first entry added above, for Object. */ - JS_ASSERT(key.id == \ - ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Function])); - key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); - JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE); - } - return fun_proto; -} - -JS_PUBLIC_API(JSBool) -JS_InitStandardClasses(JSContext *cx, JSObject *obj) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - - /* Define a top-level property 'undefined' with the undefined value. */ - atom = cx->runtime->atomState.typeAtoms[JSTYPE_VOID]; - if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, - NULL, NULL, JSPROP_PERMANENT, NULL)) { - return JS_FALSE; - } - - /* Function and Object require cooperative bootstrapping magic. */ - if (!js_InitFunctionAndObjectClasses(cx, obj)) - return JS_FALSE; - - /* Initialize the rest of the standard objects and functions. */ - return js_InitArrayClass(cx, obj) && - js_InitBlockClass(cx, obj) && - js_InitBooleanClass(cx, obj) && - js_InitCallClass(cx, obj) && - js_InitExceptionClasses(cx, obj) && - js_InitMathClass(cx, obj) && - js_InitNumberClass(cx, obj) && - js_InitRegExpClass(cx, obj) && - js_InitStringClass(cx, obj) && -#if JS_HAS_SCRIPT_OBJECT - js_InitScriptClass(cx, obj) && -#endif -#if JS_HAS_XML_SUPPORT - js_InitXMLClasses(cx, obj) && -#endif -#if JS_HAS_FILE_OBJECT - js_InitFileClass(cx, obj) && -#endif -#if JS_HAS_GENERATORS - js_InitIteratorClasses(cx, obj) && -#endif - js_InitDateClass(cx, obj); -} - -#define ATOM_OFFSET(name) offsetof(JSAtomState,name##Atom) -#define CLASS_ATOM_OFFSET(name) offsetof(JSAtomState,classAtoms[JSProto_##name]) -#define OFFSET_TO_ATOM(rt,off) (*(JSAtom **)((char*)&(rt)->atomState + (off))) -#define CLASP(name) (JSClass *)&js_##name##Class - -#define EAGER_ATOM(name) ATOM_OFFSET(name), NULL -#define EAGER_CLASS_ATOM(name) CLASS_ATOM_OFFSET(name), NULL -#define EAGER_ATOM_AND_CLASP(name) EAGER_CLASS_ATOM(name), CLASP(name) -#define LAZY_ATOM(name) ATOM_OFFSET(lazy.name), js_##name##_str - -typedef struct JSStdName { - JSObjectOp init; - size_t atomOffset; /* offset of atom pointer in JSAtomState */ - const char *name; /* null if atom is pre-pinned, else name */ - JSClass *clasp; -} JSStdName; - -static JSAtom * -StdNameToAtom(JSContext *cx, JSStdName *stdn) -{ - size_t offset; - JSAtom *atom; - const char *name; - - offset = stdn->atomOffset; - atom = OFFSET_TO_ATOM(cx->runtime, offset); - if (!atom) { - name = stdn->name; - if (name) { - atom = js_Atomize(cx, name, strlen(name), ATOM_PINNED); - OFFSET_TO_ATOM(cx->runtime, offset) = atom; - } - } - return atom; -} - -/* - * Table of class initializers and their atom offsets in rt->atomState. - * If you add a "standard" class, remember to update this table. - */ -static JSStdName standard_class_atoms[] = { - {js_InitFunctionAndObjectClasses, EAGER_ATOM_AND_CLASP(Function)}, - {js_InitFunctionAndObjectClasses, EAGER_ATOM_AND_CLASP(Object)}, - {js_InitArrayClass, EAGER_ATOM_AND_CLASP(Array)}, - {js_InitBlockClass, EAGER_ATOM_AND_CLASP(Block)}, - {js_InitBooleanClass, EAGER_ATOM_AND_CLASP(Boolean)}, - {js_InitDateClass, EAGER_ATOM_AND_CLASP(Date)}, - {js_InitMathClass, EAGER_ATOM_AND_CLASP(Math)}, - {js_InitNumberClass, EAGER_ATOM_AND_CLASP(Number)}, - {js_InitStringClass, EAGER_ATOM_AND_CLASP(String)}, - {js_InitCallClass, EAGER_ATOM_AND_CLASP(Call)}, - {js_InitExceptionClasses, EAGER_ATOM_AND_CLASP(Error)}, - {js_InitRegExpClass, EAGER_ATOM_AND_CLASP(RegExp)}, -#if JS_HAS_SCRIPT_OBJECT - {js_InitScriptClass, EAGER_ATOM_AND_CLASP(Script)}, -#endif -#if JS_HAS_XML_SUPPORT - {js_InitXMLClass, EAGER_ATOM_AND_CLASP(XML)}, - {js_InitNamespaceClass, EAGER_ATOM_AND_CLASP(Namespace)}, - {js_InitQNameClass, EAGER_ATOM_AND_CLASP(QName)}, -#endif -#if JS_HAS_FILE_OBJECT - {js_InitFileClass, EAGER_ATOM_AND_CLASP(File)}, -#endif -#if JS_HAS_GENERATORS - {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(StopIteration)}, -#endif - {NULL, 0, NULL, NULL} -}; - -/* - * Table of top-level function and constant names and their init functions. - * If you add a "standard" global function or property, remember to update - * this table. - */ -static JSStdName standard_class_names[] = { - /* ECMA requires that eval be a direct property of the global object. */ - {js_InitObjectClass, EAGER_ATOM(eval), NULL}, - - /* Global properties and functions defined by the Number class. */ - {js_InitNumberClass, LAZY_ATOM(NaN), NULL}, - {js_InitNumberClass, LAZY_ATOM(Infinity), NULL}, - {js_InitNumberClass, LAZY_ATOM(isNaN), NULL}, - {js_InitNumberClass, LAZY_ATOM(isFinite), NULL}, - {js_InitNumberClass, LAZY_ATOM(parseFloat), NULL}, - {js_InitNumberClass, LAZY_ATOM(parseInt), NULL}, - - /* String global functions. */ - {js_InitStringClass, LAZY_ATOM(escape), NULL}, - {js_InitStringClass, LAZY_ATOM(unescape), NULL}, - {js_InitStringClass, LAZY_ATOM(decodeURI), NULL}, - {js_InitStringClass, LAZY_ATOM(encodeURI), NULL}, - {js_InitStringClass, LAZY_ATOM(decodeURIComponent), NULL}, - {js_InitStringClass, LAZY_ATOM(encodeURIComponent), NULL}, -#if JS_HAS_UNEVAL - {js_InitStringClass, LAZY_ATOM(uneval), NULL}, -#endif - - /* Exception constructors. */ - {js_InitExceptionClasses, EAGER_CLASS_ATOM(Error), CLASP(Error)}, - {js_InitExceptionClasses, EAGER_CLASS_ATOM(InternalError), CLASP(Error)}, - {js_InitExceptionClasses, EAGER_CLASS_ATOM(EvalError), CLASP(Error)}, - {js_InitExceptionClasses, EAGER_CLASS_ATOM(RangeError), CLASP(Error)}, - {js_InitExceptionClasses, EAGER_CLASS_ATOM(ReferenceError), CLASP(Error)}, - {js_InitExceptionClasses, EAGER_CLASS_ATOM(SyntaxError), CLASP(Error)}, - {js_InitExceptionClasses, EAGER_CLASS_ATOM(TypeError), CLASP(Error)}, - {js_InitExceptionClasses, EAGER_CLASS_ATOM(URIError), CLASP(Error)}, - -#if JS_HAS_XML_SUPPORT - {js_InitAnyNameClass, EAGER_ATOM_AND_CLASP(AnyName)}, - {js_InitAttributeNameClass, EAGER_ATOM_AND_CLASP(AttributeName)}, - {js_InitXMLClass, LAZY_ATOM(XMLList), &js_XMLClass}, - {js_InitXMLClass, LAZY_ATOM(isXMLName), NULL}, -#endif - -#if JS_HAS_GENERATORS - {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(Iterator)}, - {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(Generator)}, -#endif - - {NULL, 0, NULL, NULL} -}; - -static JSStdName object_prototype_names[] = { - /* Object.prototype properties (global delegates to Object.prototype). */ - {js_InitObjectClass, EAGER_ATOM(proto), NULL}, - {js_InitObjectClass, EAGER_ATOM(parent), NULL}, - {js_InitObjectClass, EAGER_ATOM(count), NULL}, -#if JS_HAS_TOSOURCE - {js_InitObjectClass, EAGER_ATOM(toSource), NULL}, -#endif - {js_InitObjectClass, EAGER_ATOM(toString), NULL}, - {js_InitObjectClass, EAGER_ATOM(toLocaleString), NULL}, - {js_InitObjectClass, EAGER_ATOM(valueOf), NULL}, -#if JS_HAS_OBJ_WATCHPOINT - {js_InitObjectClass, LAZY_ATOM(watch), NULL}, - {js_InitObjectClass, LAZY_ATOM(unwatch), NULL}, -#endif - {js_InitObjectClass, LAZY_ATOM(hasOwnProperty), NULL}, - {js_InitObjectClass, LAZY_ATOM(isPrototypeOf), NULL}, - {js_InitObjectClass, LAZY_ATOM(propertyIsEnumerable), NULL}, -#if JS_HAS_GETTER_SETTER - {js_InitObjectClass, LAZY_ATOM(defineGetter), NULL}, - {js_InitObjectClass, LAZY_ATOM(defineSetter), NULL}, - {js_InitObjectClass, LAZY_ATOM(lookupGetter), NULL}, - {js_InitObjectClass, LAZY_ATOM(lookupSetter), NULL}, -#endif - - {NULL, 0, NULL, NULL} -}; - -JS_PUBLIC_API(JSBool) -JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id, - JSBool *resolved) -{ - JSString *idstr; - JSRuntime *rt; - JSAtom *atom; - JSStdName *stdnm; - uintN i; - - CHECK_REQUEST(cx); - *resolved = JS_FALSE; - - if (!JSVAL_IS_STRING(id)) - return JS_TRUE; - idstr = JSVAL_TO_STRING(id); - rt = cx->runtime; - - /* Check whether we're resolving 'undefined', and define it if so. */ - atom = rt->atomState.typeAtoms[JSTYPE_VOID]; - if (idstr == ATOM_TO_STRING(atom)) { - *resolved = JS_TRUE; - return OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, - NULL, NULL, JSPROP_PERMANENT, NULL); - } - - /* Try for class constructors/prototypes named by well-known atoms. */ - stdnm = NULL; - for (i = 0; standard_class_atoms[i].init; i++) { - atom = OFFSET_TO_ATOM(rt, standard_class_atoms[i].atomOffset); - if (idstr == ATOM_TO_STRING(atom)) { - stdnm = &standard_class_atoms[i]; - break; - } - } - - if (!stdnm) { - /* Try less frequently used top-level functions and constants. */ - for (i = 0; standard_class_names[i].init; i++) { - atom = StdNameToAtom(cx, &standard_class_names[i]); - if (!atom) - return JS_FALSE; - if (idstr == ATOM_TO_STRING(atom)) { - stdnm = &standard_class_names[i]; - break; - } - } - - if (!stdnm && !OBJ_GET_PROTO(cx, obj)) { - /* - * Try even less frequently used names delegated from the global - * object to Object.prototype, but only if the Object class hasn't - * yet been initialized. - */ - for (i = 0; object_prototype_names[i].init; i++) { - atom = StdNameToAtom(cx, &object_prototype_names[i]); - if (!atom) - return JS_FALSE; - if (idstr == ATOM_TO_STRING(atom)) { - stdnm = &standard_class_names[i]; - break; - } - } - } - } - - if (stdnm) { - /* - * If this standard class is anonymous and obj advertises itself as a - * global object (in order to reserve slots for standard class object - * pointers), then we don't want to resolve by name. - * - * If inversely, either id does not name a class, or id does not name - * an anonymous class, or the global does not reserve slots for class - * objects, then we must call the init hook here. - */ - if (stdnm->clasp && - (stdnm->clasp->flags & JSCLASS_IS_ANONYMOUS) && - (OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) { - return JS_TRUE; - } - - if (!stdnm->init(cx, obj)) - return JS_FALSE; - *resolved = JS_TRUE; - } - return JS_TRUE; -} - -static JSBool -AlreadyHasOwnProperty(JSContext *cx, JSObject *obj, JSAtom *atom) -{ - JSScopeProperty *sprop; - JSScope *scope; - - JS_ASSERT(OBJ_IS_NATIVE(obj)); - JS_LOCK_OBJ(cx, obj); - scope = OBJ_SCOPE(obj); - sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom)); - JS_UNLOCK_SCOPE(cx, scope); - return sprop != NULL; -} - -JS_PUBLIC_API(JSBool) -JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj) -{ - JSRuntime *rt; - JSAtom *atom; - uintN i; - - CHECK_REQUEST(cx); - rt = cx->runtime; - - /* Check whether we need to bind 'undefined' and define it if so. */ - atom = rt->atomState.typeAtoms[JSTYPE_VOID]; - if (!AlreadyHasOwnProperty(cx, obj, atom) && - !OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, - NULL, NULL, JSPROP_PERMANENT, NULL)) { - return JS_FALSE; - } - - /* Initialize any classes that have not been resolved yet. */ - for (i = 0; standard_class_atoms[i].init; i++) { - atom = OFFSET_TO_ATOM(rt, standard_class_atoms[i].atomOffset); - if (!AlreadyHasOwnProperty(cx, obj, atom) && - !standard_class_atoms[i].init(cx, obj)) { - return JS_FALSE; - } - } - - return JS_TRUE; -} - -static JSIdArray * -AddAtomToArray(JSContext *cx, JSAtom *atom, JSIdArray *ida, jsint *ip) -{ - jsint i, length; - - i = *ip; - length = ida->length; - if (i >= length) { - ida = js_SetIdArrayLength(cx, ida, JS_MAX(length * 2, 8)); - if (!ida) - return NULL; - JS_ASSERT(i < ida->length); - } - ida->vector[i] = ATOM_TO_JSID(atom); - *ip = i + 1; - return ida; -} - -static JSIdArray * -EnumerateIfResolved(JSContext *cx, JSObject *obj, JSAtom *atom, JSIdArray *ida, - jsint *ip, JSBool *foundp) -{ - *foundp = AlreadyHasOwnProperty(cx, obj, atom); - if (*foundp) - ida = AddAtomToArray(cx, atom, ida, ip); - return ida; -} - -JS_PUBLIC_API(JSIdArray *) -JS_EnumerateResolvedStandardClasses(JSContext *cx, JSObject *obj, - JSIdArray *ida) -{ - JSRuntime *rt; - jsint i, j, k; - JSAtom *atom; - JSBool found; - JSObjectOp init; - - CHECK_REQUEST(cx); - rt = cx->runtime; - if (ida) { - i = ida->length; - } else { - ida = js_NewIdArray(cx, 8); - if (!ida) - return NULL; - i = 0; - } - - /* Check whether 'undefined' has been resolved and enumerate it if so. */ - atom = rt->atomState.typeAtoms[JSTYPE_VOID]; - ida = EnumerateIfResolved(cx, obj, atom, ida, &i, &found); - if (!ida) - return NULL; - - /* Enumerate only classes that *have* been resolved. */ - for (j = 0; standard_class_atoms[j].init; j++) { - atom = OFFSET_TO_ATOM(rt, standard_class_atoms[j].atomOffset); - ida = EnumerateIfResolved(cx, obj, atom, ida, &i, &found); - if (!ida) - return NULL; - - if (found) { - init = standard_class_atoms[j].init; - - for (k = 0; standard_class_names[k].init; k++) { - if (standard_class_names[k].init == init) { - atom = StdNameToAtom(cx, &standard_class_names[k]); - ida = AddAtomToArray(cx, atom, ida, &i); - if (!ida) - return NULL; - } - } - - if (init == js_InitObjectClass) { - for (k = 0; object_prototype_names[k].init; k++) { - atom = StdNameToAtom(cx, &object_prototype_names[k]); - ida = AddAtomToArray(cx, atom, ida, &i); - if (!ida) - return NULL; - } - } - } - } - - /* Trim to exact length via js_SetIdArrayLength. */ - return js_SetIdArrayLength(cx, ida, i); -} - -#undef ATOM_OFFSET -#undef CLASS_ATOM_OFFSET -#undef OFFSET_TO_ATOM -#undef CLASP - -#undef EAGER_ATOM -#undef EAGER_CLASS_ATOM -#undef EAGER_ATOM_CLASP -#undef LAZY_ATOM - -JS_PUBLIC_API(JSBool) -JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, - JSObject **objp) -{ - CHECK_REQUEST(cx); - return js_GetClassObject(cx, obj, key, objp); -} - -JS_PUBLIC_API(JSObject *) -JS_GetScopeChain(JSContext *cx) -{ - JSStackFrame *fp; - - fp = cx->fp; - if (!fp) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE); - return NULL; - } - return js_GetScopeChain(cx, fp); -} - -JS_PUBLIC_API(void *) -JS_malloc(JSContext *cx, size_t nbytes) -{ - void *p; - - JS_ASSERT(nbytes != 0); - if (nbytes == 0) - nbytes = 1; - - p = malloc(nbytes); - if (!p) { - JS_ReportOutOfMemory(cx); - return NULL; - } - js_UpdateMallocCounter(cx, nbytes); - - return p; -} - -JS_PUBLIC_API(void *) -JS_realloc(JSContext *cx, void *p, size_t nbytes) -{ - p = realloc(p, nbytes); - if (!p) - JS_ReportOutOfMemory(cx); - return p; -} - -JS_PUBLIC_API(void) -JS_free(JSContext *cx, void *p) -{ - if (p) - free(p); -} - -JS_PUBLIC_API(char *) -JS_strdup(JSContext *cx, const char *s) -{ - size_t n; - void *p; - - n = strlen(s) + 1; - p = JS_malloc(cx, n); - if (!p) - return NULL; - return (char *)memcpy(p, s, n); -} - -JS_PUBLIC_API(jsdouble *) -JS_NewDouble(JSContext *cx, jsdouble d) -{ - CHECK_REQUEST(cx); - return js_NewDouble(cx, d, 0); -} - -JS_PUBLIC_API(JSBool) -JS_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval) -{ - CHECK_REQUEST(cx); - return js_NewDoubleValue(cx, d, rval); -} - -JS_PUBLIC_API(JSBool) -JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval) -{ - CHECK_REQUEST(cx); - return js_NewNumberValue(cx, d, rval); -} - -#undef JS_AddRoot -JS_PUBLIC_API(JSBool) -JS_AddRoot(JSContext *cx, void *rp) -{ - CHECK_REQUEST(cx); - return js_AddRoot(cx, rp, NULL); -} - -JS_PUBLIC_API(JSBool) -JS_AddNamedRootRT(JSRuntime *rt, void *rp, const char *name) -{ - return js_AddRootRT(rt, rp, name); -} - -JS_PUBLIC_API(JSBool) -JS_RemoveRoot(JSContext *cx, void *rp) -{ - CHECK_REQUEST(cx); - return js_RemoveRoot(cx->runtime, rp); -} - -JS_PUBLIC_API(JSBool) -JS_RemoveRootRT(JSRuntime *rt, void *rp) -{ - return js_RemoveRoot(rt, rp); -} - -JS_PUBLIC_API(JSBool) -JS_AddNamedRoot(JSContext *cx, void *rp, const char *name) -{ - CHECK_REQUEST(cx); - return js_AddRoot(cx, rp, name); -} - -JS_PUBLIC_API(void) -JS_ClearNewbornRoots(JSContext *cx) -{ - JS_CLEAR_WEAK_ROOTS(&cx->weakRoots); -} - -JS_PUBLIC_API(JSBool) -JS_EnterLocalRootScope(JSContext *cx) -{ - CHECK_REQUEST(cx); - return js_EnterLocalRootScope(cx); -} - -JS_PUBLIC_API(void) -JS_LeaveLocalRootScope(JSContext *cx) -{ - CHECK_REQUEST(cx); - js_LeaveLocalRootScope(cx); -} - -JS_PUBLIC_API(void) -JS_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval) -{ - CHECK_REQUEST(cx); - js_LeaveLocalRootScopeWithResult(cx, rval); -} - -JS_PUBLIC_API(void) -JS_ForgetLocalRoot(JSContext *cx, void *thing) -{ - CHECK_REQUEST(cx); - js_ForgetLocalRoot(cx, (jsval) thing); -} - -#ifdef DEBUG - -JS_PUBLIC_API(void) -JS_DumpNamedRoots(JSRuntime *rt, - void (*dump)(const char *name, void *rp, void *data), - void *data) -{ - js_DumpNamedRoots(rt, dump, data); -} - -#endif /* DEBUG */ - -JS_PUBLIC_API(uint32) -JS_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data) -{ - return js_MapGCRoots(rt, map, data); -} - -JS_PUBLIC_API(JSBool) -JS_LockGCThing(JSContext *cx, void *thing) -{ - JSBool ok; - - CHECK_REQUEST(cx); - ok = js_LockGCThing(cx, thing); - if (!ok) - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_LOCK); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_LockGCThingRT(JSRuntime *rt, void *thing) -{ - return js_LockGCThingRT(rt, thing); -} - -JS_PUBLIC_API(JSBool) -JS_UnlockGCThing(JSContext *cx, void *thing) -{ - JSBool ok; - - CHECK_REQUEST(cx); - ok = js_UnlockGCThingRT(cx->runtime, thing); - if (!ok) - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_UNLOCK); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_UnlockGCThingRT(JSRuntime *rt, void *thing) -{ - return js_UnlockGCThingRT(rt, thing); -} - -JS_PUBLIC_API(void) -JS_MarkGCThing(JSContext *cx, void *thing, const char *name, void *arg) -{ - JS_ASSERT(cx->runtime->gcLevel > 0); -#ifdef JS_THREADSAFE - JS_ASSERT(cx->runtime->gcThread->id == js_CurrentThreadId()); -#endif - - GC_MARK(cx, thing, name); -} - -JS_PUBLIC_API(void) -JS_GC(JSContext *cx) -{ -#if JS_HAS_GENERATORS - /* Run previously scheduled but delayed close hooks. */ - js_RunCloseHooks(cx); -#endif - - /* Don't nuke active arenas if executing or compiling. */ - if (cx->stackPool.current == &cx->stackPool.first) - JS_FinishArenaPool(&cx->stackPool); - if (cx->tempPool.current == &cx->tempPool.first) - JS_FinishArenaPool(&cx->tempPool); - js_GC(cx, GC_NORMAL); - -#if JS_HAS_GENERATORS - /* - * Run close hooks for objects that became unreachable after the last GC. - */ - js_RunCloseHooks(cx); -#endif -} - -JS_PUBLIC_API(void) -JS_MaybeGC(JSContext *cx) -{ -#ifdef WAY_TOO_MUCH_GC - JS_GC(cx); -#else - JSRuntime *rt; - uint32 bytes, lastBytes; - - rt = cx->runtime; - bytes = rt->gcBytes; - lastBytes = rt->gcLastBytes; - - /* - * We run the GC if we used all available free GC cells and had to - * allocate extra 1/5 of GC arenas since the last run of GC, or if - * we have malloc'd more bytes through JS_malloc than we were told - * to allocate by JS_NewRuntime. - * - * The reason for - * bytes > 6/5 lastBytes - * condition is the following. Bug 312238 changed bytes and lastBytes - * to mean the total amount of memory that the GC uses now and right - * after the last GC. - * - * Before the bug the variables meant the size of allocated GC things - * now and right after the last GC. That size did not include the - * memory taken by free GC cells and the condition was - * bytes > 3/2 lastBytes. - * That is, we run the GC if we have half again as many bytes of - * GC-things as the last time we GC'd. To be compatible we need to - * express that condition through the new meaning of bytes and - * lastBytes. - * - * We write the original condition as - * B*(1-F) > 3/2 Bl*(1-Fl) - * where B is the total memory size allocated by GC and F is the free - * cell density currently and Sl and Fl are the size and the density - * right after GC. The density by definition is memory taken by free - * cells divided by total amount of memory. In other words, B and Bl - * are bytes and lastBytes with the new meaning and B*(1-F) and - * Bl*(1-Fl) are bytes and lastBytes with the original meaning. - * - * Our task is to exclude F and Fl from the last statement. According - * the stats from bug 331770 Fl is about 20-30% for GC allocations - * that contribute to S and Sl for a typical run of the browser. It - * means that the original condition implied that we did not run GC - * unless we exhausted the pool of free cells. Indeed if we still - * have free cells, then B == Bl since we did not yet allocated any - * new arenas and the condition means - * 1 - F > 3/2 (1-Fl) or 3/2Fl > 1/2 + F - * That implies 3/2 Fl > 1/2 or Fl > 1/3. That can not be fulfilled - * for the state described by the stats. So we can write the original - * condition as: - * F == 0 && B > 3/2 Bl(1-Fl) - * Again using the stats we see that Fl is about 20% when the browser - * starts up and when we are far from hitting rt->gcMaxBytes. With - * this F we have - * F == 0 && B > 3/2 Bl(1-0.8) or just B > 6/5 Bl. - */ - if ((bytes > 8192 && bytes > lastBytes + lastBytes / 5) || - rt->gcMallocBytes >= rt->gcMaxMallocBytes) { - JS_GC(cx); - } -#if JS_HAS_GENERATORS - else { - /* Run scheduled but not yet executed close hooks. */ - js_RunCloseHooks(cx); - } -#endif -#endif -} - -JS_PUBLIC_API(JSGCCallback) -JS_SetGCCallback(JSContext *cx, JSGCCallback cb) -{ - return JS_SetGCCallbackRT(cx->runtime, cb); -} - -JS_PUBLIC_API(JSGCCallback) -JS_SetGCCallbackRT(JSRuntime *rt, JSGCCallback cb) -{ - JSGCCallback oldcb; - - oldcb = rt->gcCallback; - rt->gcCallback = cb; - return oldcb; -} - -JS_PUBLIC_API(JSBool) -JS_IsAboutToBeFinalized(JSContext *cx, void *thing) -{ - JS_ASSERT(thing); - return js_IsAboutToBeFinalized(cx, thing); -} - -JS_PUBLIC_API(void) -JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value) -{ - switch (key) { - case JSGC_MAX_BYTES: - rt->gcMaxBytes = value; - break; - case JSGC_MAX_MALLOC_BYTES: - rt->gcMaxMallocBytes = value; - break; - } -} - -JS_PUBLIC_API(intN) -JS_AddExternalStringFinalizer(JSStringFinalizeOp finalizer) -{ - return js_ChangeExternalStringFinalizer(NULL, finalizer); -} - -JS_PUBLIC_API(intN) -JS_RemoveExternalStringFinalizer(JSStringFinalizeOp finalizer) -{ - return js_ChangeExternalStringFinalizer(finalizer, NULL); -} - -JS_PUBLIC_API(JSString *) -JS_NewExternalString(JSContext *cx, jschar *chars, size_t length, intN type) -{ - JSString *str; - - CHECK_REQUEST(cx); - JS_ASSERT(GCX_EXTERNAL_STRING <= type && type < (intN) GCX_NTYPES); - - str = (JSString *) js_NewGCThing(cx, (uintN) type, sizeof(JSString)); - if (!str) - return NULL; - str->length = length; - str->chars = chars; - return str; -} - -JS_PUBLIC_API(intN) -JS_GetExternalStringGCType(JSRuntime *rt, JSString *str) -{ - uint8 type = (uint8) (*js_GetGCThingFlags(str) & GCF_TYPEMASK); - - if (type >= GCX_EXTERNAL_STRING) - return (intN)type; - JS_ASSERT(type == GCX_STRING || type == GCX_MUTABLE_STRING); - return -1; -} - -JS_PUBLIC_API(void) -JS_SetThreadStackLimit(JSContext *cx, jsuword limitAddr) -{ -#if JS_STACK_GROWTH_DIRECTION > 0 - if (limitAddr == 0) - limitAddr = (jsuword)-1; -#endif - cx->stackLimit = limitAddr; -} - -/************************************************************************/ - -JS_PUBLIC_API(void) -JS_DestroyIdArray(JSContext *cx, JSIdArray *ida) -{ - JS_free(cx, ida); -} - -JS_PUBLIC_API(JSBool) -JS_ValueToId(JSContext *cx, jsval v, jsid *idp) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - if (JSVAL_IS_INT(v)) { - *idp = INT_JSVAL_TO_JSID(v); - } else { -#if JS_HAS_XML_SUPPORT - if (JSVAL_IS_OBJECT(v)) { - *idp = OBJECT_JSVAL_TO_JSID(v); - return JS_TRUE; - } -#endif - atom = js_ValueToStringAtom(cx, v); - if (!atom) - return JS_FALSE; - *idp = ATOM_TO_JSID(atom); - } - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_IdToValue(JSContext *cx, jsid id, jsval *vp) -{ - CHECK_REQUEST(cx); - *vp = ID_TO_VALUE(id); - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_PropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_EnumerateStub(JSContext *cx, JSObject *obj) -{ - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_ResolveStub(JSContext *cx, JSObject *obj, jsval id) -{ - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp) -{ - return js_TryValueOf(cx, obj, type, vp); -} - -JS_PUBLIC_API(void) -JS_FinalizeStub(JSContext *cx, JSObject *obj) -{ -} - -JS_PUBLIC_API(JSObject *) -JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, - JSClass *clasp, JSNative constructor, uintN nargs, - JSPropertySpec *ps, JSFunctionSpec *fs, - JSPropertySpec *static_ps, JSFunctionSpec *static_fs) -{ - JSAtom *atom; - JSProtoKey key; - JSObject *proto, *ctor; - JSTempValueRooter tvr; - jsval cval, rval; - JSBool named; - JSFunction *fun; - - CHECK_REQUEST(cx); - atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); - if (!atom) - return NULL; - - /* - * When initializing a standard class, if no parent_proto (grand-proto of - * instances of the class, parent-proto of the class's prototype object) - * is given, we must use Object.prototype if it is available. Otherwise, - * we could look up the wrong binding for a class name in obj. Example: - * - * String = Array; - * print("hi there".join); - * - * should print undefined, not Array.prototype.join. This is required by - * ECMA-262, alas. It might have been better to make String readonly and - * permanent in the global object, instead -- but that's too big a change - * to swallow at this point. - */ - key = JSCLASS_CACHED_PROTO_KEY(clasp); - if (key != JSProto_Null && - !parent_proto && - !js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Object), - &parent_proto)) { - return NULL; - } - - /* Create a prototype object for this class. */ - proto = js_NewObject(cx, clasp, parent_proto, obj); - if (!proto) - return NULL; - - /* After this point, control must exit via label bad or out. */ - JS_PUSH_TEMP_ROOT_OBJECT(cx, proto, &tvr); - - if (!constructor) { - /* - * Lacking a constructor, name the prototype (e.g., Math) unless this - * class (a) is anonymous, i.e. for internal use only; (b) the class - * of obj (the global object) is has a reserved slot indexed by key; - * and (c) key is not the null key. - */ - if ((clasp->flags & JSCLASS_IS_ANONYMOUS) && - (OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL) && - key != JSProto_Null) { - named = JS_FALSE; - } else { - named = OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), - OBJECT_TO_JSVAL(proto), - NULL, NULL, - (clasp->flags & JSCLASS_IS_ANONYMOUS) - ? JSPROP_READONLY | JSPROP_PERMANENT - : 0, - NULL); - if (!named) - goto bad; - } - - ctor = proto; - } else { - /* Define the constructor function in obj's scope. */ - fun = js_DefineFunction(cx, obj, atom, constructor, nargs, 0); - named = (fun != NULL); - if (!fun) - goto bad; - - /* - * Remember the class this function is a constructor for so that - * we know to create an object of this class when we call the - * constructor. - */ - fun->clasp = clasp; - - /* - * Optionally construct the prototype object, before the class has - * been fully initialized. Allow the ctor to replace proto with a - * different object, as is done for operator new -- and as at least - * XML support requires. - */ - ctor = fun->object; - if (clasp->flags & JSCLASS_CONSTRUCT_PROTOTYPE) { - cval = OBJECT_TO_JSVAL(ctor); - if (!js_InternalConstruct(cx, proto, cval, 0, NULL, &rval)) - goto bad; - if (!JSVAL_IS_PRIMITIVE(rval) && JSVAL_TO_OBJECT(rval) != proto) - proto = JSVAL_TO_OBJECT(rval); - } - - /* Connect constructor and prototype by named properties. */ - if (!js_SetClassPrototype(cx, ctor, proto, - JSPROP_READONLY | JSPROP_PERMANENT)) { - goto bad; - } - - /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */ - if (OBJ_GET_CLASS(cx, ctor) == clasp) { - JS_ASSERT(!OBJ_GET_PROTO(cx, ctor)); - OBJ_SET_PROTO(cx, ctor, proto); - } - } - - /* Add properties and methods to the prototype and the constructor. */ - if ((ps && !JS_DefineProperties(cx, proto, ps)) || - (fs && !JS_DefineFunctions(cx, proto, fs)) || - (static_ps && !JS_DefineProperties(cx, ctor, static_ps)) || - (static_fs && !JS_DefineFunctions(cx, ctor, static_fs))) { - goto bad; - } - - /* If this is a standard class, cache its prototype. */ - if (key != JSProto_Null && !js_SetClassObject(cx, obj, key, ctor)) - goto bad; - -out: - JS_POP_TEMP_ROOT(cx, &tvr); - return proto; - -bad: - if (named) - (void) OBJ_DELETE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &rval); - proto = NULL; - goto out; -} - -#ifdef JS_THREADSAFE -JS_PUBLIC_API(JSClass *) -JS_GetClass(JSContext *cx, JSObject *obj) -{ - return (JSClass *) - JSVAL_TO_PRIVATE(GC_AWARE_GET_SLOT(cx, obj, JSSLOT_CLASS)); -} -#else -JS_PUBLIC_API(JSClass *) -JS_GetClass(JSObject *obj) -{ - return LOCKED_OBJ_GET_CLASS(obj); -} -#endif - -JS_PUBLIC_API(JSBool) -JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv) -{ - JSFunction *fun; - - CHECK_REQUEST(cx); - if (OBJ_GET_CLASS(cx, obj) == clasp) - return JS_TRUE; - if (argv) { - fun = js_ValueToFunction(cx, &argv[-2], 0); - if (fun) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_INCOMPATIBLE_PROTO, - clasp->name, JS_GetFunctionName(fun), - OBJ_GET_CLASS(cx, obj)->name); - } - } - return JS_FALSE; -} - -JS_PUBLIC_API(JSBool) -JS_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - return js_HasInstance(cx, obj, v, bp); -} - -JS_PUBLIC_API(void *) -JS_GetPrivate(JSContext *cx, JSObject *obj) -{ - jsval v; - - JS_ASSERT(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_HAS_PRIVATE); - v = GC_AWARE_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - if (!JSVAL_IS_INT(v)) - return NULL; - return JSVAL_TO_PRIVATE(v); -} - -JS_PUBLIC_API(JSBool) -JS_SetPrivate(JSContext *cx, JSObject *obj, void *data) -{ - JS_ASSERT(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_HAS_PRIVATE); - OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(data)); - return JS_TRUE; -} - -JS_PUBLIC_API(void *) -JS_GetInstancePrivate(JSContext *cx, JSObject *obj, JSClass *clasp, - jsval *argv) -{ - if (!JS_InstanceOf(cx, obj, clasp, argv)) - return NULL; - return JS_GetPrivate(cx, obj); -} - -JS_PUBLIC_API(JSObject *) -JS_GetPrototype(JSContext *cx, JSObject *obj) -{ - JSObject *proto; - - CHECK_REQUEST(cx); - proto = JSVAL_TO_OBJECT(GC_AWARE_GET_SLOT(cx, obj, JSSLOT_PROTO)); - - /* Beware ref to dead object (we may be called from obj's finalizer). */ - return proto && proto->map ? proto : NULL; -} - -JS_PUBLIC_API(JSBool) -JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto) -{ - CHECK_REQUEST(cx); - if (obj->map->ops->setProto) - return obj->map->ops->setProto(cx, obj, JSSLOT_PROTO, proto); - OBJ_SET_SLOT(cx, obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(proto)); - return JS_TRUE; -} - -JS_PUBLIC_API(JSObject *) -JS_GetParent(JSContext *cx, JSObject *obj) -{ - JSObject *parent; - - parent = JSVAL_TO_OBJECT(GC_AWARE_GET_SLOT(cx, obj, JSSLOT_PARENT)); - - /* Beware ref to dead object (we may be called from obj's finalizer). */ - return parent && parent->map ? parent : NULL; -} - -JS_PUBLIC_API(JSBool) -JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent) -{ - CHECK_REQUEST(cx); - if (obj->map->ops->setParent) - return obj->map->ops->setParent(cx, obj, JSSLOT_PARENT, parent); - OBJ_SET_SLOT(cx, obj, JSSLOT_PARENT, OBJECT_TO_JSVAL(parent)); - return JS_TRUE; -} - -JS_PUBLIC_API(JSObject *) -JS_GetConstructor(JSContext *cx, JSObject *proto) -{ - jsval cval; - - CHECK_REQUEST(cx); - if (!OBJ_GET_PROPERTY(cx, proto, - ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), - &cval)) { - return NULL; - } - if (!VALUE_IS_FUNCTION(cx, cval)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_CONSTRUCTOR, - OBJ_GET_CLASS(cx, proto)->name); - return NULL; - } - return JSVAL_TO_OBJECT(cval); -} - -JS_PUBLIC_API(JSBool) -JS_GetObjectId(JSContext *cx, JSObject *obj, jsid *idp) -{ - JS_ASSERT(((jsid)obj & JSID_TAGMASK) == 0); - *idp = OBJECT_TO_JSID(obj); - return JS_TRUE; -} - -JS_PUBLIC_API(JSObject *) -JS_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) -{ - CHECK_REQUEST(cx); - if (!clasp) - clasp = &js_ObjectClass; /* default class is Object */ - return js_NewObject(cx, clasp, proto, parent); -} - -JS_PUBLIC_API(JSBool) -JS_SealObject(JSContext *cx, JSObject *obj, JSBool deep) -{ - JSScope *scope; - JSIdArray *ida; - uint32 nslots; - jsval v, *vp, *end; - - if (!OBJ_IS_NATIVE(obj)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CANT_SEAL_OBJECT, - OBJ_GET_CLASS(cx, obj)->name); - return JS_FALSE; - } - - scope = OBJ_SCOPE(obj); - -#if defined JS_THREADSAFE && defined DEBUG - /* Insist on scope being used exclusively by cx's thread. */ - if (scope->ownercx != cx) { - JS_LOCK_OBJ(cx, obj); - JS_ASSERT(OBJ_SCOPE(obj) == scope); - JS_ASSERT(scope->ownercx == cx); - JS_UNLOCK_SCOPE(cx, scope); - } -#endif - - /* Nothing to do if obj's scope is already sealed. */ - if (SCOPE_IS_SEALED(scope)) - return JS_TRUE; - - /* XXX Enumerate lazy properties now, as they can't be added later. */ - ida = JS_Enumerate(cx, obj); - if (!ida) - return JS_FALSE; - JS_DestroyIdArray(cx, ida); - - /* Ensure that obj has its own, mutable scope, and seal that scope. */ - JS_LOCK_OBJ(cx, obj); - scope = js_GetMutableScope(cx, obj); - if (scope) - SCOPE_SET_SEALED(scope); - JS_UNLOCK_OBJ(cx, obj); - if (!scope) - return JS_FALSE; - - /* If we are not sealing an entire object graph, we're done. */ - if (!deep) - return JS_TRUE; - - /* Walk obj->slots and if any value is a non-null object, seal it. */ - nslots = JS_MIN(scope->map.freeslot, scope->map.nslots); - for (vp = obj->slots, end = vp + nslots; vp < end; vp++) { - v = *vp; - if (JSVAL_IS_PRIMITIVE(v)) - continue; - if (!JS_SealObject(cx, JSVAL_TO_OBJECT(v), deep)) - return JS_FALSE; - } - return JS_TRUE; -} - -JS_PUBLIC_API(JSObject *) -JS_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, - JSObject *parent) -{ - CHECK_REQUEST(cx); - if (!clasp) - clasp = &js_ObjectClass; /* default class is Object */ - return js_ConstructObject(cx, clasp, proto, parent, 0, NULL); -} - -JS_PUBLIC_API(JSObject *) -JS_ConstructObjectWithArguments(JSContext *cx, JSClass *clasp, JSObject *proto, - JSObject *parent, uintN argc, jsval *argv) -{ - CHECK_REQUEST(cx); - if (!clasp) - clasp = &js_ObjectClass; /* default class is Object */ - return js_ConstructObject(cx, clasp, proto, parent, argc, argv); -} - -static JSBool -DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs, - uintN flags, intN tinyid) -{ - jsid id; - JSAtom *atom; - - if (attrs & JSPROP_INDEX) { - id = INT_TO_JSID(JS_PTR_TO_INT32(name)); - atom = NULL; - attrs &= ~JSPROP_INDEX; - } else { - atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return JS_FALSE; - id = ATOM_TO_JSID(atom); - } - if (flags != 0 && OBJ_IS_NATIVE(obj)) { - return js_DefineNativeProperty(cx, obj, id, value, getter, setter, - attrs, flags, tinyid, NULL); - } - return OBJ_DEFINE_PROPERTY(cx, obj, id, value, getter, setter, attrs, - NULL); -} - -#define AUTO_NAMELEN(s,n) (((n) == (size_t)-1) ? js_strlen(s) : (n)) - -static JSBool -DefineUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs, - uintN flags, intN tinyid) -{ - JSAtom *atom; - - atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); - if (!atom) - return JS_FALSE; - if (flags != 0 && OBJ_IS_NATIVE(obj)) { - return js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value, - getter, setter, attrs, flags, tinyid, - NULL); - } - return OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), value, - getter, setter, attrs, NULL); -} - -JS_PUBLIC_API(JSObject *) -JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *clasp, - JSObject *proto, uintN attrs) -{ - JSObject *nobj; - - CHECK_REQUEST(cx); - if (!clasp) - clasp = &js_ObjectClass; /* default class is Object */ - nobj = js_NewObject(cx, clasp, proto, obj); - if (!nobj) - return NULL; - if (!DefineProperty(cx, obj, name, OBJECT_TO_JSVAL(nobj), NULL, NULL, attrs, - 0, 0)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; - } - return nobj; -} - -JS_PUBLIC_API(JSBool) -JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds) -{ - JSBool ok; - jsval value; - uintN flags; - - CHECK_REQUEST(cx); - for (ok = JS_TRUE; cds->name; cds++) { - ok = js_NewNumberValue(cx, cds->dval, &value); - if (!ok) - break; - flags = cds->flags; - if (!flags) - flags = JSPROP_READONLY | JSPROP_PERMANENT; - ok = DefineProperty(cx, obj, cds->name, value, NULL, NULL, flags, 0, 0); - if (!ok) - break; - } - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_DefineProperties(JSContext *cx, JSObject *obj, JSPropertySpec *ps) -{ - JSBool ok; - - CHECK_REQUEST(cx); - for (ok = JS_TRUE; ps->name; ps++) { - ok = DefineProperty(cx, obj, ps->name, JSVAL_VOID, - ps->getter, ps->setter, ps->flags, - SPROP_HAS_SHORTID, ps->tinyid); - if (!ok) - break; - } - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs) -{ - CHECK_REQUEST(cx); - return DefineProperty(cx, obj, name, value, getter, setter, attrs, 0, 0); -} - -JS_PUBLIC_API(JSBool) -JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name, - int8 tinyid, jsval value, - JSPropertyOp getter, JSPropertyOp setter, - uintN attrs) -{ - CHECK_REQUEST(cx); - return DefineProperty(cx, obj, name, value, getter, setter, attrs, - SPROP_HAS_SHORTID, tinyid); -} - -static JSBool -LookupProperty(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, - JSProperty **propp) -{ - JSAtom *atom; - - atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return JS_FALSE; - return OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), objp, propp); -} - -static JSBool -LookupUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - JSObject **objp, JSProperty **propp) -{ - JSAtom *atom; - - atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); - if (!atom) - return JS_FALSE; - return OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), objp, propp); -} - -JS_PUBLIC_API(JSBool) -JS_AliasProperty(JSContext *cx, JSObject *obj, const char *name, - const char *alias) -{ - JSObject *obj2; - JSProperty *prop; - JSAtom *atom; - JSBool ok; - JSScopeProperty *sprop; - - CHECK_REQUEST(cx); - if (!LookupProperty(cx, obj, name, &obj2, &prop)) - return JS_FALSE; - if (!prop) { - js_ReportIsNotDefined(cx, name); - return JS_FALSE; - } - if (obj2 != obj || !OBJ_IS_NATIVE(obj)) { - OBJ_DROP_PROPERTY(cx, obj2, prop); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_ALIAS, - alias, name, OBJ_GET_CLASS(cx, obj2)->name); - return JS_FALSE; - } - atom = js_Atomize(cx, alias, strlen(alias), 0); - if (!atom) { - ok = JS_FALSE; - } else { - sprop = (JSScopeProperty *)prop; - ok = (js_AddNativeProperty(cx, obj, ATOM_TO_JSID(atom), - sprop->getter, sprop->setter, sprop->slot, - sprop->attrs, sprop->flags | SPROP_IS_ALIAS, - sprop->shortid) - != NULL); - } - OBJ_DROP_PROPERTY(cx, obj, prop); - return ok; -} - -static jsval -LookupResult(JSContext *cx, JSObject *obj, JSObject *obj2, JSProperty *prop) -{ - JSScopeProperty *sprop; - jsval rval; - - if (!prop) { - /* XXX bad API: no way to tell "not defined" from "void value" */ - return JSVAL_VOID; - } - if (OBJ_IS_NATIVE(obj2)) { - /* Peek at the native property's slot value, without doing a Get. */ - sprop = (JSScopeProperty *)prop; - rval = SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj2)) - ? LOCKED_OBJ_GET_SLOT(obj2, sprop->slot) - : JSVAL_TRUE; - } else { - /* XXX bad API: no way to return "defined but value unknown" */ - rval = JSVAL_TRUE; - } - OBJ_DROP_PROPERTY(cx, obj2, prop); - return rval; -} - -static JSBool -GetPropertyAttributes(JSContext *cx, JSObject *obj, JSAtom *atom, - uintN *attrsp, JSBool *foundp, - JSPropertyOp *getterp, JSPropertyOp *setterp) -{ - JSObject *obj2; - JSProperty *prop; - JSBool ok; - - if (!atom) - return JS_FALSE; - if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop)) - return JS_FALSE; - - if (!prop || obj != obj2) { - *attrsp = 0; - *foundp = JS_FALSE; - if (getterp) - *getterp = NULL; - if (setterp) - *setterp = NULL; - if (prop) - OBJ_DROP_PROPERTY(cx, obj2, prop); - return JS_TRUE; - } - - *foundp = JS_TRUE; - ok = OBJ_GET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, attrsp); - if (ok && OBJ_IS_NATIVE(obj)) { - JSScopeProperty *sprop = (JSScopeProperty *) prop; - - if (getterp) - *getterp = sprop->getter; - if (setterp) - *setterp = sprop->setter; - } - OBJ_DROP_PROPERTY(cx, obj, prop); - return ok; -} - -static JSBool -SetPropertyAttributes(JSContext *cx, JSObject *obj, JSAtom *atom, - uintN attrs, JSBool *foundp) -{ - JSObject *obj2; - JSProperty *prop; - JSBool ok; - - if (!atom) - return JS_FALSE; - if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop)) - return JS_FALSE; - if (!prop || obj != obj2) { - *foundp = JS_FALSE; - if (prop) - OBJ_DROP_PROPERTY(cx, obj2, prop); - return JS_TRUE; - } - - *foundp = JS_TRUE; - ok = OBJ_SET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs); - OBJ_DROP_PROPERTY(cx, obj, prop); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_GetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, - uintN *attrsp, JSBool *foundp) -{ - CHECK_REQUEST(cx); - return GetPropertyAttributes(cx, obj, - js_Atomize(cx, name, strlen(name), 0), - attrsp, foundp, NULL, NULL); -} - -JS_PUBLIC_API(JSBool) -JS_GetPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, - const char *name, - uintN *attrsp, JSBool *foundp, - JSPropertyOp *getterp, - JSPropertyOp *setterp) -{ - CHECK_REQUEST(cx); - return GetPropertyAttributes(cx, obj, - js_Atomize(cx, name, strlen(name), 0), - attrsp, foundp, getterp, setterp); -} - -JS_PUBLIC_API(JSBool) -JS_SetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, - uintN attrs, JSBool *foundp) -{ - CHECK_REQUEST(cx); - return SetPropertyAttributes(cx, obj, - js_Atomize(cx, name, strlen(name), 0), - attrs, foundp); -} - -JS_PUBLIC_API(JSBool) -JS_HasProperty(JSContext *cx, JSObject *obj, const char *name, JSBool *foundp) -{ - JSBool ok; - JSObject *obj2; - JSProperty *prop; - - CHECK_REQUEST(cx); - ok = LookupProperty(cx, obj, name, &obj2, &prop); - if (ok) { - *foundp = (prop != NULL); - if (prop) - OBJ_DROP_PROPERTY(cx, obj2, prop); - } - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_LookupProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) -{ - JSBool ok; - JSObject *obj2; - JSProperty *prop; - - CHECK_REQUEST(cx); - ok = LookupProperty(cx, obj, name, &obj2, &prop); - if (ok) - *vp = LookupResult(cx, obj, obj2, prop); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, const char *name, - uintN flags, jsval *vp) -{ - JSAtom *atom; - JSBool ok; - JSObject *obj2; - JSProperty *prop; - - CHECK_REQUEST(cx); - atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return JS_FALSE; - ok = OBJ_IS_NATIVE(obj) - ? js_LookupPropertyWithFlags(cx, obj, ATOM_TO_JSID(atom), flags, - &obj2, &prop) - : OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop); - if (ok) - *vp = LookupResult(cx, obj, obj2, prop); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return JS_FALSE; - return OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); -} - -JS_PUBLIC_API(JSBool) -JS_GetMethodById(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - jsval *vp) -{ - CHECK_REQUEST(cx); - -#if JS_HAS_XML_SUPPORT - if (OBJECT_IS_XML(cx, obj)) { - JSXMLObjectOps *ops; - - ops = (JSXMLObjectOps *) obj->map->ops; - obj = ops->getMethod(cx, obj, id, vp); - if (!obj) - return JS_FALSE; - } else -#endif - { - if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) - return JS_FALSE; - } - - *objp = obj; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_GetMethod(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, - jsval *vp) -{ - JSAtom *atom; - - atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return JS_FALSE; - return JS_GetMethodById(cx, obj, ATOM_TO_JSID(atom), objp, vp); -} - -JS_PUBLIC_API(JSBool) -JS_SetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return JS_FALSE; - return OBJ_SET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); -} - -JS_PUBLIC_API(JSBool) -JS_DeleteProperty(JSContext *cx, JSObject *obj, const char *name) -{ - jsval junk; - - CHECK_REQUEST(cx); - return JS_DeleteProperty2(cx, obj, name, &junk); -} - -JS_PUBLIC_API(JSBool) -JS_DeleteProperty2(JSContext *cx, JSObject *obj, const char *name, - jsval *rval) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return JS_FALSE; - return OBJ_DELETE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval); -} - -JS_PUBLIC_API(JSBool) -JS_DefineUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, jsval value, - JSPropertyOp getter, JSPropertyOp setter, - uintN attrs) -{ - CHECK_REQUEST(cx); - return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, - attrs, 0, 0); -} - -JS_PUBLIC_API(JSBool) -JS_GetUCPropertyAttributes(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - uintN *attrsp, JSBool *foundp) -{ - CHECK_REQUEST(cx); - return GetPropertyAttributes(cx, obj, - js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0), - attrsp, foundp, NULL, NULL); -} - -JS_PUBLIC_API(JSBool) -JS_GetUCPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - uintN *attrsp, JSBool *foundp, - JSPropertyOp *getterp, - JSPropertyOp *setterp) -{ - CHECK_REQUEST(cx); - return GetPropertyAttributes(cx, obj, - js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0), - attrsp, foundp, getterp, setterp); -} - -JS_PUBLIC_API(JSBool) -JS_SetUCPropertyAttributes(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - uintN attrs, JSBool *foundp) -{ - CHECK_REQUEST(cx); - return SetPropertyAttributes(cx, obj, - js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0), - attrs, foundp); -} - -JS_PUBLIC_API(JSBool) -JS_DefineUCPropertyWithTinyId(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - int8 tinyid, jsval value, - JSPropertyOp getter, JSPropertyOp setter, - uintN attrs) -{ - CHECK_REQUEST(cx); - return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, - attrs, SPROP_HAS_SHORTID, tinyid); -} - -JS_PUBLIC_API(JSBool) -JS_HasUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - JSBool *vp) -{ - JSBool ok; - JSObject *obj2; - JSProperty *prop; - - CHECK_REQUEST(cx); - ok = LookupUCProperty(cx, obj, name, namelen, &obj2, &prop); - if (ok) { - *vp = (prop != NULL); - if (prop) - OBJ_DROP_PROPERTY(cx, obj2, prop); - } - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_LookupUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - jsval *vp) -{ - JSBool ok; - JSObject *obj2; - JSProperty *prop; - - CHECK_REQUEST(cx); - ok = LookupUCProperty(cx, obj, name, namelen, &obj2, &prop); - if (ok) - *vp = LookupResult(cx, obj, obj2, prop); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_GetUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - jsval *vp) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); - if (!atom) - return JS_FALSE; - return OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); -} - -JS_PUBLIC_API(JSBool) -JS_SetUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - jsval *vp) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); - if (!atom) - return JS_FALSE; - return OBJ_SET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); -} - -JS_PUBLIC_API(JSBool) -JS_DeleteUCProperty2(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - jsval *rval) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); - if (!atom) - return JS_FALSE; - return OBJ_DELETE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval); -} - -JS_PUBLIC_API(JSObject *) -JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector) -{ - CHECK_REQUEST(cx); - /* NB: jsuint cast does ToUint32. */ - return js_NewArrayObject(cx, (jsuint)length, vector); -} - -JS_PUBLIC_API(JSBool) -JS_IsArrayObject(JSContext *cx, JSObject *obj) -{ - return OBJ_GET_CLASS(cx, obj) == &js_ArrayClass; -} - -JS_PUBLIC_API(JSBool) -JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp) -{ - CHECK_REQUEST(cx); - return js_GetLengthProperty(cx, obj, lengthp); -} - -JS_PUBLIC_API(JSBool) -JS_SetArrayLength(JSContext *cx, JSObject *obj, jsuint length) -{ - CHECK_REQUEST(cx); - return js_SetLengthProperty(cx, obj, length); -} - -JS_PUBLIC_API(JSBool) -JS_HasArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp) -{ - CHECK_REQUEST(cx); - return js_HasLengthProperty(cx, obj, lengthp); -} - -JS_PUBLIC_API(JSBool) -JS_DefineElement(JSContext *cx, JSObject *obj, jsint index, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs) -{ - CHECK_REQUEST(cx); - return OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(index), value, - getter, setter, attrs, NULL); -} - -JS_PUBLIC_API(JSBool) -JS_AliasElement(JSContext *cx, JSObject *obj, const char *name, jsint alias) -{ - JSObject *obj2; - JSProperty *prop; - JSScopeProperty *sprop; - JSBool ok; - - CHECK_REQUEST(cx); - if (!LookupProperty(cx, obj, name, &obj2, &prop)) - return JS_FALSE; - if (!prop) { - js_ReportIsNotDefined(cx, name); - return JS_FALSE; - } - if (obj2 != obj || !OBJ_IS_NATIVE(obj)) { - char numBuf[12]; - OBJ_DROP_PROPERTY(cx, obj2, prop); - JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)alias); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_ALIAS, - numBuf, name, OBJ_GET_CLASS(cx, obj2)->name); - return JS_FALSE; - } - sprop = (JSScopeProperty *)prop; - ok = (js_AddNativeProperty(cx, obj, INT_TO_JSID(alias), - sprop->getter, sprop->setter, sprop->slot, - sprop->attrs, sprop->flags | SPROP_IS_ALIAS, - sprop->shortid) - != NULL); - OBJ_DROP_PROPERTY(cx, obj, prop); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_HasElement(JSContext *cx, JSObject *obj, jsint index, JSBool *foundp) -{ - JSBool ok; - JSObject *obj2; - JSProperty *prop; - - CHECK_REQUEST(cx); - ok = OBJ_LOOKUP_PROPERTY(cx, obj, INT_TO_JSID(index), &obj2, &prop); - if (ok) { - *foundp = (prop != NULL); - if (prop) - OBJ_DROP_PROPERTY(cx, obj2, prop); - } - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_LookupElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) -{ - JSBool ok; - JSObject *obj2; - JSProperty *prop; - - CHECK_REQUEST(cx); - ok = OBJ_LOOKUP_PROPERTY(cx, obj, INT_TO_JSID(index), &obj2, &prop); - if (ok) - *vp = LookupResult(cx, obj, obj2, prop); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_GetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) -{ - CHECK_REQUEST(cx); - return OBJ_GET_PROPERTY(cx, obj, INT_TO_JSID(index), vp); -} - -JS_PUBLIC_API(JSBool) -JS_SetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) -{ - CHECK_REQUEST(cx); - return OBJ_SET_PROPERTY(cx, obj, INT_TO_JSID(index), vp); -} - -JS_PUBLIC_API(JSBool) -JS_DeleteElement(JSContext *cx, JSObject *obj, jsint index) -{ - jsval junk; - - CHECK_REQUEST(cx); - return JS_DeleteElement2(cx, obj, index, &junk); -} - -JS_PUBLIC_API(JSBool) -JS_DeleteElement2(JSContext *cx, JSObject *obj, jsint index, jsval *rval) -{ - CHECK_REQUEST(cx); - return OBJ_DELETE_PROPERTY(cx, obj, INT_TO_JSID(index), rval); -} - -JS_PUBLIC_API(void) -JS_ClearScope(JSContext *cx, JSObject *obj) -{ - CHECK_REQUEST(cx); - - if (obj->map->ops->clear) - obj->map->ops->clear(cx, obj); - - /* Clear cached class objects on the global object. */ - if (JS_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL) { - JSProtoKey key; - - for (key = JSProto_Null; key < JSProto_LIMIT; key++) - JS_SetReservedSlot(cx, obj, key, JSVAL_VOID); - } -} - -JS_PUBLIC_API(JSIdArray *) -JS_Enumerate(JSContext *cx, JSObject *obj) -{ - jsint i, n; - jsval iter_state, num_properties; - jsid id; - JSIdArray *ida; - jsval *vector; - - CHECK_REQUEST(cx); - - ida = NULL; - iter_state = JSVAL_NULL; - - /* Get the number of properties to enumerate. */ - if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties)) - goto error; - if (!JSVAL_IS_INT(num_properties)) { - JS_ASSERT(0); - goto error; - } - - /* Grow as needed if we don't know the exact amount ahead of time. */ - n = JSVAL_TO_INT(num_properties); - if (n <= 0) - n = 8; - - /* Create an array of jsids large enough to hold all the properties */ - ida = js_NewIdArray(cx, n); - if (!ida) - goto error; - - i = 0; - vector = &ida->vector[0]; - for (;;) { - if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &iter_state, &id)) - goto error; - - /* No more jsid's to enumerate ? */ - if (iter_state == JSVAL_NULL) - break; - - if (i == ida->length) { - ida = js_SetIdArrayLength(cx, ida, ida->length * 2); - if (!ida) - goto error; - vector = &ida->vector[0]; - } - vector[i++] = id; - } - return js_SetIdArrayLength(cx, ida, i); - -error: - if (iter_state != JSVAL_NULL) - OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0); - if (ida) - JS_DestroyIdArray(cx, ida); - return NULL; -} - -/* - * XXX reverse iterator for properties, unreverse and meld with jsinterp.c's - * prop_iterator_class somehow... - * + preserve the OBJ_ENUMERATE API while optimizing the native object case - * + native case here uses a JSScopeProperty *, but that iterates in reverse! - * + so we make non-native match, by reverse-iterating after JS_Enumerating - */ -#define JSSLOT_ITER_INDEX (JSSLOT_PRIVATE + 1) - -#if JSSLOT_ITER_INDEX >= JS_INITIAL_NSLOTS -# error "JSSLOT_ITER_INDEX botch!" -#endif - -static void -prop_iter_finalize(JSContext *cx, JSObject *obj) -{ - jsval v; - jsint i; - JSIdArray *ida; - - v = GC_AWARE_GET_SLOT(cx, obj, JSSLOT_ITER_INDEX); - if (JSVAL_IS_VOID(v)) - return; - - i = JSVAL_TO_INT(v); - if (i >= 0) { - /* Non-native case: destroy the ida enumerated when obj was created. */ - ida = (JSIdArray *) JS_GetPrivate(cx, obj); - if (ida) - JS_DestroyIdArray(cx, ida); - } -} - -static uint32 -prop_iter_mark(JSContext *cx, JSObject *obj, void *arg) -{ - jsval v; - jsint i, n; - JSScopeProperty *sprop; - JSIdArray *ida; - jsid id; - - v = GC_AWARE_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - JS_ASSERT(!JSVAL_IS_VOID(v)); - - i = JSVAL_TO_INT(OBJ_GET_SLOT(cx, obj, JSSLOT_ITER_INDEX)); - if (i < 0) { - /* Native case: just mark the next property to visit. */ - sprop = (JSScopeProperty *) JSVAL_TO_PRIVATE(v); - if (sprop) - MARK_SCOPE_PROPERTY(cx, sprop); - } else { - /* Non-native case: mark each id in the JSIdArray private. */ - ida = (JSIdArray *) JSVAL_TO_PRIVATE(v); - for (i = 0, n = ida->length; i < n; i++) { - id = ida->vector[i]; - MARK_ID(cx, id); - } - } - return 0; -} - -static JSClass prop_iter_class = { - "PropertyIterator", - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, prop_iter_finalize, - NULL, NULL, NULL, NULL, - NULL, NULL, prop_iter_mark, NULL -}; - -JS_PUBLIC_API(JSObject *) -JS_NewPropertyIterator(JSContext *cx, JSObject *obj) -{ - JSObject *iterobj; - JSScope *scope; - void *pdata; - jsint index; - JSIdArray *ida; - - CHECK_REQUEST(cx); - iterobj = js_NewObject(cx, &prop_iter_class, NULL, obj); - if (!iterobj) - return NULL; - - if (OBJ_IS_NATIVE(obj)) { - /* Native case: start with the last property in obj's own scope. */ - scope = OBJ_SCOPE(obj); - pdata = (scope->object == obj) ? scope->lastProp : NULL; - index = -1; - } else { - JSTempValueRooter tvr; - - /* - * Non-native case: enumerate a JSIdArray and keep it via private. - * - * Note: we have to make sure that we root obj around the call to - * JS_Enumerate to protect against multiple allocations under it. - */ - JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(iterobj), &tvr); - ida = JS_Enumerate(cx, obj); - JS_POP_TEMP_ROOT(cx, &tvr); - if (!ida) - goto bad; - pdata = ida; - index = ida->length; - } - - /* iterobj can not escape to other threads here. */ - iterobj->slots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(pdata); - iterobj->slots[JSSLOT_ITER_INDEX] = INT_TO_JSVAL(index); - return iterobj; - - bad: - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; -} - -JS_PUBLIC_API(JSBool) -JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp) -{ - jsint i; - JSObject *obj; - JSScope *scope; - JSScopeProperty *sprop; - JSIdArray *ida; - - CHECK_REQUEST(cx); - i = JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_INDEX)); - if (i < 0) { - /* Native case: private data is a property tree node pointer. */ - obj = OBJ_GET_PARENT(cx, iterobj); - JS_ASSERT(OBJ_IS_NATIVE(obj)); - scope = OBJ_SCOPE(obj); - JS_ASSERT(scope->object == obj); - sprop = (JSScopeProperty *) JS_GetPrivate(cx, iterobj); - - /* - * If the next property mapped by scope in the property tree ancestor - * line is not enumerable, or it's an alias, or one or more properties - * were deleted from the "middle" of the scope-mapped ancestor line - * and the next property was among those deleted, skip it and keep on - * trying to find an enumerable property that is still in scope. - */ - while (sprop && - (!(sprop->attrs & JSPROP_ENUMERATE) || - (sprop->flags & SPROP_IS_ALIAS) || - (SCOPE_HAD_MIDDLE_DELETE(scope) && - !SCOPE_HAS_PROPERTY(scope, sprop)))) { - sprop = sprop->parent; - } - - if (!sprop) { - *idp = JSVAL_VOID; - } else { - if (!JS_SetPrivate(cx, iterobj, sprop->parent)) - return JS_FALSE; - *idp = sprop->id; - } - } else { - /* Non-native case: use the ida enumerated when iterobj was created. */ - ida = (JSIdArray *) JS_GetPrivate(cx, iterobj); - JS_ASSERT(i <= ida->length); - if (i == 0) { - *idp = JSVAL_VOID; - } else { - *idp = ida->vector[--i]; - OBJ_SET_SLOT(cx, iterobj, JSSLOT_ITER_INDEX, INT_TO_JSVAL(i)); - } - } - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, - jsval *vp, uintN *attrsp) -{ - CHECK_REQUEST(cx); - return OBJ_CHECK_ACCESS(cx, obj, id, mode, vp, attrsp); -} - -JS_PUBLIC_API(JSCheckAccessOp) -JS_SetCheckObjectAccessCallback(JSRuntime *rt, JSCheckAccessOp acb) -{ - JSCheckAccessOp oldacb; - - oldacb = rt->checkObjectAccess; - rt->checkObjectAccess = acb; - return oldacb; -} - -static JSBool -ReservedSlotIndexOK(JSContext *cx, JSObject *obj, JSClass *clasp, - uint32 index, uint32 limit) -{ - /* Check the computed, possibly per-instance, upper bound. */ - if (clasp->reserveSlots) - JS_LOCK_OBJ_VOID(cx, obj, limit += clasp->reserveSlots(cx, obj)); - if (index >= limit) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_RESERVED_SLOT_RANGE); - return JS_FALSE; - } - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval *vp) -{ - JSClass *clasp; - uint32 limit, slot; - - CHECK_REQUEST(cx); - clasp = OBJ_GET_CLASS(cx, obj); - limit = JSCLASS_RESERVED_SLOTS(clasp); - if (index >= limit && !ReservedSlotIndexOK(cx, obj, clasp, index, limit)) - return JS_FALSE; - slot = JSSLOT_START(clasp) + index; - *vp = OBJ_GET_REQUIRED_SLOT(cx, obj, slot); - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v) -{ - JSClass *clasp; - uint32 limit, slot; - - CHECK_REQUEST(cx); - clasp = OBJ_GET_CLASS(cx, obj); - limit = JSCLASS_RESERVED_SLOTS(clasp); - if (index >= limit && !ReservedSlotIndexOK(cx, obj, clasp, index, limit)) - return JS_FALSE; - slot = JSSLOT_START(clasp) + index; - return OBJ_SET_REQUIRED_SLOT(cx, obj, slot, v); -} - -#ifdef JS_THREADSAFE -JS_PUBLIC_API(jsrefcount) -JS_HoldPrincipals(JSContext *cx, JSPrincipals *principals) -{ - return JS_ATOMIC_INCREMENT(&principals->refcount); -} - -JS_PUBLIC_API(jsrefcount) -JS_DropPrincipals(JSContext *cx, JSPrincipals *principals) -{ - jsrefcount rc = JS_ATOMIC_DECREMENT(&principals->refcount); - if (rc == 0) - principals->destroy(cx, principals); - return rc; -} -#endif - -JS_PUBLIC_API(JSPrincipalsTranscoder) -JS_SetPrincipalsTranscoder(JSRuntime *rt, JSPrincipalsTranscoder px) -{ - JSPrincipalsTranscoder oldpx; - - oldpx = rt->principalsTranscoder; - rt->principalsTranscoder = px; - return oldpx; -} - -JS_PUBLIC_API(JSObjectPrincipalsFinder) -JS_SetObjectPrincipalsFinder(JSRuntime *rt, JSObjectPrincipalsFinder fop) -{ - JSObjectPrincipalsFinder oldfop; - - oldfop = rt->findObjectPrincipals; - rt->findObjectPrincipals = fop; - return oldfop; -} - -JS_PUBLIC_API(JSFunction *) -JS_NewFunction(JSContext *cx, JSNative native, uintN nargs, uintN flags, - JSObject *parent, const char *name) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - - if (!name) { - atom = NULL; - } else { - atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return NULL; - } - return js_NewFunction(cx, NULL, native, nargs, flags, parent, atom); -} - -JS_PUBLIC_API(JSObject *) -JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) -{ - CHECK_REQUEST(cx); - if (OBJ_GET_CLASS(cx, funobj) != &js_FunctionClass) { - /* Indicate we cannot clone this object. */ - return funobj; - } - return js_CloneFunctionObject(cx, funobj, parent); -} - -JS_PUBLIC_API(JSObject *) -JS_GetFunctionObject(JSFunction *fun) -{ - return fun->object; -} - -JS_PUBLIC_API(const char *) -JS_GetFunctionName(JSFunction *fun) -{ - return fun->atom - ? JS_GetStringBytes(ATOM_TO_STRING(fun->atom)) - : js_anonymous_str; -} - -JS_PUBLIC_API(JSString *) -JS_GetFunctionId(JSFunction *fun) -{ - return fun->atom ? ATOM_TO_STRING(fun->atom) : NULL; -} - -JS_PUBLIC_API(uintN) -JS_GetFunctionFlags(JSFunction *fun) -{ -#ifdef MOZILLA_1_8_BRANCH - uintN flags = fun->flags; - - return JSFUN_DISJOINT_FLAGS(flags) | - (JSFUN_GETTER_TEST(flags) ? JSFUN_GETTER : 0) | - (JSFUN_SETTER_TEST(flags) ? JSFUN_SETTER : 0) | - (JSFUN_BOUND_METHOD_TEST(flags) ? JSFUN_BOUND_METHOD : 0) | - (JSFUN_HEAVYWEIGHT_TEST(flags) ? JSFUN_HEAVYWEIGHT : 0); -#else - return fun->flags; -#endif -} - -JS_PUBLIC_API(uint16) -JS_GetFunctionArity(JSFunction *fun) -{ - return fun->nargs; -} - -JS_PUBLIC_API(JSBool) -JS_ObjectIsFunction(JSContext *cx, JSObject *obj) -{ - return OBJ_GET_CLASS(cx, obj) == &js_FunctionClass; -} - -JS_STATIC_DLL_CALLBACK(JSBool) -js_generic_native_method_dispatcher(JSContext *cx, JSObject *obj, - uintN argc, jsval *argv, jsval *rval) -{ - jsval fsv; - JSFunctionSpec *fs; - JSObject *tmp; - - if (!JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(argv[-2]), 0, &fsv)) - return JS_FALSE; - fs = (JSFunctionSpec *) JSVAL_TO_PRIVATE(fsv); - - /* - * We know that argv[0] is valid because JS_DefineFunctions, which is our - * only (indirect) referrer, defined us as requiring at least one argument - * (notice how it passes fs->nargs + 1 as the next-to-last argument to - * JS_DefineFunction). - */ - if (JSVAL_IS_PRIMITIVE(argv[0])) { - /* - * Make sure that this is an object or null, as required by the generic - * functions. - */ - if (!js_ValueToObject(cx, argv[0], &tmp)) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(tmp); - } - - /* - * Copy all actual (argc) and required but missing (fs->nargs + 1 - argc) - * args down over our |this| parameter, argv[-1], which is almost always - * the class constructor object, e.g. Array. Then call the corresponding - * prototype native method with our first argument passed as |this|. - */ - memmove(argv - 1, argv, JS_MAX(fs->nargs + 1U, argc) * sizeof(jsval)); - - /* - * Follow Function.prototype.apply and .call by using the global object as - * the 'this' param if no args. - */ - JS_ASSERT(cx->fp->argv == argv); - tmp = js_ComputeThis(cx, JSVAL_TO_OBJECT(argv[-1]), argv); - if (!tmp) - return JS_FALSE; - cx->fp->thisp = tmp; - - /* - * Protect against argc - 1 underflowing below. By calling js_ComputeThis, - * we made it as if the static was called with one parameter. - */ - if (argc == 0) - argc = 1; - - return fs->call(cx, JSVAL_TO_OBJECT(argv[-1]), argc - 1, argv, rval); -} - -JS_PUBLIC_API(JSBool) -JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs) -{ - uintN flags; - JSObject *ctor; - JSFunction *fun; - - CHECK_REQUEST(cx); - ctor = NULL; - for (; fs->name; fs++) { - - /* High bits of fs->extra are reserved. */ - JS_ASSERT((fs->extra & 0xFFFF0000) == 0); - flags = fs->flags; - - /* - * Define a generic arity N+1 static method for the arity N prototype - * method if flags contains JSFUN_GENERIC_NATIVE. - */ - if (flags & JSFUN_GENERIC_NATIVE) { - if (!ctor) { - ctor = JS_GetConstructor(cx, obj); - if (!ctor) - return JS_FALSE; - } - - flags &= ~JSFUN_GENERIC_NATIVE; - fun = JS_DefineFunction(cx, ctor, fs->name, - js_generic_native_method_dispatcher, - fs->nargs + 1, flags); - if (!fun) - return JS_FALSE; - fun->u.n.extra = (uint16)fs->extra; - - /* - * As jsapi.h notes, fs must point to storage that lives as long - * as fun->object lives. - */ - if (!JS_SetReservedSlot(cx, fun->object, 0, PRIVATE_TO_JSVAL(fs))) - return JS_FALSE; - } - - fun = JS_DefineFunction(cx, obj, fs->name, fs->call, fs->nargs, flags); - if (!fun) - return JS_FALSE; - fun->u.n.extra = (uint16)fs->extra; - } - return JS_TRUE; -} - -JS_PUBLIC_API(JSFunction *) -JS_DefineFunction(JSContext *cx, JSObject *obj, const char *name, JSNative call, - uintN nargs, uintN attrs) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return NULL; - return js_DefineFunction(cx, obj, atom, call, nargs, attrs); -} - -JS_PUBLIC_API(JSFunction *) -JS_DefineUCFunction(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, JSNative call, - uintN nargs, uintN attrs) -{ - JSAtom *atom; - - atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); - if (!atom) - return NULL; - return js_DefineFunction(cx, obj, atom, call, nargs, attrs); -} - -static JSScript * -CompileTokenStream(JSContext *cx, JSObject *obj, JSTokenStream *ts, - void *tempMark, JSBool *eofp) -{ - JSBool eof; - JSArenaPool codePool, notePool; - JSCodeGenerator cg; - JSScript *script; - - CHECK_REQUEST(cx); - eof = JS_FALSE; - JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode)); - JS_InitArenaPool(¬ePool, "note", 1024, sizeof(jssrcnote)); - if (!js_InitCodeGenerator(cx, &cg, &codePool, ¬ePool, - ts->filename, ts->lineno, - ts->principals)) { - script = NULL; - } else if (!js_CompileTokenStream(cx, obj, ts, &cg)) { - script = NULL; - eof = (ts->flags & TSF_EOF) != 0; - } else { - script = js_NewScriptFromCG(cx, &cg, NULL); - } - if (eofp) - *eofp = eof; - if (!js_CloseTokenStream(cx, ts)) { - if (script) - js_DestroyScript(cx, script); - script = NULL; - } - cg.tempMark = tempMark; - js_FinishCodeGenerator(cx, &cg); - JS_FinishArenaPool(&codePool); - JS_FinishArenaPool(¬ePool); - return script; -} - -JS_PUBLIC_API(JSScript *) -JS_CompileScript(JSContext *cx, JSObject *obj, - const char *bytes, size_t length, - const char *filename, uintN lineno) -{ - jschar *chars; - JSScript *script; - - CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, &length); - if (!chars) - return NULL; - script = JS_CompileUCScript(cx, obj, chars, length, filename, lineno); - JS_free(cx, chars); - return script; -} - -JS_PUBLIC_API(JSScript *) -JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, - const char *bytes, size_t length, - const char *filename, uintN lineno) -{ - jschar *chars; - JSScript *script; - - CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, &length); - if (!chars) - return NULL; - script = JS_CompileUCScriptForPrincipals(cx, obj, principals, - chars, length, filename, lineno); - JS_free(cx, chars); - return script; -} - -JS_PUBLIC_API(JSScript *) -JS_CompileUCScript(JSContext *cx, JSObject *obj, - const jschar *chars, size_t length, - const char *filename, uintN lineno) -{ - CHECK_REQUEST(cx); - return JS_CompileUCScriptForPrincipals(cx, obj, NULL, chars, length, - filename, lineno); -} - -#define LAST_FRAME_EXCEPTION_CHECK(cx,result) \ - JS_BEGIN_MACRO \ - if (!(result) && !((cx)->options & JSOPTION_DONT_REPORT_UNCAUGHT)) \ - js_ReportUncaughtException(cx); \ - JS_END_MACRO - -#define LAST_FRAME_CHECKS(cx,result) \ - JS_BEGIN_MACRO \ - if (!(cx)->fp) { \ - (cx)->weakRoots.lastInternalResult = JSVAL_NULL; \ - LAST_FRAME_EXCEPTION_CHECK(cx, result); \ - } \ - JS_END_MACRO - -JS_PUBLIC_API(JSScript *) -JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, - const jschar *chars, size_t length, - const char *filename, uintN lineno) -{ - void *mark; - JSTokenStream *ts; - JSScript *script; - - CHECK_REQUEST(cx); - mark = JS_ARENA_MARK(&cx->tempPool); - ts = js_NewTokenStream(cx, chars, length, filename, lineno, principals); - if (!ts) - return NULL; - script = CompileTokenStream(cx, obj, ts, mark, NULL); - LAST_FRAME_CHECKS(cx, script); - return script; -} - -JS_PUBLIC_API(JSBool) -JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj, - const char *bytes, size_t length) -{ - jschar *chars; - JSBool result; - - CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, &length); - if (!chars) - return JS_TRUE; - - result = JS_UCBufferIsCompilableUnit(cx, obj, chars, length); - JS_free(cx, chars); - return result; -} - -JS_PUBLIC_API(JSBool) -JS_UCBufferIsCompilableUnit(JSContext *cx, JSObject *obj, - const jschar *chars, size_t length) -{ - JSBool result; - JSExceptionState *exnState; - void *tempMark; - JSTokenStream *ts; - JSErrorReporter older; - - CHECK_REQUEST(cx); - - /* - * Return true on any out-of-memory error, so our caller doesn't try to - * collect more buffered source. - */ - result = JS_TRUE; - exnState = JS_SaveExceptionState(cx); - tempMark = JS_ARENA_MARK(&cx->tempPool); - ts = js_NewTokenStream(cx, chars, length, NULL, 0, NULL); - if (ts) { - older = JS_SetErrorReporter(cx, NULL); - if (!js_ParseTokenStream(cx, obj, ts) && - (ts->flags & TSF_UNEXPECTED_EOF)) { - /* - * We ran into an error. If it was because we ran out of source, - * we return false, so our caller will know to try to collect more - * buffered source. - */ - result = JS_FALSE; - } - - JS_SetErrorReporter(cx, older); - js_CloseTokenStream(cx, ts); - JS_ARENA_RELEASE(&cx->tempPool, tempMark); - } - - JS_RestoreExceptionState(cx, exnState); - return result; -} - -JS_PUBLIC_API(JSScript *) -JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename) -{ - void *mark; - JSTokenStream *ts; - JSScript *script; - - CHECK_REQUEST(cx); - mark = JS_ARENA_MARK(&cx->tempPool); - ts = js_NewFileTokenStream(cx, filename, stdin); - if (!ts) - return NULL; - script = CompileTokenStream(cx, obj, ts, mark, NULL); - LAST_FRAME_CHECKS(cx, script); - return script; -} - -JS_PUBLIC_API(JSScript *) -JS_CompileFileHandle(JSContext *cx, JSObject *obj, const char *filename, - FILE *file) -{ - return JS_CompileFileHandleForPrincipals(cx, obj, filename, file, NULL); -} - -JS_PUBLIC_API(JSScript *) -JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj, - const char *filename, FILE *file, - JSPrincipals *principals) -{ - void *mark; - JSTokenStream *ts; - JSScript *script; - - CHECK_REQUEST(cx); - mark = JS_ARENA_MARK(&cx->tempPool); - ts = js_NewFileTokenStream(cx, NULL, file); - if (!ts) - return NULL; - ts->filename = filename; - /* XXXshaver js_NewFileTokenStream should do this, because it drops */ - if (principals) { - ts->principals = principals; - JSPRINCIPALS_HOLD(cx, ts->principals); - } - script = CompileTokenStream(cx, obj, ts, mark, NULL); - LAST_FRAME_CHECKS(cx, script); - return script; -} - -JS_PUBLIC_API(JSObject *) -JS_NewScriptObject(JSContext *cx, JSScript *script) -{ - JSObject *obj; - - obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); - if (!obj) - return NULL; - - if (script) { - if (!JS_SetPrivate(cx, obj, script)) - return NULL; - script->object = obj; - } - return obj; -} - -JS_PUBLIC_API(JSObject *) -JS_GetScriptObject(JSScript *script) -{ - return script->object; -} - -JS_PUBLIC_API(void) -JS_DestroyScript(JSContext *cx, JSScript *script) -{ - CHECK_REQUEST(cx); - js_DestroyScript(cx, script); -} - -JS_PUBLIC_API(JSFunction *) -JS_CompileFunction(JSContext *cx, JSObject *obj, const char *name, - uintN nargs, const char **argnames, - const char *bytes, size_t length, - const char *filename, uintN lineno) -{ - jschar *chars; - JSFunction *fun; - - CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, &length); - if (!chars) - return NULL; - fun = JS_CompileUCFunction(cx, obj, name, nargs, argnames, chars, length, - filename, lineno); - JS_free(cx, chars); - return fun; -} - -JS_PUBLIC_API(JSFunction *) -JS_CompileFunctionForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, const char *name, - uintN nargs, const char **argnames, - const char *bytes, size_t length, - const char *filename, uintN lineno) -{ - jschar *chars; - JSFunction *fun; - - CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, &length); - if (!chars) - return NULL; - fun = JS_CompileUCFunctionForPrincipals(cx, obj, principals, name, - nargs, argnames, chars, length, - filename, lineno); - JS_free(cx, chars); - return fun; -} - -JS_PUBLIC_API(JSFunction *) -JS_CompileUCFunction(JSContext *cx, JSObject *obj, const char *name, - uintN nargs, const char **argnames, - const jschar *chars, size_t length, - const char *filename, uintN lineno) -{ - CHECK_REQUEST(cx); - return JS_CompileUCFunctionForPrincipals(cx, obj, NULL, name, - nargs, argnames, - chars, length, - filename, lineno); -} - -JS_PUBLIC_API(JSFunction *) -JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, const char *name, - uintN nargs, const char **argnames, - const jschar *chars, size_t length, - const char *filename, uintN lineno) -{ - void *mark; - JSTokenStream *ts; - JSFunction *fun; - JSAtom *funAtom, *argAtom; - uintN i; - - CHECK_REQUEST(cx); - mark = JS_ARENA_MARK(&cx->tempPool); - ts = js_NewTokenStream(cx, chars, length, filename, lineno, principals); - if (!ts) { - fun = NULL; - goto out; - } - if (!name) { - funAtom = NULL; - } else { - funAtom = js_Atomize(cx, name, strlen(name), 0); - if (!funAtom) { - fun = NULL; - goto out; - } - } - fun = js_NewFunction(cx, NULL, NULL, nargs, 0, obj, funAtom); - if (!fun) - goto out; - if (nargs) { - for (i = 0; i < nargs; i++) { - argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i]), 0); - if (!argAtom) - break; - if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(argAtom), - js_GetArgument, js_SetArgument, - SPROP_INVALID_SLOT, - JSPROP_PERMANENT | JSPROP_SHARED, - SPROP_HAS_SHORTID, i)) { - break; - } - } - if (i < nargs) { - fun = NULL; - goto out; - } - } - if (!js_CompileFunctionBody(cx, ts, fun)) { - fun = NULL; - goto out; - } - if (obj && funAtom) { - if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(funAtom), - OBJECT_TO_JSVAL(fun->object), - NULL, NULL, JSPROP_ENUMERATE, NULL)) { - return NULL; - } - } -out: - if (ts) - js_CloseTokenStream(cx, ts); - JS_ARENA_RELEASE(&cx->tempPool, mark); - LAST_FRAME_CHECKS(cx, fun); - return fun; -} - -JS_PUBLIC_API(JSString *) -JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, - uintN indent) -{ - JSPrinter *jp; - JSString *str; - - CHECK_REQUEST(cx); - jp = js_NewPrinter(cx, name, - indent & ~JS_DONT_PRETTY_PRINT, - !(indent & JS_DONT_PRETTY_PRINT)); - if (!jp) - return NULL; - if (js_DecompileScript(jp, script)) - str = js_GetPrinterOutput(jp); - else - str = NULL; - js_DestroyPrinter(jp); - return str; -} - -JS_PUBLIC_API(JSString *) -JS_DecompileFunction(JSContext *cx, JSFunction *fun, uintN indent) -{ - JSPrinter *jp; - JSString *str; - - CHECK_REQUEST(cx); - jp = js_NewPrinter(cx, JS_GetFunctionName(fun), - indent & ~JS_DONT_PRETTY_PRINT, - !(indent & JS_DONT_PRETTY_PRINT)); - if (!jp) - return NULL; - if (js_DecompileFunction(jp, fun)) - str = js_GetPrinterOutput(jp); - else - str = NULL; - js_DestroyPrinter(jp); - return str; -} - -JS_PUBLIC_API(JSString *) -JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent) -{ - JSPrinter *jp; - JSString *str; - - CHECK_REQUEST(cx); - jp = js_NewPrinter(cx, JS_GetFunctionName(fun), - indent & ~JS_DONT_PRETTY_PRINT, - !(indent & JS_DONT_PRETTY_PRINT)); - if (!jp) - return NULL; - if (js_DecompileFunctionBody(jp, fun)) - str = js_GetPrinterOutput(jp); - else - str = NULL; - js_DestroyPrinter(jp); - return str; -} - -JS_PUBLIC_API(JSBool) -JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval) -{ - JSBool ok; - - CHECK_REQUEST(cx); - ok = js_Execute(cx, obj, script, NULL, 0, rval); - LAST_FRAME_CHECKS(cx, ok); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_ExecuteScriptPart(JSContext *cx, JSObject *obj, JSScript *script, - JSExecPart part, jsval *rval) -{ - JSScript tmp; - JSRuntime *rt; - JSBool ok; - - /* Make a temporary copy of the JSScript structure and farble it a bit. */ - tmp = *script; - if (part == JSEXEC_PROLOG) { - tmp.length = PTRDIFF(tmp.main, tmp.code, jsbytecode); - } else { - tmp.length -= PTRDIFF(tmp.main, tmp.code, jsbytecode); - tmp.code = tmp.main; - } - - /* Tell the debugger about our temporary copy of the script structure. */ - rt = cx->runtime; - if (rt->newScriptHook) { - rt->newScriptHook(cx, tmp.filename, tmp.lineno, &tmp, NULL, - rt->newScriptHookData); - } - - /* Execute the farbled struct and tell the debugger to forget about it. */ - ok = JS_ExecuteScript(cx, obj, &tmp, rval); - if (rt->destroyScriptHook) - rt->destroyScriptHook(cx, &tmp, rt->destroyScriptHookData); - return ok; -} - -/* Ancient uintN nbytes is part of API/ABI, so use size_t length local. */ -JS_PUBLIC_API(JSBool) -JS_EvaluateScript(JSContext *cx, JSObject *obj, - const char *bytes, uintN nbytes, - const char *filename, uintN lineno, - jsval *rval) -{ - size_t length = nbytes; - jschar *chars; - JSBool ok; - - CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, &length); - if (!chars) - return JS_FALSE; - ok = JS_EvaluateUCScript(cx, obj, chars, length, filename, lineno, rval); - JS_free(cx, chars); - return ok; -} - -/* Ancient uintN nbytes is part of API/ABI, so use size_t length local. */ -JS_PUBLIC_API(JSBool) -JS_EvaluateScriptForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, - const char *bytes, uintN nbytes, - const char *filename, uintN lineno, - jsval *rval) -{ - size_t length = nbytes; - jschar *chars; - JSBool ok; - - CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, &length); - if (!chars) - return JS_FALSE; - ok = JS_EvaluateUCScriptForPrincipals(cx, obj, principals, chars, length, - filename, lineno, rval); - JS_free(cx, chars); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_EvaluateUCScript(JSContext *cx, JSObject *obj, - const jschar *chars, uintN length, - const char *filename, uintN lineno, - jsval *rval) -{ - CHECK_REQUEST(cx); - return JS_EvaluateUCScriptForPrincipals(cx, obj, NULL, chars, length, - filename, lineno, rval); -} - -JS_PUBLIC_API(JSBool) -JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, - const jschar *chars, uintN length, - const char *filename, uintN lineno, - jsval *rval) -{ - uint32 options; - JSScript *script; - JSBool ok; - - CHECK_REQUEST(cx); - options = cx->options; - cx->options = options | JSOPTION_COMPILE_N_GO; - script = JS_CompileUCScriptForPrincipals(cx, obj, principals, chars, length, - filename, lineno); - cx->options = options; - if (!script) - return JS_FALSE; - ok = js_Execute(cx, obj, script, NULL, 0, rval); - LAST_FRAME_CHECKS(cx, ok); - JS_DestroyScript(cx, script); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc, - jsval *argv, jsval *rval) -{ - JSBool ok; - - CHECK_REQUEST(cx); - ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(fun->object), argc, argv, - rval); - LAST_FRAME_CHECKS(cx, ok); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc, - jsval *argv, jsval *rval) -{ - JSBool ok; - jsval fval; - - CHECK_REQUEST(cx); -#if JS_HAS_XML_SUPPORT - if (OBJECT_IS_XML(cx, obj)) { - JSXMLObjectOps *ops; - JSAtom *atom; - - ops = (JSXMLObjectOps *) obj->map->ops; - atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return JS_FALSE; - obj = ops->getMethod(cx, obj, ATOM_TO_JSID(atom), &fval); - if (!obj) - return JS_FALSE; - } else -#endif - if (!JS_GetProperty(cx, obj, name, &fval)) - return JS_FALSE; - ok = js_InternalCall(cx, obj, fval, argc, argv, rval); - LAST_FRAME_CHECKS(cx, ok); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, - jsval *argv, jsval *rval) -{ - JSBool ok; - - CHECK_REQUEST(cx); - ok = js_InternalCall(cx, obj, fval, argc, argv, rval); - LAST_FRAME_CHECKS(cx, ok); - return ok; -} - -JS_PUBLIC_API(JSBranchCallback) -JS_SetBranchCallback(JSContext *cx, JSBranchCallback cb) -{ - JSBranchCallback oldcb; - - oldcb = cx->branchCallback; - cx->branchCallback = cb; - return oldcb; -} - -JS_PUBLIC_API(JSBool) -JS_IsRunning(JSContext *cx) -{ - return cx->fp != NULL; -} - -JS_PUBLIC_API(JSBool) -JS_IsConstructing(JSContext *cx) -{ - return cx->fp && (cx->fp->flags & JSFRAME_CONSTRUCTING); -} - -JS_FRIEND_API(JSBool) -JS_IsAssigning(JSContext *cx) -{ - JSStackFrame *fp; - jsbytecode *pc; - - for (fp = cx->fp; fp && !fp->script; fp = fp->down) - continue; - if (!fp || !(pc = fp->pc)) - return JS_FALSE; - return (js_CodeSpec[*pc].format & JOF_ASSIGNING) != 0; -} - -JS_PUBLIC_API(void) -JS_SetCallReturnValue2(JSContext *cx, jsval v) -{ -#if JS_HAS_LVALUE_RETURN - cx->rval2 = v; - cx->rval2set = JS_TRUE; -#endif -} - -JS_PUBLIC_API(JSStackFrame *) -JS_SaveFrameChain(JSContext *cx) -{ - JSStackFrame *fp; - - fp = cx->fp; - if (!fp) - return fp; - - JS_ASSERT(!fp->dormantNext); - fp->dormantNext = cx->dormantFrameChain; - cx->dormantFrameChain = fp; - cx->fp = NULL; - return fp; -} - -JS_PUBLIC_API(void) -JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp) -{ - JS_ASSERT(!cx->fp); - if (!fp) - return; - - JS_ASSERT(cx->dormantFrameChain == fp); - cx->fp = fp; - cx->dormantFrameChain = fp->dormantNext; - fp->dormantNext = NULL; -} - -/************************************************************************/ - -JS_PUBLIC_API(JSString *) -JS_NewString(JSContext *cx, char *bytes, size_t nbytes) -{ - size_t length = nbytes; - jschar *chars; - JSString *str; - - CHECK_REQUEST(cx); - - /* Make a UTF-16 vector from the 8-bit char codes in bytes. */ - chars = js_InflateString(cx, bytes, &length); - if (!chars) - return NULL; - - /* Free chars (but not bytes, which caller frees on error) if we fail. */ - str = js_NewString(cx, chars, length, 0); - if (!str) { - JS_free(cx, chars); - return NULL; - } - - /* Hand off bytes to the deflated string cache, if possible. */ - if (!js_SetStringBytes(cx->runtime, str, bytes, nbytes)) - JS_free(cx, bytes); - return str; -} - -JS_PUBLIC_API(JSString *) -JS_NewStringCopyN(JSContext *cx, const char *s, size_t n) -{ - jschar *js; - JSString *str; - - CHECK_REQUEST(cx); - js = js_InflateString(cx, s, &n); - if (!js) - return NULL; - str = js_NewString(cx, js, n, 0); - if (!str) - JS_free(cx, js); - return str; -} - -JS_PUBLIC_API(JSString *) -JS_NewStringCopyZ(JSContext *cx, const char *s) -{ - size_t n; - jschar *js; - JSString *str; - - CHECK_REQUEST(cx); - if (!s) - return cx->runtime->emptyString; - n = strlen(s); - js = js_InflateString(cx, s, &n); - if (!js) - return NULL; - str = js_NewString(cx, js, n, 0); - if (!str) - JS_free(cx, js); - return str; -} - -JS_PUBLIC_API(JSString *) -JS_InternString(JSContext *cx, const char *s) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - atom = js_Atomize(cx, s, strlen(s), ATOM_INTERNED); - if (!atom) - return NULL; - return ATOM_TO_STRING(atom); -} - -JS_PUBLIC_API(JSString *) -JS_NewUCString(JSContext *cx, jschar *chars, size_t length) -{ - CHECK_REQUEST(cx); - return js_NewString(cx, chars, length, 0); -} - -JS_PUBLIC_API(JSString *) -JS_NewUCStringCopyN(JSContext *cx, const jschar *s, size_t n) -{ - CHECK_REQUEST(cx); - return js_NewStringCopyN(cx, s, n, 0); -} - -JS_PUBLIC_API(JSString *) -JS_NewUCStringCopyZ(JSContext *cx, const jschar *s) -{ - CHECK_REQUEST(cx); - if (!s) - return cx->runtime->emptyString; - return js_NewStringCopyZ(cx, s, 0); -} - -JS_PUBLIC_API(JSString *) -JS_InternUCStringN(JSContext *cx, const jschar *s, size_t length) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - atom = js_AtomizeChars(cx, s, length, ATOM_INTERNED); - if (!atom) - return NULL; - return ATOM_TO_STRING(atom); -} - -JS_PUBLIC_API(JSString *) -JS_InternUCString(JSContext *cx, const jschar *s) -{ - return JS_InternUCStringN(cx, s, js_strlen(s)); -} - -JS_PUBLIC_API(char *) -JS_GetStringBytes(JSString *str) -{ - JSRuntime *rt; - char *bytes; - - rt = js_GetGCStringRuntime(str); - bytes = js_GetStringBytes(rt, str); - return bytes ? bytes : ""; -} - -JS_PUBLIC_API(jschar *) -JS_GetStringChars(JSString *str) -{ - /* - * API botch (again, shades of JS_GetStringBytes): we have no cx to pass - * to js_UndependString (called by js_GetStringChars) for out-of-memory - * error reports, so js_UndependString passes NULL and suppresses errors. - * If it fails to convert a dependent string into an independent one, our - * caller will not be guaranteed a \u0000 terminator as a backstop. This - * may break some clients who already misbehave on embedded NULs. - * - * The gain of dependent strings, which cure quadratic and cubic growth - * rate bugs in string concatenation, is worth this slight loss in API - * compatibility. - */ - jschar *chars; - - chars = js_GetStringChars(str); - return chars ? chars : JSSTRING_CHARS(str); -} - -JS_PUBLIC_API(size_t) -JS_GetStringLength(JSString *str) -{ - return JSSTRING_LENGTH(str); -} - -JS_PUBLIC_API(intN) -JS_CompareStrings(JSString *str1, JSString *str2) -{ - return js_CompareStrings(str1, str2); -} - -JS_PUBLIC_API(JSString *) -JS_NewGrowableString(JSContext *cx, jschar *chars, size_t length) -{ - CHECK_REQUEST(cx); - return js_NewString(cx, chars, length, GCF_MUTABLE); -} - -JS_PUBLIC_API(JSString *) -JS_NewDependentString(JSContext *cx, JSString *str, size_t start, - size_t length) -{ - CHECK_REQUEST(cx); - return js_NewDependentString(cx, str, start, length, 0); -} - -JS_PUBLIC_API(JSString *) -JS_ConcatStrings(JSContext *cx, JSString *left, JSString *right) -{ - CHECK_REQUEST(cx); - return js_ConcatStrings(cx, left, right); -} - -JS_PUBLIC_API(const jschar *) -JS_UndependString(JSContext *cx, JSString *str) -{ - CHECK_REQUEST(cx); - return js_UndependString(cx, str); -} - -JS_PUBLIC_API(JSBool) -JS_MakeStringImmutable(JSContext *cx, JSString *str) -{ - CHECK_REQUEST(cx); - if (!js_UndependString(cx, str)) - return JS_FALSE; - - *js_GetGCThingFlags(str) &= ~GCF_MUTABLE; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_EncodeCharacters(JSContext *cx, const jschar *src, size_t srclen, char *dst, - size_t *dstlenp) -{ - return js_DeflateStringToBuffer(cx, src, srclen, dst, dstlenp); -} - -JS_PUBLIC_API(JSBool) -JS_DecodeBytes(JSContext *cx, const char *src, size_t srclen, jschar *dst, - size_t *dstlenp) -{ - return js_InflateStringToBuffer(cx, src, srclen, dst, dstlenp); -} - -JS_PUBLIC_API(JSBool) -JS_CStringsAreUTF8() -{ -#ifdef JS_C_STRINGS_ARE_UTF8 - return JS_TRUE; -#else - return JS_FALSE; -#endif -} - -/************************************************************************/ - -JS_PUBLIC_API(void) -JS_ReportError(JSContext *cx, const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - js_ReportErrorVA(cx, JSREPORT_ERROR, format, ap); - va_end(ap); -} - -JS_PUBLIC_API(void) -JS_ReportErrorNumber(JSContext *cx, JSErrorCallback errorCallback, - void *userRef, const uintN errorNumber, ...) -{ - va_list ap; - - va_start(ap, errorNumber); - js_ReportErrorNumberVA(cx, JSREPORT_ERROR, errorCallback, userRef, - errorNumber, JS_TRUE, ap); - va_end(ap); -} - -JS_PUBLIC_API(void) -JS_ReportErrorNumberUC(JSContext *cx, JSErrorCallback errorCallback, - void *userRef, const uintN errorNumber, ...) -{ - va_list ap; - - va_start(ap, errorNumber); - js_ReportErrorNumberVA(cx, JSREPORT_ERROR, errorCallback, userRef, - errorNumber, JS_FALSE, ap); - va_end(ap); -} - -JS_PUBLIC_API(JSBool) -JS_ReportWarning(JSContext *cx, const char *format, ...) -{ - va_list ap; - JSBool ok; - - va_start(ap, format); - ok = js_ReportErrorVA(cx, JSREPORT_WARNING, format, ap); - va_end(ap); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_ReportErrorFlagsAndNumber(JSContext *cx, uintN flags, - JSErrorCallback errorCallback, void *userRef, - const uintN errorNumber, ...) -{ - va_list ap; - JSBool ok; - - va_start(ap, errorNumber); - ok = js_ReportErrorNumberVA(cx, flags, errorCallback, userRef, - errorNumber, JS_TRUE, ap); - va_end(ap); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_ReportErrorFlagsAndNumberUC(JSContext *cx, uintN flags, - JSErrorCallback errorCallback, void *userRef, - const uintN errorNumber, ...) -{ - va_list ap; - JSBool ok; - - va_start(ap, errorNumber); - ok = js_ReportErrorNumberVA(cx, flags, errorCallback, userRef, - errorNumber, JS_FALSE, ap); - va_end(ap); - return ok; -} - -JS_PUBLIC_API(void) -JS_ReportOutOfMemory(JSContext *cx) -{ - js_ReportOutOfMemory(cx); -} - -JS_PUBLIC_API(JSErrorReporter) -JS_SetErrorReporter(JSContext *cx, JSErrorReporter er) -{ - JSErrorReporter older; - - older = cx->errorReporter; - cx->errorReporter = er; - return older; -} - -/************************************************************************/ - -/* - * Regular Expressions. - */ -JS_PUBLIC_API(JSObject *) -JS_NewRegExpObject(JSContext *cx, char *bytes, size_t length, uintN flags) -{ - jschar *chars; - JSObject *obj; - - CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, &length); - if (!chars) - return NULL; - obj = js_NewRegExpObject(cx, NULL, chars, length, flags); - JS_free(cx, chars); - return obj; -} - -JS_PUBLIC_API(JSObject *) -JS_NewUCRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags) -{ - CHECK_REQUEST(cx); - return js_NewRegExpObject(cx, NULL, chars, length, flags); -} - -JS_PUBLIC_API(void) -JS_SetRegExpInput(JSContext *cx, JSString *input, JSBool multiline) -{ - JSRegExpStatics *res; - - CHECK_REQUEST(cx); - /* No locking required, cx is thread-private and input must be live. */ - res = &cx->regExpStatics; - res->input = input; - res->multiline = multiline; - cx->runtime->gcPoke = JS_TRUE; -} - -JS_PUBLIC_API(void) -JS_ClearRegExpStatics(JSContext *cx) -{ - JSRegExpStatics *res; - - /* No locking required, cx is thread-private and input must be live. */ - res = &cx->regExpStatics; - res->input = NULL; - res->multiline = JS_FALSE; - res->parenCount = 0; - res->lastMatch = res->lastParen = js_EmptySubString; - res->leftContext = res->rightContext = js_EmptySubString; - cx->runtime->gcPoke = JS_TRUE; -} - -JS_PUBLIC_API(void) -JS_ClearRegExpRoots(JSContext *cx) -{ - JSRegExpStatics *res; - - /* No locking required, cx is thread-private and input must be live. */ - res = &cx->regExpStatics; - res->input = NULL; - cx->runtime->gcPoke = JS_TRUE; -} - -/* TODO: compile, execute, get/set other statics... */ - -/************************************************************************/ - -JS_PUBLIC_API(void) -JS_SetLocaleCallbacks(JSContext *cx, JSLocaleCallbacks *callbacks) -{ - cx->localeCallbacks = callbacks; -} - -JS_PUBLIC_API(JSLocaleCallbacks *) -JS_GetLocaleCallbacks(JSContext *cx) -{ - return cx->localeCallbacks; -} - -/************************************************************************/ - -JS_PUBLIC_API(JSBool) -JS_IsExceptionPending(JSContext *cx) -{ - return (JSBool) cx->throwing; -} - -JS_PUBLIC_API(JSBool) -JS_GetPendingException(JSContext *cx, jsval *vp) -{ - CHECK_REQUEST(cx); - if (!cx->throwing) - return JS_FALSE; - *vp = cx->exception; - return JS_TRUE; -} - -JS_PUBLIC_API(void) -JS_SetPendingException(JSContext *cx, jsval v) -{ - CHECK_REQUEST(cx); - cx->throwing = JS_TRUE; - cx->exception = v; -} - -JS_PUBLIC_API(void) -JS_ClearPendingException(JSContext *cx) -{ - cx->throwing = JS_FALSE; - cx->exception = JSVAL_VOID; -} - -JS_PUBLIC_API(JSBool) -JS_ReportPendingException(JSContext *cx) -{ - JSBool save, ok; - - CHECK_REQUEST(cx); - - /* - * Set cx->creatingException to suppress the standard error-to-exception - * conversion done by all {js,JS}_Report* functions except for OOM. The - * cx->creatingException flag was added to suppress recursive divergence - * under js_ErrorToException, but it serves for our purposes here too. - */ - save = cx->creatingException; - cx->creatingException = JS_TRUE; - ok = js_ReportUncaughtException(cx); - cx->creatingException = save; - return ok; -} - -struct JSExceptionState { - JSBool throwing; - jsval exception; -}; - -JS_PUBLIC_API(JSExceptionState *) -JS_SaveExceptionState(JSContext *cx) -{ - JSExceptionState *state; - - CHECK_REQUEST(cx); - state = (JSExceptionState *) JS_malloc(cx, sizeof(JSExceptionState)); - if (state) { - state->throwing = JS_GetPendingException(cx, &state->exception); - if (state->throwing && JSVAL_IS_GCTHING(state->exception)) - js_AddRoot(cx, &state->exception, "JSExceptionState.exception"); - } - return state; -} - -JS_PUBLIC_API(void) -JS_RestoreExceptionState(JSContext *cx, JSExceptionState *state) -{ - CHECK_REQUEST(cx); - if (state) { - if (state->throwing) - JS_SetPendingException(cx, state->exception); - else - JS_ClearPendingException(cx); - JS_DropExceptionState(cx, state); - } -} - -JS_PUBLIC_API(void) -JS_DropExceptionState(JSContext *cx, JSExceptionState *state) -{ - CHECK_REQUEST(cx); - if (state) { - if (state->throwing && JSVAL_IS_GCTHING(state->exception)) - JS_RemoveRoot(cx, &state->exception); - JS_free(cx, state); - } -} - -JS_PUBLIC_API(JSErrorReport *) -JS_ErrorFromException(JSContext *cx, jsval v) -{ - CHECK_REQUEST(cx); - return js_ErrorFromException(cx, v); -} - -JS_PUBLIC_API(JSBool) -JS_ThrowReportedError(JSContext *cx, const char *message, - JSErrorReport *reportp) -{ - return js_ErrorToException(cx, message, reportp); -} - -#ifdef JS_THREADSAFE -/* - * Get the owning thread id of a context. Returns 0 if the context is not - * owned by any thread. - */ -JS_PUBLIC_API(jsword) -JS_GetContextThread(JSContext *cx) -{ - return JS_THREAD_ID(cx); -} - -/* - * Set the current thread as the owning thread of a context. Returns the - * old owning thread id, or -1 if the operation failed. - */ -JS_PUBLIC_API(jsword) -JS_SetContextThread(JSContext *cx) -{ - jsword old = JS_THREAD_ID(cx); - if (!js_SetContextThread(cx)) - return -1; - return old; -} - -JS_PUBLIC_API(jsword) -JS_ClearContextThread(JSContext *cx) -{ - jsword old = JS_THREAD_ID(cx); - js_ClearContextThread(cx); - return old; -} -#endif - -/************************************************************************/ - -#if defined(XP_WIN) -#include -/* - * Initialization routine for the JS DLL... - */ - -/* - * Global Instance handle... - * In Win32 this is the module handle of the DLL. - * - * In Win16 this is the instance handle of the application - * which loaded the DLL. - */ - -#ifdef _WIN32 -BOOL WINAPI DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved) -{ - return TRUE; -} - -#else /* !_WIN32 */ - -int CALLBACK LibMain( HINSTANCE hInst, WORD wDataSeg, - WORD cbHeapSize, LPSTR lpszCmdLine ) -{ - return TRUE; -} - -BOOL CALLBACK __loadds WEP(BOOL fSystemExit) -{ - return TRUE; -} - -#endif /* !_WIN32 */ -#endif /* XP_WIN */ diff --git a/spidermonkey/src/jsapi.h b/spidermonkey/src/jsapi.h deleted file mode 100644 index f341839..0000000 --- a/spidermonkey/src/jsapi.h +++ /dev/null @@ -1,2224 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsapi_h___ -#define jsapi_h___ -/* - * JavaScript API. - */ -#include -#include -#include "jspubtd.h" - -JS_BEGIN_EXTERN_C - -/* - * Type tags stored in the low bits of a jsval. - */ -#define JSVAL_OBJECT 0x0 /* untagged reference to object */ -#define JSVAL_INT 0x1 /* tagged 31-bit integer value */ -#define JSVAL_DOUBLE 0x2 /* tagged reference to double */ -#define JSVAL_STRING 0x4 /* tagged reference to string */ -#define JSVAL_BOOLEAN 0x6 /* tagged boolean value */ - -/* Type tag bitfield length and derived macros. */ -#define JSVAL_TAGBITS 3 -#define JSVAL_TAGMASK JS_BITMASK(JSVAL_TAGBITS) -#define JSVAL_TAG(v) ((v) & JSVAL_TAGMASK) -#define JSVAL_SETTAG(v,t) ((v) | (t)) -#define JSVAL_CLRTAG(v) ((v) & ~(jsval)JSVAL_TAGMASK) -#define JSVAL_ALIGN JS_BIT(JSVAL_TAGBITS) - -/* Predicates for type testing. */ -#define JSVAL_IS_OBJECT(v) (JSVAL_TAG(v) == JSVAL_OBJECT) -#define JSVAL_IS_NUMBER(v) (JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v)) -#define JSVAL_IS_INT(v) (((v) & JSVAL_INT) && (v) != JSVAL_VOID) -#define JSVAL_IS_DOUBLE(v) (JSVAL_TAG(v) == JSVAL_DOUBLE) -#define JSVAL_IS_STRING(v) (JSVAL_TAG(v) == JSVAL_STRING) -#define JSVAL_IS_BOOLEAN(v) (JSVAL_TAG(v) == JSVAL_BOOLEAN) -#define JSVAL_IS_NULL(v) ((v) == JSVAL_NULL) -#define JSVAL_IS_VOID(v) ((v) == JSVAL_VOID) -#define JSVAL_IS_PRIMITIVE(v) (!JSVAL_IS_OBJECT(v) || JSVAL_IS_NULL(v)) - -/* Objects, strings, and doubles are GC'ed. */ -#define JSVAL_IS_GCTHING(v) (!((v) & JSVAL_INT) && !JSVAL_IS_BOOLEAN(v)) -#define JSVAL_TO_GCTHING(v) ((void *)JSVAL_CLRTAG(v)) -#define JSVAL_TO_OBJECT(v) ((JSObject *)JSVAL_TO_GCTHING(v)) -#define JSVAL_TO_DOUBLE(v) ((jsdouble *)JSVAL_TO_GCTHING(v)) -#define JSVAL_TO_STRING(v) ((JSString *)JSVAL_TO_GCTHING(v)) -#define OBJECT_TO_JSVAL(obj) ((jsval)(obj)) -#define DOUBLE_TO_JSVAL(dp) JSVAL_SETTAG((jsval)(dp), JSVAL_DOUBLE) -#define STRING_TO_JSVAL(str) JSVAL_SETTAG((jsval)(str), JSVAL_STRING) - -/* Lock and unlock the GC thing held by a jsval. */ -#define JSVAL_LOCK(cx,v) (JSVAL_IS_GCTHING(v) \ - ? JS_LockGCThing(cx, JSVAL_TO_GCTHING(v)) \ - : JS_TRUE) -#define JSVAL_UNLOCK(cx,v) (JSVAL_IS_GCTHING(v) \ - ? JS_UnlockGCThing(cx, JSVAL_TO_GCTHING(v)) \ - : JS_TRUE) - -/* Domain limits for the jsval int type. */ -#define JSVAL_INT_BITS 31 -#define JSVAL_INT_POW2(n) ((jsval)1 << (n)) -#define JSVAL_INT_MIN ((jsval)1 - JSVAL_INT_POW2(30)) -#define JSVAL_INT_MAX (JSVAL_INT_POW2(30) - 1) -#define INT_FITS_IN_JSVAL(i) ((jsuint)((i)+JSVAL_INT_MAX) <= 2*JSVAL_INT_MAX) -#define JSVAL_TO_INT(v) ((jsint)(v) >> 1) -#define INT_TO_JSVAL(i) (((jsval)(i) << 1) | JSVAL_INT) - -/* Convert between boolean and jsval. */ -#define JSVAL_TO_BOOLEAN(v) ((JSBool)((v) >> JSVAL_TAGBITS)) -#define BOOLEAN_TO_JSVAL(b) JSVAL_SETTAG((jsval)(b) << JSVAL_TAGBITS, \ - JSVAL_BOOLEAN) - -/* A private data pointer (2-byte-aligned) can be stored as an int jsval. */ -#define JSVAL_TO_PRIVATE(v) ((void *)((v) & ~JSVAL_INT)) -#define PRIVATE_TO_JSVAL(p) ((jsval)(p) | JSVAL_INT) - -/* Property attributes, set in JSPropertySpec and passed to API functions. */ -#define JSPROP_ENUMERATE 0x01 /* property is visible to for/in loop */ -#define JSPROP_READONLY 0x02 /* not settable: assignment is no-op */ -#define JSPROP_PERMANENT 0x04 /* property cannot be deleted */ -#define JSPROP_EXPORTED 0x08 /* property is exported from object */ -#define JSPROP_GETTER 0x10 /* property holds getter function */ -#define JSPROP_SETTER 0x20 /* property holds setter function */ -#define JSPROP_SHARED 0x40 /* don't allocate a value slot for this - property; don't copy the property on - set of the same-named property in an - object that delegates to a prototype - containing this property */ -#define JSPROP_INDEX 0x80 /* name is actually (jsint) index */ - -/* Function flags, set in JSFunctionSpec and passed to JS_NewFunction etc. */ -#define JSFUN_LAMBDA 0x08 /* expressed, not declared, function */ -#define JSFUN_GETTER JSPROP_GETTER -#define JSFUN_SETTER JSPROP_SETTER -#define JSFUN_BOUND_METHOD 0x40 /* bind this to fun->object's parent */ -#define JSFUN_HEAVYWEIGHT 0x80 /* activation requires a Call object */ - -#define JSFUN_DISJOINT_FLAGS(f) ((f) & 0x0f) -#define JSFUN_GSFLAGS(f) ((f) & (JSFUN_GETTER | JSFUN_SETTER)) - -#ifdef MOZILLA_1_8_BRANCH - -/* - * Squeeze three more bits into existing 8-bit flags by taking advantage of - * the invalid combination (JSFUN_GETTER | JSFUN_SETTER). - */ -#define JSFUN_GETTER_TEST(f) (JSFUN_GSFLAGS(f) == JSFUN_GETTER) -#define JSFUN_SETTER_TEST(f) (JSFUN_GSFLAGS(f) == JSFUN_SETTER) -#define JSFUN_FLAGS_TEST(f,t) (JSFUN_GSFLAGS(~(f)) ? (f) & (t) : 0) -#define JSFUN_BOUND_METHOD_TEST(f) JSFUN_FLAGS_TEST(f, JSFUN_BOUND_METHOD) -#define JSFUN_HEAVYWEIGHT_TEST(f) JSFUN_FLAGS_TEST(f, JSFUN_HEAVYWEIGHT) - -#define JSFUN_GSFLAG2ATTR(f) (JSFUN_GETTER_TEST(f) ? JSPROP_GETTER : \ - JSFUN_SETTER_TEST(f) ? JSPROP_SETTER : 0) - -#define JSFUN_THISP_FLAGS(f) (JSFUN_GSFLAGS(~(f)) ? 0 : \ - (f) & JSFUN_THISP_PRIMITIVE) -#define JSFUN_THISP_TEST(f,t) ((f) == (t) || (f) == JSFUN_THISP_PRIMITIVE) - -#define JSFUN_THISP_STRING 0x30 /* |this| may be a primitive string */ -#define JSFUN_THISP_NUMBER 0x70 /* |this| may be a primitive number */ -#define JSFUN_THISP_BOOLEAN 0xb0 /* |this| may be a primitive boolean */ -#define JSFUN_THISP_PRIMITIVE 0xf0 /* |this| may be any primitive value */ - -#define JSFUN_FLAGS_MASK 0xf8 /* overlay JSFUN_* attributes */ - -#else - -#define JSFUN_GETTER_TEST(f) ((f) & JSFUN_GETTER) -#define JSFUN_SETTER_TEST(f) ((f) & JSFUN_SETTER) -#define JSFUN_BOUND_METHOD_TEST(f) ((f) & JSFUN_BOUND_METHOD) -#define JSFUN_HEAVYWEIGHT_TEST(f) ((f) & JSFUN_HEAVYWEIGHT) - -#define JSFUN_GSFLAG2ATTR(f) JSFUN_GSFLAGS(f) - -#define JSFUN_THISP_FLAGS(f) (f) -#define JSFUN_THISP_TEST(f,t) ((f) & t) - -#define JSFUN_THISP_STRING 0x0100 /* |this| may be a primitive string */ -#define JSFUN_THISP_NUMBER 0x0200 /* |this| may be a primitive number */ -#define JSFUN_THISP_BOOLEAN 0x0400 /* |this| may be a primitive boolean */ -#define JSFUN_THISP_PRIMITIVE 0x0700 /* |this| may be any primitive value */ - -#define JSFUN_FLAGS_MASK 0x07f8 /* overlay JSFUN_* attributes -- - note that bit #15 is used internally - to flag interpreted functions */ - -#endif - -/* - * Re-use JSFUN_LAMBDA, which applies only to scripted functions, for use in - * JSFunctionSpec arrays that specify generic native prototype methods, i.e., - * methods of a class prototype that are exposed as static methods taking an - * extra leading argument: the generic |this| parameter. - * - * If you set this flag in a JSFunctionSpec struct's flags initializer, then - * that struct must live at least as long as the native static method object - * created due to this flag by JS_DefineFunctions or JS_InitClass. Typically - * JSFunctionSpec structs are allocated in static arrays. - */ -#define JSFUN_GENERIC_NATIVE JSFUN_LAMBDA - -/* - * Well-known JS values. The extern'd variables are initialized when the - * first JSContext is created by JS_NewContext (see below). - */ -#define JSVAL_VOID INT_TO_JSVAL(0 - JSVAL_INT_POW2(30)) -#define JSVAL_NULL OBJECT_TO_JSVAL(0) -#define JSVAL_ZERO INT_TO_JSVAL(0) -#define JSVAL_ONE INT_TO_JSVAL(1) -#define JSVAL_FALSE BOOLEAN_TO_JSVAL(JS_FALSE) -#define JSVAL_TRUE BOOLEAN_TO_JSVAL(JS_TRUE) - -/* - * Microseconds since the epoch, midnight, January 1, 1970 UTC. See the - * comment in jstypes.h regarding safe int64 usage. - */ -extern JS_PUBLIC_API(int64) -JS_Now(); - -/* Don't want to export data, so provide accessors for non-inline jsvals. */ -extern JS_PUBLIC_API(jsval) -JS_GetNaNValue(JSContext *cx); - -extern JS_PUBLIC_API(jsval) -JS_GetNegativeInfinityValue(JSContext *cx); - -extern JS_PUBLIC_API(jsval) -JS_GetPositiveInfinityValue(JSContext *cx); - -extern JS_PUBLIC_API(jsval) -JS_GetEmptyStringValue(JSContext *cx); - -/* - * Format is a string of the following characters (spaces are insignificant), - * specifying the tabulated type conversions: - * - * b JSBool Boolean - * c uint16/jschar ECMA uint16, Unicode char - * i int32 ECMA int32 - * u uint32 ECMA uint32 - * j int32 Rounded int32 (coordinate) - * d jsdouble IEEE double - * I jsdouble Integral IEEE double - * s char * C string - * S JSString * Unicode string, accessed by a JSString pointer - * W jschar * Unicode character vector, 0-terminated (W for wide) - * o JSObject * Object reference - * f JSFunction * Function private - * v jsval Argument value (no conversion) - * * N/A Skip this argument (no vararg) - * / N/A End of required arguments - * - * The variable argument list after format must consist of &b, &c, &s, e.g., - * where those variables have the types given above. For the pointer types - * char *, JSString *, and JSObject *, the pointed-at memory returned belongs - * to the JS runtime, not to the calling native code. The runtime promises - * to keep this memory valid so long as argv refers to allocated stack space - * (so long as the native function is active). - * - * Fewer arguments than format specifies may be passed only if there is a / - * in format after the last required argument specifier and argc is at least - * the number of required arguments. More arguments than format specifies - * may be passed without error; it is up to the caller to deal with trailing - * unconverted arguments. - */ -extern JS_PUBLIC_API(JSBool) -JS_ConvertArguments(JSContext *cx, uintN argc, jsval *argv, const char *format, - ...); - -#ifdef va_start -extern JS_PUBLIC_API(JSBool) -JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv, - const char *format, va_list ap); -#endif - -/* - * Inverse of JS_ConvertArguments: scan format and convert trailing arguments - * into jsvals, GC-rooted if necessary by the JS stack. Return null on error, - * and a pointer to the new argument vector on success. Also return a stack - * mark on success via *markp, in which case the caller must eventually clean - * up by calling JS_PopArguments. - * - * Note that the number of actual arguments supplied is specified exclusively - * by format, so there is no argc parameter. - */ -extern JS_PUBLIC_API(jsval *) -JS_PushArguments(JSContext *cx, void **markp, const char *format, ...); - -#ifdef va_start -extern JS_PUBLIC_API(jsval *) -JS_PushArgumentsVA(JSContext *cx, void **markp, const char *format, va_list ap); -#endif - -extern JS_PUBLIC_API(void) -JS_PopArguments(JSContext *cx, void *mark); - -#ifdef JS_ARGUMENT_FORMATTER_DEFINED - -/* - * Add and remove a format string handler for JS_{Convert,Push}Arguments{,VA}. - * The handler function has this signature (see jspubtd.h): - * - * JSBool MyArgumentFormatter(JSContext *cx, const char *format, - * JSBool fromJS, jsval **vpp, va_list *app); - * - * It should return true on success, and return false after reporting an error - * or detecting an already-reported error. - * - * For a given format string, for example "AA", the formatter is called from - * JS_ConvertArgumentsVA like so: - * - * formatter(cx, "AA...", JS_TRUE, &sp, &ap); - * - * sp points into the arguments array on the JS stack, while ap points into - * the stdarg.h va_list on the C stack. The JS_TRUE passed for fromJS tells - * the formatter to convert zero or more jsvals at sp to zero or more C values - * accessed via pointers-to-values at ap, updating both sp (via *vpp) and ap - * (via *app) to point past the converted arguments and their result pointers - * on the C stack. - * - * When called from JS_PushArgumentsVA, the formatter is invoked thus: - * - * formatter(cx, "AA...", JS_FALSE, &sp, &ap); - * - * where JS_FALSE for fromJS means to wrap the C values at ap according to the - * format specifier and store them at sp, updating ap and sp appropriately. - * - * The "..." after "AA" is the rest of the format string that was passed into - * JS_{Convert,Push}Arguments{,VA}. The actual format trailing substring used - * in each Convert or PushArguments call is passed to the formatter, so that - * one such function may implement several formats, in order to share code. - * - * Remove just forgets about any handler associated with format. Add does not - * copy format, it points at the string storage allocated by the caller, which - * is typically a string constant. If format is in dynamic storage, it is up - * to the caller to keep the string alive until Remove is called. - */ -extern JS_PUBLIC_API(JSBool) -JS_AddArgumentFormatter(JSContext *cx, const char *format, - JSArgumentFormatter formatter); - -extern JS_PUBLIC_API(void) -JS_RemoveArgumentFormatter(JSContext *cx, const char *format); - -#endif /* JS_ARGUMENT_FORMATTER_DEFINED */ - -extern JS_PUBLIC_API(JSBool) -JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_ValueToObject(JSContext *cx, jsval v, JSObject **objp); - -extern JS_PUBLIC_API(JSFunction *) -JS_ValueToFunction(JSContext *cx, jsval v); - -extern JS_PUBLIC_API(JSFunction *) -JS_ValueToConstructor(JSContext *cx, jsval v); - -extern JS_PUBLIC_API(JSString *) -JS_ValueToString(JSContext *cx, jsval v); - -extern JS_PUBLIC_API(JSBool) -JS_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp); - -/* - * Convert a value to a number, then to an int32, according to the ECMA rules - * for ToInt32. - */ -extern JS_PUBLIC_API(JSBool) -JS_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip); - -/* - * Convert a value to a number, then to a uint32, according to the ECMA rules - * for ToUint32. - */ -extern JS_PUBLIC_API(JSBool) -JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip); - -/* - * Convert a value to a number, then to an int32 if it fits by rounding to - * nearest; but failing with an error report if the double is out of range - * or unordered. - */ -extern JS_PUBLIC_API(JSBool) -JS_ValueToInt32(JSContext *cx, jsval v, int32 *ip); - -/* - * ECMA ToUint16, for mapping a jsval to a Unicode point. - */ -extern JS_PUBLIC_API(JSBool) -JS_ValueToUint16(JSContext *cx, jsval v, uint16 *ip); - -extern JS_PUBLIC_API(JSBool) -JS_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp); - -extern JS_PUBLIC_API(JSType) -JS_TypeOfValue(JSContext *cx, jsval v); - -extern JS_PUBLIC_API(const char *) -JS_GetTypeName(JSContext *cx, JSType type); - -/************************************************************************/ - -/* - * Initialization, locking, contexts, and memory allocation. - */ -#define JS_NewRuntime JS_Init -#define JS_DestroyRuntime JS_Finish -#define JS_LockRuntime JS_Lock -#define JS_UnlockRuntime JS_Unlock - -extern JS_PUBLIC_API(JSRuntime *) -JS_NewRuntime(uint32 maxbytes); - -extern JS_PUBLIC_API(void) -JS_DestroyRuntime(JSRuntime *rt); - -extern JS_PUBLIC_API(void) -JS_ShutDown(void); - -JS_PUBLIC_API(void *) -JS_GetRuntimePrivate(JSRuntime *rt); - -JS_PUBLIC_API(void) -JS_SetRuntimePrivate(JSRuntime *rt, void *data); - -#ifdef JS_THREADSAFE - -extern JS_PUBLIC_API(void) -JS_BeginRequest(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_EndRequest(JSContext *cx); - -/* Yield to pending GC operations, regardless of request depth */ -extern JS_PUBLIC_API(void) -JS_YieldRequest(JSContext *cx); - -extern JS_PUBLIC_API(jsrefcount) -JS_SuspendRequest(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth); - -#ifdef __cplusplus -JS_END_EXTERN_C - -class JSAutoRequest { - public: - JSAutoRequest(JSContext *cx) : mContext(cx), mSaveDepth(0) { - JS_BeginRequest(mContext); - } - ~JSAutoRequest() { - JS_EndRequest(mContext); - } - - void suspend() { - mSaveDepth = JS_SuspendRequest(mContext); - } - void resume() { - JS_ResumeRequest(mContext, mSaveDepth); - } - - protected: - JSContext *mContext; - jsrefcount mSaveDepth; - -#if 0 - private: - static void *operator new(size_t) CPP_THROW_NEW { return 0; }; - static void operator delete(void *, size_t) { }; -#endif -}; - -JS_BEGIN_EXTERN_C -#endif - -#endif /* JS_THREADSAFE */ - -extern JS_PUBLIC_API(void) -JS_Lock(JSRuntime *rt); - -extern JS_PUBLIC_API(void) -JS_Unlock(JSRuntime *rt); - -extern JS_PUBLIC_API(JSContextCallback) -JS_SetContextCallback(JSRuntime *rt, JSContextCallback cxCallback); - -extern JS_PUBLIC_API(JSContext *) -JS_NewContext(JSRuntime *rt, size_t stackChunkSize); - -extern JS_PUBLIC_API(void) -JS_DestroyContext(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_DestroyContextNoGC(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_DestroyContextMaybeGC(JSContext *cx); - -extern JS_PUBLIC_API(void *) -JS_GetContextPrivate(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_SetContextPrivate(JSContext *cx, void *data); - -extern JS_PUBLIC_API(JSRuntime *) -JS_GetRuntime(JSContext *cx); - -extern JS_PUBLIC_API(JSContext *) -JS_ContextIterator(JSRuntime *rt, JSContext **iterp); - -extern JS_PUBLIC_API(JSVersion) -JS_GetVersion(JSContext *cx); - -extern JS_PUBLIC_API(JSVersion) -JS_SetVersion(JSContext *cx, JSVersion version); - -extern JS_PUBLIC_API(const char *) -JS_VersionToString(JSVersion version); - -extern JS_PUBLIC_API(JSVersion) -JS_StringToVersion(const char *string); - -/* - * JS options are orthogonal to version, and may be freely composed with one - * another as well as with version. - * - * JSOPTION_VAROBJFIX is recommended -- see the comments associated with the - * prototypes for JS_ExecuteScript, JS_EvaluateScript, etc. - */ -#define JSOPTION_STRICT JS_BIT(0) /* warn on dubious practice */ -#define JSOPTION_WERROR JS_BIT(1) /* convert warning to error */ -#define JSOPTION_VAROBJFIX JS_BIT(2) /* make JS_EvaluateScript use - the last object on its 'obj' - param's scope chain as the - ECMA 'variables object' */ -#define JSOPTION_PRIVATE_IS_NSISUPPORTS \ - JS_BIT(3) /* context private data points - to an nsISupports subclass */ -#define JSOPTION_COMPILE_N_GO JS_BIT(4) /* caller of JS_Compile*Script - promises to execute compiled - script once only; enables - compile-time scope chain - resolution of consts. */ -#define JSOPTION_ATLINE JS_BIT(5) /* //@line number ["filename"] - option supported for the - XUL preprocessor and kindred - beasts. */ -#define JSOPTION_XML JS_BIT(6) /* EMCAScript for XML support: - parse as a token, - not backward compatible with - the comment-hiding hack used - in HTML script tags. */ -#define JSOPTION_NATIVE_BRANCH_CALLBACK \ - JS_BIT(7) /* the branch callback set by - JS_SetBranchCallback may be - called with a null script - parameter, by native code - that loops intensively */ -#define JSOPTION_DONT_REPORT_UNCAUGHT \ - JS_BIT(8) /* When returning from the - outermost API call, prevent - uncaught exceptions from - being converted to error - reports */ - -extern JS_PUBLIC_API(uint32) -JS_GetOptions(JSContext *cx); - -extern JS_PUBLIC_API(uint32) -JS_SetOptions(JSContext *cx, uint32 options); - -extern JS_PUBLIC_API(uint32) -JS_ToggleOptions(JSContext *cx, uint32 options); - -extern JS_PUBLIC_API(const char *) -JS_GetImplementationVersion(void); - -extern JS_PUBLIC_API(JSObject *) -JS_GetGlobalObject(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_SetGlobalObject(JSContext *cx, JSObject *obj); - -/* - * Initialize standard JS class constructors, prototypes, and any top-level - * functions and constants associated with the standard classes (e.g. isNaN - * for Number). - * - * NB: This sets cx's global object to obj if it was null. - */ -extern JS_PUBLIC_API(JSBool) -JS_InitStandardClasses(JSContext *cx, JSObject *obj); - -/* - * Resolve id, which must contain either a string or an int, to a standard - * class name in obj if possible, defining the class's constructor and/or - * prototype and storing true in *resolved. If id does not name a standard - * class or a top-level property induced by initializing a standard class, - * store false in *resolved and just return true. Return false on error, - * as usual for JSBool result-typed API entry points. - * - * This API can be called directly from a global object class's resolve op, - * to define standard classes lazily. The class's enumerate op should call - * JS_EnumerateStandardClasses(cx, obj), to define eagerly during for..in - * loops any classes not yet resolved lazily. - */ -extern JS_PUBLIC_API(JSBool) -JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id, - JSBool *resolved); - -extern JS_PUBLIC_API(JSBool) -JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj); - -/* - * Enumerate any already-resolved standard class ids into ida, or into a new - * JSIdArray if ida is null. Return the augmented array on success, null on - * failure with ida (if it was non-null on entry) destroyed. - */ -extern JS_PUBLIC_API(JSIdArray *) -JS_EnumerateResolvedStandardClasses(JSContext *cx, JSObject *obj, - JSIdArray *ida); - -extern JS_PUBLIC_API(JSBool) -JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, - JSObject **objp); - -extern JS_PUBLIC_API(JSObject *) -JS_GetScopeChain(JSContext *cx); - -extern JS_PUBLIC_API(void *) -JS_malloc(JSContext *cx, size_t nbytes); - -extern JS_PUBLIC_API(void *) -JS_realloc(JSContext *cx, void *p, size_t nbytes); - -extern JS_PUBLIC_API(void) -JS_free(JSContext *cx, void *p); - -extern JS_PUBLIC_API(char *) -JS_strdup(JSContext *cx, const char *s); - -extern JS_PUBLIC_API(jsdouble *) -JS_NewDouble(JSContext *cx, jsdouble d); - -extern JS_PUBLIC_API(JSBool) -JS_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval); - -extern JS_PUBLIC_API(JSBool) -JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval); - -/* - * A JS GC root is a pointer to a JSObject *, JSString *, or jsdouble * that - * itself points into the GC heap (more recently, we support this extension: - * a root may be a pointer to a jsval v for which JSVAL_IS_GCTHING(v) is true). - * - * Therefore, you never pass JSObject *obj to JS_AddRoot(cx, obj). You always - * call JS_AddRoot(cx, &obj), passing obj by reference. And later, before obj - * or the structure it is embedded within goes out of scope or is freed, you - * must call JS_RemoveRoot(cx, &obj). - * - * Also, use JS_AddNamedRoot(cx, &structPtr->memberObj, "structPtr->memberObj") - * in preference to JS_AddRoot(cx, &structPtr->memberObj), in order to identify - * roots by their source callsites. This way, you can find the callsite while - * debugging if you should fail to do JS_RemoveRoot(cx, &structPtr->memberObj) - * before freeing structPtr's memory. - */ -extern JS_PUBLIC_API(JSBool) -JS_AddRoot(JSContext *cx, void *rp); - -#ifdef NAME_ALL_GC_ROOTS -#define JS_DEFINE_TO_TOKEN(def) #def -#define JS_DEFINE_TO_STRING(def) JS_DEFINE_TO_TOKEN(def) -#define JS_AddRoot(cx,rp) JS_AddNamedRoot((cx), (rp), (__FILE__ ":" JS_TOKEN_TO_STRING(__LINE__)) -#endif - -extern JS_PUBLIC_API(JSBool) -JS_AddNamedRoot(JSContext *cx, void *rp, const char *name); - -extern JS_PUBLIC_API(JSBool) -JS_AddNamedRootRT(JSRuntime *rt, void *rp, const char *name); - -extern JS_PUBLIC_API(JSBool) -JS_RemoveRoot(JSContext *cx, void *rp); - -extern JS_PUBLIC_API(JSBool) -JS_RemoveRootRT(JSRuntime *rt, void *rp); - -/* - * The last GC thing of each type (object, string, double, external string - * types) created on a given context is kept alive until another thing of the - * same type is created, using a newborn root in the context. These newborn - * roots help native code protect newly-created GC-things from GC invocations - * activated before those things can be rooted using local or global roots. - * - * However, the newborn roots can also entrain great gobs of garbage, so the - * JS_GC entry point clears them for the context on which GC is being forced. - * Embeddings may need to do likewise for all contexts. - * - * See the scoped local root API immediately below for a better way to manage - * newborns in cases where native hooks (functions, getters, setters, etc.) - * create many GC-things, potentially without connecting them to predefined - * local roots such as *rval or argv[i] in an active native function. Using - * JS_EnterLocalRootScope disables updating of the context's per-gc-thing-type - * newborn roots, until control flow unwinds and leaves the outermost nesting - * local root scope. - */ -extern JS_PUBLIC_API(void) -JS_ClearNewbornRoots(JSContext *cx); - -/* - * Scoped local root management allows native functions, getter/setters, etc. - * to avoid worrying about the newborn root pigeon-holes, overloading local - * roots allocated in argv and *rval, or ending up having to call JS_Add*Root - * and JS_RemoveRoot to manage global roots temporarily. - * - * Instead, calling JS_EnterLocalRootScope and JS_LeaveLocalRootScope around - * the body of the native hook causes the engine to allocate a local root for - * each newborn created in between the two API calls, using a local root stack - * associated with cx. For example: - * - * JSBool - * my_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) - * { - * JSBool ok; - * - * if (!JS_EnterLocalRootScope(cx)) - * return JS_FALSE; - * ok = my_GetPropertyBody(cx, obj, id, vp); - * JS_LeaveLocalRootScope(cx); - * return ok; - * } - * - * NB: JS_LeaveLocalRootScope must be called once for every prior successful - * call to JS_EnterLocalRootScope. If JS_EnterLocalRootScope fails, you must - * not make the matching JS_LeaveLocalRootScope call. - * - * JS_LeaveLocalRootScopeWithResult(cx, rval) is an alternative way to leave - * a local root scope that protects a result or return value, by effectively - * pushing it in the caller's local root scope. - * - * In case a native hook allocates many objects or other GC-things, but the - * native protects some of those GC-things by storing them as property values - * in an object that is itself protected, the hook can call JS_ForgetLocalRoot - * to free the local root automatically pushed for the now-protected GC-thing. - * - * JS_ForgetLocalRoot works on any GC-thing allocated in the current local - * root scope, but it's more time-efficient when called on references to more - * recently created GC-things. Calling it successively on other than the most - * recently allocated GC-thing will tend to average the time inefficiency, and - * may risk O(n^2) growth rate, but in any event, you shouldn't allocate too - * many local roots if you can root as you go (build a tree of objects from - * the top down, forgetting each latest-allocated GC-thing immediately upon - * linking it to its parent). - */ -extern JS_PUBLIC_API(JSBool) -JS_EnterLocalRootScope(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_LeaveLocalRootScope(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval); - -extern JS_PUBLIC_API(void) -JS_ForgetLocalRoot(JSContext *cx, void *thing); - -#ifdef __cplusplus -JS_END_EXTERN_C - -class JSAutoLocalRootScope { - public: - JSAutoLocalRootScope(JSContext *cx) : mContext(cx) { - JS_EnterLocalRootScope(mContext); - } - ~JSAutoLocalRootScope() { - JS_LeaveLocalRootScope(mContext); - } - - void forget(void *thing) { - JS_ForgetLocalRoot(mContext, thing); - } - - protected: - JSContext *mContext; - -#if 0 - private: - static void *operator new(size_t) CPP_THROW_NEW { return 0; }; - static void operator delete(void *, size_t) { }; -#endif -}; - -JS_BEGIN_EXTERN_C -#endif - -#ifdef DEBUG -extern JS_PUBLIC_API(void) -JS_DumpNamedRoots(JSRuntime *rt, - void (*dump)(const char *name, void *rp, void *data), - void *data); -#endif - -/* - * Call JS_MapGCRoots to map the GC's roots table using map(rp, name, data). - * The root is pointed at by rp; if the root is unnamed, name is null; data is - * supplied from the third parameter to JS_MapGCRoots. - * - * The map function should return JS_MAP_GCROOT_REMOVE to cause the currently - * enumerated root to be removed. To stop enumeration, set JS_MAP_GCROOT_STOP - * in the return value. To keep on mapping, return JS_MAP_GCROOT_NEXT. These - * constants are flags; you can OR them together. - * - * This function acquires and releases rt's GC lock around the mapping of the - * roots table, so the map function should run to completion in as few cycles - * as possible. Of course, map cannot call JS_GC, JS_MaybeGC, JS_BeginRequest, - * or any JS API entry point that acquires locks, without double-tripping or - * deadlocking on the GC lock. - * - * JS_MapGCRoots returns the count of roots that were successfully mapped. - */ -#define JS_MAP_GCROOT_NEXT 0 /* continue mapping entries */ -#define JS_MAP_GCROOT_STOP 1 /* stop mapping entries */ -#define JS_MAP_GCROOT_REMOVE 2 /* remove and free the current entry */ - -typedef intN -(* JS_DLL_CALLBACK JSGCRootMapFun)(void *rp, const char *name, void *data); - -extern JS_PUBLIC_API(uint32) -JS_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data); - -extern JS_PUBLIC_API(JSBool) -JS_LockGCThing(JSContext *cx, void *thing); - -extern JS_PUBLIC_API(JSBool) -JS_LockGCThingRT(JSRuntime *rt, void *thing); - -extern JS_PUBLIC_API(JSBool) -JS_UnlockGCThing(JSContext *cx, void *thing); - -extern JS_PUBLIC_API(JSBool) -JS_UnlockGCThingRT(JSRuntime *rt, void *thing); - -/* - * For implementors of JSObjectOps.mark, to mark a GC-thing reachable via a - * property or other strong ref identified for debugging purposes by name. - * The name argument's storage needs to live only as long as the call to - * this routine. - * - * The final arg is used by GC_MARK_DEBUG code to build a ref path through - * the GC's live thing graph. Implementors of JSObjectOps.mark should pass - * its final arg through to this function when marking all GC-things that are - * directly reachable from the object being marked. - * - * See the JSMarkOp typedef in jspubtd.h, and the JSObjectOps struct below. - */ -extern JS_PUBLIC_API(void) -JS_MarkGCThing(JSContext *cx, void *thing, const char *name, void *arg); - -extern JS_PUBLIC_API(void) -JS_GC(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_MaybeGC(JSContext *cx); - -extern JS_PUBLIC_API(JSGCCallback) -JS_SetGCCallback(JSContext *cx, JSGCCallback cb); - -extern JS_PUBLIC_API(JSGCCallback) -JS_SetGCCallbackRT(JSRuntime *rt, JSGCCallback cb); - -extern JS_PUBLIC_API(JSBool) -JS_IsAboutToBeFinalized(JSContext *cx, void *thing); - -typedef enum JSGCParamKey { - JSGC_MAX_BYTES = 0, /* maximum nominal heap before last ditch GC */ - JSGC_MAX_MALLOC_BYTES = 1 /* # of JS_malloc bytes before last ditch GC */ -} JSGCParamKey; - -extern JS_PUBLIC_API(void) -JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value); - -/* - * Add a finalizer for external strings created by JS_NewExternalString (see - * below) using a type-code returned from this function, and that understands - * how to free or release the memory pointed at by JS_GetStringChars(str). - * - * Return a nonnegative type index if there is room for finalizer in the - * global GC finalizers table, else return -1. If the engine is compiled - * JS_THREADSAFE and used in a multi-threaded environment, this function must - * be invoked on the primordial thread only, at startup -- or else the entire - * program must single-thread itself while loading a module that calls this - * function. - */ -extern JS_PUBLIC_API(intN) -JS_AddExternalStringFinalizer(JSStringFinalizeOp finalizer); - -/* - * Remove finalizer from the global GC finalizers table, returning its type - * code if found, -1 if not found. - * - * As with JS_AddExternalStringFinalizer, there is a threading restriction - * if you compile the engine JS_THREADSAFE: this function may be called for a - * given finalizer pointer on only one thread; different threads may call to - * remove distinct finalizers safely. - * - * You must ensure that all strings with finalizer's type have been collected - * before calling this function. Otherwise, string data will be leaked by the - * GC, for want of a finalizer to call. - */ -extern JS_PUBLIC_API(intN) -JS_RemoveExternalStringFinalizer(JSStringFinalizeOp finalizer); - -/* - * Create a new JSString whose chars member refers to external memory, i.e., - * memory requiring special, type-specific finalization. The type code must - * be a nonnegative return value from JS_AddExternalStringFinalizer. - */ -extern JS_PUBLIC_API(JSString *) -JS_NewExternalString(JSContext *cx, jschar *chars, size_t length, intN type); - -/* - * Returns the external-string finalizer index for this string, or -1 if it is - * an "internal" (native to JS engine) string. - */ -extern JS_PUBLIC_API(intN) -JS_GetExternalStringGCType(JSRuntime *rt, JSString *str); - -/* - * Sets maximum (if stack grows upward) or minimum (downward) legal stack byte - * address in limitAddr for the thread or process stack used by cx. To disable - * stack size checking, pass 0 for limitAddr. - */ -extern JS_PUBLIC_API(void) -JS_SetThreadStackLimit(JSContext *cx, jsuword limitAddr); - -/************************************************************************/ - -/* - * Classes, objects, and properties. - */ - -/* For detailed comments on the function pointer types, see jspubtd.h. */ -struct JSClass { - const char *name; - uint32 flags; - - /* Mandatory non-null function pointer members. */ - JSPropertyOp addProperty; - JSPropertyOp delProperty; - JSPropertyOp getProperty; - JSPropertyOp setProperty; - JSEnumerateOp enumerate; - JSResolveOp resolve; - JSConvertOp convert; - JSFinalizeOp finalize; - - /* Optionally non-null members start here. */ - JSGetObjectOps getObjectOps; - JSCheckAccessOp checkAccess; - JSNative call; - JSNative construct; - JSXDRObjectOp xdrObject; - JSHasInstanceOp hasInstance; - JSMarkOp mark; - JSReserveSlotsOp reserveSlots; -}; - -struct JSExtendedClass { - JSClass base; - JSEqualityOp equality; - JSObjectOp outerObject; - JSObjectOp innerObject; - void (*reserved0)(); - void (*reserved1)(); - void (*reserved2)(); - void (*reserved3)(); - void (*reserved4)(); -}; - -#define JSCLASS_HAS_PRIVATE (1<<0) /* objects have private slot */ -#define JSCLASS_NEW_ENUMERATE (1<<1) /* has JSNewEnumerateOp hook */ -#define JSCLASS_NEW_RESOLVE (1<<2) /* has JSNewResolveOp hook */ -#define JSCLASS_PRIVATE_IS_NSISUPPORTS (1<<3) /* private is (nsISupports *) */ -#define JSCLASS_SHARE_ALL_PROPERTIES (1<<4) /* all properties are SHARED */ -#define JSCLASS_NEW_RESOLVE_GETS_START (1<<5) /* JSNewResolveOp gets starting - object in prototype chain - passed in via *objp in/out - parameter */ -#define JSCLASS_CONSTRUCT_PROTOTYPE (1<<6) /* call constructor on class - prototype */ -#define JSCLASS_DOCUMENT_OBSERVER (1<<7) /* DOM document observer */ - -/* - * To reserve slots fetched and stored via JS_Get/SetReservedSlot, bitwise-or - * JSCLASS_HAS_RESERVED_SLOTS(n) into the initializer for JSClass.flags, where - * n is a constant in [1, 255]. Reserved slots are indexed from 0 to n-1. - */ -#define JSCLASS_RESERVED_SLOTS_SHIFT 8 /* room for 8 flags below */ -#define JSCLASS_RESERVED_SLOTS_WIDTH 8 /* and 16 above this field */ -#define JSCLASS_RESERVED_SLOTS_MASK JS_BITMASK(JSCLASS_RESERVED_SLOTS_WIDTH) -#define JSCLASS_HAS_RESERVED_SLOTS(n) (((n) & JSCLASS_RESERVED_SLOTS_MASK) \ - << JSCLASS_RESERVED_SLOTS_SHIFT) -#define JSCLASS_RESERVED_SLOTS(clasp) (((clasp)->flags \ - >> JSCLASS_RESERVED_SLOTS_SHIFT) \ - & JSCLASS_RESERVED_SLOTS_MASK) - -#define JSCLASS_HIGH_FLAGS_SHIFT (JSCLASS_RESERVED_SLOTS_SHIFT + \ - JSCLASS_RESERVED_SLOTS_WIDTH) - -/* True if JSClass is really a JSExtendedClass. */ -#define JSCLASS_IS_EXTENDED (1<<(JSCLASS_HIGH_FLAGS_SHIFT+0)) -#define JSCLASS_IS_ANONYMOUS (1<<(JSCLASS_HIGH_FLAGS_SHIFT+1)) -#define JSCLASS_IS_GLOBAL (1<<(JSCLASS_HIGH_FLAGS_SHIFT+2)) - -/* - * ECMA-262 requires that most constructors used internally create objects - * with "the original Foo.prototype value" as their [[Prototype]] (__proto__) - * member initial value. The "original ... value" verbiage is there because - * in ECMA-262, global properties naming class objects are read/write and - * deleteable, for the most part. - * - * Implementing this efficiently requires that global objects have classes - * with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS won't break - * anything except the ECMA-262 "original prototype value" behavior, which was - * broken for years in SpiderMonkey. In other words, without these flags you - * get backward compatibility. - */ -#define JSCLASS_GLOBAL_FLAGS \ - (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSProto_LIMIT)) - -/* Fast access to the original value of each standard class's prototype. */ -#define JSCLASS_CACHED_PROTO_SHIFT (JSCLASS_HIGH_FLAGS_SHIFT + 8) -#define JSCLASS_CACHED_PROTO_WIDTH 8 -#define JSCLASS_CACHED_PROTO_MASK JS_BITMASK(JSCLASS_CACHED_PROTO_WIDTH) -#define JSCLASS_HAS_CACHED_PROTO(key) ((key) << JSCLASS_CACHED_PROTO_SHIFT) -#define JSCLASS_CACHED_PROTO_KEY(clasp) (((clasp)->flags \ - >> JSCLASS_CACHED_PROTO_SHIFT) \ - & JSCLASS_CACHED_PROTO_MASK) - -/* Initializer for unused members of statically initialized JSClass structs. */ -#define JSCLASS_NO_OPTIONAL_MEMBERS 0,0,0,0,0,0,0,0 -#define JSCLASS_NO_RESERVED_MEMBERS 0,0,0,0,0 - -/* For detailed comments on these function pointer types, see jspubtd.h. */ -struct JSObjectOps { - /* Mandatory non-null function pointer members. */ - JSNewObjectMapOp newObjectMap; - JSObjectMapOp destroyObjectMap; - JSLookupPropOp lookupProperty; - JSDefinePropOp defineProperty; - JSPropertyIdOp getProperty; - JSPropertyIdOp setProperty; - JSAttributesOp getAttributes; - JSAttributesOp setAttributes; - JSPropertyIdOp deleteProperty; - JSConvertOp defaultValue; - JSNewEnumerateOp enumerate; - JSCheckAccessIdOp checkAccess; - - /* Optionally non-null members start here. */ - JSObjectOp thisObject; - JSPropertyRefOp dropProperty; - JSNative call; - JSNative construct; - JSXDRObjectOp xdrObject; - JSHasInstanceOp hasInstance; - JSSetObjectSlotOp setProto; - JSSetObjectSlotOp setParent; - JSMarkOp mark; - JSFinalizeOp clear; - JSGetRequiredSlotOp getRequiredSlot; - JSSetRequiredSlotOp setRequiredSlot; -}; - -struct JSXMLObjectOps { - JSObjectOps base; - JSGetMethodOp getMethod; - JSSetMethodOp setMethod; - JSEnumerateValuesOp enumerateValues; - JSEqualityOp equality; - JSConcatenateOp concatenate; -}; - -/* - * Classes that expose JSObjectOps via a non-null getObjectOps class hook may - * derive a property structure from this struct, return a pointer to it from - * lookupProperty and defineProperty, and use the pointer to avoid rehashing - * in getAttributes and setAttributes. - * - * The jsid type contains either an int jsval (see JSVAL_IS_INT above), or an - * internal pointer that is opaque to users of this API, but which users may - * convert from and to a jsval using JS_ValueToId and JS_IdToValue. - */ -struct JSProperty { - jsid id; -}; - -struct JSIdArray { - jsint length; - jsid vector[1]; /* actually, length jsid words */ -}; - -extern JS_PUBLIC_API(void) -JS_DestroyIdArray(JSContext *cx, JSIdArray *ida); - -extern JS_PUBLIC_API(JSBool) -JS_ValueToId(JSContext *cx, jsval v, jsid *idp); - -extern JS_PUBLIC_API(JSBool) -JS_IdToValue(JSContext *cx, jsid id, jsval *vp); - -/* - * The magic XML namespace id is int-tagged, but not a valid integer jsval. - * Global object classes in embeddings that enable JS_HAS_XML_SUPPORT (E4X) - * should handle this id specially before converting id via JSVAL_TO_INT. - */ -#define JS_DEFAULT_XML_NAMESPACE_ID ((jsid) JSVAL_VOID) - -/* - * JSNewResolveOp flag bits. - */ -#define JSRESOLVE_QUALIFIED 0x01 /* resolve a qualified property id */ -#define JSRESOLVE_ASSIGNING 0x02 /* resolve on the left of assignment */ -#define JSRESOLVE_DETECTING 0x04 /* 'if (o.p)...' or '(o.p) ?...:...' */ -#define JSRESOLVE_DECLARING 0x08 /* var, const, or function prolog op */ -#define JSRESOLVE_CLASSNAME 0x10 /* class name used when constructing */ - -extern JS_PUBLIC_API(JSBool) -JS_PropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_EnumerateStub(JSContext *cx, JSObject *obj); - -extern JS_PUBLIC_API(JSBool) -JS_ResolveStub(JSContext *cx, JSObject *obj, jsval id); - -extern JS_PUBLIC_API(JSBool) -JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp); - -extern JS_PUBLIC_API(void) -JS_FinalizeStub(JSContext *cx, JSObject *obj); - -struct JSConstDoubleSpec { - jsdouble dval; - const char *name; - uint8 flags; - uint8 spare[3]; -}; - -/* - * To define an array element rather than a named property member, cast the - * element's index to (const char *) and initialize name with it, and set the - * JSPROP_INDEX bit in flags. - */ -struct JSPropertySpec { - const char *name; - int8 tinyid; - uint8 flags; - JSPropertyOp getter; - JSPropertyOp setter; -}; - -struct JSFunctionSpec { - const char *name; - JSNative call; -#ifdef MOZILLA_1_8_BRANCH - uint8 nargs; - uint8 flags; - uint16 extra; -#else - uint16 nargs; - uint16 flags; - uint32 extra; /* extra & 0xFFFF: - number of arg slots for local GC roots - extra >> 16: - reserved, must be zero */ -#endif -}; - -extern JS_PUBLIC_API(JSObject *) -JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, - JSClass *clasp, JSNative constructor, uintN nargs, - JSPropertySpec *ps, JSFunctionSpec *fs, - JSPropertySpec *static_ps, JSFunctionSpec *static_fs); - -#ifdef JS_THREADSAFE -extern JS_PUBLIC_API(JSClass *) -JS_GetClass(JSContext *cx, JSObject *obj); - -#define JS_GET_CLASS(cx,obj) JS_GetClass(cx, obj) -#else -extern JS_PUBLIC_API(JSClass *) -JS_GetClass(JSObject *obj); - -#define JS_GET_CLASS(cx,obj) JS_GetClass(obj) -#endif - -extern JS_PUBLIC_API(JSBool) -JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv); - -extern JS_PUBLIC_API(JSBool) -JS_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); - -extern JS_PUBLIC_API(void *) -JS_GetPrivate(JSContext *cx, JSObject *obj); - -extern JS_PUBLIC_API(JSBool) -JS_SetPrivate(JSContext *cx, JSObject *obj, void *data); - -extern JS_PUBLIC_API(void *) -JS_GetInstancePrivate(JSContext *cx, JSObject *obj, JSClass *clasp, - jsval *argv); - -extern JS_PUBLIC_API(JSObject *) -JS_GetPrototype(JSContext *cx, JSObject *obj); - -extern JS_PUBLIC_API(JSBool) -JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto); - -extern JS_PUBLIC_API(JSObject *) -JS_GetParent(JSContext *cx, JSObject *obj); - -extern JS_PUBLIC_API(JSBool) -JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent); - -extern JS_PUBLIC_API(JSObject *) -JS_GetConstructor(JSContext *cx, JSObject *proto); - -/* - * Get a unique identifier for obj, good for the lifetime of obj (even if it - * is moved by a copying GC). Return false on failure (likely out of memory), - * and true with *idp containing the unique id on success. - */ -extern JS_PUBLIC_API(JSBool) -JS_GetObjectId(JSContext *cx, JSObject *obj, jsid *idp); - -extern JS_PUBLIC_API(JSObject *) -JS_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent); - -extern JS_PUBLIC_API(JSBool) -JS_SealObject(JSContext *cx, JSObject *obj, JSBool deep); - -extern JS_PUBLIC_API(JSObject *) -JS_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, - JSObject *parent); - -extern JS_PUBLIC_API(JSObject *) -JS_ConstructObjectWithArguments(JSContext *cx, JSClass *clasp, JSObject *proto, - JSObject *parent, uintN argc, jsval *argv); - -extern JS_PUBLIC_API(JSObject *) -JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *clasp, - JSObject *proto, uintN attrs); - -extern JS_PUBLIC_API(JSBool) -JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds); - -extern JS_PUBLIC_API(JSBool) -JS_DefineProperties(JSContext *cx, JSObject *obj, JSPropertySpec *ps); - -extern JS_PUBLIC_API(JSBool) -JS_DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs); - -/* - * Determine the attributes (JSPROP_* flags) of a property on a given object. - * - * If the object does not have a property by that name, *foundp will be - * JS_FALSE and the value of *attrsp is undefined. - */ -extern JS_PUBLIC_API(JSBool) -JS_GetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, - uintN *attrsp, JSBool *foundp); - -/* - * The same, but if the property is native, return its getter and setter via - * *getterp and *setterp, respectively (and only if the out parameter pointer - * is not null). - */ -extern JS_PUBLIC_API(JSBool) -JS_GetPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, - const char *name, - uintN *attrsp, JSBool *foundp, - JSPropertyOp *getterp, - JSPropertyOp *setterp); - -/* - * Set the attributes of a property on a given object. - * - * If the object does not have a property by that name, *foundp will be - * JS_FALSE and nothing will be altered. - */ -extern JS_PUBLIC_API(JSBool) -JS_SetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, - uintN attrs, JSBool *foundp); - -extern JS_PUBLIC_API(JSBool) -JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name, - int8 tinyid, jsval value, - JSPropertyOp getter, JSPropertyOp setter, - uintN attrs); - -extern JS_PUBLIC_API(JSBool) -JS_AliasProperty(JSContext *cx, JSObject *obj, const char *name, - const char *alias); - -extern JS_PUBLIC_API(JSBool) -JS_HasProperty(JSContext *cx, JSObject *obj, const char *name, JSBool *foundp); - -extern JS_PUBLIC_API(JSBool) -JS_LookupProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, const char *name, - uintN flags, jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_GetMethodById(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_GetMethod(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, - jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_SetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_DeleteProperty(JSContext *cx, JSObject *obj, const char *name); - -extern JS_PUBLIC_API(JSBool) -JS_DeleteProperty2(JSContext *cx, JSObject *obj, const char *name, - jsval *rval); - -extern JS_PUBLIC_API(JSBool) -JS_DefineUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, jsval value, - JSPropertyOp getter, JSPropertyOp setter, - uintN attrs); - -/* - * Determine the attributes (JSPROP_* flags) of a property on a given object. - * - * If the object does not have a property by that name, *foundp will be - * JS_FALSE and the value of *attrsp is undefined. - */ -extern JS_PUBLIC_API(JSBool) -JS_GetUCPropertyAttributes(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - uintN *attrsp, JSBool *foundp); - -/* - * The same, but if the property is native, return its getter and setter via - * *getterp and *setterp, respectively (and only if the out parameter pointer - * is not null). - */ -extern JS_PUBLIC_API(JSBool) -JS_GetUCPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - uintN *attrsp, JSBool *foundp, - JSPropertyOp *getterp, - JSPropertyOp *setterp); - -/* - * Set the attributes of a property on a given object. - * - * If the object does not have a property by that name, *foundp will be - * JS_FALSE and nothing will be altered. - */ -extern JS_PUBLIC_API(JSBool) -JS_SetUCPropertyAttributes(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - uintN attrs, JSBool *foundp); - - -extern JS_PUBLIC_API(JSBool) -JS_DefineUCPropertyWithTinyId(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - int8 tinyid, jsval value, - JSPropertyOp getter, JSPropertyOp setter, - uintN attrs); - -extern JS_PUBLIC_API(JSBool) -JS_HasUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - JSBool *vp); - -extern JS_PUBLIC_API(JSBool) -JS_LookupUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_GetUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_SetUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_DeleteUCProperty2(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - jsval *rval); - -extern JS_PUBLIC_API(JSObject *) -JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector); - -extern JS_PUBLIC_API(JSBool) -JS_IsArrayObject(JSContext *cx, JSObject *obj); - -extern JS_PUBLIC_API(JSBool) -JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp); - -extern JS_PUBLIC_API(JSBool) -JS_SetArrayLength(JSContext *cx, JSObject *obj, jsuint length); - -extern JS_PUBLIC_API(JSBool) -JS_HasArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp); - -extern JS_PUBLIC_API(JSBool) -JS_DefineElement(JSContext *cx, JSObject *obj, jsint index, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs); - -extern JS_PUBLIC_API(JSBool) -JS_AliasElement(JSContext *cx, JSObject *obj, const char *name, jsint alias); - -extern JS_PUBLIC_API(JSBool) -JS_HasElement(JSContext *cx, JSObject *obj, jsint index, JSBool *foundp); - -extern JS_PUBLIC_API(JSBool) -JS_LookupElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_GetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_SetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_DeleteElement(JSContext *cx, JSObject *obj, jsint index); - -extern JS_PUBLIC_API(JSBool) -JS_DeleteElement2(JSContext *cx, JSObject *obj, jsint index, jsval *rval); - -extern JS_PUBLIC_API(void) -JS_ClearScope(JSContext *cx, JSObject *obj); - -extern JS_PUBLIC_API(JSIdArray *) -JS_Enumerate(JSContext *cx, JSObject *obj); - -/* - * Create an object to iterate over enumerable properties of obj, in arbitrary - * property definition order. NB: This differs from longstanding for..in loop - * order, which uses order of property definition in obj. - */ -extern JS_PUBLIC_API(JSObject *) -JS_NewPropertyIterator(JSContext *cx, JSObject *obj); - -/* - * Return true on success with *idp containing the id of the next enumerable - * property to visit using iterobj, or JSVAL_VOID if there is no such property - * left to visit. Return false on error. - */ -extern JS_PUBLIC_API(JSBool) -JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp); - -extern JS_PUBLIC_API(JSBool) -JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, - jsval *vp, uintN *attrsp); - -extern JS_PUBLIC_API(JSCheckAccessOp) -JS_SetCheckObjectAccessCallback(JSRuntime *rt, JSCheckAccessOp acb); - -extern JS_PUBLIC_API(JSBool) -JS_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v); - -/************************************************************************/ - -/* - * Security protocol. - */ -struct JSPrincipals { - char *codebase; - - /* XXX unspecified and unused by Mozilla code -- can we remove these? */ - void * (* JS_DLL_CALLBACK getPrincipalArray)(JSContext *cx, JSPrincipals *); - JSBool (* JS_DLL_CALLBACK globalPrivilegesEnabled)(JSContext *cx, JSPrincipals *); - - /* Don't call "destroy"; use reference counting macros below. */ - jsrefcount refcount; - - void (* JS_DLL_CALLBACK destroy)(JSContext *cx, JSPrincipals *); - JSBool (* JS_DLL_CALLBACK subsume)(JSPrincipals *, JSPrincipals *); -}; - -#ifdef JS_THREADSAFE -#define JSPRINCIPALS_HOLD(cx, principals) JS_HoldPrincipals(cx,principals) -#define JSPRINCIPALS_DROP(cx, principals) JS_DropPrincipals(cx,principals) - -extern JS_PUBLIC_API(jsrefcount) -JS_HoldPrincipals(JSContext *cx, JSPrincipals *principals); - -extern JS_PUBLIC_API(jsrefcount) -JS_DropPrincipals(JSContext *cx, JSPrincipals *principals); - -#else -#define JSPRINCIPALS_HOLD(cx, principals) (++(principals)->refcount) -#define JSPRINCIPALS_DROP(cx, principals) \ - ((--(principals)->refcount == 0) \ - ? ((*(principals)->destroy)((cx), (principals)), 0) \ - : (principals)->refcount) -#endif - -extern JS_PUBLIC_API(JSPrincipalsTranscoder) -JS_SetPrincipalsTranscoder(JSRuntime *rt, JSPrincipalsTranscoder px); - -extern JS_PUBLIC_API(JSObjectPrincipalsFinder) -JS_SetObjectPrincipalsFinder(JSRuntime *rt, JSObjectPrincipalsFinder fop); - -/************************************************************************/ - -/* - * Functions and scripts. - */ -extern JS_PUBLIC_API(JSFunction *) -JS_NewFunction(JSContext *cx, JSNative call, uintN nargs, uintN flags, - JSObject *parent, const char *name); - -extern JS_PUBLIC_API(JSObject *) -JS_GetFunctionObject(JSFunction *fun); - -/* - * Deprecated, useful only for diagnostics. Use JS_GetFunctionId instead for - * anonymous vs. "anonymous" disambiguation and Unicode fidelity. - */ -extern JS_PUBLIC_API(const char *) -JS_GetFunctionName(JSFunction *fun); - -/* - * Return the function's identifier as a JSString, or null if fun is unnamed. - * The returned string lives as long as fun, so you don't need to root a saved - * reference to it if fun is well-connected or rooted, and provided you bound - * the use of the saved reference by fun's lifetime. - * - * Prefer JS_GetFunctionId over JS_GetFunctionName because it returns null for - * truly anonymous functions, and because it doesn't chop to ISO-Latin-1 chars - * from UTF-16-ish jschars. - */ -extern JS_PUBLIC_API(JSString *) -JS_GetFunctionId(JSFunction *fun); - -/* - * Return JSFUN_* flags for fun. - */ -extern JS_PUBLIC_API(uintN) -JS_GetFunctionFlags(JSFunction *fun); - -/* - * Return the arity (length) of fun. - */ -extern JS_PUBLIC_API(uint16) -JS_GetFunctionArity(JSFunction *fun); - -/* - * Infallible predicate to test whether obj is a function object (faster than - * comparing obj's class name to "Function", but equivalent unless someone has - * overwritten the "Function" identifier with a different constructor and then - * created instances using that constructor that might be passed in as obj). - */ -extern JS_PUBLIC_API(JSBool) -JS_ObjectIsFunction(JSContext *cx, JSObject *obj); - -extern JS_PUBLIC_API(JSBool) -JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs); - -extern JS_PUBLIC_API(JSFunction *) -JS_DefineFunction(JSContext *cx, JSObject *obj, const char *name, JSNative call, - uintN nargs, uintN attrs); - -extern JS_PUBLIC_API(JSFunction *) -JS_DefineUCFunction(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, JSNative call, - uintN nargs, uintN attrs); - -extern JS_PUBLIC_API(JSObject *) -JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent); - -/* - * Given a buffer, return JS_FALSE if the buffer might become a valid - * javascript statement with the addition of more lines. Otherwise return - * JS_TRUE. The intent is to support interactive compilation - accumulate - * lines in a buffer until JS_BufferIsCompilableUnit is true, then pass it to - * the compiler. - */ -extern JS_PUBLIC_API(JSBool) -JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj, - const char *bytes, size_t length); - -JS_PUBLIC_API(JSBool) -JS_UCBufferIsCompilableUnit(JSContext *cx, JSObject *obj, - const jschar *chars, size_t length); - -/* - * The JSScript objects returned by the following functions refer to string and - * other kinds of literals, including doubles and RegExp objects. These - * literals are vulnerable to garbage collection; to root script objects and - * prevent literals from being collected, create a rootable object using - * JS_NewScriptObject, and root the resulting object using JS_Add[Named]Root. - */ -extern JS_PUBLIC_API(JSScript *) -JS_CompileScript(JSContext *cx, JSObject *obj, - const char *bytes, size_t length, - const char *filename, uintN lineno); - -extern JS_PUBLIC_API(JSScript *) -JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, - const char *bytes, size_t length, - const char *filename, uintN lineno); - -extern JS_PUBLIC_API(JSScript *) -JS_CompileUCScript(JSContext *cx, JSObject *obj, - const jschar *chars, size_t length, - const char *filename, uintN lineno); - -extern JS_PUBLIC_API(JSScript *) -JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, - const jschar *chars, size_t length, - const char *filename, uintN lineno); - -extern JS_PUBLIC_API(JSScript *) -JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename); - -extern JS_PUBLIC_API(JSScript *) -JS_CompileFileHandle(JSContext *cx, JSObject *obj, const char *filename, - FILE *fh); - -extern JS_PUBLIC_API(JSScript *) -JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj, - const char *filename, FILE *fh, - JSPrincipals *principals); - -/* - * NB: you must use JS_NewScriptObject and root a pointer to its return value - * in order to keep a JSScript and its atoms safe from garbage collection after - * creating the script via JS_Compile* and before a JS_ExecuteScript* call. - * E.g., and without error checks: - * - * JSScript *script = JS_CompileFile(cx, global, filename); - * JSObject *scrobj = JS_NewScriptObject(cx, script); - * JS_AddNamedRoot(cx, &scrobj, "scrobj"); - * do { - * jsval result; - * JS_ExecuteScript(cx, global, script, &result); - * JS_GC(); - * } while (!JSVAL_IS_BOOLEAN(result) || JSVAL_TO_BOOLEAN(result)); - * JS_RemoveRoot(cx, &scrobj); - */ -extern JS_PUBLIC_API(JSObject *) -JS_NewScriptObject(JSContext *cx, JSScript *script); - -/* - * Infallible getter for a script's object. If JS_NewScriptObject has not been - * called on script yet, the return value will be null. - */ -extern JS_PUBLIC_API(JSObject *) -JS_GetScriptObject(JSScript *script); - -extern JS_PUBLIC_API(void) -JS_DestroyScript(JSContext *cx, JSScript *script); - -extern JS_PUBLIC_API(JSFunction *) -JS_CompileFunction(JSContext *cx, JSObject *obj, const char *name, - uintN nargs, const char **argnames, - const char *bytes, size_t length, - const char *filename, uintN lineno); - -extern JS_PUBLIC_API(JSFunction *) -JS_CompileFunctionForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, const char *name, - uintN nargs, const char **argnames, - const char *bytes, size_t length, - const char *filename, uintN lineno); - -extern JS_PUBLIC_API(JSFunction *) -JS_CompileUCFunction(JSContext *cx, JSObject *obj, const char *name, - uintN nargs, const char **argnames, - const jschar *chars, size_t length, - const char *filename, uintN lineno); - -extern JS_PUBLIC_API(JSFunction *) -JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, const char *name, - uintN nargs, const char **argnames, - const jschar *chars, size_t length, - const char *filename, uintN lineno); - -extern JS_PUBLIC_API(JSString *) -JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, - uintN indent); - -/* - * API extension: OR this into indent to avoid pretty-printing the decompiled - * source resulting from JS_DecompileFunction{,Body}. - */ -#define JS_DONT_PRETTY_PRINT ((uintN)0x8000) - -extern JS_PUBLIC_API(JSString *) -JS_DecompileFunction(JSContext *cx, JSFunction *fun, uintN indent); - -extern JS_PUBLIC_API(JSString *) -JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent); - -/* - * NB: JS_ExecuteScript, JS_ExecuteScriptPart, and the JS_Evaluate*Script* - * quadruplets all use the obj parameter as the initial scope chain header, - * the 'this' keyword value, and the variables object (ECMA parlance for where - * 'var' and 'function' bind names) of the execution context for script. - * - * Using obj as the variables object is problematic if obj's parent (which is - * the scope chain link; see JS_SetParent and JS_NewObject) is not null: in - * this case, variables created by 'var x = 0', e.g., go in obj, but variables - * created by assignment to an unbound id, 'x = 0', go in the last object on - * the scope chain linked by parent. - * - * ECMA calls that last scoping object the "global object", but note that many - * embeddings have several such objects. ECMA requires that "global code" be - * executed with the variables object equal to this global object. But these - * JS API entry points provide freedom to execute code against a "sub-global", - * i.e., a parented or scoped object, in which case the variables object will - * differ from the last object on the scope chain, resulting in confusing and - * non-ECMA explicit vs. implicit variable creation. - * - * Caveat embedders: unless you already depend on this buggy variables object - * binding behavior, you should call JS_SetOptions(cx, JSOPTION_VAROBJFIX) or - * JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_VAROBJFIX) -- the latter if - * someone may have set other options on cx already -- for each context in the - * application, if you pass parented objects as the obj parameter, or may ever - * pass such objects in the future. - * - * Why a runtime option? The alternative is to add six or so new API entry - * points with signatures matching the following six, and that doesn't seem - * worth the code bloat cost. Such new entry points would probably have less - * obvious names, too, so would not tend to be used. The JS_SetOption call, - * OTOH, can be more easily hacked into existing code that does not depend on - * the bug; such code can continue to use the familiar JS_EvaluateScript, - * etc., entry points. - */ -extern JS_PUBLIC_API(JSBool) -JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval); - -/* - * Execute either the function-defining prolog of a script, or the script's - * main body, but not both. - */ -typedef enum JSExecPart { JSEXEC_PROLOG, JSEXEC_MAIN } JSExecPart; - -extern JS_PUBLIC_API(JSBool) -JS_ExecuteScriptPart(JSContext *cx, JSObject *obj, JSScript *script, - JSExecPart part, jsval *rval); - -extern JS_PUBLIC_API(JSBool) -JS_EvaluateScript(JSContext *cx, JSObject *obj, - const char *bytes, uintN length, - const char *filename, uintN lineno, - jsval *rval); - -extern JS_PUBLIC_API(JSBool) -JS_EvaluateScriptForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, - const char *bytes, uintN length, - const char *filename, uintN lineno, - jsval *rval); - -extern JS_PUBLIC_API(JSBool) -JS_EvaluateUCScript(JSContext *cx, JSObject *obj, - const jschar *chars, uintN length, - const char *filename, uintN lineno, - jsval *rval); - -extern JS_PUBLIC_API(JSBool) -JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, - const jschar *chars, uintN length, - const char *filename, uintN lineno, - jsval *rval); - -extern JS_PUBLIC_API(JSBool) -JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc, - jsval *argv, jsval *rval); - -extern JS_PUBLIC_API(JSBool) -JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc, - jsval *argv, jsval *rval); - -extern JS_PUBLIC_API(JSBool) -JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, - jsval *argv, jsval *rval); - -extern JS_PUBLIC_API(JSBranchCallback) -JS_SetBranchCallback(JSContext *cx, JSBranchCallback cb); - -extern JS_PUBLIC_API(JSBool) -JS_IsRunning(JSContext *cx); - -extern JS_PUBLIC_API(JSBool) -JS_IsConstructing(JSContext *cx); - -/* - * Returns true if a script is executing and its current bytecode is a set - * (assignment) operation, even if there are native (no script) stack frames - * between the script and the caller to JS_IsAssigning. - */ -extern JS_FRIEND_API(JSBool) -JS_IsAssigning(JSContext *cx); - -/* - * Set the second return value, which should be a string or int jsval that - * identifies a property in the returned object, to form an ECMA reference - * type value (obj, id). Only native methods can return reference types, - * and if the returned value is used on the left-hand side of an assignment - * op, the identified property will be set. If the return value is in an - * r-value, the interpreter just gets obj[id]'s value. - */ -extern JS_PUBLIC_API(void) -JS_SetCallReturnValue2(JSContext *cx, jsval v); - -/* - * Saving and restoring frame chains. - * - * These two functions are used to set aside cx->fp while that frame is - * inactive. After a call to JS_SaveFrameChain, it looks as if there is no - * code running on cx. Before calling JS_RestoreFrameChain, cx's call stack - * must be balanced and all nested calls to JS_SaveFrameChain must have had - * matching JS_RestoreFrameChain calls. - * - * JS_SaveFrameChain deals with cx not having any code running on it. A null - * return does not signify an error and JS_RestoreFrameChain handles null - * frames. - */ -extern JS_PUBLIC_API(JSStackFrame *) -JS_SaveFrameChain(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp); - -/************************************************************************/ - -/* - * Strings. - * - * NB: JS_NewString takes ownership of bytes on success, avoiding a copy; but - * on error (signified by null return), it leaves bytes owned by the caller. - * So the caller must free bytes in the error case, if it has no use for them. - * In contrast, all the JS_New*StringCopy* functions do not take ownership of - * the character memory passed to them -- they copy it. - */ -extern JS_PUBLIC_API(JSString *) -JS_NewString(JSContext *cx, char *bytes, size_t length); - -extern JS_PUBLIC_API(JSString *) -JS_NewStringCopyN(JSContext *cx, const char *s, size_t n); - -extern JS_PUBLIC_API(JSString *) -JS_NewStringCopyZ(JSContext *cx, const char *s); - -extern JS_PUBLIC_API(JSString *) -JS_InternString(JSContext *cx, const char *s); - -extern JS_PUBLIC_API(JSString *) -JS_NewUCString(JSContext *cx, jschar *chars, size_t length); - -extern JS_PUBLIC_API(JSString *) -JS_NewUCStringCopyN(JSContext *cx, const jschar *s, size_t n); - -extern JS_PUBLIC_API(JSString *) -JS_NewUCStringCopyZ(JSContext *cx, const jschar *s); - -extern JS_PUBLIC_API(JSString *) -JS_InternUCStringN(JSContext *cx, const jschar *s, size_t length); - -extern JS_PUBLIC_API(JSString *) -JS_InternUCString(JSContext *cx, const jschar *s); - -extern JS_PUBLIC_API(char *) -JS_GetStringBytes(JSString *str); - -extern JS_PUBLIC_API(jschar *) -JS_GetStringChars(JSString *str); - -extern JS_PUBLIC_API(size_t) -JS_GetStringLength(JSString *str); - -extern JS_PUBLIC_API(intN) -JS_CompareStrings(JSString *str1, JSString *str2); - -/* - * Mutable string support. A string's characters are never mutable in this JS - * implementation, but a growable string has a buffer that can be reallocated, - * and a dependent string is a substring of another (growable, dependent, or - * immutable) string. The direct data members of the (opaque to API clients) - * JSString struct may be changed in a single-threaded way for growable and - * dependent strings. - * - * Therefore mutable strings cannot be used by more than one thread at a time. - * You may call JS_MakeStringImmutable to convert the string from a mutable - * (growable or dependent) string to an immutable (and therefore thread-safe) - * string. The engine takes care of converting growable and dependent strings - * to immutable for you if you store strings in multi-threaded objects using - * JS_SetProperty or kindred API entry points. - * - * If you store a JSString pointer in a native data structure that is (safely) - * accessible to multiple threads, you must call JS_MakeStringImmutable before - * retiring the store. - */ -extern JS_PUBLIC_API(JSString *) -JS_NewGrowableString(JSContext *cx, jschar *chars, size_t length); - -/* - * Create a dependent string, i.e., a string that owns no character storage, - * but that refers to a slice of another string's chars. Dependent strings - * are mutable by definition, so the thread safety comments above apply. - */ -extern JS_PUBLIC_API(JSString *) -JS_NewDependentString(JSContext *cx, JSString *str, size_t start, - size_t length); - -/* - * Concatenate two strings, resulting in a new growable string. If you create - * the left string and pass it to JS_ConcatStrings on a single thread, try to - * use JS_NewGrowableString to create the left string -- doing so helps Concat - * avoid allocating a new buffer for the result and copying left's chars into - * the new buffer. See above for thread safety comments. - */ -extern JS_PUBLIC_API(JSString *) -JS_ConcatStrings(JSContext *cx, JSString *left, JSString *right); - -/* - * Convert a dependent string into an independent one. This function does not - * change the string's mutability, so the thread safety comments above apply. - */ -extern JS_PUBLIC_API(const jschar *) -JS_UndependString(JSContext *cx, JSString *str); - -/* - * Convert a mutable string (either growable or dependent) into an immutable, - * thread-safe one. - */ -extern JS_PUBLIC_API(JSBool) -JS_MakeStringImmutable(JSContext *cx, JSString *str); - -/* - * Return JS_TRUE if C (char []) strings passed via the API and internally - * are UTF-8. The source must be compiled with JS_C_STRINGS_ARE_UTF8 defined - * to get UTF-8 support. - */ -JS_PUBLIC_API(JSBool) -JS_CStringsAreUTF8(); - -/* - * Character encoding support. - * - * For both JS_EncodeCharacters and JS_DecodeBytes, set *dstlenp to the size - * of the destination buffer before the call; on return, *dstlenp contains the - * number of bytes (JS_EncodeCharacters) or jschars (JS_DecodeBytes) actually - * stored. To determine the necessary destination buffer size, make a sizing - * call that passes NULL for dst. - * - * On errors, the functions report the error. In that case, *dstlenp contains - * the number of characters or bytes transferred so far. If cx is NULL, no - * error is reported on failure, and the functions simply return JS_FALSE. - * - * NB: Neither function stores an additional zero byte or jschar after the - * transcoded string. - * - * If the source has been compiled with the #define JS_C_STRINGS_ARE_UTF8 to - * enable UTF-8 interpretation of C char[] strings, then JS_EncodeCharacters - * encodes to UTF-8, and JS_DecodeBytes decodes from UTF-8, which may create - * addititional errors if the character sequence is malformed. If UTF-8 - * support is disabled, the functions deflate and inflate, respectively. - */ -JS_PUBLIC_API(JSBool) -JS_EncodeCharacters(JSContext *cx, const jschar *src, size_t srclen, char *dst, - size_t *dstlenp); - -JS_PUBLIC_API(JSBool) -JS_DecodeBytes(JSContext *cx, const char *src, size_t srclen, jschar *dst, - size_t *dstlenp); - -/************************************************************************/ - -/* - * Locale specific string conversion and error message callbacks. - */ -struct JSLocaleCallbacks { - JSLocaleToUpperCase localeToUpperCase; - JSLocaleToLowerCase localeToLowerCase; - JSLocaleCompare localeCompare; - JSLocaleToUnicode localeToUnicode; - JSErrorCallback localeGetErrorMessage; -}; - -/* - * Establish locale callbacks. The pointer must persist as long as the - * JSContext. Passing NULL restores the default behaviour. - */ -extern JS_PUBLIC_API(void) -JS_SetLocaleCallbacks(JSContext *cx, JSLocaleCallbacks *callbacks); - -/* - * Return the address of the current locale callbacks struct, which may - * be NULL. - */ -extern JS_PUBLIC_API(JSLocaleCallbacks *) -JS_GetLocaleCallbacks(JSContext *cx); - -/************************************************************************/ - -/* - * Error reporting. - */ - -/* - * Report an exception represented by the sprintf-like conversion of format - * and its arguments. This exception message string is passed to a pre-set - * JSErrorReporter function (set by JS_SetErrorReporter; see jspubtd.h for - * the JSErrorReporter typedef). - */ -extern JS_PUBLIC_API(void) -JS_ReportError(JSContext *cx, const char *format, ...); - -/* - * Use an errorNumber to retrieve the format string, args are char * - */ -extern JS_PUBLIC_API(void) -JS_ReportErrorNumber(JSContext *cx, JSErrorCallback errorCallback, - void *userRef, const uintN errorNumber, ...); - -/* - * Use an errorNumber to retrieve the format string, args are jschar * - */ -extern JS_PUBLIC_API(void) -JS_ReportErrorNumberUC(JSContext *cx, JSErrorCallback errorCallback, - void *userRef, const uintN errorNumber, ...); - -/* - * As above, but report a warning instead (JSREPORT_IS_WARNING(report.flags)). - * Return true if there was no error trying to issue the warning, and if the - * warning was not converted into an error due to the JSOPTION_WERROR option - * being set, false otherwise. - */ -extern JS_PUBLIC_API(JSBool) -JS_ReportWarning(JSContext *cx, const char *format, ...); - -extern JS_PUBLIC_API(JSBool) -JS_ReportErrorFlagsAndNumber(JSContext *cx, uintN flags, - JSErrorCallback errorCallback, void *userRef, - const uintN errorNumber, ...); - -extern JS_PUBLIC_API(JSBool) -JS_ReportErrorFlagsAndNumberUC(JSContext *cx, uintN flags, - JSErrorCallback errorCallback, void *userRef, - const uintN errorNumber, ...); - -/* - * Complain when out of memory. - */ -extern JS_PUBLIC_API(void) -JS_ReportOutOfMemory(JSContext *cx); - -struct JSErrorReport { - const char *filename; /* source file name, URL, etc., or null */ - uintN lineno; /* source line number */ - const char *linebuf; /* offending source line without final \n */ - const char *tokenptr; /* pointer to error token in linebuf */ - const jschar *uclinebuf; /* unicode (original) line buffer */ - const jschar *uctokenptr; /* unicode (original) token pointer */ - uintN flags; /* error/warning, etc. */ - uintN errorNumber; /* the error number, e.g. see js.msg */ - const jschar *ucmessage; /* the (default) error message */ - const jschar **messageArgs; /* arguments for the error message */ -}; - -/* - * JSErrorReport flag values. These may be freely composed. - */ -#define JSREPORT_ERROR 0x0 /* pseudo-flag for default case */ -#define JSREPORT_WARNING 0x1 /* reported via JS_ReportWarning */ -#define JSREPORT_EXCEPTION 0x2 /* exception was thrown */ -#define JSREPORT_STRICT 0x4 /* error or warning due to strict option */ - -/* - * If JSREPORT_EXCEPTION is set, then a JavaScript-catchable exception - * has been thrown for this runtime error, and the host should ignore it. - * Exception-aware hosts should also check for JS_IsExceptionPending if - * JS_ExecuteScript returns failure, and signal or propagate the exception, as - * appropriate. - */ -#define JSREPORT_IS_WARNING(flags) (((flags) & JSREPORT_WARNING) != 0) -#define JSREPORT_IS_EXCEPTION(flags) (((flags) & JSREPORT_EXCEPTION) != 0) -#define JSREPORT_IS_STRICT(flags) (((flags) & JSREPORT_STRICT) != 0) - -extern JS_PUBLIC_API(JSErrorReporter) -JS_SetErrorReporter(JSContext *cx, JSErrorReporter er); - -/************************************************************************/ - -/* - * Regular Expressions. - */ -#define JSREG_FOLD 0x01 /* fold uppercase to lowercase */ -#define JSREG_GLOB 0x02 /* global exec, creates array of matches */ -#define JSREG_MULTILINE 0x04 /* treat ^ and $ as begin and end of line */ - -extern JS_PUBLIC_API(JSObject *) -JS_NewRegExpObject(JSContext *cx, char *bytes, size_t length, uintN flags); - -extern JS_PUBLIC_API(JSObject *) -JS_NewUCRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags); - -extern JS_PUBLIC_API(void) -JS_SetRegExpInput(JSContext *cx, JSString *input, JSBool multiline); - -extern JS_PUBLIC_API(void) -JS_ClearRegExpStatics(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_ClearRegExpRoots(JSContext *cx); - -/* TODO: compile, exec, get/set other statics... */ - -/************************************************************************/ - -extern JS_PUBLIC_API(JSBool) -JS_IsExceptionPending(JSContext *cx); - -extern JS_PUBLIC_API(JSBool) -JS_GetPendingException(JSContext *cx, jsval *vp); - -extern JS_PUBLIC_API(void) -JS_SetPendingException(JSContext *cx, jsval v); - -extern JS_PUBLIC_API(void) -JS_ClearPendingException(JSContext *cx); - -extern JS_PUBLIC_API(JSBool) -JS_ReportPendingException(JSContext *cx); - -/* - * Save the current exception state. This takes a snapshot of cx's current - * exception state without making any change to that state. - * - * The returned state pointer MUST be passed later to JS_RestoreExceptionState - * (to restore that saved state, overriding any more recent state) or else to - * JS_DropExceptionState (to free the state struct in case it is not correct - * or desirable to restore it). Both Restore and Drop free the state struct, - * so callers must stop using the pointer returned from Save after calling the - * Release or Drop API. - */ -extern JS_PUBLIC_API(JSExceptionState *) -JS_SaveExceptionState(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_RestoreExceptionState(JSContext *cx, JSExceptionState *state); - -extern JS_PUBLIC_API(void) -JS_DropExceptionState(JSContext *cx, JSExceptionState *state); - -/* - * If the given value is an exception object that originated from an error, - * the exception will contain an error report struct, and this API will return - * the address of that struct. Otherwise, it returns NULL. The lifetime of - * the error report struct that might be returned is the same as the lifetime - * of the exception object. - */ -extern JS_PUBLIC_API(JSErrorReport *) -JS_ErrorFromException(JSContext *cx, jsval v); - -/* - * Given a reported error's message and JSErrorReport struct pointer, throw - * the corresponding exception on cx. - */ -extern JS_PUBLIC_API(JSBool) -JS_ThrowReportedError(JSContext *cx, const char *message, - JSErrorReport *reportp); - -#ifdef JS_THREADSAFE - -/* - * Associate the current thread with the given context. This is done - * implicitly by JS_NewContext. - * - * Returns the old thread id for this context, which should be treated as - * an opaque value. This value is provided for comparison to 0, which - * indicates that ClearContextThread has been called on this context - * since the last SetContextThread, or non-0, which indicates the opposite. - */ -extern JS_PUBLIC_API(jsword) -JS_GetContextThread(JSContext *cx); - -extern JS_PUBLIC_API(jsword) -JS_SetContextThread(JSContext *cx); - -extern JS_PUBLIC_API(jsword) -JS_ClearContextThread(JSContext *cx); - -#endif /* JS_THREADSAFE */ - -/************************************************************************/ - -JS_END_EXTERN_C - -#endif /* jsapi_h___ */ diff --git a/spidermonkey/src/jsarena.c b/spidermonkey/src/jsarena.c deleted file mode 100644 index ef6ccd1..0000000 --- a/spidermonkey/src/jsarena.c +++ /dev/null @@ -1,502 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * Lifetime-based fast allocation, inspired by much prior art, including - * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes" - * David R. Hanson, Software -- Practice and Experience, Vol. 20(1). - */ -#include "jsstddef.h" -#include -#include -#include "jstypes.h" -#include "jsbit.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ - -#ifdef JS_ARENAMETER -static JSArenaStats *arena_stats_list; - -#define COUNT(pool,what) (pool)->stats.what++ -#else -#define COUNT(pool,what) /* nothing */ -#endif - -#define JS_ARENA_DEFAULT_ALIGN sizeof(double) - -JS_PUBLIC_API(void) -JS_InitArenaPool(JSArenaPool *pool, const char *name, size_t size, size_t align) -{ - if (align == 0) - align = JS_ARENA_DEFAULT_ALIGN; - pool->mask = JS_BITMASK(JS_CeilingLog2(align)); - pool->first.next = NULL; - pool->first.base = pool->first.avail = pool->first.limit = - JS_ARENA_ALIGN(pool, &pool->first + 1); - pool->current = &pool->first; - pool->arenasize = size; -#ifdef JS_ARENAMETER - memset(&pool->stats, 0, sizeof pool->stats); - pool->stats.name = strdup(name); - pool->stats.next = arena_stats_list; - arena_stats_list = &pool->stats; -#endif -} - -/* - * An allocation that consumes more than pool->arenasize also has a header - * pointing back to its previous arena's next member. This header is not - * included in [a->base, a->limit), so its space can't be wrongly claimed. - * - * As the header is a pointer, it must be well-aligned. If pool->mask is - * greater than or equal to POINTER_MASK, the header just preceding a->base - * for an oversized arena a is well-aligned, because a->base is well-aligned. - * However, we may need to add more space to pad the JSArena ** back-pointer - * so that it lies just behind a->base, because a might not be aligned such - * that (jsuword)(a + 1) is on a pointer boundary. - * - * By how much must we pad? Let M be the alignment modulus for pool and P - * the modulus for a pointer. Given M >= P, the base of an oversized arena - * that satisfies M is well-aligned for P. - * - * On the other hand, if M < P, we must include enough space in the header - * size to align the back-pointer on a P boundary so that it can be found by - * subtracting P from a->base. This means a->base must be on a P boundary, - * even though subsequent allocations from a may be aligned on a lesser (M) - * boundary. Given powers of two M and P as above, the extra space needed - * when M < P is P-M or POINTER_MASK - pool->mask. - * - * The size of a header including padding is given by the HEADER_SIZE macro, - * below, for any pool (for any value of M). - * - * The mask to align a->base for any pool is (pool->mask | POINTER_MASK), or - * HEADER_BASE_MASK(pool). - * - * PTR_TO_HEADER computes the address of the back-pointer, given an oversized - * allocation at p. By definition, p must be a->base for the arena a that - * contains p. GET_HEADER and SET_HEADER operate on an oversized arena a, in - * the case of SET_HEADER with back-pointer ap. - */ -#define POINTER_MASK ((jsuword)(JS_ALIGN_OF_POINTER - 1)) -#define HEADER_SIZE(pool) (sizeof(JSArena **) \ - + (((pool)->mask < POINTER_MASK) \ - ? POINTER_MASK - (pool)->mask \ - : 0)) -#define HEADER_BASE_MASK(pool) ((pool)->mask | POINTER_MASK) -#define PTR_TO_HEADER(pool,p) (JS_ASSERT(((jsuword)(p) \ - & HEADER_BASE_MASK(pool)) \ - == 0), \ - (JSArena ***)(p) - 1) -#define GET_HEADER(pool,a) (*PTR_TO_HEADER(pool, (a)->base)) -#define SET_HEADER(pool,a,ap) (*PTR_TO_HEADER(pool, (a)->base) = (ap)) - -JS_PUBLIC_API(void *) -JS_ArenaAllocate(JSArenaPool *pool, size_t nb) -{ - JSArena **ap, *a, *b; - jsuword extra, hdrsz, gross; - void *p; - - /* - * Search pool from current forward till we find or make enough space. - * - * NB: subtract nb from a->limit in the loop condition, instead of adding - * nb to a->avail, to avoid overflowing a 32-bit address space (possible - * when running a 32-bit program on a 64-bit system where the kernel maps - * the heap up against the top of the 32-bit address space). - * - * Thanks to Juergen Kreileder , who brought this up in - * https://bugzilla.mozilla.org/show_bug.cgi?id=279273. - */ - JS_ASSERT((nb & pool->mask) == 0); - for (a = pool->current; nb > a->limit || a->avail > a->limit - nb; - pool->current = a) { - ap = &a->next; - if (!*ap) { - /* Not enough space in pool, so we must malloc. */ - extra = (nb > pool->arenasize) ? HEADER_SIZE(pool) : 0; - hdrsz = sizeof *a + extra + pool->mask; - gross = hdrsz + JS_MAX(nb, pool->arenasize); - if (gross < nb) - return NULL; - b = (JSArena *) malloc(gross); - if (!b) - return NULL; - b->next = NULL; - b->limit = (jsuword)b + gross; - JS_COUNT_ARENA(pool,++); - COUNT(pool, nmallocs); - - /* If oversized, store ap in the header, just before a->base. */ - *ap = a = b; - JS_ASSERT(gross <= JS_UPTRDIFF(a->limit, a)); - if (extra) { - a->base = a->avail = - ((jsuword)a + hdrsz) & ~HEADER_BASE_MASK(pool); - SET_HEADER(pool, a, ap); - } else { - a->base = a->avail = JS_ARENA_ALIGN(pool, a + 1); - } - continue; - } - a = *ap; /* move to next arena */ - } - - p = (void *)a->avail; - a->avail += nb; - JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); - return p; -} - -JS_PUBLIC_API(void *) -JS_ArenaRealloc(JSArenaPool *pool, void *p, size_t size, size_t incr) -{ - JSArena **ap, *a, *b; - jsuword boff, aoff, extra, hdrsz, gross; - - /* - * Use the oversized-single-allocation header to avoid searching for ap. - * See JS_ArenaAllocate, the SET_HEADER call. - */ - if (size > pool->arenasize) { - ap = *PTR_TO_HEADER(pool, p); - a = *ap; - } else { - ap = &pool->first.next; - while ((a = *ap) != pool->current) - ap = &a->next; - } - - JS_ASSERT(a->base == (jsuword)p); - boff = JS_UPTRDIFF(a->base, a); - aoff = JS_ARENA_ALIGN(pool, size + incr); - JS_ASSERT(aoff > pool->arenasize); - extra = HEADER_SIZE(pool); /* oversized header holds ap */ - hdrsz = sizeof *a + extra + pool->mask; /* header and alignment slop */ - gross = hdrsz + aoff; - JS_ASSERT(gross > aoff); - a = (JSArena *) realloc(a, gross); - if (!a) - return NULL; -#ifdef JS_ARENAMETER - pool->stats.nreallocs++; -#endif - - if (a != *ap) { - /* Oops, realloc moved the allocation: update other pointers to a. */ - if (pool->current == *ap) - pool->current = a; - b = a->next; - if (b && b->avail - b->base > pool->arenasize) { - JS_ASSERT(GET_HEADER(pool, b) == &(*ap)->next); - SET_HEADER(pool, b, &a->next); - } - - /* Now update *ap, the next link of the arena before a. */ - *ap = a; - } - - a->base = ((jsuword)a + hdrsz) & ~HEADER_BASE_MASK(pool); - a->limit = (jsuword)a + gross; - a->avail = a->base + aoff; - JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); - - /* Check whether realloc aligned differently, and copy if necessary. */ - if (boff != JS_UPTRDIFF(a->base, a)) - memmove((void *)a->base, (char *)a + boff, size); - - /* Store ap in the oversized-load arena header. */ - SET_HEADER(pool, a, ap); - return (void *)a->base; -} - -JS_PUBLIC_API(void *) -JS_ArenaGrow(JSArenaPool *pool, void *p, size_t size, size_t incr) -{ - void *newp; - - /* - * If p points to an oversized allocation, it owns an entire arena, so we - * can simply realloc the arena. - */ - if (size > pool->arenasize) - return JS_ArenaRealloc(pool, p, size, incr); - - JS_ARENA_ALLOCATE(newp, pool, size + incr); - if (newp) - memcpy(newp, p, size); - return newp; -} - -/* - * Free tail arenas linked after head, which may not be the true list head. - * Reset pool->current to point to head in case it pointed at a tail arena. - */ -static void -FreeArenaList(JSArenaPool *pool, JSArena *head) -{ - JSArena **ap, *a; - - ap = &head->next; - a = *ap; - if (!a) - return; - -#ifdef DEBUG - do { - JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); - a->avail = a->base; - JS_CLEAR_UNUSED(a); - } while ((a = a->next) != NULL); - a = *ap; -#endif - - do { - *ap = a->next; - JS_CLEAR_ARENA(a); - JS_COUNT_ARENA(pool,--); - free(a); - } while ((a = *ap) != NULL); - - pool->current = head; -} - -JS_PUBLIC_API(void) -JS_ArenaRelease(JSArenaPool *pool, char *mark) -{ - JSArena *a; - - for (a = &pool->first; a; a = a->next) { - JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); - - if (JS_UPTRDIFF(mark, a->base) <= JS_UPTRDIFF(a->avail, a->base)) { - a->avail = JS_ARENA_ALIGN(pool, mark); - JS_ASSERT(a->avail <= a->limit); - FreeArenaList(pool, a); - return; - } - } -} - -JS_PUBLIC_API(void) -JS_ArenaFreeAllocation(JSArenaPool *pool, void *p, size_t size) -{ - JSArena **ap, *a, *b; - jsuword q; - - /* - * If the allocation is oversized, it consumes an entire arena, and it has - * a header just before the allocation pointing back to its predecessor's - * next member. Otherwise, we have to search pool for a. - */ - if (size > pool->arenasize) { - ap = *PTR_TO_HEADER(pool, p); - a = *ap; - } else { - q = (jsuword)p + size; - q = JS_ARENA_ALIGN(pool, q); - ap = &pool->first.next; - while ((a = *ap) != NULL) { - JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); - - if (a->avail == q) { - /* - * If a is consumed by the allocation at p, we can free it to - * the malloc heap. - */ - if (a->base == (jsuword)p) - break; - - /* - * We can't free a, but we can "retract" its avail cursor -- - * whether there are others after it in pool. - */ - a->avail = (jsuword)p; - return; - } - ap = &a->next; - } - } - - /* - * At this point, a is doomed, so ensure that pool->current doesn't point - * at it. We must preserve LIFO order of mark/release cursors, so we use - * the oversized-allocation arena's back pointer (or if not oversized, we - * use the result of searching the entire pool) to compute the address of - * the arena that precedes a. - */ - if (pool->current == a) - pool->current = (JSArena *) ((char *)ap - offsetof(JSArena, next)); - - /* - * This is a non-LIFO deallocation, so take care to fix up a->next's back - * pointer in its header, if a->next is oversized. - */ - *ap = b = a->next; - if (b && b->avail - b->base > pool->arenasize) { - JS_ASSERT(GET_HEADER(pool, b) == &a->next); - SET_HEADER(pool, b, ap); - } - JS_CLEAR_ARENA(a); - JS_COUNT_ARENA(pool,--); - free(a); -} - -JS_PUBLIC_API(void) -JS_FreeArenaPool(JSArenaPool *pool) -{ - FreeArenaList(pool, &pool->first); - COUNT(pool, ndeallocs); -} - -JS_PUBLIC_API(void) -JS_FinishArenaPool(JSArenaPool *pool) -{ - FreeArenaList(pool, &pool->first); -#ifdef JS_ARENAMETER - { - JSArenaStats *stats, **statsp; - - if (pool->stats.name) - free(pool->stats.name); - for (statsp = &arena_stats_list; (stats = *statsp) != 0; - statsp = &stats->next) { - if (stats == &pool->stats) { - *statsp = stats->next; - return; - } - } - } -#endif -} - -JS_PUBLIC_API(void) -JS_ArenaFinish() -{ -} - -JS_PUBLIC_API(void) -JS_ArenaShutDown(void) -{ -} - -#ifdef JS_ARENAMETER -JS_PUBLIC_API(void) -JS_ArenaCountAllocation(JSArenaPool *pool, size_t nb) -{ - pool->stats.nallocs++; - pool->stats.nbytes += nb; - if (nb > pool->stats.maxalloc) - pool->stats.maxalloc = nb; - pool->stats.variance += nb * nb; -} - -JS_PUBLIC_API(void) -JS_ArenaCountInplaceGrowth(JSArenaPool *pool, size_t size, size_t incr) -{ - pool->stats.ninplace++; -} - -JS_PUBLIC_API(void) -JS_ArenaCountGrowth(JSArenaPool *pool, size_t size, size_t incr) -{ - pool->stats.ngrows++; - pool->stats.nbytes += incr; - pool->stats.variance -= size * size; - size += incr; - if (size > pool->stats.maxalloc) - pool->stats.maxalloc = size; - pool->stats.variance += size * size; -} - -JS_PUBLIC_API(void) -JS_ArenaCountRelease(JSArenaPool *pool, char *mark) -{ - pool->stats.nreleases++; -} - -JS_PUBLIC_API(void) -JS_ArenaCountRetract(JSArenaPool *pool, char *mark) -{ - pool->stats.nfastrels++; -} - -#include -#include - -JS_PUBLIC_API(void) -JS_DumpArenaStats(FILE *fp) -{ - JSArenaStats *stats; - uint32 nallocs, nbytes; - double mean, variance, sigma; - - for (stats = arena_stats_list; stats; stats = stats->next) { - nallocs = stats->nallocs; - if (nallocs != 0) { - nbytes = stats->nbytes; - mean = (double)nbytes / nallocs; - variance = stats->variance * nallocs - nbytes * nbytes; - if (variance < 0 || nallocs == 1) - variance = 0; - else - variance /= nallocs * (nallocs - 1); - sigma = sqrt(variance); - } else { - mean = variance = sigma = 0; - } - - fprintf(fp, "\n%s allocation statistics:\n", stats->name); - fprintf(fp, " number of arenas: %u\n", stats->narenas); - fprintf(fp, " number of allocations: %u\n", stats->nallocs); - fprintf(fp, " number of free arena reclaims: %u\n", stats->nreclaims); - fprintf(fp, " number of malloc calls: %u\n", stats->nmallocs); - fprintf(fp, " number of deallocations: %u\n", stats->ndeallocs); - fprintf(fp, " number of allocation growths: %u\n", stats->ngrows); - fprintf(fp, " number of in-place growths: %u\n", stats->ninplace); - fprintf(fp, " number of realloc'ing growths: %u\n", stats->nreallocs); - fprintf(fp, "number of released allocations: %u\n", stats->nreleases); - fprintf(fp, " number of fast releases: %u\n", stats->nfastrels); - fprintf(fp, " total bytes allocated: %u\n", stats->nbytes); - fprintf(fp, " mean allocation size: %g\n", mean); - fprintf(fp, " standard deviation: %g\n", sigma); - fprintf(fp, " maximum allocation size: %u\n", stats->maxalloc); - } -} -#endif /* JS_ARENAMETER */ diff --git a/spidermonkey/src/jsarena.h b/spidermonkey/src/jsarena.h deleted file mode 100644 index 8be15d0..0000000 --- a/spidermonkey/src/jsarena.h +++ /dev/null @@ -1,303 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsarena_h___ -#define jsarena_h___ -/* - * Lifetime-based fast allocation, inspired by much prior art, including - * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes" - * David R. Hanson, Software -- Practice and Experience, Vol. 20(1). - * - * Also supports LIFO allocation (JS_ARENA_MARK/JS_ARENA_RELEASE). - */ -#include -#include "jstypes.h" -#include "jscompat.h" - -JS_BEGIN_EXTERN_C - -typedef struct JSArena JSArena; -typedef struct JSArenaPool JSArenaPool; - -struct JSArena { - JSArena *next; /* next arena for this lifetime */ - jsuword base; /* aligned base address, follows this header */ - jsuword limit; /* one beyond last byte in arena */ - jsuword avail; /* points to next available byte */ -}; - -#ifdef JS_ARENAMETER -typedef struct JSArenaStats JSArenaStats; - -struct JSArenaStats { - JSArenaStats *next; /* next in arenaStats list */ - char *name; /* name for debugging */ - uint32 narenas; /* number of arenas in pool */ - uint32 nallocs; /* number of JS_ARENA_ALLOCATE() calls */ - uint32 nmallocs; /* number of malloc() calls */ - uint32 ndeallocs; /* number of lifetime deallocations */ - uint32 ngrows; /* number of JS_ARENA_GROW() calls */ - uint32 ninplace; /* number of in-place growths */ - uint32 nreallocs; /* number of arena grow extending reallocs */ - uint32 nreleases; /* number of JS_ARENA_RELEASE() calls */ - uint32 nfastrels; /* number of "fast path" releases */ - size_t nbytes; /* total bytes allocated */ - size_t maxalloc; /* maximum allocation size in bytes */ - double variance; /* size variance accumulator */ -}; -#endif - -struct JSArenaPool { - JSArena first; /* first arena in pool list */ - JSArena *current; /* arena from which to allocate space */ - size_t arenasize; /* net exact size of a new arena */ - jsuword mask; /* alignment mask (power-of-2 - 1) */ -#ifdef JS_ARENAMETER - JSArenaStats stats; -#endif -}; - -/* - * If the including .c file uses only one power-of-2 alignment, it may define - * JS_ARENA_CONST_ALIGN_MASK to the alignment mask and save a few instructions - * per ALLOCATE and GROW. - */ -#ifdef JS_ARENA_CONST_ALIGN_MASK -#define JS_ARENA_ALIGN(pool, n) (((jsuword)(n) + JS_ARENA_CONST_ALIGN_MASK) \ - & ~(jsuword)JS_ARENA_CONST_ALIGN_MASK) - -#define JS_INIT_ARENA_POOL(pool, name, size) \ - JS_InitArenaPool(pool, name, size, JS_ARENA_CONST_ALIGN_MASK + 1) -#else -#define JS_ARENA_ALIGN(pool, n) (((jsuword)(n) + (pool)->mask) & ~(pool)->mask) -#endif - -#define JS_ARENA_ALLOCATE(p, pool, nb) \ - JS_ARENA_ALLOCATE_CAST(p, void *, pool, nb) - -#define JS_ARENA_ALLOCATE_TYPE(p, type, pool) \ - JS_ARENA_ALLOCATE_COMMON(p, type *, pool, sizeof(type), 0) - -#define JS_ARENA_ALLOCATE_CAST(p, type, pool, nb) \ - JS_ARENA_ALLOCATE_COMMON(p, type, pool, nb, _nb > _a->limit) - -/* - * NB: In JS_ARENA_ALLOCATE_CAST and JS_ARENA_GROW_CAST, always subtract _nb - * from a->limit rather than adding _nb to _p, to avoid overflowing a 32-bit - * address space (possible when running a 32-bit program on a 64-bit system - * where the kernel maps the heap up against the top of the 32-bit address - * space). - * - * Thanks to Juergen Kreileder , who brought this up in - * https://bugzilla.mozilla.org/show_bug.cgi?id=279273. - */ -#define JS_ARENA_ALLOCATE_COMMON(p, type, pool, nb, guard) \ - JS_BEGIN_MACRO \ - JSArena *_a = (pool)->current; \ - size_t _nb = JS_ARENA_ALIGN(pool, nb); \ - jsuword _p = _a->avail; \ - if ((guard) || _p > _a->limit - _nb) \ - _p = (jsuword)JS_ArenaAllocate(pool, _nb); \ - else \ - _a->avail = _p + _nb; \ - p = (type) _p; \ - JS_ArenaCountAllocation(pool, nb); \ - JS_END_MACRO - -#define JS_ARENA_GROW(p, pool, size, incr) \ - JS_ARENA_GROW_CAST(p, void *, pool, size, incr) - -#define JS_ARENA_GROW_CAST(p, type, pool, size, incr) \ - JS_BEGIN_MACRO \ - JSArena *_a = (pool)->current; \ - if (_a->avail == (jsuword)(p) + JS_ARENA_ALIGN(pool, size)) { \ - size_t _nb = (size) + (incr); \ - _nb = JS_ARENA_ALIGN(pool, _nb); \ - if (_a->limit >= _nb && (jsuword)(p) <= _a->limit - _nb) { \ - _a->avail = (jsuword)(p) + _nb; \ - JS_ArenaCountInplaceGrowth(pool, size, incr); \ - } else if ((jsuword)(p) == _a->base) { \ - p = (type) JS_ArenaRealloc(pool, p, size, incr); \ - } else { \ - p = (type) JS_ArenaGrow(pool, p, size, incr); \ - } \ - } else { \ - p = (type) JS_ArenaGrow(pool, p, size, incr); \ - } \ - JS_ArenaCountGrowth(pool, size, incr); \ - JS_END_MACRO - -#define JS_ARENA_MARK(pool) ((void *) (pool)->current->avail) -#define JS_UPTRDIFF(p,q) ((jsuword)(p) - (jsuword)(q)) - -#ifdef DEBUG -#define JS_FREE_PATTERN 0xDA -#define JS_CLEAR_UNUSED(a) (JS_ASSERT((a)->avail <= (a)->limit), \ - memset((void*)(a)->avail, JS_FREE_PATTERN, \ - (a)->limit - (a)->avail)) -#define JS_CLEAR_ARENA(a) memset((void*)(a), JS_FREE_PATTERN, \ - (a)->limit - (jsuword)(a)) -#else -#define JS_CLEAR_UNUSED(a) /* nothing */ -#define JS_CLEAR_ARENA(a) /* nothing */ -#endif - -#define JS_ARENA_RELEASE(pool, mark) \ - JS_BEGIN_MACRO \ - char *_m = (char *)(mark); \ - JSArena *_a = (pool)->current; \ - if (_a != &(pool)->first && \ - JS_UPTRDIFF(_m, _a->base) <= JS_UPTRDIFF(_a->avail, _a->base)) { \ - _a->avail = (jsuword)JS_ARENA_ALIGN(pool, _m); \ - JS_ASSERT(_a->avail <= _a->limit); \ - JS_CLEAR_UNUSED(_a); \ - JS_ArenaCountRetract(pool, _m); \ - } else { \ - JS_ArenaRelease(pool, _m); \ - } \ - JS_ArenaCountRelease(pool, _m); \ - JS_END_MACRO - -#ifdef JS_ARENAMETER -#define JS_COUNT_ARENA(pool,op) ((pool)->stats.narenas op) -#else -#define JS_COUNT_ARENA(pool,op) -#endif - -#define JS_ARENA_DESTROY(pool, a, pnext) \ - JS_BEGIN_MACRO \ - JS_COUNT_ARENA(pool,--); \ - if ((pool)->current == (a)) (pool)->current = &(pool)->first; \ - *(pnext) = (a)->next; \ - JS_CLEAR_ARENA(a); \ - free(a); \ - (a) = NULL; \ - JS_END_MACRO - -/* - * Initialize an arena pool with the given name for debugging and metering, - * with a minimum size per arena of size bytes. - */ -extern JS_PUBLIC_API(void) -JS_InitArenaPool(JSArenaPool *pool, const char *name, size_t size, - size_t align); - -/* - * Free the arenas in pool. The user may continue to allocate from pool - * after calling this function. There is no need to call JS_InitArenaPool() - * again unless JS_FinishArenaPool(pool) has been called. - */ -extern JS_PUBLIC_API(void) -JS_FreeArenaPool(JSArenaPool *pool); - -/* - * Free the arenas in pool and finish using it altogether. - */ -extern JS_PUBLIC_API(void) -JS_FinishArenaPool(JSArenaPool *pool); - -/* - * Deprecated do-nothing function. - */ -extern JS_PUBLIC_API(void) -JS_ArenaFinish(void); - -/* - * Deprecated do-nothing function. - */ -extern JS_PUBLIC_API(void) -JS_ArenaShutDown(void); - -/* - * Friend functions used by the JS_ARENA_*() macros. - */ -extern JS_PUBLIC_API(void *) -JS_ArenaAllocate(JSArenaPool *pool, size_t nb); - -extern JS_PUBLIC_API(void *) -JS_ArenaRealloc(JSArenaPool *pool, void *p, size_t size, size_t incr); - -extern JS_PUBLIC_API(void *) -JS_ArenaGrow(JSArenaPool *pool, void *p, size_t size, size_t incr); - -extern JS_PUBLIC_API(void) -JS_ArenaRelease(JSArenaPool *pool, char *mark); - -/* - * Function to be used directly when an allocation has likely grown to consume - * an entire JSArena, in which case the arena is returned to the malloc heap. - */ -extern JS_PUBLIC_API(void) -JS_ArenaFreeAllocation(JSArenaPool *pool, void *p, size_t size); - -#ifdef JS_ARENAMETER - -#include - -extern JS_PUBLIC_API(void) -JS_ArenaCountAllocation(JSArenaPool *pool, size_t nb); - -extern JS_PUBLIC_API(void) -JS_ArenaCountInplaceGrowth(JSArenaPool *pool, size_t size, size_t incr); - -extern JS_PUBLIC_API(void) -JS_ArenaCountGrowth(JSArenaPool *pool, size_t size, size_t incr); - -extern JS_PUBLIC_API(void) -JS_ArenaCountRelease(JSArenaPool *pool, char *mark); - -extern JS_PUBLIC_API(void) -JS_ArenaCountRetract(JSArenaPool *pool, char *mark); - -extern JS_PUBLIC_API(void) -JS_DumpArenaStats(FILE *fp); - -#else /* !JS_ARENAMETER */ - -#define JS_ArenaCountAllocation(ap, nb) /* nothing */ -#define JS_ArenaCountInplaceGrowth(ap, size, incr) /* nothing */ -#define JS_ArenaCountGrowth(ap, size, incr) /* nothing */ -#define JS_ArenaCountRelease(ap, mark) /* nothing */ -#define JS_ArenaCountRetract(ap, mark) /* nothing */ - -#endif /* !JS_ARENAMETER */ - -JS_END_EXTERN_C - -#endif /* jsarena_h___ */ diff --git a/spidermonkey/src/jsarray.c b/spidermonkey/src/jsarray.c deleted file mode 100644 index 532a1be..0000000 --- a/spidermonkey/src/jsarray.c +++ /dev/null @@ -1,1864 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set sw=4 ts=8 et tw=80: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS array class. - */ -#include "jsstddef.h" -#include -#include -#include "jstypes.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jsbool.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsstr.h" - -/* 2^32 - 1 as a number and a string */ -#define MAXINDEX 4294967295u -#define MAXSTR "4294967295" - -/* - * Determine if the id represents an array index or an XML property index. - * - * An id is an array index according to ECMA by (15.4): - * - * "Array objects give special treatment to a certain class of property names. - * A property name P (in the form of a string value) is an array index if and - * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal - * to 2^32-1." - * - * In our implementation, it would be sufficient to check for JSVAL_IS_INT(id) - * except that by using signed 32-bit integers we miss the top half of the - * valid range. This function checks the string representation itself; note - * that calling a standard conversion routine might allow strings such as - * "08" or "4.0" as array indices, which they are not. - */ -JSBool -js_IdIsIndex(jsval id, jsuint *indexp) -{ - JSString *str; - jschar *cp; - - if (JSVAL_IS_INT(id)) { - jsint i; - i = JSVAL_TO_INT(id); - if (i < 0) - return JS_FALSE; - *indexp = (jsuint)i; - return JS_TRUE; - } - - /* NB: id should be a string, but jsxml.c may call us with an object id. */ - if (!JSVAL_IS_STRING(id)) - return JS_FALSE; - - str = JSVAL_TO_STRING(id); - cp = JSSTRING_CHARS(str); - if (JS7_ISDEC(*cp) && JSSTRING_LENGTH(str) < sizeof(MAXSTR)) { - jsuint index = JS7_UNDEC(*cp++); - jsuint oldIndex = 0; - jsuint c = 0; - if (index != 0) { - while (JS7_ISDEC(*cp)) { - oldIndex = index; - c = JS7_UNDEC(*cp); - index = 10*index + c; - cp++; - } - } - - /* Ensure that all characters were consumed and we didn't overflow. */ - if (*cp == 0 && - (oldIndex < (MAXINDEX / 10) || - (oldIndex == (MAXINDEX / 10) && c < (MAXINDEX % 10)))) - { - *indexp = index; - return JS_TRUE; - } - } - return JS_FALSE; -} - -static JSBool -ValueIsLength(JSContext *cx, jsval v, jsuint *lengthp) -{ - jsint i; - jsdouble d; - - if (JSVAL_IS_INT(v)) { - i = JSVAL_TO_INT(v); - if (i < 0) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_ARRAY_LENGTH); - return JS_FALSE; - } - *lengthp = (jsuint) i; - return JS_TRUE; - } - - if (!js_ValueToNumber(cx, v, &d)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_ARRAY_LENGTH); - return JS_FALSE; - } - if (!js_DoubleToECMAUint32(cx, d, (uint32 *)lengthp)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_ARRAY_LENGTH); - return JS_FALSE; - } - if (JSDOUBLE_IS_NaN(d) || d != *lengthp) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_ARRAY_LENGTH); - return JS_FALSE; - } - return JS_TRUE; -} - -JSBool -js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) -{ - JSTempValueRooter tvr; - jsid id; - JSBool ok; - jsint i; - - JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); - id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); - ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value); - if (ok) { - /* - * Short-circuit, because js_ValueToECMAUint32 fails when called - * during init time. - */ - if (JSVAL_IS_INT(tvr.u.value)) { - i = JSVAL_TO_INT(tvr.u.value); - *lengthp = (jsuint)i; /* jsuint cast does ToUint32 */ - } else { - ok = js_ValueToECMAUint32(cx, tvr.u.value, (uint32 *)lengthp); - } - } - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; -} - -static JSBool -IndexToValue(JSContext *cx, jsuint index, jsval *vp) -{ - if (index <= JSVAL_INT_MAX) { - *vp = INT_TO_JSVAL(index); - return JS_TRUE; - } - return js_NewDoubleValue(cx, (jsdouble)index, vp); -} - -static JSBool -BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom, - jsid *idp) -{ - jschar buf[10], *start; - JSClass *clasp; - JSAtom *atom; - JS_STATIC_ASSERT((jsuint)-1 == 4294967295U); - - JS_ASSERT(index > JSVAL_INT_MAX); - - start = JS_ARRAY_END(buf); - do { - --start; - *start = (jschar)('0' + index % 10); - index /= 10; - } while (index != 0); - - /* - * Skip the atomization if the class is known to store atoms corresponding - * to big indexes together with elements. In such case we know that the - * array does not have an element at the given index if its atom does not - * exist. - */ - if (!createAtom && - ((clasp = OBJ_GET_CLASS(cx, obj)) == &js_ArrayClass || - clasp == &js_ArgumentsClass || - clasp == &js_ObjectClass)) { - atom = js_GetExistingStringAtom(cx, start, JS_ARRAY_END(buf) - start); - if (!atom) { - *idp = JSVAL_VOID; - return JS_TRUE; - } - } else { - atom = js_AtomizeChars(cx, start, JS_ARRAY_END(buf) - start, 0); - if (!atom) - return JS_FALSE; - } - - *idp = ATOM_TO_JSID(atom); - return JS_TRUE; -} - -/* - * If the property at the given index exists, get its value into location - * pointed by vp and set *hole to false. Otherwise set *hole to true and *vp - * to JSVAL_VOID. This function assumes that the location pointed by vp is - * properly rooted and can be used as GC-protected storage for temporaries. - */ -static JSBool -GetArrayElement(JSContext *cx, JSObject *obj, jsuint index, JSBool *hole, - jsval *vp) -{ - jsid id; - JSObject *obj2; - JSProperty *prop; - - if (index <= JSVAL_INT_MAX) { - id = INT_TO_JSID(index); - } else { - if (!BigIndexToId(cx, obj, index, JS_FALSE, &id)) - return JS_FALSE; - if (id == JSVAL_VOID) { - *hole = JS_TRUE; - *vp = JSVAL_VOID; - return JS_TRUE; - } - } - - if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) - return JS_FALSE; - if (!prop) { - *hole = JS_TRUE; - *vp = JSVAL_VOID; - } else { - OBJ_DROP_PROPERTY(cx, obj2, prop); - if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) - return JS_FALSE; - *hole = JS_FALSE; - } - return JS_TRUE; -} - -/* - * Set the value of the property at the given index to v assuming v is rooted. - */ -static JSBool -SetArrayElement(JSContext *cx, JSObject *obj, jsuint index, jsval v) -{ - jsid id; - - if (index <= JSVAL_INT_MAX) { - id = INT_TO_JSID(index); - } else { - if (!BigIndexToId(cx, obj, index, JS_TRUE, &id)) - return JS_FALSE; - JS_ASSERT(id != JSVAL_VOID); - } - return OBJ_SET_PROPERTY(cx, obj, id, &v); -} - -static JSBool -DeleteArrayElement(JSContext *cx, JSObject *obj, jsuint index) -{ - jsid id; - jsval junk; - - if (index <= JSVAL_INT_MAX) { - id = INT_TO_JSID(index); - } else { - if (!BigIndexToId(cx, obj, index, JS_FALSE, &id)) - return JS_FALSE; - if (id == JSVAL_VOID) - return JS_TRUE; - } - return OBJ_DELETE_PROPERTY(cx, obj, id, &junk); -} - -/* - * When hole is true, delete the property at the given index. Otherwise set - * its value to v assuming v is rooted. - */ -static JSBool -SetOrDeleteArrayElement(JSContext *cx, JSObject *obj, jsuint index, - JSBool hole, jsval v) -{ - if (hole) { - JS_ASSERT(v == JSVAL_VOID); - return DeleteArrayElement(cx, obj, index); - } else { - return SetArrayElement(cx, obj, index, v); - } -} - - -JSBool -js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length) -{ - jsval v; - jsid id; - - if (!IndexToValue(cx, length, &v)) - return JS_FALSE; - id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); - return OBJ_SET_PROPERTY(cx, obj, id, &v); -} - -JSBool -js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) -{ - JSErrorReporter older; - JSTempValueRooter tvr; - jsid id; - JSBool ok; - - older = JS_SetErrorReporter(cx, NULL); - JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); - id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); - ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value); - JS_SetErrorReporter(cx, older); - if (ok) - ok = ValueIsLength(cx, tvr.u.value, lengthp); - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; -} - -JSBool -js_IsArrayLike(JSContext *cx, JSObject *obj, JSBool *answerp, jsuint *lengthp) -{ - JSClass *clasp; - - clasp = OBJ_GET_CLASS(cx, obj); - *answerp = (clasp == &js_ArgumentsClass || clasp == &js_ArrayClass); - if (!*answerp) { - *lengthp = 0; - return JS_TRUE; - } - return js_GetLengthProperty(cx, obj, lengthp); -} - -/* - * This get function is specific to Array.prototype.length and other array - * instance length properties. It calls back through the class get function - * in case some magic happens there (see call_getProperty in jsfun.c). - */ -static JSBool -array_length_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - return OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, id, vp); -} - -static JSBool -array_length_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsuint newlen, oldlen, gap, index; - jsid id2; - jsval junk; - JSObject *iter; - JSTempValueRooter tvr; - JSBool ok; - - if (!ValueIsLength(cx, *vp, &newlen)) - return JS_FALSE; - if (!js_GetLengthProperty(cx, obj, &oldlen)) - return JS_FALSE; - if (oldlen > newlen) { - if (oldlen - newlen < (1 << 24)) { - do { - --oldlen; - if (!DeleteArrayElement(cx, obj, oldlen)) - return JS_FALSE; - } while (oldlen != newlen); - } else { - /* - * We are going to remove a lot of indexes in a presumably sparse - * array. So instead of looping through indexes between newlen and - * oldlen, we iterate through all properties and remove those that - * correspond to indexes from the [newlen, oldlen) range. - * See bug 322135. - */ - iter = JS_NewPropertyIterator(cx, obj); - if (!iter) - return JS_FALSE; - - /* Protect iter against GC in OBJ_DELETE_PROPERTY. */ - JS_PUSH_TEMP_ROOT_OBJECT(cx, iter, &tvr); - gap = oldlen - newlen; - for (;;) { - ok = JS_NextProperty(cx, iter, &id2); - if (!ok) - break; - if (id2 == JSVAL_VOID) - break; - if (js_IdIsIndex(id2, &index) && index - newlen < gap) { - ok = OBJ_DELETE_PROPERTY(cx, obj, id2, &junk); - if (!ok) - break; - } - } - JS_POP_TEMP_ROOT(cx, &tvr); - if (!ok) - return JS_FALSE; - } - } - return IndexToValue(cx, newlen, vp); -} - -static JSBool -array_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsuint index, length; - - if (!js_IdIsIndex(id, &index)) - return JS_TRUE; - if (!js_GetLengthProperty(cx, obj, &length)) - return JS_FALSE; - if (index >= length) { - length = index + 1; - return js_SetLengthProperty(cx, obj, length); - } - return JS_TRUE; -} - -static JSBool -array_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) -{ - return js_TryValueOf(cx, obj, type, vp); -} - -JSClass js_ArrayClass = { - "Array", - JSCLASS_HAS_CACHED_PROTO(JSProto_Array), - array_addProperty, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, array_convert, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -enum ArrayToStringOp { - TO_STRING, - TO_LOCALE_STRING, - TO_SOURCE -}; - -/* - * When op is TO_STRING or TO_LOCALE_STRING sep indicates a separator to use - * or "," when sep is NULL. - * When op is TO_SOURCE sep must be NULL. - */ -static JSBool -array_join_sub(JSContext *cx, JSObject *obj, enum ArrayToStringOp op, - JSString *sep, jsval *rval) -{ - JSBool ok, hole; - jsuint length, index; - jschar *chars, *ochars; - size_t nchars, growth, seplen, tmplen, extratail; - const jschar *sepstr; - JSString *str; - JSHashEntry *he; - JSTempValueRooter tvr; - JSAtom *atom; - int stackDummy; - - if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); - return JS_FALSE; - } - - ok = js_GetLengthProperty(cx, obj, &length); - if (!ok) - return JS_FALSE; - - he = js_EnterSharpObject(cx, obj, NULL, &chars); - if (!he) - return JS_FALSE; -#ifdef DEBUG - growth = (size_t) -1; -#endif - - if (op == TO_SOURCE) { - if (IS_SHARP(he)) { -#if JS_HAS_SHARP_VARS - nchars = js_strlen(chars); -#else - chars[0] = '['; - chars[1] = ']'; - chars[2] = 0; - nchars = 2; -#endif - goto make_string; - } - - /* - * Always allocate 2 extra chars for closing ']' and terminating 0 - * and then preallocate 1 + extratail to include starting '['. - */ - extratail = 2; - growth = (1 + extratail) * sizeof(jschar); - if (!chars) { - nchars = 0; - chars = (jschar *) malloc(growth); - if (!chars) - goto done; - } else { - MAKE_SHARP(he); - nchars = js_strlen(chars); - growth += nchars * sizeof(jschar); - chars = (jschar *)realloc((ochars = chars), growth); - if (!chars) { - free(ochars); - goto done; - } - } - chars[nchars++] = '['; - JS_ASSERT(sep == NULL); - sepstr = NULL; /* indicates to use ", " as separator */ - seplen = 2; - } else { - /* - * Free any sharp variable definition in chars. Normally, we would - * MAKE_SHARP(he) so that only the first sharp variable annotation is - * a definition, and all the rest are references, but in the current - * case of (op != TO_SOURCE), we don't need chars at all. - */ - if (chars) - JS_free(cx, chars); - chars = NULL; - nchars = 0; - extratail = 1; /* allocate extra char for terminating 0 */ - - /* Return the empty string on a cycle as well as on empty join. */ - if (IS_BUSY(he) || length == 0) { - js_LeaveSharpObject(cx, NULL); - *rval = JS_GetEmptyStringValue(cx); - return ok; - } - - /* Flag he as BUSY so we can distinguish a cycle from a join-point. */ - MAKE_BUSY(he); - - if (sep) { - sepstr = JSSTRING_CHARS(sep); - seplen = JSSTRING_LENGTH(sep); - } else { - sepstr = NULL; /* indicates to use "," as separator */ - seplen = 1; - } - } - - /* Use rval to locally root each element value as we loop and convert. */ -#define v (*rval) - - for (index = 0; index < length; index++) { - ok = GetArrayElement(cx, obj, index, &hole, &v); - if (!ok) - goto done; - if (hole || - (op != TO_SOURCE && (JSVAL_IS_VOID(v) || JSVAL_IS_NULL(v)))) { - str = cx->runtime->emptyString; - } else { - if (op == TO_LOCALE_STRING) { - atom = cx->runtime->atomState.toLocaleStringAtom; - JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr); - ok = js_ValueToObject(cx, v, &tvr.u.object) && - js_TryMethod(cx, tvr.u.object, atom, 0, NULL, &v); - JS_POP_TEMP_ROOT(cx, &tvr); - if (!ok) - goto done; - str = js_ValueToString(cx, v); - } else if (op == TO_STRING) { - str = js_ValueToString(cx, v); - } else { - JS_ASSERT(op == TO_SOURCE); - str = js_ValueToSource(cx, v); - } - if (!str) { - ok = JS_FALSE; - goto done; - } - } - - /* - * Do not append separator after the last element unless it is a hole - * and we are in toSource. In that case we append single ",". - */ - if (index + 1 == length) - seplen = (hole && op == TO_SOURCE) ? 1 : 0; - - /* Allocate 1 at end for closing bracket and zero. */ - tmplen = JSSTRING_LENGTH(str); - growth = nchars + tmplen + seplen + extratail; - if (nchars > growth || tmplen > growth || - growth > (size_t)-1 / sizeof(jschar)) { - if (chars) { - free(chars); - chars = NULL; - } - goto done; - } - growth *= sizeof(jschar); - if (!chars) { - chars = (jschar *) malloc(growth); - if (!chars) - goto done; - } else { - chars = (jschar *) realloc((ochars = chars), growth); - if (!chars) { - free(ochars); - goto done; - } - } - - js_strncpy(&chars[nchars], JSSTRING_CHARS(str), tmplen); - nchars += tmplen; - - if (seplen) { - if (sepstr) { - js_strncpy(&chars[nchars], sepstr, seplen); - } else { - JS_ASSERT(seplen == 1 || seplen == 2); - chars[nchars] = ','; - if (seplen == 2) - chars[nchars + 1] = ' '; - } - nchars += seplen; - } - } - - done: - if (op == TO_SOURCE) { - if (chars) - chars[nchars++] = ']'; - } else { - CLEAR_BUSY(he); - } - js_LeaveSharpObject(cx, NULL); - if (!ok) { - if (chars) - free(chars); - return ok; - } - -#undef v - - make_string: - if (!chars) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - chars[nchars] = 0; - JS_ASSERT(growth == (size_t)-1 || (nchars + 1) * sizeof(jschar) == growth); - str = js_NewString(cx, chars, nchars, 0); - if (!str) { - free(chars); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -#if JS_HAS_TOSOURCE -static JSBool -array_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return array_join_sub(cx, obj, TO_SOURCE, NULL, rval); -} -#endif - -static JSBool -array_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return array_join_sub(cx, obj, TO_STRING, NULL, rval); -} - -static JSBool -array_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - /* - * Passing comma here as the separator. Need a way to get a - * locale-specific version. - */ - return array_join_sub(cx, obj, TO_LOCALE_STRING, NULL, rval); -} - -static JSBool -InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint end, - jsval *vector) -{ - while (start != end) { - if (!SetArrayElement(cx, obj, start++, *vector++)) - return JS_FALSE; - } - return JS_TRUE; -} - -static JSBool -InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, jsval *vector) -{ - jsval v; - jsid id; - - if (!IndexToValue(cx, length, &v)) - return JS_FALSE; - id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); - if (!OBJ_DEFINE_PROPERTY(cx, obj, id, v, - array_length_getter, array_length_setter, - JSPROP_PERMANENT, - NULL)) { - return JS_FALSE; - } - if (!vector) - return JS_TRUE; - return InitArrayElements(cx, obj, 0, length, vector); -} - -/* - * Perl-inspired join, reverse, and sort. - */ -static JSBool -array_join(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - - if (JSVAL_IS_VOID(argv[0])) { - str = NULL; - } else { - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str); - } - return array_join_sub(cx, obj, TO_STRING, str, rval); -} - -static JSBool -array_reverse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsuint len, half, i; - JSBool hole, hole2; - jsval *tmproot, *tmproot2; - - if (!js_GetLengthProperty(cx, obj, &len)) - return JS_FALSE; - - /* - * Use argv[argc] and argv[argc + 1] as local roots to hold temporarily - * array elements for GC-safe swap. - */ - tmproot = argv + argc; - tmproot2 = argv + argc + 1; - half = len / 2; - for (i = 0; i < half; i++) { - if (!GetArrayElement(cx, obj, i, &hole, tmproot) || - !GetArrayElement(cx, obj, len - i - 1, &hole2, tmproot2) || - !SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, *tmproot) || - !SetOrDeleteArrayElement(cx, obj, i, hole2, *tmproot2)) { - return JS_FALSE; - } - } - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -typedef struct HSortArgs { - void *vec; - size_t elsize; - void *pivot; - JSComparator cmp; - void *arg; - JSBool fastcopy; -} HSortArgs; - -static JSBool -sort_compare(void *arg, const void *a, const void *b, int *result); - -static int -sort_compare_strings(void *arg, const void *a, const void *b, int *result); - -static JSBool -HeapSortHelper(JSBool building, HSortArgs *hsa, size_t lo, size_t hi) -{ - void *pivot, *vec, *vec2, *arg, *a, *b; - size_t elsize; - JSComparator cmp; - JSBool fastcopy; - size_t j, hiDiv2; - int cmp_result; - - pivot = hsa->pivot; - vec = hsa->vec; - elsize = hsa->elsize; - vec2 = (char *)vec - 2 * elsize; - cmp = hsa->cmp; - arg = hsa->arg; - - fastcopy = hsa->fastcopy; -#define MEMCPY(p,q,n) \ - (fastcopy ? (void)(*(jsval*)(p) = *(jsval*)(q)) : (void)memcpy(p, q, n)) -#define CALL_CMP(a, b) \ - if (!cmp(arg, (a), (b), &cmp_result)) return JS_FALSE; - - if (lo == 1) { - j = 2; - b = (char *)vec + elsize; - if (j < hi) { - CALL_CMP(vec, b); - if (cmp_result < 0) - j++; - } - a = (char *)vec + (hi - 1) * elsize; - b = (char *)vec2 + j * elsize; - - /* - * During sorting phase b points to a member of heap that cannot be - * bigger then biggest of vec[0] and vec[1], and cmp(a, b, arg) <= 0 - * always holds. - */ - if (building || hi == 2) { - CALL_CMP(a, b); - if (cmp_result >= 0) - return JS_TRUE; - } - - MEMCPY(pivot, a, elsize); - MEMCPY(a, b, elsize); - lo = j; - } else { - a = (char *)vec2 + lo * elsize; - MEMCPY(pivot, a, elsize); - } - - hiDiv2 = hi/2; - while (lo <= hiDiv2) { - j = lo + lo; - a = (char *)vec2 + j * elsize; - b = (char *)vec + (j - 1) * elsize; - if (j < hi) { - CALL_CMP(a, b); - if (cmp_result < 0) - j++; - } - b = (char *)vec2 + j * elsize; - CALL_CMP(pivot, b); - if (cmp_result >= 0) - break; - - a = (char *)vec2 + lo * elsize; - MEMCPY(a, b, elsize); - lo = j; - } - - a = (char *)vec2 + lo * elsize; - MEMCPY(a, pivot, elsize); - - return JS_TRUE; - -#undef CALL_CMP -#undef MEMCPY - -} - -JSBool -js_HeapSort(void *vec, size_t nel, void *pivot, size_t elsize, - JSComparator cmp, void *arg) -{ - HSortArgs hsa; - size_t i; - - hsa.vec = vec; - hsa.elsize = elsize; - hsa.pivot = pivot; - hsa.cmp = cmp; - hsa.arg = arg; - hsa.fastcopy = (cmp == sort_compare || cmp == sort_compare_strings); - - for (i = nel/2; i != 0; i--) { - if (!HeapSortHelper(JS_TRUE, &hsa, i, nel)) - return JS_FALSE; - } - while (nel > 2) { - if (!HeapSortHelper(JS_FALSE, &hsa, 1, --nel)) - return JS_FALSE; - } - - return JS_TRUE; -} - -typedef struct CompareArgs { - JSContext *context; - jsval fval; - jsval *localroot; /* need one local root, for sort_compare */ -} CompareArgs; - -static JSBool -sort_compare(void *arg, const void *a, const void *b, int *result) -{ - jsval av = *(const jsval *)a, bv = *(const jsval *)b; - CompareArgs *ca = (CompareArgs *) arg; - JSContext *cx = ca->context; - jsval fval; - JSBool ok; - - /** - * array_sort deals with holes and undefs on its own and they should not - * come here. - */ - JS_ASSERT(av != JSVAL_VOID); - JS_ASSERT(bv != JSVAL_VOID); - - *result = 0; - ok = JS_TRUE; - fval = ca->fval; - if (fval == JSVAL_NULL) { - JSString *astr, *bstr; - - if (av != bv) { - /* - * Set our local root to astr in case the second js_ValueToString - * displaces the newborn root in cx, and the GC nests under that - * call. Don't bother guarding the local root store with an astr - * non-null test. If we tag null as a string, the GC will untag, - * null-test, and avoid dereferencing null. - */ - astr = js_ValueToString(cx, av); - *ca->localroot = STRING_TO_JSVAL(astr); - if (astr && (bstr = js_ValueToString(cx, bv))) - *result = js_CompareStrings(astr, bstr); - else - ok = JS_FALSE; - } - } else { - jsdouble cmp; - jsval argv[2]; - - argv[0] = av; - argv[1] = bv; - ok = js_InternalCall(cx, - OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(fval)), - fval, 2, argv, ca->localroot); - if (ok) { - ok = js_ValueToNumber(cx, *ca->localroot, &cmp); - - /* Clamp cmp to -1, 0, 1. */ - if (ok) { - if (JSDOUBLE_IS_NaN(cmp)) { - /* - * XXX report some kind of error here? ECMA talks about - * 'consistent compare functions' that don't return NaN, - * but is silent about what the result should be. So we - * currently ignore it. - */ - } else if (cmp != 0) { - *result = cmp > 0 ? 1 : -1; - } - } - } - } - return ok; -} - -static int -sort_compare_strings(void *arg, const void *a, const void *b, int *result) -{ - jsval av = *(const jsval *)a, bv = *(const jsval *)b; - - *result = (int) js_CompareStrings(JSVAL_TO_STRING(av), JSVAL_TO_STRING(bv)); - return JS_TRUE; -} - -static JSBool -array_sort(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval fval, *vec, *pivotroot; - CompareArgs ca; - jsuint len, newlen, i, undefs; - JSTempValueRooter tvr; - JSBool hole, ok; - - /* - * Optimize the default compare function case if all of obj's elements - * have values of type string. - */ - JSBool all_strings; - - if (argc > 0) { - if (JSVAL_IS_PRIMITIVE(argv[0])) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_SORT_ARG); - return JS_FALSE; - } - fval = argv[0]; - all_strings = JS_FALSE; /* non-default compare function */ - } else { - fval = JSVAL_NULL; - all_strings = JS_TRUE; /* check for all string values */ - } - - if (!js_GetLengthProperty(cx, obj, &len)) - return JS_FALSE; - if (len == 0) { - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; - } - - /* - * We need a temporary array of len jsvals to hold elements of the array. - * Check that its size does not overflow size_t, which would allow for - * indexing beyond the end of the malloc'd vector. - */ - if (len > ((size_t) -1) / sizeof(jsval)) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - vec = (jsval *) JS_malloc(cx, ((size_t) len) * sizeof(jsval)); - if (!vec) - return JS_FALSE; - - /* - * Initialize vec as a root. We will clear elements of vec one by - * one while increasing tvr.count when we know that the property at - * the corresponding index exists and its value must be rooted. - * - * In this way when sorting a huge mostly sparse array we will not - * access the tail of vec corresponding to properties that do not - * exist, allowing OS to avoiding committing RAM. See bug 330812. - * - * After this point control must flow through label out: to exit. - */ - JS_PUSH_TEMP_ROOT(cx, 0, vec, &tvr); - - /* - * By ECMA 262, 15.4.4.11, a property that does not exist (which we - * call a "hole") is always greater than an existing property with - * value undefined and that is always greater than any other property. - * Thus to sort holes and undefs we simply count them, sort the rest - * of elements, append undefs after them and then make holes after - * undefs. - */ - undefs = 0; - newlen = 0; - for (i = 0; i < len; i++) { - /* Clear vec[newlen] before including it in the rooted set. */ - vec[newlen] = JSVAL_NULL; - tvr.count = newlen + 1; - ok = GetArrayElement(cx, obj, i, &hole, &vec[newlen]); - if (!ok) - goto out; - - if (hole) - continue; - - if (vec[newlen] == JSVAL_VOID) { - ++undefs; - continue; - } - - /* We know JSVAL_IS_STRING yields 0 or 1, so avoid a branch via &=. */ - all_strings &= JSVAL_IS_STRING(vec[newlen]); - - ++newlen; - } - - /* Here len == newlen + undefs + number_of_holes. */ - ca.context = cx; - ca.fval = fval; - ca.localroot = argv + argc; /* local GC root for temporary string */ - pivotroot = argv + argc + 1; /* local GC root for pivot val */ - ok = js_HeapSort(vec, (size_t) newlen, pivotroot, sizeof(jsval), - all_strings ? sort_compare_strings : sort_compare, - &ca); - if (!ok) - goto out; - - ok = InitArrayElements(cx, obj, 0, newlen, vec); - if (!ok) - goto out; - - out: - JS_POP_TEMP_ROOT(cx, &tvr); - JS_free(cx, vec); - if (!ok) - return JS_FALSE; - - /* Set undefs that sorted after the rest of elements. */ - while (undefs != 0) { - --undefs; - if (!SetArrayElement(cx, obj, newlen++, JSVAL_VOID)) - return JS_FALSE; - } - - /* Re-create any holes that sorted to the end of the array. */ - while (len > newlen) { - if (!DeleteArrayElement(cx, obj, --len)) - return JS_FALSE; - } - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -/* - * Perl-inspired push, pop, shift, unshift, and splice methods. - */ -static JSBool -array_push(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsuint length, newlength; - - if (!js_GetLengthProperty(cx, obj, &length)) - return JS_FALSE; - newlength = length + argc; - if (!InitArrayElements(cx, obj, length, newlength, argv)) - return JS_FALSE; - - /* Per ECMA-262, return the new array length. */ - if (!IndexToValue(cx, newlength, rval)) - return JS_FALSE; - return js_SetLengthProperty(cx, obj, newlength); -} - -static JSBool -array_pop(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsuint index; - JSBool hole; - - if (!js_GetLengthProperty(cx, obj, &index)) - return JS_FALSE; - if (index > 0) { - index--; - - /* Get the to-be-deleted property's value into rval. */ - if (!GetArrayElement(cx, obj, index, &hole, rval)) - return JS_FALSE; - if (!hole && !DeleteArrayElement(cx, obj, index)) - return JS_FALSE; - } - return js_SetLengthProperty(cx, obj, index); -} - -static JSBool -array_shift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsuint length, i; - JSBool hole; - - if (!js_GetLengthProperty(cx, obj, &length)) - return JS_FALSE; - if (length == 0) { - *rval = JSVAL_VOID; - } else { - length--; - - /* Get the to-be-deleted property's value into rval ASAP. */ - if (!GetArrayElement(cx, obj, 0, &hole, rval)) - return JS_FALSE; - - /* - * Slide down the array above the first element. - */ - for (i = 0; i != length; i++) { - if (!GetArrayElement(cx, obj, i + 1, &hole, &argv[0])) - return JS_FALSE; - if (!SetOrDeleteArrayElement(cx, obj, i, hole, argv[0])) - return JS_FALSE; - } - - /* Delete the only or last element when it exist. */ - if (!hole && !DeleteArrayElement(cx, obj, length)) - return JS_FALSE; - } - return js_SetLengthProperty(cx, obj, length); -} - -static JSBool -array_unshift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsuint length, last; - jsval *vp; - JSBool hole; - - if (!js_GetLengthProperty(cx, obj, &length)) - return JS_FALSE; - if (argc > 0) { - /* Slide up the array to make room for argc at the bottom. */ - if (length > 0) { - last = length; - vp = argv + argc; /* local root */ - do { - --last; - if (!GetArrayElement(cx, obj, last, &hole, vp) || - !SetOrDeleteArrayElement(cx, obj, last + argc, hole, *vp)) { - return JS_FALSE; - } - } while (last != 0); - } - - /* Copy from argv to the bottom of the array. */ - if (!InitArrayElements(cx, obj, 0, argc, argv)) - return JS_FALSE; - - length += argc; - if (!js_SetLengthProperty(cx, obj, length)) - return JS_FALSE; - } - - /* Follow Perl by returning the new array length. */ - return IndexToValue(cx, length, rval); -} - -static JSBool -array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval *vp; - jsuint length, begin, end, count, delta, last; - jsdouble d; - JSBool hole; - JSObject *obj2; - - /* - * Nothing to do if no args. Otherwise point vp at our one explicit local - * root and get length. - */ - if (argc == 0) - return JS_TRUE; - vp = argv + argc; - if (!js_GetLengthProperty(cx, obj, &length)) - return JS_FALSE; - - /* Convert the first argument into a starting index. */ - if (!js_ValueToNumber(cx, *argv, &d)) - return JS_FALSE; - d = js_DoubleToInteger(d); - if (d < 0) { - d += length; - if (d < 0) - d = 0; - } else if (d > length) { - d = length; - } - begin = (jsuint)d; /* d has been clamped to uint32 */ - argc--; - argv++; - - /* Convert the second argument from a count into a fencepost index. */ - delta = length - begin; - if (argc == 0) { - count = delta; - end = length; - } else { - if (!js_ValueToNumber(cx, *argv, &d)) - return JS_FALSE; - d = js_DoubleToInteger(d); - if (d < 0) - d = 0; - else if (d > delta) - d = delta; - count = (jsuint)d; - end = begin + count; - argc--; - argv++; - } - - - /* - * Create a new array value to return. Our ECMA v2 proposal specs - * that splice always returns an array value, even when given no - * arguments. We think this is best because it eliminates the need - * for callers to do an extra test to handle the empty splice case. - */ - obj2 = js_NewArrayObject(cx, 0, NULL); - if (!obj2) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj2); - - /* If there are elements to remove, put them into the return value. */ - if (count > 0) { - for (last = begin; last < end; last++) { - if (!GetArrayElement(cx, obj, last, &hole, vp)) - return JS_FALSE; - - /* Copy *vp to new array unless it's a hole. */ - if (!hole && !SetArrayElement(cx, obj2, last - begin, *vp)) - return JS_FALSE; - } - - if (!js_SetLengthProperty(cx, obj2, end - begin)) - return JS_FALSE; - } - - /* Find the direction (up or down) to copy and make way for argv. */ - if (argc > count) { - delta = (jsuint)argc - count; - last = length; - /* (uint) end could be 0, so can't use vanilla >= test */ - while (last-- > end) { - if (!GetArrayElement(cx, obj, last, &hole, vp) || - !SetOrDeleteArrayElement(cx, obj, last + delta, hole, *vp)) { - return JS_FALSE; - } - } - length += delta; - } else if (argc < count) { - delta = count - (jsuint)argc; - for (last = end; last < length; last++) { - if (!GetArrayElement(cx, obj, last, &hole, vp) || - !SetOrDeleteArrayElement(cx, obj, last - delta, hole, *vp)) { - return JS_FALSE; - } - } - length -= delta; - } - - /* Copy from argv into the hole to complete the splice. */ - if (!InitArrayElements(cx, obj, begin, begin + argc, argv)) - return JS_FALSE; - - /* Update length in case we deleted elements from the end. */ - return js_SetLengthProperty(cx, obj, length); -} - -/* - * Python-esque sequence operations. - */ -static JSBool -array_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval *vp, v; - JSObject *nobj, *aobj; - jsuint length, alength, slot; - uintN i; - JSBool hole; - - /* Hoist the explicit local root address computation. */ - vp = argv + argc; - - /* Treat obj as the first argument; see ECMA 15.4.4.4. */ - --argv; - JS_ASSERT(obj == JSVAL_TO_OBJECT(argv[0])); - - /* Create a new Array object and store it in the rval local root. */ - nobj = js_NewArrayObject(cx, 0, NULL); - if (!nobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(nobj); - - /* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */ - length = 0; - for (i = 0; i <= argc; i++) { - v = argv[i]; - if (JSVAL_IS_OBJECT(v)) { - aobj = JSVAL_TO_OBJECT(v); - if (aobj && OBJ_GET_CLASS(cx, aobj) == &js_ArrayClass) { - if (!OBJ_GET_PROPERTY(cx, aobj, - ATOM_TO_JSID(cx->runtime->atomState - .lengthAtom), - vp)) { - return JS_FALSE; - } - if (!ValueIsLength(cx, *vp, &alength)) - return JS_FALSE; - for (slot = 0; slot < alength; slot++) { - if (!GetArrayElement(cx, aobj, slot, &hole, vp)) - return JS_FALSE; - - /* - * Per ECMA 262, 15.4.4.4, step 9, ignore non-existent - * properties. - */ - if (!hole && !SetArrayElement(cx, nobj, length + slot, *vp)) - return JS_FALSE; - } - length += alength; - continue; - } - } - - if (!SetArrayElement(cx, nobj, length, v)) - return JS_FALSE; - length++; - } - - return js_SetLengthProperty(cx, nobj, length); -} - -static JSBool -array_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval *vp; - JSObject *nobj; - jsuint length, begin, end, slot; - jsdouble d; - JSBool hole; - - /* Hoist the explicit local root address computation. */ - vp = argv + argc; - - /* Create a new Array object and store it in the rval local root. */ - nobj = js_NewArrayObject(cx, 0, NULL); - if (!nobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(nobj); - - if (!js_GetLengthProperty(cx, obj, &length)) - return JS_FALSE; - begin = 0; - end = length; - - if (argc > 0) { - if (!js_ValueToNumber(cx, argv[0], &d)) - return JS_FALSE; - d = js_DoubleToInteger(d); - if (d < 0) { - d += length; - if (d < 0) - d = 0; - } else if (d > length) { - d = length; - } - begin = (jsuint)d; - - if (argc > 1) { - if (!js_ValueToNumber(cx, argv[1], &d)) - return JS_FALSE; - d = js_DoubleToInteger(d); - if (d < 0) { - d += length; - if (d < 0) - d = 0; - } else if (d > length) { - d = length; - } - end = (jsuint)d; - } - } - - if (begin > end) - begin = end; - - for (slot = begin; slot < end; slot++) { - if (!GetArrayElement(cx, obj, slot, &hole, vp)) - return JS_FALSE; - if (!hole && !SetArrayElement(cx, nobj, slot - begin, *vp)) - return JS_FALSE; - } - return js_SetLengthProperty(cx, nobj, end - begin); -} - -#if JS_HAS_ARRAY_EXTRAS - -static JSBool -array_indexOfHelper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval, JSBool isLast) -{ - jsuint length, i, stop; - jsint direction; - JSBool hole; - - if (!js_GetLengthProperty(cx, obj, &length)) - return JS_FALSE; - if (length == 0) - goto not_found; - - if (argc <= 1) { - i = isLast ? length - 1 : 0; - } else { - jsdouble start; - - if (!js_ValueToNumber(cx, argv[1], &start)) - return JS_FALSE; - start = js_DoubleToInteger(start); - if (start < 0) { - start += length; - if (start < 0) { - if (isLast) - goto not_found; - i = 0; - } else { - i = (jsuint)start; - } - } else if (start >= length) { - if (!isLast) - goto not_found; - i = length - 1; - } else { - i = (jsuint)start; - } - } - - if (isLast) { - stop = 0; - direction = -1; - } else { - stop = length - 1; - direction = 1; - } - - for (;;) { - if (!GetArrayElement(cx, obj, (jsuint)i, &hole, rval)) - return JS_FALSE; - if (!hole && js_StrictlyEqual(*rval, argv[0])) - return js_NewNumberValue(cx, i, rval); - if (i == stop) - goto not_found; - i += direction; - } - - not_found: - *rval = INT_TO_JSVAL(-1); - return JS_TRUE; -} - -static JSBool -array_indexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return array_indexOfHelper(cx, obj, argc, argv, rval, JS_FALSE); -} - -static JSBool -array_lastIndexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return array_indexOfHelper(cx, obj, argc, argv, rval, JS_TRUE); -} - -/* Order is important; extras that use a caller's predicate must follow MAP. */ -typedef enum ArrayExtraMode { - FOREACH, - MAP, - FILTER, - SOME, - EVERY -} ArrayExtraMode; - -static JSBool -array_extra(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, - ArrayExtraMode mode) -{ - jsval *vp, *sp, *origsp, *oldsp; - jsuint length, newlen, i; - JSObject *callable, *thisp, *newarr; - void *mark; - JSStackFrame *fp; - JSBool ok, cond, hole; - - /* Hoist the explicit local root address computation. */ - vp = argv + argc; - - if (!js_GetLengthProperty(cx, obj, &length)) - return JS_FALSE; - - /* - * First, get or compute our callee, so that we error out consistently - * when passed a non-callable object. - */ - callable = js_ValueToCallableObject(cx, &argv[0], JSV2F_SEARCH_STACK); - if (!callable) - return JS_FALSE; - - /* - * Set our initial return condition, used for zero-length array cases - * (and pre-size our map return to match our known length, for all cases). - */ -#ifdef __GNUC__ /* quell GCC overwarning */ - newlen = 0; - newarr = NULL; - ok = JS_TRUE; -#endif - switch (mode) { - case MAP: - case FILTER: - newlen = (mode == MAP) ? length : 0; - newarr = js_NewArrayObject(cx, newlen, NULL); - if (!newarr) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(newarr); - break; - case SOME: - *rval = JSVAL_FALSE; - break; - case EVERY: - *rval = JSVAL_TRUE; - break; - case FOREACH: - break; - } - - if (length == 0) - return JS_TRUE; - - if (argc > 1) { - if (!js_ValueToObject(cx, argv[1], &thisp)) - return JS_FALSE; - argv[1] = OBJECT_TO_JSVAL(thisp); - } else { - thisp = NULL; - } - - /* We call with 3 args (value, index, array), plus room for rval. */ - origsp = js_AllocStack(cx, 2 + 3 + 1, &mark); - if (!origsp) - return JS_FALSE; - - /* Lift current frame to include our args. */ - fp = cx->fp; - oldsp = fp->sp; - - for (i = 0; i < length; i++) { - ok = GetArrayElement(cx, obj, i, &hole, vp); - if (!ok) - break; - if (hole) - continue; - - /* - * Push callable and 'this', then args. We must do this for every - * iteration around the loop since js_Invoke uses origsp[0] for rval - * storage and some native functions use origsp[1] for local rooting. - */ - sp = origsp; - *sp++ = OBJECT_TO_JSVAL(callable); - *sp++ = OBJECT_TO_JSVAL(thisp); - *sp++ = *vp; - *sp++ = INT_TO_JSVAL(i); - *sp++ = OBJECT_TO_JSVAL(obj); - - /* Do the call. */ - fp->sp = sp; - ok = js_Invoke(cx, 3, JSINVOKE_INTERNAL); - vp[1] = fp->sp[-1]; - fp->sp = oldsp; - if (!ok) - break; - - if (mode > MAP) { - if (vp[1] == JSVAL_NULL) { - cond = JS_FALSE; - } else if (JSVAL_IS_BOOLEAN(vp[1])) { - cond = JSVAL_TO_BOOLEAN(vp[1]); - } else { - ok = js_ValueToBoolean(cx, vp[1], &cond); - if (!ok) - goto out; - } - } - - switch (mode) { - case FOREACH: - break; - case MAP: - ok = SetArrayElement(cx, newarr, i, vp[1]); - if (!ok) - goto out; - break; - case FILTER: - if (!cond) - break; - /* Filter passed *vp, push as result. */ - ok = SetArrayElement(cx, newarr, newlen++, *vp); - if (!ok) - goto out; - break; - case SOME: - if (cond) { - *rval = JSVAL_TRUE; - goto out; - } - break; - case EVERY: - if (!cond) { - *rval = JSVAL_FALSE; - goto out; - } - break; - } - } - - out: - js_FreeStack(cx, mark); - if (ok && mode == FILTER) - ok = js_SetLengthProperty(cx, newarr, newlen); - return ok; -} - -static JSBool -array_forEach(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return array_extra(cx, obj, argc, argv, rval, FOREACH); -} - -static JSBool -array_map(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return array_extra(cx, obj, argc, argv, rval, MAP); -} - -static JSBool -array_filter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return array_extra(cx, obj, argc, argv, rval, FILTER); -} - -static JSBool -array_some(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return array_extra(cx, obj, argc, argv, rval, SOME); -} - -static JSBool -array_every(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return array_extra(cx, obj, argc, argv, rval, EVERY); -} -#endif - -static JSFunctionSpec array_methods[] = { -#if JS_HAS_TOSOURCE - {js_toSource_str, array_toSource, 0,0,0}, -#endif - {js_toString_str, array_toString, 0,0,0}, - {js_toLocaleString_str, array_toLocaleString, 0,0,0}, - - /* Perl-ish methods. */ - {"join", array_join, 1,JSFUN_GENERIC_NATIVE,0}, - {"reverse", array_reverse, 0,JSFUN_GENERIC_NATIVE,2}, - {"sort", array_sort, 1,JSFUN_GENERIC_NATIVE,2}, - {"push", array_push, 1,JSFUN_GENERIC_NATIVE,0}, - {"pop", array_pop, 0,JSFUN_GENERIC_NATIVE,0}, - {"shift", array_shift, 0,JSFUN_GENERIC_NATIVE,1}, - {"unshift", array_unshift, 1,JSFUN_GENERIC_NATIVE,1}, - {"splice", array_splice, 2,JSFUN_GENERIC_NATIVE,1}, - - /* Python-esque sequence methods. */ - {"concat", array_concat, 1,JSFUN_GENERIC_NATIVE,1}, - {"slice", array_slice, 2,JSFUN_GENERIC_NATIVE,1}, - -#if JS_HAS_ARRAY_EXTRAS - {"indexOf", array_indexOf, 1,JSFUN_GENERIC_NATIVE,0}, - {"lastIndexOf", array_lastIndexOf, 1,JSFUN_GENERIC_NATIVE,0}, - {"forEach", array_forEach, 1,JSFUN_GENERIC_NATIVE,2}, - {"map", array_map, 1,JSFUN_GENERIC_NATIVE,2}, - {"filter", array_filter, 1,JSFUN_GENERIC_NATIVE,2}, - {"some", array_some, 1,JSFUN_GENERIC_NATIVE,2}, - {"every", array_every, 1,JSFUN_GENERIC_NATIVE,2}, -#endif - - {0,0,0,0,0} -}; - -static JSBool -Array(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsuint length; - jsval *vector; - - /* If called without new, replace obj with a new Array object. */ - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL); - if (!obj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - } - - if (argc == 0) { - length = 0; - vector = NULL; - } else if (argc > 1) { - length = (jsuint) argc; - vector = argv; - } else if (!JSVAL_IS_NUMBER(argv[0])) { - length = 1; - vector = argv; - } else { - if (!ValueIsLength(cx, argv[0], &length)) - return JS_FALSE; - vector = NULL; - } - return InitArrayObject(cx, obj, length, vector); -} - -JSObject * -js_InitArrayClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto; - - proto = JS_InitClass(cx, obj, NULL, &js_ArrayClass, Array, 1, - NULL, array_methods, NULL, NULL); - - /* Initialize the Array prototype object so it gets a length property. */ - if (!proto || !InitArrayObject(cx, proto, 0, NULL)) - return NULL; - return proto; -} - -JSObject * -js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector) -{ - JSTempValueRooter tvr; - JSObject *obj; - - obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL); - if (!obj) - return NULL; - - JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); - if (!InitArrayObject(cx, obj, length, vector)) - obj = NULL; - JS_POP_TEMP_ROOT(cx, &tvr); - - /* Set/clear newborn root, in case we lost it. */ - cx->weakRoots.newborn[GCX_OBJECT] = (JSGCThing *) obj; - return obj; -} diff --git a/spidermonkey/src/jsarray.h b/spidermonkey/src/jsarray.h deleted file mode 100644 index a89561b..0000000 --- a/spidermonkey/src/jsarray.h +++ /dev/null @@ -1,95 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsarray_h___ -#define jsarray_h___ -/* - * JS Array interface. - */ -#include "jsprvtd.h" -#include "jspubtd.h" - -JS_BEGIN_EXTERN_C - -/* Generous sanity-bound on length (in elements) of array initialiser. */ -#define ARRAY_INIT_LIMIT JS_BIT(24) - -extern JSBool -js_IdIsIndex(jsval id, jsuint *indexp); - -extern JSClass js_ArrayClass; - -extern JSObject * -js_InitArrayClass(JSContext *cx, JSObject *obj); - -extern JSObject * -js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector); - -extern JSBool -js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp); - -extern JSBool -js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length); - -extern JSBool -js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp); - -/* - * Test whether an object is "array-like". Currently this means whether obj - * is an Array or an arguments object. We would like an API, and probably a - * way in the language, to bless other objects as array-like: having indexed - * properties, and a 'length' property of uint32 value equal to one more than - * the greatest index. - */ -extern JSBool -js_IsArrayLike(JSContext *cx, JSObject *obj, JSBool *answerp, jsuint *lengthp); - -/* - * JS-specific heap sort function. - */ -typedef JSBool (*JSComparator)(void *arg, const void *a, const void *b, - int *result); - -extern JSBool -js_HeapSort(void *vec, size_t nel, void *pivot, size_t elsize, - JSComparator cmp, void *arg); - -JS_END_EXTERN_C - -#endif /* jsarray_h___ */ diff --git a/spidermonkey/src/jsatom.c b/spidermonkey/src/jsatom.c deleted file mode 100644 index 02ee250..0000000 --- a/spidermonkey/src/jsatom.c +++ /dev/null @@ -1,999 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS atom table. - */ -#include "jsstddef.h" -#include -#include -#include "jstypes.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jshash.h" /* Added by JSIFY */ -#include "jsprf.h" -#include "jsapi.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsgc.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsscan.h" -#include "jsstr.h" - -JS_FRIEND_API(const char *) -js_AtomToPrintableString(JSContext *cx, JSAtom *atom) -{ - return js_ValueToPrintableString(cx, ATOM_KEY(atom)); -} - -/* - * Keep this in sync with jspubtd.h -- an assertion below will insist that - * its length match the JSType enum's JSTYPE_LIMIT limit value. - */ -const char *js_type_strs[] = { - "undefined", - js_object_str, - "function", - "string", - "number", - "boolean", - "null", - "xml", -}; - -JS_STATIC_ASSERT(JSTYPE_LIMIT == - sizeof js_type_strs / sizeof js_type_strs[0]); - -const char *js_boolean_strs[] = { - js_false_str, - js_true_str -}; - -#define JS_PROTO(name,code,init) const char js_##name##_str[] = #name; -#include "jsproto.tbl" -#undef JS_PROTO - -const char *js_proto_strs[JSProto_LIMIT] = { -#define JS_PROTO(name,code,init) js_##name##_str, -#include "jsproto.tbl" -#undef JS_PROTO -}; - -const char js_anonymous_str[] = "anonymous"; -const char js_arguments_str[] = "arguments"; -const char js_arity_str[] = "arity"; -const char js_callee_str[] = "callee"; -const char js_caller_str[] = "caller"; -const char js_class_prototype_str[] = "prototype"; -const char js_constructor_str[] = "constructor"; -const char js_count_str[] = "__count__"; -const char js_each_str[] = "each"; -const char js_eval_str[] = "eval"; -const char js_fileName_str[] = "fileName"; -const char js_get_str[] = "get"; -const char js_getter_str[] = "getter"; -const char js_index_str[] = "index"; -const char js_input_str[] = "input"; -const char js_iterator_str[] = "__iterator__"; -const char js_length_str[] = "length"; -const char js_lineNumber_str[] = "lineNumber"; -const char js_message_str[] = "message"; -const char js_name_str[] = "name"; -const char js_next_str[] = "next"; -const char js_noSuchMethod_str[] = "__noSuchMethod__"; -const char js_object_str[] = "object"; -const char js_parent_str[] = "__parent__"; -const char js_proto_str[] = "__proto__"; -const char js_setter_str[] = "setter"; -const char js_set_str[] = "set"; -const char js_stack_str[] = "stack"; -const char js_toSource_str[] = "toSource"; -const char js_toString_str[] = "toString"; -const char js_toLocaleString_str[] = "toLocaleString"; -const char js_valueOf_str[] = "valueOf"; - -#if JS_HAS_XML_SUPPORT -const char js_etago_str[] = ""; -const char js_qualifier_str[] = "::"; -const char js_space_str[] = " "; -const char js_stago_str[] = "<"; -const char js_star_str[] = "*"; -const char js_starQualifier_str[] = "*::"; -const char js_tagc_str[] = ">"; -const char js_xml_str[] = "xml"; -#endif - -#if JS_HAS_GENERATORS -const char js_close_str[] = "close"; -const char js_send_str[] = "send"; -#endif - -#ifdef NARCISSUS -const char js_call_str[] = "__call__"; -const char js_construct_str[] = "__construct__"; -const char js_hasInstance_str[] = "__hasInstance__"; -const char js_ExecutionContext_str[] = "ExecutionContext"; -const char js_current_str[] = "current"; -#endif - -#define HASH_OBJECT(o) (JS_PTR_TO_UINT32(o) >> JSVAL_TAGBITS) -#define HASH_INT(i) ((JSHashNumber)(i)) -#define HASH_DOUBLE(dp) ((JSDOUBLE_HI32(*dp) ^ JSDOUBLE_LO32(*dp))) -#define HASH_BOOLEAN(b) ((JSHashNumber)(b)) - -JS_STATIC_DLL_CALLBACK(JSHashNumber) -js_hash_atom_key(const void *key) -{ - jsval v; - jsdouble *dp; - - /* Order JSVAL_IS_* tests by likelihood of success. */ - v = (jsval)key; - if (JSVAL_IS_STRING(v)) - return js_HashString(JSVAL_TO_STRING(v)); - if (JSVAL_IS_INT(v)) - return HASH_INT(JSVAL_TO_INT(v)); - if (JSVAL_IS_DOUBLE(v)) { - dp = JSVAL_TO_DOUBLE(v); - return HASH_DOUBLE(dp); - } - if (JSVAL_IS_OBJECT(v)) - return HASH_OBJECT(JSVAL_TO_OBJECT(v)); - if (JSVAL_IS_BOOLEAN(v)) - return HASH_BOOLEAN(JSVAL_TO_BOOLEAN(v)); - return (JSHashNumber)v; -} - -JS_STATIC_DLL_CALLBACK(intN) -js_compare_atom_keys(const void *k1, const void *k2) -{ - jsval v1, v2; - - v1 = (jsval)k1, v2 = (jsval)k2; - if (JSVAL_IS_STRING(v1) && JSVAL_IS_STRING(v2)) - return js_EqualStrings(JSVAL_TO_STRING(v1), JSVAL_TO_STRING(v2)); - if (JSVAL_IS_DOUBLE(v1) && JSVAL_IS_DOUBLE(v2)) { - double d1 = *JSVAL_TO_DOUBLE(v1); - double d2 = *JSVAL_TO_DOUBLE(v2); - if (JSDOUBLE_IS_NaN(d1)) - return JSDOUBLE_IS_NaN(d2); -#if defined(XP_WIN) - /* XXX MSVC miscompiles such that (NaN == 0) */ - if (JSDOUBLE_IS_NaN(d2)) - return JS_FALSE; -#endif - return d1 == d2; - } - return v1 == v2; -} - -JS_STATIC_DLL_CALLBACK(int) -js_compare_stub(const void *v1, const void *v2) -{ - return 1; -} - -/* These next two are exported to jsscript.c and used similarly there. */ -void * JS_DLL_CALLBACK -js_alloc_table_space(void *priv, size_t size) -{ - return malloc(size); -} - -void JS_DLL_CALLBACK -js_free_table_space(void *priv, void *item) -{ - free(item); -} - -JS_STATIC_DLL_CALLBACK(JSHashEntry *) -js_alloc_atom(void *priv, const void *key) -{ - JSAtomState *state = (JSAtomState *) priv; - JSAtom *atom; - - atom = (JSAtom *) malloc(sizeof(JSAtom)); - if (!atom) - return NULL; -#ifdef JS_THREADSAFE - state->tablegen++; -#endif - atom->entry.key = key; - atom->entry.value = NULL; - atom->flags = 0; - atom->number = state->number++; - return &atom->entry; -} - -JS_STATIC_DLL_CALLBACK(void) -js_free_atom(void *priv, JSHashEntry *he, uintN flag) -{ - if (flag != HT_FREE_ENTRY) - return; -#ifdef JS_THREADSAFE - ((JSAtomState *)priv)->tablegen++; -#endif - free(he); -} - -static JSHashAllocOps atom_alloc_ops = { - js_alloc_table_space, js_free_table_space, - js_alloc_atom, js_free_atom -}; - -#define JS_ATOM_HASH_SIZE 1024 - -JSBool -js_InitAtomState(JSContext *cx, JSAtomState *state) -{ - state->table = JS_NewHashTable(JS_ATOM_HASH_SIZE, js_hash_atom_key, - js_compare_atom_keys, js_compare_stub, - &atom_alloc_ops, state); - if (!state->table) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - state->runtime = cx->runtime; -#ifdef JS_THREADSAFE - js_InitLock(&state->lock); - state->tablegen = 0; -#endif - - if (!js_InitPinnedAtoms(cx, state)) { - js_FreeAtomState(cx, state); - return JS_FALSE; - } - return JS_TRUE; -} - -JSBool -js_InitPinnedAtoms(JSContext *cx, JSAtomState *state) -{ - uintN i; - -#define FROB(lval,str) \ - JS_BEGIN_MACRO \ - if (!(state->lval = js_Atomize(cx, str, strlen(str), ATOM_PINNED))) \ - return JS_FALSE; \ - JS_END_MACRO - - for (i = 0; i < JSTYPE_LIMIT; i++) - FROB(typeAtoms[i], js_type_strs[i]); - - for (i = 0; i < JSProto_LIMIT; i++) - FROB(classAtoms[i], js_proto_strs[i]); - - FROB(booleanAtoms[0], js_false_str); - FROB(booleanAtoms[1], js_true_str); - FROB(nullAtom, js_null_str); - - FROB(anonymousAtom, js_anonymous_str); - FROB(argumentsAtom, js_arguments_str); - FROB(arityAtom, js_arity_str); - FROB(calleeAtom, js_callee_str); - FROB(callerAtom, js_caller_str); - FROB(classPrototypeAtom, js_class_prototype_str); - FROB(constructorAtom, js_constructor_str); - FROB(countAtom, js_count_str); - FROB(eachAtom, js_each_str); - FROB(evalAtom, js_eval_str); - FROB(fileNameAtom, js_fileName_str); - FROB(getAtom, js_get_str); - FROB(getterAtom, js_getter_str); - FROB(indexAtom, js_index_str); - FROB(inputAtom, js_input_str); - FROB(iteratorAtom, js_iterator_str); - FROB(lengthAtom, js_length_str); - FROB(lineNumberAtom, js_lineNumber_str); - FROB(messageAtom, js_message_str); - FROB(nameAtom, js_name_str); - FROB(nextAtom, js_next_str); - FROB(noSuchMethodAtom, js_noSuchMethod_str); - FROB(parentAtom, js_parent_str); - FROB(protoAtom, js_proto_str); - FROB(setAtom, js_set_str); - FROB(setterAtom, js_setter_str); - FROB(stackAtom, js_stack_str); - FROB(toSourceAtom, js_toSource_str); - FROB(toStringAtom, js_toString_str); - FROB(toLocaleStringAtom, js_toLocaleString_str); - FROB(valueOfAtom, js_valueOf_str); - -#if JS_HAS_XML_SUPPORT - FROB(etagoAtom, js_etago_str); - FROB(namespaceAtom, js_namespace_str); - FROB(ptagcAtom, js_ptagc_str); - FROB(qualifierAtom, js_qualifier_str); - FROB(spaceAtom, js_space_str); - FROB(stagoAtom, js_stago_str); - FROB(starAtom, js_star_str); - FROB(starQualifierAtom, js_starQualifier_str); - FROB(tagcAtom, js_tagc_str); - FROB(xmlAtom, js_xml_str); -#endif - -#if JS_HAS_GENERATORS - FROB(closeAtom, js_close_str); -#endif - -#ifdef NARCISSUS - FROB(callAtom, js_call_str); - FROB(constructAtom, js_construct_str); - FROB(hasInstanceAtom, js_hasInstance_str); - FROB(ExecutionContextAtom, js_ExecutionContext_str); - FROB(currentAtom, js_current_str); -#endif - -#undef FROB - - memset(&state->lazy, 0, sizeof state->lazy); - return JS_TRUE; -} - -/* NB: cx unused; js_FinishAtomState calls us with null cx. */ -void -js_FreeAtomState(JSContext *cx, JSAtomState *state) -{ - if (state->table) - JS_HashTableDestroy(state->table); -#ifdef JS_THREADSAFE - js_FinishLock(&state->lock); -#endif - memset(state, 0, sizeof *state); -} - -typedef struct UninternArgs { - JSRuntime *rt; - jsatomid leaks; -} UninternArgs; - -JS_STATIC_DLL_CALLBACK(intN) -js_atom_uninterner(JSHashEntry *he, intN i, void *arg) -{ - JSAtom *atom; - UninternArgs *args; - - atom = (JSAtom *)he; - args = (UninternArgs *)arg; - if (ATOM_IS_STRING(atom)) - js_FinalizeStringRT(args->rt, ATOM_TO_STRING(atom)); - else if (ATOM_IS_OBJECT(atom)) - args->leaks++; - return HT_ENUMERATE_NEXT; -} - -void -js_FinishAtomState(JSAtomState *state) -{ - UninternArgs args; - - if (!state->table) - return; - args.rt = state->runtime; - args.leaks = 0; - JS_HashTableEnumerateEntries(state->table, js_atom_uninterner, &args); -#ifdef DEBUG - if (args.leaks != 0) { - fprintf(stderr, -"JS engine warning: %lu atoms remain after destroying the JSRuntime.\n" -" These atoms may point to freed memory. Things reachable\n" -" through them have not been finalized.\n", - (unsigned long) args.leaks); - } -#endif - js_FreeAtomState(NULL, state); -} - -typedef struct MarkArgs { - JSBool keepAtoms; - JSGCThingMarker mark; - void *data; -} MarkArgs; - -JS_STATIC_DLL_CALLBACK(intN) -js_atom_marker(JSHashEntry *he, intN i, void *arg) -{ - JSAtom *atom; - MarkArgs *args; - jsval key; - - atom = (JSAtom *)he; - args = (MarkArgs *)arg; - if ((atom->flags & (ATOM_PINNED | ATOM_INTERNED)) || args->keepAtoms) { - atom->flags |= ATOM_MARK; - key = ATOM_KEY(atom); - if (JSVAL_IS_GCTHING(key)) - args->mark(JSVAL_TO_GCTHING(key), args->data); - } - return HT_ENUMERATE_NEXT; -} - -void -js_MarkAtomState(JSAtomState *state, JSBool keepAtoms, JSGCThingMarker mark, - void *data) -{ - MarkArgs args; - - if (!state->table) - return; - args.keepAtoms = keepAtoms; - args.mark = mark; - args.data = data; - JS_HashTableEnumerateEntries(state->table, js_atom_marker, &args); -} - -JS_STATIC_DLL_CALLBACK(intN) -js_atom_sweeper(JSHashEntry *he, intN i, void *arg) -{ - JSAtom *atom; - JSAtomState *state; - - atom = (JSAtom *)he; - if (atom->flags & ATOM_MARK) { - atom->flags &= ~ATOM_MARK; - state = (JSAtomState *)arg; - state->liveAtoms++; - return HT_ENUMERATE_NEXT; - } - JS_ASSERT((atom->flags & (ATOM_PINNED | ATOM_INTERNED)) == 0); - atom->entry.key = atom->entry.value = NULL; - atom->flags = 0; - return HT_ENUMERATE_REMOVE; -} - -void -js_SweepAtomState(JSAtomState *state) -{ - state->liveAtoms = 0; - if (state->table) - JS_HashTableEnumerateEntries(state->table, js_atom_sweeper, state); -} - -JS_STATIC_DLL_CALLBACK(intN) -js_atom_unpinner(JSHashEntry *he, intN i, void *arg) -{ - JSAtom *atom; - - atom = (JSAtom *)he; - atom->flags &= ~ATOM_PINNED; - return HT_ENUMERATE_NEXT; -} - -void -js_UnpinPinnedAtoms(JSAtomState *state) -{ - if (state->table) - JS_HashTableEnumerateEntries(state->table, js_atom_unpinner, NULL); -} - -static JSAtom * -js_AtomizeHashedKey(JSContext *cx, jsval key, JSHashNumber keyHash, uintN flags) -{ - JSAtomState *state; - JSHashTable *table; - JSHashEntry *he, **hep; - JSAtom *atom; - - state = &cx->runtime->atomState; - JS_LOCK(&state->lock, cx); - table = state->table; - hep = JS_HashTableRawLookup(table, keyHash, (void *)key); - if ((he = *hep) == NULL) { - he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL); - if (!he) { - JS_ReportOutOfMemory(cx); - atom = NULL; - goto out; - } - } - - atom = (JSAtom *)he; - atom->flags |= flags; - cx->weakRoots.lastAtom = atom; -out: - JS_UNLOCK(&state->lock,cx); - return atom; -} - -JSAtom * -js_AtomizeObject(JSContext *cx, JSObject *obj, uintN flags) -{ - jsval key; - JSHashNumber keyHash; - - /* XXX must be set in the following order or MSVC1.52 will crash */ - keyHash = HASH_OBJECT(obj); - key = OBJECT_TO_JSVAL(obj); - return js_AtomizeHashedKey(cx, key, keyHash, flags); -} - -JSAtom * -js_AtomizeBoolean(JSContext *cx, JSBool b, uintN flags) -{ - jsval key; - JSHashNumber keyHash; - - key = BOOLEAN_TO_JSVAL(b); - keyHash = HASH_BOOLEAN(b); - return js_AtomizeHashedKey(cx, key, keyHash, flags); -} - -JSAtom * -js_AtomizeInt(JSContext *cx, jsint i, uintN flags) -{ - jsval key; - JSHashNumber keyHash; - - key = INT_TO_JSVAL(i); - keyHash = HASH_INT(i); - return js_AtomizeHashedKey(cx, key, keyHash, flags); -} - -/* Worst-case alignment grain and aligning macro for 2x-sized buffer. */ -#define ALIGNMENT(t) JS_MAX(JSVAL_ALIGN, sizeof(t)) -#define ALIGN(b,t) ((t*) &(b)[ALIGNMENT(t) - (jsuword)(b) % ALIGNMENT(t)]) - -JSAtom * -js_AtomizeDouble(JSContext *cx, jsdouble d, uintN flags) -{ - jsdouble *dp; - JSHashNumber keyHash; - jsval key; - JSAtomState *state; - JSHashTable *table; - JSHashEntry *he, **hep; - JSAtom *atom; - char buf[2 * ALIGNMENT(double)]; - - dp = ALIGN(buf, double); - *dp = d; - keyHash = HASH_DOUBLE(dp); - key = DOUBLE_TO_JSVAL(dp); - state = &cx->runtime->atomState; - JS_LOCK(&state->lock, cx); - table = state->table; - hep = JS_HashTableRawLookup(table, keyHash, (void *)key); - if ((he = *hep) == NULL) { -#ifdef JS_THREADSAFE - uint32 gen = state->tablegen; -#endif - JS_UNLOCK(&state->lock,cx); - if (!js_NewDoubleValue(cx, d, &key)) - return NULL; - JS_LOCK(&state->lock, cx); -#ifdef JS_THREADSAFE - if (state->tablegen != gen) { - hep = JS_HashTableRawLookup(table, keyHash, (void *)key); - if ((he = *hep) != NULL) { - atom = (JSAtom *)he; - goto out; - } - } -#endif - he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL); - if (!he) { - JS_ReportOutOfMemory(cx); - atom = NULL; - goto out; - } - } - - atom = (JSAtom *)he; - atom->flags |= flags; - cx->weakRoots.lastAtom = atom; -out: - JS_UNLOCK(&state->lock,cx); - return atom; -} - -/* - * To put an atom into the hidden subspace. XOR its keyHash with this value, - * which is (sqrt(2)-1) in 32-bit fixed point. - */ -#define HIDDEN_ATOM_SUBSPACE_KEYHASH 0x6A09E667 - -JSAtom * -js_AtomizeString(JSContext *cx, JSString *str, uintN flags) -{ - JSHashNumber keyHash; - jsval key; - JSAtomState *state; - JSHashTable *table; - JSHashEntry *he, **hep; - JSAtom *atom; - - keyHash = js_HashString(str); - if (flags & ATOM_HIDDEN) - keyHash ^= HIDDEN_ATOM_SUBSPACE_KEYHASH; - key = STRING_TO_JSVAL(str); - state = &cx->runtime->atomState; - JS_LOCK(&state->lock, cx); - table = state->table; - hep = JS_HashTableRawLookup(table, keyHash, (void *)key); - if ((he = *hep) == NULL) { -#ifdef JS_THREADSAFE - uint32 gen = state->tablegen; - JS_UNLOCK(&state->lock, cx); -#endif - - if (flags & ATOM_TMPSTR) { - str = (flags & ATOM_NOCOPY) - ? js_NewString(cx, str->chars, str->length, 0) - : js_NewStringCopyN(cx, str->chars, str->length, 0); - if (!str) - return NULL; - key = STRING_TO_JSVAL(str); - } else { - if (!JS_MakeStringImmutable(cx, str)) - return NULL; - } - -#ifdef JS_THREADSAFE - JS_LOCK(&state->lock, cx); - if (state->tablegen != gen) { - hep = JS_HashTableRawLookup(table, keyHash, (void *)key); - if ((he = *hep) != NULL) { - atom = (JSAtom *)he; - if (flags & ATOM_NOCOPY) - str->chars = NULL; - goto out; - } - } -#endif - - he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL); - if (!he) { - JS_ReportOutOfMemory(cx); - atom = NULL; - goto out; - } - } - - atom = (JSAtom *)he; - atom->flags |= flags & (ATOM_PINNED | ATOM_INTERNED | ATOM_HIDDEN); - cx->weakRoots.lastAtom = atom; -out: - JS_UNLOCK(&state->lock,cx); - return atom; -} - -JS_FRIEND_API(JSAtom *) -js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags) -{ - jschar *chars; - JSString *str; - JSAtom *atom; - char buf[2 * ALIGNMENT(JSString)]; - - /* - * Avoiding the malloc in js_InflateString on shorter strings saves us - * over 20,000 malloc calls on mozilla browser startup. This compares to - * only 131 calls where the string is longer than a 31 char (net) buffer. - * The vast majority of atomized strings are already in the hashtable. So - * js_AtomizeString rarely has to copy the temp string we make. - */ -#define ATOMIZE_BUF_MAX 32 - jschar inflated[ATOMIZE_BUF_MAX]; - size_t inflatedLength = ATOMIZE_BUF_MAX - 1; - - if (length < ATOMIZE_BUF_MAX) { - js_InflateStringToBuffer(cx, bytes, length, inflated, &inflatedLength); - inflated[inflatedLength] = 0; - chars = inflated; - } else { - inflatedLength = length; - chars = js_InflateString(cx, bytes, &inflatedLength); - if (!chars) - return NULL; - flags |= ATOM_NOCOPY; - } - - str = ALIGN(buf, JSString); - - str->chars = chars; - str->length = inflatedLength; - atom = js_AtomizeString(cx, str, ATOM_TMPSTR | flags); - if (chars != inflated && (!atom || ATOM_TO_STRING(atom)->chars != chars)) - JS_free(cx, chars); - return atom; -} - -JS_FRIEND_API(JSAtom *) -js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags) -{ - JSString *str; - char buf[2 * ALIGNMENT(JSString)]; - - str = ALIGN(buf, JSString); - str->chars = (jschar *)chars; - str->length = length; - return js_AtomizeString(cx, str, ATOM_TMPSTR | flags); -} - -JSAtom * -js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length) -{ - JSString *str; - char buf[2 * ALIGNMENT(JSString)]; - JSHashNumber keyHash; - jsval key; - JSAtomState *state; - JSHashTable *table; - JSHashEntry **hep; - - str = ALIGN(buf, JSString); - str->chars = (jschar *)chars; - str->length = length; - keyHash = js_HashString(str); - key = STRING_TO_JSVAL(str); - state = &cx->runtime->atomState; - JS_LOCK(&state->lock, cx); - table = state->table; - hep = JS_HashTableRawLookup(table, keyHash, (void *)key); - JS_UNLOCK(&state->lock, cx); - return (hep) ? (JSAtom *)*hep : NULL; -} - -JSAtom * -js_AtomizeValue(JSContext *cx, jsval value, uintN flags) -{ - if (JSVAL_IS_STRING(value)) - return js_AtomizeString(cx, JSVAL_TO_STRING(value), flags); - if (JSVAL_IS_INT(value)) - return js_AtomizeInt(cx, JSVAL_TO_INT(value), flags); - if (JSVAL_IS_DOUBLE(value)) - return js_AtomizeDouble(cx, *JSVAL_TO_DOUBLE(value), flags); - if (JSVAL_IS_OBJECT(value)) - return js_AtomizeObject(cx, JSVAL_TO_OBJECT(value), flags); - if (JSVAL_IS_BOOLEAN(value)) - return js_AtomizeBoolean(cx, JSVAL_TO_BOOLEAN(value), flags); - return js_AtomizeHashedKey(cx, value, (JSHashNumber)value, flags); -} - -JSAtom * -js_ValueToStringAtom(JSContext *cx, jsval v) -{ - JSString *str; - - str = js_ValueToString(cx, v); - if (!str) - return NULL; - return js_AtomizeString(cx, str, 0); -} - -JS_STATIC_DLL_CALLBACK(JSHashNumber) -js_hash_atom_ptr(const void *key) -{ - const JSAtom *atom = key; - return atom->number; -} - -JS_STATIC_DLL_CALLBACK(void *) -js_alloc_temp_space(void *priv, size_t size) -{ - JSContext *cx = priv; - void *space; - - JS_ARENA_ALLOCATE(space, &cx->tempPool, size); - if (!space) - JS_ReportOutOfMemory(cx); - return space; -} - -JS_STATIC_DLL_CALLBACK(void) -js_free_temp_space(void *priv, void *item) -{ -} - -JS_STATIC_DLL_CALLBACK(JSHashEntry *) -js_alloc_temp_entry(void *priv, const void *key) -{ - JSContext *cx = priv; - JSAtomListElement *ale; - - JS_ARENA_ALLOCATE_TYPE(ale, JSAtomListElement, &cx->tempPool); - if (!ale) { - JS_ReportOutOfMemory(cx); - return NULL; - } - return &ale->entry; -} - -JS_STATIC_DLL_CALLBACK(void) -js_free_temp_entry(void *priv, JSHashEntry *he, uintN flag) -{ -} - -static JSHashAllocOps temp_alloc_ops = { - js_alloc_temp_space, js_free_temp_space, - js_alloc_temp_entry, js_free_temp_entry -}; - -JSAtomListElement * -js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al) -{ - JSAtomListElement *ale, *ale2, *next; - JSHashEntry **hep; - - ATOM_LIST_LOOKUP(ale, hep, al, atom); - if (!ale) { - if (al->count < 10) { - /* Few enough for linear search, no hash table needed. */ - JS_ASSERT(!al->table); - ale = (JSAtomListElement *)js_alloc_temp_entry(cx, atom); - if (!ale) - return NULL; - ALE_SET_ATOM(ale, atom); - ALE_SET_NEXT(ale, al->list); - al->list = ale; - } else { - /* We want to hash. Have we already made a hash table? */ - if (!al->table) { - /* No hash table yet, so hep had better be null! */ - JS_ASSERT(!hep); - al->table = JS_NewHashTable(al->count + 1, js_hash_atom_ptr, - JS_CompareValues, JS_CompareValues, - &temp_alloc_ops, cx); - if (!al->table) - return NULL; - - /* - * Set ht->nentries explicitly, because we are moving entries - * from al to ht, not calling JS_HashTable(Raw|)Add. - */ - al->table->nentries = al->count; - - /* Insert each ale on al->list into the new hash table. */ - for (ale2 = al->list; ale2; ale2 = next) { - next = ALE_NEXT(ale2); - ale2->entry.keyHash = ALE_ATOM(ale2)->number; - hep = JS_HashTableRawLookup(al->table, ale2->entry.keyHash, - ale2->entry.key); - ALE_SET_NEXT(ale2, *hep); - *hep = &ale2->entry; - } - al->list = NULL; - - /* Set hep for insertion of atom's ale, immediately below. */ - hep = JS_HashTableRawLookup(al->table, atom->number, atom); - } - - /* Finally, add an entry for atom into the hash bucket at hep. */ - ale = (JSAtomListElement *) - JS_HashTableRawAdd(al->table, hep, atom->number, atom, NULL); - if (!ale) - return NULL; - } - - ALE_SET_INDEX(ale, al->count++); - } - return ale; -} - -JS_FRIEND_API(JSAtom *) -js_GetAtom(JSContext *cx, JSAtomMap *map, jsatomid i) -{ - JSAtom *atom; - static JSAtom dummy; - - JS_ASSERT(map->vector && i < map->length); - if (!map->vector || i >= map->length) { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%lu", (unsigned long)i); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_ATOMIC_NUMBER, numBuf); - return &dummy; - } - atom = map->vector[i]; - JS_ASSERT(atom); - return atom; -} - -JS_STATIC_DLL_CALLBACK(intN) -js_map_atom(JSHashEntry *he, intN i, void *arg) -{ - JSAtomListElement *ale = (JSAtomListElement *)he; - JSAtom **vector = arg; - - vector[ALE_INDEX(ale)] = ALE_ATOM(ale); - return HT_ENUMERATE_NEXT; -} - -#ifdef DEBUG -static jsrefcount js_atom_map_count; -static jsrefcount js_atom_map_hash_table_count; -#endif - -JS_FRIEND_API(JSBool) -js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al) -{ - JSAtom **vector; - JSAtomListElement *ale; - uint32 count; - -#ifdef DEBUG - JS_ATOMIC_INCREMENT(&js_atom_map_count); -#endif - ale = al->list; - if (!ale && !al->table) { - map->vector = NULL; - map->length = 0; - return JS_TRUE; - } - - count = al->count; - if (count >= ATOM_INDEX_LIMIT) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_TOO_MANY_LITERALS); - return JS_FALSE; - } - vector = (JSAtom **) JS_malloc(cx, (size_t) count * sizeof *vector); - if (!vector) - return JS_FALSE; - - if (al->table) { -#ifdef DEBUG - JS_ATOMIC_INCREMENT(&js_atom_map_hash_table_count); -#endif - JS_HashTableEnumerateEntries(al->table, js_map_atom, vector); - } else { - do { - vector[ALE_INDEX(ale)] = ALE_ATOM(ale); - } while ((ale = ALE_NEXT(ale)) != NULL); - } - ATOM_LIST_INIT(al); - - map->vector = vector; - map->length = (jsatomid)count; - return JS_TRUE; -} - -JS_FRIEND_API(void) -js_FreeAtomMap(JSContext *cx, JSAtomMap *map) -{ - if (map->vector) { - JS_free(cx, map->vector); - map->vector = NULL; - } - map->length = 0; -} diff --git a/spidermonkey/src/jsatom.h b/spidermonkey/src/jsatom.h deleted file mode 100644 index 4fb3d8d..0000000 --- a/spidermonkey/src/jsatom.h +++ /dev/null @@ -1,456 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsatom_h___ -#define jsatom_h___ -/* - * JS atom table. - */ -#include -#include "jstypes.h" -#include "jshash.h" /* Added by JSIFY */ -#include "jsapi.h" -#include "jsprvtd.h" -#include "jspubtd.h" - -#ifdef JS_THREADSAFE -#include "jslock.h" -#endif - -JS_BEGIN_EXTERN_C - -#define ATOM_PINNED 0x01 /* atom is pinned against GC */ -#define ATOM_INTERNED 0x02 /* pinned variant for JS_Intern* API */ -#define ATOM_MARK 0x04 /* atom is reachable via GC */ -#define ATOM_HIDDEN 0x08 /* atom is in special hidden subspace */ -#define ATOM_NOCOPY 0x40 /* don't copy atom string bytes */ -#define ATOM_TMPSTR 0x80 /* internal, to avoid extra string */ - -struct JSAtom { - JSHashEntry entry; /* key is jsval or unhidden atom - if ATOM_HIDDEN */ - uint32 flags; /* pinned, interned, and mark flags */ - jsatomid number; /* atom serial number and hash code */ -}; - -#define ATOM_KEY(atom) ((jsval)(atom)->entry.key) -#define ATOM_IS_OBJECT(atom) JSVAL_IS_OBJECT(ATOM_KEY(atom)) -#define ATOM_TO_OBJECT(atom) JSVAL_TO_OBJECT(ATOM_KEY(atom)) -#define ATOM_IS_INT(atom) JSVAL_IS_INT(ATOM_KEY(atom)) -#define ATOM_TO_INT(atom) JSVAL_TO_INT(ATOM_KEY(atom)) -#define ATOM_IS_DOUBLE(atom) JSVAL_IS_DOUBLE(ATOM_KEY(atom)) -#define ATOM_TO_DOUBLE(atom) JSVAL_TO_DOUBLE(ATOM_KEY(atom)) -#define ATOM_IS_STRING(atom) JSVAL_IS_STRING(ATOM_KEY(atom)) -#define ATOM_TO_STRING(atom) JSVAL_TO_STRING(ATOM_KEY(atom)) -#define ATOM_IS_BOOLEAN(atom) JSVAL_IS_BOOLEAN(ATOM_KEY(atom)) -#define ATOM_TO_BOOLEAN(atom) JSVAL_TO_BOOLEAN(ATOM_KEY(atom)) - -/* - * Return a printable, lossless char[] representation of a string-type atom. - * The lifetime of the result extends at least until the next GC activation, - * longer if cx's string newborn root is not overwritten. - */ -extern JS_FRIEND_API(const char *) -js_AtomToPrintableString(JSContext *cx, JSAtom *atom); - -struct JSAtomListElement { - JSHashEntry entry; -}; - -#define ALE_ATOM(ale) ((JSAtom *) (ale)->entry.key) -#define ALE_INDEX(ale) ((jsatomid) JS_PTR_TO_UINT32((ale)->entry.value)) -#define ALE_JSOP(ale) ((JSOp) (ale)->entry.value) -#define ALE_VALUE(ale) ((jsval) (ale)->entry.value) -#define ALE_NEXT(ale) ((JSAtomListElement *) (ale)->entry.next) - -#define ALE_SET_ATOM(ale,atom) ((ale)->entry.key = (const void *)(atom)) -#define ALE_SET_INDEX(ale,index)((ale)->entry.value = JS_UINT32_TO_PTR(index)) -#define ALE_SET_JSOP(ale,op) ((ale)->entry.value = JS_UINT32_TO_PTR(op)) -#define ALE_SET_VALUE(ale,val) ((ale)->entry.value = (JSHashEntry *)(val)) -#define ALE_SET_NEXT(ale,link) ((ale)->entry.next = (JSHashEntry *)(link)) - -struct JSAtomList { - JSAtomListElement *list; /* literals indexed for mapping */ - JSHashTable *table; /* hash table if list gets too long */ - jsuint count; /* count of indexed literals */ -}; - -#define ATOM_LIST_INIT(al) ((al)->list = NULL, (al)->table = NULL, \ - (al)->count = 0) - -#define ATOM_LIST_SEARCH(_ale,_al,_atom) \ - JS_BEGIN_MACRO \ - JSHashEntry **_hep; \ - ATOM_LIST_LOOKUP(_ale, _hep, _al, _atom); \ - JS_END_MACRO - -#define ATOM_LIST_LOOKUP(_ale,_hep,_al,_atom) \ - JS_BEGIN_MACRO \ - if ((_al)->table) { \ - _hep = JS_HashTableRawLookup((_al)->table, _atom->number, _atom); \ - _ale = *_hep ? (JSAtomListElement *) *_hep : NULL; \ - } else { \ - JSAtomListElement **_alep = &(_al)->list; \ - _hep = NULL; \ - while ((_ale = *_alep) != NULL) { \ - if (ALE_ATOM(_ale) == (_atom)) { \ - /* Hit, move atom's element to the front of the list. */ \ - *_alep = ALE_NEXT(_ale); \ - ALE_SET_NEXT(_ale, (_al)->list); \ - (_al)->list = _ale; \ - break; \ - } \ - _alep = (JSAtomListElement **)&_ale->entry.next; \ - } \ - } \ - JS_END_MACRO - -struct JSAtomMap { - JSAtom **vector; /* array of ptrs to indexed atoms */ - jsatomid length; /* count of (to-be-)indexed atoms */ -}; - -struct JSAtomState { - JSRuntime *runtime; /* runtime that owns us */ - JSHashTable *table; /* hash table containing all atoms */ - jsatomid number; /* one beyond greatest atom number */ - jsatomid liveAtoms; /* number of live atoms after last GC */ - - /* The rt->emptyString atom, see jsstr.c's js_InitRuntimeStringState. */ - JSAtom *emptyAtom; - - /* Type names and value literals. */ - JSAtom *typeAtoms[JSTYPE_LIMIT]; - JSAtom *booleanAtoms[2]; - JSAtom *nullAtom; - - /* Standard class constructor or prototype names. */ - JSAtom *classAtoms[JSProto_LIMIT]; - - /* Various built-in or commonly-used atoms, pinned on first context. */ - JSAtom *anonymousAtom; - JSAtom *argumentsAtom; - JSAtom *arityAtom; - JSAtom *calleeAtom; - JSAtom *callerAtom; - JSAtom *classPrototypeAtom; - JSAtom *closeAtom; - JSAtom *constructorAtom; - JSAtom *countAtom; - JSAtom *eachAtom; - JSAtom *etagoAtom; - JSAtom *evalAtom; - JSAtom *fileNameAtom; - JSAtom *getAtom; - JSAtom *getterAtom; - JSAtom *indexAtom; - JSAtom *inputAtom; - JSAtom *iteratorAtom; - JSAtom *lengthAtom; - JSAtom *lineNumberAtom; - JSAtom *messageAtom; - JSAtom *nameAtom; - JSAtom *namespaceAtom; - JSAtom *nextAtom; - JSAtom *noSuchMethodAtom; - JSAtom *parentAtom; - JSAtom *protoAtom; - JSAtom *ptagcAtom; - JSAtom *qualifierAtom; - JSAtom *setAtom; - JSAtom *setterAtom; - JSAtom *spaceAtom; - JSAtom *stackAtom; - JSAtom *stagoAtom; - JSAtom *starAtom; - JSAtom *starQualifierAtom; - JSAtom *tagcAtom; - JSAtom *toLocaleStringAtom; - JSAtom *toSourceAtom; - JSAtom *toStringAtom; - JSAtom *valueOfAtom; - JSAtom *xmlAtom; - - /* Less frequently used atoms, pinned lazily by JS_ResolveStandardClass. */ - struct { - JSAtom *InfinityAtom; - JSAtom *NaNAtom; - JSAtom *XMLListAtom; - JSAtom *decodeURIAtom; - JSAtom *decodeURIComponentAtom; - JSAtom *defineGetterAtom; - JSAtom *defineSetterAtom; - JSAtom *encodeURIAtom; - JSAtom *encodeURIComponentAtom; - JSAtom *escapeAtom; - JSAtom *functionNamespaceURIAtom; - JSAtom *hasOwnPropertyAtom; - JSAtom *isFiniteAtom; - JSAtom *isNaNAtom; - JSAtom *isPrototypeOfAtom; - JSAtom *isXMLNameAtom; - JSAtom *lookupGetterAtom; - JSAtom *lookupSetterAtom; - JSAtom *parseFloatAtom; - JSAtom *parseIntAtom; - JSAtom *propertyIsEnumerableAtom; - JSAtom *unescapeAtom; - JSAtom *unevalAtom; - JSAtom *unwatchAtom; - JSAtom *watchAtom; - } lazy; - -#ifdef JS_THREADSAFE - JSThinLock lock; - volatile uint32 tablegen; -#endif -#ifdef NARCISSUS - JSAtom *callAtom; - JSAtom *constructAtom; - JSAtom *hasInstanceAtom; - JSAtom *ExecutionContextAtom; - JSAtom *currentAtom; -#endif -}; - -#define CLASS_ATOM(cx,name) \ - ((cx)->runtime->atomState.classAtoms[JSProto_##name]) - -/* Well-known predefined strings and their atoms. */ -extern const char *js_type_strs[]; -extern const char *js_boolean_strs[]; -extern const char *js_proto_strs[]; - -#define JS_PROTO(name,code,init) extern const char js_##name##_str[]; -#include "jsproto.tbl" -#undef JS_PROTO - -extern const char js_anonymous_str[]; -extern const char js_arguments_str[]; -extern const char js_arity_str[]; -extern const char js_callee_str[]; -extern const char js_caller_str[]; -extern const char js_class_prototype_str[]; -extern const char js_close_str[]; -extern const char js_constructor_str[]; -extern const char js_count_str[]; -extern const char js_etago_str[]; -extern const char js_each_str[]; -extern const char js_eval_str[]; -extern const char js_fileName_str[]; -extern const char js_get_str[]; -extern const char js_getter_str[]; -extern const char js_index_str[]; -extern const char js_input_str[]; -extern const char js_iterator_str[]; -extern const char js_length_str[]; -extern const char js_lineNumber_str[]; -extern const char js_message_str[]; -extern const char js_name_str[]; -extern const char js_namespace_str[]; -extern const char js_next_str[]; -extern const char js_noSuchMethod_str[]; -extern const char js_object_str[]; -extern const char js_parent_str[]; -extern const char js_private_str[]; -extern const char js_proto_str[]; -extern const char js_ptagc_str[]; -extern const char js_qualifier_str[]; -extern const char js_send_str[]; -extern const char js_setter_str[]; -extern const char js_set_str[]; -extern const char js_space_str[]; -extern const char js_stack_str[]; -extern const char js_stago_str[]; -extern const char js_star_str[]; -extern const char js_starQualifier_str[]; -extern const char js_tagc_str[]; -extern const char js_toSource_str[]; -extern const char js_toString_str[]; -extern const char js_toLocaleString_str[]; -extern const char js_valueOf_str[]; -extern const char js_xml_str[]; - -#ifdef NARCISSUS -extern const char js_call_str[]; -extern const char js_construct_str[]; -extern const char js_hasInstance_str[]; -extern const char js_ExecutionContext_str[]; -extern const char js_current_str[]; -#endif - -/* - * Initialize atom state. Return true on success, false with an out of - * memory error report on failure. - */ -extern JSBool -js_InitAtomState(JSContext *cx, JSAtomState *state); - -/* - * Free and clear atom state (except for any interned string atoms). - */ -extern void -js_FreeAtomState(JSContext *cx, JSAtomState *state); - -/* - * Interned strings are atoms that live until state's runtime is destroyed. - * This function frees all interned string atoms, and then frees and clears - * state's members (just as js_FreeAtomState does), unless there aren't any - * interned strings in state -- in which case state must be "free" already. - * - * NB: js_FreeAtomState is called for each "last" context being destroyed in - * a runtime, where there may yet be another context created in the runtime; - * whereas js_FinishAtomState is called from JS_DestroyRuntime, when we know - * that no more contexts will be created. Thus we minimize garbage during - * context-free episodes on a runtime, while preserving atoms created by the - * JS_Intern*String APIs for the life of the runtime. - */ -extern void -js_FinishAtomState(JSAtomState *state); - -/* - * Atom garbage collection hooks. - */ -typedef void -(*JSGCThingMarker)(void *thing, void *data); - -extern void -js_MarkAtomState(JSAtomState *state, JSBool keepAtoms, JSGCThingMarker mark, - void *data); - -extern void -js_SweepAtomState(JSAtomState *state); - -extern JSBool -js_InitPinnedAtoms(JSContext *cx, JSAtomState *state); - -extern void -js_UnpinPinnedAtoms(JSAtomState *state); - -/* - * Find or create the atom for an object. If we create a new atom, give it the - * type indicated in flags. Return 0 on failure to allocate memory. - */ -extern JSAtom * -js_AtomizeObject(JSContext *cx, JSObject *obj, uintN flags); - -/* - * Find or create the atom for a Boolean value. If we create a new atom, give - * it the type indicated in flags. Return 0 on failure to allocate memory. - */ -extern JSAtom * -js_AtomizeBoolean(JSContext *cx, JSBool b, uintN flags); - -/* - * Find or create the atom for an integer value. If we create a new atom, give - * it the type indicated in flags. Return 0 on failure to allocate memory. - */ -extern JSAtom * -js_AtomizeInt(JSContext *cx, jsint i, uintN flags); - -/* - * Find or create the atom for a double value. If we create a new atom, give - * it the type indicated in flags. Return 0 on failure to allocate memory. - */ -extern JSAtom * -js_AtomizeDouble(JSContext *cx, jsdouble d, uintN flags); - -/* - * Find or create the atom for a string. If we create a new atom, give it the - * type indicated in flags. Return 0 on failure to allocate memory. - */ -extern JSAtom * -js_AtomizeString(JSContext *cx, JSString *str, uintN flags); - -extern JS_FRIEND_API(JSAtom *) -js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags); - -extern JS_FRIEND_API(JSAtom *) -js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags); - -/* - * Return an existing atom for the given char array or null if the char - * sequence is currently not atomized. - */ -extern JSAtom * -js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length); - -/* - * This variant handles all value tag types. - */ -extern JSAtom * -js_AtomizeValue(JSContext *cx, jsval value, uintN flags); - -/* - * Convert v to an atomized string. - */ -extern JSAtom * -js_ValueToStringAtom(JSContext *cx, jsval v); - -/* - * Assign atom an index and insert it on al. - */ -extern JSAtomListElement * -js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al); - -/* - * Get the atom with index i from map. - */ -extern JS_FRIEND_API(JSAtom *) -js_GetAtom(JSContext *cx, JSAtomMap *map, jsatomid i); - -/* - * For all unmapped atoms recorded in al, add a mapping from the atom's index - * to its address. The GC must not run until all indexed atoms in atomLists - * have been mapped by scripts connected to live objects (Function and Script - * class objects have scripts as/in their private data -- the GC knows about - * these two classes). - */ -extern JS_FRIEND_API(JSBool) -js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al); - -/* - * Free map->vector and clear map. - */ -extern JS_FRIEND_API(void) -js_FreeAtomMap(JSContext *cx, JSAtomMap *map); - -JS_END_EXTERN_C - -#endif /* jsatom_h___ */ diff --git a/spidermonkey/src/jsbit.h b/spidermonkey/src/jsbit.h deleted file mode 100644 index 87bb047..0000000 --- a/spidermonkey/src/jsbit.h +++ /dev/null @@ -1,195 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsbit_h___ -#define jsbit_h___ - -#include "jstypes.h" -#include "jsutil.h" - -JS_BEGIN_EXTERN_C - -/* -** A jsbitmap_t is a long integer that can be used for bitmaps -*/ -typedef JSUword jsbitmap_t; /* NSPR name, a la Unix system types */ -typedef jsbitmap_t jsbitmap; /* JS-style scalar typedef name */ - -#define JS_TEST_BIT(_map,_bit) \ - ((_map)[(_bit)>>JS_BITS_PER_WORD_LOG2] & (1L << ((_bit) & (JS_BITS_PER_WORD-1)))) -#define JS_SET_BIT(_map,_bit) \ - ((_map)[(_bit)>>JS_BITS_PER_WORD_LOG2] |= (1L << ((_bit) & (JS_BITS_PER_WORD-1)))) -#define JS_CLEAR_BIT(_map,_bit) \ - ((_map)[(_bit)>>JS_BITS_PER_WORD_LOG2] &= ~(1L << ((_bit) & (JS_BITS_PER_WORD-1)))) - -/* -** Compute the log of the least power of 2 greater than or equal to n -*/ -extern JS_PUBLIC_API(JSIntn) JS_CeilingLog2(JSUint32 i); - -/* -** Compute the log of the greatest power of 2 less than or equal to n -*/ -extern JS_PUBLIC_API(JSIntn) JS_FloorLog2(JSUint32 i); - -/* - * Check if __builtin_clz is available which apeared first in GCC 3.4. - * The built-in allows to speedup calculations of ceiling/floor log2, - * see bug 327129. - */ -#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) -# define JS_HAS_GCC_BUILTIN_CLZ -#endif - -/* -** Macro version of JS_CeilingLog2: Compute the log of the least power of -** 2 greater than or equal to _n. The result is returned in _log2. -*/ -#ifdef JS_HAS_GCC_BUILTIN_CLZ -/* - * Use __builtin_clz or count-leading-zeros to calculate ceil(log2(_n)). - * The macro checks for "n <= 1" and not "n != 0" as __builtin_clz(0) is - * undefined. - */ -# define JS_CEILING_LOG2(_log2,_n) \ - JS_BEGIN_MACRO \ - JS_STATIC_ASSERT(sizeof(unsigned int) == sizeof(JSUint32)); \ - unsigned int j_ = (unsigned int)(_n); \ - (_log2) = (j_ <= 1 ? 0 : 32 - __builtin_clz(j_ - 1)); \ - JS_END_MACRO -#else -# define JS_CEILING_LOG2(_log2,_n) \ - JS_BEGIN_MACRO \ - JSUint32 j_ = (JSUint32)(_n); \ - (_log2) = 0; \ - if ((j_) & ((j_)-1)) \ - (_log2) += 1; \ - if ((j_) >> 16) \ - (_log2) += 16, (j_) >>= 16; \ - if ((j_) >> 8) \ - (_log2) += 8, (j_) >>= 8; \ - if ((j_) >> 4) \ - (_log2) += 4, (j_) >>= 4; \ - if ((j_) >> 2) \ - (_log2) += 2, (j_) >>= 2; \ - if ((j_) >> 1) \ - (_log2) += 1; \ - JS_END_MACRO -#endif - -/* -** Macro version of JS_FloorLog2: Compute the log of the greatest power of -** 2 less than or equal to _n. The result is returned in _log2. -** -** This is equivalent to finding the highest set bit in the word. -*/ -#if JS_GCC_HAS_BUILTIN_CLZ -/* - * Use __builtin_clz or count-leading-zeros to calculate floor(log2(_n)). - * Since __builtin_clz(0) is undefined, the macro set the loweset bit to 1 - * to ensure 0 result when _n == 0. - */ -# define JS_FLOOR_LOG2(_log2,_n) \ - JS_BEGIN_MACRO \ - JS_STATIC_ASSERT(sizeof(unsigned int) == sizeof(JSUint32)); \ - (_log2) = 31 - __builtin_clz(((unsigned int)(_n)) | 1); \ - JS_END_MACRO -#else -# define JS_FLOOR_LOG2(_log2,_n) \ - JS_BEGIN_MACRO \ - JSUint32 j_ = (JSUint32)(_n); \ - (_log2) = 0; \ - if ((j_) >> 16) \ - (_log2) += 16, (j_) >>= 16; \ - if ((j_) >> 8) \ - (_log2) += 8, (j_) >>= 8; \ - if ((j_) >> 4) \ - (_log2) += 4, (j_) >>= 4; \ - if ((j_) >> 2) \ - (_log2) += 2, (j_) >>= 2; \ - if ((j_) >> 1) \ - (_log2) += 1; \ - JS_END_MACRO -#endif - -/* - * Internal function. - * Compute the log of the least power of 2 greater than or equal to n. - * This is a version of JS_CeilingLog2 that operates on jsuword with - * CPU-dependant size. - */ -#define JS_CEILING_LOG2W(n) ((n) <= 1 ? 0 : 1 + JS_FLOOR_LOG2W((n) - 1)) - -/* - * Internal function. - * Compute the log of the greatest power of 2 less than or equal to n. - * This is a version of JS_FloorLog2 that operates on jsuword with - * CPU-dependant size and requires that n != 0. - */ -#define JS_FLOOR_LOG2W(n) (JS_ASSERT((n) != 0), js_FloorLog2wImpl(n)) - -#ifdef JS_HAS_GCC_BUILTIN_CLZ - -# if JS_BYTES_PER_WORD == 4 -JS_STATIC_ASSERT(sizeof(unsigned) == sizeof(JSUword)); -# define js_FloorLog2wImpl(n) \ - ((JSUword)(JS_BITS_PER_WORD - 1 - __builtin_clz(n))) -# elif JS_BYTES_PER_WORD == 8 -JS_STATIC_ASSERT(sizeof(unsigned long long) == sizeof(JSUword)); -# define js_FloorLog2wImpl(n) \ - ((JSUword)(JS_BITS_PER_WORD - 1 - __builtin_clzll(n))) -# else -# error "NOT SUPPORTED" -# endif - -#else - -# if JS_BYTES_PER_WORD == 4 -# define js_FloorLog2wImpl(n) ((JSUword)JS_FloorLog2(n)) -# elif JS_BYTES_PER_WORD == 8 -extern JSUword -js_FloorLog2wImpl(JSUword n); -# else -# error "NOT SUPPORTED" -# endif - -#endif - - -JS_END_EXTERN_C -#endif /* jsbit_h___ */ diff --git a/spidermonkey/src/jsbool.c b/spidermonkey/src/jsbool.c deleted file mode 100644 index 543b4f3..0000000 --- a/spidermonkey/src/jsbool.c +++ /dev/null @@ -1,227 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS boolean implementation. - */ -#include "jsstddef.h" -#include "jstypes.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jsapi.h" -#include "jsatom.h" -#include "jsbool.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsstr.h" - -JSClass js_BooleanClass = { - "Boolean", - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Boolean), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -#if JS_HAS_TOSOURCE -#include "jsprf.h" - -static JSBool -bool_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsval v; - char buf[32]; - JSString *str; - - if (JSVAL_IS_BOOLEAN((jsval)obj)) { - v = (jsval)obj; - } else { - if (!JS_InstanceOf(cx, obj, &js_BooleanClass, argv)) - return JS_FALSE; - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - if (!JSVAL_IS_BOOLEAN(v)) - return js_obj_toSource(cx, obj, argc, argv, rval); - } - JS_snprintf(buf, sizeof buf, "(new %s(%s))", - js_BooleanClass.name, - js_boolean_strs[JSVAL_TO_BOOLEAN(v) ? 1 : 0]); - str = JS_NewStringCopyZ(cx, buf); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} -#endif - -static JSBool -bool_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsval v; - JSAtom *atom; - JSString *str; - - if (JSVAL_IS_BOOLEAN((jsval)obj)) { - v = (jsval)obj; - } else { - if (!JS_InstanceOf(cx, obj, &js_BooleanClass, argv)) - return JS_FALSE; - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - if (!JSVAL_IS_BOOLEAN(v)) - return js_obj_toString(cx, obj, argc, argv, rval); - } - atom = cx->runtime->atomState.booleanAtoms[JSVAL_TO_BOOLEAN(v) ? 1 : 0]; - str = ATOM_TO_STRING(atom); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -bool_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - if (JSVAL_IS_BOOLEAN((jsval)obj)) { - *rval = (jsval)obj; - return JS_TRUE; - } - if (!JS_InstanceOf(cx, obj, &js_BooleanClass, argv)) - return JS_FALSE; - *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - return JS_TRUE; -} - -static JSFunctionSpec boolean_methods[] = { -#if JS_HAS_TOSOURCE - {js_toSource_str, bool_toSource, 0,JSFUN_THISP_BOOLEAN,0}, -#endif - {js_toString_str, bool_toString, 0,JSFUN_THISP_BOOLEAN,0}, - {js_valueOf_str, bool_valueOf, 0,JSFUN_THISP_BOOLEAN,0}, - {0,0,0,0,0} -}; - -static JSBool -Boolean(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSBool b; - jsval bval; - - if (argc != 0) { - if (!js_ValueToBoolean(cx, argv[0], &b)) - return JS_FALSE; - bval = BOOLEAN_TO_JSVAL(b); - } else { - bval = JSVAL_FALSE; - } - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - *rval = bval; - return JS_TRUE; - } - OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, bval); - return JS_TRUE; -} - -JSObject * -js_InitBooleanClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto; - - proto = JS_InitClass(cx, obj, NULL, &js_BooleanClass, Boolean, 1, - NULL, boolean_methods, NULL, NULL); - if (!proto) - return NULL; - OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, JSVAL_FALSE); - return proto; -} - -JSObject * -js_BooleanToObject(JSContext *cx, JSBool b) -{ - JSObject *obj; - - obj = js_NewObject(cx, &js_BooleanClass, NULL, NULL); - if (!obj) - return NULL; - OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, BOOLEAN_TO_JSVAL(b)); - return obj; -} - -JSString * -js_BooleanToString(JSContext *cx, JSBool b) -{ - return ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[b ? 1 : 0]); -} - -JSBool -js_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp) -{ - JSBool b; - jsdouble d; - - if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { - b = JS_FALSE; - } else if (JSVAL_IS_OBJECT(v)) { - if (!JS_VERSION_IS_ECMA(cx)) { - if (!OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), JSTYPE_BOOLEAN, &v)) - return JS_FALSE; - if (!JSVAL_IS_BOOLEAN(v)) - v = JSVAL_TRUE; /* non-null object is true */ - b = JSVAL_TO_BOOLEAN(v); - } else { - b = JS_TRUE; - } - } else if (JSVAL_IS_STRING(v)) { - b = JSSTRING_LENGTH(JSVAL_TO_STRING(v)) ? JS_TRUE : JS_FALSE; - } else if (JSVAL_IS_INT(v)) { - b = JSVAL_TO_INT(v) ? JS_TRUE : JS_FALSE; - } else if (JSVAL_IS_DOUBLE(v)) { - d = *JSVAL_TO_DOUBLE(v); - b = (!JSDOUBLE_IS_NaN(d) && d != 0) ? JS_TRUE : JS_FALSE; - } else { - JS_ASSERT(JSVAL_IS_BOOLEAN(v)); - b = JSVAL_TO_BOOLEAN(v); - } - - *bp = b; - return JS_TRUE; -} diff --git a/spidermonkey/src/jsbool.h b/spidermonkey/src/jsbool.h deleted file mode 100644 index 8dbd218..0000000 --- a/spidermonkey/src/jsbool.h +++ /dev/null @@ -1,76 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsbool_h___ -#define jsbool_h___ -/* - * JS boolean interface. - */ - -JS_BEGIN_EXTERN_C - -/* - * Crypto-booleans, not visible to script but used internally by the engine. - * - * JSVAL_HOLE is a useful value for identifying a hole in an array. It's also - * used in the interpreter to represent "no exception pending". In general it - * can be used to represent "no value". - * - * JSVAL_ARETURN is used to throw asynchronous return for generator.close(). - */ -#define JSVAL_HOLE BOOLEAN_TO_JSVAL(2) -#define JSVAL_ARETURN BOOLEAN_TO_JSVAL(3) - -extern JSClass js_BooleanClass; - -extern JSObject * -js_InitBooleanClass(JSContext *cx, JSObject *obj); - -extern JSObject * -js_BooleanToObject(JSContext *cx, JSBool b); - -extern JSString * -js_BooleanToString(JSContext *cx, JSBool b); - -extern JSBool -js_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp); - -JS_END_EXTERN_C - -#endif /* jsbool_h___ */ diff --git a/spidermonkey/src/jsclist.h b/spidermonkey/src/jsclist.h deleted file mode 100644 index 604ec0e..0000000 --- a/spidermonkey/src/jsclist.h +++ /dev/null @@ -1,139 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsclist_h___ -#define jsclist_h___ - -#include "jstypes.h" - -/* -** Circular linked list -*/ -typedef struct JSCListStr { - struct JSCListStr *next; - struct JSCListStr *prev; -} JSCList; - -/* -** Insert element "_e" into the list, before "_l". -*/ -#define JS_INSERT_BEFORE(_e,_l) \ - JS_BEGIN_MACRO \ - (_e)->next = (_l); \ - (_e)->prev = (_l)->prev; \ - (_l)->prev->next = (_e); \ - (_l)->prev = (_e); \ - JS_END_MACRO - -/* -** Insert element "_e" into the list, after "_l". -*/ -#define JS_INSERT_AFTER(_e,_l) \ - JS_BEGIN_MACRO \ - (_e)->next = (_l)->next; \ - (_e)->prev = (_l); \ - (_l)->next->prev = (_e); \ - (_l)->next = (_e); \ - JS_END_MACRO - -/* -** Return the element following element "_e" -*/ -#define JS_NEXT_LINK(_e) \ - ((_e)->next) -/* -** Return the element preceding element "_e" -*/ -#define JS_PREV_LINK(_e) \ - ((_e)->prev) - -/* -** Append an element "_e" to the end of the list "_l" -*/ -#define JS_APPEND_LINK(_e,_l) JS_INSERT_BEFORE(_e,_l) - -/* -** Insert an element "_e" at the head of the list "_l" -*/ -#define JS_INSERT_LINK(_e,_l) JS_INSERT_AFTER(_e,_l) - -/* Return the head/tail of the list */ -#define JS_LIST_HEAD(_l) (_l)->next -#define JS_LIST_TAIL(_l) (_l)->prev - -/* -** Remove the element "_e" from it's circular list. -*/ -#define JS_REMOVE_LINK(_e) \ - JS_BEGIN_MACRO \ - (_e)->prev->next = (_e)->next; \ - (_e)->next->prev = (_e)->prev; \ - JS_END_MACRO - -/* -** Remove the element "_e" from it's circular list. Also initializes the -** linkage. -*/ -#define JS_REMOVE_AND_INIT_LINK(_e) \ - JS_BEGIN_MACRO \ - (_e)->prev->next = (_e)->next; \ - (_e)->next->prev = (_e)->prev; \ - (_e)->next = (_e); \ - (_e)->prev = (_e); \ - JS_END_MACRO - -/* -** Return non-zero if the given circular list "_l" is empty, zero if the -** circular list is not empty -*/ -#define JS_CLIST_IS_EMPTY(_l) \ - ((_l)->next == (_l)) - -/* -** Initialize a circular list -*/ -#define JS_INIT_CLIST(_l) \ - JS_BEGIN_MACRO \ - (_l)->next = (_l); \ - (_l)->prev = (_l); \ - JS_END_MACRO - -#define JS_INIT_STATIC_CLIST(_l) \ - {(_l), (_l)} - -#endif /* jsclist_h___ */ diff --git a/spidermonkey/src/jscntxt.c b/spidermonkey/src/jscntxt.c deleted file mode 100644 index 139ad9b..0000000 --- a/spidermonkey/src/jscntxt.c +++ /dev/null @@ -1,1229 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=80: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS execution context. - */ -#include "jsstddef.h" -#include -#include -#include -#include "jstypes.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ -#include "jsclist.h" -#include "jsprf.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsdbgapi.h" -#include "jsexn.h" -#include "jsgc.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" - -#ifdef JS_THREADSAFE - -/* - * Callback function to delete a JSThread info when the thread that owns it - * is destroyed. - */ -void JS_DLL_CALLBACK -js_ThreadDestructorCB(void *ptr) -{ - JSThread *thread = (JSThread *)ptr; - - if (!thread) - return; - while (!JS_CLIST_IS_EMPTY(&thread->contextList)) { - /* NB: use a temporary, as the macro evaluates its args many times. */ - JSCList *link = thread->contextList.next; - - JS_REMOVE_AND_INIT_LINK(link); - } - GSN_CACHE_CLEAR(&thread->gsnCache); - free(thread); -} - -/* - * Get current thread-local JSThread info, creating one if it doesn't exist. - * Each thread has a unique JSThread pointer. - * - * Since we are dealing with thread-local data, no lock is needed. - * - * Return a pointer to the thread local info, NULL if the system runs out - * of memory, or it failed to set thread private data (neither case is very - * likely; both are probably due to out-of-memory). It is up to the caller - * to report an error, if possible. - */ -JSThread * -js_GetCurrentThread(JSRuntime *rt) -{ - JSThread *thread; - - thread = (JSThread *)PR_GetThreadPrivate(rt->threadTPIndex); - if (!thread) { - thread = (JSThread *) calloc(1, sizeof(JSThread)); - if (!thread) - return NULL; - - if (PR_FAILURE == PR_SetThreadPrivate(rt->threadTPIndex, thread)) { - free(thread); - return NULL; - } - - JS_INIT_CLIST(&thread->contextList); - thread->id = js_CurrentThreadId(); - - /* js_SetContextThread initialize gcFreeLists as necessary. */ -#ifdef DEBUG - memset(thread->gcFreeLists, JS_FREE_PATTERN, - sizeof(thread->gcFreeLists)); -#endif - } - return thread; -} - -/* - * Sets current thread as owning thread of a context by assigning the - * thread-private info to the context. If the current thread doesn't have - * private JSThread info, create one. - */ -JSBool -js_SetContextThread(JSContext *cx) -{ - JSThread *thread = js_GetCurrentThread(cx->runtime); - - if (!thread) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - /* - * Clear gcFreeLists on each transition from 0 to 1 context active on the - * current thread. See bug 351602. - */ - if (JS_CLIST_IS_EMPTY(&thread->contextList)) - memset(thread->gcFreeLists, 0, sizeof(thread->gcFreeLists)); - - cx->thread = thread; - JS_REMOVE_LINK(&cx->threadLinks); - JS_APPEND_LINK(&cx->threadLinks, &thread->contextList); - return JS_TRUE; -} - -/* Remove the owning thread info of a context. */ -void -js_ClearContextThread(JSContext *cx) -{ - JS_REMOVE_AND_INIT_LINK(&cx->threadLinks); -#ifdef DEBUG - if (JS_CLIST_IS_EMPTY(&cx->thread->contextList)) { - memset(cx->thread->gcFreeLists, JS_FREE_PATTERN, - sizeof(cx->thread->gcFreeLists)); - } -#endif - cx->thread = NULL; -} - -#endif /* JS_THREADSAFE */ - -void -js_OnVersionChange(JSContext *cx) -{ -#ifdef DEBUG - JSVersion version = JSVERSION_NUMBER(cx); - - JS_ASSERT(version == JSVERSION_DEFAULT || version >= JSVERSION_ECMA_3); -#endif -} - -void -js_SetVersion(JSContext *cx, JSVersion version) -{ - cx->version = version; - js_OnVersionChange(cx); -} - -JSContext * -js_NewContext(JSRuntime *rt, size_t stackChunkSize) -{ - JSContext *cx; - JSBool ok, first; - JSContextCallback cxCallback; - - cx = (JSContext *) malloc(sizeof *cx); - if (!cx) - return NULL; - memset(cx, 0, sizeof *cx); - - cx->runtime = rt; -#if JS_STACK_GROWTH_DIRECTION > 0 - cx->stackLimit = (jsuword)-1; -#endif -#ifdef JS_THREADSAFE - JS_INIT_CLIST(&cx->threadLinks); - js_SetContextThread(cx); -#endif - - JS_LOCK_GC(rt); - for (;;) { - first = (rt->contextList.next == &rt->contextList); - if (rt->state == JSRTS_UP) { - JS_ASSERT(!first); - break; - } - if (rt->state == JSRTS_DOWN) { - JS_ASSERT(first); - rt->state = JSRTS_LAUNCHING; - break; - } - JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT); - } - JS_APPEND_LINK(&cx->links, &rt->contextList); - JS_UNLOCK_GC(rt); - - /* - * First we do the infallible, every-time per-context initializations. - * Should a later, fallible initialization (js_InitRegExpStatics, e.g., - * or the stuff under 'if (first)' below) fail, at least the version - * and arena-pools will be valid and safe to use (say, from the last GC - * done by js_DestroyContext). - */ - cx->version = JSVERSION_DEFAULT; - cx->jsop_eq = JSOP_EQ; - cx->jsop_ne = JSOP_NE; - JS_InitArenaPool(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval)); - JS_InitArenaPool(&cx->tempPool, "temp", 1024, sizeof(jsdouble)); - - if (!js_InitRegExpStatics(cx, &cx->regExpStatics)) { - js_DestroyContext(cx, JSDCM_NEW_FAILED); - return NULL; - } - - /* - * If cx is the first context on this runtime, initialize well-known atoms, - * keywords, numbers, and strings. If one of these steps should fail, the - * runtime will be left in a partially initialized state, with zeroes and - * nulls stored in the default-initialized remainder of the struct. We'll - * clean the runtime up under js_DestroyContext, because cx will be "last" - * as well as "first". - */ - if (first) { -#ifdef JS_THREADSAFE - JS_BeginRequest(cx); -#endif - /* - * Both atomState and the scriptFilenameTable may be left over from a - * previous episode of non-zero contexts alive in rt, so don't re-init - * either table if it's not necessary. Just repopulate atomState with - * well-known internal atoms, and with the reserved identifiers added - * by the scanner. - */ - ok = (rt->atomState.liveAtoms == 0) - ? js_InitAtomState(cx, &rt->atomState) - : js_InitPinnedAtoms(cx, &rt->atomState); - if (ok && !rt->scriptFilenameTable) - ok = js_InitRuntimeScriptState(rt); - if (ok) - ok = js_InitRuntimeNumberState(cx); - if (ok) - ok = js_InitRuntimeStringState(cx); -#ifdef JS_THREADSAFE - JS_EndRequest(cx); -#endif - if (!ok) { - js_DestroyContext(cx, JSDCM_NEW_FAILED); - return NULL; - } - - JS_LOCK_GC(rt); - rt->state = JSRTS_UP; - JS_NOTIFY_ALL_CONDVAR(rt->stateChange); - JS_UNLOCK_GC(rt); - } - - cxCallback = rt->cxCallback; - if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW)) { - js_DestroyContext(cx, JSDCM_NEW_FAILED); - return NULL; - } - return cx; -} - -void -js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) -{ - JSRuntime *rt; - JSContextCallback cxCallback; - JSBool last; - JSArgumentFormatMap *map; - JSLocalRootStack *lrs; - JSLocalRootChunk *lrc; - - rt = cx->runtime; - - if (mode != JSDCM_NEW_FAILED) { - cxCallback = rt->cxCallback; - if (cxCallback) { - /* - * JSCONTEXT_DESTROY callback is not allowed to fail and must - * return true. - */ -#ifdef DEBUG - JSBool callbackStatus = -#endif - cxCallback(cx, JSCONTEXT_DESTROY); - JS_ASSERT(callbackStatus); - } - } - - /* Remove cx from context list first. */ - JS_LOCK_GC(rt); - JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING); - JS_REMOVE_LINK(&cx->links); - last = (rt->contextList.next == &rt->contextList); - if (last) - rt->state = JSRTS_LANDING; - JS_UNLOCK_GC(rt); - - if (last) { -#ifdef JS_THREADSAFE - /* - * If cx is not in a request already, begin one now so that we wait - * for any racing GC started on a not-last context to finish, before - * we plow ahead and unpin atoms. Note that even though we begin a - * request here if necessary, we end all requests on cx below before - * forcing a final GC. This lets any not-last context destruction - * racing in another thread try to force or maybe run the GC, but by - * that point, rt->state will not be JSRTS_UP, and that GC attempt - * will return early. - */ - if (cx->requestDepth == 0) - JS_BeginRequest(cx); -#endif - - /* Unpin all pinned atoms before final GC. */ - js_UnpinPinnedAtoms(&rt->atomState); - - /* Unlock and clear GC things held by runtime pointers. */ - js_FinishRuntimeNumberState(cx); - js_FinishRuntimeStringState(cx); - - /* Clear debugging state to remove GC roots. */ - JS_ClearAllTraps(cx); - JS_ClearAllWatchPoints(cx); - } - - /* - * Remove more GC roots in regExpStatics, then collect garbage. - * XXX anti-modularity alert: we rely on the call to js_RemoveRoot within - * XXX this function call to wait for any racing GC to complete, in the - * XXX case where JS_DestroyContext is called outside of a request on cx - */ - js_FreeRegExpStatics(cx, &cx->regExpStatics); - -#ifdef JS_THREADSAFE - /* - * Destroying a context implicitly calls JS_EndRequest(). Also, we must - * end our request here in case we are "last" -- in that event, another - * js_DestroyContext that was not last might be waiting in the GC for our - * request to end. We'll let it run below, just before we do the truly - * final GC and then free atom state. - * - * At this point, cx must be inaccessible to other threads. It's off the - * rt->contextList, and it should not be reachable via any object private - * data structure. - */ - while (cx->requestDepth != 0) - JS_EndRequest(cx); -#endif - - if (last) { - js_GC(cx, GC_LAST_CONTEXT); - - /* Try to free atom state, now that no unrooted scripts survive. */ - if (rt->atomState.liveAtoms == 0) - js_FreeAtomState(cx, &rt->atomState); - - /* Also free the script filename table if it exists and is empty. */ - if (rt->scriptFilenameTable && rt->scriptFilenameTable->nentries == 0) - js_FinishRuntimeScriptState(rt); - - /* - * Free the deflated string cache, but only after the last GC has - * collected all unleaked strings. - */ - js_FinishDeflatedStringCache(rt); - - /* Take the runtime down, now that it has no contexts or atoms. */ - JS_LOCK_GC(rt); - rt->state = JSRTS_DOWN; - JS_NOTIFY_ALL_CONDVAR(rt->stateChange); - JS_UNLOCK_GC(rt); - } else { - if (mode == JSDCM_FORCE_GC) - js_GC(cx, GC_NORMAL); - else if (mode == JSDCM_MAYBE_GC) - JS_MaybeGC(cx); - } - - /* Free the stuff hanging off of cx. */ - JS_FinishArenaPool(&cx->stackPool); - JS_FinishArenaPool(&cx->tempPool); - - if (cx->lastMessage) - free(cx->lastMessage); - - /* Remove any argument formatters. */ - map = cx->argumentFormatMap; - while (map) { - JSArgumentFormatMap *temp = map; - map = map->next; - JS_free(cx, temp); - } - - /* Destroy the resolve recursion damper. */ - if (cx->resolvingTable) { - JS_DHashTableDestroy(cx->resolvingTable); - cx->resolvingTable = NULL; - } - - lrs = cx->localRootStack; - if (lrs) { - while ((lrc = lrs->topChunk) != &lrs->firstChunk) { - lrs->topChunk = lrc->down; - JS_free(cx, lrc); - } - JS_free(cx, lrs); - } - -#ifdef JS_THREADSAFE - js_ClearContextThread(cx); -#endif - - /* Finally, free cx itself. */ - free(cx); -} - -JSBool -js_ValidContextPointer(JSRuntime *rt, JSContext *cx) -{ - JSCList *cl; - - for (cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) { - if (cl == &cx->links) - return JS_TRUE; - } - JS_RUNTIME_METER(rt, deadContexts); - return JS_FALSE; -} - -JSContext * -js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp) -{ - JSContext *cx = *iterp; - - if (unlocked) - JS_LOCK_GC(rt); - if (!cx) - cx = (JSContext *)&rt->contextList; - cx = (JSContext *)cx->links.next; - if (&cx->links == &rt->contextList) - cx = NULL; - *iterp = cx; - if (unlocked) - JS_UNLOCK_GC(rt); - return cx; -} - -JS_STATIC_DLL_CALLBACK(const void *) -resolving_GetKey(JSDHashTable *table, JSDHashEntryHdr *hdr) -{ - JSResolvingEntry *entry = (JSResolvingEntry *)hdr; - - return &entry->key; -} - -JS_STATIC_DLL_CALLBACK(JSDHashNumber) -resolving_HashKey(JSDHashTable *table, const void *ptr) -{ - const JSResolvingKey *key = (const JSResolvingKey *)ptr; - - return ((JSDHashNumber)JS_PTR_TO_UINT32(key->obj) >> JSVAL_TAGBITS) ^ key->id; -} - -JS_PUBLIC_API(JSBool) -resolving_MatchEntry(JSDHashTable *table, - const JSDHashEntryHdr *hdr, - const void *ptr) -{ - const JSResolvingEntry *entry = (const JSResolvingEntry *)hdr; - const JSResolvingKey *key = (const JSResolvingKey *)ptr; - - return entry->key.obj == key->obj && entry->key.id == key->id; -} - -static const JSDHashTableOps resolving_dhash_ops = { - JS_DHashAllocTable, - JS_DHashFreeTable, - resolving_GetKey, - resolving_HashKey, - resolving_MatchEntry, - JS_DHashMoveEntryStub, - JS_DHashClearEntryStub, - JS_DHashFinalizeStub, - NULL -}; - -JSBool -js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, - JSResolvingEntry **entryp) -{ - JSDHashTable *table; - JSResolvingEntry *entry; - - table = cx->resolvingTable; - if (!table) { - table = JS_NewDHashTable(&resolving_dhash_ops, NULL, - sizeof(JSResolvingEntry), - JS_DHASH_MIN_SIZE); - if (!table) - goto outofmem; - cx->resolvingTable = table; - } - - entry = (JSResolvingEntry *) - JS_DHashTableOperate(table, key, JS_DHASH_ADD); - if (!entry) - goto outofmem; - - if (entry->flags & flag) { - /* An entry for (key, flag) exists already -- dampen recursion. */ - entry = NULL; - } else { - /* Fill in key if we were the first to add entry, then set flag. */ - if (!entry->key.obj) - entry->key = *key; - entry->flags |= flag; - } - *entryp = entry; - return JS_TRUE; - -outofmem: - JS_ReportOutOfMemory(cx); - return JS_FALSE; -} - -void -js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, - JSResolvingEntry *entry, uint32 generation) -{ - JSDHashTable *table; - - /* - * Clear flag from entry->flags and return early if other flags remain. - * We must take care to re-lookup entry if the table has changed since - * it was found by js_StartResolving. - */ - table = cx->resolvingTable; - if (!entry || table->generation != generation) { - entry = (JSResolvingEntry *) - JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP); - } - JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&entry->hdr)); - entry->flags &= ~flag; - if (entry->flags) - return; - - /* - * Do a raw remove only if fewer entries were removed than would cause - * alpha to be less than .5 (alpha is at most .75). Otherwise, we just - * call JS_DHashTableOperate to re-lookup the key and remove its entry, - * compressing or shrinking the table as needed. - */ - if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2) - JS_DHashTableRawRemove(table, &entry->hdr); - else - JS_DHashTableOperate(table, key, JS_DHASH_REMOVE); -} - -JSBool -js_EnterLocalRootScope(JSContext *cx) -{ - JSLocalRootStack *lrs; - int mark; - - lrs = cx->localRootStack; - if (!lrs) { - lrs = (JSLocalRootStack *) JS_malloc(cx, sizeof *lrs); - if (!lrs) - return JS_FALSE; - lrs->scopeMark = JSLRS_NULL_MARK; - lrs->rootCount = 0; - lrs->topChunk = &lrs->firstChunk; - lrs->firstChunk.down = NULL; - cx->localRootStack = lrs; - } - - /* Push lrs->scopeMark to save it for restore when leaving. */ - mark = js_PushLocalRoot(cx, lrs, INT_TO_JSVAL(lrs->scopeMark)); - if (mark < 0) - return JS_FALSE; - lrs->scopeMark = (uint32) mark; - return JS_TRUE; -} - -void -js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval) -{ - JSLocalRootStack *lrs; - uint32 mark, m, n; - JSLocalRootChunk *lrc; - - /* Defend against buggy native callers. */ - lrs = cx->localRootStack; - JS_ASSERT(lrs && lrs->rootCount != 0); - if (!lrs || lrs->rootCount == 0) - return; - - mark = lrs->scopeMark; - JS_ASSERT(mark != JSLRS_NULL_MARK); - if (mark == JSLRS_NULL_MARK) - return; - - /* Free any chunks being popped by this leave operation. */ - m = mark >> JSLRS_CHUNK_SHIFT; - n = (lrs->rootCount - 1) >> JSLRS_CHUNK_SHIFT; - while (n > m) { - lrc = lrs->topChunk; - JS_ASSERT(lrc != &lrs->firstChunk); - lrs->topChunk = lrc->down; - JS_free(cx, lrc); - --n; - } - - /* - * Pop the scope, restoring lrs->scopeMark. If rval is a GC-thing, push - * it on the caller's scope, or store it in lastInternalResult if we are - * leaving the outermost scope. We don't need to allocate a new lrc - * because we can overwrite the old mark's slot with rval. - */ - lrc = lrs->topChunk; - m = mark & JSLRS_CHUNK_MASK; - lrs->scopeMark = (uint32) JSVAL_TO_INT(lrc->roots[m]); - if (JSVAL_IS_GCTHING(rval) && !JSVAL_IS_NULL(rval)) { - if (mark == 0) { - cx->weakRoots.lastInternalResult = rval; - } else { - /* - * Increment m to avoid the "else if (m == 0)" case below. If - * rval is not a GC-thing, that case would take care of freeing - * any chunk that contained only the old mark. Since rval *is* - * a GC-thing here, we want to reuse that old mark's slot. - */ - lrc->roots[m++] = rval; - ++mark; - } - } - lrs->rootCount = (uint32) mark; - - /* - * Free the stack eagerly, risking malloc churn. The alternative would - * require an lrs->entryCount member, maintained by Enter and Leave, and - * tested by the GC in addition to the cx->localRootStack non-null test. - * - * That approach would risk hoarding 264 bytes (net) per context. Right - * now it seems better to give fresh (dirty in CPU write-back cache, and - * the data is no longer needed) memory back to the malloc heap. - */ - if (mark == 0) { - cx->localRootStack = NULL; - JS_free(cx, lrs); - } else if (m == 0) { - lrs->topChunk = lrc->down; - JS_free(cx, lrc); - } -} - -void -js_ForgetLocalRoot(JSContext *cx, jsval v) -{ - JSLocalRootStack *lrs; - uint32 i, j, m, n, mark; - JSLocalRootChunk *lrc, *lrc2; - jsval top; - - lrs = cx->localRootStack; - JS_ASSERT(lrs && lrs->rootCount); - if (!lrs || lrs->rootCount == 0) - return; - - /* Prepare to pop the top-most value from the stack. */ - n = lrs->rootCount - 1; - m = n & JSLRS_CHUNK_MASK; - lrc = lrs->topChunk; - top = lrc->roots[m]; - - /* Be paranoid about calls on an empty scope. */ - mark = lrs->scopeMark; - JS_ASSERT(mark < n); - if (mark >= n) - return; - - /* If v was not the last root pushed in the top scope, find it. */ - if (top != v) { - /* Search downward in case v was recently pushed. */ - i = n; - j = m; - lrc2 = lrc; - while (--i > mark) { - if (j == 0) - lrc2 = lrc2->down; - j = i & JSLRS_CHUNK_MASK; - if (lrc2->roots[j] == v) - break; - } - - /* If we didn't find v in this scope, assert and bail out. */ - JS_ASSERT(i != mark); - if (i == mark) - return; - - /* Swap top and v so common tail code can pop v. */ - lrc2->roots[j] = top; - } - - /* Pop the last value from the stack. */ - lrc->roots[m] = JSVAL_NULL; - lrs->rootCount = n; - if (m == 0) { - JS_ASSERT(n != 0); - JS_ASSERT(lrc != &lrs->firstChunk); - lrs->topChunk = lrc->down; - JS_free(cx, lrc); - } -} - -int -js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v) -{ - uint32 n, m; - JSLocalRootChunk *lrc; - - n = lrs->rootCount; - m = n & JSLRS_CHUNK_MASK; - if (n == 0 || m != 0) { - /* - * At start of first chunk, or not at start of a non-first top chunk. - * Check for lrs->rootCount overflow. - */ - if ((uint32)(n + 1) == 0) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_TOO_MANY_LOCAL_ROOTS); - return -1; - } - lrc = lrs->topChunk; - JS_ASSERT(n != 0 || lrc == &lrs->firstChunk); - } else { - /* - * After lrs->firstChunk, trying to index at a power-of-two chunk - * boundary: need a new chunk. - */ - lrc = (JSLocalRootChunk *) JS_malloc(cx, sizeof *lrc); - if (!lrc) - return -1; - lrc->down = lrs->topChunk; - lrs->topChunk = lrc; - } - lrs->rootCount = n + 1; - lrc->roots[m] = v; - return (int) n; -} - -void -js_MarkLocalRoots(JSContext *cx, JSLocalRootStack *lrs) -{ - uint32 n, m, mark; - JSLocalRootChunk *lrc; - - n = lrs->rootCount; - if (n == 0) - return; - - mark = lrs->scopeMark; - lrc = lrs->topChunk; - do { - while (--n > mark) { -#ifdef GC_MARK_DEBUG - char name[22]; - JS_snprintf(name, sizeof name, "", n); -#endif - m = n & JSLRS_CHUNK_MASK; - JS_ASSERT(JSVAL_IS_GCTHING(lrc->roots[m])); - GC_MARK(cx, JSVAL_TO_GCTHING(lrc->roots[m]), name); - if (m == 0) - lrc = lrc->down; - } - m = n & JSLRS_CHUNK_MASK; - mark = JSVAL_TO_INT(lrc->roots[m]); - if (m == 0) - lrc = lrc->down; - } while (n != 0); - JS_ASSERT(!lrc); -} - -static void -ReportError(JSContext *cx, const char *message, JSErrorReport *reportp) -{ - /* - * Check the error report, and set a JavaScript-catchable exception - * if the error is defined to have an associated exception. If an - * exception is thrown, then the JSREPORT_EXCEPTION flag will be set - * on the error report, and exception-aware hosts should ignore it. - */ - JS_ASSERT(reportp); - if (reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION) - reportp->flags |= JSREPORT_EXCEPTION; - - /* - * Call the error reporter only if an exception wasn't raised. - * - * If an exception was raised, then we call the debugErrorHook - * (if present) to give it a chance to see the error before it - * propagates out of scope. This is needed for compatability - * with the old scheme. - */ - if (!js_ErrorToException(cx, message, reportp)) { - js_ReportErrorAgain(cx, message, reportp); - } else if (cx->runtime->debugErrorHook && cx->errorReporter) { - JSDebugErrorHook hook = cx->runtime->debugErrorHook; - /* test local in case debugErrorHook changed on another thread */ - if (hook) - hook(cx, message, reportp, cx->runtime->debugErrorHookData); - } -} - -/* - * We don't post an exception in this case, since doing so runs into - * complications of pre-allocating an exception object which required - * running the Exception class initializer early etc. - * Instead we just invoke the errorReporter with an "Out Of Memory" - * type message, and then hope the process ends swiftly. - */ -void -js_ReportOutOfMemory(JSContext *cx) -{ - JSStackFrame *fp; - JSErrorReport report; - JSErrorReporter onError = cx->errorReporter; - - /* Get the message for this error, but we won't expand any arguments. */ - const JSErrorFormatString *efs = - js_GetLocalizedErrorMessage(cx, NULL, NULL, JSMSG_OUT_OF_MEMORY); - const char *msg = efs ? efs->format : "Out of memory"; - - /* Fill out the report, but don't do anything that requires allocation. */ - memset(&report, 0, sizeof (struct JSErrorReport)); - report.flags = JSREPORT_ERROR; - report.errorNumber = JSMSG_OUT_OF_MEMORY; - - /* - * Walk stack until we find a frame that is associated with some script - * rather than a native frame. - */ - for (fp = cx->fp; fp; fp = fp->down) { - if (fp->script && fp->pc) { - report.filename = fp->script->filename; - report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc); - break; - } - } - - /* - * If debugErrorHook is present then we give it a chance to veto - * sending the error on to the regular ErrorReporter. - */ - if (onError) { - JSDebugErrorHook hook = cx->runtime->debugErrorHook; - if (hook && - !hook(cx, msg, &report, cx->runtime->debugErrorHookData)) { - onError = NULL; - } - } - - if (onError) - onError(cx, msg, &report); -} - -JSBool -js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap) -{ - char *message; - jschar *ucmessage; - size_t messagelen; - JSStackFrame *fp; - JSErrorReport report; - JSBool warning; - - if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx)) - return JS_TRUE; - - message = JS_vsmprintf(format, ap); - if (!message) - return JS_FALSE; - messagelen = strlen(message); - - memset(&report, 0, sizeof (struct JSErrorReport)); - report.flags = flags; - report.errorNumber = JSMSG_USER_DEFINED_ERROR; - report.ucmessage = ucmessage = js_InflateString(cx, message, &messagelen); - - /* Find the top-most active script frame, for best line number blame. */ - for (fp = cx->fp; fp; fp = fp->down) { - if (fp->script && fp->pc) { - report.filename = fp->script->filename; - report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc); - break; - } - } - - warning = JSREPORT_IS_WARNING(report.flags); - if (warning && JS_HAS_WERROR_OPTION(cx)) { - report.flags &= ~JSREPORT_WARNING; - warning = JS_FALSE; - } - - ReportError(cx, message, &report); - free(message); - JS_free(cx, ucmessage); - return warning; -} - -/* - * The arguments from ap need to be packaged up into an array and stored - * into the report struct. - * - * The format string addressed by the error number may contain operands - * identified by the format {N}, where N is a decimal digit. Each of these - * is to be replaced by the Nth argument from the va_list. The complete - * message is placed into reportp->ucmessage converted to a JSString. - * - * Returns true if the expansion succeeds (can fail if out of memory). - */ -JSBool -js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, - void *userRef, const uintN errorNumber, - char **messagep, JSErrorReport *reportp, - JSBool *warningp, JSBool charArgs, va_list ap) -{ - const JSErrorFormatString *efs; - int i; - int argCount; - - *warningp = JSREPORT_IS_WARNING(reportp->flags); - if (*warningp && JS_HAS_WERROR_OPTION(cx)) { - reportp->flags &= ~JSREPORT_WARNING; - *warningp = JS_FALSE; - } - - *messagep = NULL; - - /* Most calls supply js_GetErrorMessage; if this is so, assume NULL. */ - if (!callback || callback == js_GetErrorMessage) - efs = js_GetLocalizedErrorMessage(cx, userRef, NULL, errorNumber); - else - efs = callback(userRef, NULL, errorNumber); - if (efs) { - size_t totalArgsLength = 0; - size_t argLengths[10]; /* only {0} thru {9} supported */ - argCount = efs->argCount; - JS_ASSERT(argCount <= 10); - if (argCount > 0) { - /* - * Gather the arguments into an array, and accumulate - * their sizes. We allocate 1 more than necessary and - * null it out to act as the caboose when we free the - * pointers later. - */ - reportp->messageArgs = (const jschar **) - JS_malloc(cx, sizeof(jschar *) * (argCount + 1)); - if (!reportp->messageArgs) - return JS_FALSE; - reportp->messageArgs[argCount] = NULL; - for (i = 0; i < argCount; i++) { - if (charArgs) { - char *charArg = va_arg(ap, char *); - size_t charArgLength = strlen(charArg); - reportp->messageArgs[i] - = js_InflateString(cx, charArg, &charArgLength); - if (!reportp->messageArgs[i]) - goto error; - } else { - reportp->messageArgs[i] = va_arg(ap, jschar *); - } - argLengths[i] = js_strlen(reportp->messageArgs[i]); - totalArgsLength += argLengths[i]; - } - /* NULL-terminate for easy copying. */ - reportp->messageArgs[i] = NULL; - } - /* - * Parse the error format, substituting the argument X - * for {X} in the format. - */ - if (argCount > 0) { - if (efs->format) { - jschar *buffer, *fmt, *out; - int expandedArgs = 0; - size_t expandedLength; - size_t len = strlen(efs->format); - - buffer = fmt = js_InflateString (cx, efs->format, &len); - if (!buffer) - goto error; - expandedLength = len - - (3 * argCount) /* exclude the {n} */ - + totalArgsLength; - - /* - * Note - the above calculation assumes that each argument - * is used once and only once in the expansion !!! - */ - reportp->ucmessage = out = (jschar *) - JS_malloc(cx, (expandedLength + 1) * sizeof(jschar)); - if (!out) { - JS_free (cx, buffer); - goto error; - } - while (*fmt) { - if (*fmt == '{') { - if (isdigit(fmt[1])) { - int d = JS7_UNDEC(fmt[1]); - JS_ASSERT(d < argCount); - js_strncpy(out, reportp->messageArgs[d], - argLengths[d]); - out += argLengths[d]; - fmt += 3; - expandedArgs++; - continue; - } - } - *out++ = *fmt++; - } - JS_ASSERT(expandedArgs == argCount); - *out = 0; - JS_free (cx, buffer); - *messagep = - js_DeflateString(cx, reportp->ucmessage, - (size_t)(out - reportp->ucmessage)); - if (!*messagep) - goto error; - } - } else { - /* - * Zero arguments: the format string (if it exists) is the - * entire message. - */ - if (efs->format) { - size_t len; - *messagep = JS_strdup(cx, efs->format); - if (!*messagep) - goto error; - len = strlen(*messagep); - reportp->ucmessage = js_InflateString(cx, *messagep, &len); - if (!reportp->ucmessage) - goto error; - } - } - } - if (*messagep == NULL) { - /* where's the right place for this ??? */ - const char *defaultErrorMessage - = "No error message available for error number %d"; - size_t nbytes = strlen(defaultErrorMessage) + 16; - *messagep = (char *)JS_malloc(cx, nbytes); - if (!*messagep) - goto error; - JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber); - } - return JS_TRUE; - -error: - if (reportp->messageArgs) { - /* free the arguments only if we allocated them */ - if (charArgs) { - i = 0; - while (reportp->messageArgs[i]) - JS_free(cx, (void *)reportp->messageArgs[i++]); - } - JS_free(cx, (void *)reportp->messageArgs); - reportp->messageArgs = NULL; - } - if (reportp->ucmessage) { - JS_free(cx, (void *)reportp->ucmessage); - reportp->ucmessage = NULL; - } - if (*messagep) { - JS_free(cx, (void *)*messagep); - *messagep = NULL; - } - return JS_FALSE; -} - -JSBool -js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback, - void *userRef, const uintN errorNumber, - JSBool charArgs, va_list ap) -{ - JSStackFrame *fp; - JSErrorReport report; - char *message; - JSBool warning; - - if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx)) - return JS_TRUE; - - memset(&report, 0, sizeof (struct JSErrorReport)); - report.flags = flags; - report.errorNumber = errorNumber; - - /* - * If we can't find out where the error was based on the current frame, - * see if the next frame has a script/pc combo we can use. - */ - for (fp = cx->fp; fp; fp = fp->down) { - if (fp->script && fp->pc) { - report.filename = fp->script->filename; - report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc); - break; - } - } - - if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber, - &message, &report, &warning, charArgs, ap)) { - return JS_FALSE; - } - - ReportError(cx, message, &report); - - if (message) - JS_free(cx, message); - if (report.messageArgs) { - /* - * js_ExpandErrorArguments owns its messageArgs only if it had to - * inflate the arguments (from regular |char *|s). - */ - if (charArgs) { - int i = 0; - while (report.messageArgs[i]) - JS_free(cx, (void *)report.messageArgs[i++]); - } - JS_free(cx, (void *)report.messageArgs); - } - if (report.ucmessage) - JS_free(cx, (void *)report.ucmessage); - - return warning; -} - -JS_FRIEND_API(void) -js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *reportp) -{ - JSErrorReporter onError; - - if (!message) - return; - - if (cx->lastMessage) - free(cx->lastMessage); - cx->lastMessage = JS_strdup(cx, message); - if (!cx->lastMessage) - return; - onError = cx->errorReporter; - - /* - * If debugErrorHook is present then we give it a chance to veto - * sending the error on to the regular ErrorReporter. - */ - if (onError) { - JSDebugErrorHook hook = cx->runtime->debugErrorHook; - if (hook && - !hook(cx, cx->lastMessage, reportp, - cx->runtime->debugErrorHookData)) { - onError = NULL; - } - } - if (onError) - onError(cx, cx->lastMessage, reportp); -} - -void -js_ReportIsNotDefined(JSContext *cx, const char *name) -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_DEFINED, name); -} - -#if defined DEBUG && defined XP_UNIX -/* For gdb usage. */ -void js_traceon(JSContext *cx) { cx->tracefp = stderr; } -void js_traceoff(JSContext *cx) { cx->tracefp = NULL; } -#endif - -JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = { -#define MSG_DEF(name, number, count, exception, format) \ - { format, count, exception } , -#include "js.msg" -#undef MSG_DEF -}; - -const JSErrorFormatString * -js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber) -{ - if ((errorNumber > 0) && (errorNumber < JSErr_Limit)) - return &js_ErrorFormatString[errorNumber]; - return NULL; -} diff --git a/spidermonkey/src/jscntxt.h b/spidermonkey/src/jscntxt.h deleted file mode 100644 index 7ca678e..0000000 --- a/spidermonkey/src/jscntxt.h +++ /dev/null @@ -1,1013 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jscntxt_h___ -#define jscntxt_h___ -/* - * JS execution context. - */ -#include "jsarena.h" /* Added by JSIFY */ -#include "jsclist.h" -#include "jslong.h" -#include "jsatom.h" -#include "jsconfig.h" -#include "jsdhash.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jsobj.h" -#include "jsprvtd.h" -#include "jspubtd.h" -#include "jsregexp.h" -#include "jsutil.h" - -JS_BEGIN_EXTERN_C - -/* - * js_GetSrcNote cache to avoid O(n^2) growth in finding a source note for a - * given pc in a script. - */ -typedef struct JSGSNCache { - JSScript *script; - JSDHashTable table; -#ifdef JS_GSNMETER - uint32 hits; - uint32 misses; - uint32 fills; - uint32 clears; -# define GSN_CACHE_METER(cache,cnt) (++(cache)->cnt) -#else -# define GSN_CACHE_METER(cache,cnt) /* nothing */ -#endif -} JSGSNCache; - -#define GSN_CACHE_CLEAR(cache) \ - JS_BEGIN_MACRO \ - (cache)->script = NULL; \ - if ((cache)->table.ops) { \ - JS_DHashTableFinish(&(cache)->table); \ - (cache)->table.ops = NULL; \ - } \ - GSN_CACHE_METER(cache, clears); \ - JS_END_MACRO - -/* These helper macros take a cx as parameter and operate on its GSN cache. */ -#define JS_CLEAR_GSN_CACHE(cx) GSN_CACHE_CLEAR(&JS_GSN_CACHE(cx)) -#define JS_METER_GSN_CACHE(cx,cnt) GSN_CACHE_METER(&JS_GSN_CACHE(cx), cnt) - -#ifdef JS_THREADSAFE - -/* - * Structure uniquely representing a thread. It holds thread-private data - * that can be accessed without a global lock. - */ -struct JSThread { - /* Linked list of all contexts active on this thread. */ - JSCList contextList; - - /* Opaque thread-id, from NSPR's PR_GetCurrentThread(). */ - jsword id; - - /* Thread-local gc free lists array. */ - JSGCThing *gcFreeLists[GC_NUM_FREELISTS]; - - /* - * Thread-local version of JSRuntime.gcMallocBytes to avoid taking - * locks on each JS_malloc. - */ - uint32 gcMallocBytes; - -#if JS_HAS_GENERATORS - /* Flag indicating that the current thread is executing close hooks. */ - JSBool gcRunningCloseHooks; -#endif - - /* - * Store the GSN cache in struct JSThread, not struct JSContext, both to - * save space and to simplify cleanup in js_GC. Any embedding (Firefox - * or another Gecko application) that uses many contexts per thread is - * unlikely to interleave js_GetSrcNote-intensive loops in the decompiler - * among two or more contexts running script in one thread. - */ - JSGSNCache gsnCache; -}; - -#define JS_GSN_CACHE(cx) ((cx)->thread->gsnCache) - -extern void JS_DLL_CALLBACK -js_ThreadDestructorCB(void *ptr); - -extern JSBool -js_SetContextThread(JSContext *cx); - -extern void -js_ClearContextThread(JSContext *cx); - -extern JSThread * -js_GetCurrentThread(JSRuntime *rt); - -#endif /* JS_THREADSAFE */ - -typedef enum JSDestroyContextMode { - JSDCM_NO_GC, - JSDCM_MAYBE_GC, - JSDCM_FORCE_GC, - JSDCM_NEW_FAILED -} JSDestroyContextMode; - -typedef enum JSRuntimeState { - JSRTS_DOWN, - JSRTS_LAUNCHING, - JSRTS_UP, - JSRTS_LANDING -} JSRuntimeState; - -typedef struct JSPropertyTreeEntry { - JSDHashEntryHdr hdr; - JSScopeProperty *child; -} JSPropertyTreeEntry; - -/* - * Forward declaration for opaque JSRuntime.nativeIteratorStates. - */ -typedef struct JSNativeIteratorState JSNativeIteratorState; - -struct JSRuntime { - /* Runtime state, synchronized by the stateChange/gcLock condvar/lock. */ - JSRuntimeState state; - - /* Context create/destroy callback. */ - JSContextCallback cxCallback; - - /* Garbage collector state, used by jsgc.c. */ - JSGCArenaList gcArenaList[GC_NUM_FREELISTS]; - JSDHashTable gcRootsHash; - JSDHashTable *gcLocksHash; - jsrefcount gcKeepAtoms; - uint32 gcBytes; - uint32 gcLastBytes; - uint32 gcMaxBytes; - uint32 gcMaxMallocBytes; - uint32 gcLevel; - uint32 gcNumber; - - /* - * NB: do not pack another flag here by claiming gcPadding unless the new - * flag is written only by the GC thread. Atomic updates to packed bytes - * are not guaranteed, so stores issued by one thread may be lost due to - * unsynchronized read-modify-write cycles on other threads. - */ - JSPackedBool gcPoke; - JSPackedBool gcRunning; - uint16 gcPadding; - - JSGCCallback gcCallback; - uint32 gcMallocBytes; - JSGCArena *gcUnscannedArenaStackTop; -#ifdef DEBUG - size_t gcUnscannedBagSize; -#endif - - /* - * API compatibility requires keeping GCX_PRIVATE bytes separate from the - * original GC types' byte tally. Otherwise embeddings that configure a - * good limit for pre-GCX_PRIVATE versions of the engine will see memory - * over-pressure too often, possibly leading to failed last-ditch GCs. - * - * The new XML GC-thing types do add to gcBytes, and they're larger than - * the original GC-thing type size (8 bytes on most architectures). So a - * user who enables E4X may want to increase the maxbytes value passed to - * JS_NewRuntime. TODO: Note this in the API docs. - */ - uint32 gcPrivateBytes; - - /* - * Table for tracking iterators to ensure that we close iterator's state - * before finalizing the iterable object. - */ - JSPtrTable gcIteratorTable; - -#if JS_HAS_GENERATORS - /* Runtime state to support close hooks. */ - JSGCCloseState gcCloseState; -#endif - -#ifdef JS_GCMETER - JSGCStats gcStats; -#endif - - /* Literal table maintained by jsatom.c functions. */ - JSAtomState atomState; - - /* Random number generator state, used by jsmath.c. */ - JSBool rngInitialized; - int64 rngMultiplier; - int64 rngAddend; - int64 rngMask; - int64 rngSeed; - jsdouble rngDscale; - - /* Well-known numbers held for use by this runtime's contexts. */ - jsdouble *jsNaN; - jsdouble *jsNegativeInfinity; - jsdouble *jsPositiveInfinity; - -#ifdef JS_THREADSAFE - JSLock *deflatedStringCacheLock; -#endif - JSHashTable *deflatedStringCache; -#ifdef DEBUG - uint32 deflatedStringCacheBytes; -#endif - - /* Empty string held for use by this runtime's contexts. */ - JSString *emptyString; - - /* List of active contexts sharing this runtime; protected by gcLock. */ - JSCList contextList; - - /* These are used for debugging -- see jsprvtd.h and jsdbgapi.h. */ - JSTrapHandler interruptHandler; - void *interruptHandlerData; - JSNewScriptHook newScriptHook; - void *newScriptHookData; - JSDestroyScriptHook destroyScriptHook; - void *destroyScriptHookData; - JSTrapHandler debuggerHandler; - void *debuggerHandlerData; - JSSourceHandler sourceHandler; - void *sourceHandlerData; - JSInterpreterHook executeHook; - void *executeHookData; - JSInterpreterHook callHook; - void *callHookData; - JSObjectHook objectHook; - void *objectHookData; - JSTrapHandler throwHook; - void *throwHookData; - JSDebugErrorHook debugErrorHook; - void *debugErrorHookData; - - /* More debugging state, see jsdbgapi.c. */ - JSCList trapList; - JSCList watchPointList; - - /* Weak links to properties, indexed by quickened get/set opcodes. */ - /* XXX must come after JSCLists or MSVC alignment bug bites empty lists */ - JSPropertyCache propertyCache; - - /* Client opaque pointer */ - void *data; - -#ifdef JS_THREADSAFE - /* These combine to interlock the GC and new requests. */ - PRLock *gcLock; - PRCondVar *gcDone; - PRCondVar *requestDone; - uint32 requestCount; - JSThread *gcThread; - - /* Lock and owning thread pointer for JS_LOCK_RUNTIME. */ - PRLock *rtLock; -#ifdef DEBUG - jsword rtLockOwner; -#endif - - /* Used to synchronize down/up state change; protected by gcLock. */ - PRCondVar *stateChange; - - /* Used to serialize cycle checks when setting __proto__ or __parent__. */ - PRLock *setSlotLock; - PRCondVar *setSlotDone; - JSBool setSlotBusy; - JSScope *setSlotScope; /* deadlock avoidance, see jslock.c */ - - /* - * State for sharing single-threaded scopes, once a second thread tries to - * lock a scope. The scopeSharingDone condvar is protected by rt->gcLock, - * to minimize number of locks taken in JS_EndRequest. - * - * The scopeSharingTodo linked list is likewise "global" per runtime, not - * one-list-per-context, to conserve space over all contexts, optimizing - * for the likely case that scopes become shared rarely, and among a very - * small set of threads (contexts). - */ - PRCondVar *scopeSharingDone; - JSScope *scopeSharingTodo; - -/* - * Magic terminator for the rt->scopeSharingTodo linked list, threaded through - * scope->u.link. This hack allows us to test whether a scope is on the list - * by asking whether scope->u.link is non-null. We use a large, likely bogus - * pointer here to distinguish this value from any valid u.count (small int) - * value. - */ -#define NO_SCOPE_SHARING_TODO ((JSScope *) 0xfeedbeef) - - /* - * The index for JSThread info, returned by PR_NewThreadPrivateIndex. - * The value is visible and shared by all threads, but the data is - * private to each thread. - */ - PRUintn threadTPIndex; -#endif /* JS_THREADSAFE */ - - /* - * Check property accessibility for objects of arbitrary class. Used at - * present to check f.caller accessibility for any function object f. - */ - JSCheckAccessOp checkObjectAccess; - - /* Security principals serialization support. */ - JSPrincipalsTranscoder principalsTranscoder; - - /* Optional hook to find principals for an object in this runtime. */ - JSObjectPrincipalsFinder findObjectPrincipals; - - /* - * Shared scope property tree, and arena-pool for allocating its nodes. - * The propertyRemovals counter is incremented for every js_ClearScope, - * and for each js_RemoveScopeProperty that frees a slot in an object. - * See js_NativeGet and js_NativeSet in jsobj.c. - */ - JSDHashTable propertyTreeHash; - JSScopeProperty *propertyFreeList; - JSArenaPool propertyArenaPool; - int32 propertyRemovals; - - /* Script filename table. */ - struct JSHashTable *scriptFilenameTable; - JSCList scriptFilenamePrefixes; -#ifdef JS_THREADSAFE - PRLock *scriptFilenameTableLock; -#endif - - /* Number localization, used by jsnum.c */ - const char *thousandsSeparator; - const char *decimalSeparator; - const char *numGrouping; - - /* - * Weak references to lazily-created, well-known XML singletons. - * - * NB: Singleton objects must be carefully disconnected from the rest of - * the object graph usually associated with a JSContext's global object, - * including the set of standard class objects. See jsxml.c for details. - */ - JSObject *anynameObject; - JSObject *functionNamespaceObject; - - /* - * A helper list for the GC, so it can mark native iterator states. See - * js_MarkNativeIteratorStates for details. - */ - JSNativeIteratorState *nativeIteratorStates; - -#ifndef JS_THREADSAFE - /* - * For thread-unsafe embeddings, the GSN cache lives in the runtime and - * not each context, since we expect it to be filled once when decompiling - * a longer script, then hit repeatedly as js_GetSrcNote is called during - * the decompiler activation that filled it. - */ - JSGSNCache gsnCache; - -#define JS_GSN_CACHE(cx) ((cx)->runtime->gsnCache) -#endif - -#ifdef DEBUG - /* Function invocation metering. */ - jsrefcount inlineCalls; - jsrefcount nativeCalls; - jsrefcount nonInlineCalls; - jsrefcount constructs; - - /* Scope lock and property metering. */ - jsrefcount claimAttempts; - jsrefcount claimedScopes; - jsrefcount deadContexts; - jsrefcount deadlocksAvoided; - jsrefcount liveScopes; - jsrefcount sharedScopes; - jsrefcount totalScopes; - jsrefcount badUndependStrings; - jsrefcount liveScopeProps; - jsrefcount totalScopeProps; - jsrefcount livePropTreeNodes; - jsrefcount duplicatePropTreeNodes; - jsrefcount totalPropTreeNodes; - jsrefcount propTreeKidsChunks; - jsrefcount middleDeleteFixups; - - /* String instrumentation. */ - jsrefcount liveStrings; - jsrefcount totalStrings; - jsrefcount liveDependentStrings; - jsrefcount totalDependentStrings; - double lengthSum; - double lengthSquaredSum; - double strdepLengthSum; - double strdepLengthSquaredSum; -#endif -}; - -#ifdef DEBUG -# define JS_RUNTIME_METER(rt, which) JS_ATOMIC_INCREMENT(&(rt)->which) -# define JS_RUNTIME_UNMETER(rt, which) JS_ATOMIC_DECREMENT(&(rt)->which) -#else -# define JS_RUNTIME_METER(rt, which) /* nothing */ -# define JS_RUNTIME_UNMETER(rt, which) /* nothing */ -#endif - -#define JS_KEEP_ATOMS(rt) JS_ATOMIC_INCREMENT(&(rt)->gcKeepAtoms); -#define JS_UNKEEP_ATOMS(rt) JS_ATOMIC_DECREMENT(&(rt)->gcKeepAtoms); - -#ifdef JS_ARGUMENT_FORMATTER_DEFINED -/* - * Linked list mapping format strings for JS_{Convert,Push}Arguments{,VA} to - * formatter functions. Elements are sorted in non-increasing format string - * length order. - */ -struct JSArgumentFormatMap { - const char *format; - size_t length; - JSArgumentFormatter formatter; - JSArgumentFormatMap *next; -}; -#endif - -struct JSStackHeader { - uintN nslots; - JSStackHeader *down; -}; - -#define JS_STACK_SEGMENT(sh) ((jsval *)(sh) + 2) - -/* - * Key and entry types for the JSContext.resolvingTable hash table, typedef'd - * here because all consumers need to see these declarations (and not just the - * typedef names, as would be the case for an opaque pointer-to-typedef'd-type - * declaration), along with cx->resolvingTable. - */ -typedef struct JSResolvingKey { - JSObject *obj; - jsid id; -} JSResolvingKey; - -typedef struct JSResolvingEntry { - JSDHashEntryHdr hdr; - JSResolvingKey key; - uint32 flags; -} JSResolvingEntry; - -#define JSRESFLAG_LOOKUP 0x1 /* resolving id from lookup */ -#define JSRESFLAG_WATCH 0x2 /* resolving id from watch */ - -typedef struct JSLocalRootChunk JSLocalRootChunk; - -#define JSLRS_CHUNK_SHIFT 8 -#define JSLRS_CHUNK_SIZE JS_BIT(JSLRS_CHUNK_SHIFT) -#define JSLRS_CHUNK_MASK JS_BITMASK(JSLRS_CHUNK_SHIFT) - -struct JSLocalRootChunk { - jsval roots[JSLRS_CHUNK_SIZE]; - JSLocalRootChunk *down; -}; - -typedef struct JSLocalRootStack { - uint32 scopeMark; - uint32 rootCount; - JSLocalRootChunk *topChunk; - JSLocalRootChunk firstChunk; -} JSLocalRootStack; - -#define JSLRS_NULL_MARK ((uint32) -1) - -typedef struct JSTempValueRooter JSTempValueRooter; -typedef void -(* JS_DLL_CALLBACK JSTempValueMarker)(JSContext *cx, JSTempValueRooter *tvr); - -typedef union JSTempValueUnion { - jsval value; - JSObject *object; - JSString *string; - void *gcthing; - JSTempValueMarker marker; - JSScopeProperty *sprop; - JSWeakRoots *weakRoots; - jsval *array; -} JSTempValueUnion; - -/* - * The following allows to reinterpret JSTempValueUnion.object as jsval using - * the tagging property of a generic jsval described below. - */ -JS_STATIC_ASSERT(sizeof(JSTempValueUnion) == sizeof(jsval)); -JS_STATIC_ASSERT(sizeof(JSTempValueUnion) == sizeof(JSObject *)); - -/* - * Context-linked stack of temporary GC roots. - * - * If count is -1, then u.value contains the single value or GC-thing to root. - * If count is -2, then u.marker holds a mark hook called to mark the values. - * If count is -3, then u.sprop points to the property tree node to mark. - * If count is -4, then u.weakRoots points to saved weak roots. - * If count >= 0, then u.array points to a stack-allocated vector of jsvals. - * - * To root a single GC-thing pointer, which need not be tagged and stored as a - * jsval, use JS_PUSH_TEMP_ROOT_GCTHING. The macro reinterprets an arbitrary - * GC-thing as jsval. It works because a GC-thing is aligned on a 0 mod 8 - * boundary, and object has the 0 jsval tag. So any GC-thing may be tagged as - * if it were an object and untagged, if it's then used only as an opaque - * pointer until discriminated by other means than tag bits (this is how the - * GC mark function uses its |thing| parameter -- it consults GC-thing flags - * stored separately from the thing to decide the type of thing). - * - * JS_PUSH_TEMP_ROOT_OBJECT and JS_PUSH_TEMP_ROOT_STRING are type-safe - * alternatives to JS_PUSH_TEMP_ROOT_GCTHING for JSObject and JSString. They - * also provide a simple way to get a single pointer to rooted JSObject or - * JSString via JS_PUSH_TEMP_ROOT_(OBJECT|STRTING)(cx, NULL, &tvr). Then - * &tvr.u.object or tvr.u.string gives the necessary pointer, which puns - * tvr.u.value safely because JSObject * and JSString * are GC-things and, as - * such, their tag bits are all zeroes. - * - * If you need to protect a result value that flows out of a C function across - * several layers of other functions, use the js_LeaveLocalRootScopeWithResult - * internal API (see further below) instead. - */ -struct JSTempValueRooter { - JSTempValueRooter *down; - ptrdiff_t count; - JSTempValueUnion u; -}; - -#define JSTVU_SINGLE (-1) -#define JSTVU_MARKER (-2) -#define JSTVU_SPROP (-3) -#define JSTVU_WEAK_ROOTS (-4) - -#define JS_PUSH_TEMP_ROOT_COMMON(cx,tvr) \ - JS_BEGIN_MACRO \ - JS_ASSERT((cx)->tempValueRooters != (tvr)); \ - (tvr)->down = (cx)->tempValueRooters; \ - (cx)->tempValueRooters = (tvr); \ - JS_END_MACRO - -#define JS_PUSH_SINGLE_TEMP_ROOT(cx,val,tvr) \ - JS_BEGIN_MACRO \ - (tvr)->count = JSTVU_SINGLE; \ - (tvr)->u.value = val; \ - JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ - JS_END_MACRO - -#define JS_PUSH_TEMP_ROOT(cx,cnt,arr,tvr) \ - JS_BEGIN_MACRO \ - JS_ASSERT((ptrdiff_t)(cnt) >= 0); \ - (tvr)->count = (ptrdiff_t)(cnt); \ - (tvr)->u.array = (arr); \ - JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ - JS_END_MACRO - -#define JS_PUSH_TEMP_ROOT_MARKER(cx,marker_,tvr) \ - JS_BEGIN_MACRO \ - (tvr)->count = JSTVU_MARKER; \ - (tvr)->u.marker = (marker_); \ - JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ - JS_END_MACRO - -#define JS_PUSH_TEMP_ROOT_OBJECT(cx,obj,tvr) \ - JS_BEGIN_MACRO \ - (tvr)->count = JSTVU_SINGLE; \ - (tvr)->u.object = (obj); \ - JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ - JS_END_MACRO - -#define JS_PUSH_TEMP_ROOT_STRING(cx,str,tvr) \ - JS_BEGIN_MACRO \ - (tvr)->count = JSTVU_SINGLE; \ - (tvr)->u.string = (str); \ - JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ - JS_END_MACRO - -#define JS_PUSH_TEMP_ROOT_GCTHING(cx,thing,tvr) \ - JS_BEGIN_MACRO \ - JS_ASSERT(JSVAL_IS_OBJECT((jsval)thing)); \ - (tvr)->count = JSTVU_SINGLE; \ - (tvr)->u.gcthing = (thing); \ - JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ - JS_END_MACRO - -#define JS_POP_TEMP_ROOT(cx,tvr) \ - JS_BEGIN_MACRO \ - JS_ASSERT((cx)->tempValueRooters == (tvr)); \ - (cx)->tempValueRooters = (tvr)->down; \ - JS_END_MACRO - -#define JS_TEMP_ROOT_EVAL(cx,cnt,val,expr) \ - JS_BEGIN_MACRO \ - JSTempValueRooter tvr; \ - JS_PUSH_TEMP_ROOT(cx, cnt, val, &tvr); \ - (expr); \ - JS_POP_TEMP_ROOT(cx, &tvr); \ - JS_END_MACRO - -#define JS_PUSH_TEMP_ROOT_SPROP(cx,sprop_,tvr) \ - JS_BEGIN_MACRO \ - (tvr)->count = JSTVU_SPROP; \ - (tvr)->u.sprop = (sprop_); \ - JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ - JS_END_MACRO - -#define JS_PUSH_TEMP_ROOT_WEAK_COPY(cx,weakRoots_,tvr) \ - JS_BEGIN_MACRO \ - (tvr)->count = JSTVU_WEAK_ROOTS; \ - (tvr)->u.weakRoots = (weakRoots_); \ - JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ - JS_END_MACRO - -struct JSContext { - /* JSRuntime contextList linkage. */ - JSCList links; - - /* Interpreter activation count. */ - uintN interpLevel; - - /* Limit pointer for checking stack consumption during recursion. */ - jsuword stackLimit; - - /* Runtime version control identifier and equality operators. */ - uint16 version; - jsbytecode jsop_eq; - jsbytecode jsop_ne; - - /* Data shared by threads in an address space. */ - JSRuntime *runtime; - - /* Stack arena pool and frame pointer register. */ - JSArenaPool stackPool; - JSStackFrame *fp; - - /* Temporary arena pool used while compiling and decompiling. */ - JSArenaPool tempPool; - - /* Top-level object and pointer to top stack frame's scope chain. */ - JSObject *globalObject; - - /* Storage to root recently allocated GC things and script result. */ - JSWeakRoots weakRoots; - - /* Regular expression class statics (XXX not shared globally). */ - JSRegExpStatics regExpStatics; - - /* State for object and array toSource conversion. */ - JSSharpObjectMap sharpObjectMap; - - /* Argument formatter support for JS_{Convert,Push}Arguments{,VA}. */ - JSArgumentFormatMap *argumentFormatMap; - - /* Last message string and trace file for debugging. */ - char *lastMessage; -#ifdef DEBUG - void *tracefp; -#endif - - /* Per-context optional user callbacks. */ - JSBranchCallback branchCallback; - JSErrorReporter errorReporter; - - /* Client opaque pointer */ - void *data; - - /* GC and thread-safe state. */ - JSStackFrame *dormantFrameChain; /* dormant stack frame to scan */ -#ifdef JS_THREADSAFE - JSThread *thread; - jsrefcount requestDepth; - JSScope *scopeToShare; /* weak reference, see jslock.c */ - JSScope *lockedSealedScope; /* weak ref, for low-cost sealed - scope locking */ - JSCList threadLinks; /* JSThread contextList linkage */ - -#define CX_FROM_THREAD_LINKS(tl) \ - ((JSContext *)((char *)(tl) - offsetof(JSContext, threadLinks))) -#endif - -#if JS_HAS_LVALUE_RETURN - /* - * Secondary return value from native method called on the left-hand side - * of an assignment operator. The native should store the object in which - * to set a property in *rval, and return the property's id expressed as a - * jsval by calling JS_SetCallReturnValue2(cx, idval). - */ - jsval rval2; - JSPackedBool rval2set; -#endif - -#if JS_HAS_XML_SUPPORT - /* - * Bit-set formed from binary exponentials of the XML_* tiny-ids defined - * for boolean settings in jsxml.c, plus an XSF_CACHE_VALID bit. Together - * these act as a cache of the boolean XML.ignore* and XML.prettyPrinting - * property values associated with this context's global object. - */ - uint8 xmlSettingFlags; -#endif - - /* - * True if creating an exception object, to prevent runaway recursion. - * NB: creatingException packs with rval2set, #if JS_HAS_LVALUE_RETURN; - * with xmlSettingFlags, #if JS_HAS_XML_SUPPORT; and with throwing below. - */ - JSPackedBool creatingException; - - /* - * Exception state -- the exception member is a GC root by definition. - * NB: throwing packs with creatingException and rval2set, above. - */ - JSPackedBool throwing; /* is there a pending exception? */ - jsval exception; /* most-recently-thrown exception */ - /* Flag to indicate that we run inside gcCallback(cx, JSGC_MARK_END). */ - JSPackedBool insideGCMarkCallback; - - /* Per-context options. */ - uint32 options; /* see jsapi.h for JSOPTION_* */ - - /* Locale specific callbacks for string conversion. */ - JSLocaleCallbacks *localeCallbacks; - - /* - * cx->resolvingTable is non-null and non-empty if we are initializing - * standard classes lazily, or if we are otherwise recursing indirectly - * from js_LookupProperty through a JSClass.resolve hook. It is used to - * limit runaway recursion (see jsapi.c and jsobj.c). - */ - JSDHashTable *resolvingTable; - - /* PDL of stack headers describing stack slots not rooted by argv, etc. */ - JSStackHeader *stackHeaders; - - /* Optional stack of heap-allocated scoped local GC roots. */ - JSLocalRootStack *localRootStack; - - /* Stack of thread-stack-allocated temporary GC roots. */ - JSTempValueRooter *tempValueRooters; - -#ifdef GC_MARK_DEBUG - /* Top of the GC mark stack. */ - void *gcCurrentMarkNode; -#endif -}; - -#ifdef JS_THREADSAFE -# define JS_THREAD_ID(cx) ((cx)->thread ? (cx)->thread->id : 0) -#endif - -#ifdef __cplusplus -/* FIXME(bug 332648): Move this into a public header. */ -class JSAutoTempValueRooter -{ - public: - JSAutoTempValueRooter(JSContext *cx, size_t len, jsval *vec) - : mContext(cx) { - JS_PUSH_TEMP_ROOT(mContext, len, vec, &mTvr); - } - JSAutoTempValueRooter(JSContext *cx, jsval v) - : mContext(cx) { - JS_PUSH_SINGLE_TEMP_ROOT(mContext, v, &mTvr); - } - - ~JSAutoTempValueRooter() { - JS_POP_TEMP_ROOT(mContext, &mTvr); - } - - private: - static void *operator new(size_t); - static void operator delete(void *, size_t); - - JSContext *mContext; - JSTempValueRooter mTvr; -}; -#endif - -/* - * Slightly more readable macros for testing per-context option settings (also - * to hide bitset implementation detail). - * - * JSOPTION_XML must be handled specially in order to propagate from compile- - * to run-time (from cx->options to script->version/cx->version). To do that, - * we copy JSOPTION_XML from cx->options into cx->version as JSVERSION_HAS_XML - * whenever options are set, and preserve this XML flag across version number - * changes done via the JS_SetVersion API. - * - * But when executing a script or scripted function, the interpreter changes - * cx->version, including the XML flag, to script->version. Thus JSOPTION_XML - * is a compile-time option that causes a run-time version change during each - * activation of the compiled script. That version change has the effect of - * changing JS_HAS_XML_OPTION, so that any compiling done via eval enables XML - * support. If an XML-enabled script or function calls a non-XML function, - * the flag bit will be cleared during the callee's activation. - * - * Note that JS_SetVersion API calls never pass JSVERSION_HAS_XML or'd into - * that API's version parameter. - * - * Note also that script->version must contain this XML option flag in order - * for XDR'ed scripts to serialize and deserialize with that option preserved - * for detection at run-time. We can't copy other compile-time options into - * script->version because that would break backward compatibility (certain - * other options, e.g. JSOPTION_VAROBJFIX, are analogous to JSOPTION_XML). - */ -#define JS_HAS_OPTION(cx,option) (((cx)->options & (option)) != 0) -#define JS_HAS_STRICT_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_STRICT) -#define JS_HAS_WERROR_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_WERROR) -#define JS_HAS_COMPILE_N_GO_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_COMPILE_N_GO) -#define JS_HAS_ATLINE_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_ATLINE) - -#define JSVERSION_MASK 0x0FFF /* see JSVersion in jspubtd.h */ -#define JSVERSION_HAS_XML 0x1000 /* flag induced by XML option */ - -#define JSVERSION_NUMBER(cx) ((cx)->version & JSVERSION_MASK) -#define JS_HAS_XML_OPTION(cx) ((cx)->version & JSVERSION_HAS_XML || \ - JSVERSION_NUMBER(cx) >= JSVERSION_1_6) - -#define JS_HAS_NATIVE_BRANCH_CALLBACK_OPTION(cx) \ - JS_HAS_OPTION(cx, JSOPTION_NATIVE_BRANCH_CALLBACK) - -/* - * Wrappers for the JSVERSION_IS_* macros from jspubtd.h taking JSContext *cx - * and masking off the XML flag and any other high order bits. - */ -#define JS_VERSION_IS_ECMA(cx) JSVERSION_IS_ECMA(JSVERSION_NUMBER(cx)) - -/* - * Common subroutine of JS_SetVersion and js_SetVersion, to update per-context - * data that depends on version. - */ -extern void -js_OnVersionChange(JSContext *cx); - -/* - * Unlike the JS_SetVersion API, this function stores JSVERSION_HAS_XML and - * any future non-version-number flags induced by compiler options. - */ -extern void -js_SetVersion(JSContext *cx, JSVersion version); - -/* - * Create and destroy functions for JSContext, which is manually allocated - * and exclusively owned. - */ -extern JSContext * -js_NewContext(JSRuntime *rt, size_t stackChunkSize); - -extern void -js_DestroyContext(JSContext *cx, JSDestroyContextMode mode); - -/* - * Return true if cx points to a context in rt->contextList, else return false. - * NB: the caller (see jslock.c:ClaimScope) must hold rt->gcLock. - */ -extern JSBool -js_ValidContextPointer(JSRuntime *rt, JSContext *cx); - -/* - * If unlocked, acquire and release rt->gcLock around *iterp update; otherwise - * the caller must be holding rt->gcLock. - */ -extern JSContext * -js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp); - -/* - * JSClass.resolve and watchpoint recursion damping machinery. - */ -extern JSBool -js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, - JSResolvingEntry **entryp); - -extern void -js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, - JSResolvingEntry *entry, uint32 generation); - -/* - * Local root set management. - * - * NB: the jsval parameters below may be properly tagged jsvals, or GC-thing - * pointers cast to (jsval). This relies on JSObject's tag being zero, but - * on the up side it lets us push int-jsval-encoded scopeMark values on the - * local root stack. - */ -extern JSBool -js_EnterLocalRootScope(JSContext *cx); - -#define js_LeaveLocalRootScope(cx) \ - js_LeaveLocalRootScopeWithResult(cx, JSVAL_NULL) - -extern void -js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval); - -extern void -js_ForgetLocalRoot(JSContext *cx, jsval v); - -extern int -js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v); - -extern void -js_MarkLocalRoots(JSContext *cx, JSLocalRootStack *lrs); - -/* - * Report an exception, which is currently realized as a printf-style format - * string and its arguments. - */ -typedef enum JSErrNum { -#define MSG_DEF(name, number, count, exception, format) \ - name = number, -#include "js.msg" -#undef MSG_DEF - JSErr_Limit -} JSErrNum; - -extern const JSErrorFormatString * -js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber); - -#ifdef va_start -extern JSBool -js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap); - -extern JSBool -js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback, - void *userRef, const uintN errorNumber, - JSBool charArgs, va_list ap); - -extern JSBool -js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, - void *userRef, const uintN errorNumber, - char **message, JSErrorReport *reportp, - JSBool *warningp, JSBool charArgs, va_list ap); -#endif - -extern void -js_ReportOutOfMemory(JSContext *cx); - -/* - * Report an exception using a previously composed JSErrorReport. - * XXXbe remove from "friend" API - */ -extern JS_FRIEND_API(void) -js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *report); - -extern void -js_ReportIsNotDefined(JSContext *cx, const char *name); - -extern JSErrorFormatString js_ErrorFormatString[JSErr_Limit]; - -/* - * See JS_SetThreadStackLimit in jsapi.c, where we check that the stack grows - * in the expected direction. On Unix-y systems, JS_STACK_GROWTH_DIRECTION is - * computed on the build host by jscpucfg.c and written into jsautocfg.h. The - * macro is hardcoded in jscpucfg.h on Windows and Mac systems (for historical - * reasons pre-dating autoconf usage). - */ -#if JS_STACK_GROWTH_DIRECTION > 0 -# define JS_CHECK_STACK_SIZE(cx, lval) ((jsuword)&(lval) < (cx)->stackLimit) -#else -# define JS_CHECK_STACK_SIZE(cx, lval) ((jsuword)&(lval) > (cx)->stackLimit) -#endif - -JS_END_EXTERN_C - -#endif /* jscntxt_h___ */ diff --git a/spidermonkey/src/jscompat.h b/spidermonkey/src/jscompat.h deleted file mode 100644 index 80d8605..0000000 --- a/spidermonkey/src/jscompat.h +++ /dev/null @@ -1,57 +0,0 @@ -/* ***** 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 Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998-1999 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of 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 ***** */ - -/* -*- Mode: C; tab-width: 8 -*- - * Copyright (C) 1996-1999 Netscape Communications Corporation, All Rights Reserved. - */ -#ifndef jscompat_h___ -#define jscompat_h___ -/* - * Compatibility glue for various NSPR versions. We must always define int8, - * int16, jsword, and so on to minimize differences with js/ref, no matter what - * the NSPR typedef names may be. - */ -#include "jstypes.h" -#include "jslong.h" - -typedef JSIntn intN; -typedef JSUintn uintN; -typedef JSUword jsuword; -typedef JSWord jsword; -typedef float float32; -#define allocPriv allocPool -#endif /* jscompat_h___ */ diff --git a/spidermonkey/src/jsconfig.h b/spidermonkey/src/jsconfig.h deleted file mode 100644 index d61e802..0000000 --- a/spidermonkey/src/jsconfig.h +++ /dev/null @@ -1,208 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS configuration macros. - */ -#ifndef JS_VERSION -#define JS_VERSION 170 -#endif - -/* - * Compile-time JS version configuration. The JS version numbers lie on the - * number line like so: - * - * 1.0 1.1 1.2 1.3 1.4 ECMAv3 1.5 1.6 - * ^ ^ - * | | - * basis for ECMAv1 close to ECMAv2 - * - * where ECMAv3 stands for ECMA-262 Edition 3. See the runtime version enum - * JSVersion in jspubtd.h. Code in the engine can therefore count on version - * <= JSVERSION_1_4 to mean "before the Third Edition of ECMA-262" and version - * > JSVERSION_1_4 to mean "at or after the Third Edition". - * - * In the (likely?) event that SpiderMonkey grows to implement JavaScript 2.0, - * or ECMA-262 Edition 4 (JS2 without certain extensions), the version number - * to use would be near 200, or greater. - * - * The JS_VERSION_ECMA_3 version is the minimal configuration conforming to - * the ECMA-262 Edition 3 specification. Use it for minimal embeddings, where - * you're sure you don't need any of the extensions disabled in this version. - * In order to facilitate testing, JS_HAS_OBJ_PROTO_PROP is defined as part of - * the JS_VERSION_ECMA_3_TEST version. - * - * To keep things sane in the modern age, where we need exceptions in order to - * implement, e.g., iterators and generators, we are dropping support for all - * versions <= 1.4. - */ -#define JS_VERSION_ECMA_3 148 -#define JS_VERSION_ECMA_3_TEST 149 - -#if JS_VERSION == JS_VERSION_ECMA_3 || \ - JS_VERSION == JS_VERSION_ECMA_3_TEST - -#define JS_HAS_STR_HTML_HELPERS 0 /* has str.anchor, str.bold, etc. */ -#define JS_HAS_PERL_SUBSTR 0 /* has str.substr */ -#if JS_VERSION == JS_VERSION_ECMA_3_TEST -#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ -#else -#define JS_HAS_OBJ_PROTO_PROP 0 /* has o.__proto__ etc. */ -#endif -#define JS_HAS_OBJ_WATCHPOINT 0 /* has o.watch and o.unwatch */ -#define JS_HAS_EXPORT_IMPORT 0 /* has export fun; import obj.fun */ -#define JS_HAS_EVAL_THIS_SCOPE 0 /* Math.eval is same as with (Math) */ -#define JS_HAS_SHARP_VARS 0 /* has #n=, #n# for object literals */ -#define JS_HAS_SCRIPT_OBJECT 0 /* has (new Script("x++")).exec() */ -#define JS_HAS_XDR 0 /* has XDR API and internal support */ -#define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ -#define JS_HAS_TOSOURCE 0 /* has Object/Array toSource method */ -#define JS_HAS_DEBUGGER_KEYWORD 0 /* has hook for debugger keyword */ -#define JS_HAS_CATCH_GUARD 0 /* has exception handling catch guard */ -#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ -#define JS_HAS_GETTER_SETTER 0 /* has JS2 getter/setter functions */ -#define JS_HAS_UNEVAL 0 /* has uneval() top-level function */ -#define JS_HAS_CONST 0 /* has JS2 const as alternative var */ -#define JS_HAS_FUN_EXPR_STMT 0 /* has function expression statement */ -#define JS_HAS_LVALUE_RETURN 1 /* has o.item(i) = j; for native item */ -#define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ -#define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ -#define JS_HAS_ARRAY_EXTRAS 0 /* has indexOf and Lispy extras */ -#define JS_HAS_GENERATORS 0 /* has yield in generator function */ -#define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */ -#define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */ - -#elif JS_VERSION < 150 - -#error "unsupported JS_VERSION" - -#elif JS_VERSION == 150 - -#define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ -#define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ -#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ -#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ -#define JS_HAS_EXPORT_IMPORT 1 /* has export fun; import obj.fun */ -#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ -#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ -#define JS_HAS_SCRIPT_OBJECT 1 /* has (new Script("x++")).exec() */ -#define JS_HAS_XDR 1 /* has XDR API and internal support */ -#define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ -#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ -#define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */ -#define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ -#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ -#define JS_HAS_GETTER_SETTER 1 /* has JS2 getter/setter functions */ -#define JS_HAS_UNEVAL 1 /* has uneval() top-level function */ -#define JS_HAS_CONST 1 /* has JS2 const as alternative var */ -#define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */ -#define JS_HAS_LVALUE_RETURN 1 /* has o.item(i) = j; for native item */ -#define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ -#define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ -#define JS_HAS_ARRAY_EXTRAS 0 /* has indexOf and Lispy extras */ -#define JS_HAS_GENERATORS 0 /* has yield in generator function */ -#define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */ -#define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */ - -#elif JS_VERSION == 160 - -#define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ -#define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ -#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ -#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ -#define JS_HAS_EXPORT_IMPORT 1 /* has export fun; import obj.fun */ -#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ -#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ -#define JS_HAS_SCRIPT_OBJECT 1 /* has (new Script("x++")).exec() */ -#define JS_HAS_XDR 1 /* has XDR API and internal support */ -#define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ -#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ -#define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */ -#define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ -#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ -#define JS_HAS_GETTER_SETTER 1 /* has JS2 getter/setter functions */ -#define JS_HAS_UNEVAL 1 /* has uneval() top-level function */ -#define JS_HAS_CONST 1 /* has JS2 const as alternative var */ -#define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */ -#define JS_HAS_LVALUE_RETURN 1 /* has o.item(i) = j; for native item */ -#define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ -#define JS_HAS_XML_SUPPORT 1 /* has ECMAScript for XML support */ -#define JS_HAS_ARRAY_EXTRAS 1 /* has indexOf and Lispy extras */ -#define JS_HAS_GENERATORS 0 /* has yield in generator function */ -#define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */ -#define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */ - -#elif JS_VERSION == 170 - -#define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ -#define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ -#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ -#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ -#define JS_HAS_EXPORT_IMPORT 1 /* has export fun; import obj.fun */ -#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ -#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ -#define JS_HAS_SCRIPT_OBJECT 1 /* has (new Script("x++")).exec() */ -#define JS_HAS_XDR 1 /* has XDR API and internal support */ -#define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ -#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ -#define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */ -#define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ -#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ -#define JS_HAS_GETTER_SETTER 1 /* has JS2 getter/setter functions */ -#define JS_HAS_UNEVAL 1 /* has uneval() top-level function */ -#define JS_HAS_CONST 1 /* has JS2 const as alternative var */ -#define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */ -#define JS_HAS_LVALUE_RETURN 1 /* has o.item(i) = j; for native item */ -#define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ -#define JS_HAS_XML_SUPPORT 1 /* has ECMAScript for XML support */ -#define JS_HAS_ARRAY_EXTRAS 1 /* has indexOf and Lispy extras */ -#define JS_HAS_GENERATORS 1 /* has yield in generator function */ -#define JS_HAS_BLOCK_SCOPE 1 /* has block scope via let/arraycomp */ -#define JS_HAS_DESTRUCTURING 1 /* has [a,b] = ... or {p:a,q:b} = ... */ - -#else - -#error "unknown JS_VERSION" - -#endif - -/* Features that are present in all versions. */ -#define JS_HAS_RESERVED_JAVA_KEYWORDS 1 -#define JS_HAS_RESERVED_ECMA_KEYWORDS 1 - diff --git a/spidermonkey/src/jscpucfg.c b/spidermonkey/src/jscpucfg.c deleted file mode 100644 index daa9121..0000000 --- a/spidermonkey/src/jscpucfg.c +++ /dev/null @@ -1,380 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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): - * Roland Mainz - * - * Alternatively, the contents of this file may be used under the terms of - * either of 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 ***** */ - -/* - * Generate CPU-specific bit-size and similar #defines. - */ -#include -#include - -#ifdef CROSS_COMPILE -#include -#define INT64 PRInt64 -#else - -/************************************************************************/ - -/* Generate cpucfg.h */ - -#if defined(XP_WIN) || defined(XP_OS2) -#ifdef WIN32 -#if defined(__GNUC__) -#define INT64 long long -#else -#define INT64 _int64 -#endif /* __GNUC__ */ -#else -#define INT64 long -#endif -#else -#if defined(HPUX) || defined(__QNX__) || defined(_SCO_DS) || defined(UNIXWARE) -#define INT64 long -#else -#define INT64 long long -#endif -#endif - -#endif /* CROSS_COMPILE */ - -#ifdef __GNUC__ -#define NS_NEVER_INLINE __attribute__((noinline)) -#else -#define NS_NEVER_INLINE -#endif - -#ifdef __SUNPRO_C -static int StackGrowthDirection(int *dummy1addr); -#pragma no_inline(StackGrowthDirection) -#endif - -typedef void *prword; - -struct align_short { - char c; - short a; -}; -struct align_int { - char c; - int a; -}; -struct align_long { - char c; - long a; -}; -struct align_int64 { - char c; - INT64 a; -}; -struct align_fakelonglong { - char c; - struct { - long hi, lo; - } a; -}; -struct align_float { - char c; - float a; -}; -struct align_double { - char c; - double a; -}; -struct align_pointer { - char c; - void *a; -}; -struct align_prword { - char c; - prword a; -}; - -#define ALIGN_OF(type) \ - (((char*)&(((struct align_##type *)0)->a)) - ((char*)0)) - -unsigned int bpb; - -static int Log2(unsigned int n) -{ - int log2 = 0; - - if (n & (n-1)) - log2++; - if (n >> 16) - log2 += 16, n >>= 16; - if (n >> 8) - log2 += 8, n >>= 8; - if (n >> 4) - log2 += 4, n >>= 4; - if (n >> 2) - log2 += 2, n >>= 2; - if (n >> 1) - log2++; - return log2; -} - -/* - * Conceivably this could actually be used, but there is lots of code out - * there with ands and shifts in it that assumes a byte is exactly 8 bits, - * so forget about porting THIS code to all those non 8 bit byte machines. - */ -static void BitsPerByte(void) -{ - bpb = 8; -} - -static int NS_NEVER_INLINE StackGrowthDirection(int *dummy1addr) -{ - int dummy2; - - return (&dummy2 < dummy1addr) ? -1 : 1; -} - -int main(int argc, char **argv) -{ - int sizeof_char, sizeof_short, sizeof_int, sizeof_int64, sizeof_long, - sizeof_float, sizeof_double, sizeof_word, sizeof_dword; - int bits_per_int64_log2, align_of_short, align_of_int, align_of_long, - align_of_int64, align_of_float, align_of_double, align_of_pointer, - align_of_word; - int dummy1; - - BitsPerByte(); - - printf("#ifndef js_cpucfg___\n"); - printf("#define js_cpucfg___\n\n"); - - printf("/* AUTOMATICALLY GENERATED - DO NOT EDIT */\n\n"); - -#ifdef CROSS_COMPILE -#if defined(IS_LITTLE_ENDIAN) - printf("#define IS_LITTLE_ENDIAN 1\n"); - printf("#undef IS_BIG_ENDIAN\n\n"); -#elif defined(IS_BIG_ENDIAN) - printf("#undef IS_LITTLE_ENDIAN\n"); - printf("#define IS_BIG_ENDIAN 1\n\n"); -#else -#error "Endianess not defined." -#endif - - sizeof_char = PR_BYTES_PER_BYTE; - sizeof_short = PR_BYTES_PER_SHORT; - sizeof_int = PR_BYTES_PER_INT; - sizeof_int64 = PR_BYTES_PER_INT64; - sizeof_long = PR_BYTES_PER_LONG; - sizeof_float = PR_BYTES_PER_FLOAT; - sizeof_double = PR_BYTES_PER_DOUBLE; - sizeof_word = PR_BYTES_PER_WORD; - sizeof_dword = PR_BYTES_PER_DWORD; - - bits_per_int64_log2 = PR_BITS_PER_INT64_LOG2; - - align_of_short = PR_ALIGN_OF_SHORT; - align_of_int = PR_ALIGN_OF_INT; - align_of_long = PR_ALIGN_OF_LONG; - align_of_int64 = PR_ALIGN_OF_INT64; - align_of_float = PR_ALIGN_OF_FLOAT; - align_of_double = PR_ALIGN_OF_DOUBLE; - align_of_pointer = PR_ALIGN_OF_POINTER; - align_of_word = PR_ALIGN_OF_WORD; - -#else /* !CROSS_COMPILE */ - - /* - * We don't handle PDP-endian or similar orders: if a short is big-endian, - * so must int and long be big-endian for us to generate the IS_BIG_ENDIAN - * #define and the IS_LITTLE_ENDIAN #undef. - */ - { - int big_endian = 0, little_endian = 0, ntests = 0; - - if (sizeof(short) == 2) { - /* force |volatile| here to get rid of any compiler optimisations - * (var in register etc.) which may be appiled to |auto| vars - - * even those in |union|s... - * (|static| is used to get the same functionality for compilers - * which do not honor |volatile|...). - */ - volatile static union { - short i; - char c[2]; - } u; - - u.i = 0x0102; - big_endian += (u.c[0] == 0x01 && u.c[1] == 0x02); - little_endian += (u.c[0] == 0x02 && u.c[1] == 0x01); - ntests++; - } - - if (sizeof(int) == 4) { - /* force |volatile| here ... */ - volatile static union { - int i; - char c[4]; - } u; - - u.i = 0x01020304; - big_endian += (u.c[0] == 0x01 && u.c[1] == 0x02 && - u.c[2] == 0x03 && u.c[3] == 0x04); - little_endian += (u.c[0] == 0x04 && u.c[1] == 0x03 && - u.c[2] == 0x02 && u.c[3] == 0x01); - ntests++; - } - - if (sizeof(long) == 8) { - /* force |volatile| here ... */ - volatile static union { - long i; - char c[8]; - } u; - - /* - * Write this as portably as possible: avoid 0x0102030405060708L - * and <<= 32. - */ - u.i = 0x01020304; - u.i <<= 16, u.i <<= 16; - u.i |= 0x05060708; - big_endian += (u.c[0] == 0x01 && u.c[1] == 0x02 && - u.c[2] == 0x03 && u.c[3] == 0x04 && - u.c[4] == 0x05 && u.c[5] == 0x06 && - u.c[6] == 0x07 && u.c[7] == 0x08); - little_endian += (u.c[0] == 0x08 && u.c[1] == 0x07 && - u.c[2] == 0x06 && u.c[3] == 0x05 && - u.c[4] == 0x04 && u.c[5] == 0x03 && - u.c[6] == 0x02 && u.c[7] == 0x01); - ntests++; - } - - if (big_endian && big_endian == ntests) { - printf("#undef IS_LITTLE_ENDIAN\n"); - printf("#define IS_BIG_ENDIAN 1\n\n"); - } else if (little_endian && little_endian == ntests) { - printf("#define IS_LITTLE_ENDIAN 1\n"); - printf("#undef IS_BIG_ENDIAN\n\n"); - } else { - fprintf(stderr, "%s: unknown byte order" - "(big_endian=%d, little_endian=%d, ntests=%d)!\n", - argv[0], big_endian, little_endian, ntests); - return EXIT_FAILURE; - } - } - - sizeof_char = sizeof(char); - sizeof_short = sizeof(short); - sizeof_int = sizeof(int); - sizeof_int64 = 8; - sizeof_long = sizeof(long); - sizeof_float = sizeof(float); - sizeof_double = sizeof(double); - sizeof_word = sizeof(prword); - sizeof_dword = 8; - - bits_per_int64_log2 = 6; - - align_of_short = ALIGN_OF(short); - align_of_int = ALIGN_OF(int); - align_of_long = ALIGN_OF(long); - if (sizeof(INT64) < 8) { - /* this machine doesn't actually support int64's */ - align_of_int64 = ALIGN_OF(fakelonglong); - } else { - align_of_int64 = ALIGN_OF(int64); - } - align_of_float = ALIGN_OF(float); - align_of_double = ALIGN_OF(double); - align_of_pointer = ALIGN_OF(pointer); - align_of_word = ALIGN_OF(prword); - -#endif /* CROSS_COMPILE */ - - printf("#define JS_BYTES_PER_BYTE %dL\n", sizeof_char); - printf("#define JS_BYTES_PER_SHORT %dL\n", sizeof_short); - printf("#define JS_BYTES_PER_INT %dL\n", sizeof_int); - printf("#define JS_BYTES_PER_INT64 %dL\n", sizeof_int64); - printf("#define JS_BYTES_PER_LONG %dL\n", sizeof_long); - printf("#define JS_BYTES_PER_FLOAT %dL\n", sizeof_float); - printf("#define JS_BYTES_PER_DOUBLE %dL\n", sizeof_double); - printf("#define JS_BYTES_PER_WORD %dL\n", sizeof_word); - printf("#define JS_BYTES_PER_DWORD %dL\n", sizeof_dword); - printf("\n"); - - printf("#define JS_BITS_PER_BYTE %dL\n", bpb); - printf("#define JS_BITS_PER_SHORT %dL\n", bpb * sizeof_short); - printf("#define JS_BITS_PER_INT %dL\n", bpb * sizeof_int); - printf("#define JS_BITS_PER_INT64 %dL\n", bpb * sizeof_int64); - printf("#define JS_BITS_PER_LONG %dL\n", bpb * sizeof_long); - printf("#define JS_BITS_PER_FLOAT %dL\n", bpb * sizeof_float); - printf("#define JS_BITS_PER_DOUBLE %dL\n", bpb * sizeof_double); - printf("#define JS_BITS_PER_WORD %dL\n", bpb * sizeof_word); - printf("\n"); - - printf("#define JS_BITS_PER_BYTE_LOG2 %dL\n", Log2(bpb)); - printf("#define JS_BITS_PER_SHORT_LOG2 %dL\n", Log2(bpb * sizeof_short)); - printf("#define JS_BITS_PER_INT_LOG2 %dL\n", Log2(bpb * sizeof_int)); - printf("#define JS_BITS_PER_INT64_LOG2 %dL\n", bits_per_int64_log2); - printf("#define JS_BITS_PER_LONG_LOG2 %dL\n", Log2(bpb * sizeof_long)); - printf("#define JS_BITS_PER_FLOAT_LOG2 %dL\n", Log2(bpb * sizeof_float)); - printf("#define JS_BITS_PER_DOUBLE_LOG2 %dL\n", Log2(bpb * sizeof_double)); - printf("#define JS_BITS_PER_WORD_LOG2 %dL\n", Log2(bpb * sizeof_word)); - printf("\n"); - - printf("#define JS_ALIGN_OF_SHORT %dL\n", align_of_short); - printf("#define JS_ALIGN_OF_INT %dL\n", align_of_int); - printf("#define JS_ALIGN_OF_LONG %dL\n", align_of_long); - printf("#define JS_ALIGN_OF_INT64 %dL\n", align_of_int64); - printf("#define JS_ALIGN_OF_FLOAT %dL\n", align_of_float); - printf("#define JS_ALIGN_OF_DOUBLE %dL\n", align_of_double); - printf("#define JS_ALIGN_OF_POINTER %dL\n", align_of_pointer); - printf("#define JS_ALIGN_OF_WORD %dL\n", align_of_word); - printf("\n"); - - printf("#define JS_BYTES_PER_WORD_LOG2 %dL\n", Log2(sizeof_word)); - printf("#define JS_BYTES_PER_DWORD_LOG2 %dL\n", Log2(sizeof_dword)); - printf("#define JS_WORDS_PER_DWORD_LOG2 %dL\n", Log2(sizeof_dword/sizeof_word)); - printf("\n"); - - printf("#define JS_STACK_GROWTH_DIRECTION (%d)\n", StackGrowthDirection(&dummy1)); - printf("\n"); - - printf("#endif /* js_cpucfg___ */\n"); - - return EXIT_SUCCESS; -} - diff --git a/spidermonkey/src/jscpucfg.h b/spidermonkey/src/jscpucfg.h deleted file mode 100644 index 63ef932..0000000 --- a/spidermonkey/src/jscpucfg.h +++ /dev/null @@ -1,212 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef js_cpucfg___ -#define js_cpucfg___ - -#include "jsosdep.h" - -#if defined(XP_WIN) || defined(XP_OS2) || defined(WINCE) - -#if defined(_WIN64) - -#if defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_) -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN - -#define JS_BYTES_PER_BYTE 1L -#define JS_BYTES_PER_SHORT 2L -#define JS_BYTES_PER_INT 4L -#define JS_BYTES_PER_INT64 8L -#define JS_BYTES_PER_LONG 4L -#define JS_BYTES_PER_FLOAT 4L -#define JS_BYTES_PER_DOUBLE 8L -#define JS_BYTES_PER_WORD 8L -#define JS_BYTES_PER_DWORD 8L - -#define JS_BITS_PER_BYTE 8L -#define JS_BITS_PER_SHORT 16L -#define JS_BITS_PER_INT 32L -#define JS_BITS_PER_INT64 64L -#define JS_BITS_PER_LONG 32L -#define JS_BITS_PER_FLOAT 32L -#define JS_BITS_PER_DOUBLE 64L -#define JS_BITS_PER_WORD 64L - -#define JS_BITS_PER_BYTE_LOG2 3L -#define JS_BITS_PER_SHORT_LOG2 4L -#define JS_BITS_PER_INT_LOG2 5L -#define JS_BITS_PER_INT64_LOG2 6L -#define JS_BITS_PER_LONG_LOG2 5L -#define JS_BITS_PER_FLOAT_LOG2 5L -#define JS_BITS_PER_DOUBLE_LOG2 6L -#define JS_BITS_PER_WORD_LOG2 6L - -#define JS_ALIGN_OF_SHORT 2L -#define JS_ALIGN_OF_INT 4L -#define JS_ALIGN_OF_LONG 4L -#define JS_ALIGN_OF_INT64 8L -#define JS_ALIGN_OF_FLOAT 4L -#define JS_ALIGN_OF_DOUBLE 8L -#define JS_ALIGN_OF_POINTER 8L -#define JS_ALIGN_OF_WORD 8L - -#define JS_BYTES_PER_WORD_LOG2 3L -#define JS_BYTES_PER_DWORD_LOG2 3L -#define PR_WORDS_PER_DWORD_LOG2 0L -#else /* !(defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)) */ -#error "CPU type is unknown" -#endif /* !(defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)) */ - -#elif defined(_WIN32) || defined(XP_OS2) || defined(WINCE) - -#ifdef __WATCOMC__ -#define HAVE_VA_LIST_AS_ARRAY -#endif - -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN - -#define JS_BYTES_PER_BYTE 1L -#define JS_BYTES_PER_SHORT 2L -#define JS_BYTES_PER_INT 4L -#define JS_BYTES_PER_INT64 8L -#define JS_BYTES_PER_LONG 4L -#define JS_BYTES_PER_FLOAT 4L -#define JS_BYTES_PER_DOUBLE 8L -#define JS_BYTES_PER_WORD 4L -#define JS_BYTES_PER_DWORD 8L - -#define JS_BITS_PER_BYTE 8L -#define JS_BITS_PER_SHORT 16L -#define JS_BITS_PER_INT 32L -#define JS_BITS_PER_INT64 64L -#define JS_BITS_PER_LONG 32L -#define JS_BITS_PER_FLOAT 32L -#define JS_BITS_PER_DOUBLE 64L -#define JS_BITS_PER_WORD 32L - -#define JS_BITS_PER_BYTE_LOG2 3L -#define JS_BITS_PER_SHORT_LOG2 4L -#define JS_BITS_PER_INT_LOG2 5L -#define JS_BITS_PER_INT64_LOG2 6L -#define JS_BITS_PER_LONG_LOG2 5L -#define JS_BITS_PER_FLOAT_LOG2 5L -#define JS_BITS_PER_DOUBLE_LOG2 6L -#define JS_BITS_PER_WORD_LOG2 5L - -#define JS_ALIGN_OF_SHORT 2L -#define JS_ALIGN_OF_INT 4L -#define JS_ALIGN_OF_LONG 4L -#define JS_ALIGN_OF_INT64 8L -#define JS_ALIGN_OF_FLOAT 4L -#define JS_ALIGN_OF_DOUBLE 4L -#define JS_ALIGN_OF_POINTER 4L -#define JS_ALIGN_OF_WORD 4L - -#define JS_BYTES_PER_WORD_LOG2 2L -#define JS_BYTES_PER_DWORD_LOG2 3L -#define PR_WORDS_PER_DWORD_LOG2 1L -#endif /* _WIN32 || XP_OS2 || WINCE*/ - -#if defined(_WINDOWS) && !defined(_WIN32) /* WIN16 */ - -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN - -#define JS_BYTES_PER_BYTE 1L -#define JS_BYTES_PER_SHORT 2L -#define JS_BYTES_PER_INT 2L -#define JS_BYTES_PER_INT64 8L -#define JS_BYTES_PER_LONG 4L -#define JS_BYTES_PER_FLOAT 4L -#define JS_BYTES_PER_DOUBLE 8L -#define JS_BYTES_PER_WORD 4L -#define JS_BYTES_PER_DWORD 8L - -#define JS_BITS_PER_BYTE 8L -#define JS_BITS_PER_SHORT 16L -#define JS_BITS_PER_INT 16L -#define JS_BITS_PER_INT64 64L -#define JS_BITS_PER_LONG 32L -#define JS_BITS_PER_FLOAT 32L -#define JS_BITS_PER_DOUBLE 64L -#define JS_BITS_PER_WORD 32L - -#define JS_BITS_PER_BYTE_LOG2 3L -#define JS_BITS_PER_SHORT_LOG2 4L -#define JS_BITS_PER_INT_LOG2 4L -#define JS_BITS_PER_INT64_LOG2 6L -#define JS_BITS_PER_LONG_LOG2 5L -#define JS_BITS_PER_FLOAT_LOG2 5L -#define JS_BITS_PER_DOUBLE_LOG2 6L -#define JS_BITS_PER_WORD_LOG2 5L - -#define JS_ALIGN_OF_SHORT 2L -#define JS_ALIGN_OF_INT 2L -#define JS_ALIGN_OF_LONG 2L -#define JS_ALIGN_OF_INT64 2L -#define JS_ALIGN_OF_FLOAT 2L -#define JS_ALIGN_OF_DOUBLE 2L -#define JS_ALIGN_OF_POINTER 2L -#define JS_ALIGN_OF_WORD 2L - -#define JS_BYTES_PER_WORD_LOG2 2L -#define JS_BYTES_PER_DWORD_LOG2 3L -#define PR_WORDS_PER_DWORD_LOG2 1L - -#endif /* defined(_WINDOWS) && !defined(_WIN32) */ - -#elif defined(XP_UNIX) || defined(XP_BEOS) - -#error "This file is supposed to be auto-generated on UNIX platforms, but the" -#error "static version for Mac and Windows platforms is being used." -#error "Something's probably wrong with paths/headers/dependencies/Makefiles." - -#else - -#error "Must define one of XP_BEOS, XP_OS2, XP_WIN, or XP_UNIX" - -#endif - -#ifndef JS_STACK_GROWTH_DIRECTION -#define JS_STACK_GROWTH_DIRECTION (-1) -#endif - -#endif /* js_cpucfg___ */ diff --git a/spidermonkey/src/jsdate.c b/spidermonkey/src/jsdate.c deleted file mode 100644 index 9e6697f..0000000 --- a/spidermonkey/src/jsdate.c +++ /dev/null @@ -1,2371 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS date methods. - */ - -/* - * "For example, OS/360 devotes 26 bytes of the permanently - * resident date-turnover routine to the proper handling of - * December 31 on leap years (when it is Day 366). That - * might have been left to the operator." - * - * Frederick Brooks, 'The Second-System Effect'. - */ - -#include "jsstddef.h" -#include -#include -#include -#include -#include -#include "jstypes.h" -#include "jsprf.h" -#include "prmjtime.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jsapi.h" -#include "jsconfig.h" -#include "jscntxt.h" -#include "jsdate.h" -#include "jsinterp.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsstr.h" - -/* - * The JS 'Date' object is patterned after the Java 'Date' object. - * Here is an script: - * - * today = new Date(); - * - * print(today.toLocaleString()); - * - * weekDay = today.getDay(); - * - * - * These Java (and ECMA-262) methods are supported: - * - * UTC - * getDate (getUTCDate) - * getDay (getUTCDay) - * getHours (getUTCHours) - * getMinutes (getUTCMinutes) - * getMonth (getUTCMonth) - * getSeconds (getUTCSeconds) - * getMilliseconds (getUTCMilliseconds) - * getTime - * getTimezoneOffset - * getYear - * getFullYear (getUTCFullYear) - * parse - * setDate (setUTCDate) - * setHours (setUTCHours) - * setMinutes (setUTCMinutes) - * setMonth (setUTCMonth) - * setSeconds (setUTCSeconds) - * setMilliseconds (setUTCMilliseconds) - * setTime - * setYear (setFullYear, setUTCFullYear) - * toGMTString (toUTCString) - * toLocaleString - * toString - * - * - * These Java methods are not supported - * - * setDay - * before - * after - * equals - * hashCode - */ - -/* - * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language - * definition and reduce dependence on NSPR. NSPR is used to get the current - * time in milliseconds, the time zone offset, and the daylight savings time - * offset for a given time. NSPR is also used for Date.toLocaleString(), for - * locale-specific formatting, and to get a string representing the timezone. - * (Which turns out to be platform-dependent.) - * - * To do: - * (I did some performance tests by timing how long it took to run what - * I had of the js ECMA conformance tests.) - * - * - look at saving results across multiple calls to supporting - * functions; the toString functions compute some of the same values - * multiple times. Although - I took a quick stab at this, and I lost - * rather than gained. (Fractionally.) Hard to tell what compilers/processors - * are doing these days. - * - * - look at tweaking function return types to return double instead - * of int; this seems to make things run slightly faster sometimes. - * (though it could be architecture-dependent.) It'd be good to see - * how this does on win32. (Tried it on irix.) Types could use a - * general going-over. - */ - -/* - * Supporting functions - ECMA 15.9.1.* - */ - -#define HalfTimeDomain 8.64e15 -#define HoursPerDay 24.0 -#define MinutesPerDay (HoursPerDay * MinutesPerHour) -#define MinutesPerHour 60.0 -#define SecondsPerDay (MinutesPerDay * SecondsPerMinute) -#define SecondsPerHour (MinutesPerHour * SecondsPerMinute) -#define SecondsPerMinute 60.0 - -#if defined(XP_WIN) || defined(XP_OS2) -/* Work around msvc double optimization bug by making these runtime values; if - * they're available at compile time, msvc optimizes division by them by - * computing the reciprocal and multiplying instead of dividing - this loses - * when the reciprocal isn't representable in a double. - */ -static jsdouble msPerSecond = 1000.0; -static jsdouble msPerDay = SecondsPerDay * 1000.0; -static jsdouble msPerHour = SecondsPerHour * 1000.0; -static jsdouble msPerMinute = SecondsPerMinute * 1000.0; -#else -#define msPerDay (SecondsPerDay * msPerSecond) -#define msPerHour (SecondsPerHour * msPerSecond) -#define msPerMinute (SecondsPerMinute * msPerSecond) -#define msPerSecond 1000.0 -#endif - -#define Day(t) floor((t) / msPerDay) - -static jsdouble -TimeWithinDay(jsdouble t) -{ - jsdouble result; - result = fmod(t, msPerDay); - if (result < 0) - result += msPerDay; - return result; -} - -#define DaysInYear(y) ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0)) \ - ? 366 : 365) - -/* math here has to be f.p, because we need - * floor((1968 - 1969) / 4) == -1 - */ -#define DayFromYear(y) (365 * ((y)-1970) + floor(((y)-1969)/4.0) \ - - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0)) -#define TimeFromYear(y) (DayFromYear(y) * msPerDay) - -static jsint -YearFromTime(jsdouble t) -{ - jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970; - jsdouble t2 = (jsdouble) TimeFromYear(y); - - if (t2 > t) { - y--; - } else { - if (t2 + msPerDay * DaysInYear(y) <= t) - y++; - } - return y; -} - -#define InLeapYear(t) (JSBool) (DaysInYear(YearFromTime(t)) == 366) - -#define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year))) - -/* - * The following array contains the day of year for the first day of - * each month, where index 0 is January, and day 0 is January 1. - */ -static jsdouble firstDayOfMonth[2][12] = { - {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0}, - {0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0} -}; - -#define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m]; - -static intN -MonthFromTime(jsdouble t) -{ - intN d, step; - jsint year = YearFromTime(t); - d = DayWithinYear(t, year); - - if (d < (step = 31)) - return 0; - step += (InLeapYear(t) ? 29 : 28); - if (d < step) - return 1; - if (d < (step += 31)) - return 2; - if (d < (step += 30)) - return 3; - if (d < (step += 31)) - return 4; - if (d < (step += 30)) - return 5; - if (d < (step += 31)) - return 6; - if (d < (step += 31)) - return 7; - if (d < (step += 30)) - return 8; - if (d < (step += 31)) - return 9; - if (d < (step += 30)) - return 10; - return 11; -} - -static intN -DateFromTime(jsdouble t) -{ - intN d, step, next; - jsint year = YearFromTime(t); - d = DayWithinYear(t, year); - - if (d <= (next = 30)) - return d + 1; - step = next; - next += (InLeapYear(t) ? 29 : 28); - if (d <= next) - return d - step; - step = next; - if (d <= (next += 31)) - return d - step; - step = next; - if (d <= (next += 30)) - return d - step; - step = next; - if (d <= (next += 31)) - return d - step; - step = next; - if (d <= (next += 30)) - return d - step; - step = next; - if (d <= (next += 31)) - return d - step; - step = next; - if (d <= (next += 31)) - return d - step; - step = next; - if (d <= (next += 30)) - return d - step; - step = next; - if (d <= (next += 31)) - return d - step; - step = next; - if (d <= (next += 30)) - return d - step; - step = next; - return d - step; -} - -static intN -WeekDay(jsdouble t) -{ - jsint result; - result = (jsint) Day(t) + 4; - result = result % 7; - if (result < 0) - result += 7; - return (intN) result; -} - -#define MakeTime(hour, min, sec, ms) \ -((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms)) - -static jsdouble -MakeDay(jsdouble year, jsdouble month, jsdouble date) -{ - JSBool leap; - jsdouble yearday; - jsdouble monthday; - - year += floor(month / 12); - - month = fmod(month, 12.0); - if (month < 0) - month += 12; - - leap = (DaysInYear((jsint) year) == 366); - - yearday = floor(TimeFromYear(year) / msPerDay); - monthday = DayFromMonth(month, leap); - - return yearday + monthday + date - 1; -} - -#define MakeDate(day, time) ((day) * msPerDay + (time)) - -/* - * Years and leap years on which Jan 1 is a Sunday, Monday, etc. - * - * yearStartingWith[0][i] is an example non-leap year where - * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc. - * - * yearStartingWith[1][i] is an example leap year where - * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc. - */ -static jsint yearStartingWith[2][7] = { - {1978, 1973, 1974, 1975, 1981, 1971, 1977}, - {1984, 1996, 1980, 1992, 1976, 1988, 1972} -}; - -/* - * Find a year for which any given date will fall on the same weekday. - * - * This function should be used with caution when used other than - * for determining DST; it hasn't been proven not to produce an - * incorrect year for times near year boundaries. - */ -static jsint -EquivalentYearForDST(jsint year) -{ - jsint day; - JSBool isLeapYear; - - day = (jsint) DayFromYear(year) + 4; - day = day % 7; - if (day < 0) - day += 7; - - isLeapYear = (DaysInYear(year) == 366); - - return yearStartingWith[isLeapYear][day]; -} - -/* LocalTZA gets set by js_InitDateClass() */ -static jsdouble LocalTZA; - -static jsdouble -DaylightSavingTA(jsdouble t) -{ - volatile int64 PR_t; - int64 ms2us; - int64 offset; - jsdouble result; - - /* abort if NaN */ - if (JSDOUBLE_IS_NaN(t)) - return t; - - /* - * If earlier than 1970 or after 2038, potentially beyond the ken of - * many OSes, map it to an equivalent year before asking. - */ - if (t < 0.0 || t > 2145916800000.0) { - jsint year; - jsdouble day; - - year = EquivalentYearForDST(YearFromTime(t)); - day = MakeDay(year, MonthFromTime(t), DateFromTime(t)); - t = MakeDate(day, TimeWithinDay(t)); - } - - /* put our t in an LL, and map it to usec for prtime */ - JSLL_D2L(PR_t, t); - JSLL_I2L(ms2us, PRMJ_USEC_PER_MSEC); - JSLL_MUL(PR_t, PR_t, ms2us); - - offset = PRMJ_DSTOffset(PR_t); - - JSLL_DIV(offset, offset, ms2us); - JSLL_L2D(result, offset); - return result; -} - - -#define AdjustTime(t) fmod(LocalTZA + DaylightSavingTA(t), msPerDay) - -#define LocalTime(t) ((t) + AdjustTime(t)) - -static jsdouble -UTC(jsdouble t) -{ - return t - AdjustTime(t - LocalTZA); -} - -static intN -HourFromTime(jsdouble t) -{ - intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay); - if (result < 0) - result += (intN)HoursPerDay; - return result; -} - -static intN -MinFromTime(jsdouble t) -{ - intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour); - if (result < 0) - result += (intN)MinutesPerHour; - return result; -} - -static intN -SecFromTime(jsdouble t) -{ - intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute); - if (result < 0) - result += (intN)SecondsPerMinute; - return result; -} - -static intN -msFromTime(jsdouble t) -{ - intN result = (intN) fmod(t, msPerSecond); - if (result < 0) - result += (intN)msPerSecond; - return result; -} - -#define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \ - && !((d < 0 ? -d : d) > HalfTimeDomain)) \ - ? js_DoubleToInteger(d + (+0.)) : *cx->runtime->jsNaN) - -/** - * end of ECMA 'support' functions - */ - -/* - * Other Support routines and definitions - */ - -JSClass js_DateClass = { - js_Date_str, - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Date), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -/* for use by date_parse */ - -static const char* wtb[] = { - "am", "pm", - "monday", "tuesday", "wednesday", "thursday", "friday", - "saturday", "sunday", - "january", "february", "march", "april", "may", "june", - "july", "august", "september", "october", "november", "december", - "gmt", "ut", "utc", - "est", "edt", - "cst", "cdt", - "mst", "mdt", - "pst", "pdt" - /* time zone table needs to be expanded */ -}; - -static int ttb[] = { - -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */ - 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, - 10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */ - 10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */ - 10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */ - 10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */ - 10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */ -}; - -/* helper for date_parse */ -static JSBool -date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off, - int count, int ignoreCase) -{ - JSBool result = JS_FALSE; - /* return true if matches, otherwise, false */ - - while (count > 0 && s1[s1off] && s2[s2off]) { - if (ignoreCase) { - if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) { - break; - } - } else { - if ((jschar)s1[s1off] != s2[s2off]) { - break; - } - } - s1off++; - s2off++; - count--; - } - - if (count == 0) { - result = JS_TRUE; - } - - return result; -} - -/* find UTC time from given date... no 1900 correction! */ -static jsdouble -date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour, - jsdouble min, jsdouble sec, jsdouble msec) -{ - jsdouble day; - jsdouble msec_time; - jsdouble result; - - day = MakeDay(year, mon, mday); - msec_time = MakeTime(hour, min, sec, msec); - result = MakeDate(day, msec_time); - return result; -} - -/* - * See ECMA 15.9.4.[3-10]; - */ -/* XXX this function must be above date_parseString to avoid a - horrid bug in the Win16 1.52 compiler */ -#define MAXARGS 7 -static JSBool -date_UTC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble array[MAXARGS]; - uintN loop; - jsdouble d; - - for (loop = 0; loop < MAXARGS; loop++) { - if (loop < argc) { - if (!js_ValueToNumber(cx, argv[loop], &d)) - return JS_FALSE; - /* return NaN if any arg is NaN */ - if (!JSDOUBLE_IS_FINITE(d)) { - return js_NewNumberValue(cx, d, rval); - } - array[loop] = floor(d); - } else { - array[loop] = 0; - } - } - - /* adjust 2-digit years into the 20th century */ - if (array[0] >= 0 && array[0] <= 99) - array[0] += 1900; - - /* if we got a 0 for 'date' (which is out of range) - * pretend it's a 1. (So Date.UTC(1972, 5) works) */ - if (array[2] < 1) - array[2] = 1; - - d = date_msecFromDate(array[0], array[1], array[2], - array[3], array[4], array[5], array[6]); - d = TIMECLIP(d); - - return js_NewNumberValue(cx, d, rval); -} - -static JSBool -date_parseString(JSString *str, jsdouble *result) -{ - jsdouble msec; - - const jschar *s = JSSTRING_CHARS(str); - size_t limit = JSSTRING_LENGTH(str); - size_t i = 0; - int year = -1; - int mon = -1; - int mday = -1; - int hour = -1; - int min = -1; - int sec = -1; - int c = -1; - int n = -1; - jsdouble tzoffset = -1; /* was an int, overflowed on win16!!! */ - int prevc = 0; - JSBool seenplusminus = JS_FALSE; - int temp; - JSBool seenmonthname = JS_FALSE; - - if (limit == 0) - goto syntax; - while (i < limit) { - c = s[i]; - i++; - if (c <= ' ' || c == ',' || c == '-') { - if (c == '-' && '0' <= s[i] && s[i] <= '9') { - prevc = c; - } - continue; - } - if (c == '(') { /* comments) */ - int depth = 1; - while (i < limit) { - c = s[i]; - i++; - if (c == '(') depth++; - else if (c == ')') - if (--depth <= 0) - break; - } - continue; - } - if ('0' <= c && c <= '9') { - n = c - '0'; - while (i < limit && '0' <= (c = s[i]) && c <= '9') { - n = n * 10 + c - '0'; - i++; - } - - /* allow TZA before the year, so - * 'Wed Nov 05 21:49:11 GMT-0800 1997' - * works */ - - /* uses of seenplusminus allow : in TZA, so Java - * no-timezone style of GMT+4:30 works - */ - - if ((prevc == '+' || prevc == '-')/* && year>=0 */) { - /* make ':' case below change tzoffset */ - seenplusminus = JS_TRUE; - - /* offset */ - if (n < 24) - n = n * 60; /* EG. "GMT-3" */ - else - n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */ - if (prevc == '+') /* plus means east of GMT */ - n = -n; - if (tzoffset != 0 && tzoffset != -1) - goto syntax; - tzoffset = n; - } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) { - if (c <= ' ' || c == ',' || c == '/' || i >= limit) - year = n; - else - goto syntax; - } else if (c == ':') { - if (hour < 0) - hour = /*byte*/ n; - else if (min < 0) - min = /*byte*/ n; - else - goto syntax; - } else if (c == '/') { - /* until it is determined that mon is the actual - month, keep it as 1-based rather than 0-based */ - if (mon < 0) - mon = /*byte*/ n; - else if (mday < 0) - mday = /*byte*/ n; - else - goto syntax; - } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') { - goto syntax; - } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */ - if (tzoffset < 0) - tzoffset -= n; - else - tzoffset += n; - } else if (hour >= 0 && min < 0) { - min = /*byte*/ n; - } else if (prevc == ':' && min >= 0 && sec < 0) { - sec = /*byte*/ n; - } else if (mon < 0) { - mon = /*byte*/n; - } else if (mon >= 0 && mday < 0) { - mday = /*byte*/ n; - } else if (mon >= 0 && mday >= 0 && year < 0) { - year = n; - } else { - goto syntax; - } - prevc = 0; - } else if (c == '/' || c == ':' || c == '+' || c == '-') { - prevc = c; - } else { - size_t st = i - 1; - int k; - while (i < limit) { - c = s[i]; - if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))) - break; - i++; - } - if (i <= st + 1) - goto syntax; - for (k = (sizeof(wtb)/sizeof(char*)); --k >= 0;) - if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) { - int action = ttb[k]; - if (action != 0) { - if (action < 0) { - /* - * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as - * 12:30, instead of blindly adding 12 if PM. - */ - JS_ASSERT(action == -1 || action == -2); - if (hour > 12 || hour < 0) { - goto syntax; - } else { - if (action == -1 && hour == 12) { /* am */ - hour = 0; - } else if (action == -2 && hour != 12) { /* pm */ - hour += 12; - } - } - } else if (action <= 13) { /* month! */ - /* Adjust mon to be 1-based until the final values - for mon, mday and year are adjusted below */ - if (seenmonthname) { - goto syntax; - } - seenmonthname = JS_TRUE; - temp = /*byte*/ (action - 2) + 1; - - if (mon < 0) { - mon = temp; - } else if (mday < 0) { - mday = mon; - mon = temp; - } else if (year < 0) { - year = mon; - mon = temp; - } else { - goto syntax; - } - } else { - tzoffset = action - 10000; - } - } - break; - } - if (k < 0) - goto syntax; - prevc = 0; - } - } - if (year < 0 || mon < 0 || mday < 0) - goto syntax; - /* - Case 1. The input string contains an English month name. - The form of the string can be month f l, or f month l, or - f l month which each evaluate to the same date. - If f and l are both greater than or equal to 70, or - both less than 70, the date is invalid. - The year is taken to be the greater of the values f, l. - If the year is greater than or equal to 70 and less than 100, - it is considered to be the number of years after 1900. - Case 2. The input string is of the form "f/m/l" where f, m and l are - integers, e.g. 7/16/45. - Adjust the mon, mday and year values to achieve 100% MSIE - compatibility. - a. If 0 <= f < 70, f/m/l is interpreted as month/day/year. - i. If year < 100, it is the number of years after 1900 - ii. If year >= 100, it is the number of years after 0. - b. If 70 <= f < 100 - i. If m < 70, f/m/l is interpreted as - year/month/day where year is the number of years after - 1900. - ii. If m >= 70, the date is invalid. - c. If f >= 100 - i. If m < 70, f/m/l is interpreted as - year/month/day where year is the number of years after 0. - ii. If m >= 70, the date is invalid. - */ - if (seenmonthname) { - if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) { - goto syntax; - } - if (mday > year) { - temp = year; - year = mday; - mday = temp; - } - if (year >= 70 && year < 100) { - year += 1900; - } - } else if (mon < 70) { /* (a) month/day/year */ - if (year < 100) { - year += 1900; - } - } else if (mon < 100) { /* (b) year/month/day */ - if (mday < 70) { - temp = year; - year = mon + 1900; - mon = mday; - mday = temp; - } else { - goto syntax; - } - } else { /* (c) year/month/day */ - if (mday < 70) { - temp = year; - year = mon; - mon = mday; - mday = temp; - } else { - goto syntax; - } - } - mon -= 1; /* convert month to 0-based */ - if (sec < 0) - sec = 0; - if (min < 0) - min = 0; - if (hour < 0) - hour = 0; - if (tzoffset == -1) { /* no time zone specified, have to use local */ - jsdouble msec_time; - msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0); - - *result = UTC(msec_time); - return JS_TRUE; - } - - msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0); - msec += tzoffset * msPerMinute; - *result = msec; - return JS_TRUE; - -syntax: - /* syntax error */ - *result = 0; - return JS_FALSE; -} - -static JSBool -date_parse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - jsdouble result; - - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - if (!date_parseString(str, &result)) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); - return JS_TRUE; - } - - result = TIMECLIP(result); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_now(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - int64 us, ms, us2ms; - jsdouble msec_time; - - us = PRMJ_Now(); - JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC); - JSLL_DIV(ms, us, us2ms); - JSLL_L2D(msec_time, ms); - - return js_NewDoubleValue(cx, msec_time, rval); -} - -/* - * Check that obj is an object of class Date, and get the date value. - * Return NULL on failure. - */ -static jsdouble * -date_getProlog(JSContext *cx, JSObject *obj, jsval *argv) -{ - if (!JS_InstanceOf(cx, obj, &js_DateClass, argv)) - return NULL; - return JSVAL_TO_DOUBLE(OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE)); -} - -/* - * See ECMA 15.9.5.4 thru 15.9.5.23 - */ -static JSBool -date_getTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - - return js_NewNumberValue(cx, *date, rval); -} - -static JSBool -date_getYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble *date; - jsdouble result; - - date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - - result = *date; - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = YearFromTime(LocalTime(result)); - - /* Follow ECMA-262 to the letter, contrary to IE JScript. */ - result -= 1900; - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = YearFromTime(LocalTime(result)); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getUTCFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = YearFromTime(result); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = MonthFromTime(LocalTime(result)); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getUTCMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = MonthFromTime(result); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = LocalTime(result); - result = DateFromTime(result); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getUTCDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = DateFromTime(result); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = LocalTime(result); - result = WeekDay(result); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getUTCDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = WeekDay(result); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = HourFromTime(LocalTime(result)); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getUTCHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = HourFromTime(result); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = MinFromTime(LocalTime(result)); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getUTCMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = MinFromTime(result); - return js_NewNumberValue(cx, result, rval); -} - -/* Date.getSeconds is mapped to getUTCSeconds */ - -static JSBool -date_getUTCSeconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = SecFromTime(result); - return js_NewNumberValue(cx, result, rval); -} - -/* Date.getMilliseconds is mapped to getUTCMilliseconds */ - -static JSBool -date_getUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = msFromTime(result); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getTimezoneOffset(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - /* - * Return the time zone offset in minutes for the current locale - * that is appropriate for this time. This value would be a - * constant except for daylight savings time. - */ - result = (result - LocalTime(result)) / msPerMinute; - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_setTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - - if (!js_ValueToNumber(cx, argv[0], &result)) - return JS_FALSE; - - result = TIMECLIP(result); - - *date = result; - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_makeTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - uintN maxargs, JSBool local, jsval *rval) -{ - uintN i; - jsdouble args[4], *argp, *stop; - jsdouble hour, min, sec, msec; - jsdouble lorutime; /* Local or UTC version of *date */ - - jsdouble msec_time; - jsdouble result; - - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - - result = *date; - - /* just return NaN if the date is already NaN */ - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - /* Satisfy the ECMA rule that if a function is called with - * fewer arguments than the specified formal arguments, the - * remaining arguments are set to undefined. Seems like all - * the Date.setWhatever functions in ECMA are only varargs - * beyond the first argument; this should be set to undefined - * if it's not given. This means that "d = new Date(); - * d.setMilliseconds()" returns NaN. Blech. - */ - if (argc == 0) - argc = 1; /* should be safe, because length of all setters is 1 */ - else if (argc > maxargs) - argc = maxargs; /* clamp argc */ - - for (i = 0; i < argc; i++) { - if (!js_ValueToNumber(cx, argv[i], &args[i])) - return JS_FALSE; - if (!JSDOUBLE_IS_FINITE(args[i])) { - *date = *cx->runtime->jsNaN; - return js_NewNumberValue(cx, *date, rval); - } - args[i] = js_DoubleToInteger(args[i]); - } - - if (local) - lorutime = LocalTime(result); - else - lorutime = result; - - argp = args; - stop = argp + argc; - if (maxargs >= 4 && argp < stop) - hour = *argp++; - else - hour = HourFromTime(lorutime); - - if (maxargs >= 3 && argp < stop) - min = *argp++; - else - min = MinFromTime(lorutime); - - if (maxargs >= 2 && argp < stop) - sec = *argp++; - else - sec = SecFromTime(lorutime); - - if (maxargs >= 1 && argp < stop) - msec = *argp; - else - msec = msFromTime(lorutime); - - msec_time = MakeTime(hour, min, sec, msec); - result = MakeDate(Day(lorutime), msec_time); - -/* fprintf(stderr, "%f\n", result); */ - - if (local) - result = UTC(result); - -/* fprintf(stderr, "%f\n", result); */ - - *date = TIMECLIP(result); - return js_NewNumberValue(cx, *date, rval); -} - -static JSBool -date_setMilliseconds(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeTime(cx, obj, argc, argv, 1, JS_TRUE, rval); -} - -static JSBool -date_setUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeTime(cx, obj, argc, argv, 1, JS_FALSE, rval); -} - -static JSBool -date_setSeconds(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeTime(cx, obj, argc, argv, 2, JS_TRUE, rval); -} - -static JSBool -date_setUTCSeconds(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeTime(cx, obj, argc, argv, 2, JS_FALSE, rval); -} - -static JSBool -date_setMinutes(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeTime(cx, obj, argc, argv, 3, JS_TRUE, rval); -} - -static JSBool -date_setUTCMinutes(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeTime(cx, obj, argc, argv, 3, JS_FALSE, rval); -} - -static JSBool -date_setHours(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeTime(cx, obj, argc, argv, 4, JS_TRUE, rval); -} - -static JSBool -date_setUTCHours(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeTime(cx, obj, argc, argv, 4, JS_FALSE, rval); -} - -static JSBool -date_makeDate(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, uintN maxargs, JSBool local, jsval *rval) -{ - uintN i; - jsdouble lorutime; /* local or UTC version of *date */ - jsdouble args[3], *argp, *stop; - jsdouble year, month, day; - jsdouble result; - - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - - result = *date; - - /* see complaint about ECMA in date_MakeTime */ - if (argc == 0) - argc = 1; /* should be safe, because length of all setters is 1 */ - else if (argc > maxargs) - argc = maxargs; /* clamp argc */ - - for (i = 0; i < argc; i++) { - if (!js_ValueToNumber(cx, argv[i], &args[i])) - return JS_FALSE; - if (!JSDOUBLE_IS_FINITE(args[i])) { - *date = *cx->runtime->jsNaN; - return js_NewNumberValue(cx, *date, rval); - } - args[i] = js_DoubleToInteger(args[i]); - } - - /* return NaN if date is NaN and we're not setting the year, - * If we are, use 0 as the time. */ - if (!(JSDOUBLE_IS_FINITE(result))) { - if (maxargs < 3) - return js_NewNumberValue(cx, result, rval); - else - lorutime = +0.; - } else { - if (local) - lorutime = LocalTime(result); - else - lorutime = result; - } - - argp = args; - stop = argp + argc; - if (maxargs >= 3 && argp < stop) - year = *argp++; - else - year = YearFromTime(lorutime); - - if (maxargs >= 2 && argp < stop) - month = *argp++; - else - month = MonthFromTime(lorutime); - - if (maxargs >= 1 && argp < stop) - day = *argp++; - else - day = DateFromTime(lorutime); - - day = MakeDay(year, month, day); /* day within year */ - result = MakeDate(day, TimeWithinDay(lorutime)); - - if (local) - result = UTC(result); - - *date = TIMECLIP(result); - return js_NewNumberValue(cx, *date, rval); -} - -static JSBool -date_setDate(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeDate(cx, obj, argc, argv, 1, JS_TRUE, rval); -} - -static JSBool -date_setUTCDate(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeDate(cx, obj, argc, argv, 1, JS_FALSE, rval); -} - -static JSBool -date_setMonth(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeDate(cx, obj, argc, argv, 2, JS_TRUE, rval); -} - -static JSBool -date_setUTCMonth(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeDate(cx, obj, argc, argv, 2, JS_FALSE, rval); -} - -static JSBool -date_setFullYear(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeDate(cx, obj, argc, argv, 3, JS_TRUE, rval); -} - -static JSBool -date_setUTCFullYear(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeDate(cx, obj, argc, argv, 3, JS_FALSE, rval); -} - -static JSBool -date_setYear(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - jsdouble t; - jsdouble year; - jsdouble day; - jsdouble result; - - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - - result = *date; - - if (!js_ValueToNumber(cx, argv[0], &year)) - return JS_FALSE; - if (!JSDOUBLE_IS_FINITE(year)) { - *date = *cx->runtime->jsNaN; - return js_NewNumberValue(cx, *date, rval); - } - - year = js_DoubleToInteger(year); - - if (!JSDOUBLE_IS_FINITE(result)) { - t = +0.0; - } else { - t = LocalTime(result); - } - - if (year >= 0 && year <= 99) - year += 1900; - - day = MakeDay(year, MonthFromTime(t), DateFromTime(t)); - result = MakeDate(day, TimeWithinDay(t)); - result = UTC(result); - - *date = TIMECLIP(result); - return js_NewNumberValue(cx, *date, rval); -} - -/* constants for toString, toUTCString */ -static char js_NaN_date_str[] = "Invalid Date"; -static const char* days[] = -{ - "Sun","Mon","Tue","Wed","Thu","Fri","Sat" -}; -static const char* months[] = -{ - "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" -}; - -static JSBool -date_toGMTString(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - char buf[100]; - JSString *str; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - - if (!JSDOUBLE_IS_FINITE(*date)) { - JS_snprintf(buf, sizeof buf, js_NaN_date_str); - } else { - jsdouble temp = *date; - - /* Avoid dependence on PRMJ_FormatTimeUSEnglish, because it - * requires a PRMJTime... which only has 16-bit years. Sub-ECMA. - */ - JS_snprintf(buf, sizeof buf, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT", - days[WeekDay(temp)], - DateFromTime(temp), - months[MonthFromTime(temp)], - YearFromTime(temp), - HourFromTime(temp), - MinFromTime(temp), - SecFromTime(temp)); - } - str = JS_NewStringCopyZ(cx, buf); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -/* for Date.toLocaleString; interface to PRMJTime date struct. - * If findEquivalent is true, then try to map the year to an equivalent year - * that's in range. - */ -static void -new_explode(jsdouble timeval, PRMJTime *split, JSBool findEquivalent) -{ - jsint year = YearFromTime(timeval); - int16 adjustedYear; - - /* If the year doesn't fit in a PRMJTime, find something to do about it. */ - if (year > 32767 || year < -32768) { - if (findEquivalent) { - /* We're really just trying to get a timezone string; map the year - * to some equivalent year in the range 0 to 2800. Borrowed from - * A. D. Olsen. - */ - jsint cycles; -#define CYCLE_YEARS 2800L - cycles = (year >= 0) ? year / CYCLE_YEARS - : -1 - (-1 - year) / CYCLE_YEARS; - adjustedYear = (int16)(year - cycles * CYCLE_YEARS); - } else { - /* Clamp it to the nearest representable year. */ - adjustedYear = (int16)((year > 0) ? 32767 : - 32768); - } - } else { - adjustedYear = (int16)year; - } - - split->tm_usec = (int32) msFromTime(timeval) * 1000; - split->tm_sec = (int8) SecFromTime(timeval); - split->tm_min = (int8) MinFromTime(timeval); - split->tm_hour = (int8) HourFromTime(timeval); - split->tm_mday = (int8) DateFromTime(timeval); - split->tm_mon = (int8) MonthFromTime(timeval); - split->tm_wday = (int8) WeekDay(timeval); - split->tm_year = (int16) adjustedYear; - split->tm_yday = (int16) DayWithinYear(timeval, year); - - /* not sure how this affects things, but it doesn't seem - to matter. */ - split->tm_isdst = (DaylightSavingTA(timeval) != 0); -} - -typedef enum formatspec { - FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME -} formatspec; - -/* helper function */ -static JSBool -date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval) -{ - char buf[100]; - JSString *str; - char tzbuf[100]; - JSBool usetz; - size_t i, tzlen; - PRMJTime split; - - if (!JSDOUBLE_IS_FINITE(date)) { - JS_snprintf(buf, sizeof buf, js_NaN_date_str); - } else { - jsdouble local = LocalTime(date); - - /* offset from GMT in minutes. The offset includes daylight savings, - if it applies. */ - jsint minutes = (jsint) floor(AdjustTime(date) / msPerMinute); - - /* map 510 minutes to 0830 hours */ - intN offset = (minutes / 60) * 100 + minutes % 60; - - /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is - * printed as 'GMT-0800' rather than as 'PST' to avoid - * operating-system dependence on strftime (which - * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints - * PST as 'Pacific Standard Time.' This way we always know - * what we're getting, and can parse it if we produce it. - * The OS TZA string is included as a comment. - */ - - /* get a timezone string from the OS to include as a - comment. */ - new_explode(date, &split, JS_TRUE); - if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) { - - /* Decide whether to use the resulting timezone string. - * - * Reject it if it contains any non-ASCII, non-alphanumeric - * characters. It's then likely in some other character - * encoding, and we probably won't display it correctly. - */ - usetz = JS_TRUE; - tzlen = strlen(tzbuf); - if (tzlen > 100) { - usetz = JS_FALSE; - } else { - for (i = 0; i < tzlen; i++) { - jschar c = tzbuf[i]; - if (c > 127 || - !(isalpha(c) || isdigit(c) || - c == ' ' || c == '(' || c == ')')) { - usetz = JS_FALSE; - } - } - } - - /* Also reject it if it's not parenthesized or if it's '()'. */ - if (tzbuf[0] != '(' || tzbuf[1] == ')') - usetz = JS_FALSE; - } else - usetz = JS_FALSE; - - switch (format) { - case FORMATSPEC_FULL: - /* - * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it - * requires a PRMJTime... which only has 16-bit years. Sub-ECMA. - */ - /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */ - JS_snprintf(buf, sizeof buf, - "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s", - days[WeekDay(local)], - months[MonthFromTime(local)], - DateFromTime(local), - YearFromTime(local), - HourFromTime(local), - MinFromTime(local), - SecFromTime(local), - offset, - usetz ? " " : "", - usetz ? tzbuf : ""); - break; - case FORMATSPEC_DATE: - /* Tue Oct 31 2000 */ - JS_snprintf(buf, sizeof buf, - "%s %s %.2d %.4d", - days[WeekDay(local)], - months[MonthFromTime(local)], - DateFromTime(local), - YearFromTime(local)); - break; - case FORMATSPEC_TIME: - /* 09:41:40 GMT-0800 (PST) */ - JS_snprintf(buf, sizeof buf, - "%.2d:%.2d:%.2d GMT%+.4d%s%s", - HourFromTime(local), - MinFromTime(local), - SecFromTime(local), - offset, - usetz ? " " : "", - usetz ? tzbuf : ""); - break; - } - } - - str = JS_NewStringCopyZ(cx, buf); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -date_toLocaleHelper(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval, char *format) -{ - char buf[100]; - JSString *str; - PRMJTime split; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - - if (!JSDOUBLE_IS_FINITE(*date)) { - JS_snprintf(buf, sizeof buf, js_NaN_date_str); - } else { - intN result_len; - jsdouble local = LocalTime(*date); - new_explode(local, &split, JS_FALSE); - - /* let PRMJTime format it. */ - result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split); - - /* If it failed, default to toString. */ - if (result_len == 0) - return date_format(cx, *date, FORMATSPEC_FULL, rval); - - /* Hacked check against undesired 2-digit year 00/00/00 form. */ - if (strcmp(format, "%x") == 0 && result_len >= 6 && - /* Format %x means use OS settings, which may have 2-digit yr, so - hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/ - !isdigit(buf[result_len - 3]) && - isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) && - /* ...but not if starts with 4-digit year, like 2022/3/11. */ - !(isdigit(buf[0]) && isdigit(buf[1]) && - isdigit(buf[2]) && isdigit(buf[3]))) { - JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2), - "%d", js_DateGetYear(cx, obj)); - } - - } - - if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode) - return cx->localeCallbacks->localeToUnicode(cx, buf, rval); - - str = JS_NewStringCopyZ(cx, buf); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -date_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - /* Use '%#c' for windows, because '%c' is - * backward-compatible and non-y2k with msvc; '%#c' requests that a - * full year be used in the result string. - */ - return date_toLocaleHelper(cx, obj, argc, argv, rval, -#if defined(_WIN32) && !defined(__MWERKS__) - "%#c" -#else - "%c" -#endif - ); -} - -static JSBool -date_toLocaleDateString(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - /* Use '%#x' for windows, because '%x' is - * backward-compatible and non-y2k with msvc; '%#x' requests that a - * full year be used in the result string. - */ - return date_toLocaleHelper(cx, obj, argc, argv, rval, -#if defined(_WIN32) && !defined(__MWERKS__) - "%#x" -#else - "%x" -#endif - ); -} - -static JSBool -date_toLocaleTimeString(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_toLocaleHelper(cx, obj, argc, argv, rval, "%X"); -} - -static JSBool -date_toLocaleFormat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *fmt; - - if (argc == 0) - return date_toLocaleString(cx, obj, argc, argv, rval); - - fmt = JS_ValueToString(cx, argv[0]); - if (!fmt) - return JS_FALSE; - - return date_toLocaleHelper(cx, obj, argc, argv, rval, - JS_GetStringBytes(fmt)); -} - -static JSBool -date_toTimeString(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - return date_format(cx, *date, FORMATSPEC_TIME, rval); -} - -static JSBool -date_toDateString(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - return date_format(cx, *date, FORMATSPEC_DATE, rval); -} - -#if JS_HAS_TOSOURCE -#include -#include "jsdtoa.h" - -static JSBool -date_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble *date; - char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes; - JSString *str; - - date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - - numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, *date); - if (!numStr) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - bytes = JS_smprintf("(new %s(%s))", js_Date_str, numStr); - if (!bytes) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - str = JS_NewString(cx, bytes, strlen(bytes)); - if (!str) { - free(bytes); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} -#endif - -static JSBool -date_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - return date_format(cx, *date, FORMATSPEC_FULL, rval); -} - -static JSBool -date_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - /* It is an error to call date_valueOf on a non-date object, but we don't - * need to check for that explicitly here because every path calls - * date_getProlog, which does the check. - */ - - /* If called directly with no arguments, convert to a time number. */ - if (argc == 0) - return date_getTime(cx, obj, argc, argv, rval); - - /* Convert to number only if the hint was given, otherwise favor string. */ - if (argc == 1) { - JSString *str, *str2; - - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - str2 = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]); - if (js_EqualStrings(str, str2)) - return date_getTime(cx, obj, argc, argv, rval); - } - return date_toString(cx, obj, argc, argv, rval); -} - - -/* - * creation and destruction - */ - -static JSFunctionSpec date_static_methods[] = { - {"UTC", date_UTC, MAXARGS,0,0 }, - {"parse", date_parse, 1,0,0 }, - {"now", date_now, 0,0,0 }, - {0,0,0,0,0} -}; - -static JSFunctionSpec date_methods[] = { - {"getTime", date_getTime, 0,0,0 }, - {"getTimezoneOffset", date_getTimezoneOffset, 0,0,0 }, - {"getYear", date_getYear, 0,0,0 }, - {"getFullYear", date_getFullYear, 0,0,0 }, - {"getUTCFullYear", date_getUTCFullYear, 0,0,0 }, - {"getMonth", date_getMonth, 0,0,0 }, - {"getUTCMonth", date_getUTCMonth, 0,0,0 }, - {"getDate", date_getDate, 0,0,0 }, - {"getUTCDate", date_getUTCDate, 0,0,0 }, - {"getDay", date_getDay, 0,0,0 }, - {"getUTCDay", date_getUTCDay, 0,0,0 }, - {"getHours", date_getHours, 0,0,0 }, - {"getUTCHours", date_getUTCHours, 0,0,0 }, - {"getMinutes", date_getMinutes, 0,0,0 }, - {"getUTCMinutes", date_getUTCMinutes, 0,0,0 }, - {"getSeconds", date_getUTCSeconds, 0,0,0 }, - {"getUTCSeconds", date_getUTCSeconds, 0,0,0 }, - {"getMilliseconds", date_getUTCMilliseconds,0,0,0 }, - {"getUTCMilliseconds", date_getUTCMilliseconds,0,0,0 }, - {"setTime", date_setTime, 1,0,0 }, - {"setYear", date_setYear, 1,0,0 }, - {"setFullYear", date_setFullYear, 3,0,0 }, - {"setUTCFullYear", date_setUTCFullYear, 3,0,0 }, - {"setMonth", date_setMonth, 2,0,0 }, - {"setUTCMonth", date_setUTCMonth, 2,0,0 }, - {"setDate", date_setDate, 1,0,0 }, - {"setUTCDate", date_setUTCDate, 1,0,0 }, - {"setHours", date_setHours, 4,0,0 }, - {"setUTCHours", date_setUTCHours, 4,0,0 }, - {"setMinutes", date_setMinutes, 3,0,0 }, - {"setUTCMinutes", date_setUTCMinutes, 3,0,0 }, - {"setSeconds", date_setSeconds, 2,0,0 }, - {"setUTCSeconds", date_setUTCSeconds, 2,0,0 }, - {"setMilliseconds", date_setMilliseconds, 1,0,0 }, - {"setUTCMilliseconds", date_setUTCMilliseconds,1,0,0 }, - {"toUTCString", date_toGMTString, 0,0,0 }, - {js_toLocaleString_str, date_toLocaleString, 0,0,0 }, - {"toLocaleDateString", date_toLocaleDateString,0,0,0 }, - {"toLocaleTimeString", date_toLocaleTimeString,0,0,0 }, - {"toLocaleFormat", date_toLocaleFormat, 1,0,0 }, - {"toDateString", date_toDateString, 0,0,0 }, - {"toTimeString", date_toTimeString, 0,0,0 }, -#if JS_HAS_TOSOURCE - {js_toSource_str, date_toSource, 0,0,0 }, -#endif - {js_toString_str, date_toString, 0,0,0 }, - {js_valueOf_str, date_valueOf, 0,0,0 }, - {0,0,0,0,0} -}; - -static jsdouble * -date_constructor(JSContext *cx, JSObject* obj) -{ - jsdouble *date; - - date = js_NewDouble(cx, 0.0, 0); - if (!date) - return NULL; - OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, DOUBLE_TO_JSVAL(date)); - return date; -} - -static JSBool -Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble *date; - JSString *str; - jsdouble d; - - /* Date called as function. */ - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - int64 us, ms, us2ms; - jsdouble msec_time; - - /* NSPR 2.0 docs say 'We do not support PRMJ_NowMS and PRMJ_NowS', - * so compute ms from PRMJ_Now. - */ - us = PRMJ_Now(); - JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC); - JSLL_DIV(ms, us, us2ms); - JSLL_L2D(msec_time, ms); - - return date_format(cx, msec_time, FORMATSPEC_FULL, rval); - } - - /* Date called as constructor. */ - if (argc == 0) { - int64 us, ms, us2ms; - jsdouble msec_time; - - date = date_constructor(cx, obj); - if (!date) - return JS_FALSE; - - us = PRMJ_Now(); - JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC); - JSLL_DIV(ms, us, us2ms); - JSLL_L2D(msec_time, ms); - - *date = msec_time; - } else if (argc == 1) { - if (!JSVAL_IS_STRING(argv[0])) { - /* the argument is a millisecond number */ - if (!js_ValueToNumber(cx, argv[0], &d)) - return JS_FALSE; - date = date_constructor(cx, obj); - if (!date) - return JS_FALSE; - *date = TIMECLIP(d); - } else { - /* the argument is a string; parse it. */ - date = date_constructor(cx, obj); - if (!date) - return JS_FALSE; - - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - - if (!date_parseString(str, date)) - *date = *cx->runtime->jsNaN; - *date = TIMECLIP(*date); - } - } else { - jsdouble array[MAXARGS]; - uintN loop; - jsdouble double_arg; - jsdouble day; - jsdouble msec_time; - - for (loop = 0; loop < MAXARGS; loop++) { - if (loop < argc) { - if (!js_ValueToNumber(cx, argv[loop], &double_arg)) - return JS_FALSE; - /* if any arg is NaN, make a NaN date object - and return */ - if (!JSDOUBLE_IS_FINITE(double_arg)) { - date = date_constructor(cx, obj); - if (!date) - return JS_FALSE; - *date = *cx->runtime->jsNaN; - return JS_TRUE; - } - array[loop] = js_DoubleToInteger(double_arg); - } else { - if (loop == 2) { - array[loop] = 1; /* Default the date argument to 1. */ - } else { - array[loop] = 0; - } - } - } - - date = date_constructor(cx, obj); - if (!date) - return JS_FALSE; - - /* adjust 2-digit years into the 20th century */ - if (array[0] >= 0 && array[0] <= 99) - array[0] += 1900; - - day = MakeDay(array[0], array[1], array[2]); - msec_time = MakeTime(array[3], array[4], array[5], array[6]); - msec_time = MakeDate(day, msec_time); - msec_time = UTC(msec_time); - *date = TIMECLIP(msec_time); - } - return JS_TRUE; -} - -JSObject * -js_InitDateClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto; - jsdouble *proto_date; - - /* set static LocalTZA */ - LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond); - proto = JS_InitClass(cx, obj, NULL, &js_DateClass, Date, MAXARGS, - NULL, date_methods, NULL, date_static_methods); - if (!proto) - return NULL; - - /* Alias toUTCString with toGMTString. (ECMA B.2.6) */ - if (!JS_AliasProperty(cx, proto, "toUTCString", "toGMTString")) - return NULL; - - /* Set the value of the Date.prototype date to NaN */ - proto_date = date_constructor(cx, proto); - if (!proto_date) - return NULL; - *proto_date = *cx->runtime->jsNaN; - - return proto; -} - -JS_FRIEND_API(JSObject *) -js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time) -{ - JSObject *obj; - jsdouble *date; - - obj = js_NewObject(cx, &js_DateClass, NULL, NULL); - if (!obj) - return NULL; - - date = date_constructor(cx, obj); - if (!date) - return NULL; - - *date = msec_time; - return obj; -} - -JS_FRIEND_API(JSObject *) -js_NewDateObject(JSContext* cx, int year, int mon, int mday, - int hour, int min, int sec) -{ - JSObject *obj; - jsdouble msec_time; - - msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0); - obj = js_NewDateObjectMsec(cx, UTC(msec_time)); - return obj; -} - -JS_FRIEND_API(JSBool) -js_DateIsValid(JSContext *cx, JSObject* obj) -{ - jsdouble *date = date_getProlog(cx, obj, NULL); - - if (!date || JSDOUBLE_IS_NaN(*date)) - return JS_FALSE; - else - return JS_TRUE; -} - -JS_FRIEND_API(int) -js_DateGetYear(JSContext *cx, JSObject* obj) -{ - jsdouble *date = date_getProlog(cx, obj, NULL); - - /* Preserve legacy API behavior of returning 0 for invalid dates. */ - if (!date || JSDOUBLE_IS_NaN(*date)) - return 0; - return (int) YearFromTime(LocalTime(*date)); -} - -JS_FRIEND_API(int) -js_DateGetMonth(JSContext *cx, JSObject* obj) -{ - jsdouble *date = date_getProlog(cx, obj, NULL); - - if (!date || JSDOUBLE_IS_NaN(*date)) - return 0; - return (int) MonthFromTime(LocalTime(*date)); -} - -JS_FRIEND_API(int) -js_DateGetDate(JSContext *cx, JSObject* obj) -{ - jsdouble *date = date_getProlog(cx, obj, NULL); - - if (!date || JSDOUBLE_IS_NaN(*date)) - return 0; - return (int) DateFromTime(LocalTime(*date)); -} - -JS_FRIEND_API(int) -js_DateGetHours(JSContext *cx, JSObject* obj) -{ - jsdouble *date = date_getProlog(cx, obj, NULL); - - if (!date || JSDOUBLE_IS_NaN(*date)) - return 0; - return (int) HourFromTime(LocalTime(*date)); -} - -JS_FRIEND_API(int) -js_DateGetMinutes(JSContext *cx, JSObject* obj) -{ - jsdouble *date = date_getProlog(cx, obj, NULL); - - if (!date || JSDOUBLE_IS_NaN(*date)) - return 0; - return (int) MinFromTime(LocalTime(*date)); -} - -JS_FRIEND_API(int) -js_DateGetSeconds(JSContext *cx, JSObject* obj) -{ - jsdouble *date = date_getProlog(cx, obj, NULL); - - if (!date || JSDOUBLE_IS_NaN(*date)) - return 0; - return (int) SecFromTime(*date); -} - -JS_FRIEND_API(void) -js_DateSetYear(JSContext *cx, JSObject *obj, int year) -{ - jsdouble local; - jsdouble *date = date_getProlog(cx, obj, NULL); - if (!date) - return; - local = LocalTime(*date); - /* reset date if it was NaN */ - if (JSDOUBLE_IS_NaN(local)) - local = 0; - local = date_msecFromDate(year, - MonthFromTime(local), - DateFromTime(local), - HourFromTime(local), - MinFromTime(local), - SecFromTime(local), - msFromTime(local)); - *date = UTC(local); -} - -JS_FRIEND_API(void) -js_DateSetMonth(JSContext *cx, JSObject *obj, int month) -{ - jsdouble local; - jsdouble *date = date_getProlog(cx, obj, NULL); - if (!date) - return; - local = LocalTime(*date); - /* bail if date was NaN */ - if (JSDOUBLE_IS_NaN(local)) - return; - local = date_msecFromDate(YearFromTime(local), - month, - DateFromTime(local), - HourFromTime(local), - MinFromTime(local), - SecFromTime(local), - msFromTime(local)); - *date = UTC(local); -} - -JS_FRIEND_API(void) -js_DateSetDate(JSContext *cx, JSObject *obj, int date) -{ - jsdouble local; - jsdouble *datep = date_getProlog(cx, obj, NULL); - if (!datep) - return; - local = LocalTime(*datep); - if (JSDOUBLE_IS_NaN(local)) - return; - local = date_msecFromDate(YearFromTime(local), - MonthFromTime(local), - date, - HourFromTime(local), - MinFromTime(local), - SecFromTime(local), - msFromTime(local)); - *datep = UTC(local); -} - -JS_FRIEND_API(void) -js_DateSetHours(JSContext *cx, JSObject *obj, int hours) -{ - jsdouble local; - jsdouble *date = date_getProlog(cx, obj, NULL); - if (!date) - return; - local = LocalTime(*date); - if (JSDOUBLE_IS_NaN(local)) - return; - local = date_msecFromDate(YearFromTime(local), - MonthFromTime(local), - DateFromTime(local), - hours, - MinFromTime(local), - SecFromTime(local), - msFromTime(local)); - *date = UTC(local); -} - -JS_FRIEND_API(void) -js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes) -{ - jsdouble local; - jsdouble *date = date_getProlog(cx, obj, NULL); - if (!date) - return; - local = LocalTime(*date); - if (JSDOUBLE_IS_NaN(local)) - return; - local = date_msecFromDate(YearFromTime(local), - MonthFromTime(local), - DateFromTime(local), - HourFromTime(local), - minutes, - SecFromTime(local), - msFromTime(local)); - *date = UTC(local); -} - -JS_FRIEND_API(void) -js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds) -{ - jsdouble local; - jsdouble *date = date_getProlog(cx, obj, NULL); - if (!date) - return; - local = LocalTime(*date); - if (JSDOUBLE_IS_NaN(local)) - return; - local = date_msecFromDate(YearFromTime(local), - MonthFromTime(local), - DateFromTime(local), - HourFromTime(local), - MinFromTime(local), - seconds, - msFromTime(local)); - *date = UTC(local); -} - -JS_FRIEND_API(jsdouble) -js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj) -{ - jsdouble *date = date_getProlog(cx, obj, NULL); - if (!date || JSDOUBLE_IS_NaN(*date)) - return 0; - return (*date); -} diff --git a/spidermonkey/src/jsdate.h b/spidermonkey/src/jsdate.h deleted file mode 100644 index 88bd5f5..0000000 --- a/spidermonkey/src/jsdate.h +++ /dev/null @@ -1,120 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS Date class interface. - */ - -#ifndef jsdate_h___ -#define jsdate_h___ - -JS_BEGIN_EXTERN_C - -extern JSClass js_DateClass; - -extern JSObject * -js_InitDateClass(JSContext *cx, JSObject *obj); - -/* - * These functions provide a C interface to the date/time object - */ - -/* - * Construct a new Date Object from a time value given in milliseconds UTC - * since the epoch. - */ -extern JS_FRIEND_API(JSObject*) -js_NewDateObjectMsec(JSContext* cx, jsdouble msec_time); - -/* - * Construct a new Date Object from an exploded local time value. - */ -extern JS_FRIEND_API(JSObject*) -js_NewDateObject(JSContext* cx, int year, int mon, int mday, - int hour, int min, int sec); - -/* - * Detect whether the internal date value is NaN. (Because failure is - * out-of-band for js_DateGet*) - */ -extern JS_FRIEND_API(JSBool) -js_DateIsValid(JSContext *cx, JSObject* obj); - -extern JS_FRIEND_API(int) -js_DateGetYear(JSContext *cx, JSObject* obj); - -extern JS_FRIEND_API(int) -js_DateGetMonth(JSContext *cx, JSObject* obj); - -extern JS_FRIEND_API(int) -js_DateGetDate(JSContext *cx, JSObject* obj); - -extern JS_FRIEND_API(int) -js_DateGetHours(JSContext *cx, JSObject* obj); - -extern JS_FRIEND_API(int) -js_DateGetMinutes(JSContext *cx, JSObject* obj); - -extern JS_FRIEND_API(int) -js_DateGetSeconds(JSContext *cx, JSObject* obj); - -extern JS_FRIEND_API(void) -js_DateSetYear(JSContext *cx, JSObject *obj, int year); - -extern JS_FRIEND_API(void) -js_DateSetMonth(JSContext *cx, JSObject *obj, int year); - -extern JS_FRIEND_API(void) -js_DateSetDate(JSContext *cx, JSObject *obj, int date); - -extern JS_FRIEND_API(void) -js_DateSetHours(JSContext *cx, JSObject *obj, int hours); - -extern JS_FRIEND_API(void) -js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes); - -extern JS_FRIEND_API(void) -js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds); - -extern JS_FRIEND_API(jsdouble) -js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj); - -JS_END_EXTERN_C - -#endif /* jsdate_h___ */ diff --git a/spidermonkey/src/jsdbgapi.c b/spidermonkey/src/jsdbgapi.c deleted file mode 100644 index 722a210..0000000 --- a/spidermonkey/src/jsdbgapi.c +++ /dev/null @@ -1,1441 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS debugging API. - */ -#include "jsstddef.h" -#include -#include "jstypes.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jsclist.h" -#include "jsapi.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsdbgapi.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" - -typedef struct JSTrap { - JSCList links; - JSScript *script; - jsbytecode *pc; - JSOp op; - JSTrapHandler handler; - void *closure; -} JSTrap; - -static JSTrap * -FindTrap(JSRuntime *rt, JSScript *script, jsbytecode *pc) -{ - JSTrap *trap; - - for (trap = (JSTrap *)rt->trapList.next; - trap != (JSTrap *)&rt->trapList; - trap = (JSTrap *)trap->links.next) { - if (trap->script == script && trap->pc == pc) - return trap; - } - return NULL; -} - -void -js_PatchOpcode(JSContext *cx, JSScript *script, jsbytecode *pc, JSOp op) -{ - JSTrap *trap; - - trap = FindTrap(cx->runtime, script, pc); - if (trap) - trap->op = op; - else - *pc = (jsbytecode)op; -} - -JS_PUBLIC_API(JSBool) -JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, - JSTrapHandler handler, void *closure) -{ - JSRuntime *rt; - JSTrap *trap; - - rt = cx->runtime; - trap = FindTrap(rt, script, pc); - if (trap) { - JS_ASSERT(trap->script == script && trap->pc == pc); - JS_ASSERT(*pc == JSOP_TRAP); - } else { - trap = (JSTrap *) JS_malloc(cx, sizeof *trap); - if (!trap || !js_AddRoot(cx, &trap->closure, "trap->closure")) { - if (trap) - JS_free(cx, trap); - return JS_FALSE; - } - JS_APPEND_LINK(&trap->links, &rt->trapList); - trap->script = script; - trap->pc = pc; - trap->op = (JSOp)*pc; - *pc = JSOP_TRAP; - } - trap->handler = handler; - trap->closure = closure; - return JS_TRUE; -} - -JS_PUBLIC_API(JSOp) -JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc) -{ - JSTrap *trap; - - trap = FindTrap(cx->runtime, script, pc); - if (!trap) { - JS_ASSERT(0); /* XXX can't happen */ - return JSOP_LIMIT; - } - return trap->op; -} - -static void -DestroyTrap(JSContext *cx, JSTrap *trap) -{ - JS_REMOVE_LINK(&trap->links); - *trap->pc = (jsbytecode)trap->op; - js_RemoveRoot(cx->runtime, &trap->closure); - JS_free(cx, trap); -} - -JS_PUBLIC_API(void) -JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc, - JSTrapHandler *handlerp, void **closurep) -{ - JSTrap *trap; - - trap = FindTrap(cx->runtime, script, pc); - if (handlerp) - *handlerp = trap ? trap->handler : NULL; - if (closurep) - *closurep = trap ? trap->closure : NULL; - if (trap) - DestroyTrap(cx, trap); -} - -JS_PUBLIC_API(void) -JS_ClearScriptTraps(JSContext *cx, JSScript *script) -{ - JSRuntime *rt; - JSTrap *trap, *next; - - rt = cx->runtime; - for (trap = (JSTrap *)rt->trapList.next; - trap != (JSTrap *)&rt->trapList; - trap = next) { - next = (JSTrap *)trap->links.next; - if (trap->script == script) - DestroyTrap(cx, trap); - } -} - -JS_PUBLIC_API(void) -JS_ClearAllTraps(JSContext *cx) -{ - JSRuntime *rt; - JSTrap *trap, *next; - - rt = cx->runtime; - for (trap = (JSTrap *)rt->trapList.next; - trap != (JSTrap *)&rt->trapList; - trap = next) { - next = (JSTrap *)trap->links.next; - DestroyTrap(cx, trap); - } -} - -JS_PUBLIC_API(JSTrapStatus) -JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval) -{ - JSTrap *trap; - JSTrapStatus status; - jsint op; - - trap = FindTrap(cx->runtime, script, pc); - if (!trap) { - JS_ASSERT(0); /* XXX can't happen */ - return JSTRAP_ERROR; - } - /* - * It's important that we not use 'trap->' after calling the callback -- - * the callback might remove the trap! - */ - op = (jsint)trap->op; - status = trap->handler(cx, script, pc, rval, trap->closure); - if (status == JSTRAP_CONTINUE) { - /* By convention, return the true op to the interpreter in rval. */ - *rval = INT_TO_JSVAL(op); - } - return status; -} - -JS_PUBLIC_API(JSBool) -JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure) -{ - rt->interruptHandler = handler; - rt->interruptHandlerData = closure; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep) -{ - if (handlerp) - *handlerp = (JSTrapHandler)rt->interruptHandler; - if (closurep) - *closurep = rt->interruptHandlerData; - rt->interruptHandler = 0; - rt->interruptHandlerData = 0; - return JS_TRUE; -} - -/************************************************************************/ - -typedef struct JSWatchPoint { - JSCList links; - JSObject *object; /* weak link, see js_FinalizeObject */ - JSScopeProperty *sprop; - JSPropertyOp setter; - JSWatchPointHandler handler; - void *closure; - uintN flags; -} JSWatchPoint; - -#define JSWP_LIVE 0x1 /* live because set and not cleared */ -#define JSWP_HELD 0x2 /* held while running handler/setter */ - -static JSBool -DropWatchPoint(JSContext *cx, JSWatchPoint *wp, uintN flag) -{ - JSBool ok; - JSScopeProperty *sprop; - JSObject *pobj; - JSProperty *prop; - JSPropertyOp setter; - - ok = JS_TRUE; - wp->flags &= ~flag; - if (wp->flags != 0) - return JS_TRUE; - - /* - * Remove wp from the list, then if there are no other watchpoints for - * wp->sprop in any scope, restore wp->sprop->setter from wp. - */ - JS_REMOVE_LINK(&wp->links); - sprop = wp->sprop; - - /* - * If js_ChangeNativePropertyAttrs fails, propagate failure after removing - * wp->closure's root and freeing wp. - */ - setter = js_GetWatchedSetter(cx->runtime, NULL, sprop); - if (!setter) { - ok = js_LookupProperty(cx, wp->object, sprop->id, &pobj, &prop); - - /* - * If the property wasn't found on wp->object or didn't exist, then - * someone else has dealt with this sprop, and we don't need to change - * the property attributes. - */ - if (ok && prop) { - if (pobj == wp->object) { - JS_ASSERT(OBJ_SCOPE(pobj)->object == pobj); - - sprop = js_ChangeScopePropertyAttrs(cx, OBJ_SCOPE(pobj), sprop, - 0, sprop->attrs, - sprop->getter, - wp->setter); - if (!sprop) - ok = JS_FALSE; - } - OBJ_DROP_PROPERTY(cx, pobj, prop); - } - } - - js_RemoveRoot(cx->runtime, &wp->closure); - JS_free(cx, wp); - return ok; -} - -void -js_MarkWatchPoints(JSContext *cx) -{ - JSRuntime *rt; - JSWatchPoint *wp; - - rt = cx->runtime; - for (wp = (JSWatchPoint *)rt->watchPointList.next; - wp != (JSWatchPoint *)&rt->watchPointList; - wp = (JSWatchPoint *)wp->links.next) { - MARK_SCOPE_PROPERTY(cx, wp->sprop); - if (wp->sprop->attrs & JSPROP_SETTER) - JS_MarkGCThing(cx, wp->setter, "wp->setter", NULL); - } -} - -static JSWatchPoint * -FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id) -{ - JSWatchPoint *wp; - - for (wp = (JSWatchPoint *)rt->watchPointList.next; - wp != (JSWatchPoint *)&rt->watchPointList; - wp = (JSWatchPoint *)wp->links.next) { - if (wp->object == scope->object && wp->sprop->id == id) - return wp; - } - return NULL; -} - -JSScopeProperty * -js_FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id) -{ - JSWatchPoint *wp; - - wp = FindWatchPoint(rt, scope, id); - if (!wp) - return NULL; - return wp->sprop; -} - -JSPropertyOp -js_GetWatchedSetter(JSRuntime *rt, JSScope *scope, - const JSScopeProperty *sprop) -{ - JSWatchPoint *wp; - - for (wp = (JSWatchPoint *)rt->watchPointList.next; - wp != (JSWatchPoint *)&rt->watchPointList; - wp = (JSWatchPoint *)wp->links.next) { - if ((!scope || wp->object == scope->object) && wp->sprop == sprop) - return wp->setter; - } - return NULL; -} - -JSBool JS_DLL_CALLBACK -js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSRuntime *rt; - JSWatchPoint *wp; - JSScopeProperty *sprop; - jsval propid, userid; - JSScope *scope; - JSBool ok; - - rt = cx->runtime; - for (wp = (JSWatchPoint *)rt->watchPointList.next; - wp != (JSWatchPoint *)&rt->watchPointList; - wp = (JSWatchPoint *)wp->links.next) { - sprop = wp->sprop; - if (wp->object == obj && SPROP_USERID(sprop) == id && - !(wp->flags & JSWP_HELD)) { - wp->flags |= JSWP_HELD; - - JS_LOCK_OBJ(cx, obj); - propid = ID_TO_VALUE(sprop->id); - userid = (sprop->flags & SPROP_HAS_SHORTID) - ? INT_TO_JSVAL(sprop->shortid) - : propid; - scope = OBJ_SCOPE(obj); - JS_UNLOCK_OBJ(cx, obj); - - /* NB: wp is held, so we can safely dereference it still. */ - ok = wp->handler(cx, obj, propid, - SPROP_HAS_VALID_SLOT(sprop, scope) - ? OBJ_GET_SLOT(cx, obj, sprop->slot) - : JSVAL_VOID, - vp, wp->closure); - if (ok) { - /* - * Create a pseudo-frame for the setter invocation so that any - * stack-walking security code under the setter will correctly - * identify the guilty party. So that the watcher appears to - * be active to obj_eval and other such code, point frame.pc - * at the JSOP_STOP at the end of the script. - */ - JSObject *closure; - JSClass *clasp; - JSFunction *fun; - JSScript *script; - uintN nslots; - jsval smallv[5]; - jsval *argv; - JSStackFrame frame; - - closure = (JSObject *) wp->closure; - clasp = OBJ_GET_CLASS(cx, closure); - if (clasp == &js_FunctionClass) { - fun = (JSFunction *) JS_GetPrivate(cx, closure); - script = FUN_SCRIPT(fun); - } else if (clasp == &js_ScriptClass) { - fun = NULL; - script = (JSScript *) JS_GetPrivate(cx, closure); - } else { - fun = NULL; - script = NULL; - } - - nslots = 2; - if (fun) { - nslots += fun->nargs; - if (FUN_NATIVE(fun)) - nslots += fun->u.n.extra; - } - - if (nslots <= JS_ARRAY_LENGTH(smallv)) { - argv = smallv; - } else { - argv = JS_malloc(cx, nslots * sizeof(jsval)); - if (!argv) { - DropWatchPoint(cx, wp, JSWP_HELD); - return JS_FALSE; - } - } - - argv[0] = OBJECT_TO_JSVAL(closure); - argv[1] = JSVAL_NULL; - memset(argv + 2, 0, (nslots - 2) * sizeof(jsval)); - - memset(&frame, 0, sizeof(frame)); - frame.script = script; - if (script) { - JS_ASSERT(script->length >= JSOP_STOP_LENGTH); - frame.pc = script->code + script->length - - JSOP_STOP_LENGTH; - } - frame.fun = fun; - frame.argv = argv + 2; - frame.down = cx->fp; - frame.scopeChain = OBJ_GET_PARENT(cx, closure); - - cx->fp = &frame; - ok = !wp->setter || - ((sprop->attrs & JSPROP_SETTER) - ? js_InternalCall(cx, obj, OBJECT_TO_JSVAL(wp->setter), - 1, vp, vp) - : wp->setter(cx, OBJ_THIS_OBJECT(cx, obj), userid, vp)); - cx->fp = frame.down; - if (argv != smallv) - JS_free(cx, argv); - } - return DropWatchPoint(cx, wp, JSWP_HELD) && ok; - } - } - return JS_TRUE; -} - -JSBool JS_DLL_CALLBACK -js_watch_set_wrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSObject *funobj; - JSFunction *wrapper; - jsval userid; - - funobj = JSVAL_TO_OBJECT(argv[-2]); - JS_ASSERT(OBJ_GET_CLASS(cx, funobj) == &js_FunctionClass); - wrapper = (JSFunction *) JS_GetPrivate(cx, funobj); - userid = ATOM_KEY(wrapper->atom); - *rval = argv[0]; - return js_watch_set(cx, obj, userid, rval); -} - -JSPropertyOp -js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, JSPropertyOp setter) -{ - JSAtom *atom; - JSFunction *wrapper; - - if (!(attrs & JSPROP_SETTER)) - return &js_watch_set; /* & to silence schoolmarmish MSVC */ - - if (JSID_IS_ATOM(id)) { - atom = JSID_TO_ATOM(id); - } else if (JSID_IS_INT(id)) { - atom = js_AtomizeInt(cx, JSID_TO_INT(id), 0); - if (!atom) - return NULL; - } else { - atom = NULL; - } - wrapper = js_NewFunction(cx, NULL, js_watch_set_wrapper, 1, 0, - OBJ_GET_PARENT(cx, (JSObject *)setter), - atom); - if (!wrapper) - return NULL; - return (JSPropertyOp) wrapper->object; -} - -JS_PUBLIC_API(JSBool) -JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id, - JSWatchPointHandler handler, void *closure) -{ - JSAtom *atom; - jsid propid; - JSObject *pobj; - JSProperty *prop; - JSScopeProperty *sprop; - JSRuntime *rt; - JSBool ok; - JSWatchPoint *wp; - JSPropertyOp watcher; - - if (!OBJ_IS_NATIVE(obj)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH, - OBJ_GET_CLASS(cx, obj)->name); - return JS_FALSE; - } - - if (JSVAL_IS_INT(id)) { - propid = INT_JSVAL_TO_JSID(id); - atom = NULL; - } else { - atom = js_ValueToStringAtom(cx, id); - if (!atom) - return JS_FALSE; - propid = ATOM_TO_JSID(atom); - } - - if (!js_LookupProperty(cx, obj, propid, &pobj, &prop)) - return JS_FALSE; - sprop = (JSScopeProperty *) prop; - rt = cx->runtime; - if (!sprop) { - /* Check for a deleted symbol watchpoint, which holds its property. */ - sprop = js_FindWatchPoint(rt, OBJ_SCOPE(obj), propid); - if (!sprop) { - /* Make a new property in obj so we can watch for the first set. */ - if (!js_DefineProperty(cx, obj, propid, JSVAL_VOID, - NULL, NULL, JSPROP_ENUMERATE, - &prop)) { - return JS_FALSE; - } - sprop = (JSScopeProperty *) prop; - } - } else if (pobj != obj) { - /* Clone the prototype property so we can watch the right object. */ - jsval value; - JSPropertyOp getter, setter; - uintN attrs, flags; - intN shortid; - - if (OBJ_IS_NATIVE(pobj)) { - value = SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)) - ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot) - : JSVAL_VOID; - getter = sprop->getter; - setter = sprop->setter; - attrs = sprop->attrs; - flags = sprop->flags; - shortid = sprop->shortid; - } else { - if (!OBJ_GET_PROPERTY(cx, pobj, id, &value) || - !OBJ_GET_ATTRIBUTES(cx, pobj, id, prop, &attrs)) { - OBJ_DROP_PROPERTY(cx, pobj, prop); - return JS_FALSE; - } - getter = setter = NULL; - flags = 0; - shortid = 0; - } - OBJ_DROP_PROPERTY(cx, pobj, prop); - - /* Recall that obj is native, whether or not pobj is native. */ - if (!js_DefineNativeProperty(cx, obj, propid, value, getter, setter, - attrs, flags, shortid, &prop)) { - return JS_FALSE; - } - sprop = (JSScopeProperty *) prop; - } - - /* - * At this point, prop/sprop exists in obj, obj is locked, and we must - * OBJ_DROP_PROPERTY(cx, obj, prop) before returning. - */ - ok = JS_TRUE; - wp = FindWatchPoint(rt, OBJ_SCOPE(obj), propid); - if (!wp) { - watcher = js_WrapWatchedSetter(cx, propid, sprop->attrs, sprop->setter); - if (!watcher) { - ok = JS_FALSE; - goto out; - } - - wp = (JSWatchPoint *) JS_malloc(cx, sizeof *wp); - if (!wp) { - ok = JS_FALSE; - goto out; - } - wp->handler = NULL; - wp->closure = NULL; - ok = js_AddRoot(cx, &wp->closure, "wp->closure"); - if (!ok) { - JS_free(cx, wp); - goto out; - } - wp->object = obj; - JS_ASSERT(sprop->setter != js_watch_set || pobj != obj); - wp->setter = sprop->setter; - wp->flags = JSWP_LIVE; - - /* XXXbe nest in obj lock here */ - sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, 0, sprop->attrs, - sprop->getter, watcher); - if (!sprop) { - /* Self-link so DropWatchPoint can JS_REMOVE_LINK it. */ - JS_INIT_CLIST(&wp->links); - DropWatchPoint(cx, wp, JSWP_LIVE); - ok = JS_FALSE; - goto out; - } - wp->sprop = sprop; - - /* - * Now that wp is fully initialized, append it to rt's wp list. - * Because obj is locked we know that no other thread could have added - * a watchpoint for (obj, propid). - */ - JS_ASSERT(!FindWatchPoint(rt, OBJ_SCOPE(obj), propid)); - JS_APPEND_LINK(&wp->links, &rt->watchPointList); - } - wp->handler = handler; - wp->closure = closure; - -out: - OBJ_DROP_PROPERTY(cx, obj, prop); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id, - JSWatchPointHandler *handlerp, void **closurep) -{ - JSRuntime *rt; - JSWatchPoint *wp; - - rt = cx->runtime; - for (wp = (JSWatchPoint *)rt->watchPointList.next; - wp != (JSWatchPoint *)&rt->watchPointList; - wp = (JSWatchPoint *)wp->links.next) { - if (wp->object == obj && SPROP_USERID(wp->sprop) == id) { - if (handlerp) - *handlerp = wp->handler; - if (closurep) - *closurep = wp->closure; - return DropWatchPoint(cx, wp, JSWP_LIVE); - } - } - if (handlerp) - *handlerp = NULL; - if (closurep) - *closurep = NULL; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj) -{ - JSRuntime *rt; - JSWatchPoint *wp, *next; - - rt = cx->runtime; - for (wp = (JSWatchPoint *)rt->watchPointList.next; - wp != (JSWatchPoint *)&rt->watchPointList; - wp = next) { - next = (JSWatchPoint *)wp->links.next; - if (wp->object == obj && !DropWatchPoint(cx, wp, JSWP_LIVE)) - return JS_FALSE; - } - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_ClearAllWatchPoints(JSContext *cx) -{ - JSRuntime *rt; - JSWatchPoint *wp, *next; - - rt = cx->runtime; - for (wp = (JSWatchPoint *)rt->watchPointList.next; - wp != (JSWatchPoint *)&rt->watchPointList; - wp = next) { - next = (JSWatchPoint *)wp->links.next; - if (!DropWatchPoint(cx, wp, JSWP_LIVE)) - return JS_FALSE; - } - return JS_TRUE; -} - -/************************************************************************/ - -JS_PUBLIC_API(uintN) -JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc) -{ - return js_PCToLineNumber(cx, script, pc); -} - -JS_PUBLIC_API(jsbytecode *) -JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno) -{ - return js_LineNumberToPC(script, lineno); -} - -JS_PUBLIC_API(JSScript *) -JS_GetFunctionScript(JSContext *cx, JSFunction *fun) -{ - return FUN_SCRIPT(fun); -} - -JS_PUBLIC_API(JSNative) -JS_GetFunctionNative(JSContext *cx, JSFunction *fun) -{ - return FUN_NATIVE(fun); -} - -JS_PUBLIC_API(JSPrincipals *) -JS_GetScriptPrincipals(JSContext *cx, JSScript *script) -{ - return script->principals; -} - -/************************************************************************/ - -/* - * Stack Frame Iterator - */ -JS_PUBLIC_API(JSStackFrame *) -JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp) -{ - *iteratorp = (*iteratorp == NULL) ? cx->fp : (*iteratorp)->down; - return *iteratorp; -} - -JS_PUBLIC_API(JSScript *) -JS_GetFrameScript(JSContext *cx, JSStackFrame *fp) -{ - return fp->script; -} - -JS_PUBLIC_API(jsbytecode *) -JS_GetFramePC(JSContext *cx, JSStackFrame *fp) -{ - return fp->pc; -} - -JS_PUBLIC_API(JSStackFrame *) -JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp) -{ - if (!fp) - fp = cx->fp; - while ((fp = fp->down) != NULL) { - if (fp->script) - return fp; - } - return NULL; -} - -JS_PUBLIC_API(JSPrincipals *) -JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp) -{ - if (fp->fun) { - JSRuntime *rt = cx->runtime; - - if (rt->findObjectPrincipals) { - JSObject *callee = JSVAL_TO_OBJECT(fp->argv[-2]); - - if (fp->fun->object != callee) - return rt->findObjectPrincipals(cx, callee); - /* FALL THROUGH */ - } - } - if (fp->script) - return fp->script->principals; - return NULL; -} - -JS_PUBLIC_API(JSPrincipals *) -JS_EvalFramePrincipals(JSContext *cx, JSStackFrame *fp, JSStackFrame *caller) -{ - JSRuntime *rt; - JSObject *callee; - JSPrincipals *principals, *callerPrincipals; - - rt = cx->runtime; - if (rt->findObjectPrincipals) { - callee = JSVAL_TO_OBJECT(fp->argv[-2]); - principals = rt->findObjectPrincipals(cx, callee); - } else { - principals = NULL; - } - if (!caller) - return principals; - callerPrincipals = JS_StackFramePrincipals(cx, caller); - return (callerPrincipals && principals && - callerPrincipals->subsume(callerPrincipals, principals)) - ? principals - : callerPrincipals; -} - -JS_PUBLIC_API(void *) -JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp) -{ - if (fp->annotation && fp->script) { - JSPrincipals *principals = JS_StackFramePrincipals(cx, fp); - - if (principals && principals->globalPrivilegesEnabled(cx, principals)) { - /* - * Give out an annotation only if privileges have not been revoked - * or disabled globally. - */ - return fp->annotation; - } - } - - return NULL; -} - -JS_PUBLIC_API(void) -JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation) -{ - fp->annotation = annotation; -} - -JS_PUBLIC_API(void *) -JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp) -{ - JSPrincipals *principals; - - principals = JS_StackFramePrincipals(cx, fp); - if (!principals) - return NULL; - return principals->getPrincipalArray(cx, principals); -} - -JS_PUBLIC_API(JSBool) -JS_IsNativeFrame(JSContext *cx, JSStackFrame *fp) -{ - return !fp->script; -} - -/* this is deprecated, use JS_GetFrameScopeChain instead */ -JS_PUBLIC_API(JSObject *) -JS_GetFrameObject(JSContext *cx, JSStackFrame *fp) -{ - return fp->scopeChain; -} - -JS_PUBLIC_API(JSObject *) -JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp) -{ - /* Force creation of argument and call objects if not yet created */ - (void) JS_GetFrameCallObject(cx, fp); - return js_GetScopeChain(cx, fp); -} - -JS_PUBLIC_API(JSObject *) -JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp) -{ - if (! fp->fun) - return NULL; - - /* Force creation of argument object if not yet created */ - (void) js_GetArgsObject(cx, fp); - - /* - * XXX ill-defined: null return here means error was reported, unlike a - * null returned above or in the #else - */ - return js_GetCallObject(cx, fp, NULL); -} - - -JS_PUBLIC_API(JSObject *) -JS_GetFrameThis(JSContext *cx, JSStackFrame *fp) -{ - return fp->thisp; -} - -JS_PUBLIC_API(JSFunction *) -JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp) -{ - return fp->fun; -} - -JS_PUBLIC_API(JSObject *) -JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp) -{ - return fp->argv && fp->fun ? JSVAL_TO_OBJECT(fp->argv[-2]) : NULL; -} - -JS_PUBLIC_API(JSBool) -JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp) -{ - return (fp->flags & JSFRAME_CONSTRUCTING) != 0; -} - -JS_PUBLIC_API(JSObject *) -JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp) -{ - return fp->argv ? JSVAL_TO_OBJECT(fp->argv[-2]) : NULL; -} - -JS_PUBLIC_API(JSBool) -JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp) -{ - return (fp->flags & JSFRAME_DEBUGGER) != 0; -} - -JS_PUBLIC_API(jsval) -JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp) -{ - return fp->rval; -} - -JS_PUBLIC_API(void) -JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval) -{ - fp->rval = rval; -} - -/************************************************************************/ - -JS_PUBLIC_API(const char *) -JS_GetScriptFilename(JSContext *cx, JSScript *script) -{ - return script->filename; -} - -JS_PUBLIC_API(uintN) -JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script) -{ - return script->lineno; -} - -JS_PUBLIC_API(uintN) -JS_GetScriptLineExtent(JSContext *cx, JSScript *script) -{ - return js_GetScriptLineExtent(script); -} - -JS_PUBLIC_API(JSVersion) -JS_GetScriptVersion(JSContext *cx, JSScript *script) -{ - return script->version & JSVERSION_MASK; -} - -/***************************************************************************/ - -JS_PUBLIC_API(void) -JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata) -{ - rt->newScriptHook = hook; - rt->newScriptHookData = callerdata; -} - -JS_PUBLIC_API(void) -JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook, - void *callerdata) -{ - rt->destroyScriptHook = hook; - rt->destroyScriptHookData = callerdata; -} - -/***************************************************************************/ - -JS_PUBLIC_API(JSBool) -JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp, - const jschar *chars, uintN length, - const char *filename, uintN lineno, - jsval *rval) -{ - JSObject *scobj; - uint32 flags, options; - JSScript *script; - JSBool ok; - - scobj = JS_GetFrameScopeChain(cx, fp); - if (!scobj) - return JS_FALSE; - - /* - * XXX Hack around ancient compiler API to propagate the JSFRAME_SPECIAL - * flags to the code generator (see js_EmitTree's TOK_SEMI case). - */ - flags = fp->flags; - fp->flags |= JSFRAME_DEBUGGER | JSFRAME_EVAL; - options = cx->options; - cx->options = options | JSOPTION_COMPILE_N_GO; - script = JS_CompileUCScriptForPrincipals(cx, scobj, - JS_StackFramePrincipals(cx, fp), - chars, length, filename, lineno); - fp->flags = flags; - cx->options = options; - if (!script) - return JS_FALSE; - - ok = js_Execute(cx, scobj, script, fp, JSFRAME_DEBUGGER | JSFRAME_EVAL, - rval); - js_DestroyScript(cx, script); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp, - const char *bytes, uintN length, - const char *filename, uintN lineno, - jsval *rval) -{ - jschar *chars; - JSBool ok; - size_t len = length; - - chars = js_InflateString(cx, bytes, &len); - if (!chars) - return JS_FALSE; - length = (uintN) len; - ok = JS_EvaluateUCInStackFrame(cx, fp, chars, length, filename, lineno, - rval); - JS_free(cx, chars); - - return ok; -} - -/************************************************************************/ - -/* XXXbe this all needs to be reworked to avoid requiring JSScope types. */ - -JS_PUBLIC_API(JSScopeProperty *) -JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp) -{ - JSScopeProperty *sprop; - JSScope *scope; - - sprop = *iteratorp; - scope = OBJ_SCOPE(obj); - - /* XXXbe minor(?) incompatibility: iterate in reverse definition order */ - if (!sprop) { - sprop = SCOPE_LAST_PROP(scope); - } else { - while ((sprop = sprop->parent) != NULL) { - if (!SCOPE_HAD_MIDDLE_DELETE(scope)) - break; - if (SCOPE_HAS_PROPERTY(scope, sprop)) - break; - } - } - *iteratorp = sprop; - return sprop; -} - -JS_PUBLIC_API(JSBool) -JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, - JSPropertyDesc *pd) -{ - JSPropertyOp getter; - JSScope *scope; - JSScopeProperty *aprop; - jsval lastException; - JSBool wasThrowing; - - pd->id = ID_TO_VALUE(sprop->id); - - wasThrowing = cx->throwing; - if (wasThrowing) { - lastException = cx->exception; - if (JSVAL_IS_GCTHING(lastException) && - !js_AddRoot(cx, &lastException, "lastException")) { - return JS_FALSE; - } - cx->throwing = JS_FALSE; - } - - if (!js_GetProperty(cx, obj, sprop->id, &pd->value)) { - if (!cx->throwing) { - pd->flags = JSPD_ERROR; - pd->value = JSVAL_VOID; - } else { - pd->flags = JSPD_EXCEPTION; - pd->value = cx->exception; - } - } else { - pd->flags = 0; - } - - cx->throwing = wasThrowing; - if (wasThrowing) { - cx->exception = lastException; - if (JSVAL_IS_GCTHING(lastException)) - js_RemoveRoot(cx->runtime, &lastException); - } - - getter = sprop->getter; - pd->flags |= ((sprop->attrs & JSPROP_ENUMERATE) ? JSPD_ENUMERATE : 0) - | ((sprop->attrs & JSPROP_READONLY) ? JSPD_READONLY : 0) - | ((sprop->attrs & JSPROP_PERMANENT) ? JSPD_PERMANENT : 0) - | ((getter == js_GetCallVariable) ? JSPD_VARIABLE : 0) - | ((getter == js_GetArgument) ? JSPD_ARGUMENT : 0) - | ((getter == js_GetLocalVariable) ? JSPD_VARIABLE : 0); - - /* for Call Object 'real' getter isn't passed in to us */ - if (OBJ_GET_CLASS(cx, obj) == &js_CallClass && - getter == js_CallClass.getProperty) { - /* - * Property of a heavyweight function's variable object having the - * class-default getter. It's either an argument if permanent, or a - * nested function if impermanent. Local variables have a special - * getter (js_GetCallVariable, tested above) and setter, and not the - * class default. - */ - pd->flags |= (sprop->attrs & JSPROP_PERMANENT) - ? JSPD_ARGUMENT - : JSPD_VARIABLE; - } - - pd->spare = 0; - pd->slot = (pd->flags & (JSPD_ARGUMENT | JSPD_VARIABLE)) - ? sprop->shortid - : 0; - pd->alias = JSVAL_VOID; - scope = OBJ_SCOPE(obj); - if (SPROP_HAS_VALID_SLOT(sprop, scope)) { - for (aprop = SCOPE_LAST_PROP(scope); aprop; aprop = aprop->parent) { - if (aprop != sprop && aprop->slot == sprop->slot) { - pd->alias = ID_TO_VALUE(aprop->id); - break; - } - } - } - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda) -{ - JSClass *clasp; - JSScope *scope; - uint32 i, n; - JSPropertyDesc *pd; - JSScopeProperty *sprop; - - clasp = OBJ_GET_CLASS(cx, obj); - if (!OBJ_IS_NATIVE(obj) || (clasp->flags & JSCLASS_NEW_ENUMERATE)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CANT_DESCRIBE_PROPS, clasp->name); - return JS_FALSE; - } - if (!clasp->enumerate(cx, obj)) - return JS_FALSE; - - /* have no props, or object's scope has not mutated from that of proto */ - scope = OBJ_SCOPE(obj); - if (scope->object != obj || scope->entryCount == 0) { - pda->length = 0; - pda->array = NULL; - return JS_TRUE; - } - - 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; - i = 0; - for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { - if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop)) - continue; - if (!js_AddRoot(cx, &pd[i].id, NULL)) - goto bad; - if (!js_AddRoot(cx, &pd[i].value, NULL)) - goto bad; - if (!JS_GetPropertyDesc(cx, obj, sprop, &pd[i])) - goto bad; - if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, &pd[i].alias, NULL)) - goto bad; - if (++i == n) - break; - } - pda->length = i; - pda->array = pd; - return JS_TRUE; - -bad: - pda->length = i + 1; - pda->array = pd; - JS_PutPropertyDescArray(cx, pda); - return JS_FALSE; -} - -JS_PUBLIC_API(void) -JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda) -{ - JSPropertyDesc *pd; - uint32 i; - - pd = pda->array; - for (i = 0; i < pda->length; i++) { - js_RemoveRoot(cx->runtime, &pd[i].id); - js_RemoveRoot(cx->runtime, &pd[i].value); - if (pd[i].flags & JSPD_ALIAS) - js_RemoveRoot(cx->runtime, &pd[i].alias); - } - JS_free(cx, pd); -} - -/************************************************************************/ - -JS_PUBLIC_API(JSBool) -JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure) -{ - rt->debuggerHandler = handler; - rt->debuggerHandlerData = closure; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure) -{ - rt->sourceHandler = handler; - rt->sourceHandlerData = closure; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure) -{ - rt->executeHook = hook; - rt->executeHookData = closure; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure) -{ - rt->callHook = hook; - rt->callHookData = closure; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure) -{ - rt->objectHook = hook; - rt->objectHookData = closure; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_SetThrowHook(JSRuntime *rt, JSTrapHandler hook, void *closure) -{ - rt->throwHook = hook; - rt->throwHookData = closure; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure) -{ - rt->debugErrorHook = hook; - rt->debugErrorHookData = closure; - return JS_TRUE; -} - -/************************************************************************/ - -JS_PUBLIC_API(size_t) -JS_GetObjectTotalSize(JSContext *cx, JSObject *obj) -{ - size_t nbytes; - JSScope *scope; - - nbytes = sizeof *obj + obj->map->nslots * sizeof obj->slots[0]; - if (OBJ_IS_NATIVE(obj)) { - scope = OBJ_SCOPE(obj); - if (scope->object == obj) { - nbytes += sizeof *scope; - nbytes += SCOPE_CAPACITY(scope) * sizeof(JSScopeProperty *); - } - } - return nbytes; -} - -static size_t -GetAtomTotalSize(JSContext *cx, JSAtom *atom) -{ - size_t nbytes; - - nbytes = sizeof *atom; - if (ATOM_IS_STRING(atom)) { - nbytes += sizeof(JSString); - nbytes += (ATOM_TO_STRING(atom)->length + 1) * sizeof(jschar); - } else if (ATOM_IS_DOUBLE(atom)) { - nbytes += sizeof(jsdouble); - } else if (ATOM_IS_OBJECT(atom)) { - nbytes += JS_GetObjectTotalSize(cx, ATOM_TO_OBJECT(atom)); - } - return nbytes; -} - -JS_PUBLIC_API(size_t) -JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun) -{ - size_t nbytes; - - nbytes = sizeof *fun; - if (fun->object) - nbytes += JS_GetObjectTotalSize(cx, fun->object); - if (FUN_INTERPRETED(fun)) - nbytes += JS_GetScriptTotalSize(cx, fun->u.i.script); - if (fun->atom) - nbytes += GetAtomTotalSize(cx, fun->atom); - return nbytes; -} - -#include "jsemit.h" - -JS_PUBLIC_API(size_t) -JS_GetScriptTotalSize(JSContext *cx, JSScript *script) -{ - size_t nbytes, pbytes; - JSObject *obj; - jsatomid i; - jssrcnote *sn, *notes; - JSTryNote *tn, *tnotes; - JSPrincipals *principals; - - nbytes = sizeof *script; - obj = script->object; - if (obj) - nbytes += JS_GetObjectTotalSize(cx, obj); - - nbytes += script->length * sizeof script->code[0]; - nbytes += script->atomMap.length * sizeof script->atomMap.vector[0]; - for (i = 0; i < script->atomMap.length; i++) - nbytes += GetAtomTotalSize(cx, script->atomMap.vector[i]); - - if (script->filename) - nbytes += strlen(script->filename) + 1; - - notes = SCRIPT_NOTES(script); - for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) - continue; - nbytes += (sn - notes + 1) * sizeof *sn; - - tnotes = script->trynotes; - if (tnotes) { - for (tn = tnotes; tn->catchStart; tn++) - continue; - nbytes += (tn - tnotes + 1) * sizeof *tn; - } - - principals = script->principals; - if (principals) { - JS_ASSERT(principals->refcount); - pbytes = sizeof *principals; - if (principals->refcount > 1) - pbytes = JS_HOWMANY(pbytes, principals->refcount); - nbytes += pbytes; - } - - return nbytes; -} - -JS_PUBLIC_API(uint32) -JS_GetTopScriptFilenameFlags(JSContext *cx, JSStackFrame *fp) -{ - if (!fp) - fp = cx->fp; - while (fp) { - if (fp->script) { - return JS_GetScriptFilenameFlags(fp->script); - } - fp = fp->down; - } - return 0; - } - -JS_PUBLIC_API(uint32) -JS_GetScriptFilenameFlags(JSScript *script) -{ - JS_ASSERT(script); - if (!script->filename) - return JSFILENAME_NULL; - return js_GetScriptFilenameFlags(script->filename); -} - -JS_PUBLIC_API(JSBool) -JS_FlagScriptFilenamePrefix(JSRuntime *rt, const char *prefix, uint32 flags) -{ - if (!js_SaveScriptFilenameRT(rt, prefix, flags)) - return JS_FALSE; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_IsSystemObject(JSContext *cx, JSObject *obj) -{ - return (*js_GetGCThingFlags(obj) & GCF_SYSTEM) != 0; -} - -JS_PUBLIC_API(void) -JS_FlagSystemObject(JSContext *cx, JSObject *obj) -{ - uint8 *flagp; - - flagp = js_GetGCThingFlags(obj); - *flagp |= GCF_SYSTEM; -} diff --git a/spidermonkey/src/jsdbgapi.h b/spidermonkey/src/jsdbgapi.h deleted file mode 100644 index d2e1f1c..0000000 --- a/spidermonkey/src/jsdbgapi.h +++ /dev/null @@ -1,406 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsdbgapi_h___ -#define jsdbgapi_h___ -/* - * JS debugger API. - */ -#include "jsapi.h" -#include "jsopcode.h" -#include "jsprvtd.h" - -JS_BEGIN_EXTERN_C - -extern void -js_PatchOpcode(JSContext *cx, JSScript *script, jsbytecode *pc, JSOp op); - -extern JS_PUBLIC_API(JSBool) -JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, - JSTrapHandler handler, void *closure); - -extern JS_PUBLIC_API(JSOp) -JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc); - -extern JS_PUBLIC_API(void) -JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc, - JSTrapHandler *handlerp, void **closurep); - -extern JS_PUBLIC_API(void) -JS_ClearScriptTraps(JSContext *cx, JSScript *script); - -extern JS_PUBLIC_API(void) -JS_ClearAllTraps(JSContext *cx); - -extern JS_PUBLIC_API(JSTrapStatus) -JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval); - -extern JS_PUBLIC_API(JSBool) -JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure); - -extern JS_PUBLIC_API(JSBool) -JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep); - -/************************************************************************/ - -extern JS_PUBLIC_API(JSBool) -JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id, - JSWatchPointHandler handler, void *closure); - -extern JS_PUBLIC_API(JSBool) -JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id, - JSWatchPointHandler *handlerp, void **closurep); - -extern JS_PUBLIC_API(JSBool) -JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj); - -extern JS_PUBLIC_API(JSBool) -JS_ClearAllWatchPoints(JSContext *cx); - -#ifdef JS_HAS_OBJ_WATCHPOINT -/* - * Hide these non-API function prototypes by testing whether the internal - * header file "jsconfig.h" has been included. - */ -extern void -js_MarkWatchPoints(JSContext *cx); - -extern JSScopeProperty * -js_FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id); - -extern JSPropertyOp -js_GetWatchedSetter(JSRuntime *rt, JSScope *scope, - const JSScopeProperty *sprop); - -extern JSBool JS_DLL_CALLBACK -js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -extern JSBool JS_DLL_CALLBACK -js_watch_set_wrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); - -extern JSPropertyOp -js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, JSPropertyOp setter); - -#endif /* JS_HAS_OBJ_WATCHPOINT */ - -/************************************************************************/ - -extern JS_PUBLIC_API(uintN) -JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc); - -extern JS_PUBLIC_API(jsbytecode *) -JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno); - -extern JS_PUBLIC_API(JSScript *) -JS_GetFunctionScript(JSContext *cx, JSFunction *fun); - -extern JS_PUBLIC_API(JSNative) -JS_GetFunctionNative(JSContext *cx, JSFunction *fun); - -extern JS_PUBLIC_API(JSPrincipals *) -JS_GetScriptPrincipals(JSContext *cx, JSScript *script); - -/* - * Stack Frame Iterator - * - * Used to iterate through the JS stack frames to extract - * information from the frames. - */ - -extern JS_PUBLIC_API(JSStackFrame *) -JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp); - -extern JS_PUBLIC_API(JSScript *) -JS_GetFrameScript(JSContext *cx, JSStackFrame *fp); - -extern JS_PUBLIC_API(jsbytecode *) -JS_GetFramePC(JSContext *cx, JSStackFrame *fp); - -/* - * Get the closest scripted frame below fp. If fp is null, start from cx->fp. - */ -extern JS_PUBLIC_API(JSStackFrame *) -JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp); - -/* - * Return a weak reference to fp's principals. A null return does not denote - * an error, it means there are no principals. - */ -extern JS_PUBLIC_API(JSPrincipals *) -JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp); - -/* - * This API is like JS_StackFramePrincipals(cx, caller), except that if - * cx->runtime->findObjectPrincipals is non-null, it returns the weaker of - * the caller's principals and the object principals of fp's callee function - * object (fp->argv[-2]), which is eval, Function, or a similar eval-like - * method. The caller parameter should be JS_GetScriptedCaller(cx, fp). - * - * All eval-like methods must use JS_EvalFramePrincipals to acquire a weak - * reference to the correct principals for the eval call to be secure, given - * an embedding that calls JS_SetObjectPrincipalsFinder (see jsapi.h). - */ -extern JS_PUBLIC_API(JSPrincipals *) -JS_EvalFramePrincipals(JSContext *cx, JSStackFrame *fp, JSStackFrame *caller); - -extern JS_PUBLIC_API(void *) -JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp); - -extern JS_PUBLIC_API(void) -JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation); - -extern JS_PUBLIC_API(void *) -JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp); - -extern JS_PUBLIC_API(JSBool) -JS_IsNativeFrame(JSContext *cx, JSStackFrame *fp); - -/* this is deprecated, use JS_GetFrameScopeChain instead */ -extern JS_PUBLIC_API(JSObject *) -JS_GetFrameObject(JSContext *cx, JSStackFrame *fp); - -extern JS_PUBLIC_API(JSObject *) -JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp); - -extern JS_PUBLIC_API(JSObject *) -JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp); - -extern JS_PUBLIC_API(JSObject *) -JS_GetFrameThis(JSContext *cx, JSStackFrame *fp); - -extern JS_PUBLIC_API(JSFunction *) -JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp); - -extern JS_PUBLIC_API(JSObject *) -JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp); - -/* XXXrginda Initially published with typo */ -#define JS_IsContructorFrame JS_IsConstructorFrame -extern JS_PUBLIC_API(JSBool) -JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp); - -extern JS_PUBLIC_API(JSBool) -JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp); - -extern JS_PUBLIC_API(jsval) -JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp); - -extern JS_PUBLIC_API(void) -JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval); - -/** - * Return fp's callee function object (fp->argv[-2]) if it has one. - */ -extern JS_PUBLIC_API(JSObject *) -JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp); - -/************************************************************************/ - -extern JS_PUBLIC_API(const char *) -JS_GetScriptFilename(JSContext *cx, JSScript *script); - -extern JS_PUBLIC_API(uintN) -JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script); - -extern JS_PUBLIC_API(uintN) -JS_GetScriptLineExtent(JSContext *cx, JSScript *script); - -extern JS_PUBLIC_API(JSVersion) -JS_GetScriptVersion(JSContext *cx, JSScript *script); - -/************************************************************************/ - -/* - * Hook setters for script creation and destruction, see jsprvtd.h for the - * typedefs. These macros provide binary compatibility and newer, shorter - * synonyms. - */ -#define JS_SetNewScriptHook JS_SetNewScriptHookProc -#define JS_SetDestroyScriptHook JS_SetDestroyScriptHookProc - -extern JS_PUBLIC_API(void) -JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata); - -extern JS_PUBLIC_API(void) -JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook, - void *callerdata); - -/************************************************************************/ - -extern JS_PUBLIC_API(JSBool) -JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp, - const jschar *chars, uintN length, - const char *filename, uintN lineno, - jsval *rval); - -extern JS_PUBLIC_API(JSBool) -JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp, - const char *bytes, uintN length, - const char *filename, uintN lineno, - jsval *rval); - -/************************************************************************/ - -typedef struct JSPropertyDesc { - jsval id; /* primary id, a string or int */ - jsval value; /* property value */ - uint8 flags; /* flags, see below */ - uint8 spare; /* unused */ - uint16 slot; /* argument/variable slot */ - jsval alias; /* alias id if JSPD_ALIAS flag */ -} JSPropertyDesc; - -#define JSPD_ENUMERATE 0x01 /* visible to for/in loop */ -#define JSPD_READONLY 0x02 /* assignment is error */ -#define JSPD_PERMANENT 0x04 /* property cannot be deleted */ -#define JSPD_ALIAS 0x08 /* property has an alias id */ -#define JSPD_ARGUMENT 0x10 /* argument to function */ -#define JSPD_VARIABLE 0x20 /* local variable in function */ -#define JSPD_EXCEPTION 0x40 /* exception occurred fetching the property, */ - /* value is exception */ -#define JSPD_ERROR 0x80 /* native getter returned JS_FALSE without */ - /* throwing an exception */ - -typedef struct JSPropertyDescArray { - uint32 length; /* number of elements in array */ - JSPropertyDesc *array; /* alloc'd by Get, freed by Put */ -} JSPropertyDescArray; - -extern JS_PUBLIC_API(JSScopeProperty *) -JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp); - -extern JS_PUBLIC_API(JSBool) -JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, - JSPropertyDesc *pd); - -extern JS_PUBLIC_API(JSBool) -JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda); - -extern JS_PUBLIC_API(void) -JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda); - -/************************************************************************/ - -extern JS_PUBLIC_API(JSBool) -JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure); - -extern JS_PUBLIC_API(JSBool) -JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure); - -extern JS_PUBLIC_API(JSBool) -JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure); - -extern JS_PUBLIC_API(JSBool) -JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure); - -extern JS_PUBLIC_API(JSBool) -JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure); - -extern JS_PUBLIC_API(JSBool) -JS_SetThrowHook(JSRuntime *rt, JSTrapHandler hook, void *closure); - -extern JS_PUBLIC_API(JSBool) -JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure); - -/************************************************************************/ - -extern JS_PUBLIC_API(size_t) -JS_GetObjectTotalSize(JSContext *cx, JSObject *obj); - -extern JS_PUBLIC_API(size_t) -JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun); - -extern JS_PUBLIC_API(size_t) -JS_GetScriptTotalSize(JSContext *cx, JSScript *script); - -/* - * Get the top-most running script on cx starting from fp, or from the top of - * cx's frame stack if fp is null, and return its script filename flags. If - * the script has a null filename member, return JSFILENAME_NULL. - */ -extern JS_PUBLIC_API(uint32) -JS_GetTopScriptFilenameFlags(JSContext *cx, JSStackFrame *fp); - -/* - * Get the script filename flags for the script. If the script doesn't have a - * filename, return JSFILENAME_NULL. - */ -extern JS_PUBLIC_API(uint32) -JS_GetScriptFilenameFlags(JSScript *script); - -/* - * Associate flags with a script filename prefix in rt, so that any subsequent - * script compilation will inherit those flags if the script's filename is the - * same as prefix, or if prefix is a substring of the script's filename. - * - * The API defines only one flag bit, JSFILENAME_SYSTEM, leaving the remaining - * 31 bits up to the API client to define. The union of all 32 bits must not - * be a legal combination, however, in order to preserve JSFILENAME_NULL as a - * unique value. API clients may depend on JSFILENAME_SYSTEM being a set bit - * in JSFILENAME_NULL -- a script with a null filename member is presumed to - * be a "system" script. - */ -extern JS_PUBLIC_API(JSBool) -JS_FlagScriptFilenamePrefix(JSRuntime *rt, const char *prefix, uint32 flags); - -#define JSFILENAME_NULL 0xffffffff /* null script filename */ -#define JSFILENAME_SYSTEM 0x00000001 /* "system" script, see below */ - -/* - * Return true if obj is a "system" object, that is, one flagged by a prior - * call to JS_FlagSystemObject(cx, obj). What "system" means is up to the API - * client, but it can be used to coordinate access control policies based on - * script filenames and their prefixes, using JS_FlagScriptFilenamePrefix and - * JS_GetTopScriptFilenameFlags. - */ -extern JS_PUBLIC_API(JSBool) -JS_IsSystemObject(JSContext *cx, JSObject *obj); - -/* - * Flag obj as a "system" object. The API client can flag system objects to - * optimize access control checks. The engine stores but does not interpret - * the per-object flag set by this call. - */ -extern JS_PUBLIC_API(void) -JS_FlagSystemObject(JSContext *cx, JSObject *obj); - -JS_END_EXTERN_C - -#endif /* jsdbgapi_h___ */ diff --git a/spidermonkey/src/jsdhash.c b/spidermonkey/src/jsdhash.c deleted file mode 100644 index 295883b..0000000 --- a/spidermonkey/src/jsdhash.c +++ /dev/null @@ -1,826 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 JavaScript code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1999-2001 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Brendan Eich (Original Author) - * Chris Waterson - * - * Alternatively, the contents of this file may be used under the terms of - * either of 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 ***** */ - -/* - * Double hashing implementation. - */ -#include -#include -#include -#include "jsbit.h" -#include "jsdhash.h" -#include "jsutil.h" /* for JS_ASSERT */ - -#ifdef JS_DHASHMETER -# if defined MOZILLA_CLIENT && defined DEBUG_XXXbrendan -# include "nsTraceMalloc.h" -# endif -# define METER(x) x -#else -# define METER(x) /* nothing */ -#endif - -/* - * The following DEBUG-only code is used to assert that calls to one of - * table->ops or to an enumerator do not cause re-entry into a call that - * can mutate the table. The recursion level is stored in additional - * space allocated at the end of the entry store to avoid changing - * JSDHashTable, which could cause issues when mixing DEBUG and - * non-DEBUG components. - */ -#ifdef DEBUG - -#define RECURSION_LEVEL(table_) (*(uint32*)(table_->entryStore + \ - JS_DHASH_TABLE_SIZE(table_) * \ - table_->entrySize)) - -#define ENTRY_STORE_EXTRA sizeof(uint32) -#define INCREMENT_RECURSION_LEVEL(table_) (++RECURSION_LEVEL(table_)) -#define DECREMENT_RECURSION_LEVEL(table_) (--RECURSION_LEVEL(table_)) - -#else - -#define ENTRY_STORE_EXTRA 0 -#define INCREMENT_RECURSION_LEVEL(table_) ((void)1) -#define DECREMENT_RECURSION_LEVEL(table_) ((void)0) - -#endif /* defined(DEBUG) */ - -JS_PUBLIC_API(void *) -JS_DHashAllocTable(JSDHashTable *table, uint32 nbytes) -{ - return malloc(nbytes); -} - -JS_PUBLIC_API(void) -JS_DHashFreeTable(JSDHashTable *table, void *ptr) -{ - free(ptr); -} - -JS_PUBLIC_API(JSDHashNumber) -JS_DHashStringKey(JSDHashTable *table, const void *key) -{ - JSDHashNumber h; - const unsigned char *s; - - h = 0; - for (s = key; *s != '\0'; s++) - h = (h >> (JS_DHASH_BITS - 4)) ^ (h << 4) ^ *s; - return h; -} - -JS_PUBLIC_API(const void *) -JS_DHashGetKeyStub(JSDHashTable *table, JSDHashEntryHdr *entry) -{ - JSDHashEntryStub *stub = (JSDHashEntryStub *)entry; - - return stub->key; -} - -JS_PUBLIC_API(JSDHashNumber) -JS_DHashVoidPtrKeyStub(JSDHashTable *table, const void *key) -{ - return (JSDHashNumber)(unsigned long)key >> 2; -} - -JS_PUBLIC_API(JSBool) -JS_DHashMatchEntryStub(JSDHashTable *table, - const JSDHashEntryHdr *entry, - const void *key) -{ - const JSDHashEntryStub *stub = (const JSDHashEntryStub *)entry; - - return stub->key == key; -} - -JS_PUBLIC_API(JSBool) -JS_DHashMatchStringKey(JSDHashTable *table, - const JSDHashEntryHdr *entry, - const void *key) -{ - const JSDHashEntryStub *stub = (const JSDHashEntryStub *)entry; - - /* XXX tolerate null keys on account of sloppy Mozilla callers. */ - return stub->key == key || - (stub->key && key && strcmp(stub->key, key) == 0); -} - -JS_PUBLIC_API(void) -JS_DHashMoveEntryStub(JSDHashTable *table, - const JSDHashEntryHdr *from, - JSDHashEntryHdr *to) -{ - memcpy(to, from, table->entrySize); -} - -JS_PUBLIC_API(void) -JS_DHashClearEntryStub(JSDHashTable *table, JSDHashEntryHdr *entry) -{ - memset(entry, 0, table->entrySize); -} - -JS_PUBLIC_API(void) -JS_DHashFreeStringKey(JSDHashTable *table, JSDHashEntryHdr *entry) -{ - const JSDHashEntryStub *stub = (const JSDHashEntryStub *)entry; - - free((void *) stub->key); - memset(entry, 0, table->entrySize); -} - -JS_PUBLIC_API(void) -JS_DHashFinalizeStub(JSDHashTable *table) -{ -} - -static const JSDHashTableOps stub_ops = { - JS_DHashAllocTable, - JS_DHashFreeTable, - JS_DHashGetKeyStub, - JS_DHashVoidPtrKeyStub, - JS_DHashMatchEntryStub, - JS_DHashMoveEntryStub, - JS_DHashClearEntryStub, - JS_DHashFinalizeStub, - NULL -}; - -JS_PUBLIC_API(const JSDHashTableOps *) -JS_DHashGetStubOps(void) -{ - return &stub_ops; -} - -JS_PUBLIC_API(JSDHashTable *) -JS_NewDHashTable(const JSDHashTableOps *ops, void *data, uint32 entrySize, - uint32 capacity) -{ - JSDHashTable *table; - - table = (JSDHashTable *) malloc(sizeof *table); - if (!table) - return NULL; - if (!JS_DHashTableInit(table, ops, data, entrySize, capacity)) { - free(table); - return NULL; - } - return table; -} - -JS_PUBLIC_API(void) -JS_DHashTableDestroy(JSDHashTable *table) -{ - JS_DHashTableFinish(table); - free(table); -} - -JS_PUBLIC_API(JSBool) -JS_DHashTableInit(JSDHashTable *table, const JSDHashTableOps *ops, void *data, - uint32 entrySize, uint32 capacity) -{ - int log2; - uint32 nbytes; - -#ifdef DEBUG - if (entrySize > 10 * sizeof(void *)) { - fprintf(stderr, - "jsdhash: for the table at address %p, the given entrySize" - " of %lu %s favors chaining over double hashing.\n", - (void *)table, - (unsigned long) entrySize, - (entrySize > 16 * sizeof(void*)) ? "definitely" : "probably"); - } -#endif - - table->ops = ops; - table->data = data; - if (capacity < JS_DHASH_MIN_SIZE) - capacity = JS_DHASH_MIN_SIZE; - - JS_CEILING_LOG2(log2, capacity); - - capacity = JS_BIT(log2); - if (capacity >= JS_DHASH_SIZE_LIMIT) - return JS_FALSE; - table->hashShift = JS_DHASH_BITS - log2; - table->maxAlphaFrac = 0xC0; /* .75 */ - table->minAlphaFrac = 0x40; /* .25 */ - table->entrySize = entrySize; - table->entryCount = table->removedCount = 0; - table->generation = 0; - nbytes = capacity * entrySize; - - table->entryStore = ops->allocTable(table, nbytes + ENTRY_STORE_EXTRA); - if (!table->entryStore) - return JS_FALSE; - memset(table->entryStore, 0, nbytes); - METER(memset(&table->stats, 0, sizeof table->stats)); - -#ifdef DEBUG - RECURSION_LEVEL(table) = 0; -#endif - - return JS_TRUE; -} - -/* - * Compute max and min load numbers (entry counts) from table params. - */ -#define MAX_LOAD(table, size) (((table)->maxAlphaFrac * (size)) >> 8) -#define MIN_LOAD(table, size) (((table)->minAlphaFrac * (size)) >> 8) - -JS_PUBLIC_API(void) -JS_DHashTableSetAlphaBounds(JSDHashTable *table, - float maxAlpha, - float minAlpha) -{ - uint32 size; - - /* - * Reject obviously insane bounds, rather than trying to guess what the - * buggy caller intended. - */ - JS_ASSERT(0.5 <= maxAlpha && maxAlpha < 1 && 0 <= minAlpha); - if (maxAlpha < 0.5 || 1 <= maxAlpha || minAlpha < 0) - return; - - /* - * Ensure that at least one entry will always be free. If maxAlpha at - * minimum size leaves no entries free, reduce maxAlpha based on minimum - * size and the precision limit of maxAlphaFrac's fixed point format. - */ - JS_ASSERT(JS_DHASH_MIN_SIZE - (maxAlpha * JS_DHASH_MIN_SIZE) >= 1); - if (JS_DHASH_MIN_SIZE - (maxAlpha * JS_DHASH_MIN_SIZE) < 1) { - maxAlpha = (float) - (JS_DHASH_MIN_SIZE - JS_MAX(JS_DHASH_MIN_SIZE / 256, 1)) - / JS_DHASH_MIN_SIZE; - } - - /* - * Ensure that minAlpha is strictly less than half maxAlpha. Take care - * not to truncate an entry's worth of alpha when storing in minAlphaFrac - * (8-bit fixed point format). - */ - JS_ASSERT(minAlpha < maxAlpha / 2); - if (minAlpha >= maxAlpha / 2) { - size = JS_DHASH_TABLE_SIZE(table); - minAlpha = (size * maxAlpha - JS_MAX(size / 256, 1)) / (2 * size); - } - - table->maxAlphaFrac = (uint8)(maxAlpha * 256); - table->minAlphaFrac = (uint8)(minAlpha * 256); -} - -/* - * Double hashing needs the second hash code to be relatively prime to table - * size, so we simply make hash2 odd. - */ -#define HASH1(hash0, shift) ((hash0) >> (shift)) -#define HASH2(hash0,log2,shift) ((((hash0) << (log2)) >> (shift)) | 1) - -/* - * Reserve keyHash 0 for free entries and 1 for removed-entry sentinels. Note - * that a removed-entry sentinel need be stored only if the removed entry had - * a colliding entry added after it. Therefore we can use 1 as the collision - * flag in addition to the removed-entry sentinel value. Multiplicative hash - * uses the high order bits of keyHash, so this least-significant reservation - * should not hurt the hash function's effectiveness much. - * - * If you change any of these magic numbers, also update JS_DHASH_ENTRY_IS_LIVE - * in jsdhash.h. It used to be private to jsdhash.c, but then became public to - * assist iterator writers who inspect table->entryStore directly. - */ -#define COLLISION_FLAG ((JSDHashNumber) 1) -#define MARK_ENTRY_FREE(entry) ((entry)->keyHash = 0) -#define MARK_ENTRY_REMOVED(entry) ((entry)->keyHash = 1) -#define ENTRY_IS_REMOVED(entry) ((entry)->keyHash == 1) -#define ENTRY_IS_LIVE(entry) JS_DHASH_ENTRY_IS_LIVE(entry) -#define ENSURE_LIVE_KEYHASH(hash0) if (hash0 < 2) hash0 -= 2; else (void)0 - -/* Match an entry's keyHash against an unstored one computed from a key. */ -#define MATCH_ENTRY_KEYHASH(entry,hash0) \ - (((entry)->keyHash & ~COLLISION_FLAG) == (hash0)) - -/* Compute the address of the indexed entry in table. */ -#define ADDRESS_ENTRY(table, index) \ - ((JSDHashEntryHdr *)((table)->entryStore + (index) * (table)->entrySize)) - -JS_PUBLIC_API(void) -JS_DHashTableFinish(JSDHashTable *table) -{ - char *entryAddr, *entryLimit; - uint32 entrySize; - JSDHashEntryHdr *entry; - -#ifdef DEBUG_XXXbrendan - static FILE *dumpfp = NULL; - if (!dumpfp) dumpfp = fopen("/tmp/jsdhash.bigdump", "w"); - if (dumpfp) { -#ifdef MOZILLA_CLIENT - NS_TraceStack(1, dumpfp); -#endif - JS_DHashTableDumpMeter(table, NULL, dumpfp); - fputc('\n', dumpfp); - } -#endif - - INCREMENT_RECURSION_LEVEL(table); - - /* Call finalize before clearing entries, so it can enumerate them. */ - table->ops->finalize(table); - - /* Clear any remaining live entries. */ - entryAddr = table->entryStore; - entrySize = table->entrySize; - entryLimit = entryAddr + JS_DHASH_TABLE_SIZE(table) * entrySize; - while (entryAddr < entryLimit) { - entry = (JSDHashEntryHdr *)entryAddr; - if (ENTRY_IS_LIVE(entry)) { - METER(table->stats.removeEnums++); - table->ops->clearEntry(table, entry); - } - entryAddr += entrySize; - } - - DECREMENT_RECURSION_LEVEL(table); - JS_ASSERT(RECURSION_LEVEL(table) == 0); - - /* Free entry storage last. */ - table->ops->freeTable(table, table->entryStore); -} - -static JSDHashEntryHdr * JS_DHASH_FASTCALL -SearchTable(JSDHashTable *table, const void *key, JSDHashNumber keyHash, - JSDHashOperator op) -{ - JSDHashNumber hash1, hash2; - int hashShift, sizeLog2; - JSDHashEntryHdr *entry, *firstRemoved; - JSDHashMatchEntry matchEntry; - uint32 sizeMask; - - METER(table->stats.searches++); - JS_ASSERT(!(keyHash & COLLISION_FLAG)); - - /* Compute the primary hash address. */ - hashShift = table->hashShift; - hash1 = HASH1(keyHash, hashShift); - entry = ADDRESS_ENTRY(table, hash1); - - /* Miss: return space for a new entry. */ - if (JS_DHASH_ENTRY_IS_FREE(entry)) { - METER(table->stats.misses++); - return entry; - } - - /* Hit: return entry. */ - matchEntry = table->ops->matchEntry; - if (MATCH_ENTRY_KEYHASH(entry, keyHash) && matchEntry(table, entry, key)) { - METER(table->stats.hits++); - return entry; - } - - /* Collision: double hash. */ - sizeLog2 = JS_DHASH_BITS - table->hashShift; - hash2 = HASH2(keyHash, sizeLog2, hashShift); - sizeMask = JS_BITMASK(sizeLog2); - - /* Save the first removed entry pointer so JS_DHASH_ADD can recycle it. */ - if (ENTRY_IS_REMOVED(entry)) { - firstRemoved = entry; - } else { - firstRemoved = NULL; - if (op == JS_DHASH_ADD) - entry->keyHash |= COLLISION_FLAG; - } - - for (;;) { - METER(table->stats.steps++); - hash1 -= hash2; - hash1 &= sizeMask; - - entry = ADDRESS_ENTRY(table, hash1); - if (JS_DHASH_ENTRY_IS_FREE(entry)) { - METER(table->stats.misses++); - return (firstRemoved && op == JS_DHASH_ADD) ? firstRemoved : entry; - } - - if (MATCH_ENTRY_KEYHASH(entry, keyHash) && - matchEntry(table, entry, key)) { - METER(table->stats.hits++); - return entry; - } - - if (ENTRY_IS_REMOVED(entry)) { - if (!firstRemoved) - firstRemoved = entry; - } else { - if (op == JS_DHASH_ADD) - entry->keyHash |= COLLISION_FLAG; - } - } - - /* NOTREACHED */ - return NULL; -} - -static JSBool -ChangeTable(JSDHashTable *table, int deltaLog2) -{ - int oldLog2, newLog2; - uint32 oldCapacity, newCapacity; - char *newEntryStore, *oldEntryStore, *oldEntryAddr; - uint32 entrySize, i, nbytes; - JSDHashEntryHdr *oldEntry, *newEntry; - JSDHashGetKey getKey; - JSDHashMoveEntry moveEntry; -#ifdef DEBUG - uint32 recursionLevel; -#endif - - /* Look, but don't touch, until we succeed in getting new entry store. */ - oldLog2 = JS_DHASH_BITS - table->hashShift; - newLog2 = oldLog2 + deltaLog2; - oldCapacity = JS_BIT(oldLog2); - newCapacity = JS_BIT(newLog2); - if (newCapacity >= JS_DHASH_SIZE_LIMIT) - return JS_FALSE; - entrySize = table->entrySize; - nbytes = newCapacity * entrySize; - - newEntryStore = table->ops->allocTable(table, nbytes + ENTRY_STORE_EXTRA); - if (!newEntryStore) - return JS_FALSE; - - /* We can't fail from here on, so update table parameters. */ -#ifdef DEBUG - recursionLevel = RECURSION_LEVEL(table); -#endif - table->hashShift = JS_DHASH_BITS - newLog2; - table->removedCount = 0; - table->generation++; - - /* Assign the new entry store to table. */ - memset(newEntryStore, 0, nbytes); - oldEntryAddr = oldEntryStore = table->entryStore; - table->entryStore = newEntryStore; - getKey = table->ops->getKey; - moveEntry = table->ops->moveEntry; -#ifdef DEBUG - RECURSION_LEVEL(table) = recursionLevel; -#endif - - /* Copy only live entries, leaving removed ones behind. */ - for (i = 0; i < oldCapacity; i++) { - oldEntry = (JSDHashEntryHdr *)oldEntryAddr; - if (ENTRY_IS_LIVE(oldEntry)) { - oldEntry->keyHash &= ~COLLISION_FLAG; - newEntry = SearchTable(table, getKey(table, oldEntry), - oldEntry->keyHash, JS_DHASH_ADD); - JS_ASSERT(JS_DHASH_ENTRY_IS_FREE(newEntry)); - moveEntry(table, oldEntry, newEntry); - newEntry->keyHash = oldEntry->keyHash; - } - oldEntryAddr += entrySize; - } - - table->ops->freeTable(table, oldEntryStore); - return JS_TRUE; -} - -JS_PUBLIC_API(JSDHashEntryHdr *) JS_DHASH_FASTCALL -JS_DHashTableOperate(JSDHashTable *table, const void *key, JSDHashOperator op) -{ - JSDHashNumber keyHash; - JSDHashEntryHdr *entry; - uint32 size; - int deltaLog2; - - JS_ASSERT(op == JS_DHASH_LOOKUP || RECURSION_LEVEL(table) == 0); - INCREMENT_RECURSION_LEVEL(table); - - keyHash = table->ops->hashKey(table, key); - keyHash *= JS_DHASH_GOLDEN_RATIO; - - /* Avoid 0 and 1 hash codes, they indicate free and removed entries. */ - ENSURE_LIVE_KEYHASH(keyHash); - keyHash &= ~COLLISION_FLAG; - - switch (op) { - case JS_DHASH_LOOKUP: - METER(table->stats.lookups++); - entry = SearchTable(table, key, keyHash, op); - break; - - case JS_DHASH_ADD: - /* - * If alpha is >= .75, grow or compress the table. If key is already - * in the table, we may grow once more than necessary, but only if we - * are on the edge of being overloaded. - */ - size = JS_DHASH_TABLE_SIZE(table); - if (table->entryCount + table->removedCount >= MAX_LOAD(table, size)) { - /* Compress if a quarter or more of all entries are removed. */ - if (table->removedCount >= size >> 2) { - METER(table->stats.compresses++); - deltaLog2 = 0; - } else { - METER(table->stats.grows++); - deltaLog2 = 1; - } - - /* - * Grow or compress table, returning null if ChangeTable fails and - * falling through might claim the last free entry. - */ - if (!ChangeTable(table, deltaLog2) && - table->entryCount + table->removedCount == size - 1) { - METER(table->stats.addFailures++); - entry = NULL; - break; - } - } - - /* - * Look for entry after possibly growing, so we don't have to add it, - * then skip it while growing the table and re-add it after. - */ - entry = SearchTable(table, key, keyHash, op); - if (!ENTRY_IS_LIVE(entry)) { - /* Initialize the entry, indicating that it's no longer free. */ - METER(table->stats.addMisses++); - if (ENTRY_IS_REMOVED(entry)) { - METER(table->stats.addOverRemoved++); - table->removedCount--; - keyHash |= COLLISION_FLAG; - } - if (table->ops->initEntry && - !table->ops->initEntry(table, entry, key)) { - /* We haven't claimed entry yet; fail with null return. */ - memset(entry + 1, 0, table->entrySize - sizeof *entry); - entry = NULL; - break; - } - entry->keyHash = keyHash; - table->entryCount++; - } - METER(else table->stats.addHits++); - break; - - case JS_DHASH_REMOVE: - entry = SearchTable(table, key, keyHash, op); - if (ENTRY_IS_LIVE(entry)) { - /* Clear this entry and mark it as "removed". */ - METER(table->stats.removeHits++); - JS_DHashTableRawRemove(table, entry); - - /* Shrink if alpha is <= .25 and table isn't too small already. */ - size = JS_DHASH_TABLE_SIZE(table); - if (size > JS_DHASH_MIN_SIZE && - table->entryCount <= MIN_LOAD(table, size)) { - METER(table->stats.shrinks++); - (void) ChangeTable(table, -1); - } - } - METER(else table->stats.removeMisses++); - entry = NULL; - break; - - default: - JS_ASSERT(0); - entry = NULL; - } - - DECREMENT_RECURSION_LEVEL(table); - - return entry; -} - -JS_PUBLIC_API(void) -JS_DHashTableRawRemove(JSDHashTable *table, JSDHashEntryHdr *entry) -{ - JSDHashNumber keyHash; /* load first in case clearEntry goofs it */ - - JS_ASSERT(JS_DHASH_ENTRY_IS_LIVE(entry)); - keyHash = entry->keyHash; - table->ops->clearEntry(table, entry); - if (keyHash & COLLISION_FLAG) { - MARK_ENTRY_REMOVED(entry); - table->removedCount++; - } else { - METER(table->stats.removeFrees++); - MARK_ENTRY_FREE(entry); - } - table->entryCount--; -} - -JS_PUBLIC_API(uint32) -JS_DHashTableEnumerate(JSDHashTable *table, JSDHashEnumerator etor, void *arg) -{ - char *entryAddr, *entryLimit; - uint32 i, capacity, entrySize, ceiling; - JSBool didRemove; - JSDHashEntryHdr *entry; - JSDHashOperator op; - - INCREMENT_RECURSION_LEVEL(table); - - entryAddr = table->entryStore; - entrySize = table->entrySize; - capacity = JS_DHASH_TABLE_SIZE(table); - entryLimit = entryAddr + capacity * entrySize; - i = 0; - didRemove = JS_FALSE; - while (entryAddr < entryLimit) { - entry = (JSDHashEntryHdr *)entryAddr; - if (ENTRY_IS_LIVE(entry)) { - op = etor(table, entry, i++, arg); - if (op & JS_DHASH_REMOVE) { - METER(table->stats.removeEnums++); - JS_DHashTableRawRemove(table, entry); - didRemove = JS_TRUE; - } - if (op & JS_DHASH_STOP) - break; - } - entryAddr += entrySize; - } - - JS_ASSERT(!didRemove || RECURSION_LEVEL(table) == 1); - - /* - * Shrink or compress if a quarter or more of all entries are removed, or - * if the table is underloaded according to the configured minimum alpha, - * and is not minimal-size already. Do this only if we removed above, so - * non-removing enumerations can count on stable table->entryStore until - * the next non-lookup-Operate or removing-Enumerate. - */ - if (didRemove && - (table->removedCount >= capacity >> 2 || - (capacity > JS_DHASH_MIN_SIZE && - table->entryCount <= MIN_LOAD(table, capacity)))) { - METER(table->stats.enumShrinks++); - capacity = table->entryCount; - capacity += capacity >> 1; - if (capacity < JS_DHASH_MIN_SIZE) - capacity = JS_DHASH_MIN_SIZE; - - JS_CEILING_LOG2(ceiling, capacity); - ceiling -= JS_DHASH_BITS - table->hashShift; - - (void) ChangeTable(table, ceiling); - } - - DECREMENT_RECURSION_LEVEL(table); - - return i; -} - -#ifdef JS_DHASHMETER -#include - -JS_PUBLIC_API(void) -JS_DHashTableDumpMeter(JSDHashTable *table, JSDHashEnumerator dump, FILE *fp) -{ - char *entryAddr; - uint32 entrySize, entryCount; - int hashShift, sizeLog2; - uint32 i, tableSize, sizeMask, chainLen, maxChainLen, chainCount; - JSDHashNumber hash1, hash2, saveHash1, maxChainHash1, maxChainHash2; - double sqsum, mean, variance, sigma; - JSDHashEntryHdr *entry, *probe; - - entryAddr = table->entryStore; - entrySize = table->entrySize; - hashShift = table->hashShift; - sizeLog2 = JS_DHASH_BITS - hashShift; - tableSize = JS_DHASH_TABLE_SIZE(table); - sizeMask = JS_BITMASK(sizeLog2); - chainCount = maxChainLen = 0; - hash2 = 0; - sqsum = 0; - - for (i = 0; i < tableSize; i++) { - entry = (JSDHashEntryHdr *)entryAddr; - entryAddr += entrySize; - if (!ENTRY_IS_LIVE(entry)) - continue; - hash1 = HASH1(entry->keyHash & ~COLLISION_FLAG, hashShift); - saveHash1 = hash1; - probe = ADDRESS_ENTRY(table, hash1); - chainLen = 1; - if (probe == entry) { - /* Start of a (possibly unit-length) chain. */ - chainCount++; - } else { - hash2 = HASH2(entry->keyHash & ~COLLISION_FLAG, sizeLog2, - hashShift); - do { - chainLen++; - hash1 -= hash2; - hash1 &= sizeMask; - probe = ADDRESS_ENTRY(table, hash1); - } while (probe != entry); - } - sqsum += chainLen * chainLen; - if (chainLen > maxChainLen) { - maxChainLen = chainLen; - maxChainHash1 = saveHash1; - maxChainHash2 = hash2; - } - } - - entryCount = table->entryCount; - if (entryCount && chainCount) { - mean = (double)entryCount / chainCount; - variance = chainCount * sqsum - entryCount * entryCount; - if (variance < 0 || chainCount == 1) - variance = 0; - else - variance /= chainCount * (chainCount - 1); - sigma = sqrt(variance); - } else { - mean = sigma = 0; - } - - fprintf(fp, "Double hashing statistics:\n"); - fprintf(fp, " table size (in entries): %u\n", tableSize); - fprintf(fp, " number of entries: %u\n", table->entryCount); - fprintf(fp, " number of removed entries: %u\n", table->removedCount); - fprintf(fp, " number of searches: %u\n", table->stats.searches); - fprintf(fp, " number of hits: %u\n", table->stats.hits); - fprintf(fp, " number of misses: %u\n", table->stats.misses); - fprintf(fp, " mean steps per search: %g\n", table->stats.searches ? - (double)table->stats.steps - / table->stats.searches : - 0.); - fprintf(fp, " mean hash chain length: %g\n", mean); - fprintf(fp, " standard deviation: %g\n", sigma); - fprintf(fp, " maximum hash chain length: %u\n", maxChainLen); - fprintf(fp, " number of lookups: %u\n", table->stats.lookups); - fprintf(fp, " adds that made a new entry: %u\n", table->stats.addMisses); - fprintf(fp, "adds that recycled removeds: %u\n", table->stats.addOverRemoved); - fprintf(fp, " adds that found an entry: %u\n", table->stats.addHits); - fprintf(fp, " add failures: %u\n", table->stats.addFailures); - fprintf(fp, " useful removes: %u\n", table->stats.removeHits); - fprintf(fp, " useless removes: %u\n", table->stats.removeMisses); - fprintf(fp, "removes that freed an entry: %u\n", table->stats.removeFrees); - fprintf(fp, " removes while enumerating: %u\n", table->stats.removeEnums); - fprintf(fp, " number of grows: %u\n", table->stats.grows); - fprintf(fp, " number of shrinks: %u\n", table->stats.shrinks); - fprintf(fp, " number of compresses: %u\n", table->stats.compresses); - fprintf(fp, "number of enumerate shrinks: %u\n", table->stats.enumShrinks); - - if (dump && maxChainLen && hash2) { - fputs("Maximum hash chain:\n", fp); - hash1 = maxChainHash1; - hash2 = maxChainHash2; - entry = ADDRESS_ENTRY(table, hash1); - i = 0; - do { - if (dump(table, entry, i++, fp) != JS_DHASH_NEXT) - break; - hash1 -= hash2; - hash1 &= sizeMask; - entry = ADDRESS_ENTRY(table, hash1); - } while (JS_DHASH_ENTRY_IS_BUSY(entry)); - } -} -#endif /* JS_DHASHMETER */ diff --git a/spidermonkey/src/jsdhash.h b/spidermonkey/src/jsdhash.h deleted file mode 100644 index 76867e5..0000000 --- a/spidermonkey/src/jsdhash.h +++ /dev/null @@ -1,581 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 JavaScript code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1999-2001 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Brendan Eich (Original Author) - * - * Alternatively, the contents of this file may be used under the terms of - * either of 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 ***** */ - -#ifndef jsdhash_h___ -#define jsdhash_h___ -/* - * Double hashing, a la Knuth 6. - */ -#include "jstypes.h" - -JS_BEGIN_EXTERN_C - -#if defined(__GNUC__) && defined(__i386__) && (__GNUC__ >= 3) && !defined(XP_OS2) -#define JS_DHASH_FASTCALL __attribute__ ((regparm (3),stdcall)) -#elif defined(XP_WIN) -#define JS_DHASH_FASTCALL __fastcall -#else -#define JS_DHASH_FASTCALL -#endif - -#ifdef DEBUG_XXXbrendan -#define JS_DHASHMETER 1 -#endif - -/* Table size limit, do not equal or exceed (see min&maxAlphaFrac, below). */ -#undef JS_DHASH_SIZE_LIMIT -#define JS_DHASH_SIZE_LIMIT JS_BIT(24) - -/* Minimum table size, or gross entry count (net is at most .75 loaded). */ -#ifndef JS_DHASH_MIN_SIZE -#define JS_DHASH_MIN_SIZE 16 -#elif (JS_DHASH_MIN_SIZE & (JS_DHASH_MIN_SIZE - 1)) != 0 -#error "JS_DHASH_MIN_SIZE must be a power of two!" -#endif - -/* - * Multiplicative hash uses an unsigned 32 bit integer and the golden ratio, - * expressed as a fixed-point 32-bit fraction. - */ -#define JS_DHASH_BITS 32 -#define JS_DHASH_GOLDEN_RATIO 0x9E3779B9U - -/* Primitive and forward-struct typedefs. */ -typedef uint32 JSDHashNumber; -typedef struct JSDHashEntryHdr JSDHashEntryHdr; -typedef struct JSDHashEntryStub JSDHashEntryStub; -typedef struct JSDHashTable JSDHashTable; -typedef struct JSDHashTableOps JSDHashTableOps; - -/* - * Table entry header structure. - * - * In order to allow in-line allocation of key and value, we do not declare - * either here. Instead, the API uses const void *key as a formal parameter, - * and asks each entry for its key when necessary via a getKey callback, used - * when growing or shrinking the table. Other callback types are defined - * below and grouped into the JSDHashTableOps structure, for single static - * initialization per hash table sub-type. - * - * Each hash table sub-type should nest the JSDHashEntryHdr structure at the - * front of its particular entry type. The keyHash member contains the result - * of multiplying the hash code returned from the hashKey callback (see below) - * by JS_DHASH_GOLDEN_RATIO, then constraining the result to avoid the magic 0 - * and 1 values. The stored keyHash value is table size invariant, and it is - * maintained automatically by JS_DHashTableOperate -- users should never set - * it, and its only uses should be via the entry macros below. - * - * The JS_DHASH_ENTRY_IS_LIVE macro tests whether entry is neither free nor - * removed. An entry may be either busy or free; if busy, it may be live or - * removed. Consumers of this API should not access members of entries that - * are not live. - * - * However, use JS_DHASH_ENTRY_IS_BUSY for faster liveness testing of entries - * returned by JS_DHashTableOperate, as JS_DHashTableOperate never returns a - * non-live, busy (i.e., removed) entry pointer to its caller. See below for - * more details on JS_DHashTableOperate's calling rules. - */ -struct JSDHashEntryHdr { - JSDHashNumber keyHash; /* every entry must begin like this */ -}; - -#define JS_DHASH_ENTRY_IS_FREE(entry) ((entry)->keyHash == 0) -#define JS_DHASH_ENTRY_IS_BUSY(entry) (!JS_DHASH_ENTRY_IS_FREE(entry)) -#define JS_DHASH_ENTRY_IS_LIVE(entry) ((entry)->keyHash >= 2) - -/* - * A JSDHashTable is currently 8 words (without the JS_DHASHMETER overhead) - * on most architectures, and may be allocated on the stack or within another - * structure or class (see below for the Init and Finish functions to use). - * - * To decide whether to use double hashing vs. chaining, we need to develop a - * trade-off relation, as follows: - * - * Let alpha be the load factor, esize the entry size in words, count the - * entry count, and pow2 the power-of-two table size in entries. - * - * (JSDHashTable overhead) > (JSHashTable overhead) - * (unused table entry space) > (malloc and .next overhead per entry) + - * (buckets overhead) - * (1 - alpha) * esize * pow2 > 2 * count + pow2 - * - * Notice that alpha is by definition (count / pow2): - * - * (1 - alpha) * esize * pow2 > 2 * alpha * pow2 + pow2 - * (1 - alpha) * esize > 2 * alpha + 1 - * - * esize > (1 + 2 * alpha) / (1 - alpha) - * - * This assumes both tables must keep keyHash, key, and value for each entry, - * where key and value point to separately allocated strings or structures. - * If key and value can be combined into one pointer, then the trade-off is: - * - * esize > (1 + 3 * alpha) / (1 - alpha) - * - * If the entry value can be a subtype of JSDHashEntryHdr, rather than a type - * that must be allocated separately and referenced by an entry.value pointer - * member, and provided key's allocation can be fused with its entry's, then - * k (the words wasted per entry with chaining) is 4. - * - * To see these curves, feed gnuplot input like so: - * - * gnuplot> f(x,k) = (1 + k * x) / (1 - x) - * gnuplot> plot [0:.75] f(x,2), f(x,3), f(x,4) - * - * For k of 2 and a well-loaded table (alpha > .5), esize must be more than 4 - * words for chaining to be more space-efficient than double hashing. - * - * Solving for alpha helps us decide when to shrink an underloaded table: - * - * esize > (1 + k * alpha) / (1 - alpha) - * esize - alpha * esize > 1 + k * alpha - * esize - 1 > (k + esize) * alpha - * (esize - 1) / (k + esize) > alpha - * - * alpha < (esize - 1) / (esize + k) - * - * Therefore double hashing should keep alpha >= (esize - 1) / (esize + k), - * assuming esize is not too large (in which case, chaining should probably be - * used for any alpha). For esize=2 and k=3, we want alpha >= .2; for esize=3 - * and k=2, we want alpha >= .4. For k=4, esize could be 6, and alpha >= .5 - * would still obtain. See the JS_DHASH_MIN_ALPHA macro further below. - * - * The current implementation uses a configurable lower bound on alpha, which - * defaults to .25, when deciding to shrink the table (while still respecting - * JS_DHASH_MIN_SIZE). - * - * Note a qualitative difference between chaining and double hashing: under - * chaining, entry addresses are stable across table shrinks and grows. With - * double hashing, you can't safely hold an entry pointer and use it after an - * ADD or REMOVE operation, unless you sample table->generation before adding - * or removing, and compare the sample after, dereferencing the entry pointer - * only if table->generation has not changed. - * - * The moral of this story: there is no one-size-fits-all hash table scheme, - * but for small table entry size, and assuming entry address stability is not - * required, double hashing wins. - */ -struct JSDHashTable { - const JSDHashTableOps *ops; /* virtual operations, see below */ - void *data; /* ops- and instance-specific data */ - int16 hashShift; /* multiplicative hash shift */ - uint8 maxAlphaFrac; /* 8-bit fixed point max alpha */ - uint8 minAlphaFrac; /* 8-bit fixed point min alpha */ - uint32 entrySize; /* number of bytes in an entry */ - uint32 entryCount; /* number of entries in table */ - uint32 removedCount; /* removed entry sentinels in table */ - uint32 generation; /* entry storage generation number */ - char *entryStore; /* entry storage */ -#ifdef JS_DHASHMETER - struct JSDHashStats { - uint32 searches; /* total number of table searches */ - uint32 steps; /* hash chain links traversed */ - uint32 hits; /* searches that found key */ - uint32 misses; /* searches that didn't find key */ - uint32 lookups; /* number of JS_DHASH_LOOKUPs */ - uint32 addMisses; /* adds that miss, and do work */ - uint32 addOverRemoved; /* adds that recycled a removed entry */ - uint32 addHits; /* adds that hit an existing entry */ - uint32 addFailures; /* out-of-memory during add growth */ - uint32 removeHits; /* removes that hit, and do work */ - uint32 removeMisses; /* useless removes that miss */ - uint32 removeFrees; /* removes that freed entry directly */ - uint32 removeEnums; /* removes done by Enumerate */ - uint32 grows; /* table expansions */ - uint32 shrinks; /* table contractions */ - uint32 compresses; /* table compressions */ - uint32 enumShrinks; /* contractions after Enumerate */ - } stats; -#endif -}; - -/* - * Size in entries (gross, not net of free and removed sentinels) for table. - * We store hashShift rather than sizeLog2 to optimize the collision-free case - * in SearchTable. - */ -#define JS_DHASH_TABLE_SIZE(table) JS_BIT(JS_DHASH_BITS - (table)->hashShift) - -/* - * Table space at entryStore is allocated and freed using these callbacks. - * The allocator should return null on error only (not if called with nbytes - * equal to 0; but note that jsdhash.c code will never call with 0 nbytes). - */ -typedef void * -(* JS_DLL_CALLBACK JSDHashAllocTable)(JSDHashTable *table, uint32 nbytes); - -typedef void -(* JS_DLL_CALLBACK JSDHashFreeTable) (JSDHashTable *table, void *ptr); - -/* - * When a table grows or shrinks, each entry is queried for its key using this - * callback. NB: in that event, entry is not in table any longer; it's in the - * old entryStore vector, which is due to be freed once all entries have been - * moved via moveEntry callbacks. - */ -typedef const void * -(* JS_DLL_CALLBACK JSDHashGetKey) (JSDHashTable *table, - JSDHashEntryHdr *entry); - -/* - * Compute the hash code for a given key to be looked up, added, or removed - * from table. A hash code may have any JSDHashNumber value. - */ -typedef JSDHashNumber -(* JS_DLL_CALLBACK JSDHashHashKey) (JSDHashTable *table, const void *key); - -/* - * Compare the key identifying entry in table with the provided key parameter. - * Return JS_TRUE if keys match, JS_FALSE otherwise. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSDHashMatchEntry)(JSDHashTable *table, - const JSDHashEntryHdr *entry, - const void *key); - -/* - * Copy the data starting at from to the new entry storage at to. Do not add - * reference counts for any strong references in the entry, however, as this - * is a "move" operation: the old entry storage at from will be freed without - * any reference-decrementing callback shortly. - */ -typedef void -(* JS_DLL_CALLBACK JSDHashMoveEntry)(JSDHashTable *table, - const JSDHashEntryHdr *from, - JSDHashEntryHdr *to); - -/* - * Clear the entry and drop any strong references it holds. This callback is - * invoked during a JS_DHASH_REMOVE operation (see below for operation codes), - * but only if the given key is found in the table. - */ -typedef void -(* JS_DLL_CALLBACK JSDHashClearEntry)(JSDHashTable *table, - JSDHashEntryHdr *entry); - -/* - * Called when a table (whether allocated dynamically by itself, or nested in - * a larger structure, or allocated on the stack) is finished. This callback - * allows table->ops-specific code to finalize table->data. - */ -typedef void -(* JS_DLL_CALLBACK JSDHashFinalize) (JSDHashTable *table); - -/* - * Initialize a new entry, apart from keyHash. This function is called when - * JS_DHashTableOperate's JS_DHASH_ADD case finds no existing entry for the - * given key, and must add a new one. At that point, entry->keyHash is not - * set yet, to avoid claiming the last free entry in a severely overloaded - * table. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSDHashInitEntry)(JSDHashTable *table, - JSDHashEntryHdr *entry, - const void *key); - -/* - * Finally, the "vtable" structure for JSDHashTable. The first eight hooks - * must be provided by implementations; they're called unconditionally by the - * generic jsdhash.c code. Hooks after these may be null. - * - * Summary of allocation-related hook usage with C++ placement new emphasis: - * allocTable Allocate raw bytes with malloc, no ctors run. - * freeTable Free raw bytes with free, no dtors run. - * initEntry Call placement new using default key-based ctor. - * Return JS_TRUE on success, JS_FALSE on error. - * moveEntry Call placement new using copy ctor, run dtor on old - * entry storage. - * clearEntry Run dtor on entry. - * finalize Stub unless table->data was initialized and needs to - * be finalized. - * - * Note the reason why initEntry is optional: the default hooks (stubs) clear - * entry storage: On successful JS_DHashTableOperate(tbl, key, JS_DHASH_ADD), - * the returned entry pointer addresses an entry struct whose keyHash member - * has been set non-zero, but all other entry members are still clear (null). - * JS_DHASH_ADD callers can test such members to see whether the entry was - * newly created by the JS_DHASH_ADD call that just succeeded. If placement - * new or similar initialization is required, define an initEntry hook. Of - * course, the clearEntry hook must zero or null appropriately. - * - * XXX assumes 0 is null for pointer types. - */ -struct JSDHashTableOps { - /* Mandatory hooks. All implementations must provide these. */ - JSDHashAllocTable allocTable; - JSDHashFreeTable freeTable; - JSDHashGetKey getKey; - JSDHashHashKey hashKey; - JSDHashMatchEntry matchEntry; - JSDHashMoveEntry moveEntry; - JSDHashClearEntry clearEntry; - JSDHashFinalize finalize; - - /* Optional hooks start here. If null, these are not called. */ - JSDHashInitEntry initEntry; -}; - -/* - * Default implementations for the above ops. - */ -extern JS_PUBLIC_API(void *) -JS_DHashAllocTable(JSDHashTable *table, uint32 nbytes); - -extern JS_PUBLIC_API(void) -JS_DHashFreeTable(JSDHashTable *table, void *ptr); - -extern JS_PUBLIC_API(JSDHashNumber) -JS_DHashStringKey(JSDHashTable *table, const void *key); - -/* A minimal entry contains a keyHash header and a void key pointer. */ -struct JSDHashEntryStub { - JSDHashEntryHdr hdr; - const void *key; -}; - -extern JS_PUBLIC_API(const void *) -JS_DHashGetKeyStub(JSDHashTable *table, JSDHashEntryHdr *entry); - -extern JS_PUBLIC_API(JSDHashNumber) -JS_DHashVoidPtrKeyStub(JSDHashTable *table, const void *key); - -extern JS_PUBLIC_API(JSBool) -JS_DHashMatchEntryStub(JSDHashTable *table, - const JSDHashEntryHdr *entry, - const void *key); - -extern JS_PUBLIC_API(JSBool) -JS_DHashMatchStringKey(JSDHashTable *table, - const JSDHashEntryHdr *entry, - const void *key); - -extern JS_PUBLIC_API(void) -JS_DHashMoveEntryStub(JSDHashTable *table, - const JSDHashEntryHdr *from, - JSDHashEntryHdr *to); - -extern JS_PUBLIC_API(void) -JS_DHashClearEntryStub(JSDHashTable *table, JSDHashEntryHdr *entry); - -extern JS_PUBLIC_API(void) -JS_DHashFreeStringKey(JSDHashTable *table, JSDHashEntryHdr *entry); - -extern JS_PUBLIC_API(void) -JS_DHashFinalizeStub(JSDHashTable *table); - -/* - * If you use JSDHashEntryStub or a subclass of it as your entry struct, and - * if your entries move via memcpy and clear via memset(0), you can use these - * stub operations. - */ -extern JS_PUBLIC_API(const JSDHashTableOps *) -JS_DHashGetStubOps(void); - -/* - * Dynamically allocate a new JSDHashTable using malloc, initialize it using - * JS_DHashTableInit, and return its address. Return null on malloc failure. - * Note that the entry storage at table->entryStore will be allocated using - * the ops->allocTable callback. - */ -extern JS_PUBLIC_API(JSDHashTable *) -JS_NewDHashTable(const JSDHashTableOps *ops, void *data, uint32 entrySize, - uint32 capacity); - -/* - * Finalize table's data, free its entry storage (via table->ops->freeTable), - * and return the memory starting at table to the malloc heap. - */ -extern JS_PUBLIC_API(void) -JS_DHashTableDestroy(JSDHashTable *table); - -/* - * Initialize table with ops, data, entrySize, and capacity. Capacity is a - * guess for the smallest table size at which the table will usually be less - * than 75% loaded (the table will grow or shrink as needed; capacity serves - * only to avoid inevitable early growth from JS_DHASH_MIN_SIZE). - */ -extern JS_PUBLIC_API(JSBool) -JS_DHashTableInit(JSDHashTable *table, const JSDHashTableOps *ops, void *data, - uint32 entrySize, uint32 capacity); - -/* - * Set maximum and minimum alpha for table. The defaults are 0.75 and .25. - * maxAlpha must be in [0.5, 0.9375] for the default JS_DHASH_MIN_SIZE; or if - * MinSize=JS_DHASH_MIN_SIZE <= 256, in [0.5, (float)(MinSize-1)/MinSize]; or - * else in [0.5, 255.0/256]. minAlpha must be in [0, maxAlpha / 2), so that - * we don't shrink on the very next remove after growing a table upon adding - * an entry that brings entryCount past maxAlpha * tableSize. - */ -extern JS_PUBLIC_API(void) -JS_DHashTableSetAlphaBounds(JSDHashTable *table, - float maxAlpha, - float minAlpha); - -/* - * Call this macro with k, the number of pointer-sized words wasted per entry - * under chaining, to compute the minimum alpha at which double hashing still - * beats chaining. - */ -#define JS_DHASH_MIN_ALPHA(table, k) \ - ((float)((table)->entrySize / sizeof(void *) - 1) \ - / ((table)->entrySize / sizeof(void *) + (k))) - -/* - * Finalize table's data, free its entry storage using table->ops->freeTable, - * and leave its members unchanged from their last live values (which leaves - * pointers dangling). If you want to burn cycles clearing table, it's up to - * your code to call memset. - */ -extern JS_PUBLIC_API(void) -JS_DHashTableFinish(JSDHashTable *table); - -/* - * To consolidate keyHash computation and table grow/shrink code, we use a - * single entry point for lookup, add, and remove operations. The operation - * codes are declared here, along with codes returned by JSDHashEnumerator - * functions, which control JS_DHashTableEnumerate's behavior. - */ -typedef enum JSDHashOperator { - JS_DHASH_LOOKUP = 0, /* lookup entry */ - JS_DHASH_ADD = 1, /* add entry */ - JS_DHASH_REMOVE = 2, /* remove entry, or enumerator says remove */ - JS_DHASH_NEXT = 0, /* enumerator says continue */ - JS_DHASH_STOP = 1 /* enumerator says stop */ -} JSDHashOperator; - -/* - * To lookup a key in table, call: - * - * entry = JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP); - * - * If JS_DHASH_ENTRY_IS_BUSY(entry) is true, key was found and it identifies - * entry. If JS_DHASH_ENTRY_IS_FREE(entry) is true, key was not found. - * - * To add an entry identified by key to table, call: - * - * entry = JS_DHashTableOperate(table, key, JS_DHASH_ADD); - * - * If entry is null upon return, then either the table is severely overloaded, - * and memory can't be allocated for entry storage via table->ops->allocTable; - * Or if table->ops->initEntry is non-null, the table->ops->initEntry op may - * have returned false. - * - * Otherwise, entry->keyHash has been set so that JS_DHASH_ENTRY_IS_BUSY(entry) - * is true, and it is up to the caller to initialize the key and value parts - * of the entry sub-type, if they have not been set already (i.e. if entry was - * not already in the table, and if the optional initEntry hook was not used). - * - * To remove an entry identified by key from table, call: - * - * (void) JS_DHashTableOperate(table, key, JS_DHASH_REMOVE); - * - * If key's entry is found, it is cleared (via table->ops->clearEntry) and - * the entry is marked so that JS_DHASH_ENTRY_IS_FREE(entry). This operation - * returns null unconditionally; you should ignore its return value. - */ -extern JS_PUBLIC_API(JSDHashEntryHdr *) JS_DHASH_FASTCALL -JS_DHashTableOperate(JSDHashTable *table, const void *key, JSDHashOperator op); - -/* - * Remove an entry already accessed via LOOKUP or ADD. - * - * NB: this is a "raw" or low-level routine, intended to be used only where - * the inefficiency of a full JS_DHashTableOperate (which rehashes in order - * to find the entry given its key) is not tolerable. This function does not - * shrink the table if it is underloaded. It does not update stats #ifdef - * JS_DHASHMETER, either. - */ -extern JS_PUBLIC_API(void) -JS_DHashTableRawRemove(JSDHashTable *table, JSDHashEntryHdr *entry); - -/* - * Enumerate entries in table using etor: - * - * count = JS_DHashTableEnumerate(table, etor, arg); - * - * JS_DHashTableEnumerate calls etor like so: - * - * op = etor(table, entry, number, arg); - * - * where number is a zero-based ordinal assigned to live entries according to - * their order in table->entryStore. - * - * The return value, op, is treated as a set of flags. If op is JS_DHASH_NEXT, - * then continue enumerating. If op contains JS_DHASH_REMOVE, then clear (via - * table->ops->clearEntry) and free entry. Then we check whether op contains - * JS_DHASH_STOP; if so, stop enumerating and return the number of live entries - * that were enumerated so far. Return the total number of live entries when - * enumeration completes normally. - * - * If etor calls JS_DHashTableOperate on table with op != JS_DHASH_LOOKUP, it - * must return JS_DHASH_STOP; otherwise undefined behavior results. - * - * If any enumerator returns JS_DHASH_REMOVE, table->entryStore may be shrunk - * or compressed after enumeration, but before JS_DHashTableEnumerate returns. - * Such an enumerator therefore can't safely set aside entry pointers, but an - * enumerator that never returns JS_DHASH_REMOVE can set pointers to entries - * aside, e.g., to avoid copying live entries into an array of the entry type. - * Copying entry pointers is cheaper, and safe so long as the caller of such a - * "stable" Enumerate doesn't use the set-aside pointers after any call either - * to PL_DHashTableOperate, or to an "unstable" form of Enumerate, which might - * grow or shrink entryStore. - * - * If your enumerator wants to remove certain entries, but set aside pointers - * to other entries that it retains, it can use JS_DHashTableRawRemove on the - * entries to be removed, returning JS_DHASH_NEXT to skip them. Likewise, if - * you want to remove entries, but for some reason you do not want entryStore - * to be shrunk or compressed, you can call JS_DHashTableRawRemove safely on - * the entry being enumerated, rather than returning JS_DHASH_REMOVE. - */ -typedef JSDHashOperator -(* JS_DLL_CALLBACK JSDHashEnumerator)(JSDHashTable *table, JSDHashEntryHdr *hdr, - uint32 number, void *arg); - -extern JS_PUBLIC_API(uint32) -JS_DHashTableEnumerate(JSDHashTable *table, JSDHashEnumerator etor, void *arg); - -#ifdef JS_DHASHMETER -#include - -extern JS_PUBLIC_API(void) -JS_DHashTableDumpMeter(JSDHashTable *table, JSDHashEnumerator dump, FILE *fp); -#endif - -JS_END_EXTERN_C - -#endif /* jsdhash_h___ */ diff --git a/spidermonkey/src/jsdtoa.c b/spidermonkey/src/jsdtoa.c deleted file mode 100644 index 5b0b09f..0000000 --- a/spidermonkey/src/jsdtoa.c +++ /dev/null @@ -1,3132 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * Portable double to alphanumeric string and back converters. - */ -#include "jsstddef.h" -#include "jslibmath.h" -#include "jstypes.h" -#include "jsdtoa.h" -#include "jsprf.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jspubtd.h" -#include "jsnum.h" - -#ifdef JS_THREADSAFE -#include "prlock.h" -#endif - -/**************************************************************** - * - * The author of this software is David M. Gay. - * - * Copyright (c) 1991 by Lucent Technologies. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY - * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - * - ***************************************************************/ - -/* Please send bug reports to - David M. Gay - Bell Laboratories, Room 2C-463 - 600 Mountain Avenue - Murray Hill, NJ 07974-0636 - U.S.A. - dmg@bell-labs.com - */ - -/* On a machine with IEEE extended-precision registers, it is - * necessary to specify double-precision (53-bit) rounding precision - * before invoking strtod or dtoa. If the machine uses (the equivalent - * of) Intel 80x87 arithmetic, the call - * _control87(PC_53, MCW_PC); - * does this with many compilers. Whether this or another call is - * appropriate depends on the compiler; for this to work, it may be - * necessary to #include "float.h" or another system-dependent header - * file. - */ - -/* strtod for IEEE-arithmetic machines. - * - * This strtod returns a nearest machine number to the input decimal - * string (or sets err to JS_DTOA_ERANGE or JS_DTOA_ENOMEM). With IEEE - * arithmetic, ties are broken by the IEEE round-even rule. Otherwise - * ties are broken by biased rounding (add half and chop). - * - * Inspired loosely by William D. Clinger's paper "How to Read Floating - * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101]. - * - * Modifications: - * - * 1. We only require IEEE double-precision - * arithmetic (not IEEE double-extended). - * 2. We get by with floating-point arithmetic in a case that - * Clinger missed -- when we're computing d * 10^n - * for a small integer d and the integer n is not too - * much larger than 22 (the maximum integer k for which - * we can represent 10^k exactly), we may be able to - * compute (d*10^k) * 10^(e-k) with just one roundoff. - * 3. Rather than a bit-at-a-time adjustment of the binary - * result in the hard case, we use floating-point - * arithmetic to determine the adjustment to within - * one bit; only in really hard cases do we need to - * compute a second residual. - * 4. Because of 3., we don't need a large table of powers of 10 - * for ten-to-e (just some small tables, e.g. of 10^k - * for 0 <= k <= 22). - */ - -/* - * #define IEEE_8087 for IEEE-arithmetic machines where the least - * significant byte has the lowest address. - * #define IEEE_MC68k for IEEE-arithmetic machines where the most - * significant byte has the lowest address. - * #define Long int on machines with 32-bit ints and 64-bit longs. - * #define Sudden_Underflow for IEEE-format machines without gradual - * underflow (i.e., that flush to zero on underflow). - * #define No_leftright to omit left-right logic in fast floating-point - * computation of js_dtoa. - * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3. - * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines - * that use extended-precision instructions to compute rounded - * products and quotients) with IBM. - * #define ROUND_BIASED for IEEE-format with biased rounding. - * #define Inaccurate_Divide for IEEE-format with correctly rounded - * products but inaccurate quotients, e.g., for Intel i860. - * #define JS_HAVE_LONG_LONG on machines that have a "long long" - * integer type (of >= 64 bits). If long long is available and the name is - * something other than "long long", #define Llong to be the name, - * and if "unsigned Llong" does not work as an unsigned version of - * Llong, #define #ULLong to be the corresponding unsigned type. - * #define Bad_float_h if your system lacks a float.h or if it does not - * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP, - * FLT_RADIX, FLT_ROUNDS, and DBL_MAX. - * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n) - * if memory is available and otherwise does something you deem - * appropriate. If MALLOC is undefined, malloc will be invoked - * directly -- and assumed always to succeed. - * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making - * memory allocations from a private pool of memory when possible. - * When used, the private pool is PRIVATE_MEM bytes long: 2000 bytes, - * unless #defined to be a different length. This default length - * suffices to get rid of MALLOC calls except for unusual cases, - * such as decimal-to-binary conversion of a very long string of - * digits. - * #define INFNAN_CHECK on IEEE systems to cause strtod to check for - * Infinity and NaN (case insensitively). On some systems (e.g., - * some HP systems), it may be necessary to #define NAN_WORD0 - * appropriately -- to the most significant word of a quiet NaN. - * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.) - * #define MULTIPLE_THREADS if the system offers preemptively scheduled - * multiple threads. In this case, you must provide (or suitably - * #define) two locks, acquired by ACQUIRE_DTOA_LOCK() and released - * by RELEASE_DTOA_LOCK(). (The second lock, accessed - * in pow5mult, ensures lazy evaluation of only one copy of high - * powers of 5; omitting this lock would introduce a small - * probability of wasting memory, but would otherwise be harmless.) - * You must also invoke freedtoa(s) to free the value s returned by - * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined. - * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that - * avoids underflows on inputs whose result does not underflow. - */ -#ifdef IS_LITTLE_ENDIAN -#define IEEE_8087 -#else -#define IEEE_MC68k -#endif - -#ifndef Long -#define Long int32 -#endif - -#ifndef ULong -#define ULong uint32 -#endif - -#define Bug(errorMessageString) JS_ASSERT(!errorMessageString) - -#include "stdlib.h" -#include "string.h" - -#ifdef MALLOC -extern void *MALLOC(size_t); -#else -#define MALLOC malloc -#endif - -#define Omit_Private_Memory -/* Private memory currently doesn't work with JS_THREADSAFE */ -#ifndef Omit_Private_Memory -#ifndef PRIVATE_MEM -#define PRIVATE_MEM 2000 -#endif -#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)) -static double private_mem[PRIVATE_mem], *pmem_next = private_mem; -#endif - -#ifdef Bad_float_h -#undef __STDC__ - -#define DBL_DIG 15 -#define DBL_MAX_10_EXP 308 -#define DBL_MAX_EXP 1024 -#define FLT_RADIX 2 -#define FLT_ROUNDS 1 -#define DBL_MAX 1.7976931348623157e+308 - - - -#ifndef LONG_MAX -#define LONG_MAX 2147483647 -#endif - -#else /* ifndef Bad_float_h */ -#include "float.h" -#endif /* Bad_float_h */ - -#ifndef __MATH_H__ -#include "math.h" -#endif - -#ifndef CONST -#define CONST const -#endif - -#if defined(IEEE_8087) + defined(IEEE_MC68k) != 1 -Exactly one of IEEE_8087 or IEEE_MC68k should be defined. -#endif - -#define word0(x) JSDOUBLE_HI32(x) -#define set_word0(x, y) JSDOUBLE_SET_HI32(x, y) -#define word1(x) JSDOUBLE_LO32(x) -#define set_word1(x, y) JSDOUBLE_SET_LO32(x, y) - -#define Storeinc(a,b,c) (*(a)++ = (b) << 16 | (c) & 0xffff) - -/* #define P DBL_MANT_DIG */ -/* Ten_pmax = floor(P*log(2)/log(5)) */ -/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */ -/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */ -/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */ - -#define Exp_shift 20 -#define Exp_shift1 20 -#define Exp_msk1 0x100000 -#define Exp_msk11 0x100000 -#define Exp_mask 0x7ff00000 -#define P 53 -#define Bias 1023 -#define Emin (-1022) -#define Exp_1 0x3ff00000 -#define Exp_11 0x3ff00000 -#define Ebits 11 -#define Frac_mask 0xfffff -#define Frac_mask1 0xfffff -#define Ten_pmax 22 -#define Bletch 0x10 -#define Bndry_mask 0xfffff -#define Bndry_mask1 0xfffff -#define LSB 1 -#define Sign_bit 0x80000000 -#define Log2P 1 -#define Tiny0 0 -#define Tiny1 1 -#define Quick_max 14 -#define Int_max 14 -#define Infinite(x) (word0(x) == 0x7ff00000) /* sufficient test for here */ -#ifndef NO_IEEE_Scale -#define Avoid_Underflow -#endif - - - -#ifdef RND_PRODQUOT -#define rounded_product(a,b) a = rnd_prod(a, b) -#define rounded_quotient(a,b) a = rnd_quot(a, b) -extern double rnd_prod(double, double), rnd_quot(double, double); -#else -#define rounded_product(a,b) a *= b -#define rounded_quotient(a,b) a /= b -#endif - -#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1)) -#define Big1 0xffffffff - -#ifndef JS_HAVE_LONG_LONG -#undef ULLong -#else /* long long available */ -#ifndef Llong -#define Llong JSInt64 -#endif -#ifndef ULLong -#define ULLong JSUint64 -#endif -#endif /* JS_HAVE_LONG_LONG */ - -#ifdef JS_THREADSAFE -#define MULTIPLE_THREADS -static PRLock *freelist_lock; -#define ACQUIRE_DTOA_LOCK() \ - JS_BEGIN_MACRO \ - if (!initialized) \ - InitDtoa(); \ - PR_Lock(freelist_lock); \ - JS_END_MACRO -#define RELEASE_DTOA_LOCK() PR_Unlock(freelist_lock) -#else -#undef MULTIPLE_THREADS -#define ACQUIRE_DTOA_LOCK() /*nothing*/ -#define RELEASE_DTOA_LOCK() /*nothing*/ -#endif - -#define Kmax 15 - -struct Bigint { - struct Bigint *next; /* Free list link */ - int32 k; /* lg2(maxwds) */ - int32 maxwds; /* Number of words allocated for x */ - int32 sign; /* Zero if positive, 1 if negative. Ignored by most Bigint routines! */ - int32 wds; /* Actual number of words. If value is nonzero, the most significant word must be nonzero. */ - ULong x[1]; /* wds words of number in little endian order */ -}; - -#ifdef ENABLE_OOM_TESTING -/* Out-of-memory testing. Use a good testcase (over and over) and then use - * these routines to cause a memory failure on every possible Balloc allocation, - * to make sure that all out-of-memory paths can be followed. See bug 14044. - */ - -static int allocationNum; /* which allocation is next? */ -static int desiredFailure; /* which allocation should fail? */ - -/** - * js_BigintTestingReset - * - * Call at the beginning of a test run to set the allocation failure position. - * (Set to 0 to just have the engine count allocations without failing.) - */ -JS_PUBLIC_API(void) -js_BigintTestingReset(int newFailure) -{ - allocationNum = 0; - desiredFailure = newFailure; -} - -/** - * js_BigintTestingWhere - * - * Report the current allocation position. This is really only useful when you - * want to learn how many allocations a test run has. - */ -JS_PUBLIC_API(int) -js_BigintTestingWhere() -{ - return allocationNum; -} - - -/* - * So here's what you do: Set up a fantastic test case that exercises the - * elements of the code you wish. Set the failure point at 0 and run the test, - * then get the allocation position. This number is the number of allocations - * your test makes. Now loop from 1 to that number, setting the failure point - * at each loop count, and run the test over and over, causing failures at each - * step. Any memory failure *should* cause a Out-Of-Memory exception; if it - * doesn't, then there's still an error here. - */ -#endif - -typedef struct Bigint Bigint; - -static Bigint *freelist[Kmax+1]; - -/* - * Allocate a Bigint with 2^k words. - * This is not threadsafe. The caller must use thread locks - */ -static Bigint *Balloc(int32 k) -{ - int32 x; - Bigint *rv; -#ifndef Omit_Private_Memory - uint32 len; -#endif - -#ifdef ENABLE_OOM_TESTING - if (++allocationNum == desiredFailure) { - printf("Forced Failing Allocation number %d\n", allocationNum); - return NULL; - } -#endif - - if ((rv = freelist[k]) != NULL) - freelist[k] = rv->next; - if (rv == NULL) { - x = 1 << k; -#ifdef Omit_Private_Memory - rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong)); -#else - len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1) - /sizeof(double); - if (pmem_next - private_mem + len <= PRIVATE_mem) { - rv = (Bigint*)pmem_next; - pmem_next += len; - } - else - rv = (Bigint*)MALLOC(len*sizeof(double)); -#endif - if (!rv) - return NULL; - rv->k = k; - rv->maxwds = x; - } - rv->sign = rv->wds = 0; - return rv; -} - -static void Bfree(Bigint *v) -{ - if (v) { - v->next = freelist[v->k]; - freelist[v->k] = v; - } -} - -#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ - y->wds*sizeof(Long) + 2*sizeof(int32)) - -/* Return b*m + a. Deallocate the old b. Both a and m must be between 0 and - * 65535 inclusive. NOTE: old b is deallocated on memory failure. - */ -static Bigint *multadd(Bigint *b, int32 m, int32 a) -{ - int32 i, wds; -#ifdef ULLong - ULong *x; - ULLong carry, y; -#else - ULong carry, *x, y; - ULong xi, z; -#endif - Bigint *b1; - -#ifdef ENABLE_OOM_TESTING - if (++allocationNum == desiredFailure) { - /* Faux allocation, because I'm not getting all of the failure paths - * without it. - */ - printf("Forced Failing Allocation number %d\n", allocationNum); - Bfree(b); - return NULL; - } -#endif - - wds = b->wds; - x = b->x; - i = 0; - carry = a; - do { -#ifdef ULLong - y = *x * (ULLong)m + carry; - carry = y >> 32; - *x++ = (ULong)(y & 0xffffffffUL); -#else - xi = *x; - y = (xi & 0xffff) * m + carry; - z = (xi >> 16) * m + (y >> 16); - carry = z >> 16; - *x++ = (z << 16) + (y & 0xffff); -#endif - } - while(++i < wds); - if (carry) { - if (wds >= b->maxwds) { - b1 = Balloc(b->k+1); - if (!b1) { - Bfree(b); - return NULL; - } - Bcopy(b1, b); - Bfree(b); - b = b1; - } - b->x[wds++] = (ULong)carry; - b->wds = wds; - } - return b; -} - -static Bigint *s2b(CONST char *s, int32 nd0, int32 nd, ULong y9) -{ - Bigint *b; - int32 i, k; - Long x, y; - - x = (nd + 8) / 9; - for(k = 0, y = 1; x > y; y <<= 1, k++) ; - b = Balloc(k); - if (!b) - return NULL; - b->x[0] = y9; - b->wds = 1; - - i = 9; - if (9 < nd0) { - s += 9; - do { - b = multadd(b, 10, *s++ - '0'); - if (!b) - return NULL; - } while(++i < nd0); - s++; - } - else - s += 10; - for(; i < nd; i++) { - b = multadd(b, 10, *s++ - '0'); - if (!b) - return NULL; - } - return b; -} - - -/* Return the number (0 through 32) of most significant zero bits in x. */ -static int32 hi0bits(register ULong x) -{ - register int32 k = 0; - - if (!(x & 0xffff0000)) { - k = 16; - x <<= 16; - } - if (!(x & 0xff000000)) { - k += 8; - x <<= 8; - } - if (!(x & 0xf0000000)) { - k += 4; - x <<= 4; - } - if (!(x & 0xc0000000)) { - k += 2; - x <<= 2; - } - if (!(x & 0x80000000)) { - k++; - if (!(x & 0x40000000)) - return 32; - } - return k; -} - - -/* Return the number (0 through 32) of least significant zero bits in y. - * Also shift y to the right past these 0 through 32 zeros so that y's - * least significant bit will be set unless y was originally zero. */ -static int32 lo0bits(ULong *y) -{ - register int32 k; - register ULong x = *y; - - if (x & 7) { - if (x & 1) - return 0; - if (x & 2) { - *y = x >> 1; - return 1; - } - *y = x >> 2; - return 2; - } - k = 0; - if (!(x & 0xffff)) { - k = 16; - x >>= 16; - } - if (!(x & 0xff)) { - k += 8; - x >>= 8; - } - if (!(x & 0xf)) { - k += 4; - x >>= 4; - } - if (!(x & 0x3)) { - k += 2; - x >>= 2; - } - if (!(x & 1)) { - k++; - x >>= 1; - if (!x & 1) - return 32; - } - *y = x; - return k; -} - -/* Return a new Bigint with the given integer value, which must be nonnegative. */ -static Bigint *i2b(int32 i) -{ - Bigint *b; - - b = Balloc(1); - if (!b) - return NULL; - b->x[0] = i; - b->wds = 1; - return b; -} - -/* Return a newly allocated product of a and b. */ -static Bigint *mult(CONST Bigint *a, CONST Bigint *b) -{ - CONST Bigint *t; - Bigint *c; - int32 k, wa, wb, wc; - ULong y; - ULong *xc, *xc0, *xce; - CONST ULong *x, *xa, *xae, *xb, *xbe; -#ifdef ULLong - ULLong carry, z; -#else - ULong carry, z; - ULong z2; -#endif - - if (a->wds < b->wds) { - t = a; - a = b; - b = t; - } - k = a->k; - wa = a->wds; - wb = b->wds; - wc = wa + wb; - if (wc > a->maxwds) - k++; - c = Balloc(k); - if (!c) - return NULL; - for(xc = c->x, xce = xc + wc; xc < xce; xc++) - *xc = 0; - xa = a->x; - xae = xa + wa; - xb = b->x; - xbe = xb + wb; - xc0 = c->x; -#ifdef ULLong - for(; xb < xbe; xc0++) { - if ((y = *xb++) != 0) { - x = xa; - xc = xc0; - carry = 0; - do { - z = *x++ * (ULLong)y + *xc + carry; - carry = z >> 32; - *xc++ = (ULong)(z & 0xffffffffUL); - } - while(x < xae); - *xc = (ULong)carry; - } - } -#else - for(; xb < xbe; xb++, xc0++) { - if ((y = *xb & 0xffff) != 0) { - x = xa; - xc = xc0; - carry = 0; - do { - z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; - carry = z >> 16; - z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; - carry = z2 >> 16; - Storeinc(xc, z2, z); - } - while(x < xae); - *xc = carry; - } - if ((y = *xb >> 16) != 0) { - x = xa; - xc = xc0; - carry = 0; - z2 = *xc; - do { - z = (*x & 0xffff) * y + (*xc >> 16) + carry; - carry = z >> 16; - Storeinc(xc, z, z2); - z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; - carry = z2 >> 16; - } - while(x < xae); - *xc = z2; - } - } -#endif - for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; - c->wds = wc; - return c; -} - -/* - * 'p5s' points to a linked list of Bigints that are powers of 5. - * This list grows on demand, and it can only grow: it won't change - * in any other way. So if we read 'p5s' or the 'next' field of - * some Bigint on the list, and it is not NULL, we know it won't - * change to NULL or some other value. Only when the value of - * 'p5s' or 'next' is NULL do we need to acquire the lock and add - * a new Bigint to the list. - */ - -static Bigint *p5s; - -#ifdef JS_THREADSAFE -static PRLock *p5s_lock; -#endif - -/* Return b * 5^k. Deallocate the old b. k must be nonnegative. */ -/* NOTE: old b is deallocated on memory failure. */ -static Bigint *pow5mult(Bigint *b, int32 k) -{ - Bigint *b1, *p5, *p51; - int32 i; - static CONST int32 p05[3] = { 5, 25, 125 }; - - if ((i = k & 3) != 0) { - b = multadd(b, p05[i-1], 0); - if (!b) - return NULL; - } - - if (!(k >>= 2)) - return b; - if (!(p5 = p5s)) { -#ifdef JS_THREADSAFE - /* - * We take great care to not call i2b() and Bfree() - * while holding the lock. - */ - Bigint *wasted_effort = NULL; - p5 = i2b(625); - if (!p5) { - Bfree(b); - return NULL; - } - /* lock and check again */ - PR_Lock(p5s_lock); - if (!p5s) { - /* first time */ - p5s = p5; - p5->next = 0; - } else { - /* some other thread just beat us */ - wasted_effort = p5; - p5 = p5s; - } - PR_Unlock(p5s_lock); - if (wasted_effort) { - Bfree(wasted_effort); - } -#else - /* first time */ - p5 = p5s = i2b(625); - if (!p5) { - Bfree(b); - return NULL; - } - p5->next = 0; -#endif - } - for(;;) { - if (k & 1) { - b1 = mult(b, p5); - Bfree(b); - if (!b1) - return NULL; - b = b1; - } - if (!(k >>= 1)) - break; - if (!(p51 = p5->next)) { -#ifdef JS_THREADSAFE - Bigint *wasted_effort = NULL; - p51 = mult(p5, p5); - if (!p51) { - Bfree(b); - return NULL; - } - PR_Lock(p5s_lock); - if (!p5->next) { - p5->next = p51; - p51->next = 0; - } else { - wasted_effort = p51; - p51 = p5->next; - } - PR_Unlock(p5s_lock); - if (wasted_effort) { - Bfree(wasted_effort); - } -#else - p51 = mult(p5,p5); - if (!p51) { - Bfree(b); - return NULL; - } - p51->next = 0; - p5->next = p51; -#endif - } - p5 = p51; - } - return b; -} - -/* Return b * 2^k. Deallocate the old b. k must be nonnegative. - * NOTE: on memory failure, old b is deallocated. */ -static Bigint *lshift(Bigint *b, int32 k) -{ - int32 i, k1, n, n1; - Bigint *b1; - ULong *x, *x1, *xe, z; - - n = k >> 5; - k1 = b->k; - n1 = n + b->wds + 1; - for(i = b->maxwds; n1 > i; i <<= 1) - k1++; - b1 = Balloc(k1); - if (!b1) - goto done; - x1 = b1->x; - for(i = 0; i < n; i++) - *x1++ = 0; - x = b->x; - xe = x + b->wds; - if (k &= 0x1f) { - k1 = 32 - k; - z = 0; - do { - *x1++ = *x << k | z; - z = *x++ >> k1; - } - while(x < xe); - if ((*x1 = z) != 0) - ++n1; - } - else do - *x1++ = *x++; - while(x < xe); - b1->wds = n1 - 1; -done: - Bfree(b); - return b1; -} - -/* Return -1, 0, or 1 depending on whether ab, respectively. */ -static int32 cmp(Bigint *a, Bigint *b) -{ - ULong *xa, *xa0, *xb, *xb0; - int32 i, j; - - i = a->wds; - j = b->wds; -#ifdef DEBUG - if (i > 1 && !a->x[i-1]) - Bug("cmp called with a->x[a->wds-1] == 0"); - if (j > 1 && !b->x[j-1]) - Bug("cmp called with b->x[b->wds-1] == 0"); -#endif - if (i -= j) - return i; - xa0 = a->x; - xa = xa0 + j; - xb0 = b->x; - xb = xb0 + j; - for(;;) { - if (*--xa != *--xb) - return *xa < *xb ? -1 : 1; - if (xa <= xa0) - break; - } - return 0; -} - -static Bigint *diff(Bigint *a, Bigint *b) -{ - Bigint *c; - int32 i, wa, wb; - ULong *xa, *xae, *xb, *xbe, *xc; -#ifdef ULLong - ULLong borrow, y; -#else - ULong borrow, y; - ULong z; -#endif - - i = cmp(a,b); - if (!i) { - c = Balloc(0); - if (!c) - return NULL; - c->wds = 1; - c->x[0] = 0; - return c; - } - if (i < 0) { - c = a; - a = b; - b = c; - i = 1; - } - else - i = 0; - c = Balloc(a->k); - if (!c) - return NULL; - c->sign = i; - wa = a->wds; - xa = a->x; - xae = xa + wa; - wb = b->wds; - xb = b->x; - xbe = xb + wb; - xc = c->x; - borrow = 0; -#ifdef ULLong - do { - y = (ULLong)*xa++ - *xb++ - borrow; - borrow = y >> 32 & 1UL; - *xc++ = (ULong)(y & 0xffffffffUL); - } - while(xb < xbe); - while(xa < xae) { - y = *xa++ - borrow; - borrow = y >> 32 & 1UL; - *xc++ = (ULong)(y & 0xffffffffUL); - } -#else - do { - y = (*xa & 0xffff) - (*xb & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - z = (*xa++ >> 16) - (*xb++ >> 16) - borrow; - borrow = (z & 0x10000) >> 16; - Storeinc(xc, z, y); - } - while(xb < xbe); - while(xa < xae) { - y = (*xa & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - z = (*xa++ >> 16) - borrow; - borrow = (z & 0x10000) >> 16; - Storeinc(xc, z, y); - } -#endif - while(!*--xc) - wa--; - c->wds = wa; - return c; -} - -/* Return the absolute difference between x and the adjacent greater-magnitude double number (ignoring exponent overflows). */ -static double ulp(double x) -{ - register Long L; - double a = 0; - - L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; -#ifndef Sudden_Underflow - if (L > 0) { -#endif - set_word0(a, L); - set_word1(a, 0); -#ifndef Sudden_Underflow - } - else { - L = -L >> Exp_shift; - if (L < Exp_shift) { - set_word0(a, 0x80000 >> L); - set_word1(a, 0); - } - else { - set_word0(a, 0); - L -= Exp_shift; - set_word1(a, L >= 31 ? 1 : 1 << (31 - L)); - } - } -#endif - return a; -} - - -static double b2d(Bigint *a, int32 *e) -{ - ULong *xa, *xa0, w, y, z; - int32 k; - double d = 0; -#define d0 word0(d) -#define d1 word1(d) -#define set_d0(x) set_word0(d, x) -#define set_d1(x) set_word1(d, x) - - xa0 = a->x; - xa = xa0 + a->wds; - y = *--xa; -#ifdef DEBUG - if (!y) Bug("zero y in b2d"); -#endif - k = hi0bits(y); - *e = 32 - k; - if (k < Ebits) { - set_d0(Exp_1 | y >> (Ebits - k)); - w = xa > xa0 ? *--xa : 0; - set_d1(y << (32-Ebits + k) | w >> (Ebits - k)); - goto ret_d; - } - z = xa > xa0 ? *--xa : 0; - if (k -= Ebits) { - set_d0(Exp_1 | y << k | z >> (32 - k)); - y = xa > xa0 ? *--xa : 0; - set_d1(z << k | y >> (32 - k)); - } - else { - set_d0(Exp_1 | y); - set_d1(z); - } - ret_d: -#undef d0 -#undef d1 -#undef set_d0 -#undef set_d1 - return d; -} - - -/* Convert d into the form b*2^e, where b is an odd integer. b is the returned - * Bigint and e is the returned binary exponent. Return the number of significant - * bits in b in bits. d must be finite and nonzero. */ -static Bigint *d2b(double d, int32 *e, int32 *bits) -{ - Bigint *b; - int32 de, i, k; - ULong *x, y, z; -#define d0 word0(d) -#define d1 word1(d) -#define set_d0(x) set_word0(d, x) -#define set_d1(x) set_word1(d, x) - - b = Balloc(1); - if (!b) - return NULL; - x = b->x; - - z = d0 & Frac_mask; - set_d0(d0 & 0x7fffffff); /* clear sign bit, which we ignore */ -#ifdef Sudden_Underflow - de = (int32)(d0 >> Exp_shift); - z |= Exp_msk11; -#else - if ((de = (int32)(d0 >> Exp_shift)) != 0) - z |= Exp_msk1; -#endif - if ((y = d1) != 0) { - if ((k = lo0bits(&y)) != 0) { - x[0] = y | z << (32 - k); - z >>= k; - } - else - x[0] = y; - i = b->wds = (x[1] = z) ? 2 : 1; - } - else { - JS_ASSERT(z); - k = lo0bits(&z); - x[0] = z; - i = b->wds = 1; - k += 32; - } -#ifndef Sudden_Underflow - if (de) { -#endif - *e = de - Bias - (P-1) + k; - *bits = P - k; -#ifndef Sudden_Underflow - } - else { - *e = de - Bias - (P-1) + 1 + k; - *bits = 32*i - hi0bits(x[i-1]); - } -#endif - return b; -} -#undef d0 -#undef d1 -#undef set_d0 -#undef set_d1 - - -static double ratio(Bigint *a, Bigint *b) -{ - double da, db; - int32 k, ka, kb; - - da = b2d(a, &ka); - db = b2d(b, &kb); - k = ka - kb + 32*(a->wds - b->wds); - if (k > 0) - set_word0(da, word0(da) + k*Exp_msk1); - else { - k = -k; - set_word0(db, word0(db) + k*Exp_msk1); - } - return da / db; -} - -static CONST double -tens[] = { - 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, - 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, - 1e20, 1e21, 1e22 -}; - -static CONST double bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; -static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, -#ifdef Avoid_Underflow - 9007199254740992.e-256 -#else - 1e-256 -#endif - }; -/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */ -/* flag unnecessarily. It leads to a song and dance at the end of strtod. */ -#define Scale_Bit 0x10 -#define n_bigtens 5 - - -#ifdef INFNAN_CHECK - -#ifndef NAN_WORD0 -#define NAN_WORD0 0x7ff80000 -#endif - -#ifndef NAN_WORD1 -#define NAN_WORD1 0 -#endif - -static int match(CONST char **sp, char *t) -{ - int c, d; - CONST char *s = *sp; - - while(d = *t++) { - if ((c = *++s) >= 'A' && c <= 'Z') - c += 'a' - 'A'; - if (c != d) - return 0; - } - *sp = s + 1; - return 1; - } -#endif /* INFNAN_CHECK */ - - -#ifdef JS_THREADSAFE -static JSBool initialized = JS_FALSE; - -/* hacked replica of nspr _PR_InitDtoa */ -static void InitDtoa(void) -{ - freelist_lock = PR_NewLock(); - p5s_lock = PR_NewLock(); - initialized = JS_TRUE; -} -#endif - -void js_FinishDtoa(void) -{ - int count; - Bigint *temp; - -#ifdef JS_THREADSAFE - if (initialized == JS_TRUE) { - PR_DestroyLock(freelist_lock); - PR_DestroyLock(p5s_lock); - initialized = JS_FALSE; - } -#endif - - /* clear down the freelist array and p5s */ - - /* static Bigint *freelist[Kmax+1]; */ - for (count = 0; count <= Kmax; count++) { - Bigint **listp = &freelist[count]; - while ((temp = *listp) != NULL) { - *listp = temp->next; - free(temp); - } - freelist[count] = NULL; - } - - /* static Bigint *p5s; */ - while (p5s) { - temp = p5s; - p5s = p5s->next; - free(temp); - } -} - -/* nspr2 watcom bug ifdef omitted */ - -JS_FRIEND_API(double) -JS_strtod(CONST char *s00, char **se, int *err) -{ - int32 scale; - int32 bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign, - e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign; - CONST char *s, *s0, *s1; - double aadj, aadj1, adj, rv, rv0; - Long L; - ULong y, z; - Bigint *bb, *bb1, *bd, *bd0, *bs, *delta; - - *err = 0; - - bb = bd = bs = delta = NULL; - sign = nz0 = nz = 0; - rv = 0.; - - /* Locking for Balloc's shared buffers that will be used in this block */ - ACQUIRE_DTOA_LOCK(); - - for(s = s00;;s++) switch(*s) { - case '-': - sign = 1; - /* no break */ - case '+': - if (*++s) - goto break2; - /* no break */ - case 0: - s = s00; - goto ret; - case '\t': - case '\n': - case '\v': - case '\f': - case '\r': - case ' ': - continue; - default: - goto break2; - } -break2: - - if (*s == '0') { - nz0 = 1; - while(*++s == '0') ; - if (!*s) - goto ret; - } - s0 = s; - y = z = 0; - for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) - if (nd < 9) - y = 10*y + c - '0'; - else if (nd < 16) - z = 10*z + c - '0'; - nd0 = nd; - if (c == '.') { - c = *++s; - if (!nd) { - for(; c == '0'; c = *++s) - nz++; - if (c > '0' && c <= '9') { - s0 = s; - nf += nz; - nz = 0; - goto have_dig; - } - goto dig_done; - } - for(; c >= '0' && c <= '9'; c = *++s) { - have_dig: - nz++; - if (c -= '0') { - nf += nz; - for(i = 1; i < nz; i++) - if (nd++ < 9) - y *= 10; - else if (nd <= DBL_DIG + 1) - z *= 10; - if (nd++ < 9) - y = 10*y + c; - else if (nd <= DBL_DIG + 1) - z = 10*z + c; - nz = 0; - } - } - } -dig_done: - e = 0; - if (c == 'e' || c == 'E') { - if (!nd && !nz && !nz0) { - s = s00; - goto ret; - } - s00 = s; - esign = 0; - switch(c = *++s) { - case '-': - esign = 1; - case '+': - c = *++s; - } - if (c >= '0' && c <= '9') { - while(c == '0') - c = *++s; - if (c > '0' && c <= '9') { - L = c - '0'; - s1 = s; - while((c = *++s) >= '0' && c <= '9') - L = 10*L + c - '0'; - if (s - s1 > 8 || L > 19999) - /* Avoid confusion from exponents - * so large that e might overflow. - */ - e = 19999; /* safe for 16 bit ints */ - else - e = (int32)L; - if (esign) - e = -e; - } - else - e = 0; - } - else - s = s00; - } - if (!nd) { - if (!nz && !nz0) { -#ifdef INFNAN_CHECK - /* Check for Nan and Infinity */ - switch(c) { - case 'i': - case 'I': - if (match(&s,"nfinity")) { - set_word0(rv, 0x7ff00000); - set_word1(rv, 0); - goto ret; - } - break; - case 'n': - case 'N': - if (match(&s, "an")) { - set_word0(rv, NAN_WORD0); - set_word1(rv, NAN_WORD1); - goto ret; - } - } -#endif /* INFNAN_CHECK */ - s = s00; - } - goto ret; - } - e1 = e -= nf; - - /* Now we have nd0 digits, starting at s0, followed by a - * decimal point, followed by nd-nd0 digits. The number we're - * after is the integer represented by those digits times - * 10**e */ - - if (!nd0) - nd0 = nd; - k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1; - rv = y; - if (k > 9) - rv = tens[k - 9] * rv + z; - bd0 = 0; - if (nd <= DBL_DIG -#ifndef RND_PRODQUOT - && FLT_ROUNDS == 1 -#endif - ) { - if (!e) - goto ret; - if (e > 0) { - if (e <= Ten_pmax) { - /* rv = */ rounded_product(rv, tens[e]); - goto ret; - } - i = DBL_DIG - nd; - if (e <= Ten_pmax + i) { - /* A fancier test would sometimes let us do - * this for larger i values. - */ - e -= i; - rv *= tens[i]; - /* rv = */ rounded_product(rv, tens[e]); - goto ret; - } - } -#ifndef Inaccurate_Divide - else if (e >= -Ten_pmax) { - /* rv = */ rounded_quotient(rv, tens[-e]); - goto ret; - } -#endif - } - e1 += nd - k; - - scale = 0; - - /* Get starting approximation = rv * 10**e1 */ - - if (e1 > 0) { - if ((i = e1 & 15) != 0) - rv *= tens[i]; - if (e1 &= ~15) { - if (e1 > DBL_MAX_10_EXP) { - ovfl: - *err = JS_DTOA_ERANGE; -#ifdef __STDC__ - rv = HUGE_VAL; -#else - /* Can't trust HUGE_VAL */ - set_word0(rv, Exp_mask); - set_word1(rv, 0); -#endif - if (bd0) - goto retfree; - goto ret; - } - e1 >>= 4; - for(j = 0; e1 > 1; j++, e1 >>= 1) - if (e1 & 1) - rv *= bigtens[j]; - /* The last multiplication could overflow. */ - set_word0(rv, word0(rv) - P*Exp_msk1); - rv *= bigtens[j]; - if ((z = word0(rv) & Exp_mask) > Exp_msk1*(DBL_MAX_EXP+Bias-P)) - goto ovfl; - if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { - /* set to largest number */ - /* (Can't trust DBL_MAX) */ - set_word0(rv, Big0); - set_word1(rv, Big1); - } - else - set_word0(rv, word0(rv) + P*Exp_msk1); - } - } - else if (e1 < 0) { - e1 = -e1; - if ((i = e1 & 15) != 0) - rv /= tens[i]; - if (e1 &= ~15) { - e1 >>= 4; - if (e1 >= 1 << n_bigtens) - goto undfl; -#ifdef Avoid_Underflow - if (e1 & Scale_Bit) - scale = P; - for(j = 0; e1 > 0; j++, e1 >>= 1) - if (e1 & 1) - rv *= tinytens[j]; - if (scale && (j = P + 1 - ((word0(rv) & Exp_mask) - >> Exp_shift)) > 0) { - /* scaled rv is denormal; zap j low bits */ - if (j >= 32) { - set_word1(rv, 0); - set_word0(rv, word0(rv) & (0xffffffff << (j-32))); - if (!word0(rv)) - set_word0(rv, 1); - } - else - set_word1(rv, word1(rv) & (0xffffffff << j)); - } -#else - for(j = 0; e1 > 1; j++, e1 >>= 1) - if (e1 & 1) - rv *= tinytens[j]; - /* The last multiplication could underflow. */ - rv0 = rv; - rv *= tinytens[j]; - if (!rv) { - rv = 2.*rv0; - rv *= tinytens[j]; -#endif - if (!rv) { - undfl: - rv = 0.; - *err = JS_DTOA_ERANGE; - if (bd0) - goto retfree; - goto ret; - } -#ifndef Avoid_Underflow - set_word0(rv, Tiny0); - set_word1(rv, Tiny1); - /* The refinement below will clean - * this approximation up. - */ - } -#endif - } - } - - /* Now the hard part -- adjusting rv to the correct value.*/ - - /* Put digits into bd: true value = bd * 10^e */ - - bd0 = s2b(s0, nd0, nd, y); - if (!bd0) - goto nomem; - - for(;;) { - bd = Balloc(bd0->k); - if (!bd) - goto nomem; - Bcopy(bd, bd0); - bb = d2b(rv, &bbe, &bbbits); /* rv = bb * 2^bbe */ - if (!bb) - goto nomem; - bs = i2b(1); - if (!bs) - goto nomem; - - if (e >= 0) { - bb2 = bb5 = 0; - bd2 = bd5 = e; - } - else { - bb2 = bb5 = -e; - bd2 = bd5 = 0; - } - if (bbe >= 0) - bb2 += bbe; - else - bd2 -= bbe; - bs2 = bb2; -#ifdef Sudden_Underflow - j = P + 1 - bbbits; -#else -#ifdef Avoid_Underflow - j = bbe - scale; -#else - j = bbe; -#endif - i = j + bbbits - 1; /* logb(rv) */ - if (i < Emin) /* denormal */ - j += P - Emin; - else - j = P + 1 - bbbits; -#endif - bb2 += j; - bd2 += j; -#ifdef Avoid_Underflow - bd2 += scale; -#endif - i = bb2 < bd2 ? bb2 : bd2; - if (i > bs2) - i = bs2; - if (i > 0) { - bb2 -= i; - bd2 -= i; - bs2 -= i; - } - if (bb5 > 0) { - bs = pow5mult(bs, bb5); - if (!bs) - goto nomem; - bb1 = mult(bs, bb); - if (!bb1) - goto nomem; - Bfree(bb); - bb = bb1; - } - if (bb2 > 0) { - bb = lshift(bb, bb2); - if (!bb) - goto nomem; - } - if (bd5 > 0) { - bd = pow5mult(bd, bd5); - if (!bd) - goto nomem; - } - if (bd2 > 0) { - bd = lshift(bd, bd2); - if (!bd) - goto nomem; - } - if (bs2 > 0) { - bs = lshift(bs, bs2); - if (!bs) - goto nomem; - } - delta = diff(bb, bd); - if (!delta) - goto nomem; - dsign = delta->sign; - delta->sign = 0; - i = cmp(delta, bs); - if (i < 0) { - /* Error is less than half an ulp -- check for - * special case of mantissa a power of two. - */ - if (dsign || word1(rv) || word0(rv) & Bndry_mask -#ifdef Avoid_Underflow - || (word0(rv) & Exp_mask) <= Exp_msk1 + P*Exp_msk1 -#else - || (word0(rv) & Exp_mask) <= Exp_msk1 -#endif - ) { -#ifdef Avoid_Underflow - if (!delta->x[0] && delta->wds == 1) - dsign = 2; -#endif - break; - } - delta = lshift(delta,Log2P); - if (!delta) - goto nomem; - if (cmp(delta, bs) > 0) - goto drop_down; - break; - } - if (i == 0) { - /* exactly half-way between */ - if (dsign) { - if ((word0(rv) & Bndry_mask1) == Bndry_mask1 - && word1(rv) == 0xffffffff) { - /*boundary case -- increment exponent*/ - set_word0(rv, (word0(rv) & Exp_mask) + Exp_msk1); - set_word1(rv, 0); -#ifdef Avoid_Underflow - dsign = 0; -#endif - break; - } - } - else if (!(word0(rv) & Bndry_mask) && !word1(rv)) { -#ifdef Avoid_Underflow - dsign = 2; -#endif - drop_down: - /* boundary case -- decrement exponent */ -#ifdef Sudden_Underflow - L = word0(rv) & Exp_mask; - if (L <= Exp_msk1) - goto undfl; - L -= Exp_msk1; -#else - L = (word0(rv) & Exp_mask) - Exp_msk1; -#endif - set_word0(rv, L | Bndry_mask1); - set_word1(rv, 0xffffffff); - break; - } -#ifndef ROUND_BIASED - if (!(word1(rv) & LSB)) - break; -#endif - if (dsign) - rv += ulp(rv); -#ifndef ROUND_BIASED - else { - rv -= ulp(rv); -#ifndef Sudden_Underflow - if (!rv) - goto undfl; -#endif - } -#ifdef Avoid_Underflow - dsign = 1 - dsign; -#endif -#endif - break; - } - if ((aadj = ratio(delta, bs)) <= 2.) { - if (dsign) - aadj = aadj1 = 1.; - else if (word1(rv) || word0(rv) & Bndry_mask) { -#ifndef Sudden_Underflow - if (word1(rv) == Tiny1 && !word0(rv)) - goto undfl; -#endif - aadj = 1.; - aadj1 = -1.; - } - else { - /* special case -- power of FLT_RADIX to be */ - /* rounded down... */ - - if (aadj < 2./FLT_RADIX) - aadj = 1./FLT_RADIX; - else - aadj *= 0.5; - aadj1 = -aadj; - } - } - else { - aadj *= 0.5; - aadj1 = dsign ? aadj : -aadj; -#ifdef Check_FLT_ROUNDS - switch(FLT_ROUNDS) { - case 2: /* towards +infinity */ - aadj1 -= 0.5; - break; - case 0: /* towards 0 */ - case 3: /* towards -infinity */ - aadj1 += 0.5; - } -#else - if (FLT_ROUNDS == 0) - aadj1 += 0.5; -#endif - } - y = word0(rv) & Exp_mask; - - /* Check for overflow */ - - if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { - rv0 = rv; - set_word0(rv, word0(rv) - P*Exp_msk1); - adj = aadj1 * ulp(rv); - rv += adj; - if ((word0(rv) & Exp_mask) >= - Exp_msk1*(DBL_MAX_EXP+Bias-P)) { - if (word0(rv0) == Big0 && word1(rv0) == Big1) - goto ovfl; - set_word0(rv, Big0); - set_word1(rv, Big1); - goto cont; - } - else - set_word0(rv, word0(rv) + P*Exp_msk1); - } - else { -#ifdef Sudden_Underflow - if ((word0(rv) & Exp_mask) <= P*Exp_msk1) { - rv0 = rv; - set_word0(rv, word0(rv) + P*Exp_msk1); - adj = aadj1 * ulp(rv); - rv += adj; - if ((word0(rv) & Exp_mask) <= P*Exp_msk1) - { - if (word0(rv0) == Tiny0 - && word1(rv0) == Tiny1) - goto undfl; - set_word0(rv, Tiny0); - set_word1(rv, Tiny1); - goto cont; - } - else - set_word0(rv, word0(rv) - P*Exp_msk1); - } - else { - adj = aadj1 * ulp(rv); - rv += adj; - } -#else - /* Compute adj so that the IEEE rounding rules will - * correctly round rv + adj in some half-way cases. - * If rv * ulp(rv) is denormalized (i.e., - * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid - * trouble from bits lost to denormalization; - * example: 1.2e-307 . - */ -#ifdef Avoid_Underflow - if (y <= P*Exp_msk1 && aadj > 1.) -#else - if (y <= (P-1)*Exp_msk1 && aadj > 1.) -#endif - { - aadj1 = (double)(int32)(aadj + 0.5); - if (!dsign) - aadj1 = -aadj1; - } -#ifdef Avoid_Underflow - if (scale && y <= P*Exp_msk1) - set_word0(aadj1, word0(aadj1) + (P+1)*Exp_msk1 - y); -#endif - adj = aadj1 * ulp(rv); - rv += adj; -#endif - } - z = word0(rv) & Exp_mask; -#ifdef Avoid_Underflow - if (!scale) -#endif - if (y == z) { - /* Can we stop now? */ - L = (Long)aadj; - aadj -= L; - /* The tolerances below are conservative. */ - if (dsign || word1(rv) || word0(rv) & Bndry_mask) { - if (aadj < .4999999 || aadj > .5000001) - break; - } - else if (aadj < .4999999/FLT_RADIX) - break; - } - cont: - Bfree(bb); - Bfree(bd); - Bfree(bs); - Bfree(delta); - bb = bd = bs = delta = NULL; - } -#ifdef Avoid_Underflow - if (scale) { - rv0 = 0.; - set_word0(rv0, Exp_1 - P*Exp_msk1); - set_word1(rv0, 0); - if ((word0(rv) & Exp_mask) <= P*Exp_msk1 - && word1(rv) & 1 - && dsign != 2) { - if (dsign) { -#ifdef Sudden_Underflow - /* rv will be 0, but this would give the */ - /* right result if only rv *= rv0 worked. */ - set_word0(rv, word0(rv) + P*Exp_msk1); - set_word0(rv0, Exp_1 - 2*P*Exp_msk1); -#endif - rv += ulp(rv); - } - else - set_word1(rv, word1(rv) & ~1); - } - rv *= rv0; - } -#endif /* Avoid_Underflow */ -retfree: - Bfree(bb); - Bfree(bd); - Bfree(bs); - Bfree(bd0); - Bfree(delta); -ret: - RELEASE_DTOA_LOCK(); - if (se) - *se = (char *)s; - return sign ? -rv : rv; - -nomem: - Bfree(bb); - Bfree(bd); - Bfree(bs); - Bfree(bd0); - Bfree(delta); - RELEASE_DTOA_LOCK(); - *err = JS_DTOA_ENOMEM; - return 0; -} - - -/* Return floor(b/2^k) and set b to be the remainder. The returned quotient must be less than 2^32. */ -static uint32 quorem2(Bigint *b, int32 k) -{ - ULong mask; - ULong result; - ULong *bx, *bxe; - int32 w; - int32 n = k >> 5; - k &= 0x1F; - mask = (1<wds - n; - if (w <= 0) - return 0; - JS_ASSERT(w <= 2); - bx = b->x; - bxe = bx + n; - result = *bxe >> k; - *bxe &= mask; - if (w == 2) { - JS_ASSERT(!(bxe[1] & ~mask)); - if (k) - result |= bxe[1] << (32 - k); - } - n++; - while (!*bxe && bxe != bx) { - n--; - bxe--; - } - b->wds = n; - return result; -} - -/* Return floor(b/S) and set b to be the remainder. As added restrictions, b must not have - * more words than S, the most significant word of S must not start with a 1 bit, and the - * returned quotient must be less than 36. */ -static int32 quorem(Bigint *b, Bigint *S) -{ - int32 n; - ULong *bx, *bxe, q, *sx, *sxe; -#ifdef ULLong - ULLong borrow, carry, y, ys; -#else - ULong borrow, carry, y, ys; - ULong si, z, zs; -#endif - - n = S->wds; - JS_ASSERT(b->wds <= n); - if (b->wds < n) - return 0; - sx = S->x; - sxe = sx + --n; - bx = b->x; - bxe = bx + n; - JS_ASSERT(*sxe <= 0x7FFFFFFF); - q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ - JS_ASSERT(q < 36); - if (q) { - borrow = 0; - carry = 0; - do { -#ifdef ULLong - ys = *sx++ * (ULLong)q + carry; - carry = ys >> 32; - y = *bx - (ys & 0xffffffffUL) - borrow; - borrow = y >> 32 & 1UL; - *bx++ = (ULong)(y & 0xffffffffUL); -#else - si = *sx++; - ys = (si & 0xffff) * q + carry; - zs = (si >> 16) * q + (ys >> 16); - carry = zs >> 16; - y = (*bx & 0xffff) - (ys & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - z = (*bx >> 16) - (zs & 0xffff) - borrow; - borrow = (z & 0x10000) >> 16; - Storeinc(bx, z, y); -#endif - } - while(sx <= sxe); - if (!*bxe) { - bx = b->x; - while(--bxe > bx && !*bxe) - --n; - b->wds = n; - } - } - if (cmp(b, S) >= 0) { - q++; - borrow = 0; - carry = 0; - bx = b->x; - sx = S->x; - do { -#ifdef ULLong - ys = *sx++ + carry; - carry = ys >> 32; - y = *bx - (ys & 0xffffffffUL) - borrow; - borrow = y >> 32 & 1UL; - *bx++ = (ULong)(y & 0xffffffffUL); -#else - si = *sx++; - ys = (si & 0xffff) + carry; - zs = (si >> 16) + (ys >> 16); - carry = zs >> 16; - y = (*bx & 0xffff) - (ys & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - z = (*bx >> 16) - (zs & 0xffff) - borrow; - borrow = (z & 0x10000) >> 16; - Storeinc(bx, z, y); -#endif - } while(sx <= sxe); - bx = b->x; - bxe = bx + n; - if (!*bxe) { - while(--bxe > bx && !*bxe) - --n; - b->wds = n; - } - } - return (int32)q; -} - -/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. - * - * Inspired by "How to Print Floating-Point Numbers Accurately" by - * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 92-101]. - * - * Modifications: - * 1. Rather than iterating, we use a simple numeric overestimate - * to determine k = floor(log10(d)). We scale relevant - * quantities using O(log2(k)) rather than O(k) multiplications. - * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't - * try to generate digits strictly left to right. Instead, we - * compute with fewer bits and propagate the carry if necessary - * when rounding the final digit up. This is often faster. - * 3. Under the assumption that input will be rounded nearest, - * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. - * That is, we allow equality in stopping tests when the - * round-nearest rule will give the same floating-point value - * as would satisfaction of the stopping test with strict - * inequality. - * 4. We remove common factors of powers of 2 from relevant - * quantities. - * 5. When converting floating-point integers less than 1e16, - * we use floating-point arithmetic rather than resorting - * to multiple-precision integers. - * 6. When asked to produce fewer than 15 digits, we first try - * to get by with floating-point arithmetic; we resort to - * multiple-precision integer arithmetic only if we cannot - * guarantee that the floating-point calculation has given - * the correctly rounded result. For k requested digits and - * "uniformly" distributed input, the probability is - * something like 10^(k-15) that we must resort to the Long - * calculation. - */ - -/* Always emits at least one digit. */ -/* If biasUp is set, then rounding in modes 2 and 3 will round away from zero - * when the number is exactly halfway between two representable values. For example, - * rounding 2.5 to zero digits after the decimal point will return 3 and not 2. - * 2.49 will still round to 2, and 2.51 will still round to 3. */ -/* bufsize should be at least 20 for modes 0 and 1. For the other modes, - * bufsize should be two greater than the maximum number of output characters expected. */ -static JSBool -js_dtoa(double d, int mode, JSBool biasUp, int ndigits, - int *decpt, int *sign, char **rve, char *buf, size_t bufsize) -{ - /* Arguments ndigits, decpt, sign are similar to those - of ecvt and fcvt; trailing zeros are suppressed from - the returned string. If not null, *rve is set to point - to the end of the return value. If d is +-Infinity or NaN, - then *decpt is set to 9999. - - mode: - 0 ==> shortest string that yields d when read in - and rounded to nearest. - 1 ==> like 0, but with Steele & White stopping rule; - e.g. with IEEE P754 arithmetic , mode 0 gives - 1e23 whereas mode 1 gives 9.999999999999999e22. - 2 ==> max(1,ndigits) significant digits. This gives a - return value similar to that of ecvt, except - that trailing zeros are suppressed. - 3 ==> through ndigits past the decimal point. This - gives a return value similar to that from fcvt, - except that trailing zeros are suppressed, and - ndigits can be negative. - 4-9 should give the same return values as 2-3, i.e., - 4 <= mode <= 9 ==> same return as mode - 2 + (mode & 1). These modes are mainly for - debugging; often they run slower but sometimes - faster than modes 2-3. - 4,5,8,9 ==> left-to-right digit generation. - 6-9 ==> don't try fast floating-point estimate - (if applicable). - - Values of mode other than 0-9 are treated as mode 0. - - Sufficient space is allocated to the return value - to hold the suppressed trailing zeros. - */ - - int32 bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1, - j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, - spec_case, try_quick; - Long L; -#ifndef Sudden_Underflow - int32 denorm; - ULong x; -#endif - Bigint *b, *b1, *delta, *mlo, *mhi, *S; - double d2, ds, eps; - char *s; - - if (word0(d) & Sign_bit) { - /* set sign for everything, including 0's and NaNs */ - *sign = 1; - set_word0(d, word0(d) & ~Sign_bit); /* clear sign bit */ - } - else - *sign = 0; - - if ((word0(d) & Exp_mask) == Exp_mask) { - /* Infinity or NaN */ - *decpt = 9999; - s = !word1(d) && !(word0(d) & Frac_mask) ? "Infinity" : "NaN"; - if ((s[0] == 'I' && bufsize < 9) || (s[0] == 'N' && bufsize < 4)) { - JS_ASSERT(JS_FALSE); -/* JS_SetError(JS_BUFFER_OVERFLOW_ERROR, 0); */ - return JS_FALSE; - } - strcpy(buf, s); - if (rve) { - *rve = buf[3] ? buf + 8 : buf + 3; - JS_ASSERT(**rve == '\0'); - } - return JS_TRUE; - } - - b = NULL; /* initialize for abort protection */ - S = NULL; - mlo = mhi = NULL; - - if (!d) { - no_digits: - *decpt = 1; - if (bufsize < 2) { - JS_ASSERT(JS_FALSE); -/* JS_SetError(JS_BUFFER_OVERFLOW_ERROR, 0); */ - return JS_FALSE; - } - buf[0] = '0'; buf[1] = '\0'; /* copy "0" to buffer */ - if (rve) - *rve = buf + 1; - /* We might have jumped to "no_digits" from below, so we need - * to be sure to free the potentially allocated Bigints to avoid - * memory leaks. */ - Bfree(b); - Bfree(S); - if (mlo != mhi) - Bfree(mlo); - Bfree(mhi); - return JS_TRUE; - } - - b = d2b(d, &be, &bbits); - if (!b) - goto nomem; -#ifdef Sudden_Underflow - i = (int32)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); -#else - if ((i = (int32)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1))) != 0) { -#endif - d2 = d; - set_word0(d2, word0(d2) & Frac_mask1); - set_word0(d2, word0(d2) | Exp_11); - - /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 - * log10(x) = log(x) / log(10) - * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) - * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) - * - * This suggests computing an approximation k to log10(d) by - * - * k = (i - Bias)*0.301029995663981 - * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); - * - * We want k to be too large rather than too small. - * The error in the first-order Taylor series approximation - * is in our favor, so we just round up the constant enough - * to compensate for any error in the multiplication of - * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, - * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, - * adding 1e-13 to the constant term more than suffices. - * Hence we adjust the constant term to 0.1760912590558. - * (We could get a more accurate k by invoking log10, - * but this is probably not worthwhile.) - */ - - i -= Bias; -#ifndef Sudden_Underflow - denorm = 0; - } - else { - /* d is denormalized */ - - i = bbits + be + (Bias + (P-1) - 1); - x = i > 32 ? word0(d) << (64 - i) | word1(d) >> (i - 32) : word1(d) << (32 - i); - d2 = x; - set_word0(d2, word0(d2) - 31*Exp_msk1); /* adjust exponent */ - i -= (Bias + (P-1) - 1) + 1; - denorm = 1; - } -#endif - /* At this point d = f*2^i, where 1 <= f < 2. d2 is an approximation of f. */ - ds = (d2-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; - k = (int32)ds; - if (ds < 0. && ds != k) - k--; /* want k = floor(ds) */ - k_check = 1; - if (k >= 0 && k <= Ten_pmax) { - if (d < tens[k]) - k--; - k_check = 0; - } - /* At this point floor(log10(d)) <= k <= floor(log10(d))+1. - If k_check is zero, we're guaranteed that k = floor(log10(d)). */ - j = bbits - i - 1; - /* At this point d = b/2^j, where b is an odd integer. */ - if (j >= 0) { - b2 = 0; - s2 = j; - } - else { - b2 = -j; - s2 = 0; - } - if (k >= 0) { - b5 = 0; - s5 = k; - s2 += k; - } - else { - b2 -= k; - b5 = -k; - s5 = 0; - } - /* At this point d/10^k = (b * 2^b2 * 5^b5) / (2^s2 * 5^s5), where b is an odd integer, - b2 >= 0, b5 >= 0, s2 >= 0, and s5 >= 0. */ - if (mode < 0 || mode > 9) - mode = 0; - try_quick = 1; - if (mode > 5) { - mode -= 4; - try_quick = 0; - } - leftright = 1; - ilim = ilim1 = 0; - switch(mode) { - case 0: - case 1: - ilim = ilim1 = -1; - i = 18; - ndigits = 0; - break; - case 2: - leftright = 0; - /* no break */ - case 4: - if (ndigits <= 0) - ndigits = 1; - ilim = ilim1 = i = ndigits; - break; - case 3: - leftright = 0; - /* no break */ - case 5: - i = ndigits + k + 1; - ilim = i; - ilim1 = i - 1; - if (i <= 0) - i = 1; - } - /* ilim is the maximum number of significant digits we want, based on k and ndigits. */ - /* ilim1 is the maximum number of significant digits we want, based on k and ndigits, - when it turns out that k was computed too high by one. */ - - /* Ensure space for at least i+1 characters, including trailing null. */ - if (bufsize <= (size_t)i) { - Bfree(b); - JS_ASSERT(JS_FALSE); - return JS_FALSE; - } - s = buf; - - if (ilim >= 0 && ilim <= Quick_max && try_quick) { - - /* Try to get by with floating-point arithmetic. */ - - i = 0; - d2 = d; - k0 = k; - ilim0 = ilim; - ieps = 2; /* conservative */ - /* Divide d by 10^k, keeping track of the roundoff error and avoiding overflows. */ - if (k > 0) { - ds = tens[k&0xf]; - j = k >> 4; - if (j & Bletch) { - /* prevent overflows */ - j &= Bletch - 1; - d /= bigtens[n_bigtens-1]; - ieps++; - } - for(; j; j >>= 1, i++) - if (j & 1) { - ieps++; - ds *= bigtens[i]; - } - d /= ds; - } - else if ((j1 = -k) != 0) { - d *= tens[j1 & 0xf]; - for(j = j1 >> 4; j; j >>= 1, i++) - if (j & 1) { - ieps++; - d *= bigtens[i]; - } - } - /* Check that k was computed correctly. */ - if (k_check && d < 1. && ilim > 0) { - if (ilim1 <= 0) - goto fast_failed; - ilim = ilim1; - k--; - d *= 10.; - ieps++; - } - /* eps bounds the cumulative error. */ - eps = ieps*d + 7.; - set_word0(eps, word0(eps) - (P-1)*Exp_msk1); - if (ilim == 0) { - S = mhi = 0; - d -= 5.; - if (d > eps) - goto one_digit; - if (d < -eps) - goto no_digits; - goto fast_failed; - } -#ifndef No_leftright - if (leftright) { - /* Use Steele & White method of only - * generating digits needed. - */ - eps = 0.5/tens[ilim-1] - eps; - for(i = 0;;) { - L = (Long)d; - d -= L; - *s++ = '0' + (char)L; - if (d < eps) - goto ret1; - if (1. - d < eps) - goto bump_up; - if (++i >= ilim) - break; - eps *= 10.; - d *= 10.; - } - } - else { -#endif - /* Generate ilim digits, then fix them up. */ - eps *= tens[ilim-1]; - for(i = 1;; i++, d *= 10.) { - L = (Long)d; - d -= L; - *s++ = '0' + (char)L; - if (i == ilim) { - if (d > 0.5 + eps) - goto bump_up; - else if (d < 0.5 - eps) { - while(*--s == '0') ; - s++; - goto ret1; - } - break; - } - } -#ifndef No_leftright - } -#endif - fast_failed: - s = buf; - d = d2; - k = k0; - ilim = ilim0; - } - - /* Do we have a "small" integer? */ - - if (be >= 0 && k <= Int_max) { - /* Yes. */ - ds = tens[k]; - if (ndigits < 0 && ilim <= 0) { - S = mhi = 0; - if (ilim < 0 || d < 5*ds || (!biasUp && d == 5*ds)) - goto no_digits; - goto one_digit; - } - - /* Use true number of digits to limit looping. */ - for(i = 1; i<=k+1; i++) { - L = (Long) (d / ds); - d -= L*ds; -#ifdef Check_FLT_ROUNDS - /* If FLT_ROUNDS == 2, L will usually be high by 1 */ - if (d < 0) { - L--; - d += ds; - } -#endif - *s++ = '0' + (char)L; - if (i == ilim) { - d += d; - if ((d > ds) || (d == ds && (L & 1 || biasUp))) { - bump_up: - while(*--s == '9') - if (s == buf) { - k++; - *s = '0'; - break; - } - ++*s++; - } - break; - } - d *= 10.; - } - goto ret1; - } - - m2 = b2; - m5 = b5; - if (leftright) { - if (mode < 2) { - i = -#ifndef Sudden_Underflow - denorm ? be + (Bias + (P-1) - 1 + 1) : -#endif - 1 + P - bbits; - /* i is 1 plus the number of trailing zero bits in d's significand. Thus, - (2^m2 * 5^m5) / (2^(s2+i) * 5^s5) = (1/2 lsb of d)/10^k. */ - } - else { - j = ilim - 1; - if (m5 >= j) - m5 -= j; - else { - s5 += j -= m5; - b5 += j; - m5 = 0; - } - if ((i = ilim) < 0) { - m2 -= i; - i = 0; - } - /* (2^m2 * 5^m5) / (2^(s2+i) * 5^s5) = (1/2 * 10^(1-ilim))/10^k. */ - } - b2 += i; - s2 += i; - mhi = i2b(1); - if (!mhi) - goto nomem; - /* (mhi * 2^m2 * 5^m5) / (2^s2 * 5^s5) = one-half of last printed (when mode >= 2) or - input (when mode < 2) significant digit, divided by 10^k. */ - } - /* We still have d/10^k = (b * 2^b2 * 5^b5) / (2^s2 * 5^s5). Reduce common factors in - b2, m2, and s2 without changing the equalities. */ - if (m2 > 0 && s2 > 0) { - i = m2 < s2 ? m2 : s2; - b2 -= i; - m2 -= i; - s2 -= i; - } - - /* Fold b5 into b and m5 into mhi. */ - if (b5 > 0) { - if (leftright) { - if (m5 > 0) { - mhi = pow5mult(mhi, m5); - if (!mhi) - goto nomem; - b1 = mult(mhi, b); - if (!b1) - goto nomem; - Bfree(b); - b = b1; - } - if ((j = b5 - m5) != 0) { - b = pow5mult(b, j); - if (!b) - goto nomem; - } - } - else { - b = pow5mult(b, b5); - if (!b) - goto nomem; - } - } - /* Now we have d/10^k = (b * 2^b2) / (2^s2 * 5^s5) and - (mhi * 2^m2) / (2^s2 * 5^s5) = one-half of last printed or input significant digit, divided by 10^k. */ - - S = i2b(1); - if (!S) - goto nomem; - if (s5 > 0) { - S = pow5mult(S, s5); - if (!S) - goto nomem; - } - /* Now we have d/10^k = (b * 2^b2) / (S * 2^s2) and - (mhi * 2^m2) / (S * 2^s2) = one-half of last printed or input significant digit, divided by 10^k. */ - - /* Check for special case that d is a normalized power of 2. */ - spec_case = 0; - if (mode < 2) { - if (!word1(d) && !(word0(d) & Bndry_mask) -#ifndef Sudden_Underflow - && word0(d) & (Exp_mask & Exp_mask << 1) -#endif - ) { - /* The special case. Here we want to be within a quarter of the last input - significant digit instead of one half of it when the decimal output string's value is less than d. */ - b2 += Log2P; - s2 += Log2P; - spec_case = 1; - } - } - - /* Arrange for convenient computation of quotients: - * shift left if necessary so divisor has 4 leading 0 bits. - * - * Perhaps we should just compute leading 28 bits of S once - * and for all and pass them and a shift to quorem, so it - * can do shifts and ors to compute the numerator for q. - */ - if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f) != 0) - i = 32 - i; - /* i is the number of leading zero bits in the most significant word of S*2^s2. */ - if (i > 4) { - i -= 4; - b2 += i; - m2 += i; - s2 += i; - } - else if (i < 4) { - i += 28; - b2 += i; - m2 += i; - s2 += i; - } - /* Now S*2^s2 has exactly four leading zero bits in its most significant word. */ - if (b2 > 0) { - b = lshift(b, b2); - if (!b) - goto nomem; - } - if (s2 > 0) { - S = lshift(S, s2); - if (!S) - goto nomem; - } - /* Now we have d/10^k = b/S and - (mhi * 2^m2) / S = maximum acceptable error, divided by 10^k. */ - if (k_check) { - if (cmp(b,S) < 0) { - k--; - b = multadd(b, 10, 0); /* we botched the k estimate */ - if (!b) - goto nomem; - if (leftright) { - mhi = multadd(mhi, 10, 0); - if (!mhi) - goto nomem; - } - ilim = ilim1; - } - } - /* At this point 1 <= d/10^k = b/S < 10. */ - - if (ilim <= 0 && mode > 2) { - /* We're doing fixed-mode output and d is less than the minimum nonzero output in this mode. - Output either zero or the minimum nonzero output depending on which is closer to d. */ - if (ilim < 0) - goto no_digits; - S = multadd(S,5,0); - if (!S) - goto nomem; - i = cmp(b,S); - if (i < 0 || (i == 0 && !biasUp)) { - /* Always emit at least one digit. If the number appears to be zero - using the current mode, then emit one '0' digit and set decpt to 1. */ - /*no_digits: - k = -1 - ndigits; - goto ret; */ - goto no_digits; - } - one_digit: - *s++ = '1'; - k++; - goto ret; - } - if (leftright) { - if (m2 > 0) { - mhi = lshift(mhi, m2); - if (!mhi) - goto nomem; - } - - /* Compute mlo -- check for special case - * that d is a normalized power of 2. - */ - - mlo = mhi; - if (spec_case) { - mhi = Balloc(mhi->k); - if (!mhi) - goto nomem; - Bcopy(mhi, mlo); - mhi = lshift(mhi, Log2P); - if (!mhi) - goto nomem; - } - /* mlo/S = maximum acceptable error, divided by 10^k, if the output is less than d. */ - /* mhi/S = maximum acceptable error, divided by 10^k, if the output is greater than d. */ - - for(i = 1;;i++) { - dig = quorem(b,S) + '0'; - /* Do we yet have the shortest decimal string - * that will round to d? - */ - j = cmp(b, mlo); - /* j is b/S compared with mlo/S. */ - delta = diff(S, mhi); - if (!delta) - goto nomem; - j1 = delta->sign ? 1 : cmp(b, delta); - Bfree(delta); - /* j1 is b/S compared with 1 - mhi/S. */ -#ifndef ROUND_BIASED - if (j1 == 0 && !mode && !(word1(d) & 1)) { - if (dig == '9') - goto round_9_up; - if (j > 0) - dig++; - *s++ = (char)dig; - goto ret; - } -#endif - if ((j < 0) || (j == 0 && !mode -#ifndef ROUND_BIASED - && !(word1(d) & 1) -#endif - )) { - if (j1 > 0) { - /* Either dig or dig+1 would work here as the least significant decimal digit. - Use whichever would produce a decimal value closer to d. */ - b = lshift(b, 1); - if (!b) - goto nomem; - j1 = cmp(b, S); - if (((j1 > 0) || (j1 == 0 && (dig & 1 || biasUp))) - && (dig++ == '9')) - goto round_9_up; - } - *s++ = (char)dig; - goto ret; - } - if (j1 > 0) { - if (dig == '9') { /* possible if i == 1 */ - round_9_up: - *s++ = '9'; - goto roundoff; - } - *s++ = (char)dig + 1; - goto ret; - } - *s++ = (char)dig; - if (i == ilim) - break; - b = multadd(b, 10, 0); - if (!b) - goto nomem; - if (mlo == mhi) { - mlo = mhi = multadd(mhi, 10, 0); - if (!mhi) - goto nomem; - } - else { - mlo = multadd(mlo, 10, 0); - if (!mlo) - goto nomem; - mhi = multadd(mhi, 10, 0); - if (!mhi) - goto nomem; - } - } - } - else - for(i = 1;; i++) { - *s++ = (char)(dig = quorem(b,S) + '0'); - if (i >= ilim) - break; - b = multadd(b, 10, 0); - if (!b) - goto nomem; - } - - /* Round off last digit */ - - b = lshift(b, 1); - if (!b) - goto nomem; - j = cmp(b, S); - if ((j > 0) || (j == 0 && (dig & 1 || biasUp))) { - roundoff: - while(*--s == '9') - if (s == buf) { - k++; - *s++ = '1'; - goto ret; - } - ++*s++; - } - else { - /* Strip trailing zeros */ - while(*--s == '0') ; - s++; - } - ret: - Bfree(S); - if (mhi) { - if (mlo && mlo != mhi) - Bfree(mlo); - Bfree(mhi); - } - ret1: - Bfree(b); - JS_ASSERT(s < buf + bufsize); - *s = '\0'; - if (rve) - *rve = s; - *decpt = k + 1; - return JS_TRUE; - -nomem: - Bfree(S); - if (mhi) { - if (mlo && mlo != mhi) - Bfree(mlo); - Bfree(mhi); - } - Bfree(b); - return JS_FALSE; -} - - -/* Mapping of JSDToStrMode -> js_dtoa mode */ -static const int dtoaModes[] = { - 0, /* DTOSTR_STANDARD */ - 0, /* DTOSTR_STANDARD_EXPONENTIAL, */ - 3, /* DTOSTR_FIXED, */ - 2, /* DTOSTR_EXPONENTIAL, */ - 2}; /* DTOSTR_PRECISION */ - -JS_FRIEND_API(char *) -JS_dtostr(char *buffer, size_t bufferSize, JSDToStrMode mode, int precision, double d) -{ - int decPt; /* Position of decimal point relative to first digit returned by js_dtoa */ - int sign; /* Nonzero if the sign bit was set in d */ - int nDigits; /* Number of significand digits returned by js_dtoa */ - char *numBegin = buffer+2; /* Pointer to the digits returned by js_dtoa; the +2 leaves space for */ - /* the sign and/or decimal point */ - char *numEnd; /* Pointer past the digits returned by js_dtoa */ - JSBool dtoaRet; - - JS_ASSERT(bufferSize >= (size_t)(mode <= DTOSTR_STANDARD_EXPONENTIAL ? DTOSTR_STANDARD_BUFFER_SIZE : - DTOSTR_VARIABLE_BUFFER_SIZE(precision))); - - if (mode == DTOSTR_FIXED && (d >= 1e21 || d <= -1e21)) - mode = DTOSTR_STANDARD; /* Change mode here rather than below because the buffer may not be large enough to hold a large integer. */ - - /* Locking for Balloc's shared buffers */ - ACQUIRE_DTOA_LOCK(); - dtoaRet = js_dtoa(d, dtoaModes[mode], mode >= DTOSTR_FIXED, precision, &decPt, &sign, &numEnd, numBegin, bufferSize-2); - RELEASE_DTOA_LOCK(); - if (!dtoaRet) - return 0; - - nDigits = numEnd - numBegin; - - /* If Infinity, -Infinity, or NaN, return the string regardless of the mode. */ - if (decPt != 9999) { - JSBool exponentialNotation = JS_FALSE; - int minNDigits = 0; /* Minimum number of significand digits required by mode and precision */ - char *p; - char *q; - - switch (mode) { - case DTOSTR_STANDARD: - if (decPt < -5 || decPt > 21) - exponentialNotation = JS_TRUE; - else - minNDigits = decPt; - break; - - case DTOSTR_FIXED: - if (precision >= 0) - minNDigits = decPt + precision; - else - minNDigits = decPt; - break; - - case DTOSTR_EXPONENTIAL: - JS_ASSERT(precision > 0); - minNDigits = precision; - /* Fall through */ - case DTOSTR_STANDARD_EXPONENTIAL: - exponentialNotation = JS_TRUE; - break; - - case DTOSTR_PRECISION: - JS_ASSERT(precision > 0); - minNDigits = precision; - if (decPt < -5 || decPt > precision) - exponentialNotation = JS_TRUE; - break; - } - - /* If the number has fewer than minNDigits, pad it with zeros at the end */ - if (nDigits < minNDigits) { - p = numBegin + minNDigits; - nDigits = minNDigits; - do { - *numEnd++ = '0'; - } while (numEnd != p); - *numEnd = '\0'; - } - - if (exponentialNotation) { - /* Insert a decimal point if more than one significand digit */ - if (nDigits != 1) { - numBegin--; - numBegin[0] = numBegin[1]; - numBegin[1] = '.'; - } - JS_snprintf(numEnd, bufferSize - (numEnd - buffer), "e%+d", decPt-1); - } else if (decPt != nDigits) { - /* Some kind of a fraction in fixed notation */ - JS_ASSERT(decPt <= nDigits); - if (decPt > 0) { - /* dd...dd . dd...dd */ - p = --numBegin; - do { - *p = p[1]; - p++; - } while (--decPt); - *p = '.'; - } else { - /* 0 . 00...00dd...dd */ - p = numEnd; - numEnd += 1 - decPt; - q = numEnd; - JS_ASSERT(numEnd < buffer + bufferSize); - *numEnd = '\0'; - while (p != numBegin) - *--q = *--p; - for (p = numBegin + 1; p != q; p++) - *p = '0'; - *numBegin = '.'; - *--numBegin = '0'; - } - } - } - - /* If negative and neither -0.0 nor NaN, output a leading '-'. */ - if (sign && - !(word0(d) == Sign_bit && word1(d) == 0) && - !((word0(d) & Exp_mask) == Exp_mask && - (word1(d) || (word0(d) & Frac_mask)))) { - *--numBegin = '-'; - } - return numBegin; -} - - -/* Let b = floor(b / divisor), and return the remainder. b must be nonnegative. - * divisor must be between 1 and 65536. - * This function cannot run out of memory. */ -static uint32 -divrem(Bigint *b, uint32 divisor) -{ - int32 n = b->wds; - uint32 remainder = 0; - ULong *bx; - ULong *bp; - - JS_ASSERT(divisor > 0 && divisor <= 65536); - - if (!n) - return 0; /* b is zero */ - bx = b->x; - bp = bx + n; - do { - ULong a = *--bp; - ULong dividend = remainder << 16 | a >> 16; - ULong quotientHi = dividend / divisor; - ULong quotientLo; - - remainder = dividend - quotientHi*divisor; - JS_ASSERT(quotientHi <= 0xFFFF && remainder < divisor); - dividend = remainder << 16 | (a & 0xFFFF); - quotientLo = dividend / divisor; - remainder = dividend - quotientLo*divisor; - JS_ASSERT(quotientLo <= 0xFFFF && remainder < divisor); - *bp = quotientHi << 16 | quotientLo; - } while (bp != bx); - /* Decrease the size of the number if its most significant word is now zero. */ - if (bx[n-1] == 0) - b->wds--; - return remainder; -} - - -/* "-0.0000...(1073 zeros after decimal point)...0001\0" is the longest string that we could produce, - * which occurs when printing -5e-324 in binary. We could compute a better estimate of the size of - * the output string and malloc fewer bytes depending on d and base, but why bother? */ -#define DTOBASESTR_BUFFER_SIZE 1078 -#define BASEDIGIT(digit) ((char)(((digit) >= 10) ? 'a' - 10 + (digit) : '0' + (digit))) - -JS_FRIEND_API(char *) -JS_dtobasestr(int base, double d) -{ - char *buffer; /* The output string */ - char *p; /* Pointer to current position in the buffer */ - char *pInt; /* Pointer to the beginning of the integer part of the string */ - char *q; - uint32 digit; - double di; /* d truncated to an integer */ - double df; /* The fractional part of d */ - - JS_ASSERT(base >= 2 && base <= 36); - - buffer = (char*) malloc(DTOBASESTR_BUFFER_SIZE); - if (buffer) { - p = buffer; - if (d < 0.0 -#if defined(XP_WIN) || defined(XP_OS2) - && !((word0(d) & Exp_mask) == Exp_mask && ((word0(d) & Frac_mask) || word1(d))) /* Visual C++ doesn't know how to compare against NaN */ -#endif - ) { - *p++ = '-'; - d = -d; - } - - /* Check for Infinity and NaN */ - if ((word0(d) & Exp_mask) == Exp_mask) { - strcpy(p, !word1(d) && !(word0(d) & Frac_mask) ? "Infinity" : "NaN"); - return buffer; - } - - /* Locking for Balloc's shared buffers */ - ACQUIRE_DTOA_LOCK(); - - /* Output the integer part of d with the digits in reverse order. */ - pInt = p; - di = fd_floor(d); - if (di <= 4294967295.0) { - uint32 n = (uint32)di; - if (n) - do { - uint32 m = n / base; - digit = n - m*base; - n = m; - JS_ASSERT(digit < (uint32)base); - *p++ = BASEDIGIT(digit); - } while (n); - else *p++ = '0'; - } else { - int32 e; - int32 bits; /* Number of significant bits in di; not used. */ - Bigint *b = d2b(di, &e, &bits); - if (!b) - goto nomem1; - b = lshift(b, e); - if (!b) { - nomem1: - Bfree(b); - RELEASE_DTOA_LOCK(); - free(buffer); - return NULL; - } - do { - digit = divrem(b, base); - JS_ASSERT(digit < (uint32)base); - *p++ = BASEDIGIT(digit); - } while (b->wds); - Bfree(b); - } - /* Reverse the digits of the integer part of d. */ - q = p-1; - while (q > pInt) { - char ch = *pInt; - *pInt++ = *q; - *q-- = ch; - } - - df = d - di; - if (df != 0.0) { - /* We have a fraction. */ - int32 e, bbits, s2, done; - Bigint *b, *s, *mlo, *mhi; - - b = s = mlo = mhi = NULL; - - *p++ = '.'; - b = d2b(df, &e, &bbits); - if (!b) { - nomem2: - Bfree(b); - Bfree(s); - if (mlo != mhi) - Bfree(mlo); - Bfree(mhi); - RELEASE_DTOA_LOCK(); - free(buffer); - return NULL; - } - JS_ASSERT(e < 0); - /* At this point df = b * 2^e. e must be less than zero because 0 < df < 1. */ - - s2 = -(int32)(word0(d) >> Exp_shift1 & Exp_mask>>Exp_shift1); -#ifndef Sudden_Underflow - if (!s2) - s2 = -1; -#endif - s2 += Bias + P; - /* 1/2^s2 = (nextDouble(d) - d)/2 */ - JS_ASSERT(-s2 < e); - mlo = i2b(1); - if (!mlo) - goto nomem2; - mhi = mlo; - if (!word1(d) && !(word0(d) & Bndry_mask) -#ifndef Sudden_Underflow - && word0(d) & (Exp_mask & Exp_mask << 1) -#endif - ) { - /* The special case. Here we want to be within a quarter of the last input - significant digit instead of one half of it when the output string's value is less than d. */ - s2 += Log2P; - mhi = i2b(1< df = b/2^s2 > 0; - * (d - prevDouble(d))/2 = mlo/2^s2; - * (nextDouble(d) - d)/2 = mhi/2^s2. */ - - done = JS_FALSE; - do { - int32 j, j1; - Bigint *delta; - - b = multadd(b, base, 0); - if (!b) - goto nomem2; - digit = quorem2(b, s2); - if (mlo == mhi) { - mlo = mhi = multadd(mlo, base, 0); - if (!mhi) - goto nomem2; - } - else { - mlo = multadd(mlo, base, 0); - if (!mlo) - goto nomem2; - mhi = multadd(mhi, base, 0); - if (!mhi) - goto nomem2; - } - - /* Do we yet have the shortest string that will round to d? */ - j = cmp(b, mlo); - /* j is b/2^s2 compared with mlo/2^s2. */ - delta = diff(s, mhi); - if (!delta) - goto nomem2; - j1 = delta->sign ? 1 : cmp(b, delta); - Bfree(delta); - /* j1 is b/2^s2 compared with 1 - mhi/2^s2. */ - -#ifndef ROUND_BIASED - if (j1 == 0 && !(word1(d) & 1)) { - if (j > 0) - digit++; - done = JS_TRUE; - } else -#endif - if (j < 0 || (j == 0 -#ifndef ROUND_BIASED - && !(word1(d) & 1) -#endif - )) { - if (j1 > 0) { - /* Either dig or dig+1 would work here as the least significant digit. - Use whichever would produce an output value closer to d. */ - b = lshift(b, 1); - if (!b) - goto nomem2; - j1 = cmp(b, s); - if (j1 > 0) /* The even test (|| (j1 == 0 && (digit & 1))) is not here because it messes up odd base output - * such as 3.5 in base 3. */ - digit++; - } - done = JS_TRUE; - } else if (j1 > 0) { - digit++; - done = JS_TRUE; - } - JS_ASSERT(digit < (uint32)base); - *p++ = BASEDIGIT(digit); - } while (!done); - Bfree(b); - Bfree(s); - if (mlo != mhi) - Bfree(mlo); - Bfree(mhi); - } - JS_ASSERT(p < buffer + DTOBASESTR_BUFFER_SIZE); - *p = '\0'; - RELEASE_DTOA_LOCK(); - } - return buffer; -} diff --git a/spidermonkey/src/jsdtoa.h b/spidermonkey/src/jsdtoa.h deleted file mode 100644 index 409f454..0000000 --- a/spidermonkey/src/jsdtoa.h +++ /dev/null @@ -1,130 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsdtoa_h___ -#define jsdtoa_h___ -/* - * Public interface to portable double-precision floating point to string - * and back conversion package. - */ - -#include "jscompat.h" - -JS_BEGIN_EXTERN_C - -/* - * JS_strtod() returns as a double-precision floating-point number - * the value represented by the character string pointed to by - * s00. The string is scanned up to the first unrecognized - * character. - * If the value of se is not (char **)NULL, a pointer to - * the character terminating the scan is returned in the location pointed - * to by se. If no number can be formed, se is set to s00r, and - * zero is returned. - * - * *err is set to zero on success; it's set to JS_DTOA_ERANGE on range - * errors and JS_DTOA_ENOMEM on memory failure. - */ -#define JS_DTOA_ERANGE 1 -#define JS_DTOA_ENOMEM 2 -JS_FRIEND_API(double) -JS_strtod(const char *s00, char **se, int *err); - -/* - * Modes for converting floating-point numbers to strings. - * - * Some of the modes can round-trip; this means that if the number is converted to - * a string using one of these mode and then converted back to a number, the result - * will be identical to the original number (except that, due to ECMA, -0 will get converted - * to +0). These round-trip modes return the minimum number of significand digits that - * permit the round trip. - * - * Some of the modes take an integer parameter . - */ -/* NB: Keep this in sync with number_constants[]. */ -typedef enum JSDToStrMode { - DTOSTR_STANDARD, /* Either fixed or exponential format; round-trip */ - DTOSTR_STANDARD_EXPONENTIAL, /* Always exponential format; round-trip */ - DTOSTR_FIXED, /* Round to digits after the decimal point; exponential if number is large */ - DTOSTR_EXPONENTIAL, /* Always exponential format; significant digits */ - DTOSTR_PRECISION /* Either fixed or exponential format; significant digits */ -} JSDToStrMode; - - -/* Maximum number of characters (including trailing null) that a DTOSTR_STANDARD or DTOSTR_STANDARD_EXPONENTIAL - * conversion can produce. This maximum is reached for a number like -0.0000012345678901234567. */ -#define DTOSTR_STANDARD_BUFFER_SIZE 26 - -/* Maximum number of characters (including trailing null) that one of the other conversions - * can produce. This maximum is reached for TO_FIXED, which can generate up to 21 digits before the decimal point. */ -#define DTOSTR_VARIABLE_BUFFER_SIZE(precision) ((precision)+24 > DTOSTR_STANDARD_BUFFER_SIZE ? (precision)+24 : DTOSTR_STANDARD_BUFFER_SIZE) - -/* - * Convert dval according to the given mode and return a pointer to the resulting ASCII string. - * The result is held somewhere in buffer, but not necessarily at the beginning. The size of - * buffer is given in bufferSize, and must be at least as large as given by the above macros. - * - * Return NULL if out of memory. - */ -JS_FRIEND_API(char *) -JS_dtostr(char *buffer, size_t bufferSize, JSDToStrMode mode, int precision, double dval); - -/* - * Convert d to a string in the given base. The integral part of d will be printed exactly - * in that base, regardless of how large it is, because there is no exponential notation for non-base-ten - * numbers. The fractional part will be rounded to as few digits as possible while still preserving - * the round-trip property (analogous to that of printing decimal numbers). In other words, if one were - * to read the resulting string in via a hypothetical base-number-reading routine that rounds to the nearest - * IEEE double (and to an even significand if there are two equally near doubles), then the result would - * equal d (except for -0.0, which converts to "0", and NaN, which is not equal to itself). - * - * Return NULL if out of memory. If the result is not NULL, it must be released via free(). - */ -JS_FRIEND_API(char *) -JS_dtobasestr(int base, double d); - -/* - * Clean up any persistent RAM allocated during the execution of DtoA - * routines, and remove any locks that might have been created. - */ -extern void js_FinishDtoa(void); - -JS_END_EXTERN_C - -#endif /* jsdtoa_h___ */ diff --git a/spidermonkey/src/jsemit.c b/spidermonkey/src/jsemit.c deleted file mode 100644 index f8a06be..0000000 --- a/spidermonkey/src/jsemit.c +++ /dev/null @@ -1,6845 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS bytecode generation. - */ -#include "jsstddef.h" -#ifdef HAVE_MEMORY_H -#include -#endif -#include -#include "jstypes.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ -#include "jsbit.h" -#include "jsprf.h" -#include "jsapi.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsemit.h" -#include "jsfun.h" -#include "jsnum.h" -#include "jsopcode.h" -#include "jsparse.h" -#include "jsregexp.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" - -/* Allocation chunk counts, must be powers of two in general. */ -#define BYTECODE_CHUNK 256 /* code allocation increment */ -#define SRCNOTE_CHUNK 64 /* initial srcnote allocation increment */ -#define TRYNOTE_CHUNK 64 /* trynote allocation increment */ - -/* Macros to compute byte sizes from typed element counts. */ -#define BYTECODE_SIZE(n) ((n) * sizeof(jsbytecode)) -#define SRCNOTE_SIZE(n) ((n) * sizeof(jssrcnote)) -#define TRYNOTE_SIZE(n) ((n) * sizeof(JSTryNote)) - -JS_FRIEND_API(JSBool) -js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg, - JSArenaPool *codePool, JSArenaPool *notePool, - const char *filename, uintN lineno, - JSPrincipals *principals) -{ - memset(cg, 0, sizeof *cg); - TREE_CONTEXT_INIT(&cg->treeContext); - cg->treeContext.flags |= TCF_COMPILING; - cg->codePool = codePool; - cg->notePool = notePool; - cg->codeMark = JS_ARENA_MARK(codePool); - cg->noteMark = JS_ARENA_MARK(notePool); - cg->tempMark = JS_ARENA_MARK(&cx->tempPool); - cg->current = &cg->main; - cg->filename = filename; - cg->firstLine = cg->prolog.currentLine = cg->main.currentLine = lineno; - cg->principals = principals; - ATOM_LIST_INIT(&cg->atomList); - cg->prolog.noteMask = cg->main.noteMask = SRCNOTE_CHUNK - 1; - ATOM_LIST_INIT(&cg->constList); - return JS_TRUE; -} - -JS_FRIEND_API(void) -js_FinishCodeGenerator(JSContext *cx, JSCodeGenerator *cg) -{ - TREE_CONTEXT_FINISH(&cg->treeContext); - JS_ARENA_RELEASE(cg->codePool, cg->codeMark); - JS_ARENA_RELEASE(cg->notePool, cg->noteMark); - JS_ARENA_RELEASE(&cx->tempPool, cg->tempMark); -} - -static ptrdiff_t -EmitCheck(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t delta) -{ - jsbytecode *base, *limit, *next; - ptrdiff_t offset, length; - size_t incr, size; - - base = CG_BASE(cg); - next = CG_NEXT(cg); - limit = CG_LIMIT(cg); - offset = PTRDIFF(next, base, jsbytecode); - if (next + delta > limit) { - length = offset + delta; - length = (length <= BYTECODE_CHUNK) - ? BYTECODE_CHUNK - : JS_BIT(JS_CeilingLog2(length)); - incr = BYTECODE_SIZE(length); - if (!base) { - JS_ARENA_ALLOCATE_CAST(base, jsbytecode *, cg->codePool, incr); - } else { - size = BYTECODE_SIZE(PTRDIFF(limit, base, jsbytecode)); - incr -= size; - JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr); - } - if (!base) { - JS_ReportOutOfMemory(cx); - return -1; - } - CG_BASE(cg) = base; - CG_LIMIT(cg) = base + length; - CG_NEXT(cg) = base + offset; - } - return offset; -} - -static void -UpdateDepth(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t target) -{ - jsbytecode *pc; - const JSCodeSpec *cs; - intN nuses; - - pc = CG_CODE(cg, target); - cs = &js_CodeSpec[pc[0]]; - nuses = cs->nuses; - if (nuses < 0) - nuses = 2 + GET_ARGC(pc); /* stack: fun, this, [argc arguments] */ - cg->stackDepth -= nuses; - JS_ASSERT(cg->stackDepth >= 0); - if (cg->stackDepth < 0) { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%d", target); - JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, - js_GetErrorMessage, NULL, - JSMSG_STACK_UNDERFLOW, - cg->filename ? cg->filename : "stdin", - numBuf); - } - cg->stackDepth += cs->ndefs; - if ((uintN)cg->stackDepth > cg->maxStackDepth) - cg->maxStackDepth = cg->stackDepth; -} - -ptrdiff_t -js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op) -{ - ptrdiff_t offset = EmitCheck(cx, cg, op, 1); - - if (offset >= 0) { - *CG_NEXT(cg)++ = (jsbytecode)op; - UpdateDepth(cx, cg, offset); - } - return offset; -} - -ptrdiff_t -js_Emit2(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1) -{ - ptrdiff_t offset = EmitCheck(cx, cg, op, 2); - - if (offset >= 0) { - jsbytecode *next = CG_NEXT(cg); - next[0] = (jsbytecode)op; - next[1] = op1; - CG_NEXT(cg) = next + 2; - UpdateDepth(cx, cg, offset); - } - return offset; -} - -ptrdiff_t -js_Emit3(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1, - jsbytecode op2) -{ - ptrdiff_t offset = EmitCheck(cx, cg, op, 3); - - if (offset >= 0) { - jsbytecode *next = CG_NEXT(cg); - next[0] = (jsbytecode)op; - next[1] = op1; - next[2] = op2; - CG_NEXT(cg) = next + 3; - UpdateDepth(cx, cg, offset); - } - return offset; -} - -ptrdiff_t -js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra) -{ - ptrdiff_t length = 1 + (ptrdiff_t)extra; - ptrdiff_t offset = EmitCheck(cx, cg, op, length); - - if (offset >= 0) { - jsbytecode *next = CG_NEXT(cg); - *next = (jsbytecode)op; - memset(next + 1, 0, BYTECODE_SIZE(extra)); - CG_NEXT(cg) = next + length; - UpdateDepth(cx, cg, offset); - } - return offset; -} - -/* XXX too many "... statement" L10N gaffes below -- fix via js.msg! */ -const char js_with_statement_str[] = "with statement"; -const char js_finally_block_str[] = "finally block"; -const char js_script_str[] = "script"; - -static const char *statementName[] = { - "label statement", /* LABEL */ - "if statement", /* IF */ - "else statement", /* ELSE */ - "switch statement", /* SWITCH */ - "block", /* BLOCK */ - js_with_statement_str, /* WITH */ - "catch block", /* CATCH */ - "try block", /* TRY */ - js_finally_block_str, /* FINALLY */ - js_finally_block_str, /* SUBROUTINE */ - "do loop", /* DO_LOOP */ - "for loop", /* FOR_LOOP */ - "for/in loop", /* FOR_IN_LOOP */ - "while loop", /* WHILE_LOOP */ -}; - -static const char * -StatementName(JSCodeGenerator *cg) -{ - if (!cg->treeContext.topStmt) - return js_script_str; - return statementName[cg->treeContext.topStmt->type]; -} - -static void -ReportStatementTooLarge(JSContext *cx, JSCodeGenerator *cg) -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEED_DIET, - StatementName(cg)); -} - -/** - Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) - and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided - into unconditional (gotos and gosubs), and conditional jumps or branches - (which pop a value, test it, and jump depending on its value). Most jumps - have just one immediate operand, a signed offset from the jump opcode's pc - to the target bytecode. The lookup and table switch opcodes may contain - many jump offsets. - - Mozilla bug #80981 (http://bugzilla.mozilla.org/show_bug.cgi?id=80981) was - fixed by adding extended "X" counterparts to the opcodes/formats (NB: X is - suffixed to prefer JSOP_ORX thereby avoiding a JSOP_XOR name collision for - the extended form of the JSOP_OR branch opcode). The unextended or short - formats have 16-bit signed immediate offset operands, the extended or long - formats have 32-bit signed immediates. The span-dependency problem consists - of selecting as few long instructions as possible, or about as few -- since - jumps can span other jumps, extending one jump may cause another to need to - be extended. - - Most JS scripts are short, so need no extended jumps. We optimize for this - case by generating short jumps until we know a long jump is needed. After - that point, we keep generating short jumps, but each jump's 16-bit immediate - offset operand is actually an unsigned index into cg->spanDeps, an array of - JSSpanDep structs. Each struct tells the top offset in the script of the - opcode, the "before" offset of the jump (which will be the same as top for - simplex jumps, but which will index further into the bytecode array for a - non-initial jump offset in a lookup or table switch), the after "offset" - adjusted during span-dependent instruction selection (initially the same - value as the "before" offset), and the jump target (more below). - - Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must - ensure that all bytecode generated so far can be inspected to discover where - the jump offset immediate operands lie within CG_CODE(cg). But the bonus is - that we generate span-dependency records sorted by their offsets, so we can - binary-search when trying to find a JSSpanDep for a given bytecode offset, - or the nearest JSSpanDep at or above a given pc. - - To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows - 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This - tells us that we need to binary-search for the cg->spanDeps entry by the - jump opcode's bytecode offset (sd->before). - - Jump targets need to be maintained in a data structure that lets us look - up an already-known target by its address (jumps may have a common target), - and that also lets us update the addresses (script-relative, a.k.a. absolute - offsets) of targets that come after a jump target (for when a jump below - that target needs to be extended). We use an AVL tree, implemented using - recursion, but with some tricky optimizations to its height-balancing code - (see http://www.cmcrossroads.com/bradapp/ftp/src/libs/C++/AvlTrees.html). - - A final wrinkle: backpatch chains are linked by jump-to-jump offsets with - positive sign, even though they link "backward" (i.e., toward lower bytecode - address). We don't want to waste space and search time in the AVL tree for - such temporary backpatch deltas, so we use a single-bit wildcard scheme to - tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas - in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known - target, or is still awaiting backpatching. - - Note that backpatch chains would present a problem for BuildSpanDepTable, - which inspects bytecode to build cg->spanDeps on demand, when the first - short jump offset overflows. To solve this temporary problem, we emit a - proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_POP for branch ops) whose - nuses/ndefs counts help keep the stack balanced, but whose opcode format - distinguishes its backpatch delta immediate operand from a normal jump - offset. - */ -static int -BalanceJumpTargets(JSJumpTarget **jtp) -{ - JSJumpTarget *jt, *jt2, *root; - int dir, otherDir, heightChanged; - JSBool doubleRotate; - - jt = *jtp; - JS_ASSERT(jt->balance != 0); - - if (jt->balance < -1) { - dir = JT_RIGHT; - doubleRotate = (jt->kids[JT_LEFT]->balance > 0); - } else if (jt->balance > 1) { - dir = JT_LEFT; - doubleRotate = (jt->kids[JT_RIGHT]->balance < 0); - } else { - return 0; - } - - otherDir = JT_OTHER_DIR(dir); - if (doubleRotate) { - jt2 = jt->kids[otherDir]; - *jtp = root = jt2->kids[dir]; - - jt->kids[otherDir] = root->kids[dir]; - root->kids[dir] = jt; - - jt2->kids[dir] = root->kids[otherDir]; - root->kids[otherDir] = jt2; - - heightChanged = 1; - root->kids[JT_LEFT]->balance = -JS_MAX(root->balance, 0); - root->kids[JT_RIGHT]->balance = -JS_MIN(root->balance, 0); - root->balance = 0; - } else { - *jtp = root = jt->kids[otherDir]; - jt->kids[otherDir] = root->kids[dir]; - root->kids[dir] = jt; - - heightChanged = (root->balance != 0); - jt->balance = -((dir == JT_LEFT) ? --root->balance : ++root->balance); - } - - return heightChanged; -} - -typedef struct AddJumpTargetArgs { - JSContext *cx; - JSCodeGenerator *cg; - ptrdiff_t offset; - JSJumpTarget *node; -} AddJumpTargetArgs; - -static int -AddJumpTarget(AddJumpTargetArgs *args, JSJumpTarget **jtp) -{ - JSJumpTarget *jt; - int balanceDelta; - - jt = *jtp; - if (!jt) { - JSCodeGenerator *cg = args->cg; - - jt = cg->jtFreeList; - if (jt) { - cg->jtFreeList = jt->kids[JT_LEFT]; - } else { - JS_ARENA_ALLOCATE_CAST(jt, JSJumpTarget *, &args->cx->tempPool, - sizeof *jt); - if (!jt) { - JS_ReportOutOfMemory(args->cx); - return 0; - } - } - jt->offset = args->offset; - jt->balance = 0; - jt->kids[JT_LEFT] = jt->kids[JT_RIGHT] = NULL; - cg->numJumpTargets++; - args->node = jt; - *jtp = jt; - return 1; - } - - if (jt->offset == args->offset) { - args->node = jt; - return 0; - } - - if (args->offset < jt->offset) - balanceDelta = -AddJumpTarget(args, &jt->kids[JT_LEFT]); - else - balanceDelta = AddJumpTarget(args, &jt->kids[JT_RIGHT]); - if (!args->node) - return 0; - - jt->balance += balanceDelta; - return (balanceDelta && jt->balance) - ? 1 - BalanceJumpTargets(jtp) - : 0; -} - -#ifdef DEBUG_brendan -static int AVLCheck(JSJumpTarget *jt) -{ - int lh, rh; - - if (!jt) return 0; - JS_ASSERT(-1 <= jt->balance && jt->balance <= 1); - lh = AVLCheck(jt->kids[JT_LEFT]); - rh = AVLCheck(jt->kids[JT_RIGHT]); - JS_ASSERT(jt->balance == rh - lh); - return 1 + JS_MAX(lh, rh); -} -#endif - -static JSBool -SetSpanDepTarget(JSContext *cx, JSCodeGenerator *cg, JSSpanDep *sd, - ptrdiff_t off) -{ - AddJumpTargetArgs args; - - if (off < JUMPX_OFFSET_MIN || JUMPX_OFFSET_MAX < off) { - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } - - args.cx = cx; - args.cg = cg; - args.offset = sd->top + off; - args.node = NULL; - AddJumpTarget(&args, &cg->jumpTargets); - if (!args.node) - return JS_FALSE; - -#ifdef DEBUG_brendan - AVLCheck(cg->jumpTargets); -#endif - - SD_SET_TARGET(sd, args.node); - return JS_TRUE; -} - -#define SPANDEPS_MIN 256 -#define SPANDEPS_SIZE(n) ((n) * sizeof(JSSpanDep)) -#define SPANDEPS_SIZE_MIN SPANDEPS_SIZE(SPANDEPS_MIN) - -static JSBool -AddSpanDep(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, jsbytecode *pc2, - ptrdiff_t off) -{ - uintN index; - JSSpanDep *sdbase, *sd; - size_t size; - - index = cg->numSpanDeps; - if (index + 1 == 0) { - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } - - if ((index & (index - 1)) == 0 && - (!(sdbase = cg->spanDeps) || index >= SPANDEPS_MIN)) { - if (!sdbase) { - size = SPANDEPS_SIZE_MIN; - JS_ARENA_ALLOCATE_CAST(sdbase, JSSpanDep *, &cx->tempPool, size); - } else { - size = SPANDEPS_SIZE(index); - JS_ARENA_GROW_CAST(sdbase, JSSpanDep *, &cx->tempPool, size, size); - } - if (!sdbase) - return JS_FALSE; - cg->spanDeps = sdbase; - } - - cg->numSpanDeps = index + 1; - sd = cg->spanDeps + index; - sd->top = PTRDIFF(pc, CG_BASE(cg), jsbytecode); - sd->offset = sd->before = PTRDIFF(pc2, CG_BASE(cg), jsbytecode); - - if (js_CodeSpec[*pc].format & JOF_BACKPATCH) { - /* Jump offset will be backpatched if off is a non-zero "bpdelta". */ - if (off != 0) { - JS_ASSERT(off >= 1 + JUMP_OFFSET_LEN); - if (off > BPDELTA_MAX) { - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } - } - SD_SET_BPDELTA(sd, off); - } else if (off == 0) { - /* Jump offset will be patched directly, without backpatch chaining. */ - SD_SET_TARGET(sd, NULL); - } else { - /* The jump offset in off is non-zero, therefore it's already known. */ - if (!SetSpanDepTarget(cx, cg, sd, off)) - return JS_FALSE; - } - - if (index > SPANDEP_INDEX_MAX) - index = SPANDEP_INDEX_HUGE; - SET_SPANDEP_INDEX(pc2, index); - return JS_TRUE; -} - -static JSBool -BuildSpanDepTable(JSContext *cx, JSCodeGenerator *cg) -{ - jsbytecode *pc, *end; - JSOp op; - const JSCodeSpec *cs; - ptrdiff_t len, off; - - pc = CG_BASE(cg) + cg->spanDepTodo; - end = CG_NEXT(cg); - while (pc < end) { - op = (JSOp)*pc; - cs = &js_CodeSpec[op]; - len = (ptrdiff_t)cs->length; - - switch (cs->format & JOF_TYPEMASK) { - case JOF_JUMP: - off = GET_JUMP_OFFSET(pc); - if (!AddSpanDep(cx, cg, pc, pc, off)) - return JS_FALSE; - break; - - case JOF_TABLESWITCH: - { - jsbytecode *pc2; - jsint i, low, high; - - pc2 = pc; - off = GET_JUMP_OFFSET(pc2); - if (!AddSpanDep(cx, cg, pc, pc2, off)) - return JS_FALSE; - pc2 += JUMP_OFFSET_LEN; - low = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - high = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - for (i = low; i <= high; i++) { - off = GET_JUMP_OFFSET(pc2); - if (!AddSpanDep(cx, cg, pc, pc2, off)) - return JS_FALSE; - pc2 += JUMP_OFFSET_LEN; - } - len = 1 + pc2 - pc; - break; - } - - case JOF_LOOKUPSWITCH: - { - jsbytecode *pc2; - jsint npairs; - - pc2 = pc; - off = GET_JUMP_OFFSET(pc2); - if (!AddSpanDep(cx, cg, pc, pc2, off)) - return JS_FALSE; - pc2 += JUMP_OFFSET_LEN; - npairs = (jsint) GET_ATOM_INDEX(pc2); - pc2 += ATOM_INDEX_LEN; - while (npairs) { - pc2 += ATOM_INDEX_LEN; - off = GET_JUMP_OFFSET(pc2); - if (!AddSpanDep(cx, cg, pc, pc2, off)) - return JS_FALSE; - pc2 += JUMP_OFFSET_LEN; - npairs--; - } - len = 1 + pc2 - pc; - break; - } - } - - JS_ASSERT(len > 0); - pc += len; - } - - return JS_TRUE; -} - -static JSSpanDep * -GetSpanDep(JSCodeGenerator *cg, jsbytecode *pc) -{ - uintN index; - ptrdiff_t offset; - int lo, hi, mid; - JSSpanDep *sd; - - index = GET_SPANDEP_INDEX(pc); - if (index != SPANDEP_INDEX_HUGE) - return cg->spanDeps + index; - - offset = PTRDIFF(pc, CG_BASE(cg), jsbytecode); - lo = 0; - hi = cg->numSpanDeps - 1; - while (lo <= hi) { - mid = (lo + hi) / 2; - sd = cg->spanDeps + mid; - if (sd->before == offset) - return sd; - if (sd->before < offset) - lo = mid + 1; - else - hi = mid - 1; - } - - JS_ASSERT(0); - return NULL; -} - -static JSBool -SetBackPatchDelta(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, - ptrdiff_t delta) -{ - JSSpanDep *sd; - - JS_ASSERT(delta >= 1 + JUMP_OFFSET_LEN); - if (!cg->spanDeps && delta < JUMP_OFFSET_MAX) { - SET_JUMP_OFFSET(pc, delta); - return JS_TRUE; - } - - if (delta > BPDELTA_MAX) { - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } - - if (!cg->spanDeps && !BuildSpanDepTable(cx, cg)) - return JS_FALSE; - - sd = GetSpanDep(cg, pc); - JS_ASSERT(SD_GET_BPDELTA(sd) == 0); - SD_SET_BPDELTA(sd, delta); - return JS_TRUE; -} - -static void -UpdateJumpTargets(JSJumpTarget *jt, ptrdiff_t pivot, ptrdiff_t delta) -{ - if (jt->offset > pivot) { - jt->offset += delta; - if (jt->kids[JT_LEFT]) - UpdateJumpTargets(jt->kids[JT_LEFT], pivot, delta); - } - if (jt->kids[JT_RIGHT]) - UpdateJumpTargets(jt->kids[JT_RIGHT], pivot, delta); -} - -static JSSpanDep * -FindNearestSpanDep(JSCodeGenerator *cg, ptrdiff_t offset, int lo, - JSSpanDep *guard) -{ - int num, hi, mid; - JSSpanDep *sdbase, *sd; - - num = cg->numSpanDeps; - JS_ASSERT(num > 0); - hi = num - 1; - sdbase = cg->spanDeps; - while (lo <= hi) { - mid = (lo + hi) / 2; - sd = sdbase + mid; - if (sd->before == offset) - return sd; - if (sd->before < offset) - lo = mid + 1; - else - hi = mid - 1; - } - if (lo == num) - return guard; - sd = sdbase + lo; - JS_ASSERT(sd->before >= offset && (lo == 0 || sd[-1].before < offset)); - return sd; -} - -static void -FreeJumpTargets(JSCodeGenerator *cg, JSJumpTarget *jt) -{ - if (jt->kids[JT_LEFT]) - FreeJumpTargets(cg, jt->kids[JT_LEFT]); - if (jt->kids[JT_RIGHT]) - FreeJumpTargets(cg, jt->kids[JT_RIGHT]); - jt->kids[JT_LEFT] = cg->jtFreeList; - cg->jtFreeList = jt; -} - -static JSBool -OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg) -{ - jsbytecode *pc, *oldpc, *base, *limit, *next; - JSSpanDep *sd, *sd2, *sdbase, *sdlimit, *sdtop, guard; - ptrdiff_t offset, growth, delta, top, pivot, span, length, target; - JSBool done; - JSOp op; - uint32 type; - size_t size, incr; - jssrcnote *sn, *snlimit; - JSSrcNoteSpec *spec; - uintN i, n, noteIndex; - JSTryNote *tn, *tnlimit; -#ifdef DEBUG_brendan - int passes = 0; -#endif - - base = CG_BASE(cg); - sdbase = cg->spanDeps; - sdlimit = sdbase + cg->numSpanDeps; - offset = CG_OFFSET(cg); - growth = 0; - - do { - done = JS_TRUE; - delta = 0; - top = pivot = -1; - sdtop = NULL; - pc = NULL; - op = JSOP_NOP; - type = 0; -#ifdef DEBUG_brendan - passes++; -#endif - - for (sd = sdbase; sd < sdlimit; sd++) { - JS_ASSERT(JT_HAS_TAG(sd->target)); - sd->offset += delta; - - if (sd->top != top) { - sdtop = sd; - top = sd->top; - JS_ASSERT(top == sd->before); - pivot = sd->offset; - pc = base + top; - op = (JSOp) *pc; - type = (js_CodeSpec[op].format & JOF_TYPEMASK); - if (JOF_TYPE_IS_EXTENDED_JUMP(type)) { - /* - * We already extended all the jump offset operands for - * the opcode at sd->top. Jumps and branches have only - * one jump offset operand, but switches have many, all - * of which are adjacent in cg->spanDeps. - */ - continue; - } - - JS_ASSERT(type == JOF_JUMP || - type == JOF_TABLESWITCH || - type == JOF_LOOKUPSWITCH); - } - - if (!JOF_TYPE_IS_EXTENDED_JUMP(type)) { - span = SD_SPAN(sd, pivot); - if (span < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < span) { - ptrdiff_t deltaFromTop = 0; - - done = JS_FALSE; - - switch (op) { - case JSOP_GOTO: op = JSOP_GOTOX; break; - case JSOP_IFEQ: op = JSOP_IFEQX; break; - case JSOP_IFNE: op = JSOP_IFNEX; break; - case JSOP_OR: op = JSOP_ORX; break; - case JSOP_AND: op = JSOP_ANDX; break; - case JSOP_GOSUB: op = JSOP_GOSUBX; break; - case JSOP_CASE: op = JSOP_CASEX; break; - case JSOP_DEFAULT: op = JSOP_DEFAULTX; break; - case JSOP_TABLESWITCH: op = JSOP_TABLESWITCHX; break; - case JSOP_LOOKUPSWITCH: op = JSOP_LOOKUPSWITCHX; break; - default: - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } - *pc = (jsbytecode) op; - - for (sd2 = sdtop; sd2 < sdlimit && sd2->top == top; sd2++) { - if (sd2 <= sd) { - /* - * sd2->offset already includes delta as it stood - * before we entered this loop, but it must also - * include the delta relative to top due to all the - * extended jump offset immediates for the opcode - * starting at top, which we extend in this loop. - * - * If there is only one extended jump offset, then - * sd2->offset won't change and this for loop will - * iterate once only. - */ - sd2->offset += deltaFromTop; - deltaFromTop += JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN; - } else { - /* - * sd2 comes after sd, and won't be revisited by - * the outer for loop, so we have to increase its - * offset by delta, not merely by deltaFromTop. - */ - sd2->offset += delta; - } - - delta += JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN; - UpdateJumpTargets(cg->jumpTargets, sd2->offset, - JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN); - } - sd = sd2 - 1; - } - } - } - - growth += delta; - } while (!done); - - if (growth) { -#ifdef DEBUG_brendan - printf("%s:%u: %u/%u jumps extended in %d passes (%d=%d+%d)\n", - cg->filename ? cg->filename : "stdin", cg->firstLine, - growth / (JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN), cg->numSpanDeps, - passes, offset + growth, offset, growth); -#endif - - /* - * Ensure that we have room for the extended jumps, but don't round up - * to a power of two -- we're done generating code, so we cut to fit. - */ - limit = CG_LIMIT(cg); - length = offset + growth; - next = base + length; - if (next > limit) { - JS_ASSERT(length > BYTECODE_CHUNK); - size = BYTECODE_SIZE(PTRDIFF(limit, base, jsbytecode)); - incr = BYTECODE_SIZE(length) - size; - JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr); - if (!base) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - CG_BASE(cg) = base; - CG_LIMIT(cg) = next = base + length; - } - CG_NEXT(cg) = next; - - /* - * Set up a fake span dependency record to guard the end of the code - * being generated. This guard record is returned as a fencepost by - * FindNearestSpanDep if there is no real spandep at or above a given - * unextended code offset. - */ - guard.top = -1; - guard.offset = offset + growth; - guard.before = offset; - guard.target = NULL; - } - - /* - * Now work backwards through the span dependencies, copying chunks of - * bytecode between each extended jump toward the end of the grown code - * space, and restoring immediate offset operands for all jump bytecodes. - * The first chunk of bytecodes, starting at base and ending at the first - * extended jump offset (NB: this chunk includes the operation bytecode - * just before that immediate jump offset), doesn't need to be copied. - */ - JS_ASSERT(sd == sdlimit); - top = -1; - while (--sd >= sdbase) { - if (sd->top != top) { - top = sd->top; - op = (JSOp) base[top]; - type = (js_CodeSpec[op].format & JOF_TYPEMASK); - - for (sd2 = sd - 1; sd2 >= sdbase && sd2->top == top; sd2--) - continue; - sd2++; - pivot = sd2->offset; - JS_ASSERT(top == sd2->before); - } - - oldpc = base + sd->before; - span = SD_SPAN(sd, pivot); - - /* - * If this jump didn't need to be extended, restore its span immediate - * offset operand now, overwriting the index of sd within cg->spanDeps - * that was stored temporarily after *pc when BuildSpanDepTable ran. - * - * Note that span might fit in 16 bits even for an extended jump op, - * if the op has multiple span operands, not all of which overflowed - * (e.g. JSOP_LOOKUPSWITCH or JSOP_TABLESWITCH where some cases are in - * range for a short jump, but others are not). - */ - if (!JOF_TYPE_IS_EXTENDED_JUMP(type)) { - JS_ASSERT(JUMP_OFFSET_MIN <= span && span <= JUMP_OFFSET_MAX); - SET_JUMP_OFFSET(oldpc, span); - continue; - } - - /* - * Set up parameters needed to copy the next run of bytecode starting - * at offset (which is a cursor into the unextended, original bytecode - * vector), down to sd->before (a cursor of the same scale as offset, - * it's the index of the original jump pc). Reuse delta to count the - * nominal number of bytes to copy. - */ - pc = base + sd->offset; - delta = offset - sd->before; - JS_ASSERT(delta >= 1 + JUMP_OFFSET_LEN); - - /* - * Don't bother copying the jump offset we're about to reset, but do - * copy the bytecode at oldpc (which comes just before its immediate - * jump offset operand), on the next iteration through the loop, by - * including it in offset's new value. - */ - offset = sd->before + 1; - size = BYTECODE_SIZE(delta - (1 + JUMP_OFFSET_LEN)); - if (size) { - memmove(pc + 1 + JUMPX_OFFSET_LEN, - oldpc + 1 + JUMP_OFFSET_LEN, - size); - } - - SET_JUMPX_OFFSET(pc, span); - } - - if (growth) { - /* - * Fix source note deltas. Don't hardwire the delta fixup adjustment, - * even though currently it must be JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN - * at each sd that moved. The future may bring different offset sizes - * for span-dependent instruction operands. However, we fix only main - * notes here, not prolog notes -- we know that prolog opcodes are not - * span-dependent, and aren't likely ever to be. - */ - offset = growth = 0; - sd = sdbase; - for (sn = cg->main.notes, snlimit = sn + cg->main.noteCount; - sn < snlimit; - sn = SN_NEXT(sn)) { - /* - * Recall that the offset of a given note includes its delta, and - * tells the offset of the annotated bytecode from the main entry - * point of the script. - */ - offset += SN_DELTA(sn); - while (sd < sdlimit && sd->before < offset) { - /* - * To compute the delta to add to sn, we need to look at the - * spandep after sd, whose offset - (before + growth) tells by - * how many bytes sd's instruction grew. - */ - sd2 = sd + 1; - if (sd2 == sdlimit) - sd2 = &guard; - delta = sd2->offset - (sd2->before + growth); - if (delta > 0) { - JS_ASSERT(delta == JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN); - sn = js_AddToSrcNoteDelta(cx, cg, sn, delta); - if (!sn) - return JS_FALSE; - snlimit = cg->main.notes + cg->main.noteCount; - growth += delta; - } - sd++; - } - - /* - * If sn has span-dependent offset operands, check whether each - * covers further span-dependencies, and increase those operands - * accordingly. Some source notes measure offset not from the - * annotated pc, but from that pc plus some small bias. NB: we - * assume that spec->offsetBias can't itself span span-dependent - * instructions! - */ - spec = &js_SrcNoteSpec[SN_TYPE(sn)]; - if (spec->isSpanDep) { - pivot = offset + spec->offsetBias; - n = spec->arity; - for (i = 0; i < n; i++) { - span = js_GetSrcNoteOffset(sn, i); - if (span == 0) - continue; - target = pivot + span * spec->isSpanDep; - sd2 = FindNearestSpanDep(cg, target, - (target >= pivot) - ? sd - sdbase - : 0, - &guard); - - /* - * Increase target by sd2's before-vs-after offset delta, - * which is absolute (i.e., relative to start of script, - * as is target). Recompute the span by subtracting its - * adjusted pivot from target. - */ - target += sd2->offset - sd2->before; - span = target - (pivot + growth); - span *= spec->isSpanDep; - noteIndex = sn - cg->main.notes; - if (!js_SetSrcNoteOffset(cx, cg, noteIndex, i, span)) - return JS_FALSE; - sn = cg->main.notes + noteIndex; - snlimit = cg->main.notes + cg->main.noteCount; - } - } - } - cg->main.lastNoteOffset += growth; - - /* - * Fix try/catch notes (O(numTryNotes * log2(numSpanDeps)), but it's - * not clear how we can beat that). - */ - for (tn = cg->tryBase, tnlimit = cg->tryNext; tn < tnlimit; tn++) { - /* - * First, look for the nearest span dependency at/above tn->start. - * There may not be any such spandep, in which case the guard will - * be returned. - */ - offset = tn->start; - sd = FindNearestSpanDep(cg, offset, 0, &guard); - delta = sd->offset - sd->before; - tn->start = offset + delta; - - /* - * Next, find the nearest spandep at/above tn->start + tn->length. - * Use its delta minus tn->start's delta to increase tn->length. - */ - length = tn->length; - sd2 = FindNearestSpanDep(cg, offset + length, sd - sdbase, &guard); - if (sd2 != sd) - tn->length = length + sd2->offset - sd2->before - delta; - - /* - * Finally, adjust tn->catchStart upward only if it is non-zero, - * and provided there are spandeps below it that grew. - */ - offset = tn->catchStart; - if (offset != 0) { - sd = FindNearestSpanDep(cg, offset, sd2 - sdbase, &guard); - tn->catchStart = offset + sd->offset - sd->before; - } - } - } - -#ifdef DEBUG_brendan - { - uintN bigspans = 0; - top = -1; - for (sd = sdbase; sd < sdlimit; sd++) { - offset = sd->offset; - - /* NB: sd->top cursors into the original, unextended bytecode vector. */ - if (sd->top != top) { - JS_ASSERT(top == -1 || - !JOF_TYPE_IS_EXTENDED_JUMP(type) || - bigspans != 0); - bigspans = 0; - top = sd->top; - JS_ASSERT(top == sd->before); - op = (JSOp) base[offset]; - type = (js_CodeSpec[op].format & JOF_TYPEMASK); - JS_ASSERT(type == JOF_JUMP || - type == JOF_JUMPX || - type == JOF_TABLESWITCH || - type == JOF_TABLESWITCHX || - type == JOF_LOOKUPSWITCH || - type == JOF_LOOKUPSWITCHX); - pivot = offset; - } - - pc = base + offset; - if (JOF_TYPE_IS_EXTENDED_JUMP(type)) { - span = GET_JUMPX_OFFSET(pc); - if (span < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < span) { - bigspans++; - } else { - JS_ASSERT(type == JOF_TABLESWITCHX || - type == JOF_LOOKUPSWITCHX); - } - } else { - span = GET_JUMP_OFFSET(pc); - } - JS_ASSERT(SD_SPAN(sd, pivot) == span); - } - JS_ASSERT(!JOF_TYPE_IS_EXTENDED_JUMP(type) || bigspans != 0); - } -#endif - - /* - * Reset so we optimize at most once -- cg may be used for further code - * generation of successive, independent, top-level statements. No jump - * can span top-level statements, because JS lacks goto. - */ - size = SPANDEPS_SIZE(JS_BIT(JS_CeilingLog2(cg->numSpanDeps))); - JS_ArenaFreeAllocation(&cx->tempPool, cg->spanDeps, - JS_MAX(size, SPANDEPS_SIZE_MIN)); - cg->spanDeps = NULL; - FreeJumpTargets(cg, cg->jumpTargets); - cg->jumpTargets = NULL; - cg->numSpanDeps = cg->numJumpTargets = 0; - cg->spanDepTodo = CG_OFFSET(cg); - return JS_TRUE; -} - -static JSBool -EmitJump(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t off) -{ - JSBool extend; - ptrdiff_t jmp; - jsbytecode *pc; - - extend = off < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < off; - if (extend && !cg->spanDeps && !BuildSpanDepTable(cx, cg)) - return JS_FALSE; - - jmp = js_Emit3(cx, cg, op, JUMP_OFFSET_HI(off), JUMP_OFFSET_LO(off)); - if (jmp >= 0 && (extend || cg->spanDeps)) { - pc = CG_CODE(cg, jmp); - if (!AddSpanDep(cx, cg, pc, pc, off)) - return JS_FALSE; - } - return jmp; -} - -static ptrdiff_t -GetJumpOffset(JSCodeGenerator *cg, jsbytecode *pc) -{ - JSSpanDep *sd; - JSJumpTarget *jt; - ptrdiff_t top; - - if (!cg->spanDeps) - return GET_JUMP_OFFSET(pc); - - sd = GetSpanDep(cg, pc); - jt = sd->target; - if (!JT_HAS_TAG(jt)) - return JT_TO_BPDELTA(jt); - - top = sd->top; - while (--sd >= cg->spanDeps && sd->top == top) - continue; - sd++; - return JT_CLR_TAG(jt)->offset - sd->offset; -} - -JSBool -js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, - ptrdiff_t off) -{ - if (!cg->spanDeps) { - if (JUMP_OFFSET_MIN <= off && off <= JUMP_OFFSET_MAX) { - SET_JUMP_OFFSET(pc, off); - return JS_TRUE; - } - - if (!BuildSpanDepTable(cx, cg)) - return JS_FALSE; - } - - return SetSpanDepTarget(cx, cg, GetSpanDep(cg, pc), off); -} - -JSBool -js_InStatement(JSTreeContext *tc, JSStmtType type) -{ - JSStmtInfo *stmt; - - for (stmt = tc->topStmt; stmt; stmt = stmt->down) { - if (stmt->type == type) - return JS_TRUE; - } - return JS_FALSE; -} - -JSBool -js_IsGlobalReference(JSTreeContext *tc, JSAtom *atom, JSBool *loopyp) -{ - JSStmtInfo *stmt; - JSObject *obj; - JSScope *scope; - - *loopyp = JS_FALSE; - for (stmt = tc->topStmt; stmt; stmt = stmt->down) { - if (stmt->type == STMT_WITH) - return JS_FALSE; - if (STMT_IS_LOOP(stmt)) { - *loopyp = JS_TRUE; - continue; - } - if (stmt->flags & SIF_SCOPE) { - obj = ATOM_TO_OBJECT(stmt->atom); - JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass); - scope = OBJ_SCOPE(obj); - if (SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom))) - return JS_FALSE; - } - } - return JS_TRUE; -} - -void -js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type, - ptrdiff_t top) -{ - stmt->type = type; - stmt->flags = 0; - SET_STATEMENT_TOP(stmt, top); - stmt->atom = NULL; - stmt->down = tc->topStmt; - tc->topStmt = stmt; - if (STMT_LINKS_SCOPE(stmt)) { - stmt->downScope = tc->topScopeStmt; - tc->topScopeStmt = stmt; - } else { - stmt->downScope = NULL; - } -} - -void -js_PushBlockScope(JSTreeContext *tc, JSStmtInfo *stmt, JSAtom *blockAtom, - ptrdiff_t top) -{ - JSObject *blockObj; - - js_PushStatement(tc, stmt, STMT_BLOCK, top); - stmt->flags |= SIF_SCOPE; - blockObj = ATOM_TO_OBJECT(blockAtom); - blockObj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(tc->blockChain); - stmt->downScope = tc->topScopeStmt; - tc->topScopeStmt = stmt; - tc->blockChain = blockObj; - stmt->atom = blockAtom; -} - -/* - * Emit a backpatch op with offset pointing to the previous jump of this type, - * so that we can walk back up the chain fixing up the op and jump offset. - */ -static ptrdiff_t -EmitBackPatchOp(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t *lastp) -{ - ptrdiff_t offset, delta; - - offset = CG_OFFSET(cg); - delta = offset - *lastp; - *lastp = offset; - JS_ASSERT(delta > 0); - return EmitJump(cx, cg, op, delta); -} - -/* - * Macro to emit a bytecode followed by a uint16 immediate operand stored in - * big-endian order, used for arg and var numbers as well as for atomIndexes. - * NB: We use cx and cg from our caller's lexical environment, and return - * false on error. - */ -#define EMIT_UINT16_IMM_OP(op, i) \ - JS_BEGIN_MACRO \ - if (js_Emit3(cx, cg, op, UINT16_HI(i), UINT16_LO(i)) < 0) \ - return JS_FALSE; \ - JS_END_MACRO - -/* Emit additional bytecode(s) for non-local jumps. */ -static JSBool -EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, - JSOp *returnop) -{ - intN depth; - JSStmtInfo *stmt; - ptrdiff_t jmp; - - /* - * Return from within a try block that has a finally clause must be split - * into two ops: JSOP_SETRVAL, to pop the r.v. and store it in fp->rval; - * and JSOP_RETRVAL, which makes control flow go back to the caller, who - * picks up fp->rval as usual. Otherwise, the stack will be unbalanced - * when executing the finally clause. - * - * We mutate *returnop once only if we find an enclosing try-block (viz, - * STMT_FINALLY) to ensure that we emit just one JSOP_SETRVAL before one - * or more JSOP_GOSUBs and other fixup opcodes emitted by this function. - * Our caller (the TOK_RETURN case of js_EmitTree) then emits *returnop. - * The fixup opcodes and gosubs must interleave in the proper order, from - * inner statement to outer, so that finally clauses run at the correct - * stack depth. - */ - if (returnop) { - JS_ASSERT(*returnop == JSOP_RETURN); - for (stmt = cg->treeContext.topStmt; stmt != toStmt; - stmt = stmt->down) { - if (stmt->type == STMT_FINALLY || - ((cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT) && - STMT_MAYBE_SCOPE(stmt))) { - if (js_Emit1(cx, cg, JSOP_SETRVAL) < 0) - return JS_FALSE; - *returnop = JSOP_RETRVAL; - break; - } - } - - /* - * If there are no try-with-finally blocks open around this return - * statement, we can generate a return forthwith and skip generating - * any fixup code. - */ - if (*returnop == JSOP_RETURN) - return JS_TRUE; - } - - /* - * The non-local jump fixup we emit will unbalance cg->stackDepth, because - * the fixup replicates balanced code such as JSOP_LEAVEWITH emitted at the - * end of a with statement, so we save cg->stackDepth here and restore it - * just before a successful return. - */ - depth = cg->stackDepth; - for (stmt = cg->treeContext.topStmt; stmt != toStmt; stmt = stmt->down) { - switch (stmt->type) { - case STMT_FINALLY: - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &GOSUBS(*stmt)); - if (jmp < 0) - return JS_FALSE; - break; - - case STMT_WITH: - /* There's a With object on the stack that we need to pop. */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) - return JS_FALSE; - break; - - case STMT_FOR_IN_LOOP: - /* - * The iterator and the object being iterated need to be popped. - */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_ENDITER) < 0) - return JS_FALSE; - break; - - case STMT_SUBROUTINE: - /* - * There's a [exception or hole, retsub pc-index] pair on the - * stack that we need to pop. - */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_POP2) < 0) - return JS_FALSE; - break; - - default:; - } - - if (stmt->flags & SIF_SCOPE) { - uintN i; - - /* There is a Block object with locals on the stack to pop. */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - i = OBJ_BLOCK_COUNT(cx, ATOM_TO_OBJECT(stmt->atom)); - EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, i); - } - } - - cg->stackDepth = depth; - return JS_TRUE; -} - -static ptrdiff_t -EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, - ptrdiff_t *lastp, JSAtomListElement *label, JSSrcNoteType noteType) -{ - intN index; - - if (!EmitNonLocalJumpFixup(cx, cg, toStmt, NULL)) - return -1; - - if (label) - index = js_NewSrcNote2(cx, cg, noteType, (ptrdiff_t) ALE_INDEX(label)); - else if (noteType != SRC_NULL) - index = js_NewSrcNote(cx, cg, noteType); - else - index = 0; - if (index < 0) - return -1; - - return EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, lastp); -} - -static JSBool -BackPatch(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t last, - jsbytecode *target, jsbytecode op) -{ - jsbytecode *pc, *stop; - ptrdiff_t delta, span; - - pc = CG_CODE(cg, last); - stop = CG_CODE(cg, -1); - while (pc != stop) { - delta = GetJumpOffset(cg, pc); - span = PTRDIFF(target, pc, jsbytecode); - CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, span); - - /* - * Set *pc after jump offset in case bpdelta didn't overflow, but span - * does (if so, CHECK_AND_SET_JUMP_OFFSET might call BuildSpanDepTable - * and need to see the JSOP_BACKPATCH* op at *pc). - */ - *pc = op; - pc -= delta; - } - return JS_TRUE; -} - -void -js_PopStatement(JSTreeContext *tc) -{ - JSStmtInfo *stmt; - JSObject *blockObj; - - stmt = tc->topStmt; - tc->topStmt = stmt->down; - if (STMT_LINKS_SCOPE(stmt)) { - tc->topScopeStmt = stmt->downScope; - if (stmt->flags & SIF_SCOPE) { - blockObj = ATOM_TO_OBJECT(stmt->atom); - tc->blockChain = JSVAL_TO_OBJECT(blockObj->slots[JSSLOT_PARENT]); - } - } -} - -JSBool -js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg) -{ - JSStmtInfo *stmt; - - stmt = cg->treeContext.topStmt; - if (!STMT_IS_TRYING(stmt) && - (!BackPatch(cx, cg, stmt->breaks, CG_NEXT(cg), JSOP_GOTO) || - !BackPatch(cx, cg, stmt->continues, CG_CODE(cg, stmt->update), - JSOP_GOTO))) { - return JS_FALSE; - } - js_PopStatement(&cg->treeContext); - return JS_TRUE; -} - -JSBool -js_DefineCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, - JSParseNode *pn) -{ - jsdouble dval; - jsint ival; - JSAtom *valueAtom; - JSAtomListElement *ale; - - /* XXX just do numbers for now */ - if (pn->pn_type == TOK_NUMBER) { - dval = pn->pn_dval; - valueAtom = (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival)) - ? js_AtomizeInt(cx, ival, 0) - : js_AtomizeDouble(cx, dval, 0); - if (!valueAtom) - return JS_FALSE; - ale = js_IndexAtom(cx, atom, &cg->constList); - if (!ale) - return JS_FALSE; - ALE_SET_VALUE(ale, ATOM_KEY(valueAtom)); - } - return JS_TRUE; -} - -JSStmtInfo * -js_LexicalLookup(JSTreeContext *tc, JSAtom *atom, jsint *slotp, JSBool letdecl) -{ - JSStmtInfo *stmt; - JSObject *obj; - JSScope *scope; - JSScopeProperty *sprop; - jsval v; - - for (stmt = tc->topScopeStmt; stmt; stmt = stmt->downScope) { - if (stmt->type == STMT_WITH) { - /* Ignore with statements enclosing a single let declaration. */ - if (letdecl) - continue; - break; - } - - /* Skip "maybe scope" statements that don't contain let bindings. */ - if (!(stmt->flags & SIF_SCOPE)) - continue; - - obj = ATOM_TO_OBJECT(stmt->atom); - JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass); - scope = OBJ_SCOPE(obj); - sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom)); - if (sprop) { - JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); - - if (slotp) { - /* - * Use LOCKED_OBJ_GET_SLOT since we know obj is single- - * threaded and owned by this compiler activation. - */ - v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_BLOCK_DEPTH); - JS_ASSERT(JSVAL_IS_INT(v) && JSVAL_TO_INT(v) >= 0); - *slotp = JSVAL_TO_INT(v) + sprop->shortid; - } - return stmt; - } - } - - if (slotp) - *slotp = -1; - return stmt; -} - -JSBool -js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, - jsval *vp) -{ - JSBool ok; - JSStackFrame *fp; - JSStmtInfo *stmt; - jsint slot; - JSAtomListElement *ale; - JSObject *obj, *pobj; - JSProperty *prop; - uintN attrs; - - /* - * fp chases cg down the stack, but only until we reach the outermost cg. - * This enables propagating consts from top-level into switch cases in a - * function compiled along with the top-level script. All stack frames - * with matching code generators should be flagged with JSFRAME_COMPILING; - * we check sanity here. - */ - *vp = JSVAL_VOID; - ok = JS_TRUE; - fp = cx->fp; - do { - JS_ASSERT(fp->flags & JSFRAME_COMPILING); - - obj = fp->varobj; - if (obj == fp->scopeChain) { - /* XXX this will need revising when 'let const' is added. */ - stmt = js_LexicalLookup(&cg->treeContext, atom, &slot, JS_FALSE); - if (stmt) - return JS_TRUE; - - ATOM_LIST_SEARCH(ale, &cg->constList, atom); - if (ale) { - *vp = ALE_VALUE(ale); - return JS_TRUE; - } - - /* - * Try looking in the variable object for a direct property that - * is readonly and permanent. We know such a property can't be - * shadowed by another property on obj's prototype chain, or a - * with object or catch variable; nor can prop's value be changed, - * nor can prop be deleted. - */ - prop = NULL; - if (OBJ_GET_CLASS(cx, obj) == &js_FunctionClass) { - ok = js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), - &pobj, &prop); - if (!ok) - break; - if (prop) { -#ifdef DEBUG - JSScopeProperty *sprop = (JSScopeProperty *)prop; - - /* - * Any hidden property must be a formal arg or local var, - * which will shadow a global const of the same name. - */ - JS_ASSERT(sprop->getter == js_GetArgument || - sprop->getter == js_GetLocalVariable); -#endif - OBJ_DROP_PROPERTY(cx, pobj, prop); - break; - } - } - - ok = OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop); - if (ok) { - if (pobj == obj && - (fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO))) { - /* - * We're compiling code that will be executed immediately, - * not re-executed against a different scope chain and/or - * variable object. Therefore we can get constant values - * from our variable object here. - */ - ok = OBJ_GET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, - &attrs); - if (ok && !(~attrs & (JSPROP_READONLY | JSPROP_PERMANENT))) - ok = OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); - } - if (prop) - OBJ_DROP_PROPERTY(cx, pobj, prop); - } - if (!ok || prop) - break; - } - fp = fp->down; - } while ((cg = cg->parent) != NULL); - return ok; -} - -/* - * Allocate an index invariant for all activations of the code being compiled - * in cg, that can be used to store and fetch a reference to a cloned RegExp - * object that shares the same JSRegExp private data created for the object - * literal in pn->pn_atom. We need clones to hold lastIndex and other direct - * properties that should not be shared among threads sharing a precompiled - * function or script. - * - * If the code being compiled is function code, allocate a reserved slot in - * the cloned function object that shares its precompiled script with other - * cloned function objects and with the compiler-created clone-parent. There - * are fun->nregexps such reserved slots in each function object cloned from - * fun->object. NB: during compilation, funobj slots must never be allocated, - * because js_AllocSlot could hand out one of the slots that should be given - * to a regexp clone. - * - * If the code being compiled is global code, reserve the fp->vars slot at - * ALE_INDEX(ale), by ensuring that cg->treeContext.numGlobalVars is at least - * one more than this index. For global code, fp->vars is parallel to the - * global script->atomMap.vector array, but possibly shorter for the common - * case (where var declarations and regexp literals cluster toward the front - * of the script or function body). - * - * Global variable name literals in script->atomMap have fast-global slot - * numbers (stored as int-tagged jsvals) in the corresponding fp->vars array - * element. The atomIndex for a regexp object literal thus also addresses an - * fp->vars element that is not used by any optimized global variable, so we - * use that GC-scanned element to keep the regexp object clone alive, as well - * as to lazily create and find it at run-time for the JSOP_REGEXP bytecode. - * - * In no case can cx->fp->varobj be a Call object here, because that implies - * we are compiling eval code, in which case (cx->fp->flags & JSFRAME_EVAL) - * is true, and js_GetToken will have already selected JSOP_OBJECT instead of - * JSOP_REGEXP, to avoid all this RegExp object cloning business. - * - * Why clone regexp objects? ECMA specifies that when a regular expression - * literal is scanned, a RegExp object is created. In the spec, compilation - * and execution happen indivisibly, but in this implementation and many of - * its embeddings, code is precompiled early and re-executed in multiple - * threads, or using multiple global objects, or both, for efficiency. - * - * In such cases, naively following ECMA leads to wrongful sharing of RegExp - * objects, which makes for collisions on the lastIndex property (especially - * for global regexps) and on any ad-hoc properties. Also, __proto__ and - * __parent__ refer to the pre-compilation prototype and global objects, a - * pigeon-hole problem for instanceof tests. - */ -static JSBool -IndexRegExpClone(JSContext *cx, JSParseNode *pn, JSAtomListElement *ale, - JSCodeGenerator *cg) -{ - JSObject *varobj, *reobj; - JSClass *clasp; - JSFunction *fun; - JSRegExp *re; - uint16 *countPtr; - uintN cloneIndex; - - JS_ASSERT(!(cx->fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO))); - - varobj = cx->fp->varobj; - clasp = OBJ_GET_CLASS(cx, varobj); - if (clasp == &js_FunctionClass) { - fun = (JSFunction *) JS_GetPrivate(cx, varobj); - countPtr = &fun->u.i.nregexps; - cloneIndex = *countPtr; - } else { - JS_ASSERT(clasp != &js_CallClass); - countPtr = &cg->treeContext.numGlobalVars; - cloneIndex = ALE_INDEX(ale); - } - - if ((cloneIndex + 1) >> 16) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_NEED_DIET, js_script_str); - return JS_FALSE; - } - if (cloneIndex >= *countPtr) - *countPtr = cloneIndex + 1; - - reobj = ATOM_TO_OBJECT(pn->pn_atom); - JS_ASSERT(OBJ_GET_CLASS(cx, reobj) == &js_RegExpClass); - re = (JSRegExp *) JS_GetPrivate(cx, reobj); - re->cloneIndex = cloneIndex; - return JS_TRUE; -} - -/* - * Emit a bytecode and its 2-byte constant (atom) index immediate operand. - * If the atomIndex requires more than 2 bytes, emit a prefix op whose 24-bit - * immediate operand indexes the atom in script->atomMap. - * - * If op has JOF_NAME mode, emit JSOP_FINDNAME to find and push the object in - * the scope chain in which the literal name was found, followed by the name - * as a string. This enables us to use the JOF_ELEM counterpart to op. - * - * Otherwise, if op has JOF_PROP mode, emit JSOP_LITERAL before op, to push - * the atom's value key. For JOF_PROP ops, the object being operated on has - * already been pushed, and JSOP_LITERAL will push the id, leaving the stack - * in the proper state for a JOF_ELEM counterpart. - * - * Otherwise, emit JSOP_LITOPX to push the atom index, then perform a special - * dispatch on op, but getting op's atom index from the stack instead of from - * an unsigned 16-bit immediate operand. - */ -static JSBool -EmitAtomIndexOp(JSContext *cx, JSOp op, jsatomid atomIndex, JSCodeGenerator *cg) -{ - uint32 mode; - JSOp prefixOp; - ptrdiff_t off; - jsbytecode *pc; - - if (atomIndex >= JS_BIT(16)) { - mode = (js_CodeSpec[op].format & JOF_MODEMASK); - if (op != JSOP_SETNAME) { - prefixOp = ((mode != JOF_NAME && mode != JOF_PROP) || -#if JS_HAS_XML_SUPPORT - op == JSOP_GETMETHOD || - op == JSOP_SETMETHOD || -#endif - op == JSOP_SETCONST) - ? JSOP_LITOPX - : (mode == JOF_NAME) - ? JSOP_FINDNAME - : JSOP_LITERAL; - off = js_EmitN(cx, cg, prefixOp, 3); - if (off < 0) - return JS_FALSE; - pc = CG_CODE(cg, off); - SET_LITERAL_INDEX(pc, atomIndex); - } - - switch (op) { - case JSOP_DECNAME: op = JSOP_DECELEM; break; - case JSOP_DECPROP: op = JSOP_DECELEM; break; - case JSOP_DELNAME: op = JSOP_DELELEM; break; - case JSOP_DELPROP: op = JSOP_DELELEM; break; - case JSOP_FORNAME: op = JSOP_FORELEM; break; - case JSOP_FORPROP: op = JSOP_FORELEM; break; - case JSOP_GETPROP: op = JSOP_GETELEM; break; - case JSOP_GETXPROP: op = JSOP_GETXELEM; break; - case JSOP_IMPORTPROP: op = JSOP_IMPORTELEM; break; - case JSOP_INCNAME: op = JSOP_INCELEM; break; - case JSOP_INCPROP: op = JSOP_INCELEM; break; - case JSOP_INITPROP: op = JSOP_INITELEM; break; - case JSOP_NAME: op = JSOP_GETELEM; break; - case JSOP_NAMEDEC: op = JSOP_ELEMDEC; break; - case JSOP_NAMEINC: op = JSOP_ELEMINC; break; - case JSOP_PROPDEC: op = JSOP_ELEMDEC; break; - case JSOP_PROPINC: op = JSOP_ELEMINC; break; - case JSOP_BINDNAME: return JS_TRUE; - case JSOP_SETNAME: op = JSOP_SETELEM; break; - case JSOP_SETPROP: op = JSOP_SETELEM; break; -#if JS_HAS_EXPORT_IMPORT - case JSOP_EXPORTNAME: - ReportStatementTooLarge(cx, cg); - return JS_FALSE; -#endif - default: -#if JS_HAS_XML_SUPPORT - JS_ASSERT(mode == 0 || op == JSOP_SETCONST || - op == JSOP_GETMETHOD || op == JSOP_SETMETHOD); -#else - JS_ASSERT(mode == 0 || op == JSOP_SETCONST); -#endif - break; - } - - return js_Emit1(cx, cg, op) >= 0; - } - - EMIT_UINT16_IMM_OP(op, atomIndex); - return JS_TRUE; -} - -/* - * Slight sugar for EmitAtomIndexOp, again accessing cx and cg from the macro - * caller's lexical environment, and embedding a false return on error. - * XXXbe hey, who checks for fun->nvars and fun->nargs overflow?! - */ -#define EMIT_ATOM_INDEX_OP(op, atomIndex) \ - JS_BEGIN_MACRO \ - if (!EmitAtomIndexOp(cx, op, atomIndex, cg)) \ - return JS_FALSE; \ - JS_END_MACRO - -static JSBool -EmitAtomOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) -{ - JSAtomListElement *ale; - - ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList); - if (!ale) - return JS_FALSE; - if (op == JSOP_REGEXP && !IndexRegExpClone(cx, pn, ale, cg)) - return JS_FALSE; - return EmitAtomIndexOp(cx, op, ALE_INDEX(ale), cg); -} - -/* - * This routine tries to optimize name gets and sets to stack slot loads and - * stores, given the variables object and scope chain in cx's top frame, the - * compile-time context in tc, and a TOK_NAME node pn. It returns false on - * error, true on success. - * - * The caller can inspect pn->pn_slot for a non-negative slot number to tell - * whether optimization occurred, in which case BindNameToSlot also updated - * pn->pn_op. If pn->pn_slot is still -1 on return, pn->pn_op nevertheless - * may have been optimized, e.g., from JSOP_NAME to JSOP_ARGUMENTS. Whether - * or not pn->pn_op was modified, if this function finds an argument or local - * variable name, pn->pn_attrs will contain the property's attributes after a - * successful return. - * - * NB: if you add more opcodes specialized from JSOP_NAME, etc., don't forget - * to update the TOK_FOR (for-in) and TOK_ASSIGN (op=, e.g. +=) special cases - * in js_EmitTree. - */ -static JSBool -BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, - JSBool letdecl) -{ - JSAtom *atom; - JSStmtInfo *stmt; - jsint slot; - JSOp op; - JSStackFrame *fp; - JSObject *obj, *pobj; - JSClass *clasp; - JSBool optimizeGlobals; - JSPropertyOp getter; - uintN attrs; - JSAtomListElement *ale; - JSProperty *prop; - JSScopeProperty *sprop; - - JS_ASSERT(pn->pn_type == TOK_NAME); - if (pn->pn_slot >= 0 || pn->pn_op == JSOP_ARGUMENTS) - return JS_TRUE; - - /* QNAME references can never be optimized to use arg/var storage. */ - if (pn->pn_op == JSOP_QNAMEPART) - return JS_TRUE; - - /* - * We can't optimize if we are compiling a with statement and its body, - * or we're in a catch block whose exception variable has the same name - * as this node. FIXME: we should be able to optimize catch vars to be - * block-locals. - */ - atom = pn->pn_atom; - stmt = js_LexicalLookup(tc, atom, &slot, letdecl); - if (stmt) { - if (stmt->type == STMT_WITH) - return JS_TRUE; - - JS_ASSERT(stmt->flags & SIF_SCOPE); - JS_ASSERT(slot >= 0); - op = pn->pn_op; - switch (op) { - case JSOP_NAME: op = JSOP_GETLOCAL; break; - case JSOP_SETNAME: op = JSOP_SETLOCAL; break; - case JSOP_INCNAME: op = JSOP_INCLOCAL; break; - case JSOP_NAMEINC: op = JSOP_LOCALINC; break; - case JSOP_DECNAME: op = JSOP_DECLOCAL; break; - case JSOP_NAMEDEC: op = JSOP_LOCALDEC; break; - case JSOP_FORNAME: op = JSOP_FORLOCAL; break; - case JSOP_DELNAME: op = JSOP_FALSE; break; - default: JS_ASSERT(0); - } - if (op != pn->pn_op) { - pn->pn_op = op; - pn->pn_slot = slot; - } - return JS_TRUE; - } - - /* - * A Script object can be used to split an eval into a compile step done - * at construction time, and an execute step done separately, possibly in - * a different scope altogether. We therefore cannot do any name-to-slot - * optimizations, but must lookup names at runtime. Note that script_exec - * ensures that its caller's frame has a Call object, so arg and var name - * lookups will succeed. - */ - fp = cx->fp; - if (fp->flags & JSFRAME_SCRIPT_OBJECT) - return JS_TRUE; - - /* - * We can't optimize if var and closure (a local function not in a larger - * expression and not at top-level within another's body) collide. - * XXX suboptimal: keep track of colliding names and deoptimize only those - */ - if (tc->flags & TCF_FUN_CLOSURE_VS_VAR) - return JS_TRUE; - - /* - * We can't optimize if we're not compiling a function body, whether via - * eval, or directly when compiling a function statement or expression. - */ - obj = fp->varobj; - clasp = OBJ_GET_CLASS(cx, obj); - if (clasp != &js_FunctionClass && clasp != &js_CallClass) { - /* Check for an eval or debugger frame. */ - if (fp->flags & JSFRAME_SPECIAL) - return JS_TRUE; - - /* - * Optimize global variable accesses if there are at least 100 uses - * in unambiguous contexts, or failing that, if least half of all the - * uses of global vars/consts/functions are in loops. - */ - optimizeGlobals = (tc->globalUses >= 100 || - (tc->loopyGlobalUses && - tc->loopyGlobalUses >= tc->globalUses / 2)); - if (!optimizeGlobals) - return JS_TRUE; - } else { - optimizeGlobals = JS_FALSE; - } - - /* - * We can't optimize if we are in an eval called inside a with statement. - */ - if (fp->scopeChain != obj) - return JS_TRUE; - - op = pn->pn_op; - getter = NULL; -#ifdef __GNUC__ - attrs = slot = 0; /* quell GCC overwarning */ -#endif - if (optimizeGlobals) { - /* - * We are optimizing global variables, and there is no pre-existing - * global property named atom. If atom was declared via const or var, - * optimize pn to access fp->vars using the appropriate JOF_QVAR op. - */ - ATOM_LIST_SEARCH(ale, &tc->decls, atom); - if (!ale) { - /* Use precedes declaration, or name is never declared. */ - return JS_TRUE; - } - - attrs = (ALE_JSOP(ale) == JSOP_DEFCONST) - ? JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT - : JSPROP_ENUMERATE | JSPROP_PERMANENT; - - /* Index atom so we can map fast global number to name. */ - JS_ASSERT(tc->flags & TCF_COMPILING); - ale = js_IndexAtom(cx, atom, &((JSCodeGenerator *) tc)->atomList); - if (!ale) - return JS_FALSE; - - /* Defend against tc->numGlobalVars 16-bit overflow. */ - slot = ALE_INDEX(ale); - if ((slot + 1) >> 16) - return JS_TRUE; - - if ((uint16)(slot + 1) > tc->numGlobalVars) - tc->numGlobalVars = (uint16)(slot + 1); - } else { - /* - * We may be able to optimize name to stack slot. Look for an argument - * or variable property in the function, or its call object, not found - * in any prototype object. Rewrite pn_op and update pn accordingly. - * NB: We know that JSOP_DELNAME on an argument or variable evaluates - * to false, due to JSPROP_PERMANENT. - */ - if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) - return JS_FALSE; - sprop = (JSScopeProperty *) prop; - if (sprop) { - if (pobj == obj) { - getter = sprop->getter; - attrs = sprop->attrs; - slot = (sprop->flags & SPROP_HAS_SHORTID) ? sprop->shortid : -1; - } - OBJ_DROP_PROPERTY(cx, pobj, prop); - } - } - - if (optimizeGlobals || getter) { - if (optimizeGlobals) { - switch (op) { - case JSOP_NAME: op = JSOP_GETGVAR; break; - case JSOP_SETNAME: op = JSOP_SETGVAR; break; - case JSOP_SETCONST: /* NB: no change */ break; - case JSOP_INCNAME: op = JSOP_INCGVAR; break; - case JSOP_NAMEINC: op = JSOP_GVARINC; break; - case JSOP_DECNAME: op = JSOP_DECGVAR; break; - case JSOP_NAMEDEC: op = JSOP_GVARDEC; break; - case JSOP_FORNAME: /* NB: no change */ break; - case JSOP_DELNAME: /* NB: no change */ break; - default: JS_ASSERT(0); - } - } else if (getter == js_GetLocalVariable || - getter == js_GetCallVariable) { - switch (op) { - case JSOP_NAME: op = JSOP_GETVAR; break; - case JSOP_SETNAME: op = JSOP_SETVAR; break; - case JSOP_SETCONST: op = JSOP_SETVAR; break; - case JSOP_INCNAME: op = JSOP_INCVAR; break; - case JSOP_NAMEINC: op = JSOP_VARINC; break; - case JSOP_DECNAME: op = JSOP_DECVAR; break; - case JSOP_NAMEDEC: op = JSOP_VARDEC; break; - case JSOP_FORNAME: op = JSOP_FORVAR; break; - case JSOP_DELNAME: op = JSOP_FALSE; break; - default: JS_ASSERT(0); - } - } else if (getter == js_GetArgument || - (getter == js_CallClass.getProperty && - fp->fun && (uintN) slot < fp->fun->nargs)) { - switch (op) { - case JSOP_NAME: op = JSOP_GETARG; break; - case JSOP_SETNAME: op = JSOP_SETARG; break; - case JSOP_INCNAME: op = JSOP_INCARG; break; - case JSOP_NAMEINC: op = JSOP_ARGINC; break; - case JSOP_DECNAME: op = JSOP_DECARG; break; - case JSOP_NAMEDEC: op = JSOP_ARGDEC; break; - case JSOP_FORNAME: op = JSOP_FORARG; break; - case JSOP_DELNAME: op = JSOP_FALSE; break; - default: JS_ASSERT(0); - } - } - if (op != pn->pn_op) { - pn->pn_op = op; - pn->pn_slot = slot; - } - pn->pn_attrs = attrs; - } - - if (pn->pn_slot < 0) { - /* - * We couldn't optimize pn, so it's not a global or local slot name. - * Now we must check for the predefined arguments variable. It may be - * overridden by assignment, in which case the function is heavyweight - * and the interpreter will look up 'arguments' in the function's call - * object. - */ - if (pn->pn_op == JSOP_NAME && - atom == cx->runtime->atomState.argumentsAtom) { - pn->pn_op = JSOP_ARGUMENTS; - return JS_TRUE; - } - - tc->flags |= TCF_FUN_USES_NONLOCALS; - } - return JS_TRUE; -} - -/* - * If pn contains a useful expression, return true with *answer set to true. - * If pn contains a useless expression, return true with *answer set to false. - * Return false on error. - * - * The caller should initialize *answer to false and invoke this function on - * an expression statement or similar subtree to decide whether the tree could - * produce code that has any side effects. For an expression statement, we - * define useless code as code with no side effects, because the main effect, - * the value left on the stack after the code executes, will be discarded by a - * pop bytecode. - */ -static JSBool -CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, - JSBool *answer) -{ - JSBool ok; - JSFunction *fun; - JSParseNode *pn2; - - ok = JS_TRUE; - if (!pn || *answer) - return ok; - - switch (pn->pn_arity) { - case PN_FUNC: - /* - * A named function is presumed useful: we can't yet know that it is - * not called. The side effects are the creation of a scope object - * to parent this function object, and the binding of the function's - * name in that scope object. See comments at case JSOP_NAMEDFUNOBJ: - * in jsinterp.c. - */ - fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(pn->pn_funAtom)); - if (fun->atom) - *answer = JS_TRUE; - break; - - case PN_LIST: - if (pn->pn_type == TOK_NEW || - pn->pn_type == TOK_LP || - pn->pn_type == TOK_LB || - pn->pn_type == TOK_RB || - pn->pn_type == TOK_RC) { - /* - * All invocation operations (construct: TOK_NEW, call: TOK_LP) - * are presumed to be useful, because they may have side effects - * even if their main effect (their return value) is discarded. - * - * TOK_LB binary trees of 3 or more nodes are flattened into lists - * to avoid too much recursion. All such lists must be presumed - * to be useful because each index operation could invoke a getter - * (the JSOP_ARGUMENTS special case below, in the PN_BINARY case, - * does not apply here: arguments[i][j] might invoke a getter). - * - * Array and object initializers (TOK_RB and TOK_RC lists) must be - * considered useful, because they are sugar for constructor calls - * (to Array and Object, respectively). - */ - *answer = JS_TRUE; - } else { - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) - ok &= CheckSideEffects(cx, tc, pn2, answer); - } - break; - - case PN_TERNARY: - ok = CheckSideEffects(cx, tc, pn->pn_kid1, answer) && - CheckSideEffects(cx, tc, pn->pn_kid2, answer) && - CheckSideEffects(cx, tc, pn->pn_kid3, answer); - break; - - case PN_BINARY: - if (pn->pn_type == TOK_ASSIGN) { - /* - * Assignment is presumed to be useful, even if the next operation - * is another assignment overwriting this one's ostensible effect, - * because the left operand may be a property with a setter that - * has side effects. - * - * The only exception is assignment of a useless value to a const - * declared in the function currently being compiled. - */ - pn2 = pn->pn_left; - if (pn2->pn_type != TOK_NAME) { - *answer = JS_TRUE; - } else { - if (!BindNameToSlot(cx, tc, pn2, JS_FALSE)) - return JS_FALSE; - if (!CheckSideEffects(cx, tc, pn->pn_right, answer)) - return JS_FALSE; - if (!*answer && - (pn2->pn_slot < 0 || !(pn2->pn_attrs & JSPROP_READONLY))) { - *answer = JS_TRUE; - } - } - } else { - if (pn->pn_type == TOK_LB) { - pn2 = pn->pn_left; - if (pn2->pn_type == TOK_NAME && - !BindNameToSlot(cx, tc, pn2, JS_FALSE)) { - return JS_FALSE; - } - if (pn2->pn_op != JSOP_ARGUMENTS) { - /* - * Any indexed property reference could call a getter with - * side effects, except for arguments[i] where arguments is - * unambiguous. - */ - *answer = JS_TRUE; - } - } - ok = CheckSideEffects(cx, tc, pn->pn_left, answer) && - CheckSideEffects(cx, tc, pn->pn_right, answer); - } - break; - - case PN_UNARY: - if (pn->pn_type == TOK_INC || pn->pn_type == TOK_DEC || - pn->pn_type == TOK_THROW || -#if JS_HAS_GENERATORS - pn->pn_type == TOK_YIELD || -#endif - pn->pn_type == TOK_DEFSHARP) { - /* All these operations have effects that we must commit. */ - *answer = JS_TRUE; - } else if (pn->pn_type == TOK_DELETE) { - pn2 = pn->pn_kid; - switch (pn2->pn_type) { - case TOK_NAME: - case TOK_DOT: -#if JS_HAS_XML_SUPPORT - case TOK_DBLDOT: -#endif -#if JS_HAS_LVALUE_RETURN - case TOK_LP: -#endif - case TOK_LB: - /* All these delete addressing modes have effects too. */ - *answer = JS_TRUE; - break; - default: - ok = CheckSideEffects(cx, tc, pn2, answer); - break; - } - } else { - ok = CheckSideEffects(cx, tc, pn->pn_kid, answer); - } - break; - - case PN_NAME: - /* - * Take care to avoid trying to bind a label name (labels, both for - * statements and property values in object initialisers, have pn_op - * defaulted to JSOP_NOP). - */ - if (pn->pn_type == TOK_NAME && pn->pn_op != JSOP_NOP) { - if (!BindNameToSlot(cx, tc, pn, JS_FALSE)) - return JS_FALSE; - if (pn->pn_slot < 0 && pn->pn_op != JSOP_ARGUMENTS) { - /* - * Not an argument or local variable use, so this expression - * could invoke a getter that has side effects. - */ - *answer = JS_TRUE; - } - } - pn2 = pn->pn_expr; - if (pn->pn_type == TOK_DOT) { - if (pn2->pn_type == TOK_NAME && - !BindNameToSlot(cx, tc, pn2, JS_FALSE)) { - return JS_FALSE; - } - if (!(pn2->pn_op == JSOP_ARGUMENTS && - pn->pn_atom == cx->runtime->atomState.lengthAtom)) { - /* - * Any dotted property reference could call a getter, except - * for arguments.length where arguments is unambiguous. - */ - *answer = JS_TRUE; - } - } - ok = CheckSideEffects(cx, tc, pn2, answer); - break; - - case PN_NULLARY: - if (pn->pn_type == TOK_DEBUGGER) - *answer = JS_TRUE; - break; - } - return ok; -} - -/* - * Secret handshake with js_EmitTree's TOK_LP/TOK_NEW case logic, to flag all - * uses of JSOP_GETMETHOD that implicitly qualify the method property's name - * with a function:: prefix. All other JSOP_GETMETHOD and JSOP_SETMETHOD uses - * must be explicit, so we need a distinct source note (SRC_METHODBASE rather - * than SRC_PCBASE) for round-tripping through the beloved decompiler. - */ -#define JSPROP_IMPLICIT_FUNCTION_NAMESPACE 0x100 - -static jssrcnote -SrcNoteForPropOp(JSParseNode *pn, JSOp op) -{ - return ((op == JSOP_GETMETHOD && - !(pn->pn_attrs & JSPROP_IMPLICIT_FUNCTION_NAMESPACE)) || - op == JSOP_SETMETHOD) - ? SRC_METHODBASE - : SRC_PCBASE; -} - -static JSBool -EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) -{ - JSParseNode *pn2, *pndot, *pnup, *pndown; - ptrdiff_t top; - - pn2 = pn->pn_expr; - if (op == JSOP_GETPROP && - pn->pn_type == TOK_DOT && - pn2->pn_type == TOK_NAME) { - /* Try to optimize arguments.length into JSOP_ARGCNT. */ - if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE)) - return JS_FALSE; - if (pn2->pn_op == JSOP_ARGUMENTS && - pn->pn_atom == cx->runtime->atomState.lengthAtom) { - return js_Emit1(cx, cg, JSOP_ARGCNT) >= 0; - } - } - - /* - * If the object operand is also a dotted property reference, reverse the - * list linked via pn_expr temporarily so we can iterate over it from the - * bottom up (reversing again as we go), to avoid excessive recursion. - */ - if (pn2->pn_type == TOK_DOT) { - pndot = pn2; - pnup = NULL; - top = CG_OFFSET(cg); - for (;;) { - /* Reverse pndot->pn_expr to point up, not down. */ - pndot->pn_offset = top; - pndown = pndot->pn_expr; - pndot->pn_expr = pnup; - if (pndown->pn_type != TOK_DOT) - break; - pnup = pndot; - pndot = pndown; - } - - /* pndown is a primary expression, not a dotted property reference. */ - if (!js_EmitTree(cx, cg, pndown)) - return JS_FALSE; - - do { - /* Walk back up the list, emitting annotated name ops. */ - if (js_NewSrcNote2(cx, cg, SrcNoteForPropOp(pndot, pndot->pn_op), - CG_OFFSET(cg) - pndown->pn_offset) < 0) { - return JS_FALSE; - } - if (!EmitAtomOp(cx, pndot, pndot->pn_op, cg)) - return JS_FALSE; - - /* Reverse the pn_expr link again. */ - pnup = pndot->pn_expr; - pndot->pn_expr = pndown; - pndown = pndot; - } while ((pndot = pnup) != NULL); - } else { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - } - - if (js_NewSrcNote2(cx, cg, SrcNoteForPropOp(pn, op), - CG_OFFSET(cg) - pn2->pn_offset) < 0) { - return JS_FALSE; - } - if (!pn->pn_atom) { - JS_ASSERT(op == JSOP_IMPORTALL); - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } else { - if (!EmitAtomOp(cx, pn, op, cg)) - return JS_FALSE; - } - return JS_TRUE; -} - -static JSBool -EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) -{ - ptrdiff_t top; - JSParseNode *left, *right, *next, ltmp, rtmp; - jsint slot; - - top = CG_OFFSET(cg); - if (pn->pn_arity == PN_LIST) { - /* Left-associative operator chain to avoid too much recursion. */ - JS_ASSERT(pn->pn_op == JSOP_GETELEM || pn->pn_op == JSOP_IMPORTELEM); - JS_ASSERT(pn->pn_count >= 3); - left = pn->pn_head; - right = PN_LAST(pn); - next = left->pn_next; - JS_ASSERT(next != right); - - /* - * Try to optimize arguments[0][j]... into JSOP_ARGSUB<0> followed by - * one or more index expression and JSOP_GETELEM op pairs. - */ - if (left->pn_type == TOK_NAME && next->pn_type == TOK_NUMBER) { - if (!BindNameToSlot(cx, &cg->treeContext, left, JS_FALSE)) - return JS_FALSE; - if (left->pn_op == JSOP_ARGUMENTS && - JSDOUBLE_IS_INT(next->pn_dval, slot) && - (jsuint)slot < JS_BIT(16)) { - left->pn_offset = next->pn_offset = top; - EMIT_UINT16_IMM_OP(JSOP_ARGSUB, (jsatomid)slot); - left = next; - next = left->pn_next; - } - } - - /* - * Check whether we generated JSOP_ARGSUB, just above, and have only - * one more index expression to emit. Given arguments[0][j], we must - * skip the while loop altogether, falling through to emit code for j - * (in the subtree referenced by right), followed by the annotated op, - * at the bottom of this function. - */ - JS_ASSERT(next != right || pn->pn_count == 3); - if (left == pn->pn_head) { - if (!js_EmitTree(cx, cg, left)) - return JS_FALSE; - } - while (next != right) { - if (!js_EmitTree(cx, cg, next)) - return JS_FALSE; - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_GETELEM) < 0) - return JS_FALSE; - next = next->pn_next; - } - } else { - if (pn->pn_arity == PN_NAME) { - /* - * Set left and right so pn appears to be a TOK_LB node, instead - * of a TOK_DOT node. See the TOK_FOR/IN case in js_EmitTree, and - * EmitDestructuringOps nearer below. In the destructuring case, - * the base expression (pn_expr) of the name may be null, which - * means we have to emit a JSOP_BINDNAME. - */ - left = pn->pn_expr; - if (!left) { - left = <mp; - left->pn_type = TOK_OBJECT; - left->pn_op = JSOP_BINDNAME; - left->pn_arity = PN_NULLARY; - left->pn_pos = pn->pn_pos; - left->pn_atom = pn->pn_atom; - } - right = &rtmp; - right->pn_type = TOK_STRING; - JS_ASSERT(ATOM_IS_STRING(pn->pn_atom)); - right->pn_op = js_IsIdentifier(ATOM_TO_STRING(pn->pn_atom)) - ? JSOP_QNAMEPART - : JSOP_STRING; - right->pn_arity = PN_NULLARY; - right->pn_pos = pn->pn_pos; - right->pn_atom = pn->pn_atom; - } else { - JS_ASSERT(pn->pn_arity == PN_BINARY); - left = pn->pn_left; - right = pn->pn_right; - } - - /* Try to optimize arguments[0] (e.g.) into JSOP_ARGSUB<0>. */ - if (op == JSOP_GETELEM && - left->pn_type == TOK_NAME && - right->pn_type == TOK_NUMBER) { - if (!BindNameToSlot(cx, &cg->treeContext, left, JS_FALSE)) - return JS_FALSE; - if (left->pn_op == JSOP_ARGUMENTS && - JSDOUBLE_IS_INT(right->pn_dval, slot) && - (jsuint)slot < JS_BIT(16)) { - left->pn_offset = right->pn_offset = top; - EMIT_UINT16_IMM_OP(JSOP_ARGSUB, (jsatomid)slot); - return JS_TRUE; - } - } - - if (!js_EmitTree(cx, cg, left)) - return JS_FALSE; - } - - /* The right side of the descendant operator is implicitly quoted. */ - JS_ASSERT(op != JSOP_DESCENDANTS || right->pn_type != TOK_STRING || - right->pn_op == JSOP_QNAMEPART); - if (!js_EmitTree(cx, cg, right)) - return JS_FALSE; - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) - return JS_FALSE; - return js_Emit1(cx, cg, op) >= 0; -} - -static JSBool -EmitNumberOp(JSContext *cx, jsdouble dval, JSCodeGenerator *cg) -{ - jsint ival; - jsatomid atomIndex; - ptrdiff_t off; - jsbytecode *pc; - JSAtom *atom; - JSAtomListElement *ale; - - if (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival)) { - if (ival == 0) - return js_Emit1(cx, cg, JSOP_ZERO) >= 0; - if (ival == 1) - return js_Emit1(cx, cg, JSOP_ONE) >= 0; - - atomIndex = (jsatomid)ival; - if (atomIndex < JS_BIT(16)) { - EMIT_UINT16_IMM_OP(JSOP_UINT16, atomIndex); - return JS_TRUE; - } - - if (atomIndex < JS_BIT(24)) { - off = js_EmitN(cx, cg, JSOP_UINT24, 3); - if (off < 0) - return JS_FALSE; - pc = CG_CODE(cg, off); - SET_LITERAL_INDEX(pc, atomIndex); - return JS_TRUE; - } - - atom = js_AtomizeInt(cx, ival, 0); - } else { - atom = js_AtomizeDouble(cx, dval, 0); - } - if (!atom) - return JS_FALSE; - - ale = js_IndexAtom(cx, atom, &cg->atomList); - if (!ale) - return JS_FALSE; - return EmitAtomIndexOp(cx, JSOP_NUMBER, ALE_INDEX(ale), cg); -} - -static JSBool -EmitSwitch(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, - JSStmtInfo *stmtInfo) -{ - JSOp switchOp; - JSBool ok, hasDefault, constPropagated; - ptrdiff_t top, off, defaultOffset; - JSParseNode *pn2, *pn3, *pn4; - uint32 caseCount, tableLength; - JSParseNode **table; - jsdouble d; - jsint i, low, high; - jsval v; - JSAtom *atom; - JSAtomListElement *ale; - intN noteIndex; - size_t switchSize, tableSize; - jsbytecode *pc, *savepc; -#if JS_HAS_BLOCK_SCOPE - JSObject *obj; - jsint count; -#endif - - /* Try for most optimal, fall back if not dense ints, and per ECMAv2. */ - switchOp = JSOP_TABLESWITCH; - ok = JS_TRUE; - hasDefault = constPropagated = JS_FALSE; - defaultOffset = -1; - - /* - * If the switch contains let variables scoped by its body, model the - * resulting block on the stack first, before emitting the discriminant's - * bytecode (in case the discriminant contains a stack-model dependency - * such as a let expression). - */ - pn2 = pn->pn_right; -#if JS_HAS_BLOCK_SCOPE - if (pn2->pn_type == TOK_LEXICALSCOPE) { - atom = pn2->pn_atom; - obj = ATOM_TO_OBJECT(atom); - OBJ_SET_BLOCK_DEPTH(cx, obj, cg->stackDepth); - - /* - * Push the body's block scope before discriminant code-gen for proper - * static block scope linkage in case the discriminant contains a let - * expression. The block's locals must lie under the discriminant on - * the stack so that case-dispatch bytecodes can find the discriminant - * on top of stack. - */ - js_PushBlockScope(&cg->treeContext, stmtInfo, atom, -1); - stmtInfo->type = STMT_SWITCH; - - count = OBJ_BLOCK_COUNT(cx, obj); - cg->stackDepth += count; - if ((uintN)cg->stackDepth > cg->maxStackDepth) - cg->maxStackDepth = cg->stackDepth; - - /* Emit JSOP_ENTERBLOCK before code to evaluate the discriminant. */ - ale = js_IndexAtom(cx, atom, &cg->atomList); - if (!ale) - return JS_FALSE; - EMIT_ATOM_INDEX_OP(JSOP_ENTERBLOCK, ALE_INDEX(ale)); - - /* - * Pop the switch's statement info around discriminant code-gen. Note - * how this leaves cg->treeContext.blockChain referencing the switch's - * block scope object, which is necessary for correct block parenting - * in the case where the discriminant contains a let expression. - */ - cg->treeContext.topStmt = stmtInfo->down; - cg->treeContext.topScopeStmt = stmtInfo->downScope; - } -#ifdef __GNUC__ - else { - atom = NULL; - count = -1; - } -#endif -#endif - - /* - * Emit code for the discriminant first (or nearly first, in the case of a - * switch whose body is a block scope). - */ - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - - /* Switch bytecodes run from here till end of final case. */ - top = CG_OFFSET(cg); -#if !JS_HAS_BLOCK_SCOPE - js_PushStatement(&cg->treeContext, stmtInfo, STMT_SWITCH, top); -#else - if (pn2->pn_type == TOK_LC) { - js_PushStatement(&cg->treeContext, stmtInfo, STMT_SWITCH, top); - } else { - /* Re-push the switch's statement info record. */ - cg->treeContext.topStmt = cg->treeContext.topScopeStmt = stmtInfo; - - /* Set the statement info record's idea of top. */ - stmtInfo->update = top; - - /* Advance pn2 to refer to the switch case list. */ - pn2 = pn2->pn_expr; - } -#endif - - caseCount = pn2->pn_count; - tableLength = 0; - table = NULL; - - if (caseCount == 0 || - (caseCount == 1 && - (hasDefault = (pn2->pn_head->pn_type == TOK_DEFAULT)))) { - caseCount = 0; - low = 0; - high = -1; - } else { -#define INTMAP_LENGTH 256 - jsbitmap intmap_space[INTMAP_LENGTH]; - jsbitmap *intmap = NULL; - int32 intmap_bitlen = 0; - - low = JSVAL_INT_MAX; - high = JSVAL_INT_MIN; - - for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { - if (pn3->pn_type == TOK_DEFAULT) { - hasDefault = JS_TRUE; - caseCount--; /* one of the "cases" was the default */ - continue; - } - - JS_ASSERT(pn3->pn_type == TOK_CASE); - if (switchOp == JSOP_CONDSWITCH) - continue; - - pn4 = pn3->pn_left; - switch (pn4->pn_type) { - case TOK_NUMBER: - d = pn4->pn_dval; - if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) { - pn3->pn_val = INT_TO_JSVAL(i); - } else { - atom = js_AtomizeDouble(cx, d, 0); - if (!atom) { - ok = JS_FALSE; - goto release; - } - pn3->pn_val = ATOM_KEY(atom); - } - break; - case TOK_STRING: - pn3->pn_val = ATOM_KEY(pn4->pn_atom); - break; - case TOK_NAME: - if (!pn4->pn_expr) { - ok = js_LookupCompileTimeConstant(cx, cg, pn4->pn_atom, &v); - if (!ok) - goto release; - if (!JSVAL_IS_VOID(v)) { - pn3->pn_val = v; - constPropagated = JS_TRUE; - break; - } - } - /* FALL THROUGH */ - case TOK_PRIMARY: - if (pn4->pn_op == JSOP_TRUE) { - pn3->pn_val = JSVAL_TRUE; - break; - } - if (pn4->pn_op == JSOP_FALSE) { - pn3->pn_val = JSVAL_FALSE; - break; - } - /* FALL THROUGH */ - default: - switchOp = JSOP_CONDSWITCH; - continue; - } - - JS_ASSERT(JSVAL_IS_NUMBER(pn3->pn_val) || - JSVAL_IS_STRING(pn3->pn_val) || - JSVAL_IS_BOOLEAN(pn3->pn_val)); - - if (switchOp != JSOP_TABLESWITCH) - continue; - if (!JSVAL_IS_INT(pn3->pn_val)) { - switchOp = JSOP_LOOKUPSWITCH; - continue; - } - i = JSVAL_TO_INT(pn3->pn_val); - if ((jsuint)(i + (jsint)JS_BIT(15)) >= (jsuint)JS_BIT(16)) { - switchOp = JSOP_LOOKUPSWITCH; - continue; - } - if (i < low) - low = i; - if (high < i) - high = i; - - /* - * Check for duplicates, which require a JSOP_LOOKUPSWITCH. - * We bias i by 65536 if it's negative, and hope that's a rare - * case (because it requires a malloc'd bitmap). - */ - if (i < 0) - i += JS_BIT(16); - if (i >= intmap_bitlen) { - if (!intmap && - i < (INTMAP_LENGTH << JS_BITS_PER_WORD_LOG2)) { - intmap = intmap_space; - intmap_bitlen = INTMAP_LENGTH << JS_BITS_PER_WORD_LOG2; - } else { - /* Just grab 8K for the worst-case bitmap. */ - intmap_bitlen = JS_BIT(16); - intmap = (jsbitmap *) - JS_malloc(cx, - (JS_BIT(16) >> JS_BITS_PER_WORD_LOG2) - * sizeof(jsbitmap)); - if (!intmap) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - } - memset(intmap, 0, intmap_bitlen >> JS_BITS_PER_BYTE_LOG2); - } - if (JS_TEST_BIT(intmap, i)) { - switchOp = JSOP_LOOKUPSWITCH; - continue; - } - JS_SET_BIT(intmap, i); - } - - release: - if (intmap && intmap != intmap_space) - JS_free(cx, intmap); - if (!ok) - return JS_FALSE; - - /* - * Compute table length and select lookup instead if overlarge or - * more than half-sparse. - */ - if (switchOp == JSOP_TABLESWITCH) { - tableLength = (uint32)(high - low + 1); - if (tableLength >= JS_BIT(16) || tableLength > 2 * caseCount) - switchOp = JSOP_LOOKUPSWITCH; - } else if (switchOp == JSOP_LOOKUPSWITCH) { - /* - * Lookup switch supports only atom indexes below 64K limit. - * Conservatively estimate the maximum possible index during - * switch generation and use conditional switch if it exceeds - * the limit. - */ - if (caseCount + cg->atomList.count > JS_BIT(16)) - switchOp = JSOP_CONDSWITCH; - } - } - - /* - * Emit a note with two offsets: first tells total switch code length, - * second tells offset to first JSOP_CASE if condswitch. - */ - noteIndex = js_NewSrcNote3(cx, cg, SRC_SWITCH, 0, 0); - if (noteIndex < 0) - return JS_FALSE; - - if (switchOp == JSOP_CONDSWITCH) { - /* - * 0 bytes of immediate for unoptimized ECMAv2 switch. - */ - switchSize = 0; - } else if (switchOp == JSOP_TABLESWITCH) { - /* - * 3 offsets (len, low, high) before the table, 1 per entry. - */ - switchSize = (size_t)(JUMP_OFFSET_LEN * (3 + tableLength)); - } else { - /* - * JSOP_LOOKUPSWITCH: - * 1 offset (len) and 1 atom index (npairs) before the table, - * 1 atom index and 1 jump offset per entry. - */ - switchSize = (size_t)(JUMP_OFFSET_LEN + ATOM_INDEX_LEN + - (ATOM_INDEX_LEN + JUMP_OFFSET_LEN) * caseCount); - } - - /* - * Emit switchOp followed by switchSize bytes of jump or lookup table. - * - * If switchOp is JSOP_LOOKUPSWITCH or JSOP_TABLESWITCH, it is crucial - * to emit the immediate operand(s) by which bytecode readers such as - * BuildSpanDepTable discover the length of the switch opcode *before* - * calling js_SetJumpOffset (which may call BuildSpanDepTable). It's - * also important to zero all unknown jump offset immediate operands, - * so they can be converted to span dependencies with null targets to - * be computed later (js_EmitN zeros switchSize bytes after switchOp). - */ - if (js_EmitN(cx, cg, switchOp, switchSize) < 0) - return JS_FALSE; - - off = -1; - if (switchOp == JSOP_CONDSWITCH) { - intN caseNoteIndex = -1; - JSBool beforeCases = JS_TRUE; - - /* Emit code for evaluating cases and jumping to case statements. */ - for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { - pn4 = pn3->pn_left; - if (pn4 && !js_EmitTree(cx, cg, pn4)) - return JS_FALSE; - if (caseNoteIndex >= 0) { - /* off is the previous JSOP_CASE's bytecode offset. */ - if (!js_SetSrcNoteOffset(cx, cg, (uintN)caseNoteIndex, 0, - CG_OFFSET(cg) - off)) { - return JS_FALSE; - } - } - if (!pn4) { - JS_ASSERT(pn3->pn_type == TOK_DEFAULT); - continue; - } - caseNoteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); - if (caseNoteIndex < 0) - return JS_FALSE; - off = EmitJump(cx, cg, JSOP_CASE, 0); - if (off < 0) - return JS_FALSE; - pn3->pn_offset = off; - if (beforeCases) { - uintN noteCount, noteCountDelta; - - /* Switch note's second offset is to first JSOP_CASE. */ - noteCount = CG_NOTE_COUNT(cg); - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, - off - top)) { - return JS_FALSE; - } - noteCountDelta = CG_NOTE_COUNT(cg) - noteCount; - if (noteCountDelta != 0) - caseNoteIndex += noteCountDelta; - beforeCases = JS_FALSE; - } - } - - /* - * If we didn't have an explicit default (which could fall in between - * cases, preventing us from fusing this js_SetSrcNoteOffset with the - * call in the loop above), link the last case to the implicit default - * for the decompiler. - */ - if (!hasDefault && - caseNoteIndex >= 0 && - !js_SetSrcNoteOffset(cx, cg, (uintN)caseNoteIndex, 0, - CG_OFFSET(cg) - off)) { - return JS_FALSE; - } - - /* Emit default even if no explicit default statement. */ - defaultOffset = EmitJump(cx, cg, JSOP_DEFAULT, 0); - if (defaultOffset < 0) - return JS_FALSE; - } else { - pc = CG_CODE(cg, top + JUMP_OFFSET_LEN); - - if (switchOp == JSOP_TABLESWITCH) { - /* Fill in switch bounds, which we know fit in 16-bit offsets. */ - SET_JUMP_OFFSET(pc, low); - pc += JUMP_OFFSET_LEN; - SET_JUMP_OFFSET(pc, high); - pc += JUMP_OFFSET_LEN; - - /* - * Use malloc to avoid arena bloat for programs with many switches. - * We free table if non-null at label out, so all control flow must - * exit this function through goto out or goto bad. - */ - if (tableLength != 0) { - tableSize = (size_t)tableLength * sizeof *table; - table = (JSParseNode **) JS_malloc(cx, tableSize); - if (!table) - return JS_FALSE; - memset(table, 0, tableSize); - for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { - if (pn3->pn_type == TOK_DEFAULT) - continue; - i = JSVAL_TO_INT(pn3->pn_val); - i -= low; - JS_ASSERT((uint32)i < tableLength); - table[i] = pn3; - } - } - } else { - JS_ASSERT(switchOp == JSOP_LOOKUPSWITCH); - - /* Fill in the number of cases. */ - SET_ATOM_INDEX(pc, caseCount); - pc += ATOM_INDEX_LEN; - } - - /* - * After this point, all control flow involving JSOP_TABLESWITCH - * must set ok and goto out to exit this function. To keep things - * simple, all switchOp cases exit that way. - */ - if (constPropagated) { - /* - * Skip switchOp, as we are not setting jump offsets in the two - * for loops below. We'll restore CG_NEXT(cg) from savepc after, - * unless there was an error. - */ - savepc = CG_NEXT(cg); - CG_NEXT(cg) = pc + 1; - if (switchOp == JSOP_TABLESWITCH) { - for (i = 0; i < (jsint)tableLength; i++) { - pn3 = table[i]; - if (pn3 && - (pn4 = pn3->pn_left) != NULL && - pn4->pn_type == TOK_NAME) { - /* Note a propagated constant with the const's name. */ - JS_ASSERT(!pn4->pn_expr); - ale = js_IndexAtom(cx, pn4->pn_atom, &cg->atomList); - if (!ale) - goto bad; - CG_NEXT(cg) = pc; - if (js_NewSrcNote2(cx, cg, SRC_LABEL, (ptrdiff_t) - ALE_INDEX(ale)) < 0) { - goto bad; - } - } - pc += JUMP_OFFSET_LEN; - } - } else { - for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { - pn4 = pn3->pn_left; - if (pn4 && pn4->pn_type == TOK_NAME) { - /* Note a propagated constant with the const's name. */ - JS_ASSERT(!pn4->pn_expr); - ale = js_IndexAtom(cx, pn4->pn_atom, &cg->atomList); - if (!ale) - goto bad; - CG_NEXT(cg) = pc; - if (js_NewSrcNote2(cx, cg, SRC_LABEL, (ptrdiff_t) - ALE_INDEX(ale)) < 0) { - goto bad; - } - } - pc += ATOM_INDEX_LEN + JUMP_OFFSET_LEN; - } - } - CG_NEXT(cg) = savepc; - } - } - - /* Emit code for each case's statements, copying pn_offset up to pn3. */ - for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { - if (switchOp == JSOP_CONDSWITCH && pn3->pn_type != TOK_DEFAULT) - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, pn3->pn_offset); - pn4 = pn3->pn_right; - ok = js_EmitTree(cx, cg, pn4); - if (!ok) - goto out; - pn3->pn_offset = pn4->pn_offset; - if (pn3->pn_type == TOK_DEFAULT) - off = pn3->pn_offset - top; - } - - if (!hasDefault) { - /* If no default case, offset for default is to end of switch. */ - off = CG_OFFSET(cg) - top; - } - - /* We better have set "off" by now. */ - JS_ASSERT(off != -1); - - /* Set the default offset (to end of switch if no default). */ - if (switchOp == JSOP_CONDSWITCH) { - pc = NULL; - JS_ASSERT(defaultOffset != -1); - ok = js_SetJumpOffset(cx, cg, CG_CODE(cg, defaultOffset), - off - (defaultOffset - top)); - if (!ok) - goto out; - } else { - pc = CG_CODE(cg, top); - ok = js_SetJumpOffset(cx, cg, pc, off); - if (!ok) - goto out; - pc += JUMP_OFFSET_LEN; - } - - /* Set the SRC_SWITCH note's offset operand to tell end of switch. */ - off = CG_OFFSET(cg) - top; - ok = js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, off); - if (!ok) - goto out; - - if (switchOp == JSOP_TABLESWITCH) { - /* Skip over the already-initialized switch bounds. */ - pc += 2 * JUMP_OFFSET_LEN; - - /* Fill in the jump table, if there is one. */ - for (i = 0; i < (jsint)tableLength; i++) { - pn3 = table[i]; - off = pn3 ? pn3->pn_offset - top : 0; - ok = js_SetJumpOffset(cx, cg, pc, off); - if (!ok) - goto out; - pc += JUMP_OFFSET_LEN; - } - } else if (switchOp == JSOP_LOOKUPSWITCH) { - /* Skip over the already-initialized number of cases. */ - pc += ATOM_INDEX_LEN; - - for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { - if (pn3->pn_type == TOK_DEFAULT) - continue; - atom = js_AtomizeValue(cx, pn3->pn_val, 0); - if (!atom) - goto bad; - ale = js_IndexAtom(cx, atom, &cg->atomList); - if (!ale) - goto bad; - SET_ATOM_INDEX(pc, ALE_INDEX(ale)); - pc += ATOM_INDEX_LEN; - - off = pn3->pn_offset - top; - ok = js_SetJumpOffset(cx, cg, pc, off); - if (!ok) - goto out; - pc += JUMP_OFFSET_LEN; - } - } - -out: - if (table) - JS_free(cx, table); - if (ok) { - ok = js_PopStatementCG(cx, cg); - -#if JS_HAS_BLOCK_SCOPE - if (ok && pn->pn_right->pn_type == TOK_LEXICALSCOPE) { - EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count); - cg->stackDepth -= count; - } -#endif - } - return ok; - -bad: - ok = JS_FALSE; - goto out; -} - -JSBool -js_EmitFunctionBytecode(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body) -{ - if (!js_AllocTryNotes(cx, cg)) - return JS_FALSE; - - if (cg->treeContext.flags & TCF_FUN_IS_GENERATOR) { - /* JSOP_GENERATOR must be the first instruction. */ - CG_SWITCH_TO_PROLOG(cg); - JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg)); - if (js_Emit1(cx, cg, JSOP_GENERATOR) < 0) - return JS_FALSE; - CG_SWITCH_TO_MAIN(cg); - } - - return js_EmitTree(cx, cg, body) && - js_Emit1(cx, cg, JSOP_STOP) >= 0; -} - -JSBool -js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body, - JSFunction *fun) -{ - JSStackFrame *fp, frame; - JSObject *funobj; - JSBool ok; - - fp = cx->fp; - funobj = fun->object; - JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj && - fp->scopeChain != funobj)); - memset(&frame, 0, sizeof frame); - frame.fun = fun; - frame.varobj = frame.scopeChain = funobj; - frame.down = fp; - frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx) - ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO - : JSFRAME_COMPILING; - cx->fp = &frame; - ok = js_EmitFunctionBytecode(cx, cg, body); - cx->fp = fp; - if (!ok) - return JS_FALSE; - - if (!js_NewScriptFromCG(cx, cg, fun)) - return JS_FALSE; - - JS_ASSERT(FUN_INTERPRETED(fun)); - return JS_TRUE; -} - -/* A macro for inlining at the top of js_EmitTree (whence it came). */ -#define UPDATE_LINE_NUMBER_NOTES(cx, cg, pn) \ - JS_BEGIN_MACRO \ - uintN line_ = (pn)->pn_pos.begin.lineno; \ - uintN delta_ = line_ - CG_CURRENT_LINE(cg); \ - if (delta_ != 0) { \ - /* \ - * Encode any change in the current source line number by using \ - * either several SRC_NEWLINE notes or just one SRC_SETLINE note, \ - * whichever consumes less space. \ - * \ - * NB: We handle backward line number deltas (possible with for \ - * loops where the update part is emitted after the body, but its \ - * line number is <= any line number in the body) here by letting \ - * unsigned delta_ wrap to a very large number, which triggers a \ - * SRC_SETLINE. \ - */ \ - CG_CURRENT_LINE(cg) = line_; \ - if (delta_ >= (uintN)(2 + ((line_ > SN_3BYTE_OFFSET_MASK)<<1))) { \ - if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)line_) < 0)\ - return JS_FALSE; \ - } else { \ - do { \ - if (js_NewSrcNote(cx, cg, SRC_NEWLINE) < 0) \ - return JS_FALSE; \ - } while (--delta_ != 0); \ - } \ - } \ - JS_END_MACRO - -/* A function, so that we avoid macro-bloating all the other callsites. */ -static JSBool -UpdateLineNumberNotes(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) -{ - UPDATE_LINE_NUMBER_NOTES(cx, cg, pn); - return JS_TRUE; -} - -static JSBool -MaybeEmitVarDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, - JSParseNode *pn, jsatomid *result) -{ - jsatomid atomIndex; - JSAtomListElement *ale; - - if (pn->pn_slot >= 0) { - atomIndex = (jsatomid) pn->pn_slot; - } else { - ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList); - if (!ale) - return JS_FALSE; - atomIndex = ALE_INDEX(ale); - } - - if ((js_CodeSpec[pn->pn_op].format & JOF_TYPEMASK) == JOF_CONST && - (!(cg->treeContext.flags & TCF_IN_FUNCTION) || - (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT))) { - /* Emit a prolog bytecode to predefine the variable. */ - CG_SWITCH_TO_PROLOG(cg); - if (!UpdateLineNumberNotes(cx, cg, pn)) - return JS_FALSE; - EMIT_ATOM_INDEX_OP(prologOp, atomIndex); - CG_SWITCH_TO_MAIN(cg); - } - - if (result) - *result = atomIndex; - return JS_TRUE; -} - -#if JS_HAS_DESTRUCTURING - -typedef JSBool -(*DestructuringDeclEmitter)(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, - JSParseNode *pn); - -static JSBool -EmitDestructuringDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, - JSParseNode *pn) -{ - JS_ASSERT(pn->pn_type == TOK_NAME); - if (!BindNameToSlot(cx, &cg->treeContext, pn, prologOp == JSOP_NOP)) - return JS_FALSE; - - JS_ASSERT(pn->pn_op != JSOP_ARGUMENTS); - return MaybeEmitVarDecl(cx, cg, prologOp, pn, NULL); -} - -static JSBool -EmitDestructuringDecls(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, - JSParseNode *pn) -{ - JSParseNode *pn2, *pn3; - DestructuringDeclEmitter emitter; - - if (pn->pn_type == TOK_RB) { - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - if (pn2->pn_type == TOK_COMMA) - continue; - emitter = (pn2->pn_type == TOK_NAME) - ? EmitDestructuringDecl - : EmitDestructuringDecls; - if (!emitter(cx, cg, prologOp, pn2)) - return JS_FALSE; - } - } else { - JS_ASSERT(pn->pn_type == TOK_RC); - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - pn3 = pn2->pn_right; - emitter = (pn3->pn_type == TOK_NAME) - ? EmitDestructuringDecl - : EmitDestructuringDecls; - if (!emitter(cx, cg, prologOp, pn3)) - return JS_FALSE; - } - } - return JS_TRUE; -} - -static JSBool -EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn); - -static JSBool -EmitDestructuringLHS(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, - JSBool wantpop) -{ - jsuint slot; - - /* Skip any parenthesization. */ - while (pn->pn_type == TOK_RP) - pn = pn->pn_kid; - - /* - * Now emit the lvalue opcode sequence. If the lvalue is a nested - * destructuring initialiser-form, call ourselves to handle it, then - * pop the matched value. Otherwise emit an lvalue bytecode sequence - * ending with a JSOP_ENUMELEM or equivalent op. - */ - if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) { - if (!EmitDestructuringOpsHelper(cx, cg, pn)) - return JS_FALSE; - if (wantpop && js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - } else { - if (pn->pn_type == TOK_NAME && - !BindNameToSlot(cx, &cg->treeContext, pn, JS_FALSE)) { - return JS_FALSE; - } - - switch (pn->pn_op) { - case JSOP_SETNAME: - /* - * NB: pn is a PN_NAME node, not a PN_BINARY. Nevertheless, - * we want to emit JSOP_ENUMELEM, which has format JOF_ELEM. - * So here and for JSOP_ENUMCONSTELEM, we use EmitElemOp. - */ - if (!EmitElemOp(cx, pn, JSOP_ENUMELEM, cg)) - return JS_FALSE; - break; - - case JSOP_SETCONST: - if (!EmitElemOp(cx, pn, JSOP_ENUMCONSTELEM, cg)) - return JS_FALSE; - break; - - case JSOP_SETLOCAL: - if (wantpop) { - slot = (jsuint) pn->pn_slot; - EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, slot); - break; - } - /* FALL THROUGH */ - - case JSOP_SETARG: - case JSOP_SETVAR: - case JSOP_SETGVAR: - slot = (jsuint) pn->pn_slot; - EMIT_UINT16_IMM_OP(pn->pn_op, slot); - if (wantpop && js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - break; - - default: -#if JS_HAS_LVALUE_RETURN || JS_HAS_XML_SUPPORT - { - ptrdiff_t top; - - top = CG_OFFSET(cg); - if (!js_EmitTree(cx, cg, pn)) - return JS_FALSE; - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_ENUMELEM) < 0) - return JS_FALSE; - break; - } -#endif - case JSOP_ENUMELEM: - JS_ASSERT(0); - } - } - - return JS_TRUE; -} - -/* - * Recursive helper for EmitDestructuringOps. - * - * Given a value to destructure on the stack, walk over an object or array - * initialiser at pn, emitting bytecodes to match property values and store - * them in the lvalues identified by the matched property names. - */ -static JSBool -EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) -{ - jsuint index; - JSParseNode *pn2, *pn3; - JSBool doElemOp; - -#ifdef DEBUG - intN stackDepth = cg->stackDepth; - JS_ASSERT(stackDepth != 0); - JS_ASSERT(pn->pn_arity == PN_LIST); - JS_ASSERT(pn->pn_type == TOK_RB || pn->pn_type == TOK_RC); -#endif - - if (pn->pn_count == 0) { - /* Emit a DUP;POP sequence for the decompiler. */ - return js_Emit1(cx, cg, JSOP_DUP) >= 0 && - js_Emit1(cx, cg, JSOP_POP) >= 0; - } - - index = 0; - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - /* - * Duplicate the value being destructured to use as a reference base. - */ - if (js_Emit1(cx, cg, JSOP_DUP) < 0) - return JS_FALSE; - - /* - * Now push the property name currently being matched, which is either - * the array initialiser's current index, or the current property name - * "label" on the left of a colon in the object initialiser. Set pn3 - * to the lvalue node, which is in the value-initializing position. - */ - doElemOp = JS_TRUE; - if (pn->pn_type == TOK_RB) { - if (!EmitNumberOp(cx, index, cg)) - return JS_FALSE; - pn3 = pn2; - } else { - JS_ASSERT(pn->pn_type == TOK_RC); - JS_ASSERT(pn2->pn_type == TOK_COLON); - pn3 = pn2->pn_left; - if (pn3->pn_type == TOK_NUMBER) { - /* - * If we are emitting an object destructuring initialiser, - * annotate the index op with SRC_INITPROP so we know we are - * not decompiling an array initialiser. - */ - if (js_NewSrcNote(cx, cg, SRC_INITPROP) < 0) - return JS_FALSE; - if (!EmitNumberOp(cx, pn3->pn_dval, cg)) - return JS_FALSE; - } else { - JS_ASSERT(pn3->pn_type == TOK_STRING || - pn3->pn_type == TOK_NAME); - if (!EmitAtomOp(cx, pn3, JSOP_GETPROP, cg)) - return JS_FALSE; - doElemOp = JS_FALSE; - } - pn3 = pn2->pn_right; - } - - if (doElemOp) { - /* - * Ok, get the value of the matching property name. This leaves - * that value on top of the value being destructured, so the stack - * is one deeper than when we started. - */ - if (js_Emit1(cx, cg, JSOP_GETELEM) < 0) - return JS_FALSE; - JS_ASSERT(cg->stackDepth == stackDepth + 1); - } - - /* Nullary comma node makes a hole in the array destructurer. */ - if (pn3->pn_type == TOK_COMMA && pn3->pn_arity == PN_NULLARY) { - JS_ASSERT(pn->pn_type == TOK_RB); - JS_ASSERT(pn2 == pn3); - if (js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - } else { - if (!EmitDestructuringLHS(cx, cg, pn3, JS_TRUE)) - return JS_FALSE; - } - - JS_ASSERT(cg->stackDepth == stackDepth); - ++index; - } - - return JS_TRUE; -} - -static ptrdiff_t -OpToDeclType(JSOp op) -{ - switch (op) { - case JSOP_NOP: - return SRC_DECL_LET; - case JSOP_DEFCONST: - return SRC_DECL_CONST; - case JSOP_DEFVAR: - return SRC_DECL_VAR; - default: - return SRC_DECL_NONE; - } -} - -static JSBool -EmitDestructuringOps(JSContext *cx, JSCodeGenerator *cg, JSOp declOp, - JSParseNode *pn) -{ - /* - * If we're called from a variable declaration, help the decompiler by - * annotating the first JSOP_DUP that EmitDestructuringOpsHelper emits. - * If the destructuring initialiser is empty, our helper will emit a - * JSOP_DUP followed by a JSOP_POP for the decompiler. - */ - if (js_NewSrcNote2(cx, cg, SRC_DESTRUCT, OpToDeclType(declOp)) < 0) - return JS_FALSE; - - /* - * Call our recursive helper to emit the destructuring assignments and - * related stack manipulations. - */ - return EmitDestructuringOpsHelper(cx, cg, pn); -} - -static JSBool -EmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp declOp, - JSParseNode *lhs, JSParseNode *rhs) -{ - jsuint depth, limit, slot; - JSParseNode *pn; - - depth = limit = (uintN) cg->stackDepth; - for (pn = rhs->pn_head; pn; pn = pn->pn_next) { - if (limit == JS_BIT(16)) { - js_ReportCompileErrorNumber(cx, rhs, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_ARRAY_INIT_TOO_BIG); - return JS_FALSE; - } - - if (pn->pn_type == TOK_COMMA) { - if (js_Emit1(cx, cg, JSOP_PUSH) < 0) - return JS_FALSE; - } else { - JS_ASSERT(pn->pn_type != TOK_DEFSHARP); - if (!js_EmitTree(cx, cg, pn)) - return JS_FALSE; - } - ++limit; - } - - if (js_NewSrcNote2(cx, cg, SRC_GROUPASSIGN, OpToDeclType(declOp)) < 0) - return JS_FALSE; - - slot = depth; - for (pn = lhs->pn_head; pn; pn = pn->pn_next) { - if (slot < limit) { - EMIT_UINT16_IMM_OP(JSOP_GETLOCAL, slot); - } else { - if (js_Emit1(cx, cg, JSOP_PUSH) < 0) - return JS_FALSE; - } - if (pn->pn_type == TOK_COMMA && pn->pn_arity == PN_NULLARY) { - if (js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - } else { - if (!EmitDestructuringLHS(cx, cg, pn, pn->pn_next != NULL)) - return JS_FALSE; - } - ++slot; - } - - EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth); - cg->stackDepth = (uintN) depth; - return JS_TRUE; -} - -/* - * Helper called with pop out param initialized to a JSOP_POP* opcode. If we - * can emit a group assignment sequence, which results in 0 stack depth delta, - * we set *pop to JSOP_NOP so callers can veto emitting pn followed by a pop. - */ -static JSBool -MaybeEmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp declOp, - JSParseNode *pn, JSOp *pop) -{ - JSParseNode *lhs, *rhs; - - JS_ASSERT(pn->pn_type == TOK_ASSIGN); - JS_ASSERT(*pop == JSOP_POP || *pop == JSOP_POPV); - lhs = pn->pn_left; - rhs = pn->pn_right; - if (lhs->pn_type == TOK_RB && rhs->pn_type == TOK_RB && - lhs->pn_count <= rhs->pn_count && - (rhs->pn_count == 0 || - rhs->pn_head->pn_type != TOK_DEFSHARP)) { - if (!EmitGroupAssignment(cx, cg, declOp, lhs, rhs)) - return JS_FALSE; - *pop = JSOP_NOP; - } - return JS_TRUE; -} - -#endif /* JS_HAS_DESTRUCTURING */ - -static JSBool -EmitVariables(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, - JSBool inLetHead, ptrdiff_t *headNoteIndex) -{ - JSTreeContext *tc; - JSBool let, forInVar; -#if JS_HAS_BLOCK_SCOPE - JSBool forInLet, popScope; - JSStmtInfo *stmt, *scopeStmt; -#endif - ptrdiff_t off, noteIndex, tmp; - JSParseNode *pn2, *pn3; - JSOp op; - jsatomid atomIndex; - uintN oldflags; - - /* Default in case of JS_HAS_BLOCK_SCOPE early return, below. */ - *headNoteIndex = -1; - - /* - * Let blocks and expressions have a parenthesized head in which the new - * scope is not yet open. Initializer evaluation uses the parent node's - * lexical scope. If popScope is true below, then we hide the top lexical - * block from any calls to BindNameToSlot hiding in pn2->pn_expr so that - * it won't find any names in the new let block. - * - * The same goes for let declarations in the head of any kind of for loop. - * Unlike a let declaration 'let x = i' within a block, where x is hoisted - * to the start of the block, a 'for (let x = i...) ...' loop evaluates i - * in the containing scope, and puts x in the loop body's scope. - */ - tc = &cg->treeContext; - let = (pn->pn_op == JSOP_NOP); - forInVar = (pn->pn_extra & PNX_FORINVAR) != 0; -#if JS_HAS_BLOCK_SCOPE - forInLet = let && forInVar; - popScope = (inLetHead || (let && (tc->flags & TCF_IN_FOR_INIT))); - JS_ASSERT(!popScope || let); -#endif - - off = noteIndex = -1; - for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) { -#if JS_HAS_DESTRUCTURING - if (pn2->pn_type != TOK_NAME) { - if (pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC) { - /* - * Emit variable binding ops, but not destructuring ops. - * The parser (see Variables, jsparse.c) has ensured that - * our caller will be the TOK_FOR/TOK_IN case in js_EmitTree, - * and that case will emit the destructuring code only after - * emitting an enumerating opcode and a branch that tests - * whether the enumeration ended. - */ - JS_ASSERT(forInVar); - JS_ASSERT(pn->pn_count == 1); - if (!EmitDestructuringDecls(cx, cg, pn->pn_op, pn2)) - return JS_FALSE; - break; - } - - /* - * A destructuring initialiser assignment preceded by var is - * always evaluated promptly, even if it is to the left of 'in' - * in a for-in loop. As with 'for (var x = i in o)...', this - * will cause the entire 'var [a, b] = i' to be hoisted out of - * the head of the loop. - */ - JS_ASSERT(pn2->pn_type == TOK_ASSIGN); - if (pn->pn_count == 1 && !forInLet) { - /* - * If this is the only destructuring assignment in the list, - * try to optimize to a group assignment. If we're in a let - * head, pass JSOP_POP rather than the pseudo-prolog JSOP_NOP - * in pn->pn_op, to suppress a second (and misplaced) 'let'. - */ - JS_ASSERT(noteIndex < 0 && !pn2->pn_next); - op = JSOP_POP; - if (!MaybeEmitGroupAssignment(cx, cg, - inLetHead ? JSOP_POP : pn->pn_op, - pn2, &op)) { - return JS_FALSE; - } - if (op == JSOP_NOP) { - pn->pn_extra = (pn->pn_extra & ~PNX_POPVAR) | PNX_GROUPINIT; - break; - } - } - - pn3 = pn2->pn_left; - if (!EmitDestructuringDecls(cx, cg, pn->pn_op, pn3)) - return JS_FALSE; - -#if JS_HAS_BLOCK_SCOPE - /* - * If this is a 'for (let [x, y] = i in o) ...' let declaration, - * throw away i if it is a useless expression. - */ - if (forInLet) { - JSBool useful = JS_FALSE; - - JS_ASSERT(pn->pn_count == 1); - if (!CheckSideEffects(cx, tc, pn2->pn_right, &useful)) - return JS_FALSE; - if (!useful) - return JS_TRUE; - } -#endif - - if (!js_EmitTree(cx, cg, pn2->pn_right)) - return JS_FALSE; - -#if JS_HAS_BLOCK_SCOPE - /* - * The expression i in 'for (let [x, y] = i in o) ...', which is - * pn2->pn_right above, appears to have side effects. We've just - * emitted code to evaluate i, but we must not destructure i yet. - * Let the TOK_FOR: code in js_EmitTree do the destructuring to - * emit the right combination of source notes and bytecode for the - * decompiler. - * - * This has the effect of hoisting the evaluation of i out of the - * for-in loop, without hoisting the let variables, which must of - * course be scoped by the loop. Set PNX_POPVAR to cause JSOP_POP - * to be emitted, just before returning from this function. - */ - if (forInVar) { - pn->pn_extra |= PNX_POPVAR; - if (forInLet) - break; - } -#endif - - /* - * Veto pn->pn_op if inLetHead to avoid emitting a SRC_DESTRUCT - * that's redundant with respect to the SRC_DECL/SRC_DECL_LET that - * we will emit at the bottom of this function. - */ - if (!EmitDestructuringOps(cx, cg, - inLetHead ? JSOP_POP : pn->pn_op, - pn3)) { - return JS_FALSE; - } - goto emit_note_pop; - } -#else - JS_ASSERT(pn2->pn_type == TOK_NAME); -#endif - - if (!BindNameToSlot(cx, &cg->treeContext, pn2, let)) - return JS_FALSE; - JS_ASSERT(pn2->pn_slot >= 0 || !let); - - op = pn2->pn_op; - if (op == JSOP_ARGUMENTS) { - /* JSOP_ARGUMENTS => no initializer */ - JS_ASSERT(!pn2->pn_expr && !let); - pn3 = NULL; -#ifdef __GNUC__ - atomIndex = 0; /* quell GCC overwarning */ -#endif - } else { - if (!MaybeEmitVarDecl(cx, cg, pn->pn_op, pn2, &atomIndex)) - return JS_FALSE; - - pn3 = pn2->pn_expr; - if (pn3) { -#if JS_HAS_BLOCK_SCOPE - /* - * If this is a 'for (let x = i in o) ...' let declaration, - * throw away i if it is a useless expression. - */ - if (forInLet) { - JSBool useful = JS_FALSE; - - JS_ASSERT(pn->pn_count == 1); - if (!CheckSideEffects(cx, tc, pn3, &useful)) - return JS_FALSE; - if (!useful) - return JS_TRUE; - } -#endif - - if (op == JSOP_SETNAME) { - JS_ASSERT(!let); - EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex); - } - if (pn->pn_op == JSOP_DEFCONST && - !js_DefineCompileTimeConstant(cx, cg, pn2->pn_atom, - pn3)) { - return JS_FALSE; - } - -#if JS_HAS_BLOCK_SCOPE - /* Evaluate expr in the outer lexical scope if requested. */ - if (popScope) { - stmt = tc->topStmt; - scopeStmt = tc->topScopeStmt; - - tc->topStmt = stmt->down; - tc->topScopeStmt = scopeStmt->downScope; - } -#ifdef __GNUC__ - else { - stmt = scopeStmt = NULL; /* quell GCC overwarning */ - } -#endif -#endif - - oldflags = cg->treeContext.flags; - cg->treeContext.flags &= ~TCF_IN_FOR_INIT; - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; - -#if JS_HAS_BLOCK_SCOPE - if (popScope) { - tc->topStmt = stmt; - tc->topScopeStmt = scopeStmt; - } -#endif - } - } - - /* - * 'for (var x in o) ...' and 'for (var x = i in o) ...' call the - * TOK_VAR case, but only the initialized case (a strange one that - * falls out of ECMA-262's grammar) wants to run past this point. - * Both cases must conditionally emit a JSOP_DEFVAR, above. Note - * that the parser error-checks to ensure that pn->pn_count is 1. - * - * 'for (let x = i in o) ...' must evaluate i before the loop, and - * subject it to useless expression elimination. The variable list - * in pn is a single let declaration if pn_op == JSOP_NOP. We test - * the let local in order to break early in this case, as well as in - * the 'for (var x in o)' case. - * - * XXX Narcissus keeps track of variable declarations in the node - * for the script being compiled, so there's no need to share any - * conditional prolog code generation there. We could do likewise, - * but it's a big change, requiring extra allocation, so probably - * not worth the trouble for SpiderMonkey. - */ - JS_ASSERT(pn3 == pn2->pn_expr); - if (forInVar && (!pn3 || let)) { - JS_ASSERT(pn->pn_count == 1); - break; - } - - if (pn2 == pn->pn_head && - !inLetHead && - js_NewSrcNote2(cx, cg, SRC_DECL, - (pn->pn_op == JSOP_DEFCONST) - ? SRC_DECL_CONST - : (pn->pn_op == JSOP_DEFVAR) - ? SRC_DECL_VAR - : SRC_DECL_LET) < 0) { - return JS_FALSE; - } - if (op == JSOP_ARGUMENTS) { - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } else if (pn2->pn_slot >= 0) { - EMIT_UINT16_IMM_OP(op, atomIndex); - } else { - EMIT_ATOM_INDEX_OP(op, atomIndex); - } - -#if JS_HAS_DESTRUCTURING - emit_note_pop: -#endif - tmp = CG_OFFSET(cg); - if (noteIndex >= 0) { - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off)) - return JS_FALSE; - } - if (!pn2->pn_next) - break; - off = tmp; - noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); - if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - } - - /* If this is a let head, emit and return a srcnote on the pop. */ - if (inLetHead) { - *headNoteIndex = js_NewSrcNote(cx, cg, SRC_DECL); - if (*headNoteIndex < 0) - return JS_FALSE; - if (!(pn->pn_extra & PNX_POPVAR)) - return js_Emit1(cx, cg, JSOP_NOP) >= 0; - } - - return !(pn->pn_extra & PNX_POPVAR) || js_Emit1(cx, cg, JSOP_POP) >= 0; -} - -#if defined DEBUG_brendan || defined DEBUG_mrbkap -static JSBool -GettableNoteForNextOp(JSCodeGenerator *cg) -{ - ptrdiff_t offset, target; - jssrcnote *sn, *end; - - offset = 0; - target = CG_OFFSET(cg); - for (sn = CG_NOTES(cg), end = sn + CG_NOTE_COUNT(cg); sn < end; - sn = SN_NEXT(sn)) { - if (offset == target && SN_IS_GETTABLE(sn)) - return JS_TRUE; - offset += SN_DELTA(sn); - } - return JS_FALSE; -} -#endif - -JSBool -js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) -{ - JSBool ok, useful, wantval; - JSStmtInfo *stmt, stmtInfo; - ptrdiff_t top, off, tmp, beq, jmp; - JSParseNode *pn2, *pn3; - JSAtom *atom; - JSAtomListElement *ale; - jsatomid atomIndex; - ptrdiff_t noteIndex; - JSSrcNoteType noteType; - jsbytecode *pc; - JSOp op; - JSTokenType type; - uint32 argc; - int stackDummy; - - if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); - return JS_FALSE; - } - - ok = JS_TRUE; - cg->emitLevel++; - pn->pn_offset = top = CG_OFFSET(cg); - - /* Emit notes to tell the current bytecode's source line number. */ - UPDATE_LINE_NUMBER_NOTES(cx, cg, pn); - - switch (pn->pn_type) { - case TOK_FUNCTION: - { - void *cg2mark; - JSCodeGenerator *cg2; - JSFunction *fun; - -#if JS_HAS_XML_SUPPORT - if (pn->pn_arity == PN_NULLARY) { - if (js_Emit1(cx, cg, JSOP_GETFUNNS) < 0) - return JS_FALSE; - break; - } -#endif - - /* Generate code for the function's body. */ - cg2mark = JS_ARENA_MARK(&cx->tempPool); - JS_ARENA_ALLOCATE_TYPE(cg2, JSCodeGenerator, &cx->tempPool); - if (!cg2) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - if (!js_InitCodeGenerator(cx, cg2, cg->codePool, cg->notePool, - cg->filename, pn->pn_pos.begin.lineno, - cg->principals)) { - return JS_FALSE; - } - cg2->treeContext.flags = (uint16) (pn->pn_flags | TCF_IN_FUNCTION); - cg2->treeContext.tryCount = pn->pn_tryCount; - cg2->parent = cg; - fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(pn->pn_funAtom)); - if (!js_EmitFunctionBody(cx, cg2, pn->pn_body, fun)) - return JS_FALSE; - - /* - * We need an activation object if an inner peeks out, or if such - * inner-peeking caused one of our inners to become heavyweight. - */ - if (cg2->treeContext.flags & - (TCF_FUN_USES_NONLOCALS | TCF_FUN_HEAVYWEIGHT)) { - cg->treeContext.flags |= TCF_FUN_HEAVYWEIGHT; - } - js_FinishCodeGenerator(cx, cg2); - JS_ARENA_RELEASE(&cx->tempPool, cg2mark); - - /* Make the function object a literal in the outer script's pool. */ - ale = js_IndexAtom(cx, pn->pn_funAtom, &cg->atomList); - if (!ale) - return JS_FALSE; - atomIndex = ALE_INDEX(ale); - - /* Emit a bytecode pointing to the closure object in its immediate. */ - if (pn->pn_op != JSOP_NOP) { - EMIT_ATOM_INDEX_OP(pn->pn_op, atomIndex); - break; - } - - /* Top-level named functions need a nop for decompilation. */ - noteIndex = js_NewSrcNote2(cx, cg, SRC_FUNCDEF, (ptrdiff_t)atomIndex); - if (noteIndex < 0 || - js_Emit1(cx, cg, JSOP_NOP) < 0) { - return JS_FALSE; - } - - /* - * Top-levels also need a prolog op to predefine their names in the - * variable object, or if local, to fill their stack slots. - */ - CG_SWITCH_TO_PROLOG(cg); - - if (cg->treeContext.flags & TCF_IN_FUNCTION) { - JSObject *obj, *pobj; - JSProperty *prop; - JSScopeProperty *sprop; - uintN slot; - - obj = OBJ_GET_PARENT(cx, fun->object); - if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(fun->atom), - &pobj, &prop)) { - return JS_FALSE; - } - - JS_ASSERT(prop && pobj == obj); - sprop = (JSScopeProperty *) prop; - JS_ASSERT(sprop->getter == js_GetLocalVariable); - slot = sprop->shortid; - OBJ_DROP_PROPERTY(cx, pobj, prop); - - /* - * If this local function is declared in a body block induced by - * let declarations, reparent fun->object to the compiler-created - * body block object so that JSOP_DEFLOCALFUN can clone that block - * into the runtime scope chain. - */ - stmt = cg->treeContext.topStmt; - if (stmt && stmt->type == STMT_BLOCK && - stmt->down && stmt->down->type == STMT_BLOCK && - (stmt->down->flags & SIF_SCOPE)) { - obj = ATOM_TO_OBJECT(stmt->down->atom); - JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass); - OBJ_SET_PARENT(cx, fun->object, obj); - } - - if (atomIndex >= JS_BIT(16)) { - /* - * Lots of literals in the outer function, so we have to emit - * [JSOP_LITOPX, atomIndex, JSOP_DEFLOCALFUN, var slot]. - */ - off = js_EmitN(cx, cg, JSOP_LITOPX, 3); - if (off < 0) - return JS_FALSE; - pc = CG_CODE(cg, off); - SET_LITERAL_INDEX(pc, atomIndex); - EMIT_UINT16_IMM_OP(JSOP_DEFLOCALFUN, slot); - } else { - /* Emit [JSOP_DEFLOCALFUN, var slot, atomIndex]. */ - off = js_EmitN(cx, cg, JSOP_DEFLOCALFUN, - VARNO_LEN + ATOM_INDEX_LEN); - if (off < 0) - return JS_FALSE; - pc = CG_CODE(cg, off); - SET_VARNO(pc, slot); - pc += VARNO_LEN; - SET_ATOM_INDEX(pc, atomIndex); - } - } else { - JS_ASSERT(!cg->treeContext.topStmt); - EMIT_ATOM_INDEX_OP(JSOP_DEFFUN, atomIndex); - } - - CG_SWITCH_TO_MAIN(cg); - break; - } - -#if JS_HAS_EXPORT_IMPORT - case TOK_EXPORT: - pn2 = pn->pn_head; - if (pn2->pn_type == TOK_STAR) { - /* - * 'export *' must have no other elements in the list (what would - * be the point?). - */ - if (js_Emit1(cx, cg, JSOP_EXPORTALL) < 0) - return JS_FALSE; - } else { - /* - * If not 'export *', the list consists of NAME nodes identifying - * properties of the variables object to flag as exported. - */ - do { - ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList); - if (!ale) - return JS_FALSE; - EMIT_ATOM_INDEX_OP(JSOP_EXPORTNAME, ALE_INDEX(ale)); - } while ((pn2 = pn2->pn_next) != NULL); - } - break; - - case TOK_IMPORT: - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - /* - * Each subtree on an import list is rooted by a DOT or LB node. - * A DOT may have a null pn_atom member, in which case pn_op must - * be JSOP_IMPORTALL -- see EmitPropOp above. - */ - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - } - break; -#endif /* JS_HAS_EXPORT_IMPORT */ - - case TOK_IF: - /* Initialize so we can detect else-if chains and avoid recursion. */ - stmtInfo.type = STMT_IF; - beq = jmp = -1; - noteIndex = -1; - - if_again: - /* Emit code for the condition before pushing stmtInfo. */ - if (!js_EmitTree(cx, cg, pn->pn_kid1)) - return JS_FALSE; - top = CG_OFFSET(cg); - if (stmtInfo.type == STMT_IF) { - js_PushStatement(&cg->treeContext, &stmtInfo, STMT_IF, top); - } else { - /* - * We came here from the goto further below that detects else-if - * chains, so we must mutate stmtInfo back into a STMT_IF record. - * Also (see below for why) we need a note offset for SRC_IF_ELSE - * to help the decompiler. Actually, we need two offsets, one for - * decompiling any else clause and the second for decompiling an - * else-if chain without bracing, overindenting, or incorrectly - * scoping let declarations. - */ - JS_ASSERT(stmtInfo.type == STMT_ELSE); - stmtInfo.type = STMT_IF; - stmtInfo.update = top; - if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq)) - return JS_FALSE; - if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 1, top - jmp)) - return JS_FALSE; - } - - /* Emit an annotated branch-if-false around the then part. */ - pn3 = pn->pn_kid3; - noteIndex = js_NewSrcNote(cx, cg, pn3 ? SRC_IF_ELSE : SRC_IF); - if (noteIndex < 0) - return JS_FALSE; - beq = EmitJump(cx, cg, JSOP_IFEQ, 0); - if (beq < 0) - return JS_FALSE; - - /* Emit code for the then and optional else parts. */ - if (!js_EmitTree(cx, cg, pn->pn_kid2)) - return JS_FALSE; - if (pn3) { - /* Modify stmtInfo so we know we're in the else part. */ - stmtInfo.type = STMT_ELSE; - - /* - * Emit a JSOP_BACKPATCH op to jump from the end of our then part - * around the else part. The js_PopStatementCG call at the bottom - * of this switch case will fix up the backpatch chain linked from - * stmtInfo.breaks. - */ - jmp = EmitGoto(cx, cg, &stmtInfo, &stmtInfo.breaks, NULL, SRC_NULL); - if (jmp < 0) - return JS_FALSE; - - /* Ensure the branch-if-false comes here, then emit the else. */ - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); - if (pn3->pn_type == TOK_IF) { - pn = pn3; - goto if_again; - } - - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - - /* - * Annotate SRC_IF_ELSE with the offset from branch to jump, for - * the decompiler's benefit. We can't just "back up" from the pc - * of the else clause, because we don't know whether an extended - * jump was required to leap from the end of the then clause over - * the else clause. - */ - if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq)) - return JS_FALSE; - } else { - /* No else part, fixup the branch-if-false to come here. */ - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); - } - ok = js_PopStatementCG(cx, cg); - break; - - case TOK_SWITCH: - /* Out of line to avoid bloating js_EmitTree's stack frame size. */ - ok = EmitSwitch(cx, cg, pn, &stmtInfo); - break; - - case TOK_WHILE: - js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WHILE_LOOP, top); - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE); - if (noteIndex < 0) - return JS_FALSE; - beq = EmitJump(cx, cg, JSOP_IFEQ, 0); - if (beq < 0) - return JS_FALSE; - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; - jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg)); - if (jmp < 0) - return JS_FALSE; - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); - if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq)) - return JS_FALSE; - ok = js_PopStatementCG(cx, cg); - break; - - case TOK_DO: - /* Emit an annotated nop so we know to decompile a 'do' keyword. */ - if (js_NewSrcNote(cx, cg, SRC_WHILE) < 0 || - js_Emit1(cx, cg, JSOP_NOP) < 0) { - return JS_FALSE; - } - - /* Compile the loop body. */ - top = CG_OFFSET(cg); - js_PushStatement(&cg->treeContext, &stmtInfo, STMT_DO_LOOP, top); - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - - /* Set loop and enclosing label update offsets, for continue. */ - stmt = &stmtInfo; - do { - stmt->update = CG_OFFSET(cg); - } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL); - - /* Compile the loop condition, now that continues know where to go. */ - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; - - /* - * No source note needed, because JSOP_IFNE is used only for do-while. - * If we ever use JSOP_IFNE for other purposes, we can still avoid yet - * another note here, by storing (jmp - top) in the SRC_WHILE note's - * offset, and fetching that delta in order to decompile recursively. - */ - if (EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg)) < 0) - return JS_FALSE; - ok = js_PopStatementCG(cx, cg); - break; - - case TOK_FOR: - beq = 0; /* suppress gcc warnings */ - pn2 = pn->pn_left; - js_PushStatement(&cg->treeContext, &stmtInfo, STMT_FOR_LOOP, top); - - if (pn2->pn_type == TOK_IN) { - JSBool emitIFEQ; - - /* Set stmtInfo type for later testing. */ - stmtInfo.type = STMT_FOR_IN_LOOP; - noteIndex = -1; - - /* - * If the left part is 'var x', emit code to define x if necessary - * using a prolog opcode, but do not emit a pop. If the left part - * is 'var x = i', emit prolog code to define x if necessary; then - * emit code to evaluate i, assign the result to x, and pop the - * result off the stack. - * - * All the logic to do this is implemented in the outer switch's - * TOK_VAR case, conditioned on pn_extra flags set by the parser. - * - * In the 'for (var x = i in o) ...' case, the js_EmitTree(...pn3) - * called here will generate the proper note for the assignment - * op that sets x = i, hoisting the initialized var declaration - * out of the loop: 'var x = i; for (x in o) ...'. - * - * In the 'for (var x in o) ...' case, nothing but the prolog op - * (if needed) should be generated here, we must emit the note - * just before the JSOP_FOR* opcode in the switch on pn3->pn_type - * a bit below, so nothing is hoisted: 'for (var x in o) ...'. - * - * A 'for (let x = i in o)' loop must not be hoisted, since in - * this form the let variable is scoped by the loop body (but not - * the head). The initializer expression i must be evaluated for - * any side effects. So we hoist only i in the let case. - */ - pn3 = pn2->pn_left; - type = pn3->pn_type; - cg->treeContext.flags |= TCF_IN_FOR_INIT; - if (TOKEN_TYPE_IS_DECL(type) && !js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - cg->treeContext.flags &= ~TCF_IN_FOR_INIT; - - /* Emit a push to allocate the iterator. */ - if (js_Emit1(cx, cg, JSOP_STARTITER) < 0) - return JS_FALSE; - - /* Compile the object expression to the right of 'in'. */ - if (!js_EmitTree(cx, cg, pn2->pn_right)) - return JS_FALSE; - - /* - * Emit a bytecode to convert top of stack value to the iterator - * object depending on the loop variant (for-in, for-each-in, or - * destructuring for-in). - */ -#if JS_HAS_DESTRUCTURING - JS_ASSERT(pn->pn_op == JSOP_FORIN || - pn->pn_op == JSOP_FOREACHKEYVAL || - pn->pn_op == JSOP_FOREACH); -#else - JS_ASSERT(pn->pn_op == JSOP_FORIN || pn->pn_op == JSOP_FOREACH); -#endif - if (js_Emit1(cx, cg, pn->pn_op) < 0) - return JS_FALSE; - - top = CG_OFFSET(cg); - SET_STATEMENT_TOP(&stmtInfo, top); - - /* - * Compile a JSOP_FOR* bytecode based on the left hand side. - * - * Initialize op to JSOP_SETNAME in case of |for ([a, b] in o)...| - * or similar, to signify assignment, rather than declaration, to - * the decompiler. EmitDestructuringOps takes a prolog bytecode - * parameter and emits the appropriate source note, defaulting to - * assignment, so JSOP_SETNAME is not critical here; many similar - * ops could be used -- just not JSOP_NOP (which means 'let'). - */ - emitIFEQ = JS_TRUE; - op = JSOP_SETNAME; - switch (type) { -#if JS_HAS_BLOCK_SCOPE - case TOK_LET: -#endif - case TOK_VAR: - JS_ASSERT(pn3->pn_arity == PN_LIST && pn3->pn_count == 1); - pn3 = pn3->pn_head; -#if JS_HAS_DESTRUCTURING - if (pn3->pn_type == TOK_ASSIGN) { - pn3 = pn3->pn_left; - JS_ASSERT(pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC); - } - if (pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC) { - op = pn2->pn_left->pn_op; - goto destructuring_for; - } -#else - JS_ASSERT(pn3->pn_type == TOK_NAME); -#endif - /* - * Always annotate JSOP_FORLOCAL if given input of the form - * 'for (let x in * o)' -- the decompiler must not hoist the - * 'let x' out of the loop head, or x will be bound in the - * wrong scope. Likewise, but in this case only for the sake - * of higher decompilation fidelity only, do not hoist 'var x' - * when given 'for (var x in o)'. But 'for (var x = i in o)' - * requires hoisting in order to preserve the initializer i. - * The decompiler can only handle so much! - */ - if (( -#if JS_HAS_BLOCK_SCOPE - type == TOK_LET || -#endif - !pn3->pn_expr) && - js_NewSrcNote2(cx, cg, SRC_DECL, - type == TOK_VAR - ? SRC_DECL_VAR - : SRC_DECL_LET) < 0) { - return JS_FALSE; - } - /* FALL THROUGH */ - case TOK_NAME: - if (pn3->pn_slot >= 0) { - op = pn3->pn_op; - switch (op) { - case JSOP_GETARG: /* FALL THROUGH */ - case JSOP_SETARG: op = JSOP_FORARG; break; - case JSOP_GETVAR: /* FALL THROUGH */ - case JSOP_SETVAR: op = JSOP_FORVAR; break; - case JSOP_GETGVAR: /* FALL THROUGH */ - case JSOP_SETGVAR: op = JSOP_FORNAME; break; - case JSOP_GETLOCAL: /* FALL THROUGH */ - case JSOP_SETLOCAL: op = JSOP_FORLOCAL; break; - default: JS_ASSERT(0); - } - } else { - pn3->pn_op = JSOP_FORNAME; - if (!BindNameToSlot(cx, &cg->treeContext, pn3, JS_FALSE)) - return JS_FALSE; - op = pn3->pn_op; - } - if (pn3->pn_slot >= 0) { - if (pn3->pn_attrs & JSPROP_READONLY) { - JS_ASSERT(op == JSOP_FORVAR); - op = JSOP_GETVAR; - } - atomIndex = (jsatomid) pn3->pn_slot; - EMIT_UINT16_IMM_OP(op, atomIndex); - } else { - if (!EmitAtomOp(cx, pn3, op, cg)) - return JS_FALSE; - } - break; - - case TOK_DOT: - useful = JS_FALSE; - if (!CheckSideEffects(cx, &cg->treeContext, pn3->pn_expr, - &useful)) { - return JS_FALSE; - } - if (!useful) { - if (!EmitPropOp(cx, pn3, JSOP_FORPROP, cg)) - return JS_FALSE; - break; - } - /* FALL THROUGH */ - -#if JS_HAS_DESTRUCTURING - case TOK_RB: - case TOK_RC: - destructuring_for: -#endif -#if JS_HAS_XML_SUPPORT - case TOK_UNARYOP: -#endif -#if JS_HAS_LVALUE_RETURN - case TOK_LP: -#endif - case TOK_LB: - /* - * We separate the first/next bytecode from the enumerator - * variable binding to avoid any side-effects in the index - * expression (e.g., for (x[i++] in {}) should not bind x[i] - * or increment i at all). - */ - emitIFEQ = JS_FALSE; - if (!js_Emit1(cx, cg, JSOP_FORELEM)) - return JS_FALSE; - - /* - * Emit a SRC_WHILE note with offset telling the distance to - * the loop-closing jump (we can't reckon from the branch at - * the top of the loop, because the loop-closing jump might - * need to be an extended jump, independent of whether the - * branch is short or long). - */ - noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE); - if (noteIndex < 0) - return JS_FALSE; - beq = EmitJump(cx, cg, JSOP_IFEQ, 0); - if (beq < 0) - return JS_FALSE; - -#if JS_HAS_DESTRUCTURING - if (pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC) { - if (!EmitDestructuringOps(cx, cg, op, pn3)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - break; - } -#endif -#if JS_HAS_LVALUE_RETURN - if (pn3->pn_type == TOK_LP) { - JS_ASSERT(pn3->pn_op == JSOP_SETCALL); - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - if (!js_Emit1(cx, cg, JSOP_ENUMELEM)) - return JS_FALSE; - break; - } -#endif -#if JS_HAS_XML_SUPPORT - if (pn3->pn_type == TOK_UNARYOP) { - JS_ASSERT(pn3->pn_op == JSOP_BINDXMLNAME); - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - if (!js_Emit1(cx, cg, JSOP_ENUMELEM)) - return JS_FALSE; - break; - } -#endif - - /* Now that we're safely past the IFEQ, commit side effects. */ - if (!EmitElemOp(cx, pn3, JSOP_ENUMELEM, cg)) - return JS_FALSE; - break; - - default: - JS_ASSERT(0); - } - - if (emitIFEQ) { - /* Annotate so the decompiler can find the loop-closing jump. */ - noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE); - if (noteIndex < 0) - return JS_FALSE; - - /* Pop and test the loop condition generated by JSOP_FOR*. */ - beq = EmitJump(cx, cg, JSOP_IFEQ, 0); - if (beq < 0) - return JS_FALSE; - } - } else { - op = JSOP_POP; - if (!pn2->pn_kid1) { - /* No initializer: emit an annotated nop for the decompiler. */ - op = JSOP_NOP; - } else { - cg->treeContext.flags |= TCF_IN_FOR_INIT; -#if JS_HAS_DESTRUCTURING - pn3 = pn2->pn_kid1; - if (pn3->pn_type == TOK_ASSIGN && - !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) { - return JS_FALSE; - } -#endif - if (op == JSOP_POP) { - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - if (TOKEN_TYPE_IS_DECL(pn3->pn_type)) { - /* - * Check whether a destructuring-initialized var decl - * was optimized to a group assignment. If so, we do - * not need to emit a pop below, so switch to a nop, - * just for the decompiler. - */ - JS_ASSERT(pn3->pn_arity == PN_LIST); - if (pn3->pn_extra & PNX_GROUPINIT) - op = JSOP_NOP; - } - } - cg->treeContext.flags &= ~TCF_IN_FOR_INIT; - } - noteIndex = js_NewSrcNote(cx, cg, SRC_FOR); - if (noteIndex < 0 || - js_Emit1(cx, cg, op) < 0) { - return JS_FALSE; - } - - top = CG_OFFSET(cg); - SET_STATEMENT_TOP(&stmtInfo, top); - if (!pn2->pn_kid2) { - /* No loop condition: flag this fact in the source notes. */ - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, 0)) - return JS_FALSE; - } else { - if (!js_EmitTree(cx, cg, pn2->pn_kid2)) - return JS_FALSE; - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, - CG_OFFSET(cg) - top)) { - return JS_FALSE; - } - beq = EmitJump(cx, cg, JSOP_IFEQ, 0); - if (beq < 0) - return JS_FALSE; - } - - /* Set pn3 (used below) here to avoid spurious gcc warnings. */ - pn3 = pn2->pn_kid3; - } - - /* Emit code for the loop body. */ - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; - - if (pn2->pn_type != TOK_IN) { - /* Set the second note offset so we can find the update part. */ - JS_ASSERT(noteIndex != -1); - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, - CG_OFFSET(cg) - top)) { - return JS_FALSE; - } - - if (pn3) { - /* Set loop and enclosing "update" offsets, for continue. */ - stmt = &stmtInfo; - do { - stmt->update = CG_OFFSET(cg); - } while ((stmt = stmt->down) != NULL && - stmt->type == STMT_LABEL); - - op = JSOP_POP; -#if JS_HAS_DESTRUCTURING - if (pn3->pn_type == TOK_ASSIGN && - !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) { - return JS_FALSE; - } -#endif - if (op == JSOP_POP) { - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } - - /* Restore the absolute line number for source note readers. */ - off = (ptrdiff_t) pn->pn_pos.end.lineno; - if (CG_CURRENT_LINE(cg) != (uintN) off) { - if (js_NewSrcNote2(cx, cg, SRC_SETLINE, off) < 0) - return JS_FALSE; - CG_CURRENT_LINE(cg) = (uintN) off; - } - } - - /* The third note offset helps us find the loop-closing jump. */ - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 2, - CG_OFFSET(cg) - top)) { - return JS_FALSE; - } - } - - /* Emit the loop-closing jump and fixup all jump offsets. */ - jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg)); - if (jmp < 0) - return JS_FALSE; - if (beq > 0) - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); - if (pn2->pn_type == TOK_IN) { - /* Set the SRC_WHILE note offset so we can find the closing jump. */ - JS_ASSERT(noteIndex != -1); - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, jmp - beq)) - return JS_FALSE; - } - - /* Now fixup all breaks and continues (before for/in's JSOP_ENDITER). */ - if (!js_PopStatementCG(cx, cg)) - return JS_FALSE; - - if (pn2->pn_type == TOK_IN) { - if (js_Emit1(cx, cg, JSOP_ENDITER) < 0) - return JS_FALSE; - } - break; - - case TOK_BREAK: - stmt = cg->treeContext.topStmt; - atom = pn->pn_atom; - if (atom) { - ale = js_IndexAtom(cx, atom, &cg->atomList); - if (!ale) - return JS_FALSE; - while (stmt->type != STMT_LABEL || stmt->atom != atom) - stmt = stmt->down; - noteType = SRC_BREAK2LABEL; - } else { - ale = NULL; - while (!STMT_IS_LOOP(stmt) && stmt->type != STMT_SWITCH) - stmt = stmt->down; - noteType = SRC_NULL; - } - - if (EmitGoto(cx, cg, stmt, &stmt->breaks, ale, noteType) < 0) - return JS_FALSE; - break; - - case TOK_CONTINUE: - stmt = cg->treeContext.topStmt; - atom = pn->pn_atom; - if (atom) { - /* Find the loop statement enclosed by the matching label. */ - JSStmtInfo *loop = NULL; - ale = js_IndexAtom(cx, atom, &cg->atomList); - if (!ale) - return JS_FALSE; - while (stmt->type != STMT_LABEL || stmt->atom != atom) { - if (STMT_IS_LOOP(stmt)) - loop = stmt; - stmt = stmt->down; - } - stmt = loop; - noteType = SRC_CONT2LABEL; - } else { - ale = NULL; - while (!STMT_IS_LOOP(stmt)) - stmt = stmt->down; - noteType = SRC_CONTINUE; - } - - if (EmitGoto(cx, cg, stmt, &stmt->continues, ale, noteType) < 0) - return JS_FALSE; - break; - - case TOK_WITH: - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WITH, CG_OFFSET(cg)); - if (js_Emit1(cx, cg, JSOP_ENTERWITH) < 0) - return JS_FALSE; - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) - return JS_FALSE; - ok = js_PopStatementCG(cx, cg); - break; - - case TOK_TRY: - { - ptrdiff_t start, end, catchJump, catchStart, finallyCatch; - intN depth; - JSParseNode *lastCatch; - - catchJump = catchStart = finallyCatch = -1; - - /* - * Push stmtInfo to track jumps-over-catches and gosubs-to-finally - * for later fixup. - * - * When a finally block is 'active' (STMT_FINALLY on the treeContext), - * non-local jumps (including jumps-over-catches) result in a GOSUB - * being written into the bytecode stream and fixed-up later (c.f. - * EmitBackPatchOp and BackPatch). - */ - js_PushStatement(&cg->treeContext, &stmtInfo, - pn->pn_kid3 ? STMT_FINALLY : STMT_TRY, - CG_OFFSET(cg)); - - /* - * About JSOP_SETSP: an exception can be thrown while the stack is in - * an unbalanced state, and this imbalance causes problems with things - * like function invocation later on. - * - * To fix this, we compute the 'balanced' stack depth upon try entry, - * and then restore the stack to this depth when we hit the first catch - * or finally block. We can't just zero the stack, because things like - * for/in and with that are active upon entry to the block keep state - * variables on the stack. - */ - depth = cg->stackDepth; - - /* Mark try location for decompilation, then emit try block. */ - if (js_Emit1(cx, cg, JSOP_TRY) < 0) - return JS_FALSE; - start = CG_OFFSET(cg); - if (!js_EmitTree(cx, cg, pn->pn_kid1)) - return JS_FALSE; - - /* GOSUB to finally, if present. */ - if (pn->pn_kid3) { - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &GOSUBS(stmtInfo)); - if (jmp < 0) - return JS_FALSE; - - /* JSOP_RETSUB pops the return pc-index, balancing the stack. */ - cg->stackDepth = depth; - } - - /* Emit (hidden) jump over catch and/or finally. */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &catchJump); - if (jmp < 0) - return JS_FALSE; - - end = CG_OFFSET(cg); - - /* If this try has a catch block, emit it. */ - pn2 = pn->pn_kid2; - lastCatch = NULL; - if (pn2) { - jsint count = 0; /* previous catch block's population */ - - catchStart = end; - - /* - * The emitted code for a catch block looks like: - * - * [throwing] only if 2nd+ catch block - * [leaveblock] only if 2nd+ catch block - * enterblock with SRC_CATCH - * exception - * [dup] only if catchguard - * setlocalpop or destructuring code - * [< catchguard code >] if there's a catchguard - * [ifeq ] " " - * [pop] only if catchguard - * < catch block contents > - * leaveblock - * goto non-local; finally applies - * - * If there's no catch block without a catchguard, the last - * points to rethrow code. This - * code will [gosub] to the finally code if appropriate, and is - * also used for the catch-all trynote for capturing exceptions - * thrown from catch{} blocks. - */ - for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { - ptrdiff_t guardJump, catchNote; - - guardJump = GUARDJUMP(stmtInfo); - if (guardJump == -1) { - /* Set stack to original depth (see SETSP comment above). */ - EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth); - cg->stackDepth = depth; - } else { - /* Fix up and clean up previous catch block. */ - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, guardJump); - - /* - * Account for the pushed exception object that we still - * have after the jumping from the previous guard. - */ - JS_ASSERT(cg->stackDepth == depth); - cg->stackDepth = depth + 1; - - /* - * Move exception back to cx->exception to prepare for - * the next catch. We hide [throwing] from the decompiler - * since it compensates for the hidden JSOP_DUP at the - * start of the previous guarded catch. - */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || - js_Emit1(cx, cg, JSOP_THROWING) < 0) { - return JS_FALSE; - } - - /* - * Emit an unbalanced [leaveblock] for the previous catch, - * whose block object count is saved below. - */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - JS_ASSERT(count >= 0); - EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count); - } - - /* - * Annotate the JSOP_ENTERBLOCK that's about to be generated - * by the call to js_EmitTree immediately below. Save this - * source note's index in stmtInfo for use by the TOK_CATCH: - * case, where the length of the catch guard is set as the - * note's offset. - */ - catchNote = js_NewSrcNote2(cx, cg, SRC_CATCH, 0); - if (catchNote < 0) - return JS_FALSE; - CATCHNOTE(stmtInfo) = catchNote; - - /* - * Emit the lexical scope and catch body. Save the catch's - * block object population via count, for use when targeting - * guardJump at the next catch (the guard mismatch case). - */ - JS_ASSERT(pn3->pn_type == TOK_LEXICALSCOPE); - count = OBJ_BLOCK_COUNT(cx, ATOM_TO_OBJECT(pn3->pn_atom)); - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - - /* gosub , if required */ - if (pn->pn_kid3) { - jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, - &GOSUBS(stmtInfo)); - if (jmp < 0) - return JS_FALSE; - JS_ASSERT(cg->stackDepth == depth); - } - - /* - * Jump over the remaining catch blocks. This will get fixed - * up to jump to after catch/finally. - */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &catchJump); - if (jmp < 0) - return JS_FALSE; - - /* - * Save a pointer to the last catch node to handle try-finally - * and try-catch(guard)-finally special cases. - */ - lastCatch = pn3->pn_expr; - } - } - - /* - * Last catch guard jumps to the rethrow code sequence if none of the - * guards match. Target guardJump at the beginning of the rethrow - * sequence, just in case a guard expression throws and leaves the - * stack unbalanced. - */ - if (lastCatch && lastCatch->pn_kid2) { - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, GUARDJUMP(stmtInfo)); - - /* Sync the stack to take into account pushed exception. */ - JS_ASSERT(cg->stackDepth == depth); - cg->stackDepth = depth + 1; - - /* - * Rethrow the exception, delegating executing of finally if any - * to the exception handler. - */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || - js_Emit1(cx, cg, JSOP_THROW) < 0) { - return JS_FALSE; - } - } - - JS_ASSERT(cg->stackDepth == depth); - - /* Emit finally handler if any. */ - if (pn->pn_kid3) { - /* - * We emit [setsp][gosub] to call try-finally when an exception is - * thrown from try or try-catch blocks. The [gosub] and [retsub] - * opcodes will take care of stacking and rethrowing any exception - * pending across the finally. - */ - finallyCatch = CG_OFFSET(cg); - EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth); - - jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, - &GOSUBS(stmtInfo)); - if (jmp < 0) - return JS_FALSE; - - JS_ASSERT(cg->stackDepth == depth); - JS_ASSERT((uintN)depth <= cg->maxStackDepth); - - /* - * Fix up the gosubs that might have been emitted before non-local - * jumps to the finally code. - */ - if (!BackPatch(cx, cg, GOSUBS(stmtInfo), CG_NEXT(cg), JSOP_GOSUB)) - return JS_FALSE; - - /* - * The stack budget must be balanced at this point. All [gosub] - * calls emitted before this point will push two stack slots, one - * for the pending exception (or JSVAL_HOLE if there is no pending - * exception) and one for the [retsub] pc-index. - */ - JS_ASSERT(cg->stackDepth == depth); - cg->stackDepth += 2; - if ((uintN)cg->stackDepth > cg->maxStackDepth) - cg->maxStackDepth = cg->stackDepth; - - /* Now indicate that we're emitting a subroutine body. */ - stmtInfo.type = STMT_SUBROUTINE; - if (!UpdateLineNumberNotes(cx, cg, pn->pn_kid3)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_FINALLY) < 0 || - !js_EmitTree(cx, cg, pn->pn_kid3) || - js_Emit1(cx, cg, JSOP_RETSUB) < 0) { - return JS_FALSE; - } - - /* Restore stack depth budget to its balanced state. */ - JS_ASSERT(cg->stackDepth == depth + 2); - cg->stackDepth = depth; - } - if (!js_PopStatementCG(cx, cg)) - return JS_FALSE; - - if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 || - js_Emit1(cx, cg, JSOP_NOP) < 0) { - return JS_FALSE; - } - - /* Fix up the end-of-try/catch jumps to come here. */ - if (!BackPatch(cx, cg, catchJump, CG_NEXT(cg), JSOP_GOTO)) - return JS_FALSE; - - /* - * Add the try note last, to let post-order give us the right ordering - * (first to last for a given nesting level, inner to outer by level). - */ - if (pn->pn_kid2) { - JS_ASSERT(end != -1 && catchStart != -1); - if (!js_NewTryNote(cx, cg, start, end, catchStart)) - return JS_FALSE; - } - - /* - * If we've got a finally, mark try+catch region with additional - * trynote to catch exceptions (re)thrown from a catch block or - * for the try{}finally{} case. - */ - if (pn->pn_kid3) { - JS_ASSERT(finallyCatch != -1); - if (!js_NewTryNote(cx, cg, start, finallyCatch, finallyCatch)) - return JS_FALSE; - } - break; - } - - case TOK_CATCH: - { - ptrdiff_t catchStart, guardJump; - - /* - * Morph STMT_BLOCK to STMT_CATCH, note the block entry code offset, - * and save the block object atom. - */ - stmt = cg->treeContext.topStmt; - JS_ASSERT(stmt->type == STMT_BLOCK && (stmt->flags & SIF_SCOPE)); - stmt->type = STMT_CATCH; - catchStart = stmt->update; - atom = stmt->atom; - - /* Go up one statement info record to the TRY or FINALLY record. */ - stmt = stmt->down; - JS_ASSERT(stmt->type == STMT_TRY || stmt->type == STMT_FINALLY); - - /* Pick up the pending exception and bind it to the catch variable. */ - if (js_Emit1(cx, cg, JSOP_EXCEPTION) < 0) - return JS_FALSE; - - /* - * Dup the exception object if there is a guard for rethrowing to use - * it later when rethrowing or in other catches. - */ - if (pn->pn_kid2) { - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || - js_Emit1(cx, cg, JSOP_DUP) < 0) { - return JS_FALSE; - } - } - - pn2 = pn->pn_kid1; - switch (pn2->pn_type) { -#if JS_HAS_DESTRUCTURING - case TOK_RB: - case TOK_RC: - if (!EmitDestructuringOps(cx, cg, JSOP_NOP, pn2)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - break; -#endif - - case TOK_NAME: - /* Inline BindNameToSlot, adding block depth to pn2->pn_slot. */ - pn2->pn_slot += OBJ_BLOCK_DEPTH(cx, ATOM_TO_OBJECT(atom)); - EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, pn2->pn_slot); - break; - - default: - JS_ASSERT(0); - } - - /* Emit the guard expression, if there is one. */ - if (pn->pn_kid2) { - if (!js_EmitTree(cx, cg, pn->pn_kid2)) - return JS_FALSE; - if (!js_SetSrcNoteOffset(cx, cg, CATCHNOTE(*stmt), 0, - CG_OFFSET(cg) - catchStart)) { - return JS_FALSE; - } - /* ifeq */ - guardJump = EmitJump(cx, cg, JSOP_IFEQ, 0); - if (guardJump < 0) - return JS_FALSE; - GUARDJUMP(*stmt) = guardJump; - - /* Pop duplicated exception object as we no longer need it. */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || - js_Emit1(cx, cg, JSOP_POP) < 0) { - return JS_FALSE; - } - } - - /* Emit the catch body. */ - if (!js_EmitTree(cx, cg, pn->pn_kid3)) - return JS_FALSE; - - /* - * Annotate the JSOP_LEAVEBLOCK that will be emitted as we unwind via - * our TOK_LEXICALSCOPE parent, so the decompiler knows to pop. - */ - off = cg->stackDepth; - if (js_NewSrcNote2(cx, cg, SRC_CATCH, off) < 0) - return JS_FALSE; - break; - } - - case TOK_VAR: - if (!EmitVariables(cx, cg, pn, JS_FALSE, ¬eIndex)) - return JS_FALSE; - break; - - case TOK_RETURN: - /* Push a return value */ - pn2 = pn->pn_kid; - if (pn2) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - } else { - if (js_Emit1(cx, cg, JSOP_PUSH) < 0) - return JS_FALSE; - } - - /* - * EmitNonLocalJumpFixup mutates op to JSOP_RETRVAL after emitting a - * JSOP_SETRVAL if there are open try blocks having finally clauses. - * We can't simply transfer control flow to our caller in that case, - * because we must gosub to those clauses from inner to outer, with - * the correct stack pointer (i.e., after popping any with, for/in, - * etc., slots nested inside the finally's try). - */ - op = JSOP_RETURN; - if (!EmitNonLocalJumpFixup(cx, cg, NULL, &op)) - return JS_FALSE; - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - break; - -#if JS_HAS_GENERATORS - case TOK_YIELD: - if (pn->pn_kid) { - if (!js_EmitTree(cx, cg, pn->pn_kid)) - return JS_FALSE; - } else { - if (js_Emit1(cx, cg, JSOP_PUSH) < 0) - return JS_FALSE; - } - if (js_Emit1(cx, cg, JSOP_YIELD) < 0) - return JS_FALSE; - break; -#endif - - case TOK_LC: -#if JS_HAS_XML_SUPPORT - if (pn->pn_arity == PN_UNARY) { - if (!js_EmitTree(cx, cg, pn->pn_kid)) - return JS_FALSE; - if (js_Emit1(cx, cg, pn->pn_op) < 0) - return JS_FALSE; - break; - } -#endif - - JS_ASSERT(pn->pn_arity == PN_LIST); - - noteIndex = -1; - tmp = CG_OFFSET(cg); - if (pn->pn_extra & PNX_NEEDBRACES) { - noteIndex = js_NewSrcNote2(cx, cg, SRC_BRACE, 0); - if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0) - return JS_FALSE; - } - - js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BLOCK, top); - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - } - - if (noteIndex >= 0 && - !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, - CG_OFFSET(cg) - tmp)) { - return JS_FALSE; - } - - ok = js_PopStatementCG(cx, cg); - break; - - case TOK_BODY: - JS_ASSERT(pn->pn_arity == PN_LIST); - js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BODY, top); - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - } - ok = js_PopStatementCG(cx, cg); - break; - - case TOK_SEMI: - pn2 = pn->pn_kid; - if (pn2) { - /* - * Top-level or called-from-a-native JS_Execute/EvaluateScript, - * debugger, and eval frames may need the value of the ultimate - * expression statement as the script's result, despite the fact - * that it appears useless to the compiler. - */ - useful = wantval = !cx->fp->fun || - !FUN_INTERPRETED(cx->fp->fun) || - (cx->fp->flags & JSFRAME_SPECIAL); - if (!useful) { - if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful)) - return JS_FALSE; - } - - /* - * Don't eliminate apparently useless expressions if they are - * labeled expression statements. The tc->topStmt->update test - * catches the case where we are nesting in js_EmitTree for a - * labeled compound statement. - */ - if (!useful && - (!cg->treeContext.topStmt || - cg->treeContext.topStmt->type != STMT_LABEL || - cg->treeContext.topStmt->update < CG_OFFSET(cg))) { - CG_CURRENT_LINE(cg) = pn2->pn_pos.begin.lineno; - if (!js_ReportCompileErrorNumber(cx, cg, - JSREPORT_CG | - JSREPORT_WARNING | - JSREPORT_STRICT, - JSMSG_USELESS_EXPR)) { - return JS_FALSE; - } - } else { - op = wantval ? JSOP_POPV : JSOP_POP; -#if JS_HAS_DESTRUCTURING - if (!wantval && - pn2->pn_type == TOK_ASSIGN && - !MaybeEmitGroupAssignment(cx, cg, op, pn2, &op)) { - return JS_FALSE; - } -#endif - if (op != JSOP_NOP) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } - } - } - break; - - case TOK_COLON: - /* Emit an annotated nop so we know to decompile a label. */ - atom = pn->pn_atom; - ale = js_IndexAtom(cx, atom, &cg->atomList); - if (!ale) - return JS_FALSE; - pn2 = pn->pn_expr; - noteType = (pn2->pn_type == TOK_LC || - (pn2->pn_type == TOK_LEXICALSCOPE && - pn2->pn_expr->pn_type == TOK_LC)) - ? SRC_LABELBRACE - : SRC_LABEL; - noteIndex = js_NewSrcNote2(cx, cg, noteType, - (ptrdiff_t) ALE_INDEX(ale)); - if (noteIndex < 0 || - js_Emit1(cx, cg, JSOP_NOP) < 0) { - return JS_FALSE; - } - - /* Emit code for the labeled statement. */ - js_PushStatement(&cg->treeContext, &stmtInfo, STMT_LABEL, - CG_OFFSET(cg)); - stmtInfo.atom = atom; - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if (!js_PopStatementCG(cx, cg)) - return JS_FALSE; - - /* If the statement was compound, emit a note for the end brace. */ - if (noteType == SRC_LABELBRACE) { - if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 || - js_Emit1(cx, cg, JSOP_NOP) < 0) { - return JS_FALSE; - } - } - break; - - case TOK_COMMA: - /* - * Emit SRC_PCDELTA notes on each JSOP_POP between comma operands. - * These notes help the decompiler bracket the bytecodes generated - * from each sub-expression that follows a comma. - */ - off = noteIndex = -1; - for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - tmp = CG_OFFSET(cg); - if (noteIndex >= 0) { - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off)) - return JS_FALSE; - } - if (!pn2->pn_next) - break; - off = tmp; - noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); - if (noteIndex < 0 || - js_Emit1(cx, cg, JSOP_POP) < 0) { - return JS_FALSE; - } - } - break; - - case TOK_ASSIGN: - /* - * Check left operand type and generate specialized code for it. - * Specialize to avoid ECMA "reference type" values on the operand - * stack, which impose pervasive runtime "GetValue" costs. - */ - pn2 = pn->pn_left; - JS_ASSERT(pn2->pn_type != TOK_RP); - atomIndex = (jsatomid) -1; - switch (pn2->pn_type) { - case TOK_NAME: - if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE)) - return JS_FALSE; - if (pn2->pn_slot >= 0) { - atomIndex = (jsatomid) pn2->pn_slot; - } else { - ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList); - if (!ale) - return JS_FALSE; - atomIndex = ALE_INDEX(ale); - EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex); - } - break; - case TOK_DOT: - if (!js_EmitTree(cx, cg, pn2->pn_expr)) - return JS_FALSE; - ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList); - if (!ale) - return JS_FALSE; - atomIndex = ALE_INDEX(ale); - break; - case TOK_LB: - JS_ASSERT(pn2->pn_arity == PN_BINARY); - if (!js_EmitTree(cx, cg, pn2->pn_left)) - return JS_FALSE; - if (!js_EmitTree(cx, cg, pn2->pn_right)) - return JS_FALSE; - break; -#if JS_HAS_DESTRUCTURING - case TOK_RB: - case TOK_RC: - break; -#endif -#if JS_HAS_LVALUE_RETURN - case TOK_LP: - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - break; -#endif -#if JS_HAS_XML_SUPPORT - case TOK_UNARYOP: - JS_ASSERT(pn2->pn_op == JSOP_SETXMLNAME); - if (!js_EmitTree(cx, cg, pn2->pn_kid)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_BINDXMLNAME) < 0) - return JS_FALSE; - break; -#endif - default: - JS_ASSERT(0); - } - - op = pn->pn_op; -#if JS_HAS_GETTER_SETTER - if (op == JSOP_GETTER || op == JSOP_SETTER) { - /* We'll emit these prefix bytecodes after emitting the r.h.s. */ - if (atomIndex != (jsatomid) -1 && atomIndex >= JS_BIT(16)) { - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } - } else -#endif - /* If += or similar, dup the left operand and get its value. */ - if (op != JSOP_NOP) { - switch (pn2->pn_type) { - case TOK_NAME: - if (pn2->pn_op != JSOP_SETNAME) { - EMIT_UINT16_IMM_OP((pn2->pn_op == JSOP_SETGVAR) - ? JSOP_GETGVAR - : (pn2->pn_op == JSOP_SETARG) - ? JSOP_GETARG - : (pn2->pn_op == JSOP_SETLOCAL) - ? JSOP_GETLOCAL - : JSOP_GETVAR, - atomIndex); - break; - } - /* FALL THROUGH */ - case TOK_DOT: - if (js_Emit1(cx, cg, JSOP_DUP) < 0) - return JS_FALSE; - EMIT_ATOM_INDEX_OP((pn2->pn_type == TOK_NAME) - ? JSOP_GETXPROP - : JSOP_GETPROP, - atomIndex); - break; - case TOK_LB: -#if JS_HAS_LVALUE_RETURN - case TOK_LP: -#endif -#if JS_HAS_XML_SUPPORT - case TOK_UNARYOP: -#endif - if (js_Emit1(cx, cg, JSOP_DUP2) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_GETELEM) < 0) - return JS_FALSE; - break; - default:; - } - } - - /* Now emit the right operand (it may affect the namespace). */ - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; - - /* If += etc., emit the binary operator with a decompiler note. */ - if (op != JSOP_NOP) { - /* - * Take care to avoid SRC_ASSIGNOP if the left-hand side is a - * const declared in a function (i.e., with non-negative pn_slot - * and JSPROP_READONLY in pn_attrs), as in this case (just a bit - * further below) we will avoid emitting the assignment op. - */ - if (pn2->pn_type != TOK_NAME || - pn2->pn_slot < 0 || - !(pn2->pn_attrs & JSPROP_READONLY)) { - if (js_NewSrcNote(cx, cg, SRC_ASSIGNOP) < 0) - return JS_FALSE; - } - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } - - /* Left parts such as a.b.c and a[b].c need a decompiler note. */ - if (pn2->pn_type != TOK_NAME && -#if JS_HAS_DESTRUCTURING - pn2->pn_type != TOK_RB && - pn2->pn_type != TOK_RC && -#endif - js_NewSrcNote2(cx, cg, SrcNoteForPropOp(pn2, pn2->pn_op), - CG_OFFSET(cg) - top) < 0) { - return JS_FALSE; - } - - /* Finally, emit the specialized assignment bytecode. */ - switch (pn2->pn_type) { - case TOK_NAME: - if (pn2->pn_slot < 0 || !(pn2->pn_attrs & JSPROP_READONLY)) { - if (pn2->pn_slot >= 0) { - EMIT_UINT16_IMM_OP(pn2->pn_op, atomIndex); - } else { - case TOK_DOT: - EMIT_ATOM_INDEX_OP(pn2->pn_op, atomIndex); - } - } - break; - case TOK_LB: -#if JS_HAS_LVALUE_RETURN - case TOK_LP: -#endif - if (js_Emit1(cx, cg, JSOP_SETELEM) < 0) - return JS_FALSE; - break; -#if JS_HAS_DESTRUCTURING - case TOK_RB: - case TOK_RC: - if (!EmitDestructuringOps(cx, cg, JSOP_SETNAME, pn2)) - return JS_FALSE; - break; -#endif -#if JS_HAS_XML_SUPPORT - case TOK_UNARYOP: - if (js_Emit1(cx, cg, JSOP_SETXMLNAME) < 0) - return JS_FALSE; - break; -#endif - default: - JS_ASSERT(0); - } - break; - - case TOK_HOOK: - /* Emit the condition, then branch if false to the else part. */ - if (!js_EmitTree(cx, cg, pn->pn_kid1)) - return JS_FALSE; - noteIndex = js_NewSrcNote(cx, cg, SRC_COND); - if (noteIndex < 0) - return JS_FALSE; - beq = EmitJump(cx, cg, JSOP_IFEQ, 0); - if (beq < 0 || !js_EmitTree(cx, cg, pn->pn_kid2)) - return JS_FALSE; - - /* Jump around else, fixup the branch, emit else, fixup jump. */ - jmp = EmitJump(cx, cg, JSOP_GOTO, 0); - if (jmp < 0) - return JS_FALSE; - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); - - /* - * Because each branch pushes a single value, but our stack budgeting - * analysis ignores branches, we now have to adjust cg->stackDepth to - * ignore the value pushed by the first branch. Execution will follow - * only one path, so we must decrement cg->stackDepth. - * - * Failing to do this will foil code, such as the try/catch/finally - * exception handling code generator, that samples cg->stackDepth for - * use at runtime (JSOP_SETSP), or in let expression and block code - * generation, which must use the stack depth to compute local stack - * indexes correctly. - */ - JS_ASSERT(cg->stackDepth > 0); - cg->stackDepth--; - if (!js_EmitTree(cx, cg, pn->pn_kid3)) - return JS_FALSE; - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); - if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq)) - return JS_FALSE; - break; - - case TOK_OR: - case TOK_AND: - /* - * JSOP_OR converts the operand on the stack to boolean, and if true, - * leaves the original operand value on the stack and jumps; otherwise - * it pops and falls into the next bytecode, which evaluates the right - * operand. The jump goes around the right operand evaluation. - * - * JSOP_AND converts the operand on the stack to boolean, and if false, - * leaves the original operand value on the stack and jumps; otherwise - * it pops and falls into the right operand's bytecode. - * - * Avoid tail recursion for long ||...|| expressions and long &&...&& - * expressions or long mixtures of ||'s and &&'s that can easily blow - * the stack, by forward-linking and then backpatching all the JSOP_OR - * and JSOP_AND bytecodes' immediate jump-offset operands. - */ - pn3 = pn; - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - top = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0); - if (top < 0) - return JS_FALSE; - jmp = top; - pn2 = pn->pn_right; - while (pn2->pn_type == TOK_OR || pn2->pn_type == TOK_AND) { - pn = pn2; - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - off = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0); - if (off < 0) - return JS_FALSE; - if (!SetBackPatchDelta(cx, cg, CG_CODE(cg, jmp), off - jmp)) - return JS_FALSE; - jmp = off; - pn2 = pn->pn_right; - } - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - off = CG_OFFSET(cg); - do { - pc = CG_CODE(cg, top); - tmp = GetJumpOffset(cg, pc); - CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, off - top); - *pc = pn3->pn_op; - top += tmp; - } while ((pn3 = pn3->pn_right) != pn2); - break; - - case TOK_BITOR: - case TOK_BITXOR: - case TOK_BITAND: - case TOK_EQOP: - case TOK_RELOP: - case TOK_IN: - case TOK_INSTANCEOF: - case TOK_SHOP: - case TOK_PLUS: - case TOK_MINUS: - case TOK_STAR: - case TOK_DIVOP: - if (pn->pn_arity == PN_LIST) { - /* Left-associative operator chain: avoid too much recursion. */ - pn2 = pn->pn_head; - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - op = pn->pn_op; - while ((pn2 = pn2->pn_next) != NULL) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } - } else { -#if JS_HAS_XML_SUPPORT - uintN oldflags; - - case TOK_DBLCOLON: - if (pn->pn_arity == PN_NAME) { - if (!js_EmitTree(cx, cg, pn->pn_expr)) - return JS_FALSE; - if (!EmitAtomOp(cx, pn, pn->pn_op, cg)) - return JS_FALSE; - break; - } - - /* - * Binary :: has a right operand that brackets arbitrary code, - * possibly including a let (a = b) ... expression. We must clear - * TCF_IN_FOR_INIT to avoid mis-compiling such beasts. - */ - oldflags = cg->treeContext.flags; - cg->treeContext.flags &= ~TCF_IN_FOR_INIT; -#endif - - /* Binary operators that evaluate both operands unconditionally. */ - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; -#if JS_HAS_XML_SUPPORT - cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; -#endif - if (js_Emit1(cx, cg, pn->pn_op) < 0) - return JS_FALSE; - } - break; - - case TOK_THROW: -#if JS_HAS_XML_SUPPORT - case TOK_AT: - case TOK_DEFAULT: - JS_ASSERT(pn->pn_arity == PN_UNARY); - /* FALL THROUGH */ -#endif - case TOK_UNARYOP: - { - uintN oldflags; - - /* Unary op, including unary +/-. */ - pn2 = pn->pn_kid; - op = pn->pn_op; - if (op == JSOP_TYPEOF) { - for (pn3 = pn2; pn3->pn_type == TOK_RP; pn3 = pn3->pn_kid) - continue; - if (pn3->pn_type != TOK_NAME) - op = JSOP_TYPEOFEXPR; - } - oldflags = cg->treeContext.flags; - cg->treeContext.flags &= ~TCF_IN_FOR_INIT; - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; -#if JS_HAS_XML_SUPPORT - if (op == JSOP_XMLNAME && - js_NewSrcNote2(cx, cg, SRC_PCBASE, - CG_OFFSET(cg) - pn2->pn_offset) < 0) { - return JS_FALSE; - } -#endif - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - break; - } - - case TOK_INC: - case TOK_DEC: - { - intN depth; - - /* Emit lvalue-specialized code for ++/-- operators. */ - pn2 = pn->pn_kid; - JS_ASSERT(pn2->pn_type != TOK_RP); - op = pn->pn_op; - depth = cg->stackDepth; - switch (pn2->pn_type) { - case TOK_NAME: - pn2->pn_op = op; - if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE)) - return JS_FALSE; - op = pn2->pn_op; - if (pn2->pn_slot >= 0) { - if (pn2->pn_attrs & JSPROP_READONLY) { - /* Incrementing a declared const: just get its value. */ - op = ((js_CodeSpec[op].format & JOF_TYPEMASK) == JOF_CONST) - ? JSOP_GETGVAR - : JSOP_GETVAR; - } - atomIndex = (jsatomid) pn2->pn_slot; - EMIT_UINT16_IMM_OP(op, atomIndex); - } else { - if (!EmitAtomOp(cx, pn2, op, cg)) - return JS_FALSE; - } - break; - case TOK_DOT: - if (!EmitPropOp(cx, pn2, op, cg)) - return JS_FALSE; - ++depth; - break; - case TOK_LB: - if (!EmitElemOp(cx, pn2, op, cg)) - return JS_FALSE; - depth += 2; - break; -#if JS_HAS_LVALUE_RETURN - case TOK_LP: - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - depth = cg->stackDepth; - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, - CG_OFFSET(cg) - pn2->pn_offset) < 0) { - return JS_FALSE; - } - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - break; -#endif -#if JS_HAS_XML_SUPPORT - case TOK_UNARYOP: - JS_ASSERT(pn2->pn_op == JSOP_SETXMLNAME); - if (!js_EmitTree(cx, cg, pn2->pn_kid)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_BINDXMLNAME) < 0) - return JS_FALSE; - depth = cg->stackDepth; - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - break; -#endif - default: - JS_ASSERT(0); - } - - /* - * Allocate another stack slot for GC protection in case the initial - * value being post-incremented or -decremented is not a number, but - * converts to a jsdouble. In the TOK_NAME cases, op has 0 operand - * uses and 1 definition, so we don't need an extra stack slot -- we - * can use the one allocated for the def. - */ - if (pn2->pn_type != TOK_NAME && - (js_CodeSpec[op].format & JOF_POST) && - (uintN)depth == cg->maxStackDepth) { - ++cg->maxStackDepth; - } - break; - } - - case TOK_DELETE: - /* - * Under ECMA 3, deleting a non-reference returns true -- but alas we - * must evaluate the operand if it appears it might have side effects. - */ - pn2 = pn->pn_kid; - switch (pn2->pn_type) { - case TOK_NAME: - pn2->pn_op = JSOP_DELNAME; - if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE)) - return JS_FALSE; - op = pn2->pn_op; - if (op == JSOP_FALSE) { - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } else { - if (!EmitAtomOp(cx, pn2, op, cg)) - return JS_FALSE; - } - break; - case TOK_DOT: - if (!EmitPropOp(cx, pn2, JSOP_DELPROP, cg)) - return JS_FALSE; - break; -#if JS_HAS_XML_SUPPORT - case TOK_DBLDOT: - if (!EmitElemOp(cx, pn2, JSOP_DELDESC, cg)) - return JS_FALSE; - break; -#endif -#if JS_HAS_LVALUE_RETURN - case TOK_LP: - if (pn2->pn_op != JSOP_SETCALL) { - JS_ASSERT(pn2->pn_op == JSOP_CALL || pn2->pn_op == JSOP_EVAL); - pn2->pn_op = JSOP_SETCALL; - } - top = CG_OFFSET(cg); - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_DELELEM) < 0) - return JS_FALSE; - break; -#endif - case TOK_LB: - if (!EmitElemOp(cx, pn2, JSOP_DELELEM, cg)) - return JS_FALSE; - break; - default: - /* - * If useless, just emit JSOP_TRUE; otherwise convert delete foo() - * to foo(), true (a comma expression, requiring SRC_PCDELTA). - */ - useful = JS_FALSE; - if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful)) - return JS_FALSE; - if (!useful) { - off = noteIndex = -1; - } else { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - off = CG_OFFSET(cg); - noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); - if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - } - if (js_Emit1(cx, cg, JSOP_TRUE) < 0) - return JS_FALSE; - if (noteIndex >= 0) { - tmp = CG_OFFSET(cg); - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off)) - return JS_FALSE; - } - } - break; - -#if JS_HAS_XML_SUPPORT - case TOK_FILTER: - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - jmp = js_Emit3(cx, cg, JSOP_FILTER, 0, 0); - if (jmp < 0) - return JS_FALSE; - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_ENDFILTER) < 0) - return JS_FALSE; - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); - break; -#endif - - case TOK_DOT: - /* - * Pop a stack operand, convert it to object, get a property named by - * this bytecode's immediate-indexed atom operand, and push its value - * (not a reference to it). This bytecode sets the virtual machine's - * "obj" register to the left operand's ToObject conversion result, - * for use by JSOP_PUSHOBJ. - */ - ok = EmitPropOp(cx, pn, pn->pn_op, cg); - break; - - case TOK_LB: -#if JS_HAS_XML_SUPPORT - case TOK_DBLDOT: -#endif - /* - * Pop two operands, convert the left one to object and the right one - * to property name (atom or tagged int), get the named property, and - * push its value. Set the "obj" register to the result of ToObject - * on the left operand. - */ - ok = EmitElemOp(cx, pn, pn->pn_op, cg); - break; - - case TOK_NEW: - case TOK_LP: - { - uintN oldflags; - - /* - * Emit function call or operator new (constructor call) code. - * First, emit code for the left operand to evaluate the callable or - * constructable object expression. - * - * For E4X, if this expression is a dotted member reference, select - * JSOP_GETMETHOD instead of JSOP_GETPROP. ECMA-357 separates XML - * method lookup from the normal property id lookup done for native - * objects. - */ - pn2 = pn->pn_head; -#if JS_HAS_XML_SUPPORT - if (pn2->pn_type == TOK_DOT && pn2->pn_op != JSOP_GETMETHOD) { - JS_ASSERT(pn2->pn_op == JSOP_GETPROP); - pn2->pn_op = JSOP_GETMETHOD; - pn2->pn_attrs |= JSPROP_IMPLICIT_FUNCTION_NAMESPACE; - } -#endif - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - - /* - * Push the virtual machine's "obj" register, which was set by a - * name, property, or element get (or set) bytecode. - */ - if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0) - return JS_FALSE; - - /* Remember start of callable-object bytecode for decompilation hint. */ - off = top; - - /* - * Emit code for each argument in order, then emit the JSOP_*CALL or - * JSOP_NEW bytecode with a two-byte immediate telling how many args - * were pushed on the operand stack. - */ - oldflags = cg->treeContext.flags; - cg->treeContext.flags &= ~TCF_IN_FOR_INIT; - for (pn2 = pn2->pn_next; pn2; pn2 = pn2->pn_next) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - } - cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - off) < 0) - return JS_FALSE; - - argc = pn->pn_count - 1; - if (js_Emit3(cx, cg, pn->pn_op, ARGC_HI(argc), ARGC_LO(argc)) < 0) - return JS_FALSE; - break; - } - - case TOK_LEXICALSCOPE: - { - JSObject *obj; - jsint count; - - atom = pn->pn_atom; - obj = ATOM_TO_OBJECT(atom); - js_PushBlockScope(&cg->treeContext, &stmtInfo, atom, CG_OFFSET(cg)); - - OBJ_SET_BLOCK_DEPTH(cx, obj, cg->stackDepth); - count = OBJ_BLOCK_COUNT(cx, obj); - cg->stackDepth += count; - if ((uintN)cg->stackDepth > cg->maxStackDepth) - cg->maxStackDepth = cg->stackDepth; - - /* - * If this lexical scope is not for a catch block, let block or let - * expression, or any kind of for loop (where the scope starts in the - * head after the first part if for (;;), else in the body if for-in); - * and if our container is top-level but not a function body, or else - * a block statement; then emit a SRC_BRACE note. All other container - * statements get braces by default from the decompiler. - */ - noteIndex = -1; - type = pn->pn_expr->pn_type; - if (type != TOK_CATCH && type != TOK_LET && type != TOK_FOR && - (!(stmt = stmtInfo.down) - ? !(cg->treeContext.flags & TCF_IN_FUNCTION) - : stmt->type == STMT_BLOCK)) { -#if defined DEBUG_brendan || defined DEBUG_mrbkap - /* There must be no source note already output for the next op. */ - JS_ASSERT(CG_NOTE_COUNT(cg) == 0 || - CG_LAST_NOTE_OFFSET(cg) != CG_OFFSET(cg) || - !GettableNoteForNextOp(cg)); -#endif - noteIndex = js_NewSrcNote2(cx, cg, SRC_BRACE, 0); - if (noteIndex < 0) - return JS_FALSE; - } - - ale = js_IndexAtom(cx, atom, &cg->atomList); - if (!ale) - return JS_FALSE; - JS_ASSERT(CG_OFFSET(cg) == top); - EMIT_ATOM_INDEX_OP(JSOP_ENTERBLOCK, ALE_INDEX(ale)); - - if (!js_EmitTree(cx, cg, pn->pn_expr)) - return JS_FALSE; - - op = pn->pn_op; - if (op == JSOP_LEAVEBLOCKEXPR) { - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) - return JS_FALSE; - } else { - if (noteIndex >= 0 && - !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, - CG_OFFSET(cg) - top)) { - return JS_FALSE; - } - } - - /* Emit the JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR opcode. */ - EMIT_UINT16_IMM_OP(op, count); - cg->stackDepth -= count; - - ok = js_PopStatementCG(cx, cg); - break; - } - -#if JS_HAS_BLOCK_SCOPE - case TOK_LET: - /* Let statements have their variable declarations on the left. */ - if (pn->pn_arity == PN_BINARY) { - pn2 = pn->pn_right; - pn = pn->pn_left; - } else { - pn2 = NULL; - } - - /* Non-null pn2 means that pn is the variable list from a let head. */ - JS_ASSERT(pn->pn_arity == PN_LIST); - if (!EmitVariables(cx, cg, pn, pn2 != NULL, ¬eIndex)) - return JS_FALSE; - - /* Thus non-null pn2 is the body of the let block or expression. */ - tmp = CG_OFFSET(cg); - if (pn2 && !js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - - if (noteIndex >= 0 && - !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, - CG_OFFSET(cg) - tmp)) { - return JS_FALSE; - } - break; -#endif /* JS_HAS_BLOCK_SCOPE */ - -#if JS_HAS_GENERATORS - case TOK_ARRAYPUSH: - /* - * The array object's stack index is in cg->arrayCompSlot. See below - * under the array initialiser code generator for array comprehension - * special casing. - */ - if (!js_EmitTree(cx, cg, pn->pn_kid)) - return JS_FALSE; - EMIT_UINT16_IMM_OP(pn->pn_op, cg->arrayCompSlot); - break; -#endif - - case TOK_RB: -#if JS_HAS_GENERATORS - case TOK_ARRAYCOMP: -#endif - /* - * Emit code for [a, b, c] of the form: - * t = new Array; t[0] = a; t[1] = b; t[2] = c; t; - * but use a stack slot for t and avoid dup'ing and popping it via - * the JSOP_NEWINIT and JSOP_INITELEM bytecodes. - */ - ale = js_IndexAtom(cx, CLASS_ATOM(cx, Array), &cg->atomList); - if (!ale) - return JS_FALSE; - EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale)); - if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0) - return JS_FALSE; - - pn2 = pn->pn_head; -#if JS_HAS_SHARP_VARS - if (pn2 && pn2->pn_type == TOK_DEFSHARP) { - EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num); - pn2 = pn2->pn_next; - } -#endif - -#if JS_HAS_GENERATORS - if (pn->pn_type == TOK_ARRAYCOMP) { - uintN saveSlot; - - /* - * Pass the new array's stack index to the TOK_ARRAYPUSH case by - * storing it in pn->pn_extra, then simply traverse the TOK_FOR - * node and its kids under pn2 to generate this comprehension. - */ - JS_ASSERT(cg->stackDepth > 0); - saveSlot = cg->arrayCompSlot; - cg->arrayCompSlot = (uint32) (cg->stackDepth - 1); - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - cg->arrayCompSlot = saveSlot; - - /* Emit the usual op needed for decompilation. */ - if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0) - return JS_FALSE; - break; - } -#endif /* JS_HAS_GENERATORS */ - - for (atomIndex = 0; pn2; atomIndex++, pn2 = pn2->pn_next) { - if (!EmitNumberOp(cx, atomIndex, cg)) - return JS_FALSE; - - /* FIXME 260106: holes in a sparse initializer are void-filled. */ - if (pn2->pn_type == TOK_COMMA) { - if (js_Emit1(cx, cg, JSOP_PUSH) < 0) - return JS_FALSE; - } else { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - } - - if (js_Emit1(cx, cg, JSOP_INITELEM) < 0) - return JS_FALSE; - } - - if (pn->pn_extra & PNX_ENDCOMMA) { - /* Emit a source note so we know to decompile an extra comma. */ - if (js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0) - return JS_FALSE; - } - - /* Emit an op for sharp array cleanup and decompilation. */ - if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0) - return JS_FALSE; - break; - - case TOK_RC: - /* - * Emit code for {p:a, '%q':b, 2:c} of the form: - * t = new Object; t.p = a; t['%q'] = b; t[2] = c; t; - * but use a stack slot for t and avoid dup'ing and popping it via - * the JSOP_NEWINIT and JSOP_INITELEM bytecodes. - */ - ale = js_IndexAtom(cx, CLASS_ATOM(cx, Object), &cg->atomList); - if (!ale) - return JS_FALSE; - EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale)); - - if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0) - return JS_FALSE; - - pn2 = pn->pn_head; -#if JS_HAS_SHARP_VARS - if (pn2 && pn2->pn_type == TOK_DEFSHARP) { - EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num); - pn2 = pn2->pn_next; - } -#endif - - for (; pn2; pn2 = pn2->pn_next) { - /* Emit an index for t[2], else map an atom for t.p or t['%q']. */ - pn3 = pn2->pn_left; - switch (pn3->pn_type) { - case TOK_NUMBER: - if (!EmitNumberOp(cx, pn3->pn_dval, cg)) - return JS_FALSE; - break; - case TOK_NAME: - case TOK_STRING: - ale = js_IndexAtom(cx, pn3->pn_atom, &cg->atomList); - if (!ale) - return JS_FALSE; - break; - default: - JS_ASSERT(0); - } - - /* Emit code for the property initializer. */ - if (!js_EmitTree(cx, cg, pn2->pn_right)) - return JS_FALSE; - -#if JS_HAS_GETTER_SETTER - op = pn2->pn_op; - if (op == JSOP_GETTER || op == JSOP_SETTER) { - if (pn3->pn_type != TOK_NUMBER && - ALE_INDEX(ale) >= JS_BIT(16)) { - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } -#endif - /* Annotate JSOP_INITELEM so we decompile 2:c and not just c. */ - if (pn3->pn_type == TOK_NUMBER) { - if (js_NewSrcNote(cx, cg, SRC_INITPROP) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_INITELEM) < 0) - return JS_FALSE; - } else { - EMIT_ATOM_INDEX_OP(JSOP_INITPROP, ALE_INDEX(ale)); - } - } - - /* Emit an op for sharpArray cleanup and decompilation. */ - if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0) - return JS_FALSE; - break; - -#if JS_HAS_SHARP_VARS - case TOK_DEFSHARP: - if (!js_EmitTree(cx, cg, pn->pn_kid)) - return JS_FALSE; - EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid) pn->pn_num); - break; - - case TOK_USESHARP: - EMIT_UINT16_IMM_OP(JSOP_USESHARP, (jsatomid) pn->pn_num); - break; -#endif /* JS_HAS_SHARP_VARS */ - - case TOK_RP: - { - uintN oldflags; - - /* - * The node for (e) has e as its kid, enabling users who want to nest - * assignment expressions in conditions to avoid the error correction - * done by Condition (from x = y to x == y) by double-parenthesizing. - */ - oldflags = cg->treeContext.flags; - cg->treeContext.flags &= ~TCF_IN_FOR_INIT; - if (!js_EmitTree(cx, cg, pn->pn_kid)) - return JS_FALSE; - cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; - if (js_Emit1(cx, cg, JSOP_GROUP) < 0) - return JS_FALSE; - break; - } - - case TOK_NAME: - if (!BindNameToSlot(cx, &cg->treeContext, pn, JS_FALSE)) - return JS_FALSE; - op = pn->pn_op; - if (op == JSOP_ARGUMENTS) { - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - break; - } - if (pn->pn_slot >= 0) { - atomIndex = (jsatomid) pn->pn_slot; - EMIT_UINT16_IMM_OP(op, atomIndex); - break; - } - /* FALL THROUGH */ - -#if JS_HAS_XML_SUPPORT - case TOK_XMLATTR: - case TOK_XMLSPACE: - case TOK_XMLTEXT: - case TOK_XMLCDATA: - case TOK_XMLCOMMENT: -#endif - case TOK_STRING: - case TOK_OBJECT: - /* - * The scanner and parser associate JSOP_NAME with TOK_NAME, although - * other bytecodes may result instead (JSOP_BINDNAME/JSOP_SETNAME, - * JSOP_FORNAME, etc.). Among JSOP_*NAME* variants, only JSOP_NAME - * may generate the first operand of a call or new expression, so only - * it sets the "obj" virtual machine register to the object along the - * scope chain in which the name was found. - * - * Token types for STRING and OBJECT have corresponding bytecode ops - * in pn_op and emit the same format as NAME, so they share this code. - */ - ok = EmitAtomOp(cx, pn, pn->pn_op, cg); - break; - - case TOK_NUMBER: - ok = EmitNumberOp(cx, pn->pn_dval, cg); - break; - -#if JS_HAS_XML_SUPPORT - case TOK_ANYNAME: -#endif - case TOK_PRIMARY: - if (js_Emit1(cx, cg, pn->pn_op) < 0) - return JS_FALSE; - break; - -#if JS_HAS_DEBUGGER_KEYWORD - case TOK_DEBUGGER: - if (js_Emit1(cx, cg, JSOP_DEBUGGER) < 0) - return JS_FALSE; - break; -#endif /* JS_HAS_DEBUGGER_KEYWORD */ - -#if JS_HAS_XML_SUPPORT - case TOK_XMLELEM: - case TOK_XMLLIST: - if (pn->pn_op == JSOP_XMLOBJECT) { - ok = EmitAtomOp(cx, pn, pn->pn_op, cg); - break; - } - - JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0); - switch (pn->pn_head ? pn->pn_head->pn_type : TOK_XMLLIST) { - case TOK_XMLETAGO: - JS_ASSERT(0); - /* FALL THROUGH */ - case TOK_XMLPTAGC: - case TOK_XMLSTAGO: - break; - default: - if (js_Emit1(cx, cg, JSOP_STARTXML) < 0) - return JS_FALSE; - } - - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - if (pn2->pn_type == TOK_LC && - js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) { - return JS_FALSE; - } - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0) - return JS_FALSE; - } - - if (pn->pn_extra & PNX_XMLROOT) { - if (pn->pn_count == 0) { - JS_ASSERT(pn->pn_type == TOK_XMLLIST); - atom = cx->runtime->atomState.emptyAtom; - ale = js_IndexAtom(cx, atom, &cg->atomList); - if (!ale) - return JS_FALSE; - EMIT_ATOM_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); - } - if (js_Emit1(cx, cg, pn->pn_op) < 0) - return JS_FALSE; - } -#ifdef DEBUG - else - JS_ASSERT(pn->pn_count != 0); -#endif - break; - - case TOK_XMLPTAGC: - if (pn->pn_op == JSOP_XMLOBJECT) { - ok = EmitAtomOp(cx, pn, pn->pn_op, cg); - break; - } - /* FALL THROUGH */ - - case TOK_XMLSTAGO: - case TOK_XMLETAGO: - { - uint32 i; - - if (js_Emit1(cx, cg, JSOP_STARTXML) < 0) - return JS_FALSE; - - ale = js_IndexAtom(cx, - (pn->pn_type == TOK_XMLETAGO) - ? cx->runtime->atomState.etagoAtom - : cx->runtime->atomState.stagoAtom, - &cg->atomList); - if (!ale) - return JS_FALSE; - EMIT_ATOM_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); - - JS_ASSERT(pn->pn_count != 0); - pn2 = pn->pn_head; - if (pn2->pn_type == TOK_LC && js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) - return JS_FALSE; - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_ADD) < 0) - return JS_FALSE; - - for (pn2 = pn2->pn_next, i = 0; pn2; pn2 = pn2->pn_next, i++) { - if (pn2->pn_type == TOK_LC && - js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) { - return JS_FALSE; - } - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if ((i & 1) && pn2->pn_type == TOK_LC) { - if (js_Emit1(cx, cg, JSOP_TOATTRVAL) < 0) - return JS_FALSE; - } - if (js_Emit1(cx, cg, - (i & 1) ? JSOP_ADDATTRVAL : JSOP_ADDATTRNAME) < 0) { - return JS_FALSE; - } - } - - ale = js_IndexAtom(cx, - (pn->pn_type == TOK_XMLPTAGC) - ? cx->runtime->atomState.ptagcAtom - : cx->runtime->atomState.tagcAtom, - &cg->atomList); - if (!ale) - return JS_FALSE; - EMIT_ATOM_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); - if (js_Emit1(cx, cg, JSOP_ADD) < 0) - return JS_FALSE; - - if ((pn->pn_extra & PNX_XMLROOT) && js_Emit1(cx, cg, pn->pn_op) < 0) - return JS_FALSE; - break; - } - - case TOK_XMLNAME: - if (pn->pn_arity == PN_LIST) { - JS_ASSERT(pn->pn_count != 0); - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0) - return JS_FALSE; - } - } else { - JS_ASSERT(pn->pn_arity == PN_NULLARY); - ok = EmitAtomOp(cx, pn, pn->pn_op, cg); - } - break; - - case TOK_XMLPI: - ale = js_IndexAtom(cx, pn->pn_atom2, &cg->atomList); - if (!ale) - return JS_FALSE; - if (!EmitAtomIndexOp(cx, JSOP_QNAMEPART, ALE_INDEX(ale), cg)) - return JS_FALSE; - if (!EmitAtomOp(cx, pn, JSOP_XMLPI, cg)) - return JS_FALSE; - break; -#endif /* JS_HAS_XML_SUPPORT */ - - default: - JS_ASSERT(0); - } - - if (ok && --cg->emitLevel == 0 && cg->spanDeps) - ok = OptimizeSpanDeps(cx, cg); - - return ok; -} - -/* XXX get rid of offsetBias, it's used only by SRC_FOR and SRC_DECL */ -JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = { - {"null", 0, 0, 0}, - {"if", 0, 0, 0}, - {"if-else", 2, 0, 1}, - {"while", 1, 0, 1}, - {"for", 3, 1, 1}, - {"continue", 0, 0, 0}, - {"decl", 1, 1, 1}, - {"pcdelta", 1, 0, 1}, - {"assignop", 0, 0, 0}, - {"cond", 1, 0, 1}, - {"brace", 1, 0, 1}, - {"hidden", 0, 0, 0}, - {"pcbase", 1, 0, -1}, - {"label", 1, 0, 0}, - {"labelbrace", 1, 0, 0}, - {"endbrace", 0, 0, 0}, - {"break2label", 1, 0, 0}, - {"cont2label", 1, 0, 0}, - {"switch", 2, 0, 1}, - {"funcdef", 1, 0, 0}, - {"catch", 1, 0, 1}, - {"extended", -1, 0, 0}, - {"newline", 0, 0, 0}, - {"setline", 1, 0, 0}, - {"xdelta", 0, 0, 0}, -}; - -static intN -AllocSrcNote(JSContext *cx, JSCodeGenerator *cg) -{ - intN index; - JSArenaPool *pool; - size_t size; - - index = CG_NOTE_COUNT(cg); - if (((uintN)index & CG_NOTE_MASK(cg)) == 0) { - pool = cg->notePool; - size = SRCNOTE_SIZE(CG_NOTE_MASK(cg) + 1); - if (!CG_NOTES(cg)) { - /* Allocate the first note array lazily; leave noteMask alone. */ - JS_ARENA_ALLOCATE_CAST(CG_NOTES(cg), jssrcnote *, pool, size); - } else { - /* Grow by doubling note array size; update noteMask on success. */ - JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size); - if (CG_NOTES(cg)) - CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1; - } - if (!CG_NOTES(cg)) { - JS_ReportOutOfMemory(cx); - return -1; - } - } - - CG_NOTE_COUNT(cg) = index + 1; - return index; -} - -intN -js_NewSrcNote(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type) -{ - intN index, n; - jssrcnote *sn; - ptrdiff_t offset, delta, xdelta; - - /* - * Claim a note slot in CG_NOTES(cg) by growing it if necessary and then - * incrementing CG_NOTE_COUNT(cg). - */ - index = AllocSrcNote(cx, cg); - if (index < 0) - return -1; - sn = &CG_NOTES(cg)[index]; - - /* - * Compute delta from the last annotated bytecode's offset. If it's too - * big to fit in sn, allocate one or more xdelta notes and reset sn. - */ - offset = CG_OFFSET(cg); - delta = offset - CG_LAST_NOTE_OFFSET(cg); - CG_LAST_NOTE_OFFSET(cg) = offset; - if (delta >= SN_DELTA_LIMIT) { - do { - xdelta = JS_MIN(delta, SN_XDELTA_MASK); - SN_MAKE_XDELTA(sn, xdelta); - delta -= xdelta; - index = AllocSrcNote(cx, cg); - if (index < 0) - return -1; - sn = &CG_NOTES(cg)[index]; - } while (delta >= SN_DELTA_LIMIT); - } - - /* - * Initialize type and delta, then allocate the minimum number of notes - * needed for type's arity. Usually, we won't need more, but if an offset - * does take two bytes, js_SetSrcNoteOffset will grow CG_NOTES(cg). - */ - SN_MAKE_NOTE(sn, type, delta); - for (n = (intN)js_SrcNoteSpec[type].arity; n > 0; n--) { - if (js_NewSrcNote(cx, cg, SRC_NULL) < 0) - return -1; - } - return index; -} - -intN -js_NewSrcNote2(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, - ptrdiff_t offset) -{ - intN index; - - index = js_NewSrcNote(cx, cg, type); - if (index >= 0) { - if (!js_SetSrcNoteOffset(cx, cg, index, 0, offset)) - return -1; - } - return index; -} - -intN -js_NewSrcNote3(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, - ptrdiff_t offset1, ptrdiff_t offset2) -{ - intN index; - - index = js_NewSrcNote(cx, cg, type); - if (index >= 0) { - if (!js_SetSrcNoteOffset(cx, cg, index, 0, offset1)) - return -1; - if (!js_SetSrcNoteOffset(cx, cg, index, 1, offset2)) - return -1; - } - return index; -} - -static JSBool -GrowSrcNotes(JSContext *cx, JSCodeGenerator *cg) -{ - JSArenaPool *pool; - size_t size; - - /* Grow by doubling note array size; update noteMask on success. */ - pool = cg->notePool; - size = SRCNOTE_SIZE(CG_NOTE_MASK(cg) + 1); - JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size); - if (!CG_NOTES(cg)) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1; - return JS_TRUE; -} - -jssrcnote * -js_AddToSrcNoteDelta(JSContext *cx, JSCodeGenerator *cg, jssrcnote *sn, - ptrdiff_t delta) -{ - ptrdiff_t base, limit, newdelta, diff; - intN index; - - /* - * Called only from OptimizeSpanDeps and js_FinishTakingSrcNotes to add to - * main script note deltas, and only by a small positive amount. - */ - JS_ASSERT(cg->current == &cg->main); - JS_ASSERT((unsigned) delta < (unsigned) SN_XDELTA_LIMIT); - - base = SN_DELTA(sn); - limit = SN_IS_XDELTA(sn) ? SN_XDELTA_LIMIT : SN_DELTA_LIMIT; - newdelta = base + delta; - if (newdelta < limit) { - SN_SET_DELTA(sn, newdelta); - } else { - index = sn - cg->main.notes; - if ((cg->main.noteCount & cg->main.noteMask) == 0) { - if (!GrowSrcNotes(cx, cg)) - return NULL; - sn = cg->main.notes + index; - } - diff = cg->main.noteCount - index; - cg->main.noteCount++; - memmove(sn + 1, sn, SRCNOTE_SIZE(diff)); - SN_MAKE_XDELTA(sn, delta); - sn++; - } - return sn; -} - -JS_FRIEND_API(uintN) -js_SrcNoteLength(jssrcnote *sn) -{ - uintN arity; - jssrcnote *base; - - arity = (intN)js_SrcNoteSpec[SN_TYPE(sn)].arity; - for (base = sn++; arity; sn++, arity--) { - if (*sn & SN_3BYTE_OFFSET_FLAG) - sn += 2; - } - return sn - base; -} - -JS_FRIEND_API(ptrdiff_t) -js_GetSrcNoteOffset(jssrcnote *sn, uintN which) -{ - /* Find the offset numbered which (i.e., skip exactly which offsets). */ - JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA); - JS_ASSERT(which < js_SrcNoteSpec[SN_TYPE(sn)].arity); - for (sn++; which; sn++, which--) { - if (*sn & SN_3BYTE_OFFSET_FLAG) - sn += 2; - } - if (*sn & SN_3BYTE_OFFSET_FLAG) { - return (ptrdiff_t)(((uint32)(sn[0] & SN_3BYTE_OFFSET_MASK) << 16) - | (sn[1] << 8) - | sn[2]); - } - return (ptrdiff_t)*sn; -} - -JSBool -js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index, - uintN which, ptrdiff_t offset) -{ - jssrcnote *sn; - ptrdiff_t diff; - - if ((jsuword)offset >= (jsuword)((ptrdiff_t)SN_3BYTE_OFFSET_FLAG << 16)) { - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } - - /* Find the offset numbered which (i.e., skip exactly which offsets). */ - sn = &CG_NOTES(cg)[index]; - JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA); - JS_ASSERT(which < js_SrcNoteSpec[SN_TYPE(sn)].arity); - for (sn++; which; sn++, which--) { - if (*sn & SN_3BYTE_OFFSET_FLAG) - sn += 2; - } - - /* See if the new offset requires three bytes. */ - if (offset > (ptrdiff_t)SN_3BYTE_OFFSET_MASK) { - /* Maybe this offset was already set to a three-byte value. */ - if (!(*sn & SN_3BYTE_OFFSET_FLAG)) { - /* Losing, need to insert another two bytes for this offset. */ - index = PTRDIFF(sn, CG_NOTES(cg), jssrcnote); - - /* - * Simultaneously test to see if the source note array must grow to - * accomodate either the first or second byte of additional storage - * required by this 3-byte offset. - */ - if (((CG_NOTE_COUNT(cg) + 1) & CG_NOTE_MASK(cg)) <= 1) { - if (!GrowSrcNotes(cx, cg)) - return JS_FALSE; - sn = CG_NOTES(cg) + index; - } - CG_NOTE_COUNT(cg) += 2; - - diff = CG_NOTE_COUNT(cg) - (index + 3); - JS_ASSERT(diff >= 0); - if (diff > 0) - memmove(sn + 3, sn + 1, SRCNOTE_SIZE(diff)); - } - *sn++ = (jssrcnote)(SN_3BYTE_OFFSET_FLAG | (offset >> 16)); - *sn++ = (jssrcnote)(offset >> 8); - } - *sn = (jssrcnote)offset; - return JS_TRUE; -} - -#ifdef DEBUG_notme -#define DEBUG_srcnotesize -#endif - -#ifdef DEBUG_srcnotesize -#define NBINS 10 -static uint32 hist[NBINS]; - -void DumpSrcNoteSizeHist() -{ - static FILE *fp; - int i, n; - - if (!fp) { - fp = fopen("/tmp/srcnotes.hist", "w"); - if (!fp) - return; - setvbuf(fp, NULL, _IONBF, 0); - } - fprintf(fp, "SrcNote size histogram:\n"); - for (i = 0; i < NBINS; i++) { - fprintf(fp, "%4u %4u ", JS_BIT(i), hist[i]); - for (n = (int) JS_HOWMANY(hist[i], 10); n > 0; --n) - fputc('*', fp); - fputc('\n', fp); - } - fputc('\n', fp); -} -#endif - -/* - * Fill in the storage at notes with prolog and main srcnotes; the space at - * notes was allocated using the CG_COUNT_FINAL_SRCNOTES macro from jsemit.h. - * SO DON'T CHANGE THIS FUNCTION WITHOUT AT LEAST CHECKING WHETHER jsemit.h's - * CG_COUNT_FINAL_SRCNOTES MACRO NEEDS CORRESPONDING CHANGES! - */ -JSBool -js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg, jssrcnote *notes) -{ - uintN prologCount, mainCount, totalCount; - ptrdiff_t offset, delta; - jssrcnote *sn; - - JS_ASSERT(cg->current == &cg->main); - - prologCount = cg->prolog.noteCount; - if (prologCount && cg->prolog.currentLine != cg->firstLine) { - CG_SWITCH_TO_PROLOG(cg); - if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)cg->firstLine) < 0) - return JS_FALSE; - prologCount = cg->prolog.noteCount; - CG_SWITCH_TO_MAIN(cg); - } else { - /* - * Either no prolog srcnotes, or no line number change over prolog. - * We don't need a SRC_SETLINE, but we may need to adjust the offset - * of the first main note, by adding to its delta and possibly even - * prepending SRC_XDELTA notes to it to account for prolog bytecodes - * that came at and after the last annotated bytecode. - */ - offset = CG_PROLOG_OFFSET(cg) - cg->prolog.lastNoteOffset; - JS_ASSERT(offset >= 0); - if (offset > 0 && cg->main.noteCount != 0) { - /* NB: Use as much of the first main note's delta as we can. */ - sn = cg->main.notes; - delta = SN_IS_XDELTA(sn) - ? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK) - : SN_DELTA_MASK - (*sn & SN_DELTA_MASK); - if (offset < delta) - delta = offset; - for (;;) { - if (!js_AddToSrcNoteDelta(cx, cg, sn, delta)) - return JS_FALSE; - offset -= delta; - if (offset == 0) - break; - delta = JS_MIN(offset, SN_XDELTA_MASK); - sn = cg->main.notes; - } - } - } - - mainCount = cg->main.noteCount; - totalCount = prologCount + mainCount; - if (prologCount) - memcpy(notes, cg->prolog.notes, SRCNOTE_SIZE(prologCount)); - memcpy(notes + prologCount, cg->main.notes, SRCNOTE_SIZE(mainCount)); - SN_MAKE_TERMINATOR(¬es[totalCount]); - -#ifdef DEBUG_notme - { int bin = JS_CeilingLog2(totalCount); - if (bin >= NBINS) - bin = NBINS - 1; - ++hist[bin]; - } -#endif - return JS_TRUE; -} - -JSBool -js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg) -{ - size_t size, incr; - ptrdiff_t delta; - - size = TRYNOTE_SIZE(cg->treeContext.tryCount); - if (size <= cg->tryNoteSpace) - return JS_TRUE; - - /* - * Allocate trynotes from cx->tempPool. - * XXX Too much growing and we bloat, as other tempPool allocators block - * in-place growth, and we never recycle old free space in an arena. - * YYY But once we consume an entire arena, we'll realloc it, letting the - * malloc heap recycle old space, while still freeing _en masse_ via the - * arena pool. - */ - if (!cg->tryBase) { - size = JS_ROUNDUP(size, TRYNOTE_SIZE(TRYNOTE_CHUNK)); - JS_ARENA_ALLOCATE_CAST(cg->tryBase, JSTryNote *, &cx->tempPool, size); - if (!cg->tryBase) - return JS_FALSE; - cg->tryNoteSpace = size; - cg->tryNext = cg->tryBase; - } else { - delta = PTRDIFF((char *)cg->tryNext, (char *)cg->tryBase, char); - incr = size - cg->tryNoteSpace; - incr = JS_ROUNDUP(incr, TRYNOTE_SIZE(TRYNOTE_CHUNK)); - size = cg->tryNoteSpace; - JS_ARENA_GROW_CAST(cg->tryBase, JSTryNote *, &cx->tempPool, size, incr); - if (!cg->tryBase) - return JS_FALSE; - cg->tryNoteSpace = size + incr; - cg->tryNext = (JSTryNote *)((char *)cg->tryBase + delta); - } - return JS_TRUE; -} - -JSTryNote * -js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start, - ptrdiff_t end, ptrdiff_t catchStart) -{ - JSTryNote *tn; - - JS_ASSERT(cg->tryBase <= cg->tryNext); - JS_ASSERT(catchStart >= 0); - tn = cg->tryNext++; - tn->start = start; - tn->length = end - start; - tn->catchStart = catchStart; - return tn; -} - -void -js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote *notes) -{ - uintN count; - - count = PTRDIFF(cg->tryNext, cg->tryBase, JSTryNote); - if (!count) - return; - - memcpy(notes, cg->tryBase, TRYNOTE_SIZE(count)); - notes[count].start = 0; - notes[count].length = CG_OFFSET(cg); - notes[count].catchStart = 0; -} diff --git a/spidermonkey/src/jsemit.h b/spidermonkey/src/jsemit.h deleted file mode 100644 index 90709c2..0000000 --- a/spidermonkey/src/jsemit.h +++ /dev/null @@ -1,743 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsemit_h___ -#define jsemit_h___ -/* - * JS bytecode generation. - */ - -#include "jsstddef.h" -#include "jstypes.h" -#include "jsatom.h" -#include "jsopcode.h" -#include "jsprvtd.h" -#include "jspubtd.h" - -JS_BEGIN_EXTERN_C - -/* - * NB: If you add enumerators for scope statements, add them between STMT_WITH - * and STMT_CATCH, or you will break the STMT_TYPE_IS_SCOPE macro. If you add - * non-looping statement enumerators, add them before STMT_DO_LOOP or you will - * break the STMT_TYPE_IS_LOOP macro. - * - * Also remember to keep the statementName array in jsemit.c in sync. - */ -typedef enum JSStmtType { - STMT_LABEL, /* labeled statement: L: s */ - STMT_IF, /* if (then) statement */ - STMT_ELSE, /* else clause of if statement */ - STMT_BODY, /* synthetic body of function with - destructuring formal parameters */ - STMT_BLOCK, /* compound statement: { s1[;... sN] } */ - STMT_SWITCH, /* switch statement */ - STMT_WITH, /* with statement */ - STMT_CATCH, /* catch block */ - STMT_TRY, /* try block */ - STMT_FINALLY, /* finally block */ - STMT_SUBROUTINE, /* gosub-target subroutine body */ - STMT_DO_LOOP, /* do/while loop statement */ - STMT_FOR_LOOP, /* for loop statement */ - STMT_FOR_IN_LOOP, /* for/in loop statement */ - STMT_WHILE_LOOP /* while loop statement */ -} JSStmtType; - -#define STMT_TYPE_IN_RANGE(t,b,e) ((uint)((t) - (b)) <= (uintN)((e) - (b))) - -/* - * A comment on the encoding of the JSStmtType enum and type-testing macros: - * - * STMT_TYPE_MAYBE_SCOPE tells whether a statement type is always, or may - * become, a lexical scope. It therefore includes block and switch (the two - * low-numbered "maybe" scope types) and excludes with (with has dynamic scope - * pending the "reformed with" in ES4/JS2). It includes all try-catch-finally - * types, which are high-numbered maybe-scope types. - * - * STMT_TYPE_LINKS_SCOPE tells whether a JSStmtInfo of the given type eagerly - * links to other scoping statement info records. It excludes the two early - * "maybe" types, block and switch, as well as the try and both finally types, - * since try and the other trailing maybe-scope types don't need block scope - * unless they contain let declarations. - * - * We treat with as a static scope because it prevents lexical binding from - * continuing further up the static scope chain. With the "reformed with" - * proposal for JS2, we'll be able to model it statically, too. - */ -#define STMT_TYPE_MAYBE_SCOPE(type) \ - (type != STMT_WITH && \ - STMT_TYPE_IN_RANGE(type, STMT_BLOCK, STMT_SUBROUTINE)) - -#define STMT_TYPE_LINKS_SCOPE(type) \ - STMT_TYPE_IN_RANGE(type, STMT_WITH, STMT_CATCH) - -#define STMT_TYPE_IS_TRYING(type) \ - STMT_TYPE_IN_RANGE(type, STMT_TRY, STMT_SUBROUTINE) - -#define STMT_TYPE_IS_LOOP(type) ((type) >= STMT_DO_LOOP) - -#define STMT_MAYBE_SCOPE(stmt) STMT_TYPE_MAYBE_SCOPE((stmt)->type) -#define STMT_LINKS_SCOPE(stmt) (STMT_TYPE_LINKS_SCOPE((stmt)->type) || \ - ((stmt)->flags & SIF_SCOPE)) -#define STMT_IS_TRYING(stmt) STMT_TYPE_IS_TRYING((stmt)->type) -#define STMT_IS_LOOP(stmt) STMT_TYPE_IS_LOOP((stmt)->type) - -typedef struct JSStmtInfo JSStmtInfo; - -struct JSStmtInfo { - uint16 type; /* statement type */ - uint16 flags; /* flags, see below */ - ptrdiff_t update; /* loop update offset (top if none) */ - ptrdiff_t breaks; /* offset of last break in loop */ - ptrdiff_t continues; /* offset of last continue in loop */ - JSAtom *atom; /* name of LABEL, or block scope object */ - JSStmtInfo *down; /* info for enclosing statement */ - JSStmtInfo *downScope; /* next enclosing lexical scope */ -}; - -#define SIF_SCOPE 0x0001 /* statement has its own lexical scope */ -#define SIF_BODY_BLOCK 0x0002 /* STMT_BLOCK type is a function body */ - -/* - * To reuse space in JSStmtInfo, rename breaks and continues for use during - * try/catch/finally code generation and backpatching. To match most common - * use cases, the macro argument is a struct, not a struct pointer. Only a - * loop, switch, or label statement info record can have breaks and continues, - * and only a for loop has an update backpatch chain, so it's safe to overlay - * these for the "trying" JSStmtTypes. - */ -#define CATCHNOTE(stmt) ((stmt).update) -#define GOSUBS(stmt) ((stmt).breaks) -#define GUARDJUMP(stmt) ((stmt).continues) - -#define AT_TOP_LEVEL(tc) \ - (!(tc)->topStmt || ((tc)->topStmt->flags & SIF_BODY_BLOCK)) - -#define SET_STATEMENT_TOP(stmt, top) \ - ((stmt)->update = (top), (stmt)->breaks = (stmt)->continues = (-1)) - -struct JSTreeContext { /* tree context for semantic checks */ - uint16 flags; /* statement state flags, see below */ - uint16 numGlobalVars; /* max. no. of global variables/regexps */ - uint32 tryCount; /* total count of try statements parsed */ - uint32 globalUses; /* optimizable global var uses in total */ - uint32 loopyGlobalUses;/* optimizable global var uses in loops */ - JSStmtInfo *topStmt; /* top of statement info stack */ - JSStmtInfo *topScopeStmt; /* top lexical scope statement */ - JSObject *blockChain; /* compile time block scope chain (NB: one - deeper than the topScopeStmt/downScope - chain when in head of let block/expr) */ - JSParseNode *blockNode; /* parse node for a lexical scope. - XXX combine with blockChain? */ - JSAtomList decls; /* function, const, and var declarations */ - JSParseNode *nodeList; /* list of recyclable parse-node structs */ -}; - -#define TCF_COMPILING 0x01 /* generating bytecode; this tc is a cg */ -#define TCF_IN_FUNCTION 0x02 /* parsing inside function body */ -#define TCF_RETURN_EXPR 0x04 /* function has 'return expr;' */ -#define TCF_RETURN_VOID 0x08 /* function has 'return;' */ -#define TCF_RETURN_FLAGS 0x0C /* propagate these out of blocks */ -#define TCF_IN_FOR_INIT 0x10 /* parsing init expr of for; exclude 'in' */ -#define TCF_FUN_CLOSURE_VS_VAR 0x20 /* function and var with same name */ -#define TCF_FUN_USES_NONLOCALS 0x40 /* function refers to non-local names */ -#define TCF_FUN_HEAVYWEIGHT 0x80 /* function needs Call object per call */ -#define TCF_FUN_IS_GENERATOR 0x100 /* parsed yield statement in function */ -#define TCF_FUN_FLAGS 0x1E0 /* flags to propagate from FunctionBody */ -#define TCF_HAS_DEFXMLNS 0x200 /* default xml namespace = ...; parsed */ -#define TCF_HAS_FUNCTION_STMT 0x400 /* block contains a function statement */ - -#define TREE_CONTEXT_INIT(tc) \ - ((tc)->flags = (tc)->numGlobalVars = 0, \ - (tc)->tryCount = (tc)->globalUses = (tc)->loopyGlobalUses = 0, \ - (tc)->topStmt = (tc)->topScopeStmt = NULL, \ - (tc)->blockChain = NULL, \ - ATOM_LIST_INIT(&(tc)->decls), \ - (tc)->nodeList = NULL, (tc)->blockNode = NULL) - -#define TREE_CONTEXT_FINISH(tc) \ - ((void)0) - -/* - * Span-dependent instructions are jumps whose span (from the jump bytecode to - * the jump target) may require 2 or 4 bytes of immediate operand. - */ -typedef struct JSSpanDep JSSpanDep; -typedef struct JSJumpTarget JSJumpTarget; - -struct JSSpanDep { - ptrdiff_t top; /* offset of first bytecode in an opcode */ - ptrdiff_t offset; /* offset - 1 within opcode of jump operand */ - ptrdiff_t before; /* original offset - 1 of jump operand */ - JSJumpTarget *target; /* tagged target pointer or backpatch delta */ -}; - -/* - * Jump targets are stored in an AVL tree, for O(log(n)) lookup with targets - * sorted by offset from left to right, so that targets after a span-dependent - * instruction whose jump offset operand must be extended can be found quickly - * and adjusted upward (toward higher offsets). - */ -struct JSJumpTarget { - ptrdiff_t offset; /* offset of span-dependent jump target */ - int balance; /* AVL tree balance number */ - JSJumpTarget *kids[2]; /* left and right AVL tree child pointers */ -}; - -#define JT_LEFT 0 -#define JT_RIGHT 1 -#define JT_OTHER_DIR(dir) (1 - (dir)) -#define JT_IMBALANCE(dir) (((dir) << 1) - 1) -#define JT_DIR(imbalance) (((imbalance) + 1) >> 1) - -/* - * Backpatch deltas are encoded in JSSpanDep.target if JT_TAG_BIT is clear, - * so we can maintain backpatch chains when using span dependency records to - * hold jump offsets that overflow 16 bits. - */ -#define JT_TAG_BIT ((jsword) 1) -#define JT_UNTAG_SHIFT 1 -#define JT_SET_TAG(jt) ((JSJumpTarget *)((jsword)(jt) | JT_TAG_BIT)) -#define JT_CLR_TAG(jt) ((JSJumpTarget *)((jsword)(jt) & ~JT_TAG_BIT)) -#define JT_HAS_TAG(jt) ((jsword)(jt) & JT_TAG_BIT) - -#define BITS_PER_PTRDIFF (sizeof(ptrdiff_t) * JS_BITS_PER_BYTE) -#define BITS_PER_BPDELTA (BITS_PER_PTRDIFF - 1 - JT_UNTAG_SHIFT) -#define BPDELTA_MAX (((ptrdiff_t)1 << BITS_PER_BPDELTA) - 1) -#define BPDELTA_TO_JT(bp) ((JSJumpTarget *)((bp) << JT_UNTAG_SHIFT)) -#define JT_TO_BPDELTA(jt) ((ptrdiff_t)((jsword)(jt) >> JT_UNTAG_SHIFT)) - -#define SD_SET_TARGET(sd,jt) ((sd)->target = JT_SET_TAG(jt)) -#define SD_GET_TARGET(sd) (JS_ASSERT(JT_HAS_TAG((sd)->target)), \ - JT_CLR_TAG((sd)->target)) -#define SD_SET_BPDELTA(sd,bp) ((sd)->target = BPDELTA_TO_JT(bp)) -#define SD_GET_BPDELTA(sd) (JS_ASSERT(!JT_HAS_TAG((sd)->target)), \ - JT_TO_BPDELTA((sd)->target)) - -/* Avoid asserting twice by expanding SD_GET_TARGET in the "then" clause. */ -#define SD_SPAN(sd,pivot) (SD_GET_TARGET(sd) \ - ? JT_CLR_TAG((sd)->target)->offset - (pivot) \ - : 0) - -struct JSCodeGenerator { - JSTreeContext treeContext; /* base state: statement info stack, etc. */ - - JSArenaPool *codePool; /* pointer to thread code arena pool */ - JSArenaPool *notePool; /* pointer to thread srcnote arena pool */ - void *codeMark; /* low watermark in cg->codePool */ - void *noteMark; /* low watermark in cg->notePool */ - void *tempMark; /* low watermark in cx->tempPool */ - - struct { - jsbytecode *base; /* base of JS bytecode vector */ - jsbytecode *limit; /* one byte beyond end of bytecode */ - jsbytecode *next; /* pointer to next free bytecode */ - jssrcnote *notes; /* source notes, see below */ - uintN noteCount; /* number of source notes so far */ - uintN noteMask; /* growth increment for notes */ - ptrdiff_t lastNoteOffset; /* code offset for last source note */ - uintN currentLine; /* line number for tree-based srcnote gen */ - } prolog, main, *current; - - const char *filename; /* null or weak link to source filename */ - uintN firstLine; /* first line, for js_NewScriptFromCG */ - JSPrincipals *principals; /* principals for constant folding eval */ - JSAtomList atomList; /* literals indexed for mapping */ - - intN stackDepth; /* current stack depth in script frame */ - uintN maxStackDepth; /* maximum stack depth so far */ - - JSTryNote *tryBase; /* first exception handling note */ - JSTryNote *tryNext; /* next available note */ - size_t tryNoteSpace; /* # of bytes allocated at tryBase */ - - JSSpanDep *spanDeps; /* span dependent instruction records */ - JSJumpTarget *jumpTargets; /* AVL tree of jump target offsets */ - JSJumpTarget *jtFreeList; /* JT_LEFT-linked list of free structs */ - uintN numSpanDeps; /* number of span dependencies */ - uintN numJumpTargets; /* number of jump targets */ - ptrdiff_t spanDepTodo; /* offset from main.base of potentially - unoptimized spandeps */ - - uintN arrayCompSlot; /* stack slot of array in comprehension */ - - uintN emitLevel; /* js_EmitTree recursion level */ - JSAtomList constList; /* compile time constants */ - JSCodeGenerator *parent; /* Enclosing function or global context */ -}; - -#define CG_BASE(cg) ((cg)->current->base) -#define CG_LIMIT(cg) ((cg)->current->limit) -#define CG_NEXT(cg) ((cg)->current->next) -#define CG_CODE(cg,offset) (CG_BASE(cg) + (offset)) -#define CG_OFFSET(cg) PTRDIFF(CG_NEXT(cg), CG_BASE(cg), jsbytecode) - -#define CG_NOTES(cg) ((cg)->current->notes) -#define CG_NOTE_COUNT(cg) ((cg)->current->noteCount) -#define CG_NOTE_MASK(cg) ((cg)->current->noteMask) -#define CG_LAST_NOTE_OFFSET(cg) ((cg)->current->lastNoteOffset) -#define CG_CURRENT_LINE(cg) ((cg)->current->currentLine) - -#define CG_PROLOG_BASE(cg) ((cg)->prolog.base) -#define CG_PROLOG_LIMIT(cg) ((cg)->prolog.limit) -#define CG_PROLOG_NEXT(cg) ((cg)->prolog.next) -#define CG_PROLOG_CODE(cg,poff) (CG_PROLOG_BASE(cg) + (poff)) -#define CG_PROLOG_OFFSET(cg) PTRDIFF(CG_PROLOG_NEXT(cg), CG_PROLOG_BASE(cg),\ - jsbytecode) - -#define CG_SWITCH_TO_MAIN(cg) ((cg)->current = &(cg)->main) -#define CG_SWITCH_TO_PROLOG(cg) ((cg)->current = &(cg)->prolog) - -/* - * Initialize cg to allocate bytecode space from codePool, source note space - * from notePool, and all other arena-allocated temporaries from cx->tempPool. - * Return true on success. Report an error and return false if the initial - * code segment can't be allocated. - */ -extern JS_FRIEND_API(JSBool) -js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg, - JSArenaPool *codePool, JSArenaPool *notePool, - const char *filename, uintN lineno, - JSPrincipals *principals); - -/* - * Release cg->codePool, cg->notePool, and cx->tempPool to marks set by - * js_InitCodeGenerator. Note that cgs are magic: they own the arena pool - * "tops-of-stack" space above their codeMark, noteMark, and tempMark points. - * This means you cannot alloc from tempPool and save the pointer beyond the - * next JS_FinishCodeGenerator. - */ -extern JS_FRIEND_API(void) -js_FinishCodeGenerator(JSContext *cx, JSCodeGenerator *cg); - -/* - * Emit one bytecode. - */ -extern ptrdiff_t -js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op); - -/* - * Emit two bytecodes, an opcode (op) with a byte of immediate operand (op1). - */ -extern ptrdiff_t -js_Emit2(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1); - -/* - * Emit three bytecodes, an opcode with two bytes of immediate operands. - */ -extern ptrdiff_t -js_Emit3(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1, - jsbytecode op2); - -/* - * Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand. - */ -extern ptrdiff_t -js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra); - -/* - * Unsafe macro to call js_SetJumpOffset and return false if it does. - */ -#define CHECK_AND_SET_JUMP_OFFSET(cx,cg,pc,off) \ - JS_BEGIN_MACRO \ - if (!js_SetJumpOffset(cx, cg, pc, off)) \ - return JS_FALSE; \ - JS_END_MACRO - -#define CHECK_AND_SET_JUMP_OFFSET_AT(cx,cg,off) \ - CHECK_AND_SET_JUMP_OFFSET(cx, cg, CG_CODE(cg,off), CG_OFFSET(cg) - (off)) - -extern JSBool -js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, - ptrdiff_t off); - -/* Test whether we're in a statement of given type. */ -extern JSBool -js_InStatement(JSTreeContext *tc, JSStmtType type); - -/* Test whether we're in a with statement. */ -#define js_InWithStatement(tc) js_InStatement(tc, STMT_WITH) - -/* - * Test whether atom refers to a global variable (or is a reference error). - * Return true in *loopyp if any loops enclose the lexical reference, false - * otherwise. - */ -extern JSBool -js_IsGlobalReference(JSTreeContext *tc, JSAtom *atom, JSBool *loopyp); - -/* - * Push the C-stack-allocated struct at stmt onto the stmtInfo stack. - */ -extern void -js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type, - ptrdiff_t top); - -/* - * Push a block scope statement and link blockAtom's object-valued key into - * tc->blockChain. To pop this statement info record, use js_PopStatement as - * usual, or if appropriate (if generating code), js_PopStatementCG. - */ -extern void -js_PushBlockScope(JSTreeContext *tc, JSStmtInfo *stmt, JSAtom *blockAtom, - ptrdiff_t top); - -/* - * Pop tc->topStmt. If the top JSStmtInfo struct is not stack-allocated, it - * is up to the caller to free it. - */ -extern void -js_PopStatement(JSTreeContext *tc); - -/* - * Like js_PopStatement(&cg->treeContext), also patch breaks and continues - * unless the top statement info record represents a try-catch-finally suite. - * May fail if a jump offset overflows. - */ -extern JSBool -js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg); - -/* - * Define and lookup a primitive jsval associated with the const named by atom. - * js_DefineCompileTimeConstant analyzes the constant-folded initializer at pn - * and saves the const's value in cg->constList, if it can be used at compile - * time. It returns true unless an error occurred. - * - * If the initializer's value could not be saved, js_LookupCompileTimeConstant - * calls will return the undefined value. js_LookupCompileTimeConstant tries - * to find a const value memorized for atom, returning true with *vp set to a - * value other than undefined if the constant was found, true with *vp set to - * JSVAL_VOID if not found, and false on error. - */ -extern JSBool -js_DefineCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, - JSParseNode *pn); - -extern JSBool -js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, - jsval *vp); - -/* - * Find a lexically scoped variable (one declared by let, catch, or an array - * comprehension) named by atom, looking in tc's compile-time scopes. - * - * If a WITH statement is reached along the scope stack, return its statement - * info record, so callers can tell that atom is ambiguous. If slotp is not - * null, then if atom is found, set *slotp to its stack slot, otherwise to -1. - * This means that if slotp is not null, all the block objects on the lexical - * scope chain must have had their depth slots computed by the code generator, - * so the caller must be under js_EmitTree. - * - * In any event, directly return the statement info record in which atom was - * found. Otherwise return null. - */ -extern JSStmtInfo * -js_LexicalLookup(JSTreeContext *tc, JSAtom *atom, jsint *slotp, - JSBool letdecl); - -/* - * Emit code into cg for the tree rooted at pn. - */ -extern JSBool -js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn); - -/* - * Emit function code into cg for the tree rooted at body. - */ -extern JSBool -js_EmitFunctionBytecode(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body); - -/* - * Emit code into cg for the tree rooted at body, then create a persistent - * script for fun from cg. - */ -extern JSBool -js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body, - JSFunction *fun); - -/* - * Source notes generated along with bytecode for decompiling and debugging. - * A source note is a uint8 with 5 bits of type and 3 of offset from the pc of - * the previous note. If 3 bits of offset aren't enough, extended delta notes - * (SRC_XDELTA) consisting of 2 set high order bits followed by 6 offset bits - * are emitted before the next note. Some notes have operand offsets encoded - * immediately after them, in note bytes or byte-triples. - * - * Source Note Extended Delta - * +7-6-5-4-3+2-1-0+ +7-6-5+4-3-2-1-0+ - * |note-type|delta| |1 1| ext-delta | - * +---------+-----+ +---+-----------+ - * - * At most one "gettable" note (i.e., a note of type other than SRC_NEWLINE, - * SRC_SETLINE, and SRC_XDELTA) applies to a given bytecode. - * - * NB: the js_SrcNoteSpec array in jsemit.c is indexed by this enum, so its - * initializers need to match the order here. - * - * Note on adding new source notes: every pair of bytecodes (A, B) where A and - * B have disjoint sets of source notes that could apply to each bytecode may - * reuse the same note type value for two notes (snA, snB) that have the same - * arity, offsetBias, and isSpanDep initializers in js_SrcNoteSpec. This is - * why SRC_IF and SRC_INITPROP have the same value below. For bad historical - * reasons, some bytecodes below that could be overlayed have not been, but - * before using SRC_EXTENDED, consider compressing the existing note types. - * - * Don't forget to update JSXDR_BYTECODE_VERSION in jsxdrapi.h for all such - * incompatible source note or other bytecode changes. - */ -typedef enum JSSrcNoteType { - SRC_NULL = 0, /* terminates a note vector */ - SRC_IF = 1, /* JSOP_IFEQ bytecode is from an if-then */ - SRC_INITPROP = 1, /* disjoint meaning applied to JSOP_INITELEM or - to an index label in a regular (structuring) - or a destructuring object initialiser */ - SRC_IF_ELSE = 2, /* JSOP_IFEQ bytecode is from an if-then-else */ - SRC_WHILE = 3, /* JSOP_IFEQ is from a while loop */ - SRC_FOR = 4, /* JSOP_NOP or JSOP_POP in for loop head */ - SRC_CONTINUE = 5, /* JSOP_GOTO is a continue, not a break; - also used on JSOP_ENDINIT if extra comma - at end of array literal: [1,2,,] */ - SRC_DECL = 6, /* type of a declaration (var, const, let*) */ - SRC_DESTRUCT = 6, /* JSOP_DUP starting a destructuring assignment - operation, with SRC_DECL_* offset operand */ - SRC_PCDELTA = 7, /* distance forward from comma-operator to - next POP, or from CONDSWITCH to first CASE - opcode, etc. -- always a forward delta */ - SRC_GROUPASSIGN = 7, /* SRC_DESTRUCT variant for [a, b] = [c, d] */ - SRC_ASSIGNOP = 8, /* += or another assign-op follows */ - SRC_COND = 9, /* JSOP_IFEQ is from conditional ?: operator */ - SRC_BRACE = 10, /* mandatory brace, for scope or to avoid - dangling else */ - SRC_HIDDEN = 11, /* opcode shouldn't be decompiled */ - SRC_PCBASE = 12, /* distance back from annotated getprop or - setprop op to left-most obj.prop.subprop - bytecode -- always a backward delta */ - SRC_METHODBASE = 13, /* SRC_PCBASE variant for obj.function::foo - gets and sets; disjoint from SRC_LABEL by - bytecode to which it applies */ - SRC_LABEL = 13, /* JSOP_NOP for label: with atomid immediate */ - SRC_LABELBRACE = 14, /* JSOP_NOP for label: {...} begin brace */ - SRC_ENDBRACE = 15, /* JSOP_NOP for label: {...} end brace */ - SRC_BREAK2LABEL = 16, /* JSOP_GOTO for 'break label' with atomid */ - SRC_CONT2LABEL = 17, /* JSOP_GOTO for 'continue label' with atomid */ - SRC_SWITCH = 18, /* JSOP_*SWITCH with offset to end of switch, - 2nd off to first JSOP_CASE if condswitch */ - SRC_FUNCDEF = 19, /* JSOP_NOP for function f() with atomid */ - SRC_CATCH = 20, /* catch block has guard */ - SRC_EXTENDED = 21, /* extended source note, 32-159, in next byte */ - SRC_NEWLINE = 22, /* bytecode follows a source newline */ - SRC_SETLINE = 23, /* a file-absolute source line number note */ - SRC_XDELTA = 24 /* 24-31 are for extended delta notes */ -} JSSrcNoteType; - -/* - * Constants for the SRC_DECL source note. Note that span-dependent bytecode - * selection means that any SRC_DECL offset greater than SRC_DECL_LET may need - * to be adjusted, but these "offsets" are too small to span a span-dependent - * instruction, so can be used to denote distinct declaration syntaxes to the - * decompiler. - * - * NB: the var_prefix array in jsopcode.c depends on these dense indexes from - * SRC_DECL_VAR through SRC_DECL_LET. - */ -#define SRC_DECL_VAR 0 -#define SRC_DECL_CONST 1 -#define SRC_DECL_LET 2 -#define SRC_DECL_NONE 3 - -#define SN_TYPE_BITS 5 -#define SN_DELTA_BITS 3 -#define SN_XDELTA_BITS 6 -#define SN_TYPE_MASK (JS_BITMASK(SN_TYPE_BITS) << SN_DELTA_BITS) -#define SN_DELTA_MASK ((ptrdiff_t)JS_BITMASK(SN_DELTA_BITS)) -#define SN_XDELTA_MASK ((ptrdiff_t)JS_BITMASK(SN_XDELTA_BITS)) - -#define SN_MAKE_NOTE(sn,t,d) (*(sn) = (jssrcnote) \ - (((t) << SN_DELTA_BITS) \ - | ((d) & SN_DELTA_MASK))) -#define SN_MAKE_XDELTA(sn,d) (*(sn) = (jssrcnote) \ - ((SRC_XDELTA << SN_DELTA_BITS) \ - | ((d) & SN_XDELTA_MASK))) - -#define SN_IS_XDELTA(sn) ((*(sn) >> SN_DELTA_BITS) >= SRC_XDELTA) -#define SN_TYPE(sn) (SN_IS_XDELTA(sn) ? SRC_XDELTA \ - : *(sn) >> SN_DELTA_BITS) -#define SN_SET_TYPE(sn,type) SN_MAKE_NOTE(sn, type, SN_DELTA(sn)) -#define SN_IS_GETTABLE(sn) (SN_TYPE(sn) < SRC_NEWLINE) - -#define SN_DELTA(sn) ((ptrdiff_t)(SN_IS_XDELTA(sn) \ - ? *(sn) & SN_XDELTA_MASK \ - : *(sn) & SN_DELTA_MASK)) -#define SN_SET_DELTA(sn,delta) (SN_IS_XDELTA(sn) \ - ? SN_MAKE_XDELTA(sn, delta) \ - : SN_MAKE_NOTE(sn, SN_TYPE(sn), delta)) - -#define SN_DELTA_LIMIT ((ptrdiff_t)JS_BIT(SN_DELTA_BITS)) -#define SN_XDELTA_LIMIT ((ptrdiff_t)JS_BIT(SN_XDELTA_BITS)) - -/* - * Offset fields follow certain notes and are frequency-encoded: an offset in - * [0,0x7f] consumes one byte, an offset in [0x80,0x7fffff] takes three, and - * the high bit of the first byte is set. - */ -#define SN_3BYTE_OFFSET_FLAG 0x80 -#define SN_3BYTE_OFFSET_MASK 0x7f - -typedef struct JSSrcNoteSpec { - const char *name; /* name for disassembly/debugging output */ - uint8 arity; /* number of offset operands */ - uint8 offsetBias; /* bias of offset(s) from annotated pc */ - int8 isSpanDep; /* 1 or -1 if offsets could span extended ops, - 0 otherwise; sign tells span direction */ -} JSSrcNoteSpec; - -extern JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[]; -extern JS_FRIEND_API(uintN) js_SrcNoteLength(jssrcnote *sn); - -#define SN_LENGTH(sn) ((js_SrcNoteSpec[SN_TYPE(sn)].arity == 0) ? 1 \ - : js_SrcNoteLength(sn)) -#define SN_NEXT(sn) ((sn) + SN_LENGTH(sn)) - -/* A source note array is terminated by an all-zero element. */ -#define SN_MAKE_TERMINATOR(sn) (*(sn) = SRC_NULL) -#define SN_IS_TERMINATOR(sn) (*(sn) == SRC_NULL) - -/* - * Append a new source note of the given type (and therefore size) to cg's - * notes dynamic array, updating cg->noteCount. Return the new note's index - * within the array pointed at by cg->current->notes. Return -1 if out of - * memory. - */ -extern intN -js_NewSrcNote(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type); - -extern intN -js_NewSrcNote2(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, - ptrdiff_t offset); - -extern intN -js_NewSrcNote3(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, - ptrdiff_t offset1, ptrdiff_t offset2); - -/* - * NB: this function can add at most one extra extended delta note. - */ -extern jssrcnote * -js_AddToSrcNoteDelta(JSContext *cx, JSCodeGenerator *cg, jssrcnote *sn, - ptrdiff_t delta); - -/* - * Get and set the offset operand identified by which (0 for the first, etc.). - */ -extern JS_FRIEND_API(ptrdiff_t) -js_GetSrcNoteOffset(jssrcnote *sn, uintN which); - -extern JSBool -js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index, - uintN which, ptrdiff_t offset); - -/* - * Finish taking source notes in cx's notePool, copying final notes to the new - * stable store allocated by the caller and passed in via notes. Return false - * on malloc failure, which means this function reported an error. - * - * To compute the number of jssrcnotes to allocate and pass in via notes, use - * the CG_COUNT_FINAL_SRCNOTES macro. This macro knows a lot about details of - * js_FinishTakingSrcNotes, SO DON'T CHANGE jsemit.c's js_FinishTakingSrcNotes - * FUNCTION WITHOUT CHECKING WHETHER THIS MACRO NEEDS CORRESPONDING CHANGES! - */ -#define CG_COUNT_FINAL_SRCNOTES(cg, cnt) \ - JS_BEGIN_MACRO \ - ptrdiff_t diff_ = CG_PROLOG_OFFSET(cg) - (cg)->prolog.lastNoteOffset; \ - cnt = (cg)->prolog.noteCount + (cg)->main.noteCount + 1; \ - if ((cg)->prolog.noteCount && \ - (cg)->prolog.currentLine != (cg)->firstLine) { \ - if (diff_ > SN_DELTA_MASK) \ - cnt += JS_HOWMANY(diff_ - SN_DELTA_MASK, SN_XDELTA_MASK); \ - cnt += 2 + (((cg)->firstLine > SN_3BYTE_OFFSET_MASK) << 1); \ - } else if (diff_ > 0) { \ - if (cg->main.noteCount) { \ - jssrcnote *sn_ = (cg)->main.notes; \ - diff_ -= SN_IS_XDELTA(sn_) \ - ? SN_XDELTA_MASK - (*sn_ & SN_XDELTA_MASK) \ - : SN_DELTA_MASK - (*sn_ & SN_DELTA_MASK); \ - } \ - if (diff_ > 0) \ - cnt += JS_HOWMANY(diff_, SN_XDELTA_MASK); \ - } \ - JS_END_MACRO - -extern JSBool -js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg, jssrcnote *notes); - -/* - * Allocate cg->treeContext.tryCount notes (plus one for the end sentinel) - * from cx->tempPool and set up cg->tryBase/tryNext for exactly tryCount - * js_NewTryNote calls. The storage is freed by js_FinishCodeGenerator. - */ -extern JSBool -js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg); - -/* - * Grab the next trynote slot in cg, filling it in appropriately. - */ -extern JSTryNote * -js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start, - ptrdiff_t end, ptrdiff_t catchStart); - -/* - * Finish generating exception information into the space at notes. As with - * js_FinishTakingSrcNotes, the caller must use CG_COUNT_FINAL_TRYNOTES(cg) to - * preallocate enough space in a JSTryNote[] to pass as the notes parameter of - * js_FinishTakingTryNotes. - */ -#define CG_COUNT_FINAL_TRYNOTES(cg, cnt) \ - JS_BEGIN_MACRO \ - cnt = ((cg)->tryNext > (cg)->tryBase) \ - ? PTRDIFF(cg->tryNext, cg->tryBase, JSTryNote) + 1 \ - : 0; \ - JS_END_MACRO - -extern void -js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote *notes); - -JS_END_EXTERN_C - -#endif /* jsemit_h___ */ diff --git a/spidermonkey/src/jsexn.c b/spidermonkey/src/jsexn.c deleted file mode 100644 index e60f85e..0000000 --- a/spidermonkey/src/jsexn.c +++ /dev/null @@ -1,1348 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS standard exception implementation. - */ - -#include "jsstddef.h" -#include -#include -#include "jstypes.h" -#include "jsbit.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jsprf.h" -#include "jsapi.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsdbgapi.h" -#include "jsexn.h" -#include "jsfun.h" -#include "jsinterp.h" -#include "jsnum.h" -#include "jsopcode.h" -#include "jsscript.h" - -/* Forward declarations for js_ErrorClass's initializer. */ -static JSBool -Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); - -static void -exn_finalize(JSContext *cx, JSObject *obj); - -static uint32 -exn_mark(JSContext *cx, JSObject *obj, void *arg); - -static void -exn_finalize(JSContext *cx, JSObject *obj); - -static JSBool -exn_enumerate(JSContext *cx, JSObject *obj); - -static JSBool -exn_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, - JSObject **objp); - -JSClass js_ErrorClass = { - js_Error_str, - JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | - JSCLASS_HAS_CACHED_PROTO(JSProto_Error), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - exn_enumerate, (JSResolveOp)exn_resolve, JS_ConvertStub, exn_finalize, - NULL, NULL, NULL, Exception, - NULL, NULL, exn_mark, NULL -}; - -typedef struct JSStackTraceElem { - JSString *funName; - size_t argc; - const char *filename; - uintN ulineno; -} JSStackTraceElem; - -typedef struct JSExnPrivate { - /* A copy of the JSErrorReport originally generated. */ - JSErrorReport *errorReport; - JSString *message; - JSString *filename; - uintN lineno; - size_t stackDepth; - JSStackTraceElem stackElems[1]; -} JSExnPrivate; - -static JSString * -StackTraceToString(JSContext *cx, JSExnPrivate *priv); - -static JSErrorReport * -CopyErrorReport(JSContext *cx, JSErrorReport *report) -{ - /* - * We use a single malloc block to make a deep copy of JSErrorReport with - * the following layout: - * JSErrorReport - * array of copies of report->messageArgs - * jschar array with characters for all messageArgs - * jschar array with characters for ucmessage - * jschar array with characters for uclinebuf and uctokenptr - * char array with characters for linebuf and tokenptr - * char array with characters for filename - * Such layout together with the properties enforced by the following - * asserts does not need any extra alignment padding. - */ - JS_STATIC_ASSERT(sizeof(JSErrorReport) % sizeof(const char *) == 0); - JS_STATIC_ASSERT(sizeof(const char *) % sizeof(jschar) == 0); - - size_t filenameSize; - size_t linebufSize; - size_t uclinebufSize; - size_t ucmessageSize; - size_t i, argsArraySize, argsCopySize, argSize; - size_t mallocSize; - JSErrorReport *copy; - uint8 *cursor; - -#define JS_CHARS_SIZE(jschars) ((js_strlen(jschars) + 1) * sizeof(jschar)) - - filenameSize = report->filename ? strlen(report->filename) + 1 : 0; - linebufSize = report->linebuf ? strlen(report->linebuf) + 1 : 0; - uclinebufSize = report->uclinebuf ? JS_CHARS_SIZE(report->uclinebuf) : 0; - ucmessageSize = 0; - argsArraySize = 0; - argsCopySize = 0; - if (report->ucmessage) { - ucmessageSize = JS_CHARS_SIZE(report->ucmessage); - if (report->messageArgs) { - for (i = 0; report->messageArgs[i]; ++i) - argsCopySize += JS_CHARS_SIZE(report->messageArgs[i]); - - /* Non-null messageArgs should have at least one non-null arg. */ - JS_ASSERT(i != 0); - argsArraySize = (i + 1) * sizeof(const jschar *); - } - } - - /* - * The mallocSize can not overflow since it represents the sum of the - * sizes of already allocated objects. - */ - mallocSize = sizeof(JSErrorReport) + argsArraySize + argsCopySize + - ucmessageSize + uclinebufSize + linebufSize + filenameSize; - cursor = (uint8 *)JS_malloc(cx, mallocSize); - if (!cursor) - return NULL; - - copy = (JSErrorReport *)cursor; - memset(cursor, 0, sizeof(JSErrorReport)); - cursor += sizeof(JSErrorReport); - - if (argsArraySize != 0) { - copy->messageArgs = (const jschar **)cursor; - cursor += argsArraySize; - for (i = 0; report->messageArgs[i]; ++i) { - copy->messageArgs[i] = (const jschar *)cursor; - argSize = JS_CHARS_SIZE(report->messageArgs[i]); - memcpy(cursor, report->messageArgs[i], argSize); - cursor += argSize; - } - copy->messageArgs[i] = NULL; - JS_ASSERT(cursor == (uint8 *)copy->messageArgs[0] + argsCopySize); - } - - if (report->ucmessage) { - copy->ucmessage = (const jschar *)cursor; - memcpy(cursor, report->ucmessage, ucmessageSize); - cursor += ucmessageSize; - } - - if (report->uclinebuf) { - copy->uclinebuf = (const jschar *)cursor; - memcpy(cursor, report->uclinebuf, uclinebufSize); - cursor += uclinebufSize; - if (report->uctokenptr) { - copy->uctokenptr = copy->uclinebuf + (report->uctokenptr - - report->uclinebuf); - } - } - - if (report->linebuf) { - copy->linebuf = (const char *)cursor; - memcpy(cursor, report->linebuf, linebufSize); - cursor += linebufSize; - if (report->tokenptr) { - copy->tokenptr = copy->linebuf + (report->tokenptr - - report->linebuf); - } - } - - if (report->filename) { - copy->filename = (const char *)cursor; - memcpy(cursor, report->filename, filenameSize); - } - JS_ASSERT(cursor + filenameSize == (uint8 *)copy + mallocSize); - - /* Copy non-pointer members. */ - copy->lineno = report->lineno; - copy->errorNumber = report->errorNumber; - - /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */ - copy->flags = report->flags; - -#undef JS_CHARS_SIZE - return copy; -} - -static jsval * -GetStackTraceValueBuffer(JSExnPrivate *priv) -{ - /* - * We use extra memory after JSExnPrivateInfo.stackElems to store jsvals - * that helps to produce more informative stack traces. The following - * assert allows us to assume that no gap after stackElems is necessary to - * align the buffer properly. - */ - JS_STATIC_ASSERT(sizeof(JSStackTraceElem) % sizeof(jsval) == 0); - - return (jsval *)(priv->stackElems + priv->stackDepth); -} - -static JSBool -InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message, - JSString *filename, uintN lineno, JSErrorReport *report) -{ - JSCheckAccessOp checkAccess; - JSErrorReporter older; - JSExceptionState *state; - jsval callerid, v; - JSStackFrame *fp, *fpstop; - size_t stackDepth, valueCount, size; - JSBool overflow; - JSExnPrivate *priv; - JSStackTraceElem *elem; - jsval *values; - - JS_ASSERT(OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass); - - /* - * Prepare stack trace data. - * - * Set aside any error reporter for cx and save its exception state - * so we can suppress any checkAccess failures. Such failures should stop - * the backtrace procedure, not result in a failure of this constructor. - */ - checkAccess = cx->runtime->checkObjectAccess; - older = JS_SetErrorReporter(cx, NULL); - state = JS_SaveExceptionState(cx); - - callerid = ATOM_KEY(cx->runtime->atomState.callerAtom); - stackDepth = 0; - valueCount = 0; - for (fp = cx->fp; fp; fp = fp->down) { - if (fp->fun && fp->argv) { - if (checkAccess) { - v = fp->argv[-2]; - if (!JSVAL_IS_PRIMITIVE(v) && - !checkAccess(cx, JSVAL_TO_OBJECT(v), callerid, - JSACC_READ, &v /* ignored */)) { - break; - } - } - valueCount += fp->argc; - } - ++stackDepth; - } - JS_RestoreExceptionState(cx, state); - JS_SetErrorReporter(cx, older); - fpstop = fp; - - size = offsetof(JSExnPrivate, stackElems); - overflow = (stackDepth > ((size_t)-1 - size) / sizeof(JSStackTraceElem)); - size += stackDepth * sizeof(JSStackTraceElem); - overflow |= (valueCount > ((size_t)-1 - size) / sizeof(jsval)); - size += valueCount * sizeof(jsval); - if (overflow) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - priv = (JSExnPrivate *)JS_malloc(cx, size); - if (!priv) - return JS_FALSE; - - /* - * We initialize errorReport with a copy of report after setting the - * private slot, to prevent GC accessing a junk value we clear the field - * here. - */ - priv->errorReport = NULL; - priv->message = message; - priv->filename = filename; - priv->lineno = lineno; - priv->stackDepth = stackDepth; - - values = GetStackTraceValueBuffer(priv); - elem = priv->stackElems; - for (fp = cx->fp; fp != fpstop; fp = fp->down) { - if (!fp->fun) { - elem->funName = NULL; - elem->argc = 0; - } else { - elem->funName = fp->fun->atom - ? ATOM_TO_STRING(fp->fun->atom) - : cx->runtime->emptyString; - elem->argc = fp->argc; - memcpy(values, fp->argv, fp->argc * sizeof(jsval)); - values += fp->argc; - } - elem->ulineno = 0; - elem->filename = NULL; - if (fp->script) { - elem->filename = fp->script->filename; - if (fp->pc) - elem->ulineno = js_PCToLineNumber(cx, fp->script, fp->pc); - } - ++elem; - } - JS_ASSERT(priv->stackElems + stackDepth == elem); - JS_ASSERT(GetStackTraceValueBuffer(priv) + valueCount == values); - - OBJ_SET_SLOT(cx, exnObject, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(priv)); - - if (report) { - /* - * Construct a new copy of the error report struct. We can't use the - * error report struct that was passed in, because it's allocated on - * the stack, and also because it may point to transient data in the - * JSTokenStream. - */ - priv->errorReport = CopyErrorReport(cx, report); - if (!priv->errorReport) { - /* The finalizer realeases priv since it is in the private slot. */ - return JS_FALSE; - } - } - - return JS_TRUE; -} - -static JSExnPrivate * -GetExnPrivate(JSContext *cx, JSObject *obj) -{ - jsval privateValue; - JSExnPrivate *priv; - - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ErrorClass); - privateValue = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - if (JSVAL_IS_VOID(privateValue)) - return NULL; - priv = (JSExnPrivate *)JSVAL_TO_PRIVATE(privateValue); - JS_ASSERT(priv); - return priv; -} - -static uint32 -exn_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSExnPrivate *priv; - JSStackTraceElem *elem; - size_t vcount, i; - jsval *vp, v; - - priv = GetExnPrivate(cx, obj); - if (priv) { - GC_MARK(cx, priv->message, "exception message"); - GC_MARK(cx, priv->filename, "exception filename"); - elem = priv->stackElems; - for (vcount = i = 0; i != priv->stackDepth; ++i, ++elem) { - if (elem->funName) - GC_MARK(cx, elem->funName, "stack trace function name"); - if (elem->filename) - js_MarkScriptFilename(elem->filename); - vcount += elem->argc; - } - vp = GetStackTraceValueBuffer(priv); - for (i = 0; i != vcount; ++i, ++vp) { - v = *vp; - if (JSVAL_IS_GCTHING(v)) - GC_MARK(cx, JSVAL_TO_GCTHING(v), "stack trace argument"); - } - } - return 0; -} - -static void -exn_finalize(JSContext *cx, JSObject *obj) -{ - JSExnPrivate *priv; - - priv = GetExnPrivate(cx, obj); - if (priv) { - if (priv->errorReport) - JS_free(cx, priv->errorReport); - JS_free(cx, priv); - } -} - -static JSBool -exn_enumerate(JSContext *cx, JSObject *obj) -{ - JSAtomState *atomState; - uintN i; - JSAtom *atom; - JSObject *pobj; - JSProperty *prop; - - JS_STATIC_ASSERT(sizeof(JSAtomState) <= (size_t)(uint16)-1); - static const uint16 offsets[] = { - (uint16)offsetof(JSAtomState, messageAtom), - (uint16)offsetof(JSAtomState, fileNameAtom), - (uint16)offsetof(JSAtomState, lineNumberAtom), - (uint16)offsetof(JSAtomState, stackAtom), - }; - - atomState = &cx->runtime->atomState; - for (i = 0; i != JS_ARRAY_LENGTH(offsets); ++i) { - atom = *(JSAtom **)((uint8 *)atomState + offsets[i]); - if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) - return JS_FALSE; - if (prop) - OBJ_DROP_PROPERTY(cx, pobj, prop); - } - return JS_TRUE; -} - -static JSBool -exn_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, - JSObject **objp) -{ - JSExnPrivate *priv; - JSString *str; - JSAtom *atom; - JSString *stack; - const char *prop; - jsval v; - - *objp = NULL; - priv = GetExnPrivate(cx, obj); - if (priv && JSVAL_IS_STRING(id)) { - str = JSVAL_TO_STRING(id); - - atom = cx->runtime->atomState.messageAtom; - if (str == ATOM_TO_STRING(atom)) { - prop = js_message_str; - v = STRING_TO_JSVAL(priv->message); - goto define; - } - - atom = cx->runtime->atomState.fileNameAtom; - if (str == ATOM_TO_STRING(atom)) { - prop = js_fileName_str; - v = STRING_TO_JSVAL(priv->filename); - goto define; - } - - atom = cx->runtime->atomState.lineNumberAtom; - if (str == ATOM_TO_STRING(atom)) { - prop = js_lineNumber_str; - v = INT_TO_JSVAL(priv->lineno); - goto define; - } - - atom = cx->runtime->atomState.stackAtom; - if (str == ATOM_TO_STRING(atom)) { - stack = StackTraceToString(cx, priv); - if (!stack) - return JS_FALSE; - - /* Allow to GC all things that were used to build stack trace. */ - priv->stackDepth = 0; - prop = js_stack_str; - v = STRING_TO_JSVAL(stack); - goto define; - } - } - return JS_TRUE; - - define: - if (!JS_DefineProperty(cx, obj, prop, v, NULL, NULL, JSPROP_ENUMERATE)) - return JS_FALSE; - *objp = obj; - return JS_TRUE; -} - -JSErrorReport * -js_ErrorFromException(JSContext *cx, jsval exn) -{ - JSObject *obj; - JSExnPrivate *priv; - - if (JSVAL_IS_PRIMITIVE(exn)) - return NULL; - obj = JSVAL_TO_OBJECT(exn); - if (OBJ_GET_CLASS(cx, obj) != &js_ErrorClass) - return NULL; - priv = GetExnPrivate(cx, obj); - if (!priv) - return NULL; - return priv->errorReport; -} - -struct JSExnSpec { - int protoIndex; - const char *name; - JSProtoKey key; - JSNative native; -}; - -/* - * All *Error constructors share the same JSClass, js_ErrorClass. But each - * constructor function for an *Error class must have a distinct native 'call' - * function pointer, in order for instanceof to work properly across multiple - * standard class sets. See jsfun.c:fun_hasInstance. - */ -#define MAKE_EXCEPTION_CTOR(name) \ -static JSBool \ -name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) \ -{ \ - return Exception(cx, obj, argc, argv, rval); \ -} - -MAKE_EXCEPTION_CTOR(Error) -MAKE_EXCEPTION_CTOR(InternalError) -MAKE_EXCEPTION_CTOR(EvalError) -MAKE_EXCEPTION_CTOR(RangeError) -MAKE_EXCEPTION_CTOR(ReferenceError) -MAKE_EXCEPTION_CTOR(SyntaxError) -MAKE_EXCEPTION_CTOR(TypeError) -MAKE_EXCEPTION_CTOR(URIError) - -#undef MAKE_EXCEPTION_CTOR - -static struct JSExnSpec exceptions[] = { - {JSEXN_NONE, js_Error_str, JSProto_Error, Error}, - {JSEXN_ERR, js_InternalError_str, JSProto_InternalError, InternalError}, - {JSEXN_ERR, js_EvalError_str, JSProto_EvalError, EvalError}, - {JSEXN_ERR, js_RangeError_str, JSProto_RangeError, RangeError}, - {JSEXN_ERR, js_ReferenceError_str, JSProto_ReferenceError, ReferenceError}, - {JSEXN_ERR, js_SyntaxError_str, JSProto_SyntaxError, SyntaxError}, - {JSEXN_ERR, js_TypeError_str, JSProto_TypeError, TypeError}, - {JSEXN_ERR, js_URIError_str, JSProto_URIError, URIError}, - {0, NULL, JSProto_Null, NULL} -}; - -static JSString * -ValueToShortSource(JSContext *cx, jsval v) -{ - JSString *str; - - /* Avoid toSource bloat and fallibility for object types. */ - if (JSVAL_IS_PRIMITIVE(v)) { - str = js_ValueToSource(cx, v); - } else if (VALUE_IS_FUNCTION(cx, v)) { - /* - * XXX Avoid function decompilation bloat for now. - */ - str = JS_GetFunctionId(JS_ValueToFunction(cx, v)); - if (!str && !(str = js_ValueToSource(cx, v))) { - /* - * Continue to soldier on if the function couldn't be - * converted into a string. - */ - JS_ClearPendingException(cx); - str = JS_NewStringCopyZ(cx, "[unknown function]"); - } - } else { - /* - * XXX Avoid toString on objects, it takes too long and uses too much - * memory, for too many classes (see Mozilla bug 166743). - */ - char buf[100]; - JS_snprintf(buf, sizeof buf, "[object %s]", - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v))->name); - str = JS_NewStringCopyZ(cx, buf); - } - return str; -} - -static JSString * -StackTraceToString(JSContext *cx, JSExnPrivate *priv) -{ - jschar *stackbuf; - size_t stacklen, stackmax; - JSStackTraceElem *elem, *endElem; - jsval *values; - size_t i; - JSString *str; - const char *cp; - char ulnbuf[11]; - - /* After this point, failing control flow must goto bad. */ - stackbuf = NULL; - stacklen = stackmax = 0; - -/* Limit the stackbuf length to a reasonable value to avoid overflow checks. */ -#define STACK_LENGTH_LIMIT JS_BIT(20) - -#define APPEND_CHAR_TO_STACK(c) \ - JS_BEGIN_MACRO \ - if (stacklen == stackmax) { \ - void *ptr_; \ - if (stackmax >= STACK_LENGTH_LIMIT) \ - goto done; \ - stackmax = stackmax ? 2 * stackmax : 64; \ - ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \ - if (!ptr_) \ - goto bad; \ - stackbuf = ptr_; \ - } \ - stackbuf[stacklen++] = (c); \ - JS_END_MACRO - -#define APPEND_STRING_TO_STACK(str) \ - JS_BEGIN_MACRO \ - JSString *str_ = str; \ - size_t length_ = JSSTRING_LENGTH(str_); \ - if (length_ > stackmax - stacklen) { \ - void *ptr_; \ - if (stackmax >= STACK_LENGTH_LIMIT || \ - length_ >= STACK_LENGTH_LIMIT - stacklen) { \ - goto done; \ - } \ - stackmax = JS_BIT(JS_CeilingLog2(stacklen + length_)); \ - ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \ - if (!ptr_) \ - goto bad; \ - stackbuf = ptr_; \ - } \ - js_strncpy(stackbuf + stacklen, JSSTRING_CHARS(str_), length_); \ - stacklen += length_; \ - JS_END_MACRO - - values = GetStackTraceValueBuffer(priv); - elem = priv->stackElems; - for (endElem = elem + priv->stackDepth; elem != endElem; elem++) { - if (elem->funName) { - APPEND_STRING_TO_STACK(elem->funName); - APPEND_CHAR_TO_STACK('('); - for (i = 0; i != elem->argc; i++, values++) { - if (i > 0) - APPEND_CHAR_TO_STACK(','); - str = ValueToShortSource(cx, *values); - if (!str) - goto bad; - APPEND_STRING_TO_STACK(str); - } - APPEND_CHAR_TO_STACK(')'); - } - APPEND_CHAR_TO_STACK('@'); - if (elem->filename) { - for (cp = elem->filename; *cp; cp++) - APPEND_CHAR_TO_STACK(*cp); - } - APPEND_CHAR_TO_STACK(':'); - JS_snprintf(ulnbuf, sizeof ulnbuf, "%u", elem->ulineno); - for (cp = ulnbuf; *cp; cp++) - APPEND_CHAR_TO_STACK(*cp); - APPEND_CHAR_TO_STACK('\n'); - } -#undef APPEND_CHAR_TO_STACK -#undef APPEND_STRING_TO_STACK -#undef STACK_LENGTH_LIMIT - - done: - if (stacklen == 0) { - JS_ASSERT(!stackbuf); - return cx->runtime->emptyString; - } - if (stacklen < stackmax) { - /* - * Realloc can fail when shrinking on some FreeBSD versions, so - * don't use JS_realloc here; simply let the oversized allocation - * be owned by the string in that rare case. - */ - void *shrunk = JS_realloc(cx, stackbuf, (stacklen+1) * sizeof(jschar)); - if (shrunk) - stackbuf = shrunk; - } - - stackbuf[stacklen] = 0; - str = js_NewString(cx, stackbuf, stacklen, 0); - if (str) - return str; - - bad: - if (stackbuf) - JS_free(cx, stackbuf); - return NULL; -} - -/* XXXbe Consolidate the ugly truth that we don't treat filename as UTF-8 - with these two functions. */ -static JSString * -FilenameToString(JSContext *cx, const char *filename) -{ - return JS_NewStringCopyZ(cx, filename); -} - -static const char * -StringToFilename(JSContext *cx, JSString *str) -{ - return JS_GetStringBytes(str); -} - -static JSBool -Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSBool ok; - uint32 lineno; - JSString *message, *filename; - JSStackFrame *fp; - - if (cx->creatingException) - return JS_FALSE; - cx->creatingException = JS_TRUE; - - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - /* - * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when - * called as functions, without operator new. But as we do not give - * each constructor a distinct JSClass, whose .name member is used by - * js_NewObject to find the class prototype, we must get the class - * prototype ourselves. - */ - ok = OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(argv[-2]), - ATOM_TO_JSID(cx->runtime->atomState - .classPrototypeAtom), - rval); - if (!ok) - goto out; - obj = js_NewObject(cx, &js_ErrorClass, JSVAL_TO_OBJECT(*rval), NULL); - if (!obj) { - ok = JS_FALSE; - goto out; - } - *rval = OBJECT_TO_JSVAL(obj); - } - - /* - * If it's a new object of class Exception, then null out the private - * data so that the finalizer doesn't attempt to free it. - */ - if (OBJ_GET_CLASS(cx, obj) == &js_ErrorClass) - OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, JSVAL_VOID); - - /* Set the 'message' property. */ - if (argc != 0) { - message = js_ValueToString(cx, argv[0]); - if (!message) { - ok = JS_FALSE; - goto out; - } - argv[0] = STRING_TO_JSVAL(message); - } else { - message = cx->runtime->emptyString; - } - - /* Set the 'fileName' property. */ - if (argc > 1) { - filename = js_ValueToString(cx, argv[1]); - if (!filename) { - ok = JS_FALSE; - goto out; - } - argv[1] = STRING_TO_JSVAL(filename); - fp = NULL; - } else { - fp = JS_GetScriptedCaller(cx, NULL); - if (fp) { - filename = FilenameToString(cx, fp->script->filename); - if (!filename) { - ok = JS_FALSE; - goto out; - } - } else { - filename = cx->runtime->emptyString; - } - } - - /* Set the 'lineNumber' property. */ - if (argc > 2) { - ok = js_ValueToECMAUint32(cx, argv[2], &lineno); - if (!ok) - goto out; - } else { - if (!fp) - fp = JS_GetScriptedCaller(cx, NULL); - lineno = (fp && fp->pc) ? js_PCToLineNumber(cx, fp->script, fp->pc) : 0; - } - - ok = (OBJ_GET_CLASS(cx, obj) != &js_ErrorClass) || - InitExnPrivate(cx, obj, message, filename, lineno, NULL); - - out: - cx->creatingException = JS_FALSE; - return ok; -} - -/* - * Convert to string. - * - * This method only uses JavaScript-modifiable properties name, message. It - * is left to the host to check for private data and report filename and line - * number information along with this message. - */ -static JSBool -exn_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval v; - JSString *name, *message, *result; - jschar *chars, *cp; - size_t name_length, message_length, length; - - if (!OBJ_GET_PROPERTY(cx, obj, - ATOM_TO_JSID(cx->runtime->atomState.nameAtom), - &v)) { - return JS_FALSE; - } - name = JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v) : cx->runtime->emptyString; - *rval = STRING_TO_JSVAL(name); - - if (!JS_GetProperty(cx, obj, js_message_str, &v)) - return JS_FALSE; - message = JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v) - : cx->runtime->emptyString; - - if (JSSTRING_LENGTH(message) != 0) { - name_length = JSSTRING_LENGTH(name); - message_length = JSSTRING_LENGTH(message); - length = (name_length ? name_length + 2 : 0) + message_length; - cp = chars = (jschar*) JS_malloc(cx, (length + 1) * sizeof(jschar)); - if (!chars) - return JS_FALSE; - - if (name_length) { - js_strncpy(cp, JSSTRING_CHARS(name), name_length); - cp += name_length; - *cp++ = ':'; *cp++ = ' '; - } - js_strncpy(cp, JSSTRING_CHARS(message), message_length); - cp += message_length; - *cp = 0; - - result = js_NewString(cx, chars, length, 0); - if (!result) { - JS_free(cx, chars); - return JS_FALSE; - } - } else { - result = name; - } - - *rval = STRING_TO_JSVAL(result); - return JS_TRUE; -} - -#if JS_HAS_TOSOURCE -/* - * Return a string that may eval to something similar to the original object. - */ -static JSBool -exn_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval *vp; - JSString *name, *message, *filename, *lineno_as_str, *result; - uint32 lineno; - size_t lineno_length, name_length, message_length, filename_length, length; - jschar *chars, *cp; - - vp = argv + argc; /* beginning of explicit local roots */ - - if (!OBJ_GET_PROPERTY(cx, obj, - ATOM_TO_JSID(cx->runtime->atomState.nameAtom), - rval)) { - return JS_FALSE; - } - name = js_ValueToString(cx, *rval); - if (!name) - return JS_FALSE; - *rval = STRING_TO_JSVAL(name); - - if (!JS_GetProperty(cx, obj, js_message_str, &vp[0]) || - !(message = js_ValueToSource(cx, vp[0]))) { - return JS_FALSE; - } - vp[0] = STRING_TO_JSVAL(message); - - if (!JS_GetProperty(cx, obj, js_fileName_str, &vp[1]) || - !(filename = js_ValueToSource(cx, vp[1]))) { - return JS_FALSE; - } - vp[1] = STRING_TO_JSVAL(filename); - - if (!JS_GetProperty(cx, obj, js_lineNumber_str, &vp[2]) || - !js_ValueToECMAUint32 (cx, vp[2], &lineno)) { - return JS_FALSE; - } - - if (lineno != 0) { - lineno_as_str = js_ValueToString(cx, vp[2]); - if (!lineno_as_str) - return JS_FALSE; - lineno_length = JSSTRING_LENGTH(lineno_as_str); - } else { - lineno_as_str = NULL; - lineno_length = 0; - } - - /* Magic 8, for the characters in ``(new ())''. */ - name_length = JSSTRING_LENGTH(name); - message_length = JSSTRING_LENGTH(message); - length = 8 + name_length + message_length; - - filename_length = JSSTRING_LENGTH(filename); - if (filename_length != 0) { - /* append filename as ``, {filename}'' */ - length += 2 + filename_length; - if (lineno_as_str) { - /* append lineno as ``, {lineno_as_str}'' */ - length += 2 + lineno_length; - } - } else { - if (lineno_as_str) { - /* - * no filename, but have line number, - * need to append ``, "", {lineno_as_str}'' - */ - length += 6 + lineno_length; - } - } - - cp = chars = (jschar*) JS_malloc(cx, (length + 1) * sizeof(jschar)); - if (!chars) - return JS_FALSE; - - *cp++ = '('; *cp++ = 'n'; *cp++ = 'e'; *cp++ = 'w'; *cp++ = ' '; - js_strncpy(cp, JSSTRING_CHARS(name), name_length); - cp += name_length; - *cp++ = '('; - if (message_length != 0) { - js_strncpy(cp, JSSTRING_CHARS(message), message_length); - cp += message_length; - } - - if (filename_length != 0) { - /* append filename as ``, {filename}'' */ - *cp++ = ','; *cp++ = ' '; - js_strncpy(cp, JSSTRING_CHARS(filename), filename_length); - cp += filename_length; - } else { - if (lineno_as_str) { - /* - * no filename, but have line number, - * need to append ``, "", {lineno_as_str}'' - */ - *cp++ = ','; *cp++ = ' '; *cp++ = '"'; *cp++ = '"'; - } - } - if (lineno_as_str) { - /* append lineno as ``, {lineno_as_str}'' */ - *cp++ = ','; *cp++ = ' '; - js_strncpy(cp, JSSTRING_CHARS(lineno_as_str), lineno_length); - cp += lineno_length; - } - - *cp++ = ')'; *cp++ = ')'; *cp = 0; - - result = js_NewString(cx, chars, length, 0); - if (!result) { - JS_free(cx, chars); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(result); - return JS_TRUE; -} -#endif - -static JSFunctionSpec exception_methods[] = { -#if JS_HAS_TOSOURCE - {js_toSource_str, exn_toSource, 0,0,3}, -#endif - {js_toString_str, exn_toString, 0,0,0}, - {0,0,0,0,0} -}; - -JSObject * -js_InitExceptionClasses(JSContext *cx, JSObject *obj) -{ - JSObject *obj_proto, *protos[JSEXN_LIMIT]; - int i; - - /* - * If lazy class initialization occurs for any Error subclass, then all - * classes are initialized, starting with Error. To avoid reentry and - * redundant initialization, we must not pass a null proto parameter to - * js_NewObject below, when called for the Error superclass. We need to - * ensure that Object.prototype is the proto of Error.prototype. - * - * See the equivalent code to ensure that parent_proto is non-null when - * JS_InitClass calls js_NewObject, in jsapi.c. - */ - if (!js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Object), - &obj_proto)) { - return NULL; - } - - if (!js_EnterLocalRootScope(cx)) - return NULL; - - /* Initialize the prototypes first. */ - for (i = 0; exceptions[i].name != 0; i++) { - JSAtom *atom; - JSFunction *fun; - JSObject *funobj; - JSString *nameString; - int protoIndex = exceptions[i].protoIndex; - - /* Make the prototype for the current constructor name. */ - protos[i] = js_NewObject(cx, &js_ErrorClass, - (protoIndex != JSEXN_NONE) - ? protos[protoIndex] - : obj_proto, - obj); - if (!protos[i]) - break; - - /* So exn_finalize knows whether to destroy private data. */ - OBJ_SET_SLOT(cx, protos[i], JSSLOT_PRIVATE, JSVAL_VOID); - - /* Make a constructor function for the current name. */ - atom = cx->runtime->atomState.classAtoms[exceptions[i].key]; - fun = js_DefineFunction(cx, obj, atom, exceptions[i].native, 3, 0); - if (!fun) - break; - - /* Make this constructor make objects of class Exception. */ - fun->clasp = &js_ErrorClass; - - /* Extract the constructor object. */ - funobj = fun->object; - - /* Make the prototype and constructor links. */ - if (!js_SetClassPrototype(cx, funobj, protos[i], - JSPROP_READONLY | JSPROP_PERMANENT)) { - break; - } - - /* proto bootstrap bit from JS_InitClass omitted. */ - nameString = JS_NewStringCopyZ(cx, exceptions[i].name); - if (!nameString) - break; - - /* Add the name property to the prototype. */ - if (!JS_DefineProperty(cx, protos[i], js_name_str, - STRING_TO_JSVAL(nameString), - NULL, NULL, - JSPROP_ENUMERATE)) { - break; - } - - /* Finally, stash the constructor for later uses. */ - if (!js_SetClassObject(cx, obj, exceptions[i].key, funobj)) - break; - } - - js_LeaveLocalRootScope(cx); - if (exceptions[i].name) - return NULL; - - /* - * Add an empty message property. (To Exception.prototype only, - * because this property will be the same for all the exception - * protos.) - */ - if (!JS_DefineProperty(cx, protos[0], js_message_str, - STRING_TO_JSVAL(cx->runtime->emptyString), - NULL, NULL, JSPROP_ENUMERATE)) { - return NULL; - } - if (!JS_DefineProperty(cx, protos[0], js_fileName_str, - STRING_TO_JSVAL(cx->runtime->emptyString), - NULL, NULL, JSPROP_ENUMERATE)) { - return NULL; - } - if (!JS_DefineProperty(cx, protos[0], js_lineNumber_str, - INT_TO_JSVAL(0), - NULL, NULL, JSPROP_ENUMERATE)) { - return NULL; - } - - /* - * Add methods only to Exception.prototype, because ostensibly all - * exception types delegate to that. - */ - if (!JS_DefineFunctions(cx, protos[0], exception_methods)) - return NULL; - - return protos[0]; -} - -const JSErrorFormatString* -js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale, const uintN errorNumber) -{ - const JSErrorFormatString *errorString = NULL; - - if (cx->localeCallbacks && cx->localeCallbacks->localeGetErrorMessage) { - errorString = cx->localeCallbacks - ->localeGetErrorMessage(userRef, locale, errorNumber); - } - if (!errorString) - errorString = js_GetErrorMessage(userRef, locale, errorNumber); - return errorString; -} - -#if defined ( DEBUG_mccabe ) && defined ( PRINTNAMES ) -/* For use below... get character strings for error name and exception name */ -static struct exnname { char *name; char *exception; } errortoexnname[] = { -#define MSG_DEF(name, number, count, exception, format) \ - {#name, #exception}, -#include "js.msg" -#undef MSG_DEF -}; -#endif /* DEBUG */ - -JSBool -js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp) -{ - JSErrNum errorNumber; - const JSErrorFormatString *errorString; - JSExnType exn; - jsval tv[4]; - JSTempValueRooter tvr; - JSBool ok; - JSObject *errProto, *errObject; - JSString *messageStr, *filenameStr; - - /* - * Tell our caller to report immediately if cx has no active frames, or if - * this report is just a warning. - */ - JS_ASSERT(reportp); - if (!cx->fp || JSREPORT_IS_WARNING(reportp->flags)) - return JS_FALSE; - - /* Find the exception index associated with this error. */ - errorNumber = (JSErrNum) reportp->errorNumber; - errorString = js_GetLocalizedErrorMessage(cx, NULL, NULL, errorNumber); - exn = errorString ? errorString->exnType : JSEXN_NONE; - JS_ASSERT(exn < JSEXN_LIMIT); - -#if defined( DEBUG_mccabe ) && defined ( PRINTNAMES ) - /* Print the error name and the associated exception name to stderr */ - fprintf(stderr, "%s\t%s\n", - errortoexnname[errorNumber].name, - errortoexnname[errorNumber].exception); -#endif - - /* - * Return false (no exception raised) if no exception is associated - * with the given error number. - */ - if (exn == JSEXN_NONE) - return JS_FALSE; - - /* - * Prevent runaway recursion, just as the Exception native constructor - * must do, via cx->creatingException. If an out-of-memory error occurs, - * no exception object will be created, but we don't assume that OOM is - * the only kind of error that subroutines of this function called below - * might raise. - */ - if (cx->creatingException) - return JS_FALSE; - - /* After this point the control must flow through the label out. */ - cx->creatingException = JS_TRUE; - - /* Protect the newly-created strings below from nesting GCs. */ - memset(tv, 0, sizeof tv); - JS_PUSH_TEMP_ROOT(cx, sizeof tv / sizeof tv[0], tv, &tvr); - - /* - * Try to get an appropriate prototype by looking up the corresponding - * exception constructor name in the scope chain of the current context's - * top stack frame, or in the global object if no frame is active. - */ - ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(exceptions[exn].key), - &errProto); - if (!ok) - goto out; - tv[0] = OBJECT_TO_JSVAL(errProto); - - errObject = js_NewObject(cx, &js_ErrorClass, errProto, NULL); - if (!errObject) { - ok = JS_FALSE; - goto out; - } - tv[1] = OBJECT_TO_JSVAL(errObject); - - messageStr = JS_NewStringCopyZ(cx, message); - if (!messageStr) { - ok = JS_FALSE; - goto out; - } - tv[2] = STRING_TO_JSVAL(messageStr); - - filenameStr = JS_NewStringCopyZ(cx, reportp->filename); - if (!filenameStr) { - ok = JS_FALSE; - goto out; - } - tv[3] = STRING_TO_JSVAL(filenameStr); - - ok = InitExnPrivate(cx, errObject, messageStr, filenameStr, - reportp->lineno, reportp); - if (!ok) - goto out; - - JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject)); - - /* Flag the error report passed in to indicate an exception was raised. */ - reportp->flags |= JSREPORT_EXCEPTION; - -out: - JS_POP_TEMP_ROOT(cx, &tvr); - cx->creatingException = JS_FALSE; - return ok; -} - -JSBool -js_ReportUncaughtException(JSContext *cx) -{ - jsval exn; - JSObject *exnObject; - jsval vp[5]; - JSTempValueRooter tvr; - JSErrorReport *reportp, report; - JSString *str; - const char *bytes; - JSBool ok; - - if (!JS_IsExceptionPending(cx)) - return JS_TRUE; - - if (!JS_GetPendingException(cx, &exn)) - return JS_FALSE; - - /* - * Because js_ValueToString below could error and an exception object - * could become unrooted, we must root exnObject. Later, if exnObject is - * non-null, we need to root other intermediates, so allocate an operand - * stack segment to protect all of these values. - */ - if (JSVAL_IS_PRIMITIVE(exn)) { - exnObject = NULL; - } else { - exnObject = JSVAL_TO_OBJECT(exn); - vp[0] = exn; - memset(vp + 1, 0, sizeof vp - sizeof vp[0]); - JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(vp), vp, &tvr); - } - - JS_ClearPendingException(cx); - reportp = js_ErrorFromException(cx, exn); - - /* XXX L10N angels cry once again (see also jsemit.c, /L10N gaffes/) */ - str = js_ValueToString(cx, exn); - if (!str) { - bytes = "unknown (can't convert to string)"; - } else { - if (exnObject) - vp[1] = STRING_TO_JSVAL(str); - bytes = js_GetStringBytes(cx->runtime, str); - } - ok = JS_TRUE; - - if (!reportp && - exnObject && - OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass) { - const char *filename; - uint32 lineno; - - ok = JS_GetProperty(cx, exnObject, js_message_str, &vp[2]); - if (!ok) - goto out; - if (JSVAL_IS_STRING(vp[2])) - bytes = JS_GetStringBytes(JSVAL_TO_STRING(vp[2])); - - ok = JS_GetProperty(cx, exnObject, js_fileName_str, &vp[3]); - if (!ok) - goto out; - str = js_ValueToString(cx, vp[3]); - if (!str) { - ok = JS_FALSE; - goto out; - } - filename = StringToFilename(cx, str); - - ok = JS_GetProperty(cx, exnObject, js_lineNumber_str, &vp[4]); - if (!ok) - goto out; - ok = js_ValueToECMAUint32 (cx, vp[4], &lineno); - if (!ok) - goto out; - - reportp = &report; - memset(&report, 0, sizeof report); - report.filename = filename; - report.lineno = (uintN) lineno; - } - - if (!reportp) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_UNCAUGHT_EXCEPTION, bytes); - } else { - /* Flag the error as an exception. */ - reportp->flags |= JSREPORT_EXCEPTION; - js_ReportErrorAgain(cx, bytes, reportp); - } - -out: - if (exnObject) - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; -} diff --git a/spidermonkey/src/jsexn.h b/spidermonkey/src/jsexn.h deleted file mode 100644 index 58cb984..0000000 --- a/spidermonkey/src/jsexn.h +++ /dev/null @@ -1,96 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS runtime exception classes. - */ - -#ifndef jsexn_h___ -#define jsexn_h___ - -JS_BEGIN_EXTERN_C - -extern JSClass js_ErrorClass; - -/* - * Initialize the exception constructor/prototype hierarchy. - */ -extern JSObject * -js_InitExceptionClasses(JSContext *cx, JSObject *obj); - -/* - * Given a JSErrorReport, check to see if there is an exception associated with - * the error number. If there is, then create an appropriate exception object, - * set it as the pending exception, and set the JSREPORT_EXCEPTION flag on the - * error report. Exception-aware host error reporters should probably ignore - * error reports so flagged. Returns JS_TRUE if an associated exception is - * found and set, JS_FALSE otherwise.. - */ -extern JSBool -js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp); - -/* - * Called if a JS API call to js_Execute or js_InternalCall fails; calls the - * error reporter with the error report associated with any uncaught exception - * that has been raised. Returns true if there was an exception pending, and - * the error reporter was actually called. - * - * The JSErrorReport * that the error reporter is called with is currently - * associated with a JavaScript object, and is not guaranteed to persist after - * the object is collected. Any persistent uses of the JSErrorReport contents - * should make their own copy. - * - * The flags field of the JSErrorReport will have the JSREPORT_EXCEPTION flag - * set; embeddings that want to silently propagate JavaScript exceptions to - * other contexts may want to use an error reporter that ignores errors with - * this flag. - */ -extern JSBool -js_ReportUncaughtException(JSContext *cx); - -extern JSErrorReport * -js_ErrorFromException(JSContext *cx, jsval exn); - -extern const JSErrorFormatString * -js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale, - const uintN errorNumber); - -JS_END_EXTERN_C - -#endif /* jsexn_h___ */ diff --git a/spidermonkey/src/jsfile.c b/spidermonkey/src/jsfile.c deleted file mode 100644 index ed1c4e8..0000000 --- a/spidermonkey/src/jsfile.c +++ /dev/null @@ -1,2735 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=80: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS File object - */ -#if JS_HAS_FILE_OBJECT - -#include "jsstddef.h" -#include "jsfile.h" - -/* ----------------- Platform-specific includes and defines ----------------- */ -#if defined(XP_WIN) || defined(XP_OS2) -# include -# include -# include -# include -# define FILESEPARATOR '\\' -# define FILESEPARATOR2 '/' -# define CURRENT_DIR "c:\\" -# define POPEN _popen -# define PCLOSE _pclose -#elif defined(XP_UNIX) || defined(XP_BEOS) -# include -# include -# include -# include -# define FILESEPARATOR '/' -# define FILESEPARATOR2 '\0' -# define CURRENT_DIR "/" -# define POPEN popen -# define PCLOSE pclose -#endif - -/* --------------- Platform-independent includes and defines ---------------- */ -#include "jsapi.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsdate.h" -#include "jsdbgapi.h" -#include "jsemit.h" -#include "jsfun.h" -#include "jslock.h" -#include "jsobj.h" -#include "jsparse.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" -#include "jsutil.h" /* Added by JSIFY */ -#include - -/* NSPR dependencies */ -#include "prio.h" -#include "prerror.h" - -#define SPECIAL_FILE_STRING "Special File" -#define CURRENTDIR_PROPERTY "currentDir" -#define SEPARATOR_PROPERTY "separator" -#define FILE_CONSTRUCTOR "File" -#define PIPE_SYMBOL '|' - -#define ASCII 0 -#define UTF8 1 -#define UCS2 2 - -#define asciistring "text" -#define utfstring "binary" -#define unicodestring "unicode" - -#define MAX_PATH_LENGTH 1024 -#define MODE_SIZE 256 -#define NUMBER_SIZE 32 -#define MAX_LINE_LENGTH 256 -#define URL_PREFIX "file://" - -#define STDINPUT_NAME "Standard input stream" -#define STDOUTPUT_NAME "Standard output stream" -#define STDERROR_NAME "Standard error stream" - -#define RESOLVE_PATH js_canonicalPath /* js_absolutePath */ - -/* Error handling */ -typedef enum JSFileErrNum { -#define MSG_DEF(name, number, count, exception, format) \ - name = number, -#include "jsfile.msg" -#undef MSG_DEF - JSFileErr_Limit -#undef MSGDEF -} JSFileErrNum; - -#define JSFILE_HAS_DFLT_MSG_STRINGS 1 - -JSErrorFormatString JSFile_ErrorFormatString[JSFileErr_Limit] = { -#if JSFILE_HAS_DFLT_MSG_STRINGS -#define MSG_DEF(name, number, count, exception, format) \ - { format, count }, -#else -#define MSG_DEF(name, number, count, exception, format) \ - { NULL, count }, -#endif -#include "jsfile.msg" -#undef MSG_DEF -}; - -const JSErrorFormatString * -JSFile_GetErrorMessage(void *userRef, const char *locale, - const uintN errorNumber) -{ - if ((errorNumber > 0) && (errorNumber < JSFileErr_Limit)) - return &JSFile_ErrorFormatString[errorNumber]; - else - return NULL; -} - -#define JSFILE_CHECK_NATIVE(op) \ - if (file->isNative) { \ - JS_ReportWarning(cx, "Cannot call or access \"%s\" on native file %s",\ - op, file->path); \ - goto out; \ - } - -#define JSFILE_CHECK_WRITE \ - if (!file->isOpen) { \ - JS_ReportWarning(cx, \ - "File %s is closed, will open it for writing, proceeding", \ - file->path); \ - js_FileOpen(cx, obj, file, "write,append,create"); \ - } \ - if (!js_canWrite(cx, file)) { \ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ - JSFILEMSG_CANNOT_WRITE, file->path); \ - goto out; \ - } - -#define JSFILE_CHECK_READ \ - if (!file->isOpen) { \ - JS_ReportWarning(cx, \ - "File %s is closed, will open it for reading, proceeding", \ - file->path); \ - js_FileOpen(cx, obj, file, "read"); \ - } \ - if (!js_canRead(cx, file)) { \ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ - JSFILEMSG_CANNOT_READ, file->path); \ - goto out; \ - } - -#define JSFILE_CHECK_OPEN(op) \ - if (!file->isOpen) { \ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ - JSFILEMSG_FILE_MUST_BE_CLOSED, op); \ - goto out; \ - } - -#define JSFILE_CHECK_CLOSED(op) \ - if (file->isOpen) { \ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ - JSFILEMSG_FILE_MUST_BE_OPEN, op); \ - goto out; \ - } - -#define JSFILE_CHECK_ONE_ARG(op) \ - if (argc != 1) { \ - char str[NUMBER_SIZE]; \ - sprintf(str, "%d", argc); \ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ - JSFILEMSG_EXPECTS_ONE_ARG_ERROR, op, str); \ - goto out; \ - } - - -/* - Security mechanism, should define a callback for this. - The parameters are as follows: - SECURITY_CHECK(JSContext *cx, JSPrincipals *ps, char *op_name, JSFile *file) - XXX Should this be a real function returning a JSBool result (and getting - some typesafety help from the compiler?). -*/ -#define SECURITY_CHECK(cx, ps, op, file) \ - /* Define a callback here... */ - - -/* Structure representing the file internally */ -typedef struct JSFile { - char *path; /* the path to the file. */ - JSBool isOpen; - int32 mode; /* mode used to open the file: read, write, append, create, etc.. */ - int32 type; /* Asciiz, utf, unicode */ - char byteBuffer[3]; /* bytes read in advance by js_FileRead ( UTF8 encoding ) */ - jsint nbBytesInBuf; /* number of bytes stored in the buffer above */ - jschar charBuffer; /* character read in advance by readln ( mac files only ) */ - JSBool charBufferUsed; /* flag indicating if the buffer above is being used */ - JSBool hasRandomAccess;/* can the file be randomly accessed? false for stdin, and - UTF-encoded files. */ - JSBool hasAutoflush; /* should we force a flush for each line break? */ - JSBool isNative; /* if the file is using OS-specific file FILE type */ - /* We can actually put the following two in a union since they should never be used at the same time */ - PRFileDesc *handle; /* the handle for the file, if open. */ - FILE *nativehandle; /* native handle, for stuff NSPR doesn't do. */ - JSBool isPipe; /* if the file is really an OS pipe */ -} JSFile; - -/* a few forward declarations... */ -JS_PUBLIC_API(JSObject*) js_NewFileObject(JSContext *cx, char *filename); -static JSBool file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); -static JSBool file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); - -/* New filename manipulation procesures */ -/* assumes we don't have leading/trailing spaces */ -static JSBool -js_filenameHasAPipe(const char *filename) -{ - if (!filename) - return JS_FALSE; - - return filename[0] == PIPE_SYMBOL || - filename[strlen(filename) - 1] == PIPE_SYMBOL; -} - -static JSBool -js_isAbsolute(const char *name) -{ -#if defined(XP_WIN) || defined(XP_OS2) - return *name && name[1] == ':'; -#else - return (name[0] -# if defined(XP_UNIX) || defined(XP_BEOS) - == -# else - != -# endif - FILESEPARATOR); -#endif -} - -/* - * Concatinates base and name to produce a valid filename. - * Returned string must be freed. -*/ -static char* -js_combinePath(JSContext *cx, const char *base, const char *name) -{ - int len = strlen(base); - char* result = JS_malloc(cx, len + strlen(name) + 2); - - if (!result) - return NULL; - - strcpy(result, base); - - if (base[len - 1] != FILESEPARATOR && base[len - 1] != FILESEPARATOR2) { - result[len] = FILESEPARATOR; - result[len + 1] = '\0'; - } - strcat(result, name); - return result; -} - -/* Extract the last component from a path name. Returned string must be freed */ -static char * -js_fileBaseName(JSContext *cx, const char *pathname) -{ - jsint index, aux; - char *result; - - index = strlen(pathname)-1; - - /* Chop off trailing seperators. */ - while (index > 0 && (pathname[index]==FILESEPARATOR || - pathname[index]==FILESEPARATOR2)) { - --index; - } - - aux = index; - - /* Now find the next separator. */ - while (index >= 0 && pathname[index] != FILESEPARATOR && - pathname[index] != FILESEPARATOR2) { - --index; - } - - /* Allocate and copy. */ - result = JS_malloc(cx, aux - index + 1); - if (!result) - return NULL; - strncpy(result, pathname + index + 1, aux - index); - result[aux - index] = '\0'; - return result; -} - -/* - * Returns everything but the last component from a path name. - * Returned string must be freed. - */ -static char * -js_fileDirectoryName(JSContext *cx, const char *pathname) -{ - char *result; - const char *cp, *end; - size_t pathsize; - - end = pathname + strlen(pathname); - cp = end - 1; - - /* If this is already a directory, chop off the trailing /s. */ - while (cp >= pathname) { - if (*cp != FILESEPARATOR && *cp != FILESEPARATOR2) - break; - --cp; - } - - if (cp < pathname && end != pathname) { - /* There were just /s, return the root. */ - result = JS_malloc(cx, 1 + 1); /* The separator + trailing NUL. */ - result[0] = FILESEPARATOR; - result[1] = '\0'; - return result; - } - - /* Now chop off the last portion. */ - while (cp >= pathname) { - if (*cp == FILESEPARATOR || *cp == FILESEPARATOR2) - break; - --cp; - } - - /* Check if this is a leaf. */ - if (cp < pathname) { - /* It is, return "pathname/". */ - if (end[-1] == FILESEPARATOR || end[-1] == FILESEPARATOR2) { - /* Already has its terminating /. */ - return JS_strdup(cx, pathname); - } - - pathsize = end - pathname + 1; - result = JS_malloc(cx, pathsize + 1); - if (!result) - return NULL; - - strcpy(result, pathname); - result[pathsize - 1] = FILESEPARATOR; - result[pathsize] = '\0'; - - return result; - } - - /* Return everything up to and including the seperator. */ - pathsize = cp - pathname + 1; - result = JS_malloc(cx, pathsize + 1); - if (!result) - return NULL; - - strncpy(result, pathname, pathsize); - result[pathsize] = '\0'; - - return result; -} - -static char * -js_absolutePath(JSContext *cx, const char * path) -{ - JSObject *obj; - JSString *str; - jsval prop; - - if (js_isAbsolute(path)) { - return JS_strdup(cx, path); - } else { - obj = JS_GetGlobalObject(cx); - if (!JS_GetProperty(cx, obj, FILE_CONSTRUCTOR, &prop)) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FILE_CONSTRUCTOR_UNDEFINED_ERROR); - return JS_strdup(cx, path); - } - - obj = JSVAL_TO_OBJECT(prop); - if (!JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, &prop)) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FILE_CURRENTDIR_UNDEFINED_ERROR); - return JS_strdup(cx, path); - } - - str = JS_ValueToString(cx, prop); - if (!str) - return JS_strdup(cx, path); - - /* should we have an array of curr dirs indexed by drive for windows? */ - return js_combinePath(cx, JS_GetStringBytes(str), path); - } -} - -/* Side effect: will remove spaces in the beginning/end of the filename */ -static char * -js_canonicalPath(JSContext *cx, char *oldpath) -{ - char *tmp; - char *path = oldpath; - char *base, *dir, *current, *result; - jsint c; - jsint back = 0; - unsigned int i = 0, j = strlen(path)-1; - - /* This is probably optional */ - /* Remove possible spaces in the beginning and end */ - while (i < j && path[i] == ' ') - i++; - while (j >= 0 && path[j] == ' ') - j--; - - tmp = JS_malloc(cx, j-i+2); - if (!tmp) - return NULL; - - strncpy(tmp, path + i, j - i + 1); - tmp[j - i + 1] = '\0'; - - path = tmp; - - /* Pipe support. */ - if (js_filenameHasAPipe(path)) - return path; - - /* file:// support. */ - if (!strncmp(path, URL_PREFIX, strlen(URL_PREFIX))) { - tmp = js_canonicalPath(cx, path + strlen(URL_PREFIX)); - JS_free(cx, path); - return tmp; - } - - if (!js_isAbsolute(path)) { - tmp = js_absolutePath(cx, path); - if (!tmp) - return NULL; - path = tmp; - } - - result = JS_strdup(cx, ""); - - current = path; - - base = js_fileBaseName(cx, current); - dir = js_fileDirectoryName(cx, current); - - while (strcmp(dir, current)) { - if (!strcmp(base, "..")) { - back++; - } else { - if (back > 0) { - back--; - } else { - tmp = result; - result = JS_malloc(cx, strlen(base) + 1 + strlen(tmp) + 1); - if (!result) - goto out; - - strcpy(result, base); - c = strlen(result); - if (*tmp) { - result[c] = FILESEPARATOR; - result[c + 1] = '\0'; - strcat(result, tmp); - } - JS_free(cx, tmp); - } - } - JS_free(cx, current); - JS_free(cx, base); - current = dir; - base = js_fileBaseName(cx, current); - dir = js_fileDirectoryName(cx, current); - } - - tmp = result; - result = JS_malloc(cx, strlen(dir)+1+strlen(tmp)+1); - if (!result) - goto out; - - strcpy(result, dir); - c = strlen(result); - if (tmp[0]!='\0') { - if ((result[c-1]!=FILESEPARATOR)&&(result[c-1]!=FILESEPARATOR2)) { - result[c] = FILESEPARATOR; - result[c+1] = '\0'; - } - strcat(result, tmp); - } - -out: - if (tmp) - JS_free(cx, tmp); - if (dir) - JS_free(cx, dir); - if (base) - JS_free(cx, base); - if (current) - JS_free(cx, current); - - return result; -} - -/* -------------------------- Text conversion ------------------------------- */ -/* The following is ripped from libi18n/unicvt.c and include files.. */ - -/* - * UTF8 defines and macros - */ -#define ONE_OCTET_BASE 0x00 /* 0xxxxxxx */ -#define ONE_OCTET_MASK 0x7F /* x1111111 */ -#define CONTINUING_OCTET_BASE 0x80 /* 10xxxxxx */ -#define CONTINUING_OCTET_MASK 0x3F /* 00111111 */ -#define TWO_OCTET_BASE 0xC0 /* 110xxxxx */ -#define TWO_OCTET_MASK 0x1F /* 00011111 */ -#define THREE_OCTET_BASE 0xE0 /* 1110xxxx */ -#define THREE_OCTET_MASK 0x0F /* 00001111 */ -#define FOUR_OCTET_BASE 0xF0 /* 11110xxx */ -#define FOUR_OCTET_MASK 0x07 /* 00000111 */ -#define FIVE_OCTET_BASE 0xF8 /* 111110xx */ -#define FIVE_OCTET_MASK 0x03 /* 00000011 */ -#define SIX_OCTET_BASE 0xFC /* 1111110x */ -#define SIX_OCTET_MASK 0x01 /* 00000001 */ - -#define IS_UTF8_1ST_OF_1(x) (( (x)&~ONE_OCTET_MASK ) == ONE_OCTET_BASE) -#define IS_UTF8_1ST_OF_2(x) (( (x)&~TWO_OCTET_MASK ) == TWO_OCTET_BASE) -#define IS_UTF8_1ST_OF_3(x) (( (x)&~THREE_OCTET_MASK) == THREE_OCTET_BASE) -#define IS_UTF8_1ST_OF_4(x) (( (x)&~FOUR_OCTET_MASK ) == FOUR_OCTET_BASE) -#define IS_UTF8_1ST_OF_5(x) (( (x)&~FIVE_OCTET_MASK ) == FIVE_OCTET_BASE) -#define IS_UTF8_1ST_OF_6(x) (( (x)&~SIX_OCTET_MASK ) == SIX_OCTET_BASE) -#define IS_UTF8_2ND_THRU_6TH(x) \ - (( (x)&~CONTINUING_OCTET_MASK ) == CONTINUING_OCTET_BASE) -#define IS_UTF8_1ST_OF_UCS2(x) \ - IS_UTF8_1ST_OF_1(x) \ - || IS_UTF8_1ST_OF_2(x) \ - || IS_UTF8_1ST_OF_3(x) - - -#define MAX_UCS2 0xFFFF -#define DEFAULT_CHAR 0x003F /* Default char is "?" */ -#define BYTE_MASK 0xBF -#define BYTE_MARK 0x80 - - -/* Function: one_ucs2_to_utf8_char - * - * Function takes one UCS-2 char and writes it to a UTF-8 buffer. - * We need a UTF-8 buffer because we don't know before this - * function how many bytes of utf-8 data will be written. It also - * takes a pointer to the end of the UTF-8 buffer so that we don't - * overwrite data. This function returns the number of UTF-8 bytes - * of data written, or -1 if the buffer would have been overrun. - */ - -#define LINE_SEPARATOR 0x2028 -#define PARAGRAPH_SEPARATOR 0x2029 -static int16 one_ucs2_to_utf8_char(unsigned char *tobufp, - unsigned char *tobufendp, - uint16 onechar) -{ - int16 numUTF8bytes = 0; - - if (onechar == LINE_SEPARATOR || onechar == PARAGRAPH_SEPARATOR) { - strcpy((char*)tobufp, "\n"); - return strlen((char*)tobufp); - } - - if (onechar < 0x80) { - numUTF8bytes = 1; - } else if (onechar < 0x800) { - numUTF8bytes = 2; - } else { - /* 0x800 >= onechar <= MAX_UCS2 */ - numUTF8bytes = 3; - } - - tobufp += numUTF8bytes; - - /* return error if we don't have space for the whole character */ - if (tobufp > tobufendp) { - return(-1); - } - - switch(numUTF8bytes) { - case 3: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6; - *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6; - *--tobufp = onechar | THREE_OCTET_BASE; - break; - - case 2: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6; - *--tobufp = onechar | TWO_OCTET_BASE; - break; - - case 1: *--tobufp = (unsigned char)onechar; - break; - } - - return numUTF8bytes; -} - -/* - * utf8_to_ucs2_char - * - * Convert a utf8 multibyte character to ucs2 - * - * inputs: pointer to utf8 character(s) - * length of utf8 buffer ("read" length limit) - * pointer to return ucs2 character - * - * outputs: number of bytes in the utf8 character - * -1 if not a valid utf8 character sequence - * -2 if the buffer is too short - */ -static int16 -utf8_to_ucs2_char(const unsigned char *utf8p, int16 buflen, uint16 *ucs2p) -{ - uint16 lead, cont1, cont2; - - /* - * Check for minimum buffer length - */ - if ((buflen < 1) || (utf8p == NULL)) { - return -2; - } - lead = (uint16) (*utf8p); - - /* - * Check for a one octet sequence - */ - if (IS_UTF8_1ST_OF_1(lead)) { - *ucs2p = lead & ONE_OCTET_MASK; - return 1; - } - - /* - * Check for a two octet sequence - */ - if (IS_UTF8_1ST_OF_2(*utf8p)) { - if (buflen < 2) - return -2; - cont1 = (uint16) *(utf8p+1); - if (!IS_UTF8_2ND_THRU_6TH(cont1)) - return -1; - *ucs2p = (lead & TWO_OCTET_MASK) << 6; - *ucs2p |= cont1 & CONTINUING_OCTET_MASK; - return 2; - } - - /* - * Check for a three octet sequence - */ - else if (IS_UTF8_1ST_OF_3(lead)) { - if (buflen < 3) - return -2; - cont1 = (uint16) *(utf8p+1); - cont2 = (uint16) *(utf8p+2); - if ( (!IS_UTF8_2ND_THRU_6TH(cont1)) - || (!IS_UTF8_2ND_THRU_6TH(cont2))) - return -1; - *ucs2p = (lead & THREE_OCTET_MASK) << 12; - *ucs2p |= (cont1 & CONTINUING_OCTET_MASK) << 6; - *ucs2p |= cont2 & CONTINUING_OCTET_MASK; - return 3; - } - else { /* not a valid utf8/ucs2 character */ - return -1; - } -} - -/* ----------------------------- Helper functions --------------------------- */ -/* Ripped off from lm_win.c .. */ -/* where is strcasecmp?.. for now, it's case sensitive.. - * - * strcasecmp is in strings.h, but on windows it's called _stricmp... - * will need to #ifdef this -*/ - -static int32 -js_FileHasOption(JSContext *cx, const char *oldoptions, const char *name) -{ - char *comma, *equal, *current; - char *options = JS_strdup(cx, oldoptions); - int32 found = 0; - - current = options; - for (;;) { - comma = strchr(current, ','); - if (comma) *comma = '\0'; - equal = strchr(current, '='); - if (equal) *equal = '\0'; - if (strcmp(current, name) == 0) { - if (!equal || strcmp(equal + 1, "yes") == 0) - found = 1; - else - found = atoi(equal + 1); - } - if (equal) *equal = '='; - if (comma) *comma = ','; - if (found || !comma) - break; - current = comma + 1; - } - JS_free(cx, options); - return found; -} - -/* empty the buffer */ -static void -js_ResetBuffers(JSFile * file) -{ - file->charBufferUsed = JS_FALSE; - file->nbBytesInBuf = 0; -} - -/* Reset file attributes */ -static void -js_ResetAttributes(JSFile * file) -{ - file->mode = file->type = 0; - file->isOpen = JS_FALSE; - file->handle = NULL; - file->nativehandle = NULL; - file->hasRandomAccess = JS_TRUE; /* Innocent until proven guilty. */ - file->hasAutoflush = JS_FALSE; - file->isNative = JS_FALSE; - file->isPipe = JS_FALSE; - - js_ResetBuffers(file); -} - -static JSBool -js_FileOpen(JSContext *cx, JSObject *obj, JSFile *file, char *mode){ - JSString *type, *mask; - jsval v[2]; - jsval rval; - - type = JS_InternString(cx, asciistring); - mask = JS_NewStringCopyZ(cx, mode); - v[0] = STRING_TO_JSVAL(mask); - v[1] = STRING_TO_JSVAL(type); - - if (!file_open(cx, obj, 2, v, &rval)) - return JS_FALSE; - return JS_TRUE; -} - -/* Buffered version of PR_Read. Used by js_FileRead */ -static int32 -js_BufferedRead(JSFile *f, unsigned char *buf, int32 len) -{ - int32 count = 0; - - while (f->nbBytesInBuf>0&&len>0) { - buf[0] = f->byteBuffer[0]; - f->byteBuffer[0] = f->byteBuffer[1]; - f->byteBuffer[1] = f->byteBuffer[2]; - f->nbBytesInBuf--; - len--; - buf+=1; - count++; - } - - if (len > 0) { - count += (!f->isNative) - ? PR_Read(f->handle, buf, len) - : fread(buf, 1, len, f->nativehandle); - } - return count; -} - -static int32 -js_FileRead(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode) -{ - unsigned char *aux; - int32 count = 0, i; - jsint remainder; - unsigned char utfbuf[3]; - - if (file->charBufferUsed) { - buf[0] = file->charBuffer; - buf++; - len--; - file->charBufferUsed = JS_FALSE; - } - - switch (mode) { - case ASCII: - aux = (unsigned char*)JS_malloc(cx, len); - if (!aux) - return 0; - - count = js_BufferedRead(file, aux, len); - if (count == -1) { - JS_free(cx, aux); - return 0; - } - - for (i = 0; i < len; i++) - buf[i] = (jschar)aux[i]; - - JS_free(cx, aux); - break; - - case UTF8: - remainder = 0; - for (count = 0;count0) { - file->byteBuffer[file->nbBytesInBuf] = utfbuf[0]; - file->nbBytesInBuf++; - utfbuf[0] = utfbuf[1]; - utfbuf[1] = utfbuf[2]; - remainder--; - } - break; - - case UCS2: - count = js_BufferedRead(file, (unsigned char *)buf, len * 2) >> 1; - if (count == -1) - return 0; - - break; - - default: - /* Not reached. */ - JS_ASSERT(0); - } - - if(count == -1) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "read", file->path); - } - - return count; -} - -static int32 -js_FileSeek(JSContext *cx, JSFile *file, int32 len, int32 mode) -{ - int32 count = 0, i; - jsint remainder; - unsigned char utfbuf[3]; - jschar tmp; - - switch (mode) { - case ASCII: - count = PR_Seek(file->handle, len, PR_SEEK_CUR); - break; - - case UTF8: - remainder = 0; - for (count = 0;count0) { - file->byteBuffer[file->nbBytesInBuf] = utfbuf[0]; - file->nbBytesInBuf++; - utfbuf[0] = utfbuf[1]; - utfbuf[1] = utfbuf[2]; - remainder--; - } - break; - - case UCS2: - count = PR_Seek(file->handle, len*2, PR_SEEK_CUR)/2; - break; - - default: - /* Not reached. */ - JS_ASSERT(0); - } - - if(count == -1) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "seek", file->path); - } - - return count; -} - -static int32 -js_FileWrite(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode) -{ - unsigned char *aux; - int32 count = 0, i, j; - unsigned char *utfbuf; - - switch (mode) { - case ASCII: - aux = (unsigned char*)JS_malloc(cx, len); - if (!aux) - return 0; - - for (i = 0; iisNative) - ? PR_Write(file->handle, aux, len) - : fwrite(aux, 1, len, file->nativehandle); - - if (count==-1) { - JS_free(cx, aux); - return 0; - } - - JS_free(cx, aux); - break; - - case UTF8: - utfbuf = (unsigned char*)JS_malloc(cx, len*3); - if (!utfbuf) return 0; - i = 0; - for (count = 0;countisNative) - ? PR_Write(file->handle, utfbuf, i) - : fwrite(utfbuf, 1, i, file->nativehandle); - - if (jisNative) - ? PR_Write(file->handle, buf, len*2) >> 1 - : fwrite(buf, 1, len*2, file->nativehandle) >> 1; - - if (count == -1) - return 0; - break; - - default: - /* Not reached. */ - JS_ASSERT(0); - } - - if(count == -1) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "write", file->path); - } - - return count; -} - -/* ----------------------------- Property checkers -------------------------- */ -static JSBool -js_exists(JSContext *cx, JSFile *file) -{ - if (file->isNative) { - /* It doesn't make sense for a pipe of stdstream. */ - return JS_FALSE; - } - - return PR_Access(file->path, PR_ACCESS_EXISTS) == PR_SUCCESS; -} - -static JSBool -js_canRead(JSContext *cx, JSFile *file) -{ - if (!file->isNative) { - if (file->isOpen && !(file->mode & PR_RDONLY)) - return JS_FALSE; - return PR_Access(file->path, PR_ACCESS_READ_OK) == PR_SUCCESS; - } - - if (file->isPipe) { - /* Is this pipe open for reading? */ - return file->path[0] == PIPE_SYMBOL; - } - - return !strcmp(file->path, STDINPUT_NAME); -} - -static JSBool -js_canWrite(JSContext *cx, JSFile *file) -{ - if (!file->isNative) { - if (file->isOpen && !(file->mode & PR_WRONLY)) - return JS_FALSE; - return PR_Access(file->path, PR_ACCESS_WRITE_OK) == PR_SUCCESS; - } - - if(file->isPipe) { - /* Is this pipe open for writing? */ - return file->path[strlen(file->path)-1] == PIPE_SYMBOL; - } - - return !strcmp(file->path, STDOUTPUT_NAME) || - !strcmp(file->path, STDERROR_NAME); -} - -static JSBool -js_isFile(JSContext *cx, JSFile *file) -{ - if (!file->isNative) { - PRFileInfo info; - - if (file->isOpen - ? PR_GetOpenFileInfo(file->handle, &info) - : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); - return JS_FALSE; - } - - return info.type == PR_FILE_FILE; - } - - /* This doesn't make sense for a pipe of stdstream. */ - return JS_FALSE; -} - -static JSBool -js_isDirectory(JSContext *cx, JSFile *file) -{ - if(!file->isNative){ - PRFileInfo info; - - /* Hack needed to get get_property to work. */ - if (!js_exists(cx, file)) - return JS_FALSE; - - if (file->isOpen - ? PR_GetOpenFileInfo(file->handle, &info) - : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); - return JS_FALSE; - } - - return info.type == PR_FILE_DIRECTORY; - } - - /* This doesn't make sense for a pipe of stdstream. */ - return JS_FALSE; -} - -static jsval -js_size(JSContext *cx, JSFile *file) -{ - PRFileInfo info; - - JSFILE_CHECK_NATIVE("size"); - - if (file->isOpen - ? PR_GetOpenFileInfo(file->handle, &info) - : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); - return JSVAL_VOID; - } - - return INT_TO_JSVAL(info.size); - -out: - return JSVAL_VOID; -} - -/* - * Return the parent object - */ -static JSBool -js_parent(JSContext *cx, JSFile *file, jsval *resultp) -{ - char *str; - - /* Since we only care about pipes and native files, return NULL. */ - if (file->isNative) { - *resultp = JSVAL_VOID; - return JS_TRUE; - } - - str = js_fileDirectoryName(cx, file->path); - if (!str) - return JS_FALSE; - - /* If the directory is equal to the original path, we're at the root. */ - if (!strcmp(file->path, str)) { - *resultp = JSVAL_NULL; - } else { - JSObject *obj = js_NewFileObject(cx, str); - if (!obj) { - JS_free(cx, str); - return JS_FALSE; - } - *resultp = OBJECT_TO_JSVAL(obj); - } - - JS_free(cx, str); - return JS_TRUE; -} - -static JSBool -js_name(JSContext *cx, JSFile *file, jsval *vp) -{ - char *name; - JSString *str; - - if (file->isPipe) { - *vp = JSVAL_VOID; - return JS_TRUE; - } - - name = js_fileBaseName(cx, file->path); - if (!name) - return JS_FALSE; - - str = JS_NewString(cx, name, strlen(name)); - if (!str) { - JS_free(cx, name); - return JS_FALSE; - } - - *vp = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -/* ------------------------------ File object methods ---------------------------- */ -static JSBool -file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - JSString *strmode, *strtype; - char *ctype, *mode; - int32 mask, type; - int len; - - mode = NULL; - - SECURITY_CHECK(cx, NULL, "open", file); - - /* A native file that is already open */ - if(file->isOpen && file->isNative) { - JS_ReportWarning(cx, "Native file %s is already open, proceeding", - file->path); - goto good; - } - - /* Close before proceeding */ - if (file->isOpen) { - JS_ReportWarning(cx, "File %s is already open, we will close it and " - "reopen, proceeding", file->path); - if(!file_close(cx, obj, 0, NULL, rval)) - goto out; - } - - if (js_isDirectory(cx, file)) { - JS_ReportWarning(cx, "%s seems to be a directory, there is no point in " - "trying to open it, proceeding", file->path); - goto good; - } - - /* Path must be defined at this point */ - len = strlen(file->path); - - /* Mode */ - if (argc >= 1) { - strmode = JS_ValueToString(cx, argv[0]); - if (!strmode) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FIRST_ARGUMENT_OPEN_NOT_STRING_ERROR, - argv[0]); - goto out; - } - mode = JS_strdup(cx, JS_GetStringBytes(strmode)); - } else { - if(file->path[0]==PIPE_SYMBOL) { - /* pipe default mode */ - mode = JS_strdup(cx, "read"); - } else if(file->path[len-1]==PIPE_SYMBOL) { - /* pipe default mode */ - mode = JS_strdup(cx, "write"); - } else { - /* non-destructive, permissive defaults. */ - mode = JS_strdup(cx, "readWrite,append,create"); - } - } - - /* Process the mode */ - mask = 0; - /* TODO: this is pretty ugly, we walk thru the string too many times */ - mask |= js_FileHasOption(cx, mode, "read") ? PR_RDONLY : 0; - mask |= js_FileHasOption(cx, mode, "write") ? PR_WRONLY : 0; - mask |= js_FileHasOption(cx, mode, "readWrite")? PR_RDWR : 0; - mask |= js_FileHasOption(cx, mode, "append") ? PR_APPEND : 0; - mask |= js_FileHasOption(cx, mode, "create") ? PR_CREATE_FILE : 0; - mask |= js_FileHasOption(cx, mode, "replace") ? PR_TRUNCATE : 0; - - if (mask & PR_RDWR) - mask |= (PR_RDONLY | PR_WRONLY); - if ((mask & PR_RDONLY) && (mask & PR_WRONLY)) - mask |= PR_RDWR; - - file->hasAutoflush |= js_FileHasOption(cx, mode, "autoflush"); - - /* Type */ - if (argc > 1) { - strtype = JS_ValueToString(cx, argv[1]); - if (!strtype) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_SECOND_ARGUMENT_OPEN_NOT_STRING_ERROR, - argv[1]); - goto out; - } - ctype = JS_GetStringBytes(strtype); - - if(!strcmp(ctype, utfstring)) { - type = UTF8; - } else if (!strcmp(ctype, unicodestring)) { - type = UCS2; - } else { - if (strcmp(ctype, asciistring)) { - JS_ReportWarning(cx, "File type %s is not supported, using " - "'text' instead, proceeding", ctype); - } - type = ASCII; - } - } else { - type = ASCII; - } - - /* Save the relevant fields */ - file->type = type; - file->mode = mask; - file->nativehandle = NULL; - file->hasRandomAccess = (type != UTF8); - - /* - * Deal with pipes here. We can't use NSPR for pipes, so we have to use - * POPEN. - */ - if (file->path[0]==PIPE_SYMBOL || file->path[len-1]==PIPE_SYMBOL) { - if (file->path[0] == PIPE_SYMBOL && file->path[len-1] == PIPE_SYMBOL) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_BIDIRECTIONAL_PIPE_NOT_SUPPORTED); - goto out; - } else { - int i = 0; - char pipemode[3]; - SECURITY_CHECK(cx, NULL, "pipe_open", file); - - if(file->path[0] == PIPE_SYMBOL){ - if(mask & (PR_WRONLY | PR_APPEND | PR_CREATE_FILE | PR_TRUNCATE)){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OPEN_MODE_NOT_SUPPORTED_WITH_PIPES, - mode, file->path); - goto out; - } - /* open(SPOOLER, "| cat -v | lpr -h 2>/dev/null") -- pipe for writing */ - pipemode[i++] = 'r'; -#ifndef XP_UNIX - pipemode[i++] = file->type==UTF8 ? 'b' : 't'; -#endif - pipemode[i++] = '\0'; - file->nativehandle = POPEN(&file->path[1], pipemode); - } else if(file->path[len-1] == PIPE_SYMBOL) { - char *command = JS_malloc(cx, len); - - strncpy(command, file->path, len-1); - command[len-1] = '\0'; - /* open(STATUS, "netstat -an 2>&1 |") */ - pipemode[i++] = 'w'; -#ifndef XP_UNIX - pipemode[i++] = file->type==UTF8 ? 'b' : 't'; -#endif - pipemode[i++] = '\0'; - file->nativehandle = POPEN(command, pipemode); - JS_free(cx, command); - } - /* set the flags */ - file->isNative = JS_TRUE; - file->isPipe = JS_TRUE; - file->hasRandomAccess = JS_FALSE; - } - } else { - /* TODO: what about the permissions?? Java ignores the problem... */ - file->handle = PR_Open(file->path, mask, 0644); - } - - js_ResetBuffers(file); - JS_free(cx, mode); - mode = NULL; - - /* Set the open flag and return result */ - if (file->handle == NULL && file->nativehandle == NULL) { - file->isOpen = JS_FALSE; - - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "open", file->path); - goto out; - } - -good: - file->isOpen = JS_TRUE; - *rval = JSVAL_TRUE; - return JS_TRUE; - -out: - if(mode) - JS_free(cx, mode); - return JS_FALSE; -} - -static JSBool -file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - - SECURITY_CHECK(cx, NULL, "close", file); - - if(!file->isOpen){ - JS_ReportWarning(cx, "File %s is not open, can't close it, proceeding", - file->path); - goto out; - } - - if(!file->isPipe){ - if(file->isNative){ - JS_ReportWarning(cx, "Unable to close a native file, proceeding", file->path); - goto out; - }else{ - if(file->handle && PR_Close(file->handle)){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "close", file->path); - - goto out; - } - } - }else{ - if(PCLOSE(file->nativehandle)==-1){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "pclose", file->path); - goto out; - } - } - - js_ResetAttributes(file); - *rval = JSVAL_TRUE; - return JS_TRUE; - -out: - return JS_FALSE; -} - - -static JSBool -file_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - - SECURITY_CHECK(cx, NULL, "remove", file); - JSFILE_CHECK_NATIVE("remove"); - JSFILE_CHECK_CLOSED("remove"); - - if ((js_isDirectory(cx, file) ? - PR_RmDir(file->path) : PR_Delete(file->path))==PR_SUCCESS) { - js_ResetAttributes(file); - *rval = JSVAL_TRUE; - return JS_TRUE; - } else { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "remove", file->path); - goto out; - } -out: - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -/* Raw PR-based function. No text processing. Just raw data copying. */ -static JSBool -file_copyTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - char *dest = NULL; - PRFileDesc *handle = NULL; - char *buffer; - jsval count, size; - JSBool fileInitiallyOpen=JS_FALSE; - - SECURITY_CHECK(cx, NULL, "copyTo", file); /* may need a second argument!*/ - JSFILE_CHECK_ONE_ARG("copyTo"); - JSFILE_CHECK_NATIVE("copyTo"); - /* remeber the state */ - fileInitiallyOpen = file->isOpen; - JSFILE_CHECK_READ; - - dest = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); - - /* make sure we are not reading a file open for writing */ - if (file->isOpen && !js_canRead(cx, file)) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_COPY_FILE_OPEN_FOR_WRITING_ERROR, file->path); - goto out; - } - - if (file->handle==NULL){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "open", file->path); - goto out; - } - - handle = PR_Open(dest, PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, 0644); - - if(!handle){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "open", dest); - goto out; - } - - if ((size=js_size(cx, file))==JSVAL_VOID) { - goto out; - } - - buffer = JS_malloc(cx, size); - - count = INT_TO_JSVAL(PR_Read(file->handle, buffer, size)); - - /* reading panic */ - if (count!=size) { - JS_free(cx, buffer); - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_COPY_READ_ERROR, file->path); - goto out; - } - - count = INT_TO_JSVAL(PR_Write(handle, buffer, JSVAL_TO_INT(size))); - - /* writing panic */ - if (count!=size) { - JS_free(cx, buffer); - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_COPY_WRITE_ERROR, file->path); - goto out; - } - - JS_free(cx, buffer); - - if(!fileInitiallyOpen){ - if(!file_close(cx, obj, 0, NULL, rval)) goto out; - } - - if(PR_Close(handle)!=PR_SUCCESS){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "close", dest); - goto out; - } - - *rval = JSVAL_TRUE; - return JS_TRUE; -out: - if(file->isOpen && !fileInitiallyOpen){ - if(PR_Close(file->handle)!=PR_SUCCESS){ - JS_ReportWarning(cx, "Can't close %s, proceeding", file->path); - } - } - - if(handle && PR_Close(handle)!=PR_SUCCESS){ - JS_ReportWarning(cx, "Can't close %s, proceeding", dest); - } - - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -static JSBool -file_renameTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - char *dest; - - SECURITY_CHECK(cx, NULL, "renameTo", file); /* may need a second argument!*/ - JSFILE_CHECK_ONE_ARG("renameTo"); - JSFILE_CHECK_NATIVE("renameTo"); - JSFILE_CHECK_CLOSED("renameTo"); - - dest = RESOLVE_PATH(cx, JS_GetStringBytes(JS_ValueToString(cx, argv[0]))); - - if (PR_Rename(file->path, dest)==PR_SUCCESS){ - /* copy the new filename */ - JS_free(cx, file->path); - file->path = dest; - *rval = JSVAL_TRUE; - return JS_TRUE; - }else{ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_RENAME_FAILED, file->path, dest); - goto out; - } -out: - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -static JSBool -file_flush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - - SECURITY_CHECK(cx, NULL, "flush", file); - JSFILE_CHECK_NATIVE("flush"); - JSFILE_CHECK_OPEN("flush"); - - if (PR_Sync(file->handle)==PR_SUCCESS){ - *rval = JSVAL_TRUE; - return JS_TRUE; - }else{ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "flush", file->path); - goto out; - } -out: - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -static JSBool -file_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - JSString *str; - int32 count; - uintN i; - - SECURITY_CHECK(cx, NULL, "write", file); - JSFILE_CHECK_WRITE; - - for (i = 0; itype); - if (count==-1){ - *rval = JSVAL_FALSE; - return JS_FALSE; - } - } - - *rval = JSVAL_TRUE; - return JS_TRUE; -out: - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -static JSBool -file_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - JSString *str; - - SECURITY_CHECK(cx, NULL, "writeln", file); - JSFILE_CHECK_WRITE; - - /* don't report an error here */ - if(!file_write(cx, obj, argc, argv, rval)) return JS_FALSE; - /* don't do security here -- we passed the check in file_write */ - str = JS_NewStringCopyZ(cx, "\n"); - - if (js_FileWrite(cx, file, JS_GetStringChars(str), JS_GetStringLength(str), - file->type)==-1){ - *rval = JSVAL_FALSE; - return JS_FALSE; - } - - /* eol causes flush if hasAutoflush is turned on */ - if (file->hasAutoflush) - file_flush(cx, obj, 0, NULL, rval); - - *rval = JSVAL_TRUE; - return JS_TRUE; -out: - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -static JSBool -file_writeAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - jsuint i; - jsuint limit; - JSObject *array; - JSObject *elem; - jsval elemval; - - SECURITY_CHECK(cx, NULL, "writeAll", file); - JSFILE_CHECK_ONE_ARG("writeAll"); - JSFILE_CHECK_WRITE; - - if (!JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[0]))) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FIRST_ARGUMENT_WRITEALL_NOT_ARRAY_ERROR); - goto out; - } - - array = JSVAL_TO_OBJECT(argv[0]); - - JS_GetArrayLength(cx, array, &limit); - - for (i = 0; i262144)?262144:want; * arbitrary size limitation */ - - buf = JS_malloc(cx, want*sizeof buf[0]); - if (!buf) goto out; - - count = js_FileRead(cx, file, buf, want, file->type); - if (count>0) { - str = JS_NewUCStringCopyN(cx, buf, count); - *rval = STRING_TO_JSVAL(str); - JS_free(cx, buf); - return JS_TRUE; - } else { - JS_free(cx, buf); - goto out; - } -out: - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -static JSBool -file_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - JSString *str; - jschar *buf = NULL, *tmp; - int32 offset, read; - intN room; - jschar data, data2; - - SECURITY_CHECK(cx, NULL, "readln", file); - JSFILE_CHECK_READ; - - buf = JS_malloc(cx, MAX_LINE_LENGTH * sizeof data); - if (!buf) - return JS_FALSE; - - room = MAX_LINE_LENGTH - 1; - offset = 0; - - for (;;) { - read = js_FileRead(cx, file, &data, 1, file->type); - if (read < 0) - goto out; - if (read == 0) - goto eof; - - switch (data) { - case '\r': - read = js_FileRead(cx, file, &data2, 1, file->type); - if (read < 0) - goto out; - - if (read == 1 && data2 != '\n') { - /* We read one char too far. Buffer it. */ - file->charBuffer = data2; - file->charBufferUsed = JS_TRUE; - } - - /* Fall through. */ - case '\n': - goto done; - - default: - if (--room < 0) { - tmp = JS_realloc(cx, buf, - (offset + MAX_LINE_LENGTH) * sizeof data); - if (!tmp) - goto out; - - room = MAX_LINE_LENGTH - 1; - buf = tmp; - } - - buf[offset++] = data; - break; - } - } - -eof: - if (offset == 0) { - *rval = JSVAL_NULL; - return JS_TRUE; - } - -done: - buf[offset] = 0; - tmp = JS_realloc(cx, buf, (offset + 1) * sizeof data); - if (!tmp) - goto out; - - str = JS_NewUCString(cx, tmp, offset); - if (!str) - goto out; - - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; - -out: - if (buf) - JS_free(cx, buf); - - return JS_FALSE; -} - -static JSBool -file_readAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - JSObject *array; - jsint len; - jsval line; - JSBool lineok = JS_FALSE; - - SECURITY_CHECK(cx, NULL, "readAll", file); - JSFILE_CHECK_READ; - - array = JS_NewArrayObject(cx, 0, NULL); - if (!array) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(array); - - len = 0; - - lineok = file_readln(cx, obj, 0, NULL, &line); - while (lineok && !JSVAL_IS_NULL(line)) { - JS_SetElement(cx, array, len++, &line); - lineok = file_readln(cx, obj, 0, NULL, &line); - } - -out: - return lineok; -} - -static JSBool -file_seek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - int32 toskip; - int32 pos; - - SECURITY_CHECK(cx, NULL, "seek", file); - JSFILE_CHECK_ONE_ARG("seek"); - JSFILE_CHECK_NATIVE("seek"); - JSFILE_CHECK_READ; - - if (!JS_ValueToInt32(cx, argv[0], &toskip)){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "seek", argv[0]); - goto out; - } - - if(!file->hasRandomAccess){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_NO_RANDOM_ACCESS, file->path); - goto out; - } - - if(js_isDirectory(cx, file)){ - JS_ReportWarning(cx,"Seek on directories is not supported, proceeding"); - goto out; - } - - pos = js_FileSeek(cx, file, toskip, file->type); - - if (pos!=-1) { - *rval = INT_TO_JSVAL(pos); - return JS_TRUE; - } -out: - *rval = JSVAL_VOID; - return JS_FALSE; -} - -static JSBool -file_list(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - PRDir *dir; - PRDirEntry *entry; - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - JSObject *array; - JSObject *eachFile; - jsint len; - jsval v; - JSRegExp *re = NULL; - JSFunction *func = NULL; - JSString *str; - jsval args[1]; - char *filePath; - - SECURITY_CHECK(cx, NULL, "list", file); - JSFILE_CHECK_NATIVE("list"); - - if (argc==1) { - if (JSVAL_IS_REGEXP(cx, argv[0])) { - re = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0])); - }else - if (VALUE_IS_FUNCTION(cx, argv[0])) { - func = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0])); - }else{ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_FUNCTION_OR_REGEX, argv[0]); - goto out; - } - } - - if (!js_isDirectory(cx, file)) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_DO_LIST_ON_A_FILE, file->path); - goto out; - } - - dir = PR_OpenDir(file->path); - if(!dir){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "open", file->path); - goto out; - } - - /* create JSArray here... */ - array = JS_NewArrayObject(cx, 0, NULL); - len = 0; - - while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))!=NULL) { - /* first, check if we have a regexp */ - if (re!=NULL) { - size_t index = 0; - - str = JS_NewStringCopyZ(cx, entry->name); - if(!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &v)){ - /* don't report anything here */ - goto out; - } - /* not matched! */ - if (JSVAL_IS_NULL(v)) { - continue; - } - }else - if (func!=NULL) { - str = JS_NewStringCopyZ(cx, entry->name); - args[0] = STRING_TO_JSVAL(str); - if(!JS_CallFunction(cx, obj, func, 1, args, &v)){ - goto out; - } - - if (v==JSVAL_FALSE) { - continue; - } - } - - filePath = js_combinePath(cx, file->path, (char*)entry->name); - - eachFile = js_NewFileObject(cx, filePath); - JS_free(cx, filePath); - if (!eachFile){ - JS_ReportWarning(cx, "File %s cannot be retrieved", filePath); - continue; - } - v = OBJECT_TO_JSVAL(eachFile); - JS_SetElement(cx, array, len, &v); - JS_SetProperty(cx, array, entry->name, &v); - len++; - } - - if(PR_CloseDir(dir)!=PR_SUCCESS){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "close", file->path); - goto out; - } - *rval = OBJECT_TO_JSVAL(array); - return JS_TRUE; -out: - *rval = JSVAL_NULL; - return JS_FALSE; -} - -static JSBool -file_mkdir(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - - SECURITY_CHECK(cx, NULL, "mkdir", file); - JSFILE_CHECK_ONE_ARG("mkdir"); - JSFILE_CHECK_NATIVE("mkdir"); - - /* if the current file is not a directory, find out the directory name */ - if (!js_isDirectory(cx, file)) { - char *dir = js_fileDirectoryName(cx, file->path); - JSObject *dirObj = js_NewFileObject(cx, dir); - - JS_free(cx, dir); - - /* call file_mkdir with the right set of parameters if needed */ - if (file_mkdir(cx, dirObj, argc, argv, rval)) - return JS_TRUE; - else - goto out; - }else{ - char *dirName = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); - char *fullName; - - fullName = js_combinePath(cx, file->path, dirName); - if (PR_MkDir(fullName, 0755)==PR_SUCCESS){ - *rval = JSVAL_TRUE; - JS_free(cx, fullName); - return JS_TRUE; - }else{ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "mkdir", fullName); - JS_free(cx, fullName); - goto out; - } - } -out: - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -static JSBool -file_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval*rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - JSString *str; - - str = JS_NewStringCopyZ(cx, file->path); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -file_toURL(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - char url[MAX_PATH_LENGTH]; - jschar *urlChars; - size_t len; - JSString *str; - - JSFILE_CHECK_NATIVE("toURL"); - - sprintf(url, "file://%s", file->path); - - len = strlen(url); - urlChars = js_InflateString(cx, url, &len); - if (!urlChars) - return JS_FALSE; - str = js_NewString(cx, urlChars, len, 0); - if (!str) { - JS_free(cx, urlChars); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - - /* TODO: js_escape in jsstr.h may go away at some point */ - return js_str_escape(cx, obj, 0, rval, rval); - -out: - *rval = JSVAL_VOID; - return JS_FALSE; -} - - -static void -file_finalize(JSContext *cx, JSObject *obj) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - - if(file) { - /* Close the file before exiting. */ - if(file->isOpen && !file->isNative) { - jsval vp; - file_close(cx, obj, 0, NULL, &vp); - } - - if (file->path) - JS_free(cx, file->path); - - JS_free(cx, file); - } -} - -/* - Allocates memory for the file object, sets fields to defaults. -*/ -static JSFile* -file_init(JSContext *cx, JSObject *obj, char *bytes) -{ - JSFile *file; - - file = JS_malloc(cx, sizeof *file); - if (!file) - return NULL; - memset(file, 0 , sizeof *file); - - js_ResetAttributes(file); - - file->path = RESOLVE_PATH(cx, bytes); - - if (!JS_SetPrivate(cx, obj, file)) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_SET_PRIVATE_FILE, file->path); - JS_free(cx, file); - return NULL; - } - - return file; -} - -/* Returns a JSObject. This function is globally visible */ -JS_PUBLIC_API(JSObject*) -js_NewFileObject(JSContext *cx, char *filename) -{ - JSObject *obj; - JSFile *file; - - obj = JS_NewObject(cx, &js_FileClass, NULL, NULL); - if (!obj){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObject"); - return NULL; - } - file = file_init(cx, obj, filename); - if(!file) return NULL; - return obj; -} - -/* Internal function, used for cases which NSPR file support doesn't cover */ -JSObject* -js_NewFileObjectFromFILE(JSContext *cx, FILE *nativehandle, char *filename, - int32 mode, JSBool open, JSBool randomAccess) -{ - JSObject *obj; - JSFile *file; - - obj = JS_NewObject(cx, &js_FileClass, NULL, NULL); - if (!obj){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObjectFromFILE"); - return NULL; - } - file = file_init(cx, obj, filename); - if(!file) return NULL; - - file->nativehandle = nativehandle; - - /* free result of RESOLVE_PATH from file_init. */ - JS_ASSERT(file->path != NULL); - JS_free(cx, file->path); - - file->path = strdup(filename); - file->isOpen = open; - file->mode = mode; - file->hasRandomAccess = randomAccess; - file->isNative = JS_TRUE; - return obj; -} - -/* - Real file constructor that is called from JavaScript. - Basically, does error processing and calls file_init. -*/ -static JSBool -file_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - JSFile *file; - - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - /* Replace obj with a new File object. */ - obj = JS_NewObject(cx, &js_FileClass, NULL, NULL); - if (!obj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - } - - str = (argc == 0) - ? JS_InternString(cx, "") - : JS_ValueToString(cx, argv[0]); - - if (!str) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FIRST_ARGUMENT_CONSTRUCTOR_NOT_STRING_ERROR, - argv[0]); - return JS_FALSE; - } - - file = file_init(cx, obj, JS_GetStringBytes(str)); - if (!file) - return JS_FALSE; - - SECURITY_CHECK(cx, NULL, "constructor", file); - - return JS_TRUE; -} - -/* -------------------- File methods and properties ------------------------- */ -static JSFunctionSpec file_functions[] = { - { "open", file_open, 0}, - { "close", file_close, 0}, - { "remove", file_remove, 0}, - { "copyTo", file_copyTo, 0}, - { "renameTo", file_renameTo, 0}, - { "flush", file_flush, 0}, - { "seek", file_seek, 0}, - { "read", file_read, 0}, - { "readln", file_readln, 0}, - { "readAll", file_readAll, 0}, - { "write", file_write, 0}, - { "writeln", file_writeln, 0}, - { "writeAll", file_writeAll, 0}, - { "list", file_list, 0}, - { "mkdir", file_mkdir, 0}, - { "toString", file_toString, 0}, - { "toURL", file_toURL, 0}, - {0} -}; - -enum file_tinyid { - FILE_LENGTH = -2, - FILE_PARENT = -3, - FILE_PATH = -4, - FILE_NAME = -5, - FILE_ISDIR = -6, - FILE_ISFILE = -7, - FILE_EXISTS = -8, - FILE_CANREAD = -9, - FILE_CANWRITE = -10, - FILE_OPEN = -11, - FILE_TYPE = -12, - FILE_MODE = -13, - FILE_CREATED = -14, - FILE_MODIFIED = -15, - FILE_SIZE = -16, - FILE_RANDOMACCESS = -17, - FILE_POSITION = -18, - FILE_APPEND = -19, - FILE_REPLACE = -20, - FILE_AUTOFLUSH = -21, - FILE_ISNATIVE = -22, -}; - -static JSPropertySpec file_props[] = { - {"length", FILE_LENGTH, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"parent", FILE_PARENT, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"path", FILE_PATH, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"name", FILE_NAME, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"isDirectory", FILE_ISDIR, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"isFile", FILE_ISFILE, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"exists", FILE_EXISTS, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"canRead", FILE_CANREAD, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"canWrite", FILE_CANWRITE, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"canAppend", FILE_APPEND, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"canReplace", FILE_REPLACE, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"isOpen", FILE_OPEN, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"type", FILE_TYPE, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"mode", FILE_MODE, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"creationTime", FILE_CREATED, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"lastModified", FILE_MODIFIED, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"size", FILE_SIZE, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"hasRandomAccess", FILE_RANDOMACCESS, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"hasAutoFlush", FILE_AUTOFLUSH, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"position", FILE_POSITION, JSPROP_ENUMERATE }, - {"isNative", FILE_ISNATIVE, JSPROP_ENUMERATE | JSPROP_READONLY }, - {0} -}; - -/* ------------------------- Property getter/setter ------------------------- */ -static JSBool -file_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - char *bytes; - JSString *str; - jsint tiny; - PRFileInfo info; - JSBool flag; - PRExplodedTime expandedTime; - - tiny = JSVAL_TO_INT(id); - if (!file) - return JS_TRUE; - - switch (tiny) { - case FILE_PARENT: - SECURITY_CHECK(cx, NULL, "parent", file); - if (!js_parent(cx, file, vp)) - return JS_FALSE; - break; - case FILE_PATH: - str = JS_NewStringCopyZ(cx, file->path); - if (!str) - return JS_FALSE; - *vp = STRING_TO_JSVAL(str); - break; - case FILE_NAME: - if (!js_name(cx, file, vp)) - return JS_FALSE; - break; - case FILE_ISDIR: - SECURITY_CHECK(cx, NULL, "isDirectory", file); - *vp = BOOLEAN_TO_JSVAL(js_isDirectory(cx, file)); - break; - case FILE_ISFILE: - SECURITY_CHECK(cx, NULL, "isFile", file); - *vp = BOOLEAN_TO_JSVAL(js_isFile(cx, file)); - break; - case FILE_EXISTS: - SECURITY_CHECK(cx, NULL, "exists", file); - *vp = BOOLEAN_TO_JSVAL(js_exists(cx, file)); - break; - case FILE_ISNATIVE: - SECURITY_CHECK(cx, NULL, "isNative", file); - *vp = BOOLEAN_TO_JSVAL(file->isNative); - break; - case FILE_CANREAD: - SECURITY_CHECK(cx, NULL, "canRead", file); - *vp = BOOLEAN_TO_JSVAL(js_canRead(cx, file)); - break; - case FILE_CANWRITE: - SECURITY_CHECK(cx, NULL, "canWrite", file); - *vp = BOOLEAN_TO_JSVAL(js_canWrite(cx, file)); - break; - case FILE_OPEN: - SECURITY_CHECK(cx, NULL, "isOpen", file); - *vp = BOOLEAN_TO_JSVAL(file->isOpen); - break; - case FILE_APPEND : - SECURITY_CHECK(cx, NULL, "canAppend", file); - JSFILE_CHECK_OPEN("canAppend"); - *vp = BOOLEAN_TO_JSVAL(!file->isNative && - (file->mode&PR_APPEND)==PR_APPEND); - break; - case FILE_REPLACE : - SECURITY_CHECK(cx, NULL, "canReplace", file); - JSFILE_CHECK_OPEN("canReplace"); - *vp = BOOLEAN_TO_JSVAL(!file->isNative && - (file->mode&PR_TRUNCATE)==PR_TRUNCATE); - break; - case FILE_AUTOFLUSH : - SECURITY_CHECK(cx, NULL, "hasAutoFlush", file); - JSFILE_CHECK_OPEN("hasAutoFlush"); - *vp = BOOLEAN_TO_JSVAL(!file->isNative && file->hasAutoflush); - break; - case FILE_TYPE: - SECURITY_CHECK(cx, NULL, "type", file); - JSFILE_CHECK_OPEN("type"); - if(js_isDirectory(cx, file)){ - *vp = JSVAL_VOID; - break; - } - - switch (file->type) { - case ASCII: - *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, asciistring)); - break; - case UTF8: - *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, utfstring)); - break; - case UCS2: - *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, unicodestring)); - break; - default: - JS_ReportWarning(cx, "Unsupported file type %d, proceeding", - file->type); - } - break; - case FILE_MODE: - SECURITY_CHECK(cx, NULL, "mode", file); - JSFILE_CHECK_OPEN("mode"); - bytes = JS_malloc(cx, MODE_SIZE); - bytes[0] = '\0'; - flag = JS_FALSE; - - if ((file->mode&PR_RDONLY)==PR_RDONLY) { - if (flag) strcat(bytes, ","); - strcat(bytes, "read"); - flag = JS_TRUE; - } - if ((file->mode&PR_WRONLY)==PR_WRONLY) { - if (flag) strcat(bytes, ","); - strcat(bytes, "write"); - flag = JS_TRUE; - } - if ((file->mode&PR_RDWR)==PR_RDWR) { - if (flag) strcat(bytes, ","); - strcat(bytes, "readWrite"); - flag = JS_TRUE; - } - if ((file->mode&PR_APPEND)==PR_APPEND) { - if (flag) strcat(bytes, ","); - strcat(bytes, "append"); - flag = JS_TRUE; - } - if ((file->mode&PR_CREATE_FILE)==PR_CREATE_FILE) { - if (flag) strcat(bytes, ","); - strcat(bytes, "create"); - flag = JS_TRUE; - } - if ((file->mode&PR_TRUNCATE)==PR_TRUNCATE) { - if (flag) strcat(bytes, ","); - strcat(bytes, "replace"); - flag = JS_TRUE; - } - if (file->hasAutoflush) { - if (flag) strcat(bytes, ","); - strcat(bytes, "hasAutoFlush"); - flag = JS_TRUE; - } - *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, bytes)); - JS_free(cx, bytes); - break; - case FILE_CREATED: - SECURITY_CHECK(cx, NULL, "creationTime", file); - JSFILE_CHECK_NATIVE("creationTime"); - if(((file->isOpen)? - PR_GetOpenFileInfo(file->handle, &info): - PR_GetFileInfo(file->path, &info))!=PR_SUCCESS){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); - goto out; - } - - PR_ExplodeTime(info.creationTime, PR_LocalTimeParameters,&expandedTime); - *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx, expandedTime.tm_year, - expandedTime.tm_month, - expandedTime.tm_mday, - expandedTime.tm_hour, - expandedTime.tm_min, - expandedTime.tm_sec)); - break; - case FILE_MODIFIED: - SECURITY_CHECK(cx, NULL, "lastModified", file); - JSFILE_CHECK_NATIVE("lastModified"); - if(((file->isOpen)? - PR_GetOpenFileInfo(file->handle, &info): - PR_GetFileInfo(file->path, &info))!=PR_SUCCESS){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); - goto out; - } - - PR_ExplodeTime(info.modifyTime, PR_LocalTimeParameters, &expandedTime); - *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx, expandedTime.tm_year, - expandedTime.tm_month, - expandedTime.tm_mday, - expandedTime.tm_hour, - expandedTime.tm_min, - expandedTime.tm_sec)); - break; - case FILE_SIZE: - SECURITY_CHECK(cx, NULL, "size", file); - *vp = js_size(cx, file); - break; - case FILE_LENGTH: - SECURITY_CHECK(cx, NULL, "length", file); - JSFILE_CHECK_NATIVE("length"); - - if (js_isDirectory(cx, file)) { /* XXX debug me */ - PRDir *dir; - PRDirEntry *entry; - jsint count = 0; - - if(!(dir = PR_OpenDir(file->path))){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_OPEN_DIR, file->path); - goto out; - } - - while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))) { - count++; - } - - if(!PR_CloseDir(dir)){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "close", file->path); - - goto out; - } - - *vp = INT_TO_JSVAL(count); - break; - }else{ - /* return file size */ - *vp = js_size(cx, file); - } - break; - case FILE_RANDOMACCESS: - SECURITY_CHECK(cx, NULL, "hasRandomAccess", file); - JSFILE_CHECK_OPEN("hasRandomAccess"); - *vp = BOOLEAN_TO_JSVAL(file->hasRandomAccess); - break; - case FILE_POSITION: - SECURITY_CHECK(cx, NULL, "position", file); - JSFILE_CHECK_NATIVE("position"); - JSFILE_CHECK_OPEN("position"); - - if(!file->hasRandomAccess){ - JS_ReportWarning(cx, "File %s doesn't support random access, can't report the position, proceeding"); - *vp = JSVAL_VOID; - break; - } - - if (file->isOpen && js_isFile(cx, file)) { - int pos = PR_Seek(file->handle, 0, PR_SEEK_CUR); - if(pos!=-1){ - *vp = INT_TO_JSVAL(pos); - }else{ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_REPORT_POSITION, file->path); - goto out; - } - }else { - JS_ReportWarning(cx, "File %s is closed or not a plain file," - " can't report position, proceeding"); - goto out; - } - break; - default: - SECURITY_CHECK(cx, NULL, "file_access", file); - - /* this is some other property -- try to use the dir["file"] syntax */ - if (js_isDirectory(cx, file)) { - PRDir *dir = NULL; - PRDirEntry *entry = NULL; - char *prop_name; - - str = JS_ValueToString(cx, id); - if (!str) - return JS_FALSE; - - prop_name = JS_GetStringBytes(str); - - /* no native files past this point */ - dir = PR_OpenDir(file->path); - if(!dir) { - /* This is probably not a directory */ - JS_ReportWarning(cx, "Can't open directory %s", file->path); - return JS_FALSE; - } - - while ((entry = PR_ReadDir(dir, PR_SKIP_NONE)) != NULL) { - if (!strcmp(entry->name, prop_name)){ - bytes = js_combinePath(cx, file->path, prop_name); - *vp = OBJECT_TO_JSVAL(js_NewFileObject(cx, bytes)); - PR_CloseDir(dir); - JS_free(cx, bytes); - return !JSVAL_IS_NULL(*vp); - } - } - PR_CloseDir(dir); - } - } - return JS_TRUE; - -out: - return JS_FALSE; -} - -static JSBool -file_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - jsint slot; - - if (JSVAL_IS_STRING(id)){ - return JS_TRUE; - } - - slot = JSVAL_TO_INT(id); - - switch (slot) { - /* File.position = 10 */ - case FILE_POSITION: - SECURITY_CHECK(cx, NULL, "set_position", file); - JSFILE_CHECK_NATIVE("set_position"); - - if(!file->hasRandomAccess){ - JS_ReportWarning(cx, "File %s doesn't support random access, can't " - "report the position, proceeding"); - goto out; - } - - if (file->isOpen && js_isFile(cx, file)) { - int32 pos; - int32 offset; - - if (!JS_ValueToInt32(cx, *vp, &offset)){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "position", *vp); - goto out; - } - - pos = PR_Seek(file->handle, offset, PR_SEEK_SET); - - if(pos!=-1){ - *vp = INT_TO_JSVAL(pos); - }else{ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_SET_POSITION, file->path); - goto out; - } - } else { - JS_ReportWarning(cx, "File %s is closed or not a file, can't set " - "position, proceeding", file->path); - goto out; - } - } - - return JS_TRUE; -out: - return JS_FALSE; -} - -/* - File.currentDir = new File("D:\") or File.currentDir = "D:\" -*/ -static JSBool -file_currentDirSetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSFile *file; - - file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - - /* Look at the rhs and extract a file object from it */ - if (JSVAL_IS_OBJECT(*vp)) { - if (JS_InstanceOf(cx, obj, &js_FileClass, NULL)) { - /* Braindamaged rhs -- just return the old value */ - if (file && (!js_exists(cx, file) || !js_isDirectory(cx, file))) { - JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp); - return JS_FALSE; - } else { - chdir(file->path); - return JS_TRUE; - } - } else { - return JS_FALSE; - } - } else { - JSObject *rhsObject; - char *path; - - path = JS_GetStringBytes(JS_ValueToString(cx, *vp)); - rhsObject = js_NewFileObject(cx, path); - if (!rhsObject) - return JS_FALSE; - - if (!file || !js_exists(cx, file) || !js_isDirectory(cx, file)){ - JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp); - } else { - *vp = OBJECT_TO_JSVAL(rhsObject); - chdir(path); - } - } - - return JS_TRUE; -} - -/* Declare class */ -JSClass js_FileClass = { - "File", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_File), - JS_PropertyStub, JS_PropertyStub, file_getProperty, file_setProperty, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, file_finalize -}; - -/* -------------------- Functions exposed to the outside -------------------- */ -JS_PUBLIC_API(JSObject*) -js_InitFileClass(JSContext *cx, JSObject* obj) -{ - JSObject *file, *ctor, *afile; - jsval vp; - char *currentdir; - char separator[2]; - - file = JS_InitClass(cx, obj, NULL, &js_FileClass, file_constructor, 1, - file_props, file_functions, NULL, NULL); - if (!file) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_INIT_FAILED); - return NULL; - } - - ctor = JS_GetConstructor(cx, file); - if (!ctor) return NULL; - - /* Define CURRENTDIR property. We are doing this to get a - slash at the end of the current dir */ - afile = js_NewFileObject(cx, CURRENT_DIR); - currentdir = JS_malloc(cx, MAX_PATH_LENGTH); - currentdir = getcwd(currentdir, MAX_PATH_LENGTH); - afile = js_NewFileObject(cx, currentdir); - JS_free(cx, currentdir); - vp = OBJECT_TO_JSVAL(afile); - JS_DefinePropertyWithTinyId(cx, ctor, CURRENTDIR_PROPERTY, 0, vp, - JS_PropertyStub, file_currentDirSetter, - JSPROP_ENUMERATE | JSPROP_READONLY ); - - /* Define input */ - vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stdin, - STDINPUT_NAME, PR_RDONLY, JS_TRUE, JS_FALSE)); - JS_SetProperty(cx, ctor, "input", &vp); - - /* Define output */ - vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stdout, - STDOUTPUT_NAME, PR_WRONLY, JS_TRUE, JS_FALSE)); - JS_SetProperty(cx, ctor, "output", &vp); - - /* Define error */ - vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stderr, - STDERROR_NAME, PR_WRONLY, JS_TRUE, JS_FALSE)); - JS_SetProperty(cx, ctor, "error", &vp); - - separator[0] = FILESEPARATOR; - separator[1] = '\0'; - vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, separator)); - JS_DefinePropertyWithTinyId(cx, ctor, SEPARATOR_PROPERTY, 0, vp, - JS_PropertyStub, JS_PropertyStub, - JSPROP_ENUMERATE | JSPROP_READONLY ); - return file; -} -#endif /* JS_HAS_FILE_OBJECT */ diff --git a/spidermonkey/src/jsfile.h b/spidermonkey/src/jsfile.h deleted file mode 100644 index 78707e8..0000000 --- a/spidermonkey/src/jsfile.h +++ /dev/null @@ -1,56 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef _jsfile_h__ -#define _jsfile_h__ - -#if JS_HAS_FILE_OBJECT - -#include "jsobj.h" - -extern JS_PUBLIC_API(JSObject*) -js_InitFileClass(JSContext *cx, JSObject* obj); - -extern JS_PUBLIC_API(JSObject*) -js_NewFileObject(JSContext *cx, char *bytes); - -extern JSClass js_FileClass; - -#endif /* JS_HAS_FILE_OBJECT */ -#endif /* _jsfile_h__ */ diff --git a/spidermonkey/src/jsfile.msg b/spidermonkey/src/jsfile.msg deleted file mode 100644 index 137b35d..0000000 --- a/spidermonkey/src/jsfile.msg +++ /dev/null @@ -1,90 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - Error messages for jsfile.c. See js.msg for format specification. -*/ - -MSG_DEF(JSFILEMSG_NOT_AN_ERROR, 0, 0, JSEXN_NONE, "") -MSG_DEF(JSFILEMSG_FILE_CONSTRUCTOR_UNDEFINED_ERROR, 1, 0, JSEXN_NONE, "File constructor is undefined") -MSG_DEF(JSFILEMSG_FILE_CURRENTDIR_UNDEFINED_ERROR, 2, 0, JSEXN_NONE, "File.currentDir is undefined") -MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_OPEN_NOT_STRING_ERROR, 3, 1, JSEXN_NONE, "The first argument {0} to file.open must be a string") -MSG_DEF(JSFILEMSG_SECOND_ARGUMENT_OPEN_NOT_STRING_ERROR, 4, 0, JSEXN_NONE, "The second argument to file.open must be a string") -MSG_DEF(JSFILEMSG_CANNOT_COPY_FILE_OPEN_FOR_WRITING_ERROR, 5, 1, JSEXN_NONE, "Cannot copy file {0} open for writing") -MSG_DEF(JSFILEMSG_CANNOT_ACCESS_FILE_INFO_ERROR, 6, 1, JSEXN_NONE, "Cannot access file information for {0}") -MSG_DEF(JSFILEMSG_COPY_READ_ERROR, 7, 1, JSEXN_NONE, "An error occured while attempting to read a file {0} to copy") -MSG_DEF(JSFILEMSG_COPY_WRITE_ERROR, 8, 1, JSEXN_NONE, "An error occured while attempting to copy into file {0}") -MSG_DEF(JSFILEMSG_EXPECTS_ONE_ARG_ERROR, 9, 0, JSEXN_NONE, "Operation {0} expects one argument, not {1}") -MSG_DEF(JSFILEMSG_CANNOT_FLUSH_CLOSE_FILE_ERROR, 10, 1, JSEXN_NONE, "Cannot flush closed file {0}") -MSG_DEF(JSFILEMSG_CANNOT_OPEN_WRITING_ERROR, 11, 1, JSEXN_NONE, "Cannot open file {0} for writing") -MSG_DEF(JSFILEMSG_WRITEALL_EXPECTS_ONE_ARG_ERROR, 12, 0, JSEXN_NONE, "writeAll expects one argument") -MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_WRITEALL_NOT_ARRAY_ERROR, 13, 0, JSEXN_NONE, "writeAll expects an array as an argument") -MSG_DEF(JSFILEMSG_UNUSED0, 14, 0, JSEXN_NONE, "Unused error message slot") -MSG_DEF(JSFILEMSG_CANNOT_OPEN_FILE_ERROR, 15, 1, JSEXN_NONE, "Cannot open file {0}") -MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_CONSTRUCTOR_NOT_STRING_ERROR, 16, 1, JSEXN_NONE, "The argument to the File constructor {0} must be a string") -MSG_DEF(JSFILEMSG_BIDIRECTIONAL_PIPE_NOT_SUPPORTED, 17, 0, JSEXN_NONE, "Bidirectional pipes are not supported") -MSG_DEF(JSFILEMSG_OPEN_MODE_NOT_SUPPORTED_WITH_PIPES, 18, 2, JSEXN_NONE, "The opening mode you have chosen {0} is not supported by the pipe you are trying to open: {1}") -MSG_DEF(JSFILEMSG_OPEN_FAILED, 19, 1, JSEXN_NONE, "open on file {0} failed") -MSG_DEF(JSFILEMSG_CLOSE_FAILED, 20, 1, JSEXN_NONE, "close on file {0} failed") -MSG_DEF(JSFILEMSG_PCLOSE_FAILED, 21, 1, JSEXN_NONE, "pclose on file {0} failed") -MSG_DEF(JSFILEMSG_REMOVE_FAILED, 22, 1, JSEXN_NONE, "remove on file {0} failed") -MSG_DEF(JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, 23, 1, JSEXN_NONE, "Cannot access file status for {0}") -MSG_DEF(JSFILEMSG_RENAME_FAILED, 24, 2, JSEXN_NONE, "Cannot rename {0} to {1}") -MSG_DEF(JSFILEMSG_WRITE_FAILED, 25, 1, JSEXN_NONE, "Write failed on file {0}") -MSG_DEF(JSFILEMSG_READ_FAILED, 26, 1, JSEXN_NONE, "Read failed on file {0}") -MSG_DEF(JSFILEMSG_SKIP_FAILED, 27, 1, JSEXN_NONE, "Skip failed on file {0}") -MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_FUNCTION_OR_REGEX, 28, 1, JSEXN_NONE, "The first argument to file.list must be a function or a regex") -MSG_DEF(JSFILEMSG_CANNOT_DO_LIST_ON_A_FILE, 29, 1, JSEXN_NONE, "{0} must be a directory, cannot do list") -MSG_DEF(JSFILEMSG_NATIVE_OPERATION_IS_NOT_SUPPORTED, 30, 2, JSEXN_NONE, "Native operation {0} is not supported on {1}") -MSG_DEF(JSFILEMSG_CANNOT_SET_PRIVATE_FILE, 31, 1, JSEXN_NONE, "Cannot set private data for file {0}") -MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, 32, 2, JSEXN_NONE, "First argument to {0} must be a number, not {1}") -MSG_DEF(JSFILEMSG_CANNOT_WRITE, 33, 1, JSEXN_NONE, "Cannot write to {0}, file mode is different") -MSG_DEF(JSFILEMSG_CANNOT_READ, 34, 1, JSEXN_NONE, "Cannot read from {0}, file mode is different") -MSG_DEF(JSFILEMSG_CANNOT_FLUSH, 35, 1, JSEXN_NONE, "Flush failed on {0}") -MSG_DEF(JSFILEMSG_OP_FAILED, 36, 1, JSEXN_NONE, "File operation {0} failed") -MSG_DEF(JSFILEMSG_FILE_MUST_BE_OPEN, 37, 1, JSEXN_NONE, "File must be open for {0}") -MSG_DEF(JSFILEMSG_FILE_MUST_BE_CLOSED, 38, 1, JSEXN_NONE, "File must be closed for {0}") -MSG_DEF(JSFILEMSG_NO_RANDOM_ACCESS, 39, 1, JSEXN_NONE, "File {0} doesn't allow random access") -MSG_DEF(JSFILEMSG_OBJECT_CREATION_FAILED, 40, 1, JSEXN_NONE, "Couldn't create {0}") -MSG_DEF(JSFILEMSG_CANNOT_OPEN_DIR, 41, 1, JSEXN_NONE, "Couldn't open directory {0}") -MSG_DEF(JSFILEMSG_CANNOT_REPORT_POSITION, 42, 1, JSEXN_NONE, "Couldn't report position for {0}") -MSG_DEF(JSFILEMSG_CANNOT_SET_POSITION, 43, 1, JSEXN_NONE, "Couldn't set position for {0}") -MSG_DEF(JSFILEMSG_INIT_FAILED, 44, 0, JSEXN_NONE, "File class initialization failed") - - diff --git a/spidermonkey/src/jsfun.c b/spidermonkey/src/jsfun.c deleted file mode 100644 index 2a2df53..0000000 --- a/spidermonkey/src/jsfun.c +++ /dev/null @@ -1,2330 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS function support. - */ -#include "jsstddef.h" -#include -#include "jstypes.h" -#include "jsbit.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsdbgapi.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsparse.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" -#include "jsexn.h" - -#if JS_HAS_GENERATORS -# include "jsiter.h" -#endif - -/* Generic function/call/arguments tinyids -- also reflected bit numbers. */ -enum { - CALL_ARGUMENTS = -1, /* predefined arguments local variable */ - CALL_CALLEE = -2, /* reference to active function's object */ - ARGS_LENGTH = -3, /* number of actual args, arity if inactive */ - ARGS_CALLEE = -4, /* reference from arguments to active funobj */ - FUN_ARITY = -5, /* number of formal parameters; desired argc */ - FUN_NAME = -6, /* function name, "" if anonymous */ - FUN_CALLER = -7 /* Function.prototype.caller, backward compat */ -}; - -#if JSFRAME_OVERRIDE_BITS < 8 -# error "not enough override bits in JSStackFrame.flags!" -#endif - -#define TEST_OVERRIDE_BIT(fp, tinyid) \ - ((fp)->flags & JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1))) - -#define SET_OVERRIDE_BIT(fp, tinyid) \ - ((fp)->flags |= JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1))) - -JSBool -js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp) -{ - JSObject *argsobj; - - if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) { - JS_ASSERT(fp->callobj); - return OBJ_GET_PROPERTY(cx, fp->callobj, - ATOM_TO_JSID(cx->runtime->atomState - .argumentsAtom), - vp); - } - argsobj = js_GetArgsObject(cx, fp); - if (!argsobj) - return JS_FALSE; - *vp = OBJECT_TO_JSVAL(argsobj); - return JS_TRUE; -} - -static JSBool -MarkArgDeleted(JSContext *cx, JSStackFrame *fp, uintN slot) -{ - JSObject *argsobj; - jsval bmapval, bmapint; - size_t nbits, nbytes; - jsbitmap *bitmap; - - argsobj = fp->argsobj; - (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval); - nbits = fp->argc; - JS_ASSERT(slot < nbits); - if (JSVAL_IS_VOID(bmapval)) { - if (nbits <= JSVAL_INT_BITS) { - bmapint = 0; - bitmap = (jsbitmap *) &bmapint; - } else { - nbytes = JS_HOWMANY(nbits, JS_BITS_PER_WORD) * sizeof(jsbitmap); - bitmap = (jsbitmap *) JS_malloc(cx, nbytes); - if (!bitmap) - return JS_FALSE; - memset(bitmap, 0, nbytes); - bmapval = PRIVATE_TO_JSVAL(bitmap); - JS_SetReservedSlot(cx, argsobj, 0, bmapval); - } - } else { - if (nbits <= JSVAL_INT_BITS) { - bmapint = JSVAL_TO_INT(bmapval); - bitmap = (jsbitmap *) &bmapint; - } else { - bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval); - } - } - JS_SET_BIT(bitmap, slot); - if (bitmap == (jsbitmap *) &bmapint) { - bmapval = INT_TO_JSVAL(bmapint); - JS_SetReservedSlot(cx, argsobj, 0, bmapval); - } - return JS_TRUE; -} - -/* NB: Infallible predicate, false does not mean error/exception. */ -static JSBool -ArgWasDeleted(JSContext *cx, JSStackFrame *fp, uintN slot) -{ - JSObject *argsobj; - jsval bmapval, bmapint; - jsbitmap *bitmap; - - argsobj = fp->argsobj; - (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval); - if (JSVAL_IS_VOID(bmapval)) - return JS_FALSE; - if (fp->argc <= JSVAL_INT_BITS) { - bmapint = JSVAL_TO_INT(bmapval); - bitmap = (jsbitmap *) &bmapint; - } else { - bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval); - } - return JS_TEST_BIT(bitmap, slot) != 0; -} - -JSBool -js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, - JSObject **objp, jsval *vp) -{ - jsval val; - JSObject *obj; - uintN slot; - - if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) { - JS_ASSERT(fp->callobj); - if (!OBJ_GET_PROPERTY(cx, fp->callobj, - ATOM_TO_JSID(cx->runtime->atomState - .argumentsAtom), - &val)) { - return JS_FALSE; - } - if (JSVAL_IS_PRIMITIVE(val)) { - obj = js_ValueToNonNullObject(cx, val); - if (!obj) - return JS_FALSE; - } else { - obj = JSVAL_TO_OBJECT(val); - } - *objp = obj; - return OBJ_GET_PROPERTY(cx, obj, id, vp); - } - - *objp = NULL; - *vp = JSVAL_VOID; - if (JSID_IS_INT(id)) { - slot = (uintN) JSID_TO_INT(id); - if (slot < fp->argc) { - if (fp->argsobj && ArgWasDeleted(cx, fp, slot)) - return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp); - *vp = fp->argv[slot]; - } else { - /* - * Per ECMA-262 Ed. 3, 10.1.8, last bulleted item, do not share - * storage between the formal parameter and arguments[k] for all - * k >= fp->argc && k < fp->fun->nargs. For example, in - * - * function f(x) { x = 42; return arguments[0]; } - * f(); - * - * the call to f should return undefined, not 42. If fp->argsobj - * is null at this point, as it would be in the example, return - * undefined in *vp. - */ - if (fp->argsobj) - return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp); - } - } else { - if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) { - if (fp->argsobj && TEST_OVERRIDE_BIT(fp, ARGS_LENGTH)) - return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp); - *vp = INT_TO_JSVAL((jsint) fp->argc); - } - } - return JS_TRUE; -} - -JSObject * -js_GetArgsObject(JSContext *cx, JSStackFrame *fp) -{ - JSObject *argsobj, *global, *parent; - - /* - * We must be in a function activation; the function must be lightweight - * or else fp must have a variable object. - */ - JS_ASSERT(fp->fun && (!(fp->fun->flags & JSFUN_HEAVYWEIGHT) || fp->varobj)); - - /* Skip eval and debugger frames. */ - while (fp->flags & JSFRAME_SPECIAL) - fp = fp->down; - - /* Create an arguments object for fp only if it lacks one. */ - argsobj = fp->argsobj; - if (argsobj) - return argsobj; - - /* Link the new object to fp so it can get actual argument values. */ - argsobj = js_NewObject(cx, &js_ArgumentsClass, NULL, NULL); - if (!argsobj || !JS_SetPrivate(cx, argsobj, fp)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; - } - - /* - * Give arguments an intrinsic scope chain link to fp's global object. - * Since the arguments object lacks a prototype because js_ArgumentsClass - * is not initialized, js_NewObject won't assign a default parent to it. - * - * Therefore if arguments is used as the head of an eval scope chain (via - * a direct or indirect call to eval(program, arguments)), any reference - * to a standard class object in the program will fail to resolve due to - * js_GetClassPrototype not being able to find a global object containing - * the standard prototype by starting from arguments and following parent. - */ - global = fp->scopeChain; - while ((parent = OBJ_GET_PARENT(cx, global)) != NULL) - global = parent; - argsobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(global); - fp->argsobj = argsobj; - return argsobj; -} - -static JSBool -args_enumerate(JSContext *cx, JSObject *obj); - -JSBool -js_PutArgsObject(JSContext *cx, JSStackFrame *fp) -{ - JSObject *argsobj; - jsval bmapval, rval; - JSBool ok; - JSRuntime *rt; - - /* - * Reuse args_enumerate here to reflect fp's actual arguments as indexed - * elements of argsobj. Do this first, before clearing and freeing the - * deleted argument slot bitmap, because args_enumerate depends on that. - */ - argsobj = fp->argsobj; - ok = args_enumerate(cx, argsobj); - - /* - * Now clear the deleted argument number bitmap slot and free the bitmap, - * if one was actually created due to 'delete arguments[0]' or similar. - */ - (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval); - if (!JSVAL_IS_VOID(bmapval)) { - JS_SetReservedSlot(cx, argsobj, 0, JSVAL_VOID); - if (fp->argc > JSVAL_INT_BITS) - JS_free(cx, JSVAL_TO_PRIVATE(bmapval)); - } - - /* - * Now get the prototype properties so we snapshot fp->fun and fp->argc - * before fp goes away. - */ - rt = cx->runtime; - ok &= js_GetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.calleeAtom), - &rval); - ok &= js_SetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.calleeAtom), - &rval); - ok &= js_GetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.lengthAtom), - &rval); - ok &= js_SetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.lengthAtom), - &rval); - - /* - * Clear the private pointer to fp, which is about to go away (js_Invoke). - * Do this last because the args_enumerate and js_GetProperty calls above - * need to follow the private slot to find fp. - */ - ok &= JS_SetPrivate(cx, argsobj, NULL); - fp->argsobj = NULL; - return ok; -} - -static JSBool -args_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsint slot; - JSStackFrame *fp; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - fp = (JSStackFrame *) - JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); - if (!fp) - return JS_TRUE; - JS_ASSERT(fp->argsobj); - - slot = JSVAL_TO_INT(id); - switch (slot) { - case ARGS_CALLEE: - case ARGS_LENGTH: - SET_OVERRIDE_BIT(fp, slot); - break; - - default: - if ((uintN)slot < fp->argc && !MarkArgDeleted(cx, fp, slot)) - return JS_FALSE; - break; - } - return JS_TRUE; -} - -static JSBool -args_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsint slot; - JSStackFrame *fp; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - fp = (JSStackFrame *) - JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); - if (!fp) - return JS_TRUE; - JS_ASSERT(fp->argsobj); - - slot = JSVAL_TO_INT(id); - switch (slot) { - case ARGS_CALLEE: - if (!TEST_OVERRIDE_BIT(fp, slot)) - *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object); - break; - - case ARGS_LENGTH: - if (!TEST_OVERRIDE_BIT(fp, slot)) - *vp = INT_TO_JSVAL((jsint)fp->argc); - break; - - default: - if ((uintN)slot < fp->argc && !ArgWasDeleted(cx, fp, slot)) - *vp = fp->argv[slot]; - break; - } - return JS_TRUE; -} - -static JSBool -args_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSStackFrame *fp; - jsint slot; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - fp = (JSStackFrame *) - JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); - if (!fp) - return JS_TRUE; - JS_ASSERT(fp->argsobj); - - slot = JSVAL_TO_INT(id); - switch (slot) { - case ARGS_CALLEE: - case ARGS_LENGTH: - SET_OVERRIDE_BIT(fp, slot); - break; - - default: - if (FUN_INTERPRETED(fp->fun) && - (uintN)slot < fp->argc && - !ArgWasDeleted(cx, fp, slot)) { - fp->argv[slot] = *vp; - } - break; - } - return JS_TRUE; -} - -static JSBool -args_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, - JSObject **objp) -{ - JSStackFrame *fp; - uintN slot; - JSString *str; - JSAtom *atom; - intN tinyid; - jsval value; - - *objp = NULL; - fp = (JSStackFrame *) - JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); - if (!fp) - return JS_TRUE; - JS_ASSERT(fp->argsobj); - - if (JSVAL_IS_INT(id)) { - slot = JSVAL_TO_INT(id); - if (slot < fp->argc && !ArgWasDeleted(cx, fp, slot)) { - /* XXX ECMA specs DontEnum, contrary to other array-like objects */ - if (!js_DefineProperty(cx, obj, INT_JSVAL_TO_JSID(id), - fp->argv[slot], - args_getProperty, args_setProperty, - JS_VERSION_IS_ECMA(cx) - ? 0 - : JSPROP_ENUMERATE, - NULL)) { - return JS_FALSE; - } - *objp = obj; - } - } else { - str = JSVAL_TO_STRING(id); - atom = cx->runtime->atomState.lengthAtom; - if (str == ATOM_TO_STRING(atom)) { - tinyid = ARGS_LENGTH; - value = INT_TO_JSVAL(fp->argc); - } else { - atom = cx->runtime->atomState.calleeAtom; - if (str == ATOM_TO_STRING(atom)) { - tinyid = ARGS_CALLEE; - value = fp->argv ? fp->argv[-2] - : OBJECT_TO_JSVAL(fp->fun->object); - } else { - atom = NULL; - - /* Quell GCC overwarnings. */ - tinyid = 0; - value = JSVAL_NULL; - } - } - - if (atom && !TEST_OVERRIDE_BIT(fp, tinyid)) { - if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value, - args_getProperty, args_setProperty, 0, - SPROP_HAS_SHORTID, tinyid, NULL)) { - return JS_FALSE; - } - *objp = obj; - } - } - - return JS_TRUE; -} - -static JSBool -args_enumerate(JSContext *cx, JSObject *obj) -{ - JSStackFrame *fp; - JSObject *pobj; - JSProperty *prop; - uintN slot, argc; - - fp = (JSStackFrame *) - JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); - if (!fp) - return JS_TRUE; - JS_ASSERT(fp->argsobj); - - /* - * Trigger reflection with value snapshot in args_resolve using a series - * of js_LookupProperty calls. We handle length, callee, and the indexed - * argument properties. We know that args_resolve covers all these cases - * and creates direct properties of obj, but that it may fail to resolve - * length or callee if overridden. - */ - if (!js_LookupProperty(cx, obj, - ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), - &pobj, &prop)) { - return JS_FALSE; - } - if (prop) - OBJ_DROP_PROPERTY(cx, pobj, prop); - - if (!js_LookupProperty(cx, obj, - ATOM_TO_JSID(cx->runtime->atomState.calleeAtom), - &pobj, &prop)) { - return JS_FALSE; - } - if (prop) - OBJ_DROP_PROPERTY(cx, pobj, prop); - - argc = fp->argc; - for (slot = 0; slot < argc; slot++) { - if (!js_LookupProperty(cx, obj, INT_TO_JSID((jsint)slot), &pobj, &prop)) - return JS_FALSE; - if (prop) - OBJ_DROP_PROPERTY(cx, pobj, prop); - } - return JS_TRUE; -} - -#if JS_HAS_GENERATORS -/* - * If a generator-iterator's arguments or call object escapes, it needs to - * mark its generator object. - */ -static uint32 -args_or_call_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSStackFrame *fp; - - fp = JS_GetPrivate(cx, obj); - if (fp && (fp->flags & JSFRAME_GENERATOR)) - GC_MARK(cx, FRAME_TO_GENERATOR(fp)->obj, "FRAME_TO_GENERATOR(fp)->obj"); - return 0; -} -#else -# define args_or_call_mark NULL -#endif - -/* - * The Arguments class is not initialized via JS_InitClass, and must not be, - * because its name is "Object". Per ECMA, that causes instances of it to - * delegate to the object named by Object.prototype. It also ensures that - * arguments.toString() returns "[object Object]". - * - * The JSClass functions below collaborate to lazily reflect and synchronize - * actual argument values, argument count, and callee function object stored - * in a JSStackFrame with their corresponding property values in the frame's - * arguments object. - */ -JSClass js_ArgumentsClass = { - js_Object_str, - JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1) | - JSCLASS_HAS_CACHED_PROTO(JSProto_Object), - JS_PropertyStub, args_delProperty, - args_getProperty, args_setProperty, - args_enumerate, (JSResolveOp) args_resolve, - JS_ConvertStub, JS_FinalizeStub, - NULL, NULL, - NULL, NULL, - NULL, NULL, - args_or_call_mark, NULL -}; - -JSObject * -js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent) -{ - JSObject *callobj, *funobj; - - /* Create a call object for fp only if it lacks one. */ - JS_ASSERT(fp->fun); - callobj = fp->callobj; - if (callobj) - return callobj; - JS_ASSERT(fp->fun); - - /* The default call parent is its function's parent (static link). */ - if (!parent) { - funobj = fp->argv ? JSVAL_TO_OBJECT(fp->argv[-2]) : fp->fun->object; - if (funobj) - parent = OBJ_GET_PARENT(cx, funobj); - } - - /* Create the call object and link it to its stack frame. */ - callobj = js_NewObject(cx, &js_CallClass, NULL, parent); - if (!callobj || !JS_SetPrivate(cx, callobj, fp)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; - } - fp->callobj = callobj; - - /* Make callobj be the scope chain and the variables object. */ - JS_ASSERT(fp->scopeChain == parent); - fp->scopeChain = callobj; - fp->varobj = callobj; - return callobj; -} - -static JSBool -call_enumerate(JSContext *cx, JSObject *obj); - -JSBool -js_PutCallObject(JSContext *cx, JSStackFrame *fp) -{ - JSObject *callobj; - JSBool ok; - jsid argsid; - jsval aval; - - /* - * Reuse call_enumerate here to reflect all actual args and vars into the - * call object from fp. - */ - callobj = fp->callobj; - if (!callobj) - return JS_TRUE; - ok = call_enumerate(cx, callobj); - - /* - * Get the arguments object to snapshot fp's actual argument values. - */ - if (fp->argsobj) { - argsid = ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom); - ok &= js_GetProperty(cx, callobj, argsid, &aval); - ok &= js_SetProperty(cx, callobj, argsid, &aval); - ok &= js_PutArgsObject(cx, fp); - } - - /* - * Clear the private pointer to fp, which is about to go away (js_Invoke). - * Do this last because the call_enumerate and js_GetProperty calls above - * need to follow the private slot to find fp. - */ - ok &= JS_SetPrivate(cx, callobj, NULL); - fp->callobj = NULL; - return ok; -} - -static JSPropertySpec call_props[] = { - {js_arguments_str, CALL_ARGUMENTS, JSPROP_PERMANENT,0,0}, - {"__callee__", CALL_CALLEE, 0,0,0}, - {0,0,0,0,0} -}; - -static JSBool -call_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSStackFrame *fp; - jsint slot; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); - if (!fp) - return JS_TRUE; - JS_ASSERT(fp->fun); - - slot = JSVAL_TO_INT(id); - switch (slot) { - case CALL_ARGUMENTS: - if (!TEST_OVERRIDE_BIT(fp, slot)) { - JSObject *argsobj = js_GetArgsObject(cx, fp); - if (!argsobj) - return JS_FALSE; - *vp = OBJECT_TO_JSVAL(argsobj); - } - break; - - case CALL_CALLEE: - if (!TEST_OVERRIDE_BIT(fp, slot)) - *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object); - break; - - default: - if ((uintN)slot < JS_MAX(fp->argc, fp->fun->nargs)) - *vp = fp->argv[slot]; - break; - } - return JS_TRUE; -} - -static JSBool -call_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSStackFrame *fp; - jsint slot; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); - if (!fp) - return JS_TRUE; - JS_ASSERT(fp->fun); - - slot = JSVAL_TO_INT(id); - switch (slot) { - case CALL_ARGUMENTS: - case CALL_CALLEE: - SET_OVERRIDE_BIT(fp, slot); - break; - - default: - if ((uintN)slot < JS_MAX(fp->argc, fp->fun->nargs)) - fp->argv[slot] = *vp; - break; - } - return JS_TRUE; -} - -JSBool -js_GetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSStackFrame *fp; - - JS_ASSERT(JSVAL_IS_INT(id)); - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); - if (fp) { - /* XXX no jsint slot commoning here to avoid MSVC1.52 crashes */ - if ((uintN)JSVAL_TO_INT(id) < fp->nvars) - *vp = fp->vars[JSVAL_TO_INT(id)]; - } - return JS_TRUE; -} - -JSBool -js_SetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSStackFrame *fp; - - JS_ASSERT(JSVAL_IS_INT(id)); - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); - if (fp) { - /* XXX jsint slot is block-local here to avoid MSVC1.52 crashes */ - jsint slot = JSVAL_TO_INT(id); - if ((uintN)slot < fp->nvars) - fp->vars[slot] = *vp; - } - return JS_TRUE; -} - -static JSBool -call_enumerate(JSContext *cx, JSObject *obj) -{ - JSStackFrame *fp; - JSObject *funobj, *pobj; - JSScope *scope; - JSScopeProperty *sprop, *cprop; - JSPropertyOp getter; - jsval *vec; - JSAtom *atom; - JSProperty *prop; - - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); - if (!fp) - return JS_TRUE; - - /* - * Do not enumerate a cloned function object at fp->argv[-2], it may have - * gained its own (mutable) scope (e.g., a brutally-shared XUL script sets - * the clone's prototype property). We must enumerate the function object - * that was decorated with parameter and local variable properties by the - * compiler when the compiler created fp->fun, namely fp->fun->object. - * - * Contrast with call_resolve, where we prefer fp->argv[-2], because we'll - * use js_LookupProperty to find any overridden properties in that object, - * if it was a mutated clone; and if not, we will search its prototype, - * fp->fun->object, to find compiler-created params and locals. - */ - funobj = fp->fun->object; - if (!funobj) - return JS_TRUE; - - /* - * Reflect actual args from fp->argv for formal parameters, and local vars - * and functions in fp->vars for declared variables and nested-at-top-level - * local functions. - */ - scope = OBJ_SCOPE(funobj); - for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { - getter = sprop->getter; - if (getter == js_GetArgument) - vec = fp->argv; - else if (getter == js_GetLocalVariable) - vec = fp->vars; - else - continue; - - /* Trigger reflection by looking up the unhidden atom for sprop->id. */ - JS_ASSERT(JSID_IS_ATOM(sprop->id)); - atom = JSID_TO_ATOM(sprop->id); - JS_ASSERT(atom->flags & ATOM_HIDDEN); - atom = atom->entry.value; - - if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) - return JS_FALSE; - - /* - * If we found the property in a different object, don't try sticking - * it into wrong slots vector. This can occur because we have a mutable - * __proto__ slot, and cloned function objects rely on their __proto__ - * to delegate to the object that contains the var and arg properties. - */ - if (!prop || pobj != obj) { - if (prop) - OBJ_DROP_PROPERTY(cx, pobj, prop); - continue; - } - cprop = (JSScopeProperty *)prop; - LOCKED_OBJ_SET_SLOT(obj, cprop->slot, vec[(uint16) sprop->shortid]); - OBJ_DROP_PROPERTY(cx, obj, prop); - } - - return JS_TRUE; -} - -static JSBool -call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, - JSObject **objp) -{ - JSStackFrame *fp; - JSObject *funobj; - JSString *str; - JSAtom *atom; - JSObject *obj2; - JSProperty *prop; - JSScopeProperty *sprop; - JSPropertyOp getter, setter; - uintN attrs, slot, nslots, spflags; - jsval *vp, value; - intN shortid; - - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); - if (!fp) - return JS_TRUE; - JS_ASSERT(fp->fun); - - if (!JSVAL_IS_STRING(id)) - return JS_TRUE; - - funobj = fp->argv ? JSVAL_TO_OBJECT(fp->argv[-2]) : fp->fun->object; - if (!funobj) - return JS_TRUE; - JS_ASSERT((JSFunction *) JS_GetPrivate(cx, funobj) == fp->fun); - - str = JSVAL_TO_STRING(id); - atom = js_AtomizeString(cx, str, 0); - if (!atom) - return JS_FALSE; - if (!js_LookupHiddenProperty(cx, funobj, ATOM_TO_JSID(atom), &obj2, &prop)) - return JS_FALSE; - - if (prop) { - if (!OBJ_IS_NATIVE(obj2)) { - OBJ_DROP_PROPERTY(cx, obj2, prop); - return JS_TRUE; - } - - sprop = (JSScopeProperty *) prop; - getter = sprop->getter; - attrs = sprop->attrs & ~JSPROP_SHARED; - slot = (uintN) sprop->shortid; - OBJ_DROP_PROPERTY(cx, obj2, prop); - - /* Ensure we found an arg or var property for the same function. */ - if ((sprop->flags & SPROP_IS_HIDDEN) && - (obj2 == funobj || - (JSFunction *) JS_GetPrivate(cx, obj2) == fp->fun)) { - if (getter == js_GetArgument) { - vp = fp->argv; - nslots = JS_MAX(fp->argc, fp->fun->nargs); - getter = setter = NULL; - } else { - JS_ASSERT(getter == js_GetLocalVariable); - vp = fp->vars; - nslots = fp->nvars; - getter = js_GetCallVariable; - setter = js_SetCallVariable; - } - if (slot < nslots) { - value = vp[slot]; - spflags = SPROP_HAS_SHORTID; - shortid = (intN) slot; - } else { - value = JSVAL_VOID; - spflags = 0; - shortid = 0; - } - if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value, - getter, setter, attrs, - spflags, shortid, NULL)) { - return JS_FALSE; - } - *objp = obj; - } - } - - return JS_TRUE; -} - -static JSBool -call_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) -{ - JSStackFrame *fp; - - if (type == JSTYPE_FUNCTION) { - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); - if (fp) { - JS_ASSERT(fp->fun); - *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object); - } - } - return JS_TRUE; -} - -JSClass js_CallClass = { - js_Call_str, - JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS | - JSCLASS_HAS_CACHED_PROTO(JSProto_Call), - JS_PropertyStub, JS_PropertyStub, - call_getProperty, call_setProperty, - call_enumerate, (JSResolveOp)call_resolve, - call_convert, JS_FinalizeStub, - NULL, NULL, - NULL, NULL, - NULL, NULL, - args_or_call_mark, NULL, -}; - -/* - * ECMA-262 specifies that length is a property of function object instances, - * but we can avoid that space cost by delegating to a prototype property that - * is JSPROP_PERMANENT and JSPROP_SHARED. Each fun_getProperty call computes - * a fresh length value based on the arity of the individual function object's - * private data. - * - * The extensions below other than length, i.e., the ones not in ECMA-262, - * are neither JSPROP_READONLY nor JSPROP_SHARED, because for compatibility - * with ECMA we must allow a delegating object to override them. - */ -#define LENGTH_PROP_ATTRS (JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED) - -static JSPropertySpec function_props[] = { - {js_arguments_str, CALL_ARGUMENTS, JSPROP_PERMANENT, 0,0}, - {js_arity_str, FUN_ARITY, JSPROP_PERMANENT, 0,0}, - {js_caller_str, FUN_CALLER, JSPROP_PERMANENT, 0,0}, - {js_length_str, ARGS_LENGTH, LENGTH_PROP_ATTRS, 0,0}, - {js_name_str, FUN_NAME, JSPROP_PERMANENT, 0,0}, - {0,0,0,0,0} -}; - -static JSBool -fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsint slot; - JSFunction *fun; - JSStackFrame *fp; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - slot = JSVAL_TO_INT(id); - - /* - * Loop because getter and setter can be delegated from another class, - * but loop only for ARGS_LENGTH because we must pretend that f.length - * is in each function instance f, per ECMA-262, instead of only in the - * Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED - * to make it appear so). - * - * This code couples tightly to the attributes for the function_props[] - * initializers above, and to js_SetProperty and js_HasOwnPropertyHelper. - * - * It's important to allow delegating objects, even though they inherit - * this getter (fun_getProperty), to override arguments, arity, caller, - * and name. If we didn't return early for slot != ARGS_LENGTH, we would - * clobber *vp with the native property value, instead of letting script - * override that value in delegating objects. - * - * Note how that clobbering is what simulates JSPROP_READONLY for all of - * the non-standard properties when the directly addressed object (obj) - * is a function object (i.e., when this loop does not iterate). - */ - while (!(fun = (JSFunction *) - JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL))) { - if (slot != ARGS_LENGTH) - return JS_TRUE; - obj = OBJ_GET_PROTO(cx, obj); - if (!obj) - return JS_TRUE; - } - - /* Find fun's top-most activation record. */ - for (fp = cx->fp; fp && (fp->fun != fun || (fp->flags & JSFRAME_SPECIAL)); - fp = fp->down) { - continue; - } - - switch (slot) { - case CALL_ARGUMENTS: - /* Warn if strict about f.arguments or equivalent unqualified uses. */ - if (!JS_ReportErrorFlagsAndNumber(cx, - JSREPORT_WARNING | JSREPORT_STRICT, - js_GetErrorMessage, NULL, - JSMSG_DEPRECATED_USAGE, - js_arguments_str)) { - return JS_FALSE; - } - if (fp) { - if (!js_GetArgsValue(cx, fp, vp)) - return JS_FALSE; - } else { - *vp = JSVAL_NULL; - } - break; - - case ARGS_LENGTH: - case FUN_ARITY: - *vp = INT_TO_JSVAL((jsint)fun->nargs); - break; - - case FUN_NAME: - *vp = fun->atom - ? ATOM_KEY(fun->atom) - : STRING_TO_JSVAL(cx->runtime->emptyString); - break; - - case FUN_CALLER: - while (fp && (fp->flags & JSFRAME_SKIP_CALLER) && fp->down) - fp = fp->down; - if (fp && fp->down && fp->down->fun && fp->down->argv) - *vp = fp->down->argv[-2]; - else - *vp = JSVAL_NULL; - if (!JSVAL_IS_PRIMITIVE(*vp) && cx->runtime->checkObjectAccess) { - id = ATOM_KEY(cx->runtime->atomState.callerAtom); - if (!cx->runtime->checkObjectAccess(cx, obj, id, JSACC_READ, vp)) - return JS_FALSE; - } - break; - - default: - /* XXX fun[0] and fun.arguments[0] are equivalent. */ - if (fp && fp->fun && (uintN)slot < fp->fun->nargs) - *vp = fp->argv[slot]; - break; - } - - return JS_TRUE; -} - -static JSBool -fun_enumerate(JSContext *cx, JSObject *obj) -{ - jsid prototypeId; - JSObject *pobj; - JSProperty *prop; - - prototypeId = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom); - if (!OBJ_LOOKUP_PROPERTY(cx, obj, prototypeId, &pobj, &prop)) - return JS_FALSE; - if (prop) - OBJ_DROP_PROPERTY(cx, pobj, prop); - return JS_TRUE; -} - -static JSBool -fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, - JSObject **objp) -{ - JSFunction *fun; - JSString *str; - JSAtom *prototypeAtom; - - /* - * No need to reflect fun.prototype in 'fun.prototype = ...' or in an - * unqualified reference to prototype, which the emitter looks up as a - * hidden atom when attempting to bind to a formal parameter or local - * variable slot. - */ - if (flags & (JSRESOLVE_ASSIGNING | JSRESOLVE_HIDDEN)) - return JS_TRUE; - - if (!JSVAL_IS_STRING(id)) - return JS_TRUE; - - /* No valid function object should lack private data, but check anyway. */ - fun = (JSFunction *)JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL); - if (!fun || !fun->object) - return JS_TRUE; - - /* - * Ok, check whether id is 'prototype' and bootstrap the function object's - * prototype property. - */ - str = JSVAL_TO_STRING(id); - prototypeAtom = cx->runtime->atomState.classPrototypeAtom; - if (str == ATOM_TO_STRING(prototypeAtom)) { - JSObject *proto, *parentProto; - jsval pval; - - proto = parentProto = NULL; - if (fun->object != obj && fun->object) { - /* - * Clone of a function: make its prototype property value have the - * same class as the clone-parent's prototype. - */ - if (!OBJ_GET_PROPERTY(cx, fun->object, ATOM_TO_JSID(prototypeAtom), - &pval)) { - return JS_FALSE; - } - if (!JSVAL_IS_PRIMITIVE(pval)) { - /* - * We are about to allocate a new object, so hack the newborn - * root until then to protect pval in case it is figuratively - * up in the air, with no strong refs protecting it. - */ - cx->weakRoots.newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(pval); - parentProto = JSVAL_TO_OBJECT(pval); - } - } - - /* - * Beware of the wacky case of a user function named Object -- trying - * to find a prototype for that will recur back here _ad perniciem_. - */ - if (!parentProto && fun->atom == CLASS_ATOM(cx, Object)) - return JS_TRUE; - - /* - * If resolving "prototype" in a clone, clone the parent's prototype. - * Pass the constructor's (obj's) parent as the prototype parent, to - * avoid defaulting to parentProto.constructor.__parent__. - */ - proto = js_NewObject(cx, &js_ObjectClass, parentProto, - OBJ_GET_PARENT(cx, obj)); - if (!proto) - return JS_FALSE; - - /* - * ECMA (15.3.5.2) says that constructor.prototype is DontDelete for - * user-defined functions, but DontEnum | ReadOnly | DontDelete for - * native "system" constructors such as Object or Function. So lazily - * set the former here in fun_resolve, but eagerly define the latter - * in JS_InitClass, with the right attributes. - */ - if (!js_SetClassPrototype(cx, obj, proto, - JSPROP_ENUMERATE | JSPROP_PERMANENT)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return JS_FALSE; - } - *objp = obj; - } - - return JS_TRUE; -} - -static JSBool -fun_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) -{ - switch (type) { - case JSTYPE_FUNCTION: - *vp = OBJECT_TO_JSVAL(obj); - return JS_TRUE; - default: - return js_TryValueOf(cx, obj, type, vp); - } -} - -static void -fun_finalize(JSContext *cx, JSObject *obj) -{ - JSFunction *fun; - JSScript *script; - - /* No valid function object should lack private data, but check anyway. */ - fun = (JSFunction *) JS_GetPrivate(cx, obj); - if (!fun) - return; - if (fun->object == obj) - fun->object = NULL; - - /* Null-check required since the parser sets interpreted very early. */ - if (FUN_INTERPRETED(fun) && fun->u.i.script && - js_IsAboutToBeFinalized(cx, fun)) - { - script = fun->u.i.script; - fun->u.i.script = NULL; - js_DestroyScript(cx, script); - } -} - -#if JS_HAS_XDR - -#include "jsxdrapi.h" - -enum { - JSXDR_FUNARG = 1, - JSXDR_FUNVAR = 2, - JSXDR_FUNCONST = 3 -}; - -/* XXX store parent and proto, if defined */ -static JSBool -fun_xdrObject(JSXDRState *xdr, JSObject **objp) -{ - JSContext *cx; - JSFunction *fun; - uint32 nullAtom; /* flag to indicate if fun->atom is NULL */ - JSTempValueRooter tvr; - uint32 flagsword; /* originally only flags was JS_XDRUint8'd */ - uint16 extraUnused; /* variable for no longer used field */ - JSAtom *propAtom; - JSScopeProperty *sprop; - uint32 userid; /* NB: holds a signed int-tagged jsval */ - uintN i, n, dupflag; - uint32 type; - JSBool ok; -#ifdef DEBUG - uintN nvars = 0, nargs = 0; -#endif - - cx = xdr->cx; - if (xdr->mode == JSXDR_ENCODE) { - /* - * No valid function object should lack private data, but fail soft - * (return true, no error report) in case one does due to API pilot - * or internal error. - */ - fun = (JSFunction *) JS_GetPrivate(cx, *objp); - if (!fun) - return JS_TRUE; - if (!FUN_INTERPRETED(fun)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_NOT_SCRIPTED_FUNCTION, - JS_GetFunctionName(fun)); - return JS_FALSE; - } - nullAtom = !fun->atom; - flagsword = ((uint32)fun->u.i.nregexps << 16) | fun->flags; - extraUnused = 0; - } else { - fun = js_NewFunction(cx, NULL, NULL, 0, 0, NULL, NULL); - if (!fun) - return JS_FALSE; - } - - /* From here on, control flow must flow through label out. */ - JS_PUSH_TEMP_ROOT_OBJECT(cx, fun->object, &tvr); - ok = JS_TRUE; - - if (!JS_XDRUint32(xdr, &nullAtom)) - goto bad; - if (!nullAtom && !js_XDRStringAtom(xdr, &fun->atom)) - goto bad; - - if (!JS_XDRUint16(xdr, &fun->nargs) || - !JS_XDRUint16(xdr, &extraUnused) || - !JS_XDRUint16(xdr, &fun->u.i.nvars) || - !JS_XDRUint32(xdr, &flagsword)) { - goto bad; - } - - /* Assert that all previous writes of extraUnused were writes of 0. */ - JS_ASSERT(extraUnused == 0); - - /* do arguments and local vars */ - if (fun->object) { - n = fun->nargs + fun->u.i.nvars; - if (xdr->mode == JSXDR_ENCODE) { - JSScope *scope; - JSScopeProperty **spvec, *auto_spvec[8]; - void *mark; - - if (n <= sizeof auto_spvec / sizeof auto_spvec[0]) { - spvec = auto_spvec; - mark = NULL; - } else { - mark = JS_ARENA_MARK(&cx->tempPool); - JS_ARENA_ALLOCATE_CAST(spvec, JSScopeProperty **, &cx->tempPool, - n * sizeof(JSScopeProperty *)); - if (!spvec) { - JS_ReportOutOfMemory(cx); - goto bad; - } - } - scope = OBJ_SCOPE(fun->object); - for (sprop = SCOPE_LAST_PROP(scope); sprop; - sprop = sprop->parent) { - if (sprop->getter == js_GetArgument) { - JS_ASSERT(nargs++ <= fun->nargs); - spvec[sprop->shortid] = sprop; - } else if (sprop->getter == js_GetLocalVariable) { - JS_ASSERT(nvars++ <= fun->u.i.nvars); - spvec[fun->nargs + sprop->shortid] = sprop; - } - } - for (i = 0; i < n; i++) { - sprop = spvec[i]; - JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); - type = (i < fun->nargs) - ? JSXDR_FUNARG - : (sprop->attrs & JSPROP_READONLY) - ? JSXDR_FUNCONST - : JSXDR_FUNVAR; - userid = INT_TO_JSVAL(sprop->shortid); - propAtom = JSID_TO_ATOM(sprop->id); - if (!JS_XDRUint32(xdr, &type) || - !JS_XDRUint32(xdr, &userid) || - !js_XDRCStringAtom(xdr, &propAtom)) { - if (mark) - JS_ARENA_RELEASE(&cx->tempPool, mark); - goto bad; - } - } - if (mark) - JS_ARENA_RELEASE(&cx->tempPool, mark); - } else { - JSPropertyOp getter, setter; - - for (i = n; i != 0; i--) { - uintN attrs = JSPROP_PERMANENT; - - if (!JS_XDRUint32(xdr, &type) || - !JS_XDRUint32(xdr, &userid) || - !js_XDRCStringAtom(xdr, &propAtom)) { - goto bad; - } - JS_ASSERT(type == JSXDR_FUNARG || type == JSXDR_FUNVAR || - type == JSXDR_FUNCONST); - if (type == JSXDR_FUNARG) { - getter = js_GetArgument; - setter = js_SetArgument; - JS_ASSERT(nargs++ <= fun->nargs); - } else if (type == JSXDR_FUNVAR || type == JSXDR_FUNCONST) { - getter = js_GetLocalVariable; - setter = js_SetLocalVariable; - if (type == JSXDR_FUNCONST) - attrs |= JSPROP_READONLY; - JS_ASSERT(nvars++ <= fun->u.i.nvars); - } else { - getter = NULL; - setter = NULL; - } - - /* Flag duplicate argument if atom is bound in fun->object. */ - dupflag = SCOPE_GET_PROPERTY(OBJ_SCOPE(fun->object), - ATOM_TO_JSID(propAtom)) - ? SPROP_IS_DUPLICATE - : 0; - - if (!js_AddHiddenProperty(cx, fun->object, - ATOM_TO_JSID(propAtom), - getter, setter, SPROP_INVALID_SLOT, - attrs | JSPROP_SHARED, - dupflag | SPROP_HAS_SHORTID, - JSVAL_TO_INT(userid))) { - goto bad; - } - } - } - } - - if (!js_XDRScript(xdr, &fun->u.i.script, NULL)) - goto bad; - - if (xdr->mode == JSXDR_DECODE) { - fun->flags = (uint16) flagsword | JSFUN_INTERPRETED; - fun->u.i.nregexps = (uint16) (flagsword >> 16); - - *objp = fun->object; - js_CallNewScriptHook(cx, fun->u.i.script, fun); - } - -out: - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; - -bad: - ok = JS_FALSE; - goto out; -} - -#else /* !JS_HAS_XDR */ - -#define fun_xdrObject NULL - -#endif /* !JS_HAS_XDR */ - -/* - * [[HasInstance]] internal method for Function objects: fetch the .prototype - * property of its 'this' parameter, and walks the prototype chain of v (only - * if v is an object) returning true if .prototype is found. - */ -static JSBool -fun_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - jsval pval; - JSString *str; - - if (!OBJ_GET_PROPERTY(cx, obj, - ATOM_TO_JSID(cx->runtime->atomState - .classPrototypeAtom), - &pval)) { - return JS_FALSE; - } - - if (JSVAL_IS_PRIMITIVE(pval)) { - /* - * Throw a runtime error if instanceof is called on a function that - * has a non-object as its .prototype value. - */ - str = js_DecompileValueGenerator(cx, -1, OBJECT_TO_JSVAL(obj), NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_PROTOTYPE, JS_GetStringBytes(str)); - } - return JS_FALSE; - } - - return js_IsDelegate(cx, JSVAL_TO_OBJECT(pval), v, bp); -} - -static uint32 -fun_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSFunction *fun; - - fun = (JSFunction *) JS_GetPrivate(cx, obj); - if (fun) { - GC_MARK(cx, fun, "private"); - if (fun->atom) - GC_MARK_ATOM(cx, fun->atom); - if (FUN_INTERPRETED(fun) && fun->u.i.script) - js_MarkScript(cx, fun->u.i.script); - } - return 0; -} - -static uint32 -fun_reserveSlots(JSContext *cx, JSObject *obj) -{ - JSFunction *fun; - - fun = (JSFunction *) JS_GetPrivate(cx, obj); - return (fun && FUN_INTERPRETED(fun)) ? fun->u.i.nregexps : 0; -} - -/* - * Reserve two slots in all function objects for XPConnect. Note that this - * does not bloat every instance, only those on which reserved slots are set, - * and those on which ad-hoc properties are defined. - */ -JS_FRIEND_DATA(JSClass) js_FunctionClass = { - js_Function_str, - JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(2) | - JSCLASS_HAS_CACHED_PROTO(JSProto_Function), - JS_PropertyStub, JS_PropertyStub, - fun_getProperty, JS_PropertyStub, - fun_enumerate, (JSResolveOp)fun_resolve, - fun_convert, fun_finalize, - NULL, NULL, - NULL, NULL, - fun_xdrObject, fun_hasInstance, - fun_mark, fun_reserveSlots -}; - -JSBool -js_fun_toString(JSContext *cx, JSObject *obj, uint32 indent, - uintN argc, jsval *argv, jsval *rval) -{ - jsval fval; - JSFunction *fun; - JSString *str; - - if (!argv) { - JS_ASSERT(JS_ObjectIsFunction(cx, obj)); - } else { - fval = argv[-1]; - if (!VALUE_IS_FUNCTION(cx, fval)) { - /* - * If we don't have a function to start off with, try converting - * the object to a function. If that doesn't work, complain. - */ - if (JSVAL_IS_OBJECT(fval)) { - obj = JSVAL_TO_OBJECT(fval); - if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, JSTYPE_FUNCTION, - &fval)) { - return JS_FALSE; - } - argv[-1] = fval; - } - if (!VALUE_IS_FUNCTION(cx, fval)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_INCOMPATIBLE_PROTO, - js_Function_str, js_toString_str, - JS_GetTypeName(cx, - JS_TypeOfValue(cx, fval))); - return JS_FALSE; - } - } - - obj = JSVAL_TO_OBJECT(fval); - } - - fun = (JSFunction *) JS_GetPrivate(cx, obj); - if (!fun) - return JS_TRUE; - if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) - return JS_FALSE; - str = JS_DecompileFunction(cx, fun, (uintN)indent); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -fun_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return js_fun_toString(cx, obj, 0, argc, argv, rval); -} - -#if JS_HAS_TOSOURCE -static JSBool -fun_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return js_fun_toString(cx, obj, JS_DONT_PRETTY_PRINT, argc, argv, rval); -} -#endif - -static const char call_str[] = "call"; - -static JSBool -fun_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval fval, *sp, *oldsp; - JSString *str; - void *mark; - uintN i; - JSStackFrame *fp; - JSBool ok; - - if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &argv[-1])) - return JS_FALSE; - fval = argv[-1]; - - if (!VALUE_IS_FUNCTION(cx, fval)) { - str = JS_ValueToString(cx, fval); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_INCOMPATIBLE_PROTO, - js_Function_str, call_str, - JS_GetStringBytes(str)); - } - return JS_FALSE; - } - - if (argc == 0) { - /* Call fun with its global object as the 'this' param if no args. */ - obj = NULL; - } else { - /* Otherwise convert the first arg to 'this' and skip over it. */ - if (!js_ValueToObject(cx, argv[0], &obj)) - return JS_FALSE; - argc--; - argv++; - } - - /* Allocate stack space for fval, obj, and the args. */ - sp = js_AllocStack(cx, 2 + argc, &mark); - if (!sp) - return JS_FALSE; - - /* Push fval, obj, and the args. */ - *sp++ = fval; - *sp++ = OBJECT_TO_JSVAL(obj); - for (i = 0; i < argc; i++) - *sp++ = argv[i]; - - /* Lift current frame to include the args and do the call. */ - fp = cx->fp; - oldsp = fp->sp; - fp->sp = sp; - ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL | JSINVOKE_SKIP_CALLER); - - /* Store rval and pop stack back to our frame's sp. */ - *rval = fp->sp[-1]; - fp->sp = oldsp; - js_FreeStack(cx, mark); - return ok; -} - -static JSBool -fun_apply(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval fval, *sp, *oldsp; - JSString *str; - JSObject *aobj; - jsuint length; - JSBool arraylike, ok; - void *mark; - uintN i; - JSStackFrame *fp; - - if (argc == 0) { - /* Will get globalObject as 'this' and no other arguments. */ - return fun_call(cx, obj, argc, argv, rval); - } - - if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &argv[-1])) - return JS_FALSE; - fval = argv[-1]; - - if (!VALUE_IS_FUNCTION(cx, fval)) { - str = JS_ValueToString(cx, fval); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_INCOMPATIBLE_PROTO, - js_Function_str, "apply", - JS_GetStringBytes(str)); - } - return JS_FALSE; - } - - /* Quell GCC overwarnings. */ - aobj = NULL; - length = 0; - - if (argc >= 2) { - /* If the 2nd arg is null or void, call the function with 0 args. */ - if (JSVAL_IS_NULL(argv[1]) || JSVAL_IS_VOID(argv[1])) { - argc = 0; - } else { - /* The second arg must be an array (or arguments object). */ - arraylike = JS_FALSE; - if (!JSVAL_IS_PRIMITIVE(argv[1])) { - aobj = JSVAL_TO_OBJECT(argv[1]); - if (!js_IsArrayLike(cx, aobj, &arraylike, &length)) - return JS_FALSE; - } - if (!arraylike) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_APPLY_ARGS, "apply"); - return JS_FALSE; - } - } - } - - /* Convert the first arg to 'this' and skip over it. */ - if (!js_ValueToObject(cx, argv[0], &obj)) - return JS_FALSE; - - /* Allocate stack space for fval, obj, and the args. */ - argc = (uintN)JS_MIN(length, ARRAY_INIT_LIMIT - 1); - sp = js_AllocStack(cx, 2 + argc, &mark); - if (!sp) - return JS_FALSE; - - /* Push fval, obj, and aobj's elements as args. */ - *sp++ = fval; - *sp++ = OBJECT_TO_JSVAL(obj); - for (i = 0; i < argc; i++) { - ok = JS_GetElement(cx, aobj, (jsint)i, sp); - if (!ok) - goto out; - sp++; - } - - /* Lift current frame to include the args and do the call. */ - fp = cx->fp; - oldsp = fp->sp; - fp->sp = sp; - ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL | JSINVOKE_SKIP_CALLER); - - /* Store rval and pop stack back to our frame's sp. */ - *rval = fp->sp[-1]; - fp->sp = oldsp; -out: - js_FreeStack(cx, mark); - return ok; -} - -#ifdef NARCISSUS -static JSBool -fun_applyConstructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSObject *aobj; - uintN length, i; - void *mark; - jsval *sp, *newsp, *oldsp; - JSStackFrame *fp; - JSBool ok; - - if (JSVAL_IS_PRIMITIVE(argv[0]) || - (aobj = JSVAL_TO_OBJECT(argv[0]), - OBJ_GET_CLASS(cx, aobj) != &js_ArrayClass && - OBJ_GET_CLASS(cx, aobj) != &js_ArgumentsClass)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_APPLY_ARGS, "__applyConstruct__"); - return JS_FALSE; - } - - if (!js_GetLengthProperty(cx, aobj, &length)) - return JS_FALSE; - - if (length >= ARRAY_INIT_LIMIT) - length = ARRAY_INIT_LIMIT - 1; - newsp = sp = js_AllocStack(cx, 2 + length, &mark); - if (!sp) - return JS_FALSE; - - fp = cx->fp; - oldsp = fp->sp; - *sp++ = OBJECT_TO_JSVAL(obj); - *sp++ = JSVAL_NULL; /* This is filled automagically. */ - for (i = 0; i < length; i++) { - ok = JS_GetElement(cx, aobj, (jsint)i, sp); - if (!ok) - goto out; - sp++; - } - - oldsp = fp->sp; - fp->sp = sp; - ok = js_InvokeConstructor(cx, newsp, length); - - *rval = fp->sp[-1]; - fp->sp = oldsp; -out: - js_FreeStack(cx, mark); - return ok; -} -#endif - -static JSFunctionSpec function_methods[] = { -#if JS_HAS_TOSOURCE - {js_toSource_str, fun_toSource, 0,0,0}, -#endif - {js_toString_str, fun_toString, 1,0,0}, - {"apply", fun_apply, 2,0,0}, - {call_str, fun_call, 1,0,0}, -#ifdef NARCISSUS - {"__applyConstructor__", fun_applyConstructor, 1,0,0}, -#endif - {0,0,0,0,0} -}; - -JSBool -js_IsIdentifier(JSString *str) -{ - size_t length; - jschar c, *chars, *end, *s; - - length = JSSTRING_LENGTH(str); - if (length == 0) - return JS_FALSE; - chars = JSSTRING_CHARS(str); - c = *chars; - if (!JS_ISIDSTART(c)) - return JS_FALSE; - end = chars + length; - for (s = chars + 1; s != end; ++s) { - c = *s; - if (!JS_ISIDENT(c)) - return JS_FALSE; - } - return !js_IsKeyword(chars, length); -} - -static JSBool -Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSStackFrame *fp, *caller; - JSFunction *fun; - JSObject *parent; - uintN i, n, lineno, dupflag; - JSAtom *atom; - const char *filename; - JSObject *obj2; - JSProperty *prop; - JSScopeProperty *sprop; - JSString *str, *arg; - void *mark; - JSTokenStream *ts; - JSPrincipals *principals; - jschar *collected_args, *cp; - size_t arg_length, args_length, old_args_length; - JSTokenType tt; - JSBool ok; - - fp = cx->fp; - if (!(fp->flags & JSFRAME_CONSTRUCTING)) { - obj = js_NewObject(cx, &js_FunctionClass, NULL, NULL); - if (!obj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - } - fun = (JSFunction *) JS_GetPrivate(cx, obj); - if (fun) - return JS_TRUE; - - /* - * NB: (new Function) is not lexically closed by its caller, it's just an - * anonymous function in the top-level scope that its constructor inhabits. - * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42, - * and so would a call to f from another top-level's script or function. - * - * In older versions, before call objects, a new Function was adopted by - * its running context's globalObject, which might be different from the - * top-level reachable from scopeChain (in HTML frames, e.g.). - */ - parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2])); - - fun = js_NewFunction(cx, obj, NULL, 0, JSFUN_LAMBDA, parent, - cx->runtime->atomState.anonymousAtom); - - if (!fun) - return JS_FALSE; - - /* - * Function is static and not called directly by other functions in this - * file, therefore it is callable only as a native function by js_Invoke. - * Find the scripted caller, possibly skipping other native frames such as - * are built for Function.prototype.call or .apply activations that invoke - * Function indirectly from a script. - */ - JS_ASSERT(!fp->script && fp->fun && fp->fun->u.n.native == Function); - caller = JS_GetScriptedCaller(cx, fp); - if (caller) { - filename = caller->script->filename; - lineno = js_PCToLineNumber(cx, caller->script, caller->pc); - principals = JS_EvalFramePrincipals(cx, fp, caller); - } else { - filename = NULL; - lineno = 0; - principals = NULL; - } - - /* Belt-and-braces: check that the caller has access to parent. */ - if (!js_CheckPrincipalsAccess(cx, parent, principals, - CLASS_ATOM(cx, Function))) { - return JS_FALSE; - } - - n = argc ? argc - 1 : 0; - if (n > 0) { - /* - * Collect the function-argument arguments into one string, separated - * by commas, then make a tokenstream from that string, and scan it to - * get the arguments. We need to throw the full scanner at the - * problem, because the argument string can legitimately contain - * comments and linefeeds. XXX It might be better to concatenate - * everything up into a function definition and pass it to the - * compiler, but doing it this way is less of a delta from the old - * code. See ECMA 15.3.2.1. - */ - args_length = 0; - for (i = 0; i < n; i++) { - /* Collect the lengths for all the function-argument arguments. */ - arg = js_ValueToString(cx, argv[i]); - if (!arg) - return JS_FALSE; - argv[i] = STRING_TO_JSVAL(arg); - - /* - * Check for overflow. The < test works because the maximum - * JSString length fits in 2 fewer bits than size_t has. - */ - old_args_length = args_length; - args_length = old_args_length + JSSTRING_LENGTH(arg); - if (args_length < old_args_length) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - } - - /* Add 1 for each joining comma and check for overflow (two ways). */ - old_args_length = args_length; - args_length = old_args_length + n - 1; - if (args_length < old_args_length || - args_length >= ~(size_t)0 / sizeof(jschar)) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - /* - * Allocate a string to hold the concatenated arguments, including room - * for a terminating 0. Mark cx->tempPool for later release, to free - * collected_args and its tokenstream in one swoop. - */ - mark = JS_ARENA_MARK(&cx->tempPool); - JS_ARENA_ALLOCATE_CAST(cp, jschar *, &cx->tempPool, - (args_length+1) * sizeof(jschar)); - if (!cp) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - collected_args = cp; - - /* - * Concatenate the arguments into the new string, separated by commas. - */ - for (i = 0; i < n; i++) { - arg = JSVAL_TO_STRING(argv[i]); - arg_length = JSSTRING_LENGTH(arg); - (void) js_strncpy(cp, JSSTRING_CHARS(arg), arg_length); - cp += arg_length; - - /* Add separating comma or terminating 0. */ - *cp++ = (i + 1 < n) ? ',' : 0; - } - - /* - * Make a tokenstream (allocated from cx->tempPool) that reads from - * the given string. - */ - ts = js_NewTokenStream(cx, collected_args, args_length, filename, - lineno, principals); - if (!ts) { - JS_ARENA_RELEASE(&cx->tempPool, mark); - return JS_FALSE; - } - - /* The argument string may be empty or contain no tokens. */ - tt = js_GetToken(cx, ts); - if (tt != TOK_EOF) { - for (;;) { - /* - * Check that it's a name. This also implicitly guards against - * TOK_ERROR, which was already reported. - */ - if (tt != TOK_NAME) - goto bad_formal; - - /* - * Get the atom corresponding to the name from the tokenstream; - * we're assured at this point that it's a valid identifier. - */ - atom = CURRENT_TOKEN(ts).t_atom; - if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), - &obj2, &prop)) { - goto bad_formal; - } - sprop = (JSScopeProperty *) prop; - dupflag = 0; - if (sprop) { - ok = JS_TRUE; - if (obj2 == obj) { - const char *name = js_AtomToPrintableString(cx, atom); - - /* - * A duplicate parameter name. We force a duplicate - * node on the SCOPE_LAST_PROP(scope) list with the - * same id, distinguished by the SPROP_IS_DUPLICATE - * flag, and not mapped by an entry in scope. - */ - JS_ASSERT(sprop->getter == js_GetArgument); - ok = name && - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | - JSREPORT_WARNING | - JSREPORT_STRICT, - JSMSG_DUPLICATE_FORMAL, - name); - - dupflag = SPROP_IS_DUPLICATE; - } - OBJ_DROP_PROPERTY(cx, obj2, prop); - if (!ok) - goto bad_formal; - sprop = NULL; - } - if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(atom), - js_GetArgument, js_SetArgument, - SPROP_INVALID_SLOT, - JSPROP_PERMANENT | JSPROP_SHARED, - dupflag | SPROP_HAS_SHORTID, - fun->nargs)) { - goto bad_formal; - } - if (fun->nargs == JS_BITMASK(16)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_TOO_MANY_FUN_ARGS); - goto bad; - } - fun->nargs++; - - /* - * Get the next token. Stop on end of stream. Otherwise - * insist on a comma, get another name, and iterate. - */ - tt = js_GetToken(cx, ts); - if (tt == TOK_EOF) - break; - if (tt != TOK_COMMA) - goto bad_formal; - tt = js_GetToken(cx, ts); - } - } - - /* Clean up. */ - ok = js_CloseTokenStream(cx, ts); - JS_ARENA_RELEASE(&cx->tempPool, mark); - if (!ok) - return JS_FALSE; - } - - if (argc) { - str = js_ValueToString(cx, argv[argc-1]); - } else { - /* Can't use cx->runtime->emptyString because we're called too early. */ - str = js_NewStringCopyZ(cx, js_empty_ucstr, 0); - } - if (!str) - return JS_FALSE; - if (argv) { - /* Use the last arg (or this if argc == 0) as a local GC root. */ - argv[(intN)(argc-1)] = STRING_TO_JSVAL(str); - } - - mark = JS_ARENA_MARK(&cx->tempPool); - ts = js_NewTokenStream(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str), - filename, lineno, principals); - if (!ts) { - ok = JS_FALSE; - } else { - ok = js_CompileFunctionBody(cx, ts, fun) && - js_CloseTokenStream(cx, ts); - } - JS_ARENA_RELEASE(&cx->tempPool, mark); - return ok; - -bad_formal: - /* - * Report "malformed formal parameter" iff no illegal char or similar - * scanner error was already reported. - */ - if (!(ts->flags & TSF_ERROR)) - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_FORMAL); - -bad: - /* - * Clean up the arguments string and tokenstream if we failed to parse - * the arguments. - */ - (void)js_CloseTokenStream(cx, ts); - JS_ARENA_RELEASE(&cx->tempPool, mark); - return JS_FALSE; -} - -JSObject * -js_InitFunctionClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto; - JSAtom *atom; - JSFunction *fun; - - proto = JS_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1, - function_props, function_methods, NULL, NULL); - if (!proto) - return NULL; - atom = js_Atomize(cx, js_FunctionClass.name, strlen(js_FunctionClass.name), - 0); - if (!atom) - goto bad; - fun = js_NewFunction(cx, proto, NULL, 0, 0, obj, NULL); - if (!fun) - goto bad; - fun->u.i.script = js_NewScript(cx, 1, 0, 0); - if (!fun->u.i.script) - goto bad; - fun->u.i.script->code[0] = JSOP_STOP; - fun->flags |= JSFUN_INTERPRETED; - return proto; - -bad: - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; -} - -JSObject * -js_InitCallClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto; - - proto = JS_InitClass(cx, obj, NULL, &js_CallClass, NULL, 0, - call_props, NULL, NULL, NULL); - if (!proto) - return NULL; - - /* - * Null Call.prototype's proto slot so that Object.prototype.* does not - * pollute the scope of heavyweight functions. - */ - OBJ_SET_PROTO(cx, proto, NULL); - return proto; -} - -JSFunction * -js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs, - uintN flags, JSObject *parent, JSAtom *atom) -{ - JSFunction *fun; - JSTempValueRooter tvr; - - /* If funobj is null, allocate an object for it. */ - if (funobj) { - OBJ_SET_PARENT(cx, funobj, parent); - } else { - funobj = js_NewObject(cx, &js_FunctionClass, NULL, parent); - if (!funobj) - return NULL; - } - - /* Protect fun from any potential GC callback. */ - JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(funobj), &tvr); - - /* - * Allocate fun after allocating funobj so slot allocation in js_NewObject - * does not wipe out fun from newborn[GCX_PRIVATE]. - */ - fun = (JSFunction *) js_NewGCThing(cx, GCX_PRIVATE, sizeof(JSFunction)); - if (!fun) - goto out; - - /* Initialize all function members. */ - fun->object = NULL; - fun->nargs = nargs; - fun->flags = flags & JSFUN_FLAGS_MASK; - fun->u.n.native = native; - fun->u.n.extra = 0; - fun->u.n.spare = 0; - fun->atom = atom; - fun->clasp = NULL; - - /* Link fun to funobj and vice versa. */ - if (!js_LinkFunctionObject(cx, fun, funobj)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - fun = NULL; - } - -out: - JS_POP_TEMP_ROOT(cx, &tvr); - return fun; -} - -JSObject * -js_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) -{ - JSObject *newfunobj; - JSFunction *fun; - - JS_ASSERT(OBJ_GET_CLASS(cx, funobj) == &js_FunctionClass); - newfunobj = js_NewObject(cx, &js_FunctionClass, funobj, parent); - if (!newfunobj) - return NULL; - fun = (JSFunction *) JS_GetPrivate(cx, funobj); - if (!js_LinkFunctionObject(cx, fun, newfunobj)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; - } - return newfunobj; -} - -JSBool -js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *funobj) -{ - if (!fun->object) - fun->object = funobj; - return JS_SetPrivate(cx, funobj, fun); -} - -JSFunction * -js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native, - uintN nargs, uintN attrs) -{ - JSFunction *fun; - - fun = js_NewFunction(cx, NULL, native, nargs, attrs, obj, atom); - if (!fun) - return NULL; - if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), - OBJECT_TO_JSVAL(fun->object), - NULL, NULL, - attrs & ~JSFUN_FLAGS_MASK, NULL)) { - return NULL; - } - return fun; -} - -#if (JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK) -# error "JSINVOKE_CONSTRUCT and JSV2F_SEARCH_STACK are not disjoint!" -#endif - -JSFunction * -js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags) -{ - jsval v; - JSObject *obj; - - v = *vp; - obj = NULL; - if (JSVAL_IS_OBJECT(v)) { - obj = JSVAL_TO_OBJECT(v); - if (obj && OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) { - if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &v)) - return NULL; - obj = VALUE_IS_FUNCTION(cx, v) ? JSVAL_TO_OBJECT(v) : NULL; - } - } - if (!obj) { - js_ReportIsNotFunction(cx, vp, flags); - return NULL; - } - return (JSFunction *) JS_GetPrivate(cx, obj); -} - -JSObject * -js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags) -{ - JSFunction *fun; - JSObject *funobj; - JSStackFrame *caller; - JSPrincipals *principals; - - if (VALUE_IS_FUNCTION(cx, *vp)) - return JSVAL_TO_OBJECT(*vp); - - fun = js_ValueToFunction(cx, vp, flags); - if (!fun) - return NULL; - funobj = fun->object; - *vp = OBJECT_TO_JSVAL(funobj); - - caller = JS_GetScriptedCaller(cx, cx->fp); - if (caller) { - principals = caller->script->principals; - } else { - /* No scripted caller, don't allow access. */ - principals = NULL; - } - - if (!js_CheckPrincipalsAccess(cx, funobj, principals, - fun->atom - ? fun->atom - : cx->runtime->atomState.anonymousAtom)) { - return NULL; - } - return funobj; -} - -JSObject * -js_ValueToCallableObject(JSContext *cx, jsval *vp, uintN flags) -{ - JSObject *callable; - - callable = JSVAL_IS_PRIMITIVE(*vp) ? NULL : JSVAL_TO_OBJECT(*vp); - if (callable && - ((callable->map->ops == &js_ObjectOps) - ? OBJ_GET_CLASS(cx, callable)->call - : callable->map->ops->call)) { - *vp = OBJECT_TO_JSVAL(callable); - } else { - callable = js_ValueToFunctionObject(cx, vp, flags); - } - return callable; -} - -void -js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags) -{ - JSStackFrame *fp; - JSString *str; - JSTempValueRooter tvr; - const char *bytes, *source; - - for (fp = cx->fp; fp && !fp->spbase; fp = fp->down) - continue; - str = js_DecompileValueGenerator(cx, - (fp && fp->spbase <= vp && vp < fp->sp) - ? vp - fp->sp - : (flags & JSV2F_SEARCH_STACK) - ? JSDVG_SEARCH_STACK - : JSDVG_IGNORE_STACK, - *vp, - NULL); - if (str) { - JS_PUSH_TEMP_ROOT_STRING(cx, str, &tvr); - bytes = JS_GetStringBytes(str); - if (flags & JSV2F_ITERATOR) { - source = js_ValueToPrintableSource(cx, *vp); - if (source) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_ITERATOR, - bytes, js_iterator_str, source); - } - } else { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - (uintN)((flags & JSV2F_CONSTRUCT) - ? JSMSG_NOT_CONSTRUCTOR - : JSMSG_NOT_FUNCTION), - bytes); - } - JS_POP_TEMP_ROOT(cx, &tvr); - } -} diff --git a/spidermonkey/src/jsfun.h b/spidermonkey/src/jsfun.h deleted file mode 100644 index 8d5c185..0000000 --- a/spidermonkey/src/jsfun.h +++ /dev/null @@ -1,170 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsfun_h___ -#define jsfun_h___ -/* - * JS function definitions. - */ -#include "jsprvtd.h" -#include "jspubtd.h" - -JS_BEGIN_EXTERN_C - -struct JSFunction { - JSObject *object; /* back-pointer to GC'ed object header */ - uint16 nargs; /* minimum number of actual arguments */ - uint16 flags; /* bound method and other flags, see jsapi.h */ - union { - struct { - uint16 extra; /* number of arg slots for local GC roots */ - uint16 spare; /* reserved for future use */ - JSNative native; /* native method pointer or null */ - } n; - struct { - uint16 nvars; /* number of local variables */ - uint16 nregexps; /* number of regular expressions literals */ - JSScript *script; /* interpreted bytecode descriptor or null */ - } i; - } u; - JSAtom *atom; /* name for diagnostics and decompiling */ - JSClass *clasp; /* if non-null, constructor for this class */ -}; - -#define JSFUN_INTERPRETED 0x8000 /* use u.i if set, u.n if unset */ - -#define FUN_INTERPRETED(fun) ((fun)->flags & JSFUN_INTERPRETED) -#define FUN_NATIVE(fun) (FUN_INTERPRETED(fun) ? NULL : (fun)->u.n.native) -#define FUN_SCRIPT(fun) (FUN_INTERPRETED(fun) ? (fun)->u.i.script : NULL) - -extern JSClass js_ArgumentsClass; -extern JSClass js_CallClass; - -/* JS_FRIEND_DATA so that VALUE_IS_FUNCTION is callable from the shell. */ -extern JS_FRIEND_DATA(JSClass) js_FunctionClass; - -/* - * NB: jsapi.h and jsobj.h must be included before any call to this macro. - */ -#define VALUE_IS_FUNCTION(cx, v) \ - (!JSVAL_IS_PRIMITIVE(v) && \ - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_FunctionClass) - -extern JSBool -js_fun_toString(JSContext *cx, JSObject *obj, uint32 indent, - uintN argc, jsval *argv, jsval *rval); - -extern JSBool -js_IsIdentifier(JSString *str); - -extern JSObject * -js_InitFunctionClass(JSContext *cx, JSObject *obj); - -extern JSObject * -js_InitArgumentsClass(JSContext *cx, JSObject *obj); - -extern JSObject * -js_InitCallClass(JSContext *cx, JSObject *obj); - -extern JSFunction * -js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs, - uintN flags, JSObject *parent, JSAtom *atom); - -extern JSObject * -js_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent); - -extern JSBool -js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *object); - -extern JSFunction * -js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native, - uintN nargs, uintN flags); - -/* - * Flags for js_ValueToFunction and js_ReportIsNotFunction. We depend on the - * fact that JSINVOKE_CONSTRUCT (aka JSFRAME_CONSTRUCTING) is 1, and test that - * with #if/#error in jsfun.c. - */ -#define JSV2F_CONSTRUCT JSINVOKE_CONSTRUCT -#define JSV2F_ITERATOR JSINVOKE_ITERATOR -#define JSV2F_SEARCH_STACK 0x10000 - -extern JSFunction * -js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags); - -extern JSObject * -js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags); - -extern JSObject * -js_ValueToCallableObject(JSContext *cx, jsval *vp, uintN flags); - -extern void -js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags); - -extern JSObject * -js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent); - -extern JSBool -js_PutCallObject(JSContext *cx, JSStackFrame *fp); - -extern JSBool -js_GetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -extern JSBool -js_SetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -extern JSBool -js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp); - -extern JSBool -js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, - JSObject **objp, jsval *vp); - -extern JSObject * -js_GetArgsObject(JSContext *cx, JSStackFrame *fp); - -extern JSBool -js_PutArgsObject(JSContext *cx, JSStackFrame *fp); - -extern JSBool -js_XDRFunction(JSXDRState *xdr, JSObject **objp); - -JS_END_EXTERN_C - -#endif /* jsfun_h___ */ diff --git a/spidermonkey/src/jsgc.c b/spidermonkey/src/jsgc.c deleted file mode 100644 index 7fae096..0000000 --- a/spidermonkey/src/jsgc.c +++ /dev/null @@ -1,3201 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS Mark-and-Sweep Garbage Collector. - * - * This GC allocates fixed-sized things with sizes up to GC_NBYTES_MAX (see - * jsgc.h). It allocates from a special GC arena pool with each arena allocated - * using malloc. It uses an ideally parallel array of flag bytes to hold the - * mark bit, finalizer type index, etc. - * - * XXX swizzle page to freelist for better locality of reference - */ -#include "jsstddef.h" -#include /* for free */ -#include /* for memset used when DEBUG */ -#include "jstypes.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jshash.h" /* Added by JSIFY */ -#include "jsapi.h" -#include "jsatom.h" -#include "jsbit.h" -#include "jsclist.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsdbgapi.h" -#include "jsexn.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jsiter.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" - -#if JS_HAS_XML_SUPPORT -#include "jsxml.h" -#endif - -/* - * GC arena sizing depends on amortizing arena overhead using a large number - * of things per arena, and on the thing/flags ratio of 8:1 on most platforms. - * - * On 64-bit platforms, we would have half as many things per arena because - * pointers are twice as big, so we double the bytes for things per arena. - * This preserves the 1024 byte flags sub-arena size, which relates to the - * GC_PAGE_SIZE (see below for why). - */ -#if JS_BYTES_PER_WORD == 8 -# define GC_THINGS_SHIFT 14 /* 16KB for things on Alpha, etc. */ -#else -# define GC_THINGS_SHIFT 13 /* 8KB for things on most platforms */ -#endif -#define GC_THINGS_SIZE JS_BIT(GC_THINGS_SHIFT) -#define GC_FLAGS_SIZE (GC_THINGS_SIZE / sizeof(JSGCThing)) - -/* - * A GC arena contains one flag byte for each thing in its heap, and supports - * O(1) lookup of a flag given its thing's address. - * - * To implement this, we take advantage of the thing/flags numerology: given - * the 8K bytes worth of GC-things, there are 1K flag bytes. Within each 9K - * allocation for things+flags there are always 8 consecutive 1K-pages each - * aligned on 1K boundary. We use these pages to allocate things and the - * remaining 1K of space before and after the aligned pages to store flags. - * If we are really lucky and things+flags starts on a 1K boundary, then - * flags would consist of a single 1K chunk that comes after 8K of things. - * Otherwise there are 2 chunks of flags, one before and one after things. - * - * To be able to find the flag byte for a particular thing, we put a - * JSGCPageInfo record at the beginning of each 1K-aligned page to hold that - * page's offset from the beginning of things+flags allocation and we allocate - * things after this record. Thus for each thing |thing_address & ~1023| - * gives the address of a JSGCPageInfo record from which we read page_offset. - * Due to page alignment - * (page_offset & ~1023) + (thing_address & 1023) - * gives thing_offset from the beginning of 8K paged things. We then divide - * thing_offset by sizeof(JSGCThing) to get thing_index. - * - * Now |page_address - page_offset| is things+flags arena_address and - * (page_offset & 1023) is the offset of the first page from the start of - * things+flags area. Thus if - * thing_index < (page_offset & 1023) - * then - * allocation_start_address + thing_index < address_of_the_first_page - * and we use - * allocation_start_address + thing_index - * as the address to store thing's flags. If - * thing_index >= (page_offset & 1023), - * then we use the chunk of flags that comes after the pages with things - * and calculate the address for the flag byte as - * address_of_the_first_page + 8K + (thing_index - (page_offset & 1023)) - * which is just - * allocation_start_address + thing_index + 8K. - * - * When we allocate things with size equal to sizeof(JSGCThing), the overhead - * of this scheme for 32 bit platforms is (8+8*(8+1))/(8+9K) or 0.87% - * (assuming 4 bytes for each JSGCArena header, and 8 bytes for each - * JSGCThing and JSGCPageInfo). When thing_size > 8, the scheme wastes the - * flag byte for each extra 8 bytes beyond sizeof(JSGCThing) in thing_size - * and the overhead is close to 1/8 or 12.5%. - * FIXME: How can we avoid this overhead? - * - * Here's some ASCII art showing an arena: - * - * split or the first 1-K aligned address. - * | - * V - * +--+-------+-------+-------+-------+-------+-------+-------+-------+-----+ - * |fB| tp0 | tp1 | tp2 | tp3 | tp4 | tp5 | tp6 | tp7 | fA | - * +--+-------+-------+-------+-------+-------+-------+-------+-------+-----+ - * ^ ^ - * tI ---------+ | - * tJ -------------------------------------------+ - * - * - fB are the "before split" flags, fA are the "after split" flags - * - tp0-tp7 are the 8 thing pages - * - thing tI points into tp1, whose flags are below the split, in fB - * - thing tJ points into tp5, clearly above the split - * - * In general, one of the thing pages will have some of its things' flags on - * the low side of the split, and the rest of its things' flags on the high - * side. All the other pages have flags only below or only above. - * - * (If we need to implement card-marking for an incremental GC write barrier, - * we can replace word-sized offsetInArena in JSGCPageInfo by pair of - * uint8 card_mark and uint16 offsetInArena fields as the offset can not exceed - * GC_THINGS_SIZE. This would gives an extremely efficient write barrier: - * when mutating an object obj, just store a 1 byte at - * (uint8 *) ((jsuword)obj & ~1023) on 32-bit platforms.) - */ -#define GC_PAGE_SHIFT 10 -#define GC_PAGE_MASK ((jsuword) JS_BITMASK(GC_PAGE_SHIFT)) -#define GC_PAGE_SIZE JS_BIT(GC_PAGE_SHIFT) -#define GC_PAGE_COUNT (1 << (GC_THINGS_SHIFT - GC_PAGE_SHIFT)) - -typedef struct JSGCPageInfo { - jsuword offsetInArena; /* offset from the arena start */ - jsuword unscannedBitmap; /* bitset for fast search of marked - but not yet scanned GC things */ -} JSGCPageInfo; - -struct JSGCArena { - JSGCArenaList *list; /* allocation list for the arena */ - JSGCArena *prev; /* link field for allocation list */ - JSGCArena *prevUnscanned; /* link field for the list of arenas - with marked but not yet scanned - things */ - jsuword unscannedPages; /* bitset for fast search of pages - with marked but not yet scanned - things */ - uint8 base[1]; /* things+flags allocation area */ -}; - -#define GC_ARENA_SIZE \ - (offsetof(JSGCArena, base) + GC_THINGS_SIZE + GC_FLAGS_SIZE) - -#define FIRST_THING_PAGE(a) \ - (((jsuword)(a)->base + GC_FLAGS_SIZE - 1) & ~GC_PAGE_MASK) - -#define PAGE_TO_ARENA(pi) \ - ((JSGCArena *)((jsuword)(pi) - (pi)->offsetInArena \ - - offsetof(JSGCArena, base))) - -#define PAGE_INDEX(pi) \ - ((size_t)((pi)->offsetInArena >> GC_PAGE_SHIFT)) - -#define THING_TO_PAGE(thing) \ - ((JSGCPageInfo *)((jsuword)(thing) & ~GC_PAGE_MASK)) - -/* - * Given a thing size n, return the size of the gap from the page start before - * the first thing. We know that any n not a power of two packs from - * the end of the page leaving at least enough room for one JSGCPageInfo, but - * not for another thing, at the front of the page (JS_ASSERTs below insist - * on this). - * - * This works because all allocations are a multiple of sizeof(JSGCThing) == - * sizeof(JSGCPageInfo) in size. - */ -#define PAGE_THING_GAP(n) (((n) & ((n) - 1)) ? (GC_PAGE_SIZE % (n)) : (n)) - -#ifdef JS_THREADSAFE -/* - * The maximum number of things to put to the local free list by taking - * several things from the global free list or from the tail of the last - * allocated arena to amortize the cost of rt->gcLock. - * - * We use number 8 based on benchmarks from bug 312238. - */ -#define MAX_THREAD_LOCAL_THINGS 8 - -#endif - -JS_STATIC_ASSERT(sizeof(JSGCThing) == sizeof(JSGCPageInfo)); -JS_STATIC_ASSERT(sizeof(JSGCThing) >= sizeof(JSObject)); -JS_STATIC_ASSERT(sizeof(JSGCThing) >= sizeof(JSString)); -JS_STATIC_ASSERT(sizeof(JSGCThing) >= sizeof(jsdouble)); -JS_STATIC_ASSERT(GC_FLAGS_SIZE >= GC_PAGE_SIZE); -JS_STATIC_ASSERT(sizeof(JSStackHeader) >= 2 * sizeof(jsval)); - -/* - * JSPtrTable capacity growth descriptor. The table grows by powers of two - * starting from capacity JSPtrTableInfo.minCapacity, but switching to linear - * growth when capacity reaches JSPtrTableInfo.linearGrowthThreshold. - */ -typedef struct JSPtrTableInfo { - uint16 minCapacity; - uint16 linearGrowthThreshold; -} JSPtrTableInfo; - -#define GC_ITERATOR_TABLE_MIN 4 -#define GC_ITERATOR_TABLE_LINEAR 1024 - -static const JSPtrTableInfo iteratorTableInfo = { - GC_ITERATOR_TABLE_MIN, - GC_ITERATOR_TABLE_LINEAR -}; - -/* Calculate table capacity based on the current value of JSPtrTable.count. */ -static size_t -PtrTableCapacity(size_t count, const JSPtrTableInfo *info) -{ - size_t linear, log, capacity; - - linear = info->linearGrowthThreshold; - JS_ASSERT(info->minCapacity <= linear); - - if (count == 0) { - capacity = 0; - } else if (count < linear) { - log = JS_CEILING_LOG2W(count); - JS_ASSERT(log != JS_BITS_PER_WORD); - capacity = (size_t)1 << log; - if (capacity < info->minCapacity) - capacity = info->minCapacity; - } else { - capacity = JS_ROUNDUP(count, linear); - } - - JS_ASSERT(capacity >= count); - return capacity; -} - -static void -FreePtrTable(JSPtrTable *table, const JSPtrTableInfo *info) -{ - if (table->array) { - JS_ASSERT(table->count > 0); - free(table->array); - table->array = NULL; - table->count = 0; - } - JS_ASSERT(table->count == 0); -} - -static JSBool -AddToPtrTable(JSContext *cx, JSPtrTable *table, const JSPtrTableInfo *info, - void *ptr) -{ - size_t count, capacity; - void **array; - - count = table->count; - capacity = PtrTableCapacity(count, info); - - if (count == capacity) { - if (capacity < info->minCapacity) { - JS_ASSERT(capacity == 0); - JS_ASSERT(!table->array); - capacity = info->minCapacity; - } else { - /* - * Simplify the overflow detection assuming pointer is bigger - * than byte. - */ - JS_STATIC_ASSERT(2 <= sizeof table->array[0]); - capacity = (capacity < info->linearGrowthThreshold) - ? 2 * capacity - : capacity + info->linearGrowthThreshold; - if (capacity > (size_t)-1 / sizeof table->array[0]) - goto bad; - } - array = (void **) realloc(table->array, - capacity * sizeof table->array[0]); - if (!array) - goto bad; -#ifdef DEBUG - memset(array + count, JS_FREE_PATTERN, - (capacity - count) * sizeof table->array[0]); -#endif - table->array = array; - } - - table->array[count] = ptr; - table->count = count + 1; - - return JS_TRUE; - - bad: - JS_ReportOutOfMemory(cx); - return JS_FALSE; -} - -static void -ShrinkPtrTable(JSPtrTable *table, const JSPtrTableInfo *info, - size_t newCount) -{ - size_t oldCapacity, capacity; - void **array; - - JS_ASSERT(newCount <= table->count); - if (newCount == table->count) - return; - - oldCapacity = PtrTableCapacity(table->count, info); - table->count = newCount; - capacity = PtrTableCapacity(newCount, info); - - if (oldCapacity != capacity) { - array = table->array; - JS_ASSERT(array); - if (capacity == 0) { - free(array); - table->array = NULL; - return; - } - array = (void **) realloc(array, capacity * sizeof array[0]); - if (array) - table->array = array; - } -#ifdef DEBUG - memset(table->array + newCount, JS_FREE_PATTERN, - (capacity - newCount) * sizeof table->array[0]); -#endif -} - -#ifdef JS_GCMETER -# define METER(x) x -#else -# define METER(x) ((void) 0) -#endif - -static JSBool -NewGCArena(JSRuntime *rt, JSGCArenaList *arenaList) -{ - JSGCArena *a; - jsuword offset; - JSGCPageInfo *pi; - uint32 *bytesptr; - - /* Check if we are allowed and can allocate a new arena. */ - if (rt->gcBytes >= rt->gcMaxBytes) - return JS_FALSE; - a = (JSGCArena *)malloc(GC_ARENA_SIZE); - if (!a) - return JS_FALSE; - - /* Initialize the JSGCPageInfo records at the start of every thing page. */ - offset = (GC_PAGE_SIZE - ((jsuword)a->base & GC_PAGE_MASK)) & GC_PAGE_MASK; - JS_ASSERT((jsuword)a->base + offset == FIRST_THING_PAGE(a)); - do { - pi = (JSGCPageInfo *) (a->base + offset); - pi->offsetInArena = offset; - pi->unscannedBitmap = 0; - offset += GC_PAGE_SIZE; - } while (offset < GC_THINGS_SIZE); - - METER(++arenaList->stats.narenas); - METER(arenaList->stats.maxarenas - = JS_MAX(arenaList->stats.maxarenas, arenaList->stats.narenas)); - - a->list = arenaList; - a->prev = arenaList->last; - a->prevUnscanned = NULL; - a->unscannedPages = 0; - arenaList->last = a; - arenaList->lastLimit = 0; - - bytesptr = (arenaList == &rt->gcArenaList[0]) - ? &rt->gcBytes - : &rt->gcPrivateBytes; - *bytesptr += GC_ARENA_SIZE; - - return JS_TRUE; -} - -static void -DestroyGCArena(JSRuntime *rt, JSGCArenaList *arenaList, JSGCArena **ap) -{ - JSGCArena *a; - uint32 *bytesptr; - - a = *ap; - JS_ASSERT(a); - bytesptr = (arenaList == &rt->gcArenaList[0]) - ? &rt->gcBytes - : &rt->gcPrivateBytes; - JS_ASSERT(*bytesptr >= GC_ARENA_SIZE); - *bytesptr -= GC_ARENA_SIZE; - METER(rt->gcStats.afree++); - METER(--arenaList->stats.narenas); - if (a == arenaList->last) - arenaList->lastLimit = (uint16)(a->prev ? GC_THINGS_SIZE : 0); - *ap = a->prev; - -#ifdef DEBUG - memset(a, JS_FREE_PATTERN, GC_ARENA_SIZE); -#endif - free(a); -} - -static void -InitGCArenaLists(JSRuntime *rt) -{ - uintN i, thingSize; - JSGCArenaList *arenaList; - - for (i = 0; i < GC_NUM_FREELISTS; i++) { - arenaList = &rt->gcArenaList[i]; - thingSize = GC_FREELIST_NBYTES(i); - JS_ASSERT((size_t)(uint16)thingSize == thingSize); - arenaList->last = NULL; - arenaList->lastLimit = 0; - arenaList->thingSize = (uint16)thingSize; - arenaList->freeList = NULL; - METER(memset(&arenaList->stats, 0, sizeof arenaList->stats)); - } -} - -static void -FinishGCArenaLists(JSRuntime *rt) -{ - uintN i; - JSGCArenaList *arenaList; - - for (i = 0; i < GC_NUM_FREELISTS; i++) { - arenaList = &rt->gcArenaList[i]; - while (arenaList->last) - DestroyGCArena(rt, arenaList, &arenaList->last); - arenaList->freeList = NULL; - } -} - -uint8 * -js_GetGCThingFlags(void *thing) -{ - JSGCPageInfo *pi; - jsuword offsetInArena, thingIndex; - - pi = THING_TO_PAGE(thing); - offsetInArena = pi->offsetInArena; - JS_ASSERT(offsetInArena < GC_THINGS_SIZE); - thingIndex = ((offsetInArena & ~GC_PAGE_MASK) | - ((jsuword)thing & GC_PAGE_MASK)) / sizeof(JSGCThing); - JS_ASSERT(thingIndex < GC_PAGE_SIZE); - if (thingIndex >= (offsetInArena & GC_PAGE_MASK)) - thingIndex += GC_THINGS_SIZE; - return (uint8 *)pi - offsetInArena + thingIndex; -} - -JSRuntime* -js_GetGCStringRuntime(JSString *str) -{ - JSGCPageInfo *pi; - JSGCArenaList *list; - - pi = THING_TO_PAGE(str); - list = PAGE_TO_ARENA(pi)->list; - - JS_ASSERT(list->thingSize == sizeof(JSGCThing)); - JS_ASSERT(GC_FREELIST_INDEX(sizeof(JSGCThing)) == 0); - - return (JSRuntime *)((uint8 *)list - offsetof(JSRuntime, gcArenaList)); -} - -JSBool -js_IsAboutToBeFinalized(JSContext *cx, void *thing) -{ - uint8 flags = *js_GetGCThingFlags(thing); - - return !(flags & (GCF_MARK | GCF_LOCK | GCF_FINAL)); -} - -typedef void (*GCFinalizeOp)(JSContext *cx, JSGCThing *thing); - -#ifndef DEBUG -# define js_FinalizeDouble NULL -#endif - -#if !JS_HAS_XML_SUPPORT -# define js_FinalizeXMLNamespace NULL -# define js_FinalizeXMLQName NULL -# define js_FinalizeXML NULL -#endif - -static GCFinalizeOp gc_finalizers[GCX_NTYPES] = { - (GCFinalizeOp) js_FinalizeObject, /* GCX_OBJECT */ - (GCFinalizeOp) js_FinalizeString, /* GCX_STRING */ - (GCFinalizeOp) js_FinalizeDouble, /* GCX_DOUBLE */ - (GCFinalizeOp) js_FinalizeString, /* GCX_MUTABLE_STRING */ - NULL, /* GCX_PRIVATE */ - (GCFinalizeOp) js_FinalizeXMLNamespace, /* GCX_NAMESPACE */ - (GCFinalizeOp) js_FinalizeXMLQName, /* GCX_QNAME */ - (GCFinalizeOp) js_FinalizeXML, /* GCX_XML */ - NULL, /* GCX_EXTERNAL_STRING */ - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL -}; - -#ifdef GC_MARK_DEBUG -static const char newborn_external_string[] = "newborn external string"; - -static const char *gc_typenames[GCX_NTYPES] = { - "newborn object", - "newborn string", - "newborn double", - "newborn mutable string", - "newborn private", - "newborn Namespace", - "newborn QName", - "newborn XML", - newborn_external_string, - newborn_external_string, - newborn_external_string, - newborn_external_string, - newborn_external_string, - newborn_external_string, - newborn_external_string, - newborn_external_string -}; -#endif - -intN -js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop, - JSStringFinalizeOp newop) -{ - uintN i; - - for (i = GCX_EXTERNAL_STRING; i < GCX_NTYPES; i++) { - if (gc_finalizers[i] == (GCFinalizeOp) oldop) { - gc_finalizers[i] = (GCFinalizeOp) newop; - return (intN) i; - } - } - return -1; -} - -/* This is compatible with JSDHashEntryStub. */ -typedef struct JSGCRootHashEntry { - JSDHashEntryHdr hdr; - void *root; - const char *name; -} JSGCRootHashEntry; - -/* Initial size of the gcRootsHash table (SWAG, small enough to amortize). */ -#define GC_ROOTS_SIZE 256 -#define GC_FINALIZE_LEN 1024 - -JSBool -js_InitGC(JSRuntime *rt, uint32 maxbytes) -{ - InitGCArenaLists(rt); - if (!JS_DHashTableInit(&rt->gcRootsHash, JS_DHashGetStubOps(), NULL, - sizeof(JSGCRootHashEntry), GC_ROOTS_SIZE)) { - rt->gcRootsHash.ops = NULL; - return JS_FALSE; - } - rt->gcLocksHash = NULL; /* create lazily */ - - /* - * Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes - * for default backward API compatibility. - */ - rt->gcMaxBytes = rt->gcMaxMallocBytes = maxbytes; - - return JS_TRUE; -} - -#ifdef JS_GCMETER -JS_FRIEND_API(void) -js_DumpGCStats(JSRuntime *rt, FILE *fp) -{ - uintN i; - size_t totalThings, totalMaxThings, totalBytes; - - fprintf(fp, "\nGC allocation statistics:\n"); - -#define UL(x) ((unsigned long)(x)) -#define ULSTAT(x) UL(rt->gcStats.x) - totalThings = 0; - totalMaxThings = 0; - totalBytes = 0; - for (i = 0; i < GC_NUM_FREELISTS; i++) { - JSGCArenaList *list = &rt->gcArenaList[i]; - JSGCArenaStats *stats = &list->stats; - if (stats->maxarenas == 0) { - fprintf(fp, "ARENA LIST %u (thing size %lu): NEVER USED\n", - i, UL(GC_FREELIST_NBYTES(i))); - continue; - } - fprintf(fp, "ARENA LIST %u (thing size %lu):\n", - i, UL(GC_FREELIST_NBYTES(i))); - fprintf(fp, " arenas: %lu\n", UL(stats->narenas)); - fprintf(fp, " max arenas: %lu\n", UL(stats->maxarenas)); - fprintf(fp, " things: %lu\n", UL(stats->nthings)); - fprintf(fp, " max things: %lu\n", UL(stats->maxthings)); - fprintf(fp, " free list: %lu\n", UL(stats->freelen)); - fprintf(fp, " free list density: %.1f%%\n", - stats->narenas == 0 - ? 0.0 - : (100.0 * list->thingSize * (jsdouble)stats->freelen / - (GC_THINGS_SIZE * (jsdouble)stats->narenas))); - fprintf(fp, " average free list density: %.1f%%\n", - stats->totalarenas == 0 - ? 0.0 - : (100.0 * list->thingSize * (jsdouble)stats->totalfreelen / - (GC_THINGS_SIZE * (jsdouble)stats->totalarenas))); - fprintf(fp, " recycles: %lu\n", UL(stats->recycle)); - fprintf(fp, " recycle/alloc ratio: %.2f\n", - (jsdouble)stats->recycle / - (jsdouble)(stats->totalnew - stats->recycle)); - totalThings += stats->nthings; - totalMaxThings += stats->maxthings; - totalBytes += GC_FREELIST_NBYTES(i) * stats->nthings; - } - fprintf(fp, "TOTAL STATS:\n"); - fprintf(fp, " public bytes allocated: %lu\n", UL(rt->gcBytes)); - fprintf(fp, " private bytes allocated: %lu\n", UL(rt->gcPrivateBytes)); - fprintf(fp, " alloc attempts: %lu\n", ULSTAT(alloc)); -#ifdef JS_THREADSAFE - fprintf(fp, " alloc without locks: %1u\n", ULSTAT(localalloc)); -#endif - fprintf(fp, " total GC things: %lu\n", UL(totalThings)); - fprintf(fp, " max total GC things: %lu\n", UL(totalMaxThings)); - fprintf(fp, " GC things size: %lu\n", UL(totalBytes)); - fprintf(fp, "allocation retries after GC: %lu\n", ULSTAT(retry)); - fprintf(fp, " allocation failures: %lu\n", ULSTAT(fail)); - fprintf(fp, " things born locked: %lu\n", ULSTAT(lockborn)); - fprintf(fp, " valid lock calls: %lu\n", ULSTAT(lock)); - fprintf(fp, " valid unlock calls: %lu\n", ULSTAT(unlock)); - fprintf(fp, " mark recursion depth: %lu\n", ULSTAT(depth)); - fprintf(fp, " maximum mark recursion: %lu\n", ULSTAT(maxdepth)); - fprintf(fp, " mark C recursion depth: %lu\n", ULSTAT(cdepth)); - fprintf(fp, " maximum mark C recursion: %lu\n", ULSTAT(maxcdepth)); - fprintf(fp, " delayed scan bag adds: %lu\n", ULSTAT(unscanned)); -#ifdef DEBUG - fprintf(fp, " max delayed scan bag size: %lu\n", ULSTAT(maxunscanned)); -#endif - fprintf(fp, " maximum GC nesting level: %lu\n", ULSTAT(maxlevel)); - fprintf(fp, "potentially useful GC calls: %lu\n", ULSTAT(poke)); - fprintf(fp, " useless GC calls: %lu\n", ULSTAT(nopoke)); - fprintf(fp, " thing arenas freed so far: %lu\n", ULSTAT(afree)); - fprintf(fp, " stack segments scanned: %lu\n", ULSTAT(stackseg)); - fprintf(fp, "stack segment slots scanned: %lu\n", ULSTAT(segslots)); - fprintf(fp, "reachable closeable objects: %lu\n", ULSTAT(nclose)); - fprintf(fp, " max reachable closeable: %lu\n", ULSTAT(maxnclose)); - fprintf(fp, " scheduled close hooks: %lu\n", ULSTAT(closelater)); - fprintf(fp, " max scheduled close hooks: %lu\n", ULSTAT(maxcloselater)); -#undef UL -#undef US - -#ifdef JS_ARENAMETER - JS_DumpArenaStats(fp); -#endif -} -#endif - -#ifdef DEBUG -static void -CheckLeakedRoots(JSRuntime *rt); -#endif - -void -js_FinishGC(JSRuntime *rt) -{ -#ifdef JS_ARENAMETER - JS_DumpArenaStats(stdout); -#endif -#ifdef JS_GCMETER - js_DumpGCStats(rt, stdout); -#endif - - FreePtrTable(&rt->gcIteratorTable, &iteratorTableInfo); -#if JS_HAS_GENERATORS - rt->gcCloseState.reachableList = NULL; - METER(rt->gcStats.nclose = 0); - rt->gcCloseState.todoQueue = NULL; -#endif - FinishGCArenaLists(rt); - - if (rt->gcRootsHash.ops) { -#ifdef DEBUG - CheckLeakedRoots(rt); -#endif - JS_DHashTableFinish(&rt->gcRootsHash); - rt->gcRootsHash.ops = NULL; - } - if (rt->gcLocksHash) { - JS_DHashTableDestroy(rt->gcLocksHash); - rt->gcLocksHash = NULL; - } -} - -JSBool -js_AddRoot(JSContext *cx, void *rp, const char *name) -{ - JSBool ok = js_AddRootRT(cx->runtime, rp, name); - if (!ok) - JS_ReportOutOfMemory(cx); - return ok; -} - -JSBool -js_AddRootRT(JSRuntime *rt, void *rp, const char *name) -{ - JSBool ok; - JSGCRootHashEntry *rhe; - - /* - * Due to the long-standing, but now removed, use of rt->gcLock across the - * bulk of js_GC, API users have come to depend on JS_AddRoot etc. locking - * properly with a racing GC, without calling JS_AddRoot from a request. - * We have to preserve API compatibility here, now that we avoid holding - * rt->gcLock across the mark phase (including the root hashtable mark). - * - * If the GC is running and we're called on another thread, wait for this - * GC activation to finish. We can safely wait here (in the case where we - * are called within a request on another thread's context) without fear - * of deadlock because the GC doesn't set rt->gcRunning until after it has - * waited for all active requests to end. - */ - JS_LOCK_GC(rt); -#ifdef JS_THREADSAFE - JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0); - if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) { - do { - JS_AWAIT_GC_DONE(rt); - } while (rt->gcLevel > 0); - } -#endif - rhe = (JSGCRootHashEntry *) JS_DHashTableOperate(&rt->gcRootsHash, rp, - JS_DHASH_ADD); - if (rhe) { - rhe->root = rp; - rhe->name = name; - ok = JS_TRUE; - } else { - ok = JS_FALSE; - } - JS_UNLOCK_GC(rt); - return ok; -} - -JSBool -js_RemoveRoot(JSRuntime *rt, void *rp) -{ - /* - * Due to the JS_RemoveRootRT API, we may be called outside of a request. - * Same synchronization drill as above in js_AddRoot. - */ - JS_LOCK_GC(rt); -#ifdef JS_THREADSAFE - JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0); - if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) { - do { - JS_AWAIT_GC_DONE(rt); - } while (rt->gcLevel > 0); - } -#endif - (void) JS_DHashTableOperate(&rt->gcRootsHash, rp, JS_DHASH_REMOVE); - rt->gcPoke = JS_TRUE; - JS_UNLOCK_GC(rt); - return JS_TRUE; -} - -#ifdef DEBUG - -JS_STATIC_DLL_CALLBACK(JSDHashOperator) -js_root_printer(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 i, void *arg) -{ - uint32 *leakedroots = (uint32 *)arg; - JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; - - (*leakedroots)++; - fprintf(stderr, - "JS engine warning: leaking GC root \'%s\' at %p\n", - rhe->name ? (char *)rhe->name : "", rhe->root); - - return JS_DHASH_NEXT; -} - -static void -CheckLeakedRoots(JSRuntime *rt) -{ - uint32 leakedroots = 0; - - /* Warn (but don't assert) debug builds of any remaining roots. */ - JS_DHashTableEnumerate(&rt->gcRootsHash, js_root_printer, - &leakedroots); - if (leakedroots > 0) { - if (leakedroots == 1) { - fprintf(stderr, -"JS engine warning: 1 GC root remains after destroying the JSRuntime.\n" -" This root may point to freed memory. Objects reachable\n" -" through it have not been finalized.\n"); - } else { - fprintf(stderr, -"JS engine warning: %lu GC roots remain after destroying the JSRuntime.\n" -" These roots may point to freed memory. Objects reachable\n" -" through them have not been finalized.\n", - (unsigned long) leakedroots); - } - } -} - -typedef struct NamedRootDumpArgs { - void (*dump)(const char *name, void *rp, void *data); - void *data; -} NamedRootDumpArgs; - -JS_STATIC_DLL_CALLBACK(JSDHashOperator) -js_named_root_dumper(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, - void *arg) -{ - NamedRootDumpArgs *args = (NamedRootDumpArgs *) arg; - JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; - - if (rhe->name) - args->dump(rhe->name, rhe->root, args->data); - return JS_DHASH_NEXT; -} - -void -js_DumpNamedRoots(JSRuntime *rt, - void (*dump)(const char *name, void *rp, void *data), - void *data) -{ - NamedRootDumpArgs args; - - args.dump = dump; - args.data = data; - JS_DHashTableEnumerate(&rt->gcRootsHash, js_named_root_dumper, &args); -} - -#endif /* DEBUG */ - -typedef struct GCRootMapArgs { - JSGCRootMapFun map; - void *data; -} GCRootMapArgs; - -JS_STATIC_DLL_CALLBACK(JSDHashOperator) -js_gcroot_mapper(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, - void *arg) -{ - GCRootMapArgs *args = (GCRootMapArgs *) arg; - JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; - intN mapflags; - JSDHashOperator op; - - mapflags = args->map(rhe->root, rhe->name, args->data); - -#if JS_MAP_GCROOT_NEXT == JS_DHASH_NEXT && \ - JS_MAP_GCROOT_STOP == JS_DHASH_STOP && \ - JS_MAP_GCROOT_REMOVE == JS_DHASH_REMOVE - op = (JSDHashOperator)mapflags; -#else - op = JS_DHASH_NEXT; - if (mapflags & JS_MAP_GCROOT_STOP) - op |= JS_DHASH_STOP; - if (mapflags & JS_MAP_GCROOT_REMOVE) - op |= JS_DHASH_REMOVE; -#endif - - return op; -} - -uint32 -js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data) -{ - GCRootMapArgs args; - uint32 rv; - - args.map = map; - args.data = data; - JS_LOCK_GC(rt); - rv = JS_DHashTableEnumerate(&rt->gcRootsHash, js_gcroot_mapper, &args); - JS_UNLOCK_GC(rt); - return rv; -} - -JSBool -js_RegisterCloseableIterator(JSContext *cx, JSObject *obj) -{ - JSRuntime *rt; - JSBool ok; - - rt = cx->runtime; - JS_ASSERT(!rt->gcRunning); - - JS_LOCK_GC(rt); - ok = AddToPtrTable(cx, &rt->gcIteratorTable, &iteratorTableInfo, obj); - JS_UNLOCK_GC(rt); - return ok; -} - -static void -CloseIteratorStates(JSContext *cx) -{ - JSRuntime *rt; - size_t count, newCount, i; - void **array; - JSObject *obj; - - rt = cx->runtime; - count = rt->gcIteratorTable.count; - array = rt->gcIteratorTable.array; - - newCount = 0; - for (i = 0; i != count; ++i) { - obj = (JSObject *)array[i]; - if (js_IsAboutToBeFinalized(cx, obj)) - js_CloseIteratorState(cx, obj); - else - array[newCount++] = obj; - } - ShrinkPtrTable(&rt->gcIteratorTable, &iteratorTableInfo, newCount); -} - -#if JS_HAS_GENERATORS - -void -js_RegisterGenerator(JSContext *cx, JSGenerator *gen) -{ - JSRuntime *rt; - - rt = cx->runtime; - JS_ASSERT(!rt->gcRunning); - JS_ASSERT(rt->state != JSRTS_LANDING); - JS_ASSERT(gen->state == JSGEN_NEWBORN); - - JS_LOCK_GC(rt); - gen->next = rt->gcCloseState.reachableList; - rt->gcCloseState.reachableList = gen; - METER(rt->gcStats.nclose++); - METER(rt->gcStats.maxnclose = JS_MAX(rt->gcStats.maxnclose, - rt->gcStats.nclose)); - JS_UNLOCK_GC(rt); -} - -/* - * We do not run close hooks when the parent scope of the generator instance - * becomes unreachable to prevent denial-of-service and resource leakage from - * misbehaved generators. - * - * Called from the GC. - */ -static JSBool -CanScheduleCloseHook(JSGenerator *gen) -{ - JSObject *parent; - JSBool canSchedule; - - /* Avoid OBJ_GET_PARENT overhead as we are in GC. */ - parent = JSVAL_TO_OBJECT(gen->obj->slots[JSSLOT_PARENT]); - canSchedule = *js_GetGCThingFlags(parent) & GCF_MARK; -#ifdef DEBUG_igor - if (!canSchedule) { - fprintf(stderr, "GEN: Kill without schedule, gen=%p parent=%p\n", - (void *)gen, (void *)parent); - } -#endif - return canSchedule; -} - -/* - * Check if we should delay execution of the close hook. - * - * Called outside GC or any locks. - * - * XXX The current implementation is a hack that embeds the knowledge of the - * browser embedding pending the resolution of bug 352788. In the browser we - * must not close any generators that came from a page that is currently in - * the browser history. We detect that using the fact in the browser the scope - * is history if scope->outerObject->innerObject != scope. - */ -static JSBool -ShouldDeferCloseHook(JSContext *cx, JSGenerator *gen, JSBool *defer) -{ - JSObject *parent, *obj; - JSClass *clasp; - JSExtendedClass *xclasp; - - /* - * This is called outside any locks, so use thread-safe macros to access - * parent and classes. - */ - *defer = JS_FALSE; - parent = OBJ_GET_PARENT(cx, gen->obj); - clasp = OBJ_GET_CLASS(cx, parent); - if (clasp->flags & JSCLASS_IS_EXTENDED) { - xclasp = (JSExtendedClass *)clasp; - if (xclasp->outerObject) { - obj = xclasp->outerObject(cx, parent); - if (!obj) - return JS_FALSE; - OBJ_TO_INNER_OBJECT(cx, obj); - if (!obj) - return JS_FALSE; - *defer = obj != parent; - } - } -#ifdef DEBUG_igor - if (*defer) { - fprintf(stderr, "GEN: deferring, gen=%p parent=%p\n", - (void *)gen, (void *)parent); - } -#endif - return JS_TRUE; -} - -/* - * Find all unreachable generators and move them to the todo queue from - * rt->gcCloseState.reachableList to execute thier close hooks after the GC - * cycle completes. To ensure liveness during the sweep phase we mark all - * generators we are going to close later. - */ -static void -FindAndMarkObjectsToClose(JSContext *cx, JSGCInvocationKind gckind, - JSGenerator **todoQueueTail) -{ - JSRuntime *rt; - JSGenerator *todo, **genp, *gen; - - rt = cx->runtime; - todo = NULL; - genp = &rt->gcCloseState.reachableList; - while ((gen = *genp) != NULL) { - if (*js_GetGCThingFlags(gen->obj) & GCF_MARK) { - genp = &gen->next; - } else { - /* Generator must not be executing when it becomes unreachable. */ - JS_ASSERT(gen->state == JSGEN_NEWBORN || - gen->state == JSGEN_OPEN || - gen->state == JSGEN_CLOSED); - - *genp = gen->next; - if (gen->state == JSGEN_OPEN && - js_FindFinallyHandler(gen->frame.script, gen->frame.pc) && - CanScheduleCloseHook(gen)) { - /* - * Generator yielded inside a try with a finally block. - * Schedule it for closing. - * - * We keep generators that yielded outside try-with-finally - * with gen->state == JSGEN_OPEN. The finalizer must deal with - * open generators as we may skip the close hooks, see below. - */ - gen->next = NULL; - *todoQueueTail = gen; - todoQueueTail = &gen->next; - if (!todo) - todo = gen; - METER(JS_ASSERT(rt->gcStats.nclose)); - METER(rt->gcStats.nclose--); - METER(rt->gcStats.closelater++); - METER(rt->gcStats.maxcloselater - = JS_MAX(rt->gcStats.maxcloselater, - rt->gcStats.closelater)); - } - } - } - - if (gckind == GC_LAST_CONTEXT) { - /* - * Remove scheduled hooks on shutdown as it is too late to run them: - * we do not allow execution of arbitrary scripts at this point. - */ - rt->gcCloseState.todoQueue = NULL; - } else { - /* - * Mark just-found unreachable generators *after* we scan the global - * list to prevent a generator that refers to other unreachable - * generators from keeping them on gcCloseState.reachableList. - */ - for (gen = todo; gen; gen = gen->next) - GC_MARK(cx, gen->obj, "newly scheduled generator"); - } -} - -/* - * Mark unreachable generators already scheduled to close and return the tail - * pointer to JSGCCloseState.todoQueue. - */ -static JSGenerator ** -MarkScheduledGenerators(JSContext *cx) -{ - JSRuntime *rt; - JSGenerator **genp, *gen; - - rt = cx->runtime; - genp = &rt->gcCloseState.todoQueue; - while ((gen = *genp) != NULL) { - if (CanScheduleCloseHook(gen)) { - GC_MARK(cx, gen->obj, "scheduled generator"); - genp = &gen->next; - } else { - /* Discard the generator from the list if its schedule is over. */ - *genp = gen->next; - METER(JS_ASSERT(rt->gcStats.closelater > 0)); - METER(rt->gcStats.closelater--); - } - } - return genp; -} - -#ifdef JS_THREADSAFE -# define GC_RUNNING_CLOSE_HOOKS_PTR(cx) \ - (&(cx)->thread->gcRunningCloseHooks) -#else -# define GC_RUNNING_CLOSE_HOOKS_PTR(cx) \ - (&(cx)->runtime->gcCloseState.runningCloseHook) -#endif - -typedef struct JSTempCloseList { - JSTempValueRooter tvr; - JSGenerator *head; -} JSTempCloseList; - -JS_STATIC_DLL_CALLBACK(void) -mark_temp_close_list(JSContext *cx, JSTempValueRooter *tvr) -{ - JSTempCloseList *list = (JSTempCloseList *)tvr; - JSGenerator *gen; - - for (gen = list->head; gen; gen = gen->next) - GC_MARK(cx, gen->obj, "temp list generator"); -} - -#define JS_PUSH_TEMP_CLOSE_LIST(cx, tempList) \ - JS_PUSH_TEMP_ROOT_MARKER(cx, mark_temp_close_list, &(tempList)->tvr) - -#define JS_POP_TEMP_CLOSE_LIST(cx, tempList) \ - JS_BEGIN_MACRO \ - JS_ASSERT((tempList)->tvr.u.marker == mark_temp_close_list); \ - JS_POP_TEMP_ROOT(cx, &(tempList)->tvr); \ - JS_END_MACRO - -JSBool -js_RunCloseHooks(JSContext *cx) -{ - JSRuntime *rt; - JSTempCloseList tempList; - JSStackFrame *fp; - JSGenerator **genp, *gen; - JSBool ok, defer; -#if JS_GCMETER - uint32 deferCount; -#endif - - rt = cx->runtime; - - /* - * It is OK to access todoQueue outside the lock here. When many threads - * update the todo list, accessing some older value of todoQueue in the - * worst case just delays the excution of close hooks. - */ - if (!rt->gcCloseState.todoQueue) - return JS_TRUE; - - /* - * To prevent an infinite loop when a close hook creats more objects with - * close hooks and then triggers GC we ignore recursive invocations of - * js_RunCloseHooks and limit number of hooks to execute to the initial - * size of the list. - */ - if (*GC_RUNNING_CLOSE_HOOKS_PTR(cx)) - return JS_TRUE; - - *GC_RUNNING_CLOSE_HOOKS_PTR(cx) = JS_TRUE; - - JS_LOCK_GC(rt); - tempList.head = rt->gcCloseState.todoQueue; - JS_PUSH_TEMP_CLOSE_LIST(cx, &tempList); - rt->gcCloseState.todoQueue = NULL; - METER(rt->gcStats.closelater = 0); - rt->gcPoke = JS_TRUE; - JS_UNLOCK_GC(rt); - - /* - * Set aside cx->fp since we do not want a close hook using caller or - * other means to backtrace into whatever stack might be active when - * running the hook. We store the current frame on the dormant list to - * protect against GC that the hook can trigger. - */ - fp = cx->fp; - if (fp) { - JS_ASSERT(!fp->dormantNext); - fp->dormantNext = cx->dormantFrameChain; - cx->dormantFrameChain = fp; - } - cx->fp = NULL; - - genp = &tempList.head; - ok = JS_TRUE; - while ((gen = *genp) != NULL) { - ok = ShouldDeferCloseHook(cx, gen, &defer); - if (!ok) { - /* Quit ASAP discarding the hook. */ - *genp = gen->next; - break; - } - if (defer) { - genp = &gen->next; - METER(deferCount++); - continue; - } - ok = js_CloseGeneratorObject(cx, gen); - - /* - * Unlink the generator after closing it to make sure it always stays - * rooted through tempList. - */ - *genp = gen->next; - - if (cx->throwing) { - /* - * Report the exception thrown by the close hook and continue to - * execute the rest of the hooks. - */ - if (!js_ReportUncaughtException(cx)) - JS_ClearPendingException(cx); - ok = JS_TRUE; - } else if (!ok) { - /* - * Assume this is a stop signal from the branch callback or - * other quit ASAP condition. Break execution until the next - * invocation of js_RunCloseHooks. - */ - break; - } - } - - cx->fp = fp; - if (fp) { - JS_ASSERT(cx->dormantFrameChain == fp); - cx->dormantFrameChain = fp->dormantNext; - fp->dormantNext = NULL; - } - - if (tempList.head) { - /* - * Some close hooks were not yet executed, put them back into the - * scheduled list. - */ - while ((gen = *genp) != NULL) { - genp = &gen->next; - METER(deferCount++); - } - - /* Now genp is a pointer to the tail of tempList. */ - JS_LOCK_GC(rt); - *genp = rt->gcCloseState.todoQueue; - rt->gcCloseState.todoQueue = tempList.head; - METER(rt->gcStats.closelater += deferCount); - METER(rt->gcStats.maxcloselater - = JS_MAX(rt->gcStats.maxcloselater, rt->gcStats.closelater)); - JS_UNLOCK_GC(rt); - } - - JS_POP_TEMP_CLOSE_LIST(cx, &tempList); - *GC_RUNNING_CLOSE_HOOKS_PTR(cx) = JS_FALSE; - - return ok; -} - -#endif /* JS_HAS_GENERATORS */ - -#if defined(DEBUG_brendan) || defined(DEBUG_timeless) -#define DEBUG_gchist -#endif - -#ifdef DEBUG_gchist -#define NGCHIST 64 - -static struct GCHist { - JSBool lastDitch; - JSGCThing *freeList; -} gchist[NGCHIST]; - -unsigned gchpos; -#endif - -void * -js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes) -{ - JSRuntime *rt; - uintN flindex; - JSBool doGC; - JSGCThing *thing; - uint8 *flagp, *firstPage; - JSGCArenaList *arenaList; - jsuword offset; - JSGCArena *a; - JSLocalRootStack *lrs; -#ifdef JS_THREADSAFE - JSBool gcLocked; - uintN localMallocBytes; - JSGCThing **flbase, **lastptr; - JSGCThing *tmpthing; - uint8 *tmpflagp; - uintN maxFreeThings; /* max to take from the global free list */ - METER(size_t nfree); -#endif - - rt = cx->runtime; - METER(rt->gcStats.alloc++); /* this is not thread-safe */ - nbytes = JS_ROUNDUP(nbytes, sizeof(JSGCThing)); - flindex = GC_FREELIST_INDEX(nbytes); - -#ifdef JS_THREADSAFE - gcLocked = JS_FALSE; - JS_ASSERT(cx->thread); - flbase = cx->thread->gcFreeLists; - JS_ASSERT(flbase); - thing = flbase[flindex]; - localMallocBytes = cx->thread->gcMallocBytes; - if (thing && rt->gcMaxMallocBytes - rt->gcMallocBytes > localMallocBytes) { - flagp = thing->flagp; - flbase[flindex] = thing->next; - METER(rt->gcStats.localalloc++); /* this is not thread-safe */ - goto success; - } - - JS_LOCK_GC(rt); - gcLocked = JS_TRUE; - - /* Transfer thread-local counter to global one. */ - if (localMallocBytes != 0) { - cx->thread->gcMallocBytes = 0; - if (rt->gcMaxMallocBytes - rt->gcMallocBytes < localMallocBytes) - rt->gcMallocBytes = rt->gcMaxMallocBytes; - else - rt->gcMallocBytes += localMallocBytes; - } -#endif - JS_ASSERT(!rt->gcRunning); - if (rt->gcRunning) { - METER(rt->gcStats.finalfail++); - JS_UNLOCK_GC(rt); - return NULL; - } - -#ifdef TOO_MUCH_GC -#ifdef WAY_TOO_MUCH_GC - rt->gcPoke = JS_TRUE; -#endif - doGC = JS_TRUE; -#else - doGC = (rt->gcMallocBytes >= rt->gcMaxMallocBytes); -#endif - - arenaList = &rt->gcArenaList[flindex]; - for (;;) { - if (doGC) { - /* - * Keep rt->gcLock across the call into js_GC so we don't starve - * and lose to racing threads who deplete the heap just after - * js_GC has replenished it (or has synchronized with a racing - * GC that collected a bunch of garbage). This unfair scheduling - * can happen on certain operating systems. For the gory details, - * see bug 162779 at https://bugzilla.mozilla.org/. - */ - js_GC(cx, GC_LAST_DITCH); - METER(rt->gcStats.retry++); - } - - /* Try to get thing from the free list. */ - thing = arenaList->freeList; - if (thing) { - arenaList->freeList = thing->next; - flagp = thing->flagp; - JS_ASSERT(*flagp & GCF_FINAL); - METER(arenaList->stats.freelen--); - METER(arenaList->stats.recycle++); - -#ifdef JS_THREADSAFE - /* - * Refill the local free list by taking several things from the - * global free list unless we are still at rt->gcMaxMallocBytes - * barrier or the free list is already populated. The former - * happens when GC is canceled due to !gcCallback(cx, JSGC_BEGIN) - * or no gcPoke. The latter is caused via allocating new things - * in gcCallback(cx, JSGC_END). - */ - if (rt->gcMallocBytes >= rt->gcMaxMallocBytes || flbase[flindex]) - break; - tmpthing = arenaList->freeList; - if (tmpthing) { - maxFreeThings = MAX_THREAD_LOCAL_THINGS; - do { - if (!tmpthing->next) - break; - tmpthing = tmpthing->next; - } while (--maxFreeThings != 0); - - flbase[flindex] = arenaList->freeList; - arenaList->freeList = tmpthing->next; - tmpthing->next = NULL; - } -#endif - break; - } - - /* Allocate from the tail of last arena or from new arena if we can. */ - if ((arenaList->last && arenaList->lastLimit != GC_THINGS_SIZE) || - NewGCArena(rt, arenaList)) { - - offset = arenaList->lastLimit; - if ((offset & GC_PAGE_MASK) == 0) { - /* - * Skip JSGCPageInfo record located at GC_PAGE_SIZE boundary. - */ - offset += PAGE_THING_GAP(nbytes); - } - JS_ASSERT(offset + nbytes <= GC_THINGS_SIZE); - arenaList->lastLimit = (uint16)(offset + nbytes); - a = arenaList->last; - firstPage = (uint8 *)FIRST_THING_PAGE(a); - thing = (JSGCThing *)(firstPage + offset); - flagp = a->base + offset / sizeof(JSGCThing); - if (flagp >= firstPage) - flagp += GC_THINGS_SIZE; - METER(++arenaList->stats.nthings); - METER(arenaList->stats.maxthings = - JS_MAX(arenaList->stats.nthings, - arenaList->stats.maxthings)); - -#ifdef JS_THREADSAFE - /* - * Refill the local free list by taking free things from the last - * arena. Prefer to order free things by ascending address in the - * (unscientific) hope of better cache locality. - */ - if (rt->gcMallocBytes >= rt->gcMaxMallocBytes || flbase[flindex]) - break; - METER(nfree = 0); - lastptr = &flbase[flindex]; - maxFreeThings = MAX_THREAD_LOCAL_THINGS; - for (offset = arenaList->lastLimit; - offset != GC_THINGS_SIZE && maxFreeThings-- != 0; - offset += nbytes) { - if ((offset & GC_PAGE_MASK) == 0) - offset += PAGE_THING_GAP(nbytes); - JS_ASSERT(offset + nbytes <= GC_THINGS_SIZE); - tmpflagp = a->base + offset / sizeof(JSGCThing); - if (tmpflagp >= firstPage) - tmpflagp += GC_THINGS_SIZE; - - tmpthing = (JSGCThing *)(firstPage + offset); - tmpthing->flagp = tmpflagp; - *tmpflagp = GCF_FINAL; /* signifying that thing is free */ - - *lastptr = tmpthing; - lastptr = &tmpthing->next; - METER(++nfree); - } - arenaList->lastLimit = offset; - *lastptr = NULL; - METER(arenaList->stats.freelen += nfree); -#endif - break; - } - - /* Consider doing a "last ditch" GC unless already tried. */ - if (doGC) - goto fail; - rt->gcPoke = JS_TRUE; - doGC = JS_TRUE; - } - - /* We successfully allocated the thing. */ -#ifdef JS_THREADSAFE - success: -#endif - lrs = cx->localRootStack; - if (lrs) { - /* - * If we're in a local root scope, don't set newborn[type] at all, to - * avoid entraining garbage from it for an unbounded amount of time - * on this context. A caller will leave the local root scope and pop - * this reference, allowing thing to be GC'd if it has no other refs. - * See JS_EnterLocalRootScope and related APIs. - */ - if (js_PushLocalRoot(cx, lrs, (jsval) thing) < 0) { - /* - * When we fail for a thing allocated through the tail of the last - * arena, thing's flag byte is not initialized. So to prevent GC - * accessing the uninitialized flags during the finalization, we - * always mark the thing as final. See bug 337407. - */ - *flagp = GCF_FINAL; - goto fail; - } - } else { - /* - * No local root scope, so we're stuck with the old, fragile model of - * depending on a pigeon-hole newborn per type per context. - */ - cx->weakRoots.newborn[flags & GCF_TYPEMASK] = thing; - } - - /* We can't fail now, so update flags and rt->gc{,Private}Bytes. */ - *flagp = (uint8)flags; - - /* - * Clear thing before unlocking in case a GC run is about to scan it, - * finding it via newborn[]. - */ - thing->next = NULL; - thing->flagp = NULL; -#ifdef DEBUG_gchist - gchist[gchpos].lastDitch = doGC; - gchist[gchpos].freeList = rt->gcArenaList[flindex].freeList; - if (++gchpos == NGCHIST) - gchpos = 0; -#endif - METER(if (flags & GCF_LOCK) rt->gcStats.lockborn++); - METER(++rt->gcArenaList[flindex].stats.totalnew); -#ifdef JS_THREADSAFE - if (gcLocked) - JS_UNLOCK_GC(rt); -#endif - return thing; - -fail: -#ifdef JS_THREADSAFE - if (gcLocked) - JS_UNLOCK_GC(rt); -#endif - METER(rt->gcStats.fail++); - JS_ReportOutOfMemory(cx); - return NULL; -} - -JSBool -js_LockGCThing(JSContext *cx, void *thing) -{ - JSBool ok = js_LockGCThingRT(cx->runtime, thing); - if (!ok) - JS_ReportOutOfMemory(cx); - return ok; -} - -/* - * Deep GC-things can't be locked just by setting the GCF_LOCK bit, because - * their descendants must be marked by the GC. To find them during the mark - * phase, they are added to rt->gcLocksHash, which is created lazily. - * - * NB: we depend on the order of GC-thing type indexes here! - */ -#define GC_TYPE_IS_STRING(t) ((t) == GCX_STRING || \ - (t) >= GCX_EXTERNAL_STRING) -#define GC_TYPE_IS_XML(t) ((unsigned)((t) - GCX_NAMESPACE) <= \ - (unsigned)(GCX_XML - GCX_NAMESPACE)) -#define GC_TYPE_IS_DEEP(t) ((t) == GCX_OBJECT || GC_TYPE_IS_XML(t)) - -#define IS_DEEP_STRING(t,o) (GC_TYPE_IS_STRING(t) && \ - JSSTRING_IS_DEPENDENT((JSString *)(o))) - -#define GC_THING_IS_DEEP(t,o) (GC_TYPE_IS_DEEP(t) || IS_DEEP_STRING(t, o)) - -/* This is compatible with JSDHashEntryStub. */ -typedef struct JSGCLockHashEntry { - JSDHashEntryHdr hdr; - const JSGCThing *thing; - uint32 count; -} JSGCLockHashEntry; - -JSBool -js_LockGCThingRT(JSRuntime *rt, void *thing) -{ - JSBool ok, deep; - uint8 *flagp; - uintN flags, lock, type; - JSGCLockHashEntry *lhe; - - ok = JS_TRUE; - if (!thing) - return ok; - - flagp = js_GetGCThingFlags(thing); - - JS_LOCK_GC(rt); - flags = *flagp; - lock = (flags & GCF_LOCK); - type = (flags & GCF_TYPEMASK); - deep = GC_THING_IS_DEEP(type, thing); - - /* - * Avoid adding a rt->gcLocksHash entry for shallow things until someone - * nests a lock -- then start such an entry with a count of 2, not 1. - */ - if (lock || deep) { - if (!rt->gcLocksHash) { - rt->gcLocksHash = - JS_NewDHashTable(JS_DHashGetStubOps(), NULL, - sizeof(JSGCLockHashEntry), - GC_ROOTS_SIZE); - if (!rt->gcLocksHash) { - ok = JS_FALSE; - goto done; - } - } else if (lock == 0) { -#ifdef DEBUG - JSDHashEntryHdr *hdr = - JS_DHashTableOperate(rt->gcLocksHash, thing, - JS_DHASH_LOOKUP); - JS_ASSERT(JS_DHASH_ENTRY_IS_FREE(hdr)); -#endif - } - - lhe = (JSGCLockHashEntry *) - JS_DHashTableOperate(rt->gcLocksHash, thing, JS_DHASH_ADD); - if (!lhe) { - ok = JS_FALSE; - goto done; - } - if (!lhe->thing) { - lhe->thing = thing; - lhe->count = deep ? 1 : 2; - } else { - JS_ASSERT(lhe->count >= 1); - lhe->count++; - } - } - - *flagp = (uint8)(flags | GCF_LOCK); - METER(rt->gcStats.lock++); - ok = JS_TRUE; -done: - JS_UNLOCK_GC(rt); - return ok; -} - -JSBool -js_UnlockGCThingRT(JSRuntime *rt, void *thing) -{ - uint8 *flagp, flags; - JSGCLockHashEntry *lhe; - - if (!thing) - return JS_TRUE; - - flagp = js_GetGCThingFlags(thing); - JS_LOCK_GC(rt); - flags = *flagp; - - if (flags & GCF_LOCK) { - if (!rt->gcLocksHash || - (lhe = (JSGCLockHashEntry *) - JS_DHashTableOperate(rt->gcLocksHash, thing, - JS_DHASH_LOOKUP), - JS_DHASH_ENTRY_IS_FREE(&lhe->hdr))) { - /* Shallow GC-thing with an implicit lock count of 1. */ - JS_ASSERT(!GC_THING_IS_DEEP(flags & GCF_TYPEMASK, thing)); - } else { - /* Basis or nested unlock of a deep thing, or nested of shallow. */ - if (--lhe->count != 0) - goto out; - JS_DHashTableOperate(rt->gcLocksHash, thing, JS_DHASH_REMOVE); - } - *flagp = (uint8)(flags & ~GCF_LOCK); - } - - rt->gcPoke = JS_TRUE; -out: - METER(rt->gcStats.unlock++); - JS_UNLOCK_GC(rt); - return JS_TRUE; -} - -#ifdef GC_MARK_DEBUG - -#include -#include "jsprf.h" - -typedef struct GCMarkNode GCMarkNode; - -struct GCMarkNode { - void *thing; - const char *name; - GCMarkNode *next; - GCMarkNode *prev; -}; - -JS_FRIEND_DATA(FILE *) js_DumpGCHeap; -JS_EXPORT_DATA(void *) js_LiveThingToFind; - -#ifdef HAVE_XPCONNECT -#include "dump_xpc.h" -#endif - -static void -GetObjSlotName(JSScope *scope, JSObject *obj, uint32 slot, char *buf, - size_t bufsize) -{ - jsval nval; - JSScopeProperty *sprop; - JSClass *clasp; - uint32 key; - const char *slotname; - - if (!scope) { - JS_snprintf(buf, bufsize, "**UNKNOWN OBJECT MAP ENTRY**"); - return; - } - - sprop = SCOPE_LAST_PROP(scope); - while (sprop && sprop->slot != slot) - sprop = sprop->parent; - - if (!sprop) { - switch (slot) { - case JSSLOT_PROTO: - JS_snprintf(buf, bufsize, "__proto__"); - break; - case JSSLOT_PARENT: - JS_snprintf(buf, bufsize, "__parent__"); - break; - default: - slotname = NULL; - clasp = LOCKED_OBJ_GET_CLASS(obj); - if (clasp->flags & JSCLASS_IS_GLOBAL) { - key = slot - JSSLOT_START(clasp); -#define JS_PROTO(name,code,init) \ - if ((code) == key) { slotname = js_##name##_str; goto found; } -#include "jsproto.tbl" -#undef JS_PROTO - } - found: - if (slotname) - JS_snprintf(buf, bufsize, "CLASS_OBJECT(%s)", slotname); - else - JS_snprintf(buf, bufsize, "**UNKNOWN SLOT %ld**", (long)slot); - break; - } - } else { - nval = ID_TO_VALUE(sprop->id); - if (JSVAL_IS_INT(nval)) { - JS_snprintf(buf, bufsize, "%ld", (long)JSVAL_TO_INT(nval)); - } else if (JSVAL_IS_STRING(nval)) { - JS_snprintf(buf, bufsize, "%s", - JS_GetStringBytes(JSVAL_TO_STRING(nval))); - } else { - JS_snprintf(buf, bufsize, "**FINALIZED ATOM KEY**"); - } - } -} - -static const char * -gc_object_class_name(void* thing) -{ - uint8 *flagp = js_GetGCThingFlags(thing); - const char *className = ""; - static char depbuf[32]; - - switch (*flagp & GCF_TYPEMASK) { - case GCX_OBJECT: { - JSObject *obj = (JSObject *)thing; - JSClass *clasp = JSVAL_TO_PRIVATE(obj->slots[JSSLOT_CLASS]); - className = clasp->name; -#ifdef HAVE_XPCONNECT - if (clasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) { - jsval privateValue = obj->slots[JSSLOT_PRIVATE]; - - JS_ASSERT(clasp->flags & JSCLASS_HAS_PRIVATE); - if (!JSVAL_IS_VOID(privateValue)) { - void *privateThing = JSVAL_TO_PRIVATE(privateValue); - const char *xpcClassName = GetXPCObjectClassName(privateThing); - - if (xpcClassName) - className = xpcClassName; - } - } -#endif - break; - } - - case GCX_STRING: - case GCX_MUTABLE_STRING: { - JSString *str = (JSString *)thing; - if (JSSTRING_IS_DEPENDENT(str)) { - JS_snprintf(depbuf, sizeof depbuf, "start:%u, length:%u", - JSSTRDEP_START(str), JSSTRDEP_LENGTH(str)); - className = depbuf; - } else { - className = "string"; - } - break; - } - - case GCX_DOUBLE: - className = "double"; - break; - } - - return className; -} - -static void -gc_dump_thing(JSContext *cx, JSGCThing *thing, FILE *fp) -{ - GCMarkNode *prev = (GCMarkNode *)cx->gcCurrentMarkNode; - GCMarkNode *next = NULL; - char *path = NULL; - - while (prev) { - next = prev; - prev = prev->prev; - } - while (next) { - uint8 nextFlags = *js_GetGCThingFlags(next->thing); - if ((nextFlags & GCF_TYPEMASK) == GCX_OBJECT) { - path = JS_sprintf_append(path, "%s(%s @ 0x%08p).", - next->name, - gc_object_class_name(next->thing), - (JSObject*)next->thing); - } else { - path = JS_sprintf_append(path, "%s(%s).", - next->name, - gc_object_class_name(next->thing)); - } - next = next->next; - } - if (!path) - return; - - fprintf(fp, "%08lx ", (long)thing); - switch (*js_GetGCThingFlags(thing) & GCF_TYPEMASK) { - case GCX_OBJECT: - { - JSObject *obj = (JSObject *)thing; - jsval privateValue = obj->slots[JSSLOT_PRIVATE]; - void *privateThing = JSVAL_IS_VOID(privateValue) - ? NULL - : JSVAL_TO_PRIVATE(privateValue); - const char *className = gc_object_class_name(thing); - fprintf(fp, "object %8p %s", privateThing, className); - break; - } -#if JS_HAS_XML_SUPPORT - case GCX_NAMESPACE: - { - JSXMLNamespace *ns = (JSXMLNamespace *)thing; - fprintf(fp, "namespace %s:%s", - JS_GetStringBytes(ns->prefix), JS_GetStringBytes(ns->uri)); - break; - } - case GCX_QNAME: - { - JSXMLQName *qn = (JSXMLQName *)thing; - fprintf(fp, "qname %s(%s):%s", - JS_GetStringBytes(qn->prefix), JS_GetStringBytes(qn->uri), - JS_GetStringBytes(qn->localName)); - break; - } - case GCX_XML: - { - extern const char *js_xml_class_str[]; - JSXML *xml = (JSXML *)thing; - fprintf(fp, "xml %8p %s", xml, js_xml_class_str[xml->xml_class]); - break; - } -#endif - case GCX_DOUBLE: - fprintf(fp, "double %g", *(jsdouble *)thing); - break; - case GCX_PRIVATE: - fprintf(fp, "private %8p", (void *)thing); - break; - default: - fprintf(fp, "string %s", JS_GetStringBytes((JSString *)thing)); - break; - } - fprintf(fp, " via %s\n", path); - free(path); -} - -void -js_MarkNamedGCThing(JSContext *cx, void *thing, const char *name) -{ - GCMarkNode markNode; - - if (!thing) - return; - - markNode.thing = thing; - markNode.name = name; - markNode.next = NULL; - markNode.prev = (GCMarkNode *)cx->gcCurrentMarkNode; - if (markNode.prev) - markNode.prev->next = &markNode; - cx->gcCurrentMarkNode = &markNode; - - if (thing == js_LiveThingToFind) { - /* - * Dump js_LiveThingToFind each time we reach it during the marking - * phase of GC to print all live references to the thing. - */ - gc_dump_thing(cx, thing, stderr); - } - - js_MarkGCThing(cx, thing); - - if (markNode.prev) - markNode.prev->next = NULL; - cx->gcCurrentMarkNode = markNode.prev; -} - -#endif /* !GC_MARK_DEBUG */ - -static void -gc_mark_atom_key_thing(void *thing, void *arg) -{ - JSContext *cx = (JSContext *) arg; - - GC_MARK(cx, thing, "atom"); -} - -void -js_MarkAtom(JSContext *cx, JSAtom *atom) -{ - jsval key; - - if (atom->flags & ATOM_MARK) - return; - atom->flags |= ATOM_MARK; - key = ATOM_KEY(atom); - if (JSVAL_IS_GCTHING(key)) { -#ifdef GC_MARK_DEBUG - char name[32]; - - if (JSVAL_IS_STRING(key)) { - JS_snprintf(name, sizeof name, "'%s'", - JS_GetStringBytes(JSVAL_TO_STRING(key))); - } else { - JS_snprintf(name, sizeof name, "<%x>", key); - } -#endif - GC_MARK(cx, JSVAL_TO_GCTHING(key), name); - } - if (atom->flags & ATOM_HIDDEN) - js_MarkAtom(cx, atom->entry.value); -} - -static void -AddThingToUnscannedBag(JSRuntime *rt, void *thing, uint8 *flagp); - -static void -MarkGCThingChildren(JSContext *cx, void *thing, uint8 *flagp, - JSBool shouldCheckRecursion) -{ - JSRuntime *rt; - JSObject *obj; - jsval v, *vp, *end; - void *next_thing; - uint8 *next_flagp; - JSString *str; -#ifdef JS_GCMETER - uint32 tailCallNesting; -#endif -#ifdef GC_MARK_DEBUG - JSScope *scope; - char name[32]; -#endif - - /* - * With JS_GC_ASSUME_LOW_C_STACK defined the mark phase of GC always - * uses the non-recursive code that otherwise would be called only on - * a low C stack condition. - */ -#ifdef JS_GC_ASSUME_LOW_C_STACK -# define RECURSION_TOO_DEEP() shouldCheckRecursion -#else - int stackDummy; -# define RECURSION_TOO_DEEP() (shouldCheckRecursion && \ - !JS_CHECK_STACK_SIZE(cx, stackDummy)) -#endif - - rt = cx->runtime; - METER(tailCallNesting = 0); - METER(if (++rt->gcStats.cdepth > rt->gcStats.maxcdepth) - rt->gcStats.maxcdepth = rt->gcStats.cdepth); - -#ifndef GC_MARK_DEBUG - start: -#endif - JS_ASSERT(flagp); - JS_ASSERT(*flagp & GCF_MARK); /* the caller must already mark the thing */ - METER(if (++rt->gcStats.depth > rt->gcStats.maxdepth) - rt->gcStats.maxdepth = rt->gcStats.depth); -#ifdef GC_MARK_DEBUG - if (js_DumpGCHeap) - gc_dump_thing(cx, thing, js_DumpGCHeap); -#endif - - switch (*flagp & GCF_TYPEMASK) { - case GCX_OBJECT: - if (RECURSION_TOO_DEEP()) - goto add_to_unscanned_bag; - /* If obj->slots is null, obj must be a newborn. */ - obj = (JSObject *) thing; - vp = obj->slots; - if (!vp) - break; - - /* Mark slots if they are small enough to be GC-allocated. */ - if ((vp[-1] + 1) * sizeof(jsval) <= GC_NBYTES_MAX) - GC_MARK(cx, vp - 1, "slots"); - - /* Set up local variables to loop over unmarked things. */ - end = vp + ((obj->map->ops->mark) - ? obj->map->ops->mark(cx, obj, NULL) - : JS_MIN(obj->map->freeslot, obj->map->nslots)); - thing = NULL; - flagp = NULL; -#ifdef GC_MARK_DEBUG - scope = OBJ_IS_NATIVE(obj) ? OBJ_SCOPE(obj) : NULL; -#endif - for (; vp != end; ++vp) { - v = *vp; - if (!JSVAL_IS_GCTHING(v) || v == JSVAL_NULL) - continue; - next_thing = JSVAL_TO_GCTHING(v); - if (next_thing == thing) - continue; - next_flagp = js_GetGCThingFlags(next_thing); - if (*next_flagp & GCF_MARK) - continue; - JS_ASSERT(*next_flagp != GCF_FINAL); - if (thing) { -#ifdef GC_MARK_DEBUG - GC_MARK(cx, thing, name); -#else - *flagp |= GCF_MARK; - MarkGCThingChildren(cx, thing, flagp, JS_TRUE); -#endif - if (*next_flagp & GCF_MARK) { - /* - * This happens when recursive MarkGCThingChildren marks - * the thing with flags referred by *next_flagp. - */ - thing = NULL; - continue; - } - } -#ifdef GC_MARK_DEBUG - GetObjSlotName(scope, obj, vp - obj->slots, name, sizeof name); -#endif - thing = next_thing; - flagp = next_flagp; - } - if (thing) { - /* - * thing came from the last unmarked GC-thing slot and we - * can optimize tail recursion. - * - * Since we already know that there is enough C stack space, - * we clear shouldCheckRecursion to avoid extra checking in - * RECURSION_TOO_DEEP. - */ - shouldCheckRecursion = JS_FALSE; - goto on_tail_recursion; - } - break; - -#ifdef DEBUG - case GCX_STRING: - str = (JSString *)thing; - JS_ASSERT(!JSSTRING_IS_DEPENDENT(str)); - break; -#endif - - case GCX_MUTABLE_STRING: - str = (JSString *)thing; - if (!JSSTRING_IS_DEPENDENT(str)) - break; - thing = JSSTRDEP_BASE(str); - flagp = js_GetGCThingFlags(thing); - if (*flagp & GCF_MARK) - break; -#ifdef GC_MARK_DEBUG - strcpy(name, "base"); -#endif - /* Fallthrough to code to deal with the tail recursion. */ - - on_tail_recursion: -#ifdef GC_MARK_DEBUG - /* - * Do not eliminate C recursion when debugging to allow - * js_MarkNamedGCThing to build a full dump of live GC - * things. - */ - GC_MARK(cx, thing, name); - break; -#else - /* Eliminate tail recursion for the last unmarked child. */ - JS_ASSERT(*flagp != GCF_FINAL); - METER(++tailCallNesting); - *flagp |= GCF_MARK; - goto start; -#endif - -#if JS_HAS_XML_SUPPORT - case GCX_NAMESPACE: - if (RECURSION_TOO_DEEP()) - goto add_to_unscanned_bag; - js_MarkXMLNamespace(cx, (JSXMLNamespace *)thing); - break; - - case GCX_QNAME: - if (RECURSION_TOO_DEEP()) - goto add_to_unscanned_bag; - js_MarkXMLQName(cx, (JSXMLQName *)thing); - break; - - case GCX_XML: - if (RECURSION_TOO_DEEP()) - goto add_to_unscanned_bag; - js_MarkXML(cx, (JSXML *)thing); - break; -#endif - add_to_unscanned_bag: - AddThingToUnscannedBag(cx->runtime, thing, flagp); - break; - } - -#undef RECURSION_TOO_DEEP - - METER(rt->gcStats.depth -= 1 + tailCallNesting); - METER(rt->gcStats.cdepth--); -} - -/* - * Avoid using PAGE_THING_GAP inside this macro to optimize the - * thingsPerUnscannedChunk calculation when thingSize is a power of two. - */ -#define GET_GAP_AND_CHUNK_SPAN(thingSize, thingsPerUnscannedChunk, pageGap) \ - JS_BEGIN_MACRO \ - if (0 == ((thingSize) & ((thingSize) - 1))) { \ - pageGap = (thingSize); \ - thingsPerUnscannedChunk = ((GC_PAGE_SIZE / (thingSize)) \ - + JS_BITS_PER_WORD - 1) \ - >> JS_BITS_PER_WORD_LOG2; \ - } else { \ - pageGap = GC_PAGE_SIZE % (thingSize); \ - thingsPerUnscannedChunk = JS_HOWMANY(GC_PAGE_SIZE / (thingSize), \ - JS_BITS_PER_WORD); \ - } \ - JS_END_MACRO - -static void -AddThingToUnscannedBag(JSRuntime *rt, void *thing, uint8 *flagp) -{ - JSGCPageInfo *pi; - JSGCArena *arena; - size_t thingSize; - size_t thingsPerUnscannedChunk; - size_t pageGap; - size_t chunkIndex; - jsuword bit; - - /* Things from delayed scanning bag are marked as GCF_MARK | GCF_FINAL. */ - JS_ASSERT((*flagp & (GCF_MARK | GCF_FINAL)) == GCF_MARK); - *flagp |= GCF_FINAL; - - METER(rt->gcStats.unscanned++); -#ifdef DEBUG - ++rt->gcUnscannedBagSize; - METER(if (rt->gcUnscannedBagSize > rt->gcStats.maxunscanned) - rt->gcStats.maxunscanned = rt->gcUnscannedBagSize); -#endif - - pi = THING_TO_PAGE(thing); - arena = PAGE_TO_ARENA(pi); - thingSize = arena->list->thingSize; - GET_GAP_AND_CHUNK_SPAN(thingSize, thingsPerUnscannedChunk, pageGap); - chunkIndex = (((jsuword)thing & GC_PAGE_MASK) - pageGap) / - (thingSize * thingsPerUnscannedChunk); - JS_ASSERT(chunkIndex < JS_BITS_PER_WORD); - bit = (jsuword)1 << chunkIndex; - if (pi->unscannedBitmap != 0) { - JS_ASSERT(rt->gcUnscannedArenaStackTop); - if (thingsPerUnscannedChunk != 1) { - if (pi->unscannedBitmap & bit) { - /* Chunk already contains things to scan later. */ - return; - } - } else { - /* - * The chunk must not contain things to scan later if there is - * only one thing per chunk. - */ - JS_ASSERT(!(pi->unscannedBitmap & bit)); - } - pi->unscannedBitmap |= bit; - JS_ASSERT(arena->unscannedPages & ((size_t)1 << PAGE_INDEX(pi))); - } else { - /* - * The thing is the first unscanned thing in the page, set the bit - * corresponding to this page arena->unscannedPages. - */ - pi->unscannedBitmap = bit; - JS_ASSERT(PAGE_INDEX(pi) < JS_BITS_PER_WORD); - bit = (jsuword)1 << PAGE_INDEX(pi); - JS_ASSERT(!(arena->unscannedPages & bit)); - if (arena->unscannedPages != 0) { - arena->unscannedPages |= bit; - JS_ASSERT(arena->prevUnscanned); - JS_ASSERT(rt->gcUnscannedArenaStackTop); - } else { - /* - * The thing is the first unscanned thing in the whole arena, push - * the arena on the stack of unscanned arenas unless the arena - * has already been pushed. We detect that through prevUnscanned - * field which is NULL only for not yet pushed arenas. To ensure - * that prevUnscanned != NULL even when the stack contains one - * element, we make prevUnscanned for the arena at the bottom - * to point to itself. - * - * See comments in ScanDelayedChildren. - */ - arena->unscannedPages = bit; - if (!arena->prevUnscanned) { - if (!rt->gcUnscannedArenaStackTop) { - /* Stack was empty, mark the arena as bottom element. */ - arena->prevUnscanned = arena; - } else { - JS_ASSERT(rt->gcUnscannedArenaStackTop->prevUnscanned); - arena->prevUnscanned = rt->gcUnscannedArenaStackTop; - } - rt->gcUnscannedArenaStackTop = arena; - } - } - } - JS_ASSERT(rt->gcUnscannedArenaStackTop); -} - -static void -ScanDelayedChildren(JSContext *cx) -{ - JSRuntime *rt; - JSGCArena *arena; - size_t thingSize; - size_t thingsPerUnscannedChunk; - size_t pageGap; - size_t pageIndex; - JSGCPageInfo *pi; - size_t chunkIndex; - size_t thingOffset, thingLimit; - JSGCThing *thing; - uint8 *flagp; - JSGCArena *prevArena; - - rt = cx->runtime; - arena = rt->gcUnscannedArenaStackTop; - if (!arena) { - JS_ASSERT(rt->gcUnscannedBagSize == 0); - return; - } - - init_size: - thingSize = arena->list->thingSize; - GET_GAP_AND_CHUNK_SPAN(thingSize, thingsPerUnscannedChunk, pageGap); - for (;;) { - /* - * The following assert verifies that the current arena belongs to - * the unscan stack since AddThingToUnscannedBag ensures that even - * for stack's bottom prevUnscanned != NULL but rather points to self. - */ - JS_ASSERT(arena->prevUnscanned); - JS_ASSERT(rt->gcUnscannedArenaStackTop->prevUnscanned); - while (arena->unscannedPages != 0) { - pageIndex = JS_FLOOR_LOG2W(arena->unscannedPages); - JS_ASSERT(pageIndex < GC_PAGE_COUNT); - pi = (JSGCPageInfo *)(FIRST_THING_PAGE(arena) + - pageIndex * GC_PAGE_SIZE); - JS_ASSERT(pi->unscannedBitmap); - chunkIndex = JS_FLOOR_LOG2W(pi->unscannedBitmap); - pi->unscannedBitmap &= ~((jsuword)1 << chunkIndex); - if (pi->unscannedBitmap == 0) - arena->unscannedPages &= ~((jsuword)1 << pageIndex); - thingOffset = (pageGap - + chunkIndex * thingsPerUnscannedChunk * thingSize); - JS_ASSERT(thingOffset >= sizeof(JSGCPageInfo)); - thingLimit = thingOffset + thingsPerUnscannedChunk * thingSize; - if (thingsPerUnscannedChunk != 1) { - /* - * thingLimit can go beyond the last allocated thing for the - * last chunk as the real limit can be inside the chunk. - */ - if (arena->list->last == arena && - arena->list->lastLimit < (pageIndex * GC_PAGE_SIZE + - thingLimit)) { - thingLimit = (arena->list->lastLimit - - pageIndex * GC_PAGE_SIZE); - } else if (thingLimit > GC_PAGE_SIZE) { - thingLimit = GC_PAGE_SIZE; - } - JS_ASSERT(thingLimit > thingOffset); - } - JS_ASSERT(arena->list->last != arena || - arena->list->lastLimit >= (pageIndex * GC_PAGE_SIZE + - thingLimit)); - JS_ASSERT(thingLimit <= GC_PAGE_SIZE); - - for (; thingOffset != thingLimit; thingOffset += thingSize) { - /* - * XXX: inline js_GetGCThingFlags() to use already available - * pi. - */ - thing = (void *)((jsuword)pi + thingOffset); - flagp = js_GetGCThingFlags(thing); - if (thingsPerUnscannedChunk != 1) { - /* - * Skip free or already scanned things that share the chunk - * with unscanned ones. - */ - if ((*flagp & (GCF_MARK|GCF_FINAL)) != (GCF_MARK|GCF_FINAL)) - continue; - } - JS_ASSERT((*flagp & (GCF_MARK|GCF_FINAL)) - == (GCF_MARK|GCF_FINAL)); - *flagp &= ~GCF_FINAL; -#ifdef DEBUG - JS_ASSERT(rt->gcUnscannedBagSize != 0); - --rt->gcUnscannedBagSize; - - /* - * Check that GC thing type is consistent with the type of - * things that can be put to the unscanned bag. - */ - switch (*flagp & GCF_TYPEMASK) { - case GCX_OBJECT: -# if JS_HAS_XML_SUPPORT - case GCX_NAMESPACE: - case GCX_QNAME: - case GCX_XML: -# endif - break; - default: - JS_ASSERT(0); - } -#endif - MarkGCThingChildren(cx, thing, flagp, JS_FALSE); - } - } - /* - * We finished scanning of the arena but we can only pop it from - * the stack if the arena is the stack's top. - * - * When MarkGCThingChildren from the above calls - * AddThingToUnscannedBag and the latter pushes new arenas to the - * stack, we have to skip popping of this arena until it becomes - * the top of the stack again. - */ - if (arena == rt->gcUnscannedArenaStackTop) { - prevArena = arena->prevUnscanned; - arena->prevUnscanned = NULL; - if (arena == prevArena) { - /* - * prevUnscanned points to itself and we reached the bottom - * of the stack. - */ - break; - } - rt->gcUnscannedArenaStackTop = arena = prevArena; - } else { - arena = rt->gcUnscannedArenaStackTop; - } - if (arena->list->thingSize != thingSize) - goto init_size; - } - JS_ASSERT(rt->gcUnscannedArenaStackTop); - JS_ASSERT(!rt->gcUnscannedArenaStackTop->prevUnscanned); - rt->gcUnscannedArenaStackTop = NULL; - JS_ASSERT(rt->gcUnscannedBagSize == 0); -} - -void -js_MarkGCThing(JSContext *cx, void *thing) -{ - uint8 *flagp; - - if (!thing) - return; - - flagp = js_GetGCThingFlags(thing); - JS_ASSERT(*flagp != GCF_FINAL); - if (*flagp & GCF_MARK) - return; - *flagp |= GCF_MARK; - - if (!cx->insideGCMarkCallback) { - MarkGCThingChildren(cx, thing, flagp, JS_TRUE); - } else { - /* - * For API compatibility we allow for the callback to assume that - * after it calls js_MarkGCThing for the last time, the callback - * can start to finalize its own objects that are only referenced - * by unmarked GC things. - * - * Since we do not know which call from inside the callback is the - * last, we ensure that the unscanned bag is always empty when we - * return to the callback and all marked things are scanned. - * - * As an optimization we do not check for the stack size here and - * pass JS_FALSE as the last argument to MarkGCThingChildren. - * Otherwise with low C stack the thing would be pushed to the bag - * just to be feed to MarkGCThingChildren from inside - * ScanDelayedChildren. - */ - cx->insideGCMarkCallback = JS_FALSE; - MarkGCThingChildren(cx, thing, flagp, JS_FALSE); - ScanDelayedChildren(cx); - cx->insideGCMarkCallback = JS_TRUE; - } -} - -JS_STATIC_DLL_CALLBACK(JSDHashOperator) -gc_root_marker(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 num, void *arg) -{ - JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; - jsval *rp = (jsval *)rhe->root; - jsval v = *rp; - - /* Ignore null object and scalar values. */ - if (!JSVAL_IS_NULL(v) && JSVAL_IS_GCTHING(v)) { - JSContext *cx = (JSContext *)arg; -#ifdef DEBUG - JSBool root_points_to_gcArenaList = JS_FALSE; - jsuword thing = (jsuword) JSVAL_TO_GCTHING(v); - uintN i; - JSGCArenaList *arenaList; - JSGCArena *a; - size_t limit; - - for (i = 0; i < GC_NUM_FREELISTS; i++) { - arenaList = &cx->runtime->gcArenaList[i]; - limit = arenaList->lastLimit; - for (a = arenaList->last; a; a = a->prev) { - if (thing - FIRST_THING_PAGE(a) < limit) { - root_points_to_gcArenaList = JS_TRUE; - break; - } - limit = GC_THINGS_SIZE; - } - } - if (!root_points_to_gcArenaList && rhe->name) { - fprintf(stderr, -"JS API usage error: the address passed to JS_AddNamedRoot currently holds an\n" -"invalid jsval. This is usually caused by a missing call to JS_RemoveRoot.\n" -"The root's name is \"%s\".\n", - rhe->name); - } - JS_ASSERT(root_points_to_gcArenaList); -#endif - - GC_MARK(cx, JSVAL_TO_GCTHING(v), rhe->name ? rhe->name : "root"); - } - return JS_DHASH_NEXT; -} - -JS_STATIC_DLL_CALLBACK(JSDHashOperator) -gc_lock_marker(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 num, void *arg) -{ - JSGCLockHashEntry *lhe = (JSGCLockHashEntry *)hdr; - void *thing = (void *)lhe->thing; - JSContext *cx = (JSContext *)arg; - - GC_MARK(cx, thing, "locked object"); - return JS_DHASH_NEXT; -} - -#define GC_MARK_JSVALS(cx, len, vec, name) \ - JS_BEGIN_MACRO \ - jsval _v, *_vp, *_end; \ - \ - for (_vp = vec, _end = _vp + len; _vp < _end; _vp++) { \ - _v = *_vp; \ - if (JSVAL_IS_GCTHING(_v)) \ - GC_MARK(cx, JSVAL_TO_GCTHING(_v), name); \ - } \ - JS_END_MACRO - -void -js_MarkStackFrame(JSContext *cx, JSStackFrame *fp) -{ - uintN depth, nslots; - - if (fp->callobj) - GC_MARK(cx, fp->callobj, "call object"); - if (fp->argsobj) - GC_MARK(cx, fp->argsobj, "arguments object"); - if (fp->varobj) - GC_MARK(cx, fp->varobj, "variables object"); - if (fp->script) { - js_MarkScript(cx, fp->script); - if (fp->spbase) { - /* - * Don't mark what has not been pushed yet, or what has been - * popped already. - */ - depth = fp->script->depth; - nslots = (JS_UPTRDIFF(fp->sp, fp->spbase) - < depth * sizeof(jsval)) - ? (uintN)(fp->sp - fp->spbase) - : depth; - GC_MARK_JSVALS(cx, nslots, fp->spbase, "operand"); - } - } - - /* Allow for primitive this parameter due to JSFUN_THISP_* flags. */ - JS_ASSERT(JSVAL_IS_OBJECT((jsval)fp->thisp) || - (fp->fun && JSFUN_THISP_FLAGS(fp->fun->flags))); - if (JSVAL_IS_GCTHING((jsval)fp->thisp)) - GC_MARK(cx, JSVAL_TO_GCTHING((jsval)fp->thisp), "this"); - - /* - * Mark fp->argv, even though in the common case it will be marked via our - * caller's frame, or via a JSStackHeader if fp was pushed by an external - * invocation. - * - * The hard case is when there is not enough contiguous space in the stack - * arena for actual, missing formal, and local root (JSFunctionSpec.extra) - * slots. In this case, fp->argv points to new space in a new arena, and - * marking the caller's operand stack, or an external caller's allocated - * stack tracked by a JSStackHeader, will not mark all the values stored - * and addressable via fp->argv. - * - * So in summary, solely for the hard case of moving argv due to missing - * formals and extra roots, we must mark actuals, missing formals, and any - * local roots arrayed at fp->argv here. - * - * It would be good to avoid redundant marking of the same reference, in - * the case where fp->argv does point into caller-allocated space tracked - * by fp->down->spbase or cx->stackHeaders. This would allow callbacks - * such as the forthcoming rt->gcThingCallback (bug 333078) to compute JS - * reference counts. So this comment deserves a FIXME bug to cite. - */ - if (fp->argv) { - nslots = fp->argc; - if (fp->fun) { - if (fp->fun->nargs > nslots) - nslots = fp->fun->nargs; - if (!FUN_INTERPRETED(fp->fun)) - nslots += fp->fun->u.n.extra; - } - GC_MARK_JSVALS(cx, nslots + 2, fp->argv - 2, "arg"); - } - if (JSVAL_IS_GCTHING(fp->rval)) - GC_MARK(cx, JSVAL_TO_GCTHING(fp->rval), "rval"); - if (fp->vars) - GC_MARK_JSVALS(cx, fp->nvars, fp->vars, "var"); - GC_MARK(cx, fp->scopeChain, "scope chain"); - if (fp->sharpArray) - GC_MARK(cx, fp->sharpArray, "sharp array"); - - if (fp->xmlNamespace) - GC_MARK(cx, fp->xmlNamespace, "xmlNamespace"); -} - -static void -MarkWeakRoots(JSContext *cx, JSWeakRoots *wr) -{ - uintN i; - void *thing; - - for (i = 0; i < GCX_NTYPES; i++) - GC_MARK(cx, wr->newborn[i], gc_typenames[i]); - if (wr->lastAtom) - GC_MARK_ATOM(cx, wr->lastAtom); - if (JSVAL_IS_GCTHING(wr->lastInternalResult)) { - thing = JSVAL_TO_GCTHING(wr->lastInternalResult); - if (thing) - GC_MARK(cx, thing, "lastInternalResult"); - } -} - -/* - * When gckind is GC_LAST_DITCH, it indicates a call from js_NewGCThing with - * rt->gcLock already held and when the lock should be kept on return. - */ -void -js_GC(JSContext *cx, JSGCInvocationKind gckind) -{ - JSRuntime *rt; - JSBool keepAtoms; - uintN i, type; - JSContext *iter, *acx; -#if JS_HAS_GENERATORS - JSGenerator **genTodoTail; -#endif - JSStackFrame *fp, *chain; - JSStackHeader *sh; - JSTempValueRooter *tvr; - size_t nbytes, limit, offset; - JSGCArena *a, **ap; - uint8 flags, *flagp, *firstPage; - JSGCThing *thing, *freeList; - JSGCArenaList *arenaList; - GCFinalizeOp finalizer; - JSBool allClear; -#ifdef JS_THREADSAFE - uint32 requestDebit; -#endif - - rt = cx->runtime; -#ifdef JS_THREADSAFE - /* Avoid deadlock. */ - JS_ASSERT(!JS_IS_RUNTIME_LOCKED(rt)); -#endif - - if (gckind == GC_LAST_DITCH) { - /* The last ditch GC preserves all atoms and weak roots. */ - keepAtoms = JS_TRUE; - } else { - JS_CLEAR_WEAK_ROOTS(&cx->weakRoots); - rt->gcPoke = JS_TRUE; - - /* Keep atoms when a suspended compile is running on another context. */ - keepAtoms = (rt->gcKeepAtoms != 0); - } - - /* - * Don't collect garbage if the runtime isn't up, and cx is not the last - * context in the runtime. The last context must force a GC, and nothing - * should suppress that final collection or there may be shutdown leaks, - * or runtime bloat until the next context is created. - */ - if (rt->state != JSRTS_UP && gckind != GC_LAST_CONTEXT) - return; - - restart_after_callback: - /* - * Let the API user decide to defer a GC if it wants to (unless this - * is the last context). Invoke the callback regardless. - */ - if (rt->gcCallback && - !rt->gcCallback(cx, JSGC_BEGIN) && - gckind != GC_LAST_CONTEXT) { - return; - } - - /* Lock out other GC allocator and collector invocations. */ - if (gckind != GC_LAST_DITCH) - JS_LOCK_GC(rt); - - /* Do nothing if no mutator has executed since the last GC. */ - if (!rt->gcPoke) { - METER(rt->gcStats.nopoke++); - if (gckind != GC_LAST_DITCH) - JS_UNLOCK_GC(rt); - return; - } - METER(rt->gcStats.poke++); - rt->gcPoke = JS_FALSE; - -#ifdef JS_THREADSAFE - JS_ASSERT(cx->thread->id == js_CurrentThreadId()); - - /* Bump gcLevel and return rather than nest on this thread. */ - if (rt->gcThread == cx->thread) { - JS_ASSERT(rt->gcLevel > 0); - rt->gcLevel++; - METER(if (rt->gcLevel > rt->gcStats.maxlevel) - rt->gcStats.maxlevel = rt->gcLevel); - if (gckind != GC_LAST_DITCH) - JS_UNLOCK_GC(rt); - return; - } - - /* - * If we're in one or more requests (possibly on more than one context) - * running on the current thread, indicate, temporarily, that all these - * requests are inactive. If cx->thread is NULL, then cx is not using - * the request model, and does not contribute to rt->requestCount. - */ - requestDebit = 0; - if (cx->thread) { - JSCList *head, *link; - - /* - * Check all contexts on cx->thread->contextList for active requests, - * counting each such context against requestDebit. - */ - head = &cx->thread->contextList; - for (link = head->next; link != head; link = link->next) { - acx = CX_FROM_THREAD_LINKS(link); - JS_ASSERT(acx->thread == cx->thread); - if (acx->requestDepth) - requestDebit++; - } - } else { - /* - * We assert, but check anyway, in case someone is misusing the API. - * Avoiding the loop over all of rt's contexts is a win in the event - * that the GC runs only on request-less contexts with null threads, - * in a special thread such as might be used by the UI/DOM/Layout - * "mozilla" or "main" thread in Mozilla-the-browser. - */ - JS_ASSERT(cx->requestDepth == 0); - if (cx->requestDepth) - requestDebit = 1; - } - if (requestDebit) { - JS_ASSERT(requestDebit <= rt->requestCount); - rt->requestCount -= requestDebit; - if (rt->requestCount == 0) - JS_NOTIFY_REQUEST_DONE(rt); - } - - /* If another thread is already in GC, don't attempt GC; wait instead. */ - if (rt->gcLevel > 0) { - /* Bump gcLevel to restart the current GC, so it finds new garbage. */ - rt->gcLevel++; - METER(if (rt->gcLevel > rt->gcStats.maxlevel) - rt->gcStats.maxlevel = rt->gcLevel); - - /* Wait for the other thread to finish, then resume our request. */ - while (rt->gcLevel > 0) - JS_AWAIT_GC_DONE(rt); - if (requestDebit) - rt->requestCount += requestDebit; - if (gckind != GC_LAST_DITCH) - JS_UNLOCK_GC(rt); - return; - } - - /* No other thread is in GC, so indicate that we're now in GC. */ - rt->gcLevel = 1; - rt->gcThread = cx->thread; - - /* Wait for all other requests to finish. */ - while (rt->requestCount > 0) - JS_AWAIT_REQUEST_DONE(rt); - -#else /* !JS_THREADSAFE */ - - /* Bump gcLevel and return rather than nest; the outer gc will restart. */ - rt->gcLevel++; - METER(if (rt->gcLevel > rt->gcStats.maxlevel) - rt->gcStats.maxlevel = rt->gcLevel); - if (rt->gcLevel > 1) - return; - -#endif /* !JS_THREADSAFE */ - - /* - * Set rt->gcRunning here within the GC lock, and after waiting for any - * active requests to end, so that new requests that try to JS_AddRoot, - * JS_RemoveRoot, or JS_RemoveRootRT block in JS_BeginRequest waiting for - * rt->gcLevel to drop to zero, while request-less calls to the *Root* - * APIs block in js_AddRoot or js_RemoveRoot (see above in this file), - * waiting for GC to finish. - */ - rt->gcRunning = JS_TRUE; - JS_UNLOCK_GC(rt); - - /* Reset malloc counter. */ - rt->gcMallocBytes = 0; - - /* Drop atoms held by the property cache, and clear property weak links. */ - js_DisablePropertyCache(cx); - js_FlushPropertyCache(cx); -#ifdef DEBUG_scopemeters - { extern void js_DumpScopeMeters(JSRuntime *rt); - js_DumpScopeMeters(rt); - } -#endif - -#ifdef JS_THREADSAFE - /* - * Set all thread local freelists to NULL. We may visit a thread's - * freelist more than once. To avoid redundant clearing we unroll the - * current thread's step. - * - * Also, in case a JSScript wrapped within an object was finalized, we - * null acx->thread->gsnCache.script and finish the cache's hashtable. - * Note that js_DestroyScript, called from script_finalize, will have - * already cleared cx->thread->gsnCache above during finalization, so we - * don't have to here. - */ - memset(cx->thread->gcFreeLists, 0, sizeof cx->thread->gcFreeLists); - iter = NULL; - while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) { - if (!acx->thread || acx->thread == cx->thread) - continue; - memset(acx->thread->gcFreeLists, 0, sizeof acx->thread->gcFreeLists); - GSN_CACHE_CLEAR(&acx->thread->gsnCache); - } -#else - /* The thread-unsafe case just has to clear the runtime's GSN cache. */ - GSN_CACHE_CLEAR(&rt->gsnCache); -#endif - -restart: - rt->gcNumber++; - JS_ASSERT(!rt->gcUnscannedArenaStackTop); - JS_ASSERT(rt->gcUnscannedBagSize == 0); - - /* - * Mark phase. - */ - JS_DHashTableEnumerate(&rt->gcRootsHash, gc_root_marker, cx); - if (rt->gcLocksHash) - JS_DHashTableEnumerate(rt->gcLocksHash, gc_lock_marker, cx); - js_MarkAtomState(&rt->atomState, keepAtoms, gc_mark_atom_key_thing, cx); - js_MarkWatchPoints(cx); - js_MarkScriptFilenames(rt, keepAtoms); - js_MarkNativeIteratorStates(cx); - -#if JS_HAS_GENERATORS - genTodoTail = MarkScheduledGenerators(cx); - JS_ASSERT(!*genTodoTail); -#endif - - iter = NULL; - while ((acx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL) { - /* - * Iterate frame chain and dormant chains. Temporarily tack current - * frame onto the head of the dormant list to ease iteration. - * - * (NB: see comment on this whole "dormant" thing in js_Execute.) - */ - chain = acx->fp; - if (chain) { - JS_ASSERT(!chain->dormantNext); - chain->dormantNext = acx->dormantFrameChain; - } else { - chain = acx->dormantFrameChain; - } - - for (fp = chain; fp; fp = chain = chain->dormantNext) { - do { - js_MarkStackFrame(cx, fp); - } while ((fp = fp->down) != NULL); - } - - /* Cleanup temporary "dormant" linkage. */ - if (acx->fp) - acx->fp->dormantNext = NULL; - - /* Mark other roots-by-definition in acx. */ - GC_MARK(cx, acx->globalObject, "global object"); - MarkWeakRoots(cx, &acx->weakRoots); - if (acx->throwing) { - if (JSVAL_IS_GCTHING(acx->exception)) - GC_MARK(cx, JSVAL_TO_GCTHING(acx->exception), "exception"); - } else { - /* Avoid keeping GC-ed junk stored in JSContext.exception. */ - acx->exception = JSVAL_NULL; - } -#if JS_HAS_LVALUE_RETURN - if (acx->rval2set && JSVAL_IS_GCTHING(acx->rval2)) - GC_MARK(cx, JSVAL_TO_GCTHING(acx->rval2), "rval2"); -#endif - - for (sh = acx->stackHeaders; sh; sh = sh->down) { - METER(rt->gcStats.stackseg++); - METER(rt->gcStats.segslots += sh->nslots); - GC_MARK_JSVALS(cx, sh->nslots, JS_STACK_SEGMENT(sh), "stack"); - } - - if (acx->localRootStack) - js_MarkLocalRoots(cx, acx->localRootStack); - - for (tvr = acx->tempValueRooters; tvr; tvr = tvr->down) { - switch (tvr->count) { - case JSTVU_SINGLE: - if (JSVAL_IS_GCTHING(tvr->u.value)) { - GC_MARK(cx, JSVAL_TO_GCTHING(tvr->u.value), - "tvr->u.value"); - } - break; - case JSTVU_MARKER: - tvr->u.marker(cx, tvr); - break; - case JSTVU_SPROP: - MARK_SCOPE_PROPERTY(cx, tvr->u.sprop); - break; - case JSTVU_WEAK_ROOTS: - MarkWeakRoots(cx, tvr->u.weakRoots); - break; - default: - JS_ASSERT(tvr->count >= 0); - GC_MARK_JSVALS(cx, tvr->count, tvr->u.array, "tvr->u.array"); - } - } - - if (acx->sharpObjectMap.depth > 0) - js_GCMarkSharpMap(cx, &acx->sharpObjectMap); - } - -#ifdef DUMP_CALL_TABLE - js_DumpCallTable(cx); -#endif - - /* - * Mark children of things that caused too deep recursion during above - * marking phase. - */ - ScanDelayedChildren(cx); - -#if JS_HAS_GENERATORS - /* - * Close phase: search and mark part. See comments in - * FindAndMarkObjectsToClose for details. - */ - FindAndMarkObjectsToClose(cx, gckind, genTodoTail); - - /* - * Mark children of things that caused too deep recursion during the - * just-completed marking part of the close phase. - */ - ScanDelayedChildren(cx); -#endif - - JS_ASSERT(!cx->insideGCMarkCallback); - if (rt->gcCallback) { - cx->insideGCMarkCallback = JS_TRUE; - (void) rt->gcCallback(cx, JSGC_MARK_END); - JS_ASSERT(cx->insideGCMarkCallback); - cx->insideGCMarkCallback = JS_FALSE; - } - JS_ASSERT(rt->gcUnscannedBagSize == 0); - - /* Finalize iterator states before the objects they iterate over. */ - CloseIteratorStates(cx); - - /* - * Sweep phase. - * - * Finalize as we sweep, outside of rt->gcLock but with rt->gcRunning set - * so that any attempt to allocate a GC-thing from a finalizer will fail, - * rather than nest badly and leave the unmarked newborn to be swept. - * - * Finalize smaller objects before larger, to guarantee finalization of - * GC-allocated obj->slots after obj. See FreeSlots in jsobj.c. - */ - for (i = 0; i < GC_NUM_FREELISTS; i++) { - arenaList = &rt->gcArenaList[i]; - nbytes = GC_FREELIST_NBYTES(i); - limit = arenaList->lastLimit; - for (a = arenaList->last; a; a = a->prev) { - JS_ASSERT(!a->prevUnscanned); - JS_ASSERT(a->unscannedPages == 0); - firstPage = (uint8 *) FIRST_THING_PAGE(a); - for (offset = 0; offset != limit; offset += nbytes) { - if ((offset & GC_PAGE_MASK) == 0) { - JS_ASSERT(((JSGCPageInfo *)(firstPage + offset))-> - unscannedBitmap == 0); - offset += PAGE_THING_GAP(nbytes); - } - JS_ASSERT(offset < limit); - flagp = a->base + offset / sizeof(JSGCThing); - if (flagp >= firstPage) - flagp += GC_THINGS_SIZE; - flags = *flagp; - if (flags & GCF_MARK) { - *flagp &= ~GCF_MARK; - } else if (!(flags & (GCF_LOCK | GCF_FINAL))) { - /* Call the finalizer with GCF_FINAL ORed into flags. */ - type = flags & GCF_TYPEMASK; - finalizer = gc_finalizers[type]; - if (finalizer) { - thing = (JSGCThing *)(firstPage + offset); - *flagp = (uint8)(flags | GCF_FINAL); - if (type >= GCX_EXTERNAL_STRING) - js_PurgeDeflatedStringCache(rt, (JSString *)thing); - finalizer(cx, thing); - } - - /* Set flags to GCF_FINAL, signifying that thing is free. */ - *flagp = GCF_FINAL; - } - } - limit = GC_THINGS_SIZE; - } - } - - /* - * Sweep the runtime's property tree after finalizing objects, in case any - * had watchpoints referencing tree nodes. Then sweep atoms, which may be - * referenced from dead property ids. - */ - js_SweepScopeProperties(rt); - js_SweepAtomState(&rt->atomState); - - /* - * Sweep script filenames after sweeping functions in the generic loop - * above. In this way when a scripted function's finalizer destroys the - * script and calls rt->destroyScriptHook, the hook can still access the - * script's filename. See bug 323267. - */ - js_SweepScriptFilenames(rt); - - /* - * Free phase. - * Free any unused arenas and rebuild the JSGCThing freelist. - */ - for (i = 0; i < GC_NUM_FREELISTS; i++) { - arenaList = &rt->gcArenaList[i]; - ap = &arenaList->last; - a = *ap; - if (!a) - continue; - - allClear = JS_TRUE; - arenaList->freeList = NULL; - freeList = NULL; - METER(arenaList->stats.nthings = 0); - METER(arenaList->stats.freelen = 0); - - nbytes = GC_FREELIST_NBYTES(i); - limit = arenaList->lastLimit; - do { - METER(size_t nfree = 0); - firstPage = (uint8 *) FIRST_THING_PAGE(a); - for (offset = 0; offset != limit; offset += nbytes) { - if ((offset & GC_PAGE_MASK) == 0) - offset += PAGE_THING_GAP(nbytes); - JS_ASSERT(offset < limit); - flagp = a->base + offset / sizeof(JSGCThing); - if (flagp >= firstPage) - flagp += GC_THINGS_SIZE; - - if (*flagp != GCF_FINAL) { - allClear = JS_FALSE; - METER(++arenaList->stats.nthings); - } else { - thing = (JSGCThing *)(firstPage + offset); - thing->flagp = flagp; - thing->next = freeList; - freeList = thing; - METER(++nfree); - } - } - if (allClear) { - /* - * Forget just assembled free list head for the arena - * and destroy the arena itself. - */ - freeList = arenaList->freeList; - DestroyGCArena(rt, arenaList, ap); - } else { - allClear = JS_TRUE; - arenaList->freeList = freeList; - ap = &a->prev; - METER(arenaList->stats.freelen += nfree); - METER(arenaList->stats.totalfreelen += nfree); - METER(++arenaList->stats.totalarenas); - } - limit = GC_THINGS_SIZE; - } while ((a = *ap) != NULL); - } - - if (rt->gcCallback) - (void) rt->gcCallback(cx, JSGC_FINALIZE_END); -#ifdef DEBUG_srcnotesize - { extern void DumpSrcNoteSizeHist(); - DumpSrcNoteSizeHist(); - printf("GC HEAP SIZE %lu (%lu)\n", - (unsigned long)rt->gcBytes, (unsigned long)rt->gcPrivateBytes); - } -#endif - - JS_LOCK_GC(rt); - - /* - * We want to restart GC if js_GC was called recursively or if any of the - * finalizers called js_RemoveRoot or js_UnlockGCThingRT. - */ - if (rt->gcLevel > 1 || rt->gcPoke) { - rt->gcLevel = 1; - rt->gcPoke = JS_FALSE; - JS_UNLOCK_GC(rt); - goto restart; - } - js_EnablePropertyCache(cx); - rt->gcLevel = 0; - rt->gcLastBytes = rt->gcBytes; - rt->gcRunning = JS_FALSE; - -#ifdef JS_THREADSAFE - /* If we were invoked during a request, pay back the temporary debit. */ - if (requestDebit) - rt->requestCount += requestDebit; - rt->gcThread = NULL; - JS_NOTIFY_GC_DONE(rt); - - /* - * Unlock unless we have GC_LAST_DITCH which requires locked GC on return. - */ - if (gckind != GC_LAST_DITCH) - JS_UNLOCK_GC(rt); -#endif - - /* Execute JSGC_END callback outside the lock. */ - if (rt->gcCallback) { - JSWeakRoots savedWeakRoots; - JSTempValueRooter tvr; - - if (gckind == GC_LAST_DITCH) { - /* - * We allow JSGC_END implementation to force a full GC or allocate - * new GC things. Thus we must protect the weak roots from GC or - * overwrites. - */ - savedWeakRoots = cx->weakRoots; - JS_PUSH_TEMP_ROOT_WEAK_COPY(cx, &savedWeakRoots, &tvr); - JS_KEEP_ATOMS(rt); - JS_UNLOCK_GC(rt); - } - - (void) rt->gcCallback(cx, JSGC_END); - - if (gckind == GC_LAST_DITCH) { - JS_LOCK_GC(rt); - JS_UNKEEP_ATOMS(rt); - JS_POP_TEMP_ROOT(cx, &tvr); - } else if (gckind == GC_LAST_CONTEXT && rt->gcPoke) { - /* - * On shutdown iterate until JSGC_END callback stops creating - * garbage. - */ - goto restart_after_callback; - } - } -} - -void -js_UpdateMallocCounter(JSContext *cx, size_t nbytes) -{ - uint32 *pbytes, bytes; - -#ifdef JS_THREADSAFE - pbytes = &cx->thread->gcMallocBytes; -#else - pbytes = &cx->runtime->gcMallocBytes; -#endif - bytes = *pbytes; - *pbytes = ((uint32)-1 - bytes <= nbytes) ? (uint32)-1 : bytes + nbytes; -} diff --git a/spidermonkey/src/jsgc.h b/spidermonkey/src/jsgc.h deleted file mode 100644 index ec623a1..0000000 --- a/spidermonkey/src/jsgc.h +++ /dev/null @@ -1,368 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsgc_h___ -#define jsgc_h___ -/* - * JS Garbage Collector. - */ -#include "jsprvtd.h" -#include "jspubtd.h" -#include "jsdhash.h" -#include "jsutil.h" - -JS_BEGIN_EXTERN_C - -/* GC thing type indexes. */ -#define GCX_OBJECT 0 /* JSObject */ -#define GCX_STRING 1 /* JSString */ -#define GCX_DOUBLE 2 /* jsdouble */ -#define GCX_MUTABLE_STRING 3 /* JSString that's mutable -- - single-threaded only! */ -#define GCX_PRIVATE 4 /* private (unscanned) data */ -#define GCX_NAMESPACE 5 /* JSXMLNamespace */ -#define GCX_QNAME 6 /* JSXMLQName */ -#define GCX_XML 7 /* JSXML */ -#define GCX_EXTERNAL_STRING 8 /* JSString w/ external chars */ - -#define GCX_NTYPES_LOG2 4 /* type index bits */ -#define GCX_NTYPES JS_BIT(GCX_NTYPES_LOG2) - -/* GC flag definitions, must fit in 8 bits (type index goes in the low bits). */ -#define GCF_TYPEMASK JS_BITMASK(GCX_NTYPES_LOG2) -#define GCF_MARK JS_BIT(GCX_NTYPES_LOG2) -#define GCF_FINAL JS_BIT(GCX_NTYPES_LOG2 + 1) -#define GCF_SYSTEM JS_BIT(GCX_NTYPES_LOG2 + 2) -#define GCF_LOCKSHIFT (GCX_NTYPES_LOG2 + 3) /* lock bit shift */ -#define GCF_LOCK JS_BIT(GCF_LOCKSHIFT) /* lock request bit in API */ - -/* Pseudo-flag that modifies GCX_STRING to make GCX_MUTABLE_STRING. */ -#define GCF_MUTABLE 2 - -#if (GCX_STRING | GCF_MUTABLE) != GCX_MUTABLE_STRING -# error "mutable string type index botch!" -#endif - -extern uint8 * -js_GetGCThingFlags(void *thing); - -/* - * The sole purpose of the function is to preserve public API compatibility - * in JS_GetStringBytes which takes only single JSString* argument. - */ -JSRuntime* -js_GetGCStringRuntime(JSString *str); - -#if 1 -/* - * Since we're forcing a GC from JS_GC anyway, don't bother wasting cycles - * loading oldval. XXX remove implied force, fix jsinterp.c's "second arg - * ignored", etc. - */ -#define GC_POKE(cx, oldval) ((cx)->runtime->gcPoke = JS_TRUE) -#else -#define GC_POKE(cx, oldval) ((cx)->runtime->gcPoke = JSVAL_IS_GCTHING(oldval)) -#endif - -extern intN -js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop, - JSStringFinalizeOp newop); - -extern JSBool -js_InitGC(JSRuntime *rt, uint32 maxbytes); - -extern void -js_FinishGC(JSRuntime *rt); - -extern JSBool -js_AddRoot(JSContext *cx, void *rp, const char *name); - -extern JSBool -js_AddRootRT(JSRuntime *rt, void *rp, const char *name); - -extern JSBool -js_RemoveRoot(JSRuntime *rt, void *rp); - -#ifdef DEBUG -extern void -js_DumpNamedRoots(JSRuntime *rt, - void (*dump)(const char *name, void *rp, void *data), - void *data); -#endif - -extern uint32 -js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data); - -/* Table of pointers with count valid members. */ -typedef struct JSPtrTable { - size_t count; - void **array; -} JSPtrTable; - -extern JSBool -js_RegisterCloseableIterator(JSContext *cx, JSObject *obj); - -#if JS_HAS_GENERATORS - -/* - * Runtime state to support generators' close hooks. - */ -typedef struct JSGCCloseState { - /* - * Singly linked list of generators that are reachable from GC roots or - * were created after the last GC. - */ - JSGenerator *reachableList; - - /* - * Head of the queue of generators that have already become unreachable but - * whose close hooks are not yet run. - */ - JSGenerator *todoQueue; - -#ifndef JS_THREADSAFE - /* - * Flag indicating that the current thread is excuting a close hook for - * single thread case. - */ - JSBool runningCloseHook; -#endif -} JSGCCloseState; - -extern void -js_RegisterGenerator(JSContext *cx, JSGenerator *gen); - -extern JSBool -js_RunCloseHooks(JSContext *cx); - -#endif - -/* - * The private JSGCThing struct, which describes a gcFreeList element. - */ -struct JSGCThing { - JSGCThing *next; - uint8 *flagp; -}; - -#define GC_NBYTES_MAX (10 * sizeof(JSGCThing)) -#define GC_NUM_FREELISTS (GC_NBYTES_MAX / sizeof(JSGCThing)) -#define GC_FREELIST_NBYTES(i) (((i) + 1) * sizeof(JSGCThing)) -#define GC_FREELIST_INDEX(n) (((n) / sizeof(JSGCThing)) - 1) - -extern void * -js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes); - -extern JSBool -js_LockGCThing(JSContext *cx, void *thing); - -extern JSBool -js_LockGCThingRT(JSRuntime *rt, void *thing); - -extern JSBool -js_UnlockGCThingRT(JSRuntime *rt, void *thing); - -extern JSBool -js_IsAboutToBeFinalized(JSContext *cx, void *thing); - -extern void -js_MarkAtom(JSContext *cx, JSAtom *atom); - -/* We avoid a large number of unnecessary calls by doing the flag check first */ -#define GC_MARK_ATOM(cx, atom) \ - JS_BEGIN_MACRO \ - if (!((atom)->flags & ATOM_MARK)) \ - js_MarkAtom(cx, atom); \ - JS_END_MACRO - -/* - * Always use GC_MARK macro and never call js_MarkGCThing directly so - * when GC_MARK_DEBUG is defined the dump of live GC things does not miss - * a thing. - */ -extern void -js_MarkGCThing(JSContext *cx, void *thing); - -#ifdef GC_MARK_DEBUG - -# define GC_MARK(cx, thing, name) js_MarkNamedGCThing(cx, thing, name) - -extern void -js_MarkNamedGCThing(JSContext *cx, void *thing, const char *name); - -extern JS_FRIEND_DATA(FILE *) js_DumpGCHeap; -JS_EXTERN_DATA(void *) js_LiveThingToFind; - -#else - -# define GC_MARK(cx, thing, name) js_MarkGCThing(cx, thing) - -#endif - -extern void -js_MarkStackFrame(JSContext *cx, JSStackFrame *fp); - -/* - * Kinds of js_GC invocation. - */ -typedef enum JSGCInvocationKind { - /* Normal invocation. */ - GC_NORMAL, - - /* - * Called from js_DestroyContext for last JSContext in a JSRuntime, when - * it is imperative that rt->gcPoke gets cleared early in js_GC. - */ - GC_LAST_CONTEXT, - - /* - * Called from js_NewGCThing as a last-ditch GC attempt. See comments - * before js_GC definition for details. - */ - GC_LAST_DITCH -} JSGCInvocationKind; - -extern void -js_GC(JSContext *cx, JSGCInvocationKind gckind); - -/* Call this after succesful malloc of memory for GC-related things. */ -extern void -js_UpdateMallocCounter(JSContext *cx, size_t nbytes); - -#ifdef DEBUG_notme -#define JS_GCMETER 1 -#endif - -#ifdef JS_GCMETER - -typedef struct JSGCStats { -#ifdef JS_THREADSAFE - uint32 localalloc; /* number of succeeded allocations from local lists */ -#endif - uint32 alloc; /* number of allocation attempts */ - uint32 retry; /* allocation attempt retries after running the GC */ - uint32 retryhalt; /* allocation retries halted by the branch callback */ - uint32 fail; /* allocation failures */ - uint32 finalfail; /* finalizer calls allocator failures */ - uint32 lockborn; /* things born locked */ - uint32 lock; /* valid lock calls */ - uint32 unlock; /* valid unlock calls */ - uint32 depth; /* mark tail recursion depth */ - uint32 maxdepth; /* maximum mark tail recursion depth */ - uint32 cdepth; /* mark recursion depth of C functions */ - uint32 maxcdepth; /* maximum mark recursion depth of C functions */ - uint32 unscanned; /* mark C stack overflows or number of times - GC things were put in unscanned bag */ -#ifdef DEBUG - uint32 maxunscanned; /* maximum size of unscanned bag */ -#endif - uint32 maxlevel; /* maximum GC nesting (indirect recursion) level */ - uint32 poke; /* number of potentially useful GC calls */ - uint32 nopoke; /* useless GC calls where js_PokeGC was not set */ - uint32 afree; /* thing arenas freed so far */ - uint32 stackseg; /* total extraordinary stack segments scanned */ - uint32 segslots; /* total stack segment jsval slots scanned */ - uint32 nclose; /* number of objects with close hooks */ - uint32 maxnclose; /* max number of objects with close hooks */ - uint32 closelater; /* number of close hooks scheduled to run */ - uint32 maxcloselater; /* max number of close hooks scheduled to run */ -} JSGCStats; - -extern JS_FRIEND_API(void) -js_DumpGCStats(JSRuntime *rt, FILE *fp); - -#endif /* JS_GCMETER */ - -typedef struct JSGCArena JSGCArena; -typedef struct JSGCArenaList JSGCArenaList; - -#ifdef JS_GCMETER -typedef struct JSGCArenaStats JSGCArenaStats; - -struct JSGCArenaStats { - uint32 narenas; /* number of arena in list */ - uint32 maxarenas; /* maximun number of allocated arenas */ - uint32 nthings; /* number of allocates JSGCThing */ - uint32 maxthings; /* maximum number number of allocates JSGCThing */ - uint32 totalnew; /* number of succeeded calls to js_NewGCThing */ - uint32 freelen; /* freeList lengths */ - uint32 recycle; /* number of things recycled through freeList */ - uint32 totalarenas; /* total number of arenas with live things that - GC scanned so far */ - uint32 totalfreelen; /* total number of things that GC put to free - list so far */ -}; -#endif - -struct JSGCArenaList { - JSGCArena *last; /* last allocated GC arena */ - uint16 lastLimit; /* end offset of allocated so far things in - the last arena */ - uint16 thingSize; /* size of things to allocate on this list */ - JSGCThing *freeList; /* list of free GC things */ -#ifdef JS_GCMETER - JSGCArenaStats stats; -#endif -}; - -typedef struct JSWeakRoots { - /* Most recently created things by type, members of the GC's root set. */ - JSGCThing *newborn[GCX_NTYPES]; - - /* Atom root for the last-looked-up atom on this context. */ - JSAtom *lastAtom; - - /* Root for the result of the most recent js_InternalInvoke call. */ - jsval lastInternalResult; -} JSWeakRoots; - -JS_STATIC_ASSERT(JSVAL_NULL == 0); -#define JS_CLEAR_WEAK_ROOTS(wr) (memset((wr), 0, sizeof(JSWeakRoots))) - -#ifdef DEBUG_notme -#define TOO_MUCH_GC 1 -#endif - -#ifdef WAY_TOO_MUCH_GC -#define TOO_MUCH_GC 1 -#endif - -JS_END_EXTERN_C - -#endif /* jsgc_h___ */ diff --git a/spidermonkey/src/jshash.c b/spidermonkey/src/jshash.c deleted file mode 100644 index 8e25517..0000000 --- a/spidermonkey/src/jshash.c +++ /dev/null @@ -1,483 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * PR hash table package. - */ -#include "jsstddef.h" -#include -#include -#include "jstypes.h" -#include "jsbit.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jshash.h" /* Added by JSIFY */ - -/* Compute the number of buckets in ht */ -#define NBUCKETS(ht) JS_BIT(JS_HASH_BITS - (ht)->shift) - -/* The smallest table has 16 buckets */ -#define MINBUCKETSLOG2 4 -#define MINBUCKETS JS_BIT(MINBUCKETSLOG2) - -/* Compute the maximum entries given n buckets that we will tolerate, ~90% */ -#define OVERLOADED(n) ((n) - ((n) >> 3)) - -/* Compute the number of entries below which we shrink the table by half */ -#define UNDERLOADED(n) (((n) > MINBUCKETS) ? ((n) >> 2) : 0) - -/* -** Stubs for default hash allocator ops. -*/ -static void * -DefaultAllocTable(void *pool, size_t size) -{ - return malloc(size); -} - -static void -DefaultFreeTable(void *pool, void *item) -{ - free(item); -} - -static JSHashEntry * -DefaultAllocEntry(void *pool, const void *key) -{ - return (JSHashEntry*) malloc(sizeof(JSHashEntry)); -} - -static void -DefaultFreeEntry(void *pool, JSHashEntry *he, uintN flag) -{ - if (flag == HT_FREE_ENTRY) - free(he); -} - -static JSHashAllocOps defaultHashAllocOps = { - DefaultAllocTable, DefaultFreeTable, - DefaultAllocEntry, DefaultFreeEntry -}; - -JS_PUBLIC_API(JSHashTable *) -JS_NewHashTable(uint32 n, JSHashFunction keyHash, - JSHashComparator keyCompare, JSHashComparator valueCompare, - JSHashAllocOps *allocOps, void *allocPriv) -{ - JSHashTable *ht; - size_t nb; - - if (n <= MINBUCKETS) { - n = MINBUCKETSLOG2; - } else { - n = JS_CeilingLog2(n); - if ((int32)n < 0) - return NULL; - } - - if (!allocOps) allocOps = &defaultHashAllocOps; - - ht = (JSHashTable*) allocOps->allocTable(allocPriv, sizeof *ht); - if (!ht) - return NULL; - memset(ht, 0, sizeof *ht); - ht->shift = JS_HASH_BITS - n; - n = JS_BIT(n); - nb = n * sizeof(JSHashEntry *); - ht->buckets = (JSHashEntry**) allocOps->allocTable(allocPriv, nb); - if (!ht->buckets) { - allocOps->freeTable(allocPriv, ht); - return NULL; - } - memset(ht->buckets, 0, nb); - - ht->keyHash = keyHash; - ht->keyCompare = keyCompare; - ht->valueCompare = valueCompare; - ht->allocOps = allocOps; - ht->allocPriv = allocPriv; - return ht; -} - -JS_PUBLIC_API(void) -JS_HashTableDestroy(JSHashTable *ht) -{ - uint32 i, n; - JSHashEntry *he, **hep; - JSHashAllocOps *allocOps = ht->allocOps; - void *allocPriv = ht->allocPriv; - - n = NBUCKETS(ht); - for (i = 0; i < n; i++) { - hep = &ht->buckets[i]; - while ((he = *hep) != NULL) { - *hep = he->next; - allocOps->freeEntry(allocPriv, he, HT_FREE_ENTRY); - } - } -#ifdef DEBUG - memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]); -#endif - allocOps->freeTable(allocPriv, ht->buckets); -#ifdef DEBUG - memset(ht, 0xDB, sizeof *ht); -#endif - allocOps->freeTable(allocPriv, ht); -} - -/* - * Multiplicative hash, from Knuth 6.4. - */ -#define BUCKET_HEAD(ht, keyHash) \ - (&(ht)->buckets[((keyHash) * JS_GOLDEN_RATIO) >> (ht)->shift]) - -JS_PUBLIC_API(JSHashEntry **) -JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key) -{ - JSHashEntry *he, **hep, **hep0; - -#ifdef HASHMETER - ht->nlookups++; -#endif - hep = hep0 = BUCKET_HEAD(ht, keyHash); - while ((he = *hep) != NULL) { - if (he->keyHash == keyHash && ht->keyCompare(key, he->key)) { - /* Move to front of chain if not already there */ - if (hep != hep0) { - *hep = he->next; - he->next = *hep0; - *hep0 = he; - } - return hep0; - } - hep = &he->next; -#ifdef HASHMETER - ht->nsteps++; -#endif - } - return hep; -} - -static JSBool -Resize(JSHashTable *ht, uint32 newshift) -{ - size_t nb, nentries, i; - JSHashEntry **oldbuckets, *he, *next, **hep; -#ifdef DEBUG - size_t nold = NBUCKETS(ht); -#endif - - JS_ASSERT(newshift < JS_HASH_BITS); - - nb = (size_t)1 << (JS_HASH_BITS - newshift); - - /* Integer overflow protection. */ - if (nb > (size_t)-1 / sizeof(JSHashEntry*)) - return JS_FALSE; - nb *= sizeof(JSHashEntry*); - - oldbuckets = ht->buckets; - ht->buckets = (JSHashEntry**)ht->allocOps->allocTable(ht->allocPriv, nb); - if (!ht->buckets) { - ht->buckets = oldbuckets; - return JS_FALSE; - } - memset(ht->buckets, 0, nb); - - ht->shift = newshift; - nentries = ht->nentries; - - for (i = 0; nentries != 0; i++) { - for (he = oldbuckets[i]; he; he = next) { - JS_ASSERT(nentries != 0); - --nentries; - next = he->next; - hep = BUCKET_HEAD(ht, he->keyHash); - - /* - * Since he comes from the old table, it must be unique and we - * simply add it to the head of bucket chain without chain lookup. - */ - he->next = *hep; - *hep = he; - } - } -#ifdef DEBUG - memset(oldbuckets, 0xDB, nold * sizeof oldbuckets[0]); -#endif - ht->allocOps->freeTable(ht->allocPriv, oldbuckets); - return JS_TRUE; -} - -JS_PUBLIC_API(JSHashEntry *) -JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **hep, - JSHashNumber keyHash, const void *key, void *value) -{ - uint32 n; - JSHashEntry *he; - - /* Grow the table if it is overloaded */ - n = NBUCKETS(ht); - if (ht->nentries >= OVERLOADED(n)) { - if (!Resize(ht, ht->shift - 1)) - return NULL; -#ifdef HASHMETER - ht->ngrows++; -#endif - hep = JS_HashTableRawLookup(ht, keyHash, key); - } - - /* Make a new key value entry */ - he = ht->allocOps->allocEntry(ht->allocPriv, key); - if (!he) - return NULL; - he->keyHash = keyHash; - he->key = key; - he->value = value; - he->next = *hep; - *hep = he; - ht->nentries++; - return he; -} - -JS_PUBLIC_API(JSHashEntry *) -JS_HashTableAdd(JSHashTable *ht, const void *key, void *value) -{ - JSHashNumber keyHash; - JSHashEntry *he, **hep; - - keyHash = ht->keyHash(key); - hep = JS_HashTableRawLookup(ht, keyHash, key); - if ((he = *hep) != NULL) { - /* Hit; see if values match */ - if (ht->valueCompare(he->value, value)) { - /* key,value pair is already present in table */ - return he; - } - if (he->value) - ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_VALUE); - he->value = value; - return he; - } - return JS_HashTableRawAdd(ht, hep, keyHash, key, value); -} - -JS_PUBLIC_API(void) -JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he) -{ - uint32 n; - - *hep = he->next; - ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_ENTRY); - - /* Shrink table if it's underloaded */ - n = NBUCKETS(ht); - if (--ht->nentries < UNDERLOADED(n)) { - Resize(ht, ht->shift + 1); -#ifdef HASHMETER - ht->nshrinks++; -#endif - } -} - -JS_PUBLIC_API(JSBool) -JS_HashTableRemove(JSHashTable *ht, const void *key) -{ - JSHashNumber keyHash; - JSHashEntry *he, **hep; - - keyHash = ht->keyHash(key); - hep = JS_HashTableRawLookup(ht, keyHash, key); - if ((he = *hep) == NULL) - return JS_FALSE; - - /* Hit; remove element */ - JS_HashTableRawRemove(ht, hep, he); - return JS_TRUE; -} - -JS_PUBLIC_API(void *) -JS_HashTableLookup(JSHashTable *ht, const void *key) -{ - JSHashNumber keyHash; - JSHashEntry *he, **hep; - - keyHash = ht->keyHash(key); - hep = JS_HashTableRawLookup(ht, keyHash, key); - if ((he = *hep) != NULL) { - return he->value; - } - return NULL; -} - -/* -** Iterate over the entries in the hash table calling func for each -** entry found. Stop if "f" says to (return value & JS_ENUMERATE_STOP). -** Return a count of the number of elements scanned. -*/ -JS_PUBLIC_API(int) -JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg) -{ - JSHashEntry *he, **hep, **bucket; - uint32 nlimit, n, nbuckets, newlog2; - int rv; - - nlimit = ht->nentries; - n = 0; - for (bucket = ht->buckets; n != nlimit; ++bucket) { - hep = bucket; - while ((he = *hep) != NULL) { - JS_ASSERT(n < nlimit); - rv = f(he, n, arg); - n++; - if (rv & HT_ENUMERATE_REMOVE) { - *hep = he->next; - ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_ENTRY); - --ht->nentries; - } else { - hep = &he->next; - } - if (rv & HT_ENUMERATE_STOP) { - goto out; - } - } - } - -out: - /* Shrink table if removal of entries made it underloaded */ - if (ht->nentries != nlimit) { - JS_ASSERT(ht->nentries < nlimit); - nbuckets = NBUCKETS(ht); - if (MINBUCKETS < nbuckets && ht->nentries < UNDERLOADED(nbuckets)) { - newlog2 = JS_CeilingLog2(ht->nentries); - if (newlog2 < MINBUCKETSLOG2) - newlog2 = MINBUCKETSLOG2; - - /* Check that we really shrink the table. */ - JS_ASSERT(JS_HASH_BITS - ht->shift > newlog2); - Resize(ht, JS_HASH_BITS - newlog2); - } - } - return (int)n; -} - -#ifdef HASHMETER -#include -#include - -JS_PUBLIC_API(void) -JS_HashTableDumpMeter(JSHashTable *ht, JSHashEnumerator dump, FILE *fp) -{ - double sqsum, mean, variance, sigma; - uint32 nchains, nbuckets, nentries; - uint32 i, n, maxChain, maxChainLen; - JSHashEntry *he; - - sqsum = 0; - nchains = 0; - maxChainLen = 0; - nbuckets = NBUCKETS(ht); - for (i = 0; i < nbuckets; i++) { - he = ht->buckets[i]; - if (!he) - continue; - nchains++; - for (n = 0; he; he = he->next) - n++; - sqsum += n * n; - if (n > maxChainLen) { - maxChainLen = n; - maxChain = i; - } - } - nentries = ht->nentries; - mean = (double)nentries / nchains; - variance = nchains * sqsum - nentries * nentries; - if (variance < 0 || nchains == 1) - variance = 0; - else - variance /= nchains * (nchains - 1); - sigma = sqrt(variance); - - fprintf(fp, "\nHash table statistics:\n"); - fprintf(fp, " number of lookups: %u\n", ht->nlookups); - fprintf(fp, " number of entries: %u\n", ht->nentries); - fprintf(fp, " number of grows: %u\n", ht->ngrows); - fprintf(fp, " number of shrinks: %u\n", ht->nshrinks); - fprintf(fp, " mean steps per hash: %g\n", (double)ht->nsteps - / ht->nlookups); - fprintf(fp, "mean hash chain length: %g\n", mean); - fprintf(fp, " standard deviation: %g\n", sigma); - fprintf(fp, " max hash chain length: %u\n", maxChainLen); - fprintf(fp, " max hash chain: [%u]\n", maxChain); - - for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++) - if (dump(he, i, fp) != HT_ENUMERATE_NEXT) - break; -} -#endif /* HASHMETER */ - -JS_PUBLIC_API(int) -JS_HashTableDump(JSHashTable *ht, JSHashEnumerator dump, FILE *fp) -{ - int count; - - count = JS_HashTableEnumerateEntries(ht, dump, fp); -#ifdef HASHMETER - JS_HashTableDumpMeter(ht, dump, fp); -#endif - return count; -} - -JS_PUBLIC_API(JSHashNumber) -JS_HashString(const void *key) -{ - JSHashNumber h; - const unsigned char *s; - - h = 0; - for (s = (const unsigned char *)key; *s; s++) - h = (h >> (JS_HASH_BITS - 4)) ^ (h << 4) ^ *s; - return h; -} - -JS_PUBLIC_API(int) -JS_CompareValues(const void *v1, const void *v2) -{ - return v1 == v2; -} diff --git a/spidermonkey/src/jshash.h b/spidermonkey/src/jshash.h deleted file mode 100644 index 2a125e1..0000000 --- a/spidermonkey/src/jshash.h +++ /dev/null @@ -1,151 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jshash_h___ -#define jshash_h___ -/* - * API to portable hash table code. - */ -#include -#include -#include "jstypes.h" -#include "jscompat.h" - -JS_BEGIN_EXTERN_C - -typedef uint32 JSHashNumber; -typedef struct JSHashEntry JSHashEntry; -typedef struct JSHashTable JSHashTable; - -#define JS_HASH_BITS 32 -#define JS_GOLDEN_RATIO 0x9E3779B9U - -typedef JSHashNumber (* JS_DLL_CALLBACK JSHashFunction)(const void *key); -typedef intN (* JS_DLL_CALLBACK JSHashComparator)(const void *v1, const void *v2); -typedef intN (* JS_DLL_CALLBACK JSHashEnumerator)(JSHashEntry *he, intN i, void *arg); - -/* Flag bits in JSHashEnumerator's return value */ -#define HT_ENUMERATE_NEXT 0 /* continue enumerating entries */ -#define HT_ENUMERATE_STOP 1 /* stop enumerating entries */ -#define HT_ENUMERATE_REMOVE 2 /* remove and free the current entry */ - -typedef struct JSHashAllocOps { - void * (*allocTable)(void *pool, size_t size); - void (*freeTable)(void *pool, void *item); - JSHashEntry * (*allocEntry)(void *pool, const void *key); - void (*freeEntry)(void *pool, JSHashEntry *he, uintN flag); -} JSHashAllocOps; - -#define HT_FREE_VALUE 0 /* just free the entry's value */ -#define HT_FREE_ENTRY 1 /* free value and entire entry */ - -struct JSHashEntry { - JSHashEntry *next; /* hash chain linkage */ - JSHashNumber keyHash; /* key hash function result */ - const void *key; /* ptr to opaque key */ - void *value; /* ptr to opaque value */ -}; - -struct JSHashTable { - JSHashEntry **buckets; /* vector of hash buckets */ - uint32 nentries; /* number of entries in table */ - uint32 shift; /* multiplicative hash shift */ - JSHashFunction keyHash; /* key hash function */ - JSHashComparator keyCompare; /* key comparison function */ - JSHashComparator valueCompare; /* value comparison function */ - JSHashAllocOps *allocOps; /* allocation operations */ - void *allocPriv; /* allocation private data */ -#ifdef HASHMETER - uint32 nlookups; /* total number of lookups */ - uint32 nsteps; /* number of hash chains traversed */ - uint32 ngrows; /* number of table expansions */ - uint32 nshrinks; /* number of table contractions */ -#endif -}; - -/* - * Create a new hash table. - * If allocOps is null, use default allocator ops built on top of malloc(). - */ -extern JS_PUBLIC_API(JSHashTable *) -JS_NewHashTable(uint32 n, JSHashFunction keyHash, - JSHashComparator keyCompare, JSHashComparator valueCompare, - JSHashAllocOps *allocOps, void *allocPriv); - -extern JS_PUBLIC_API(void) -JS_HashTableDestroy(JSHashTable *ht); - -/* Low level access methods */ -extern JS_PUBLIC_API(JSHashEntry **) -JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key); - -extern JS_PUBLIC_API(JSHashEntry *) -JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **hep, JSHashNumber keyHash, - const void *key, void *value); - -extern JS_PUBLIC_API(void) -JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he); - -/* Higher level access methods */ -extern JS_PUBLIC_API(JSHashEntry *) -JS_HashTableAdd(JSHashTable *ht, const void *key, void *value); - -extern JS_PUBLIC_API(JSBool) -JS_HashTableRemove(JSHashTable *ht, const void *key); - -extern JS_PUBLIC_API(intN) -JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg); - -extern JS_PUBLIC_API(void *) -JS_HashTableLookup(JSHashTable *ht, const void *key); - -extern JS_PUBLIC_API(intN) -JS_HashTableDump(JSHashTable *ht, JSHashEnumerator dump, FILE *fp); - -/* General-purpose C string hash function. */ -extern JS_PUBLIC_API(JSHashNumber) -JS_HashString(const void *key); - -/* Stub function just returns v1 == v2 */ -extern JS_PUBLIC_API(intN) -JS_CompareValues(const void *v1, const void *v2); - -JS_END_EXTERN_C - -#endif /* jshash_h___ */ diff --git a/spidermonkey/src/jsify.pl b/spidermonkey/src/jsify.pl deleted file mode 100644 index fa7f4f8..0000000 --- a/spidermonkey/src/jsify.pl +++ /dev/null @@ -1,485 +0,0 @@ -#!/usr/local/bin/perl - -# This script modifies C code to use the hijacked NSPR routines that are -# now baked into the JavaScript engine rather than using the NSPR -# routines that they were based on, i.e. types like PRArenaPool are changed -# to JSArenaPool. -# -# This script was used in 9/98 to facilitate the incorporation of some NSPR -# code into the JS engine so as to minimize dependency on NSPR. -# - -# Command-line: jsify.pl [options] [filename]* -# -# Options: -# -r Reverse direction of transformation, i.e. JS ==> NSPR2 -# -outdir Directory in which to place output files - - -# NSPR2 symbols that will be modified to JS symbols, e.g. -# PRArena <==> JSArena - -@NSPR_symbols = ( -"PRArena", -"PRArenaPool", -"PRArenaStats", -"PR_ARENAMETER", -"PR_ARENA_", -"PR_ARENA_ALIGN", -"PR_ARENA_ALLOCATE", -"PR_ARENA_CONST_ALIGN_MASK", -"PR_ARENA_DEFAULT_ALIGN", -"PR_ARENA_DESTROY", -"PR_ARENA_GROW", -"PR_ARENA_MARK", -"PR_ARENA_RELEASE", - -"PR_smprintf", -"PR_smprintf_free", -"PR_snprintf", -"PR_sprintf_append", -"PR_sscanf", -"PR_sxprintf", -"PR_vsmprintf", -"PR_vsnprintf", -"PR_vsprintf_append", -"PR_vsxprintf", - -"PRCList", -"PRCListStr", -"PRCLists", - -"PRDestroyEventProc", -"PREvent", -"PREventFunProc", -"PREventQueue", -"PRHandleEventProc", -"PR_PostEvent", -"PR_PostSynchronousEvent", -"PR_ProcessPendingEvents", -"PR_CreateEventQueue", -"PR_DequeueEvent", -"PR_DestroyEvent", -"PR_DestroyEventQueue", -"PR_EventAvailable", -"PR_EventLoop", -"PR_GetEvent", -"PR_GetEventOwner", -"PR_GetEventQueueMonitor", -"PR_GetEventQueueSelectFD", -"PR_GetMainEventQueue", -"PR_HandleEvent", -"PR_InitEvent", -"PR_ENTER_EVENT_QUEUE_MONITOR", -"PR_EXIT_EVENT_QUEUE_MONITOR", -"PR_MapEvents", -"PR_RevokeEvents", - -"PR_cnvtf", -"PR_dtoa", -"PR_strtod", - -"PRFileDesc", - -"PR_HASH_BITS", -"PR_GOLDEN_RATIO", -"PRHashAllocOps", -"PRHashComparator", -"PRHashEntry", -"PRHashEnumerator", -"PRHashFunction", -"PRHashNumber", -"PRHashTable", -"PR_HashString", -"PR_HashTableAdd", -"PR_HashTableDestroy", -"PR_HashTableDump", -"PR_HashTableEnumerateEntries", -"PR_HashTableLookup", -"PR_HashTableRawAdd", -"PR_HashTableRawLookup", -"PR_HashTableRawRemove", -"PR_HashTableRemove", - -"PRBool", -"PRFloat64", -"PRInt16", -"PRInt32", -"PRInt64", -"PRInt8", -"PRIntn", -"PRUint16", -"PRUint32", -"PRUint64", -"PRUint8", -"PRUintn", -"PRPtrDiff", -"PRPtrdiff", -"PRUptrdiff", -"PRUword", -"PRWord", -"PRPackedBool", -"PRSize", -"PRStatus", -"pruword", -"prword", -"prword_t", - -"PR_ALIGN_OF_DOUBLE", -"PR_ALIGN_OF_FLOAT", -"PR_ALIGN_OF_INT", -"PR_ALIGN_OF_INT64", -"PR_ALIGN_OF_LONG", -"PR_ALIGN_OF_POINTER", -"PR_ALIGN_OF_SHORT", -"PR_ALIGN_OF_WORD", -"PR_BITS_PER_BYTE", -"PR_BITS_PER_BYTE_LOG2", -"PR_BITS_PER_DOUBLE", -"PR_BITS_PER_DOUBLE_LOG2", -"PR_BITS_PER_FLOAT", -"PR_BITS_PER_FLOAT_LOG2", -"PR_BITS_PER_INT", -"PR_BITS_PER_INT64", -"PR_BITS_PER_INT64_LOG2", -"PR_BITS_PER_INT_LOG2", -"PR_BITS_PER_LONG", -"PR_BITS_PER_LONG_LOG2", -"PR_BITS_PER_SHORT", -"PR_BITS_PER_SHORT_LOG2", -"PR_BITS_PER_WORD", -"PR_BITS_PER_WORD_LOG2", -"PR_BYTES_PER_BYTE", -"PR_BYTES_PER_DOUBLE", -"PR_BYTES_PER_DWORD", -"PR_BYTES_PER_DWORD_LOG2", -"PR_BYTES_PER_FLOAT", -"PR_BYTES_PER_INT", -"PR_BYTES_PER_INT64", -"PR_BYTES_PER_LONG", -"PR_BYTES_PER_SHORT", -"PR_BYTES_PER_WORD", -"PR_BYTES_PER_WORD_LOG2", - -"PRSegment", -"PRSegmentAccess", -"PRStuffFunc", -"PRThread", - -"PR_APPEND_LINK", - -"PR_ASSERT", - -"PR_ATOMIC_DWORD_LOAD", -"PR_ATOMIC_DWORD_STORE", - -"PR_Abort", - -"PR_ArenaAllocate", -"PR_ArenaCountAllocation", -"PR_ArenaCountGrowth", -"PR_ArenaCountInplaceGrowth", -"PR_ArenaCountRelease", -"PR_ArenaCountRetract", -"PR_ArenaFinish", -"PR_ArenaGrow", -"PR_ArenaRelease", -"PR_CompactArenaPool", -"PR_DumpArenaStats", -"PR_FinishArenaPool", -"PR_FreeArenaPool", -"PR_InitArenaPool", - -"PR_Assert", - -"PR_AttachThread", - -"PR_BEGIN_EXTERN_C", -"PR_BEGIN_MACRO", - -"PR_BIT", -"PR_BITMASK", - -"PR_BUFFER_OVERFLOW_ERROR", - -"PR_CALLBACK", -"PR_CALLBACK_DECL", -"PR_CALLOC", -"PR_CEILING_LOG2", -"PR_CLEAR_ARENA", -"PR_CLEAR_BIT", -"PR_CLEAR_UNUSED", -"PR_CLIST_IS_EMPTY", -"PR_COUNT_ARENA", -"PR_CURRENT_THREAD", - -"PR_GetSegmentAccess", -"PR_GetSegmentSize", -"PR_GetSegmentVaddr", -"PR_GrowSegment", -"PR_DestroySegment", -"PR_MapSegment", -"PR_NewSegment", -"PR_Segment", -"PR_Seg", -"PR_SEGMENT_NONE", -"PR_SEGMENT_RDONLY", -"PR_SEGMENT_RDWR", - -"PR_Calloc", -"PR_CeilingLog2", -"PR_CompareStrings", -"PR_CompareValues", -"PR_DELETE", -"PR_END_EXTERN_C", -"PR_END_MACRO", -"PR_ENUMERATE_STOP", -"PR_FAILURE", -"PR_FALSE", -"PR_FLOOR_LOG2", -"PR_FREEIF", -"PR_FREE_PATTERN", -"PR_FloorLog2", -"PR_FormatTime", -"PR_Free", - -"PR_GetEnv", -"PR_GetError", -"PR_INIT_ARENA_POOL", -"PR_INIT_CLIST", -"PR_INIT_STATIC_CLIST", -"PR_INLINE", -"PR_INSERT_AFTER", -"PR_INSERT_BEFORE", -"PR_INSERT_LINK", -"PR_INT32", -"PR_INTERVAL_NO_TIMEOUT", -"PR_INTERVAL_NO_WAIT", -"PR_Init", -"PR_LIST_HEAD", -"PR_LIST_TAIL", -"PR_LOG", -"PR_LOGGING", -"PR_LOG_ALWAYS", -"PR_LOG_BEGIN", -"PR_LOG_DEBUG", -"PR_LOG_DEFINE", -"PR_LOG_END", -"PR_LOG_ERROR", -"PR_LOG_MAX", -"PR_LOG_MIN", -"PR_LOG_NONE", -"PR_LOG_NOTICE", -"PR_LOG_TEST", -"PR_LOG_WARN", -"PR_LOG_WARNING", -"PR_LogFlush", -"PR_LogPrint", -"PR_MALLOC", -"PR_MAX", -"PR_MD_calloc", -"PR_MD_free", -"PR_MD_malloc", -"PR_MD_realloc", -"PR_MIN", -"PR_Malloc", -"PR_NEW", -"PR_NEWZAP", -"PR_NEXT_LINK", -"PR_NOT_REACHED", -"PR_NewCondVar", -"PR_NewHashTable", -"PR_NewLogModule", -"PR_PREV_LINK", -"PR_PUBLIC_API", -"PR_PUBLIC_DATA", -"PR_RANGE_ERROR", -"PR_REALLOC", -"PR_REMOVE_AND_INIT_LINK", -"PR_REMOVE_LINK", -"PR_ROUNDUP", -"PR_Realloc", - -"PR_SET_BIT", -"PR_STATIC_CALLBACK", -"PR_SUCCESS", -"PR_SetError", -"PR_SetLogBuffering", -"PR_SetLogFile", - -"PR_TEST_BIT", -"PR_TRUE", -"PR_UINT32", -"PR_UPTRDIFF", - -"prarena_h___", -"prbit_h___", -"prclist_h___", -"prdtoa_h___", -"prlog_h___", -"prlong_h___", -"prmacos_h___", -"prmem_h___", -"prprf_h___", -"prtypes_h___", - -"prarena", -"prbit", -"prbitmap_t", -"prclist", -"prcpucfg", -"prdtoa", -"prhash", -"plhash", -"prlong", -"prmacos", -"prmem", -"prosdep", -"protypes", -"prprf", -"prtypes" -); - -while ($ARGV[0] =~ /^-/) { - if ($ARGV[0] eq "-r") { - shift; - $reverse_conversion = 1; - } elsif ($ARGV[0] eq "-outdir") { - shift; - $outdir = shift; - } -} - -# Given an NSPR symbol compute the JS equivalent or -# vice-versa -sub subst { - local ($replacement); - local ($sym) = @_; - - $replacement = substr($sym,0,2) eq "pr" ? "js" : "JS"; - $replacement .= substr($sym, 2); - return $replacement; -} - -# Build the regular expression that will convert between the NSPR -# types and the JS types -if ($reverse_conversion) { - die "Not implemented yet"; -} else { - foreach $sym (@NSPR_symbols) { - $regexp .= $sym . "|" - } - # Get rid of the last "!" - chop $regexp; - - # Replace PR* with JS* and replace pr* with js* - $regexp = 's/(^|\\W)(' . $regexp . ')/$1 . &subst($2)/eg'; -# print $regexp; -} - -# Pre-compile a little subroutine to perform the regexp substitution -# between NSPR types and JS types -eval('sub convert_from_NSPR {($line) = @_; $line =~ ' . $regexp . ';}'); - -sub convert_mallocs { - ($line) = @_; - $line =~ s/PR_MALLOC/malloc/g; - $line =~ s/PR_REALLOC/realloc/g; - $line =~ s/PR_FREE/free/g; - return $line; -} - -sub convert_includes { - ($line) = @_; - if ($line !~ /include/) { - return $line; - } - - if ($line =~ /prlog\.h/) { - $line = '#include "jsutil.h"'. " /* Added by JSIFY */\n"; - } elsif ($line =~ /plhash\.h/) { - $line = '#include "jshash.h"'. " /* Added by JSIFY */\n"; - } elsif ($line =~ /plarena\.h/) { - $line = '#include "jsarena.h"'. " /* Added by JSIFY */\n"; - } elsif ($line =~ /prmem\.h/) { - $line = ""; - } elsif ($line =~ /jsmsg\.def/) { - $line = '#include "js.msg"' . "\n"; - } elsif ($line =~ /shellmsg\.def/) { - $line = '#include "jsshell.msg"' . "\n"; - } elsif ($line =~ /jsopcode\.def/) { - $line = '#include "jsopcode.tbl"' . "\n"; - } - return $line; -} - -sub convert_declarations { - ($line) = @_; - $line =~ s/PR_EXTERN/JS_EXTERN_API/g; - $line =~ s/PR_IMPLEMENT_DATA/JS_EXPORT_DATA/g; - $line =~ s/PR_IMPLEMENT/JS_EXPORT_API/g; - $line =~ s/PR_CALLBACK/JS_DLL_CALLBACK/g; - $line =~ s/PR_STATIC_CALLBACK/JS_STATIC_DLL_CALLBACK/g; - $line =~ s/PR_IMPORT/JS_IMPORT/g; - $line =~ s/PR_PUBLIC_API/JS_EXPORT_API/g; - $line =~ s/PR_PUBLIC_DATA/JS_EXPORT_DATA/g; - return $line; -} - -sub convert_long_long_macros { - ($line) = @_; - $line =~ s/\b(LL_)/JSLL_/g; - return $line; -} - -sub convert_asserts { - ($line) = @_; - $line =~ s/\bPR_ASSERT/JS_ASSERT/g; - return $line; -} - -while ($#ARGV >= 0) { - $infile = shift; - - # Change filename, e.g. prtime.h to jsprtime.h, except for legacy - # files that start with 'prmj', like prmjtime.h. - $outfile = $infile; - if ($infile !~ /^prmj/) { - $outfile =~ s/^pr/js/; - $outfile =~ s/^pl/js/; - } - - if ($outdir) { - $outfile = $outdir . '/' . $outfile; - } - - if ($infile eq $outfile) { - die "Error: refuse to overwrite $outfile, use -outdir option." - } - die "Can't open $infile" if !open(INFILE, "<$infile"); - die "Can't open $outfile for writing" if !open(OUTFILE, ">$outfile"); - - while () { - $line = $_; - - #Get rid of #include "prlog.h" - &convert_includes($line); - - # Rename PR_EXTERN, PR_IMPORT, etc. - &convert_declarations($line); - - # Convert from PR_MALLOC to malloc, etc. - &convert_mallocs($line); - - # Convert from PR_ASSERT to JS_ASSERT -# &convert_asserts($line); - - # Convert from, e.g. PRArena to JSPRArena - &convert_from_NSPR($line); - - # Change LL_* macros to JSLL_* - &convert_long_long_macros($line); - - print OUTFILE $line; - } -} diff --git a/spidermonkey/src/jsinterp.c b/spidermonkey/src/jsinterp.c deleted file mode 100644 index c8c1204..0000000 --- a/spidermonkey/src/jsinterp.c +++ /dev/null @@ -1,6216 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JavaScript bytecode interpreter. - */ -#include "jsstddef.h" -#include -#include -#include -#include "jstypes.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ -#include "jsprf.h" -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jsbool.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsdbgapi.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jsiter.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" - -#if JS_HAS_XML_SUPPORT -#include "jsxml.h" -#endif - -#ifdef DEBUG -#define ASSERT_CACHE_IS_EMPTY(cache) \ - JS_BEGIN_MACRO \ - JSPropertyCacheEntry *end_, *pce_, entry_; \ - JSPropertyCache *cache_ = (cache); \ - JS_ASSERT(cache_->empty); \ - end_ = &cache_->table[PROPERTY_CACHE_SIZE]; \ - for (pce_ = &cache_->table[0]; pce_ < end_; pce_++) { \ - PCE_LOAD(cache_, pce_, entry_); \ - JS_ASSERT(!PCE_OBJECT(entry_)); \ - JS_ASSERT(!PCE_PROPERTY(entry_)); \ - } \ - JS_END_MACRO -#else -#define ASSERT_CACHE_IS_EMPTY(cache) ((void)0) -#endif - -void -js_FlushPropertyCache(JSContext *cx) -{ - JSPropertyCache *cache; - - cache = &cx->runtime->propertyCache; - if (cache->empty) { - ASSERT_CACHE_IS_EMPTY(cache); - return; - } - memset(cache->table, 0, sizeof cache->table); - cache->empty = JS_TRUE; -#ifdef JS_PROPERTY_CACHE_METERING - cache->flushes++; -#endif -} - -void -js_DisablePropertyCache(JSContext *cx) -{ - JS_ASSERT(!cx->runtime->propertyCache.disabled); - cx->runtime->propertyCache.disabled = JS_TRUE; -} - -void -js_EnablePropertyCache(JSContext *cx) -{ - JS_ASSERT(cx->runtime->propertyCache.disabled); - ASSERT_CACHE_IS_EMPTY(&cx->runtime->propertyCache); - cx->runtime->propertyCache.disabled = JS_FALSE; -} - -/* - * Stack macros and functions. These all use a local variable, jsval *sp, to - * point to the next free stack slot. SAVE_SP must be called before any call - * to a function that may invoke the interpreter. RESTORE_SP must be called - * only after return from js_Invoke, because only js_Invoke changes fp->sp. - */ -#define PUSH(v) (*sp++ = (v)) -#define POP() (*--sp) -#ifdef DEBUG -#define SAVE_SP(fp) \ - (JS_ASSERT((fp)->script || !(fp)->spbase || (sp) == (fp)->spbase), \ - (fp)->sp = sp) -#else -#define SAVE_SP(fp) ((fp)->sp = sp) -#endif -#define RESTORE_SP(fp) (sp = (fp)->sp) - -/* - * SAVE_SP_AND_PC commits deferred stores of interpreter registers to their - * homes in fp, when calling out of the interpreter loop or threaded code. - * RESTORE_SP_AND_PC copies the other way, to update registers after a call - * to a subroutine that interprets a piece of the current script. - */ -#define SAVE_SP_AND_PC(fp) (SAVE_SP(fp), (fp)->pc = pc) -#define RESTORE_SP_AND_PC(fp) (RESTORE_SP(fp), pc = (fp)->pc) - -/* - * Push the generating bytecode's pc onto the parallel pc stack that runs - * depth slots below the operands. - * - * NB: PUSH_OPND uses sp, depth, and pc from its lexical environment. See - * js_Interpret for these local variables' declarations and uses. - */ -#define PUSH_OPND(v) (sp[-depth] = (jsval)pc, PUSH(v)) -#define STORE_OPND(n,v) (sp[(n)-depth] = (jsval)pc, sp[n] = (v)) -#define POP_OPND() POP() -#define FETCH_OPND(n) (sp[n]) - -/* - * Push the jsdouble d using sp, depth, and pc from the lexical environment. - * Try to convert d to a jsint that fits in a jsval, otherwise GC-alloc space - * for it and push a reference. - */ -#define STORE_NUMBER(cx, n, d) \ - JS_BEGIN_MACRO \ - jsint i_; \ - jsval v_; \ - \ - if (JSDOUBLE_IS_INT(d, i_) && INT_FITS_IN_JSVAL(i_)) { \ - v_ = INT_TO_JSVAL(i_); \ - } else { \ - ok = js_NewDoubleValue(cx, d, &v_); \ - if (!ok) \ - goto out; \ - } \ - STORE_OPND(n, v_); \ - JS_END_MACRO - -#define STORE_INT(cx, n, i) \ - JS_BEGIN_MACRO \ - jsval v_; \ - \ - if (INT_FITS_IN_JSVAL(i)) { \ - v_ = INT_TO_JSVAL(i); \ - } else { \ - ok = js_NewDoubleValue(cx, (jsdouble)(i), &v_); \ - if (!ok) \ - goto out; \ - } \ - STORE_OPND(n, v_); \ - JS_END_MACRO - -#define STORE_UINT(cx, n, u) \ - JS_BEGIN_MACRO \ - jsval v_; \ - \ - if ((u) <= JSVAL_INT_MAX) { \ - v_ = INT_TO_JSVAL(u); \ - } else { \ - ok = js_NewDoubleValue(cx, (jsdouble)(u), &v_); \ - if (!ok) \ - goto out; \ - } \ - STORE_OPND(n, v_); \ - JS_END_MACRO - -#define FETCH_NUMBER(cx, n, d) \ - JS_BEGIN_MACRO \ - jsval v_; \ - \ - v_ = FETCH_OPND(n); \ - VALUE_TO_NUMBER(cx, v_, d); \ - JS_END_MACRO - -#define FETCH_INT(cx, n, i) \ - JS_BEGIN_MACRO \ - jsval v_ = FETCH_OPND(n); \ - if (JSVAL_IS_INT(v_)) { \ - i = JSVAL_TO_INT(v_); \ - } else { \ - SAVE_SP_AND_PC(fp); \ - ok = js_ValueToECMAInt32(cx, v_, &i); \ - if (!ok) \ - goto out; \ - } \ - JS_END_MACRO - -#define FETCH_UINT(cx, n, ui) \ - JS_BEGIN_MACRO \ - jsval v_ = FETCH_OPND(n); \ - jsint i_; \ - if (JSVAL_IS_INT(v_) && (i_ = JSVAL_TO_INT(v_)) >= 0) { \ - ui = (uint32) i_; \ - } else { \ - SAVE_SP_AND_PC(fp); \ - ok = js_ValueToECMAUint32(cx, v_, &ui); \ - if (!ok) \ - goto out; \ - } \ - JS_END_MACRO - -/* - * Optimized conversion macros that test for the desired type in v before - * homing sp and calling a conversion function. - */ -#define VALUE_TO_NUMBER(cx, v, d) \ - JS_BEGIN_MACRO \ - if (JSVAL_IS_INT(v)) { \ - d = (jsdouble)JSVAL_TO_INT(v); \ - } else if (JSVAL_IS_DOUBLE(v)) { \ - d = *JSVAL_TO_DOUBLE(v); \ - } else { \ - SAVE_SP_AND_PC(fp); \ - ok = js_ValueToNumber(cx, v, &d); \ - if (!ok) \ - goto out; \ - } \ - JS_END_MACRO - -#define POP_BOOLEAN(cx, v, b) \ - JS_BEGIN_MACRO \ - v = FETCH_OPND(-1); \ - if (v == JSVAL_NULL) { \ - b = JS_FALSE; \ - } else if (JSVAL_IS_BOOLEAN(v)) { \ - b = JSVAL_TO_BOOLEAN(v); \ - } else { \ - SAVE_SP_AND_PC(fp); \ - ok = js_ValueToBoolean(cx, v, &b); \ - if (!ok) \ - goto out; \ - } \ - sp--; \ - JS_END_MACRO - -/* - * Convert a primitive string, number or boolean to a corresponding object. - * v must not be an object, null or undefined when using this macro. - */ -#define PRIMITIVE_TO_OBJECT(cx, v, obj) \ - JS_BEGIN_MACRO \ - SAVE_SP(fp); \ - if (JSVAL_IS_STRING(v)) { \ - obj = js_StringToObject(cx, JSVAL_TO_STRING(v)); \ - } else if (JSVAL_IS_INT(v)) { \ - obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v)); \ - } else if (JSVAL_IS_DOUBLE(v)) { \ - obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v)); \ - } else { \ - JS_ASSERT(JSVAL_IS_BOOLEAN(v)); \ - obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v)); \ - } \ - JS_END_MACRO - -#define VALUE_TO_OBJECT(cx, v, obj) \ - JS_BEGIN_MACRO \ - if (!JSVAL_IS_PRIMITIVE(v)) { \ - obj = JSVAL_TO_OBJECT(v); \ - } else { \ - SAVE_SP_AND_PC(fp); \ - obj = js_ValueToNonNullObject(cx, v); \ - if (!obj) { \ - ok = JS_FALSE; \ - goto out; \ - } \ - } \ - JS_END_MACRO - -#define FETCH_OBJECT(cx, n, v, obj) \ - JS_BEGIN_MACRO \ - v = FETCH_OPND(n); \ - VALUE_TO_OBJECT(cx, v, obj); \ - STORE_OPND(n, OBJECT_TO_JSVAL(obj)); \ - JS_END_MACRO - -#define VALUE_TO_PRIMITIVE(cx, v, hint, vp) \ - JS_BEGIN_MACRO \ - if (JSVAL_IS_PRIMITIVE(v)) { \ - *vp = v; \ - } else { \ - SAVE_SP_AND_PC(fp); \ - ok = OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), hint, vp); \ - if (!ok) \ - goto out; \ - } \ - JS_END_MACRO - -JS_FRIEND_API(jsval *) -js_AllocRawStack(JSContext *cx, uintN nslots, void **markp) -{ - jsval *sp; - - if (markp) - *markp = JS_ARENA_MARK(&cx->stackPool); - JS_ARENA_ALLOCATE_CAST(sp, jsval *, &cx->stackPool, nslots * sizeof(jsval)); - if (!sp) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_STACK_OVERFLOW, - (cx->fp && cx->fp->fun) - ? JS_GetFunctionName(cx->fp->fun) - : "script"); - } - return sp; -} - -JS_FRIEND_API(void) -js_FreeRawStack(JSContext *cx, void *mark) -{ - JS_ARENA_RELEASE(&cx->stackPool, mark); -} - -JS_FRIEND_API(jsval *) -js_AllocStack(JSContext *cx, uintN nslots, void **markp) -{ - jsval *sp, *vp, *end; - JSArena *a; - JSStackHeader *sh; - JSStackFrame *fp; - - /* Callers don't check for zero nslots: we do to avoid empty segments. */ - if (nslots == 0) { - *markp = NULL; - return JS_ARENA_MARK(&cx->stackPool); - } - - /* Allocate 2 extra slots for the stack segment header we'll likely need. */ - sp = js_AllocRawStack(cx, 2 + nslots, markp); - if (!sp) - return NULL; - - /* Try to avoid another header if we can piggyback on the last segment. */ - a = cx->stackPool.current; - sh = cx->stackHeaders; - if (sh && JS_STACK_SEGMENT(sh) + sh->nslots == sp) { - /* Extend the last stack segment, give back the 2 header slots. */ - sh->nslots += nslots; - a->avail -= 2 * sizeof(jsval); - } else { - /* - * Need a new stack segment, so we must initialize unused slots in the - * current frame. See js_GC, just before marking the "operand" jsvals, - * where we scan from fp->spbase to fp->sp or through fp->script->depth - * (whichever covers fewer slots). - */ - fp = cx->fp; - if (fp && fp->script && fp->spbase) { -#ifdef DEBUG - jsuword depthdiff = fp->script->depth * sizeof(jsval); - JS_ASSERT(JS_UPTRDIFF(fp->sp, fp->spbase) <= depthdiff); - JS_ASSERT(JS_UPTRDIFF(*markp, fp->spbase) >= depthdiff); -#endif - end = fp->spbase + fp->script->depth; - for (vp = fp->sp; vp < end; vp++) - *vp = JSVAL_VOID; - } - - /* Allocate and push a stack segment header from the 2 extra slots. */ - sh = (JSStackHeader *)sp; - sh->nslots = nslots; - sh->down = cx->stackHeaders; - cx->stackHeaders = sh; - sp += 2; - } - - /* - * Store JSVAL_NULL using memset, to let compilers optimize as they see - * fit, in case a caller allocates and pushes GC-things one by one, which - * could nest a last-ditch GC that will scan this segment. - */ - memset(sp, 0, nslots * sizeof(jsval)); - return sp; -} - -JS_FRIEND_API(void) -js_FreeStack(JSContext *cx, void *mark) -{ - JSStackHeader *sh; - jsuword slotdiff; - - /* Check for zero nslots allocation special case. */ - if (!mark) - return; - - /* We can assert because js_FreeStack always balances js_AllocStack. */ - sh = cx->stackHeaders; - JS_ASSERT(sh); - - /* If mark is in the current segment, reduce sh->nslots, else pop sh. */ - slotdiff = JS_UPTRDIFF(mark, JS_STACK_SEGMENT(sh)) / sizeof(jsval); - if (slotdiff < (jsuword)sh->nslots) - sh->nslots = slotdiff; - else - cx->stackHeaders = sh->down; - - /* Release the stackPool space allocated since mark was set. */ - JS_ARENA_RELEASE(&cx->stackPool, mark); -} - -JSBool -js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - return JS_TRUE; -} - -JSBool -js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - return JS_TRUE; -} - -JSBool -js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - return JS_TRUE; -} - -JSBool -js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - return JS_TRUE; -} - -JSObject * -js_GetScopeChain(JSContext *cx, JSStackFrame *fp) -{ - JSObject *obj, *cursor, *clonedChild, *parent; - JSTempValueRooter tvr; - - obj = fp->blockChain; - if (!obj) { - /* - * Don't force a call object for a lightweight function call, but do - * insist that there is a call object for a heavyweight function call. - */ - JS_ASSERT(!fp->fun || - !(fp->fun->flags & JSFUN_HEAVYWEIGHT) || - fp->callobj); - JS_ASSERT(fp->scopeChain); - return fp->scopeChain; - } - - /* - * We have one or more lexical scopes to reflect into fp->scopeChain, so - * make sure there's a call object at the current head of the scope chain, - * if this frame is a call frame. - */ - if (fp->fun && !fp->callobj) { - JS_ASSERT(OBJ_GET_CLASS(cx, fp->scopeChain) != &js_BlockClass || - JS_GetPrivate(cx, fp->scopeChain) != fp); - if (!js_GetCallObject(cx, fp, fp->scopeChain)) - return NULL; - } - - /* - * Clone the block chain. To avoid recursive cloning we set the parent of - * the cloned child after we clone the parent. In the following loop when - * clonedChild is null it indicates the first iteration when no special GC - * rooting is necessary. On the second and the following iterations we - * have to protect cloned so far chain against the GC during cloning of - * the cursor object. - */ - cursor = obj; - clonedChild = NULL; - for (;;) { - parent = OBJ_GET_PARENT(cx, cursor); - - /* - * We pass fp->scopeChain and not null even if we override the parent - * slot later as null triggers useless calculations of slot's value in - * js_NewObject that js_CloneBlockObject calls. - */ - cursor = js_CloneBlockObject(cx, cursor, fp->scopeChain, fp); - if (!cursor) { - if (clonedChild) - JS_POP_TEMP_ROOT(cx, &tvr); - return NULL; - } - if (!clonedChild) { - /* - * The first iteration. Check if other follow and root obj if so - * to protect the whole cloned chain against GC. - */ - obj = cursor; - if (!parent) - break; - JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); - } else { - /* - * Avoid OBJ_SET_PARENT overhead as clonedChild cannot escape to - * other threads. - */ - clonedChild->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(cursor); - if (!parent) { - JS_ASSERT(tvr.u.value == OBJECT_TO_JSVAL(obj)); - JS_POP_TEMP_ROOT(cx, &tvr); - break; - } - } - clonedChild = cursor; - cursor = parent; - } - fp->flags |= JSFRAME_POP_BLOCKS; - fp->scopeChain = obj; - fp->blockChain = NULL; - return obj; -} - -/* - * Walk the scope chain looking for block scopes whose locals need to be - * copied from stack slots into object slots before fp goes away. - */ -static JSBool -PutBlockObjects(JSContext *cx, JSStackFrame *fp) -{ - JSBool ok; - JSObject *obj; - - ok = JS_TRUE; - for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) { - if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) { - if (JS_GetPrivate(cx, obj) != fp) - break; - ok &= js_PutBlockObject(cx, obj); - } - } - return ok; -} - -JSObject * -js_ComputeThis(JSContext *cx, JSObject *thisp, jsval *argv) -{ - if (thisp && OBJ_GET_CLASS(cx, thisp) != &js_CallClass) { - /* Some objects (e.g., With) delegate 'this' to another object. */ - thisp = OBJ_THIS_OBJECT(cx, thisp); - if (!thisp) - return NULL; - } else { - /* - * ECMA requires "the global object", but in the presence of multiple - * top-level objects (windows, frames, or certain layers in the client - * object model), we prefer fun's parent. An example that causes this - * code to run: - * - * // in window w1 - * function f() { return this } - * function g() { return f } - * - * // in window w2 - * var h = w1.g() - * alert(h() == w1) - * - * The alert should display "true". - */ - if (JSVAL_IS_PRIMITIVE(argv[-2]) || - !OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2]))) { - thisp = cx->globalObject; - } else { - jsid id; - jsval v; - uintN attrs; - - /* Walk up the parent chain. */ - thisp = JSVAL_TO_OBJECT(argv[-2]); - id = ATOM_TO_JSID(cx->runtime->atomState.parentAtom); - for (;;) { - if (!OBJ_CHECK_ACCESS(cx, thisp, id, JSACC_PARENT, &v, &attrs)) - return NULL; - if (JSVAL_IS_VOID(v)) - v = OBJ_GET_SLOT(cx, thisp, JSSLOT_PARENT); - if (JSVAL_IS_NULL(v)) - break; - thisp = JSVAL_TO_OBJECT(v); - } - } - } - argv[-1] = OBJECT_TO_JSVAL(thisp); - return thisp; -} - -#if JS_HAS_NO_SUCH_METHOD - -static JSBool -NoSuchMethod(JSContext *cx, JSStackFrame *fp, jsval *vp, uint32 flags, - uintN argc) -{ - JSObject *thisp, *argsobj; - jsval *sp, roots[3]; - JSTempValueRooter tvr; - jsid id; - JSBool ok; - jsbytecode *pc; - jsatomid atomIndex; - - /* - * We must call js_ComputeThis here to censor Call objects. A performance - * hit, since we'll call it again in the normal sequence of invoke events, - * but at least it's idempotent. - * - * Normally, we call ComputeThis after all frame members have been set, - * and in particular, after any revision of the callee value at *vp due - * to clasp->convert (see below). This matters because ComputeThis may - * access *vp via fp->argv[-2], to follow the parent chain to a global - * object to use as the 'this' parameter. - * - * Obviously, here in the JSVAL_IS_PRIMITIVE(v) case, there can't be any - * such defaulting of 'this' to callee (v, *vp) ancestor. - */ - JS_ASSERT(JSVAL_IS_PRIMITIVE(vp[0])); - RESTORE_SP(fp); - if (JSVAL_IS_OBJECT(vp[1])) { - thisp = JSVAL_TO_OBJECT(vp[1]); - } else { - PRIMITIVE_TO_OBJECT(cx, vp[1], thisp); - if (!thisp) - return JS_FALSE; - vp[1] = OBJECT_TO_JSVAL(thisp); - } - thisp = js_ComputeThis(cx, thisp, vp + 2); - if (!thisp) - return JS_FALSE; - vp[1] = OBJECT_TO_JSVAL(thisp); - - /* From here on, control must flow through label out: to return. */ - memset(roots, 0, sizeof roots); - JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(roots), roots, &tvr); - - id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom); -#if JS_HAS_XML_SUPPORT - if (OBJECT_IS_XML(cx, thisp)) { - JSXMLObjectOps *ops; - - ops = (JSXMLObjectOps *) thisp->map->ops; - thisp = ops->getMethod(cx, thisp, id, &roots[2]); - if (!thisp) { - ok = JS_FALSE; - goto out; - } - vp[1] = OBJECT_TO_JSVAL(thisp); - } else -#endif - { - ok = OBJ_GET_PROPERTY(cx, thisp, id, &roots[2]); - if (!ok) - goto out; - } - if (JSVAL_IS_PRIMITIVE(roots[2])) - goto not_function; - - pc = (jsbytecode *) vp[-(intN)fp->script->depth]; - switch ((JSOp) *pc) { - case JSOP_NAME: - case JSOP_GETPROP: -#if JS_HAS_XML_SUPPORT - case JSOP_GETMETHOD: -#endif - atomIndex = GET_ATOM_INDEX(pc); - roots[0] = ATOM_KEY(js_GetAtom(cx, &fp->script->atomMap, atomIndex)); - argsobj = js_NewArrayObject(cx, argc, vp + 2); - if (!argsobj) { - ok = JS_FALSE; - goto out; - } - roots[1] = OBJECT_TO_JSVAL(argsobj); - ok = js_InternalInvoke(cx, thisp, roots[2], flags | JSINVOKE_INTERNAL, - 2, roots, &vp[0]); - break; - - default: - goto not_function; - } - - out: - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; - - not_function: - js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS); - ok = JS_FALSE; - goto out; -} - -#endif /* JS_HAS_NO_SUCH_METHOD */ - -#ifdef DUMP_CALL_TABLE - -#include "jsclist.h" -#include "jshash.h" -#include "jsdtoa.h" - -typedef struct CallKey { - jsval callee; /* callee value */ - const char *filename; /* function filename or null */ - uintN lineno; /* function lineno or 0 */ -} CallKey; - -/* Compensate for typeof null == "object" brain damage. */ -#define JSTYPE_NULL JSTYPE_LIMIT -#define TYPEOF(cx,v) (JSVAL_IS_NULL(v) ? JSTYPE_NULL : JS_TypeOfValue(cx,v)) -#define TYPENAME(t) (((t) == JSTYPE_NULL) ? js_null_str : js_type_str[t]) -#define NTYPEHIST (JSTYPE_LIMIT + 1) - -typedef struct CallValue { - uint32 total; /* total call count */ - uint32 recycled; /* LRU-recycled calls lost */ - uint16 minargc; /* minimum argument count */ - uint16 maxargc; /* maximum argument count */ - struct ArgInfo { - uint32 typeHist[NTYPEHIST]; /* histogram by type */ - JSCList lruList; /* top 10 values LRU list */ - struct ArgValCount { - JSCList lruLink; /* LRU list linkage */ - jsval value; /* recently passed value */ - uint32 count; /* number of times passed */ - char strbuf[112]; /* string conversion buffer */ - } topValCounts[10]; /* top 10 value storage */ - } argInfo[8]; -} CallValue; - -typedef struct CallEntry { - JSHashEntry entry; - CallKey key; - CallValue value; - char name[32]; /* function name copy */ -} CallEntry; - -static void * -AllocCallTable(void *pool, size_t size) -{ - return malloc(size); -} - -static void -FreeCallTable(void *pool, void *item) -{ - free(item); -} - -static JSHashEntry * -AllocCallEntry(void *pool, const void *key) -{ - return (JSHashEntry*) calloc(1, sizeof(CallEntry)); -} - -static void -FreeCallEntry(void *pool, JSHashEntry *he, uintN flag) -{ - JS_ASSERT(flag == HT_FREE_ENTRY); - free(he); -} - -static JSHashAllocOps callTableAllocOps = { - AllocCallTable, FreeCallTable, - AllocCallEntry, FreeCallEntry -}; - -JS_STATIC_DLL_CALLBACK(JSHashNumber) -js_hash_call_key(const void *key) -{ - CallKey *ck = (CallKey *) key; - JSHashNumber hash = (jsuword)ck->callee >> 3; - - if (ck->filename) { - hash = (hash << 4) ^ JS_HashString(ck->filename); - hash = (hash << 4) ^ ck->lineno; - } - return hash; -} - -JS_STATIC_DLL_CALLBACK(intN) -js_compare_call_keys(const void *k1, const void *k2) -{ - CallKey *ck1 = (CallKey *)k1, *ck2 = (CallKey *)k2; - - return ck1->callee == ck2->callee && - ((ck1->filename && ck2->filename) - ? strcmp(ck1->filename, ck2->filename) == 0 - : ck1->filename == ck2->filename) && - ck1->lineno == ck2->lineno; -} - -JSHashTable *js_CallTable; -size_t js_LogCallToSourceLimit; - -JS_STATIC_DLL_CALLBACK(intN) -CallTableDumper(JSHashEntry *he, intN k, void *arg) -{ - CallEntry *ce = (CallEntry *)he; - FILE *fp = (FILE *)arg; - uintN argc, i, n; - struct ArgInfo *ai; - JSType save, type; - JSCList *cl; - struct ArgValCount *avc; - jsval argval; - - if (ce->key.filename) { - /* We're called at the end of the mark phase, so mark our filenames. */ - js_MarkScriptFilename(ce->key.filename); - fprintf(fp, "%s:%u ", ce->key.filename, ce->key.lineno); - } else { - fprintf(fp, "@%p ", (void *) ce->key.callee); - } - - if (ce->name[0]) - fprintf(fp, "name %s ", ce->name); - fprintf(fp, "calls %lu (%lu) argc %u/%u\n", - (unsigned long) ce->value.total, - (unsigned long) ce->value.recycled, - ce->value.minargc, ce->value.maxargc); - - argc = JS_MIN(ce->value.maxargc, 8); - for (i = 0; i < argc; i++) { - ai = &ce->value.argInfo[i]; - - n = 0; - save = -1; - for (type = JSTYPE_VOID; type <= JSTYPE_LIMIT; type++) { - if (ai->typeHist[type]) { - save = type; - ++n; - } - } - if (n == 1) { - fprintf(fp, " arg %u type %s: %lu\n", - i, TYPENAME(save), (unsigned long) ai->typeHist[save]); - } else { - fprintf(fp, " arg %u type histogram:\n", i); - for (type = JSTYPE_VOID; type <= JSTYPE_LIMIT; type++) { - fprintf(fp, " %9s: %8lu ", - TYPENAME(type), (unsigned long) ai->typeHist[type]); - for (n = (uintN) JS_HOWMANY(ai->typeHist[type], 10); n > 0; --n) - fputc('*', fp); - fputc('\n', fp); - } - } - - fprintf(fp, " arg %u top 10 values:\n", i); - n = 1; - for (cl = ai->lruList.prev; cl != &ai->lruList; cl = cl->prev) { - avc = (struct ArgValCount *)cl; - if (!avc->count) - break; - argval = avc->value; - fprintf(fp, " %9u: %8lu %.*s (%#lx)\n", - n, (unsigned long) avc->count, - sizeof avc->strbuf, avc->strbuf, argval); - ++n; - } - } - - return HT_ENUMERATE_NEXT; -} - -void -js_DumpCallTable(JSContext *cx) -{ - char name[24]; - FILE *fp; - static uintN dumpCount; - - if (!js_CallTable) - return; - - JS_snprintf(name, sizeof name, "/tmp/calltable.dump.%u", dumpCount & 7); - dumpCount++; - fp = fopen(name, "w"); - if (!fp) - return; - - JS_HashTableEnumerateEntries(js_CallTable, CallTableDumper, fp); - fclose(fp); -} - -static void -LogCall(JSContext *cx, jsval callee, uintN argc, jsval *argv) -{ - CallKey key; - const char *name, *cstr; - JSFunction *fun; - JSHashNumber keyHash; - JSHashEntry **hep, *he; - CallEntry *ce; - uintN i, j; - jsval argval; - JSType type; - struct ArgInfo *ai; - struct ArgValCount *avc; - JSString *str; - - if (!js_CallTable) { - js_CallTable = JS_NewHashTable(1024, js_hash_call_key, - js_compare_call_keys, NULL, - &callTableAllocOps, NULL); - if (!js_CallTable) - return; - } - - key.callee = callee; - key.filename = NULL; - key.lineno = 0; - name = ""; - if (VALUE_IS_FUNCTION(cx, callee)) { - fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(callee)); - if (fun->atom) - name = js_AtomToPrintableString(cx, fun->atom); - if (FUN_INTERPRETED(fun)) { - key.filename = fun->u.i.script->filename; - key.lineno = fun->u.i.script->lineno; - } - } - keyHash = js_hash_call_key(&key); - - hep = JS_HashTableRawLookup(js_CallTable, keyHash, &key); - he = *hep; - if (he) { - ce = (CallEntry *) he; - JS_ASSERT(strncmp(ce->name, name, sizeof ce->name) == 0); - } else { - he = JS_HashTableRawAdd(js_CallTable, hep, keyHash, &key, NULL); - if (!he) - return; - ce = (CallEntry *) he; - ce->entry.key = &ce->key; - ce->entry.value = &ce->value; - ce->key = key; - for (i = 0; i < 8; i++) { - ai = &ce->value.argInfo[i]; - JS_INIT_CLIST(&ai->lruList); - for (j = 0; j < 10; j++) - JS_APPEND_LINK(&ai->topValCounts[j].lruLink, &ai->lruList); - } - strncpy(ce->name, name, sizeof ce->name); - } - - ++ce->value.total; - if (ce->value.minargc < argc) - ce->value.minargc = argc; - if (ce->value.maxargc < argc) - ce->value.maxargc = argc; - if (argc > 8) - argc = 8; - for (i = 0; i < argc; i++) { - ai = &ce->value.argInfo[i]; - argval = argv[i]; - type = TYPEOF(cx, argval); - ++ai->typeHist[type]; - - for (j = 0; ; j++) { - if (j == 10) { - avc = (struct ArgValCount *) ai->lruList.next; - ce->value.recycled += avc->count; - avc->value = argval; - avc->count = 1; - break; - } - avc = &ai->topValCounts[j]; - if (avc->value == argval) { - ++avc->count; - break; - } - } - - /* Move avc to the back of the LRU list. */ - JS_REMOVE_LINK(&avc->lruLink); - JS_APPEND_LINK(&avc->lruLink, &ai->lruList); - - str = NULL; - cstr = ""; - switch (TYPEOF(cx, argval)) { - case JSTYPE_VOID: - cstr = js_type_str[JSTYPE_VOID]; - break; - case JSTYPE_NULL: - cstr = js_null_str; - break; - case JSTYPE_BOOLEAN: - cstr = js_boolean_str[JSVAL_TO_BOOLEAN(argval)]; - break; - case JSTYPE_NUMBER: - if (JSVAL_IS_INT(argval)) { - JS_snprintf(avc->strbuf, sizeof avc->strbuf, "%ld", - JSVAL_TO_INT(argval)); - } else { - JS_dtostr(avc->strbuf, sizeof avc->strbuf, DTOSTR_STANDARD, 0, - *JSVAL_TO_DOUBLE(argval)); - } - continue; - case JSTYPE_STRING: - str = js_QuoteString(cx, JSVAL_TO_STRING(argval), (jschar)'"'); - break; - case JSTYPE_FUNCTION: - if (VALUE_IS_FUNCTION(cx, argval)) { - fun = (JSFunction *)JS_GetPrivate(cx, JSVAL_TO_OBJECT(argval)); - if (fun && fun->atom) { - str = ATOM_TO_STRING(fun->atom); - break; - } - } - /* FALL THROUGH */ - case JSTYPE_OBJECT: - js_LogCallToSourceLimit = sizeof avc->strbuf; - cx->options |= JSOPTION_LOGCALL_TOSOURCE; - str = js_ValueToSource(cx, argval); - cx->options &= ~JSOPTION_LOGCALL_TOSOURCE; - break; - } - if (str) - cstr = JS_GetStringBytes(str); - strncpy(avc->strbuf, cstr, sizeof avc->strbuf); - } -} - -#endif /* DUMP_CALL_TABLE */ - -/* - * Conditional assert to detect failure to clear a pending exception that is - * suppressed (or unintentional suppression of a wanted exception). - */ -#if defined DEBUG_brendan || defined DEBUG_mrbkap || defined DEBUG_shaver -# define DEBUG_NOT_THROWING 1 -#endif - -#ifdef DEBUG_NOT_THROWING -# define ASSERT_NOT_THROWING(cx) JS_ASSERT(!(cx)->throwing) -#else -# define ASSERT_NOT_THROWING(cx) /* nothing */ -#endif - -/* - * Find a function reference and its 'this' object implicit first parameter - * under argc arguments on cx's stack, and call the function. Push missing - * required arguments, allocate declared local variables, and pop everything - * when done. Then push the return value. - */ -JS_FRIEND_API(JSBool) -js_Invoke(JSContext *cx, uintN argc, uintN flags) -{ - void *mark; - JSStackFrame *fp, frame; - jsval *sp, *newsp, *limit; - jsval *vp, v, thisv; - JSObject *funobj, *parent, *thisp; - JSBool ok; - JSClass *clasp; - JSObjectOps *ops; - JSNative native; - JSFunction *fun; - JSScript *script; - uintN nslots, nvars, nalloc, surplus; - JSInterpreterHook hook; - void *hookData; - - /* Mark the top of stack and load frequently-used registers. */ - mark = JS_ARENA_MARK(&cx->stackPool); - fp = cx->fp; - sp = fp->sp; - - /* - * Set vp to the callee value's stack slot (it's where rval goes). - * Once vp is set, control should flow through label out2: to return. - * Set frame.rval early so native class and object ops can throw and - * return false, causing a goto out2 with ok set to false. - */ - vp = sp - (2 + argc); - v = *vp; - frame.rval = JSVAL_VOID; - - /* - * A callee must be an object reference, unless its 'this' parameter - * implements the __noSuchMethod__ method, in which case that method will - * be called like so: - * - * thisp.__noSuchMethod__(id, args) - * - * where id is the name of the method that this invocation attempted to - * call by name, and args is an Array containing this invocation's actual - * parameters. - */ - if (JSVAL_IS_PRIMITIVE(v)) { -#if JS_HAS_NO_SUCH_METHOD - if (fp->script && !(flags & JSINVOKE_INTERNAL)) { - ok = NoSuchMethod(cx, fp, vp, flags, argc); - if (ok) - frame.rval = *vp; - goto out2; - } -#endif - goto bad; - } - - /* Load thisv after potentially calling NoSuchMethod, which may set it. */ - thisv = vp[1]; - - funobj = JSVAL_TO_OBJECT(v); - parent = OBJ_GET_PARENT(cx, funobj); - clasp = OBJ_GET_CLASS(cx, funobj); - if (clasp != &js_FunctionClass) { - /* Function is inlined, all other classes use object ops. */ - ops = funobj->map->ops; - - /* - * XXX this makes no sense -- why convert to function if clasp->call? - * XXX better to call that hook without converting - * XXX the only thing that needs fixing is liveconnect - * - * Try converting to function, for closure and API compatibility. - * We attempt the conversion under all circumstances for 1.2, but - * only if there is a call op defined otherwise. - */ - if ((ops == &js_ObjectOps) ? clasp->call : ops->call) { - ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v); - if (!ok) - goto out2; - - if (VALUE_IS_FUNCTION(cx, v)) { - /* Make vp refer to funobj to keep it available as argv[-2]. */ - *vp = v; - funobj = JSVAL_TO_OBJECT(v); - parent = OBJ_GET_PARENT(cx, funobj); - goto have_fun; - } - } - fun = NULL; - script = NULL; - nslots = nvars = 0; - - /* Try a call or construct native object op. */ - native = (flags & JSINVOKE_CONSTRUCT) ? ops->construct : ops->call; - if (!native) - goto bad; - - if (JSVAL_IS_OBJECT(thisv)) { - thisp = JSVAL_TO_OBJECT(thisv); - } else { - PRIMITIVE_TO_OBJECT(cx, thisv, thisp); - if (!thisp) - goto out2; - vp[1] = thisv = OBJECT_TO_JSVAL(thisp); - } - } else { -have_fun: - /* Get private data and set derived locals from it. */ - fun = (JSFunction *) JS_GetPrivate(cx, funobj); - nslots = (fun->nargs > argc) ? fun->nargs - argc : 0; - if (FUN_INTERPRETED(fun)) { - native = NULL; - script = fun->u.i.script; - nvars = fun->u.i.nvars; - } else { - native = fun->u.n.native; - script = NULL; - nvars = 0; - nslots += fun->u.n.extra; - } - - if (JSFUN_BOUND_METHOD_TEST(fun->flags)) { - /* Handle bound method special case. */ - thisp = parent; - } else if (JSVAL_IS_OBJECT(thisv)) { - thisp = JSVAL_TO_OBJECT(thisv); - } else { - uintN thispflags = JSFUN_THISP_FLAGS(fun->flags); - - JS_ASSERT(!(flags & JSINVOKE_CONSTRUCT)); - if (JSVAL_IS_STRING(thisv)) { - if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_STRING)) { - thisp = (JSObject *) thisv; - goto init_frame; - } - thisp = js_StringToObject(cx, JSVAL_TO_STRING(thisv)); - } else if (JSVAL_IS_INT(thisv)) { - if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_NUMBER)) { - thisp = (JSObject *) thisv; - goto init_frame; - } - thisp = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(thisv)); - } else if (JSVAL_IS_DOUBLE(thisv)) { - if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_NUMBER)) { - thisp = (JSObject *) thisv; - goto init_frame; - } - thisp = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(thisv)); - } else { - JS_ASSERT(JSVAL_IS_BOOLEAN(thisv)); - if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_BOOLEAN)) { - thisp = (JSObject *) thisv; - goto init_frame; - } - thisp = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(thisv)); - } - if (!thisp) { - ok = JS_FALSE; - goto out2; - } - goto init_frame; - } - } - - if (flags & JSINVOKE_CONSTRUCT) { - /* Default return value for a constructor is the new object. */ - frame.rval = OBJECT_TO_JSVAL(thisp); - } else { - thisp = js_ComputeThis(cx, thisp, vp + 2); - if (!thisp) { - ok = JS_FALSE; - goto out2; - } - } - - init_frame: - /* Initialize the rest of frame, except for sp (set by SAVE_SP later). */ - frame.thisp = thisp; - frame.varobj = NULL; - frame.callobj = frame.argsobj = NULL; - frame.script = script; - frame.fun = fun; - frame.argc = argc; - frame.argv = sp - argc; - frame.nvars = nvars; - frame.vars = sp; - frame.down = fp; - frame.annotation = NULL; - frame.scopeChain = NULL; /* set below for real, after cx->fp is set */ - frame.pc = NULL; - frame.spbase = NULL; - frame.sharpDepth = 0; - frame.sharpArray = NULL; - frame.flags = flags; - frame.dormantNext = NULL; - frame.xmlNamespace = NULL; - frame.blockChain = NULL; - - /* From here on, control must flow through label out: to return. */ - cx->fp = &frame; - - /* Init these now in case we goto out before first hook call. */ - hook = cx->runtime->callHook; - hookData = NULL; - - /* Check for argument slots required by the function. */ - if (nslots) { - /* All arguments must be contiguous, so we may have to copy actuals. */ - nalloc = nslots; - limit = (jsval *) cx->stackPool.current->limit; - JS_ASSERT((jsval *) cx->stackPool.current->base <= sp && sp <= limit); - if (sp + nslots > limit) { - /* Hit end of arena: we have to copy argv[-2..(argc+nslots-1)]. */ - nalloc += 2 + argc; - } else { - /* Take advantage of surplus slots in the caller's frame depth. */ - JS_ASSERT((jsval *)mark >= sp); - surplus = (jsval *)mark - sp; - nalloc -= surplus; - } - - /* Check whether we have enough space in the caller's frame. */ - if ((intN)nalloc > 0) { - /* Need space for actuals plus missing formals minus surplus. */ - newsp = js_AllocRawStack(cx, nalloc, NULL); - if (!newsp) { - ok = JS_FALSE; - goto out; - } - - /* If we couldn't allocate contiguous args, copy actuals now. */ - if (newsp != mark) { - JS_ASSERT(sp + nslots > limit); - JS_ASSERT(2 + argc + nslots == nalloc); - *newsp++ = vp[0]; - *newsp++ = vp[1]; - if (argc) - memcpy(newsp, frame.argv, argc * sizeof(jsval)); - frame.argv = newsp; - sp = frame.vars = newsp + argc; - } - } - - /* Advance frame.vars to make room for the missing args. */ - frame.vars += nslots; - - /* Push void to initialize missing args. */ - do { - PUSH(JSVAL_VOID); - } while (--nslots != 0); - } - JS_ASSERT(nslots == 0); - - /* Now allocate stack space for local variables. */ - if (nvars) { - JS_ASSERT((jsval *)cx->stackPool.current->avail >= frame.vars); - surplus = (jsval *)cx->stackPool.current->avail - frame.vars; - if (surplus < nvars) { - newsp = js_AllocRawStack(cx, nvars, NULL); - if (!newsp) { - ok = JS_FALSE; - goto out; - } - if (newsp != sp) { - /* NB: Discontinuity between argv and vars. */ - sp = frame.vars = newsp; - } - } - - /* Push void to initialize local variables. */ - do { - PUSH(JSVAL_VOID); - } while (--nvars != 0); - } - JS_ASSERT(nvars == 0); - - /* Store the current sp in frame before calling fun. */ - SAVE_SP(&frame); - - /* call the hook if present */ - if (hook && (native || script)) - hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->callHookData); - - /* Call the function, either a native method or an interpreted script. */ - if (native) { -#ifdef DEBUG_NOT_THROWING - JSBool alreadyThrowing = cx->throwing; -#endif - -#if JS_HAS_LVALUE_RETURN - /* Set by JS_SetCallReturnValue2, used to return reference types. */ - cx->rval2set = JS_FALSE; -#endif - - /* If native, use caller varobj and scopeChain for eval. */ - frame.varobj = fp->varobj; - frame.scopeChain = fp->scopeChain; - ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval); - JS_RUNTIME_METER(cx->runtime, nativeCalls); -#ifdef DEBUG_NOT_THROWING - if (ok && !alreadyThrowing) - ASSERT_NOT_THROWING(cx); -#endif - } else if (script) { -#ifdef DUMP_CALL_TABLE - LogCall(cx, *vp, argc, frame.argv); -#endif - /* Use parent scope so js_GetCallObject can find the right "Call". */ - frame.scopeChain = parent; - if (JSFUN_HEAVYWEIGHT_TEST(fun->flags)) { - /* Scope with a call object parented by the callee's parent. */ - if (!js_GetCallObject(cx, &frame, parent)) { - ok = JS_FALSE; - goto out; - } - } - ok = js_Interpret(cx, script->code, &v); - } else { - /* fun might be onerror trying to report a syntax error in itself. */ - frame.scopeChain = NULL; - ok = JS_TRUE; - } - -out: - if (hookData) { - hook = cx->runtime->callHook; - if (hook) - hook(cx, &frame, JS_FALSE, &ok, hookData); - } - - /* If frame has a call object, sync values and clear back-pointer. */ - if (frame.callobj) - ok &= js_PutCallObject(cx, &frame); - - /* If frame has an arguments object, sync values and clear back-pointer. */ - if (frame.argsobj) - ok &= js_PutArgsObject(cx, &frame); - - /* Restore cx->fp now that we're done releasing frame objects. */ - cx->fp = fp; - -out2: - /* Pop everything we may have allocated off the stack. */ - JS_ARENA_RELEASE(&cx->stackPool, mark); - - /* Store the return value and restore sp just above it. */ - *vp = frame.rval; - fp->sp = vp + 1; - - /* - * Store the location of the JSOP_CALL or JSOP_EVAL that generated the - * return value, but only if this is an external (compiled from script - * source) call that has stack budget for the generating pc. - */ - if (fp->script && !(flags & JSINVOKE_INTERNAL)) - vp[-(intN)fp->script->depth] = (jsval)fp->pc; - return ok; - -bad: - js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS); - ok = JS_FALSE; - goto out2; -} - -JSBool -js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags, - uintN argc, jsval *argv, jsval *rval) -{ - JSStackFrame *fp, *oldfp, frame; - jsval *oldsp, *sp; - void *mark; - uintN i; - JSBool ok; - - fp = oldfp = cx->fp; - if (!fp) { - memset(&frame, 0, sizeof frame); - cx->fp = fp = &frame; - } - oldsp = fp->sp; - sp = js_AllocStack(cx, 2 + argc, &mark); - if (!sp) { - ok = JS_FALSE; - goto out; - } - - PUSH(fval); - PUSH(OBJECT_TO_JSVAL(obj)); - for (i = 0; i < argc; i++) - PUSH(argv[i]); - SAVE_SP(fp); - ok = js_Invoke(cx, argc, flags | JSINVOKE_INTERNAL); - if (ok) { - RESTORE_SP(fp); - - /* - * Store *rval in the a scoped local root if a scope is open, else in - * the lastInternalResult pigeon-hole GC root, solely so users of - * js_InternalInvoke and its direct and indirect (js_ValueToString for - * example) callers do not need to manage roots for local, temporary - * references to such results. - */ - *rval = POP_OPND(); - if (JSVAL_IS_GCTHING(*rval)) { - if (cx->localRootStack) { - if (js_PushLocalRoot(cx, cx->localRootStack, *rval) < 0) - ok = JS_FALSE; - } else { - cx->weakRoots.lastInternalResult = *rval; - } - } - } - - js_FreeStack(cx, mark); -out: - fp->sp = oldsp; - if (oldfp != fp) - cx->fp = oldfp; - - return ok; -} - -JSBool -js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval, - JSAccessMode mode, uintN argc, jsval *argv, jsval *rval) -{ - int stackDummy; - - /* - * js_InternalInvoke could result in another try to get or set the same id - * again, see bug 355497. - */ - if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_OVER_RECURSED); - return JS_FALSE; - } - /* - * Check general (not object-ops/class-specific) access from the running - * script to obj.id only if id has a scripted getter or setter that we're - * about to invoke. If we don't check this case, nothing else will -- no - * other native code has the chance to check. - * - * Contrast this non-native (scripted) case with native getter and setter - * accesses, where the native itself must do an access check, if security - * policies requires it. We make a checkAccess or checkObjectAccess call - * back to the embedding program only in those cases where we're not going - * to call an embedding-defined native function, getter, setter, or class - * hook anyway. Where we do call such a native, there's no need for the - * engine to impose a separate access check callback on all embeddings -- - * many embeddings have no security policy at all. - */ - JS_ASSERT(mode == JSACC_READ || mode == JSACC_WRITE); - if (cx->runtime->checkObjectAccess && - VALUE_IS_FUNCTION(cx, fval) && - FUN_INTERPRETED((JSFunction *) - JS_GetPrivate(cx, JSVAL_TO_OBJECT(fval))) && - !cx->runtime->checkObjectAccess(cx, obj, ID_TO_VALUE(id), mode, - &fval)) { - return JS_FALSE; - } - - return js_InternalCall(cx, obj, fval, argc, argv, rval); -} - -JSBool -js_Execute(JSContext *cx, JSObject *chain, JSScript *script, - JSStackFrame *down, uintN flags, jsval *result) -{ - JSInterpreterHook hook; - void *hookData, *mark; - JSStackFrame *oldfp, frame; - JSObject *obj, *tmp; - JSBool ok; - - hook = cx->runtime->executeHook; - hookData = mark = NULL; - oldfp = cx->fp; - frame.script = script; - if (down) { - /* Propagate arg/var state for eval and the debugger API. */ - frame.callobj = down->callobj; - frame.argsobj = down->argsobj; - frame.varobj = down->varobj; - frame.fun = down->fun; - frame.thisp = down->thisp; - frame.argc = down->argc; - frame.argv = down->argv; - frame.nvars = down->nvars; - frame.vars = down->vars; - frame.annotation = down->annotation; - frame.sharpArray = down->sharpArray; - } else { - frame.callobj = frame.argsobj = NULL; - obj = chain; - if (cx->options & JSOPTION_VAROBJFIX) { - while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) - obj = tmp; - } - frame.varobj = obj; - frame.fun = NULL; - frame.thisp = chain; - frame.argc = 0; - frame.argv = NULL; - frame.nvars = script->numGlobalVars; - if (frame.nvars) { - frame.vars = js_AllocRawStack(cx, frame.nvars, &mark); - if (!frame.vars) - return JS_FALSE; - memset(frame.vars, 0, frame.nvars * sizeof(jsval)); - } else { - frame.vars = NULL; - } - frame.annotation = NULL; - frame.sharpArray = NULL; - } - frame.rval = JSVAL_VOID; - frame.down = down; - frame.scopeChain = chain; - frame.pc = NULL; - frame.sp = oldfp ? oldfp->sp : NULL; - frame.spbase = NULL; - frame.sharpDepth = 0; - frame.flags = flags; - frame.dormantNext = NULL; - frame.xmlNamespace = NULL; - frame.blockChain = NULL; - - /* - * Here we wrap the call to js_Interpret with code to (conditionally) - * save and restore the old stack frame chain into a chain of 'dormant' - * frame chains. Since we are replacing cx->fp, we were running into - * the problem that if GC was called under this frame, some of the GC - * things associated with the old frame chain (available here only in - * the C variable 'oldfp') were not rooted and were being collected. - * - * So, now we preserve the links to these 'dormant' frame chains in cx - * before calling js_Interpret and cleanup afterwards. The GC walks - * these dormant chains and marks objects in the same way that it marks - * objects in the primary cx->fp chain. - */ - if (oldfp && oldfp != down) { - JS_ASSERT(!oldfp->dormantNext); - oldfp->dormantNext = cx->dormantFrameChain; - cx->dormantFrameChain = oldfp; - } - - cx->fp = &frame; - if (hook) - hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->executeHookData); - - /* - * Use frame.rval, not result, so the last result stays rooted across any - * GC activations nested within this js_Interpret. - */ - ok = js_Interpret(cx, script->code, &frame.rval); - *result = frame.rval; - - if (hookData) { - hook = cx->runtime->executeHook; - if (hook) - hook(cx, &frame, JS_FALSE, &ok, hookData); - } - if (mark) - js_FreeRawStack(cx, mark); - cx->fp = oldfp; - - if (oldfp && oldfp != down) { - JS_ASSERT(cx->dormantFrameChain == oldfp); - cx->dormantFrameChain = oldfp->dormantNext; - oldfp->dormantNext = NULL; - } - - return ok; -} - -#if JS_HAS_EXPORT_IMPORT -/* - * If id is JSVAL_VOID, import all exported properties from obj. - */ -static JSBool -ImportProperty(JSContext *cx, JSObject *obj, jsid id) -{ - JSBool ok; - JSIdArray *ida; - JSProperty *prop; - JSObject *obj2, *target, *funobj, *closure; - JSString *str; - uintN attrs; - jsint i; - jsval value; - - if (JSVAL_IS_VOID(id)) { - ida = JS_Enumerate(cx, obj); - if (!ida) - return JS_FALSE; - ok = JS_TRUE; - if (ida->length == 0) - goto out; - } else { - ida = NULL; - if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) - return JS_FALSE; - if (!prop) { - str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, - ID_TO_VALUE(id), NULL); - if (str) - js_ReportIsNotDefined(cx, JS_GetStringBytes(str)); - return JS_FALSE; - } - ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs); - OBJ_DROP_PROPERTY(cx, obj2, prop); - if (!ok) - return JS_FALSE; - if (!(attrs & JSPROP_EXPORTED)) { - str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, - ID_TO_VALUE(id), NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_NOT_EXPORTED, - JS_GetStringBytes(str)); - } - return JS_FALSE; - } - } - - target = cx->fp->varobj; - i = 0; - do { - if (ida) { - id = ida->vector[i]; - ok = OBJ_GET_ATTRIBUTES(cx, obj, id, NULL, &attrs); - if (!ok) - goto out; - if (!(attrs & JSPROP_EXPORTED)) - continue; - } - ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_IMPORT, &value, &attrs); - if (!ok) - goto out; - if (VALUE_IS_FUNCTION(cx, value)) { - funobj = JSVAL_TO_OBJECT(value); - closure = js_CloneFunctionObject(cx, funobj, obj); - if (!closure) { - ok = JS_FALSE; - goto out; - } - value = OBJECT_TO_JSVAL(closure); - } - - /* - * Handle the case of importing a property that refers to a local - * variable or formal parameter of a function activation. These - * properties are accessed by opcodes using stack slot numbers - * generated by the compiler rather than runtime name-lookup. These - * local references, therefore, bypass the normal scope chain lookup. - * So, instead of defining a new property in the activation object, - * modify the existing value in the stack slot. - */ - if (OBJ_GET_CLASS(cx, target) == &js_CallClass) { - ok = OBJ_LOOKUP_PROPERTY(cx, target, id, &obj2, &prop); - if (!ok) - goto out; - } else { - prop = NULL; - } - if (prop && target == obj2) { - ok = OBJ_SET_PROPERTY(cx, target, id, &value); - } else { - ok = OBJ_DEFINE_PROPERTY(cx, target, id, value, NULL, NULL, - attrs & ~(JSPROP_EXPORTED | - JSPROP_GETTER | - JSPROP_SETTER), - NULL); - } - if (prop) - OBJ_DROP_PROPERTY(cx, obj2, prop); - if (!ok) - goto out; - } while (ida && ++i < ida->length); - -out: - if (ida) - JS_DestroyIdArray(cx, ida); - return ok; -} -#endif /* JS_HAS_EXPORT_IMPORT */ - -JSBool -js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs, - JSObject **objp, JSProperty **propp) -{ - JSObject *obj2; - JSProperty *prop; - uintN oldAttrs, report; - JSBool isFunction; - jsval value; - const char *type, *name; - - if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) - return JS_FALSE; - if (propp) { - *objp = obj2; - *propp = prop; - } - if (!prop) - return JS_TRUE; - - /* - * Use prop as a speedup hint to OBJ_GET_ATTRIBUTES, but drop it on error. - * An assertion at label bad: will insist that it is null. - */ - if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs)) { - OBJ_DROP_PROPERTY(cx, obj2, prop); -#ifdef DEBUG - prop = NULL; -#endif - goto bad; - } - - /* - * From here, return true, or else goto bad on failure to null out params. - * If our caller doesn't want prop, drop it (we don't need it any longer). - */ - if (!propp) { - OBJ_DROP_PROPERTY(cx, obj2, prop); - prop = NULL; - } - - /* If either property is readonly, we have an error. */ - report = ((oldAttrs | attrs) & JSPROP_READONLY) - ? JSREPORT_ERROR - : JSREPORT_WARNING | JSREPORT_STRICT; - - if (report != JSREPORT_ERROR) { - /* - * Allow redeclaration of variables and functions, but insist that the - * new value is not a getter if the old value was, ditto for setters -- - * unless prop is impermanent (in which case anyone could delete it and - * redefine it, willy-nilly). - */ - if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER))) - return JS_TRUE; - if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0) - return JS_TRUE; - if (!(oldAttrs & JSPROP_PERMANENT)) - return JS_TRUE; - report = JSREPORT_ERROR; - } - - isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0; - if (!isFunction) { - if (!OBJ_GET_PROPERTY(cx, obj, id, &value)) - goto bad; - isFunction = VALUE_IS_FUNCTION(cx, value); - } - type = (oldAttrs & attrs & JSPROP_GETTER) - ? js_getter_str - : (oldAttrs & attrs & JSPROP_SETTER) - ? js_setter_str - : (oldAttrs & JSPROP_READONLY) - ? js_const_str - : isFunction - ? js_function_str - : js_var_str; - name = js_AtomToPrintableString(cx, JSID_TO_ATOM(id)); - if (!name) - goto bad; - return JS_ReportErrorFlagsAndNumber(cx, report, - js_GetErrorMessage, NULL, - JSMSG_REDECLARED_VAR, - type, name); - -bad: - if (propp) { - *objp = NULL; - *propp = NULL; - } - JS_ASSERT(!prop); - return JS_FALSE; -} - -JSBool -js_StrictlyEqual(jsval lval, jsval rval) -{ - jsval ltag = JSVAL_TAG(lval), rtag = JSVAL_TAG(rval); - jsdouble ld, rd; - - if (ltag == rtag) { - if (ltag == JSVAL_STRING) { - JSString *lstr = JSVAL_TO_STRING(lval), - *rstr = JSVAL_TO_STRING(rval); - return js_EqualStrings(lstr, rstr); - } - if (ltag == JSVAL_DOUBLE) { - ld = *JSVAL_TO_DOUBLE(lval); - rd = *JSVAL_TO_DOUBLE(rval); - return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); - } - return lval == rval; - } - if (ltag == JSVAL_DOUBLE && JSVAL_IS_INT(rval)) { - ld = *JSVAL_TO_DOUBLE(lval); - rd = JSVAL_TO_INT(rval); - return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); - } - if (JSVAL_IS_INT(lval) && rtag == JSVAL_DOUBLE) { - ld = JSVAL_TO_INT(lval); - rd = *JSVAL_TO_DOUBLE(rval); - return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); - } - return lval == rval; -} - -JSBool -js_InvokeConstructor(JSContext *cx, jsval *vp, uintN argc) -{ - JSFunction *fun; - JSObject *obj, *obj2, *proto, *parent; - jsval lval, rval; - JSClass *clasp, *funclasp; - - fun = NULL; - obj2 = NULL; - lval = *vp; - if (!JSVAL_IS_OBJECT(lval) || - (obj2 = JSVAL_TO_OBJECT(lval)) == NULL || - /* XXX clean up to avoid special cases above ObjectOps layer */ - OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass || - !obj2->map->ops->construct) - { - fun = js_ValueToFunction(cx, vp, JSV2F_CONSTRUCT); - if (!fun) - return JS_FALSE; - } - - clasp = &js_ObjectClass; - if (!obj2) { - proto = parent = NULL; - fun = NULL; - } else { - /* - * Get the constructor prototype object for this function. - * Use the nominal 'this' parameter slot, vp[1], as a local - * root to protect this prototype, in case it has no other - * strong refs. - */ - if (!OBJ_GET_PROPERTY(cx, obj2, - ATOM_TO_JSID(cx->runtime->atomState - .classPrototypeAtom), - &vp[1])) { - return JS_FALSE; - } - rval = vp[1]; - proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL; - parent = OBJ_GET_PARENT(cx, obj2); - - if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) { - funclasp = ((JSFunction *)JS_GetPrivate(cx, obj2))->clasp; - if (funclasp) - clasp = funclasp; - } - } - obj = js_NewObject(cx, clasp, proto, parent); - if (!obj) - return JS_FALSE; - - /* Now we have an object with a constructor method; call it. */ - vp[1] = OBJECT_TO_JSVAL(obj); - if (!js_Invoke(cx, argc, JSINVOKE_CONSTRUCT)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return JS_FALSE; - } - - /* Check the return value and if it's primitive, force it to be obj. */ - rval = *vp; - if (JSVAL_IS_PRIMITIVE(rval)) { - if (!fun) { - /* native [[Construct]] returning primitive is error */ - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_NEW_RESULT, - js_ValueToPrintableString(cx, rval)); - return JS_FALSE; - } - *vp = OBJECT_TO_JSVAL(obj); - } - - JS_RUNTIME_METER(cx->runtime, constructs); - return JS_TRUE; -} - -static JSBool -InternStringElementId(JSContext *cx, jsval idval, jsid *idp) -{ - JSAtom *atom; - - atom = js_ValueToStringAtom(cx, idval); - if (!atom) - return JS_FALSE; - *idp = ATOM_TO_JSID(atom); - return JS_TRUE; -} - -static JSBool -InternNonIntElementId(JSContext *cx, jsval idval, jsid *idp) -{ - JS_ASSERT(!JSVAL_IS_INT(idval)); - -#if JS_HAS_XML_SUPPORT - if (JSVAL_IS_OBJECT(idval)) { - *idp = OBJECT_JSVAL_TO_JSID(idval); - return JS_TRUE; - } -#endif - - return InternStringElementId(cx, idval, idp); -} - -#if JS_HAS_XML_SUPPORT -#define CHECK_ELEMENT_ID(obj, id) \ - JS_BEGIN_MACRO \ - if (JSID_IS_OBJECT(id) && !OBJECT_IS_XML(cx, obj)) { \ - SAVE_SP_AND_PC(fp); \ - ok = InternStringElementId(cx, OBJECT_JSID_TO_JSVAL(id), &id); \ - if (!ok) \ - goto out; \ - } \ - JS_END_MACRO - -#else -#define CHECK_ELEMENT_ID(obj, id) JS_ASSERT(!JSID_IS_OBJECT(id)) -#endif - -#ifndef MAX_INTERP_LEVEL -#if defined(XP_OS2) -#define MAX_INTERP_LEVEL 250 -#else -#define MAX_INTERP_LEVEL 1000 -#endif -#endif - -#define MAX_INLINE_CALL_COUNT 1000 - -/* - * Threaded interpretation via computed goto appears to be well-supported by - * GCC 3 and higher. IBM's C compiler when run with the right options (e.g., - * -qlanglvl=extended) also supports threading. Ditto the SunPro C compiler. - * Currently it's broken for JS_VERSION < 160, though this isn't worth fixing. - * Add your compiler support macros here. - */ -#if JS_VERSION >= 160 && ( \ - __GNUC__ >= 3 || \ - (__IBMC__ >= 700 && defined __IBM_COMPUTED_GOTO) || \ - __SUNPRO_C >= 0x570) -# define JS_THREADED_INTERP 1 -#else -# undef JS_THREADED_INTERP -#endif - -JSBool -js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result) -{ - JSRuntime *rt; - JSStackFrame *fp; - JSScript *script; - uintN inlineCallCount; - JSObject *obj, *obj2, *parent; - JSVersion currentVersion, originalVersion; - JSBranchCallback onbranch; - JSBool ok, cond; - JSTrapHandler interruptHandler; - jsint depth, len; - jsval *sp, *newsp; - void *mark; - jsbytecode *endpc, *pc2; - JSOp op, op2; - jsatomid atomIndex; - JSAtom *atom; - uintN argc, attrs, flags, slot; - jsval *vp, lval, rval, ltmp, rtmp; - jsid id; - JSObject *withobj, *iterobj; - JSProperty *prop; - JSScopeProperty *sprop; - JSString *str, *str2; - jsint i, j; - jsdouble d, d2; - JSClass *clasp; - JSFunction *fun; - JSType type; -#if !defined JS_THREADED_INTERP && defined DEBUG - FILE *tracefp = NULL; -#endif -#if JS_HAS_EXPORT_IMPORT - JSIdArray *ida; -#endif - jsint low, high, off, npairs; - JSBool match; -#if JS_HAS_GETTER_SETTER - JSPropertyOp getter, setter; -#endif - int stackDummy; - -#ifdef __GNUC__ -# define JS_EXTENSION __extension__ -# define JS_EXTENSION_(s) __extension__ ({ s; }) -#else -# define JS_EXTENSION -# define JS_EXTENSION_(s) s -#endif - -#ifdef JS_THREADED_INTERP - static void *normalJumpTable[] = { -# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ - JS_EXTENSION &&L_##op, -# include "jsopcode.tbl" -# undef OPDEF - }; - - static void *interruptJumpTable[] = { -# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ - ((op != JSOP_PUSHOBJ) \ - ? JS_EXTENSION &&interrupt \ - : JS_EXTENSION &&L_JSOP_PUSHOBJ), -# include "jsopcode.tbl" -# undef OPDEF - }; - - register void **jumpTable = normalJumpTable; - -# define DO_OP() JS_EXTENSION_(goto *jumpTable[op]) -# define DO_NEXT_OP(n) do { op = *(pc += (n)); DO_OP(); } while (0) -# define BEGIN_CASE(OP) L_##OP: -# define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH); -# define END_VARLEN_CASE DO_NEXT_OP(len); -# define EMPTY_CASE(OP) BEGIN_CASE(OP) op = *++pc; DO_OP(); -#else -# define DO_OP() goto do_op -# define DO_NEXT_OP(n) goto advance_pc -# define BEGIN_CASE(OP) case OP: -# define END_CASE(OP) break; -# define END_VARLEN_CASE break; -# define EMPTY_CASE(OP) BEGIN_CASE(OP) END_CASE(OP) -#endif - - *result = JSVAL_VOID; - rt = cx->runtime; - - /* Set registerized frame pointer and derived script pointer. */ - fp = cx->fp; - script = fp->script; - JS_ASSERT(script->length != 0); - - /* Count of JS function calls that nest in this C js_Interpret frame. */ - inlineCallCount = 0; - - /* - * Optimized Get and SetVersion for proper script language versioning. - * - * If any native method or JSClass/JSObjectOps hook calls js_SetVersion - * and changes cx->version, the effect will "stick" and we will stop - * maintaining currentVersion. This is relied upon by testsuites, for - * the most part -- web browsers select version before compiling and not - * at run-time. - */ - currentVersion = script->version; - originalVersion = cx->version; - if (currentVersion != originalVersion) - js_SetVersion(cx, currentVersion); - -#ifdef __GNUC__ - flags = 0; /* suppress gcc warnings */ - id = 0; -#endif - - /* - * Prepare to call a user-supplied branch handler, and abort the script - * if it returns false. We reload onbranch after calling out to native - * functions (but not to getters, setters, or other native hooks). - */ -#define LOAD_BRANCH_CALLBACK(cx) (onbranch = (cx)->branchCallback) - - LOAD_BRANCH_CALLBACK(cx); -#define CHECK_BRANCH(len) \ - JS_BEGIN_MACRO \ - if (len <= 0 && onbranch) { \ - SAVE_SP_AND_PC(fp); \ - if (!(ok = (*onbranch)(cx, script))) \ - goto out; \ - } \ - JS_END_MACRO - - /* - * Load the debugger's interrupt hook here and after calling out to native - * functions (but not to getters, setters, or other native hooks), so we do - * not have to reload it each time through the interpreter loop -- we hope - * the compiler can keep it in a register when it is non-null. - */ -#ifdef JS_THREADED_INTERP -# define LOAD_JUMP_TABLE() \ - (jumpTable = interruptHandler ? interruptJumpTable : normalJumpTable) -#else -# define LOAD_JUMP_TABLE() /* nothing */ -#endif - -#define LOAD_INTERRUPT_HANDLER(rt) \ - JS_BEGIN_MACRO \ - interruptHandler = (rt)->interruptHandler; \ - LOAD_JUMP_TABLE(); \ - JS_END_MACRO - - LOAD_INTERRUPT_HANDLER(rt); - - /* Check for too much js_Interpret nesting, or too deep a C stack. */ - if (++cx->interpLevel == MAX_INTERP_LEVEL || - !JS_CHECK_STACK_SIZE(cx, stackDummy)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); - ok = JS_FALSE; - goto out2; - } - - /* - * Allocate operand and pc stack slots for the script's worst-case depth, - * unless we're called to interpret a part of an already active script, a - * filtering predicate expression for example. - */ - depth = (jsint) script->depth; - if (JS_LIKELY(!fp->spbase)) { - newsp = js_AllocRawStack(cx, (uintN)(2 * depth), &mark); - if (!newsp) { - ok = JS_FALSE; - goto out2; - } - sp = newsp + depth; - fp->spbase = sp; - SAVE_SP(fp); - } else { - sp = fp->sp; - JS_ASSERT(JS_UPTRDIFF(sp, fp->spbase) <= depth * sizeof(jsval)); - newsp = fp->spbase - depth; - mark = NULL; - } - - /* - * To support generator_throw and to catch ignored exceptions, fail right - * away if cx->throwing is set. If no exception is pending, null obj in - * case a callable object is being sent into a yield expression, and the - * yield's result is invoked. - */ - ok = !cx->throwing; - if (!ok) { -#ifdef DEBUG_NOT_THROWING - printf("JS INTERPRETER CALLED WITH PENDING EXCEPTION %lx\n", - (unsigned long) cx->exception); -#endif - goto out; - } - obj = NULL; - -#ifdef JS_THREADED_INTERP - - /* - * This is a loop, but it does not look like a loop. The loop-closing - * jump is distributed throughout interruptJumpTable, and comes back to - * the interrupt label. The dispatch on op is through normalJumpTable. - * The trick is LOAD_INTERRUPT_HANDLER setting jumpTable appropriately. - * - * It is important that "op" be initialized before the interrupt label - * because it is possible for "op" to be specially assigned during the - * normally processing of an opcode while looping (in particular, this - * happens in JSOP_TRAP while debugging). We rely on DO_NEXT_OP to - * correctly manage "op" in all other cases. - */ - op = (JSOp) *pc; - if (interruptHandler) { -interrupt: - SAVE_SP_AND_PC(fp); - switch (interruptHandler(cx, script, pc, &rval, - rt->interruptHandlerData)) { - case JSTRAP_ERROR: - ok = JS_FALSE; - goto out; - case JSTRAP_CONTINUE: - break; - case JSTRAP_RETURN: - fp->rval = rval; - goto out; - case JSTRAP_THROW: - cx->throwing = JS_TRUE; - cx->exception = rval; - ok = JS_FALSE; - goto out; - default:; - } - LOAD_INTERRUPT_HANDLER(rt); - } - - JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT); - JS_EXTENSION_(goto *normalJumpTable[op]); - -#else /* !JS_THREADED_INTERP */ - - for (;;) { - op = (JSOp) *pc; - do_op: - len = js_CodeSpec[op].length; - -#ifdef DEBUG - tracefp = (FILE *) cx->tracefp; - if (tracefp) { - intN nuses, n; - - fprintf(tracefp, "%4u: ", js_PCToLineNumber(cx, script, pc)); - js_Disassemble1(cx, script, pc, - PTRDIFF(pc, script->code, jsbytecode), JS_FALSE, - tracefp); - nuses = js_CodeSpec[op].nuses; - if (nuses) { - SAVE_SP_AND_PC(fp); - for (n = -nuses; n < 0; n++) { - str = js_DecompileValueGenerator(cx, n, sp[n], NULL); - if (str) { - fprintf(tracefp, "%s %s", - (n == -nuses) ? " inputs:" : ",", - JS_GetStringBytes(str)); - } - } - fprintf(tracefp, " @ %d\n", sp - fp->spbase); - } - } -#endif /* DEBUG */ - - if (interruptHandler && op != JSOP_PUSHOBJ) { - SAVE_SP_AND_PC(fp); - switch (interruptHandler(cx, script, pc, &rval, - rt->interruptHandlerData)) { - case JSTRAP_ERROR: - ok = JS_FALSE; - goto out; - case JSTRAP_CONTINUE: - break; - case JSTRAP_RETURN: - fp->rval = rval; - goto out; - case JSTRAP_THROW: - cx->throwing = JS_TRUE; - cx->exception = rval; - ok = JS_FALSE; - goto out; - default:; - } - LOAD_INTERRUPT_HANDLER(rt); - } - - switch (op) { - -#endif /* !JS_THREADED_INTERP */ - - BEGIN_CASE(JSOP_STOP) - goto out; - - EMPTY_CASE(JSOP_NOP) - - BEGIN_CASE(JSOP_GROUP) - obj = NULL; - END_CASE(JSOP_GROUP) - - BEGIN_CASE(JSOP_PUSH) - PUSH_OPND(JSVAL_VOID); - END_CASE(JSOP_PUSH) - - BEGIN_CASE(JSOP_POP) - sp--; - END_CASE(JSOP_POP) - - BEGIN_CASE(JSOP_POP2) - sp -= 2; - END_CASE(JSOP_POP2) - - BEGIN_CASE(JSOP_SWAP) - vp = sp - depth; /* swap generating pc's for the decompiler */ - ltmp = vp[-1]; - vp[-1] = vp[-2]; - sp[-2] = ltmp; - rtmp = sp[-1]; - sp[-1] = sp[-2]; - sp[-2] = rtmp; - END_CASE(JSOP_SWAP) - - BEGIN_CASE(JSOP_POPV) - *result = POP_OPND(); - END_CASE(JSOP_POPV) - - BEGIN_CASE(JSOP_ENTERWITH) - FETCH_OBJECT(cx, -1, rval, obj); - SAVE_SP_AND_PC(fp); - OBJ_TO_INNER_OBJECT(cx, obj); - if (!obj || !(obj2 = js_GetScopeChain(cx, fp))) { - ok = JS_FALSE; - goto out; - } - withobj = js_NewWithObject(cx, obj, obj2, sp - fp->spbase - 1); - if (!withobj) { - ok = JS_FALSE; - goto out; - } - fp->scopeChain = withobj; - STORE_OPND(-1, OBJECT_TO_JSVAL(withobj)); - END_CASE(JSOP_ENTERWITH) - - BEGIN_CASE(JSOP_LEAVEWITH) - rval = POP_OPND(); - JS_ASSERT(JSVAL_IS_OBJECT(rval)); - withobj = JSVAL_TO_OBJECT(rval); - JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass); - fp->scopeChain = OBJ_GET_PARENT(cx, withobj); - JS_SetPrivate(cx, withobj, NULL); - END_CASE(JSOP_LEAVEWITH) - - BEGIN_CASE(JSOP_SETRVAL) - ASSERT_NOT_THROWING(cx); - fp->rval = POP_OPND(); - END_CASE(JSOP_SETRVAL) - - BEGIN_CASE(JSOP_RETURN) - CHECK_BRANCH(-1); - fp->rval = POP_OPND(); - /* FALL THROUGH */ - - BEGIN_CASE(JSOP_RETRVAL) /* fp->rval already set */ - ASSERT_NOT_THROWING(cx); - if (inlineCallCount) - inline_return: - { - JSInlineFrame *ifp = (JSInlineFrame *) fp; - void *hookData = ifp->hookData; - - /* - * If fp has blocks on its scope chain, home their locals now, - * before calling any debugger hook, and before freeing stack. - * This matches the order of block putting and hook calling in - * the "out-of-line" return code at the bottom of js_Interpret - * and in js_Invoke. - */ - if (fp->flags & JSFRAME_POP_BLOCKS) { - SAVE_SP_AND_PC(fp); - ok &= PutBlockObjects(cx, fp); - } - - if (hookData) { - JSInterpreterHook hook = rt->callHook; - if (hook) { - SAVE_SP_AND_PC(fp); - hook(cx, fp, JS_FALSE, &ok, hookData); - LOAD_INTERRUPT_HANDLER(rt); - } - } - - /* - * If fp has a call object, sync values and clear the back- - * pointer. This can happen for a lightweight function if it - * calls eval unexpectedly (in a way that is hidden from the - * compiler). See bug 325540. - */ - if (fp->callobj) { - SAVE_SP_AND_PC(fp); - ok &= js_PutCallObject(cx, fp); - } - - if (fp->argsobj) { - SAVE_SP_AND_PC(fp); - ok &= js_PutArgsObject(cx, fp); - } - - /* Restore context version only if callee hasn't set version. */ - if (JS_LIKELY(cx->version == currentVersion)) { - currentVersion = ifp->callerVersion; - if (currentVersion != cx->version) - js_SetVersion(cx, currentVersion); - } - - /* Store the return value in the caller's operand frame. */ - vp = ifp->rvp; - *vp = fp->rval; - - /* Restore cx->fp and release the inline frame's space. */ - cx->fp = fp = fp->down; - JS_ARENA_RELEASE(&cx->stackPool, ifp->mark); - - /* Restore sp to point just above the return value. */ - fp->sp = vp + 1; - RESTORE_SP(fp); - - /* Restore the calling script's interpreter registers. */ - obj = NULL; - script = fp->script; - depth = (jsint) script->depth; - pc = fp->pc; -#ifndef JS_THREADED_INTERP - endpc = script->code + script->length; -#endif - - /* Store the generating pc for the return value. */ - vp[-depth] = (jsval)pc; - - /* Resume execution in the calling frame. */ - inlineCallCount--; - if (JS_LIKELY(ok)) { - JS_ASSERT(js_CodeSpec[*pc].length == JSOP_CALL_LENGTH); - len = JSOP_CALL_LENGTH; - DO_NEXT_OP(len); - } - } - goto out; - - BEGIN_CASE(JSOP_DEFAULT) - (void) POP(); - /* FALL THROUGH */ - BEGIN_CASE(JSOP_GOTO) - len = GET_JUMP_OFFSET(pc); - CHECK_BRANCH(len); - END_VARLEN_CASE - - BEGIN_CASE(JSOP_IFEQ) - POP_BOOLEAN(cx, rval, cond); - if (cond == JS_FALSE) { - len = GET_JUMP_OFFSET(pc); - CHECK_BRANCH(len); - DO_NEXT_OP(len); - } - END_CASE(JSOP_IFEQ) - - BEGIN_CASE(JSOP_IFNE) - POP_BOOLEAN(cx, rval, cond); - if (cond != JS_FALSE) { - len = GET_JUMP_OFFSET(pc); - CHECK_BRANCH(len); - DO_NEXT_OP(len); - } - END_CASE(JSOP_IFNE) - - BEGIN_CASE(JSOP_OR) - POP_BOOLEAN(cx, rval, cond); - if (cond == JS_TRUE) { - len = GET_JUMP_OFFSET(pc); - PUSH_OPND(rval); - DO_NEXT_OP(len); - } - END_CASE(JSOP_OR) - - BEGIN_CASE(JSOP_AND) - POP_BOOLEAN(cx, rval, cond); - if (cond == JS_FALSE) { - len = GET_JUMP_OFFSET(pc); - PUSH_OPND(rval); - DO_NEXT_OP(len); - } - END_CASE(JSOP_AND) - - BEGIN_CASE(JSOP_DEFAULTX) - (void) POP(); - /* FALL THROUGH */ - BEGIN_CASE(JSOP_GOTOX) - len = GET_JUMPX_OFFSET(pc); - CHECK_BRANCH(len); - END_VARLEN_CASE - - BEGIN_CASE(JSOP_IFEQX) - POP_BOOLEAN(cx, rval, cond); - if (cond == JS_FALSE) { - len = GET_JUMPX_OFFSET(pc); - CHECK_BRANCH(len); - DO_NEXT_OP(len); - } - END_CASE(JSOP_IFEQX) - - BEGIN_CASE(JSOP_IFNEX) - POP_BOOLEAN(cx, rval, cond); - if (cond != JS_FALSE) { - len = GET_JUMPX_OFFSET(pc); - CHECK_BRANCH(len); - DO_NEXT_OP(len); - } - END_CASE(JSOP_IFNEX) - - BEGIN_CASE(JSOP_ORX) - POP_BOOLEAN(cx, rval, cond); - if (cond == JS_TRUE) { - len = GET_JUMPX_OFFSET(pc); - PUSH_OPND(rval); - DO_NEXT_OP(len); - } - END_CASE(JSOP_ORX) - - BEGIN_CASE(JSOP_ANDX) - POP_BOOLEAN(cx, rval, cond); - if (cond == JS_FALSE) { - len = GET_JUMPX_OFFSET(pc); - PUSH_OPND(rval); - DO_NEXT_OP(len); - } - END_CASE(JSOP_ANDX) - -/* - * If the index value at sp[n] is not an int that fits in a jsval, it could - * be an object (an XML QName, AttributeName, or AnyName), but only if we are - * compiling with JS_HAS_XML_SUPPORT. Otherwise convert the index value to a - * string atom id. - */ -#define FETCH_ELEMENT_ID(n, id) \ - JS_BEGIN_MACRO \ - jsval idval_ = FETCH_OPND(n); \ - if (JSVAL_IS_INT(idval_)) { \ - id = INT_JSVAL_TO_JSID(idval_); \ - } else { \ - SAVE_SP_AND_PC(fp); \ - ok = InternNonIntElementId(cx, idval_, &id); \ - if (!ok) \ - goto out; \ - } \ - JS_END_MACRO - - BEGIN_CASE(JSOP_IN) - SAVE_SP_AND_PC(fp); - rval = FETCH_OPND(-1); - if (JSVAL_IS_PRIMITIVE(rval)) { - str = js_DecompileValueGenerator(cx, -1, rval, NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_IN_NOT_OBJECT, - JS_GetStringBytes(str)); - } - ok = JS_FALSE; - goto out; - } - obj = JSVAL_TO_OBJECT(rval); - FETCH_ELEMENT_ID(-2, id); - CHECK_ELEMENT_ID(obj, id); - ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); - if (!ok) - goto out; - sp--; - STORE_OPND(-1, BOOLEAN_TO_JSVAL(prop != NULL)); - if (prop) - OBJ_DROP_PROPERTY(cx, obj2, prop); - END_CASE(JSOP_IN) - - BEGIN_CASE(JSOP_FOREACH) - flags = JSITER_ENUMERATE | JSITER_FOREACH; - goto value_to_iter; - -#if JS_HAS_DESTRUCTURING - BEGIN_CASE(JSOP_FOREACHKEYVAL) - flags = JSITER_ENUMERATE | JSITER_FOREACH | JSITER_KEYVALUE; - goto value_to_iter; -#endif - - BEGIN_CASE(JSOP_FORIN) - /* - * Set JSITER_ENUMERATE to indicate that for-in loop should use - * the enumeration protocol's iterator for compatibility if an - * explicit iterator is not given via the optional __iterator__ - * method. - */ - flags = JSITER_ENUMERATE; - - value_to_iter: - JS_ASSERT(sp > fp->spbase); - SAVE_SP_AND_PC(fp); - ok = js_ValueToIterator(cx, flags, &sp[-1]); - if (!ok) - goto out; - JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[-1])); - JS_ASSERT(JSOP_FORIN_LENGTH == js_CodeSpec[op].length); - END_CASE(JSOP_FORIN) - - BEGIN_CASE(JSOP_FORPROP) - /* - * Handle JSOP_FORPROP first, so the cost of the goto do_forinloop - * is not paid for the more common cases. - */ - lval = FETCH_OPND(-1); - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - i = -2; - goto do_forinloop; - - BEGIN_CASE(JSOP_FORNAME) - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - - /* - * ECMA 12.6.3 says to eval the LHS after looking for properties - * to enumerate, and bail without LHS eval if there are no props. - * We do Find here to share the most code at label do_forinloop. - * If looking for enumerable properties could have side effects, - * then we'd have to move this into the common code and condition - * it on op == JSOP_FORNAME. - */ - SAVE_SP_AND_PC(fp); - ok = js_FindProperty(cx, id, &obj, &obj2, &prop); - if (!ok) - goto out; - if (prop) - OBJ_DROP_PROPERTY(cx, obj2, prop); - lval = OBJECT_TO_JSVAL(obj); - /* FALL THROUGH */ - - BEGIN_CASE(JSOP_FORARG) - BEGIN_CASE(JSOP_FORVAR) - BEGIN_CASE(JSOP_FORLOCAL) - /* - * JSOP_FORARG and JSOP_FORVAR don't require any lval computation - * here, because they address slots on the stack (in fp->args and - * fp->vars, respectively). Same applies to JSOP_FORLOCAL, which - * addresses fp->spbase. - */ - /* FALL THROUGH */ - - BEGIN_CASE(JSOP_FORELEM) - /* - * JSOP_FORELEM simply initializes or updates the iteration state - * and leaves the index expression evaluation and assignment to the - * enumerator until after the next property has been acquired, via - * a JSOP_ENUMELEM bytecode. - */ - i = -1; - - do_forinloop: - /* - * Reach under the top of stack to find our property iterator, a - * JSObject that contains the iteration state. - */ - JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[i])); - iterobj = JSVAL_TO_OBJECT(sp[i]); - - SAVE_SP_AND_PC(fp); - ok = js_CallIteratorNext(cx, iterobj, &rval); - if (!ok) - goto out; - if (rval == JSVAL_HOLE) { - rval = JSVAL_FALSE; - goto end_forinloop; - } - - switch (op) { - case JSOP_FORARG: - slot = GET_ARGNO(pc); - JS_ASSERT(slot < fp->fun->nargs); - fp->argv[slot] = rval; - break; - - case JSOP_FORVAR: - slot = GET_VARNO(pc); - JS_ASSERT(slot < fp->fun->u.i.nvars); - fp->vars[slot] = rval; - break; - - case JSOP_FORLOCAL: - slot = GET_UINT16(pc); - JS_ASSERT(slot < (uintN)depth); - vp = &fp->spbase[slot]; - GC_POKE(cx, *vp); - *vp = rval; - break; - - case JSOP_FORELEM: - /* FORELEM is not a SET operation, it's more like BINDNAME. */ - PUSH_OPND(rval); - break; - - default: - JS_ASSERT(op == JSOP_FORPROP || op == JSOP_FORNAME); - - /* Convert lval to a non-null object containing id. */ - VALUE_TO_OBJECT(cx, lval, obj); - if (op == JSOP_FORPROP) - STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - - /* Set the variable obj[id] to refer to rval. */ - fp->flags |= JSFRAME_ASSIGNING; - ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); - fp->flags &= ~JSFRAME_ASSIGNING; - if (!ok) - goto out; - break; - } - - /* Push true to keep looping through properties. */ - rval = JSVAL_TRUE; - - end_forinloop: - sp += i + 1; - PUSH_OPND(rval); - len = js_CodeSpec[op].length; - DO_NEXT_OP(len); - - BEGIN_CASE(JSOP_DUP) - JS_ASSERT(sp > fp->spbase); - vp = sp - 1; /* address top of stack */ - rval = *vp; - vp -= depth; /* address generating pc */ - vp[1] = *vp; - PUSH(rval); - END_CASE(JSOP_DUP) - - BEGIN_CASE(JSOP_DUP2) - JS_ASSERT(sp - 2 >= fp->spbase); - vp = sp - 1; /* address top of stack */ - lval = vp[-1]; - rval = *vp; - vp -= depth; /* address generating pc */ - vp[1] = vp[2] = *vp; - PUSH(lval); - PUSH(rval); - END_CASE(JSOP_DUP2) - -#define PROPERTY_OP(n, call) \ - JS_BEGIN_MACRO \ - /* Fetch the left part and resolve it to a non-null object. */ \ - FETCH_OBJECT(cx, n, lval, obj); \ - \ - /* Get or set the property, set ok false if error, true if success. */\ - SAVE_SP_AND_PC(fp); \ - call; \ - if (!ok) \ - goto out; \ - JS_END_MACRO - -#define ELEMENT_OP(n, call) \ - JS_BEGIN_MACRO \ - /* Fetch the right part and resolve it to an internal id. */ \ - FETCH_ELEMENT_ID(n, id); \ - \ - /* Fetch the left part and resolve it to a non-null object. */ \ - FETCH_OBJECT(cx, n - 1, lval, obj); \ - \ - /* Ensure that id has a type suitable for use with obj. */ \ - CHECK_ELEMENT_ID(obj, id); \ - \ - /* Get or set the element, set ok false if error, true if success. */ \ - SAVE_SP_AND_PC(fp); \ - call; \ - if (!ok) \ - goto out; \ - JS_END_MACRO - -#define NATIVE_GET(cx,obj,pobj,sprop,vp) \ - JS_BEGIN_MACRO \ - if (SPROP_HAS_STUB_GETTER(sprop)) { \ - /* Fast path for Object instance properties. */ \ - JS_ASSERT((sprop)->slot != SPROP_INVALID_SLOT || \ - !SPROP_HAS_STUB_SETTER(sprop)); \ - *vp = ((sprop)->slot != SPROP_INVALID_SLOT) \ - ? LOCKED_OBJ_GET_SLOT(pobj, (sprop)->slot) \ - : JSVAL_VOID; \ - } else { \ - SAVE_SP_AND_PC(fp); \ - ok = js_NativeGet(cx, obj, pobj, sprop, vp); \ - if (!ok) \ - goto out; \ - } \ - JS_END_MACRO - -#define NATIVE_SET(cx,obj,sprop,vp) \ - JS_BEGIN_MACRO \ - if (SPROP_HAS_STUB_SETTER(sprop) && \ - (sprop)->slot != SPROP_INVALID_SLOT) { \ - /* Fast path for Object instance properties. */ \ - LOCKED_OBJ_SET_SLOT(obj, (sprop)->slot, *vp); \ - } else { \ - SAVE_SP_AND_PC(fp); \ - ok = js_NativeSet(cx, obj, sprop, vp); \ - if (!ok) \ - goto out; \ - } \ - JS_END_MACRO - -/* - * CACHED_GET and CACHED_SET use cx, obj, id, and rval from their callers' - * environments. - */ -#define CACHED_GET(call) \ - JS_BEGIN_MACRO \ - if (!OBJ_IS_NATIVE(obj)) { \ - ok = call; \ - } else { \ - JS_LOCK_OBJ(cx, obj); \ - PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop); \ - if (sprop) { \ - NATIVE_GET(cx, obj, obj, sprop, &rval); \ - JS_UNLOCK_OBJ(cx, obj); \ - } else { \ - JS_UNLOCK_OBJ(cx, obj); \ - ok = call; \ - /* No fill here: js_GetProperty fills the cache. */ \ - } \ - } \ - JS_END_MACRO - -#define CACHED_SET(call) \ - JS_BEGIN_MACRO \ - if (!OBJ_IS_NATIVE(obj)) { \ - ok = call; \ - } else { \ - JSScope *scope_; \ - JS_LOCK_OBJ(cx, obj); \ - PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop); \ - if (sprop && \ - !(sprop->attrs & JSPROP_READONLY) && \ - (scope_ = OBJ_SCOPE(obj), !SCOPE_IS_SEALED(scope_))) { \ - NATIVE_SET(cx, obj, sprop, &rval); \ - JS_UNLOCK_SCOPE(cx, scope_); \ - } else { \ - JS_UNLOCK_OBJ(cx, obj); \ - ok = call; \ - /* No fill here: js_SetProperty writes through the cache. */ \ - } \ - } \ - JS_END_MACRO - -#define BEGIN_LITOPX_CASE(OP,PCOFF) \ - BEGIN_CASE(OP) \ - pc2 = pc; \ - atomIndex = GET_ATOM_INDEX(pc + PCOFF); \ - do_##OP: \ - atom = js_GetAtom(cx, &script->atomMap, atomIndex); - -#define END_LITOPX_CASE(OP) \ - END_CASE(OP) - - BEGIN_LITOPX_CASE(JSOP_SETCONST, 0) - obj = fp->varobj; - rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - ok = OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval, - NULL, NULL, - JSPROP_ENUMERATE | JSPROP_PERMANENT | - JSPROP_READONLY, - NULL); - if (!ok) - goto out; - STORE_OPND(-1, rval); - END_LITOPX_CASE(JSOP_SETCONST) - -#if JS_HAS_DESTRUCTURING - BEGIN_CASE(JSOP_ENUMCONSTELEM) - FETCH_ELEMENT_ID(-1, id); - FETCH_OBJECT(cx, -2, lval, obj); - CHECK_ELEMENT_ID(obj, id); - rval = FETCH_OPND(-3); - SAVE_SP_AND_PC(fp); - ok = OBJ_DEFINE_PROPERTY(cx, obj, id, rval, NULL, NULL, - JSPROP_ENUMERATE | JSPROP_PERMANENT | - JSPROP_READONLY, - NULL); - if (!ok) - goto out; - sp -= 3; - END_CASE(JSOP_ENUMCONSTELEM) -#endif - - BEGIN_LITOPX_CASE(JSOP_BINDNAME, 0) - SAVE_SP_AND_PC(fp); - obj = js_FindIdentifierBase(cx, ATOM_TO_JSID(atom)); - if (!obj) { - ok = JS_FALSE; - goto out; - } - PUSH_OPND(OBJECT_TO_JSVAL(obj)); - END_LITOPX_CASE(JSOP_BINDNAME) - - BEGIN_CASE(JSOP_SETNAME) - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - rval = FETCH_OPND(-1); - lval = FETCH_OPND(-2); - JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval)); - obj = JSVAL_TO_OBJECT(lval); - SAVE_SP_AND_PC(fp); - CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); - if (!ok) - goto out; - sp--; - STORE_OPND(-1, rval); - obj = NULL; - END_CASE(JSOP_SETNAME) - -#define INTEGER_OP(OP, EXTRA_CODE) \ - JS_BEGIN_MACRO \ - FETCH_INT(cx, -1, j); \ - FETCH_INT(cx, -2, i); \ - EXTRA_CODE \ - i = i OP j; \ - sp--; \ - STORE_INT(cx, -1, i); \ - JS_END_MACRO - -#define BITWISE_OP(OP) INTEGER_OP(OP, (void) 0;) -#define SIGNED_SHIFT_OP(OP) INTEGER_OP(OP, j &= 31;) - - BEGIN_CASE(JSOP_BITOR) - BITWISE_OP(|); - END_CASE(JSOP_BITOR) - - BEGIN_CASE(JSOP_BITXOR) - BITWISE_OP(^); - END_CASE(JSOP_BITXOR) - - BEGIN_CASE(JSOP_BITAND) - BITWISE_OP(&); - END_CASE(JSOP_BITAND) - -#define RELATIONAL_OP(OP) \ - JS_BEGIN_MACRO \ - rval = FETCH_OPND(-1); \ - lval = FETCH_OPND(-2); \ - /* Optimize for two int-tagged operands (typical loop control). */ \ - if ((lval & rval) & JSVAL_INT) { \ - ltmp = lval ^ JSVAL_VOID; \ - rtmp = rval ^ JSVAL_VOID; \ - if (ltmp && rtmp) { \ - cond = JSVAL_TO_INT(lval) OP JSVAL_TO_INT(rval); \ - } else { \ - d = ltmp ? JSVAL_TO_INT(lval) : *rt->jsNaN; \ - d2 = rtmp ? JSVAL_TO_INT(rval) : *rt->jsNaN; \ - cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \ - } \ - } else { \ - VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_NUMBER, &lval); \ - sp[-2] = lval; \ - VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_NUMBER, &rval); \ - if (JSVAL_IS_STRING(lval) && JSVAL_IS_STRING(rval)) { \ - str = JSVAL_TO_STRING(lval); \ - str2 = JSVAL_TO_STRING(rval); \ - cond = js_CompareStrings(str, str2) OP 0; \ - } else { \ - VALUE_TO_NUMBER(cx, lval, d); \ - VALUE_TO_NUMBER(cx, rval, d2); \ - cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \ - } \ - } \ - sp--; \ - STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ - JS_END_MACRO - -/* - * NB: These macros can't use JS_BEGIN_MACRO/JS_END_MACRO around their bodies - * because they begin if/else chains, so callers must not put semicolons after - * the call expressions! - */ -#if JS_HAS_XML_SUPPORT -#define XML_EQUALITY_OP(OP) \ - if ((ltmp == JSVAL_OBJECT && \ - (obj2 = JSVAL_TO_OBJECT(lval)) && \ - OBJECT_IS_XML(cx, obj2)) || \ - (rtmp == JSVAL_OBJECT && \ - (obj2 = JSVAL_TO_OBJECT(rval)) && \ - OBJECT_IS_XML(cx, obj2))) { \ - JSXMLObjectOps *ops; \ - \ - ops = (JSXMLObjectOps *) obj2->map->ops; \ - if (obj2 == JSVAL_TO_OBJECT(rval)) \ - rval = lval; \ - SAVE_SP_AND_PC(fp); \ - ok = ops->equality(cx, obj2, rval, &cond); \ - if (!ok) \ - goto out; \ - cond = cond OP JS_TRUE; \ - } else - -#define EXTENDED_EQUALITY_OP(OP) \ - if (ltmp == JSVAL_OBJECT && \ - (obj2 = JSVAL_TO_OBJECT(lval)) && \ - ((clasp = OBJ_GET_CLASS(cx, obj2))->flags & JSCLASS_IS_EXTENDED)) { \ - JSExtendedClass *xclasp; \ - \ - xclasp = (JSExtendedClass *) clasp; \ - SAVE_SP_AND_PC(fp); \ - ok = xclasp->equality(cx, obj2, rval, &cond); \ - if (!ok) \ - goto out; \ - cond = cond OP JS_TRUE; \ - } else -#else -#define XML_EQUALITY_OP(OP) /* nothing */ -#define EXTENDED_EQUALITY_OP(OP) /* nothing */ -#endif - -#define EQUALITY_OP(OP, IFNAN) \ - JS_BEGIN_MACRO \ - rval = FETCH_OPND(-1); \ - lval = FETCH_OPND(-2); \ - ltmp = JSVAL_TAG(lval); \ - rtmp = JSVAL_TAG(rval); \ - XML_EQUALITY_OP(OP) \ - if (ltmp == rtmp) { \ - if (ltmp == JSVAL_STRING) { \ - str = JSVAL_TO_STRING(lval); \ - str2 = JSVAL_TO_STRING(rval); \ - cond = js_EqualStrings(str, str2) OP JS_TRUE; \ - } else if (ltmp == JSVAL_DOUBLE) { \ - d = *JSVAL_TO_DOUBLE(lval); \ - d2 = *JSVAL_TO_DOUBLE(rval); \ - cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \ - } else { \ - EXTENDED_EQUALITY_OP(OP) \ - /* Handle all undefined (=>NaN) and int combinations. */ \ - cond = lval OP rval; \ - } \ - } else { \ - if (JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)) { \ - cond = (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) OP 1; \ - } else if (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) { \ - cond = 1 OP 0; \ - } else { \ - if (ltmp == JSVAL_OBJECT) { \ - VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &sp[-2]); \ - lval = sp[-2]; \ - ltmp = JSVAL_TAG(lval); \ - } else if (rtmp == JSVAL_OBJECT) { \ - VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &sp[-1]); \ - rval = sp[-1]; \ - rtmp = JSVAL_TAG(rval); \ - } \ - if (ltmp == JSVAL_STRING && rtmp == JSVAL_STRING) { \ - str = JSVAL_TO_STRING(lval); \ - str2 = JSVAL_TO_STRING(rval); \ - cond = js_EqualStrings(str, str2) OP JS_TRUE; \ - } else { \ - VALUE_TO_NUMBER(cx, lval, d); \ - VALUE_TO_NUMBER(cx, rval, d2); \ - cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \ - } \ - } \ - } \ - sp--; \ - STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ - JS_END_MACRO - - BEGIN_CASE(JSOP_EQ) - EQUALITY_OP(==, JS_FALSE); - END_CASE(JSOP_EQ) - - BEGIN_CASE(JSOP_NE) - EQUALITY_OP(!=, JS_TRUE); - END_CASE(JSOP_NE) - -#define NEW_EQUALITY_OP(OP) \ - JS_BEGIN_MACRO \ - rval = FETCH_OPND(-1); \ - lval = FETCH_OPND(-2); \ - cond = js_StrictlyEqual(lval, rval) OP JS_TRUE; \ - sp--; \ - STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ - JS_END_MACRO - - BEGIN_CASE(JSOP_NEW_EQ) - NEW_EQUALITY_OP(==); - END_CASE(JSOP_NEW_EQ) - - BEGIN_CASE(JSOP_NEW_NE) - NEW_EQUALITY_OP(!=); - END_CASE(JSOP_NEW_NE) - - BEGIN_CASE(JSOP_CASE) - pc2 = (jsbytecode *) sp[-2-depth]; - NEW_EQUALITY_OP(==); - (void) POP(); - if (cond) { - len = GET_JUMP_OFFSET(pc); - CHECK_BRANCH(len); - DO_NEXT_OP(len); - } - sp[-depth] = (jsval)pc2; - PUSH(lval); - END_CASE(JSOP_CASE) - - BEGIN_CASE(JSOP_CASEX) - pc2 = (jsbytecode *) sp[-2-depth]; - NEW_EQUALITY_OP(==); - (void) POP(); - if (cond) { - len = GET_JUMPX_OFFSET(pc); - CHECK_BRANCH(len); - DO_NEXT_OP(len); - } - sp[-depth] = (jsval)pc2; - PUSH(lval); - END_CASE(JSOP_CASEX) - - BEGIN_CASE(JSOP_LT) - RELATIONAL_OP(<); - END_CASE(JSOP_LT) - - BEGIN_CASE(JSOP_LE) - RELATIONAL_OP(<=); - END_CASE(JSOP_LE) - - BEGIN_CASE(JSOP_GT) - RELATIONAL_OP(>); - END_CASE(JSOP_GT) - - BEGIN_CASE(JSOP_GE) - RELATIONAL_OP(>=); - END_CASE(JSOP_GE) - -#undef EQUALITY_OP -#undef RELATIONAL_OP - - BEGIN_CASE(JSOP_LSH) - SIGNED_SHIFT_OP(<<); - END_CASE(JSOP_LSH) - - BEGIN_CASE(JSOP_RSH) - SIGNED_SHIFT_OP(>>); - END_CASE(JSOP_RSH) - - BEGIN_CASE(JSOP_URSH) - { - uint32 u; - - FETCH_INT(cx, -1, j); - FETCH_UINT(cx, -2, u); - u >>= j & 31; - sp--; - STORE_UINT(cx, -1, u); - } - END_CASE(JSOP_URSH) - -#undef INTEGER_OP -#undef BITWISE_OP -#undef SIGNED_SHIFT_OP - - BEGIN_CASE(JSOP_ADD) - rval = FETCH_OPND(-1); - lval = FETCH_OPND(-2); -#if JS_HAS_XML_SUPPORT - if (!JSVAL_IS_PRIMITIVE(lval) && - (obj2 = JSVAL_TO_OBJECT(lval), OBJECT_IS_XML(cx, obj2)) && - VALUE_IS_XML(cx, rval)) { - JSXMLObjectOps *ops; - - ops = (JSXMLObjectOps *) obj2->map->ops; - SAVE_SP_AND_PC(fp); - ok = ops->concatenate(cx, obj2, rval, &rval); - if (!ok) - goto out; - sp--; - STORE_OPND(-1, rval); - } else -#endif - { - VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &sp[-2]); - lval = sp[-2]; - VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &sp[-1]); - rval = sp[-1]; - if ((cond = JSVAL_IS_STRING(lval)) || JSVAL_IS_STRING(rval)) { - SAVE_SP_AND_PC(fp); - if (cond) { - str = JSVAL_TO_STRING(lval); - ok = (str2 = js_ValueToString(cx, rval)) != NULL; - if (!ok) - goto out; - sp[-1] = STRING_TO_JSVAL(str2); - } else { - str2 = JSVAL_TO_STRING(rval); - ok = (str = js_ValueToString(cx, lval)) != NULL; - if (!ok) - goto out; - sp[-2] = STRING_TO_JSVAL(str); - } - str = js_ConcatStrings(cx, str, str2); - if (!str) { - ok = JS_FALSE; - goto out; - } - sp--; - STORE_OPND(-1, STRING_TO_JSVAL(str)); - } else { - VALUE_TO_NUMBER(cx, lval, d); - VALUE_TO_NUMBER(cx, rval, d2); - d += d2; - sp--; - STORE_NUMBER(cx, -1, d); - } - } - END_CASE(JSOP_ADD) - -#define BINARY_OP(OP) \ - JS_BEGIN_MACRO \ - FETCH_NUMBER(cx, -1, d2); \ - FETCH_NUMBER(cx, -2, d); \ - d = d OP d2; \ - sp--; \ - STORE_NUMBER(cx, -1, d); \ - JS_END_MACRO - - BEGIN_CASE(JSOP_SUB) - BINARY_OP(-); - END_CASE(JSOP_SUB) - - BEGIN_CASE(JSOP_MUL) - BINARY_OP(*); - END_CASE(JSOP_MUL) - - BEGIN_CASE(JSOP_DIV) - FETCH_NUMBER(cx, -1, d2); - FETCH_NUMBER(cx, -2, d); - sp--; - if (d2 == 0) { -#ifdef XP_WIN - /* XXX MSVC miscompiles such that (NaN == 0) */ - if (JSDOUBLE_IS_NaN(d2)) - rval = DOUBLE_TO_JSVAL(rt->jsNaN); - else -#endif - if (d == 0 || JSDOUBLE_IS_NaN(d)) - rval = DOUBLE_TO_JSVAL(rt->jsNaN); - else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31) - rval = DOUBLE_TO_JSVAL(rt->jsNegativeInfinity); - else - rval = DOUBLE_TO_JSVAL(rt->jsPositiveInfinity); - STORE_OPND(-1, rval); - } else { - d /= d2; - STORE_NUMBER(cx, -1, d); - } - END_CASE(JSOP_DIV) - - BEGIN_CASE(JSOP_MOD) - FETCH_NUMBER(cx, -1, d2); - FETCH_NUMBER(cx, -2, d); - sp--; - if (d2 == 0) { - STORE_OPND(-1, DOUBLE_TO_JSVAL(rt->jsNaN)); - } else { -#ifdef XP_WIN - /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */ - if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2))) -#endif - d = fmod(d, d2); - STORE_NUMBER(cx, -1, d); - } - END_CASE(JSOP_MOD) - - BEGIN_CASE(JSOP_NOT) - POP_BOOLEAN(cx, rval, cond); - PUSH_OPND(BOOLEAN_TO_JSVAL(!cond)); - END_CASE(JSOP_NOT) - - BEGIN_CASE(JSOP_BITNOT) - FETCH_INT(cx, -1, i); - i = ~i; - STORE_INT(cx, -1, i); - END_CASE(JSOP_BITNOT) - - BEGIN_CASE(JSOP_NEG) - /* - * Optimize the case of an int-tagged operand by noting that - * INT_FITS_IN_JSVAL(i) => INT_FITS_IN_JSVAL(-i) unless i is 0 - * when -i is the negative zero which is jsdouble. - */ - rval = FETCH_OPND(-1); - if (JSVAL_IS_INT(rval) && (i = JSVAL_TO_INT(rval)) != 0) { - i = -i; - JS_ASSERT(INT_FITS_IN_JSVAL(i)); - rval = INT_TO_JSVAL(i); - } else { - if (JSVAL_IS_DOUBLE(rval)) { - d = *JSVAL_TO_DOUBLE(rval); - } else { - SAVE_SP_AND_PC(fp); - ok = js_ValueToNumber(cx, rval, &d); - if (!ok) - goto out; - } -#ifdef HPUX - /* - * Negation of a zero doesn't produce a negative - * zero on HPUX. Perform the operation by bit - * twiddling. - */ - JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT; -#else - d = -d; -#endif - ok = js_NewNumberValue(cx, d, &rval); - if (!ok) - goto out; - } - STORE_OPND(-1, rval); - END_CASE(JSOP_NEG) - - BEGIN_CASE(JSOP_POS) - rval = FETCH_OPND(-1); - if (!JSVAL_IS_NUMBER(rval)) { - SAVE_SP_AND_PC(fp); - ok = js_ValueToNumber(cx, rval, &d); - if (!ok) - goto out; - ok = js_NewNumberValue(cx, d, &rval); - if (!ok) - goto out; - sp[-1] = rval; - } - sp[-1-depth] = (jsval)pc; - END_CASE(JSOP_POS) - - BEGIN_CASE(JSOP_NEW) - /* Get immediate argc and find the constructor function. */ - argc = GET_ARGC(pc); - - do_new: - SAVE_SP_AND_PC(fp); - vp = sp - (2 + argc); - JS_ASSERT(vp >= fp->spbase); - - ok = js_InvokeConstructor(cx, vp, argc); - if (!ok) - goto out; - RESTORE_SP(fp); - LOAD_BRANCH_CALLBACK(cx); - LOAD_INTERRUPT_HANDLER(rt); - obj = JSVAL_TO_OBJECT(*vp); - len = js_CodeSpec[op].length; - DO_NEXT_OP(len); - - BEGIN_CASE(JSOP_DELNAME) - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - - SAVE_SP_AND_PC(fp); - ok = js_FindProperty(cx, id, &obj, &obj2, &prop); - if (!ok) - goto out; - - /* ECMA says to return true if name is undefined or inherited. */ - rval = JSVAL_TRUE; - if (prop) { - OBJ_DROP_PROPERTY(cx, obj2, prop); - ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval); - if (!ok) - goto out; - } - PUSH_OPND(rval); - END_CASE(JSOP_DELNAME) - - BEGIN_CASE(JSOP_DELPROP) - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - PROPERTY_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval)); - STORE_OPND(-1, rval); - END_CASE(JSOP_DELPROP) - - BEGIN_CASE(JSOP_DELELEM) - ELEMENT_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval)); - sp--; - STORE_OPND(-1, rval); - END_CASE(JSOP_DELELEM) - - BEGIN_CASE(JSOP_TYPEOFEXPR) - BEGIN_CASE(JSOP_TYPEOF) - rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - type = JS_TypeOfValue(cx, rval); - atom = rt->atomState.typeAtoms[type]; - STORE_OPND(-1, ATOM_KEY(atom)); - END_CASE(JSOP_TYPEOF) - - BEGIN_CASE(JSOP_VOID) - (void) POP_OPND(); - PUSH_OPND(JSVAL_VOID); - END_CASE(JSOP_VOID) - - BEGIN_CASE(JSOP_INCNAME) - BEGIN_CASE(JSOP_DECNAME) - BEGIN_CASE(JSOP_NAMEINC) - BEGIN_CASE(JSOP_NAMEDEC) - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - - SAVE_SP_AND_PC(fp); - ok = js_FindProperty(cx, id, &obj, &obj2, &prop); - if (!ok) - goto out; - if (!prop) - goto atom_not_defined; - - OBJ_DROP_PROPERTY(cx, obj2, prop); - lval = OBJECT_TO_JSVAL(obj); - i = 0; - goto do_incop; - - BEGIN_CASE(JSOP_INCPROP) - BEGIN_CASE(JSOP_DECPROP) - BEGIN_CASE(JSOP_PROPINC) - BEGIN_CASE(JSOP_PROPDEC) - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - lval = FETCH_OPND(-1); - i = -1; - goto do_incop; - - BEGIN_CASE(JSOP_INCELEM) - BEGIN_CASE(JSOP_DECELEM) - BEGIN_CASE(JSOP_ELEMINC) - BEGIN_CASE(JSOP_ELEMDEC) - FETCH_ELEMENT_ID(-1, id); - lval = FETCH_OPND(-2); - i = -2; - - do_incop: - { - const JSCodeSpec *cs; - - VALUE_TO_OBJECT(cx, lval, obj); - if (i < 0) - STORE_OPND(i, OBJECT_TO_JSVAL(obj)); - CHECK_ELEMENT_ID(obj, id); - - /* The operand must contain a number. */ - SAVE_SP_AND_PC(fp); - CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); - if (!ok) - goto out; - - /* Preload for use in the if/else immediately below. */ - cs = &js_CodeSpec[op]; - - /* The expression result goes in rtmp, the updated value in rval. */ - if (JSVAL_IS_INT(rval) && - rval != INT_TO_JSVAL(JSVAL_INT_MIN) && - rval != INT_TO_JSVAL(JSVAL_INT_MAX)) { - if (cs->format & JOF_POST) { - rtmp = rval; - (cs->format & JOF_INC) ? (rval += 2) : (rval -= 2); - } else { - (cs->format & JOF_INC) ? (rval += 2) : (rval -= 2); - rtmp = rval; - } - } else { - -/* - * Initially, rval contains the value to increment or decrement, which is not - * yet converted. As above, the expression result goes in rtmp, the updated - * value goes in rval. Our caller must set vp to point at a GC-rooted jsval - * in which we home rtmp, to protect it from GC in case the unconverted rval - * is not a number. - */ -#define NONINT_INCREMENT_OP_MIDDLE() \ - JS_BEGIN_MACRO \ - VALUE_TO_NUMBER(cx, rval, d); \ - if (cs->format & JOF_POST) { \ - rtmp = rval; \ - if (!JSVAL_IS_NUMBER(rtmp)) { \ - ok = js_NewNumberValue(cx, d, &rtmp); \ - if (!ok) \ - goto out; \ - } \ - *vp = rtmp; \ - (cs->format & JOF_INC) ? d++ : d--; \ - ok = js_NewNumberValue(cx, d, &rval); \ - } else { \ - (cs->format & JOF_INC) ? ++d : --d; \ - ok = js_NewNumberValue(cx, d, &rval); \ - rtmp = rval; \ - } \ - if (!ok) \ - goto out; \ - JS_END_MACRO - - if (cs->format & JOF_POST) { - /* - * We must push early to protect the postfix increment - * or decrement result, if converted to a jsdouble from - * a non-number value, from GC nesting in the setter. - */ - vp = sp; - PUSH(JSVAL_VOID); - SAVE_SP(fp); - --i; - } -#ifdef __GNUC__ - else vp = NULL; /* suppress bogus gcc warnings */ -#endif - - NONINT_INCREMENT_OP_MIDDLE(); - } - - fp->flags |= JSFRAME_ASSIGNING; - CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); - fp->flags &= ~JSFRAME_ASSIGNING; - if (!ok) - goto out; - sp += i; - PUSH_OPND(rtmp); - len = js_CodeSpec[op].length; - DO_NEXT_OP(len); - } - -/* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */ -#define FAST_INCREMENT_OP(SLOT,COUNT,BASE,PRE,OPEQ,MINMAX) \ - slot = SLOT; \ - JS_ASSERT(slot < fp->fun->COUNT); \ - vp = fp->BASE + slot; \ - rval = *vp; \ - if (!JSVAL_IS_INT(rval) || rval == INT_TO_JSVAL(JSVAL_INT_##MINMAX)) \ - goto do_nonint_fast_incop; \ - PRE = rval; \ - rval OPEQ 2; \ - *vp = rval; \ - PUSH_OPND(PRE); \ - goto end_nonint_fast_incop - - BEGIN_CASE(JSOP_INCARG) - FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, +=, MAX); - BEGIN_CASE(JSOP_DECARG) - FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, -=, MIN); - BEGIN_CASE(JSOP_ARGINC) - FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, +=, MAX); - BEGIN_CASE(JSOP_ARGDEC) - FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, -=, MIN); - - BEGIN_CASE(JSOP_INCVAR) - FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rval, +=, MAX); - BEGIN_CASE(JSOP_DECVAR) - FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rval, -=, MIN); - BEGIN_CASE(JSOP_VARINC) - FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rtmp, +=, MAX); - BEGIN_CASE(JSOP_VARDEC) - FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rtmp, -=, MIN); - - end_nonint_fast_incop: - len = JSOP_INCARG_LENGTH; /* all fast incops are same length */ - DO_NEXT_OP(len); - -#undef FAST_INCREMENT_OP - - do_nonint_fast_incop: - { - const JSCodeSpec *cs = &js_CodeSpec[op]; - - NONINT_INCREMENT_OP_MIDDLE(); - *vp = rval; - PUSH_OPND(rtmp); - len = cs->length; - DO_NEXT_OP(len); - } - -/* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */ -#define FAST_GLOBAL_INCREMENT_OP(SLOWOP,PRE,OPEQ,MINMAX) \ - slot = GET_VARNO(pc); \ - JS_ASSERT(slot < fp->nvars); \ - lval = fp->vars[slot]; \ - if (JSVAL_IS_NULL(lval)) { \ - op = SLOWOP; \ - DO_OP(); \ - } \ - slot = JSVAL_TO_INT(lval); \ - obj = fp->varobj; \ - rval = OBJ_GET_SLOT(cx, obj, slot); \ - if (!JSVAL_IS_INT(rval) || rval == INT_TO_JSVAL(JSVAL_INT_##MINMAX)) \ - goto do_nonint_fast_global_incop; \ - PRE = rval; \ - rval OPEQ 2; \ - OBJ_SET_SLOT(cx, obj, slot, rval); \ - PUSH_OPND(PRE); \ - goto end_nonint_fast_global_incop - - BEGIN_CASE(JSOP_INCGVAR) - FAST_GLOBAL_INCREMENT_OP(JSOP_INCNAME, rval, +=, MAX); - BEGIN_CASE(JSOP_DECGVAR) - FAST_GLOBAL_INCREMENT_OP(JSOP_DECNAME, rval, -=, MIN); - BEGIN_CASE(JSOP_GVARINC) - FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEINC, rtmp, +=, MAX); - BEGIN_CASE(JSOP_GVARDEC) - FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEDEC, rtmp, -=, MIN); - - end_nonint_fast_global_incop: - len = JSOP_INCGVAR_LENGTH; /* all gvar incops are same length */ - JS_ASSERT(len == js_CodeSpec[op].length); - DO_NEXT_OP(len); - -#undef FAST_GLOBAL_INCREMENT_OP - - do_nonint_fast_global_incop: - { - const JSCodeSpec *cs = &js_CodeSpec[op]; - - vp = sp++; - SAVE_SP(fp); - NONINT_INCREMENT_OP_MIDDLE(); - OBJ_SET_SLOT(cx, obj, slot, rval); - STORE_OPND(-1, rtmp); - len = cs->length; - DO_NEXT_OP(len); - } - - BEGIN_CASE(JSOP_GETPROP) - BEGIN_CASE(JSOP_GETXPROP) - /* Get an immediate atom naming the property. */ - atom = GET_ATOM(cx, script, pc); - lval = FETCH_OPND(-1); - if (JSVAL_IS_STRING(lval) && - atom == cx->runtime->atomState.lengthAtom) { - rval = INT_TO_JSVAL(JSSTRING_LENGTH(JSVAL_TO_STRING(lval))); - obj = NULL; - } else { - id = ATOM_TO_JSID(atom); - VALUE_TO_OBJECT(cx, lval, obj); - STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - SAVE_SP_AND_PC(fp); - CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); - if (!ok) - goto out; - } - STORE_OPND(-1, rval); - END_CASE(JSOP_GETPROP) - - BEGIN_CASE(JSOP_SETPROP) - /* Pop the right-hand side into rval for OBJ_SET_PROPERTY. */ - rval = FETCH_OPND(-1); - - /* Get an immediate atom naming the property. */ - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - PROPERTY_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval))); - sp--; - STORE_OPND(-1, rval); - obj = NULL; - END_CASE(JSOP_SETPROP) - - BEGIN_CASE(JSOP_GETELEM) - BEGIN_CASE(JSOP_GETXELEM) - ELEMENT_OP(-1, CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval))); - sp--; - STORE_OPND(-1, rval); - END_CASE(JSOP_GETELEM) - - BEGIN_CASE(JSOP_SETELEM) - rval = FETCH_OPND(-1); - ELEMENT_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval))); - sp -= 2; - STORE_OPND(-1, rval); - obj = NULL; - END_CASE(JSOP_SETELEM) - - BEGIN_CASE(JSOP_ENUMELEM) - /* Funky: the value to set is under the [obj, id] pair. */ - FETCH_ELEMENT_ID(-1, id); - FETCH_OBJECT(cx, -2, lval, obj); - CHECK_ELEMENT_ID(obj, id); - rval = FETCH_OPND(-3); - SAVE_SP_AND_PC(fp); - ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); - if (!ok) - goto out; - sp -= 3; - END_CASE(JSOP_ENUMELEM) - -/* - * LAZY_ARGS_THISP allows the JSOP_ARGSUB bytecode to defer creation of the - * arguments object until it is truly needed. JSOP_ARGSUB optimizes away - * arguments objects when the only uses of the 'arguments' parameter are to - * fetch individual actual parameters. But if such a use were then invoked, - * e.g., arguments[i](), the 'this' parameter would and must bind to the - * caller's arguments object. So JSOP_ARGSUB sets obj to LAZY_ARGS_THISP. - */ -#define LAZY_ARGS_THISP ((JSObject *) JSVAL_VOID) - - BEGIN_CASE(JSOP_PUSHOBJ) - if (obj == LAZY_ARGS_THISP && !(obj = js_GetArgsObject(cx, fp))) { - ok = JS_FALSE; - goto out; - } - PUSH_OPND(OBJECT_TO_JSVAL(obj)); - END_CASE(JSOP_PUSHOBJ) - - BEGIN_CASE(JSOP_CALL) - BEGIN_CASE(JSOP_EVAL) - argc = GET_ARGC(pc); - vp = sp - (argc + 2); - lval = *vp; - SAVE_SP_AND_PC(fp); - if (VALUE_IS_FUNCTION(cx, lval) && - (obj = JSVAL_TO_OBJECT(lval), - fun = (JSFunction *) JS_GetPrivate(cx, obj), - FUN_INTERPRETED(fun))) - /* inline_call: */ - { - uintN nframeslots, nvars, nslots, missing; - JSArena *a; - jsuword avail, nbytes; - JSBool overflow; - void *newmark; - jsval *rvp; - JSInlineFrame *newifp; - JSInterpreterHook hook; - - /* Restrict recursion of lightweight functions. */ - if (inlineCallCount == MAX_INLINE_CALL_COUNT) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_OVER_RECURSED); - ok = JS_FALSE; - goto out; - } - - /* Compute the total number of stack slots needed for fun. */ - nframeslots = JS_HOWMANY(sizeof(JSInlineFrame), sizeof(jsval)); - nvars = fun->u.i.nvars; - script = fun->u.i.script; - depth = (jsint) script->depth; - nslots = nframeslots + nvars + 2 * depth; - - /* Allocate missing expected args adjacent to actual args. */ - missing = (fun->nargs > argc) ? fun->nargs - argc : 0; - a = cx->stackPool.current; - avail = a->avail; - newmark = (void *) avail; - if (missing) { - newsp = sp + missing; - overflow = (jsuword) newsp > a->limit; - if (overflow) - nslots += 2 + argc + missing; - else if ((jsuword) newsp > avail) - avail = a->avail = (jsuword) newsp; - } -#ifdef __GNUC__ - else overflow = JS_FALSE; /* suppress bogus gcc warnings */ -#endif - - /* Allocate the inline frame with its vars and operand slots. */ - newsp = (jsval *) avail; - nbytes = nslots * sizeof(jsval); - avail += nbytes; - if (avail <= a->limit) { - a->avail = avail; - } else { - JS_ARENA_ALLOCATE_CAST(newsp, jsval *, &cx->stackPool, - nbytes); - if (!newsp) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_STACK_OVERFLOW, - (fp && fp->fun) - ? JS_GetFunctionName(fp->fun) - : "script"); - goto bad_inline_call; - } - } - - /* Move args if missing overflow arena a, push missing args. */ - rvp = vp; - if (missing) { - if (overflow) { - memcpy(newsp, vp, (2 + argc) * sizeof(jsval)); - vp = newsp; - sp = vp + 2 + argc; - newsp = sp + missing; - } - do { - PUSH(JSVAL_VOID); - } while (--missing != 0); - } - - /* Claim space for the stack frame and initialize it. */ - newifp = (JSInlineFrame *) newsp; - newsp += nframeslots; - newifp->frame.callobj = NULL; - newifp->frame.argsobj = NULL; - newifp->frame.varobj = NULL; - newifp->frame.script = script; - newifp->frame.fun = fun; - newifp->frame.argc = argc; - newifp->frame.argv = vp + 2; - newifp->frame.rval = JSVAL_VOID; - newifp->frame.nvars = nvars; - newifp->frame.vars = newsp; - newifp->frame.down = fp; - newifp->frame.annotation = NULL; - newifp->frame.scopeChain = parent = OBJ_GET_PARENT(cx, obj); - newifp->frame.sharpDepth = 0; - newifp->frame.sharpArray = NULL; - newifp->frame.flags = 0; - newifp->frame.dormantNext = NULL; - newifp->frame.xmlNamespace = NULL; - newifp->frame.blockChain = NULL; - newifp->rvp = rvp; - newifp->mark = newmark; - - /* Compute the 'this' parameter now that argv is set. */ - if (!JSVAL_IS_OBJECT(vp[1])) { - PRIMITIVE_TO_OBJECT(cx, vp[1], obj2); - if (!obj2) - goto bad_inline_call; - vp[1] = OBJECT_TO_JSVAL(obj2); - } - newifp->frame.thisp = - js_ComputeThis(cx, - JSFUN_BOUND_METHOD_TEST(fun->flags) - ? parent - : JSVAL_TO_OBJECT(vp[1]), - newifp->frame.argv); - if (!newifp->frame.thisp) - goto bad_inline_call; -#ifdef DUMP_CALL_TABLE - LogCall(cx, *vp, argc, vp + 2); -#endif - - /* Push void to initialize local variables. */ - sp = newsp; - while (nvars--) - PUSH(JSVAL_VOID); - sp += depth; - newifp->frame.spbase = sp; - SAVE_SP(&newifp->frame); - - /* Call the debugger hook if present. */ - hook = rt->callHook; - if (hook) { - newifp->frame.pc = NULL; - newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0, - rt->callHookData); - LOAD_INTERRUPT_HANDLER(rt); - } else { - newifp->hookData = NULL; - } - - /* Scope with a call object parented by the callee's parent. */ - if (JSFUN_HEAVYWEIGHT_TEST(fun->flags) && - !js_GetCallObject(cx, &newifp->frame, parent)) { - goto bad_inline_call; - } - - /* Switch to new version if currentVersion wasn't overridden. */ - newifp->callerVersion = cx->version; - if (JS_LIKELY(cx->version == currentVersion)) { - currentVersion = script->version; - if (currentVersion != cx->version) - js_SetVersion(cx, currentVersion); - } - - /* Push the frame and set interpreter registers. */ - cx->fp = fp = &newifp->frame; - pc = script->code; -#ifndef JS_THREADED_INTERP - endpc = pc + script->length; -#endif - obj = NULL; - inlineCallCount++; - JS_RUNTIME_METER(rt, inlineCalls); - - /* Load first opcode and dispatch it (safe since JSOP_STOP). */ - op = *pc; - DO_OP(); - - bad_inline_call: - RESTORE_SP(fp); - JS_ASSERT(fp->pc == pc); - script = fp->script; - depth = (jsint) script->depth; - js_FreeRawStack(cx, newmark); - ok = JS_FALSE; - goto out; - } - - ok = js_Invoke(cx, argc, 0); - RESTORE_SP(fp); - LOAD_BRANCH_CALLBACK(cx); - LOAD_INTERRUPT_HANDLER(rt); - if (!ok) - goto out; - JS_RUNTIME_METER(rt, nonInlineCalls); -#if JS_HAS_LVALUE_RETURN - if (cx->rval2set) { - /* - * Use the stack depth we didn't claim in our budget, but that - * we know is there on account of [fun, this] already having - * been pushed, at a minimum (if no args). Those two slots - * have been popped and [rval] has been pushed, which leaves - * one more slot for rval2 before we might overflow. - * - * NB: rval2 must be the property identifier, and rval the - * object from which to get the property. The pair form an - * ECMA "reference type", which can be used on the right- or - * left-hand side of assignment ops. Note well: only native - * methods can return reference types. See JSOP_SETCALL just - * below for the left-hand-side case. - */ - PUSH_OPND(cx->rval2); - ELEMENT_OP(-1, ok = OBJ_GET_PROPERTY(cx, obj, id, &rval)); - - sp--; - STORE_OPND(-1, rval); - cx->rval2set = JS_FALSE; - } -#endif /* JS_HAS_LVALUE_RETURN */ - obj = NULL; - END_CASE(JSOP_CALL) - -#if JS_HAS_LVALUE_RETURN - BEGIN_CASE(JSOP_SETCALL) - argc = GET_ARGC(pc); - SAVE_SP_AND_PC(fp); - ok = js_Invoke(cx, argc, 0); - RESTORE_SP(fp); - LOAD_BRANCH_CALLBACK(cx); - LOAD_INTERRUPT_HANDLER(rt); - if (!ok) - goto out; - if (!cx->rval2set) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_LEFTSIDE_OF_ASS); - ok = JS_FALSE; - goto out; - } - PUSH_OPND(cx->rval2); - cx->rval2set = JS_FALSE; - obj = NULL; - END_CASE(JSOP_SETCALL) -#endif - - BEGIN_CASE(JSOP_NAME) - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - - SAVE_SP_AND_PC(fp); - ok = js_FindProperty(cx, id, &obj, &obj2, &prop); - if (!ok) - goto out; - if (!prop) { - /* Kludge to allow (typeof foo == "undefined") tests. */ - len = JSOP_NAME_LENGTH; - endpc = script->code + script->length; - for (pc2 = pc + len; pc2 < endpc; pc2++) { - op2 = (JSOp)*pc2; - if (op2 == JSOP_TYPEOF) { - PUSH_OPND(JSVAL_VOID); - DO_NEXT_OP(len); - } - if (op2 != JSOP_GROUP) - break; - } - goto atom_not_defined; - } - - /* Take the slow path if prop was not found in a native object. */ - if (!OBJ_IS_NATIVE(obj) || !OBJ_IS_NATIVE(obj2)) { - OBJ_DROP_PROPERTY(cx, obj2, prop); - ok = OBJ_GET_PROPERTY(cx, obj, id, &rval); - if (!ok) - goto out; - } else { - sprop = (JSScopeProperty *)prop; - NATIVE_GET(cx, obj, obj2, sprop, &rval); - OBJ_DROP_PROPERTY(cx, obj2, prop); - } - PUSH_OPND(rval); - END_CASE(JSOP_NAME) - - BEGIN_CASE(JSOP_UINT16) - i = (jsint) GET_ATOM_INDEX(pc); - rval = INT_TO_JSVAL(i); - PUSH_OPND(rval); - obj = NULL; - END_CASE(JSOP_UINT16) - - BEGIN_CASE(JSOP_UINT24) - i = (jsint) GET_LITERAL_INDEX(pc); - rval = INT_TO_JSVAL(i); - PUSH_OPND(rval); - END_CASE(JSOP_UINT24) - - BEGIN_CASE(JSOP_LITERAL) - atomIndex = GET_LITERAL_INDEX(pc); - atom = js_GetAtom(cx, &script->atomMap, atomIndex); - PUSH_OPND(ATOM_KEY(atom)); - obj = NULL; - END_CASE(JSOP_LITERAL) - - BEGIN_CASE(JSOP_FINDNAME) - atomIndex = GET_LITERAL_INDEX(pc); - atom = js_GetAtom(cx, &script->atomMap, atomIndex); - SAVE_SP_AND_PC(fp); - obj = js_FindIdentifierBase(cx, ATOM_TO_JSID(atom)); - if (!obj) { - ok = JS_FALSE; - goto out; - } - PUSH_OPND(OBJECT_TO_JSVAL(obj)); - PUSH_OPND(ATOM_KEY(atom)); - END_CASE(JSOP_FINDNAME) - - BEGIN_CASE(JSOP_LITOPX) - /* - * Load atomIndex, which is used by code at each do_JSOP_* label. - * - * Also set pc2 to point at the bytecode extended by this prefix - * to have a leading 24 bit atomIndex, instead of the unextended - * 16-bit atomIndex that normally comes after op. This enables - * JOF_INDEXCONST format ops (which have multiple immediates) to - * collect their other immediate via GET_VARNO(pc2) or similar. - * - * Finally, load op and, if threading, adjust pc so that it will - * be advanced properly at the end of op's case by DO_NEXT_OP. - */ - atomIndex = GET_LITERAL_INDEX(pc); - pc2 = pc + 1 + LITERAL_INDEX_LEN; - op = *pc2; - pc += JSOP_LITOPX_LENGTH - (1 + ATOM_INDEX_LEN); -#ifndef JS_THREADED_INTERP - len = js_CodeSpec[op].length; -#endif - switch (op) { - case JSOP_ANONFUNOBJ: goto do_JSOP_ANONFUNOBJ; - case JSOP_BINDNAME: goto do_JSOP_BINDNAME; - case JSOP_CLOSURE: goto do_JSOP_CLOSURE; - case JSOP_DEFCONST: goto do_JSOP_DEFCONST; - case JSOP_DEFFUN: goto do_JSOP_DEFFUN; - case JSOP_DEFLOCALFUN: goto do_JSOP_DEFLOCALFUN; - case JSOP_DEFVAR: goto do_JSOP_DEFVAR; -#if JS_HAS_EXPORT_IMPORT - case JSOP_EXPORTNAME: goto do_JSOP_EXPORTNAME; -#endif -#if JS_HAS_XML_SUPPORT - case JSOP_GETMETHOD: goto do_JSOP_GETMETHOD; - case JSOP_SETMETHOD: goto do_JSOP_SETMETHOD; -#endif - case JSOP_NAMEDFUNOBJ: goto do_JSOP_NAMEDFUNOBJ; - case JSOP_NUMBER: goto do_JSOP_NUMBER; - case JSOP_OBJECT: goto do_JSOP_OBJECT; -#if JS_HAS_XML_SUPPORT - case JSOP_QNAMECONST: goto do_JSOP_QNAMECONST; - case JSOP_QNAMEPART: goto do_JSOP_QNAMEPART; -#endif - case JSOP_REGEXP: goto do_JSOP_REGEXP; - case JSOP_SETCONST: goto do_JSOP_SETCONST; - case JSOP_STRING: goto do_JSOP_STRING; -#if JS_HAS_XML_SUPPORT - case JSOP_XMLCDATA: goto do_JSOP_XMLCDATA; - case JSOP_XMLCOMMENT: goto do_JSOP_XMLCOMMENT; - case JSOP_XMLOBJECT: goto do_JSOP_XMLOBJECT; - case JSOP_XMLPI: goto do_JSOP_XMLPI; -#endif - case JSOP_ENTERBLOCK: goto do_JSOP_ENTERBLOCK; - default: JS_ASSERT(0); - } - /* NOTREACHED */ - - BEGIN_CASE(JSOP_NUMBER) - BEGIN_CASE(JSOP_STRING) - BEGIN_CASE(JSOP_OBJECT) - atomIndex = GET_ATOM_INDEX(pc); - - do_JSOP_NUMBER: - do_JSOP_STRING: - do_JSOP_OBJECT: - atom = js_GetAtom(cx, &script->atomMap, atomIndex); - PUSH_OPND(ATOM_KEY(atom)); - obj = NULL; - END_CASE(JSOP_NUMBER) - - BEGIN_LITOPX_CASE(JSOP_REGEXP, 0) - { - JSRegExp *re; - JSObject *funobj; - - /* - * Push a regexp object for the atom mapped by the bytecode at pc, - * cloning the literal's regexp object if necessary, to simulate in - * the pre-compile/execute-later case what ECMA specifies for the - * compile-and-go case: that scanning each regexp literal creates - * a single corresponding RegExp object. - * - * To support pre-compilation transparently, we must handle the - * case where a regexp object literal is used in a different global - * at execution time from the global with which it was scanned at - * compile time. We do this by re-wrapping the JSRegExp private - * data struct with a cloned object having the right prototype and - * parent, and having its own lastIndex property value storage. - * - * Unlike JSOP_DEFFUN and other prolog bytecodes that may clone - * literal objects, we don't want to pay a script prolog execution - * price for all regexp literals in a script (many may not be used - * by a particular execution of that script, depending on control - * flow), so we initialize lazily here. - * - * XXX This code is specific to regular expression objects. If we - * need a similar op for other kinds of object literals, we should - * push cloning down under JSObjectOps and reuse code here. - */ - JS_ASSERT(ATOM_IS_OBJECT(atom)); - obj = ATOM_TO_OBJECT(atom); - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass); - - re = (JSRegExp *) JS_GetPrivate(cx, obj); - slot = re->cloneIndex; - if (fp->fun) { - /* - * We're in function code, not global or eval code (in eval - * code, JSOP_REGEXP is never emitted). The code generator - * recorded in fp->fun->nregexps the number of re->cloneIndex - * slots that it reserved in the cloned funobj. - */ - funobj = JSVAL_TO_OBJECT(fp->argv[-2]); - slot += JSCLASS_RESERVED_SLOTS(&js_FunctionClass); - if (!JS_GetReservedSlot(cx, funobj, slot, &rval)) - return JS_FALSE; - if (JSVAL_IS_VOID(rval)) - rval = JSVAL_NULL; - } else { - /* - * We're in global code. The code generator already arranged - * via script->numGlobalVars to reserve a global variable slot - * at cloneIndex. All global variable slots are initialized - * to null, not void, for faster testing in JSOP_*GVAR cases. - */ - rval = fp->vars[slot]; -#ifdef __GNUC__ - funobj = NULL; /* suppress bogus gcc warnings */ -#endif - } - - if (JSVAL_IS_NULL(rval)) { - /* Compute the current global object in obj2. */ - obj2 = fp->scopeChain; - while ((parent = OBJ_GET_PARENT(cx, obj2)) != NULL) - obj2 = parent; - - /* - * We must home sp here, because either js_CloneRegExpObject - * or JS_SetReservedSlot could nest a last-ditch GC. We home - * pc as well, in case js_CloneRegExpObject has to lookup the - * "RegExp" class in the global object, which could entail a - * JSNewResolveOp call. - */ - SAVE_SP_AND_PC(fp); - - /* - * If obj's parent is not obj2, we must clone obj so that it - * has the right parent, and therefore, the right prototype. - * - * Yes, this means we assume that the correct RegExp.prototype - * to which regexp instances (including literals) delegate can - * be distinguished solely by the instance's parent, which was - * set to the parent of the RegExp constructor function object - * when the instance was created. In other words, - * - * (/x/.__parent__ == RegExp.__parent__) implies - * (/x/.__proto__ == RegExp.prototype) - * - * (unless you assign a different object to RegExp.prototype - * at runtime, in which case, ECMA doesn't specify operation, - * and you get what you deserve). - * - * This same coupling between instance parent and constructor - * parent turns up everywhere (see jsobj.c's FindClassObject, - * js_ConstructObject, and js_NewObject). It's fundamental to - * the design of the language when you consider multiple global - * objects and separate compilation and execution, even though - * it is not specified fully in ECMA. - */ - if (OBJ_GET_PARENT(cx, obj) != obj2) { - obj = js_CloneRegExpObject(cx, obj, obj2); - if (!obj) { - ok = JS_FALSE; - goto out; - } - } - rval = OBJECT_TO_JSVAL(obj); - - /* Store the regexp object value in its cloneIndex slot. */ - if (fp->fun) { - if (!JS_SetReservedSlot(cx, funobj, slot, rval)) - return JS_FALSE; - } else { - fp->vars[slot] = rval; - } - } - - PUSH_OPND(rval); - obj = NULL; - } - END_LITOPX_CASE(JSOP_REGEXP) - - BEGIN_CASE(JSOP_ZERO) - PUSH_OPND(JSVAL_ZERO); - obj = NULL; - END_CASE(JSOP_ZERO) - - BEGIN_CASE(JSOP_ONE) - PUSH_OPND(JSVAL_ONE); - obj = NULL; - END_CASE(JSOP_ONE) - - BEGIN_CASE(JSOP_NULL) - PUSH_OPND(JSVAL_NULL); - obj = NULL; - END_CASE(JSOP_NULL) - - BEGIN_CASE(JSOP_THIS) - obj = fp->thisp; - clasp = OBJ_GET_CLASS(cx, obj); - if (clasp->flags & JSCLASS_IS_EXTENDED) { - JSExtendedClass *xclasp; - - xclasp = (JSExtendedClass *) clasp; - if (xclasp->outerObject) { - obj = xclasp->outerObject(cx, obj); - if (!obj) { - ok = JS_FALSE; - goto out; - } - } - } - - PUSH_OPND(OBJECT_TO_JSVAL(obj)); - obj = NULL; - END_CASE(JSOP_THIS) - - BEGIN_CASE(JSOP_FALSE) - PUSH_OPND(JSVAL_FALSE); - obj = NULL; - END_CASE(JSOP_FALSE) - - BEGIN_CASE(JSOP_TRUE) - PUSH_OPND(JSVAL_TRUE); - obj = NULL; - END_CASE(JSOP_TRUE) - - BEGIN_CASE(JSOP_TABLESWITCH) - pc2 = pc; - len = GET_JUMP_OFFSET(pc2); - - /* - * ECMAv2+ forbids conversion of discriminant, so we will skip to - * the default case if the discriminant isn't already an int jsval. - * (This opcode is emitted only for dense jsint-domain switches.) - */ - rval = POP_OPND(); - if (!JSVAL_IS_INT(rval)) - DO_NEXT_OP(len); - i = JSVAL_TO_INT(rval); - - pc2 += JUMP_OFFSET_LEN; - low = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - high = GET_JUMP_OFFSET(pc2); - - i -= low; - if ((jsuint)i < (jsuint)(high - low + 1)) { - pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i; - off = (jsint) GET_JUMP_OFFSET(pc2); - if (off) - len = off; - } - END_VARLEN_CASE - - BEGIN_CASE(JSOP_LOOKUPSWITCH) - lval = POP_OPND(); - pc2 = pc; - len = GET_JUMP_OFFSET(pc2); - - if (!JSVAL_IS_NUMBER(lval) && - !JSVAL_IS_STRING(lval) && - !JSVAL_IS_BOOLEAN(lval)) { - DO_NEXT_OP(len); - } - - pc2 += JUMP_OFFSET_LEN; - npairs = (jsint) GET_ATOM_INDEX(pc2); - pc2 += ATOM_INDEX_LEN; - -#define SEARCH_PAIRS(MATCH_CODE) \ - while (npairs) { \ - atom = GET_ATOM(cx, script, pc2); \ - rval = ATOM_KEY(atom); \ - MATCH_CODE \ - if (match) { \ - pc2 += ATOM_INDEX_LEN; \ - len = GET_JUMP_OFFSET(pc2); \ - DO_NEXT_OP(len); \ - } \ - pc2 += ATOM_INDEX_LEN + JUMP_OFFSET_LEN; \ - npairs--; \ - } - if (JSVAL_IS_STRING(lval)) { - str = JSVAL_TO_STRING(lval); - SEARCH_PAIRS( - match = (JSVAL_IS_STRING(rval) && - ((str2 = JSVAL_TO_STRING(rval)) == str || - js_EqualStrings(str2, str))); - ) - } else if (JSVAL_IS_DOUBLE(lval)) { - d = *JSVAL_TO_DOUBLE(lval); - SEARCH_PAIRS( - match = (JSVAL_IS_DOUBLE(rval) && - *JSVAL_TO_DOUBLE(rval) == d); - ) - } else { - SEARCH_PAIRS( - match = (lval == rval); - ) - } -#undef SEARCH_PAIRS - END_VARLEN_CASE - - BEGIN_CASE(JSOP_TABLESWITCHX) - pc2 = pc; - len = GET_JUMPX_OFFSET(pc2); - - /* - * ECMAv2+ forbids conversion of discriminant, so we will skip to - * the default case if the discriminant isn't already an int jsval. - * (This opcode is emitted only for dense jsint-domain switches.) - */ - rval = POP_OPND(); - if (!JSVAL_IS_INT(rval)) - DO_NEXT_OP(len); - i = JSVAL_TO_INT(rval); - - pc2 += JUMPX_OFFSET_LEN; - low = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - high = GET_JUMP_OFFSET(pc2); - - i -= low; - if ((jsuint)i < (jsuint)(high - low + 1)) { - pc2 += JUMP_OFFSET_LEN + JUMPX_OFFSET_LEN * i; - off = (jsint) GET_JUMPX_OFFSET(pc2); - if (off) - len = off; - } - END_VARLEN_CASE - - BEGIN_CASE(JSOP_LOOKUPSWITCHX) - lval = POP_OPND(); - pc2 = pc; - len = GET_JUMPX_OFFSET(pc2); - - if (!JSVAL_IS_NUMBER(lval) && - !JSVAL_IS_STRING(lval) && - !JSVAL_IS_BOOLEAN(lval)) { - DO_NEXT_OP(len); - } - - pc2 += JUMPX_OFFSET_LEN; - npairs = (jsint) GET_ATOM_INDEX(pc2); - pc2 += ATOM_INDEX_LEN; - -#define SEARCH_EXTENDED_PAIRS(MATCH_CODE) \ - while (npairs) { \ - atom = GET_ATOM(cx, script, pc2); \ - rval = ATOM_KEY(atom); \ - MATCH_CODE \ - if (match) { \ - pc2 += ATOM_INDEX_LEN; \ - len = GET_JUMPX_OFFSET(pc2); \ - DO_NEXT_OP(len); \ - } \ - pc2 += ATOM_INDEX_LEN + JUMPX_OFFSET_LEN; \ - npairs--; \ - } - if (JSVAL_IS_STRING(lval)) { - str = JSVAL_TO_STRING(lval); - SEARCH_EXTENDED_PAIRS( - match = (JSVAL_IS_STRING(rval) && - ((str2 = JSVAL_TO_STRING(rval)) == str || - js_EqualStrings(str2, str))); - ) - } else if (JSVAL_IS_DOUBLE(lval)) { - d = *JSVAL_TO_DOUBLE(lval); - SEARCH_EXTENDED_PAIRS( - match = (JSVAL_IS_DOUBLE(rval) && - *JSVAL_TO_DOUBLE(rval) == d); - ) - } else { - SEARCH_EXTENDED_PAIRS( - match = (lval == rval); - ) - } -#undef SEARCH_EXTENDED_PAIRS - END_VARLEN_CASE - - EMPTY_CASE(JSOP_CONDSWITCH) - -#if JS_HAS_EXPORT_IMPORT - BEGIN_CASE(JSOP_EXPORTALL) - obj = fp->varobj; - SAVE_SP_AND_PC(fp); - ida = JS_Enumerate(cx, obj); - if (!ida) { - ok = JS_FALSE; - } else { - for (i = 0, j = ida->length; i < j; i++) { - id = ida->vector[i]; - ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); - if (!ok) - break; - if (!prop) - continue; - ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs); - if (ok) { - attrs |= JSPROP_EXPORTED; - ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs); - } - OBJ_DROP_PROPERTY(cx, obj2, prop); - if (!ok) - break; - } - JS_DestroyIdArray(cx, ida); - } - END_CASE(JSOP_EXPORTALL) - - BEGIN_LITOPX_CASE(JSOP_EXPORTNAME, 0) - id = ATOM_TO_JSID(atom); - obj = fp->varobj; - SAVE_SP_AND_PC(fp); - ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); - if (!ok) - goto out; - if (!prop) { - ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL, - JSPROP_EXPORTED, NULL); - } else { - ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs); - if (ok) { - attrs |= JSPROP_EXPORTED; - ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs); - } - OBJ_DROP_PROPERTY(cx, obj2, prop); - } - if (!ok) - goto out; - END_LITOPX_CASE(JSOP_EXPORTNAME) - - BEGIN_CASE(JSOP_IMPORTALL) - id = (jsid) JSVAL_VOID; - PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id)); - sp--; - END_CASE(JSOP_IMPORTALL) - - BEGIN_CASE(JSOP_IMPORTPROP) - /* Get an immediate atom naming the property. */ - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id)); - sp--; - END_CASE(JSOP_IMPORTPROP) - - BEGIN_CASE(JSOP_IMPORTELEM) - ELEMENT_OP(-1, ok = ImportProperty(cx, obj, id)); - sp -= 2; - END_CASE(JSOP_IMPORTELEM) -#endif /* JS_HAS_EXPORT_IMPORT */ - - BEGIN_CASE(JSOP_TRAP) - SAVE_SP_AND_PC(fp); - switch (JS_HandleTrap(cx, script, pc, &rval)) { - case JSTRAP_ERROR: - ok = JS_FALSE; - goto out; - case JSTRAP_CONTINUE: - JS_ASSERT(JSVAL_IS_INT(rval)); - op = (JSOp) JSVAL_TO_INT(rval); - JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT); - LOAD_INTERRUPT_HANDLER(rt); - DO_OP(); - case JSTRAP_RETURN: - fp->rval = rval; - goto out; - case JSTRAP_THROW: - cx->throwing = JS_TRUE; - cx->exception = rval; - ok = JS_FALSE; - goto out; - default:; - } - LOAD_INTERRUPT_HANDLER(rt); - END_CASE(JSOP_TRAP) - - BEGIN_CASE(JSOP_ARGUMENTS) - SAVE_SP_AND_PC(fp); - ok = js_GetArgsValue(cx, fp, &rval); - if (!ok) - goto out; - PUSH_OPND(rval); - obj = NULL; - END_CASE(JSOP_ARGUMENTS) - - BEGIN_CASE(JSOP_ARGSUB) - id = INT_TO_JSID(GET_ARGNO(pc)); - SAVE_SP_AND_PC(fp); - ok = js_GetArgsProperty(cx, fp, id, &obj, &rval); - if (!ok) - goto out; - if (!obj) { - /* - * If arguments was not overridden by eval('arguments = ...'), - * set obj to the magic cookie respected by JSOP_PUSHOBJ, just - * in case this bytecode is part of an 'arguments[i](j, k)' or - * similar such invocation sequence, where the function that - * is invoked expects its 'this' parameter to be the caller's - * arguments object. - */ - obj = LAZY_ARGS_THISP; - } - PUSH_OPND(rval); - END_CASE(JSOP_ARGSUB) - -#undef LAZY_ARGS_THISP - - BEGIN_CASE(JSOP_ARGCNT) - id = ATOM_TO_JSID(rt->atomState.lengthAtom); - SAVE_SP_AND_PC(fp); - ok = js_GetArgsProperty(cx, fp, id, &obj, &rval); - if (!ok) - goto out; - PUSH_OPND(rval); - END_CASE(JSOP_ARGCNT) - - BEGIN_CASE(JSOP_GETARG) - slot = GET_ARGNO(pc); - JS_ASSERT(slot < fp->fun->nargs); - PUSH_OPND(fp->argv[slot]); - obj = NULL; - END_CASE(JSOP_GETARG) - - BEGIN_CASE(JSOP_SETARG) - slot = GET_ARGNO(pc); - JS_ASSERT(slot < fp->fun->nargs); - vp = &fp->argv[slot]; - GC_POKE(cx, *vp); - *vp = FETCH_OPND(-1); - obj = NULL; - END_CASE(JSOP_SETARG) - - BEGIN_CASE(JSOP_GETVAR) - slot = GET_VARNO(pc); - JS_ASSERT(slot < fp->fun->u.i.nvars); - PUSH_OPND(fp->vars[slot]); - obj = NULL; - END_CASE(JSOP_GETVAR) - - BEGIN_CASE(JSOP_SETVAR) - slot = GET_VARNO(pc); - JS_ASSERT(slot < fp->fun->u.i.nvars); - vp = &fp->vars[slot]; - GC_POKE(cx, *vp); - *vp = FETCH_OPND(-1); - obj = NULL; - END_CASE(JSOP_SETVAR) - - BEGIN_CASE(JSOP_GETGVAR) - slot = GET_VARNO(pc); - JS_ASSERT(slot < fp->nvars); - lval = fp->vars[slot]; - if (JSVAL_IS_NULL(lval)) { - op = JSOP_NAME; - DO_OP(); - } - slot = JSVAL_TO_INT(lval); - obj = fp->varobj; - rval = OBJ_GET_SLOT(cx, obj, slot); - PUSH_OPND(rval); - END_CASE(JSOP_GETGVAR) - - BEGIN_CASE(JSOP_SETGVAR) - slot = GET_VARNO(pc); - JS_ASSERT(slot < fp->nvars); - rval = FETCH_OPND(-1); - lval = fp->vars[slot]; - obj = fp->varobj; - if (JSVAL_IS_NULL(lval)) { - /* - * Inline-clone and specialize JSOP_SETNAME code here because - * JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval] - * as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME. - */ - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - SAVE_SP_AND_PC(fp); - CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); - if (!ok) - goto out; - STORE_OPND(-1, rval); - } else { - slot = JSVAL_TO_INT(lval); - GC_POKE(cx, obj->slots[slot]); - OBJ_SET_SLOT(cx, obj, slot, rval); - } - obj = NULL; - END_CASE(JSOP_SETGVAR) - - BEGIN_CASE(JSOP_DEFCONST) - BEGIN_CASE(JSOP_DEFVAR) - atomIndex = GET_ATOM_INDEX(pc); - - do_JSOP_DEFCONST: - do_JSOP_DEFVAR: - atom = js_GetAtom(cx, &script->atomMap, atomIndex); - obj = fp->varobj; - attrs = JSPROP_ENUMERATE; - if (!(fp->flags & JSFRAME_EVAL)) - attrs |= JSPROP_PERMANENT; - if (op == JSOP_DEFCONST) - attrs |= JSPROP_READONLY; - - /* Lookup id in order to check for redeclaration problems. */ - id = ATOM_TO_JSID(atom); - SAVE_SP_AND_PC(fp); - ok = js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop); - if (!ok) - goto out; - - /* Bind a variable only if it's not yet defined. */ - if (!prop) { - ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL, - attrs, &prop); - if (!ok) - goto out; - JS_ASSERT(prop); - obj2 = obj; - } - - /* - * Try to optimize a property we either just created, or found - * directly in the global object, that is permanent, has a slot, - * and has stub getter and setter, into a "fast global" accessed - * by the JSOP_*GVAR opcodes. - */ - if (atomIndex < script->numGlobalVars && - (attrs & JSPROP_PERMANENT) && - obj2 == obj && - OBJ_IS_NATIVE(obj)) { - sprop = (JSScopeProperty *) prop; - if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)) && - SPROP_HAS_STUB_GETTER(sprop) && - SPROP_HAS_STUB_SETTER(sprop)) { - /* - * Fast globals use fp->vars to map the global name's - * atomIndex to the permanent fp->varobj slot number, - * tagged as a jsval. The atomIndex for the global's - * name literal is identical to its fp->vars index. - */ - fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot); - } - } - - OBJ_DROP_PROPERTY(cx, obj2, prop); - END_CASE(JSOP_DEFVAR) - - BEGIN_LITOPX_CASE(JSOP_DEFFUN, 0) - obj = ATOM_TO_OBJECT(atom); - fun = (JSFunction *) JS_GetPrivate(cx, obj); - id = ATOM_TO_JSID(fun->atom); - - /* - * We must be at top-level (either outermost block that forms a - * function's body, or a global) scope, not inside an expression - * (JSOP_{ANON,NAMED}FUNOBJ) or compound statement (JSOP_CLOSURE) - * in the same compilation unit (ECMA Program). - * - * However, we could be in a Program being eval'd from inside a - * with statement, so we need to distinguish scope chain head from - * variables object. Hence the obj2 vs. parent distinction below. - * First we make sure the function object we're defining has the - * right scope chain. Then we define its name in fp->varobj. - * - * If static link is not current scope, clone fun's object to link - * to the current scope via parent. This clause exists to enable - * sharing of compiled functions among multiple equivalent scopes, - * splitting the cost of compilation evenly among the scopes and - * amortizing it over a number of executions. Examples include XUL - * scripts and event handlers shared among Mozilla chrome windows, - * and server-side JS user-defined functions shared among requests. - * - * NB: The Script object exposes compile and exec in the language, - * such that this clause introduces an incompatible change from old - * JS versions that supported Script. Such a JS version supported - * executing a script that defined and called functions scoped by - * the compile-time static link, not by the exec-time scope chain. - * - * We sacrifice compatibility, breaking such scripts, in order to - * promote compile-cost sharing and amortizing, and because Script - * is not and will not be standardized. - */ - JS_ASSERT(!fp->blockChain); - obj2 = fp->scopeChain; - if (OBJ_GET_PARENT(cx, obj) != obj2) { - obj = js_CloneFunctionObject(cx, obj, obj2); - if (!obj) { - ok = JS_FALSE; - goto out; - } - } - - /* - * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All - * paths from here must flow through the "Restore fp->scopeChain" - * code below the OBJ_DEFINE_PROPERTY call. - */ - fp->scopeChain = obj; - rval = OBJECT_TO_JSVAL(obj); - - /* - * ECMA requires functions defined when entering Global code to be - * permanent, and functions defined when entering Eval code to be - * impermanent. - */ - attrs = JSPROP_ENUMERATE; - if (!(fp->flags & JSFRAME_EVAL)) - attrs |= JSPROP_PERMANENT; - - /* - * Load function flags that are also property attributes. Getters - * and setters do not need a slot, their value is stored elsewhere - * in the property itself, not in obj->slots. - */ - flags = JSFUN_GSFLAG2ATTR(fun->flags); - if (flags) { - attrs |= flags | JSPROP_SHARED; - rval = JSVAL_VOID; - } - - /* - * Check for a const property of the same name -- or any kind - * of property if executing with the strict option. We check - * here at runtime as well as at compile-time, to handle eval - * as well as multiple HTML script tags. - */ - parent = fp->varobj; - SAVE_SP_AND_PC(fp); - ok = js_CheckRedeclaration(cx, parent, id, attrs, NULL, NULL); - if (ok) { - ok = OBJ_DEFINE_PROPERTY(cx, parent, id, rval, - (flags & JSPROP_GETTER) - ? JS_EXTENSION (JSPropertyOp) obj - : NULL, - (flags & JSPROP_SETTER) - ? JS_EXTENSION (JSPropertyOp) obj - : NULL, - attrs, - &prop); - } - - /* Restore fp->scopeChain now that obj is defined in fp->varobj. */ - fp->scopeChain = obj2; - if (!ok) - goto out; - -#if 0 - if (attrs == (JSPROP_ENUMERATE | JSPROP_PERMANENT) && - script->numGlobalVars) { - /* - * As with JSOP_DEFVAR and JSOP_DEFCONST (above), fast globals - * use fp->vars to map the global function name's atomIndex to - * its permanent fp->varobj slot number, tagged as a jsval. - */ - sprop = (JSScopeProperty *) prop; - fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot); - } -#endif - OBJ_DROP_PROPERTY(cx, parent, prop); - END_LITOPX_CASE(JSOP_DEFFUN) - - BEGIN_LITOPX_CASE(JSOP_DEFLOCALFUN, VARNO_LEN) - /* - * Define a local function (i.e., one nested at the top level of - * another function), parented by the current scope chain, and - * stored in a local variable slot that the compiler allocated. - * This is an optimization over JSOP_DEFFUN that avoids requiring - * a call object for the outer function's activation. - */ - slot = GET_VARNO(pc2); - obj = ATOM_TO_OBJECT(atom); - - JS_ASSERT(!fp->blockChain); - if (!(fp->flags & JSFRAME_POP_BLOCKS)) { - /* - * If the compiler-created function object (obj) is scoped by a - * let-induced body block, temporarily update fp->blockChain so - * that js_GetScopeChain will clone the block into the runtime - * scope needed to parent the function object's clone. - */ - parent = OBJ_GET_PARENT(cx, obj); - if (OBJ_GET_CLASS(cx, parent) == &js_BlockClass) - fp->blockChain = parent; - parent = js_GetScopeChain(cx, fp); - } else { - /* - * We have already emulated JSOP_ENTERBLOCK for the enclosing - * body block, for a prior JSOP_DEFLOCALFUN in the prolog, so - * we just load fp->scopeChain into parent. - * - * In typical execution scenarios, the prolog bytecodes that - * include this JSOP_DEFLOCALFUN run, then come main bytecodes - * including JSOP_ENTERBLOCK for the outermost (body) block. - * JSOP_ENTERBLOCK will detect that it need not do anything if - * the body block was entered above due to a local function. - * Finally the matching JSOP_LEAVEBLOCK runs. - * - * If the matching JSOP_LEAVEBLOCK for the body block does not - * run for some reason, the body block will be properly "put" - * (via js_PutBlockObject) by the PutBlockObjects call at the - * bottom of js_Interpret. - */ - parent = fp->scopeChain; - JS_ASSERT(OBJ_GET_CLASS(cx, parent) == &js_BlockClass); - JS_ASSERT(OBJ_GET_PROTO(cx, parent) == OBJ_GET_PARENT(cx, obj)); - JS_ASSERT(OBJ_GET_CLASS(cx, OBJ_GET_PARENT(cx, parent)) - == &js_CallClass); - } - - /* If re-parenting, store a clone of the function object. */ - if (OBJ_GET_PARENT(cx, obj) != parent) { - SAVE_SP_AND_PC(fp); - obj = js_CloneFunctionObject(cx, obj, parent); - if (!obj) { - ok = JS_FALSE; - goto out; - } - } - fp->vars[slot] = OBJECT_TO_JSVAL(obj); - END_LITOPX_CASE(JSOP_DEFLOCALFUN) - - BEGIN_LITOPX_CASE(JSOP_ANONFUNOBJ, 0) - /* Push the specified function object literal. */ - obj = ATOM_TO_OBJECT(atom); - - /* If re-parenting, push a clone of the function object. */ - SAVE_SP_AND_PC(fp); - parent = js_GetScopeChain(cx, fp); - if (!parent) { - ok = JS_FALSE; - goto out; - } - if (OBJ_GET_PARENT(cx, obj) != parent) { - obj = js_CloneFunctionObject(cx, obj, parent); - if (!obj) { - ok = JS_FALSE; - goto out; - } - } - PUSH_OPND(OBJECT_TO_JSVAL(obj)); - obj = NULL; - END_LITOPX_CASE(JSOP_ANONFUNOBJ) - - BEGIN_LITOPX_CASE(JSOP_NAMEDFUNOBJ, 0) - /* ECMA ed. 3 FunctionExpression: function Identifier [etc.]. */ - rval = ATOM_KEY(atom); - JS_ASSERT(VALUE_IS_FUNCTION(cx, rval)); - - /* - * 1. Create a new object as if by the expression new Object(). - * 2. Add Result(1) to the front of the scope chain. - * - * Step 2 is achieved by making the new object's parent be the - * current scope chain, and then making the new object the parent - * of the Function object clone. - */ - SAVE_SP_AND_PC(fp); - obj2 = js_GetScopeChain(cx, fp); - if (!obj2) { - ok = JS_FALSE; - goto out; - } - parent = js_NewObject(cx, &js_ObjectClass, NULL, obj2); - if (!parent) { - ok = JS_FALSE; - goto out; - } - - /* - * 3. Create a new Function object as specified in section 13.2 - * with [parameters and body specified by the function expression - * that was parsed by the compiler into a Function object, and - * saved in the script's atom map]. - * - * Protect parent from GC after js_CloneFunctionObject calls into - * js_NewObject, which displaces the newborn object root in cx by - * allocating the clone, then runs a last-ditch GC while trying - * to allocate the clone's slots vector. Another, multi-threaded - * path: js_CloneFunctionObject => js_NewObject => OBJ_GET_CLASS - * which may suspend the current request in ClaimScope, with the - * newborn displaced as in the first scenario. - */ - fp->scopeChain = parent; - obj = js_CloneFunctionObject(cx, JSVAL_TO_OBJECT(rval), parent); - if (!obj) { - ok = JS_FALSE; - goto out; - } - - /* - * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All - * paths from here must flow through the "Restore fp->scopeChain" - * code below the OBJ_DEFINE_PROPERTY call. - */ - fp->scopeChain = obj; - rval = OBJECT_TO_JSVAL(obj); - - /* - * 4. Create a property in the object Result(1). The property's - * name is [fun->atom, the identifier parsed by the compiler], - * value is Result(3), and attributes are { DontDelete, ReadOnly }. - */ - fun = (JSFunction *) JS_GetPrivate(cx, obj); - attrs = JSFUN_GSFLAG2ATTR(fun->flags); - if (attrs) { - attrs |= JSPROP_SHARED; - rval = JSVAL_VOID; - } - ok = OBJ_DEFINE_PROPERTY(cx, parent, ATOM_TO_JSID(fun->atom), rval, - (attrs & JSPROP_GETTER) - ? JS_EXTENSION (JSPropertyOp) obj - : NULL, - (attrs & JSPROP_SETTER) - ? JS_EXTENSION (JSPropertyOp) obj - : NULL, - attrs | - JSPROP_ENUMERATE | JSPROP_PERMANENT | - JSPROP_READONLY, - NULL); - - /* Restore fp->scopeChain now that obj is defined in parent. */ - fp->scopeChain = obj2; - if (!ok) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - goto out; - } - - /* - * 5. Remove Result(1) from the front of the scope chain [no-op]. - * 6. Return Result(3). - */ - PUSH_OPND(OBJECT_TO_JSVAL(obj)); - obj = NULL; - END_LITOPX_CASE(JSOP_NAMEDFUNOBJ) - - BEGIN_LITOPX_CASE(JSOP_CLOSURE, 0) - /* - * ECMA ed. 3 extension: a named function expression in a compound - * statement (not at the top statement level of global code, or at - * the top level of a function body). - * - * Get immediate operand atom, which is a function object literal. - * From it, get the function to close. - */ - JS_ASSERT(VALUE_IS_FUNCTION(cx, ATOM_KEY(atom))); - obj = ATOM_TO_OBJECT(atom); - - /* - * Clone the function object with the current scope chain as the - * clone's parent. The original function object is the prototype - * of the clone. Do this only if re-parenting; the compiler may - * have seen the right parent already and created a sufficiently - * well-scoped function object. - */ - SAVE_SP_AND_PC(fp); - obj2 = js_GetScopeChain(cx, fp); - if (!obj2) { - ok = JS_FALSE; - goto out; - } - if (OBJ_GET_PARENT(cx, obj) != obj2) { - obj = js_CloneFunctionObject(cx, obj, obj2); - if (!obj) { - ok = JS_FALSE; - goto out; - } - } - - /* - * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All - * paths from here must flow through the "Restore fp->scopeChain" - * code below the OBJ_DEFINE_PROPERTY call. - */ - fp->scopeChain = obj; - rval = OBJECT_TO_JSVAL(obj); - - /* - * Make a property in fp->varobj with id fun->atom and value obj, - * unless fun is a getter or setter (in which case, obj is cast to - * a JSPropertyOp and passed accordingly). - */ - fun = (JSFunction *) JS_GetPrivate(cx, obj); - attrs = JSFUN_GSFLAG2ATTR(fun->flags); - if (attrs) { - attrs |= JSPROP_SHARED; - rval = JSVAL_VOID; - } - parent = fp->varobj; - ok = OBJ_DEFINE_PROPERTY(cx, parent, ATOM_TO_JSID(fun->atom), rval, - (attrs & JSPROP_GETTER) - ? JS_EXTENSION (JSPropertyOp) obj - : NULL, - (attrs & JSPROP_SETTER) - ? JS_EXTENSION (JSPropertyOp) obj - : NULL, - attrs | JSPROP_ENUMERATE - | JSPROP_PERMANENT, - &prop); - - /* Restore fp->scopeChain now that obj is defined in fp->varobj. */ - fp->scopeChain = obj2; - if (!ok) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - goto out; - } - -#if 0 - if (attrs == 0 && script->numGlobalVars) { - /* - * As with JSOP_DEFVAR and JSOP_DEFCONST (above), fast globals - * use fp->vars to map the global function name's atomIndex to - * its permanent fp->varobj slot number, tagged as a jsval. - */ - sprop = (JSScopeProperty *) prop; - fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot); - } -#endif - OBJ_DROP_PROPERTY(cx, parent, prop); - END_LITOPX_CASE(JSOP_CLOSURE) - -#if JS_HAS_GETTER_SETTER - BEGIN_CASE(JSOP_GETTER) - BEGIN_CASE(JSOP_SETTER) - op2 = (JSOp) *++pc; - switch (op2) { - case JSOP_SETNAME: - case JSOP_SETPROP: - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - rval = FETCH_OPND(-1); - i = -1; - goto gs_pop_lval; - - case JSOP_SETELEM: - rval = FETCH_OPND(-1); - FETCH_ELEMENT_ID(-2, id); - i = -2; - gs_pop_lval: - FETCH_OBJECT(cx, i - 1, lval, obj); - break; - - case JSOP_INITPROP: - JS_ASSERT(sp - fp->spbase >= 2); - rval = FETCH_OPND(-1); - i = -1; - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - goto gs_get_lval; - - case JSOP_INITELEM: - JS_ASSERT(sp - fp->spbase >= 3); - rval = FETCH_OPND(-1); - FETCH_ELEMENT_ID(-2, id); - i = -2; - gs_get_lval: - lval = FETCH_OPND(i-1); - JS_ASSERT(JSVAL_IS_OBJECT(lval)); - obj = JSVAL_TO_OBJECT(lval); - break; - - default: - JS_ASSERT(0); - } - - /* Ensure that id has a type suitable for use with obj. */ - CHECK_ELEMENT_ID(obj, id); - - SAVE_SP_AND_PC(fp); - if (JS_TypeOfValue(cx, rval) != JSTYPE_FUNCTION) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_GETTER_OR_SETTER, - (op == JSOP_GETTER) - ? js_getter_str - : js_setter_str); - ok = JS_FALSE; - goto out; - } - - /* - * Getters and setters are just like watchpoints from an access - * control point of view. - */ - ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &rtmp, &attrs); - if (!ok) - goto out; - - if (op == JSOP_GETTER) { - getter = JS_EXTENSION (JSPropertyOp) JSVAL_TO_OBJECT(rval); - setter = NULL; - attrs = JSPROP_GETTER; - } else { - getter = NULL; - setter = JS_EXTENSION (JSPropertyOp) JSVAL_TO_OBJECT(rval); - attrs = JSPROP_SETTER; - } - attrs |= JSPROP_ENUMERATE | JSPROP_SHARED; - - /* Check for a readonly or permanent property of the same name. */ - ok = js_CheckRedeclaration(cx, obj, id, attrs, NULL, NULL); - if (!ok) - goto out; - - ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, getter, setter, - attrs, NULL); - if (!ok) - goto out; - - obj = NULL; - sp += i; - if (js_CodeSpec[op2].ndefs) - STORE_OPND(-1, rval); - len = js_CodeSpec[op2].length; - DO_NEXT_OP(len); -#endif /* JS_HAS_GETTER_SETTER */ - - BEGIN_CASE(JSOP_NEWINIT) - argc = 0; - fp->sharpDepth++; - goto do_new; - - BEGIN_CASE(JSOP_ENDINIT) - if (--fp->sharpDepth == 0) - fp->sharpArray = NULL; - - /* Re-set the newborn root to the top of this object tree. */ - JS_ASSERT(sp - fp->spbase >= 1); - lval = FETCH_OPND(-1); - JS_ASSERT(JSVAL_IS_OBJECT(lval)); - cx->weakRoots.newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(lval); - END_CASE(JSOP_ENDINIT) - - BEGIN_CASE(JSOP_INITPROP) - /* Pop the property's value into rval. */ - JS_ASSERT(sp - fp->spbase >= 2); - rval = FETCH_OPND(-1); - - /* Get the immediate property name into id. */ - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - i = -1; - goto do_init; - - BEGIN_CASE(JSOP_INITELEM) - /* Pop the element's value into rval. */ - JS_ASSERT(sp - fp->spbase >= 3); - rval = FETCH_OPND(-1); - - /* Pop and conditionally atomize the element id. */ - FETCH_ELEMENT_ID(-2, id); - i = -2; - - do_init: - /* Find the object being initialized at top of stack. */ - lval = FETCH_OPND(i-1); - JS_ASSERT(JSVAL_IS_OBJECT(lval)); - obj = JSVAL_TO_OBJECT(lval); - - /* Ensure that id has a type suitable for use with obj. */ - CHECK_ELEMENT_ID(obj, id); - - /* Set the property named by obj[id] to rval. */ - SAVE_SP_AND_PC(fp); - ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); - if (!ok) - goto out; - sp += i; - len = js_CodeSpec[op].length; - DO_NEXT_OP(len); - -#if JS_HAS_SHARP_VARS - BEGIN_CASE(JSOP_DEFSHARP) - SAVE_SP_AND_PC(fp); - obj = fp->sharpArray; - if (!obj) { - obj = js_NewArrayObject(cx, 0, NULL); - if (!obj) { - ok = JS_FALSE; - goto out; - } - fp->sharpArray = obj; - } - i = (jsint) GET_ATOM_INDEX(pc); - id = INT_TO_JSID(i); - rval = FETCH_OPND(-1); - if (JSVAL_IS_PRIMITIVE(rval)) { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_SHARP_DEF, numBuf); - ok = JS_FALSE; - goto out; - } - ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); - if (!ok) - goto out; - END_CASE(JSOP_DEFSHARP) - - BEGIN_CASE(JSOP_USESHARP) - i = (jsint) GET_ATOM_INDEX(pc); - id = INT_TO_JSID(i); - obj = fp->sharpArray; - if (!obj) { - rval = JSVAL_VOID; - } else { - SAVE_SP_AND_PC(fp); - ok = OBJ_GET_PROPERTY(cx, obj, id, &rval); - if (!ok) - goto out; - } - if (!JSVAL_IS_OBJECT(rval)) { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i); - - SAVE_SP_AND_PC(fp); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_SHARP_USE, numBuf); - ok = JS_FALSE; - goto out; - } - PUSH_OPND(rval); - END_CASE(JSOP_USESHARP) -#endif /* JS_HAS_SHARP_VARS */ - - /* No-ops for ease of decompilation and jit'ing. */ - EMPTY_CASE(JSOP_TRY) - EMPTY_CASE(JSOP_FINALLY) - - /* Reset the stack to the given depth. */ - BEGIN_CASE(JSOP_SETSP) - i = (jsint) GET_ATOM_INDEX(pc); - JS_ASSERT(i >= 0); - - for (obj = fp->blockChain; obj; obj = OBJ_GET_PARENT(cx, obj)) { - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass); - if (OBJ_BLOCK_DEPTH(cx, obj) + (jsint)OBJ_BLOCK_COUNT(cx, obj) <= i) { - JS_ASSERT(OBJ_BLOCK_DEPTH(cx, obj) < i || OBJ_BLOCK_COUNT(cx, obj) == 0); - break; - } - } - fp->blockChain = obj; - - JS_ASSERT(ok); - for (obj = fp->scopeChain; - (clasp = OBJ_GET_CLASS(cx, obj)) == &js_WithClass || - clasp == &js_BlockClass; - obj = OBJ_GET_PARENT(cx, obj)) { - if (JS_GetPrivate(cx, obj) != fp || - OBJ_BLOCK_DEPTH(cx, obj) < i) { - break; - } - if (clasp == &js_BlockClass) - ok &= js_PutBlockObject(cx, obj); - else - JS_SetPrivate(cx, obj, NULL); - } - - fp->scopeChain = obj; - - /* Set sp after js_PutBlockObject to avoid potential GC hazards. */ - sp = fp->spbase + i; - - /* Don't fail until after we've updated all stacks. */ - if (!ok) - goto out; - END_CASE(JSOP_SETSP) - - BEGIN_CASE(JSOP_GOSUB) - JS_ASSERT(cx->exception != JSVAL_HOLE); - if (!cx->throwing) { - lval = JSVAL_HOLE; - } else { - lval = cx->exception; - cx->throwing = JS_FALSE; - } - PUSH(lval); - i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUB_LENGTH; - len = GET_JUMP_OFFSET(pc); - PUSH(INT_TO_JSVAL(i)); - END_VARLEN_CASE - - BEGIN_CASE(JSOP_GOSUBX) - JS_ASSERT(cx->exception != JSVAL_HOLE); - if (!cx->throwing) { - lval = JSVAL_HOLE; - } else { - lval = cx->exception; - cx->throwing = JS_FALSE; - } - PUSH(lval); - i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUBX_LENGTH; - len = GET_JUMPX_OFFSET(pc); - PUSH(INT_TO_JSVAL(i)); - END_VARLEN_CASE - - BEGIN_CASE(JSOP_RETSUB) - rval = POP(); - JS_ASSERT(JSVAL_IS_INT(rval)); - lval = POP(); - if (lval != JSVAL_HOLE) { - /* - * Exception was pending during finally, throw it *before* we - * adjust pc, because pc indexes into script->trynotes. This - * turns out not to be necessary, but it seems clearer. And - * it points out a FIXME: 350509, due to Igor Bukanov. - */ - cx->throwing = JS_TRUE; - cx->exception = lval; - ok = JS_FALSE; - goto out; - } - len = JSVAL_TO_INT(rval); - pc = script->main; - END_VARLEN_CASE - - BEGIN_CASE(JSOP_EXCEPTION) - JS_ASSERT(cx->throwing); - PUSH(cx->exception); - cx->throwing = JS_FALSE; - END_CASE(JSOP_EXCEPTION) - - BEGIN_CASE(JSOP_THROWING) - JS_ASSERT(!cx->throwing); - cx->throwing = JS_TRUE; - cx->exception = POP_OPND(); - END_CASE(JSOP_THROWING) - - BEGIN_CASE(JSOP_THROW) - JS_ASSERT(!cx->throwing); - cx->throwing = JS_TRUE; - cx->exception = POP_OPND(); - ok = JS_FALSE; - /* let the code at out try to catch the exception. */ - goto out; - - BEGIN_CASE(JSOP_SETLOCALPOP) - /* - * The stack must have a block with at least one local slot below - * the exception object. - */ - JS_ASSERT(sp - fp->spbase >= 2); - slot = GET_UINT16(pc); - JS_ASSERT(slot + 1 < (uintN)depth); - fp->spbase[slot] = POP_OPND(); - END_CASE(JSOP_SETLOCALPOP) - - BEGIN_CASE(JSOP_INSTANCEOF) - SAVE_SP_AND_PC(fp); - rval = FETCH_OPND(-1); - if (JSVAL_IS_PRIMITIVE(rval) || - !(obj = JSVAL_TO_OBJECT(rval))->map->ops->hasInstance) { - str = js_DecompileValueGenerator(cx, -1, rval, NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_INSTANCEOF_RHS, - JS_GetStringBytes(str)); - } - ok = JS_FALSE; - goto out; - } - lval = FETCH_OPND(-2); - cond = JS_FALSE; - ok = obj->map->ops->hasInstance(cx, obj, lval, &cond); - if (!ok) - goto out; - sp--; - STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); - END_CASE(JSOP_INSTANCEOF) - -#if JS_HAS_DEBUGGER_KEYWORD - BEGIN_CASE(JSOP_DEBUGGER) - { - JSTrapHandler handler = rt->debuggerHandler; - if (handler) { - SAVE_SP_AND_PC(fp); - switch (handler(cx, script, pc, &rval, - rt->debuggerHandlerData)) { - case JSTRAP_ERROR: - ok = JS_FALSE; - goto out; - case JSTRAP_CONTINUE: - break; - case JSTRAP_RETURN: - fp->rval = rval; - goto out; - case JSTRAP_THROW: - cx->throwing = JS_TRUE; - cx->exception = rval; - ok = JS_FALSE; - goto out; - default:; - } - LOAD_INTERRUPT_HANDLER(rt); - } - } - END_CASE(JSOP_DEBUGGER) -#endif /* JS_HAS_DEBUGGER_KEYWORD */ - -#if JS_HAS_XML_SUPPORT - BEGIN_CASE(JSOP_DEFXMLNS) - rval = POP(); - SAVE_SP_AND_PC(fp); - ok = js_SetDefaultXMLNamespace(cx, rval); - if (!ok) - goto out; - END_CASE(JSOP_DEFXMLNS) - - BEGIN_CASE(JSOP_ANYNAME) - SAVE_SP_AND_PC(fp); - ok = js_GetAnyName(cx, &rval); - if (!ok) - goto out; - PUSH_OPND(rval); - END_CASE(JSOP_ANYNAME) - - BEGIN_LITOPX_CASE(JSOP_QNAMEPART, 0) - PUSH_OPND(ATOM_KEY(atom)); - END_LITOPX_CASE(JSOP_QNAMEPART) - - BEGIN_LITOPX_CASE(JSOP_QNAMECONST, 0) - rval = ATOM_KEY(atom); - lval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - obj = js_ConstructXMLQNameObject(cx, lval, rval); - if (!obj) { - ok = JS_FALSE; - goto out; - } - STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - END_LITOPX_CASE(JSOP_QNAMECONST) - - BEGIN_CASE(JSOP_QNAME) - rval = FETCH_OPND(-1); - lval = FETCH_OPND(-2); - SAVE_SP_AND_PC(fp); - obj = js_ConstructXMLQNameObject(cx, lval, rval); - if (!obj) { - ok = JS_FALSE; - goto out; - } - sp--; - STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - END_CASE(JSOP_QNAME) - - BEGIN_CASE(JSOP_TOATTRNAME) - rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - ok = js_ToAttributeName(cx, &rval); - if (!ok) - goto out; - STORE_OPND(-1, rval); - END_CASE(JSOP_TOATTRNAME) - - BEGIN_CASE(JSOP_TOATTRVAL) - rval = FETCH_OPND(-1); - JS_ASSERT(JSVAL_IS_STRING(rval)); - SAVE_SP_AND_PC(fp); - str = js_EscapeAttributeValue(cx, JSVAL_TO_STRING(rval)); - if (!str) { - ok = JS_FALSE; - goto out; - } - STORE_OPND(-1, STRING_TO_JSVAL(str)); - END_CASE(JSOP_TOATTRVAL) - - BEGIN_CASE(JSOP_ADDATTRNAME) - BEGIN_CASE(JSOP_ADDATTRVAL) - rval = FETCH_OPND(-1); - lval = FETCH_OPND(-2); - str = JSVAL_TO_STRING(lval); - str2 = JSVAL_TO_STRING(rval); - SAVE_SP_AND_PC(fp); - str = js_AddAttributePart(cx, op == JSOP_ADDATTRNAME, str, str2); - if (!str) { - ok = JS_FALSE; - goto out; - } - sp--; - STORE_OPND(-1, STRING_TO_JSVAL(str)); - END_CASE(JSOP_ADDATTRNAME) - - BEGIN_CASE(JSOP_BINDXMLNAME) - lval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - ok = js_FindXMLProperty(cx, lval, &obj, &rval); - if (!ok) - goto out; - STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - PUSH_OPND(rval); - END_CASE(JSOP_BINDXMLNAME) - - BEGIN_CASE(JSOP_SETXMLNAME) - obj = JSVAL_TO_OBJECT(FETCH_OPND(-3)); - lval = FETCH_OPND(-2); - rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - ok = js_SetXMLProperty(cx, obj, lval, &rval); - if (!ok) - goto out; - sp -= 2; - STORE_OPND(-1, rval); - obj = NULL; - END_CASE(JSOP_SETXMLNAME) - - BEGIN_CASE(JSOP_XMLNAME) - lval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - ok = js_FindXMLProperty(cx, lval, &obj, &rval); - if (!ok) - goto out; - ok = js_GetXMLProperty(cx, obj, rval, &rval); - if (!ok) - goto out; - STORE_OPND(-1, rval); - END_CASE(JSOP_XMLNAME) - - BEGIN_CASE(JSOP_DESCENDANTS) - BEGIN_CASE(JSOP_DELDESC) - FETCH_OBJECT(cx, -2, lval, obj); - rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - ok = js_GetXMLDescendants(cx, obj, rval, &rval); - if (!ok) - goto out; - - if (op == JSOP_DELDESC) { - sp[-1] = rval; /* set local root */ - ok = js_DeleteXMLListElements(cx, JSVAL_TO_OBJECT(rval)); - if (!ok) - goto out; - rval = JSVAL_TRUE; /* always succeed */ - } - - sp--; - STORE_OPND(-1, rval); - END_CASE(JSOP_DESCENDANTS) - - BEGIN_CASE(JSOP_FILTER) - FETCH_OBJECT(cx, -1, lval, obj); - len = GET_JUMP_OFFSET(pc); - SAVE_SP_AND_PC(fp); - ok = js_FilterXMLList(cx, obj, pc + js_CodeSpec[op].length, &rval); - if (!ok) - goto out; - JS_ASSERT(fp->sp == sp); - STORE_OPND(-1, rval); - END_VARLEN_CASE - - BEGIN_CASE(JSOP_ENDFILTER) - *result = POP_OPND(); - goto out; - - EMPTY_CASE(JSOP_STARTXML) - EMPTY_CASE(JSOP_STARTXMLEXPR) - - BEGIN_CASE(JSOP_TOXML) - rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - obj = js_ValueToXMLObject(cx, rval); - if (!obj) { - ok = JS_FALSE; - goto out; - } - STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - END_CASE(JSOP_TOXML) - - BEGIN_CASE(JSOP_TOXMLLIST) - rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - obj = js_ValueToXMLListObject(cx, rval); - if (!obj) { - ok = JS_FALSE; - goto out; - } - STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - END_CASE(JSOP_TOXMLLIST) - - BEGIN_CASE(JSOP_XMLTAGEXPR) - rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - str = js_ValueToString(cx, rval); - if (!str) { - ok = JS_FALSE; - goto out; - } - STORE_OPND(-1, STRING_TO_JSVAL(str)); - END_CASE(JSOP_XMLTAGEXPR) - - BEGIN_CASE(JSOP_XMLELTEXPR) - rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - if (VALUE_IS_XML(cx, rval)) { - str = js_ValueToXMLString(cx, rval); - } else { - str = js_ValueToString(cx, rval); - if (str) - str = js_EscapeElementValue(cx, str); - } - if (!str) { - ok = JS_FALSE; - goto out; - } - STORE_OPND(-1, STRING_TO_JSVAL(str)); - END_CASE(JSOP_XMLELTEXPR) - - BEGIN_LITOPX_CASE(JSOP_XMLOBJECT, 0) - SAVE_SP_AND_PC(fp); - obj = js_CloneXMLObject(cx, ATOM_TO_OBJECT(atom)); - if (!obj) { - ok = JS_FALSE; - goto out; - } - PUSH_OPND(OBJECT_TO_JSVAL(obj)); - obj = NULL; - END_LITOPX_CASE(JSOP_XMLOBJECT) - - BEGIN_LITOPX_CASE(JSOP_XMLCDATA, 0) - str = ATOM_TO_STRING(atom); - obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_TEXT, NULL, str); - if (!obj) { - ok = JS_FALSE; - goto out; - } - PUSH_OPND(OBJECT_TO_JSVAL(obj)); - END_LITOPX_CASE(JSOP_XMLCDATA) - - BEGIN_LITOPX_CASE(JSOP_XMLCOMMENT, 0) - str = ATOM_TO_STRING(atom); - obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_COMMENT, NULL, str); - if (!obj) { - ok = JS_FALSE; - goto out; - } - PUSH_OPND(OBJECT_TO_JSVAL(obj)); - END_LITOPX_CASE(JSOP_XMLCOMMENT) - - BEGIN_LITOPX_CASE(JSOP_XMLPI, 0) - str = ATOM_TO_STRING(atom); - rval = FETCH_OPND(-1); - str2 = JSVAL_TO_STRING(rval); - SAVE_SP_AND_PC(fp); - obj = js_NewXMLSpecialObject(cx, - JSXML_CLASS_PROCESSING_INSTRUCTION, - str, str2); - if (!obj) { - ok = JS_FALSE; - goto out; - } - STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - END_LITOPX_CASE(JSOP_XMLPI) - - BEGIN_LITOPX_CASE(JSOP_GETMETHOD, 0) - /* Get an immediate atom naming the property. */ - id = ATOM_TO_JSID(atom); - lval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - if (!JSVAL_IS_PRIMITIVE(lval)) { - STORE_OPND(-1, lval); - obj = JSVAL_TO_OBJECT(lval); - - /* Special-case XML object method lookup, per ECMA-357. */ - if (OBJECT_IS_XML(cx, obj)) { - JSXMLObjectOps *ops; - - ops = (JSXMLObjectOps *) obj->map->ops; - obj = ops->getMethod(cx, obj, id, &rval); - if (!obj) - ok = JS_FALSE; - } else { - CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); - } - } else { - if (JSVAL_IS_STRING(lval)) { - i = JSProto_String; - } else if (JSVAL_IS_NUMBER(lval)) { - i = JSProto_Number; - } else if (JSVAL_IS_BOOLEAN(lval)) { - i = JSProto_Boolean; - } else { - JS_ASSERT(JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)); - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, - lval, NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_NO_PROPERTIES, - JS_GetStringBytes(str)); - } - ok = JS_FALSE; - goto out; - } - ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(i), &obj); - if (!ok) - goto out; - JS_ASSERT(obj); - STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); - obj = (JSObject *) lval; /* keep tagged as non-object */ - } - if (!ok) - goto out; - STORE_OPND(-1, rval); - END_LITOPX_CASE(JSOP_GETMETHOD) - - BEGIN_LITOPX_CASE(JSOP_SETMETHOD, 0) - /* Get an immediate atom naming the property. */ - id = ATOM_TO_JSID(atom); - rval = FETCH_OPND(-1); - FETCH_OBJECT(cx, -2, lval, obj); - SAVE_SP_AND_PC(fp); - - /* Special-case XML object method lookup, per ECMA-357. */ - if (OBJECT_IS_XML(cx, obj)) { - JSXMLObjectOps *ops; - - ops = (JSXMLObjectOps *) obj->map->ops; - ok = ops->setMethod(cx, obj, id, &rval); - } else { - CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); - } - if (!ok) - goto out; - --sp; - STORE_OPND(-1, rval); - obj = NULL; - END_LITOPX_CASE(JSOP_SETMETHOD) - - BEGIN_CASE(JSOP_GETFUNNS) - SAVE_SP_AND_PC(fp); - ok = js_GetFunctionNamespace(cx, &rval); - if (!ok) - goto out; - PUSH_OPND(rval); - END_CASE(JSOP_GETFUNNS) -#endif /* JS_HAS_XML_SUPPORT */ - - BEGIN_LITOPX_CASE(JSOP_ENTERBLOCK, 0) - obj = ATOM_TO_OBJECT(atom); - JS_ASSERT(fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp); - vp = sp + OBJ_BLOCK_COUNT(cx, obj); - JS_ASSERT(vp <= fp->spbase + depth); - while (sp < vp) { - STORE_OPND(0, JSVAL_VOID); - sp++; - } - - /* - * If this frame had to reflect the compile-time block chain into - * the runtime scope chain, we can't optimize block scopes out of - * runtime any longer, because an outer block that parents obj has - * been cloned onto the scope chain. To avoid re-cloning such a - * parent and accumulating redundant clones via js_GetScopeChain, - * we must clone each block eagerly on entry, and push it on the - * scope chain, until this frame pops. - */ - if (fp->flags & JSFRAME_POP_BLOCKS) { - JS_ASSERT(!fp->blockChain); - - /* - * Check whether JSOP_DEFLOCALFUN emulated JSOP_ENTERBLOCK for - * the body block in order to correctly scope the local cloned - * function object it creates. - */ - parent = fp->scopeChain; - if (OBJ_GET_PROTO(cx, parent) == obj) { - JS_ASSERT(OBJ_GET_CLASS(cx, parent) == &js_BlockClass); - } else { - obj = js_CloneBlockObject(cx, obj, parent, fp); - if (!obj) { - ok = JS_FALSE; - goto out; - } - fp->scopeChain = obj; - } - } else { - JS_ASSERT(!fp->blockChain || - OBJ_GET_PARENT(cx, obj) == fp->blockChain); - fp->blockChain = obj; - } - END_LITOPX_CASE(JSOP_ENTERBLOCK) - - BEGIN_CASE(JSOP_LEAVEBLOCKEXPR) - BEGIN_CASE(JSOP_LEAVEBLOCK) - { - JSObject **chainp; - - /* Grab the result of the expression. */ - if (op == JSOP_LEAVEBLOCKEXPR) - rval = FETCH_OPND(-1); - - chainp = &fp->blockChain; - obj = *chainp; - if (!obj) { - chainp = &fp->scopeChain; - obj = *chainp; - - /* - * This block was cloned, so clear its private data and sync - * its locals to their property slots. - */ - SAVE_SP_AND_PC(fp); - ok = js_PutBlockObject(cx, obj); - if (!ok) - goto out; - } - - sp -= GET_UINT16(pc); - JS_ASSERT(fp->spbase <= sp && sp <= fp->spbase + depth); - - /* Store the result into the topmost stack slot. */ - if (op == JSOP_LEAVEBLOCKEXPR) - STORE_OPND(-1, rval); - - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass); - JS_ASSERT(op == JSOP_LEAVEBLOCKEXPR - ? fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp - 1 - : fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp); - - *chainp = OBJ_GET_PARENT(cx, obj); - JS_ASSERT(chainp != &fp->blockChain || - !*chainp || - OBJ_GET_CLASS(cx, *chainp) == &js_BlockClass); - } - END_CASE(JSOP_LEAVEBLOCK) - - BEGIN_CASE(JSOP_GETLOCAL) - slot = GET_UINT16(pc); - JS_ASSERT(slot < (uintN)depth); - PUSH_OPND(fp->spbase[slot]); - obj = NULL; - END_CASE(JSOP_GETLOCAL) - - BEGIN_CASE(JSOP_SETLOCAL) - slot = GET_UINT16(pc); - JS_ASSERT(slot < (uintN)depth); - vp = &fp->spbase[slot]; - GC_POKE(cx, *vp); - *vp = FETCH_OPND(-1); - obj = NULL; - END_CASE(JSOP_SETLOCAL) - -/* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */ -#define FAST_LOCAL_INCREMENT_OP(PRE,OPEQ,MINMAX) \ - slot = GET_UINT16(pc); \ - JS_ASSERT(slot < (uintN)depth); \ - vp = fp->spbase + slot; \ - rval = *vp; \ - if (!JSVAL_IS_INT(rval) || rval == INT_TO_JSVAL(JSVAL_INT_##MINMAX)) \ - goto do_nonint_fast_incop; \ - PRE = rval; \ - rval OPEQ 2; \ - *vp = rval; \ - PUSH_OPND(PRE) - - BEGIN_CASE(JSOP_INCLOCAL) - FAST_LOCAL_INCREMENT_OP(rval, +=, MAX); - END_CASE(JSOP_INCLOCAL) - - BEGIN_CASE(JSOP_DECLOCAL) - FAST_LOCAL_INCREMENT_OP(rval, -=, MIN); - END_CASE(JSOP_DECLOCAL) - - BEGIN_CASE(JSOP_LOCALINC) - FAST_LOCAL_INCREMENT_OP(rtmp, +=, MAX); - END_CASE(JSOP_LOCALINC) - - BEGIN_CASE(JSOP_LOCALDEC) - FAST_LOCAL_INCREMENT_OP(rtmp, -=, MIN); - END_CASE(JSOP_LOCALDEC) - -#undef FAST_LOCAL_INCREMENT_OP - - EMPTY_CASE(JSOP_STARTITER) - - BEGIN_CASE(JSOP_ENDITER) - JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[-1])); - iterobj = JSVAL_TO_OBJECT(sp[-1]); - - /* - * js_CloseNativeIterator checks whether the iterator is not - * native, and also detects the case of a native iterator that - * has already escaped, even though a for-in loop caused it to - * be created. See jsiter.c. - */ - SAVE_SP_AND_PC(fp); - js_CloseNativeIterator(cx, iterobj); - *--sp = JSVAL_NULL; - END_CASE(JSOP_ENDITER) - -#if JS_HAS_GENERATORS - BEGIN_CASE(JSOP_GENERATOR) - pc += JSOP_GENERATOR_LENGTH; - SAVE_SP_AND_PC(fp); - obj = js_NewGenerator(cx, fp); - if (!obj) { - ok = JS_FALSE; - } else { - JS_ASSERT(!fp->callobj && !fp->argsobj); - fp->rval = OBJECT_TO_JSVAL(obj); - } - goto out; - - BEGIN_CASE(JSOP_YIELD) - ASSERT_NOT_THROWING(cx); - if (fp->flags & JSFRAME_FILTERING) { - /* FIXME: bug 309894 -- fix to eliminate this error. */ - JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, - JSMSG_YIELD_FROM_FILTER); - ok = JS_FALSE; - goto out; - } - if (FRAME_TO_GENERATOR(fp)->state == JSGEN_CLOSING) { - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, - fp->argv[-2], NULL); - if (str) { - JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_GENERATOR_YIELD, - JSSTRING_CHARS(str)); - } - ok = JS_FALSE; - goto out; - } - fp->rval = FETCH_OPND(-1); - fp->flags |= JSFRAME_YIELDING; - pc += JSOP_YIELD_LENGTH; - SAVE_SP_AND_PC(fp); - goto out; - - BEGIN_CASE(JSOP_ARRAYPUSH) - slot = GET_UINT16(pc); - JS_ASSERT(slot < (uintN)depth); - lval = fp->spbase[slot]; - obj = JSVAL_TO_OBJECT(lval); - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ArrayClass); - rval = FETCH_OPND(-1); - - /* We know that the array is created with only a 'length' slot. */ - i = obj->map->freeslot - (JSSLOT_FREE(&js_ArrayClass) + 1); - id = INT_TO_JSID(i); - - SAVE_SP_AND_PC(fp); - ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); - if (!ok) - goto out; - --sp; - END_CASE(JSOP_ARRAYPUSH) -#endif /* JS_HAS_GENERATORS */ - -#if !JS_HAS_GENERATORS - L_JSOP_GENERATOR: - L_JSOP_YIELD: - L_JSOP_ARRAYPUSH: -#endif - -#if !JS_HAS_DESTRUCTURING - L_JSOP_FOREACHKEYVAL: - L_JSOP_ENUMCONSTELEM: -#endif - -#ifdef JS_THREADED_INTERP - L_JSOP_BACKPATCH: - L_JSOP_BACKPATCH_POP: -#else - default: -#endif - { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%d", op); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_BYTECODE, numBuf); - ok = JS_FALSE; - goto out; - } - -#ifndef JS_THREADED_INTERP - - } /* switch (op) */ - - advance_pc: - pc += len; - -#ifdef DEBUG - if (tracefp) { - intN ndefs, n; - jsval *siter; - - ndefs = js_CodeSpec[op].ndefs; - if (ndefs) { - SAVE_SP_AND_PC(fp); - if (op == JSOP_FORELEM && sp[-1] == JSVAL_FALSE) - --ndefs; - for (n = -ndefs; n < 0; n++) { - str = js_DecompileValueGenerator(cx, n, sp[n], NULL); - if (str) { - fprintf(tracefp, "%s %s", - (n == -ndefs) ? " output:" : ",", - JS_GetStringBytes(str)); - } - } - fprintf(tracefp, " @ %d\n", sp - fp->spbase); - } - fprintf(tracefp, " stack: "); - for (siter = fp->spbase; siter < sp; siter++) { - str = js_ValueToSource(cx, *siter); - fprintf(tracefp, "%s ", - str ? JS_GetStringBytes(str) : ""); - } - fputc('\n', tracefp); - } -#endif /* DEBUG */ - } -#endif /* !JS_THREADED_INTERP */ - -out: - if (!ok) { - /* - * Has an exception been raised? Also insist that we are not in an - * XML filtering predicate expression, to avoid catching exceptions - * within the filtering predicate, such as this example taken from - * tests/e4x/Regress/regress-301596.js: - * - * try { - * .(@a == 1); - * throw 5; - * } catch (e) { - * } - * - * The inner interpreter activation executing the predicate bytecode - * will throw "reference to undefined XML name @a" (or 5, in older - * versions that followed the first edition of ECMA-357 and evaluated - * unbound identifiers to undefined), and the exception must not be - * caught until control unwinds to the outer interpreter activation. - * - * Otherwise, the wrong stack depth will be restored by JSOP_SETSP, - * and the catch will move into the filtering predicate expression, - * leading to double catch execution if it rethrows. - * - * FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=309894 - */ - if (cx->throwing && !(fp->flags & JSFRAME_FILTERING)) { - /* - * Call debugger throw hook if set (XXX thread safety?). - */ - JSTrapHandler handler = rt->throwHook; - if (handler) { - SAVE_SP_AND_PC(fp); - switch (handler(cx, script, pc, &rval, rt->throwHookData)) { - case JSTRAP_ERROR: - cx->throwing = JS_FALSE; - goto no_catch; - case JSTRAP_RETURN: - ok = JS_TRUE; - cx->throwing = JS_FALSE; - fp->rval = rval; - goto no_catch; - case JSTRAP_THROW: - cx->exception = rval; - case JSTRAP_CONTINUE: - default:; - } - LOAD_INTERRUPT_HANDLER(rt); - } - - /* - * Look for a try block in script that can catch this exception. - */ -#if JS_HAS_GENERATORS - if (JS_LIKELY(cx->exception != JSVAL_ARETURN)) { - SCRIPT_FIND_CATCH_START(script, pc, pc); - if (!pc) - goto no_catch; - } else { - pc = js_FindFinallyHandler(script, pc); - if (!pc) { - cx->throwing = JS_FALSE; - ok = JS_TRUE; - fp->rval = JSVAL_VOID; - goto no_catch; - } - } -#else - SCRIPT_FIND_CATCH_START(script, pc, pc); - if (!pc) - goto no_catch; -#endif - - /* Don't clear cx->throwing to save cx->exception from GC. */ - len = 0; - ok = JS_TRUE; - DO_NEXT_OP(len); - } -no_catch:; - } - - /* - * Check whether control fell off the end of a lightweight function, or an - * exception thrown under such a function was not caught by it. If so, go - * to the inline code under JSOP_RETURN. - */ - if (inlineCallCount) - goto inline_return; - - /* - * Reset sp before freeing stack slots, because our caller may GC soon. - * Clear spbase to indicate that we've popped the 2 * depth operand slots. - * Restore the previous frame's execution state. - */ - if (JS_LIKELY(mark != NULL)) { - /* If fp has blocks on its scope chain, home their locals now. */ - if (fp->flags & JSFRAME_POP_BLOCKS) { - SAVE_SP_AND_PC(fp); - ok &= PutBlockObjects(cx, fp); - } - - fp->sp = fp->spbase; - fp->spbase = NULL; - js_FreeRawStack(cx, mark); - } else { - SAVE_SP(fp); - } - -out2: - if (cx->version == currentVersion && currentVersion != originalVersion) - js_SetVersion(cx, originalVersion); - cx->interpLevel--; - return ok; - -atom_not_defined: - { - const char *printable = js_AtomToPrintableString(cx, atom); - if (printable) - js_ReportIsNotDefined(cx, printable); - ok = JS_FALSE; - goto out; - } -} diff --git a/spidermonkey/src/jsinterp.h b/spidermonkey/src/jsinterp.h deleted file mode 100644 index ab60b3a..0000000 --- a/spidermonkey/src/jsinterp.h +++ /dev/null @@ -1,361 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsinterp_h___ -#define jsinterp_h___ -/* - * JS interpreter interface. - */ -#include "jsprvtd.h" -#include "jspubtd.h" - -JS_BEGIN_EXTERN_C - -/* - * JS stack frame, may be allocated on the C stack by native callers. Always - * allocated on cx->stackPool for calls from the interpreter to an interpreted - * function. - * - * NB: This struct is manually initialized in jsinterp.c and jsiter.c. If you - * add new members, update both files. But first, try to remove members. The - * sharp* and xml* members should be moved onto the stack as local variables - * with well-known slots, if possible. - */ -struct JSStackFrame { - JSObject *callobj; /* lazily created Call object */ - JSObject *argsobj; /* lazily created arguments object */ - JSObject *varobj; /* variables object, where vars go */ - JSScript *script; /* script being interpreted */ - JSFunction *fun; /* function being called or null */ - JSObject *thisp; /* "this" pointer if in method */ - uintN argc; /* actual argument count */ - jsval *argv; /* base of argument stack slots */ - jsval rval; /* function return value */ - uintN nvars; /* local variable count */ - jsval *vars; /* base of variable stack slots */ - JSStackFrame *down; /* previous frame */ - void *annotation; /* used by Java security */ - JSObject *scopeChain; /* scope chain */ - jsbytecode *pc; /* program counter */ - jsval *sp; /* stack pointer */ - jsval *spbase; /* operand stack base */ - uintN sharpDepth; /* array/object initializer depth */ - JSObject *sharpArray; /* scope for #n= initializer vars */ - uint32 flags; /* frame flags -- see below */ - JSStackFrame *dormantNext; /* next dormant frame chain */ - JSObject *xmlNamespace; /* null or default xml namespace in E4X */ - JSObject *blockChain; /* active compile-time block scopes */ -}; - -typedef struct JSInlineFrame { - JSStackFrame frame; /* base struct */ - jsval *rvp; /* ptr to caller's return value slot */ - void *mark; /* mark before inline frame */ - void *hookData; /* debugger call hook data */ - JSVersion callerVersion; /* dynamic version of calling script */ -} JSInlineFrame; - -/* JS stack frame flags. */ -#define JSFRAME_CONSTRUCTING 0x01 /* frame is for a constructor invocation */ -#define JSFRAME_INTERNAL 0x02 /* internal call, not invoked by a script */ -#define JSFRAME_SKIP_CALLER 0x04 /* skip one link when evaluating f.caller - for this invocation of f */ -#define JSFRAME_ASSIGNING 0x08 /* a complex (not simplex JOF_ASSIGNING) op - is currently assigning to a property */ -#define JSFRAME_DEBUGGER 0x10 /* frame for JS_EvaluateInStackFrame */ -#define JSFRAME_EVAL 0x20 /* frame for obj_eval */ -#define JSFRAME_SPECIAL 0x30 /* special evaluation frame flags */ -#define JSFRAME_COMPILING 0x40 /* frame is being used by compiler */ -#define JSFRAME_COMPILE_N_GO 0x80 /* compiler-and-go mode, can optimize name - references based on scope chain */ -#define JSFRAME_SCRIPT_OBJECT 0x100 /* compiling source for a Script object */ -#define JSFRAME_YIELDING 0x200 /* js_Interpret dispatched JSOP_YIELD */ -#define JSFRAME_FILTERING 0x400 /* XML filtering predicate expression */ -#define JSFRAME_ITERATOR 0x800 /* trying to get an iterator for for-in */ -#define JSFRAME_POP_BLOCKS 0x1000 /* scope chain contains blocks to pop */ -#define JSFRAME_GENERATOR 0x2000 /* frame belongs to generator-iterator */ - -#define JSFRAME_OVERRIDE_SHIFT 24 /* override bit-set params; see jsfun.c */ -#define JSFRAME_OVERRIDE_BITS 8 - -/* - * Property cache for quickened get/set property opcodes. - */ -#define PROPERTY_CACHE_LOG2 10 -#define PROPERTY_CACHE_SIZE JS_BIT(PROPERTY_CACHE_LOG2) -#define PROPERTY_CACHE_MASK JS_BITMASK(PROPERTY_CACHE_LOG2) - -#define PROPERTY_CACHE_HASH(obj, id) \ - ((((jsuword)(obj) >> JSVAL_TAGBITS) ^ (jsuword)(id)) & PROPERTY_CACHE_MASK) - -#ifdef JS_THREADSAFE - -#if HAVE_ATOMIC_DWORD_ACCESS - -#define PCE_LOAD(cache, pce, entry) JS_ATOMIC_DWORD_LOAD(pce, entry) -#define PCE_STORE(cache, pce, entry) JS_ATOMIC_DWORD_STORE(pce, entry) - -#else /* !HAVE_ATOMIC_DWORD_ACCESS */ - -#define JS_PROPERTY_CACHE_METERING 1 - -#define PCE_LOAD(cache, pce, entry) \ - JS_BEGIN_MACRO \ - uint32 prefills_; \ - uint32 fills_ = (cache)->fills; \ - do { \ - /* Load until cache->fills is stable (see FILL macro below). */ \ - prefills_ = fills_; \ - (entry) = *(pce); \ - } while ((fills_ = (cache)->fills) != prefills_); \ - JS_END_MACRO - -#define PCE_STORE(cache, pce, entry) \ - JS_BEGIN_MACRO \ - do { \ - /* Store until no racing collider stores half or all of pce. */ \ - *(pce) = (entry); \ - } while (PCE_OBJECT(*pce) != PCE_OBJECT(entry) || \ - PCE_PROPERTY(*pce) != PCE_PROPERTY(entry)); \ - JS_END_MACRO - -#endif /* !HAVE_ATOMIC_DWORD_ACCESS */ - -#else /* !JS_THREADSAFE */ - -#define PCE_LOAD(cache, pce, entry) ((entry) = *(pce)) -#define PCE_STORE(cache, pce, entry) (*(pce) = (entry)) - -#endif /* !JS_THREADSAFE */ - -typedef union JSPropertyCacheEntry { - struct { - JSObject *object; /* weak link to object */ - JSScopeProperty *property; /* weak link to property */ - } s; -#ifdef HAVE_ATOMIC_DWORD_ACCESS - prdword align; -#endif -} JSPropertyCacheEntry; - -/* These may be called in lvalue or rvalue position. */ -#define PCE_OBJECT(entry) ((entry).s.object) -#define PCE_PROPERTY(entry) ((entry).s.property) - -typedef struct JSPropertyCache { - JSPropertyCacheEntry table[PROPERTY_CACHE_SIZE]; - JSBool empty; - JSBool disabled; -#ifdef JS_PROPERTY_CACHE_METERING - uint32 fills; - uint32 recycles; - uint32 tests; - uint32 misses; - uint32 flushes; -# define PCMETER(x) x -#else -# define PCMETER(x) /* nothing */ -#endif -} JSPropertyCache; - -#define PROPERTY_CACHE_FILL(cache, obj, id, sprop) \ - JS_BEGIN_MACRO \ - JSPropertyCache *cache_ = (cache); \ - if (!cache_->disabled) { \ - uintN hashIndex_ = (uintN) PROPERTY_CACHE_HASH(obj, id); \ - JSPropertyCacheEntry *pce_ = &cache_->table[hashIndex_]; \ - JSPropertyCacheEntry entry_; \ - JSScopeProperty *pce_sprop_; \ - PCE_LOAD(cache_, pce_, entry_); \ - pce_sprop_ = PCE_PROPERTY(entry_); \ - PCMETER(if (pce_sprop_ && pce_sprop_ != sprop) \ - cache_->recycles++); \ - PCE_OBJECT(entry_) = obj; \ - PCE_PROPERTY(entry_) = sprop; \ - cache_->empty = JS_FALSE; \ - PCMETER(cache_->fills++); \ - PCE_STORE(cache_, pce_, entry_); \ - } \ - JS_END_MACRO - -#define PROPERTY_CACHE_TEST(cache, obj, id, sprop) \ - JS_BEGIN_MACRO \ - uintN hashIndex_ = (uintN) PROPERTY_CACHE_HASH(obj, id); \ - JSPropertyCache *cache_ = (cache); \ - JSPropertyCacheEntry *pce_ = &cache_->table[hashIndex_]; \ - JSPropertyCacheEntry entry_; \ - JSScopeProperty *pce_sprop_; \ - PCE_LOAD(cache_, pce_, entry_); \ - pce_sprop_ = PCE_PROPERTY(entry_); \ - PCMETER(cache_->tests++); \ - if (pce_sprop_ && \ - PCE_OBJECT(entry_) == obj && \ - pce_sprop_->id == id) { \ - sprop = pce_sprop_; \ - } else { \ - PCMETER(cache_->misses++); \ - sprop = NULL; \ - } \ - JS_END_MACRO - -extern void -js_FlushPropertyCache(JSContext *cx); - -extern void -js_DisablePropertyCache(JSContext *cx); - -extern void -js_EnablePropertyCache(JSContext *cx); - -extern JS_FRIEND_API(jsval *) -js_AllocStack(JSContext *cx, uintN nslots, void **markp); - -extern JS_FRIEND_API(void) -js_FreeStack(JSContext *cx, void *mark); - -extern JSBool -js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -extern JSBool -js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -extern JSBool -js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -extern JSBool -js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -#ifdef DUMP_CALL_TABLE -# define JSOPTION_LOGCALL_TOSOURCE JS_BIT(15) - -extern JSHashTable *js_CallTable; -extern size_t js_LogCallToSourceLimit; - -extern void js_DumpCallTable(JSContext *cx); -#endif - -/* - * Refresh and return fp->scopeChain. It may be stale if block scopes are - * active but not yet reflected by objects in the scope chain. If a block - * scope contains a with, eval, XML filtering predicate, or similar such - * dynamically scoped construct, then compile-time block scope at fp->blocks - * must reflect at runtime. - */ -extern JSObject * -js_GetScopeChain(JSContext *cx, JSStackFrame *fp); - -/* - * Compute the 'this' parameter for a call with nominal 'this' given by thisp - * and arguments including argv[-1] (nominal 'this') and argv[-2] (callee). - * Activation objects ("Call" objects not created with "new Call()", i.e., - * "Call" objects that have private data) may not be referred to by 'this', - * per ECMA-262, so js_ComputeThis censors them. - */ -extern JSObject * -js_ComputeThis(JSContext *cx, JSObject *thisp, jsval *argv); - -/* - * NB: js_Invoke requires that cx is currently running JS (i.e., that cx->fp - * is non-null), and that the callee, |this| parameter, and actual arguments - * are already pushed on the stack under cx->fp->sp. - */ -extern JS_FRIEND_API(JSBool) -js_Invoke(JSContext *cx, uintN argc, uintN flags); - -/* - * Consolidated js_Invoke flags simply rename certain JSFRAME_* flags, so that - * we can share bits stored in JSStackFrame.flags and passed to: - * - * js_Invoke - * js_InternalInvoke - * js_ValueToFunction - * js_ValueToFunctionObject - * js_ValueToCallableObject - * js_ReportIsNotFunction - * - * See jsfun.h for the latter four and flag renaming macros. - */ -#define JSINVOKE_CONSTRUCT JSFRAME_CONSTRUCTING -#define JSINVOKE_INTERNAL JSFRAME_INTERNAL -#define JSINVOKE_SKIP_CALLER JSFRAME_SKIP_CALLER -#define JSINVOKE_ITERATOR JSFRAME_ITERATOR - -/* - * Mask to isolate construct and iterator flags for use with jsfun.h functions. - */ -#define JSINVOKE_FUNFLAGS (JSINVOKE_CONSTRUCT | JSINVOKE_ITERATOR) - -/* - * "Internal" calls may come from C or C++ code using a JSContext on which no - * JS is running (!cx->fp), so they may need to push a dummy JSStackFrame. - */ -#define js_InternalCall(cx,obj,fval,argc,argv,rval) \ - js_InternalInvoke(cx, obj, fval, 0, argc, argv, rval) - -#define js_InternalConstruct(cx,obj,fval,argc,argv,rval) \ - js_InternalInvoke(cx, obj, fval, JSINVOKE_CONSTRUCT, argc, argv, rval) - -extern JSBool -js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags, - uintN argc, jsval *argv, jsval *rval); - -extern JSBool -js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval, - JSAccessMode mode, uintN argc, jsval *argv, jsval *rval); - -extern JSBool -js_Execute(JSContext *cx, JSObject *chain, JSScript *script, - JSStackFrame *down, uintN flags, jsval *result); - -extern JSBool -js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs, - JSObject **objp, JSProperty **propp); - -extern JSBool -js_StrictlyEqual(jsval lval, jsval rval); - -extern JSBool -js_InvokeConstructor(JSContext *cx, jsval *vp, uintN argc); - -extern JSBool -js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result); - -JS_END_EXTERN_C - -#endif /* jsinterp_h___ */ diff --git a/spidermonkey/src/jsiter.c b/spidermonkey/src/jsiter.c deleted file mode 100644 index 0a4de54..0000000 --- a/spidermonkey/src/jsiter.c +++ /dev/null @@ -1,1080 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JavaScript iterators. - */ -#include "jsstddef.h" -#include /* for memcpy */ -#include "jstypes.h" -#include "jsutil.h" -#include "jsarena.h" -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jsbool.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsexn.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jsiter.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsscope.h" -#include "jsscript.h" - -#if JS_HAS_XML_SUPPORT -#include "jsxml.h" -#endif - -extern const char js_throw_str[]; /* from jsscan.h */ - -#define JSSLOT_ITER_STATE (JSSLOT_PRIVATE) -#define JSSLOT_ITER_FLAGS (JSSLOT_PRIVATE + 1) - -#if JSSLOT_ITER_FLAGS >= JS_INITIAL_NSLOTS -#error JS_INITIAL_NSLOTS must be greater than JSSLOT_ITER_FLAGS. -#endif - -/* - * Shared code to close iterator's state either through an explicit call or - * when GC detects that the iterator is no longer reachable. - */ -void -js_CloseIteratorState(JSContext *cx, JSObject *iterobj) -{ - jsval *slots; - jsval state, parent; - JSObject *iterable; - - JS_ASSERT(JS_InstanceOf(cx, iterobj, &js_IteratorClass, NULL)); - slots = iterobj->slots; - - /* Avoid double work if js_CloseNativeIterator was called on obj. */ - state = slots[JSSLOT_ITER_STATE]; - if (JSVAL_IS_NULL(state)) - return; - - /* Protect against failure to fully initialize obj. */ - parent = slots[JSSLOT_PARENT]; - if (!JSVAL_IS_PRIMITIVE(parent)) { - iterable = JSVAL_TO_OBJECT(parent); -#if JS_HAS_XML_SUPPORT - if ((JSVAL_TO_INT(slots[JSSLOT_ITER_FLAGS]) & JSITER_FOREACH) && - OBJECT_IS_XML(cx, iterable)) { - ((JSXMLObjectOps *) iterable->map->ops)-> - enumerateValues(cx, iterable, JSENUMERATE_DESTROY, &state, - NULL, NULL); - } else -#endif - OBJ_ENUMERATE(cx, iterable, JSENUMERATE_DESTROY, &state, NULL); - } - slots[JSSLOT_ITER_STATE] = JSVAL_NULL; -} - -JSClass js_IteratorClass = { - "Iterator", - JSCLASS_HAS_RESERVED_SLOTS(2) | /* slots for state and flags */ - JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -static JSBool -InitNativeIterator(JSContext *cx, JSObject *iterobj, JSObject *obj, uintN flags) -{ - jsval state; - JSBool ok; - - JS_ASSERT(JSVAL_TO_PRIVATE(iterobj->slots[JSSLOT_CLASS]) == - &js_IteratorClass); - - /* Initialize iterobj in case of enumerate hook failure. */ - iterobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj); - iterobj->slots[JSSLOT_ITER_STATE] = JSVAL_NULL; - iterobj->slots[JSSLOT_ITER_FLAGS] = INT_TO_JSVAL(flags); - if (!js_RegisterCloseableIterator(cx, iterobj)) - return JS_FALSE; - if (!obj) - return JS_TRUE; - - ok = -#if JS_HAS_XML_SUPPORT - ((flags & JSITER_FOREACH) && OBJECT_IS_XML(cx, obj)) - ? ((JSXMLObjectOps *) obj->map->ops)-> - enumerateValues(cx, obj, JSENUMERATE_INIT, &state, NULL, NULL) - : -#endif - OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &state, NULL); - if (!ok) - return JS_FALSE; - - iterobj->slots[JSSLOT_ITER_STATE] = state; - if (flags & JSITER_ENUMERATE) { - /* - * The enumerating iterator needs the original object to suppress - * enumeration of deleted or shadowed prototype properties. Since the - * enumerator never escapes to scripts, we use the prototype slot to - * store the original object. - */ - JS_ASSERT(obj != iterobj); - iterobj->slots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(obj); - } - return JS_TRUE; -} - -static JSBool -Iterator(JSContext *cx, JSObject *iterobj, uintN argc, jsval *argv, jsval *rval) -{ - JSBool keyonly; - uintN flags; - JSObject *obj; - - keyonly = JS_FALSE; - if (!js_ValueToBoolean(cx, argv[1], &keyonly)) - return JS_FALSE; - flags = keyonly ? 0 : JSITER_FOREACH; - - if (cx->fp->flags & JSFRAME_CONSTRUCTING) { - /* XXX work around old valueOf call hidden beneath js_ValueToObject */ - if (!JSVAL_IS_PRIMITIVE(argv[0])) { - obj = JSVAL_TO_OBJECT(argv[0]); - } else { - obj = js_ValueToNonNullObject(cx, argv[0]); - if (!obj) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(obj); - } - return InitNativeIterator(cx, iterobj, obj, flags); - } - - *rval = argv[0]; - return js_ValueToIterator(cx, flags, rval); -} - -static JSBool -NewKeyValuePair(JSContext *cx, jsid key, jsval val, jsval *rval) -{ - jsval vec[2]; - JSTempValueRooter tvr; - JSObject *aobj; - - vec[0] = ID_TO_VALUE(key); - vec[1] = val; - - JS_PUSH_TEMP_ROOT(cx, 2, vec, &tvr); - aobj = js_NewArrayObject(cx, 2, vec); - *rval = OBJECT_TO_JSVAL(aobj); - JS_POP_TEMP_ROOT(cx, &tvr); - - return aobj != NULL; -} - -static JSBool -IteratorNextImpl(JSContext *cx, JSObject *obj, jsval *rval) -{ - JSObject *iterable; - jsval state; - uintN flags; - JSBool foreach, ok; - jsid id; - - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_IteratorClass); - - iterable = OBJ_GET_PARENT(cx, obj); - JS_ASSERT(iterable); - state = OBJ_GET_SLOT(cx, obj, JSSLOT_ITER_STATE); - if (JSVAL_IS_NULL(state)) - goto stop; - - flags = JSVAL_TO_INT(OBJ_GET_SLOT(cx, obj, JSSLOT_ITER_FLAGS)); - JS_ASSERT(!(flags & JSITER_ENUMERATE)); - foreach = (flags & JSITER_FOREACH) != 0; - ok = -#if JS_HAS_XML_SUPPORT - (foreach && OBJECT_IS_XML(cx, iterable)) - ? ((JSXMLObjectOps *) iterable->map->ops)-> - enumerateValues(cx, iterable, JSENUMERATE_NEXT, &state, - &id, rval) - : -#endif - OBJ_ENUMERATE(cx, iterable, JSENUMERATE_NEXT, &state, &id); - if (!ok) - return JS_FALSE; - - OBJ_SET_SLOT(cx, obj, JSSLOT_ITER_STATE, state); - if (JSVAL_IS_NULL(state)) - goto stop; - - if (foreach) { -#if JS_HAS_XML_SUPPORT - if (!OBJECT_IS_XML(cx, iterable) && - !OBJ_GET_PROPERTY(cx, iterable, id, rval)) { - return JS_FALSE; - } -#endif - if (!NewKeyValuePair(cx, id, *rval, rval)) - return JS_FALSE; - } else { - *rval = ID_TO_VALUE(id); - } - return JS_TRUE; - - stop: - JS_ASSERT(OBJ_GET_SLOT(cx, obj, JSSLOT_ITER_STATE) == JSVAL_NULL); - *rval = JSVAL_HOLE; - return JS_TRUE; -} - -static JSBool -js_ThrowStopIteration(JSContext *cx, JSObject *obj) -{ - jsval v; - - JS_ASSERT(!JS_IsExceptionPending(cx)); - if (js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_StopIteration), &v)) - JS_SetPendingException(cx, v); - return JS_FALSE; -} - -static JSBool -iterator_next(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - if (!JS_InstanceOf(cx, obj, &js_IteratorClass, argv)) - return JS_FALSE; - - if (!IteratorNextImpl(cx, obj, rval)) - return JS_FALSE; - - if (*rval == JSVAL_HOLE) { - *rval = JSVAL_NULL; - js_ThrowStopIteration(cx, obj); - return JS_FALSE; - } - return JS_TRUE; -} - -static JSBool -iterator_self(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -static JSFunctionSpec iterator_methods[] = { - {js_iterator_str, iterator_self, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {js_next_str, iterator_next, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {0,0,0,0,0} -}; - -uintN -js_GetNativeIteratorFlags(JSContext *cx, JSObject *iterobj) -{ - if (OBJ_GET_CLASS(cx, iterobj) != &js_IteratorClass) - return 0; - return JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_FLAGS)); -} - -void -js_CloseNativeIterator(JSContext *cx, JSObject *iterobj) -{ - uintN flags; - - /* - * If this iterator is not an instance of the native default iterator - * class, leave it to be GC'ed. - */ - if (!JS_InstanceOf(cx, iterobj, &js_IteratorClass, NULL)) - return; - - /* - * If this iterator was not created by js_ValueToIterator called from the - * for-in loop code in js_Interpret, leave it to be GC'ed. - */ - flags = JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_FLAGS)); - if (!(flags & JSITER_ENUMERATE)) - return; - - js_CloseIteratorState(cx, iterobj); -} - -/* - * Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists. - * Otherwise construct the defualt iterator. - */ -JSBool -js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp) -{ - JSObject *obj; - JSTempValueRooter tvr; - const JSAtom *atom; - JSBool ok; - JSObject *iterobj; - jsval arg; - JSString *str; - - JS_ASSERT(!(flags & ~(JSITER_ENUMERATE | - JSITER_FOREACH | - JSITER_KEYVALUE))); - - /* JSITER_KEYVALUE must always come with JSITER_FOREACH */ - JS_ASSERT(!(flags & JSITER_KEYVALUE) || (flags & JSITER_FOREACH)); - - /* XXX work around old valueOf call hidden beneath js_ValueToObject */ - if (!JSVAL_IS_PRIMITIVE(*vp)) { - obj = JSVAL_TO_OBJECT(*vp); - } else { - /* - * Enumerating over null and undefined gives an empty enumerator. - * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of - * the first production in 12.6.4 and step 4 of the second production, - * but it's "web JS" compatible. - */ - if ((flags & JSITER_ENUMERATE)) { - if (!js_ValueToObject(cx, *vp, &obj)) - return JS_FALSE; - if (!obj) - goto default_iter; - } else { - obj = js_ValueToNonNullObject(cx, *vp); - if (!obj) - return JS_FALSE; - } - } - - JS_ASSERT(obj); - JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); - - atom = cx->runtime->atomState.iteratorAtom; -#if JS_HAS_XML_SUPPORT - if (OBJECT_IS_XML(cx, obj)) { - if (!js_GetXMLFunction(cx, obj, ATOM_TO_JSID(atom), vp)) - goto bad; - } else -#endif - { - if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp)) - goto bad; - } - - if (JSVAL_IS_VOID(*vp)) { - default_iter: - /* - * Fail over to the default enumerating native iterator. - * - * Create iterobj with a NULL parent to ensure that we use the correct - * scope chain to lookup the iterator's constructor. Since we use the - * parent slot to keep track of the iterable, we must fix it up after. - */ - iterobj = js_NewObject(cx, &js_IteratorClass, NULL, NULL); - if (!iterobj) - goto bad; - - /* Store iterobj in *vp to protect it from GC (callers must root vp). */ - *vp = OBJECT_TO_JSVAL(iterobj); - - if (!InitNativeIterator(cx, iterobj, obj, flags)) - goto bad; - } else { - arg = BOOLEAN_TO_JSVAL((flags & JSITER_FOREACH) == 0); - if (!js_InternalInvoke(cx, obj, *vp, JSINVOKE_ITERATOR, 1, &arg, vp)) - goto bad; - if (JSVAL_IS_PRIMITIVE(*vp)) { - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, *vp, NULL); - if (str) { - JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_ITERATOR_RETURN, - JSSTRING_CHARS(str), - JSSTRING_CHARS(ATOM_TO_STRING(atom))); - } - goto bad; - } - } - - ok = JS_TRUE; - out: - if (obj) - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; - bad: - ok = JS_FALSE; - goto out; -} - -static JSBool -CallEnumeratorNext(JSContext *cx, JSObject *iterobj, uintN flags, jsval *rval) -{ - JSObject *obj, *origobj; - jsval state; - JSBool foreach; - jsid id; - JSObject *obj2; - JSBool cond; - JSClass *clasp; - JSExtendedClass *xclasp; - JSProperty *prop; - JSString *str; - - JS_ASSERT(flags & JSITER_ENUMERATE); - JS_ASSERT(JSVAL_TO_PRIVATE(iterobj->slots[JSSLOT_CLASS]) == - &js_IteratorClass); - - obj = JSVAL_TO_OBJECT(iterobj->slots[JSSLOT_PARENT]); - origobj = JSVAL_TO_OBJECT(iterobj->slots[JSSLOT_PROTO]); - state = iterobj->slots[JSSLOT_ITER_STATE]; - if (JSVAL_IS_NULL(state)) - goto stop; - - foreach = (flags & JSITER_FOREACH) != 0; -#if JS_HAS_XML_SUPPORT - /* - * Treat an XML object specially only when it starts the prototype chain. - * Otherwise we need to do the usual deleted and shadowed property checks. - */ - if (obj == origobj && OBJECT_IS_XML(cx, obj)) { - if (foreach) { - JSXMLObjectOps *xmlops = (JSXMLObjectOps *) obj->map->ops; - - if (!xmlops->enumerateValues(cx, obj, JSENUMERATE_NEXT, &state, - &id, rval)) { - return JS_FALSE; - } - } else { - if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id)) - return JS_FALSE; - } - iterobj->slots[JSSLOT_ITER_STATE] = state; - if (JSVAL_IS_NULL(state)) - goto stop; - } else -#endif - { - restart: - if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id)) - return JS_TRUE; - - iterobj->slots[JSSLOT_ITER_STATE] = state; - if (JSVAL_IS_NULL(state)) { -#if JS_HAS_XML_SUPPORT - if (OBJECT_IS_XML(cx, obj)) { - /* - * We just finished enumerating an XML obj that is present on - * the prototype chain of a non-XML origobj. Stop further - * prototype chain searches because XML objects don't - * enumerate prototypes. - */ - JS_ASSERT(origobj != obj); - JS_ASSERT(!OBJECT_IS_XML(cx, origobj)); - } else -#endif - { - obj = OBJ_GET_PROTO(cx, obj); - if (obj) { - iterobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj); - if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &state, NULL)) - return JS_FALSE; - iterobj->slots[JSSLOT_ITER_STATE] = state; - if (!JSVAL_IS_NULL(state)) - goto restart; - } - } - goto stop; - } - - /* Skip properties not in obj when looking from origobj. */ - if (!OBJ_LOOKUP_PROPERTY(cx, origobj, id, &obj2, &prop)) - return JS_FALSE; - if (!prop) - goto restart; - OBJ_DROP_PROPERTY(cx, obj2, prop); - - /* - * If the id was found in a prototype object or an unrelated object - * (specifically, not in an inner object for obj), skip it. This step - * means that all OBJ_LOOKUP_PROPERTY implementations must return an - * object further along on the prototype chain, or else possibly an - * object returned by the JSExtendedClass.outerObject optional hook. - */ - if (obj != obj2) { - cond = JS_FALSE; - clasp = OBJ_GET_CLASS(cx, obj2); - if (clasp->flags & JSCLASS_IS_EXTENDED) { - xclasp = (JSExtendedClass *) clasp; - cond = xclasp->outerObject && - xclasp->outerObject(cx, obj2) == obj; - } - if (!cond) - goto restart; - } - - if (foreach) { - /* Get property querying the original object. */ - if (!OBJ_GET_PROPERTY(cx, origobj, id, rval)) - return JS_FALSE; - } - } - - if (foreach) { - if (flags & JSITER_KEYVALUE) { - if (!NewKeyValuePair(cx, id, *rval, rval)) - return JS_FALSE; - } - } else { - /* Make rval a string for uniformity and compatibility. */ - if (JSID_IS_ATOM(id)) { - *rval = ATOM_KEY(JSID_TO_ATOM(id)); - } -#if JS_HAS_XML_SUPPORT - else if (JSID_IS_OBJECT(id)) { - str = js_ValueToString(cx, OBJECT_JSID_TO_JSVAL(id)); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - } -#endif - else { - str = js_NumberToString(cx, (jsdouble)JSID_TO_INT(id)); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - } - } - return JS_TRUE; - - stop: - JS_ASSERT(iterobj->slots[JSSLOT_ITER_STATE] == JSVAL_NULL); - *rval = JSVAL_HOLE; - return JS_TRUE; -} - -JSBool -js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval) -{ - uintN flags; - - /* Fast path for native iterators */ - if (OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass) { - flags = JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_FLAGS)); - if (flags & JSITER_ENUMERATE) - return CallEnumeratorNext(cx, iterobj, flags, rval); - - /* - * Call next directly as all the methods of the native iterator are - * read-only and permanent. - */ - if (!IteratorNextImpl(cx, iterobj, rval)) - return JS_FALSE; - } else { - jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom); - - if (!JS_GetMethodById(cx, iterobj, id, &iterobj, rval)) - return JS_FALSE; - if (!js_InternalCall(cx, iterobj, *rval, 0, NULL, rval)) { - /* Check for StopIteration. */ - if (!cx->throwing || - JSVAL_IS_PRIMITIVE(cx->exception) || - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(cx->exception)) - != &js_StopIterationClass) { - return JS_FALSE; - } - - /* Inline JS_ClearPendingException(cx). */ - cx->throwing = JS_FALSE; - cx->exception = JSVAL_VOID; - *rval = JSVAL_HOLE; - return JS_TRUE; - } - } - - return JS_TRUE; -} - -static JSBool -stopiter_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - *bp = !JSVAL_IS_PRIMITIVE(v) && - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_StopIterationClass; - return JS_TRUE; -} - -JSClass js_StopIterationClass = { - js_StopIteration_str, - JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration), - JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, JS_FinalizeStub, - NULL, NULL, - NULL, NULL, - NULL, stopiter_hasInstance, - NULL, NULL -}; - -#if JS_HAS_GENERATORS - -static void -generator_finalize(JSContext *cx, JSObject *obj) -{ - JSGenerator *gen; - - gen = (JSGenerator *) JS_GetPrivate(cx, obj); - if (gen) { - /* - * gen can be open on shutdown when close hooks are ignored or when - * the embedding cancels scheduled close hooks. - */ - JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_CLOSED || - gen->state == JSGEN_OPEN); - JS_free(cx, gen); - } -} - -static uint32 -generator_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSGenerator *gen; - - gen = (JSGenerator *) JS_GetPrivate(cx, obj); - if (gen) { - /* - * We must mark argv[-2], as js_MarkStackFrame will not. Note that - * js_MarkStackFrame will mark thisp (argv[-1]) and actual arguments, - * plus any missing formals and local GC roots. - */ - JS_ASSERT(!JSVAL_IS_PRIMITIVE(gen->frame.argv[-2])); - GC_MARK(cx, JSVAL_TO_GCTHING(gen->frame.argv[-2]), "generator"); - js_MarkStackFrame(cx, &gen->frame); - } - return 0; -} - -JSClass js_GeneratorClass = { - js_Generator_str, - JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS | - JSCLASS_HAS_CACHED_PROTO(JSProto_Generator), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, generator_finalize, - NULL, NULL, NULL, NULL, - NULL, NULL, generator_mark, NULL -}; - -/* - * Called from the JSOP_GENERATOR case in the interpreter, with fp referring - * to the frame by which the generator function was activated. Create a new - * JSGenerator object, which contains its own JSStackFrame that we populate - * from *fp. We know that upon return, the JSOP_GENERATOR opcode will return - * from the activation in fp, so we can steal away fp->callobj and fp->argsobj - * if they are non-null. - */ -JSObject * -js_NewGenerator(JSContext *cx, JSStackFrame *fp) -{ - JSObject *obj; - uintN argc, nargs, nvars, depth, nslots; - JSGenerator *gen; - jsval *newsp; - - /* After the following return, failing control flow must goto bad. */ - obj = js_NewObject(cx, &js_GeneratorClass, NULL, NULL); - if (!obj) - return NULL; - - /* Load and compute stack slot counts. */ - argc = fp->argc; - nargs = JS_MAX(argc, fp->fun->nargs); - nvars = fp->nvars; - depth = fp->script->depth; - nslots = 2 + nargs + nvars + 2 * depth; - - /* Allocate obj's private data struct. */ - gen = (JSGenerator *) - JS_malloc(cx, sizeof(JSGenerator) + (nslots - 1) * sizeof(jsval)); - if (!gen) - goto bad; - - gen->obj = obj; - - /* Steal away objects reflecting fp and point them at gen->frame. */ - gen->frame.callobj = fp->callobj; - if (fp->callobj) { - JS_SetPrivate(cx, fp->callobj, &gen->frame); - fp->callobj = NULL; - } - gen->frame.argsobj = fp->argsobj; - if (fp->argsobj) { - JS_SetPrivate(cx, fp->argsobj, &gen->frame); - fp->argsobj = NULL; - } - - /* These two references can be shared with fp until it goes away. */ - gen->frame.varobj = fp->varobj; - gen->frame.thisp = fp->thisp; - - /* Copy call-invariant script and function references. */ - gen->frame.script = fp->script; - gen->frame.fun = fp->fun; - - /* Use newsp to carve space out of gen->stack. */ - newsp = gen->stack; - gen->arena.next = NULL; - gen->arena.base = (jsuword) newsp; - gen->arena.limit = gen->arena.avail = (jsuword) (newsp + nslots); - -#define COPY_STACK_ARRAY(vec,cnt,num) \ - JS_BEGIN_MACRO \ - gen->frame.cnt = cnt; \ - gen->frame.vec = newsp; \ - newsp += (num); \ - memcpy(gen->frame.vec, fp->vec, (num) * sizeof(jsval)); \ - JS_END_MACRO - - /* Copy argv, rval, and vars. */ - *newsp++ = fp->argv[-2]; - *newsp++ = fp->argv[-1]; - COPY_STACK_ARRAY(argv, argc, nargs); - gen->frame.rval = fp->rval; - COPY_STACK_ARRAY(vars, nvars, nvars); - -#undef COPY_STACK_ARRAY - - /* Initialize or copy virtual machine state. */ - gen->frame.down = NULL; - gen->frame.annotation = NULL; - gen->frame.scopeChain = fp->scopeChain; - gen->frame.pc = fp->pc; - - /* Allocate generating pc and operand stack space. */ - gen->frame.spbase = gen->frame.sp = newsp + depth; - - /* Copy remaining state (XXX sharp* and xml* should be local vars). */ - gen->frame.sharpDepth = 0; - gen->frame.sharpArray = NULL; - gen->frame.flags = fp->flags | JSFRAME_GENERATOR; - gen->frame.dormantNext = NULL; - gen->frame.xmlNamespace = NULL; - gen->frame.blockChain = NULL; - - /* Note that gen is newborn. */ - gen->state = JSGEN_NEWBORN; - - if (!JS_SetPrivate(cx, obj, gen)) { - JS_free(cx, gen); - goto bad; - } - - /* - * Register with GC to ensure that suspended finally blocks will be - * executed. - */ - js_RegisterGenerator(cx, gen); - return obj; - - bad: - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; -} - -typedef enum JSGeneratorOp { - JSGENOP_NEXT, - JSGENOP_SEND, - JSGENOP_THROW, - JSGENOP_CLOSE -} JSGeneratorOp; - -/* - * Start newborn or restart yielding generator and perform the requested - * operation inside its frame. - */ -static JSBool -SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj, - JSGenerator *gen, jsval arg, jsval *rval) -{ - JSStackFrame *fp; - jsval junk; - JSArena *arena; - JSBool ok; - - JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN); - switch (op) { - case JSGENOP_NEXT: - case JSGENOP_SEND: - if (gen->state == JSGEN_OPEN) { - /* - * Store the argument to send as the result of the yield - * expression. - */ - gen->frame.sp[-1] = arg; - } - gen->state = JSGEN_RUNNING; - break; - - case JSGENOP_THROW: - JS_SetPendingException(cx, arg); - gen->state = JSGEN_RUNNING; - break; - - default: - JS_ASSERT(op == JSGENOP_CLOSE); - JS_SetPendingException(cx, JSVAL_ARETURN); - gen->state = JSGEN_CLOSING; - break; - } - - /* Extend the current stack pool with gen->arena. */ - arena = cx->stackPool.current; - JS_ASSERT(!arena->next); - JS_ASSERT(!gen->arena.next); - JS_ASSERT(cx->stackPool.current != &gen->arena); - cx->stackPool.current = arena->next = &gen->arena; - - /* Push gen->frame around the interpreter activation. */ - fp = cx->fp; - cx->fp = &gen->frame; - gen->frame.down = fp; - ok = js_Interpret(cx, gen->frame.pc, &junk); - cx->fp = fp; - gen->frame.down = NULL; - - /* Retract the stack pool and sanitize gen->arena. */ - JS_ASSERT(!gen->arena.next); - JS_ASSERT(arena->next == &gen->arena); - JS_ASSERT(cx->stackPool.current == &gen->arena); - cx->stackPool.current = arena; - arena->next = NULL; - - if (gen->frame.flags & JSFRAME_YIELDING) { - /* Yield cannot fail, throw or be called on closing. */ - JS_ASSERT(ok); - JS_ASSERT(!cx->throwing); - JS_ASSERT(gen->state == JSGEN_RUNNING); - JS_ASSERT(op != JSGENOP_CLOSE); - gen->frame.flags &= ~JSFRAME_YIELDING; - gen->state = JSGEN_OPEN; - *rval = gen->frame.rval; - return JS_TRUE; - } - - gen->state = JSGEN_CLOSED; - - if (ok) { - /* Returned, explicitly or by falling off the end. */ - if (op == JSGENOP_CLOSE) - return JS_TRUE; - return js_ThrowStopIteration(cx, obj); - } - - /* - * An error, silent termination by branch callback or an exception. - * Propagate the condition to the caller. - */ - return JS_FALSE; -} - -/* - * Execute gen's close hook after the GC detects that the object has become - * unreachable. - */ -JSBool -js_CloseGeneratorObject(JSContext *cx, JSGenerator *gen) -{ - /* We pass null as rval since SendToGenerator never uses it with CLOSE. */ - return SendToGenerator(cx, JSGENOP_CLOSE, gen->obj, gen, JSVAL_VOID, NULL); -} - -/* - * Common subroutine of generator_(next|send|throw|close) methods. - */ -static JSBool -generator_op(JSContext *cx, JSGeneratorOp op, - JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSGenerator *gen; - JSString *str; - jsval arg; - - if (!JS_InstanceOf(cx, obj, &js_GeneratorClass, argv)) - return JS_FALSE; - - gen = (JSGenerator *) JS_GetPrivate(cx, obj); - if (gen == NULL) { - /* This happens when obj is the generator prototype. See bug 352885. */ - goto closed_generator; - } - - switch (gen->state) { - case JSGEN_NEWBORN: - switch (op) { - case JSGENOP_NEXT: - case JSGENOP_THROW: - break; - - case JSGENOP_SEND: - if (!JSVAL_IS_VOID(argv[0])) { - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, - argv[0], NULL); - if (str) { - JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_GENERATOR_SEND, - JSSTRING_CHARS(str)); - } - return JS_FALSE; - } - break; - - default: - JS_ASSERT(op == JSGENOP_CLOSE); - gen->state = JSGEN_CLOSED; - return JS_TRUE; - } - break; - - case JSGEN_OPEN: - break; - - case JSGEN_RUNNING: - case JSGEN_CLOSING: - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, argv[-1], - JS_GetFunctionId(gen->frame.fun)); - if (str) { - JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, - JSMSG_NESTING_GENERATOR, - JSSTRING_CHARS(str)); - } - return JS_FALSE; - - default: - JS_ASSERT(gen->state == JSGEN_CLOSED); - - closed_generator: - switch (op) { - case JSGENOP_NEXT: - case JSGENOP_SEND: - return js_ThrowStopIteration(cx, obj); - case JSGENOP_THROW: - JS_SetPendingException(cx, argv[0]); - return JS_FALSE; - default: - JS_ASSERT(op == JSGENOP_CLOSE); - return JS_TRUE; - } - } - - arg = (op == JSGENOP_SEND || op == JSGENOP_THROW) - ? argv[0] - : JSVAL_VOID; - if (!SendToGenerator(cx, op, obj, gen, arg, rval)) - return JS_FALSE; - return JS_TRUE; -} - -static JSBool -generator_send(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return generator_op(cx, JSGENOP_SEND, obj, argc, argv, rval); -} - -static JSBool -generator_next(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return generator_op(cx, JSGENOP_NEXT, obj, argc, argv, rval); -} - -static JSBool -generator_throw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return generator_op(cx, JSGENOP_THROW, obj, argc, argv, rval); -} - -static JSBool -generator_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return generator_op(cx, JSGENOP_CLOSE, obj, argc, argv, rval); -} - -static JSFunctionSpec generator_methods[] = { - {js_iterator_str, iterator_self, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {js_next_str, generator_next, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {js_send_str, generator_send, 1,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {js_throw_str, generator_throw, 1,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {js_close_str, generator_close, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {0,0,0,0,0} -}; - -#endif /* JS_HAS_GENERATORS */ - -JSObject * -js_InitIteratorClasses(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *stop; - - /* Idempotency required: we initialize several things, possibly lazily. */ - if (!js_GetClassObject(cx, obj, JSProto_StopIteration, &stop)) - return NULL; - if (stop) - return stop; - - proto = JS_InitClass(cx, obj, NULL, &js_IteratorClass, Iterator, 2, - NULL, iterator_methods, NULL, NULL); - if (!proto) - return NULL; - proto->slots[JSSLOT_ITER_STATE] = JSVAL_NULL; - -#if JS_HAS_GENERATORS - /* Initialize the generator internals if configured. */ - if (!JS_InitClass(cx, obj, NULL, &js_GeneratorClass, NULL, 0, - NULL, generator_methods, NULL, NULL)) { - return NULL; - } -#endif - - return JS_InitClass(cx, obj, NULL, &js_StopIterationClass, NULL, 0, - NULL, NULL, NULL, NULL); -} diff --git a/spidermonkey/src/jsiter.h b/spidermonkey/src/jsiter.h deleted file mode 100644 index 1a99b6b..0000000 --- a/spidermonkey/src/jsiter.h +++ /dev/null @@ -1,114 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsiter_h___ -#define jsiter_h___ -/* - * JavaScript iterators. - */ -#include "jsprvtd.h" -#include "jspubtd.h" - -#define JSITER_ENUMERATE 0x1 /* for-in compatible hidden default iterator */ -#define JSITER_FOREACH 0x2 /* return [key, value] pair rather than key */ -#define JSITER_KEYVALUE 0x4 /* destructuring for-in wants [key, value] */ - -extern void -js_CloseNativeIterator(JSContext *cx, JSObject *iterobj); - -extern void -js_CloseIteratorState(JSContext *cx, JSObject *iterobj); - -/* - * Convert the value stored in *vp to its iteration object. The flags should - * contain JSITER_ENUMERATE if js_ValueToIterator is called when enumerating - * for-in semantics are required, and when the caller can guarantee that the - * iterator will never be exposed to scripts. - */ -extern JSBool -js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp); - -/* - * Given iterobj, call iterobj.next(). If the iterator stopped, set *rval to - * JSVAL_HOLE. Otherwise set it to the result of the next call. - */ -extern JSBool -js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval); - -#if JS_HAS_GENERATORS - -/* - * Generator state codes. - */ -typedef enum JSGeneratorState { - JSGEN_NEWBORN, /* not yet started */ - JSGEN_OPEN, /* started by a .next() or .send(undefined) call */ - JSGEN_RUNNING, /* currently executing via .next(), etc., call */ - JSGEN_CLOSING, /* close method is doing asynchronous return */ - JSGEN_CLOSED /* closed, cannot be started or closed again */ -} JSGeneratorState; - -struct JSGenerator { - JSGenerator *next; - JSObject *obj; - JSGeneratorState state; - JSStackFrame frame; - JSArena arena; - jsval stack[1]; -}; - -#define FRAME_TO_GENERATOR(fp) \ - ((JSGenerator *) ((uint8 *)(fp) - offsetof(JSGenerator, frame))) - -extern JSObject * -js_NewGenerator(JSContext *cx, JSStackFrame *fp); - -extern JSBool -js_CloseGeneratorObject(JSContext *cx, JSGenerator *gen); - -#endif - -extern JSClass js_GeneratorClass; -extern JSClass js_IteratorClass; -extern JSClass js_StopIterationClass; - -extern JSObject * -js_InitIteratorClasses(JSContext *cx, JSObject *obj); - -#endif /* jsiter_h___ */ diff --git a/spidermonkey/src/jskeyword.tbl b/spidermonkey/src/jskeyword.tbl deleted file mode 100644 index 49b9c6c..0000000 --- a/spidermonkey/src/jskeyword.tbl +++ /dev/null @@ -1,124 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set sw=4 ts=8 et tw=80: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -JS_KEYWORD(break, TOK_BREAK, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(case, TOK_CASE, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(continue, TOK_CONTINUE, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(default, TOK_DEFAULT, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(delete, TOK_DELETE, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(do, TOK_DO, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(else, TOK_ELSE, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(export, TOK_EXPORT, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(false, TOK_PRIMARY, JSOP_FALSE, JSVERSION_DEFAULT) -JS_KEYWORD(for, TOK_FOR, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(function, TOK_FUNCTION, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(if, TOK_IF, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(in, TOK_IN, JSOP_IN, JSVERSION_DEFAULT) -JS_KEYWORD(new, TOK_NEW, JSOP_NEW, JSVERSION_DEFAULT) -JS_KEYWORD(null, TOK_PRIMARY, JSOP_NULL, JSVERSION_DEFAULT) -JS_KEYWORD(return, TOK_RETURN, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(switch, TOK_SWITCH, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(this, TOK_PRIMARY, JSOP_THIS, JSVERSION_DEFAULT) -JS_KEYWORD(true, TOK_PRIMARY, JSOP_TRUE, JSVERSION_DEFAULT) -JS_KEYWORD(typeof, TOK_UNARYOP, JSOP_TYPEOF, JSVERSION_DEFAULT) -JS_KEYWORD(var, TOK_VAR, JSOP_DEFVAR, JSVERSION_DEFAULT) -JS_KEYWORD(void, TOK_UNARYOP, JSOP_VOID, JSVERSION_DEFAULT) -JS_KEYWORD(while, TOK_WHILE, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(with, TOK_WITH, JSOP_NOP, JSVERSION_DEFAULT) -#if JS_HAS_CONST -JS_KEYWORD(const, TOK_VAR, JSOP_DEFCONST, JSVERSION_DEFAULT) -#else -JS_KEYWORD(const, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -#endif - -JS_KEYWORD(try, TOK_TRY, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(catch, TOK_CATCH, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(finally, TOK_FINALLY, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(throw, TOK_THROW, JSOP_NOP, JSVERSION_DEFAULT) - -JS_KEYWORD(instanceof, TOK_INSTANCEOF, JSOP_INSTANCEOF,JSVERSION_DEFAULT) - -#if JS_HAS_RESERVED_JAVA_KEYWORDS -JS_KEYWORD(abstract, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(boolean, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(byte, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(char, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(class, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(double, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(extends, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(final, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(float, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(goto, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(implements, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(import, TOK_IMPORT, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(int, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(interface, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(long, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(native, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(package, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(private, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(protected, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(public, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(short, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(static, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(super, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(synchronized,TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(throws, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(transient, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(volatile, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -#endif - -#if JS_HAS_RESERVED_ECMA_KEYWORDS -JS_KEYWORD(enum, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -#endif - -#if JS_HAS_DEBUGGER_KEYWORD -JS_KEYWORD(debugger, TOK_DEBUGGER, JSOP_NOP, JSVERSION_DEFAULT) -#elif JS_HAS_RESERVED_ECMA_KEYWORDS -JS_KEYWORD(debugger, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -#endif - -#if JS_HAS_GENERATORS -JS_KEYWORD(yield, TOK_YIELD, JSOP_NOP, JSVERSION_1_7) -#endif - -#if JS_HAS_BLOCK_SCOPE -JS_KEYWORD(let, TOK_LET, JSOP_NOP, JSVERSION_1_7) -#endif diff --git a/spidermonkey/src/jskwgen.c b/spidermonkey/src/jskwgen.c deleted file mode 100644 index 5ae39bd..0000000 --- a/spidermonkey/src/jskwgen.c +++ /dev/null @@ -1,460 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set sw=4 ts=8 et tw=80: - * - * ***** 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 String Switch Generator for JavaScript Keywords, - * released 2005-12-09. - * - * The Initial Developer of the Original Code is - * Igor Bukanov. - * Portions created by the Initial Developer are Copyright (C) 2005-2006 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of 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 "jsstddef.h" -#include -#include -#include -#include -#include -#include - -#include "jsconfig.h" - -const char * const keyword_list[] = { -#define JS_KEYWORD(keyword, type, op, version) #keyword, -#include "jskeyword.tbl" -#undef JS_KEYWORD -}; - -struct gen_opt { - FILE *output; /* output file for generated source */ - unsigned use_if_threshold; /* max number of choices to generate - "if" selector instead of "switch" */ - unsigned char_tail_test_threshold; /* max number of unprocessed columns - to use inlined char compare - for remaining chars and not generic - string compare code */ - unsigned indent_level; /* current source identation level */ -}; - -static unsigned column_to_compare; - -static int -length_comparator(const void *a, const void *b) -{ - const char *str1 = keyword_list[*(unsigned *)a]; - const char *str2 = keyword_list[*(unsigned *)b]; - return (int)strlen(str1) - (int)strlen(str2); -} - -static int -column_comparator(const void *a, const void *b) -{ - const char *str1 = keyword_list[*(unsigned *)a]; - const char *str2 = keyword_list[*(unsigned *)b]; - return (int)str1[column_to_compare] - (int)str2[column_to_compare]; -} - -static unsigned -count_different_lengths(unsigned indexes[], unsigned nelem) -{ - unsigned nlength, current_length, i, l; - - current_length = 0; - nlength = 0; - for (i = 0; i != nelem; ++i) { - l = (unsigned)strlen(keyword_list[indexes[i]]); - assert(l != 0); - if (current_length != l) { - ++nlength; - current_length = l; - } - } - return nlength; -} - -static void -find_char_span_and_count(unsigned indexes[], unsigned nelem, unsigned column, - unsigned *span_result, unsigned *count_result) -{ - unsigned i, count; - unsigned char c, prev, minc, maxc; - - assert(nelem != 0); - minc = maxc = prev = (unsigned char)keyword_list[indexes[0]][column]; - count = 1; - for (i = 1; i != nelem; ++i) { - c = (unsigned char)keyword_list[indexes[i]][column]; - if (prev != c) { - prev = c; - ++count; - if (minc > c) { - minc = c; - } else if (maxc < c) { - maxc = c; - } - } - } - - *span_result = maxc - minc + 1; - *count_result = count; -} - -static unsigned -find_optimal_switch_column(struct gen_opt *opt, - unsigned indexes[], unsigned nelem, - unsigned columns[], unsigned unprocessed_columns, - int *use_if_result) -{ - unsigned i; - unsigned span, min_span, min_span_index; - unsigned nchar, min_nchar, min_nchar_index; - - assert(unprocessed_columns != 0); - i = 0; - min_nchar = min_span = (unsigned)-1; - min_nchar_index = min_span_index = 0; - do { - column_to_compare = columns[i]; - qsort(indexes, nelem, sizeof(indexes[0]), column_comparator); - find_char_span_and_count(indexes, nelem, column_to_compare, - &span, &nchar); - assert(span != 0); - if (span == 1) { - assert(nchar == 1); - *use_if_result = 1; - return 1; - } - assert(nchar != 1); - if (min_span > span) { - min_span = span; - min_span_index = i; - } - if (min_nchar > nchar) { - min_nchar = nchar; - min_nchar_index = i; - } - } while (++i != unprocessed_columns); - - if (min_nchar <= opt->use_if_threshold) { - *use_if_result = 1; - i = min_nchar_index; - } else { - *use_if_result = 0; - i = min_span_index; - } - - /* - * Restore order corresponding to i if it was destroyed by - * subsequent sort. - */ - if (i != unprocessed_columns - 1) { - column_to_compare = columns[i]; - qsort(indexes, nelem, sizeof(indexes[0]), column_comparator); - } - - return i; -} - - -static void -p(struct gen_opt *opt, const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - vfprintf(opt->output, format, ap); - va_end(ap); -} - -/* Size for '\xxx' where xxx is octal escape */ -#define MIN_QUOTED_CHAR_BUFFER 7 - -static char * -qchar(char c, char *quoted_buffer) -{ - char *s; - - s = quoted_buffer; - *s++ = '\''; - switch (c) { - case '\n': c = 'n'; goto one_char_escape; - case '\r': c = 'r'; goto one_char_escape; - case '\t': c = 't'; goto one_char_escape; - case '\f': c = 't'; goto one_char_escape; - case '\0': c = '0'; goto one_char_escape; - case '\'': goto one_char_escape; - one_char_escape: - *s++ = '\\'; - break; - default: - if (!isprint(c)) { - *s++ = '\\'; - *s++ = (char)('0' + (0x3 & (((unsigned char)c) >> 6))); - *s++ = (char)('0' + (0x7 & (((unsigned char)c) >> 3))); - c = (char)('0' + (0x7 & ((unsigned char)c))); - } - } - *s++ = c; - *s++ = '\''; - *s = '\0'; - assert(s + 1 <= quoted_buffer + MIN_QUOTED_CHAR_BUFFER); - return quoted_buffer; -} - -static void -nl(struct gen_opt *opt) -{ - putc('\n', opt->output); -} - -static void -indent(struct gen_opt *opt) -{ - unsigned n = opt->indent_level; - while (n != 0) { - --n; - fputs(" ", opt->output); - } -} - -static void -line(struct gen_opt *opt, const char *format, ...) -{ - va_list ap; - - indent(opt); - va_start(ap, format); - vfprintf(opt->output, format, ap); - va_end(ap); - nl(opt); -} - -static void -generate_letter_switch_r(struct gen_opt *opt, - unsigned indexes[], unsigned nelem, - unsigned columns[], unsigned unprocessed_columns) -{ - char qbuf[MIN_QUOTED_CHAR_BUFFER]; - - assert(nelem != 0); - if (nelem == 1) { - unsigned kw_index = indexes[0]; - const char *keyword = keyword_list[kw_index]; - - if (unprocessed_columns == 0) { - line(opt, "JSKW_GOT_MATCH(%u) /* %s */", kw_index, keyword); - } else if (unprocessed_columns > opt->char_tail_test_threshold) { - line(opt, "JSKW_TEST_GUESS(%u) /* %s */", kw_index, keyword); - } else { - unsigned i, column; - - indent(opt); p(opt, "if ("); - for (i = 0; i != unprocessed_columns; ++i) { - column = columns[i]; - qchar(keyword[column], qbuf); - p(opt, "%sJSKW_AT(%u)==%s", (i == 0) ? "" : " && ", - column, qbuf); - } - p(opt, ") {"); nl(opt); - ++opt->indent_level; - line(opt, "JSKW_GOT_MATCH(%u) /* %s */", kw_index, keyword); - --opt->indent_level; - line(opt, "}"); - line(opt, "JSKW_NO_MATCH()"); - } - } else { - unsigned optimal_column_index, optimal_column; - unsigned i; - int use_if; - char current; - - assert(unprocessed_columns != 0); - optimal_column_index = find_optimal_switch_column(opt, indexes, nelem, - columns, - unprocessed_columns, - &use_if); - optimal_column = columns[optimal_column_index]; - columns[optimal_column_index] = columns[unprocessed_columns - 1]; - - if (!use_if) - line(opt, "switch (JSKW_AT(%u)) {", optimal_column); - - current = keyword_list[indexes[0]][optimal_column]; - for (i = 0; i != nelem;) { - unsigned same_char_begin = i; - char next = current; - - for (++i; i != nelem; ++i) { - next = keyword_list[indexes[i]][optimal_column]; - if (next != current) - break; - } - qchar(current, qbuf); - if (use_if) { - line(opt, "if (JSKW_AT(%u) == %s) {", optimal_column, qbuf); - } else { - line(opt, " case %s:", qbuf); - } - ++opt->indent_level; - generate_letter_switch_r(opt, indexes + same_char_begin, - i - same_char_begin, - columns, unprocessed_columns - 1); - --opt->indent_level; - if (use_if) { - line(opt, "}"); - } - current = next; - } - - if (!use_if) { - line(opt, "}"); - } - - columns[optimal_column_index] = optimal_column; - - line(opt, "JSKW_NO_MATCH()"); - } -} - -static void -generate_letter_switch(struct gen_opt *opt, - unsigned indexes[], unsigned nelem, - unsigned current_length) -{ - unsigned *columns; - unsigned i; - - columns = malloc(sizeof(columns[0]) * current_length); - if (!columns) { - perror("malloc"); - exit(EXIT_FAILURE); - } - for (i = 0; i != current_length; ++i) { - columns[i] = i; - } - generate_letter_switch_r(opt, indexes, nelem, columns, current_length); - free(columns); -} - - -static void -generate_switch(struct gen_opt *opt) -{ - unsigned *indexes; - unsigned nlength; - unsigned i, current; - int use_if; - unsigned nelem; - - nelem = sizeof(keyword_list)/sizeof(keyword_list[0]); - - line(opt, "/*"); - line(opt, " * Generating switch for the list of %u entries:", nelem); - for (i = 0; i != nelem; ++i) { - line(opt, " * %s", keyword_list[i]); - } - line(opt, " */"); - - indexes = malloc(sizeof(indexes[0]) * nelem); - if (!indexes) { - perror("malloc"); - exit(EXIT_FAILURE); - } - for (i = 0; i != nelem; ++i) - indexes[i] = i; - qsort(indexes, nelem, sizeof(indexes[i]), length_comparator); - nlength = count_different_lengths(indexes, nelem); - - use_if = (nlength <= opt->use_if_threshold); - - if (!use_if) - line(opt, "switch (JSKW_LENGTH()) {"); - - current = (unsigned)strlen(keyword_list[indexes[0]]); - for (i = 0; i != nelem;) { - unsigned same_length_begin = i; - unsigned next = current; - - for (++i; i != nelem; ++i) { - next = (unsigned)strlen(keyword_list[indexes[i]]); - if (next != current) - break; - } - if (use_if) { - line(opt, "if (JSKW_LENGTH() == %u) {", current); - } else { - line(opt, " case %u:", current); - } - ++opt->indent_level; - generate_letter_switch(opt, indexes + same_length_begin, - i - same_length_begin, - current); - --opt->indent_level; - if (use_if) { - line(opt, "}"); - } - current = next; - } - if (!use_if) - line(opt, "}"); - line(opt, "JSKW_NO_MATCH()"); - free(indexes); -} - -int main(int argc, char **argv) -{ - struct gen_opt opt; - - if (argc < 2) { - opt.output = stdout; - } else { - opt.output = fopen(argv[1], "w"); - if (!opt.output) { - perror("fopen"); - exit(EXIT_FAILURE); - } - } - opt.indent_level = 1; - opt.use_if_threshold = 3; - opt.char_tail_test_threshold = 4; - - generate_switch(&opt); - - if (opt.output != stdout) { - if (fclose(opt.output)) { - perror("fclose"); - exit(EXIT_FAILURE); - } - } - - return EXIT_SUCCESS; -} diff --git a/spidermonkey/src/jslibmath.h b/spidermonkey/src/jslibmath.h deleted file mode 100644 index 3f75f30..0000000 --- a/spidermonkey/src/jslibmath.h +++ /dev/null @@ -1,266 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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): - * IBM Corp. - * - * Alternatively, the contents of this file may be used under the terms of - * either of 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 ***** */ - -/* - * By default all math calls go to fdlibm. The defines for each platform - * remap the math calls to native routines. - */ - -#ifndef _LIBMATH_H -#define _LIBMATH_H - -#include -#include "jsconfig.h" - -/* - * Define on which platforms to use fdlibm. Not used by default under - * assumption that native math library works unless proved guilty. - * Plus there can be problems with endian-ness and such in fdlibm itself. - * - * fdlibm compatibility notes: - * - fdlibm broken on OSF1/alpha - */ - -#ifndef JS_USE_FDLIBM_MATH -#define JS_USE_FDLIBM_MATH 0 -#endif - -#if !JS_USE_FDLIBM_MATH - -/* - * Use system provided math routines. - */ - -#define fd_acos acos -#define fd_asin asin -#define fd_atan atan -#define fd_atan2 atan2 -#define fd_ceil ceil - -/* The right copysign function is not always named the same thing. */ -#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) -#define fd_copysign __builtin_copysign -#elif defined WINCE -#define fd_copysign _copysign -#elif defined _WIN32 -#if _MSC_VER < 1400 -/* Try to work around apparent _copysign bustage in VC6 and VC7. */ -#define fd_copysign js_copysign -extern double js_copysign(double, double); -#else -#define fd_copysign _copysign -#endif -#else -#define fd_copysign copysign -#endif - -#define fd_cos cos -#define fd_exp exp -#define fd_fabs fabs -#define fd_floor floor -#define fd_fmod fmod -#define fd_log log -#define fd_pow pow -#define fd_sin sin -#define fd_sqrt sqrt -#define fd_tan tan - -#else - -/* - * Use math routines in fdlibm. - */ - -#undef __P -#ifdef __STDC__ -#define __P(p) p -#else -#define __P(p) () -#endif - -#if (defined _WIN32 && !defined WINCE) || defined SUNOS4 - -#define fd_acos acos -#define fd_asin asin -#define fd_atan atan -#define fd_cos cos -#define fd_sin sin -#define fd_tan tan -#define fd_exp exp -#define fd_log log -#define fd_sqrt sqrt -#define fd_ceil ceil -#define fd_fabs fabs -#define fd_floor floor -#define fd_fmod fmod - -extern double fd_atan2 __P((double, double)); -extern double fd_copysign __P((double, double)); -extern double fd_pow __P((double, double)); - -#elif defined IRIX - -#define fd_acos acos -#define fd_asin asin -#define fd_atan atan -#define fd_exp exp -#define fd_log log -#define fd_log10 log10 -#define fd_sqrt sqrt -#define fd_fabs fabs -#define fd_floor floor -#define fd_fmod fmod - -extern double fd_cos __P((double)); -extern double fd_sin __P((double)); -extern double fd_tan __P((double)); -extern double fd_atan2 __P((double, double)); -extern double fd_pow __P((double, double)); -extern double fd_ceil __P((double)); -extern double fd_copysign __P((double, double)); - -#elif defined SOLARIS - -#define fd_atan atan -#define fd_cos cos -#define fd_sin sin -#define fd_tan tan -#define fd_exp exp -#define fd_sqrt sqrt -#define fd_ceil ceil -#define fd_fabs fabs -#define fd_floor floor -#define fd_fmod fmod - -extern double fd_acos __P((double)); -extern double fd_asin __P((double)); -extern double fd_log __P((double)); -extern double fd_atan2 __P((double, double)); -extern double fd_pow __P((double, double)); -extern double fd_copysign __P((double, double)); - -#elif defined HPUX - -#define fd_cos cos -#define fd_sin sin -#define fd_exp exp -#define fd_sqrt sqrt -#define fd_fabs fabs -#define fd_floor floor -#define fd_fmod fmod - -extern double fd_ceil __P((double)); -extern double fd_acos __P((double)); -extern double fd_log __P((double)); -extern double fd_atan2 __P((double, double)); -extern double fd_tan __P((double)); -extern double fd_pow __P((double, double)); -extern double fd_asin __P((double)); -extern double fd_atan __P((double)); -extern double fd_copysign __P((double, double)); - -#elif defined(OSF1) - -#define fd_acos acos -#define fd_asin asin -#define fd_atan atan -#define fd_copysign copysign -#define fd_cos cos -#define fd_exp exp -#define fd_fabs fabs -#define fd_fmod fmod -#define fd_sin sin -#define fd_sqrt sqrt -#define fd_tan tan - -extern double fd_atan2 __P((double, double)); -extern double fd_ceil __P((double)); -extern double fd_floor __P((double)); -extern double fd_log __P((double)); -extern double fd_pow __P((double, double)); - -#elif defined(AIX) - -#define fd_acos acos -#define fd_asin asin -#define fd_atan2 atan2 -#define fd_copysign copysign -#define fd_cos cos -#define fd_exp exp -#define fd_fabs fabs -#define fd_floor floor -#define fd_fmod fmod -#define fd_log log -#define fd_sin sin -#define fd_sqrt sqrt - -extern double fd_atan __P((double)); -extern double fd_ceil __P((double)); -extern double fd_pow __P((double,double)); -extern double fd_tan __P((double)); - -#else /* other platform.. generic paranoid slow fdlibm */ - -extern double fd_acos __P((double)); -extern double fd_asin __P((double)); -extern double fd_atan __P((double)); -extern double fd_cos __P((double)); -extern double fd_sin __P((double)); -extern double fd_tan __P((double)); - -extern double fd_exp __P((double)); -extern double fd_log __P((double)); -extern double fd_sqrt __P((double)); - -extern double fd_ceil __P((double)); -extern double fd_fabs __P((double)); -extern double fd_floor __P((double)); -extern double fd_fmod __P((double, double)); - -extern double fd_atan2 __P((double, double)); -extern double fd_pow __P((double, double)); -extern double fd_copysign __P((double, double)); - -#endif - -#endif /* JS_USE_FDLIBM_MATH */ - -#endif /* _LIBMATH_H */ - diff --git a/spidermonkey/src/jslock.c b/spidermonkey/src/jslock.c deleted file mode 100644 index 48550099..0000000 --- a/spidermonkey/src/jslock.c +++ /dev/null @@ -1,1303 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifdef JS_THREADSAFE - -/* - * JS locking stubs. - */ -#include "jsstddef.h" -#include -#include "jspubtd.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jstypes.h" -#include "jsbit.h" -#include "jscntxt.h" -#include "jsdtoa.h" -#include "jsgc.h" -#include "jslock.h" -#include "jsscope.h" -#include "jsstr.h" - -#define ReadWord(W) (W) - -#ifndef NSPR_LOCK - -#include - -static PRLock **global_locks; -static uint32 global_lock_count = 1; -static uint32 global_locks_log2 = 0; -static uint32 global_locks_mask = 0; - -#define GLOBAL_LOCK_INDEX(id) (((uint32)(id) >> 2) & global_locks_mask) - -static void -js_LockGlobal(void *id) -{ - uint32 i = GLOBAL_LOCK_INDEX(id); - PR_Lock(global_locks[i]); -} - -static void -js_UnlockGlobal(void *id) -{ - uint32 i = GLOBAL_LOCK_INDEX(id); - PR_Unlock(global_locks[i]); -} - -/* Exclude Alpha NT. */ -#if defined(_WIN32) && defined(_M_IX86) -#pragma warning( disable : 4035 ) - -static JS_INLINE int -js_CompareAndSwap(jsword *w, jsword ov, jsword nv) -{ - __asm { - mov eax, ov - mov ecx, nv - mov ebx, w - lock cmpxchg [ebx], ecx - sete al - and eax, 1h - } -} - -#elif defined(__GNUC__) && defined(__i386__) - -/* Note: This fails on 386 cpus, cmpxchgl is a >= 486 instruction */ -static JS_INLINE int -js_CompareAndSwap(jsword *w, jsword ov, jsword nv) -{ - unsigned int res; - - __asm__ __volatile__ ( - "lock\n" - "cmpxchgl %2, (%1)\n" - "sete %%al\n" - "andl $1, %%eax\n" - : "=a" (res) - : "r" (w), "r" (nv), "a" (ov) - : "cc", "memory"); - return (int)res; -} - -#elif (defined(__USLC__) || defined(_SCO_DS)) && defined(i386) - -/* Note: This fails on 386 cpus, cmpxchgl is a >= 486 instruction */ - -asm int -js_CompareAndSwap(jsword *w, jsword ov, jsword nv) -{ -%ureg w, nv; - movl ov,%eax - lock - cmpxchgl nv,(w) - sete %al - andl $1,%eax -%ureg w; mem ov, nv; - movl ov,%eax - movl nv,%ecx - lock - cmpxchgl %ecx,(w) - sete %al - andl $1,%eax -%ureg nv; - movl ov,%eax - movl w,%edx - lock - cmpxchgl nv,(%edx) - sete %al - andl $1,%eax -%mem w, ov, nv; - movl ov,%eax - movl nv,%ecx - movl w,%edx - lock - cmpxchgl %ecx,(%edx) - sete %al - andl $1,%eax -} -#pragma asm full_optimization js_CompareAndSwap - -#elif defined(SOLARIS) && defined(sparc) && defined(ULTRA_SPARC) - -static JS_INLINE int -js_CompareAndSwap(jsword *w, jsword ov, jsword nv) -{ -#if defined(__GNUC__) - unsigned int res; - JS_ASSERT(ov != nv); - asm volatile ("\ -stbar\n\ -cas [%1],%2,%3\n\ -cmp %2,%3\n\ -be,a 1f\n\ -mov 1,%0\n\ -mov 0,%0\n\ -1:" - : "=r" (res) - : "r" (w), "r" (ov), "r" (nv)); - return (int)res; -#else /* !__GNUC__ */ - extern int compare_and_swap(jsword*, jsword, jsword); - JS_ASSERT(ov != nv); - return compare_and_swap(w, ov, nv); -#endif -} - -#elif defined(AIX) - -#include - -static JS_INLINE int -js_CompareAndSwap(jsword *w, jsword ov, jsword nv) -{ - return !_check_lock((atomic_p)w, ov, nv); -} - -#else - -#error "Define NSPR_LOCK if your platform lacks a compare-and-swap instruction." - -#endif /* arch-tests */ - -#endif /* !NSPR_LOCK */ - -void -js_InitLock(JSThinLock *tl) -{ -#ifdef NSPR_LOCK - tl->owner = 0; - tl->fat = (JSFatLock*)JS_NEW_LOCK(); -#else - memset(tl, 0, sizeof(JSThinLock)); -#endif -} - -void -js_FinishLock(JSThinLock *tl) -{ -#ifdef NSPR_LOCK - tl->owner = 0xdeadbeef; - if (tl->fat) - JS_DESTROY_LOCK(((JSLock*)tl->fat)); -#else - JS_ASSERT(tl->owner == 0); - JS_ASSERT(tl->fat == NULL); -#endif -} - -static void js_Dequeue(JSThinLock *); - -#ifdef DEBUG_SCOPE_COUNT - -#include -#include "jsdhash.h" - -static FILE *logfp; -static JSDHashTable logtbl; - -typedef struct logentry { - JSDHashEntryStub stub; - char op; - const char *file; - int line; -} logentry; - -static void -logit(JSScope *scope, char op, const char *file, int line) -{ - logentry *entry; - - if (!logfp) { - logfp = fopen("/tmp/scope.log", "w"); - if (!logfp) - return; - setvbuf(logfp, NULL, _IONBF, 0); - } - fprintf(logfp, "%p %c %s %d\n", scope, op, file, line); - - if (!logtbl.entryStore && - !JS_DHashTableInit(&logtbl, JS_DHashGetStubOps(), NULL, - sizeof(logentry), 100)) { - return; - } - entry = (logentry *) JS_DHashTableOperate(&logtbl, scope, JS_DHASH_ADD); - if (!entry) - return; - entry->stub.key = scope; - entry->op = op; - entry->file = file; - entry->line = line; -} - -void -js_unlog_scope(JSScope *scope) -{ - if (!logtbl.entryStore) - return; - (void) JS_DHashTableOperate(&logtbl, scope, JS_DHASH_REMOVE); -} - -# define LOGIT(scope,op) logit(scope, op, __FILE__, __LINE__) - -#else - -# define LOGIT(scope,op) /* nothing */ - -#endif /* DEBUG_SCOPE_COUNT */ - -/* - * Return true if scope's ownercx, or the ownercx of a single-threaded scope - * for which ownercx is waiting to become multi-threaded and shared, is cx. - * That condition implies deadlock in ClaimScope if cx's thread were to wait - * to share scope. - * - * (i) rt->gcLock held - */ -static JSBool -WillDeadlock(JSScope *scope, JSContext *cx) -{ - JSContext *ownercx; - - do { - ownercx = scope->ownercx; - if (ownercx == cx) { - JS_RUNTIME_METER(cx->runtime, deadlocksAvoided); - return JS_TRUE; - } - } while (ownercx && (scope = ownercx->scopeToShare) != NULL); - return JS_FALSE; -} - -/* - * Make scope multi-threaded, i.e. share its ownership among contexts in rt - * using a "thin" or (if necessary due to contention) "fat" lock. Called only - * from ClaimScope, immediately below, when we detect deadlock were we to wait - * for scope's lock, because its ownercx is waiting on a scope owned by the - * calling cx. - * - * (i) rt->gcLock held - */ -static void -ShareScope(JSRuntime *rt, JSScope *scope) -{ - JSScope **todop; - - if (scope->u.link) { - for (todop = &rt->scopeSharingTodo; *todop != scope; - todop = &(*todop)->u.link) { - JS_ASSERT(*todop != NO_SCOPE_SHARING_TODO); - } - *todop = scope->u.link; - scope->u.link = NULL; /* null u.link for sanity ASAP */ - JS_NOTIFY_ALL_CONDVAR(rt->scopeSharingDone); - } - js_InitLock(&scope->lock); - if (scope == rt->setSlotScope) { - /* - * Nesting locks on another thread that's using scope->ownercx: give - * the held lock a reentrancy count of 1 and set its lock.owner field - * directly (no compare-and-swap needed while scope->ownercx is still - * non-null). See below in ClaimScope, before the ShareScope call, - * for more on why this is necessary. - * - * If NSPR_LOCK is defined, we cannot deadlock holding rt->gcLock and - * acquiring scope->lock.fat here, against another thread holding that - * fat lock and trying to grab rt->gcLock. This is because no other - * thread can attempt to acquire scope->lock.fat until scope->ownercx - * is null *and* our thread has released rt->gcLock, which interlocks - * scope->ownercx's transition to null against tests of that member - * in ClaimScope. - */ - scope->lock.owner = CX_THINLOCK_ID(scope->ownercx); -#ifdef NSPR_LOCK - JS_ACQUIRE_LOCK((JSLock*)scope->lock.fat); -#endif - scope->u.count = 1; - } else { - scope->u.count = 0; - } - js_FinishSharingScope(rt, scope); -} - -/* - * js_FinishSharingScope is the tail part of ShareScope, split out to become a - * subroutine of JS_EndRequest too. The bulk of the work here involves making - * mutable strings in the scope's object's slots be immutable. We have to do - * this because such strings will soon be available to multiple threads, so - * their buffers can't be realloc'd any longer in js_ConcatStrings, and their - * members can't be modified by js_ConcatStrings, js_MinimizeDependentStrings, - * or js_UndependString. - * - * The last bit of work done by js_FinishSharingScope nulls scope->ownercx and - * updates rt->sharedScopes. - */ -#define MAKE_STRING_IMMUTABLE(rt, v, vp) \ - JS_BEGIN_MACRO \ - JSString *str_ = JSVAL_TO_STRING(v); \ - uint8 *flagp_ = js_GetGCThingFlags(str_); \ - if (*flagp_ & GCF_MUTABLE) { \ - if (JSSTRING_IS_DEPENDENT(str_) && \ - !js_UndependString(NULL, str_)) { \ - JS_RUNTIME_METER(rt, badUndependStrings); \ - *vp = JSVAL_VOID; \ - } else { \ - *flagp_ &= ~GCF_MUTABLE; \ - } \ - } \ - JS_END_MACRO - -void -js_FinishSharingScope(JSRuntime *rt, JSScope *scope) -{ - JSObject *obj; - uint32 nslots; - jsval v, *vp, *end; - - obj = scope->object; - nslots = JS_MIN(obj->map->freeslot, obj->map->nslots); - for (vp = obj->slots, end = vp + nslots; vp < end; vp++) { - v = *vp; - if (JSVAL_IS_STRING(v)) - MAKE_STRING_IMMUTABLE(rt, v, vp); - } - - scope->ownercx = NULL; /* NB: set last, after lock init */ - JS_RUNTIME_METER(rt, sharedScopes); -} - -/* - * Given a scope with apparently non-null ownercx different from cx, try to - * set ownercx to cx, claiming exclusive (single-threaded) ownership of scope. - * If we claim ownership, return true. Otherwise, we wait for ownercx to be - * set to null (indicating that scope is multi-threaded); or if waiting would - * deadlock, we set ownercx to null ourselves via ShareScope. In any case, - * once ownercx is null we return false. - */ -static JSBool -ClaimScope(JSScope *scope, JSContext *cx) -{ - JSRuntime *rt; - JSContext *ownercx; - jsrefcount saveDepth; - PRStatus stat; - - rt = cx->runtime; - JS_RUNTIME_METER(rt, claimAttempts); - JS_LOCK_GC(rt); - - /* Reload in case ownercx went away while we blocked on the lock. */ - while ((ownercx = scope->ownercx) != NULL) { - /* - * Avoid selflock if ownercx is dead, or is not running a request, or - * has the same thread as cx. Set scope->ownercx to cx so that the - * matching JS_UNLOCK_SCOPE or JS_UNLOCK_OBJ macro call will take the - * fast path around the corresponding js_UnlockScope or js_UnlockObj - * function call. - * - * If scope->u.link is non-null, scope has already been inserted on - * the rt->scopeSharingTodo list, because another thread's context - * already wanted to lock scope while ownercx was running a request. - * We can't claim any scope whose u.link is non-null at this point, - * even if ownercx->requestDepth is 0 (see below where we suspend our - * request before waiting on rt->scopeSharingDone). - */ - if (!scope->u.link && - (!js_ValidContextPointer(rt, ownercx) || - !ownercx->requestDepth || - ownercx->thread == cx->thread)) { - JS_ASSERT(scope->u.count == 0); - scope->ownercx = cx; - JS_UNLOCK_GC(rt); - JS_RUNTIME_METER(rt, claimedScopes); - return JS_TRUE; - } - - /* - * Avoid deadlock if scope's owner context is waiting on a scope that - * we own, by revoking scope's ownership. This approach to deadlock - * avoidance works because the engine never nests scope locks, except - * for the notable case of js_SetProtoOrParent (see jsobj.c). - * - * If cx could hold locks on ownercx->scopeToShare, or if ownercx - * could hold locks on scope, we would need to keep reentrancy counts - * for all such "flyweight" (ownercx != NULL) locks, so that control - * would unwind properly once these locks became "thin" or "fat". - * Apart from the js_SetProtoOrParent exception, the engine promotes - * a scope from exclusive to shared access only when locking, never - * when holding or unlocking. - * - * If ownercx's thread is calling js_SetProtoOrParent, trying to lock - * the inner scope (the scope of the object being set as the prototype - * of the outer object), ShareScope will find the outer object's scope - * at rt->setSlotScope. If it's the same as scope, we give it a lock - * held by ownercx's thread with reentrancy count of 1, then we return - * here and break. After that we unwind to js_[GS]etSlotThreadSafe or - * js_LockScope (our caller), where we wait on the newly-fattened lock - * until ownercx's thread unwinds from js_SetProtoOrParent. - * - * Avoid deadlock before any of this scope/context cycle detection if - * cx is on the active GC's thread, because in that case, no requests - * will run until the GC completes. Any scope wanted by the GC (from - * a finalizer) that can't be claimed must be slated for sharing. - */ - if (rt->gcThread == cx->thread || - (ownercx->scopeToShare && - WillDeadlock(ownercx->scopeToShare, cx))) { - ShareScope(rt, scope); - break; - } - - /* - * Thanks to the non-zero NO_SCOPE_SHARING_TODO link terminator, we - * can decide whether scope is on rt->scopeSharingTodo with a single - * non-null test, and avoid double-insertion bugs. - */ - if (!scope->u.link) { - scope->u.link = rt->scopeSharingTodo; - rt->scopeSharingTodo = scope; - js_HoldObjectMap(cx, &scope->map); - } - - /* - * Inline JS_SuspendRequest before we wait on rt->scopeSharingDone, - * saving and clearing cx->requestDepth so we don't deadlock if the - * GC needs to run on ownercx. - * - * Unlike JS_SuspendRequest and JS_EndRequest, we must take care not - * to decrement rt->requestCount if cx is active on the GC's thread, - * because the GC has already reduced rt->requestCount to exclude all - * such such contexts. - */ - saveDepth = cx->requestDepth; - if (saveDepth) { - cx->requestDepth = 0; - if (rt->gcThread != cx->thread) { - JS_ASSERT(rt->requestCount > 0); - rt->requestCount--; - if (rt->requestCount == 0) - JS_NOTIFY_REQUEST_DONE(rt); - } - } - - /* - * We know that some other thread's context owns scope, which is now - * linked onto rt->scopeSharingTodo, awaiting the end of that other - * thread's request. So it is safe to wait on rt->scopeSharingDone. - */ - cx->scopeToShare = scope; - stat = PR_WaitCondVar(rt->scopeSharingDone, PR_INTERVAL_NO_TIMEOUT); - JS_ASSERT(stat != PR_FAILURE); - - /* - * Inline JS_ResumeRequest after waiting on rt->scopeSharingDone, - * restoring cx->requestDepth. Same note as above for the inlined, - * specialized JS_SuspendRequest code: beware rt->gcThread. - */ - if (saveDepth) { - if (rt->gcThread != cx->thread) { - while (rt->gcLevel > 0) - JS_AWAIT_GC_DONE(rt); - rt->requestCount++; - } - cx->requestDepth = saveDepth; - } - - /* - * Don't clear cx->scopeToShare until after we're through waiting on - * all condition variables protected by rt->gcLock -- that includes - * rt->scopeSharingDone *and* rt->gcDone (hidden in JS_AWAIT_GC_DONE, - * in the inlined JS_ResumeRequest code immediately above). - * - * Otherwise, the GC could easily deadlock with another thread that - * owns a scope wanted by a finalizer. By keeping cx->scopeToShare - * set till here, we ensure that such deadlocks are detected, which - * results in the finalized object's scope being shared (it must, of - * course, have other, live objects sharing it). - */ - cx->scopeToShare = NULL; - } - - JS_UNLOCK_GC(rt); - return JS_FALSE; -} - -/* Exported to js.c, which calls it via OBJ_GET_* and JSVAL_IS_* macros. */ -JS_FRIEND_API(jsval) -js_GetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot) -{ - jsval v; - JSScope *scope; -#ifndef NSPR_LOCK - JSThinLock *tl; - jsword me; -#endif - - /* - * We handle non-native objects via JSObjectOps.getRequiredSlot, treating - * all slots starting from 0 as required slots. A property definition or - * some prior arrangement must have allocated slot. - * - * Note once again (see jspubtd.h, before JSGetRequiredSlotOp's typedef) - * the crucial distinction between a |required slot number| that's passed - * to the get/setRequiredSlot JSObjectOps, and a |reserved slot index| - * passed to the JS_Get/SetReservedSlot APIs. - */ - if (!OBJ_IS_NATIVE(obj)) - return OBJ_GET_REQUIRED_SLOT(cx, obj, slot); - - /* - * Native object locking is inlined here to optimize the single-threaded - * and contention-free multi-threaded cases. - */ - scope = OBJ_SCOPE(obj); - JS_ASSERT(scope->ownercx != cx); - JS_ASSERT(obj->slots && slot < obj->map->freeslot); - - /* - * Avoid locking if called from the GC (see GC_AWARE_GET_SLOT in jsobj.h). - * Also avoid locking an object owning a sealed scope. If neither of those - * special cases applies, try to claim scope's flyweight lock from whatever - * context may have had it in an earlier request. - */ - if (CX_THREAD_IS_RUNNING_GC(cx) || - (SCOPE_IS_SEALED(scope) && scope->object == obj) || - (scope->ownercx && ClaimScope(scope, cx))) { - return obj->slots[slot]; - } - -#ifndef NSPR_LOCK - tl = &scope->lock; - me = CX_THINLOCK_ID(cx); - JS_ASSERT(CURRENT_THREAD_IS_ME(me)); - if (js_CompareAndSwap(&tl->owner, 0, me)) { - /* - * Got the lock with one compare-and-swap. Even so, someone else may - * have mutated obj so it now has its own scope and lock, which would - * require either a restart from the top of this routine, or a thin - * lock release followed by fat lock acquisition. - */ - if (scope == OBJ_SCOPE(obj)) { - v = obj->slots[slot]; - if (!js_CompareAndSwap(&tl->owner, me, 0)) { - /* Assert that scope locks never revert to flyweight. */ - JS_ASSERT(scope->ownercx != cx); - LOGIT(scope, '1'); - scope->u.count = 1; - js_UnlockObj(cx, obj); - } - return v; - } - if (!js_CompareAndSwap(&tl->owner, me, 0)) - js_Dequeue(tl); - } - else if (Thin_RemoveWait(ReadWord(tl->owner)) == me) { - return obj->slots[slot]; - } -#endif - - js_LockObj(cx, obj); - v = obj->slots[slot]; - - /* - * Test whether cx took ownership of obj's scope during js_LockObj. - * - * This does not mean that a given scope reverted to flyweight from "thin" - * or "fat" -- it does mean that obj's map pointer changed due to another - * thread setting a property, requiring obj to cease sharing a prototype - * object's scope (whose lock was not flyweight, else we wouldn't be here - * in the first place!). - */ - scope = OBJ_SCOPE(obj); - if (scope->ownercx != cx) - js_UnlockScope(cx, scope); - return v; -} - -void -js_SetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot, jsval v) -{ - JSScope *scope; -#ifndef NSPR_LOCK - JSThinLock *tl; - jsword me; -#endif - - /* Any string stored in a thread-safe object must be immutable. */ - if (JSVAL_IS_STRING(v)) - MAKE_STRING_IMMUTABLE(cx->runtime, v, &v); - - /* - * We handle non-native objects via JSObjectOps.setRequiredSlot, as above - * for the Get case. - */ - if (!OBJ_IS_NATIVE(obj)) { - OBJ_SET_REQUIRED_SLOT(cx, obj, slot, v); - return; - } - - /* - * Native object locking is inlined here to optimize the single-threaded - * and contention-free multi-threaded cases. - */ - scope = OBJ_SCOPE(obj); - JS_ASSERT(scope->ownercx != cx); - JS_ASSERT(obj->slots && slot < obj->map->freeslot); - - /* - * Avoid locking if called from the GC (see GC_AWARE_GET_SLOT in jsobj.h). - * Also avoid locking an object owning a sealed scope. If neither of those - * special cases applies, try to claim scope's flyweight lock from whatever - * context may have had it in an earlier request. - */ - if (CX_THREAD_IS_RUNNING_GC(cx) || - (SCOPE_IS_SEALED(scope) && scope->object == obj) || - (scope->ownercx && ClaimScope(scope, cx))) { - obj->slots[slot] = v; - return; - } - -#ifndef NSPR_LOCK - tl = &scope->lock; - me = CX_THINLOCK_ID(cx); - JS_ASSERT(CURRENT_THREAD_IS_ME(me)); - if (js_CompareAndSwap(&tl->owner, 0, me)) { - if (scope == OBJ_SCOPE(obj)) { - obj->slots[slot] = v; - if (!js_CompareAndSwap(&tl->owner, me, 0)) { - /* Assert that scope locks never revert to flyweight. */ - JS_ASSERT(scope->ownercx != cx); - LOGIT(scope, '1'); - scope->u.count = 1; - js_UnlockObj(cx, obj); - } - return; - } - if (!js_CompareAndSwap(&tl->owner, me, 0)) - js_Dequeue(tl); - } - else if (Thin_RemoveWait(ReadWord(tl->owner)) == me) { - obj->slots[slot] = v; - return; - } -#endif - - js_LockObj(cx, obj); - obj->slots[slot] = v; - - /* - * Same drill as above, in js_GetSlotThreadSafe. Note that we cannot - * assume obj has its own mutable scope (where scope->object == obj) yet, - * because OBJ_SET_SLOT is called for the "universal", common slots such - * as JSSLOT_PROTO and JSSLOT_PARENT, without a prior js_GetMutableScope. - * See also the JSPROP_SHARED attribute and its usage. - */ - scope = OBJ_SCOPE(obj); - if (scope->ownercx != cx) - js_UnlockScope(cx, scope); -} - -#ifndef NSPR_LOCK - -static JSFatLock * -NewFatlock() -{ - JSFatLock *fl = (JSFatLock *)malloc(sizeof(JSFatLock)); /* for now */ - if (!fl) return NULL; - fl->susp = 0; - fl->next = NULL; - fl->prevp = NULL; - fl->slock = PR_NewLock(); - fl->svar = PR_NewCondVar(fl->slock); - return fl; -} - -static void -DestroyFatlock(JSFatLock *fl) -{ - PR_DestroyLock(fl->slock); - PR_DestroyCondVar(fl->svar); - free(fl); -} - -static JSFatLock * -ListOfFatlocks(int listc) -{ - JSFatLock *m; - JSFatLock *m0; - int i; - - JS_ASSERT(listc>0); - m0 = m = NewFatlock(); - for (i=1; inext = NewFatlock(); - m = m->next; - } - return m0; -} - -static void -DeleteListOfFatlocks(JSFatLock *m) -{ - JSFatLock *m0; - for (; m; m=m0) { - m0 = m->next; - DestroyFatlock(m); - } -} - -static JSFatLockTable *fl_list_table = NULL; -static uint32 fl_list_table_len = 0; -static uint32 fl_list_chunk_len = 0; - -static JSFatLock * -GetFatlock(void *id) -{ - JSFatLock *m; - - uint32 i = GLOBAL_LOCK_INDEX(id); - if (fl_list_table[i].free == NULL) { -#ifdef DEBUG - if (fl_list_table[i].taken) - printf("Ran out of fat locks!\n"); -#endif - fl_list_table[i].free = ListOfFatlocks(fl_list_chunk_len); - } - m = fl_list_table[i].free; - fl_list_table[i].free = m->next; - m->susp = 0; - m->next = fl_list_table[i].taken; - m->prevp = &fl_list_table[i].taken; - if (fl_list_table[i].taken) - fl_list_table[i].taken->prevp = &m->next; - fl_list_table[i].taken = m; - return m; -} - -static void -PutFatlock(JSFatLock *m, void *id) -{ - uint32 i; - if (m == NULL) - return; - - /* Unlink m from fl_list_table[i].taken. */ - *m->prevp = m->next; - if (m->next) - m->next->prevp = m->prevp; - - /* Insert m in fl_list_table[i].free. */ - i = GLOBAL_LOCK_INDEX(id); - m->next = fl_list_table[i].free; - fl_list_table[i].free = m; -} - -#endif /* !NSPR_LOCK */ - -JSBool -js_SetupLocks(int listc, int globc) -{ -#ifndef NSPR_LOCK - uint32 i; - - if (global_locks) - return JS_TRUE; -#ifdef DEBUG - if (listc > 10000 || listc < 0) /* listc == fat lock list chunk length */ - printf("Bad number %d in js_SetupLocks()!\n", listc); - if (globc > 100 || globc < 0) /* globc == number of global locks */ - printf("Bad number %d in js_SetupLocks()!\n", listc); -#endif - global_locks_log2 = JS_CeilingLog2(globc); - global_locks_mask = JS_BITMASK(global_locks_log2); - global_lock_count = JS_BIT(global_locks_log2); - global_locks = (PRLock **) malloc(global_lock_count * sizeof(PRLock*)); - if (!global_locks) - return JS_FALSE; - for (i = 0; i < global_lock_count; i++) { - global_locks[i] = PR_NewLock(); - if (!global_locks[i]) { - global_lock_count = i; - js_CleanupLocks(); - return JS_FALSE; - } - } - fl_list_table = (JSFatLockTable *) malloc(i * sizeof(JSFatLockTable)); - if (!fl_list_table) { - js_CleanupLocks(); - return JS_FALSE; - } - fl_list_table_len = global_lock_count; - for (i = 0; i < global_lock_count; i++) - fl_list_table[i].free = fl_list_table[i].taken = NULL; - fl_list_chunk_len = listc; -#endif /* !NSPR_LOCK */ - return JS_TRUE; -} - -void -js_CleanupLocks() -{ -#ifndef NSPR_LOCK - uint32 i; - - if (global_locks) { - for (i = 0; i < global_lock_count; i++) - PR_DestroyLock(global_locks[i]); - free(global_locks); - global_locks = NULL; - global_lock_count = 1; - global_locks_log2 = 0; - global_locks_mask = 0; - } - if (fl_list_table) { - for (i = 0; i < fl_list_table_len; i++) { - DeleteListOfFatlocks(fl_list_table[i].free); - fl_list_table[i].free = NULL; - DeleteListOfFatlocks(fl_list_table[i].taken); - fl_list_table[i].taken = NULL; - } - free(fl_list_table); - fl_list_table = NULL; - fl_list_table_len = 0; - } -#endif /* !NSPR_LOCK */ -} - -#ifndef NSPR_LOCK - -/* - * Fast locking and unlocking is implemented by delaying the allocation of a - * system lock (fat lock) until contention. As long as a locking thread A - * runs uncontended, the lock is represented solely by storing A's identity in - * the object being locked. - * - * If another thread B tries to lock the object currently locked by A, B is - * enqueued into a fat lock structure (which might have to be allocated and - * pointed to by the object), and suspended using NSPR conditional variables - * (wait). A wait bit (Bacon bit) is set in the lock word of the object, - * signalling to A that when releasing the lock, B must be dequeued and - * notified. - * - * The basic operation of the locking primitives (js_Lock, js_Unlock, - * js_Enqueue, and js_Dequeue) is compare-and-swap. Hence, when locking into - * the word pointed at by p, compare-and-swap(p, 0, A) success implies that p - * is unlocked. Similarly, when unlocking p, if compare-and-swap(p, A, 0) - * succeeds this implies that p is uncontended (no one is waiting because the - * wait bit is not set). - * - * When dequeueing, the lock is released, and one of the threads suspended on - * the lock is notified. If other threads still are waiting, the wait bit is - * kept (in js_Enqueue), and if not, the fat lock is deallocated. - * - * The functions js_Enqueue, js_Dequeue, js_SuspendThread, and js_ResumeThread - * are serialized using a global lock. For scalability, a hashtable of global - * locks is used, which is indexed modulo the thin lock pointer. - */ - -/* - * Invariants: - * (i) global lock is held - * (ii) fl->susp >= 0 - */ -static int -js_SuspendThread(JSThinLock *tl) -{ - JSFatLock *fl; - PRStatus stat; - - if (tl->fat == NULL) - fl = tl->fat = GetFatlock(tl); - else - fl = tl->fat; - JS_ASSERT(fl->susp >= 0); - fl->susp++; - PR_Lock(fl->slock); - js_UnlockGlobal(tl); - stat = PR_WaitCondVar(fl->svar, PR_INTERVAL_NO_TIMEOUT); - JS_ASSERT(stat != PR_FAILURE); - PR_Unlock(fl->slock); - js_LockGlobal(tl); - fl->susp--; - if (fl->susp == 0) { - PutFatlock(fl, tl); - tl->fat = NULL; - } - return tl->fat == NULL; -} - -/* - * (i) global lock is held - * (ii) fl->susp > 0 - */ -static void -js_ResumeThread(JSThinLock *tl) -{ - JSFatLock *fl = tl->fat; - PRStatus stat; - - JS_ASSERT(fl != NULL); - JS_ASSERT(fl->susp > 0); - PR_Lock(fl->slock); - js_UnlockGlobal(tl); - stat = PR_NotifyCondVar(fl->svar); - JS_ASSERT(stat != PR_FAILURE); - PR_Unlock(fl->slock); -} - -static void -js_Enqueue(JSThinLock *tl, jsword me) -{ - jsword o, n; - - js_LockGlobal(tl); - for (;;) { - o = ReadWord(tl->owner); - n = Thin_SetWait(o); - if (o != 0 && js_CompareAndSwap(&tl->owner, o, n)) { - if (js_SuspendThread(tl)) - me = Thin_RemoveWait(me); - else - me = Thin_SetWait(me); - } - else if (js_CompareAndSwap(&tl->owner, 0, me)) { - js_UnlockGlobal(tl); - return; - } - } -} - -static void -js_Dequeue(JSThinLock *tl) -{ - jsword o; - - js_LockGlobal(tl); - o = ReadWord(tl->owner); - JS_ASSERT(Thin_GetWait(o) != 0); - JS_ASSERT(tl->fat != NULL); - if (!js_CompareAndSwap(&tl->owner, o, 0)) /* release it */ - JS_ASSERT(0); - js_ResumeThread(tl); -} - -JS_INLINE void -js_Lock(JSThinLock *tl, jsword me) -{ - JS_ASSERT(CURRENT_THREAD_IS_ME(me)); - if (js_CompareAndSwap(&tl->owner, 0, me)) - return; - if (Thin_RemoveWait(ReadWord(tl->owner)) != me) - js_Enqueue(tl, me); -#ifdef DEBUG - else - JS_ASSERT(0); -#endif -} - -JS_INLINE void -js_Unlock(JSThinLock *tl, jsword me) -{ - JS_ASSERT(CURRENT_THREAD_IS_ME(me)); - - /* - * Only me can hold the lock, no need to use compare and swap atomic - * operation for this common case. - */ - if (tl->owner == me) { - tl->owner = 0; - return; - } - JS_ASSERT(Thin_GetWait(tl->owner)); - if (Thin_RemoveWait(ReadWord(tl->owner)) == me) - js_Dequeue(tl); -#ifdef DEBUG - else - JS_ASSERT(0); /* unbalanced unlock */ -#endif -} - -#endif /* !NSPR_LOCK */ - -void -js_LockRuntime(JSRuntime *rt) -{ - PR_Lock(rt->rtLock); -#ifdef DEBUG - rt->rtLockOwner = js_CurrentThreadId(); -#endif -} - -void -js_UnlockRuntime(JSRuntime *rt) -{ -#ifdef DEBUG - rt->rtLockOwner = 0; -#endif - PR_Unlock(rt->rtLock); -} - -void -js_LockScope(JSContext *cx, JSScope *scope) -{ - jsword me = CX_THINLOCK_ID(cx); - - JS_ASSERT(CURRENT_THREAD_IS_ME(me)); - JS_ASSERT(scope->ownercx != cx); - if (CX_THREAD_IS_RUNNING_GC(cx)) - return; - if (scope->ownercx && ClaimScope(scope, cx)) - return; - - if (Thin_RemoveWait(ReadWord(scope->lock.owner)) == me) { - JS_ASSERT(scope->u.count > 0); - LOGIT(scope, '+'); - scope->u.count++; - } else { - JSThinLock *tl = &scope->lock; - JS_LOCK0(tl, me); - JS_ASSERT(scope->u.count == 0); - LOGIT(scope, '1'); - scope->u.count = 1; - } -} - -void -js_UnlockScope(JSContext *cx, JSScope *scope) -{ - jsword me = CX_THINLOCK_ID(cx); - - /* We hope compilers use me instead of reloading cx->thread in the macro. */ - if (CX_THREAD_IS_RUNNING_GC(cx)) - return; - if (cx->lockedSealedScope == scope) { - cx->lockedSealedScope = NULL; - return; - } - - /* - * If scope->ownercx is not null, it's likely that two contexts not using - * requests nested locks for scope. The first context, cx here, claimed - * scope; the second, scope->ownercx here, re-claimed it because the first - * was not in a request, or was on the same thread. We don't want to keep - * track of such nesting, because it penalizes the common non-nested case. - * Instead of asserting here and silently coping, we simply re-claim scope - * for cx and return. - * - * See http://bugzilla.mozilla.org/show_bug.cgi?id=229200 for a real world - * case where an asymmetric thread model (Mozilla's main thread is known - * to be the only thread that runs the GC) combined with multiple contexts - * per thread has led to such request-less nesting. - */ - if (scope->ownercx) { - JS_ASSERT(scope->u.count == 0); - JS_ASSERT(scope->lock.owner == 0); - scope->ownercx = cx; - return; - } - - JS_ASSERT(scope->u.count > 0); - if (Thin_RemoveWait(ReadWord(scope->lock.owner)) != me) { - JS_ASSERT(0); /* unbalanced unlock */ - return; - } - LOGIT(scope, '-'); - if (--scope->u.count == 0) { - JSThinLock *tl = &scope->lock; - JS_UNLOCK0(tl, me); - } -} - -/* - * NB: oldscope may be null if our caller is js_GetMutableScope and it just - * dropped the last reference to oldscope. - */ -void -js_TransferScopeLock(JSContext *cx, JSScope *oldscope, JSScope *newscope) -{ - jsword me; - JSThinLock *tl; - - JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, newscope)); - - /* - * If the last reference to oldscope went away, newscope needs no lock - * state update. - */ - if (!oldscope) - return; - JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, oldscope)); - - /* - * Special case in js_LockScope and js_UnlockScope for the GC calling - * code that locks, unlocks, or mutates. Nothing to do in these cases, - * because scope and newscope were "locked" by the GC thread, so neither - * was actually locked. - */ - if (CX_THREAD_IS_RUNNING_GC(cx)) - return; - - /* - * Special case in js_LockObj and js_UnlockScope for locking the sealed - * scope of an object that owns that scope (the prototype or mutated obj - * for which OBJ_SCOPE(obj)->object == obj), and unlocking it. - */ - JS_ASSERT(cx->lockedSealedScope != newscope); - if (cx->lockedSealedScope == oldscope) { - JS_ASSERT(newscope->ownercx == cx || - (!newscope->ownercx && newscope->u.count == 1)); - cx->lockedSealedScope = NULL; - return; - } - - /* - * If oldscope is single-threaded, there's nothing to do. - */ - if (oldscope->ownercx) { - JS_ASSERT(oldscope->ownercx == cx); - JS_ASSERT(newscope->ownercx == cx || - (!newscope->ownercx && newscope->u.count == 1)); - return; - } - - /* - * We transfer oldscope->u.count only if newscope is not single-threaded. - * Flow unwinds from here through some number of JS_UNLOCK_SCOPE and/or - * JS_UNLOCK_OBJ macro calls, which will decrement newscope->u.count only - * if they find newscope->ownercx != cx. - */ - if (newscope->ownercx != cx) { - JS_ASSERT(!newscope->ownercx); - newscope->u.count = oldscope->u.count; - } - - /* - * Reset oldscope's lock state so that it is completely unlocked. - */ - LOGIT(oldscope, '0'); - oldscope->u.count = 0; - tl = &oldscope->lock; - me = CX_THINLOCK_ID(cx); - JS_UNLOCK0(tl, me); -} - -void -js_LockObj(JSContext *cx, JSObject *obj) -{ - JSScope *scope; - - JS_ASSERT(OBJ_IS_NATIVE(obj)); - - /* - * We must test whether the GC is calling and return without mutating any - * state, especially cx->lockedSealedScope. Note asymmetry with respect to - * js_UnlockObj, which is a thin-layer on top of js_UnlockScope. - */ - if (CX_THREAD_IS_RUNNING_GC(cx)) - return; - - for (;;) { - scope = OBJ_SCOPE(obj); - if (SCOPE_IS_SEALED(scope) && scope->object == obj && - !cx->lockedSealedScope) { - cx->lockedSealedScope = scope; - return; - } - - js_LockScope(cx, scope); - - /* If obj still has this scope, we're done. */ - if (scope == OBJ_SCOPE(obj)) - return; - - /* Lost a race with a mutator; retry with obj's new scope. */ - js_UnlockScope(cx, scope); - } -} - -void -js_UnlockObj(JSContext *cx, JSObject *obj) -{ - JS_ASSERT(OBJ_IS_NATIVE(obj)); - js_UnlockScope(cx, OBJ_SCOPE(obj)); -} - -#ifdef DEBUG - -JSBool -js_IsRuntimeLocked(JSRuntime *rt) -{ - return js_CurrentThreadId() == rt->rtLockOwner; -} - -JSBool -js_IsObjLocked(JSContext *cx, JSObject *obj) -{ - JSScope *scope = OBJ_SCOPE(obj); - - return MAP_IS_NATIVE(&scope->map) && js_IsScopeLocked(cx, scope); -} - -JSBool -js_IsScopeLocked(JSContext *cx, JSScope *scope) -{ - /* Special case: the GC locking any object's scope, see js_LockScope. */ - if (CX_THREAD_IS_RUNNING_GC(cx)) - return JS_TRUE; - - /* Special case: locked object owning a sealed scope, see js_LockObj. */ - if (cx->lockedSealedScope == scope) - return JS_TRUE; - - /* - * General case: the scope is either exclusively owned (by cx), or it has - * a thin or fat lock to cope with shared (concurrent) ownership. - */ - if (scope->ownercx) { - JS_ASSERT(scope->ownercx == cx || scope->ownercx->thread == cx->thread); - return JS_TRUE; - } - return js_CurrentThreadId() == - ((JSThread *)Thin_RemoveWait(ReadWord(scope->lock.owner)))->id; -} - -#endif /* DEBUG */ -#endif /* JS_THREADSAFE */ diff --git a/spidermonkey/src/jslock.h b/spidermonkey/src/jslock.h deleted file mode 100644 index f9ed03d..0000000 --- a/spidermonkey/src/jslock.h +++ /dev/null @@ -1,266 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ -#ifndef jslock_h__ -#define jslock_h__ - -#ifdef JS_THREADSAFE - -#include "jstypes.h" -#include "pratom.h" -#include "prlock.h" -#include "prcvar.h" -#include "prthread.h" - -#include "jsprvtd.h" /* for JSScope, etc. */ -#include "jspubtd.h" /* for JSRuntime, etc. */ - -#define Thin_GetWait(W) ((jsword)(W) & 0x1) -#define Thin_SetWait(W) ((jsword)(W) | 0x1) -#define Thin_RemoveWait(W) ((jsword)(W) & ~0x1) - -typedef struct JSFatLock JSFatLock; - -struct JSFatLock { - int susp; - PRLock *slock; - PRCondVar *svar; - JSFatLock *next; - JSFatLock **prevp; -}; - -typedef struct JSThinLock { - jsword owner; - JSFatLock *fat; -} JSThinLock; - -#define CX_THINLOCK_ID(cx) ((jsword)(cx)->thread) -#define CURRENT_THREAD_IS_ME(me) (((JSThread *)me)->id == js_CurrentThreadId()) - -typedef PRLock JSLock; - -typedef struct JSFatLockTable { - JSFatLock *free; - JSFatLock *taken; -} JSFatLockTable; - -/* - * Atomic increment and decrement for a reference counter, given jsrefcount *p. - * NB: jsrefcount is int32, aka PRInt32, so that pratom.h functions work. - */ -#define JS_ATOMIC_INCREMENT(p) PR_AtomicIncrement((PRInt32 *)(p)) -#define JS_ATOMIC_DECREMENT(p) PR_AtomicDecrement((PRInt32 *)(p)) -#define JS_ATOMIC_ADD(p,v) PR_AtomicAdd((PRInt32 *)(p), (PRInt32)(v)) - -#define js_CurrentThreadId() (jsword)PR_GetCurrentThread() -#define JS_NEW_LOCK() PR_NewLock() -#define JS_DESTROY_LOCK(l) PR_DestroyLock(l) -#define JS_ACQUIRE_LOCK(l) PR_Lock(l) -#define JS_RELEASE_LOCK(l) PR_Unlock(l) -#define JS_LOCK0(P,M) js_Lock(P,M) -#define JS_UNLOCK0(P,M) js_Unlock(P,M) - -#define JS_NEW_CONDVAR(l) PR_NewCondVar(l) -#define JS_DESTROY_CONDVAR(cv) PR_DestroyCondVar(cv) -#define JS_WAIT_CONDVAR(cv,to) PR_WaitCondVar(cv,to) -#define JS_NO_TIMEOUT PR_INTERVAL_NO_TIMEOUT -#define JS_NOTIFY_CONDVAR(cv) PR_NotifyCondVar(cv) -#define JS_NOTIFY_ALL_CONDVAR(cv) PR_NotifyAllCondVar(cv) - -/* - * Include jsscope.h so JS_LOCK_OBJ macro callers don't have to include it. - * Since there is a JSThinLock member in JSScope, we can't nest this include - * much earlier (see JSThinLock's typedef, above). Yes, that means there is - * an #include cycle between jslock.h and jsscope.h: moderate-sized XXX here, - * to be fixed by moving JS_LOCK_SCOPE to jsscope.h, JS_LOCK_OBJ to jsobj.h, - * and so on. - */ -#include "jsscope.h" - -#define JS_LOCK_RUNTIME(rt) js_LockRuntime(rt) -#define JS_UNLOCK_RUNTIME(rt) js_UnlockRuntime(rt) - -/* - * NB: The JS_LOCK_OBJ and JS_UNLOCK_OBJ macros work *only* on native objects - * (objects for which OBJ_IS_NATIVE returns true). All uses of these macros in - * the engine are predicated on OBJ_IS_NATIVE or equivalent checks. These uses - * are for optimizations above the JSObjectOps layer, under which object locks - * normally hide. - */ -#define JS_LOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->ownercx == (cx)) \ - ? (void)0 \ - : (js_LockObj(cx, obj))) -#define JS_UNLOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->ownercx == (cx)) \ - ? (void)0 : js_UnlockObj(cx, obj)) - -#define JS_LOCK_SCOPE(cx,scope) ((scope)->ownercx == (cx) ? (void)0 \ - : js_LockScope(cx, scope)) -#define JS_UNLOCK_SCOPE(cx,scope) ((scope)->ownercx == (cx) ? (void)0 \ - : js_UnlockScope(cx, scope)) -#define JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope) \ - js_TransferScopeLock(cx, scope, newscope) - -extern void js_LockRuntime(JSRuntime *rt); -extern void js_UnlockRuntime(JSRuntime *rt); -extern void js_LockObj(JSContext *cx, JSObject *obj); -extern void js_UnlockObj(JSContext *cx, JSObject *obj); -extern void js_LockScope(JSContext *cx, JSScope *scope); -extern void js_UnlockScope(JSContext *cx, JSScope *scope); -extern int js_SetupLocks(int,int); -extern void js_CleanupLocks(); -extern void js_TransferScopeLock(JSContext *, JSScope *, JSScope *); -extern JS_FRIEND_API(jsval) -js_GetSlotThreadSafe(JSContext *, JSObject *, uint32); -extern void js_SetSlotThreadSafe(JSContext *, JSObject *, uint32, jsval); -extern void js_InitLock(JSThinLock *); -extern void js_FinishLock(JSThinLock *); -extern void js_FinishSharingScope(JSRuntime *rt, JSScope *scope); - -#ifdef DEBUG - -#define JS_IS_RUNTIME_LOCKED(rt) js_IsRuntimeLocked(rt) -#define JS_IS_OBJ_LOCKED(cx,obj) js_IsObjLocked(cx,obj) -#define JS_IS_SCOPE_LOCKED(cx,scope) js_IsScopeLocked(cx,scope) - -extern JSBool js_IsRuntimeLocked(JSRuntime *rt); -extern JSBool js_IsObjLocked(JSContext *cx, JSObject *obj); -extern JSBool js_IsScopeLocked(JSContext *cx, JSScope *scope); - -#else - -#define JS_IS_RUNTIME_LOCKED(rt) 0 -#define JS_IS_OBJ_LOCKED(cx,obj) 1 -#define JS_IS_SCOPE_LOCKED(cx,scope) 1 - -#endif /* DEBUG */ - -#define JS_LOCK_OBJ_VOID(cx, obj, e) \ - JS_BEGIN_MACRO \ - JS_LOCK_OBJ(cx, obj); \ - e; \ - JS_UNLOCK_OBJ(cx, obj); \ - JS_END_MACRO - -#define JS_LOCK_VOID(cx, e) \ - JS_BEGIN_MACRO \ - JSRuntime *_rt = (cx)->runtime; \ - JS_LOCK_RUNTIME_VOID(_rt, e); \ - JS_END_MACRO - -/* FIXME: bug 353962 hackaround */ -#define JS_USE_ONLY_NSPR_LOCKS 1 - -#if defined(JS_USE_ONLY_NSPR_LOCKS) || \ - !( (defined(_WIN32) && defined(_M_IX86)) || \ - (defined(__GNUC__) && defined(__i386__)) || \ - ((defined(__USLC__) || defined(_SCO_DS)) && defined(i386)) || \ - (defined(SOLARIS) && defined(sparc) && defined(ULTRA_SPARC)) || \ - defined(AIX) ) - -#define NSPR_LOCK 1 - -#undef JS_LOCK0 -#undef JS_UNLOCK0 -#define JS_LOCK0(P,M) (JS_ACQUIRE_LOCK(((JSLock*)(P)->fat)), (P)->owner = (M)) -#define JS_UNLOCK0(P,M) ((P)->owner = 0, JS_RELEASE_LOCK(((JSLock*)(P)->fat))) - -#else /* arch-tests */ - -#undef NSPR_LOCK - -extern JS_INLINE void js_Lock(JSThinLock *tl, jsword me); -extern JS_INLINE void js_Unlock(JSThinLock *tl, jsword me); - -#endif /* arch-tests */ - -#else /* !JS_THREADSAFE */ - -#define JS_ATOMIC_INCREMENT(p) (++*(p)) -#define JS_ATOMIC_DECREMENT(p) (--*(p)) -#define JS_ATOMIC_ADD(p,v) (*(p) += (v)) - -#define JS_CurrentThreadId() 0 -#define JS_NEW_LOCK() NULL -#define JS_DESTROY_LOCK(l) ((void)0) -#define JS_ACQUIRE_LOCK(l) ((void)0) -#define JS_RELEASE_LOCK(l) ((void)0) -#define JS_LOCK0(P,M) ((void)0) -#define JS_UNLOCK0(P,M) ((void)0) - -#define JS_NEW_CONDVAR(l) NULL -#define JS_DESTROY_CONDVAR(cv) ((void)0) -#define JS_WAIT_CONDVAR(cv,to) ((void)0) -#define JS_NOTIFY_CONDVAR(cv) ((void)0) -#define JS_NOTIFY_ALL_CONDVAR(cv) ((void)0) - -#define JS_LOCK_RUNTIME(rt) ((void)0) -#define JS_UNLOCK_RUNTIME(rt) ((void)0) -#define JS_LOCK_OBJ(cx,obj) ((void)0) -#define JS_UNLOCK_OBJ(cx,obj) ((void)0) -#define JS_LOCK_OBJ_VOID(cx,obj,e) (e) -#define JS_LOCK_SCOPE(cx,scope) ((void)0) -#define JS_UNLOCK_SCOPE(cx,scope) ((void)0) -#define JS_TRANSFER_SCOPE_LOCK(c,o,n) ((void)0) - -#define JS_IS_RUNTIME_LOCKED(rt) 1 -#define JS_IS_OBJ_LOCKED(cx,obj) 1 -#define JS_IS_SCOPE_LOCKED(cx,scope) 1 -#define JS_LOCK_VOID(cx, e) JS_LOCK_RUNTIME_VOID((cx)->runtime, e) - -#endif /* !JS_THREADSAFE */ - -#define JS_LOCK_RUNTIME_VOID(rt,e) \ - JS_BEGIN_MACRO \ - JS_LOCK_RUNTIME(rt); \ - e; \ - JS_UNLOCK_RUNTIME(rt); \ - JS_END_MACRO - -#define JS_LOCK_GC(rt) JS_ACQUIRE_LOCK((rt)->gcLock) -#define JS_UNLOCK_GC(rt) JS_RELEASE_LOCK((rt)->gcLock) -#define JS_LOCK_GC_VOID(rt,e) (JS_LOCK_GC(rt), (e), JS_UNLOCK_GC(rt)) -#define JS_AWAIT_GC_DONE(rt) JS_WAIT_CONDVAR((rt)->gcDone, JS_NO_TIMEOUT) -#define JS_NOTIFY_GC_DONE(rt) JS_NOTIFY_ALL_CONDVAR((rt)->gcDone) -#define JS_AWAIT_REQUEST_DONE(rt) JS_WAIT_CONDVAR((rt)->requestDone, \ - JS_NO_TIMEOUT) -#define JS_NOTIFY_REQUEST_DONE(rt) JS_NOTIFY_CONDVAR((rt)->requestDone) - -#define JS_LOCK(P,CX) JS_LOCK0(P, CX_THINLOCK_ID(CX)) -#define JS_UNLOCK(P,CX) JS_UNLOCK0(P, CX_THINLOCK_ID(CX)) - -#endif /* jslock_h___ */ diff --git a/spidermonkey/src/jslocko.asm b/spidermonkey/src/jslocko.asm deleted file mode 100644 index 95353ba..0000000 --- a/spidermonkey/src/jslocko.asm +++ /dev/null @@ -1,60 +0,0 @@ -; -*- Mode: asm; tab-width: 8; 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 an OS/2 implementation of js_CompareAndSwap in assembly. -; -; The Initial Developer of the Original Code is -; IBM Corporation. -; Portions created by the Initial Developer are Copyright (C) 2001 -; 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 ***** - - .486P - .MODEL FLAT, OPTLINK - .STACK - - .CODE - -;;;--------------------------------------------------------------------- -;;; int _Optlink js_CompareAndSwap(jsword *w, jsword ov, jsword nv) -;;;--------------------------------------------------------------------- -js_CompareAndSwap PROC OPTLINK EXPORT - push ebx - mov ebx, eax - mov eax, edx - mov edx, ebx - lock cmpxchg [ebx], ecx - sete al - and eax, 1h - pop ebx - ret -js_CompareAndSwap endp - - END diff --git a/spidermonkey/src/jslog2.c b/spidermonkey/src/jslog2.c deleted file mode 100644 index 876e528..0000000 --- a/spidermonkey/src/jslog2.c +++ /dev/null @@ -1,94 +0,0 @@ -/* -*- 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 "jsstddef.h" -#include "jsbit.h" -#include "jsutil.h" - -/* -** Compute the log of the least power of 2 greater than or equal to n -*/ -JS_PUBLIC_API(JSIntn) JS_CeilingLog2(JSUint32 n) -{ - JSIntn log2; - - JS_CEILING_LOG2(log2, n); - return log2; -} - -/* -** Compute the log of the greatest power of 2 less than or equal to n. -** This really just finds the highest set bit in the word. -*/ -JS_PUBLIC_API(JSIntn) JS_FloorLog2(JSUint32 n) -{ - JSIntn log2; - - JS_FLOOR_LOG2(log2, n); - return log2; -} - -/* - * js_FloorLog2wImpl has to be defined only for 64-bit non-GCC case. - */ -#if !defined(JS_HAS_GCC_BUILTIN_CLZ) && JS_BYTES_PER_WORD == 8 - -JSUword -js_FloorLog2wImpl(JSUword n) -{ - JSUword log2, m; - - JS_ASSERT(n != 0); - - log2 = 0; - m = n >> 32; - if (m != 0) { n = m; log2 = 32; } - m = n >> 16; - if (m != 0) { n = m; log2 |= 16; } - m = n >> 8; - if (m != 0) { n = m; log2 |= 8; } - m = n >> 4; - if (m != 0) { n = m; log2 |= 4; } - m = n >> 2; - if (m != 0) { n = m; log2 |= 2; } - log2 |= (n >> 1); - - return log2; -} - -#endif diff --git a/spidermonkey/src/jslong.c b/spidermonkey/src/jslong.c deleted file mode 100644 index 9a4a5b4..0000000 --- a/spidermonkey/src/jslong.c +++ /dev/null @@ -1,281 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 "jsstddef.h" -#include "jstypes.h" -#include "jslong.h" - -static JSInt64 ll_zero = JSLL_INIT( 0x00000000,0x00000000 ); -static JSInt64 ll_maxint = JSLL_INIT( 0x7fffffff, 0xffffffff ); -static JSInt64 ll_minint = JSLL_INIT( 0x80000000, 0x00000000 ); - -#ifdef HAVE_WATCOM_BUG_2 -JSInt64 __pascal __loadds __export - JSLL_Zero(void) { return ll_zero; } -JSInt64 __pascal __loadds __export - JSLL_MaxInt(void) { return ll_maxint; } -JSInt64 __pascal __loadds __export - JSLL_MinInt(void) { return ll_minint; } -#else -JS_PUBLIC_API(JSInt64) JSLL_Zero(void) { return ll_zero; } -JS_PUBLIC_API(JSInt64) JSLL_MaxInt(void) { return ll_maxint; } -JS_PUBLIC_API(JSInt64) JSLL_MinInt(void) { return ll_minint; } -#endif - -#ifndef JS_HAVE_LONG_LONG -/* -** Divide 64-bit a by 32-bit b, which must be normalized so its high bit is 1. -*/ -static void norm_udivmod32(JSUint32 *qp, JSUint32 *rp, JSUint64 a, JSUint32 b) -{ - JSUint32 d1, d0, q1, q0; - JSUint32 r1, r0, m; - - d1 = jshi16(b); - d0 = jslo16(b); - r1 = a.hi % d1; - q1 = a.hi / d1; - m = q1 * d0; - r1 = (r1 << 16) | jshi16(a.lo); - if (r1 < m) { - q1--, r1 += b; - if (r1 >= b /* i.e., we didn't get a carry when adding to r1 */ - && r1 < m) { - q1--, r1 += b; - } - } - r1 -= m; - r0 = r1 % d1; - q0 = r1 / d1; - m = q0 * d0; - r0 = (r0 << 16) | jslo16(a.lo); - if (r0 < m) { - q0--, r0 += b; - if (r0 >= b - && r0 < m) { - q0--, r0 += b; - } - } - *qp = (q1 << 16) | q0; - *rp = r0 - m; -} - -static JSUint32 CountLeadingZeros(JSUint32 a) -{ - JSUint32 t; - JSUint32 r = 32; - - if ((t = a >> 16) != 0) - r -= 16, a = t; - if ((t = a >> 8) != 0) - r -= 8, a = t; - if ((t = a >> 4) != 0) - r -= 4, a = t; - if ((t = a >> 2) != 0) - r -= 2, a = t; - if ((t = a >> 1) != 0) - r -= 1, a = t; - if (a & 1) - r--; - return r; -} - -JS_PUBLIC_API(void) jsll_udivmod(JSUint64 *qp, JSUint64 *rp, JSUint64 a, JSUint64 b) -{ - JSUint32 n0, n1, n2; - JSUint32 q0, q1; - JSUint32 rsh, lsh; - - n0 = a.lo; - n1 = a.hi; - - if (b.hi == 0) { - if (b.lo > n1) { - /* (0 q0) = (n1 n0) / (0 D0) */ - - lsh = CountLeadingZeros(b.lo); - - if (lsh) { - /* - * Normalize, i.e. make the most significant bit of the - * denominator be set. - */ - b.lo = b.lo << lsh; - n1 = (n1 << lsh) | (n0 >> (32 - lsh)); - n0 = n0 << lsh; - } - - a.lo = n0, a.hi = n1; - norm_udivmod32(&q0, &n0, a, b.lo); - q1 = 0; - - /* remainder is in n0 >> lsh */ - } else { - /* (q1 q0) = (n1 n0) / (0 d0) */ - - if (b.lo == 0) /* user wants to divide by zero! */ - b.lo = 1 / b.lo; /* so go ahead and crash */ - - lsh = CountLeadingZeros(b.lo); - - if (lsh == 0) { - /* - * From (n1 >= b.lo) - * && (the most significant bit of b.lo is set), - * conclude that - * (the most significant bit of n1 is set) - * && (the leading quotient digit q1 = 1). - * - * This special case is necessary, not an optimization - * (Shifts counts of 32 are undefined). - */ - n1 -= b.lo; - q1 = 1; - } else { - /* - * Normalize. - */ - rsh = 32 - lsh; - - b.lo = b.lo << lsh; - n2 = n1 >> rsh; - n1 = (n1 << lsh) | (n0 >> rsh); - n0 = n0 << lsh; - - a.lo = n1, a.hi = n2; - norm_udivmod32(&q1, &n1, a, b.lo); - } - - /* n1 != b.lo... */ - - a.lo = n0, a.hi = n1; - norm_udivmod32(&q0, &n0, a, b.lo); - - /* remainder in n0 >> lsh */ - } - - if (rp) { - rp->lo = n0 >> lsh; - rp->hi = 0; - } - } else { - if (b.hi > n1) { - /* (0 0) = (n1 n0) / (D1 d0) */ - - q0 = 0; - q1 = 0; - - /* remainder in (n1 n0) */ - if (rp) { - rp->lo = n0; - rp->hi = n1; - } - } else { - /* (0 q0) = (n1 n0) / (d1 d0) */ - - lsh = CountLeadingZeros(b.hi); - if (lsh == 0) { - /* - * From (n1 >= b.hi) - * && (the most significant bit of b.hi is set), - * conclude that - * (the most significant bit of n1 is set) - * && (the quotient digit q0 = 0 or 1). - * - * This special case is necessary, not an optimization. - */ - - /* - * The condition on the next line takes advantage of that - * n1 >= b.hi (true due to control flow). - */ - if (n1 > b.hi || n0 >= b.lo) { - q0 = 1; - a.lo = n0, a.hi = n1; - JSLL_SUB(a, a, b); - } else { - q0 = 0; - } - q1 = 0; - - if (rp) { - rp->lo = n0; - rp->hi = n1; - } - } else { - JSInt64 m; - - /* - * Normalize. - */ - rsh = 32 - lsh; - - b.hi = (b.hi << lsh) | (b.lo >> rsh); - b.lo = b.lo << lsh; - n2 = n1 >> rsh; - n1 = (n1 << lsh) | (n0 >> rsh); - n0 = n0 << lsh; - - a.lo = n1, a.hi = n2; - norm_udivmod32(&q0, &n1, a, b.hi); - JSLL_MUL32(m, q0, b.lo); - - if ((m.hi > n1) || ((m.hi == n1) && (m.lo > n0))) { - q0--; - JSLL_SUB(m, m, b); - } - - q1 = 0; - - /* Remainder is ((n1 n0) - (m1 m0)) >> lsh */ - if (rp) { - a.lo = n0, a.hi = n1; - JSLL_SUB(a, a, m); - rp->lo = (a.hi << rsh) | (a.lo >> lsh); - rp->hi = a.hi >> lsh; - } - } - } - } - - if (qp) { - qp->lo = q0; - qp->hi = q1; - } -} -#endif /* !JS_HAVE_LONG_LONG */ diff --git a/spidermonkey/src/jslong.h b/spidermonkey/src/jslong.h deleted file mode 100644 index 059cf00..0000000 --- a/spidermonkey/src/jslong.h +++ /dev/null @@ -1,437 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* -** File: jslong.h -** Description: Portable access to 64 bit numerics -** -** Long-long (64-bit signed integer type) support. Some C compilers -** don't support 64 bit integers yet, so we use these macros to -** support both machines that do and don't. -**/ -#ifndef jslong_h___ -#define jslong_h___ - -#include "jstypes.h" - -JS_BEGIN_EXTERN_C - -/*********************************************************************** -** DEFINES: JSLL_MaxInt -** JSLL_MinInt -** JSLL_Zero -** DESCRIPTION: -** Various interesting constants and static variable -** initializer -***********************************************************************/ -#ifdef HAVE_WATCOM_BUG_2 -JSInt64 __pascal __loadds __export - JSLL_MaxInt(void); -JSInt64 __pascal __loadds __export - JSLL_MinInt(void); -JSInt64 __pascal __loadds __export - JSLL_Zero(void); -#else -extern JS_PUBLIC_API(JSInt64) JSLL_MaxInt(void); -extern JS_PUBLIC_API(JSInt64) JSLL_MinInt(void); -extern JS_PUBLIC_API(JSInt64) JSLL_Zero(void); -#endif - -#define JSLL_MAXINT JSLL_MaxInt() -#define JSLL_MININT JSLL_MinInt() -#define JSLL_ZERO JSLL_Zero() - -#ifdef JS_HAVE_LONG_LONG - -#if JS_BYTES_PER_LONG == 8 -#define JSLL_INIT(hi, lo) ((hi ## L << 32) + lo ## L) -#elif (defined(WIN32) || defined(WIN16)) && !defined(__GNUC__) -#define JSLL_INIT(hi, lo) ((hi ## i64 << 32) + lo ## i64) -#else -#define JSLL_INIT(hi, lo) ((hi ## LL << 32) + lo ## LL) -#endif - -/*********************************************************************** -** MACROS: JSLL_* -** DESCRIPTION: -** The following macros define portable access to the 64 bit -** math facilities. -** -***********************************************************************/ - -/*********************************************************************** -** MACROS: JSLL_ -** -** JSLL_IS_ZERO Test for zero -** JSLL_EQ Test for equality -** JSLL_NE Test for inequality -** JSLL_GE_ZERO Test for zero or positive -** JSLL_CMP Compare two values -***********************************************************************/ -#define JSLL_IS_ZERO(a) ((a) == 0) -#define JSLL_EQ(a, b) ((a) == (b)) -#define JSLL_NE(a, b) ((a) != (b)) -#define JSLL_GE_ZERO(a) ((a) >= 0) -#define JSLL_CMP(a, op, b) ((JSInt64)(a) op (JSInt64)(b)) -#define JSLL_UCMP(a, op, b) ((JSUint64)(a) op (JSUint64)(b)) - -/*********************************************************************** -** MACROS: JSLL_ -** -** JSLL_AND Logical and -** JSLL_OR Logical or -** JSLL_XOR Logical exclusion -** JSLL_OR2 A disgusting deviation -** JSLL_NOT Negation (one's compliment) -***********************************************************************/ -#define JSLL_AND(r, a, b) ((r) = (a) & (b)) -#define JSLL_OR(r, a, b) ((r) = (a) | (b)) -#define JSLL_XOR(r, a, b) ((r) = (a) ^ (b)) -#define JSLL_OR2(r, a) ((r) = (r) | (a)) -#define JSLL_NOT(r, a) ((r) = ~(a)) - -/*********************************************************************** -** MACROS: JSLL_ -** -** JSLL_NEG Negation (two's compliment) -** JSLL_ADD Summation (two's compliment) -** JSLL_SUB Difference (two's compliment) -***********************************************************************/ -#define JSLL_NEG(r, a) ((r) = -(a)) -#define JSLL_ADD(r, a, b) ((r) = (a) + (b)) -#define JSLL_SUB(r, a, b) ((r) = (a) - (b)) - -/*********************************************************************** -** MACROS: JSLL_ -** -** JSLL_MUL Product (two's compliment) -** JSLL_DIV Quotient (two's compliment) -** JSLL_MOD Modulus (two's compliment) -***********************************************************************/ -#define JSLL_MUL(r, a, b) ((r) = (a) * (b)) -#define JSLL_DIV(r, a, b) ((r) = (a) / (b)) -#define JSLL_MOD(r, a, b) ((r) = (a) % (b)) - -/*********************************************************************** -** MACROS: JSLL_ -** -** JSLL_SHL Shift left [0..64] bits -** JSLL_SHR Shift right [0..64] bits with sign extension -** JSLL_USHR Unsigned shift right [0..64] bits -** JSLL_ISHL Signed shift left [0..64] bits -***********************************************************************/ -#define JSLL_SHL(r, a, b) ((r) = (JSInt64)(a) << (b)) -#define JSLL_SHR(r, a, b) ((r) = (JSInt64)(a) >> (b)) -#define JSLL_USHR(r, a, b) ((r) = (JSUint64)(a) >> (b)) -#define JSLL_ISHL(r, a, b) ((r) = (JSInt64)(a) << (b)) - -/*********************************************************************** -** MACROS: JSLL_ -** -** JSLL_L2I Convert to signed 32 bit -** JSLL_L2UI Convert to unsigned 32 bit -** JSLL_L2F Convert to floating point -** JSLL_L2D Convert to floating point -** JSLL_I2L Convert signed to 64 bit -** JSLL_UI2L Convert unsigned to 64 bit -** JSLL_F2L Convert float to 64 bit -** JSLL_D2L Convert float to 64 bit -***********************************************************************/ -#define JSLL_L2I(i, l) ((i) = (JSInt32)(l)) -#define JSLL_L2UI(ui, l) ((ui) = (JSUint32)(l)) -#define JSLL_L2F(f, l) ((f) = (JSFloat64)(l)) -#define JSLL_L2D(d, l) ((d) = (JSFloat64)(l)) - -#define JSLL_I2L(l, i) ((l) = (JSInt64)(i)) -#define JSLL_UI2L(l, ui) ((l) = (JSInt64)(ui)) -#define JSLL_F2L(l, f) ((l) = (JSInt64)(f)) -#define JSLL_D2L(l, d) ((l) = (JSInt64)(d)) - -/*********************************************************************** -** MACROS: JSLL_UDIVMOD -** DESCRIPTION: -** Produce both a quotient and a remainder given an unsigned -** INPUTS: JSUint64 a: The dividend of the operation -** JSUint64 b: The quotient of the operation -** OUTPUTS: JSUint64 *qp: pointer to quotient -** JSUint64 *rp: pointer to remainder -***********************************************************************/ -#define JSLL_UDIVMOD(qp, rp, a, b) \ - (*(qp) = ((JSUint64)(a) / (b)), \ - *(rp) = ((JSUint64)(a) % (b))) - -#else /* !JS_HAVE_LONG_LONG */ - -#ifdef IS_LITTLE_ENDIAN -#define JSLL_INIT(hi, lo) {JS_INT32(lo), JS_INT32(hi)} -#else -#define JSLL_INIT(hi, lo) {JS_INT32(hi), JS_INT32(lo)} -#endif - -#define JSLL_IS_ZERO(a) (((a).hi == 0) && ((a).lo == 0)) -#define JSLL_EQ(a, b) (((a).hi == (b).hi) && ((a).lo == (b).lo)) -#define JSLL_NE(a, b) (((a).hi != (b).hi) || ((a).lo != (b).lo)) -#define JSLL_GE_ZERO(a) (((a).hi >> 31) == 0) - -#ifdef DEBUG -#define JSLL_CMP(a, op, b) (JS_ASSERT((#op)[1] != '='), JSLL_REAL_CMP(a, op, b)) -#define JSLL_UCMP(a, op, b) (JS_ASSERT((#op)[1] != '='), JSLL_REAL_UCMP(a, op, b)) -#else -#define JSLL_CMP(a, op, b) JSLL_REAL_CMP(a, op, b) -#define JSLL_UCMP(a, op, b) JSLL_REAL_UCMP(a, op, b) -#endif - -#define JSLL_REAL_CMP(a,op,b) (((JSInt32)(a).hi op (JSInt32)(b).hi) || \ - (((a).hi == (b).hi) && ((a).lo op (b).lo))) -#define JSLL_REAL_UCMP(a,op,b) (((a).hi op (b).hi) || \ - (((a).hi == (b).hi) && ((a).lo op (b).lo))) - -#define JSLL_AND(r, a, b) ((r).lo = (a).lo & (b).lo, \ - (r).hi = (a).hi & (b).hi) -#define JSLL_OR(r, a, b) ((r).lo = (a).lo | (b).lo, \ - (r).hi = (a).hi | (b).hi) -#define JSLL_XOR(r, a, b) ((r).lo = (a).lo ^ (b).lo, \ - (r).hi = (a).hi ^ (b).hi) -#define JSLL_OR2(r, a) ((r).lo = (r).lo | (a).lo, \ - (r).hi = (r).hi | (a).hi) -#define JSLL_NOT(r, a) ((r).lo = ~(a).lo, \ - (r).hi = ~(a).hi) - -#define JSLL_NEG(r, a) ((r).lo = -(JSInt32)(a).lo, \ - (r).hi = -(JSInt32)(a).hi - ((r).lo != 0)) -#define JSLL_ADD(r, a, b) { \ - JSInt64 _a, _b; \ - _a = a; _b = b; \ - (r).lo = _a.lo + _b.lo; \ - (r).hi = _a.hi + _b.hi + ((r).lo < _b.lo); \ -} - -#define JSLL_SUB(r, a, b) { \ - JSInt64 _a, _b; \ - _a = a; _b = b; \ - (r).lo = _a.lo - _b.lo; \ - (r).hi = _a.hi - _b.hi - (_a.lo < _b.lo); \ -} - -#define JSLL_MUL(r, a, b) { \ - JSInt64 _a, _b; \ - _a = a; _b = b; \ - JSLL_MUL32(r, _a.lo, _b.lo); \ - (r).hi += _a.hi * _b.lo + _a.lo * _b.hi; \ -} - -#define jslo16(a) ((a) & JS_BITMASK(16)) -#define jshi16(a) ((a) >> 16) - -#define JSLL_MUL32(r, a, b) { \ - JSUint32 _a1, _a0, _b1, _b0, _y0, _y1, _y2, _y3; \ - _a1 = jshi16(a), _a0 = jslo16(a); \ - _b1 = jshi16(b), _b0 = jslo16(b); \ - _y0 = _a0 * _b0; \ - _y1 = _a0 * _b1; \ - _y2 = _a1 * _b0; \ - _y3 = _a1 * _b1; \ - _y1 += jshi16(_y0); /* can't carry */ \ - _y1 += _y2; /* might carry */ \ - if (_y1 < _y2) \ - _y3 += (JSUint32)(JS_BIT(16)); /* propagate */ \ - (r).lo = (jslo16(_y1) << 16) + jslo16(_y0); \ - (r).hi = _y3 + jshi16(_y1); \ -} - -#define JSLL_UDIVMOD(qp, rp, a, b) jsll_udivmod(qp, rp, a, b) - -extern JS_PUBLIC_API(void) jsll_udivmod(JSUint64 *qp, JSUint64 *rp, JSUint64 a, JSUint64 b); - -#define JSLL_DIV(r, a, b) { \ - JSInt64 _a, _b; \ - JSUint32 _negative = (JSInt32)(a).hi < 0; \ - if (_negative) { \ - JSLL_NEG(_a, a); \ - } else { \ - _a = a; \ - } \ - if ((JSInt32)(b).hi < 0) { \ - _negative ^= 1; \ - JSLL_NEG(_b, b); \ - } else { \ - _b = b; \ - } \ - JSLL_UDIVMOD(&(r), 0, _a, _b); \ - if (_negative) \ - JSLL_NEG(r, r); \ -} - -#define JSLL_MOD(r, a, b) { \ - JSInt64 _a, _b; \ - JSUint32 _negative = (JSInt32)(a).hi < 0; \ - if (_negative) { \ - JSLL_NEG(_a, a); \ - } else { \ - _a = a; \ - } \ - if ((JSInt32)(b).hi < 0) { \ - JSLL_NEG(_b, b); \ - } else { \ - _b = b; \ - } \ - JSLL_UDIVMOD(0, &(r), _a, _b); \ - if (_negative) \ - JSLL_NEG(r, r); \ -} - -#define JSLL_SHL(r, a, b) { \ - if (b) { \ - JSInt64 _a; \ - _a = a; \ - if ((b) < 32) { \ - (r).lo = _a.lo << ((b) & 31); \ - (r).hi = (_a.hi << ((b) & 31)) | (_a.lo >> (32 - (b))); \ - } else { \ - (r).lo = 0; \ - (r).hi = _a.lo << ((b) & 31); \ - } \ - } else { \ - (r) = (a); \ - } \ -} - -/* a is an JSInt32, b is JSInt32, r is JSInt64 */ -#define JSLL_ISHL(r, a, b) { \ - if (b) { \ - JSInt64 _a; \ - _a.lo = (a); \ - _a.hi = 0; \ - if ((b) < 32) { \ - (r).lo = (a) << ((b) & 31); \ - (r).hi = ((a) >> (32 - (b))); \ - } else { \ - (r).lo = 0; \ - (r).hi = (a) << ((b) & 31); \ - } \ - } else { \ - (r).lo = (a); \ - (r).hi = 0; \ - } \ -} - -#define JSLL_SHR(r, a, b) { \ - if (b) { \ - JSInt64 _a; \ - _a = a; \ - if ((b) < 32) { \ - (r).lo = (_a.hi << (32 - (b))) | (_a.lo >> ((b) & 31)); \ - (r).hi = (JSInt32)_a.hi >> ((b) & 31); \ - } else { \ - (r).lo = (JSInt32)_a.hi >> ((b) & 31); \ - (r).hi = (JSInt32)_a.hi >> 31; \ - } \ - } else { \ - (r) = (a); \ - } \ -} - -#define JSLL_USHR(r, a, b) { \ - if (b) { \ - JSInt64 _a; \ - _a = a; \ - if ((b) < 32) { \ - (r).lo = (_a.hi << (32 - (b))) | (_a.lo >> ((b) & 31)); \ - (r).hi = _a.hi >> ((b) & 31); \ - } else { \ - (r).lo = _a.hi >> ((b) & 31); \ - (r).hi = 0; \ - } \ - } else { \ - (r) = (a); \ - } \ -} - -#define JSLL_L2I(i, l) ((i) = (l).lo) -#define JSLL_L2UI(ui, l) ((ui) = (l).lo) -#define JSLL_L2F(f, l) { double _d; JSLL_L2D(_d, l); (f) = (JSFloat64)_d; } - -#define JSLL_L2D(d, l) { \ - int _negative; \ - JSInt64 _absval; \ - \ - _negative = (l).hi >> 31; \ - if (_negative) { \ - JSLL_NEG(_absval, l); \ - } else { \ - _absval = l; \ - } \ - (d) = (double)_absval.hi * 4.294967296e9 + _absval.lo; \ - if (_negative) \ - (d) = -(d); \ -} - -#define JSLL_I2L(l, i) { JSInt32 _i = (i) >> 31; (l).lo = (i); (l).hi = _i; } -#define JSLL_UI2L(l, ui) ((l).lo = (ui), (l).hi = 0) -#define JSLL_F2L(l, f) { double _d = (double)f; JSLL_D2L(l, _d); } - -#define JSLL_D2L(l, d) { \ - int _negative; \ - double _absval, _d_hi; \ - JSInt64 _lo_d; \ - \ - _negative = ((d) < 0); \ - _absval = _negative ? -(d) : (d); \ - \ - (l).hi = _absval / 4.294967296e9; \ - (l).lo = 0; \ - JSLL_L2D(_d_hi, l); \ - _absval -= _d_hi; \ - _lo_d.hi = 0; \ - if (_absval < 0) { \ - _lo_d.lo = -_absval; \ - JSLL_SUB(l, l, _lo_d); \ - } else { \ - _lo_d.lo = _absval; \ - JSLL_ADD(l, l, _lo_d); \ - } \ - \ - if (_negative) \ - JSLL_NEG(l, l); \ -} - -#endif /* !JS_HAVE_LONG_LONG */ - -JS_END_EXTERN_C - -#endif /* jslong_h___ */ diff --git a/spidermonkey/src/jsmath.c b/spidermonkey/src/jsmath.c deleted file mode 100644 index 2062916..0000000 --- a/spidermonkey/src/jsmath.c +++ /dev/null @@ -1,514 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS math package. - */ -#include "jsstddef.h" -#include "jslibmath.h" -#include -#include "jstypes.h" -#include "jslong.h" -#include "prmjtime.h" -#include "jsapi.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jslock.h" -#include "jsmath.h" -#include "jsnum.h" -#include "jsobj.h" - -#ifndef M_E -#define M_E 2.7182818284590452354 -#endif -#ifndef M_LOG2E -#define M_LOG2E 1.4426950408889634074 -#endif -#ifndef M_LOG10E -#define M_LOG10E 0.43429448190325182765 -#endif -#ifndef M_LN2 -#define M_LN2 0.69314718055994530942 -#endif -#ifndef M_LN10 -#define M_LN10 2.30258509299404568402 -#endif -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif -#ifndef M_SQRT2 -#define M_SQRT2 1.41421356237309504880 -#endif -#ifndef M_SQRT1_2 -#define M_SQRT1_2 0.70710678118654752440 -#endif - -static JSConstDoubleSpec math_constants[] = { - {M_E, "E", 0, {0,0,0}}, - {M_LOG2E, "LOG2E", 0, {0,0,0}}, - {M_LOG10E, "LOG10E", 0, {0,0,0}}, - {M_LN2, "LN2", 0, {0,0,0}}, - {M_LN10, "LN10", 0, {0,0,0}}, - {M_PI, "PI", 0, {0,0,0}}, - {M_SQRT2, "SQRT2", 0, {0,0,0}}, - {M_SQRT1_2, "SQRT1_2", 0, {0,0,0}}, - {0,0,0,{0,0,0}} -}; - -JSClass js_MathClass = { - js_Math_str, - JSCLASS_HAS_CACHED_PROTO(JSProto_Math), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -static JSBool -math_abs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_fabs(x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_acos(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_acos(x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_asin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_asin(x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_atan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_atan(x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_atan2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, y, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - if (!js_ValueToNumber(cx, argv[1], &y)) - return JS_FALSE; -#if !JS_USE_FDLIBM_MATH && defined(_MSC_VER) - /* - * MSVC's atan2 does not yield the result demanded by ECMA when both x - * and y are infinite. - * - The result is a multiple of pi/4. - * - The sign of x determines the sign of the result. - * - The sign of y determines the multiplicator, 1 or 3. - */ - if (JSDOUBLE_IS_INFINITE(x) && JSDOUBLE_IS_INFINITE(y)) { - z = fd_copysign(M_PI / 4, x); - if (y < 0) - z *= 3; - return js_NewDoubleValue(cx, z, rval); - } -#endif - z = fd_atan2(x, y); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_ceil(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_ceil(x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_cos(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_cos(x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_exp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; -#ifdef _WIN32 - if (!JSDOUBLE_IS_NaN(x)) { - if (x == *cx->runtime->jsPositiveInfinity) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity); - return JS_TRUE; - } - if (x == *cx->runtime->jsNegativeInfinity) { - *rval = JSVAL_ZERO; - return JS_TRUE; - } - } -#endif - z = fd_exp(x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_floor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_floor(x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_log(x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_max(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z = *cx->runtime->jsNegativeInfinity; - uintN i; - - if (argc == 0) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNegativeInfinity); - return JS_TRUE; - } - for (i = 0; i < argc; i++) { - if (!js_ValueToNumber(cx, argv[i], &x)) - return JS_FALSE; - if (JSDOUBLE_IS_NaN(x)) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); - return JS_TRUE; - } - if (x == 0 && x == z && fd_copysign(1.0, z) == -1) - z = x; - else - z = (x > z) ? x : z; - } - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_min(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z = *cx->runtime->jsPositiveInfinity; - uintN i; - - if (argc == 0) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity); - return JS_TRUE; - } - for (i = 0; i < argc; i++) { - if (!js_ValueToNumber(cx, argv[i], &x)) - return JS_FALSE; - if (JSDOUBLE_IS_NaN(x)) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); - return JS_TRUE; - } - if (x == 0 && x == z && fd_copysign(1.0,x) == -1) - z = x; - else - z = (x < z) ? x : z; - } - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_pow(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, y, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - if (!js_ValueToNumber(cx, argv[1], &y)) - return JS_FALSE; -#if !JS_USE_FDLIBM_MATH - /* - * Because C99 and ECMA specify different behavior for pow(), - * we need to wrap the libm call to make it ECMA compliant. - */ - if (!JSDOUBLE_IS_FINITE(y) && (x == 1.0 || x == -1.0)) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); - return JS_TRUE; - } - /* pow(x, +-0) is always 1, even for x = NaN. */ - if (y == 0) { - *rval = JSVAL_ONE; - return JS_TRUE; - } -#endif - z = fd_pow(x, y); - return js_NewNumberValue(cx, z, rval); -} - -/* - * Math.random() support, lifted from java.util.Random.java. - */ -static void -random_setSeed(JSRuntime *rt, int64 seed) -{ - int64 tmp; - - JSLL_I2L(tmp, 1000); - JSLL_DIV(seed, seed, tmp); - JSLL_XOR(tmp, seed, rt->rngMultiplier); - JSLL_AND(rt->rngSeed, tmp, rt->rngMask); -} - -static void -random_init(JSRuntime *rt) -{ - int64 tmp, tmp2; - - /* Do at most once. */ - if (rt->rngInitialized) - return; - rt->rngInitialized = JS_TRUE; - - /* rt->rngMultiplier = 0x5DEECE66DL */ - JSLL_ISHL(tmp, 0x5, 32); - JSLL_UI2L(tmp2, 0xDEECE66DL); - JSLL_OR(rt->rngMultiplier, tmp, tmp2); - - /* rt->rngAddend = 0xBL */ - JSLL_I2L(rt->rngAddend, 0xBL); - - /* rt->rngMask = (1L << 48) - 1 */ - JSLL_I2L(tmp, 1); - JSLL_SHL(tmp2, tmp, 48); - JSLL_SUB(rt->rngMask, tmp2, tmp); - - /* rt->rngDscale = (jsdouble)(1L << 53) */ - JSLL_SHL(tmp2, tmp, 53); - JSLL_L2D(rt->rngDscale, tmp2); - - /* Finally, set the seed from current time. */ - random_setSeed(rt, PRMJ_Now()); -} - -static uint32 -random_next(JSRuntime *rt, int bits) -{ - int64 nextseed, tmp; - uint32 retval; - - JSLL_MUL(nextseed, rt->rngSeed, rt->rngMultiplier); - JSLL_ADD(nextseed, nextseed, rt->rngAddend); - JSLL_AND(nextseed, nextseed, rt->rngMask); - rt->rngSeed = nextseed; - JSLL_USHR(tmp, nextseed, 48 - bits); - JSLL_L2I(retval, tmp); - return retval; -} - -static jsdouble -random_nextDouble(JSRuntime *rt) -{ - int64 tmp, tmp2; - jsdouble d; - - JSLL_ISHL(tmp, random_next(rt, 26), 27); - JSLL_UI2L(tmp2, random_next(rt, 27)); - JSLL_ADD(tmp, tmp, tmp2); - JSLL_L2D(d, tmp); - return d / rt->rngDscale; -} - -static JSBool -math_random(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSRuntime *rt; - jsdouble z; - - rt = cx->runtime; - JS_LOCK_RUNTIME(rt); - random_init(rt); - z = random_nextDouble(rt); - JS_UNLOCK_RUNTIME(rt); - return js_NewNumberValue(cx, z, rval); -} - -#if defined _WIN32 && !defined WINCE && _MSC_VER < 1400 -/* Try to work around apparent _copysign bustage in VC6 and VC7. */ -double -js_copysign(double x, double y) -{ - jsdpun xu, yu; - - xu.d = x; - yu.d = y; - xu.s.hi &= ~JSDOUBLE_HI32_SIGNBIT; - xu.s.hi |= yu.s.hi & JSDOUBLE_HI32_SIGNBIT; - return xu.d; -} -#endif - -static JSBool -math_round(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_copysign(fd_floor(x + 0.5), x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_sin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_sin(x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_sqrt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_sqrt(x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_tan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_tan(x); - return js_NewNumberValue(cx, z, rval); -} - -#if JS_HAS_TOSOURCE -static JSBool -math_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - *rval = ATOM_KEY(CLASS_ATOM(cx, Math)); - return JS_TRUE; -} -#endif - -static JSFunctionSpec math_static_methods[] = { -#if JS_HAS_TOSOURCE - {js_toSource_str, math_toSource, 0, 0, 0}, -#endif - {"abs", math_abs, 1, 0, 0}, - {"acos", math_acos, 1, 0, 0}, - {"asin", math_asin, 1, 0, 0}, - {"atan", math_atan, 1, 0, 0}, - {"atan2", math_atan2, 2, 0, 0}, - {"ceil", math_ceil, 1, 0, 0}, - {"cos", math_cos, 1, 0, 0}, - {"exp", math_exp, 1, 0, 0}, - {"floor", math_floor, 1, 0, 0}, - {"log", math_log, 1, 0, 0}, - {"max", math_max, 2, 0, 0}, - {"min", math_min, 2, 0, 0}, - {"pow", math_pow, 2, 0, 0}, - {"random", math_random, 0, 0, 0}, - {"round", math_round, 1, 0, 0}, - {"sin", math_sin, 1, 0, 0}, - {"sqrt", math_sqrt, 1, 0, 0}, - {"tan", math_tan, 1, 0, 0}, - {0,0,0,0,0} -}; - -JSObject * -js_InitMathClass(JSContext *cx, JSObject *obj) -{ - JSObject *Math; - - Math = JS_DefineObject(cx, obj, js_Math_str, &js_MathClass, NULL, 0); - if (!Math) - return NULL; - if (!JS_DefineFunctions(cx, Math, math_static_methods)) - return NULL; - if (!JS_DefineConstDoubles(cx, Math, math_constants)) - return NULL; - return Math; -} diff --git a/spidermonkey/src/jsmath.h b/spidermonkey/src/jsmath.h deleted file mode 100644 index 1f60630..0000000 --- a/spidermonkey/src/jsmath.h +++ /dev/null @@ -1,57 +0,0 @@ -/* ***** 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 Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998-1999 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of 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 ***** */ - -/* -*- Mode: C; tab-width: 8 -*- - * Copyright (C) 1998-1999 Netscape Communications Corporation, All Rights Reserved. - */ - -#ifndef jsmath_h___ -#define jsmath_h___ -/* - * JS math functions. - */ - -JS_BEGIN_EXTERN_C - -extern JSClass js_MathClass; - -extern JSObject * -js_InitMathClass(JSContext *cx, JSObject *obj); - -JS_END_EXTERN_C - -#endif /* jsmath_h___ */ diff --git a/spidermonkey/src/jsnum.c b/spidermonkey/src/jsnum.c deleted file mode 100644 index 987619d..0000000 --- a/spidermonkey/src/jsnum.c +++ /dev/null @@ -1,1147 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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): - * IBM Corp. - * - * Alternatively, the contents of this file may be used under the terms of - * either of 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 ***** */ - -/* - * JS number type and wrapper class. - */ -#include "jsstddef.h" -#if defined(XP_WIN) || defined(XP_OS2) -#include -#endif -#include -#include -#include -#include -#include -#include "jstypes.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jsapi.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsdtoa.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsprf.h" -#include "jsstr.h" - -static JSBool -num_isNaN(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - *rval = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_NaN(x)); - return JS_TRUE; -} - -static JSBool -num_isFinite(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - *rval = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_FINITE(x)); - return JS_TRUE; -} - -static JSBool -num_parseFloat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - jsdouble d; - const jschar *bp, *ep; - - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - /* XXXbe js_strtod shouldn't require NUL termination */ - bp = js_UndependString(cx, str); - if (!bp) - return JS_FALSE; - if (!js_strtod(cx, bp, &ep, &d)) - return JS_FALSE; - if (ep == bp) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); - return JS_TRUE; - } - return js_NewNumberValue(cx, d, rval); -} - -/* See ECMA 15.1.2.2. */ -static JSBool -num_parseInt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsint radix; - JSString *str; - jsdouble d; - const jschar *bp, *ep; - - if (argc > 1) { - if (!js_ValueToECMAInt32(cx, argv[1], &radix)) - return JS_FALSE; - } else { - radix = 0; - } - if (radix != 0 && (radix < 2 || radix > 36)) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); - return JS_TRUE; - } - - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - /* XXXbe js_strtointeger shouldn't require NUL termination */ - bp = js_UndependString(cx, str); - if (!bp) - return JS_FALSE; - if (!js_strtointeger(cx, bp, &ep, radix, &d)) - return JS_FALSE; - if (ep == bp) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); - return JS_TRUE; - } - return js_NewNumberValue(cx, d, rval); -} - -const char js_Infinity_str[] = "Infinity"; -const char js_NaN_str[] = "NaN"; -const char js_isNaN_str[] = "isNaN"; -const char js_isFinite_str[] = "isFinite"; -const char js_parseFloat_str[] = "parseFloat"; -const char js_parseInt_str[] = "parseInt"; - -static JSFunctionSpec number_functions[] = { - {js_isNaN_str, num_isNaN, 1,0,0}, - {js_isFinite_str, num_isFinite, 1,0,0}, - {js_parseFloat_str, num_parseFloat, 1,0,0}, - {js_parseInt_str, num_parseInt, 2,0,0}, - {0,0,0,0,0} -}; - -JSClass js_NumberClass = { - js_Number_str, - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Number), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -static JSBool -Number(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble d; - jsval v; - - if (argc != 0) { - if (!js_ValueToNumber(cx, argv[0], &d)) - return JS_FALSE; - } else { - d = 0.0; - } - if (!js_NewNumberValue(cx, d, &v)) - return JS_FALSE; - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - *rval = v; - return JS_TRUE; - } - OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, v); - return JS_TRUE; -} - -#if JS_HAS_TOSOURCE -static JSBool -num_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval v; - jsdouble d; - char numBuf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr; - char buf[64]; - JSString *str; - - if (JSVAL_IS_NUMBER((jsval)obj)) { - v = (jsval)obj; - } else { - if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv)) - return JS_FALSE; - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - JS_ASSERT(JSVAL_IS_NUMBER(v)); - } - d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); - numStr = JS_dtostr(numBuf, sizeof numBuf, DTOSTR_STANDARD, 0, d); - if (!numStr) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - JS_snprintf(buf, sizeof buf, "(new %s(%s))", js_NumberClass.name, numStr); - str = JS_NewStringCopyZ(cx, buf); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} -#endif - -/* The buf must be big enough for MIN_INT to fit including '-' and '\0'. */ -static char * -IntToString(jsint i, char *buf, size_t bufSize) -{ - char *cp; - jsuint u; - - u = (i < 0) ? -i : i; - - cp = buf + bufSize; /* one past last buffer cell */ - *--cp = '\0'; /* null terminate the string to be */ - - /* - * Build the string from behind. We use multiply and subtraction - * instead of modulus because that's much faster. - */ - do { - jsuint newu = u / 10; - *--cp = (char)(u - newu * 10) + '0'; - u = newu; - } while (u != 0); - - if (i < 0) - *--cp = '-'; - - return cp; -} - -static JSBool -num_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval v; - jsdouble d; - jsint base; - JSString *str; - - if (JSVAL_IS_NUMBER((jsval)obj)) { - v = (jsval)obj; - } else { - if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv)) - return JS_FALSE; - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - JS_ASSERT(JSVAL_IS_NUMBER(v)); - } - d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); - base = 10; - if (argc != 0) { - if (!js_ValueToECMAInt32(cx, argv[0], &base)) - return JS_FALSE; - if (base < 2 || base > 36) { - char numBuf[12]; - char *numStr = IntToString(base, numBuf, sizeof numBuf); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_RADIX, - numStr); - return JS_FALSE; - } - } - if (base == 10) { - str = js_NumberToString(cx, d); - } else { - char *dStr = JS_dtobasestr(base, d); - if (!dStr) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - str = JS_NewStringCopyZ(cx, dStr); - free(dStr); - } - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -num_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - char thousandsLength, decimalLength; - const char *numGrouping, *tmpGroup; - JSRuntime *rt; - JSString *numStr, *str; - char *num, *buf, *dec, *end, *tmpSrc, *tmpDest; - int digits, size, remainder, nrepeat; - - /* - * Create the string, move back to bytes to make string twiddling - * a bit easier and so we can insert platform charset seperators. - */ - if (!num_toString(cx, obj, 0, argv, rval)) - return JS_FALSE; - JS_ASSERT(JSVAL_IS_STRING(*rval)); - numStr = JSVAL_TO_STRING(*rval); - num = js_GetStringBytes(cx->runtime, numStr); - - /* Find bit before the decimal. */ - dec = strchr(num, '.'); - digits = dec ? dec - num : (int)strlen(num); - end = num + digits; - - rt = cx->runtime; - thousandsLength = strlen(rt->thousandsSeparator); - decimalLength = strlen(rt->decimalSeparator); - - /* Figure out how long resulting string will be. */ - size = digits + (dec ? decimalLength + strlen(dec + 1) : 0); - - numGrouping = tmpGroup = rt->numGrouping; - remainder = digits; - if (*num == '-') - remainder--; - - while (*tmpGroup != CHAR_MAX && *tmpGroup != '\0') { - if (*tmpGroup >= remainder) - break; - size += thousandsLength; - remainder -= *tmpGroup; - tmpGroup++; - } - if (*tmpGroup == '\0' && *numGrouping != '\0') { - nrepeat = (remainder - 1) / tmpGroup[-1]; - size += thousandsLength * nrepeat; - remainder -= nrepeat * tmpGroup[-1]; - } else { - nrepeat = 0; - } - tmpGroup--; - - buf = (char *)JS_malloc(cx, size + 1); - if (!buf) - return JS_FALSE; - - tmpDest = buf; - tmpSrc = num; - - while (*tmpSrc == '-' || remainder--) - *tmpDest++ = *tmpSrc++; - while (tmpSrc < end) { - strcpy(tmpDest, rt->thousandsSeparator); - tmpDest += thousandsLength; - memcpy(tmpDest, tmpSrc, *tmpGroup); - tmpDest += *tmpGroup; - tmpSrc += *tmpGroup; - if (--nrepeat < 0) - tmpGroup--; - } - - if (dec) { - strcpy(tmpDest, rt->decimalSeparator); - tmpDest += decimalLength; - strcpy(tmpDest, dec + 1); - } else { - *tmpDest++ = '\0'; - } - - if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode) - return cx->localeCallbacks->localeToUnicode(cx, buf, rval); - - str = JS_NewString(cx, buf, size); - if (!str) { - JS_free(cx, buf); - return JS_FALSE; - } - - *rval = STRING_TO_JSVAL(str); - - return JS_TRUE; -} - -static JSBool -num_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - if (JSVAL_IS_NUMBER((jsval)obj)) { - *rval = (jsval)obj; - return JS_TRUE; - } - if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv)) - return JS_FALSE; - *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - return JS_TRUE; -} - - -#define MAX_PRECISION 100 - -static JSBool -num_to(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, JSDToStrMode zeroArgMode, - JSDToStrMode oneArgMode, jsint precisionMin, jsint precisionMax, jsint precisionOffset) -{ - jsval v; - jsdouble d, precision; - JSString *str; - char buf[DTOSTR_VARIABLE_BUFFER_SIZE(MAX_PRECISION+1)], *numStr; /* Use MAX_PRECISION+1 because precisionOffset can be 1 */ - - if (JSVAL_IS_NUMBER((jsval)obj)) { - v = (jsval)obj; - } else { - if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv)) - return JS_FALSE; - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - JS_ASSERT(JSVAL_IS_NUMBER(v)); - } - d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); - - if (JSVAL_IS_VOID(argv[0])) { - precision = 0.0; - oneArgMode = zeroArgMode; - } else { - if (!js_ValueToNumber(cx, argv[0], &precision)) - return JS_FALSE; - precision = js_DoubleToInteger(precision); - if (precision < precisionMin || precision > precisionMax) { - numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, precision); - if (!numStr) - JS_ReportOutOfMemory(cx); - else - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PRECISION_RANGE, numStr); - return JS_FALSE; - } - } - - numStr = JS_dtostr(buf, sizeof buf, oneArgMode, (jsint)precision + precisionOffset, d); - if (!numStr) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - str = JS_NewStringCopyZ(cx, numStr); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -num_toFixed(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - /* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */ - return num_to(cx, obj, argc, argv, rval, DTOSTR_FIXED, DTOSTR_FIXED, -20, MAX_PRECISION, 0); -} - -static JSBool -num_toExponential(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - /* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */ - return num_to(cx, obj, argc, argv, rval, DTOSTR_STANDARD_EXPONENTIAL, DTOSTR_EXPONENTIAL, 0, MAX_PRECISION, 1); -} - -static JSBool -num_toPrecision(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - /* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */ - return num_to(cx, obj, argc, argv, rval, DTOSTR_STANDARD, DTOSTR_PRECISION, 1, MAX_PRECISION, 0); -} - -static JSFunctionSpec number_methods[] = { -#if JS_HAS_TOSOURCE - {js_toSource_str, num_toSource, 0,JSFUN_THISP_NUMBER,0}, -#endif - {js_toString_str, num_toString, 0,JSFUN_THISP_NUMBER,0}, - {js_toLocaleString_str, num_toLocaleString, 0,JSFUN_THISP_NUMBER,0}, - {js_valueOf_str, num_valueOf, 0,JSFUN_THISP_NUMBER,0}, - {"toFixed", num_toFixed, 1,JSFUN_THISP_NUMBER,0}, - {"toExponential", num_toExponential, 1,JSFUN_THISP_NUMBER,0}, - {"toPrecision", num_toPrecision, 1,JSFUN_THISP_NUMBER,0}, - {0,0,0,0,0} -}; - -/* NB: Keep this in synch with number_constants[]. */ -enum nc_slot { - NC_NaN, - NC_POSITIVE_INFINITY, - NC_NEGATIVE_INFINITY, - NC_MAX_VALUE, - NC_MIN_VALUE, - NC_LIMIT -}; - -/* - * Some to most C compilers forbid spelling these at compile time, or barf - * if you try, so all but MAX_VALUE are set up by js_InitRuntimeNumberState - * using union jsdpun. - */ -static JSConstDoubleSpec number_constants[] = { - {0, js_NaN_str, 0,{0,0,0}}, - {0, "POSITIVE_INFINITY", 0,{0,0,0}}, - {0, "NEGATIVE_INFINITY", 0,{0,0,0}}, - {1.7976931348623157E+308, "MAX_VALUE", 0,{0,0,0}}, - {0, "MIN_VALUE", 0,{0,0,0}}, - {0,0,0,{0,0,0}} -}; - -static jsdouble NaN; - -#if (defined XP_WIN || defined XP_OS2) && \ - !defined WINCE && \ - !defined __MWERKS__ && \ - (defined _M_IX86 || \ - (defined __GNUC__ && !defined __MINGW32__)) - -/* - * Set the exception mask to mask all exceptions and set the FPU precision - * to 53 bit mantissa. - * On Alpha platform this is handled via Compiler option. - */ -#define FIX_FPU() _control87(MCW_EM | PC_53, MCW_EM | MCW_PC) - -#else - -#define FIX_FPU() ((void)0) - -#endif - -JSBool -js_InitRuntimeNumberState(JSContext *cx) -{ - JSRuntime *rt; - jsdpun u; - struct lconv *locale; - - rt = cx->runtime; - JS_ASSERT(!rt->jsNaN); - - FIX_FPU(); - - u.s.hi = JSDOUBLE_HI32_EXPMASK | JSDOUBLE_HI32_MANTMASK; - u.s.lo = 0xffffffff; - number_constants[NC_NaN].dval = NaN = u.d; - rt->jsNaN = js_NewDouble(cx, NaN, GCF_LOCK); - if (!rt->jsNaN) - return JS_FALSE; - - u.s.hi = JSDOUBLE_HI32_EXPMASK; - u.s.lo = 0x00000000; - number_constants[NC_POSITIVE_INFINITY].dval = u.d; - rt->jsPositiveInfinity = js_NewDouble(cx, u.d, GCF_LOCK); - if (!rt->jsPositiveInfinity) - return JS_FALSE; - - u.s.hi = JSDOUBLE_HI32_SIGNBIT | JSDOUBLE_HI32_EXPMASK; - u.s.lo = 0x00000000; - number_constants[NC_NEGATIVE_INFINITY].dval = u.d; - rt->jsNegativeInfinity = js_NewDouble(cx, u.d, GCF_LOCK); - if (!rt->jsNegativeInfinity) - return JS_FALSE; - - u.s.hi = 0; - u.s.lo = 1; - number_constants[NC_MIN_VALUE].dval = u.d; - - locale = localeconv(); - rt->thousandsSeparator = - JS_strdup(cx, locale->thousands_sep ? locale->thousands_sep : "'"); - rt->decimalSeparator = - JS_strdup(cx, locale->decimal_point ? locale->decimal_point : "."); - rt->numGrouping = - JS_strdup(cx, locale->grouping ? locale->grouping : "\3\0"); - - return rt->thousandsSeparator && rt->decimalSeparator && rt->numGrouping; -} - -void -js_FinishRuntimeNumberState(JSContext *cx) -{ - JSRuntime *rt = cx->runtime; - - js_UnlockGCThingRT(rt, rt->jsNaN); - js_UnlockGCThingRT(rt, rt->jsNegativeInfinity); - js_UnlockGCThingRT(rt, rt->jsPositiveInfinity); - - rt->jsNaN = NULL; - rt->jsNegativeInfinity = NULL; - rt->jsPositiveInfinity = NULL; - - JS_free(cx, (void *)rt->thousandsSeparator); - JS_free(cx, (void *)rt->decimalSeparator); - JS_free(cx, (void *)rt->numGrouping); - rt->thousandsSeparator = rt->decimalSeparator = rt->numGrouping = NULL; -} - -JSObject * -js_InitNumberClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *ctor; - JSRuntime *rt; - - /* XXX must do at least once per new thread, so do it per JSContext... */ - FIX_FPU(); - - if (!JS_DefineFunctions(cx, obj, number_functions)) - return NULL; - - proto = JS_InitClass(cx, obj, NULL, &js_NumberClass, Number, 1, - NULL, number_methods, NULL, NULL); - if (!proto || !(ctor = JS_GetConstructor(cx, proto))) - return NULL; - OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, JSVAL_ZERO); - if (!JS_DefineConstDoubles(cx, ctor, number_constants)) - return NULL; - - /* ECMA 15.1.1.1 */ - rt = cx->runtime; - if (!JS_DefineProperty(cx, obj, js_NaN_str, DOUBLE_TO_JSVAL(rt->jsNaN), - NULL, NULL, JSPROP_PERMANENT)) { - return NULL; - } - - /* ECMA 15.1.1.2 */ - if (!JS_DefineProperty(cx, obj, js_Infinity_str, - DOUBLE_TO_JSVAL(rt->jsPositiveInfinity), - NULL, NULL, JSPROP_PERMANENT)) { - return NULL; - } - return proto; -} - -jsdouble * -js_NewDouble(JSContext *cx, jsdouble d, uintN gcflag) -{ - jsdouble *dp; - - dp = (jsdouble *) js_NewGCThing(cx, gcflag | GCX_DOUBLE, sizeof(jsdouble)); - if (!dp) - return NULL; - *dp = d; - return dp; -} - -void -js_FinalizeDouble(JSContext *cx, jsdouble *dp) -{ - *dp = NaN; -} - -JSBool -js_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval) -{ - jsdouble *dp; - - dp = js_NewDouble(cx, d, 0); - if (!dp) - return JS_FALSE; - *rval = DOUBLE_TO_JSVAL(dp); - return JS_TRUE; -} - -JSBool -js_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval) -{ - jsint i; - - if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) { - *rval = INT_TO_JSVAL(i); - } else { - if (!js_NewDoubleValue(cx, d, rval)) - return JS_FALSE; - } - return JS_TRUE; -} - -JSObject * -js_NumberToObject(JSContext *cx, jsdouble d) -{ - JSObject *obj; - jsval v; - - obj = js_NewObject(cx, &js_NumberClass, NULL, NULL); - if (!obj) - return NULL; - if (!js_NewNumberValue(cx, d, &v)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; - } - OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, v); - return obj; -} - -JSString * -js_NumberToString(JSContext *cx, jsdouble d) -{ - jsint i; - char buf[DTOSTR_STANDARD_BUFFER_SIZE]; - char *numStr; - - if (JSDOUBLE_IS_INT(d, i)) { - numStr = IntToString(i, buf, sizeof buf); - } else { - numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, d); - if (!numStr) { - JS_ReportOutOfMemory(cx); - return NULL; - } - } - return JS_NewStringCopyZ(cx, numStr); -} - -JSBool -js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp) -{ - JSObject *obj; - JSString *str; - const jschar *bp, *ep; - - if (JSVAL_IS_OBJECT(v)) { - obj = JSVAL_TO_OBJECT(v); - if (!obj) { - *dp = 0; - return JS_TRUE; - } - if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_NUMBER, &v)) - return JS_FALSE; - } - if (JSVAL_IS_INT(v)) { - *dp = (jsdouble)JSVAL_TO_INT(v); - } else if (JSVAL_IS_DOUBLE(v)) { - *dp = *JSVAL_TO_DOUBLE(v); - } else if (JSVAL_IS_STRING(v)) { - str = JSVAL_TO_STRING(v); - /* - * Note that ECMA doesn't treat a string beginning with a '0' as an - * octal number here. This works because all such numbers will be - * interpreted as decimal by js_strtod and will never get passed to - * js_strtointeger (which would interpret them as octal). - */ - /* XXXbe js_strtod shouldn't require NUL termination */ - bp = js_UndependString(cx, str); - if (!bp) - return JS_FALSE; - if ((!js_strtod(cx, bp, &ep, dp) || - js_SkipWhiteSpace(ep) != bp + str->length) && - (!js_strtointeger(cx, bp, &ep, 0, dp) || - js_SkipWhiteSpace(ep) != bp + str->length)) { - goto badstr; - } - } else if (JSVAL_IS_BOOLEAN(v)) { - *dp = JSVAL_TO_BOOLEAN(v) ? 1 : 0; - } else { -badstr: - *dp = *cx->runtime->jsNaN; - } - return JS_TRUE; -} - -JSBool -js_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip) -{ - jsdouble d; - - if (!js_ValueToNumber(cx, v, &d)) - return JS_FALSE; - return js_DoubleToECMAInt32(cx, d, ip); -} - -JSBool -js_DoubleToECMAInt32(JSContext *cx, jsdouble d, int32 *ip) -{ - jsdouble two32 = 4294967296.0; - jsdouble two31 = 2147483648.0; - - if (!JSDOUBLE_IS_FINITE(d) || d == 0) { - *ip = 0; - return JS_TRUE; - } - d = fmod(d, two32); - d = (d >= 0) ? floor(d) : ceil(d) + two32; - if (d >= two31) - *ip = (int32)(d - two32); - else - *ip = (int32)d; - return JS_TRUE; -} - -JSBool -js_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip) -{ - jsdouble d; - - if (!js_ValueToNumber(cx, v, &d)) - return JS_FALSE; - return js_DoubleToECMAUint32(cx, d, ip); -} - -JSBool -js_DoubleToECMAUint32(JSContext *cx, jsdouble d, uint32 *ip) -{ - JSBool neg; - jsdouble two32 = 4294967296.0; - - if (!JSDOUBLE_IS_FINITE(d) || d == 0) { - *ip = 0; - return JS_TRUE; - } - - neg = (d < 0); - d = floor(neg ? -d : d); - d = neg ? -d : d; - - d = fmod(d, two32); - - d = (d >= 0) ? d : d + two32; - *ip = (uint32)d; - return JS_TRUE; -} - -JSBool -js_ValueToInt32(JSContext *cx, jsval v, int32 *ip) -{ - jsdouble d; - JSString *str; - - if (JSVAL_IS_INT(v)) { - *ip = JSVAL_TO_INT(v); - return JS_TRUE; - } - if (!js_ValueToNumber(cx, v, &d)) - return JS_FALSE; - if (JSDOUBLE_IS_NaN(d) || d <= -2147483649.0 || 2147483648.0 <= d) { - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CANT_CONVERT, JS_GetStringBytes(str)); - - } - return JS_FALSE; - } - *ip = (int32)floor(d + 0.5); /* Round to nearest */ - return JS_TRUE; -} - -JSBool -js_ValueToUint16(JSContext *cx, jsval v, uint16 *ip) -{ - jsdouble d; - jsuint i, m; - JSBool neg; - - if (!js_ValueToNumber(cx, v, &d)) - return JS_FALSE; - if (d == 0 || !JSDOUBLE_IS_FINITE(d)) { - *ip = 0; - return JS_TRUE; - } - i = (jsuint)d; - if ((jsdouble)i == d) { - *ip = (uint16)i; - return JS_TRUE; - } - neg = (d < 0); - d = floor(neg ? -d : d); - d = neg ? -d : d; - m = JS_BIT(16); - d = fmod(d, (double)m); - if (d < 0) - d += m; - *ip = (uint16) d; - return JS_TRUE; -} - -jsdouble -js_DoubleToInteger(jsdouble d) -{ - JSBool neg; - - if (d == 0) - return d; - if (!JSDOUBLE_IS_FINITE(d)) { - if (JSDOUBLE_IS_NaN(d)) - return 0; - return d; - } - neg = (d < 0); - d = floor(neg ? -d : d); - return neg ? -d : d; -} - - -JSBool -js_strtod(JSContext *cx, const jschar *s, const jschar **ep, jsdouble *dp) -{ - char cbuf[32]; - size_t i; - char *cstr, *istr, *estr; - JSBool negative; - jsdouble d; - const jschar *s1 = js_SkipWhiteSpace(s); - size_t length = js_strlen(s1); - - /* Use cbuf to avoid malloc */ - if (length >= sizeof cbuf) { - cstr = (char *) JS_malloc(cx, length + 1); - if (!cstr) - return JS_FALSE; - } else { - cstr = cbuf; - } - - for (i = 0; i <= length; i++) { - if (s1[i] >> 8) { - cstr[i] = 0; - break; - } - cstr[i] = (char)s1[i]; - } - - istr = cstr; - if ((negative = (*istr == '-')) != 0 || *istr == '+') - istr++; - if (!strncmp(istr, js_Infinity_str, sizeof js_Infinity_str - 1)) { - d = *(negative ? cx->runtime->jsNegativeInfinity : cx->runtime->jsPositiveInfinity); - estr = istr + 8; - } else { - int err; - d = JS_strtod(cstr, &estr, &err); - if (err == JS_DTOA_ENOMEM) { - JS_ReportOutOfMemory(cx); - if (cstr != cbuf) - JS_free(cx, cstr); - return JS_FALSE; - } - if (err == JS_DTOA_ERANGE) { - if (d == HUGE_VAL) - d = *cx->runtime->jsPositiveInfinity; - else if (d == -HUGE_VAL) - d = *cx->runtime->jsNegativeInfinity; - } -#ifdef HPUX - if (d == 0.0 && negative) { - /* - * "-0", "-1e-2000" come out as positive zero - * here on HPUX. Force a negative zero instead. - */ - JSDOUBLE_HI32(d) = JSDOUBLE_HI32_SIGNBIT; - JSDOUBLE_LO32(d) = 0; - } -#endif - } - - i = estr - cstr; - if (cstr != cbuf) - JS_free(cx, cstr); - *ep = i ? s1 + i : s; - *dp = d; - return JS_TRUE; -} - -struct BinaryDigitReader -{ - uintN base; /* Base of number; must be a power of 2 */ - uintN digit; /* Current digit value in radix given by base */ - uintN digitMask; /* Mask to extract the next bit from digit */ - const jschar *digits; /* Pointer to the remaining digits */ - const jschar *end; /* Pointer to first non-digit */ -}; - -/* Return the next binary digit from the number or -1 if done */ -static intN GetNextBinaryDigit(struct BinaryDigitReader *bdr) -{ - intN bit; - - if (bdr->digitMask == 0) { - uintN c; - - if (bdr->digits == bdr->end) - return -1; - - c = *bdr->digits++; - if ('0' <= c && c <= '9') - bdr->digit = c - '0'; - else if ('a' <= c && c <= 'z') - bdr->digit = c - 'a' + 10; - else bdr->digit = c - 'A' + 10; - bdr->digitMask = bdr->base >> 1; - } - bit = (bdr->digit & bdr->digitMask) != 0; - bdr->digitMask >>= 1; - return bit; -} - -JSBool -js_strtointeger(JSContext *cx, const jschar *s, const jschar **ep, jsint base, jsdouble *dp) -{ - JSBool negative; - jsdouble value; - const jschar *start; - const jschar *s1 = js_SkipWhiteSpace(s); - - if ((negative = (*s1 == '-')) != 0 || *s1 == '+') - s1++; - - if (base == 0) { - /* No base supplied, or some base that evaluated to 0. */ - if (*s1 == '0') { - /* It's either hex or octal; only increment char if str isn't '0' */ - if (s1[1] == 'X' || s1[1] == 'x') { /* Hex */ - s1 += 2; - base = 16; - } else { /* Octal */ - base = 8; - } - } else { - base = 10; /* Default to decimal. */ - } - } else if (base == 16 && *s1 == '0' && (s1[1] == 'X' || s1[1] == 'x')) { - /* If base is 16, ignore hex prefix. */ - s1 += 2; - } - - /* - * Done with the preliminaries; find some prefix of the string that's - * a number in the given base. - */ - start = s1; /* Mark - if string is empty, we return NaN. */ - value = 0.0; - for (;;) { - uintN digit; - jschar c = *s1; - if ('0' <= c && c <= '9') - digit = c - '0'; - else if ('a' <= c && c <= 'z') - digit = c - 'a' + 10; - else if ('A' <= c && c <= 'Z') - digit = c - 'A' + 10; - else - break; - if (digit >= (uintN)base) - break; - value = value * base + digit; - s1++; - } - - if (value >= 9007199254740992.0) { - if (base == 10) { - /* - * If we're accumulating a decimal number and the number is >= - * 2^53, then the result from the repeated multiply-add above may - * be inaccurate. Call JS_strtod to get the correct answer. - */ - size_t i; - size_t length = s1 - start; - char *cstr = (char *) JS_malloc(cx, length + 1); - char *estr; - int err=0; - - if (!cstr) - return JS_FALSE; - for (i = 0; i != length; i++) - cstr[i] = (char)start[i]; - cstr[length] = 0; - - value = JS_strtod(cstr, &estr, &err); - if (err == JS_DTOA_ENOMEM) { - JS_ReportOutOfMemory(cx); - JS_free(cx, cstr); - return JS_FALSE; - } - if (err == JS_DTOA_ERANGE && value == HUGE_VAL) - value = *cx->runtime->jsPositiveInfinity; - JS_free(cx, cstr); - } else if ((base & (base - 1)) == 0) { - /* - * The number may also be inaccurate for power-of-two bases. This - * happens if the addition in value * base + digit causes a round- - * down to an even least significant mantissa bit when the first - * dropped bit is a one. If any of the following digits in the - * number (which haven't been added in yet) are nonzero, then the - * correct action would have been to round up instead of down. An - * example occurs when reading the number 0x1000000000000081, which - * rounds to 0x1000000000000000 instead of 0x1000000000000100. - */ - struct BinaryDigitReader bdr; - intN bit, bit2; - intN j; - - bdr.base = base; - bdr.digitMask = 0; - bdr.digits = start; - bdr.end = s1; - value = 0.0; - - /* Skip leading zeros. */ - do { - bit = GetNextBinaryDigit(&bdr); - } while (bit == 0); - - if (bit == 1) { - /* Gather the 53 significant bits (including the leading 1) */ - value = 1.0; - for (j = 52; j; j--) { - bit = GetNextBinaryDigit(&bdr); - if (bit < 0) - goto done; - value = value*2 + bit; - } - /* bit2 is the 54th bit (the first dropped from the mantissa) */ - bit2 = GetNextBinaryDigit(&bdr); - if (bit2 >= 0) { - jsdouble factor = 2.0; - intN sticky = 0; /* sticky is 1 if any bit beyond the 54th is 1 */ - intN bit3; - - while ((bit3 = GetNextBinaryDigit(&bdr)) >= 0) { - sticky |= bit3; - factor *= 2; - } - value += bit2 & (bit | sticky); - value *= factor; - } - done:; - } - } - } - /* We don't worry about inaccurate numbers for any other base. */ - - if (s1 == start) { - *dp = 0.0; - *ep = s; - } else { - *dp = negative ? -value : value; - *ep = s1; - } - return JS_TRUE; -} diff --git a/spidermonkey/src/jsnum.h b/spidermonkey/src/jsnum.h deleted file mode 100644 index cd99501..0000000 --- a/spidermonkey/src/jsnum.h +++ /dev/null @@ -1,268 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsnum_h___ -#define jsnum_h___ -/* - * JS number (IEEE double) interface. - * - * JS numbers are optimistically stored in the top 31 bits of 32-bit integers, - * but floating point literals, results that overflow 31 bits, and division and - * modulus operands and results require a 64-bit IEEE double. These are GC'ed - * and pointed to by 32-bit jsvals on the stack and in object properties. - * - * When a JS number is treated as an object (followed by . or []), the runtime - * wraps it with a JSObject whose valueOf method returns the unwrapped number. - */ - -JS_BEGIN_EXTERN_C - -/* - * Stefan Hanske reports: - * ARM is a little endian architecture but 64 bit double words are stored - * differently: the 32 bit words are in little endian byte order, the two words - * are stored in big endian`s way. - */ - -#if defined(__arm) || defined(__arm32__) || defined(__arm26__) || defined(__arm__) -#define CPU_IS_ARM -#endif - -typedef union jsdpun { - struct { -#if defined(IS_LITTLE_ENDIAN) && !defined(CPU_IS_ARM) - uint32 lo, hi; -#else - uint32 hi, lo; -#endif - } s; - jsdouble d; -} jsdpun; - -#if (__GNUC__ == 2 && __GNUC_MINOR__ > 95) || __GNUC__ > 2 -/* - * This version of the macros is safe for the alias optimizations that gcc - * does, but uses gcc-specific extensions. - */ - -#define JSDOUBLE_HI32(x) (__extension__ ({ jsdpun u; u.d = (x); u.s.hi; })) -#define JSDOUBLE_LO32(x) (__extension__ ({ jsdpun u; u.d = (x); u.s.lo; })) -#define JSDOUBLE_SET_HI32(x, y) \ - (__extension__ ({ jsdpun u; u.d = (x); u.s.hi = (y); (x) = u.d; })) -#define JSDOUBLE_SET_LO32(x, y) \ - (__extension__ ({ jsdpun u; u.d = (x); u.s.lo = (y); (x) = u.d; })) - -#else /* not or old GNUC */ - -/* - * We don't know of any non-gcc compilers that perform alias optimization, - * so this code should work. - */ - -#if defined(IS_LITTLE_ENDIAN) && !defined(CPU_IS_ARM) -#define JSDOUBLE_HI32(x) (((uint32 *)&(x))[1]) -#define JSDOUBLE_LO32(x) (((uint32 *)&(x))[0]) -#else -#define JSDOUBLE_HI32(x) (((uint32 *)&(x))[0]) -#define JSDOUBLE_LO32(x) (((uint32 *)&(x))[1]) -#endif - -#define JSDOUBLE_SET_HI32(x, y) (JSDOUBLE_HI32(x)=(y)) -#define JSDOUBLE_SET_LO32(x, y) (JSDOUBLE_LO32(x)=(y)) - -#endif /* not or old GNUC */ - -#define JSDOUBLE_HI32_SIGNBIT 0x80000000 -#define JSDOUBLE_HI32_EXPMASK 0x7ff00000 -#define JSDOUBLE_HI32_MANTMASK 0x000fffff - -#define JSDOUBLE_IS_NaN(x) \ - ((JSDOUBLE_HI32(x) & JSDOUBLE_HI32_EXPMASK) == JSDOUBLE_HI32_EXPMASK && \ - (JSDOUBLE_LO32(x) || (JSDOUBLE_HI32(x) & JSDOUBLE_HI32_MANTMASK))) - -#define JSDOUBLE_IS_INFINITE(x) \ - ((JSDOUBLE_HI32(x) & ~JSDOUBLE_HI32_SIGNBIT) == JSDOUBLE_HI32_EXPMASK && \ - !JSDOUBLE_LO32(x)) - -#define JSDOUBLE_IS_FINITE(x) \ - ((JSDOUBLE_HI32(x) & JSDOUBLE_HI32_EXPMASK) != JSDOUBLE_HI32_EXPMASK) - -#define JSDOUBLE_IS_NEGZERO(d) (JSDOUBLE_HI32(d) == JSDOUBLE_HI32_SIGNBIT && \ - JSDOUBLE_LO32(d) == 0) - -/* - * JSDOUBLE_IS_INT first checks that d is neither NaN nor infinite, to avoid - * raising SIGFPE on platforms such as Alpha Linux, then (only if the cast is - * safe) leaves i as (jsint)d. This also avoid anomalous NaN floating point - * comparisons under MSVC. - */ -#define JSDOUBLE_IS_INT(d, i) (JSDOUBLE_IS_FINITE(d) \ - && !JSDOUBLE_IS_NEGZERO(d) \ - && ((d) == (i = (jsint)(d)))) - -#if defined(XP_WIN) -#define JSDOUBLE_COMPARE(LVAL, OP, RVAL, IFNAN) \ - ((JSDOUBLE_IS_NaN(LVAL) || JSDOUBLE_IS_NaN(RVAL)) \ - ? (IFNAN) \ - : (LVAL) OP (RVAL)) -#else -#define JSDOUBLE_COMPARE(LVAL, OP, RVAL, IFNAN) ((LVAL) OP (RVAL)) -#endif - -/* Initialize number constants and runtime state for the first context. */ -extern JSBool -js_InitRuntimeNumberState(JSContext *cx); - -extern void -js_FinishRuntimeNumberState(JSContext *cx); - -/* Initialize the Number class, returning its prototype object. */ -extern JSClass js_NumberClass; - -extern JSObject * -js_InitNumberClass(JSContext *cx, JSObject *obj); - -/* - * String constants for global function names, used in jsapi.c and jsnum.c. - */ -extern const char js_Infinity_str[]; -extern const char js_NaN_str[]; -extern const char js_isNaN_str[]; -extern const char js_isFinite_str[]; -extern const char js_parseFloat_str[]; -extern const char js_parseInt_str[]; - -/* GC-allocate a new JS number. */ -extern jsdouble * -js_NewDouble(JSContext *cx, jsdouble d, uintN gcflag); - -extern void -js_FinalizeDouble(JSContext *cx, jsdouble *dp); - -extern JSBool -js_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval); - -extern JSBool -js_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval); - -/* Construct a Number instance that wraps around d. */ -extern JSObject * -js_NumberToObject(JSContext *cx, jsdouble d); - -/* Convert a number to a GC'ed string. */ -extern JSString * -js_NumberToString(JSContext *cx, jsdouble d); - -/* - * Convert a value to a number, returning false after reporting any error, - * otherwise returning true with *dp set. - */ -extern JSBool -js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp); - -/* - * Convert a value or a double to an int32, according to the ECMA rules - * for ToInt32. - */ -extern JSBool -js_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip); - -extern JSBool -js_DoubleToECMAInt32(JSContext *cx, jsdouble d, int32 *ip); - -/* - * Convert a value or a double to a uint32, according to the ECMA rules - * for ToUint32. - */ -extern JSBool -js_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip); - -extern JSBool -js_DoubleToECMAUint32(JSContext *cx, jsdouble d, uint32 *ip); - -/* - * Convert a value to a number, then to an int32 if it fits by rounding to - * nearest; but failing with an error report if the double is out of range - * or unordered. - */ -extern JSBool -js_ValueToInt32(JSContext *cx, jsval v, int32 *ip); - -/* - * Convert a value to a number, then to a uint16 according to the ECMA rules - * for ToUint16. - */ -extern JSBool -js_ValueToUint16(JSContext *cx, jsval v, uint16 *ip); - -/* - * Convert a jsdouble to an integral number, stored in a jsdouble. - * If d is NaN, return 0. If d is an infinity, return it without conversion. - */ -extern jsdouble -js_DoubleToInteger(jsdouble d); - -/* - * Similar to strtod except that it replaces overflows with infinities of the - * correct sign, and underflows with zeros of the correct sign. Guaranteed to - * return the closest double number to the given input in dp. - * - * Also allows inputs of the form [+|-]Infinity, which produce an infinity of - * the appropriate sign. The case of the "Infinity" string must match exactly. - * If the string does not contain a number, set *ep to s and return 0.0 in dp. - * Return false if out of memory. - */ -extern JSBool -js_strtod(JSContext *cx, const jschar *s, const jschar **ep, jsdouble *dp); - -/* - * Similar to strtol except that it handles integers of arbitrary size. - * Guaranteed to return the closest double number to the given input when radix - * is 10 or a power of 2. Callers may see round-off errors for very large - * numbers of a different radix than 10 or a power of 2. - * - * If the string does not contain a number, set *ep to s and return 0.0 in dp. - * Return false if out of memory. - */ -extern JSBool -js_strtointeger(JSContext *cx, const jschar *s, const jschar **ep, jsint radix, jsdouble *dp); - -JS_END_EXTERN_C - -#endif /* jsnum_h___ */ diff --git a/spidermonkey/src/jsobj.c b/spidermonkey/src/jsobj.c deleted file mode 100644 index b552aca..0000000 --- a/spidermonkey/src/jsobj.c +++ /dev/null @@ -1,5035 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS object implementation. - */ -#include "jsstddef.h" -#include -#include -#include "jstypes.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsbit.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jshash.h" /* Added by JSIFY */ -#include "jsdhash.h" -#include "jsprf.h" -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jsbool.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" -#include "jsopcode.h" - -#include "jsdbgapi.h" /* whether or not JS_HAS_OBJ_WATCHPOINT */ - -#if JS_HAS_GENERATORS -#include "jsiter.h" -#endif - -#if JS_HAS_XML_SUPPORT -#include "jsxml.h" -#endif - -#if JS_HAS_XDR -#include "jsxdrapi.h" -#endif - -#ifdef JS_THREADSAFE -#define NATIVE_DROP_PROPERTY js_DropProperty - -extern void -js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop); -#else -#define NATIVE_DROP_PROPERTY NULL -#endif - -JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = { - js_NewObjectMap, js_DestroyObjectMap, - js_LookupProperty, js_DefineProperty, - js_GetProperty, js_SetProperty, - js_GetAttributes, js_SetAttributes, - js_DeleteProperty, js_DefaultValue, - js_Enumerate, js_CheckAccess, - NULL, NATIVE_DROP_PROPERTY, - js_Call, js_Construct, - NULL, js_HasInstance, - js_SetProtoOrParent, js_SetProtoOrParent, - js_Mark, js_Clear, - js_GetRequiredSlot, js_SetRequiredSlot -}; - -JSClass js_ObjectClass = { - js_Object_str, - JSCLASS_HAS_CACHED_PROTO(JSProto_Object), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -#if JS_HAS_OBJ_PROTO_PROP - -static JSBool -obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -static JSBool -obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -static JSBool -obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -static JSPropertySpec object_props[] = { - /* These two must come first; see object_props[slot].name usage below. */ - {js_proto_str, JSSLOT_PROTO, JSPROP_PERMANENT|JSPROP_SHARED, - obj_getSlot, obj_setSlot}, - {js_parent_str,JSSLOT_PARENT,JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED, - obj_getSlot, obj_setSlot}, - {js_count_str, 0, JSPROP_PERMANENT,obj_getCount, obj_getCount}, - {0,0,0,0,0} -}; - -/* NB: JSSLOT_PROTO and JSSLOT_PARENT are already indexes into object_props. */ -#define JSSLOT_COUNT 2 - -static JSBool -ReportStrictSlot(JSContext *cx, uint32 slot) -{ - if (slot == JSSLOT_PROTO) - return JS_TRUE; - return JS_ReportErrorFlagsAndNumber(cx, - JSREPORT_WARNING | JSREPORT_STRICT, - js_GetErrorMessage, NULL, - JSMSG_DEPRECATED_USAGE, - object_props[slot].name); -} - -static JSBool -obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - uint32 slot; - jsid propid; - JSAccessMode mode; - uintN attrs; - JSObject *pobj; - JSClass *clasp; - JSExtendedClass *xclasp; - - slot = (uint32) JSVAL_TO_INT(id); - if (id == INT_TO_JSVAL(JSSLOT_PROTO)) { - propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom); - mode = JSACC_PROTO; - } else { - propid = ATOM_TO_JSID(cx->runtime->atomState.parentAtom); - mode = JSACC_PARENT; - } - - /* Let OBJ_CHECK_ACCESS get the slot's value, based on the access mode. */ - if (!OBJ_CHECK_ACCESS(cx, obj, propid, mode, vp, &attrs)) - return JS_FALSE; - - pobj = JSVAL_TO_OBJECT(*vp); - if (pobj) { - clasp = OBJ_GET_CLASS(cx, pobj); - if (clasp == &js_CallClass || clasp == &js_BlockClass) { - /* Censor activations and lexical scopes per ECMA-262. */ - *vp = JSVAL_NULL; - } else if (clasp->flags & JSCLASS_IS_EXTENDED) { - xclasp = (JSExtendedClass *) clasp; - if (xclasp->outerObject) { - pobj = xclasp->outerObject(cx, pobj); - if (!pobj) - return JS_FALSE; - *vp = OBJECT_TO_JSVAL(pobj); - } - } - } - return JS_TRUE; -} - -static JSBool -obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSObject *pobj; - uint32 slot; - jsid propid; - uintN attrs; - - if (!JSVAL_IS_OBJECT(*vp)) - return JS_TRUE; - pobj = JSVAL_TO_OBJECT(*vp); - - if (pobj) { - /* - * Innerize pobj here to avoid sticking unwanted properties on the - * outer object. This ensures that any with statements only grant - * access to the inner object. - */ - OBJ_TO_INNER_OBJECT(cx, pobj); - if (!pobj) - return JS_FALSE; - } - slot = (uint32) JSVAL_TO_INT(id); - if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot)) - return JS_FALSE; - - /* __parent__ is readonly and permanent, only __proto__ may be set. */ - propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom); - if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_PROTO|JSACC_WRITE, vp, &attrs)) - return JS_FALSE; - - return js_SetProtoOrParent(cx, obj, slot, pobj); -} - -static JSBool -obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsval iter_state; - jsid num_properties; - JSBool ok; - - if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT)) - return JS_FALSE; - - /* Get the number of properties to enumerate. */ - iter_state = JSVAL_NULL; - ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties); - if (!ok) - goto out; - - if (!JSVAL_IS_INT(num_properties)) { - JS_ASSERT(0); - *vp = JSVAL_ZERO; - goto out; - } - *vp = num_properties; - -out: - if (iter_state != JSVAL_NULL) - ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0); - return ok; -} - -#else /* !JS_HAS_OBJ_PROTO_PROP */ - -#define object_props NULL - -#endif /* !JS_HAS_OBJ_PROTO_PROP */ - -JSBool -js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj) -{ - JSRuntime *rt; - JSObject *obj2, *oldproto; - JSScope *scope, *newscope; - - /* - * Serialize all proto and parent setting in order to detect cycles. - * We nest locks in this function, and only here, in the following orders: - * - * (1) rt->setSlotLock < pobj's scope lock; - * rt->setSlotLock < pobj's proto-or-parent's scope lock; - * rt->setSlotLock < pobj's grand-proto-or-parent's scope lock; - * etc... - * (2) rt->setSlotLock < obj's scope lock < pobj's scope lock. - * - * We avoid AB-BA deadlock by restricting obj from being on pobj's parent - * or proto chain (pobj may already be on obj's parent or proto chain; it - * could be moving up or down). We finally order obj with respect to pobj - * at the bottom of this routine (just before releasing rt->setSlotLock), - * by making pobj be obj's prototype or parent. - * - * After we have set the slot and released rt->setSlotLock, another call - * to js_SetProtoOrParent could nest locks according to the first order - * list above, but it cannot deadlock with any other thread. For there - * to be a deadlock, other parts of the engine would have to nest scope - * locks in the opposite order. XXXbe ensure they don't! - */ - rt = cx->runtime; -#ifdef JS_THREADSAFE - - JS_ACQUIRE_LOCK(rt->setSlotLock); - while (rt->setSlotBusy) { - jsrefcount saveDepth; - - /* Take pains to avoid nesting rt->gcLock inside rt->setSlotLock! */ - JS_RELEASE_LOCK(rt->setSlotLock); - saveDepth = JS_SuspendRequest(cx); - JS_ACQUIRE_LOCK(rt->setSlotLock); - if (rt->setSlotBusy) - JS_WAIT_CONDVAR(rt->setSlotDone, JS_NO_TIMEOUT); - JS_RELEASE_LOCK(rt->setSlotLock); - JS_ResumeRequest(cx, saveDepth); - JS_ACQUIRE_LOCK(rt->setSlotLock); - } - rt->setSlotBusy = JS_TRUE; - JS_RELEASE_LOCK(rt->setSlotLock); - -#define SET_SLOT_DONE(rt) \ - JS_BEGIN_MACRO \ - JS_ACQUIRE_LOCK((rt)->setSlotLock); \ - (rt)->setSlotBusy = JS_FALSE; \ - JS_NOTIFY_ALL_CONDVAR((rt)->setSlotDone); \ - JS_RELEASE_LOCK((rt)->setSlotLock); \ - JS_END_MACRO - -#else - -#define SET_SLOT_DONE(rt) /* nothing */ - -#endif - - obj2 = pobj; - while (obj2) { - if (obj2 == obj) { - SET_SLOT_DONE(rt); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CYCLIC_VALUE, -#if JS_HAS_OBJ_PROTO_PROP - object_props[slot].name -#else - (slot == JSSLOT_PROTO) ? js_proto_str - : js_parent_str -#endif - ); - return JS_FALSE; - } - obj2 = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj2, slot)); - } - - if (slot == JSSLOT_PROTO && OBJ_IS_NATIVE(obj)) { - /* Check to see whether obj shares its prototype's scope. */ - JS_LOCK_OBJ(cx, obj); - scope = OBJ_SCOPE(obj); - oldproto = JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO)); - if (oldproto && OBJ_SCOPE(oldproto) == scope) { - /* Either obj needs a new empty scope, or it should share pobj's. */ - if (!pobj || - !OBJ_IS_NATIVE(pobj) || - OBJ_GET_CLASS(cx, pobj) != LOCKED_OBJ_GET_CLASS(oldproto)) { - /* - * With no proto and no scope of its own, obj is truly empty. - * - * If pobj is not native, obj needs its own empty scope -- it - * should not continue to share oldproto's scope once oldproto - * is not on obj's prototype chain. That would put properties - * from oldproto's scope ahead of properties defined by pobj, - * in lookup order. - * - * If pobj's class differs from oldproto's, we may need a new - * scope to handle differences in private and reserved slots, - * so we suboptimally but safely make one. - */ - scope = js_GetMutableScope(cx, obj); - if (!scope) { - JS_UNLOCK_OBJ(cx, obj); - SET_SLOT_DONE(rt); - return JS_FALSE; - } - } else if (OBJ_SCOPE(pobj) != scope) { -#ifdef JS_THREADSAFE - /* - * We are about to nest scope locks. Help jslock.c:ShareScope - * keep scope->u.count balanced for the JS_UNLOCK_SCOPE, while - * avoiding deadlock, by recording scope in rt->setSlotScope. - */ - if (scope->ownercx) { - JS_ASSERT(scope->ownercx == cx); - rt->setSlotScope = scope; - } -#endif - - /* We can't deadlock because we checked for cycles above (2). */ - JS_LOCK_OBJ(cx, pobj); - newscope = (JSScope *) js_HoldObjectMap(cx, pobj->map); - obj->map = &newscope->map; - js_DropObjectMap(cx, &scope->map, obj); - JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope); - scope = newscope; -#ifdef JS_THREADSAFE - rt->setSlotScope = NULL; -#endif - } - } - LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(pobj)); - JS_UNLOCK_SCOPE(cx, scope); - } else { - OBJ_SET_SLOT(cx, obj, slot, OBJECT_TO_JSVAL(pobj)); - } - - SET_SLOT_DONE(rt); - return JS_TRUE; - -#undef SET_SLOT_DONE -} - -JS_STATIC_DLL_CALLBACK(JSHashNumber) -js_hash_object(const void *key) -{ - return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS; -} - -static JSHashEntry * -MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap) -{ - JSSharpObjectMap *map; - JSHashTable *table; - JSHashNumber hash; - JSHashEntry **hep, *he; - jsatomid sharpid; - JSIdArray *ida; - JSBool ok; - jsint i, length; - jsid id; -#if JS_HAS_GETTER_SETTER - JSObject *obj2; - JSProperty *prop; - uintN attrs; -#endif - jsval val; - int stackDummy; - - if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); - return NULL; - } - - map = &cx->sharpObjectMap; - table = map->table; - hash = js_hash_object(obj); - hep = JS_HashTableRawLookup(table, hash, obj); - he = *hep; - if (!he) { - sharpid = 0; - he = JS_HashTableRawAdd(table, hep, hash, obj, - JS_UINT32_TO_PTR(sharpid)); - if (!he) { - JS_ReportOutOfMemory(cx); - return NULL; - } - - /* - * Increment map->depth to protect js_EnterSharpObject from reentering - * itself badly. Without this fix, if we reenter the basis case where - * map->depth == 0, when unwinding the inner call we will destroy the - * newly-created hash table and crash. - */ - ++map->depth; - ida = JS_Enumerate(cx, obj); - --map->depth; - if (!ida) - return NULL; - - ok = JS_TRUE; - for (i = 0, length = ida->length; i < length; i++) { - id = ida->vector[i]; -#if JS_HAS_GETTER_SETTER - ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); - if (!ok) - break; - if (!prop) - continue; - ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs); - if (ok) { - if (OBJ_IS_NATIVE(obj2) && - (attrs & (JSPROP_GETTER | JSPROP_SETTER))) { - val = JSVAL_NULL; - if (attrs & JSPROP_GETTER) - val = (jsval) ((JSScopeProperty*)prop)->getter; - if (attrs & JSPROP_SETTER) { - if (val != JSVAL_NULL) { - /* Mark the getter, then set val to setter. */ - ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), - NULL) - != NULL); - } - val = (jsval) ((JSScopeProperty*)prop)->setter; - } - } else { - ok = OBJ_GET_PROPERTY(cx, obj, id, &val); - } - } - OBJ_DROP_PROPERTY(cx, obj2, prop); -#else - ok = OBJ_GET_PROPERTY(cx, obj, id, &val); -#endif - if (!ok) - break; - if (!JSVAL_IS_PRIMITIVE(val) && - !MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) { - ok = JS_FALSE; - break; - } - } - if (!ok || !idap) - JS_DestroyIdArray(cx, ida); - if (!ok) - return NULL; - } else { - sharpid = JS_PTR_TO_UINT32(he->value); - if (sharpid == 0) { - sharpid = ++map->sharpgen << SHARP_ID_SHIFT; - he->value = JS_UINT32_TO_PTR(sharpid); - } - ida = NULL; - } - if (idap) - *idap = ida; - return he; -} - -JSHashEntry * -js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, - jschar **sp) -{ - JSSharpObjectMap *map; - JSHashTable *table; - JSIdArray *ida; - JSHashNumber hash; - JSHashEntry *he, **hep; - jsatomid sharpid; - char buf[20]; - size_t len; - - if (JS_HAS_NATIVE_BRANCH_CALLBACK_OPTION(cx) && - cx->branchCallback && - !cx->branchCallback(cx, NULL)) { - return NULL; - } - - /* Set to null in case we return an early error. */ - *sp = NULL; - map = &cx->sharpObjectMap; - table = map->table; - if (!table) { - table = JS_NewHashTable(8, js_hash_object, JS_CompareValues, - JS_CompareValues, NULL, NULL); - if (!table) { - JS_ReportOutOfMemory(cx); - return NULL; - } - map->table = table; - JS_KEEP_ATOMS(cx->runtime); - } - - /* From this point the control must flow either through out: or bad:. */ - ida = NULL; - if (map->depth == 0) { - he = MarkSharpObjects(cx, obj, &ida); - if (!he) - goto bad; - JS_ASSERT((JS_PTR_TO_UINT32(he->value) & SHARP_BIT) == 0); - if (!idap) { - JS_DestroyIdArray(cx, ida); - ida = NULL; - } - } else { - hash = js_hash_object(obj); - hep = JS_HashTableRawLookup(table, hash, obj); - he = *hep; - - /* - * It's possible that the value of a property has changed from the - * first time the object's properties are traversed (when the property - * ids are entered into the hash table) to the second (when they are - * converted to strings), i.e., the OBJ_GET_PROPERTY() call is not - * idempotent. - */ - if (!he) { - he = JS_HashTableRawAdd(table, hep, hash, obj, NULL); - if (!he) { - JS_ReportOutOfMemory(cx); - goto bad; - } - sharpid = 0; - goto out; - } - } - - sharpid = JS_PTR_TO_UINT32(he->value); - if (sharpid != 0) { - len = JS_snprintf(buf, sizeof buf, "#%u%c", - sharpid >> SHARP_ID_SHIFT, - (sharpid & SHARP_BIT) ? '#' : '='); - *sp = js_InflateString(cx, buf, &len); - if (!*sp) { - if (ida) - JS_DestroyIdArray(cx, ida); - goto bad; - } - } - -out: - JS_ASSERT(he); - if ((sharpid & SHARP_BIT) == 0) { - if (idap && !ida) { - ida = JS_Enumerate(cx, obj); - if (!ida) { - if (*sp) { - JS_free(cx, *sp); - *sp = NULL; - } - goto bad; - } - } - map->depth++; - } - - if (idap) - *idap = ida; - return he; - -bad: - /* Clean up the sharpObjectMap table on outermost error. */ - if (map->depth == 0) { - JS_UNKEEP_ATOMS(cx->runtime); - map->sharpgen = 0; - JS_HashTableDestroy(map->table); - map->table = NULL; - } - return NULL; -} - -void -js_LeaveSharpObject(JSContext *cx, JSIdArray **idap) -{ - JSSharpObjectMap *map; - JSIdArray *ida; - - map = &cx->sharpObjectMap; - JS_ASSERT(map->depth > 0); - if (--map->depth == 0) { - JS_UNKEEP_ATOMS(cx->runtime); - map->sharpgen = 0; - JS_HashTableDestroy(map->table); - map->table = NULL; - } - if (idap) { - ida = *idap; - if (ida) { - JS_DestroyIdArray(cx, ida); - *idap = NULL; - } - } -} - -JS_STATIC_DLL_CALLBACK(intN) -gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg) -{ - GC_MARK((JSContext *)arg, (JSObject *)he->key, "sharp table entry"); - return JS_DHASH_NEXT; -} - -void -js_GCMarkSharpMap(JSContext *cx, JSSharpObjectMap *map) -{ - JS_ASSERT(map->depth > 0); - JS_ASSERT(map->table); - - /* - * During recursive calls to MarkSharpObjects a non-native object or - * object with a custom getProperty method can potentially return an - * unrooted value or even cut from the object graph an argument of one of - * MarkSharpObjects recursive invocations. So we must protect map->table - * entries against GC. - * - * We can not simply use JSTempValueRooter to mark the obj argument of - * MarkSharpObjects during recursion as we have to protect *all* entries - * in JSSharpObjectMap including those that contains otherwise unreachable - * objects just allocated through custom getProperty. Otherwise newer - * allocations can re-use the address of an object stored in the hashtable - * confusing js_EnterSharpObject. So to address the problem we simply - * mark all objects from map->table. - * - * An alternative "proper" solution is to use JSTempValueRooter in - * MarkSharpObjects with code to remove during finalization entries - * with otherwise unreachable objects. But this is way too complex - * to justify spending efforts. - */ - JS_HashTableEnumerateEntries(map->table, gc_sharp_table_entry_marker, cx); -} - -#define OBJ_TOSTRING_EXTRA 4 /* for 4 local GC roots */ - -#if JS_HAS_TOSOURCE -JSBool -js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSBool ok, outermost; - JSHashEntry *he; - JSIdArray *ida; - jschar *chars, *ochars, *vsharp; - const jschar *idstrchars, *vchars; - size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen; - char *comma; - jsint i, j, length, valcnt; - jsid id; -#if JS_HAS_GETTER_SETTER - JSObject *obj2; - JSProperty *prop; - uintN attrs; -#endif - jsval *val; - JSString *gsopold[2]; - JSString *gsop[2]; - JSAtom *atom; - JSString *idstr, *valstr, *str; - int stackDummy; - - if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); - return JS_FALSE; - } - - /* If outermost, we need parentheses to be an expression, not a block. */ - outermost = (cx->sharpObjectMap.depth == 0); - he = js_EnterSharpObject(cx, obj, &ida, &chars); - if (!he) - return JS_FALSE; - if (IS_SHARP(he)) { - /* - * We didn't enter -- obj is already "sharp", meaning we've visited it - * already in our depth first search, and therefore chars contains a - * string of the form "#n#". - */ - JS_ASSERT(!ida); -#if JS_HAS_SHARP_VARS - nchars = js_strlen(chars); -#else - chars[0] = '{'; - chars[1] = '}'; - chars[2] = 0; - nchars = 2; -#endif - goto make_string; - } - JS_ASSERT(ida); - ok = JS_TRUE; - - if (!chars) { - /* If outermost, allocate 4 + 1 for "({})" and the terminator. */ - chars = (jschar *) malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar)); - nchars = 0; - if (!chars) - goto error; - if (outermost) - chars[nchars++] = '('; - } else { - /* js_EnterSharpObject returned a string of the form "#n=" in chars. */ - MAKE_SHARP(he); - nchars = js_strlen(chars); - chars = (jschar *) - realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar)); - if (!chars) { - free(ochars); - goto error; - } - if (outermost) { - /* - * No need for parentheses around the whole shebang, because #n= - * unambiguously begins an object initializer, and never a block - * statement. - */ - outermost = JS_FALSE; - } - } - -#ifdef DUMP_CALL_TABLE - if (cx->options & JSOPTION_LOGCALL_TOSOURCE) { - const char *classname = OBJ_GET_CLASS(cx, obj)->name; - size_t classnchars = strlen(classname); - static const char classpropid[] = "C"; - const char *cp; - size_t onchars = nchars; - - /* 2 for ': ', 2 quotes around classname, 2 for ', ' after. */ - classnchars += sizeof classpropid - 1 + 2 + 2; - if (ida->length) - classnchars += 2; - - /* 2 for the braces, 1 for the terminator */ - chars = (jschar *) - realloc((ochars = chars), - (nchars + classnchars + 2 + 1) * sizeof(jschar)); - if (!chars) { - free(ochars); - goto error; - } - - chars[nchars++] = '{'; /* 1 from the 2 braces */ - for (cp = classpropid; *cp; cp++) - chars[nchars++] = (jschar) *cp; - chars[nchars++] = ':'; - chars[nchars++] = ' '; /* 2 for ': ' */ - chars[nchars++] = '"'; - for (cp = classname; *cp; cp++) - chars[nchars++] = (jschar) *cp; - chars[nchars++] = '"'; /* 2 quotes */ - if (ida->length) { - chars[nchars++] = ','; - chars[nchars++] = ' '; /* 2 for ', ' */ - } - - JS_ASSERT(nchars - onchars == 1 + classnchars); - } else -#endif - chars[nchars++] = '{'; - - comma = NULL; - - /* - * We have four local roots for cooked and raw value GC safety. Hoist the - * "argv + 2" out of the loop using the val local, which refers to the raw - * (unconverted, "uncooked") values. - */ - val = argv + 2; - - for (i = 0, length = ida->length; i < length; i++) { - JSBool idIsLexicalIdentifier, needOldStyleGetterSetter; - - /* Get strings for id and value and GC-root them via argv. */ - id = ida->vector[i]; - -#if JS_HAS_GETTER_SETTER - ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); - if (!ok) - goto error; -#endif - - /* - * Convert id to a jsval and then to a string. Decide early whether we - * prefer get/set or old getter/setter syntax. - */ - atom = JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL; - idstr = js_ValueToString(cx, ID_TO_VALUE(id)); - if (!idstr) { - ok = JS_FALSE; - OBJ_DROP_PROPERTY(cx, obj2, prop); - goto error; - } - *rval = STRING_TO_JSVAL(idstr); /* local root */ - idIsLexicalIdentifier = js_IsIdentifier(idstr); - needOldStyleGetterSetter = - !idIsLexicalIdentifier || - js_CheckKeyword(JSSTRING_CHARS(idstr), - JSSTRING_LENGTH(idstr)) != TOK_EOF; - -#if JS_HAS_GETTER_SETTER - - valcnt = 0; - if (prop) { - ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs); - if (!ok) { - OBJ_DROP_PROPERTY(cx, obj2, prop); - goto error; - } - if (OBJ_IS_NATIVE(obj2) && - (attrs & (JSPROP_GETTER | JSPROP_SETTER))) { - if (attrs & JSPROP_GETTER) { - val[valcnt] = (jsval) ((JSScopeProperty *)prop)->getter; - gsopold[valcnt] = - ATOM_TO_STRING(cx->runtime->atomState.getterAtom); - gsop[valcnt] = - ATOM_TO_STRING(cx->runtime->atomState.getAtom); - valcnt++; - } - if (attrs & JSPROP_SETTER) { - val[valcnt] = (jsval) ((JSScopeProperty *)prop)->setter; - gsopold[valcnt] = - ATOM_TO_STRING(cx->runtime->atomState.setterAtom); - gsop[valcnt] = - ATOM_TO_STRING(cx->runtime->atomState.setAtom); - valcnt++; - } - } else { - valcnt = 1; - gsop[0] = NULL; - gsopold[0] = NULL; - ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]); - } - OBJ_DROP_PROPERTY(cx, obj2, prop); - } - -#else /* !JS_HAS_GETTER_SETTER */ - - /* - * We simplify the source code at the price of minor dead code bloat in - * the ECMA version (for testing only, see jsconfig.h). The null - * default values in gsop[j] suffice to disable non-ECMA getter and - * setter code. - */ - valcnt = 1; - gsop[0] = NULL; - gsopold[0] = NULL; - ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]); - -#endif /* !JS_HAS_GETTER_SETTER */ - - if (!ok) - goto error; - - /* - * If id is a string that's not an identifier, then it needs to be - * quoted. Also, negative integer ids must be quoted. - */ - if (atom - ? !idIsLexicalIdentifier - : (JSID_IS_OBJECT(id) || JSID_TO_INT(id) < 0)) { - idstr = js_QuoteString(cx, idstr, (jschar)'\''); - if (!idstr) { - ok = JS_FALSE; - goto error; - } - *rval = STRING_TO_JSVAL(idstr); /* local root */ - } - idstrchars = JSSTRING_CHARS(idstr); - idstrlength = JSSTRING_LENGTH(idstr); - - for (j = 0; j < valcnt; j++) { - /* Convert val[j] to its canonical source form. */ - valstr = js_ValueToSource(cx, val[j]); - if (!valstr) { - ok = JS_FALSE; - goto error; - } - argv[j] = STRING_TO_JSVAL(valstr); /* local root */ - vchars = JSSTRING_CHARS(valstr); - vlength = JSSTRING_LENGTH(valstr); - - if (vchars[0] == '#') - needOldStyleGetterSetter = JS_TRUE; - - if (needOldStyleGetterSetter) - gsop[j] = gsopold[j]; - -#ifndef OLD_GETTER_SETTER - /* - * Remove '(function ' from the beginning of valstr and ')' from the - * end so that we can put "get" in front of the function definition. - */ - if (gsop[j] && VALUE_IS_FUNCTION(cx, val[j]) && - !needOldStyleGetterSetter) { - const jschar *start = vchars; - if (vchars[0] == '(') - vchars++; - vchars = js_strchr_limit(vchars, '(', vchars + vlength); - if (vchars) { - vlength -= vchars - start + 1; - } else { - gsop[j] = NULL; - vchars = start; - } - } -#else - needOldStyleGetterSetter = JS_TRUE; - gsop[j] = gsopold[j]; -#endif - - /* If val[j] is a non-sharp object, consider sharpening it. */ - vsharp = NULL; - vsharplength = 0; -#if JS_HAS_SHARP_VARS - if (!JSVAL_IS_PRIMITIVE(val[j]) && vchars[0] != '#') { - he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val[j]), NULL, - &vsharp); - if (!he) { - ok = JS_FALSE; - goto error; - } - if (IS_SHARP(he)) { - vchars = vsharp; - vlength = js_strlen(vchars); - needOldStyleGetterSetter = JS_TRUE; - gsop[j] = gsopold[j]; - } else { - if (vsharp) { - vsharplength = js_strlen(vsharp); - MAKE_SHARP(he); - needOldStyleGetterSetter = JS_TRUE; - gsop[j] = gsopold[j]; - } - js_LeaveSharpObject(cx, NULL); - } - } -#endif - -#define SAFE_ADD(n) \ - JS_BEGIN_MACRO \ - size_t n_ = (n); \ - curlen += n_; \ - if (curlen < n_) \ - goto overflow; \ - JS_END_MACRO - - curlen = nchars; - if (comma) - SAFE_ADD(2); - SAFE_ADD(idstrlength + 1); - if (gsop[j]) - SAFE_ADD(JSSTRING_LENGTH(gsop[j]) + 1); - SAFE_ADD(vsharplength); - SAFE_ADD(vlength); - /* Account for the trailing null. */ - SAFE_ADD((outermost ? 2 : 1) + 1); -#undef SAFE_ADD - - if (curlen > (size_t)-1 / sizeof(jschar)) - goto overflow; - - /* Allocate 1 + 1 at end for closing brace and terminating 0. */ - chars = (jschar *) - realloc((ochars = chars), curlen * sizeof(jschar)); - if (!chars) { - /* Save code space on error: let JS_free ignore null vsharp. */ - JS_free(cx, vsharp); - free(ochars); - goto error; - } - - if (comma) { - chars[nchars++] = comma[0]; - chars[nchars++] = comma[1]; - } - comma = ", "; - - if (needOldStyleGetterSetter) { - js_strncpy(&chars[nchars], idstrchars, idstrlength); - nchars += idstrlength; - if (gsop[j]) { - chars[nchars++] = ' '; - gsoplength = JSSTRING_LENGTH(gsop[j]); - js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), - gsoplength); - nchars += gsoplength; - } - chars[nchars++] = ':'; - } else { /* New style "decompilation" */ - if (gsop[j]) { - gsoplength = JSSTRING_LENGTH(gsop[j]); - js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), - gsoplength); - nchars += gsoplength; - chars[nchars++] = ' '; - } - js_strncpy(&chars[nchars], idstrchars, idstrlength); - nchars += idstrlength; - /* Extraneous space after id here will be extracted later */ - chars[nchars++] = gsop[j] ? ' ' : ':'; - } - - if (vsharplength) { - js_strncpy(&chars[nchars], vsharp, vsharplength); - nchars += vsharplength; - } - js_strncpy(&chars[nchars], vchars, vlength); - nchars += vlength; - - if (vsharp) - JS_free(cx, vsharp); -#ifdef DUMP_CALL_TABLE - if (outermost && nchars >= js_LogCallToSourceLimit) - break; -#endif - } - } - - chars[nchars++] = '}'; - if (outermost) - chars[nchars++] = ')'; - chars[nchars] = 0; - - error: - js_LeaveSharpObject(cx, &ida); - - if (!ok) { - if (chars) - free(chars); - return ok; - } - - if (!chars) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - make_string: - str = js_NewString(cx, chars, nchars, 0); - if (!str) { - free(chars); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; - - overflow: - JS_free(cx, vsharp); - free(chars); - chars = NULL; - goto error; -} -#endif /* JS_HAS_TOSOURCE */ - -JSBool -js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jschar *chars; - size_t nchars; - const char *clazz, *prefix; - JSString *str; - - clazz = OBJ_GET_CLASS(cx, obj)->name; - nchars = 9 + strlen(clazz); /* 9 for "[object ]" */ - chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar)); - if (!chars) - return JS_FALSE; - - prefix = "[object "; - nchars = 0; - while ((chars[nchars] = (jschar)*prefix) != 0) - nchars++, prefix++; - while ((chars[nchars] = (jschar)*clazz) != 0) - nchars++, clazz++; - chars[nchars++] = ']'; - chars[nchars] = 0; - - str = js_NewString(cx, chars, nchars, 0); - if (!str) { - JS_free(cx, chars); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -js_obj_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - - str = js_ValueToString(cx, argv[-1]); - if (!str) - return JS_FALSE; - - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -obj_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -/* - * Check whether principals subsumes scopeobj's principals, and return true - * if so (or if scopeobj has no principals, for backward compatibility with - * the JS API, which does not require principals), and false otherwise. - */ -JSBool -js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj, - JSPrincipals *principals, JSAtom *caller) -{ - JSRuntime *rt; - JSPrincipals *scopePrincipals; - const char *callerstr; - - rt = cx->runtime; - if (rt->findObjectPrincipals) { - scopePrincipals = rt->findObjectPrincipals(cx, scopeobj); - if (!principals || !scopePrincipals || - !principals->subsume(principals, scopePrincipals)) { - callerstr = js_AtomToPrintableString(cx, caller); - if (!callerstr) - return JS_FALSE; - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_INDIRECT_CALL, callerstr); - return JS_FALSE; - } - } - return JS_TRUE; -} - -JSObject * -js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller) -{ - JSClass *clasp; - JSExtendedClass *xclasp; - JSObject *inner; - - if (!scopeobj) - goto bad; - - OBJ_TO_INNER_OBJECT(cx, scopeobj); - if (!scopeobj) - return NULL; - - inner = scopeobj; - - /* XXX This is an awful gross hack. */ - while (scopeobj) { - clasp = OBJ_GET_CLASS(cx, scopeobj); - if (clasp->flags & JSCLASS_IS_EXTENDED) { - xclasp = (JSExtendedClass*)clasp; - if (xclasp->innerObject && - xclasp->innerObject(cx, scopeobj) != scopeobj) { - goto bad; - } - } - - scopeobj = OBJ_GET_PARENT(cx, scopeobj); - } - - return inner; - -bad: - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_INDIRECT_CALL, caller); - return NULL; -} - -static JSBool -obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSStackFrame *fp, *caller; - JSBool indirectCall; - JSObject *scopeobj; - JSString *str; - const char *file; - uintN line; - JSPrincipals *principals; - JSScript *script; - JSBool ok; -#if JS_HAS_EVAL_THIS_SCOPE - JSObject *callerScopeChain = NULL, *callerVarObj = NULL; - JSObject *setCallerScopeChain = NULL; - JSBool setCallerVarObj = JS_FALSE; -#endif - - fp = cx->fp; - caller = JS_GetScriptedCaller(cx, fp); - JS_ASSERT(!caller || caller->pc); - indirectCall = (caller && *caller->pc != JSOP_EVAL); - - if (indirectCall && - !JS_ReportErrorFlagsAndNumber(cx, - JSREPORT_WARNING | JSREPORT_STRICT, - js_GetErrorMessage, NULL, - JSMSG_BAD_INDIRECT_CALL, - js_eval_str)) { - return JS_FALSE; - } - - if (!JSVAL_IS_STRING(argv[0])) { - *rval = argv[0]; - return JS_TRUE; - } - - /* - * If the caller is a lightweight function and doesn't have a variables - * object, then we need to provide one for the compiler to stick any - * declared (var) variables into. - */ - if (caller && !caller->varobj && !js_GetCallObject(cx, caller, NULL)) - return JS_FALSE; - -#if JS_HAS_SCRIPT_OBJECT - /* - * Script.prototype.compile/exec and Object.prototype.eval all take an - * optional trailing argument that overrides the scope object. - */ - scopeobj = NULL; - if (argc >= 2) { - if (!js_ValueToObject(cx, argv[1], &scopeobj)) - return JS_FALSE; - argv[1] = OBJECT_TO_JSVAL(scopeobj); - } - if (!scopeobj) -#endif - { -#if JS_HAS_EVAL_THIS_SCOPE - /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */ - if (indirectCall) { - callerScopeChain = js_GetScopeChain(cx, caller); - if (!callerScopeChain) - return JS_FALSE; - OBJ_TO_INNER_OBJECT(cx, obj); - if (!obj) - return JS_FALSE; - if (obj != callerScopeChain) { - if (!js_CheckPrincipalsAccess(cx, obj, - caller->script->principals, - cx->runtime->atomState.evalAtom)) - { - return JS_FALSE; - } - - scopeobj = js_NewWithObject(cx, obj, callerScopeChain, -1); - if (!scopeobj) - return JS_FALSE; - - /* Set fp->scopeChain too, for the compiler. */ - caller->scopeChain = fp->scopeChain = scopeobj; - - /* Remember scopeobj so we can null its private when done. */ - setCallerScopeChain = scopeobj; - } - - callerVarObj = caller->varobj; - if (obj != callerVarObj) { - /* Set fp->varobj too, for the compiler. */ - caller->varobj = fp->varobj = obj; - setCallerVarObj = JS_TRUE; - } - } - /* From here on, control must exit through label out with ok set. */ -#endif - - /* Compile using caller's current scope object. */ - if (caller) { - scopeobj = js_GetScopeChain(cx, caller); - if (!scopeobj) { - ok = JS_FALSE; - goto out; - } - } - } - - /* Ensure we compile this eval with the right object in the scope chain. */ - scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_eval_str); - if (!scopeobj) - return JS_FALSE; - - str = JSVAL_TO_STRING(argv[0]); - if (caller) { - principals = JS_EvalFramePrincipals(cx, fp, caller); - if (principals == caller->script->principals) { - file = caller->script->filename; - line = js_PCToLineNumber(cx, caller->script, caller->pc); - } else { - file = principals->codebase; - line = 0; - } - } else { - file = NULL; - line = 0; - principals = NULL; - } - - /* - * Set JSFRAME_EVAL on fp and any frames (e.g., fun_call if eval.call was - * invoked) between fp and its scripted caller, to help the compiler easily - * find the same caller whose scope and var obj we've set. - * - * XXX this nonsense could, and perhaps should, go away with a better way - * to pass params to the compiler than via the top-most frame. - */ - do { - fp->flags |= JSFRAME_EVAL; - } while ((fp = fp->down) != caller); - - script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals, - JSSTRING_CHARS(str), - JSSTRING_LENGTH(str), - file, line); - if (!script) { - ok = JS_FALSE; - goto out; - } - -#if JS_HAS_SCRIPT_OBJECT - if (argc < 2) -#endif - { - /* Execute using caller's new scope object (might be a Call object). */ - if (caller) - scopeobj = caller->scopeChain; - } - - /* - * Belt-and-braces: check that the lesser of eval's principals and the - * caller's principals has access to scopeobj. - */ - ok = js_CheckPrincipalsAccess(cx, scopeobj, principals, - cx->runtime->atomState.evalAtom); - if (ok) - ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); - - JS_DestroyScript(cx, script); - -out: -#if JS_HAS_EVAL_THIS_SCOPE - /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */ - if (setCallerScopeChain) { - caller->scopeChain = callerScopeChain; - JS_ASSERT(OBJ_GET_CLASS(cx, setCallerScopeChain) == &js_WithClass); - JS_SetPrivate(cx, setCallerScopeChain, NULL); - } - if (setCallerVarObj) - caller->varobj = callerVarObj; -#endif - return ok; -} - -#if JS_HAS_OBJ_WATCHPOINT - -static JSBool -obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp, - void *closure) -{ - JSObject *callable; - JSRuntime *rt; - JSStackFrame *caller; - JSPrincipals *subject, *watcher; - JSResolvingKey key; - JSResolvingEntry *entry; - uint32 generation; - jsval argv[3]; - JSBool ok; - - callable = (JSObject *) closure; - - rt = cx->runtime; - if (rt->findObjectPrincipals) { - /* Skip over any obj_watch_* frames between us and the real subject. */ - caller = JS_GetScriptedCaller(cx, cx->fp); - if (caller) { - /* - * Only call the watch handler if the watcher is allowed to watch - * the currently executing script. - */ - watcher = rt->findObjectPrincipals(cx, callable); - subject = JS_StackFramePrincipals(cx, caller); - - if (watcher && subject && !watcher->subsume(watcher, subject)) { - /* Silently don't call the watch handler. */ - return JS_TRUE; - } - } - } - - /* Avoid recursion on (obj, id) already being watched on cx. */ - key.obj = obj; - key.id = id; - if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry)) - return JS_FALSE; - if (!entry) - return JS_TRUE; - generation = cx->resolvingTable->generation; - - argv[0] = id; - argv[1] = old; - argv[2] = *nvp; - ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(callable), 3, argv, nvp); - js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation); - return ok; -} - -static JSBool -obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSObject *callable; - jsval userid, value; - jsid propid; - uintN attrs; - - callable = js_ValueToCallableObject(cx, &argv[1], 0); - if (!callable) - return JS_FALSE; - - /* Compute the unique int/atom symbol id needed by js_LookupProperty. */ - userid = argv[0]; - if (!JS_ValueToId(cx, userid, &propid)) - return JS_FALSE; - - if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs)) - return JS_FALSE; - if (attrs & JSPROP_READONLY) - return JS_TRUE; - return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, callable); -} - -static JSBool -obj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return JS_ClearWatchPoint(cx, obj, argv[0], NULL, NULL); -} - -#endif /* JS_HAS_OBJ_WATCHPOINT */ - -/* - * Prototype and property query methods, to complement the 'in' and - * 'instanceof' operators. - */ - -/* Proposed ECMA 15.2.4.5. */ -static JSBool -obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return js_HasOwnPropertyHelper(cx, obj, obj->map->ops->lookupProperty, - argc, argv, rval); -} - -JSBool -js_HasOwnPropertyHelper(JSContext *cx, JSObject *obj, JSLookupPropOp lookup, - uintN argc, jsval *argv, jsval *rval) -{ - jsid id; - JSObject *obj2; - JSProperty *prop; - JSScopeProperty *sprop; - - if (!JS_ValueToId(cx, argv[0], &id)) - return JS_FALSE; - if (!lookup(cx, obj, id, &obj2, &prop)) - return JS_FALSE; - if (!prop) { - *rval = JSVAL_FALSE; - } else if (obj2 == obj) { - *rval = JSVAL_TRUE; - } else { - JSClass *clasp; - JSExtendedClass *xclasp; - - clasp = OBJ_GET_CLASS(cx, obj); - xclasp = (clasp->flags & JSCLASS_IS_EXTENDED) - ? (JSExtendedClass *)clasp - : NULL; - if (xclasp && xclasp->outerObject && - xclasp->outerObject(cx, obj2) == obj) { - *rval = JSVAL_TRUE; - } else if (OBJ_IS_NATIVE(obj2) && OBJ_GET_CLASS(cx, obj2) == clasp) { - /* - * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a - * delegated property makes that property appear to be direct in - * all delegating instances of the same native class. This hack - * avoids bloating every function instance with its own 'length' - * (AKA 'arity') property. But it must not extend across class - * boundaries, to avoid making hasOwnProperty lie (bug 320854). - * - * It's not really a hack, of course: a permanent property can't - * be deleted, and JSPROP_SHARED means "don't allocate a slot in - * any instance, prototype or delegating". Without a slot, and - * without the ability to remove and recreate (with differences) - * the property, there is no way to tell whether it is directly - * owned, or indirectly delegated. - */ - sprop = (JSScopeProperty *)prop; - *rval = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop)); - } else { - *rval = JSVAL_FALSE; - } - } - if (prop) - OBJ_DROP_PROPERTY(cx, obj2, prop); - return JS_TRUE; -} - -/* Proposed ECMA 15.2.4.6. */ -static JSBool -obj_isPrototypeOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSBool b; - - if (!js_IsDelegate(cx, obj, *argv, &b)) - return JS_FALSE; - *rval = BOOLEAN_TO_JSVAL(b); - return JS_TRUE; -} - -/* Proposed ECMA 15.2.4.7. */ -static JSBool -obj_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsid id; - uintN attrs; - JSObject *obj2; - JSProperty *prop; - JSBool ok; - - if (!JS_ValueToId(cx, argv[0], &id)) - return JS_FALSE; - - if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) - return JS_FALSE; - - if (!prop) { - *rval = JSVAL_FALSE; - return JS_TRUE; - } - - /* - * XXX ECMA spec error compatible: return false unless hasOwnProperty. - * The ECMA spec really should be fixed so propertyIsEnumerable and the - * for..in loop agree on whether prototype properties are enumerable, - * obviously by fixing this method (not by breaking the for..in loop!). - * - * We check here for shared permanent prototype properties, which should - * be treated as if they are local to obj. They are an implementation - * technique used to satisfy ECMA requirements; users should not be able - * to distinguish a shared permanent proto-property from a local one. - */ - if (obj2 != obj && - !(OBJ_IS_NATIVE(obj2) && - SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) { - OBJ_DROP_PROPERTY(cx, obj2, prop); - *rval = JSVAL_FALSE; - return JS_TRUE; - } - - ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs); - OBJ_DROP_PROPERTY(cx, obj2, prop); - if (ok) - *rval = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0); - return ok; -} - -#if JS_HAS_GETTER_SETTER -static JSBool -obj_defineGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsval fval, junk; - jsid id; - uintN attrs; - - fval = argv[1]; - if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_GETTER_OR_SETTER, - js_getter_str); - return JS_FALSE; - } - - if (!JS_ValueToId(cx, argv[0], &id)) - return JS_FALSE; - if (!js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL)) - return JS_FALSE; - /* - * Getters and setters are just like watchpoints from an access - * control point of view. - */ - if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs)) - return JS_FALSE; - return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, - (JSPropertyOp) JSVAL_TO_OBJECT(fval), NULL, - JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED, - NULL); -} - -static JSBool -obj_defineSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsval fval, junk; - jsid id; - uintN attrs; - - fval = argv[1]; - if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_GETTER_OR_SETTER, - js_setter_str); - return JS_FALSE; - } - - if (!JS_ValueToId(cx, argv[0], &id)) - return JS_FALSE; - if (!js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL)) - return JS_FALSE; - /* - * Getters and setters are just like watchpoints from an access - * control point of view. - */ - if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs)) - return JS_FALSE; - return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, - NULL, (JSPropertyOp) JSVAL_TO_OBJECT(fval), - JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED, - NULL); -} - -static JSBool -obj_lookupGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsid id; - JSObject *pobj; - JSProperty *prop; - JSScopeProperty *sprop; - - if (!JS_ValueToId(cx, argv[0], &id)) - return JS_FALSE; - if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) - return JS_FALSE; - if (prop) { - if (OBJ_IS_NATIVE(pobj)) { - sprop = (JSScopeProperty *) prop; - if (sprop->attrs & JSPROP_GETTER) - *rval = OBJECT_TO_JSVAL(sprop->getter); - } - OBJ_DROP_PROPERTY(cx, pobj, prop); - } - return JS_TRUE; -} - -static JSBool -obj_lookupSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsid id; - JSObject *pobj; - JSProperty *prop; - JSScopeProperty *sprop; - - if (!JS_ValueToId(cx, argv[0], &id)) - return JS_FALSE; - if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) - return JS_FALSE; - if (prop) { - if (OBJ_IS_NATIVE(pobj)) { - sprop = (JSScopeProperty *) prop; - if (sprop->attrs & JSPROP_SETTER) - *rval = OBJECT_TO_JSVAL(sprop->setter); - } - OBJ_DROP_PROPERTY(cx, pobj, prop); - } - return JS_TRUE; -} -#endif /* JS_HAS_GETTER_SETTER */ - -#if JS_HAS_OBJ_WATCHPOINT -const char js_watch_str[] = "watch"; -const char js_unwatch_str[] = "unwatch"; -#endif -const char js_hasOwnProperty_str[] = "hasOwnProperty"; -const char js_isPrototypeOf_str[] = "isPrototypeOf"; -const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable"; -#if JS_HAS_GETTER_SETTER -const char js_defineGetter_str[] = "__defineGetter__"; -const char js_defineSetter_str[] = "__defineSetter__"; -const char js_lookupGetter_str[] = "__lookupGetter__"; -const char js_lookupSetter_str[] = "__lookupSetter__"; -#endif - -static JSFunctionSpec object_methods[] = { -#if JS_HAS_TOSOURCE - {js_toSource_str, js_obj_toSource, 0, 0, OBJ_TOSTRING_EXTRA}, -#endif - {js_toString_str, js_obj_toString, 0, 0, OBJ_TOSTRING_EXTRA}, - {js_toLocaleString_str, js_obj_toLocaleString, 0, 0, OBJ_TOSTRING_EXTRA}, - {js_valueOf_str, obj_valueOf, 0,0,0}, - {js_eval_str, obj_eval, 1,0,0}, -#if JS_HAS_OBJ_WATCHPOINT - {js_watch_str, obj_watch, 2,0,0}, - {js_unwatch_str, obj_unwatch, 1,0,0}, -#endif - {js_hasOwnProperty_str, obj_hasOwnProperty, 1,0,0}, - {js_isPrototypeOf_str, obj_isPrototypeOf, 1,0,0}, - {js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0,0}, -#if JS_HAS_GETTER_SETTER - {js_defineGetter_str, obj_defineGetter, 2,0,0}, - {js_defineSetter_str, obj_defineSetter, 2,0,0}, - {js_lookupGetter_str, obj_lookupGetter, 1,0,0}, - {js_lookupSetter_str, obj_lookupSetter, 1,0,0}, -#endif - {0,0,0,0,0} -}; - -static JSBool -Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - if (argc == 0) { - /* Trigger logic below to construct a blank object. */ - obj = NULL; - } else { - /* If argv[0] is null or undefined, obj comes back null. */ - if (!js_ValueToObject(cx, argv[0], &obj)) - return JS_FALSE; - } - if (!obj) { - JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0])); - if (cx->fp->flags & JSFRAME_CONSTRUCTING) - return JS_TRUE; - obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL); - if (!obj) - return JS_FALSE; - } - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -/* - * ObjectOps and Class for with-statement stack objects. - */ -static JSBool -with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - JSProperty **propp) -{ - JSObject *proto = OBJ_GET_PROTO(cx, obj); - if (!proto) - return js_LookupProperty(cx, obj, id, objp, propp); - return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp); -} - -static JSBool -with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - JSObject *proto = OBJ_GET_PROTO(cx, obj); - if (!proto) - return js_GetProperty(cx, obj, id, vp); - return OBJ_GET_PROPERTY(cx, proto, id, vp); -} - -static JSBool -with_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - JSObject *proto = OBJ_GET_PROTO(cx, obj); - if (!proto) - return js_SetProperty(cx, obj, id, vp); - return OBJ_SET_PROPERTY(cx, proto, id, vp); -} - -static JSBool -with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, - uintN *attrsp) -{ - JSObject *proto = OBJ_GET_PROTO(cx, obj); - if (!proto) - return js_GetAttributes(cx, obj, id, prop, attrsp); - return OBJ_GET_ATTRIBUTES(cx, proto, id, prop, attrsp); -} - -static JSBool -with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, - uintN *attrsp) -{ - JSObject *proto = OBJ_GET_PROTO(cx, obj); - if (!proto) - return js_SetAttributes(cx, obj, id, prop, attrsp); - return OBJ_SET_ATTRIBUTES(cx, proto, id, prop, attrsp); -} - -static JSBool -with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) -{ - JSObject *proto = OBJ_GET_PROTO(cx, obj); - if (!proto) - return js_DeleteProperty(cx, obj, id, rval); - return OBJ_DELETE_PROPERTY(cx, proto, id, rval); -} - -static JSBool -with_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) -{ - JSObject *proto = OBJ_GET_PROTO(cx, obj); - if (!proto) - return js_DefaultValue(cx, obj, hint, vp); - return OBJ_DEFAULT_VALUE(cx, proto, hint, vp); -} - -static JSBool -with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, - jsval *statep, jsid *idp) -{ - JSObject *proto = OBJ_GET_PROTO(cx, obj); - if (!proto) - return js_Enumerate(cx, obj, enum_op, statep, idp); - return OBJ_ENUMERATE(cx, proto, enum_op, statep, idp); -} - -static JSBool -with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, - jsval *vp, uintN *attrsp) -{ - JSObject *proto = OBJ_GET_PROTO(cx, obj); - if (!proto) - return js_CheckAccess(cx, obj, id, mode, vp, attrsp); - return OBJ_CHECK_ACCESS(cx, proto, id, mode, vp, attrsp); -} - -static JSObject * -with_ThisObject(JSContext *cx, JSObject *obj) -{ - JSObject *proto = OBJ_GET_PROTO(cx, obj); - if (!proto) - return obj; - return OBJ_THIS_OBJECT(cx, proto); -} - -JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = { - js_NewObjectMap, js_DestroyObjectMap, - with_LookupProperty, js_DefineProperty, - with_GetProperty, with_SetProperty, - with_GetAttributes, with_SetAttributes, - with_DeleteProperty, with_DefaultValue, - with_Enumerate, with_CheckAccess, - with_ThisObject, NATIVE_DROP_PROPERTY, - NULL, NULL, - NULL, NULL, - js_SetProtoOrParent, js_SetProtoOrParent, - js_Mark, js_Clear, - NULL, NULL -}; - -static JSObjectOps * -with_getObjectOps(JSContext *cx, JSClass *clasp) -{ - return &js_WithObjectOps; -} - -JSClass js_WithClass = { - "With", - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS, - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, - with_getObjectOps, - 0,0,0,0,0,0,0 -}; - -JSObject * -js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth) -{ - JSObject *obj; - - obj = js_NewObject(cx, &js_WithClass, proto, parent); - if (!obj) - return NULL; - obj->slots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(cx->fp); - OBJ_SET_BLOCK_DEPTH(cx, obj, depth); - return obj; -} - -JSObject * -js_NewBlockObject(JSContext *cx) -{ - JSObject *obj; - - /* - * Null obj's proto slot so that Object.prototype.* does not pollute block - * scopes. Make sure obj has its own scope too, since clearing proto does - * not affect OBJ_SCOPE(obj). - */ - obj = js_NewObject(cx, &js_BlockClass, NULL, NULL); - if (!obj || !js_GetMutableScope(cx, obj)) - return NULL; - OBJ_SET_PROTO(cx, obj, NULL); - return obj; -} - -JSObject * -js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent, - JSStackFrame *fp) -{ - JSObject *clone; - - clone = js_NewObject(cx, &js_BlockClass, proto, parent); - if (!clone) - return NULL; - clone->slots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fp); - clone->slots[JSSLOT_BLOCK_DEPTH] = - OBJ_GET_SLOT(cx, proto, JSSLOT_BLOCK_DEPTH); - return clone; -} - -/* - * XXXblock this reverses a path in the property tree -- try to share - * the prototype's scope harder! - */ -JSBool -js_PutBlockObject(JSContext *cx, JSObject *obj) -{ - JSStackFrame *fp; - uintN depth, slot; - JSScopeProperty *sprop; - - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); - JS_ASSERT(fp); - depth = OBJ_BLOCK_DEPTH(cx, obj); - for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; sprop = sprop->parent) { - if (sprop->getter != js_BlockClass.getProperty) - continue; - if (!(sprop->flags & SPROP_HAS_SHORTID)) - continue; - slot = depth + (uintN)sprop->shortid; - JS_ASSERT(slot < fp->script->depth); - if (!js_DefineNativeProperty(cx, obj, sprop->id, - fp->spbase[slot], NULL, NULL, - JSPROP_ENUMERATE | JSPROP_PERMANENT, - SPROP_HAS_SHORTID, sprop->shortid, - NULL)) { - JS_SetPrivate(cx, obj, NULL); - return JS_FALSE; - } - } - - return JS_SetPrivate(cx, obj, NULL); -} - -static JSBool -block_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSStackFrame *fp; - jsint slot; - - JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL)); - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); - if (!fp) - return JS_TRUE; - - slot = OBJ_BLOCK_DEPTH(cx, obj) + (uint16) JSVAL_TO_INT(id); - JS_ASSERT((uintN)slot < fp->script->depth); - *vp = fp->spbase[slot]; - return JS_TRUE; -} - -static JSBool -block_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSStackFrame *fp; - jsint slot; - - JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL)); - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); - if (!fp) - return JS_TRUE; - - slot = OBJ_BLOCK_DEPTH(cx, obj) + (uint16) JSVAL_TO_INT(id); - JS_ASSERT((uintN)slot < fp->script->depth); - fp->spbase[slot] = *vp; - return JS_TRUE; -} - -#if JS_HAS_XDR - -#define NO_PARENT_INDEX (jsatomid)-1 - -jsatomid -FindObjectAtomIndex(JSAtomMap *map, JSObject *obj) -{ - size_t i; - JSAtom *atom; - - for (i = 0; i < map->length; i++) { - atom = map->vector[i]; - if (ATOM_KEY(atom) == OBJECT_TO_JSVAL(obj)) - return i; - } - - return NO_PARENT_INDEX; -} - -static JSBool -block_xdrObject(JSXDRState *xdr, JSObject **objp) -{ - JSContext *cx; - jsatomid parentId; - JSAtomMap *atomMap; - JSObject *obj, *parent; - uint16 depth, count, i; - uint32 tmp; - JSTempValueRooter tvr; - JSScopeProperty *sprop; - jsid propid; - JSAtom *atom; - int16 shortid; - JSBool ok; - - cx = xdr->cx; -#ifdef __GNUC__ - obj = NULL; /* quell GCC overwarning */ -#endif - - atomMap = &xdr->script->atomMap; - if (xdr->mode == JSXDR_ENCODE) { - obj = *objp; - parent = OBJ_GET_PARENT(cx, obj); - parentId = FindObjectAtomIndex(atomMap, parent); - depth = OBJ_BLOCK_DEPTH(cx, obj); - count = OBJ_BLOCK_COUNT(cx, obj); - tmp = (uint32)(depth << 16) | count; - } -#ifdef __GNUC__ /* suppress bogus gcc warnings */ - else count = 0; -#endif - - /* First, XDR the parent atomid. */ - if (!JS_XDRUint32(xdr, &parentId)) - return JS_FALSE; - - if (xdr->mode == JSXDR_DECODE) { - obj = js_NewBlockObject(cx); - if (!obj) - return JS_FALSE; - *objp = obj; - - /* - * If there's a parent id, then get the parent out of our script's - * atomMap. We know that we XDR block object in outer-to-inner order, - * which means that getting the parent now will work. - */ - if (parentId == NO_PARENT_INDEX) { - parent = NULL; - } else { - atom = js_GetAtom(cx, atomMap, parentId); - JS_ASSERT(ATOM_IS_OBJECT(atom)); - parent = ATOM_TO_OBJECT(atom); - } - obj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent); - } - - JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(obj), &tvr); - - if (!JS_XDRUint32(xdr, &tmp)) { - JS_POP_TEMP_ROOT(cx, &tvr); - return JS_FALSE; - } - - if (xdr->mode == JSXDR_DECODE) { - depth = (uint16)(tmp >> 16); - count = (uint16)tmp; - obj->slots[JSSLOT_BLOCK_DEPTH] = INT_TO_JSVAL(depth); - } - - /* - * XDR the block object's properties. We know that there are 'count' - * properties to XDR, stored as id/shortid pairs. We do not XDR any - * non-native properties, only those that the compiler created. - */ - sprop = NULL; - ok = JS_TRUE; - for (i = 0; i < count; i++) { - if (xdr->mode == JSXDR_ENCODE) { - /* Find a property to XDR. */ - do { - /* If sprop is NULL, this is the first property. */ - sprop = sprop ? sprop->parent : OBJ_SCOPE(obj)->lastProp; - } while (!(sprop->flags & SPROP_HAS_SHORTID)); - - JS_ASSERT(sprop->getter == js_BlockClass.getProperty); - propid = sprop->id; - JS_ASSERT(JSID_IS_ATOM(propid)); - atom = JSID_TO_ATOM(propid); - shortid = sprop->shortid; - JS_ASSERT(shortid >= 0); - } - - /* XDR the real id, then the shortid. */ - if (!js_XDRStringAtom(xdr, &atom) || - !JS_XDRUint16(xdr, (uint16 *)&shortid)) { - ok = JS_FALSE; - break; - } - - if (xdr->mode == JSXDR_DECODE) { - if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), - JSVAL_VOID, NULL, NULL, - JSPROP_ENUMERATE | JSPROP_PERMANENT, - SPROP_HAS_SHORTID, shortid, NULL)) { - ok = JS_FALSE; - break; - } - } - } - - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; -} - -#else -# define block_xdrObject NULL -#endif - -JSClass js_BlockClass = { - "Block", - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | - JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_CACHED_PROTO(JSProto_Block), - JS_PropertyStub, JS_PropertyStub, block_getProperty, block_setProperty, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, - NULL, NULL, NULL, NULL, block_xdrObject, NULL, NULL, NULL -}; - -JSObject* -js_InitBlockClass(JSContext *cx, JSObject* obj) -{ - JSObject *proto; - - proto = JS_InitClass(cx, obj, NULL, &js_BlockClass, NULL, 0, NULL, - NULL, NULL, NULL); - if (!proto) - return NULL; - - OBJ_SET_PROTO(cx, proto, NULL); - return proto; -} - -JSObject * -js_InitObjectClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto; - jsval eval; - - proto = JS_InitClass(cx, obj, NULL, &js_ObjectClass, Object, 1, - object_props, object_methods, NULL, NULL); - if (!proto) - return NULL; - - /* ECMA (15.1.2.1) says 'eval' is also a property of the global object. */ - if (!OBJ_GET_PROPERTY(cx, proto, - ATOM_TO_JSID(cx->runtime->atomState.evalAtom), - &eval)) { - return NULL; - } - if (!OBJ_DEFINE_PROPERTY(cx, obj, - ATOM_TO_JSID(cx->runtime->atomState.evalAtom), - eval, NULL, NULL, 0, NULL)) { - return NULL; - } - - return proto; -} - -void -js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops, - JSClass *clasp) -{ - map->nrefs = nrefs; - map->ops = ops; - map->nslots = JS_INITIAL_NSLOTS; - map->freeslot = JSSLOT_FREE(clasp); -} - -JSObjectMap * -js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, - JSClass *clasp, JSObject *obj) -{ - return (JSObjectMap *) js_NewScope(cx, nrefs, ops, clasp, obj); -} - -void -js_DestroyObjectMap(JSContext *cx, JSObjectMap *map) -{ - js_DestroyScope(cx, (JSScope *)map); -} - -JSObjectMap * -js_HoldObjectMap(JSContext *cx, JSObjectMap *map) -{ - JS_ASSERT(map->nrefs >= 0); - JS_ATOMIC_INCREMENT(&map->nrefs); - return map; -} - -JSObjectMap * -js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj) -{ - JS_ASSERT(map->nrefs > 0); - JS_ATOMIC_DECREMENT(&map->nrefs); - if (map->nrefs == 0) { - map->ops->destroyObjectMap(cx, map); - return NULL; - } - if (MAP_IS_NATIVE(map) && ((JSScope *)map)->object == obj) - ((JSScope *)map)->object = NULL; - return map; -} - -static jsval * -AllocSlots(JSContext *cx, jsval *slots, uint32 nslots) -{ - size_t nbytes, obytes, minbytes; - uint32 i, oslots; - jsval *newslots; - - nbytes = (nslots + 1) * sizeof(jsval); - if (slots) { - oslots = slots[-1]; - obytes = (oslots + 1) * sizeof(jsval); - } else { - oslots = 0; - obytes = 0; - } - - if (nbytes <= GC_NBYTES_MAX) { - newslots = (jsval *) js_NewGCThing(cx, GCX_PRIVATE, nbytes); - } else { - newslots = (jsval *) - JS_realloc(cx, - (obytes <= GC_NBYTES_MAX) ? NULL : slots - 1, - nbytes); - } - if (!newslots) - return NULL; - - if (obytes != 0) { - /* If either nbytes or obytes fit in a GC-thing, we must copy. */ - minbytes = JS_MIN(nbytes, obytes); - if (minbytes <= GC_NBYTES_MAX) - memcpy(newslots + 1, slots, minbytes - sizeof(jsval)); - - /* If nbytes are in a GC-thing but obytes aren't, free obytes. */ - if (nbytes <= GC_NBYTES_MAX && obytes > GC_NBYTES_MAX) - JS_free(cx, slots - 1); - - /* If we're extending an allocation, initialize free slots. */ - if (nslots > oslots) { - for (i = 1 + oslots; i <= nslots; i++) - newslots[i] = JSVAL_VOID; - } - } - - newslots[0] = nslots; - return ++newslots; -} - -static void -FreeSlots(JSContext *cx, jsval *slots) -{ - size_t nbytes; - - /* - * NB: We count on smaller GC-things being finalized before larger things - * that become garbage during the same GC. Without this assumption, we - * couldn't load slots[-1] here without possibly loading a gcFreeList link - * (see struct JSGCThing in jsgc.h). - */ - nbytes = (slots[-1] + 1) * sizeof(jsval); - if (nbytes > GC_NBYTES_MAX) - JS_free(cx, slots - 1); -} - -extern JSBool -js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp) -{ - JSProtoKey key; - JSAtom *atom; - - key = JSCLASS_CACHED_PROTO_KEY(clasp); - if (key != JSProto_Null) { - *idp = INT_TO_JSID(key); - } else if (clasp->flags & JSCLASS_IS_ANONYMOUS) { - *idp = INT_TO_JSID(JSProto_Object); - } else { - atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); - if (!atom) - return JS_FALSE; - *idp = ATOM_TO_JSID(atom); - } - return JS_TRUE; -} - -JSObject * -js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) -{ - jsid id; - JSObject *obj; - JSObjectOps *ops; - JSObjectMap *map; - JSClass *protoclasp; - uint32 nslots, i; - jsval *newslots; - JSTempValueRooter tvr; - - /* Bootstrap the ur-object, and make it the default prototype object. */ - if (!proto) { - if (!js_GetClassId(cx, clasp, &id)) - return NULL; - if (!js_GetClassPrototype(cx, parent, id, &proto)) - return NULL; - if (!proto && - !js_GetClassPrototype(cx, parent, INT_TO_JSID(JSProto_Object), - &proto)) { - return NULL; - } - } - - /* Always call the class's getObjectOps hook if it has one. */ - ops = clasp->getObjectOps - ? clasp->getObjectOps(cx, clasp) - : &js_ObjectOps; - - /* - * Allocate a zeroed object from the GC heap. Do this *after* any other - * GC-thing allocations under js_GetClassPrototype or clasp->getObjectOps, - * to avoid displacing the newborn root for obj. - */ - obj = (JSObject *) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject)); - if (!obj) - return NULL; - - /* - * Root obj to prevent it from being collected out from under this call. - * to js_NewObject. AllocSlots can trigger a finalizer from a last-ditch - * GC calling JS_ClearNewbornRoots. There's also the possibilty of things - * happening under the objectHook call-out further below. - */ - JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); - - /* - * Share proto's map only if it has the same JSObjectOps, and only if - * proto's class has the same private and reserved slots as obj's map - * and class have. We assume that if prototype and object are of the - * same class, they always have the same number of computed reserved - * slots (returned via clasp->reserveSlots); otherwise, prototype and - * object classes must have the same (null or not) reserveSlots hook. - */ - if (proto && - (map = proto->map)->ops == ops && - ((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp || - (!((protoclasp->flags ^ clasp->flags) & - (JSCLASS_HAS_PRIVATE | - (JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) && - protoclasp->reserveSlots == clasp->reserveSlots))) - { - /* - * Default parent to the parent of the prototype, which was set from - * the parent of the prototype's constructor. - */ - if (!parent) - parent = OBJ_GET_PARENT(cx, proto); - - /* Share the given prototype's map. */ - obj->map = js_HoldObjectMap(cx, map); - - /* Ensure that obj starts with the minimum slots for clasp. */ - nslots = JS_INITIAL_NSLOTS; - } else { - /* Leave parent alone. Allocate a new map for obj. */ - map = ops->newObjectMap(cx, 1, ops, clasp, obj); - if (!map) - goto bad; - obj->map = map; - - /* Let ops->newObjectMap set nslots so as to reserve slots. */ - nslots = map->nslots; - } - - /* Allocate a slots vector, with a -1'st element telling its length. */ - newslots = AllocSlots(cx, NULL, nslots); - if (!newslots) { - js_DropObjectMap(cx, obj->map, obj); - obj->map = NULL; - goto bad; - } - - /* Set the proto, parent, and class properties. */ - newslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); - newslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent); - newslots[JSSLOT_CLASS] = PRIVATE_TO_JSVAL(clasp); - - /* Clear above JSSLOT_CLASS so the GC doesn't load uninitialized memory. */ - for (i = JSSLOT_CLASS + 1; i < nslots; i++) - newslots[i] = JSVAL_VOID; - - /* Store newslots after initializing all of 'em, just in case. */ - obj->slots = newslots; - - if (cx->runtime->objectHook) { - JS_KEEP_ATOMS(cx->runtime); - cx->runtime->objectHook(cx, obj, JS_TRUE, cx->runtime->objectHookData); - JS_UNKEEP_ATOMS(cx->runtime); - } - -out: - JS_POP_TEMP_ROOT(cx, &tvr); - cx->weakRoots.newborn[GCX_OBJECT] = (JSGCThing *) obj; - return obj; - -bad: - obj = NULL; - goto out; -} - -JS_STATIC_DLL_CALLBACK(JSObject *) -js_InitNullClass(JSContext *cx, JSObject *obj) -{ - JS_ASSERT(0); - return NULL; -} - -#define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *); -#include "jsproto.tbl" -#undef JS_PROTO - -static JSObjectOp lazy_prototype_init[JSProto_LIMIT] = { -#define JS_PROTO(name,code,init) init, -#include "jsproto.tbl" -#undef JS_PROTO -}; - -JSBool -js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, - JSObject **objp) -{ - JSBool ok; - JSObject *tmp, *cobj; - JSResolvingKey rkey; - JSResolvingEntry *rentry; - uint32 generation; - JSObjectOp init; - jsval v; - - while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) - obj = tmp; - if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) { - *objp = NULL; - return JS_TRUE; - } - - ok = JS_GetReservedSlot(cx, obj, key, &v); - if (!ok) - return JS_FALSE; - if (!JSVAL_IS_PRIMITIVE(v)) { - *objp = JSVAL_TO_OBJECT(v); - return JS_TRUE; - } - - rkey.obj = obj; - rkey.id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]); - if (!js_StartResolving(cx, &rkey, JSRESFLAG_LOOKUP, &rentry)) - return JS_FALSE; - if (!rentry) { - /* Already caching key in obj -- suppress recursion. */ - *objp = NULL; - return JS_TRUE; - } - generation = cx->resolvingTable->generation; - - cobj = NULL; - init = lazy_prototype_init[key]; - if (init) { - if (!init(cx, obj)) { - ok = JS_FALSE; - } else { - ok = JS_GetReservedSlot(cx, obj, key, &v); - if (ok && !JSVAL_IS_PRIMITIVE(v)) - cobj = JSVAL_TO_OBJECT(v); - } - } - - js_StopResolving(cx, &rkey, JSRESFLAG_LOOKUP, rentry, generation); - *objp = cobj; - return ok; -} - -JSBool -js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj) -{ - JS_ASSERT(!OBJ_GET_PARENT(cx, obj)); - if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) - return JS_TRUE; - - return JS_SetReservedSlot(cx, obj, key, OBJECT_TO_JSVAL(cobj)); -} - -JSBool -js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp) -{ - JSObject *obj, *cobj, *pobj; - JSProtoKey key; - JSProperty *prop; - JSScopeProperty *sprop; - - if (start || (cx->fp && (start = cx->fp->scopeChain) != NULL)) { - /* Find the topmost object in the scope chain. */ - do { - obj = start; - start = OBJ_GET_PARENT(cx, obj); - } while (start); - } else { - obj = cx->globalObject; - if (!obj) { - *vp = JSVAL_VOID; - return JS_TRUE; - } - } - - OBJ_TO_INNER_OBJECT(cx, obj); - if (!obj) - return JS_FALSE; - - if (JSID_IS_INT(id)) { - key = JSID_TO_INT(id); - JS_ASSERT(key != JSProto_Null); - if (!js_GetClassObject(cx, obj, key, &cobj)) - return JS_FALSE; - if (cobj) { - *vp = OBJECT_TO_JSVAL(cobj); - return JS_TRUE; - } - id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]); - } - - JS_ASSERT(OBJ_IS_NATIVE(obj)); - if (!js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_CLASSNAME, - &pobj, &prop)) { - return JS_FALSE; - } - if (!prop) { - *vp = JSVAL_VOID; - return JS_TRUE; - } - - JS_ASSERT(OBJ_IS_NATIVE(pobj)); - sprop = (JSScopeProperty *) prop; - JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))); - *vp = OBJ_GET_SLOT(cx, pobj, sprop->slot); - OBJ_DROP_PROPERTY(cx, pobj, prop); - return JS_TRUE; -} - -JSObject * -js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, - JSObject *parent, uintN argc, jsval *argv) -{ - jsid id; - jsval cval, rval; - JSTempValueRooter argtvr, tvr; - JSObject *obj, *ctor; - - JS_PUSH_TEMP_ROOT(cx, argc, argv, &argtvr); - - if (!js_GetClassId(cx, clasp, &id) || - !js_FindClassObject(cx, parent, id, &cval)) { - JS_POP_TEMP_ROOT(cx, &argtvr); - return NULL; - } - - if (JSVAL_IS_PRIMITIVE(cval)) { - js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK); - JS_POP_TEMP_ROOT(cx, &argtvr); - return NULL; - } - - /* - * Protect cval in case a crazy getter for .prototype uproots it. After - * this point, all control flow must exit through label out with obj set. - */ - JS_PUSH_SINGLE_TEMP_ROOT(cx, cval, &tvr); - - /* - * If proto or parent are NULL, set them to Constructor.prototype and/or - * Constructor.__parent__, just like JSOP_NEW does. - */ - ctor = JSVAL_TO_OBJECT(cval); - if (!parent) - parent = OBJ_GET_PARENT(cx, ctor); - if (!proto) { - if (!OBJ_GET_PROPERTY(cx, ctor, - ATOM_TO_JSID(cx->runtime->atomState - .classPrototypeAtom), - &rval)) { - obj = NULL; - goto out; - } - if (JSVAL_IS_OBJECT(rval)) - proto = JSVAL_TO_OBJECT(rval); - } - - obj = js_NewObject(cx, clasp, proto, parent); - if (!obj) - goto out; - - if (!js_InternalConstruct(cx, obj, cval, argc, argv, &rval)) - goto bad; - - if (JSVAL_IS_PRIMITIVE(rval)) - goto out; - obj = JSVAL_TO_OBJECT(rval); - - /* - * If the instance's class differs from what was requested, throw a type - * error. If the given class has both the JSCLASS_HAS_PRIVATE and the - * JSCLASS_CONSTRUCT_PROTOTYPE flags, and the instance does not have its - * private data set at this point, then the constructor was replaced and - * we should throw a type error. - */ - if (OBJ_GET_CLASS(cx, obj) != clasp || - (!(~clasp->flags & (JSCLASS_HAS_PRIVATE | - JSCLASS_CONSTRUCT_PROTOTYPE)) && - !JS_GetPrivate(cx, obj))) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_WRONG_CONSTRUCTOR, clasp->name); - goto bad; - } - -out: - JS_POP_TEMP_ROOT(cx, &tvr); - JS_POP_TEMP_ROOT(cx, &argtvr); - return obj; - -bad: - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - obj = NULL; - goto out; -} - -void -js_FinalizeObject(JSContext *cx, JSObject *obj) -{ - JSObjectMap *map; - - /* Cope with stillborn objects that have no map. */ - map = obj->map; - if (!map) - return; - JS_ASSERT(obj->slots); - - if (cx->runtime->objectHook) - cx->runtime->objectHook(cx, obj, JS_FALSE, cx->runtime->objectHookData); - - /* Remove all watchpoints with weak links to obj. */ - JS_ClearWatchPointsForObject(cx, obj); - - /* - * Finalize obj first, in case it needs map and slots. Optimized to use - * LOCKED_OBJ_GET_CLASS instead of OBJ_GET_CLASS, so we avoid "promoting" - * obj's scope from lock-free to lock-full (see jslock.c:ClaimScope) when - * we're called from the GC. Only the GC should call js_FinalizeObject, - * and no other threads run JS (and possibly racing to update obj->slots) - * while the GC is running. - */ - LOCKED_OBJ_GET_CLASS(obj)->finalize(cx, obj); - - /* Drop map and free slots. */ - js_DropObjectMap(cx, map, obj); - obj->map = NULL; - FreeSlots(cx, obj->slots); - obj->slots = NULL; -} - -/* XXXbe if one adds props, deletes earlier props, adds more, the last added - won't recycle the deleted props' slots. */ -JSBool -js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp) -{ - JSObjectMap *map; - JSClass *clasp; - uint32 nslots; - jsval *newslots; - - map = obj->map; - JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj); - clasp = LOCKED_OBJ_GET_CLASS(obj); - if (map->freeslot == JSSLOT_FREE(clasp)) { - /* Adjust map->freeslot to include computed reserved slots, if any. */ - if (clasp->reserveSlots) - map->freeslot += clasp->reserveSlots(cx, obj); - } - nslots = map->nslots; - if (map->freeslot >= nslots) { - nslots = map->freeslot; - JS_ASSERT(nslots >= JS_INITIAL_NSLOTS); - nslots += (nslots + 1) / 2; - - newslots = AllocSlots(cx, obj->slots, nslots); - if (!newslots) - return JS_FALSE; - map->nslots = nslots; - obj->slots = newslots; - } - -#ifdef TOO_MUCH_GC - obj->slots[map->freeslot] = JSVAL_VOID; -#endif - *slotp = map->freeslot++; - return JS_TRUE; -} - -void -js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot) -{ - JSObjectMap *map; - uint32 nslots; - jsval *newslots; - - OBJ_CHECK_SLOT(obj, slot); - obj->slots[slot] = JSVAL_VOID; - map = obj->map; - JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj); - if (map->freeslot == slot + 1) - map->freeslot = slot; - nslots = map->nslots; - if (nslots > JS_INITIAL_NSLOTS && map->freeslot < nslots / 2) { - nslots = map->freeslot; - nslots += nslots / 2; - if (nslots < JS_INITIAL_NSLOTS) - nslots = JS_INITIAL_NSLOTS; - - newslots = AllocSlots(cx, obj->slots, nslots); - if (!newslots) - return; - map->nslots = nslots; - obj->slots = newslots; - } -} - -/* JSVAL_INT_MAX as a string */ -#define JSVAL_INT_MAX_STRING "1073741823" - -#define CHECK_FOR_STRING_INDEX(id) \ - JS_BEGIN_MACRO \ - if (JSID_IS_ATOM(id)) { \ - JSAtom *atom_ = JSID_TO_ATOM(id); \ - JSString *str_ = ATOM_TO_STRING(atom_); \ - const jschar *cp_ = str_->chars; \ - JSBool negative_ = (*cp_ == '-'); \ - if (negative_) cp_++; \ - if (JS7_ISDEC(*cp_)) { \ - size_t n_ = str_->length - negative_; \ - if (n_ <= sizeof(JSVAL_INT_MAX_STRING) - 1) \ - id = CheckForStringIndex(id, cp_, cp_ + n_, negative_); \ - } \ - } \ - JS_END_MACRO - -static jsid -CheckForStringIndex(jsid id, const jschar *cp, const jschar *end, - JSBool negative) -{ - jsuint index = JS7_UNDEC(*cp++); - jsuint oldIndex = 0; - jsuint c = 0; - - if (index != 0) { - while (JS7_ISDEC(*cp)) { - oldIndex = index; - c = JS7_UNDEC(*cp); - index = 10 * index + c; - cp++; - } - } - if (cp == end && - (oldIndex < (JSVAL_INT_MAX / 10) || - (oldIndex == (JSVAL_INT_MAX / 10) && - c <= (JSVAL_INT_MAX % 10)))) { - if (negative) - index = 0 - index; - id = INT_TO_JSID((jsint)index); - } - return id; -} - -static JSBool -HidePropertyName(JSContext *cx, jsid *idp) -{ - jsid id; - JSAtom *atom, *hidden; - - id = *idp; - JS_ASSERT(JSID_IS_ATOM(id)); - - atom = JSID_TO_ATOM(id); - JS_ASSERT(!(atom->flags & ATOM_HIDDEN)); - JS_ASSERT(ATOM_IS_STRING(atom)); - - hidden = js_AtomizeString(cx, ATOM_TO_STRING(atom), ATOM_HIDDEN); - if (!hidden) - return JS_FALSE; - - /* - * Link hidden to unhidden atom to optimize call_enumerate -- this means - * the GC must mark a hidden atom's unhidden counterpart (see js_MarkAtom - * in jsgc.c). It uses the atom's entry.value member for this linkage. - */ - hidden->entry.value = atom; - *idp = ATOM_TO_JSID(hidden); - return JS_TRUE; -} - -JSScopeProperty * -js_AddHiddenProperty(JSContext *cx, JSObject *obj, jsid id, - JSPropertyOp getter, JSPropertyOp setter, uint32 slot, - uintN attrs, uintN flags, intN shortid) -{ - if (!HidePropertyName(cx, &id)) - return NULL; - - flags |= SPROP_IS_HIDDEN; - return js_AddNativeProperty(cx, obj, id, getter, setter, slot, attrs, - flags, shortid); -} - -JSBool -js_LookupHiddenProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - JSProperty **propp) -{ - return HidePropertyName(cx, &id) && - js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_HIDDEN, - objp, propp); -} - -JSScopeProperty * -js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id, - JSPropertyOp getter, JSPropertyOp setter, uint32 slot, - uintN attrs, uintN flags, intN shortid) -{ - JSScope *scope; - JSScopeProperty *sprop; - - JS_LOCK_OBJ(cx, obj); - scope = js_GetMutableScope(cx, obj); - if (!scope) { - sprop = NULL; - } else { - /* - * Handle old bug that took empty string as zero index. Also convert - * string indices to integers if appropriate. - */ - CHECK_FOR_STRING_INDEX(id); - sprop = js_AddScopeProperty(cx, scope, id, getter, setter, slot, attrs, - flags, shortid); - } - JS_UNLOCK_OBJ(cx, obj); - return sprop; -} - -JSScopeProperty * -js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj, - JSScopeProperty *sprop, uintN attrs, uintN mask, - JSPropertyOp getter, JSPropertyOp setter) -{ - JSScope *scope; - - JS_LOCK_OBJ(cx, obj); - scope = js_GetMutableScope(cx, obj); - if (!scope) { - sprop = NULL; - } else { - sprop = js_ChangeScopePropertyAttrs(cx, scope, sprop, attrs, mask, - getter, setter); - if (sprop) { - PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, sprop->id, - sprop); - } - } - JS_UNLOCK_OBJ(cx, obj); - return sprop; -} - -JSBool -js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs, - JSProperty **propp) -{ - return js_DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, - 0, 0, propp); -} - -/* - * Backward compatibility requires allowing addProperty hooks to mutate the - * nominal initial value of a slot-full property, while GC safety wants that - * value to be stored before the call-out through the hook. Optimize to do - * both while saving cycles for classes that stub their addProperty hook. - */ -#define ADD_PROPERTY_HELPER(cx,clasp,obj,scope,sprop,vp,cleanup) \ - JS_BEGIN_MACRO \ - if ((clasp)->addProperty != JS_PropertyStub) { \ - jsval nominal_ = *(vp); \ - if (!(clasp)->addProperty(cx, obj, SPROP_USERID(sprop), vp)) { \ - cleanup; \ - } \ - if (*(vp) != nominal_) { \ - if (SPROP_HAS_VALID_SLOT(sprop, scope)) \ - LOCKED_OBJ_SET_SLOT(obj, (sprop)->slot, *(vp)); \ - } \ - } \ - JS_END_MACRO - -JSBool -js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs, - uintN flags, intN shortid, JSProperty **propp) -{ - JSClass *clasp; - JSScope *scope; - JSScopeProperty *sprop; - - /* - * Handle old bug that took empty string as zero index. Also convert - * string indices to integers if appropriate. - */ - CHECK_FOR_STRING_INDEX(id); - -#if JS_HAS_GETTER_SETTER - /* - * If defining a getter or setter, we must check for its counterpart and - * update the attributes and property ops. A getter or setter is really - * only half of a property. - */ - if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) { - JSObject *pobj; - JSProperty *prop; - - /* - * If JS_THREADSAFE and id is found, js_LookupProperty returns with - * sprop non-null and pobj locked. If pobj == obj, the property is - * already in obj and obj has its own (mutable) scope. So if we are - * defining a getter whose setter was already defined, or vice versa, - * finish the job via js_ChangeScopePropertyAttributes, and refresh - * the property cache line for (obj, id) to map sprop. - */ - if (!js_LookupProperty(cx, obj, id, &pobj, &prop)) - return JS_FALSE; - sprop = (JSScopeProperty *) prop; - if (sprop && - pobj == obj && - (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) { - sprop = js_ChangeScopePropertyAttrs(cx, OBJ_SCOPE(obj), sprop, - attrs, sprop->attrs, - (attrs & JSPROP_GETTER) - ? getter - : sprop->getter, - (attrs & JSPROP_SETTER) - ? setter - : sprop->setter); - - /* NB: obj == pobj, so we can share unlock code at the bottom. */ - if (!sprop) - goto bad; - goto out; - } - - if (prop) { - /* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */ - OBJ_DROP_PROPERTY(cx, pobj, prop); - prop = NULL; - } - } -#endif /* JS_HAS_GETTER_SETTER */ - - /* Lock if object locking is required by this implementation. */ - JS_LOCK_OBJ(cx, obj); - - /* Use the object's class getter and setter by default. */ - clasp = LOCKED_OBJ_GET_CLASS(obj); - if (!getter) - getter = clasp->getProperty; - if (!setter) - setter = clasp->setProperty; - - /* Get obj's own scope if it has one, or create a new one for obj. */ - scope = js_GetMutableScope(cx, obj); - if (!scope) - goto bad; - - /* Add the property to scope, or replace an existing one of the same id. */ - if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES) - attrs |= JSPROP_SHARED; - sprop = js_AddScopeProperty(cx, scope, id, getter, setter, - SPROP_INVALID_SLOT, attrs, flags, shortid); - if (!sprop) - goto bad; - - /* Store value before calling addProperty, in case the latter GC's. */ - if (SPROP_HAS_VALID_SLOT(sprop, scope)) - LOCKED_OBJ_SET_SLOT(obj, sprop->slot, value); - - /* XXXbe called with lock held */ - ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, &value, - js_RemoveScopeProperty(cx, scope, id); - goto bad); - -#if JS_HAS_GETTER_SETTER -out: -#endif - PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop); - if (propp) - *propp = (JSProperty *) sprop; - else - JS_UNLOCK_OBJ(cx, obj); - return JS_TRUE; - -bad: - JS_UNLOCK_OBJ(cx, obj); - return JS_FALSE; -} - -/* - * Given pc pointing after a property accessing bytecode, return true if the - * access is "object-detecting" in the sense used by web scripts, e.g., when - * checking whether document.all is defined. - */ -static JSBool -Detecting(JSContext *cx, jsbytecode *pc) -{ - JSScript *script; - jsbytecode *endpc; - JSOp op; - JSAtom *atom; - - if (!cx->fp) - return JS_FALSE; - script = cx->fp->script; - for (endpc = script->code + script->length; pc < endpc; pc++) { - /* General case: a branch or equality op follows the access. */ - op = (JSOp) *pc; - if (js_CodeSpec[op].format & JOF_DETECTING) - return JS_TRUE; - - /* - * Special case #1: handle (document.all == null). Don't sweat about - * JS1.2's revision of the equality operators here. - */ - if (op == JSOP_NULL) { - if (++pc < endpc) - return *pc == JSOP_EQ || *pc == JSOP_NE; - break; - } - - /* - * Special case #2: handle (document.all == undefined). Don't worry - * about someone redefining undefined, which was added by Edition 3, - * so is read/write for backward compatibility. - */ - if (op == JSOP_NAME) { - atom = GET_ATOM(cx, script, pc); - if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] && - (pc += js_CodeSpec[op].length) < endpc) { - op = (JSOp) *pc; - return op == JSOP_EQ || op == JSOP_NE || - op == JSOP_NEW_EQ || op == JSOP_NEW_NE; - } - break; - } - - /* At this point, anything but grouping means we're not detecting. */ - if (op != JSOP_GROUP) - break; - } - return JS_FALSE; -} - -JS_FRIEND_API(JSBool) -js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - JSProperty **propp) -{ - return js_LookupPropertyWithFlags(cx, obj, id, 0, objp, propp); -} - -JSBool -js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, - JSObject **objp, JSProperty **propp) -{ - JSObject *start, *obj2, *proto; - JSScope *scope; - JSScopeProperty *sprop; - JSClass *clasp; - JSResolveOp resolve; - JSResolvingKey key; - JSResolvingEntry *entry; - uint32 generation; - JSNewResolveOp newresolve; - jsbytecode *pc; - const JSCodeSpec *cs; - uint32 format; - JSBool ok; - - /* - * Handle old bug that took empty string as zero index. Also convert - * string indices to integers if appropriate. - */ - CHECK_FOR_STRING_INDEX(id); - - /* Search scopes starting with obj and following the prototype link. */ - start = obj; - for (;;) { - JS_LOCK_OBJ(cx, obj); - scope = OBJ_SCOPE(obj); - if (scope->object == obj) { - sprop = SCOPE_GET_PROPERTY(scope, id); - } else { - /* Shared prototype scope: try resolve before lookup. */ - sprop = NULL; - } - - /* Try obj's class resolve hook if id was not found in obj's scope. */ - if (!sprop) { - clasp = LOCKED_OBJ_GET_CLASS(obj); - resolve = clasp->resolve; - if (resolve != JS_ResolveStub) { - /* Avoid recursion on (obj, id) already being resolved on cx. */ - key.obj = obj; - key.id = id; - - /* - * Once we have successfully added an entry for (obj, key) to - * cx->resolvingTable, control must go through cleanup: before - * returning. But note that JS_DHASH_ADD may find an existing - * entry, in which case we bail to suppress runaway recursion. - */ - if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) { - JS_UNLOCK_OBJ(cx, obj); - return JS_FALSE; - } - if (!entry) { - /* Already resolving id in obj -- suppress recursion. */ - JS_UNLOCK_OBJ(cx, obj); - goto out; - } - generation = cx->resolvingTable->generation; - - /* Null *propp here so we can test it at cleanup: safely. */ - *propp = NULL; - - if (clasp->flags & JSCLASS_NEW_RESOLVE) { - newresolve = (JSNewResolveOp)resolve; - if (!(flags & JSRESOLVE_CLASSNAME) && - cx->fp && - (pc = cx->fp->pc)) { - cs = &js_CodeSpec[*pc]; - format = cs->format; - if ((format & JOF_MODEMASK) != JOF_NAME) - flags |= JSRESOLVE_QUALIFIED; - if ((format & JOF_ASSIGNING) || - (cx->fp->flags & JSFRAME_ASSIGNING)) { - flags |= JSRESOLVE_ASSIGNING; - } else { - pc += cs->length; - if (Detecting(cx, pc)) - flags |= JSRESOLVE_DETECTING; - } - if (format & JOF_DECLARING) - flags |= JSRESOLVE_DECLARING; - } - obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START) - ? start - : NULL; - JS_UNLOCK_OBJ(cx, obj); - - /* Protect id and all atoms from a GC nested in resolve. */ - JS_KEEP_ATOMS(cx->runtime); - ok = newresolve(cx, obj, ID_TO_VALUE(id), flags, &obj2); - JS_UNKEEP_ATOMS(cx->runtime); - if (!ok) - goto cleanup; - - JS_LOCK_OBJ(cx, obj); - if (obj2) { - /* Resolved: juggle locks and lookup id again. */ - if (obj2 != obj) { - JS_UNLOCK_OBJ(cx, obj); - JS_LOCK_OBJ(cx, obj2); - } - scope = OBJ_SCOPE(obj2); - if (!MAP_IS_NATIVE(&scope->map)) { - /* Whoops, newresolve handed back a foreign obj2. */ - JS_ASSERT(obj2 != obj); - JS_UNLOCK_OBJ(cx, obj2); - ok = OBJ_LOOKUP_PROPERTY(cx, obj2, id, objp, propp); - if (!ok || *propp) - goto cleanup; - JS_LOCK_OBJ(cx, obj2); - } else { - /* - * Require that obj2 have its own scope now, as we - * do for old-style resolve. If it doesn't, then - * id was not truly resolved, and we'll find it in - * the proto chain, or miss it if obj2's proto is - * not on obj's proto chain. That last case is a - * "too bad!" case. - */ - if (scope->object == obj2) - sprop = SCOPE_GET_PROPERTY(scope, id); - } - if (sprop) { - JS_ASSERT(obj2 == scope->object); - obj = obj2; - } else if (obj2 != obj) { - JS_UNLOCK_OBJ(cx, obj2); - JS_LOCK_OBJ(cx, obj); - } - } - } else { - /* - * Old resolve always requires id re-lookup if obj owns - * its scope after resolve returns. - */ - JS_UNLOCK_OBJ(cx, obj); - ok = resolve(cx, obj, ID_TO_VALUE(id)); - if (!ok) - goto cleanup; - JS_LOCK_OBJ(cx, obj); - scope = OBJ_SCOPE(obj); - JS_ASSERT(MAP_IS_NATIVE(&scope->map)); - if (scope->object == obj) - sprop = SCOPE_GET_PROPERTY(scope, id); - } - - cleanup: - js_StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation); - if (!ok || *propp) - return ok; - } - } - - if (sprop) { - JS_ASSERT(OBJ_SCOPE(obj) == scope); - *objp = scope->object; /* XXXbe hide in jsscope.[ch] */ - - *propp = (JSProperty *) sprop; - return JS_TRUE; - } - - proto = LOCKED_OBJ_GET_PROTO(obj); - JS_UNLOCK_OBJ(cx, obj); - if (!proto) - break; - if (!OBJ_IS_NATIVE(proto)) - return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp); - obj = proto; - } - -out: - *objp = NULL; - *propp = NULL; - return JS_TRUE; -} - -JS_FRIEND_API(JSBool) -js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp, - JSProperty **propp) -{ - JSRuntime *rt; - JSObject *obj, *pobj, *lastobj; - JSScopeProperty *sprop; - JSProperty *prop; - - rt = cx->runtime; - obj = cx->fp->scopeChain; - do { - /* Try the property cache and return immediately on cache hit. */ - if (OBJ_IS_NATIVE(obj)) { - JS_LOCK_OBJ(cx, obj); - PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop); - if (sprop) { - JS_ASSERT(OBJ_IS_NATIVE(obj)); - *objp = obj; - *pobjp = obj; - *propp = (JSProperty *) sprop; - return JS_TRUE; - } - JS_UNLOCK_OBJ(cx, obj); - } - - /* If cache miss, take the slow path. */ - if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) - return JS_FALSE; - if (prop) { - if (OBJ_IS_NATIVE(pobj)) { - sprop = (JSScopeProperty *) prop; - PROPERTY_CACHE_FILL(&rt->propertyCache, pobj, id, sprop); - } - *objp = obj; - *pobjp = pobj; - *propp = prop; - return JS_TRUE; - } - lastobj = obj; - } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL); - - *objp = lastobj; - *pobjp = NULL; - *propp = NULL; - return JS_TRUE; -} - -JSObject * -js_FindIdentifierBase(JSContext *cx, jsid id) -{ - JSObject *obj, *pobj; - JSProperty *prop; - - /* - * Look for id's property along the "with" statement chain and the - * statically-linked scope chain. - */ - if (!js_FindProperty(cx, id, &obj, &pobj, &prop)) - return NULL; - if (prop) { - OBJ_DROP_PROPERTY(cx, pobj, prop); - return obj; - } - - /* - * Use the top-level scope from the scope chain, which won't end in the - * same scope as cx->globalObject for cross-context function calls. - */ - JS_ASSERT(obj); - - /* - * Property not found. Give a strict warning if binding an undeclared - * top-level variable. - */ - if (JS_HAS_STRICT_OPTION(cx)) { - JSString *str = JSVAL_TO_STRING(ID_TO_VALUE(id)); - if (!JS_ReportErrorFlagsAndNumber(cx, - JSREPORT_WARNING | JSREPORT_STRICT, - js_GetErrorMessage, NULL, - JSMSG_UNDECLARED_VAR, - JS_GetStringBytes(str))) { - return NULL; - } - } - return obj; -} - -JSBool -js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, - JSScopeProperty *sprop, jsval *vp) -{ - JSScope *scope; - uint32 slot; - int32 sample; - JSTempValueRooter tvr; - JSBool ok; - - JS_ASSERT(OBJ_IS_NATIVE(pobj)); - JS_ASSERT(JS_IS_OBJ_LOCKED(cx, pobj)); - scope = OBJ_SCOPE(pobj); - JS_ASSERT(scope->object == pobj); - - slot = sprop->slot; - *vp = (slot != SPROP_INVALID_SLOT) - ? LOCKED_OBJ_GET_SLOT(pobj, slot) - : JSVAL_VOID; - if (SPROP_HAS_STUB_GETTER(sprop)) - return JS_TRUE; - - sample = cx->runtime->propertyRemovals; - JS_UNLOCK_SCOPE(cx, scope); - JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr); - ok = SPROP_GET(cx, sprop, obj, pobj, vp); - JS_POP_TEMP_ROOT(cx, &tvr); - if (!ok) - return JS_FALSE; - - JS_LOCK_SCOPE(cx, scope); - JS_ASSERT(scope->object == pobj); - if (SLOT_IN_SCOPE(slot, scope) && - (JS_LIKELY(cx->runtime->propertyRemovals == sample) || - SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) { - LOCKED_OBJ_SET_SLOT(pobj, slot, *vp); - } - - return JS_TRUE; -} - -JSBool -js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp) -{ - JSScope *scope; - uint32 slot; - jsval pval; - int32 sample; - JSTempValueRooter tvr; - JSBool ok; - - JS_ASSERT(OBJ_IS_NATIVE(obj)); - JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj)); - scope = OBJ_SCOPE(obj); - JS_ASSERT(scope->object == obj); - - slot = sprop->slot; - if (slot != SPROP_INVALID_SLOT) { - pval = LOCKED_OBJ_GET_SLOT(obj, slot); - - /* If sprop has a stub setter, keep scope locked and just store *vp. */ - if (SPROP_HAS_STUB_SETTER(sprop)) - goto set_slot; - } else { - /* - * Allow API consumers to create shared properties with stub setters. - * Such properties lack value storage, so setting them is like writing - * to /dev/null. - */ - if (SPROP_HAS_STUB_SETTER(sprop)) - return JS_TRUE; - pval = JSVAL_VOID; - } - - sample = cx->runtime->propertyRemovals; - JS_UNLOCK_SCOPE(cx, scope); - JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr); - ok = SPROP_SET(cx, sprop, obj, obj, vp); - JS_POP_TEMP_ROOT(cx, &tvr); - if (!ok) - return JS_FALSE; - - JS_LOCK_SCOPE(cx, scope); - JS_ASSERT(scope->object == obj); - if (SLOT_IN_SCOPE(slot, scope) && - (JS_LIKELY(cx->runtime->propertyRemovals == sample) || - SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) { - set_slot: - GC_POKE(cx, pval); - LOCKED_OBJ_SET_SLOT(obj, slot, *vp); - } - - return JS_TRUE; -} - -JSBool -js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - JSObject *obj2; - JSProperty *prop; - JSScopeProperty *sprop; - - /* - * Handle old bug that took empty string as zero index. Also convert - * string indices to integers if appropriate. - */ - CHECK_FOR_STRING_INDEX(id); - - if (!js_LookupProperty(cx, obj, id, &obj2, &prop)) - return JS_FALSE; - if (!prop) { - jsbytecode *pc; - - *vp = JSVAL_VOID; - - if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, ID_TO_VALUE(id), vp)) - return JS_FALSE; - - /* - * Give a strict warning if foo.bar is evaluated by a script for an - * object foo with no property named 'bar'. - */ - if (JSVAL_IS_VOID(*vp) && cx->fp && (pc = cx->fp->pc)) { - JSOp op; - uintN flags; - JSString *str; - - op = *pc; - if (op == JSOP_GETXPROP || op == JSOP_GETXELEM) { - flags = JSREPORT_ERROR; - } else { - if (!JS_HAS_STRICT_OPTION(cx) || - (op != JSOP_GETPROP && op != JSOP_GETELEM)) { - return JS_TRUE; - } - - /* - * XXX do not warn about missing __iterator__ as the function - * may be called from JS_GetMethodById. See bug 355145. - */ - if (id == ATOM_TO_JSID(cx->runtime->atomState.iteratorAtom)) - return JS_TRUE; - - /* Kludge to allow (typeof foo == "undefined") tests. */ - JS_ASSERT(cx->fp->script); - pc += js_CodeSpec[op].length; - if (Detecting(cx, pc)) - return JS_TRUE; - - flags = JSREPORT_WARNING | JSREPORT_STRICT; - } - - /* Ok, bad undefined property reference: whine about it. */ - str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, - ID_TO_VALUE(id), NULL); - if (!str || - !JS_ReportErrorFlagsAndNumber(cx, flags, - js_GetErrorMessage, NULL, - JSMSG_UNDEFINED_PROP, - JS_GetStringBytes(str))) { - return JS_FALSE; - } - } - return JS_TRUE; - } - - if (!OBJ_IS_NATIVE(obj2)) { - OBJ_DROP_PROPERTY(cx, obj2, prop); - return OBJ_GET_PROPERTY(cx, obj2, id, vp); - } - - sprop = (JSScopeProperty *) prop; - if (!js_NativeGet(cx, obj, obj2, sprop, vp)) - return JS_FALSE; - - PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj2, id, sprop); - JS_UNLOCK_OBJ(cx, obj2); - return JS_TRUE; -} - -JSBool -js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - JSObject *pobj; - JSProperty *prop; - JSScopeProperty *sprop; - JSScope *scope; - uintN attrs, flags; - intN shortid; - JSClass *clasp; - JSPropertyOp getter, setter; - - /* - * Handle old bug that took empty string as zero index. Also convert - * string indices to integers if appropriate. - */ - CHECK_FOR_STRING_INDEX(id); - - if (!js_LookupProperty(cx, obj, id, &pobj, &prop)) - return JS_FALSE; - - if (prop && !OBJ_IS_NATIVE(pobj)) { - OBJ_DROP_PROPERTY(cx, pobj, prop); - prop = NULL; - } - sprop = (JSScopeProperty *) prop; - - /* - * Now either sprop is null, meaning id was not found in obj or one of its - * prototypes; or sprop is non-null, meaning id was found in pobj's scope. - * If JS_THREADSAFE and sprop is non-null, then scope is locked, and sprop - * is held: we must OBJ_DROP_PROPERTY or JS_UNLOCK_SCOPE before we return - * (the two are equivalent for native objects, but we use JS_UNLOCK_SCOPE - * because it is cheaper). - */ - attrs = JSPROP_ENUMERATE; - flags = 0; - shortid = 0; - clasp = OBJ_GET_CLASS(cx, obj); - getter = clasp->getProperty; - setter = clasp->setProperty; - - if (sprop) { - /* - * Set scope for use below. It was locked by js_LookupProperty, and - * we know pobj owns it (i.e., scope->object == pobj). Therefore we - * optimize JS_UNLOCK_OBJ(cx, pobj) into JS_UNLOCK_SCOPE(cx, scope). - */ - scope = OBJ_SCOPE(pobj); - - attrs = sprop->attrs; - if ((attrs & JSPROP_READONLY) || - (SCOPE_IS_SEALED(scope) && pobj == obj)) { - JS_UNLOCK_SCOPE(cx, scope); - - /* - * Here, we'll either return true or goto read_only_error, which - * reports a strict warning or throws an error. So we redefine - * the |flags| local variable to be JSREPORT_* flags to pass to - * JS_ReportErrorFlagsAndNumberUC at label read_only_error. We - * must likewise re-task flags further below for the other 'goto - * read_only_error;' case. - */ - flags = JSREPORT_ERROR; - if ((attrs & JSPROP_READONLY) && JS_VERSION_IS_ECMA(cx)) { - if (!JS_HAS_STRICT_OPTION(cx)) { - /* Just return true per ECMA if not in strict mode. */ - return JS_TRUE; - } - - /* Strict mode: report a read-only strict warning. */ - flags = JSREPORT_STRICT | JSREPORT_WARNING; - } - goto read_only_error; - } - - if (pobj != obj) { - /* - * We found id in a prototype object: prepare to share or shadow. - * NB: Thanks to the immutable, garbage-collected property tree - * maintained by jsscope.c in cx->runtime, we needn't worry about - * sprop going away behind our back after we've unlocked scope. - */ - JS_UNLOCK_SCOPE(cx, scope); - - /* Don't clone a shared prototype property. */ - if (attrs & JSPROP_SHARED) { - if (SPROP_HAS_STUB_SETTER(sprop) && - !(sprop->attrs & JSPROP_GETTER)) { - return JS_TRUE; - } - return SPROP_SET(cx, sprop, obj, pobj, vp); - } - - /* Restore attrs to the ECMA default for new properties. */ - attrs = JSPROP_ENUMERATE; - - /* - * Preserve the shortid, getter, and setter when shadowing any - * property that has a shortid. An old API convention requires - * that the property's getter and setter functions receive the - * shortid, not id, when they are called on the shadow we are - * about to create in obj's scope. - */ - if (sprop->flags & SPROP_HAS_SHORTID) { - flags = SPROP_HAS_SHORTID; - shortid = sprop->shortid; - getter = sprop->getter; - setter = sprop->setter; - } - - /* - * Forget we found the proto-property now that we've copied any - * needed member values. - */ - sprop = NULL; - } -#ifdef __GNUC__ /* suppress bogus gcc warnings */ - } else { - scope = NULL; -#endif - } - - if (!sprop) { - if (SCOPE_IS_SEALED(OBJ_SCOPE(obj)) && OBJ_SCOPE(obj)->object == obj) { - flags = JSREPORT_ERROR; - goto read_only_error; - } - - /* Find or make a property descriptor with the right heritage. */ - JS_LOCK_OBJ(cx, obj); - scope = js_GetMutableScope(cx, obj); - if (!scope) { - JS_UNLOCK_OBJ(cx, obj); - return JS_FALSE; - } - if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES) - attrs |= JSPROP_SHARED; - sprop = js_AddScopeProperty(cx, scope, id, getter, setter, - SPROP_INVALID_SLOT, attrs, flags, shortid); - if (!sprop) { - JS_UNLOCK_SCOPE(cx, scope); - return JS_FALSE; - } - - /* - * Initialize the new property value (passed to setter) to undefined. - * Note that we store before calling addProperty, to match the order - * in js_DefineNativeProperty. - */ - if (SPROP_HAS_VALID_SLOT(sprop, scope)) - LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID); - - /* XXXbe called with obj locked */ - ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, vp, - js_RemoveScopeProperty(cx, scope, id); - JS_UNLOCK_SCOPE(cx, scope); - return JS_FALSE); - - PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop); - } - - if (!js_NativeSet(cx, obj, sprop, vp)) - return JS_FALSE; - JS_UNLOCK_SCOPE(cx, scope); - return JS_TRUE; - - read_only_error: { - JSString *str = js_DecompileValueGenerator(cx, - JSDVG_IGNORE_STACK, - ID_TO_VALUE(id), - NULL); - if (!str) - return JS_FALSE; - return JS_ReportErrorFlagsAndNumberUC(cx, flags, js_GetErrorMessage, - NULL, JSMSG_READ_ONLY, - JS_GetStringChars(str)); - } -} - -JSBool -js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, - uintN *attrsp) -{ - JSBool noprop, ok; - JSScopeProperty *sprop; - - noprop = !prop; - if (noprop) { - if (!js_LookupProperty(cx, obj, id, &obj, &prop)) - return JS_FALSE; - if (!prop) { - *attrsp = 0; - return JS_TRUE; - } - if (!OBJ_IS_NATIVE(obj)) { - ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, attrsp); - OBJ_DROP_PROPERTY(cx, obj, prop); - return ok; - } - } - sprop = (JSScopeProperty *)prop; - *attrsp = sprop->attrs; - if (noprop) - OBJ_DROP_PROPERTY(cx, obj, prop); - return JS_TRUE; -} - -JSBool -js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, - uintN *attrsp) -{ - JSBool noprop, ok; - JSScopeProperty *sprop; - - noprop = !prop; - if (noprop) { - if (!js_LookupProperty(cx, obj, id, &obj, &prop)) - return JS_FALSE; - if (!prop) - return JS_TRUE; - if (!OBJ_IS_NATIVE(obj)) { - ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, attrsp); - OBJ_DROP_PROPERTY(cx, obj, prop); - return ok; - } - } - sprop = (JSScopeProperty *)prop; - sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, *attrsp, 0, - sprop->getter, sprop->setter); - if (noprop) - OBJ_DROP_PROPERTY(cx, obj, prop); - return (sprop != NULL); -} - -JSBool -js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) -{ - JSObject *proto; - JSProperty *prop; - JSScopeProperty *sprop; - JSString *str; - JSScope *scope; - JSBool ok; - - *rval = JSVAL_TRUE; - - /* - * Handle old bug that took empty string as zero index. Also convert - * string indices to integers if appropriate. - */ - CHECK_FOR_STRING_INDEX(id); - - if (!js_LookupProperty(cx, obj, id, &proto, &prop)) - return JS_FALSE; - if (!prop || proto != obj) { - /* - * If the property was found in a native prototype, check whether it's - * shared and permanent. Such a property stands for direct properties - * in all delegating objects, matching ECMA semantics without bloating - * each delegating object. - */ - if (prop) { - if (OBJ_IS_NATIVE(proto)) { - sprop = (JSScopeProperty *)prop; - if (SPROP_IS_SHARED_PERMANENT(sprop)) - *rval = JSVAL_FALSE; - } - OBJ_DROP_PROPERTY(cx, proto, prop); - if (*rval == JSVAL_FALSE) - return JS_TRUE; - } - - /* - * If no property, or the property comes unshared or impermanent from - * a prototype, call the class's delProperty hook, passing rval as the - * result parameter. - */ - return OBJ_GET_CLASS(cx, obj)->delProperty(cx, obj, ID_TO_VALUE(id), - rval); - } - - sprop = (JSScopeProperty *)prop; - if (sprop->attrs & JSPROP_PERMANENT) { - OBJ_DROP_PROPERTY(cx, obj, prop); - if (JS_VERSION_IS_ECMA(cx)) { - *rval = JSVAL_FALSE; - return JS_TRUE; - } - str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, - ID_TO_VALUE(id), NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_PERMANENT, JS_GetStringBytes(str)); - } - return JS_FALSE; - } - - /* XXXbe called with obj locked */ - if (!LOCKED_OBJ_GET_CLASS(obj)->delProperty(cx, obj, SPROP_USERID(sprop), - rval)) { - OBJ_DROP_PROPERTY(cx, obj, prop); - return JS_FALSE; - } - - scope = OBJ_SCOPE(obj); - if (SPROP_HAS_VALID_SLOT(sprop, scope)) - GC_POKE(cx, LOCKED_OBJ_GET_SLOT(obj, sprop->slot)); - - PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, NULL); - ok = js_RemoveScopeProperty(cx, scope, id); - OBJ_DROP_PROPERTY(cx, obj, prop); - return ok; -} - -JSBool -js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) -{ - jsval v, save; - JSString *str; - - v = save = OBJECT_TO_JSVAL(obj); - switch (hint) { - case JSTYPE_STRING: - /* - * Propagate the exception if js_TryMethod finds an appropriate - * method, and calling that method returned failure. - */ - if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, - &v)) { - return JS_FALSE; - } - - if (!JSVAL_IS_PRIMITIVE(v)) { - if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v)) - return JS_FALSE; - } - break; - - default: - if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v)) - return JS_FALSE; - if (!JSVAL_IS_PRIMITIVE(v)) { - JSType type = JS_TypeOfValue(cx, v); - if (type == hint || - (type == JSTYPE_FUNCTION && hint == JSTYPE_OBJECT)) { - goto out; - } - if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, - NULL, &v)) { - return JS_FALSE; - } - } - break; - } - if (!JSVAL_IS_PRIMITIVE(v)) { - /* Avoid recursive death through js_DecompileValueGenerator. */ - if (hint == JSTYPE_STRING) { - str = JS_InternString(cx, OBJ_GET_CLASS(cx, obj)->name); - if (!str) - return JS_FALSE; - } else { - str = NULL; - } - *vp = OBJECT_TO_JSVAL(obj); - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, save, str); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CANT_CONVERT_TO, - JS_GetStringBytes(str), - (hint == JSTYPE_VOID) - ? "primitive type" - : js_type_strs[hint]); - } - return JS_FALSE; - } -out: - *vp = v; - return JS_TRUE; -} - -JSIdArray * -js_NewIdArray(JSContext *cx, jsint length) -{ - JSIdArray *ida; - - ida = (JSIdArray *) - JS_malloc(cx, sizeof(JSIdArray) + (length-1) * sizeof(jsval)); - if (ida) - ida->length = length; - return ida; -} - -JSIdArray * -js_SetIdArrayLength(JSContext *cx, JSIdArray *ida, jsint length) -{ - JSIdArray *rida; - - rida = (JSIdArray *) - JS_realloc(cx, ida, sizeof(JSIdArray) + (length-1) * sizeof(jsval)); - if (!rida) - JS_DestroyIdArray(cx, ida); - else - rida->length = length; - return rida; -} - -/* Private type used to iterate over all properties of a native JS object */ -struct JSNativeIteratorState { - jsint next_index; /* index into jsid array */ - JSIdArray *ida; /* all property ids in enumeration */ - JSNativeIteratorState *next; /* double-linked list support */ - JSNativeIteratorState **prevp; -}; - -/* - * This function is used to enumerate the properties of native JSObjects - * and those host objects that do not define a JSNewEnumerateOp-style iterator - * function. - */ -JSBool -js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, - jsval *statep, jsid *idp) -{ - JSRuntime *rt; - JSObject *proto; - JSClass *clasp; - JSEnumerateOp enumerate; - JSScopeProperty *sprop, *lastProp; - jsint i, length; - JSScope *scope; - JSIdArray *ida; - JSNativeIteratorState *state; - - rt = cx->runtime; - clasp = OBJ_GET_CLASS(cx, obj); - enumerate = clasp->enumerate; - if (clasp->flags & JSCLASS_NEW_ENUMERATE) - return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp); - - switch (enum_op) { - case JSENUMERATE_INIT: - if (!enumerate(cx, obj)) - return JS_FALSE; - length = 0; - - /* - * The set of all property ids is pre-computed when the iterator - * is initialized so as to avoid problems with properties being - * deleted during the iteration. - */ - JS_LOCK_OBJ(cx, obj); - scope = OBJ_SCOPE(obj); - - /* - * If this object shares a scope with its prototype, don't enumerate - * its properties. Otherwise they will be enumerated a second time - * when the prototype object is enumerated. - */ - proto = OBJ_GET_PROTO(cx, obj); - if (proto && scope == OBJ_SCOPE(proto)) { - ida = js_NewIdArray(cx, 0); - if (!ida) { - JS_UNLOCK_OBJ(cx, obj); - return JS_FALSE; - } - } else { - /* Object has a private scope; Enumerate all props in scope. */ - for (sprop = lastProp = SCOPE_LAST_PROP(scope); sprop; - sprop = sprop->parent) { - if (( -#ifdef DUMP_CALL_TABLE - (cx->options & JSOPTION_LOGCALL_TOSOURCE) || -#endif - (sprop->attrs & JSPROP_ENUMERATE)) && - !(sprop->flags & SPROP_IS_ALIAS) && - (!SCOPE_HAD_MIDDLE_DELETE(scope) || - SCOPE_HAS_PROPERTY(scope, sprop))) { - length++; - } - } - ida = js_NewIdArray(cx, length); - if (!ida) { - JS_UNLOCK_OBJ(cx, obj); - return JS_FALSE; - } - i = length; - for (sprop = lastProp; sprop; sprop = sprop->parent) { - if (( -#ifdef DUMP_CALL_TABLE - (cx->options & JSOPTION_LOGCALL_TOSOURCE) || -#endif - (sprop->attrs & JSPROP_ENUMERATE)) && - !(sprop->flags & SPROP_IS_ALIAS) && - (!SCOPE_HAD_MIDDLE_DELETE(scope) || - SCOPE_HAS_PROPERTY(scope, sprop))) { - JS_ASSERT(i > 0); - ida->vector[--i] = sprop->id; - } - } - } - JS_UNLOCK_OBJ(cx, obj); - - state = (JSNativeIteratorState *) - JS_malloc(cx, sizeof(JSNativeIteratorState)); - if (!state) { - JS_DestroyIdArray(cx, ida); - return JS_FALSE; - } - state->ida = ida; - state->next_index = 0; - - JS_LOCK_RUNTIME(rt); - state->next = rt->nativeIteratorStates; - if (state->next) - state->next->prevp = &state->next; - state->prevp = &rt->nativeIteratorStates; - *state->prevp = state; - JS_UNLOCK_RUNTIME(rt); - - *statep = PRIVATE_TO_JSVAL(state); - if (idp) - *idp = INT_TO_JSVAL(length); - break; - - case JSENUMERATE_NEXT: - state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep); - ida = state->ida; - length = ida->length; - if (state->next_index != length) { - *idp = ida->vector[state->next_index++]; - break; - } - /* FALL THROUGH */ - - case JSENUMERATE_DESTROY: - state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep); - - JS_LOCK_RUNTIME(rt); - JS_ASSERT(rt->nativeIteratorStates); - JS_ASSERT(*state->prevp == state); - if (state->next) { - JS_ASSERT(state->next->prevp == &state->next); - state->next->prevp = state->prevp; - } - *state->prevp = state->next; - JS_UNLOCK_RUNTIME(rt); - - JS_DestroyIdArray(cx, state->ida); - JS_free(cx, state); - *statep = JSVAL_NULL; - break; - } - return JS_TRUE; -} - -void -js_MarkNativeIteratorStates(JSContext *cx) -{ - JSNativeIteratorState *state; - jsid *cursor, *end, id; - - state = cx->runtime->nativeIteratorStates; - if (!state) - return; - - do { - JS_ASSERT(*state->prevp == state); - cursor = state->ida->vector; - end = cursor + state->ida->length; - for (; cursor != end; ++cursor) { - id = *cursor; - MARK_ID(cx, id); - } - } while ((state = state->next) != NULL); -} - -JSBool -js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, - jsval *vp, uintN *attrsp) -{ - JSBool writing; - JSObject *pobj; - JSProperty *prop; - JSClass *clasp; - JSScopeProperty *sprop; - JSCheckAccessOp check; - - writing = (mode & JSACC_WRITE) != 0; - switch (mode & JSACC_TYPEMASK) { - case JSACC_PROTO: - pobj = obj; - if (!writing) - *vp = OBJ_GET_SLOT(cx, obj, JSSLOT_PROTO); - *attrsp = JSPROP_PERMANENT; - break; - - case JSACC_PARENT: - JS_ASSERT(!writing); - pobj = obj; - *vp = OBJ_GET_SLOT(cx, obj, JSSLOT_PARENT); - *attrsp = JSPROP_READONLY | JSPROP_PERMANENT; - break; - - default: - if (!js_LookupProperty(cx, obj, id, &pobj, &prop)) - return JS_FALSE; - if (!prop) { - if (!writing) - *vp = JSVAL_VOID; - *attrsp = 0; - clasp = OBJ_GET_CLASS(cx, obj); - return !clasp->checkAccess || - clasp->checkAccess(cx, obj, ID_TO_VALUE(id), mode, vp); - } - if (!OBJ_IS_NATIVE(pobj)) { - OBJ_DROP_PROPERTY(cx, pobj, prop); - return OBJ_CHECK_ACCESS(cx, pobj, id, mode, vp, attrsp); - } - - sprop = (JSScopeProperty *)prop; - *attrsp = sprop->attrs; - if (!writing) { - *vp = (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))) - ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot) - : JSVAL_VOID; - } - OBJ_DROP_PROPERTY(cx, pobj, prop); - } - - /* - * If obj's class has a stub (null) checkAccess hook, use the per-runtime - * checkObjectAccess callback, if configured. - * - * We don't want to require all classes to supply a checkAccess hook; we - * need that hook only for certain classes used when precompiling scripts - * and functions ("brutal sharing"). But for general safety of built-in - * magic properties such as __proto__ and __parent__, we route all access - * checks, even for classes that stub out checkAccess, through the global - * checkObjectAccess hook. This covers precompilation-based sharing and - * (possibly unintended) runtime sharing across trust boundaries. - */ - clasp = OBJ_GET_CLASS(cx, pobj); - check = clasp->checkAccess; - if (!check) - check = cx->runtime->checkObjectAccess; - return !check || check(cx, pobj, ID_TO_VALUE(id), mode, vp); -} - -#ifdef JS_THREADSAFE -void -js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop) -{ - JS_UNLOCK_OBJ(cx, obj); -} -#endif - -static void -ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags) -{ - /* - * The decompiler may need to access the args of the function in - * progress rather than the one we had hoped to call. - * So we switch the cx->fp to the frame below us. We stick the - * current frame in the dormantFrameChain to protect it from gc. - */ - - JSStackFrame *fp = cx->fp; - if (fp->down) { - JS_ASSERT(!fp->dormantNext); - fp->dormantNext = cx->dormantFrameChain; - cx->dormantFrameChain = fp; - cx->fp = fp->down; - } - - js_ReportIsNotFunction(cx, vp, flags); - - if (fp->down) { - JS_ASSERT(cx->dormantFrameChain == fp); - cx->dormantFrameChain = fp->dormantNext; - fp->dormantNext = NULL; - cx->fp = fp; - } -} - -#ifdef NARCISSUS -static JSBool -GetCurrentExecutionContext(JSContext *cx, JSObject *obj, jsval *rval) -{ - JSObject *tmp; - jsval xcval; - - while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) - obj = tmp; - if (!OBJ_GET_PROPERTY(cx, obj, - ATOM_TO_JSID(cx->runtime->atomState - .ExecutionContextAtom), - &xcval)) { - return JS_FALSE; - } - if (JSVAL_IS_PRIMITIVE(xcval)) { - JS_ReportError(cx, "invalid ExecutionContext in global object"); - return JS_FALSE; - } - if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(xcval), - ATOM_TO_JSID(cx->runtime->atomState.currentAtom), - rval)) { - return JS_FALSE; - } - return JS_TRUE; -} -#endif - -JSBool -js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSClass *clasp; - - clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2])); - if (!clasp->call) { -#ifdef NARCISSUS - JSObject *callee, *args; - jsval fval, nargv[3]; - JSBool ok; - - callee = JSVAL_TO_OBJECT(argv[-2]); - if (!OBJ_GET_PROPERTY(cx, callee, - ATOM_TO_JSID(cx->runtime->atomState.callAtom), - &fval)) { - return JS_FALSE; - } - if (VALUE_IS_FUNCTION(cx, fval)) { - if (!GetCurrentExecutionContext(cx, obj, &nargv[2])) - return JS_FALSE; - args = js_GetArgsObject(cx, cx->fp); - if (!args) - return JS_FALSE; - nargv[0] = OBJECT_TO_JSVAL(obj); - nargv[1] = OBJECT_TO_JSVAL(args); - return js_InternalCall(cx, callee, fval, 3, nargv, rval); - } - if (JSVAL_IS_OBJECT(fval) && JSVAL_TO_OBJECT(fval) != callee) { - argv[-2] = fval; - ok = js_Call(cx, obj, argc, argv, rval); - argv[-2] = OBJECT_TO_JSVAL(callee); - return ok; - } -#endif - ReportIsNotFunction(cx, &argv[-2], cx->fp->flags & JSFRAME_ITERATOR); - return JS_FALSE; - } - return clasp->call(cx, obj, argc, argv, rval); -} - -JSBool -js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSClass *clasp; - - clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2])); - if (!clasp->construct) { -#ifdef NARCISSUS - JSObject *callee, *args; - jsval cval, nargv[2]; - JSBool ok; - - callee = JSVAL_TO_OBJECT(argv[-2]); - if (!OBJ_GET_PROPERTY(cx, callee, - ATOM_TO_JSID(cx->runtime->atomState - .constructAtom), - &cval)) { - return JS_FALSE; - } - if (VALUE_IS_FUNCTION(cx, cval)) { - if (!GetCurrentExecutionContext(cx, obj, &nargv[1])) - return JS_FALSE; - args = js_GetArgsObject(cx, cx->fp); - if (!args) - return JS_FALSE; - nargv[0] = OBJECT_TO_JSVAL(args); - return js_InternalCall(cx, callee, cval, 2, nargv, rval); - } - if (JSVAL_IS_OBJECT(cval) && JSVAL_TO_OBJECT(cval) != callee) { - argv[-2] = cval; - ok = js_Call(cx, obj, argc, argv, rval); - argv[-2] = OBJECT_TO_JSVAL(callee); - return ok; - } -#endif - ReportIsNotFunction(cx, &argv[-2], JSV2F_CONSTRUCT); - return JS_FALSE; - } - return clasp->construct(cx, obj, argc, argv, rval); -} - -JSBool -js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - JSClass *clasp; - JSString *str; - - clasp = OBJ_GET_CLASS(cx, obj); - if (clasp->hasInstance) - return clasp->hasInstance(cx, obj, v, bp); -#ifdef NARCISSUS - { - jsval fval, rval; - - if (!OBJ_GET_PROPERTY(cx, obj, - ATOM_TO_JSID(cx->runtime->atomState - .hasInstanceAtom), - &fval)) { - return JS_FALSE; - } - if (VALUE_IS_FUNCTION(cx, fval)) { - return js_InternalCall(cx, obj, fval, 1, &v, &rval) && - js_ValueToBoolean(cx, rval, bp); - } - } -#endif - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, - OBJECT_TO_JSVAL(obj), NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_INSTANCEOF_RHS, - JS_GetStringBytes(str)); - } - return JS_FALSE; -} - -JSBool -js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - JSObject *obj2; - - *bp = JS_FALSE; - if (JSVAL_IS_PRIMITIVE(v)) - return JS_TRUE; - obj2 = JSVAL_TO_OBJECT(v); - while ((obj2 = OBJ_GET_PROTO(cx, obj2)) != NULL) { - if (obj2 == obj) { - *bp = JS_TRUE; - break; - } - } - return JS_TRUE; -} - -JSBool -js_GetClassPrototype(JSContext *cx, JSObject *scope, jsid id, - JSObject **protop) -{ - jsval v; - JSObject *ctor; - - if (!js_FindClassObject(cx, scope, id, &v)) - return JS_FALSE; - if (VALUE_IS_FUNCTION(cx, v)) { - ctor = JSVAL_TO_OBJECT(v); - if (!OBJ_GET_PROPERTY(cx, ctor, - ATOM_TO_JSID(cx->runtime->atomState - .classPrototypeAtom), - &v)) { - return JS_FALSE; - } - if (!JSVAL_IS_PRIMITIVE(v)) { - /* - * Set the newborn root in case v is otherwise unreferenced. - * It's ok to overwrite newborn roots here, since the getter - * called just above could have. Unlike the common GC rooting - * model, our callers do not have to protect protop thanks to - * this newborn root, since they all immediately create a new - * instance that delegates to this object, or just query the - * prototype for its class. - */ - cx->weakRoots.newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(v); - } - } - *protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL; - return JS_TRUE; -} - -/* - * For shared precompilation of function objects, we support cloning on entry - * to an execution context in which the function declaration or expression - * should be processed as if it were not precompiled, where the precompiled - * function's scope chain does not match the execution context's. The cloned - * function object carries its execution-context scope in its parent slot; it - * links to the precompiled function (the "clone-parent") via its proto slot. - * - * Note that this prototype-based delegation leaves an unchecked access path - * from the clone to the clone-parent's 'constructor' property. If the clone - * lives in a less privileged or shared scope than the clone-parent, this is - * a security hole, a sharing hazard, or both. Therefore we check all such - * accesses with the following getter/setter pair, which we use when defining - * 'constructor' in f.prototype for all function objects f. - */ -static JSBool -CheckCtorGetAccess(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSAtom *atom; - uintN attrs; - - atom = cx->runtime->atomState.constructorAtom; - JS_ASSERT(id == ATOM_KEY(atom)); - return OBJ_CHECK_ACCESS(cx, obj, ATOM_TO_JSID(atom), JSACC_READ, - vp, &attrs); -} - -static JSBool -CheckCtorSetAccess(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSAtom *atom; - uintN attrs; - - atom = cx->runtime->atomState.constructorAtom; - JS_ASSERT(id == ATOM_KEY(atom)); - return OBJ_CHECK_ACCESS(cx, obj, ATOM_TO_JSID(atom), JSACC_WRITE, - vp, &attrs); -} - -JSBool -js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, - uintN attrs) -{ - /* - * Use the given attributes for the prototype property of the constructor, - * as user-defined constructors have a DontDelete prototype (which may be - * reset), while native or "system" constructors have DontEnum | ReadOnly | - * DontDelete. - */ - if (!OBJ_DEFINE_PROPERTY(cx, ctor, - ATOM_TO_JSID(cx->runtime->atomState - .classPrototypeAtom), - OBJECT_TO_JSVAL(proto), - JS_PropertyStub, JS_PropertyStub, - attrs, NULL)) { - return JS_FALSE; - } - - /* - * ECMA says that Object.prototype.constructor, or f.prototype.constructor - * for a user-defined function f, is DontEnum. - */ - return OBJ_DEFINE_PROPERTY(cx, proto, - ATOM_TO_JSID(cx->runtime->atomState - .constructorAtom), - OBJECT_TO_JSVAL(ctor), - CheckCtorGetAccess, CheckCtorSetAccess, - 0, NULL); -} - -JSBool -js_ValueToObject(JSContext *cx, jsval v, JSObject **objp) -{ - JSObject *obj; - - if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { - obj = NULL; - } else if (JSVAL_IS_OBJECT(v)) { - obj = JSVAL_TO_OBJECT(v); - if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_OBJECT, &v)) - return JS_FALSE; - if (JSVAL_IS_OBJECT(v)) - obj = JSVAL_TO_OBJECT(v); - } else { - if (JSVAL_IS_STRING(v)) { - obj = js_StringToObject(cx, JSVAL_TO_STRING(v)); - } else if (JSVAL_IS_INT(v)) { - obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v)); - } else if (JSVAL_IS_DOUBLE(v)) { - obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v)); - } else { - JS_ASSERT(JSVAL_IS_BOOLEAN(v)); - obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v)); - } - if (!obj) - return JS_FALSE; - } - *objp = obj; - return JS_TRUE; -} - -JSObject * -js_ValueToNonNullObject(JSContext *cx, jsval v) -{ - JSObject *obj; - JSString *str; - - if (!js_ValueToObject(cx, v, &obj)) - return NULL; - if (!obj) { - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_NO_PROPERTIES, JS_GetStringBytes(str)); - } - } - return obj; -} - -JSBool -js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval) -{ - jsval argv[1]; - - argv[0] = ATOM_KEY(cx->runtime->atomState.typeAtoms[type]); - return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 1, argv, - rval); -} - -JSBool -js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom, - uintN argc, jsval *argv, jsval *rval) -{ - JSErrorReporter older; - jsid id; - jsval fval; - JSBool ok; - int stackDummy; - - if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); - return JS_FALSE; - } - - /* - * Report failure only if an appropriate method was found, and calling it - * returned failure. We propagate failure in this case to make exceptions - * behave properly. - */ - older = JS_SetErrorReporter(cx, NULL); - id = ATOM_TO_JSID(atom); - fval = JSVAL_VOID; -#if JS_HAS_XML_SUPPORT - if (OBJECT_IS_XML(cx, obj)) { - JSXMLObjectOps *ops; - - ops = (JSXMLObjectOps *) obj->map->ops; - obj = ops->getMethod(cx, obj, id, &fval); - ok = (obj != NULL); - } else -#endif - { - ok = OBJ_GET_PROPERTY(cx, obj, id, &fval); - } - if (!ok) - JS_ClearPendingException(cx); - JS_SetErrorReporter(cx, older); - - return JSVAL_IS_PRIMITIVE(fval) || - js_InternalCall(cx, obj, fval, argc, argv, rval); -} - -#if JS_HAS_XDR - -JSBool -js_XDRObject(JSXDRState *xdr, JSObject **objp) -{ - JSContext *cx; - JSAtom *atom; - JSClass *clasp; - uint32 classId, classDef; - JSProtoKey protoKey; - jsid classKey; - JSObject *proto; - - cx = xdr->cx; - atom = NULL; - if (xdr->mode == JSXDR_ENCODE) { - clasp = OBJ_GET_CLASS(cx, *objp); - classId = JS_XDRFindClassIdByName(xdr, clasp->name); - classDef = !classId; - if (classDef) { - if (!JS_XDRRegisterClass(xdr, clasp, &classId)) - return JS_FALSE; - protoKey = JSCLASS_CACHED_PROTO_KEY(clasp); - if (protoKey != JSProto_Null) { - classDef |= (protoKey << 1); - } else { - atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); - if (!atom) - return JS_FALSE; - } - } - } else { - clasp = NULL; /* quell GCC overwarning */ - classDef = 0; - } - - /* - * XDR a flag word, which could be 0 for a class use, in which case no - * name follows, only the id in xdr's class registry; 1 for a class def, - * in which case the flag word is followed by the class name transferred - * from or to atom; or a value greater than 1, an odd number that when - * divided by two yields the JSProtoKey for class. In the last case, as - * in the 0 classDef case, no name is transferred via atom. - */ - if (!JS_XDRUint32(xdr, &classDef)) - return JS_FALSE; - if (classDef == 1 && !js_XDRCStringAtom(xdr, &atom)) - return JS_FALSE; - - if (!JS_XDRUint32(xdr, &classId)) - return JS_FALSE; - - if (xdr->mode == JSXDR_DECODE) { - if (classDef) { - /* NB: we know that JSProto_Null is 0 here, for backward compat. */ - protoKey = classDef >> 1; - classKey = (protoKey != JSProto_Null) - ? INT_TO_JSID(protoKey) - : ATOM_TO_JSID(atom); - if (!js_GetClassPrototype(cx, NULL, classKey, &proto)) - return JS_FALSE; - clasp = OBJ_GET_CLASS(cx, proto); - if (!JS_XDRRegisterClass(xdr, clasp, &classId)) - return JS_FALSE; - } else { - clasp = JS_XDRFindClassById(xdr, classId); - if (!clasp) { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)classId); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CANT_FIND_CLASS, numBuf); - return JS_FALSE; - } - } - } - - if (!clasp->xdrObject) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CANT_XDR_CLASS, clasp->name); - return JS_FALSE; - } - return clasp->xdrObject(xdr, objp); -} - -#endif /* JS_HAS_XDR */ - -#ifdef DEBUG_brendan - -#include -#include - -uint32 js_entry_count_max; -uint32 js_entry_count_sum; -double js_entry_count_sqsum; -uint32 js_entry_count_hist[11]; - -static void -MeterEntryCount(uintN count) -{ - if (count) { - js_entry_count_sum += count; - js_entry_count_sqsum += (double)count * count; - if (count > js_entry_count_max) - js_entry_count_max = count; - } - js_entry_count_hist[JS_MIN(count, 10)]++; -} - -#define DEBUG_scopemeters -#endif /* DEBUG_brendan */ - -#ifdef DEBUG_scopemeters -void -js_DumpScopeMeters(JSRuntime *rt) -{ - static FILE *logfp; - if (!logfp) - logfp = fopen("/tmp/scope.stats", "a"); - - { - double mean = 0., var = 0., sigma = 0.; - double nscopes = rt->liveScopes; - double nentrys = js_entry_count_sum; - if (nscopes > 0 && nentrys >= 0) { - mean = nentrys / nscopes; - var = nscopes * js_entry_count_sqsum - nentrys * nentrys; - if (var < 0.0 || nscopes <= 1) - var = 0.0; - else - var /= nscopes * (nscopes - 1); - - /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ - sigma = (var != 0.) ? sqrt(var) : 0.; - } - - fprintf(logfp, - "scopes %g entries %g mean %g sigma %g max %u", - nscopes, nentrys, mean, sigma, js_entry_count_max); - } - - fprintf(logfp, " histogram %u %u %u %u %u %u %u %u %u %u %u\n", - js_entry_count_hist[0], js_entry_count_hist[1], - js_entry_count_hist[2], js_entry_count_hist[3], - js_entry_count_hist[4], js_entry_count_hist[5], - js_entry_count_hist[6], js_entry_count_hist[7], - js_entry_count_hist[8], js_entry_count_hist[9], - js_entry_count_hist[10]); - js_entry_count_sum = js_entry_count_max = 0; - js_entry_count_sqsum = 0; - memset(js_entry_count_hist, 0, sizeof js_entry_count_hist); - fflush(logfp); -} -#endif - -uint32 -js_Mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSScope *scope; - JSScopeProperty *sprop; - JSClass *clasp; - - JS_ASSERT(OBJ_IS_NATIVE(obj)); - scope = OBJ_SCOPE(obj); -#ifdef DEBUG_brendan - if (scope->object == obj) - MeterEntryCount(scope->entryCount); -#endif - - JS_ASSERT(!SCOPE_LAST_PROP(scope) || - SCOPE_HAS_PROPERTY(scope, SCOPE_LAST_PROP(scope))); - - for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { - if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop)) - continue; - MARK_SCOPE_PROPERTY(cx, sprop); - } - - /* No one runs while the GC is running, so we can use LOCKED_... here. */ - clasp = LOCKED_OBJ_GET_CLASS(obj); - if (clasp->mark) - (void) clasp->mark(cx, obj, NULL); - - if (scope->object != obj) { - /* - * An unmutated object that shares a prototype's scope. We can't tell - * how many slots are allocated and in use at obj->slots by looking at - * scope, so we get obj->slots' length from its -1'st element. - */ - return (uint32) obj->slots[-1]; - } - return JS_MIN(scope->map.freeslot, scope->map.nslots); -} - -void -js_Clear(JSContext *cx, JSObject *obj) -{ - JSScope *scope; - JSRuntime *rt; - JSScopeProperty *sprop; - uint32 i, n; - - /* - * Clear our scope and the property cache of all obj's properties only if - * obj owns the scope (i.e., not if obj is unmutated and therefore sharing - * its prototype's scope). NB: we do not clear any reserved slots lying - * below JSSLOT_FREE(clasp). - */ - JS_LOCK_OBJ(cx, obj); - scope = OBJ_SCOPE(obj); - if (scope->object == obj) { - /* Clear the property cache before we clear the scope. */ - rt = cx->runtime; - for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { - if (!SCOPE_HAD_MIDDLE_DELETE(scope) || - SCOPE_HAS_PROPERTY(scope, sprop)) { - PROPERTY_CACHE_FILL(&rt->propertyCache, obj, sprop->id, NULL); - } - } - - /* Now that we're done using scope->lastProp/table, clear scope. */ - js_ClearScope(cx, scope); - - /* Clear slot values and reset freeslot so we're consistent. */ - i = scope->map.nslots; - n = JSSLOT_FREE(LOCKED_OBJ_GET_CLASS(obj)); - while (--i >= n) - obj->slots[i] = JSVAL_VOID; - scope->map.freeslot = n; - } - JS_UNLOCK_OBJ(cx, obj); -} - -jsval -js_GetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot) -{ - jsval v; - - JS_LOCK_OBJ(cx, obj); - v = (slot < (uint32) obj->slots[-1]) ? obj->slots[slot] : JSVAL_VOID; - JS_UNLOCK_OBJ(cx, obj); - return v; -} - -JSBool -js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v) -{ - JSScope *scope; - uint32 nslots; - JSClass *clasp; - jsval *newslots; - - JS_LOCK_OBJ(cx, obj); - scope = OBJ_SCOPE(obj); - nslots = (uint32) obj->slots[-1]; - if (slot >= nslots) { - /* - * At this point, obj may or may not own scope. If some path calls - * js_GetMutableScope but does not add a slot-owning property, then - * scope->object == obj but nslots will be nominal. If obj shares a - * prototype's scope, then we cannot update scope->map here, but we - * must update obj->slots[-1] when we grow obj->slots. - * - * See js_Mark, before the last return, where we make a special case - * for unmutated (scope->object != obj) objects. - */ - JS_ASSERT(nslots == JS_INITIAL_NSLOTS); - clasp = LOCKED_OBJ_GET_CLASS(obj); - nslots = JSSLOT_FREE(clasp); - if (clasp->reserveSlots) - nslots += clasp->reserveSlots(cx, obj); - JS_ASSERT(slot < nslots); - - newslots = AllocSlots(cx, obj->slots, nslots); - if (!newslots) { - JS_UNLOCK_SCOPE(cx, scope); - return JS_FALSE; - } - if (scope->object == obj) - scope->map.nslots = nslots; - obj->slots = newslots; - } - - /* Whether or not we grew nslots, we may need to advance freeslot. */ - if (scope->object == obj && slot >= scope->map.freeslot) - scope->map.freeslot = slot + 1; - - obj->slots[slot] = v; - JS_UNLOCK_SCOPE(cx, scope); - return JS_TRUE; -} - -#ifdef DEBUG - -/* Routines to print out values during debugging. */ - -void printChar(jschar *cp) { - fprintf(stderr, "jschar* (0x%p) \"", (void *)cp); - while (*cp) - fputc(*cp++, stderr); - fputc('"', stderr); - fputc('\n', stderr); -} - -void printString(JSString *str) { - size_t i, n; - jschar *s; - fprintf(stderr, "string (0x%p) \"", (void *)str); - s = JSSTRING_CHARS(str); - for (i=0, n=JSSTRING_LENGTH(str); i < n; i++) - fputc(s[i], stderr); - fputc('"', stderr); - fputc('\n', stderr); -} - -void printVal(JSContext *cx, jsval val); - -void printObj(JSContext *cx, JSObject *jsobj) { - jsuint i; - jsval val; - JSClass *clasp; - - fprintf(stderr, "object 0x%p\n", (void *)jsobj); - clasp = OBJ_GET_CLASS(cx, jsobj); - fprintf(stderr, "class 0x%p %s\n", (void *)clasp, clasp->name); - for (i=0; i < jsobj->map->nslots; i++) { - fprintf(stderr, "slot %3d ", i); - val = jsobj->slots[i]; - if (JSVAL_IS_OBJECT(val)) - fprintf(stderr, "object 0x%p\n", (void *)JSVAL_TO_OBJECT(val)); - else - printVal(cx, val); - } -} - -void printVal(JSContext *cx, jsval val) { - fprintf(stderr, "val %d (0x%p) = ", (int)val, (void *)val); - if (JSVAL_IS_NULL(val)) { - fprintf(stderr, "null\n"); - } else if (JSVAL_IS_VOID(val)) { - fprintf(stderr, "undefined\n"); - } else if (JSVAL_IS_OBJECT(val)) { - printObj(cx, JSVAL_TO_OBJECT(val)); - } else if (JSVAL_IS_INT(val)) { - fprintf(stderr, "(int) %d\n", JSVAL_TO_INT(val)); - } else if (JSVAL_IS_STRING(val)) { - printString(JSVAL_TO_STRING(val)); - } else if (JSVAL_IS_DOUBLE(val)) { - fprintf(stderr, "(double) %g\n", *JSVAL_TO_DOUBLE(val)); - } else { - JS_ASSERT(JSVAL_IS_BOOLEAN(val)); - fprintf(stderr, "(boolean) %s\n", - JSVAL_TO_BOOLEAN(val) ? "true" : "false"); - } - fflush(stderr); -} - -void printId(JSContext *cx, jsid id) { - fprintf(stderr, "id %d (0x%p) is ", (int)id, (void *)id); - printVal(cx, ID_TO_VALUE(id)); -} - -void printAtom(JSAtom *atom) { - printString(ATOM_TO_STRING(atom)); -} - -#endif diff --git a/spidermonkey/src/jsobj.h b/spidermonkey/src/jsobj.h deleted file mode 100644 index eb3aedb..0000000 --- a/spidermonkey/src/jsobj.h +++ /dev/null @@ -1,596 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=80: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsobj_h___ -#define jsobj_h___ -/* - * JS object definitions. - * - * A JS object consists of a possibly-shared object descriptor containing - * ordered property names, called the map; and a dense vector of property - * values, called slots. The map/slot pointer pair is GC'ed, while the map - * is reference counted and the slot vector is malloc'ed. - */ -#include "jshash.h" /* Added by JSIFY */ -#include "jsprvtd.h" -#include "jspubtd.h" - -JS_BEGIN_EXTERN_C - -struct JSObjectMap { - jsrefcount nrefs; /* count of all referencing objects */ - JSObjectOps *ops; /* high level object operation vtable */ - uint32 nslots; /* length of obj->slots vector */ - uint32 freeslot; /* index of next free obj->slots element */ -}; - -/* Shorthand macros for frequently-made calls. */ -#define OBJ_LOOKUP_PROPERTY(cx,obj,id,objp,propp) \ - (obj)->map->ops->lookupProperty(cx,obj,id,objp,propp) -#define OBJ_DEFINE_PROPERTY(cx,obj,id,value,getter,setter,attrs,propp) \ - (obj)->map->ops->defineProperty(cx,obj,id,value,getter,setter,attrs,propp) -#define OBJ_GET_PROPERTY(cx,obj,id,vp) \ - (obj)->map->ops->getProperty(cx,obj,id,vp) -#define OBJ_SET_PROPERTY(cx,obj,id,vp) \ - (obj)->map->ops->setProperty(cx,obj,id,vp) -#define OBJ_GET_ATTRIBUTES(cx,obj,id,prop,attrsp) \ - (obj)->map->ops->getAttributes(cx,obj,id,prop,attrsp) -#define OBJ_SET_ATTRIBUTES(cx,obj,id,prop,attrsp) \ - (obj)->map->ops->setAttributes(cx,obj,id,prop,attrsp) -#define OBJ_DELETE_PROPERTY(cx,obj,id,rval) \ - (obj)->map->ops->deleteProperty(cx,obj,id,rval) -#define OBJ_DEFAULT_VALUE(cx,obj,hint,vp) \ - (obj)->map->ops->defaultValue(cx,obj,hint,vp) -#define OBJ_ENUMERATE(cx,obj,enum_op,statep,idp) \ - (obj)->map->ops->enumerate(cx,obj,enum_op,statep,idp) -#define OBJ_CHECK_ACCESS(cx,obj,id,mode,vp,attrsp) \ - (obj)->map->ops->checkAccess(cx,obj,id,mode,vp,attrsp) - -/* These four are time-optimized to avoid stub calls. */ -#define OBJ_THIS_OBJECT(cx,obj) \ - ((obj)->map->ops->thisObject \ - ? (obj)->map->ops->thisObject(cx,obj) \ - : (obj)) -#define OBJ_DROP_PROPERTY(cx,obj,prop) \ - ((obj)->map->ops->dropProperty \ - ? (obj)->map->ops->dropProperty(cx,obj,prop) \ - : (void)0) -#define OBJ_GET_REQUIRED_SLOT(cx,obj,slot) \ - ((obj)->map->ops->getRequiredSlot \ - ? (obj)->map->ops->getRequiredSlot(cx, obj, slot) \ - : JSVAL_VOID) -#define OBJ_SET_REQUIRED_SLOT(cx,obj,slot,v) \ - ((obj)->map->ops->setRequiredSlot \ - ? (obj)->map->ops->setRequiredSlot(cx, obj, slot, v) \ - : JS_TRUE) - -#define OBJ_TO_INNER_OBJECT(cx,obj) \ - JS_BEGIN_MACRO \ - JSClass *clasp_ = OBJ_GET_CLASS(cx, obj); \ - if (clasp_->flags & JSCLASS_IS_EXTENDED) { \ - JSExtendedClass *xclasp_ = (JSExtendedClass*)clasp_; \ - if (xclasp_->innerObject) \ - obj = xclasp_->innerObject(cx, obj); \ - } \ - JS_END_MACRO - -/* - * In the original JS engine design, obj->slots pointed to a vector of length - * JS_INITIAL_NSLOTS words if obj->map was shared with a prototype object, - * else of length obj->map->nslots. With the advent of JS_GetReservedSlot, - * JS_SetReservedSlot, and JSCLASS_HAS_RESERVED_SLOTS (see jsapi.h), the size - * of the minimum length slots vector in the case where map is shared cannot - * be constant. This length starts at JS_INITIAL_NSLOTS, but may advance to - * include all the reserved slots. - * - * Therefore slots must be self-describing. Rather than tag its low order bit - * (a bit is all we need) to distinguish initial length from reserved length, - * we do "the BSTR thing": over-allocate slots by one jsval, and store the - * *net* length (counting usable slots, which have non-negative obj->slots[] - * indices) in obj->slots[-1]. All code that sets obj->slots must be aware of - * this hack -- you have been warned, and jsobj.c has been updated! - */ -struct JSObject { - JSObjectMap *map; - jsval *slots; -}; - -#define JSSLOT_PROTO 0 -#define JSSLOT_PARENT 1 -#define JSSLOT_CLASS 2 -#define JSSLOT_PRIVATE 3 -#define JSSLOT_START(clasp) (((clasp)->flags & JSCLASS_HAS_PRIVATE) \ - ? JSSLOT_PRIVATE + 1 \ - : JSSLOT_CLASS + 1) - -#define JSSLOT_FREE(clasp) (JSSLOT_START(clasp) \ - + JSCLASS_RESERVED_SLOTS(clasp)) - -#define JS_INITIAL_NSLOTS 5 - -#ifdef DEBUG -#define MAP_CHECK_SLOT(map,slot) \ - JS_ASSERT((uint32)slot < JS_MIN((map)->freeslot, (map)->nslots)) -#define OBJ_CHECK_SLOT(obj,slot) \ - MAP_CHECK_SLOT((obj)->map, slot) -#else -#define OBJ_CHECK_SLOT(obj,slot) ((void)0) -#endif - -/* Fast macros for accessing obj->slots while obj is locked (if thread-safe). */ -#define LOCKED_OBJ_GET_SLOT(obj,slot) \ - (OBJ_CHECK_SLOT(obj, slot), (obj)->slots[slot]) -#define LOCKED_OBJ_SET_SLOT(obj,slot,value) \ - (OBJ_CHECK_SLOT(obj, slot), (obj)->slots[slot] = (value)) -#define LOCKED_OBJ_GET_PROTO(obj) \ - JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO)) -#define LOCKED_OBJ_GET_CLASS(obj) \ - ((JSClass *)JSVAL_TO_PRIVATE(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_CLASS))) - -#ifdef JS_THREADSAFE - -/* Thread-safe functions and wrapper macros for accessing obj->slots. */ -#define OBJ_GET_SLOT(cx,obj,slot) \ - (OBJ_CHECK_SLOT(obj, slot), \ - (OBJ_IS_NATIVE(obj) && OBJ_SCOPE(obj)->ownercx == cx) \ - ? LOCKED_OBJ_GET_SLOT(obj, slot) \ - : js_GetSlotThreadSafe(cx, obj, slot)) - -#define OBJ_SET_SLOT(cx,obj,slot,value) \ - (OBJ_CHECK_SLOT(obj, slot), \ - (OBJ_IS_NATIVE(obj) && OBJ_SCOPE(obj)->ownercx == cx) \ - ? (void) LOCKED_OBJ_SET_SLOT(obj, slot, value) \ - : js_SetSlotThreadSafe(cx, obj, slot, value)) - -/* - * If thread-safe, define an OBJ_GET_SLOT wrapper that bypasses, for a native - * object, the lock-free "fast path" test of (OBJ_SCOPE(obj)->ownercx == cx), - * to avoid needlessly switching from lock-free to lock-full scope when doing - * GC on a different context from the last one to own the scope. The caller - * in this case is probably a JSClass.mark function, e.g., fun_mark, or maybe - * a finalizer. - * - * The GC runs only when all threads except the one on which the GC is active - * are suspended at GC-safe points, so there is no hazard in directly accessing - * obj->slots[slot] from the GC's thread, once rt->gcRunning has been set. See - * jsgc.c for details. - */ -#define THREAD_IS_RUNNING_GC(rt, thread) \ - ((rt)->gcRunning && (rt)->gcThread == (thread)) - -#define CX_THREAD_IS_RUNNING_GC(cx) \ - THREAD_IS_RUNNING_GC((cx)->runtime, (cx)->thread) - -#define GC_AWARE_GET_SLOT(cx, obj, slot) \ - ((OBJ_IS_NATIVE(obj) && CX_THREAD_IS_RUNNING_GC(cx)) \ - ? (obj)->slots[slot] \ - : OBJ_GET_SLOT(cx, obj, slot)) - -#else /* !JS_THREADSAFE */ - -#define OBJ_GET_SLOT(cx,obj,slot) LOCKED_OBJ_GET_SLOT(obj,slot) -#define OBJ_SET_SLOT(cx,obj,slot,value) LOCKED_OBJ_SET_SLOT(obj,slot,value) -#define GC_AWARE_GET_SLOT(cx,obj,slot) LOCKED_OBJ_GET_SLOT(obj,slot) - -#endif /* !JS_THREADSAFE */ - -/* Thread-safe proto, parent, and class access macros. */ -#define OBJ_GET_PROTO(cx,obj) \ - JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj, JSSLOT_PROTO)) -#define OBJ_SET_PROTO(cx,obj,proto) \ - OBJ_SET_SLOT(cx, obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(proto)) - -#define OBJ_GET_PARENT(cx,obj) \ - JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj, JSSLOT_PARENT)) -#define OBJ_SET_PARENT(cx,obj,parent) \ - OBJ_SET_SLOT(cx, obj, JSSLOT_PARENT, OBJECT_TO_JSVAL(parent)) - -#define OBJ_GET_CLASS(cx,obj) \ - ((JSClass *)JSVAL_TO_PRIVATE(OBJ_GET_SLOT(cx, obj, JSSLOT_CLASS))) - -/* Test whether a map or object is native. */ -#define MAP_IS_NATIVE(map) \ - ((map)->ops == &js_ObjectOps || \ - ((map)->ops && (map)->ops->newObjectMap == js_ObjectOps.newObjectMap)) - -#define OBJ_IS_NATIVE(obj) MAP_IS_NATIVE((obj)->map) - -extern JS_FRIEND_DATA(JSObjectOps) js_ObjectOps; -extern JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps; -extern JSClass js_ObjectClass; -extern JSClass js_WithClass; -extern JSClass js_BlockClass; - -/* - * Block scope object macros. The slots reserved by js_BlockClass are: - * - * JSSLOT_PRIVATE JSStackFrame * active frame pointer or null - * JSSLOT_BLOCK_DEPTH int depth of block slots in frame - * - * After JSSLOT_BLOCK_DEPTH come one or more slots for the block locals. - * OBJ_BLOCK_COUNT depends on this arrangement. - * - * A With object is like a Block object, in that both have one reserved slot - * telling the stack depth of the relevant slots (the slot whose value is the - * object named in the with statement, the slots containing the block's local - * variables); and both have a private slot referring to the JSStackFrame in - * whose activation they were created (or null if the with or block object - * outlives the frame). - */ -#define JSSLOT_BLOCK_DEPTH (JSSLOT_PRIVATE + 1) - -#define OBJ_BLOCK_COUNT(cx,obj) \ - ((obj)->map->freeslot - (JSSLOT_BLOCK_DEPTH + 1)) -#define OBJ_BLOCK_DEPTH(cx,obj) \ - JSVAL_TO_INT(OBJ_GET_SLOT(cx, obj, JSSLOT_BLOCK_DEPTH)) -#define OBJ_SET_BLOCK_DEPTH(cx,obj,depth) \ - OBJ_SET_SLOT(cx, obj, JSSLOT_BLOCK_DEPTH, INT_TO_JSVAL(depth)) - -/* - * To make sure this slot is well-defined, always call js_NewWithObject to - * create a With object, don't call js_NewObject directly. When creating a - * With object that does not correspond to a stack slot, pass -1 for depth. - * - * When popping the stack across this object's "with" statement, client code - * must call JS_SetPrivate(cx, withobj, NULL). - */ -extern JSObject * -js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth); - -/* - * Create a new block scope object not linked to any proto or parent object. - * Blocks are created by the compiler to reify let blocks and comprehensions. - * Only when dynamic scope is captured do they need to be cloned and spliced - * into an active scope chain. - */ -extern JSObject * -js_NewBlockObject(JSContext *cx); - -extern JSObject * -js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent, - JSStackFrame *fp); - -extern JSBool -js_PutBlockObject(JSContext *cx, JSObject *obj); - -struct JSSharpObjectMap { - jsrefcount depth; - jsatomid sharpgen; - JSHashTable *table; -}; - -#define SHARP_BIT ((jsatomid) 1) -#define BUSY_BIT ((jsatomid) 2) -#define SHARP_ID_SHIFT 2 -#define IS_SHARP(he) (JS_PTR_TO_UINT32((he)->value) & SHARP_BIT) -#define MAKE_SHARP(he) ((he)->value = JS_UINT32_TO_PTR(JS_PTR_TO_UINT32((he)->value)|SHARP_BIT)) -#define IS_BUSY(he) (JS_PTR_TO_UINT32((he)->value) & BUSY_BIT) -#define MAKE_BUSY(he) ((he)->value = JS_UINT32_TO_PTR(JS_PTR_TO_UINT32((he)->value)|BUSY_BIT)) -#define CLEAR_BUSY(he) ((he)->value = JS_UINT32_TO_PTR(JS_PTR_TO_UINT32((he)->value)&~BUSY_BIT)) - -extern JSHashEntry * -js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, - jschar **sp); - -extern void -js_LeaveSharpObject(JSContext *cx, JSIdArray **idap); - -/* - * Mark objects stored in map if GC happens between js_EnterSharpObject - * and js_LeaveSharpObject. GC calls this when map->depth > 0. - */ -extern void -js_GCMarkSharpMap(JSContext *cx, JSSharpObjectMap *map); - -extern JSBool -js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); - -extern JSBool -js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); - -extern JSBool -js_HasOwnPropertyHelper(JSContext *cx, JSObject *obj, JSLookupPropOp lookup, - uintN argc, jsval *argv, jsval *rval); - -extern JSObject* -js_InitBlockClass(JSContext *cx, JSObject* obj); - -extern JSObject * -js_InitObjectClass(JSContext *cx, JSObject *obj); - -/* Select Object.prototype method names shared between jsapi.c and jsobj.c. */ -extern const char js_watch_str[]; -extern const char js_unwatch_str[]; -extern const char js_hasOwnProperty_str[]; -extern const char js_isPrototypeOf_str[]; -extern const char js_propertyIsEnumerable_str[]; -extern const char js_defineGetter_str[]; -extern const char js_defineSetter_str[]; -extern const char js_lookupGetter_str[]; -extern const char js_lookupSetter_str[]; - -extern void -js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops, - JSClass *clasp); - -extern JSObjectMap * -js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, - JSClass *clasp, JSObject *obj); - -extern void -js_DestroyObjectMap(JSContext *cx, JSObjectMap *map); - -extern JSObjectMap * -js_HoldObjectMap(JSContext *cx, JSObjectMap *map); - -extern JSObjectMap * -js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj); - -extern JSBool -js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp); - -extern JSObject * -js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent); - -/* - * Fast access to immutable standard objects (constructors and prototypes). - */ -extern JSBool -js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, - JSObject **objp); - -extern JSBool -js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj); - -extern JSBool -js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp); - -extern JSObject * -js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, - JSObject *parent, uintN argc, jsval *argv); - -extern void -js_FinalizeObject(JSContext *cx, JSObject *obj); - -extern JSBool -js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp); - -extern void -js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot); - -/* - * Native property add and lookup variants that hide id in the hidden atom - * subspace, so as to avoid collisions between internal properties such as - * formal arguments and local variables in function objects, and externally - * set properties with the same ids. - */ -extern JSScopeProperty * -js_AddHiddenProperty(JSContext *cx, JSObject *obj, jsid id, - JSPropertyOp getter, JSPropertyOp setter, uint32 slot, - uintN attrs, uintN flags, intN shortid); - -extern JSBool -js_LookupHiddenProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - JSProperty **propp); - -/* - * Find or create a property named by id in obj's scope, with the given getter - * and setter, slot, attributes, and other members. - */ -extern JSScopeProperty * -js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id, - JSPropertyOp getter, JSPropertyOp setter, uint32 slot, - uintN attrs, uintN flags, intN shortid); - -/* - * Change sprop to have the given attrs, getter, and setter in scope, morphing - * it into a potentially new JSScopeProperty. Return a pointer to the changed - * or identical property. - */ -extern JSScopeProperty * -js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj, - JSScopeProperty *sprop, uintN attrs, uintN mask, - JSPropertyOp getter, JSPropertyOp setter); - -/* - * On error, return false. On success, if propp is non-null, return true with - * obj locked and with a held property in *propp; if propp is null, return true - * but release obj's lock first. Therefore all callers who pass non-null propp - * result parameters must later call OBJ_DROP_PROPERTY(cx, obj, *propp) both to - * drop the held property, and to release the lock on obj. - */ -extern JSBool -js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs, - JSProperty **propp); - -extern JSBool -js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs, - uintN flags, intN shortid, JSProperty **propp); - -/* - * Unlike js_DefineProperty, propp must be non-null. On success, and if id was - * found, return true with *objp non-null and locked, and with a held property - * stored in *propp. If successful but id was not found, return true with both - * *objp and *propp null. Therefore all callers who receive a non-null *propp - * must later call OBJ_DROP_PROPERTY(cx, *objp, *propp). - */ -extern JS_FRIEND_API(JSBool) -js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - JSProperty **propp); - -/* - * Specialized subroutine that allows caller to preset JSRESOLVE_* flags. - * JSRESOLVE_HIDDEN flags hidden function param/local name lookups, just for - * internal use by fun_resolve and similar built-ins. - */ -extern JSBool -js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, - JSObject **objp, JSProperty **propp); - -#define JSRESOLVE_HIDDEN 0x8000 - -extern JS_FRIEND_API(JSBool) -js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp, - JSProperty **propp); - -extern JSObject * -js_FindIdentifierBase(JSContext *cx, jsid id); - -extern JSObject * -js_FindVariableScope(JSContext *cx, JSFunction **funp); - -/* - * NB: js_NativeGet and js_NativeSet are called with the scope containing sprop - * (pobj's scope for Get, obj's for Set) locked, and on successful return, that - * scope is again locked. But on failure, both functions return false with the - * scope containing sprop unlocked. - */ -extern JSBool -js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, - JSScopeProperty *sprop, jsval *vp); - -extern JSBool -js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp); - -extern JSBool -js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -extern JSBool -js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -extern JSBool -js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, - uintN *attrsp); - -extern JSBool -js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, - uintN *attrsp); - -extern JSBool -js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval); - -extern JSBool -js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp); - -extern JSIdArray * -js_NewIdArray(JSContext *cx, jsint length); - -/* - * Unlike realloc(3), this function frees ida on failure. - */ -extern JSIdArray * -js_SetIdArrayLength(JSContext *cx, JSIdArray *ida, jsint length); - -extern JSBool -js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, - jsval *statep, jsid *idp); - -extern void -js_MarkNativeIteratorStates(JSContext *cx); - -extern JSBool -js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, - jsval *vp, uintN *attrsp); - -extern JSBool -js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); - -extern JSBool -js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); - -extern JSBool -js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); - -extern JSBool -js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj); - -extern JSBool -js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); - -extern JSBool -js_GetClassPrototype(JSContext *cx, JSObject *scope, jsid id, - JSObject **protop); - -extern JSBool -js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, - uintN attrs); - -extern JSBool -js_ValueToObject(JSContext *cx, jsval v, JSObject **objp); - -extern JSObject * -js_ValueToNonNullObject(JSContext *cx, jsval v); - -extern JSBool -js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval); - -extern JSBool -js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom, - uintN argc, jsval *argv, jsval *rval); - -extern JSBool -js_XDRObject(JSXDRState *xdr, JSObject **objp); - -extern uint32 -js_Mark(JSContext *cx, JSObject *obj, void *arg); - -extern void -js_Clear(JSContext *cx, JSObject *obj); - -extern jsval -js_GetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot); - -extern JSBool -js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v); - -extern JSObject * -js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller); - -extern JSBool -js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj, - JSPrincipals *principals, JSAtom *caller); -JS_END_EXTERN_C - -#endif /* jsobj_h___ */ diff --git a/spidermonkey/src/jsopcode.c b/spidermonkey/src/jsopcode.c deleted file mode 100644 index 3dec776..0000000 --- a/spidermonkey/src/jsopcode.c +++ /dev/null @@ -1,4794 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set sw=4 ts=8 et tw=78: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS bytecode descriptors, disassemblers, and decompilers. - */ -#include "jsstddef.h" -#ifdef HAVE_MEMORY_H -#include -#endif -#include -#include -#include -#include -#include "jstypes.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ -#include "jsdtoa.h" -#include "jsprf.h" -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsdbgapi.h" -#include "jsemit.h" -#include "jsfun.h" -#include "jslock.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsregexp.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" - -#if JS_HAS_DESTRUCTURING -# include "jsnum.h" -#endif - -static const char js_incop_strs[][3] = {"++", "--"}; - -/* Pollute the namespace locally for MSVC Win16, but not for WatCom. */ -#ifdef __WINDOWS_386__ - #ifdef FAR - #undef FAR - #endif -#else /* !__WINDOWS_386__ */ -#ifndef FAR -#define FAR -#endif -#endif /* !__WINDOWS_386__ */ - -const JSCodeSpec FAR js_CodeSpec[] = { -#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ - {name,token,length,nuses,ndefs,prec,format}, -#include "jsopcode.tbl" -#undef OPDEF -}; - -uintN js_NumCodeSpecs = sizeof (js_CodeSpec) / sizeof js_CodeSpec[0]; - -/************************************************************************/ - -static ptrdiff_t -GetJumpOffset(jsbytecode *pc, jsbytecode *pc2) -{ - uint32 type; - - type = (js_CodeSpec[*pc].format & JOF_TYPEMASK); - if (JOF_TYPE_IS_EXTENDED_JUMP(type)) - return GET_JUMPX_OFFSET(pc2); - return GET_JUMP_OFFSET(pc2); -} - -#ifdef DEBUG - -JS_FRIEND_API(JSBool) -js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp) -{ - jsbytecode *pc, *end; - uintN len; - - pc = script->code; - end = pc + script->length; - while (pc < end) { - if (pc == script->main) - fputs("main:\n", fp); - len = js_Disassemble1(cx, script, pc, - PTRDIFF(pc, script->code, jsbytecode), - lines, fp); - if (!len) - return JS_FALSE; - pc += len; - } - return JS_TRUE; -} - -const char * -ToDisassemblySource(JSContext *cx, jsval v) -{ - JSObject *obj; - JSScopeProperty *sprop; - char *source; - const char *bytes; - JSString *str; - - if (!JSVAL_IS_PRIMITIVE(v)) { - obj = JSVAL_TO_OBJECT(v); - if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) { - source = JS_sprintf_append(NULL, "depth %d {", - OBJ_BLOCK_DEPTH(cx, obj)); - for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; - sprop = sprop->parent) { - bytes = js_AtomToPrintableString(cx, JSID_TO_ATOM(sprop->id)); - if (!bytes) - return NULL; - source = JS_sprintf_append(source, "%s: %d%s", - bytes, sprop->shortid, - sprop->parent ? ", " : ""); - } - source = JS_sprintf_append(source, "}"); - if (!source) - return NULL; - str = JS_NewString(cx, source, strlen(source)); - if (!str) - return NULL; - return JS_GetStringBytes(str); - } - } - return js_ValueToPrintableSource(cx, v); -} - -JS_FRIEND_API(uintN) -js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, - JSBool lines, FILE *fp) -{ - JSOp op; - const JSCodeSpec *cs; - ptrdiff_t len, off, jmplen; - uint32 type; - JSAtom *atom; - const char *bytes; - - op = (JSOp)*pc; - if (op >= JSOP_LIMIT) { - char numBuf1[12], numBuf2[12]; - JS_snprintf(numBuf1, sizeof numBuf1, "%d", op); - JS_snprintf(numBuf2, sizeof numBuf2, "%d", JSOP_LIMIT); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BYTECODE_TOO_BIG, numBuf1, numBuf2); - return 0; - } - cs = &js_CodeSpec[op]; - len = (ptrdiff_t) cs->length; - fprintf(fp, "%05u:", loc); - if (lines) - fprintf(fp, "%4u", JS_PCToLineNumber(cx, script, pc)); - fprintf(fp, " %s", cs->name); - type = cs->format & JOF_TYPEMASK; - switch (type) { - case JOF_BYTE: - if (op == JSOP_TRAP) { - op = JS_GetTrapOpcode(cx, script, pc); - if (op == JSOP_LIMIT) - return 0; - len = (ptrdiff_t) js_CodeSpec[op].length; - } - break; - - case JOF_JUMP: - case JOF_JUMPX: - off = GetJumpOffset(pc, pc); - fprintf(fp, " %u (%d)", loc + off, off); - break; - - case JOF_CONST: - atom = GET_ATOM(cx, script, pc); - bytes = ToDisassemblySource(cx, ATOM_KEY(atom)); - if (!bytes) - return 0; - fprintf(fp, " %s", bytes); - break; - - case JOF_UINT16: - case JOF_LOCAL: - fprintf(fp, " %u", GET_UINT16(pc)); - break; - - case JOF_TABLESWITCH: - case JOF_TABLESWITCHX: - { - jsbytecode *pc2; - jsint i, low, high; - - jmplen = (type == JOF_TABLESWITCH) ? JUMP_OFFSET_LEN - : JUMPX_OFFSET_LEN; - pc2 = pc; - off = GetJumpOffset(pc, pc2); - pc2 += jmplen; - low = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - high = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - fprintf(fp, " defaultOffset %d low %d high %d", off, low, high); - for (i = low; i <= high; i++) { - off = GetJumpOffset(pc, pc2); - fprintf(fp, "\n\t%d: %d", i, off); - pc2 += jmplen; - } - len = 1 + pc2 - pc; - break; - } - - case JOF_LOOKUPSWITCH: - case JOF_LOOKUPSWITCHX: - { - jsbytecode *pc2; - jsatomid npairs; - - jmplen = (type == JOF_LOOKUPSWITCH) ? JUMP_OFFSET_LEN - : JUMPX_OFFSET_LEN; - pc2 = pc; - off = GetJumpOffset(pc, pc2); - pc2 += jmplen; - npairs = GET_ATOM_INDEX(pc2); - pc2 += ATOM_INDEX_LEN; - fprintf(fp, " offset %d npairs %u", off, (uintN) npairs); - while (npairs) { - atom = GET_ATOM(cx, script, pc2); - pc2 += ATOM_INDEX_LEN; - off = GetJumpOffset(pc, pc2); - pc2 += jmplen; - - bytes = ToDisassemblySource(cx, ATOM_KEY(atom)); - if (!bytes) - return 0; - fprintf(fp, "\n\t%s: %d", bytes, off); - npairs--; - } - len = 1 + pc2 - pc; - break; - } - - case JOF_QARG: - fprintf(fp, " %u", GET_ARGNO(pc)); - break; - - case JOF_QVAR: - fprintf(fp, " %u", GET_VARNO(pc)); - break; - - case JOF_INDEXCONST: - fprintf(fp, " %u", GET_VARNO(pc)); - pc += VARNO_LEN; - atom = GET_ATOM(cx, script, pc); - bytes = ToDisassemblySource(cx, ATOM_KEY(atom)); - if (!bytes) - return 0; - fprintf(fp, " %s", bytes); - break; - - case JOF_UINT24: - if (op == JSOP_FINDNAME) { - /* Special case to avoid a JOF_FINDNAME just for this op. */ - atom = js_GetAtom(cx, &script->atomMap, GET_UINT24(pc)); - bytes = ToDisassemblySource(cx, ATOM_KEY(atom)); - if (!bytes) - return 0; - fprintf(fp, " %s", bytes); - break; - } - - JS_ASSERT(op == JSOP_UINT24 || op == JSOP_LITERAL); - fprintf(fp, " %u", GET_UINT24(pc)); - break; - - case JOF_LITOPX: - atom = js_GetAtom(cx, &script->atomMap, GET_LITERAL_INDEX(pc)); - bytes = ToDisassemblySource(cx, ATOM_KEY(atom)); - if (!bytes) - return 0; - - /* - * Bytecode: JSOP_LITOPX op [ if JSOP_DEFLOCALFUN]. - * Advance pc to point at op. - */ - pc += 1 + LITERAL_INDEX_LEN; - op = *pc; - cs = &js_CodeSpec[op]; - fprintf(fp, " %s op %s", bytes, cs->name); - if ((cs->format & JOF_TYPEMASK) == JOF_INDEXCONST) - fprintf(fp, " %u", GET_VARNO(pc)); - - /* - * Set len to advance pc to skip op and any other immediates (namely, - * if JSOP_DEFLOCALFUN). - */ - JS_ASSERT(cs->length > ATOM_INDEX_LEN); - len = cs->length - ATOM_INDEX_LEN; - break; - - default: { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) cs->format); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_UNKNOWN_FORMAT, numBuf); - return 0; - } - } - fputs("\n", fp); - return len; -} - -#endif /* DEBUG */ - -/************************************************************************/ - -/* - * Sprintf, but with unlimited and automatically allocated buffering. - */ -typedef struct Sprinter { - JSContext *context; /* context executing the decompiler */ - JSArenaPool *pool; /* string allocation pool */ - char *base; /* base address of buffer in pool */ - size_t size; /* size of buffer allocated at base */ - ptrdiff_t offset; /* offset of next free char in buffer */ -} Sprinter; - -#define INIT_SPRINTER(cx, sp, ap, off) \ - ((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0, \ - (sp)->offset = off) - -#define OFF2STR(sp,off) ((sp)->base + (off)) -#define STR2OFF(sp,str) ((str) - (sp)->base) -#define RETRACT(sp,str) ((sp)->offset = STR2OFF(sp, str)) - -static JSBool -SprintAlloc(Sprinter *sp, size_t nb) -{ - char *base; - - base = sp->base; - if (!base) { - JS_ARENA_ALLOCATE_CAST(base, char *, sp->pool, nb); - } else { - JS_ARENA_GROW_CAST(base, char *, sp->pool, sp->size, nb); - } - if (!base) { - JS_ReportOutOfMemory(sp->context); - return JS_FALSE; - } - sp->base = base; - sp->size += nb; - return JS_TRUE; -} - -static ptrdiff_t -SprintPut(Sprinter *sp, const char *s, size_t len) -{ - ptrdiff_t nb, offset; - char *bp; - - /* Allocate space for s, including the '\0' at the end. */ - nb = (sp->offset + len + 1) - sp->size; - if (nb > 0 && !SprintAlloc(sp, nb)) - return -1; - - /* Advance offset and copy s into sp's buffer. */ - offset = sp->offset; - sp->offset += len; - bp = sp->base + offset; - memmove(bp, s, len); - bp[len] = 0; - return offset; -} - -static ptrdiff_t -SprintCString(Sprinter *sp, const char *s) -{ - return SprintPut(sp, s, strlen(s)); -} - -static ptrdiff_t -Sprint(Sprinter *sp, const char *format, ...) -{ - va_list ap; - char *bp; - ptrdiff_t offset; - - va_start(ap, format); - bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */ - va_end(ap); - if (!bp) { - JS_ReportOutOfMemory(sp->context); - return -1; - } - offset = SprintCString(sp, bp); - free(bp); - return offset; -} - -const jschar js_EscapeMap[] = { - '\b', 'b', - '\f', 'f', - '\n', 'n', - '\r', 'r', - '\t', 't', - '\v', 'v', - '"', '"', - '\'', '\'', - '\\', '\\', - 0 -}; - -#define DONT_ESCAPE 0x10000 - -static char * -QuoteString(Sprinter *sp, JSString *str, uint32 quote) -{ - JSBool dontEscape, ok; - jschar qc, c; - ptrdiff_t off, len, nb; - const jschar *s, *t, *u, *z; - char *bp; - - /* Sample off first for later return value pointer computation. */ - dontEscape = (quote & DONT_ESCAPE) != 0; - qc = (jschar) quote; - off = sp->offset; - if (qc && Sprint(sp, "%c", (char)qc) < 0) - return NULL; - - /* Loop control variables: z points at end of string sentinel. */ - s = JSSTRING_CHARS(str); - z = s + JSSTRING_LENGTH(str); - for (t = s; t < z; s = ++t) { - /* Move t forward from s past un-quote-worthy characters. */ - c = *t; - while (JS_ISPRINT(c) && c != qc && c != '\\' && !(c >> 8)) { - c = *++t; - if (t == z) - break; - } - len = PTRDIFF(t, s, jschar); - - /* Allocate space for s, including the '\0' at the end. */ - nb = (sp->offset + len + 1) - sp->size; - if (nb > 0 && !SprintAlloc(sp, nb)) - return NULL; - - /* Advance sp->offset and copy s into sp's buffer. */ - bp = sp->base + sp->offset; - sp->offset += len; - while (--len >= 0) - *bp++ = (char) *s++; - *bp = '\0'; - - if (t == z) - break; - - /* Use js_EscapeMap, \u, or \x only if necessary. */ - if ((u = js_strchr(js_EscapeMap, c)) != NULL) { - ok = dontEscape - ? Sprint(sp, "%c", (char)c) >= 0 - : Sprint(sp, "\\%c", (char)u[1]) >= 0; - } else { -#ifdef JS_C_STRINGS_ARE_UTF8 - /* If this is a surrogate pair, make sure to print the pair. */ - if (c >= 0xD800 && c <= 0xDBFF) { - jschar buffer[3]; - buffer[0] = c; - buffer[1] = *++t; - buffer[2] = 0; - if (t == z) { - char numbuf[10]; - JS_snprintf(numbuf, sizeof numbuf, "0x%x", c); - JS_ReportErrorFlagsAndNumber(sp->context, JSREPORT_ERROR, - js_GetErrorMessage, NULL, - JSMSG_BAD_SURROGATE_CHAR, - numbuf); - ok = JS_FALSE; - break; - } - ok = Sprint(sp, "%hs", buffer) >= 0; - } else { - /* Print as UTF-8 string. */ - ok = Sprint(sp, "%hc", c) >= 0; - } -#else - /* Use \uXXXX or \xXX if the string can't be displayed as UTF-8. */ - ok = Sprint(sp, (c >> 8) ? "\\u%04X" : "\\x%02X", c) >= 0; -#endif - } - if (!ok) - return NULL; - } - - /* Sprint the closing quote and return the quoted string. */ - if (qc && Sprint(sp, "%c", (char)qc) < 0) - return NULL; - - /* - * If we haven't Sprint'd anything yet, Sprint an empty string so that - * the OFF2STR below gives a valid result. - */ - if (off == sp->offset && Sprint(sp, "") < 0) - return NULL; - return OFF2STR(sp, off); -} - -JSString * -js_QuoteString(JSContext *cx, JSString *str, jschar quote) -{ - void *mark; - Sprinter sprinter; - char *bytes; - JSString *escstr; - - mark = JS_ARENA_MARK(&cx->tempPool); - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); - bytes = QuoteString(&sprinter, str, quote); - escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL; - JS_ARENA_RELEASE(&cx->tempPool, mark); - return escstr; -} - -/************************************************************************/ - -#if JS_HAS_BLOCK_SCOPE -typedef enum JSBraceState { - ALWAYS_BRACE, - MAYBE_BRACE, - DONT_BRACE -} JSBraceState; -#endif - -struct JSPrinter { - Sprinter sprinter; /* base class state */ - JSArenaPool pool; /* string allocation pool */ - uintN indent; /* indentation in spaces */ - JSPackedBool pretty; /* pretty-print: indent, use newlines */ - JSPackedBool grouped; /* in parenthesized expression context */ - JSScript *script; /* script being printed */ - jsbytecode *dvgfence; /* js_DecompileValueGenerator fencepost */ - JSScope *scope; /* script function scope */ -#if JS_HAS_BLOCK_SCOPE - JSBraceState braceState; /* remove braces around let declaration */ - ptrdiff_t spaceOffset; /* -1 or offset of space before maybe-{ */ -#endif -}; - -/* - * Hack another flag, a la JS_DONT_PRETTY_PRINT, into uintN indent parameters - * to functions such as js_DecompileFunction and js_NewPrinter. This time, as - * opposed to JS_DONT_PRETTY_PRINT back in the dark ages, we can assume that a - * uintN is at least 32 bits. - */ -#define JS_IN_GROUP_CONTEXT 0x10000 - -JSPrinter * -js_NewPrinter(JSContext *cx, const char *name, uintN indent, JSBool pretty) -{ - JSPrinter *jp; - - jp = (JSPrinter *) JS_malloc(cx, sizeof(JSPrinter)); - if (!jp) - return NULL; - INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0); - JS_InitArenaPool(&jp->pool, name, 256, 1); - jp->indent = indent & ~JS_IN_GROUP_CONTEXT; - jp->pretty = pretty; - jp->grouped = (indent & JS_IN_GROUP_CONTEXT) != 0; - jp->script = NULL; - jp->dvgfence = NULL; - jp->scope = NULL; -#if JS_HAS_BLOCK_SCOPE - jp->braceState = ALWAYS_BRACE; - jp->spaceOffset = -1; -#endif - return jp; -} - -void -js_DestroyPrinter(JSPrinter *jp) -{ - JS_FinishArenaPool(&jp->pool); - JS_free(jp->sprinter.context, jp); -} - -JSString * -js_GetPrinterOutput(JSPrinter *jp) -{ - JSContext *cx; - JSString *str; - - cx = jp->sprinter.context; - if (!jp->sprinter.base) - return cx->runtime->emptyString; - str = JS_NewStringCopyZ(cx, jp->sprinter.base); - if (!str) - return NULL; - JS_FreeArenaPool(&jp->pool); - INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0); - return str; -} - -#if !JS_HAS_BLOCK_SCOPE -# define SET_MAYBE_BRACE(jp) jp -# define CLEAR_MAYBE_BRACE(jp) jp -#else -# define SET_MAYBE_BRACE(jp) ((jp)->braceState = MAYBE_BRACE, (jp)) -# define CLEAR_MAYBE_BRACE(jp) ((jp)->braceState = ALWAYS_BRACE, (jp)) - -static void -SetDontBrace(JSPrinter *jp) -{ - ptrdiff_t offset; - const char *bp; - - /* When not pretty-printing, newline after brace is chopped. */ - JS_ASSERT(jp->spaceOffset < 0); - offset = jp->sprinter.offset - (jp->pretty ? 3 : 2); - - /* The shortest case is "if (x) {". */ - JS_ASSERT(offset >= 6); - bp = jp->sprinter.base; - if (bp[offset+0] == ' ' && bp[offset+1] == '{') { - JS_ASSERT(!jp->pretty || bp[offset+2] == '\n'); - jp->spaceOffset = offset; - jp->braceState = DONT_BRACE; - } -} -#endif - -int -js_printf(JSPrinter *jp, const char *format, ...) -{ - va_list ap; - char *bp, *fp; - int cc; - - if (*format == '\0') - return 0; - - va_start(ap, format); - - /* If pretty-printing, expand magic tab into a run of jp->indent spaces. */ - if (*format == '\t') { - format++; - -#if JS_HAS_BLOCK_SCOPE - if (*format == '}' && jp->braceState != ALWAYS_BRACE) { - JSBraceState braceState; - - braceState = jp->braceState; - jp->braceState = ALWAYS_BRACE; - if (braceState == DONT_BRACE) { - ptrdiff_t offset, delta, from; - - JS_ASSERT(format[1] == '\n' || format[1] == ' '); - offset = jp->spaceOffset; - JS_ASSERT(offset >= 6); - - /* Replace " {\n" at the end of jp->sprinter with "\n". */ - bp = jp->sprinter.base; - if (bp[offset+0] == ' ' && bp[offset+1] == '{') { - delta = 2; - if (jp->pretty) { - /* If pretty, we don't have to worry about 'else'. */ - JS_ASSERT(bp[offset+2] == '\n'); - } else if (bp[offset-1] != ')') { - /* Must keep ' ' to avoid 'dolet' or 'elselet'. */ - ++offset; - delta = 1; - } - - from = offset + delta; - memmove(bp + offset, bp + from, jp->sprinter.offset - from); - jp->sprinter.offset -= delta; - jp->spaceOffset = -1; - - format += 2; - if (*format == '\0') - return 0; - } - } - } -#endif - - if (jp->pretty && Sprint(&jp->sprinter, "%*s", jp->indent, "") < 0) - return -1; - } - - /* Suppress newlines (must be once per format, at the end) if not pretty. */ - fp = NULL; - if (!jp->pretty && format[cc = strlen(format) - 1] == '\n') { - fp = JS_strdup(jp->sprinter.context, format); - if (!fp) - return -1; - fp[cc] = '\0'; - format = fp; - } - - /* Allocate temp space, convert format, and put. */ - bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */ - if (fp) { - JS_free(jp->sprinter.context, fp); - format = NULL; - } - if (!bp) { - JS_ReportOutOfMemory(jp->sprinter.context); - return -1; - } - - cc = strlen(bp); - if (SprintPut(&jp->sprinter, bp, (size_t)cc) < 0) - cc = -1; - free(bp); - - va_end(ap); - return cc; -} - -JSBool -js_puts(JSPrinter *jp, const char *s) -{ - return SprintCString(&jp->sprinter, s) >= 0; -} - -/************************************************************************/ - -typedef struct SprintStack { - Sprinter sprinter; /* sprinter for postfix to infix buffering */ - ptrdiff_t *offsets; /* stack of postfix string offsets */ - jsbytecode *opcodes; /* parallel stack of JS opcodes */ - uintN top; /* top of stack index */ - uintN inArrayInit; /* array initialiser/comprehension level */ - JSPrinter *printer; /* permanent output goes here */ -} SprintStack; - -/* - * Get a stacked offset from ss->sprinter.base, or if the stacked value |off| - * is negative, lazily fetch the generating pc at |spindex = 1 + off| and try - * to decompile the code that generated the missing value. This is used when - * reporting errors, where the model stack will lack |pcdepth| non-negative - * offsets (see js_DecompileValueGenerator and js_DecompileCode). - * - * If the stacked offset is -1, return 0 to index the NUL padding at the start - * of ss->sprinter.base. If this happens, it means there is a decompiler bug - * to fix, but it won't violate memory safety. - */ -static ptrdiff_t -GetOff(SprintStack *ss, uintN i) -{ - ptrdiff_t off; - JSString *str; - - off = ss->offsets[i]; - if (off < 0) { -#if defined DEBUG_brendan || defined DEBUG_mrbkap || defined DEBUG_crowder - JS_ASSERT(off < -1); -#endif - if (++off == 0) { - if (!ss->sprinter.base && SprintPut(&ss->sprinter, "", 0) >= 0) - memset(ss->sprinter.base, 0, ss->sprinter.offset); - return 0; - } - - str = js_DecompileValueGenerator(ss->sprinter.context, off, - JSVAL_NULL, NULL); - if (!str) - return 0; - off = SprintCString(&ss->sprinter, JS_GetStringBytes(str)); - if (off < 0) - off = 0; - ss->offsets[i] = off; - } - return off; -} - -static const char * -GetStr(SprintStack *ss, uintN i) -{ - ptrdiff_t off; - - /* - * Must call GetOff before using ss->sprinter.base, since it may be null - * until bootstrapped by GetOff. - */ - off = GetOff(ss, i); - return OFF2STR(&ss->sprinter, off); -} - -/* Gap between stacked strings to allow for insertion of parens and commas. */ -#define PAREN_SLOP (2 + 1) - -/* - * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME, - * JSOP_SETPROP, and JSOP_SETELEM, respectively. They are never stored in - * bytecode, so they don't preempt valid opcodes. - */ -#define JSOP_GETPROP2 256 -#define JSOP_GETELEM2 257 - -static JSBool -PushOff(SprintStack *ss, ptrdiff_t off, JSOp op) -{ - uintN top; - - if (!SprintAlloc(&ss->sprinter, PAREN_SLOP)) - return JS_FALSE; - - /* ss->top points to the next free slot; be paranoid about overflow. */ - top = ss->top; - JS_ASSERT(top < ss->printer->script->depth); - if (top >= ss->printer->script->depth) { - JS_ReportOutOfMemory(ss->sprinter.context); - return JS_FALSE; - } - - /* The opcodes stack must contain real bytecodes that index js_CodeSpec. */ - ss->offsets[top] = off; - ss->opcodes[top] = (op == JSOP_GETPROP2) ? JSOP_GETPROP - : (op == JSOP_GETELEM2) ? JSOP_GETELEM - : (jsbytecode) op; - ss->top = ++top; - memset(OFF2STR(&ss->sprinter, ss->sprinter.offset), 0, PAREN_SLOP); - ss->sprinter.offset += PAREN_SLOP; - return JS_TRUE; -} - -static ptrdiff_t -PopOff(SprintStack *ss, JSOp op) -{ - uintN top; - const JSCodeSpec *cs, *topcs; - ptrdiff_t off; - - /* ss->top points to the next free slot; be paranoid about underflow. */ - top = ss->top; - JS_ASSERT(top != 0); - if (top == 0) - return 0; - - ss->top = --top; - off = GetOff(ss, top); - topcs = &js_CodeSpec[ss->opcodes[top]]; - cs = &js_CodeSpec[op]; - if (topcs->prec != 0 && topcs->prec < cs->prec) { - ss->sprinter.offset = ss->offsets[top] = off - 2; - off = Sprint(&ss->sprinter, "(%s)", OFF2STR(&ss->sprinter, off)); - } else { - ss->sprinter.offset = off; - } - return off; -} - -static const char * -PopStr(SprintStack *ss, JSOp op) -{ - ptrdiff_t off; - - off = PopOff(ss, op); - return OFF2STR(&ss->sprinter, off); -} - -typedef struct TableEntry { - jsval key; - ptrdiff_t offset; - JSAtom *label; - jsint order; /* source order for stable tableswitch sort */ -} TableEntry; - -static JSBool -CompareOffsets(void *arg, const void *v1, const void *v2, int *result) -{ - ptrdiff_t offset_diff; - const TableEntry *te1 = (const TableEntry *) v1, - *te2 = (const TableEntry *) v2; - - offset_diff = te1->offset - te2->offset; - *result = (offset_diff == 0 ? te1->order - te2->order - : offset_diff < 0 ? -1 - : 1); - return JS_TRUE; -} - -static jsbytecode * -Decompile(SprintStack *ss, jsbytecode *pc, intN nb); - -static JSBool -DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, - jsbytecode *pc, ptrdiff_t switchLength, - ptrdiff_t defaultOffset, JSBool isCondSwitch) -{ - JSContext *cx; - JSPrinter *jp; - ptrdiff_t off, off2, diff, caseExprOff; - char *lval, *rval; - uintN i; - jsval key; - JSString *str; - - cx = ss->sprinter.context; - jp = ss->printer; - - /* JSOP_CONDSWITCH doesn't pop, unlike JSOP_{LOOKUP,TABLE}SWITCH. */ - off = isCondSwitch ? GetOff(ss, ss->top-1) : PopOff(ss, JSOP_NOP); - lval = OFF2STR(&ss->sprinter, off); - - js_printf(CLEAR_MAYBE_BRACE(jp), "\tswitch (%s) {\n", lval); - - if (tableLength) { - diff = table[0].offset - defaultOffset; - if (diff > 0) { - jp->indent += 2; - js_printf(jp, "\t%s:\n", js_default_str); - jp->indent += 2; - if (!Decompile(ss, pc + defaultOffset, diff)) - return JS_FALSE; - jp->indent -= 4; - } - - caseExprOff = isCondSwitch ? JSOP_CONDSWITCH_LENGTH : 0; - - for (i = 0; i < tableLength; i++) { - off = table[i].offset; - off2 = (i + 1 < tableLength) ? table[i + 1].offset : switchLength; - - key = table[i].key; - if (isCondSwitch) { - ptrdiff_t nextCaseExprOff; - - /* - * key encodes the JSOP_CASE bytecode's offset from switchtop. - * The next case expression follows immediately, unless we are - * at the last case. - */ - nextCaseExprOff = (ptrdiff_t)JSVAL_TO_INT(key); - nextCaseExprOff += js_CodeSpec[pc[nextCaseExprOff]].length; - jp->indent += 2; - if (!Decompile(ss, pc + caseExprOff, - nextCaseExprOff - caseExprOff)) { - return JS_FALSE; - } - caseExprOff = nextCaseExprOff; - - /* Balance the stack as if this JSOP_CASE matched. */ - --ss->top; - } else { - /* - * key comes from an atom, not the decompiler, so we need to - * quote it if it's a string literal. But if table[i].label - * is non-null, key was constant-propagated and label is the - * name of the const we should show as the case label. We set - * key to undefined so this identifier is escaped, if required - * by non-ASCII characters, but not quoted, by QuoteString. - */ - if (table[i].label) { - str = ATOM_TO_STRING(table[i].label); - key = JSVAL_VOID; - } else { - str = js_ValueToString(cx, key); - if (!str) - return JS_FALSE; - } - rval = QuoteString(&ss->sprinter, str, - (jschar)(JSVAL_IS_STRING(key) ? '"' : 0)); - if (!rval) - return JS_FALSE; - RETRACT(&ss->sprinter, rval); - jp->indent += 2; - js_printf(jp, "\tcase %s:\n", rval); - } - - jp->indent += 2; - if (off <= defaultOffset && defaultOffset < off2) { - diff = defaultOffset - off; - if (diff != 0) { - if (!Decompile(ss, pc + off, diff)) - return JS_FALSE; - off = defaultOffset; - } - jp->indent -= 2; - js_printf(jp, "\t%s:\n", js_default_str); - jp->indent += 2; - } - if (!Decompile(ss, pc + off, off2 - off)) - return JS_FALSE; - jp->indent -= 4; - - /* Re-balance as if last JSOP_CASE or JSOP_DEFAULT mismatched. */ - if (isCondSwitch) - ++ss->top; - } - } - - if (defaultOffset == switchLength) { - jp->indent += 2; - js_printf(jp, "\t%s:;\n", js_default_str); - jp->indent -= 2; - } - js_printf(jp, "\t}\n"); - - /* By the end of a JSOP_CONDSWITCH, the discriminant has been popped. */ - if (isCondSwitch) - --ss->top; - return JS_TRUE; -} - -static JSAtom * -GetSlotAtom(JSPrinter *jp, JSPropertyOp getter, uintN slot) -{ - JSScope *scope; - JSScopeProperty *sprop; - JSObject *obj, *proto; - - scope = jp->scope; - while (scope) { - for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { - if (sprop->getter != getter) - continue; - JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); - JS_ASSERT(JSID_IS_ATOM(sprop->id)); - if ((uintN) sprop->shortid == slot) - return JSID_TO_ATOM(sprop->id); - } - obj = scope->object; - if (!obj) - break; - proto = OBJ_GET_PROTO(jp->sprinter.context, obj); - if (!proto) - break; - scope = OBJ_SCOPE(proto); - } - return NULL; -} - -/* - * NB: Indexed by SRC_DECL_* defines from jsemit.h. - */ -static const char * const var_prefix[] = {"var ", "const ", "let "}; - -static const char * -VarPrefix(jssrcnote *sn) -{ - if (sn && (SN_TYPE(sn) == SRC_DECL || SN_TYPE(sn) == SRC_GROUPASSIGN)) { - ptrdiff_t type = js_GetSrcNoteOffset(sn, 0); - if ((uintN)type <= SRC_DECL_LET) - return var_prefix[type]; - } - return ""; -} -#define LOCAL_ASSERT_RV(expr, rv) \ - JS_BEGIN_MACRO \ - JS_ASSERT(expr); \ - if (!(expr)) return (rv); \ - JS_END_MACRO - -const char * -GetLocal(SprintStack *ss, jsint i) -{ - ptrdiff_t off; - JSContext *cx; - JSScript *script; - jsatomid j, n; - JSAtom *atom; - JSObject *obj; - jsint depth, count; - JSScopeProperty *sprop; - const char *rval; - -#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, "") - - off = ss->offsets[i]; - if (off >= 0) - return OFF2STR(&ss->sprinter, off); - - /* - * We must be called from js_DecompileValueGenerator (via Decompile) when - * dereferencing a local that's undefined or null. Search script->atomMap - * for the block containing this local by its stack index, i. - */ - cx = ss->sprinter.context; - script = ss->printer->script; - for (j = 0, n = script->atomMap.length; j < n; j++) { - atom = script->atomMap.vector[j]; - if (ATOM_IS_OBJECT(atom)) { - obj = ATOM_TO_OBJECT(atom); - if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) { - depth = OBJ_BLOCK_DEPTH(cx, obj); - count = OBJ_BLOCK_COUNT(cx, obj); - if ((jsuint)(i - depth) < (jsuint)count) - break; - } - } - } - - LOCAL_ASSERT(j < n); - i -= depth; - for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; sprop = sprop->parent) { - if (sprop->shortid == i) - break; - } - - LOCAL_ASSERT(sprop && JSID_IS_ATOM(sprop->id)); - atom = JSID_TO_ATOM(sprop->id); - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!rval) - return NULL; - RETRACT(&ss->sprinter, rval); - return rval; - -#undef LOCAL_ASSERT -} - -#if JS_HAS_DESTRUCTURING - -#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL) -#define LOAD_OP_DATA(pc) (oplen = (cs = &js_CodeSpec[op = *pc])->length) - -static jsbytecode * -DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc); - -static jsbytecode * -DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, - JSBool *hole) -{ - JSContext *cx; - JSPrinter *jp; - JSOp op; - const JSCodeSpec *cs; - uintN oplen, i; - const char *lval, *xval; - ptrdiff_t todo; - JSAtom *atom; - - *hole = JS_FALSE; - cx = ss->sprinter.context; - jp = ss->printer; - LOAD_OP_DATA(pc); - - switch (op) { - case JSOP_POP: - *hole = JS_TRUE; - todo = SprintPut(&ss->sprinter, ", ", 2); - break; - - case JSOP_DUP: - pc = DecompileDestructuring(ss, pc, endpc); - if (!pc) - return NULL; - if (pc == endpc) - return pc; - LOAD_OP_DATA(pc); - lval = PopStr(ss, JSOP_NOP); - todo = SprintCString(&ss->sprinter, lval); - if (op == JSOP_SETSP) - return pc; - LOCAL_ASSERT(*pc == JSOP_POP); - break; - - case JSOP_SETARG: - case JSOP_SETVAR: - case JSOP_SETGVAR: - case JSOP_SETLOCAL: - LOCAL_ASSERT(pc[oplen] == JSOP_POP || pc[oplen] == JSOP_SETSP); - /* FALL THROUGH */ - - case JSOP_SETLOCALPOP: - i = GET_UINT16(pc); - atom = NULL; - lval = NULL; - if (op == JSOP_SETARG) - atom = GetSlotAtom(jp, js_GetArgument, i); - else if (op == JSOP_SETVAR) - atom = GetSlotAtom(jp, js_GetLocalVariable, i); - else if (op == JSOP_SETGVAR) - atom = GET_ATOM(cx, jp->script, pc); - else - lval = GetLocal(ss, i); - if (atom) - lval = js_AtomToPrintableString(cx, atom); - LOCAL_ASSERT(lval); - todo = SprintCString(&ss->sprinter, lval); - if (op != JSOP_SETLOCALPOP) { - pc += oplen; - if (pc == endpc) - return pc; - LOAD_OP_DATA(pc); - if (op == JSOP_SETSP) - return pc; - LOCAL_ASSERT(op == JSOP_POP); - } - break; - - default: - /* - * We may need to auto-parenthesize the left-most value decompiled - * here, so add back PAREN_SLOP temporarily. Then decompile until the - * opcode that would reduce the stack depth to (ss->top-1), which we - * pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for - * the nb parameter. - */ - todo = ss->sprinter.offset; - ss->sprinter.offset = todo + PAREN_SLOP; - pc = Decompile(ss, pc, -ss->top); - if (!pc) - return NULL; - if (pc == endpc) - return pc; - LOAD_OP_DATA(pc); - LOCAL_ASSERT(op == JSOP_ENUMELEM || op == JSOP_ENUMCONSTELEM); - xval = PopStr(ss, JSOP_NOP); - lval = PopStr(ss, JSOP_GETPROP); - ss->sprinter.offset = todo; - if (*lval == '\0') { - /* lval is from JSOP_BINDNAME, so just print xval. */ - todo = SprintCString(&ss->sprinter, xval); - } else if (*xval == '\0') { - /* xval is from JSOP_SETCALL or JSOP_BINDXMLNAME, print lval. */ - todo = SprintCString(&ss->sprinter, lval); - } else { - todo = Sprint(&ss->sprinter, - (js_CodeSpec[ss->opcodes[ss->top+1]].format - & JOF_XMLNAME) - ? "%s.%s" - : "%s[%s]", - lval, xval); - } - break; - } - - if (todo < 0) - return NULL; - - LOCAL_ASSERT(pc < endpc); - pc += oplen; - return pc; -} - -/* - * Starting with a SRC_DESTRUCT-annotated JSOP_DUP, decompile a destructuring - * left-hand side object or array initialiser, including nested destructuring - * initialisers. On successful return, the decompilation will be pushed on ss - * and the return value will point to the POP or GROUP bytecode following the - * destructuring expression. - * - * At any point, if pc is equal to endpc and would otherwise advance, we stop - * immediately and return endpc. - */ -static jsbytecode * -DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) -{ - ptrdiff_t head, todo; - JSContext *cx; - JSPrinter *jp; - JSOp op, saveop; - const JSCodeSpec *cs; - uintN oplen; - jsint i, lasti; - jsdouble d; - const char *lval; - jsbytecode *pc2; - jsatomid atomIndex; - JSAtom *atom; - jssrcnote *sn; - JSString *str; - JSBool hole; - - LOCAL_ASSERT(*pc == JSOP_DUP); - pc += JSOP_DUP_LENGTH; - - /* - * Set head so we can rewrite '[' to '{' as needed. Back up PAREN_SLOP - * chars so the destructuring decompilation accumulates contiguously in - * ss->sprinter starting with "[". - */ - head = SprintPut(&ss->sprinter, "[", 1); - if (head < 0 || !PushOff(ss, head, JSOP_NOP)) - return NULL; - ss->sprinter.offset -= PAREN_SLOP; - LOCAL_ASSERT(head == ss->sprinter.offset - 1); - LOCAL_ASSERT(*OFF2STR(&ss->sprinter, head) == '['); - - cx = ss->sprinter.context; - jp = ss->printer; - lasti = -1; - - while (pc < endpc) { - LOAD_OP_DATA(pc); - saveop = op; - - switch (op) { - case JSOP_POP: - pc += oplen; - goto out; - - /* Handle the optimized number-pushing opcodes. */ - case JSOP_ZERO: d = i = 0; goto do_getelem; - case JSOP_ONE: d = i = 1; goto do_getelem; - case JSOP_UINT16: d = i = GET_UINT16(pc); goto do_getelem; - case JSOP_UINT24: d = i = GET_UINT24(pc); goto do_getelem; - - /* Handle the extended literal form of JSOP_NUMBER. */ - case JSOP_LITOPX: - atomIndex = GET_LITERAL_INDEX(pc); - pc2 = pc + 1 + LITERAL_INDEX_LEN; - op = *pc2; - LOCAL_ASSERT(op == JSOP_NUMBER); - goto do_getatom; - - case JSOP_NUMBER: - atomIndex = GET_ATOM_INDEX(pc); - - do_getatom: - atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex); - d = *ATOM_TO_DOUBLE(atom); - LOCAL_ASSERT(JSDOUBLE_IS_FINITE(d) && !JSDOUBLE_IS_NEGZERO(d)); - i = (jsint)d; - - do_getelem: - sn = js_GetSrcNote(jp->script, pc); - pc += oplen; - if (pc == endpc) - return pc; - LOAD_OP_DATA(pc); - LOCAL_ASSERT(op == JSOP_GETELEM); - - /* Distinguish object from array by opcode or source note. */ - if (saveop == JSOP_LITERAL || - (sn && SN_TYPE(sn) == SRC_INITPROP)) { - *OFF2STR(&ss->sprinter, head) = '{'; - if (Sprint(&ss->sprinter, "%g: ", d) < 0) - return NULL; - } else { - /* Sanity check for the gnarly control flow above. */ - LOCAL_ASSERT(i == d); - - /* Fill in any holes (holes at the end don't matter). */ - while (++lasti < i) { - if (SprintPut(&ss->sprinter, ", ", 2) < 0) - return NULL; - } - } - break; - - case JSOP_LITERAL: - atomIndex = GET_LITERAL_INDEX(pc); - goto do_getatom; - - case JSOP_GETPROP: - *OFF2STR(&ss->sprinter, head) = '{'; - atom = GET_ATOM(cx, jp->script, pc); - str = ATOM_TO_STRING(atom); - if (!QuoteString(&ss->sprinter, str, - js_IsIdentifier(str) ? 0 : (jschar)'\'')) { - return NULL; - } - if (SprintPut(&ss->sprinter, ": ", 2) < 0) - return NULL; - break; - - default: - LOCAL_ASSERT(0); - } - - pc += oplen; - if (pc == endpc) - return pc; - - /* - * Decompile the left-hand side expression whose bytecode starts at pc - * and continues for a bounded number of bytecodes or stack operations - * (and which in any event stops before endpc). - */ - pc = DecompileDestructuringLHS(ss, pc, endpc, &hole); - if (!pc) - return NULL; - if (pc == endpc || *pc != JSOP_DUP) - break; - - /* - * Check for SRC_DESTRUCT on this JSOP_DUP, which would mean another - * destructuring initialiser abuts this one, and we should stop. This - * happens with source of the form '[a] = [b] = c'. - */ - sn = js_GetSrcNote(jp->script, pc); - if (sn && SN_TYPE(sn) == SRC_DESTRUCT) - break; - - if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0) - return NULL; - - pc += JSOP_DUP_LENGTH; - } - -out: - lval = OFF2STR(&ss->sprinter, head); - todo = SprintPut(&ss->sprinter, (*lval == '[') ? "]" : "}", 1); - if (todo < 0) - return NULL; - return pc; -} - -static jsbytecode * -DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, - jssrcnote *sn, ptrdiff_t *todop) -{ - JSOp op; - const JSCodeSpec *cs; - uintN oplen, start, end, i; - ptrdiff_t todo; - JSBool hole; - const char *rval; - - LOAD_OP_DATA(pc); - LOCAL_ASSERT(op == JSOP_PUSH || op == JSOP_GETLOCAL); - - todo = Sprint(&ss->sprinter, "%s[", VarPrefix(sn)); - if (todo < 0 || !PushOff(ss, todo, JSOP_NOP)) - return NULL; - ss->sprinter.offset -= PAREN_SLOP; - - for (;;) { - pc += oplen; - if (pc == endpc) - return pc; - pc = DecompileDestructuringLHS(ss, pc, endpc, &hole); - if (!pc) - return NULL; - if (pc == endpc) - return pc; - LOAD_OP_DATA(pc); - if (op != JSOP_PUSH && op != JSOP_GETLOCAL) - break; - if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0) - return NULL; - } - - LOCAL_ASSERT(op == JSOP_SETSP); - if (SprintPut(&ss->sprinter, "] = [", 5) < 0) - return NULL; - - start = GET_UINT16(pc); - end = ss->top - 1; - for (i = start; i < end; i++) { - rval = GetStr(ss, i); - if (Sprint(&ss->sprinter, "%s%s", - (i == start) ? "" : ", ", - (i == end - 1 && *rval == '\0') ? ", " : rval) < 0) { - return NULL; - } - } - - if (SprintPut(&ss->sprinter, "]", 1) < 0) - return NULL; - ss->sprinter.offset = ss->offsets[i]; - ss->top = start; - *todop = todo; - return pc; -} - -#undef LOCAL_ASSERT -#undef LOAD_OP_DATA - -#endif /* JS_HAS_DESTRUCTURING */ - -/* - * If nb is non-negative, decompile nb bytecodes starting at pc. Otherwise - * the decompiler starts at pc and continues until it reaches an opcode for - * which decompiling would result in the stack depth equaling -(nb + 1). - */ -static jsbytecode * -Decompile(SprintStack *ss, jsbytecode *pc, intN nb) -{ - JSContext *cx; - JSPrinter *jp, *jp2; - jsbytecode *startpc, *endpc, *pc2, *done, *forelem_tail, *forelem_done; - ptrdiff_t tail, todo, len, oplen, cond, next; - JSOp op, lastop, saveop; - const JSCodeSpec *cs; - jssrcnote *sn, *sn2; - const char *lval, *rval, *xval, *fmt; - jsint i, argc; - char **argv; - jsatomid atomIndex; - JSAtom *atom; - JSObject *obj; - JSFunction *fun; - JSString *str; - JSBool ok; -#if JS_HAS_XML_SUPPORT - JSBool foreach, inXML, quoteAttr; -#else -#define inXML JS_FALSE -#endif - jsval val; - int stackDummy; - - static const char exception_cookie[] = "/*EXCEPTION*/"; - static const char retsub_pc_cookie[] = "/*RETSUB_PC*/"; - static const char forelem_cookie[] = "/*FORELEM*/"; - static const char with_cookie[] = "/*WITH*/"; - static const char dot_format[] = "%s.%s"; - static const char index_format[] = "%s[%s]"; - static const char predot_format[] = "%s%s.%s"; - static const char postdot_format[] = "%s.%s%s"; - static const char preindex_format[] = "%s%s[%s]"; - static const char postindex_format[] = "%s[%s]%s"; - static const char ss_format[] = "%s%s"; - -/* - * Local macros - */ -#define DECOMPILE_CODE(pc,nb) if (!Decompile(ss, pc, nb)) return NULL -#define POP_STR() PopStr(ss, op) -#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, JS_FALSE) - -/* - * Callers know that ATOM_IS_STRING(atom), and we leave it to the optimizer to - * common ATOM_TO_STRING(atom) here and near the call sites. - */ -#define ATOM_IS_IDENTIFIER(atom) js_IsIdentifier(ATOM_TO_STRING(atom)) -#define ATOM_IS_KEYWORD(atom) \ - (js_CheckKeyword(JSSTRING_CHARS(ATOM_TO_STRING(atom)), \ - JSSTRING_LENGTH(ATOM_TO_STRING(atom))) != TOK_EOF) - -/* - * Given an atom already fetched from jp->script's atom map, quote/escape its - * string appropriately into rval, and select fmt from the quoted and unquoted - * alternatives. - */ -#define GET_QUOTE_AND_FMT(qfmt, ufmt, rval) \ - JS_BEGIN_MACRO \ - jschar quote_; \ - if (!ATOM_IS_IDENTIFIER(atom)) { \ - quote_ = '\''; \ - fmt = qfmt; \ - } else { \ - quote_ = 0; \ - fmt = ufmt; \ - } \ - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), quote_); \ - if (!rval) \ - return NULL; \ - JS_END_MACRO - -/* - * Get atom from jp->script's atom map, quote/escape its string appropriately - * into rval, and select fmt from the quoted and unquoted alternatives. - */ -#define GET_ATOM_QUOTE_AND_FMT(qfmt, ufmt, rval) \ - JS_BEGIN_MACRO \ - atom = GET_ATOM(cx, jp->script, pc); \ - GET_QUOTE_AND_FMT(qfmt, ufmt, rval); \ - JS_END_MACRO - - cx = ss->sprinter.context; - if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); - return NULL; - } - - jp = ss->printer; - startpc = pc; - endpc = (nb < 0) ? jp->script->code + jp->script->length : pc + nb; - forelem_tail = forelem_done = NULL; - tail = -1; - todo = -2; /* NB: different from Sprint() error return. */ - saveop = JSOP_NOP; - sn = NULL; - rval = NULL; -#if JS_HAS_XML_SUPPORT - foreach = inXML = quoteAttr = JS_FALSE; -#endif - - while (nb < 0 || pc < endpc) { - /* - * Move saveop to lastop so prefixed bytecodes can take special action - * while sharing maximal code. Set op and saveop to the new bytecode, - * use op in POP_STR to trigger automatic parenthesization, but push - * saveop at the bottom of the loop if this op pushes. Thus op may be - * set to nop or otherwise mutated to suppress auto-parens. - */ - lastop = saveop; - op = saveop = (JSOp) *pc; - cs = &js_CodeSpec[saveop]; - len = oplen = cs->length; - - if (nb < 0 && -(nb + 1) == (intN)ss->top - cs->nuses + cs->ndefs) - return pc; - - if (pc + oplen == jp->dvgfence) { - JSStackFrame *fp; - uint32 format, mode, type; - - /* - * Rewrite non-get ops to their "get" format if the error is in - * the bytecode at pc, so we don't decompile more than the error - * expression. - */ - for (fp = cx->fp; fp && !fp->script; fp = fp->down) - continue; - format = cs->format; - if (((fp && pc == fp->pc) || - (pc == startpc && cs->nuses != 0)) && - format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_IMPORT|JOF_FOR)) { - mode = (format & JOF_MODEMASK); - if (mode == JOF_NAME) { - /* - * JOF_NAME does not imply JOF_CONST, so we must check for - * the QARG and QVAR format types, and translate those to - * JSOP_GETARG or JSOP_GETVAR appropriately, instead of to - * JSOP_NAME. - */ - type = format & JOF_TYPEMASK; - op = (type == JOF_QARG) - ? JSOP_GETARG - : (type == JOF_QVAR) - ? JSOP_GETVAR - : (type == JOF_LOCAL) - ? JSOP_GETLOCAL - : JSOP_NAME; - - i = cs->nuses - js_CodeSpec[op].nuses; - while (--i >= 0) - PopOff(ss, JSOP_NOP); - } else { - /* - * We must replace the faulting pc's bytecode with a - * corresponding JSOP_GET* code. For JSOP_SET{PROP,ELEM}, - * we must use the "2nd" form of JSOP_GET{PROP,ELEM}, to - * throw away the assignment op's right-hand operand and - * decompile it as if it were a GET of its left-hand - * operand. - */ - if (mode == JOF_PROP) { - op = (format & JOF_SET) ? JSOP_GETPROP2 : JSOP_GETPROP; - } else if (mode == JOF_ELEM) { - op = (format & JOF_SET) ? JSOP_GETELEM2 : JSOP_GETELEM; - } else { - /* - * Zero mode means precisely that op is uncategorized - * for our purposes, so we must write per-op special - * case code here. - */ - switch (op) { - case JSOP_ENUMELEM: - case JSOP_ENUMCONSTELEM: - op = JSOP_GETELEM; - break; -#if JS_HAS_LVALUE_RETURN - case JSOP_SETCALL: - op = JSOP_CALL; - break; -#endif - default: - LOCAL_ASSERT(0); - } - } - } - } - - saveop = op; - if (op >= JSOP_LIMIT) { - switch (op) { - case JSOP_GETPROP2: - saveop = JSOP_GETPROP; - break; - case JSOP_GETELEM2: - saveop = JSOP_GETELEM; - break; - default:; - } - } - LOCAL_ASSERT(js_CodeSpec[saveop].length == oplen); - - jp->dvgfence = NULL; - } - - if (cs->token) { - switch (cs->nuses) { - case 2: - sn = js_GetSrcNote(jp->script, pc); - if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) { - /* - * Avoid over-parenthesizing y in x op= y based on its - * expansion: x = x op y (replace y by z = w to see the - * problem). - */ - op = pc[oplen]; - LOCAL_ASSERT(op != saveop); - } - rval = POP_STR(); - lval = POP_STR(); - if (op != saveop) { - /* Print only the right operand of the assignment-op. */ - todo = SprintCString(&ss->sprinter, rval); - op = saveop; - } else if (!inXML) { - todo = Sprint(&ss->sprinter, "%s %s %s", - lval, cs->token, rval); - } else { - /* In XML, just concatenate the two operands. */ - LOCAL_ASSERT(op == JSOP_ADD); - todo = Sprint(&ss->sprinter, ss_format, lval, rval); - } - break; - - case 1: - rval = POP_STR(); - todo = Sprint(&ss->sprinter, ss_format, cs->token, rval); - break; - - case 0: - todo = SprintCString(&ss->sprinter, cs->token); - break; - - default: - todo = -2; - break; - } - } else { - switch (op) { -#define BEGIN_LITOPX_CASE(OP) \ - case OP: \ - atomIndex = GET_ATOM_INDEX(pc); \ - do_##OP: \ - atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex); - -#define END_LITOPX_CASE \ - break; - - case JSOP_NOP: - /* - * Check for a do-while loop, a for-loop with an empty - * initializer part, a labeled statement, a function - * definition, or try/finally. - */ - sn = js_GetSrcNote(jp->script, pc); - todo = -2; - switch (sn ? SN_TYPE(sn) : SRC_NULL) { - case SRC_WHILE: - js_printf(SET_MAYBE_BRACE(jp), "\tdo {\n"); - jp->indent += 4; - break; - - case SRC_FOR: - rval = ""; - - do_forloop: - /* Skip the JSOP_NOP or JSOP_POP bytecode. */ - pc++; - - /* Get the cond, next, and loop-closing tail offsets. */ - cond = js_GetSrcNoteOffset(sn, 0); - next = js_GetSrcNoteOffset(sn, 1); - tail = js_GetSrcNoteOffset(sn, 2); - LOCAL_ASSERT(tail + GetJumpOffset(pc+tail, pc+tail) == 0); - - /* Print the keyword and the possibly empty init-part. */ - js_printf(jp, "\tfor (%s;", rval); - - if (pc[cond] == JSOP_IFEQ || pc[cond] == JSOP_IFEQX) { - /* Decompile the loop condition. */ - DECOMPILE_CODE(pc, cond); - js_printf(jp, " %s", POP_STR()); - } - - /* Need a semicolon whether or not there was a cond. */ - js_puts(jp, ";"); - - if (pc[next] != JSOP_GOTO && pc[next] != JSOP_GOTOX) { - /* Decompile the loop updater. */ - DECOMPILE_CODE(pc + next, tail - next - 1); - js_printf(jp, " %s", POP_STR()); - } - - /* Do the loop body. */ - js_printf(SET_MAYBE_BRACE(jp), ") {\n"); - jp->indent += 4; - oplen = (cond) ? js_CodeSpec[pc[cond]].length : 0; - DECOMPILE_CODE(pc + cond + oplen, next - cond - oplen); - jp->indent -= 4; - js_printf(jp, "\t}\n"); - - /* Set len so pc skips over the entire loop. */ - len = tail + js_CodeSpec[pc[tail]].length; - break; - - case SRC_LABEL: - atom = js_GetAtom(cx, &jp->script->atomMap, - (jsatomid) js_GetSrcNoteOffset(sn, 0)); - jp->indent -= 4; - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!rval) - return NULL; - RETRACT(&ss->sprinter, rval); - js_printf(CLEAR_MAYBE_BRACE(jp), "\t%s:\n", rval); - jp->indent += 4; - break; - - case SRC_LABELBRACE: - atom = js_GetAtom(cx, &jp->script->atomMap, - (jsatomid) js_GetSrcNoteOffset(sn, 0)); - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!rval) - return NULL; - RETRACT(&ss->sprinter, rval); - js_printf(CLEAR_MAYBE_BRACE(jp), "\t%s: {\n", rval); - jp->indent += 4; - break; - - case SRC_ENDBRACE: - jp->indent -= 4; - js_printf(jp, "\t}\n"); - break; - - case SRC_FUNCDEF: - atom = js_GetAtom(cx, &jp->script->atomMap, - (jsatomid) js_GetSrcNoteOffset(sn, 0)); - LOCAL_ASSERT(ATOM_IS_OBJECT(atom)); - do_function: - obj = ATOM_TO_OBJECT(atom); - fun = (JSFunction *) JS_GetPrivate(cx, obj); - jp2 = js_NewPrinter(cx, JS_GetFunctionName(fun), - jp->indent, jp->pretty); - if (!jp2) - return NULL; - jp2->scope = jp->scope; - js_puts(jp2, "\n"); - ok = js_DecompileFunction(jp2, fun); - if (ok) { - js_puts(jp2, "\n"); - str = js_GetPrinterOutput(jp2); - if (str) - js_printf(jp, "%s\n", JS_GetStringBytes(str)); - else - ok = JS_FALSE; - } - js_DestroyPrinter(jp2); - if (!ok) - return NULL; - - break; - - case SRC_BRACE: - js_printf(CLEAR_MAYBE_BRACE(jp), "\t{\n"); - jp->indent += 4; - len = js_GetSrcNoteOffset(sn, 0); - DECOMPILE_CODE(pc + oplen, len - oplen); - jp->indent -= 4; - js_printf(jp, "\t}\n"); - break; - - default:; - } - break; - - case JSOP_GROUP: - cs = &js_CodeSpec[lastop]; - if ((cs->prec != 0 && - cs->prec == js_CodeSpec[pc[JSOP_GROUP_LENGTH]].prec) || - pc[JSOP_GROUP_LENGTH] == JSOP_PUSHOBJ || - pc[JSOP_GROUP_LENGTH] == JSOP_DUP) { - /* - * Force parens if this JSOP_GROUP forced re-association - * against precedence, or if this is a call or constructor - * expression, or if it is destructured (JSOP_DUP). - * - * This is necessary to handle the operator new grammar, - * by which new x(y).z means (new x(y))).z. For example - * new (x(y).z) must decompile with the constructor - * parenthesized, but normal precedence has JSOP_GETPROP - * (for the final .z) higher than JSOP_NEW. In general, - * if the call or constructor expression is parenthesized, - * we preserve parens. - */ - op = JSOP_NAME; - rval = POP_STR(); - todo = SprintCString(&ss->sprinter, rval); - } else { - /* - * Don't explicitly parenthesize -- just fix the top - * opcode so that the auto-parens magic in PopOff can do - * its thing. - */ - LOCAL_ASSERT(ss->top != 0); - ss->opcodes[ss->top-1] = saveop = lastop; - todo = -2; - } - break; - - case JSOP_STARTITER: - todo = -2; - break; - - case JSOP_PUSH: -#if JS_HAS_DESTRUCTURING - sn = js_GetSrcNote(jp->script, pc); - if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) { - pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo); - if (!pc) - return NULL; - LOCAL_ASSERT(*pc == JSOP_SETSP); - len = oplen = JSOP_SETSP_LENGTH; - goto end_groupassignment; - } -#endif - /* FALL THROUGH */ - - case JSOP_PUSHOBJ: - case JSOP_BINDNAME: - do_JSOP_BINDNAME: - todo = Sprint(&ss->sprinter, ""); - break; - - case JSOP_TRY: - js_printf(CLEAR_MAYBE_BRACE(jp), "\ttry {\n"); - jp->indent += 4; - todo = -2; - break; - - case JSOP_FINALLY: - jp->indent -= 4; - js_printf(CLEAR_MAYBE_BRACE(jp), "\t} finally {\n"); - jp->indent += 4; - - /* - * We must push an empty string placeholder for gosub's return - * address, popped by JSOP_RETSUB and counted by script->depth - * but not by ss->top (see JSOP_SETSP, below). - */ - todo = Sprint(&ss->sprinter, exception_cookie); - if (todo < 0 || !PushOff(ss, todo, op)) - return NULL; - todo = Sprint(&ss->sprinter, retsub_pc_cookie); - break; - - case JSOP_RETSUB: - rval = POP_STR(); - LOCAL_ASSERT(strcmp(rval, retsub_pc_cookie) == 0); - lval = POP_STR(); - LOCAL_ASSERT(strcmp(lval, exception_cookie) == 0); - todo = -2; - break; - - case JSOP_SWAP: - /* - * We don't generate this opcode currently, and previously we - * did not need to decompile it. If old, serialized bytecode - * uses it still, we should fall through and set todo = -2. - */ - /* FALL THROUGH */ - - case JSOP_GOSUB: - case JSOP_GOSUBX: - /* - * JSOP_GOSUB and GOSUBX have no effect on the decompiler's - * string stack because the next op in bytecode order finds - * the stack balanced by a JSOP_RETSUB executed elsewhere. - */ - todo = -2; - break; - - case JSOP_SETSP: - { - uintN newtop, oldtop, i; - - /* - * The compiler models operand stack depth and fixes the stack - * pointer on entry to a catch clause based on its depth model. - * The decompiler must match the code generator's model, which - * is why JSOP_FINALLY pushes a cookie that JSOP_RETSUB pops. - */ - newtop = (uintN) GET_UINT16(pc); - oldtop = ss->top; - LOCAL_ASSERT(newtop <= oldtop); - todo = -2; - -#if JS_HAS_DESTRUCTURING - sn = js_GetSrcNote(jp->script, pc); - if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) { - todo = Sprint(&ss->sprinter, "%s[] = [", - VarPrefix(sn)); - if (todo < 0) - return NULL; - for (i = newtop; i < oldtop; i++) { - rval = OFF2STR(&ss->sprinter, ss->offsets[i]); - if (Sprint(&ss->sprinter, ss_format, - (i == newtop) ? "" : ", ", - (i == oldtop - 1 && *rval == '\0') - ? ", " : rval) < 0) { - return NULL; - } - } - if (SprintPut(&ss->sprinter, "]", 1) < 0) - return NULL; - - /* - * Kill newtop before the end_groupassignment: label by - * retracting/popping early. Control will either jump to - * do_forloop: or do_letheadbody: or else break from our - * case JSOP_SETSP: after the switch (*pc2) below. - */ - if (newtop < oldtop) { - ss->sprinter.offset = GetOff(ss, newtop); - ss->top = newtop; - } - - end_groupassignment: - /* - * Thread directly to the next opcode if we can, to handle - * the special cases of a group assignment in the first or - * last part of a for(;;) loop head, or in a let block or - * expression head. - * - * NB: todo at this point indexes space in ss->sprinter - * that is liable to be overwritten. The code below knows - * exactly how long rval lives, or else copies it down via - * SprintCString. - */ - rval = OFF2STR(&ss->sprinter, todo); - todo = -2; - pc2 = pc + oplen; - switch (*pc2) { - case JSOP_NOP: - /* First part of for(;;) or let block/expr head. */ - sn = js_GetSrcNote(jp->script, pc2); - if (sn) { - if (SN_TYPE(sn) == SRC_FOR) { - pc = pc2; - goto do_forloop; - } - if (SN_TYPE(sn) == SRC_DECL) { - if (ss->top == jp->script->depth) { - /* - * This must be an empty destructuring - * in the head of a let whose body block - * is also empty. - */ - pc = pc2 + 1; - len = js_GetSrcNoteOffset(sn, 0); - LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCK); - js_printf(jp, "\tlet (%s) {\n", rval); - js_printf(jp, "\t}\n"); - goto end_setsp; - } - todo = SprintCString(&ss->sprinter, rval); - if (todo < 0 || !PushOff(ss, todo, JSOP_NOP)) - return NULL; - op = JSOP_POP; - pc = pc2 + 1; - goto do_letheadbody; - } - } - break; - - case JSOP_GOTO: - case JSOP_GOTOX: - /* Third part of for(;;) loop head. */ - cond = GetJumpOffset(pc2, pc2); - sn = js_GetSrcNote(jp->script, pc2 + cond - 1); - if (sn && SN_TYPE(sn) == SRC_FOR) { - todo = SprintCString(&ss->sprinter, rval); - saveop = JSOP_NOP; - } - break; - } - - /* - * If control flow reaches this point with todo still -2, - * just print rval as an expression statement. - */ - if (todo == -2) - js_printf(jp, "\t%s;\n", rval); - end_setsp: - break; - } -#endif - if (newtop < oldtop) { - ss->sprinter.offset = GetOff(ss, newtop); - ss->top = newtop; - } - break; - } - - case JSOP_EXCEPTION: - /* The catch decompiler handles this op itself. */ - LOCAL_ASSERT(JS_FALSE); - break; - - case JSOP_POP: - /* - * By default, do not automatically parenthesize when popping - * a stacked expression decompilation. We auto-parenthesize - * only when JSOP_POP is annotated with SRC_PCDELTA, meaning - * comma operator. - */ - op = JSOP_POPV; - /* FALL THROUGH */ - - case JSOP_POPV: - sn = js_GetSrcNote(jp->script, pc); - switch (sn ? SN_TYPE(sn) : SRC_NULL) { - case SRC_FOR: - /* Force parens around 'in' expression at 'for' front. */ - if (ss->opcodes[ss->top-1] == JSOP_IN) - op = JSOP_LSH; - rval = POP_STR(); - todo = -2; - goto do_forloop; - - case SRC_PCDELTA: - /* Comma operator: use JSOP_POP for correct precedence. */ - op = JSOP_POP; - - /* Pop and save to avoid blowing stack depth budget. */ - lval = JS_strdup(cx, POP_STR()); - if (!lval) - return NULL; - - /* - * The offset tells distance to the end of the right-hand - * operand of the comma operator. - */ - done = pc + len; - pc += js_GetSrcNoteOffset(sn, 0); - len = 0; - - if (!Decompile(ss, done, pc - done)) { - JS_free(cx, (char *)lval); - return NULL; - } - - /* Pop Decompile result and print comma expression. */ - rval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s, %s", lval, rval); - JS_free(cx, (char *)lval); - break; - - case SRC_HIDDEN: - /* Hide this pop, it's from a goto in a with or for/in. */ - todo = -2; - break; - - case SRC_DECL: - /* This pop is at the end of the let block/expr head. */ - pc += JSOP_POP_LENGTH; -#if JS_HAS_DESTRUCTURING - do_letheadbody: -#endif - len = js_GetSrcNoteOffset(sn, 0); - if (pc[len] == JSOP_LEAVEBLOCK) { - js_printf(CLEAR_MAYBE_BRACE(jp), "\tlet (%s) {\n", - POP_STR()); - jp->indent += 4; - DECOMPILE_CODE(pc, len); - jp->indent -= 4; - js_printf(jp, "\t}\n"); - todo = -2; - } else { - LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCKEXPR); - - lval = JS_strdup(cx, POP_STR()); - if (!lval) - return NULL; - - if (!Decompile(ss, pc, len)) { - JS_free(cx, (char *)lval); - return NULL; - } - rval = POP_STR(); - todo = Sprint(&ss->sprinter, - (*rval == '{') - ? "let (%s) (%s)" - : "let (%s) %s", - lval, rval); - JS_free(cx, (char *)lval); - } - break; - - default: - /* Turn off parens around a yield statement. */ - if (ss->opcodes[ss->top-1] == JSOP_YIELD) - op = JSOP_NOP; - - rval = POP_STR(); - if (*rval != '\0') { -#if JS_HAS_BLOCK_SCOPE - /* - * If a let declaration is the only child of a control - * structure that does not require braces, it must not - * be braced. If it were braced explicitly, it would - * be bracketed by JSOP_ENTERBLOCK/JSOP_LEAVEBLOCK. - */ - if (jp->braceState == MAYBE_BRACE && - pc + JSOP_POP_LENGTH == endpc && - !strncmp(rval, var_prefix[SRC_DECL_LET], 4) && - rval[4] != '(') { - SetDontBrace(jp); - } -#endif - js_printf(jp, - (*rval == '{' || - (strncmp(rval, js_function_str, 8) == 0 && - rval[8] == ' ')) - ? "\t(%s);\n" - : "\t%s;\n", - rval); - } - todo = -2; - break; - } - break; - - case JSOP_POP2: - case JSOP_ENDITER: - sn = js_GetSrcNote(jp->script, pc); - todo = -2; - if (sn && SN_TYPE(sn) == SRC_HIDDEN) - break; - (void) PopOff(ss, op); - if (op == JSOP_POP2) - (void) PopOff(ss, op); - break; - - case JSOP_ENTERWITH: - LOCAL_ASSERT(!js_GetSrcNote(jp->script, pc)); - rval = POP_STR(); - js_printf(SET_MAYBE_BRACE(jp), "\twith (%s) {\n", rval); - jp->indent += 4; - todo = Sprint(&ss->sprinter, with_cookie); - break; - - case JSOP_LEAVEWITH: - sn = js_GetSrcNote(jp->script, pc); - todo = -2; - if (sn && SN_TYPE(sn) == SRC_HIDDEN) - break; - rval = POP_STR(); - LOCAL_ASSERT(strcmp(rval, with_cookie) == 0); - jp->indent -= 4; - js_printf(jp, "\t}\n"); - break; - - BEGIN_LITOPX_CASE(JSOP_ENTERBLOCK) - { - JSAtom **atomv, *smallv[5]; - JSScopeProperty *sprop; - - obj = ATOM_TO_OBJECT(atom); - argc = OBJ_BLOCK_COUNT(cx, obj); - if ((size_t)argc <= sizeof smallv / sizeof smallv[0]) { - atomv = smallv; - } else { - atomv = (JSAtom **) JS_malloc(cx, argc * sizeof(JSAtom *)); - if (!atomv) - return NULL; - } - - /* From here on, control must flow through enterblock_out. */ - for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; - sprop = sprop->parent) { - if (!(sprop->flags & SPROP_HAS_SHORTID)) - continue; - LOCAL_ASSERT(sprop->shortid < argc); - atomv[sprop->shortid] = JSID_TO_ATOM(sprop->id); - } - ok = JS_TRUE; - for (i = 0; i < argc; i++) { - atom = atomv[i]; - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!rval || - !PushOff(ss, STR2OFF(&ss->sprinter, rval), op)) { - ok = JS_FALSE; - goto enterblock_out; - } - } - - sn = js_GetSrcNote(jp->script, pc); - switch (sn ? SN_TYPE(sn) : SRC_NULL) { -#if JS_HAS_BLOCK_SCOPE - case SRC_BRACE: - js_printf(CLEAR_MAYBE_BRACE(jp), "\t{\n"); - jp->indent += 4; - len = js_GetSrcNoteOffset(sn, 0); - ok = Decompile(ss, pc + oplen, len - oplen) != NULL; - if (!ok) - goto enterblock_out; - jp->indent -= 4; - js_printf(jp, "\t}\n"); - break; -#endif - - case SRC_CATCH: - jp->indent -= 4; - js_printf(CLEAR_MAYBE_BRACE(jp), "\t} catch ("); - - pc2 = pc; - pc += oplen; - LOCAL_ASSERT(*pc == JSOP_EXCEPTION); - pc += JSOP_EXCEPTION_LENGTH; - if (*pc == JSOP_DUP) { - sn2 = js_GetSrcNote(jp->script, pc); - if (sn2 && SN_TYPE(sn2) == SRC_HIDDEN) { - /* - * This is a hidden dup to save the exception for - * later. It must exist only when the catch has - * an exception guard. - */ - LOCAL_ASSERT(js_GetSrcNoteOffset(sn, 0) != 0); - pc += JSOP_DUP_LENGTH; - } - } -#if JS_HAS_DESTRUCTURING - if (*pc == JSOP_DUP) { - pc = DecompileDestructuring(ss, pc, endpc); - if (!pc) { - ok = JS_FALSE; - goto enterblock_out; - } - LOCAL_ASSERT(*pc == JSOP_POP); - pc += JSOP_POP_LENGTH; - lval = PopStr(ss, JSOP_NOP); - js_puts(jp, lval); - } else { -#endif - LOCAL_ASSERT(*pc == JSOP_SETLOCALPOP); - i = GET_UINT16(pc); - pc += JSOP_SETLOCALPOP_LENGTH; - atom = atomv[i - OBJ_BLOCK_DEPTH(cx, obj)]; - str = ATOM_TO_STRING(atom); - if (!QuoteString(&jp->sprinter, str, 0)) { - ok = JS_FALSE; - goto enterblock_out; - } -#if JS_HAS_DESTRUCTURING - } -#endif - - len = js_GetSrcNoteOffset(sn, 0); - if (len) { - len -= PTRDIFF(pc, pc2, jsbytecode); - LOCAL_ASSERT(len > 0); - js_printf(jp, " if "); - ok = Decompile(ss, pc, len) != NULL; - if (!ok) - goto enterblock_out; - js_printf(jp, "%s", POP_STR()); - pc += len; - LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX); - pc += js_CodeSpec[*pc].length; - } - - js_printf(jp, ") {\n"); - jp->indent += 4; - len = 0; - break; - } - - todo = -2; - - enterblock_out: - if (atomv != smallv) - JS_free(cx, atomv); - if (!ok) - return NULL; - } - END_LITOPX_CASE - - case JSOP_LEAVEBLOCK: - case JSOP_LEAVEBLOCKEXPR: - { - uintN top, depth; - - sn = js_GetSrcNote(jp->script, pc); - todo = -2; - if (op == JSOP_LEAVEBLOCKEXPR) { - LOCAL_ASSERT(SN_TYPE(sn) == SRC_PCBASE); - rval = POP_STR(); - } else if (sn) { - LOCAL_ASSERT(op == JSOP_LEAVEBLOCK); - if (SN_TYPE(sn) == SRC_HIDDEN) - break; - LOCAL_ASSERT(SN_TYPE(sn) == SRC_CATCH); - LOCAL_ASSERT((uintN)js_GetSrcNoteOffset(sn, 0) == ss->top); - } - top = ss->top; - depth = GET_UINT16(pc); - LOCAL_ASSERT(top >= depth); - top -= depth; - ss->top = top; - ss->sprinter.offset = GetOff(ss, top); - if (op == JSOP_LEAVEBLOCKEXPR) - todo = SprintCString(&ss->sprinter, rval); - break; - } - - case JSOP_GETLOCAL: - i = GET_UINT16(pc); - sn = js_GetSrcNote(jp->script, pc); - LOCAL_ASSERT((uintN)i < ss->top); - rval = GetLocal(ss, i); - -#if JS_HAS_DESTRUCTURING - if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) { - pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo); - if (!pc) - return NULL; - LOCAL_ASSERT(*pc == JSOP_SETSP); - len = oplen = JSOP_SETSP_LENGTH; - goto end_groupassignment; - } -#endif - - todo = Sprint(&ss->sprinter, ss_format, VarPrefix(sn), rval); - break; - - case JSOP_SETLOCAL: - case JSOP_SETLOCALPOP: - i = GET_UINT16(pc); - lval = GetStr(ss, i); - rval = POP_STR(); - goto do_setlval; - - case JSOP_INCLOCAL: - case JSOP_DECLOCAL: - i = GET_UINT16(pc); - lval = GetLocal(ss, i); - goto do_inclval; - - case JSOP_LOCALINC: - case JSOP_LOCALDEC: - i = GET_UINT16(pc); - lval = GetLocal(ss, i); - goto do_lvalinc; - - case JSOP_FORLOCAL: - i = GET_UINT16(pc); - lval = GetStr(ss, i); - atom = NULL; - goto do_forlvalinloop; - - case JSOP_RETRVAL: - todo = -2; - break; - - case JSOP_SETRVAL: - case JSOP_RETURN: - rval = POP_STR(); - if (*rval != '\0') - js_printf(jp, "\t%s %s;\n", js_return_str, rval); - else - js_printf(jp, "\t%s;\n", js_return_str); - todo = -2; - break; - -#if JS_HAS_GENERATORS - case JSOP_YIELD: - op = JSOP_SETNAME; /* turn off most parens */ - rval = POP_STR(); - todo = (*rval != '\0') - ? Sprint(&ss->sprinter, - (strncmp(rval, js_yield_str, 5) == 0 && - (rval[5] == ' ' || rval[5] == '\0')) - ? "%s (%s)" - : "%s %s", - js_yield_str, rval) - : SprintCString(&ss->sprinter, js_yield_str); - break; - - case JSOP_ARRAYPUSH: - { - uintN pos, blockpos, startpos; - ptrdiff_t start; - - rval = POP_STR(); - pos = ss->top; - while ((op = ss->opcodes[--pos]) != JSOP_ENTERBLOCK && - op != JSOP_NEWINIT) { - LOCAL_ASSERT(pos != 0); - } - blockpos = pos; - while (ss->opcodes[pos] == JSOP_ENTERBLOCK) { - if (pos == 0) - break; - --pos; - } - LOCAL_ASSERT(ss->opcodes[pos] == JSOP_NEWINIT); - startpos = pos; - start = ss->offsets[pos]; - LOCAL_ASSERT(ss->sprinter.base[start] == '[' || - ss->sprinter.base[start] == '#'); - pos = blockpos; - while (ss->opcodes[++pos] == JSOP_STARTITER) - LOCAL_ASSERT(pos < ss->top); - LOCAL_ASSERT(pos < ss->top); - xval = OFF2STR(&ss->sprinter, ss->offsets[pos]); - lval = OFF2STR(&ss->sprinter, start); - RETRACT(&ss->sprinter, lval); - todo = Sprint(&ss->sprinter, "%s%s%.*s", - lval, rval, rval - xval, xval); - if (todo < 0) - return NULL; - ss->offsets[startpos] = todo; - todo = -2; - break; - } -#endif - - case JSOP_THROWING: - todo = -2; - break; - - case JSOP_THROW: - sn = js_GetSrcNote(jp->script, pc); - todo = -2; - if (sn && SN_TYPE(sn) == SRC_HIDDEN) - break; - rval = POP_STR(); - js_printf(jp, "\t%s %s;\n", cs->name, rval); - break; - - case JSOP_GOTO: - case JSOP_GOTOX: - sn = js_GetSrcNote(jp->script, pc); - switch (sn ? SN_TYPE(sn) : SRC_NULL) { - case SRC_CONT2LABEL: - atom = js_GetAtom(cx, &jp->script->atomMap, - (jsatomid) js_GetSrcNoteOffset(sn, 0)); - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!rval) - return NULL; - RETRACT(&ss->sprinter, rval); - js_printf(jp, "\tcontinue %s;\n", rval); - break; - case SRC_CONTINUE: - js_printf(jp, "\tcontinue;\n"); - break; - case SRC_BREAK2LABEL: - atom = js_GetAtom(cx, &jp->script->atomMap, - (jsatomid) js_GetSrcNoteOffset(sn, 0)); - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!rval) - return NULL; - RETRACT(&ss->sprinter, rval); - js_printf(jp, "\tbreak %s;\n", rval); - break; - case SRC_HIDDEN: - break; - default: - js_printf(jp, "\tbreak;\n"); - break; - } - todo = -2; - break; - - case JSOP_IFEQ: - case JSOP_IFEQX: - { - JSBool elseif = JS_FALSE; - - if_again: - len = GetJumpOffset(pc, pc); - sn = js_GetSrcNote(jp->script, pc); - - switch (sn ? SN_TYPE(sn) : SRC_NULL) { - case SRC_IF: - case SRC_IF_ELSE: - op = JSOP_NOP; /* turn off parens */ - rval = POP_STR(); - if (ss->inArrayInit) { - LOCAL_ASSERT(SN_TYPE(sn) == SRC_IF); - if (Sprint(&ss->sprinter, " if (%s)", rval) < 0) - return NULL; - } else { - js_printf(SET_MAYBE_BRACE(jp), - elseif ? " if (%s) {\n" : "\tif (%s) {\n", - rval); - jp->indent += 4; - } - - if (SN_TYPE(sn) == SRC_IF) { - DECOMPILE_CODE(pc + oplen, len - oplen); - } else { - LOCAL_ASSERT(!ss->inArrayInit); - tail = js_GetSrcNoteOffset(sn, 0); - DECOMPILE_CODE(pc + oplen, tail - oplen); - jp->indent -= 4; - pc += tail; - LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX); - oplen = js_CodeSpec[*pc].length; - len = GetJumpOffset(pc, pc); - js_printf(jp, "\t} else"); - - /* - * If the second offset for sn is non-zero, it tells - * the distance from the goto around the else, to the - * ifeq for the if inside the else that forms an "if - * else if" chain. Thus cond spans the condition of - * the second if, so we simply decompile it and start - * over at label if_again. - */ - cond = js_GetSrcNoteOffset(sn, 1); - if (cond != 0) { - DECOMPILE_CODE(pc + oplen, cond - oplen); - pc += cond; - elseif = JS_TRUE; - goto if_again; - } - - js_printf(SET_MAYBE_BRACE(jp), " {\n"); - jp->indent += 4; - DECOMPILE_CODE(pc + oplen, len - oplen); - } - - if (!ss->inArrayInit) { - jp->indent -= 4; - js_printf(jp, "\t}\n"); - } - todo = -2; - break; - - case SRC_WHILE: - rval = POP_STR(); - js_printf(SET_MAYBE_BRACE(jp), "\twhile (%s) {\n", rval); - jp->indent += 4; - tail = js_GetSrcNoteOffset(sn, 0); - DECOMPILE_CODE(pc + oplen, tail - oplen); - jp->indent -= 4; - js_printf(jp, "\t}\n"); - todo = -2; - break; - - case SRC_COND: - xval = JS_strdup(cx, POP_STR()); - if (!xval) - return NULL; - len = js_GetSrcNoteOffset(sn, 0); - DECOMPILE_CODE(pc + oplen, len - oplen); - lval = JS_strdup(cx, POP_STR()); - if (!lval) { - JS_free(cx, (void *)xval); - return NULL; - } - pc += len; - LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX); - oplen = js_CodeSpec[*pc].length; - len = GetJumpOffset(pc, pc); - DECOMPILE_CODE(pc + oplen, len - oplen); - rval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s ? %s : %s", - xval, lval, rval); - JS_free(cx, (void *)xval); - JS_free(cx, (void *)lval); - break; - - default: - break; - } - break; - } - - case JSOP_IFNE: - case JSOP_IFNEX: - /* Currently, this must be a do-while loop's upward branch. */ - jp->indent -= 4; - js_printf(jp, "\t} while (%s);\n", POP_STR()); - todo = -2; - break; - - case JSOP_OR: - case JSOP_ORX: - xval = "||"; - - do_logical_connective: - /* Top of stack is the first clause in a disjunction (||). */ - lval = JS_strdup(cx, POP_STR()); - if (!lval) - return NULL; - done = pc + GetJumpOffset(pc, pc); - pc += len; - len = PTRDIFF(done, pc, jsbytecode); - DECOMPILE_CODE(pc, len); - rval = POP_STR(); - if (jp->pretty && - jp->indent + 4 + strlen(lval) + 4 + strlen(rval) > 75) { - rval = JS_strdup(cx, rval); - if (!rval) { - tail = -1; - } else { - todo = Sprint(&ss->sprinter, "%s %s\n", lval, xval); - tail = Sprint(&ss->sprinter, "%*s%s", - jp->indent + 4, "", rval); - JS_free(cx, (char *)rval); - } - if (tail < 0) - todo = -1; - } else { - todo = Sprint(&ss->sprinter, "%s %s %s", lval, xval, rval); - } - JS_free(cx, (char *)lval); - break; - - case JSOP_AND: - case JSOP_ANDX: - xval = "&&"; - goto do_logical_connective; - - case JSOP_FORARG: - atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc)); - LOCAL_ASSERT(atom); - goto do_fornameinloop; - - case JSOP_FORVAR: - atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); - LOCAL_ASSERT(atom); - goto do_fornameinloop; - - case JSOP_FORNAME: - atom = GET_ATOM(cx, jp->script, pc); - - do_fornameinloop: - lval = ""; - do_forlvalinloop: - sn = js_GetSrcNote(jp->script, pc); - xval = NULL; - goto do_forinloop; - - case JSOP_FORPROP: - xval = NULL; - atom = GET_ATOM(cx, jp->script, pc); - if (!ATOM_IS_IDENTIFIER(atom)) { - xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), - (jschar)'\''); - if (!xval) - return NULL; - atom = NULL; - } - lval = POP_STR(); - sn = NULL; - - do_forinloop: - pc += oplen; - LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX); - oplen = js_CodeSpec[*pc].length; - len = GetJumpOffset(pc, pc); - sn2 = js_GetSrcNote(jp->script, pc); - tail = js_GetSrcNoteOffset(sn2, 0); - - do_forinhead: - if (!atom && xval) { - /* - * If xval is not a dummy empty string, we have to strdup - * it to save it from being clobbered by the first Sprint - * below. Standard dumb decompiler operating procedure! - */ - if (*xval == '\0') { - xval = NULL; - } else { - xval = JS_strdup(cx, xval); - if (!xval) - return NULL; - } - } - -#if JS_HAS_XML_SUPPORT - if (foreach) { - foreach = JS_FALSE; - todo = Sprint(&ss->sprinter, "for %s (%s%s", - js_each_str, VarPrefix(sn), lval); - } else -#endif - { - todo = Sprint(&ss->sprinter, "for (%s%s", - VarPrefix(sn), lval); - } - if (atom) { - if (*lval && SprintPut(&ss->sprinter, ".", 1) < 0) - return NULL; - xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!xval) - return NULL; - } else if (xval) { - LOCAL_ASSERT(*xval != '\0'); - ok = (Sprint(&ss->sprinter, - (js_CodeSpec[lastop].format & JOF_XMLNAME) - ? ".%s" - : "[%s]", - xval) - >= 0); - JS_free(cx, (char *)xval); - if (!ok) - return NULL; - } - if (todo < 0) - return NULL; - - lval = OFF2STR(&ss->sprinter, todo); - rval = GetStr(ss, ss->top-1); - RETRACT(&ss->sprinter, rval); - if (ss->inArrayInit) { - todo = Sprint(&ss->sprinter, " %s in %s)", lval, rval); - if (todo < 0) - return NULL; - ss->offsets[ss->top-1] = todo; - ss->sprinter.offset += PAREN_SLOP; - DECOMPILE_CODE(pc + oplen, tail - oplen); - } else { - js_printf(SET_MAYBE_BRACE(jp), "\t%s in %s) {\n", - lval, rval); - jp->indent += 4; - DECOMPILE_CODE(pc + oplen, tail - oplen); - jp->indent -= 4; - js_printf(jp, "\t}\n"); - } - todo = -2; - break; - - case JSOP_FORELEM: - pc++; - LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX); - len = js_CodeSpec[*pc].length; - - /* - * Arrange for the JSOP_ENUMELEM case to set tail for use by - * do_forinhead: code that uses on it to find the loop-closing - * jump (whatever its format, normal or extended), in order to - * bound the recursively decompiled loop body. - */ - sn = js_GetSrcNote(jp->script, pc); - LOCAL_ASSERT(!forelem_tail); - forelem_tail = pc + js_GetSrcNoteOffset(sn, 0); - - /* - * This gets a little wacky. Only the length of the for loop - * body PLUS the element-indexing expression is known here, so - * we pass the after-loop pc to the JSOP_ENUMELEM case, which - * is immediately below, to decompile that helper bytecode via - * the 'forelem_done' local. - * - * Since a for..in loop can't nest in the head of another for - * loop, we can use forelem_{tail,done} singletons to remember - * state from JSOP_FORELEM to JSOP_ENUMELEM, thence (via goto) - * to label do_forinhead. - */ - LOCAL_ASSERT(!forelem_done); - forelem_done = pc + GetJumpOffset(pc, pc); - - /* Our net stack balance after forelem;ifeq is +1. */ - todo = SprintCString(&ss->sprinter, forelem_cookie); - break; - - case JSOP_ENUMELEM: - case JSOP_ENUMCONSTELEM: - /* - * The stack has the object under the (top) index expression. - * The "rval" property id is underneath those two on the stack. - * The for loop body net and gross lengths can now be adjusted - * to account for the length of the indexing expression that - * came after JSOP_FORELEM and before JSOP_ENUMELEM. - */ - atom = NULL; - xval = POP_STR(); - op = JSOP_GETELEM; /* lval must have high precedence */ - lval = POP_STR(); - op = saveop; - rval = POP_STR(); - LOCAL_ASSERT(strcmp(rval, forelem_cookie) == 0); - LOCAL_ASSERT(forelem_tail > pc); - tail = forelem_tail - pc; - forelem_tail = NULL; - LOCAL_ASSERT(forelem_done > pc); - len = forelem_done - pc; - forelem_done = NULL; - goto do_forinhead; - -#if JS_HAS_GETTER_SETTER - case JSOP_GETTER: - case JSOP_SETTER: - todo = -2; - break; -#endif - - case JSOP_DUP2: - rval = GetStr(ss, ss->top-2); - todo = SprintCString(&ss->sprinter, rval); - if (todo < 0 || !PushOff(ss, todo, ss->opcodes[ss->top-2])) - return NULL; - /* FALL THROUGH */ - - case JSOP_DUP: -#if JS_HAS_DESTRUCTURING - sn = js_GetSrcNote(jp->script, pc); - if (sn) { - LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCT); - pc = DecompileDestructuring(ss, pc, endpc); - if (!pc) - return NULL; - len = 0; - lval = POP_STR(); - op = saveop = JSOP_ENUMELEM; - rval = POP_STR(); - - if (strcmp(rval, forelem_cookie) == 0) { - LOCAL_ASSERT(forelem_tail > pc); - tail = forelem_tail - pc; - forelem_tail = NULL; - LOCAL_ASSERT(forelem_done > pc); - len = forelem_done - pc; - forelem_done = NULL; - xval = NULL; - atom = NULL; - - /* - * Null sn if this is a 'for (var [k, v] = i in o)' - * loop, because 'var [k, v = i;' has already been - * hoisted. - */ - if (js_GetSrcNoteOffset(sn, 0) == SRC_DECL_VAR) - sn = NULL; - goto do_forinhead; - } - - todo = Sprint(&ss->sprinter, "%s%s = %s", - VarPrefix(sn), lval, rval); - break; - } -#endif - - rval = GetStr(ss, ss->top-1); - saveop = ss->opcodes[ss->top-1]; - todo = SprintCString(&ss->sprinter, rval); - break; - - case JSOP_SETARG: - atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc)); - LOCAL_ASSERT(atom); - goto do_setname; - - case JSOP_SETVAR: - atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); - LOCAL_ASSERT(atom); - goto do_setname; - - case JSOP_SETCONST: - case JSOP_SETNAME: - case JSOP_SETGVAR: - atomIndex = GET_ATOM_INDEX(pc); - - do_JSOP_SETCONST: - atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex); - - do_setname: - lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!lval) - return NULL; - rval = POP_STR(); - if (op == JSOP_SETNAME) - (void) PopOff(ss, op); - - do_setlval: - sn = js_GetSrcNote(jp->script, pc - 1); - if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) { - todo = Sprint(&ss->sprinter, "%s %s= %s", - lval, - (lastop == JSOP_GETTER) - ? js_getter_str - : (lastop == JSOP_SETTER) - ? js_setter_str - : js_CodeSpec[lastop].token, - rval); - } else { - sn = js_GetSrcNote(jp->script, pc); - todo = Sprint(&ss->sprinter, "%s%s = %s", - VarPrefix(sn), lval, rval); - } - if (op == JSOP_SETLOCALPOP) { - if (!PushOff(ss, todo, saveop)) - return NULL; - rval = POP_STR(); - LOCAL_ASSERT(*rval != '\0'); - js_printf(jp, "\t%s;\n", rval); - todo = -2; - } - break; - - case JSOP_NEW: - case JSOP_CALL: - case JSOP_EVAL: -#if JS_HAS_LVALUE_RETURN - case JSOP_SETCALL: -#endif - op = JSOP_SETNAME; /* turn off most parens */ - argc = GET_ARGC(pc); - argv = (char **) - JS_malloc(cx, (size_t)(argc + 1) * sizeof *argv); - if (!argv) - return NULL; - - ok = JS_TRUE; - for (i = argc; i > 0; i--) { - argv[i] = JS_strdup(cx, POP_STR()); - if (!argv[i]) { - ok = JS_FALSE; - break; - } - } - - /* Skip the JSOP_PUSHOBJ-created empty string. */ - LOCAL_ASSERT(ss->top >= 2); - (void) PopOff(ss, op); - - op = saveop; - argv[0] = JS_strdup(cx, POP_STR()); - if (!argv[i]) - ok = JS_FALSE; - - lval = "(", rval = ")"; - if (op == JSOP_NEW) { - if (argc == 0) - lval = rval = ""; - todo = Sprint(&ss->sprinter, "%s %s%s", - js_new_str, argv[0], lval); - } else { - todo = Sprint(&ss->sprinter, ss_format, - argv[0], lval); - } - if (todo < 0) - ok = JS_FALSE; - - for (i = 1; i <= argc; i++) { - if (!argv[i] || - Sprint(&ss->sprinter, ss_format, - argv[i], (i < argc) ? ", " : "") < 0) { - ok = JS_FALSE; - break; - } - } - if (Sprint(&ss->sprinter, rval) < 0) - ok = JS_FALSE; - - for (i = 0; i <= argc; i++) { - if (argv[i]) - JS_free(cx, argv[i]); - } - JS_free(cx, argv); - if (!ok) - return NULL; -#if JS_HAS_LVALUE_RETURN - if (op == JSOP_SETCALL) { - if (!PushOff(ss, todo, op)) - return NULL; - todo = Sprint(&ss->sprinter, ""); - } -#endif - break; - - case JSOP_DELNAME: - atom = GET_ATOM(cx, jp->script, pc); - lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!lval) - return NULL; - RETRACT(&ss->sprinter, lval); - do_delete_lval: - todo = Sprint(&ss->sprinter, "%s %s", js_delete_str, lval); - break; - - case JSOP_DELPROP: - GET_ATOM_QUOTE_AND_FMT("%s %s[%s]", "%s %s.%s", rval); - lval = POP_STR(); - todo = Sprint(&ss->sprinter, fmt, js_delete_str, lval, rval); - break; - - case JSOP_DELELEM: - op = JSOP_NOP; /* turn off parens */ - xval = POP_STR(); - op = saveop; - lval = POP_STR(); - if (*xval == '\0') - goto do_delete_lval; - todo = Sprint(&ss->sprinter, - (js_CodeSpec[lastop].format & JOF_XMLNAME) - ? "%s %s.%s" - : "%s %s[%s]", - js_delete_str, lval, xval); - break; - -#if JS_HAS_XML_SUPPORT - case JSOP_DELDESC: - xval = POP_STR(); - lval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s %s..%s", - js_delete_str, lval, xval); - break; -#endif - - case JSOP_TYPEOFEXPR: - case JSOP_TYPEOF: - case JSOP_VOID: - rval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s %s", cs->name, rval); - break; - - case JSOP_INCARG: - case JSOP_DECARG: - atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc)); - LOCAL_ASSERT(atom); - goto do_incatom; - - case JSOP_INCVAR: - case JSOP_DECVAR: - atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); - LOCAL_ASSERT(atom); - goto do_incatom; - - case JSOP_INCNAME: - case JSOP_DECNAME: - case JSOP_INCGVAR: - case JSOP_DECGVAR: - atom = GET_ATOM(cx, jp->script, pc); - do_incatom: - lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!lval) - return NULL; - RETRACT(&ss->sprinter, lval); - do_inclval: - todo = Sprint(&ss->sprinter, ss_format, - js_incop_strs[!(cs->format & JOF_INC)], lval); - break; - - case JSOP_INCPROP: - case JSOP_DECPROP: - GET_ATOM_QUOTE_AND_FMT(preindex_format, predot_format, rval); - - /* - * Force precedence below the numeric literal opcodes, so that - * 42..foo or 10000..toString(16), e.g., decompile with parens - * around the left-hand side of dot. - */ - op = JSOP_GETPROP; - lval = POP_STR(); - todo = Sprint(&ss->sprinter, fmt, - js_incop_strs[!(cs->format & JOF_INC)], - lval, rval); - break; - - case JSOP_INCELEM: - case JSOP_DECELEM: - op = JSOP_NOP; /* turn off parens */ - xval = POP_STR(); - op = JSOP_GETELEM; - lval = POP_STR(); - if (*xval != '\0') { - todo = Sprint(&ss->sprinter, - (js_CodeSpec[lastop].format & JOF_XMLNAME) - ? predot_format - : preindex_format, - js_incop_strs[!(cs->format & JOF_INC)], - lval, xval); - } else { - todo = Sprint(&ss->sprinter, ss_format, - js_incop_strs[!(cs->format & JOF_INC)], lval); - } - break; - - case JSOP_ARGINC: - case JSOP_ARGDEC: - atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc)); - LOCAL_ASSERT(atom); - goto do_atominc; - - case JSOP_VARINC: - case JSOP_VARDEC: - atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); - LOCAL_ASSERT(atom); - goto do_atominc; - - case JSOP_NAMEINC: - case JSOP_NAMEDEC: - case JSOP_GVARINC: - case JSOP_GVARDEC: - atom = GET_ATOM(cx, jp->script, pc); - do_atominc: - lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!lval) - return NULL; - RETRACT(&ss->sprinter, lval); - do_lvalinc: - todo = Sprint(&ss->sprinter, ss_format, - lval, js_incop_strs[!(cs->format & JOF_INC)]); - break; - - case JSOP_PROPINC: - case JSOP_PROPDEC: - GET_ATOM_QUOTE_AND_FMT(postindex_format, postdot_format, rval); - - /* - * Force precedence below the numeric literal opcodes, so that - * 42..foo or 10000..toString(16), e.g., decompile with parens - * around the left-hand side of dot. - */ - op = JSOP_GETPROP; - lval = POP_STR(); - todo = Sprint(&ss->sprinter, fmt, lval, rval, - js_incop_strs[!(cs->format & JOF_INC)]); - break; - - case JSOP_ELEMINC: - case JSOP_ELEMDEC: - op = JSOP_NOP; /* turn off parens */ - xval = POP_STR(); - op = JSOP_GETELEM; - lval = POP_STR(); - if (*xval != '\0') { - todo = Sprint(&ss->sprinter, - (js_CodeSpec[lastop].format & JOF_XMLNAME) - ? postdot_format - : postindex_format, - lval, xval, - js_incop_strs[!(cs->format & JOF_INC)]); - } else { - todo = Sprint(&ss->sprinter, ss_format, - lval, js_incop_strs[!(cs->format & JOF_INC)]); - } - break; - - case JSOP_GETPROP2: - op = JSOP_GETPROP; - (void) PopOff(ss, lastop); - /* FALL THROUGH */ - - case JSOP_GETPROP: - case JSOP_GETXPROP: - atom = GET_ATOM(cx, jp->script, pc); - - do_getprop: - GET_QUOTE_AND_FMT(index_format, dot_format, rval); - - do_getprop_lval: - lval = POP_STR(); - todo = Sprint(&ss->sprinter, fmt, lval, rval); - break; - -#if JS_HAS_XML_SUPPORT - BEGIN_LITOPX_CASE(JSOP_GETMETHOD) - sn = js_GetSrcNote(jp->script, pc); - if (sn && SN_TYPE(sn) == SRC_PCBASE) - goto do_getprop; - GET_QUOTE_AND_FMT("%s.function::[%s]", "%s.function::%s", rval); - goto do_getprop_lval; - - BEGIN_LITOPX_CASE(JSOP_SETMETHOD) - sn = js_GetSrcNote(jp->script, pc); - if (sn && SN_TYPE(sn) == SRC_PCBASE) - goto do_setprop; - GET_QUOTE_AND_FMT("%s.function::[%s] %s= %s", - "%s.function::%s %s= %s", - xval); - goto do_setprop_rval; -#endif - - case JSOP_SETPROP: - atom = GET_ATOM(cx, jp->script, pc); - - do_setprop: - GET_QUOTE_AND_FMT("%s[%s] %s= %s", "%s.%s %s= %s", xval); - - do_setprop_rval: - rval = POP_STR(); - - /* - * Force precedence below the numeric literal opcodes, so that - * 42..foo or 10000..toString(16), e.g., decompile with parens - * around the left-hand side of dot. - */ - op = JSOP_GETPROP; - lval = POP_STR(); - sn = js_GetSrcNote(jp->script, pc - 1); - todo = Sprint(&ss->sprinter, fmt, lval, xval, - (sn && SN_TYPE(sn) == SRC_ASSIGNOP) - ? (lastop == JSOP_GETTER) - ? js_getter_str - : (lastop == JSOP_SETTER) - ? js_setter_str - : js_CodeSpec[lastop].token - : "", - rval); - break; - - case JSOP_GETELEM2: - op = JSOP_GETELEM; - (void) PopOff(ss, lastop); - /* FALL THROUGH */ - - case JSOP_GETELEM: - case JSOP_GETXELEM: - op = JSOP_NOP; /* turn off parens */ - xval = POP_STR(); - op = saveop; - lval = POP_STR(); - if (*xval == '\0') { - todo = Sprint(&ss->sprinter, "%s", lval); - } else { - todo = Sprint(&ss->sprinter, - (js_CodeSpec[lastop].format & JOF_XMLNAME) - ? dot_format - : index_format, - lval, xval); - } - break; - - case JSOP_SETELEM: - rval = POP_STR(); - op = JSOP_NOP; /* turn off parens */ - xval = POP_STR(); - cs = &js_CodeSpec[ss->opcodes[ss->top]]; - op = JSOP_GETELEM; /* lval must have high precedence */ - lval = POP_STR(); - op = saveop; - if (*xval == '\0') - goto do_setlval; - sn = js_GetSrcNote(jp->script, pc - 1); - todo = Sprint(&ss->sprinter, - (cs->format & JOF_XMLNAME) - ? "%s.%s %s= %s" - : "%s[%s] %s= %s", - lval, xval, - (sn && SN_TYPE(sn) == SRC_ASSIGNOP) - ? (lastop == JSOP_GETTER) - ? js_getter_str - : (lastop == JSOP_SETTER) - ? js_setter_str - : js_CodeSpec[lastop].token - : "", - rval); - break; - - case JSOP_ARGSUB: - i = (jsint) GET_ATOM_INDEX(pc); - todo = Sprint(&ss->sprinter, "%s[%d]", - js_arguments_str, (int) i); - break; - - case JSOP_ARGCNT: - todo = Sprint(&ss->sprinter, dot_format, - js_arguments_str, js_length_str); - break; - - case JSOP_GETARG: - i = GET_ARGNO(pc); - atom = GetSlotAtom(jp, js_GetArgument, i); -#if JS_HAS_DESTRUCTURING - if (!atom) { - todo = Sprint(&ss->sprinter, "%s[%d]", js_arguments_str, i); - break; - } -#else - LOCAL_ASSERT(atom); -#endif - goto do_name; - - case JSOP_GETVAR: - atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); - LOCAL_ASSERT(atom); - goto do_name; - - case JSOP_NAME: - case JSOP_GETGVAR: - atom = GET_ATOM(cx, jp->script, pc); - do_name: - lval = ""; - do_qname: - sn = js_GetSrcNote(jp->script, pc); - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!rval) - return NULL; - RETRACT(&ss->sprinter, rval); - todo = Sprint(&ss->sprinter, "%s%s%s", - VarPrefix(sn), lval, rval); - break; - - case JSOP_UINT16: - i = (jsint) GET_ATOM_INDEX(pc); - goto do_sprint_int; - - case JSOP_UINT24: - i = (jsint) GET_UINT24(pc); - do_sprint_int: - todo = Sprint(&ss->sprinter, "%u", (unsigned) i); - break; - - case JSOP_LITERAL: - atomIndex = GET_LITERAL_INDEX(pc); - goto do_JSOP_STRING; - - case JSOP_FINDNAME: - atomIndex = GET_LITERAL_INDEX(pc); - todo = Sprint(&ss->sprinter, ""); - if (todo < 0 || !PushOff(ss, todo, op)) - return NULL; - atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex); - goto do_name; - - case JSOP_LITOPX: - atomIndex = GET_LITERAL_INDEX(pc); - pc2 = pc + 1 + LITERAL_INDEX_LEN; - op = saveop = *pc2; - pc += len - (1 + ATOM_INDEX_LEN); - cs = &js_CodeSpec[op]; - len = cs->length; - switch (op) { - case JSOP_ANONFUNOBJ: goto do_JSOP_ANONFUNOBJ; - case JSOP_BINDNAME: goto do_JSOP_BINDNAME; - case JSOP_CLOSURE: goto do_JSOP_CLOSURE; -#if JS_HAS_EXPORT_IMPORT - case JSOP_EXPORTNAME: goto do_JSOP_EXPORTNAME; -#endif -#if JS_HAS_XML_SUPPORT - case JSOP_GETMETHOD: goto do_JSOP_GETMETHOD; - case JSOP_SETMETHOD: goto do_JSOP_SETMETHOD; -#endif - case JSOP_NAMEDFUNOBJ: goto do_JSOP_NAMEDFUNOBJ; - case JSOP_NUMBER: goto do_JSOP_NUMBER; - case JSOP_OBJECT: goto do_JSOP_OBJECT; -#if JS_HAS_XML_SUPPORT - case JSOP_QNAMECONST: goto do_JSOP_QNAMECONST; - case JSOP_QNAMEPART: goto do_JSOP_QNAMEPART; -#endif - case JSOP_REGEXP: goto do_JSOP_REGEXP; - case JSOP_SETCONST: goto do_JSOP_SETCONST; - case JSOP_STRING: goto do_JSOP_STRING; -#if JS_HAS_XML_SUPPORT - case JSOP_XMLCDATA: goto do_JSOP_XMLCDATA; - case JSOP_XMLCOMMENT: goto do_JSOP_XMLCOMMENT; - case JSOP_XMLOBJECT: goto do_JSOP_XMLOBJECT; - case JSOP_XMLPI: goto do_JSOP_XMLPI; -#endif - case JSOP_ENTERBLOCK: goto do_JSOP_ENTERBLOCK; - default: LOCAL_ASSERT(0); - } - /* NOTREACHED */ - break; - - BEGIN_LITOPX_CASE(JSOP_NUMBER) - val = ATOM_KEY(atom); - if (JSVAL_IS_INT(val)) { - long ival = (long)JSVAL_TO_INT(val); - todo = Sprint(&ss->sprinter, "%ld", ival); - } else { - char buf[DTOSTR_STANDARD_BUFFER_SIZE]; - char *numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, - 0, *JSVAL_TO_DOUBLE(val)); - if (!numStr) { - JS_ReportOutOfMemory(cx); - return NULL; - } - todo = Sprint(&ss->sprinter, numStr); - } - END_LITOPX_CASE - - BEGIN_LITOPX_CASE(JSOP_STRING) - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), - inXML ? DONT_ESCAPE : '"'); - if (!rval) - return NULL; - todo = STR2OFF(&ss->sprinter, rval); - END_LITOPX_CASE - - case JSOP_OBJECT: - case JSOP_REGEXP: - case JSOP_ANONFUNOBJ: - case JSOP_NAMEDFUNOBJ: - atomIndex = GET_ATOM_INDEX(pc); - - do_JSOP_OBJECT: - do_JSOP_REGEXP: - do_JSOP_ANONFUNOBJ: - do_JSOP_NAMEDFUNOBJ: - atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex); - if (op == JSOP_OBJECT || op == JSOP_REGEXP) { - if (!js_regexp_toString(cx, ATOM_TO_OBJECT(atom), 0, NULL, - &val)) { - return NULL; - } - } else { - if (!js_fun_toString(cx, ATOM_TO_OBJECT(atom), - JS_IN_GROUP_CONTEXT | - JS_DONT_PRETTY_PRINT, - 0, NULL, &val)) { - return NULL; - } - } - str = JSVAL_TO_STRING(val); - todo = SprintPut(&ss->sprinter, JS_GetStringBytes(str), - JSSTRING_LENGTH(str)); - break; - - case JSOP_TABLESWITCH: - case JSOP_TABLESWITCHX: - { - ptrdiff_t jmplen, off, off2; - jsint j, n, low, high; - TableEntry *table, pivot; - - sn = js_GetSrcNote(jp->script, pc); - LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); - len = js_GetSrcNoteOffset(sn, 0); - jmplen = (op == JSOP_TABLESWITCH) ? JUMP_OFFSET_LEN - : JUMPX_OFFSET_LEN; - pc2 = pc; - off = GetJumpOffset(pc, pc2); - pc2 += jmplen; - low = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - high = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - - n = high - low + 1; - if (n == 0) { - table = NULL; - j = 0; - } else { - table = (TableEntry *) - JS_malloc(cx, (size_t)n * sizeof *table); - if (!table) - return NULL; - for (i = j = 0; i < n; i++) { - table[j].label = NULL; - off2 = GetJumpOffset(pc, pc2); - if (off2) { - sn = js_GetSrcNote(jp->script, pc2); - if (sn) { - LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL); - table[j].label = - js_GetAtom(cx, &jp->script->atomMap, - (jsatomid) - js_GetSrcNoteOffset(sn, 0)); - } - table[j].key = INT_TO_JSVAL(low + i); - table[j].offset = off2; - table[j].order = j; - j++; - } - pc2 += jmplen; - } - js_HeapSort(table, (size_t) j, &pivot, sizeof(TableEntry), - CompareOffsets, NULL); - } - - ok = DecompileSwitch(ss, table, (uintN)j, pc, len, off, - JS_FALSE); - JS_free(cx, table); - if (!ok) - return NULL; - todo = -2; - break; - } - - case JSOP_LOOKUPSWITCH: - case JSOP_LOOKUPSWITCHX: - { - ptrdiff_t jmplen, off, off2; - jsatomid npairs, k; - TableEntry *table; - - sn = js_GetSrcNote(jp->script, pc); - LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); - len = js_GetSrcNoteOffset(sn, 0); - jmplen = (op == JSOP_LOOKUPSWITCH) ? JUMP_OFFSET_LEN - : JUMPX_OFFSET_LEN; - pc2 = pc; - off = GetJumpOffset(pc, pc2); - pc2 += jmplen; - npairs = GET_ATOM_INDEX(pc2); - pc2 += ATOM_INDEX_LEN; - - table = (TableEntry *) - JS_malloc(cx, (size_t)npairs * sizeof *table); - if (!table) - return NULL; - for (k = 0; k < npairs; k++) { - sn = js_GetSrcNote(jp->script, pc2); - if (sn) { - LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL); - table[k].label = - js_GetAtom(cx, &jp->script->atomMap, (jsatomid) - js_GetSrcNoteOffset(sn, 0)); - } else { - table[k].label = NULL; - } - atom = GET_ATOM(cx, jp->script, pc2); - pc2 += ATOM_INDEX_LEN; - off2 = GetJumpOffset(pc, pc2); - pc2 += jmplen; - table[k].key = ATOM_KEY(atom); - table[k].offset = off2; - } - - ok = DecompileSwitch(ss, table, (uintN)npairs, pc, len, off, - JS_FALSE); - JS_free(cx, table); - if (!ok) - return NULL; - todo = -2; - break; - } - - case JSOP_CONDSWITCH: - { - ptrdiff_t off, off2, caseOff; - jsint ncases; - TableEntry *table; - - sn = js_GetSrcNote(jp->script, pc); - LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); - len = js_GetSrcNoteOffset(sn, 0); - off = js_GetSrcNoteOffset(sn, 1); - - /* - * Count the cases using offsets from switch to first case, - * and case to case, stored in srcnote immediates. - */ - pc2 = pc; - off2 = off; - for (ncases = 0; off2 != 0; ncases++) { - pc2 += off2; - LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT || - *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX); - if (*pc2 == JSOP_DEFAULT || *pc2 == JSOP_DEFAULTX) { - /* End of cases, but count default as a case. */ - off2 = 0; - } else { - sn = js_GetSrcNote(jp->script, pc2); - LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA); - off2 = js_GetSrcNoteOffset(sn, 0); - } - } - - /* - * Allocate table and rescan the cases using their srcnotes, - * stashing each case's delta from switch top in table[i].key, - * and the distance to its statements in table[i].offset. - */ - table = (TableEntry *) - JS_malloc(cx, (size_t)ncases * sizeof *table); - if (!table) - return NULL; - pc2 = pc; - off2 = off; - for (i = 0; i < ncases; i++) { - pc2 += off2; - LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT || - *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX); - caseOff = pc2 - pc; - table[i].key = INT_TO_JSVAL((jsint) caseOff); - table[i].offset = caseOff + GetJumpOffset(pc2, pc2); - if (*pc2 == JSOP_CASE || *pc2 == JSOP_CASEX) { - sn = js_GetSrcNote(jp->script, pc2); - LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA); - off2 = js_GetSrcNoteOffset(sn, 0); - } - } - - /* - * Find offset of default code by fetching the default offset - * from the end of table. JSOP_CONDSWITCH always has a default - * case at the end. - */ - off = JSVAL_TO_INT(table[ncases-1].key); - pc2 = pc + off; - off += GetJumpOffset(pc2, pc2); - - ok = DecompileSwitch(ss, table, (uintN)ncases, pc, len, off, - JS_TRUE); - JS_free(cx, table); - if (!ok) - return NULL; - todo = -2; - break; - } - - case JSOP_CASE: - case JSOP_CASEX: - { - lval = POP_STR(); - if (!lval) - return NULL; - js_printf(jp, "\tcase %s:\n", lval); - todo = -2; - break; - } - - case JSOP_NEW_EQ: - case JSOP_NEW_NE: - rval = POP_STR(); - lval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s %c== %s", - lval, (op == JSOP_NEW_EQ) ? '=' : '!', rval); - break; - - BEGIN_LITOPX_CASE(JSOP_CLOSURE) - LOCAL_ASSERT(ATOM_IS_OBJECT(atom)); - todo = -2; - goto do_function; - END_LITOPX_CASE - -#if JS_HAS_EXPORT_IMPORT - case JSOP_EXPORTALL: - js_printf(jp, "\texport *;\n"); - todo = -2; - break; - - BEGIN_LITOPX_CASE(JSOP_EXPORTNAME) - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!rval) - return NULL; - RETRACT(&ss->sprinter, rval); - js_printf(jp, "\texport %s;\n", rval); - todo = -2; - END_LITOPX_CASE - - case JSOP_IMPORTALL: - lval = POP_STR(); - js_printf(jp, "\timport %s.*;\n", lval); - todo = -2; - break; - - case JSOP_IMPORTPROP: - do_importprop: - GET_ATOM_QUOTE_AND_FMT("\timport %s[%s];\n", - "\timport %s.%s;\n", - rval); - lval = POP_STR(); - js_printf(jp, fmt, lval, rval); - todo = -2; - break; - - case JSOP_IMPORTELEM: - xval = POP_STR(); - op = JSOP_GETELEM; - if (js_CodeSpec[lastop].format & JOF_XMLNAME) - goto do_importprop; - lval = POP_STR(); - js_printf(jp, "\timport %s[%s];\n", lval, xval); - todo = -2; - break; -#endif /* JS_HAS_EXPORT_IMPORT */ - - case JSOP_TRAP: - op = JS_GetTrapOpcode(cx, jp->script, pc); - if (op == JSOP_LIMIT) - return NULL; - saveop = op; - *pc = op; - cs = &js_CodeSpec[op]; - len = cs->length; - DECOMPILE_CODE(pc, len); - *pc = JSOP_TRAP; - todo = -2; - break; - - case JSOP_NEWINIT: - { - JSBool isArray; - - LOCAL_ASSERT(ss->top >= 2); - (void) PopOff(ss, op); - lval = POP_STR(); - isArray = (*lval == 'A'); - todo = ss->sprinter.offset; -#if JS_HAS_SHARP_VARS - op = (JSOp)pc[len]; - if (op == JSOP_DEFSHARP) { - pc += len; - cs = &js_CodeSpec[op]; - len = cs->length; - i = (jsint) GET_ATOM_INDEX(pc); - if (Sprint(&ss->sprinter, "#%u=", (unsigned) i) < 0) - return NULL; - } -#endif /* JS_HAS_SHARP_VARS */ - if (isArray) { - ++ss->inArrayInit; - if (SprintCString(&ss->sprinter, "[") < 0) - return NULL; - } else { - if (SprintCString(&ss->sprinter, "{") < 0) - return NULL; - } - break; - } - - case JSOP_ENDINIT: - op = JSOP_NOP; /* turn off parens */ - rval = POP_STR(); - sn = js_GetSrcNote(jp->script, pc); - - /* Skip any #n= prefix to find the opening bracket. */ - for (xval = rval; *xval != '[' && *xval != '{'; xval++) - continue; - if (*xval == '[') - --ss->inArrayInit; - todo = Sprint(&ss->sprinter, "%s%s%c", - rval, - (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "", - (*xval == '[') ? ']' : '}'); - break; - - case JSOP_INITPROP: - atom = GET_ATOM(cx, jp->script, pc); - xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), - (jschar) - (ATOM_IS_IDENTIFIER(atom) ? 0 : '\'')); - if (!xval) - return NULL; - rval = POP_STR(); - lval = POP_STR(); - do_initprop: -#ifdef OLD_GETTER_SETTER - todo = Sprint(&ss->sprinter, "%s%s%s%s%s:%s", - lval, - (lval[1] != '\0') ? ", " : "", - xval, - (lastop == JSOP_GETTER || lastop == JSOP_SETTER) - ? " " : "", - (lastop == JSOP_GETTER) ? js_getter_str : - (lastop == JSOP_SETTER) ? js_setter_str : - "", - rval); -#else - if (lastop == JSOP_GETTER || lastop == JSOP_SETTER) { - if (!atom || !ATOM_IS_STRING(atom) || - !ATOM_IS_IDENTIFIER(atom) || - ATOM_IS_KEYWORD(atom) || - ((ss->opcodes[ss->top+1] != JSOP_ANONFUNOBJ || - strncmp(rval, js_function_str, 8) != 0) && - ss->opcodes[ss->top+1] != JSOP_NAMEDFUNOBJ)) { - todo = Sprint(&ss->sprinter, "%s%s%s%s%s:%s", lval, - (lval[1] != '\0') ? ", " : "", xval, - (lastop == JSOP_GETTER || - lastop == JSOP_SETTER) - ? " " : "", - (lastop == JSOP_GETTER) ? js_getter_str : - (lastop == JSOP_SETTER) ? js_setter_str : - "", - rval); - } else { - rval += 8 + 1; - LOCAL_ASSERT(rval[strlen(rval)-1] == '}'); - todo = Sprint(&ss->sprinter, "%s%s%s %s%s", - lval, - (lval[1] != '\0') ? ", " : "", - (lastop == JSOP_GETTER) - ? js_get_str : js_set_str, - xval, - rval); - } - } else { - todo = Sprint(&ss->sprinter, "%s%s%s:%s", - lval, - (lval[1] != '\0') ? ", " : "", - xval, - rval); - } -#endif - break; - - case JSOP_INITELEM: - rval = POP_STR(); - xval = POP_STR(); - lval = POP_STR(); - sn = js_GetSrcNote(jp->script, pc); - if (sn && SN_TYPE(sn) == SRC_INITPROP) { - atom = NULL; - goto do_initprop; - } - todo = Sprint(&ss->sprinter, "%s%s%s", - lval, - (lval[1] != '\0' || *xval != '0') ? ", " : "", - rval); - break; - -#if JS_HAS_SHARP_VARS - case JSOP_DEFSHARP: - i = (jsint) GET_ATOM_INDEX(pc); - rval = POP_STR(); - todo = Sprint(&ss->sprinter, "#%u=%s", (unsigned) i, rval); - break; - - case JSOP_USESHARP: - i = (jsint) GET_ATOM_INDEX(pc); - todo = Sprint(&ss->sprinter, "#%u#", (unsigned) i); - break; -#endif /* JS_HAS_SHARP_VARS */ - -#if JS_HAS_DEBUGGER_KEYWORD - case JSOP_DEBUGGER: - js_printf(jp, "\tdebugger;\n"); - todo = -2; - break; -#endif /* JS_HAS_DEBUGGER_KEYWORD */ - -#if JS_HAS_XML_SUPPORT - case JSOP_STARTXML: - case JSOP_STARTXMLEXPR: - inXML = op == JSOP_STARTXML; - todo = -2; - break; - - case JSOP_DEFXMLNS: - rval = POP_STR(); - js_printf(jp, "\t%s %s %s = %s;\n", - js_default_str, js_xml_str, js_namespace_str, rval); - todo = -2; - break; - - case JSOP_ANYNAME: - if (pc[JSOP_ANYNAME_LENGTH] == JSOP_TOATTRNAME) { - len += JSOP_TOATTRNAME_LENGTH; - todo = SprintPut(&ss->sprinter, "@*", 2); - } else { - todo = SprintPut(&ss->sprinter, "*", 1); - } - break; - - BEGIN_LITOPX_CASE(JSOP_QNAMEPART) - if (pc[JSOP_QNAMEPART_LENGTH] == JSOP_TOATTRNAME) { - saveop = JSOP_TOATTRNAME; - len += JSOP_TOATTRNAME_LENGTH; - lval = "@"; - goto do_qname; - } - goto do_name; - END_LITOPX_CASE - - BEGIN_LITOPX_CASE(JSOP_QNAMECONST) - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!rval) - return NULL; - RETRACT(&ss->sprinter, rval); - lval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s::%s", lval, rval); - END_LITOPX_CASE - - case JSOP_QNAME: - rval = POP_STR(); - lval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s::[%s]", lval, rval); - break; - - case JSOP_TOATTRNAME: - op = JSOP_NOP; /* turn off parens */ - rval = POP_STR(); - todo = Sprint(&ss->sprinter, "@[%s]", rval); - break; - - case JSOP_TOATTRVAL: - todo = -2; - break; - - case JSOP_ADDATTRNAME: - rval = POP_STR(); - lval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s %s", lval, rval); - /* This gets reset by all XML tag expressions. */ - quoteAttr = JS_TRUE; - break; - - case JSOP_ADDATTRVAL: - rval = POP_STR(); - lval = POP_STR(); - if (quoteAttr) - todo = Sprint(&ss->sprinter, "%s=\"%s\"", lval, rval); - else - todo = Sprint(&ss->sprinter, "%s=%s", lval, rval); - break; - - case JSOP_BINDXMLNAME: - /* Leave the name stacked and push a dummy string. */ - todo = Sprint(&ss->sprinter, ""); - break; - - case JSOP_SETXMLNAME: - /* Pop the r.h.s., the dummy string, and the name. */ - rval = POP_STR(); - (void) PopOff(ss, op); - lval = POP_STR(); - goto do_setlval; - - case JSOP_XMLELTEXPR: - case JSOP_XMLTAGEXPR: - todo = Sprint(&ss->sprinter, "{%s}", POP_STR()); - inXML = JS_TRUE; - /* If we're an attribute value, we shouldn't quote this. */ - quoteAttr = JS_FALSE; - break; - - case JSOP_TOXMLLIST: - op = JSOP_NOP; /* turn off parens */ - todo = Sprint(&ss->sprinter, "<>%s", POP_STR()); - inXML = JS_FALSE; - break; - - case JSOP_FOREACH: - foreach = JS_TRUE; - todo = -2; - break; - - case JSOP_TOXML: - inXML = JS_FALSE; - /* FALL THROUGH */ - - case JSOP_XMLNAME: - case JSOP_FILTER: - /* Conversion and prefix ops do nothing in the decompiler. */ - todo = -2; - break; - - case JSOP_ENDFILTER: - rval = POP_STR(); - lval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s.(%s)", lval, rval); - break; - - case JSOP_DESCENDANTS: - rval = POP_STR(); - lval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s..%s", lval, rval); - break; - - BEGIN_LITOPX_CASE(JSOP_XMLOBJECT) - todo = Sprint(&ss->sprinter, "", - ATOM_TO_OBJECT(atom)); - END_LITOPX_CASE - - BEGIN_LITOPX_CASE(JSOP_XMLCDATA) - todo = SprintPut(&ss->sprinter, "sprinter, ATOM_TO_STRING(atom), 0)) - return NULL; - SprintPut(&ss->sprinter, "]]>", 3); - END_LITOPX_CASE - - BEGIN_LITOPX_CASE(JSOP_XMLCOMMENT) - todo = SprintPut(&ss->sprinter, "", 3); - END_LITOPX_CASE - - BEGIN_LITOPX_CASE(JSOP_XMLPI) - rval = JS_strdup(cx, POP_STR()); - if (!rval) - return NULL; - todo = SprintPut(&ss->sprinter, "sprinter, ATOM_TO_STRING(atom), 0) && - (*rval == '\0' || - (SprintPut(&ss->sprinter, " ", 1) >= 0 && - SprintCString(&ss->sprinter, rval))); - JS_free(cx, (char *)rval); - if (!ok) - return NULL; - SprintPut(&ss->sprinter, "?>", 2); - END_LITOPX_CASE - - case JSOP_GETFUNNS: - todo = SprintPut(&ss->sprinter, js_function_str, 8); - break; -#endif /* JS_HAS_XML_SUPPORT */ - - default: - todo = -2; - break; - -#undef BEGIN_LITOPX_CASE -#undef END_LITOPX_CASE - } - } - - if (todo < 0) { - /* -2 means "don't push", -1 means reported error. */ - if (todo == -1) - return NULL; - } else { - if (!PushOff(ss, todo, saveop)) - return NULL; - } - pc += len; - } - -/* - * Undefine local macros. - */ -#undef inXML -#undef DECOMPILE_CODE -#undef POP_STR -#undef LOCAL_ASSERT -#undef ATOM_IS_IDENTIFIER -#undef GET_QUOTE_AND_FMT -#undef GET_ATOM_QUOTE_AND_FMT - - return pc; -} - -static JSBool -InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth) -{ - size_t offsetsz, opcodesz; - void *space; - - INIT_SPRINTER(cx, &ss->sprinter, &cx->tempPool, PAREN_SLOP); - - /* Allocate the parallel (to avoid padding) offset and opcode stacks. */ - offsetsz = depth * sizeof(ptrdiff_t); - opcodesz = depth * sizeof(jsbytecode); - JS_ARENA_ALLOCATE(space, &cx->tempPool, offsetsz + opcodesz); - if (!space) - return JS_FALSE; - ss->offsets = (ptrdiff_t *) space; - ss->opcodes = (jsbytecode *) ((char *)space + offsetsz); - - ss->top = ss->inArrayInit = 0; - ss->printer = jp; - return JS_TRUE; -} - -JSBool -js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, - uintN pcdepth) -{ - uintN depth, i; - SprintStack ss; - JSContext *cx; - void *mark; - JSBool ok; - JSScript *oldscript; - char *last; - - depth = script->depth; - JS_ASSERT(pcdepth <= depth); - - /* Initialize a sprinter for use with the offset stack. */ - cx = jp->sprinter.context; - mark = JS_ARENA_MARK(&cx->tempPool); - ok = InitSprintStack(cx, &ss, jp, depth); - if (!ok) - goto out; - - /* - * If we are called from js_DecompileValueGenerator with a portion of - * script's bytecode that starts with a non-zero model stack depth given - * by pcdepth, attempt to initialize the missing string offsets in ss to - * |spindex| negative indexes from fp->sp for the activation fp in which - * the error arose. - * - * See js_DecompileValueGenerator for how its |spindex| parameter is used, - * and see also GetOff, which makes use of the ss.offsets[i] < -1 that are - * potentially stored below. - */ - ss.top = pcdepth; - if (pcdepth != 0) { - JSStackFrame *fp; - ptrdiff_t top; - - for (fp = cx->fp; fp && !fp->script; fp = fp->down) - continue; - top = fp ? fp->sp - fp->spbase : 0; - for (i = 0; i < pcdepth; i++) { - ss.offsets[i] = -1; - ss.opcodes[i] = JSOP_NOP; - } - if (fp && fp->pc == pc && (uintN)top == pcdepth) { - for (i = 0; i < pcdepth; i++) { - ptrdiff_t off; - jsbytecode *genpc; - - off = (intN)i - (intN)depth; - genpc = (jsbytecode *) fp->spbase[off]; - if (JS_UPTRDIFF(genpc, script->code) < script->length) { - ss.offsets[i] += (ptrdiff_t)i - top; - ss.opcodes[i] = *genpc; - } - } - } - } - - /* Call recursive subroutine to do the hard work. */ - oldscript = jp->script; - jp->script = script; - ok = Decompile(&ss, pc, len) != NULL; - jp->script = oldscript; - - /* If the given code didn't empty the stack, do it now. */ - if (ss.top) { - do { - last = OFF2STR(&ss.sprinter, PopOff(&ss, JSOP_POP)); - } while (ss.top > pcdepth); - js_printf(jp, "%s", last); - } - -out: - /* Free all temporary stuff allocated under this call. */ - JS_ARENA_RELEASE(&cx->tempPool, mark); - return ok; -} - -JSBool -js_DecompileScript(JSPrinter *jp, JSScript *script) -{ - return js_DecompileCode(jp, script, script->code, (uintN)script->length, 0); -} - -static const char native_code_str[] = "\t[native code]\n"; - -JSBool -js_DecompileFunctionBody(JSPrinter *jp, JSFunction *fun) -{ - JSScript *script; - JSScope *scope, *save; - JSBool ok; - - if (!FUN_INTERPRETED(fun)) { - js_printf(jp, native_code_str); - return JS_TRUE; - } - script = fun->u.i.script; - scope = fun->object ? OBJ_SCOPE(fun->object) : NULL; - save = jp->scope; - jp->scope = scope; - ok = js_DecompileCode(jp, script, script->code, (uintN)script->length, 0); - jp->scope = save; - return ok; -} - -JSBool -js_DecompileFunction(JSPrinter *jp, JSFunction *fun) -{ - JSContext *cx; - uintN i, nargs, indent; - void *mark; - JSAtom **params; - JSScope *scope, *oldscope; - JSScopeProperty *sprop; - jsbytecode *pc, *endpc; - ptrdiff_t len; - JSBool ok; - - /* - * If pretty, conform to ECMA-262 Edition 3, 15.3.4.2, by decompiling a - * FunctionDeclaration. Otherwise, check the JSFUN_LAMBDA flag and force - * an expression by parenthesizing. - */ - if (jp->pretty) { - js_printf(jp, "\t"); - } else { - if (!jp->grouped && (fun->flags & JSFUN_LAMBDA)) - js_puts(jp, "("); - } - if (JSFUN_GETTER_TEST(fun->flags)) - js_printf(jp, "%s ", js_getter_str); - else if (JSFUN_SETTER_TEST(fun->flags)) - js_printf(jp, "%s ", js_setter_str); - - js_printf(jp, "%s ", js_function_str); - if (fun->atom && !QuoteString(&jp->sprinter, ATOM_TO_STRING(fun->atom), 0)) - return JS_FALSE; - js_puts(jp, "("); - - if (FUN_INTERPRETED(fun) && fun->object) { - size_t paramsize; -#ifdef JS_HAS_DESTRUCTURING - SprintStack ss; - JSScript *oldscript; -#endif - - /* - * Print the parameters. - * - * This code is complicated by the need to handle duplicate parameter - * names, as required by ECMA (bah!). A duplicate parameter is stored - * as another node with the same id (the parameter name) but different - * shortid (the argument index) along the property tree ancestor line - * starting at SCOPE_LAST_PROP(scope). Only the last duplicate param - * is mapped by the scope's hash table. - */ - cx = jp->sprinter.context; - nargs = fun->nargs; - mark = JS_ARENA_MARK(&cx->tempPool); - paramsize = nargs * sizeof(JSAtom *); - JS_ARENA_ALLOCATE_CAST(params, JSAtom **, &cx->tempPool, paramsize); - if (!params) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - memset(params, 0, paramsize); - scope = OBJ_SCOPE(fun->object); - for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { - if (sprop->getter != js_GetArgument) - continue; - JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); - JS_ASSERT((uint16) sprop->shortid < nargs); - JS_ASSERT(JSID_IS_ATOM(sprop->id)); - params[(uint16) sprop->shortid] = JSID_TO_ATOM(sprop->id); - } - - pc = fun->u.i.script->main; - endpc = pc + fun->u.i.script->length; - ok = JS_TRUE; - -#ifdef JS_HAS_DESTRUCTURING - /* Skip JSOP_GENERATOR in case of destructuring parameters. */ - if (*pc == JSOP_GENERATOR) - pc += JSOP_GENERATOR_LENGTH; - - ss.printer = NULL; - oldscript = jp->script; - jp->script = fun->u.i.script; - oldscope = jp->scope; - jp->scope = scope; -#endif - - for (i = 0; i < nargs; i++) { - if (i > 0) - js_puts(jp, ", "); - -#if JS_HAS_DESTRUCTURING -#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, JS_FALSE) - - if (!params[i]) { - ptrdiff_t todo; - const char *lval; - - LOCAL_ASSERT(*pc == JSOP_GETARG); - pc += JSOP_GETARG_LENGTH; - LOCAL_ASSERT(*pc == JSOP_DUP); - if (!ss.printer) { - ok = InitSprintStack(cx, &ss, jp, fun->u.i.script->depth); - if (!ok) - break; - } - pc = DecompileDestructuring(&ss, pc, endpc); - if (!pc) { - ok = JS_FALSE; - break; - } - LOCAL_ASSERT(*pc == JSOP_POP); - pc += JSOP_POP_LENGTH; - lval = PopStr(&ss, JSOP_NOP); - todo = SprintCString(&jp->sprinter, lval); - if (todo < 0) { - ok = JS_FALSE; - break; - } - continue; - } - -#undef LOCAL_ASSERT -#endif - - if (!QuoteString(&jp->sprinter, ATOM_TO_STRING(params[i]), 0)) { - ok = JS_FALSE; - break; - } - } - -#ifdef JS_HAS_DESTRUCTURING - jp->script = oldscript; - jp->scope = oldscope; -#endif - JS_ARENA_RELEASE(&cx->tempPool, mark); - if (!ok) - return JS_FALSE; -#ifdef __GNUC__ - } else { - scope = NULL; - pc = NULL; -#endif - } - - js_printf(jp, ") {\n"); - indent = jp->indent; - jp->indent += 4; - if (FUN_INTERPRETED(fun) && fun->object) { - oldscope = jp->scope; - jp->scope = scope; - len = fun->u.i.script->code + fun->u.i.script->length - pc; - ok = js_DecompileCode(jp, fun->u.i.script, pc, (uintN)len, 0); - jp->scope = oldscope; - if (!ok) { - jp->indent = indent; - return JS_FALSE; - } - } else { - js_printf(jp, native_code_str); - } - jp->indent -= 4; - js_printf(jp, "\t}"); - - if (!jp->pretty) { - if (!jp->grouped && (fun->flags & JSFUN_LAMBDA)) - js_puts(jp, ")"); - } - return JS_TRUE; -} - -#undef LOCAL_ASSERT_RV - -JSString * -js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, - JSString *fallback) -{ - JSStackFrame *fp, *down; - jsbytecode *pc, *begin, *end; - jsval *sp, *spbase, *base, *limit; - intN depth, pcdepth; - JSScript *script; - JSOp op; - const JSCodeSpec *cs; - jssrcnote *sn; - ptrdiff_t len, oplen; - JSPrinter *jp; - JSString *name; - - for (fp = cx->fp; fp && !fp->script; fp = fp->down) - continue; - if (!fp) - goto do_fallback; - - /* Try to find sp's generating pc depth slots under it on the stack. */ - pc = fp->pc; - sp = fp->sp; - spbase = fp->spbase; - if ((uintN)(sp - spbase) > fp->script->depth) { - /* - * Preparing to make an internal invocation, using an argv stack - * segment pushed just above fp's operand stack space. Such an argv - * stack has no generating pc "basement", so we must fall back. - */ - goto do_fallback; - } - - if (spindex == JSDVG_SEARCH_STACK) { - if (!pc) { - /* - * Current frame is native: look under it for a scripted call - * in which a decompilable bytecode string that generated the - * value as an actual argument might exist. - */ - JS_ASSERT(!fp->script && !(fp->fun && FUN_INTERPRETED(fp->fun))); - down = fp->down; - if (!down) - goto do_fallback; - script = down->script; - spbase = down->spbase; - base = fp->argv; - limit = base + fp->argc; - } else { - /* - * This should be a script activation, either a top-level - * script or a scripted function. But be paranoid about calls - * to js_DecompileValueGenerator from code that hasn't fully - * initialized a (default-all-zeroes) frame. - */ - script = fp->script; - spbase = base = fp->spbase; - limit = fp->sp; - } - - /* - * Pure paranoia about default-zeroed frames being active while - * js_DecompileValueGenerator is called. It can't hurt much now; - * error reporting performance is not an issue. - */ - if (!script || !base || !limit) - goto do_fallback; - - /* - * Try to find operand-generating pc depth slots below sp. - * - * In the native case, we know the arguments have generating pc's - * under them, on account of fp->down->script being non-null: all - * compiled scripts get depth slots for generating pc's allocated - * upon activation, at the top of js_Interpret. - * - * In the script or scripted function case, the same reasoning - * applies to fp rather than to fp->down. - * - * We search from limit to base to find the most recently calculated - * value matching v under assumption that it is it that caused - * exception, see bug 328664. - */ - for (sp = limit;;) { - if (sp <= base) - goto do_fallback; - --sp; - if (*sp == v) { - depth = (intN)script->depth; - sp -= depth; - pc = (jsbytecode *) *sp; - break; - } - } - } else { - /* - * At this point, pc may or may not be null, i.e., we could be in - * a script activation, or we could be in a native frame that was - * called by another native function. Check pc and script. - */ - if (!pc) - goto do_fallback; - script = fp->script; - if (!script) - goto do_fallback; - - if (spindex != JSDVG_IGNORE_STACK) { - JS_ASSERT(spindex < 0); - depth = (intN)script->depth; -#if !JS_HAS_NO_SUCH_METHOD - JS_ASSERT(-depth <= spindex); -#endif - spindex -= depth; - - base = (jsval *) cx->stackPool.current->base; - limit = (jsval *) cx->stackPool.current->avail; - sp = fp->sp + spindex; - if (JS_UPTRDIFF(sp, base) < JS_UPTRDIFF(limit, base)) - pc = (jsbytecode *) *sp; - } - } - - /* - * Again, be paranoid, this time about possibly loading an invalid pc - * from fp->sp[-(1+depth)]. - */ - if (JS_UPTRDIFF(pc, script->code) >= (jsuword)script->length) { - pc = fp->pc; - if (!pc) - goto do_fallback; - } - op = (JSOp) *pc; - if (op == JSOP_TRAP) - op = JS_GetTrapOpcode(cx, script, pc); - - /* None of these stack-writing ops generates novel values. */ - JS_ASSERT(op != JSOP_CASE && op != JSOP_CASEX && - op != JSOP_DUP && op != JSOP_DUP2 && - op != JSOP_SWAP); - - /* - * |this| could convert to a very long object initialiser, so cite it by - * its keyword name instead. - */ - if (op == JSOP_THIS) - return JS_NewStringCopyZ(cx, js_this_str); - - /* - * JSOP_BINDNAME is special: it generates a value, the base object of a - * reference. But if it is the generating op for a diagnostic produced by - * js_DecompileValueGenerator, the name being bound is irrelevant. Just - * fall back to the base object. - */ - if (op == JSOP_BINDNAME) - goto do_fallback; - - /* NAME ops are self-contained, others require left or right context. */ - cs = &js_CodeSpec[op]; - begin = pc; - end = pc + cs->length; - if ((cs->format & JOF_MODEMASK) != JOF_NAME) { - JSSrcNoteType noteType; - - sn = js_GetSrcNote(script, pc); - if (!sn) - goto do_fallback; - noteType = SN_TYPE(sn); - if (noteType == SRC_PCBASE) { - begin -= js_GetSrcNoteOffset(sn, 0); - } else if (noteType == SRC_PCDELTA) { - end = begin + js_GetSrcNoteOffset(sn, 0); - begin += cs->length; - } else { - goto do_fallback; - } - } - len = PTRDIFF(end, begin, jsbytecode); - if (len <= 0) - goto do_fallback; - - /* - * Walk forward from script->main and compute starting stack depth. - * FIXME: Code to compute oplen copied from js_Disassemble1 and reduced. - * FIXME: Optimize to use last empty-stack sequence point. - */ - pcdepth = 0; - for (pc = script->main; pc < begin; pc += oplen) { - jsbytecode *pc2; - uint32 type; - intN nuses, ndefs; - - /* Let pc2 be non-null only for JSOP_LITOPX. */ - pc2 = NULL; - op = (JSOp) *pc; - if (op == JSOP_TRAP) - op = JS_GetTrapOpcode(cx, script, pc); - cs = &js_CodeSpec[op]; - oplen = cs->length; - - if (op == JSOP_SETSP) { - pcdepth = GET_UINT16(pc); - continue; - } - - /* - * A (C ? T : E) expression requires skipping either T (if begin is in - * E) or both T and E (if begin is after the whole expression) before - * adjusting pcdepth based on the JSOP_IFEQ or JSOP_IFEQX at pc that - * tests condition C. We know that the stack depth can't change from - * what it was with C on top of stack. - */ - sn = js_GetSrcNote(script, pc); - if (sn && SN_TYPE(sn) == SRC_COND) { - ptrdiff_t jmpoff, jmplen; - - jmpoff = js_GetSrcNoteOffset(sn, 0); - if (pc + jmpoff < begin) { - pc += jmpoff; - op = *pc; - JS_ASSERT(op == JSOP_GOTO || op == JSOP_GOTOX); - cs = &js_CodeSpec[op]; - oplen = cs->length; - jmplen = GetJumpOffset(pc, pc); - if (pc + jmplen < begin) { - oplen = (uintN) jmplen; - continue; - } - - /* - * Ok, begin lies in E. Manually pop C off the model stack, - * since we have moved beyond the IFEQ now. - */ - --pcdepth; - } - } - - type = cs->format & JOF_TYPEMASK; - switch (type) { - case JOF_TABLESWITCH: - case JOF_TABLESWITCHX: - { - jsint jmplen, i, low, high; - - jmplen = (type == JOF_TABLESWITCH) ? JUMP_OFFSET_LEN - : JUMPX_OFFSET_LEN; - pc2 = pc; - pc2 += jmplen; - low = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - high = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - for (i = low; i <= high; i++) - pc2 += jmplen; - oplen = 1 + pc2 - pc; - break; - } - - case JOF_LOOKUPSWITCH: - case JOF_LOOKUPSWITCHX: - { - jsint jmplen; - jsbytecode *pc2; - jsatomid npairs; - - jmplen = (type == JOF_LOOKUPSWITCH) ? JUMP_OFFSET_LEN - : JUMPX_OFFSET_LEN; - pc2 = pc; - pc2 += jmplen; - npairs = GET_ATOM_INDEX(pc2); - pc2 += ATOM_INDEX_LEN; - while (npairs) { - pc2 += ATOM_INDEX_LEN; - pc2 += jmplen; - npairs--; - } - oplen = 1 + pc2 - pc; - break; - } - - case JOF_LITOPX: - pc2 = pc + 1 + LITERAL_INDEX_LEN; - op = *pc2; - cs = &js_CodeSpec[op]; - JS_ASSERT(cs->length > ATOM_INDEX_LEN); - oplen += cs->length - (1 + ATOM_INDEX_LEN); - break; - - default:; - } - - if (sn && SN_TYPE(sn) == SRC_HIDDEN) - continue; - - nuses = cs->nuses; - if (nuses < 0) { - /* Call opcode pushes [callee, this, argv...]. */ - nuses = 2 + GET_ARGC(pc); - } else if (op == JSOP_RETSUB) { - /* Pop [exception or hole, retsub pc-index]. */ - JS_ASSERT(nuses == 0); - nuses = 2; - } else if (op == JSOP_LEAVEBLOCK || op == JSOP_LEAVEBLOCKEXPR) { - JS_ASSERT(nuses == 0); - nuses = GET_UINT16(pc); - } - pcdepth -= nuses; - JS_ASSERT(pcdepth >= 0); - - ndefs = cs->ndefs; - if (op == JSOP_FINALLY) { - /* Push [exception or hole, retsub pc-index]. */ - JS_ASSERT(ndefs == 0); - ndefs = 2; - } else if (op == JSOP_ENTERBLOCK) { - jsatomid atomIndex; - JSAtom *atom; - JSObject *obj; - - JS_ASSERT(ndefs == 0); - atomIndex = pc2 ? GET_LITERAL_INDEX(pc) : GET_ATOM_INDEX(pc); - atom = js_GetAtom(cx, &script->atomMap, atomIndex); - obj = ATOM_TO_OBJECT(atom); - JS_ASSERT(OBJ_BLOCK_DEPTH(cx, obj) == pcdepth); - ndefs = OBJ_BLOCK_COUNT(cx, obj); - } - pcdepth += ndefs; - } - - name = NULL; - jp = js_NewPrinter(cx, "js_DecompileValueGenerator", 0, JS_FALSE); - if (jp) { - if (fp->fun && fp->fun->object) { - JS_ASSERT(OBJ_IS_NATIVE(fp->fun->object)); - jp->scope = OBJ_SCOPE(fp->fun->object); - } - jp->dvgfence = end; - if (js_DecompileCode(jp, script, begin, (uintN)len, (uintN)pcdepth)) - name = js_GetPrinterOutput(jp); - js_DestroyPrinter(jp); - } - return name; - - do_fallback: - return fallback ? fallback : js_ValueToSource(cx, v); -} diff --git a/spidermonkey/src/jsopcode.h b/spidermonkey/src/jsopcode.h deleted file mode 100644 index 3f7e1de..0000000 --- a/spidermonkey/src/jsopcode.h +++ /dev/null @@ -1,318 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsopcode_h___ -#define jsopcode_h___ -/* - * JS bytecode definitions. - */ -#include -#include "jsprvtd.h" -#include "jspubtd.h" -#include "jsutil.h" - -JS_BEGIN_EXTERN_C - -/* - * JS operation bytecodes. - */ -typedef enum JSOp { -#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ - op = val, -#include "jsopcode.tbl" -#undef OPDEF - JSOP_LIMIT -} JSOp; - -typedef enum JSOpLength { -#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ - op##_LENGTH = length, -#include "jsopcode.tbl" -#undef OPDEF - JSOP_LIMIT_LENGTH -} JSOpLength; - -/* - * JS bytecode formats. - */ -#define JOF_BYTE 0 /* single bytecode, no immediates */ -#define JOF_JUMP 1 /* signed 16-bit jump offset immediate */ -#define JOF_CONST 2 /* unsigned 16-bit constant pool index */ -#define JOF_UINT16 3 /* unsigned 16-bit immediate operand */ -#define JOF_TABLESWITCH 4 /* table switch */ -#define JOF_LOOKUPSWITCH 5 /* lookup switch */ -#define JOF_QARG 6 /* quickened get/set function argument ops */ -#define JOF_QVAR 7 /* quickened get/set local variable ops */ -#define JOF_INDEXCONST 8 /* uint16 slot index + constant pool index */ -#define JOF_JUMPX 9 /* signed 32-bit jump offset immediate */ -#define JOF_TABLESWITCHX 10 /* extended (32-bit offset) table switch */ -#define JOF_LOOKUPSWITCHX 11 /* extended (32-bit offset) lookup switch */ -#define JOF_UINT24 12 /* extended unsigned 24-bit literal (index) */ -#define JOF_LITOPX 13 /* JOF_UINT24 followed by op being extended, - where op if JOF_CONST has no unsigned 16- - bit immediate operand */ -#define JOF_LOCAL 14 /* block-local operand stack variable */ -#define JOF_TYPEMASK 0x000f /* mask for above immediate types */ -#define JOF_NAME 0x0010 /* name operation */ -#define JOF_PROP 0x0020 /* obj.prop operation */ -#define JOF_ELEM 0x0030 /* obj[index] operation */ -#define JOF_MODEMASK 0x0030 /* mask for above addressing modes */ -#define JOF_SET 0x0040 /* set (i.e., assignment) operation */ -#define JOF_DEL 0x0080 /* delete operation */ -#define JOF_DEC 0x0100 /* decrement (--, not ++) opcode */ -#define JOF_INC 0x0200 /* increment (++, not --) opcode */ -#define JOF_INCDEC 0x0300 /* increment or decrement opcode */ -#define JOF_POST 0x0400 /* postorder increment or decrement */ -#define JOF_IMPORT 0x0800 /* import property op */ -#define JOF_FOR 0x1000 /* for-in property op */ -#define JOF_ASSIGNING JOF_SET /* hint for JSClass.resolve, used for ops - that do simplex assignment */ -#define JOF_DETECTING 0x2000 /* object detection flag for JSNewResolveOp */ -#define JOF_BACKPATCH 0x4000 /* backpatch placeholder during codegen */ -#define JOF_LEFTASSOC 0x8000 /* left-associative operator */ -#define JOF_DECLARING 0x10000 /* var, const, or function declaration op */ -#define JOF_XMLNAME 0x20000 /* XML name: *, a::b, @a, @a::b, etc. */ - -#define JOF_TYPE_IS_EXTENDED_JUMP(t) \ - ((unsigned)((t) - JOF_JUMPX) <= (unsigned)(JOF_LOOKUPSWITCHX - JOF_JUMPX)) - -/* - * Immediate operand getters, setters, and bounds. - */ - -/* Short (2-byte signed offset) relative jump macros. */ -#define JUMP_OFFSET_LEN 2 -#define JUMP_OFFSET_HI(off) ((jsbytecode)((off) >> 8)) -#define JUMP_OFFSET_LO(off) ((jsbytecode)(off)) -#define GET_JUMP_OFFSET(pc) ((int16)(((pc)[1] << 8) | (pc)[2])) -#define SET_JUMP_OFFSET(pc,off) ((pc)[1] = JUMP_OFFSET_HI(off), \ - (pc)[2] = JUMP_OFFSET_LO(off)) -#define JUMP_OFFSET_MIN ((int16)0x8000) -#define JUMP_OFFSET_MAX ((int16)0x7fff) - -/* - * When a short jump won't hold a relative offset, its 2-byte immediate offset - * operand is an unsigned index of a span-dependency record, maintained until - * code generation finishes -- after which some (but we hope not nearly all) - * span-dependent jumps must be extended (see OptimizeSpanDeps in jsemit.c). - * - * If the span-dependency record index overflows SPANDEP_INDEX_MAX, the jump - * offset will contain SPANDEP_INDEX_HUGE, indicating that the record must be - * found (via binary search) by its "before span-dependency optimization" pc - * offset (from script main entry point). - */ -#define GET_SPANDEP_INDEX(pc) ((uint16)(((pc)[1] << 8) | (pc)[2])) -#define SET_SPANDEP_INDEX(pc,i) ((pc)[1] = JUMP_OFFSET_HI(i), \ - (pc)[2] = JUMP_OFFSET_LO(i)) -#define SPANDEP_INDEX_MAX ((uint16)0xfffe) -#define SPANDEP_INDEX_HUGE ((uint16)0xffff) - -/* Ultimately, if short jumps won't do, emit long (4-byte signed) offsets. */ -#define JUMPX_OFFSET_LEN 4 -#define JUMPX_OFFSET_B3(off) ((jsbytecode)((off) >> 24)) -#define JUMPX_OFFSET_B2(off) ((jsbytecode)((off) >> 16)) -#define JUMPX_OFFSET_B1(off) ((jsbytecode)((off) >> 8)) -#define JUMPX_OFFSET_B0(off) ((jsbytecode)(off)) -#define GET_JUMPX_OFFSET(pc) ((int32)(((pc)[1] << 24) | ((pc)[2] << 16) \ - | ((pc)[3] << 8) | (pc)[4])) -#define SET_JUMPX_OFFSET(pc,off)((pc)[1] = JUMPX_OFFSET_B3(off), \ - (pc)[2] = JUMPX_OFFSET_B2(off), \ - (pc)[3] = JUMPX_OFFSET_B1(off), \ - (pc)[4] = JUMPX_OFFSET_B0(off)) -#define JUMPX_OFFSET_MIN ((int32)0x80000000) -#define JUMPX_OFFSET_MAX ((int32)0x7fffffff) - -/* - * A literal is indexed by a per-script atom map. Most scripts have relatively - * few literals, so the standard JOF_CONST format specifies a fixed 16 bits of - * immediate operand index. A script with more than 64K literals must push all - * high-indexed literals on the stack using JSOP_LITERAL, then use JOF_ELEM ops - * instead of JOF_PROP, etc. - */ -#define ATOM_INDEX_LEN 2 -#define ATOM_INDEX_HI(i) ((jsbytecode)((i) >> 8)) -#define ATOM_INDEX_LO(i) ((jsbytecode)(i)) -#define GET_ATOM_INDEX(pc) ((jsatomid)(((pc)[1] << 8) | (pc)[2])) -#define SET_ATOM_INDEX(pc,i) ((pc)[1] = ATOM_INDEX_HI(i), \ - (pc)[2] = ATOM_INDEX_LO(i)) -#define GET_ATOM(cx,script,pc) js_GetAtom((cx), &(script)->atomMap, \ - GET_ATOM_INDEX(pc)) - -/* A full atom index for JSOP_UINT24 uses 24 bits of immediate operand. */ -#define UINT24_HI(i) ((jsbytecode)((i) >> 16)) -#define UINT24_MID(i) ((jsbytecode)((i) >> 8)) -#define UINT24_LO(i) ((jsbytecode)(i)) -#define GET_UINT24(pc) ((jsatomid)(((pc)[1] << 16) | \ - ((pc)[2] << 8) | \ - (pc)[3])) -#define SET_UINT24(pc,i) ((pc)[1] = UINT24_HI(i), \ - (pc)[2] = UINT24_MID(i), \ - (pc)[3] = UINT24_LO(i)) - -/* Same format for JSOP_LITERAL, etc., but future-proof with different names. */ -#define LITERAL_INDEX_LEN 3 -#define LITERAL_INDEX_HI(i) UINT24_HI(i) -#define LITERAL_INDEX_MID(i) UINT24_MID(i) -#define LITERAL_INDEX_LO(i) UINT24_LO(i) -#define GET_LITERAL_INDEX(pc) GET_UINT24(pc) -#define SET_LITERAL_INDEX(pc,i) SET_UINT24(pc,i) - -/* Atom index limit is determined by SN_3BYTE_OFFSET_FLAG, see jsemit.h. */ -#define ATOM_INDEX_LIMIT_LOG2 23 -#define ATOM_INDEX_LIMIT ((uint32)1 << ATOM_INDEX_LIMIT_LOG2) - -JS_STATIC_ASSERT(sizeof(jsatomid) * JS_BITS_PER_BYTE >= - ATOM_INDEX_LIMIT_LOG2 + 1); - -/* Common uint16 immediate format helpers. */ -#define UINT16_HI(i) ((jsbytecode)((i) >> 8)) -#define UINT16_LO(i) ((jsbytecode)(i)) -#define GET_UINT16(pc) ((uintN)(((pc)[1] << 8) | (pc)[2])) -#define SET_UINT16(pc,i) ((pc)[1] = UINT16_HI(i), (pc)[2] = UINT16_LO(i)) -#define UINT16_LIMIT ((uintN)1 << 16) - -/* Actual argument count operand format helpers. */ -#define ARGC_HI(argc) UINT16_HI(argc) -#define ARGC_LO(argc) UINT16_LO(argc) -#define GET_ARGC(pc) GET_UINT16(pc) -#define ARGC_LIMIT UINT16_LIMIT - -/* Synonyms for quick JOF_QARG and JOF_QVAR bytecodes. */ -#define GET_ARGNO(pc) GET_UINT16(pc) -#define SET_ARGNO(pc,argno) SET_UINT16(pc,argno) -#define ARGNO_LEN 2 -#define ARGNO_LIMIT UINT16_LIMIT - -#define GET_VARNO(pc) GET_UINT16(pc) -#define SET_VARNO(pc,varno) SET_UINT16(pc,varno) -#define VARNO_LEN 2 -#define VARNO_LIMIT UINT16_LIMIT - -struct JSCodeSpec { - const char *name; /* JS bytecode name */ - const char *token; /* JS source literal or null */ - int8 length; /* length including opcode byte */ - int8 nuses; /* arity, -1 if variadic */ - int8 ndefs; /* number of stack results */ - uint8 prec; /* operator precedence */ - uint32 format; /* immediate operand format */ -}; - -extern const JSCodeSpec js_CodeSpec[]; -extern uintN js_NumCodeSpecs; -extern const jschar js_EscapeMap[]; - -/* - * Return a GC'ed string containing the chars in str, with any non-printing - * chars or quotes (' or " as specified by the quote argument) escaped, and - * with the quote character at the beginning and end of the result string. - */ -extern JSString * -js_QuoteString(JSContext *cx, JSString *str, jschar quote); - -/* - * JSPrinter operations, for printf style message formatting. The return - * value from js_GetPrinterOutput() is the printer's cumulative output, in - * a GC'ed string. - */ -extern JSPrinter * -js_NewPrinter(JSContext *cx, const char *name, uintN indent, JSBool pretty); - -extern void -js_DestroyPrinter(JSPrinter *jp); - -extern JSString * -js_GetPrinterOutput(JSPrinter *jp); - -extern int -js_printf(JSPrinter *jp, const char *format, ...); - -extern JSBool -js_puts(JSPrinter *jp, const char *s); - -#ifdef DEBUG -/* - * Disassemblers, for debugging only. - */ -#include - -extern JS_FRIEND_API(JSBool) -js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp); - -extern JS_FRIEND_API(uintN) -js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, - JSBool lines, FILE *fp); -#endif /* DEBUG */ - -/* - * Decompilers, for script, function, and expression pretty-printing. - */ -extern JSBool -js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, - uintN pcdepth); - -extern JSBool -js_DecompileScript(JSPrinter *jp, JSScript *script); - -extern JSBool -js_DecompileFunctionBody(JSPrinter *jp, JSFunction *fun); - -extern JSBool -js_DecompileFunction(JSPrinter *jp, JSFunction *fun); - -/* - * Find the source expression that resulted in v, and return a new string - * containing it. Fall back on v's string conversion (fallback) if we can't - * find the bytecode that generated and pushed v on the operand stack. - * - * Search the current stack frame if spindex is JSDVG_SEARCH_STACK. Don't - * look for v on the stack if spindex is JSDVG_IGNORE_STACK. Otherwise, - * spindex is the negative index of v, measured from cx->fp->sp, or from a - * lower frame's sp if cx->fp is native. - */ -extern JSString * -js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, - JSString *fallback); - -#define JSDVG_IGNORE_STACK 0 -#define JSDVG_SEARCH_STACK 1 - -JS_END_EXTERN_C - -#endif /* jsopcode_h___ */ diff --git a/spidermonkey/src/jsopcode.tbl b/spidermonkey/src/jsopcode.tbl deleted file mode 100644 index 4a4ca89..0000000 --- a/spidermonkey/src/jsopcode.tbl +++ /dev/null @@ -1,478 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=0 ft=C: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JavaScript operation bytecodes. If you need to allocate a bytecode, look - * for a name of the form JSOP_UNUSED* and claim it. Otherwise, always add at - * the end of the table. - * - * Includers must define an OPDEF macro of the following form: - * - * #define OPDEF(op,val,name,image,length,nuses,ndefs,prec,format) ... - * - * Selected arguments can be expanded in initializers. The op argument is - * expanded followed by comma in the JSOp enum (jsopcode.h), e.g. The value - * field must be dense for now, because jsopcode.c uses an OPDEF() expansion - * inside the js_CodeSpec[] initializer. - * - * Field Description - * op Bytecode name, which is the JSOp enumerator name - * value Bytecode value, which is the JSOp enumerator value - * name C string containing name for disassembler - * image C string containing "image" for pretty-printer, null if ugly - * length Number of bytes including any immediate operands - * nuses Number of stack slots consumed by bytecode, -1 if variadic - * ndefs Number of stack slots produced by bytecode - * prec Operator precedence, zero if not an operator - * format Bytecode plus immediate operand encoding format - * - * Precedence Operators Opcodes - * 1 let (x = y) z, w JSOP_LEAVEBLOCKEXPR - * 2 , JSOP_POP with SRC_PCDELTA note - * 3 =, +=, etc. JSOP_SETNAME, etc. (all JOF_ASSIGNING) - * 4 ?: JSOP_IFEQ, JSOP_IFEQX - * 5 || JSOP_OR, JSOP_ORX - * 6 && JSOP_AND, JSOP_ANDX - * 7 | JSOP_BITOR - * 8 ^ JSOP_BITXOR - * 9 & JSOP_BITAND - * 10 ==, !=, etc. JSOP_EQ, JSOP_NE, etc. - * 11 <, in, etc. JSOP_LT, JSOP_IN, etc. - * 12 <<, >>, >>> JSOP_LSH, JSOP_RSH, JSOP_URSH - * 13 +, -, etc. JSOP_ADD, JSOP_SUB, etc. - * 14 *, /, % JSOP_MUL, JSOP_DIV, JSOP_MOD - * 15 !, ~, etc. JSOP_NOT, JSOP_BITNOT, etc. - * 16 0, function(){} etc. JSOP_ZERO, JSOP_ANONFUNOBJ, etc. - * 17 delete, new JSOP_DEL*, JSOP_NEW - * 18 x.y, f(), etc. JSOP_GETPROP, JSOP_CALL, etc. - * 19 x, null, etc. JSOP_NAME, JSOP_NULL, etc. - * - * The push-numeric-constant operators, JSOP_ZERO, JSOP_NUMBER, etc., have - * lower precedence than the member operators emitted for the . operator, to - * cause the decompiler to parenthesize the . left operand, e.g. (0).foo. - * Otherwise the . could be taken as a decimal point. We use the same level - * 16 for function expressions too, to force parenthesization. - * - * This file is best viewed with 128 columns: -12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678 - */ - -/* legend: op val name image len use def prec format */ - -/* Longstanding JavaScript bytecodes. */ -OPDEF(JSOP_NOP, 0, "nop", NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_PUSH, 1, "push", NULL, 1, 0, 1, 0, JOF_BYTE) -OPDEF(JSOP_POPV, 2, "popv", NULL, 1, 1, 0, 2, JOF_BYTE) -OPDEF(JSOP_ENTERWITH, 3, "enterwith", NULL, 1, 1, 1, 0, JOF_BYTE) -OPDEF(JSOP_LEAVEWITH, 4, "leavewith", NULL, 1, 1, 0, 0, JOF_BYTE) -OPDEF(JSOP_RETURN, 5, "return", NULL, 1, 1, 0, 0, JOF_BYTE) -OPDEF(JSOP_GOTO, 6, "goto", NULL, 3, 0, 0, 0, JOF_JUMP) -OPDEF(JSOP_IFEQ, 7, "ifeq", NULL, 3, 1, 0, 4, JOF_JUMP|JOF_DETECTING) -OPDEF(JSOP_IFNE, 8, "ifne", NULL, 3, 1, 0, 0, JOF_JUMP) - -/* Get the arguments object for the current, lightweight function activation. */ -OPDEF(JSOP_ARGUMENTS, 9, js_arguments_str, js_arguments_str, 1, 0, 1, 18, JOF_BYTE) - -/* ECMA-compliant for-in loop with argument or local variable loop control. */ -OPDEF(JSOP_FORARG, 10, "forarg", NULL, 3, 0, 1, 19, JOF_QARG|JOF_NAME|JOF_FOR) -OPDEF(JSOP_FORVAR, 11, "forvar", NULL, 3, 0, 1, 19, JOF_QVAR|JOF_NAME|JOF_FOR) - -/* More longstanding bytecodes. */ -OPDEF(JSOP_DUP, 12, "dup", NULL, 1, 1, 2, 0, JOF_BYTE) -OPDEF(JSOP_DUP2, 13, "dup2", NULL, 1, 2, 4, 0, JOF_BYTE) -OPDEF(JSOP_SETCONST, 14, "setconst", NULL, 3, 1, 1, 3, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING) -OPDEF(JSOP_BITOR, 15, "bitor", "|", 1, 2, 1, 7, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_BITXOR, 16, "bitxor", "^", 1, 2, 1, 8, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_BITAND, 17, "bitand", "&", 1, 2, 1, 9, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_EQ, 18, "eq", "==", 1, 2, 1, 10, JOF_BYTE|JOF_LEFTASSOC|JOF_DETECTING) -OPDEF(JSOP_NE, 19, "ne", "!=", 1, 2, 1, 10, JOF_BYTE|JOF_LEFTASSOC|JOF_DETECTING) -OPDEF(JSOP_LT, 20, "lt", "<", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_LE, 21, "le", "<=", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_GT, 22, "gt", ">", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_GE, 23, "ge", ">=", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_LSH, 24, "lsh", "<<", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_RSH, 25, "rsh", ">>", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_URSH, 26, "ursh", ">>>", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_ADD, 27, "add", "+", 1, 2, 1, 13, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_SUB, 28, "sub", "-", 1, 2, 1, 13, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_MUL, 29, "mul", "*", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_DIV, 30, "div", "/", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_MOD, 31, "mod", "%", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_NOT, 32, "not", "!", 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING) -OPDEF(JSOP_BITNOT, 33, "bitnot", "~", 1, 1, 1, 15, JOF_BYTE) -OPDEF(JSOP_NEG, 34, "neg", "- ", 1, 1, 1, 15, JOF_BYTE) -OPDEF(JSOP_NEW, 35, js_new_str, NULL, 3, -1, 1, 17, JOF_UINT16) -OPDEF(JSOP_DELNAME, 36, "delname", NULL, 3, 0, 1, 17, JOF_CONST|JOF_NAME|JOF_DEL) -OPDEF(JSOP_DELPROP, 37, "delprop", NULL, 3, 1, 1, 17, JOF_CONST|JOF_PROP|JOF_DEL) -OPDEF(JSOP_DELELEM, 38, "delelem", NULL, 1, 2, 1, 17, JOF_BYTE |JOF_ELEM|JOF_DEL) -OPDEF(JSOP_TYPEOF, 39, js_typeof_str,NULL, 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING) -OPDEF(JSOP_VOID, 40, js_void_str, NULL, 1, 1, 1, 15, JOF_BYTE) -OPDEF(JSOP_INCNAME, 41, "incname", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_INC) -OPDEF(JSOP_INCPROP, 42, "incprop", NULL, 3, 1, 1, 15, JOF_CONST|JOF_PROP|JOF_INC) -OPDEF(JSOP_INCELEM, 43, "incelem", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_INC) -OPDEF(JSOP_DECNAME, 44, "decname", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_DEC) -OPDEF(JSOP_DECPROP, 45, "decprop", NULL, 3, 1, 1, 15, JOF_CONST|JOF_PROP|JOF_DEC) -OPDEF(JSOP_DECELEM, 46, "decelem", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_DEC) -OPDEF(JSOP_NAMEINC, 47, "nameinc", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_INC|JOF_POST) -OPDEF(JSOP_PROPINC, 48, "propinc", NULL, 3, 1, 1, 15, JOF_CONST|JOF_PROP|JOF_INC|JOF_POST) -OPDEF(JSOP_ELEMINC, 49, "eleminc", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_INC|JOF_POST) -OPDEF(JSOP_NAMEDEC, 50, "namedec", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_DEC|JOF_POST) -OPDEF(JSOP_PROPDEC, 51, "propdec", NULL, 3, 1, 1, 15, JOF_CONST|JOF_PROP|JOF_DEC|JOF_POST) -OPDEF(JSOP_ELEMDEC, 52, "elemdec", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_DEC|JOF_POST) -OPDEF(JSOP_GETPROP, 53, "getprop", NULL, 3, 1, 1, 18, JOF_CONST|JOF_PROP) -OPDEF(JSOP_SETPROP, 54, "setprop", NULL, 3, 2, 1, 3, JOF_CONST|JOF_PROP|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) -OPDEF(JSOP_GETELEM, 55, "getelem", NULL, 1, 2, 1, 18, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) -OPDEF(JSOP_SETELEM, 56, "setelem", NULL, 1, 3, 1, 3, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) -OPDEF(JSOP_PUSHOBJ, 57, "pushobj", NULL, 1, 0, 1, 0, JOF_BYTE) -OPDEF(JSOP_CALL, 58, "call", NULL, 3, -1, 1, 18, JOF_UINT16) -OPDEF(JSOP_NAME, 59, "name", NULL, 3, 0, 1, 19, JOF_CONST|JOF_NAME) -OPDEF(JSOP_NUMBER, 60, "number", NULL, 3, 0, 1, 16, JOF_CONST) -OPDEF(JSOP_STRING, 61, "string", NULL, 3, 0, 1, 19, JOF_CONST) -OPDEF(JSOP_ZERO, 62, "zero", "0", 1, 0, 1, 16, JOF_BYTE) -OPDEF(JSOP_ONE, 63, "one", "1", 1, 0, 1, 16, JOF_BYTE) -OPDEF(JSOP_NULL, 64, js_null_str, js_null_str, 1, 0, 1, 19, JOF_BYTE) -OPDEF(JSOP_THIS, 65, js_this_str, js_this_str, 1, 0, 1, 19, JOF_BYTE) -OPDEF(JSOP_FALSE, 66, js_false_str, js_false_str, 1, 0, 1, 19, JOF_BYTE) -OPDEF(JSOP_TRUE, 67, js_true_str, js_true_str, 1, 0, 1, 19, JOF_BYTE) -OPDEF(JSOP_OR, 68, "or", NULL, 3, 1, 0, 5, JOF_JUMP|JOF_DETECTING) -OPDEF(JSOP_AND, 69, "and", NULL, 3, 1, 0, 6, JOF_JUMP|JOF_DETECTING) - -/* The switch bytecodes have variable length. */ -OPDEF(JSOP_TABLESWITCH, 70, "tableswitch", NULL, -1, 1, 0, 0, JOF_TABLESWITCH|JOF_DETECTING) -OPDEF(JSOP_LOOKUPSWITCH, 71, "lookupswitch", NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCH|JOF_DETECTING) - -/* New, infallible/transitive identity ops. */ -OPDEF(JSOP_NEW_EQ, 72, "eq", NULL, 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING) -OPDEF(JSOP_NEW_NE, 73, "ne", NULL, 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING) - -/* Lexical closure constructor. */ -OPDEF(JSOP_CLOSURE, 74, "closure", NULL, 3, 0, 0, 0, JOF_CONST) - -/* Export and import ops. */ -OPDEF(JSOP_EXPORTALL, 75, "exportall", NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_EXPORTNAME,76, "exportname", NULL, 3, 0, 0, 0, JOF_CONST|JOF_NAME) -OPDEF(JSOP_IMPORTALL, 77, "importall", NULL, 1, 1, 0, 0, JOF_BYTE) -OPDEF(JSOP_IMPORTPROP,78, "importprop", NULL, 3, 1, 0, 0, JOF_CONST|JOF_PROP|JOF_IMPORT) -OPDEF(JSOP_IMPORTELEM,79, "importelem", NULL, 1, 2, 0, 0, JOF_BYTE |JOF_ELEM|JOF_IMPORT) - -/* Push object literal. */ -OPDEF(JSOP_OBJECT, 80, "object", NULL, 3, 0, 1, 19, JOF_CONST) - -/* Pop value and discard it. */ -OPDEF(JSOP_POP, 81, "pop", NULL, 1, 1, 0, 2, JOF_BYTE) - -/* Convert value to number, for unary +. */ -OPDEF(JSOP_POS, 82, "pos", "+ ", 1, 1, 1, 15, JOF_BYTE) - -/* Trap into debugger for breakpoint, etc. */ -OPDEF(JSOP_TRAP, 83, "trap", NULL, 1, 0, 0, 0, JOF_BYTE) - -/* Fast get/set ops for function arguments and local variables. */ -OPDEF(JSOP_GETARG, 84, "getarg", NULL, 3, 0, 1, 19, JOF_QARG |JOF_NAME) -OPDEF(JSOP_SETARG, 85, "setarg", NULL, 3, 1, 1, 3, JOF_QARG |JOF_NAME|JOF_SET|JOF_ASSIGNING) -OPDEF(JSOP_GETVAR, 86, "getvar", NULL, 3, 0, 1, 19, JOF_QVAR |JOF_NAME) -OPDEF(JSOP_SETVAR, 87, "setvar", NULL, 3, 1, 1, 3, JOF_QVAR |JOF_NAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) - -/* Push unsigned 16-bit int constant. */ -OPDEF(JSOP_UINT16, 88, "uint16", NULL, 3, 0, 1, 16, JOF_UINT16) - -/* Object and array literal support. */ -OPDEF(JSOP_NEWINIT, 89, "newinit", NULL, 1, 2, 1, 0, JOF_BYTE) -OPDEF(JSOP_ENDINIT, 90, "endinit", NULL, 1, 0, 0, 19, JOF_BYTE) -OPDEF(JSOP_INITPROP, 91, "initprop", NULL, 3, 1, 0, 3, JOF_CONST|JOF_PROP|JOF_DETECTING) -OPDEF(JSOP_INITELEM, 92, "initelem", NULL, 1, 2, 0, 3, JOF_BYTE |JOF_ELEM|JOF_DETECTING) -OPDEF(JSOP_DEFSHARP, 93, "defsharp", NULL, 3, 0, 0, 0, JOF_UINT16) -OPDEF(JSOP_USESHARP, 94, "usesharp", NULL, 3, 0, 1, 0, JOF_UINT16) - -/* Fast inc/dec ops for args and local vars. */ -OPDEF(JSOP_INCARG, 95, "incarg", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_INC) -OPDEF(JSOP_INCVAR, 96, "incvar", NULL, 3, 0, 1, 15, JOF_QVAR |JOF_NAME|JOF_INC) -OPDEF(JSOP_DECARG, 97, "decarg", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_DEC) -OPDEF(JSOP_DECVAR, 98, "decvar", NULL, 3, 0, 1, 15, JOF_QVAR |JOF_NAME|JOF_DEC) -OPDEF(JSOP_ARGINC, 99, "arginc", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_INC|JOF_POST) -OPDEF(JSOP_VARINC, 100,"varinc", NULL, 3, 0, 1, 15, JOF_QVAR |JOF_NAME|JOF_INC|JOF_POST) -OPDEF(JSOP_ARGDEC, 101,"argdec", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_DEC|JOF_POST) -OPDEF(JSOP_VARDEC, 102,"vardec", NULL, 3, 0, 1, 15, JOF_QVAR |JOF_NAME|JOF_DEC|JOF_POST) - -/* - * Initialize for-in iterator. See also JSOP_FOREACH and JSOP_FOREACHKEYVAL. - */ -OPDEF(JSOP_FORIN, 103,"forin", NULL, 1, 1, 1, 0, JOF_BYTE) - -/* ECMA-compliant for/in ops. */ -OPDEF(JSOP_FORNAME, 104,"forname", NULL, 3, 0, 1, 19, JOF_CONST|JOF_NAME|JOF_FOR) -OPDEF(JSOP_FORPROP, 105,"forprop", NULL, 3, 1, 1, 18, JOF_CONST|JOF_PROP|JOF_FOR) -OPDEF(JSOP_FORELEM, 106,"forelem", NULL, 1, 1, 3, 18, JOF_BYTE |JOF_ELEM|JOF_FOR) -OPDEF(JSOP_POP2, 107,"pop2", NULL, 1, 2, 0, 0, JOF_BYTE) - -/* ECMA-compliant assignment ops. */ -OPDEF(JSOP_BINDNAME, 108,"bindname", NULL, 3, 0, 1, 0, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING) -OPDEF(JSOP_SETNAME, 109,"setname", NULL, 3, 2, 1, 3, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) - -/* Exception handling ops. */ -OPDEF(JSOP_THROW, 110,"throw", NULL, 1, 1, 0, 0, JOF_BYTE) - -/* 'in' and 'instanceof' ops. */ -OPDEF(JSOP_IN, 111,js_in_str, js_in_str, 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_INSTANCEOF,112,js_instanceof_str,js_instanceof_str,1,2,1,11,JOF_BYTE|JOF_LEFTASSOC) - -/* debugger op */ -OPDEF(JSOP_DEBUGGER, 113,"debugger", NULL, 1, 0, 0, 0, JOF_BYTE) - -/* gosub/retsub for finally handling */ -OPDEF(JSOP_GOSUB, 114,"gosub", NULL, 3, 0, 0, 0, JOF_JUMP) -OPDEF(JSOP_RETSUB, 115,"retsub", NULL, 1, 0, 0, 0, JOF_BYTE) - -/* More exception handling ops. */ -OPDEF(JSOP_EXCEPTION, 116,"exception", NULL, 1, 0, 1, 0, JOF_BYTE) -OPDEF(JSOP_SETSP, 117,"setsp", NULL, 3, 0, 0, 0, JOF_UINT16) - -/* - * ECMA-compliant switch statement ops. - * CONDSWITCH is a decompilable NOP; CASE is ===, POP, jump if true, re-push - * lval if false; and DEFAULT is POP lval and GOTO. - */ -OPDEF(JSOP_CONDSWITCH,118,"condswitch", NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_CASE, 119,"case", NULL, 3, 1, 0, 0, JOF_JUMP) -OPDEF(JSOP_DEFAULT, 120,"default", NULL, 3, 1, 0, 0, JOF_JUMP) - -/* - * ECMA-compliant call to eval op - */ -OPDEF(JSOP_EVAL, 121,"eval", NULL, 3, -1, 1, 18, JOF_UINT16) - -/* - * ECMA-compliant helper for 'for (x[i] in o)' loops. - */ -OPDEF(JSOP_ENUMELEM, 122,"enumelem", NULL, 1, 3, 0, 3, JOF_BYTE |JOF_SET|JOF_ASSIGNING) - -/* - * Getter and setter prefix bytecodes. These modify the next bytecode, either - * an assignment or a property initializer code, which then defines a property - * getter or setter. - */ -OPDEF(JSOP_GETTER, 123,js_getter_str,NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_SETTER, 124,js_setter_str,NULL, 1, 0, 0, 0, JOF_BYTE) - -/* - * Prolog bytecodes for defining function, var, and const names. - */ -OPDEF(JSOP_DEFFUN, 125,"deffun", NULL, 3, 0, 0, 0, JOF_CONST|JOF_DECLARING) -OPDEF(JSOP_DEFCONST, 126,"defconst", NULL, 3, 0, 0, 0, JOF_CONST|JOF_DECLARING) -OPDEF(JSOP_DEFVAR, 127,"defvar", NULL, 3, 0, 0, 0, JOF_CONST|JOF_DECLARING) - -/* Auto-clone (if needed due to re-parenting) and push an anonymous function. */ -OPDEF(JSOP_ANONFUNOBJ, 128, "anonfunobj", NULL, 3, 0, 1, 16, JOF_CONST) - -/* ECMA ed. 3 named function expression. */ -OPDEF(JSOP_NAMEDFUNOBJ, 129, "namedfunobj", NULL, 3, 0, 1, 16, JOF_CONST) - -/* - * Like JSOP_SETLOCAL, but specialized to avoid requiring JSOP_POP immediately - * after to throw away the exception value. - */ -OPDEF(JSOP_SETLOCALPOP, 130, "setlocalpop", NULL, 3, 1, 0, 3, JOF_LOCAL|JOF_NAME|JOF_SET) - -/* ECMA-mandated parenthesization opcode, which nulls the reference base register, obj; see jsinterp.c. */ -OPDEF(JSOP_GROUP, 131, "group", NULL, 1, 0, 0, 0, JOF_BYTE) - -/* Host object extension: given 'o.item(i) = j', the left-hand side compiles JSOP_SETCALL, rather than JSOP_CALL. */ -OPDEF(JSOP_SETCALL, 132, "setcall", NULL, 3, -1, 2, 18, JOF_UINT16|JOF_SET|JOF_ASSIGNING) - -/* - * Exception handling no-ops, for more economical byte-coding than SRC_TRYFIN - * srcnote-annotated JSOP_NOPs. - */ -OPDEF(JSOP_TRY, 133,"try", NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_FINALLY, 134,"finally", NULL, 1, 0, 0, 0, JOF_BYTE) - -/* - * Swap the top two stack elements. - */ -OPDEF(JSOP_SWAP, 135,"swap", NULL, 1, 2, 2, 0, JOF_BYTE) - -/* - * Bytecodes that avoid making an arguments object in most cases: - * JSOP_ARGSUB gets arguments[i] from fp->argv, iff i is in [0, fp->argc-1]. - * JSOP_ARGCNT returns fp->argc. - */ -OPDEF(JSOP_ARGSUB, 136,"argsub", NULL, 3, 0, 1, 18, JOF_QARG |JOF_NAME) -OPDEF(JSOP_ARGCNT, 137,"argcnt", NULL, 1, 0, 1, 18, JOF_BYTE) - -/* - * Define a local function object as a local variable. - * The local variable's slot number is the first immediate two-byte operand. - * The function object's atom index is the second immediate operand. - */ -OPDEF(JSOP_DEFLOCALFUN, 138,"deflocalfun",NULL, 5, 0, 0, 0, JOF_INDEXCONST|JOF_DECLARING) - -/* Extended jumps. */ -OPDEF(JSOP_GOTOX, 139,"gotox", NULL, 5, 0, 0, 0, JOF_JUMPX) -OPDEF(JSOP_IFEQX, 140,"ifeqx", NULL, 5, 1, 0, 3, JOF_JUMPX|JOF_DETECTING) -OPDEF(JSOP_IFNEX, 141,"ifnex", NULL, 5, 1, 0, 0, JOF_JUMPX) -OPDEF(JSOP_ORX, 142,"orx", NULL, 5, 1, 0, 5, JOF_JUMPX|JOF_DETECTING) -OPDEF(JSOP_ANDX, 143,"andx", NULL, 5, 1, 0, 6, JOF_JUMPX|JOF_DETECTING) -OPDEF(JSOP_GOSUBX, 144,"gosubx", NULL, 5, 0, 0, 0, JOF_JUMPX) -OPDEF(JSOP_CASEX, 145,"casex", NULL, 5, 1, 0, 0, JOF_JUMPX) -OPDEF(JSOP_DEFAULTX, 146,"defaultx", NULL, 5, 1, 0, 0, JOF_JUMPX) -OPDEF(JSOP_TABLESWITCHX, 147,"tableswitchx",NULL, -1, 1, 0, 0, JOF_TABLESWITCHX|JOF_DETECTING) -OPDEF(JSOP_LOOKUPSWITCHX, 148,"lookupswitchx",NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCHX|JOF_DETECTING) - -/* Placeholders for a real jump opcode set during backpatch chain fixup. */ -OPDEF(JSOP_BACKPATCH, 149,"backpatch",NULL, 3, 0, 0, 0, JOF_JUMP|JOF_BACKPATCH) -OPDEF(JSOP_BACKPATCH_POP, 150,"backpatch_pop",NULL, 3, 1, 0, 0, JOF_JUMP|JOF_BACKPATCH) - -/* Set pending exception from the stack, to trigger rethrow. */ -OPDEF(JSOP_THROWING, 151,"throwing", NULL, 1, 1, 0, 0, JOF_BYTE) - -/* Set and get return value pseudo-register in stack frame. */ -OPDEF(JSOP_SETRVAL, 152,"setrval", NULL, 1, 1, 0, 0, JOF_BYTE) -OPDEF(JSOP_RETRVAL, 153,"retrval", NULL, 1, 0, 0, 0, JOF_BYTE) - -/* Optimized global variable ops (we don't bother doing a JSOP_FORGVAR op). */ -OPDEF(JSOP_GETGVAR, 154,"getgvar", NULL, 3, 0, 1, 19, JOF_CONST|JOF_NAME) -OPDEF(JSOP_SETGVAR, 155,"setgvar", NULL, 3, 1, 1, 3, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) -OPDEF(JSOP_INCGVAR, 156,"incgvar", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_INC) -OPDEF(JSOP_DECGVAR, 157,"decgvar", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_DEC) -OPDEF(JSOP_GVARINC, 158,"gvarinc", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_INC|JOF_POST) -OPDEF(JSOP_GVARDEC, 159,"gvardec", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_DEC|JOF_POST) - -/* Regular expression literal requiring special "fork on exec" handling. */ -OPDEF(JSOP_REGEXP, 160,"regexp", NULL, 3, 0, 1, 19, JOF_CONST) - -/* XML (ECMA-357, a.k.a. "E4X") support. */ -OPDEF(JSOP_DEFXMLNS, 161,"defxmlns", NULL, 1, 1, 0, 0, JOF_BYTE) -OPDEF(JSOP_ANYNAME, 162,"anyname", NULL, 1, 0, 1, 19, JOF_BYTE|JOF_XMLNAME) -OPDEF(JSOP_QNAMEPART, 163,"qnamepart", NULL, 3, 0, 1, 19, JOF_CONST|JOF_XMLNAME) -OPDEF(JSOP_QNAMECONST, 164,"qnameconst", NULL, 3, 1, 1, 19, JOF_CONST|JOF_XMLNAME) -OPDEF(JSOP_QNAME, 165,"qname", NULL, 1, 2, 1, 0, JOF_BYTE|JOF_XMLNAME) -OPDEF(JSOP_TOATTRNAME, 166,"toattrname", NULL, 1, 1, 1, 19, JOF_BYTE|JOF_XMLNAME) -OPDEF(JSOP_TOATTRVAL, 167,"toattrval", NULL, 1, 1, 1, 19, JOF_BYTE) -OPDEF(JSOP_ADDATTRNAME, 168,"addattrname",NULL, 1, 2, 1, 13, JOF_BYTE) -OPDEF(JSOP_ADDATTRVAL, 169,"addattrval", NULL, 1, 2, 1, 13, JOF_BYTE) -OPDEF(JSOP_BINDXMLNAME, 170,"bindxmlname",NULL, 1, 1, 2, 3, JOF_BYTE|JOF_SET|JOF_ASSIGNING) -OPDEF(JSOP_SETXMLNAME, 171,"setxmlname", NULL, 1, 3, 1, 3, JOF_BYTE|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) -OPDEF(JSOP_XMLNAME, 172,"xmlname", NULL, 1, 1, 1, 19, JOF_BYTE) -OPDEF(JSOP_DESCENDANTS, 173,"descendants",NULL, 1, 2, 1, 18, JOF_BYTE) -OPDEF(JSOP_FILTER, 174,"filter", NULL, 3, 1, 1, 0, JOF_JUMP) -OPDEF(JSOP_ENDFILTER, 175,"endfilter", NULL, 1, 1, 0, 18, JOF_BYTE) -OPDEF(JSOP_TOXML, 176,"toxml", NULL, 1, 1, 1, 19, JOF_BYTE) -OPDEF(JSOP_TOXMLLIST, 177,"toxmllist", NULL, 1, 1, 1, 19, JOF_BYTE) -OPDEF(JSOP_XMLTAGEXPR, 178,"xmltagexpr", NULL, 1, 1, 1, 0, JOF_BYTE) -OPDEF(JSOP_XMLELTEXPR, 179,"xmleltexpr", NULL, 1, 1, 1, 0, JOF_BYTE) -OPDEF(JSOP_XMLOBJECT, 180,"xmlobject", NULL, 3, 0, 1, 19, JOF_CONST) -OPDEF(JSOP_XMLCDATA, 181,"xmlcdata", NULL, 3, 0, 1, 19, JOF_CONST) -OPDEF(JSOP_XMLCOMMENT, 182,"xmlcomment", NULL, 3, 0, 1, 19, JOF_CONST) -OPDEF(JSOP_XMLPI, 183,"xmlpi", NULL, 3, 1, 1, 19, JOF_CONST) -OPDEF(JSOP_GETMETHOD, 184,"getmethod", NULL, 3, 1, 1, 18, JOF_CONST|JOF_PROP) -OPDEF(JSOP_GETFUNNS, 185,"getfunns", NULL, 1, 0, 1, 19, JOF_BYTE) -OPDEF(JSOP_FOREACH, 186,"foreach", NULL, 1, 1, 1, 0, JOF_BYTE) -OPDEF(JSOP_DELDESC, 187,"deldesc", NULL, 1, 2, 1, 17, JOF_BYTE |JOF_ELEM|JOF_DEL) - -/* - * Opcodes for extended literal addressing, using unsigned 24-bit immediate - * operands to hold integer operands (JSOP_UINT24), extended atom indexes in - * script->atomMap (JSOP_LITERAL, JSOP_FINDNAME), and ops prefixed by such - * atom index immediates (JSOP_LITOPX). See jsemit.c, EmitAtomIndexOp. - */ -OPDEF(JSOP_UINT24, 188,"uint24", NULL, 4, 0, 1, 16, JOF_UINT24) -OPDEF(JSOP_LITERAL, 189,"literal", NULL, 4, 0, 1, 19, JOF_UINT24) -OPDEF(JSOP_FINDNAME, 190,"findname", NULL, 4, 0, 2, 0, JOF_UINT24) -OPDEF(JSOP_LITOPX, 191,"litopx", NULL, 5, 0, 0, 0, JOF_LITOPX) - -/* - * Opcodes to help the decompiler deal with XML. - */ -OPDEF(JSOP_STARTXML, 192,"startxml", NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_STARTXMLEXPR, 193,"startxmlexpr",NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_SETMETHOD, 194,"setmethod", NULL, 3, 2, 1, 3, JOF_CONST|JOF_PROP|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) - -/* - * Stop interpretation, emitted at end of script to save the threaded bytecode - * interpreter an extra branch test on every DO_NEXT_OP (see jsinterp.c). - */ -OPDEF(JSOP_STOP, 195,"stop", NULL, 1, 0, 0, 0, JOF_BYTE) - -/* - * Get an extant property or element value, throwing ReferenceError if the - * identified property does not exist. - */ -OPDEF(JSOP_GETXPROP, 196,"getxprop", NULL, 3, 1, 1, 18, JOF_CONST|JOF_PROP) -OPDEF(JSOP_GETXELEM, 197,"getxelem", NULL, 1, 2, 1, 18, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) - -/* - * Specialized JSOP_TYPEOF to avoid reporting undefined for typeof(0, undef). - */ -OPDEF(JSOP_TYPEOFEXPR, 198,js_typeof_str, NULL, 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING) - -/* - * Block-local scope support. - */ -OPDEF(JSOP_ENTERBLOCK, 199,"enterblock", NULL, 3, 0, 0, 0, JOF_CONST) -OPDEF(JSOP_LEAVEBLOCK, 200,"leaveblock", NULL, 3, 0, 0, 0, JOF_UINT16) -OPDEF(JSOP_GETLOCAL, 201,"getlocal", NULL, 3, 0, 1, 19, JOF_LOCAL|JOF_NAME) -OPDEF(JSOP_SETLOCAL, 202,"setlocal", NULL, 3, 1, 1, 3, JOF_LOCAL|JOF_NAME|JOF_SET) -OPDEF(JSOP_INCLOCAL, 203,"inclocal", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_INC) -OPDEF(JSOP_DECLOCAL, 204,"declocal", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_DEC) -OPDEF(JSOP_LOCALINC, 205,"localinc", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_INC|JOF_POST) -OPDEF(JSOP_LOCALDEC, 206,"localdec", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_DEC|JOF_POST) -OPDEF(JSOP_FORLOCAL, 207,"forlocal", NULL, 3, 0, 1, 19, JOF_LOCAL|JOF_NAME|JOF_FOR) - -/* - * Iterator, generator, and array comprehension support. - */ -OPDEF(JSOP_STARTITER, 208,"startiter", NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_ENDITER, 209,"enditer", NULL, 1, 1, 0, 0, JOF_BYTE) -OPDEF(JSOP_GENERATOR, 210,"generator", NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_YIELD, 211,"yield", NULL, 1, 1, 1, 1, JOF_BYTE) -OPDEF(JSOP_ARRAYPUSH, 212,"arraypush", NULL, 3, 1, 0, 3, JOF_LOCAL) - -OPDEF(JSOP_FOREACHKEYVAL, 213,"foreachkeyval",NULL, 1, 1, 1, 0, JOF_BYTE) - -/* - * Variant of JSOP_ENUMELEM for destructuring const (const [a, b] = ...). - */ -OPDEF(JSOP_ENUMCONSTELEM, 214,"enumconstelem",NULL, 1, 3, 0, 3, JOF_BYTE|JOF_SET|JOF_ASSIGNING) - -/* - * Variant of JSOP_LEAVEBLOCK has a result on the stack above the locals, - * which must be moved down when the block pops. - */ -OPDEF(JSOP_LEAVEBLOCKEXPR,215,"leaveblockexpr",NULL, 3, 0, 0, 1, JOF_UINT16) diff --git a/spidermonkey/src/jsosdep.h b/spidermonkey/src/jsosdep.h deleted file mode 100644 index a266144..0000000 --- a/spidermonkey/src/jsosdep.h +++ /dev/null @@ -1,115 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsosdep_h___ -#define jsosdep_h___ -/* - * OS (and machine, and compiler XXX) dependent information. - */ - -#if defined(XP_WIN) || defined(XP_OS2) - -#if defined(_WIN32) || defined (XP_OS2) -#define JS_HAVE_LONG_LONG -#else -#undef JS_HAVE_LONG_LONG -#endif -#endif /* XP_WIN || XP_OS2 */ - -#ifdef XP_BEOS -#define JS_HAVE_LONG_LONG -#endif - - -#ifdef XP_UNIX - -/* - * Get OS specific header information. - */ -#if defined(XP_MACOSX) || defined(DARWIN) -#define JS_HAVE_LONG_LONG - -#elif defined(AIXV3) || defined(AIX) -#define JS_HAVE_LONG_LONG - -#elif defined(BSDI) -#define JS_HAVE_LONG_LONG - -#elif defined(HPUX) -#define JS_HAVE_LONG_LONG - -#elif defined(IRIX) -#define JS_HAVE_LONG_LONG - -#elif defined(linux) -#define JS_HAVE_LONG_LONG - -#elif defined(OSF1) -#define JS_HAVE_LONG_LONG - -#elif defined(_SCO_DS) -#undef JS_HAVE_LONG_LONG - -#elif defined(SOLARIS) -#define JS_HAVE_LONG_LONG - -#elif defined(FREEBSD) -#define JS_HAVE_LONG_LONG - -#elif defined(SUNOS4) -#undef JS_HAVE_LONG_LONG - -/* -** Missing function prototypes -*/ - -extern void *sbrk(int); - -#elif defined(UNIXWARE) -#undef JS_HAVE_LONG_LONG - -#elif defined(VMS) && defined(__ALPHA) -#define JS_HAVE_LONG_LONG - -#endif - -#endif /* XP_UNIX */ - -#endif /* jsosdep_h___ */ - diff --git a/spidermonkey/src/jsotypes.h b/spidermonkey/src/jsotypes.h deleted file mode 100644 index 38d7286..0000000 --- a/spidermonkey/src/jsotypes.h +++ /dev/null @@ -1,202 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * This section typedefs the old 'native' types to the new PRs. - * These definitions are scheduled to be eliminated at the earliest - * possible time. The NSPR API is implemented and documented using - * the new definitions. - */ - -/* - * Note that we test for PROTYPES_H, not JSOTYPES_H. This is to avoid - * double-definitions of scalar types such as uint32, if NSPR's - * protypes.h is also included. - */ -#ifndef PROTYPES_H -#define PROTYPES_H - -#ifdef XP_BEOS -/* BeOS defines most int types in SupportDefs.h (int8, uint8, int16, - * uint16, int32, uint32, int64, uint64), so in the interest of - * not conflicting with other definitions elsewhere we have to skip the - * #ifdef jungle below, duplicate some definitions, and do our stuff. - */ -#include - -typedef JSUintn uintn; -#ifndef _XP_Core_ -typedef JSIntn intn; -#endif - -#else - -/* SVR4 typedef of uint is commonly found on UNIX machines. */ -#if defined(XP_UNIX) && !defined(__QNXNTO__) -#include -#else -typedef JSUintn uint; -#endif - -typedef JSUintn uintn; -typedef JSUint64 uint64; -#if !defined(_WIN32) && !defined(XP_OS2) -typedef JSUint32 uint32; -#else -typedef unsigned long uint32; -#endif -typedef JSUint16 uint16; -typedef JSUint8 uint8; - -#ifndef _XP_Core_ -typedef JSIntn intn; -#endif - -/* - * On AIX 4.3, sys/inttypes.h (which is included by sys/types.h, a very - * common header file) defines the types int8, int16, int32, and int64. - * So we don't define these four types here to avoid conflicts in case - * the code also includes sys/types.h. - */ -#if defined(AIX) && defined(HAVE_SYS_INTTYPES_H) -#include -#else -typedef JSInt64 int64; - -/* /usr/include/model.h on HP-UX defines int8, int16, and int32 */ -#ifdef HPUX -#include -#else -#if !defined(_WIN32) && !defined(XP_OS2) -typedef JSInt32 int32; -#else -typedef long int32; -#endif -typedef JSInt16 int16; -typedef JSInt8 int8; -#endif /* HPUX */ -#endif /* AIX && HAVE_SYS_INTTYPES_H */ - -#endif /* XP_BEOS */ - -typedef JSFloat64 float64; - -/* Re: jsbit.h */ -#define TEST_BIT JS_TEST_BIT -#define SET_BIT JS_SET_BIT -#define CLEAR_BIT JS_CLEAR_BIT - -/* Re: prarena.h->plarena.h */ -#define PRArena PLArena -#define PRArenaPool PLArenaPool -#define PRArenaStats PLArenaStats -#define PR_ARENA_ALIGN PL_ARENA_ALIGN -#define PR_INIT_ARENA_POOL PL_INIT_ARENA_POOL -#define PR_ARENA_ALLOCATE PL_ARENA_ALLOCATE -#define PR_ARENA_GROW PL_ARENA_GROW -#define PR_ARENA_MARK PL_ARENA_MARK -#define PR_CLEAR_UNUSED PL_CLEAR_UNUSED -#define PR_CLEAR_ARENA PL_CLEAR_ARENA -#define PR_ARENA_RELEASE PL_ARENA_RELEASE -#define PR_COUNT_ARENA PL_COUNT_ARENA -#define PR_ARENA_DESTROY PL_ARENA_DESTROY -#define PR_InitArenaPool PL_InitArenaPool -#define PR_FreeArenaPool PL_FreeArenaPool -#define PR_FinishArenaPool PL_FinishArenaPool -#define PR_CompactArenaPool PL_CompactArenaPool -#define PR_ArenaFinish PL_ArenaFinish -#define PR_ArenaAllocate PL_ArenaAllocate -#define PR_ArenaGrow PL_ArenaGrow -#define PR_ArenaRelease PL_ArenaRelease -#define PR_ArenaCountAllocation PL_ArenaCountAllocation -#define PR_ArenaCountInplaceGrowth PL_ArenaCountInplaceGrowth -#define PR_ArenaCountGrowth PL_ArenaCountGrowth -#define PR_ArenaCountRelease PL_ArenaCountRelease -#define PR_ArenaCountRetract PL_ArenaCountRetract - -/* Re: prevent.h->plevent.h */ -#define PREvent PLEvent -#define PREventQueue PLEventQueue -#define PR_CreateEventQueue PL_CreateEventQueue -#define PR_DestroyEventQueue PL_DestroyEventQueue -#define PR_GetEventQueueMonitor PL_GetEventQueueMonitor -#define PR_ENTER_EVENT_QUEUE_MONITOR PL_ENTER_EVENT_QUEUE_MONITOR -#define PR_EXIT_EVENT_QUEUE_MONITOR PL_EXIT_EVENT_QUEUE_MONITOR -#define PR_PostEvent PL_PostEvent -#define PR_PostSynchronousEvent PL_PostSynchronousEvent -#define PR_GetEvent PL_GetEvent -#define PR_EventAvailable PL_EventAvailable -#define PREventFunProc PLEventFunProc -#define PR_MapEvents PL_MapEvents -#define PR_RevokeEvents PL_RevokeEvents -#define PR_ProcessPendingEvents PL_ProcessPendingEvents -#define PR_WaitForEvent PL_WaitForEvent -#define PR_EventLoop PL_EventLoop -#define PR_GetEventQueueSelectFD PL_GetEventQueueSelectFD -#define PRHandleEventProc PLHandleEventProc -#define PRDestroyEventProc PLDestroyEventProc -#define PR_InitEvent PL_InitEvent -#define PR_GetEventOwner PL_GetEventOwner -#define PR_HandleEvent PL_HandleEvent -#define PR_DestroyEvent PL_DestroyEvent -#define PR_DequeueEvent PL_DequeueEvent -#define PR_GetMainEventQueue PL_GetMainEventQueue - -/* Re: prhash.h->plhash.h */ -#define PRHashEntry PLHashEntry -#define PRHashTable PLHashTable -#define PRHashNumber PLHashNumber -#define PRHashFunction PLHashFunction -#define PRHashComparator PLHashComparator -#define PRHashEnumerator PLHashEnumerator -#define PRHashAllocOps PLHashAllocOps -#define PR_NewHashTable PL_NewHashTable -#define PR_HashTableDestroy PL_HashTableDestroy -#define PR_HashTableRawLookup PL_HashTableRawLookup -#define PR_HashTableRawAdd PL_HashTableRawAdd -#define PR_HashTableRawRemove PL_HashTableRawRemove -#define PR_HashTableAdd PL_HashTableAdd -#define PR_HashTableRemove PL_HashTableRemove -#define PR_HashTableEnumerateEntries PL_HashTableEnumerateEntries -#define PR_HashTableLookup PL_HashTableLookup -#define PR_HashTableDump PL_HashTableDump -#define PR_HashString PL_HashString -#define PR_CompareStrings PL_CompareStrings -#define PR_CompareValues PL_CompareValues - -#endif /* !defined(PROTYPES_H) */ diff --git a/spidermonkey/src/jsparse.c b/spidermonkey/src/jsparse.c deleted file mode 100644 index 5e37d54..0000000 --- a/spidermonkey/src/jsparse.c +++ /dev/null @@ -1,6556 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS parser. - * - * This is a recursive-descent parser for the JavaScript language specified by - * "The JavaScript 1.5 Language Specification". It uses lexical and semantic - * feedback to disambiguate non-LL(1) structures. It generates trees of nodes - * induced by the recursive parsing (not precise syntax trees, see jsparse.h). - * After tree construction, it rewrites trees to fold constants and evaluate - * compile-time expressions. Finally, it calls js_EmitTree (see jsemit.h) to - * generate bytecode. - * - * This parser attempts no error recovery. - */ -#include "jsstddef.h" -#include -#include -#include -#include "jstypes.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsemit.h" -#include "jsfun.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsparse.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" - -#if JS_HAS_XML_SUPPORT -#include "jsxml.h" -#endif - -#if JS_HAS_DESTRUCTURING -#include "jsdhash.h" -#endif - -/* - * JS parsers, from lowest to highest precedence. - * - * Each parser takes a context, a token stream, and a tree context struct. - * Each returns a parse node tree or null on error. - */ - -typedef JSParseNode * -JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc); - -typedef JSParseNode * -JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSBool allowCallSyntax); - -typedef JSParseNode * -JSPrimaryParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSTokenType tt, JSBool afterDot); - -static JSParser FunctionStmt; -static JSParser FunctionExpr; -static JSParser Statements; -static JSParser Statement; -static JSParser Variables; -static JSParser Expr; -static JSParser AssignExpr; -static JSParser CondExpr; -static JSParser OrExpr; -static JSParser AndExpr; -static JSParser BitOrExpr; -static JSParser BitXorExpr; -static JSParser BitAndExpr; -static JSParser EqExpr; -static JSParser RelExpr; -static JSParser ShiftExpr; -static JSParser AddExpr; -static JSParser MulExpr; -static JSParser UnaryExpr; -static JSMemberParser MemberExpr; -static JSPrimaryParser PrimaryExpr; - -/* - * Insist that the next token be of type tt, or report errno and return null. - * NB: this macro uses cx and ts from its lexical environment. - */ -#define MUST_MATCH_TOKEN(tt, errno) \ - JS_BEGIN_MACRO \ - if (js_GetToken(cx, ts) != tt) { \ - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, \ - errno); \ - return NULL; \ - } \ - JS_END_MACRO - -#define CHECK_RECURSION() \ - JS_BEGIN_MACRO \ - int stackDummy; \ - if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { \ - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, \ - JSMSG_OVER_RECURSED); \ - return NULL; \ - } \ - JS_END_MACRO - -#ifdef METER_PARSENODES -static uint32 parsenodes = 0; -static uint32 maxparsenodes = 0; -static uint32 recyclednodes = 0; -#endif - -static JSParseNode * -RecycleTree(JSParseNode *pn, JSTreeContext *tc) -{ - JSParseNode *next; - - if (!pn) - return NULL; - JS_ASSERT(pn != tc->nodeList); /* catch back-to-back dup recycles */ - next = pn->pn_next; - pn->pn_next = tc->nodeList; - tc->nodeList = pn; -#ifdef METER_PARSENODES - recyclednodes++; -#endif - return next; -} - -static JSParseNode * -NewOrRecycledNode(JSContext *cx, JSTreeContext *tc) -{ - JSParseNode *pn; - - pn = tc->nodeList; - if (!pn) { - JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool); - if (!pn) - JS_ReportOutOfMemory(cx); - } else { - tc->nodeList = pn->pn_next; - - /* Recycle immediate descendents only, to save work and working set. */ - switch (pn->pn_arity) { - case PN_FUNC: - RecycleTree(pn->pn_body, tc); - break; - case PN_LIST: - if (pn->pn_head) { - /* XXX check for dup recycles in the list */ - *pn->pn_tail = tc->nodeList; - tc->nodeList = pn->pn_head; -#ifdef METER_PARSENODES - recyclednodes += pn->pn_count; -#endif - } - break; - case PN_TERNARY: - RecycleTree(pn->pn_kid1, tc); - RecycleTree(pn->pn_kid2, tc); - RecycleTree(pn->pn_kid3, tc); - break; - case PN_BINARY: - RecycleTree(pn->pn_left, tc); - RecycleTree(pn->pn_right, tc); - break; - case PN_UNARY: - RecycleTree(pn->pn_kid, tc); - break; - case PN_NAME: - RecycleTree(pn->pn_expr, tc); - break; - case PN_NULLARY: - break; - } - } -#ifdef METER_PARSENODES - if (pn) { - parsenodes++; - if (parsenodes - recyclednodes > maxparsenodes) - maxparsenodes = parsenodes - recyclednodes; - } -#endif - return pn; -} - -/* - * Allocate a JSParseNode from cx's temporary arena. - */ -static JSParseNode * -NewParseNode(JSContext *cx, JSTokenStream *ts, JSParseNodeArity arity, - JSTreeContext *tc) -{ - JSParseNode *pn; - JSToken *tp; - - pn = NewOrRecycledNode(cx, tc); - if (!pn) - return NULL; - tp = &CURRENT_TOKEN(ts); - pn->pn_type = tp->type; - pn->pn_pos = tp->pos; - pn->pn_op = JSOP_NOP; - pn->pn_arity = arity; - pn->pn_next = NULL; - pn->pn_ts = ts; - pn->pn_source = NULL; - pn->pn_no_semi = JS_FALSE; - return pn; -} - -static JSParseNode * -NewBinary(JSContext *cx, JSTokenType tt, - JSOp op, JSParseNode *left, JSParseNode *right, - JSTreeContext *tc) -{ - JSParseNode *pn, *pn1, *pn2; - - if (!left || !right) - return NULL; - - /* - * Flatten a left-associative (left-heavy) tree of a given operator into - * a list, to reduce js_FoldConstants and js_EmitTree recursion. - */ - if (left->pn_type == tt && - left->pn_op == op && - (js_CodeSpec[op].format & JOF_LEFTASSOC)) { - if (left->pn_arity != PN_LIST) { - pn1 = left->pn_left, pn2 = left->pn_right; - left->pn_arity = PN_LIST; - PN_INIT_LIST_1(left, pn1); - PN_APPEND(left, pn2); - if (tt == TOK_PLUS) { - if (pn1->pn_type == TOK_STRING) - left->pn_extra |= PNX_STRCAT; - else if (pn1->pn_type != TOK_NUMBER) - left->pn_extra |= PNX_CANTFOLD; - if (pn2->pn_type == TOK_STRING) - left->pn_extra |= PNX_STRCAT; - else if (pn2->pn_type != TOK_NUMBER) - left->pn_extra |= PNX_CANTFOLD; - } - } - PN_APPEND(left, right); - left->pn_pos.end = right->pn_pos.end; - if (tt == TOK_PLUS) { - if (right->pn_type == TOK_STRING) - left->pn_extra |= PNX_STRCAT; - else if (right->pn_type != TOK_NUMBER) - left->pn_extra |= PNX_CANTFOLD; - } - return left; - } - - /* - * Fold constant addition immediately, to conserve node space and, what's - * more, so js_FoldConstants never sees mixed addition and concatenation - * operations with more than one leading non-string operand in a PN_LIST - * generated for expressions such as 1 + 2 + "pt" (which should evaluate - * to "3pt", not "12pt"). - */ - if (tt == TOK_PLUS && - left->pn_type == TOK_NUMBER && - right->pn_type == TOK_NUMBER) { - left->pn_dval += right->pn_dval; - left->pn_pos.end = right->pn_pos.end; - RecycleTree(right, tc); - return left; - } - - pn = NewOrRecycledNode(cx, tc); - if (!pn) - return NULL; - pn->pn_type = tt; - pn->pn_pos.begin = left->pn_pos.begin; - pn->pn_pos.end = right->pn_pos.end; - pn->pn_op = op; - pn->pn_arity = PN_BINARY; - pn->pn_left = left; - pn->pn_right = right; - pn->pn_next = NULL; - pn->pn_ts = NULL; - pn->pn_source = NULL; - pn->pn_no_semi = JS_FALSE; - return pn; -} - -#if JS_HAS_GETTER_SETTER -static JSTokenType -CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt) -{ - JSAtom *atom; - JSRuntime *rt; - JSOp op; - const char *name; - - JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME); - atom = CURRENT_TOKEN(ts).t_atom; - rt = cx->runtime; - if (atom == rt->atomState.getterAtom) - op = JSOP_GETTER; - else if (atom == rt->atomState.setterAtom) - op = JSOP_SETTER; - else - return TOK_NAME; - if (js_PeekTokenSameLine(cx, ts) != tt) - return TOK_NAME; - (void) js_GetToken(cx, ts); - if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_GETTER_OR_SETTER, - (op == JSOP_GETTER) - ? js_getter_str - : js_setter_str); - return TOK_ERROR; - } - CURRENT_TOKEN(ts).t_op = op; - if (JS_HAS_STRICT_OPTION(cx)) { - name = js_AtomToPrintableString(cx, atom); - if (!name || - !js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | - JSREPORT_WARNING | - JSREPORT_STRICT, - JSMSG_DEPRECATED_USAGE, - name)) { - return TOK_ERROR; - } - } - return tt; -} -#endif - -static void -MaybeSetupFrame(JSContext *cx, JSObject *chain, JSStackFrame *oldfp, - JSStackFrame *newfp) -{ - /* - * Always push a new frame if the current frame is special, so that - * Variables gets the correct variables object: the one from the special - * frame's caller. - */ - if (oldfp && - oldfp->varobj && - oldfp->scopeChain == chain && - !(oldfp->flags & JSFRAME_SPECIAL)) { - return; - } - - memset(newfp, 0, sizeof *newfp); - - /* Default to sharing the same variables object and scope chain. */ - newfp->varobj = newfp->scopeChain = chain; - if (cx->options & JSOPTION_VAROBJFIX) { - while ((chain = JS_GetParent(cx, chain)) != NULL) - newfp->varobj = chain; - } - newfp->down = oldfp; - if (oldfp) { - /* - * In the case of eval and debugger frames, we need to dig down and find - * the real variables objects and function that our new stack frame is - * going to use. - */ - newfp->flags = oldfp->flags & (JSFRAME_SPECIAL | JSFRAME_COMPILE_N_GO | - JSFRAME_SCRIPT_OBJECT); - while (oldfp->flags & JSFRAME_SPECIAL) { - oldfp = oldfp->down; - if (!oldfp) - break; - } - if (oldfp && (newfp->flags & JSFRAME_SPECIAL)) { - newfp->varobj = oldfp->varobj; - newfp->vars = oldfp->vars; - newfp->fun = oldfp->fun; - } - } - cx->fp = newfp; -} - -/* - * Parse a top-level JS script. - */ -JS_FRIEND_API(JSParseNode *) -js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts) -{ - JSStackFrame *fp, frame; - JSTreeContext tc; - JSParseNode *pn; - - /* - * Push a compiler frame if we have no frames, or if the top frame is a - * lightweight function activation, or if its scope chain doesn't match - * the one passed to us. - */ - fp = cx->fp; - MaybeSetupFrame(cx, chain, fp, &frame); - - /* - * Protect atoms from being collected by a GC activation, which might - * - nest on this thread due to out of memory (the so-called "last ditch" - * GC attempted within js_NewGCThing), or - * - run for any reason on another thread if this thread is suspended on - * an object lock before it finishes generating bytecode into a script - * protected from the GC by a root or a stack frame reference. - */ - JS_KEEP_ATOMS(cx->runtime); - TREE_CONTEXT_INIT(&tc); - pn = Statements(cx, ts, &tc); - if (pn) { - if (!js_MatchToken(cx, ts, TOK_EOF)) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_SYNTAX_ERROR); - pn = NULL; - } else { - pn->pn_type = TOK_LC; - if (!js_FoldConstants(cx, pn, &tc)) - pn = NULL; - } - } - - TREE_CONTEXT_FINISH(&tc); - JS_UNKEEP_ATOMS(cx->runtime); - cx->fp = fp; - return pn; -} - -/* - * Compile a top-level script. - */ -JS_FRIEND_API(JSBool) -js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, - JSCodeGenerator *cg) -{ - JSStackFrame *fp, frame; - uint32 flags; - JSParseNode *pn; - JSBool ok; -#ifdef METER_PARSENODES - void *sbrk(ptrdiff_t), *before = sbrk(0); -#endif - - /* - * Push a compiler frame if we have no frames, or if the top frame is a - * lightweight function activation, or if its scope chain doesn't match - * the one passed to us. - */ - fp = cx->fp; - MaybeSetupFrame(cx, chain, fp, &frame); - flags = cx->fp->flags; - cx->fp->flags = flags | - (JS_HAS_COMPILE_N_GO_OPTION(cx) - ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO - : JSFRAME_COMPILING); - - /* Prevent GC activation while compiling. */ - JS_KEEP_ATOMS(cx->runtime); - - pn = Statements(cx, ts, &cg->treeContext); - if (!pn) { - ok = JS_FALSE; - } else if (!js_MatchToken(cx, ts, TOK_EOF)) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_SYNTAX_ERROR); - ok = JS_FALSE; - } else { -#ifdef METER_PARSENODES - printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n", - (char *)sbrk(0) - (char *)before, - parsenodes, - maxparsenodes, - parsenodes - recyclednodes); - before = sbrk(0); -#endif - - /* - * No need to emit bytecode here -- Statements already has, for each - * statement in turn. Search for TCF_COMPILING in Statements, below. - * That flag is set for every tc == &cg->treeContext, and it implies - * that the tc can be downcast to a cg and used to emit code during - * parsing, rather than at the end of the parse phase. - * - * Nowadays the threaded interpreter needs a stop instruction, so we - * do have to emit that here. - */ - JS_ASSERT(cg->treeContext.flags & TCF_COMPILING); - ok = js_Emit1(cx, cg, JSOP_STOP) >= 0; - } - -#ifdef METER_PARSENODES - printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n", - (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount); -#endif -#ifdef JS_ARENAMETER - JS_DumpArenaStats(stdout); -#endif - JS_UNKEEP_ATOMS(cx->runtime); - cx->fp->flags = flags; - cx->fp = fp; - return ok; -} - -/* - * Insist on a final return before control flows out of pn. Try to be a bit - * smart about loops: do {...; return e2;} while(0) at the end of a function - * that contains an early return e1 will get a strict warning. Similarly for - * iloops: while (true){...} is treated as though ... returns. - */ -#define ENDS_IN_OTHER 0 -#define ENDS_IN_RETURN 1 -#define ENDS_IN_BREAK 2 - -static int -HasFinalReturn(JSParseNode *pn) -{ - JSParseNode *pn2, *pn3; - uintN rv, rv2, hasDefault; - - switch (pn->pn_type) { - case TOK_LC: - if (!pn->pn_head) - return ENDS_IN_OTHER; - return HasFinalReturn(PN_LAST(pn)); - - case TOK_IF: - if (!pn->pn_kid3) - return ENDS_IN_OTHER; - return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3); - - case TOK_WHILE: - pn2 = pn->pn_left; - if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE) - return ENDS_IN_RETURN; - if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval) - return ENDS_IN_RETURN; - return ENDS_IN_OTHER; - - case TOK_DO: - pn2 = pn->pn_right; - if (pn2->pn_type == TOK_PRIMARY) { - if (pn2->pn_op == JSOP_FALSE) - return HasFinalReturn(pn->pn_left); - if (pn2->pn_op == JSOP_TRUE) - return ENDS_IN_RETURN; - } - if (pn2->pn_type == TOK_NUMBER) { - if (pn2->pn_dval == 0) - return HasFinalReturn(pn->pn_left); - return ENDS_IN_RETURN; - } - return ENDS_IN_OTHER; - - case TOK_FOR: - pn2 = pn->pn_left; - if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2) - return ENDS_IN_RETURN; - return ENDS_IN_OTHER; - - case TOK_SWITCH: - rv = ENDS_IN_RETURN; - hasDefault = ENDS_IN_OTHER; - pn2 = pn->pn_right; - if (pn2->pn_type == TOK_LEXICALSCOPE) - pn2 = pn2->pn_expr; - for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) { - if (pn2->pn_type == TOK_DEFAULT) - hasDefault = ENDS_IN_RETURN; - pn3 = pn2->pn_right; - JS_ASSERT(pn3->pn_type == TOK_LC); - if (pn3->pn_head) { - rv2 = HasFinalReturn(PN_LAST(pn3)); - if (rv2 == ENDS_IN_OTHER && pn2->pn_next) - /* Falling through to next case or default. */; - else - rv &= rv2; - } - } - /* If a final switch has no default case, we judge it harshly. */ - rv &= hasDefault; - return rv; - - case TOK_BREAK: - return ENDS_IN_BREAK; - - case TOK_WITH: - return HasFinalReturn(pn->pn_right); - - case TOK_RETURN: - return ENDS_IN_RETURN; - - case TOK_COLON: - case TOK_LEXICALSCOPE: - return HasFinalReturn(pn->pn_expr); - - case TOK_THROW: - return ENDS_IN_RETURN; - - case TOK_TRY: - /* If we have a finally block that returns, we are done. */ - if (pn->pn_kid3) { - rv = HasFinalReturn(pn->pn_kid3); - if (rv == ENDS_IN_RETURN) - return rv; - } - - /* Else check the try block and any and all catch statements. */ - rv = HasFinalReturn(pn->pn_kid1); - if (pn->pn_kid2) { - JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST); - for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next) - rv &= HasFinalReturn(pn2); - } - return rv; - - case TOK_CATCH: - /* Check this catch block's body. */ - return HasFinalReturn(pn->pn_kid3); - - case TOK_LET: - /* Non-binary let statements are let declarations. */ - if (pn->pn_arity != PN_BINARY) - return ENDS_IN_OTHER; - return HasFinalReturn(pn->pn_right); - - default: - return ENDS_IN_OTHER; - } -} - -static JSBool -ReportBadReturn(JSContext *cx, JSTokenStream *ts, uintN flags, uintN errnum, - uintN anonerrnum) -{ - JSFunction *fun; - const char *name; - - fun = cx->fp->fun; - if (fun->atom) { - name = js_AtomToPrintableString(cx, fun->atom); - } else { - errnum = anonerrnum; - name = NULL; - } - return js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | flags, errnum, - name); -} - -static JSBool -CheckFinalReturn(JSContext *cx, JSTokenStream *ts, JSParseNode *pn) -{ - return HasFinalReturn(pn) == ENDS_IN_RETURN || - ReportBadReturn(cx, ts, JSREPORT_WARNING | JSREPORT_STRICT, - JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE); -} - -static JSParseNode * -FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun, - JSTreeContext *tc) -{ - JSStackFrame *fp, frame; - JSObject *funobj; - JSStmtInfo stmtInfo; - uintN oldflags, firstLine; - JSParseNode *pn; - - fp = cx->fp; - funobj = fun->object; - if (!fp || fp->fun != fun || fp->varobj != funobj || - fp->scopeChain != funobj) { - memset(&frame, 0, sizeof frame); - frame.fun = fun; - frame.varobj = frame.scopeChain = funobj; - frame.down = fp; - if (fp) - frame.flags = fp->flags & JSFRAME_COMPILE_N_GO; - cx->fp = &frame; - } - - /* - * Set interpreted early so js_EmitTree can test it to decide whether to - * eliminate useless expressions. - */ - fun->flags |= JSFUN_INTERPRETED; - - js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1); - stmtInfo.flags = SIF_BODY_BLOCK; - - oldflags = tc->flags; - tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID); - tc->flags |= TCF_IN_FUNCTION; - - /* - * Save the body's first line, and store it in pn->pn_pos.begin.lineno - * later, because we may have not peeked in ts yet, so Statements won't - * acquire a valid pn->pn_pos.begin from the current token. - */ - firstLine = ts->lineno; - pn = Statements(cx, ts, tc); - - js_PopStatement(tc); - - /* Check for falling off the end of a function that returns a value. */ - if (pn && JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR)) { - if (!CheckFinalReturn(cx, ts, pn)) - pn = NULL; - } - - /* - * If we have a parse tree in pn and a code generator in tc, emit this - * function's code. We must do this here, not in js_CompileFunctionBody, - * in order to detect TCF_IN_FUNCTION among tc->flags. - */ - if (pn) { - pn->pn_pos.begin.lineno = firstLine; - if ((tc->flags & TCF_COMPILING)) { - JSCodeGenerator *cg = (JSCodeGenerator *) tc; - - if (!js_FoldConstants(cx, pn, tc) || - !js_EmitFunctionBytecode(cx, cg, pn)) { - pn = NULL; - } - } - } - - cx->fp = fp; - tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS)); - return pn; -} - -/* - * Compile a JS function body, which might appear as the value of an event - * handler attribute in an HTML tag. - */ -JSBool -js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun) -{ - JSArenaPool codePool, notePool; - JSCodeGenerator funcg; - JSStackFrame *fp, frame; - JSObject *funobj; - JSParseNode *pn; - - JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode)); - JS_InitArenaPool(¬ePool, "note", 1024, sizeof(jssrcnote)); - if (!js_InitCodeGenerator(cx, &funcg, &codePool, ¬ePool, - ts->filename, ts->lineno, - ts->principals)) { - return JS_FALSE; - } - - /* Prevent GC activation while compiling. */ - JS_KEEP_ATOMS(cx->runtime); - - /* Push a JSStackFrame for use by FunctionBody. */ - fp = cx->fp; - funobj = fun->object; - JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj && - fp->scopeChain != funobj)); - memset(&frame, 0, sizeof frame); - frame.fun = fun; - frame.varobj = frame.scopeChain = funobj; - frame.down = fp; - frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx) - ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO - : JSFRAME_COMPILING; - cx->fp = &frame; - - /* - * Farble the body so that it looks like a block statement to js_EmitTree, - * which is called beneath FunctionBody; see Statements, further below in - * this file. FunctionBody pushes a STMT_BLOCK record around its call to - * Statements, so Statements will not compile each statement as it loops - * to save JSParseNode space -- it will not compile at all, only build a - * JSParseNode tree. - * - * Therefore we must fold constants, allocate try notes, and generate code - * for this function, including a stop opcode at the end. - */ - CURRENT_TOKEN(ts).type = TOK_LC; - pn = FunctionBody(cx, ts, fun, &funcg.treeContext); - if (pn && !js_NewScriptFromCG(cx, &funcg, fun)) - pn = NULL; - - /* Restore saved state and release code generation arenas. */ - cx->fp = fp; - JS_UNKEEP_ATOMS(cx->runtime); - js_FinishCodeGenerator(cx, &funcg); - JS_FinishArenaPool(&codePool); - JS_FinishArenaPool(¬ePool); - return pn != NULL; -} - -/* - * Parameter block types for the several Binder functions. We use a common - * helper function signature in order to share code among destructuring and - * simple variable declaration parsers. In the destructuring case, the binder - * function is called indirectly from the variable declaration parser by way - * of CheckDestructuring and its friends. - */ -typedef struct BindData BindData; - -typedef JSBool -(*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc); - -struct BindData { - JSParseNode *pn; /* error source coordinate */ - JSTokenStream *ts; /* fallback if pn is null */ - JSObject *obj; /* the variable object */ - JSOp op; /* prolog bytecode or nop */ - Binder binder; /* binder, discriminates u */ - union { - struct { - JSFunction *fun; /* must come first! see next */ - } arg; - struct { - JSFunction *fun; /* this overlays u.arg.fun */ - JSClass *clasp; - JSPropertyOp getter; - JSPropertyOp setter; - uintN attrs; - } var; - struct { - jsuint index; - uintN overflow; - } let; - } u; -}; - -/* - * Given BindData *data and JSREPORT_* flags, expand to the second and third - * actual parameters to js_ReportCompileErrorNumber. Prefer reporting via pn - * to reporting via ts, for better destructuring error pointers. - */ -#define BIND_DATA_REPORT_ARGS(data, flags) \ - (data)->pn ? (void *)(data)->pn : (void *)(data)->ts, \ - ((data)->pn ? JSREPORT_PN : JSREPORT_TS) | (flags) - -static JSBool -BumpFormalCount(JSContext *cx, JSFunction *fun) -{ - if (fun->nargs == JS_BITMASK(16)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_TOO_MANY_FUN_ARGS); - return JS_FALSE; - } - fun->nargs++; - return JS_TRUE; -} - -static JSBool -BindArg(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) -{ - JSObject *obj, *pobj; - JSProperty *prop; - JSBool ok; - uintN dupflag; - JSFunction *fun; - const char *name; - - obj = data->obj; - ok = js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop); - if (!ok) - return JS_FALSE; - - dupflag = 0; - if (prop) { - JS_ASSERT(pobj == obj); - name = js_AtomToPrintableString(cx, atom); - - /* - * A duplicate parameter name, a "feature" required by ECMA-262. - * We force a duplicate node on the SCOPE_LAST_PROP(scope) list - * with the same id, distinguished by the SPROP_IS_DUPLICATE flag, - * and not mapped by an entry in scope. - */ - ok = name && - js_ReportCompileErrorNumber(cx, - BIND_DATA_REPORT_ARGS(data, - JSREPORT_WARNING | - JSREPORT_STRICT), - JSMSG_DUPLICATE_FORMAL, - name); - - OBJ_DROP_PROPERTY(cx, pobj, prop); - if (!ok) - return JS_FALSE; - - dupflag = SPROP_IS_DUPLICATE; - } - - fun = data->u.arg.fun; - if (!js_AddHiddenProperty(cx, data->obj, ATOM_TO_JSID(atom), - js_GetArgument, js_SetArgument, - SPROP_INVALID_SLOT, - JSPROP_PERMANENT | JSPROP_SHARED, - dupflag | SPROP_HAS_SHORTID, - fun->nargs)) { - return JS_FALSE; - } - - return BumpFormalCount(cx, fun); -} - -static JSBool -BindLocalVariable(JSContext *cx, BindData *data, JSAtom *atom) -{ - JSFunction *fun; - - /* - * Can't increase fun->nvars in an active frame, so insist that getter is - * js_GetLocalVariable, not js_GetCallVariable or anything else. - */ - if (data->u.var.getter != js_GetLocalVariable) - return JS_TRUE; - - /* - * Don't bind a variable with the hidden name 'arguments', per ECMA-262. - * Instead 'var arguments' always restates the predefined property of the - * activation objects with unhidden name 'arguments'. Assignment to such - * a variable must be handled specially. - */ - if (atom == cx->runtime->atomState.argumentsAtom) - return JS_TRUE; - - fun = data->u.var.fun; - if (!js_AddHiddenProperty(cx, data->obj, ATOM_TO_JSID(atom), - data->u.var.getter, data->u.var.setter, - SPROP_INVALID_SLOT, - data->u.var.attrs | JSPROP_SHARED, - SPROP_HAS_SHORTID, fun->u.i.nvars)) { - return JS_FALSE; - } - if (fun->u.i.nvars == JS_BITMASK(16)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_TOO_MANY_FUN_VARS); - return JS_FALSE; - } - fun->u.i.nvars++; - return JS_TRUE; -} - -#if JS_HAS_DESTRUCTURING -/* - * Forward declaration to maintain top-down presentation. - */ -static JSParseNode * -DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc, - JSTokenType tt); - -static JSBool -BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom, - JSTreeContext *tc) -{ - JSAtomListElement *ale; - JSFunction *fun; - JSObject *obj, *pobj; - JSProperty *prop; - const char *name; - - ATOM_LIST_SEARCH(ale, &tc->decls, atom); - if (!ale) { - ale = js_IndexAtom(cx, atom, &tc->decls); - if (!ale) - return JS_FALSE; - ALE_SET_JSOP(ale, data->op); - } - - fun = data->u.var.fun; - obj = data->obj; - if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) - return JS_FALSE; - - if (prop) { - JS_ASSERT(pobj == obj && OBJ_IS_NATIVE(pobj)); - name = js_AtomToPrintableString(cx, atom); - if (!name || - !js_ReportCompileErrorNumber(cx, - BIND_DATA_REPORT_ARGS(data, - JSREPORT_WARNING | - JSREPORT_STRICT), - JSMSG_DUPLICATE_FORMAL, - name)) { - return JS_FALSE; - } - OBJ_DROP_PROPERTY(cx, pobj, prop); - } else { - if (!BindLocalVariable(cx, data, atom)) - return JS_FALSE; - } - return JS_TRUE; -} -#endif /* JS_HAS_DESTRUCTURING */ - -static JSParseNode * -FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSBool lambda) -{ - JSOp op, prevop; - JSParseNode *pn, *body, *result; - JSTokenType tt; - JSAtom *funAtom, *objAtom; - JSStackFrame *fp; - JSObject *varobj, *pobj; - JSAtomListElement *ale; - JSProperty *prop; - JSFunction *fun; - JSTreeContext funtc; -#if JS_HAS_DESTRUCTURING - JSParseNode *item, *list = NULL; -#endif - - /* Make a TOK_FUNCTION node. */ -#if JS_HAS_GETTER_SETTER - op = CURRENT_TOKEN(ts).t_op; -#endif - pn = NewParseNode(cx, ts, PN_FUNC, tc); - if (!pn) - return NULL; - - /* Scan the optional function name into funAtom. */ - ts->flags |= TSF_KEYWORD_IS_NAME; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_KEYWORD_IS_NAME; - if (tt == TOK_NAME) { - funAtom = CURRENT_TOKEN(ts).t_atom; - } else { - funAtom = NULL; - js_UngetToken(ts); - } - - /* Find the nearest variable-declaring scope and use it as our parent. */ - fp = cx->fp; - varobj = fp->varobj; - - /* - * Record names for function statements in tc->decls so we know when to - * avoid optimizing variable references that might name a function. - */ - if (!lambda && funAtom) { - ATOM_LIST_SEARCH(ale, &tc->decls, funAtom); - if (ale) { - prevop = ALE_JSOP(ale); - if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) { - const char *name = js_AtomToPrintableString(cx, funAtom); - if (!name || - !js_ReportCompileErrorNumber(cx, ts, - (prevop != JSOP_DEFCONST) - ? JSREPORT_TS | - JSREPORT_WARNING | - JSREPORT_STRICT - : JSREPORT_TS | JSREPORT_ERROR, - JSMSG_REDECLARED_VAR, - (prevop == JSOP_DEFFUN || - prevop == JSOP_CLOSURE) - ? js_function_str - : (prevop == JSOP_DEFCONST) - ? js_const_str - : js_var_str, - name)) { - return NULL; - } - } - if (!AT_TOP_LEVEL(tc) && prevop == JSOP_DEFVAR) - tc->flags |= TCF_FUN_CLOSURE_VS_VAR; - } else { - ale = js_IndexAtom(cx, funAtom, &tc->decls); - if (!ale) - return NULL; - } - ALE_SET_JSOP(ale, AT_TOP_LEVEL(tc) ? JSOP_DEFFUN : JSOP_CLOSURE); - - /* - * A function nested at top level inside another's body needs only a - * local variable to bind its name to its value, and not an activation - * object property (it might also need the activation property, if the - * outer function contains with statements, e.g., but the stack slot - * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a - * JSOP_GETVAR bytecode). - */ - if (AT_TOP_LEVEL(tc) && (tc->flags & TCF_IN_FUNCTION)) { - JSScopeProperty *sprop; - - /* - * Define a property on the outer function so that BindNameToSlot - * can properly optimize accesses. - */ - JS_ASSERT(OBJ_GET_CLASS(cx, varobj) == &js_FunctionClass); - JS_ASSERT(fp->fun == (JSFunction *) JS_GetPrivate(cx, varobj)); - if (!js_LookupHiddenProperty(cx, varobj, ATOM_TO_JSID(funAtom), - &pobj, &prop)) { - return NULL; - } - if (prop) - OBJ_DROP_PROPERTY(cx, pobj, prop); - sprop = NULL; - if (!prop || - pobj != varobj || - (sprop = (JSScopeProperty *)prop, - sprop->getter != js_GetLocalVariable)) { - uintN sflags; - - /* - * Use SPROP_IS_DUPLICATE if there is a formal argument of the - * same name, so the decompiler can find the parameter name. - */ - sflags = (sprop && sprop->getter == js_GetArgument) - ? SPROP_IS_DUPLICATE | SPROP_HAS_SHORTID - : SPROP_HAS_SHORTID; - if (!js_AddHiddenProperty(cx, varobj, ATOM_TO_JSID(funAtom), - js_GetLocalVariable, - js_SetLocalVariable, - SPROP_INVALID_SLOT, - JSPROP_PERMANENT | JSPROP_SHARED, - sflags, fp->fun->u.i.nvars)) { - return NULL; - } - if (fp->fun->u.i.nvars == JS_BITMASK(16)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_TOO_MANY_FUN_VARS); - return NULL; - } - fp->fun->u.i.nvars++; - } - } - } - - fun = js_NewFunction(cx, NULL, NULL, 0, lambda ? JSFUN_LAMBDA : 0, varobj, - funAtom); - if (!fun) - return NULL; -#if JS_HAS_GETTER_SETTER - if (op != JSOP_NOP) - fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER; -#endif - - /* - * Atomize fun->object early to protect against a last-ditch GC under - * js_LookupHiddenProperty. - * - * Absent use of the new scoped local GC roots API around compiler calls, - * we need to atomize here to protect against a GC activation. Atoms are - * protected from GC during compilation by the JS_FRIEND_API entry points - * in this file. There doesn't seem to be any gain in switching from the - * atom-keeping method to the bulkier, slower scoped local roots method. - */ - objAtom = js_AtomizeObject(cx, fun->object, 0); - if (!objAtom) - return NULL; - - /* Initialize early for possible flags mutation via DestructuringExpr. */ - TREE_CONTEXT_INIT(&funtc); - - /* Now parse formal argument list and compute fun->nargs. */ - MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL); - if (!js_MatchToken(cx, ts, TOK_RP)) { - BindData data; - - data.pn = NULL; - data.ts = ts; - data.obj = fun->object; - data.op = JSOP_NOP; - data.binder = BindArg; - data.u.arg.fun = fun; - - do { - tt = js_GetToken(cx, ts); - switch (tt) { -#if JS_HAS_DESTRUCTURING - case TOK_LB: - case TOK_LC: - { - JSParseNode *lhs, *rhs; - jsint slot; - - /* - * A destructuring formal parameter turns into one or more - * local variables initialized from properties of a single - * anonymous positional parameter, so here we must tweak our - * binder and its data. - */ - data.op = JSOP_DEFVAR; - data.binder = BindDestructuringArg; - data.u.var.clasp = &js_FunctionClass; - data.u.var.getter = js_GetLocalVariable; - data.u.var.setter = js_SetLocalVariable; - data.u.var.attrs = JSPROP_PERMANENT; - - /* - * Temporarily transfer the owneship of the recycle list to - * funtc. See bug 313967. - */ - funtc.nodeList = tc->nodeList; - tc->nodeList = NULL; - lhs = DestructuringExpr(cx, &data, &funtc, tt); - tc->nodeList = funtc.nodeList; - funtc.nodeList = NULL; - if (!lhs) - return NULL; - - /* - * Restore the formal parameter binder in case there are more - * non-destructuring formals in the parameter list. - */ - data.binder = BindArg; - - /* - * Adjust fun->nargs to count the single anonymous positional - * parameter that is to be destructured. - */ - slot = fun->nargs; - if (!BumpFormalCount(cx, fun)) - return NULL; - - /* - * Synthesize a destructuring assignment from the single - * anonymous positional parameter into the destructuring - * left-hand-side expression and accumulate it in list. - */ - rhs = NewParseNode(cx, ts, PN_NAME, tc); - if (!rhs) - return NULL; - rhs->pn_type = TOK_NAME; - rhs->pn_op = JSOP_GETARG; - rhs->pn_atom = cx->runtime->atomState.emptyAtom; - rhs->pn_expr = NULL; - rhs->pn_slot = slot; - rhs->pn_attrs = 0; - - item = NewBinary(cx, TOK_ASSIGN, JSOP_NOP, lhs, rhs, tc); - if (!item) - return NULL; - if (!list) { - list = NewParseNode(cx, ts, PN_LIST, tc); - if (!list) - return NULL; - list->pn_type = TOK_COMMA; - PN_INIT_LIST(list); - } - PN_APPEND(list, item); - break; - } -#endif /* JS_HAS_DESTRUCTURING */ - - case TOK_NAME: - if (!data.binder(cx, &data, CURRENT_TOKEN(ts).t_atom, tc)) - return NULL; - break; - - default: - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_MISSING_FORMAL); - return NULL; - } - } while (js_MatchToken(cx, ts, TOK_COMMA)); - - MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL); - } - - MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY); - pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin; - - /* - * Temporarily transfer the owneship of the recycle list to funtc. - * See bug 313967. - */ - funtc.nodeList = tc->nodeList; - tc->nodeList = NULL; - body = FunctionBody(cx, ts, fun, &funtc); - tc->nodeList = funtc.nodeList; - funtc.nodeList = NULL; - - if (!body) - return NULL; - - MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY); - pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - -#if JS_HAS_DESTRUCTURING - /* - * If there were destructuring formal parameters, prepend the initializing - * comma expression that we synthesized to body. If the body is a lexical - * scope node, we must make a special TOK_BODY node, to prepend the formal - * parameter destructuring code without bracing the decompilation of the - * function body's lexical scope. - */ - if (list) { - if (body->pn_arity != PN_LIST) { - JSParseNode *block; - - JS_ASSERT(body->pn_type == TOK_LEXICALSCOPE); - JS_ASSERT(body->pn_arity == PN_NAME); - - block = NewParseNode(cx, ts, PN_LIST, tc); - if (!block) - return NULL; - block->pn_type = TOK_BODY; - block->pn_pos = body->pn_pos; - PN_INIT_LIST_1(block, body); - - body = block; - } - - item = NewParseNode(cx, ts, PN_UNARY, tc); - if (!item) - return NULL; - - item->pn_type = TOK_SEMI; - item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin; - item->pn_kid = list; - item->pn_next = body->pn_head; - body->pn_head = item; - if (body->pn_tail == &body->pn_head) - body->pn_tail = &item->pn_next; - ++body->pn_count; - } -#endif - - /* - * If we collected flags that indicate nested heavyweight functions, or - * this function contains heavyweight-making statements (references to - * __parent__ or __proto__; use of with, eval, import, or export; and - * assignment to arguments), flag the function as heavyweight (requiring - * a call object per invocation). - */ - if (funtc.flags & TCF_FUN_HEAVYWEIGHT) { - fun->flags |= JSFUN_HEAVYWEIGHT; - tc->flags |= TCF_FUN_HEAVYWEIGHT; - } else { - /* - * If this function is a named statement function not at top-level - * (i.e. a JSOP_CLOSURE, not a function definiton or expression), then - * our enclosing function, if any, must be heavyweight. - * - * The TCF_FUN_USES_NONLOCALS flag is set only by the code generator, - * so it won't be set here. Assert that it's not. We have to check - * it later, in js_EmitTree, after js_EmitFunctionBody has traversed - * the function's body - */ - JS_ASSERT(!(funtc.flags & TCF_FUN_USES_NONLOCALS)); - if (!lambda && funAtom && !AT_TOP_LEVEL(tc)) - tc->flags |= TCF_FUN_HEAVYWEIGHT; - } - - result = pn; - if (lambda) { - /* - * ECMA ed. 3 standard: function expression, possibly anonymous. - */ - op = funAtom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ; - } else if (!funAtom) { - /* - * If this anonymous function definition is *not* embedded within a - * larger expression, we treat it as an expression statement, not as - * a function declaration -- and not as a syntax error (as ECMA-262 - * Edition 3 would have it). Backward compatibility trumps all. - */ - result = NewParseNode(cx, ts, PN_UNARY, tc); - if (!result) - return NULL; - result->pn_type = TOK_SEMI; - result->pn_pos = pn->pn_pos; - result->pn_kid = pn; - op = JSOP_ANONFUNOBJ; - } else if (!AT_TOP_LEVEL(tc)) { - /* - * ECMA ed. 3 extension: a function expression statement not at the - * top level, e.g., in a compound statement such as the "then" part - * of an "if" statement, binds a closure only if control reaches that - * sub-statement. - */ - op = JSOP_CLOSURE; - } else { - op = JSOP_NOP; - } - - pn->pn_funAtom = objAtom; - pn->pn_op = op; - pn->pn_body = body; - pn->pn_flags = funtc.flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS); - pn->pn_tryCount = funtc.tryCount; - TREE_CONTEXT_FINISH(&funtc); - return result; -} - -static JSParseNode * -FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - return FunctionDef(cx, ts, tc, JS_FALSE); -} - -static JSParseNode * -FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - return FunctionDef(cx, ts, tc, JS_TRUE); -} - -/* - * Parse the statements in a block, creating a TOK_LC node that lists the - * statements' trees. If called from block-parsing code, the caller must - * match { before and } after. - */ -static JSParseNode * -Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn, *pn2, *saveBlock; - JSTokenType tt; - - CHECK_RECURSION(); - - pn = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn) - return NULL; - saveBlock = tc->blockNode; - tc->blockNode = pn; - PN_INIT_LIST(pn); - - ts->flags |= TSF_OPERAND; - while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) { - ts->flags &= ~TSF_OPERAND; - pn2 = Statement(cx, ts, tc); - if (!pn2) { - if (ts->flags & TSF_EOF) - ts->flags |= TSF_UNEXPECTED_EOF; - return NULL; - } - ts->flags |= TSF_OPERAND; - - /* Detect a function statement for the TOK_LC case in Statement. */ - if (pn2->pn_type == TOK_FUNCTION && !AT_TOP_LEVEL(tc)) - tc->flags |= TCF_HAS_FUNCTION_STMT; - - /* If compiling top-level statements, emit as we go to save space. */ - if (!tc->topStmt && (tc->flags & TCF_COMPILING)) { - if (cx->fp->fun && - JS_HAS_STRICT_OPTION(cx) && - (tc->flags & TCF_RETURN_EXPR)) { - /* - * Check pn2 for lack of a final return statement if it is the - * last statement in the block. - */ - tt = js_PeekToken(cx, ts); - if ((tt == TOK_EOF || tt == TOK_RC) && - !CheckFinalReturn(cx, ts, pn2)) { - tt = TOK_ERROR; - break; - } - - /* - * Clear TCF_RETURN_EXPR so FunctionBody doesn't try to - * CheckFinalReturn again. - */ - tc->flags &= ~TCF_RETURN_EXPR; - } - if (!js_FoldConstants(cx, pn2, tc) || - !js_AllocTryNotes(cx, (JSCodeGenerator *)tc) || - !js_EmitTree(cx, (JSCodeGenerator *)tc, pn2)) { - tt = TOK_ERROR; - break; - } - RecycleTree(pn2, tc); - } else { - PN_APPEND(pn, pn2); - } - } - - /* - * Handle the case where there was a let declaration under this block. If - * it replaced tc->blockNode with a new block node then we must refresh pn - * and then restore tc->blockNode. - */ - if (tc->blockNode != pn) - pn = tc->blockNode; - tc->blockNode = saveBlock; - - ts->flags &= ~TSF_OPERAND; - if (tt == TOK_ERROR) - return NULL; - - /* Set the LC's end position to the start of the RC. The stream is - * guaranteed to have a lookahead because of the peek above. */ - pn->pn_pos.end = ts->tokens[(ts->cursor+ts->lookahead) & NTOKENS_MASK].pos.begin; - return pn; -} - -static JSParseNode * -Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn, *pn2; - - MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND); - pn = Expr(cx, ts, tc); - if (!pn) - return NULL; - MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND); - - /* - * Check for (a = b) and "correct" it to (a == b) iff b's operator has - * greater precedence than ==. - * XXX not ECMA, but documented in several books -- now a strict warning. - */ - if (pn->pn_type == TOK_ASSIGN && - pn->pn_op == JSOP_NOP && - pn->pn_right->pn_type > TOK_EQOP) - { - JSBool rewrite = !JS_VERSION_IS_ECMA(cx); - if (!js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | - JSREPORT_WARNING | - JSREPORT_STRICT, - JSMSG_EQUAL_AS_ASSIGN, - rewrite - ? "\nAssuming equality test" - : "")) { - return NULL; - } - if (rewrite) { - pn->pn_type = TOK_EQOP; - pn->pn_op = (JSOp)cx->jsop_eq; - pn2 = pn->pn_left; - switch (pn2->pn_op) { - case JSOP_SETNAME: - pn2->pn_op = JSOP_NAME; - break; - case JSOP_SETPROP: - pn2->pn_op = JSOP_GETPROP; - break; - case JSOP_SETELEM: - pn2->pn_op = JSOP_GETELEM; - break; - default: - JS_ASSERT(0); - } - } - } - return pn; -} - -static JSBool -MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn) -{ - JSAtom *label; - JSTokenType tt; - - tt = js_PeekTokenSameLine(cx, ts); - if (tt == TOK_ERROR) - return JS_FALSE; - if (tt == TOK_NAME) { - (void) js_GetToken(cx, ts); - label = CURRENT_TOKEN(ts).t_atom; - } else { - label = NULL; - } - pn->pn_atom = label; - return JS_TRUE; -} - -#if JS_HAS_EXPORT_IMPORT -static JSParseNode * -ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn, *pn2; - JSTokenType tt; - - MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME); - pn = NewParseNode(cx, ts, PN_NAME, tc); - if (!pn) - return NULL; - pn->pn_op = JSOP_NAME; - pn->pn_atom = CURRENT_TOKEN(ts).t_atom; - pn->pn_expr = NULL; - pn->pn_slot = -1; - pn->pn_attrs = 0; - - ts->flags |= TSF_OPERAND; - while ((tt = js_GetToken(cx, ts)) == TOK_DOT || tt == TOK_LB) { - ts->flags &= ~TSF_OPERAND; - if (pn->pn_op == JSOP_IMPORTALL) - goto bad_import; - - if (tt == TOK_DOT) { - pn2 = NewParseNode(cx, ts, PN_NAME, tc); - if (!pn2) - return NULL; - ts->flags |= TSF_KEYWORD_IS_NAME; - if (js_MatchToken(cx, ts, TOK_STAR)) { - pn2->pn_op = JSOP_IMPORTALL; - pn2->pn_atom = NULL; - pn2->pn_slot = -1; - pn2->pn_attrs = 0; - } else { - MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT); - pn2->pn_op = JSOP_GETPROP; - pn2->pn_atom = CURRENT_TOKEN(ts).t_atom; - pn2->pn_slot = -1; - pn2->pn_attrs = 0; - } - ts->flags &= ~TSF_KEYWORD_IS_NAME; - pn2->pn_expr = pn; - pn2->pn_pos.begin = pn->pn_pos.begin; - pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - } else { - /* Make a TOK_LB binary node. */ - pn2 = NewBinary(cx, tt, JSOP_GETELEM, pn, Expr(cx, ts, tc), tc); - if (!pn2) - return NULL; - - MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX); - } - - pn = pn2; - ts->flags |= TSF_OPERAND; - } - ts->flags &= ~TSF_OPERAND; - if (tt == TOK_ERROR) - return NULL; - js_UngetToken(ts); - - switch (pn->pn_op) { - case JSOP_GETPROP: - pn->pn_op = JSOP_IMPORTPROP; - break; - case JSOP_GETELEM: - pn->pn_op = JSOP_IMPORTELEM; - break; - case JSOP_IMPORTALL: - break; - default: - goto bad_import; - } - return pn; - - bad_import: - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_IMPORT); - return NULL; -} -#endif /* JS_HAS_EXPORT_IMPORT */ - -static JSBool -BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) -{ - JSObject *blockObj; - JSScopeProperty *sprop; - JSAtomListElement *ale; - - blockObj = data->obj; - sprop = SCOPE_GET_PROPERTY(OBJ_SCOPE(blockObj), ATOM_TO_JSID(atom)); - ATOM_LIST_SEARCH(ale, &tc->decls, atom); - if (sprop || (ale && ALE_JSOP(ale) == JSOP_DEFCONST)) { - const char *name; - - if (sprop) { - JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); - JS_ASSERT((uint16)sprop->shortid < data->u.let.index); - } - - name = js_AtomToPrintableString(cx, atom); - if (name) { - js_ReportCompileErrorNumber(cx, - BIND_DATA_REPORT_ARGS(data, - JSREPORT_ERROR), - JSMSG_REDECLARED_VAR, - (ale && ALE_JSOP(ale) == JSOP_DEFCONST) - ? js_const_str - : "variable", - name); - } - return JS_FALSE; - } - - if (data->u.let.index == JS_BIT(16)) { - js_ReportCompileErrorNumber(cx, - BIND_DATA_REPORT_ARGS(data, JSREPORT_ERROR), - data->u.let.overflow); - return JS_FALSE; - } - - /* Use JSPROP_ENUMERATE to aid the disassembler. */ - return js_DefineNativeProperty(cx, blockObj, ATOM_TO_JSID(atom), - JSVAL_VOID, NULL, NULL, - JSPROP_ENUMERATE | JSPROP_PERMANENT, - SPROP_HAS_SHORTID, - (intN)data->u.let.index++, - NULL); -} - -static JSBool -BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) -{ - JSStmtInfo *stmt; - JSAtomListElement *ale; - JSOp op, prevop; - const char *name; - JSFunction *fun; - JSObject *obj, *pobj; - JSProperty *prop; - JSBool ok; - JSPropertyOp getter, setter; - JSScopeProperty *sprop; - - stmt = js_LexicalLookup(tc, atom, NULL, JS_FALSE); - ATOM_LIST_SEARCH(ale, &tc->decls, atom); - op = data->op; - if ((stmt && stmt->type != STMT_WITH) || ale) { - prevop = ale ? ALE_JSOP(ale) : JSOP_DEFVAR; - if (JS_HAS_STRICT_OPTION(cx) - ? op != JSOP_DEFVAR || prevop != JSOP_DEFVAR - : op == JSOP_DEFCONST || prevop == JSOP_DEFCONST) { - name = js_AtomToPrintableString(cx, atom); - if (!name || - !js_ReportCompileErrorNumber(cx, - BIND_DATA_REPORT_ARGS(data, - (op != JSOP_DEFCONST && - prevop != JSOP_DEFCONST) - ? JSREPORT_WARNING | - JSREPORT_STRICT - : JSREPORT_ERROR), - JSMSG_REDECLARED_VAR, - (prevop == JSOP_DEFFUN || - prevop == JSOP_CLOSURE) - ? js_function_str - : (prevop == JSOP_DEFCONST) - ? js_const_str - : js_var_str, - name)) { - return JS_FALSE; - } - } - if (op == JSOP_DEFVAR && prevop == JSOP_CLOSURE) - tc->flags |= TCF_FUN_CLOSURE_VS_VAR; - } - if (!ale) { - ale = js_IndexAtom(cx, atom, &tc->decls); - if (!ale) - return JS_FALSE; - } - ALE_SET_JSOP(ale, op); - - fun = data->u.var.fun; - obj = data->obj; - if (!fun) { - /* Don't lookup global variables at compile time. */ - prop = NULL; - } else { - JS_ASSERT(OBJ_IS_NATIVE(obj)); - if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), - &pobj, &prop)) { - return JS_FALSE; - } - } - - ok = JS_TRUE; - getter = data->u.var.getter; - setter = data->u.var.setter; - - if (prop && pobj == obj && OBJ_IS_NATIVE(pobj)) { - sprop = (JSScopeProperty *)prop; - if (sprop->getter == js_GetArgument) { - name = js_AtomToPrintableString(cx, atom); - if (!name) { - ok = JS_FALSE; - } else if (op == JSOP_DEFCONST) { - js_ReportCompileErrorNumber(cx, - BIND_DATA_REPORT_ARGS(data, - JSREPORT_ERROR), - JSMSG_REDECLARED_PARAM, - name); - ok = JS_FALSE; - } else { - getter = js_GetArgument; - setter = js_SetArgument; - ok = js_ReportCompileErrorNumber(cx, - BIND_DATA_REPORT_ARGS(data, - JSREPORT_WARNING | - JSREPORT_STRICT), - JSMSG_VAR_HIDES_ARG, - name); - } - } else { - JS_ASSERT(getter == js_GetLocalVariable); - - if (fun) { - /* Not an argument, must be a redeclared local var. */ - if (data->u.var.clasp == &js_FunctionClass) { - JS_ASSERT(sprop->getter == js_GetLocalVariable); - JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) && - (uint16) sprop->shortid < fun->u.i.nvars); - } else if (data->u.var.clasp == &js_CallClass) { - if (sprop->getter == js_GetCallVariable) { - /* - * Referencing a name introduced by a var statement in - * the enclosing function. Check that the slot number - * we have is in range. - */ - JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) && - (uint16) sprop->shortid < fun->u.i.nvars); - } else { - /* - * A variable introduced through another eval: don't - * use the special getters and setters since we can't - * allocate a slot in the frame. - */ - getter = sprop->getter; - setter = sprop->setter; - } - } - - /* Override the old getter and setter, to handle eval. */ - sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, - 0, sprop->attrs, - getter, setter); - if (!sprop) - ok = JS_FALSE; - } - } - if (prop) - OBJ_DROP_PROPERTY(cx, pobj, prop); - } else { - /* - * Property not found in current variable scope: we have not seen this - * variable before. Define a new local variable by adding a property - * to the function's scope, allocating one slot in the function's vars - * frame. Global variables and any locals declared in with statement - * bodies are handled at runtime, by script prolog JSOP_DEFVAR opcodes - * generated for slot-less vars. - */ - sprop = NULL; - if (prop) { - OBJ_DROP_PROPERTY(cx, pobj, prop); - prop = NULL; - } - - if (cx->fp->scopeChain == obj && - !js_InWithStatement(tc) && - !BindLocalVariable(cx, data, atom)) { - return JS_FALSE; - } - } - return ok; -} - -#if JS_HAS_DESTRUCTURING - -static JSBool -BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn, - JSTreeContext *tc) -{ - JSAtom *atom; - - /* - * Destructuring is a form of assignment, so just as for an initialized - * simple variable, we must check for assignment to 'arguments' and flag - * the enclosing function (if any) as heavyweight. - */ - JS_ASSERT(pn->pn_type == TOK_NAME); - atom = pn->pn_atom; - if (atom == cx->runtime->atomState.argumentsAtom) - tc->flags |= TCF_FUN_HEAVYWEIGHT; - - data->pn = pn; - if (!data->binder(cx, data, atom, tc)) - return JS_FALSE; - data->pn = NULL; - - /* - * Select the appropriate name-setting opcode, which may be specialized - * further for local variable and argument slot optimizations. At this - * point, we can't select the optimal final opcode, yet we must preserve - * the CONST bit and convey "set", not "get". - */ - pn->pn_op = (data->op == JSOP_DEFCONST) - ? JSOP_SETCONST - : JSOP_SETNAME; - pn->pn_attrs = data->u.var.attrs; - return JS_TRUE; -} - -/* - * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any - * LHS expression except a destructuring initialiser, and R is on the stack. - * Because R is already evaluated, the usual LHS-specialized bytecodes won't - * work. After pushing R[P] we need to evaluate Q's "reference base" QB and - * then push its property name QN. At this point the stack looks like - * - * [... R, R[P], QB, QN] - * - * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes - * its operands with left-hand side above right-hand side: - * - * [rval, lval, xval] - * - * and pops all three values, setting lval[xval] = rval. But we cannot select - * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var, - * which can be optimized further. So we select JSOP_SETNAME. - */ -static JSBool -BindDestructuringLHS(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) -{ - while (pn->pn_type == TOK_RP) - pn = pn->pn_kid; - - switch (pn->pn_type) { - case TOK_NAME: - if (pn->pn_atom == cx->runtime->atomState.argumentsAtom) - tc->flags |= TCF_FUN_HEAVYWEIGHT; - /* FALL THROUGH */ - case TOK_DOT: - case TOK_LB: - pn->pn_op = JSOP_SETNAME; - break; - -#if JS_HAS_LVALUE_RETURN - case TOK_LP: - JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL); - pn->pn_op = JSOP_SETCALL; - break; -#endif - -#if JS_HAS_XML_SUPPORT - case TOK_UNARYOP: - if (pn->pn_op == JSOP_XMLNAME) { - pn->pn_op = JSOP_BINDXMLNAME; - break; - } - /* FALL THROUGH */ -#endif - - default: - js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, - JSMSG_BAD_LEFTSIDE_OF_ASS); - return JS_FALSE; - } - - return JS_TRUE; -} - -typedef struct FindPropValData { - uint32 numvars; /* # of destructuring vars in left side */ - uint32 maxstep; /* max # of steps searching right side */ - JSDHashTable table; /* hash table for O(1) right side search */ -} FindPropValData; - -typedef struct FindPropValEntry { - JSDHashEntryHdr hdr; - JSParseNode *pnkey; - JSParseNode *pnval; -} FindPropValEntry; - -#define ASSERT_VALID_PROPERTY_KEY(pnkey) \ - JS_ASSERT((pnkey)->pn_arity == PN_NULLARY && \ - ((pnkey)->pn_type == TOK_NUMBER || \ - (pnkey)->pn_type == TOK_STRING || \ - (pnkey)->pn_type == TOK_NAME)) - -JS_STATIC_DLL_CALLBACK(JSDHashNumber) -HashFindPropValKey(JSDHashTable *table, const void *key) -{ - const JSParseNode *pnkey = (const JSParseNode *)key; - - ASSERT_VALID_PROPERTY_KEY(pnkey); - return (pnkey->pn_type == TOK_NUMBER) - ? (JSDHashNumber) (JSDOUBLE_HI32(pnkey->pn_dval) ^ - JSDOUBLE_LO32(pnkey->pn_dval)) - : (JSDHashNumber) pnkey->pn_atom->number; -} - -JS_STATIC_DLL_CALLBACK(JSBool) -MatchFindPropValEntry(JSDHashTable *table, - const JSDHashEntryHdr *entry, - const void *key) -{ - const FindPropValEntry *fpve = (const FindPropValEntry *)entry; - const JSParseNode *pnkey = (const JSParseNode *)key; - - ASSERT_VALID_PROPERTY_KEY(pnkey); - return pnkey->pn_type == fpve->pnkey->pn_type && - ((pnkey->pn_type == TOK_NUMBER) - ? pnkey->pn_dval == fpve->pnkey->pn_dval - : pnkey->pn_atom == fpve->pnkey->pn_atom); -} - -static const JSDHashTableOps FindPropValOps = { - JS_DHashAllocTable, - JS_DHashFreeTable, - JS_DHashGetKeyStub, - HashFindPropValKey, - MatchFindPropValEntry, - JS_DHashMoveEntryStub, - JS_DHashClearEntryStub, - JS_DHashFinalizeStub, - NULL -}; - -#define STEP_HASH_THRESHOLD 10 -#define BIG_DESTRUCTURING 5 -#define BIG_OBJECT_INIT 20 - -static JSParseNode * -FindPropertyValue(JSParseNode *pn, JSParseNode *pnid, FindPropValData *data) -{ - FindPropValEntry *entry; - JSParseNode *pnhit, *pnprop, *pnkey; - uint32 step; - - /* If we have a hash table, use it as the sole source of truth. */ - if (data->table.ops) { - entry = (FindPropValEntry *) - JS_DHashTableOperate(&data->table, pnid, JS_DHASH_LOOKUP); - return JS_DHASH_ENTRY_IS_BUSY(&entry->hdr) ? entry->pnval : NULL; - } - - /* If pn is not an object initialiser node, we can't do anything here. */ - if (pn->pn_type != TOK_RC) - return NULL; - - /* - * We must search all the way through pn's list, to handle the case of an - * id duplicated for two or more property initialisers. - */ - pnhit = NULL; - step = 0; - ASSERT_VALID_PROPERTY_KEY(pnid); - if (pnid->pn_type == TOK_NUMBER) { - for (pnprop = pn->pn_head; pnprop; pnprop = pnprop->pn_next) { - JS_ASSERT(pnprop->pn_type == TOK_COLON); - if (pnprop->pn_op == JSOP_NOP) { - pnkey = pnprop->pn_left; - ASSERT_VALID_PROPERTY_KEY(pnkey); - if (pnkey->pn_type == TOK_NUMBER && - pnkey->pn_dval == pnid->pn_dval) { - pnhit = pnprop; - } - ++step; - } - } - } else { - for (pnprop = pn->pn_head; pnprop; pnprop = pnprop->pn_next) { - JS_ASSERT(pnprop->pn_type == TOK_COLON); - if (pnprop->pn_op == JSOP_NOP) { - pnkey = pnprop->pn_left; - ASSERT_VALID_PROPERTY_KEY(pnkey); - if (pnkey->pn_type == pnid->pn_type && - pnkey->pn_atom == pnid->pn_atom) { - pnhit = pnprop; - } - ++step; - } - } - } - if (!pnhit) - return NULL; - - /* Hit via full search -- see whether it's time to create the hash table. */ - JS_ASSERT(!data->table.ops); - if (step > data->maxstep) { - data->maxstep = step; - if (step >= STEP_HASH_THRESHOLD && - data->numvars >= BIG_DESTRUCTURING && - pn->pn_count >= BIG_OBJECT_INIT && - JS_DHashTableInit(&data->table, &FindPropValOps, pn, - sizeof(FindPropValEntry), pn->pn_count)) { - - for (pn = pn->pn_head; pn; pn = pn->pn_next) { - ASSERT_VALID_PROPERTY_KEY(pn->pn_left); - entry = (FindPropValEntry *) - JS_DHashTableOperate(&data->table, pn->pn_left, - JS_DHASH_ADD); - entry->pnval = pn->pn_right; - } - } - } - return pnhit->pn_right; -} - -/* - * If data is null, the caller is AssignExpr and instead of binding variables, - * we specialize lvalues in the propery value positions of the left-hand side. - * If right is null, just check for well-formed lvalues. - */ -static JSBool -CheckDestructuring(JSContext *cx, BindData *data, - JSParseNode *left, JSParseNode *right, - JSTreeContext *tc) -{ - JSBool ok; - FindPropValData fpvd; - JSParseNode *lhs, *rhs, *pn, *pn2; - - if (left->pn_type == TOK_ARRAYCOMP) { - js_ReportCompileErrorNumber(cx, left, JSREPORT_PN | JSREPORT_ERROR, - JSMSG_ARRAY_COMP_LEFTSIDE); - return JS_FALSE; - } - - ok = JS_TRUE; - fpvd.table.ops = NULL; - lhs = left->pn_head; - if (lhs && lhs->pn_type == TOK_DEFSHARP) { - pn = lhs; - goto no_var_name; - } - - if (left->pn_type == TOK_RB) { - rhs = (right && right->pn_type == left->pn_type) - ? right->pn_head - : NULL; - - while (lhs) { - pn = lhs, pn2 = rhs; - if (!data) { - /* Skip parenthesization if not in a variable declaration. */ - while (pn->pn_type == TOK_RP) - pn = pn->pn_kid; - if (pn2) { - while (pn2->pn_type == TOK_RP) - pn2 = pn2->pn_kid; - } - } - - /* Nullary comma is an elision; binary comma is an expression.*/ - if (pn->pn_type != TOK_COMMA || pn->pn_arity != PN_NULLARY) { - if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) { - ok = CheckDestructuring(cx, data, pn, pn2, tc); - } else { - if (data) { - if (pn->pn_type != TOK_NAME) - goto no_var_name; - - ok = BindDestructuringVar(cx, data, pn, tc); - } else { - ok = BindDestructuringLHS(cx, pn, tc); - } - } - if (!ok) - goto out; - } - - lhs = lhs->pn_next; - if (rhs) - rhs = rhs->pn_next; - } - } else { - JS_ASSERT(left->pn_type == TOK_RC); - fpvd.numvars = left->pn_count; - fpvd.maxstep = 0; - rhs = NULL; - - while (lhs) { - JS_ASSERT(lhs->pn_type == TOK_COLON); - pn = lhs->pn_right; - if (!data) { - /* Skip parenthesization if not in a variable declaration. */ - while (pn->pn_type == TOK_RP) - pn = pn->pn_kid; - } - - if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) { - if (right) { - rhs = FindPropertyValue(right, lhs->pn_left, &fpvd); - if (rhs && !data) { - while (rhs->pn_type == TOK_RP) - rhs = rhs->pn_kid; - } - } - - ok = CheckDestructuring(cx, data, pn, rhs, tc); - } else if (data) { - if (pn->pn_type != TOK_NAME) - goto no_var_name; - - ok = BindDestructuringVar(cx, data, pn, tc); - } else { - ok = BindDestructuringLHS(cx, pn, tc); - } - if (!ok) - goto out; - - lhs = lhs->pn_next; - } - } - -out: - if (fpvd.table.ops) - JS_DHashTableFinish(&fpvd.table); - return ok; - -no_var_name: - js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, - JSMSG_NO_VARIABLE_NAME); - ok = JS_FALSE; - goto out; -} - -static JSParseNode * -DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc, - JSTokenType tt) -{ - JSParseNode *pn; - - pn = PrimaryExpr(cx, data->ts, tc, tt, JS_FALSE); - if (!pn) - return NULL; - if (!CheckDestructuring(cx, data, pn, NULL, tc)) - return NULL; - return pn; -} - -#endif /* JS_HAS_DESTRUCTURING */ - -extern const char js_with_statement_str[]; - -static JSParseNode * -ContainsStmt(JSParseNode *pn, JSTokenType tt) -{ - JSParseNode *pn2, *pnt; - - if (!pn) - return NULL; - if (pn->pn_type == tt) - return pn; - switch (pn->pn_arity) { - case PN_LIST: - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - pnt = ContainsStmt(pn2, tt); - if (pnt) - return pnt; - } - break; - case PN_TERNARY: - pnt = ContainsStmt(pn->pn_kid1, tt); - if (pnt) - return pnt; - pnt = ContainsStmt(pn->pn_kid2, tt); - if (pnt) - return pnt; - return ContainsStmt(pn->pn_kid3, tt); - case PN_BINARY: - /* - * Limit recursion if pn is a binary expression, which can't contain a - * var statement. - */ - if (pn->pn_op != JSOP_NOP) - return NULL; - pnt = ContainsStmt(pn->pn_left, tt); - if (pnt) - return pnt; - return ContainsStmt(pn->pn_right, tt); - case PN_UNARY: - if (pn->pn_op != JSOP_NOP) - return NULL; - return ContainsStmt(pn->pn_kid, tt); - case PN_NAME: - return ContainsStmt(pn->pn_expr, tt); - default:; - } - return NULL; -} - -static JSParseNode * -ReturnOrYield(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSParser operandParser) -{ - JSTokenType tt, tt2; - JSParseNode *pn, *pn2; - - tt = CURRENT_TOKEN(ts).type; - if (!(tc->flags & TCF_IN_FUNCTION)) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_RETURN_OR_YIELD, -#if JS_HAS_GENERATORS - (tt == TOK_YIELD) ? js_yield_str : -#endif - js_return_str); - return NULL; - } - - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - -#if JS_HAS_GENERATORS - if (tt == TOK_YIELD) - tc->flags |= TCF_FUN_IS_GENERATOR; -#endif - - /* This is ugly, but we don't want to require a semicolon. */ - ts->flags |= TSF_OPERAND; - tt2 = js_PeekTokenSameLine(cx, ts); - ts->flags &= ~TSF_OPERAND; - if (tt2 == TOK_ERROR) - return NULL; - - if (tt2 != TOK_EOF && tt2 != TOK_EOL && tt2 != TOK_SEMI && tt2 != TOK_RC -#if JS_HAS_GENERATORS - && (tt != TOK_YIELD || (tt2 != tt && tt2 != TOK_RB && tt2 != TOK_RP)) -#endif - ) { - pn2 = operandParser(cx, ts, tc); - if (!pn2) - return NULL; -#if JS_HAS_GENERATORS - if (tt == TOK_RETURN) -#endif - tc->flags |= TCF_RETURN_EXPR; - pn->pn_pos.end = pn2->pn_pos.end; - pn->pn_kid = pn2; - } else { -#if JS_HAS_GENERATORS - if (tt == TOK_RETURN) -#endif - tc->flags |= TCF_RETURN_VOID; - pn->pn_kid = NULL; - } - - if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) { - /* As in Python (see PEP-255), disallow return v; in generators. */ - ReportBadReturn(cx, ts, JSREPORT_ERROR, - JSMSG_BAD_GENERATOR_RETURN, - JSMSG_BAD_ANON_GENERATOR_RETURN); - return NULL; - } - - if (JS_HAS_STRICT_OPTION(cx) && - (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 && - !ReportBadReturn(cx, ts, JSREPORT_WARNING | JSREPORT_STRICT, - JSMSG_NO_RETURN_VALUE, - JSMSG_ANON_NO_RETURN_VALUE)) { - return NULL; - } - - return pn; -} - -static JSParseNode * -PushLexicalScope(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSStmtInfo *stmtInfo) -{ - JSParseNode *pn; - JSObject *obj; - JSAtom *atom; - - pn = NewParseNode(cx, ts, PN_NAME, tc); - if (!pn) - return NULL; - - obj = js_NewBlockObject(cx); - if (!obj) - return NULL; - - atom = js_AtomizeObject(cx, obj, 0); - if (!atom) - return NULL; - - js_PushBlockScope(tc, stmtInfo, atom, -1); - pn->pn_type = TOK_LEXICALSCOPE; - pn->pn_op = JSOP_LEAVEBLOCK; - pn->pn_atom = atom; - pn->pn_expr = NULL; - pn->pn_slot = -1; - pn->pn_attrs = 0; - return pn; -} - -#if JS_HAS_BLOCK_SCOPE - -static JSParseNode * -LetBlock(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool statement) -{ - JSParseNode *pn, *pnblock, *pnlet; - JSStmtInfo stmtInfo; - - JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LET); - - /* Create the let binary node. */ - pnlet = NewParseNode(cx, ts, PN_BINARY, tc); - if (!pnlet) - return NULL; - - MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET); - - /* This is a let block or expression of the form: let (a, b, c) .... */ - pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo); - if (!pnblock) - return NULL; - pn = pnblock; - pn->pn_expr = pnlet; - - pnlet->pn_left = Variables(cx, ts, tc); - if (!pnlet->pn_left) - return NULL; - pnlet->pn_left->pn_extra = PNX_POPVAR; - - MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET); - - ts->flags |= TSF_OPERAND; - if (statement && !js_MatchToken(cx, ts, TOK_LC)) { - /* - * If this is really an expression in let statement guise, then we - * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop - * the return value of the expression. - */ - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - pn->pn_type = TOK_SEMI; - pn->pn_num = -1; - pn->pn_kid = pnblock; - - statement = JS_FALSE; - } - ts->flags &= ~TSF_OPERAND; - - if (statement) { - pnlet->pn_right = Statements(cx, ts, tc); - if (!pnlet->pn_right) - return NULL; - MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET); - } else { - /* - * Change pnblock's opcode to the variant that propagates the last - * result down after popping the block, and clear statement. - */ - pnblock->pn_op = JSOP_LEAVEBLOCKEXPR; - pnlet->pn_right = Expr(cx, ts, tc); - if (!pnlet->pn_right) - return NULL; - } - - js_PopStatement(tc); - return pn; -} - -#endif /* JS_HAS_BLOCK_SCOPE */ - -static JSParseNode * -Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSTokenType tt; - JSParseNode *pn, *pn1, *pn2, *pn3, *pn4; - JSStmtInfo stmtInfo, *stmt, *stmt2; - JSAtom *label; - - CHECK_RECURSION(); - - ts->flags |= TSF_OPERAND; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_OPERAND; - -#if JS_HAS_GETTER_SETTER - if (tt == TOK_NAME) { - tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION); - if (tt == TOK_ERROR) - return NULL; - } -#endif - - switch (tt) { -#if JS_HAS_EXPORT_IMPORT - case TOK_EXPORT: - pn = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn) - return NULL; - PN_INIT_LIST(pn); - if (js_MatchToken(cx, ts, TOK_STAR)) { - pn2 = NewParseNode(cx, ts, PN_NULLARY, tc); - if (!pn2) - return NULL; - PN_APPEND(pn, pn2); - } else { - do { - MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_EXPORT_NAME); - pn2 = NewParseNode(cx, ts, PN_NAME, tc); - if (!pn2) - return NULL; - pn2->pn_op = JSOP_NAME; - pn2->pn_atom = CURRENT_TOKEN(ts).t_atom; - pn2->pn_expr = NULL; - pn2->pn_slot = -1; - pn2->pn_attrs = 0; - PN_APPEND(pn, pn2); - } while (js_MatchToken(cx, ts, TOK_COMMA)); - } - pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; - tc->flags |= TCF_FUN_HEAVYWEIGHT; - break; - - case TOK_IMPORT: - pn = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn) - return NULL; - PN_INIT_LIST(pn); - do { - pn2 = ImportExpr(cx, ts, tc); - if (!pn2) - return NULL; - PN_APPEND(pn, pn2); - } while (js_MatchToken(cx, ts, TOK_COMMA)); - pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; - tc->flags |= TCF_FUN_HEAVYWEIGHT; - break; -#endif /* JS_HAS_EXPORT_IMPORT */ - - case TOK_FUNCTION: -#if JS_HAS_XML_SUPPORT - ts->flags |= TSF_KEYWORD_IS_NAME; - tt = js_PeekToken(cx, ts); - ts->flags &= ~TSF_KEYWORD_IS_NAME; - if (tt == TOK_DBLCOLON) - goto expression; -#endif - return FunctionStmt(cx, ts, tc); - - case TOK_IF: - /* An IF node has three kids: condition, then, and optional else. */ - pn = NewParseNode(cx, ts, PN_TERNARY, tc); - if (!pn) - return NULL; - pn1 = Condition(cx, ts, tc); - if (!pn1) - return NULL; - js_PushStatement(tc, &stmtInfo, STMT_IF, -1); - pn2 = Statement(cx, ts, tc); - if (!pn2) - return NULL; - ts->flags |= TSF_OPERAND; - if (js_MatchToken(cx, ts, TOK_ELSE)) { - ts->flags &= ~TSF_OPERAND; - stmtInfo.type = STMT_ELSE; - pn3 = Statement(cx, ts, tc); - if (!pn3) - return NULL; - pn->pn_pos.end = pn3->pn_pos.end; - } else { - ts->flags &= ~TSF_OPERAND; - pn3 = NULL; - pn->pn_pos.end = pn2->pn_pos.end; - } - js_PopStatement(tc); - pn->pn_kid1 = pn1; - pn->pn_kid2 = pn2; - pn->pn_kid3 = pn3; - return pn; - - case TOK_SWITCH: - { - JSParseNode *pn5, *saveBlock; - JSBool seenDefault = JS_FALSE; - - pn = NewParseNode(cx, ts, PN_BINARY, tc); - if (!pn) - return NULL; - MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH); - - /* pn1 points to the switch's discriminant. */ - pn1 = Expr(cx, ts, tc); - if (!pn1) - return NULL; - - MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH); - MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH); - - /* pn2 is a list of case nodes. The default case has pn_left == NULL */ - pn2 = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn2) - return NULL; - saveBlock = tc->blockNode; - tc->blockNode = pn2; - PN_INIT_LIST(pn2); - - js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1); - - while ((tt = js_GetToken(cx, ts)) != TOK_RC) { - switch (tt) { - case TOK_DEFAULT: - if (seenDefault) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_TOO_MANY_DEFAULTS); - return NULL; - } - seenDefault = JS_TRUE; - /* fall through */ - - case TOK_CASE: - pn3 = NewParseNode(cx, ts, PN_BINARY, tc); - if (!pn3) - return NULL; - if (tt == TOK_DEFAULT) { - pn3->pn_left = NULL; - } else { - pn3->pn_left = Expr(cx, ts, tc); - if (!pn3->pn_left) - return NULL; - } - PN_APPEND(pn2, pn3); - if (pn2->pn_count == JS_BIT(16)) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_TOO_MANY_CASES); - return NULL; - } - break; - - case TOK_ERROR: - return NULL; - - default: - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_SWITCH); - return NULL; - } - MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE); - - pn4 = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn4) - return NULL; - pn4->pn_type = TOK_LC; - PN_INIT_LIST(pn4); - ts->flags |= TSF_OPERAND; - while ((tt = js_PeekToken(cx, ts)) != TOK_RC && - tt != TOK_CASE && tt != TOK_DEFAULT) { - ts->flags &= ~TSF_OPERAND; - if (tt == TOK_ERROR) - return NULL; - pn5 = Statement(cx, ts, tc); - if (!pn5) - return NULL; - pn4->pn_pos.end = pn5->pn_pos.end; - PN_APPEND(pn4, pn5); - ts->flags |= TSF_OPERAND; - } - ts->flags &= ~TSF_OPERAND; - - /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */ - if (pn4->pn_head) - pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin; - pn3->pn_pos.end = pn4->pn_pos.end; - pn3->pn_right = pn4; - } - - /* - * Handle the case where there was a let declaration in any case in - * the switch body, but not within an inner block. If it replaced - * tc->blockNode with a new block node then we must refresh pn2 and - * then restore tc->blockNode. - */ - if (tc->blockNode != pn2) - pn2 = tc->blockNode; - tc->blockNode = saveBlock; - js_PopStatement(tc); - - pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - pn->pn_left = pn1; - pn->pn_right = pn2; - return pn; - } - - case TOK_WHILE: - pn = NewParseNode(cx, ts, PN_BINARY, tc); - if (!pn) - return NULL; - js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1); - pn2 = Condition(cx, ts, tc); - if (!pn2) - return NULL; - pn->pn_left = pn2; - pn2 = Statement(cx, ts, tc); - if (!pn2) - return NULL; - js_PopStatement(tc); - pn->pn_pos.end = pn2->pn_pos.end; - pn->pn_right = pn2; - return pn; - - case TOK_DO: - pn = NewParseNode(cx, ts, PN_BINARY, tc); - if (!pn) - return NULL; - js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1); - pn2 = Statement(cx, ts, tc); - if (!pn2) - return NULL; - pn->pn_left = pn2; - MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO); - pn2 = Condition(cx, ts, tc); - if (!pn2) - return NULL; - js_PopStatement(tc); - pn->pn_pos.end = pn2->pn_pos.end; - pn->pn_right = pn2; - if ((cx->version & JSVERSION_MASK) != JSVERSION_ECMA_3) { - /* - * All legacy and extended versions must do automatic semicolon - * insertion after do-while. See the testcase and discussion in - * http://bugzilla.mozilla.org/show_bug.cgi?id=238945. - */ - if (!js_MatchToken(cx, ts, TOK_SEMI)) - pn->pn_no_semi = JS_TRUE; - return pn; - } - break; - - case TOK_FOR: - { -#if JS_HAS_BLOCK_SCOPE - JSParseNode *pnlet; - JSStmtInfo blockInfo; - - pnlet = NULL; -#endif - - /* A FOR node is binary, left is loop control and right is the body. */ - pn = NewParseNode(cx, ts, PN_BINARY, tc); - if (!pn) - return NULL; - js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1); - - pn->pn_op = JSOP_FORIN; - if (js_MatchToken(cx, ts, TOK_NAME)) { - if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom) - pn->pn_op = JSOP_FOREACH; - else - js_UngetToken(ts); - } - - MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); - ts->flags |= TSF_OPERAND; - tt = js_PeekToken(cx, ts); - ts->flags &= ~TSF_OPERAND; - if (tt == TOK_SEMI) { - if (pn->pn_op == JSOP_FOREACH) - goto bad_for_each; - - /* No initializer -- set first kid of left sub-node to null. */ - pn1 = NULL; - } else { - /* - * Set pn1 to a var list or an initializing expression. - * - * Set the TCF_IN_FOR_INIT flag during parsing of the first clause - * of the for statement. This flag will be used by the RelExpr - * production; if it is set, then the 'in' keyword will not be - * recognized as an operator, leaving it available to be parsed as - * part of a for/in loop. - * - * A side effect of this restriction is that (unparenthesized) - * expressions involving an 'in' operator are illegal in the init - * clause of an ordinary for loop. - */ - tc->flags |= TCF_IN_FOR_INIT; - if (tt == TOK_VAR) { - (void) js_GetToken(cx, ts); - pn1 = Variables(cx, ts, tc); -#if JS_HAS_BLOCK_SCOPE - } else if (tt == TOK_LET) { - (void) js_GetToken(cx, ts); - if (js_PeekToken(cx, ts) == TOK_LP) { - pn1 = LetBlock(cx, ts, tc, JS_FALSE); - tt = TOK_LEXICALSCOPE; - } else { - pnlet = PushLexicalScope(cx, ts, tc, &blockInfo); - if (!pnlet) - return NULL; - pn1 = Variables(cx, ts, tc); - } -#endif - } else { - pn1 = Expr(cx, ts, tc); - if (pn1) { - while (pn1->pn_type == TOK_RP) - pn1 = pn1->pn_kid; - } - } - tc->flags &= ~TCF_IN_FOR_INIT; - if (!pn1) - return NULL; - } - - /* - * We can be sure that it's a for/in loop if there's still an 'in' - * keyword here, even if JavaScript recognizes 'in' as an operator, - * as we've excluded 'in' from being parsed in RelExpr by setting - * the TCF_IN_FOR_INIT flag in our JSTreeContext. - */ - if (pn1 && js_MatchToken(cx, ts, TOK_IN)) { - stmtInfo.type = STMT_FOR_IN_LOOP; - - /* Check that the left side of the 'in' is valid. */ - JS_ASSERT(!TOKEN_TYPE_IS_DECL(tt) || pn1->pn_type == tt); - if (TOKEN_TYPE_IS_DECL(tt) - ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST -#if JS_HAS_DESTRUCTURING - || (pn->pn_op == JSOP_FORIN && - (pn1->pn_head->pn_type == TOK_RC || - (pn1->pn_head->pn_type == TOK_RB && - pn1->pn_head->pn_count != 2) || - (pn1->pn_head->pn_type == TOK_ASSIGN && - (pn1->pn_head->pn_left->pn_type != TOK_RB || - pn1->pn_head->pn_left->pn_count != 2)))) -#endif - ) - : (pn1->pn_type != TOK_NAME && - pn1->pn_type != TOK_DOT && -#if JS_HAS_DESTRUCTURING - ((pn->pn_op == JSOP_FORIN) - ? (pn1->pn_type != TOK_RB || pn1->pn_count != 2) - : (pn1->pn_type != TOK_RB && pn1->pn_type != TOK_RC)) && -#endif -#if JS_HAS_LVALUE_RETURN - pn1->pn_type != TOK_LP && -#endif -#if JS_HAS_XML_SUPPORT - (pn1->pn_type != TOK_UNARYOP || - pn1->pn_op != JSOP_XMLNAME) && -#endif - pn1->pn_type != TOK_LB)) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_FOR_LEFTSIDE); - return NULL; - } - - if (TOKEN_TYPE_IS_DECL(tt)) { - /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */ - pn1->pn_extra |= PNX_FORINVAR; - - /* - * Generate a final POP only if the variable is a simple name - * (which means it is not a destructuring left-hand side) and - * it has an initializer. - */ - pn2 = pn1->pn_head; - if (pn2->pn_type == TOK_NAME && pn2->pn_expr) - pn1->pn_extra |= PNX_POPVAR; - } else { - pn2 = pn1; -#if JS_HAS_LVALUE_RETURN - if (pn2->pn_type == TOK_LP) - pn2->pn_op = JSOP_SETCALL; -#endif -#if JS_HAS_XML_SUPPORT - if (pn2->pn_type == TOK_UNARYOP) - pn2->pn_op = JSOP_BINDXMLNAME; -#endif - } - - switch (pn2->pn_type) { - case TOK_NAME: - /* Beware 'for (arguments in ...)' with or without a 'var'. */ - if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom) - tc->flags |= TCF_FUN_HEAVYWEIGHT; - break; - -#if JS_HAS_DESTRUCTURING - case TOK_ASSIGN: - pn2 = pn2->pn_left; - JS_ASSERT(pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC); - /* FALL THROUGH */ - case TOK_RB: - case TOK_RC: - /* Check for valid lvalues in var-less destructuring for-in. */ - if (pn1 == pn2 && !CheckDestructuring(cx, NULL, pn2, NULL, tc)) - return NULL; - - /* Destructuring for-in requires [key, value] enumeration. */ - if (pn->pn_op != JSOP_FOREACH) - pn->pn_op = JSOP_FOREACHKEYVAL; - break; -#endif - - default:; - } - - /* Parse the object expression as the right operand of 'in'. */ - pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc), tc); - if (!pn2) - return NULL; - pn->pn_left = pn2; - } else { - if (pn->pn_op == JSOP_FOREACH) - goto bad_for_each; - pn->pn_op = JSOP_NOP; - - /* Parse the loop condition or null into pn2. */ - MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT); - ts->flags |= TSF_OPERAND; - tt = js_PeekToken(cx, ts); - ts->flags &= ~TSF_OPERAND; - if (tt == TOK_SEMI) { - pn2 = NULL; - } else { - pn2 = Expr(cx, ts, tc); - if (!pn2) - return NULL; - } - - /* Parse the update expression or null into pn3. */ - MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND); - ts->flags |= TSF_OPERAND; - tt = js_PeekToken(cx, ts); - ts->flags &= ~TSF_OPERAND; - if (tt == TOK_RP) { - pn3 = NULL; - } else { - pn3 = Expr(cx, ts, tc); - if (!pn3) - return NULL; - } - - /* Build the RESERVED node to use as the left kid of pn. */ - pn4 = NewParseNode(cx, ts, PN_TERNARY, tc); - if (!pn4) - return NULL; - pn4->pn_type = TOK_RESERVED; - pn4->pn_op = JSOP_NOP; - pn4->pn_kid1 = pn1; - pn4->pn_kid2 = pn2; - pn4->pn_kid3 = pn3; - pn->pn_left = pn4; - } - - MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL); - - /* Parse the loop body into pn->pn_right. */ - pn2 = Statement(cx, ts, tc); - if (!pn2) - return NULL; - pn->pn_right = pn2; - - /* Record the absolute line number for source note emission. */ - pn->pn_pos.end = pn2->pn_pos.end; - -#if JS_HAS_BLOCK_SCOPE - if (pnlet) { - js_PopStatement(tc); - pnlet->pn_expr = pn; - pn = pnlet; - } -#endif - js_PopStatement(tc); - return pn; - - bad_for_each: - js_ReportCompileErrorNumber(cx, pn, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_BAD_FOR_EACH_LOOP); - return NULL; - } - - case TOK_TRY: { - JSParseNode *catchList, *lastCatch; - - /* - * try nodes are ternary. - * kid1 is the try Statement - * kid2 is the catch node list or null - * kid3 is the finally Statement - * - * catch nodes are ternary. - * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC) - * kid2 is the catch guard or null if no guard - * kid3 is the catch block - * - * catch lvalue nodes are either: - * TOK_NAME for a single identifier - * TOK_RB or TOK_RC for a destructuring left-hand side - * - * finally nodes are TOK_LC Statement lists. - */ - pn = NewParseNode(cx, ts, PN_TERNARY, tc); - if (!pn) - return NULL; - pn->pn_op = JSOP_NOP; - - MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY); - js_PushStatement(tc, &stmtInfo, STMT_TRY, -1); - pn->pn_kid1 = Statements(cx, ts, tc); - if (!pn->pn_kid1) - return NULL; - MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY); - js_PopStatement(tc); - - catchList = NULL; - tt = js_GetToken(cx, ts); - if (tt == TOK_CATCH) { - catchList = NewParseNode(cx, ts, PN_LIST, tc); - if (!catchList) - return NULL; - catchList->pn_type = TOK_RESERVED; - PN_INIT_LIST(catchList); - lastCatch = NULL; - - do { - JSParseNode *pnblock; - BindData data; - - /* Check for another catch after unconditional catch. */ - if (lastCatch && !lastCatch->pn_kid2) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_CATCH_AFTER_GENERAL); - return NULL; - } - - /* - * Create a lexical scope node around the whole catch clause, - * including the head. - */ - pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo); - if (!pnblock) - return NULL; - stmtInfo.type = STMT_CATCH; - - /* - * Legal catch forms are: - * catch (lhs) - * catch (lhs if ) - * where lhs is a name or a destructuring left-hand side. - * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD) - */ - pn2 = NewParseNode(cx, ts, PN_TERNARY, tc); - if (!pn2) - return NULL; - pnblock->pn_expr = pn2; - MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH); - - /* - * Contrary to ECMA Ed. 3, the catch variable is lexically - * scoped, not a property of a new Object instance. This is - * an intentional change that anticipates ECMA Ed. 4. - */ - data.pn = NULL; - data.ts = ts; - data.obj = tc->blockChain; - data.op = JSOP_NOP; - data.binder = BindLet; - data.u.let.index = 0; - data.u.let.overflow = JSMSG_TOO_MANY_CATCH_VARS; - - tt = js_GetToken(cx, ts); - switch (tt) { -#if JS_HAS_DESTRUCTURING - case TOK_LB: - case TOK_LC: - pn3 = DestructuringExpr(cx, &data, tc, tt); - if (!pn3) - return NULL; - break; -#endif - - case TOK_NAME: - label = CURRENT_TOKEN(ts).t_atom; - if (!data.binder(cx, &data, label, tc)) - return NULL; - - pn3 = NewParseNode(cx, ts, PN_NAME, tc); - if (!pn3) - return NULL; - pn3->pn_atom = label; - pn3->pn_expr = NULL; - pn3->pn_slot = 0; - pn3->pn_attrs = 0; - break; - - default: - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_CATCH_IDENTIFIER); - return NULL; - } - - pn2->pn_kid1 = pn3; - pn2->pn_kid2 = NULL; -#if JS_HAS_CATCH_GUARD - /* - * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)') - * to avoid conflicting with the JS2/ECMAv4 type annotation - * catchguard syntax. - */ - if (js_MatchToken(cx, ts, TOK_IF)) { - pn2->pn_kid2 = Expr(cx, ts, tc); - if (!pn2->pn_kid2) - return NULL; - } -#endif - MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH); - - MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH); - pn2->pn_kid3 = Statements(cx, ts, tc); - if (!pn2->pn_kid3) - return NULL; - MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH); - js_PopStatement(tc); - - PN_APPEND(catchList, pnblock); - lastCatch = pn2; - ts->flags |= TSF_OPERAND; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_OPERAND; - } while (tt == TOK_CATCH); - } - pn->pn_kid2 = catchList; - - if (tt == TOK_FINALLY) { - tc->tryCount++; - MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY); - js_PushStatement(tc, &stmtInfo, STMT_FINALLY, -1); - pn->pn_kid3 = Statements(cx, ts, tc); - if (!pn->pn_kid3) - return NULL; - MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY); - js_PopStatement(tc); - } else { - js_UngetToken(ts); - pn->pn_kid3 = NULL; - } - if (!catchList && !pn->pn_kid3) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_CATCH_OR_FINALLY); - return NULL; - } - tc->tryCount++; - return pn; - } - - case TOK_THROW: - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - - /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */ - ts->flags |= TSF_OPERAND; - tt = js_PeekTokenSameLine(cx, ts); - ts->flags &= ~TSF_OPERAND; - if (tt == TOK_ERROR) - return NULL; - if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_SYNTAX_ERROR); - return NULL; - } - - pn2 = Expr(cx, ts, tc); - if (!pn2) - return NULL; - pn->pn_pos.end = pn2->pn_pos.end; - pn->pn_op = JSOP_THROW; - pn->pn_kid = pn2; - break; - - /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */ - case TOK_CATCH: - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_CATCH_WITHOUT_TRY); - return NULL; - - case TOK_FINALLY: - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_FINALLY_WITHOUT_TRY); - return NULL; - - case TOK_BREAK: - pn = NewParseNode(cx, ts, PN_NULLARY, tc); - if (!pn) - return NULL; - if (!MatchLabel(cx, ts, pn)) - return NULL; - stmt = tc->topStmt; - label = pn->pn_atom; - if (label) { - for (; ; stmt = stmt->down) { - if (!stmt) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_LABEL_NOT_FOUND); - return NULL; - } - if (stmt->type == STMT_LABEL && stmt->atom == label) - break; - } - } else { - for (; ; stmt = stmt->down) { - if (!stmt) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_TOUGH_BREAK); - return NULL; - } - if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH) - break; - } - } - if (label) - pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - break; - - case TOK_CONTINUE: - pn = NewParseNode(cx, ts, PN_NULLARY, tc); - if (!pn) - return NULL; - if (!MatchLabel(cx, ts, pn)) - return NULL; - stmt = tc->topStmt; - label = pn->pn_atom; - if (label) { - for (stmt2 = NULL; ; stmt = stmt->down) { - if (!stmt) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_LABEL_NOT_FOUND); - return NULL; - } - if (stmt->type == STMT_LABEL) { - if (stmt->atom == label) { - if (!stmt2 || !STMT_IS_LOOP(stmt2)) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | - JSREPORT_ERROR, - JSMSG_BAD_CONTINUE); - return NULL; - } - break; - } - } else { - stmt2 = stmt; - } - } - } else { - for (; ; stmt = stmt->down) { - if (!stmt) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_CONTINUE); - return NULL; - } - if (STMT_IS_LOOP(stmt)) - break; - } - } - if (label) - pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - break; - - case TOK_WITH: - pn = NewParseNode(cx, ts, PN_BINARY, tc); - if (!pn) - return NULL; - MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH); - pn2 = Expr(cx, ts, tc); - if (!pn2) - return NULL; - MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH); - pn->pn_left = pn2; - - js_PushStatement(tc, &stmtInfo, STMT_WITH, -1); - pn2 = Statement(cx, ts, tc); - if (!pn2) - return NULL; - js_PopStatement(tc); - - pn->pn_pos.end = pn2->pn_pos.end; - pn->pn_right = pn2; - tc->flags |= TCF_FUN_HEAVYWEIGHT; - return pn; - - case TOK_VAR: - pn = Variables(cx, ts, tc); - if (!pn) - return NULL; - - /* Tell js_EmitTree to generate a final POP. */ - pn->pn_extra |= PNX_POPVAR; - break; - -#if JS_HAS_BLOCK_SCOPE - case TOK_LET: - { - JSStmtInfo **sip; - JSObject *obj; - JSAtom *atom; - - /* Check for a let statement or let expression. */ - if (js_PeekToken(cx, ts) == TOK_LP) { - pn = LetBlock(cx, ts, tc, JS_TRUE); - if (!pn || pn->pn_op == JSOP_LEAVEBLOCK) - return pn; - - /* Let expressions require automatic semicolon insertion. */ - JS_ASSERT(pn->pn_type == TOK_SEMI || - pn->pn_op == JSOP_LEAVEBLOCKEXPR); - break; - } - - /* - * This is a let declaration. We must convert the nearest JSStmtInfo - * that is a block or a switch body to be our scope statement. Further - * let declarations in this block will find this scope statement and - * use the same block object. If we are the first let declaration in - * this block (i.e., when the nearest maybe-scope JSStmtInfo isn't a - * scope statement) then we also need to set tc->blockNode to be our - * TOK_LEXICALSCOPE. - */ - sip = &tc->topScopeStmt; - for (stmt = tc->topStmt; stmt; stmt = stmt->down) { - if (STMT_MAYBE_SCOPE(stmt)) - break; - if (stmt == *sip) - sip = &stmt->downScope; - } - - if (stmt && (stmt->flags & SIF_SCOPE)) { - JS_ASSERT(tc->blockChain == ATOM_TO_OBJECT(stmt->atom)); - obj = tc->blockChain; - } else { - if (!stmt) { - /* - * FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=346749 - * - * This is a hard case that requires more work. In particular, - * in many cases, we're trying to emit code as we go. However, - * this means that we haven't necessarily finished processing - * all let declarations in the implicit top-level block when - * we emit a reference to one of them. For now, punt on this - * and pretend this is a var declaration. - */ - CURRENT_TOKEN(ts).type = TOK_VAR; - CURRENT_TOKEN(ts).t_op = JSOP_DEFVAR; - - pn = Variables(cx, ts, tc); - if (!pn) - return NULL; - pn->pn_extra |= PNX_POPVAR; - break; - } - - /* Convert the block statement into a scope statement. */ - obj = js_NewBlockObject(cx); - if (!obj) - return NULL; - atom = js_AtomizeObject(cx, obj, 0); - if (!atom) - return NULL; - - /* - * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked - * list stack, if it isn't already there. If it is there, but it - * lacks the SIF_SCOPE flag, it must be a try, catch, or finally - * block. - */ - JS_ASSERT(!(stmt->flags & SIF_SCOPE)); - stmt->flags |= SIF_SCOPE; - if (stmt != *sip) { - JS_ASSERT(!stmt->downScope); - JS_ASSERT(stmt->type == STMT_BLOCK || - stmt->type == STMT_SWITCH || - stmt->type == STMT_TRY || - stmt->type == STMT_FINALLY); - stmt->downScope = *sip; - *sip = stmt; - } else { - JS_ASSERT(stmt->type == STMT_CATCH); - JS_ASSERT(stmt->downScope); - } - - obj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(tc->blockChain); - tc->blockChain = obj; - stmt->atom = atom; - -#ifdef DEBUG - pn1 = tc->blockNode; - JS_ASSERT(!pn1 || pn1->pn_type != TOK_LEXICALSCOPE); -#endif - - /* Create a new lexical scope node for these statements. */ - pn1 = NewParseNode(cx, ts, PN_NAME, tc); - if (!pn1) - return NULL; - - pn1->pn_type = TOK_LEXICALSCOPE; - pn1->pn_op = JSOP_LEAVEBLOCK; - pn1->pn_pos = tc->blockNode->pn_pos; - pn1->pn_atom = atom; - pn1->pn_expr = tc->blockNode; - pn1->pn_slot = -1; - pn1->pn_attrs = 0; - tc->blockNode = pn1; - } - - pn = Variables(cx, ts, tc); - if (!pn) - return NULL; - pn->pn_extra = PNX_POPVAR; - break; - } -#endif /* JS_HAS_BLOCK_SCOPE */ - - case TOK_RETURN: - pn = ReturnOrYield(cx, ts, tc, Expr); - if (!pn) - return NULL; - break; - - case TOK_LC: - { - uintN oldflags; - - oldflags = tc->flags; - tc->flags = oldflags & ~TCF_HAS_FUNCTION_STMT; - js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1); - pn = Statements(cx, ts, tc); - if (!pn) - return NULL; - - MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND); - js_PopStatement(tc); - - /* - * If we contain a function statement and our container is top-level - * or another block, flag pn to preserve braces when decompiling. - */ - if ((tc->flags & TCF_HAS_FUNCTION_STMT) && - (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)) { - pn->pn_extra |= PNX_NEEDBRACES; - } - tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_RETURN_FLAGS)); - return pn; - } - - case TOK_EOL: - case TOK_SEMI: - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - pn->pn_type = TOK_SEMI; - pn->pn_kid = NULL; - if (tt == TOK_EOL) - pn->pn_no_semi = JS_TRUE; - return pn; - -#if JS_HAS_DEBUGGER_KEYWORD - case TOK_DEBUGGER: - pn = NewParseNode(cx, ts, PN_NULLARY, tc); - if (!pn) - return NULL; - pn->pn_type = TOK_DEBUGGER; - tc->flags |= TCF_FUN_HEAVYWEIGHT; - break; -#endif /* JS_HAS_DEBUGGER_KEYWORD */ - -#if JS_HAS_XML_SUPPORT - case TOK_DEFAULT: - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - if (!js_MatchToken(cx, ts, TOK_NAME) || - CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.xmlAtom || - !js_MatchToken(cx, ts, TOK_NAME) || - CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.namespaceAtom || - !js_MatchToken(cx, ts, TOK_ASSIGN) || - CURRENT_TOKEN(ts).t_op != JSOP_NOP) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_DEFAULT_XML_NAMESPACE); - return NULL; - } - pn2 = Expr(cx, ts, tc); - if (!pn2) - return NULL; - pn->pn_op = JSOP_DEFXMLNS; - pn->pn_pos.end = pn2->pn_pos.end; - pn->pn_kid = pn2; - tc->flags |= TCF_HAS_DEFXMLNS; - break; -#endif - - case TOK_ERROR: - return NULL; - - default: -#if JS_HAS_XML_SUPPORT - expression: -#endif - js_UngetToken(ts); - pn2 = Expr(cx, ts, tc); - if (!pn2) - return NULL; - - if (js_PeekToken(cx, ts) == TOK_COLON) { - if (pn2->pn_type != TOK_NAME) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_LABEL); - return NULL; - } - label = pn2->pn_atom; - for (stmt = tc->topStmt; stmt; stmt = stmt->down) { - if (stmt->type == STMT_LABEL && stmt->atom == label) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_DUPLICATE_LABEL); - return NULL; - } - } - (void) js_GetToken(cx, ts); - - /* Push a label struct and parse the statement. */ - js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1); - stmtInfo.atom = label; - pn = Statement(cx, ts, tc); - if (!pn) - return NULL; - - /* Normalize empty statement to empty block for the decompiler. */ - if (pn->pn_type == TOK_SEMI && !pn->pn_kid) { - pn->pn_type = TOK_LC; - pn->pn_arity = PN_LIST; - PN_INIT_LIST(pn); - } - - /* Pop the label, set pn_expr, and return early. */ - js_PopStatement(tc); - pn2->pn_type = TOK_COLON; - pn2->pn_pos.end = pn->pn_pos.end; - pn2->pn_expr = pn; - return pn2; - } - - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - pn->pn_type = TOK_SEMI; - pn->pn_pos = pn2->pn_pos; - pn->pn_kid = pn2; - break; - } - - /* Check termination of this primitive statement. */ - if (ON_CURRENT_LINE(ts, pn->pn_pos)) { - ts->flags |= TSF_OPERAND; - tt = js_PeekTokenSameLine(cx, ts); - ts->flags &= ~TSF_OPERAND; - if (tt == TOK_ERROR) - return NULL; - if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_SEMI_BEFORE_STMNT); - return NULL; - } - } - - if (!js_MatchToken(cx, ts, TOK_SEMI)) - pn->pn_no_semi = JS_TRUE; - return pn; -} - -static JSParseNode * -Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSTokenType tt; - JSBool let; - JSStmtInfo *scopeStmt; - BindData data; - JSParseNode *pn, *pn2; - JSStackFrame *fp; - JSAtom *atom; - - /* - * The three options here are: - * - TOK_LET: We are parsing a let declaration. - * - TOK_LP: We are parsing the head of a let block. - * - Otherwise, we're parsing var declarations. - */ - tt = CURRENT_TOKEN(ts).type; - let = (tt == TOK_LET || tt == TOK_LP); - JS_ASSERT(let || tt == TOK_VAR); - - /* Make sure that Statement set the tree context up correctly. */ - scopeStmt = tc->topScopeStmt; - if (let) { - while (scopeStmt && !(scopeStmt->flags & SIF_SCOPE)) { - JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt)); - scopeStmt = scopeStmt->downScope; - } - JS_ASSERT(scopeStmt); - } - - data.pn = NULL; - data.ts = ts; - data.op = let ? JSOP_NOP : CURRENT_TOKEN(ts).t_op; - data.binder = let ? BindLet : BindVarOrConst; - pn = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn) - return NULL; - pn->pn_op = data.op; - PN_INIT_LIST(pn); - - /* - * The tricky part of this code is to create special parsenode opcodes for - * getting and setting variables (which will be stored as special slots in - * the frame). The most complicated case is an eval() inside a function. - * If the evaluated string references variables in the enclosing function, - * then we need to generate the special variable opcodes. We determine - * this by looking up the variable's id in the current variable object. - * Fortunately, we can avoid doing this for let declared variables. - */ - fp = cx->fp; - if (let) { - JS_ASSERT(tc->blockChain == ATOM_TO_OBJECT(scopeStmt->atom)); - data.obj = tc->blockChain; - data.u.let.index = OBJ_BLOCK_COUNT(cx, data.obj); - data.u.let.overflow = JSMSG_TOO_MANY_FUN_VARS; - } else { - data.obj = fp->varobj; - data.u.var.fun = fp->fun; - data.u.var.clasp = OBJ_GET_CLASS(cx, data.obj); - if (data.u.var.fun && data.u.var.clasp == &js_FunctionClass) { - /* We are compiling code inside a function */ - data.u.var.getter = js_GetLocalVariable; - data.u.var.setter = js_SetLocalVariable; - } else if (data.u.var.fun && data.u.var.clasp == &js_CallClass) { - /* We are compiling code from an eval inside a function */ - data.u.var.getter = js_GetCallVariable; - data.u.var.setter = js_SetCallVariable; - } else { - data.u.var.getter = data.u.var.clasp->getProperty; - data.u.var.setter = data.u.var.clasp->setProperty; - } - - data.u.var.attrs = (data.op == JSOP_DEFCONST) - ? JSPROP_PERMANENT | JSPROP_READONLY - : JSPROP_PERMANENT; - } - - do { - tt = js_GetToken(cx, ts); -#if JS_HAS_DESTRUCTURING - if (tt == TOK_LB || tt == TOK_LC) { - pn2 = PrimaryExpr(cx, ts, tc, tt, JS_FALSE); - if (!pn2) - return NULL; - - if ((tc->flags & TCF_IN_FOR_INIT) && - js_PeekToken(cx, ts) == TOK_IN) { - if (!CheckDestructuring(cx, &data, pn2, NULL, tc)) - return NULL; - PN_APPEND(pn, pn2); - continue; - } - - MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL); - if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) - goto bad_var_init; - - pn2 = NewBinary(cx, TOK_ASSIGN, JSOP_NOP, - pn2, AssignExpr(cx, ts, tc), - tc); - if (!pn2 || - !CheckDestructuring(cx, &data, - pn2->pn_left, pn2->pn_right, - tc)) { - return NULL; - } - PN_APPEND(pn, pn2); - continue; - } -#endif - - if (tt != TOK_NAME) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_NO_VARIABLE_NAME); - return NULL; - } - atom = CURRENT_TOKEN(ts).t_atom; - if (!data.binder(cx, &data, atom, tc)) - return NULL; - - pn2 = NewParseNode(cx, ts, PN_NAME, tc); - if (!pn2) - return NULL; - pn2->pn_op = JSOP_NAME; - pn2->pn_atom = atom; - pn2->pn_expr = NULL; - pn2->pn_slot = -1; - pn2->pn_attrs = let ? 0 : data.u.var.attrs; - PN_APPEND(pn, pn2); - - if (js_MatchToken(cx, ts, TOK_ASSIGN)) { - if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) - goto bad_var_init; - - pn2->pn_expr = AssignExpr(cx, ts, tc); - if (!pn2->pn_expr) - return NULL; - pn2->pn_op = (!let && data.op == JSOP_DEFCONST) - ? JSOP_SETCONST - : JSOP_SETNAME; - if (!let && atom == cx->runtime->atomState.argumentsAtom) - tc->flags |= TCF_FUN_HEAVYWEIGHT; - } - } while (js_MatchToken(cx, ts, TOK_COMMA)); - - pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; - return pn; - -bad_var_init: - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_VAR_INIT); - return NULL; -} - -static JSParseNode * -Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn, *pn2; - - pn = AssignExpr(cx, ts, tc); - if (pn && js_MatchToken(cx, ts, TOK_COMMA)) { - pn2 = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn2) - return NULL; - pn2->pn_pos.begin = pn->pn_pos.begin; - PN_INIT_LIST_1(pn2, pn); - pn = pn2; - do { -#if JS_HAS_GENERATORS - pn2 = PN_LAST(pn); - if (pn2->pn_type == TOK_YIELD) { - js_ReportCompileErrorNumber(cx, pn2, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_BAD_YIELD_SYNTAX); - return NULL; - } -#endif - pn2 = AssignExpr(cx, ts, tc); - if (!pn2) - return NULL; - PN_APPEND(pn, pn2); - } while (js_MatchToken(cx, ts, TOK_COMMA)); - pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; - } - return pn; -} - -static JSParseNode * -AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn, *pn2; - JSTokenType tt; - JSOp op; - - CHECK_RECURSION(); - -#if JS_HAS_GENERATORS - ts->flags |= TSF_OPERAND; - if (js_MatchToken(cx, ts, TOK_YIELD)) { - ts->flags &= ~TSF_OPERAND; - return ReturnOrYield(cx, ts, tc, AssignExpr); - } - ts->flags &= ~TSF_OPERAND; -#endif - - pn = CondExpr(cx, ts, tc); - if (!pn) - return NULL; - - tt = js_GetToken(cx, ts); -#if JS_HAS_GETTER_SETTER - if (tt == TOK_NAME) { - tt = CheckGetterOrSetter(cx, ts, TOK_ASSIGN); - if (tt == TOK_ERROR) - return NULL; - } -#endif - if (tt != TOK_ASSIGN) { - js_UngetToken(ts); - return pn; - } - - op = CURRENT_TOKEN(ts).t_op; - for (pn2 = pn; pn2->pn_type == TOK_RP; pn2 = pn2->pn_kid) - continue; - switch (pn2->pn_type) { - case TOK_NAME: - pn2->pn_op = JSOP_SETNAME; - if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom) - tc->flags |= TCF_FUN_HEAVYWEIGHT; - break; - case TOK_DOT: - pn2->pn_op = (pn2->pn_op == JSOP_GETMETHOD) - ? JSOP_SETMETHOD - : JSOP_SETPROP; - break; - case TOK_LB: - pn2->pn_op = JSOP_SETELEM; - break; -#if JS_HAS_DESTRUCTURING - case TOK_RB: - case TOK_RC: - if (op != JSOP_NOP) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_DESTRUCT_ASS); - return NULL; - } - pn = AssignExpr(cx, ts, tc); - if (!pn || !CheckDestructuring(cx, NULL, pn2, pn, tc)) - return NULL; - return NewBinary(cx, TOK_ASSIGN, op, pn2, pn, tc); -#endif -#if JS_HAS_LVALUE_RETURN - case TOK_LP: - JS_ASSERT(pn2->pn_op == JSOP_CALL || pn2->pn_op == JSOP_EVAL); - pn2->pn_op = JSOP_SETCALL; - break; -#endif -#if JS_HAS_XML_SUPPORT - case TOK_UNARYOP: - if (pn2->pn_op == JSOP_XMLNAME) { - pn2->pn_op = JSOP_SETXMLNAME; - break; - } - /* FALL THROUGH */ -#endif - default: - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_LEFTSIDE_OF_ASS); - return NULL; - } - - return NewBinary(cx, TOK_ASSIGN, op, pn2, AssignExpr(cx, ts, tc), tc); -} - -static JSParseNode * -CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn, *pn1, *pn2, *pn3; - uintN oldflags; - - pn = OrExpr(cx, ts, tc); - if (pn && js_MatchToken(cx, ts, TOK_HOOK)) { - pn1 = pn; - pn = NewParseNode(cx, ts, PN_TERNARY, tc); - if (!pn) - return NULL; - /* - * Always accept the 'in' operator in the middle clause of a ternary, - * where it's unambiguous, even if we might be parsing the init of a - * for statement. - */ - oldflags = tc->flags; - tc->flags &= ~TCF_IN_FOR_INIT; - pn2 = AssignExpr(cx, ts, tc); - tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS); - - if (!pn2) - return NULL; - MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND); - pn3 = AssignExpr(cx, ts, tc); - if (!pn3) - return NULL; - pn->pn_pos.begin = pn1->pn_pos.begin; - pn->pn_pos.end = pn3->pn_pos.end; - pn->pn_kid1 = pn1; - pn->pn_kid2 = pn2; - pn->pn_kid3 = pn3; - } - return pn; -} - -static JSParseNode * -OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - - pn = AndExpr(cx, ts, tc); - if (pn && js_MatchToken(cx, ts, TOK_OR)) - pn = NewBinary(cx, TOK_OR, JSOP_OR, pn, OrExpr(cx, ts, tc), tc); - return pn; -} - -static JSParseNode * -AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - - pn = BitOrExpr(cx, ts, tc); - if (pn && js_MatchToken(cx, ts, TOK_AND)) - pn = NewBinary(cx, TOK_AND, JSOP_AND, pn, AndExpr(cx, ts, tc), tc); - return pn; -} - -static JSParseNode * -BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - - pn = BitXorExpr(cx, ts, tc); - while (pn && js_MatchToken(cx, ts, TOK_BITOR)) { - pn = NewBinary(cx, TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc), - tc); - } - return pn; -} - -static JSParseNode * -BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - - pn = BitAndExpr(cx, ts, tc); - while (pn && js_MatchToken(cx, ts, TOK_BITXOR)) { - pn = NewBinary(cx, TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc), - tc); - } - return pn; -} - -static JSParseNode * -BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - - pn = EqExpr(cx, ts, tc); - while (pn && js_MatchToken(cx, ts, TOK_BITAND)) - pn = NewBinary(cx, TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc), tc); - return pn; -} - -static JSParseNode * -EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - JSOp op; - - pn = RelExpr(cx, ts, tc); - while (pn && js_MatchToken(cx, ts, TOK_EQOP)) { - op = CURRENT_TOKEN(ts).t_op; - pn = NewBinary(cx, TOK_EQOP, op, pn, RelExpr(cx, ts, tc), tc); - } - return pn; -} - -static JSParseNode * -RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - JSTokenType tt; - JSOp op; - uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT; - - /* - * Uses of the in operator in ShiftExprs are always unambiguous, - * so unset the flag that prohibits recognizing it. - */ - tc->flags &= ~TCF_IN_FOR_INIT; - - pn = ShiftExpr(cx, ts, tc); - while (pn && - (js_MatchToken(cx, ts, TOK_RELOP) || - /* - * Recognize the 'in' token as an operator only if we're not - * currently in the init expr of a for loop. - */ - (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN)) || - js_MatchToken(cx, ts, TOK_INSTANCEOF))) { - tt = CURRENT_TOKEN(ts).type; - op = CURRENT_TOKEN(ts).t_op; - pn = NewBinary(cx, tt, op, pn, ShiftExpr(cx, ts, tc), tc); - } - /* Restore previous state of inForInit flag. */ - tc->flags |= inForInitFlag; - - return pn; -} - -static JSParseNode * -ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - JSOp op; - - pn = AddExpr(cx, ts, tc); - while (pn && js_MatchToken(cx, ts, TOK_SHOP)) { - op = CURRENT_TOKEN(ts).t_op; - pn = NewBinary(cx, TOK_SHOP, op, pn, AddExpr(cx, ts, tc), tc); - } - return pn; -} - -static JSParseNode * -AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - JSTokenType tt; - JSOp op; - - pn = MulExpr(cx, ts, tc); - while (pn && - (js_MatchToken(cx, ts, TOK_PLUS) || - js_MatchToken(cx, ts, TOK_MINUS))) { - tt = CURRENT_TOKEN(ts).type; - op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB; - pn = NewBinary(cx, tt, op, pn, MulExpr(cx, ts, tc), tc); - } - return pn; -} - -static JSParseNode * -MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - JSTokenType tt; - JSOp op; - - pn = UnaryExpr(cx, ts, tc); - while (pn && - (js_MatchToken(cx, ts, TOK_STAR) || - js_MatchToken(cx, ts, TOK_DIVOP))) { - tt = CURRENT_TOKEN(ts).type; - op = CURRENT_TOKEN(ts).t_op; - pn = NewBinary(cx, tt, op, pn, UnaryExpr(cx, ts, tc), tc); - } - return pn; -} - -static JSParseNode * -SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid, - const char *name) -{ - while (kid->pn_type == TOK_RP) - kid = kid->pn_kid; - if (kid->pn_type != TOK_NAME && - kid->pn_type != TOK_DOT && -#if JS_HAS_LVALUE_RETURN - (kid->pn_type != TOK_LP || kid->pn_op != JSOP_CALL) && -#endif -#if JS_HAS_XML_SUPPORT - (kid->pn_type != TOK_UNARYOP || kid->pn_op != JSOP_XMLNAME) && -#endif - kid->pn_type != TOK_LB) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_OPERAND, name); - return NULL; - } - pn->pn_kid = kid; - return kid; -} - -static const char incop_name_str[][10] = {"increment", "decrement"}; - -static JSBool -SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSParseNode *pn, JSParseNode *kid, - JSTokenType tt, JSBool preorder) -{ - JSOp op; - - kid = SetLvalKid(cx, ts, pn, kid, incop_name_str[tt == TOK_DEC]); - if (!kid) - return JS_FALSE; - switch (kid->pn_type) { - case TOK_NAME: - op = (tt == TOK_INC) - ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC) - : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC); - if (kid->pn_atom == cx->runtime->atomState.argumentsAtom) - tc->flags |= TCF_FUN_HEAVYWEIGHT; - break; - - case TOK_DOT: - op = (tt == TOK_INC) - ? (preorder ? JSOP_INCPROP : JSOP_PROPINC) - : (preorder ? JSOP_DECPROP : JSOP_PROPDEC); - break; - -#if JS_HAS_LVALUE_RETURN - case TOK_LP: - JS_ASSERT(kid->pn_op == JSOP_CALL); - kid->pn_op = JSOP_SETCALL; - /* FALL THROUGH */ -#endif -#if JS_HAS_XML_SUPPORT - case TOK_UNARYOP: - if (kid->pn_op == JSOP_XMLNAME) - kid->pn_op = JSOP_SETXMLNAME; - /* FALL THROUGH */ -#endif - case TOK_LB: - op = (tt == TOK_INC) - ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC) - : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC); - break; - - default: - JS_ASSERT(0); - op = JSOP_NOP; - } - pn->pn_op = op; - return JS_TRUE; -} - -static JSParseNode * -UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSTokenType tt; - JSParseNode *pn, *pn2; - - CHECK_RECURSION(); - - ts->flags |= TSF_OPERAND; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_OPERAND; - - switch (tt) { - case TOK_UNARYOP: - case TOK_PLUS: - case TOK_MINUS: - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - pn->pn_type = TOK_UNARYOP; /* PLUS and MINUS are binary */ - pn->pn_op = CURRENT_TOKEN(ts).t_op; - pn2 = UnaryExpr(cx, ts, tc); - if (!pn2) - return NULL; - pn->pn_pos.end = pn2->pn_pos.end; - pn->pn_kid = pn2; - break; - - case TOK_INC: - case TOK_DEC: - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - pn2 = MemberExpr(cx, ts, tc, JS_TRUE); - if (!pn2) - return NULL; - if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE)) - return NULL; - pn->pn_pos.end = pn2->pn_pos.end; - break; - - case TOK_DELETE: - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - pn2 = UnaryExpr(cx, ts, tc); - if (!pn2) - return NULL; - pn->pn_pos.end = pn2->pn_pos.end; - - /* - * Under ECMA3, deleting any unary expression is valid -- it simply - * returns true. Here we strip off any parentheses. - */ - while (pn2->pn_type == TOK_RP) - pn2 = pn2->pn_kid; - pn->pn_kid = pn2; - break; - - case TOK_ERROR: - return NULL; - - default: - js_UngetToken(ts); - pn = MemberExpr(cx, ts, tc, JS_TRUE); - if (!pn) - return NULL; - - /* Don't look across a newline boundary for a postfix incop. */ - if (ON_CURRENT_LINE(ts, pn->pn_pos)) { - ts->flags |= TSF_OPERAND; - tt = js_PeekTokenSameLine(cx, ts); - ts->flags &= ~TSF_OPERAND; - if (tt == TOK_INC || tt == TOK_DEC) { - (void) js_GetToken(cx, ts); - pn2 = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn2) - return NULL; - if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE)) - return NULL; - pn2->pn_pos.begin = pn->pn_pos.begin; - pn = pn2; - } - } - break; - } - return pn; -} - -static JSBool -ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSParseNode *listNode) -{ - JSBool matched; - - ts->flags |= TSF_OPERAND; - matched = js_MatchToken(cx, ts, TOK_RP); - ts->flags &= ~TSF_OPERAND; - if (!matched) { - do { - JSParseNode *argNode = AssignExpr(cx, ts, tc); - if (!argNode) - return JS_FALSE; -#if JS_HAS_GENERATORS - if (argNode->pn_type == TOK_YIELD) { - js_ReportCompileErrorNumber(cx, argNode, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_BAD_YIELD_SYNTAX); - return JS_FALSE; - } -#endif - PN_APPEND(listNode, argNode); - } while (js_MatchToken(cx, ts, TOK_COMMA)); - - if (js_GetToken(cx, ts) != TOK_RP) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_PAREN_AFTER_ARGS); - return JS_FALSE; - } - } - return JS_TRUE; -} - -static JSParseNode * -MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSBool allowCallSyntax) -{ - JSParseNode *pn, *pn2, *pn3; - JSTokenType tt; - - CHECK_RECURSION(); - - /* Check for new expression first. */ - ts->flags |= TSF_OPERAND; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_OPERAND; - if (tt == TOK_NEW) { - pn = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn) - return NULL; - pn2 = MemberExpr(cx, ts, tc, JS_FALSE); - if (!pn2) - return NULL; - pn->pn_op = JSOP_NEW; - PN_INIT_LIST_1(pn, pn2); - pn->pn_pos.begin = pn2->pn_pos.begin; - - if (js_MatchToken(cx, ts, TOK_LP) && !ArgumentList(cx, ts, tc, pn)) - return NULL; - if (pn->pn_count > ARGC_LIMIT) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_TOO_MANY_CON_ARGS); - return NULL; - } - pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; - } else { - pn = PrimaryExpr(cx, ts, tc, tt, JS_FALSE); - if (!pn) - return NULL; - - if (pn->pn_type == TOK_ANYNAME || - pn->pn_type == TOK_AT || - pn->pn_type == TOK_DBLCOLON) { - pn2 = NewOrRecycledNode(cx, tc); - if (!pn2) - return NULL; - pn2->pn_type = TOK_UNARYOP; - pn2->pn_pos = pn->pn_pos; - pn2->pn_op = JSOP_XMLNAME; - pn2->pn_arity = PN_UNARY; - pn2->pn_kid = pn; - pn2->pn_next = NULL; - pn2->pn_ts = ts; - pn2->pn_source = NULL; - pn2->pn_no_semi = JS_FALSE; - pn = pn2; - } - } - - while ((tt = js_GetToken(cx, ts)) > TOK_EOF) { - if (tt == TOK_DOT) { - pn2 = NewParseNode(cx, ts, PN_NAME, tc); - if (!pn2) - return NULL; - pn2->pn_slot = -1; - pn2->pn_attrs = 0; -#if JS_HAS_XML_SUPPORT - ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME; - tt = js_GetToken(cx, ts); - ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME); - pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE); - if (!pn3) - return NULL; - tt = pn3->pn_type; - if (tt == TOK_NAME || - (tt == TOK_DBLCOLON && - pn3->pn_arity == PN_NAME && - pn3->pn_expr->pn_type == TOK_FUNCTION)) { - pn2->pn_op = (tt == TOK_NAME) ? JSOP_GETPROP : JSOP_GETMETHOD; - pn2->pn_expr = pn; - pn2->pn_atom = pn3->pn_atom; - RecycleTree(pn3, tc); - } else { - if (TOKEN_TYPE_IS_XML(tt)) { - pn2->pn_type = TOK_LB; - pn2->pn_op = JSOP_GETELEM; - } else if (tt == TOK_RP) { - JSParseNode *group = pn3; - - /* Recycle the useless TOK_RP/JSOP_GROUP node. */ - pn3 = group->pn_kid; - group->pn_kid = NULL; - RecycleTree(group, tc); - pn2->pn_type = TOK_FILTER; - pn2->pn_op = JSOP_FILTER; - - /* A filtering predicate is like a with statement. */ - tc->flags |= TCF_FUN_HEAVYWEIGHT; - } else { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_NAME_AFTER_DOT); - return NULL; - } - pn2->pn_arity = PN_BINARY; - pn2->pn_left = pn; - pn2->pn_right = pn3; - } -#else - ts->flags |= TSF_KEYWORD_IS_NAME; - MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT); - ts->flags &= ~TSF_KEYWORD_IS_NAME; - pn2->pn_op = JSOP_GETPROP; - pn2->pn_expr = pn; - pn2->pn_atom = CURRENT_TOKEN(ts).t_atom; -#endif - pn2->pn_pos.begin = pn->pn_pos.begin; - pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; -#if JS_HAS_XML_SUPPORT - } else if (tt == TOK_DBLDOT) { - pn2 = NewParseNode(cx, ts, PN_BINARY, tc); - if (!pn2) - return NULL; - ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME; - tt = js_GetToken(cx, ts); - ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME); - pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE); - if (!pn3) - return NULL; - tt = pn3->pn_type; - if (tt == TOK_NAME) { - pn3->pn_type = TOK_STRING; - pn3->pn_arity = PN_NULLARY; - pn3->pn_op = JSOP_QNAMEPART; - } else if (!TOKEN_TYPE_IS_XML(tt)) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_NAME_AFTER_DOT); - return NULL; - } - pn2->pn_op = JSOP_DESCENDANTS; - pn2->pn_left = pn; - pn2->pn_right = pn3; - pn2->pn_pos.begin = pn->pn_pos.begin; - pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; -#endif - } else if (tt == TOK_LB) { - pn2 = NewParseNode(cx, ts, PN_BINARY, tc); - if (!pn2) - return NULL; - pn3 = Expr(cx, ts, tc); - if (!pn3) - return NULL; - - MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX); - pn2->pn_pos.begin = pn->pn_pos.begin; - pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - - /* Optimize o['p'] to o.p by rewriting pn2. */ - if (pn3->pn_type == TOK_STRING) { - pn2->pn_type = TOK_DOT; - pn2->pn_op = JSOP_GETPROP; - pn2->pn_arity = PN_NAME; - pn2->pn_expr = pn; - pn2->pn_atom = pn3->pn_atom; - } else { - pn2->pn_op = JSOP_GETELEM; - pn2->pn_left = pn; - pn2->pn_right = pn3; - } - } else if (allowCallSyntax && tt == TOK_LP) { - pn2 = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn2) - return NULL; - - /* Pick JSOP_EVAL and flag tc as heavyweight if eval(...). */ - pn2->pn_op = JSOP_CALL; - if (pn->pn_op == JSOP_NAME && - pn->pn_atom == cx->runtime->atomState.evalAtom) { - pn2->pn_op = JSOP_EVAL; - tc->flags |= TCF_FUN_HEAVYWEIGHT; - } - - PN_INIT_LIST_1(pn2, pn); - pn2->pn_pos.begin = pn->pn_pos.begin; - - if (!ArgumentList(cx, ts, tc, pn2)) - return NULL; - if (pn2->pn_count > ARGC_LIMIT) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_TOO_MANY_FUN_ARGS); - return NULL; - } - pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - } else { - js_UngetToken(ts); - return pn; - } - - pn = pn2; - } - if (tt == TOK_ERROR) - return NULL; - return pn; -} - -static JSParseNode * -BracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - uintN oldflags; - JSParseNode *pn; - - /* - * Always accept the 'in' operator in a parenthesized expression, - * where it's unambiguous, even if we might be parsing the init of a - * for statement. - */ - oldflags = tc->flags; - tc->flags &= ~TCF_IN_FOR_INIT; - pn = Expr(cx, ts, tc); - tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS); - return pn; -} - -#if JS_HAS_XML_SUPPORT - -static JSParseNode * -EndBracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - - pn = BracketedExpr(cx, ts, tc); - if (!pn) - return NULL; - - MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ATTR_EXPR); - return pn; -} - -/* - * From the ECMA-357 grammar in 11.1.1 and 11.1.2: - * - * AttributeIdentifier: - * @ PropertySelector - * @ QualifiedIdentifier - * @ [ Expression ] - * - * PropertySelector: - * Identifier - * * - * - * QualifiedIdentifier: - * PropertySelector :: PropertySelector - * PropertySelector :: [ Expression ] - * - * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so: - * - * AttributeIdentifier: - * @ QualifiedIdentifier - * @ [ Expression ] - * - * PropertySelector: - * Identifier - * * - * - * QualifiedIdentifier: - * PropertySelector :: PropertySelector - * PropertySelector :: [ Expression ] - * PropertySelector - * - * As PrimaryExpression: Identifier is in ECMA-262 and we want the semantics - * for that rule to result in a name node, but ECMA-357 extends the grammar - * to include PrimaryExpression: QualifiedIdentifier, we must factor further: - * - * QualifiedIdentifier: - * PropertySelector QualifiedSuffix - * - * QualifiedSuffix: - * :: PropertySelector - * :: [ Expression ] - * /nothing/ - * - * And use this production instead of PrimaryExpression: QualifiedIdentifier: - * - * PrimaryExpression: - * Identifier QualifiedSuffix - * - * We hoist the :: match into callers of QualifiedSuffix, in order to tweak - * PropertySelector vs. Identifier pn_arity, pn_op, and other members. - */ -static JSParseNode * -PropertySelector(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - - pn = NewParseNode(cx, ts, PN_NULLARY, tc); - if (!pn) - return NULL; - if (pn->pn_type == TOK_STAR) { - pn->pn_type = TOK_ANYNAME; - pn->pn_op = JSOP_ANYNAME; - pn->pn_atom = cx->runtime->atomState.starAtom; - } else { - JS_ASSERT(pn->pn_type == TOK_NAME); - pn->pn_op = JSOP_QNAMEPART; - pn->pn_arity = PN_NAME; - pn->pn_atom = CURRENT_TOKEN(ts).t_atom; - pn->pn_expr = NULL; - pn->pn_slot = -1; - pn->pn_attrs = 0; - } - return pn; -} - -static JSParseNode * -QualifiedSuffix(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, - JSTreeContext *tc) -{ - JSParseNode *pn2, *pn3; - JSTokenType tt; - - JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_DBLCOLON); - pn2 = NewParseNode(cx, ts, PN_NAME, tc); - if (!pn2) - return NULL; - - /* Left operand of :: must be evaluated if it is an identifier. */ - if (pn->pn_op == JSOP_QNAMEPART) - pn->pn_op = JSOP_NAME; - - ts->flags |= TSF_KEYWORD_IS_NAME; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_KEYWORD_IS_NAME; - if (tt == TOK_STAR || tt == TOK_NAME) { - /* Inline and specialize PropertySelector for JSOP_QNAMECONST. */ - pn2->pn_op = JSOP_QNAMECONST; - pn2->pn_atom = (tt == TOK_STAR) - ? cx->runtime->atomState.starAtom - : CURRENT_TOKEN(ts).t_atom; - pn2->pn_expr = pn; - pn2->pn_slot = -1; - pn2->pn_attrs = 0; - return pn2; - } - - if (tt != TOK_LB) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_SYNTAX_ERROR); - return NULL; - } - pn3 = EndBracketedExpr(cx, ts, tc); - if (!pn3) - return NULL; - - pn2->pn_op = JSOP_QNAME; - pn2->pn_arity = PN_BINARY; - pn2->pn_left = pn; - pn2->pn_right = pn3; - return pn2; -} - -static JSParseNode * -QualifiedIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - - pn = PropertySelector(cx, ts, tc); - if (!pn) - return NULL; - if (js_MatchToken(cx, ts, TOK_DBLCOLON)) - pn = QualifiedSuffix(cx, ts, pn, tc); - return pn; -} - -static JSParseNode * -AttributeIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn, *pn2; - JSTokenType tt; - - JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_AT); - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - pn->pn_op = JSOP_TOATTRNAME; - ts->flags |= TSF_KEYWORD_IS_NAME; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_KEYWORD_IS_NAME; - if (tt == TOK_STAR || tt == TOK_NAME) { - pn2 = QualifiedIdentifier(cx, ts, tc); - } else if (tt == TOK_LB) { - pn2 = EndBracketedExpr(cx, ts, tc); - } else { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_SYNTAX_ERROR); - return NULL; - } - if (!pn2) - return NULL; - pn->pn_kid = pn2; - return pn; -} - -/* - * Make a TOK_LC unary node whose pn_kid is an expression. - */ -static JSParseNode * -XMLExpr(JSContext *cx, JSTokenStream *ts, JSBool inTag, JSTreeContext *tc) -{ - JSParseNode *pn, *pn2; - uintN oldflags; - - JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LC); - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - - /* - * Turn off XML tag mode, but don't restore it after parsing this braced - * expression. Instead, simply restore ts's old flags. This is required - * because XMLExpr is called both from within a tag, and from within text - * contained in an element, but outside of any start, end, or point tag. - */ - oldflags = ts->flags; - ts->flags = oldflags & ~TSF_XMLTAGMODE; - pn2 = Expr(cx, ts, tc); - if (!pn2) - return NULL; - - MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR); - ts->flags = oldflags; - pn->pn_kid = pn2; - pn->pn_op = inTag ? JSOP_XMLTAGEXPR : JSOP_XMLELTEXPR; - return pn; -} - -/* - * Make a terminal node for one of TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE, - * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI. When converting - * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole - * child of a container tag. - */ -static JSParseNode * -XMLAtomNode(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - JSToken *tp; - - pn = NewParseNode(cx, ts, PN_NULLARY, tc); - if (!pn) - return NULL; - tp = &CURRENT_TOKEN(ts); - pn->pn_op = tp->t_op; - pn->pn_atom = tp->t_atom; - if (tp->type == TOK_XMLPI) - pn->pn_atom2 = tp->t_atom2; - return pn; -} - -/* - * Parse the productions: - * - * XMLNameExpr: - * XMLName XMLNameExpr? - * { Expr } XMLNameExpr? - * - * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces - * a list of names and/or expressions, a single expression, or a single name. - * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type - * will be TOK_LC. - */ -static JSParseNode * -XMLNameExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn, *pn2, *list; - JSTokenType tt; - - pn = list = NULL; - do { - tt = CURRENT_TOKEN(ts).type; - if (tt == TOK_LC) { - pn2 = XMLExpr(cx, ts, JS_TRUE, tc); - if (!pn2) - return NULL; - } else { - JS_ASSERT(tt == TOK_XMLNAME); - pn2 = XMLAtomNode(cx, ts, tc); - if (!pn2) - return NULL; - } - - if (!pn) { - pn = pn2; - } else { - if (!list) { - list = NewParseNode(cx, ts, PN_LIST, tc); - if (!list) - return NULL; - list->pn_type = TOK_XMLNAME; - list->pn_pos.begin = pn->pn_pos.begin; - PN_INIT_LIST_1(list, pn); - list->pn_extra = PNX_CANTFOLD; - pn = list; - } - pn->pn_pos.end = pn2->pn_pos.end; - PN_APPEND(pn, pn2); - } - } while ((tt = js_GetToken(cx, ts)) == TOK_XMLNAME || tt == TOK_LC); - - js_UngetToken(ts); - return pn; -} - -/* - * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded - * at compile time into a JSXML tree. - */ -#define XML_FOLDABLE(pn) ((pn)->pn_arity == PN_LIST \ - ? ((pn)->pn_extra & PNX_CANTFOLD) == 0 \ - : (pn)->pn_type != TOK_LC) - -/* - * Parse the productions: - * - * XMLTagContent: - * XMLNameExpr - * XMLTagContent S XMLNameExpr S? = S? XMLAttr - * XMLTagContent S XMLNameExpr S? = S? { Expr } - * - * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent - * produces a list of name and attribute values and/or braced expressions, a - * single expression, or a single name. - * - * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where - * XMLTagContent: XMLNameExpr. If pn_type is not TOK_XMLNAME but pn_arity is - * PN_LIST, pn_type will be tagtype. If PN_UNARY, pn_type will be TOK_LC and - * we parsed exactly one expression. - */ -static JSParseNode * -XMLTagContent(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSTokenType tagtype, JSAtom **namep) -{ - JSParseNode *pn, *pn2, *list; - JSTokenType tt; - - pn = XMLNameExpr(cx, ts, tc); - if (!pn) - return NULL; - *namep = (pn->pn_arity == PN_NULLARY) ? pn->pn_atom : NULL; - list = NULL; - - while (js_MatchToken(cx, ts, TOK_XMLSPACE)) { - tt = js_GetToken(cx, ts); - if (tt != TOK_XMLNAME && tt != TOK_LC) { - js_UngetToken(ts); - break; - } - - pn2 = XMLNameExpr(cx, ts, tc); - if (!pn2) - return NULL; - if (!list) { - list = NewParseNode(cx, ts, PN_LIST, tc); - if (!list) - return NULL; - list->pn_type = tagtype; - list->pn_pos.begin = pn->pn_pos.begin; - PN_INIT_LIST_1(list, pn); - pn = list; - } - PN_APPEND(pn, pn2); - if (!XML_FOLDABLE(pn2)) - pn->pn_extra |= PNX_CANTFOLD; - - js_MatchToken(cx, ts, TOK_XMLSPACE); - MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_NO_ASSIGN_IN_XML_ATTR); - js_MatchToken(cx, ts, TOK_XMLSPACE); - - tt = js_GetToken(cx, ts); - if (tt == TOK_XMLATTR) { - pn2 = XMLAtomNode(cx, ts, tc); - } else if (tt == TOK_LC) { - pn2 = XMLExpr(cx, ts, JS_TRUE, tc); - pn->pn_extra |= PNX_CANTFOLD; - } else { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_XML_ATTR_VALUE); - return NULL; - } - if (!pn2) - return NULL; - pn->pn_pos.end = pn2->pn_pos.end; - PN_APPEND(pn, pn2); - } - - return pn; -} - -#define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \ - JS_BEGIN_MACRO \ - if ((tt) <= TOK_EOF) { \ - if ((tt) == TOK_EOF) { \ - js_ReportCompileErrorNumber(cx, ts, \ - JSREPORT_TS | JSREPORT_ERROR, \ - JSMSG_END_OF_XML_SOURCE); \ - } \ - return result; \ - } \ - JS_END_MACRO - -static JSParseNode * -XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSBool allowList); - -/* - * Consume XML element tag content, including the TOK_XMLETAGO (flags &= ~TSF_XMLTAGMODE; - for (;;) { - ts->flags |= TSF_XMLTEXTMODE; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_XMLTEXTMODE; - XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE); - - JS_ASSERT(tt == TOK_XMLSPACE || tt == TOK_XMLTEXT); - textAtom = CURRENT_TOKEN(ts).t_atom; - if (textAtom) { - /* Non-zero-length XML text scanned. */ - pn2 = XMLAtomNode(cx, ts, tc); - if (!pn2) - return JS_FALSE; - pn->pn_pos.end = pn2->pn_pos.end; - PN_APPEND(pn, pn2); - } - - ts->flags |= TSF_OPERAND; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_OPERAND; - XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE); - if (tt == TOK_XMLETAGO) - break; - - if (tt == TOK_LC) { - pn2 = XMLExpr(cx, ts, JS_FALSE, tc); - pn->pn_extra |= PNX_CANTFOLD; - } else if (tt == TOK_XMLSTAGO) { - pn2 = XMLElementOrList(cx, ts, tc, JS_FALSE); - if (pn2) { - pn2->pn_extra &= ~PNX_XMLROOT; - pn->pn_extra |= pn2->pn_extra; - } - } else { - JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT || - tt == TOK_XMLPI); - pn2 = XMLAtomNode(cx, ts, tc); - } - if (!pn2) - return JS_FALSE; - pn->pn_pos.end = pn2->pn_pos.end; - PN_APPEND(pn, pn2); - } - - JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLETAGO); - ts->flags |= TSF_XMLTAGMODE; - return JS_TRUE; -} - -/* - * Return a PN_LIST node containing an XML or XMLList Initialiser. - */ -static JSParseNode * -XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSBool allowList) -{ - JSParseNode *pn, *pn2, *list; - JSBool hadSpace; - JSTokenType tt; - JSAtom *startAtom, *endAtom; - - CHECK_RECURSION(); - - JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLSTAGO); - pn = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn) - return NULL; - - ts->flags |= TSF_XMLTAGMODE; - hadSpace = js_MatchToken(cx, ts, TOK_XMLSPACE); - tt = js_GetToken(cx, ts); - if (tt == TOK_ERROR) - return NULL; - - if (tt == TOK_XMLNAME || tt == TOK_LC) { - /* - * XMLElement. Append the tag and its contents, if any, to pn. - */ - pn2 = XMLTagContent(cx, ts, tc, TOK_XMLSTAGO, &startAtom); - if (!pn2) - return NULL; - js_MatchToken(cx, ts, TOK_XMLSPACE); - - tt = js_GetToken(cx, ts); - if (tt == TOK_XMLPTAGC) { - /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */ - if (pn2->pn_type == TOK_XMLSTAGO) { - PN_INIT_LIST(pn); - RecycleTree(pn, tc); - pn = pn2; - } else { - JS_ASSERT(pn2->pn_type == TOK_XMLNAME || - pn2->pn_type == TOK_LC); - PN_INIT_LIST_1(pn, pn2); - if (!XML_FOLDABLE(pn2)) - pn->pn_extra |= PNX_CANTFOLD; - } - pn->pn_type = TOK_XMLPTAGC; - pn->pn_extra |= PNX_XMLROOT; - } else { - /* We had better have a tag-close (>) at this point. */ - if (tt != TOK_XMLTAGC) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_XML_TAG_SYNTAX); - return NULL; - } - pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - - /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */ - if (pn2->pn_type != TOK_XMLSTAGO) { - PN_INIT_LIST_1(pn, pn2); - if (!XML_FOLDABLE(pn2)) - pn->pn_extra |= PNX_CANTFOLD; - pn2 = pn; - pn = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn) - return NULL; - } - - /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */ - pn->pn_type = TOK_XMLELEM; - PN_INIT_LIST_1(pn, pn2); - if (!XML_FOLDABLE(pn2)) - pn->pn_extra |= PNX_CANTFOLD; - pn->pn_extra |= PNX_XMLROOT; - - /* Get element contents and delimiting end-tag-open sequence. */ - if (!XMLElementContent(cx, ts, pn, tc)) - return NULL; - - js_MatchToken(cx, ts, TOK_XMLSPACE); - tt = js_GetToken(cx, ts); - XML_CHECK_FOR_ERROR_AND_EOF(tt, NULL); - if (tt != TOK_XMLNAME && tt != TOK_LC) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_XML_TAG_SYNTAX); - return NULL; - } - - /* Parse end tag; check mismatch at compile-time if we can. */ - pn2 = XMLTagContent(cx, ts, tc, TOK_XMLETAGO, &endAtom); - if (!pn2) - return NULL; - if (pn2->pn_type == TOK_XMLETAGO) { - /* Oops, end tag has attributes! */ - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_XML_TAG_SYNTAX); - return NULL; - } - if (endAtom && startAtom && endAtom != startAtom) { - JSString *str = ATOM_TO_STRING(startAtom); - - /* End vs. start tag name mismatch: point to the tag name. */ - js_ReportCompileErrorNumberUC(cx, pn2, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_XML_TAG_NAME_MISMATCH, - JSSTRING_CHARS(str)); - return NULL; - } - - /* Make a TOK_XMLETAGO list with pn2 as its single child. */ - JS_ASSERT(pn2->pn_type == TOK_XMLNAME || pn2->pn_type == TOK_LC); - list = NewParseNode(cx, ts, PN_LIST, tc); - if (!list) - return NULL; - list->pn_type = TOK_XMLETAGO; - PN_INIT_LIST_1(list, pn2); - PN_APPEND(pn, list); - if (!XML_FOLDABLE(pn2)) { - list->pn_extra |= PNX_CANTFOLD; - pn->pn_extra |= PNX_CANTFOLD; - } - - js_MatchToken(cx, ts, TOK_XMLSPACE); - MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_TAG_SYNTAX); - } - - /* Set pn_op now that pn has been updated to its final value. */ - pn->pn_op = JSOP_TOXML; - } else if (!hadSpace && allowList && tt == TOK_XMLTAGC) { - /* XMLList Initialiser. */ - pn->pn_type = TOK_XMLLIST; - pn->pn_op = JSOP_TOXMLLIST; - PN_INIT_LIST(pn); - pn->pn_extra |= PNX_XMLROOT; - if (!XMLElementContent(cx, ts, pn, tc)) - return NULL; - - MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_LIST_SYNTAX); - } else { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_XML_NAME_SYNTAX); - return NULL; - } - - pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - ts->flags &= ~TSF_XMLTAGMODE; - return pn; -} - -static JSParseNode * -XMLElementOrListRoot(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSBool allowList) -{ - uint32 oldopts; - JSParseNode *pn; - - /* - * Force XML support to be enabled so that comments and CDATA literals - * are recognized, instead of ). - */ - oldopts = JS_SetOptions(cx, cx->options | JSOPTION_XML); - pn = XMLElementOrList(cx, ts, tc, allowList); - JS_SetOptions(cx, oldopts); - return pn; -} - -JS_FRIEND_API(JSParseNode *) -js_ParseXMLTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, - JSBool allowList) -{ - JSStackFrame *fp, frame; - JSParseNode *pn; - JSTreeContext tc; - JSTokenType tt; - - /* - * Push a compiler frame if we have no frames, or if the top frame is a - * lightweight function activation, or if its scope chain doesn't match - * the one passed to us. - */ - fp = cx->fp; - MaybeSetupFrame(cx, chain, fp, &frame); - JS_KEEP_ATOMS(cx->runtime); - TREE_CONTEXT_INIT(&tc); - - /* Set XML-only mode to turn off special treatment of {expr} in XML. */ - ts->flags |= TSF_OPERAND | TSF_XMLONLYMODE; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_OPERAND; - - if (tt != TOK_XMLSTAGO) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_XML_MARKUP); - pn = NULL; - } else { - pn = XMLElementOrListRoot(cx, ts, &tc, allowList); - } - - ts->flags &= ~TSF_XMLONLYMODE; - TREE_CONTEXT_FINISH(&tc); - JS_UNKEEP_ATOMS(cx->runtime); - cx->fp = fp; - return pn; -} - -#endif /* JS_HAS_XMLSUPPORT */ - -static JSParseNode * -PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSTokenType tt, JSBool afterDot) -{ - JSParseNode *pn, *pn2, *pn3; - JSOp op; - -#if JS_HAS_SHARP_VARS - JSParseNode *defsharp; - JSBool notsharp; - - defsharp = NULL; - notsharp = JS_FALSE; - again: - /* - * Control flows here after #n= is scanned. If the following primary is - * not valid after such a "sharp variable" definition, the tt switch case - * should set notsharp. - */ -#endif - - CHECK_RECURSION(); - -#if JS_HAS_GETTER_SETTER - if (tt == TOK_NAME) { - tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION); - if (tt == TOK_ERROR) - return NULL; - } -#endif - - switch (tt) { - case TOK_FUNCTION: -#if JS_HAS_XML_SUPPORT - ts->flags |= TSF_KEYWORD_IS_NAME; - if (js_MatchToken(cx, ts, TOK_DBLCOLON)) { - ts->flags &= ~TSF_KEYWORD_IS_NAME; - pn2 = NewParseNode(cx, ts, PN_NULLARY, tc); - if (!pn2) - return NULL; - pn2->pn_type = TOK_FUNCTION; - pn = QualifiedSuffix(cx, ts, pn2, tc); - if (!pn) - return NULL; - break; - } - ts->flags &= ~TSF_KEYWORD_IS_NAME; -#endif - pn = FunctionExpr(cx, ts, tc); - if (!pn) - return NULL; - break; - - case TOK_LB: - { - JSBool matched; - jsuint index; - - pn = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn) - return NULL; - pn->pn_type = TOK_RB; - -#if JS_HAS_SHARP_VARS - if (defsharp) { - PN_INIT_LIST_1(pn, defsharp); - defsharp = NULL; - } else -#endif - PN_INIT_LIST(pn); - - ts->flags |= TSF_OPERAND; - matched = js_MatchToken(cx, ts, TOK_RB); - ts->flags &= ~TSF_OPERAND; - if (!matched) { - for (index = 0; ; index++) { - if (index == ARRAY_INIT_LIMIT) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_ARRAY_INIT_TOO_BIG); - return NULL; - } - - ts->flags |= TSF_OPERAND; - tt = js_PeekToken(cx, ts); - ts->flags &= ~TSF_OPERAND; - if (tt == TOK_RB) { - pn->pn_extra |= PNX_ENDCOMMA; - break; - } - - if (tt == TOK_COMMA) { - /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */ - js_MatchToken(cx, ts, TOK_COMMA); - pn2 = NewParseNode(cx, ts, PN_NULLARY, tc); - } else { - pn2 = AssignExpr(cx, ts, tc); - } - if (!pn2) - return NULL; - PN_APPEND(pn, pn2); - - if (tt != TOK_COMMA) { - /* If we didn't already match TOK_COMMA in above case. */ - if (!js_MatchToken(cx, ts, TOK_COMMA)) - break; - } - } - -#if JS_HAS_GENERATORS - /* - * At this point, (index == 0 && pn->pn_count != 0) implies one - * element initialiser was parsed (possibly with a defsharp before - * the left bracket). - * - * An array comprehension of the form: - * - * [i * j for (i in o) for (j in p) if (i != j)] - * - * translates to roughly the following let expression: - * - * let (array = new Array, i, j) { - * for (i in o) let { - * for (j in p) - * if (i != j) - * array.push(i * j) - * } - * array - * } - * - * where array is a nameless block-local variable. The "roughly" - * means that an implementation may optimize away the array.push. - * An array comprehension opens exactly one block scope, no matter - * how many for heads it contains. - * - * Each let () {...} or for (let ...) ... compiles to: - * - * JSOP_ENTERBLOCK ... JSOP_LEAVEBLOCK - * - * where is a literal object representing the block scope, - * with properties, naming each var declared in the block. - * - * Each var declaration in a let-block binds a name in at - * compile time, and allocates a slot on the operand stack at - * runtime via JSOP_ENTERBLOCK. A block-local var is accessed - * by the JSOP_GETLOCAL and JSOP_SETLOCAL ops, and iterated with - * JSOP_FORLOCAL. These ops all have an immediate operand, the - * local slot's stack index from fp->spbase. - * - * The array comprehension iteration step, array.push(i * j) in - * the example above, is done by ; JSOP_ARRAYCOMP , - * where is the index of array's stack slot. - */ - if (index == 0 && - pn->pn_count != 0 && - js_MatchToken(cx, ts, TOK_FOR)) { - JSParseNode **pnp, *pnexp, *pntop, *pnlet; - BindData data; - JSRuntime *rt; - JSStmtInfo stmtInfo; - JSAtom *atom; - - /* Relabel pn as an array comprehension node. */ - pn->pn_type = TOK_ARRAYCOMP; - - /* - * Remove the comprehension expression from pn's linked list - * and save it via pnexp. We'll re-install it underneath the - * ARRAYPUSH node after we parse the rest of the comprehension. - */ - pnexp = PN_LAST(pn); - JS_ASSERT(pn->pn_count == 1 || pn->pn_count == 2); - pn->pn_tail = (--pn->pn_count == 1) - ? &pn->pn_head->pn_next - : &pn->pn_head; - *pn->pn_tail = NULL; - - /* - * Make a parse-node and literal object representing the array - * comprehension's block scope. - */ - pntop = PushLexicalScope(cx, ts, tc, &stmtInfo); - if (!pntop) - return NULL; - pnp = &pntop->pn_expr; - - data.pn = NULL; - data.ts = ts; - data.obj = tc->blockChain; - data.op = JSOP_NOP; - data.binder = BindLet; - data.u.let.index = 0; - data.u.let.overflow = JSMSG_ARRAY_INIT_TOO_BIG; - - rt = cx->runtime; - do { - /* - * FOR node is binary, left is control and right is body. - * Use index to count each block-local let-variable on the - * left-hand side of IN. - */ - pn2 = NewParseNode(cx, ts, PN_BINARY, tc); - if (!pn2) - return NULL; - - pn2->pn_op = JSOP_FORIN; - if (js_MatchToken(cx, ts, TOK_NAME)) { - if (CURRENT_TOKEN(ts).t_atom == rt->atomState.eachAtom) - pn2->pn_op = JSOP_FOREACH; - else - js_UngetToken(ts); - } - MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); - - tt = js_GetToken(cx, ts); - switch (tt) { -#if JS_HAS_DESTRUCTURING - case TOK_LB: - case TOK_LC: - pnlet = DestructuringExpr(cx, &data, tc, tt); - if (!pnlet) - return NULL; - - if (pnlet->pn_type != TOK_RB || pnlet->pn_count != 2) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | - JSREPORT_ERROR, - JSMSG_BAD_FOR_LEFTSIDE); - return NULL; - } - - /* Destructuring requires [key, value] enumeration. */ - if (pn2->pn_op != JSOP_FOREACH) - pn2->pn_op = JSOP_FOREACHKEYVAL; - break; -#endif - - case TOK_NAME: - atom = CURRENT_TOKEN(ts).t_atom; - if (!data.binder(cx, &data, atom, tc)) - return NULL; - - /* - * Create a name node with op JSOP_NAME. We can't set - * op to JSOP_GETLOCAL here, because we don't yet know - * the block's depth in the operand stack frame. The - * code generator computes that, and it tries to bind - * all names to slots, so we must let it do the deed. - */ - pnlet = NewParseNode(cx, ts, PN_NAME, tc); - if (!pnlet) - return NULL; - pnlet->pn_op = JSOP_NAME; - pnlet->pn_atom = atom; - pnlet->pn_expr = NULL; - pnlet->pn_slot = -1; - pnlet->pn_attrs = 0; - break; - - default: - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS|JSREPORT_ERROR, - JSMSG_NO_VARIABLE_NAME); - return NULL; - } - - MUST_MATCH_TOKEN(TOK_IN, JSMSG_IN_AFTER_FOR_NAME); - pn3 = NewBinary(cx, TOK_IN, JSOP_NOP, pnlet, - Expr(cx, ts, tc), tc); - if (!pn3) - return NULL; - - MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL); - pn2->pn_left = pn3; - *pnp = pn2; - pnp = &pn2->pn_right; - } while (js_MatchToken(cx, ts, TOK_FOR)); - - if (js_MatchToken(cx, ts, TOK_IF)) { - pn2 = NewParseNode(cx, ts, PN_TERNARY, tc); - if (!pn2) - return NULL; - pn2->pn_kid1 = Condition(cx, ts, tc); - if (!pn2->pn_kid1) - return NULL; - pn2->pn_kid2 = NULL; - pn2->pn_kid3 = NULL; - *pnp = pn2; - pnp = &pn2->pn_kid2; - } - - pn2 = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn2) - return NULL; - pn2->pn_type = TOK_ARRAYPUSH; - pn2->pn_op = JSOP_ARRAYPUSH; - pn2->pn_kid = pnexp; - *pnp = pn2; - PN_APPEND(pn, pntop); - - js_PopStatement(tc); - } -#endif /* JS_HAS_GENERATORS */ - - MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST); - } - pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - return pn; - } - -#if JS_HAS_BLOCK_SCOPE - case TOK_LET: - pn = LetBlock(cx, ts, tc, JS_FALSE); - if (!pn) - return NULL; - break; -#endif - - case TOK_LC: - { - JSBool afterComma; - - pn = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn) - return NULL; - pn->pn_type = TOK_RC; - -#if JS_HAS_SHARP_VARS - if (defsharp) { - PN_INIT_LIST_1(pn, defsharp); - defsharp = NULL; - } else -#endif - PN_INIT_LIST(pn); - - afterComma = JS_FALSE; - for (;;) { - ts->flags |= TSF_KEYWORD_IS_NAME; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_KEYWORD_IS_NAME; - switch (tt) { - case TOK_NUMBER: - pn3 = NewParseNode(cx, ts, PN_NULLARY, tc); - if (pn3) - pn3->pn_dval = CURRENT_TOKEN(ts).t_dval; - break; - case TOK_NAME: -#if JS_HAS_GETTER_SETTER - { - JSAtom *atom; - JSRuntime *rt; - - atom = CURRENT_TOKEN(ts).t_atom; - rt = cx->runtime; - if (atom == rt->atomState.getAtom || - atom == rt->atomState.setAtom) { - op = (atom == rt->atomState.getAtom) - ? JSOP_GETTER - : JSOP_SETTER; - if (js_MatchToken(cx, ts, TOK_NAME)) { - pn3 = NewParseNode(cx, ts, PN_NAME, tc); - if (!pn3) - return NULL; - pn3->pn_atom = CURRENT_TOKEN(ts).t_atom; - pn3->pn_expr = NULL; - pn3->pn_slot = -1; - pn3->pn_attrs = 0; - - /* We have to fake a 'function' token here. */ - CURRENT_TOKEN(ts).t_op = JSOP_NOP; - CURRENT_TOKEN(ts).type = TOK_FUNCTION; - pn2 = FunctionExpr(cx, ts, tc); - pn2 = NewBinary(cx, TOK_COLON, op, pn3, pn2, tc); - goto skip; - } - } - /* else fall thru ... */ - } -#endif - case TOK_STRING: - pn3 = NewParseNode(cx, ts, PN_NULLARY, tc); - if (pn3) - pn3->pn_atom = CURRENT_TOKEN(ts).t_atom; - break; - case TOK_RC: - if (afterComma && - !js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | - JSREPORT_WARNING | - JSREPORT_STRICT, - JSMSG_TRAILING_COMMA)) { - return NULL; - } - goto end_obj_init; - default: - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_PROP_ID); - return NULL; - } - - tt = js_GetToken(cx, ts); -#if JS_HAS_GETTER_SETTER - if (tt == TOK_NAME) { - tt = CheckGetterOrSetter(cx, ts, TOK_COLON); - if (tt == TOK_ERROR) - return NULL; - } -#endif - if (tt != TOK_COLON) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_COLON_AFTER_ID); - return NULL; - } - op = CURRENT_TOKEN(ts).t_op; - pn2 = NewBinary(cx, TOK_COLON, op, pn3, AssignExpr(cx, ts, tc), tc); -#if JS_HAS_GETTER_SETTER - skip: -#endif - if (!pn2) - return NULL; - PN_APPEND(pn, pn2); - - tt = js_GetToken(cx, ts); - if (tt == TOK_RC) - goto end_obj_init; - if (tt != TOK_COMMA) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_CURLY_AFTER_LIST); - return NULL; - } - afterComma = JS_TRUE; - } - end_obj_init: - pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - return pn; - } - -#if JS_HAS_SHARP_VARS - case TOK_DEFSHARP: - if (defsharp) - goto badsharp; - defsharp = NewParseNode(cx, ts, PN_UNARY, tc); - if (!defsharp) - return NULL; - defsharp->pn_kid = NULL; - defsharp->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval; - ts->flags |= TSF_OPERAND; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_OPERAND; - goto again; - - case TOK_USESHARP: - /* Check for forward/dangling references at runtime, to allow eval. */ - pn = NewParseNode(cx, ts, PN_NULLARY, tc); - if (!pn) - return NULL; - pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval; - notsharp = JS_TRUE; - break; -#endif /* JS_HAS_SHARP_VARS */ - - case TOK_LP: - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - pn2 = BracketedExpr(cx, ts, tc); - if (!pn2) - return NULL; - - MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN); - if (pn2->pn_type == TOK_RP || - (js_CodeSpec[pn2->pn_op].prec >= js_CodeSpec[JSOP_GETPROP].prec && - !afterDot)) { - /* - * Avoid redundant JSOP_GROUP opcodes, for efficiency and mainly - * to help the decompiler look ahead from a JSOP_ENDINIT to see a - * JSOP_GROUP followed by a POP or POPV. That sequence means the - * parentheses are mandatory, to disambiguate object initialisers - * as expression statements from block statements. - * - * Also drop pn if pn2 is a member or a primary expression of any - * kind. This is required to avoid generating a JSOP_GROUP that - * will null the |obj| interpreter register, causing |this| in any - * call of that member expression to bind to the global object. - */ - pn->pn_kid = NULL; - RecycleTree(pn, tc); - pn = pn2; - } else { - pn->pn_type = TOK_RP; - pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - pn->pn_kid = pn2; - } - break; - -#if JS_HAS_XML_SUPPORT - case TOK_STAR: - pn = QualifiedIdentifier(cx, ts, tc); - if (!pn) - return NULL; - notsharp = JS_TRUE; - break; - - case TOK_AT: - pn = AttributeIdentifier(cx, ts, tc); - if (!pn) - return NULL; - notsharp = JS_TRUE; - break; - - case TOK_XMLSTAGO: - pn = XMLElementOrListRoot(cx, ts, tc, JS_TRUE); - if (!pn) - return NULL; - notsharp = JS_TRUE; /* XXXbe could be sharp? */ - break; -#endif /* JS_HAS_XML_SUPPORT */ - - case TOK_STRING: -#if JS_HAS_SHARP_VARS - notsharp = JS_TRUE; - /* FALL THROUGH */ -#endif - -#if JS_HAS_XML_SUPPORT - case TOK_XMLCDATA: - case TOK_XMLCOMMENT: - case TOK_XMLPI: -#endif - case TOK_NAME: - case TOK_OBJECT: - pn = NewParseNode(cx, ts, PN_NULLARY, tc); - if (!pn) - return NULL; - pn->pn_atom = CURRENT_TOKEN(ts).t_atom; -#if JS_HAS_XML_SUPPORT - if (tt == TOK_XMLPI) - pn->pn_atom2 = CURRENT_TOKEN(ts).t_atom2; - else -#endif - pn->pn_op = CURRENT_TOKEN(ts).t_op; - if (tt == TOK_NAME) { - pn->pn_arity = PN_NAME; - pn->pn_expr = NULL; - pn->pn_slot = -1; - pn->pn_attrs = 0; - -#if JS_HAS_XML_SUPPORT - if (js_MatchToken(cx, ts, TOK_DBLCOLON)) { - if (afterDot) { - JSString *str; - - /* - * Here PrimaryExpr is called after '.' or '..' and we - * just scanned .name:: or ..name:: . This is the only - * case where a keyword after '.' or '..' is not - * treated as a property name. - */ - str = ATOM_TO_STRING(pn->pn_atom); - tt = js_CheckKeyword(JSSTRING_CHARS(str), - JSSTRING_LENGTH(str)); - if (tt == TOK_FUNCTION) { - pn->pn_arity = PN_NULLARY; - pn->pn_type = TOK_FUNCTION; - } else if (tt != TOK_EOF) { - js_ReportCompileErrorNumber( - cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_KEYWORD_NOT_NS); - return NULL; - } - } - pn = QualifiedSuffix(cx, ts, pn, tc); - if (!pn) - return NULL; - break; - } -#endif - - /* Unqualified __parent__ and __proto__ uses require activations. */ - if (pn->pn_atom == cx->runtime->atomState.parentAtom || - pn->pn_atom == cx->runtime->atomState.protoAtom) { - tc->flags |= TCF_FUN_HEAVYWEIGHT; - } else { - JSAtomListElement *ale; - JSStackFrame *fp; - JSBool loopy; - - /* Measure optimizable global variable uses. */ - ATOM_LIST_SEARCH(ale, &tc->decls, pn->pn_atom); - if (ale && - !(fp = cx->fp)->fun && - fp->scopeChain == fp->varobj && - js_IsGlobalReference(tc, pn->pn_atom, &loopy)) { - tc->globalUses++; - if (loopy) - tc->loopyGlobalUses++; - } - } - } - break; - - case TOK_NUMBER: - pn = NewParseNode(cx, ts, PN_NULLARY, tc); - if (!pn) - return NULL; - pn->pn_dval = CURRENT_TOKEN(ts).t_dval; -#if JS_HAS_SHARP_VARS - notsharp = JS_TRUE; -#endif - break; - - case TOK_PRIMARY: - pn = NewParseNode(cx, ts, PN_NULLARY, tc); - if (!pn) - return NULL; - pn->pn_op = CURRENT_TOKEN(ts).t_op; -#if JS_HAS_SHARP_VARS - notsharp = JS_TRUE; -#endif - break; - -#if !JS_HAS_EXPORT_IMPORT - case TOK_EXPORT: - case TOK_IMPORT: -#endif - case TOK_ERROR: - /* The scanner or one of its subroutines reported the error. */ - return NULL; - - default: - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_SYNTAX_ERROR); - return NULL; - } - -#if JS_HAS_SHARP_VARS - if (defsharp) { - if (notsharp) { - badsharp: - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_SHARP_VAR_DEF); - return NULL; - } - defsharp->pn_kid = pn; - return defsharp; - } -#endif - return pn; -} - -/* - * Fold from one constant type to another. - * XXX handles only strings and numbers for now - */ -static JSBool -FoldType(JSContext *cx, JSParseNode *pn, JSTokenType type) -{ - if (pn->pn_type != type) { - switch (type) { - case TOK_NUMBER: - if (pn->pn_type == TOK_STRING) { - jsdouble d; - if (!js_ValueToNumber(cx, ATOM_KEY(pn->pn_atom), &d)) - return JS_FALSE; - pn->pn_dval = d; - pn->pn_type = TOK_NUMBER; - pn->pn_op = JSOP_NUMBER; - } - break; - - case TOK_STRING: - if (pn->pn_type == TOK_NUMBER) { - JSString *str = js_NumberToString(cx, pn->pn_dval); - if (!str) - return JS_FALSE; - pn->pn_atom = js_AtomizeString(cx, str, 0); - if (!pn->pn_atom) - return JS_FALSE; - pn->pn_type = TOK_STRING; - pn->pn_op = JSOP_STRING; - } - break; - - default:; - } - } - return JS_TRUE; -} - -/* - * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless - * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after - * a successful call to this function. - */ -static JSBool -FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2, - JSParseNode *pn, JSTreeContext *tc) -{ - jsdouble d, d2; - int32 i, j; - uint32 u; - - JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER); - d = pn1->pn_dval; - d2 = pn2->pn_dval; - switch (op) { - case JSOP_LSH: - case JSOP_RSH: - if (!js_DoubleToECMAInt32(cx, d, &i)) - return JS_FALSE; - if (!js_DoubleToECMAInt32(cx, d2, &j)) - return JS_FALSE; - j &= 31; - d = (op == JSOP_LSH) ? i << j : i >> j; - break; - - case JSOP_URSH: - if (!js_DoubleToECMAUint32(cx, d, &u)) - return JS_FALSE; - if (!js_DoubleToECMAInt32(cx, d2, &j)) - return JS_FALSE; - j &= 31; - d = u >> j; - break; - - case JSOP_ADD: - d += d2; - break; - - case JSOP_SUB: - d -= d2; - break; - - case JSOP_MUL: - d *= d2; - break; - - case JSOP_DIV: - if (d2 == 0) { -#if defined(XP_WIN) - /* XXX MSVC miscompiles such that (NaN == 0) */ - if (JSDOUBLE_IS_NaN(d2)) - d = *cx->runtime->jsNaN; - else -#endif - if (d == 0 || JSDOUBLE_IS_NaN(d)) - d = *cx->runtime->jsNaN; - else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31) - d = *cx->runtime->jsNegativeInfinity; - else - d = *cx->runtime->jsPositiveInfinity; - } else { - d /= d2; - } - break; - - case JSOP_MOD: - if (d2 == 0) { - d = *cx->runtime->jsNaN; - } else { -#if defined(XP_WIN) - /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */ - if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2))) -#endif - d = fmod(d, d2); - } - break; - - default:; - } - - /* Take care to allow pn1 or pn2 to alias pn. */ - if (pn1 != pn) - RecycleTree(pn1, tc); - if (pn2 != pn) - RecycleTree(pn2, tc); - pn->pn_type = TOK_NUMBER; - pn->pn_op = JSOP_NUMBER; - pn->pn_arity = PN_NULLARY; - pn->pn_dval = d; - return JS_TRUE; -} - -#if JS_HAS_XML_SUPPORT - -static JSBool -FoldXMLConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) -{ - JSTokenType tt; - JSParseNode **pnp, *pn1, *pn2; - JSString *accum, *str; - uint32 i, j; - - JS_ASSERT(pn->pn_arity == PN_LIST); - tt = pn->pn_type; - pnp = &pn->pn_head; - pn1 = *pnp; - accum = NULL; - if ((pn->pn_extra & PNX_CANTFOLD) == 0) { - if (tt == TOK_XMLETAGO) - accum = ATOM_TO_STRING(cx->runtime->atomState.etagoAtom); - else if (tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) - accum = ATOM_TO_STRING(cx->runtime->atomState.stagoAtom); - } - - for (pn2 = pn1, i = j = 0; pn2; pn2 = pn2->pn_next, i++) { - /* The parser already rejected end-tags with attributes. */ - JS_ASSERT(tt != TOK_XMLETAGO || i == 0); - switch (pn2->pn_type) { - case TOK_XMLATTR: - if (!accum) - goto cantfold; - /* FALL THROUGH */ - case TOK_XMLNAME: - case TOK_XMLSPACE: - case TOK_XMLTEXT: - case TOK_STRING: - if (pn2->pn_arity == PN_LIST) - goto cantfold; - str = ATOM_TO_STRING(pn2->pn_atom); - break; - - case TOK_XMLCDATA: - str = js_MakeXMLCDATAString(cx, ATOM_TO_STRING(pn2->pn_atom)); - if (!str) - return JS_FALSE; - break; - - case TOK_XMLCOMMENT: - str = js_MakeXMLCommentString(cx, ATOM_TO_STRING(pn2->pn_atom)); - if (!str) - return JS_FALSE; - break; - - case TOK_XMLPI: - str = js_MakeXMLPIString(cx, ATOM_TO_STRING(pn2->pn_atom), - ATOM_TO_STRING(pn2->pn_atom2)); - if (!str) - return JS_FALSE; - break; - - cantfold: - default: - JS_ASSERT(*pnp == pn1); - if ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && - (i & 1) ^ (j & 1)) { -#ifdef DEBUG_brendanXXX - printf("1: %d, %d => %s\n", - i, j, accum ? JS_GetStringBytes(accum) : "NULL"); -#endif - } else if (accum && pn1 != pn2) { - while (pn1->pn_next != pn2) { - pn1 = RecycleTree(pn1, tc); - --pn->pn_count; - } - pn1->pn_type = TOK_XMLTEXT; - pn1->pn_op = JSOP_STRING; - pn1->pn_arity = PN_NULLARY; - pn1->pn_atom = js_AtomizeString(cx, accum, 0); - if (!pn1->pn_atom) - return JS_FALSE; - JS_ASSERT(pnp != &pn1->pn_next); - *pnp = pn1; - } - pnp = &pn2->pn_next; - pn1 = *pnp; - accum = NULL; - continue; - } - - if (accum) { - str = ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && i != 0) - ? js_AddAttributePart(cx, i & 1, accum, str) - : js_ConcatStrings(cx, accum, str); - if (!str) - return JS_FALSE; -#ifdef DEBUG_brendanXXX - printf("2: %d, %d => %s (%u)\n", - i, j, JS_GetStringBytes(str), JSSTRING_LENGTH(str)); -#endif - ++j; - } - accum = str; - } - - if (accum) { - str = NULL; - if ((pn->pn_extra & PNX_CANTFOLD) == 0) { - if (tt == TOK_XMLPTAGC) - str = ATOM_TO_STRING(cx->runtime->atomState.ptagcAtom); - else if (tt == TOK_XMLSTAGO || tt == TOK_XMLETAGO) - str = ATOM_TO_STRING(cx->runtime->atomState.tagcAtom); - } - if (str) { - accum = js_ConcatStrings(cx, accum, str); - if (!accum) - return JS_FALSE; - } - - JS_ASSERT(*pnp == pn1); - while (pn1->pn_next) { - pn1 = RecycleTree(pn1, tc); - --pn->pn_count; - } - pn1->pn_type = TOK_XMLTEXT; - pn1->pn_op = JSOP_STRING; - pn1->pn_arity = PN_NULLARY; - pn1->pn_atom = js_AtomizeString(cx, accum, 0); - if (!pn1->pn_atom) - return JS_FALSE; - JS_ASSERT(pnp != &pn1->pn_next); - *pnp = pn1; - } - - if (pn1 && pn->pn_count == 1) { - /* - * Only one node under pn, and it has been folded: move pn1 onto pn - * unless pn is an XML root (in which case we need it to tell the code - * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op). If pn is an - * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid - * extra "<" and "/>" bracketing at runtime. - */ - if (!(pn->pn_extra & PNX_XMLROOT)) { - PN_MOVE_NODE(pn, pn1); - } else if (tt == TOK_XMLPTAGC) { - pn->pn_type = TOK_XMLELEM; - pn->pn_op = JSOP_TOXML; - } - } - return JS_TRUE; -} - -#endif /* JS_HAS_XML_SUPPORT */ - -static JSBool -StartsWith(JSParseNode *pn, JSTokenType tt) -{ -#define TAIL_RECURSE(pn2) JS_BEGIN_MACRO pn = (pn2); goto recur; JS_END_MACRO - -recur: - if (pn->pn_type == tt) - return JS_TRUE; - switch (pn->pn_arity) { - case PN_FUNC: - return tt == TOK_FUNCTION; - case PN_LIST: - if (pn->pn_head) - TAIL_RECURSE(pn->pn_head); - break; - case PN_TERNARY: - if (pn->pn_kid1) - TAIL_RECURSE(pn->pn_kid1); - break; - case PN_BINARY: - if (pn->pn_left) - TAIL_RECURSE(pn->pn_left); - break; - case PN_UNARY: - /* A parenthesized expression starts with a left parenthesis. */ - if (pn->pn_type == TOK_RP) - return tt == TOK_LP; - if (pn->pn_kid) - TAIL_RECURSE(pn->pn_kid); - break; - case PN_NAME: - if (pn->pn_type == TOK_DOT || pn->pn_type == TOK_DBLDOT) - TAIL_RECURSE(pn->pn_expr); - /* FALL THROUGH */ - } - return JS_FALSE; -#undef TAIL_RECURSE -} - -JSBool -js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) -{ - JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL; - int stackDummy; - - if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); - return JS_FALSE; - } - - switch (pn->pn_arity) { - case PN_FUNC: - { - uint16 oldflags = tc->flags; - - tc->flags = (uint16) pn->pn_flags; - if (!js_FoldConstants(cx, pn->pn_body, tc)) - return JS_FALSE; - tc->flags = oldflags; - break; - } - - case PN_LIST: -#if 0 /* JS_HAS_XML_SUPPORT */ - switch (pn->pn_type) { - case TOK_XMLELEM: - case TOK_XMLLIST: - case TOK_XMLPTAGC: - /* - * Try to fold this XML parse tree once, from the top down, into - * a JSXML tree with just one object wrapping the tree root. - * - * Certain subtrees could be folded similarly, but we'd have to - * ensure that none used namespace prefixes declared elsewhere in - * its super-tree, and we would have to convert each XML object - * created at runtime for such sub-trees back into a string, and - * concatenate and re-parse anyway. - */ - if ((pn->pn_extra & (PNX_XMLROOT | PNX_CANTFOLD)) == PNX_XMLROOT && - !(tc->flags & TCF_HAS_DEFXMLNS)) { - JSObject *obj; - JSAtom *atom; - - obj = js_ParseNodeToXMLObject(cx, pn); - if (!obj) - return JS_FALSE; - atom = js_AtomizeObject(cx, obj, 0); - if (!atom) - return JS_FALSE; - pn->pn_op = JSOP_XMLOBJECT; - pn->pn_arity = PN_NULLARY; - pn->pn_atom = atom; - return JS_TRUE; - } - - /* - * Can't fold from parse node to XML tree -- try folding strings - * as much as possible, and folding XML sub-trees bottom up to - * minimize string concatenation and ToXML/ToXMLList operations - * at runtime. - */ - break; - - default:; - } -#endif - - /* Save the list head in pn1 for later use. */ - for (pn1 = pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - if (!js_FoldConstants(cx, pn2, tc)) - return JS_FALSE; - } - break; - - case PN_TERNARY: - /* Any kid may be null (e.g. for (;;)). */ - pn1 = pn->pn_kid1; - pn2 = pn->pn_kid2; - pn3 = pn->pn_kid3; - if (pn1 && !js_FoldConstants(cx, pn1, tc)) - return JS_FALSE; - if (pn2 && !js_FoldConstants(cx, pn2, tc)) - return JS_FALSE; - if (pn3 && !js_FoldConstants(cx, pn3, tc)) - return JS_FALSE; - break; - - case PN_BINARY: - /* First kid may be null (for default case in switch). */ - pn1 = pn->pn_left; - pn2 = pn->pn_right; - if (pn1 && !js_FoldConstants(cx, pn1, tc)) - return JS_FALSE; - if (!js_FoldConstants(cx, pn2, tc)) - return JS_FALSE; - break; - - case PN_UNARY: - /* Our kid may be null (e.g. return; vs. return e;). */ - pn1 = pn->pn_kid; - if (pn1 && !js_FoldConstants(cx, pn1, tc)) - return JS_FALSE; - break; - - case PN_NAME: - /* - * Skip pn1 down along a chain of dotted member expressions to avoid - * excessive recursion. Our only goal here is to fold constants (if - * any) in the primary expression operand to the left of the first - * dot in the chain. - */ - pn1 = pn->pn_expr; - while (pn1 && pn1->pn_arity == PN_NAME) - pn1 = pn1->pn_expr; - if (pn1 && !js_FoldConstants(cx, pn1, tc)) - return JS_FALSE; - break; - - case PN_NULLARY: - break; - } - - switch (pn->pn_type) { - case TOK_IF: - if (ContainsStmt(pn2, TOK_VAR) || ContainsStmt(pn3, TOK_VAR)) - break; - /* FALL THROUGH */ - - case TOK_HOOK: - /* Reduce 'if (C) T; else E' into T for true C, E for false. */ - while (pn1->pn_type == TOK_RP) - pn1 = pn1->pn_kid; - switch (pn1->pn_type) { - case TOK_NUMBER: - if (pn1->pn_dval == 0) - pn2 = pn3; - break; - case TOK_STRING: - if (JSSTRING_LENGTH(ATOM_TO_STRING(pn1->pn_atom)) == 0) - pn2 = pn3; - break; - case TOK_PRIMARY: - if (pn1->pn_op == JSOP_TRUE) - break; - if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) { - pn2 = pn3; - break; - } - /* FALL THROUGH */ - default: - /* Early return to dodge common code that copies pn2 to pn. */ - return JS_TRUE; - } - - if (pn2) { - /* - * pn2 is the then- or else-statement subtree to compile. Take - * care not to expose an object initialiser, which would be parsed - * as a block, to the Statement parser via eval(uneval(e)) where e - * is '1 ? {p:2, q:3}[i] : r;' or the like. - */ - if (pn->pn_type == TOK_HOOK && StartsWith(pn2, TOK_RC)) { - pn->pn_type = TOK_RP; - pn->pn_arity = PN_UNARY; - pn->pn_kid = pn2; - } else { - PN_MOVE_NODE(pn, pn2); - } - } - if (!pn2 || (pn->pn_type == TOK_SEMI && !pn->pn_kid)) { - /* - * False condition and no else, or an empty then-statement was - * moved up over pn. Either way, make pn an empty block (not an - * empty statement, which does not decompile, even when labeled). - * NB: pn must be a TOK_IF as TOK_HOOK can never have a null kid - * or an empty statement for a child. - */ - pn->pn_type = TOK_LC; - pn->pn_arity = PN_LIST; - PN_INIT_LIST(pn); - } - RecycleTree(pn2, tc); - if (pn3 && pn3 != pn2) - RecycleTree(pn3, tc); - break; - - case TOK_ASSIGN: - /* - * Compound operators such as *= should be subject to folding, in case - * the left-hand side is constant, and so that the decompiler produces - * the same string that you get from decompiling a script or function - * compiled from that same string. As with +, += is special. - */ - if (pn->pn_op == JSOP_NOP) - break; - if (pn->pn_op != JSOP_ADD) - goto do_binary_op; - /* FALL THROUGH */ - - case TOK_PLUS: - if (pn->pn_arity == PN_LIST) { - size_t length, length2; - jschar *chars; - JSString *str, *str2; - - /* - * Any string literal term with all others number or string means - * this is a concatenation. If any term is not a string or number - * literal, we can't fold. - */ - JS_ASSERT(pn->pn_count > 2); - if (pn->pn_extra & PNX_CANTFOLD) - return JS_TRUE; - if (pn->pn_extra != PNX_STRCAT) - goto do_binary_op; - - /* Ok, we're concatenating: convert non-string constant operands. */ - length = 0; - for (pn2 = pn1; pn2; pn2 = pn2->pn_next) { - if (!FoldType(cx, pn2, TOK_STRING)) - return JS_FALSE; - /* XXX fold only if all operands convert to string */ - if (pn2->pn_type != TOK_STRING) - return JS_TRUE; - length += ATOM_TO_STRING(pn2->pn_atom)->length; - } - - /* Allocate a new buffer and string descriptor for the result. */ - chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); - if (!chars) - return JS_FALSE; - str = js_NewString(cx, chars, length, 0); - if (!str) { - JS_free(cx, chars); - return JS_FALSE; - } - - /* Fill the buffer, advancing chars and recycling kids as we go. */ - for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) { - str2 = ATOM_TO_STRING(pn2->pn_atom); - length2 = str2->length; - js_strncpy(chars, str2->chars, length2); - chars += length2; - } - *chars = 0; - - /* Atomize the result string and mutate pn to refer to it. */ - pn->pn_atom = js_AtomizeString(cx, str, 0); - if (!pn->pn_atom) - return JS_FALSE; - pn->pn_type = TOK_STRING; - pn->pn_op = JSOP_STRING; - pn->pn_arity = PN_NULLARY; - break; - } - - /* Handle a binary string concatenation. */ - JS_ASSERT(pn->pn_arity == PN_BINARY); - if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) { - JSString *left, *right, *str; - - if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2, - TOK_STRING)) { - return JS_FALSE; - } - if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING) - return JS_TRUE; - left = ATOM_TO_STRING(pn1->pn_atom); - right = ATOM_TO_STRING(pn2->pn_atom); - str = js_ConcatStrings(cx, left, right); - if (!str) - return JS_FALSE; - pn->pn_atom = js_AtomizeString(cx, str, 0); - if (!pn->pn_atom) - return JS_FALSE; - pn->pn_type = TOK_STRING; - pn->pn_op = JSOP_STRING; - pn->pn_arity = PN_NULLARY; - RecycleTree(pn1, tc); - RecycleTree(pn2, tc); - break; - } - - /* Can't concatenate string literals, let's try numbers. */ - goto do_binary_op; - - case TOK_STAR: - /* The * in 'import *;' parses as a nullary star node. */ - if (pn->pn_arity == PN_NULLARY) - break; - /* FALL THROUGH */ - - case TOK_SHOP: - case TOK_MINUS: - case TOK_DIVOP: - do_binary_op: - if (pn->pn_arity == PN_LIST) { - JS_ASSERT(pn->pn_count > 2); - for (pn2 = pn1; pn2; pn2 = pn2->pn_next) { - if (!FoldType(cx, pn2, TOK_NUMBER)) - return JS_FALSE; - } - for (pn2 = pn1; pn2; pn2 = pn2->pn_next) { - /* XXX fold only if all operands convert to number */ - if (pn2->pn_type != TOK_NUMBER) - break; - } - if (!pn2) { - JSOp op = pn->pn_op; - - pn2 = pn1->pn_next; - pn3 = pn2->pn_next; - if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc)) - return JS_FALSE; - while ((pn2 = pn3) != NULL) { - pn3 = pn2->pn_next; - if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc)) - return JS_FALSE; - } - } - } else { - JS_ASSERT(pn->pn_arity == PN_BINARY); - if (!FoldType(cx, pn1, TOK_NUMBER) || - !FoldType(cx, pn2, TOK_NUMBER)) { - return JS_FALSE; - } - if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) { - if (!FoldBinaryNumeric(cx, pn->pn_op, pn1, pn2, pn, tc)) - return JS_FALSE; - } - } - break; - - case TOK_UNARYOP: - while (pn1->pn_type == TOK_RP) - pn1 = pn1->pn_kid; - if (pn1->pn_type == TOK_NUMBER) { - jsdouble d; - int32 i; - - /* Operate on one numeric constant. */ - d = pn1->pn_dval; - switch (pn->pn_op) { - case JSOP_BITNOT: - if (!js_DoubleToECMAInt32(cx, d, &i)) - return JS_FALSE; - d = ~i; - break; - - case JSOP_NEG: -#ifdef HPUX - /* - * Negation of a zero doesn't produce a negative - * zero on HPUX. Perform the operation by bit - * twiddling. - */ - JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT; -#else - d = -d; -#endif - break; - - case JSOP_POS: - break; - - case JSOP_NOT: - pn->pn_type = TOK_PRIMARY; - pn->pn_op = (d == 0) ? JSOP_TRUE : JSOP_FALSE; - pn->pn_arity = PN_NULLARY; - /* FALL THROUGH */ - - default: - /* Return early to dodge the common TOK_NUMBER code. */ - return JS_TRUE; - } - pn->pn_type = TOK_NUMBER; - pn->pn_op = JSOP_NUMBER; - pn->pn_arity = PN_NULLARY; - pn->pn_dval = d; - RecycleTree(pn1, tc); - } - break; - -#if JS_HAS_XML_SUPPORT - case TOK_XMLELEM: - case TOK_XMLLIST: - case TOK_XMLPTAGC: - case TOK_XMLSTAGO: - case TOK_XMLETAGO: - case TOK_XMLNAME: - if (pn->pn_arity == PN_LIST) { - JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0); - if (!FoldXMLConstants(cx, pn, tc)) - return JS_FALSE; - } - break; - - case TOK_AT: - if (pn1->pn_type == TOK_XMLNAME) { - jsval v; - JSAtom *atom; - - v = ATOM_KEY(pn1->pn_atom); - if (!js_ToAttributeName(cx, &v)) - return JS_FALSE; - JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); - atom = js_AtomizeObject(cx, JSVAL_TO_OBJECT(v), 0); - if (!atom) - return JS_FALSE; - - pn->pn_type = TOK_XMLNAME; - pn->pn_op = JSOP_OBJECT; - pn->pn_arity = PN_NULLARY; - pn->pn_atom = atom; - RecycleTree(pn1, tc); - } - break; -#endif /* JS_HAS_XML_SUPPORT */ - - default:; - } - - return JS_TRUE; -} diff --git a/spidermonkey/src/jsparse.h b/spidermonkey/src/jsparse.h deleted file mode 100644 index 68b0926..0000000 --- a/spidermonkey/src/jsparse.h +++ /dev/null @@ -1,439 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsparse_h___ -#define jsparse_h___ -/* - * JS parser definitions. - */ -#include "jsconfig.h" -#include "jsprvtd.h" -#include "jspubtd.h" -#include "jsscan.h" - -JS_BEGIN_EXTERN_C - -/* - * Parsing builds a tree of nodes that directs code generation. This tree is - * not a concrete syntax tree in all respects (for example, || and && are left - * associative, but (A && B && C) translates into the right-associated tree - * > so that code generation can emit a left-associative branch - * around when A is false). Nodes are labeled by token type, with a - * JSOp secondary label when needed: - * - * Label Variant Members - * ----- ------- ------- - * - * TOK_FUNCTION func pn_funAtom: atom holding function object containing - * arg and var properties. We create the function - * object at parse (not emit) time to specialize arg - * and var bytecodes early. - * pn_body: TOK_LC node for function body statements - * pn_flags: TCF_FUN_* flags (see jsemit.h) collected - * while parsing the function's body - * pn_tryCount: of try statements in function - * - * - * TOK_LC list pn_head: list of pn_count statements - * TOK_EXPORT list pn_head: list of pn_count TOK_NAMEs or one TOK_STAR - * (which is not a multiply node) - * TOK_IMPORT list pn_head: list of pn_count sub-trees of the form - * a.b.*, a[b].*, a.*, a.b, or a[b] -- but never a. - * Each member is expressed with TOK_DOT or TOK_LB. - * Each sub-tree's root node has a pn_op in the set - * JSOP_IMPORT{ALL,PROP,ELEM} - * TOK_IF ternary pn_kid1: cond, pn_kid2: then, pn_kid3: else or null - * TOK_SWITCH binary pn_left: discriminant - * pn_right: list of TOK_CASE nodes, with at most one - * TOK_DEFAULT node, or if there are let bindings - * in the top level of the switch body's cases, a - * TOK_LEXICALSCOPE node that contains the list of - * TOK_CASE nodes. - * TOK_CASE, binary pn_left: case expr or null if TOK_DEFAULT - * TOK_DEFAULT pn_right: TOK_LC node for this case's statements - * pn_val: constant value if lookup or table switch - * TOK_WHILE binary pn_left: cond, pn_right: body - * TOK_DO binary pn_left: body, pn_right: cond - * TOK_FOR binary pn_left: either - * for/in loop: a binary TOK_IN node with - * pn_left: TOK_VAR or TOK_NAME to left of 'in' - * if TOK_VAR, its pn_extra may have PNX_POPVAR - * and PNX_FORINVAR bits set - * pn_right: object expr to right of 'in' - * for(;;) loop: a ternary TOK_RESERVED node with - * pn_kid1: init expr before first ';' - * pn_kid2: cond expr before second ';' - * pn_kid3: update expr after second ';' - * any kid may be null - * pn_right: body - * TOK_THROW unary pn_op: JSOP_THROW, pn_kid: exception - * TOK_TRY ternary pn_kid1: try block - * pn_kid2: null or TOK_RESERVED list of - * TOK_LEXICALSCOPE nodes, each with pn_expr pointing - * to a TOK_CATCH node - * pn_kid3: null or finally block - * TOK_CATCH ternary pn_kid1: TOK_NAME, TOK_RB, or TOK_RC catch var node - * (TOK_RB or TOK_RC if destructuring) - * pn_kid2: null or the catch guard expression - * pn_kid3: catch block statements - * TOK_BREAK name pn_atom: label or null - * TOK_CONTINUE name pn_atom: label or null - * TOK_WITH binary pn_left: head expr, pn_right: body - * TOK_VAR list pn_head: list of pn_count TOK_NAME nodes - * each name node has - * pn_atom: variable name - * pn_expr: initializer or null - * TOK_RETURN unary pn_kid: return expr or null - * TOK_SEMI unary pn_kid: expr or null statement - * TOK_COLON name pn_atom: label, pn_expr: labeled statement - * - * - * All left-associated binary trees of the same type are optimized into lists - * to avoid recursion when processing expression chains. - * TOK_COMMA list pn_head: list of pn_count comma-separated exprs - * TOK_ASSIGN binary pn_left: lvalue, pn_right: rvalue - * pn_op: JSOP_ADD for +=, etc. - * TOK_HOOK ternary pn_kid1: cond, pn_kid2: then, pn_kid3: else - * TOK_OR binary pn_left: first in || chain, pn_right: rest of chain - * TOK_AND binary pn_left: first in && chain, pn_right: rest of chain - * TOK_BITOR binary pn_left: left-assoc | expr, pn_right: ^ expr - * TOK_BITXOR binary pn_left: left-assoc ^ expr, pn_right: & expr - * TOK_BITAND binary pn_left: left-assoc & expr, pn_right: EQ expr - * TOK_EQOP binary pn_left: left-assoc EQ expr, pn_right: REL expr - * pn_op: JSOP_EQ, JSOP_NE, JSOP_NEW_EQ, JSOP_NEW_NE - * TOK_RELOP binary pn_left: left-assoc REL expr, pn_right: SH expr - * pn_op: JSOP_LT, JSOP_LE, JSOP_GT, JSOP_GE - * TOK_SHOP binary pn_left: left-assoc SH expr, pn_right: ADD expr - * pn_op: JSOP_LSH, JSOP_RSH, JSOP_URSH - * TOK_PLUS, binary pn_left: left-assoc ADD expr, pn_right: MUL expr - * pn_extra: if a left-associated binary TOK_PLUS - * tree has been flattened into a list (see above - * under ), pn_extra will contain - * PNX_STRCAT if at least one list element is a - * string literal (TOK_STRING); if such a list has - * any non-string, non-number term, pn_extra will - * contain PNX_CANTFOLD. - * pn_ - * TOK_MINUS pn_op: JSOP_ADD, JSOP_SUB - * TOK_STAR, binary pn_left: left-assoc MUL expr, pn_right: UNARY expr - * TOK_DIVOP pn_op: JSOP_MUL, JSOP_DIV, JSOP_MOD - * TOK_UNARYOP unary pn_kid: UNARY expr, pn_op: JSOP_NEG, JSOP_POS, - * JSOP_NOT, JSOP_BITNOT, JSOP_TYPEOF, JSOP_VOID - * TOK_INC, unary pn_kid: MEMBER expr - * TOK_DEC - * TOK_NEW list pn_head: list of ctor, arg1, arg2, ... argN - * pn_count: 1 + N (where N is number of args) - * ctor is a MEMBER expr - * TOK_DELETE unary pn_kid: MEMBER expr - * TOK_DOT, name pn_expr: MEMBER expr to left of . - * TOK_DBLDOT pn_atom: name to right of . - * TOK_LB binary pn_left: MEMBER expr to left of [ - * pn_right: expr between [ and ] - * TOK_LP list pn_head: list of call, arg1, arg2, ... argN - * pn_count: 1 + N (where N is number of args) - * call is a MEMBER expr naming a callable object - * TOK_RB list pn_head: list of pn_count array element exprs - * [,,] holes are represented by TOK_COMMA nodes - * #n=[...] produces TOK_DEFSHARP at head of list - * pn_extra: PN_ENDCOMMA if extra comma at end - * TOK_RC list pn_head: list of pn_count TOK_COLON nodes where - * each has pn_left: property id, pn_right: value - * #n={...} produces TOK_DEFSHARP at head of list - * TOK_DEFSHARP unary pn_num: jsint value of n in #n= - * pn_kid: null for #n=[...] and #n={...}, primary - * if #n=primary for function, paren, name, object - * literal expressions - * TOK_USESHARP nullary pn_num: jsint value of n in #n# - * TOK_RP unary pn_kid: parenthesized expression - * TOK_NAME, name pn_atom: name, string, or object atom - * TOK_STRING, pn_op: JSOP_NAME, JSOP_STRING, or JSOP_OBJECT, or - * JSOP_REGEXP - * TOK_OBJECT If JSOP_NAME, pn_op may be JSOP_*ARG or JSOP_*VAR - * with pn_slot >= 0 and pn_attrs telling const-ness - * TOK_NUMBER dval pn_dval: double value of numeric literal - * TOK_PRIMARY nullary pn_op: JSOp bytecode - * - * - * TOK_ANYNAME nullary pn_op: JSOP_ANYNAME - * pn_atom: cx->runtime->atomState.starAtom - * TOK_AT unary pn_op: JSOP_TOATTRNAME; pn_kid attribute id/expr - * TOK_DBLCOLON binary pn_op: JSOP_QNAME - * pn_left: TOK_ANYNAME or TOK_NAME node - * pn_right: TOK_STRING "*" node, or expr within [] - * name pn_op: JSOP_QNAMECONST - * pn_expr: TOK_ANYNAME or TOK_NAME left operand - * pn_atom: name on right of :: - * TOK_XMLELEM list XML element node - * pn_head: start tag, content1, ... contentN, end tag - * pn_count: 2 + N where N is number of content nodes - * N may be > x.length() if {expr} embedded - * TOK_XMLLIST list XML list node - * pn_head: content1, ... contentN - * TOK_XMLSTAGO, list XML start, end, and point tag contents - * TOK_XMLETAGC, pn_head: tag name or {expr}, ... XML attrs ... - * TOK_XMLPTAGO - * TOK_XMLNAME nullary pn_atom: XML name, with no {expr} embedded - * TOK_XMLNAME list pn_head: tag name or {expr}, ... name or {expr} - * TOK_XMLATTR, nullary pn_atom: attribute value string; pn_op: JSOP_STRING - * TOK_XMLCDATA, - * TOK_XMLCOMMENT - * TOK_XMLPI nullary pn_atom: XML processing instruction target - * pn_atom2: XML PI content, or null if no content - * TOK_XMLTEXT nullary pn_atom: marked-up text, or null if empty string - * TOK_LC unary {expr} in XML tag or content; pn_kid is expr - * - * So an XML tag with no {expr} and three attributes is a list with the form: - * - * (tagname attrname1 attrvalue1 attrname2 attrvalue2 attrname2 attrvalue3) - * - * An XML tag with embedded expressions like so: - * - * - * - * would have the form: - * - * ((name1 {expr1}) (name2 {expr2} name3) {expr3}) - * - * where () bracket a list with elements separated by spaces, and {expr} is a - * TOK_LC unary node with expr as its kid. - * - * Thus, the attribute name/value pairs occupy successive odd and even list - * locations, where pn_head is the TOK_XMLNAME node at list location 0. The - * parser builds the same sort of structures for elements: - * - * Hi there!How are you?{x + y} - * - * translates to: - * - * ((a x {x}) 'Hi there!' ((b y {y}) 'How are you?') ((answer) {x + y})) - * - * - * - * Label Variant Members - * ----- ------- ------- - * TOK_LEXICALSCOPE name pn_op: JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR - * pn_atom: block object - * pn_expr: block body - * TOK_ARRAYCOMP list pn_head: list of pn_count (1 or 2) elements - * if pn_count is 2, first element is #n=[...] - * last element is block enclosing for loop(s) - * and optionally if-guarded TOK_ARRAYPUSH - * pn_extra: stack slot, used during code gen - * TOK_ARRAYPUSH unary pn_op: JSOP_ARRAYCOMP - * pn_kid: array comprehension expression - */ -typedef enum JSParseNodeArity { - PN_FUNC = -3, - PN_LIST = -2, - PN_TERNARY = 3, - PN_BINARY = 2, - PN_UNARY = 1, - PN_NAME = -1, - PN_NULLARY = 0 -} JSParseNodeArity; - -struct JSParseNode { - uint16 pn_type; - uint8 pn_op; - int8 pn_arity; - JSTokenPos pn_pos; - ptrdiff_t pn_offset; /* first generated bytecode offset */ - union { - struct { /* TOK_FUNCTION node */ - JSAtom *funAtom; /* atomized function object */ - JSParseNode *body; /* TOK_LC list of statements */ - uint32 flags; /* accumulated tree context flags */ - uint32 tryCount; /* count of try statements in body */ - } func; - struct { /* list of next-linked nodes */ - JSParseNode *head; /* first node in list */ - JSParseNode **tail; /* ptr to ptr to last node in list */ - uint32 count; /* number of nodes in list */ - uint32 extra; /* extra flags, see below */ - } list; - struct { /* ternary: if, for(;;), ?: */ - JSParseNode *kid1; /* condition, discriminant, etc. */ - JSParseNode *kid2; /* then-part, case list, etc. */ - JSParseNode *kid3; /* else-part, default case, etc. */ - } ternary; - struct { /* two kids if binary */ - JSParseNode *left; - JSParseNode *right; - jsval val; /* switch case value */ - } binary; - struct { /* one kid if unary */ - JSParseNode *kid; - jsint num; /* -1 or sharp variable number */ - } unary; - struct { /* name, labeled statement, etc. */ - JSAtom *atom; /* name or label atom, null if slot */ - JSParseNode *expr; /* object or initializer */ - jsint slot; /* -1 or arg or local var slot */ - uintN attrs; /* attributes if local var or const */ - } name; - struct { - JSAtom *atom; /* first atom in pair */ - JSAtom *atom2; /* second atom in pair or null */ - } apair; - jsdouble dval; /* aligned numeric literal value */ - } pn_u; - JSParseNode *pn_next; /* to align dval and pn_u on RISCs */ - JSTokenStream *pn_ts; /* token stream for error reports */ - JSAtom *pn_source; /* saved source for decompilation */ - JSBool pn_no_semi; /* missing semicolon */ -}; - -#define pn_funAtom pn_u.func.funAtom -#define pn_body pn_u.func.body -#define pn_flags pn_u.func.flags -#define pn_tryCount pn_u.func.tryCount -#define pn_head pn_u.list.head -#define pn_tail pn_u.list.tail -#define pn_count pn_u.list.count -#define pn_extra pn_u.list.extra -#define pn_kid1 pn_u.ternary.kid1 -#define pn_kid2 pn_u.ternary.kid2 -#define pn_kid3 pn_u.ternary.kid3 -#define pn_left pn_u.binary.left -#define pn_right pn_u.binary.right -#define pn_val pn_u.binary.val -#define pn_kid pn_u.unary.kid -#define pn_num pn_u.unary.num -#define pn_atom pn_u.name.atom -#define pn_expr pn_u.name.expr -#define pn_slot pn_u.name.slot -#define pn_attrs pn_u.name.attrs -#define pn_dval pn_u.dval -#define pn_atom2 pn_u.apair.atom2 - -/* PN_LIST pn_extra flags. */ -#define PNX_STRCAT 0x01 /* TOK_PLUS list has string term */ -#define PNX_CANTFOLD 0x02 /* TOK_PLUS list has unfoldable term */ -#define PNX_POPVAR 0x04 /* TOK_VAR last result needs popping */ -#define PNX_FORINVAR 0x08 /* TOK_VAR is left kid of TOK_IN node, - which is left kid of TOK_FOR */ -#define PNX_ENDCOMMA 0x10 /* array literal has comma at end */ -#define PNX_XMLROOT 0x20 /* top-most node in XML literal tree */ -#define PNX_GROUPINIT 0x40 /* var [a, b] = [c, d]; unit list */ -#define PNX_NEEDBRACES 0x80 /* braces necessary due to closure */ - -/* - * Move pn2 into pn, preserving pn->pn_pos and pn->pn_offset and handing off - * any kids in pn2->pn_u, by clearing pn2. - */ -#define PN_MOVE_NODE(pn, pn2) \ - JS_BEGIN_MACRO \ - (pn)->pn_type = (pn2)->pn_type; \ - (pn)->pn_op = (pn2)->pn_op; \ - (pn)->pn_arity = (pn2)->pn_arity; \ - (pn)->pn_u = (pn2)->pn_u; \ - PN_CLEAR_NODE(pn2); \ - JS_END_MACRO - -#define PN_CLEAR_NODE(pn) \ - JS_BEGIN_MACRO \ - (pn)->pn_type = TOK_EOF; \ - (pn)->pn_op = JSOP_NOP; \ - (pn)->pn_arity = PN_NULLARY; \ - JS_END_MACRO - -/* True if pn is a parsenode representing a literal constant. */ -#define PN_IS_CONSTANT(pn) \ - ((pn)->pn_type == TOK_NUMBER || \ - (pn)->pn_type == TOK_STRING || \ - ((pn)->pn_type == TOK_PRIMARY && (pn)->pn_op != JSOP_THIS)) - -/* - * Compute a pointer to the last JSParseNode element in a singly-linked list. - * NB: list must be non-empty for correct PN_LAST usage! - */ -#define PN_LAST(list) \ - ((JSParseNode *)((char *)(list)->pn_tail - offsetof(JSParseNode, pn_next))) - -#define PN_INIT_LIST(list) \ - JS_BEGIN_MACRO \ - (list)->pn_head = NULL; \ - (list)->pn_tail = &(list)->pn_head; \ - (list)->pn_count = (list)->pn_extra = 0; \ - JS_END_MACRO - -#define PN_INIT_LIST_1(list, pn) \ - JS_BEGIN_MACRO \ - (list)->pn_head = (pn); \ - (list)->pn_tail = &(pn)->pn_next; \ - (list)->pn_count = 1; \ - (list)->pn_extra = 0; \ - JS_END_MACRO - -#define PN_APPEND(list, pn) \ - JS_BEGIN_MACRO \ - *(list)->pn_tail = (pn); \ - (list)->pn_tail = &(pn)->pn_next; \ - (list)->pn_count++; \ - JS_END_MACRO - -/* - * Parse a top-level JS script. - * - * The caller must prevent the GC from running while this function is active, - * because atoms and function newborns are not rooted yet. - */ -extern JS_FRIEND_API(JSParseNode *) -js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts); - -extern JS_FRIEND_API(JSBool) -js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, - JSCodeGenerator *cg); - -extern JSBool -js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun); - -extern JSBool -js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc); - -#if JS_HAS_XML_SUPPORT -JS_FRIEND_API(JSParseNode *) -js_ParseXMLTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, - JSBool allowList); -#endif - -JS_END_EXTERN_C - -#endif /* jsparse_h___ */ diff --git a/spidermonkey/src/jsprf.c b/spidermonkey/src/jsprf.c deleted file mode 100644 index b8fc186..0000000 --- a/spidermonkey/src/jsprf.c +++ /dev/null @@ -1,1266 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* -** Portable safe sprintf code. -** -** Author: Kipp E.B. Hickman -*/ -#include "jsstddef.h" -#include -#include -#include -#include -#include "jsprf.h" -#include "jslong.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jspubtd.h" -#include "jsstr.h" - -/* -** Note: on some platforms va_list is defined as an array, -** and requires array notation. -*/ -#ifdef HAVE_VA_COPY -#define VARARGS_ASSIGN(foo, bar) VA_COPY(foo,bar) -#elif defined(va_copy) -#define VARARGS_ASSIGN(foo, bar) va_copy(foo,bar) -#elif defined(HAVE_VA_LIST_AS_ARRAY) -#define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0] -#else -#define VARARGS_ASSIGN(foo, bar) (foo) = (bar) -#endif - -/* -** WARNING: This code may *NOT* call JS_LOG (because JS_LOG calls it) -*/ - -/* -** XXX This needs to be internationalized! -*/ - -typedef struct SprintfStateStr SprintfState; - -struct SprintfStateStr { - int (*stuff)(SprintfState *ss, const char *sp, JSUint32 len); - - char *base; - char *cur; - JSUint32 maxlen; - - int (*func)(void *arg, const char *sp, JSUint32 len); - void *arg; -}; - -/* -** Numbered Arguement State -*/ -struct NumArgState{ - int type; /* type of the current ap */ - va_list ap; /* point to the corresponding position on ap */ -}; - -#define NAS_DEFAULT_NUM 20 /* default number of NumberedArgumentState array */ - - -#define TYPE_INT16 0 -#define TYPE_UINT16 1 -#define TYPE_INTN 2 -#define TYPE_UINTN 3 -#define TYPE_INT32 4 -#define TYPE_UINT32 5 -#define TYPE_INT64 6 -#define TYPE_UINT64 7 -#define TYPE_STRING 8 -#define TYPE_DOUBLE 9 -#define TYPE_INTSTR 10 -#define TYPE_WSTRING 11 -#define TYPE_UNKNOWN 20 - -#define FLAG_LEFT 0x1 -#define FLAG_SIGNED 0x2 -#define FLAG_SPACED 0x4 -#define FLAG_ZEROS 0x8 -#define FLAG_NEG 0x10 - -/* -** Fill into the buffer using the data in src -*/ -static int fill2(SprintfState *ss, const char *src, int srclen, int width, - int flags) -{ - char space = ' '; - int rv; - - width -= srclen; - if ((width > 0) && ((flags & FLAG_LEFT) == 0)) { /* Right adjusting */ - if (flags & FLAG_ZEROS) { - space = '0'; - } - while (--width >= 0) { - rv = (*ss->stuff)(ss, &space, 1); - if (rv < 0) { - return rv; - } - } - } - - /* Copy out the source data */ - rv = (*ss->stuff)(ss, src, (JSUint32)srclen); - if (rv < 0) { - return rv; - } - - if ((width > 0) && ((flags & FLAG_LEFT) != 0)) { /* Left adjusting */ - while (--width >= 0) { - rv = (*ss->stuff)(ss, &space, 1); - if (rv < 0) { - return rv; - } - } - } - return 0; -} - -/* -** Fill a number. The order is: optional-sign zero-filling conversion-digits -*/ -static int fill_n(SprintfState *ss, const char *src, int srclen, int width, - int prec, int type, int flags) -{ - int zerowidth = 0; - int precwidth = 0; - int signwidth = 0; - int leftspaces = 0; - int rightspaces = 0; - int cvtwidth; - int rv; - char sign; - - if ((type & 1) == 0) { - if (flags & FLAG_NEG) { - sign = '-'; - signwidth = 1; - } else if (flags & FLAG_SIGNED) { - sign = '+'; - signwidth = 1; - } else if (flags & FLAG_SPACED) { - sign = ' '; - signwidth = 1; - } - } - cvtwidth = signwidth + srclen; - - if (prec > 0) { - if (prec > srclen) { - precwidth = prec - srclen; /* Need zero filling */ - cvtwidth += precwidth; - } - } - - if ((flags & FLAG_ZEROS) && (prec < 0)) { - if (width > cvtwidth) { - zerowidth = width - cvtwidth; /* Zero filling */ - cvtwidth += zerowidth; - } - } - - if (flags & FLAG_LEFT) { - if (width > cvtwidth) { - /* Space filling on the right (i.e. left adjusting) */ - rightspaces = width - cvtwidth; - } - } else { - if (width > cvtwidth) { - /* Space filling on the left (i.e. right adjusting) */ - leftspaces = width - cvtwidth; - } - } - while (--leftspaces >= 0) { - rv = (*ss->stuff)(ss, " ", 1); - if (rv < 0) { - return rv; - } - } - if (signwidth) { - rv = (*ss->stuff)(ss, &sign, 1); - if (rv < 0) { - return rv; - } - } - while (--precwidth >= 0) { - rv = (*ss->stuff)(ss, "0", 1); - if (rv < 0) { - return rv; - } - } - while (--zerowidth >= 0) { - rv = (*ss->stuff)(ss, "0", 1); - if (rv < 0) { - return rv; - } - } - rv = (*ss->stuff)(ss, src, (JSUint32)srclen); - if (rv < 0) { - return rv; - } - while (--rightspaces >= 0) { - rv = (*ss->stuff)(ss, " ", 1); - if (rv < 0) { - return rv; - } - } - return 0; -} - -/* -** Convert a long into its printable form -*/ -static int cvt_l(SprintfState *ss, long num, int width, int prec, int radix, - int type, int flags, const char *hexp) -{ - char cvtbuf[100]; - char *cvt; - int digits; - - /* according to the man page this needs to happen */ - if ((prec == 0) && (num == 0)) { - return 0; - } - - /* - ** Converting decimal is a little tricky. In the unsigned case we - ** need to stop when we hit 10 digits. In the signed case, we can - ** stop when the number is zero. - */ - cvt = cvtbuf + sizeof(cvtbuf); - digits = 0; - while (num) { - int digit = (((unsigned long)num) % radix) & 0xF; - *--cvt = hexp[digit]; - digits++; - num = (long)(((unsigned long)num) / radix); - } - if (digits == 0) { - *--cvt = '0'; - digits++; - } - - /* - ** Now that we have the number converted without its sign, deal with - ** the sign and zero padding. - */ - return fill_n(ss, cvt, digits, width, prec, type, flags); -} - -/* -** Convert a 64-bit integer into its printable form -*/ -static int cvt_ll(SprintfState *ss, JSInt64 num, int width, int prec, int radix, - int type, int flags, const char *hexp) -{ - char cvtbuf[100]; - char *cvt; - int digits; - JSInt64 rad; - - /* according to the man page this needs to happen */ - if ((prec == 0) && (JSLL_IS_ZERO(num))) { - return 0; - } - - /* - ** Converting decimal is a little tricky. In the unsigned case we - ** need to stop when we hit 10 digits. In the signed case, we can - ** stop when the number is zero. - */ - JSLL_I2L(rad, radix); - cvt = cvtbuf + sizeof(cvtbuf); - digits = 0; - while (!JSLL_IS_ZERO(num)) { - JSInt32 digit; - JSInt64 quot, rem; - JSLL_UDIVMOD(", &rem, num, rad); - JSLL_L2I(digit, rem); - *--cvt = hexp[digit & 0xf]; - digits++; - num = quot; - } - if (digits == 0) { - *--cvt = '0'; - digits++; - } - - /* - ** Now that we have the number converted without its sign, deal with - ** the sign and zero padding. - */ - return fill_n(ss, cvt, digits, width, prec, type, flags); -} - -/* -** Convert a double precision floating point number into its printable -** form. -** -** XXX stop using sprintf to convert floating point -*/ -static int cvt_f(SprintfState *ss, double d, const char *fmt0, const char *fmt1) -{ - char fin[20]; - char fout[300]; - int amount = fmt1 - fmt0; - - JS_ASSERT((amount > 0) && (amount < (int)sizeof(fin))); - if (amount >= (int)sizeof(fin)) { - /* Totally bogus % command to sprintf. Just ignore it */ - return 0; - } - memcpy(fin, fmt0, (size_t)amount); - fin[amount] = 0; - - /* Convert floating point using the native sprintf code */ -#ifdef DEBUG - { - const char *p = fin; - while (*p) { - JS_ASSERT(*p != 'L'); - p++; - } - } -#endif - sprintf(fout, fin, d); - - /* - ** This assert will catch overflow's of fout, when building with - ** debugging on. At least this way we can track down the evil piece - ** of calling code and fix it! - */ - JS_ASSERT(strlen(fout) < sizeof(fout)); - - return (*ss->stuff)(ss, fout, strlen(fout)); -} - -/* -** Convert a string into its printable form. "width" is the output -** width. "prec" is the maximum number of characters of "s" to output, -** where -1 means until NUL. -*/ -static int cvt_s(SprintfState *ss, const char *s, int width, int prec, - int flags) -{ - int slen; - - if (prec == 0) - return 0; - - /* Limit string length by precision value */ - slen = s ? strlen(s) : 6; - if (prec > 0) { - if (prec < slen) { - slen = prec; - } - } - - /* and away we go */ - return fill2(ss, s ? s : "(null)", slen, width, flags); -} - -static int cvt_ws(SprintfState *ss, const jschar *ws, int width, int prec, - int flags) -{ - int result; - /* - * Supply NULL as the JSContext; errors are not reported, - * and malloc() is used to allocate the buffer buffer. - */ - if (ws) { - int slen = js_strlen(ws); - char *s = js_DeflateString(NULL, ws, slen); - if (!s) - return -1; /* JSStuffFunc error indicator. */ - result = cvt_s(ss, s, width, prec, flags); - free(s); - } else { - result = cvt_s(ss, NULL, width, prec, flags); - } - return result; -} - -/* -** BuildArgArray stands for Numbered Argument list Sprintf -** for example, -** fmp = "%4$i, %2$d, %3s, %1d"; -** the number must start from 1, and no gap among them -*/ - -static struct NumArgState* BuildArgArray( const char *fmt, va_list ap, int* rv, struct NumArgState* nasArray ) -{ - int number = 0, cn = 0, i; - const char *p; - char c; - struct NumArgState *nas; - - - /* - ** first pass: - ** detemine how many legal % I have got, then allocate space - */ - - p = fmt; - *rv = 0; - i = 0; - while( ( c = *p++ ) != 0 ){ - if( c != '%' ) - continue; - if( ( c = *p++ ) == '%' ) /* skip %% case */ - continue; - - while( c != 0 ){ - if( c > '9' || c < '0' ){ - if( c == '$' ){ /* numbered argument csae */ - if( i > 0 ){ - *rv = -1; - return NULL; - } - number++; - } else { /* non-numbered argument case */ - if( number > 0 ){ - *rv = -1; - return NULL; - } - i = 1; - } - break; - } - - c = *p++; - } - } - - if( number == 0 ){ - return NULL; - } - - - if( number > NAS_DEFAULT_NUM ){ - nas = (struct NumArgState*)malloc( number * sizeof( struct NumArgState ) ); - if( !nas ){ - *rv = -1; - return NULL; - } - } else { - nas = nasArray; - } - - for( i = 0; i < number; i++ ){ - nas[i].type = TYPE_UNKNOWN; - } - - - /* - ** second pass: - ** set nas[].type - */ - - p = fmt; - while( ( c = *p++ ) != 0 ){ - if( c != '%' ) continue; - c = *p++; - if( c == '%' ) continue; - - cn = 0; - while( c && c != '$' ){ /* should improve error check later */ - cn = cn*10 + c - '0'; - c = *p++; - } - - if( !c || cn < 1 || cn > number ){ - *rv = -1; - break; - } - - /* nas[cn] starts from 0, and make sure nas[cn].type is not assigned */ - cn--; - if( nas[cn].type != TYPE_UNKNOWN ) - continue; - - c = *p++; - - /* width */ - if (c == '*') { - /* not supported feature, for the argument is not numbered */ - *rv = -1; - break; - } - - while ((c >= '0') && (c <= '9')) { - c = *p++; - } - - /* precision */ - if (c == '.') { - c = *p++; - if (c == '*') { - /* not supported feature, for the argument is not numbered */ - *rv = -1; - break; - } - - while ((c >= '0') && (c <= '9')) { - c = *p++; - } - } - - /* size */ - nas[cn].type = TYPE_INTN; - if (c == 'h') { - nas[cn].type = TYPE_INT16; - c = *p++; - } else if (c == 'L') { - /* XXX not quite sure here */ - nas[cn].type = TYPE_INT64; - c = *p++; - } else if (c == 'l') { - nas[cn].type = TYPE_INT32; - c = *p++; - if (c == 'l') { - nas[cn].type = TYPE_INT64; - c = *p++; - } - } - - /* format */ - switch (c) { - case 'd': - case 'c': - case 'i': - case 'o': - case 'u': - case 'x': - case 'X': - break; - - case 'e': - case 'f': - case 'g': - nas[ cn ].type = TYPE_DOUBLE; - break; - - case 'p': - /* XXX should use cpp */ - if (sizeof(void *) == sizeof(JSInt32)) { - nas[ cn ].type = TYPE_UINT32; - } else if (sizeof(void *) == sizeof(JSInt64)) { - nas[ cn ].type = TYPE_UINT64; - } else if (sizeof(void *) == sizeof(JSIntn)) { - nas[ cn ].type = TYPE_UINTN; - } else { - nas[ cn ].type = TYPE_UNKNOWN; - } - break; - - case 'C': - case 'S': - case 'E': - case 'G': - /* XXX not supported I suppose */ - JS_ASSERT(0); - nas[ cn ].type = TYPE_UNKNOWN; - break; - - case 's': - nas[ cn ].type = (nas[ cn ].type == TYPE_UINT16) ? TYPE_WSTRING : TYPE_STRING; - break; - - case 'n': - nas[ cn ].type = TYPE_INTSTR; - break; - - default: - JS_ASSERT(0); - nas[ cn ].type = TYPE_UNKNOWN; - break; - } - - /* get a legal para. */ - if( nas[ cn ].type == TYPE_UNKNOWN ){ - *rv = -1; - break; - } - } - - - /* - ** third pass - ** fill the nas[cn].ap - */ - - if( *rv < 0 ){ - if( nas != nasArray ) - free( nas ); - return NULL; - } - - cn = 0; - while( cn < number ){ - if( nas[cn].type == TYPE_UNKNOWN ){ - cn++; - continue; - } - - VARARGS_ASSIGN(nas[cn].ap, ap); - - switch( nas[cn].type ){ - case TYPE_INT16: - case TYPE_UINT16: - case TYPE_INTN: - case TYPE_UINTN: (void)va_arg( ap, JSIntn ); break; - - case TYPE_INT32: (void)va_arg( ap, JSInt32 ); break; - - case TYPE_UINT32: (void)va_arg( ap, JSUint32 ); break; - - case TYPE_INT64: (void)va_arg( ap, JSInt64 ); break; - - case TYPE_UINT64: (void)va_arg( ap, JSUint64 ); break; - - case TYPE_STRING: (void)va_arg( ap, char* ); break; - - case TYPE_WSTRING: (void)va_arg( ap, jschar* ); break; - - case TYPE_INTSTR: (void)va_arg( ap, JSIntn* ); break; - - case TYPE_DOUBLE: (void)va_arg( ap, double ); break; - - default: - if( nas != nasArray ) - free( nas ); - *rv = -1; - return NULL; - } - - cn++; - } - - - return nas; -} - -/* -** The workhorse sprintf code. -*/ -static int dosprintf(SprintfState *ss, const char *fmt, va_list ap) -{ - char c; - int flags, width, prec, radix, type; - union { - char ch; - jschar wch; - int i; - long l; - JSInt64 ll; - double d; - const char *s; - const jschar* ws; - int *ip; - } u; - const char *fmt0; - static char *hex = "0123456789abcdef"; - static char *HEX = "0123456789ABCDEF"; - char *hexp; - int rv, i; - struct NumArgState *nas = NULL; - struct NumArgState nasArray[ NAS_DEFAULT_NUM ]; - char pattern[20]; - const char *dolPt = NULL; /* in "%4$.2f", dolPt will poiont to . */ -#ifdef JS_C_STRINGS_ARE_UTF8 - char utf8buf[6]; - int utf8len; -#endif - - /* - ** build an argument array, IF the fmt is numbered argument - ** list style, to contain the Numbered Argument list pointers - */ - - nas = BuildArgArray( fmt, ap, &rv, nasArray ); - if( rv < 0 ){ - /* the fmt contains error Numbered Argument format, jliu@netscape.com */ - JS_ASSERT(0); - return rv; - } - - while ((c = *fmt++) != 0) { - if (c != '%') { - rv = (*ss->stuff)(ss, fmt - 1, 1); - if (rv < 0) { - return rv; - } - continue; - } - fmt0 = fmt - 1; - - /* - ** Gobble up the % format string. Hopefully we have handled all - ** of the strange cases! - */ - flags = 0; - c = *fmt++; - if (c == '%') { - /* quoting a % with %% */ - rv = (*ss->stuff)(ss, fmt - 1, 1); - if (rv < 0) { - return rv; - } - continue; - } - - if( nas != NULL ){ - /* the fmt contains the Numbered Arguments feature */ - i = 0; - while( c && c != '$' ){ /* should imporve error check later */ - i = ( i * 10 ) + ( c - '0' ); - c = *fmt++; - } - - if( nas[i-1].type == TYPE_UNKNOWN ){ - if( nas && ( nas != nasArray ) ) - free( nas ); - return -1; - } - - ap = nas[i-1].ap; - dolPt = fmt; - c = *fmt++; - } - - /* - * Examine optional flags. Note that we do not implement the - * '#' flag of sprintf(). The ANSI C spec. of the '#' flag is - * somewhat ambiguous and not ideal, which is perhaps why - * the various sprintf() implementations are inconsistent - * on this feature. - */ - while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) { - if (c == '-') flags |= FLAG_LEFT; - if (c == '+') flags |= FLAG_SIGNED; - if (c == ' ') flags |= FLAG_SPACED; - if (c == '0') flags |= FLAG_ZEROS; - c = *fmt++; - } - if (flags & FLAG_SIGNED) flags &= ~FLAG_SPACED; - if (flags & FLAG_LEFT) flags &= ~FLAG_ZEROS; - - /* width */ - if (c == '*') { - c = *fmt++; - width = va_arg(ap, int); - } else { - width = 0; - while ((c >= '0') && (c <= '9')) { - width = (width * 10) + (c - '0'); - c = *fmt++; - } - } - - /* precision */ - prec = -1; - if (c == '.') { - c = *fmt++; - if (c == '*') { - c = *fmt++; - prec = va_arg(ap, int); - } else { - prec = 0; - while ((c >= '0') && (c <= '9')) { - prec = (prec * 10) + (c - '0'); - c = *fmt++; - } - } - } - - /* size */ - type = TYPE_INTN; - if (c == 'h') { - type = TYPE_INT16; - c = *fmt++; - } else if (c == 'L') { - /* XXX not quite sure here */ - type = TYPE_INT64; - c = *fmt++; - } else if (c == 'l') { - type = TYPE_INT32; - c = *fmt++; - if (c == 'l') { - type = TYPE_INT64; - c = *fmt++; - } - } - - /* format */ - hexp = hex; - switch (c) { - case 'd': case 'i': /* decimal/integer */ - radix = 10; - goto fetch_and_convert; - - case 'o': /* octal */ - radix = 8; - type |= 1; - goto fetch_and_convert; - - case 'u': /* unsigned decimal */ - radix = 10; - type |= 1; - goto fetch_and_convert; - - case 'x': /* unsigned hex */ - radix = 16; - type |= 1; - goto fetch_and_convert; - - case 'X': /* unsigned HEX */ - radix = 16; - hexp = HEX; - type |= 1; - goto fetch_and_convert; - - fetch_and_convert: - switch (type) { - case TYPE_INT16: - u.l = va_arg(ap, int); - if (u.l < 0) { - u.l = -u.l; - flags |= FLAG_NEG; - } - goto do_long; - case TYPE_UINT16: - u.l = va_arg(ap, int) & 0xffff; - goto do_long; - case TYPE_INTN: - u.l = va_arg(ap, int); - if (u.l < 0) { - u.l = -u.l; - flags |= FLAG_NEG; - } - goto do_long; - case TYPE_UINTN: - u.l = (long)va_arg(ap, unsigned int); - goto do_long; - - case TYPE_INT32: - u.l = va_arg(ap, JSInt32); - if (u.l < 0) { - u.l = -u.l; - flags |= FLAG_NEG; - } - goto do_long; - case TYPE_UINT32: - u.l = (long)va_arg(ap, JSUint32); - do_long: - rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp); - if (rv < 0) { - return rv; - } - break; - - case TYPE_INT64: - u.ll = va_arg(ap, JSInt64); - if (!JSLL_GE_ZERO(u.ll)) { - JSLL_NEG(u.ll, u.ll); - flags |= FLAG_NEG; - } - goto do_longlong; - case TYPE_UINT64: - u.ll = va_arg(ap, JSUint64); - do_longlong: - rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp); - if (rv < 0) { - return rv; - } - break; - } - break; - - case 'e': - case 'E': - case 'f': - case 'g': - u.d = va_arg(ap, double); - if( nas != NULL ){ - i = fmt - dolPt; - if( i < (int)sizeof( pattern ) ){ - pattern[0] = '%'; - memcpy( &pattern[1], dolPt, (size_t)i ); - rv = cvt_f(ss, u.d, pattern, &pattern[i+1] ); - } - } else - rv = cvt_f(ss, u.d, fmt0, fmt); - - if (rv < 0) { - return rv; - } - break; - - case 'c': - if ((flags & FLAG_LEFT) == 0) { - while (width-- > 1) { - rv = (*ss->stuff)(ss, " ", 1); - if (rv < 0) { - return rv; - } - } - } - switch (type) { - case TYPE_INT16: - /* Treat %hc as %c if JS_C_STRINGS_ARE_UTF8 is undefined. */ -#ifdef JS_C_STRINGS_ARE_UTF8 - u.wch = va_arg(ap, int); - utf8len = js_OneUcs4ToUtf8Char (utf8buf, u.wch); - rv = (*ss->stuff)(ss, utf8buf, utf8len); - break; -#endif - case TYPE_INTN: - u.ch = va_arg(ap, int); - rv = (*ss->stuff)(ss, &u.ch, 1); - break; - } - if (rv < 0) { - return rv; - } - if (flags & FLAG_LEFT) { - while (width-- > 1) { - rv = (*ss->stuff)(ss, " ", 1); - if (rv < 0) { - return rv; - } - } - } - break; - - case 'p': - if (sizeof(void *) == sizeof(JSInt32)) { - type = TYPE_UINT32; - } else if (sizeof(void *) == sizeof(JSInt64)) { - type = TYPE_UINT64; - } else if (sizeof(void *) == sizeof(int)) { - type = TYPE_UINTN; - } else { - JS_ASSERT(0); - break; - } - radix = 16; - goto fetch_and_convert; - -#if 0 - case 'C': - case 'S': - case 'E': - case 'G': - /* XXX not supported I suppose */ - JS_ASSERT(0); - break; -#endif - - case 's': - if(type == TYPE_INT16) { - /* - * This would do a simple string/byte conversion - * if JS_C_STRINGS_ARE_UTF8 is not defined. - */ - u.ws = va_arg(ap, const jschar*); - rv = cvt_ws(ss, u.ws, width, prec, flags); - } else { - u.s = va_arg(ap, const char*); - rv = cvt_s(ss, u.s, width, prec, flags); - } - if (rv < 0) { - return rv; - } - break; - - case 'n': - u.ip = va_arg(ap, int*); - if (u.ip) { - *u.ip = ss->cur - ss->base; - } - break; - - default: - /* Not a % token after all... skip it */ -#if 0 - JS_ASSERT(0); -#endif - rv = (*ss->stuff)(ss, "%", 1); - if (rv < 0) { - return rv; - } - rv = (*ss->stuff)(ss, fmt - 1, 1); - if (rv < 0) { - return rv; - } - } - } - - /* Stuff trailing NUL */ - rv = (*ss->stuff)(ss, "\0", 1); - - if( nas && ( nas != nasArray ) ){ - free( nas ); - } - - return rv; -} - -/************************************************************************/ - -static int FuncStuff(SprintfState *ss, const char *sp, JSUint32 len) -{ - int rv; - - rv = (*ss->func)(ss->arg, sp, len); - if (rv < 0) { - return rv; - } - ss->maxlen += len; - return 0; -} - -JS_PUBLIC_API(JSUint32) JS_sxprintf(JSStuffFunc func, void *arg, - const char *fmt, ...) -{ - va_list ap; - int rv; - - va_start(ap, fmt); - rv = JS_vsxprintf(func, arg, fmt, ap); - va_end(ap); - return rv; -} - -JS_PUBLIC_API(JSUint32) JS_vsxprintf(JSStuffFunc func, void *arg, - const char *fmt, va_list ap) -{ - SprintfState ss; - int rv; - - ss.stuff = FuncStuff; - ss.func = func; - ss.arg = arg; - ss.maxlen = 0; - rv = dosprintf(&ss, fmt, ap); - return (rv < 0) ? (JSUint32)-1 : ss.maxlen; -} - -/* -** Stuff routine that automatically grows the malloc'd output buffer -** before it overflows. -*/ -static int GrowStuff(SprintfState *ss, const char *sp, JSUint32 len) -{ - ptrdiff_t off; - char *newbase; - JSUint32 newlen; - - off = ss->cur - ss->base; - if (off + len >= ss->maxlen) { - /* Grow the buffer */ - newlen = ss->maxlen + ((len > 32) ? len : 32); - if (ss->base) { - newbase = (char*) realloc(ss->base, newlen); - } else { - newbase = (char*) malloc(newlen); - } - if (!newbase) { - /* Ran out of memory */ - return -1; - } - ss->base = newbase; - ss->maxlen = newlen; - ss->cur = ss->base + off; - } - - /* Copy data */ - while (len) { - --len; - *ss->cur++ = *sp++; - } - JS_ASSERT((JSUint32)(ss->cur - ss->base) <= ss->maxlen); - return 0; -} - -/* -** sprintf into a malloc'd buffer -*/ -JS_PUBLIC_API(char *) JS_smprintf(const char *fmt, ...) -{ - va_list ap; - char *rv; - - va_start(ap, fmt); - rv = JS_vsmprintf(fmt, ap); - va_end(ap); - return rv; -} - -/* -** Free memory allocated, for the caller, by JS_smprintf -*/ -JS_PUBLIC_API(void) JS_smprintf_free(char *mem) -{ - free(mem); -} - -JS_PUBLIC_API(char *) JS_vsmprintf(const char *fmt, va_list ap) -{ - SprintfState ss; - int rv; - - ss.stuff = GrowStuff; - ss.base = 0; - ss.cur = 0; - ss.maxlen = 0; - rv = dosprintf(&ss, fmt, ap); - if (rv < 0) { - if (ss.base) { - free(ss.base); - } - return 0; - } - return ss.base; -} - -/* -** Stuff routine that discards overflow data -*/ -static int LimitStuff(SprintfState *ss, const char *sp, JSUint32 len) -{ - JSUint32 limit = ss->maxlen - (ss->cur - ss->base); - - if (len > limit) { - len = limit; - } - while (len) { - --len; - *ss->cur++ = *sp++; - } - return 0; -} - -/* -** sprintf into a fixed size buffer. Make sure there is a NUL at the end -** when finished. -*/ -JS_PUBLIC_API(JSUint32) JS_snprintf(char *out, JSUint32 outlen, const char *fmt, ...) -{ - va_list ap; - int rv; - - JS_ASSERT((JSInt32)outlen > 0); - if ((JSInt32)outlen <= 0) { - return 0; - } - - va_start(ap, fmt); - rv = JS_vsnprintf(out, outlen, fmt, ap); - va_end(ap); - return rv; -} - -JS_PUBLIC_API(JSUint32) JS_vsnprintf(char *out, JSUint32 outlen,const char *fmt, - va_list ap) -{ - SprintfState ss; - JSUint32 n; - - JS_ASSERT((JSInt32)outlen > 0); - if ((JSInt32)outlen <= 0) { - return 0; - } - - ss.stuff = LimitStuff; - ss.base = out; - ss.cur = out; - ss.maxlen = outlen; - (void) dosprintf(&ss, fmt, ap); - - /* If we added chars, and we didn't append a null, do it now. */ - if( (ss.cur != ss.base) && (ss.cur[-1] != '\0') ) - ss.cur[-1] = '\0'; - - n = ss.cur - ss.base; - return n ? n - 1 : n; -} - -JS_PUBLIC_API(char *) JS_sprintf_append(char *last, const char *fmt, ...) -{ - va_list ap; - char *rv; - - va_start(ap, fmt); - rv = JS_vsprintf_append(last, fmt, ap); - va_end(ap); - return rv; -} - -JS_PUBLIC_API(char *) JS_vsprintf_append(char *last, const char *fmt, va_list ap) -{ - SprintfState ss; - int rv; - - ss.stuff = GrowStuff; - if (last) { - int lastlen = strlen(last); - ss.base = last; - ss.cur = last + lastlen; - ss.maxlen = lastlen; - } else { - ss.base = 0; - ss.cur = 0; - ss.maxlen = 0; - } - rv = dosprintf(&ss, fmt, ap); - if (rv < 0) { - if (ss.base) { - free(ss.base); - } - return 0; - } - return ss.base; -} - diff --git a/spidermonkey/src/jsprf.h b/spidermonkey/src/jsprf.h deleted file mode 100644 index 0eb910f..0000000 --- a/spidermonkey/src/jsprf.h +++ /dev/null @@ -1,150 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsprf_h___ -#define jsprf_h___ - -/* -** API for PR printf like routines. Supports the following formats -** %d - decimal -** %u - unsigned decimal -** %x - unsigned hex -** %X - unsigned uppercase hex -** %o - unsigned octal -** %hd, %hu, %hx, %hX, %ho - 16-bit versions of above -** %ld, %lu, %lx, %lX, %lo - 32-bit versions of above -** %lld, %llu, %llx, %llX, %llo - 64 bit versions of above -** %s - string -** %hs - 16-bit version of above (only available if compiled with JS_C_STRINGS_ARE_UTF8) -** %c - character -** %hc - 16-bit version of above (only available if compiled with JS_C_STRINGS_ARE_UTF8) -** %p - pointer (deals with machine dependent pointer size) -** %f - float -** %g - float -*/ -#include "jstypes.h" -#include -#include - -JS_BEGIN_EXTERN_C - -/* -** sprintf into a fixed size buffer. Guarantees that a NUL is at the end -** of the buffer. Returns the length of the written output, NOT including -** the NUL, or (JSUint32)-1 if an error occurs. -*/ -extern JS_PUBLIC_API(JSUint32) JS_snprintf(char *out, JSUint32 outlen, const char *fmt, ...); - -/* -** sprintf into a malloc'd buffer. Return a pointer to the malloc'd -** buffer on success, NULL on failure. Call "JS_smprintf_free" to release -** the memory returned. -*/ -extern JS_PUBLIC_API(char*) JS_smprintf(const char *fmt, ...); - -/* -** Free the memory allocated, for the caller, by JS_smprintf -*/ -extern JS_PUBLIC_API(void) JS_smprintf_free(char *mem); - -/* -** "append" sprintf into a malloc'd buffer. "last" is the last value of -** the malloc'd buffer. sprintf will append data to the end of last, -** growing it as necessary using realloc. If last is NULL, JS_sprintf_append -** will allocate the initial string. The return value is the new value of -** last for subsequent calls, or NULL if there is a malloc failure. -*/ -extern JS_PUBLIC_API(char*) JS_sprintf_append(char *last, const char *fmt, ...); - -/* -** sprintf into a function. The function "f" is called with a string to -** place into the output. "arg" is an opaque pointer used by the stuff -** function to hold any state needed to do the storage of the output -** data. The return value is a count of the number of characters fed to -** the stuff function, or (JSUint32)-1 if an error occurs. -*/ -typedef JSIntn (*JSStuffFunc)(void *arg, const char *s, JSUint32 slen); - -extern JS_PUBLIC_API(JSUint32) JS_sxprintf(JSStuffFunc f, void *arg, const char *fmt, ...); - -/* -** va_list forms of the above. -*/ -extern JS_PUBLIC_API(JSUint32) JS_vsnprintf(char *out, JSUint32 outlen, const char *fmt, va_list ap); -extern JS_PUBLIC_API(char*) JS_vsmprintf(const char *fmt, va_list ap); -extern JS_PUBLIC_API(char*) JS_vsprintf_append(char *last, const char *fmt, va_list ap); -extern JS_PUBLIC_API(JSUint32) JS_vsxprintf(JSStuffFunc f, void *arg, const char *fmt, va_list ap); - -/* -*************************************************************************** -** FUNCTION: JS_sscanf -** DESCRIPTION: -** JS_sscanf() scans the input character string, performs data -** conversions, and stores the converted values in the data objects -** pointed to by its arguments according to the format control -** string. -** -** JS_sscanf() behaves the same way as the sscanf() function in the -** Standard C Library (stdio.h), with the following exceptions: -** - JS_sscanf() handles the NSPR integer and floating point types, -** such as JSInt16, JSInt32, JSInt64, and JSFloat64, whereas -** sscanf() handles the standard C types like short, int, long, -** and double. -** - JS_sscanf() has no multibyte character support, while sscanf() -** does. -** INPUTS: -** const char *buf -** a character string holding the input to scan -** const char *fmt -** the format control string for the conversions -** ... -** variable number of arguments, each of them is a pointer to -** a data object in which the converted value will be stored -** OUTPUTS: none -** RETURNS: JSInt32 -** The number of values converted and stored. -** RESTRICTIONS: -** Multibyte characters in 'buf' or 'fmt' are not allowed. -*************************************************************************** -*/ - -extern JS_PUBLIC_API(JSInt32) JS_sscanf(const char *buf, const char *fmt, ...); - -JS_END_EXTERN_C - -#endif /* jsprf_h___ */ diff --git a/spidermonkey/src/jsproto.tbl b/spidermonkey/src/jsproto.tbl deleted file mode 100644 index 18f2355..0000000 --- a/spidermonkey/src/jsproto.tbl +++ /dev/null @@ -1,116 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set sw=4 ts=8 et tw=80 ft=c: - * - * ***** 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 SpiderMonkey 1.7 work in progress, released - * February 14, 2006. - * - * The Initial Developer of the Original Code is - * Brendan Eich - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of 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 "jsconfig.h" - -#if JS_HAS_SCRIPT_OBJECT -# define SCRIPT_INIT js_InitScriptClass -#else -# define SCRIPT_INIT js_InitNullClass -#endif - -#if JS_HAS_XML_SUPPORT -# define XML_INIT js_InitXMLClass -# define NAMESPACE_INIT js_InitNamespaceClass -# define QNAME_INIT js_InitQNameClass -# define ANYNAME_INIT js_InitAnyNameClass -# define ATTRIBUTE_INIT js_InitAttributeNameClass -#else -# define XML_INIT js_InitNullClass -# define NAMESPACE_INIT js_InitNullClass -# define QNAME_INIT js_InitNullClass -# define ANYNAME_INIT js_InitNullClass -# define ATTRIBUTE_INIT js_InitNullClass -#endif - -#if JS_HAS_GENERATORS -# define GENERATOR_INIT js_InitIteratorClasses -#else -# define GENERATOR_INIT js_InitNullClass -#endif - -#if JS_HAS_FILE_OBJECT -# define FILE_INIT js_InitFileClass -#else -# define FILE_INIT js_InitNullClass -#endif - -/* - * Enumerator codes in the second column must not change -- they are part of - * the JS XDR API. - */ -JS_PROTO(Null, 0, js_InitNullClass) -JS_PROTO(Object, 1, js_InitFunctionAndObjectClasses) -JS_PROTO(Function, 2, js_InitFunctionAndObjectClasses) -JS_PROTO(Array, 3, js_InitArrayClass) -JS_PROTO(Boolean, 4, js_InitBooleanClass) -JS_PROTO(Call, 5, js_InitCallClass) -JS_PROTO(Date, 6, js_InitDateClass) -JS_PROTO(Math, 7, js_InitMathClass) -JS_PROTO(Number, 8, js_InitNumberClass) -JS_PROTO(String, 9, js_InitStringClass) -JS_PROTO(RegExp, 10, js_InitRegExpClass) -JS_PROTO(Script, 11, SCRIPT_INIT) -JS_PROTO(XML, 12, XML_INIT) -JS_PROTO(Namespace, 13, NAMESPACE_INIT) -JS_PROTO(QName, 14, QNAME_INIT) -JS_PROTO(AnyName, 15, ANYNAME_INIT) -JS_PROTO(AttributeName, 16, ATTRIBUTE_INIT) -JS_PROTO(Error, 17, js_InitExceptionClasses) -JS_PROTO(InternalError, 18, js_InitExceptionClasses) -JS_PROTO(EvalError, 19, js_InitExceptionClasses) -JS_PROTO(RangeError, 20, js_InitExceptionClasses) -JS_PROTO(ReferenceError, 21, js_InitExceptionClasses) -JS_PROTO(SyntaxError, 22, js_InitExceptionClasses) -JS_PROTO(TypeError, 23, js_InitExceptionClasses) -JS_PROTO(URIError, 24, js_InitExceptionClasses) -JS_PROTO(Generator, 25, GENERATOR_INIT) -JS_PROTO(Iterator, 26, js_InitIteratorClasses) -JS_PROTO(StopIteration, 27, js_InitIteratorClasses) -JS_PROTO(UnusedProto28, 28, js_InitNullClass) -JS_PROTO(File, 29, FILE_INIT) -JS_PROTO(Block, 30, js_InitBlockClass) - -#undef SCRIPT_INIT -#undef XML_INIT -#undef NAMESPACE_INIT -#undef QNAME_INIT -#undef ANYNAME_INIT -#undef ATTRIBUTE_INIT -#undef GENERATOR_INIT -#undef FILE_INIT diff --git a/spidermonkey/src/jsprvtd.h b/spidermonkey/src/jsprvtd.h deleted file mode 100644 index f71b9a5..0000000 --- a/spidermonkey/src/jsprvtd.h +++ /dev/null @@ -1,202 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsprvtd_h___ -#define jsprvtd_h___ -/* - * JS private typename definitions. - * - * This header is included only in other .h files, for convenience and for - * simplicity of type naming. The alternative for structures is to use tags, - * which are named the same as their typedef names (legal in C/C++, and less - * noisy than suffixing the typedef name with "Struct" or "Str"). Instead, - * all .h files that include this file may use the same typedef name, whether - * declaring a pointer to struct type, or defining a member of struct type. - * - * A few fundamental scalar types are defined here too. Neither the scalar - * nor the struct typedefs should change much, therefore the nearly-global - * make dependency induced by this file should not prove painful. - */ - -#include "jspubtd.h" - -/* Internal identifier (jsid) macros. */ -#define JSID_ATOM 0x0 -#define JSID_INT 0x1 -#define JSID_OBJECT 0x2 -#define JSID_TAGMASK 0x3 -#define JSID_TAG(id) ((id) & JSID_TAGMASK) -#define JSID_SETTAG(id,t) ((id) | (t)) -#define JSID_CLRTAG(id) ((id) & ~(jsid)JSID_TAGMASK) - -#define JSID_IS_ATOM(id) (JSID_TAG(id) == JSID_ATOM) -#define JSID_TO_ATOM(id) ((JSAtom *)(id)) -#define ATOM_TO_JSID(atom) ((jsid)(atom)) -#define ATOM_JSID_TO_JSVAL(id) ATOM_KEY(JSID_TO_ATOM(id)) - -#define JSID_IS_INT(id) ((id) & JSID_INT) -#define JSID_TO_INT(id) ((jsint)(id) >> 1) -#define INT_TO_JSID(i) (((jsint)(i) << 1) | JSID_INT) -#define INT_JSID_TO_JSVAL(id) (id) -#define INT_JSVAL_TO_JSID(v) (v) - -#define JSID_IS_OBJECT(id) (JSID_TAG(id) == JSID_OBJECT) -#define JSID_TO_OBJECT(id) ((JSObject *) JSID_CLRTAG(id)) -#define OBJECT_TO_JSID(obj) ((jsid)(obj) | JSID_OBJECT) -#define OBJECT_JSID_TO_JSVAL(id) OBJECT_TO_JSVAL(JSID_CLRTAG(id)) -#define OBJECT_JSVAL_TO_JSID(v) OBJECT_TO_JSID(JSVAL_TO_OBJECT(v)) - -/* Scalar typedefs. */ -typedef uint8 jsbytecode; -typedef uint8 jssrcnote; -typedef uint32 jsatomid; - -/* Struct typedefs. */ -typedef struct JSArgumentFormatMap JSArgumentFormatMap; -typedef struct JSCodeGenerator JSCodeGenerator; -typedef struct JSDependentString JSDependentString; -typedef struct JSGCThing JSGCThing; -typedef struct JSGenerator JSGenerator; -typedef struct JSParseNode JSParseNode; -typedef struct JSSharpObjectMap JSSharpObjectMap; -typedef struct JSThread JSThread; -typedef struct JSToken JSToken; -typedef struct JSTokenPos JSTokenPos; -typedef struct JSTokenPtr JSTokenPtr; -typedef struct JSTokenStream JSTokenStream; -typedef struct JSTreeContext JSTreeContext; -typedef struct JSTryNote JSTryNote; - -/* Friend "Advanced API" typedefs. */ -typedef struct JSAtom JSAtom; -typedef struct JSAtomList JSAtomList; -typedef struct JSAtomListElement JSAtomListElement; -typedef struct JSAtomMap JSAtomMap; -typedef struct JSAtomState JSAtomState; -typedef struct JSCodeSpec JSCodeSpec; -typedef struct JSPrinter JSPrinter; -typedef struct JSRegExp JSRegExp; -typedef struct JSRegExpStatics JSRegExpStatics; -typedef struct JSScope JSScope; -typedef struct JSScopeOps JSScopeOps; -typedef struct JSScopeProperty JSScopeProperty; -typedef struct JSStackHeader JSStackHeader; -typedef struct JSStringBuffer JSStringBuffer; -typedef struct JSSubString JSSubString; -typedef struct JSXML JSXML; -typedef struct JSXMLNamespace JSXMLNamespace; -typedef struct JSXMLQName JSXMLQName; -typedef struct JSXMLArray JSXMLArray; -typedef struct JSXMLArrayCursor JSXMLArrayCursor; - -/* "Friend" types used by jscntxt.h and jsdbgapi.h. */ -typedef enum JSTrapStatus { - JSTRAP_ERROR, - JSTRAP_CONTINUE, - JSTRAP_RETURN, - JSTRAP_THROW, - JSTRAP_LIMIT -} JSTrapStatus; - -typedef JSTrapStatus -(* JS_DLL_CALLBACK JSTrapHandler)(JSContext *cx, JSScript *script, - jsbytecode *pc, jsval *rval, void *closure); - -typedef JSBool -(* JS_DLL_CALLBACK JSWatchPointHandler)(JSContext *cx, JSObject *obj, jsval id, - jsval old, jsval *newp, void *closure); - -/* called just after script creation */ -typedef void -(* JS_DLL_CALLBACK JSNewScriptHook)(JSContext *cx, - const char *filename, /* URL of script */ - uintN lineno, /* first line */ - JSScript *script, - JSFunction *fun, - void *callerdata); - -/* called just before script destruction */ -typedef void -(* JS_DLL_CALLBACK JSDestroyScriptHook)(JSContext *cx, - JSScript *script, - void *callerdata); - -typedef void -(* JS_DLL_CALLBACK JSSourceHandler)(const char *filename, uintN lineno, - jschar *str, size_t length, - void **listenerTSData, void *closure); - -/* - * This hook captures high level script execution and function calls (JS or - * native). It is used by JS_SetExecuteHook to hook top level scripts and by - * JS_SetCallHook to hook function calls. It will get called twice per script - * or function call: just before execution begins and just after it finishes. - * In both cases the 'current' frame is that of the executing code. - * - * The 'before' param is JS_TRUE for the hook invocation before the execution - * and JS_FALSE for the invocation after the code has run. - * - * The 'ok' param is significant only on the post execution invocation to - * signify whether or not the code completed 'normally'. - * - * The 'closure' param is as passed to JS_SetExecuteHook or JS_SetCallHook - * for the 'before'invocation, but is whatever value is returned from that - * invocation for the 'after' invocation. Thus, the hook implementor *could* - * allocate a structure in the 'before' invocation and return a pointer to that - * structure. The pointer would then be handed to the hook for the 'after' - * invocation. Alternately, the 'before' could just return the same value as - * in 'closure' to cause the 'after' invocation to be called with the same - * 'closure' value as the 'before'. - * - * Returning NULL in the 'before' hook will cause the 'after' hook *not* to - * be called. - */ -typedef void * -(* JS_DLL_CALLBACK JSInterpreterHook)(JSContext *cx, JSStackFrame *fp, JSBool before, - JSBool *ok, void *closure); - -typedef void -(* JS_DLL_CALLBACK JSObjectHook)(JSContext *cx, JSObject *obj, JSBool isNew, - void *closure); - -typedef JSBool -(* JS_DLL_CALLBACK JSDebugErrorHook)(JSContext *cx, const char *message, - JSErrorReport *report, void *closure); - -#endif /* jsprvtd_h___ */ diff --git a/spidermonkey/src/jspubtd.h b/spidermonkey/src/jspubtd.h deleted file mode 100644 index 4e8c92a..0000000 --- a/spidermonkey/src/jspubtd.h +++ /dev/null @@ -1,667 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jspubtd_h___ -#define jspubtd_h___ -/* - * JS public API typedefs. - */ -#include "jstypes.h" -#include "jscompat.h" - -JS_BEGIN_EXTERN_C - -/* Scalar typedefs. */ -typedef uint16 jschar; -typedef int32 jsint; -typedef uint32 jsuint; -typedef float64 jsdouble; -typedef jsword jsval; -typedef jsword jsid; -typedef int32 jsrefcount; /* PRInt32 if JS_THREADSAFE, see jslock.h */ - -/* - * Run-time version enumeration. See jsconfig.h for compile-time counterparts - * to these values that may be selected by the JS_VERSION macro, and tested by - * #if expressions. - */ -typedef enum JSVersion { - JSVERSION_1_0 = 100, - JSVERSION_1_1 = 110, - JSVERSION_1_2 = 120, - JSVERSION_1_3 = 130, - JSVERSION_1_4 = 140, - JSVERSION_ECMA_3 = 148, - JSVERSION_1_5 = 150, - JSVERSION_1_6 = 160, - JSVERSION_1_7 = 170, - JSVERSION_DEFAULT = 0, - JSVERSION_UNKNOWN = -1 -} JSVersion; - -#define JSVERSION_IS_ECMA(version) \ - ((version) == JSVERSION_DEFAULT || (version) >= JSVERSION_1_3) - -/* Result of typeof operator enumeration. */ -typedef enum JSType { - JSTYPE_VOID, /* undefined */ - JSTYPE_OBJECT, /* object */ - JSTYPE_FUNCTION, /* function */ - JSTYPE_STRING, /* string */ - JSTYPE_NUMBER, /* number */ - JSTYPE_BOOLEAN, /* boolean */ - JSTYPE_NULL, /* null */ - JSTYPE_XML, /* xml object */ - JSTYPE_LIMIT -} JSType; - -/* Dense index into cached prototypes and class atoms for standard objects. */ -typedef enum JSProtoKey { -#define JS_PROTO(name,code,init) JSProto_##name = code, -#include "jsproto.tbl" -#undef JS_PROTO - JSProto_LIMIT -} JSProtoKey; - -/* JSObjectOps.checkAccess mode enumeration. */ -typedef enum JSAccessMode { - JSACC_PROTO = 0, /* XXXbe redundant w.r.t. id */ - JSACC_PARENT = 1, /* XXXbe redundant w.r.t. id */ - JSACC_IMPORT = 2, /* import foo.bar */ - JSACC_WATCH = 3, /* a watchpoint on object foo for id 'bar' */ - JSACC_READ = 4, /* a "get" of foo.bar */ - JSACC_WRITE = 8, /* a "set" of foo.bar = baz */ - JSACC_LIMIT -} JSAccessMode; - -#define JSACC_TYPEMASK (JSACC_WRITE - 1) - -/* - * This enum type is used to control the behavior of a JSObject property - * iterator function that has type JSNewEnumerate. - */ -typedef enum JSIterateOp { - JSENUMERATE_INIT, /* Create new iterator state */ - JSENUMERATE_NEXT, /* Iterate once */ - JSENUMERATE_DESTROY /* Destroy iterator state */ -} JSIterateOp; - -/* Struct typedefs. */ -typedef struct JSClass JSClass; -typedef struct JSExtendedClass JSExtendedClass; -typedef struct JSConstDoubleSpec JSConstDoubleSpec; -typedef struct JSContext JSContext; -typedef struct JSErrorReport JSErrorReport; -typedef struct JSFunction JSFunction; -typedef struct JSFunctionSpec JSFunctionSpec; -typedef struct JSIdArray JSIdArray; -typedef struct JSProperty JSProperty; -typedef struct JSPropertySpec JSPropertySpec; -typedef struct JSObject JSObject; -typedef struct JSObjectMap JSObjectMap; -typedef struct JSObjectOps JSObjectOps; -typedef struct JSXMLObjectOps JSXMLObjectOps; -typedef struct JSRuntime JSRuntime; -typedef struct JSRuntime JSTaskState; /* XXX deprecated name */ -typedef struct JSScript JSScript; -typedef struct JSStackFrame JSStackFrame; -typedef struct JSString JSString; -typedef struct JSXDRState JSXDRState; -typedef struct JSExceptionState JSExceptionState; -typedef struct JSLocaleCallbacks JSLocaleCallbacks; - -/* JSClass (and JSObjectOps where appropriate) function pointer typedefs. */ - -/* - * Add, delete, get or set a property named by id in obj. Note the jsval id - * type -- id may be a string (Unicode property identifier) or an int (element - * index). The *vp out parameter, on success, is the new property value after - * an add, get, or set. After a successful delete, *vp is JSVAL_FALSE iff - * obj[id] can't be deleted (because it's permanent). - */ -typedef JSBool -(* JS_DLL_CALLBACK JSPropertyOp)(JSContext *cx, JSObject *obj, jsval id, - jsval *vp); - -/* - * This function type is used for callbacks that enumerate the properties of - * a JSObject. The behavior depends on the value of enum_op: - * - * JSENUMERATE_INIT - * A new, opaque iterator state should be allocated and stored in *statep. - * (You can use PRIVATE_TO_JSVAL() to tag the pointer to be stored). - * - * The number of properties that will be enumerated should be returned as - * an integer jsval in *idp, if idp is non-null, and provided the number of - * enumerable properties is known. If idp is non-null and the number of - * enumerable properties can't be computed in advance, *idp should be set - * to JSVAL_ZERO. - * - * JSENUMERATE_NEXT - * A previously allocated opaque iterator state is passed in via statep. - * Return the next jsid in the iteration using *idp. The opaque iterator - * state pointed at by statep is destroyed and *statep is set to JSVAL_NULL - * if there are no properties left to enumerate. - * - * JSENUMERATE_DESTROY - * Destroy the opaque iterator state previously allocated in *statep by a - * call to this function when enum_op was JSENUMERATE_INIT. - * - * The return value is used to indicate success, with a value of JS_FALSE - * indicating failure. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSNewEnumerateOp)(JSContext *cx, JSObject *obj, - JSIterateOp enum_op, - jsval *statep, jsid *idp); - -/* - * The old-style JSClass.enumerate op should define all lazy properties not - * yet reflected in obj. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSEnumerateOp)(JSContext *cx, JSObject *obj); - -/* - * Resolve a lazy property named by id in obj by defining it directly in obj. - * Lazy properties are those reflected from some peer native property space - * (e.g., the DOM attributes for a given node reflected as obj) on demand. - * - * JS looks for a property in an object, and if not found, tries to resolve - * the given id. If resolve succeeds, the engine looks again in case resolve - * defined obj[id]. If no such property exists directly in obj, the process - * is repeated with obj's prototype, etc. - * - * NB: JSNewResolveOp provides a cheaper way to resolve lazy properties. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSResolveOp)(JSContext *cx, JSObject *obj, jsval id); - -/* - * Like JSResolveOp, but flags provide contextual information as follows: - * - * JSRESOLVE_QUALIFIED a qualified property id: obj.id or obj[id], not id - * JSRESOLVE_ASSIGNING obj[id] is on the left-hand side of an assignment - * JSRESOLVE_DETECTING 'if (o.p)...' or similar detection opcode sequence - * JSRESOLVE_DECLARING var, const, or function prolog declaration opcode - * JSRESOLVE_CLASSNAME class name used when constructing - * - * The *objp out parameter, on success, should be null to indicate that id - * was not resolved; and non-null, referring to obj or one of its prototypes, - * if id was resolved. - * - * This hook instead of JSResolveOp is called via the JSClass.resolve member - * if JSCLASS_NEW_RESOLVE is set in JSClass.flags. - * - * Setting JSCLASS_NEW_RESOLVE and JSCLASS_NEW_RESOLVE_GETS_START further - * extends this hook by passing in the starting object on the prototype chain - * via *objp. Thus a resolve hook implementation may define the property id - * being resolved in the object in which the id was first sought, rather than - * in a prototype object whose class led to the resolve hook being called. - * - * When using JSCLASS_NEW_RESOLVE_GETS_START, the resolve hook must therefore - * null *objp to signify "not resolved". With only JSCLASS_NEW_RESOLVE and no - * JSCLASS_NEW_RESOLVE_GETS_START, the hook can assume *objp is null on entry. - * This is not good practice, but enough existing hook implementations count - * on it that we can't break compatibility by passing the starting object in - * *objp without a new JSClass flag. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSNewResolveOp)(JSContext *cx, JSObject *obj, jsval id, - uintN flags, JSObject **objp); - -/* - * Convert obj to the given type, returning true with the resulting value in - * *vp on success, and returning false on error or exception. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSConvertOp)(JSContext *cx, JSObject *obj, JSType type, - jsval *vp); - -/* - * Finalize obj, which the garbage collector has determined to be unreachable - * from other live objects or from GC roots. Obviously, finalizers must never - * store a reference to obj. - */ -typedef void -(* JS_DLL_CALLBACK JSFinalizeOp)(JSContext *cx, JSObject *obj); - -/* - * Used by JS_AddExternalStringFinalizer and JS_RemoveExternalStringFinalizer - * to extend and reduce the set of string types finalized by the GC. - */ -typedef void -(* JS_DLL_CALLBACK JSStringFinalizeOp)(JSContext *cx, JSString *str); - -/* - * The signature for JSClass.getObjectOps, used by JS_NewObject's internals - * to discover the set of high-level object operations to use for new objects - * of the given class. All native objects have a JSClass, which is stored as - * a private (int-tagged) pointer in obj->slots[JSSLOT_CLASS]. In contrast, - * all native and host objects have a JSObjectMap at obj->map, which may be - * shared among a number of objects, and which contains the JSObjectOps *ops - * pointer used to dispatch object operations from API calls. - * - * Thus JSClass (which pre-dates JSObjectOps in the API) provides a low-level - * interface to class-specific code and data, while JSObjectOps allows for a - * higher level of operation, which does not use the object's class except to - * find the class's JSObjectOps struct, by calling clasp->getObjectOps, and to - * finalize the object. - * - * If this seems backwards, that's because it is! API compatibility requires - * a JSClass *clasp parameter to JS_NewObject, etc. Most host objects do not - * need to implement the larger JSObjectOps, and can share the common JSScope - * code and data used by the native (js_ObjectOps, see jsobj.c) ops. - * - * Further extension to preserve API compatibility: if this function returns - * a pointer to JSXMLObjectOps.base, not to JSObjectOps, then the engine calls - * extended hooks needed for E4X. - */ -typedef JSObjectOps * -(* JS_DLL_CALLBACK JSGetObjectOps)(JSContext *cx, JSClass *clasp); - -/* - * JSClass.checkAccess type: check whether obj[id] may be accessed per mode, - * returning false on error/exception, true on success with obj[id]'s last-got - * value in *vp, and its attributes in *attrsp. As for JSPropertyOp above, id - * is either a string or an int jsval. - * - * See JSCheckAccessIdOp, below, for the JSObjectOps counterpart, which takes - * a jsid (a tagged int or aligned, unique identifier pointer) rather than a - * jsval. The native js_ObjectOps.checkAccess simply forwards to the object's - * clasp->checkAccess, so that both JSClass and JSObjectOps implementors may - * specialize access checks. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSCheckAccessOp)(JSContext *cx, JSObject *obj, jsval id, - JSAccessMode mode, jsval *vp); - -/* - * Encode or decode an object, given an XDR state record representing external - * data. See jsxdrapi.h. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSXDRObjectOp)(JSXDRState *xdr, JSObject **objp); - -/* - * Check whether v is an instance of obj. Return false on error or exception, - * true on success with JS_TRUE in *bp if v is an instance of obj, JS_FALSE in - * *bp otherwise. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSHasInstanceOp)(JSContext *cx, JSObject *obj, jsval v, - JSBool *bp); - -/* - * Function type for JSClass.mark and JSObjectOps.mark, called from the GC to - * scan live GC-things reachable from obj's private data structure. For each - * such thing, a mark implementation must call - * - * JS_MarkGCThing(cx, thing, name, arg); - * - * The trailing name and arg parameters are used for GC_MARK_DEBUG-mode heap - * dumping and ref-path tracing. The mark function should pass a (typically - * literal) string naming the private data member for name, and it must pass - * the opaque arg parameter through from its caller. - * - * For the JSObjectOps.mark hook, the return value is the number of slots at - * obj->slots to scan. For JSClass.mark, the return value is ignored. - * - * NB: JSMarkOp implementations cannot allocate new GC-things (JS_NewObject - * called from a mark function will fail silently, e.g.). - */ -typedef uint32 -(* JS_DLL_CALLBACK JSMarkOp)(JSContext *cx, JSObject *obj, void *arg); - -/* - * The optional JSClass.reserveSlots hook allows a class to make computed - * per-instance object slots reservations, in addition to or instead of using - * JSCLASS_HAS_RESERVED_SLOTS(n) in the JSClass.flags initializer to reserve - * a constant-per-class number of slots. Implementations of this hook should - * return the number of slots to reserve, not including any reserved by using - * JSCLASS_HAS_RESERVED_SLOTS(n) in JSClass.flags. - * - * NB: called with obj locked by the JSObjectOps-specific mutual exclusion - * mechanism appropriate for obj, so don't nest other operations that might - * also lock obj. - */ -typedef uint32 -(* JS_DLL_CALLBACK JSReserveSlotsOp)(JSContext *cx, JSObject *obj); - -/* JSObjectOps function pointer typedefs. */ - -/* - * Create a new subclass of JSObjectMap (see jsobj.h), with the nrefs and ops - * members initialized from the same-named parameters, and with the nslots and - * freeslot members initialized according to ops and clasp. Return null on - * error, non-null on success. - * - * JSObjectMaps are reference-counted by generic code in the engine. Usually, - * the nrefs parameter to JSObjectOps.newObjectMap will be 1, to count the ref - * returned to the caller on success. After a successful construction, some - * number of js_HoldObjectMap and js_DropObjectMap calls ensue. When nrefs - * reaches 0 due to a js_DropObjectMap call, JSObjectOps.destroyObjectMap will - * be called to dispose of the map. - */ -typedef JSObjectMap * -(* JS_DLL_CALLBACK JSNewObjectMapOp)(JSContext *cx, jsrefcount nrefs, - JSObjectOps *ops, JSClass *clasp, - JSObject *obj); - -/* - * Generic type for an infallible JSObjectMap operation, used currently by - * JSObjectOps.destroyObjectMap. - */ -typedef void -(* JS_DLL_CALLBACK JSObjectMapOp)(JSContext *cx, JSObjectMap *map); - -/* - * Look for id in obj and its prototype chain, returning false on error or - * exception, true on success. On success, return null in *propp if id was - * not found. If id was found, return the first object searching from obj - * along its prototype chain in which id names a direct property in *objp, and - * return a non-null, opaque property pointer in *propp. - * - * If JSLookupPropOp succeeds and returns with *propp non-null, that pointer - * may be passed as the prop parameter to a JSAttributesOp, as a short-cut - * that bypasses id re-lookup. In any case, a non-null *propp result after a - * successful lookup must be dropped via JSObjectOps.dropProperty. - * - * NB: successful return with non-null *propp means the implementation may - * have locked *objp and added a reference count associated with *propp, so - * callers should not risk deadlock by nesting or interleaving other lookups - * or any obj-bearing ops before dropping *propp. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSLookupPropOp)(JSContext *cx, JSObject *obj, jsid id, - JSObject **objp, JSProperty **propp); - -/* - * Define obj[id], a direct property of obj named id, having the given initial - * value, with the specified getter, setter, and attributes. If the propp out - * param is non-null, *propp on successful return contains an opaque property - * pointer usable as a speedup hint with JSAttributesOp. But note that propp - * may be null, indicating that the caller is not interested in recovering an - * opaque pointer to the newly-defined property. - * - * If propp is non-null and JSDefinePropOp succeeds, its caller must be sure - * to drop *propp using JSObjectOps.dropProperty in short order, just as with - * JSLookupPropOp. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSDefinePropOp)(JSContext *cx, JSObject *obj, - jsid id, jsval value, - JSPropertyOp getter, JSPropertyOp setter, - uintN attrs, JSProperty **propp); - -/* - * Get, set, or delete obj[id], returning false on error or exception, true - * on success. If getting or setting, the new value is returned in *vp on - * success. If deleting without error, *vp will be JSVAL_FALSE if obj[id] is - * permanent, and JSVAL_TRUE if id named a direct property of obj that was in - * fact deleted, or if id names no direct property of obj (id could name a - * prototype property, or no property in obj or its prototype chain). - */ -typedef JSBool -(* JS_DLL_CALLBACK JSPropertyIdOp)(JSContext *cx, JSObject *obj, jsid id, - jsval *vp); - -/* - * Get or set attributes of the property obj[id]. Return false on error or - * exception, true with current attributes in *attrsp. If prop is non-null, - * it must come from the *propp out parameter of a prior JSDefinePropOp or - * JSLookupPropOp call. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSAttributesOp)(JSContext *cx, JSObject *obj, jsid id, - JSProperty *prop, uintN *attrsp); - -/* - * JSObjectOps.checkAccess type: check whether obj[id] may be accessed per - * mode, returning false on error/exception, true on success with obj[id]'s - * last-got value in *vp, and its attributes in *attrsp. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSCheckAccessIdOp)(JSContext *cx, JSObject *obj, jsid id, - JSAccessMode mode, jsval *vp, - uintN *attrsp); - -/* - * A generic type for functions mapping an object to another object, or null - * if an error or exception was thrown on cx. Used by JSObjectOps.thisObject - * at present. - */ -typedef JSObject * -(* JS_DLL_CALLBACK JSObjectOp)(JSContext *cx, JSObject *obj); - -/* - * A generic type for functions taking a context, object, and property, with - * no return value. Used by JSObjectOps.dropProperty currently (see above, - * JSDefinePropOp and JSLookupPropOp, for the object-locking protocol in which - * dropProperty participates). - */ -typedef void -(* JS_DLL_CALLBACK JSPropertyRefOp)(JSContext *cx, JSObject *obj, - JSProperty *prop); - -/* - * Function type for JSObjectOps.setProto and JSObjectOps.setParent. These - * hooks must check for cycles without deadlocking, and otherwise take special - * steps. See jsobj.c, js_SetProtoOrParent, for an example. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSSetObjectSlotOp)(JSContext *cx, JSObject *obj, - uint32 slot, JSObject *pobj); - -/* - * Get and set a required slot, one that should already have been allocated. - * These operations are infallible, so required slots must be pre-allocated, - * or implementations must suppress out-of-memory errors. The native ops - * (js_ObjectOps, see jsobj.c) access slots reserved by including a call to - * the JSCLASS_HAS_RESERVED_SLOTS(n) macro in the JSClass.flags initializer. - * - * NB: the slot parameter is a zero-based index into obj->slots[], unlike the - * index parameter to the JS_GetReservedSlot and JS_SetReservedSlot API entry - * points, which is a zero-based index into the JSCLASS_RESERVED_SLOTS(clasp) - * reserved slots that come after the initial well-known slots: proto, parent, - * class, and optionally, the private data slot. - */ -typedef jsval -(* JS_DLL_CALLBACK JSGetRequiredSlotOp)(JSContext *cx, JSObject *obj, - uint32 slot); - -typedef JSBool -(* JS_DLL_CALLBACK JSSetRequiredSlotOp)(JSContext *cx, JSObject *obj, - uint32 slot, jsval v); - -typedef JSObject * -(* JS_DLL_CALLBACK JSGetMethodOp)(JSContext *cx, JSObject *obj, jsid id, - jsval *vp); - -typedef JSBool -(* JS_DLL_CALLBACK JSSetMethodOp)(JSContext *cx, JSObject *obj, jsid id, - jsval *vp); - -typedef JSBool -(* JS_DLL_CALLBACK JSEnumerateValuesOp)(JSContext *cx, JSObject *obj, - JSIterateOp enum_op, - jsval *statep, jsid *idp, jsval *vp); - -typedef JSBool -(* JS_DLL_CALLBACK JSEqualityOp)(JSContext *cx, JSObject *obj, jsval v, - JSBool *bp); - -typedef JSBool -(* JS_DLL_CALLBACK JSConcatenateOp)(JSContext *cx, JSObject *obj, jsval v, - jsval *vp); - -/* Typedef for native functions called by the JS VM. */ - -typedef JSBool -(* JS_DLL_CALLBACK JSNative)(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval); - -/* Callbacks and their arguments. */ - -typedef enum JSContextOp { - JSCONTEXT_NEW, - JSCONTEXT_DESTROY -} JSContextOp; - -/* - * The possible values for contextOp when the runtime calls the callback are: - * JSCONTEXT_NEW JS_NewContext succesfully created a new JSContext - * instance. The callback can initialize the instance as - * required. If the callback returns false, the instance - * will be destroyed and JS_NewContext returns null. In - * this case the callback is not called again. - * JSCONTEXT_DESTROY One of JS_DestroyContext* methods is called. The - * callback may perform its own cleanup and must always - * return true. - * Any other value For future compatibility the callback must do nothing - * and return true in this case. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSContextCallback)(JSContext *cx, uintN contextOp); - -typedef enum JSGCStatus { - JSGC_BEGIN, - JSGC_END, - JSGC_MARK_END, - JSGC_FINALIZE_END -} JSGCStatus; - -typedef JSBool -(* JS_DLL_CALLBACK JSGCCallback)(JSContext *cx, JSGCStatus status); - -typedef JSBool -(* JS_DLL_CALLBACK JSBranchCallback)(JSContext *cx, JSScript *script); - -typedef void -(* JS_DLL_CALLBACK JSErrorReporter)(JSContext *cx, const char *message, - JSErrorReport *report); - -/* - * Possible exception types. These types are part of a JSErrorFormatString - * structure. They define which error to throw in case of a runtime error. - * JSEXN_NONE marks an unthrowable error. - */ -typedef enum JSExnType { - JSEXN_NONE = -1, - JSEXN_ERR, - JSEXN_INTERNALERR, - JSEXN_EVALERR, - JSEXN_RANGEERR, - JSEXN_REFERENCEERR, - JSEXN_SYNTAXERR, - JSEXN_TYPEERR, - JSEXN_URIERR, - JSEXN_LIMIT -} JSExnType; - -typedef struct JSErrorFormatString { - /* The error format string (UTF-8 if JS_C_STRINGS_ARE_UTF8 is defined). */ - const char *format; - - /* The number of arguments to expand in the formatted error message. */ - uint16 argCount; - - /* One of the JSExnType constants above. */ - int16 exnType; -} JSErrorFormatString; - -typedef const JSErrorFormatString * -(* JS_DLL_CALLBACK JSErrorCallback)(void *userRef, const char *locale, - const uintN errorNumber); - -#ifdef va_start -#define JS_ARGUMENT_FORMATTER_DEFINED 1 - -typedef JSBool -(* JS_DLL_CALLBACK JSArgumentFormatter)(JSContext *cx, const char *format, - JSBool fromJS, jsval **vpp, - va_list *app); -#endif - -typedef JSBool -(* JS_DLL_CALLBACK JSLocaleToUpperCase)(JSContext *cx, JSString *src, - jsval *rval); - -typedef JSBool -(* JS_DLL_CALLBACK JSLocaleToLowerCase)(JSContext *cx, JSString *src, - jsval *rval); - -typedef JSBool -(* JS_DLL_CALLBACK JSLocaleCompare)(JSContext *cx, - JSString *src1, JSString *src2, - jsval *rval); - -typedef JSBool -(* JS_DLL_CALLBACK JSLocaleToUnicode)(JSContext *cx, char *src, jsval *rval); - -/* - * Security protocol types. - */ -typedef struct JSPrincipals JSPrincipals; - -/* - * XDR-encode or -decode a principals instance, based on whether xdr->mode is - * JSXDR_ENCODE, in which case *principalsp should be encoded; or JSXDR_DECODE, - * in which case implementations must return a held (via JSPRINCIPALS_HOLD), - * non-null *principalsp out parameter. Return true on success, false on any - * error, which the implementation must have reported. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSPrincipalsTranscoder)(JSXDRState *xdr, - JSPrincipals **principalsp); - -/* - * Return a weak reference to the principals associated with obj, possibly via - * the immutable parent chain leading from obj to a top-level container (e.g., - * a window object in the DOM level 0). If there are no principals associated - * with obj, return null. Therefore null does not mean an error was reported; - * in no event should an error be reported or an exception be thrown by this - * callback's implementation. - */ -typedef JSPrincipals * -(* JS_DLL_CALLBACK JSObjectPrincipalsFinder)(JSContext *cx, JSObject *obj); - -JS_END_EXTERN_C - -#endif /* jspubtd_h___ */ diff --git a/spidermonkey/src/jsregexp.c b/spidermonkey/src/jsregexp.c deleted file mode 100644 index 5d2fce4..0000000 --- a/spidermonkey/src/jsregexp.c +++ /dev/null @@ -1,4206 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set sw=4 ts=8 et tw=78: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS regular expressions, after Perl. - */ -#include "jsstddef.h" -#include -#include -#include "jstypes.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsregexp.h" -#include "jsscan.h" -#include "jsstr.h" - -/* Note : contiguity of 'simple opcodes' is important for SimpleMatch() */ -typedef enum REOp { - REOP_EMPTY = 0, /* match rest of input against rest of r.e. */ - REOP_ALT = 1, /* alternative subexpressions in kid and next */ - REOP_SIMPLE_START = 2, /* start of 'simple opcodes' */ - REOP_BOL = 2, /* beginning of input (or line if multiline) */ - REOP_EOL = 3, /* end of input (or line if multiline) */ - REOP_WBDRY = 4, /* match "" at word boundary */ - REOP_WNONBDRY = 5, /* match "" at word non-boundary */ - REOP_DOT = 6, /* stands for any character */ - REOP_DIGIT = 7, /* match a digit char: [0-9] */ - REOP_NONDIGIT = 8, /* match a non-digit char: [^0-9] */ - REOP_ALNUM = 9, /* match an alphanumeric char: [0-9a-z_A-Z] */ - REOP_NONALNUM = 10, /* match a non-alphanumeric char: [^0-9a-z_A-Z] */ - REOP_SPACE = 11, /* match a whitespace char */ - REOP_NONSPACE = 12, /* match a non-whitespace char */ - REOP_BACKREF = 13, /* back-reference (e.g., \1) to a parenthetical */ - REOP_FLAT = 14, /* match a flat string */ - REOP_FLAT1 = 15, /* match a single char */ - REOP_FLATi = 16, /* case-independent REOP_FLAT */ - REOP_FLAT1i = 17, /* case-independent REOP_FLAT1 */ - REOP_UCFLAT1 = 18, /* single Unicode char */ - REOP_UCFLAT1i = 19, /* case-independent REOP_UCFLAT1 */ - REOP_UCFLAT = 20, /* flat Unicode string; len immediate counts chars */ - REOP_UCFLATi = 21, /* case-independent REOP_UCFLAT */ - REOP_CLASS = 22, /* character class with index */ - REOP_NCLASS = 23, /* negated character class with index */ - REOP_SIMPLE_END = 23, /* end of 'simple opcodes' */ - REOP_QUANT = 25, /* quantified atom: atom{1,2} */ - REOP_STAR = 26, /* zero or more occurrences of kid */ - REOP_PLUS = 27, /* one or more occurrences of kid */ - REOP_OPT = 28, /* optional subexpression in kid */ - REOP_LPAREN = 29, /* left paren bytecode: kid is u.num'th sub-regexp */ - REOP_RPAREN = 30, /* right paren bytecode */ - REOP_JUMP = 31, /* for deoptimized closure loops */ - REOP_DOTSTAR = 32, /* optimize .* to use a single opcode */ - REOP_ANCHOR = 33, /* like .* but skips left context to unanchored r.e. */ - REOP_EOLONLY = 34, /* $ not preceded by any pattern */ - REOP_BACKREFi = 37, /* case-independent REOP_BACKREF */ - REOP_LPARENNON = 41, /* non-capturing version of REOP_LPAREN */ - REOP_ASSERT = 43, /* zero width positive lookahead assertion */ - REOP_ASSERT_NOT = 44, /* zero width negative lookahead assertion */ - REOP_ASSERTTEST = 45, /* sentinel at end of assertion child */ - REOP_ASSERTNOTTEST = 46, /* sentinel at end of !assertion child */ - REOP_MINIMALSTAR = 47, /* non-greedy version of * */ - REOP_MINIMALPLUS = 48, /* non-greedy version of + */ - REOP_MINIMALOPT = 49, /* non-greedy version of ? */ - REOP_MINIMALQUANT = 50, /* non-greedy version of {} */ - REOP_ENDCHILD = 51, /* sentinel at end of quantifier child */ - REOP_REPEAT = 52, /* directs execution of greedy quantifier */ - REOP_MINIMALREPEAT = 53, /* directs execution of non-greedy quantifier */ - REOP_ALTPREREQ = 54, /* prerequisite for ALT, either of two chars */ - REOP_ALTPREREQ2 = 55, /* prerequisite for ALT, a char or a class */ - REOP_ENDALT = 56, /* end of final alternate */ - REOP_CONCAT = 57, /* concatenation of terms (parse time only) */ - - REOP_END -} REOp; - -#define REOP_IS_SIMPLE(op) ((unsigned)((op) - REOP_SIMPLE_START) < \ - (unsigned)REOP_SIMPLE_END) - -struct RENode { - REOp op; /* r.e. op bytecode */ - RENode *next; /* next in concatenation order */ - void *kid; /* first operand */ - union { - void *kid2; /* second operand */ - jsint num; /* could be a number */ - size_t parenIndex; /* or a parenthesis index */ - struct { /* or a quantifier range */ - uintN min; - uintN max; - JSPackedBool greedy; - } range; - struct { /* or a character class */ - size_t startIndex; - size_t kidlen; /* length of string at kid, in jschars */ - size_t index; /* index into class list */ - uint16 bmsize; /* bitmap size, based on max char code */ - JSPackedBool sense; - } ucclass; - struct { /* or a literal sequence */ - jschar chr; /* of one character */ - size_t length; /* or many (via the kid) */ - } flat; - struct { - RENode *kid2; /* second operand from ALT */ - jschar ch1; /* match char for ALTPREREQ */ - jschar ch2; /* ditto, or class index for ALTPREREQ2 */ - } altprereq; - } u; -}; - -#define RE_IS_LETTER(c) (((c >= 'A') && (c <= 'Z')) || \ - ((c >= 'a') && (c <= 'z')) ) -#define RE_IS_LINE_TERM(c) ((c == '\n') || (c == '\r') || \ - (c == LINE_SEPARATOR) || (c == PARA_SEPARATOR)) - -#define CLASS_CACHE_SIZE 4 - -typedef struct CompilerState { - JSContext *context; - JSTokenStream *tokenStream; /* For reporting errors */ - const jschar *cpbegin; - const jschar *cpend; - const jschar *cp; - size_t parenCount; - size_t classCount; /* number of [] encountered */ - size_t treeDepth; /* maximum depth of parse tree */ - size_t progLength; /* estimated bytecode length */ - RENode *result; - size_t classBitmapsMem; /* memory to hold all class bitmaps */ - struct { - const jschar *start; /* small cache of class strings */ - size_t length; /* since they're often the same */ - size_t index; - } classCache[CLASS_CACHE_SIZE]; - uint16 flags; -} CompilerState; - -typedef struct EmitStateStackEntry { - jsbytecode *altHead; /* start of REOP_ALT* opcode */ - jsbytecode *nextAltFixup; /* fixup pointer to next-alt offset */ - jsbytecode *nextTermFixup; /* fixup ptr. to REOP_JUMP offset */ - jsbytecode *endTermFixup; /* fixup ptr. to REOPT_ALTPREREQ* offset */ - RENode *continueNode; /* original REOP_ALT* node being stacked */ - jsbytecode continueOp; /* REOP_JUMP or REOP_ENDALT continuation */ - JSPackedBool jumpToJumpFlag; /* true if we've patched jump-to-jump to - avoid 16-bit unsigned offset overflow */ -} EmitStateStackEntry; - -/* - * Immediate operand sizes and getter/setters. Unlike the ones in jsopcode.h, - * the getters and setters take the pc of the offset, not of the opcode before - * the offset. - */ -#define ARG_LEN 2 -#define GET_ARG(pc) ((uint16)(((pc)[0] << 8) | (pc)[1])) -#define SET_ARG(pc, arg) ((pc)[0] = (jsbytecode) ((arg) >> 8), \ - (pc)[1] = (jsbytecode) (arg)) - -#define OFFSET_LEN ARG_LEN -#define OFFSET_MAX (JS_BIT(ARG_LEN * 8) - 1) -#define GET_OFFSET(pc) GET_ARG(pc) - -/* - * Maximum supported tree depth is maximum size of EmitStateStackEntry stack. - * For sanity, we limit it to 2^24 bytes. - */ -#define TREE_DEPTH_MAX (JS_BIT(24) / sizeof(EmitStateStackEntry)) - -/* - * The maximum memory that can be allocated for class bitmaps. - * For sanity, we limit it to 2^24 bytes. - */ -#define CLASS_BITMAPS_MEM_LIMIT JS_BIT(24) - -/* - * Functions to get size and write/read bytecode that represent small indexes - * compactly. - * Each byte in the code represent 7-bit chunk of the index. 8th bit when set - * indicates that the following byte brings more bits to the index. Otherwise - * this is the last byte in the index bytecode representing highest index bits. - */ -static size_t -GetCompactIndexWidth(size_t index) -{ - size_t width; - - for (width = 1; (index >>= 7) != 0; ++width) { } - return width; -} - -static jsbytecode * -WriteCompactIndex(jsbytecode *pc, size_t index) -{ - size_t next; - - while ((next = index >> 7) != 0) { - *pc++ = (jsbytecode)(index | 0x80); - index = next; - } - *pc++ = (jsbytecode)index; - return pc; -} - -static jsbytecode * -ReadCompactIndex(jsbytecode *pc, size_t *result) -{ - size_t nextByte; - - nextByte = *pc++; - if ((nextByte & 0x80) == 0) { - /* - * Short-circuit the most common case when compact index <= 127. - */ - *result = nextByte; - } else { - size_t shift = 7; - *result = 0x7F & nextByte; - do { - nextByte = *pc++; - *result |= (nextByte & 0x7F) << shift; - shift += 7; - } while ((nextByte & 0x80) != 0); - } - return pc; -} - -typedef struct RECapture { - ptrdiff_t index; /* start of contents, -1 for empty */ - size_t length; /* length of capture */ -} RECapture; - -typedef struct REMatchState { - const jschar *cp; - RECapture parens[1]; /* first of 're->parenCount' captures, - allocated at end of this struct */ -} REMatchState; - -struct REBackTrackData; - -typedef struct REProgState { - jsbytecode *continue_pc; /* current continuation data */ - jsbytecode continue_op; - ptrdiff_t index; /* progress in text */ - size_t parenSoFar; /* highest indexed paren started */ - union { - struct { - uintN min; /* current quantifier limits */ - uintN max; - } quantifier; - struct { - size_t top; /* backtrack stack state */ - size_t sz; - } assertion; - } u; -} REProgState; - -typedef struct REBackTrackData { - size_t sz; /* size of previous stack entry */ - jsbytecode *backtrack_pc; /* where to backtrack to */ - jsbytecode backtrack_op; - const jschar *cp; /* index in text of match at backtrack */ - size_t parenIndex; /* start index of saved paren contents */ - size_t parenCount; /* # of saved paren contents */ - size_t saveStateStackTop; /* number of parent states */ - /* saved parent states follow */ - /* saved paren contents follow */ -} REBackTrackData; - -#define INITIAL_STATESTACK 100 -#define INITIAL_BACKTRACK 8000 - -typedef struct REGlobalData { - JSContext *cx; - JSRegExp *regexp; /* the RE in execution */ - JSBool ok; /* runtime error (out_of_memory only?) */ - size_t start; /* offset to start at */ - ptrdiff_t skipped; /* chars skipped anchoring this r.e. */ - const jschar *cpbegin; /* text base address */ - const jschar *cpend; /* text limit address */ - - REProgState *stateStack; /* stack of state of current parents */ - size_t stateStackTop; - size_t stateStackLimit; - - REBackTrackData *backTrackStack;/* stack of matched-so-far positions */ - REBackTrackData *backTrackSP; - size_t backTrackStackSize; - size_t cursz; /* size of current stack entry */ - - JSArenaPool pool; /* It's faster to use one malloc'd pool - than to malloc/free the three items - that are allocated from this pool */ -} REGlobalData; - -/* - * 1. If IgnoreCase is false, return ch. - * 2. Let u be ch converted to upper case as if by calling - * String.prototype.toUpperCase on the one-character string ch. - * 3. If u does not consist of a single character, return ch. - * 4. Let cu be u's character. - * 5. If ch's code point value is greater than or equal to decimal 128 and cu's - * code point value is less than decimal 128, then return ch. - * 6. Return cu. - */ -static jschar -upcase(jschar ch) -{ - jschar cu = JS_TOUPPER(ch); - if (ch >= 128 && cu < 128) - return ch; - return cu; -} - -static jschar -downcase(jschar ch) -{ - jschar cl = JS_TOLOWER(ch); - if (cl >= 128 && ch < 128) - return ch; - return cl; -} - -/* Construct and initialize an RENode, returning NULL for out-of-memory */ -static RENode * -NewRENode(CompilerState *state, REOp op) -{ - JSContext *cx; - RENode *ren; - - cx = state->context; - JS_ARENA_ALLOCATE_CAST(ren, RENode *, &cx->tempPool, sizeof *ren); - if (!ren) { - JS_ReportOutOfMemory(cx); - return NULL; - } - ren->op = op; - ren->next = NULL; - ren->kid = NULL; - return ren; -} - -/* - * Validates and converts hex ascii value. - */ -static JSBool -isASCIIHexDigit(jschar c, uintN *digit) -{ - uintN cv = c; - - if (cv < '0') - return JS_FALSE; - if (cv <= '9') { - *digit = cv - '0'; - return JS_TRUE; - } - cv |= 0x20; - if (cv >= 'a' && cv <= 'f') { - *digit = cv - 'a' + 10; - return JS_TRUE; - } - return JS_FALSE; -} - - -typedef struct { - REOp op; - const jschar *errPos; - size_t parenIndex; -} REOpData; - - -/* - * Process the op against the two top operands, reducing them to a single - * operand in the penultimate slot. Update progLength and treeDepth. - */ -static JSBool -ProcessOp(CompilerState *state, REOpData *opData, RENode **operandStack, - intN operandSP) -{ - RENode *result; - - switch (opData->op) { - case REOP_ALT: - result = NewRENode(state, REOP_ALT); - if (!result) - return JS_FALSE; - result->kid = operandStack[operandSP - 2]; - result->u.kid2 = operandStack[operandSP - 1]; - operandStack[operandSP - 2] = result; - - if (state->treeDepth == TREE_DEPTH_MAX) { - js_ReportCompileErrorNumber(state->context, state->tokenStream, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_REGEXP_TOO_COMPLEX); - return JS_FALSE; - } - ++state->treeDepth; - - /* - * Look at both alternates to see if there's a FLAT or a CLASS at - * the start of each. If so, use a prerequisite match. - */ - if (((RENode *) result->kid)->op == REOP_FLAT && - ((RENode *) result->u.kid2)->op == REOP_FLAT && - (state->flags & JSREG_FOLD) == 0) { - result->op = REOP_ALTPREREQ; - result->u.altprereq.ch1 = ((RENode *) result->kid)->u.flat.chr; - result->u.altprereq.ch2 = ((RENode *) result->u.kid2)->u.flat.chr; - /* ALTPREREQ, , uch1, uch2, , ..., - JUMP, ... ENDALT */ - state->progLength += 13; - } - else - if (((RENode *) result->kid)->op == REOP_CLASS && - ((RENode *) result->kid)->u.ucclass.index < 256 && - ((RENode *) result->u.kid2)->op == REOP_FLAT && - (state->flags & JSREG_FOLD) == 0) { - result->op = REOP_ALTPREREQ2; - result->u.altprereq.ch1 = ((RENode *) result->u.kid2)->u.flat.chr; - result->u.altprereq.ch2 = ((RENode *) result->kid)->u.ucclass.index; - /* ALTPREREQ2, , uch1, uch2, , ..., - JUMP, ... ENDALT */ - state->progLength += 13; - } - else - if (((RENode *) result->kid)->op == REOP_FLAT && - ((RENode *) result->u.kid2)->op == REOP_CLASS && - ((RENode *) result->u.kid2)->u.ucclass.index < 256 && - (state->flags & JSREG_FOLD) == 0) { - result->op = REOP_ALTPREREQ2; - result->u.altprereq.ch1 = ((RENode *) result->kid)->u.flat.chr; - result->u.altprereq.ch2 = - ((RENode *) result->u.kid2)->u.ucclass.index; - /* ALTPREREQ2, , uch1, uch2, , ..., - JUMP, ... ENDALT */ - state->progLength += 13; - } - else { - /* ALT, , ..., JUMP, ... ENDALT */ - state->progLength += 7; - } - break; - - case REOP_CONCAT: - result = operandStack[operandSP - 2]; - while (result->next) - result = result->next; - result->next = operandStack[operandSP - 1]; - break; - - case REOP_ASSERT: - case REOP_ASSERT_NOT: - case REOP_LPARENNON: - case REOP_LPAREN: - /* These should have been processed by a close paren. */ - js_ReportCompileErrorNumberUC(state->context, state->tokenStream, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_MISSING_PAREN, opData->errPos); - return JS_FALSE; - - default:; - } - return JS_TRUE; -} - -/* - * Parser forward declarations. - */ -static JSBool ParseTerm(CompilerState *state); -static JSBool ParseQuantifier(CompilerState *state); -static intN ParseMinMaxQuantifier(CompilerState *state, JSBool ignoreValues); - -/* - * Top-down regular expression grammar, based closely on Perl4. - * - * regexp: altern A regular expression is one or more - * altern '|' regexp alternatives separated by vertical bar. - */ -#define INITIAL_STACK_SIZE 128 - -static JSBool -ParseRegExp(CompilerState *state) -{ - size_t parenIndex; - RENode *operand; - REOpData *operatorStack; - RENode **operandStack; - REOp op; - intN i; - JSBool result = JS_FALSE; - - intN operatorSP = 0, operatorStackSize = INITIAL_STACK_SIZE; - intN operandSP = 0, operandStackSize = INITIAL_STACK_SIZE; - - /* Watch out for empty regexp */ - if (state->cp == state->cpend) { - state->result = NewRENode(state, REOP_EMPTY); - return (state->result != NULL); - } - - operatorStack = (REOpData *) - JS_malloc(state->context, sizeof(REOpData) * operatorStackSize); - if (!operatorStack) - return JS_FALSE; - - operandStack = (RENode **) - JS_malloc(state->context, sizeof(RENode *) * operandStackSize); - if (!operandStack) - goto out; - - for (;;) { - parenIndex = state->parenCount; - if (state->cp == state->cpend) { - /* - * If we are at the end of the regexp and we're short one or more - * operands, the regexp must have the form /x|/ or some such, with - * left parentheses making us short more than one operand. - */ - if (operatorSP >= operandSP) { - operand = NewRENode(state, REOP_EMPTY); - if (!operand) - goto out; - goto pushOperand; - } - } else { - switch (*state->cp) { - case '(': - ++state->cp; - if (state->cp + 1 < state->cpend && - *state->cp == '?' && - (state->cp[1] == '=' || - state->cp[1] == '!' || - state->cp[1] == ':')) { - switch (state->cp[1]) { - case '=': - op = REOP_ASSERT; - /* ASSERT, , ... ASSERTTEST */ - state->progLength += 4; - break; - case '!': - op = REOP_ASSERT_NOT; - /* ASSERTNOT, , ... ASSERTNOTTEST */ - state->progLength += 4; - break; - default: - op = REOP_LPARENNON; - break; - } - state->cp += 2; - } else { - op = REOP_LPAREN; - /* LPAREN, , ... RPAREN, */ - state->progLength - += 2 * (1 + GetCompactIndexWidth(parenIndex)); - state->parenCount++; - if (state->parenCount == 65535) { - js_ReportCompileErrorNumber(state->context, - state->tokenStream, - JSREPORT_TS | - JSREPORT_ERROR, - JSMSG_TOO_MANY_PARENS); - goto out; - } - } - goto pushOperator; - - case ')': - /* - * If there's no stacked open parenthesis, throw syntax error. - */ - for (i = operatorSP - 1; ; i--) { - if (i < 0) { - js_ReportCompileErrorNumber(state->context, - state->tokenStream, - JSREPORT_TS | - JSREPORT_ERROR, - JSMSG_UNMATCHED_RIGHT_PAREN); - goto out; - } - if (operatorStack[i].op == REOP_ASSERT || - operatorStack[i].op == REOP_ASSERT_NOT || - operatorStack[i].op == REOP_LPARENNON || - operatorStack[i].op == REOP_LPAREN) { - break; - } - } - /* FALL THROUGH */ - - case '|': - /* Expected an operand before these, so make an empty one */ - operand = NewRENode(state, REOP_EMPTY); - if (!operand) - goto out; - goto pushOperand; - - default: - if (!ParseTerm(state)) - goto out; - operand = state->result; -pushOperand: - if (operandSP == operandStackSize) { - operandStackSize += operandStackSize; - operandStack = (RENode **) - JS_realloc(state->context, operandStack, - sizeof(RENode *) * operandStackSize); - if (!operandStack) - goto out; - } - operandStack[operandSP++] = operand; - break; - } - } - - /* At the end; process remaining operators. */ -restartOperator: - if (state->cp == state->cpend) { - while (operatorSP) { - --operatorSP; - if (!ProcessOp(state, &operatorStack[operatorSP], - operandStack, operandSP)) - goto out; - --operandSP; - } - JS_ASSERT(operandSP == 1); - state->result = operandStack[0]; - result = JS_TRUE; - goto out; - } - - switch (*state->cp) { - case '|': - /* Process any stacked 'concat' operators */ - ++state->cp; - while (operatorSP && - operatorStack[operatorSP - 1].op == REOP_CONCAT) { - --operatorSP; - if (!ProcessOp(state, &operatorStack[operatorSP], - operandStack, operandSP)) { - goto out; - } - --operandSP; - } - op = REOP_ALT; - goto pushOperator; - - case ')': - /* - * If there's no stacked open parenthesis, throw syntax error. - */ - for (i = operatorSP - 1; ; i--) { - if (i < 0) { - js_ReportCompileErrorNumber(state->context, - state->tokenStream, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_UNMATCHED_RIGHT_PAREN); - goto out; - } - if (operatorStack[i].op == REOP_ASSERT || - operatorStack[i].op == REOP_ASSERT_NOT || - operatorStack[i].op == REOP_LPARENNON || - operatorStack[i].op == REOP_LPAREN) { - break; - } - } - ++state->cp; - - /* Process everything on the stack until the open parenthesis. */ - for (;;) { - JS_ASSERT(operatorSP); - --operatorSP; - switch (operatorStack[operatorSP].op) { - case REOP_ASSERT: - case REOP_ASSERT_NOT: - case REOP_LPAREN: - operand = NewRENode(state, operatorStack[operatorSP].op); - if (!operand) - goto out; - operand->u.parenIndex = - operatorStack[operatorSP].parenIndex; - JS_ASSERT(operandSP); - operand->kid = operandStack[operandSP - 1]; - operandStack[operandSP - 1] = operand; - if (state->treeDepth == TREE_DEPTH_MAX) { - js_ReportCompileErrorNumber(state->context, - state->tokenStream, - JSREPORT_TS | - JSREPORT_ERROR, - JSMSG_REGEXP_TOO_COMPLEX); - goto out; - } - ++state->treeDepth; - /* FALL THROUGH */ - - case REOP_LPARENNON: - state->result = operandStack[operandSP - 1]; - if (!ParseQuantifier(state)) - goto out; - operandStack[operandSP - 1] = state->result; - goto restartOperator; - default: - if (!ProcessOp(state, &operatorStack[operatorSP], - operandStack, operandSP)) - goto out; - --operandSP; - break; - } - } - break; - - case '{': - { - const jschar *errp = state->cp; - - if (ParseMinMaxQuantifier(state, JS_TRUE) < 0) { - /* - * This didn't even scan correctly as a quantifier, so we should - * treat it as flat. - */ - op = REOP_CONCAT; - goto pushOperator; - } - - state->cp = errp; - /* FALL THROUGH */ - } - - case '+': - case '*': - case '?': - js_ReportCompileErrorNumberUC(state->context, state->tokenStream, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_QUANTIFIER, state->cp); - result = JS_FALSE; - goto out; - - default: - /* Anything else is the start of the next term. */ - op = REOP_CONCAT; -pushOperator: - if (operatorSP == operatorStackSize) { - operatorStackSize += operatorStackSize; - operatorStack = (REOpData *) - JS_realloc(state->context, operatorStack, - sizeof(REOpData) * operatorStackSize); - if (!operatorStack) - goto out; - } - operatorStack[operatorSP].op = op; - operatorStack[operatorSP].errPos = state->cp; - operatorStack[operatorSP++].parenIndex = parenIndex; - break; - } - } -out: - if (operatorStack) - JS_free(state->context, operatorStack); - if (operandStack) - JS_free(state->context, operandStack); - return result; -} - -/* - * Hack two bits in CompilerState.flags, for use within FindParenCount to flag - * its being on the stack, and to propagate errors to its callers. - */ -#define JSREG_FIND_PAREN_COUNT 0x8000 -#define JSREG_FIND_PAREN_ERROR 0x4000 - -/* - * Magic return value from FindParenCount and GetDecimalValue, to indicate - * overflow beyond GetDecimalValue's max parameter, or a computed maximum if - * its findMax parameter is non-null. - */ -#define OVERFLOW_VALUE ((uintN)-1) - -static uintN -FindParenCount(CompilerState *state) -{ - CompilerState temp; - int i; - - if (state->flags & JSREG_FIND_PAREN_COUNT) - return OVERFLOW_VALUE; - - /* - * Copy state into temp, flag it so we never report an invalid backref, - * and reset its members to parse the entire regexp. This is obviously - * suboptimal, but GetDecimalValue calls us only if a backref appears to - * refer to a forward parenthetical, which is rare. - */ - temp = *state; - temp.flags |= JSREG_FIND_PAREN_COUNT; - temp.cp = temp.cpbegin; - temp.parenCount = 0; - temp.classCount = 0; - temp.progLength = 0; - temp.treeDepth = 0; - temp.classBitmapsMem = 0; - for (i = 0; i < CLASS_CACHE_SIZE; i++) - temp.classCache[i].start = NULL; - - if (!ParseRegExp(&temp)) { - state->flags |= JSREG_FIND_PAREN_ERROR; - return OVERFLOW_VALUE; - } - return temp.parenCount; -} - -/* - * Extract and return a decimal value at state->cp. The initial character c - * has already been read. Return OVERFLOW_VALUE if the result exceeds max. - * Callers who pass a non-null findMax should test JSREG_FIND_PAREN_ERROR in - * state->flags to discover whether an error occurred under findMax. - */ -static uintN -GetDecimalValue(jschar c, uintN max, uintN (*findMax)(CompilerState *state), - CompilerState *state) -{ - uintN value = JS7_UNDEC(c); - JSBool overflow = (value > max && (!findMax || value > findMax(state))); - - /* The following restriction allows simpler overflow checks. */ - JS_ASSERT(max <= ((uintN)-1 - 9) / 10); - while (state->cp < state->cpend) { - c = *state->cp; - if (!JS7_ISDEC(c)) - break; - value = 10 * value + JS7_UNDEC(c); - if (!overflow && value > max && (!findMax || value > findMax(state))) - overflow = JS_TRUE; - ++state->cp; - } - return overflow ? OVERFLOW_VALUE : value; -} - -/* - * Calculate the total size of the bitmap required for a class expression. - */ -static JSBool -CalculateBitmapSize(CompilerState *state, RENode *target, const jschar *src, - const jschar *end) -{ - uintN max = 0; - JSBool inRange = JS_FALSE; - jschar c, rangeStart = 0; - uintN n, digit, nDigits, i; - - target->u.ucclass.bmsize = 0; - target->u.ucclass.sense = JS_TRUE; - - if (src == end) - return JS_TRUE; - - if (*src == '^') { - ++src; - target->u.ucclass.sense = JS_FALSE; - } - - while (src != end) { - uintN localMax = 0; - switch (*src) { - case '\\': - ++src; - c = *src++; - switch (c) { - case 'b': - localMax = 0x8; - break; - case 'f': - localMax = 0xC; - break; - case 'n': - localMax = 0xA; - break; - case 'r': - localMax = 0xD; - break; - case 't': - localMax = 0x9; - break; - case 'v': - localMax = 0xB; - break; - case 'c': - if (src < end && RE_IS_LETTER(*src)) { - localMax = (jschar) (*src++ & 0x1F); - } else { - --src; - localMax = '\\'; - } - break; - case 'x': - nDigits = 2; - goto lexHex; - case 'u': - nDigits = 4; -lexHex: - n = 0; - for (i = 0; (i < nDigits) && (src < end); i++) { - c = *src++; - if (!isASCIIHexDigit(c, &digit)) { - /* - * Back off to accepting the original - *'\' as a literal. - */ - src -= i + 1; - n = '\\'; - break; - } - n = (n << 4) | digit; - } - localMax = n; - break; - case 'd': - if (inRange) { - JS_ReportErrorNumber(state->context, - js_GetErrorMessage, NULL, - JSMSG_BAD_CLASS_RANGE); - return JS_FALSE; - } - localMax = '9'; - break; - case 'D': - case 's': - case 'S': - case 'w': - case 'W': - if (inRange) { - JS_ReportErrorNumber(state->context, - js_GetErrorMessage, NULL, - JSMSG_BAD_CLASS_RANGE); - return JS_FALSE; - } - target->u.ucclass.bmsize = 65535; - return JS_TRUE; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - /* - * This is a non-ECMA extension - decimal escapes (in this - * case, octal!) are supposed to be an error inside class - * ranges, but supported here for backwards compatibility. - * - */ - n = JS7_UNDEC(c); - c = *src; - if ('0' <= c && c <= '7') { - src++; - n = 8 * n + JS7_UNDEC(c); - c = *src; - if ('0' <= c && c <= '7') { - src++; - i = 8 * n + JS7_UNDEC(c); - if (i <= 0377) - n = i; - else - src--; - } - } - localMax = n; - break; - - default: - localMax = c; - break; - } - break; - default: - localMax = *src++; - break; - } - if (state->flags & JSREG_FOLD) { - c = JS_MAX(upcase((jschar) localMax), downcase((jschar) localMax)); - if (c > localMax) - localMax = c; - } - if (inRange) { - if (rangeStart > localMax) { - JS_ReportErrorNumber(state->context, - js_GetErrorMessage, NULL, - JSMSG_BAD_CLASS_RANGE); - return JS_FALSE; - } - inRange = JS_FALSE; - } else { - if (src < end - 1) { - if (*src == '-') { - ++src; - inRange = JS_TRUE; - rangeStart = (jschar)localMax; - continue; - } - } - } - if (localMax > max) - max = localMax; - } - target->u.ucclass.bmsize = max; - return JS_TRUE; -} - -/* - * item: assertion An item is either an assertion or - * quantatom a quantified atom. - * - * assertion: '^' Assertions match beginning of string - * (or line if the class static property - * RegExp.multiline is true). - * '$' End of string (or line if the class - * static property RegExp.multiline is - * true). - * '\b' Word boundary (between \w and \W). - * '\B' Word non-boundary. - * - * quantatom: atom An unquantified atom. - * quantatom '{' n ',' m '}' - * Atom must occur between n and m times. - * quantatom '{' n ',' '}' Atom must occur at least n times. - * quantatom '{' n '}' Atom must occur exactly n times. - * quantatom '*' Zero or more times (same as {0,}). - * quantatom '+' One or more times (same as {1,}). - * quantatom '?' Zero or one time (same as {0,1}). - * - * any of which can be optionally followed by '?' for ungreedy - * - * atom: '(' regexp ')' A parenthesized regexp (what matched - * can be addressed using a backreference, - * see '\' n below). - * '.' Matches any char except '\n'. - * '[' classlist ']' A character class. - * '[' '^' classlist ']' A negated character class. - * '\f' Form Feed. - * '\n' Newline (Line Feed). - * '\r' Carriage Return. - * '\t' Horizontal Tab. - * '\v' Vertical Tab. - * '\d' A digit (same as [0-9]). - * '\D' A non-digit. - * '\w' A word character, [0-9a-z_A-Z]. - * '\W' A non-word character. - * '\s' A whitespace character, [ \b\f\n\r\t\v]. - * '\S' A non-whitespace character. - * '\' n A backreference to the nth (n decimal - * and positive) parenthesized expression. - * '\' octal An octal escape sequence (octal must be - * two or three digits long, unless it is - * 0 for the null character). - * '\x' hex A hex escape (hex must be two digits). - * '\u' unicode A unicode escape (must be four digits). - * '\c' ctrl A control character, ctrl is a letter. - * '\' literalatomchar Any character except one of the above - * that follow '\' in an atom. - * otheratomchar Any character not first among the other - * atom right-hand sides. - */ -static JSBool -ParseTerm(CompilerState *state) -{ - jschar c = *state->cp++; - uintN nDigits; - uintN num, tmp, n, i; - const jschar *termStart; - - switch (c) { - /* assertions and atoms */ - case '^': - state->result = NewRENode(state, REOP_BOL); - if (!state->result) - return JS_FALSE; - state->progLength++; - return JS_TRUE; - case '$': - state->result = NewRENode(state, REOP_EOL); - if (!state->result) - return JS_FALSE; - state->progLength++; - return JS_TRUE; - case '\\': - if (state->cp >= state->cpend) { - /* a trailing '\' is an error */ - js_ReportCompileErrorNumber(state->context, state->tokenStream, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_TRAILING_SLASH); - return JS_FALSE; - } - c = *state->cp++; - switch (c) { - /* assertion escapes */ - case 'b' : - state->result = NewRENode(state, REOP_WBDRY); - if (!state->result) - return JS_FALSE; - state->progLength++; - return JS_TRUE; - case 'B': - state->result = NewRENode(state, REOP_WNONBDRY); - if (!state->result) - return JS_FALSE; - state->progLength++; - return JS_TRUE; - /* Decimal escape */ - case '0': - /* Give a strict warning. See also the note below. */ - if (!js_ReportCompileErrorNumber(state->context, - state->tokenStream, - JSREPORT_TS | - JSREPORT_WARNING | - JSREPORT_STRICT, - JSMSG_INVALID_BACKREF)) { - return JS_FALSE; - } - doOctal: - num = 0; - while (state->cp < state->cpend) { - c = *state->cp; - if (c < '0' || '7' < c) - break; - state->cp++; - tmp = 8 * num + (uintN)JS7_UNDEC(c); - if (tmp > 0377) - break; - num = tmp; - } - c = (jschar)num; - doFlat: - state->result = NewRENode(state, REOP_FLAT); - if (!state->result) - return JS_FALSE; - state->result->u.flat.chr = c; - state->result->u.flat.length = 1; - state->progLength += 3; - break; - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - termStart = state->cp - 1; - num = GetDecimalValue(c, state->parenCount, FindParenCount, state); - if (state->flags & JSREG_FIND_PAREN_ERROR) - return JS_FALSE; - if (num == OVERFLOW_VALUE) { - /* Give a strict mode warning. */ - if (!js_ReportCompileErrorNumber(state->context, - state->tokenStream, - JSREPORT_TS | - JSREPORT_WARNING | - JSREPORT_STRICT, - (c >= '8') - ? JSMSG_INVALID_BACKREF - : JSMSG_BAD_BACKREF)) { - return JS_FALSE; - } - - /* - * Note: ECMA 262, 15.10.2.9 says that we should throw a syntax - * error here. However, for compatibility with IE, we treat the - * whole backref as flat if the first character in it is not a - * valid octal character, and as an octal escape otherwise. - */ - state->cp = termStart; - if (c >= '8') { - /* Treat this as flat. termStart - 1 is the \. */ - c = '\\'; - goto asFlat; - } - - /* Treat this as an octal escape. */ - goto doOctal; - } - JS_ASSERT(1 <= num && num <= 0x10000); - state->result = NewRENode(state, REOP_BACKREF); - if (!state->result) - return JS_FALSE; - state->result->u.parenIndex = num - 1; - state->progLength - += 1 + GetCompactIndexWidth(state->result->u.parenIndex); - break; - /* Control escape */ - case 'f': - c = 0xC; - goto doFlat; - case 'n': - c = 0xA; - goto doFlat; - case 'r': - c = 0xD; - goto doFlat; - case 't': - c = 0x9; - goto doFlat; - case 'v': - c = 0xB; - goto doFlat; - /* Control letter */ - case 'c': - if (state->cp < state->cpend && RE_IS_LETTER(*state->cp)) { - c = (jschar) (*state->cp++ & 0x1F); - } else { - /* back off to accepting the original '\' as a literal */ - --state->cp; - c = '\\'; - } - goto doFlat; - /* HexEscapeSequence */ - case 'x': - nDigits = 2; - goto lexHex; - /* UnicodeEscapeSequence */ - case 'u': - nDigits = 4; -lexHex: - n = 0; - for (i = 0; i < nDigits && state->cp < state->cpend; i++) { - uintN digit; - c = *state->cp++; - if (!isASCIIHexDigit(c, &digit)) { - /* - * Back off to accepting the original 'u' or 'x' as a - * literal. - */ - state->cp -= i + 2; - n = *state->cp++; - break; - } - n = (n << 4) | digit; - } - c = (jschar) n; - goto doFlat; - /* Character class escapes */ - case 'd': - state->result = NewRENode(state, REOP_DIGIT); -doSimple: - if (!state->result) - return JS_FALSE; - state->progLength++; - break; - case 'D': - state->result = NewRENode(state, REOP_NONDIGIT); - goto doSimple; - case 's': - state->result = NewRENode(state, REOP_SPACE); - goto doSimple; - case 'S': - state->result = NewRENode(state, REOP_NONSPACE); - goto doSimple; - case 'w': - state->result = NewRENode(state, REOP_ALNUM); - goto doSimple; - case 'W': - state->result = NewRENode(state, REOP_NONALNUM); - goto doSimple; - /* IdentityEscape */ - default: - state->result = NewRENode(state, REOP_FLAT); - if (!state->result) - return JS_FALSE; - state->result->u.flat.chr = c; - state->result->u.flat.length = 1; - state->result->kid = (void *) (state->cp - 1); - state->progLength += 3; - break; - } - break; - case '[': - state->result = NewRENode(state, REOP_CLASS); - if (!state->result) - return JS_FALSE; - termStart = state->cp; - state->result->u.ucclass.startIndex = termStart - state->cpbegin; - for (;;) { - if (state->cp == state->cpend) { - js_ReportCompileErrorNumberUC(state->context, state->tokenStream, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_UNTERM_CLASS, termStart); - - return JS_FALSE; - } - if (*state->cp == '\\') { - state->cp++; - if (state->cp != state->cpend) - state->cp++; - continue; - } - if (*state->cp == ']') { - state->result->u.ucclass.kidlen = state->cp - termStart; - break; - } - state->cp++; - } - for (i = 0; i < CLASS_CACHE_SIZE; i++) { - if (!state->classCache[i].start) { - state->classCache[i].start = termStart; - state->classCache[i].length = state->result->u.ucclass.kidlen; - state->classCache[i].index = state->classCount; - break; - } - if (state->classCache[i].length == - state->result->u.ucclass.kidlen) { - for (n = 0; ; n++) { - if (n == state->classCache[i].length) { - state->result->u.ucclass.index - = state->classCache[i].index; - goto claim; - } - if (state->classCache[i].start[n] != termStart[n]) - break; - } - } - } - state->result->u.ucclass.index = state->classCount++; - - claim: - /* - * Call CalculateBitmapSize now as we want any errors it finds - * to be reported during the parse phase, not at execution. - */ - if (!CalculateBitmapSize(state, state->result, termStart, state->cp++)) - return JS_FALSE; - /* - * Update classBitmapsMem with number of bytes to hold bmsize bits, - * which is (bitsCount + 7) / 8 or (highest_bit + 1 + 7) / 8 - * or highest_bit / 8 + 1 where highest_bit is u.ucclass.bmsize. - */ - n = (state->result->u.ucclass.bmsize >> 3) + 1; - if (n > CLASS_BITMAPS_MEM_LIMIT - state->classBitmapsMem) { - js_ReportCompileErrorNumber(state->context, state->tokenStream, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_REGEXP_TOO_COMPLEX); - return JS_FALSE; - } - state->classBitmapsMem += n; - /* CLASS, */ - state->progLength - += 1 + GetCompactIndexWidth(state->result->u.ucclass.index); - break; - - case '.': - state->result = NewRENode(state, REOP_DOT); - goto doSimple; - - case '{': - { - const jschar *errp = state->cp--; - intN err; - - err = ParseMinMaxQuantifier(state, JS_TRUE); - state->cp = errp; - - if (err < 0) - goto asFlat; - - /* FALL THROUGH */ - } - case '*': - case '+': - case '?': - js_ReportCompileErrorNumberUC(state->context, state->tokenStream, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_QUANTIFIER, state->cp - 1); - return JS_FALSE; - default: -asFlat: - state->result = NewRENode(state, REOP_FLAT); - if (!state->result) - return JS_FALSE; - state->result->u.flat.chr = c; - state->result->u.flat.length = 1; - state->result->kid = (void *) (state->cp - 1); - state->progLength += 3; - break; - } - return ParseQuantifier(state); -} - -static JSBool -ParseQuantifier(CompilerState *state) -{ - RENode *term; - term = state->result; - if (state->cp < state->cpend) { - switch (*state->cp) { - case '+': - state->result = NewRENode(state, REOP_QUANT); - if (!state->result) - return JS_FALSE; - state->result->u.range.min = 1; - state->result->u.range.max = (uintN)-1; - /* , ... */ - state->progLength += 4; - goto quantifier; - case '*': - state->result = NewRENode(state, REOP_QUANT); - if (!state->result) - return JS_FALSE; - state->result->u.range.min = 0; - state->result->u.range.max = (uintN)-1; - /* , ... */ - state->progLength += 4; - goto quantifier; - case '?': - state->result = NewRENode(state, REOP_QUANT); - if (!state->result) - return JS_FALSE; - state->result->u.range.min = 0; - state->result->u.range.max = 1; - /* , ... */ - state->progLength += 4; - goto quantifier; - case '{': /* balance '}' */ - { - intN err; - const jschar *errp = state->cp; - - err = ParseMinMaxQuantifier(state, JS_FALSE); - if (err == 0) - goto quantifier; - if (err == -1) - return JS_TRUE; - - js_ReportCompileErrorNumberUC(state->context, - state->tokenStream, - JSREPORT_TS | JSREPORT_ERROR, - err, errp); - return JS_FALSE; - } - default:; - } - } - return JS_TRUE; - -quantifier: - if (state->treeDepth == TREE_DEPTH_MAX) { - js_ReportCompileErrorNumber(state->context, state->tokenStream, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_REGEXP_TOO_COMPLEX); - return JS_FALSE; - } - - ++state->treeDepth; - ++state->cp; - state->result->kid = term; - if (state->cp < state->cpend && *state->cp == '?') { - ++state->cp; - state->result->u.range.greedy = JS_FALSE; - } else { - state->result->u.range.greedy = JS_TRUE; - } - return JS_TRUE; -} - -static intN -ParseMinMaxQuantifier(CompilerState *state, JSBool ignoreValues) -{ - uintN min, max; - jschar c; - const jschar *errp = state->cp++; - - c = *state->cp; - if (JS7_ISDEC(c)) { - ++state->cp; - min = GetDecimalValue(c, 0xFFFF, NULL, state); - c = *state->cp; - - if (!ignoreValues && min == OVERFLOW_VALUE) - return JSMSG_MIN_TOO_BIG; - - if (c == ',') { - c = *++state->cp; - if (JS7_ISDEC(c)) { - ++state->cp; - max = GetDecimalValue(c, 0xFFFF, NULL, state); - c = *state->cp; - if (!ignoreValues && max == OVERFLOW_VALUE) - return JSMSG_MAX_TOO_BIG; - if (!ignoreValues && min > max) - return JSMSG_OUT_OF_ORDER; - } else { - max = (uintN)-1; - } - } else { - max = min; - } - if (c == '}') { - state->result = NewRENode(state, REOP_QUANT); - if (!state->result) - return JS_FALSE; - state->result->u.range.min = min; - state->result->u.range.max = max; - /* - * QUANT, , , ... - * where is written as compact(max+1) to make - * (uintN)-1 sentinel to occupy 1 byte, not width_of(max)+1. - */ - state->progLength += (1 + GetCompactIndexWidth(min) - + GetCompactIndexWidth(max + 1) - +3); - return 0; - } - } - - state->cp = errp; - return -1; -} - -static JSBool -SetForwardJumpOffset(jsbytecode *jump, jsbytecode *target) -{ - ptrdiff_t offset = target - jump; - - /* Check that target really points forward. */ - JS_ASSERT(offset >= 2); - if ((size_t)offset > OFFSET_MAX) - return JS_FALSE; - - jump[0] = JUMP_OFFSET_HI(offset); - jump[1] = JUMP_OFFSET_LO(offset); - return JS_TRUE; -} - -/* - * Generate bytecode for the tree rooted at t using an explicit stack instead - * of recursion. - */ -static jsbytecode * -EmitREBytecode(CompilerState *state, JSRegExp *re, size_t treeDepth, - jsbytecode *pc, RENode *t) -{ - EmitStateStackEntry *emitStateSP, *emitStateStack; - RECharSet *charSet; - REOp op; - - if (treeDepth == 0) { - emitStateStack = NULL; - } else { - emitStateStack = - (EmitStateStackEntry *)JS_malloc(state->context, - sizeof(EmitStateStackEntry) * - treeDepth); - if (!emitStateStack) - return NULL; - } - emitStateSP = emitStateStack; - op = t->op; - - for (;;) { - *pc++ = op; - switch (op) { - case REOP_EMPTY: - --pc; - break; - - case REOP_ALTPREREQ2: - case REOP_ALTPREREQ: - JS_ASSERT(emitStateSP); - emitStateSP->altHead = pc - 1; - emitStateSP->endTermFixup = pc; - pc += OFFSET_LEN; - SET_ARG(pc, t->u.altprereq.ch1); - pc += ARG_LEN; - SET_ARG(pc, t->u.altprereq.ch2); - pc += ARG_LEN; - - emitStateSP->nextAltFixup = pc; /* offset to next alternate */ - pc += OFFSET_LEN; - - emitStateSP->continueNode = t; - emitStateSP->continueOp = REOP_JUMP; - emitStateSP->jumpToJumpFlag = JS_FALSE; - ++emitStateSP; - JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); - t = (RENode *) t->kid; - op = t->op; - continue; - - case REOP_JUMP: - emitStateSP->nextTermFixup = pc; /* offset to following term */ - pc += OFFSET_LEN; - if (!SetForwardJumpOffset(emitStateSP->nextAltFixup, pc)) - goto jump_too_big; - emitStateSP->continueOp = REOP_ENDALT; - ++emitStateSP; - JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); - t = t->u.kid2; - op = t->op; - continue; - - case REOP_ENDALT: - /* - * If we already patched emitStateSP->nextTermFixup to jump to - * a nearer jump, to avoid 16-bit immediate offset overflow, we - * are done here. - */ - if (emitStateSP->jumpToJumpFlag) - break; - - /* - * Fix up the REOP_JUMP offset to go to the op after REOP_ENDALT. - * REOP_ENDALT is executed only on successful match of the last - * alternate in a group. - */ - if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc)) - goto jump_too_big; - if (t->op != REOP_ALT) { - if (!SetForwardJumpOffset(emitStateSP->endTermFixup, pc)) - goto jump_too_big; - } - - /* - * If the program is bigger than the REOP_JUMP offset range, then - * we must check for alternates before this one that are part of - * the same group, and fix up their jump offsets to target jumps - * close enough to fit in a 16-bit unsigned offset immediate. - */ - if ((size_t)(pc - re->program) > OFFSET_MAX && - emitStateSP > emitStateStack) { - EmitStateStackEntry *esp, *esp2; - jsbytecode *alt, *jump; - ptrdiff_t span, header; - - esp2 = emitStateSP; - alt = esp2->altHead; - for (esp = esp2 - 1; esp >= emitStateStack; --esp) { - if (esp->continueOp == REOP_ENDALT && - !esp->jumpToJumpFlag && - esp->nextTermFixup + OFFSET_LEN == alt && - (size_t)(pc - ((esp->continueNode->op != REOP_ALT) - ? esp->endTermFixup - : esp->nextTermFixup)) > OFFSET_MAX) { - alt = esp->altHead; - jump = esp->nextTermFixup; - - /* - * The span must be 1 less than the distance from - * jump offset to jump offset, so we actually jump - * to a REOP_JUMP bytecode, not to its offset! - */ - for (;;) { - JS_ASSERT(jump < esp2->nextTermFixup); - span = esp2->nextTermFixup - jump - 1; - if ((size_t)span <= OFFSET_MAX) - break; - do { - if (--esp2 == esp) - goto jump_too_big; - } while (esp2->continueOp != REOP_ENDALT); - } - - jump[0] = JUMP_OFFSET_HI(span); - jump[1] = JUMP_OFFSET_LO(span); - - if (esp->continueNode->op != REOP_ALT) { - /* - * We must patch the offset at esp->endTermFixup - * as well, for the REOP_ALTPREREQ{,2} opcodes. - * If we're unlucky and endTermFixup is more than - * OFFSET_MAX bytes from its target, we cheat by - * jumping 6 bytes to the jump whose offset is at - * esp->nextTermFixup, which has the same target. - */ - jump = esp->endTermFixup; - header = esp->nextTermFixup - jump; - span += header; - if ((size_t)span > OFFSET_MAX) - span = header; - - jump[0] = JUMP_OFFSET_HI(span); - jump[1] = JUMP_OFFSET_LO(span); - } - - esp->jumpToJumpFlag = JS_TRUE; - } - } - } - break; - - case REOP_ALT: - JS_ASSERT(emitStateSP); - emitStateSP->altHead = pc - 1; - emitStateSP->nextAltFixup = pc; /* offset to next alternate */ - pc += OFFSET_LEN; - emitStateSP->continueNode = t; - emitStateSP->continueOp = REOP_JUMP; - emitStateSP->jumpToJumpFlag = JS_FALSE; - ++emitStateSP; - JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); - t = t->kid; - op = t->op; - continue; - - case REOP_FLAT: - /* - * Coalesce FLATs if possible and if it would not increase bytecode - * beyond preallocated limit. The latter happens only when bytecode - * size for coalesced string with offset p and length 2 exceeds 6 - * bytes preallocated for 2 single char nodes, i.e. when - * 1 + GetCompactIndexWidth(p) + GetCompactIndexWidth(2) > 6 or - * GetCompactIndexWidth(p) > 4. - * Since when GetCompactIndexWidth(p) <= 4 coalescing of 3 or more - * nodes strictly decreases bytecode size, the check has to be - * done only for the first coalescing. - */ - if (t->kid && - GetCompactIndexWidth((jschar *)t->kid - state->cpbegin) <= 4) - { - while (t->next && - t->next->op == REOP_FLAT && - (jschar*)t->kid + t->u.flat.length == - (jschar*)t->next->kid) { - t->u.flat.length += t->next->u.flat.length; - t->next = t->next->next; - } - } - if (t->kid && t->u.flat.length > 1) { - pc[-1] = (state->flags & JSREG_FOLD) ? REOP_FLATi : REOP_FLAT; - pc = WriteCompactIndex(pc, (jschar *)t->kid - state->cpbegin); - pc = WriteCompactIndex(pc, t->u.flat.length); - } else if (t->u.flat.chr < 256) { - pc[-1] = (state->flags & JSREG_FOLD) ? REOP_FLAT1i : REOP_FLAT1; - *pc++ = (jsbytecode) t->u.flat.chr; - } else { - pc[-1] = (state->flags & JSREG_FOLD) - ? REOP_UCFLAT1i - : REOP_UCFLAT1; - SET_ARG(pc, t->u.flat.chr); - pc += ARG_LEN; - } - break; - - case REOP_LPAREN: - JS_ASSERT(emitStateSP); - pc = WriteCompactIndex(pc, t->u.parenIndex); - emitStateSP->continueNode = t; - emitStateSP->continueOp = REOP_RPAREN; - ++emitStateSP; - JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); - t = (RENode *) t->kid; - op = t->op; - continue; - - case REOP_RPAREN: - pc = WriteCompactIndex(pc, t->u.parenIndex); - break; - - case REOP_BACKREF: - pc = WriteCompactIndex(pc, t->u.parenIndex); - break; - - case REOP_ASSERT: - JS_ASSERT(emitStateSP); - emitStateSP->nextTermFixup = pc; - pc += OFFSET_LEN; - emitStateSP->continueNode = t; - emitStateSP->continueOp = REOP_ASSERTTEST; - ++emitStateSP; - JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); - t = (RENode *) t->kid; - op = t->op; - continue; - - case REOP_ASSERTTEST: - case REOP_ASSERTNOTTEST: - if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc)) - goto jump_too_big; - break; - - case REOP_ASSERT_NOT: - JS_ASSERT(emitStateSP); - emitStateSP->nextTermFixup = pc; - pc += OFFSET_LEN; - emitStateSP->continueNode = t; - emitStateSP->continueOp = REOP_ASSERTNOTTEST; - ++emitStateSP; - JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); - t = (RENode *) t->kid; - op = t->op; - continue; - - case REOP_QUANT: - JS_ASSERT(emitStateSP); - if (t->u.range.min == 0 && t->u.range.max == (uintN)-1) { - pc[-1] = (t->u.range.greedy) ? REOP_STAR : REOP_MINIMALSTAR; - } else if (t->u.range.min == 0 && t->u.range.max == 1) { - pc[-1] = (t->u.range.greedy) ? REOP_OPT : REOP_MINIMALOPT; - } else if (t->u.range.min == 1 && t->u.range.max == (uintN) -1) { - pc[-1] = (t->u.range.greedy) ? REOP_PLUS : REOP_MINIMALPLUS; - } else { - if (!t->u.range.greedy) - pc[-1] = REOP_MINIMALQUANT; - pc = WriteCompactIndex(pc, t->u.range.min); - /* - * Write max + 1 to avoid using size_t(max) + 1 bytes - * for (uintN)-1 sentinel. - */ - pc = WriteCompactIndex(pc, t->u.range.max + 1); - } - emitStateSP->nextTermFixup = pc; - pc += OFFSET_LEN; - emitStateSP->continueNode = t; - emitStateSP->continueOp = REOP_ENDCHILD; - ++emitStateSP; - JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); - t = (RENode *) t->kid; - op = t->op; - continue; - - case REOP_ENDCHILD: - if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc)) - goto jump_too_big; - break; - - case REOP_CLASS: - if (!t->u.ucclass.sense) - pc[-1] = REOP_NCLASS; - pc = WriteCompactIndex(pc, t->u.ucclass.index); - charSet = &re->classList[t->u.ucclass.index]; - charSet->converted = JS_FALSE; - charSet->length = t->u.ucclass.bmsize; - charSet->u.src.startIndex = t->u.ucclass.startIndex; - charSet->u.src.length = t->u.ucclass.kidlen; - charSet->sense = t->u.ucclass.sense; - break; - - default: - break; - } - - t = t->next; - if (t) { - op = t->op; - } else { - if (emitStateSP == emitStateStack) - break; - --emitStateSP; - t = emitStateSP->continueNode; - op = emitStateSP->continueOp; - } - } - - cleanup: - if (emitStateStack) - JS_free(state->context, emitStateStack); - return pc; - - jump_too_big: - js_ReportCompileErrorNumber(state->context, state->tokenStream, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_REGEXP_TOO_COMPLEX); - pc = NULL; - goto cleanup; -} - - -JSRegExp * -js_NewRegExp(JSContext *cx, JSTokenStream *ts, - JSString *str, uintN flags, JSBool flat) -{ - JSRegExp *re; - void *mark; - CompilerState state; - size_t resize; - jsbytecode *endPC; - uintN i; - size_t len; - - re = NULL; - mark = JS_ARENA_MARK(&cx->tempPool); - len = JSSTRING_LENGTH(str); - - state.context = cx; - state.tokenStream = ts; - state.cp = js_UndependString(cx, str); - if (!state.cp) - goto out; - state.cpbegin = state.cp; - state.cpend = state.cp + len; - state.flags = flags; - state.parenCount = 0; - state.classCount = 0; - state.progLength = 0; - state.treeDepth = 0; - state.classBitmapsMem = 0; - for (i = 0; i < CLASS_CACHE_SIZE; i++) - state.classCache[i].start = NULL; - - if (len != 0 && flat) { - state.result = NewRENode(&state, REOP_FLAT); - state.result->u.flat.chr = *state.cpbegin; - state.result->u.flat.length = len; - state.result->kid = (void *) state.cpbegin; - /* Flat bytecode: REOP_FLAT compact(string_offset) compact(len). */ - state.progLength += 1 + GetCompactIndexWidth(0) - + GetCompactIndexWidth(len); - } else { - if (!ParseRegExp(&state)) - goto out; - } - resize = offsetof(JSRegExp, program) + state.progLength + 1; - re = (JSRegExp *) JS_malloc(cx, resize); - if (!re) - goto out; - - re->nrefs = 1; - JS_ASSERT(state.classBitmapsMem <= CLASS_BITMAPS_MEM_LIMIT); - re->classCount = state.classCount; - if (re->classCount) { - re->classList = (RECharSet *) - JS_malloc(cx, re->classCount * sizeof(RECharSet)); - if (!re->classList) { - js_DestroyRegExp(cx, re); - re = NULL; - goto out; - } - for (i = 0; i < re->classCount; i++) - re->classList[i].converted = JS_FALSE; - } else { - re->classList = NULL; - } - endPC = EmitREBytecode(&state, re, state.treeDepth, re->program, state.result); - if (!endPC) { - js_DestroyRegExp(cx, re); - re = NULL; - goto out; - } - *endPC++ = REOP_END; - /* - * Check whether size was overestimated and shrink using realloc. - * This is safe since no pointers to newly parsed regexp or its parts - * besides re exist here. - */ - if ((size_t)(endPC - re->program) != state.progLength + 1) { - JSRegExp *tmp; - JS_ASSERT((size_t)(endPC - re->program) < state.progLength + 1); - resize = offsetof(JSRegExp, program) + (endPC - re->program); - tmp = (JSRegExp *) JS_realloc(cx, re, resize); - if (tmp) - re = tmp; - } - - re->flags = flags; - re->cloneIndex = 0; - re->parenCount = state.parenCount; - re->source = str; - -out: - JS_ARENA_RELEASE(&cx->tempPool, mark); - return re; -} - -JSRegExp * -js_NewRegExpOpt(JSContext *cx, JSTokenStream *ts, - JSString *str, JSString *opt, JSBool flat) -{ - uintN flags; - jschar *s; - size_t i, n; - char charBuf[2]; - - flags = 0; - if (opt) { - s = JSSTRING_CHARS(opt); - for (i = 0, n = JSSTRING_LENGTH(opt); i < n; i++) { - switch (s[i]) { - case 'g': - flags |= JSREG_GLOB; - break; - case 'i': - flags |= JSREG_FOLD; - break; - case 'm': - flags |= JSREG_MULTILINE; - break; - default: - charBuf[0] = (char)s[i]; - charBuf[1] = '\0'; - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_FLAG, charBuf); - return NULL; - } - } - } - return js_NewRegExp(cx, ts, str, flags, flat); -} - -/* - * Save the current state of the match - the position in the input - * text as well as the position in the bytecode. The state of any - * parent expressions is also saved (preceding state). - * Contents of parenCount parentheses from parenIndex are also saved. - */ -static REBackTrackData * -PushBackTrackState(REGlobalData *gData, REOp op, - jsbytecode *target, REMatchState *x, const jschar *cp, - size_t parenIndex, size_t parenCount) -{ - size_t i; - REBackTrackData *result = - (REBackTrackData *) ((char *)gData->backTrackSP + gData->cursz); - - size_t sz = sizeof(REBackTrackData) + - gData->stateStackTop * sizeof(REProgState) + - parenCount * sizeof(RECapture); - - ptrdiff_t btsize = gData->backTrackStackSize; - ptrdiff_t btincr = ((char *)result + sz) - - ((char *)gData->backTrackStack + btsize); - - if (btincr > 0) { - ptrdiff_t offset = (char *)result - (char *)gData->backTrackStack; - - btincr = JS_ROUNDUP(btincr, btsize); - JS_ARENA_GROW_CAST(gData->backTrackStack, REBackTrackData *, - &gData->pool, btsize, btincr); - if (!gData->backTrackStack) { - JS_ReportOutOfMemory(gData->cx); - gData->ok = JS_FALSE; - return NULL; - } - gData->backTrackStackSize = btsize + btincr; - result = (REBackTrackData *) ((char *)gData->backTrackStack + offset); - } - gData->backTrackSP = result; - result->sz = gData->cursz; - gData->cursz = sz; - - result->backtrack_op = op; - result->backtrack_pc = target; - result->cp = cp; - result->parenCount = parenCount; - - result->saveStateStackTop = gData->stateStackTop; - JS_ASSERT(gData->stateStackTop); - memcpy(result + 1, gData->stateStack, - sizeof(REProgState) * result->saveStateStackTop); - - if (parenCount != 0) { - result->parenIndex = parenIndex; - memcpy((char *)(result + 1) + - sizeof(REProgState) * result->saveStateStackTop, - &x->parens[parenIndex], - sizeof(RECapture) * parenCount); - for (i = 0; i != parenCount; i++) - x->parens[parenIndex + i].index = -1; - } - - return result; -} - - -/* - * Consecutive literal characters. - */ -#if 0 -static REMatchState * -FlatNMatcher(REGlobalData *gData, REMatchState *x, jschar *matchChars, - size_t length) -{ - size_t i; - if (length > gData->cpend - x->cp) - return NULL; - for (i = 0; i != length; i++) { - if (matchChars[i] != x->cp[i]) - return NULL; - } - x->cp += length; - return x; -} -#endif - -static REMatchState * -FlatNIMatcher(REGlobalData *gData, REMatchState *x, jschar *matchChars, - size_t length) -{ - size_t i; - JS_ASSERT(gData->cpend >= x->cp); - if (length > (size_t)(gData->cpend - x->cp)) - return NULL; - for (i = 0; i != length; i++) { - if (upcase(matchChars[i]) != upcase(x->cp[i])) - return NULL; - } - x->cp += length; - return x; -} - -/* - * 1. Evaluate DecimalEscape to obtain an EscapeValue E. - * 2. If E is not a character then go to step 6. - * 3. Let ch be E's character. - * 4. Let A be a one-element RECharSet containing the character ch. - * 5. Call CharacterSetMatcher(A, false) and return its Matcher result. - * 6. E must be an integer. Let n be that integer. - * 7. If n=0 or n>NCapturingParens then throw a SyntaxError exception. - * 8. Return an internal Matcher closure that takes two arguments, a State x - * and a Continuation c, and performs the following: - * 1. Let cap be x's captures internal array. - * 2. Let s be cap[n]. - * 3. If s is undefined, then call c(x) and return its result. - * 4. Let e be x's endIndex. - * 5. Let len be s's length. - * 6. Let f be e+len. - * 7. If f>InputLength, return failure. - * 8. If there exists an integer i between 0 (inclusive) and len (exclusive) - * such that Canonicalize(s[i]) is not the same character as - * Canonicalize(Input [e+i]), then return failure. - * 9. Let y be the State (f, cap). - * 10. Call c(y) and return its result. - */ -static REMatchState * -BackrefMatcher(REGlobalData *gData, REMatchState *x, size_t parenIndex) -{ - size_t len, i; - const jschar *parenContent; - RECapture *cap = &x->parens[parenIndex]; - - if (cap->index == -1) - return x; - - len = cap->length; - if (x->cp + len > gData->cpend) - return NULL; - - parenContent = &gData->cpbegin[cap->index]; - if (gData->regexp->flags & JSREG_FOLD) { - for (i = 0; i < len; i++) { - if (upcase(parenContent[i]) != upcase(x->cp[i])) - return NULL; - } - } else { - for (i = 0; i < len; i++) { - if (parenContent[i] != x->cp[i]) - return NULL; - } - } - x->cp += len; - return x; -} - - -/* Add a single character to the RECharSet */ -static void -AddCharacterToCharSet(RECharSet *cs, jschar c) -{ - uintN byteIndex = (uintN)(c >> 3); - JS_ASSERT(c <= cs->length); - cs->u.bits[byteIndex] |= 1 << (c & 0x7); -} - - -/* Add a character range, c1 to c2 (inclusive) to the RECharSet */ -static void -AddCharacterRangeToCharSet(RECharSet *cs, jschar c1, jschar c2) -{ - uintN i; - - uintN byteIndex1 = (uintN)(c1 >> 3); - uintN byteIndex2 = (uintN)(c2 >> 3); - - JS_ASSERT((c2 <= cs->length) && (c1 <= c2)); - - c1 &= 0x7; - c2 &= 0x7; - - if (byteIndex1 == byteIndex2) { - cs->u.bits[byteIndex1] |= ((uint8)0xFF >> (7 - (c2 - c1))) << c1; - } else { - cs->u.bits[byteIndex1] |= 0xFF << c1; - for (i = byteIndex1 + 1; i < byteIndex2; i++) - cs->u.bits[i] = 0xFF; - cs->u.bits[byteIndex2] |= (uint8)0xFF >> (7 - c2); - } -} - -/* Compile the source of the class into a RECharSet */ -static JSBool -ProcessCharSet(REGlobalData *gData, RECharSet *charSet) -{ - const jschar *src, *end; - JSBool inRange = JS_FALSE; - jschar rangeStart = 0; - uintN byteLength, n; - jschar c, thisCh; - intN nDigits, i; - - JS_ASSERT(!charSet->converted); - /* - * Assert that startIndex and length points to chars inside [] inside - * source string. - */ - JS_ASSERT(1 <= charSet->u.src.startIndex); - JS_ASSERT(charSet->u.src.startIndex - < JSSTRING_LENGTH(gData->regexp->source)); - JS_ASSERT(charSet->u.src.length <= JSSTRING_LENGTH(gData->regexp->source) - - 1 - charSet->u.src.startIndex); - - charSet->converted = JS_TRUE; - src = JSSTRING_CHARS(gData->regexp->source) + charSet->u.src.startIndex; - end = src + charSet->u.src.length; - JS_ASSERT(src[-1] == '['); - JS_ASSERT(end[0] == ']'); - - byteLength = (charSet->length >> 3) + 1; - charSet->u.bits = (uint8 *)JS_malloc(gData->cx, byteLength); - if (!charSet->u.bits) { - JS_ReportOutOfMemory(gData->cx); - gData->ok = JS_FALSE; - return JS_FALSE; - } - memset(charSet->u.bits, 0, byteLength); - - if (src == end) - return JS_TRUE; - - if (*src == '^') { - JS_ASSERT(charSet->sense == JS_FALSE); - ++src; - } else { - JS_ASSERT(charSet->sense == JS_TRUE); - } - - while (src != end) { - switch (*src) { - case '\\': - ++src; - c = *src++; - switch (c) { - case 'b': - thisCh = 0x8; - break; - case 'f': - thisCh = 0xC; - break; - case 'n': - thisCh = 0xA; - break; - case 'r': - thisCh = 0xD; - break; - case 't': - thisCh = 0x9; - break; - case 'v': - thisCh = 0xB; - break; - case 'c': - if (src < end && JS_ISWORD(*src)) { - thisCh = (jschar)(*src++ & 0x1F); - } else { - --src; - thisCh = '\\'; - } - break; - case 'x': - nDigits = 2; - goto lexHex; - case 'u': - nDigits = 4; - lexHex: - n = 0; - for (i = 0; (i < nDigits) && (src < end); i++) { - uintN digit; - c = *src++; - if (!isASCIIHexDigit(c, &digit)) { - /* - * Back off to accepting the original '\' - * as a literal - */ - src -= i + 1; - n = '\\'; - break; - } - n = (n << 4) | digit; - } - thisCh = (jschar)n; - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - /* - * This is a non-ECMA extension - decimal escapes (in this - * case, octal!) are supposed to be an error inside class - * ranges, but supported here for backwards compatibility. - */ - n = JS7_UNDEC(c); - c = *src; - if ('0' <= c && c <= '7') { - src++; - n = 8 * n + JS7_UNDEC(c); - c = *src; - if ('0' <= c && c <= '7') { - src++; - i = 8 * n + JS7_UNDEC(c); - if (i <= 0377) - n = i; - else - src--; - } - } - thisCh = (jschar)n; - break; - - case 'd': - AddCharacterRangeToCharSet(charSet, '0', '9'); - continue; /* don't need range processing */ - case 'D': - AddCharacterRangeToCharSet(charSet, 0, '0' - 1); - AddCharacterRangeToCharSet(charSet, - (jschar)('9' + 1), - (jschar)charSet->length); - continue; - case 's': - for (i = (intN)charSet->length; i >= 0; i--) - if (JS_ISSPACE(i)) - AddCharacterToCharSet(charSet, (jschar)i); - continue; - case 'S': - for (i = (intN)charSet->length; i >= 0; i--) - if (!JS_ISSPACE(i)) - AddCharacterToCharSet(charSet, (jschar)i); - continue; - case 'w': - for (i = (intN)charSet->length; i >= 0; i--) - if (JS_ISWORD(i)) - AddCharacterToCharSet(charSet, (jschar)i); - continue; - case 'W': - for (i = (intN)charSet->length; i >= 0; i--) - if (!JS_ISWORD(i)) - AddCharacterToCharSet(charSet, (jschar)i); - continue; - default: - thisCh = c; - break; - - } - break; - - default: - thisCh = *src++; - break; - - } - if (inRange) { - if (gData->regexp->flags & JSREG_FOLD) { - AddCharacterRangeToCharSet(charSet, upcase(rangeStart), - upcase(thisCh)); - AddCharacterRangeToCharSet(charSet, downcase(rangeStart), - downcase(thisCh)); - } else { - AddCharacterRangeToCharSet(charSet, rangeStart, thisCh); - } - inRange = JS_FALSE; - } else { - if (gData->regexp->flags & JSREG_FOLD) { - AddCharacterToCharSet(charSet, upcase(thisCh)); - AddCharacterToCharSet(charSet, downcase(thisCh)); - } else { - AddCharacterToCharSet(charSet, thisCh); - } - if (src < end - 1) { - if (*src == '-') { - ++src; - inRange = JS_TRUE; - rangeStart = thisCh; - } - } - } - } - return JS_TRUE; -} - -void -js_DestroyRegExp(JSContext *cx, JSRegExp *re) -{ - if (JS_ATOMIC_DECREMENT(&re->nrefs) == 0) { - if (re->classList) { - uintN i; - for (i = 0; i < re->classCount; i++) { - if (re->classList[i].converted) - JS_free(cx, re->classList[i].u.bits); - re->classList[i].u.bits = NULL; - } - JS_free(cx, re->classList); - } - JS_free(cx, re); - } -} - -static JSBool -ReallocStateStack(REGlobalData *gData) -{ - size_t limit = gData->stateStackLimit; - size_t sz = sizeof(REProgState) * limit; - - JS_ARENA_GROW_CAST(gData->stateStack, REProgState *, &gData->pool, sz, sz); - if (!gData->stateStack) { - gData->ok = JS_FALSE; - return JS_FALSE; - } - gData->stateStackLimit = limit + limit; - return JS_TRUE; -} - -#define PUSH_STATE_STACK(data) \ - JS_BEGIN_MACRO \ - ++(data)->stateStackTop; \ - if ((data)->stateStackTop == (data)->stateStackLimit && \ - !ReallocStateStack((data))) { \ - return NULL; \ - } \ - JS_END_MACRO - -/* - * Apply the current op against the given input to see if it's going to match - * or fail. Return false if we don't get a match, true if we do. If updatecp is - * true, then update the current state's cp. Always update startpc to the next - * op. - */ -static REMatchState * -SimpleMatch(REGlobalData *gData, REMatchState *x, REOp op, - jsbytecode **startpc, JSBool updatecp) -{ - REMatchState *result = NULL; - jschar matchCh; - size_t parenIndex; - size_t offset, length, index; - jsbytecode *pc = *startpc; /* pc has already been incremented past op */ - jschar *source; - const jschar *startcp = x->cp; - jschar ch; - RECharSet *charSet; - - switch (op) { - case REOP_BOL: - if (x->cp != gData->cpbegin) { - if (!gData->cx->regExpStatics.multiline && - !(gData->regexp->flags & JSREG_MULTILINE)) { - break; - } - if (!RE_IS_LINE_TERM(x->cp[-1])) - break; - } - result = x; - break; - case REOP_EOL: - if (x->cp != gData->cpend) { - if (!gData->cx->regExpStatics.multiline && - !(gData->regexp->flags & JSREG_MULTILINE)) { - break; - } - if (!RE_IS_LINE_TERM(*x->cp)) - break; - } - result = x; - break; - case REOP_WBDRY: - if ((x->cp == gData->cpbegin || !JS_ISWORD(x->cp[-1])) ^ - !(x->cp != gData->cpend && JS_ISWORD(*x->cp))) { - result = x; - } - break; - case REOP_WNONBDRY: - if ((x->cp == gData->cpbegin || !JS_ISWORD(x->cp[-1])) ^ - (x->cp != gData->cpend && JS_ISWORD(*x->cp))) { - result = x; - } - break; - case REOP_DOT: - if (x->cp != gData->cpend && !RE_IS_LINE_TERM(*x->cp)) { - result = x; - result->cp++; - } - break; - case REOP_DIGIT: - if (x->cp != gData->cpend && JS_ISDIGIT(*x->cp)) { - result = x; - result->cp++; - } - break; - case REOP_NONDIGIT: - if (x->cp != gData->cpend && !JS_ISDIGIT(*x->cp)) { - result = x; - result->cp++; - } - break; - case REOP_ALNUM: - if (x->cp != gData->cpend && JS_ISWORD(*x->cp)) { - result = x; - result->cp++; - } - break; - case REOP_NONALNUM: - if (x->cp != gData->cpend && !JS_ISWORD(*x->cp)) { - result = x; - result->cp++; - } - break; - case REOP_SPACE: - if (x->cp != gData->cpend && JS_ISSPACE(*x->cp)) { - result = x; - result->cp++; - } - break; - case REOP_NONSPACE: - if (x->cp != gData->cpend && !JS_ISSPACE(*x->cp)) { - result = x; - result->cp++; - } - break; - case REOP_BACKREF: - pc = ReadCompactIndex(pc, &parenIndex); - JS_ASSERT(parenIndex < gData->regexp->parenCount); - result = BackrefMatcher(gData, x, parenIndex); - break; - case REOP_FLAT: - pc = ReadCompactIndex(pc, &offset); - JS_ASSERT(offset < JSSTRING_LENGTH(gData->regexp->source)); - pc = ReadCompactIndex(pc, &length); - JS_ASSERT(1 <= length); - JS_ASSERT(length <= JSSTRING_LENGTH(gData->regexp->source) - offset); - if (length <= (size_t)(gData->cpend - x->cp)) { - source = JSSTRING_CHARS(gData->regexp->source) + offset; - for (index = 0; index != length; index++) { - if (source[index] != x->cp[index]) - return NULL; - } - x->cp += length; - result = x; - } - break; - case REOP_FLAT1: - matchCh = *pc++; - if (x->cp != gData->cpend && *x->cp == matchCh) { - result = x; - result->cp++; - } - break; - case REOP_FLATi: - pc = ReadCompactIndex(pc, &offset); - JS_ASSERT(offset < JSSTRING_LENGTH(gData->regexp->source)); - pc = ReadCompactIndex(pc, &length); - JS_ASSERT(1 <= length); - JS_ASSERT(length <= JSSTRING_LENGTH(gData->regexp->source) - offset); - source = JSSTRING_CHARS(gData->regexp->source); - result = FlatNIMatcher(gData, x, source + offset, length); - break; - case REOP_FLAT1i: - matchCh = *pc++; - if (x->cp != gData->cpend && upcase(*x->cp) == upcase(matchCh)) { - result = x; - result->cp++; - } - break; - case REOP_UCFLAT1: - matchCh = GET_ARG(pc); - pc += ARG_LEN; - if (x->cp != gData->cpend && *x->cp == matchCh) { - result = x; - result->cp++; - } - break; - case REOP_UCFLAT1i: - matchCh = GET_ARG(pc); - pc += ARG_LEN; - if (x->cp != gData->cpend && upcase(*x->cp) == upcase(matchCh)) { - result = x; - result->cp++; - } - break; - case REOP_CLASS: - pc = ReadCompactIndex(pc, &index); - JS_ASSERT(index < gData->regexp->classCount); - if (x->cp != gData->cpend) { - charSet = &gData->regexp->classList[index]; - JS_ASSERT(charSet->converted); - ch = *x->cp; - index = ch >> 3; - if (charSet->length != 0 && - ch <= charSet->length && - (charSet->u.bits[index] & (1 << (ch & 0x7)))) { - result = x; - result->cp++; - } - } - break; - case REOP_NCLASS: - pc = ReadCompactIndex(pc, &index); - JS_ASSERT(index < gData->regexp->classCount); - if (x->cp != gData->cpend) { - charSet = &gData->regexp->classList[index]; - JS_ASSERT(charSet->converted); - ch = *x->cp; - index = ch >> 3; - if (charSet->length == 0 || - ch > charSet->length || - !(charSet->u.bits[index] & (1 << (ch & 0x7)))) { - result = x; - result->cp++; - } - } - break; - default: - JS_ASSERT(JS_FALSE); - } - if (result) { - if (!updatecp) - x->cp = startcp; - *startpc = pc; - return result; - } - x->cp = startcp; - return NULL; -} - -static REMatchState * -ExecuteREBytecode(REGlobalData *gData, REMatchState *x) -{ - REMatchState *result = NULL; - REBackTrackData *backTrackData; - jsbytecode *nextpc, *testpc; - REOp nextop; - RECapture *cap; - REProgState *curState; - const jschar *startcp; - size_t parenIndex, k; - size_t parenSoFar = 0; - - jschar matchCh1, matchCh2; - RECharSet *charSet; - - JSBranchCallback onbranch = gData->cx->branchCallback; - uintN onbranchCalls = 0; -#define ONBRANCH_CALLS_MASK 127 -#define CHECK_BRANCH() \ - JS_BEGIN_MACRO \ - if (onbranch && \ - (++onbranchCalls & ONBRANCH_CALLS_MASK) == 0 && \ - !(*onbranch)(gData->cx, NULL)) { \ - gData->ok = JS_FALSE; \ - return NULL; \ - } \ - JS_END_MACRO - - JSBool anchor; - jsbytecode *pc = gData->regexp->program; - REOp op = (REOp) *pc++; - - /* - * If the first node is a simple match, step the index into the string - * until that match is made, or fail if it can't be found at all. - */ - if (REOP_IS_SIMPLE(op)) { - anchor = JS_FALSE; - while (x->cp <= gData->cpend) { - nextpc = pc; /* reset back to start each time */ - result = SimpleMatch(gData, x, op, &nextpc, JS_TRUE); - if (result) { - anchor = JS_TRUE; - x = result; - pc = nextpc; /* accept skip to next opcode */ - op = (REOp) *pc++; - break; - } - gData->skipped++; - x->cp++; - } - if (!anchor) - return NULL; - } - - for (;;) { - if (REOP_IS_SIMPLE(op)) { - result = SimpleMatch(gData, x, op, &pc, JS_TRUE); - } else { - curState = &gData->stateStack[gData->stateStackTop]; - switch (op) { - case REOP_EMPTY: - result = x; - break; - - case REOP_ALTPREREQ2: - nextpc = pc + GET_OFFSET(pc); /* start of next op */ - pc += ARG_LEN; - matchCh2 = GET_ARG(pc); - pc += ARG_LEN; - k = GET_ARG(pc); - pc += ARG_LEN; - - if (x->cp != gData->cpend) { - if (*x->cp == matchCh2) - goto doAlt; - - charSet = &gData->regexp->classList[k]; - if (!charSet->converted && !ProcessCharSet(gData, charSet)) - return NULL; - matchCh1 = *x->cp; - k = matchCh1 >> 3; - if ((charSet->length == 0 || - matchCh1 > charSet->length || - !(charSet->u.bits[k] & (1 << (matchCh1 & 0x7)))) ^ - charSet->sense) { - goto doAlt; - } - } - result = NULL; - break; - - case REOP_ALTPREREQ: - nextpc = pc + GET_OFFSET(pc); /* start of next op */ - pc += ARG_LEN; - matchCh1 = GET_ARG(pc); - pc += ARG_LEN; - matchCh2 = GET_ARG(pc); - pc += ARG_LEN; - if (x->cp == gData->cpend || - (*x->cp != matchCh1 && *x->cp != matchCh2)) { - result = NULL; - break; - } - /* else false thru... */ - - case REOP_ALT: - doAlt: - nextpc = pc + GET_OFFSET(pc); /* start of next alternate */ - pc += ARG_LEN; /* start of this alternate */ - curState->parenSoFar = parenSoFar; - PUSH_STATE_STACK(gData); - op = (REOp) *pc++; - startcp = x->cp; - if (REOP_IS_SIMPLE(op)) { - if (!SimpleMatch(gData, x, op, &pc, JS_TRUE)) { - op = (REOp) *nextpc++; - pc = nextpc; - continue; - } - result = x; - op = (REOp) *pc++; - } - nextop = (REOp) *nextpc++; - if (!PushBackTrackState(gData, nextop, nextpc, x, startcp, 0, 0)) - return NULL; - continue; - - /* - * Occurs at (successful) end of REOP_ALT, - */ - case REOP_JUMP: - --gData->stateStackTop; - pc += GET_OFFSET(pc); - op = (REOp) *pc++; - continue; - - /* - * Occurs at last (successful) end of REOP_ALT, - */ - case REOP_ENDALT: - --gData->stateStackTop; - op = (REOp) *pc++; - continue; - - case REOP_LPAREN: - pc = ReadCompactIndex(pc, &parenIndex); - JS_ASSERT(parenIndex < gData->regexp->parenCount); - if (parenIndex + 1 > parenSoFar) - parenSoFar = parenIndex + 1; - x->parens[parenIndex].index = x->cp - gData->cpbegin; - x->parens[parenIndex].length = 0; - op = (REOp) *pc++; - continue; - - case REOP_RPAREN: - pc = ReadCompactIndex(pc, &parenIndex); - JS_ASSERT(parenIndex < gData->regexp->parenCount); - cap = &x->parens[parenIndex]; - - /* - * FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=346090 - * This wallpaper prevents a case where we somehow took a step - * backward in input while minimally-matching an empty string. - */ - if (x->cp < gData->cpbegin + cap->index) - cap->index = -1; - cap->length = x->cp - (gData->cpbegin + cap->index); - op = (REOp) *pc++; - continue; - - case REOP_ASSERT: - nextpc = pc + GET_OFFSET(pc); /* start of term after ASSERT */ - pc += ARG_LEN; /* start of ASSERT child */ - op = (REOp) *pc++; - testpc = pc; - if (REOP_IS_SIMPLE(op) && - !SimpleMatch(gData, x, op, &testpc, JS_FALSE)) { - result = NULL; - break; - } - curState->u.assertion.top = - (char *)gData->backTrackSP - (char *)gData->backTrackStack; - curState->u.assertion.sz = gData->cursz; - curState->index = x->cp - gData->cpbegin; - curState->parenSoFar = parenSoFar; - PUSH_STATE_STACK(gData); - if (!PushBackTrackState(gData, REOP_ASSERTTEST, - nextpc, x, x->cp, 0, 0)) { - return NULL; - } - continue; - - case REOP_ASSERT_NOT: - nextpc = pc + GET_OFFSET(pc); - pc += ARG_LEN; - op = (REOp) *pc++; - testpc = pc; - if (REOP_IS_SIMPLE(op) /* Note - fail to fail! */ && - SimpleMatch(gData, x, op, &testpc, JS_FALSE) && - *testpc == REOP_ASSERTNOTTEST) { - result = NULL; - break; - } - curState->u.assertion.top - = (char *)gData->backTrackSP - - (char *)gData->backTrackStack; - curState->u.assertion.sz = gData->cursz; - curState->index = x->cp - gData->cpbegin; - curState->parenSoFar = parenSoFar; - PUSH_STATE_STACK(gData); - if (!PushBackTrackState(gData, REOP_ASSERTNOTTEST, - nextpc, x, x->cp, 0, 0)) { - return NULL; - } - continue; - - case REOP_ASSERTTEST: - --gData->stateStackTop; - --curState; - x->cp = gData->cpbegin + curState->index; - gData->backTrackSP = - (REBackTrackData *) ((char *)gData->backTrackStack + - curState->u.assertion.top); - gData->cursz = curState->u.assertion.sz; - if (result) - result = x; - break; - - case REOP_ASSERTNOTTEST: - --gData->stateStackTop; - --curState; - x->cp = gData->cpbegin + curState->index; - gData->backTrackSP = - (REBackTrackData *) ((char *)gData->backTrackStack + - curState->u.assertion.top); - gData->cursz = curState->u.assertion.sz; - result = (!result) ? x : NULL; - break; - - case REOP_END: - if (x) - return x; - break; - - case REOP_STAR: - curState->u.quantifier.min = 0; - curState->u.quantifier.max = (uintN)-1; - goto quantcommon; - case REOP_PLUS: - curState->u.quantifier.min = 1; - curState->u.quantifier.max = (uintN)-1; - goto quantcommon; - case REOP_OPT: - curState->u.quantifier.min = 0; - curState->u.quantifier.max = 1; - goto quantcommon; - case REOP_QUANT: - pc = ReadCompactIndex(pc, &k); - curState->u.quantifier.min = k; - pc = ReadCompactIndex(pc, &k); - /* max is k - 1 to use one byte for (uintN)-1 sentinel. */ - curState->u.quantifier.max = k - 1; - JS_ASSERT(curState->u.quantifier.min - <= curState->u.quantifier.max); - quantcommon: - if (curState->u.quantifier.max == 0) { - pc = pc + GET_OFFSET(pc); - op = (REOp) *pc++; - result = x; - continue; - } - /* Step over */ - nextpc = pc + ARG_LEN; - op = (REOp) *nextpc++; - startcp = x->cp; - if (REOP_IS_SIMPLE(op)) { - if (!SimpleMatch(gData, x, op, &nextpc, JS_TRUE)) { - if (curState->u.quantifier.min == 0) - result = x; - else - result = NULL; - pc = pc + GET_OFFSET(pc); - break; - } - op = (REOp) *nextpc++; - result = x; - } - curState->index = startcp - gData->cpbegin; - curState->continue_op = REOP_REPEAT; - curState->continue_pc = pc; - curState->parenSoFar = parenSoFar; - PUSH_STATE_STACK(gData); - if (curState->u.quantifier.min == 0 && - !PushBackTrackState(gData, REOP_REPEAT, pc, x, startcp, - 0, 0)) { - return NULL; - } - pc = nextpc; - continue; - - case REOP_ENDCHILD: /* marks the end of a quantifier child */ - pc = curState[-1].continue_pc; - op = curState[-1].continue_op; - continue; - - case REOP_REPEAT: - CHECK_BRANCH(); - --curState; - do { - --gData->stateStackTop; - if (!result) { - /* Failed, see if we have enough children. */ - if (curState->u.quantifier.min == 0) - goto repeatDone; - goto break_switch; - } - if (curState->u.quantifier.min == 0 && - x->cp == gData->cpbegin + curState->index) { - /* matched an empty string, that'll get us nowhere */ - result = NULL; - goto break_switch; - } - if (curState->u.quantifier.min != 0) - curState->u.quantifier.min--; - if (curState->u.quantifier.max != (uintN) -1) - curState->u.quantifier.max--; - if (curState->u.quantifier.max == 0) - goto repeatDone; - nextpc = pc + ARG_LEN; - nextop = (REOp) *nextpc; - startcp = x->cp; - if (REOP_IS_SIMPLE(nextop)) { - nextpc++; - if (!SimpleMatch(gData, x, nextop, &nextpc, JS_TRUE)) { - if (curState->u.quantifier.min == 0) - goto repeatDone; - result = NULL; - goto break_switch; - } - result = x; - } - curState->index = startcp - gData->cpbegin; - PUSH_STATE_STACK(gData); - if (curState->u.quantifier.min == 0 && - !PushBackTrackState(gData, REOP_REPEAT, - pc, x, startcp, - curState->parenSoFar, - parenSoFar - - curState->parenSoFar)) { - return NULL; - } - } while (*nextpc == REOP_ENDCHILD); - pc = nextpc; - op = (REOp) *pc++; - parenSoFar = curState->parenSoFar; - continue; - - repeatDone: - result = x; - pc += GET_OFFSET(pc); - goto break_switch; - - case REOP_MINIMALSTAR: - curState->u.quantifier.min = 0; - curState->u.quantifier.max = (uintN)-1; - goto minimalquantcommon; - case REOP_MINIMALPLUS: - curState->u.quantifier.min = 1; - curState->u.quantifier.max = (uintN)-1; - goto minimalquantcommon; - case REOP_MINIMALOPT: - curState->u.quantifier.min = 0; - curState->u.quantifier.max = 1; - goto minimalquantcommon; - case REOP_MINIMALQUANT: - pc = ReadCompactIndex(pc, &k); - curState->u.quantifier.min = k; - pc = ReadCompactIndex(pc, &k); - /* See REOP_QUANT comments about k - 1. */ - curState->u.quantifier.max = k - 1; - JS_ASSERT(curState->u.quantifier.min - <= curState->u.quantifier.max); - minimalquantcommon: - curState->index = x->cp - gData->cpbegin; - curState->parenSoFar = parenSoFar; - PUSH_STATE_STACK(gData); - if (curState->u.quantifier.min != 0) { - curState->continue_op = REOP_MINIMALREPEAT; - curState->continue_pc = pc; - /* step over */ - pc += OFFSET_LEN; - op = (REOp) *pc++; - } else { - if (!PushBackTrackState(gData, REOP_MINIMALREPEAT, - pc, x, x->cp, 0, 0)) { - return NULL; - } - --gData->stateStackTop; - pc = pc + GET_OFFSET(pc); - op = (REOp) *pc++; - } - continue; - - case REOP_MINIMALREPEAT: - CHECK_BRANCH(); - --gData->stateStackTop; - --curState; - - if (!result) { - /* - * Non-greedy failure - try to consume another child. - */ - if (curState->u.quantifier.max == (uintN) -1 || - curState->u.quantifier.max > 0) { - curState->index = x->cp - gData->cpbegin; - curState->continue_op = REOP_MINIMALREPEAT; - curState->continue_pc = pc; - pc += ARG_LEN; - for (k = curState->parenSoFar; k < parenSoFar; k++) - x->parens[k].index = -1; - PUSH_STATE_STACK(gData); - op = (REOp) *pc++; - continue; - } - /* Don't need to adjust pc since we're going to pop. */ - break; - } - if (curState->u.quantifier.min == 0 && - x->cp == gData->cpbegin + curState->index) { - /* Matched an empty string, that'll get us nowhere. */ - result = NULL; - break; - } - if (curState->u.quantifier.min != 0) - curState->u.quantifier.min--; - if (curState->u.quantifier.max != (uintN) -1) - curState->u.quantifier.max--; - if (curState->u.quantifier.min != 0) { - curState->continue_op = REOP_MINIMALREPEAT; - curState->continue_pc = pc; - pc += ARG_LEN; - for (k = curState->parenSoFar; k < parenSoFar; k++) - x->parens[k].index = -1; - curState->index = x->cp - gData->cpbegin; - PUSH_STATE_STACK(gData); - op = (REOp) *pc++; - continue; - } - curState->index = x->cp - gData->cpbegin; - curState->parenSoFar = parenSoFar; - PUSH_STATE_STACK(gData); - if (!PushBackTrackState(gData, REOP_MINIMALREPEAT, - pc, x, x->cp, - curState->parenSoFar, - parenSoFar - curState->parenSoFar)) { - return NULL; - } - --gData->stateStackTop; - pc = pc + GET_OFFSET(pc); - op = (REOp) *pc++; - continue; - - default: - JS_ASSERT(JS_FALSE); - result = NULL; - } - break_switch:; - } - - /* - * If the match failed and there's a backtrack option, take it. - * Otherwise this is a complete and utter failure. - */ - if (!result) { - if (gData->cursz == 0) - return NULL; - backTrackData = gData->backTrackSP; - gData->cursz = backTrackData->sz; - gData->backTrackSP = - (REBackTrackData *) ((char *)backTrackData - backTrackData->sz); - x->cp = backTrackData->cp; - pc = backTrackData->backtrack_pc; - op = backTrackData->backtrack_op; - gData->stateStackTop = backTrackData->saveStateStackTop; - JS_ASSERT(gData->stateStackTop); - - memcpy(gData->stateStack, backTrackData + 1, - sizeof(REProgState) * backTrackData->saveStateStackTop); - curState = &gData->stateStack[gData->stateStackTop - 1]; - - if (backTrackData->parenCount) { - memcpy(&x->parens[backTrackData->parenIndex], - (char *)(backTrackData + 1) + - sizeof(REProgState) * backTrackData->saveStateStackTop, - sizeof(RECapture) * backTrackData->parenCount); - parenSoFar = backTrackData->parenIndex + backTrackData->parenCount; - } else { - for (k = curState->parenSoFar; k < parenSoFar; k++) - x->parens[k].index = -1; - parenSoFar = curState->parenSoFar; - } - continue; - } - x = result; - - /* - * Continue with the expression. - */ - op = (REOp)*pc++; - } - return NULL; -} - -static REMatchState * -MatchRegExp(REGlobalData *gData, REMatchState *x) -{ - REMatchState *result; - const jschar *cp = x->cp; - const jschar *cp2; - uintN j; - - /* - * Have to include the position beyond the last character - * in order to detect end-of-input/line condition. - */ - for (cp2 = cp; cp2 <= gData->cpend; cp2++) { - gData->skipped = cp2 - cp; - x->cp = cp2; - for (j = 0; j < gData->regexp->parenCount; j++) - x->parens[j].index = -1; - result = ExecuteREBytecode(gData, x); - if (!gData->ok || result) - return result; - gData->backTrackSP = gData->backTrackStack; - gData->cursz = 0; - gData->stateStackTop = 0; - cp2 = cp + gData->skipped; - } - return NULL; -} - - -static REMatchState * -InitMatch(JSContext *cx, REGlobalData *gData, JSRegExp *re) -{ - REMatchState *result; - uintN i; - - gData->backTrackStackSize = INITIAL_BACKTRACK; - JS_ARENA_ALLOCATE_CAST(gData->backTrackStack, REBackTrackData *, - &gData->pool, - INITIAL_BACKTRACK); - if (!gData->backTrackStack) - goto bad; - - gData->backTrackSP = gData->backTrackStack; - gData->cursz = 0; - - gData->stateStackLimit = INITIAL_STATESTACK; - JS_ARENA_ALLOCATE_CAST(gData->stateStack, REProgState *, - &gData->pool, - sizeof(REProgState) * INITIAL_STATESTACK); - if (!gData->stateStack) - goto bad; - - gData->stateStackTop = 0; - gData->cx = cx; - gData->regexp = re; - gData->ok = JS_TRUE; - - JS_ARENA_ALLOCATE_CAST(result, REMatchState *, - &gData->pool, - offsetof(REMatchState, parens) - + re->parenCount * sizeof(RECapture)); - if (!result) - goto bad; - - for (i = 0; i < re->classCount; i++) { - if (!re->classList[i].converted && - !ProcessCharSet(gData, &re->classList[i])) { - return NULL; - } - } - - return result; - -bad: - JS_ReportOutOfMemory(cx); - gData->ok = JS_FALSE; - return NULL; -} - -JSBool -js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, - JSBool test, jsval *rval) -{ - REGlobalData gData; - REMatchState *x, *result; - - const jschar *cp, *ep; - size_t i, length, start; - JSSubString *morepar; - JSBool ok; - JSRegExpStatics *res; - ptrdiff_t matchlen; - uintN num, morenum; - JSString *parstr, *matchstr; - JSObject *obj; - - RECapture *parsub = NULL; - - /* - * It's safe to load from cp because JSStrings have a zero at the end, - * and we never let cp get beyond cpend. - */ - start = *indexp; - length = JSSTRING_LENGTH(str); - if (start > length) - start = length; - cp = JSSTRING_CHARS(str); - gData.cpbegin = cp; - gData.cpend = cp + length; - cp += start; - gData.start = start; - gData.skipped = 0; - - JS_InitArenaPool(&gData.pool, "RegExpPool", 8096, 4); - x = InitMatch(cx, &gData, re); - if (!x) { - ok = JS_FALSE; - goto out; - } - x->cp = cp; - - /* - * Call the recursive matcher to do the real work. Return null on mismatch - * whether testing or not. On match, return an extended Array object. - */ - result = MatchRegExp(&gData, x); - ok = gData.ok; - if (!ok) - goto out; - if (!result) { - *rval = JSVAL_NULL; - goto out; - } - cp = result->cp; - i = cp - gData.cpbegin; - *indexp = i; - matchlen = i - (start + gData.skipped); - ep = cp; - cp -= matchlen; - - if (test) { - /* - * Testing for a match and updating cx->regExpStatics: don't allocate - * an array object, do return true. - */ - *rval = JSVAL_TRUE; - - /* Avoid warning. (gcc doesn't detect that obj is needed iff !test); */ - obj = NULL; - } else { - /* - * The array returned on match has element 0 bound to the matched - * string, elements 1 through state.parenCount bound to the paren - * matches, an index property telling the length of the left context, - * and an input property referring to the input string. - */ - obj = js_NewArrayObject(cx, 0, NULL); - if (!obj) { - ok = JS_FALSE; - goto out; - } - *rval = OBJECT_TO_JSVAL(obj); - -#define DEFVAL(val, id) { \ - ok = js_DefineProperty(cx, obj, id, val, \ - JS_PropertyStub, JS_PropertyStub, \ - JSPROP_ENUMERATE, NULL); \ - if (!ok) { \ - cx->weakRoots.newborn[GCX_OBJECT] = NULL; \ - cx->weakRoots.newborn[GCX_STRING] = NULL; \ - goto out; \ - } \ -} - - matchstr = js_NewStringCopyN(cx, cp, matchlen, 0); - if (!matchstr) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - ok = JS_FALSE; - goto out; - } - DEFVAL(STRING_TO_JSVAL(matchstr), INT_TO_JSID(0)); - } - - res = &cx->regExpStatics; - res->input = str; - res->parenCount = re->parenCount; - if (re->parenCount == 0) { - res->lastParen = js_EmptySubString; - } else { - for (num = 0; num < re->parenCount; num++) { - parsub = &result->parens[num]; - if (num < 9) { - if (parsub->index == -1) { - res->parens[num].chars = NULL; - res->parens[num].length = 0; - } else { - res->parens[num].chars = gData.cpbegin + parsub->index; - res->parens[num].length = parsub->length; - } - } else { - morenum = num - 9; - morepar = res->moreParens; - if (!morepar) { - res->moreLength = 10; - morepar = (JSSubString*) - JS_malloc(cx, 10 * sizeof(JSSubString)); - } else if (morenum >= res->moreLength) { - res->moreLength += 10; - morepar = (JSSubString*) - JS_realloc(cx, morepar, - res->moreLength * sizeof(JSSubString)); - } - if (!morepar) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - cx->weakRoots.newborn[GCX_STRING] = NULL; - ok = JS_FALSE; - goto out; - } - res->moreParens = morepar; - if (parsub->index == -1) { - morepar[morenum].chars = NULL; - morepar[morenum].length = 0; - } else { - morepar[morenum].chars = gData.cpbegin + parsub->index; - morepar[morenum].length = parsub->length; - } - } - if (test) - continue; - if (parsub->index == -1) { - ok = js_DefineProperty(cx, obj, INT_TO_JSID(num + 1), - JSVAL_VOID, NULL, NULL, - JSPROP_ENUMERATE, NULL); - } else { - parstr = js_NewStringCopyN(cx, gData.cpbegin + parsub->index, - parsub->length, 0); - if (!parstr) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - cx->weakRoots.newborn[GCX_STRING] = NULL; - ok = JS_FALSE; - goto out; - } - ok = js_DefineProperty(cx, obj, INT_TO_JSID(num + 1), - STRING_TO_JSVAL(parstr), NULL, NULL, - JSPROP_ENUMERATE, NULL); - } - if (!ok) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - cx->weakRoots.newborn[GCX_STRING] = NULL; - goto out; - } - } - if (parsub->index == -1) { - res->lastParen = js_EmptySubString; - } else { - res->lastParen.chars = gData.cpbegin + parsub->index; - res->lastParen.length = parsub->length; - } - } - - if (!test) { - /* - * Define the index and input properties last for better for/in loop - * order (so they come after the elements). - */ - DEFVAL(INT_TO_JSVAL(start + gData.skipped), - ATOM_TO_JSID(cx->runtime->atomState.indexAtom)); - DEFVAL(STRING_TO_JSVAL(str), - ATOM_TO_JSID(cx->runtime->atomState.inputAtom)); - } - -#undef DEFVAL - - res->lastMatch.chars = cp; - res->lastMatch.length = matchlen; - - /* - * For JS1.3 and ECMAv2, emulate Perl5 exactly: - * - * js1.3 "hi", "hi there" "hihitherehi therebye" - */ - res->leftContext.chars = JSSTRING_CHARS(str); - res->leftContext.length = start + gData.skipped; - res->rightContext.chars = ep; - res->rightContext.length = gData.cpend - ep; - -out: - JS_FinishArenaPool(&gData.pool); - return ok; -} - -/************************************************************************/ - -enum regexp_tinyid { - REGEXP_SOURCE = -1, - REGEXP_GLOBAL = -2, - REGEXP_IGNORE_CASE = -3, - REGEXP_LAST_INDEX = -4, - REGEXP_MULTILINE = -5 -}; - -#define REGEXP_PROP_ATTRS (JSPROP_PERMANENT|JSPROP_SHARED) - -static JSPropertySpec regexp_props[] = { - {"source", REGEXP_SOURCE, REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0}, - {"global", REGEXP_GLOBAL, REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0}, - {"ignoreCase", REGEXP_IGNORE_CASE, REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0}, - {"lastIndex", REGEXP_LAST_INDEX, REGEXP_PROP_ATTRS,0,0}, - {"multiline", REGEXP_MULTILINE, REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0}, - {0,0,0,0,0} -}; - -static JSBool -regexp_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsint slot; - JSRegExp *re; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - slot = JSVAL_TO_INT(id); - if (slot == REGEXP_LAST_INDEX) - return JS_GetReservedSlot(cx, obj, 0, vp); - - JS_LOCK_OBJ(cx, obj); - re = (JSRegExp *) JS_GetInstancePrivate(cx, obj, &js_RegExpClass, NULL); - if (re) { - switch (slot) { - case REGEXP_SOURCE: - *vp = STRING_TO_JSVAL(re->source); - break; - case REGEXP_GLOBAL: - *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_GLOB) != 0); - break; - case REGEXP_IGNORE_CASE: - *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_FOLD) != 0); - break; - case REGEXP_MULTILINE: - *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_MULTILINE) != 0); - break; - } - } - JS_UNLOCK_OBJ(cx, obj); - return JS_TRUE; -} - -static JSBool -regexp_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSBool ok; - jsint slot; - jsdouble lastIndex; - - ok = JS_TRUE; - if (!JSVAL_IS_INT(id)) - return ok; - slot = JSVAL_TO_INT(id); - if (slot == REGEXP_LAST_INDEX) { - if (!js_ValueToNumber(cx, *vp, &lastIndex)) - return JS_FALSE; - lastIndex = js_DoubleToInteger(lastIndex); - ok = js_NewNumberValue(cx, lastIndex, vp) && - JS_SetReservedSlot(cx, obj, 0, *vp); - } - return ok; -} - -/* - * RegExp class static properties and their Perl counterparts: - * - * RegExp.input $_ - * RegExp.multiline $* - * RegExp.lastMatch $& - * RegExp.lastParen $+ - * RegExp.leftContext $` - * RegExp.rightContext $' - */ -enum regexp_static_tinyid { - REGEXP_STATIC_INPUT = -1, - REGEXP_STATIC_MULTILINE = -2, - REGEXP_STATIC_LAST_MATCH = -3, - REGEXP_STATIC_LAST_PAREN = -4, - REGEXP_STATIC_LEFT_CONTEXT = -5, - REGEXP_STATIC_RIGHT_CONTEXT = -6 -}; - -JSBool -js_InitRegExpStatics(JSContext *cx, JSRegExpStatics *res) -{ - JS_ClearRegExpStatics(cx); - return js_AddRoot(cx, &res->input, "res->input"); -} - -void -js_FreeRegExpStatics(JSContext *cx, JSRegExpStatics *res) -{ - if (res->moreParens) { - JS_free(cx, res->moreParens); - res->moreParens = NULL; - } - js_RemoveRoot(cx->runtime, &res->input); -} - -static JSBool -regexp_static_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsint slot; - JSRegExpStatics *res; - JSString *str; - JSSubString *sub; - - res = &cx->regExpStatics; - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - slot = JSVAL_TO_INT(id); - switch (slot) { - case REGEXP_STATIC_INPUT: - *vp = res->input ? STRING_TO_JSVAL(res->input) - : JS_GetEmptyStringValue(cx); - return JS_TRUE; - case REGEXP_STATIC_MULTILINE: - *vp = BOOLEAN_TO_JSVAL(res->multiline); - return JS_TRUE; - case REGEXP_STATIC_LAST_MATCH: - sub = &res->lastMatch; - break; - case REGEXP_STATIC_LAST_PAREN: - sub = &res->lastParen; - break; - case REGEXP_STATIC_LEFT_CONTEXT: - sub = &res->leftContext; - break; - case REGEXP_STATIC_RIGHT_CONTEXT: - sub = &res->rightContext; - break; - default: - sub = REGEXP_PAREN_SUBSTRING(res, slot); - break; - } - str = js_NewStringCopyN(cx, sub->chars, sub->length, 0); - if (!str) - return JS_FALSE; - *vp = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -regexp_static_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSRegExpStatics *res; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - res = &cx->regExpStatics; - /* XXX use if-else rather than switch to keep MSVC1.52 from crashing */ - if (JSVAL_TO_INT(id) == REGEXP_STATIC_INPUT) { - if (!JSVAL_IS_STRING(*vp) && - !JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp)) { - return JS_FALSE; - } - res->input = JSVAL_TO_STRING(*vp); - } else if (JSVAL_TO_INT(id) == REGEXP_STATIC_MULTILINE) { - if (!JSVAL_IS_BOOLEAN(*vp) && - !JS_ConvertValue(cx, *vp, JSTYPE_BOOLEAN, vp)) { - return JS_FALSE; - } - res->multiline = JSVAL_TO_BOOLEAN(*vp); - } - return JS_TRUE; -} - -static JSPropertySpec regexp_static_props[] = { - {"input", - REGEXP_STATIC_INPUT, - JSPROP_ENUMERATE|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_setProperty}, - {"multiline", - REGEXP_STATIC_MULTILINE, - JSPROP_ENUMERATE|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_setProperty}, - {"lastMatch", - REGEXP_STATIC_LAST_MATCH, - JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - {"lastParen", - REGEXP_STATIC_LAST_PAREN, - JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - {"leftContext", - REGEXP_STATIC_LEFT_CONTEXT, - JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - {"rightContext", - REGEXP_STATIC_RIGHT_CONTEXT, - JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - - /* XXX should have block scope and local $1, etc. */ - {"$1", 0, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - {"$2", 1, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - {"$3", 2, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - {"$4", 3, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - {"$5", 4, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - {"$6", 5, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - {"$7", 6, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - {"$8", 7, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - {"$9", 8, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - - {0,0,0,0,0} -}; - -static void -regexp_finalize(JSContext *cx, JSObject *obj) -{ - JSRegExp *re; - - re = (JSRegExp *) JS_GetPrivate(cx, obj); - if (!re) - return; - js_DestroyRegExp(cx, re); -} - -/* Forward static prototype. */ -static JSBool -regexp_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); - -static JSBool -regexp_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return regexp_exec(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval); -} - -#if JS_HAS_XDR - -#include "jsxdrapi.h" - -static JSBool -regexp_xdrObject(JSXDRState *xdr, JSObject **objp) -{ - JSRegExp *re; - JSString *source; - uint32 flagsword; - JSObject *obj; - - if (xdr->mode == JSXDR_ENCODE) { - re = (JSRegExp *) JS_GetPrivate(xdr->cx, *objp); - if (!re) - return JS_FALSE; - source = re->source; - flagsword = ((uint32)re->cloneIndex << 16) | re->flags; - } - if (!JS_XDRString(xdr, &source) || - !JS_XDRUint32(xdr, &flagsword)) { - return JS_FALSE; - } - if (xdr->mode == JSXDR_DECODE) { - obj = js_NewObject(xdr->cx, &js_RegExpClass, NULL, NULL); - if (!obj) - return JS_FALSE; - re = js_NewRegExp(xdr->cx, NULL, source, (uint16)flagsword, JS_FALSE); - if (!re) - return JS_FALSE; - if (!JS_SetPrivate(xdr->cx, obj, re) || - !js_SetLastIndex(xdr->cx, obj, 0)) { - js_DestroyRegExp(xdr->cx, re); - return JS_FALSE; - } - re->cloneIndex = (uint16)(flagsword >> 16); - *objp = obj; - } - return JS_TRUE; -} - -#else /* !JS_HAS_XDR */ - -#define regexp_xdrObject NULL - -#endif /* !JS_HAS_XDR */ - -static uint32 -regexp_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSRegExp *re = (JSRegExp *) JS_GetPrivate(cx, obj); - if (re) - GC_MARK(cx, re->source, "source"); - return 0; -} - -JSClass js_RegExpClass = { - js_RegExp_str, - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | - JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp), - JS_PropertyStub, JS_PropertyStub, - regexp_getProperty, regexp_setProperty, - JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, regexp_finalize, - NULL, NULL, - regexp_call, NULL, - regexp_xdrObject, NULL, - regexp_mark, 0 -}; - -static const jschar empty_regexp_ucstr[] = {'(', '?', ':', ')', 0}; - -JSBool -js_regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSRegExp *re; - const jschar *source; - jschar *chars; - size_t length, nflags; - uintN flags; - JSString *str; - - if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv)) - return JS_FALSE; - JS_LOCK_OBJ(cx, obj); - re = (JSRegExp *) JS_GetPrivate(cx, obj); - if (!re) { - JS_UNLOCK_OBJ(cx, obj); - *rval = STRING_TO_JSVAL(cx->runtime->emptyString); - return JS_TRUE; - } - - source = JSSTRING_CHARS(re->source); - length = JSSTRING_LENGTH(re->source); - if (length == 0) { - source = empty_regexp_ucstr; - length = sizeof(empty_regexp_ucstr) / sizeof(jschar) - 1; - } - length += 2; - nflags = 0; - for (flags = re->flags; flags != 0; flags &= flags - 1) - nflags++; - chars = (jschar*) JS_malloc(cx, (length + nflags + 1) * sizeof(jschar)); - if (!chars) { - JS_UNLOCK_OBJ(cx, obj); - return JS_FALSE; - } - - chars[0] = '/'; - js_strncpy(&chars[1], source, length - 2); - chars[length-1] = '/'; - if (nflags) { - if (re->flags & JSREG_GLOB) - chars[length++] = 'g'; - if (re->flags & JSREG_FOLD) - chars[length++] = 'i'; - if (re->flags & JSREG_MULTILINE) - chars[length++] = 'm'; - } - JS_UNLOCK_OBJ(cx, obj); - chars[length] = 0; - - str = js_NewString(cx, chars, length, 0); - if (!str) { - JS_free(cx, chars); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -regexp_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *opt, *str; - JSRegExp *oldre, *re; - JSBool ok, ok2; - JSObject *obj2; - size_t length, nbytes; - const jschar *cp, *start, *end; - jschar *nstart, *ncp, *tmp; - - if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv)) - return JS_FALSE; - opt = NULL; - if (argc == 0) { - str = cx->runtime->emptyString; - } else { - if (JSVAL_IS_OBJECT(argv[0])) { - /* - * If we get passed in a RegExp object we construct a new - * RegExp that is a duplicate of it by re-compiling the - * original source code. ECMA requires that it be an error - * here if the flags are specified. (We must use the flags - * from the original RegExp also). - */ - obj2 = JSVAL_TO_OBJECT(argv[0]); - if (obj2 && OBJ_GET_CLASS(cx, obj2) == &js_RegExpClass) { - if (argc >= 2 && !JSVAL_IS_VOID(argv[1])) { /* 'flags' passed */ - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_NEWREGEXP_FLAGGED); - return JS_FALSE; - } - JS_LOCK_OBJ(cx, obj2); - re = (JSRegExp *) JS_GetPrivate(cx, obj2); - if (!re) { - JS_UNLOCK_OBJ(cx, obj2); - return JS_FALSE; - } - re = js_NewRegExp(cx, NULL, re->source, re->flags, JS_FALSE); - JS_UNLOCK_OBJ(cx, obj2); - goto created; - } - } - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str); - if (argc > 1) { - if (JSVAL_IS_VOID(argv[1])) { - opt = NULL; - } else { - opt = js_ValueToString(cx, argv[1]); - if (!opt) - return JS_FALSE; - argv[1] = STRING_TO_JSVAL(opt); - } - } - - /* Escape any naked slashes in the regexp source. */ - length = JSSTRING_LENGTH(str); - start = JSSTRING_CHARS(str); - end = start + length; - nstart = ncp = NULL; - for (cp = start; cp < end; cp++) { - if (*cp == '/' && (cp == start || cp[-1] != '\\')) { - nbytes = (++length + 1) * sizeof(jschar); - if (!nstart) { - nstart = (jschar *) JS_malloc(cx, nbytes); - if (!nstart) - return JS_FALSE; - ncp = nstart + (cp - start); - js_strncpy(nstart, start, cp - start); - } else { - tmp = (jschar *) JS_realloc(cx, nstart, nbytes); - if (!tmp) { - JS_free(cx, nstart); - return JS_FALSE; - } - ncp = tmp + (ncp - nstart); - nstart = tmp; - } - *ncp++ = '\\'; - } - if (nstart) - *ncp++ = *cp; - } - - if (nstart) { - /* Don't forget to store the backstop after the new string. */ - JS_ASSERT((size_t)(ncp - nstart) == length); - *ncp = 0; - str = js_NewString(cx, nstart, length, 0); - if (!str) { - JS_free(cx, nstart); - return JS_FALSE; - } - argv[0] = STRING_TO_JSVAL(str); - } - } - - re = js_NewRegExpOpt(cx, NULL, str, opt, JS_FALSE); -created: - if (!re) - return JS_FALSE; - JS_LOCK_OBJ(cx, obj); - oldre = (JSRegExp *) JS_GetPrivate(cx, obj); - ok = JS_SetPrivate(cx, obj, re); - ok2 = js_SetLastIndex(cx, obj, 0); - JS_UNLOCK_OBJ(cx, obj); - if (!ok) { - js_DestroyRegExp(cx, re); - return JS_FALSE; - } - if (oldre) - js_DestroyRegExp(cx, oldre); - *rval = OBJECT_TO_JSVAL(obj); - return ok2; -} - -static JSBool -regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - JSBool test, jsval *rval) -{ - JSBool ok; - JSRegExp *re; - jsdouble lastIndex; - JSString *str; - size_t i; - - ok = JS_InstanceOf(cx, obj, &js_RegExpClass, argv); - if (!ok) - return JS_FALSE; - JS_LOCK_OBJ(cx, obj); - re = (JSRegExp *) JS_GetPrivate(cx, obj); - if (!re) { - JS_UNLOCK_OBJ(cx, obj); - return JS_TRUE; - } - - /* NB: we must reach out: after this paragraph, in order to drop re. */ - HOLD_REGEXP(cx, re); - if (re->flags & JSREG_GLOB) { - ok = js_GetLastIndex(cx, obj, &lastIndex); - } else { - lastIndex = 0; - } - JS_UNLOCK_OBJ(cx, obj); - if (!ok) - goto out; - - /* Now that obj is unlocked, it's safe to (potentially) grab the GC lock. */ - if (argc == 0) { - str = cx->regExpStatics.input; - if (!str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_NO_INPUT, - JS_GetStringBytes(re->source), - (re->flags & JSREG_GLOB) ? "g" : "", - (re->flags & JSREG_FOLD) ? "i" : "", - (re->flags & JSREG_MULTILINE) ? "m" : ""); - ok = JS_FALSE; - goto out; - } - } else { - str = js_ValueToString(cx, argv[0]); - if (!str) { - ok = JS_FALSE; - goto out; - } - argv[0] = STRING_TO_JSVAL(str); - } - - if (lastIndex < 0 || JSSTRING_LENGTH(str) < lastIndex) { - ok = js_SetLastIndex(cx, obj, 0); - *rval = JSVAL_NULL; - } else { - i = (size_t) lastIndex; - ok = js_ExecuteRegExp(cx, re, str, &i, test, rval); - if (ok && (re->flags & JSREG_GLOB)) - ok = js_SetLastIndex(cx, obj, (*rval == JSVAL_NULL) ? 0 : i); - } - -out: - DROP_REGEXP(cx, re); - return ok; -} - -static JSBool -regexp_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return regexp_exec_sub(cx, obj, argc, argv, JS_FALSE, rval); -} - -static JSBool -regexp_test(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - if (!regexp_exec_sub(cx, obj, argc, argv, JS_TRUE, rval)) - return JS_FALSE; - if (*rval != JSVAL_TRUE) - *rval = JSVAL_FALSE; - return JS_TRUE; -} - -static JSFunctionSpec regexp_methods[] = { -#if JS_HAS_TOSOURCE - {js_toSource_str, js_regexp_toString, 0,0,0}, -#endif - {js_toString_str, js_regexp_toString, 0,0,0}, - {"compile", regexp_compile, 1,0,0}, - {"exec", regexp_exec, 0,0,0}, - {"test", regexp_test, 0,0,0}, - {0,0,0,0,0} -}; - -static JSBool -RegExp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - /* - * If first arg is regexp and no flags are given, just return the arg. - * (regexp_compile detects the regexp + flags case and throws a - * TypeError.) See 10.15.3.1. - */ - if ((argc < 2 || JSVAL_IS_VOID(argv[1])) && - !JSVAL_IS_PRIMITIVE(argv[0]) && - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[0])) == &js_RegExpClass) { - *rval = argv[0]; - return JS_TRUE; - } - - /* Otherwise, replace obj with a new RegExp object. */ - obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL); - if (!obj) - return JS_FALSE; - - /* - * regexp_compile does not use rval to root its temporaries - * so we can use it to root obj. - */ - *rval = OBJECT_TO_JSVAL(obj); - } - return regexp_compile(cx, obj, argc, argv, rval); -} - -JSObject * -js_InitRegExpClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *ctor; - jsval rval; - - proto = JS_InitClass(cx, obj, NULL, &js_RegExpClass, RegExp, 1, - regexp_props, regexp_methods, - regexp_static_props, NULL); - - if (!proto || !(ctor = JS_GetConstructor(cx, proto))) - return NULL; - if (!JS_AliasProperty(cx, ctor, "input", "$_") || - !JS_AliasProperty(cx, ctor, "multiline", "$*") || - !JS_AliasProperty(cx, ctor, "lastMatch", "$&") || - !JS_AliasProperty(cx, ctor, "lastParen", "$+") || - !JS_AliasProperty(cx, ctor, "leftContext", "$`") || - !JS_AliasProperty(cx, ctor, "rightContext", "$'")) { - goto bad; - } - - /* Give RegExp.prototype private data so it matches the empty string. */ - if (!regexp_compile(cx, proto, 0, NULL, &rval)) - goto bad; - return proto; - -bad: - JS_DeleteProperty(cx, obj, js_RegExpClass.name); - return NULL; -} - -JSObject * -js_NewRegExpObject(JSContext *cx, JSTokenStream *ts, - jschar *chars, size_t length, uintN flags) -{ - JSString *str; - JSObject *obj; - JSRegExp *re; - JSTempValueRooter tvr; - - str = js_NewStringCopyN(cx, chars, length, 0); - if (!str) - return NULL; - re = js_NewRegExp(cx, ts, str, flags, JS_FALSE); - if (!re) - return NULL; - JS_PUSH_TEMP_ROOT_STRING(cx, str, &tvr); - obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL); - if (!obj || !JS_SetPrivate(cx, obj, re)) { - js_DestroyRegExp(cx, re); - obj = NULL; - } - if (obj && !js_SetLastIndex(cx, obj, 0)) - obj = NULL; - JS_POP_TEMP_ROOT(cx, &tvr); - return obj; -} - -JSObject * -js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *parent) -{ - JSObject *clone; - JSRegExp *re; - - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass); - clone = js_NewObject(cx, &js_RegExpClass, NULL, parent); - if (!clone) - return NULL; - re = JS_GetPrivate(cx, obj); - if (!JS_SetPrivate(cx, clone, re) || !js_SetLastIndex(cx, clone, 0)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; - } - HOLD_REGEXP(cx, re); - return clone; -} - -JSBool -js_GetLastIndex(JSContext *cx, JSObject *obj, jsdouble *lastIndex) -{ - jsval v; - - return JS_GetReservedSlot(cx, obj, 0, &v) && - js_ValueToNumber(cx, v, lastIndex); -} - -JSBool -js_SetLastIndex(JSContext *cx, JSObject *obj, jsdouble lastIndex) -{ - jsval v; - - return js_NewNumberValue(cx, lastIndex, &v) && - JS_SetReservedSlot(cx, obj, 0, v); -} diff --git a/spidermonkey/src/jsregexp.h b/spidermonkey/src/jsregexp.h deleted file mode 100644 index 5078983..0000000 --- a/spidermonkey/src/jsregexp.h +++ /dev/null @@ -1,183 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsregexp_h___ -#define jsregexp_h___ -/* - * JS regular expression interface. - */ -#include -#include "jspubtd.h" -#include "jsstr.h" - -#ifdef JS_THREADSAFE -#include "jsdhash.h" -#endif - -struct JSRegExpStatics { - JSString *input; /* input string to match (perl $_, GC root) */ - JSBool multiline; /* whether input contains newlines (perl $*) */ - uint16 parenCount; /* number of valid elements in parens[] */ - uint16 moreLength; /* number of allocated elements in moreParens */ - JSSubString parens[9]; /* last set of parens matched (perl $1, $2) */ - JSSubString *moreParens; /* null or realloc'd vector for $10, etc. */ - JSSubString lastMatch; /* last string matched (perl $&) */ - JSSubString lastParen; /* last paren matched (perl $+) */ - JSSubString leftContext; /* input to left of last match (perl $`) */ - JSSubString rightContext; /* input to right of last match (perl $') */ -}; - -/* - * This struct holds a bitmap representation of a class from a regexp. - * There's a list of these referenced by the classList field in the JSRegExp - * struct below. The initial state has startIndex set to the offset in the - * original regexp source of the beginning of the class contents. The first - * use of the class converts the source representation into a bitmap. - * - */ -typedef struct RECharSet { - JSPackedBool converted; - JSPackedBool sense; - uint16 length; - union { - uint8 *bits; - struct { - size_t startIndex; - size_t length; - } src; - } u; -} RECharSet; - -/* - * This macro is safe because moreParens is guaranteed to be allocated and big - * enough to hold parenCount, or else be null when parenCount is 0. - */ -#define REGEXP_PAREN_SUBSTRING(res, num) \ - (((jsuint)(num) < (jsuint)(res)->parenCount) \ - ? ((jsuint)(num) < 9) \ - ? &(res)->parens[num] \ - : &(res)->moreParens[(num) - 9] \ - : &js_EmptySubString) - -typedef struct RENode RENode; - -struct JSRegExp { - jsrefcount nrefs; /* reference count */ - uint16 flags; /* flags, see jsapi.h's JSREG_* defines */ - uint16 cloneIndex; /* index in fp->vars or funobj->slots of - cloned regexp object */ - size_t parenCount; /* number of parenthesized submatches */ - size_t classCount; /* count [...] bitmaps */ - RECharSet *classList; /* list of [...] bitmaps */ - JSString *source; /* locked source string, sans // */ - jsbytecode program[1]; /* regular expression bytecode */ -}; - -extern JSRegExp * -js_NewRegExp(JSContext *cx, JSTokenStream *ts, - JSString *str, uintN flags, JSBool flat); - -extern JSRegExp * -js_NewRegExpOpt(JSContext *cx, JSTokenStream *ts, - JSString *str, JSString *opt, JSBool flat); - -#define HOLD_REGEXP(cx, re) JS_ATOMIC_INCREMENT(&(re)->nrefs) -#define DROP_REGEXP(cx, re) js_DestroyRegExp(cx, re) - -extern void -js_DestroyRegExp(JSContext *cx, JSRegExp *re); - -/* - * Execute re on input str at *indexp, returning null in *rval on mismatch. - * On match, return true if test is true, otherwise return an array object. - * Update *indexp and cx->regExpStatics always on match. - */ -extern JSBool -js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, - JSBool test, jsval *rval); - -/* - * These two add and remove GC roots, respectively, so their calls must be - * well-ordered. - */ -extern JSBool -js_InitRegExpStatics(JSContext *cx, JSRegExpStatics *res); - -extern void -js_FreeRegExpStatics(JSContext *cx, JSRegExpStatics *res); - -#define JSVAL_IS_REGEXP(cx, v) \ - (JSVAL_IS_OBJECT(v) && JSVAL_TO_OBJECT(v) && \ - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_RegExpClass) - -extern JSClass js_RegExpClass; - -extern JSObject * -js_InitRegExpClass(JSContext *cx, JSObject *obj); - -/* - * Export js_regexp_toString to the decompiler. - */ -extern JSBool -js_regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); - -/* - * Create, serialize/deserialize, or clone a RegExp object. - */ -extern JSObject * -js_NewRegExpObject(JSContext *cx, JSTokenStream *ts, - jschar *chars, size_t length, uintN flags); - -extern JSBool -js_XDRRegExp(JSXDRState *xdr, JSObject **objp); - -extern JSObject * -js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *parent); - -/* - * Get and set the per-object (clone or clone-parent) lastIndex slot. - */ -extern JSBool -js_GetLastIndex(JSContext *cx, JSObject *obj, jsdouble *lastIndex); - -extern JSBool -js_SetLastIndex(JSContext *cx, JSObject *obj, jsdouble lastIndex); - -#endif /* jsregexp_h___ */ diff --git a/spidermonkey/src/jsscan.c b/spidermonkey/src/jsscan.c deleted file mode 100644 index f9f7436..0000000 --- a/spidermonkey/src/jsscan.c +++ /dev/null @@ -1,2101 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set sw=4 ts=8 et tw=78: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS lexical scanner. - */ -#include "jsstddef.h" -#include /* first to avoid trouble on some systems */ -#include -#include -#include -#ifdef HAVE_MEMORY_H -#include -#endif -#include -#include -#include -#include "jstypes.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ -#include "jsdtoa.h" -#include "jsprf.h" -#include "jsapi.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsemit.h" -#include "jsexn.h" -#include "jsnum.h" -#include "jsopcode.h" -#include "jsregexp.h" -#include "jsscan.h" -#include "jsscript.h" - -#if JS_HAS_XML_SUPPORT -#include "jsparse.h" -#include "jsxml.h" -#endif - -#define JS_KEYWORD(keyword, type, op, version) \ - const char js_##keyword##_str[] = #keyword; -#include "jskeyword.tbl" -#undef JS_KEYWORD - -struct keyword { - const char *chars; /* C string with keyword text */ - JSTokenType tokentype; /* JSTokenType */ - JSOp op; /* JSOp */ - JSVersion version; /* JSVersion */ -}; - -static const struct keyword keyword_defs[] = { -#define JS_KEYWORD(keyword, type, op, version) \ - {js_##keyword##_str, type, op, version}, -#include "jskeyword.tbl" -#undef JS_KEYWORD -}; - -#define KEYWORD_COUNT (sizeof keyword_defs / sizeof keyword_defs[0]) - -static const struct keyword * -FindKeyword(const jschar *s, size_t length) -{ - register size_t i; - const struct keyword *kw; - const char *chars; - - JS_ASSERT(length != 0); - -#define JSKW_LENGTH() length -#define JSKW_AT(column) s[column] -#define JSKW_GOT_MATCH(index) i = (index); goto got_match; -#define JSKW_TEST_GUESS(index) i = (index); goto test_guess; -#define JSKW_NO_MATCH() goto no_match; -#include "jsautokw.h" -#undef JSKW_NO_MATCH -#undef JSKW_TEST_GUESS -#undef JSKW_GOT_MATCH -#undef JSKW_AT -#undef JSKW_LENGTH - - got_match: - return &keyword_defs[i]; - - test_guess: - kw = &keyword_defs[i]; - chars = kw->chars; - do { - if (*s++ != (unsigned char)(*chars++)) - goto no_match; - } while (--length != 0); - return kw; - - no_match: - return NULL; -} - -JSTokenType -js_CheckKeyword(const jschar *str, size_t length) -{ - const struct keyword *kw; - - JS_ASSERT(length != 0); - kw = FindKeyword(str, length); - return kw ? kw->tokentype : TOK_EOF; -} - -JS_FRIEND_API(void) -js_MapKeywords(void (*mapfun)(const char *)) -{ - size_t i; - - for (i = 0; i != KEYWORD_COUNT; ++i) - mapfun(keyword_defs[i].chars); -} - -JSTokenStream * -js_NewTokenStream(JSContext *cx, const jschar *base, size_t length, - const char *filename, uintN lineno, - JSPrincipals *principals) -{ - JSTokenStream *ts; - - ts = js_NewBufferTokenStream(cx, base, length); - if (!ts) - return NULL; - ts->filename = filename; - ts->lineno = lineno; - if (principals) - JSPRINCIPALS_HOLD(cx, principals); - ts->principals = principals; - return ts; -} - -#define TBMIN 64 - -static JSBool -GrowTokenBuf(JSStringBuffer *sb, size_t newlength) -{ - JSContext *cx; - jschar *base; - ptrdiff_t offset, length; - size_t tbsize; - JSArenaPool *pool; - - cx = sb->data; - base = sb->base; - offset = PTRDIFF(sb->ptr, base, jschar); - pool = &cx->tempPool; - if (!base) { - tbsize = TBMIN * sizeof(jschar); - length = TBMIN - 1; - JS_ARENA_ALLOCATE_CAST(base, jschar *, pool, tbsize); - } else { - length = PTRDIFF(sb->limit, base, jschar); - if ((size_t)length >= ~(size_t)0 / sizeof(jschar)) { - base = NULL; - } else { - tbsize = (length + 1) * sizeof(jschar); - length += length + 1; - JS_ARENA_GROW_CAST(base, jschar *, pool, tbsize, tbsize); - } - } - if (!base) { - JS_ReportOutOfMemory(cx); - sb->base = STRING_BUFFER_ERROR_BASE; - return JS_FALSE; - } - sb->base = base; - sb->limit = base + length; - sb->ptr = base + offset; - return JS_TRUE; -} - -JS_FRIEND_API(JSTokenStream *) -js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length) -{ - size_t nb; - JSTokenStream *ts; - - nb = sizeof(JSTokenStream) + JS_LINE_LIMIT * sizeof(jschar); - JS_ARENA_ALLOCATE_CAST(ts, JSTokenStream *, &cx->tempPool, nb); - if (!ts) { - JS_ReportOutOfMemory(cx); - return NULL; - } - memset(ts, 0, nb); - ts->lineno = 1; - ts->linebuf.base = ts->linebuf.limit = ts->linebuf.ptr = (jschar *)(ts + 1); - ts->userbuf.base = (jschar *)base; - ts->userbuf.limit = (jschar *)base + length; - ts->userbuf.ptr = (jschar *)base; - ts->tokenbuf.grow = GrowTokenBuf; - ts->tokenbuf.data = cx; - ts->listener = cx->runtime->sourceHandler; - ts->listenerData = cx->runtime->sourceHandlerData; - return ts; -} - -JS_FRIEND_API(JSTokenStream *) -js_NewFileTokenStream(JSContext *cx, const char *filename, FILE *defaultfp) -{ - jschar *base; - JSTokenStream *ts; - FILE *file; - - JS_ARENA_ALLOCATE_CAST(base, jschar *, &cx->tempPool, - JS_LINE_LIMIT * sizeof(jschar)); - if (!base) - return NULL; - ts = js_NewBufferTokenStream(cx, base, JS_LINE_LIMIT); - if (!ts) - return NULL; - if (!filename || strcmp(filename, "-") == 0) { - file = defaultfp; - } else { - file = fopen(filename, "r"); - if (!file) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_OPEN, - filename, "No such file or directory"); - return NULL; - } - } - ts->userbuf.ptr = ts->userbuf.limit; - ts->file = file; - ts->filename = filename; - return ts; -} - -JS_FRIEND_API(JSBool) -js_CloseTokenStream(JSContext *cx, JSTokenStream *ts) -{ - if (ts->flags & TSF_OWNFILENAME) - JS_free(cx, (void *) ts->filename); - if (ts->principals) - JSPRINCIPALS_DROP(cx, ts->principals); - return !ts->file || fclose(ts->file) == 0; -} - -JS_FRIEND_API(int) -js_fgets(char *buf, int size, FILE *file) -{ - int n, i, c; - JSBool crflag; - - n = size - 1; - if (n < 0) - return -1; - - crflag = JS_FALSE; - for (i = 0; i < n && (c = getc(file)) != EOF; i++) { - buf[i] = c; - if (c == '\n') { /* any \n ends a line */ - i++; /* keep the \n; we know there is room for \0 */ - break; - } - if (crflag) { /* \r not followed by \n ends line at the \r */ - ungetc(c, file); - break; /* and overwrite c in buf with \0 */ - } - crflag = (c == '\r'); - } - - buf[i] = '\0'; - return i; -} - -static int32 -GetChar(JSTokenStream *ts) -{ - int32 c; - ptrdiff_t i, j, len, olen; - JSBool crflag; - char cbuf[JS_LINE_LIMIT]; - jschar *ubuf, *nl; - - if (ts->ungetpos != 0) { - c = ts->ungetbuf[--ts->ungetpos]; - } else { - do { - if (ts->linebuf.ptr == ts->linebuf.limit) { - len = PTRDIFF(ts->userbuf.limit, ts->userbuf.ptr, jschar); - if (len <= 0) { - if (!ts->file) { - ts->flags |= TSF_EOF; - return EOF; - } - - /* Fill ts->userbuf so that \r and \r\n convert to \n. */ - crflag = (ts->flags & TSF_CRFLAG) != 0; - len = js_fgets(cbuf, JS_LINE_LIMIT - crflag, ts->file); - if (len <= 0) { - ts->flags |= TSF_EOF; - return EOF; - } - olen = len; - ubuf = ts->userbuf.base; - i = 0; - if (crflag) { - ts->flags &= ~TSF_CRFLAG; - if (cbuf[0] != '\n') { - ubuf[i++] = '\n'; - len++; - ts->linepos--; - } - } - for (j = 0; i < len; i++, j++) - ubuf[i] = (jschar) (unsigned char) cbuf[j]; - ts->userbuf.limit = ubuf + len; - ts->userbuf.ptr = ubuf; - } - if (ts->listener) { - ts->listener(ts->filename, ts->lineno, ts->userbuf.ptr, len, - &ts->listenerTSData, ts->listenerData); - } - - nl = ts->saveEOL; - if (!nl) { - /* - * Any one of \n, \r, or \r\n ends a line (the longest - * match wins). Also allow the Unicode line and paragraph - * separators. - */ - for (nl = ts->userbuf.ptr; nl < ts->userbuf.limit; nl++) { - /* - * Try to prevent value-testing on most characters by - * filtering out characters that aren't 000x or 202x. - */ - if ((*nl & 0xDFD0) == 0) { - if (*nl == '\n') - break; - if (*nl == '\r') { - if (nl + 1 < ts->userbuf.limit && nl[1] == '\n') - nl++; - break; - } - if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR) - break; - } - } - } - - /* - * If there was a line terminator, copy thru it into linebuf. - * Else copy JS_LINE_LIMIT-1 bytes into linebuf. - */ - if (nl < ts->userbuf.limit) - len = PTRDIFF(nl, ts->userbuf.ptr, jschar) + 1; - if (len >= JS_LINE_LIMIT) { - len = JS_LINE_LIMIT - 1; - ts->saveEOL = nl; - } else { - ts->saveEOL = NULL; - } - js_strncpy(ts->linebuf.base, ts->userbuf.ptr, len); - ts->userbuf.ptr += len; - olen = len; - - /* - * Make sure linebuf contains \n for EOL (don't do this in - * userbuf because the user's string might be readonly). - */ - if (nl < ts->userbuf.limit) { - if (*nl == '\r') { - if (ts->linebuf.base[len-1] == '\r') { - /* - * Does the line segment end in \r? We must check - * for a \n at the front of the next segment before - * storing a \n into linebuf. This case matters - * only when we're reading from a file. - */ - if (nl + 1 == ts->userbuf.limit && ts->file) { - len--; - ts->flags |= TSF_CRFLAG; /* clear NLFLAG? */ - if (len == 0) { - /* - * This can happen when a segment ends in - * \r\r. Start over. ptr == limit in this - * case, so we'll fall into buffer-filling - * code. - */ - return GetChar(ts); - } - } else { - ts->linebuf.base[len-1] = '\n'; - } - } - } else if (*nl == '\n') { - if (nl > ts->userbuf.base && - nl[-1] == '\r' && - ts->linebuf.base[len-2] == '\r') { - len--; - JS_ASSERT(ts->linebuf.base[len] == '\n'); - ts->linebuf.base[len-1] = '\n'; - } - } else if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR) { - ts->linebuf.base[len-1] = '\n'; - } - } - - /* Reset linebuf based on adjusted segment length. */ - ts->linebuf.limit = ts->linebuf.base + len; - ts->linebuf.ptr = ts->linebuf.base; - - /* Update position of linebuf within physical userbuf line. */ - if (!(ts->flags & TSF_NLFLAG)) - ts->linepos += ts->linelen; - else - ts->linepos = 0; - if (ts->linebuf.limit[-1] == '\n') - ts->flags |= TSF_NLFLAG; - else - ts->flags &= ~TSF_NLFLAG; - - /* Update linelen from original segment length. */ - ts->linelen = olen; - } - c = *ts->linebuf.ptr++; - } while (JS_ISFORMAT(c)); - } - if (c == '\n') - ts->lineno++; - return c; -} - -static void -UngetChar(JSTokenStream *ts, int32 c) -{ - if (c == EOF) - return; - JS_ASSERT(ts->ungetpos < sizeof ts->ungetbuf / sizeof ts->ungetbuf[0]); - if (c == '\n') - ts->lineno--; - ts->ungetbuf[ts->ungetpos++] = (jschar)c; -} - -static int32 -PeekChar(JSTokenStream *ts) -{ - int32 c; - - c = GetChar(ts); - UngetChar(ts, c); - return c; -} - -/* - * Peek n chars ahead into ts. Return true if n chars were read, false if - * there weren't enough characters in the input stream. This function cannot - * be used to peek into or past a newline. - */ -static JSBool -PeekChars(JSTokenStream *ts, intN n, jschar *cp) -{ - intN i, j; - int32 c; - - for (i = 0; i < n; i++) { - c = GetChar(ts); - if (c == EOF) - break; - if (c == '\n') { - UngetChar(ts, c); - break; - } - cp[i] = (jschar)c; - } - for (j = i - 1; j >= 0; j--) - UngetChar(ts, cp[j]); - return i == n; -} - -static void -SkipChars(JSTokenStream *ts, intN n) -{ - while (--n >= 0) - GetChar(ts); -} - -static JSBool -MatchChar(JSTokenStream *ts, int32 expect) -{ - int32 c; - - c = GetChar(ts); - if (c == expect) - return JS_TRUE; - UngetChar(ts, c); - return JS_FALSE; -} - -static JSBool -ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags, - uintN errorNumber, JSErrorReport *report, - JSBool charArgs, va_list ap) -{ - JSTempValueRooter linetvr; - JSString *linestr = NULL; - JSTokenStream *ts = NULL; - JSCodeGenerator *cg = NULL; - JSParseNode *pn = NULL; - JSErrorReporter onError; - JSTokenPos *tp; - JSStackFrame *fp; - uintN index; - char *message; - JSBool warning; - - memset(report, 0, sizeof (struct JSErrorReport)); - report->flags = flags; - report->errorNumber = errorNumber; - message = NULL; - - if (!js_ExpandErrorArguments(cx, js_GetErrorMessage, NULL, - errorNumber, &message, report, &warning, - charArgs, ap)) { - return JS_FALSE; - } - - JS_PUSH_TEMP_ROOT_STRING(cx, NULL, &linetvr); - - switch (flags & JSREPORT_HANDLE) { - case JSREPORT_TS: - ts = handle; - break; - case JSREPORT_CG: - cg = handle; - break; - case JSREPORT_PN: - pn = handle; - ts = pn->pn_ts; - break; - } - - JS_ASSERT(!ts || ts->linebuf.limit < ts->linebuf.base + JS_LINE_LIMIT); - /* - * We are typically called with non-null ts and null cg from jsparse.c. - * We can be called with null ts from the regexp compilation functions. - * The code generator (jsemit.c) may pass null ts and non-null cg. - */ - do { - if (ts) { - report->filename = ts->filename; - if (pn) { - report->lineno = pn->pn_pos.begin.lineno; - if (report->lineno != ts->lineno) - break; - } - report->lineno = ts->lineno; - linestr = js_NewStringCopyN(cx, ts->linebuf.base, - PTRDIFF(ts->linebuf.limit, - ts->linebuf.base, - jschar), - 0); - linetvr.u.string = linestr; - report->linebuf = linestr - ? JS_GetStringBytes(linestr) - : NULL; - tp = &ts->tokens[(ts->cursor+ts->lookahead) & NTOKENS_MASK].pos; - if (pn) - tp = &pn->pn_pos; - - /* - * FIXME: What should instead happen here is that we should - * find error-tokens in userbuf, if !ts->file. That will - * allow us to deliver a more helpful error message, which - * includes all or part of the bad string or bad token. The - * code here yields something that looks truncated. - * See https://bugzilla.mozilla.org/show_bug.cgi?id=352970 - */ - index = 0; - if (tp->begin.lineno == tp->end.lineno) { - if (tp->begin.index < ts->linepos) - break; - - index = tp->begin.index - ts->linepos; - } - - report->tokenptr = linestr ? report->linebuf + index : NULL; - report->uclinebuf = linestr ? JS_GetStringChars(linestr) : NULL; - report->uctokenptr = linestr ? report->uclinebuf + index : NULL; - break; - } - - if (cg) { - report->filename = cg->filename; - report->lineno = CG_CURRENT_LINE(cg); - break; - } - - /* - * If we can't find out where the error was based on the current - * frame, see if the next frame has a script/pc combo we can use. - */ - for (fp = cx->fp; fp; fp = fp->down) { - if (fp->script && fp->pc) { - report->filename = fp->script->filename; - report->lineno = js_PCToLineNumber(cx, fp->script, fp->pc); - break; - } - } - } while (0); - - /* - * If there's a runtime exception type associated with this error - * number, set that as the pending exception. For errors occuring at - * compile time, this is very likely to be a JSEXN_SYNTAXERR. - * - * If an exception is thrown but not caught, the JSREPORT_EXCEPTION - * flag will be set in report.flags. Proper behavior for an error - * reporter is to ignore a report with this flag for all but top-level - * compilation errors. The exception will remain pending, and so long - * as the non-top-level "load", "eval", or "compile" native function - * returns false, the top-level reporter will eventually receive the - * uncaught exception report. - * - * XXX it'd probably be best if there was only one call to this - * function, but there seem to be two error reporter call points. - */ - onError = cx->errorReporter; - - /* - * Try to raise an exception only if there isn't one already set -- - * otherwise the exception will describe the last compile-time error, - * which is likely spurious. - */ - if (!ts || !(ts->flags & TSF_ERROR)) { - if (js_ErrorToException(cx, message, report)) - onError = NULL; - } - - /* - * Suppress any compile-time errors that don't occur at the top level. - * This may still fail, as interplevel may be zero in contexts where we - * don't really want to call the error reporter, as when js is called - * by other code which could catch the error. - */ - if (cx->interpLevel != 0 && !JSREPORT_IS_WARNING(flags)) - onError = NULL; - - if (onError) { - JSDebugErrorHook hook = cx->runtime->debugErrorHook; - - /* - * If debugErrorHook is present then we give it a chance to veto - * sending the error on to the regular error reporter. - */ - if (hook && !hook(cx, message, report, - cx->runtime->debugErrorHookData)) { - onError = NULL; - } - } - if (onError) - (*onError)(cx, message, report); - - if (message) - JS_free(cx, message); - if (report->ucmessage) - JS_free(cx, (void *)report->ucmessage); - - JS_POP_TEMP_ROOT(cx, &linetvr); - - if (ts && !JSREPORT_IS_WARNING(flags)) { - /* Set the error flag to suppress spurious reports. */ - ts->flags |= TSF_ERROR; - } - - return warning; -} - -JSBool -js_ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags, - uintN errorNumber, ...) -{ - va_list ap; - JSErrorReport report; - JSBool warning; - - if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx)) - return JS_TRUE; - - va_start(ap, errorNumber); - warning = ReportCompileErrorNumber(cx, handle, flags, errorNumber, - &report, JS_TRUE, ap); - va_end(ap); - - /* - * We have to do this here because js_ReportCompileErrorNumberUC doesn't - * need to do this. - */ - if (report.messageArgs) { - int i = 0; - while (report.messageArgs[i]) - JS_free(cx, (void *)report.messageArgs[i++]); - JS_free(cx, (void *)report.messageArgs); - } - - return warning; -} - -JSBool -js_ReportCompileErrorNumberUC(JSContext *cx, void *handle, uintN flags, - uintN errorNumber, ...) -{ - va_list ap; - JSErrorReport report; - JSBool warning; - - if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx)) - return JS_TRUE; - - va_start(ap, errorNumber); - warning = ReportCompileErrorNumber(cx, handle, flags, errorNumber, - &report, JS_FALSE, ap); - va_end(ap); - - if (report.messageArgs) - JS_free(cx, (void *)report.messageArgs); - - return warning; -} - -static JSBool -GrowStringBuffer(JSStringBuffer *sb, size_t newlength) -{ - ptrdiff_t offset; - jschar *bp; - - offset = PTRDIFF(sb->ptr, sb->base, jschar); - JS_ASSERT(offset >= 0); - newlength += offset + 1; - if ((size_t)offset < newlength && newlength < ~(size_t)0 / sizeof(jschar)) - bp = realloc(sb->base, newlength * sizeof(jschar)); - else - bp = NULL; - if (!bp) { - free(sb->base); - sb->base = STRING_BUFFER_ERROR_BASE; - return JS_FALSE; - } - sb->base = bp; - sb->ptr = bp + offset; - sb->limit = bp + newlength - 1; - return JS_TRUE; -} - -static void -FreeStringBuffer(JSStringBuffer *sb) -{ - JS_ASSERT(STRING_BUFFER_OK(sb)); - if (sb->base) - free(sb->base); -} - -void -js_InitStringBuffer(JSStringBuffer *sb) -{ - sb->base = sb->limit = sb->ptr = NULL; - sb->data = NULL; - sb->grow = GrowStringBuffer; - sb->free = FreeStringBuffer; -} - -void -js_FinishStringBuffer(JSStringBuffer *sb) -{ - sb->free(sb); -} - -#define ENSURE_STRING_BUFFER(sb,n) \ - ((sb)->ptr + (n) <= (sb)->limit || sb->grow(sb, n)) - -static void -FastAppendChar(JSStringBuffer *sb, jschar c) -{ - if (!STRING_BUFFER_OK(sb)) - return; - if (!ENSURE_STRING_BUFFER(sb, 1)) - return; - *sb->ptr++ = c; -} - -void -js_AppendChar(JSStringBuffer *sb, jschar c) -{ - jschar *bp; - - if (!STRING_BUFFER_OK(sb)) - return; - if (!ENSURE_STRING_BUFFER(sb, 1)) - return; - bp = sb->ptr; - *bp++ = c; - *bp = 0; - sb->ptr = bp; -} - -#if JS_HAS_XML_SUPPORT - -void -js_RepeatChar(JSStringBuffer *sb, jschar c, uintN count) -{ - jschar *bp; - - if (!STRING_BUFFER_OK(sb) || count == 0) - return; - if (!ENSURE_STRING_BUFFER(sb, count)) - return; - for (bp = sb->ptr; count; --count) - *bp++ = c; - *bp = 0; - sb->ptr = bp; -} - -void -js_AppendCString(JSStringBuffer *sb, const char *asciiz) -{ - size_t length; - jschar *bp; - - if (!STRING_BUFFER_OK(sb) || *asciiz == '\0') - return; - length = strlen(asciiz); - if (!ENSURE_STRING_BUFFER(sb, length)) - return; - for (bp = sb->ptr; length; --length) - *bp++ = (jschar) *asciiz++; - *bp = 0; - sb->ptr = bp; -} - -void -js_AppendJSString(JSStringBuffer *sb, JSString *str) -{ - size_t length; - jschar *bp; - - if (!STRING_BUFFER_OK(sb)) - return; - length = JSSTRING_LENGTH(str); - if (length == 0 || !ENSURE_STRING_BUFFER(sb, length)) - return; - bp = sb->ptr; - js_strncpy(bp, JSSTRING_CHARS(str), length); - bp += length; - *bp = 0; - sb->ptr = bp; -} - -static JSBool -GetXMLEntity(JSContext *cx, JSTokenStream *ts) -{ - ptrdiff_t offset, length, i; - int32 c, d; - JSBool ispair; - jschar *bp, digit; - char *bytes; - JSErrNum msg; - - /* Put the entity, including the '&' already scanned, in ts->tokenbuf. */ - offset = PTRDIFF(ts->tokenbuf.ptr, ts->tokenbuf.base, jschar); - FastAppendChar(&ts->tokenbuf, '&'); - while ((c = GetChar(ts)) != ';') { - if (c == EOF || c == '\n') { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_END_OF_XML_ENTITY); - return JS_FALSE; - } - FastAppendChar(&ts->tokenbuf, (jschar) c); - } - - /* Let length be the number of jschars after the '&', including the ';'. */ - length = PTRDIFF(ts->tokenbuf.ptr, ts->tokenbuf.base, jschar) - offset; - bp = ts->tokenbuf.base + offset; - c = d = 0; - ispair = JS_FALSE; - if (length > 2 && bp[1] == '#') { - /* Match a well-formed XML Character Reference. */ - i = 2; - if (length > 3 && JS_TOLOWER(bp[i]) == 'x') { - if (length > 9) /* at most 6 hex digits allowed */ - goto badncr; - while (++i < length) { - digit = bp[i]; - if (!JS7_ISHEX(digit)) - goto badncr; - c = (c << 4) + JS7_UNHEX(digit); - } - } else { - while (i < length) { - digit = bp[i++]; - if (!JS7_ISDEC(digit)) - goto badncr; - c = (c * 10) + JS7_UNDEC(digit); - if (c < 0) - goto badncr; - } - } - - if (0x10000 <= c && c <= 0x10FFFF) { - /* Form a surrogate pair (c, d) -- c is the high surrogate. */ - d = 0xDC00 + (c & 0x3FF); - c = 0xD7C0 + (c >> 10); - ispair = JS_TRUE; - } else { - /* Enforce the http://www.w3.org/TR/REC-xml/#wf-Legalchar WFC. */ - if (c != 0x9 && c != 0xA && c != 0xD && - !(0x20 <= c && c <= 0xD7FF) && - !(0xE000 <= c && c <= 0xFFFD)) { - goto badncr; - } - } - } else { - /* Try to match one of the five XML 1.0 predefined entities. */ - switch (length) { - case 3: - if (bp[2] == 't') { - if (bp[1] == 'l') - c = '<'; - else if (bp[1] == 'g') - c = '>'; - } - break; - case 4: - if (bp[1] == 'a' && bp[2] == 'm' && bp[3] == 'p') - c = '&'; - break; - case 5: - if (bp[3] == 'o') { - if (bp[1] == 'a' && bp[2] == 'p' && bp[4] == 's') - c = '\''; - else if (bp[1] == 'q' && bp[2] == 'u' && bp[4] == 't') - c = '"'; - } - break; - } - if (c == 0) { - msg = JSMSG_UNKNOWN_XML_ENTITY; - goto bad; - } - } - - /* If we matched, retract ts->tokenbuf and store the entity's value. */ - *bp++ = (jschar) c; - if (ispair) - *bp++ = (jschar) d; - *bp = 0; - ts->tokenbuf.ptr = bp; - return JS_TRUE; - -badncr: - msg = JSMSG_BAD_XML_NCR; -bad: - /* No match: throw a TypeError per ECMA-357 10.3.2.1 step 8(a). */ - bytes = js_DeflateString(cx, bp + 1, - PTRDIFF(ts->tokenbuf.ptr, bp, jschar) - 1); - if (bytes) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - msg, bytes); - JS_free(cx, bytes); - } - return JS_FALSE; -} - -#endif /* JS_HAS_XML_SUPPORT */ - -JSTokenType -js_PeekToken(JSContext *cx, JSTokenStream *ts) -{ - JSTokenType tt; - - if (ts->lookahead != 0) { - tt = ts->tokens[(ts->cursor + ts->lookahead) & NTOKENS_MASK].type; - } else { - tt = js_GetToken(cx, ts); - js_UngetToken(ts); - } - return tt; -} - -JSTokenType -js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts) -{ - JSTokenType tt; - - if (!ON_CURRENT_LINE(ts, CURRENT_TOKEN(ts).pos)) - return TOK_EOL; - ts->flags |= TSF_NEWLINES; - tt = js_PeekToken(cx, ts); - ts->flags &= ~TSF_NEWLINES; - return tt; -} - -/* - * We have encountered a '\': check for a Unicode escape sequence after it, - * returning the character code value if we found a Unicode escape sequence. - * Otherwise, non-destructively return the original '\'. - */ -static int32 -GetUnicodeEscape(JSTokenStream *ts) -{ - jschar cp[5]; - int32 c; - - if (PeekChars(ts, 5, cp) && cp[0] == 'u' && - JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]) && - JS7_ISHEX(cp[3]) && JS7_ISHEX(cp[4])) - { - c = (((((JS7_UNHEX(cp[1]) << 4) - + JS7_UNHEX(cp[2])) << 4) - + JS7_UNHEX(cp[3])) << 4) - + JS7_UNHEX(cp[4]); - SkipChars(ts, 5); - return c; - } - return '\\'; -} - -static JSToken * -NewToken(JSTokenStream *ts, ptrdiff_t adjust) -{ - JSToken *tp; - - ts->cursor = (ts->cursor + 1) & NTOKENS_MASK; - tp = &CURRENT_TOKEN(ts); - tp->ptr = ts->linebuf.ptr + adjust; - tp->pos.begin.index = ts->linepos + - PTRDIFF(tp->ptr, ts->linebuf.base, jschar) - - ts->ungetpos; - tp->pos.begin.lineno = tp->pos.end.lineno = (uint16)ts->lineno; - return tp; -} - -JSTokenType -js_GetToken(JSContext *cx, JSTokenStream *ts) -{ - JSTokenType tt; - int32 c, qc; - JSToken *tp; - JSAtom *atom; - JSBool hadUnicodeEscape; - const struct keyword *kw; - -#define INIT_TOKENBUF() (ts->tokenbuf.ptr = ts->tokenbuf.base) -#define TOKENBUF_LENGTH() PTRDIFF(ts->tokenbuf.ptr, ts->tokenbuf.base, jschar) -#define TOKENBUF_OK() STRING_BUFFER_OK(&ts->tokenbuf) -#define TOKENBUF_TO_ATOM() (TOKENBUF_OK() \ - ? js_AtomizeChars(cx, \ - TOKENBUF_BASE(), \ - TOKENBUF_LENGTH(), \ - 0) \ - : NULL) -#define ADD_TO_TOKENBUF(c) FastAppendChar(&ts->tokenbuf, (jschar) (c)) - -/* The following 4 macros should only be used when TOKENBUF_OK() is true. */ -#define TOKENBUF_BASE() (ts->tokenbuf.base) -#define TOKENBUF_CHAR(i) (ts->tokenbuf.base[i]) -#define TRIM_TOKENBUF(i) (ts->tokenbuf.ptr = ts->tokenbuf.base + i) -#define NUL_TERM_TOKENBUF() (*ts->tokenbuf.ptr = 0) - - /* Check for a pushed-back token resulting from mismatching lookahead. */ - while (ts->lookahead != 0) { - JS_ASSERT(!(ts->flags & TSF_XMLTEXTMODE)); - ts->lookahead--; - ts->cursor = (ts->cursor + 1) & NTOKENS_MASK; - tt = CURRENT_TOKEN(ts).type; - if (tt != TOK_EOL || (ts->flags & TSF_NEWLINES)) - return tt; - } - - /* If there was a fatal error, keep returning TOK_ERROR. */ - if (ts->flags & TSF_ERROR) - return TOK_ERROR; - -#if JS_HAS_XML_SUPPORT - if (ts->flags & TSF_XMLTEXTMODE) { - tt = TOK_XMLSPACE; /* veto if non-space, return TOK_XMLTEXT */ - tp = NewToken(ts, 0); - INIT_TOKENBUF(); - qc = (ts->flags & TSF_XMLONLYMODE) ? '<' : '{'; - - while ((c = GetChar(ts)) != qc && c != '<' && c != EOF) { - if (c == '&' && qc == '<') { - if (!GetXMLEntity(cx, ts)) - goto error; - tt = TOK_XMLTEXT; - continue; - } - - if (!JS_ISXMLSPACE(c)) - tt = TOK_XMLTEXT; - ADD_TO_TOKENBUF(c); - } - UngetChar(ts, c); - - if (TOKENBUF_LENGTH() == 0) { - atom = NULL; - } else { - atom = TOKENBUF_TO_ATOM(); - if (!atom) - goto error; - } - tp->pos.end.lineno = (uint16)ts->lineno; - tp->t_op = JSOP_STRING; - tp->t_atom = atom; - goto out; - } - - if (ts->flags & TSF_XMLTAGMODE) { - tp = NewToken(ts, 0); - c = GetChar(ts); - if (JS_ISXMLSPACE(c)) { - do { - c = GetChar(ts); - } while (JS_ISXMLSPACE(c)); - UngetChar(ts, c); - tt = TOK_XMLSPACE; - goto out; - } - - if (c == EOF) { - tt = TOK_EOF; - goto out; - } - - INIT_TOKENBUF(); - if (JS_ISXMLNSSTART(c)) { - JSBool sawColon = JS_FALSE; - - ADD_TO_TOKENBUF(c); - while ((c = GetChar(ts)) != EOF && JS_ISXMLNAME(c)) { - if (c == ':') { - int nextc; - - if (sawColon || - (nextc = PeekChar(ts), - ((ts->flags & TSF_XMLONLYMODE) || nextc != '{') && - !JS_ISXMLNAME(nextc))) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | - JSREPORT_ERROR, - JSMSG_BAD_XML_QNAME); - goto error; - } - sawColon = JS_TRUE; - } - - ADD_TO_TOKENBUF(c); - } - - UngetChar(ts, c); - atom = TOKENBUF_TO_ATOM(); - if (!atom) - goto error; - tp->t_op = JSOP_STRING; - tp->t_atom = atom; - tt = TOK_XMLNAME; - goto out; - } - - switch (c) { - case '{': - if (ts->flags & TSF_XMLONLYMODE) - goto bad_xml_char; - tt = TOK_LC; - goto out; - - case '=': - tt = TOK_ASSIGN; - goto out; - - case '"': - case '\'': - qc = c; - while ((c = GetChar(ts)) != qc) { - if (c == EOF) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_UNTERMINATED_STRING); - goto error; - } - - /* - * XML attribute values are double-quoted when pretty-printed, - * so escape " if it is expressed directly in a single-quoted - * attribute value. - */ - if (c == '"' && !(ts->flags & TSF_XMLONLYMODE)) { - JS_ASSERT(qc == '\''); - js_AppendCString(&ts->tokenbuf, js_quot_entity_str); - continue; - } - - if (c == '&' && (ts->flags & TSF_XMLONLYMODE)) { - if (!GetXMLEntity(cx, ts)) - goto error; - continue; - } - - ADD_TO_TOKENBUF(c); - } - atom = TOKENBUF_TO_ATOM(); - if (!atom) - goto error; - tp->pos.end.lineno = (uint16)ts->lineno; - tp->t_op = JSOP_STRING; - tp->t_atom = atom; - tt = TOK_XMLATTR; - goto out; - - case '>': - tt = TOK_XMLTAGC; - goto out; - - case '/': - if (MatchChar(ts, '>')) { - tt = TOK_XMLPTAGC; - goto out; - } - /* FALL THROUGH */ - - bad_xml_char: - default: - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_XML_CHARACTER); - goto error; - } - /* NOTREACHED */ - } -#endif /* JS_HAS_XML_SUPPORT */ - -retry: - do { - c = GetChar(ts); - if (c == '\n') { - ts->flags &= ~TSF_DIRTYLINE; - if (ts->flags & TSF_NEWLINES) - break; - } - } while (JS_ISSPACE(c)); - - tp = NewToken(ts, -1); - if (c == EOF) { - tt = TOK_EOF; - goto out; - } - - hadUnicodeEscape = JS_FALSE; - if (JS_ISIDSTART(c) || - (c == '\\' && - (c = GetUnicodeEscape(ts), - hadUnicodeEscape = JS_ISIDSTART(c)))) { - INIT_TOKENBUF(); - for (;;) { - ADD_TO_TOKENBUF(c); - c = GetChar(ts); - if (c == '\\') { - c = GetUnicodeEscape(ts); - if (!JS_ISIDENT(c)) - break; - hadUnicodeEscape = JS_TRUE; - } else { - if (!JS_ISIDENT(c)) - break; - } - } - UngetChar(ts, c); - - /* - * Check for keywords unless we saw Unicode escape or parser asks - * to ignore keywords. - */ - if (!hadUnicodeEscape && - !(ts->flags & TSF_KEYWORD_IS_NAME) && - TOKENBUF_OK() && - (kw = FindKeyword(TOKENBUF_BASE(), TOKENBUF_LENGTH()))) { - if (kw->tokentype == TOK_RESERVED) { - if (!js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | - JSREPORT_WARNING | - JSREPORT_STRICT, - JSMSG_RESERVED_ID, - kw->chars)) { - goto error; - } - } else if (kw->version <= JSVERSION_NUMBER(cx)) { - tt = kw->tokentype; - tp->t_op = (JSOp) kw->op; - goto out; - } - } - - atom = TOKENBUF_TO_ATOM(); - if (!atom) - goto error; - tp->t_op = JSOP_NAME; - tp->t_atom = atom; - tt = TOK_NAME; - goto out; - } - - if (JS7_ISDEC(c) || (c == '.' && JS7_ISDEC(PeekChar(ts)))) { - jsint radix; - const jschar *endptr; - jsdouble dval; - - radix = 10; - INIT_TOKENBUF(); - - if (c == '0') { - ADD_TO_TOKENBUF(c); - c = GetChar(ts); - if (JS_TOLOWER(c) == 'x') { - ADD_TO_TOKENBUF(c); - c = GetChar(ts); - radix = 16; - } else if (JS7_ISDEC(c)) { - radix = 8; - } - } - - while (JS7_ISHEX(c)) { - if (radix < 16) { - if (JS7_ISLET(c)) - break; - - /* - * We permit 08 and 09 as decimal numbers, which makes our - * behaviour a superset of the ECMA numeric grammar. We might - * not always be so permissive, so we warn about it. - */ - if (radix == 8 && c >= '8') { - if (!js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | - JSREPORT_WARNING, - JSMSG_BAD_OCTAL, - c == '8' ? "08" : "09")) { - goto error; - } - radix = 10; - } - } - ADD_TO_TOKENBUF(c); - c = GetChar(ts); - } - - if (radix == 10 && (c == '.' || JS_TOLOWER(c) == 'e')) { - if (c == '.') { - do { - ADD_TO_TOKENBUF(c); - c = GetChar(ts); - } while (JS7_ISDEC(c)); - } - if (JS_TOLOWER(c) == 'e') { - ADD_TO_TOKENBUF(c); - c = GetChar(ts); - if (c == '+' || c == '-') { - ADD_TO_TOKENBUF(c); - c = GetChar(ts); - } - if (!JS7_ISDEC(c)) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_MISSING_EXPONENT); - goto error; - } - do { - ADD_TO_TOKENBUF(c); - c = GetChar(ts); - } while (JS7_ISDEC(c)); - } - } - - /* Put back the next char and NUL-terminate tokenbuf for js_strto*. */ - UngetChar(ts, c); - ADD_TO_TOKENBUF(0); - - if (!TOKENBUF_OK()) - goto error; - if (radix == 10) { - if (!js_strtod(cx, TOKENBUF_BASE(), &endptr, &dval)) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_OUT_OF_MEMORY); - goto error; - } - } else { - if (!js_strtointeger(cx, TOKENBUF_BASE(), &endptr, radix, &dval)) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_OUT_OF_MEMORY); - goto error; - } - } - tp->t_dval = dval; - tt = TOK_NUMBER; - goto out; - } - - if (c == '"' || c == '\'') { - qc = c; - INIT_TOKENBUF(); - while ((c = GetChar(ts)) != qc) { - if (c == '\n' || c == EOF) { - UngetChar(ts, c); - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_UNTERMINATED_STRING); - goto error; - } - if (c == '\\') { - switch (c = GetChar(ts)) { - case 'b': c = '\b'; break; - case 'f': c = '\f'; break; - case 'n': c = '\n'; break; - case 'r': c = '\r'; break; - case 't': c = '\t'; break; - case 'v': c = '\v'; break; - - default: - if ('0' <= c && c < '8') { - int32 val = JS7_UNDEC(c); - - c = PeekChar(ts); - if ('0' <= c && c < '8') { - val = 8 * val + JS7_UNDEC(c); - GetChar(ts); - c = PeekChar(ts); - if ('0' <= c && c < '8') { - int32 save = val; - val = 8 * val + JS7_UNDEC(c); - if (val <= 0377) - GetChar(ts); - else - val = save; - } - } - - c = (jschar)val; - } else if (c == 'u') { - jschar cp[4]; - if (PeekChars(ts, 4, cp) && - JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1]) && - JS7_ISHEX(cp[2]) && JS7_ISHEX(cp[3])) { - c = (((((JS7_UNHEX(cp[0]) << 4) - + JS7_UNHEX(cp[1])) << 4) - + JS7_UNHEX(cp[2])) << 4) - + JS7_UNHEX(cp[3]); - SkipChars(ts, 4); - } - } else if (c == 'x') { - jschar cp[2]; - if (PeekChars(ts, 2, cp) && - JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1])) { - c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]); - SkipChars(ts, 2); - } - } else if (c == '\n' && JS_VERSION_IS_ECMA(cx)) { - /* ECMA follows C by removing escaped newlines. */ - continue; - } - break; - } - } - ADD_TO_TOKENBUF(c); - } - atom = TOKENBUF_TO_ATOM(); - if (!atom) - goto error; - tp->pos.end.lineno = (uint16)ts->lineno; - tp->t_op = JSOP_STRING; - tp->t_atom = atom; - tt = TOK_STRING; - goto out; - } - - switch (c) { - case '\n': tt = TOK_EOL; goto eol_out; - case ';': tt = TOK_SEMI; break; - case '[': tt = TOK_LB; break; - case ']': tt = TOK_RB; break; - case '{': tt = TOK_LC; break; - case '}': tt = TOK_RC; break; - case '(': tt = TOK_LP; break; - case ')': tt = TOK_RP; break; - case ',': tt = TOK_COMMA; break; - case '?': tt = TOK_HOOK; break; - - case '.': -#if JS_HAS_XML_SUPPORT - if (MatchChar(ts, c)) - tt = TOK_DBLDOT; - else -#endif - tt = TOK_DOT; - break; - - case ':': -#if JS_HAS_XML_SUPPORT - if (MatchChar(ts, c)) { - tt = TOK_DBLCOLON; - break; - } -#endif - /* - * Default so compiler can modify to JSOP_GETTER if 'p getter: v' in an - * object initializer, likewise for setter. - */ - tp->t_op = JSOP_NOP; - tt = TOK_COLON; - break; - - case '|': - if (MatchChar(ts, c)) { - tt = TOK_OR; - } else if (MatchChar(ts, '=')) { - tp->t_op = JSOP_BITOR; - tt = TOK_ASSIGN; - } else { - tt = TOK_BITOR; - } - break; - - case '^': - if (MatchChar(ts, '=')) { - tp->t_op = JSOP_BITXOR; - tt = TOK_ASSIGN; - } else { - tt = TOK_BITXOR; - } - break; - - case '&': - if (MatchChar(ts, c)) { - tt = TOK_AND; - } else if (MatchChar(ts, '=')) { - tp->t_op = JSOP_BITAND; - tt = TOK_ASSIGN; - } else { - tt = TOK_BITAND; - } - break; - - case '=': - if (MatchChar(ts, c)) { - tp->t_op = MatchChar(ts, c) ? JSOP_NEW_EQ : (JSOp)cx->jsop_eq; - tt = TOK_EQOP; - } else { - tp->t_op = JSOP_NOP; - tt = TOK_ASSIGN; - } - break; - - case '!': - if (MatchChar(ts, '=')) { - tp->t_op = MatchChar(ts, '=') ? JSOP_NEW_NE : (JSOp)cx->jsop_ne; - tt = TOK_EQOP; - } else { - tp->t_op = JSOP_NOT; - tt = TOK_UNARYOP; - } - break; - -#if JS_HAS_XML_SUPPORT - case '@': - tt = TOK_AT; - break; -#endif - - case '<': -#if JS_HAS_XML_SUPPORT - /* - * After much testing, it's clear that Postel's advice to protocol - * designers ("be liberal in what you accept, and conservative in what - * you send") invites a natural-law repercussion for JS as "protocol": - * - * "If you are liberal in what you accept, others will utterly fail to - * be conservative in what they send." - * - * Which means you will get within every //-style comment unless we have to. So we set - * TSF_IN_HTML_COMMENT when a either on a clean line, or - * only if (ts->flags & TSF_IN_HTML_COMMENT), in a //-style comment. - * - * This still works as before given a malformed comment hiding hack such as: - * - * - * - * It does not cope with malformed comment hiding hacks where --> is hidden - * by C-style comments, or on a dirty line. Such cases are already broken. - */ -#define TSF_IN_HTML_COMMENT 0x2000 - -/* Ignore keywords and return TOK_NAME instead to the parser. */ -#define TSF_KEYWORD_IS_NAME 0x4000 - -/* Unicode separators that are treated as line terminators, in addition to \n, \r */ -#define LINE_SEPARATOR 0x2028 -#define PARA_SEPARATOR 0x2029 - -/* - * Create a new token stream, either from an input buffer or from a file. - * Return null on file-open or memory-allocation failure. - * - * NB: All of js_New{,Buffer,File}TokenStream() return a pointer to transient - * memory in the current context's temp pool. This memory is deallocated via - * JS_ARENA_RELEASE() after parsing is finished. - */ -extern JSTokenStream * -js_NewTokenStream(JSContext *cx, const jschar *base, size_t length, - const char *filename, uintN lineno, JSPrincipals *principals); - -extern JS_FRIEND_API(JSTokenStream *) -js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length); - -extern JS_FRIEND_API(JSTokenStream *) -js_NewFileTokenStream(JSContext *cx, const char *filename, FILE *defaultfp); - -extern JS_FRIEND_API(JSBool) -js_CloseTokenStream(JSContext *cx, JSTokenStream *ts); - -extern JS_FRIEND_API(int) -js_fgets(char *buf, int size, FILE *file); - -/* - * If the given char array forms JavaScript keyword, return corresponding - * token. Otherwise return TOK_EOF. - */ -extern JSTokenType -js_CheckKeyword(const jschar *chars, size_t length); - -#define js_IsKeyword(chars, length) \ - (js_CheckKeyword(chars, length) != TOK_EOF) - -/* - * Friend-exported API entry point to call a mapping function on each reserved - * identifier in the scanner's keyword table. - */ -extern JS_FRIEND_API(void) -js_MapKeywords(void (*mapfun)(const char *)); - -/* - * Report a compile-time error by its number, using ts or cg to show context. - * Return true for a warning, false for an error. - */ -extern JSBool -js_ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags, - uintN errorNumber, ...); - -extern JSBool -js_ReportCompileErrorNumberUC(JSContext *cx, void *handle, uintN flags, - uintN errorNumber, ...); - -/* Steal some JSREPORT_* bits (see jsapi.h) to tell handle's type. */ -#define JSREPORT_HANDLE 0x300 -#define JSREPORT_TS 0x000 -#define JSREPORT_CG 0x100 -#define JSREPORT_PN 0x200 - -/* - * Look ahead one token and return its type. - */ -extern JSTokenType -js_PeekToken(JSContext *cx, JSTokenStream *ts); - -extern JSTokenType -js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts); - -/* - * Get the next token from ts. - */ -extern JSTokenType -js_GetToken(JSContext *cx, JSTokenStream *ts); - -/* - * Push back the last scanned token onto ts. - */ -extern void -js_UngetToken(JSTokenStream *ts); - -/* - * Get the next token from ts if its type is tt. - */ -extern JSBool -js_MatchToken(JSContext *cx, JSTokenStream *ts, JSTokenType tt); - -JS_END_EXTERN_C - -#endif /* jsscan_h___ */ diff --git a/spidermonkey/src/jsscope.c b/spidermonkey/src/jsscope.c deleted file mode 100644 index 49b55a6..0000000 --- a/spidermonkey/src/jsscope.c +++ /dev/null @@ -1,1776 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS symbol tables. - */ -#include "jsstddef.h" -#include -#include -#include "jstypes.h" -#include "jsarena.h" -#include "jsbit.h" -#include "jsclist.h" -#include "jsdhash.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jsapi.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsdbgapi.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsscope.h" -#include "jsstr.h" - -JSScope * -js_GetMutableScope(JSContext *cx, JSObject *obj) -{ - JSScope *scope, *newscope; - - scope = OBJ_SCOPE(obj); - JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); - if (scope->object == obj) - return scope; - newscope = js_NewScope(cx, 0, scope->map.ops, LOCKED_OBJ_GET_CLASS(obj), - obj); - if (!newscope) - return NULL; - JS_LOCK_SCOPE(cx, newscope); - obj->map = js_HoldObjectMap(cx, &newscope->map); - scope = (JSScope *) js_DropObjectMap(cx, &scope->map, obj); - JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope); - return newscope; -} - -/* - * JSScope uses multiplicative hashing, _a la_ jsdhash.[ch], but specialized - * to minimize footprint. But if a scope has fewer than SCOPE_HASH_THRESHOLD - * entries, we use linear search and avoid allocating scope->table. - */ -#define SCOPE_HASH_THRESHOLD 6 -#define MIN_SCOPE_SIZE_LOG2 4 -#define MIN_SCOPE_SIZE JS_BIT(MIN_SCOPE_SIZE_LOG2) -#define SCOPE_TABLE_NBYTES(n) ((n) * sizeof(JSScopeProperty *)) - -static void -InitMinimalScope(JSScope *scope) -{ - scope->hashShift = JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2; - scope->entryCount = scope->removedCount = 0; - scope->table = NULL; - scope->lastProp = NULL; -} - -static JSBool -CreateScopeTable(JSContext *cx, JSScope *scope, JSBool report) -{ - int sizeLog2; - JSScopeProperty *sprop, **spp; - - JS_ASSERT(!scope->table); - JS_ASSERT(scope->lastProp); - - if (scope->entryCount > SCOPE_HASH_THRESHOLD) { - /* - * Ouch: calloc failed at least once already -- let's try again, - * overallocating to hold at least twice the current population. - */ - sizeLog2 = JS_CeilingLog2(2 * scope->entryCount); - scope->hashShift = JS_DHASH_BITS - sizeLog2; - } else { - JS_ASSERT(scope->hashShift == JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2); - sizeLog2 = MIN_SCOPE_SIZE_LOG2; - } - - scope->table = (JSScopeProperty **) - calloc(JS_BIT(sizeLog2), sizeof(JSScopeProperty *)); - if (!scope->table) { - if (report) - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - js_UpdateMallocCounter(cx, JS_BIT(sizeLog2) * sizeof(JSScopeProperty *)); - - scope->hashShift = JS_DHASH_BITS - sizeLog2; - for (sprop = scope->lastProp; sprop; sprop = sprop->parent) { - spp = js_SearchScope(scope, sprop->id, JS_TRUE); - SPROP_STORE_PRESERVING_COLLISION(spp, sprop); - } - return JS_TRUE; -} - -JSScope * -js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, - JSObject *obj) -{ - JSScope *scope; - - scope = (JSScope *) JS_malloc(cx, sizeof(JSScope)); - if (!scope) - return NULL; - - js_InitObjectMap(&scope->map, nrefs, ops, clasp); - scope->object = obj; - scope->flags = 0; - InitMinimalScope(scope); - -#ifdef JS_THREADSAFE - scope->ownercx = cx; - memset(&scope->lock, 0, sizeof scope->lock); - - /* - * Set u.link = NULL, not u.count = 0, in case the target architecture's - * null pointer has a non-zero integer representation. - */ - scope->u.link = NULL; - -#ifdef DEBUG - scope->file[0] = scope->file[1] = scope->file[2] = scope->file[3] = NULL; - scope->line[0] = scope->line[1] = scope->line[2] = scope->line[3] = 0; -#endif -#endif - - JS_RUNTIME_METER(cx->runtime, liveScopes); - JS_RUNTIME_METER(cx->runtime, totalScopes); - return scope; -} - -#ifdef DEBUG_SCOPE_COUNT -extern void -js_unlog_scope(JSScope *scope); -#endif - -void -js_DestroyScope(JSContext *cx, JSScope *scope) -{ -#ifdef DEBUG_SCOPE_COUNT - js_unlog_scope(scope); -#endif - -#ifdef JS_THREADSAFE - /* Scope must be single-threaded at this point, so set scope->ownercx. */ - JS_ASSERT(scope->u.count == 0); - scope->ownercx = cx; - js_FinishLock(&scope->lock); -#endif - if (scope->table) - JS_free(cx, scope->table); - -#ifdef DEBUG - JS_LOCK_RUNTIME_VOID(cx->runtime, - cx->runtime->liveScopeProps -= scope->entryCount); -#endif - JS_RUNTIME_UNMETER(cx->runtime, liveScopes); - JS_free(cx, scope); -} - -#ifdef DUMP_SCOPE_STATS -typedef struct JSScopeStats { - jsrefcount searches; - jsrefcount steps; - jsrefcount hits; - jsrefcount misses; - jsrefcount stepHits; - jsrefcount stepMisses; - jsrefcount adds; - jsrefcount redundantAdds; - jsrefcount addFailures; - jsrefcount changeFailures; - jsrefcount compresses; - jsrefcount grows; - jsrefcount removes; - jsrefcount removeFrees; - jsrefcount uselessRemoves; - jsrefcount shrinks; -} JSScopeStats; - -JS_FRIEND_DATA(JSScopeStats) js_scope_stats; - -# define METER(x) JS_ATOMIC_INCREMENT(&js_scope_stats.x) -#else -# define METER(x) /* nothing */ -#endif - -/* - * Double hashing needs the second hash code to be relatively prime to table - * size, so we simply make hash2 odd. The inputs to multiplicative hash are - * the golden ratio, expressed as a fixed-point 32 bit fraction, and the int - * property index or named property's atom number (observe that most objects - * have either no indexed properties, or almost all indexed and a few names, - * so collisions between index and atom number are unlikely). - */ -#define SCOPE_HASH0(id) (HASH_ID(id) * JS_GOLDEN_RATIO) -#define SCOPE_HASH1(hash0,shift) ((hash0) >> (shift)) -#define SCOPE_HASH2(hash0,log2,shift) ((((hash0) << (log2)) >> (shift)) | 1) - -JS_FRIEND_API(JSScopeProperty **) -js_SearchScope(JSScope *scope, jsid id, JSBool adding) -{ - JSHashNumber hash0, hash1, hash2; - int hashShift, sizeLog2; - JSScopeProperty *stored, *sprop, **spp, **firstRemoved; - uint32 sizeMask; - - METER(searches); - if (!scope->table) { - /* Not enough properties to justify hashing: search from lastProp. */ - JS_ASSERT(!SCOPE_HAD_MIDDLE_DELETE(scope)); - for (spp = &scope->lastProp; (sprop = *spp); spp = &sprop->parent) { - if (sprop->id == id) { - METER(hits); - return spp; - } - } - METER(misses); - return spp; - } - - /* Compute the primary hash address. */ - hash0 = SCOPE_HASH0(id); - hashShift = scope->hashShift; - hash1 = SCOPE_HASH1(hash0, hashShift); - spp = scope->table + hash1; - - /* Miss: return space for a new entry. */ - stored = *spp; - if (SPROP_IS_FREE(stored)) { - METER(misses); - return spp; - } - - /* Hit: return entry. */ - sprop = SPROP_CLEAR_COLLISION(stored); - if (sprop && sprop->id == id) { - METER(hits); - return spp; - } - - /* Collision: double hash. */ - sizeLog2 = JS_DHASH_BITS - hashShift; - hash2 = SCOPE_HASH2(hash0, sizeLog2, hashShift); - sizeMask = JS_BITMASK(sizeLog2); - - /* Save the first removed entry pointer so we can recycle it if adding. */ - if (SPROP_IS_REMOVED(stored)) { - firstRemoved = spp; - } else { - firstRemoved = NULL; - if (adding && !SPROP_HAD_COLLISION(stored)) - SPROP_FLAG_COLLISION(spp, sprop); - } - - for (;;) { - METER(steps); - hash1 -= hash2; - hash1 &= sizeMask; - spp = scope->table + hash1; - - stored = *spp; - if (SPROP_IS_FREE(stored)) { - METER(stepMisses); - return (adding && firstRemoved) ? firstRemoved : spp; - } - - sprop = SPROP_CLEAR_COLLISION(stored); - if (sprop && sprop->id == id) { - METER(stepHits); - return spp; - } - - if (SPROP_IS_REMOVED(stored)) { - if (!firstRemoved) - firstRemoved = spp; - } else { - if (adding && !SPROP_HAD_COLLISION(stored)) - SPROP_FLAG_COLLISION(spp, sprop); - } - } - - /* NOTREACHED */ - return NULL; -} - -static JSBool -ChangeScope(JSContext *cx, JSScope *scope, int change) -{ - int oldlog2, newlog2; - uint32 oldsize, newsize, nbytes; - JSScopeProperty **table, **oldtable, **spp, **oldspp, *sprop; - - /* Grow, shrink, or compress by changing scope->table. */ - oldlog2 = JS_DHASH_BITS - scope->hashShift; - newlog2 = oldlog2 + change; - oldsize = JS_BIT(oldlog2); - newsize = JS_BIT(newlog2); - nbytes = SCOPE_TABLE_NBYTES(newsize); - table = (JSScopeProperty **) calloc(nbytes, 1); - if (!table) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - /* Now that we have a new table allocated, update scope members. */ - scope->hashShift = JS_DHASH_BITS - newlog2; - scope->removedCount = 0; - oldtable = scope->table; - scope->table = table; - - /* Treat the above calloc as a JS_malloc, to match CreateScopeTable. */ - cx->runtime->gcMallocBytes += nbytes; - - /* Copy only live entries, leaving removed and free ones behind. */ - for (oldspp = oldtable; oldsize != 0; oldspp++) { - sprop = SPROP_FETCH(oldspp); - if (sprop) { - spp = js_SearchScope(scope, sprop->id, JS_TRUE); - JS_ASSERT(SPROP_IS_FREE(*spp)); - *spp = sprop; - } - oldsize--; - } - - /* Finally, free the old table storage. */ - JS_free(cx, oldtable); - return JS_TRUE; -} - -/* - * Take care to exclude the mark and duplicate bits, in case we're called from - * the GC, or we are searching for a property that has not yet been flagged as - * a duplicate when making a duplicate formal parameter. - */ -#define SPROP_FLAGS_NOT_MATCHED (SPROP_MARK | SPROP_IS_DUPLICATE) - -JS_STATIC_DLL_CALLBACK(JSDHashNumber) -js_HashScopeProperty(JSDHashTable *table, const void *key) -{ - const JSScopeProperty *sprop = (const JSScopeProperty *)key; - JSDHashNumber hash; - JSPropertyOp gsop; - - /* Accumulate from least to most random so the low bits are most random. */ - hash = 0; - gsop = sprop->getter; - if (gsop) - hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ (jsword)gsop; - gsop = sprop->setter; - if (gsop) - hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ (jsword)gsop; - - hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) - ^ (sprop->flags & ~SPROP_FLAGS_NOT_MATCHED); - - hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->attrs; - hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->shortid; - hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->slot; - hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->id; - return hash; -} - -#define SPROP_MATCH(sprop, child) \ - SPROP_MATCH_PARAMS(sprop, (child)->id, (child)->getter, (child)->setter, \ - (child)->slot, (child)->attrs, (child)->flags, \ - (child)->shortid) - -#define SPROP_MATCH_PARAMS(sprop, aid, agetter, asetter, aslot, aattrs, \ - aflags, ashortid) \ - ((sprop)->id == (aid) && \ - SPROP_MATCH_PARAMS_AFTER_ID(sprop, agetter, asetter, aslot, aattrs, \ - aflags, ashortid)) - -#define SPROP_MATCH_PARAMS_AFTER_ID(sprop, agetter, asetter, aslot, aattrs, \ - aflags, ashortid) \ - ((sprop)->getter == (agetter) && \ - (sprop)->setter == (asetter) && \ - (sprop)->slot == (aslot) && \ - (sprop)->attrs == (aattrs) && \ - (((sprop)->flags ^ (aflags)) & ~SPROP_FLAGS_NOT_MATCHED) == 0 && \ - (sprop)->shortid == (ashortid)) - -JS_STATIC_DLL_CALLBACK(JSBool) -js_MatchScopeProperty(JSDHashTable *table, - const JSDHashEntryHdr *hdr, - const void *key) -{ - const JSPropertyTreeEntry *entry = (const JSPropertyTreeEntry *)hdr; - const JSScopeProperty *sprop = entry->child; - const JSScopeProperty *kprop = (const JSScopeProperty *)key; - - return SPROP_MATCH(sprop, kprop); -} - -static const JSDHashTableOps PropertyTreeHashOps = { - JS_DHashAllocTable, - JS_DHashFreeTable, - JS_DHashGetKeyStub, - js_HashScopeProperty, - js_MatchScopeProperty, - JS_DHashMoveEntryStub, - JS_DHashClearEntryStub, - JS_DHashFinalizeStub, - NULL -}; - -/* - * A property tree node on rt->propertyFreeList overlays the following prefix - * struct on JSScopeProperty. - */ -typedef struct FreeNode { - jsid id; - JSScopeProperty *next; - JSScopeProperty **prevp; -} FreeNode; - -#define FREENODE(sprop) ((FreeNode *) (sprop)) - -#define FREENODE_INSERT(list, sprop) \ - JS_BEGIN_MACRO \ - FREENODE(sprop)->next = (list); \ - FREENODE(sprop)->prevp = &(list); \ - if (list) \ - FREENODE(list)->prevp = &FREENODE(sprop)->next; \ - (list) = (sprop); \ - JS_END_MACRO - -#define FREENODE_REMOVE(sprop) \ - JS_BEGIN_MACRO \ - *FREENODE(sprop)->prevp = FREENODE(sprop)->next; \ - if (FREENODE(sprop)->next) \ - FREENODE(FREENODE(sprop)->next)->prevp = FREENODE(sprop)->prevp; \ - JS_END_MACRO - -/* NB: Called with the runtime lock held. */ -static JSScopeProperty * -NewScopeProperty(JSRuntime *rt) -{ - JSScopeProperty *sprop; - - sprop = rt->propertyFreeList; - if (sprop) { - FREENODE_REMOVE(sprop); - } else { - JS_ARENA_ALLOCATE_CAST(sprop, JSScopeProperty *, - &rt->propertyArenaPool, - sizeof(JSScopeProperty)); - if (!sprop) - return NULL; - } - - JS_RUNTIME_METER(rt, livePropTreeNodes); - JS_RUNTIME_METER(rt, totalPropTreeNodes); - return sprop; -} - -#define CHUNKY_KIDS_TAG ((jsuword)1) -#define KIDS_IS_CHUNKY(kids) ((jsuword)(kids) & CHUNKY_KIDS_TAG) -#define KIDS_TO_CHUNK(kids) ((PropTreeKidsChunk *) \ - ((jsuword)(kids) & ~CHUNKY_KIDS_TAG)) -#define CHUNK_TO_KIDS(chunk) ((JSScopeProperty *) \ - ((jsuword)(chunk) | CHUNKY_KIDS_TAG)) -#define MAX_KIDS_PER_CHUNK 10 - -typedef struct PropTreeKidsChunk PropTreeKidsChunk; - -struct PropTreeKidsChunk { - JSScopeProperty *kids[MAX_KIDS_PER_CHUNK]; - PropTreeKidsChunk *next; -}; - -static PropTreeKidsChunk * -NewPropTreeKidsChunk(JSRuntime *rt) -{ - PropTreeKidsChunk *chunk; - - chunk = calloc(1, sizeof *chunk); - if (!chunk) - return NULL; - JS_ASSERT(((jsuword)chunk & CHUNKY_KIDS_TAG) == 0); - JS_RUNTIME_METER(rt, propTreeKidsChunks); - return chunk; -} - -static void -DestroyPropTreeKidsChunk(JSRuntime *rt, PropTreeKidsChunk *chunk) -{ - JS_RUNTIME_UNMETER(rt, propTreeKidsChunks); - free(chunk); -} - -/* NB: Called with the runtime lock held. */ -static JSBool -InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent, - JSScopeProperty *child, PropTreeKidsChunk *sweptChunk) -{ - JSPropertyTreeEntry *entry; - JSScopeProperty **childp, *kids, *sprop; - PropTreeKidsChunk *chunk, **chunkp; - uintN i; - - JS_ASSERT(!parent || child->parent != parent); - - if (!parent) { - entry = (JSPropertyTreeEntry *) - JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_ADD); - if (!entry) - return JS_FALSE; - childp = &entry->child; - sprop = *childp; - if (!sprop) { - *childp = child; - } else { - /* - * A "Duplicate child" case. - * - * We can't do away with child, as at least one live scope entry - * still points at it. What's more, that scope's lastProp chains - * through an ancestor line to reach child, and js_Enumerate and - * others count on this linkage. We must leave child out of the - * hash table, and not require it to be there when we eventually - * GC it (see RemovePropertyTreeChild, below). - * - * It is necessary to leave the duplicate child out of the hash - * table to preserve entry uniqueness. It is safe to leave the - * child out of the hash table (unlike the duplicate child cases - * below), because the child's parent link will be null, which - * can't dangle. - */ - JS_ASSERT(sprop != child && SPROP_MATCH(sprop, child)); - JS_RUNTIME_METER(rt, duplicatePropTreeNodes); - } - } else { - childp = &parent->kids; - kids = *childp; - if (kids) { - if (KIDS_IS_CHUNKY(kids)) { - chunk = KIDS_TO_CHUNK(kids); - do { - for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { - childp = &chunk->kids[i]; - sprop = *childp; - if (!sprop) - goto insert; - - JS_ASSERT(sprop != child); - if (SPROP_MATCH(sprop, child)) { - /* - * Duplicate child, see comment above. In this - * case, we must let the duplicate be inserted at - * this level in the tree, so we keep iterating, - * looking for an empty slot in which to insert. - */ - JS_ASSERT(sprop != child); - JS_RUNTIME_METER(rt, duplicatePropTreeNodes); - } - } - chunkp = &chunk->next; - } while ((chunk = *chunkp) != NULL); - - if (sweptChunk) { - chunk = sweptChunk; - } else { - chunk = NewPropTreeKidsChunk(rt); - if (!chunk) - return JS_FALSE; - } - *chunkp = chunk; - childp = &chunk->kids[0]; - } else { - sprop = kids; - JS_ASSERT(sprop != child); - if (SPROP_MATCH(sprop, child)) { - /* - * Duplicate child, see comment above. Once again, we - * must let duplicates created by deletion pile up in a - * kids-chunk-list, in order to find them when sweeping - * and thereby avoid dangling parent pointers. - */ - JS_RUNTIME_METER(rt, duplicatePropTreeNodes); - } - if (sweptChunk) { - chunk = sweptChunk; - } else { - chunk = NewPropTreeKidsChunk(rt); - if (!chunk) - return JS_FALSE; - } - parent->kids = CHUNK_TO_KIDS(chunk); - chunk->kids[0] = sprop; - childp = &chunk->kids[1]; - } - } - insert: - *childp = child; - } - - child->parent = parent; - return JS_TRUE; -} - -/* NB: Called with the runtime lock held. */ -static PropTreeKidsChunk * -RemovePropertyTreeChild(JSRuntime *rt, JSScopeProperty *child) -{ - JSPropertyTreeEntry *entry; - JSScopeProperty *parent, *kids, *kid; - PropTreeKidsChunk *list, *chunk, **chunkp, *lastChunk; - uintN i, j; - - parent = child->parent; - if (!parent) { - /* - * Don't remove child if it is not in rt->propertyTreeHash, but only - * matches a root child in the table that has compatible members. See - * the "Duplicate child" comments in InsertPropertyTreeChild, above. - */ - entry = (JSPropertyTreeEntry *) - JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_LOOKUP); - - if (entry->child == child) - JS_DHashTableRawRemove(&rt->propertyTreeHash, &entry->hdr); - } else { - kids = parent->kids; - if (KIDS_IS_CHUNKY(kids)) { - list = chunk = KIDS_TO_CHUNK(kids); - chunkp = &list; - - do { - for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { - if (chunk->kids[i] == child) { - lastChunk = chunk; - if (!lastChunk->next) { - j = i + 1; - } else { - j = 0; - do { - chunkp = &lastChunk->next; - lastChunk = *chunkp; - } while (lastChunk->next); - } - for (; j < MAX_KIDS_PER_CHUNK; j++) { - if (!lastChunk->kids[j]) - break; - } - --j; - if (chunk != lastChunk || j > i) - chunk->kids[i] = lastChunk->kids[j]; - lastChunk->kids[j] = NULL; - if (j == 0) { - *chunkp = NULL; - if (!list) - parent->kids = NULL; - return lastChunk; - } - return NULL; - } - } - - chunkp = &chunk->next; - } while ((chunk = *chunkp) != NULL); - } else { - kid = kids; - if (kid == child) - parent->kids = NULL; - } - } - return NULL; -} - -/* - * Called *without* the runtime lock held, this function acquires that lock - * only when inserting a new child. Thus there may be races to find or add - * a node that result in duplicates. We expect such races to be rare! - */ -static JSScopeProperty * -GetPropertyTreeChild(JSContext *cx, JSScopeProperty *parent, - JSScopeProperty *child) -{ - JSRuntime *rt; - JSPropertyTreeEntry *entry; - JSScopeProperty *sprop; - PropTreeKidsChunk *chunk; - uintN i; - - rt = cx->runtime; - if (!parent) { - JS_LOCK_RUNTIME(rt); - - entry = (JSPropertyTreeEntry *) - JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_ADD); - if (!entry) - goto out_of_memory; - - sprop = entry->child; - if (sprop) - goto out; - } else { - /* - * Because chunks are appended at the end and never deleted except by - * the GC, we can search without taking the runtime lock. We may miss - * a matching sprop added by another thread, and make a duplicate one, - * but that is an unlikely, therefore small, cost. The property tree - * has extremely low fan-out below its root in popular embeddings with - * real-world workloads. - * - * If workload changes so as to increase fan-out significantly below - * the property tree root, we'll want to add another tag bit stored in - * parent->kids that indicates a JSDHashTable pointer. - */ - entry = NULL; - sprop = parent->kids; - if (sprop) { - if (KIDS_IS_CHUNKY(sprop)) { - chunk = KIDS_TO_CHUNK(sprop); - do { - for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { - sprop = chunk->kids[i]; - if (!sprop) - goto not_found; - - if (SPROP_MATCH(sprop, child)) - return sprop; - } - } while ((chunk = chunk->next) != NULL); - } else { - if (SPROP_MATCH(sprop, child)) - return sprop; - } - } - - not_found: - JS_LOCK_RUNTIME(rt); - } - - sprop = NewScopeProperty(rt); - if (!sprop) - goto out_of_memory; - - sprop->id = child->id; - sprop->getter = child->getter; - sprop->setter = child->setter; - sprop->slot = child->slot; - sprop->attrs = child->attrs; - sprop->flags = child->flags; - sprop->shortid = child->shortid; - sprop->parent = sprop->kids = NULL; - if (!parent) { - entry->child = sprop; - } else { - if (!InsertPropertyTreeChild(rt, parent, sprop, NULL)) - goto out_of_memory; - } - -out: - JS_UNLOCK_RUNTIME(rt); - return sprop; - -out_of_memory: - JS_UNLOCK_RUNTIME(rt); - JS_ReportOutOfMemory(cx); - return NULL; -} - -#ifdef DEBUG_notbrendan -#define CHECK_ANCESTOR_LINE(scope, sparse) \ - JS_BEGIN_MACRO \ - if ((scope)->table) CheckAncestorLine(scope, sparse); \ - JS_END_MACRO - -static void -CheckAncestorLine(JSScope *scope, JSBool sparse) -{ - uint32 size; - JSScopeProperty **spp, **start, **end, *ancestorLine, *sprop, *aprop; - uint32 entryCount, ancestorCount; - - ancestorLine = SCOPE_LAST_PROP(scope); - if (ancestorLine) - JS_ASSERT(SCOPE_HAS_PROPERTY(scope, ancestorLine)); - - entryCount = 0; - size = SCOPE_CAPACITY(scope); - start = scope->table; - for (spp = start, end = start + size; spp < end; spp++) { - sprop = SPROP_FETCH(spp); - if (sprop) { - entryCount++; - for (aprop = ancestorLine; aprop; aprop = aprop->parent) { - if (aprop == sprop) - break; - } - JS_ASSERT(aprop); - } - } - JS_ASSERT(entryCount == scope->entryCount); - - ancestorCount = 0; - for (sprop = ancestorLine; sprop; sprop = sprop->parent) { - if (SCOPE_HAD_MIDDLE_DELETE(scope) && - !SCOPE_HAS_PROPERTY(scope, sprop)) { - JS_ASSERT(sparse || (sprop->flags & SPROP_IS_DUPLICATE)); - continue; - } - ancestorCount++; - } - JS_ASSERT(ancestorCount == scope->entryCount); -} -#else -#define CHECK_ANCESTOR_LINE(scope, sparse) /* nothing */ -#endif - -static void -ReportReadOnlyScope(JSContext *cx, JSScope *scope) -{ - JSString *str; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(scope->object)); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_READ_ONLY, - str - ? JS_GetStringBytes(str) - : LOCKED_OBJ_GET_CLASS(scope->object)->name); -} - -JSScopeProperty * -js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id, - JSPropertyOp getter, JSPropertyOp setter, uint32 slot, - uintN attrs, uintN flags, intN shortid) -{ - JSScopeProperty **spp, *sprop, *overwriting, **spvec, **spp2, child; - uint32 size, splen, i; - int change; - JSTempValueRooter tvr; - - JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); - CHECK_ANCESTOR_LINE(scope, JS_TRUE); - - /* - * You can't add properties to a sealed scope. But note well that you can - * change property attributes in a sealed scope, even though that replaces - * a JSScopeProperty * in the scope's hash table -- but no id is added, so - * the scope remains sealed. - */ - if (SCOPE_IS_SEALED(scope)) { - ReportReadOnlyScope(cx, scope); - return NULL; - } - - /* - * Normalize stub getter and setter values for faster is-stub testing in - * the SPROP_CALL_[GS]ETTER macros. - */ - if (getter == JS_PropertyStub) - getter = NULL; - if (setter == JS_PropertyStub) - setter = NULL; - - /* - * Search for id in order to claim its entry, allocating a property tree - * node if one doesn't already exist for our parameters. - */ - spp = js_SearchScope(scope, id, JS_TRUE); - sprop = overwriting = SPROP_FETCH(spp); - if (!sprop) { - /* Check whether we need to grow, if the load factor is >= .75. */ - size = SCOPE_CAPACITY(scope); - if (scope->entryCount + scope->removedCount >= size - (size >> 2)) { - if (scope->removedCount >= size >> 2) { - METER(compresses); - change = 0; - } else { - METER(grows); - change = 1; - } - if (!ChangeScope(cx, scope, change) && - scope->entryCount + scope->removedCount == size - 1) { - METER(addFailures); - return NULL; - } - spp = js_SearchScope(scope, id, JS_TRUE); - JS_ASSERT(!SPROP_FETCH(spp)); - } - } else { - /* Property exists: js_SearchScope must have returned a valid entry. */ - JS_ASSERT(!SPROP_IS_REMOVED(*spp)); - - /* - * If all property members match, this is a redundant add and we can - * return early. If the caller wants to allocate a slot, but doesn't - * care which slot, copy sprop->slot into slot so we can match sprop, - * if all other members match. - */ - if (!(attrs & JSPROP_SHARED) && - slot == SPROP_INVALID_SLOT && - SPROP_HAS_VALID_SLOT(sprop, scope)) { - slot = sprop->slot; - } - if (SPROP_MATCH_PARAMS_AFTER_ID(sprop, getter, setter, slot, attrs, - flags, shortid)) { - METER(redundantAdds); - return sprop; - } - - /* - * Duplicate formal parameters require us to leave the old property - * on the ancestor line, so the decompiler can find it, even though - * its entry in scope->table is overwritten to point at a new property - * descending from the old one. The SPROP_IS_DUPLICATE flag helps us - * cope with the consequent disparity between ancestor line height and - * scope->entryCount. - */ - if (flags & SPROP_IS_DUPLICATE) { - sprop->flags |= SPROP_IS_DUPLICATE; - } else { - /* - * If we are clearing sprop to force an existing property to be - * overwritten (apart from a duplicate formal parameter), we must - * unlink it from the ancestor line at scope->lastProp, lazily if - * sprop is not lastProp. And we must remove the entry at *spp, - * precisely so the lazy "middle delete" fixup code further below - * won't find sprop in scope->table, in spite of sprop being on - * the ancestor line. - * - * When we finally succeed in finding or creating a new sprop - * and storing its pointer at *spp, we'll use the |overwriting| - * local saved when we first looked up id to decide whether we're - * indeed creating a new entry, or merely overwriting an existing - * property. - */ - if (sprop == SCOPE_LAST_PROP(scope)) { - do { - SCOPE_REMOVE_LAST_PROP(scope); - if (!SCOPE_HAD_MIDDLE_DELETE(scope)) - break; - sprop = SCOPE_LAST_PROP(scope); - } while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop)); - } else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) { - /* - * If we have no hash table yet, we need one now. The middle - * delete code is simple-minded that way! - */ - if (!scope->table) { - if (!CreateScopeTable(cx, scope, JS_TRUE)) - return NULL; - spp = js_SearchScope(scope, id, JS_TRUE); - sprop = overwriting = SPROP_FETCH(spp); - } - SCOPE_SET_MIDDLE_DELETE(scope); - } - } - - /* - * If we fail later on trying to find or create a new sprop, we will - * goto fail_overwrite and restore *spp from |overwriting|. Note that - * we don't bother to keep scope->removedCount in sync, because we'll - * fix up *spp and scope->entryCount shortly, no matter how control - * flow returns from this function. - */ - if (scope->table) - SPROP_STORE_PRESERVING_COLLISION(spp, NULL); - scope->entryCount--; - CHECK_ANCESTOR_LINE(scope, JS_TRUE); - sprop = NULL; - } - - if (!sprop) { - /* - * If properties were deleted from the middle of the list starting at - * scope->lastProp, we may need to fork the property tree and squeeze - * all deleted properties out of scope's ancestor line. Otherwise we - * risk adding a node with the same id as a "middle" node, violating - * the rule that properties along an ancestor line have distinct ids - * (unless flagged SPROP_IS_DUPLICATE). - */ - if (SCOPE_HAD_MIDDLE_DELETE(scope)) { - JS_ASSERT(scope->table); - CHECK_ANCESTOR_LINE(scope, JS_TRUE); - - splen = scope->entryCount; - if (splen == 0) { - JS_ASSERT(scope->lastProp == NULL); - } else { - /* - * Enumerate live entries in scope->table using a temporary - * vector, by walking the (possibly sparse, due to deletions) - * ancestor line from scope->lastProp. - */ - spvec = (JSScopeProperty **) - JS_malloc(cx, SCOPE_TABLE_NBYTES(splen)); - if (!spvec) - goto fail_overwrite; - i = splen; - sprop = SCOPE_LAST_PROP(scope); - JS_ASSERT(sprop); - do { - /* - * NB: test SCOPE_GET_PROPERTY, not SCOPE_HAS_PROPERTY -- - * the latter insists that sprop->id maps to sprop, while - * the former simply tests whether sprop->id is bound in - * scope. We must allow for duplicate formal parameters - * along the ancestor line, and fork them as needed. - */ - if (!SCOPE_GET_PROPERTY(scope, sprop->id)) - continue; - - JS_ASSERT(sprop != overwriting); - if (i == 0) { - /* - * If our original splen estimate, scope->entryCount, - * is less than the ancestor line height, there must - * be duplicate formal parameters in this (function - * object) scope. Count remaining ancestors in order - * to realloc spvec. - */ - JSScopeProperty *tmp = sprop; - do { - if (SCOPE_GET_PROPERTY(scope, tmp->id)) - i++; - } while ((tmp = tmp->parent) != NULL); - spp2 = (JSScopeProperty **) - JS_realloc(cx, spvec, SCOPE_TABLE_NBYTES(splen+i)); - if (!spp2) { - JS_free(cx, spvec); - goto fail_overwrite; - } - - spvec = spp2; - memmove(spvec + i, spvec, SCOPE_TABLE_NBYTES(splen)); - splen += i; - } - - spvec[--i] = sprop; - } while ((sprop = sprop->parent) != NULL); - JS_ASSERT(i == 0); - - /* - * Now loop forward through spvec, forking the property tree - * whenever we see a "parent gap" due to deletions from scope. - * NB: sprop is null on first entry to the loop body. - */ - do { - if (spvec[i]->parent == sprop) { - sprop = spvec[i]; - } else { - sprop = GetPropertyTreeChild(cx, sprop, spvec[i]); - if (!sprop) { - JS_free(cx, spvec); - goto fail_overwrite; - } - - spp2 = js_SearchScope(scope, sprop->id, JS_FALSE); - JS_ASSERT(SPROP_FETCH(spp2) == spvec[i]); - SPROP_STORE_PRESERVING_COLLISION(spp2, sprop); - } - } while (++i < splen); - JS_free(cx, spvec); - - /* - * Now sprop points to the last property in scope, where the - * ancestor line from sprop to the root is dense w.r.t. scope: - * it contains no nodes not mapped by scope->table, apart from - * any stinking ECMA-mandated duplicate formal parameters. - */ - scope->lastProp = sprop; - CHECK_ANCESTOR_LINE(scope, JS_FALSE); - JS_RUNTIME_METER(cx->runtime, middleDeleteFixups); - } - - SCOPE_CLR_MIDDLE_DELETE(scope); - } - - /* - * Aliases share another property's slot, passed in the |slot| param. - * Shared properties have no slot. Unshared properties that do not - * alias another property's slot get one here, but may lose it due to - * a JS_ClearScope call. - */ - if (!(flags & SPROP_IS_ALIAS)) { - if (attrs & JSPROP_SHARED) { - slot = SPROP_INVALID_SLOT; - } else { - /* - * We may have set slot from a nearly-matching sprop, above. - * If so, we're overwriting that nearly-matching sprop, so we - * can reuse its slot -- we don't need to allocate a new one. - * Callers should therefore pass SPROP_INVALID_SLOT for all - * non-alias, unshared property adds. - */ - if (slot != SPROP_INVALID_SLOT) - JS_ASSERT(overwriting); - else if (!js_AllocSlot(cx, scope->object, &slot)) - goto fail_overwrite; - } - } - - /* - * Check for a watchpoint on a deleted property; if one exists, change - * setter to js_watch_set. - * XXXbe this could get expensive with lots of watchpoints... - */ - if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList) && - js_FindWatchPoint(cx->runtime, scope, id)) { - JS_PUSH_TEMP_ROOT_SPROP(cx, overwriting, &tvr); - setter = js_WrapWatchedSetter(cx, id, attrs, setter); - JS_POP_TEMP_ROOT(cx, &tvr); - if (!setter) - goto fail_overwrite; - } - - /* Find or create a property tree node labeled by our arguments. */ - child.id = id; - child.getter = getter; - child.setter = setter; - child.slot = slot; - child.attrs = attrs; - child.flags = flags; - child.shortid = shortid; - sprop = GetPropertyTreeChild(cx, scope->lastProp, &child); - if (!sprop) - goto fail_overwrite; - - /* Store the tree node pointer in the table entry for id. */ - if (scope->table) - SPROP_STORE_PRESERVING_COLLISION(spp, sprop); - scope->entryCount++; - scope->lastProp = sprop; - CHECK_ANCESTOR_LINE(scope, JS_FALSE); - if (!overwriting) { - JS_RUNTIME_METER(cx->runtime, liveScopeProps); - JS_RUNTIME_METER(cx->runtime, totalScopeProps); - } - - /* - * If we reach the hashing threshold, try to allocate scope->table. - * If we can't (a rare event, preceded by swapping to death on most - * modern OSes), stick with linear search rather than whining about - * this little set-back. Therefore we must test !scope->table and - * scope->entryCount >= SCOPE_HASH_THRESHOLD, not merely whether the - * entry count just reached the threshold. - */ - if (!scope->table && scope->entryCount >= SCOPE_HASH_THRESHOLD) - (void) CreateScopeTable(cx, scope, JS_FALSE); - } - - METER(adds); - return sprop; - -fail_overwrite: - if (overwriting) { - /* - * We may or may not have forked overwriting out of scope's ancestor - * line, so we must check (the alternative is to set a flag above, but - * that hurts the common, non-error case). If we did fork overwriting - * out, we'll add it back at scope->lastProp. This means enumeration - * order can change due to a failure to overwrite an id. - * XXXbe very minor incompatibility - */ - for (sprop = SCOPE_LAST_PROP(scope); ; sprop = sprop->parent) { - if (!sprop) { - sprop = SCOPE_LAST_PROP(scope); - if (overwriting->parent == sprop) { - scope->lastProp = overwriting; - } else { - sprop = GetPropertyTreeChild(cx, sprop, overwriting); - if (sprop) { - JS_ASSERT(sprop != overwriting); - scope->lastProp = sprop; - } - overwriting = sprop; - } - break; - } - if (sprop == overwriting) - break; - } - if (overwriting) { - if (scope->table) - SPROP_STORE_PRESERVING_COLLISION(spp, overwriting); - scope->entryCount++; - } - CHECK_ANCESTOR_LINE(scope, JS_TRUE); - } - METER(addFailures); - return NULL; -} - -JSScopeProperty * -js_ChangeScopePropertyAttrs(JSContext *cx, JSScope *scope, - JSScopeProperty *sprop, uintN attrs, uintN mask, - JSPropertyOp getter, JSPropertyOp setter) -{ - JSScopeProperty child, *newsprop, **spp; - - CHECK_ANCESTOR_LINE(scope, JS_TRUE); - - /* Allow only shared (slot-less) => unshared (slot-full) transition. */ - attrs |= sprop->attrs & mask; - JS_ASSERT(!((attrs ^ sprop->attrs) & JSPROP_SHARED) || - !(attrs & JSPROP_SHARED)); - if (getter == JS_PropertyStub) - getter = NULL; - if (setter == JS_PropertyStub) - setter = NULL; - if (sprop->attrs == attrs && - sprop->getter == getter && - sprop->setter == setter) { - return sprop; - } - - child.id = sprop->id; - child.getter = getter; - child.setter = setter; - child.slot = sprop->slot; - child.attrs = attrs; - child.flags = sprop->flags; - child.shortid = sprop->shortid; - - if (SCOPE_LAST_PROP(scope) == sprop) { - /* - * Optimize the case where the last property added to scope is changed - * to have a different attrs, getter, or setter. In the last property - * case, we need not fork the property tree. But since we do not call - * js_AddScopeProperty, we may need to allocate a new slot directly. - */ - if ((sprop->attrs & JSPROP_SHARED) && !(attrs & JSPROP_SHARED)) { - JS_ASSERT(child.slot == SPROP_INVALID_SLOT); - if (!js_AllocSlot(cx, scope->object, &child.slot)) - return NULL; - } - - newsprop = GetPropertyTreeChild(cx, sprop->parent, &child); - if (newsprop) { - spp = js_SearchScope(scope, sprop->id, JS_FALSE); - JS_ASSERT(SPROP_FETCH(spp) == sprop); - - if (scope->table) - SPROP_STORE_PRESERVING_COLLISION(spp, newsprop); - scope->lastProp = newsprop; - CHECK_ANCESTOR_LINE(scope, JS_TRUE); - } - } else { - /* - * Let js_AddScopeProperty handle this |overwriting| case, including - * the conservation of sprop->slot (if it's valid). We must not call - * js_RemoveScopeProperty here, it will free a valid sprop->slot and - * js_AddScopeProperty won't re-allocate it. - */ - newsprop = js_AddScopeProperty(cx, scope, child.id, - child.getter, child.setter, child.slot, - child.attrs, child.flags, child.shortid); - } - -#ifdef DUMP_SCOPE_STATS - if (!newsprop) - METER(changeFailures); -#endif - return newsprop; -} - -JSBool -js_RemoveScopeProperty(JSContext *cx, JSScope *scope, jsid id) -{ - JSScopeProperty **spp, *stored, *sprop; - uint32 size; - - JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); - CHECK_ANCESTOR_LINE(scope, JS_TRUE); - if (SCOPE_IS_SEALED(scope)) { - ReportReadOnlyScope(cx, scope); - return JS_FALSE; - } - METER(removes); - - spp = js_SearchScope(scope, id, JS_FALSE); - stored = *spp; - sprop = SPROP_CLEAR_COLLISION(stored); - if (!sprop) { - METER(uselessRemoves); - return JS_TRUE; - } - - /* Convert from a list to a hash so we can handle "middle deletes". */ - if (!scope->table && sprop != scope->lastProp) { - if (!CreateScopeTable(cx, scope, JS_TRUE)) - return JS_FALSE; - spp = js_SearchScope(scope, id, JS_FALSE); - stored = *spp; - sprop = SPROP_CLEAR_COLLISION(stored); - } - - /* First, if sprop is unshared and not cleared, free its slot number. */ - if (SPROP_HAS_VALID_SLOT(sprop, scope)) { - js_FreeSlot(cx, scope->object, sprop->slot); - JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals); - } - - /* Next, remove id by setting its entry to a removed or free sentinel. */ - if (SPROP_HAD_COLLISION(stored)) { - JS_ASSERT(scope->table); - *spp = SPROP_REMOVED; - scope->removedCount++; - } else { - METER(removeFrees); - if (scope->table) - *spp = NULL; - } - scope->entryCount--; - JS_RUNTIME_UNMETER(cx->runtime, liveScopeProps); - - /* Update scope->lastProp directly, or set its deferred update flag. */ - if (sprop == SCOPE_LAST_PROP(scope)) { - do { - SCOPE_REMOVE_LAST_PROP(scope); - if (!SCOPE_HAD_MIDDLE_DELETE(scope)) - break; - sprop = SCOPE_LAST_PROP(scope); - } while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop)); - } else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) { - SCOPE_SET_MIDDLE_DELETE(scope); - } - CHECK_ANCESTOR_LINE(scope, JS_TRUE); - - /* Last, consider shrinking scope's table if its load factor is <= .25. */ - size = SCOPE_CAPACITY(scope); - if (size > MIN_SCOPE_SIZE && scope->entryCount <= size >> 2) { - METER(shrinks); - (void) ChangeScope(cx, scope, -1); - } - - return JS_TRUE; -} - -void -js_ClearScope(JSContext *cx, JSScope *scope) -{ - CHECK_ANCESTOR_LINE(scope, JS_TRUE); -#ifdef DEBUG - JS_LOCK_RUNTIME_VOID(cx->runtime, - cx->runtime->liveScopeProps -= scope->entryCount); -#endif - - if (scope->table) - free(scope->table); - SCOPE_CLR_MIDDLE_DELETE(scope); - InitMinimalScope(scope); - JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals); -} - -void -js_MarkId(JSContext *cx, jsid id) -{ - if (JSID_IS_ATOM(id)) - GC_MARK_ATOM(cx, JSID_TO_ATOM(id)); - else if (JSID_IS_OBJECT(id)) - GC_MARK(cx, JSID_TO_OBJECT(id), "id"); - else - JS_ASSERT(JSID_IS_INT(id)); -} - -#if defined GC_MARK_DEBUG || defined DUMP_SCOPE_STATS -# include "jsprf.h" -#endif - -void -js_MarkScopeProperty(JSContext *cx, JSScopeProperty *sprop) -{ - sprop->flags |= SPROP_MARK; - MARK_ID(cx, sprop->id); - -#if JS_HAS_GETTER_SETTER - if (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER)) { -#ifdef GC_MARK_DEBUG - char buf[64]; - char buf2[11]; - const char *id; - - if (JSID_IS_ATOM(sprop->id)) { - JSAtom *atom = JSID_TO_ATOM(sprop->id); - - id = (atom && ATOM_IS_STRING(atom)) - ? JS_GetStringBytes(ATOM_TO_STRING(atom)) - : "unknown"; - } else if (JSID_IS_INT(sprop->id)) { - JS_snprintf(buf2, sizeof buf2, "%d", JSID_TO_INT(sprop->id)); - id = buf2; - } else { - id = ""; - } -#endif - - if (sprop->attrs & JSPROP_GETTER) { -#ifdef GC_MARK_DEBUG - JS_snprintf(buf, sizeof buf, "%s %s", - id, js_getter_str); -#endif - GC_MARK(cx, JSVAL_TO_GCTHING((jsval) sprop->getter), buf); - } - if (sprop->attrs & JSPROP_SETTER) { -#ifdef GC_MARK_DEBUG - JS_snprintf(buf, sizeof buf, "%s %s", - id, js_setter_str); -#endif - GC_MARK(cx, JSVAL_TO_GCTHING((jsval) sprop->setter), buf); - } - } -#endif /* JS_HAS_GETTER_SETTER */ -} - -#ifdef DUMP_SCOPE_STATS - -#include -#include - -uint32 js_nkids_max; -uint32 js_nkids_sum; -double js_nkids_sqsum; -uint32 js_nkids_hist[11]; - -static void -MeterKidCount(uintN nkids) -{ - if (nkids) { - js_nkids_sum += nkids; - js_nkids_sqsum += (double)nkids * nkids; - if (nkids > js_nkids_max) - js_nkids_max = nkids; - } - js_nkids_hist[JS_MIN(nkids, 10)]++; -} - -static void -MeterPropertyTree(JSScopeProperty *node) -{ - uintN i, nkids; - JSScopeProperty *kids, *kid; - PropTreeKidsChunk *chunk; - - nkids = 0; - kids = node->kids; - if (kids) { - if (KIDS_IS_CHUNKY(kids)) { - for (chunk = KIDS_TO_CHUNK(kids); chunk; chunk = chunk->next) { - for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { - kid = chunk->kids[i]; - if (!kid) - break; - MeterPropertyTree(kid); - nkids++; - } - } - } else { - MeterPropertyTree(kids); - nkids = 1; - } - } - - MeterKidCount(nkids); -} - -JS_STATIC_DLL_CALLBACK(JSDHashOperator) -js_MeterPropertyTree(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, - void *arg) -{ - JSPropertyTreeEntry *entry = (JSPropertyTreeEntry *)hdr; - - MeterPropertyTree(entry->child); - return JS_DHASH_NEXT; -} - -static void -DumpSubtree(JSScopeProperty *sprop, int level, FILE *fp) -{ - char buf[10]; - JSScopeProperty *kids, *kid; - PropTreeKidsChunk *chunk; - uintN i; - - fprintf(fp, "%*sid %s g/s %p/%p slot %lu attrs %x flags %x shortid %d\n", - level, "", - JSID_IS_ATOM(sprop->id) - ? JS_GetStringBytes(ATOM_TO_STRING(JSID_TO_ATOM(sprop->id))) - : JSID_IS_OBJECT(sprop->id) - ? js_ValueToPrintableString(cx, OBJECT_JSID_TO_JSVAL(sprop->id)) - : (JS_snprintf(buf, sizeof buf, "%ld", JSVAL_TO_INT(sprop->id)), - buf) - (void *) sprop->getter, (void *) sprop->setter, - (unsigned long) sprop->slot, sprop->attrs, sprop->flags, - sprop->shortid); - kids = sprop->kids; - if (kids) { - ++level; - if (KIDS_IS_CHUNKY(kids)) { - chunk = KIDS_TO_CHUNK(kids); - do { - for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { - kid = chunk->kids[i]; - if (!kid) - break; - JS_ASSERT(kid->parent == sprop); - DumpSubtree(kid, level, fp); - } - } while ((chunk = chunk->next) != NULL); - } else { - kid = kids; - DumpSubtree(kid, level, fp); - } - } -} - -#endif /* DUMP_SCOPE_STATS */ - -void -js_SweepScopeProperties(JSRuntime *rt) -{ - JSArena **ap, *a; - JSScopeProperty *limit, *sprop, *parent, *kids, *kid; - uintN liveCount; - PropTreeKidsChunk *chunk, *nextChunk, *freeChunk; - uintN i; - -#ifdef DUMP_SCOPE_STATS - uint32 livePropCapacity = 0, totalLiveCount = 0; - static FILE *logfp; - if (!logfp) - logfp = fopen("/tmp/proptree.stats", "a"); - - MeterKidCount(rt->propertyTreeHash.entryCount); - JS_DHashTableEnumerate(&rt->propertyTreeHash, js_MeterPropertyTree, NULL); - - { - double mean = 0.0, var = 0.0, sigma = 0.0; - double nodesum = rt->livePropTreeNodes; - double kidsum = js_nkids_sum; - if (nodesum > 0 && kidsum >= 0) { - mean = kidsum / nodesum; - var = nodesum * js_nkids_sqsum - kidsum * kidsum; - if (var < 0.0 || nodesum <= 1) - var = 0.0; - else - var /= nodesum * (nodesum - 1); - - /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ - sigma = (var != 0.0) ? sqrt(var) : 0.0; - } - - fprintf(logfp, - "props %u nodes %g beta %g meankids %g sigma %g max %u", - rt->liveScopeProps, nodesum, nodesum / rt->liveScopeProps, - mean, sigma, js_nkids_max); - } - - fprintf(logfp, " histogram %u %u %u %u %u %u %u %u %u %u %u", - js_nkids_hist[0], js_nkids_hist[1], - js_nkids_hist[2], js_nkids_hist[3], - js_nkids_hist[4], js_nkids_hist[5], - js_nkids_hist[6], js_nkids_hist[7], - js_nkids_hist[8], js_nkids_hist[9], - js_nkids_hist[10]); - js_nkids_sum = js_nkids_max = 0; - js_nkids_sqsum = 0; - memset(js_nkids_hist, 0, sizeof js_nkids_hist); -#endif - - ap = &rt->propertyArenaPool.first.next; - while ((a = *ap) != NULL) { - limit = (JSScopeProperty *) a->avail; - liveCount = 0; - for (sprop = (JSScopeProperty *) a->base; sprop < limit; sprop++) { - /* If the id is null, sprop is already on the freelist. */ - if (sprop->id == JSVAL_NULL) - continue; - - /* If the mark bit is set, sprop is alive, so we skip it. */ - if (sprop->flags & SPROP_MARK) { - sprop->flags &= ~SPROP_MARK; - liveCount++; - continue; - } - - /* Ok, sprop is garbage to collect: unlink it from its parent. */ - freeChunk = RemovePropertyTreeChild(rt, sprop); - - /* - * Take care to reparent all sprop's kids to their grandparent. - * InsertPropertyTreeChild can potentially fail for two reasons: - * - * 1. If parent is null, insertion into the root property hash - * table may fail. We are forced to leave the kid out of the - * table (as can already happen with duplicates) but ensure - * that the kid's parent pointer is set to null. - * - * 2. If parent is non-null, allocation of a new KidsChunk can - * fail. To prevent this from happening, we allow sprops's own - * chunks to be reused by the grandparent, which removes the - * need for InsertPropertyTreeChild to malloc a new KidsChunk. - * - * If sprop does not have chunky kids, then we rely on the - * RemovePropertyTreeChild call above (which removed sprop from - * its parent) either leaving one free entry, or else returning - * the now-unused chunk to us so we can reuse it. - * - * We also require the grandparent to have either no kids or else - * chunky kids. A single non-chunky kid would force a new chunk to - * be malloced in some cases (if sprop had a single non-chunky - * kid, or a multiple of MAX_KIDS_PER_CHUNK kids). Note that - * RemovePropertyTreeChild never converts a single-entry chunky - * kid back to a non-chunky kid, so we are assured of correct - * behaviour. - */ - kids = sprop->kids; - if (kids) { - sprop->kids = NULL; - parent = sprop->parent; - /* Validate that grandparent has no kids or chunky kids. */ - JS_ASSERT(!parent || !parent->kids || - KIDS_IS_CHUNKY(parent->kids)); - if (KIDS_IS_CHUNKY(kids)) { - chunk = KIDS_TO_CHUNK(kids); - do { - nextChunk = chunk->next; - chunk->next = NULL; - for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { - kid = chunk->kids[i]; - if (!kid) - break; - JS_ASSERT(kid->parent == sprop); - - /* - * Clear a space in the kids array for possible - * re-use by InsertPropertyTreeChild. - */ - chunk->kids[i] = NULL; - if (!InsertPropertyTreeChild(rt, parent, kid, - chunk)) { - /* - * This can happen only if we failed to add an - * entry to the root property hash table. - */ - JS_ASSERT(!parent); - kid->parent = NULL; - } - } - if (!chunk->kids[0]) { - /* The chunk wasn't reused, so we must free it. */ - DestroyPropTreeKidsChunk(rt, chunk); - } - } while ((chunk = nextChunk) != NULL); - } else { - kid = kids; - if (!InsertPropertyTreeChild(rt, parent, kid, freeChunk)) { - /* - * This can happen only if we failed to add an entry - * to the root property hash table. - */ - JS_ASSERT(!parent); - kid->parent = NULL; - } - } - } - - if (freeChunk && !freeChunk->kids[0]) { - /* The chunk wasn't reused, so we must free it. */ - DestroyPropTreeKidsChunk(rt, freeChunk); - } - - /* Clear id so we know (above) that sprop is on the freelist. */ - sprop->id = JSVAL_NULL; - FREENODE_INSERT(rt->propertyFreeList, sprop); - JS_RUNTIME_UNMETER(rt, livePropTreeNodes); - } - - /* If a contains no live properties, return it to the malloc heap. */ - if (liveCount == 0) { - for (sprop = (JSScopeProperty *) a->base; sprop < limit; sprop++) - FREENODE_REMOVE(sprop); - JS_ARENA_DESTROY(&rt->propertyArenaPool, a, ap); - } else { -#ifdef DUMP_SCOPE_STATS - livePropCapacity += limit - (JSScopeProperty *) a->base; - totalLiveCount += liveCount; -#endif - ap = &a->next; - } - } - -#ifdef DUMP_SCOPE_STATS - fprintf(logfp, " arenautil %g%%\n", - (totalLiveCount * 100.0) / livePropCapacity); - fflush(logfp); -#endif - -#ifdef DUMP_PROPERTY_TREE - { - FILE *dumpfp = fopen("/tmp/proptree.dump", "w"); - if (dumpfp) { - JSPropertyTreeEntry *pte, *end; - - pte = (JSPropertyTreeEntry *) rt->propertyTreeHash.entryStore; - end = pte + JS_DHASH_TABLE_SIZE(&rt->propertyTreeHash); - while (pte < end) { - if (pte->child) - DumpSubtree(pte->child, 0, dumpfp); - pte++; - } - fclose(dumpfp); - } - } -#endif -} - -JSBool -js_InitPropertyTree(JSRuntime *rt) -{ - if (!JS_DHashTableInit(&rt->propertyTreeHash, &PropertyTreeHashOps, NULL, - sizeof(JSPropertyTreeEntry), JS_DHASH_MIN_SIZE)) { - rt->propertyTreeHash.ops = NULL; - return JS_FALSE; - } - JS_InitArenaPool(&rt->propertyArenaPool, "properties", - 256 * sizeof(JSScopeProperty), sizeof(void *)); - return JS_TRUE; -} - -void -js_FinishPropertyTree(JSRuntime *rt) -{ - if (rt->propertyTreeHash.ops) { - JS_DHashTableFinish(&rt->propertyTreeHash); - rt->propertyTreeHash.ops = NULL; - } - JS_FinishArenaPool(&rt->propertyArenaPool); -} diff --git a/spidermonkey/src/jsscope.h b/spidermonkey/src/jsscope.h deleted file mode 100644 index 0565d4d..0000000 --- a/spidermonkey/src/jsscope.h +++ /dev/null @@ -1,407 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsscope_h___ -#define jsscope_h___ -/* - * JS symbol tables. - */ -#include "jstypes.h" -#include "jsobj.h" -#include "jsprvtd.h" -#include "jspubtd.h" - -#ifdef JS_THREADSAFE -# include "jslock.h" -#endif - -/* - * Given P independent, non-unique properties each of size S words mapped by - * all scopes in a runtime, construct a property tree of N nodes each of size - * S+L words (L for tree linkage). A nominal L value is 2 for leftmost-child - * and right-sibling links. We hope that the N < P by enough that the space - * overhead of L, and the overhead of scope entries pointing at property tree - * nodes, is worth it. - * - * The tree construction goes as follows. If any empty scope in the runtime - * has a property X added to it, find or create a node under the tree root - * labeled X, and set scope->lastProp to point at that node. If any non-empty - * scope whose most recently added property is labeled Y has another property - * labeled Z added, find or create a node for Z under the node that was added - * for Y, and set scope->lastProp to point at that node. - * - * A property is labeled by its members' values: id, getter, setter, slot, - * attributes, tiny or short id, and a field telling for..in order. Note that - * labels are not unique in the tree, but they are unique among a node's kids - * (barring rare and benign multi-threaded race condition outcomes, see below) - * and along any ancestor line from the tree root to a given leaf node (except - * for the hard case of duplicate formal parameters to a function). - * - * Thus the root of the tree represents all empty scopes, and the first ply - * of the tree represents all scopes containing one property, etc. Each node - * in the tree can stand for any number of scopes having the same ordered set - * of properties, where that node was the last added to the scope. (We need - * not store the root of the tree as a node, and do not -- all we need are - * links to its kids.) - * - * Sidebar on for..in loop order: ECMA requires no particular order, but this - * implementation has promised and delivered property definition order, and - * compatibility is king. We could use an order number per property, which - * would require a sort in js_Enumerate, and an entry order generation number - * per scope. An order number beats a list, which should be doubly-linked for - * O(1) delete. An even better scheme is to use a parent link in the property - * tree, so that the ancestor line can be iterated from scope->lastProp when - * filling in a JSIdArray from back to front. This parent link also helps the - * GC to sweep properties iteratively. - * - * What if a property Y is deleted from a scope? If Y is the last property in - * the scope, we simply adjust the scope's lastProp member after we remove the - * scope's hash-table entry pointing at that property node. The parent link - * mentioned in the for..in sidebar above makes this adjustment O(1). But if - * Y comes between X and Z in the scope, then we might have to "fork" the tree - * at X, leaving X->Y->Z in case other scopes have those properties added in - * that order; and to finish the fork, we'd add a node labeled Z with the path - * X->Z, if it doesn't exist. This could lead to lots of extra nodes, and to - * O(n^2) growth when deleting lots of properties. - * - * Rather, for O(1) growth all around, we should share the path X->Y->Z among - * scopes having those three properties added in that order, and among scopes - * having only X->Z where Y was deleted. All such scopes have a lastProp that - * points to the Z child of Y. But a scope in which Y was deleted does not - * have a table entry for Y, and when iterating that scope by traversing the - * ancestor line from Z, we will have to test for a table entry for each node, - * skipping nodes that lack entries. - * - * What if we add Y again? X->Y->Z->Y is wrong and we'll enumerate Y twice. - * Therefore we must fork in such a case, if not earlier. Because delete is - * "bursty", we should not fork eagerly. Delaying a fork till we are at risk - * of adding Y after it was deleted already requires a flag in the JSScope, to - * wit, SCOPE_MIDDLE_DELETE. - * - * What about thread safety? If the property tree operations done by requests - * are find-node and insert-node, then the only hazard is duplicate insertion. - * This is harmless except for minor bloat. When all requests have ended or - * been suspended, the GC is free to sweep the tree after marking all nodes - * reachable from scopes, performing remove-node operations as needed. Note - * also that the stable storage of the property nodes during active requests - * permits the property cache (see jsinterp.h) to dereference JSScopeProperty - * weak references safely. - * - * Is the property tree worth it compared to property storage in each table's - * entries? To decide, we must find the relation <> between the words used - * with a property tree and the words required without a tree. - * - * Model all scopes as one super-scope of capacity T entries (T a power of 2). - * Let alpha be the load factor of this double hash-table. With the property - * tree, each entry in the table is a word-sized pointer to a node that can be - * shared by many scopes. But all such pointers are overhead compared to the - * situation without the property tree, where the table stores property nodes - * directly, as entries each of size S words. With the property tree, we need - * L=2 extra words per node for siblings and kids pointers. Without the tree, - * (1-alpha)*S*T words are wasted on free or removed sentinel-entries required - * by double hashing. - * - * Therefore, - * - * (property tree) <> (no property tree) - * N*(S+L) + T <> S*T - * N*(S+L) + T <> P*S + (1-alpha)*S*T - * N*(S+L) + alpha*T + (1-alpha)*T <> P*S + (1-alpha)*S*T - * - * Note that P is alpha*T by definition, so - * - * N*(S+L) + P + (1-alpha)*T <> P*S + (1-alpha)*S*T - * N*(S+L) <> P*S - P + (1-alpha)*S*T - (1-alpha)*T - * N*(S+L) <> (P + (1-alpha)*T) * (S-1) - * N*(S+L) <> (P + (1-alpha)*P/alpha) * (S-1) - * N*(S+L) <> P * (1/alpha) * (S-1) - * - * Let N = P*beta for a compression ratio beta, beta <= 1: - * - * P*beta*(S+L) <> P * (1/alpha) * (S-1) - * beta*(S+L) <> (S-1)/alpha - * beta <> (S-1)/((S+L)*alpha) - * - * For S = 6 (32-bit architectures) and L = 2, the property tree wins iff - * - * beta < 5/(8*alpha) - * - * We ensure that alpha <= .75, so the property tree wins if beta < .83_. An - * average beta from recent Mozilla browser startups was around .6. - * - * Can we reduce L? Observe that the property tree degenerates into a list of - * lists if at most one property Y follows X in all scopes. In or near such a - * case, we waste a word on the right-sibling link outside of the root ply of - * the tree. Note also that the root ply tends to be large, so O(n^2) growth - * searching it is likely, indicating the need for hashing (but with increased - * thread safety costs). - * - * If only K out of N nodes in the property tree have more than one child, we - * could eliminate the sibling link and overlay a children list or hash-table - * pointer on the leftmost-child link (which would then be either null or an - * only-child link; the overlay could be tagged in the low bit of the pointer, - * or flagged elsewhere in the property tree node, although such a flag must - * not be considered when comparing node labels during tree search). - * - * For such a system, L = 1 + (K * averageChildrenTableSize) / N instead of 2. - * If K << N, L approaches 1 and the property tree wins if beta < .95. - * - * We observe that fan-out below the root ply of the property tree appears to - * have extremely low degree (see the MeterPropertyTree code that histograms - * child-counts in jsscope.c), so instead of a hash-table we use a linked list - * of child node pointer arrays ("kid chunks"). The details are isolated in - * jsscope.c; others must treat JSScopeProperty.kids as opaque. We leave it - * strongly typed for debug-ability of the common (null or one-kid) cases. - * - * One final twist (can you stand it?): the mean number of entries per scope - * in Mozilla is < 5, with a large standard deviation (~8). Instead of always - * allocating scope->table, we leave it null while initializing all the other - * scope members as if it were non-null and minimal-length. Until a property - * is added that crosses the threshold of 6 or more entries for hashing, or - * until a "middle delete" occurs, we use linear search from scope->lastProp - * to find a given id, and save on the space overhead of a hash table. - */ - -struct JSScope { - JSObjectMap map; /* base class state */ - JSObject *object; /* object that owns this scope */ - uint8 flags; /* flags, see below */ - int8 hashShift; /* multiplicative hash shift */ - uint16 spare; /* reserved */ - uint32 entryCount; /* number of entries in table */ - uint32 removedCount; /* removed entry sentinels in table */ - JSScopeProperty **table; /* table of ptrs to shared tree nodes */ - JSScopeProperty *lastProp; /* pointer to last property added */ -#ifdef JS_THREADSAFE - JSContext *ownercx; /* creating context, NULL if shared */ - JSThinLock lock; /* binary semaphore protecting scope */ - union { /* union lockful and lock-free state: */ - jsrefcount count; /* lock entry count for reentrancy */ - JSScope *link; /* next link in rt->scopeSharingTodo */ - } u; -#ifdef DEBUG - const char *file[4]; /* file where lock was (re-)taken */ - unsigned int line[4]; /* line where lock was (re-)taken */ -#endif -#endif -}; - -#define OBJ_SCOPE(obj) ((JSScope *)(obj)->map) - -/* By definition, hashShift = JS_DHASH_BITS - log2(capacity). */ -#define SCOPE_CAPACITY(scope) JS_BIT(JS_DHASH_BITS-(scope)->hashShift) - -/* Scope flags and some macros to hide them from other files than jsscope.c. */ -#define SCOPE_MIDDLE_DELETE 0x0001 -#define SCOPE_SEALED 0x0002 - -#define SCOPE_HAD_MIDDLE_DELETE(scope) ((scope)->flags & SCOPE_MIDDLE_DELETE) -#define SCOPE_SET_MIDDLE_DELETE(scope) ((scope)->flags |= SCOPE_MIDDLE_DELETE) -#define SCOPE_CLR_MIDDLE_DELETE(scope) ((scope)->flags &= ~SCOPE_MIDDLE_DELETE) - -#define SCOPE_IS_SEALED(scope) ((scope)->flags & SCOPE_SEALED) -#define SCOPE_SET_SEALED(scope) ((scope)->flags |= SCOPE_SEALED) -#if 0 -/* - * Don't define this, it can't be done safely because JS_LOCK_OBJ will avoid - * taking the lock if the object owns its scope and the scope is sealed. - */ -#define SCOPE_CLR_SEALED(scope) ((scope)->flags &= ~SCOPE_SEALED) -#endif - -/* - * A little information hiding for scope->lastProp, in case it ever becomes - * a tagged pointer again. - */ -#define SCOPE_LAST_PROP(scope) ((scope)->lastProp) -#define SCOPE_REMOVE_LAST_PROP(scope) ((scope)->lastProp = \ - (scope)->lastProp->parent) - -struct JSScopeProperty { - jsid id; /* int-tagged jsval/untagged JSAtom* */ - JSPropertyOp getter; /* getter and setter hooks or objects */ - JSPropertyOp setter; - uint32 slot; /* index in obj->slots vector */ - uint8 attrs; /* attributes, see jsapi.h JSPROP_* */ - uint8 flags; /* flags, see below for defines */ - int16 shortid; /* tinyid, or local arg/var index */ - JSScopeProperty *parent; /* parent node, reverse for..in order */ - JSScopeProperty *kids; /* null, single child, or a tagged ptr - to many-kids data structure */ -}; - -/* JSScopeProperty pointer tag bit indicating a collision. */ -#define SPROP_COLLISION ((jsuword)1) -#define SPROP_REMOVED ((JSScopeProperty *) SPROP_COLLISION) - -/* Macros to get and set sprop pointer values and collision flags. */ -#define SPROP_IS_FREE(sprop) ((sprop) == NULL) -#define SPROP_IS_REMOVED(sprop) ((sprop) == SPROP_REMOVED) -#define SPROP_IS_LIVE(sprop) ((sprop) > SPROP_REMOVED) -#define SPROP_FLAG_COLLISION(spp,sprop) (*(spp) = (JSScopeProperty *) \ - ((jsuword)(sprop) | SPROP_COLLISION)) -#define SPROP_HAD_COLLISION(sprop) ((jsuword)(sprop) & SPROP_COLLISION) -#define SPROP_FETCH(spp) SPROP_CLEAR_COLLISION(*(spp)) - -#define SPROP_CLEAR_COLLISION(sprop) \ - ((JSScopeProperty *) ((jsuword)(sprop) & ~SPROP_COLLISION)) - -#define SPROP_STORE_PRESERVING_COLLISION(spp, sprop) \ - (*(spp) = (JSScopeProperty *) ((jsuword)(sprop) \ - | SPROP_HAD_COLLISION(*(spp)))) - -/* Bits stored in sprop->flags. */ -#define SPROP_MARK 0x01 -#define SPROP_IS_DUPLICATE 0x02 -#define SPROP_IS_ALIAS 0x04 -#define SPROP_HAS_SHORTID 0x08 -#define SPROP_IS_HIDDEN 0x10 /* a normally-hidden property, - e.g., function arg or var */ - -/* - * If SPROP_HAS_SHORTID is set in sprop->flags, we use sprop->shortid rather - * than id when calling sprop's getter or setter. - */ -#define SPROP_USERID(sprop) \ - (((sprop)->flags & SPROP_HAS_SHORTID) ? INT_TO_JSVAL((sprop)->shortid) \ - : ID_TO_VALUE((sprop)->id)) - -#define SPROP_INVALID_SLOT 0xffffffff - -#define SLOT_IN_SCOPE(slot,scope) ((slot) < (scope)->map.freeslot) -#define SPROP_HAS_VALID_SLOT(sprop,scope) SLOT_IN_SCOPE((sprop)->slot, scope) - -#define SPROP_HAS_STUB_GETTER(sprop) (!(sprop)->getter) -#define SPROP_HAS_STUB_SETTER(sprop) (!(sprop)->setter) - -/* - * NB: SPROP_GET must not be called if SPROP_HAS_STUB_GETTER(sprop). - */ -#define SPROP_GET(cx,sprop,obj,obj2,vp) \ - (((sprop)->attrs & JSPROP_GETTER) \ - ? js_InternalGetOrSet(cx, obj, (sprop)->id, \ - OBJECT_TO_JSVAL((sprop)->getter), JSACC_READ, \ - 0, 0, vp) \ - : (sprop)->getter(cx, OBJ_THIS_OBJECT(cx,obj), SPROP_USERID(sprop), vp)) - -/* - * NB: SPROP_SET must not be called if (SPROP_HAS_STUB_SETTER(sprop) && - * !(sprop->attrs & JSPROP_GETTER)). - */ -#define SPROP_SET(cx,sprop,obj,obj2,vp) \ - (((sprop)->attrs & JSPROP_SETTER) \ - ? js_InternalGetOrSet(cx, obj, (sprop)->id, \ - OBJECT_TO_JSVAL((sprop)->setter), JSACC_WRITE, \ - 1, vp, vp) \ - : ((sprop)->attrs & JSPROP_GETTER) \ - ? (JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, \ - JSMSG_GETTER_ONLY, NULL), JS_FALSE) \ - : (sprop)->setter(cx, OBJ_THIS_OBJECT(cx,obj), SPROP_USERID(sprop), vp)) - -/* Macro for common expression to test for shared permanent attributes. */ -#define SPROP_IS_SHARED_PERMANENT(sprop) \ - ((~(sprop)->attrs & (JSPROP_SHARED | JSPROP_PERMANENT)) == 0) - -extern JSScope * -js_GetMutableScope(JSContext *cx, JSObject *obj); - -extern JSScope * -js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, - JSObject *obj); - -extern void -js_DestroyScope(JSContext *cx, JSScope *scope); - -#define ID_TO_VALUE(id) (JSID_IS_ATOM(id) ? ATOM_JSID_TO_JSVAL(id) : \ - JSID_IS_OBJECT(id) ? OBJECT_JSID_TO_JSVAL(id) : \ - (jsval)(id)) -#define HASH_ID(id) (JSID_IS_ATOM(id) ? JSID_TO_ATOM(id)->number : \ - JSID_IS_OBJECT(id) ? (jsatomid) JSID_CLRTAG(id) : \ - (jsatomid) JSID_TO_INT(id)) - -extern JS_FRIEND_API(JSScopeProperty **) -js_SearchScope(JSScope *scope, jsid id, JSBool adding); - -#define SCOPE_GET_PROPERTY(scope, id) \ - SPROP_FETCH(js_SearchScope(scope, id, JS_FALSE)) - -#define SCOPE_HAS_PROPERTY(scope, sprop) \ - (SCOPE_GET_PROPERTY(scope, (sprop)->id) == (sprop)) - -extern JSScopeProperty * -js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id, - JSPropertyOp getter, JSPropertyOp setter, uint32 slot, - uintN attrs, uintN flags, intN shortid); - -extern JSScopeProperty * -js_ChangeScopePropertyAttrs(JSContext *cx, JSScope *scope, - JSScopeProperty *sprop, uintN attrs, uintN mask, - JSPropertyOp getter, JSPropertyOp setter); - -extern JSBool -js_RemoveScopeProperty(JSContext *cx, JSScope *scope, jsid id); - -extern void -js_ClearScope(JSContext *cx, JSScope *scope); - -/* - * These macros used to inline short code sequences, but they grew over time. - * We retain them for internal backward compatibility, and in case one or both - * ever shrink to inline-able size. - */ -#define MARK_ID(cx,id) js_MarkId(cx, id) -#define MARK_SCOPE_PROPERTY(cx,sprop) js_MarkScopeProperty(cx, sprop) - -extern void -js_MarkId(JSContext *cx, jsid id); - -extern void -js_MarkScopeProperty(JSContext *cx, JSScopeProperty *sprop); - -extern void -js_SweepScopeProperties(JSRuntime *rt); - -extern JSBool -js_InitPropertyTree(JSRuntime *rt); - -extern void -js_FinishPropertyTree(JSRuntime *rt); - -#endif /* jsscope_h___ */ diff --git a/spidermonkey/src/jsscript.c b/spidermonkey/src/jsscript.c deleted file mode 100644 index 73298a4..0000000 --- a/spidermonkey/src/jsscript.c +++ /dev/null @@ -1,1717 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS script operations. - */ -#include "jsstddef.h" -#include -#include "jstypes.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jsprf.h" -#include "jsapi.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsdbgapi.h" -#include "jsemit.h" -#include "jsfun.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsopcode.h" -#include "jsscript.h" -#if JS_HAS_XDR -#include "jsxdrapi.h" -#endif - -#if JS_HAS_SCRIPT_OBJECT - -static const char js_script_exec[] = "Script.prototype.exec"; -static const char js_script_compile[] = "Script.prototype.compile"; - -/* - * This routine requires that obj has been locked previously. - */ -static jsint -GetScriptExecDepth(JSContext *cx, JSObject *obj) -{ - jsval v; - - JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj)); - v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_START(&js_ScriptClass)); - return JSVAL_TO_INT(v); -} - -static void -AdjustScriptExecDepth(JSContext *cx, JSObject *obj, jsint delta) -{ - jsint execDepth; - - JS_LOCK_OBJ(cx, obj); - execDepth = GetScriptExecDepth(cx, obj); - LOCKED_OBJ_SET_SLOT(obj, JSSLOT_START(&js_ScriptClass), - INT_TO_JSVAL(execDepth + delta)); - JS_UNLOCK_OBJ(cx, obj); -} - -#if JS_HAS_TOSOURCE -static JSBool -script_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - uint32 indent; - JSScript *script; - size_t i, j, k, n; - char buf[16]; - jschar *s, *t; - JSString *str; - - if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) - return JS_FALSE; - - indent = 0; - if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) - return JS_FALSE; - - script = (JSScript *) JS_GetPrivate(cx, obj); - - /* Let n count the source string length, j the "front porch" length. */ - j = JS_snprintf(buf, sizeof buf, "(new %s(", js_ScriptClass.name); - n = j + 2; - if (!script) { - /* Let k count the constructor argument string length. */ - k = 0; - s = NULL; /* quell GCC overwarning */ - } else { - str = JS_DecompileScript(cx, script, "Script.prototype.toSource", - (uintN)indent); - if (!str) - return JS_FALSE; - str = js_QuoteString(cx, str, '\''); - if (!str) - return JS_FALSE; - s = JSSTRING_CHARS(str); - k = JSSTRING_LENGTH(str); - n += k; - } - - /* Allocate the source string and copy into it. */ - t = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); - if (!t) - return JS_FALSE; - for (i = 0; i < j; i++) - t[i] = buf[i]; - for (j = 0; j < k; i++, j++) - t[i] = s[j]; - t[i++] = ')'; - t[i++] = ')'; - t[i] = 0; - - /* Create and return a JS string for t. */ - str = JS_NewUCString(cx, t, n); - if (!str) { - JS_free(cx, t); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} -#endif /* JS_HAS_TOSOURCE */ - -static JSBool -script_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - uint32 indent; - JSScript *script; - JSString *str; - - indent = 0; - if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) - return JS_FALSE; - - if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) - return JS_FALSE; - script = (JSScript *) JS_GetPrivate(cx, obj); - if (!script) { - *rval = STRING_TO_JSVAL(cx->runtime->emptyString); - return JS_TRUE; - } - - str = JS_DecompileScript(cx, script, "Script.prototype.toString", - (uintN)indent); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -script_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - JSObject *scopeobj; - jsval v; - JSScript *script, *oldscript; - JSStackFrame *fp, *caller; - const char *file; - uintN line; - JSPrincipals *principals; - jsint execDepth; - - /* Make sure obj is a Script object. */ - if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) - return JS_FALSE; - - /* If no args, leave private undefined and return early. */ - if (argc == 0) - goto out; - - /* Otherwise, the first arg is the script source to compile. */ - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str); - - scopeobj = NULL; - if (argc >= 2) { - if (!js_ValueToObject(cx, argv[1], &scopeobj)) - return JS_FALSE; - argv[1] = OBJECT_TO_JSVAL(scopeobj); - } - - /* Compile using the caller's scope chain, which js_Invoke passes to fp. */ - fp = cx->fp; - caller = JS_GetScriptedCaller(cx, fp); - JS_ASSERT(!caller || fp->scopeChain == caller->scopeChain); - - if (caller) { - if (!scopeobj) { - scopeobj = js_GetScopeChain(cx, caller); - if (!scopeobj) - return JS_FALSE; - fp->scopeChain = scopeobj; /* for the compiler's benefit */ - } - - principals = JS_EvalFramePrincipals(cx, fp, caller); - if (principals == caller->script->principals) { - file = caller->script->filename; - line = js_PCToLineNumber(cx, caller->script, caller->pc); - } else { - file = principals->codebase; - line = 0; - } - } else { - file = NULL; - line = 0; - principals = NULL; - } - - /* Ensure we compile this script with the right (inner) principals. */ - scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_compile); - if (!scopeobj) - return JS_FALSE; - - /* - * Compile the new script using the caller's scope chain, a la eval(). - * Unlike jsobj.c:obj_eval, however, we do not set JSFRAME_EVAL in fp's - * flags, because compilation is here separated from execution, and the - * run-time scope chain may not match the compile-time. JSFRAME_EVAL is - * tested in jsemit.c and jsscan.c to optimize based on identity of run- - * and compile-time scope. - */ - fp->flags |= JSFRAME_SCRIPT_OBJECT; - script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals, - JSSTRING_CHARS(str), - JSSTRING_LENGTH(str), - file, line); - if (!script) - return JS_FALSE; - - JS_LOCK_OBJ(cx, obj); - execDepth = GetScriptExecDepth(cx, obj); - - /* - * execDepth must be 0 to allow compilation here, otherwise the JSScript - * struct can be released while running. - */ - if (execDepth > 0) { - JS_UNLOCK_OBJ(cx, obj); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_COMPILE_EXECED_SCRIPT); - return JS_FALSE; - } - - /* Swap script for obj's old script, if any. */ - v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PRIVATE); - oldscript = !JSVAL_IS_VOID(v) ? (JSScript *) JSVAL_TO_PRIVATE(v) : NULL; - LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(script)); - JS_UNLOCK_OBJ(cx, obj); - - if (oldscript) - js_DestroyScript(cx, oldscript); - - script->object = obj; - js_CallNewScriptHook(cx, script, NULL); - -out: - /* Return the object. */ - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -static JSBool -script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSObject *scopeobj, *parent; - JSStackFrame *fp, *caller; - JSScript *script; - JSBool ok; - - if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) - return JS_FALSE; - - scopeobj = NULL; - if (argc) { - if (!js_ValueToObject(cx, argv[0], &scopeobj)) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(scopeobj); - } - - /* - * Emulate eval() by using caller's this, var object, sharp array, etc., - * all propagated by js_Execute via a non-null fourth (down) argument to - * js_Execute. If there is no scripted caller, js_Execute uses its second - * (chain) argument to set the exec frame's varobj, thisp, and scopeChain. - * - * Unlike eval, which the compiler detects, Script.prototype.exec may be - * called from a lightweight function, or even from native code (in which - * case fp->varobj and fp->scopeChain are null). If exec is called from - * a lightweight function, we will need to get a Call object representing - * its frame, to act as the var object and scope chain head. - */ - fp = cx->fp; - caller = JS_GetScriptedCaller(cx, fp); - if (caller && !caller->varobj) { - /* Called from a lightweight function. */ - JS_ASSERT(caller->fun && !JSFUN_HEAVYWEIGHT_TEST(caller->fun->flags)); - - /* Scope chain links from Call object to callee's parent. */ - parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(caller->argv[-2])); - if (!js_GetCallObject(cx, caller, parent)) - return JS_FALSE; - } - - if (!scopeobj) { - /* No scope object passed in: try to use the caller's scope chain. */ - if (caller) { - /* - * Load caller->scopeChain after the conditional js_GetCallObject - * call above, which resets scopeChain as well as varobj. - */ - scopeobj = js_GetScopeChain(cx, caller); - if (!scopeobj) - return JS_FALSE; - } else { - /* - * Called from native code, so we don't know what scope object to - * use. We could use parent (see above), but Script.prototype.exec - * might be a shared/sealed "superglobal" method. A more general - * approach would use cx->globalObject, which will be the same as - * exec.__parent__ in the non-superglobal case. In the superglobal - * case it's the right object: the global, not the superglobal. - */ - scopeobj = cx->globalObject; - } - } - - scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_exec); - if (!scopeobj) - return JS_FALSE; - - /* Keep track of nesting depth for the script. */ - AdjustScriptExecDepth(cx, obj, 1); - - /* Must get to out label after this */ - script = (JSScript *) JS_GetPrivate(cx, obj); - if (!script) { - ok = JS_FALSE; - goto out; - } - - /* Belt-and-braces: check that this script object has access to scopeobj. */ - ok = js_CheckPrincipalsAccess(cx, scopeobj, script->principals, - CLASS_ATOM(cx, Script)); - if (!ok) - goto out; - - ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); - -out: - AdjustScriptExecDepth(cx, obj, -1); - return ok; -} - -#if JS_HAS_XDR - -static JSBool -XDRAtomMap(JSXDRState *xdr, JSAtomMap *map) -{ - JSContext *cx; - uint32 natoms, i, index; - JSAtom **atoms; - - cx = xdr->cx; - - if (xdr->mode == JSXDR_ENCODE) - natoms = (uint32)map->length; - - if (!JS_XDRUint32(xdr, &natoms)) - return JS_FALSE; - - if (xdr->mode == JSXDR_ENCODE) { - atoms = map->vector; - } else { - if (natoms == 0) { - atoms = NULL; - } else { - atoms = (JSAtom **) JS_malloc(cx, (size_t)natoms * sizeof *atoms); - if (!atoms) - return JS_FALSE; -#ifdef DEBUG - memset(atoms, 0, (size_t)natoms * sizeof *atoms); -#endif - } - - map->vector = atoms; - map->length = natoms; - } - - for (i = 0; i != natoms; ++i) { - if (xdr->mode == JSXDR_ENCODE) - index = i; - if (!JS_XDRUint32(xdr, &index)) - goto bad; - - /* - * Assert that, when decoding, the read index is valid and points to - * an unoccupied element of atoms array. - */ - JS_ASSERT(index < natoms); - JS_ASSERT(xdr->mode == JSXDR_ENCODE || !atoms[index]); - if (!js_XDRAtom(xdr, &atoms[index])) - goto bad; - } - - return JS_TRUE; - - bad: - if (xdr->mode == JSXDR_DECODE) { - JS_free(cx, atoms); - map->vector = NULL; - map->length = 0; - } - - return JS_FALSE; -} - -JSBool -js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic) -{ - JSContext *cx; - JSScript *script, *newscript, *oldscript; - uint32 length, lineno, depth, magic, nsrcnotes, ntrynotes; - uint32 prologLength, version; - JSBool filenameWasSaved; - jssrcnote *notes, *sn; - - cx = xdr->cx; - script = *scriptp; - nsrcnotes = ntrynotes = 0; - filenameWasSaved = JS_FALSE; - notes = NULL; - - /* - * Encode prologLength and version after script->length (_2 or greater), - * but decode both new (>= _2) and old, prolog&version-free (_1) scripts. - * Version _3 supports principals serialization. Version _4 reorders the - * nsrcnotes and ntrynotes fields to come before everything except magic, - * length, prologLength, and version, so that srcnote and trynote storage - * can be allocated as part of the JSScript (along with bytecode storage). - * - * So far, the magic number has not changed for every jsopcode.tbl change. - * We stipulate forward compatibility by requiring old bytecodes never to - * change or go away (modulo a few exceptions before the XDR interfaces - * evolved, and a few exceptions during active trunk development). With - * the addition of JSOP_STOP to support JS_THREADED_INTERP, we make a new - * magic number (_5) so that we know to append JSOP_STOP to old scripts - * when deserializing. - */ - if (xdr->mode == JSXDR_ENCODE) - magic = JSXDR_MAGIC_SCRIPT_CURRENT; - if (!JS_XDRUint32(xdr, &magic)) - return JS_FALSE; - JS_ASSERT((uint32)JSXDR_MAGIC_SCRIPT_5 - (uint32)JSXDR_MAGIC_SCRIPT_1 == 4); - if (magic - (uint32)JSXDR_MAGIC_SCRIPT_1 > 4) { - if (!hasMagic) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_SCRIPT_MAGIC); - return JS_FALSE; - } - *hasMagic = JS_FALSE; - return JS_TRUE; - } - if (hasMagic) - *hasMagic = JS_TRUE; - - if (xdr->mode == JSXDR_ENCODE) { - length = script->length; - prologLength = PTRDIFF(script->main, script->code, jsbytecode); - JS_ASSERT((int16)script->version != JSVERSION_UNKNOWN); - version = (uint32)script->version | (script->numGlobalVars << 16); - lineno = (uint32)script->lineno; - depth = (uint32)script->depth; - - /* Count the srcnotes, keeping notes pointing at the first one. */ - notes = SCRIPT_NOTES(script); - for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) - continue; - nsrcnotes = PTRDIFF(sn, notes, jssrcnote); - nsrcnotes++; /* room for the terminator */ - - /* Count the trynotes. */ - if (script->trynotes) { - while (script->trynotes[ntrynotes].catchStart) - ntrynotes++; - ntrynotes++; /* room for the end marker */ - } - } - - if (!JS_XDRUint32(xdr, &length)) - return JS_FALSE; - if (magic >= JSXDR_MAGIC_SCRIPT_2) { - if (!JS_XDRUint32(xdr, &prologLength)) - return JS_FALSE; - if (!JS_XDRUint32(xdr, &version)) - return JS_FALSE; - - /* To fuse allocations, we need srcnote and trynote counts early. */ - if (magic >= JSXDR_MAGIC_SCRIPT_4) { - if (!JS_XDRUint32(xdr, &nsrcnotes)) - return JS_FALSE; - if (!JS_XDRUint32(xdr, &ntrynotes)) - return JS_FALSE; - } - } - - if (xdr->mode == JSXDR_DECODE) { - size_t alloclength = length; - if (magic < JSXDR_MAGIC_SCRIPT_5) - ++alloclength; /* add a byte for JSOP_STOP */ - - script = js_NewScript(cx, alloclength, nsrcnotes, ntrynotes); - if (!script) - return JS_FALSE; - if (magic >= JSXDR_MAGIC_SCRIPT_2) { - script->main += prologLength; - script->version = (JSVersion) (version & 0xffff); - script->numGlobalVars = (uint16) (version >> 16); - - /* If we know nsrcnotes, we allocated space for notes in script. */ - if (magic >= JSXDR_MAGIC_SCRIPT_4) - notes = SCRIPT_NOTES(script); - } - *scriptp = script; - } - - /* - * Control hereafter must goto error on failure, in order for the DECODE - * case to destroy script and conditionally free notes, which if non-null - * in the (DECODE and magic < _4) case must point at a temporary vector - * allocated just below. - */ - oldscript = xdr->script; - xdr->script = script; - if (!JS_XDRBytes(xdr, (char *)script->code, length * sizeof(jsbytecode)) || - !XDRAtomMap(xdr, &script->atomMap)) { - goto error; - } - - if (magic < JSXDR_MAGIC_SCRIPT_5) { - if (xdr->mode == JSXDR_DECODE) { - /* - * Append JSOP_STOP to old scripts, to relieve the interpreter - * from having to bounds-check pc. Also take care to increment - * length, as it is used below and must count all bytecode. - */ - script->code[length++] = JSOP_STOP; - } - - if (magic < JSXDR_MAGIC_SCRIPT_4) { - if (!JS_XDRUint32(xdr, &nsrcnotes)) - goto error; - if (xdr->mode == JSXDR_DECODE) { - notes = (jssrcnote *) - JS_malloc(cx, nsrcnotes * sizeof(jssrcnote)); - if (!notes) - goto error; - } - } - } - - if (!JS_XDRBytes(xdr, (char *)notes, nsrcnotes * sizeof(jssrcnote)) || - !JS_XDRCStringOrNull(xdr, (char **)&script->filename) || - !JS_XDRUint32(xdr, &lineno) || - !JS_XDRUint32(xdr, &depth) || - (magic < JSXDR_MAGIC_SCRIPT_4 && !JS_XDRUint32(xdr, &ntrynotes))) { - goto error; - } - - /* Script principals transcoding support comes with versions >= _3. */ - if (magic >= JSXDR_MAGIC_SCRIPT_3) { - JSPrincipals *principals; - uint32 encodeable; - - if (xdr->mode == JSXDR_ENCODE) { - principals = script->principals; - encodeable = (cx->runtime->principalsTranscoder != NULL); - if (!JS_XDRUint32(xdr, &encodeable)) - goto error; - if (encodeable && - !cx->runtime->principalsTranscoder(xdr, &principals)) { - goto error; - } - } else { - if (!JS_XDRUint32(xdr, &encodeable)) - goto error; - if (encodeable) { - if (!cx->runtime->principalsTranscoder) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CANT_DECODE_PRINCIPALS); - goto error; - } - if (!cx->runtime->principalsTranscoder(xdr, &principals)) - goto error; - script->principals = principals; - } - } - } - - if (xdr->mode == JSXDR_DECODE) { - const char *filename = script->filename; - if (filename) { - filename = js_SaveScriptFilename(cx, filename); - if (!filename) - goto error; - JS_free(cx, (void *) script->filename); - script->filename = filename; - filenameWasSaved = JS_TRUE; - } - script->lineno = (uintN)lineno; - script->depth = (uintN)depth; - - if (magic < JSXDR_MAGIC_SCRIPT_4) { - /* - * Argh, we have to reallocate script, copy notes into the extra - * space after the bytecodes, and free the temporary notes vector. - * First, add enough slop to nsrcnotes so we can align the address - * after the srcnotes of the first trynote. - */ - uint32 osrcnotes = nsrcnotes; - - if (ntrynotes) - nsrcnotes += JSTRYNOTE_ALIGNMASK; - newscript = (JSScript *) JS_realloc(cx, script, - sizeof(JSScript) + - length * sizeof(jsbytecode) + - nsrcnotes * sizeof(jssrcnote) + - ntrynotes * sizeof(JSTryNote)); - if (!newscript) - goto error; - - *scriptp = script = newscript; - script->code = (jsbytecode *)(script + 1); - script->main = script->code + prologLength; - memcpy(script->code + length, notes, osrcnotes * sizeof(jssrcnote)); - JS_free(cx, (void *) notes); - notes = NULL; - if (ntrynotes) { - script->trynotes = (JSTryNote *) - ((jsword)(SCRIPT_NOTES(script) + nsrcnotes) & - ~(jsword)JSTRYNOTE_ALIGNMASK); - memset(script->trynotes, 0, ntrynotes * sizeof(JSTryNote)); - } - } - } - - while (ntrynotes) { - JSTryNote *tn = &script->trynotes[--ntrynotes]; - uint32 start = (uint32) tn->start, - catchLength = (uint32) tn->length, - catchStart = (uint32) tn->catchStart; - - if (!JS_XDRUint32(xdr, &start) || - !JS_XDRUint32(xdr, &catchLength) || - !JS_XDRUint32(xdr, &catchStart)) { - goto error; - } - tn->start = (ptrdiff_t) start; - tn->length = (ptrdiff_t) catchLength; - tn->catchStart = (ptrdiff_t) catchStart; - } - - xdr->script = oldscript; - return JS_TRUE; - - error: - if (xdr->mode == JSXDR_DECODE) { - if (script->filename && !filenameWasSaved) { - JS_free(cx, (void *) script->filename); - script->filename = NULL; - } - if (notes && magic < JSXDR_MAGIC_SCRIPT_4) - JS_free(cx, (void *) notes); - js_DestroyScript(cx, script); - *scriptp = NULL; - } - return JS_FALSE; -} - -#if JS_HAS_XDR_FREEZE_THAW -/* - * These cannot be exposed to web content, and chrome does not need them, so - * we take them out of the Mozilla client altogether. Fortunately, there is - * no way to serialize a native function (see fun_xdrObject in jsfun.c). - */ - -static JSBool -script_freeze(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXDRState *xdr; - JSScript *script; - JSBool ok, hasMagic; - uint32 len; - void *buf; - JSString *str; - - if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) - return JS_FALSE; - script = (JSScript *) JS_GetPrivate(cx, obj); - if (!script) - return JS_TRUE; - - /* create new XDR */ - xdr = JS_XDRNewMem(cx, JSXDR_ENCODE); - if (!xdr) - return JS_FALSE; - - /* write */ - ok = js_XDRScript(xdr, &script, &hasMagic); - if (!ok) - goto out; - if (!hasMagic) { - *rval = JSVAL_VOID; - goto out; - } - - buf = JS_XDRMemGetData(xdr, &len); - if (!buf) { - ok = JS_FALSE; - goto out; - } - - JS_ASSERT((jsword)buf % sizeof(jschar) == 0); - len /= sizeof(jschar); - str = JS_NewUCStringCopyN(cx, (jschar *)buf, len); - if (!str) { - ok = JS_FALSE; - goto out; - } - -#if IS_BIG_ENDIAN - { - jschar *chars; - uint32 i; - - /* Swap bytes in Unichars to keep frozen strings machine-independent. */ - chars = JS_GetStringChars(str); - for (i = 0; i < len; i++) - chars[i] = JSXDR_SWAB16(chars[i]); - } -#endif - *rval = STRING_TO_JSVAL(str); - -out: - JS_XDRDestroy(xdr); - return ok; -} - -static JSBool -script_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXDRState *xdr; - JSString *str; - void *buf; - uint32 len; - jsval v; - JSScript *script, *oldscript; - JSBool ok, hasMagic; - - if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) - return JS_FALSE; - - if (argc == 0) - return JS_TRUE; - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str); - - /* create new XDR */ - xdr = JS_XDRNewMem(cx, JSXDR_DECODE); - if (!xdr) - return JS_FALSE; - - buf = JS_GetStringChars(str); - len = JS_GetStringLength(str); -#if IS_BIG_ENDIAN - { - jschar *from, *to; - uint32 i; - - /* Swap bytes in Unichars to keep frozen strings machine-independent. */ - from = (jschar *)buf; - to = (jschar *) JS_malloc(cx, len * sizeof(jschar)); - if (!to) { - JS_XDRDestroy(xdr); - return JS_FALSE; - } - for (i = 0; i < len; i++) - to[i] = JSXDR_SWAB16(from[i]); - buf = (char *)to; - } -#endif - len *= sizeof(jschar); - JS_XDRMemSetData(xdr, buf, len); - - /* XXXbe should magic mismatch be error, or false return value? */ - ok = js_XDRScript(xdr, &script, &hasMagic); - if (!ok) - goto out; - if (!hasMagic) { - *rval = JSVAL_FALSE; - goto out; - } - - JS_LOCK_OBJ(cx, obj); - execDepth = GetScriptExecDepth(cx, obj); - - /* - * execDepth must be 0 to allow compilation here, otherwise the JSScript - * struct can be released while running. - */ - if (execDepth > 0) { - JS_UNLOCK_OBJ(cx, obj); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_COMPILE_EXECED_SCRIPT); - goto out; - } - - /* Swap script for obj's old script, if any. */ - v = LOCKED_OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - oldscript = !JSVAL_IS_VOID(v) ? (JSScript *) JSVAL_TO_PRIVATE(v) : NULL; - LOCKED_OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(script)); - JS_UNLOCK_OBJ(cx, obj); - - if (oldscript) - js_DestroyScript(cx, oldscript); - - script->object = obj; - js_CallNewScriptHook(cx, script, NULL); - -out: - /* - * We reset the buffer to be NULL so that it doesn't free the chars - * memory owned by str (argv[0]). - */ - JS_XDRMemSetData(xdr, NULL, 0); - JS_XDRDestroy(xdr); -#if IS_BIG_ENDIAN - JS_free(cx, buf); -#endif - *rval = JSVAL_TRUE; - return ok; -} - -static const char js_thaw_str[] = "thaw"; - -#endif /* JS_HAS_XDR_FREEZE_THAW */ -#endif /* JS_HAS_XDR */ - -static JSFunctionSpec script_methods[] = { -#if JS_HAS_TOSOURCE - {js_toSource_str, script_toSource, 0,0,0}, -#endif - {js_toString_str, script_toString, 0,0,0}, - {"compile", script_compile, 2,0,0}, - {"exec", script_exec, 1,0,0}, -#if JS_HAS_XDR_FREEZE_THAW - {"freeze", script_freeze, 0,0,0}, - {js_thaw_str, script_thaw, 1,0,0}, -#endif /* JS_HAS_XDR_FREEZE_THAW */ - {0,0,0,0,0} -}; - -#endif /* JS_HAS_SCRIPT_OBJECT */ - -static void -script_finalize(JSContext *cx, JSObject *obj) -{ - JSScript *script; - - script = (JSScript *) JS_GetPrivate(cx, obj); - if (script) - js_DestroyScript(cx, script); -} - -static JSBool -script_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ -#if JS_HAS_SCRIPT_OBJECT - return script_exec(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval); -#else - return JS_FALSE; -#endif -} - -static uint32 -script_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSScript *script; - - script = (JSScript *) JS_GetPrivate(cx, obj); - if (script) - js_MarkScript(cx, script); - return 0; -} - -#if !JS_HAS_SCRIPT_OBJECT -const char js_Script_str[] = "Script"; - -#define JSProto_Script JSProto_Object -#endif - -JS_FRIEND_DATA(JSClass) js_ScriptClass = { - js_Script_str, - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Script) | - JSCLASS_HAS_RESERVED_SLOTS(1), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, script_finalize, - NULL, NULL, script_call, NULL,/*XXXbe xdr*/ - NULL, NULL, script_mark, 0 -}; - -#if JS_HAS_SCRIPT_OBJECT - -static JSBool -Script(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - /* If not constructing, replace obj with a new Script object. */ - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); - if (!obj) - return JS_FALSE; - - /* - * script_compile does not use rval to root its temporaries - * so we can use it to root obj. - */ - *rval = OBJECT_TO_JSVAL(obj); - } - - if (!JS_SetReservedSlot(cx, obj, 0, INT_TO_JSVAL(0))) - return JS_FALSE; - - return script_compile(cx, obj, argc, argv, rval); -} - -#if JS_HAS_XDR_FREEZE_THAW - -static JSBool -script_static_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); - if (!obj) - return JS_FALSE; - if (!script_thaw(cx, obj, argc, argv, rval)) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -static JSFunctionSpec script_static_methods[] = { - {js_thaw_str, script_static_thaw, 1,0,0}, - {0,0,0,0,0} -}; - -#else /* !JS_HAS_XDR_FREEZE_THAW */ - -#define script_static_methods NULL - -#endif /* !JS_HAS_XDR_FREEZE_THAW */ - -JSObject * -js_InitScriptClass(JSContext *cx, JSObject *obj) -{ - return JS_InitClass(cx, obj, NULL, &js_ScriptClass, Script, 1, - NULL, script_methods, NULL, script_static_methods); -} - -#endif /* JS_HAS_SCRIPT_OBJECT */ - -/* - * Shared script filename management. - */ -JS_STATIC_DLL_CALLBACK(int) -js_compare_strings(const void *k1, const void *k2) -{ - return strcmp(k1, k2) == 0; -} - -/* Shared with jsatom.c to save code space. */ -extern void * JS_DLL_CALLBACK -js_alloc_table_space(void *priv, size_t size); - -extern void JS_DLL_CALLBACK -js_free_table_space(void *priv, void *item); - -/* NB: This struct overlays JSHashEntry -- see jshash.h, do not reorganize. */ -typedef struct ScriptFilenameEntry { - JSHashEntry *next; /* hash chain linkage */ - JSHashNumber keyHash; /* key hash function result */ - const void *key; /* ptr to filename, below */ - uint32 flags; /* user-defined filename prefix flags */ - JSPackedBool mark; /* GC mark flag */ - char filename[3]; /* two or more bytes, NUL-terminated */ -} ScriptFilenameEntry; - -JS_STATIC_DLL_CALLBACK(JSHashEntry *) -js_alloc_sftbl_entry(void *priv, const void *key) -{ - size_t nbytes = offsetof(ScriptFilenameEntry, filename) + strlen(key) + 1; - - return (JSHashEntry *) malloc(JS_MAX(nbytes, sizeof(JSHashEntry))); -} - -JS_STATIC_DLL_CALLBACK(void) -js_free_sftbl_entry(void *priv, JSHashEntry *he, uintN flag) -{ - if (flag != HT_FREE_ENTRY) - return; - free(he); -} - -static JSHashAllocOps sftbl_alloc_ops = { - js_alloc_table_space, js_free_table_space, - js_alloc_sftbl_entry, js_free_sftbl_entry -}; - -JSBool -js_InitRuntimeScriptState(JSRuntime *rt) -{ -#ifdef JS_THREADSAFE - JS_ASSERT(!rt->scriptFilenameTableLock); - rt->scriptFilenameTableLock = JS_NEW_LOCK(); - if (!rt->scriptFilenameTableLock) - return JS_FALSE; -#endif - JS_ASSERT(!rt->scriptFilenameTable); - rt->scriptFilenameTable = - JS_NewHashTable(16, JS_HashString, js_compare_strings, NULL, - &sftbl_alloc_ops, NULL); - if (!rt->scriptFilenameTable) { - js_FinishRuntimeScriptState(rt); /* free lock if threadsafe */ - return JS_FALSE; - } - JS_INIT_CLIST(&rt->scriptFilenamePrefixes); - return JS_TRUE; -} - -typedef struct ScriptFilenamePrefix { - JSCList links; /* circular list linkage for easy deletion */ - const char *name; /* pointer to pinned ScriptFilenameEntry string */ - size_t length; /* prefix string length, precomputed */ - uint32 flags; /* user-defined flags to inherit from this prefix */ -} ScriptFilenamePrefix; - -void -js_FinishRuntimeScriptState(JSRuntime *rt) -{ - if (rt->scriptFilenameTable) { - JS_HashTableDestroy(rt->scriptFilenameTable); - rt->scriptFilenameTable = NULL; - } -#ifdef JS_THREADSAFE - if (rt->scriptFilenameTableLock) { - JS_DESTROY_LOCK(rt->scriptFilenameTableLock); - rt->scriptFilenameTableLock = NULL; - } -#endif -} - -void -js_FreeRuntimeScriptState(JSRuntime *rt) -{ - ScriptFilenamePrefix *sfp; - - if (!rt->scriptFilenameTable) - return; - - while (!JS_CLIST_IS_EMPTY(&rt->scriptFilenamePrefixes)) { - sfp = (ScriptFilenamePrefix *) rt->scriptFilenamePrefixes.next; - JS_REMOVE_LINK(&sfp->links); - free(sfp); - } - js_FinishRuntimeScriptState(rt); -} - -#ifdef DEBUG_brendan -#define DEBUG_SFTBL -#endif -#ifdef DEBUG_SFTBL -size_t sftbl_savings = 0; -#endif - -static ScriptFilenameEntry * -SaveScriptFilename(JSRuntime *rt, const char *filename, uint32 flags) -{ - JSHashTable *table; - JSHashNumber hash; - JSHashEntry **hep; - ScriptFilenameEntry *sfe; - size_t length; - JSCList *head, *link; - ScriptFilenamePrefix *sfp; - - table = rt->scriptFilenameTable; - hash = JS_HashString(filename); - hep = JS_HashTableRawLookup(table, hash, filename); - sfe = (ScriptFilenameEntry *) *hep; -#ifdef DEBUG_SFTBL - if (sfe) - sftbl_savings += strlen(sfe->filename); -#endif - - if (!sfe) { - sfe = (ScriptFilenameEntry *) - JS_HashTableRawAdd(table, hep, hash, filename, NULL); - if (!sfe) - return NULL; - sfe->key = strcpy(sfe->filename, filename); - sfe->flags = 0; - sfe->mark = JS_FALSE; - } - - /* If saving a prefix, add it to the set in rt->scriptFilenamePrefixes. */ - if (flags != 0) { - /* Search in case filename was saved already; we must be idempotent. */ - sfp = NULL; - length = strlen(filename); - for (head = link = &rt->scriptFilenamePrefixes; - link->next != head; - link = link->next) { - /* Lag link behind sfp to insert in non-increasing length order. */ - sfp = (ScriptFilenamePrefix *) link->next; - if (!strcmp(sfp->name, filename)) - break; - if (sfp->length <= length) { - sfp = NULL; - break; - } - sfp = NULL; - } - - if (!sfp) { - /* No such prefix: add one now. */ - sfp = (ScriptFilenamePrefix *) malloc(sizeof(ScriptFilenamePrefix)); - if (!sfp) - return NULL; - JS_INSERT_AFTER(&sfp->links, link); - sfp->name = sfe->filename; - sfp->length = length; - sfp->flags = 0; - } - - /* - * Accumulate flags in both sfe and sfp: sfe for later access from the - * JS_GetScriptedCallerFilenameFlags debug-API, and sfp so that longer - * filename entries can inherit by prefix. - */ - sfe->flags |= flags; - sfp->flags |= flags; - } - - return sfe; -} - -const char * -js_SaveScriptFilename(JSContext *cx, const char *filename) -{ - JSRuntime *rt; - ScriptFilenameEntry *sfe; - JSCList *head, *link; - ScriptFilenamePrefix *sfp; - - rt = cx->runtime; - JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock); - sfe = SaveScriptFilename(rt, filename, 0); - if (!sfe) { - JS_RELEASE_LOCK(rt->scriptFilenameTableLock); - JS_ReportOutOfMemory(cx); - return NULL; - } - - /* - * Try to inherit flags by prefix. We assume there won't be more than a - * few (dozen! ;-) prefixes, so linear search is tolerable. - * XXXbe every time I've assumed that in the JS engine, I've been wrong! - */ - for (head = &rt->scriptFilenamePrefixes, link = head->next; - link != head; - link = link->next) { - sfp = (ScriptFilenamePrefix *) link; - if (!strncmp(sfp->name, filename, sfp->length)) { - sfe->flags |= sfp->flags; - break; - } - } - JS_RELEASE_LOCK(rt->scriptFilenameTableLock); - return sfe->filename; -} - -const char * -js_SaveScriptFilenameRT(JSRuntime *rt, const char *filename, uint32 flags) -{ - ScriptFilenameEntry *sfe; - - /* This may be called very early, via the jsdbgapi.h entry point. */ - if (!rt->scriptFilenameTable && !js_InitRuntimeScriptState(rt)) - return NULL; - - JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock); - sfe = SaveScriptFilename(rt, filename, flags); - JS_RELEASE_LOCK(rt->scriptFilenameTableLock); - if (!sfe) - return NULL; - - return sfe->filename; -} - -/* - * Back up from a saved filename by its offset within its hash table entry. - */ -#define FILENAME_TO_SFE(fn) \ - ((ScriptFilenameEntry *) ((fn) - offsetof(ScriptFilenameEntry, filename))) - -/* - * The sfe->key member, redundant given sfe->filename but required by the old - * jshash.c code, here gives us a useful sanity check. This assertion will - * very likely botch if someone tries to mark a string that wasn't allocated - * as an sfe->filename. - */ -#define ASSERT_VALID_SFE(sfe) JS_ASSERT((sfe)->key == (sfe)->filename) - -uint32 -js_GetScriptFilenameFlags(const char *filename) -{ - ScriptFilenameEntry *sfe; - - sfe = FILENAME_TO_SFE(filename); - ASSERT_VALID_SFE(sfe); - return sfe->flags; -} - -void -js_MarkScriptFilename(const char *filename) -{ - ScriptFilenameEntry *sfe; - - sfe = FILENAME_TO_SFE(filename); - ASSERT_VALID_SFE(sfe); - sfe->mark = JS_TRUE; -} - -JS_STATIC_DLL_CALLBACK(intN) -js_script_filename_marker(JSHashEntry *he, intN i, void *arg) -{ - ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he; - - sfe->mark = JS_TRUE; - return HT_ENUMERATE_NEXT; -} - -void -js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms) -{ - JSCList *head, *link; - ScriptFilenamePrefix *sfp; - - if (!rt->scriptFilenameTable) - return; - - if (keepAtoms) { - JS_HashTableEnumerateEntries(rt->scriptFilenameTable, - js_script_filename_marker, - rt); - } - for (head = &rt->scriptFilenamePrefixes, link = head->next; - link != head; - link = link->next) { - sfp = (ScriptFilenamePrefix *) link; - js_MarkScriptFilename(sfp->name); - } -} - -JS_STATIC_DLL_CALLBACK(intN) -js_script_filename_sweeper(JSHashEntry *he, intN i, void *arg) -{ - ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he; - - if (!sfe->mark) - return HT_ENUMERATE_REMOVE; - sfe->mark = JS_FALSE; - return HT_ENUMERATE_NEXT; -} - -void -js_SweepScriptFilenames(JSRuntime *rt) -{ - if (!rt->scriptFilenameTable) - return; - - JS_HashTableEnumerateEntries(rt->scriptFilenameTable, - js_script_filename_sweeper, - rt); -#ifdef DEBUG_notme -#ifdef DEBUG_SFTBL - printf("script filename table savings so far: %u\n", sftbl_savings); -#endif -#endif -} - -JSScript * -js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 ntrynotes) -{ - JSScript *script; - - /* Round up source note count to align script->trynotes for its type. */ - if (ntrynotes) - nsrcnotes += JSTRYNOTE_ALIGNMASK; - script = (JSScript *) JS_malloc(cx, - sizeof(JSScript) + - length * sizeof(jsbytecode) + - nsrcnotes * sizeof(jssrcnote) + - ntrynotes * sizeof(JSTryNote)); - if (!script) - return NULL; - memset(script, 0, sizeof(JSScript)); - script->code = script->main = (jsbytecode *)(script + 1); - script->length = length; - script->version = cx->version; - if (ntrynotes) { - script->trynotes = (JSTryNote *) - ((jsword)(SCRIPT_NOTES(script) + nsrcnotes) & - ~(jsword)JSTRYNOTE_ALIGNMASK); - memset(script->trynotes, 0, ntrynotes * sizeof(JSTryNote)); - } - return script; -} - -JS_FRIEND_API(JSScript *) -js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun) -{ - uint32 mainLength, prologLength, nsrcnotes, ntrynotes; - JSScript *script; - const char *filename; - - mainLength = CG_OFFSET(cg); - prologLength = CG_PROLOG_OFFSET(cg); - CG_COUNT_FINAL_SRCNOTES(cg, nsrcnotes); - CG_COUNT_FINAL_TRYNOTES(cg, ntrynotes); - script = js_NewScript(cx, prologLength + mainLength, nsrcnotes, ntrynotes); - if (!script) - return NULL; - - /* Now that we have script, error control flow must go to label bad. */ - script->main += prologLength; - memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode)); - memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode)); - script->numGlobalVars = cg->treeContext.numGlobalVars; - if (!js_InitAtomMap(cx, &script->atomMap, &cg->atomList)) - goto bad; - - filename = cg->filename; - if (filename) { - script->filename = js_SaveScriptFilename(cx, filename); - if (!script->filename) - goto bad; - } - script->lineno = cg->firstLine; - script->depth = cg->maxStackDepth; - if (cg->principals) { - script->principals = cg->principals; - JSPRINCIPALS_HOLD(cx, script->principals); - } - - if (!js_FinishTakingSrcNotes(cx, cg, SCRIPT_NOTES(script))) - goto bad; - if (script->trynotes) - js_FinishTakingTryNotes(cx, cg, script->trynotes); - - /* - * We initialize fun->u.script to be the script constructed above - * so that the debugger has a valid FUN_SCRIPT(fun). - */ - if (fun) { - JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun)); - fun->u.i.script = script; - if (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT) - fun->flags |= JSFUN_HEAVYWEIGHT; - } - - /* Tell the debugger about this compiled script. */ - js_CallNewScriptHook(cx, script, fun); - return script; - -bad: - js_DestroyScript(cx, script); - return NULL; -} - -JS_FRIEND_API(void) -js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun) -{ - JSRuntime *rt; - JSNewScriptHook hook; - - rt = cx->runtime; - hook = rt->newScriptHook; - if (hook) { - JS_KEEP_ATOMS(rt); - hook(cx, script->filename, script->lineno, script, fun, - rt->newScriptHookData); - JS_UNKEEP_ATOMS(rt); - } -} - -JS_FRIEND_API(void) -js_CallDestroyScriptHook(JSContext *cx, JSScript *script) -{ - JSRuntime *rt; - JSDestroyScriptHook hook; - - rt = cx->runtime; - hook = rt->destroyScriptHook; - if (hook) - hook(cx, script, rt->destroyScriptHookData); -} - -void -js_DestroyScript(JSContext *cx, JSScript *script) -{ - js_CallDestroyScriptHook(cx, script); - - JS_ClearScriptTraps(cx, script); - js_FreeAtomMap(cx, &script->atomMap); - if (script->principals) - JSPRINCIPALS_DROP(cx, script->principals); - if (JS_GSN_CACHE(cx).script == script) - JS_CLEAR_GSN_CACHE(cx); - JS_free(cx, script); -} - -void -js_MarkScript(JSContext *cx, JSScript *script) -{ - JSAtomMap *map; - uintN i, length; - JSAtom **vector; - - map = &script->atomMap; - length = map->length; - vector = map->vector; - for (i = 0; i < length; i++) - GC_MARK_ATOM(cx, vector[i]); - - if (script->filename) - js_MarkScriptFilename(script->filename); -} - -typedef struct GSNCacheEntry { - JSDHashEntryHdr hdr; - jsbytecode *pc; - jssrcnote *sn; -} GSNCacheEntry; - -#define GSN_CACHE_THRESHOLD 100 - -jssrcnote * -js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc) -{ - ptrdiff_t target, offset; - GSNCacheEntry *entry; - jssrcnote *sn, *result; - uintN nsrcnotes; - - - target = PTRDIFF(pc, script->code, jsbytecode); - if ((uint32)target >= script->length) - return NULL; - - if (JS_GSN_CACHE(cx).script == script) { - JS_METER_GSN_CACHE(cx, hits); - entry = (GSNCacheEntry *) - JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc, - JS_DHASH_LOOKUP); - return entry->sn; - } - - JS_METER_GSN_CACHE(cx, misses); - offset = 0; - for (sn = SCRIPT_NOTES(script); ; sn = SN_NEXT(sn)) { - if (SN_IS_TERMINATOR(sn)) { - result = NULL; - break; - } - offset += SN_DELTA(sn); - if (offset == target && SN_IS_GETTABLE(sn)) { - result = sn; - break; - } - } - - if (JS_GSN_CACHE(cx).script != script && - script->length >= GSN_CACHE_THRESHOLD) { - JS_CLEAR_GSN_CACHE(cx); - nsrcnotes = 0; - for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); - sn = SN_NEXT(sn)) { - if (SN_IS_GETTABLE(sn)) - ++nsrcnotes; - } - if (!JS_DHashTableInit(&JS_GSN_CACHE(cx).table, JS_DHashGetStubOps(), - NULL, sizeof(GSNCacheEntry), nsrcnotes)) { - JS_GSN_CACHE(cx).table.ops = NULL; - } else { - pc = script->code; - for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); - sn = SN_NEXT(sn)) { - pc += SN_DELTA(sn); - if (SN_IS_GETTABLE(sn)) { - entry = (GSNCacheEntry *) - JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc, - JS_DHASH_ADD); - entry->pc = pc; - entry->sn = sn; - } - } - JS_GSN_CACHE(cx).script = script; - JS_METER_GSN_CACHE(cx, fills); - } - } - - return result; -} - -uintN -js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc) -{ - JSAtom *atom; - JSFunction *fun; - uintN lineno; - ptrdiff_t offset, target; - jssrcnote *sn; - JSSrcNoteType type; - - /* Cope with JSStackFrame.pc value prior to entering js_Interpret. */ - if (!pc) - return 0; - - /* - * Special case: function definition needs no line number note because - * the function's script contains its starting line number. - */ - if (*pc == JSOP_DEFFUN || - (*pc == JSOP_LITOPX && pc[1 + LITERAL_INDEX_LEN] == JSOP_DEFFUN)) { - atom = js_GetAtom(cx, &script->atomMap, - (*pc == JSOP_DEFFUN) - ? GET_ATOM_INDEX(pc) - : GET_LITERAL_INDEX(pc)); - fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(atom)); - JS_ASSERT(FUN_INTERPRETED(fun)); - return fun->u.i.script->lineno; - } - - /* - * General case: walk through source notes accumulating their deltas, - * keeping track of line-number notes, until we pass the note for pc's - * offset within script->code. - */ - lineno = script->lineno; - offset = 0; - target = PTRDIFF(pc, script->code, jsbytecode); - for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { - offset += SN_DELTA(sn); - type = (JSSrcNoteType) SN_TYPE(sn); - if (type == SRC_SETLINE) { - if (offset <= target) - lineno = (uintN) js_GetSrcNoteOffset(sn, 0); - } else if (type == SRC_NEWLINE) { - if (offset <= target) - lineno++; - } - if (offset > target) - break; - } - return lineno; -} - -/* The line number limit is the same as the jssrcnote offset limit. */ -#define SN_LINE_LIMIT (SN_3BYTE_OFFSET_FLAG << 16) - -jsbytecode * -js_LineNumberToPC(JSScript *script, uintN target) -{ - ptrdiff_t offset, best; - uintN lineno, bestdiff, diff; - jssrcnote *sn; - JSSrcNoteType type; - - offset = 0; - best = -1; - lineno = script->lineno; - bestdiff = SN_LINE_LIMIT; - for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { - if (lineno == target) - goto out; - if (lineno > target) { - diff = lineno - target; - if (diff < bestdiff) { - bestdiff = diff; - best = offset; - } - } - offset += SN_DELTA(sn); - type = (JSSrcNoteType) SN_TYPE(sn); - if (type == SRC_SETLINE) { - lineno = (uintN) js_GetSrcNoteOffset(sn, 0); - } else if (type == SRC_NEWLINE) { - lineno++; - } - } - if (best >= 0) - offset = best; -out: - return script->code + offset; -} - -JS_FRIEND_API(uintN) -js_GetScriptLineExtent(JSScript *script) -{ - uintN lineno; - jssrcnote *sn; - JSSrcNoteType type; - - lineno = script->lineno; - for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { - type = (JSSrcNoteType) SN_TYPE(sn); - if (type == SRC_SETLINE) { - lineno = (uintN) js_GetSrcNoteOffset(sn, 0); - } else if (type == SRC_NEWLINE) { - lineno++; - } - } - return 1 + lineno - script->lineno; -} - -#if JS_HAS_GENERATORS - -jsbytecode * -js_FindFinallyHandler(JSScript *script, jsbytecode *pc) -{ - JSTryNote *tn; - ptrdiff_t off; - JSOp op2; - - tn = script->trynotes; - if (!tn) - return NULL; - - off = pc - script->main; - if (off < 0) - return NULL; - - JS_ASSERT(tn->catchStart != 0); - do { - if ((jsuword)(off - tn->start) < (jsuword)tn->length) { - /* - * We have a handler: is it the finally one, or a catch handler? - * - * Catch bytecode begins with: JSOP_SETSP JSOP_ENTERBLOCK - * Finally bytecode begins with: JSOP_SETSP JSOP_(GOSUB|EXCEPTION) - */ - pc = script->main + tn->catchStart; - JS_ASSERT(*pc == JSOP_SETSP); - op2 = pc[JSOP_SETSP_LENGTH]; - if (op2 != JSOP_ENTERBLOCK) { - JS_ASSERT(op2 == JSOP_GOSUB || op2 == JSOP_EXCEPTION); - return pc; - } - } - } while ((++tn)->catchStart != 0); - return NULL; -} - -#endif diff --git a/spidermonkey/src/jsscript.h b/spidermonkey/src/jsscript.h deleted file mode 100644 index 18ad373..0000000 --- a/spidermonkey/src/jsscript.h +++ /dev/null @@ -1,225 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsscript_h___ -#define jsscript_h___ -/* - * JS script descriptor. - */ -#include "jsatom.h" -#include "jsprvtd.h" - -JS_BEGIN_EXTERN_C - -/* - * Exception handling runtime information. - * - * All fields except length are code offsets relative to the main entry point - * of the script. If script->trynotes is not null, it points to a vector of - * these structs terminated by one with catchStart == 0. - */ -struct JSTryNote { - ptrdiff_t start; /* start of try statement */ - ptrdiff_t length; /* count of try statement bytecodes */ - ptrdiff_t catchStart; /* start of catch block (0 if end) */ -}; - -#define JSTRYNOTE_GRAIN sizeof(ptrdiff_t) -#define JSTRYNOTE_ALIGNMASK (JSTRYNOTE_GRAIN - 1) - -struct JSScript { - jsbytecode *code; /* bytecodes and their immediate operands */ - uint32 length; /* length of code vector */ - jsbytecode *main; /* main entry point, after predef'ing prolog */ - uint16 version; /* JS version under which script was compiled */ - uint16 numGlobalVars; /* declared global var/const/function count */ - JSAtomMap atomMap; /* maps immediate index to literal struct */ - const char *filename; /* source filename or null */ - uintN lineno; /* base line number of script */ - uintN depth; /* maximum stack depth in slots */ - JSTryNote *trynotes; /* exception table for this script */ - JSPrincipals *principals; /* principals for this script */ - JSObject *object; /* optional Script-class object wrapper */ -}; - -/* No need to store script->notes now that it is allocated right after code. */ -#define SCRIPT_NOTES(script) ((jssrcnote*)((script)->code+(script)->length)) - -#define SCRIPT_FIND_CATCH_START(script, pc, catchpc) \ - JS_BEGIN_MACRO \ - JSTryNote *tn_ = (script)->trynotes; \ - jsbytecode *catchpc_ = NULL; \ - if (tn_) { \ - ptrdiff_t off_ = PTRDIFF(pc, (script)->main, jsbytecode); \ - if (off_ >= 0) { \ - while ((jsuword)(off_ - tn_->start) >= (jsuword)tn_->length) \ - ++tn_; \ - if (tn_->catchStart) \ - catchpc_ = (script)->main + tn_->catchStart; \ - } \ - } \ - catchpc = catchpc_; \ - JS_END_MACRO - -/* - * Find the innermost finally block that handles the given pc. This is a - * version of SCRIPT_FIND_CATCH_START that ignore catch blocks and is used - * to implement generator.close(). - */ -jsbytecode * -js_FindFinallyHandler(JSScript *script, jsbytecode *pc); - -extern JS_FRIEND_DATA(JSClass) js_ScriptClass; - -extern JSObject * -js_InitScriptClass(JSContext *cx, JSObject *obj); - -/* - * On first new context in rt, initialize script runtime state, specifically - * the script filename table and its lock. - */ -extern JSBool -js_InitRuntimeScriptState(JSRuntime *rt); - -/* - * On last context destroy for rt, if script filenames are all GC'd, free the - * script filename table and its lock. - */ -extern void -js_FinishRuntimeScriptState(JSRuntime *rt); - -/* - * On JS_DestroyRuntime(rt), forcibly free script filename prefixes and any - * script filename table entries that have not been GC'd, the latter using - * js_FinishRuntimeScriptState. - * - * This allows script filename prefixes to outlive any context in rt. - */ -extern void -js_FreeRuntimeScriptState(JSRuntime *rt); - -extern const char * -js_SaveScriptFilename(JSContext *cx, const char *filename); - -extern const char * -js_SaveScriptFilenameRT(JSRuntime *rt, const char *filename, uint32 flags); - -extern uint32 -js_GetScriptFilenameFlags(const char *filename); - -extern void -js_MarkScriptFilename(const char *filename); - -extern void -js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms); - -extern void -js_SweepScriptFilenames(JSRuntime *rt); - -/* - * Two successively less primitive ways to make a new JSScript. The first - * does *not* call a non-null cx->runtime->newScriptHook -- only the second, - * js_NewScriptFromCG, calls this optional debugger hook. - * - * The js_NewScript function can't know whether the script it creates belongs - * to a function, or is top-level or eval code, but the debugger wants access - * to the newly made script's function, if any -- so callers of js_NewScript - * are responsible for notifying the debugger after successfully creating any - * kind (function or other) of new JSScript. - */ -extern JSScript * -js_NewScript(JSContext *cx, uint32 length, uint32 snlength, uint32 tnlength); - -extern JS_FRIEND_API(JSScript *) -js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun); - -/* - * New-script-hook calling is factored from js_NewScriptFromCG so that it - * and callers of js_XDRScript can share this code. In the case of callers - * of js_XDRScript, the hook should be invoked only after successful decode - * of any owning function (the fun parameter) or script object (null fun). - */ -extern JS_FRIEND_API(void) -js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun); - -extern JS_FRIEND_API(void) -js_CallDestroyScriptHook(JSContext *cx, JSScript *script); - -extern void -js_DestroyScript(JSContext *cx, JSScript *script); - -extern void -js_MarkScript(JSContext *cx, JSScript *script); - -/* - * To perturb as little code as possible, we introduce a js_GetSrcNote lookup - * cache without adding an explicit cx parameter. Thus js_GetSrcNote becomes - * a macro that uses cx from its calls' lexical environments. - */ -#define js_GetSrcNote(script,pc) js_GetSrcNoteCached(cx, script, pc) - -extern jssrcnote * -js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc); - -/* XXX need cx to lock function objects declared by prolog bytecodes. */ -extern uintN -js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc); - -extern jsbytecode * -js_LineNumberToPC(JSScript *script, uintN lineno); - -extern JS_FRIEND_API(uintN) -js_GetScriptLineExtent(JSScript *script); - -/* - * If magic is non-null, js_XDRScript succeeds on magic number mismatch but - * returns false in *magic; it reflects a match via a true *magic out param. - * If magic is null, js_XDRScript returns false on bad magic number errors, - * which it reports. - * - * NB: callers must call js_CallNewScriptHook after successful JSXDR_DECODE - * and subsequent set-up of owning function or script object, if any. - */ -extern JSBool -js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *magic); - -JS_END_EXTERN_C - -#endif /* jsscript_h___ */ diff --git a/spidermonkey/src/jsshell.msg b/spidermonkey/src/jsshell.msg deleted file mode 100644 index 4b811ac..0000000 --- a/spidermonkey/src/jsshell.msg +++ /dev/null @@ -1,50 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - Error messages for JSShell. See js.msg for format. -*/ - -MSG_DEF(JSSMSG_NOT_AN_ERROR, 0, 0, JSEXN_NONE, "") -MSG_DEF(JSSMSG_CANT_OPEN, 1, 2, JSEXN_NONE, "can't open {0}: {1}") -MSG_DEF(JSSMSG_TRAP_USAGE, 2, 0, JSEXN_NONE, "usage: trap [fun] [pc] expr") -MSG_DEF(JSSMSG_LINE2PC_USAGE, 3, 0, JSEXN_NONE, "usage: line2pc [fun] line") -MSG_DEF(JSSMSG_FILE_SCRIPTS_ONLY, 4, 0, JSEXN_NONE, "only works on JS scripts read from files") -MSG_DEF(JSSMSG_UNEXPECTED_EOF, 5, 1, JSEXN_NONE, "unexpected EOF in {0}") -MSG_DEF(JSSMSG_DOEXP_USAGE, 6, 0, JSEXN_NONE, "usage: doexp obj id") diff --git a/spidermonkey/src/jsstddef.h b/spidermonkey/src/jsstddef.h deleted file mode 100644 index addaa88..0000000 --- a/spidermonkey/src/jsstddef.h +++ /dev/null @@ -1,83 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * stddef inclusion here to first declare ptrdif as a signed long instead of a - * signed int. - */ - -#ifdef _WINDOWS -# ifndef XP_WIN -# define XP_WIN -# endif -#if defined(_WIN32) || defined(WIN32) -# ifndef XP_WIN32 -# define XP_WIN32 -# endif -#else -# ifndef XP_WIN16 -# define XP_WIN16 -# endif -#endif -#endif - -#ifdef XP_WIN16 -#ifndef _PTRDIFF_T_DEFINED -typedef long ptrdiff_t; - -/* - * The Win16 compiler treats pointer differences as 16-bit signed values. - * This macro allows us to treat them as 17-bit signed values, stored in - * a 32-bit type. - */ -#define PTRDIFF(p1, p2, type) \ - ((((unsigned long)(p1)) - ((unsigned long)(p2))) / sizeof(type)) - -#define _PTRDIFF_T_DEFINED -#endif /*_PTRDIFF_T_DEFINED*/ -#else /*WIN16*/ - -#define PTRDIFF(p1, p2, type) \ - ((p1) - (p2)) - -#endif - -#include - - diff --git a/spidermonkey/src/jsstr.c b/spidermonkey/src/jsstr.c deleted file mode 100644 index e38f652..0000000 --- a/spidermonkey/src/jsstr.c +++ /dev/null @@ -1,4818 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=80: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * JS string type implementation. - * - * In order to avoid unnecessary js_LockGCThing/js_UnlockGCThing calls, these - * native methods store strings (possibly newborn) converted from their 'this' - * parameter and arguments on the stack: 'this' conversions at argv[-1], arg - * conversions at their index (argv[0], argv[1]). This is a legitimate method - * of rooting things that might lose their newborn root due to subsequent GC - * allocations in the same native method. - */ -#include "jsstddef.h" -#include -#include -#include "jstypes.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jshash.h" /* Added by JSIFY */ -#include "jsprf.h" -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jsbool.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsregexp.h" -#include "jsstr.h" - -#define JSSTRDEP_RECURSION_LIMIT 100 - -size_t -js_MinimizeDependentStrings(JSString *str, int level, JSString **basep) -{ - JSString *base; - size_t start, length; - - JS_ASSERT(JSSTRING_IS_DEPENDENT(str)); - base = JSSTRDEP_BASE(str); - start = JSSTRDEP_START(str); - if (JSSTRING_IS_DEPENDENT(base)) { - if (level < JSSTRDEP_RECURSION_LIMIT) { - start += js_MinimizeDependentStrings(base, level + 1, &base); - } else { - do { - start += JSSTRDEP_START(base); - base = JSSTRDEP_BASE(base); - } while (JSSTRING_IS_DEPENDENT(base)); - } - if (start == 0) { - JS_ASSERT(JSSTRING_IS_PREFIX(str)); - JSPREFIX_SET_BASE(str, base); - } else if (start <= JSSTRDEP_START_MASK) { - length = JSSTRDEP_LENGTH(str); - JSSTRDEP_SET_START_AND_LENGTH(str, start, length); - JSSTRDEP_SET_BASE(str, base); - } - } - *basep = base; - return start; -} - -jschar * -js_GetDependentStringChars(JSString *str) -{ - size_t start; - JSString *base; - - start = js_MinimizeDependentStrings(str, 0, &base); - JS_ASSERT(!JSSTRING_IS_DEPENDENT(base)); - JS_ASSERT(start < base->length); - return base->chars + start; -} - -jschar * -js_GetStringChars(JSString *str) -{ - if (JSSTRING_IS_DEPENDENT(str) && !js_UndependString(NULL, str)) - return NULL; - - *js_GetGCThingFlags(str) &= ~GCF_MUTABLE; - return str->chars; -} - -JSString * -js_ConcatStrings(JSContext *cx, JSString *left, JSString *right) -{ - size_t rn, ln, lrdist, n; - jschar *rs, *ls, *s; - JSDependentString *ldep; /* non-null if left should become dependent */ - JSString *str; - - if (JSSTRING_IS_DEPENDENT(right)) { - rn = JSSTRDEP_LENGTH(right); - rs = JSSTRDEP_CHARS(right); - } else { - rn = right->length; - rs = right->chars; - } - if (rn == 0) - return left; - - if (JSSTRING_IS_DEPENDENT(left) || - !(*js_GetGCThingFlags(left) & GCF_MUTABLE)) { - /* We must copy if left does not own a buffer to realloc. */ - ln = JSSTRING_LENGTH(left); - if (ln == 0) - return right; - ls = JSSTRING_CHARS(left); - s = (jschar *) JS_malloc(cx, (ln + rn + 1) * sizeof(jschar)); - if (!s) - return NULL; - js_strncpy(s, ls, ln); - ldep = NULL; - } else { - /* We can realloc left's space and make it depend on our result. */ - ln = left->length; - if (ln == 0) - return right; - ls = left->chars; - s = (jschar *) JS_realloc(cx, ls, (ln + rn + 1) * sizeof(jschar)); - if (!s) - return NULL; - - /* Take care: right could depend on left! */ - lrdist = (size_t)(rs - ls); - if (lrdist < ln) - rs = s + lrdist; - left->chars = ls = s; - ldep = JSSTRDEP(left); - } - - js_strncpy(s + ln, rs, rn); - n = ln + rn; - s[n] = 0; - str = js_NewString(cx, s, n, GCF_MUTABLE); - if (!str) { - /* Out of memory: clean up any space we (re-)allocated. */ - if (!ldep) { - JS_free(cx, s); - } else { - s = JS_realloc(cx, ls, (ln + 1) * sizeof(jschar)); - if (s) - left->chars = s; - } - } else { - /* Morph left into a dependent prefix if we realloc'd its buffer. */ - if (ldep) { - JSPREFIX_SET_LENGTH(ldep, ln); - JSPREFIX_SET_BASE(ldep, str); -#ifdef DEBUG - { - JSRuntime *rt = cx->runtime; - JS_RUNTIME_METER(rt, liveDependentStrings); - JS_RUNTIME_METER(rt, totalDependentStrings); - JS_LOCK_RUNTIME_VOID(rt, - (rt->strdepLengthSum += (double)ln, - rt->strdepLengthSquaredSum += (double)ln * (double)ln)); - } -#endif - } - } - - return str; -} - -/* - * May be called with null cx by js_GetStringChars, above; and by the jslock.c - * MAKE_STRING_IMMUTABLE file-local macro. - */ -const jschar * -js_UndependString(JSContext *cx, JSString *str) -{ - size_t n, size; - jschar *s; - - if (JSSTRING_IS_DEPENDENT(str)) { - n = JSSTRDEP_LENGTH(str); - size = (n + 1) * sizeof(jschar); - s = (jschar *) (cx ? JS_malloc(cx, size) : malloc(size)); - if (!s) - return NULL; - - js_strncpy(s, JSSTRDEP_CHARS(str), n); - s[n] = 0; - str->length = n; - str->chars = s; - -#ifdef DEBUG - if (cx) { - JSRuntime *rt = cx->runtime; - JS_RUNTIME_UNMETER(rt, liveDependentStrings); - JS_RUNTIME_UNMETER(rt, totalDependentStrings); - JS_LOCK_RUNTIME_VOID(rt, - (rt->strdepLengthSum -= (double)n, - rt->strdepLengthSquaredSum -= (double)n * (double)n)); - } -#endif - } - - return str->chars; -} - -/* - * Forward declarations for URI encode/decode and helper routines - */ -static JSBool -str_decodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); - -static JSBool -str_decodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); - -static JSBool -str_encodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); - -static JSBool -str_encodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); - -static uint32 -Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length); - -/* - * Contributions from the String class to the set of methods defined for the - * global object. escape and unescape used to be defined in the Mocha library, - * but as ECMA decided to spec them, they've been moved to the core engine - * and made ECMA-compliant. (Incomplete escapes are interpreted as literal - * characters by unescape.) - */ - -/* - * Stuff to emulate the old libmocha escape, which took a second argument - * giving the type of escape to perform. Retained for compatibility, and - * copied here to avoid reliance on net.h, mkparse.c/NET_EscapeBytes. - */ - -#define URL_XALPHAS ((uint8) 1) -#define URL_XPALPHAS ((uint8) 2) -#define URL_PATH ((uint8) 4) - -static const uint8 urlCharType[256] = -/* Bit 0 xalpha -- the alphas - * Bit 1 xpalpha -- as xalpha but - * converts spaces to plus and plus to %20 - * Bit 2 ... path -- as xalphas but doesn't escape '/' - */ - /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x */ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x */ - 0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4, /* 2x !"#$%&'()*+,-./ */ - 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */ - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */ - 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */ - 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */ - 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, /* 7X pqrstuvwxyz{\}~ DEL */ - 0, }; - -/* This matches the ECMA escape set when mask is 7 (default.) */ - -#define IS_OK(C, mask) (urlCharType[((uint8) (C))] & (mask)) - -/* See ECMA-262 15.1.2.4. */ -JSBool -js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - size_t i, ni, length, newlength; - const jschar *chars; - jschar *newchars; - jschar ch; - jsint mask; - jsdouble d; - const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; - - mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH; - if (argc > 1) { - if (!js_ValueToNumber(cx, argv[1], &d)) - return JS_FALSE; - if (!JSDOUBLE_IS_FINITE(d) || - (mask = (jsint)d) != d || - mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH)) - { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) mask); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_STRING_MASK, numBuf); - return JS_FALSE; - } - } - - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str); - - chars = JSSTRING_CHARS(str); - length = newlength = JSSTRING_LENGTH(str); - - /* Take a first pass and see how big the result string will need to be. */ - for (i = 0; i < length; i++) { - if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) - continue; - if (ch < 256) { - if (mask == URL_XPALPHAS && ch == ' ') - continue; /* The character will be encoded as '+' */ - newlength += 2; /* The character will be encoded as %XX */ - } else { - newlength += 5; /* The character will be encoded as %uXXXX */ - } - - /* - * This overflow test works because newlength is incremented by at - * most 5 on each iteration. - */ - if (newlength < length) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - } - - if (newlength >= ~(size_t)0 / sizeof(jschar)) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - newchars = (jschar *) JS_malloc(cx, (newlength + 1) * sizeof(jschar)); - if (!newchars) - return JS_FALSE; - for (i = 0, ni = 0; i < length; i++) { - if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) { - newchars[ni++] = ch; - } else if (ch < 256) { - if (mask == URL_XPALPHAS && ch == ' ') { - newchars[ni++] = '+'; /* convert spaces to pluses */ - } else { - newchars[ni++] = '%'; - newchars[ni++] = digits[ch >> 4]; - newchars[ni++] = digits[ch & 0xF]; - } - } else { - newchars[ni++] = '%'; - newchars[ni++] = 'u'; - newchars[ni++] = digits[ch >> 12]; - newchars[ni++] = digits[(ch & 0xF00) >> 8]; - newchars[ni++] = digits[(ch & 0xF0) >> 4]; - newchars[ni++] = digits[ch & 0xF]; - } - } - JS_ASSERT(ni == newlength); - newchars[newlength] = 0; - - str = js_NewString(cx, newchars, newlength, 0); - if (!str) { - JS_free(cx, newchars); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} -#undef IS_OK - -/* See ECMA-262 15.1.2.5 */ -static JSBool -str_unescape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - size_t i, ni, length; - const jschar *chars; - jschar *newchars; - jschar ch; - - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str); - - chars = JSSTRING_CHARS(str); - length = JSSTRING_LENGTH(str); - - /* Don't bother allocating less space for the new string. */ - newchars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); - if (!newchars) - return JS_FALSE; - ni = i = 0; - while (i < length) { - ch = chars[i++]; - if (ch == '%') { - if (i + 1 < length && - JS7_ISHEX(chars[i]) && JS7_ISHEX(chars[i + 1])) - { - ch = JS7_UNHEX(chars[i]) * 16 + JS7_UNHEX(chars[i + 1]); - i += 2; - } else if (i + 4 < length && chars[i] == 'u' && - JS7_ISHEX(chars[i + 1]) && JS7_ISHEX(chars[i + 2]) && - JS7_ISHEX(chars[i + 3]) && JS7_ISHEX(chars[i + 4])) - { - ch = (((((JS7_UNHEX(chars[i + 1]) << 4) - + JS7_UNHEX(chars[i + 2])) << 4) - + JS7_UNHEX(chars[i + 3])) << 4) - + JS7_UNHEX(chars[i + 4]); - i += 5; - } - } - newchars[ni++] = ch; - } - newchars[ni] = 0; - - str = js_NewString(cx, newchars, ni, 0); - if (!str) { - JS_free(cx, newchars); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -#if JS_HAS_UNEVAL -static JSBool -str_uneval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - - str = js_ValueToSource(cx, argv[0]); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} -#endif - -const char js_escape_str[] = "escape"; -const char js_unescape_str[] = "unescape"; -#if JS_HAS_UNEVAL -const char js_uneval_str[] = "uneval"; -#endif -const char js_decodeURI_str[] = "decodeURI"; -const char js_encodeURI_str[] = "encodeURI"; -const char js_decodeURIComponent_str[] = "decodeURIComponent"; -const char js_encodeURIComponent_str[] = "encodeURIComponent"; - -static JSFunctionSpec string_functions[] = { - {js_escape_str, js_str_escape, 1,0,0}, - {js_unescape_str, str_unescape, 1,0,0}, -#if JS_HAS_UNEVAL - {js_uneval_str, str_uneval, 1,0,0}, -#endif - {js_decodeURI_str, str_decodeURI, 1,0,0}, - {js_encodeURI_str, str_encodeURI, 1,0,0}, - {js_decodeURIComponent_str, str_decodeURI_Component, 1,0,0}, - {js_encodeURIComponent_str, str_encodeURI_Component, 1,0,0}, - - {0,0,0,0,0} -}; - -jschar js_empty_ucstr[] = {0}; -JSSubString js_EmptySubString = {0, js_empty_ucstr}; - -enum string_tinyid { - STRING_LENGTH = -1 -}; - -static JSPropertySpec string_props[] = { - {js_length_str, STRING_LENGTH, - JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED, 0,0}, - {0,0,0,0,0} -}; - -static JSBool -str_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsval v; - JSString *str; - jsint slot; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - - slot = JSVAL_TO_INT(id); - if (slot == STRING_LENGTH) { - if (OBJ_GET_CLASS(cx, obj) == &js_StringClass) { - /* Follow ECMA-262 by fetching intrinsic length of our string. */ - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - JS_ASSERT(JSVAL_IS_STRING(v)); - str = JSVAL_TO_STRING(v); - } else { - /* Preserve compatibility: convert obj to a string primitive. */ - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - } - - *vp = INT_TO_JSVAL((jsint) JSSTRING_LENGTH(str)); - } - return JS_TRUE; -} - -#define STRING_ELEMENT_ATTRS (JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT) - -static JSBool -str_enumerate(JSContext *cx, JSObject *obj) -{ - jsval v; - JSString *str, *str1; - size_t i, length; - - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - JS_ASSERT(JSVAL_IS_STRING(v)); - str = JSVAL_TO_STRING(v); - - length = JSSTRING_LENGTH(str); - for (i = 0; i < length; i++) { - str1 = js_NewDependentString(cx, str, i, 1, 0); - if (!str1) - return JS_FALSE; - if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(i), - STRING_TO_JSVAL(str1), NULL, NULL, - STRING_ELEMENT_ATTRS, NULL)) { - return JS_FALSE; - } - } - return JS_TRUE; -} - -static JSBool -str_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, - JSObject **objp) -{ - jsval v; - JSString *str, *str1; - jsint slot; - - if (!JSVAL_IS_INT(id) || (flags & JSRESOLVE_ASSIGNING)) - return JS_TRUE; - - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - JS_ASSERT(JSVAL_IS_STRING(v)); - str = JSVAL_TO_STRING(v); - - slot = JSVAL_TO_INT(id); - if ((size_t)slot < JSSTRING_LENGTH(str)) { - str1 = js_NewDependentString(cx, str, (size_t)slot, 1, 0); - if (!str1) - return JS_FALSE; - if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(slot), - STRING_TO_JSVAL(str1), NULL, NULL, - STRING_ELEMENT_ATTRS, NULL)) { - return JS_FALSE; - } - *objp = obj; - } - return JS_TRUE; -} - -JSClass js_StringClass = { - js_String_str, - JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | - JSCLASS_HAS_CACHED_PROTO(JSProto_String), - JS_PropertyStub, JS_PropertyStub, str_getProperty, JS_PropertyStub, - str_enumerate, (JSResolveOp)str_resolve, JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -#if JS_HAS_TOSOURCE - -/* - * String.prototype.quote is generic (as are most string methods), unlike - * toSource, toString, and valueOf. - */ -static JSBool -str_quote(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - - str = js_QuoteString(cx, str, '"'); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -str_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval v; - JSString *str; - size_t i, j, k, n; - char buf[16]; - jschar *s, *t; - - if (JSVAL_IS_STRING((jsval)obj)) { - v = (jsval)obj; - } else { - if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) - return JS_FALSE; - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - if (!JSVAL_IS_STRING(v)) - return js_obj_toSource(cx, obj, argc, argv, rval); - } - str = js_QuoteString(cx, JSVAL_TO_STRING(v), '"'); - if (!str) - return JS_FALSE; - j = JS_snprintf(buf, sizeof buf, "(new %s(", js_StringClass.name); - s = JSSTRING_CHARS(str); - k = JSSTRING_LENGTH(str); - n = j + k + 2; - t = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); - if (!t) - return JS_FALSE; - for (i = 0; i < j; i++) - t[i] = buf[i]; - for (j = 0; j < k; i++, j++) - t[i] = s[j]; - t[i++] = ')'; - t[i++] = ')'; - t[i] = 0; - str = js_NewString(cx, t, n, 0); - if (!str) { - JS_free(cx, t); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -#endif /* JS_HAS_TOSOURCE */ - -static JSBool -str_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval v; - - if (JSVAL_IS_STRING((jsval)obj)) { - *rval = (jsval)obj; - return JS_TRUE; - } - if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) - return JS_FALSE; - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - if (!JSVAL_IS_STRING(v)) - return js_obj_toString(cx, obj, argc, argv, rval); - *rval = v; - return JS_TRUE; -} - -static JSBool -str_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - if (JSVAL_IS_STRING((jsval)obj)) { - *rval = (jsval)obj; - return JS_TRUE; - } - if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) - return JS_FALSE; - *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - return JS_TRUE; -} - -/* - * Java-like string native methods. - */ -static JSBool -str_substring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - jsdouble d; - jsdouble length, begin, end; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - - if (argc != 0) { - if (!js_ValueToNumber(cx, argv[0], &d)) - return JS_FALSE; - length = JSSTRING_LENGTH(str); - begin = js_DoubleToInteger(d); - if (begin < 0) - begin = 0; - else if (begin > length) - begin = length; - - if (argc == 1) { - end = length; - } else { - if (!js_ValueToNumber(cx, argv[1], &d)) - return JS_FALSE; - end = js_DoubleToInteger(d); - if (end < 0) - end = 0; - else if (end > length) - end = length; - if (end < begin) { - /* ECMA emulates old JDK1.0 java.lang.String.substring. */ - jsdouble tmp = begin; - begin = end; - end = tmp; - } - } - - str = js_NewDependentString(cx, str, (size_t)begin, - (size_t)(end - begin), 0); - if (!str) - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -str_toLowerCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - size_t i, n; - jschar *s, *news; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - - n = JSSTRING_LENGTH(str); - news = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); - if (!news) - return JS_FALSE; - s = JSSTRING_CHARS(str); - for (i = 0; i < n; i++) - news[i] = JS_TOLOWER(s[i]); - news[n] = 0; - str = js_NewString(cx, news, n, 0); - if (!str) { - JS_free(cx, news); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -str_toLocaleLowerCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - - /* - * Forcefully ignore the first (or any) argument and return toLowerCase(), - * ECMA has reserved that argument, presumably for defining the locale. - */ - if (cx->localeCallbacks && cx->localeCallbacks->localeToLowerCase) { - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - return cx->localeCallbacks->localeToLowerCase(cx, str, rval); - } - return str_toLowerCase(cx, obj, 0, argv, rval); -} - -static JSBool -str_toUpperCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - size_t i, n; - jschar *s, *news; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - - n = JSSTRING_LENGTH(str); - news = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); - if (!news) - return JS_FALSE; - s = JSSTRING_CHARS(str); - for (i = 0; i < n; i++) - news[i] = JS_TOUPPER(s[i]); - news[n] = 0; - str = js_NewString(cx, news, n, 0); - if (!str) { - JS_free(cx, news); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -str_toLocaleUpperCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - - /* - * Forcefully ignore the first (or any) argument and return toUpperCase(), - * ECMA has reserved that argument, presumbaly for defining the locale. - */ - if (cx->localeCallbacks && cx->localeCallbacks->localeToUpperCase) { - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - return cx->localeCallbacks->localeToUpperCase(cx, str, rval); - } - return str_toUpperCase(cx, obj, 0, argv, rval); -} - -static JSBool -str_localeCompare(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str, *thatStr; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - - if (argc == 0) { - *rval = JSVAL_ZERO; - } else { - thatStr = js_ValueToString(cx, argv[0]); - if (!thatStr) - return JS_FALSE; - if (cx->localeCallbacks && cx->localeCallbacks->localeCompare) { - argv[0] = STRING_TO_JSVAL(thatStr); - return cx->localeCallbacks->localeCompare(cx, str, thatStr, rval); - } - *rval = INT_TO_JSVAL(js_CompareStrings(str, thatStr)); - } - return JS_TRUE; -} - -static JSBool -str_charAt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - jsdouble d; - size_t index; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - - if (argc == 0) { - d = 0.0; - } else { - if (!js_ValueToNumber(cx, argv[0], &d)) - return JS_FALSE; - d = js_DoubleToInteger(d); - } - - if (d < 0 || JSSTRING_LENGTH(str) <= d) { - *rval = JS_GetEmptyStringValue(cx); - } else { - index = (size_t)d; - str = js_NewDependentString(cx, str, index, 1, 0); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - } - return JS_TRUE; -} - -static JSBool -str_charCodeAt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - jsdouble d; - size_t index; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - - if (argc == 0) { - d = 0.0; - } else { - if (!js_ValueToNumber(cx, argv[0], &d)) - return JS_FALSE; - d = js_DoubleToInteger(d); - } - - if (d < 0 || JSSTRING_LENGTH(str) <= d) { - *rval = JS_GetNaNValue(cx); - } else { - index = (size_t)d; - *rval = INT_TO_JSVAL((jsint) JSSTRING_CHARS(str)[index]); - } - return JS_TRUE; -} - -jsint -js_BoyerMooreHorspool(const jschar *text, jsint textlen, - const jschar *pat, jsint patlen, - jsint start) -{ - jsint i, j, k, m; - uint8 skip[BMH_CHARSET_SIZE]; - jschar c; - - JS_ASSERT(0 < patlen && patlen <= BMH_PATLEN_MAX); - for (i = 0; i < BMH_CHARSET_SIZE; i++) - skip[i] = (uint8)patlen; - m = patlen - 1; - for (i = 0; i < m; i++) { - c = pat[i]; - if (c >= BMH_CHARSET_SIZE) - return BMH_BAD_PATTERN; - skip[c] = (uint8)(m - i); - } - for (k = start + m; - k < textlen; - k += ((c = text[k]) >= BMH_CHARSET_SIZE) ? patlen : skip[c]) { - for (i = k, j = m; ; i--, j--) { - if (j < 0) - return i + 1; - if (text[i] != pat[j]) - break; - } - } - return -1; -} - -static JSBool -str_indexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str, *str2; - jsint i, j, index, textlen, patlen; - const jschar *text, *pat; - jsdouble d; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - text = JSSTRING_CHARS(str); - textlen = (jsint) JSSTRING_LENGTH(str); - - str2 = js_ValueToString(cx, argv[0]); - if (!str2) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str2); - pat = JSSTRING_CHARS(str2); - patlen = (jsint) JSSTRING_LENGTH(str2); - - if (argc > 1) { - if (!js_ValueToNumber(cx, argv[1], &d)) - return JS_FALSE; - d = js_DoubleToInteger(d); - if (d < 0) - i = 0; - else if (d > textlen) - i = textlen; - else - i = (jsint)d; - } else { - i = 0; - } - if (patlen == 0) { - *rval = INT_TO_JSVAL(i); - return JS_TRUE; - } - - /* XXX tune the BMH threshold (512) */ - if ((jsuint)(patlen - 2) <= BMH_PATLEN_MAX - 2 && textlen >= 512) { - index = js_BoyerMooreHorspool(text, textlen, pat, patlen, i); - if (index != BMH_BAD_PATTERN) - goto out; - } - - index = -1; - j = 0; - while (i + j < textlen) { - if (text[i + j] == pat[j]) { - if (++j == patlen) { - index = i; - break; - } - } else { - i++; - j = 0; - } - } - -out: - *rval = INT_TO_JSVAL(index); - return JS_TRUE; -} - -static JSBool -str_lastIndexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str, *str2; - const jschar *text, *pat; - jsint i, j, textlen, patlen; - jsdouble d; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - text = JSSTRING_CHARS(str); - textlen = (jsint) JSSTRING_LENGTH(str); - - str2 = js_ValueToString(cx, argv[0]); - if (!str2) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str2); - pat = JSSTRING_CHARS(str2); - patlen = (jsint) JSSTRING_LENGTH(str2); - - if (argc > 1) { - if (!js_ValueToNumber(cx, argv[1], &d)) - return JS_FALSE; - if (JSDOUBLE_IS_NaN(d)) { - i = textlen; - } else { - d = js_DoubleToInteger(d); - if (d < 0) - i = 0; - else if (d > textlen) - i = textlen; - else - i = (jsint)d; - } - } else { - i = textlen; - } - - if (patlen == 0) { - *rval = INT_TO_JSVAL(i); - return JS_TRUE; - } - - j = 0; - while (i >= 0) { - /* Don't assume that text is NUL-terminated: it could be dependent. */ - if (i + j < textlen && text[i + j] == pat[j]) { - if (++j == patlen) - break; - } else { - i--; - j = 0; - } - } - *rval = INT_TO_JSVAL(i); - return JS_TRUE; -} - -/* - * Perl-inspired string functions. - */ -typedef struct GlobData { - uintN flags; /* inout: mode and flag bits, see below */ - uintN optarg; /* in: index of optional flags argument */ - JSString *str; /* out: 'this' parameter object as string */ - JSRegExp *regexp; /* out: regexp parameter object private data */ -} GlobData; - -/* - * Mode and flag bit definitions for match_or_replace's GlobData.flags field. - */ -#define MODE_MATCH 0x00 /* in: return match array on success */ -#define MODE_REPLACE 0x01 /* in: match and replace */ -#define MODE_SEARCH 0x02 /* in: search only, return match index or -1 */ -#define GET_MODE(f) ((f) & 0x03) -#define FORCE_FLAT 0x04 /* in: force flat (non-regexp) string match */ -#define KEEP_REGEXP 0x08 /* inout: keep GlobData.regexp alive for caller - of match_or_replace; if set on input - but clear on output, regexp ownership - does not pass to caller */ -#define GLOBAL_REGEXP 0x10 /* out: regexp had the 'g' flag */ - -static JSBool -match_or_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - JSBool (*glob)(JSContext *cx, jsint count, GlobData *data), - GlobData *data, jsval *rval) -{ - JSString *str, *src, *opt; - JSObject *reobj; - JSRegExp *re; - size_t index, length; - JSBool ok, test; - jsint count; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - data->str = str; - - if (JSVAL_IS_REGEXP(cx, argv[0])) { - reobj = JSVAL_TO_OBJECT(argv[0]); - re = (JSRegExp *) JS_GetPrivate(cx, reobj); - } else { - src = js_ValueToString(cx, argv[0]); - if (!src) - return JS_FALSE; - if (data->optarg < argc) { - argv[0] = STRING_TO_JSVAL(src); - opt = js_ValueToString(cx, argv[data->optarg]); - if (!opt) - return JS_FALSE; - } else { - opt = NULL; - } - re = js_NewRegExpOpt(cx, NULL, src, opt, - (data->flags & FORCE_FLAT) != 0); - if (!re) - return JS_FALSE; - reobj = NULL; - } - /* From here on, all control flow must reach the matching DROP. */ - data->regexp = re; - HOLD_REGEXP(cx, re); - - if (re->flags & JSREG_GLOB) - data->flags |= GLOBAL_REGEXP; - index = 0; - if (GET_MODE(data->flags) == MODE_SEARCH) { - ok = js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, rval); - if (ok) { - *rval = (*rval == JSVAL_TRUE) - ? INT_TO_JSVAL(cx->regExpStatics.leftContext.length) - : INT_TO_JSVAL(-1); - } - } else if (data->flags & GLOBAL_REGEXP) { - if (reobj) { - /* Set the lastIndex property's reserved slot to 0. */ - ok = js_SetLastIndex(cx, reobj, 0); - } else { - ok = JS_TRUE; - } - if (ok) { - length = JSSTRING_LENGTH(str); - for (count = 0; index <= length; count++) { - ok = js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, rval); - if (!ok || *rval != JSVAL_TRUE) - break; - ok = glob(cx, count, data); - if (!ok) - break; - if (cx->regExpStatics.lastMatch.length == 0) { - if (index == length) - break; - index++; - } - } - } - } else { - if (GET_MODE(data->flags) == MODE_REPLACE) { - test = JS_TRUE; - } else { - /* - * MODE_MATCH implies str_match is being called from a script or a - * scripted function. If the caller cares only about testing null - * vs. non-null return value, optimize away the array object that - * would normally be returned in *rval. - */ - JSStackFrame *fp = cx->fp->down; - - /* Skip Function.prototype.call and .apply frames. */ - while (fp && !fp->pc) { - JS_ASSERT(!fp->script); - fp = fp->down; - } - - /* Assume a full array result is required, then prove otherwise. */ - test = JS_FALSE; - if (fp) { - JS_ASSERT(*fp->pc == JSOP_CALL || *fp->pc == JSOP_NEW); - JS_ASSERT(js_CodeSpec[*fp->pc].length == 3); - switch (fp->pc[3]) { - case JSOP_POP: - case JSOP_IFEQ: - case JSOP_IFNE: - case JSOP_IFEQX: - case JSOP_IFNEX: - test = JS_TRUE; - break; - default:; - } - } - } - ok = js_ExecuteRegExp(cx, re, str, &index, test, rval); - } - - DROP_REGEXP(cx, re); - if (reobj) { - /* Tell our caller that it doesn't need to destroy data->regexp. */ - data->flags &= ~KEEP_REGEXP; - } else if (!(data->flags & KEEP_REGEXP)) { - /* Caller didn't want to keep data->regexp, so null and destroy it. */ - data->regexp = NULL; - js_DestroyRegExp(cx, re); - } - - return ok; -} - -typedef struct MatchData { - GlobData base; - jsval *arrayval; /* NB: local root pointer */ -} MatchData; - -static JSBool -match_glob(JSContext *cx, jsint count, GlobData *data) -{ - MatchData *mdata; - JSObject *arrayobj; - JSSubString *matchsub; - JSString *matchstr; - jsval v; - - mdata = (MatchData *)data; - arrayobj = JSVAL_TO_OBJECT(*mdata->arrayval); - if (!arrayobj) { - arrayobj = js_ConstructObject(cx, &js_ArrayClass, NULL, NULL, 0, NULL); - if (!arrayobj) - return JS_FALSE; - *mdata->arrayval = OBJECT_TO_JSVAL(arrayobj); - } - matchsub = &cx->regExpStatics.lastMatch; - matchstr = js_NewStringCopyN(cx, matchsub->chars, matchsub->length, 0); - if (!matchstr) - return JS_FALSE; - v = STRING_TO_JSVAL(matchstr); - return js_SetProperty(cx, arrayobj, INT_TO_JSID(count), &v); -} - -static JSBool -str_match(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - MatchData mdata; - JSBool ok; - - mdata.base.flags = MODE_MATCH; - mdata.base.optarg = 1; - mdata.arrayval = &argv[2]; - *mdata.arrayval = JSVAL_NULL; - ok = match_or_replace(cx, obj, argc, argv, match_glob, &mdata.base, rval); - if (ok && !JSVAL_IS_NULL(*mdata.arrayval)) - *rval = *mdata.arrayval; - return ok; -} - -static JSBool -str_search(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - GlobData data; - - data.flags = MODE_SEARCH; - data.optarg = 1; - return match_or_replace(cx, obj, argc, argv, NULL, &data, rval); -} - -typedef struct ReplaceData { - GlobData base; /* base struct state */ - JSObject *lambda; /* replacement function object or null */ - JSString *repstr; /* replacement string */ - jschar *dollar; /* null or pointer to first $ in repstr */ - jschar *dollarEnd; /* limit pointer for js_strchr_limit */ - jschar *chars; /* result chars, null initially */ - size_t length; /* result length, 0 initially */ - jsint index; /* index in result of next replacement */ - jsint leftIndex; /* left context index in base.str->chars */ - JSSubString dollarStr; /* for "$$" interpret_dollar result */ -} ReplaceData; - -static JSSubString * -interpret_dollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData *rdata, - size_t *skip) -{ - JSRegExpStatics *res; - jschar dc, *cp; - uintN num, tmp; - - JS_ASSERT(*dp == '$'); - - /* If there is only a dollar, bail now */ - if (dp + 1 >= ep) - return NULL; - - /* Interpret all Perl match-induced dollar variables. */ - res = &cx->regExpStatics; - dc = dp[1]; - if (JS7_ISDEC(dc)) { - /* ECMA-262 Edition 3: 1-9 or 01-99 */ - num = JS7_UNDEC(dc); - if (num > res->parenCount) - return NULL; - - cp = dp + 2; - if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) { - tmp = 10 * num + JS7_UNDEC(dc); - if (tmp <= res->parenCount) { - cp++; - num = tmp; - } - } - if (num == 0) - return NULL; - - /* Adjust num from 1 $n-origin to 0 array-index-origin. */ - num--; - *skip = cp - dp; - return REGEXP_PAREN_SUBSTRING(res, num); - } - - *skip = 2; - switch (dc) { - case '$': - rdata->dollarStr.chars = dp; - rdata->dollarStr.length = 1; - return &rdata->dollarStr; - case '&': - return &res->lastMatch; - case '+': - return &res->lastParen; - case '`': - return &res->leftContext; - case '\'': - return &res->rightContext; - } - return NULL; -} - -static JSBool -find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep) -{ - JSString *repstr; - size_t replen, skip; - jschar *dp, *ep; - JSSubString *sub; - JSObject *lambda; - - lambda = rdata->lambda; - if (lambda) { - uintN argc, i, j, m, n, p; - jsval *sp, *oldsp, rval; - void *mark; - JSStackFrame *fp; - JSBool ok; - - /* - * Save the regExpStatics from the current regexp, since they may be - * clobbered by a RegExp usage in the lambda function. Note that all - * members of JSRegExpStatics are JSSubStrings, so not GC roots, save - * input, which is rooted otherwise via argv[-1] in str_replace. - */ - JSRegExpStatics save = cx->regExpStatics; - JSBool freeMoreParens = JS_FALSE; - - /* - * In the lambda case, not only do we find the replacement string's - * length, we compute repstr and return it via rdata for use within - * do_replace. The lambda is called with arguments ($&, $1, $2, ..., - * index, input), i.e., all the properties of a regexp match array. - * For $&, etc., we must create string jsvals from cx->regExpStatics. - * We grab up stack space to keep the newborn strings GC-rooted. - */ - p = rdata->base.regexp->parenCount; - argc = 1 + p + 2; - sp = js_AllocStack(cx, 2 + argc, &mark); - if (!sp) - return JS_FALSE; - - /* Push lambda and its 'this' parameter. */ - *sp++ = OBJECT_TO_JSVAL(lambda); - *sp++ = OBJECT_TO_JSVAL(OBJ_GET_PARENT(cx, lambda)); - -#define PUSH_REGEXP_STATIC(sub) \ - JS_BEGIN_MACRO \ - JSString *str = js_NewStringCopyN(cx, \ - cx->regExpStatics.sub.chars, \ - cx->regExpStatics.sub.length, \ - 0); \ - if (!str) { \ - ok = JS_FALSE; \ - goto lambda_out; \ - } \ - *sp++ = STRING_TO_JSVAL(str); \ - JS_END_MACRO - - /* Push $&, $1, $2, ... */ - PUSH_REGEXP_STATIC(lastMatch); - i = 0; - m = cx->regExpStatics.parenCount; - n = JS_MIN(m, 9); - for (j = 0; i < n; i++, j++) - PUSH_REGEXP_STATIC(parens[j]); - for (j = 0; i < m; i++, j++) - PUSH_REGEXP_STATIC(moreParens[j]); - - /* - * We need to clear moreParens in the top-of-stack cx->regExpStatics - * to it won't be possibly realloc'ed, leaving the bottom-of-stack - * moreParens pointing to freed memory. - */ - cx->regExpStatics.moreParens = NULL; - freeMoreParens = JS_TRUE; - -#undef PUSH_REGEXP_STATIC - - /* Make sure to push undefined for any unmatched parens. */ - for (; i < p; i++) - *sp++ = JSVAL_VOID; - - /* Push match index and input string. */ - *sp++ = INT_TO_JSVAL((jsint)cx->regExpStatics.leftContext.length); - *sp++ = STRING_TO_JSVAL(rdata->base.str); - - /* Lift current frame to include the args and do the call. */ - fp = cx->fp; - oldsp = fp->sp; - fp->sp = sp; - ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL); - rval = fp->sp[-1]; - fp->sp = oldsp; - - if (ok) { - /* - * NB: we count on the newborn string root to hold any string - * created by this js_ValueToString that would otherwise be GC- - * able, until we use rdata->repstr in do_replace. - */ - repstr = js_ValueToString(cx, rval); - if (!repstr) { - ok = JS_FALSE; - } else { - rdata->repstr = repstr; - *sizep = JSSTRING_LENGTH(repstr); - } - } - - lambda_out: - js_FreeStack(cx, mark); - if (freeMoreParens) - JS_free(cx, cx->regExpStatics.moreParens); - cx->regExpStatics = save; - return ok; - } - - repstr = rdata->repstr; - replen = JSSTRING_LENGTH(repstr); - for (dp = rdata->dollar, ep = rdata->dollarEnd; dp; - dp = js_strchr_limit(dp, '$', ep)) { - sub = interpret_dollar(cx, dp, ep, rdata, &skip); - if (sub) { - replen += sub->length - skip; - dp += skip; - } - else - dp++; - } - *sizep = replen; - return JS_TRUE; -} - -static void -do_replace(JSContext *cx, ReplaceData *rdata, jschar *chars) -{ - JSString *repstr; - jschar *bp, *cp, *dp, *ep; - size_t len, skip; - JSSubString *sub; - - repstr = rdata->repstr; - bp = cp = JSSTRING_CHARS(repstr); - for (dp = rdata->dollar, ep = rdata->dollarEnd; dp; - dp = js_strchr_limit(dp, '$', ep)) { - len = dp - cp; - js_strncpy(chars, cp, len); - chars += len; - cp = dp; - sub = interpret_dollar(cx, dp, ep, rdata, &skip); - if (sub) { - len = sub->length; - js_strncpy(chars, sub->chars, len); - chars += len; - cp += skip; - dp += skip; - } else { - dp++; - } - } - js_strncpy(chars, cp, JSSTRING_LENGTH(repstr) - (cp - bp)); -} - -static JSBool -replace_glob(JSContext *cx, jsint count, GlobData *data) -{ - ReplaceData *rdata; - JSString *str; - size_t leftoff, leftlen, replen, growth; - const jschar *left; - jschar *chars; - - rdata = (ReplaceData *)data; - str = data->str; - leftoff = rdata->leftIndex; - left = JSSTRING_CHARS(str) + leftoff; - leftlen = cx->regExpStatics.lastMatch.chars - left; - rdata->leftIndex = cx->regExpStatics.lastMatch.chars - JSSTRING_CHARS(str); - rdata->leftIndex += cx->regExpStatics.lastMatch.length; - if (!find_replen(cx, rdata, &replen)) - return JS_FALSE; - growth = leftlen + replen; - chars = (jschar *) - (rdata->chars - ? JS_realloc(cx, rdata->chars, (rdata->length + growth + 1) - * sizeof(jschar)) - : JS_malloc(cx, (growth + 1) * sizeof(jschar))); - if (!chars) { - JS_free(cx, rdata->chars); - rdata->chars = NULL; - return JS_FALSE; - } - rdata->chars = chars; - rdata->length += growth; - chars += rdata->index; - rdata->index += growth; - js_strncpy(chars, left, leftlen); - chars += leftlen; - do_replace(cx, rdata, chars); - return JS_TRUE; -} - -static JSBool -str_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSObject *lambda; - JSString *repstr, *str; - ReplaceData rdata; - JSBool ok; - jschar *chars; - size_t leftlen, rightlen, length; - - if (JS_TypeOfValue(cx, argv[1]) == JSTYPE_FUNCTION) { - lambda = JSVAL_TO_OBJECT(argv[1]); - repstr = NULL; - } else { - if (!JS_ConvertValue(cx, argv[1], JSTYPE_STRING, &argv[1])) - return JS_FALSE; - repstr = JSVAL_TO_STRING(argv[1]); - lambda = NULL; - } - - /* - * For ECMA Edition 3, the first argument is to be converted to a string - * to match in a "flat" sense (without regular expression metachars having - * special meanings) UNLESS the first arg is a RegExp object. - */ - rdata.base.flags = MODE_REPLACE | KEEP_REGEXP | FORCE_FLAT; - rdata.base.optarg = 2; - - rdata.lambda = lambda; - rdata.repstr = repstr; - if (repstr) { - rdata.dollarEnd = JSSTRING_CHARS(repstr) + JSSTRING_LENGTH(repstr); - rdata.dollar = js_strchr_limit(JSSTRING_CHARS(repstr), '$', - rdata.dollarEnd); - } else { - rdata.dollar = rdata.dollarEnd = NULL; - } - rdata.chars = NULL; - rdata.length = 0; - rdata.index = 0; - rdata.leftIndex = 0; - - ok = match_or_replace(cx, obj, argc, argv, replace_glob, &rdata.base, rval); - if (!ok) - return JS_FALSE; - - if (!rdata.chars) { - if ((rdata.base.flags & GLOBAL_REGEXP) || *rval != JSVAL_TRUE) { - /* Didn't match even once. */ - *rval = STRING_TO_JSVAL(rdata.base.str); - goto out; - } - leftlen = cx->regExpStatics.leftContext.length; - ok = find_replen(cx, &rdata, &length); - if (!ok) - goto out; - length += leftlen; - chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); - if (!chars) { - ok = JS_FALSE; - goto out; - } - js_strncpy(chars, cx->regExpStatics.leftContext.chars, leftlen); - do_replace(cx, &rdata, chars + leftlen); - rdata.chars = chars; - rdata.length = length; - } - - rightlen = cx->regExpStatics.rightContext.length; - length = rdata.length + rightlen; - chars = (jschar *) - JS_realloc(cx, rdata.chars, (length + 1) * sizeof(jschar)); - if (!chars) { - JS_free(cx, rdata.chars); - ok = JS_FALSE; - goto out; - } - js_strncpy(chars + rdata.length, cx->regExpStatics.rightContext.chars, - rightlen); - chars[length] = 0; - - str = js_NewString(cx, chars, length, 0); - if (!str) { - JS_free(cx, chars); - ok = JS_FALSE; - goto out; - } - *rval = STRING_TO_JSVAL(str); - -out: - /* If KEEP_REGEXP is still set, it's our job to destroy regexp now. */ - if (rdata.base.flags & KEEP_REGEXP) - js_DestroyRegExp(cx, rdata.base.regexp); - return ok; -} - -/* - * Subroutine used by str_split to find the next split point in str, starting - * at offset *ip and looking either for the separator substring given by sep, - * or for the next re match. In the re case, return the matched separator in - * *sep, and the possibly updated offset in *ip. - * - * Return -2 on error, -1 on end of string, >= 0 for a valid index of the next - * separator occurrence if found, or str->length if no separator is found. - */ -static jsint -find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip, - JSSubString *sep) -{ - jsint i, j, k; - size_t length; - jschar *chars; - - /* - * Stop if past end of string. If at end of string, we will compare the - * null char stored there (by js_NewString*) to sep->chars[j] in the while - * loop at the end of this function, so that - * - * "ab,".split(',') => ["ab", ""] - * - * and the resulting array converts back to the string "ab," for symmetry. - * However, we ape Perl and do this only if there is a sufficiently large - * limit argument (see str_split). - */ - i = *ip; - length = JSSTRING_LENGTH(str); - if ((size_t)i > length) - return -1; - - chars = JSSTRING_CHARS(str); - - /* - * Match a regular expression against the separator at or above index i. - * Call js_ExecuteRegExp with true for the test argument. On successful - * match, get the separator from cx->regExpStatics.lastMatch. - */ - if (re) { - size_t index; - jsval rval; - - again: - /* JS1.2 deviated from Perl by never matching at end of string. */ - index = (size_t)i; - if (!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &rval)) - return -2; - if (rval != JSVAL_TRUE) { - /* Mismatch: ensure our caller advances i past end of string. */ - sep->length = 1; - return length; - } - i = (jsint)index; - *sep = cx->regExpStatics.lastMatch; - if (sep->length == 0) { - /* - * Empty string match: never split on an empty match at the start - * of a find_split cycle. Same rule as for an empty global match - * in match_or_replace. - */ - if (i == *ip) { - /* - * "Bump-along" to avoid sticking at an empty match, but don't - * bump past end of string -- our caller must do that by adding - * sep->length to our return value. - */ - if ((size_t)i == length) - return -1; - i++; - goto again; - } - if ((size_t)i == length) { - /* - * If there was a trivial zero-length match at the end of the - * split, then we shouldn't output the matched string at the end - * of the split array. See ECMA-262 Ed. 3, 15.5.4.14, Step 15. - */ - sep->chars = NULL; - } - } - JS_ASSERT((size_t)i >= sep->length); - return i - sep->length; - } - - /* - * Deviate from ECMA by never splitting an empty string by any separator - * string into a non-empty array (an array of length 1 that contains the - * empty string). - */ - if (!JS_VERSION_IS_ECMA(cx) && length == 0) - return -1; - - /* - * Special case: if sep is the empty string, split str into one character - * substrings. Let our caller worry about whether to split once at end of - * string into an empty substring. - */ - if (sep->length == 0) - return ((size_t)i == length) ? -1 : i + 1; - - /* - * Now that we know sep is non-empty, search starting at i in str for an - * occurrence of all of sep's chars. If we find them, return the index of - * the first separator char. Otherwise, return length. - */ - j = 0; - while ((size_t)(k = i + j) < length) { - if (chars[k] == sep->chars[j]) { - if ((size_t)++j == sep->length) - return i; - } else { - i++; - j = 0; - } - } - return k; -} - -static JSBool -str_split(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str, *sub; - JSObject *arrayobj; - jsval v; - JSBool ok, limited; - JSRegExp *re; - JSSubString *sep, tmp; - jsdouble d; - jsint i, j; - uint32 len, limit; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - - arrayobj = js_ConstructObject(cx, &js_ArrayClass, NULL, NULL, 0, NULL); - if (!arrayobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(arrayobj); - - if (argc == 0) { - v = STRING_TO_JSVAL(str); - ok = JS_SetElement(cx, arrayobj, 0, &v); - } else { - if (JSVAL_IS_REGEXP(cx, argv[0])) { - re = (JSRegExp *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0])); - sep = &tmp; - - /* Set a magic value so we can detect a successful re match. */ - sep->chars = NULL; - sep->length = 0; - } else { - JSString *str2 = js_ValueToString(cx, argv[0]); - if (!str2) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str2); - - /* - * Point sep at a local copy of str2's header because find_split - * will modify sep->length. - */ - tmp.length = JSSTRING_LENGTH(str2); - tmp.chars = JSSTRING_CHARS(str2); - sep = &tmp; - re = NULL; - } - - /* Use the second argument as the split limit, if given. */ - limited = (argc > 1) && !JSVAL_IS_VOID(argv[1]); - limit = 0; /* Avoid warning. */ - if (limited) { - if (!js_ValueToNumber(cx, argv[1], &d)) - return JS_FALSE; - - /* Clamp limit between 0 and 1 + string length. */ - if (!js_DoubleToECMAUint32(cx, d, &limit)) - return JS_FALSE; - if (limit > JSSTRING_LENGTH(str)) - limit = 1 + JSSTRING_LENGTH(str); - } - - len = i = 0; - while ((j = find_split(cx, str, re, &i, sep)) >= 0) { - if (limited && len >= limit) - break; - sub = js_NewDependentString(cx, str, i, (size_t)(j - i), 0); - if (!sub) - return JS_FALSE; - v = STRING_TO_JSVAL(sub); - if (!JS_SetElement(cx, arrayobj, len, &v)) - return JS_FALSE; - len++; - - /* - * Imitate perl's feature of including parenthesized substrings - * that matched part of the delimiter in the new array, after the - * split substring that was delimited. - */ - if (re && sep->chars) { - uintN num; - JSSubString *parsub; - - for (num = 0; num < cx->regExpStatics.parenCount; num++) { - if (limited && len >= limit) - break; - parsub = REGEXP_PAREN_SUBSTRING(&cx->regExpStatics, num); - sub = js_NewStringCopyN(cx, parsub->chars, parsub->length, - 0); - if (!sub) - return JS_FALSE; - v = STRING_TO_JSVAL(sub); - if (!JS_SetElement(cx, arrayobj, len, &v)) - return JS_FALSE; - len++; - } - sep->chars = NULL; - } - - i = j + sep->length; - if (!JS_VERSION_IS_ECMA(cx)) { - /* - * Deviate from ECMA to imitate Perl, which omits a final - * split unless a limit argument is given and big enough. - */ - if (!limited && (size_t)i == JSSTRING_LENGTH(str)) - break; - } - } - ok = (j != -2); - } - return ok; -} - -#if JS_HAS_PERL_SUBSTR -static JSBool -str_substr(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - jsdouble d; - jsdouble length, begin, end; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - - if (argc != 0) { - if (!js_ValueToNumber(cx, argv[0], &d)) - return JS_FALSE; - length = JSSTRING_LENGTH(str); - begin = js_DoubleToInteger(d); - if (begin < 0) { - begin += length; - if (begin < 0) - begin = 0; - } else if (begin > length) { - begin = length; - } - - if (argc == 1) { - end = length; - } else { - if (!js_ValueToNumber(cx, argv[1], &d)) - return JS_FALSE; - end = js_DoubleToInteger(d); - if (end < 0) - end = 0; - end += begin; - if (end > length) - end = length; - } - - str = js_NewDependentString(cx, str, (size_t)begin, - (size_t)(end - begin), 0); - if (!str) - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} -#endif /* JS_HAS_PERL_SUBSTR */ - -/* - * Python-esque sequence operations. - */ -static JSBool -str_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str, *str2; - uintN i; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - - for (i = 0; i < argc; i++) { - str2 = js_ValueToString(cx, argv[i]); - if (!str2) - return JS_FALSE; - argv[i] = STRING_TO_JSVAL(str2); - - str = js_ConcatStrings(cx, str, str2); - if (!str) - return JS_FALSE; - } - - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -str_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - jsdouble d; - jsdouble length, begin, end; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - - if (argc != 0) { - if (!js_ValueToNumber(cx, argv[0], &d)) - return JS_FALSE; - length = JSSTRING_LENGTH(str); - begin = js_DoubleToInteger(d); - if (begin < 0) { - begin += length; - if (begin < 0) - begin = 0; - } else if (begin > length) { - begin = length; - } - - if (argc == 1) { - end = length; - } else { - if (!js_ValueToNumber(cx, argv[1], &d)) - return JS_FALSE; - end = js_DoubleToInteger(d); - if (end < 0) { - end += length; - if (end < 0) - end = 0; - } else if (end > length) { - end = length; - } - if (end < begin) - end = begin; - } - - str = js_NewDependentString(cx, str, (size_t)begin, - (size_t)(end - begin), 0); - if (!str) - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -#if JS_HAS_STR_HTML_HELPERS -/* - * HTML composition aids. - */ -static JSBool -tagify(JSContext *cx, JSObject *obj, jsval *argv, - const char *begin, JSString *param, const char *end, - jsval *rval) -{ - JSString *str; - jschar *tagbuf; - size_t beglen, endlen, parlen, taglen; - size_t i, j; - - if (JSVAL_IS_STRING((jsval)obj)) { - str = JSVAL_TO_STRING((jsval)obj); - } else { - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - } - - if (!end) - end = begin; - - beglen = strlen(begin); - taglen = 1 + beglen + 1; /* '' */ - parlen = 0; /* Avoid warning. */ - if (param) { - parlen = JSSTRING_LENGTH(param); - taglen += 2 + parlen + 1; /* '="param"' */ - } - endlen = strlen(end); - taglen += JSSTRING_LENGTH(str) + 2 + endlen + 1; /* 'str' */ - - if (taglen >= ~(size_t)0 / sizeof(jschar)) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - tagbuf = (jschar *) JS_malloc(cx, (taglen + 1) * sizeof(jschar)); - if (!tagbuf) - return JS_FALSE; - - j = 0; - tagbuf[j++] = '<'; - for (i = 0; i < beglen; i++) - tagbuf[j++] = (jschar)begin[i]; - if (param) { - tagbuf[j++] = '='; - tagbuf[j++] = '"'; - js_strncpy(&tagbuf[j], JSSTRING_CHARS(param), parlen); - j += parlen; - tagbuf[j++] = '"'; - } - tagbuf[j++] = '>'; - js_strncpy(&tagbuf[j], JSSTRING_CHARS(str), JSSTRING_LENGTH(str)); - j += JSSTRING_LENGTH(str); - tagbuf[j++] = '<'; - tagbuf[j++] = '/'; - for (i = 0; i < endlen; i++) - tagbuf[j++] = (jschar)end[i]; - tagbuf[j++] = '>'; - JS_ASSERT(j == taglen); - tagbuf[j] = 0; - - str = js_NewString(cx, tagbuf, taglen, 0); - if (!str) { - free((char *)tagbuf); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -tagify_value(JSContext *cx, JSObject *obj, jsval *argv, - const char *begin, const char *end, - jsval *rval) -{ - JSString *param; - - param = js_ValueToString(cx, argv[0]); - if (!param) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(param); - return tagify(cx, obj, argv, begin, param, end, rval); -} - -static JSBool -str_bold(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify(cx, obj, argv, "b", NULL, NULL, rval); -} - -static JSBool -str_italics(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify(cx, obj, argv, "i", NULL, NULL, rval); -} - -static JSBool -str_fixed(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify(cx, obj, argv, "tt", NULL, NULL, rval); -} - -static JSBool -str_fontsize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify_value(cx, obj, argv, "font size", "font", rval); -} - -static JSBool -str_fontcolor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return tagify_value(cx, obj, argv, "font color", "font", rval); -} - -static JSBool -str_link(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify_value(cx, obj, argv, "a href", "a", rval); -} - -static JSBool -str_anchor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify_value(cx, obj, argv, "a name", "a", rval); -} - -static JSBool -str_strike(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify(cx, obj, argv, "strike", NULL, NULL, rval); -} - -static JSBool -str_small(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify(cx, obj, argv, "small", NULL, NULL, rval); -} - -static JSBool -str_big(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify(cx, obj, argv, "big", NULL, NULL, rval); -} - -static JSBool -str_blink(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify(cx, obj, argv, "blink", NULL, NULL, rval); -} - -static JSBool -str_sup(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify(cx, obj, argv, "sup", NULL, NULL, rval); -} - -static JSBool -str_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify(cx, obj, argv, "sub", NULL, NULL, rval); -} -#endif /* JS_HAS_STR_HTML_HELPERS */ - -static JSFunctionSpec string_methods[] = { -#if JS_HAS_TOSOURCE - {"quote", str_quote, 0,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {js_toSource_str, str_toSource, 0,JSFUN_THISP_STRING,0}, -#endif - - /* Java-like methods. */ - {js_toString_str, str_toString, 0,JSFUN_THISP_STRING,0}, - {js_valueOf_str, str_valueOf, 0,JSFUN_THISP_STRING,0}, - {"substring", str_substring, 2,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"toLowerCase", str_toLowerCase, 0,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"toUpperCase", str_toUpperCase, 0,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"charAt", str_charAt, 1,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"charCodeAt", str_charCodeAt, 1,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"indexOf", str_indexOf, 1,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"lastIndexOf", str_lastIndexOf, 1,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"toLocaleLowerCase", str_toLocaleLowerCase, 0,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"toLocaleUpperCase", str_toLocaleUpperCase, 0,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"localeCompare", str_localeCompare, 1,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - - /* Perl-ish methods (search is actually Python-esque). */ - {"match", str_match, 1,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,2}, - {"search", str_search, 1,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"replace", str_replace, 2,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"split", str_split, 2,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, -#if JS_HAS_PERL_SUBSTR - {"substr", str_substr, 2,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, -#endif - - /* Python-esque sequence methods. */ - {"concat", str_concat, 0,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"slice", str_slice, 0,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - - /* HTML string methods. */ -#if JS_HAS_STR_HTML_HELPERS - {"bold", str_bold, 0,JSFUN_THISP_PRIMITIVE,0}, - {"italics", str_italics, 0,JSFUN_THISP_PRIMITIVE,0}, - {"fixed", str_fixed, 0,JSFUN_THISP_PRIMITIVE,0}, - {"fontsize", str_fontsize, 1,JSFUN_THISP_PRIMITIVE,0}, - {"fontcolor", str_fontcolor, 1,JSFUN_THISP_PRIMITIVE,0}, - {"link", str_link, 1,JSFUN_THISP_PRIMITIVE,0}, - {"anchor", str_anchor, 1,JSFUN_THISP_PRIMITIVE,0}, - {"strike", str_strike, 0,JSFUN_THISP_PRIMITIVE,0}, - {"small", str_small, 0,JSFUN_THISP_PRIMITIVE,0}, - {"big", str_big, 0,JSFUN_THISP_PRIMITIVE,0}, - {"blink", str_blink, 0,JSFUN_THISP_PRIMITIVE,0}, - {"sup", str_sup, 0,JSFUN_THISP_PRIMITIVE,0}, - {"sub", str_sub, 0,JSFUN_THISP_PRIMITIVE,0}, -#endif - - {0,0,0,0,0} -}; - -static JSBool -String(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - - if (argc > 0) { - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str); - } else { - str = cx->runtime->emptyString; - } - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; - } - OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str)); - return JS_TRUE; -} - -static JSBool -str_fromCharCode(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jschar *chars; - uintN i; - uint16 code; - JSString *str; - - JS_ASSERT(argc < ARRAY_INIT_LIMIT); - chars = (jschar *) JS_malloc(cx, (argc + 1) * sizeof(jschar)); - if (!chars) - return JS_FALSE; - for (i = 0; i < argc; i++) { - if (!js_ValueToUint16(cx, argv[i], &code)) { - JS_free(cx, chars); - return JS_FALSE; - } - chars[i] = (jschar)code; - } - chars[i] = 0; - str = js_NewString(cx, chars, argc, 0); - if (!str) { - JS_free(cx, chars); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSFunctionSpec string_static_methods[] = { - {"fromCharCode", str_fromCharCode, 1,0,0}, - {0,0,0,0,0} -}; - -JSBool -js_InitRuntimeStringState(JSContext *cx) -{ - JSRuntime *rt; - JSString *empty; - JSAtom *atom; - - rt = cx->runtime; - - /* Initialize string cache */ -#ifdef JS_THREADSAFE - JS_ASSERT(!rt->deflatedStringCacheLock); - rt->deflatedStringCacheLock = JS_NEW_LOCK(); - if (!rt->deflatedStringCacheLock) - return JS_FALSE; -#endif - - /* Make a permanently locked empty string. */ - JS_ASSERT(!rt->emptyString); - empty = js_NewStringCopyN(cx, js_empty_ucstr, 0, GCF_LOCK); - if (!empty) - goto bad; - - /* Atomize it for scripts that use '' + x to convert x to string. */ - atom = js_AtomizeString(cx, empty, ATOM_PINNED); - if (!atom) - goto bad; - - rt->emptyString = empty; - rt->atomState.emptyAtom = atom; - - return JS_TRUE; - - bad: -#ifdef JS_THREADSAFE - JS_DESTROY_LOCK(rt->deflatedStringCacheLock); - rt->deflatedStringCacheLock = NULL; -#endif - return JS_FALSE; - -} - -void -js_FinishRuntimeStringState(JSContext *cx) -{ - JSRuntime *rt = cx->runtime; - - js_UnlockGCThingRT(rt, rt->emptyString); - rt->emptyString = NULL; -} - -void -js_FinishDeflatedStringCache(JSRuntime *rt) -{ - if (rt->deflatedStringCache) { - JS_HashTableDestroy(rt->deflatedStringCache); - rt->deflatedStringCache = NULL; - } -#ifdef JS_THREADSAFE - if (rt->deflatedStringCacheLock) { - JS_DESTROY_LOCK(rt->deflatedStringCacheLock); - rt->deflatedStringCacheLock = NULL; - } -#endif -} - -JSObject * -js_InitStringClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto; - - /* Define the escape, unescape functions in the global object. */ - if (!JS_DefineFunctions(cx, obj, string_functions)) - return NULL; - - proto = JS_InitClass(cx, obj, NULL, &js_StringClass, String, 1, - string_props, string_methods, - NULL, string_static_methods); - if (!proto) - return NULL; - OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, - STRING_TO_JSVAL(cx->runtime->emptyString)); - return proto; -} - -JSString * -js_NewString(JSContext *cx, jschar *chars, size_t length, uintN gcflag) -{ - JSString *str; - - if (length > JSSTRING_LENGTH_MASK) { - JS_ReportOutOfMemory(cx); - return NULL; - } - - str = (JSString *) js_NewGCThing(cx, gcflag | GCX_STRING, sizeof(JSString)); - if (!str) - return NULL; - str->length = length; - str->chars = chars; -#ifdef DEBUG - { - JSRuntime *rt = cx->runtime; - JS_RUNTIME_METER(rt, liveStrings); - JS_RUNTIME_METER(rt, totalStrings); - JS_LOCK_RUNTIME_VOID(rt, - (rt->lengthSum += (double)length, - rt->lengthSquaredSum += (double)length * (double)length)); - } -#endif - return str; -} - -JSString * -js_NewDependentString(JSContext *cx, JSString *base, size_t start, - size_t length, uintN gcflag) -{ - JSDependentString *ds; - - if (length == 0) - return cx->runtime->emptyString; - - if (start == 0 && length == JSSTRING_LENGTH(base)) - return base; - - if (start > JSSTRDEP_START_MASK || - (start != 0 && length > JSSTRDEP_LENGTH_MASK)) { - return js_NewStringCopyN(cx, JSSTRING_CHARS(base) + start, length, - gcflag); - } - - ds = (JSDependentString *) - js_NewGCThing(cx, gcflag | GCX_MUTABLE_STRING, sizeof(JSString)); - if (!ds) - return NULL; - if (start == 0) { - JSPREFIX_SET_LENGTH(ds, length); - JSPREFIX_SET_BASE(ds, base); - } else { - JSSTRDEP_SET_START_AND_LENGTH(ds, start, length); - JSSTRDEP_SET_BASE(ds, base); - } -#ifdef DEBUG - { - JSRuntime *rt = cx->runtime; - JS_RUNTIME_METER(rt, liveDependentStrings); - JS_RUNTIME_METER(rt, totalDependentStrings); - JS_RUNTIME_METER(rt, liveStrings); - JS_RUNTIME_METER(rt, totalStrings); - JS_LOCK_RUNTIME_VOID(rt, - (rt->strdepLengthSum += (double)length, - rt->strdepLengthSquaredSum += (double)length * (double)length)); - JS_LOCK_RUNTIME_VOID(rt, - (rt->lengthSum += (double)length, - rt->lengthSquaredSum += (double)length * (double)length)); - } -#endif - return (JSString *)ds; -} - -#ifdef DEBUG -#include - -void printJSStringStats(JSRuntime *rt) { - double mean = 0., var = 0., sigma = 0.; - jsrefcount count = rt->totalStrings; - if (count > 0 && rt->lengthSum >= 0) { - mean = rt->lengthSum / count; - var = count * rt->lengthSquaredSum - rt->lengthSum * rt->lengthSum; - if (var < 0.0 || count <= 1) - var = 0.0; - else - var /= count * (count - 1); - - /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ - sigma = (var != 0.) ? sqrt(var) : 0.; - } - fprintf(stderr, "%lu total strings, mean length %g (sigma %g)\n", - (unsigned long)count, mean, sigma); - - mean = var = sigma = 0.; - count = rt->totalDependentStrings; - if (count > 0 && rt->strdepLengthSum >= 0) { - mean = rt->strdepLengthSum / count; - var = count * rt->strdepLengthSquaredSum - - rt->strdepLengthSum * rt->strdepLengthSum; - if (var < 0.0 || count <= 1) - var = 0.0; - else - var /= count * (count - 1); - - /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ - sigma = (var != 0.) ? sqrt(var) : 0.; - } - fprintf(stderr, "%lu total dependent strings, mean length %g (sigma %g)\n", - (unsigned long)count, mean, sigma); -} -#endif - -JSString * -js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n, uintN gcflag) -{ - jschar *news; - JSString *str; - - news = (jschar *)JS_malloc(cx, (n + 1) * sizeof(jschar)); - if (!news) - return NULL; - js_strncpy(news, s, n); - news[n] = 0; - str = js_NewString(cx, news, n, gcflag); - if (!str) - JS_free(cx, news); - return str; -} - -JSString * -js_NewStringCopyZ(JSContext *cx, const jschar *s, uintN gcflag) -{ - size_t n, m; - jschar *news; - JSString *str; - - n = js_strlen(s); - m = (n + 1) * sizeof(jschar); - news = (jschar *) JS_malloc(cx, m); - if (!news) - return NULL; - memcpy(news, s, m); - str = js_NewString(cx, news, n, gcflag); - if (!str) - JS_free(cx, news); - return str; -} - -JS_STATIC_DLL_CALLBACK(JSHashNumber) -js_hash_string_pointer(const void *key) -{ - return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS; -} - -void -js_PurgeDeflatedStringCache(JSRuntime *rt, JSString *str) -{ - JSHashNumber hash; - JSHashEntry *he, **hep; - - if (!rt->deflatedStringCache) - return; - - hash = js_hash_string_pointer(str); - JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock); - hep = JS_HashTableRawLookup(rt->deflatedStringCache, hash, str); - he = *hep; - if (he) { -#ifdef DEBUG - rt->deflatedStringCacheBytes -= JSSTRING_LENGTH(str); -#endif - free(he->value); - JS_HashTableRawRemove(rt->deflatedStringCache, hep, he); - } - JS_RELEASE_LOCK(rt->deflatedStringCacheLock); -} - -void -js_FinalizeString(JSContext *cx, JSString *str) -{ - js_FinalizeStringRT(cx->runtime, str); -} - -void -js_FinalizeStringRT(JSRuntime *rt, JSString *str) -{ - JSBool valid; - - JS_RUNTIME_UNMETER(rt, liveStrings); - if (JSSTRING_IS_DEPENDENT(str)) { - /* If JSSTRFLAG_DEPENDENT is set, this string must be valid. */ - JS_ASSERT(JSSTRDEP_BASE(str)); - JS_RUNTIME_UNMETER(rt, liveDependentStrings); - valid = JS_TRUE; - } else { - /* A stillborn string has null chars, so is not valid. */ - valid = (str->chars != NULL); - if (valid) - free(str->chars); - } - if (valid) { - js_PurgeDeflatedStringCache(rt, str); - str->chars = NULL; - } - str->length = 0; -} - -JSObject * -js_StringToObject(JSContext *cx, JSString *str) -{ - JSObject *obj; - - obj = js_NewObject(cx, &js_StringClass, NULL, NULL); - if (!obj) - return NULL; - OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str)); - return obj; -} - -JS_FRIEND_API(const char *) -js_ValueToPrintable(JSContext *cx, jsval v, JSValueToStringFun v2sfun) -{ - JSString *str; - const char *bytes; - - str = v2sfun(cx, v); - if (!str) - return NULL; - str = js_QuoteString(cx, str, 0); - if (!str) - return NULL; - bytes = js_GetStringBytes(cx->runtime, str); - if (!bytes) - JS_ReportOutOfMemory(cx); - return bytes; -} - -JS_FRIEND_API(JSString *) -js_ValueToString(JSContext *cx, jsval v) -{ - JSObject *obj; - JSString *str; - - if (JSVAL_IS_OBJECT(v)) { - obj = JSVAL_TO_OBJECT(v); - if (!obj) - return ATOM_TO_STRING(cx->runtime->atomState.nullAtom); - if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v)) - return NULL; - } - if (JSVAL_IS_STRING(v)) { - str = JSVAL_TO_STRING(v); - } else if (JSVAL_IS_INT(v)) { - str = js_NumberToString(cx, JSVAL_TO_INT(v)); - } else if (JSVAL_IS_DOUBLE(v)) { - str = js_NumberToString(cx, *JSVAL_TO_DOUBLE(v)); - } else if (JSVAL_IS_BOOLEAN(v)) { - str = js_BooleanToString(cx, JSVAL_TO_BOOLEAN(v)); - } else { - str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); - } - return str; -} - -JS_FRIEND_API(JSString *) -js_ValueToSource(JSContext *cx, jsval v) -{ - JSTempValueRooter tvr; - JSString *str; - - if (JSVAL_IS_STRING(v)) - return js_QuoteString(cx, JSVAL_TO_STRING(v), '"'); - if (JSVAL_IS_PRIMITIVE(v)) { - /* Special case to preserve negative zero, _contra_ toString. */ - if (JSVAL_IS_DOUBLE(v) && JSDOUBLE_IS_NEGZERO(*JSVAL_TO_DOUBLE(v))) { - /* NB: _ucNstr rather than _ucstr to indicate non-terminated. */ - static const jschar js_negzero_ucNstr[] = {'-', '0'}; - - return js_NewStringCopyN(cx, js_negzero_ucNstr, 2, 0); - } - return js_ValueToString(cx, v); - } - - JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); - if (!js_TryMethod(cx, JSVAL_TO_OBJECT(v), - cx->runtime->atomState.toSourceAtom, - 0, NULL, &tvr.u.value)) { - str = NULL; - } else { - str = js_ValueToString(cx, tvr.u.value); - } - JS_POP_TEMP_ROOT(cx, &tvr); - return str; -} - -JSHashNumber -js_HashString(JSString *str) -{ - JSHashNumber h; - const jschar *s; - size_t n; - - h = 0; - for (s = JSSTRING_CHARS(str), n = JSSTRING_LENGTH(str); n; s++, n--) - h = (h >> (JS_HASH_BITS - 4)) ^ (h << 4) ^ *s; - return h; -} - -intN -js_CompareStrings(JSString *str1, JSString *str2) -{ - size_t l1, l2, n, i; - const jschar *s1, *s2; - intN cmp; - - JS_ASSERT(str1); - JS_ASSERT(str2); - - /* Fast case: pointer equality could be a quick win. */ - if (str1 == str2) - return 0; - - l1 = JSSTRING_LENGTH(str1), l2 = JSSTRING_LENGTH(str2); - s1 = JSSTRING_CHARS(str1), s2 = JSSTRING_CHARS(str2); - n = JS_MIN(l1, l2); - for (i = 0; i < n; i++) { - cmp = s1[i] - s2[i]; - if (cmp != 0) - return cmp; - } - return (intN)(l1 - l2); -} - -JSBool -js_EqualStrings(JSString *str1, JSString *str2) -{ - size_t n; - const jschar *s1, *s2; - - JS_ASSERT(str1); - JS_ASSERT(str2); - - /* Fast case: pointer equality could be a quick win. */ - if (str1 == str2) - return JS_TRUE; - - n = JSSTRING_LENGTH(str1); - if (n != JSSTRING_LENGTH(str2)) - return JS_FALSE; - - if (n == 0) - return JS_TRUE; - - s1 = JSSTRING_CHARS(str1), s2 = JSSTRING_CHARS(str2); - do { - if (*s1 != *s2) - return JS_FALSE; - ++s1, ++s2; - } while (--n != 0); - - return JS_TRUE; -} - -size_t -js_strlen(const jschar *s) -{ - const jschar *t; - - for (t = s; *t != 0; t++) - continue; - return (size_t)(t - s); -} - -jschar * -js_strchr(const jschar *s, jschar c) -{ - while (*s != 0) { - if (*s == c) - return (jschar *)s; - s++; - } - return NULL; -} - -jschar * -js_strchr_limit(const jschar *s, jschar c, const jschar *limit) -{ - while (s < limit) { - if (*s == c) - return (jschar *)s; - s++; - } - return NULL; -} - -const jschar * -js_SkipWhiteSpace(const jschar *s) -{ - /* JS_ISSPACE is false on a null. */ - while (JS_ISSPACE(*s)) - s++; - return s; -} - -#ifdef JS_C_STRINGS_ARE_UTF8 - -jschar * -js_InflateString(JSContext *cx, const char *bytes, size_t *length) -{ - jschar *chars = NULL; - size_t dstlen = 0; - - if (!js_InflateStringToBuffer(cx, bytes, *length, NULL, &dstlen)) - return NULL; - chars = (jschar *) JS_malloc(cx, (dstlen + 1) * sizeof (jschar)); - if (!chars) - return NULL; - js_InflateStringToBuffer(cx, bytes, *length, chars, &dstlen); - chars[dstlen] = 0; - *length = dstlen; - return chars; -} - -/* - * May be called with null cx by js_GetStringBytes, see below. - */ -char * -js_DeflateString(JSContext *cx, const jschar *chars, size_t length) -{ - size_t size = 0; - char *bytes = NULL; - if (!js_DeflateStringToBuffer(cx, chars, length, NULL, &size)) - return NULL; - bytes = (char *) (cx ? JS_malloc(cx, size+1) : malloc(size+1)); - if (!bytes) - return NULL; - js_DeflateStringToBuffer(cx, chars, length, bytes, &size); - bytes[size] = 0; - return bytes; -} - -JSBool -js_DeflateStringToBuffer(JSContext *cx, const jschar *src, size_t srclen, - char *dst, size_t *dstlenp) -{ - size_t i, utf8Len, dstlen = *dstlenp, origDstlen = dstlen; - jschar c, c2; - uint32 v; - uint8 utf8buf[6]; - - if (!dst) - dstlen = origDstlen = (size_t) -1; - - while (srclen) { - c = *src++; - srclen--; - if ((c >= 0xDC00) && (c <= 0xDFFF)) - goto badSurrogate; - if (c < 0xD800 || c > 0xDBFF) { - v = c; - } else { - if (srclen < 1) - goto bufferTooSmall; - c2 = *src++; - srclen--; - if ((c2 < 0xDC00) || (c2 > 0xDFFF)) { - c = c2; - goto badSurrogate; - } - v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; - } - if (v < 0x0080) { - /* no encoding necessary - performance hack */ - if (!dstlen) - goto bufferTooSmall; - if (dst) - *dst++ = (char) v; - utf8Len = 1; - } else { - utf8Len = js_OneUcs4ToUtf8Char(utf8buf, v); - if (utf8Len > dstlen) - goto bufferTooSmall; - if (dst) { - for (i = 0; i < utf8Len; i++) - *dst++ = (char) utf8buf[i]; - } - } - dstlen -= utf8Len; - } - *dstlenp = (origDstlen - dstlen); - return JS_TRUE; - -badSurrogate: - *dstlenp = (origDstlen - dstlen); - if (cx) { - char buffer[10]; - JS_snprintf(buffer, 10, "0x%x", c); - JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, - js_GetErrorMessage, NULL, - JSMSG_BAD_SURROGATE_CHAR, - buffer); - } - return JS_FALSE; - -bufferTooSmall: - *dstlenp = (origDstlen - dstlen); - if (cx) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BUFFER_TOO_SMALL); - } - return JS_FALSE; -} - -JSBool -js_InflateStringToBuffer(JSContext *cx, const char *src, size_t srclen, - jschar *dst, size_t *dstlenp) -{ - uint32 v; - size_t offset = 0, j, n, dstlen = *dstlenp, origDstlen = dstlen; - - if (!dst) - dstlen = origDstlen = (size_t) -1; - - while (srclen) { - v = (uint8) *src; - n = 1; - if (v & 0x80) { - while (v & (0x80 >> n)) - n++; - if (n > srclen) - goto bufferTooSmall; - if (n == 1 || n > 6) - goto badCharacter; - for (j = 1; j < n; j++) { - if ((src[j] & 0xC0) != 0x80) - goto badCharacter; - } - v = Utf8ToOneUcs4Char(src, n); - if (v >= 0x10000) { - v -= 0x10000; - if (v > 0xFFFFF || dstlen < 2) { - *dstlenp = (origDstlen - dstlen); - if (cx) { - char buffer[10]; - JS_snprintf(buffer, 10, "0x%x", v + 0x10000); - JS_ReportErrorFlagsAndNumber(cx, - JSREPORT_ERROR, - js_GetErrorMessage, NULL, - JSMSG_UTF8_CHAR_TOO_LARGE, - buffer); - } - return JS_FALSE; - } - if (dstlen < 2) - goto bufferTooSmall; - if (dst) { - *dst++ = (jschar)((v >> 10) + 0xD800); - v = (jschar)((v & 0x3FF) + 0xDC00); - } - dstlen--; - } - } - if (!dstlen) - goto bufferTooSmall; - if (dst) - *dst++ = (jschar) v; - dstlen--; - offset += n; - src += n; - srclen -= n; - } - *dstlenp = (origDstlen - dstlen); - return JS_TRUE; - -badCharacter: - *dstlenp = (origDstlen - dstlen); - if (cx) { - char buffer[10]; - JS_snprintf(buffer, 10, "%d", offset); - JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, - js_GetErrorMessage, NULL, - JSMSG_MALFORMED_UTF8_CHAR, - buffer); - } - return JS_FALSE; - -bufferTooSmall: - *dstlenp = (origDstlen - dstlen); - if (cx) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BUFFER_TOO_SMALL); - } - return JS_FALSE; -} - -#else - -JSBool -js_InflateStringToBuffer(JSContext* cx, const char *bytes, size_t length, - jschar *chars, size_t* charsLength) -{ - size_t i; - - if (length > *charsLength) { - for (i = 0; i < *charsLength; i++) - chars[i] = (unsigned char) bytes[i]; - if (cx) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BUFFER_TOO_SMALL); - } - return JS_FALSE; - } - for (i = 0; i < length; i++) - chars[i] = (unsigned char) bytes[i]; - *charsLength = length; - return JS_TRUE; -} - -jschar * -js_InflateString(JSContext *cx, const char *bytes, size_t *bytesLength) -{ - jschar *chars; - size_t i, length = *bytesLength; - - chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); - if (!chars) { - *bytesLength = 0; - return NULL; - } - for (i = 0; i < length; i++) - chars[i] = (unsigned char) bytes[i]; - chars[length] = 0; - *bytesLength = length; - return chars; -} - -JSBool -js_DeflateStringToBuffer(JSContext* cx, const jschar *chars, size_t length, - char *bytes, size_t* bytesLength) -{ - size_t i; - - if (length > *bytesLength) { - for (i = 0; i < *bytesLength; i++) - bytes[i] = (char) chars[i]; - if (cx) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BUFFER_TOO_SMALL); - } - return JS_FALSE; - } - for (i = 0; i < length; i++) - bytes[i] = (char) chars[i]; - *bytesLength = length; - return JS_TRUE; -} - -/* - * May be called with null cx by js_GetStringBytes, see below. - */ -char * -js_DeflateString(JSContext *cx, const jschar *chars, size_t length) -{ - size_t i, size; - char *bytes; - - size = (length + 1) * sizeof(char); - bytes = (char *) (cx ? JS_malloc(cx, size) : malloc(size)); - if (!bytes) - return NULL; - - for (i = 0; i < length; i++) - bytes[i] = (char) chars[i]; - - bytes[length] = 0; - return bytes; -} - -#endif - -static JSHashTable * -GetDeflatedStringCache(JSRuntime *rt) -{ - JSHashTable *cache; - - cache = rt->deflatedStringCache; - if (!cache) { - cache = JS_NewHashTable(8, js_hash_string_pointer, - JS_CompareValues, JS_CompareValues, - NULL, NULL); - rt->deflatedStringCache = cache; - } - return cache; -} - -JSBool -js_SetStringBytes(JSRuntime *rt, JSString *str, char *bytes, size_t length) -{ - JSHashTable *cache; - JSBool ok; - JSHashNumber hash; - JSHashEntry **hep; - - JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock); - - cache = GetDeflatedStringCache(rt); - if (!cache) { - ok = JS_FALSE; - } else { - hash = js_hash_string_pointer(str); - hep = JS_HashTableRawLookup(cache, hash, str); - JS_ASSERT(*hep == NULL); - ok = JS_HashTableRawAdd(cache, hep, hash, str, bytes) != NULL; -#ifdef DEBUG - if (ok) - rt->deflatedStringCacheBytes += length; -#endif - } - - JS_RELEASE_LOCK(rt->deflatedStringCacheLock); - return ok; -} - -char * -js_GetStringBytes(JSRuntime *rt, JSString *str) -{ - JSHashTable *cache; - char *bytes; - JSHashNumber hash; - JSHashEntry *he, **hep; - - JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock); - - cache = GetDeflatedStringCache(rt); - if (!cache) { - bytes = NULL; - } else { - hash = js_hash_string_pointer(str); - hep = JS_HashTableRawLookup(cache, hash, str); - he = *hep; - if (he) { - bytes = (char *) he->value; - - /* Try to catch failure to JS_ShutDown between runtime epochs. */ - JS_ASSERT((*bytes == '\0' && JSSTRING_LENGTH(str) == 0) || - *bytes == (char) JSSTRING_CHARS(str)[0]); - } else { - bytes = js_DeflateString(NULL, JSSTRING_CHARS(str), - JSSTRING_LENGTH(str)); - if (bytes) { - if (JS_HashTableRawAdd(cache, hep, hash, str, bytes)) { -#ifdef DEBUG - rt->deflatedStringCacheBytes += JSSTRING_LENGTH(str); -#endif - } else { - free(bytes); - bytes = NULL; - } - } - } - } - - JS_RELEASE_LOCK(rt->deflatedStringCacheLock); - return bytes; -} - -/* - * From java.lang.Character.java: - * - * The character properties are currently encoded into 32 bits in the - * following manner: - * - * 10 bits signed offset used for converting case - * 1 bit if 1, adding the signed offset converts the character to - * lowercase - * 1 bit if 1, subtracting the signed offset converts the character to - * uppercase - * 1 bit if 1, character has a titlecase equivalent (possibly itself) - * 3 bits 0 may not be part of an identifier - * 1 ignorable control; may continue a Unicode identifier or JS - * identifier - * 2 may continue a JS identifier but not a Unicode identifier - * (unused) - * 3 may continue a Unicode identifier or JS identifier - * 4 is a JS whitespace character - * 5 may start or continue a JS identifier; - * may continue but not start a Unicode identifier (_) - * 6 may start or continue a JS identifier but not a Unicode - * identifier ($) - * 7 may start or continue a Unicode identifier or JS identifier - * Thus: - * 5, 6, 7 may start a JS identifier - * 1, 2, 3, 5, 6, 7 may continue a JS identifier - * 7 may start a Unicode identifier - * 1, 3, 5, 7 may continue a Unicode identifier - * 1 is ignorable within an identifier - * 4 is JS whitespace - * 2 bits 0 this character has no numeric property - * 1 adding the digit offset to the character code and then - * masking with 0x1F will produce the desired numeric value - * 2 this character has a "strange" numeric value - * 3 a JS supradecimal digit: adding the digit offset to the - * character code, then masking with 0x1F, then adding 10 - * will produce the desired numeric value - * 5 bits digit offset - * 1 bit XML 1.0 name start character - * 1 bit XML 1.0 name character - * 2 bits reserved for future use - * 5 bits character type - */ - -/* The X table has 1024 entries for a total of 1024 bytes. */ - -const uint8 js_X[] = { - 0, 1, 2, 3, 4, 5, 6, 7, /* 0x0000 */ - 8, 9, 10, 11, 12, 13, 14, 15, /* 0x0200 */ - 16, 17, 18, 19, 20, 21, 22, 23, /* 0x0400 */ - 24, 25, 26, 27, 28, 28, 28, 28, /* 0x0600 */ - 28, 28, 28, 28, 29, 30, 31, 32, /* 0x0800 */ - 33, 34, 35, 36, 37, 38, 39, 40, /* 0x0A00 */ - 41, 42, 43, 44, 45, 46, 28, 28, /* 0x0C00 */ - 47, 48, 49, 50, 51, 52, 53, 28, /* 0x0E00 */ - 28, 28, 54, 55, 56, 57, 58, 59, /* 0x1000 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1200 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1400 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1600 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1800 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1A00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1C00 */ - 60, 60, 61, 62, 63, 64, 65, 66, /* 0x1E00 */ - 67, 68, 69, 70, 71, 72, 73, 74, /* 0x2000 */ - 75, 75, 75, 76, 77, 78, 28, 28, /* 0x2200 */ - 79, 80, 81, 82, 83, 83, 84, 85, /* 0x2400 */ - 86, 85, 28, 28, 87, 88, 89, 28, /* 0x2600 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2800 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2A00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2C00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2E00 */ - 90, 91, 92, 93, 94, 56, 95, 28, /* 0x3000 */ - 96, 97, 98, 99, 83, 100, 83, 101, /* 0x3200 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3400 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3600 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3800 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3A00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3C00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3E00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4000 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4200 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4400 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4600 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4800 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4A00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4C00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x4E00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5400 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5600 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5800 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5A00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5C00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5E00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6400 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6600 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6800 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6A00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6C00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6E00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7400 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7600 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7800 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7A00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7C00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7E00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8400 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8600 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8800 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8A00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8C00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8E00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9400 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9600 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9800 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9A00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9C00 */ - 56, 56, 56, 56, 56, 56, 102, 28, /* 0x9E00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA000 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA200 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA400 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA600 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA800 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0xAA00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xAC00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xAE00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB400 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB600 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB800 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBA00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBC00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBE00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC400 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC600 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC800 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCA00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCC00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCE00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD400 */ - 56, 56, 56, 56, 56, 56, 103, 28, /* 0xD600 */ -104, 104, 104, 104, 104, 104, 104, 104, /* 0xD800 */ -104, 104, 104, 104, 104, 104, 104, 104, /* 0xDA00 */ -104, 104, 104, 104, 104, 104, 104, 104, /* 0xDC00 */ -104, 104, 104, 104, 104, 104, 104, 104, /* 0xDE00 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xE000 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xE200 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xE400 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xE600 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xE800 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xEA00 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xEC00 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xEE00 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xF000 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xF200 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xF400 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xF600 */ -105, 105, 105, 105, 56, 56, 56, 56, /* 0xF800 */ -106, 28, 28, 28, 107, 108, 109, 110, /* 0xFA00 */ - 56, 56, 56, 56, 111, 112, 113, 114, /* 0xFC00 */ -115, 116, 56, 117, 118, 119, 120, 121 /* 0xFE00 */ -}; - -/* The Y table has 7808 entries for a total of 7808 bytes. */ - -const uint8 js_Y[] = { - 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ - 0, 1, 1, 1, 1, 1, 0, 0, /* 0 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ - 2, 3, 3, 3, 4, 3, 3, 3, /* 0 */ - 5, 6, 3, 7, 3, 8, 3, 3, /* 0 */ - 9, 9, 9, 9, 9, 9, 9, 9, /* 0 */ - 9, 9, 3, 3, 7, 7, 7, 3, /* 0 */ - 3, 10, 10, 10, 10, 10, 10, 10, /* 1 */ - 10, 10, 10, 10, 10, 10, 10, 10, /* 1 */ - 10, 10, 10, 10, 10, 10, 10, 10, /* 1 */ - 10, 10, 10, 5, 3, 6, 11, 12, /* 1 */ - 11, 13, 13, 13, 13, 13, 13, 13, /* 1 */ - 13, 13, 13, 13, 13, 13, 13, 13, /* 1 */ - 13, 13, 13, 13, 13, 13, 13, 13, /* 1 */ - 13, 13, 13, 5, 7, 6, 7, 0, /* 1 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ - 2, 3, 4, 4, 4, 4, 15, 15, /* 2 */ - 11, 15, 16, 5, 7, 8, 15, 11, /* 2 */ - 15, 7, 17, 17, 11, 16, 15, 3, /* 2 */ - 11, 18, 16, 6, 19, 19, 19, 3, /* 2 */ - 20, 20, 20, 20, 20, 20, 20, 20, /* 3 */ - 20, 20, 20, 20, 20, 20, 20, 20, /* 3 */ - 20, 20, 20, 20, 20, 20, 20, 7, /* 3 */ - 20, 20, 20, 20, 20, 20, 20, 16, /* 3 */ - 21, 21, 21, 21, 21, 21, 21, 21, /* 3 */ - 21, 21, 21, 21, 21, 21, 21, 21, /* 3 */ - 21, 21, 21, 21, 21, 21, 21, 7, /* 3 */ - 21, 21, 21, 21, 21, 21, 21, 22, /* 3 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ - 25, 26, 23, 24, 23, 24, 23, 24, /* 4 */ - 16, 23, 24, 23, 24, 23, 24, 23, /* 4 */ - 24, 23, 24, 23, 24, 23, 24, 23, /* 5 */ - 24, 16, 23, 24, 23, 24, 23, 24, /* 5 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ - 27, 23, 24, 23, 24, 23, 24, 28, /* 5 */ - 16, 29, 23, 24, 23, 24, 30, 23, /* 6 */ - 24, 31, 31, 23, 24, 16, 32, 32, /* 6 */ - 33, 23, 24, 31, 34, 16, 35, 36, /* 6 */ - 23, 24, 16, 16, 35, 37, 16, 38, /* 6 */ - 23, 24, 23, 24, 23, 24, 38, 23, /* 6 */ - 24, 39, 40, 16, 23, 24, 39, 23, /* 6 */ - 24, 41, 41, 23, 24, 23, 24, 42, /* 6 */ - 23, 24, 16, 40, 23, 24, 40, 40, /* 6 */ - 40, 40, 40, 40, 43, 44, 45, 43, /* 7 */ - 44, 45, 43, 44, 45, 23, 24, 23, /* 7 */ - 24, 23, 24, 23, 24, 23, 24, 23, /* 7 */ - 24, 23, 24, 23, 24, 16, 23, 24, /* 7 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 7 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 7 */ - 16, 43, 44, 45, 23, 24, 46, 46, /* 7 */ - 46, 46, 23, 24, 23, 24, 23, 24, /* 7 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 9 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 9 */ - 16, 16, 16, 47, 48, 16, 49, 49, /* 9 */ - 50, 50, 16, 51, 16, 16, 16, 16, /* 9 */ - 49, 16, 16, 52, 16, 16, 16, 16, /* 9 */ - 53, 54, 16, 16, 16, 16, 16, 54, /* 9 */ - 16, 16, 55, 16, 16, 16, 16, 16, /* 9 */ - 16, 16, 16, 16, 16, 16, 16, 16, /* 9 */ - 16, 16, 16, 56, 16, 16, 16, 16, /* 10 */ - 56, 16, 57, 57, 16, 16, 16, 16, /* 10 */ - 16, 16, 58, 16, 16, 16, 16, 16, /* 10 */ - 16, 16, 16, 16, 16, 16, 16, 16, /* 10 */ - 16, 16, 16, 16, 16, 16, 16, 16, /* 10 */ - 16, 46, 46, 46, 46, 46, 46, 46, /* 10 */ - 59, 59, 59, 59, 59, 59, 59, 59, /* 10 */ - 59, 11, 11, 59, 59, 59, 59, 59, /* 10 */ - 59, 59, 11, 11, 11, 11, 11, 11, /* 11 */ - 11, 11, 11, 11, 11, 11, 11, 11, /* 11 */ - 59, 59, 11, 11, 11, 11, 11, 11, /* 11 */ - 11, 11, 11, 11, 11, 11, 11, 46, /* 11 */ - 59, 59, 59, 59, 59, 11, 11, 11, /* 11 */ - 11, 11, 46, 46, 46, 46, 46, 46, /* 11 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 11 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 11 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 46, 46, /* 13 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ - 60, 60, 46, 46, 46, 46, 46, 46, /* 13 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ - 46, 46, 46, 46, 3, 3, 46, 46, /* 13 */ - 46, 46, 59, 46, 46, 46, 3, 46, /* 13 */ - 46, 46, 46, 46, 11, 11, 61, 3, /* 14 */ - 62, 62, 62, 46, 63, 46, 64, 64, /* 14 */ - 16, 20, 20, 20, 20, 20, 20, 20, /* 14 */ - 20, 20, 20, 20, 20, 20, 20, 20, /* 14 */ - 20, 20, 46, 20, 20, 20, 20, 20, /* 14 */ - 20, 20, 20, 20, 65, 66, 66, 66, /* 14 */ - 16, 21, 21, 21, 21, 21, 21, 21, /* 14 */ - 21, 21, 21, 21, 21, 21, 21, 21, /* 14 */ - 21, 21, 16, 21, 21, 21, 21, 21, /* 15 */ - 21, 21, 21, 21, 67, 68, 68, 46, /* 15 */ - 69, 70, 38, 38, 38, 71, 72, 46, /* 15 */ - 46, 46, 38, 46, 38, 46, 38, 46, /* 15 */ - 38, 46, 23, 24, 23, 24, 23, 24, /* 15 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 15 */ - 73, 74, 16, 40, 46, 46, 46, 46, /* 15 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 15 */ - 46, 75, 75, 75, 75, 75, 75, 75, /* 16 */ - 75, 75, 75, 75, 75, 46, 75, 75, /* 16 */ - 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ - 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ - 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ - 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ - 21, 21, 21, 21, 21, 21, 21, 21, /* 16 */ - 21, 21, 21, 21, 21, 21, 21, 21, /* 16 */ - 21, 21, 21, 21, 21, 21, 21, 21, /* 17 */ - 21, 21, 21, 21, 21, 21, 21, 21, /* 17 */ - 46, 74, 74, 74, 74, 74, 74, 74, /* 17 */ - 74, 74, 74, 74, 74, 46, 74, 74, /* 17 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ - 23, 24, 15, 60, 60, 60, 60, 46, /* 18 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 18 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ - 40, 23, 24, 23, 24, 46, 46, 23, /* 19 */ - 24, 46, 46, 23, 24, 46, 46, 46, /* 19 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */ - 23, 24, 23, 24, 46, 46, 23, 24, /* 19 */ - 23, 24, 23, 24, 23, 24, 46, 46, /* 19 */ - 23, 24, 46, 46, 46, 46, 46, 46, /* 19 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ - 46, 76, 76, 76, 76, 76, 76, 76, /* 20 */ - 76, 76, 76, 76, 76, 76, 76, 76, /* 20 */ - 76, 76, 76, 76, 76, 76, 76, 76, /* 21 */ - 76, 76, 76, 76, 76, 76, 76, 76, /* 21 */ - 76, 76, 76, 76, 76, 76, 76, 46, /* 21 */ - 46, 59, 3, 3, 3, 3, 3, 3, /* 21 */ - 46, 77, 77, 77, 77, 77, 77, 77, /* 21 */ - 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */ - 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */ - 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */ - 77, 77, 77, 77, 77, 77, 77, 16, /* 22 */ - 46, 3, 46, 46, 46, 46, 46, 46, /* 22 */ - 46, 60, 60, 60, 60, 60, 60, 60, /* 22 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */ - 60, 60, 46, 60, 60, 60, 60, 60, /* 22 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */ - 60, 60, 46, 60, 60, 60, 3, 60, /* 22 */ - 3, 60, 60, 3, 60, 46, 46, 46, /* 23 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 23 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */ - 40, 40, 40, 46, 46, 46, 46, 46, /* 23 */ - 40, 40, 40, 3, 3, 46, 46, 46, /* 23 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 23 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 24 */ - 46, 46, 46, 46, 3, 46, 46, 46, /* 24 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 24 */ - 46, 46, 46, 3, 46, 46, 46, 3, /* 24 */ - 46, 40, 40, 40, 40, 40, 40, 40, /* 24 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 24 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 24 */ - 40, 40, 40, 46, 46, 46, 46, 46, /* 24 */ - 59, 40, 40, 40, 40, 40, 40, 40, /* 25 */ - 40, 40, 40, 60, 60, 60, 60, 60, /* 25 */ - 60, 60, 60, 46, 46, 46, 46, 46, /* 25 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 25 */ - 78, 78, 78, 78, 78, 78, 78, 78, /* 25 */ - 78, 78, 3, 3, 3, 3, 46, 46, /* 25 */ - 60, 40, 40, 40, 40, 40, 40, 40, /* 25 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 25 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ - 46, 46, 40, 40, 40, 40, 40, 46, /* 26 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 27 */ - 40, 40, 40, 40, 40, 40, 40, 46, /* 27 */ - 40, 40, 40, 40, 3, 40, 60, 60, /* 27 */ - 60, 60, 60, 60, 60, 79, 79, 60, /* 27 */ - 60, 60, 60, 60, 60, 59, 59, 60, /* 27 */ - 60, 15, 60, 60, 60, 60, 46, 46, /* 27 */ - 9, 9, 9, 9, 9, 9, 9, 9, /* 27 */ - 9, 9, 46, 46, 46, 46, 46, 46, /* 27 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 60, 60, 80, 46, 40, 40, 40, /* 29 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ - 40, 40, 46, 46, 60, 40, 80, 80, /* 29 */ - 80, 60, 60, 60, 60, 60, 60, 60, /* 30 */ - 60, 80, 80, 80, 80, 60, 46, 46, /* 30 */ - 15, 60, 60, 60, 60, 46, 46, 46, /* 30 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 30 */ - 40, 40, 60, 60, 3, 3, 81, 81, /* 30 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 30 */ - 3, 46, 46, 46, 46, 46, 46, 46, /* 30 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 30 */ - 46, 60, 80, 80, 46, 40, 40, 40, /* 31 */ - 40, 40, 40, 40, 40, 46, 46, 40, /* 31 */ - 40, 46, 46, 40, 40, 40, 40, 40, /* 31 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 31 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 31 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 31 */ - 40, 46, 40, 46, 46, 46, 40, 40, /* 31 */ - 40, 40, 46, 46, 60, 46, 80, 80, /* 31 */ - 80, 60, 60, 60, 60, 46, 46, 80, /* 32 */ - 80, 46, 46, 80, 80, 60, 46, 46, /* 32 */ - 46, 46, 46, 46, 46, 46, 46, 80, /* 32 */ - 46, 46, 46, 46, 40, 40, 46, 40, /* 32 */ - 40, 40, 60, 60, 46, 46, 81, 81, /* 32 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 32 */ - 40, 40, 4, 4, 82, 82, 82, 82, /* 32 */ - 19, 83, 15, 46, 46, 46, 46, 46, /* 32 */ - 46, 46, 60, 46, 46, 40, 40, 40, /* 33 */ - 40, 40, 40, 46, 46, 46, 46, 40, /* 33 */ - 40, 46, 46, 40, 40, 40, 40, 40, /* 33 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 33 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 33 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 33 */ - 40, 46, 40, 40, 46, 40, 40, 46, /* 33 */ - 40, 40, 46, 46, 60, 46, 80, 80, /* 33 */ - 80, 60, 60, 46, 46, 46, 46, 60, /* 34 */ - 60, 46, 46, 60, 60, 60, 46, 46, /* 34 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 34 */ - 46, 40, 40, 40, 40, 46, 40, 46, /* 34 */ - 46, 46, 46, 46, 46, 46, 81, 81, /* 34 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 34 */ - 60, 60, 40, 40, 40, 46, 46, 46, /* 34 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 34 */ - 46, 60, 60, 80, 46, 40, 40, 40, /* 35 */ - 40, 40, 40, 40, 46, 40, 46, 40, /* 35 */ - 40, 40, 46, 40, 40, 40, 40, 40, /* 35 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 35 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 35 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 35 */ - 40, 46, 40, 40, 46, 40, 40, 40, /* 35 */ - 40, 40, 46, 46, 60, 40, 80, 80, /* 35 */ - 80, 60, 60, 60, 60, 60, 46, 60, /* 36 */ - 60, 80, 46, 80, 80, 60, 46, 46, /* 36 */ - 15, 46, 46, 46, 46, 46, 46, 46, /* 36 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */ - 40, 46, 46, 46, 46, 46, 81, 81, /* 36 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 36 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */ - 46, 60, 80, 80, 46, 40, 40, 40, /* 37 */ - 40, 40, 40, 40, 40, 46, 46, 40, /* 37 */ - 40, 46, 46, 40, 40, 40, 40, 40, /* 37 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 37 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 37 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 37 */ - 40, 46, 40, 40, 46, 46, 40, 40, /* 37 */ - 40, 40, 46, 46, 60, 40, 80, 60, /* 37 */ - 80, 60, 60, 60, 46, 46, 46, 80, /* 38 */ - 80, 46, 46, 80, 80, 60, 46, 46, /* 38 */ - 46, 46, 46, 46, 46, 46, 60, 80, /* 38 */ - 46, 46, 46, 46, 40, 40, 46, 40, /* 38 */ - 40, 40, 46, 46, 46, 46, 81, 81, /* 38 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 38 */ - 15, 46, 46, 46, 46, 46, 46, 46, /* 38 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 38 */ - 46, 46, 60, 80, 46, 40, 40, 40, /* 39 */ - 40, 40, 40, 46, 46, 46, 40, 40, /* 39 */ - 40, 46, 40, 40, 40, 40, 46, 46, /* 39 */ - 46, 40, 40, 46, 40, 46, 40, 40, /* 39 */ - 46, 46, 46, 40, 40, 46, 46, 46, /* 39 */ - 40, 40, 40, 46, 46, 46, 40, 40, /* 39 */ - 40, 40, 40, 40, 40, 40, 46, 40, /* 39 */ - 40, 40, 46, 46, 46, 46, 80, 80, /* 39 */ - 60, 80, 80, 46, 46, 46, 80, 80, /* 40 */ - 80, 46, 80, 80, 80, 60, 46, 46, /* 40 */ - 46, 46, 46, 46, 46, 46, 46, 80, /* 40 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 40 */ - 46, 46, 46, 46, 46, 46, 46, 81, /* 40 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 40 */ - 84, 19, 19, 46, 46, 46, 46, 46, /* 40 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 40 */ - 46, 80, 80, 80, 46, 40, 40, 40, /* 41 */ - 40, 40, 40, 40, 40, 46, 40, 40, /* 41 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 41 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 41 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 41 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 41 */ - 40, 40, 40, 40, 46, 40, 40, 40, /* 41 */ - 40, 40, 46, 46, 46, 46, 60, 60, /* 41 */ - 60, 80, 80, 80, 80, 46, 60, 60, /* 42 */ - 60, 46, 60, 60, 60, 60, 46, 46, /* 42 */ - 46, 46, 46, 46, 46, 60, 60, 46, /* 42 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */ - 40, 40, 46, 46, 46, 46, 81, 81, /* 42 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 42 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */ - 46, 46, 80, 80, 46, 40, 40, 40, /* 43 */ - 40, 40, 40, 40, 40, 46, 40, 40, /* 43 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 43 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 43 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 43 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 43 */ - 40, 40, 40, 40, 46, 40, 40, 40, /* 43 */ - 40, 40, 46, 46, 46, 46, 80, 60, /* 43 */ - 80, 80, 80, 80, 80, 46, 60, 80, /* 44 */ - 80, 46, 80, 80, 60, 60, 46, 46, /* 44 */ - 46, 46, 46, 46, 46, 80, 80, 46, /* 44 */ - 46, 46, 46, 46, 46, 46, 40, 46, /* 44 */ - 40, 40, 46, 46, 46, 46, 81, 81, /* 44 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 44 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 44 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 44 */ - 46, 46, 80, 80, 46, 40, 40, 40, /* 45 */ - 40, 40, 40, 40, 40, 46, 40, 40, /* 45 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 45 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 45 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */ - 40, 40, 46, 46, 46, 46, 80, 80, /* 45 */ - 80, 60, 60, 60, 46, 46, 80, 80, /* 46 */ - 80, 46, 80, 80, 80, 60, 46, 46, /* 46 */ - 46, 46, 46, 46, 46, 46, 46, 80, /* 46 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */ - 40, 40, 46, 46, 46, 46, 81, 81, /* 46 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 46 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */ - 46, 40, 40, 40, 40, 40, 40, 40, /* 47 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ - 40, 40, 40, 40, 40, 40, 40, 3, /* 47 */ - 40, 60, 40, 40, 60, 60, 60, 60, /* 47 */ - 60, 60, 60, 46, 46, 46, 46, 4, /* 47 */ - 40, 40, 40, 40, 40, 40, 59, 60, /* 48 */ - 60, 60, 60, 60, 60, 60, 60, 15, /* 48 */ - 9, 9, 9, 9, 9, 9, 9, 9, /* 48 */ - 9, 9, 3, 3, 46, 46, 46, 46, /* 48 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ - 46, 40, 40, 46, 40, 46, 46, 40, /* 49 */ - 40, 46, 40, 46, 46, 40, 46, 46, /* 49 */ - 46, 46, 46, 46, 40, 40, 40, 40, /* 49 */ - 46, 40, 40, 40, 40, 40, 40, 40, /* 49 */ - 46, 40, 40, 40, 46, 40, 46, 40, /* 49 */ - 46, 46, 40, 40, 46, 40, 40, 3, /* 49 */ - 40, 60, 40, 40, 60, 60, 60, 60, /* 49 */ - 60, 60, 46, 60, 60, 40, 46, 46, /* 49 */ - 40, 40, 40, 40, 40, 46, 59, 46, /* 50 */ - 60, 60, 60, 60, 60, 60, 46, 46, /* 50 */ - 9, 9, 9, 9, 9, 9, 9, 9, /* 50 */ - 9, 9, 46, 46, 40, 40, 46, 46, /* 50 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ - 15, 15, 15, 15, 3, 3, 3, 3, /* 51 */ - 3, 3, 3, 3, 3, 3, 3, 3, /* 51 */ - 3, 3, 3, 15, 15, 15, 15, 15, /* 51 */ - 60, 60, 15, 15, 15, 15, 15, 15, /* 51 */ - 78, 78, 78, 78, 78, 78, 78, 78, /* 51 */ - 78, 78, 85, 85, 85, 85, 85, 85, /* 51 */ - 85, 85, 85, 85, 15, 60, 15, 60, /* 51 */ - 15, 60, 5, 6, 5, 6, 80, 80, /* 51 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ - 46, 40, 40, 40, 40, 40, 40, 40, /* 52 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ - 40, 40, 46, 46, 46, 46, 46, 46, /* 52 */ - 46, 60, 60, 60, 60, 60, 60, 60, /* 52 */ - 60, 60, 60, 60, 60, 60, 60, 80, /* 52 */ - 60, 60, 60, 60, 60, 3, 60, 60, /* 53 */ - 60, 60, 60, 60, 46, 46, 46, 46, /* 53 */ - 60, 60, 60, 60, 60, 60, 46, 60, /* 53 */ - 46, 60, 60, 60, 60, 60, 60, 60, /* 53 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 53 */ - 60, 60, 60, 60, 60, 60, 46, 46, /* 53 */ - 46, 60, 60, 60, 60, 60, 60, 60, /* 53 */ - 46, 60, 46, 46, 46, 46, 46, 46, /* 53 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ - 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ - 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ - 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ - 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ - 76, 76, 76, 76, 76, 76, 46, 46, /* 55 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 55 */ - 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ - 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ - 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ - 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ - 16, 16, 16, 16, 16, 16, 16, 46, /* 55 */ - 46, 46, 46, 3, 46, 46, 46, 46, /* 55 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ - 40, 40, 46, 46, 46, 46, 46, 40, /* 57 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ - 40, 40, 40, 46, 46, 46, 46, 46, /* 58 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ - 40, 40, 46, 46, 46, 46, 46, 46, /* 59 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ - 23, 24, 23, 24, 23, 24, 16, 16, /* 61 */ - 16, 16, 16, 16, 46, 46, 46, 46, /* 61 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ - 23, 24, 46, 46, 46, 46, 46, 46, /* 62 */ - 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */ - 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */ - 86, 86, 86, 86, 86, 86, 46, 46, /* 63 */ - 87, 87, 87, 87, 87, 87, 46, 46, /* 63 */ - 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */ - 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */ - 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */ - 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */ - 86, 86, 86, 86, 86, 86, 46, 46, /* 64 */ - 87, 87, 87, 87, 87, 87, 46, 46, /* 64 */ - 16, 86, 16, 86, 16, 86, 16, 86, /* 64 */ - 46, 87, 46, 87, 46, 87, 46, 87, /* 64 */ - 86, 86, 86, 86, 86, 86, 86, 86, /* 64 */ - 87, 87, 87, 87, 87, 87, 87, 87, /* 64 */ - 88, 88, 89, 89, 89, 89, 90, 90, /* 64 */ - 91, 91, 92, 92, 93, 93, 46, 46, /* 64 */ - 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */ - 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */ - 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */ - 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */ - 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */ - 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */ - 86, 86, 16, 94, 16, 46, 16, 16, /* 65 */ - 87, 87, 95, 95, 96, 11, 38, 11, /* 65 */ - 11, 11, 16, 94, 16, 46, 16, 16, /* 66 */ - 97, 97, 97, 97, 96, 11, 11, 11, /* 66 */ - 86, 86, 16, 16, 46, 46, 16, 16, /* 66 */ - 87, 87, 98, 98, 46, 11, 11, 11, /* 66 */ - 86, 86, 16, 16, 16, 99, 16, 16, /* 66 */ - 87, 87, 100, 100, 101, 11, 11, 11, /* 66 */ - 46, 46, 16, 94, 16, 46, 16, 16, /* 66 */ -102, 102, 103, 103, 96, 11, 11, 46, /* 66 */ - 2, 2, 2, 2, 2, 2, 2, 2, /* 67 */ - 2, 2, 2, 2, 104, 104, 104, 104, /* 67 */ - 8, 8, 8, 8, 8, 8, 3, 3, /* 67 */ - 5, 6, 5, 5, 5, 6, 5, 5, /* 67 */ - 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */ -105, 106, 104, 104, 104, 104, 104, 46, /* 67 */ - 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */ - 3, 5, 6, 3, 3, 3, 3, 12, /* 67 */ - 12, 3, 3, 3, 7, 5, 6, 46, /* 68 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ - 46, 46, 104, 104, 104, 104, 104, 104, /* 68 */ - 17, 46, 46, 46, 17, 17, 17, 17, /* 68 */ - 17, 17, 7, 7, 7, 5, 6, 16, /* 68 */ -107, 107, 107, 107, 107, 107, 107, 107, /* 69 */ -107, 107, 7, 7, 7, 5, 6, 46, /* 69 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ - 4, 4, 4, 4, 4, 4, 4, 4, /* 69 */ - 4, 4, 4, 4, 46, 46, 46, 46, /* 69 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 70 */ - 60, 60, 60, 60, 60, 79, 79, 79, /* 70 */ - 79, 60, 46, 46, 46, 46, 46, 46, /* 70 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ - 15, 15, 38, 15, 15, 15, 15, 38, /* 71 */ - 15, 15, 16, 38, 38, 38, 16, 16, /* 71 */ - 38, 38, 38, 16, 15, 38, 15, 15, /* 71 */ - 38, 38, 38, 38, 38, 38, 15, 15, /* 71 */ - 15, 15, 15, 15, 38, 15, 38, 15, /* 71 */ - 38, 15, 38, 38, 38, 38, 16, 16, /* 71 */ - 38, 38, 15, 38, 16, 40, 40, 40, /* 71 */ - 40, 46, 46, 46, 46, 46, 46, 46, /* 71 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 72 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 72 */ - 46, 46, 46, 19, 19, 19, 19, 19, /* 72 */ - 19, 19, 19, 19, 19, 19, 19, 108, /* 72 */ -109, 109, 109, 109, 109, 109, 109, 109, /* 72 */ -109, 109, 109, 109, 110, 110, 110, 110, /* 72 */ -111, 111, 111, 111, 111, 111, 111, 111, /* 72 */ -111, 111, 111, 111, 112, 112, 112, 112, /* 72 */ -113, 113, 113, 46, 46, 46, 46, 46, /* 73 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 73 */ - 7, 7, 7, 7, 7, 15, 15, 15, /* 73 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ - 15, 15, 7, 15, 7, 15, 15, 15, /* 74 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ - 15, 15, 15, 46, 46, 46, 46, 46, /* 74 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 74 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 74 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ - 7, 7, 46, 46, 46, 46, 46, 46, /* 76 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 76 */ - 15, 46, 15, 15, 15, 15, 15, 15, /* 77 */ - 7, 7, 7, 7, 15, 15, 15, 15, /* 77 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ - 7, 7, 15, 15, 15, 15, 15, 15, /* 77 */ - 15, 5, 6, 15, 15, 15, 15, 15, /* 77 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ - 15, 15, 15, 46, 46, 46, 46, 46, /* 78 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ - 15, 15, 15, 15, 15, 46, 46, 46, /* 79 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 80 */ - 15, 15, 15, 46, 46, 46, 46, 46, /* 80 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 80 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 80 */ -114, 114, 114, 114, 114, 114, 114, 114, /* 80 */ -114, 114, 114, 114, 114, 114, 114, 114, /* 80 */ -114, 114, 114, 114, 82, 82, 82, 82, /* 80 */ - 82, 82, 82, 82, 82, 82, 82, 82, /* 80 */ - 82, 82, 82, 82, 82, 82, 82, 82, /* 81 */ -115, 115, 115, 115, 115, 115, 115, 115, /* 81 */ -115, 115, 115, 115, 115, 115, 115, 115, /* 81 */ -115, 115, 115, 115, 15, 15, 15, 15, /* 81 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 81 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 81 */ - 15, 15, 15, 15, 15, 15, 116, 116, /* 81 */ -116, 116, 116, 116, 116, 116, 116, 116, /* 81 */ -116, 116, 116, 116, 116, 116, 116, 116, /* 82 */ -116, 116, 116, 116, 116, 116, 116, 116, /* 82 */ -117, 117, 117, 117, 117, 117, 117, 117, /* 82 */ -117, 117, 117, 117, 117, 117, 117, 117, /* 82 */ -117, 117, 117, 117, 117, 117, 117, 117, /* 82 */ -117, 117, 118, 46, 46, 46, 46, 46, /* 82 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 82 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 82 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ - 15, 15, 15, 15, 15, 15, 46, 46, /* 84 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 84 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 85 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 85 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ - 15, 15, 15, 15, 46, 46, 46, 46, /* 86 */ - 46, 46, 15, 15, 15, 15, 15, 15, /* 86 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ - 46, 15, 15, 15, 15, 46, 15, 15, /* 87 */ - 15, 15, 46, 46, 15, 15, 15, 15, /* 87 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ - 46, 15, 15, 15, 15, 15, 15, 15, /* 87 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 88 */ - 15, 15, 15, 15, 46, 15, 46, 15, /* 88 */ - 15, 15, 15, 46, 46, 46, 15, 46, /* 88 */ - 15, 15, 15, 15, 15, 15, 15, 46, /* 88 */ - 46, 15, 15, 15, 15, 15, 15, 15, /* 88 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 88 */ - 46, 46, 46, 46, 46, 46, 119, 119, /* 88 */ -119, 119, 119, 119, 119, 119, 119, 119, /* 88 */ -114, 114, 114, 114, 114, 114, 114, 114, /* 89 */ -114, 114, 83, 83, 83, 83, 83, 83, /* 89 */ - 83, 83, 83, 83, 15, 46, 46, 46, /* 89 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */ - 46, 15, 15, 15, 15, 15, 15, 15, /* 89 */ - 15, 15, 15, 15, 15, 15, 15, 46, /* 89 */ - 2, 3, 3, 3, 15, 59, 3, 120, /* 90 */ - 5, 6, 5, 6, 5, 6, 5, 6, /* 90 */ - 5, 6, 15, 15, 5, 6, 5, 6, /* 90 */ - 5, 6, 5, 6, 8, 5, 6, 5, /* 90 */ - 15, 121, 121, 121, 121, 121, 121, 121, /* 90 */ -121, 121, 60, 60, 60, 60, 60, 60, /* 90 */ - 8, 59, 59, 59, 59, 59, 15, 15, /* 90 */ - 46, 46, 46, 46, 46, 46, 46, 15, /* 90 */ - 46, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ - 40, 40, 40, 40, 40, 46, 46, 46, /* 92 */ - 46, 60, 60, 59, 59, 59, 59, 46, /* 92 */ - 46, 40, 40, 40, 40, 40, 40, 40, /* 92 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ - 40, 40, 40, 3, 59, 59, 59, 46, /* 93 */ - 46, 46, 46, 46, 46, 40, 40, 40, /* 94 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ - 40, 40, 40, 40, 40, 46, 46, 46, /* 94 */ - 46, 40, 40, 40, 40, 40, 40, 40, /* 94 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 95 */ - 40, 40, 40, 40, 40, 40, 40, 46, /* 95 */ - 15, 15, 85, 85, 85, 85, 15, 15, /* 95 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 95 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ - 15, 15, 15, 15, 15, 46, 46, 46, /* 96 */ - 85, 85, 85, 85, 85, 85, 85, 85, /* 96 */ - 85, 85, 15, 15, 15, 15, 15, 15, /* 96 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ - 15, 15, 15, 15, 46, 46, 46, 46, /* 97 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */ - 15, 15, 15, 15, 46, 46, 46, 15, /* 97 */ -114, 114, 114, 114, 114, 114, 114, 114, /* 98 */ -114, 114, 15, 15, 15, 15, 15, 15, /* 98 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ - 15, 46, 46, 46, 46, 46, 46, 46, /* 98 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 98 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ - 15, 15, 15, 15, 46, 46, 46, 46, /* 99 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ - 15, 15, 15, 15, 15, 15, 15, 46, /* 99 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ - 15, 15, 15, 15, 15, 15, 15, 46, /* 100 */ - 46, 46, 46, 15, 15, 15, 15, 15, /* 100 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ - 15, 15, 15, 15, 15, 15, 46, 46, /* 101 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ - 15, 15, 15, 15, 15, 15, 15, 46, /* 101 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ - 40, 40, 40, 40, 40, 40, 46, 46, /* 102 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ - 40, 40, 40, 40, 46, 46, 46, 46, /* 103 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ - 40, 40, 40, 40, 40, 40, 46, 46, /* 106 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 106 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 106 */ - 16, 16, 16, 16, 16, 16, 16, 46, /* 107 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 107 */ - 46, 46, 46, 16, 16, 16, 16, 16, /* 107 */ - 46, 46, 46, 46, 46, 46, 60, 40, /* 107 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 107 */ - 40, 7, 40, 40, 40, 40, 40, 40, /* 107 */ - 40, 40, 40, 40, 40, 40, 40, 46, /* 107 */ - 40, 40, 40, 40, 40, 46, 40, 46, /* 107 */ - 40, 40, 46, 40, 40, 46, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ - 40, 40, 46, 46, 46, 46, 46, 46, /* 109 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 109 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 110 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 110 */ - 46, 46, 46, 40, 40, 40, 40, 40, /* 110 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ - 40, 40, 40, 40, 40, 40, 5, 6, /* 111 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 112 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 112 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ - 46, 46, 40, 40, 40, 40, 40, 40, /* 113 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 114 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 114 */ - 40, 40, 40, 40, 46, 46, 46, 46, /* 114 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ - 60, 60, 60, 60, 46, 46, 46, 46, /* 115 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ - 3, 8, 8, 12, 12, 5, 6, 5, /* 115 */ - 6, 5, 6, 5, 6, 5, 6, 5, /* 115 */ - 6, 5, 6, 5, 6, 46, 46, 46, /* 116 */ - 46, 3, 3, 3, 3, 12, 12, 12, /* 116 */ - 3, 3, 3, 46, 3, 3, 3, 3, /* 116 */ - 8, 5, 6, 5, 6, 5, 6, 3, /* 116 */ - 3, 3, 7, 8, 7, 7, 7, 46, /* 116 */ - 3, 4, 3, 3, 46, 46, 46, 46, /* 116 */ - 40, 40, 40, 46, 40, 46, 40, 40, /* 116 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 116 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ - 40, 40, 40, 40, 40, 46, 46, 104, /* 117 */ - 46, 3, 3, 3, 4, 3, 3, 3, /* 118 */ - 5, 6, 3, 7, 3, 8, 3, 3, /* 118 */ - 9, 9, 9, 9, 9, 9, 9, 9, /* 118 */ - 9, 9, 3, 3, 7, 7, 7, 3, /* 118 */ - 3, 10, 10, 10, 10, 10, 10, 10, /* 118 */ - 10, 10, 10, 10, 10, 10, 10, 10, /* 118 */ - 10, 10, 10, 10, 10, 10, 10, 10, /* 118 */ - 10, 10, 10, 5, 3, 6, 11, 12, /* 118 */ - 11, 13, 13, 13, 13, 13, 13, 13, /* 119 */ - 13, 13, 13, 13, 13, 13, 13, 13, /* 119 */ - 13, 13, 13, 13, 13, 13, 13, 13, /* 119 */ - 13, 13, 13, 5, 7, 6, 7, 46, /* 119 */ - 46, 3, 5, 6, 3, 3, 40, 40, /* 119 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 119 */ - 59, 40, 40, 40, 40, 40, 40, 40, /* 119 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 119 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ - 40, 40, 40, 40, 40, 40, 59, 59, /* 120 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ - 40, 40, 40, 40, 40, 40, 40, 46, /* 120 */ - 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */ - 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */ - 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */ - 46, 46, 40, 40, 40, 46, 46, 46, /* 121 */ - 4, 4, 7, 11, 15, 4, 4, 46, /* 121 */ - 7, 7, 7, 7, 7, 15, 15, 46, /* 121 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 121 */ - 46, 46, 46, 46, 46, 15, 46, 46 /* 121 */ -}; - -/* The A table has 124 entries for a total of 496 bytes. */ - -const uint32 js_A[] = { -0x0001000F, /* 0 Cc, ignorable */ -0x0004000F, /* 1 Cc, whitespace */ -0x0004000C, /* 2 Zs, whitespace */ -0x00000018, /* 3 Po */ -0x0006001A, /* 4 Sc, currency */ -0x00000015, /* 5 Ps */ -0x00000016, /* 6 Pe */ -0x00000019, /* 7 Sm */ -0x00000014, /* 8 Pd */ -0x00036089, /* 9 Nd, identifier part, decimal 16 */ -0x0827FF81, /* 10 Lu, hasLower (add 32), identifier start, supradecimal 31 */ -0x0000001B, /* 11 Sk */ -0x00050017, /* 12 Pc, underscore */ -0x0817FF82, /* 13 Ll, hasUpper (subtract 32), identifier start, supradecimal 31 */ -0x0000000C, /* 14 Zs */ -0x0000001C, /* 15 So */ -0x00070182, /* 16 Ll, identifier start */ -0x0000600B, /* 17 No, decimal 16 */ -0x0000500B, /* 18 No, decimal 8 */ -0x0000800B, /* 19 No, strange */ -0x08270181, /* 20 Lu, hasLower (add 32), identifier start */ -0x08170182, /* 21 Ll, hasUpper (subtract 32), identifier start */ -0xE1D70182, /* 22 Ll, hasUpper (subtract -121), identifier start */ -0x00670181, /* 23 Lu, hasLower (add 1), identifier start */ -0x00570182, /* 24 Ll, hasUpper (subtract 1), identifier start */ -0xCE670181, /* 25 Lu, hasLower (add -199), identifier start */ -0x3A170182, /* 26 Ll, hasUpper (subtract 232), identifier start */ -0xE1E70181, /* 27 Lu, hasLower (add -121), identifier start */ -0x4B170182, /* 28 Ll, hasUpper (subtract 300), identifier start */ -0x34A70181, /* 29 Lu, hasLower (add 210), identifier start */ -0x33A70181, /* 30 Lu, hasLower (add 206), identifier start */ -0x33670181, /* 31 Lu, hasLower (add 205), identifier start */ -0x32A70181, /* 32 Lu, hasLower (add 202), identifier start */ -0x32E70181, /* 33 Lu, hasLower (add 203), identifier start */ -0x33E70181, /* 34 Lu, hasLower (add 207), identifier start */ -0x34E70181, /* 35 Lu, hasLower (add 211), identifier start */ -0x34670181, /* 36 Lu, hasLower (add 209), identifier start */ -0x35670181, /* 37 Lu, hasLower (add 213), identifier start */ -0x00070181, /* 38 Lu, identifier start */ -0x36A70181, /* 39 Lu, hasLower (add 218), identifier start */ -0x00070185, /* 40 Lo, identifier start */ -0x36670181, /* 41 Lu, hasLower (add 217), identifier start */ -0x36E70181, /* 42 Lu, hasLower (add 219), identifier start */ -0x00AF0181, /* 43 Lu, hasLower (add 2), hasTitle, identifier start */ -0x007F0183, /* 44 Lt, hasUpper (subtract 1), hasLower (add 1), hasTitle, identifier start */ -0x009F0182, /* 45 Ll, hasUpper (subtract 2), hasTitle, identifier start */ -0x00000000, /* 46 unassigned */ -0x34970182, /* 47 Ll, hasUpper (subtract 210), identifier start */ -0x33970182, /* 48 Ll, hasUpper (subtract 206), identifier start */ -0x33570182, /* 49 Ll, hasUpper (subtract 205), identifier start */ -0x32970182, /* 50 Ll, hasUpper (subtract 202), identifier start */ -0x32D70182, /* 51 Ll, hasUpper (subtract 203), identifier start */ -0x33D70182, /* 52 Ll, hasUpper (subtract 207), identifier start */ -0x34570182, /* 53 Ll, hasUpper (subtract 209), identifier start */ -0x34D70182, /* 54 Ll, hasUpper (subtract 211), identifier start */ -0x35570182, /* 55 Ll, hasUpper (subtract 213), identifier start */ -0x36970182, /* 56 Ll, hasUpper (subtract 218), identifier start */ -0x36570182, /* 57 Ll, hasUpper (subtract 217), identifier start */ -0x36D70182, /* 58 Ll, hasUpper (subtract 219), identifier start */ -0x00070084, /* 59 Lm, identifier start */ -0x00030086, /* 60 Mn, identifier part */ -0x09A70181, /* 61 Lu, hasLower (add 38), identifier start */ -0x09670181, /* 62 Lu, hasLower (add 37), identifier start */ -0x10270181, /* 63 Lu, hasLower (add 64), identifier start */ -0x0FE70181, /* 64 Lu, hasLower (add 63), identifier start */ -0x09970182, /* 65 Ll, hasUpper (subtract 38), identifier start */ -0x09570182, /* 66 Ll, hasUpper (subtract 37), identifier start */ -0x10170182, /* 67 Ll, hasUpper (subtract 64), identifier start */ -0x0FD70182, /* 68 Ll, hasUpper (subtract 63), identifier start */ -0x0F970182, /* 69 Ll, hasUpper (subtract 62), identifier start */ -0x0E570182, /* 70 Ll, hasUpper (subtract 57), identifier start */ -0x0BD70182, /* 71 Ll, hasUpper (subtract 47), identifier start */ -0x0D970182, /* 72 Ll, hasUpper (subtract 54), identifier start */ -0x15970182, /* 73 Ll, hasUpper (subtract 86), identifier start */ -0x14170182, /* 74 Ll, hasUpper (subtract 80), identifier start */ -0x14270181, /* 75 Lu, hasLower (add 80), identifier start */ -0x0C270181, /* 76 Lu, hasLower (add 48), identifier start */ -0x0C170182, /* 77 Ll, hasUpper (subtract 48), identifier start */ -0x00034089, /* 78 Nd, identifier part, decimal 0 */ -0x00000087, /* 79 Me */ -0x00030088, /* 80 Mc, identifier part */ -0x00037489, /* 81 Nd, identifier part, decimal 26 */ -0x00005A0B, /* 82 No, decimal 13 */ -0x00006E0B, /* 83 No, decimal 23 */ -0x0000740B, /* 84 No, decimal 26 */ -0x0000000B, /* 85 No */ -0xFE170182, /* 86 Ll, hasUpper (subtract -8), identifier start */ -0xFE270181, /* 87 Lu, hasLower (add -8), identifier start */ -0xED970182, /* 88 Ll, hasUpper (subtract -74), identifier start */ -0xEA970182, /* 89 Ll, hasUpper (subtract -86), identifier start */ -0xE7170182, /* 90 Ll, hasUpper (subtract -100), identifier start */ -0xE0170182, /* 91 Ll, hasUpper (subtract -128), identifier start */ -0xE4170182, /* 92 Ll, hasUpper (subtract -112), identifier start */ -0xE0970182, /* 93 Ll, hasUpper (subtract -126), identifier start */ -0xFDD70182, /* 94 Ll, hasUpper (subtract -9), identifier start */ -0xEDA70181, /* 95 Lu, hasLower (add -74), identifier start */ -0xFDE70181, /* 96 Lu, hasLower (add -9), identifier start */ -0xEAA70181, /* 97 Lu, hasLower (add -86), identifier start */ -0xE7270181, /* 98 Lu, hasLower (add -100), identifier start */ -0xFE570182, /* 99 Ll, hasUpper (subtract -7), identifier start */ -0xE4270181, /* 100 Lu, hasLower (add -112), identifier start */ -0xFE670181, /* 101 Lu, hasLower (add -7), identifier start */ -0xE0270181, /* 102 Lu, hasLower (add -128), identifier start */ -0xE0A70181, /* 103 Lu, hasLower (add -126), identifier start */ -0x00010010, /* 104 Cf, ignorable */ -0x0004000D, /* 105 Zl, whitespace */ -0x0004000E, /* 106 Zp, whitespace */ -0x0000400B, /* 107 No, decimal 0 */ -0x0000440B, /* 108 No, decimal 2 */ -0x0427438A, /* 109 Nl, hasLower (add 16), identifier start, decimal 1 */ -0x0427818A, /* 110 Nl, hasLower (add 16), identifier start, strange */ -0x0417638A, /* 111 Nl, hasUpper (subtract 16), identifier start, decimal 17 */ -0x0417818A, /* 112 Nl, hasUpper (subtract 16), identifier start, strange */ -0x0007818A, /* 113 Nl, identifier start, strange */ -0x0000420B, /* 114 No, decimal 1 */ -0x0000720B, /* 115 No, decimal 25 */ -0x06A0001C, /* 116 So, hasLower (add 26) */ -0x0690001C, /* 117 So, hasUpper (subtract 26) */ -0x00006C0B, /* 118 No, decimal 22 */ -0x0000560B, /* 119 No, decimal 11 */ -0x0007738A, /* 120 Nl, identifier start, decimal 25 */ -0x0007418A, /* 121 Nl, identifier start, decimal 0 */ -0x00000013, /* 122 Cs */ -0x00000012 /* 123 Co */ -}; - -const jschar js_uriReservedPlusPound_ucstr[] = - {';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '#', 0}; -const jschar js_uriUnescaped_ucstr[] = - {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '-', '_', '.', '!', '~', '*', '\'', '(', ')', 0}; - -#define URI_CHUNK 64U - -/* Concatenate jschars onto an unshared/newborn JSString. */ -static JSBool -AddCharsToURI(JSContext *cx, JSString *str, const jschar *chars, size_t length) -{ - size_t total; - - JS_ASSERT(!JSSTRING_IS_DEPENDENT(str)); - total = str->length + length + 1; - if (!str->chars || - JS_HOWMANY(total, URI_CHUNK) > JS_HOWMANY(str->length + 1, URI_CHUNK)) { - total = JS_ROUNDUP(total, URI_CHUNK); - str->chars = JS_realloc(cx, str->chars, total * sizeof(jschar)); - if (!str->chars) - return JS_FALSE; - } - js_strncpy(str->chars + str->length, chars, length); - str->length += length; - str->chars[str->length] = 0; - return JS_TRUE; -} - -/* - * ECMA 3, 15.1.3 URI Handling Function Properties - * - * The following are implementations of the algorithms - * given in the ECMA specification for the hidden functions - * 'Encode' and 'Decode'. - */ -static JSBool -Encode(JSContext *cx, JSString *str, const jschar *unescapedSet, - const jschar *unescapedSet2, jsval *rval) -{ - size_t length, j, k, L; - jschar *chars, c, c2; - uint32 v; - uint8 utf8buf[6]; - jschar hexBuf[4]; - static const char HexDigits[] = "0123456789ABCDEF"; /* NB: uppercase */ - JSString *R; - - length = JSSTRING_LENGTH(str); - if (length == 0) { - *rval = STRING_TO_JSVAL(cx->runtime->emptyString); - return JS_TRUE; - } - - R = js_NewString(cx, NULL, 0, 0); - if (!R) - return JS_FALSE; - - hexBuf[0] = '%'; - hexBuf[3] = 0; - chars = JSSTRING_CHARS(str); - for (k = 0; k < length; k++) { - c = chars[k]; - if (js_strchr(unescapedSet, c) || - (unescapedSet2 && js_strchr(unescapedSet2, c))) { - if (!AddCharsToURI(cx, R, &c, 1)) - return JS_FALSE; - } else { - if ((c >= 0xDC00) && (c <= 0xDFFF)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_URI, NULL); - return JS_FALSE; - } - if (c < 0xD800 || c > 0xDBFF) { - v = c; - } else { - k++; - if (k == length) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_URI, NULL); - return JS_FALSE; - } - c2 = chars[k]; - if ((c2 < 0xDC00) || (c2 > 0xDFFF)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_URI, NULL); - return JS_FALSE; - } - v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; - } - L = js_OneUcs4ToUtf8Char(utf8buf, v); - for (j = 0; j < L; j++) { - hexBuf[1] = HexDigits[utf8buf[j] >> 4]; - hexBuf[2] = HexDigits[utf8buf[j] & 0xf]; - if (!AddCharsToURI(cx, R, hexBuf, 3)) - return JS_FALSE; - } - } - } - - /* - * Shrinking realloc can fail (e.g., with a BSD-style allocator), but we - * don't worry about that case here. Worst case, R hangs onto URI_CHUNK-1 - * more jschars than it needs. - */ - chars = (jschar *) JS_realloc(cx, R->chars, (R->length+1) * sizeof(jschar)); - if (chars) - R->chars = chars; - *rval = STRING_TO_JSVAL(R); - return JS_TRUE; -} - -static JSBool -Decode(JSContext *cx, JSString *str, const jschar *reservedSet, jsval *rval) -{ - size_t length, start, k; - jschar *chars, c, H; - uint32 v; - jsuint B; - uint8 octets[6]; - JSString *R; - intN j, n; - - length = JSSTRING_LENGTH(str); - if (length == 0) { - *rval = STRING_TO_JSVAL(cx->runtime->emptyString); - return JS_TRUE; - } - - R = js_NewString(cx, NULL, 0, 0); - if (!R) - return JS_FALSE; - - chars = JSSTRING_CHARS(str); - for (k = 0; k < length; k++) { - c = chars[k]; - if (c == '%') { - start = k; - if ((k + 2) >= length) - goto bad; - if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2])) - goto bad; - B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]); - k += 2; - if (!(B & 0x80)) { - c = (jschar)B; - } else { - n = 1; - while (B & (0x80 >> n)) - n++; - if (n == 1 || n > 6) - goto bad; - octets[0] = (uint8)B; - if (k + 3 * (n - 1) >= length) - goto bad; - for (j = 1; j < n; j++) { - k++; - if (chars[k] != '%') - goto bad; - if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2])) - goto bad; - B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]); - if ((B & 0xC0) != 0x80) - goto bad; - k += 2; - octets[j] = (char)B; - } - v = Utf8ToOneUcs4Char(octets, n); - if (v >= 0x10000) { - v -= 0x10000; - if (v > 0xFFFFF) - goto bad; - c = (jschar)((v & 0x3FF) + 0xDC00); - H = (jschar)((v >> 10) + 0xD800); - if (!AddCharsToURI(cx, R, &H, 1)) - return JS_FALSE; - } else { - c = (jschar)v; - } - } - if (js_strchr(reservedSet, c)) { - if (!AddCharsToURI(cx, R, &chars[start], (k - start + 1))) - return JS_FALSE; - } else { - if (!AddCharsToURI(cx, R, &c, 1)) - return JS_FALSE; - } - } else { - if (!AddCharsToURI(cx, R, &c, 1)) - return JS_FALSE; - } - } - - /* - * Shrinking realloc can fail (e.g., with a BSD-style allocator), but we - * don't worry about that case here. Worst case, R hangs onto URI_CHUNK-1 - * more jschars than it needs. - */ - chars = (jschar *) JS_realloc(cx, R->chars, (R->length+1) * sizeof(jschar)); - if (chars) - R->chars = chars; - *rval = STRING_TO_JSVAL(R); - return JS_TRUE; - -bad: - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_URI); - return JS_FALSE; -} - -static JSBool -str_decodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str); - return Decode(cx, str, js_uriReservedPlusPound_ucstr, rval); -} - -static JSBool -str_decodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str); - return Decode(cx, str, js_empty_ucstr, rval); -} - -static JSBool -str_encodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str); - return Encode(cx, str, js_uriReservedPlusPound_ucstr, js_uriUnescaped_ucstr, - rval); -} - -static JSBool -str_encodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str); - return Encode(cx, str, js_uriUnescaped_ucstr, NULL, rval); -} - -/* - * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at - * least 6 bytes long. Return the number of UTF-8 bytes of data written. - */ -int -js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char) -{ - int utf8Length = 1; - - JS_ASSERT(ucs4Char <= 0x7FFFFFFF); - if (ucs4Char < 0x80) { - *utf8Buffer = (uint8)ucs4Char; - } else { - int i; - uint32 a = ucs4Char >> 11; - utf8Length = 2; - while (a) { - a >>= 5; - utf8Length++; - } - i = utf8Length; - while (--i) { - utf8Buffer[i] = (uint8)((ucs4Char & 0x3F) | 0x80); - ucs4Char >>= 6; - } - *utf8Buffer = (uint8)(0x100 - (1 << (8-utf8Length)) + ucs4Char); - } - return utf8Length; -} - -/* - * Convert a utf8 character sequence into a UCS-4 character and return that - * character. It is assumed that the caller already checked that the sequence - * is valid. - */ -static uint32 -Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length) -{ - uint32 ucs4Char; - uint32 minucs4Char; - /* from Unicode 3.1, non-shortest form is illegal */ - static const uint32 minucs4Table[] = { - 0x00000080, 0x00000800, 0x0001000, 0x0020000, 0x0400000 - }; - - JS_ASSERT(utf8Length >= 1 && utf8Length <= 6); - if (utf8Length == 1) { - ucs4Char = *utf8Buffer; - JS_ASSERT(!(ucs4Char & 0x80)); - } else { - JS_ASSERT((*utf8Buffer & (0x100 - (1 << (7-utf8Length)))) == - (0x100 - (1 << (8-utf8Length)))); - ucs4Char = *utf8Buffer++ & ((1<<(7-utf8Length))-1); - minucs4Char = minucs4Table[utf8Length-2]; - while (--utf8Length) { - JS_ASSERT((*utf8Buffer & 0xC0) == 0x80); - ucs4Char = ucs4Char<<6 | (*utf8Buffer++ & 0x3F); - } - if (ucs4Char < minucs4Char || - ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) { - ucs4Char = 0xFFFD; - } - } - return ucs4Char; -} diff --git a/spidermonkey/src/jsstr.h b/spidermonkey/src/jsstr.h deleted file mode 100644 index 708c69a..0000000 --- a/spidermonkey/src/jsstr.h +++ /dev/null @@ -1,500 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsstr_h___ -#define jsstr_h___ -/* - * JS string type implementation. - * - * A JS string is a counted array of unicode characters. To support handoff - * of API client memory, the chars are allocated separately from the length, - * necessitating a pointer after the count, to form a separately allocated - * string descriptor. String descriptors are GC'ed, while their chars are - * allocated from the malloc heap. - * - * When a string is treated as an object (by following it with . or []), the - * runtime wraps it with a JSObject whose valueOf method returns the unwrapped - * string descriptor. - */ -#include -#include "jspubtd.h" -#include "jsprvtd.h" -#include "jshash.h" - -JS_BEGIN_EXTERN_C - -/* - * The original GC-thing "string" type, a flat character string owned by its - * GC-thing descriptor. The chars member points to a vector having byte size - * (length + 1) * sizeof(jschar), terminated at index length by a zero jschar. - * The terminator is purely a backstop, in case the chars pointer flows out to - * native code that requires \u0000 termination. - * - * NB: Always use the JSSTRING_LENGTH and JSSTRING_CHARS accessor macros, - * unless you guard str->member uses with !JSSTRING_IS_DEPENDENT(str). - */ -struct JSString { - size_t length; - jschar *chars; -}; - -/* - * Overlay structure for a string that depends on another string's characters. - * Distinguished by the JSSTRFLAG_DEPENDENT bit being set in length. The base - * member may point to another dependent string if JSSTRING_CHARS has not been - * called yet. The length chars in a dependent string are stored starting at - * base->chars + start, and are not necessarily zero-terminated. If start is - * 0, it is not stored, length is a full size_t (minus the JSSTRFLAG_* bits in - * the high two positions), and the JSSTRFLAG_PREFIX flag is set. - */ -struct JSDependentString { - size_t length; - JSString *base; -}; - -/* Definitions for flags stored in the high order bits of JSString.length. */ -#define JSSTRFLAG_BITS 2 -#define JSSTRFLAG_SHIFT(flg) ((size_t)(flg) << JSSTRING_LENGTH_BITS) -#define JSSTRFLAG_MASK JSSTRFLAG_SHIFT(JS_BITMASK(JSSTRFLAG_BITS)) -#define JSSTRFLAG_DEPENDENT JSSTRFLAG_SHIFT(1) -#define JSSTRFLAG_PREFIX JSSTRFLAG_SHIFT(2) - -/* Universal JSString type inquiry and accessor macros. */ -#define JSSTRING_BIT(n) ((size_t)1 << (n)) -#define JSSTRING_BITMASK(n) (JSSTRING_BIT(n) - 1) -#define JSSTRING_HAS_FLAG(str,flg) ((str)->length & (flg)) -#define JSSTRING_IS_DEPENDENT(str) JSSTRING_HAS_FLAG(str, JSSTRFLAG_DEPENDENT) -#define JSSTRING_IS_PREFIX(str) JSSTRING_HAS_FLAG(str, JSSTRFLAG_PREFIX) -#define JSSTRING_CHARS(str) (JSSTRING_IS_DEPENDENT(str) \ - ? JSSTRDEP_CHARS(str) \ - : (str)->chars) -#define JSSTRING_LENGTH(str) (JSSTRING_IS_DEPENDENT(str) \ - ? JSSTRDEP_LENGTH(str) \ - : (str)->length) -#define JSSTRING_LENGTH_BITS (sizeof(size_t) * JS_BITS_PER_BYTE \ - - JSSTRFLAG_BITS) -#define JSSTRING_LENGTH_MASK JSSTRING_BITMASK(JSSTRING_LENGTH_BITS) - -/* Specific JSDependentString shift/mask accessor and mutator macros. */ -#define JSSTRDEP_START_BITS (JSSTRING_LENGTH_BITS-JSSTRDEP_LENGTH_BITS) -#define JSSTRDEP_START_SHIFT JSSTRDEP_LENGTH_BITS -#define JSSTRDEP_START_MASK JSSTRING_BITMASK(JSSTRDEP_START_BITS) -#define JSSTRDEP_LENGTH_BITS (JSSTRING_LENGTH_BITS / 2) -#define JSSTRDEP_LENGTH_MASK JSSTRING_BITMASK(JSSTRDEP_LENGTH_BITS) - -#define JSSTRDEP(str) ((JSDependentString *)(str)) -#define JSSTRDEP_START(str) (JSSTRING_IS_PREFIX(str) ? 0 \ - : ((JSSTRDEP(str)->length \ - >> JSSTRDEP_START_SHIFT) \ - & JSSTRDEP_START_MASK)) -#define JSSTRDEP_LENGTH(str) (JSSTRDEP(str)->length \ - & (JSSTRING_IS_PREFIX(str) \ - ? JSSTRING_LENGTH_MASK \ - : JSSTRDEP_LENGTH_MASK)) - -#define JSSTRDEP_SET_START_AND_LENGTH(str,off,len) \ - (JSSTRDEP(str)->length = JSSTRFLAG_DEPENDENT \ - | ((off) << JSSTRDEP_START_SHIFT) \ - | (len)) -#define JSPREFIX_SET_LENGTH(str,len) \ - (JSSTRDEP(str)->length = JSSTRFLAG_DEPENDENT | JSSTRFLAG_PREFIX | (len)) - -#define JSSTRDEP_BASE(str) (JSSTRDEP(str)->base) -#define JSSTRDEP_SET_BASE(str,bstr) (JSSTRDEP(str)->base = (bstr)) -#define JSPREFIX_BASE(str) JSSTRDEP_BASE(str) -#define JSPREFIX_SET_BASE(str,bstr) JSSTRDEP_SET_BASE(str,bstr) - -#define JSSTRDEP_CHARS(str) \ - (JSSTRING_IS_DEPENDENT(JSSTRDEP_BASE(str)) \ - ? js_GetDependentStringChars(str) \ - : JSSTRDEP_BASE(str)->chars + JSSTRDEP_START(str)) - -extern size_t -js_MinimizeDependentStrings(JSString *str, int level, JSString **basep); - -extern jschar * -js_GetDependentStringChars(JSString *str); - -extern jschar * -js_GetStringChars(JSString *str); - -extern JSString * -js_ConcatStrings(JSContext *cx, JSString *left, JSString *right); - -extern const jschar * -js_UndependString(JSContext *cx, JSString *str); - -struct JSSubString { - size_t length; - const jschar *chars; -}; - -extern jschar js_empty_ucstr[]; -extern JSSubString js_EmptySubString; - -/* Unicode character attribute lookup tables. */ -extern const uint8 js_X[]; -extern const uint8 js_Y[]; -extern const uint32 js_A[]; - -/* Enumerated Unicode general category types. */ -typedef enum JSCharType { - JSCT_UNASSIGNED = 0, - JSCT_UPPERCASE_LETTER = 1, - JSCT_LOWERCASE_LETTER = 2, - JSCT_TITLECASE_LETTER = 3, - JSCT_MODIFIER_LETTER = 4, - JSCT_OTHER_LETTER = 5, - JSCT_NON_SPACING_MARK = 6, - JSCT_ENCLOSING_MARK = 7, - JSCT_COMBINING_SPACING_MARK = 8, - JSCT_DECIMAL_DIGIT_NUMBER = 9, - JSCT_LETTER_NUMBER = 10, - JSCT_OTHER_NUMBER = 11, - JSCT_SPACE_SEPARATOR = 12, - JSCT_LINE_SEPARATOR = 13, - JSCT_PARAGRAPH_SEPARATOR = 14, - JSCT_CONTROL = 15, - JSCT_FORMAT = 16, - JSCT_PRIVATE_USE = 18, - JSCT_SURROGATE = 19, - JSCT_DASH_PUNCTUATION = 20, - JSCT_START_PUNCTUATION = 21, - JSCT_END_PUNCTUATION = 22, - JSCT_CONNECTOR_PUNCTUATION = 23, - JSCT_OTHER_PUNCTUATION = 24, - JSCT_MATH_SYMBOL = 25, - JSCT_CURRENCY_SYMBOL = 26, - JSCT_MODIFIER_SYMBOL = 27, - JSCT_OTHER_SYMBOL = 28 -} JSCharType; - -/* Character classifying and mapping macros, based on java.lang.Character. */ -#define JS_CCODE(c) (js_A[js_Y[(js_X[(uint16)(c)>>6]<<6)|((c)&0x3F)]]) -#define JS_CTYPE(c) (JS_CCODE(c) & 0x1F) - -#define JS_ISALPHA(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ - (1 << JSCT_LOWERCASE_LETTER) | \ - (1 << JSCT_TITLECASE_LETTER) | \ - (1 << JSCT_MODIFIER_LETTER) | \ - (1 << JSCT_OTHER_LETTER)) \ - >> JS_CTYPE(c)) & 1) - -#define JS_ISALNUM(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ - (1 << JSCT_LOWERCASE_LETTER) | \ - (1 << JSCT_TITLECASE_LETTER) | \ - (1 << JSCT_MODIFIER_LETTER) | \ - (1 << JSCT_OTHER_LETTER) | \ - (1 << JSCT_DECIMAL_DIGIT_NUMBER)) \ - >> JS_CTYPE(c)) & 1) - -/* A unicode letter, suitable for use in an identifier. */ -#define JS_ISLETTER(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ - (1 << JSCT_LOWERCASE_LETTER) | \ - (1 << JSCT_TITLECASE_LETTER) | \ - (1 << JSCT_MODIFIER_LETTER) | \ - (1 << JSCT_OTHER_LETTER) | \ - (1 << JSCT_LETTER_NUMBER)) \ - >> JS_CTYPE(c)) & 1) - -/* - * 'IdentifierPart' from ECMA grammar, is Unicode letter or combining mark or - * digit or connector punctuation. - */ -#define JS_ISIDPART(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ - (1 << JSCT_LOWERCASE_LETTER) | \ - (1 << JSCT_TITLECASE_LETTER) | \ - (1 << JSCT_MODIFIER_LETTER) | \ - (1 << JSCT_OTHER_LETTER) | \ - (1 << JSCT_LETTER_NUMBER) | \ - (1 << JSCT_NON_SPACING_MARK) | \ - (1 << JSCT_COMBINING_SPACING_MARK) | \ - (1 << JSCT_DECIMAL_DIGIT_NUMBER) | \ - (1 << JSCT_CONNECTOR_PUNCTUATION)) \ - >> JS_CTYPE(c)) & 1) - -/* Unicode control-format characters, ignored in input */ -#define JS_ISFORMAT(c) (((1 << JSCT_FORMAT) >> JS_CTYPE(c)) & 1) - -/* - * Per ECMA-262 15.10.2.6, these characters are the only ones that make up a - * "word", as far as a RegExp is concerned. If we want a Unicode-friendlier - * definition of "word", we should rename this macro to something regexp-y. - */ -#define JS_ISWORD(c) ((c) < 128 && (isalnum(c) || (c) == '_')) - -#define JS_ISIDSTART(c) (JS_ISLETTER(c) || (c) == '_' || (c) == '$') -#define JS_ISIDENT(c) (JS_ISIDPART(c) || (c) == '_' || (c) == '$') - -#define JS_ISXMLSPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || \ - (c) == '\n') -#define JS_ISXMLNSSTART(c) ((JS_CCODE(c) & 0x00000100) || (c) == '_') -#define JS_ISXMLNS(c) ((JS_CCODE(c) & 0x00000080) || (c) == '.' || \ - (c) == '-' || (c) == '_') -#define JS_ISXMLNAMESTART(c) (JS_ISXMLNSSTART(c) || (c) == ':') -#define JS_ISXMLNAME(c) (JS_ISXMLNS(c) || (c) == ':') - -#define JS_ISDIGIT(c) (JS_CTYPE(c) == JSCT_DECIMAL_DIGIT_NUMBER) - -/* XXXbe unify on A/X/Y tbls, avoid ctype.h? */ -/* XXXbe fs, etc. ? */ -#define JS_ISSPACE(c) ((JS_CCODE(c) & 0x00070000) == 0x00040000) -#define JS_ISPRINT(c) ((c) < 128 && isprint(c)) - -#define JS_ISUPPER(c) (JS_CTYPE(c) == JSCT_UPPERCASE_LETTER) -#define JS_ISLOWER(c) (JS_CTYPE(c) == JSCT_LOWERCASE_LETTER) - -#define JS_TOUPPER(c) ((jschar) ((JS_CCODE(c) & 0x00100000) \ - ? (c) - ((int32)JS_CCODE(c) >> 22) \ - : (c))) -#define JS_TOLOWER(c) ((jschar) ((JS_CCODE(c) & 0x00200000) \ - ? (c) + ((int32)JS_CCODE(c) >> 22) \ - : (c))) - -/* - * Shorthands for ASCII (7-bit) decimal and hex conversion. - * Manually inline isdigit for performance; MSVC doesn't do this for us. - */ -#define JS7_ISDEC(c) ((((unsigned)(c)) - '0') <= 9) -#define JS7_UNDEC(c) ((c) - '0') -#define JS7_ISHEX(c) ((c) < 128 && isxdigit(c)) -#define JS7_UNHEX(c) (uintN)(JS7_ISDEC(c) ? (c) - '0' : 10 + tolower(c) - 'a') -#define JS7_ISLET(c) ((c) < 128 && isalpha(c)) - -/* Initialize per-runtime string state for the first context in the runtime. */ -extern JSBool -js_InitRuntimeStringState(JSContext *cx); - -extern void -js_FinishRuntimeStringState(JSContext *cx); - -extern void -js_FinishDeflatedStringCache(JSRuntime *rt); - -/* Initialize the String class, returning its prototype object. */ -extern JSClass js_StringClass; - -extern JSObject * -js_InitStringClass(JSContext *cx, JSObject *obj); - -extern const char js_escape_str[]; -extern const char js_unescape_str[]; -extern const char js_uneval_str[]; -extern const char js_decodeURI_str[]; -extern const char js_encodeURI_str[]; -extern const char js_decodeURIComponent_str[]; -extern const char js_encodeURIComponent_str[]; - -/* GC-allocate a string descriptor for the given malloc-allocated chars. */ -extern JSString * -js_NewString(JSContext *cx, jschar *chars, size_t length, uintN gcflag); - -extern JSString * -js_NewDependentString(JSContext *cx, JSString *base, size_t start, - size_t length, uintN gcflag); - -/* Copy a counted string and GC-allocate a descriptor for it. */ -extern JSString * -js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n, uintN gcflag); - -/* Copy a C string and GC-allocate a descriptor for it. */ -extern JSString * -js_NewStringCopyZ(JSContext *cx, const jschar *s, uintN gcflag); - -/* Free the chars held by str when it is finalized by the GC. */ -extern void -js_FinalizeString(JSContext *cx, JSString *str); - -extern void -js_FinalizeStringRT(JSRuntime *rt, JSString *str); - -/* Wrap a string value in a String object. */ -extern JSObject * -js_StringToObject(JSContext *cx, JSString *str); - -/* - * Convert a value to a printable C string. - */ -typedef JSString *(*JSValueToStringFun)(JSContext *cx, jsval v); - -extern JS_FRIEND_API(const char *) -js_ValueToPrintable(JSContext *cx, jsval v, JSValueToStringFun v2sfun); - -#define js_ValueToPrintableString(cx,v) \ - js_ValueToPrintable(cx, v, js_ValueToString) - -#define js_ValueToPrintableSource(cx,v) \ - js_ValueToPrintable(cx, v, js_ValueToSource) - -/* - * Convert a value to a string, returning null after reporting an error, - * otherwise returning a new string reference. - */ -extern JS_FRIEND_API(JSString *) -js_ValueToString(JSContext *cx, jsval v); - -/* - * Convert a value to its source expression, returning null after reporting - * an error, otherwise returning a new string reference. - */ -extern JS_FRIEND_API(JSString *) -js_ValueToSource(JSContext *cx, jsval v); - -#ifdef HT_ENUMERATE_NEXT /* XXX don't require jshash.h */ -/* - * Compute a hash function from str. - */ -extern JSHashNumber -js_HashString(JSString *str); -#endif - -/* - * Return less than, equal to, or greater than zero depending on whether - * str1 is less than, equal to, or greater than str2. - */ -extern intN -js_CompareStrings(JSString *str1, JSString *str2); - -/* - * Test if strings are equal. - */ -extern JSBool -js_EqualStrings(JSString *str1, JSString *str2); - -/* - * Boyer-Moore-Horspool superlinear search for pat:patlen in text:textlen. - * The patlen argument must be positive and no greater than BMH_PATLEN_MAX. - * The start argument tells where in text to begin the search. - * - * Return the index of pat in text, or -1 if not found. - */ -#define BMH_CHARSET_SIZE 256 /* ISO-Latin-1 */ -#define BMH_PATLEN_MAX 255 /* skip table element is uint8 */ - -#define BMH_BAD_PATTERN (-2) /* return value if pat is not ISO-Latin-1 */ - -extern jsint -js_BoyerMooreHorspool(const jschar *text, jsint textlen, - const jschar *pat, jsint patlen, - jsint start); - -extern size_t -js_strlen(const jschar *s); - -extern jschar * -js_strchr(const jschar *s, jschar c); - -extern jschar * -js_strchr_limit(const jschar *s, jschar c, const jschar *limit); - -#define js_strncpy(t, s, n) memcpy((t), (s), (n) * sizeof(jschar)) - -/* - * Return s advanced past any Unicode white space characters. - */ -extern const jschar * -js_SkipWhiteSpace(const jschar *s); - -/* - * Inflate bytes to JS chars and vice versa. Report out of memory via cx - * and return null on error, otherwise return the jschar or byte vector that - * was JS_malloc'ed. length is updated with the length of the new string in jschars. - */ -extern jschar * -js_InflateString(JSContext *cx, const char *bytes, size_t *length); - -extern char * -js_DeflateString(JSContext *cx, const jschar *chars, size_t length); - -/* - * Inflate bytes to JS chars into a buffer. - * 'chars' must be large enough for 'length' jschars. - * The buffer is NOT null-terminated. - * cx may be NULL, which means no errors are thrown. - * The destination length needs to be initialized with the buffer size, takes - * the number of chars moved. - */ -extern JSBool -js_InflateStringToBuffer(JSContext* cx, const char *bytes, size_t length, - jschar *chars, size_t* charsLength); - -/* - * Deflate JS chars to bytes into a buffer. - * 'bytes' must be large enough for 'length chars. - * The buffer is NOT null-terminated. - * cx may be NULL, which means no errors are thrown. - * The destination length needs to be initialized with the buffer size, takes - * the number of bytes moved. - */ -extern JSBool -js_DeflateStringToBuffer(JSContext* cx, const jschar *chars, - size_t charsLength, char *bytes, size_t* length); - -/* - * Associate bytes with str in the deflated string cache, returning true on - * successful association, false on out of memory. - */ -extern JSBool -js_SetStringBytes(JSRuntime *rt, JSString *str, char *bytes, size_t length); - -/* - * Find or create a deflated string cache entry for str that contains its - * characters chopped from Unicode code points into bytes. - */ -extern char * -js_GetStringBytes(JSRuntime *rt, JSString *str); - -/* Remove a deflated string cache entry associated with str if any. */ -extern void -js_PurgeDeflatedStringCache(JSRuntime *rt, JSString *str); - -JSBool -js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); - -/* - * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at - * least 6 bytes long. Return the number of UTF-8 bytes of data written. - */ -extern int -js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char); - -JS_END_EXTERN_C - -#endif /* jsstr_h___ */ diff --git a/spidermonkey/src/jstypes.h b/spidermonkey/src/jstypes.h deleted file mode 100644 index 8aca929..0000000 --- a/spidermonkey/src/jstypes.h +++ /dev/null @@ -1,464 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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): - * IBM Corp. - * - * Alternatively, the contents of this file may be used under the terms of - * either of 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 ***** */ - -/* -** File: jstypes.h -** Description: Definitions of NSPR's basic types -** -** Prototypes and macros used to make up for deficiencies in ANSI environments -** that we have found. -** -** Since we do not wrap and all the other standard headers, authors -** of portable code will not know in general that they need these definitions. -** Instead of requiring these authors to find the dependent uses in their code -** and take the following steps only in those C files, we take steps once here -** for all C files. -**/ - -#ifndef jstypes_h___ -#define jstypes_h___ - -#include - -/*********************************************************************** -** MACROS: JS_EXTERN_API -** JS_EXPORT_API -** DESCRIPTION: -** These are only for externally visible routines and globals. For -** internal routines, just use "extern" for type checking and that -** will not export internal cross-file or forward-declared symbols. -** Define a macro for declaring procedures return types. We use this to -** deal with windoze specific type hackery for DLL definitions. Use -** JS_EXTERN_API when the prototype for the method is declared. Use -** JS_EXPORT_API for the implementation of the method. -** -** Example: -** in dowhim.h -** JS_EXTERN_API( void ) DoWhatIMean( void ); -** in dowhim.c -** JS_EXPORT_API( void ) DoWhatIMean( void ) { return; } -** -** -***********************************************************************/ -#ifdef WIN32 -/* These also work for __MWERKS__ */ -#define JS_EXTERN_API(__type) extern __declspec(dllexport) __type -#define JS_EXPORT_API(__type) __declspec(dllexport) __type -#define JS_EXTERN_DATA(__type) extern __declspec(dllexport) __type -#define JS_EXPORT_DATA(__type) __declspec(dllexport) __type - -#define JS_DLL_CALLBACK -#define JS_STATIC_DLL_CALLBACK(__x) static __x - -#elif defined(XP_OS2) && defined(__declspec) - -#define JS_EXTERN_API(__type) extern __declspec(dllexport) __type -#define JS_EXPORT_API(__type) __declspec(dllexport) __type -#define JS_EXTERN_DATA(__type) extern __declspec(dllexport) __type -#define JS_EXPORT_DATA(__type) __declspec(dllexport) __type - -#define JS_DLL_CALLBACK -#define JS_STATIC_DLL_CALLBACK(__x) static __x - -#elif defined(WIN16) - -#ifdef _WINDLL -#define JS_EXTERN_API(__type) extern __type _cdecl _export _loadds -#define JS_EXPORT_API(__type) __type _cdecl _export _loadds -#define JS_EXTERN_DATA(__type) extern __type _export -#define JS_EXPORT_DATA(__type) __type _export - -#define JS_DLL_CALLBACK __cdecl __loadds -#define JS_STATIC_DLL_CALLBACK(__x) static __x CALLBACK - -#else /* this must be .EXE */ -#define JS_EXTERN_API(__type) extern __type _cdecl _export -#define JS_EXPORT_API(__type) __type _cdecl _export -#define JS_EXTERN_DATA(__type) extern __type _export -#define JS_EXPORT_DATA(__type) __type _export - -#define JS_DLL_CALLBACK __cdecl __loadds -#define JS_STATIC_DLL_CALLBACK(__x) __x JS_DLL_CALLBACK -#endif /* _WINDLL */ - -#else /* Unix */ - -#ifdef HAVE_VISIBILITY_ATTRIBUTE -#define JS_EXTERNAL_VIS __attribute__((visibility ("default"))) -#else -#define JS_EXTERNAL_VIS -#endif - -#define JS_EXTERN_API(__type) extern JS_EXTERNAL_VIS __type -#define JS_EXPORT_API(__type) JS_EXTERNAL_VIS __type -#define JS_EXTERN_DATA(__type) extern JS_EXTERNAL_VIS __type -#define JS_EXPORT_DATA(__type) JS_EXTERNAL_VIS __type - -#define JS_DLL_CALLBACK -#define JS_STATIC_DLL_CALLBACK(__x) static __x - -#endif - -#ifdef _WIN32 -# if defined(__MWERKS__) || defined(__GNUC__) -# define JS_IMPORT_API(__x) __x -# else -# define JS_IMPORT_API(__x) __declspec(dllimport) __x -# endif -#elif defined(XP_OS2) && defined(__declspec) -# define JS_IMPORT_API(__x) __declspec(dllimport) __x -#else -# define JS_IMPORT_API(__x) JS_EXPORT_API (__x) -#endif - -#if defined(_WIN32) && !defined(__MWERKS__) -# define JS_IMPORT_DATA(__x) __declspec(dllimport) __x -#elif defined(XP_OS2) && defined(__declspec) -# define JS_IMPORT_DATA(__x) __declspec(dllimport) __x -#else -# define JS_IMPORT_DATA(__x) JS_EXPORT_DATA (__x) -#endif - -/* - * The linkage of JS API functions differs depending on whether the file is - * used within the JS library or not. Any source file within the JS - * interpreter should define EXPORT_JS_API whereas any client of the library - * should not. - */ -#ifdef EXPORT_JS_API -#define JS_PUBLIC_API(t) JS_EXPORT_API(t) -#define JS_PUBLIC_DATA(t) JS_EXPORT_DATA(t) -#else -#define JS_PUBLIC_API(t) JS_IMPORT_API(t) -#define JS_PUBLIC_DATA(t) JS_IMPORT_DATA(t) -#endif - -#define JS_FRIEND_API(t) JS_PUBLIC_API(t) -#define JS_FRIEND_DATA(t) JS_PUBLIC_DATA(t) - -#ifdef _WIN32 -# define JS_INLINE __inline -#elif defined(__GNUC__) -# define JS_INLINE -#else -# define JS_INLINE -#endif - -/*********************************************************************** -** MACROS: JS_BEGIN_MACRO -** JS_END_MACRO -** DESCRIPTION: -** Macro body brackets so that macros with compound statement definitions -** behave syntactically more like functions when called. -***********************************************************************/ -#define JS_BEGIN_MACRO do { -#define JS_END_MACRO } while (0) - -/*********************************************************************** -** MACROS: JS_BEGIN_EXTERN_C -** JS_END_EXTERN_C -** DESCRIPTION: -** Macro shorthands for conditional C++ extern block delimiters. -***********************************************************************/ -#ifdef __cplusplus -#define JS_BEGIN_EXTERN_C extern "C" { -#define JS_END_EXTERN_C } -#else -#define JS_BEGIN_EXTERN_C -#define JS_END_EXTERN_C -#endif - -/*********************************************************************** -** MACROS: JS_BIT -** JS_BITMASK -** DESCRIPTION: -** Bit masking macros. XXX n must be <= 31 to be portable -***********************************************************************/ -#define JS_BIT(n) ((JSUint32)1 << (n)) -#define JS_BITMASK(n) (JS_BIT(n) - 1) - -/*********************************************************************** -** MACROS: JS_PTR_TO_INT32 -** JS_PTR_TO_UINT32 -** JS_INT32_TO_PTR -** JS_UINT32_TO_PTR -** DESCRIPTION: -** Integer to pointer and pointer to integer conversion macros. -***********************************************************************/ -#define JS_PTR_TO_INT32(x) ((jsint)((char *)(x) - (char *)0)) -#define JS_PTR_TO_UINT32(x) ((jsuint)((char *)(x) - (char *)0)) -#define JS_INT32_TO_PTR(x) ((void *)((char *)0 + (jsint)(x))) -#define JS_UINT32_TO_PTR(x) ((void *)((char *)0 + (jsuint)(x))) - -/*********************************************************************** -** MACROS: JS_HOWMANY -** JS_ROUNDUP -** JS_MIN -** JS_MAX -** DESCRIPTION: -** Commonly used macros for operations on compatible types. -***********************************************************************/ -#define JS_HOWMANY(x,y) (((x)+(y)-1)/(y)) -#define JS_ROUNDUP(x,y) (JS_HOWMANY(x,y)*(y)) -#define JS_MIN(x,y) ((x)<(y)?(x):(y)) -#define JS_MAX(x,y) ((x)>(y)?(x):(y)) - -#if (defined(XP_WIN) && !defined(CROSS_COMPILE)) || defined (WINCE) -# include "jscpucfg.h" /* Use standard Mac or Windows configuration */ -#elif defined(XP_UNIX) || defined(XP_BEOS) || defined(XP_OS2) || defined(CROSS_COMPILE) -# include "jsautocfg.h" /* Use auto-detected configuration */ -# include "jsosdep.h" /* ...and platform-specific flags */ -#else -# error "Must define one of XP_BEOS, XP_OS2, XP_WIN or XP_UNIX" -#endif - -JS_BEGIN_EXTERN_C - -/************************************************************************ -** TYPES: JSUint8 -** JSInt8 -** DESCRIPTION: -** The int8 types are known to be 8 bits each. There is no type that -** is equivalent to a plain "char". -************************************************************************/ -#if JS_BYTES_PER_BYTE == 1 -typedef unsigned char JSUint8; -typedef signed char JSInt8; -#else -#error No suitable type for JSInt8/JSUint8 -#endif - -/************************************************************************ -** TYPES: JSUint16 -** JSInt16 -** DESCRIPTION: -** The int16 types are known to be 16 bits each. -************************************************************************/ -#if JS_BYTES_PER_SHORT == 2 -typedef unsigned short JSUint16; -typedef short JSInt16; -#else -#error No suitable type for JSInt16/JSUint16 -#endif - -/************************************************************************ -** TYPES: JSUint32 -** JSInt32 -** DESCRIPTION: -** The int32 types are known to be 32 bits each. -************************************************************************/ -#if JS_BYTES_PER_INT == 4 -typedef unsigned int JSUint32; -typedef int JSInt32; -#define JS_INT32(x) x -#define JS_UINT32(x) x ## U -#elif JS_BYTES_PER_LONG == 4 -typedef unsigned long JSUint32; -typedef long JSInt32; -#define JS_INT32(x) x ## L -#define JS_UINT32(x) x ## UL -#else -#error No suitable type for JSInt32/JSUint32 -#endif - -/************************************************************************ -** TYPES: JSUint64 -** JSInt64 -** DESCRIPTION: -** The int64 types are known to be 64 bits each. Care must be used when -** declaring variables of type JSUint64 or JSInt64. Different hardware -** architectures and even different compilers have varying support for -** 64 bit values. The only guaranteed portability requires the use of -** the JSLL_ macros (see jslong.h). -************************************************************************/ -#ifdef JS_HAVE_LONG_LONG -#if JS_BYTES_PER_LONG == 8 -typedef long JSInt64; -typedef unsigned long JSUint64; -#elif defined(WIN16) -typedef __int64 JSInt64; -typedef unsigned __int64 JSUint64; -#elif defined(WIN32) && !defined(__GNUC__) -typedef __int64 JSInt64; -typedef unsigned __int64 JSUint64; -#else -typedef long long JSInt64; -typedef unsigned long long JSUint64; -#endif /* JS_BYTES_PER_LONG == 8 */ -#else /* !JS_HAVE_LONG_LONG */ -typedef struct { -#ifdef IS_LITTLE_ENDIAN - JSUint32 lo, hi; -#else - JSUint32 hi, lo; -#endif -} JSInt64; -typedef JSInt64 JSUint64; -#endif /* !JS_HAVE_LONG_LONG */ - -/************************************************************************ -** TYPES: JSUintn -** JSIntn -** DESCRIPTION: -** The JSIntn types are most appropriate for automatic variables. They are -** guaranteed to be at least 16 bits, though various architectures may -** define them to be wider (e.g., 32 or even 64 bits). These types are -** never valid for fields of a structure. -************************************************************************/ -#if JS_BYTES_PER_INT >= 2 -typedef int JSIntn; -typedef unsigned int JSUintn; -#else -#error 'sizeof(int)' not sufficient for platform use -#endif - -/************************************************************************ -** TYPES: JSFloat64 -** DESCRIPTION: -** NSPR's floating point type is always 64 bits. -************************************************************************/ -typedef double JSFloat64; - -/************************************************************************ -** TYPES: JSSize -** DESCRIPTION: -** A type for representing the size of objects. -************************************************************************/ -typedef size_t JSSize; - -/************************************************************************ -** TYPES: JSPtrDiff -** DESCRIPTION: -** A type for pointer difference. Variables of this type are suitable -** for storing a pointer or pointer sutraction. -************************************************************************/ -typedef ptrdiff_t JSPtrdiff; - -/************************************************************************ -** TYPES: JSUptrdiff -** DESCRIPTION: -** A type for pointer difference. Variables of this type are suitable -** for storing a pointer or pointer sutraction. -************************************************************************/ -#if JS_BYTES_PER_WORD == 8 && JS_BYTES_PER_LONG != 8 -typedef JSUint64 JSUptrdiff; -#else -typedef unsigned long JSUptrdiff; -#endif - -/************************************************************************ -** TYPES: JSBool -** DESCRIPTION: -** Use JSBool for variables and parameter types. Use JS_FALSE and JS_TRUE -** for clarity of target type in assignments and actual arguments. Use -** 'if (bool)', 'while (!bool)', '(bool) ? x : y' etc., to test booleans -** just as you would C int-valued conditions. -************************************************************************/ -typedef JSIntn JSBool; -#define JS_TRUE (JSIntn)1 -#define JS_FALSE (JSIntn)0 - -/************************************************************************ -** TYPES: JSPackedBool -** DESCRIPTION: -** Use JSPackedBool within structs where bitfields are not desireable -** but minimum and consistent overhead matters. -************************************************************************/ -typedef JSUint8 JSPackedBool; - -/* -** A JSWord is an integer that is the same size as a void* -*/ -#if JS_BYTES_PER_WORD == 8 && JS_BYTES_PER_LONG != 8 -typedef JSInt64 JSWord; -typedef JSUint64 JSUword; -#else -typedef long JSWord; -typedef unsigned long JSUword; -#endif - -#include "jsotypes.h" - -/*********************************************************************** -** MACROS: JS_LIKELY -** JS_UNLIKELY -** DESCRIPTION: -** These macros allow you to give a hint to the compiler about branch -** probability so that it can better optimize. Use them like this: -** -** if (JS_LIKELY(v == 1)) { -** ... expected code path ... -** } -** -** if (JS_UNLIKELY(v == 0)) { -** ... non-expected code path ... -** } -** -***********************************************************************/ -#if defined(__GNUC__) && (__GNUC__ > 2) -#define JS_LIKELY(x) (__builtin_expect((x), 1)) -#define JS_UNLIKELY(x) (__builtin_expect((x), 0)) -#else -#define JS_LIKELY(x) (x) -#define JS_UNLIKELY(x) (x) -#endif - -/*********************************************************************** -** MACROS: JS_ARRAY_LENGTH -** JS_ARRAY_END -** DESCRIPTION: -** Macros to get the number of elements and the pointer to one past the -** last element of a C array. Use them like this: -** -** jschar buf[10], *s; -** JSString *str; -** ... -** for (s = buf; s != JS_ARRAY_END(buf); ++s) *s = ...; -** ... -** str = JS_NewStringCopyN(cx, buf, JS_ARRAY_LENGTH(buf)); -** ... -** -***********************************************************************/ - -#define JS_ARRAY_LENGTH(array) (sizeof (array) / sizeof (array)[0]) -#define JS_ARRAY_END(array) ((array) + JS_ARRAY_LENGTH(array)) - -JS_END_EXTERN_C - -#endif /* jstypes_h___ */ diff --git a/spidermonkey/src/jsutil.c b/spidermonkey/src/jsutil.c deleted file mode 100644 index 1bb9f93..0000000 --- a/spidermonkey/src/jsutil.c +++ /dev/null @@ -1,198 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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): - * IBM Corp. - * - * Alternatively, the contents of this file may be used under the terms of - * either of 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 ***** */ - -/* - * PR assertion checker. - */ -#include "jsstddef.h" -#include -#include -#include "jstypes.h" -#include "jsutil.h" - -#ifdef WIN32 -# include -#endif - -JS_PUBLIC_API(void) JS_Assert(const char *s, const char *file, JSIntn ln) -{ - fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln); -#if defined(WIN32) - DebugBreak(); - exit(3); -#elif defined(XP_OS2) || (defined(__GNUC__) && defined(__i386)) - asm("int $3"); -#endif - abort(); -} - -#if defined DEBUG_notme && defined XP_UNIX - -#define __USE_GNU 1 -#include -#include -#include "jshash.h" -#include "jsprf.h" - -JSCallsite js_calltree_root = {0, NULL, NULL, 0, NULL, NULL, NULL, NULL}; - -static JSCallsite * -CallTree(void **bp) -{ - void **bpup, **bpdown, *pc; - JSCallsite *parent, *site, **csp; - Dl_info info; - int ok, offset; - const char *symbol; - char *method; - - /* Reverse the stack frame list to avoid recursion. */ - bpup = NULL; - for (;;) { - bpdown = (void**) bp[0]; - bp[0] = (void*) bpup; - if ((void**) bpdown[0] < bpdown) - break; - bpup = bp; - bp = bpdown; - } - - /* Reverse the stack again, finding and building a path in the tree. */ - parent = &js_calltree_root; - do { - bpup = (void**) bp[0]; - bp[0] = (void*) bpdown; - pc = bp[1]; - - csp = &parent->kids; - while ((site = *csp) != NULL) { - if (site->pc == pc) { - /* Put the most recently used site at the front of siblings. */ - *csp = site->siblings; - site->siblings = parent->kids; - parent->kids = site; - - /* Site already built -- go up the stack. */ - goto upward; - } - csp = &site->siblings; - } - - /* Check for recursion: see if pc is on our ancestor line. */ - for (site = parent; site; site = site->parent) { - if (site->pc == pc) - goto upward; - } - - /* - * Not in tree at all: let's find our symbolic callsite info. - * XXX static syms are masked by nearest lower global - */ - info.dli_fname = info.dli_sname = NULL; - ok = dladdr(pc, &info); - if (ok < 0) { - fprintf(stderr, "dladdr failed!\n"); - return NULL; - } - -/* XXXbe sub 0x08040000? or something, see dbaron bug with tenthumbs comment */ - symbol = info.dli_sname; - offset = (char*)pc - (char*)info.dli_fbase; - method = symbol - ? strdup(symbol) - : JS_smprintf("%s+%X", - info.dli_fname ? info.dli_fname : "main", - offset); - if (!method) - return NULL; - - /* Create a new callsite record. */ - site = (JSCallsite *) malloc(sizeof(JSCallsite)); - if (!site) - return NULL; - - /* Insert the new site into the tree. */ - site->pc = pc; - site->name = method; - site->library = info.dli_fname; - site->offset = offset; - site->parent = parent; - site->siblings = parent->kids; - parent->kids = site; - site->kids = NULL; - - upward: - parent = site; - bpdown = bp; - bp = bpup; - } while (bp); - - return site; -} - -JSCallsite * -JS_Backtrace(int skip) -{ - void **bp, **bpdown; - - /* Stack walking code adapted from Kipp's "leaky". */ -#if defined(__i386) - __asm__( "movl %%ebp, %0" : "=g"(bp)); -#elif defined(__x86_64__) - __asm__( "movq %%rbp, %0" : "=g"(bp)); -#else - /* - * It would be nice if this worked uniformly, but at least on i386 and - * x86_64, it stopped working with gcc 4.1, because it points to the - * end of the saved registers instead of the start. - */ - bp = (void**) __builtin_frame_address(0); -#endif - while (--skip >= 0) { - bpdown = (void**) *bp++; - if (bpdown < bp) - break; - bp = bpdown; - } - - return CallTree(bp); -} - -#endif /* DEBUG_notme && XP_UNIX */ diff --git a/spidermonkey/src/jsutil.h b/spidermonkey/src/jsutil.h deleted file mode 100644 index efcb614..0000000 --- a/spidermonkey/src/jsutil.h +++ /dev/null @@ -1,106 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * PR assertion checker. - */ - -#ifndef jsutil_h___ -#define jsutil_h___ - -JS_BEGIN_EXTERN_C - -#ifdef DEBUG - -extern JS_PUBLIC_API(void) -JS_Assert(const char *s, const char *file, JSIntn ln); -#define JS_ASSERT(_expr) \ - ((_expr)?((void)0):JS_Assert(# _expr,__FILE__,__LINE__)) - -#define JS_NOT_REACHED(_reasonStr) \ - JS_Assert(_reasonStr,__FILE__,__LINE__) - -#else - -#define JS_ASSERT(expr) ((void) 0) -#define JS_NOT_REACHED(reasonStr) - -#endif /* defined(DEBUG) */ - -/* - * Compile-time assert. "condition" must be a constant expression. - * The macro should be used only once per source line in places where - * a "typedef" declaration is allowed. - */ -#define JS_STATIC_ASSERT(condition) \ - JS_STATIC_ASSERT_IMPL(condition, __LINE__) -#define JS_STATIC_ASSERT_IMPL(condition, line) \ - JS_STATIC_ASSERT_IMPL2(condition, line) -#define JS_STATIC_ASSERT_IMPL2(condition, line) \ - typedef int js_static_assert_line_##line[(condition) ? 1 : -1] - -/* -** Abort the process in a non-graceful manner. This will cause a core file, -** call to the debugger or other moral equivalent as well as causing the -** entire process to stop. -*/ -extern JS_PUBLIC_API(void) JS_Abort(void); - -#ifdef XP_UNIX - -typedef struct JSCallsite JSCallsite; - -struct JSCallsite { - uint32 pc; - char *name; - const char *library; - int offset; - JSCallsite *parent; - JSCallsite *siblings; - JSCallsite *kids; - void *handy; -}; - -extern JSCallsite *JS_Backtrace(int skip); - -#endif - -JS_END_EXTERN_C - -#endif /* jsutil_h___ */ diff --git a/spidermonkey/src/jsxdrapi.c b/spidermonkey/src/jsxdrapi.c deleted file mode 100644 index 2855c60..0000000 --- a/spidermonkey/src/jsxdrapi.c +++ /dev/null @@ -1,835 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 "jsstddef.h" -#include "jsconfig.h" - -#if JS_HAS_XDR - -#include -#include "jstypes.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jsdhash.h" -#include "jsprf.h" -#include "jsapi.h" -#include "jscntxt.h" -#include "jsnum.h" -#include "jsobj.h" /* js_XDRObject */ -#include "jsscript.h" /* js_XDRScript */ -#include "jsstr.h" -#include "jsxdrapi.h" - -#ifdef DEBUG -#define DBG(x) x -#else -#define DBG(x) ((void)0) -#endif - -typedef struct JSXDRMemState { - JSXDRState state; - char *base; - uint32 count; - uint32 limit; -} JSXDRMemState; - -#define MEM_BLOCK 8192 -#define MEM_PRIV(xdr) ((JSXDRMemState *)(xdr)) - -#define MEM_BASE(xdr) (MEM_PRIV(xdr)->base) -#define MEM_COUNT(xdr) (MEM_PRIV(xdr)->count) -#define MEM_LIMIT(xdr) (MEM_PRIV(xdr)->limit) - -#define MEM_LEFT(xdr, bytes) \ - JS_BEGIN_MACRO \ - if ((xdr)->mode == JSXDR_DECODE && \ - MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) { \ - JS_ReportErrorNumber((xdr)->cx, js_GetErrorMessage, NULL, \ - JSMSG_END_OF_DATA); \ - return 0; \ - } \ - JS_END_MACRO - -#define MEM_NEED(xdr, bytes) \ - JS_BEGIN_MACRO \ - if ((xdr)->mode == JSXDR_ENCODE) { \ - if (MEM_LIMIT(xdr) && \ - MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) { \ - uint32 limit_ = JS_ROUNDUP(MEM_COUNT(xdr) + bytes, MEM_BLOCK);\ - void *data_ = JS_realloc((xdr)->cx, MEM_BASE(xdr), limit_); \ - if (!data_) \ - return 0; \ - MEM_BASE(xdr) = data_; \ - MEM_LIMIT(xdr) = limit_; \ - } \ - } else { \ - MEM_LEFT(xdr, bytes); \ - } \ - JS_END_MACRO - -#define MEM_DATA(xdr) ((void *)(MEM_BASE(xdr) + MEM_COUNT(xdr))) -#define MEM_INCR(xdr,bytes) (MEM_COUNT(xdr) += (bytes)) - -static JSBool -mem_get32(JSXDRState *xdr, uint32 *lp) -{ - MEM_LEFT(xdr, 4); - *lp = *(uint32 *)MEM_DATA(xdr); - MEM_INCR(xdr, 4); - return JS_TRUE; -} - -static JSBool -mem_set32(JSXDRState *xdr, uint32 *lp) -{ - MEM_NEED(xdr, 4); - *(uint32 *)MEM_DATA(xdr) = *lp; - MEM_INCR(xdr, 4); - return JS_TRUE; -} - -static JSBool -mem_getbytes(JSXDRState *xdr, char *bytes, uint32 len) -{ - MEM_LEFT(xdr, len); - memcpy(bytes, MEM_DATA(xdr), len); - MEM_INCR(xdr, len); - return JS_TRUE; -} - -static JSBool -mem_setbytes(JSXDRState *xdr, char *bytes, uint32 len) -{ - MEM_NEED(xdr, len); - memcpy(MEM_DATA(xdr), bytes, len); - MEM_INCR(xdr, len); - return JS_TRUE; -} - -static void * -mem_raw(JSXDRState *xdr, uint32 len) -{ - void *data; - if (xdr->mode == JSXDR_ENCODE) { - MEM_NEED(xdr, len); - } else if (xdr->mode == JSXDR_DECODE) { - MEM_LEFT(xdr, len); - } - data = MEM_DATA(xdr); - MEM_INCR(xdr, len); - return data; -} - -static JSBool -mem_seek(JSXDRState *xdr, int32 offset, JSXDRWhence whence) -{ - switch (whence) { - case JSXDR_SEEK_CUR: - if ((int32)MEM_COUNT(xdr) + offset < 0) { - JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, - JSMSG_SEEK_BEYOND_START); - return JS_FALSE; - } - if (offset > 0) - MEM_NEED(xdr, offset); - MEM_COUNT(xdr) += offset; - return JS_TRUE; - case JSXDR_SEEK_SET: - if (offset < 0) { - JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, - JSMSG_SEEK_BEYOND_START); - return JS_FALSE; - } - if (xdr->mode == JSXDR_ENCODE) { - if ((uint32)offset > MEM_COUNT(xdr)) - MEM_NEED(xdr, offset - MEM_COUNT(xdr)); - MEM_COUNT(xdr) = offset; - } else { - if ((uint32)offset > MEM_LIMIT(xdr)) { - JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, - JSMSG_SEEK_BEYOND_END); - return JS_FALSE; - } - MEM_COUNT(xdr) = offset; - } - return JS_TRUE; - case JSXDR_SEEK_END: - if (offset >= 0 || - xdr->mode == JSXDR_ENCODE || - (int32)MEM_LIMIT(xdr) + offset < 0) { - JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, - JSMSG_END_SEEK); - return JS_FALSE; - } - MEM_COUNT(xdr) = MEM_LIMIT(xdr) + offset; - return JS_TRUE; - default: { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%d", whence); - JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, - JSMSG_WHITHER_WHENCE, numBuf); - return JS_FALSE; - } - } -} - -static uint32 -mem_tell(JSXDRState *xdr) -{ - return MEM_COUNT(xdr); -} - -static void -mem_finalize(JSXDRState *xdr) -{ - JS_free(xdr->cx, MEM_BASE(xdr)); -} - -static JSXDROps xdrmem_ops = { - mem_get32, mem_set32, mem_getbytes, mem_setbytes, - mem_raw, mem_seek, mem_tell, mem_finalize -}; - -JS_PUBLIC_API(void) -JS_XDRInitBase(JSXDRState *xdr, JSXDRMode mode, JSContext *cx) -{ - xdr->mode = mode; - xdr->cx = cx; - xdr->registry = NULL; - xdr->numclasses = xdr->maxclasses = 0; - xdr->reghash = NULL; - xdr->userdata = NULL; - xdr->script = NULL; -} - -JS_PUBLIC_API(JSXDRState *) -JS_XDRNewMem(JSContext *cx, JSXDRMode mode) -{ - JSXDRState *xdr = (JSXDRState *) JS_malloc(cx, sizeof(JSXDRMemState)); - if (!xdr) - return NULL; - JS_XDRInitBase(xdr, mode, cx); - if (mode == JSXDR_ENCODE) { - if (!(MEM_BASE(xdr) = JS_malloc(cx, MEM_BLOCK))) { - JS_free(cx, xdr); - return NULL; - } - } else { - /* XXXbe ok, so better not deref MEM_BASE(xdr) if not ENCODE */ - MEM_BASE(xdr) = NULL; - } - xdr->ops = &xdrmem_ops; - MEM_COUNT(xdr) = 0; - MEM_LIMIT(xdr) = MEM_BLOCK; - return xdr; -} - -JS_PUBLIC_API(void *) -JS_XDRMemGetData(JSXDRState *xdr, uint32 *lp) -{ - if (xdr->ops != &xdrmem_ops) - return NULL; - *lp = MEM_COUNT(xdr); - return MEM_BASE(xdr); -} - -JS_PUBLIC_API(void) -JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32 len) -{ - if (xdr->ops != &xdrmem_ops) - return; - MEM_LIMIT(xdr) = len; - MEM_BASE(xdr) = data; - MEM_COUNT(xdr) = 0; -} - -JS_PUBLIC_API(uint32) -JS_XDRMemDataLeft(JSXDRState *xdr) -{ - if (xdr->ops != &xdrmem_ops) - return 0; - return MEM_LIMIT(xdr) - MEM_COUNT(xdr); -} - -JS_PUBLIC_API(void) -JS_XDRMemResetData(JSXDRState *xdr) -{ - if (xdr->ops != &xdrmem_ops) - return; - MEM_COUNT(xdr) = 0; -} - -JS_PUBLIC_API(void) -JS_XDRDestroy(JSXDRState *xdr) -{ - JSContext *cx = xdr->cx; - xdr->ops->finalize(xdr); - if (xdr->registry) { - JS_free(cx, xdr->registry); - if (xdr->reghash) - JS_DHashTableDestroy(xdr->reghash); - } - JS_free(cx, xdr); -} - -JS_PUBLIC_API(JSBool) -JS_XDRUint8(JSXDRState *xdr, uint8 *b) -{ - uint32 l = *b; - if (!JS_XDRUint32(xdr, &l)) - return JS_FALSE; - *b = (uint8) l; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_XDRUint16(JSXDRState *xdr, uint16 *s) -{ - uint32 l = *s; - if (!JS_XDRUint32(xdr, &l)) - return JS_FALSE; - *s = (uint16) l; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_XDRUint32(JSXDRState *xdr, uint32 *lp) -{ - JSBool ok = JS_TRUE; - if (xdr->mode == JSXDR_ENCODE) { - uint32 xl = JSXDR_SWAB32(*lp); - ok = xdr->ops->set32(xdr, &xl); - } else if (xdr->mode == JSXDR_DECODE) { - ok = xdr->ops->get32(xdr, lp); - *lp = JSXDR_SWAB32(*lp); - } - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_XDRBytes(JSXDRState *xdr, char *bytes, uint32 len) -{ - uint32 padlen; - static char padbuf[JSXDR_ALIGN-1]; - - if (xdr->mode == JSXDR_ENCODE) { - if (!xdr->ops->setbytes(xdr, bytes, len)) - return JS_FALSE; - } else { - if (!xdr->ops->getbytes(xdr, bytes, len)) - return JS_FALSE; - } - len = xdr->ops->tell(xdr); - if (len % JSXDR_ALIGN) { - padlen = JSXDR_ALIGN - (len % JSXDR_ALIGN); - if (xdr->mode == JSXDR_ENCODE) { - if (!xdr->ops->setbytes(xdr, padbuf, padlen)) - return JS_FALSE; - } else { - if (!xdr->ops->seek(xdr, padlen, JSXDR_SEEK_CUR)) - return JS_FALSE; - } - } - return JS_TRUE; -} - -/** - * Convert between a C string and the XDR representation: - * leading 32-bit count, then counted vector of chars, - * then possibly \0 padding to multiple of 4. - */ -JS_PUBLIC_API(JSBool) -JS_XDRCString(JSXDRState *xdr, char **sp) -{ - uint32 len; - - if (xdr->mode == JSXDR_ENCODE) - len = strlen(*sp); - JS_XDRUint32(xdr, &len); - if (xdr->mode == JSXDR_DECODE) { - if (!(*sp = (char *) JS_malloc(xdr->cx, len + 1))) - return JS_FALSE; - } - if (!JS_XDRBytes(xdr, *sp, len)) { - if (xdr->mode == JSXDR_DECODE) - JS_free(xdr->cx, *sp); - return JS_FALSE; - } - if (xdr->mode == JSXDR_DECODE) { - (*sp)[len] = '\0'; - } else if (xdr->mode == JSXDR_FREE) { - JS_free(xdr->cx, *sp); - *sp = NULL; - } - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_XDRCStringOrNull(JSXDRState *xdr, char **sp) -{ - uint32 null = (*sp == NULL); - if (!JS_XDRUint32(xdr, &null)) - return JS_FALSE; - if (null) { - *sp = NULL; - return JS_TRUE; - } - return JS_XDRCString(xdr, sp); -} - -static JSBool -XDRChars(JSXDRState *xdr, jschar *chars, uint32 nchars) -{ - uint32 i, padlen, nbytes; - jschar *raw; - - nbytes = nchars * sizeof(jschar); - padlen = nbytes % JSXDR_ALIGN; - if (padlen) { - padlen = JSXDR_ALIGN - padlen; - nbytes += padlen; - } - if (!(raw = (jschar *) xdr->ops->raw(xdr, nbytes))) - return JS_FALSE; - if (xdr->mode == JSXDR_ENCODE) { - for (i = 0; i != nchars; i++) - raw[i] = JSXDR_SWAB16(chars[i]); - if (padlen) - memset((char *)raw + nbytes - padlen, 0, padlen); - } else if (xdr->mode == JSXDR_DECODE) { - for (i = 0; i != nchars; i++) - chars[i] = JSXDR_SWAB16(raw[i]); - } - return JS_TRUE; -} - -/* - * Convert between a JS (Unicode) string and the XDR representation. - */ -JS_PUBLIC_API(JSBool) -JS_XDRString(JSXDRState *xdr, JSString **strp) -{ - uint32 nchars; - jschar *chars; - - if (xdr->mode == JSXDR_ENCODE) - nchars = JSSTRING_LENGTH(*strp); - if (!JS_XDRUint32(xdr, &nchars)) - return JS_FALSE; - - if (xdr->mode == JSXDR_DECODE) { - chars = (jschar *) JS_malloc(xdr->cx, (nchars + 1) * sizeof(jschar)); - if (!chars) - return JS_FALSE; - } else { - chars = JSSTRING_CHARS(*strp); - } - - if (!XDRChars(xdr, chars, nchars)) - goto bad; - if (xdr->mode == JSXDR_DECODE) { - chars[nchars] = 0; - *strp = JS_NewUCString(xdr->cx, chars, nchars); - if (!*strp) - goto bad; - } - return JS_TRUE; - -bad: - if (xdr->mode == JSXDR_DECODE) - JS_free(xdr->cx, chars); - return JS_FALSE; -} - -JS_PUBLIC_API(JSBool) -JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp) -{ - uint32 null = (*strp == NULL); - if (!JS_XDRUint32(xdr, &null)) - return JS_FALSE; - if (null) { - *strp = NULL; - return JS_TRUE; - } - return JS_XDRString(xdr, strp); -} - -static JSBool -XDRDoubleValue(JSXDRState *xdr, jsdouble *dp) -{ - jsdpun u; - - if (xdr->mode == JSXDR_ENCODE) - u.d = *dp; - if (!JS_XDRUint32(xdr, &u.s.lo) || !JS_XDRUint32(xdr, &u.s.hi)) - return JS_FALSE; - if (xdr->mode == JSXDR_DECODE) - *dp = u.d; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_XDRDouble(JSXDRState *xdr, jsdouble **dpp) -{ - jsdouble d; - - if (xdr->mode == JSXDR_ENCODE) - d = **dpp; - if (!XDRDoubleValue(xdr, &d)) - return JS_FALSE; - if (xdr->mode == JSXDR_DECODE) { - *dpp = JS_NewDouble(xdr->cx, d); - if (!*dpp) - return JS_FALSE; - } - return JS_TRUE; -} - -/* These are magic pseudo-tags: see jsapi.h, near the top, for real tags. */ -#define JSVAL_XDRNULL 0x8 -#define JSVAL_XDRVOID 0xA - -static JSBool -XDRValueBody(JSXDRState *xdr, uint32 type, jsval *vp) -{ - switch (type) { - case JSVAL_XDRNULL: - *vp = JSVAL_NULL; - break; - case JSVAL_XDRVOID: - *vp = JSVAL_VOID; - break; - case JSVAL_STRING: { - JSString *str; - if (xdr->mode == JSXDR_ENCODE) - str = JSVAL_TO_STRING(*vp); - if (!JS_XDRString(xdr, &str)) - return JS_FALSE; - if (xdr->mode == JSXDR_DECODE) - *vp = STRING_TO_JSVAL(str); - break; - } - case JSVAL_DOUBLE: { - jsdouble *dp; - if (xdr->mode == JSXDR_ENCODE) - dp = JSVAL_TO_DOUBLE(*vp); - if (!JS_XDRDouble(xdr, &dp)) - return JS_FALSE; - if (xdr->mode == JSXDR_DECODE) - *vp = DOUBLE_TO_JSVAL(dp); - break; - } - case JSVAL_OBJECT: { - JSObject *obj; - if (xdr->mode == JSXDR_ENCODE) - obj = JSVAL_TO_OBJECT(*vp); - if (!js_XDRObject(xdr, &obj)) - return JS_FALSE; - if (xdr->mode == JSXDR_DECODE) - *vp = OBJECT_TO_JSVAL(obj); - break; - } - case JSVAL_BOOLEAN: { - uint32 b; - if (xdr->mode == JSXDR_ENCODE) - b = (uint32) JSVAL_TO_BOOLEAN(*vp); - if (!JS_XDRUint32(xdr, &b)) - return JS_FALSE; - if (xdr->mode == JSXDR_DECODE) - *vp = BOOLEAN_TO_JSVAL((JSBool) b); - break; - } - default: { - uint32 i; - - JS_ASSERT(type & JSVAL_INT); - if (xdr->mode == JSXDR_ENCODE) - i = (uint32) JSVAL_TO_INT(*vp); - if (!JS_XDRUint32(xdr, &i)) - return JS_FALSE; - if (xdr->mode == JSXDR_DECODE) - *vp = INT_TO_JSVAL((int32) i); - break; - } - } - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_XDRValue(JSXDRState *xdr, jsval *vp) -{ - uint32 type; - - if (xdr->mode == JSXDR_ENCODE) { - if (JSVAL_IS_NULL(*vp)) - type = JSVAL_XDRNULL; - else if (JSVAL_IS_VOID(*vp)) - type = JSVAL_XDRVOID; - else - type = JSVAL_TAG(*vp); - } - return JS_XDRUint32(xdr, &type) && XDRValueBody(xdr, type, vp); -} - -JSBool -js_XDRAtom(JSXDRState *xdr, JSAtom **atomp) -{ - jsval v; - uint32 type; - jsdouble d; - JSAtom *atom; - - if (xdr->mode == JSXDR_ENCODE) { - v = ATOM_KEY(*atomp); - return JS_XDRValue(xdr, &v); - } - - /* - * Inline JS_XDRValue when decoding to avoid ceation of GC things when - * then corresponding atom already exists. See bug 321985. - */ - if (!JS_XDRUint32(xdr, &type)) - return JS_FALSE; - if (type == JSVAL_STRING) - return js_XDRStringAtom(xdr, atomp); - - if (type == JSVAL_DOUBLE) { - if (!XDRDoubleValue(xdr, &d)) - return JS_FALSE; - atom = js_AtomizeDouble(xdr->cx, d, 0); - } else { - if (!XDRValueBody(xdr, type, &v)) - return JS_FALSE; - atom = js_AtomizeValue(xdr->cx, v, 0); - } - - if (!atom) - return JS_FALSE; - *atomp = atom; - return JS_TRUE; -} - -extern JSBool -js_XDRStringAtom(JSXDRState *xdr, JSAtom **atomp) -{ - JSString *str; - uint32 nchars; - JSAtom *atom; - JSContext *cx; - void *mark; - jschar *chars; - - if (xdr->mode == JSXDR_ENCODE) { - JS_ASSERT(ATOM_IS_STRING(*atomp)); - str = ATOM_TO_STRING(*atomp); - return JS_XDRString(xdr, &str); - } - - /* - * Inline JS_XDRString when decoding to avoid JSString allocation - * for already existing atoms. See bug 321985. - */ - if (!JS_XDRUint32(xdr, &nchars)) - return JS_FALSE; - atom = NULL; - cx = xdr->cx; - mark = JS_ARENA_MARK(&cx->tempPool); - JS_ARENA_ALLOCATE_CAST(chars, jschar *, &cx->tempPool, - nchars * sizeof(jschar)); - if (!chars) - JS_ReportOutOfMemory(cx); - else if (XDRChars(xdr, chars, nchars)) - atom = js_AtomizeChars(cx, chars, nchars, 0); - JS_ARENA_RELEASE(&cx->tempPool, mark); - if (!atom) - return JS_FALSE; - *atomp = atom; - return JS_TRUE; -} - -/* - * FIXME: This performs lossy conversion and we need to switch to - * js_XDRStringAtom while allowing to read older XDR files. See bug 325202. - */ -JSBool -js_XDRCStringAtom(JSXDRState *xdr, JSAtom **atomp) -{ - char *bytes; - uint32 nbytes; - JSAtom *atom; - JSContext *cx; - void *mark; - - if (xdr->mode == JSXDR_ENCODE) { - JS_ASSERT(ATOM_IS_STRING(*atomp)); - bytes = JS_GetStringBytes(ATOM_TO_STRING(*atomp)); - return JS_XDRCString(xdr, &bytes); - } - - /* - * Inline JS_XDRCString when decoding not to malloc temporary buffer - * just to free it after atomization. See bug 321985. - */ - if (!JS_XDRUint32(xdr, &nbytes)) - return JS_FALSE; - atom = NULL; - cx = xdr->cx; - mark = JS_ARENA_MARK(&cx->tempPool); - JS_ARENA_ALLOCATE_CAST(bytes, char *, &cx->tempPool, - nbytes * sizeof *bytes); - if (!bytes) - JS_ReportOutOfMemory(cx); - else if (JS_XDRBytes(xdr, bytes, nbytes)) - atom = js_Atomize(cx, bytes, nbytes, 0); - JS_ARENA_RELEASE(&cx->tempPool, mark); - if (!atom) - return JS_FALSE; - *atomp = atom; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_XDRScript(JSXDRState *xdr, JSScript **scriptp) -{ - if (!js_XDRScript(xdr, scriptp, NULL)) - return JS_FALSE; - if (xdr->mode == JSXDR_DECODE) - js_CallNewScriptHook(xdr->cx, *scriptp, NULL); - return JS_TRUE; -} - -#define CLASS_REGISTRY_MIN 8 -#define CLASS_INDEX_TO_ID(i) ((i)+1) -#define CLASS_ID_TO_INDEX(id) ((id)-1) - -typedef struct JSRegHashEntry { - JSDHashEntryHdr hdr; - const char *name; - uint32 index; -} JSRegHashEntry; - -JS_PUBLIC_API(JSBool) -JS_XDRRegisterClass(JSXDRState *xdr, JSClass *clasp, uint32 *idp) -{ - uintN numclasses, maxclasses; - JSClass **registry; - - numclasses = xdr->numclasses; - maxclasses = xdr->maxclasses; - if (numclasses == maxclasses) { - maxclasses = (maxclasses == 0) ? CLASS_REGISTRY_MIN : maxclasses << 1; - registry = (JSClass **) - JS_realloc(xdr->cx, xdr->registry, maxclasses * sizeof(JSClass *)); - if (!registry) - return JS_FALSE; - xdr->registry = registry; - xdr->maxclasses = maxclasses; - } else { - JS_ASSERT(numclasses && numclasses < maxclasses); - registry = xdr->registry; - } - - registry[numclasses] = clasp; - if (xdr->reghash) { - JSRegHashEntry *entry = (JSRegHashEntry *) - JS_DHashTableOperate(xdr->reghash, clasp->name, JS_DHASH_ADD); - if (!entry) { - JS_ReportOutOfMemory(xdr->cx); - return JS_FALSE; - } - entry->name = clasp->name; - entry->index = numclasses; - } - *idp = CLASS_INDEX_TO_ID(numclasses); - xdr->numclasses = ++numclasses; - return JS_TRUE; -} - -JS_PUBLIC_API(uint32) -JS_XDRFindClassIdByName(JSXDRState *xdr, const char *name) -{ - uintN i, numclasses; - - numclasses = xdr->numclasses; - if (numclasses >= 10) { - JSRegHashEntry *entry; - - /* Bootstrap reghash from registry on first overpopulated Find. */ - if (!xdr->reghash) { - xdr->reghash = JS_NewDHashTable(JS_DHashGetStubOps(), NULL, - sizeof(JSRegHashEntry), - numclasses); - if (xdr->reghash) { - for (i = 0; i < numclasses; i++) { - JSClass *clasp = xdr->registry[i]; - entry = (JSRegHashEntry *) - JS_DHashTableOperate(xdr->reghash, clasp->name, - JS_DHASH_ADD); - entry->name = clasp->name; - entry->index = i; - } - } - } - - /* If we managed to create reghash, use it for O(1) Find. */ - if (xdr->reghash) { - entry = (JSRegHashEntry *) - JS_DHashTableOperate(xdr->reghash, name, JS_DHASH_LOOKUP); - if (JS_DHASH_ENTRY_IS_BUSY(&entry->hdr)) - return CLASS_INDEX_TO_ID(entry->index); - } - } - - /* Only a few classes, or we couldn't malloc reghash: use linear search. */ - for (i = 0; i < numclasses; i++) { - if (!strcmp(name, xdr->registry[i]->name)) - return CLASS_INDEX_TO_ID(i); - } - return 0; -} - -JS_PUBLIC_API(JSClass *) -JS_XDRFindClassById(JSXDRState *xdr, uint32 id) -{ - uintN i = CLASS_ID_TO_INDEX(id); - - if (i >= xdr->numclasses) - return NULL; - return xdr->registry[i]; -} - -#endif /* JS_HAS_XDR */ diff --git a/spidermonkey/src/jsxdrapi.h b/spidermonkey/src/jsxdrapi.h deleted file mode 100644 index 35d9918..0000000 --- a/spidermonkey/src/jsxdrapi.h +++ /dev/null @@ -1,223 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef jsxdrapi_h___ -#define jsxdrapi_h___ - -/* - * JS external data representation interface API. - * - * The XDR system is comprised of three major parts: - * - * - the state serialization/deserialization APIs, which allow consumers - * of the API to serialize JS runtime state (script bytecodes, atom maps, - * object graphs, etc.) for later restoration. These portions - * are implemented in various appropriate files, such as jsscript.c - * for the script portions and jsobj.c for object state. - * - the callback APIs through which the runtime requests an opaque - * representation of a native object, and through which the runtime - * constructs a live native object from an opaque representation. These - * portions are the responsibility of the native object implementor. - * - utility functions for en/decoding of primitive types, such as - * JSStrings. This portion is implemented in jsxdrapi.c. - * - * Spiritually guided by Sun's XDR, where appropriate. - */ - -#include "jspubtd.h" -#include "jsprvtd.h" - -JS_BEGIN_EXTERN_C - -/* We use little-endian byteorder for all encoded data */ - -#if defined IS_LITTLE_ENDIAN -#define JSXDR_SWAB32(x) x -#define JSXDR_SWAB16(x) x -#elif defined IS_BIG_ENDIAN -#define JSXDR_SWAB32(x) (((uint32)(x) >> 24) | \ - (((uint32)(x) >> 8) & 0xff00) | \ - (((uint32)(x) << 8) & 0xff0000) | \ - ((uint32)(x) << 24)) -#define JSXDR_SWAB16(x) (((uint16)(x) >> 8) | ((uint16)(x) << 8)) -#else -#error "unknown byte order" -#endif - -#define JSXDR_ALIGN 4 - -typedef enum JSXDRMode { - JSXDR_ENCODE, - JSXDR_DECODE, - JSXDR_FREE -} JSXDRMode; - -typedef enum JSXDRWhence { - JSXDR_SEEK_SET, - JSXDR_SEEK_CUR, - JSXDR_SEEK_END -} JSXDRWhence; - -typedef struct JSXDROps { - JSBool (*get32)(JSXDRState *, uint32 *); - JSBool (*set32)(JSXDRState *, uint32 *); - JSBool (*getbytes)(JSXDRState *, char *, uint32); - JSBool (*setbytes)(JSXDRState *, char *, uint32); - void * (*raw)(JSXDRState *, uint32); - JSBool (*seek)(JSXDRState *, int32, JSXDRWhence); - uint32 (*tell)(JSXDRState *); - void (*finalize)(JSXDRState *); -} JSXDROps; - -struct JSXDRState { - JSXDRMode mode; - JSXDROps *ops; - JSContext *cx; - JSClass **registry; - uintN numclasses; - uintN maxclasses; - void *reghash; - void *userdata; - JSScript *script; -}; - -extern JS_PUBLIC_API(void) -JS_XDRInitBase(JSXDRState *xdr, JSXDRMode mode, JSContext *cx); - -extern JS_PUBLIC_API(JSXDRState *) -JS_XDRNewMem(JSContext *cx, JSXDRMode mode); - -extern JS_PUBLIC_API(void *) -JS_XDRMemGetData(JSXDRState *xdr, uint32 *lp); - -extern JS_PUBLIC_API(void) -JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32 len); - -extern JS_PUBLIC_API(uint32) -JS_XDRMemDataLeft(JSXDRState *xdr); - -extern JS_PUBLIC_API(void) -JS_XDRMemResetData(JSXDRState *xdr); - -extern JS_PUBLIC_API(void) -JS_XDRDestroy(JSXDRState *xdr); - -extern JS_PUBLIC_API(JSBool) -JS_XDRUint8(JSXDRState *xdr, uint8 *b); - -extern JS_PUBLIC_API(JSBool) -JS_XDRUint16(JSXDRState *xdr, uint16 *s); - -extern JS_PUBLIC_API(JSBool) -JS_XDRUint32(JSXDRState *xdr, uint32 *lp); - -extern JS_PUBLIC_API(JSBool) -JS_XDRBytes(JSXDRState *xdr, char *bytes, uint32 len); - -extern JS_PUBLIC_API(JSBool) -JS_XDRCString(JSXDRState *xdr, char **sp); - -extern JS_PUBLIC_API(JSBool) -JS_XDRCStringOrNull(JSXDRState *xdr, char **sp); - -extern JS_PUBLIC_API(JSBool) -JS_XDRString(JSXDRState *xdr, JSString **strp); - -extern JS_PUBLIC_API(JSBool) -JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp); - -extern JS_PUBLIC_API(JSBool) -JS_XDRDouble(JSXDRState *xdr, jsdouble **dp); - -extern JS_PUBLIC_API(JSBool) -JS_XDRValue(JSXDRState *xdr, jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_XDRScript(JSXDRState *xdr, JSScript **scriptp); - -extern JS_PUBLIC_API(JSBool) -JS_XDRRegisterClass(JSXDRState *xdr, JSClass *clasp, uint32 *lp); - -extern JS_PUBLIC_API(uint32) -JS_XDRFindClassIdByName(JSXDRState *xdr, const char *name); - -extern JS_PUBLIC_API(JSClass *) -JS_XDRFindClassById(JSXDRState *xdr, uint32 id); - -/* - * Magic numbers. - */ -#define JSXDR_MAGIC_SCRIPT_1 0xdead0001 -#define JSXDR_MAGIC_SCRIPT_2 0xdead0002 -#define JSXDR_MAGIC_SCRIPT_3 0xdead0003 -#define JSXDR_MAGIC_SCRIPT_4 0xdead0004 -#define JSXDR_MAGIC_SCRIPT_5 0xdead0005 -#define JSXDR_MAGIC_SCRIPT_CURRENT JSXDR_MAGIC_SCRIPT_5 - -/* - * Bytecode version number. Decrement the second term whenever JS bytecode - * changes incompatibly. - * - * This version number should be XDR'ed once near the front of any file or - * larger storage unit containing XDR'ed bytecode and other data, and checked - * before deserialization of bytecode. If the saved version does not match - * the current version, abort deserialization and invalidate the file. - */ -#define JSXDR_BYTECODE_VERSION (0xb973c0de - 16) - -/* - * Library-private functions. - */ -extern JSBool -js_XDRAtom(JSXDRState *xdr, JSAtom **atomp); - -extern JSBool -js_XDRStringAtom(JSXDRState *xdr, JSAtom **atomp); - -/* - * FIXME: This is non-unicode version of js_XDRStringAtom that performs lossy - * conversion. Do not use it in the new code! See bug 325202. - */ -extern JSBool -js_XDRCStringAtom(JSXDRState *xdr, JSAtom **atomp); - -JS_END_EXTERN_C - -#endif /* ! jsxdrapi_h___ */ diff --git a/spidermonkey/src/jsxml.c b/spidermonkey/src/jsxml.c deleted file mode 100644 index 1266255..0000000 --- a/spidermonkey/src/jsxml.c +++ /dev/null @@ -1,8357 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=4 sw=4 et tw=78: - * - * ***** 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 SpiderMonkey E4X code, released August, 2004. - * - * 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 of 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 "jsstddef.h" -#include "jsconfig.h" - -#if JS_HAS_XML_SUPPORT - -#include -#include -#include -#include "jstypes.h" -#include "jsbit.h" -#include "jsprf.h" -#include "jsutil.h" -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jsbool.h" -#include "jscntxt.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsparse.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" -#include "jsxml.h" - -#ifdef DEBUG -#include /* for #ifdef DEBUG memset calls */ -#endif - -/* - * NOTES - * - in the js shell, you must use the -x command line option, or call - * options('xml') before compiling anything that uses XML literals - * - * TODO - * - XXXbe patrol - * - Fuse objects and their JSXML* private data into single GC-things - * - fix function::foo vs. x.(foo == 42) collision using proper namespacing - * - fix the !TCF_HAS_DEFXMLNS optimization in js_FoldConstants - * - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM! - * - JS_TypeOfValue sure could use a cleaner interface to "types" - */ - -#ifdef DEBUG_brendan -#define METERING 1 -#endif - -#ifdef METERING -static struct { - jsrefcount qname; - jsrefcount qnameobj; - jsrefcount liveqname; - jsrefcount liveqnameobj; - jsrefcount namespace; - jsrefcount namespaceobj; - jsrefcount livenamespace; - jsrefcount livenamespaceobj; - jsrefcount xml; - jsrefcount xmlobj; - jsrefcount livexml; - jsrefcount livexmlobj; -} xml_stats; - -#define METER(x) JS_ATOMIC_INCREMENT(&(x)) -#define UNMETER(x) JS_ATOMIC_DECREMENT(&(x)) -#else -#define METER(x) /* nothing */ -#define UNMETER(x) /* nothing */ -#endif - -/* - * Random utilities and global functions. - */ -const char js_isXMLName_str[] = "isXMLName"; -const char js_XMLList_str[] = "XMLList"; -const char js_localName_str[] = "localName"; -const char js_xml_parent_str[] = "parent"; -const char js_prefix_str[] = "prefix"; -const char js_toXMLString_str[] = "toXMLString"; -const char js_uri_str[] = "uri"; - -const char js_amp_entity_str[] = "&"; -const char js_gt_entity_str[] = ">"; -const char js_lt_entity_str[] = "<"; -const char js_quot_entity_str[] = """; - -#define IS_EMPTY(str) (JSSTRING_LENGTH(str) == 0) -#define IS_STAR(str) (JSSTRING_LENGTH(str) == 1 && *JSSTRING_CHARS(str) == '*') - -static JSBool -xml_isXMLName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - *rval = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argv[0])); - return JS_TRUE; -} - -/* - * Namespace class and library functions. - */ -enum namespace_tinyid { - NAMESPACE_PREFIX = -1, - NAMESPACE_URI = -2 -}; - -static JSBool -namespace_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSXMLNamespace *ns; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - - ns = (JSXMLNamespace *) - JS_GetInstancePrivate(cx, obj, &js_NamespaceClass.base, NULL); - if (!ns) - return JS_TRUE; - - switch (JSVAL_TO_INT(id)) { - case NAMESPACE_PREFIX: - *vp = ns->prefix ? STRING_TO_JSVAL(ns->prefix) : JSVAL_VOID; - break; - case NAMESPACE_URI: - *vp = STRING_TO_JSVAL(ns->uri); - break; - } - return JS_TRUE; -} - -static void -namespace_finalize(JSContext *cx, JSObject *obj) -{ - JSXMLNamespace *ns; - JSRuntime *rt; - - ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); - if (!ns) - return; - JS_ASSERT(ns->object == obj); - ns->object = NULL; - UNMETER(xml_stats.livenamespaceobj); - - rt = cx->runtime; - if (rt->functionNamespaceObject == obj) - rt->functionNamespaceObject = NULL; -} - -static void -namespace_mark_vector(JSContext *cx, JSXMLNamespace **vec, uint32 len) -{ - uint32 i; - JSXMLNamespace *ns; - - for (i = 0; i < len; i++) { - ns = vec[i]; - { -#ifdef GC_MARK_DEBUG - char buf[100]; - - JS_snprintf(buf, sizeof buf, "%s=%s", - ns->prefix ? JS_GetStringBytes(ns->prefix) : "", - JS_GetStringBytes(ns->uri)); -#endif - GC_MARK(cx, ns, buf); - } - } -} - -static uint32 -namespace_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSXMLNamespace *ns; - - ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); - GC_MARK(cx, ns, "private"); - return 0; -} - -static JSBool -namespace_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - JSXMLNamespace *ns, *ns2; - JSObject *obj2; - - ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); - JS_ASSERT(JSVAL_IS_OBJECT(v)); - obj2 = JSVAL_TO_OBJECT(v); - if (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_NamespaceClass.base) { - *bp = JS_FALSE; - } else { - ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, obj2); - *bp = js_EqualStrings(ns->uri, ns2->uri); - } - return JS_TRUE; -} - -JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass = { - { "Namespace", - JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED | - JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace), - JS_PropertyStub, JS_PropertyStub, namespace_getProperty, NULL, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, namespace_finalize, - NULL, NULL, NULL, NULL, - NULL, NULL, namespace_mark, NULL }, - namespace_equality,NULL, NULL, NULL, - NULL, NULL, NULL, NULL -}; - -#define NAMESPACE_ATTRS \ - (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED) - -static JSPropertySpec namespace_props[] = { - {js_prefix_str, NAMESPACE_PREFIX, NAMESPACE_ATTRS, 0, 0}, - {js_uri_str, NAMESPACE_URI, NAMESPACE_ATTRS, 0, 0}, - {0,0,0,0,0} -}; - -static JSBool -namespace_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXMLNamespace *ns; - - ns = (JSXMLNamespace *) - JS_GetInstancePrivate(cx, obj, &js_NamespaceClass.base, argv); - if (!ns) - return JS_FALSE; - - *rval = STRING_TO_JSVAL(ns->uri); - return JS_TRUE; -} - -static JSFunctionSpec namespace_methods[] = { - {js_toString_str, namespace_toString, 0,0,0}, - {0,0,0,0,0} -}; - -JSXMLNamespace * -js_NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, - JSBool declared) -{ - JSXMLNamespace *ns; - - ns = (JSXMLNamespace *) - js_NewGCThing(cx, GCX_NAMESPACE, sizeof(JSXMLNamespace)); - if (!ns) - return NULL; - ns->object = NULL; - ns->prefix = prefix; - ns->uri = uri; - ns->declared = declared; - METER(xml_stats.namespace); - METER(xml_stats.livenamespace); - return ns; -} - -void -js_MarkXMLNamespace(JSContext *cx, JSXMLNamespace *ns) -{ - GC_MARK(cx, ns->object, "object"); - GC_MARK(cx, ns->prefix, "prefix"); - GC_MARK(cx, ns->uri, "uri"); -} - -void -js_FinalizeXMLNamespace(JSContext *cx, JSXMLNamespace *ns) -{ - UNMETER(xml_stats.livenamespace); -} - -JSObject * -js_NewXMLNamespaceObject(JSContext *cx, JSString *prefix, JSString *uri, - JSBool declared) -{ - JSXMLNamespace *ns; - - ns = js_NewXMLNamespace(cx, prefix, uri, declared); - if (!ns) - return NULL; - return js_GetXMLNamespaceObject(cx, ns); -} - -JSObject * -js_GetXMLNamespaceObject(JSContext *cx, JSXMLNamespace *ns) -{ - JSObject *obj; - - obj = ns->object; - if (obj) { - JS_ASSERT(JS_GetPrivate(cx, obj) == ns); - return obj; - } - obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL); - if (!obj || !JS_SetPrivate(cx, obj, ns)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; - } - ns->object = obj; - METER(xml_stats.namespaceobj); - METER(xml_stats.livenamespaceobj); - return obj; -} - -/* - * QName class and library functions. - */ -enum qname_tinyid { - QNAME_URI = -1, - QNAME_LOCALNAME = -2 -}; - -static JSBool -qname_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSXMLQName *qn; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - - qn = (JSXMLQName *) - JS_GetInstancePrivate(cx, obj, &js_QNameClass.base, NULL); - if (!qn) - return JS_TRUE; - - switch (JSVAL_TO_INT(id)) { - case QNAME_URI: - *vp = qn->uri ? STRING_TO_JSVAL(qn->uri) : JSVAL_NULL; - break; - case QNAME_LOCALNAME: - *vp = STRING_TO_JSVAL(qn->localName); - break; - } - return JS_TRUE; -} - -static void -qname_finalize(JSContext *cx, JSObject *obj) -{ - JSXMLQName *qn; - - qn = (JSXMLQName *) JS_GetPrivate(cx, obj); - if (!qn) - return; - JS_ASSERT(qn->object == obj); - qn->object = NULL; - UNMETER(xml_stats.liveqnameobj); -} - -static void -anyname_finalize(JSContext* cx, JSObject* obj) -{ - JSRuntime *rt; - - /* Make sure the next call to js_GetAnyName doesn't try to use obj. */ - rt = cx->runtime; - if (rt->anynameObject == obj) - rt->anynameObject = NULL; - - qname_finalize(cx, obj); -} - -static uint32 -qname_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSXMLQName *qn; - - qn = (JSXMLQName *) JS_GetPrivate(cx, obj); - GC_MARK(cx, qn, "private"); - return 0; -} - -static JSBool -qname_identity(JSXMLQName *qna, JSXMLQName *qnb) -{ - if (!qna->uri ^ !qnb->uri) - return JS_FALSE; - if (qna->uri && !js_EqualStrings(qna->uri, qnb->uri)) - return JS_FALSE; - return js_EqualStrings(qna->localName, qnb->localName); -} - -static JSBool -qname_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - JSXMLQName *qn, *qn2; - JSObject *obj2; - - qn = (JSXMLQName *) JS_GetPrivate(cx, obj); - JS_ASSERT(JSVAL_IS_OBJECT(v)); - obj2 = JSVAL_TO_OBJECT(v); - if (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_QNameClass.base) { - *bp = JS_FALSE; - } else { - qn2 = (JSXMLQName *) JS_GetPrivate(cx, obj2); - *bp = qname_identity(qn, qn2); - } - return JS_TRUE; -} - -JS_FRIEND_DATA(JSExtendedClass) js_QNameClass = { - { "QName", - JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED | - JSCLASS_HAS_CACHED_PROTO(JSProto_QName), - JS_PropertyStub, JS_PropertyStub, qname_getProperty, NULL, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, qname_finalize, - NULL, NULL, NULL, NULL, - NULL, NULL, qname_mark, NULL }, - qname_equality, NULL, NULL, NULL, - NULL, NULL, NULL, NULL -}; - -/* - * Classes for the ECMA-357-internal types AttributeName and AnyName, which - * are like QName, except that they have no property getters. They share the - * qname_toString method, and therefore are exposed as constructable objects - * in this implementation. - */ -JS_FRIEND_DATA(JSClass) js_AttributeNameClass = { - js_AttributeName_str, - JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | - JSCLASS_HAS_CACHED_PROTO(JSProto_AttributeName), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, qname_finalize, - NULL, NULL, NULL, NULL, - NULL, NULL, qname_mark, NULL -}; - -JS_FRIEND_DATA(JSClass) js_AnyNameClass = { - js_AnyName_str, - JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | - JSCLASS_HAS_CACHED_PROTO(JSProto_AnyName), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, anyname_finalize, - NULL, NULL, NULL, NULL, - NULL, NULL, qname_mark, NULL -}; - -#define QNAME_ATTRS \ - (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED) - -static JSPropertySpec qname_props[] = { - {js_uri_str, QNAME_URI, QNAME_ATTRS, 0, 0}, - {js_localName_str, QNAME_LOCALNAME, QNAME_ATTRS, 0, 0}, - {0,0,0,0,0} -}; - -static JSBool -qname_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSClass *clasp; - JSXMLQName *qn; - JSString *str, *qualstr; - size_t length; - jschar *chars; - - clasp = OBJ_GET_CLASS(cx, obj); - if (clasp == &js_AttributeNameClass || clasp == &js_AnyNameClass) { - qn = (JSXMLQName *) JS_GetPrivate(cx, obj); - } else { - qn = (JSXMLQName *) - JS_GetInstancePrivate(cx, obj, &js_QNameClass.base, argv); - if (!qn) - return JS_FALSE; - } - - if (!qn->uri) { - /* No uri means wildcard qualifier. */ - str = ATOM_TO_STRING(cx->runtime->atomState.starQualifierAtom); - } else if (IS_EMPTY(qn->uri)) { - /* Empty string for uri means localName is in no namespace. */ - str = cx->runtime->emptyString; - } else { - qualstr = ATOM_TO_STRING(cx->runtime->atomState.qualifierAtom); - str = js_ConcatStrings(cx, qn->uri, qualstr); - if (!str) - return JS_FALSE; - } - str = js_ConcatStrings(cx, str, qn->localName); - if (!str) - return JS_FALSE; - - if (str && clasp == &js_AttributeNameClass) { - length = JSSTRING_LENGTH(str); - chars = (jschar *) JS_malloc(cx, (length + 2) * sizeof(jschar)); - if (!chars) - return JS_FALSE; - *chars = '@'; - js_strncpy(chars + 1, JSSTRING_CHARS(str), length); - chars[++length] = 0; - str = js_NewString(cx, chars, length, 0); - if (!str) { - JS_free(cx, chars); - return JS_FALSE; - } - } - - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSFunctionSpec qname_methods[] = { - {js_toString_str, qname_toString, 0,0,0}, - {0,0,0,0,0} -}; - -JSXMLQName * -js_NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, - JSString *localName) -{ - JSXMLQName *qn; - - qn = (JSXMLQName *) js_NewGCThing(cx, GCX_QNAME, sizeof(JSXMLQName)); - if (!qn) - return NULL; - qn->object = NULL; - qn->uri = uri; - qn->prefix = prefix; - qn->localName = localName; - METER(xml_stats.qname); - METER(xml_stats.liveqname); - return qn; -} - -void -js_MarkXMLQName(JSContext *cx, JSXMLQName *qn) -{ - GC_MARK(cx, qn->object, "object"); - GC_MARK(cx, qn->uri, "uri"); - GC_MARK(cx, qn->prefix, "prefix"); - GC_MARK(cx, qn->localName, "localName"); -} - -void -js_FinalizeXMLQName(JSContext *cx, JSXMLQName *qn) -{ - UNMETER(xml_stats.liveqname); -} - -JSObject * -js_NewXMLQNameObject(JSContext *cx, JSString *uri, JSString *prefix, - JSString *localName) -{ - JSXMLQName *qn; - - qn = js_NewXMLQName(cx, uri, prefix, localName); - if (!qn) - return NULL; - return js_GetXMLQNameObject(cx, qn); -} - -JSObject * -js_GetXMLQNameObject(JSContext *cx, JSXMLQName *qn) -{ - JSObject *obj; - - obj = qn->object; - if (obj) { - JS_ASSERT(JS_GetPrivate(cx, obj) == qn); - return obj; - } - obj = js_NewObject(cx, &js_QNameClass.base, NULL, NULL); - if (!obj || !JS_SetPrivate(cx, obj, qn)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; - } - qn->object = obj; - METER(xml_stats.qnameobj); - METER(xml_stats.liveqnameobj); - return obj; -} - -JSObject * -js_GetAttributeNameObject(JSContext *cx, JSXMLQName *qn) -{ - JSObject *obj; - - obj = qn->object; - if (obj) { - if (OBJ_GET_CLASS(cx, obj) == &js_AttributeNameClass) - return obj; - qn = js_NewXMLQName(cx, qn->uri, qn->prefix, qn->localName); - if (!qn) - return NULL; - } - - obj = js_NewObject(cx, &js_AttributeNameClass, NULL, NULL); - if (!obj || !JS_SetPrivate(cx, obj, qn)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; - } - - qn->object = obj; - METER(xml_stats.qnameobj); - METER(xml_stats.liveqnameobj); - return obj; -} - -JSObject * -js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval) -{ - jsval argv[2]; - - /* - * ECMA-357 11.1.2, - * The _QualifiedIdentifier : PropertySelector :: PropertySelector_ - * production, step 2. - */ - if (!JSVAL_IS_PRIMITIVE(nsval) && - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) == &js_AnyNameClass) { - nsval = JSVAL_NULL; - } - - argv[0] = nsval; - argv[1] = lnval; - return js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, argv); -} - -static JSBool -IsXMLName(const jschar *cp, size_t n) -{ - JSBool rv; - jschar c; - - rv = JS_FALSE; - if (n != 0 && JS_ISXMLNSSTART(*cp)) { - while (--n != 0) { - c = *++cp; - if (!JS_ISXMLNS(c)) - return rv; - } - rv = JS_TRUE; - } - return rv; -} - -JSBool -js_IsXMLName(JSContext *cx, jsval v) -{ - JSClass *clasp; - JSXMLQName *qn; - JSString *name; - JSErrorReporter older; - - /* - * Inline specialization of the QName constructor called with v passed as - * the only argument, to compute the localName for the constructed qname, - * without actually allocating the object or computing its uri and prefix. - * See ECMA-357 13.1.2.1 step 1 and 13.3.2. - */ - if (!JSVAL_IS_PRIMITIVE(v) && - (clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)), - clasp == &js_QNameClass.base || - clasp == &js_AttributeNameClass || - clasp == &js_AnyNameClass)) { - qn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); - name = qn->localName; - } else { - older = JS_SetErrorReporter(cx, NULL); - name = js_ValueToString(cx, v); - JS_SetErrorReporter(cx, older); - if (!name) { - JS_ClearPendingException(cx); - return JS_FALSE; - } - } - - return IsXMLName(JSSTRING_CHARS(name), JSSTRING_LENGTH(name)); -} - -static JSBool -Namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval urival, prefixval; - JSObject *uriobj; - JSBool isNamespace, isQName; - JSClass *clasp; - JSString *empty, *prefix; - JSXMLNamespace *ns, *ns2; - JSXMLQName *qn; - - urival = argv[argc > 1]; - isNamespace = isQName = JS_FALSE; - if (!JSVAL_IS_PRIMITIVE(urival)) { - uriobj = JSVAL_TO_OBJECT(urival); - clasp = OBJ_GET_CLASS(cx, uriobj); - isNamespace = (clasp == &js_NamespaceClass.base); - isQName = (clasp == &js_QNameClass.base); - } -#ifdef __GNUC__ /* suppress bogus gcc warnings */ - else uriobj = NULL; -#endif - - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - /* Namespace called as function. */ - if (argc == 1 && isNamespace) { - /* Namespace called with one Namespace argument is identity. */ - *rval = urival; - return JS_TRUE; - } - - /* Create and return a new QName object exactly as if constructed. */ - obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL); - if (!obj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - } - METER(xml_stats.namespaceobj); - METER(xml_stats.livenamespaceobj); - - /* - * Create and connect private data to rooted obj early, so we don't have - * to worry about rooting string newborns hanging off of the private data - * further below. - */ - empty = cx->runtime->emptyString; - ns = js_NewXMLNamespace(cx, empty, empty, JS_FALSE); - if (!ns) - return JS_FALSE; - if (!JS_SetPrivate(cx, obj, ns)) - return JS_FALSE; - ns->object = obj; - - if (argc == 1) { - if (isNamespace) { - ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, uriobj); - ns->uri = ns2->uri; - ns->prefix = ns2->prefix; - } else if (isQName && - (qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) { - ns->uri = qn->uri; - ns->prefix = qn->prefix; - } else { - ns->uri = js_ValueToString(cx, urival); - if (!ns->uri) - return JS_FALSE; - - /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */ - if (!IS_EMPTY(ns->uri)) - ns->prefix = NULL; - } - } else if (argc == 2) { - if (isQName && - (qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) { - ns->uri = qn->uri; - } else { - ns->uri = js_ValueToString(cx, urival); - if (!ns->uri) - return JS_FALSE; - } - - prefixval = argv[0]; - if (IS_EMPTY(ns->uri)) { - if (!JSVAL_IS_VOID(prefixval)) { - prefix = js_ValueToString(cx, prefixval); - if (!prefix) - return JS_FALSE; - if (!IS_EMPTY(prefix)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XML_NAMESPACE, - js_ValueToPrintableString(cx, - STRING_TO_JSVAL(prefix))); - return JS_FALSE; - } - } - } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) { - /* NULL here represents *undefined* in ECMA-357 13.2.2 4(d) etc. */ - ns->prefix = NULL; - } else { - prefix = js_ValueToString(cx, prefixval); - if (!prefix) - return JS_FALSE; - ns->prefix = prefix; - } - } - - return JS_TRUE; -} - -static JSBool -QName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval nameval, nsval; - JSBool isQName, isNamespace; - JSXMLQName *qn; - JSString *uri, *prefix, *name; - JSObject *nsobj; - JSClass *clasp; - JSXMLNamespace *ns; - - nameval = argv[argc > 1]; - isQName = - !JSVAL_IS_PRIMITIVE(nameval) && - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nameval)) == &js_QNameClass.base; - - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - /* QName called as function. */ - if (argc == 1 && isQName) { - /* QName called with one QName argument is identity. */ - *rval = nameval; - return JS_TRUE; - } - - /* - * Create and return a new QName object exactly as if constructed. - * Use the constructor's clasp so we can be shared by AttributeName - * (see below after this function). - */ - obj = js_NewObject(cx, - JS_ValueToFunction(cx, argv[-2])->clasp, - NULL, NULL); - if (!obj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - } - METER(xml_stats.qnameobj); - METER(xml_stats.liveqnameobj); - - if (isQName) { - /* If namespace is not specified and name is a QName, clone it. */ - qn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nameval)); - if (argc == 1) { - uri = qn->uri; - prefix = qn->prefix; - name = qn->localName; - goto out; - } - - /* Namespace and qname were passed -- use the qname's localName. */ - nameval = STRING_TO_JSVAL(qn->localName); - } - - if (argc == 0) { - name = cx->runtime->emptyString; - } else { - name = js_ValueToString(cx, nameval); - if (!name) - return JS_FALSE; - - /* Use argv[1] as a local root for name, even if it was not passed. */ - argv[1] = STRING_TO_JSVAL(name); - } - - nsval = argv[0]; - if (argc == 1 || JSVAL_IS_VOID(nsval)) { - if (IS_STAR(name)) { - nsval = JSVAL_NULL; - } else { - if (!js_GetDefaultXMLNamespace(cx, &nsval)) - return JS_FALSE; - } - } - - if (JSVAL_IS_NULL(nsval)) { - /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */ - uri = prefix = NULL; - } else { - /* - * Inline specialization of the Namespace constructor called with - * nsval passed as the only argument, to compute the uri and prefix - * for the constructed namespace, without actually allocating the - * object or computing other members. See ECMA-357 13.3.2 6(a) and - * 13.2.2. - */ - isNamespace = isQName = JS_FALSE; - if (!JSVAL_IS_PRIMITIVE(nsval)) { - nsobj = JSVAL_TO_OBJECT(nsval); - clasp = OBJ_GET_CLASS(cx, nsobj); - isNamespace = (clasp == &js_NamespaceClass.base); - isQName = (clasp == &js_QNameClass.base); - } -#ifdef __GNUC__ /* suppress bogus gcc warnings */ - else nsobj = NULL; -#endif - - if (isNamespace) { - ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); - uri = ns->uri; - prefix = ns->prefix; - } else if (isQName && - (qn = (JSXMLQName *) JS_GetPrivate(cx, nsobj))->uri) { - uri = qn->uri; - prefix = qn->prefix; - } else { - uri = js_ValueToString(cx, nsval); - if (!uri) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(uri); /* local root */ - - /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */ - prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL; - } - } - -out: - qn = js_NewXMLQName(cx, uri, prefix, name); - if (!qn) - return JS_FALSE; - if (!JS_SetPrivate(cx, obj, qn)) - return JS_FALSE; - qn->object = obj; - return JS_TRUE; -} - -static JSBool -AttributeName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - /* - * Since js_AttributeNameClass was initialized, obj will have that as its - * class, not js_QNameClass. - */ - return QName(cx, obj, argc, argv, rval); -} - -/* - * XMLArray library functions. - */ -static JSBool -namespace_identity(const void *a, const void *b) -{ - const JSXMLNamespace *nsa = (const JSXMLNamespace *) a; - const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; - - if (nsa->prefix && nsb->prefix) { - if (!js_EqualStrings(nsa->prefix, nsb->prefix)) - return JS_FALSE; - } else { - if (nsa->prefix || nsb->prefix) - return JS_FALSE; - } - return js_EqualStrings(nsa->uri, nsb->uri); -} - -static JSBool -attr_identity(const void *a, const void *b) -{ - const JSXML *xmla = (const JSXML *) a; - const JSXML *xmlb = (const JSXML *) b; - - return qname_identity(xmla->name, xmlb->name); -} - -static void -XMLArrayCursorInit(JSXMLArrayCursor *cursor, JSXMLArray *array) -{ - JSXMLArrayCursor *next; - - cursor->array = array; - cursor->index = 0; - next = cursor->next = array->cursors; - if (next) - next->prevp = &cursor->next; - cursor->prevp = &array->cursors; - array->cursors = cursor; - cursor->root = NULL; -} - -static void -XMLArrayCursorFinish(JSXMLArrayCursor *cursor) -{ - JSXMLArrayCursor *next; - - if (!cursor->array) - return; - next = cursor->next; - if (next) - next->prevp = cursor->prevp; - *cursor->prevp = next; - cursor->array = NULL; -} - -static void * -XMLArrayCursorNext(JSXMLArrayCursor *cursor) -{ - JSXMLArray *array; - - array = cursor->array; - if (!array || cursor->index >= array->length) - return NULL; - return cursor->root = array->vector[cursor->index++]; -} - -static void * -XMLArrayCursorItem(JSXMLArrayCursor *cursor) -{ - JSXMLArray *array; - - array = cursor->array; - if (!array || cursor->index >= array->length) - return NULL; - return cursor->root = array->vector[cursor->index]; -} - -static void -XMLArrayCursorMark(JSContext *cx, JSXMLArrayCursor *cursor) -{ - while (cursor) { - GC_MARK(cx, cursor->root, "cursor->root"); - cursor = cursor->next; - } -} - -/* NB: called with null cx from the GC, via xml_mark => XMLArrayTrim. */ -static JSBool -XMLArraySetCapacity(JSContext *cx, JSXMLArray *array, uint32 capacity) -{ - void **vector; - - if (capacity == 0) { - /* We could let realloc(p, 0) free this, but purify gets confused. */ - if (array->vector) - free(array->vector); - vector = NULL; - } else { - if ((size_t)capacity > ~(size_t)0 / sizeof(void *) || - !(vector = (void **) - realloc(array->vector, capacity * sizeof(void *)))) { - if (cx) - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - } - array->capacity = JSXML_PRESET_CAPACITY | capacity; - array->vector = vector; - return JS_TRUE; -} - -static void -XMLArrayTrim(JSXMLArray *array) -{ - if (array->capacity & JSXML_PRESET_CAPACITY) - return; - if (array->length < array->capacity) - XMLArraySetCapacity(NULL, array, array->length); -} - -static JSBool -XMLArrayInit(JSContext *cx, JSXMLArray *array, uint32 capacity) -{ - array->length = array->capacity = 0; - array->vector = NULL; - array->cursors = NULL; - return capacity == 0 || XMLArraySetCapacity(cx, array, capacity); -} - -static void -XMLArrayFinish(JSContext *cx, JSXMLArray *array) -{ - JSXMLArrayCursor *cursor; - - JS_free(cx, array->vector); - - while ((cursor = array->cursors) != NULL) - XMLArrayCursorFinish(cursor); - -#ifdef DEBUG - memset(array, 0xd5, sizeof *array); -#endif -} - -#define XML_NOT_FOUND ((uint32) -1) - -static uint32 -XMLArrayFindMember(const JSXMLArray *array, void *elt, JSIdentityOp identity) -{ - void **vector; - uint32 i, n; - - /* The identity op must not reallocate array->vector. */ - vector = array->vector; - if (identity) { - for (i = 0, n = array->length; i < n; i++) { - if (identity(vector[i], elt)) - return i; - } - } else { - for (i = 0, n = array->length; i < n; i++) { - if (vector[i] == elt) - return i; - } - } - return XML_NOT_FOUND; -} - -/* - * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after - * that, grow by LINEAR_INCREMENT. Both must be powers of two, and threshold - * should be greater than increment. - */ -#define LINEAR_THRESHOLD 256 -#define LINEAR_INCREMENT 32 - -static JSBool -XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32 index, void *elt) -{ - uint32 capacity, i; - int log2; - void **vector; - - if (index >= array->length) { - if (index >= JSXML_CAPACITY(array)) { - /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */ - capacity = index + 1; - if (index >= LINEAR_THRESHOLD) { - capacity = JS_ROUNDUP(capacity, LINEAR_INCREMENT); - } else { - JS_CEILING_LOG2(log2, capacity); - capacity = JS_BIT(log2); - } - if ((size_t)capacity > ~(size_t)0 / sizeof(void *) || - !(vector = (void **) - realloc(array->vector, capacity * sizeof(void *)))) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - array->capacity = capacity; - array->vector = vector; - for (i = array->length; i < index; i++) - vector[i] = NULL; - } - array->length = index + 1; - } - - array->vector[index] = elt; - return JS_TRUE; -} - -static JSBool -XMLArrayInsert(JSContext *cx, JSXMLArray *array, uint32 i, uint32 n) -{ - uint32 j; - JSXMLArrayCursor *cursor; - - j = array->length; - JS_ASSERT(i <= j); - if (!XMLArraySetCapacity(cx, array, j + n)) - return JS_FALSE; - - array->length = j + n; - JS_ASSERT(n != (uint32)-1); - while (j != i) { - --j; - array->vector[j + n] = array->vector[j]; - } - - for (cursor = array->cursors; cursor; cursor = cursor->next) { - if (cursor->index > i) - cursor->index += n; - } - return JS_TRUE; -} - -static void * -XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32 index, JSBool compress) -{ - uint32 length; - void **vector, *elt; - JSXMLArrayCursor *cursor; - - length = array->length; - if (index >= length) - return NULL; - - vector = array->vector; - elt = vector[index]; - if (compress) { - while (++index < length) - vector[index-1] = vector[index]; - array->length = length - 1; - array->capacity = JSXML_CAPACITY(array); - } else { - vector[index] = NULL; - } - - for (cursor = array->cursors; cursor; cursor = cursor->next) { - if (cursor->index > index) - --cursor->index; - } - return elt; -} - -static void -XMLArrayTruncate(JSContext *cx, JSXMLArray *array, uint32 length) -{ - void **vector; - - JS_ASSERT(!array->cursors); - if (length >= array->length) - return; - - if (length == 0) { - if (array->vector) - free(array->vector); - vector = NULL; - } else { - vector = realloc(array->vector, length * sizeof(void *)); - if (!vector) - return; - } - - if (array->length > length) - array->length = length; - array->capacity = length; - array->vector = vector; -} - -#define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f) -#define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, (void *)(e), f) != \ - XML_NOT_FOUND) -#define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \ - ? (t *) (a)->vector[i] \ - : NULL) -#define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \ - if ((a)->length <= (i)) \ - (a)->length = (i) + 1; \ - ((a)->vector[i] = (void *)(e)); \ - JS_END_MACRO -#define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e)) -#define XMLARRAY_INSERT(x,a,i,n) XMLArrayInsert(x, a, i, n) -#define XMLARRAY_APPEND(x,a,e) XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e)) -#define XMLARRAY_DELETE(x,a,i,c,t) ((t *) XMLArrayDelete(x, a, i, c)) -#define XMLARRAY_TRUNCATE(x,a,n) XMLArrayTruncate(x, a, n) - -/* - * Define XML setting property strings and constants early, so everyone can - * use the same names and their magic numbers (tinyids, flags). - */ -static const char js_ignoreComments_str[] = "ignoreComments"; -static const char js_ignoreProcessingInstructions_str[] - = "ignoreProcessingInstructions"; -static const char js_ignoreWhitespace_str[] = "ignoreWhitespace"; -static const char js_prettyPrinting_str[] = "prettyPrinting"; -static const char js_prettyIndent_str[] = "prettyIndent"; - -/* - * NB: These XML static property tinyids must - * (a) not collide with the generic negative tinyids at the top of jsfun.c; - * (b) index their corresponding xml_static_props array elements. - * Don't change 'em! - */ -enum xml_static_tinyid { - XML_IGNORE_COMMENTS, - XML_IGNORE_PROCESSING_INSTRUCTIONS, - XML_IGNORE_WHITESPACE, - XML_PRETTY_PRINTING, - XML_PRETTY_INDENT -}; - -static JSBool -xml_setting_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - return JS_TRUE; -} - -static JSBool -xml_setting_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSBool b; - uint8 flag; - - JS_ASSERT(JSVAL_IS_INT(id)); - if (!js_ValueToBoolean(cx, *vp, &b)) - return JS_FALSE; - - flag = JS_BIT(JSVAL_TO_INT(id)); - if (b) - cx->xmlSettingFlags |= flag; - else - cx->xmlSettingFlags &= ~flag; - return JS_TRUE; -} - -static JSPropertySpec xml_static_props[] = { - {js_ignoreComments_str, XML_IGNORE_COMMENTS, JSPROP_PERMANENT, - xml_setting_getter, xml_setting_setter}, - {js_ignoreProcessingInstructions_str, - XML_IGNORE_PROCESSING_INSTRUCTIONS, JSPROP_PERMANENT, - xml_setting_getter, xml_setting_setter}, - {js_ignoreWhitespace_str, XML_IGNORE_WHITESPACE, JSPROP_PERMANENT, - xml_setting_getter, xml_setting_setter}, - {js_prettyPrinting_str, XML_PRETTY_PRINTING, JSPROP_PERMANENT, - xml_setting_getter, xml_setting_setter}, - {js_prettyIndent_str, XML_PRETTY_INDENT, JSPROP_PERMANENT, - xml_setting_getter, NULL}, - {0,0,0,0,0} -}; - -/* Derive cx->xmlSettingFlags bits from xml_static_props tinyids. */ -#define XSF_IGNORE_COMMENTS JS_BIT(XML_IGNORE_COMMENTS) -#define XSF_IGNORE_PROCESSING_INSTRUCTIONS \ - JS_BIT(XML_IGNORE_PROCESSING_INSTRUCTIONS) -#define XSF_IGNORE_WHITESPACE JS_BIT(XML_IGNORE_WHITESPACE) -#define XSF_PRETTY_PRINTING JS_BIT(XML_PRETTY_PRINTING) -#define XSF_CACHE_VALID JS_BIT(XML_PRETTY_INDENT) - -/* - * Extra, unrelated but necessarily disjoint flag used by ParseNodeToXML. - * This flag means a couple of things: - * - * - The top JSXML created for a parse tree must have an object owning it. - * - * - That the default namespace normally inherited from the temporary - * tag that wraps a runtime-concatenated XML source - * string must, in the case of a precompiled XML object tree, inherit via - * ad-hoc code in ParseNodeToXML. - * - * Because of the second purpose, we name this flag XSF_PRECOMPILED_ROOT. - */ -#define XSF_PRECOMPILED_ROOT (XSF_CACHE_VALID << 1) - -/* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */ -#define IS_XML(str) \ - (JSSTRING_LENGTH(str) == 3 && IS_XML_CHARS(JSSTRING_CHARS(str))) - -#define IS_XMLNS(str) \ - (JSSTRING_LENGTH(str) == 5 && IS_XMLNS_CHARS(JSSTRING_CHARS(str))) - -#define IS_XML_CHARS(chars) \ - (JS_TOLOWER((chars)[0]) == 'x' && \ - JS_TOLOWER((chars)[1]) == 'm' && \ - JS_TOLOWER((chars)[2]) == 'l') - -#define HAS_NS_AFTER_XML(chars) \ - (JS_TOLOWER((chars)[3]) == 'n' && \ - JS_TOLOWER((chars)[4]) == 's') - -#define IS_XMLNS_CHARS(chars) \ - (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars)) - -#define STARTS_WITH_XML(chars,length) \ - (length >= 3 && IS_XML_CHARS(chars)) - -static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace"; -static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/"; - -static JSXMLQName * -ParseNodeToQName(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes, - JSBool isAttributeName) -{ - JSString *str, *uri, *prefix, *localName; - size_t length, offset; - const jschar *start, *limit, *colon; - uint32 n; - JSXMLNamespace *ns; - - JS_ASSERT(pn->pn_arity == PN_NULLARY); - str = ATOM_TO_STRING(pn->pn_atom); - length = JSSTRING_LENGTH(str); - start = JSSTRING_CHARS(str); - JS_ASSERT(length != 0 && *start != '@'); - JS_ASSERT(length != 1 || *start != '*'); - - uri = cx->runtime->emptyString; - limit = start + length; - colon = js_strchr_limit(start, ':', limit); - if (colon) { - offset = PTRDIFF(colon, start, jschar); - prefix = js_NewDependentString(cx, str, 0, offset, 0); - if (!prefix) - return NULL; - - if (STARTS_WITH_XML(start, offset)) { - if (offset == 3) { - uri = JS_InternString(cx, xml_namespace_str); - if (!uri) - return NULL; - } else if (offset == 5 && HAS_NS_AFTER_XML(start)) { - uri = JS_InternString(cx, xmlns_namespace_str); - if (!uri) - return NULL; - } else { - uri = NULL; - } - } else { - uri = NULL; - n = inScopeNSes->length; - while (n != 0) { - --n; - ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace); - if (ns->prefix && js_EqualStrings(ns->prefix, prefix)) { - uri = ns->uri; - break; - } - } - } - - if (!uri) { - js_ReportCompileErrorNumber(cx, pn, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_BAD_XML_NAMESPACE, - js_ValueToPrintableString(cx, - STRING_TO_JSVAL(prefix))); - return NULL; - } - - localName = js_NewStringCopyN(cx, colon + 1, length - (offset + 1), 0); - if (!localName) - return NULL; - } else { - if (isAttributeName) { - /* - * An unprefixed attribute is not in any namespace, so set prefix - * as well as uri to the empty string. - */ - prefix = uri; - } else { - /* - * Loop from back to front looking for the closest declared default - * namespace. - */ - n = inScopeNSes->length; - while (n != 0) { - --n; - ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace); - if (!ns->prefix || IS_EMPTY(ns->prefix)) { - uri = ns->uri; - break; - } - } - prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL; - } - localName = str; - } - - return js_NewXMLQName(cx, uri, prefix, localName); -} - -static JSString * -ChompXMLWhitespace(JSContext *cx, JSString *str) -{ - size_t length, newlength, offset; - const jschar *cp, *start, *end; - jschar c; - - length = JSSTRING_LENGTH(str); - for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { - c = *cp; - if (!JS_ISXMLSPACE(c)) - break; - } - while (end > cp) { - c = end[-1]; - if (!JS_ISXMLSPACE(c)) - break; - --end; - } - newlength = PTRDIFF(end, cp, jschar); - if (newlength == length) - return str; - offset = PTRDIFF(cp, start, jschar); - return js_NewDependentString(cx, str, offset, newlength, 0); -} - -static JSXML * -ParseNodeToXML(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes, - uintN flags) -{ - JSXML *xml, *kid, *attr, *attrj; - JSString *str; - uint32 length, n, i, j; - JSParseNode *pn2, *pn3, *head, **pnp; - JSXMLNamespace *ns; - JSXMLQName *qn, *attrjqn; - JSXMLClass xml_class; - int stackDummy; - - if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { - js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, - JSMSG_OVER_RECURSED); - return NULL; - } - -#define PN2X_SKIP_CHILD ((JSXML *) 1) - - /* - * Cases return early to avoid common code that gets an outermost xml's - * object, which protects GC-things owned by xml and its descendants from - * garbage collection. - */ - xml = NULL; - if (!js_EnterLocalRootScope(cx)) - return NULL; - switch (pn->pn_type) { - case TOK_XMLELEM: - length = inScopeNSes->length; - pn2 = pn->pn_head; - xml = ParseNodeToXML(cx, pn2, inScopeNSes, flags); - if (!xml) - goto fail; - - flags &= ~XSF_PRECOMPILED_ROOT; - n = pn->pn_count; - JS_ASSERT(n >= 2); - n -= 2; - if (!XMLArraySetCapacity(cx, &xml->xml_kids, n)) - goto fail; - - i = 0; - while ((pn2 = pn2->pn_next) != NULL) { - if (!pn2->pn_next) { - /* Don't append the end tag! */ - JS_ASSERT(pn2->pn_type == TOK_XMLETAGO); - break; - } - - if ((flags & XSF_IGNORE_WHITESPACE) && - n > 1 && pn2->pn_type == TOK_XMLSPACE) { - --n; - continue; - } - - kid = ParseNodeToXML(cx, pn2, inScopeNSes, flags); - if (kid == PN2X_SKIP_CHILD) { - --n; - continue; - } - - if (!kid) - goto fail; - - /* Store kid in xml right away, to protect it from GC. */ - XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid); - kid->parent = xml; - ++i; - - /* XXX where is this documented in an XML spec, or in E4X? */ - if ((flags & XSF_IGNORE_WHITESPACE) && - n > 1 && kid->xml_class == JSXML_CLASS_TEXT) { - str = ChompXMLWhitespace(cx, kid->xml_value); - if (!str) - goto fail; - kid->xml_value = str; - } - } - - JS_ASSERT(i == n); - if (n < pn->pn_count - 2) - XMLArrayTrim(&xml->xml_kids); - XMLARRAY_TRUNCATE(cx, inScopeNSes, length); - break; - - case TOK_XMLLIST: - xml = js_NewXML(cx, JSXML_CLASS_LIST); - if (!xml) - goto fail; - - n = pn->pn_count; - if (!XMLArraySetCapacity(cx, &xml->xml_kids, n)) - goto fail; - - i = 0; - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - /* - * Always ignore insignificant whitespace in lists -- we shouldn't - * condition this on an XML.ignoreWhitespace setting when the list - * constructor is XMLList (note XML/XMLList unification hazard). - */ - if (pn2->pn_type == TOK_XMLSPACE) { - --n; - continue; - } - - kid = ParseNodeToXML(cx, pn2, inScopeNSes, flags); - if (kid == PN2X_SKIP_CHILD) { - --n; - continue; - } - - if (!kid) - goto fail; - - XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid); - ++i; - } - - if (n < pn->pn_count) - XMLArrayTrim(&xml->xml_kids); - break; - - case TOK_XMLSTAGO: - case TOK_XMLPTAGC: - length = inScopeNSes->length; - pn2 = pn->pn_head; - JS_ASSERT(pn2->pn_type == TOK_XMLNAME); - if (pn2->pn_arity == PN_LIST) - goto syntax; - - xml = js_NewXML(cx, JSXML_CLASS_ELEMENT); - if (!xml) - goto fail; - - /* First pass: check syntax and process namespace declarations. */ - JS_ASSERT(pn->pn_count >= 1); - n = pn->pn_count - 1; - pnp = &pn2->pn_next; - head = *pnp; - while ((pn2 = *pnp) != NULL) { - size_t length; - const jschar *chars; - - if (pn2->pn_type != TOK_XMLNAME || pn2->pn_arity != PN_NULLARY) - goto syntax; - - /* Enforce "Well-formedness constraint: Unique Att Spec". */ - for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) { - if (pn3->pn_atom == pn2->pn_atom) { - js_ReportCompileErrorNumber(cx, pn2, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_DUPLICATE_XML_ATTR, - js_ValueToPrintableString(cx, - ATOM_KEY(pn2->pn_atom))); - goto fail; - } - } - - str = ATOM_TO_STRING(pn2->pn_atom); - pn2 = pn2->pn_next; - JS_ASSERT(pn2); - if (pn2->pn_type != TOK_XMLATTR) - goto syntax; - - length = JSSTRING_LENGTH(str); - chars = JSSTRING_CHARS(str); - if (length >= 5 && - IS_XMLNS_CHARS(chars) && - (length == 5 || chars[5] == ':')) { - JSString *uri, *prefix; - - uri = ATOM_TO_STRING(pn2->pn_atom); - if (length == 5) { - /* 10.3.2.1. Step 6(h)(i)(1)(a). */ - prefix = cx->runtime->emptyString; - } else { - prefix = js_NewStringCopyN(cx, chars + 6, length - 6, 0); - if (!prefix) - goto fail; - } - - /* - * Once the new ns is appended to xml->xml_namespaces, it is - * protected from GC by the object that owns xml -- which is - * either xml->object if outermost, or the object owning xml's - * oldest ancestor if !outermost. - */ - ns = js_NewXMLNamespace(cx, prefix, uri, JS_TRUE); - if (!ns) - goto fail; - - /* - * Don't add a namespace that's already in scope. If someone - * extracts a child property from its parent via [[Get]], then - * we enforce the invariant, noted many times in ECMA-357, that - * the child's namespaces form a possibly-improper superset of - * its ancestors' namespaces. - */ - if (!XMLARRAY_HAS_MEMBER(inScopeNSes, ns, namespace_identity)) { - if (!XMLARRAY_APPEND(cx, inScopeNSes, ns) || - !XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) { - goto fail; - } - } - - JS_ASSERT(n >= 2); - n -= 2; - *pnp = pn2->pn_next; - /* XXXbe recycle pn2 */ - continue; - } - - pnp = &pn2->pn_next; - } - - /* - * If called from js_ParseNodeToXMLObject, emulate the effect of the - * ... wrapping done by "ToXML Applied to - * the String Type" (ECMA-357 10.3.1). - */ - if (flags & XSF_PRECOMPILED_ROOT) { - JS_ASSERT(length >= 1); - ns = XMLARRAY_MEMBER(inScopeNSes, 0, JSXMLNamespace); - JS_ASSERT(!XMLARRAY_HAS_MEMBER(&xml->xml_namespaces, ns, - namespace_identity)); - ns = js_NewXMLNamespace(cx, ns->prefix, ns->uri, JS_FALSE); - if (!ns) - goto fail; - if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) - goto fail; - } - XMLArrayTrim(&xml->xml_namespaces); - - /* Second pass: process tag name and attributes, using namespaces. */ - pn2 = pn->pn_head; - qn = ParseNodeToQName(cx, pn2, inScopeNSes, JS_FALSE); - if (!qn) - goto fail; - xml->name = qn; - - JS_ASSERT((n & 1) == 0); - n >>= 1; - if (!XMLArraySetCapacity(cx, &xml->xml_attrs, n)) - goto fail; - - for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) { - qn = ParseNodeToQName(cx, pn2, inScopeNSes, JS_TRUE); - if (!qn) { - xml->xml_attrs.length = i; - goto fail; - } - - /* - * Enforce "Well-formedness constraint: Unique Att Spec", part 2: - * this time checking local name and namespace URI. - */ - for (j = 0; j < i; j++) { - attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML); - attrjqn = attrj->name; - if (js_EqualStrings(attrjqn->uri, qn->uri) && - js_EqualStrings(attrjqn->localName, qn->localName)) { - js_ReportCompileErrorNumber(cx, pn2, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_DUPLICATE_XML_ATTR, - js_ValueToPrintableString(cx, - ATOM_KEY(pn2->pn_atom))); - goto fail; - } - } - - pn2 = pn2->pn_next; - JS_ASSERT(pn2); - JS_ASSERT(pn2->pn_type == TOK_XMLATTR); - - attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); - if (!attr) - goto fail; - - XMLARRAY_SET_MEMBER(&xml->xml_attrs, i, attr); - attr->parent = xml; - attr->name = qn; - attr->xml_value = ATOM_TO_STRING(pn2->pn_atom); - } - - /* Point tag closes its own namespace scope. */ - if (pn->pn_type == TOK_XMLPTAGC) - XMLARRAY_TRUNCATE(cx, inScopeNSes, length); - break; - - case TOK_XMLSPACE: - case TOK_XMLTEXT: - case TOK_XMLCDATA: - case TOK_XMLCOMMENT: - case TOK_XMLPI: - str = ATOM_TO_STRING(pn->pn_atom); - qn = NULL; - if (pn->pn_type == TOK_XMLCOMMENT) { - if (flags & XSF_IGNORE_COMMENTS) - goto skip_child; - xml_class = JSXML_CLASS_COMMENT; - } else if (pn->pn_type == TOK_XMLPI) { - if (IS_XML(str)) { - js_ReportCompileErrorNumber(cx, pn, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_RESERVED_ID, - js_ValueToPrintableString(cx, - STRING_TO_JSVAL(str))); - goto fail; - } - - if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) - goto skip_child; - - qn = ParseNodeToQName(cx, pn, inScopeNSes, JS_FALSE); - if (!qn) - goto fail; - - str = pn->pn_atom2 - ? ATOM_TO_STRING(pn->pn_atom2) - : cx->runtime->emptyString; - xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION; - } else { - /* CDATA section content, or element text. */ - xml_class = JSXML_CLASS_TEXT; - } - - xml = js_NewXML(cx, xml_class); - if (!xml) - goto fail; - xml->name = qn; - if (pn->pn_type == TOK_XMLSPACE) - xml->xml_flags |= XMLF_WHITESPACE_TEXT; - xml->xml_value = str; - break; - - default: - goto syntax; - } - - js_LeaveLocalRootScopeWithResult(cx, (jsval) xml); - if ((flags & XSF_PRECOMPILED_ROOT) && !js_GetXMLObject(cx, xml)) - return NULL; - return xml; - -skip_child: - js_LeaveLocalRootScope(cx); - return PN2X_SKIP_CHILD; - -#undef PN2X_SKIP_CHILD - -syntax: - js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, - JSMSG_BAD_XML_MARKUP); -fail: - js_LeaveLocalRootScope(cx); - return NULL; -} - -/* - * XML helper, object-ops, and library functions. We start with the helpers, - * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers. - */ -static JSBool -GetXMLSetting(JSContext *cx, const char *name, jsval *vp) -{ - jsval v; - - if (!js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_XML), &v)) - return JS_FALSE; - if (!VALUE_IS_FUNCTION(cx, v)) { - *vp = JSVAL_VOID; - return JS_TRUE; - } - return JS_GetProperty(cx, JSVAL_TO_OBJECT(v), name, vp); -} - -static JSBool -FillSettingsCache(JSContext *cx) -{ - int i; - const char *name; - jsval v; - JSBool isSet; - - /* Note: XML_PRETTY_INDENT is not a boolean setting. */ - for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { - name = xml_static_props[i].name; - if (!GetXMLSetting(cx, name, &v) || !js_ValueToBoolean(cx, v, &isSet)) - return JS_FALSE; - if (isSet) - cx->xmlSettingFlags |= JS_BIT(i); - else - cx->xmlSettingFlags &= ~JS_BIT(i); - } - - cx->xmlSettingFlags |= XSF_CACHE_VALID; - return JS_TRUE; -} - -static JSBool -GetBooleanXMLSetting(JSContext *cx, const char *name, JSBool *bp) -{ - int i; - - if (!(cx->xmlSettingFlags & XSF_CACHE_VALID) && !FillSettingsCache(cx)) - return JS_FALSE; - - for (i = 0; xml_static_props[i].name; i++) { - if (!strcmp(xml_static_props[i].name, name)) { - *bp = (cx->xmlSettingFlags & JS_BIT(i)) != 0; - return JS_TRUE; - } - } - *bp = JS_FALSE; - return JS_TRUE; -} - -static JSBool -GetUint32XMLSetting(JSContext *cx, const char *name, uint32 *uip) -{ - jsval v; - - return GetXMLSetting(cx, name, &v) && js_ValueToECMAUint32(cx, v, uip); -} - -static JSBool -GetXMLSettingFlags(JSContext *cx, uintN *flagsp) -{ - JSBool flag; - - /* Just get the first flag to validate the setting flags cache. */ - if (!GetBooleanXMLSetting(cx, js_ignoreComments_str, &flag)) - return JS_FALSE; - *flagsp = cx->xmlSettingFlags; - return JS_TRUE; -} - -static JSXML * -ParseXMLSource(JSContext *cx, JSString *src) -{ - jsval nsval; - JSXMLNamespace *ns; - size_t urilen, srclen, length, offset, dstlen; - jschar *chars; - const jschar *srcp, *endp; - void *mark; - JSTokenStream *ts; - uintN lineno; - JSStackFrame *fp; - JSOp op; - JSParseNode *pn; - JSXML *xml; - JSXMLArray nsarray; - uintN flags; - - static const char prefix[] = ""; - static const char suffix[] = ""; - -#define constrlen(constr) (sizeof(constr) - 1) - - if (!js_GetDefaultXMLNamespace(cx, &nsval)) - return NULL; - ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); - - urilen = JSSTRING_LENGTH(ns->uri); - srclen = JSSTRING_LENGTH(src); - length = constrlen(prefix) + urilen + constrlen(middle) + srclen + - constrlen(suffix); - - chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); - if (!chars) - return NULL; - - dstlen = length; - js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen); - offset = dstlen; - js_strncpy(chars + offset, JSSTRING_CHARS(ns->uri), urilen); - offset += urilen; - dstlen = length - offset + 1; - js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset, - &dstlen); - offset += dstlen; - srcp = JSSTRING_CHARS(src); - js_strncpy(chars + offset, srcp, srclen); - offset += srclen; - dstlen = length - offset + 1; - js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset, - &dstlen); - chars [offset + dstlen] = 0; - - mark = JS_ARENA_MARK(&cx->tempPool); - ts = js_NewBufferTokenStream(cx, chars, length); - if (!ts) - return NULL; - for (fp = cx->fp; fp && !fp->pc; fp = fp->down) - continue; - if (fp) { - op = (JSOp) *fp->pc; - if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) { - ts->filename = fp->script->filename; - lineno = js_PCToLineNumber(cx, fp->script, fp->pc); - for (endp = srcp + srclen; srcp < endp; srcp++) - if (*srcp == '\n') - --lineno; - ts->lineno = lineno; - } - } - - JS_KEEP_ATOMS(cx->runtime); - pn = js_ParseXMLTokenStream(cx, cx->fp->scopeChain, ts, JS_FALSE); - xml = NULL; - if (pn && XMLArrayInit(cx, &nsarray, 1)) { - if (GetXMLSettingFlags(cx, &flags)) - xml = ParseNodeToXML(cx, pn, &nsarray, flags); - - XMLArrayFinish(cx, &nsarray); - } - JS_UNKEEP_ATOMS(cx->runtime); - - JS_ARENA_RELEASE(&cx->tempPool, mark); - JS_free(cx, chars); - return xml; - -#undef constrlen -} - -/* - * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least). - * - * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce - * the constraint: - * - * for all x belonging to XML: - * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]] - * - * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here - * (in new sub-step 6(a), renumbering the others to (b) and (c)). - * - * Same goes for 10.4.1 Step 7(a). - * - * In order for XML.prototype.namespaceDeclarations() to work correctly, the - * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be - * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such - * undeclared namespaces associated with x not belonging to ancestorNS. - */ -static JSXML * -OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i) -{ - JSXMLNamespace *ns; - - ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSXMLNamespace); - xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (!ns || !xml) - return xml; - if (xml->xml_class == JSXML_CLASS_ELEMENT) { - if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) - return NULL; - ns->declared = JS_FALSE; - } - xml->parent = NULL; - return xml; -} - -static JSObject * -ToXML(JSContext *cx, jsval v) -{ - JSObject *obj; - JSXML *xml; - JSClass *clasp; - JSString *str; - uint32 length; - - if (JSVAL_IS_PRIMITIVE(v)) { - if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) - goto bad; - } else { - obj = JSVAL_TO_OBJECT(v); - if (OBJECT_IS_XML(cx, obj)) { - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (xml->xml_class == JSXML_CLASS_LIST) { - if (xml->xml_kids.length != 1) - goto bad; - xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (xml) { - JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); - return js_GetXMLObject(cx, xml); - } - } - return obj; - } - - clasp = OBJ_GET_CLASS(cx, obj); - if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) { - JS_ASSERT(0); - } - - if (clasp != &js_StringClass && - clasp != &js_NumberClass && - clasp != &js_BooleanClass) { - goto bad; - } - } - - str = js_ValueToString(cx, v); - if (!str) - return NULL; - if (IS_EMPTY(str)) { - length = 0; -#ifdef __GNUC__ /* suppress bogus gcc warnings */ - xml = NULL; -#endif - } else { - xml = ParseXMLSource(cx, str); - if (!xml) - return NULL; - length = JSXML_LENGTH(xml); - } - - if (length == 0) { - obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT); - if (!obj) - return NULL; - } else if (length == 1) { - xml = OrphanXMLChild(cx, xml, 0); - if (!xml) - return NULL; - obj = js_GetXMLObject(cx, xml); - if (!obj) - return NULL; - } else { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR); - return NULL; - } - return obj; - -bad: - str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XML_CONVERSION, - JS_GetStringBytes(str)); - } - return NULL; -} - -static JSBool -Append(JSContext *cx, JSXML *list, JSXML *kid); - -static JSObject * -ToXMLList(JSContext *cx, jsval v) -{ - JSObject *obj, *listobj; - JSXML *xml, *list, *kid; - JSClass *clasp; - JSString *str; - uint32 i, length; - - if (JSVAL_IS_PRIMITIVE(v)) { - if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) - goto bad; - } else { - obj = JSVAL_TO_OBJECT(v); - if (OBJECT_IS_XML(cx, obj)) { - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (xml->xml_class != JSXML_CLASS_LIST) { - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) - return NULL; - list = (JSXML *) JS_GetPrivate(cx, listobj); - if (!Append(cx, list, xml)) - return NULL; - return listobj; - } - return obj; - } - - clasp = OBJ_GET_CLASS(cx, obj); - if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) { - JS_ASSERT(0); - } - - if (clasp != &js_StringClass && - clasp != &js_NumberClass && - clasp != &js_BooleanClass) { - goto bad; - } - } - - str = js_ValueToString(cx, v); - if (!str) - return NULL; - if (IS_EMPTY(str)) { - xml = NULL; - length = 0; - } else { - if (!js_EnterLocalRootScope(cx)) - return NULL; - xml = ParseXMLSource(cx, str); - if (!xml) { - js_LeaveLocalRootScope(cx); - return NULL; - } - length = JSXML_LENGTH(xml); - } - - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (listobj) { - list = (JSXML *) JS_GetPrivate(cx, listobj); - for (i = 0; i < length; i++) { - kid = OrphanXMLChild(cx, xml, i); - if (!kid || !Append(cx, list, kid)) { - listobj = NULL; - break; - } - } - } - - if (xml) - js_LeaveLocalRootScopeWithResult(cx, (jsval) listobj); - return listobj; - -bad: - str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XMLLIST_CONVERSION, - JS_GetStringBytes(str)); - } - return NULL; -} - -/* - * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString - * and their library-public js_* counterparts. The guts of MakeXMLCDataString, - * MakeXMLCommentString, and MakeXMLPIString are further factored into a common - * MakeXMLSpecialString subroutine. - * - * These functions take ownership of sb->base, if sb is non-null, in all cases - * of success or failure. - */ -static JSString * -MakeXMLSpecialString(JSContext *cx, JSStringBuffer *sb, - JSString *str, JSString *str2, - const jschar *prefix, size_t prefixlength, - const jschar *suffix, size_t suffixlength) -{ - JSStringBuffer localSB; - size_t length, length2, newlength; - jschar *bp, *base; - - if (!sb) { - sb = &localSB; - js_InitStringBuffer(sb); - } - - length = JSSTRING_LENGTH(str); - length2 = str2 ? JSSTRING_LENGTH(str2) : 0; - newlength = STRING_BUFFER_OFFSET(sb) + - prefixlength + length + ((length2 != 0) ? 1 + length2 : 0) + - suffixlength; - bp = base = (jschar *) - JS_realloc(cx, sb->base, (newlength + 1) * sizeof(jschar)); - if (!bp) { - js_FinishStringBuffer(sb); - return NULL; - } - - bp += STRING_BUFFER_OFFSET(sb); - js_strncpy(bp, prefix, prefixlength); - bp += prefixlength; - js_strncpy(bp, JSSTRING_CHARS(str), length); - bp += length; - if (length2 != 0) { - *bp++ = (jschar) ' '; - js_strncpy(bp, JSSTRING_CHARS(str2), length2); - bp += length2; - } - js_strncpy(bp, suffix, suffixlength); - bp[suffixlength] = 0; - - str = js_NewString(cx, base, newlength, 0); - if (!str) - free(base); - return str; -} - -static JSString * -MakeXMLCDATAString(JSContext *cx, JSStringBuffer *sb, JSString *str) -{ - static const jschar cdata_prefix_ucNstr[] = {'<', '!', '[', - 'C', 'D', 'A', 'T', 'A', - '['}; - static const jschar cdata_suffix_ucNstr[] = {']', ']', '>'}; - - return MakeXMLSpecialString(cx, sb, str, NULL, - cdata_prefix_ucNstr, 9, - cdata_suffix_ucNstr, 3); -} - -static JSString * -MakeXMLCommentString(JSContext *cx, JSStringBuffer *sb, JSString *str) -{ - static const jschar comment_prefix_ucNstr[] = {'<', '!', '-', '-'}; - static const jschar comment_suffix_ucNstr[] = {'-', '-', '>'}; - - return MakeXMLSpecialString(cx, sb, str, NULL, - comment_prefix_ucNstr, 4, - comment_suffix_ucNstr, 3); -} - -static JSString * -MakeXMLPIString(JSContext *cx, JSStringBuffer *sb, JSString *name, - JSString *value) -{ - static const jschar pi_prefix_ucNstr[] = {'<', '?'}; - static const jschar pi_suffix_ucNstr[] = {'?', '>'}; - - return MakeXMLSpecialString(cx, sb, name, value, - pi_prefix_ucNstr, 2, - pi_suffix_ucNstr, 2); -} - -/* - * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends - * equals, a double quote, an attribute value, and a closing double quote. - */ -static void -AppendAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *valstr) -{ - js_AppendCString(sb, "=\""); - valstr = js_EscapeAttributeValue(cx, valstr); - if (!valstr) { - free(sb->base); - sb->base = STRING_BUFFER_ERROR_BASE; - return; - } - js_AppendJSString(sb, valstr); - js_AppendChar(sb, '"'); -} - -/* - * ECMA-357 10.2.1.1 EscapeElementValue helper method. - * - * This function takes ownership of sb->base, if sb is non-null, in all cases - * of success or failure. - */ -static JSString * -EscapeElementValue(JSContext *cx, JSStringBuffer *sb, JSString *str) -{ - size_t length, newlength; - const jschar *cp, *start, *end; - jschar c; - - length = newlength = JSSTRING_LENGTH(str); - for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { - c = *cp; - if (c == '<' || c == '>') - newlength += 3; - else if (c == '&') - newlength += 4; - - if (newlength < length) { - JS_ReportOutOfMemory(cx); - return NULL; - } - } - if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) { - JSStringBuffer localSB; - if (!sb) { - sb = &localSB; - js_InitStringBuffer(sb); - } - if (!sb->grow(sb, newlength)) { - JS_ReportOutOfMemory(cx); - return NULL; - } - for (cp = start; cp < end; cp++) { - c = *cp; - if (c == '<') - js_AppendCString(sb, js_lt_entity_str); - else if (c == '>') - js_AppendCString(sb, js_gt_entity_str); - else if (c == '&') - js_AppendCString(sb, js_amp_entity_str); - else - js_AppendChar(sb, c); - } - JS_ASSERT(STRING_BUFFER_OK(sb)); - str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0); - if (!str) - js_FinishStringBuffer(sb); - } - return str; -} - -/* - * ECMA-357 10.2.1.2 EscapeAttributeValue helper method. - * This function takes ownership of sb->base, if sb is non-null, in all cases. - */ -static JSString * -EscapeAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *str) -{ - size_t length, newlength; - const jschar *cp, *start, *end; - jschar c; - - length = newlength = JSSTRING_LENGTH(str); - for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { - c = *cp; - if (c == '"') - newlength += 5; - else if (c == '<') - newlength += 3; - else if (c == '&' || c == '\n' || c == '\r' || c == '\t') - newlength += 4; - - if (newlength < length) { - JS_ReportOutOfMemory(cx); - return NULL; - } - } - if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) { - JSStringBuffer localSB; - if (!sb) { - sb = &localSB; - js_InitStringBuffer(sb); - } - if (!sb->grow(sb, newlength)) { - JS_ReportOutOfMemory(cx); - return NULL; - } - for (cp = start; cp < end; cp++) { - c = *cp; - if (c == '"') - js_AppendCString(sb, js_quot_entity_str); - else if (c == '<') - js_AppendCString(sb, js_lt_entity_str); - else if (c == '&') - js_AppendCString(sb, js_amp_entity_str); - else if (c == '\n') - js_AppendCString(sb, " "); - else if (c == '\r') - js_AppendCString(sb, " "); - else if (c == '\t') - js_AppendCString(sb, " "); - else - js_AppendChar(sb, c); - } - JS_ASSERT(STRING_BUFFER_OK(sb)); - str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0); - if (!str) - js_FinishStringBuffer(sb); - } - return str; -} - -/* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */ -static JSXMLNamespace * -GetNamespace(JSContext *cx, JSXMLQName *qn, const JSXMLArray *inScopeNSes) -{ - JSXMLNamespace *match, *ns; - uint32 i, n; - jsval argv[2]; - JSObject *nsobj; - - JS_ASSERT(qn->uri); - if (!qn->uri) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XML_NAMESPACE, - qn->prefix - ? js_ValueToPrintableString(cx, - STRING_TO_JSVAL(qn->prefix)) - : js_type_strs[JSTYPE_VOID]); - return NULL; - } - - /* Look for a matching namespace in inScopeNSes, if provided. */ - match = NULL; - if (inScopeNSes) { - for (i = 0, n = inScopeNSes->length; i < n; i++) { - ns = XMLARRAY_MEMBER(inScopeNSes, i, JSXMLNamespace); - if (!ns) - continue; - - /* - * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4: - * If we preserve prefixes, we must match null qn->prefix against - * an empty ns->prefix, in order to avoid generating redundant - * prefixed and default namespaces for cases such as: - * - * x = - * print(x.toXMLString()); - * - * Per 10.3.2.1, the namespace attribute in t has an empty string - * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1): - * - * 1. If the [local name] property of a is "xmlns" - * a. Map ns.prefix to the empty string - * - * But t's name has a null prefix in this implementation, meaning - * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to - * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without - * saying how "no value" maps to an ECMA-357 value -- but it must - * map to the *undefined* prefix value). - * - * Since "" != undefined (or null, in the current implementation) - * the ECMA-357 spec will fail to match in [[GetNamespace]] called - * on t with argument {} U {(prefix="", uri="http://foo.com")}. - * This spec bug leads to ToXMLString results that duplicate the - * declared namespace. - */ - if (js_EqualStrings(ns->uri, qn->uri) && - (ns->prefix == qn->prefix || - ((ns->prefix && qn->prefix) - ? js_EqualStrings(ns->prefix, qn->prefix) - : IS_EMPTY(ns->prefix ? ns->prefix : qn->prefix)))) { - match = ns; - break; - } - } - } - - /* If we didn't match, make a new namespace from qn. */ - if (!match) { - argv[0] = qn->prefix ? STRING_TO_JSVAL(qn->prefix) : JSVAL_VOID; - argv[1] = STRING_TO_JSVAL(qn->uri); - nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL, - 2, argv); - if (!nsobj) - return NULL; - match = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); - } - return match; -} - -static JSString * -GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls) -{ - const jschar *cp, *start, *end; - size_t length, newlength, offset; - uint32 i, n, m, serial; - jschar *bp, *dp; - JSBool done; - JSXMLNamespace *ns; - JSString *prefix; - - JS_ASSERT(!IS_EMPTY(uri)); - - /* - * If there are no *declared* namespaces, skip all collision detection and - * return a short prefix quickly; an example of such a situation: - * - * var x = ; - * var n = new Namespace("http://example.com/"); - * x.@n::att = "val"; - * x.toXMLString(); - * - * This is necessary for various log10 uses below to be valid. - */ - if (decls->length == 0) - return JS_NewStringCopyZ(cx, "a"); - - /* - * Try peeling off the last filename suffix or pathname component till - * we have a valid XML name. This heuristic will prefer "xul" given - * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any - * likely URI of the form ".../xbl2/2005". - */ - start = JSSTRING_CHARS(uri); - cp = end = start + JSSTRING_LENGTH(uri); - while (--cp > start) { - if (*cp == '.' || *cp == '/' || *cp == ':') { - ++cp; - length = PTRDIFF(end, cp, jschar); - if (IsXMLName(cp, length) && !STARTS_WITH_XML(cp, length)) - break; - end = --cp; - } - } - length = PTRDIFF(end, cp, jschar); - - /* - * If the namespace consisted only of non-XML names or names that begin - * case-insensitively with "xml", arbitrarily create a prefix consisting - * of 'a's of size length (allowing dp-calculating code to work with or - * without this branch executing) plus the space for storing a hyphen and - * the serial number (avoiding reallocation if a collision happens). - */ - bp = (jschar *) cp; - newlength = length; - if (STARTS_WITH_XML(cp, length) || !IsXMLName(cp, length)) { - newlength = length + 2 + (size_t) log10(decls->length); - bp = (jschar *) - JS_malloc(cx, (newlength + 1) * sizeof(jschar)); - if (!bp) - return NULL; - - bp[newlength] = 0; - for (i = 0; i < newlength; i++) - bp[i] = 'a'; - } - - /* - * Now search through decls looking for a collision. If we collide with - * an existing prefix, start tacking on a hyphen and a serial number. - */ - serial = 0; - do { - done = JS_TRUE; - for (i = 0, n = decls->length; i < n; i++) { - ns = XMLARRAY_MEMBER(decls, i, JSXMLNamespace); - if (ns && ns->prefix && - JSSTRING_LENGTH(ns->prefix) == newlength && - !memcmp(JSSTRING_CHARS(ns->prefix), bp, - newlength * sizeof(jschar))) { - if (bp == cp) { - newlength = length + 2 + (size_t) log10(n); - bp = (jschar *) - JS_malloc(cx, (newlength + 1) * sizeof(jschar)); - if (!bp) - return NULL; - js_strncpy(bp, cp, length); - } - - ++serial; - JS_ASSERT(serial <= n); - dp = bp + length + 2 + (size_t) log10(serial); - *dp = 0; - for (m = serial; m != 0; m /= 10) - *--dp = (jschar)('0' + m % 10); - *--dp = '-'; - JS_ASSERT(dp == bp + length); - - done = JS_FALSE; - break; - } - } - } while (!done); - - if (bp == cp) { - offset = PTRDIFF(cp, start, jschar); - prefix = js_NewDependentString(cx, uri, offset, length, 0); - } else { - prefix = js_NewString(cx, bp, newlength, 0); - if (!prefix) - JS_free(cx, bp); - } - return prefix; -} - -static JSBool -namespace_match(const void *a, const void *b) -{ - const JSXMLNamespace *nsa = (const JSXMLNamespace *) a; - const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; - - if (nsb->prefix) - return nsa->prefix && js_EqualStrings(nsa->prefix, nsb->prefix); - return js_EqualStrings(nsa->uri, nsb->uri); -} - -/* ECMA-357 10.2.1 and 10.2.2 */ -static JSString * -XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, - uintN indentLevel) -{ - JSBool pretty, indentKids; - JSStringBuffer sb; - JSString *str, *prefix, *kidstr; - JSXMLArrayCursor cursor; - uint32 i, n; - JSXMLArray empty, decls, ancdecls; - JSXMLNamespace *ns, *ns2; - uintN nextIndentLevel; - JSXML *attr, *kid; - - if (!GetBooleanXMLSetting(cx, js_prettyPrinting_str, &pretty)) - return NULL; - - js_InitStringBuffer(&sb); - if (pretty) - js_RepeatChar(&sb, ' ', indentLevel); - str = NULL; - - switch (xml->xml_class) { - case JSXML_CLASS_TEXT: - /* Step 4. */ - if (pretty) { - str = ChompXMLWhitespace(cx, xml->xml_value); - if (!str) - return NULL; - } else { - str = xml->xml_value; - } - return EscapeElementValue(cx, &sb, str); - - case JSXML_CLASS_ATTRIBUTE: - /* Step 5. */ - return EscapeAttributeValue(cx, &sb, xml->xml_value); - - case JSXML_CLASS_COMMENT: - /* Step 6. */ - return MakeXMLCommentString(cx, &sb, xml->xml_value); - - case JSXML_CLASS_PROCESSING_INSTRUCTION: - /* Step 7. */ - return MakeXMLPIString(cx, &sb, xml->name->localName, xml->xml_value); - - case JSXML_CLASS_LIST: - /* ECMA-357 10.2.2. */ - XMLArrayCursorInit(&cursor, &xml->xml_kids); - i = 0; - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (pretty && i != 0) - js_AppendChar(&sb, '\n'); - - kidstr = XMLToXMLString(cx, kid, ancestorNSes, indentLevel); - if (!kidstr) - break; - - js_AppendJSString(&sb, kidstr); - ++i; - } - XMLArrayCursorFinish(&cursor); - if (kid) - goto list_out; - - if (!sb.base) { - if (!STRING_BUFFER_OK(&sb)) { - JS_ReportOutOfMemory(cx); - return NULL; - } - return cx->runtime->emptyString; - } - - str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0); - list_out: - if (!str) - js_FinishStringBuffer(&sb); - return str; - - default:; - } - - /* After this point, control must flow through label out: to exit. */ - if (!js_EnterLocalRootScope(cx)) - return NULL; - - /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */ - if (!ancestorNSes) { - XMLArrayInit(cx, &empty, 0); - ancestorNSes = ∅ - } - XMLArrayInit(cx, &decls, 0); - ancdecls.capacity = 0; - - /* Clone in-scope namespaces not in ancestorNSes into decls. */ - XMLArrayCursorInit(&cursor, &xml->xml_namespaces); - while ((ns = (JSXMLNamespace *) XMLArrayCursorNext(&cursor)) != NULL) { - if (!ns->declared) - continue; - if (!XMLARRAY_HAS_MEMBER(ancestorNSes, ns, namespace_identity)) { - /* NOTE: may want to exclude unused namespaces here. */ - ns2 = js_NewXMLNamespace(cx, ns->prefix, ns->uri, JS_TRUE); - if (!ns2 || !XMLARRAY_APPEND(cx, &decls, ns2)) - break; - } - } - XMLArrayCursorFinish(&cursor); - if (ns) - goto out; - - /* - * Union ancestorNSes and decls into ancdecls. Note that ancdecls does - * not own its member references. In the spec, ancdecls has no name, but - * is always written out as (AncestorNamespaces U namespaceDeclarations). - */ - if (!XMLArrayInit(cx, &ancdecls, ancestorNSes->length + decls.length)) - goto out; - for (i = 0, n = ancestorNSes->length; i < n; i++) { - ns2 = XMLARRAY_MEMBER(ancestorNSes, i, JSXMLNamespace); - if (!ns2) - continue; - JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls, ns2, namespace_identity)); - if (!XMLARRAY_APPEND(cx, &ancdecls, ns2)) - goto out; - } - for (i = 0, n = decls.length; i < n; i++) { - ns2 = XMLARRAY_MEMBER(&decls, i, JSXMLNamespace); - if (!ns2) - continue; - JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls, ns2, namespace_identity)); - if (!XMLARRAY_APPEND(cx, &ancdecls, ns2)) - goto out; - } - - /* Step 11, except we don't clone ns unless its prefix is undefined. */ - ns = GetNamespace(cx, xml->name, &ancdecls); - if (!ns) - goto out; - - /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */ - if (!ns->prefix) { - /* - * Create a namespace prefix that isn't used by any member of decls. - * Assign the new prefix to a copy of ns. Flag this namespace as if - * it were declared, for assertion-testing's sake later below. - * - * Erratum: if ns->prefix and xml->name are both null (*undefined* in - * ECMA-357), we know that xml was named using the default namespace - * (proof: see GetNamespace and the Namespace constructor called with - * two arguments). So we ought not generate a new prefix here, when - * we can declare ns as the default namespace for xml. - * - * This helps descendants inherit the namespace instead of redundantly - * redeclaring it with generated prefixes in each descendant. - */ - if (!xml->name->prefix) { - prefix = cx->runtime->emptyString; - } else { - prefix = GeneratePrefix(cx, ns->uri, &ancdecls); - if (!prefix) - goto out; - } - ns = js_NewXMLNamespace(cx, prefix, ns->uri, JS_TRUE); - if (!ns) - goto out; - - /* - * If the xml->name was unprefixed, we must remove any declared default - * namespace from decls before appending ns. How can you get a default - * namespace in decls that doesn't match the one from name? Apparently - * by calling x.setNamespace(ns) where ns has no prefix. The other way - * to fix this is to update x's in-scope namespaces when setNamespace - * is called, but that's not specified by ECMA-357. - * - * Likely Erratum here, depending on whether the lack of update to x's - * in-scope namespace in XML.prototype.setNamespace (13.4.4.36) is an - * erratum or not. Note that changing setNamespace to update the list - * of in-scope namespaces will change x.namespaceDeclarations(). - */ - if (IS_EMPTY(prefix)) { - i = XMLArrayFindMember(&decls, ns, namespace_match); - if (i != XML_NOT_FOUND) - XMLArrayDelete(cx, &decls, i, JS_TRUE); - } - - /* - * In the spec, ancdecls has no name, but is always written out as - * (AncestorNamespaces U namespaceDeclarations). Since we compute - * that union in ancdecls, any time we append a namespace strong - * ref to decls, we must also append a weak ref to ancdecls. Order - * matters here: code at label out: releases strong refs in decls. - */ - if (!XMLARRAY_APPEND(cx, &ancdecls, ns) || - !XMLARRAY_APPEND(cx, &decls, ns)) { - goto out; - } - } - - /* Format the element or point-tag into sb. */ - js_AppendChar(&sb, '<'); - - if (ns->prefix && !IS_EMPTY(ns->prefix)) { - js_AppendJSString(&sb, ns->prefix); - js_AppendChar(&sb, ':'); - } - js_AppendJSString(&sb, xml->name->localName); - - /* - * Step 16 makes a union to avoid writing two loops in step 17, to share - * common attribute value appending spec-code. We prefer two loops for - * faster code and less data overhead. - */ - - /* Step 17(b): append attributes. */ - XMLArrayCursorInit(&cursor, &xml->xml_attrs); - while ((attr = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - js_AppendChar(&sb, ' '); - ns2 = GetNamespace(cx, attr->name, &ancdecls); - if (!ns2) - break; - - /* 17(b)(ii): NULL means *undefined* here. */ - if (!ns2->prefix) { - prefix = GeneratePrefix(cx, ns2->uri, &ancdecls); - if (!prefix) - break; - - /* Again, we avoid copying ns2 until we know it's prefix-less. */ - ns2 = js_NewXMLNamespace(cx, prefix, ns2->uri, JS_TRUE); - if (!ns2) - break; - - /* - * In the spec, ancdecls has no name, but is always written out as - * (AncestorNamespaces U namespaceDeclarations). Since we compute - * that union in ancdecls, any time we append a namespace strong - * ref to decls, we must also append a weak ref to ancdecls. Order - * matters here: code at label out: releases strong refs in decls. - */ - if (!XMLARRAY_APPEND(cx, &ancdecls, ns2) || - !XMLARRAY_APPEND(cx, &decls, ns2)) { - break; - } - } - - /* 17(b)(iii). */ - if (!IS_EMPTY(ns2->prefix)) { - js_AppendJSString(&sb, ns2->prefix); - js_AppendChar(&sb, ':'); - } - - /* 17(b)(iv). */ - js_AppendJSString(&sb, attr->name->localName); - - /* 17(d-g). */ - AppendAttributeValue(cx, &sb, attr->xml_value); - } - XMLArrayCursorFinish(&cursor); - if (attr) - goto out; - - /* Step 17(c): append XML namespace declarations. */ - XMLArrayCursorInit(&cursor, &decls); - while ((ns2 = (JSXMLNamespace *) XMLArrayCursorNext(&cursor)) != NULL) { - JS_ASSERT(ns2->declared); - - js_AppendCString(&sb, " xmlns"); - - /* 17(c)(ii): NULL means *undefined* here. */ - if (!ns2->prefix) { - prefix = GeneratePrefix(cx, ns2->uri, &ancdecls); - if (!prefix) - break; - ns2->prefix = prefix; - } - - /* 17(c)(iii). */ - if (!IS_EMPTY(ns2->prefix)) { - js_AppendChar(&sb, ':'); - js_AppendJSString(&sb, ns2->prefix); - } - - /* 17(d-g). */ - AppendAttributeValue(cx, &sb, ns2->uri); - } - XMLArrayCursorFinish(&cursor); - if (ns2) - goto out; - - /* Step 18: handle point tags. */ - n = xml->xml_kids.length; - if (n == 0) { - js_AppendCString(&sb, "/>"); - } else { - /* Steps 19 through 25: handle element content, and open the end-tag. */ - js_AppendChar(&sb, '>'); - indentKids = n > 1 || - (n == 1 && - (kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML)) && - kid->xml_class != JSXML_CLASS_TEXT); - - if (pretty && indentKids) { - if (!GetUint32XMLSetting(cx, js_prettyIndent_str, &i)) - goto out; - nextIndentLevel = indentLevel + i; - } else { - nextIndentLevel = 0; - } - - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (pretty && indentKids) - js_AppendChar(&sb, '\n'); - - kidstr = XMLToXMLString(cx, kid, &ancdecls, nextIndentLevel); - if (!kidstr) - break; - - js_AppendJSString(&sb, kidstr); - } - XMLArrayCursorFinish(&cursor); - if (kid) - goto out; - - if (pretty && indentKids) { - js_AppendChar(&sb, '\n'); - js_RepeatChar(&sb, ' ', indentLevel); - } - js_AppendCString(&sb, "prefix && !IS_EMPTY(ns->prefix)) { - js_AppendJSString(&sb, ns->prefix); - js_AppendChar(&sb, ':'); - } - - /* Step 27. */ - js_AppendJSString(&sb, xml->name->localName); - js_AppendChar(&sb, '>'); - } - - if (!STRING_BUFFER_OK(&sb)) { - JS_ReportOutOfMemory(cx); - goto out; - } - - str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0); -out: - js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str)); - if (!str && STRING_BUFFER_OK(&sb)) - js_FinishStringBuffer(&sb); - XMLArrayFinish(cx, &decls); - if (ancdecls.capacity != 0) - XMLArrayFinish(cx, &ancdecls); - return str; -} - -/* ECMA-357 10.2 */ -static JSString * -ToXMLString(JSContext *cx, jsval v) -{ - JSObject *obj; - JSString *str; - JSXML *xml; - - if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XML_CONVERSION, - js_type_strs[JSVAL_IS_NULL(v) - ? JSTYPE_NULL - : JSTYPE_VOID]); - return NULL; - } - - if (JSVAL_IS_BOOLEAN(v) || JSVAL_IS_NUMBER(v)) - return js_ValueToString(cx, v); - - if (JSVAL_IS_STRING(v)) - return EscapeElementValue(cx, NULL, JSVAL_TO_STRING(v)); - - obj = JSVAL_TO_OBJECT(v); - if (!OBJECT_IS_XML(cx, obj)) { - if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v)) - return NULL; - str = js_ValueToString(cx, v); - if (!str) - return NULL; - return EscapeElementValue(cx, NULL, str); - } - - /* Handle non-element cases in this switch, returning from each case. */ - xml = (JSXML *) JS_GetPrivate(cx, obj); - return XMLToXMLString(cx, xml, NULL, 0); -} - -static JSXMLQName * -ToAttributeName(JSContext *cx, jsval v) -{ - JSString *name, *uri, *prefix; - JSObject *obj; - JSClass *clasp; - JSXMLQName *qn; - JSTempValueRooter tvr; - - if (JSVAL_IS_STRING(v)) { - name = JSVAL_TO_STRING(v); - uri = prefix = cx->runtime->emptyString; - } else { - if (JSVAL_IS_PRIMITIVE(v)) { - name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); - if (name) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XML_ATTR_NAME, - JS_GetStringBytes(name)); - } - return NULL; - } - - obj = JSVAL_TO_OBJECT(v); - clasp = OBJ_GET_CLASS(cx, obj); - if (clasp == &js_AttributeNameClass) - return (JSXMLQName *) JS_GetPrivate(cx, obj); - - if (clasp == &js_QNameClass.base) { - qn = (JSXMLQName *) JS_GetPrivate(cx, obj); - uri = qn->uri; - prefix = qn->prefix; - name = qn->localName; - } else { - if (clasp == &js_AnyNameClass) { - name = ATOM_TO_STRING(cx->runtime->atomState.starAtom); - } else { - name = js_ValueToString(cx, v); - if (!name) - return NULL; - } - uri = prefix = cx->runtime->emptyString; - } - } - - qn = js_NewXMLQName(cx, uri, prefix, name); - if (!qn) - return NULL; - - JS_PUSH_TEMP_ROOT_GCTHING(cx, qn, &tvr); - obj = js_GetAttributeNameObject(cx, qn); - JS_POP_TEMP_ROOT(cx, &tvr); - if (!obj) - return NULL; - return qn; -} - -static JSXMLQName * -ToXMLName(JSContext *cx, jsval v, jsid *funidp) -{ - JSString *name; - JSObject *obj; - JSClass *clasp; - uint32 index; - JSXMLQName *qn; - JSAtom *atom; - - if (JSVAL_IS_STRING(v)) { - name = JSVAL_TO_STRING(v); - } else { - if (JSVAL_IS_PRIMITIVE(v)) { - name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); - if (name) - goto bad; - return NULL; - } - - obj = JSVAL_TO_OBJECT(v); - clasp = OBJ_GET_CLASS(cx, obj); - if (clasp == &js_AttributeNameClass || clasp == &js_QNameClass.base) - goto out; - if (clasp == &js_AnyNameClass) { - name = ATOM_TO_STRING(cx->runtime->atomState.starAtom); - goto construct; - } - name = js_ValueToString(cx, v); - if (!name) - return NULL; - } - - /* - * ECMA-357 10.6.1 step 1 seems to be incorrect. The spec says: - * - * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception - * - * First, _P_ should be _s_, to refer to the given string. - * - * Second, why does ToXMLName applied to the string type throw TypeError - * only for numeric literals without any leading or trailing whitespace? - * - * If the idea is to reject uint32 property names, then the check needs to - * be stricter, to exclude hexadecimal and floating point literals. - */ - if (js_IdIsIndex(STRING_TO_JSVAL(name), &index)) - goto bad; - - if (*JSSTRING_CHARS(name) == '@') { - name = js_NewDependentString(cx, name, 1, JSSTRING_LENGTH(name) - 1, 0); - if (!name) - return NULL; - *funidp = 0; - return ToAttributeName(cx, STRING_TO_JSVAL(name)); - } - -construct: - v = STRING_TO_JSVAL(name); - obj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &v); - if (!obj) - return NULL; - -out: - qn = (JSXMLQName *) JS_GetPrivate(cx, obj); - atom = cx->runtime->atomState.lazy.functionNamespaceURIAtom; - if (qn->uri && atom && - (qn->uri == ATOM_TO_STRING(atom) || - js_EqualStrings(qn->uri, ATOM_TO_STRING(atom)))) { - if (!JS_ValueToId(cx, STRING_TO_JSVAL(qn->localName), funidp)) - return NULL; - } else { - *funidp = 0; - } - return qn; - -bad: - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XML_NAME, - js_ValueToPrintableString(cx, STRING_TO_JSVAL(name))); - return NULL; -} - -/* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */ -static JSBool -AddInScopeNamespace(JSContext *cx, JSXML *xml, JSXMLNamespace *ns) -{ - JSXMLNamespace *match, *ns2; - uint32 i, n, m; - - if (xml->xml_class != JSXML_CLASS_ELEMENT) - return JS_TRUE; - - /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */ - if (!ns->prefix) { - match = NULL; - for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { - ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); - if (ns2 && js_EqualStrings(ns2->uri, ns->uri)) { - match = ns2; - break; - } - } - if (!match && !XMLARRAY_ADD_MEMBER(cx, &xml->xml_namespaces, n, ns)) - return JS_FALSE; - } else { - if (IS_EMPTY(ns->prefix) && IS_EMPTY(xml->name->uri)) - return JS_TRUE; - match = NULL; -#ifdef __GNUC__ /* suppress bogus gcc warnings */ - m = XML_NOT_FOUND; -#endif - for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { - ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); - if (ns2 && ns2->prefix && - js_EqualStrings(ns2->prefix, ns->prefix)) { - match = ns2; - m = i; - break; - } - } - if (match && !js_EqualStrings(match->uri, ns->uri)) { - ns2 = XMLARRAY_DELETE(cx, &xml->xml_namespaces, m, JS_TRUE, - JSXMLNamespace); - JS_ASSERT(ns2 == match); - match->prefix = NULL; - if (!AddInScopeNamespace(cx, xml, match)) - return JS_FALSE; - } - if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) - return JS_FALSE; - } - - /* OPTION: enforce that descendants have superset namespaces. */ - return JS_TRUE; -} - -/* ECMA-357 9.2.1.6 XMLList [[Append]]. */ -static JSBool -Append(JSContext *cx, JSXML *list, JSXML *xml) -{ - uint32 i, j, k, n; - JSXML *kid; - - JS_ASSERT(list->xml_class == JSXML_CLASS_LIST); - i = list->xml_kids.length; - n = 1; - if (xml->xml_class == JSXML_CLASS_LIST) { - list->xml_target = xml->xml_target; - list->xml_targetprop = xml->xml_targetprop; - n = JSXML_LENGTH(xml); - k = i + n; - if (!XMLArraySetCapacity(cx, &list->xml_kids, k)) - return JS_FALSE; - for (j = 0; j < n; j++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, j, JSXML); - if (kid) - XMLARRAY_SET_MEMBER(&list->xml_kids, i + j, kid); - } - return JS_TRUE; - } - - list->xml_target = xml->parent; - if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) - list->xml_targetprop = NULL; - else - list->xml_targetprop = xml->name; - if (!XMLARRAY_ADD_MEMBER(cx, &list->xml_kids, i, xml)) - return JS_FALSE; - return JS_TRUE; -} - -/* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */ -static JSXML * -DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags); - -static JSXML * -DeepCopy(JSContext *cx, JSXML *xml, JSObject *obj, uintN flags) -{ - JSXML *copy; - JSBool ok; - - /* Our caller may not be protecting newborns with a local root scope. */ - if (!js_EnterLocalRootScope(cx)) - return NULL; - copy = DeepCopyInLRS(cx, xml, flags); - if (copy) { - if (obj) { - /* Caller provided the object for this copy, hook 'em up. */ - ok = JS_SetPrivate(cx, obj, copy); - if (ok) - copy->object = obj; - } else { - ok = js_GetXMLObject(cx, copy) != NULL; - } - if (!ok) - copy = NULL; - } - js_LeaveLocalRootScopeWithResult(cx, (jsval) copy); - return copy; -} - -/* - * (i) We must be in a local root scope (InLRS). - * (ii) parent must have a rooted object. - * (iii) from's owning object must be locked if not thread-local. - */ -static JSBool -DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent, - uintN flags) -{ - uint32 j, n; - JSXMLArrayCursor cursor; - JSBool ok; - JSXML *kid, *kid2; - JSString *str; - - JS_ASSERT(cx->localRootStack); - - n = from->length; - if (!XMLArraySetCapacity(cx, to, n)) - return JS_FALSE; - - XMLArrayCursorInit(&cursor, from); - j = 0; - ok = JS_TRUE; - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if ((flags & XSF_IGNORE_COMMENTS) && - kid->xml_class == JSXML_CLASS_COMMENT) { - continue; - } - if ((flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) && - kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) { - continue; - } - if ((flags & XSF_IGNORE_WHITESPACE) && - (kid->xml_flags & XMLF_WHITESPACE_TEXT)) { - continue; - } - kid2 = DeepCopyInLRS(cx, kid, flags); - if (!kid2) { - to->length = j; - ok = JS_FALSE; - break; - } - - if ((flags & XSF_IGNORE_WHITESPACE) && - n > 1 && kid2->xml_class == JSXML_CLASS_TEXT) { - str = ChompXMLWhitespace(cx, kid2->xml_value); - if (!str) { - to->length = j; - ok = JS_FALSE; - break; - } - kid2->xml_value = str; - } - - XMLARRAY_SET_MEMBER(to, j, kid2); - ++j; - if (parent->xml_class != JSXML_CLASS_LIST) - kid2->parent = parent; - } - XMLArrayCursorFinish(&cursor); - if (!ok) - return JS_FALSE; - - if (j < n) - XMLArrayTrim(to); - return JS_TRUE; -} - -static JSXML * -DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags) -{ - JSXML *copy; - JSXMLQName *qn; - JSBool ok; - uint32 i, n; - JSXMLNamespace *ns, *ns2; - - /* Our caller must be protecting newborn objects. */ - JS_ASSERT(cx->localRootStack); - - copy = js_NewXML(cx, xml->xml_class); - if (!copy) - return NULL; - qn = xml->name; - if (qn) { - qn = js_NewXMLQName(cx, qn->uri, qn->prefix, qn->localName); - if (!qn) { - ok = JS_FALSE; - goto out; - } - } - copy->name = qn; - copy->xml_flags = xml->xml_flags; - - if (JSXML_HAS_VALUE(xml)) { - copy->xml_value = xml->xml_value; - ok = JS_TRUE; - } else { - ok = DeepCopySetInLRS(cx, &xml->xml_kids, ©->xml_kids, copy, flags); - if (!ok) - goto out; - - if (xml->xml_class == JSXML_CLASS_LIST) { - copy->xml_target = xml->xml_target; - copy->xml_targetprop = xml->xml_targetprop; - } else { - n = xml->xml_namespaces.length; - ok = XMLArraySetCapacity(cx, ©->xml_namespaces, n); - if (!ok) - goto out; - for (i = 0; i < n; i++) { - ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); - if (!ns) - continue; - ns2 = js_NewXMLNamespace(cx, ns->prefix, ns->uri, ns->declared); - if (!ns2) { - copy->xml_namespaces.length = i; - ok = JS_FALSE; - goto out; - } - XMLARRAY_SET_MEMBER(©->xml_namespaces, i, ns2); - } - - ok = DeepCopySetInLRS(cx, &xml->xml_attrs, ©->xml_attrs, copy, - 0); - if (!ok) - goto out; - } - } - -out: - if (!ok) - return NULL; - return copy; -} - -static void -ReportBadXMLName(JSContext *cx, jsval id) -{ - JSString *name; - - name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, id, NULL); - if (name) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XML_NAME, - JS_GetStringBytes(name)); - } -} - -/* ECMA-357 9.1.1.4 XML [[DeleteByIndex]]. */ -static JSBool -DeleteByIndex(JSContext *cx, JSXML *xml, jsval id, jsval *vp) -{ - uint32 index; - JSXML *kid; - - if (!js_IdIsIndex(id, &index)) { - ReportBadXMLName(cx, id); - return JS_FALSE; - } - - if (JSXML_HAS_KIDS(xml) && index < xml->xml_kids.length) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); - if (kid) - kid->parent = NULL; - XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE); - } - - *vp = JSVAL_TRUE; - return JS_TRUE; -} - -typedef JSBool (*JSXMLNameMatcher)(JSXMLQName *nameqn, JSXML *xml); - -static JSBool -MatchAttrName(JSXMLQName *nameqn, JSXML *attr) -{ - JSXMLQName *attrqn = attr->name; - - return (IS_STAR(nameqn->localName) || - js_EqualStrings(attrqn->localName, nameqn->localName)) && - (!nameqn->uri || - js_EqualStrings(attrqn->uri, nameqn->uri)); -} - -static JSBool -MatchElemName(JSXMLQName *nameqn, JSXML *elem) -{ - return (IS_STAR(nameqn->localName) || - (elem->xml_class == JSXML_CLASS_ELEMENT && - js_EqualStrings(elem->name->localName, nameqn->localName))) && - (!nameqn->uri || - (elem->xml_class == JSXML_CLASS_ELEMENT && - js_EqualStrings(elem->name->uri, nameqn->uri))); -} - -/* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */ -static JSBool -DescendantsHelper(JSContext *cx, JSXML *xml, JSXMLQName *nameqn, JSXML *list) -{ - uint32 i, n; - JSXML *attr, *kid; - - if (xml->xml_class == JSXML_CLASS_ELEMENT && - OBJ_GET_CLASS(cx, nameqn->object) == &js_AttributeNameClass) { - for (i = 0, n = xml->xml_attrs.length; i < n; i++) { - attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); - if (attr && MatchAttrName(nameqn, attr)) { - if (!Append(cx, list, attr)) - return JS_FALSE; - } - } - } - - for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (!kid) - continue; - if (OBJ_GET_CLASS(cx, nameqn->object) != &js_AttributeNameClass && - MatchElemName(nameqn, kid)) { - if (!Append(cx, list, kid)) - return JS_FALSE; - } - if (!DescendantsHelper(cx, kid, nameqn, list)) - return JS_FALSE; - } - return JS_TRUE; -} - -static JSXML * -Descendants(JSContext *cx, JSXML *xml, jsval id) -{ - jsid funid; - JSXMLQName *nameqn; - JSObject *listobj; - JSXML *list, *kid; - uint32 i, n; - JSBool ok; - - nameqn = ToXMLName(cx, id, &funid); - if (!nameqn) - return NULL; - - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) - return NULL; - list = (JSXML *) JS_GetPrivate(cx, listobj); - if (funid) - return list; - - /* - * Protect nameqn's object and strings from GC by linking list to it - * temporarily. The cx->newborn[GCX_OBJECT] GC root protects listobj, - * which protects list. Any other object allocations occuring beneath - * DescendantsHelper use local roots. - */ - list->name = nameqn; - if (!js_EnterLocalRootScope(cx)) - return NULL; - if (xml->xml_class == JSXML_CLASS_LIST) { - ok = JS_TRUE; - for (i = 0, n = xml->xml_kids.length; i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { - ok = DescendantsHelper(cx, kid, nameqn, list); - if (!ok) - break; - } - } - } else { - ok = DescendantsHelper(cx, xml, nameqn, list); - } - js_LeaveLocalRootScopeWithResult(cx, (jsval) list); - if (!ok) - return NULL; - list->name = NULL; - return list; -} - -static JSBool -xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); - -/* Recursive (JSXML *) parameterized version of Equals. */ -static JSBool -XMLEquals(JSContext *cx, JSXML *xml, JSXML *vxml, JSBool *bp) -{ - JSXMLQName *qn, *vqn; - uint32 i, j, n; - JSXMLArrayCursor cursor, vcursor; - JSXML *kid, *vkid, *attr, *vattr; - JSBool ok; - JSObject *xobj, *vobj; - -retry: - if (xml->xml_class != vxml->xml_class) { - if (xml->xml_class == JSXML_CLASS_LIST && xml->xml_kids.length == 1) { - xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (xml) - goto retry; - } - if (vxml->xml_class == JSXML_CLASS_LIST && vxml->xml_kids.length == 1) { - vxml = XMLARRAY_MEMBER(&vxml->xml_kids, 0, JSXML); - if (vxml) - goto retry; - } - *bp = JS_FALSE; - return JS_TRUE; - } - - qn = xml->name; - vqn = vxml->name; - if (qn) { - *bp = vqn && - js_EqualStrings(qn->localName, vqn->localName) && - js_EqualStrings(qn->uri, vqn->uri); - } else { - *bp = vqn == NULL; - } - if (!*bp) - return JS_TRUE; - - if (JSXML_HAS_VALUE(xml)) { - *bp = js_EqualStrings(xml->xml_value, vxml->xml_value); - } else if (xml->xml_kids.length != vxml->xml_kids.length) { - *bp = JS_FALSE; - } else { - XMLArrayCursorInit(&cursor, &xml->xml_kids); - XMLArrayCursorInit(&vcursor, &vxml->xml_kids); - for (;;) { - kid = (JSXML *) XMLArrayCursorNext(&cursor); - vkid = (JSXML *) XMLArrayCursorNext(&vcursor); - if (!kid || !vkid) { - *bp = !kid && !vkid; - ok = JS_TRUE; - break; - } - xobj = js_GetXMLObject(cx, kid); - vobj = js_GetXMLObject(cx, vkid); - ok = xobj && vobj && - xml_equality(cx, xobj, OBJECT_TO_JSVAL(vobj), bp); - if (!ok || !*bp) - break; - } - XMLArrayCursorFinish(&vcursor); - XMLArrayCursorFinish(&cursor); - if (!ok) - return JS_FALSE; - - if (*bp && xml->xml_class == JSXML_CLASS_ELEMENT) { - n = xml->xml_attrs.length; - if (n != vxml->xml_attrs.length) - *bp = JS_FALSE; - for (i = 0; *bp && i < n; i++) { - attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); - if (!attr) - continue; - j = XMLARRAY_FIND_MEMBER(&vxml->xml_attrs, attr, attr_identity); - if (j == XML_NOT_FOUND) { - *bp = JS_FALSE; - break; - } - vattr = XMLARRAY_MEMBER(&vxml->xml_attrs, j, JSXML); - if (!vattr) - continue; - *bp = js_EqualStrings(attr->xml_value, vattr->xml_value); - } - } - } - - return JS_TRUE; -} - -/* ECMA-357 9.1.1.9 XML [[Equals]] and 9.2.1.9 XMLList [[Equals]]. */ -static JSBool -Equals(JSContext *cx, JSXML *xml, jsval v, JSBool *bp) -{ - JSObject *vobj; - JSXML *vxml; - - if (JSVAL_IS_PRIMITIVE(v)) { - *bp = JS_FALSE; - if (xml->xml_class == JSXML_CLASS_LIST) { - if (xml->xml_kids.length == 1) { - vxml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (!vxml) - return JS_TRUE; - vobj = js_GetXMLObject(cx, vxml); - if (!vobj) - return JS_FALSE; - return js_XMLObjectOps.equality(cx, vobj, v, bp); - } - if (JSVAL_IS_VOID(v) && xml->xml_kids.length == 0) - *bp = JS_TRUE; - } - } else { - vobj = JSVAL_TO_OBJECT(v); - if (!OBJECT_IS_XML(cx, vobj)) { - *bp = JS_FALSE; - } else { - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - if (!XMLEquals(cx, xml, vxml, bp)) - return JS_FALSE; - } - } - return JS_TRUE; -} - -static JSBool -CheckCycle(JSContext *cx, JSXML *xml, JSXML *kid) -{ - JS_ASSERT(kid->xml_class != JSXML_CLASS_LIST); - - do { - if (xml == kid) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CYCLIC_VALUE, js_XML_str); - return JS_FALSE; - } - } while ((xml = xml->parent) != NULL); - - return JS_TRUE; -} - -/* ECMA-357 9.1.1.11 XML [[Insert]]. */ -static JSBool -Insert(JSContext *cx, JSXML *xml, uint32 i, jsval v) -{ - uint32 j, n; - JSXML *vxml, *kid; - JSObject *vobj; - JSString *str; - - if (!JSXML_HAS_KIDS(xml)) - return JS_TRUE; - - n = 1; - vxml = NULL; - if (!JSVAL_IS_PRIMITIVE(v)) { - vobj = JSVAL_TO_OBJECT(v); - if (OBJECT_IS_XML(cx, vobj)) { - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - if (vxml->xml_class == JSXML_CLASS_LIST) { - n = vxml->xml_kids.length; - if (n == 0) - return JS_TRUE; - for (j = 0; j < n; j++) { - kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML); - if (!kid) - continue; - if (!CheckCycle(cx, xml, kid)) - return JS_FALSE; - } - } else if (vxml->xml_class == JSXML_CLASS_ELEMENT) { - /* OPTION: enforce that descendants have superset namespaces. */ - if (!CheckCycle(cx, xml, vxml)) - return JS_FALSE; - } - } - } - if (!vxml) { - str = js_ValueToString(cx, v); - if (!str) - return JS_FALSE; - - vxml = js_NewXML(cx, JSXML_CLASS_TEXT); - if (!vxml) - return JS_FALSE; - vxml->xml_value = str; - } - - if (i > xml->xml_kids.length) - i = xml->xml_kids.length; - - if (!XMLArrayInsert(cx, &xml->xml_kids, i, n)) - return JS_FALSE; - - if (vxml->xml_class == JSXML_CLASS_LIST) { - for (j = 0; j < n; j++) { - kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML); - if (!kid) - continue; - kid->parent = xml; - XMLARRAY_SET_MEMBER(&xml->xml_kids, i + j, kid); - - /* OPTION: enforce that descendants have superset namespaces. */ - } - } else { - vxml->parent = xml; - XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml); - } - return JS_TRUE; -} - -static JSBool -IndexToIdVal(JSContext *cx, uint32 index, jsval *idvp) -{ - JSString *str; - - if (index <= JSVAL_INT_MAX) { - *idvp = INT_TO_JSVAL(index); - } else { - str = js_NumberToString(cx, (jsdouble) index); - if (!str) - return JS_FALSE; - *idvp = STRING_TO_JSVAL(str); - } - return JS_TRUE; -} - -/* ECMA-357 9.1.1.12 XML [[Replace]]. */ -static JSBool -Replace(JSContext *cx, JSXML *xml, jsval id, jsval v) -{ - uint32 i, n; - JSXML *vxml, *kid; - JSObject *vobj; - jsval junk; - JSString *str; - - if (!JSXML_HAS_KIDS(xml)) - return JS_TRUE; - - if (!js_IdIsIndex(id, &i)) { - ReportBadXMLName(cx, id); - return JS_FALSE; - } - - /* - * 9.1.1.12 - * [[Replace]] handles _i >= x.[[Length]]_ by incrementing _x.[[Length]_. - * It should therefore constrain callers to pass in _i <= x.[[Length]]_. - */ - n = xml->xml_kids.length; - if (i >= n) { - if (!IndexToIdVal(cx, n, &id)) - return JS_FALSE; - i = n; - } - - vxml = NULL; - if (!JSVAL_IS_PRIMITIVE(v)) { - vobj = JSVAL_TO_OBJECT(v); - if (OBJECT_IS_XML(cx, vobj)) - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - } - - switch (vxml ? vxml->xml_class : JSXML_CLASS_LIMIT) { - case JSXML_CLASS_ELEMENT: - /* OPTION: enforce that descendants have superset namespaces. */ - if (!CheckCycle(cx, xml, vxml)) - return JS_FALSE; - case JSXML_CLASS_COMMENT: - case JSXML_CLASS_PROCESSING_INSTRUCTION: - case JSXML_CLASS_TEXT: - goto do_replace; - - case JSXML_CLASS_LIST: - if (i < n && !DeleteByIndex(cx, xml, id, &junk)) - return JS_FALSE; - if (!Insert(cx, xml, i, v)) - return JS_FALSE; - break; - - default: - str = js_ValueToString(cx, v); - if (!str) - return JS_FALSE; - - vxml = js_NewXML(cx, JSXML_CLASS_TEXT); - if (!vxml) - return JS_FALSE; - vxml->xml_value = str; - - do_replace: - vxml->parent = xml; - if (i < n) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid) - kid->parent = NULL; - } - if (!XMLARRAY_ADD_MEMBER(cx, &xml->xml_kids, i, vxml)) - return JS_FALSE; - break; - } - - return JS_TRUE; -} - -/* Forward declared -- its implementation uses other statics that call it. */ -static JSBool -ResolveValue(JSContext *cx, JSXML *list, JSXML **result); - -/* ECMA-357 9.1.1.3 XML [[Delete]], 9.2.1.3 XML [[Delete]]. */ -static JSBool -DeleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSXML *xml, *kid, *parent; - JSBool isIndex; - JSXMLArray *array; - uint32 length, index, kidIndex, deleteCount; - JSXMLQName *nameqn; - jsid funid; - JSObject *nameobj, *kidobj; - JSXMLNameMatcher matcher; - - xml = (JSXML *) JS_GetPrivate(cx, obj); - isIndex = js_IdIsIndex(id, &index); - if (JSXML_HAS_KIDS(xml)) { - array = &xml->xml_kids; - length = array->length; - } else { - array = NULL; - length = 0; - } - - if (xml->xml_class == JSXML_CLASS_LIST) { - /* ECMA-357 9.2.1.3. */ - if (isIndex && index < length) { - kid = XMLARRAY_MEMBER(array, index, JSXML); - if (!kid) - goto out; - parent = kid->parent; - if (parent) { - JS_ASSERT(parent != xml); - JS_ASSERT(JSXML_HAS_KIDS(parent)); - - if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) { - nameqn = kid->name; - nameobj = js_GetAttributeNameObject(cx, nameqn); - if (!nameobj || !js_GetXMLObject(cx, parent)) - return JS_FALSE; - - id = OBJECT_TO_JSVAL(nameobj); - if (!DeleteProperty(cx, parent->object, id, vp)) - return JS_FALSE; - } else { - kidIndex = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, - NULL); - JS_ASSERT(kidIndex != XML_NOT_FOUND); - if (!IndexToIdVal(cx, kidIndex, &id)) - return JS_FALSE; - if (!DeleteByIndex(cx, parent, id, vp)) - return JS_FALSE; - } - } - - XMLArrayDelete(cx, array, index, JS_TRUE); - } else { - for (index = 0; index < length; index++) { - kid = XMLARRAY_MEMBER(array, index, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj || !DeleteProperty(cx, kidobj, id, vp)) - return JS_FALSE; - } - } - } - } else { - /* ECMA-357 9.1.1.3. */ - if (isIndex) { - /* See NOTE in spec: this variation is reserved for future use. */ - ReportBadXMLName(cx, id); - return JS_FALSE; - } - - nameqn = ToXMLName(cx, id, &funid); - if (!nameqn) - return JS_FALSE; - if (funid) - goto out; - nameobj = nameqn->object; - - if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { - if (xml->xml_class != JSXML_CLASS_ELEMENT) - goto out; - array = &xml->xml_attrs; - length = array->length; - matcher = MatchAttrName; - } else { - matcher = MatchElemName; - } - if (length != 0) { - deleteCount = 0; - for (index = 0; index < length; index++) { - kid = XMLARRAY_MEMBER(array, index, JSXML); - if (kid && matcher(nameqn, kid)) { - kid->parent = NULL; - XMLArrayDelete(cx, array, index, JS_FALSE); - ++deleteCount; - } else if (deleteCount != 0) { - XMLARRAY_SET_MEMBER(array, - index - deleteCount, - array->vector[index]); - } - } - array->length -= deleteCount; - } - } - -out: - *vp = JSVAL_TRUE; - return JS_TRUE; -} - -static JSBool -SyncInScopeNamespaces(JSContext *cx, JSXML *xml) -{ - JSXMLArray *nsarray; - uint32 i, n; - JSXMLNamespace *ns; - - nsarray = &xml->xml_namespaces; - while ((xml = xml->parent) != NULL) { - for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { - ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); - if (ns && !XMLARRAY_HAS_MEMBER(nsarray, ns, namespace_identity)) { - if (!XMLARRAY_APPEND(cx, nsarray, ns)) - return JS_FALSE; - } - } - } - return JS_TRUE; -} - -static JSBool -GetNamedProperty(JSContext *cx, JSXML *xml, JSXMLQName* nameqn, - JSBool attributes, JSXML *list) -{ - JSXMLArray *array; - JSXMLNameMatcher matcher; - JSXMLArrayCursor cursor; - JSXML *kid; - JSBool ok; - - if (!JSXML_HAS_KIDS(xml)) - return JS_TRUE; - - if (attributes) { - array = &xml->xml_attrs; - matcher = MatchAttrName; - } else { - array = &xml->xml_kids; - matcher = MatchElemName; - } - - XMLArrayCursorInit(&cursor, array); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (matcher(nameqn, kid)) { - if (!attributes && kid->xml_class == JSXML_CLASS_ELEMENT) { - ok = SyncInScopeNamespaces(cx, kid); - if (!ok) - goto out; - } - ok = Append(cx, list, kid); - if (!ok) - goto out; - } - } - ok = JS_TRUE; - - out: - XMLArrayCursorFinish(&cursor); - return ok; -} - -/* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */ -static JSBool -GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSXML *xml, *list, *kid; - uint32 index; - JSObject *kidobj, *listobj; - JSXMLQName *nameqn; - jsid funid; - jsval roots[2]; - JSTempValueRooter tvr; - JSBool attributes; - JSXMLArrayCursor cursor; - - xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); - if (!xml) - return JS_TRUE; - - if (js_IdIsIndex(id, &index)) { - if (xml->xml_class != JSXML_CLASS_LIST) { - *vp = (index == 0) ? OBJECT_TO_JSVAL(obj) : JSVAL_VOID; - } else { - /* - * ECMA-357 9.2.1.1 starts here. - * - * Erratum: 9.2 is not completely clear that indexed properties - * correspond to kids, but that's what it seems to say, and it's - * what any sane user would want. - */ - if (index < xml->xml_kids.length) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); - if (!kid) { - *vp = JSVAL_VOID; - return JS_TRUE; - } - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - return JS_FALSE; - - *vp = OBJECT_TO_JSVAL(kidobj); - } else { - *vp = JSVAL_VOID; - } - } - return JS_TRUE; - } - - /* - * ECMA-357 9.2.1.1/9.1.1.1 qname case. - */ - nameqn = ToXMLName(cx, id, &funid); - if (!nameqn) - return JS_FALSE; - if (funid) - return js_GetXMLFunction(cx, obj, funid, vp); - - roots[0] = OBJECT_TO_JSVAL(nameqn->object); - JS_PUSH_TEMP_ROOT(cx, 1, roots, &tvr); - - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (listobj) { - roots[1] = OBJECT_TO_JSVAL(listobj); - tvr.count++; - - list = (JSXML *) JS_GetPrivate(cx, listobj); - attributes = (OBJ_GET_CLASS(cx, nameqn->object) == - &js_AttributeNameClass); - - if (xml->xml_class == JSXML_CLASS_LIST) { - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (kid->xml_class == JSXML_CLASS_ELEMENT && - !GetNamedProperty(cx, kid, nameqn, attributes, list)) { - listobj = NULL; - break; - } - } - XMLArrayCursorFinish(&cursor); - } else { - if (!GetNamedProperty(cx, xml, nameqn, attributes, list)) - listobj = NULL; - } - - /* - * Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the given - * list's [[TargetProperty]] to the property that is being appended. - * This means that any use of the internal [[Get]] property returns - * a list which, when used by e.g. [[Insert]] duplicates the last - * element matched by id. - * See bug 336921. - */ - list->xml_target = xml; - list->xml_targetprop = nameqn; - *vp = OBJECT_TO_JSVAL(listobj); - } - - JS_POP_TEMP_ROOT(cx, &tvr); - return listobj != NULL; -} - -static JSXML * -CopyOnWrite(JSContext *cx, JSXML *xml, JSObject *obj) -{ - JS_ASSERT(xml->object != obj); - - xml = DeepCopy(cx, xml, obj, 0); - if (!xml) - return NULL; - - JS_ASSERT(xml->object == obj); - return xml; -} - -#define CHECK_COPY_ON_WRITE(cx,xml,obj) \ - (xml->object == obj ? xml : CopyOnWrite(cx, xml, obj)) - -static JSString * -KidToString(JSContext *cx, JSXML *xml, uint32 index) -{ - JSXML *kid; - JSObject *kidobj; - - kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); - if (!kid) - return cx->runtime->emptyString; - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - return NULL; - return js_ValueToString(cx, OBJECT_TO_JSVAL(kidobj)); -} - -/* ECMA-357 9.1.1.2 XML [[Put]] and 9.2.1.2 XMLList [[Put]]. */ -static JSBool -PutProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSBool ok, primitiveAssign; - enum { OBJ_ROOT, ID_ROOT, VAL_ROOT }; - jsval roots[3]; - JSTempValueRooter tvr; - JSXML *xml, *vxml, *rxml, *kid, *attr, *parent, *copy, *kid2, *match; - JSObject *vobj, *nameobj, *attrobj, *parentobj, *kidobj, *copyobj; - JSXMLQName *targetprop, *nameqn, *attrqn; - uint32 index, i, j, k, n, q; - jsval attrval, nsval, junk; - jsid funid; - JSString *left, *right, *space; - JSXMLNamespace *ns; - - xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); - if (!xml) - return JS_TRUE; - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - - /* Precompute vxml for 9.2.1.2 2(c)(vii)(2-3) and 2(d) and 9.1.1.2 1. */ - vxml = NULL; - if (!JSVAL_IS_PRIMITIVE(*vp)) { - vobj = JSVAL_TO_OBJECT(*vp); - if (OBJECT_IS_XML(cx, vobj)) - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - } - - /* Control flow after here must exit via label out. */ - ok = js_EnterLocalRootScope(cx); - if (!ok) - return JS_FALSE; - roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj); - roots[ID_ROOT] = id; - roots[VAL_ROOT] = *vp; - JS_PUSH_TEMP_ROOT(cx, 3, roots, &tvr); - - if (xml->xml_class == JSXML_CLASS_LIST) { - /* ECMA-357 9.2.1.2. */ - if (js_IdIsIndex(id, &index)) { - /* Step 1 sets i to the property index. */ - i = index; - - /* 2(a-b). */ - if (xml->xml_target) { - ok = ResolveValue(cx, xml->xml_target, &rxml); - if (!ok) - goto out; - if (!rxml) - goto out; - JS_ASSERT(rxml->object); - } else { - rxml = NULL; - } - - /* 2(c). */ - if (index >= xml->xml_kids.length) { - /* 2(c)(i). */ - if (rxml) { - if (rxml->xml_class == JSXML_CLASS_LIST) { - if (rxml->xml_kids.length != 1) - goto out; - rxml = XMLARRAY_MEMBER(&rxml->xml_kids, 0, JSXML); - if (!rxml) - goto out; - ok = js_GetXMLObject(cx, rxml) != NULL; - if (!ok) - goto out; - } - - /* - * Erratum: ECMA-357 9.2.1.2 step 2(c)(ii) sets - * _y.[[Parent]] = r_ where _r_ is the result of - * [[ResolveValue]] called on _x.[[TargetObject]] in - * 2(a)(i). This can result in text parenting text: - * - * var MYXML = new XML(); - * MYXML.appendChild(new XML("Giants")); - * - * (testcase from Werner Sharp ). - * - * To match insertChildAfter, insertChildBefore, - * prependChild, and setChildren, we should silently - * do nothing in this case. - */ - if (!JSXML_HAS_KIDS(rxml)) - goto out; - } - - /* 2(c)(ii) is distributed below as several js_NewXML calls. */ - targetprop = xml->xml_targetprop; - if (!targetprop || IS_STAR(targetprop->localName)) { - /* 2(c)(iv)(1-2), out of order w.r.t. 2(c)(iii). */ - kid = js_NewXML(cx, JSXML_CLASS_TEXT); - if (!kid) - goto bad; - } else { - nameobj = js_GetXMLQNameObject(cx, targetprop); - if (!nameobj) - goto bad; - if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { - /* - * 2(c)(iii)(1-3). - * Note that rxml can't be null here, because target - * and targetprop are non-null. - */ - ok = GetProperty(cx, rxml->object, id, &attrval); - if (!ok) - goto out; - if (JSVAL_IS_PRIMITIVE(attrval)) /* no such attribute */ - goto out; - attrobj = JSVAL_TO_OBJECT(attrval); - attr = (JSXML *) JS_GetPrivate(cx, attrobj); - if (JSXML_LENGTH(attr) != 0) - goto out; - - kid = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); - } else { - /* 2(c)(v). */ - kid = js_NewXML(cx, JSXML_CLASS_ELEMENT); - } - if (!kid) - goto bad; - - /* An important bit of 2(c)(ii). */ - kid->name = targetprop; - } - - /* Final important bit of 2(c)(ii). */ - kid->parent = rxml; - - /* 2(c)(vi-vii). */ - i = xml->xml_kids.length; - if (kid->xml_class != JSXML_CLASS_ATTRIBUTE) { - /* - * 2(c)(vii)(1) tests whether _y.[[Parent]]_ is not null. - * y.[[Parent]] is here called kid->parent, which we know - * from 2(c)(ii) is _r_, here called rxml. So let's just - * test that! Erratum, the spec should be simpler here. - */ - if (rxml) { - JS_ASSERT(JSXML_HAS_KIDS(rxml)); - n = rxml->xml_kids.length; - j = n - 1; - if (n != 0 && i != 0) { - for (n = j, j = 0; j < n; j++) { - if (rxml->xml_kids.vector[j] == - xml->xml_kids.vector[i-1]) { - break; - } - } - } - - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - goto bad; - ok = Insert(cx, rxml, j + 1, OBJECT_TO_JSVAL(kidobj)); - if (!ok) - goto out; - } - - /* - * 2(c)(vii)(2-3). - * Erratum: [[PropertyName]] in 2(c)(vii)(3) must be a - * typo for [[TargetProperty]]. - */ - if (vxml) { - kid->name = (vxml->xml_class == JSXML_CLASS_LIST) - ? vxml->xml_targetprop - : vxml->name; - } - } - - /* 2(c)(viii). */ - ok = Append(cx, xml, kid); - if (!ok) - goto out; - } - - /* 2(d). */ - if (!vxml || - vxml->xml_class == JSXML_CLASS_TEXT || - vxml->xml_class == JSXML_CLASS_ATTRIBUTE) { - ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); - if (!ok) - goto out; - roots[VAL_ROOT] = *vp; - } - - /* 2(e). */ - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (!kid) - goto out; - parent = kid->parent; - if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) { - nameobj = js_GetAttributeNameObject(cx, kid->name); - if (!nameobj) - goto bad; - id = OBJECT_TO_JSVAL(nameobj); - - if (parent) { - /* 2(e)(i). */ - parentobj = js_GetXMLObject(cx, parent); - if (!parentobj) - goto bad; - ok = PutProperty(cx, parentobj, id, vp); - if (!ok) - goto out; - - /* 2(e)(ii). */ - ok = GetProperty(cx, parentobj, id, vp); - if (!ok) - goto out; - attr = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*vp)); - - /* 2(e)(iii). */ - xml->xml_kids.vector[i] = attr->xml_kids.vector[0]; - } - } - - /* 2(f). */ - else if (vxml && vxml->xml_class == JSXML_CLASS_LIST) { - /* 2(f)(i) Create a shallow copy _c_ of _V_. */ - copyobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!copyobj) - goto bad; - copy = (JSXML *) JS_GetPrivate(cx, copyobj); - n = vxml->xml_kids.length; - ok = XMLArraySetCapacity(cx, ©->xml_kids, n); - if (!ok) - goto out; - for (k = 0; k < n; k++) { - kid2 = XMLARRAY_MEMBER(&vxml->xml_kids, k, JSXML); - XMLARRAY_SET_MEMBER(©->xml_kids, k, kid2); - } - - JS_ASSERT(parent != xml); - if (parent) { - q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL); - JS_ASSERT(q != XML_NOT_FOUND); - - ok = IndexToIdVal(cx, q, &id); - if (!ok) - goto out; - ok = Replace(cx, parent, id, OBJECT_TO_JSVAL(copyobj)); - if (!ok) - goto out; - -#ifdef DEBUG - /* Erratum: this loop in the spec is useless. */ - for (j = 0, n = copy->xml_kids.length; j < n; j++) { - kid2 = XMLARRAY_MEMBER(&parent->xml_kids, q + j, JSXML); - JS_ASSERT(XMLARRAY_MEMBER(©->xml_kids, j, JSXML) - == kid2); - } -#endif - } - - /* - * 2(f)(iv-vi). - * Erratum: notice the unhandled zero-length V basis case and - * the off-by-one errors for the n != 0 cases in the spec. - */ - if (n == 0) { - XMLArrayDelete(cx, &xml->xml_kids, i, JS_TRUE); - } else { - ok = XMLArrayInsert(cx, &xml->xml_kids, i + 1, n - 1); - if (!ok) - goto out; - - for (j = 0; j < n; j++) - xml->xml_kids.vector[i + j] = copy->xml_kids.vector[j]; - } - } - - /* 2(g). */ - else if (vxml || JSXML_HAS_VALUE(kid)) { - if (parent) { - q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL); - JS_ASSERT(q != XML_NOT_FOUND); - - ok = IndexToIdVal(cx, q, &id); - if (!ok) - goto out; - ok = Replace(cx, parent, id, *vp); - if (!ok) - goto out; - - vxml = XMLARRAY_MEMBER(&parent->xml_kids, q, JSXML); - if (!vxml) - goto out; - roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vxml->object); - } - - /* - * 2(g)(iii). - * Erratum: _V_ may not be of type XML, but all index-named - * properties _x[i]_ in an XMLList _x_ must be of type XML, - * according to 9.2.1.1 Overview and other places in the spec. - * - * Thanks to 2(d), we know _V_ (*vp here) is either a string - * or an XML/XMLList object. If *vp is a string, call ToXML - * on it to satisfy the constraint. - */ - if (!vxml) { - JS_ASSERT(JSVAL_IS_STRING(*vp)); - vobj = ToXML(cx, *vp); - if (!vobj) - goto bad; - roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vobj); - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - } - XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml); - } - - /* 2(h). */ - else { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - goto bad; - id = ATOM_KEY(cx->runtime->atomState.starAtom); - ok = PutProperty(cx, kidobj, id, vp); - if (!ok) - goto out; - } - } else { - /* - * 3. - * Erratum: if x.[[Length]] > 1 or [[ResolveValue]] returns null - * or an r with r.[[Length]] != 1, throw TypeError. - */ - n = JSXML_LENGTH(xml); - if (n > 1) - goto type_error; - if (n == 0) { - ok = ResolveValue(cx, xml, &rxml); - if (!ok) - goto out; - if (!rxml || JSXML_LENGTH(rxml) != 1) - goto type_error; - ok = Append(cx, xml, rxml); - if (!ok) - goto out; - } - JS_ASSERT(JSXML_LENGTH(xml) == 1); - kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (!kid) - goto out; - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - goto bad; - ok = PutProperty(cx, kidobj, id, vp); - if (!ok) - goto out; - } - } else { - /* - * ECMA-357 9.1.1.2. - * Erratum: move steps 3 and 4 to before 1 and 2, to avoid wasted - * effort in ToString or [[DeepCopy]]. - */ - if (js_IdIsIndex(id, &index)) { - /* See NOTE in spec: this variation is reserved for future use. */ - ReportBadXMLName(cx, id); - goto bad; - } - - nameqn = ToXMLName(cx, id, &funid); - if (!nameqn) - goto bad; - if (funid) { - ok = js_SetProperty(cx, obj, funid, vp); - goto out; - } - nameobj = nameqn->object; - - if (JSXML_HAS_VALUE(xml)) - goto out; - - if (!vxml || - vxml->xml_class == JSXML_CLASS_TEXT || - vxml->xml_class == JSXML_CLASS_ATTRIBUTE) { - ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); - if (!ok) - goto out; - } else { - rxml = DeepCopyInLRS(cx, vxml, 0); - if (!rxml || !js_GetXMLObject(cx, rxml)) - goto bad; - vxml = rxml; - *vp = OBJECT_TO_JSVAL(vxml->object); - } - roots[VAL_ROOT] = *vp; - - /* - * 6. - * Erratum: why is this done here, so early? use is way later.... - */ - ok = js_GetDefaultXMLNamespace(cx, &nsval); - if (!ok) - goto out; - - if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { - /* 7(a). */ - if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj))) - goto out; - - /* 7(b-c). */ - if (vxml && vxml->xml_class == JSXML_CLASS_LIST) { - n = vxml->xml_kids.length; - if (n == 0) { - *vp = STRING_TO_JSVAL(cx->runtime->emptyString); - } else { - left = KidToString(cx, vxml, 0); - if (!left) - goto bad; - - space = ATOM_TO_STRING(cx->runtime->atomState.spaceAtom); - for (i = 1; i < n; i++) { - left = js_ConcatStrings(cx, left, space); - if (!left) - goto bad; - right = KidToString(cx, vxml, i); - if (!right) - goto bad; - left = js_ConcatStrings(cx, left, right); - if (!left) - goto bad; - } - - roots[VAL_ROOT] = *vp = STRING_TO_JSVAL(left); - } - } else { - ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); - if (!ok) - goto out; - roots[VAL_ROOT] = *vp; - } - - /* 7(d-e). */ - match = NULL; - for (i = 0, n = xml->xml_attrs.length; i < n; i++) { - attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); - if (!attr) - continue; - attrqn = attr->name; - if (js_EqualStrings(attrqn->localName, nameqn->localName) && - (!nameqn->uri || - js_EqualStrings(attrqn->uri, nameqn->uri))) { - if (!match) { - match = attr; - } else { - nameobj = js_GetAttributeNameObject(cx, attrqn); - if (!nameobj) - goto bad; - - id = OBJECT_TO_JSVAL(nameobj); - ok = DeleteProperty(cx, obj, id, &junk); - if (!ok) - goto out; - --i; - } - } - } - - /* 7(f). */ - attr = match; - if (!attr) { - /* 7(f)(i-ii). */ - if (!nameqn->uri) { - left = right = cx->runtime->emptyString; - } else { - left = nameqn->uri; - right = nameqn->prefix; - } - nameqn = js_NewXMLQName(cx, left, right, nameqn->localName); - if (!nameqn) - goto bad; - - /* 7(f)(iii). */ - attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); - if (!attr) - goto bad; - attr->parent = xml; - attr->name = nameqn; - - /* 7(f)(iv). */ - ok = XMLARRAY_ADD_MEMBER(cx, &xml->xml_attrs, n, attr); - if (!ok) - goto out; - - /* 7(f)(v-vi). */ - ns = GetNamespace(cx, nameqn, NULL); - if (!ns) - goto bad; - ok = AddInScopeNamespace(cx, xml, ns); - if (!ok) - goto out; - } - - /* 7(g). */ - attr->xml_value = JSVAL_TO_STRING(*vp); - goto out; - } - - /* 8-9. */ - if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)) && - !IS_STAR(nameqn->localName)) { - goto out; - } - - /* 10-11. */ - id = JSVAL_VOID; - primitiveAssign = !vxml && !IS_STAR(nameqn->localName); - - /* 12. */ - k = n = xml->xml_kids.length; - kid2 = NULL; - while (k != 0) { - --k; - kid = XMLARRAY_MEMBER(&xml->xml_kids, k, JSXML); - if (kid && MatchElemName(nameqn, kid)) { - if (!JSVAL_IS_VOID(id)) { - ok = DeleteByIndex(cx, xml, id, &junk); - if (!ok) - goto out; - } - ok = IndexToIdVal(cx, k, &id); - if (!ok) - goto out; - kid2 = kid; - } - } - - /* - * Erratum: ECMA-357 specified child insertion inconsistently: - * insertChildBefore and insertChildAfter insert an arbitrary XML - * instance, and therefore can create cycles, but appendChild as - * specified by the "Overview" of 13.4.4.3 calls [[DeepCopy]] on - * its argument. But the "Semantics" in 13.4.4.3 do not include - * any [[DeepCopy]] call. - * - * Fixing this (https://bugzilla.mozilla.org/show_bug.cgi?id=312692) - * required adding cycle detection, and allowing duplicate kids to - * be created (see comment 6 in the bug). Allowing duplicate kid - * references means the loop above will delete all but the lowest - * indexed reference, and each [[DeleteByIndex]] nulls the kid's - * parent. Thus the need to restore parent here. This is covered - * by https://bugzilla.mozilla.org/show_bug.cgi?id=327564. - */ - if (kid2) { - JS_ASSERT(kid2->parent == xml || !kid2->parent); - if (!kid2->parent) - kid2->parent = xml; - } - - /* 13. */ - if (JSVAL_IS_VOID(id)) { - /* 13(a). */ - ok = IndexToIdVal(cx, n, &id); - if (!ok) - goto out; - - /* 13(b). */ - if (primitiveAssign) { - if (!nameqn->uri) { - ns = (JSXMLNamespace *) - JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); - left = ns->uri; - right = ns->prefix; - } else { - left = nameqn->uri; - right = nameqn->prefix; - } - nameqn = js_NewXMLQName(cx, left, right, nameqn->localName); - if (!nameqn) - goto bad; - - /* 13(b)(iii). */ - vobj = js_NewXMLObject(cx, JSXML_CLASS_ELEMENT); - if (!vobj) - goto bad; - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - vxml->parent = xml; - vxml->name = nameqn; - - /* 13(b)(iv-vi). */ - ns = GetNamespace(cx, nameqn, NULL); - if (!ns) - goto bad; - ok = Replace(cx, xml, id, OBJECT_TO_JSVAL(vobj)); - if (!ok) - goto out; - ok = AddInScopeNamespace(cx, vxml, ns); - if (!ok) - goto out; - } - } - - /* 14. */ - if (primitiveAssign) { - JSXMLArrayCursor cursor; - - js_IdIsIndex(id, &index); - XMLArrayCursorInit(&cursor, &xml->xml_kids); - cursor.index = index; - kid = (JSXML *) XMLArrayCursorItem(&cursor); - if (JSXML_HAS_KIDS(kid)) { - XMLArrayFinish(cx, &kid->xml_kids); - ok = XMLArrayInit(cx, &kid->xml_kids, 1); - } - - /* 14(b-c). */ - /* XXXbe Erratum? redundant w.r.t. 7(b-c) else clause above */ - if (ok) { - ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); - if (ok && !IS_EMPTY(JSVAL_TO_STRING(*vp))) { - roots[VAL_ROOT] = *vp; - if ((JSXML *) XMLArrayCursorItem(&cursor) == kid) - ok = Replace(cx, kid, JSVAL_ZERO, *vp); - } - } - XMLArrayCursorFinish(&cursor); - } else { - /* 15(a). */ - ok = Replace(cx, xml, id, *vp); - } - } - -out: - JS_POP_TEMP_ROOT(cx, &tvr); - js_LeaveLocalRootScope(cx); - return ok; - -type_error: - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XMLLIST_PUT, - js_ValueToPrintableString(cx, id)); -bad: - ok = JS_FALSE; - goto out; -} - -/* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */ -static JSBool -ResolveValue(JSContext *cx, JSXML *list, JSXML **result) -{ - JSXML *target, *base; - JSXMLQName *targetprop; - JSObject *targetpropobj; - jsval id, tv; - - /* Our caller must be protecting newborn objects. */ - JS_ASSERT(cx->localRootStack); - - if (list->xml_class != JSXML_CLASS_LIST || list->xml_kids.length != 0) { - if (!js_GetXMLObject(cx, list)) - return JS_FALSE; - *result = list; - return JS_TRUE; - } - - target = list->xml_target; - targetprop = list->xml_targetprop; - if (!target || !targetprop || IS_STAR(targetprop->localName)) { - *result = NULL; - return JS_TRUE; - } - - targetpropobj = js_GetXMLQNameObject(cx, targetprop); - if (!targetpropobj) - return JS_FALSE; - if (OBJ_GET_CLASS(cx, targetpropobj) == &js_AttributeNameClass) { - *result = NULL; - return JS_TRUE; - } - - if (!ResolveValue(cx, target, &base)) - return JS_FALSE; - if (!base) { - *result = NULL; - return JS_TRUE; - } - if (!js_GetXMLObject(cx, base)) - return JS_FALSE; - - id = OBJECT_TO_JSVAL(targetpropobj); - if (!GetProperty(cx, base->object, id, &tv)) - return JS_FALSE; - target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv)); - - if (JSXML_LENGTH(target) == 0) { - if (base->xml_class == JSXML_CLASS_LIST && JSXML_LENGTH(base) > 1) { - *result = NULL; - return JS_TRUE; - } - tv = STRING_TO_JSVAL(cx->runtime->emptyString); - if (!PutProperty(cx, base->object, id, &tv)) - return JS_FALSE; - if (!GetProperty(cx, base->object, id, &tv)) - return JS_FALSE; - target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv)); - } - - *result = target; - return JS_TRUE; -} - -/* - * HasProperty must be able to return a found JSProperty and the object in - * which it was found, if id is of the form function::name. For other ids, - * if they index or name an XML child, we return FOUND_XML_PROPERTY in *propp - * and null in *objp. - * - * DROP_PROPERTY helps HasProperty callers drop function properties without - * trying to drop the magic FOUND_XML_PROPERTY cookie. - */ -#define FOUND_XML_PROPERTY ((JSProperty *) 1) -#define DROP_PROPERTY(cx,pobj,prop) (((prop) != FOUND_XML_PROPERTY) \ - ? OBJ_DROP_PROPERTY(cx, pobj, prop) \ - : (void) 0) - -/* ECMA-357 9.1.1.6 XML [[HasProperty]] and 9.2.1.5 XMLList [[HasProperty]]. */ -static JSBool -HasProperty(JSContext *cx, JSObject *obj, jsval id, JSObject **objp, - JSProperty **propp) -{ - JSXML *xml, *kid; - JSXMLArrayCursor cursor; - JSObject *kidobj; - JSXMLQName *qn; - jsid funid; - JSXMLArray *array; - JSXMLNameMatcher matcher; - uint32 i, n; - - *objp = NULL; - *propp = NULL; - - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (xml->xml_class == JSXML_CLASS_LIST) { - n = JSXML_LENGTH(xml); - if (js_IdIsIndex(id, &i)) { - if (i < n) - *propp = FOUND_XML_PROPERTY; - return JS_TRUE; - } - - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (kid->xml_class == JSXML_CLASS_ELEMENT) { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj || !HasProperty(cx, kidobj, id, objp, propp)) - break; - if (*propp) - break; - } - } - XMLArrayCursorFinish(&cursor); - if (kid) - return *propp != NULL; - } else { - if (xml->xml_class == JSXML_CLASS_ELEMENT && js_IdIsIndex(id, &i)) { - if (i == 0) - *propp = FOUND_XML_PROPERTY; - return JS_TRUE; - } - - qn = ToXMLName(cx, id, &funid); - if (!qn) - return JS_FALSE; - if (funid) - return js_LookupProperty(cx, obj, funid, objp, propp); - - if (xml->xml_class != JSXML_CLASS_ELEMENT) - return JS_TRUE; - - if (OBJ_GET_CLASS(cx, qn->object) == &js_AttributeNameClass) { - array = &xml->xml_attrs; - matcher = MatchAttrName; - } else { - array = &xml->xml_kids; - matcher = MatchElemName; - } - for (i = 0, n = array->length; i < n; i++) { - kid = XMLARRAY_MEMBER(array, i, JSXML); - if (kid && matcher(qn, kid)) { - *propp = FOUND_XML_PROPERTY; - return JS_TRUE; - } - } - } - - return JS_TRUE; -} - -static void -xml_finalize(JSContext *cx, JSObject *obj) -{ - JSXML *xml; - - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (!xml) - return; - if (xml->object == obj) - xml->object = NULL; - UNMETER(xml_stats.livexmlobj); -} - -static void -xml_mark_vector(JSContext *cx, JSXML **vec, uint32 len) -{ - uint32 i; - JSXML *elt; - - for (i = 0; i < len; i++) { - elt = vec[i]; - { -#ifdef GC_MARK_DEBUG - char buf[120]; - - if (elt->xml_class == JSXML_CLASS_LIST) { - strcpy(buf, js_XMLList_str); - } else if (JSXML_HAS_NAME(elt)) { - JSXMLQName *qn = elt->name; - - JS_snprintf(buf, sizeof buf, "%s::%s", - qn->uri ? JS_GetStringBytes(qn->uri) : "*", - JS_GetStringBytes(qn->localName)); - } else { - JSString *str = elt->xml_value; - size_t srclen = JSSTRING_LENGTH(str); - size_t dstlen = sizeof buf; - - if (srclen >= sizeof buf / 6) - srclen = sizeof buf / 6 - 1; - js_DeflateStringToBuffer(cx, JSSTRING_CHARS(str), srclen, - buf, &dstlen); - } -#endif - GC_MARK(cx, elt, buf); - } - } -} - -/* - * js_XMLObjectOps.newObjectMap == js_NewObjectMap, so XML objects appear to - * be native. Therefore, xml_lookupProperty must return a valid JSProperty - * pointer parameter via *propp to signify "property found". Since the only - * call to xml_lookupProperty is via OBJ_LOOKUP_PROPERTY, and then only from - * js_FindXMLProperty (in this file), js_FindProperty (in jsobj.c, called from - * jsinterp.c) or from JSOP_IN case in the interpreter, the only time we add a - * JSScopeProperty here is when an unqualified name or XML name is being - * accessed or when "name in xml" is called. - * - * This scope property keeps the JSOP_NAME code in js_Interpret happy by - * giving it an sprop with (getter, setter) == (GetProperty, PutProperty). - * - * NB: xml_deleteProperty must take care to remove any property added here. - * - * FIXME This clashes with the function namespace implementation which also - * uses native properties. Effectively after xml_lookupProperty any property - * stored previously using assignments to xml.function::name will be removed. - * We partially workaround the problem in js_GetXMLFunction. There we take - * advantage of the fact that typically function:: is used to access the - * functions from XML.prototype. So when js_GetProperty returns a non-function - * property, we assume that it represents the result of GetProperty setter - * hiding the function and use an extra prototype chain lookup to recover it. - * For a proper solution see bug 355257. - */ -static JSBool -xml_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - JSProperty **propp) -{ - JSScopeProperty *sprop; - - if (!HasProperty(cx, obj, ID_TO_VALUE(id), objp, propp)) - return JS_FALSE; - - if (*propp == FOUND_XML_PROPERTY) { - sprop = js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty, - SPROP_INVALID_SLOT, JSPROP_ENUMERATE, - 0, 0); - if (!sprop) - return JS_FALSE; - - JS_LOCK_OBJ(cx, obj); - *objp = obj; - *propp = (JSProperty *) sprop; - } - return JS_TRUE; -} - -static JSBool -xml_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs, - JSProperty **propp) -{ - if (VALUE_IS_FUNCTION(cx, value) || getter || setter || - (attrs & JSPROP_ENUMERATE) == 0 || - (attrs & (JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED))) { - return js_DefineProperty(cx, obj, id, value, getter, setter, attrs, - propp); - } - - if (!PutProperty(cx, obj, ID_TO_VALUE(id), &value)) - return JS_FALSE; - if (propp) - *propp = NULL; - return JS_TRUE; -} - -static JSBool -xml_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - if (id == JS_DEFAULT_XML_NAMESPACE_ID) { - *vp = JSVAL_VOID; - return JS_TRUE; - } - - return GetProperty(cx, obj, ID_TO_VALUE(id), vp); -} - -static JSBool -xml_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - return PutProperty(cx, obj, ID_TO_VALUE(id), vp); -} - -static JSBool -FoundProperty(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, - JSBool *foundp) -{ - JSObject *pobj; - - if (prop) { - *foundp = JS_TRUE; - } else { - if (!HasProperty(cx, obj, ID_TO_VALUE(id), &pobj, &prop)) - return JS_FALSE; - if (prop) - DROP_PROPERTY(cx, pobj, prop); - *foundp = (prop != NULL); - } - return JS_TRUE; -} - -static JSBool -xml_getAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, - uintN *attrsp) -{ - JSBool found; - - if (!FoundProperty(cx, obj, id, prop, &found)) - return JS_FALSE; - *attrsp = found ? JSPROP_ENUMERATE : 0; - return JS_TRUE; -} - -static JSBool -xml_setAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, - uintN *attrsp) -{ - JSBool found; - - if (!FoundProperty(cx, obj, id, prop, &found)) - return JS_FALSE; - if (found) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CANT_SET_XML_ATTRS); - } - return !found; -} - -static JSBool -xml_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) -{ - /* - * If this object has its own (mutable) scope, and if id isn't an index, - * then we may have added a property to the scope in xml_lookupProperty - * for it to return to mean "found" and to provide a handle for access - * operations to call the property's getter or setter. The property also - * helps speed up unqualified accesses via the property cache, avoiding - * what amount to two HasProperty searches. - * - * But now it's time to remove any such property, to purge the property - * cache and remove the scope entry. - */ - if (OBJ_SCOPE(obj)->object == obj && !JSID_IS_INT(id)) { - if (!js_DeleteProperty(cx, obj, id, rval)) - return JS_FALSE; - } - - return DeleteProperty(cx, obj, ID_TO_VALUE(id), rval); -} - -static JSBool -xml_defaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) -{ - JSXML *xml; - - if (hint == JSTYPE_OBJECT) { - /* Called from for..in code in js_Interpret: return an XMLList. */ - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (xml->xml_class != JSXML_CLASS_LIST) { - obj = ToXMLList(cx, OBJECT_TO_JSVAL(obj)); - if (!obj) - return JS_FALSE; - } - *vp = OBJECT_TO_JSVAL(obj); - return JS_TRUE; - } - - return JS_CallFunctionName(cx, obj, js_toString_str, 0, NULL, vp); -} - -static JSBool -xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, - jsval *statep, jsid *idp) -{ - JSXML *xml; - uint32 length, index; - JSXMLArrayCursor *cursor; - - xml = (JSXML *) JS_GetPrivate(cx, obj); - length = JSXML_LENGTH(xml); - - switch (enum_op) { - case JSENUMERATE_INIT: - if (length == 0) { - cursor = NULL; - } else { - cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor); - if (!cursor) - return JS_FALSE; - XMLArrayCursorInit(cursor, &xml->xml_kids); - } - *statep = PRIVATE_TO_JSVAL(cursor); - if (idp) - *idp = INT_TO_JSID(length); - break; - - case JSENUMERATE_NEXT: - cursor = JSVAL_TO_PRIVATE(*statep); - if (cursor && cursor->array && (index = cursor->index) < length) { - *idp = INT_TO_JSID(index); - cursor->index = index + 1; - break; - } - /* FALL THROUGH */ - - case JSENUMERATE_DESTROY: - cursor = JSVAL_TO_PRIVATE(*statep); - if (cursor) { - XMLArrayCursorFinish(cursor); - JS_free(cx, cursor); - } - *statep = JSVAL_NULL; - break; - } - return JS_TRUE; -} - -static JSBool -xml_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - return JS_TRUE; -} - -static uint32 -xml_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSXML *xml; - - xml = (JSXML *) JS_GetPrivate(cx, obj); - GC_MARK(cx, xml, "private"); - return js_Mark(cx, obj, NULL); -} - -static void -xml_clear(JSContext *cx, JSObject *obj) -{ -} - -static JSBool -HasSimpleContent(JSXML *xml) -{ - JSXML *kid; - JSBool simple; - uint32 i, n; - -again: - switch (xml->xml_class) { - case JSXML_CLASS_COMMENT: - case JSXML_CLASS_PROCESSING_INSTRUCTION: - return JS_FALSE; - case JSXML_CLASS_LIST: - if (xml->xml_kids.length == 0) - return JS_TRUE; - if (xml->xml_kids.length == 1) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (kid) { - xml = kid; - goto again; - } - } - /* FALL THROUGH */ - default: - simple = JS_TRUE; - for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { - simple = JS_FALSE; - break; - } - } - return simple; - } -} - -/* - * 11.2.2.1 Step 3(d) onward. - */ -static JSObject * -xml_getMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - JSTempValueRooter tvr; - - JS_ASSERT(JS_InstanceOf(cx, obj, &js_XMLClass, NULL)); - - /* - * As our callers have a bad habit of passing a pointer to an unrooted - * local value as vp, we use a proper root here. - */ - JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); - if (!js_GetXMLFunction(cx, obj, id, &tvr.u.value)) - obj = NULL; - *vp = tvr.u.value; - JS_POP_TEMP_ROOT(cx, &tvr); - return obj; -} - -static JSBool -xml_setMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - return js_SetProperty(cx, obj, id, vp); -} - -static JSBool -xml_enumerateValues(JSContext *cx, JSObject *obj, JSIterateOp enum_op, - jsval *statep, jsid *idp, jsval *vp) -{ - JSXML *xml, *kid; - uint32 length, index; - JSXMLArrayCursor *cursor; - JSObject *kidobj; - - xml = (JSXML *) JS_GetPrivate(cx, obj); - length = JSXML_LENGTH(xml); - JS_ASSERT(INT_FITS_IN_JSVAL(length)); - - switch (enum_op) { - case JSENUMERATE_INIT: - if (length == 0) { - cursor = NULL; - } else { - cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor); - if (!cursor) - return JS_FALSE; - XMLArrayCursorInit(cursor, &xml->xml_kids); - } - *statep = PRIVATE_TO_JSVAL(cursor); - if (idp) - *idp = INT_TO_JSID(length); - if (vp) - *vp = JSVAL_VOID; - break; - - case JSENUMERATE_NEXT: - cursor = JSVAL_TO_PRIVATE(*statep); - if (cursor && cursor->array && (index = cursor->index) < length) { - while (!(kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML))) { - if (++index == length) - goto destroy; - } - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - return JS_FALSE; - JS_ASSERT(INT_FITS_IN_JSVAL(index)); - *idp = INT_TO_JSID(index); - *vp = OBJECT_TO_JSVAL(kidobj); - cursor->index = index + 1; - break; - } - /* FALL THROUGH */ - - case JSENUMERATE_DESTROY: - cursor = JSVAL_TO_PRIVATE(*statep); - if (cursor) { - destroy: - XMLArrayCursorFinish(cursor); - JS_free(cx, cursor); - } - *statep = JSVAL_NULL; - break; - } - return JS_TRUE; -} - -static JSBool -xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - JSXML *xml, *vxml; - JSObject *vobj; - JSBool ok; - JSString *str, *vstr; - jsdouble d, d2; - - xml = (JSXML *) JS_GetPrivate(cx, obj); - vxml = NULL; - if (!JSVAL_IS_PRIMITIVE(v)) { - vobj = JSVAL_TO_OBJECT(v); - if (OBJECT_IS_XML(cx, vobj)) - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - } - - if (xml->xml_class == JSXML_CLASS_LIST) { - ok = Equals(cx, xml, v, bp); - } else if (vxml) { - if (vxml->xml_class == JSXML_CLASS_LIST) { - ok = Equals(cx, vxml, OBJECT_TO_JSVAL(obj), bp); - } else { - if (((xml->xml_class == JSXML_CLASS_TEXT || - xml->xml_class == JSXML_CLASS_ATTRIBUTE) && - HasSimpleContent(vxml)) || - ((vxml->xml_class == JSXML_CLASS_TEXT || - vxml->xml_class == JSXML_CLASS_ATTRIBUTE) && - HasSimpleContent(xml))) { - ok = js_EnterLocalRootScope(cx); - if (ok) { - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - vstr = js_ValueToString(cx, v); - ok = str && vstr; - if (ok) - *bp = js_EqualStrings(str, vstr); - js_LeaveLocalRootScope(cx); - } - } else { - ok = XMLEquals(cx, xml, vxml, bp); - } - } - } else { - ok = js_EnterLocalRootScope(cx); - if (ok) { - if (HasSimpleContent(xml)) { - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - vstr = js_ValueToString(cx, v); - ok = str && vstr; - if (ok) - *bp = js_EqualStrings(str, vstr); - } else if (JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v)) { - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) { - ok = JS_FALSE; - } else if (JSVAL_IS_STRING(v)) { - *bp = js_EqualStrings(str, JSVAL_TO_STRING(v)); - } else { - ok = js_ValueToNumber(cx, STRING_TO_JSVAL(str), &d); - if (ok) { - d2 = JSVAL_IS_INT(v) ? JSVAL_TO_INT(v) - : *JSVAL_TO_DOUBLE(v); - *bp = JSDOUBLE_COMPARE(d, ==, d2, JS_FALSE); - } - } - } else { - *bp = JS_FALSE; - } - js_LeaveLocalRootScope(cx); - } - } - return ok; -} - -static JSBool -xml_concatenate(JSContext *cx, JSObject *obj, jsval v, jsval *vp) -{ - JSBool ok; - JSObject *listobj, *robj; - JSXML *list, *lxml, *rxml; - - ok = js_EnterLocalRootScope(cx); - if (!ok) - return JS_FALSE; - - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) { - ok = JS_FALSE; - goto out; - } - - list = (JSXML *) JS_GetPrivate(cx, listobj); - lxml = (JSXML *) JS_GetPrivate(cx, obj); - ok = Append(cx, list, lxml); - if (!ok) - goto out; - - if (VALUE_IS_XML(cx, v)) { - rxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); - } else { - robj = ToXML(cx, v); - if (!robj) { - ok = JS_FALSE; - goto out; - } - rxml = (JSXML *) JS_GetPrivate(cx, robj); - } - ok = Append(cx, list, rxml); - if (!ok) - goto out; - - *vp = OBJECT_TO_JSVAL(listobj); -out: - js_LeaveLocalRootScopeWithResult(cx, *vp); - return ok; -} - -/* Use js_NewObjectMap so XML objects satisfy OBJ_IS_NATIVE tests. */ -JS_FRIEND_DATA(JSXMLObjectOps) js_XMLObjectOps = { - { js_NewObjectMap, js_DestroyObjectMap, - xml_lookupProperty, xml_defineProperty, - xml_getProperty, xml_setProperty, - xml_getAttributes, xml_setAttributes, - xml_deleteProperty, xml_defaultValue, - xml_enumerate, js_CheckAccess, - NULL, NULL, - NULL, NULL, - NULL, xml_hasInstance, - js_SetProtoOrParent, js_SetProtoOrParent, - xml_mark, xml_clear, - NULL, NULL }, - xml_getMethod, xml_setMethod, - xml_enumerateValues, xml_equality, - xml_concatenate -}; - -static JSObjectOps * -xml_getObjectOps(JSContext *cx, JSClass *clasp) -{ - return &js_XMLObjectOps.base; -} - -JS_FRIEND_DATA(JSClass) js_XMLClass = { - js_XML_str, - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_XML), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, xml_finalize, - xml_getObjectOps, NULL, NULL, NULL, - NULL, NULL, NULL, NULL -}; - -static JSObject * -CallConstructorFunction(JSContext *cx, JSObject *obj, JSClass *clasp, - uintN argc, jsval *argv) -{ - JSObject *tmp; - jsval rval; - - while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) - obj = tmp; - if (!JS_CallFunctionName(cx, obj, clasp->name, argc, argv, &rval)) - return NULL; - JS_ASSERT(!JSVAL_IS_PRIMITIVE(rval)); - return JSVAL_TO_OBJECT(rval); -} - -static JSXML * -StartNonListXMLMethod(JSContext *cx, JSObject **objp, jsval *argv) -{ - JSXML *xml; - JSFunction *fun; - - JS_ASSERT(VALUE_IS_FUNCTION(cx, argv[-2])); - - xml = (JSXML *) JS_GetInstancePrivate(cx, *objp, &js_XMLClass, argv); - if (!xml || xml->xml_class != JSXML_CLASS_LIST) - return xml; - - if (xml->xml_kids.length == 1) { - xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (xml) { - *objp = js_GetXMLObject(cx, xml); - if (!*objp) - return NULL; - argv[-1] = OBJECT_TO_JSVAL(*objp); - return xml; - } - } - - fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[-2])); - if (fun) { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%u", xml->xml_kids.length); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_NON_LIST_XML_METHOD, - JS_GetFunctionName(fun), numBuf); - } - return NULL; -} - -#define XML_METHOD_PROLOG \ - JS_BEGIN_MACRO \ - xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, argv); \ - if (!xml) \ - return JS_FALSE; \ - JS_END_MACRO - -#define NON_LIST_XML_METHOD_PROLOG \ - JS_BEGIN_MACRO \ - xml = StartNonListXMLMethod(cx, &obj, argv); \ - if (!xml) \ - return JS_FALSE; \ - JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); \ - JS_END_MACRO - -static JSBool -xml_addNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - JSObject *nsobj; - JSXMLNamespace *ns; - - NON_LIST_XML_METHOD_PROLOG; - if (xml->xml_class != JSXML_CLASS_ELEMENT) - return JS_TRUE; - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - - nsobj = CallConstructorFunction(cx, obj, &js_NamespaceClass.base, 1, argv); - if (!nsobj) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(nsobj); - - ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); - if (!AddInScopeNamespace(cx, xml, ns)) - return JS_FALSE; - ns->declared = JS_TRUE; - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -static JSBool -xml_appendChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *vxml; - jsval name, v; - JSObject *vobj; - - NON_LIST_XML_METHOD_PROLOG; - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - - if (!js_GetAnyName(cx, &name)) - return JS_FALSE; - - if (!GetProperty(cx, obj, name, &v)) - return JS_FALSE; - - JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); - vobj = JSVAL_TO_OBJECT(v); - JS_ASSERT(OBJECT_IS_XML(cx, vobj)); - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - JS_ASSERT(vxml->xml_class == JSXML_CLASS_LIST); - - if (!IndexToIdVal(cx, vxml->xml_kids.length, &name)) - return JS_FALSE; - if (!PutProperty(cx, JSVAL_TO_OBJECT(v), name, &argv[0])) - return JS_FALSE; - - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_attribute(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXMLQName *qn; - - qn = ToAttributeName(cx, argv[0]); - if (!qn) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(qn->object); /* local root */ - return GetProperty(cx, obj, argv[0], rval); -} - -/* XML and XMLList */ -static JSBool -xml_attributes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsval name; - JSXMLQName *qn; - JSTempValueRooter tvr; - JSBool ok; - - name = ATOM_KEY(cx->runtime->atomState.starAtom); - qn = ToAttributeName(cx, name); - if (!qn) - return JS_FALSE; - name = OBJECT_TO_JSVAL(qn->object); - JS_PUSH_SINGLE_TEMP_ROOT(cx, name, &tvr); - ok = GetProperty(cx, obj, name, rval); - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; -} - -static JSXML * -xml_list_helper(JSContext *cx, JSXML *xml, jsval *rval) -{ - JSObject *listobj; - JSXML *list; - - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) - return NULL; - - *rval = OBJECT_TO_JSVAL(listobj); - list = (JSXML *) JS_GetPrivate(cx, listobj); - list->xml_target = xml; - return list; -} - -static JSBool -xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name, - jsval *rval) -{ - uint32 index; - JSXML *kid; - JSObject *kidobj; - - /* ECMA-357 13.4.4.6 */ - JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); - - if (js_IdIsIndex(name, &index)) { - if (index >= JSXML_LENGTH(xml)) { - *rval = JSVAL_VOID; - } else { - kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); - if (!kid) { - *rval = JSVAL_VOID; - } else { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(kidobj); - } - } - return JS_TRUE; - } - - return GetProperty(cx, obj, name, rval); -} - -/* XML and XMLList */ -static JSBool -xml_child(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml, *list, *kid, *vxml; - JSXMLArrayCursor cursor; - jsval name, v; - JSObject *kidobj; - - XML_METHOD_PROLOG; - name = argv[0]; - if (xml->xml_class == JSXML_CLASS_LIST) { - /* ECMA-357 13.5.4.4 */ - list = xml_list_helper(cx, xml, rval); - if (!list) - return JS_FALSE; - - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - break; - if (!xml_child_helper(cx, kidobj, kid, name, &v)) - break; - if (JSVAL_IS_VOID(v)) { - /* The property didn't exist in this kid. */ - continue; - } - - JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); - vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); - if ((!JSXML_HAS_KIDS(vxml) || vxml->xml_kids.length != 0) && - !Append(cx, list, vxml)) { - break; - } - } - XMLArrayCursorFinish(&cursor); - return !kid; - } - - /* ECMA-357 Edition 2 13.3.4.6 (note 13.3, not 13.4 as in Edition 1). */ - if (!xml_child_helper(cx, obj, xml, name, rval)) - return JS_FALSE; - if (JSVAL_IS_VOID(*rval) && !xml_list_helper(cx, xml, rval)) - return JS_FALSE; - return JS_TRUE; -} - -static JSBool -xml_childIndex(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *parent; - uint32 i, n; - - NON_LIST_XML_METHOD_PROLOG; - parent = xml->parent; - if (!parent || xml->xml_class == JSXML_CLASS_ATTRIBUTE) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); - return JS_TRUE; - } - for (i = 0, n = JSXML_LENGTH(parent); i < n; i++) { - if (XMLARRAY_MEMBER(&parent->xml_kids, i, JSXML) == xml) - break; - } - JS_ASSERT(i < n); - return js_NewNumberValue(cx, i, rval); -} - -/* XML and XMLList */ -static JSBool -xml_children(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsval name; - - name = ATOM_KEY(cx->runtime->atomState.starAtom); - return GetProperty(cx, obj, name, rval); -} - -/* XML and XMLList */ -static JSBool -xml_comments(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *list, *kid, *vxml; - JSBool ok; - uint32 i, n; - JSObject *kidobj; - jsval v; - - XML_METHOD_PROLOG; - list = xml_list_helper(cx, xml, rval); - if (!list) - return JS_FALSE; - - ok = JS_TRUE; - - if (xml->xml_class == JSXML_CLASS_LIST) { - /* 13.5.4.6 Step 2. */ - for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { - ok = js_EnterLocalRootScope(cx); - if (!ok) - break; - kidobj = js_GetXMLObject(cx, kid); - if (kidobj) { - ok = xml_comments(cx, kidobj, argc, argv, &v); - } else { - ok = JS_FALSE; - v = JSVAL_NULL; - } - js_LeaveLocalRootScopeWithResult(cx, v); - if (!ok) - break; - vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); - if (JSXML_LENGTH(vxml) != 0) { - ok = Append(cx, list, vxml); - if (!ok) - break; - } - } - } - } else { - /* 13.4.4.9 Step 2. */ - for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_COMMENT) { - ok = Append(cx, list, kid); - if (!ok) - break; - } - } - } - - return ok; -} - -/* XML and XMLList */ -static JSBool -xml_contains(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *kid; - jsval value; - JSBool eq; - JSXMLArrayCursor cursor; - JSObject *kidobj; - - XML_METHOD_PROLOG; - value = argv[0]; - if (xml->xml_class == JSXML_CLASS_LIST) { - eq = JS_FALSE; - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj || !xml_equality(cx, kidobj, value, &eq)) - break; - if (eq) - break; - } - XMLArrayCursorFinish(&cursor); - if (kid && !eq) - return JS_FALSE; - } else { - if (!xml_equality(cx, obj, value, &eq)) - return JS_FALSE; - } - *rval = BOOLEAN_TO_JSVAL(eq); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_copy(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml, *copy; - - XML_METHOD_PROLOG; - copy = DeepCopy(cx, xml, NULL, 0); - if (!copy) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(copy->object); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_descendants(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *list; - jsval name; - - XML_METHOD_PROLOG; - name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0]; - list = Descendants(cx, xml, name); - if (!list) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(list->object); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_elements(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *list, *kid, *vxml; - jsval name, v; - JSXMLQName *nameqn; - jsid funid; - JSBool ok; - JSXMLArrayCursor cursor; - JSObject *kidobj; - uint32 i, n; - - XML_METHOD_PROLOG; - name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0]; - nameqn = ToXMLName(cx, name, &funid); - if (!nameqn) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(nameqn->object); - - list = xml_list_helper(cx, xml, rval); - if (!list) - return JS_FALSE; - if (funid) - return JS_TRUE; - - list->xml_targetprop = nameqn; - ok = JS_TRUE; - - if (xml->xml_class == JSXML_CLASS_LIST) { - /* 13.5.4.6 */ - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (kid->xml_class == JSXML_CLASS_ELEMENT) { - ok = js_EnterLocalRootScope(cx); - if (!ok) - break; - kidobj = js_GetXMLObject(cx, kid); - if (kidobj) { - ok = xml_elements(cx, kidobj, argc, argv, &v); - } else { - ok = JS_FALSE; - v = JSVAL_NULL; - } - js_LeaveLocalRootScopeWithResult(cx, v); - if (!ok) - break; - vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); - if (JSXML_LENGTH(vxml) != 0) { - ok = Append(cx, list, vxml); - if (!ok) - break; - } - } - } - XMLArrayCursorFinish(&cursor); - } else { - for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT && - MatchElemName(nameqn, kid)) { - ok = Append(cx, list, kid); - if (!ok) - break; - } - } - } - - return ok; -} - -/* XML and XMLList */ -static JSBool -xml_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsval name; - JSObject *pobj; - JSProperty *prop; - - if (!JS_InstanceOf(cx, obj, &js_XMLClass, argv)) - return JS_FALSE; - - name = argv[0]; - if (!HasProperty(cx, obj, name, &pobj, &prop)) - return JS_FALSE; - if (!prop) { - return js_HasOwnPropertyHelper(cx, obj, js_LookupProperty, argc, argv, - rval); - } - DROP_PROPERTY(cx, pobj, prop); - *rval = JSVAL_TRUE; - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_hasComplexContent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *kid; - JSObject *kidobj; - uint32 i, n; - - XML_METHOD_PROLOG; -again: - switch (xml->xml_class) { - case JSXML_CLASS_ATTRIBUTE: - case JSXML_CLASS_COMMENT: - case JSXML_CLASS_PROCESSING_INSTRUCTION: - case JSXML_CLASS_TEXT: - *rval = JSVAL_FALSE; - break; - case JSXML_CLASS_LIST: - if (xml->xml_kids.length == 0) { - *rval = JSVAL_TRUE; - } else if (xml->xml_kids.length == 1) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (kid) { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - return JS_FALSE; - obj = kidobj; - xml = (JSXML *) JS_GetPrivate(cx, obj); - goto again; - } - } - /* FALL THROUGH */ - default: - *rval = JSVAL_FALSE; - for (i = 0, n = xml->xml_kids.length; i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { - *rval = JSVAL_TRUE; - break; - } - } - break; - } - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_hasSimpleContent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - - XML_METHOD_PROLOG; - *rval = BOOLEAN_TO_JSVAL(HasSimpleContent(xml)); - return JS_TRUE; -} - -typedef struct JSTempRootedNSArray { - JSTempValueRooter tvr; - JSXMLArray array; - jsval value; /* extra root for temporaries */ -} JSTempRootedNSArray; - -JS_STATIC_DLL_CALLBACK(void) -mark_temp_ns_array(JSContext *cx, JSTempValueRooter *tvr) -{ - JSTempRootedNSArray *tmp = (JSTempRootedNSArray *)tvr; - - namespace_mark_vector(cx, - (JSXMLNamespace **)tmp->array.vector, - tmp->array.length); - XMLArrayCursorMark(cx, tmp->array.cursors); - if (JSVAL_IS_GCTHING(tmp->value)) - GC_MARK(cx, JSVAL_TO_GCTHING(tmp->value), "temp_ns_array_value"); -} - -static void -InitTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp) -{ - XMLArrayInit(cx, &tmp->array, 0); - tmp->value = JSVAL_NULL; - JS_PUSH_TEMP_ROOT_MARKER(cx, mark_temp_ns_array, &tmp->tvr); -} - -static void -FinishTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp) -{ - JS_ASSERT(tmp->tvr.u.marker == mark_temp_ns_array); - JS_POP_TEMP_ROOT(cx, &tmp->tvr); - XMLArrayFinish(cx, &tmp->array); -} - -/* - * Populate a new JS array with elements of JSTempRootedNSArray.array and - * place the result into rval. rval must point to a rooted location. - */ -static JSBool -TempNSArrayToJSArray(JSContext *cx, JSTempRootedNSArray *tmp, jsval *rval) -{ - JSObject *arrayobj; - uint32 i, n; - JSXMLNamespace *ns; - JSObject *nsobj; - - arrayobj = js_NewArrayObject(cx, 0, NULL); - if (!arrayobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(arrayobj); - for (i = 0, n = tmp->array.length; i < n; i++) { - ns = XMLARRAY_MEMBER(&tmp->array, i, JSXMLNamespace); - if (!ns) - continue; - nsobj = js_GetXMLNamespaceObject(cx, ns); - if (!nsobj) - return JS_FALSE; - tmp->value = OBJECT_TO_JSVAL(nsobj); - if (!OBJ_SET_PROPERTY(cx, arrayobj, INT_TO_JSID(i), &tmp->value)) - return JS_FALSE; - } - return JS_TRUE; -} - -static JSBool -FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray) -{ - uint32 length, i, j, n; - JSXMLNamespace *ns, *ns2; - - length = nsarray->length; - do { - if (xml->xml_class != JSXML_CLASS_ELEMENT) - continue; - for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { - ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); - if (!ns) - continue; - - for (j = 0; j < length; j++) { - ns2 = XMLARRAY_MEMBER(nsarray, j, JSXMLNamespace); - if (ns2 && - ((ns2->prefix && ns->prefix) - ? js_EqualStrings(ns2->prefix, ns->prefix) - : js_EqualStrings(ns2->uri, ns->uri))) { - break; - } - } - - if (j == length) { - if (!XMLARRAY_APPEND(cx, nsarray, ns)) - return JS_FALSE; - ++length; - } - } - } while ((xml = xml->parent) != NULL); - JS_ASSERT(length == nsarray->length); - - return JS_TRUE; -} - -static JSBool -xml_inScopeNamespaces(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - JSTempRootedNSArray namespaces; - JSBool ok; - - NON_LIST_XML_METHOD_PROLOG; - - InitTempNSArray(cx, &namespaces); - ok = FindInScopeNamespaces(cx, xml, &namespaces.array) && - TempNSArrayToJSArray(cx, &namespaces, rval); - FinishTempNSArray(cx, &namespaces); - return ok; -} - -static JSBool -xml_insertChildAfter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *kid; - jsval arg; - uint32 i; - - NON_LIST_XML_METHOD_PROLOG; - if (!JSXML_HAS_KIDS(xml)) - return JS_TRUE; - - arg = argv[0]; - if (JSVAL_IS_NULL(arg)) { - kid = NULL; - i = 0; - } else { - if (!VALUE_IS_XML(cx, arg)) - return JS_TRUE; - kid = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(arg)); - i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL); - if (i == XML_NOT_FOUND) - return JS_TRUE; - ++i; - } - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - if (!Insert(cx, xml, i, argv[1])) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -static JSBool -xml_insertChildBefore(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *kid; - jsval arg; - uint32 i; - - NON_LIST_XML_METHOD_PROLOG; - if (!JSXML_HAS_KIDS(xml)) - return JS_TRUE; - - arg = argv[0]; - if (JSVAL_IS_NULL(arg)) { - kid = NULL; - i = xml->xml_kids.length; - } else { - if (!VALUE_IS_XML(cx, arg)) - return JS_TRUE; - kid = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(arg)); - i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL); - if (i == XML_NOT_FOUND) - return JS_TRUE; - } - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - if (!Insert(cx, xml, i, argv[1])) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_length(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml; - - XML_METHOD_PROLOG; - if (xml->xml_class != JSXML_CLASS_LIST) { - *rval = JSVAL_ONE; - } else { - if (!js_NewNumberValue(cx, xml->xml_kids.length, rval)) - return JS_FALSE; - } - return JS_TRUE; -} - -static JSBool -xml_localName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - - NON_LIST_XML_METHOD_PROLOG; - *rval = xml->name ? STRING_TO_JSVAL(xml->name->localName) : JSVAL_NULL; - return JS_TRUE; -} - -static JSBool -xml_name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml; - JSObject *nameobj; - - NON_LIST_XML_METHOD_PROLOG; - if (!xml->name) { - *rval = JSVAL_NULL; - } else { - nameobj = js_GetXMLQNameObject(cx, xml->name); - if (!nameobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(nameobj); - } - return JS_TRUE; -} - -static JSBool -xml_namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - JSString *prefix; - JSTempRootedNSArray inScopeNSes; - JSBool ok; - jsuint i, length; - JSXMLNamespace *ns; - JSObject *nsobj; - - NON_LIST_XML_METHOD_PROLOG; - if (argc == 0 && !JSXML_HAS_NAME(xml)) { - *rval = JSVAL_NULL; - return JS_TRUE; - } - - if (argc == 0) { - prefix = NULL; - } else { - prefix = js_ValueToString(cx, argv[0]); - if (!prefix) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(prefix); /* local root */ - } - - /* After this point the control must flow through label out. */ - InitTempNSArray(cx, &inScopeNSes); - ok = FindInScopeNamespaces(cx, xml, &inScopeNSes.array); - if (!ok) - goto out; - - if (!prefix) { - ns = GetNamespace(cx, xml->name, &inScopeNSes.array); - if (!ns) { - ok = JS_FALSE; - goto out; - } - } else { - ns = NULL; - for (i = 0, length = inScopeNSes.array.length; i < length; i++) { - ns = XMLARRAY_MEMBER(&inScopeNSes.array, i, JSXMLNamespace); - if (ns && ns->prefix && js_EqualStrings(ns->prefix, prefix)) - break; - ns = NULL; - } - } - - if (!ns) { - *rval = JSVAL_VOID; - } else { - nsobj = js_GetXMLNamespaceObject(cx, ns); - if (!nsobj) { - ok = JS_FALSE; - goto out; - } - *rval = OBJECT_TO_JSVAL(nsobj); - } - - out: - FinishTempNSArray(cx, &inScopeNSes); - return JS_TRUE; -} - -static JSBool -xml_namespaceDeclarations(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *yml; - JSBool ok; - JSTempRootedNSArray ancestors, declared; - uint32 i, n; - JSXMLNamespace *ns; - - NON_LIST_XML_METHOD_PROLOG; - if (JSXML_HAS_VALUE(xml)) - return JS_TRUE; - - /* From here, control flow must goto out to finish these arrays. */ - ok = JS_TRUE; - InitTempNSArray(cx, &ancestors); - InitTempNSArray(cx, &declared); - yml = xml; - - while ((yml = yml->parent) != NULL) { - JS_ASSERT(yml->xml_class == JSXML_CLASS_ELEMENT); - for (i = 0, n = yml->xml_namespaces.length; i < n; i++) { - ns = XMLARRAY_MEMBER(&yml->xml_namespaces, i, JSXMLNamespace); - if (ns && - !XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) { - ok = XMLARRAY_APPEND(cx, &ancestors.array, ns); - if (!ok) - goto out; - } - } - } - - for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { - ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); - if (!ns) - continue; - if (!ns->declared) - continue; - if (!XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) { - ok = XMLARRAY_APPEND(cx, &declared.array, ns); - if (!ok) - goto out; - } - } - - ok = TempNSArrayToJSArray(cx, &declared, rval); - -out: - /* Finishing must be in reverse order of initialization to follow LIFO. */ - FinishTempNSArray(cx, &declared); - FinishTempNSArray(cx, &ancestors); - return ok; -} - -static const char js_attribute_str[] = "attribute"; -static const char js_text_str[] = "text"; - -/* Exported to jsgc.c #ifdef GC_MARK_DEBUG. */ -const char *js_xml_class_str[] = { - "list", - "element", - js_attribute_str, - "processing-instruction", - js_text_str, - "comment" -}; - -static JSBool -xml_nodeKind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - JSString *str; - - NON_LIST_XML_METHOD_PROLOG; - str = JS_InternString(cx, js_xml_class_str[xml->xml_class]); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -NormalizingDelete(JSContext *cx, JSObject *obj, JSXML *xml, jsval id) -{ - jsval junk; - - if (xml->xml_class == JSXML_CLASS_LIST) - return DeleteProperty(cx, obj, id, &junk); - return DeleteByIndex(cx, xml, id, &junk); -} - -/* - * Erratum? the testcase js/tests/e4x/XML/13.4.4.26.js wants all-whitespace - * text between tags to be removed by normalize. - */ -static JSBool -IsXMLSpace(JSString *str) -{ - const jschar *cp, *end; - - cp = JSSTRING_CHARS(str); - end = cp + JSSTRING_LENGTH(str); - while (cp < end) { - if (!JS_ISXMLSPACE(*cp)) - return JS_FALSE; - ++cp; - } - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_normalize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *kid, *kid2; - uint32 i, n; - JSObject *kidobj; - JSString *str; - jsval junk; - - XML_METHOD_PROLOG; - *rval = OBJECT_TO_JSVAL(obj); - if (!JSXML_HAS_KIDS(xml)) - return JS_TRUE; - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - - for (i = 0, n = xml->xml_kids.length; i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (!kid) - continue; - if (kid->xml_class == JSXML_CLASS_ELEMENT) { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj || !xml_normalize(cx, kidobj, argc, argv, &junk)) - return JS_FALSE; - } else if (kid->xml_class == JSXML_CLASS_TEXT) { - while (i + 1 < n && - (kid2 = XMLARRAY_MEMBER(&xml->xml_kids, i + 1, JSXML)) && - kid2->xml_class == JSXML_CLASS_TEXT) { - str = js_ConcatStrings(cx, kid->xml_value, kid2->xml_value); - if (!str) - return JS_FALSE; - if (!NormalizingDelete(cx, obj, xml, INT_TO_JSVAL(i + 1))) - return JS_FALSE; - n = xml->xml_kids.length; - kid->xml_value = str; - } - if (IS_EMPTY(kid->xml_value) || IsXMLSpace(kid->xml_value)) { - if (!NormalizingDelete(cx, obj, xml, INT_TO_JSVAL(i))) - return JS_FALSE; - n = xml->xml_kids.length; - --i; - } - } - } - - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_parent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml, *parent, *kid; - uint32 i, n; - JSObject *parentobj; - - XML_METHOD_PROLOG; - parent = xml->parent; - if (xml->xml_class == JSXML_CLASS_LIST) { - *rval = JSVAL_VOID; - n = xml->xml_kids.length; - if (n == 0) - return JS_TRUE; - - kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (!kid) - return JS_TRUE; - parent = kid->parent; - for (i = 1; i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->parent != parent) - return JS_TRUE; - } - } - - if (!parent) { - *rval = JSVAL_NULL; - return JS_TRUE; - } - - parentobj = js_GetXMLObject(cx, parent); - if (!parentobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(parentobj); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_processingInstructions(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - JSXML *xml, *list, *kid, *vxml; - jsval name, v; - JSXMLQName *nameqn; - jsid funid; - JSBool ok; - JSXMLArrayCursor cursor; - JSObject *kidobj; - uint32 i, n; - - XML_METHOD_PROLOG; - name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0]; - nameqn = ToXMLName(cx, name, &funid); - if (!nameqn) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(nameqn->object); - - list = xml_list_helper(cx, xml, rval); - if (!list) - return JS_FALSE; - if (funid) - return JS_TRUE; - - list->xml_targetprop = nameqn; - ok = JS_TRUE; - - if (xml->xml_class == JSXML_CLASS_LIST) { - /* 13.5.4.17 Step 4 (misnumbered 9 -- Erratum?). */ - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (kid->xml_class == JSXML_CLASS_ELEMENT) { - ok = js_EnterLocalRootScope(cx); - if (!ok) - break; - kidobj = js_GetXMLObject(cx, kid); - if (kidobj) { - ok = xml_processingInstructions(cx, kidobj, argc, argv, &v); - } else { - ok = JS_FALSE; - v = JSVAL_NULL; - } - js_LeaveLocalRootScopeWithResult(cx, v); - if (!ok) - break; - vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); - if (JSXML_LENGTH(vxml) != 0) { - ok = Append(cx, list, vxml); - if (!ok) - break; - } - } - } - XMLArrayCursorFinish(&cursor); - } else { - /* 13.4.4.28 Step 4. */ - for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION && - (IS_STAR(nameqn->localName) || - js_EqualStrings(nameqn->localName, kid->name->localName))) { - ok = Append(cx, list, kid); - if (!ok) - break; - } - } - } - - return ok; -} - -static JSBool -xml_prependChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - - NON_LIST_XML_METHOD_PROLOG; - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - return Insert(cx, xml, 0, argv[0]); -} - -/* XML and XMLList */ -static JSBool -xml_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - jsval name; - uint32 index; - - XML_METHOD_PROLOG; - name = argv[0]; - *rval = JSVAL_FALSE; - if (js_IdIsIndex(name, &index)) { - if (xml->xml_class == JSXML_CLASS_LIST) { - /* 13.5.4.18. */ - *rval = BOOLEAN_TO_JSVAL(index < xml->xml_kids.length); - } else { - /* 13.4.4.30. */ - *rval = BOOLEAN_TO_JSVAL(index == 0); - } - } - return JS_TRUE; -} - -static JSBool -namespace_full_match(const void *a, const void *b) -{ - const JSXMLNamespace *nsa = (const JSXMLNamespace *) a; - const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; - - if (nsa->prefix && nsb->prefix && - !js_EqualStrings(nsa->prefix, nsb->prefix)) { - return JS_FALSE; - } - return js_EqualStrings(nsa->uri, nsb->uri); -} - -static JSBool -xml_removeNamespace_helper(JSContext *cx, JSXML *xml, JSXMLNamespace *ns) -{ - JSXMLNamespace *thisns, *attrns; - uint32 i, n; - JSXML *attr, *kid; - - thisns = GetNamespace(cx, xml->name, &xml->xml_namespaces); - JS_ASSERT(thisns); - if (thisns == ns) - return JS_TRUE; - - for (i = 0, n = xml->xml_attrs.length; i < n; i++) { - attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); - if (!attr) - continue; - attrns = GetNamespace(cx, attr->name, &xml->xml_namespaces); - JS_ASSERT(attrns); - if (attrns == ns) - return JS_TRUE; - } - - i = XMLARRAY_FIND_MEMBER(&xml->xml_namespaces, ns, namespace_full_match); - if (i != XML_NOT_FOUND) - XMLArrayDelete(cx, &xml->xml_namespaces, i, JS_TRUE); - - for (i = 0, n = xml->xml_kids.length; i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { - if (!xml_removeNamespace_helper(cx, kid, ns)) - return JS_FALSE; - } - } - return JS_TRUE; -} - -static JSBool -xml_removeNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - JSObject *nsobj; - JSXMLNamespace *ns; - - NON_LIST_XML_METHOD_PROLOG; - *rval = OBJECT_TO_JSVAL(obj); - if (xml->xml_class != JSXML_CLASS_ELEMENT) - return JS_TRUE; - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - - nsobj = CallConstructorFunction(cx, obj, &js_NamespaceClass.base, 1, argv); - if (!nsobj) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(nsobj); - ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); - - /* NOTE: remove ns from each ancestor if not used by that ancestor. */ - return xml_removeNamespace_helper(cx, xml, ns); -} - -static JSBool -xml_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml, *vxml, *kid; - jsval name, value, id, junk; - uint32 index; - JSObject *nameobj; - JSXMLQName *nameqn; - - NON_LIST_XML_METHOD_PROLOG; - *rval = OBJECT_TO_JSVAL(obj); - if (xml->xml_class != JSXML_CLASS_ELEMENT) - return JS_TRUE; - - value = argv[1]; - vxml = VALUE_IS_XML(cx, value) - ? (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(value)) - : NULL; - if (!vxml) { - if (!JS_ConvertValue(cx, value, JSTYPE_STRING, &argv[1])) - return JS_FALSE; - value = argv[1]; - } else { - vxml = DeepCopy(cx, vxml, NULL, 0); - if (!vxml) - return JS_FALSE; - value = argv[1] = OBJECT_TO_JSVAL(vxml->object); - } - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - - name = argv[0]; - if (js_IdIsIndex(name, &index)) - return Replace(cx, xml, name, value); - - /* Call function QName per spec, not ToXMLName, to avoid attribute names. */ - nameobj = CallConstructorFunction(cx, obj, &js_QNameClass.base, 1, &name); - if (!nameobj) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(nameobj); - nameqn = (JSXMLQName *) JS_GetPrivate(cx, nameobj); - - id = JSVAL_VOID; - index = xml->xml_kids.length; - while (index != 0) { - --index; - kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); - if (kid && MatchElemName(nameqn, kid)) { - if (!JSVAL_IS_VOID(id) && !DeleteByIndex(cx, xml, id, &junk)) - return JS_FALSE; - if (!IndexToIdVal(cx, index, &id)) - return JS_FALSE; - } - } - if (JSVAL_IS_VOID(id)) - return JS_TRUE; - return Replace(cx, xml, id, value); -} - -static JSBool -xml_setChildren(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - if (!StartNonListXMLMethod(cx, &obj, argv)) - return JS_FALSE; - - if (!PutProperty(cx, obj, ATOM_KEY(cx->runtime->atomState.starAtom), - &argv[0])) { - return JS_FALSE; - } - - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -static JSBool -xml_setLocalName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - jsval name; - JSXMLQName *nameqn; - JSString *namestr; - - NON_LIST_XML_METHOD_PROLOG; - if (!JSXML_HAS_NAME(xml)) - return JS_TRUE; - - name = argv[0]; - if (!JSVAL_IS_PRIMITIVE(name) && - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base) { - nameqn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(name)); - namestr = nameqn->localName; - } else { - if (!JS_ConvertValue(cx, name, JSTYPE_STRING, &argv[0])) - return JS_FALSE; - name = argv[0]; - namestr = JSVAL_TO_STRING(name); - } - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - xml->name->localName = namestr; - return JS_TRUE; -} - -static JSBool -xml_setName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml, *nsowner; - jsval name; - JSXMLQName *nameqn; - JSObject *nameobj; - JSXMLArray *nsarray; - uint32 i, n; - JSXMLNamespace *ns; - - NON_LIST_XML_METHOD_PROLOG; - if (!JSXML_HAS_NAME(xml)) - return JS_TRUE; - - name = argv[0]; - if (!JSVAL_IS_PRIMITIVE(name) && - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base && - !(nameqn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(name))) - ->uri) { - name = argv[0] = STRING_TO_JSVAL(nameqn->localName); - } - - nameobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &name); - if (!nameobj) - return JS_FALSE; - nameqn = (JSXMLQName *) JS_GetPrivate(cx, nameobj); - - /* ECMA-357 13.4.4.35 Step 4. */ - if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) - nameqn->uri = cx->runtime->emptyString; - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - xml->name = nameqn; - - /* - * Erratum: nothing in 13.4.4.35 talks about making the name match the - * in-scope namespaces, either by finding an in-scope namespace with a - * matching uri and setting the new name's prefix to that namespace's - * prefix, or by extending the in-scope namespaces for xml (which are in - * xml->parent if xml is an attribute or a PI). - */ - if (xml->xml_class == JSXML_CLASS_ELEMENT) { - nsowner = xml; - } else { - if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT) - return JS_TRUE; - nsowner = xml->parent; - } - - if (nameqn->prefix) { - /* - * The name being set has a prefix, which originally came from some - * namespace object (which may be the null namespace, where both the - * prefix and uri are the empty string). We must go through a full - * GetNamespace in case that namespace is in-scope in nsowner. - * - * If we find such an in-scope namespace, we return true right away, - * in this block. Otherwise, we fall through to the final return of - * AddInScopeNamespace(cx, nsowner, ns). - */ - ns = GetNamespace(cx, nameqn, &nsowner->xml_namespaces); - if (!ns) - return JS_FALSE; - - /* XXXbe have to test membership to see whether GetNamespace added */ - if (XMLARRAY_HAS_MEMBER(&nsowner->xml_namespaces, ns, NULL)) - return JS_TRUE; - } else { - /* - * At this point, we know nameqn->prefix is null, so nameqn->uri can't - * be the empty string (the null namespace always uses the empty string - * for both prefix and uri). - * - * This means we must inline GetNamespace and specialize it to match - * uri only, never prefix. If we find a namespace with nameqn's uri - * already in nsowner->xml_namespaces, then all that we need do is set - * nameqn->prefix to that namespace's prefix. - * - * If no such namespace exists, we can create one without going through - * the constructor, because we know nameqn->uri is non-empty (so prefix - * does not need to be converted from null to empty by QName). - */ - JS_ASSERT(!IS_EMPTY(nameqn->uri)); - - nsarray = &nsowner->xml_namespaces; - for (i = 0, n = nsarray->length; i < n; i++) { - ns = XMLARRAY_MEMBER(nsarray, i, JSXMLNamespace); - if (ns && js_EqualStrings(ns->uri, nameqn->uri)) { - nameqn->prefix = ns->prefix; - return JS_TRUE; - } - } - - ns = js_NewXMLNamespace(cx, NULL, nameqn->uri, JS_TRUE); - if (!ns) - return JS_FALSE; - } - - return AddInScopeNamespace(cx, nsowner, ns); -} - -static JSBool -xml_setNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *nsowner; - JSObject *nsobj, *qnobj; - JSXMLNamespace *ns; - jsval qnargv[2]; - - NON_LIST_XML_METHOD_PROLOG; - if (!JSXML_HAS_NAME(xml)) - return JS_TRUE; - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml || !js_GetXMLQNameObject(cx, xml->name)) - return JS_FALSE; - - nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, 1, argv); - if (!nsobj) - return JS_FALSE; - ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); - ns->declared = JS_TRUE; - - qnargv[0] = argv[0] = OBJECT_TO_JSVAL(nsobj); - qnargv[1] = OBJECT_TO_JSVAL(xml->name->object); - qnobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, qnargv); - if (!qnobj) - return JS_FALSE; - - xml->name = (JSXMLQName *) JS_GetPrivate(cx, qnobj); - - /* - * Erratum: the spec fails to update the governing in-scope namespaces. - * See the erratum noted in xml_setName, above. - */ - if (xml->xml_class == JSXML_CLASS_ELEMENT) { - nsowner = xml; - } else { - if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT) - return JS_TRUE; - nsowner = xml->parent; - } - return AddInScopeNamespace(cx, nsowner, ns); -} - -/* XML and XMLList */ -static JSBool -xml_text(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml, *list, *kid, *vxml; - uint32 i, n; - JSBool ok; - JSObject *kidobj; - jsval v; - - XML_METHOD_PROLOG; - list = xml_list_helper(cx, xml, rval); - if (!list) - return JS_FALSE; - - if (xml->xml_class == JSXML_CLASS_LIST) { - ok = JS_TRUE; - for (i = 0, n = xml->xml_kids.length; i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { - ok = js_EnterLocalRootScope(cx); - if (!ok) - break; - kidobj = js_GetXMLObject(cx, kid); - if (kidobj) { - ok = xml_text(cx, kidobj, argc, argv, &v); - } else { - ok = JS_FALSE; - v = JSVAL_NULL; - } - js_LeaveLocalRootScopeWithResult(cx, v); - if (!ok) - return JS_FALSE; - vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); - if (JSXML_LENGTH(vxml) != 0 && !Append(cx, list, vxml)) - return JS_FALSE; - } - } - } else { - for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_TEXT) { - if (!Append(cx, list, kid)) - return JS_FALSE; - } - } - } - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_toXMLString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - - str = ToXMLString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSString * -xml_toString_helper(JSContext *cx, JSXML *xml) -{ - JSString *str, *kidstr; - JSXML *kid; - JSXMLArrayCursor cursor; - - if (xml->xml_class == JSXML_CLASS_ATTRIBUTE || - xml->xml_class == JSXML_CLASS_TEXT) { - return xml->xml_value; - } - - if (!HasSimpleContent(xml)) - return ToXMLString(cx, OBJECT_TO_JSVAL(xml->object)); - - str = cx->runtime->emptyString; - js_EnterLocalRootScope(cx); - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (kid->xml_class != JSXML_CLASS_COMMENT && - kid->xml_class != JSXML_CLASS_PROCESSING_INSTRUCTION) { - kidstr = xml_toString_helper(cx, kid); - if (!kidstr) { - str = NULL; - break; - } - str = js_ConcatStrings(cx, str, kidstr); - if (!str) - break; - } - } - XMLArrayCursorFinish(&cursor); - js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str)); - return str; -} - -static JSBool -xml_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - JSString *str; - - XML_METHOD_PROLOG; - str = xml_toString_helper(cx, xml); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -static JSFunctionSpec xml_methods[] = { - {"addNamespace", xml_addNamespace, 1,0,0}, - {"appendChild", xml_appendChild, 1,0,0}, - {js_attribute_str, xml_attribute, 1,0,0}, - {"attributes", xml_attributes, 0,0,0}, - {"child", xml_child, 1,0,0}, - {"childIndex", xml_childIndex, 0,0,0}, - {"children", xml_children, 0,0,0}, - {"comments", xml_comments, 0,0,0}, - {"contains", xml_contains, 1,0,0}, - {"copy", xml_copy, 0,0,0}, - {"descendants", xml_descendants, 1,0,0}, - {"elements", xml_elements, 1,0,0}, - {"hasOwnProperty", xml_hasOwnProperty, 1,0,0}, - {"hasComplexContent", xml_hasComplexContent, 1,0,0}, - {"hasSimpleContent", xml_hasSimpleContent, 1,0,0}, - {"inScopeNamespaces", xml_inScopeNamespaces, 0,0,0}, - {"insertChildAfter", xml_insertChildAfter, 2,0,0}, - {"insertChildBefore", xml_insertChildBefore, 2,0,0}, - {js_length_str, xml_length, 0,0,0}, - {js_localName_str, xml_localName, 0,0,0}, - {js_name_str, xml_name, 0,0,0}, - {js_namespace_str, xml_namespace, 1,0,0}, - {"namespaceDeclarations", xml_namespaceDeclarations, 0,0,0}, - {"nodeKind", xml_nodeKind, 0,0,0}, - {"normalize", xml_normalize, 0,0,0}, - {js_xml_parent_str, xml_parent, 0,0,0}, - {"processingInstructions",xml_processingInstructions,1,0,0}, - {"prependChild", xml_prependChild, 1,0,0}, - {"propertyIsEnumerable", xml_propertyIsEnumerable, 1,0,0}, - {"removeNamespace", xml_removeNamespace, 1,0,0}, - {"replace", xml_replace, 2,0,0}, - {"setChildren", xml_setChildren, 1,0,0}, - {"setLocalName", xml_setLocalName, 1,0,0}, - {"setName", xml_setName, 1,0,0}, - {"setNamespace", xml_setNamespace, 1,0,0}, - {js_text_str, xml_text, 0,0,0}, - {js_toString_str, xml_toString, 0,0,0}, - {js_toXMLString_str, xml_toXMLString, 0,0,0}, - {js_toSource_str, xml_toXMLString, 0,0,0}, - {js_valueOf_str, xml_valueOf, 0,0,0}, - {0,0,0,0,0} -}; - -static JSBool -CopyXMLSettings(JSContext *cx, JSObject *from, JSObject *to) -{ - int i; - const char *name; - jsval v; - - for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { - name = xml_static_props[i].name; - if (!JS_GetProperty(cx, from, name, &v)) - return JS_FALSE; - if (JSVAL_IS_BOOLEAN(v) && !JS_SetProperty(cx, to, name, &v)) - return JS_FALSE; - } - - name = xml_static_props[i].name; - if (!JS_GetProperty(cx, from, name, &v)) - return JS_FALSE; - if (JSVAL_IS_NUMBER(v) && !JS_SetProperty(cx, to, name, &v)) - return JS_FALSE; - return JS_TRUE; -} - -static JSBool -SetDefaultXMLSettings(JSContext *cx, JSObject *obj) -{ - int i; - jsval v; - - for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { - v = JSVAL_TRUE; - if (!JS_SetProperty(cx, obj, xml_static_props[i].name, &v)) - return JS_FALSE; - } - v = INT_TO_JSVAL(2); - return JS_SetProperty(cx, obj, xml_static_props[i].name, &v); -} - -static JSBool -xml_settings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSObject *settings; - - settings = JS_NewObject(cx, NULL, NULL, NULL); - if (!settings) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(settings); - return CopyXMLSettings(cx, obj, settings); -} - -static JSBool -xml_setSettings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsval v; - JSBool ok; - JSObject *settings; - - v = argv[0]; - if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { - cx->xmlSettingFlags = 0; - ok = SetDefaultXMLSettings(cx, obj); - } else { - if (JSVAL_IS_PRIMITIVE(v)) - return JS_TRUE; - settings = JSVAL_TO_OBJECT(v); - cx->xmlSettingFlags = 0; - ok = CopyXMLSettings(cx, settings, obj); - } - if (ok) - cx->xmlSettingFlags |= XSF_CACHE_VALID; - return ok; -} - -static JSBool -xml_defaultSettings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSObject *settings; - - settings = JS_NewObject(cx, NULL, NULL, NULL); - if (!settings) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(settings); - return SetDefaultXMLSettings(cx, settings); -} - -static JSFunctionSpec xml_static_methods[] = { - {"settings", xml_settings, 0,0,0}, - {"setSettings", xml_setSettings, 1,0,0}, - {"defaultSettings", xml_defaultSettings, 0,0,0}, - {0,0,0,0,0} -}; - -static JSBool -XML(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval v; - JSXML *xml, *copy; - JSObject *xobj, *vobj; - JSClass *clasp; - - v = argv[0]; - if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) - v = STRING_TO_JSVAL(cx->runtime->emptyString); - - xobj = ToXML(cx, v); - if (!xobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(xobj); - xml = (JSXML *) JS_GetPrivate(cx, xobj); - - if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && !JSVAL_IS_PRIMITIVE(v)) { - vobj = JSVAL_TO_OBJECT(v); - clasp = OBJ_GET_CLASS(cx, vobj); - if (clasp == &js_XMLClass || - (clasp->flags & JSCLASS_DOCUMENT_OBSERVER)) { - /* No need to lock obj, it's newly constructed and thread local. */ - copy = DeepCopy(cx, xml, obj, 0); - if (!copy) - return JS_FALSE; - JS_ASSERT(copy->object == obj); - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; - } - } - return JS_TRUE; -} - -static JSBool -XMLList(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval v; - JSObject *vobj, *listobj; - JSXML *xml, *list; - - v = argv[0]; - if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) - v = STRING_TO_JSVAL(cx->runtime->emptyString); - - if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && !JSVAL_IS_PRIMITIVE(v)) { - vobj = JSVAL_TO_OBJECT(v); - if (OBJECT_IS_XML(cx, vobj)) { - xml = (JSXML *) JS_GetPrivate(cx, vobj); - if (xml->xml_class == JSXML_CLASS_LIST) { - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(listobj); - - list = (JSXML *) JS_GetPrivate(cx, listobj); - if (!Append(cx, list, xml)) - return JS_FALSE; - return JS_TRUE; - } - } - } - - /* Toggle on XML support since the script has explicitly requested it. */ - listobj = ToXMLList(cx, v); - if (!listobj) - return JS_FALSE; - - *rval = OBJECT_TO_JSVAL(listobj); - return JS_TRUE; -} - -#define JSXML_LIST_SIZE (offsetof(JSXML, u) + sizeof(struct JSXMLListVar)) -#define JSXML_ELEMENT_SIZE (offsetof(JSXML, u) + sizeof(struct JSXMLVar)) -#define JSXML_LEAF_SIZE (offsetof(JSXML, u) + sizeof(JSString *)) - -static size_t sizeof_JSXML[JSXML_CLASS_LIMIT] = { - JSXML_LIST_SIZE, /* JSXML_CLASS_LIST */ - JSXML_ELEMENT_SIZE, /* JSXML_CLASS_ELEMENT */ - JSXML_LEAF_SIZE, /* JSXML_CLASS_ATTRIBUTE */ - JSXML_LEAF_SIZE, /* JSXML_CLASS_PROCESSING_INSTRUCTION */ - JSXML_LEAF_SIZE, /* JSXML_CLASS_TEXT */ - JSXML_LEAF_SIZE /* JSXML_CLASS_COMMENT */ -}; - -#ifdef DEBUG_notme -JSCList xml_leaks = JS_INIT_STATIC_CLIST(&xml_leaks); -uint32 xml_serial; -#endif - -JSXML * -js_NewXML(JSContext *cx, JSXMLClass xml_class) -{ - JSXML *xml; - - xml = (JSXML *) js_NewGCThing(cx, GCX_XML, sizeof_JSXML[xml_class]); - if (!xml) - return NULL; - - xml->object = NULL; - xml->domnode = NULL; - xml->parent = NULL; - xml->name = NULL; - xml->xml_class = xml_class; - xml->xml_flags = 0; - if (JSXML_CLASS_HAS_VALUE(xml_class)) { - xml->xml_value = cx->runtime->emptyString; - } else { - XMLArrayInit(cx, &xml->xml_kids, 0); - if (xml_class == JSXML_CLASS_LIST) { - xml->xml_target = NULL; - xml->xml_targetprop = NULL; - } else { - XMLArrayInit(cx, &xml->xml_namespaces, 0); - XMLArrayInit(cx, &xml->xml_attrs, 0); - } - } - -#ifdef DEBUG_notme - JS_APPEND_LINK(&xml->links, &xml_leaks); - xml->serial = xml_serial++; -#endif - METER(xml_stats.xml); - METER(xml_stats.livexml); - return xml; -} - -void -js_MarkXML(JSContext *cx, JSXML *xml) -{ - GC_MARK(cx, xml->object, "object"); - GC_MARK(cx, xml->name, "name"); - GC_MARK(cx, xml->parent, "xml_parent"); - - if (JSXML_HAS_VALUE(xml)) { - GC_MARK(cx, xml->xml_value, "value"); - return; - } - - xml_mark_vector(cx, - (JSXML **) xml->xml_kids.vector, - xml->xml_kids.length); - XMLArrayCursorMark(cx, xml->xml_kids.cursors); - XMLArrayTrim(&xml->xml_kids); - - if (xml->xml_class == JSXML_CLASS_LIST) { - if (xml->xml_target) - GC_MARK(cx, xml->xml_target, "target"); - if (xml->xml_targetprop) - GC_MARK(cx, xml->xml_targetprop, "targetprop"); - } else { - namespace_mark_vector(cx, - (JSXMLNamespace **) xml->xml_namespaces.vector, - xml->xml_namespaces.length); - XMLArrayCursorMark(cx, xml->xml_namespaces.cursors); - XMLArrayTrim(&xml->xml_namespaces); - - xml_mark_vector(cx, - (JSXML **) xml->xml_attrs.vector, - xml->xml_attrs.length); - XMLArrayCursorMark(cx, xml->xml_attrs.cursors); - XMLArrayTrim(&xml->xml_attrs); - } -} - -void -js_FinalizeXML(JSContext *cx, JSXML *xml) -{ - if (JSXML_HAS_KIDS(xml)) { - XMLArrayFinish(cx, &xml->xml_kids); - if (xml->xml_class == JSXML_CLASS_ELEMENT) { - XMLArrayFinish(cx, &xml->xml_namespaces); - XMLArrayFinish(cx, &xml->xml_attrs); - } - } - -#ifdef DEBUG_notme - JS_REMOVE_LINK(&xml->links); -#endif - - UNMETER(xml_stats.livexml); -} - -JSObject * -js_ParseNodeToXMLObject(JSContext *cx, JSParseNode *pn) -{ - jsval nsval; - JSXMLNamespace *ns; - JSXMLArray nsarray; - JSXML *xml; - - if (!js_GetDefaultXMLNamespace(cx, &nsval)) - return NULL; - JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval)); - ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); - - if (!XMLArrayInit(cx, &nsarray, 1)) - return NULL; - - XMLARRAY_APPEND(cx, &nsarray, ns); - xml = ParseNodeToXML(cx, pn, &nsarray, XSF_PRECOMPILED_ROOT); - XMLArrayFinish(cx, &nsarray); - if (!xml) - return NULL; - - return xml->object; -} - -JSObject * -js_NewXMLObject(JSContext *cx, JSXMLClass xml_class) -{ - JSXML *xml; - JSObject *obj; - JSTempValueRooter tvr; - - xml = js_NewXML(cx, xml_class); - if (!xml) - return NULL; - JS_PUSH_TEMP_ROOT_GCTHING(cx, xml, &tvr); - obj = js_GetXMLObject(cx, xml); - JS_POP_TEMP_ROOT(cx, &tvr); - return obj; -} - -static JSObject * -NewXMLObject(JSContext *cx, JSXML *xml) -{ - JSObject *obj; - - obj = js_NewObject(cx, &js_XMLClass, NULL, NULL); - if (!obj || !JS_SetPrivate(cx, obj, xml)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; - } - METER(xml_stats.xmlobj); - METER(xml_stats.livexmlobj); - return obj; -} - -JSObject * -js_GetXMLObject(JSContext *cx, JSXML *xml) -{ - JSObject *obj; - - obj = xml->object; - if (obj) { - JS_ASSERT(JS_GetPrivate(cx, obj) == xml); - return obj; - } - - /* - * A JSXML cannot be shared among threads unless it has an object. - * A JSXML cannot be given an object unless: - * (a) it has no parent; or - * (b) its parent has no object (therefore is thread-private); or - * (c) its parent's object is locked. - * - * Once given an object, a JSXML is immutable. - */ - JS_ASSERT(!xml->parent || - !xml->parent->object || - JS_IS_OBJ_LOCKED(cx, xml->parent->object)); - - obj = NewXMLObject(cx, xml); - if (!obj) - return NULL; - xml->object = obj; - return obj; -} - -JSObject * -js_InitNamespaceClass(JSContext *cx, JSObject *obj) -{ - return JS_InitClass(cx, obj, NULL, &js_NamespaceClass.base, Namespace, 2, - namespace_props, namespace_methods, NULL, NULL); -} - -JSObject * -js_InitQNameClass(JSContext *cx, JSObject *obj) -{ - return JS_InitClass(cx, obj, NULL, &js_QNameClass.base, QName, 2, - qname_props, qname_methods, NULL, NULL); -} - -JSObject * -js_InitAttributeNameClass(JSContext *cx, JSObject *obj) -{ - return JS_InitClass(cx, obj, NULL, &js_AttributeNameClass, AttributeName, 2, - qname_props, qname_methods, NULL, NULL); -} - -JSObject * -js_InitAnyNameClass(JSContext *cx, JSObject *obj) -{ - jsval v; - - if (!js_GetAnyName(cx, &v)) - return NULL; - return JSVAL_TO_OBJECT(v); -} - -JSObject * -js_InitXMLClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *pobj, *ctor; - JSFunction *fun; - JSXML *xml; - JSProperty *prop; - JSScopeProperty *sprop; - jsval cval, argv[1], junk; - - /* Define the isXMLName function. */ - if (!JS_DefineFunction(cx, obj, js_isXMLName_str, xml_isXMLName, 1, 0)) - return NULL; - - /* Define the XML class constructor and prototype. */ - proto = JS_InitClass(cx, obj, NULL, &js_XMLClass, XML, 1, - NULL, xml_methods, - xml_static_props, xml_static_methods); - if (!proto) - return NULL; - - xml = js_NewXML(cx, JSXML_CLASS_TEXT); - if (!xml || !JS_SetPrivate(cx, proto, xml)) - return NULL; - xml->object = proto; - METER(xml_stats.xmlobj); - METER(xml_stats.livexmlobj); - - /* - * Prepare to set default settings on the XML constructor we just made. - * NB: We can't use JS_GetConstructor, because it calls OBJ_GET_PROPERTY, - * which is xml_getProperty, which creates a new XMLList every time! We - * must instead call js_LookupProperty directly. - */ - if (!js_LookupProperty(cx, proto, - ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), - &pobj, &prop)) { - return NULL; - } - JS_ASSERT(prop); - sprop = (JSScopeProperty *) prop; - JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))); - cval = OBJ_GET_SLOT(cx, pobj, sprop->slot); - OBJ_DROP_PROPERTY(cx, pobj, prop); - JS_ASSERT(VALUE_IS_FUNCTION(cx, cval)); - - /* Set default settings. */ - ctor = JSVAL_TO_OBJECT(cval); - argv[0] = JSVAL_VOID; - if (!xml_setSettings(cx, ctor, 1, argv, &junk)) - return NULL; - - /* Define the XMLList function and give it the same prototype as XML. */ - fun = JS_DefineFunction(cx, obj, js_XMLList_str, XMLList, 1, 0); - if (!fun) - return NULL; - if (!js_SetClassPrototype(cx, fun->object, proto, - JSPROP_READONLY | JSPROP_PERMANENT)) { - return NULL; - } - return proto; -} - -JSObject * -js_InitXMLClasses(JSContext *cx, JSObject *obj) -{ - if (!js_InitNamespaceClass(cx, obj)) - return NULL; - if (!js_InitQNameClass(cx, obj)) - return NULL; - if (!js_InitAttributeNameClass(cx, obj)) - return NULL; - if (!js_InitAnyNameClass(cx, obj)) - return NULL; - return js_InitXMLClass(cx, obj); -} - -JSBool -js_GetFunctionNamespace(JSContext *cx, jsval *vp) -{ - JSRuntime *rt; - JSObject *obj; - JSAtom *atom; - JSString *prefix, *uri; - - /* An invalid URI, for internal use only, guaranteed not to collide. */ - static const char anti_uri[] = "@mozilla.org/js/function"; - - /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */ - rt = cx->runtime; - obj = rt->functionNamespaceObject; - if (!obj) { - JS_LOCK_GC(rt); - obj = rt->functionNamespaceObject; - if (!obj) { - JS_UNLOCK_GC(rt); - atom = js_Atomize(cx, js_function_str, 8, 0); - JS_ASSERT(atom); - prefix = ATOM_TO_STRING(atom); - - /* - * Note that any race to atomize anti_uri here is resolved by - * the atom table code, such that at most one atom for anti_uri - * is created. We store in rt->atomState.lazy unconditionally, - * since we are guaranteed to overwrite either null or the same - * atom pointer. - */ - atom = js_Atomize(cx, anti_uri, sizeof anti_uri - 1, ATOM_PINNED); - if (!atom) - return JS_FALSE; - rt->atomState.lazy.functionNamespaceURIAtom = atom; - - uri = ATOM_TO_STRING(atom); - obj = js_NewXMLNamespaceObject(cx, prefix, uri, JS_FALSE); - if (!obj) - return JS_FALSE; - - /* - * Avoid entraining any in-scope Object.prototype. The loss of - * Namespace.prototype is not detectable, as there is no way to - * refer to this instance in scripts. When used to qualify method - * names, its prefix and uri references are copied to the QName. - */ - OBJ_SET_PROTO(cx, obj, NULL); - OBJ_SET_PARENT(cx, obj, NULL); - - JS_LOCK_GC(rt); - if (!rt->functionNamespaceObject) - rt->functionNamespaceObject = obj; - else - obj = rt->functionNamespaceObject; - } - JS_UNLOCK_GC(rt); - } - *vp = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -/* - * Note the asymmetry between js_GetDefaultXMLNamespace and js_SetDefaultXML- - * Namespace. Get searches fp->scopeChain for JS_DEFAULT_XML_NAMESPACE_ID, - * while Set sets JS_DEFAULT_XML_NAMESPACE_ID in fp->varobj (unless fp is a - * lightweight function activation). There's no requirement that fp->varobj - * lie directly on fp->scopeChain, although it should be reachable using the - * prototype chain from a scope object (cf. JSOPTION_VAROBJFIX in jsapi.h). - * - * If Get can't find JS_DEFAULT_XML_NAMESPACE_ID along the scope chain, it - * creates a default namespace via 'new Namespace()'. In contrast, Set uses - * its v argument as the uri of a new Namespace, with "" as the prefix. See - * ECMA-357 12.1 and 12.1.1. Note that if Set is called with a Namespace n, - * the default XML namespace will be set to ("", n.uri). So the uri string - * is really the only usefully stored value of the default namespace. - */ -JSBool -js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp) -{ - JSStackFrame *fp; - JSObject *nsobj, *obj, *tmp; - jsval v; - - fp = cx->fp; - nsobj = fp->xmlNamespace; - if (nsobj) { - *vp = OBJECT_TO_JSVAL(nsobj); - return JS_TRUE; - } - - obj = NULL; - for (tmp = fp->scopeChain; tmp; tmp = OBJ_GET_PARENT(cx, obj)) { - obj = tmp; - if (!OBJ_GET_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, &v)) - return JS_FALSE; - if (!JSVAL_IS_PRIMITIVE(v)) { - fp->xmlNamespace = JSVAL_TO_OBJECT(v); - *vp = v; - return JS_TRUE; - } - } - - nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, 0, NULL); - if (!nsobj) - return JS_FALSE; - v = OBJECT_TO_JSVAL(nsobj); - if (obj && - !OBJ_DEFINE_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, v, - JS_PropertyStub, JS_PropertyStub, - JSPROP_PERMANENT, NULL)) { - return JS_FALSE; - } - fp->xmlNamespace = nsobj; - *vp = v; - return JS_TRUE; -} - -JSBool -js_SetDefaultXMLNamespace(JSContext *cx, jsval v) -{ - jsval argv[2]; - JSObject *nsobj, *varobj; - JSStackFrame *fp; - - argv[0] = STRING_TO_JSVAL(cx->runtime->emptyString); - argv[1] = v; - nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL, - 2, argv); - if (!nsobj) - return JS_FALSE; - v = OBJECT_TO_JSVAL(nsobj); - - fp = cx->fp; - varobj = fp->varobj; - if (varobj) { - if (!OBJ_DEFINE_PROPERTY(cx, varobj, JS_DEFAULT_XML_NAMESPACE_ID, v, - JS_PropertyStub, JS_PropertyStub, - JSPROP_PERMANENT, NULL)) { - return JS_FALSE; - } - } else { - JS_ASSERT(fp->fun && !JSFUN_HEAVYWEIGHT_TEST(fp->fun->flags)); - } - fp->xmlNamespace = JSVAL_TO_OBJECT(v); - return JS_TRUE; -} - -JSBool -js_ToAttributeName(JSContext *cx, jsval *vp) -{ - JSXMLQName *qn; - - qn = ToAttributeName(cx, *vp); - if (!qn) - return JS_FALSE; - *vp = OBJECT_TO_JSVAL(qn->object); - return JS_TRUE; -} - -JSString * -js_EscapeAttributeValue(JSContext *cx, JSString *str) -{ - return EscapeAttributeValue(cx, NULL, str); -} - -JSString * -js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2) -{ - size_t len, len2, newlen; - jschar *chars; - - if (JSSTRING_IS_DEPENDENT(str) || - !(*js_GetGCThingFlags(str) & GCF_MUTABLE)) { - str = js_NewStringCopyN(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str), - 0); - if (!str) - return NULL; - } - - len = str->length; - len2 = JSSTRING_LENGTH(str2); - newlen = (isName) ? len + 1 + len2 : len + 2 + len2 + 1; - chars = (jschar *) JS_realloc(cx, str->chars, (newlen+1) * sizeof(jschar)); - if (!chars) - return NULL; - - /* - * Reallocating str (because we know it has no other references) requires - * purging any deflated string cached for it. - */ - js_PurgeDeflatedStringCache(cx->runtime, str); - - str->chars = chars; - str->length = newlen; - chars += len; - if (isName) { - *chars++ = ' '; - js_strncpy(chars, JSSTRING_CHARS(str2), len2); - chars += len2; - } else { - *chars++ = '='; - *chars++ = '"'; - js_strncpy(chars, JSSTRING_CHARS(str2), len2); - chars += len2; - *chars++ = '"'; - } - *chars = 0; - return str; -} - -JSString * -js_EscapeElementValue(JSContext *cx, JSString *str) -{ - return EscapeElementValue(cx, NULL, str); -} - -JSString * -js_ValueToXMLString(JSContext *cx, jsval v) -{ - return ToXMLString(cx, v); -} - -static JSBool -anyname_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - *rval = ATOM_KEY(cx->runtime->atomState.starAtom); - return JS_TRUE; -} - -JSBool -js_GetAnyName(JSContext *cx, jsval *vp) -{ - JSRuntime *rt; - JSObject *obj; - JSXMLQName *qn; - JSBool ok; - - /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */ - rt = cx->runtime; - obj = rt->anynameObject; - if (!obj) { - JS_LOCK_GC(rt); - obj = rt->anynameObject; - if (!obj) { - JS_UNLOCK_GC(rt); - - /* - * Protect multiple newborns created below, in the do-while(0) - * loop used to ensure that we leave this local root scope. - */ - ok = js_EnterLocalRootScope(cx); - if (!ok) - return JS_FALSE; - - do { - qn = js_NewXMLQName(cx, rt->emptyString, rt->emptyString, - ATOM_TO_STRING(rt->atomState.starAtom)); - if (!qn) { - ok = JS_FALSE; - break; - } - - obj = js_NewObject(cx, &js_AnyNameClass, NULL, NULL); - if (!obj || !JS_SetPrivate(cx, obj, qn)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - ok = JS_FALSE; - break; - } - qn->object = obj; - METER(xml_stats.qnameobj); - METER(xml_stats.liveqnameobj); - - /* - * Avoid entraining any Object.prototype found via cx's scope - * chain or global object. This loses the default toString, - * but no big deal: we want to customize toString anyway for - * clearer diagnostics. - */ - if (!JS_DefineFunction(cx, obj, js_toString_str, - anyname_toString, 0, 0)) { - ok = JS_FALSE; - break; - } - OBJ_SET_PROTO(cx, obj, NULL); - JS_ASSERT(!OBJ_GET_PARENT(cx, obj)); - } while (0); - - js_LeaveLocalRootScopeWithResult(cx, OBJECT_TO_JSVAL(obj)); - if (!ok) - return JS_FALSE; - - JS_LOCK_GC(rt); - if (!rt->anynameObject) - rt->anynameObject = obj; - else - obj = rt->anynameObject; - } - JS_UNLOCK_GC(rt); - } - *vp = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -JSBool -js_FindXMLProperty(JSContext *cx, jsval name, JSObject **objp, jsval *namep) -{ - JSXMLQName *qn; - jsid funid, id; - JSObject *obj, *pobj, *lastobj; - JSProperty *prop; - const char *printable; - - qn = ToXMLName(cx, name, &funid); - if (!qn) - return JS_FALSE; - id = OBJECT_TO_JSID(qn->object); - - obj = cx->fp->scopeChain; - do { - if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) - return JS_FALSE; - if (prop) { - OBJ_DROP_PROPERTY(cx, pobj, prop); - - /* - * Call OBJ_THIS_OBJECT to skip any With object that wraps an XML - * object to carry scope chain linkage in js_FilterXMLList. - */ - pobj = OBJ_THIS_OBJECT(cx, obj); - if (OBJECT_IS_XML(cx, pobj)) { - *objp = pobj; - *namep = ID_TO_VALUE(id); - return JS_TRUE; - } - } - - lastobj = obj; - } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL); - - printable = js_ValueToPrintableString(cx, name); - if (printable) { - JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, - js_GetErrorMessage, NULL, - JSMSG_UNDEFINED_XML_NAME, printable); - } - return JS_FALSE; -} - -JSBool -js_GetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp) -{ - return GetProperty(cx, obj, name, vp); -} - -JSBool -js_GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - JSObject *target; - JSXML *xml; - JSTempValueRooter tvr; - JSBool ok; - - JS_ASSERT(OBJECT_IS_XML(cx, obj)); - - /* After this point, control must flow through label out: to exit. */ - JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr); - - /* - * See comments before xml_lookupProperty about the need for the proto - * chain lookup. - */ - target = obj; - for (;;) { - ok = js_GetProperty(cx, target, id, vp); - if (!ok) - goto out; - if (VALUE_IS_FUNCTION(cx, *vp)) { - ok = JS_TRUE; - goto out; - } - target = OBJ_GET_PROTO(cx, target); - if (target == NULL) - break; - tvr.u.object = target; - } - - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (HasSimpleContent(xml)) { - /* Search in String.prototype to implement 11.2.2.1 Step 3(f). */ - ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(JSProto_String), - &tvr.u.object); - if (!ok) - goto out; - JS_ASSERT(tvr.u.object); - ok = OBJ_GET_PROPERTY(cx, tvr.u.object, id, vp); - } - - out: - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; -} - -JSBool -js_SetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp) -{ - return PutProperty(cx, obj, name, vp); -} - -static JSXML * -GetPrivate(JSContext *cx, JSObject *obj, const char *method) -{ - JSXML *xml; - - xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); - if (!xml) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_INCOMPATIBLE_METHOD, - js_XML_str, method, OBJ_GET_CLASS(cx, obj)->name); - } - return xml; -} - -JSBool -js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSXML *xml, *list; - - xml = GetPrivate(cx, obj, "descendants internal method"); - if (!xml) - return JS_FALSE; - - list = Descendants(cx, xml, id); - if (!list) - return JS_FALSE; - *vp = OBJECT_TO_JSVAL(list->object); - return JS_TRUE; -} - -JSBool -js_DeleteXMLListElements(JSContext *cx, JSObject *listobj) -{ - JSXML *list; - uint32 n; - jsval junk; - - list = (JSXML *) JS_GetPrivate(cx, listobj); - for (n = list->xml_kids.length; n != 0; --n) { - if (!DeleteProperty(cx, listobj, INT_TO_JSID(0), &junk)) - return JS_FALSE; - } - return JS_TRUE; -} - -JSBool -js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, jsval *vp) -{ - JSBool ok, match; - JSStackFrame *fp; - uint32 flags; - JSObject *scobj, *listobj, *resobj, *withobj, *kidobj; - JSXML *xml, *list, *result, *kid; - JSXMLArrayCursor cursor; - - ok = js_EnterLocalRootScope(cx); - if (!ok) - return JS_FALSE; - - /* All control flow after this point must exit via label out or bad. */ - *vp = JSVAL_NULL; - fp = cx->fp; - flags = fp->flags; - fp->flags = flags | JSFRAME_FILTERING; - scobj = js_GetScopeChain(cx, fp); - withobj = NULL; - if (!scobj) - goto bad; - xml = GetPrivate(cx, obj, "filtering predicate operator"); - if (!xml) - goto bad; - - if (xml->xml_class == JSXML_CLASS_LIST) { - list = xml; - } else { - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) - goto bad; - list = (JSXML *) JS_GetPrivate(cx, listobj); - ok = Append(cx, list, xml); - if (!ok) - goto out; - } - - resobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!resobj) - goto bad; - result = (JSXML *) JS_GetPrivate(cx, resobj); - - /* Hoist the scope chain update out of the loop over kids. */ - withobj = js_NewWithObject(cx, NULL, scobj, -1); - if (!withobj) - goto bad; - fp->scopeChain = withobj; - - XMLArrayCursorInit(&cursor, &list->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - break; - OBJ_SET_PROTO(cx, withobj, kidobj); - ok = js_Interpret(cx, pc, vp) && js_ValueToBoolean(cx, *vp, &match); - if (ok && match) - ok = Append(cx, result, kid); - if (!ok) - break; - } - XMLArrayCursorFinish(&cursor); - if (!ok) - goto out; - if (kid) - goto bad; - - *vp = OBJECT_TO_JSVAL(resobj); - -out: - fp->flags = flags | (fp->flags & JSFRAME_POP_BLOCKS); - if (withobj) { - fp->scopeChain = scobj; - JS_SetPrivate(cx, withobj, NULL); - } - js_LeaveLocalRootScopeWithResult(cx, *vp); - return ok; -bad: - ok = JS_FALSE; - goto out; -} - -JSObject * -js_ValueToXMLObject(JSContext *cx, jsval v) -{ - return ToXML(cx, v); -} - -JSObject * -js_ValueToXMLListObject(JSContext *cx, jsval v) -{ - return ToXMLList(cx, v); -} - -JSObject * -js_CloneXMLObject(JSContext *cx, JSObject *obj) -{ - uintN flags; - JSXML *xml; - - if (!GetXMLSettingFlags(cx, &flags)) - return NULL; - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (flags & (XSF_IGNORE_COMMENTS | - XSF_IGNORE_PROCESSING_INSTRUCTIONS | - XSF_IGNORE_WHITESPACE)) { - xml = DeepCopy(cx, xml, NULL, flags); - if (!xml) - return NULL; - return xml->object; - } - return NewXMLObject(cx, xml); -} - -JSObject * -js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name, - JSString *value) -{ - uintN flags; - JSObject *obj; - JSXML *xml; - JSXMLQName *qn; - - if (!GetXMLSettingFlags(cx, &flags)) - return NULL; - - if ((xml_class == JSXML_CLASS_COMMENT && - (flags & XSF_IGNORE_COMMENTS)) || - (xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION && - (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS))) { - return js_NewXMLObject(cx, JSXML_CLASS_TEXT); - } - - obj = js_NewXMLObject(cx, xml_class); - if (!obj) - return NULL; - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (name) { - qn = js_NewXMLQName(cx, cx->runtime->emptyString, NULL, name); - if (!qn) - return NULL; - xml->name = qn; - } - xml->xml_value = value; - return obj; -} - -JSString * -js_MakeXMLCDATAString(JSContext *cx, JSString *str) -{ - return MakeXMLCDATAString(cx, NULL, str); -} - -JSString * -js_MakeXMLCommentString(JSContext *cx, JSString *str) -{ - return MakeXMLCommentString(cx, NULL, str); -} - -JSString * -js_MakeXMLPIString(JSContext *cx, JSString *name, JSString *str) -{ - return MakeXMLPIString(cx, NULL, name, str); -} - -#endif /* JS_HAS_XML_SUPPORT */ diff --git a/spidermonkey/src/jsxml.h b/spidermonkey/src/jsxml.h deleted file mode 100644 index 71e591a..0000000 --- a/spidermonkey/src/jsxml.h +++ /dev/null @@ -1,332 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 SpiderMonkey E4X code, released August, 2004. - * - * 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 of 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 ***** */ - -#ifndef jsxml_h___ -#define jsxml_h___ - -#include "jsstddef.h" -#include "jspubtd.h" - -extern const char js_AnyName_str[]; -extern const char js_AttributeName_str[]; -extern const char js_isXMLName_str[]; -extern const char js_XMLList_str[]; - -extern const char js_amp_entity_str[]; -extern const char js_gt_entity_str[]; -extern const char js_lt_entity_str[]; -extern const char js_quot_entity_str[]; - -struct JSXMLNamespace { - JSObject *object; - JSString *prefix; - JSString *uri; - JSBool declared; /* true if declared in its XML tag */ -}; - -extern JSXMLNamespace * -js_NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, - JSBool declared); - -extern void -js_MarkXMLNamespace(JSContext *cx, JSXMLNamespace *ns); - -extern void -js_FinalizeXMLNamespace(JSContext *cx, JSXMLNamespace *ns); - -extern JSObject * -js_NewXMLNamespaceObject(JSContext *cx, JSString *prefix, JSString *uri, - JSBool declared); - -extern JSObject * -js_GetXMLNamespaceObject(JSContext *cx, JSXMLNamespace *ns); - -struct JSXMLQName { - JSObject *object; - JSString *uri; - JSString *prefix; - JSString *localName; -}; - -extern JSXMLQName * -js_NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, - JSString *localName); - -extern void -js_MarkXMLQName(JSContext *cx, JSXMLQName *qn); - -extern void -js_FinalizeXMLQName(JSContext *cx, JSXMLQName *qn); - -extern JSObject * -js_NewXMLQNameObject(JSContext *cx, JSString *uri, JSString *prefix, - JSString *localName); - -extern JSObject * -js_GetXMLQNameObject(JSContext *cx, JSXMLQName *qn); - -extern JSObject * -js_GetAttributeNameObject(JSContext *cx, JSXMLQName *qn); - -extern JSObject * -js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval); - -typedef JSBool -(* JS_DLL_CALLBACK JSIdentityOp)(const void *a, const void *b); - -struct JSXMLArray { - uint32 length; - uint32 capacity; - void **vector; - JSXMLArrayCursor *cursors; -}; - -#define JSXML_PRESET_CAPACITY JS_BIT(31) -#define JSXML_CAPACITY_MASK JS_BITMASK(31) -#define JSXML_CAPACITY(array) ((array)->capacity & JSXML_CAPACITY_MASK) - -struct JSXMLArrayCursor { - JSXMLArray *array; - uint32 index; - JSXMLArrayCursor *next; - JSXMLArrayCursor **prevp; - void *root; -}; - -/* - * NB: don't reorder this enum without changing all array initializers that - * depend on it in jsxml.c. - */ -typedef enum JSXMLClass { - JSXML_CLASS_LIST, - JSXML_CLASS_ELEMENT, - JSXML_CLASS_ATTRIBUTE, - JSXML_CLASS_PROCESSING_INSTRUCTION, - JSXML_CLASS_TEXT, - JSXML_CLASS_COMMENT, - JSXML_CLASS_LIMIT -} JSXMLClass; - -#define JSXML_CLASS_HAS_KIDS(class_) ((class_) < JSXML_CLASS_ATTRIBUTE) -#define JSXML_CLASS_HAS_VALUE(class_) ((class_) >= JSXML_CLASS_ATTRIBUTE) -#define JSXML_CLASS_HAS_NAME(class_) \ - ((uintN)((class_) - JSXML_CLASS_ELEMENT) <= \ - (uintN)(JSXML_CLASS_PROCESSING_INSTRUCTION - JSXML_CLASS_ELEMENT)) - -#ifdef DEBUG_notme -#include "jsclist.h" -#endif - -struct JSXML { -#ifdef DEBUG_notme - JSCList links; - uint32 serial; -#endif - JSObject *object; - void *domnode; /* DOM node if mapped info item */ - JSXML *parent; - JSXMLQName *name; - uint16 xml_class; /* discriminates u, below */ - uint16 xml_flags; /* flags, see below */ - union { - struct JSXMLListVar { - JSXMLArray kids; /* NB: must come first */ - JSXML *target; - JSXMLQName *targetprop; - } list; - struct JSXMLVar { - JSXMLArray kids; /* NB: must come first */ - JSXMLArray namespaces; - JSXMLArray attrs; - } elem; - JSString *value; - } u; - - /* Don't add anything after u -- see js_NewXML for why. */ -}; - -/* union member shorthands */ -#define xml_kids u.list.kids -#define xml_target u.list.target -#define xml_targetprop u.list.targetprop -#define xml_namespaces u.elem.namespaces -#define xml_attrs u.elem.attrs -#define xml_value u.value - -/* xml_flags values */ -#define XMLF_WHITESPACE_TEXT 0x1 - -/* xml_class-testing macros */ -#define JSXML_HAS_KIDS(xml) JSXML_CLASS_HAS_KIDS((xml)->xml_class) -#define JSXML_HAS_VALUE(xml) JSXML_CLASS_HAS_VALUE((xml)->xml_class) -#define JSXML_HAS_NAME(xml) JSXML_CLASS_HAS_NAME((xml)->xml_class) -#define JSXML_LENGTH(xml) (JSXML_CLASS_HAS_KIDS((xml)->xml_class) \ - ? (xml)->xml_kids.length \ - : 0) - -extern JSXML * -js_NewXML(JSContext *cx, JSXMLClass xml_class); - -extern void -js_MarkXML(JSContext *cx, JSXML *xml); - -extern void -js_FinalizeXML(JSContext *cx, JSXML *xml); - -extern JSObject * -js_ParseNodeToXMLObject(JSContext *cx, JSParseNode *pn); - -extern JSObject * -js_NewXMLObject(JSContext *cx, JSXMLClass xml_class); - -extern JSObject * -js_GetXMLObject(JSContext *cx, JSXML *xml); - -extern JS_FRIEND_DATA(JSXMLObjectOps) js_XMLObjectOps; -extern JS_FRIEND_DATA(JSClass) js_XMLClass; -extern JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass; -extern JS_FRIEND_DATA(JSExtendedClass) js_QNameClass; -extern JS_FRIEND_DATA(JSClass) js_AttributeNameClass; -extern JS_FRIEND_DATA(JSClass) js_AnyNameClass; - -/* - * Macros to test whether an object or a value is of type "xml" (per typeof). - * NB: jsapi.h must be included before any call to VALUE_IS_XML. - */ -#define OBJECT_IS_XML(cx,obj) ((obj)->map->ops == &js_XMLObjectOps.base) -#define VALUE_IS_XML(cx,v) (!JSVAL_IS_PRIMITIVE(v) && \ - OBJECT_IS_XML(cx, JSVAL_TO_OBJECT(v))) - -extern JSObject * -js_InitNamespaceClass(JSContext *cx, JSObject *obj); - -extern JSObject * -js_InitQNameClass(JSContext *cx, JSObject *obj); - -extern JSObject * -js_InitAttributeNameClass(JSContext *cx, JSObject *obj); - -extern JSObject * -js_InitAnyNameClass(JSContext *cx, JSObject *obj); - -extern JSObject * -js_InitXMLClass(JSContext *cx, JSObject *obj); - -extern JSObject * -js_InitXMLClasses(JSContext *cx, JSObject *obj); - -extern JSBool -js_GetFunctionNamespace(JSContext *cx, jsval *vp); - -extern JSBool -js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp); - -extern JSBool -js_SetDefaultXMLNamespace(JSContext *cx, jsval v); - -/* - * Return true if v is a XML QName object, or if it converts to a string that - * contains a valid XML qualified name (one containing no :), false otherwise. - * NB: This function is an infallible predicate, it hides exceptions. - */ -extern JSBool -js_IsXMLName(JSContext *cx, jsval v); - -extern JSBool -js_ToAttributeName(JSContext *cx, jsval *vp); - -extern JSString * -js_EscapeAttributeValue(JSContext *cx, JSString *str); - -extern JSString * -js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, - JSString *str2); - -extern JSString * -js_EscapeElementValue(JSContext *cx, JSString *str); - -extern JSString * -js_ValueToXMLString(JSContext *cx, jsval v); - -extern JSBool -js_GetAnyName(JSContext *cx, jsval *vp); - -extern JSBool -js_FindXMLProperty(JSContext *cx, jsval name, JSObject **objp, jsval *namep); - -extern JSBool -js_GetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp); - -extern JSBool -js_GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -extern JSBool -js_SetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp); - -extern JSBool -js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -extern JSBool -js_DeleteXMLListElements(JSContext *cx, JSObject *listobj); - -extern JSBool -js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, jsval *vp); - -extern JSObject * -js_ValueToXMLObject(JSContext *cx, jsval v); - -extern JSObject * -js_ValueToXMLListObject(JSContext *cx, jsval v); - -extern JSObject * -js_CloneXMLObject(JSContext *cx, JSObject *obj); - -extern JSObject * -js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name, - JSString *value); - -extern JSString * -js_MakeXMLCDATAString(JSContext *cx, JSString *str); - -extern JSString * -js_MakeXMLCommentString(JSContext *cx, JSString *str); - -extern JSString * -js_MakeXMLPIString(JSContext *cx, JSString *name, JSString *str); - -#endif /* jsxml_h___ */ diff --git a/spidermonkey/src/perfect.js b/spidermonkey/src/perfect.js deleted file mode 100644 index aeca121..0000000 --- a/spidermonkey/src/perfect.js +++ /dev/null @@ -1,39 +0,0 @@ -// Some simple testing of new, eval and some string stuff. - -// constructor -- expression array initialization -function ExprArray(n,v) -{ - // Initializes n values to v coerced to a string. - for (var i = 0; i < n; i++) { - this[i] = "" + v; - } -} - - -// Print the perfect numbers up to n and the sum expression for n's divisors. -function perfect(n) -{ - print("The perfect numbers up to " + n + " are:"); - - // We build sumOfDivisors[i] to hold a string expression for - // the sum of the divisors of i, excluding i itself. - var sumOfDivisors = new ExprArray(n+1,1); - for (var divisor = 2; divisor <= n; divisor++) { - for (var j = divisor + divisor; j <= n; j += divisor) { - sumOfDivisors[j] += " + " + divisor; - } - // At this point everything up to 'divisor' has its sumOfDivisors - // expression calculated, so we can determine whether it's perfect - // already by evaluating. - if (eval(sumOfDivisors[divisor]) == divisor) { - print("" + divisor + " = " + sumOfDivisors[divisor]); - } - } - print("That's all."); -} - - -print("\nA number is 'perfect' if it is equal to the sum of its") -print("divisors (excluding itself).\n"); -perfect(500); - diff --git a/spidermonkey/src/plify_jsdhash.sed b/spidermonkey/src/plify_jsdhash.sed deleted file mode 100644 index eff4901..0000000 --- a/spidermonkey/src/plify_jsdhash.sed +++ /dev/null @@ -1,33 +0,0 @@ -/ * Double hashing implementation./a\ - * GENERATED BY js/src/plify_jsdhash.sed -- DO NOT EDIT!!! -/ * Double hashing, a la Knuth 6./a\ - * GENERATED BY js/src/plify_jsdhash.sed -- DO NOT EDIT!!! -s/jsdhash_h___/pldhash_h___/ -s/jsdhash\.bigdump/pldhash.bigdump/ -s/jstypes\.h/nscore.h/ -s/jsbit\.h/prbit.h/ -s/jsdhash\.h/pldhash.h/ -s/jsdhash\.c/pldhash.c/ -s/jsdhash:/pldhash:/ -s/jsutil\.h/nsDebug.h/ -s/JS_DHASH/PL_DHASH/g -s/JS_DHash/PL_DHash/g -s/JSDHash/PLDHash/g -s/JSHash/PLHash/g -s/uint32 /PRUint32/g -s/\([^U]\)int32 /\1PRInt32/g -s/uint16 /PRUint16/g -s/\([^U]\)int16 /\1PRInt16/g -s/uint32/PRUint32/g -s/\([^U]\)int32/\1PRInt32/g -s/uint16/PRUint16/g -s/\([^U]\)int16/\1PRInt16/g -s/JSBool/PRBool/g -s/extern JS_PUBLIC_API(\([^()]*\))/NS_COM_GLUE \1/ -s/JS_PUBLIC_API(\([^()]*\))/\1/ -s/JS_DLL_CALLBACK/PR_CALLBACK/ -s/JS_STATIC_DLL_CALLBACK/PR_STATIC_CALLBACK/ -s/JS_NewDHashTable/PL_NewDHashTable/ -s/JS_ASSERT(0)/NS_NOTREACHED("0")/ -s/\( *\)JS_ASSERT(\(.*\));/\1NS_ASSERTION(\2,\n\1 "\2");/ -s/JS_/PR_/g diff --git a/spidermonkey/src/prmjtime.c b/spidermonkey/src/prmjtime.c deleted file mode 100644 index 6e08423..0000000 --- a/spidermonkey/src/prmjtime.c +++ /dev/null @@ -1,440 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -/* - * PR time code. - */ -#include "jsstddef.h" -#ifdef SOLARIS -#define _REENTRANT 1 -#endif -#include -#include -#include "jstypes.h" -#include "jsutil.h" - -#include "jsprf.h" -#include "prmjtime.h" - -#define PRMJ_DO_MILLISECONDS 1 - -#ifdef XP_OS2 -#include -#endif -#ifdef XP_WIN -#include -#include -#endif - -#if defined(XP_UNIX) || defined(XP_BEOS) - -#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris */ -extern int gettimeofday(struct timeval *tv); -#endif - -#include - -#endif /* XP_UNIX */ - -#define IS_LEAP(year) \ - (year != 0 && ((((year & 0x3) == 0) && \ - ((year - ((year/100) * 100)) != 0)) || \ - (year - ((year/400) * 400)) == 0)) - -#define PRMJ_HOUR_SECONDS 3600L -#define PRMJ_DAY_SECONDS (24L * PRMJ_HOUR_SECONDS) -#define PRMJ_YEAR_SECONDS (PRMJ_DAY_SECONDS * 365L) -#define PRMJ_MAX_UNIX_TIMET 2145859200L /*time_t value equiv. to 12/31/2037 */ -/* function prototypes */ -static void PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm); -/* - * get the difference in seconds between this time zone and UTC (GMT) - */ -JSInt32 -PRMJ_LocalGMTDifference() -{ -#if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) - struct tm ltime; - - /* get the difference between this time zone and GMT */ - memset((char *)<ime,0,sizeof(ltime)); - ltime.tm_mday = 2; - ltime.tm_year = 70; -#ifdef SUNOS4 - ltime.tm_zone = 0; - ltime.tm_gmtoff = 0; - return timelocal(<ime) - (24 * 3600); -#else - return mktime(<ime) - (24L * 3600L); -#endif -#endif -} - -/* Constants for GMT offset from 1970 */ -#define G1970GMTMICROHI 0x00dcdcad /* micro secs to 1970 hi */ -#define G1970GMTMICROLOW 0x8b3fa000 /* micro secs to 1970 low */ - -#define G2037GMTMICROHI 0x00e45fab /* micro secs to 2037 high */ -#define G2037GMTMICROLOW 0x7a238000 /* micro secs to 2037 low */ - -/* Convert from base time to extended time */ -static JSInt64 -PRMJ_ToExtendedTime(JSInt32 base_time) -{ - JSInt64 exttime; - JSInt64 g1970GMTMicroSeconds; - JSInt64 low; - JSInt32 diff; - JSInt64 tmp; - JSInt64 tmp1; - - diff = PRMJ_LocalGMTDifference(); - JSLL_UI2L(tmp, PRMJ_USEC_PER_SEC); - JSLL_I2L(tmp1,diff); - JSLL_MUL(tmp,tmp,tmp1); - - JSLL_UI2L(g1970GMTMicroSeconds,G1970GMTMICROHI); - JSLL_UI2L(low,G1970GMTMICROLOW); -#ifndef JS_HAVE_LONG_LONG - JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16); - JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16); -#else - JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,32); -#endif - JSLL_ADD(g1970GMTMicroSeconds,g1970GMTMicroSeconds,low); - - JSLL_I2L(exttime,base_time); - JSLL_ADD(exttime,exttime,g1970GMTMicroSeconds); - JSLL_SUB(exttime,exttime,tmp); - return exttime; -} - -JSInt64 -PRMJ_Now(void) -{ -#ifdef XP_OS2 - JSInt64 s, us, ms2us, s2us; - struct timeb b; -#endif -#ifdef XP_WIN - JSInt64 s, us, - win2un = JSLL_INIT(0x19DB1DE, 0xD53E8000), - ten = JSLL_INIT(0, 10); - FILETIME time, midnight; -#endif -#if defined(XP_UNIX) || defined(XP_BEOS) - struct timeval tv; - JSInt64 s, us, s2us; -#endif /* XP_UNIX */ - -#ifdef XP_OS2 - ftime(&b); - JSLL_UI2L(ms2us, PRMJ_USEC_PER_MSEC); - JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC); - JSLL_UI2L(s, b.time); - JSLL_UI2L(us, b.millitm); - JSLL_MUL(us, us, ms2us); - JSLL_MUL(s, s, s2us); - JSLL_ADD(s, s, us); - return s; -#endif -#ifdef XP_WIN - /* The windows epoch is around 1600. The unix epoch is around 1970. - win2un is the difference (in windows time units which are 10 times - more precise than the JS time unit) */ - GetSystemTimeAsFileTime(&time); - /* Win9x gets confused at midnight - http://support.microsoft.com/default.aspx?scid=KB;en-us;q224423 - So if the low part (precision <8mins) is 0 then we get the time - again. */ - if (!time.dwLowDateTime) { - GetSystemTimeAsFileTime(&midnight); - time.dwHighDateTime = midnight.dwHighDateTime; - } - JSLL_UI2L(s, time.dwHighDateTime); - JSLL_UI2L(us, time.dwLowDateTime); - JSLL_SHL(s, s, 32); - JSLL_ADD(s, s, us); - JSLL_SUB(s, s, win2un); - JSLL_DIV(s, s, ten); - return s; -#endif - -#if defined(XP_UNIX) || defined(XP_BEOS) -#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris */ - gettimeofday(&tv); -#else - gettimeofday(&tv, 0); -#endif /* _SVID_GETTOD */ - JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC); - JSLL_UI2L(s, tv.tv_sec); - JSLL_UI2L(us, tv.tv_usec); - JSLL_MUL(s, s, s2us); - JSLL_ADD(s, s, us); - return s; -#endif /* XP_UNIX */ -} - -/* Get the DST timezone offset for the time passed in */ -JSInt64 -PRMJ_DSTOffset(JSInt64 local_time) -{ - JSInt64 us2s; - time_t local; - JSInt32 diff; - JSInt64 maxtimet; - struct tm tm; - PRMJTime prtm; -#ifndef HAVE_LOCALTIME_R - struct tm *ptm; -#endif - - - JSLL_UI2L(us2s, PRMJ_USEC_PER_SEC); - JSLL_DIV(local_time, local_time, us2s); - - /* get the maximum of time_t value */ - JSLL_UI2L(maxtimet,PRMJ_MAX_UNIX_TIMET); - - if(JSLL_CMP(local_time,>,maxtimet)){ - JSLL_UI2L(local_time,PRMJ_MAX_UNIX_TIMET); - } else if(!JSLL_GE_ZERO(local_time)){ - /*go ahead a day to make localtime work (does not work with 0) */ - JSLL_UI2L(local_time,PRMJ_DAY_SECONDS); - } - JSLL_L2UI(local,local_time); - PRMJ_basetime(local_time,&prtm); -#ifndef HAVE_LOCALTIME_R - ptm = localtime(&local); - if(!ptm){ - return JSLL_ZERO; - } - tm = *ptm; -#else - localtime_r(&local,&tm); /* get dst information */ -#endif - - diff = ((tm.tm_hour - prtm.tm_hour) * PRMJ_HOUR_SECONDS) + - ((tm.tm_min - prtm.tm_min) * 60); - - if(diff < 0){ - diff += PRMJ_DAY_SECONDS; - } - - JSLL_UI2L(local_time,diff); - - JSLL_MUL(local_time,local_time,us2s); - - return(local_time); -} - -/* Format a time value into a buffer. Same semantics as strftime() */ -size_t -PRMJ_FormatTime(char *buf, int buflen, char *fmt, PRMJTime *prtm) -{ -#if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) - struct tm a; - - /* Zero out the tm struct. Linux, SunOS 4 struct tm has extra members int - * tm_gmtoff, char *tm_zone; when tm_zone is garbage, strftime gets - * confused and dumps core. NSPR20 prtime.c attempts to fill these in by - * calling mktime on the partially filled struct, but this doesn't seem to - * work as well; the result string has "can't get timezone" for ECMA-valid - * years. Might still make sense to use this, but find the range of years - * for which valid tz information exists, and map (per ECMA hint) from the - * given year into that range. - - * N.B. This hasn't been tested with anything that actually _uses_ - * tm_gmtoff; zero might be the wrong thing to set it to if you really need - * to format a time. This fix is for jsdate.c, which only uses - * JS_FormatTime to get a string representing the time zone. */ - memset(&a, 0, sizeof(struct tm)); - - a.tm_sec = prtm->tm_sec; - a.tm_min = prtm->tm_min; - a.tm_hour = prtm->tm_hour; - a.tm_mday = prtm->tm_mday; - a.tm_mon = prtm->tm_mon; - a.tm_wday = prtm->tm_wday; - a.tm_year = prtm->tm_year - 1900; - a.tm_yday = prtm->tm_yday; - a.tm_isdst = prtm->tm_isdst; - - /* Even with the above, SunOS 4 seems to detonate if tm_zone and tm_gmtoff - * are null. This doesn't quite work, though - the timezone is off by - * tzoff + dst. (And mktime seems to return -1 for the exact dst - * changeover time.) - - */ - -#if defined(SUNOS4) - if (mktime(&a) == -1) { - /* Seems to fail whenever the requested date is outside of the 32-bit - * UNIX epoch. We could proceed at this point (setting a.tm_zone to - * "") but then strftime returns a string with a 2-digit field of - * garbage for the year. So we return 0 and hope jsdate.c - * will fall back on toString. - */ - return 0; - } -#endif - - return strftime(buf, buflen, fmt, &a); -#endif -} - -/* table for number of days in a month */ -static int mtab[] = { - /* jan, feb,mar,apr,may,jun */ - 31,28,31,30,31,30, - /* july,aug,sep,oct,nov,dec */ - 31,31,30,31,30,31 -}; - -/* - * basic time calculation functionality for localtime and gmtime - * setups up prtm argument with correct values based upon input number - * of seconds. - */ -static void -PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm) -{ - /* convert tsecs back to year,month,day,hour,secs */ - JSInt32 year = 0; - JSInt32 month = 0; - JSInt32 yday = 0; - JSInt32 mday = 0; - JSInt32 wday = 6; /* start on a Sunday */ - JSInt32 days = 0; - JSInt32 seconds = 0; - JSInt32 minutes = 0; - JSInt32 hours = 0; - JSInt32 isleap = 0; - JSInt64 result; - JSInt64 result1; - JSInt64 result2; - JSInt64 base; - - JSLL_UI2L(result,0); - JSLL_UI2L(result1,0); - JSLL_UI2L(result2,0); - - /* get the base time via UTC */ - base = PRMJ_ToExtendedTime(0); - JSLL_UI2L(result, PRMJ_USEC_PER_SEC); - JSLL_DIV(base,base,result); - JSLL_ADD(tsecs,tsecs,base); - - JSLL_UI2L(result, PRMJ_YEAR_SECONDS); - JSLL_UI2L(result1,PRMJ_DAY_SECONDS); - JSLL_ADD(result2,result,result1); - - /* get the year */ - while ((isleap == 0) ? !JSLL_CMP(tsecs,<,result) : !JSLL_CMP(tsecs,<,result2)) { - /* subtract a year from tsecs */ - JSLL_SUB(tsecs,tsecs,result); - days += 365; - /* is it a leap year ? */ - if(IS_LEAP(year)){ - JSLL_SUB(tsecs,tsecs,result1); - days++; - } - year++; - isleap = IS_LEAP(year); - } - - JSLL_UI2L(result1,PRMJ_DAY_SECONDS); - - JSLL_DIV(result,tsecs,result1); - JSLL_L2I(mday,result); - - /* let's find the month */ - while(((month == 1 && isleap) ? - (mday >= mtab[month] + 1) : - (mday >= mtab[month]))){ - yday += mtab[month]; - days += mtab[month]; - - mday -= mtab[month]; - - /* it's a Feb, check if this is a leap year */ - if(month == 1 && isleap != 0){ - yday++; - days++; - mday--; - } - month++; - } - - /* now adjust tsecs */ - JSLL_MUL(result,result,result1); - JSLL_SUB(tsecs,tsecs,result); - - mday++; /* day of month always start with 1 */ - days += mday; - wday = (days + wday) % 7; - - yday += mday; - - /* get the hours */ - JSLL_UI2L(result1,PRMJ_HOUR_SECONDS); - JSLL_DIV(result,tsecs,result1); - JSLL_L2I(hours,result); - JSLL_MUL(result,result,result1); - JSLL_SUB(tsecs,tsecs,result); - - /* get minutes */ - JSLL_UI2L(result1,60); - JSLL_DIV(result,tsecs,result1); - JSLL_L2I(minutes,result); - JSLL_MUL(result,result,result1); - JSLL_SUB(tsecs,tsecs,result); - - JSLL_L2I(seconds,tsecs); - - prtm->tm_usec = 0L; - prtm->tm_sec = (JSInt8)seconds; - prtm->tm_min = (JSInt8)minutes; - prtm->tm_hour = (JSInt8)hours; - prtm->tm_mday = (JSInt8)mday; - prtm->tm_mon = (JSInt8)month; - prtm->tm_wday = (JSInt8)wday; - prtm->tm_year = (JSInt16)year; - prtm->tm_yday = (JSInt16)yday; -} diff --git a/spidermonkey/src/prmjtime.h b/spidermonkey/src/prmjtime.h deleted file mode 100644 index b74fe84..0000000 --- a/spidermonkey/src/prmjtime.h +++ /dev/null @@ -1,95 +0,0 @@ -/* -*- Mode: C; tab-width: 8; 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 Communicator client code, released - * March 31, 1998. - * - * 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 of 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 ***** */ - -#ifndef prmjtime_h___ -#define prmjtime_h___ -/* - * PR date stuff for mocha and java. Placed here temporarily not to break - * Navigator and localize changes to mocha. - */ -#include -#include "jslong.h" -#ifdef MOZILLA_CLIENT -#include "jscompat.h" -#endif - -JS_BEGIN_EXTERN_C - -typedef struct PRMJTime PRMJTime; - -/* - * Broken down form of 64 bit time value. - */ -struct PRMJTime { - JSInt32 tm_usec; /* microseconds of second (0-999999) */ - JSInt8 tm_sec; /* seconds of minute (0-59) */ - JSInt8 tm_min; /* minutes of hour (0-59) */ - JSInt8 tm_hour; /* hour of day (0-23) */ - JSInt8 tm_mday; /* day of month (1-31) */ - JSInt8 tm_mon; /* month of year (0-11) */ - JSInt8 tm_wday; /* 0=sunday, 1=monday, ... */ - JSInt16 tm_year; /* absolute year, AD */ - JSInt16 tm_yday; /* day of year (0 to 365) */ - JSInt8 tm_isdst; /* non-zero if DST in effect */ -}; - -/* Some handy constants */ -#define PRMJ_USEC_PER_SEC 1000000L -#define PRMJ_USEC_PER_MSEC 1000L - -/* Return the current local time in micro-seconds */ -extern JSInt64 -PRMJ_Now(void); - -/* get the difference between this time zone and gmt timezone in seconds */ -extern JSInt32 -PRMJ_LocalGMTDifference(void); - -/* Format a time value into a buffer. Same semantics as strftime() */ -extern size_t -PRMJ_FormatTime(char *buf, int buflen, char *fmt, PRMJTime *tm); - -/* Get the DST offset for the local time passed in */ -extern JSInt64 -PRMJ_DSTOffset(JSInt64 local_time); - -JS_END_EXTERN_C - -#endif /* prmjtime_h___ */ - diff --git a/spidermonkey/src/resource.h b/spidermonkey/src/resource.h deleted file mode 100644 index 9301810..0000000 --- a/spidermonkey/src/resource.h +++ /dev/null @@ -1,15 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Developer Studio generated include file. -// Used by js3240.rc -// - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 101 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1000 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/spidermonkey/src/win32.order b/spidermonkey/src/win32.order deleted file mode 100644 index cf4e8c4..0000000 --- a/spidermonkey/src/win32.order +++ /dev/null @@ -1,391 +0,0 @@ -js_MarkGCThing ; 5893956 -JS_GetPrivate ; 2090130 -JS_HashTableRawLookup ; 1709984 -js_Mark ; 1547496 -js_GetToken ; 1406677 -js_UngetToken ; 1154416 -js_MarkAtom ; 992874 -js_MatchToken ; 980277 -js_CompareStrings ; 662772 -js_Lock ; 628184 -js_Unlock ; 628184 -js_AtomizeString ; 611102 -js_HashString ; 611102 -js_DropScopeProperty ; 546476 -JS_malloc ; 484350 -js_Atomize ; 464433 -js_InflateStringToBuffer ; 460739 -js_HoldScopeProperty ; 442612 -JS_free ; 382991 -js_MarkScript ; 376942 -js_HashId ; 365238 -JS_CompareValues ; 352366 -js_IdToValue ; 337594 -JS_GetClass ; 325296 -js_LookupProperty ; 324680 -js_GetAtom ; 244669 -js_DropProperty ; 223217 -JS_GetParent ; 209680 -js_LiveContext ; 205767 -js_PeekToken ; 200646 -js_GetSlotThreadSafe ; 198839 -JS_GetStringChars ; 190862 -JS_HashTableRawAdd ; 179156 -js_FoldConstants ; 162626 -js_EmitTree ; 145634 -JS_EnumerateStub ; 140640 -js_NewSrcNote ; 136983 -js_GetProperty ; 135639 -js_NewScopeProperty ; 135057 -js_MutateScope ; 135057 -js_GetMutableScope ; 135057 -js_AllocSlot ; 132401 -JS_GetRuntime ; 127316 -JS_FrameIterator ; 121963 -JS_GetFrameFunctionObject ; 120567 -js_AllocGCThing ; 119828 -js_DestroyScopeProperty ; 115989 -js_Emit3 ; 109135 -js_AtomizeChars ; 108038 -JS_HashTableLookup ; 107154 -JS_InstanceOf ; 103905 -js_DefineProperty ; 99514 -js_strncpy ; 88276 -js_PeekTokenSameLine ; 87197 -js_HoldObjectMap ; 79084 -js_DropObjectMap ; 77824 -js_NewObject ; 72421 -js_ValueToString ; 72143 -js_GetClassPrototype ; 66235 -js_UnlockRuntime ; 64699 -js_LockRuntime ; 64699 -js_ContextIterator ; 64586 -JS_ClearWatchPointsForObject ; 64155 -js_FinalizeObject ; 63925 -js_IndexAtom ; 63789 -JS_SetPrivate ; 63702 -JS_GetGlobalObject ; 63546 -js_Emit1 ; 63012 -JS_ContextIterator ; 57847 -JS_GetInstancePrivate ; 57817 -JS_HashTableRawRemove ; 57057 -js_AllocRawStack ; 54181 -js_Invoke ; 53568 -js_FindProperty ; 53150 -JS_GetFrameScript ; 51395 -js_LinkFunctionObject ; 50651 -js_SetSrcNoteOffset ; 47735 -js_InWithStatement ; 47346 -js_NewFunction ; 47074 -js_NewSrcNote2 ; 46165 -JS_HashTableAdd ; 45503 -JS_HashTableRemove ; 45213 -js_InCatchBlock ; 42198 -js_AddRootRT ; 40587 -js_AddRoot ; 40587 -js_SetProperty ; 40558 -JS_AddNamedRoot ; 40462 -js_RemoveRoot ; 40384 -JS_RemoveRootRT ; 38129 -js_NewString ; 37471 -js_DefineFunction ; 36629 -JS_GetContextThread ; 36498 -JS_LookupProperty ; 35137 -JS_ValueToString ; 34072 -JS_realloc ; 33776 -JS_DefineFunction ; 33268 -JS_SetErrorReporter ; 32851 -js_FinalizeString ; 30311 -js_FinalizeStringRT ; 30311 -JS_ArenaAllocate ; 30099 -JS_BeginRequest ; 29323 -JS_EndRequest ; 29323 -JS_GetContextPrivate ; 29189 -JS_CompactArenaPool ; 28874 -js_ValueToStringAtom ; 27934 -JS_ValueToId ; 26517 -js_ValueToBoolean ; 25908 -JS_InternString ; 25467 -js_PopStatement ; 24364 -js_PushStatement ; 24364 -js_NewStringCopyN ; 23911 -js_FlushPropertyCacheByProp ; 23883 -js_GetStringBytes ; 23421 -JS_ArenaRelease ; 23267 -JS_GetStringBytes ; 23106 -js_FreeStack ; 22399 -js_AllocStack ; 22399 -JS_SetProperty ; 21240 -js_InitObjectMap ; 19991 -js_NewScope ; 19991 -js_strlen ; 19070 -JS_GetScriptPrincipals ; 18063 -js_SrcNoteLength ; 17369 -js_DestroyObjectMap ; 17198 -js_DestroyScope ; 17198 -JS_GetStringLength ; 16306 -js_PopStatementCG ; 15418 -JS_GetFrameAnnotation ; 14949 -js_FreeRawStack ; 14032 -js_Interpret ; 14032 -js_TransferScopeLock ; 13899 -JS_ResolveStandardClass ; 13645 -JS_ResumeRequest ; 12837 -JS_SuspendRequest ; 12837 -JS_GetProperty ; 12488 -JS_NewObject ; 11660 -js_AllocTryNotes ; 11418 -js_NewNumberValue ; 10859 -js_InternalInvoke ; 10051 -js_NewDouble ; 9936 -js_SetJumpOffset ; 9886 -js_SkipWhiteSpace ; 9299 -js_NewDoubleValue ; 7474 -JS_GetPendingException ; 7404 -js_NewObjectMap ; 7236 -JS_ClearPendingException ; 7092 -JS_strtod ; 7053 -js_strtod ; 7053 -js_InflateString ; 7004 -JS_GetFunctionName ; 6808 -JS_NewHashTable ; 6794 -JS_NewFunction ; 6575 -js_FreeSlot ; 6476 -js_LockScope ; 6332 -JS_HashTableEnumerateEntries ; 6285 -js_GetLengthProperty ; 6162 -js_LockObj ; 6149 -JS_NewUCStringCopyN ; 5994 -JS_NewNumberValue ; 5904 -js_NewStringCopyZ ; 5809 -JS_NewUCStringCopyZ ; 5809 -js_DeflateString ; 5612 -js_ValueToNumber ; 5456 -JS_SetOptions ; 5322 -js_NewScript ; 4941 -js_InitCodeGenerator ; 4810 -js_FinishTakingSrcNotes ; 4810 -js_NewScriptFromParams ; 4810 -js_InitAtomMap ; 4810 -js_FinishTakingTryNotes ; 4810 -js_NewScriptFromCG ; 4810 -js_FinishCodeGenerator ; 4810 -JS_strdup ; 4534 -JS_HashTableDestroy ; 4119 -js_CheckRedeclaration ; 3965 -JS_DefineFunctions ; 3808 -js_EmitFunctionBody ; 3739 -js_TryMethod ; 3685 -js_DefaultValue ; 3610 -js_CloneFunctionObject ; 3577 -JS_InitClass ; 3546 -js_SetClassPrototype ; 3377 -JS_GetPrototype ; 3268 -JS_DefineProperties ; 3115 -js_FindVariable ; 3093 -js_DestroyScript ; 3041 -JS_ClearScriptTraps ; 3041 -js_FreeAtomMap ; 3041 -JS_NewStringCopyZ ; 2953 -js_AtomizeObject ; 2709 -JS_ValueToBoolean ; 2643 -js_SetLengthProperty ; 2637 -JS_GetOptions ; 2593 -js_ValueToObject ; 2522 -js_ValueToNonNullObject ; 2510 -js_StringToObject ; 2482 -JS_SetElement ; 2448 -js_NumberToString ; 2407 -JS_TypeOfValue ; 2275 -js_NewBufferTokenStream ; 2253 -js_NewTokenStream ; 2253 -js_CloseTokenStream ; 2253 -JS_RemoveRoot ; 2148 -JS_NewDouble ; 2129 -JS_vsnprintf ; 1937 -JS_snprintf ; 1937 -JS_CallFunctionValue ; 1844 -JS_DHashVoidPtrKeyStub ; 1840 -JS_DHashTableOperate ; 1840 -js_SetProtoOrParent ; 1758 -js_DoubleToInteger ; 1729 -JS_SetVersion ; 1531 -js_ValueToFunction ; 1476 -JS_SetPrototype ; 1408 -JS_CeilingLog2 ; 1317 -js_Execute ; 1199 -js_CompileFunctionBody ; 1182 -JS_CompileUCFunctionForPrincipals ; 1182 -js_GetSrcNoteOffset ; 1139 -JS_DHashMatchEntryStub ; 1094 -JS_VersionToString ; 1090 -JS_CompileUCScriptForPrincipals ; 1071 -js_CompileTokenStream ; 1071 -js_CurrentThreadId ; 1058 -JS_IdToValue ; 1046 -js_ConstructObject ; 974 -JS_DestroyScript ; 967 -js_PCToLineNumber ; 967 -JS_DefineProperty ; 930 -JS_GetScriptFilename ; 924 -JS_GetFramePC ; 899 -JS_EvaluateUCScriptForPrincipals ; 892 -JS_PCToLineNumber ; 848 -JS_StringToVersion ; 761 -js_ExecuteRegExp ; 755 -JS_MaybeGC ; 717 -JS_ValueToNumber ; 698 -JS_GetVersion ; 698 -JS_AliasProperty ; 693 -js_AtomizeValue ; 664 -js_BooleanToString ; 664 -js_SetSlotThreadSafe ; 596 -JS_DHashClearEntryStub ; 584 -JS_DHashTableRawRemove ; 584 -JS_DefineObject ; 557 -js_PutCallObject ; 516 -js_GetCallObject ; 516 -js_strchr ; 511 -JS_DefineUCProperty ; 480 -JS_dtostr ; 475 -JS_ValueToInt32 ; 464 -js_ValueToInt32 ; 464 -JS_FinishArenaPool ; 453 -js_NewTryNote ; 441 -js_strtointeger ; 437 -JS_vsmprintf ; 428 -JS_DHashTableInit ; 423 -JS_DHashAllocTable ; 423 -JS_DHashGetStubOps ; 423 -JS_NewDHashTable ; 423 -JS_DHashTableDestroy ; 423 -JS_DHashFreeTable ; 423 -JS_DHashTableFinish ; 423 -js_EmitBreak ; 412 -js_GetAttributes ; 412 -JS_DefineConstDoubles ; 407 -JS_ArenaGrow ; 374 -js_AtomizeInt ; 372 -JS_SetParent ; 345 -JS_CloneFunctionObject ; 343 -JS_IsNativeFrame ; 343 -JS_ReportErrorNumber ; 340 -js_ErrorToException ; 340 -js_ReportErrorNumberVA ; 340 -js_GetErrorMessage ; 340 -js_ExpandErrorArguments ; 340 -js_ReportUncaughtException ; 315 -JS_IsExceptionPending ; 315 -js_ReportErrorAgain ; 315 -js_ErrorFromException ; 315 -JS_LookupUCProperty ; 307 -JS_InitArenaPool ; 293 -PRMJ_Now ; 262 -DllMain@12 ; 235 -JS_ExecuteScript ; 232 -JS_GetFrameFunction ; 226 -PRMJ_LocalGMTDifference ; 175 -JS_GetConstructor ; 175 -JS_SetGlobalObject ; 164 -js_LockGCThing ; 155 -js_NewRegExpObject ; 152 -js_NewRegExp ; 152 -js_InitObjectClass ; 131 -js_InitFunctionClass ; 131 -js_EmitN ; 128 -JS_ArenaFinish ; 124 -js_GC ; 124 -js_SweepAtomState ; 124 -js_MarkAtomState ; 124 -JS_ArenaRealloc ; 124 -js_ForceGC ; 124 -js_FlushPropertyCache ; 122 -js_InitNumberClass ; 114 -JS_smprintf ; 112 -js_DoubleToECMAInt32 ; 112 -js_ValueToECMAInt32 ; 111 -JS_ValueToECMAInt32 ; 111 -JS_SetContextPrivate ; 109 -PRMJ_DSTOffset ; 108 -js_Clear ; 105 -JS_ClearScope ; 105 -JS_NewScriptObject ; 104 -JS_smprintf_free ; 104 -JS_ConvertValue ; 99 -js_GetSrcNote ; 98 -JS_ValueToECMAUint32 ; 93 -js_ValueToECMAUint32 ; 93 -js_printf ; 93 -js_DoubleToECMAUint32 ; 93 -js_DestroyRegExp ; 89 -js_UnlockGCThing ; 89 -js_TryValueOf ; 87 -js_NewSrcNote3 ; 86 -JS_ConvertStub ; 81 -JS_SetPendingException ; 80 -js_InitStringClass ; 79 -JS_GC ; 78 -js_InitArrayClass ; 74 -js_InitDateClass ; 67 -JS_NewContext ; 64 -JS_AddArgumentFormatter ; 64 -js_InitContextForLocking ; 64 -js_NewContext ; 64 -JS_SetBranchCallback ; 64 -JS_ClearRegExpStatics ; 64 -js_InitRegExpStatics ; 64 -js_InitCallClass ; 63 -js_InitRegExpClass ; 61 -js_Enumerate ; 58 -JS_DestroyContext ; 46 -js_DestroyContext ; 46 -js_FreeRegExpStatics ; 46 -js_InitScanner ; 39 -js_NewPrinter ; 36 -js_DestroyPrinter ; 36 -js_GetPrinterOutput ; 36 -JS_FreeArenaPool ; 36 -js_DecompileCode ; 34 -js_EmitContinue ; 33 -js_CheckAccess ; 30 -js_DecompileValueGenerator ; 28 -js_InitMathClass ; 27 -js_InitExceptionClasses ; 25 -js_NewArrayObject ; 24 -js_InitArgumentsClass ; 21 -js_puts ; 20 -js_InitBooleanClass ; 19 -JS_InitStandardClasses ; 19 -js_InitScriptClass ; 19 -js_obj_toString ; 15 -js_GetArgsValue ; 14 -js_GetArgsObject ; 14 -js_AtomizeDouble ; 12 -JS_DestroyIdArray ; 11 -js_NewIdArray ; 11 -JS_GetElement ; 11 -JS_EvaluateScript ; 9 -JS_EvaluateUCScript ; 9 -JS_DecompileFunction ; 8 -js_DecompileFunction ; 8 -JS_NewString ; 8 -js_SetStringBytes ; 8 -JS_GetArrayLength ; 7 -JS_NewArrayObject ; 7 -JS_IsArrayObject ; 7 -JS_ValueToObject ; 7 -JS_DefineElement ; 6 -js_DecompileScript ; 6 -JS_PushArguments ; 4 -JS_PopArguments ; 4 -JS_PushArgumentsVA ; 4 -js_PutArgsObject ; 2 -JS_SetGCCallbackRT ; 2 -JS_Init ; 1 -js_SetupLocks ; 1 -js_InitRuntimeNumberState ; 1 -js_InitRuntimeStringState ; 1 -js_InitLock ; 1 -js_InitGC ; 1 -js_InitAtomState ; 1 -js_InitStringGlobals ; 1 diff --git a/test.py b/test.py index 23a0432..8175b0e 100755 --- a/test.py +++ b/test.py @@ -59,7 +59,7 @@ def lint_error(path, line, col, errname, errdesc): if warning in expected_warnings: expected_warnings.remove(warning) else: - unexpected_warnings.append(warning) + unexpected_warnings.append(warning + (errdesc,)) javascriptlint.lint.lint_files([path], lint_error, conf=conf) @@ -70,8 +70,8 @@ def lint_error(path, line, col, errname, errdesc): errors.append('\tline %i: %s' % (line+1, warning)) if unexpected_warnings: errors.append('Unexpected warnings:') - for line, warning in unexpected_warnings: - errors.append('\tline %i: %s' % (line+1, warning)) + for line, warning, errdesc in unexpected_warnings: + errors.append('\tline %i: %s/%s' % (line+1, warning, errdesc)) if errors: raise TestError, '\n'.join(errors) diff --git a/tests/control_comments/conf-version.js b/tests/control_comments/conf-version.js index f15dddb..a75e029 100644 --- a/tests/control_comments/conf-version.js +++ b/tests/control_comments/conf-version.js @@ -1,4 +1,5 @@ /*conf:+default-version text/javascript;version=1.7*/ function default_version() { - yield true; + // TODO: Support js1.7 + yield true; /*warning:semi_before_stmnt*/ } diff --git a/tests/html/e4x.html b/tests/html/e4x.html index 0a9ac69..c916d14 100644 --- a/tests/html/e4x.html +++ b/tests/html/e4x.html @@ -10,16 +10,16 @@ ? /*warning:syntax_error*/ -->; - diff --git a/tests/html/script_tag_in_js_comment.html b/tests/html/script_tag_in_js_comment.html index f4b07b7..e103787 100644 --- a/tests/html/script_tag_in_js_comment.html +++ b/tests/html/script_tag_in_js_comment.html @@ -3,7 +3,7 @@ JavaScript Lint Test Page end tag */ /*warning:unterminated_comment*/ + /* alert the end tag */ //--> diff --git a/tests/warnings/identifier_hides_another.js b/tests/warnings/identifier_hides_another.js index 1c85929..a8d48ed 100644 --- a/tests/warnings/identifier_hides_another.js +++ b/tests/warnings/identifier_hides_another.js @@ -19,10 +19,10 @@ function identifier_hides_another(arg_hides_arg, /*jsl:declare var_hides_declare*/ /*jsl:declare declare_hides_declare*/ - function inner(arg_hides_arg, - arg_hides_function, - arg_hides_var, - arg_hides_declare) { /*warning:identifier_hides_another*//*warning:identifier_hides_another*//*warning:identifier_hides_another*//*warning:identifier_hides_another*/ + function inner(arg_hides_arg, /*warning:identifier_hides_another*/ + arg_hides_function, /*warning:identifier_hides_another*/ + arg_hides_var, /*warning:identifier_hides_another*/ + arg_hides_declare) { /*warning:identifier_hides_another*/ function function_hides_arg() { return true; } /*warning:identifier_hides_another*/ function function_hides_function() { return true; } /*warning:identifier_hides_another*/ diff --git a/tests/warnings/spidermonkey/bad_backref.js b/tests/warnings/spidermonkey/bad_backref.js index 5822b98..e994614 100644 --- a/tests/warnings/spidermonkey/bad_backref.js +++ b/tests/warnings/spidermonkey/bad_backref.js @@ -1,5 +1,5 @@ /*jsl:option explicit*/ function bad_backref() { /* illegal - one 1 backreference */ - return /(.)\2/; /*warning:bad_backref*/ + return /(.)\2/; /* TODO: Implement regex warnings. */ } diff --git a/tests/warnings/spidermonkey/deprecated_usage.js b/tests/warnings/spidermonkey/deprecated_usage.js deleted file mode 100644 index b66f422..0000000 --- a/tests/warnings/spidermonkey/deprecated_usage.js +++ /dev/null @@ -1,11 +0,0 @@ -/*jsl:option explicit*/ -function deprecated_usage() { - /* illegal - getter/setter is deprecated */ - - Array.bogon getter = function () { /*warning:deprecated_usage*/ - return ""; - }; - Array.bogon setter = function (o) { /*warning:deprecated_usage*/ - this.push(o); - }; -} diff --git a/tests/warnings/spidermonkey/invalid_backref.js b/tests/warnings/spidermonkey/invalid_backref.js index 9da228b..cb3a039 100644 --- a/tests/warnings/spidermonkey/invalid_backref.js +++ b/tests/warnings/spidermonkey/invalid_backref.js @@ -1,5 +1,5 @@ /*jsl:option explicit*/ function invalid_backref() { /* illegal - \0 is not a valid regex backreference */ - return /\0/; /*warning:invalid_backref*/ + return /\0/; /* TODO: Implement regex warnings. */ } diff --git a/tests/warnings/want_assign_or_call.js b/tests/warnings/want_assign_or_call.js index 42bf5b8..16390db 100644 --- a/tests/warnings/want_assign_or_call.js +++ b/tests/warnings/want_assign_or_call.js @@ -16,7 +16,7 @@ function want_assign_or_call() { function() { /*warning:want_assign_or_call*/ return 42; - } + }; delete a; From 960190acf1a70b548fae8787ce1b6ff86adc5057 Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Sat, 28 Sep 2013 03:38:24 +0000 Subject: [PATCH 04/59] r305: python re-write doesn't support option explicit Fix traceback mentioned in comment regarding invalid control comments --- javascriptlint/lint.py | 43 +++++++++++----------- tests/control_comments/control_comments.js | 10 +++++ 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/javascriptlint/lint.py b/javascriptlint/lint.py index 3966539..4f34fc1 100644 --- a/javascriptlint/lint.py +++ b/javascriptlint/lint.py @@ -56,28 +56,27 @@ def _parse_control_comment(comment): control_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), - 'unused': (True), - 'content-type': (True), - } - if control_comment.lower() in control_comments: - keyword = control_comment.lower() - else: - keyword = control_comment.lower().split()[0] - if not keyword in control_comments: - return None - - parms = control_comment[len(keyword):].strip() - return (comment, keyword, parms) + control_comment = control_comment.lower().rstrip() + + keywords = ( + 'ignoreall', + 'ignore', + 'end', + 'option explicit', + 'import', + 'fallthru', + 'pass', + 'declare', + 'unused', + 'content-type', + ) + for keyword in keywords: + # The keyword must either match or be separated by a space. + if control_comment == keyword or \ + (control_comment.startswith(keyword) and \ + control_comment[len(keyword)].isspace()): + parms = control_comment[len(keyword):].strip() + return (comment, keyword, parms.strip()) class Scope: """ Outer-level scopes will never be associated with a node. diff --git a/tests/control_comments/control_comments.js b/tests/control_comments/control_comments.js index 93585a6..bc164a1 100644 --- a/tests/control_comments/control_comments.js +++ b/tests/control_comments/control_comments.js @@ -29,5 +29,15 @@ function control_comments() { /* illegal - don't forget to end */ /*jsl:ignore*/ /*warning:mismatch_ctrl_comments*/ + + // The following are illegal. Make sure jsl doesn't choke. + /*jsl:*/ /*warning:jsl_cc_not_understood*/ + if (a) + { + /*jsl:pass */ + } + /*jsl:ignoreal*/ /*warning:jsl_cc_not_understood*/ + /*jsl:declarebogus*/ /*warning:jsl_cc_not_understood*/ + /*jsl:declare bogus */ } From f1030a0538538132a04c241fbaae9228568859fd Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Fri, 6 May 2016 18:10:40 -0700 Subject: [PATCH 05/59] r306: jsl: declarations should allow for surrounding spaces --- javascriptlint/lint.py | 12 +++++------- tests/control_comments/control_comments.js | 6 ++++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/javascriptlint/lint.py b/javascriptlint/lint.py index 4f34fc1..a1dbee8 100644 --- a/javascriptlint/lint.py +++ b/javascriptlint/lint.py @@ -48,15 +48,13 @@ def _find_functions(node): def _parse_control_comment(comment): """ Returns None or (keyword, parms) """ - atom = comment.atom.strip() - atom_lower = atom.lower() - if atom_lower.startswith('jsl:'): - control_comment = atom[4:] - elif atom.startswith('@') and atom.endswith('@'): - control_comment = atom[1:-1] + comment_atom = comment.atom.lower().strip() + if comment_atom.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_comment = control_comment.lower().rstrip() keywords = ( 'ignoreall', diff --git a/tests/control_comments/control_comments.js b/tests/control_comments/control_comments.js index bc164a1..666b7bb 100644 --- a/tests/control_comments/control_comments.js +++ b/tests/control_comments/control_comments.js @@ -32,10 +32,12 @@ function control_comments() { // The following are illegal. Make sure jsl doesn't choke. /*jsl:*/ /*warning:jsl_cc_not_understood*/ - if (a) - { + if (a) { /*jsl:pass */ } + else if (b) { + /* jsl:pass */ //allow spaces on both sides + } /*jsl:ignoreal*/ /*warning:jsl_cc_not_understood*/ /*jsl:declarebogus*/ /*warning:jsl_cc_not_understood*/ /*jsl:declare bogus */ From 90aea4d4bde95e8e53bdb6dbb6c9d62623059b8f Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Sat, 28 Sep 2013 03:50:06 +0000 Subject: [PATCH 06/59] r307: Problem with identifiers as object properties Add test case to confirm this works. --- tests/bugs/sf-43-reserved_object_def.js | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/bugs/sf-43-reserved_object_def.js diff --git a/tests/bugs/sf-43-reserved_object_def.js b/tests/bugs/sf-43-reserved_object_def.js new file mode 100644 index 0000000..3a3399e --- /dev/null +++ b/tests/bugs/sf-43-reserved_object_def.js @@ -0,0 +1,7 @@ +// Test future keywords as object properties. +function f() { + var record = { + class: 'A' + }; + return record.class; +} From 4771f1828e49f0e04f44f4c811f45dbdaff7330a Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Sat, 28 Sep 2013 04:08:31 +0000 Subject: [PATCH 07/59] r308: Failure when input file is not utf-8 encoded Allow passing the input file encoding on the command line. --- javascriptlint/conf.py | 2 +- javascriptlint/fs.py | 4 ++-- javascriptlint/jsl.py | 14 ++++++++------ javascriptlint/lint.py | 12 ++++++------ test.py | 2 +- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/javascriptlint/conf.py b/javascriptlint/conf.py index 66c31dd..a666e0b 100644 --- a/javascriptlint/conf.py +++ b/javascriptlint/conf.py @@ -187,7 +187,7 @@ def __init__(self): def loadfile(self, path): path = os.path.abspath(path) - conf = fs.readfile(path) + conf = fs.readfile(path, 'utf-8') try: self.loadtext(conf, dir=os.path.dirname(path)) except ConfError, error: diff --git a/javascriptlint/fs.py b/javascriptlint/fs.py index cc05dbb..0cf65f8 100644 --- a/javascriptlint/fs.py +++ b/javascriptlint/fs.py @@ -2,8 +2,8 @@ import codecs import os -def readfile(path): - file = codecs.open(path, 'r', 'utf-8') +def readfile(path, encoding): + file = codecs.open(path, 'r', encoding) contents = file.read() if contents and contents[0] == unicode(codecs.BOM_UTF8, 'utf8'): contents = contents[1:] diff --git a/javascriptlint/jsl.py b/javascriptlint/jsl.py index 10d69d8..f498914 100755 --- a/javascriptlint/jsl.py +++ b/javascriptlint/jsl.py @@ -20,17 +20,17 @@ 'errors': 0 } -def _dump(paths): +def _dump(paths, encoding): for path in paths: - script = fs.readfile(path) + script = fs.readfile(path, encoding) jsparse.dump_tree(script) -def _lint(paths, conf_, printpaths): +def _lint(paths, conf_, printpaths, encoding): def lint_error(path, line, col, errname, errdesc): _lint_results['warnings'] = _lint_results['warnings'] + 1 print util.format_error(conf_['output-format'], path, line, col, errname, errdesc) - lint.lint_files(paths, lint_error, conf=conf_, printpaths=printpaths) + lint.lint_files(paths, lint_error, encoding, conf=conf_, printpaths=printpaths) def _resolve_paths(path, recurse): # Build a list of directories @@ -97,6 +97,8 @@ def main(): help="suppress lint summary") add("--help:conf", dest="showdefaultconf", action="store_true", default=False, help="display the default configuration file") + add("--encoding", dest="encoding", metavar="ENCODING", default="utf-8", + help="encoding for input file(s)") parser.set_defaults(verbosity=1) options, args = parser.parse_args() @@ -138,9 +140,9 @@ def main(): else: paths.append(arg) if options.dump: - profile_func(_dump, paths) + profile_func(_dump, paths, options.encoding) else: - profile_func(_lint, paths, conf_, options.printlisting) + profile_func(_lint, paths, conf_, options.printlisting, options.encoding) if options.printsummary: print '\n%i error(s), %i warnings(s)' % (_lint_results['errors'], diff --git a/javascriptlint/lint.py b/javascriptlint/lint.py index a1dbee8..65736af 100644 --- a/javascriptlint/lint.py +++ b/javascriptlint/lint.py @@ -286,14 +286,14 @@ def _findhtmlscripts(contents, default_version): else: assert False, 'Invalid internal tag type %s' % tag['type'] -def lint_files(paths, lint_error, conf=conf.Conf(), printpaths=True): - def lint_file(path, kind, jsversion): +def lint_files(paths, lint_error, encoding, conf=conf.Conf(), printpaths=True): + def lint_file(path, kind, jsversion, encoding): def import_script(import_path, jsversion): # The user can specify paths using backslashes (such as when # linting Windows scripts on a posix environment. import_path = import_path.replace('\\', os.sep) import_path = os.path.join(os.path.dirname(path), import_path) - return lint_file(import_path, 'js', jsversion) + return lint_file(import_path, 'js', jsversion, encoding) def _lint_error(*args): return lint_error(normpath, *args) @@ -302,7 +302,7 @@ def _lint_error(*args): return lint_cache[normpath] if printpaths: print normpath - contents = fs.readfile(path) + contents = fs.readfile(path, encoding) lint_cache[normpath] = _Script() script_parts = [] @@ -334,9 +334,9 @@ def _lint_error(*args): for path in paths: ext = os.path.splitext(path)[1] if ext.lower() in ['.htm', '.html']: - lint_file(path, 'html', None) + lint_file(path, 'html', None, encoding) else: - lint_file(path, 'js', None) + lint_file(path, 'js', None, encoding) def _lint_script_part(scriptpos, jsversion, script, script_cache, conf, ignores, report_native, report_lint, import_callback): diff --git a/test.py b/test.py index 8175b0e..6cf0768 100755 --- a/test.py +++ b/test.py @@ -61,7 +61,7 @@ def lint_error(path, line, col, errname, errdesc): else: unexpected_warnings.append(warning + (errdesc,)) - javascriptlint.lint.lint_files([path], lint_error, conf=conf) + javascriptlint.lint.lint_files([path], lint_error, 'utf-8', conf=conf) errors = [] if expected_warnings: From 77c469a1c9c092630abae4baa77c22ecde89accd Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Sat, 28 Sep 2013 04:17:38 +0000 Subject: [PATCH 08/59] r309: require variable names in "for .. in" statements --- javascriptlint/warnings.py | 8 ++++++++ tests/warnings/for_in_missing_identifier.js | 12 ++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 tests/warnings/for_in_missing_identifier.js diff --git a/javascriptlint/warnings.py b/javascriptlint/warnings.py index f69c2b3..7e93659 100644 --- a/javascriptlint/warnings.py +++ b/javascriptlint/warnings.py @@ -100,6 +100,7 @@ def _get_assigned_lambda(node): 'anon_no_return_value': 'anonymous function does not always return value', 'unsupported_version': 'JavaScript {version} is not supported', 'incorrect_version': 'Expected /*jsl:content-type*/ control comment. The script was parsed with the wrong version.', + 'for_in_missing_identifier': 'for..in should have identifier on left side', } errors = { @@ -587,6 +588,13 @@ def anon_no_return_value(node): if not node.fn_name: _check_return_value(node) +@lookfor((tok.FOR, op.FORIN)) +def for_in_missing_identifier(node): + assert node.kids[0].kind == tok.IN + left, right = node.kids[0].kids + if not left.kind in (tok.VAR, tok.NAME): + raise LintWarning, left + @lookfor() def mismatch_ctrl_comments(node): pass diff --git a/tests/warnings/for_in_missing_identifier.js b/tests/warnings/for_in_missing_identifier.js new file mode 100644 index 0000000..983f5ce --- /dev/null +++ b/tests/warnings/for_in_missing_identifier.js @@ -0,0 +1,12 @@ +/*jsl:option explicit*/ +function for_in_missing_identifier(o) { + var prop; + for (prop in o) + o[prop]++; + + for (var prop2 in o) + o[prop2]++; + + for (!prop in o) /*warning:for_in_missing_identifier*/ + o[prop]++; +} From eaac3078aec19c0a2fcb8ca6a0745c7d7867ecc3 Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Sat, 28 Sep 2013 04:27:44 +0000 Subject: [PATCH 09/59] r310: Fix traceback when dumping a script with syntax errors --- javascriptlint/jsparse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascriptlint/jsparse.py b/javascriptlint/jsparse.py index a1fc1c8..a5ee4ac 100644 --- a/javascriptlint/jsparse.py +++ b/javascriptlint/jsparse.py @@ -119,7 +119,7 @@ def _dump_node(node, depth=0): _dump_node(node, depth+1) def dump_tree(script): - def error_callback(line, col, msg): + def error_callback(line, col, msg, msg_args): print '(%i, %i): %s', (line, col, msg) node = parse(script, None, error_callback) _dump_node(node) From a8e6817414a6772157314611c4c497a110d35ea4 Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Sat, 28 Sep 2013 04:30:11 +0000 Subject: [PATCH 10/59] r311: Show test results when complete. --- test.py | 5 +++++ tests/warnings/for_in_missing_identifier.js | 16 ++++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/test.py b/test.py index 6cf0768..aa3e689 100755 --- a/test.py +++ b/test.py @@ -100,6 +100,11 @@ def main(): except TestError, error: haderrors = True print error + + if haderrors: + print '\nOne or more tests failed!' + else: + print '\nAll tests passed successfully.' sys.exit(haderrors) if __name__ == '__main__': diff --git a/tests/warnings/for_in_missing_identifier.js b/tests/warnings/for_in_missing_identifier.js index 983f5ce..429c59f 100644 --- a/tests/warnings/for_in_missing_identifier.js +++ b/tests/warnings/for_in_missing_identifier.js @@ -1,12 +1,12 @@ /*jsl:option explicit*/ function for_in_missing_identifier(o) { - var prop; - for (prop in o) - o[prop]++; - - for (var prop2 in o) - o[prop2]++; + var prop; + for (prop in o) + o[prop]++; + + for (var prop2 in o) + o[prop2]++; - for (!prop in o) /*warning:for_in_missing_identifier*/ - o[prop]++; + for (!prop in o) /*warning:for_in_missing_identifier*/ + o[prop]++; } From 1175dd48c0ca35e964990c4bb718d7dcb738817d Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Sat, 28 Sep 2013 04:44:05 +0000 Subject: [PATCH 11/59] r312: case with continue results in 'missing break statement' Treat CONTINUE as an exit point --- javascriptlint/warnings.py | 2 ++ tests/warnings/missing_break.js | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/javascriptlint/warnings.py b/javascriptlint/warnings.py index 7e93659..a613ffd 100644 --- a/javascriptlint/warnings.py +++ b/javascriptlint/warnings.py @@ -202,6 +202,8 @@ def break_to_none(node): exit_points.add(None) elif node.kind == tok.BREAK: exit_points = set([node]) + elif node.kind == tok.CONTINUE: + exit_points = set([node]) elif node.kind == tok.WITH: exit_points = _get_exit_points(node.kids[-1]) elif node.kind == tok.RETURN: diff --git a/tests/warnings/missing_break.js b/tests/warnings/missing_break.js index 4488a1e..7663d56 100644 --- a/tests/warnings/missing_break.js +++ b/tests/warnings/missing_break.js @@ -102,5 +102,18 @@ function missing_break() { break; } + for (;;) { + switch(i) { + case 1: + i++; + continue; + case 2: + if (i) + continue; + default: /*warning:missing_break*/ + break; + } + } + return ""; } From 39d8b598eb752a0ed195955dbf505219ec9e6639 Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Sat, 28 Sep 2013 04:48:18 +0000 Subject: [PATCH 12/59] r313: Crashing on valid regex string Add a test case to confirm this works. --- tests/html/script_tag_in_regexp.html | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/html/script_tag_in_regexp.html diff --git a/tests/html/script_tag_in_regexp.html b/tests/html/script_tag_in_regexp.html new file mode 100644 index 0000000..2c74695 --- /dev/null +++ b/tests/html/script_tag_in_regexp.html @@ -0,0 +1,13 @@ + + + + JavaScript Lint Test Page + + + + + From 62297d344c26935e367e2bd1157972f475b931f1 Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Sat, 28 Sep 2013 04:50:25 +0000 Subject: [PATCH 13/59] r314: Crashing on valid regex string Additional test case --- tests/html/script_tag_in_regexp.js | 1 + 1 file changed, 1 insertion(+) create mode 100644 tests/html/script_tag_in_regexp.js diff --git a/tests/html/script_tag_in_regexp.js b/tests/html/script_tag_in_regexp.js new file mode 100644 index 0000000..8151b4c --- /dev/null +++ b/tests/html/script_tag_in_regexp.js @@ -0,0 +1 @@ +var re = //g; From 3b3f2b3aaf795f68120ea8c17122bfdffb0f788b Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Sat, 28 Sep 2013 05:00:30 +0000 Subject: [PATCH 14/59] r315: Warn against multiple useless comparison (e.g. a == a == b) --- javascriptlint/warnings.py | 7 ++++--- tests/warnings/useless_comparison.js | 8 ++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/javascriptlint/warnings.py b/javascriptlint/warnings.py index a613ffd..65df77b 100644 --- a/javascriptlint/warnings.py +++ b/javascriptlint/warnings.py @@ -14,6 +14,7 @@ def warning_name(node): if questionable: raise LintWarning, node """ +import itertools import re import sys import types @@ -287,9 +288,9 @@ def with_statement(node): @lookfor(tok.EQOP,tok.RELOP) def useless_comparison(node): - lvalue, rvalue = node.kids - if lvalue.is_equivalent(rvalue): - raise LintWarning, node + for lvalue, rvalue in itertools.combinations(node.kids, 2): + if lvalue.is_equivalent(rvalue): + raise LintWarning, node @lookfor((tok.COLON, op.NAME)) def use_of_label(node): diff --git a/tests/warnings/useless_comparison.js b/tests/warnings/useless_comparison.js index 20a928e..cf14da1 100644 --- a/tests/warnings/useless_comparison.js +++ b/tests/warnings/useless_comparison.js @@ -52,4 +52,12 @@ function useless_comparison() { if (useless_comparison() == useless_comparison()) { return; } + + // Test multiple comparisons. + if (i == i == 3) /*warning:useless_comparison*/ + return; + if (i == 3 == i) /*warning:useless_comparison*/ + return; + if (i == 3 == j == 3) /*warning:useless_comparison*/ + return;} } From fa7c9ddf20167659a1b0a601a7efd933ec5e7383 Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Sat, 28 Sep 2013 19:39:16 +0000 Subject: [PATCH 15/59] r316: Fix unexpected-eof warning. --- javascriptlint/warnings.py | 1 + jsengine/__init__.py | 2 +- jsengine/parser/__init__.py | 4 ++-- jsengine/tokenizer/__init__.py | 2 +- tests/warnings/useless_comparison.js | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/javascriptlint/warnings.py b/javascriptlint/warnings.py index 65df77b..9a406f1 100644 --- a/javascriptlint/warnings.py +++ b/javascriptlint/warnings.py @@ -110,6 +110,7 @@ def _get_assigned_lambda(node): 'syntax_error': 'syntax error', 'expected_tok': 'expected token: {token}', 'unexpected_char': 'unexpected character: {char}', + 'unexpected_eof': 'unexpected end of file', } def format_error(errname, **errargs): diff --git a/jsengine/__init__.py b/jsengine/__init__.py index b60947b..dbe4dd1 100644 --- a/jsengine/__init__.py +++ b/jsengine/__init__.py @@ -1,7 +1,7 @@ # vim: sw=4 ts=4 et _MESSAGES = ( - 'eof', + 'unexpected_eof', 'semi_before_stmnt', 'syntax_error', 'unterminated_comment', diff --git a/jsengine/parser/__init__.py b/jsengine/parser/__init__.py index e2ca85c..c8c82f3 100644 --- a/jsengine/parser/__init__.py +++ b/jsengine/parser/__init__.py @@ -781,7 +781,7 @@ def _statement(t): elif x.tok == tok.TRY: return _try_statement(t) elif x.tok == tok.EOF: - raise JSSyntaxError(x.startpos, 'eof') + raise JSSyntaxError(x.startpos, 'unexpected_eof') elif x.tok == tok.FUNCTION: return _function_declaration(t, op.CLOSURE) #TODO: warn, since this is not reliable @@ -844,7 +844,7 @@ def is_compilable_unit(script, jsversion): try: parsestring(script) except JSSyntaxError as error: - return error.msg not in ('eof', 'unterminated_comment') + return error.msg not in ('unexpected_eof', 'unterminated_comment') return True class TestParser(unittest.TestCase): diff --git a/jsengine/tokenizer/__init__.py b/jsengine/tokenizer/__init__.py index f4e1057..cc7734a 100644 --- a/jsengine/tokenizer/__init__.py +++ b/jsengine/tokenizer/__init__.py @@ -176,7 +176,7 @@ def readchr(self): if self._pos < len(self._content): self._pos += 1 return self._content[self._pos - 1] - raise JSSyntaxError(self.getpos(-1), 'eof') + raise JSSyntaxError(self.getpos(-1), 'unexpected_eof') def readif(self, len_, seq): s = self.peekif(len_, seq) diff --git a/tests/warnings/useless_comparison.js b/tests/warnings/useless_comparison.js index cf14da1..b6f6656 100644 --- a/tests/warnings/useless_comparison.js +++ b/tests/warnings/useless_comparison.js @@ -59,5 +59,5 @@ function useless_comparison() { if (i == 3 == i) /*warning:useless_comparison*/ return; if (i == 3 == j == 3) /*warning:useless_comparison*/ - return;} + return; } From f9d3e5938558241a16a1d34fb39674b9f505b7cc Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Mon, 30 Sep 2013 20:53:58 +0000 Subject: [PATCH 16/59] r317: Fix number parsing. --- jsengine/structs.py | 7 ++++++- jsengine/tokenizer/__init__.py | 29 ++++++++++++++++------------ tests/warnings/useless_comparison.js | 6 ++++++ 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/jsengine/structs.py b/jsengine/structs.py index 88b14af..3b0a180 100644 --- a/jsengine/structs.py +++ b/jsengine/structs.py @@ -125,7 +125,12 @@ def __init__(self, kind_, op_, start_pos, end_pos, atom, kids, else: assert fn_args is None if self.kind == kind.NUMBER: - self.dval = float(self.atom) + if self.atom.lower().startswith('0x'): + self.dval = int(self.atom, 16) + elif self.atom.startswith('0') and self.atom.isdigit(): + self.dval = int(self.atom, 8) + else: + self.dval = float(self.atom) def start_pos(self): return self.startpos diff --git a/jsengine/tokenizer/__init__.py b/jsengine/tokenizer/__init__.py index cc7734a..86e4d41 100644 --- a/jsengine/tokenizer/__init__.py +++ b/jsengine/tokenizer/__init__.py @@ -378,26 +378,31 @@ def _next(self, parse_regexp=False): s = c # TODO stream.watch_reads() if c == '0' and stream.readif(1, 'xX'): + # Hex while stream.readif(1, _HEX_DIGITS): pass - return Token(tok.NUMBER, atom=stream.get_watched_reads()) - - if c != '.': + elif c == '0' and stream.readif(1, _DIGITS): + # Octal while stream.readif(1, _DIGITS): pass - stream.readif(1, '.') - - while stream.readif(1, _DIGITS): - pass + else: + # Decimal + if c != '.': + while stream.readif(1, _DIGITS): + pass + stream.readif(1, '.') - if stream.readif(1, 'eE'): - stream.readif(1, '+-') - stream.require(_DIGITS) while stream.readif(1, _DIGITS): pass - if stream.peekchr(_IDENT): - return Token(tok.ERROR) + if stream.readif(1, 'eE'): + stream.readif(1, '+-') + stream.require(_DIGITS) + while stream.readif(1, _DIGITS): + pass + + if stream.peekchr(_IDENT): + return Token(tok.ERROR) atom = s + stream.get_watched_reads() return Token(tok.NUMBER, atom=atom) diff --git a/tests/warnings/useless_comparison.js b/tests/warnings/useless_comparison.js index b6f6656..04f2c38 100644 --- a/tests/warnings/useless_comparison.js +++ b/tests/warnings/useless_comparison.js @@ -60,4 +60,10 @@ function useless_comparison() { return; if (i == 3 == j == 3) /*warning:useless_comparison*/ return; + + // Test bases + if (010 == 8) /*warning:useless_comparison*/ /*warning:octal_number*/ + return; + if (0xA == 10) /*warning:useless_comparison*/ + return; } From 3e9a87ec427e3d37f75967f0919f71164b9afc97 Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Mon, 30 Sep 2013 20:59:56 +0000 Subject: [PATCH 17/59] r318: Fix case preservation in control comments. --- javascriptlint/lint.py | 8 ++++---- tests/warnings/unreferenced_identifier.js | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/javascriptlint/lint.py b/javascriptlint/lint.py index 65736af..fb1edb9 100644 --- a/javascriptlint/lint.py +++ b/javascriptlint/lint.py @@ -48,8 +48,8 @@ def _find_functions(node): def _parse_control_comment(comment): """ Returns None or (keyword, parms) """ - comment_atom = comment.atom.lower().strip() - if comment_atom.startswith('jsl:'): + comment_atom = comment.atom.strip() + 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] @@ -70,8 +70,8 @@ def _parse_control_comment(comment): ) for keyword in keywords: # The keyword must either match or be separated by a space. - if control_comment == keyword or \ - (control_comment.startswith(keyword) and \ + if control_comment.lower() == keyword or \ + (control_comment.lower().startswith(keyword) and \ control_comment[len(keyword)].isspace()): parms = control_comment[len(keyword):].strip() return (comment, keyword, parms.strip()) diff --git a/tests/warnings/unreferenced_identifier.js b/tests/warnings/unreferenced_identifier.js index 1ef098a..9244cb3 100644 --- a/tests/warnings/unreferenced_identifier.js +++ b/tests/warnings/unreferenced_identifier.js @@ -98,5 +98,9 @@ function unreferenced_identifier() { } } + function test_unused_camel_case(CamelCaseParm) { /*warning:unreferenced_function*/ + /*jsl:unused CamelCaseParm*/ + } + return get_callback(42); } From 75c073246c191b68ea84f2dc343a8a1cf8574678 Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Mon, 30 Sep 2013 21:03:23 +0000 Subject: [PATCH 18/59] r319: Allow chained assigns. --- javascriptlint/warnings.py | 3 ++- tests/warnings/equal_as_assign.js | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/javascriptlint/warnings.py b/javascriptlint/warnings.py index 9a406f1..dc49a1e 100644 --- a/javascriptlint/warnings.py +++ b/javascriptlint/warnings.py @@ -324,7 +324,8 @@ def assign_to_function_call(node): @lookfor(tok.ASSIGN) def equal_as_assign(node): - if not node.parent.kind in (tok.SEMI, tok.RESERVED, tok.RP, tok.COMMA): + if not node.parent.kind in (tok.SEMI, tok.RESERVED, tok.RP, tok.COMMA, + tok.ASSIGN): raise LintWarning, node @lookfor(tok.IF) diff --git a/tests/warnings/equal_as_assign.js b/tests/warnings/equal_as_assign.js index df43778..17182b1 100644 --- a/tests/warnings/equal_as_assign.js +++ b/tests/warnings/equal_as_assign.js @@ -4,4 +4,7 @@ function equal_as_assign() { while (a = b) { /*warning:equal_as_assign*/ a++; } + + var c; + a = b = c; } From 81365c1e9d3dc6f8dad30465449ecaec96e38167 Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Mon, 30 Sep 2013 21:11:45 +0000 Subject: [PATCH 19/59] r320: Allow chained assignments in VAR statements. --- javascriptlint/warnings.py | 4 ++++ tests/warnings/equal_as_assign.js | 3 +++ 2 files changed, 7 insertions(+) diff --git a/javascriptlint/warnings.py b/javascriptlint/warnings.py index dc49a1e..a750237 100644 --- a/javascriptlint/warnings.py +++ b/javascriptlint/warnings.py @@ -324,6 +324,10 @@ def assign_to_function_call(node): @lookfor(tok.ASSIGN) def equal_as_assign(node): + # Allow in VAR statements. + if node.parent.parent and node.parent.parent.kind == tok.VAR: + return + if not node.parent.kind in (tok.SEMI, tok.RESERVED, tok.RP, tok.COMMA, tok.ASSIGN): raise LintWarning, node diff --git a/tests/warnings/equal_as_assign.js b/tests/warnings/equal_as_assign.js index 17182b1..9e234e7 100644 --- a/tests/warnings/equal_as_assign.js +++ b/tests/warnings/equal_as_assign.js @@ -7,4 +7,7 @@ function equal_as_assign() { var c; a = b = c; + + var x, y, z; + var w = x = y = z; } From 8b1817626b0e889ab067f61b5ab925c7bb121b35 Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Mon, 30 Sep 2013 21:19:27 +0000 Subject: [PATCH 20/59] r321: Don't warning against increment/decrement assignments in expressions. --- javascriptlint/warnings.py | 2 +- tests/warnings/equal_as_assign.js | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/javascriptlint/warnings.py b/javascriptlint/warnings.py index a750237..b16a6c3 100644 --- a/javascriptlint/warnings.py +++ b/javascriptlint/warnings.py @@ -322,7 +322,7 @@ def assign_to_function_call(node): if kid.kind == tok.LP: raise LintWarning, node -@lookfor(tok.ASSIGN) +@lookfor((tok.ASSIGN, None)) def equal_as_assign(node): # Allow in VAR statements. if node.parent.parent and node.parent.parent.kind == tok.VAR: diff --git a/tests/warnings/equal_as_assign.js b/tests/warnings/equal_as_assign.js index 9e234e7..4be7049 100644 --- a/tests/warnings/equal_as_assign.js +++ b/tests/warnings/equal_as_assign.js @@ -4,6 +4,9 @@ function equal_as_assign() { while (a = b) { /*warning:equal_as_assign*/ a++; } + while (a -= b) { + a--; + } var c; a = b = c; From e0bf0ba4046608f443eb6e8f8024d8f4fbddd540 Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Mon, 30 Sep 2013 21:54:32 +0000 Subject: [PATCH 21/59] r322: Fix single-line returns without semicolons. --- jsengine/parser/__init__.py | 2 +- tests/bugs/one_line_return.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 tests/bugs/one_line_return.js diff --git a/jsengine/parser/__init__.py b/jsengine/parser/__init__.py index c8c82f3..db28667 100644 --- a/jsengine/parser/__init__.py +++ b/jsengine/parser/__init__.py @@ -638,7 +638,7 @@ def _return_statement(t): endtoken = t.expect(tok.RETURN) startpos = endtoken.startpos - if t.peek_sameline().tok not in (tok.EOF, tok.EOL, tok.SEMI): + if t.peek_sameline().tok not in (tok.EOF, tok.EOL, tok.SEMI, tok.RBRACE): expr = _expression(t, True) endtoken = expr else: diff --git a/tests/bugs/one_line_return.js b/tests/bugs/one_line_return.js new file mode 100644 index 0000000..4415228 --- /dev/null +++ b/tests/bugs/one_line_return.js @@ -0,0 +1,2 @@ +function one_line_return() { return } /*warning:missing_semicolon*/ + From 3f70d38b0aa12ed22277e0732a47da87d8822b79 Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Mon, 30 Sep 2013 23:16:37 +0000 Subject: [PATCH 22/59] r323: Function expressions should not declare a variable. --- javascriptlint/lint.py | 2 +- jsengine/parser/__init__.py | 6 ++---- tests/control_comments/option_explicit.js | 7 +++++++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/javascriptlint/lint.py b/javascriptlint/lint.py index fb1edb9..ab6e4d9 100644 --- a/javascriptlint/lint.py +++ b/javascriptlint/lint.py @@ -609,7 +609,7 @@ def _name(self, node): @visitation.visit('push', tok.FUNCTION) def _push_func(self, node): - if node.fn_name: + if node.opcode != op.CLOSURE and node.fn_name: _warn_or_declare(scopes[-1], node.fn_name, 'function', node, report) self._push_scope(node) for var_name in node.fn_args: diff --git a/jsengine/parser/__init__.py b/jsengine/parser/__init__.py index db28667..9ec2493 100644 --- a/jsengine/parser/__init__.py +++ b/jsengine/parser/__init__.py @@ -162,10 +162,8 @@ def _function_expression(t, named_opcode): fn_body_endpos = t.expect(tok.RBRACE).endpos fn_body = ParseNode(kind.LC, None, fn_body_startpos, fn_body_endpos, None, kids) - return ParseNode(kind.FUNCTION, - op.ANONFUNOBJ if fn_name is None else op.NAMEDFUNOBJ, - startpos, fn_body.endpos, - fn_name, [fn_body], fn_args=fn_args) + return ParseNode(kind.FUNCTION, opcode, startpos, fn_body.endpos, + fn_name, [fn_body], fn_args=fn_args) def _argument_list(t): args = [] diff --git a/tests/control_comments/option_explicit.js b/tests/control_comments/option_explicit.js index bba2875..b012e0f 100644 --- a/tests/control_comments/option_explicit.js +++ b/tests/control_comments/option_explicit.js @@ -60,5 +60,12 @@ function option_explicit(parm) { /* illegal */ y(); /*warning:undeclared_identifier*/ + // This should be undeclared because this is an expression, + // not a declaration. + (function func_expr() { /*warning:want_assign_or_call*/ + return 10; + }); + j = func_expr(); + return ""; } From bd69d4dda8cdcd2bfeb99d1847045970b746e698 Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Tue, 1 Oct 2013 17:07:11 +0000 Subject: [PATCH 23/59] r324: Correctly handle top-level function declarations. --- javascriptlint/lint.py | 2 +- tests/control_comments/option_explicit.js | 5 ++++- tests/warnings/redeclared_var.js | 5 +++++ tests/warnings/unreferenced_identifier.js | 6 ++++-- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/javascriptlint/lint.py b/javascriptlint/lint.py index ab6e4d9..9ffb4d3 100644 --- a/javascriptlint/lint.py +++ b/javascriptlint/lint.py @@ -609,7 +609,7 @@ def _name(self, node): @visitation.visit('push', tok.FUNCTION) def _push_func(self, node): - if node.opcode != op.CLOSURE and node.fn_name: + if node.opcode in (None, op.CLOSURE) and node.fn_name: _warn_or_declare(scopes[-1], node.fn_name, 'function', node, report) self._push_scope(node) for var_name in node.fn_args: diff --git a/tests/control_comments/option_explicit.js b/tests/control_comments/option_explicit.js index b012e0f..d078421 100644 --- a/tests/control_comments/option_explicit.js +++ b/tests/control_comments/option_explicit.js @@ -65,7 +65,10 @@ function option_explicit(parm) { (function func_expr() { /*warning:want_assign_or_call*/ return 10; }); - j = func_expr(); + j = func_expr(); /*warning:undeclared_identifier*/ return ""; } + +// Ensure that we can reference top-level functions. +option_explicit(null); diff --git a/tests/warnings/redeclared_var.js b/tests/warnings/redeclared_var.js index e790d1e..b47a5cf 100644 --- a/tests/warnings/redeclared_var.js +++ b/tests/warnings/redeclared_var.js @@ -7,4 +7,9 @@ function redeclared_var() { return; } var myFunction; /*warning:redeclared_var*/ + + // myFunction isn't a redeclaration, since function names in function + // expressions don't matter. + var tmp = function myFunction(){}; + /*jsl:unused tmp*/ } diff --git a/tests/warnings/unreferenced_identifier.js b/tests/warnings/unreferenced_identifier.js index 9244cb3..ffdb54b 100644 --- a/tests/warnings/unreferenced_identifier.js +++ b/tests/warnings/unreferenced_identifier.js @@ -74,8 +74,10 @@ function unreferenced_identifier() { tmp = ref_dec--; /*warning:inc_dec_within_stmt*/ tmp = -tmp; - /* Test named functions as references. */ - var fn = function ref_func() { return 42; }; /*warning:unreferenced_function*/ + /* Test named functions as references. + * (The name is ignored since it's a function expression.) + */ + var fn = function ref_func() { return 42; }; fn(); /* Test nested scopes. */ From 503449605335a3f679e77f538bcf59ff212007d5 Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Tue, 1 Oct 2013 17:12:20 +0000 Subject: [PATCH 24/59] r325: First cut at warnings to validate function expression names (disabled by default) --- javascriptlint/conf.py | 11 ++- javascriptlint/warnings.py | 84 +++++++++++++++++++++++ tests/control_comments/option_explicit.js | 2 +- tests/warnings/function_name_mismatch.js | 36 ++++++++++ tests/warnings/function_name_missing.js | 30 ++++++++ tests/warnings/misplaced_function.js | 33 +++++++++ tests/warnings/want_assign_or_call.js | 2 +- 7 files changed, 194 insertions(+), 4 deletions(-) create mode 100644 tests/warnings/function_name_mismatch.js create mode 100644 tests/warnings/function_name_missing.js create mode 100644 tests/warnings/misplaced_function.js diff --git a/javascriptlint/conf.py b/javascriptlint/conf.py index a666e0b..4c83c05 100644 --- a/javascriptlint/conf.py +++ b/javascriptlint/conf.py @@ -6,12 +6,18 @@ import util import warnings +_DISABLED_WARNINGS = ( + 'block_without_braces', + 'function_name_missing', + 'function_name_mismatch', +) + def _getwarningsconf(): lines = [] for name in sorted(warnings.warnings.keys()): message = warnings.warnings[name] sign = '+' - if name == 'block_without_braces': + if name in _DISABLED_WARNINGS: sign = '-' assert len(name) < 29 lines.append(sign + name.ljust(29) + '# ' + message) @@ -183,7 +189,8 @@ def __init__(self): } for name in warnings.warnings: self._settings[name] = BooleanSetting(True) - self.loadline('-block_without_braces') + for warning in _DISABLED_WARNINGS: + self.loadline('-%s' % warning) def loadfile(self, path): path = os.path.abspath(path) diff --git a/javascriptlint/warnings.py b/javascriptlint/warnings.py index b16a6c3..67be3fa 100644 --- a/javascriptlint/warnings.py +++ b/javascriptlint/warnings.py @@ -102,6 +102,9 @@ def _get_assigned_lambda(node): 'unsupported_version': 'JavaScript {version} is not supported', 'incorrect_version': 'Expected /*jsl:content-type*/ control comment. The script was parsed with the wrong version.', 'for_in_missing_identifier': 'for..in should have identifier on left side', + 'misplaced_function': 'unconventional use of function expression', + 'function_name_missing': 'anonymous function should be named to match property name {name}', + 'function_name_mismatch': 'function name {fn_name} does not match property name {prop_name}', } errors = { @@ -604,6 +607,87 @@ def for_in_missing_identifier(node): if not left.kind in (tok.VAR, tok.NAME): raise LintWarning, left +@lookfor(tok.FUNCTION) +def misplaced_function(node): + # Ignore function statements. + if node.opcode in (None, op.CLOSURE): + return + + # Ignore parens. + parent = node.parent + while parent.kind == tok.RP: + parent = parent.parent + + # Allow x = x || ... + if parent.kind == tok.OR and len(parent.kids) == 2 and \ + node is parent.kids[-1]: + parent = parent.parent + + if parent.kind == tok.NAME and parent.opcode == op.SETNAME: + return # Allow in var statements + if parent.kind == tok.ASSIGN and parent.opcode == op.NOP: + return # Allow in assigns + if parent.kind == tok.COLON and parent.parent.kind == tok.RC: + return # Allow in object literals + if parent.kind == tok.LP and parent.opcode in (op.CALL, op.SETCALL): + return # Allow in parameters + if parent.kind == tok.RETURN: + return # Allow for return values + if parent.kind == tok.NEW: + return # Allow as constructors + raise LintWarning, node + +def _get_expected_function_name(node): + # Ignore function statements. + if node.opcode in (None, op.CLOSURE): + return + + # Ignore parens. + parent = node.parent + while parent.kind == tok.RP: + parent = parent.parent + + # Allow x = x || ... + if parent.kind == tok.OR and len(parent.kids) == 2 and \ + node is parent.kids[-1]: + parent = parent.parent + + # Var assignment. + if parent.kind == tok.NAME and parent.opcode == op.SETNAME: + return parent.atom + + # Assignment. + if parent.kind == tok.ASSIGN and parent.opcode == op.NOP: + if parent.kids[0].kind == tok.NAME and \ + parent.kids[0].opcode == op.SETNAME: + return parent.kids[0].atom + return '' + + # Object literal. + if parent.kind == tok.COLON and parent.parent.kind == tok.RC: + return parent.kids[0].atom + +@lookfor(tok.FUNCTION) +def function_name_missing(node): + if node.fn_name: + return + + expected_name = _get_expected_function_name(node) + if not expected_name is None: + raise LintWarning(node, name=expected_name) + +@lookfor(tok.FUNCTION) +def function_name_mismatch(node): + if not node.fn_name: + return + + expected_name = _get_expected_function_name(node) + if expected_name is None: + return + + if expected_name != node.fn_name: + raise LintWarning(node, fn_name=node.fn_name, prop_name=expected_name) + @lookfor() def mismatch_ctrl_comments(node): pass diff --git a/tests/control_comments/option_explicit.js b/tests/control_comments/option_explicit.js index d078421..986b93d 100644 --- a/tests/control_comments/option_explicit.js +++ b/tests/control_comments/option_explicit.js @@ -62,7 +62,7 @@ function option_explicit(parm) { // This should be undeclared because this is an expression, // not a declaration. - (function func_expr() { /*warning:want_assign_or_call*/ + (function func_expr() { /*warning:misplaced_function*/ /*warning:want_assign_or_call*/ return 10; }); j = func_expr(); /*warning:undeclared_identifier*/ diff --git a/tests/warnings/function_name_mismatch.js b/tests/warnings/function_name_mismatch.js new file mode 100644 index 0000000..d0f962a --- /dev/null +++ b/tests/warnings/function_name_mismatch.js @@ -0,0 +1,36 @@ +/*conf:+function_name_mismatch*/ +function function_name_mismatch() { + var f = function bogus() { /*warning:function_name_mismatch*/ + }; + var g = function g() { + }; + + f = new function bogus() { + }; + f = new function() { + }; + + f = (function() { + return 10; + })(); + + var o = { + f: function bogus() { /*warning:function_name_mismatch*/ + return null; + } + }; + o.a.b = { + f: function bogus() { /*warning:function_name_mismatch*/ + return null; + } + }; + o.a.b = o.a.b || function bogus() { return 10; }; /*warning:function_name_mismatch*/ + + function closure(a) { + return function() { return a; }; + } + + function x() { + } +} + diff --git a/tests/warnings/function_name_missing.js b/tests/warnings/function_name_missing.js new file mode 100644 index 0000000..31e39ed --- /dev/null +++ b/tests/warnings/function_name_missing.js @@ -0,0 +1,30 @@ +/*conf:+function_name_missing*/ +function function_name_missing() { + var f = function() { /*warning:function_name_missing*/ + }; + f = new function() { + }; + f = (function() { + return 10; + })(); + + var o = { + f: function() { /*warning:function_name_missing*/ + return null; + } + }; + o.a.b = { + f: function() { /*warning:function_name_missing*/ + return null; + } + }; + o.a.b = o.a.b || function() { return 10; }; /*warning:function_name_missing*/ + + function closure(a) { + return function() { return a; }; + } + + function x() { + } +} + diff --git a/tests/warnings/misplaced_function.js b/tests/warnings/misplaced_function.js new file mode 100644 index 0000000..edbd3c6 --- /dev/null +++ b/tests/warnings/misplaced_function.js @@ -0,0 +1,33 @@ +/*conf:-want_assign_or_call*/ +function misplaced_functions() { + var f = function() { + }; + f = new function() { + }; + f = (function() { + return 10; + })(); + + var o = { + f: function() { + return null; + } + }; + o.a.b = { + f: function() { + return null; + } + }; + o.a.b = o.a.b || function() { return 10; }; + o.a.b = o.a.c || function() { return 10; }; /*TODO*/ + + function closure(a) { + return function() { return a; }; + } + + function x() { + } + + function() {}; /*warning:misplaced_function*/ +} + diff --git a/tests/warnings/want_assign_or_call.js b/tests/warnings/want_assign_or_call.js index 16390db..d120691 100644 --- a/tests/warnings/want_assign_or_call.js +++ b/tests/warnings/want_assign_or_call.js @@ -14,7 +14,7 @@ function want_assign_or_call() { function test() { } - function() { /*warning:want_assign_or_call*/ + function() { /*warning:want_assign_or_call*/ /*warning:misplaced_function*/ return 42; }; From a0b65fa5a357a3d905abef0085d87f557bdfd3a2 Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Wed, 2 Oct 2013 23:16:54 +0000 Subject: [PATCH 25/59] r326: pylint: start work to run pylint on jsl --- test.py | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/test.py b/test.py index aa3e689..bd4a7e5 100755 --- a/test.py +++ b/test.py @@ -4,6 +4,9 @@ import re import sys +import pylint.lint +from pylint.reporters.text import TextReporter + import javascriptlint.conf import javascriptlint.lint @@ -90,7 +93,93 @@ def _get_test_files(): all_files.sort() return all_files +class _CustomLintReporter(TextReporter): + line_format = '{path}({line}): [{msg_id}({symbol}){obj}] {msg}' + def __init__(self): + TextReporter.__init__(self) + self.msg_count = 0 + + def write_message(self, msg): + TextReporter.write_message(self, msg) + self.msg_count += 1 + +def _get_python_modules(dir_): + for root, dirs, files in os.walk(dir_): + if '.svn' in dirs: + dirs.remove('.svn') + for name in files: + if name.endswith('.py'): + yield os.path.join(root, name) + +def _run_pylint(): + IGNORE = [ + 'C0111', # Missing docstring + 'I0011', # Locally disabling warning + 'R0902', # Too many instance attributes (%s/%s) + 'R0903', # Too few public methods (%s/%s) + 'R0904', # Too many public methods (%s/%s) + 'R0911', # Too many return statements (%s/%s) + 'R0912', # Too many branches (%s/%s) + 'R0913', # Too many arguments (%s/%s) + 'R0914', # Too many local variables (%s/%s) + 'R0915', # Too many statements (%s/%s) + 'W0142', # Used * or ** magic + ] + REVIEW = [ + 'C0103', # Invalid name "%s" (should match %s) + 'C0202', # Class method should have "cls" as first argument + 'C0301', # Line too long (%s/%s) + 'C0303', # Trailing whitespace + 'C0321', # More than one statement on a single line + 'C0323', # Operator not followed by a space + 'C0324', # Comma not followed by a space + 'C1001', # Old style class + 'E0602', # Undefined variable %r + 'E1101', # %s %r has no %r member + 'E1103', # %s %r has no %r member (but some types could not be inferred) + 'E1306', # Not enough arguments for format string + 'F0401', # Cyclic import (%s) + 'R0201', # Attribute %r defined outside __init__ + 'R0924', # Badly implemented + 'W0109', # Duplicate key %r in dictionary + 'W0120', # Else clause on loop without a break statement + 'W0121', # old-raise-syntax + 'W0141', # Used builtin function %r + 'W0201', # Attribute %r defined outside __init__ + 'W0212', # Access to a protected member %s of a client class + 'W0231', # __init__ method from base class %r is not called + 'W0232', # Class has no __init__ method + 'W0301', # Unnecessary semicolon + 'W0311', # Bad indentation + 'W0401', # Wildcard import %s + 'W0403', # Relative import %r + 'W0511', # TODO + 'W0611', # Unused import %s + 'W0612', # unused variable + 'W0613', # Unused argument %r + 'W0614', # Unused import %s from wildcard import + 'W0621', # Redefining name %r from outer scope (line %s) + 'W0622', # Redefining built-in %r + 'W0631', # Using possibly undefined loop variable %r + 'W0632', # unbalanced-tuple-unpacking + 'W1401', # Anomalous backslash in string + ] + + dir_ = os.path.dirname(os.path.abspath(__file__)) + modules = list(_get_python_modules(dir_)) + reporter = _CustomLintReporter() + pylint.lint.Run([ + '--reports=n', + ] + [ + '--disable=%s' % code for code in (IGNORE + REVIEW) + ] + modules, reporter=reporter, exit=False) + if reporter.msg_count: + print '\nLint failed!\n' + sys.exit(1) + def main(): + # _run_pylint() + haderrors = False for file in _get_test_files(): ext = os.path.splitext(file)[1] @@ -108,5 +197,9 @@ def main(): sys.exit(haderrors) if __name__ == '__main__': - main() + try: + main() + except KeyboardInterrupt: + sys.stderr.write('\n\nCanceled.\n') + sys.exit(1) From ab85177d81556a242bf126c419eba5fca2121a89 Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Wed, 2 Oct 2013 23:18:30 +0000 Subject: [PATCH 26/59] r327: pylint: fix raise syntax --- javascriptlint/conf.py | 14 +++--- javascriptlint/visitation.py | 4 +- javascriptlint/warnings.py | 94 ++++++++++++++++++------------------ test.py | 3 +- 4 files changed, 57 insertions(+), 58 deletions(-) diff --git a/javascriptlint/conf.py b/javascriptlint/conf.py index 4c83c05..733dc77 100644 --- a/javascriptlint/conf.py +++ b/javascriptlint/conf.py @@ -119,7 +119,7 @@ class DeprecatedSetting(Setting): wants_parm = False value = None def load(self, enabled): - raise ConfError, 'This setting is deprecated.' + raise ConfError('This setting is deprecated.') class BooleanSetting(Setting): wants_parm = False @@ -134,7 +134,7 @@ def __init__(self, default): self.value = default def load(self, enabled, parm): if not enabled: - raise ConfError, 'Expected +.' + raise ConfError('Expected +.') self.value = parm class DeclareSetting(Setting): @@ -143,7 +143,7 @@ def __init__(self): self.value = [] def load(self, enabled, parm): if not enabled: - raise ConfError, 'Expected +.' + raise ConfError('Expected +.') self.value.append(parm) class ProcessSetting(Setting): @@ -162,11 +162,11 @@ class JSVersionSetting(Setting): value = util.JSVersion.default() def load(self, enabled, parm): if not enabled: - raise ConfError, 'Expected +.' + raise ConfError('Expected +.') self.value = util.JSVersion.fromtype(parm) if not self.value: - raise ConfError, 'Invalid JavaScript version: %s' % parm + raise ConfError('Invalid JavaScript version: %s' % parm) class Conf: def __init__(self): @@ -227,7 +227,7 @@ def loadline(self, line, dir=None): elif line.startswith('-'): enabled = False else: - raise ConfError, 'Expected + or -.' + raise ConfError('Expected + or -.') line = line[1:] # Parse the key/parms @@ -242,7 +242,7 @@ def loadline(self, line, dir=None): if setting.wants_parm: args['parm'] = parm elif parm: - raise ConfError, 'The %s setting does not expect a parameter.' % name + raise ConfError('The %s setting does not expect a parameter.' % name) if setting.wants_dir: args['dir'] = dir setting.load(**args) diff --git a/javascriptlint/visitation.py b/javascriptlint/visitation.py index 573db1c..60a9f17 100644 --- a/javascriptlint/visitation.py +++ b/javascriptlint/visitation.py @@ -27,9 +27,9 @@ def make_visitors(visitors, klasses): # Intantiate an instance of each class for klass in klasses: if klass.__name__.lower() != klass.__name__: - raise ValueError, 'class names must be lowercase' + raise ValueError('class names must be lowercase') if not klass.__doc__: - raise ValueError, 'missing docstring on class %s' % klass.__name__ + raise ValueError('missing docstring on class %s' % klass.__name__) # Look for functions with the "_visit_nodes" property. visitor = klass() diff --git a/javascriptlint/warnings.py b/javascriptlint/warnings.py index 67be3fa..db1d01d 100644 --- a/javascriptlint/warnings.py +++ b/javascriptlint/warnings.py @@ -12,7 +12,7 @@ @lookfor(tok.NODEKIND, (tok.NODEKIND, op.OPCODE)) def warning_name(node): if questionable: - raise LintWarning, node + raise LintWarning(node) """ import itertools import re @@ -125,7 +125,7 @@ def format_error(errname, **errargs): try: errdesc = re.sub(r"{(\w+)}", lambda match: errargs[match.group(1)], errdesc) except (TypeError, KeyError): - raise KeyError, 'Invalid keyword in error: ' + errdesc + raise KeyError('Invalid keyword in error: ' + errdesc) return errdesc _visitors = [] @@ -253,17 +253,17 @@ def break_to_none(node): def comparison_type_conv(node): for kid in node.kids: if kid.kind == tok.PRIMARY and kid.opcode in (op.NULL, op.TRUE, op.FALSE): - raise LintWarning, kid + raise LintWarning(kid) if kid.kind == tok.NUMBER and not kid.dval: - raise LintWarning, kid + raise LintWarning(kid) if kid.kind == tok.STRING and not kid.atom: - raise LintWarning, kid + raise LintWarning(kid) @lookfor(tok.DEFAULT) def default_not_at_end(node): siblings = node.parent.kids if node.node_index != len(siblings)-1: - raise LintWarning, siblings[node.node_index+1] + raise LintWarning(siblings[node.node_index+1]) @lookfor(tok.CASE) def duplicate_case_in_switch(node): @@ -276,7 +276,7 @@ def duplicate_case_in_switch(node): if sibling.kind == tok.CASE: sibling_value = sibling.kids[0] if node_value.is_equivalent(sibling_value, True): - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.SWITCH) def missing_default_case(node): @@ -284,21 +284,21 @@ def missing_default_case(node): for case in cases.kids: if case.kind == tok.DEFAULT: return - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.WITH) def with_statement(node): - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.EQOP,tok.RELOP) def useless_comparison(node): for lvalue, rvalue in itertools.combinations(node.kids, 2): if lvalue.is_equivalent(rvalue): - raise LintWarning, node + raise LintWarning(node) @lookfor((tok.COLON, op.NAME)) def use_of_label(node): - raise LintWarning, node + raise LintWarning(node) @lookfor((tok.OBJECT, op.REGEXP)) def misplaced_regex(node): @@ -314,7 +314,7 @@ def misplaced_regex(node): return # Allow in /re/.property if node.parent.kind == tok.RETURN: return # Allow for return values - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.ASSIGN) def assign_to_function_call(node): @@ -323,7 +323,7 @@ def assign_to_function_call(node): while kid.kind == tok.RP: kid, = kid.kids if kid.kind == tok.LP: - raise LintWarning, node + raise LintWarning(node) @lookfor((tok.ASSIGN, None)) def equal_as_assign(node): @@ -333,7 +333,7 @@ def equal_as_assign(node): if not node.parent.kind in (tok.SEMI, tok.RESERVED, tok.RP, tok.COMMA, tok.ASSIGN): - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.IF) def ambiguous_else_stmt(node): @@ -349,13 +349,13 @@ def ambiguous_else_stmt(node): return # Else is only ambiguous in the first branch of an if statement. if tmp.parent.kind == tok.IF and tmp.node_index == 1: - raise LintWarning, else_ + raise LintWarning(else_) tmp = tmp.parent @lookfor(tok.IF, tok.WHILE, tok.DO, tok.FOR, tok.WITH) def block_without_braces(node): if node.kids[1].kind != tok.LC: - raise LintWarning, node.kids[1] + raise LintWarning(node.kids[1]) _block_nodes = (tok.IF, tok.WHILE, tok.DO, tok.FOR, tok.WITH) @lookfor(*_block_nodes) @@ -368,7 +368,7 @@ def ambiguous_nested_stmt(node): # was inside a block statement without clarifying curlies. # (Otherwise, the node type would be tok.LC.) if node.parent.kind in _block_nodes: - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.INC, tok.DEC) def inc_dec_within_stmt(node): @@ -384,7 +384,7 @@ def inc_dec_within_stmt(node): tmp.parent.parent.kind == tok.FOR: return - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.COMMA) def comma_separated_stmts(node): @@ -394,12 +394,12 @@ def comma_separated_stmts(node): # This is an array if node.parent.kind == tok.RB: return - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.SEMI) def empty_statement(node): if not node.kids[0]: - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.LC) def empty_statement_(node): if node.kids: @@ -410,7 +410,7 @@ def empty_statement_(node): # Some empty blocks are meaningful. if node.parent.kind in (tok.CATCH, tok.CASE, tok.DEFAULT, tok.SWITCH, tok.FUNCTION): return - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.CASE, tok.DEFAULT) def missing_break(node): @@ -424,7 +424,7 @@ def missing_break(node): return if None in _get_exit_points(case_contents): # Show the warning on the *next* node. - raise LintWarning, node.parent.kids[node.node_index+1] + raise LintWarning(node.parent.kids[node.node_index+1]) @lookfor(tok.CASE, tok.DEFAULT) def missing_break_for_last_case(node): @@ -433,16 +433,16 @@ def missing_break_for_last_case(node): case_contents = node.kids[1] assert case_contents.kind == tok.LC if None in _get_exit_points(case_contents): - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.INC) def multiple_plus_minus(node): if node.node_index == 0 and node.parent.kind == tok.PLUS: - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.DEC) def multiple_plus_minus_(node): if node.node_index == 0 and node.parent.kind == tok.MINUS: - raise LintWarning, node + raise LintWarning(node) @lookfor((tok.NAME, op.SETNAME)) def useless_assign(node): @@ -452,7 +452,7 @@ def useless_assign(node): elif node.parent.kind == tok.VAR: value = node.kids[0] if value and value.kind == tok.NAME and node.atom == value.atom: - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.BREAK, tok.CONTINUE, tok.RETURN, tok.THROW) def unreachable_code(node): @@ -463,12 +463,12 @@ def unreachable_code(node): for variable in sibling.kids: value, = variable.kids if value: - raise LintWarning, value + raise LintWarning(value) elif sibling.kind == tok.FUNCTION: # Functions are always declared. pass else: - raise LintWarning, sibling + raise LintWarning(sibling) @lookfor(tok.FOR) def unreachable_code_(node): @@ -478,73 +478,73 @@ def unreachable_code_(node): pre, condition, post = preamble.kids if post: if not None in _get_exit_points(code): - raise LintWarning, post + raise LintWarning(post) @lookfor(tok.DO) def unreachable_code__(node): # Warn if the do..while loop always exits. code, condition = node.kids if not None in _get_exit_points(code): - raise LintWarning, condition + raise LintWarning(condition) #TODO: @lookfor(tok.IF) def meaningless_block(node): condition, if_, else_ = node.kids if condition.kind == tok.PRIMARY and condition.opcode in (op.TRUE, op.FALSE, op.NULL): - raise LintWarning, condition + raise LintWarning(condition) #TODO: @lookfor(tok.WHILE) def meaningless_blocK_(node): condition = node.kids[0] if condition.kind == tok.PRIMARY and condition.opcode in (op.FALSE, op.NULL): - raise LintWarning, condition + raise LintWarning(condition) @lookfor(tok.LC) def meaningless_block__(node): if node.parent and node.parent.kind == tok.LC: - raise LintWarning, node + raise LintWarning(node) @lookfor((tok.UNARYOP, op.VOID)) def useless_void(node): - raise LintWarning, node + raise LintWarning(node) @lookfor((tok.LP, op.CALL)) def parseint_missing_radix(node): if node.kids[0].kind == tok.NAME and node.kids[0].atom == 'parseInt' and len(node.kids) <= 2: - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.NUMBER) def leading_decimal_point(node): if node.atom.startswith('.'): - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.NUMBER) def trailing_decimal_point(node): if node.parent.kind == tok.DOT: - raise LintWarning, node + raise LintWarning(node) if node.atom.endswith('.'): - raise LintWarning, node + raise LintWarning(node) _octal_regexp = re.compile('^0[0-9]') @lookfor(tok.NUMBER) def octal_number(node): if _octal_regexp.match(node.atom): - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.RC) def trailing_comma(node): if node.end_comma: - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.RB) def trailing_comma_in_array(node): if node.end_comma: - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.STRING) def useless_quotes(node): if node.node_index == 0 and node.parent.kind == tok.COLON: # Only warn if the quotes could safely be removed. if util.isidentifier(node.atom): - raise LintWarning, node + raise LintWarning(node) @lookfor(tok.SEMI) def want_assign_or_call(node): @@ -566,7 +566,7 @@ def want_assign_or_call(node): grandchild = child.kids[0] if grandchild.kind == tok.FUNCTION: return - raise LintWarning, child + raise LintWarning(child) def _check_return_value(node): name = node.fn_name or '(anonymous function)' @@ -605,7 +605,7 @@ def for_in_missing_identifier(node): assert node.kids[0].kind == tok.IN left, right = node.kids[0].kids if not left.kind in (tok.VAR, tok.NAME): - raise LintWarning, left + raise LintWarning(left) @lookfor(tok.FUNCTION) def misplaced_function(node): @@ -635,7 +635,7 @@ def misplaced_function(node): return # Allow for return values if parent.kind == tok.NEW: return # Allow as constructors - raise LintWarning, node + raise LintWarning(node) def _get_expected_function_name(node): # Ignore function statements. @@ -724,7 +724,7 @@ def duplicate_formal(node): def missing_semicolon(node): if node.no_semi: if not _get_assigned_lambda(node): - raise LintWarning, node + raise LintWarning(node) @lookfor(*_ALL_TOKENS) def missing_semicolon_for_lambda(node): @@ -733,7 +733,7 @@ def missing_semicolon_for_lambda(node): # statements, so use the position of the lambda instead. lambda_ = _get_assigned_lambda(node) if lambda_: - raise LintWarning, lambda_ + raise LintWarning(lambda_) @lookfor() def ambiguous_newline(node): diff --git a/test.py b/test.py index bd4a7e5..0e3de1a 100755 --- a/test.py +++ b/test.py @@ -76,7 +76,7 @@ def lint_error(path, line, col, errname, errdesc): for line, warning, errdesc in unexpected_warnings: errors.append('\tline %i: %s/%s' % (line+1, warning, errdesc)) if errors: - raise TestError, '\n'.join(errors) + raise TestError('\n'.join(errors)) def _get_test_files(): # Get a list of test files. @@ -143,7 +143,6 @@ def _run_pylint(): 'R0924', # Badly implemented 'W0109', # Duplicate key %r in dictionary 'W0120', # Else clause on loop without a break statement - 'W0121', # old-raise-syntax 'W0141', # Used builtin function %r 'W0201', # Attribute %r defined outside __init__ 'W0212', # Access to a protected member %s of a client class From b3d52f262f36a9d39a4aab465fcc899f9a29137e Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Wed, 2 Oct 2013 23:23:24 +0000 Subject: [PATCH 27/59] r328: pylint: clean up trailing whitespace --- javascriptlint/conf.py | 4 ++-- jsengine/parser/__init__.py | 28 ++++++++++++++-------------- jsengine/structs.py | 2 +- jsengine/tokenizer/__init__.py | 12 ++++++------ test.py | 3 +-- 5 files changed, 24 insertions(+), 25 deletions(-) diff --git a/javascriptlint/conf.py b/javascriptlint/conf.py index 733dc77..1b6b52f 100644 --- a/javascriptlint/conf.py +++ b/javascriptlint/conf.py @@ -163,14 +163,14 @@ class JSVersionSetting(Setting): def load(self, enabled, parm): if not enabled: raise ConfError('Expected +.') - + self.value = util.JSVersion.fromtype(parm) if not self.value: raise ConfError('Invalid JavaScript version: %s' % parm) class Conf: def __init__(self): - recurse = BooleanSetting(False) + recurse = BooleanSetting(False) self._settings = { 'recurse': recurse, 'output-format': StringSetting('__FILE__(__LINE__): __ERROR__'), diff --git a/jsengine/parser/__init__.py b/jsengine/parser/__init__.py index 9ec2493..f0a29e2 100644 --- a/jsengine/parser/__init__.py +++ b/jsengine/parser/__init__.py @@ -86,7 +86,7 @@ def _primary_expression(t): x.startpos, x.endpos, None, []) items[-1] = items[-1] or comma - # Check for the end. + # Check for the end. if t.peek().tok == tok.RBRACKET: end_comma = comma break @@ -306,7 +306,7 @@ def _binary_expression(t, dict_, child_expr_callback): _MULTIPLICATIVE = { tok.MUL: (kind.STAR, op.MUL), tok.DIV: (kind.DIVOP, op.DIV), - tok.MOD: (kind.DIVOP, op.MOD), + tok.MOD: (kind.DIVOP, op.MOD), } def _multiplicative_expression(t): return _binary_expression(t, _MULTIPLICATIVE, _unary_expression) @@ -391,7 +391,7 @@ def _logical_and_expression(t, allowin): t.expect(tok.LOGICAL_AND) else: break - + while len(exprs) > 1: right = exprs.pop() left = exprs[-1] @@ -408,7 +408,7 @@ def _logical_or_expression(t, allowin): t.expect(tok.LOGICAL_OR) else: break - + while len(exprs) > 1: right = exprs.pop() left = exprs[-1] @@ -467,7 +467,7 @@ def _assignment_expression(t, allowin): kind_, op_ = _ASSIGNS[t.peek().tok] t.advance() right = _assignment_expression(t, allowin) - return ParseNode(kind_, op_, + return ParseNode(kind_, op_, left.startpos, right.endpos, None, [left, right]) else: return left @@ -607,7 +607,7 @@ def _for_statement(t): op.FORIN if condition.kind == kind.IN else None, for_startpos, body.endpos, None, [condition, body]) - + def _continue_statement(t): endtoken = t.expect(tok.CONTINUE) startpos = endtoken.startpos @@ -617,7 +617,7 @@ def _continue_statement(t): name = endtoken.atom else: name = None - # TODO: Validate Scope Labels + # TODO: Validate Scope Labels return _auto_semicolon(t, kind.CONTINUE, None, startpos, endtoken.endpos, name, []) def _break_statement(t): @@ -629,19 +629,19 @@ def _break_statement(t): name = endtoken.atom else: name = None - # TODO: Validate Scope Labels + # TODO: Validate Scope Labels return _auto_semicolon(t, kind.BREAK, None, startpos, endtoken.endpos, name, []) def _return_statement(t): endtoken = t.expect(tok.RETURN) startpos = endtoken.startpos - + if t.peek_sameline().tok not in (tok.EOF, tok.EOL, tok.SEMI, tok.RBRACE): expr = _expression(t, True) endtoken = expr else: expr = None - # TODO: Validate Scope Labels + # TODO: Validate Scope Labels return _auto_semicolon(t, kind.RETURN, None, startpos, endtoken.endpos, None, [expr]) @@ -672,7 +672,7 @@ def _switch_statement(t): case_kind = kind.DEFAULT else: raise JSSyntaxError(t.peek().startpos, 'invalid_case') - + case_endpos = t.expect(tok.COLON).endpos statements = [] @@ -692,7 +692,7 @@ def _switch_statement(t): ParseNode(kind.LC, None, statements_startpos, statements_endpos, None, statements) ])) - + rc_endpos = t.expect(tok.RBRACE).endpos return ParseNode(kind.SWITCH, None, switch_startpos, rc_endpos, None, [expr, @@ -734,7 +734,7 @@ def _try_statement(t): ]) ]) try_endpos = catch_endpos - + if t.peek().tok == tok.FINALLY: t.advance() finally_node = _block_statement(t) @@ -782,7 +782,7 @@ def _statement(t): raise JSSyntaxError(x.startpos, 'unexpected_eof') elif x.tok == tok.FUNCTION: return _function_declaration(t, op.CLOSURE) #TODO: warn, since this is not reliable - + elif x.tok not in (tok.LBRACE, tok.FUNCTION): expr = _expression(t, True) if expr.kind == tok.NAME and t.peek().tok == tok.COLON: diff --git a/jsengine/structs.py b/jsengine/structs.py index 3b0a180..6d74ada 100644 --- a/jsengine/structs.py +++ b/jsengine/structs.py @@ -112,7 +112,7 @@ def __init__(self, kind_, op_, start_pos, end_pos, atom, kids, self.endpos = end_pos self.no_semi = no_semi self.end_comma = end_comma - + for i, kid in enumerate(self.kids): if kid: assert isinstance(kid, ParseNode) diff --git a/jsengine/tokenizer/__init__.py b/jsengine/tokenizer/__init__.py index 86e4d41..b905038 100644 --- a/jsengine/tokenizer/__init__.py +++ b/jsengine/tokenizer/__init__.py @@ -41,7 +41,7 @@ "}": "RBRACE", "(": "LPAREN", ")": "RPAREN", - "[": "LBRACKET", + "[": "LBRACKET", "]": "RBRACKET", ".": "DOT", ";": "SEMI", @@ -58,7 +58,7 @@ "!": "LOGICAL_NOT", "~": "BIT_NOT", "?": "QUESTION", - ":": "COLON", + ":": "COLON", "=": "ASSIGN", "/": "DIV", "!": "LOGICAL_NOT", @@ -159,7 +159,7 @@ def __init__(self, content, startpos=None): def getpos(self, offset=0): return self._nodepositions.from_offset(self._pos+offset) - + def watch_reads(self): self._watched_pos = self._pos @@ -316,7 +316,7 @@ def _parse_rest_of_regexp(self): def _next(self, parse_regexp=False): stream = self._stream - + if stream.eof(): return Token(tok.EOF) @@ -331,10 +331,10 @@ def _next(self, parse_regexp=False): elif stream.readif(1, _WHITESPACE): pass else: - break + break if linebreak: return Token(tok.EOL) - else: + else: return Token(tok.SPACE) # COMMENTS diff --git a/test.py b/test.py index 0e3de1a..2defa17 100755 --- a/test.py +++ b/test.py @@ -96,7 +96,7 @@ def _get_test_files(): class _CustomLintReporter(TextReporter): line_format = '{path}({line}): [{msg_id}({symbol}){obj}] {msg}' def __init__(self): - TextReporter.__init__(self) + TextReporter.__init__(self) self.msg_count = 0 def write_message(self, msg): @@ -129,7 +129,6 @@ def _run_pylint(): 'C0103', # Invalid name "%s" (should match %s) 'C0202', # Class method should have "cls" as first argument 'C0301', # Line too long (%s/%s) - 'C0303', # Trailing whitespace 'C0321', # More than one statement on a single line 'C0323', # Operator not followed by a space 'C0324', # Comma not followed by a space From 3f10ced4f01a4d3139a6f02db0afff3e80bc206d Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Wed, 2 Oct 2013 23:27:18 +0000 Subject: [PATCH 28/59] r329: pylint: fix more whitespace issues --- javascriptlint/jsparse.py | 60 ++++++++++++++++++------------------- javascriptlint/warnings.py | 2 +- jsengine/parser/__init__.py | 2 +- jsengine/structs.py | 2 +- test.py | 2 -- 5 files changed, 33 insertions(+), 35 deletions(-) diff --git a/javascriptlint/jsparse.py b/javascriptlint/jsparse.py index a5ee4ac..e263945 100644 --- a/javascriptlint/jsparse.py +++ b/javascriptlint/jsparse.py @@ -55,7 +55,7 @@ def parse(script, jsversion, error_callback, startpos=None): """ All node positions will be relative to startpos. This allows scripts to be embedded in a file (for example, HTML). """ - startpos = startpos or NodePos(0,0) + startpos = startpos or NodePos(0, 0) jsversion = jsversion or JSVersion.default() assert isvalidversion(jsversion), jsversion if jsversion.e4x: @@ -173,7 +173,7 @@ def testOffset(self): self.assertEquals(pos.to_offset(NodePos(1, 0)), 5) self.assertEquals(pos.to_offset(NodePos(3, 1)), 11) def testStartPos(self): - pos = NodePositions('abc\r\ndef\n\nghi', NodePos(3,4)) + pos = NodePositions('abc\r\ndef\n\nghi', NodePos(3, 4)) self.assertEquals(pos.to_offset(NodePos(3, 4)), 0) self.assertEquals(pos.to_offset(NodePos(3, 5)), 1) self.assertEquals(pos.from_offset(0), NodePos(3, 4)) @@ -185,21 +185,21 @@ class TestNodeRanges(unittest.TestCase): def testAdd(self): r = NodeRanges() r.add(5, 10) - self.assertEquals(r._offsets, [5,11]) + 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]) + 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) @@ -239,8 +239,8 @@ def onerror(line, col, msg, msg_args): return errors[0] self.assertEquals(geterror(' ?', None), (0, 1, 'syntax_error', {})) self.assertEquals(geterror('\n ?', None), (1, 1, 'syntax_error', {})) - self.assertEquals(geterror(' ?', NodePos(1,1)), (1, 2, 'syntax_error', {})) - self.assertEquals(geterror('\n ?', NodePos(1,1)), (2, 1, 'syntax_error', {})) + self.assertEquals(geterror(' ?', NodePos(1, 1)), (1, 2, 'syntax_error', {})) + self.assertEquals(geterror('\n ?', NodePos(1, 1)), (2, 1, 'syntax_error', {})) def testNodePos(self): def getnodepos(script, startpos): root = parse(script, None, None, startpos) @@ -248,24 +248,24 @@ def getnodepos(script, startpos): var, = root.kids self.assertEquals(var.kind, tok.VAR) return var.start_pos() - self.assertEquals(getnodepos('var x;', None), NodePos(0,0)) - self.assertEquals(getnodepos(' var x;', None), NodePos(0,1)) - self.assertEquals(getnodepos('\n\n var x;', None), NodePos(2,1)) - self.assertEquals(getnodepos('var x;', NodePos(3,4)), NodePos(3,4)) - self.assertEquals(getnodepos(' var x;', NodePos(3,4)), NodePos(3,5)) - self.assertEquals(getnodepos('\n\n var x;', NodePos(3,4)), NodePos(5,1)) + self.assertEquals(getnodepos('var x;', None), NodePos(0, 0)) + self.assertEquals(getnodepos(' var x;', None), NodePos(0, 1)) + self.assertEquals(getnodepos('\n\n var x;', None), NodePos(2, 1)) + self.assertEquals(getnodepos('var x;', NodePos(3, 4)), NodePos(3, 4)) + self.assertEquals(getnodepos(' var x;', NodePos(3, 4)), NodePos(3, 5)) + self.assertEquals(getnodepos('\n\n var x;', NodePos(3, 4)), NodePos(5, 1)) def testComments(self): def testcomment(comment, startpos, expectedpos): root = parse(comment, None, None, startpos) comment, = findcomments(comment, root, startpos) self.assertEquals(comment.start_pos(), expectedpos) for comment in ('/*comment*/', '//comment'): - testcomment(comment, None, NodePos(0,0)) - testcomment(' %s' % comment, None, NodePos(0,1)) - testcomment('\n\n %s' % comment, None, NodePos(2,1)) - testcomment('%s' % comment, NodePos(3,4), NodePos(3,4)) - testcomment(' %s' % comment, NodePos(3,4), NodePos(3,5)) - testcomment('\n\n %s' % comment, NodePos(3,4), NodePos(5,1)) + testcomment(comment, None, NodePos(0, 0)) + testcomment(' %s' % comment, None, NodePos(0, 1)) + testcomment('\n\n %s' % comment, None, NodePos(2, 1)) + testcomment('%s' % comment, NodePos(3, 4), NodePos(3, 4)) + testcomment(' %s' % comment, NodePos(3, 4), NodePos(3, 5)) + testcomment('\n\n %s' % comment, NodePos(3, 4), NodePos(5, 1)) if __name__ == '__main__': unittest.main() diff --git a/javascriptlint/warnings.py b/javascriptlint/warnings.py index db1d01d..569a009 100644 --- a/javascriptlint/warnings.py +++ b/javascriptlint/warnings.py @@ -290,7 +290,7 @@ def missing_default_case(node): def with_statement(node): raise LintWarning(node) -@lookfor(tok.EQOP,tok.RELOP) +@lookfor(tok.EQOP, tok.RELOP) def useless_comparison(node): for lvalue, rvalue in itertools.combinations(node.kids, 2): if lvalue.is_equivalent(rvalue): diff --git a/jsengine/parser/__init__.py b/jsengine/parser/__init__.py index f0a29e2..80b9fcf 100644 --- a/jsengine/parser/__init__.py +++ b/jsengine/parser/__init__.py @@ -854,7 +854,7 @@ def testUnterminatedComment(self): try: parsestring('/*') except JSSyntaxError as error: - self.assertEqual(error.pos, NodePos(0,1)) + self.assertEqual(error.pos, NodePos(0, 1)) else: self.assert_(False) def testObjectEndComma(self): diff --git a/jsengine/structs.py b/jsengine/structs.py index 6d74ada..55676f1 100644 --- a/jsengine/structs.py +++ b/jsengine/structs.py @@ -63,7 +63,7 @@ def add(self, start, end): end = self._offsets[j] j += 1 - self._offsets[i:j] = [start,end] + self._offsets[i:j] = [start, end] def has(self, pos): return bisect.bisect_right(self._offsets, pos) % 2 == 1 diff --git a/test.py b/test.py index 2defa17..9afe03f 100755 --- a/test.py +++ b/test.py @@ -130,8 +130,6 @@ def _run_pylint(): 'C0202', # Class method should have "cls" as first argument 'C0301', # Line too long (%s/%s) 'C0321', # More than one statement on a single line - 'C0323', # Operator not followed by a space - 'C0324', # Comma not followed by a space 'C1001', # Old style class 'E0602', # Undefined variable %r 'E1101', # %s %r has no %r member From eb1f94645c1f4c831004c6daeef333e91e73ae70 Mon Sep 17 00:00:00 2001 From: Cody Peter Mello Date: Fri, 3 Jun 2016 10:04:00 -0700 Subject: [PATCH 29/59] r330: Embed version number and revision. --- javascriptlint/conf.py | 3 ++- javascriptlint/jsl.py | 4 ++-- javascriptlint/version.py | 2 ++ 3 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 javascriptlint/version.py diff --git a/javascriptlint/conf.py b/javascriptlint/conf.py index 1b6b52f..240413b 100644 --- a/javascriptlint/conf.py +++ b/javascriptlint/conf.py @@ -4,6 +4,7 @@ import fs import util +import version import warnings _DISABLED_WARNINGS = ( @@ -100,7 +101,7 @@ def _getwarningsconf(): # or "+process Folder\Path\*.htm". # """ % { - 'version': '', # TODO + 'version': version.version, 'warnings': _getwarningsconf(), } diff --git a/javascriptlint/jsl.py b/javascriptlint/jsl.py index f498914..cf5d8ec 100755 --- a/javascriptlint/jsl.py +++ b/javascriptlint/jsl.py @@ -14,6 +14,7 @@ import jsparse import lint import util +import version _lint_results = { 'warnings': 0, @@ -48,8 +49,7 @@ def _resolve_paths(path, recurse): return paths or [path] def printlogo(): - # TODO: Print version number. - print "JavaScript Lint" + print "JavaScript Lint %s" % version.version print "Developed by Matthias Miller (http://www.JavaScriptLint.com)" def _profile_enabled(func, *args, **kwargs): diff --git a/javascriptlint/version.py b/javascriptlint/version.py new file mode 100644 index 0000000..46d2b1d --- /dev/null +++ b/javascriptlint/version.py @@ -0,0 +1,2 @@ + +version = '0.5.0/r9999' From eba1b716126193854c6c5a3559ac2b70b3c129aa Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Thu, 3 Oct 2013 19:39:07 +0000 Subject: [PATCH 30/59] r331: Handle Ctrl+C. --- javascriptlint/jsl.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/javascriptlint/jsl.py b/javascriptlint/jsl.py index cf5d8ec..4230e40 100755 --- a/javascriptlint/jsl.py +++ b/javascriptlint/jsl.py @@ -66,7 +66,7 @@ def _profile_enabled(func, *args, **kwargs): def _profile_disabled(func, *args, **kwargs): func(*args, **kwargs) -def main(): +def _main(): parser = OptionParser(usage="%prog [options] [files]") add = parser.add_option add("--conf", dest="conf", metavar="CONF", @@ -154,6 +154,12 @@ def main(): sys.exit(1) sys.exit(0) +def main(): + try: + _main() + except KeyboardInterrupt: + raise SystemExit(130) + if __name__ == '__main__': main() From 3e108b18e0cd9f3a123ea4f33bdba66a3b51831a Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Thu, 3 Oct 2013 19:48:25 +0000 Subject: [PATCH 31/59] r332: Gracefully handle IO errors. --- javascriptlint/lint.py | 7 ++++++- javascriptlint/warnings.py | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/javascriptlint/lint.py b/javascriptlint/lint.py index 9ffb4d3..e35432e 100644 --- a/javascriptlint/lint.py +++ b/javascriptlint/lint.py @@ -302,8 +302,13 @@ def _lint_error(*args): return lint_cache[normpath] if printpaths: print normpath - contents = fs.readfile(path, encoding) + lint_cache[normpath] = _Script() + try: + contents = fs.readfile(path, encoding) + except IOError, error: + _lint_error(0, 0, 'io_error', unicode(error)) + return lint_cache[normpath] script_parts = [] if kind == 'js': diff --git a/javascriptlint/warnings.py b/javascriptlint/warnings.py index 569a009..37cfd50 100644 --- a/javascriptlint/warnings.py +++ b/javascriptlint/warnings.py @@ -114,6 +114,7 @@ def _get_assigned_lambda(node): 'expected_tok': 'expected token: {token}', 'unexpected_char': 'unexpected character: {char}', 'unexpected_eof': 'unexpected end of file', + 'io_error': '{error}', } def format_error(errname, **errargs): From 59b0fc7799bec3923f4873d1543aff5be9c1dbb5 Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Thu, 3 Oct 2013 20:13:37 +0000 Subject: [PATCH 32/59] r333: Fix parsing numbers with exponents. --- jsengine/tokenizer/__init__.py | 3 ++- test.py | 4 ++++ tests/bugs/numbers.js | 5 +++++ 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 tests/bugs/numbers.js diff --git a/jsengine/tokenizer/__init__.py b/jsengine/tokenizer/__init__.py index b905038..38c0a32 100644 --- a/jsengine/tokenizer/__init__.py +++ b/jsengine/tokenizer/__init__.py @@ -397,7 +397,8 @@ def _next(self, parse_regexp=False): if stream.readif(1, 'eE'): stream.readif(1, '+-') - stream.require(_DIGITS) + if not stream.readif(1, _DIGITS): + raise JSSyntaxError(stream.getpos(), 'syntax_error') while stream.readif(1, _DIGITS): pass diff --git a/test.py b/test.py index 9afe03f..857a462 100755 --- a/test.py +++ b/test.py @@ -105,6 +105,10 @@ def write_message(self, msg): def _get_python_modules(dir_): for root, dirs, files in os.walk(dir_): + for exclude in ('build', 'dist'): + if exclude in dirs: + build.remove(exclude) + if '.svn' in dirs: dirs.remove('.svn') for name in files: diff --git a/tests/bugs/numbers.js b/tests/bugs/numbers.js new file mode 100644 index 0000000..4575a85 --- /dev/null +++ b/tests/bugs/numbers.js @@ -0,0 +1,5 @@ +function number() { + var i = 1.1e10; + i = 1.1e+10; + i = 1.1e-10; +} From d30fc888e6c7327d149378db9288d1764f2f332c Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Tue, 8 Oct 2013 16:36:44 +0000 Subject: [PATCH 33/59] r334: Change the parser to only use offsets internally and to convert offsets to line/col only when reporting errors. --- javascriptlint/htmlparse.py | 25 ++- javascriptlint/jsparse.py | 102 ++++++----- javascriptlint/lint.py | 98 ++++++----- javascriptlint/warnings.py | 2 +- jsengine/__init__.py | 8 +- jsengine/parser/__init__.py | 300 ++++++++++++++++----------------- jsengine/structs.py | 22 +-- jsengine/tokenizer/__init__.py | 76 ++++----- test.py | 2 +- 9 files changed, 318 insertions(+), 317 deletions(-) diff --git a/javascriptlint/htmlparse.py b/javascriptlint/htmlparse.py index 5d38bce..6312831 100644 --- a/javascriptlint/htmlparse.py +++ b/javascriptlint/htmlparse.py @@ -2,18 +2,25 @@ import HTMLParser import unittest +from jsengine.structs import NodePos, NodePositions + class _Parser(HTMLParser.HTMLParser): def __init__(self): HTMLParser.HTMLParser.__init__(self) self._tags = [] + self._node_positions = None + + def feed(self, data): + # Reset line numbers whenever we get data. + self._node_positions = None + HTMLParser.HTMLParser.feed(self, data) def handle_starttag(self, tag, attributes): if tag.lower() == 'script': attr = dict(attributes) self._tags.append({ 'type': 'start', - 'lineno': self.lineno, - 'offset': self.offset, + 'offset': self._getoffset(), 'len': len(self.get_starttag_text()), 'attr': attr }) @@ -22,8 +29,7 @@ def handle_endtag(self, tag): if tag.lower() == 'script': self._tags.append({ 'type': 'end', - 'lineno': self.lineno, - 'offset': self.offset, + 'offset': self._getoffset(), }) def unknown_decl(self, data): @@ -33,9 +39,16 @@ def unknown_decl(self, data): def gettags(self): return self._tags + def _getoffset(self): + # htmlparse returns 1-based line numbers. Calculate the offset of the + # script's contents. + if self._node_positions is None: + self._node_positions = NodePositions(self.rawdata) + pos = NodePos(self.lineno - 1, self.offset) + return self._node_positions.to_offset(pos) + + def findscripttags(s): - """ Note that the lineno is 1-based. - """ parser = _Parser() parser.feed(s) parser.close() diff --git a/javascriptlint/jsparse.py b/javascriptlint/jsparse.py index e263945..5249f3f 100644 --- a/javascriptlint/jsparse.py +++ b/javascriptlint/jsparse.py @@ -16,7 +16,8 @@ def isvalidversion(jsversion): return True return jsengine.parser.is_valid_version(jsversion.version) -def findpossiblecomments(script, node_positions): +def findpossiblecomments(script, script_offset): + assert not script_offset is None pos = 0 single_line_re = r"//[^\r\n]*" multi_line_re = r"/\*(.*?)\*/" @@ -41,38 +42,34 @@ def findpossiblecomments(script, node_positions): start_offset = match.start() end_offset = match.end()-1 - start_pos = node_positions.from_offset(start_offset) - end_pos = node_positions.from_offset(end_offset) - comment_node = ParseNode(kind.COMMENT, opcode, start_pos, end_pos, - comment_text, []) + comment_node = ParseNode(kind.COMMENT, opcode, + script_offset + start_offset, + script_offset + end_offset, comment_text, []) comments.append(comment_node) # Start searching immediately after the start of the comment in case # this one was within a string or a regexp. pos = match.start()+1 -def parse(script, jsversion, error_callback, startpos=None): - """ All node positions will be relative to startpos. This allows scripts - to be embedded in a file (for example, HTML). +def parse(script, jsversion, error_callback, start_offset=0): + """ All node positions will be relative to start_offset. This allows + scripts to be embedded in a file (for example, HTML). """ - startpos = startpos or NodePos(0, 0) + assert not start_offset is None jsversion = jsversion or JSVersion.default() assert isvalidversion(jsversion), jsversion if jsversion.e4x: - error_callback(startpos.line, startpos.col, 'e4x_deprecated', {}) + error_callback(start_offset, 'e4x_deprecated', {}) return jsengine.parser.parse(script, jsversion.version, - error_callback, - startpos) + error_callback, start_offset) -def filtercomments(possible_comments, node_positions, root_node): +def filtercomments(possible_comments, root_node): comment_ignore_ranges = NodeRanges() def process(node): if node.kind == tok.STRING or \ (node.kind == tok.OBJECT and node.opcode == op.REGEXP): - start_offset = node_positions.to_offset(node.start_pos()) - end_offset = node_positions.to_offset(node.end_pos()) - comment_ignore_ranges.add(start_offset, end_offset) + comment_ignore_ranges.add(node.start_offset, node.end_offset) for kid in node.kids: if kid: process(kid) @@ -80,25 +77,22 @@ def process(node): comments = [] for comment in possible_comments: - start_offset = node_positions.to_offset(comment.start_pos()) - end_offset = node_positions.to_offset(comment.end_pos()) - if comment_ignore_ranges.has(start_offset): + if comment_ignore_ranges.has(comment.start_offset): continue - comment_ignore_ranges.add(start_offset, end_offset) + comment_ignore_ranges.add(comment.start_offset, comment.end_offset) comments.append(comment) return comments -def findcomments(script, root_node, start_pos=None): - node_positions = NodePositions(script, start_pos) - possible_comments = findpossiblecomments(script, node_positions) - return filtercomments(possible_comments, node_positions, root_node) +def findcomments(script, root_node, start_offset=0): + possible_comments = findpossiblecomments(script, start_offset) + return filtercomments(possible_comments, root_node) def is_compilable_unit(script, jsversion): jsversion = jsversion or JSVersion.default() assert isvalidversion(jsversion) return jsengine.parser.is_compilable_unit(script, jsversion.version) -def _dump_node(node, depth=0): +def _dump_node(node, node_positions, depth=0): if node is None: print ' '*depth, print '(None)' @@ -107,7 +101,8 @@ def _dump_node(node, depth=0): print ' '*depth, print '%s, %s' % (repr(node.kind), repr(node.opcode)) print ' '*depth, - print '%s - %s' % (node.start_pos(), node.end_pos()) + print '%s - %s' % (node_positions.from_offset(node.start_offset), + node_positions.from_offset(node.end_offset)) if hasattr(node, 'atom'): print ' '*depth, print 'atom: %s' % node.atom @@ -116,13 +111,14 @@ def _dump_node(node, depth=0): print '(no semicolon)' print for node in node.kids: - _dump_node(node, depth+1) + _dump_node(node, node_positions, depth+1) def dump_tree(script): def error_callback(line, col, msg, msg_args): print '(%i, %i): %s', (line, col, msg) node = parse(script, None, error_callback) - _dump_node(node) + node_positions = NodePositions(script) + _dump_node(node, node_positions) class TestComments(unittest.TestCase): def _test(self, script, expected_comments): @@ -230,42 +226,42 @@ def test(self): class TestLineOffset(unittest.TestCase): def testErrorPos(self): - def geterror(script, startpos): + def geterror(script, start_offset): errors = [] - def onerror(line, col, msg, msg_args): - errors.append((line, col, msg, msg_args)) - parse(script, None, onerror, startpos) + def onerror(offset, msg, msg_args): + errors.append((offset, msg, msg_args)) + parse(script, None, onerror, start_offset) self.assertEquals(len(errors), 1) return errors[0] - self.assertEquals(geterror(' ?', None), (0, 1, 'syntax_error', {})) - self.assertEquals(geterror('\n ?', None), (1, 1, 'syntax_error', {})) - self.assertEquals(geterror(' ?', NodePos(1, 1)), (1, 2, 'syntax_error', {})) - self.assertEquals(geterror('\n ?', NodePos(1, 1)), (2, 1, 'syntax_error', {})) + self.assertEquals(geterror(' ?', 0), (1, 'syntax_error', {})) + self.assertEquals(geterror('\n ?', 0), (2, 'syntax_error', {})) + self.assertEquals(geterror(' ?', 2), (3, 'syntax_error', {})) + self.assertEquals(geterror('\n ?', 2), (4, 'syntax_error', {})) def testNodePos(self): - def getnodepos(script, startpos): - root = parse(script, None, None, startpos) + def getnodepos(script, start_offset): + root = parse(script, None, None, start_offset) self.assertEquals(root.kind, tok.LC) var, = root.kids self.assertEquals(var.kind, tok.VAR) - return var.start_pos() - self.assertEquals(getnodepos('var x;', None), NodePos(0, 0)) - self.assertEquals(getnodepos(' var x;', None), NodePos(0, 1)) - self.assertEquals(getnodepos('\n\n var x;', None), NodePos(2, 1)) - self.assertEquals(getnodepos('var x;', NodePos(3, 4)), NodePos(3, 4)) - self.assertEquals(getnodepos(' var x;', NodePos(3, 4)), NodePos(3, 5)) - self.assertEquals(getnodepos('\n\n var x;', NodePos(3, 4)), NodePos(5, 1)) + return var.start_offset + self.assertEquals(getnodepos('var x;', 0), 0) + self.assertEquals(getnodepos(' var x;', 0), 1) + self.assertEquals(getnodepos('\n\n var x;', 0), 3) + self.assertEquals(getnodepos('var x;', 7), 7) + self.assertEquals(getnodepos(' var x;', 7), 8) + self.assertEquals(getnodepos('\n\n var x;', 7), 10) def testComments(self): - def testcomment(comment, startpos, expectedpos): + def testcomment(comment, startpos, expected_offset): root = parse(comment, None, None, startpos) comment, = findcomments(comment, root, startpos) - self.assertEquals(comment.start_pos(), expectedpos) + self.assertEquals(comment.start_offset, expected_offset) for comment in ('/*comment*/', '//comment'): - testcomment(comment, None, NodePos(0, 0)) - testcomment(' %s' % comment, None, NodePos(0, 1)) - testcomment('\n\n %s' % comment, None, NodePos(2, 1)) - testcomment('%s' % comment, NodePos(3, 4), NodePos(3, 4)) - testcomment(' %s' % comment, NodePos(3, 4), NodePos(3, 5)) - testcomment('\n\n %s' % comment, NodePos(3, 4), NodePos(5, 1)) + testcomment(comment, 0, 0) + testcomment(' %s' % comment, 0, 1) + testcomment('\n\n %s' % comment, 0, 3) + testcomment('%s' % comment, 7, 7) + testcomment(' %s' % comment, 7, 8) + testcomment('\n\n %s' % comment, 7, 10) if __name__ == '__main__': unittest.main() diff --git a/javascriptlint/lint.py b/javascriptlint/lint.py index e35432e..e08e852 100644 --- a/javascriptlint/lint.py +++ b/javascriptlint/lint.py @@ -140,7 +140,7 @@ def get_identifier_warnings(self): # sorted by node position. unreferenced = [(key[0], key[1], node) for key, node in unreferenced.items()] - unreferenced.sort(key=lambda x: x[2].start_pos()) + unreferenced.sort(key=lambda x: x[2].start_offset) return { 'unreferenced': unreferenced, @@ -214,8 +214,8 @@ def find_scope(self, node): # Conditionally add it to an inner scope. assert self._node - if (node.start_pos() >= self._node.start_pos() and \ - node.end_pos() <= self._node.end_pos()): + if (node.start_offset >= self._node.start_offset and \ + node.end_offset <= self._node.end_offset): return self class _Script: @@ -245,7 +245,6 @@ def _findglobal(self, name, searched): def _findhtmlscripts(contents, default_version): starttag = None - nodepos = jsparse.NodePositions(contents) for tag in htmlparse.findscripttags(contents): if tag['type'] == 'start': # Ignore nested start tags. @@ -265,13 +264,9 @@ def _findhtmlscripts(contents, default_version): # htmlparse returns 1-based line numbers. Calculate the # position of the script's contents. - tagpos = jsparse.NodePos(starttag['lineno']-1, starttag['offset']) - tagoffset = nodepos.to_offset(tagpos) - startoffset = tagoffset + starttag['len'] - startpos = nodepos.from_offset(startoffset) - endpos = jsparse.NodePos(tag['lineno']-1, tag['offset']) - endoffset = nodepos.to_offset(endpos) - script = contents[startoffset:endoffset] + start_offset = starttag['offset'] + starttag['len'] + end_offset = tag['offset'] + script = contents[start_offset:end_offset] if not jsparse.isvalidversion(starttag['jsversion']) or \ jsparse.is_compilable_unit(script, starttag['jsversion']): @@ -279,7 +274,7 @@ def _findhtmlscripts(contents, default_version): yield { 'type': 'inline', 'jsversion': starttag['jsversion'], - 'pos': startpos, + 'offset': start_offset, 'contents': script, } starttag = None @@ -294,8 +289,9 @@ def import_script(import_path, jsversion): import_path = import_path.replace('\\', os.sep) import_path = os.path.join(os.path.dirname(path), import_path) return lint_file(import_path, 'js', jsversion, encoding) - def _lint_error(*args): - return lint_error(normpath, *args) + def _lint_error(offset, errname, errdesc): + pos = node_positions.from_offset(offset) + return lint_error(normpath, pos.line, pos.col, errname, errdesc) normpath = fs.normpath(path) if normpath in lint_cache: @@ -307,12 +303,13 @@ def _lint_error(*args): try: contents = fs.readfile(path, encoding) except IOError, error: - _lint_error(0, 0, 'io_error', unicode(error)) + lint_error(normpath, 0, 0, 'io_error', unicode(error)) return lint_cache[normpath] + node_positions = jsparse.NodePositions(contents) script_parts = [] if kind == 'js': - script_parts.append((None, jsversion or conf['default-version'], contents)) + script_parts.append((0, jsversion or conf['default-version'], contents)) elif kind == 'html': assert jsversion is None for script in _findhtmlscripts(contents, conf['default-version']): @@ -324,7 +321,7 @@ def _lint_error(*args): other = import_script(script['src'], script['jsversion']) lint_cache[normpath].importscript(other) elif script['type'] == 'inline': - script_parts.append((script['pos'], script['jsversion'], + script_parts.append((script['offset'], script['jsversion'], script['contents'])) else: assert False, 'Invalid internal script type %s' % \ @@ -343,18 +340,18 @@ def _lint_error(*args): else: lint_file(path, 'js', None, encoding) -def _lint_script_part(scriptpos, jsversion, script, script_cache, conf, +def _lint_script_part(script_offset, jsversion, script, script_cache, conf, ignores, report_native, report_lint, import_callback): - def parse_error(row, col, msg, msg_args): + def parse_error(offset, msg, msg_args): if not msg in ('anon_no_return_value', 'no_return_value', 'redeclared_var', 'var_hides_arg'): - parse_errors.append((jsparse.NodePos(row, col), msg, msg_args)) + parse_errors.append((offset, msg, msg_args)) - def report(node, errname, pos=None, **errargs): + def report(node, errname, offset=0, **errargs): if errname == 'empty_statement' and node.kind == tok.LC: for pass_ in passes: - if pass_.start_pos() > node.start_pos() and \ - pass_.end_pos() < node.end_pos(): + if pass_.start_offset > node.start_offset and \ + pass_.end_offset < node.end_offset: passes.remove(pass_) return @@ -363,12 +360,12 @@ def report(node, errname, pos=None, **errargs): # the next case/default. assert node.kind in (tok.CASE, tok.DEFAULT) prevnode = node.parent.kids[node.node_index-1] - expectedfallthru = prevnode.end_pos(), node.start_pos() + expectedfallthru = prevnode.end_offset, node.start_offset elif errname == 'missing_break_for_last_case': # Find the end of the current case/default and the end of the # switch. assert node.parent.kind == tok.LC - expectedfallthru = node.end_pos(), node.parent.end_pos() + expectedfallthru = node.end_offset, node.parent.end_offset else: expectedfallthru = None @@ -377,11 +374,11 @@ def report(node, errname, pos=None, **errargs): for fallthru in fallthrus: # Look for a fallthru between the end of the current case or # default statement and the beginning of the next token. - if fallthru.start_pos() > start and fallthru.end_pos() < end: + if fallthru.start_offset > start and fallthru.end_offset < end: fallthrus.remove(fallthru) return - report_lint(node, errname, pos, **errargs) + report_lint(node, errname, offset, **errargs) parse_errors = [] declares = [] @@ -390,8 +387,7 @@ def report(node, errname, pos=None, **errargs): fallthrus = [] passes = [] - node_positions = jsparse.NodePositions(script, scriptpos) - possible_comments = jsparse.findpossiblecomments(script, node_positions) + possible_comments = jsparse.findpossiblecomments(script, script_offset) # Check control comments for the correct version. It may be this comment # isn't a valid comment (for example, it might be inside a string literal) @@ -410,18 +406,18 @@ def report(node, errname, pos=None, **errargs): report(node, 'unsupported_version', version=parms) if not jsparse.isvalidversion(jsversion): - report_lint(jsversionnode, 'unsupported_version', scriptpos, + report_lint(jsversionnode, 'unsupported_version', script_offset, version=jsversion.version) return - root = jsparse.parse(script, jsversion, parse_error, scriptpos) + root = jsparse.parse(script, jsversion, parse_error, script_offset) if not root: # Report errors and quit. - for pos, msg, msg_args in parse_errors: - report_native(pos, msg, msg_args) + for offset, msg, msg_args in parse_errors: + report_native(offset, msg, msg_args) return - comments = jsparse.filtercomments(possible_comments, node_positions, root) + comments = jsparse.filtercomments(possible_comments, root) if jsversionnode is not None and jsversionnode not in comments: # TODO @@ -449,7 +445,7 @@ def report(node, errname, pos=None, **errargs): start_ignore = node elif keyword == 'end': if start_ignore: - ignores.append((start_ignore.start_pos(), node.end_pos())) + ignores.append((start_ignore.start_offset, node.end_offset)) start_ignore = None else: report(node, 'mismatch_ctrl_comments') @@ -471,8 +467,8 @@ def report(node, errname, pos=None, **errargs): # Report at the actual error of the location. Add two # characters for the opening two characters. if nested_comment >= 0: - pos = node_positions.from_offset(node_positions.to_offset(comment.start_pos()) + 2 + nested_comment) - report(comment, 'nested_comment', pos=pos) + offset = comment.start_offset + 2 + nested_comment + report(comment, 'nested_comment', offset=offset) if comment.atom.lower().startswith('jsl:'): report(comment, 'jsl_cc_not_understood') elif comment.atom.startswith('@'): @@ -481,8 +477,8 @@ def report(node, errname, pos=None, **errargs): report(start_ignore, 'mismatch_ctrl_comments') # Wait to report parse errors until loading jsl:ignore directives. - for pos, msg in parse_errors: - report_native(pos, msg) + for offset, msg in parse_errors: + report_native(offset, msg) # Find all visitors and convert them into "onpush" callbacks that call "report" visitors = { @@ -516,15 +512,15 @@ def report(node, errname, pos=None, **errargs): unused_scope.set_unused(name, node) def _lint_script_parts(script_parts, script_cache, lint_error, conf, import_callback): - def report_lint(node, errname, pos=None, **errargs): + def report_lint(node, errname, offset=0, **errargs): errdesc = warnings.format_error(errname, **errargs) - _report(pos or node.start_pos(), errname, errdesc, True) + _report(offset or node.start_offset, errname, errdesc, True) - def report_native(pos, errname, errargs): + def report_native(offset, errname, errargs): errdesc = warnings.format_error(errname, **errargs) - _report(pos, errname, errdesc, False) + _report(offset, errname, errdesc, False) - def _report(pos, errname, errdesc, require_key): + def _report(offset, errname, errdesc, require_key): try: if not conf[errname]: return @@ -533,14 +529,14 @@ def _report(pos, errname, errdesc, require_key): raise for start, end in ignores: - if pos >= start and pos <= end: + if offset >= start and offset <= end: return - return lint_error(pos.line, pos.col, errname, errdesc) + return lint_error(offset, errname, errdesc) - for scriptpos, jsversion, script in script_parts: + for script_offset, jsversion, script in script_parts: ignores = [] - _lint_script_part(scriptpos, jsversion, script, script_cache, conf, ignores, + _lint_script_part(script_offset, jsversion, script, script_cache, conf, ignores, report_native, report_lint, import_callback) scope = script_cache.scope @@ -576,10 +572,10 @@ def onpush(node): # TODO: This is ugly hardcoding to improve the error positioning of # "missing_semicolon" errors. if visitor.warning in ('missing_semicolon', 'missing_semicolon_for_lambda'): - pos = warning.node.end_pos() + offset = warning.node.end_offset else: - pos = None - report(warning.node, visitor.warning, pos=pos, **warning.errargs) + offset = None + report(warning.node, visitor.warning, offset=offset, **warning.errargs) return onpush def _warn_or_declare(scope, name, type_, node, report): diff --git a/javascriptlint/warnings.py b/javascriptlint/warnings.py index 37cfd50..4933409 100644 --- a/javascriptlint/warnings.py +++ b/javascriptlint/warnings.py @@ -584,7 +584,7 @@ def is_return_without_val(node): if filter(is_return_with_val, exit_points): # If the function returns a value, find all returns without a value. returns = filter(is_return_without_val, exit_points) - returns.sort(key=lambda node: node.start_pos()) + returns.sort(key=lambda node: node.start_offset) if returns: raise LintWarning(returns[0], name=name) # Warn if the function sometimes exits naturally. diff --git a/jsengine/__init__.py b/jsengine/__init__.py index dbe4dd1..66c6bf3 100644 --- a/jsengine/__init__.py +++ b/jsengine/__init__.py @@ -10,12 +10,12 @@ ) class JSSyntaxError(BaseException): - def __init__(self, pos, msg, msg_args=None): + def __init__(self, offset, msg, msg_args=None): assert msg in _MESSAGES, msg - self.pos = pos + self.offset = offset self.msg = msg self.msg_args = msg_args or {} def __unicode__(self): - return '%s: %s' % (self.pos, self.msg) + return '%s: %s' % (self.offset, self.msg) def __repr__(self): - return 'JSSyntaxError(%r, %r, %r)' % (self.pos, self.msg. self.msg_args) + return 'JSSyntaxError(%r, %r, %r)' % (self.offset, self.msg. self.msg_args) diff --git a/jsengine/parser/__init__.py b/jsengine/parser/__init__.py index 80b9fcf..2d4d0a9 100644 --- a/jsengine/parser/__init__.py +++ b/jsengine/parser/__init__.py @@ -22,16 +22,16 @@ "1.7", ] -def _auto_semicolon(t, kind_, op_, startpos, endpos, atom, kids): +def _auto_semicolon(t, kind_, op_, start_offset, end_offset, atom, kids): nosemi = False if t.peek_sameline().tok not in (tok.EOF, tok.EOL, tok.RBRACE): x = t.advance() if x.tok != tok.SEMI: - raise JSSyntaxError(x.startpos, 'semi_before_stmnt') - endpos = x.endpos + raise JSSyntaxError(x.start_offset, 'semi_before_stmnt') + end_offset = x.end_offset else: nosemi = True - return ParseNode(kind_, op_, startpos, endpos, atom, kids, nosemi) + return ParseNode(kind_, op_, start_offset, end_offset, atom, kids, nosemi) def _function_arglist(t): fn_args = [] @@ -39,8 +39,8 @@ def _function_arglist(t): while True: x = t.expect(tok.NAME) fn_args.append(ParseNode(kind.NAME, op.ARGNAME, - x.startpos, - x.endpos, x.atom, [])) + x.start_offset, + x.end_offset, x.atom, [])) if t.peek().tok == tok.COMMA: t.advance() else: @@ -50,23 +50,23 @@ def _function_arglist(t): def _primary_expression(t): x = t.next_withregexp() if x.tok == tok.THIS: - return ParseNode(kind.PRIMARY, op.THIS, x.startpos, x.endpos, None, []) + return ParseNode(kind.PRIMARY, op.THIS, x.start_offset, x.end_offset, None, []) elif x.tok == tok.NAME: - return ParseNode(kind.NAME, op.NAME, x.startpos, x.endpos, x.atom, [None]) + return ParseNode(kind.NAME, op.NAME, x.start_offset, x.end_offset, x.atom, [None]) elif x.tok == tok.NULL: - return ParseNode(kind.PRIMARY, op.NULL, x.startpos, x.endpos, None, []) + return ParseNode(kind.PRIMARY, op.NULL, x.start_offset, x.end_offset, None, []) elif x.tok == tok.TRUE: - return ParseNode(kind.PRIMARY, op.TRUE, x.startpos, x.endpos, None, []) + return ParseNode(kind.PRIMARY, op.TRUE, x.start_offset, x.end_offset, None, []) elif x.tok == tok.FALSE: - return ParseNode(kind.PRIMARY, op.FALSE, x.startpos, x.endpos, None, []) + return ParseNode(kind.PRIMARY, op.FALSE, x.start_offset, x.end_offset, None, []) elif x.tok == tok.STRING: - return ParseNode(kind.STRING, op.STRING, x.startpos, x.endpos, x.atom, []) + return ParseNode(kind.STRING, op.STRING, x.start_offset, x.end_offset, x.atom, []) elif x.tok == tok.REGEXP: - return ParseNode(kind.OBJECT, op.REGEXP, x.startpos, x.endpos, None, []) + return ParseNode(kind.OBJECT, op.REGEXP, x.start_offset, x.end_offset, None, []) elif x.tok == tok.NUMBER: - return ParseNode(kind.NUMBER, None, x.startpos, x.endpos, x.atom, []) + return ParseNode(kind.NUMBER, None, x.start_offset, x.end_offset, x.atom, []) elif x.tok == tok.LBRACKET: - startpos = x.startpos + start_offset = x.start_offset items = [] end_comma = None if t.peek().tok != tok.RBRACKET: @@ -83,18 +83,18 @@ def _primary_expression(t): # Expect a comma and use it if the value was missing. x = t.expect(tok.COMMA) comma = ParseNode(kind.COMMA, None, - x.startpos, x.endpos, None, []) + x.start_offset, x.end_offset, None, []) items[-1] = items[-1] or comma # Check for the end. if t.peek().tok == tok.RBRACKET: end_comma = comma break - endpos = t.expect(tok.RBRACKET).endpos - return ParseNode(kind.RB, None, startpos, endpos, None, items, + end_offset = t.expect(tok.RBRACKET).end_offset + return ParseNode(kind.RB, None, start_offset, end_offset, None, items, end_comma=end_comma) elif x.tok == tok.LBRACE: - startpos = x.startpos + start_offset = x.start_offset kids = [] # TODO: get/set end_comma = None @@ -104,50 +104,50 @@ def _primary_expression(t): break elif x.tok == tok.STRING: t.expect(tok.STRING) - key = ParseNode(kind.STRING, None, x.startpos, - x.endpos, x.atom, []) + key = ParseNode(kind.STRING, None, x.start_offset, + x.end_offset, x.atom, []) elif x.tok == tok.NUMBER: t.expect(tok.NUMBER) - key = ParseNode(kind.NUMBER, None, x.startpos, - x.endpos, x.atom, []) + key = ParseNode(kind.NUMBER, None, x.start_offset, + x.end_offset, x.atom, []) else: x = t.expect_identifiername() - key = ParseNode(kind.NAME, None, x.startpos, x.endpos, + key = ParseNode(kind.NAME, None, x.start_offset, x.end_offset, x.atom, []) t.expect(tok.COLON) value = _assignment_expression(t, True) - kids.append(ParseNode(kind.COLON, None, key.startpos, - value.endpos, None, [key, value])) + kids.append(ParseNode(kind.COLON, None, key.start_offset, + value.end_offset, None, [key, value])) if t.peek().tok == tok.COMMA: x = t.advance() end_comma = ParseNode(kind.COMMA, None, - x.startpos, x.endpos, None, []) + x.start_offset, x.end_offset, None, []) else: end_comma = None break - endpos = t.expect(tok.RBRACE).endpos - return ParseNode(kind.RC, None, startpos, endpos, None, kids, + end_offset = t.expect(tok.RBRACE).end_offset + return ParseNode(kind.RC, None, start_offset, end_offset, None, kids, end_comma=end_comma) elif x.tok == tok.LPAREN: - startpos = x.startpos + start_offset = x.start_offset kid = _expression(t, True) - endpos = t.expect(tok.RPAREN).endpos - return ParseNode(kind.RP, None, startpos, endpos, None, [kid]) + end_offset = t.expect(tok.RPAREN).end_offset + return ParseNode(kind.RP, None, start_offset, end_offset, None, [kid]) else: - raise JSSyntaxError(x.startpos, 'syntax_error') + raise JSSyntaxError(x.start_offset, 'syntax_error') def _function_declaration(t, named_opcode): node = _function_expression(t, named_opcode) # Convert anonymous functions in expressions. if node.opcode == op.ANONFUNOBJ: - node = _auto_semicolon(t, kind.SEMI, None, node.startpos, node.endpos, + node = _auto_semicolon(t, kind.SEMI, None, node.start_offset, node.end_offset, None, [node]) return node def _function_expression(t, named_opcode): - startpos = t.expect(tok.FUNCTION).startpos + start_offset = t.expect(tok.FUNCTION).start_offset if t.peek().tok == tok.NAME: fn_name = t.expect(tok.NAME).atom opcode = named_opcode @@ -157,12 +157,12 @@ def _function_expression(t, named_opcode): t.expect(tok.LPAREN) fn_args = _function_arglist(t) t.expect(tok.RPAREN) - fn_body_startpos = t.expect(tok.LBRACE).startpos + fn_body_start_offset = t.expect(tok.LBRACE).start_offset kids = _sourceelements(t, tok.RBRACE) - fn_body_endpos = t.expect(tok.RBRACE).endpos - fn_body = ParseNode(kind.LC, None, fn_body_startpos, - fn_body_endpos, None, kids) - return ParseNode(kind.FUNCTION, opcode, startpos, fn_body.endpos, + fn_body_end_offset = t.expect(tok.RBRACE).end_offset + fn_body = ParseNode(kind.LC, None, fn_body_start_offset, + fn_body_end_offset, None, kids) + return ParseNode(kind.FUNCTION, opcode, start_offset, fn_body.end_offset, fn_name, [fn_body], fn_args=fn_args) def _argument_list(t): @@ -177,17 +177,17 @@ def _argument_list(t): return args def _new_expression(t): - startpos = t.expect(tok.NEW).startpos + start_offset = t.expect(tok.NEW).start_offset expr = _member_expression(t) # If no (), this is a variant of the NewExpression if t.peek().tok == tok.LPAREN: t.expect(tok.LPAREN) args = _argument_list(t) - endpos = t.expect(tok.RPAREN).endpos + end_offset = t.expect(tok.RPAREN).end_offset else: args = [] - endpos = expr.endpos - return ParseNode(kind.NEW, op.NEW, startpos, endpos, + end_offset = expr.end_offset + return ParseNode(kind.NEW, op.NEW, start_offset, end_offset, None, [expr] + args) def _member_expression(t, _recurse=True): @@ -203,13 +203,13 @@ def _member_expression(t, _recurse=True): if t.peek().tok == tok.LBRACKET: t.advance() expr = _expression(t, True) - endpos = t.expect(tok.RBRACKET).endpos - kid = ParseNode(kind.LB, op.GETELEM, kid.startpos, endpos, + end_offset = t.expect(tok.RBRACKET).end_offset + kid = ParseNode(kind.LB, op.GETELEM, kid.start_offset, end_offset, None, [kid, expr]) elif t.peek().tok == tok.DOT: t.advance() expr = t.expect_identifiername() - kid = ParseNode(kind.DOT, op.GETPROP, kid.startpos, expr.endpos, + kid = ParseNode(kind.DOT, op.GETPROP, kid.start_offset, expr.end_offset, expr.atom, [kid]) else: return kid @@ -224,21 +224,21 @@ def _call_expression(t): if x.tok == tok.LPAREN: t.expect(tok.LPAREN) args = _argument_list(t) - endpos = t.expect(tok.RPAREN).endpos - expr = ParseNode(kind.LP, op.CALL, expr.startpos, - endpos, None, [expr] + args) + end_offset = t.expect(tok.RPAREN).end_offset + expr = ParseNode(kind.LP, op.CALL, expr.start_offset, + end_offset, None, [expr] + args) elif x.tok == tok.LBRACKET: t.expect(tok.LBRACKET) lookup = _expression(t, True) - endpos = t.expect(tok.RBRACKET).endpos + end_offset = t.expect(tok.RBRACKET).end_offset expr = ParseNode(kind.LB, op.GETELEM, - expr.startpos, endpos, + expr.start_offset, end_offset, None, [expr, lookup]) elif x.tok == tok.DOT: t.expect(tok.DOT) lookup = t.expect_identifiername() expr = ParseNode(kind.DOT, op.GETPROP, - expr.startpos, lookup.endpos, + expr.start_offset, lookup.end_offset, lookup.atom, [expr]) else: return expr @@ -251,17 +251,17 @@ def _lefthandside_expression(t): def _postfix_expression(t): kid = _lefthandside_expression(t) if t.peek_sameline().tok == tok.INC: - endpos = t.expect(tok.INC).endpos + end_offset = t.expect(tok.INC).end_offset if kid.kind == kind.DOT and kid.opcode == op.GETPROP: opcode = op.PROPINC else: opcode = op.NAMEINC return ParseNode(kind.INC, opcode, - kid.startpos, endpos, None, [kid]) + kid.start_offset, end_offset, None, [kid]) elif t.peek_sameline().tok == tok.DEC: - endpos = t.expect(tok.DEC).endpos + end_offset = t.expect(tok.DEC).end_offset return ParseNode(kind.DEC, op.NAMEDEC, - kid.startpos, endpos, None, [kid]) + kid.start_offset, end_offset, None, [kid]) else: return kid @@ -280,9 +280,9 @@ def _unary_expression(t): x = t.peek() if x.tok in _UNARY: kind_, op_ = _UNARY[x.tok] - startpos = t.advance().startpos + start_offset = t.advance().start_offset kid = _unary_expression(t) - return ParseNode(kind_, op_, startpos, kid.endpos, None, [kid]) + return ParseNode(kind_, op_, start_offset, kid.end_offset, None, [kid]) else: return _postfix_expression(t) @@ -300,7 +300,7 @@ def _binary_expression(t, dict_, child_expr_callback): t.advance() kids.append(child_expr_callback(t)) expr = ParseNode(kind_, op_, - kids[0].startpos, kids[1].endpos, + kids[0].start_offset, kids[1].end_offset, None, kids) _MULTIPLICATIVE = { @@ -359,7 +359,7 @@ def _bitwise_and_expression(t, allowin): t.advance() right = _equality_expression(t, allowin) left = ParseNode(kind.BITAND, op.BITAND, - left.startpos, right.endpos, + left.start_offset, right.end_offset, None, [left, right]) return left @@ -369,7 +369,7 @@ def _bitwise_xor_expression(t, allowin): t.advance() right = _bitwise_and_expression(t, allowin) left = ParseNode(kind.BITXOR, op.BITXOR, - left.startpos, right.endpos, + left.start_offset, right.end_offset, None, [left, right]) return left @@ -379,7 +379,7 @@ def _bitwise_or_expression(t, allowin): t.advance() right = _bitwise_xor_expression(t, allowin) left = ParseNode(kind.BITOR, op.BITOR, - left.startpos, right.endpos, + left.start_offset, right.end_offset, None, [left, right]) return left @@ -396,7 +396,7 @@ def _logical_and_expression(t, allowin): right = exprs.pop() left = exprs[-1] exprs[-1] = ParseNode(kind.AND, op.AND, - left.startpos, right.endpos, + left.start_offset, right.end_offset, None, [left, right]) return exprs[0] @@ -413,7 +413,7 @@ def _logical_or_expression(t, allowin): right = exprs.pop() left = exprs[-1] exprs[-1] = ParseNode(kind.OR, op.OR, - left.startpos, right.endpos, + left.start_offset, right.end_offset, None, [left, right]) return exprs[0] @@ -425,7 +425,7 @@ def _conditional_expression(t, allowin): t.expect(tok.COLON) else_ = _assignment_expression(t, allowin) return ParseNode(kind.HOOK, None, - kid.startpos, else_.endpos, + kid.start_offset, else_.end_offset, None, [kid, if_, else_]) else: return kid @@ -463,12 +463,12 @@ def _assignment_expression(t, allowin): assert kid.opcode == op.CALL kid.opcode = op.SETCALL else: - raise JSSyntaxError(left.startpos, 'invalid_assign') + raise JSSyntaxError(left.start_offset, 'invalid_assign') kind_, op_ = _ASSIGNS[t.peek().tok] t.advance() right = _assignment_expression(t, allowin) return ParseNode(kind_, op_, - left.startpos, right.endpos, None, [left, right]) + left.start_offset, right.end_offset, None, [left, right]) else: return left @@ -479,8 +479,8 @@ def _expression(t, allowin): t.advance() items.append(_assignment_expression(t, allowin)) if len(items) > 1: - return ParseNode(kind.COMMA, None, items[0].startpos, - items[-1].endpos, None, items) + return ParseNode(kind.COMMA, None, items[0].start_offset, + items[-1].end_offset, None, items) else: return items[0] @@ -493,8 +493,8 @@ def _variable_declaration(t, allowin): t.advance() value = _assignment_expression(t, allowin) nodes.append(ParseNode(kind.NAME, op.SETNAME if value else op.NAME, - x.startpos, - value.endpos if value else x.endpos, + x.start_offset, + value.end_offset if value else x.end_offset, x.atom, [value])) if t.peek().tok == tok.COMMA: @@ -504,27 +504,27 @@ def _variable_declaration(t, allowin): def _block_statement(t): kids = [] - startpos = t.expect(tok.LBRACE).startpos + start_offset = t.expect(tok.LBRACE).start_offset while t.peek().tok != tok.RBRACE: kids.append(_statement(t)) - endpos = t.expect(tok.RBRACE).endpos - return ParseNode(kind.LC, None, startpos, endpos, None, kids) + end_offset = t.expect(tok.RBRACE).end_offset + return ParseNode(kind.LC, None, start_offset, end_offset, None, kids) def _empty_statement(t): # EMPTY STATEMENT x = t.expect(tok.SEMI) - return ParseNode(kind.SEMI, None, x.startpos, x.endpos, None, [None]) + return ParseNode(kind.SEMI, None, x.start_offset, x.end_offset, None, [None]) def _var_statement(t): # VARIABLE STATEMENT - startpos = t.expect(tok.VAR).startpos + start_offset = t.expect(tok.VAR).start_offset nodes = _variable_declaration(t, True) return _auto_semicolon(t, kind.VAR, op.DEFVAR, - startpos, nodes[-1].endpos, None, nodes) + start_offset, nodes[-1].end_offset, None, nodes) def _if_statement(t): # IF STATEMENT - startpos = t.expect(tok.IF).startpos + start_offset = t.expect(tok.IF).start_offset t.expect(tok.LPAREN) condition = _expression(t, True) t.expect(tok.RPAREN) @@ -534,38 +534,38 @@ def _if_statement(t): else_body = _statement(t) else: else_body = None - endpos = else_body.endpos if else_body else if_body.endpos - return ParseNode(kind.IF, None, startpos, - endpos, None, [condition, if_body, else_body]) + end_offset = else_body.end_offset if else_body else if_body.end_offset + return ParseNode(kind.IF, None, start_offset, + end_offset, None, [condition, if_body, else_body]) def _do_statement(t): - startpos = t.expect(tok.DO).startpos + start_offset = t.expect(tok.DO).start_offset code = _statement(t) t.expect(tok.WHILE) t.expect(tok.LPAREN) expr = _expression(t, True) endtoken = t.expect(tok.RPAREN) return _auto_semicolon(t, kind.DO, None, - startpos, endtoken.endpos, None, [code, expr]) + start_offset, endtoken.end_offset, None, [code, expr]) def _while_statement(t): - startpos = t.expect(tok.WHILE).startpos + start_offset = t.expect(tok.WHILE).start_offset t.expect(tok.LPAREN) expr = _expression(t, True) t.expect(tok.RPAREN) code = _statement(t) return ParseNode(kind.WHILE, None, - startpos, code.endpos, None, [expr, code]) + start_offset, code.end_offset, None, [expr, code]) def _for_statement(t): - for_startpos = t.expect(tok.FOR).startpos + for_start_offset = t.expect(tok.FOR).start_offset t.expect(tok.LPAREN) for_exprs = [] if t.peek().tok == tok.VAR: - var_startpos = t.advance().startpos + var_start_offset = t.advance().start_offset kids = _variable_declaration(t, False) - vars = ParseNode(kind.VAR, op.DEFVAR, var_startpos, kids[-1].endpos, + vars = ParseNode(kind.VAR, op.DEFVAR, var_start_offset, kids[-1].end_offset, None, kids) if t.peek().tok == tok.IN: @@ -589,8 +589,8 @@ def _for_statement(t): for_exprs = [expr, None, None] if len(for_exprs) == 2: - condition = ParseNode(kind.IN, None, for_exprs[0].startpos, - for_exprs[-1].endpos, None, for_exprs) + condition = ParseNode(kind.IN, None, for_exprs[0].start_offset, + for_exprs[-1].end_offset, None, for_exprs) else: x = t.expect(tok.SEMI) if t.peek().tok != tok.SEMI: @@ -605,12 +605,12 @@ def _for_statement(t): body = _statement(t) return ParseNode(kind.FOR, op.FORIN if condition.kind == kind.IN else None, - for_startpos, body.endpos, + for_start_offset, body.end_offset, None, [condition, body]) def _continue_statement(t): endtoken = t.expect(tok.CONTINUE) - startpos = endtoken.startpos + start_offset = endtoken.start_offset if t.peek_sameline().tok == tok.NAME: endtoken = t.expect(tok.NAME) @@ -618,11 +618,11 @@ def _continue_statement(t): else: name = None # TODO: Validate Scope Labels - return _auto_semicolon(t, kind.CONTINUE, None, startpos, endtoken.endpos, name, []) + return _auto_semicolon(t, kind.CONTINUE, None, start_offset, endtoken.end_offset, name, []) def _break_statement(t): endtoken = t.expect(tok.BREAK) - startpos = endtoken.startpos + start_offset = endtoken.start_offset if t.peek_sameline().tok == tok.NAME: endtoken = t.expect(tok.NAME) @@ -630,11 +630,11 @@ def _break_statement(t): else: name = None # TODO: Validate Scope Labels - return _auto_semicolon(t, kind.BREAK, None, startpos, endtoken.endpos, name, []) + return _auto_semicolon(t, kind.BREAK, None, start_offset, endtoken.end_offset, name, []) def _return_statement(t): endtoken = t.expect(tok.RETURN) - startpos = endtoken.startpos + start_offset = endtoken.start_offset if t.peek_sameline().tok not in (tok.EOF, tok.EOL, tok.SEMI, tok.RBRACE): expr = _expression(t, True) @@ -642,108 +642,108 @@ def _return_statement(t): else: expr = None # TODO: Validate Scope Labels - return _auto_semicolon(t, kind.RETURN, None, startpos, endtoken.endpos, + return _auto_semicolon(t, kind.RETURN, None, start_offset, endtoken.end_offset, None, [expr]) def _with_statement(t): - startpos = t.expect(tok.WITH).startpos + start_offset = t.expect(tok.WITH).start_offset t.expect(tok.LPAREN) expr = _expression(t, True) t.expect(tok.RPAREN) body = _statement(t) - return ParseNode(kind.WITH, None, startpos, body.endpos, None, [expr, body]) + return ParseNode(kind.WITH, None, start_offset, body.end_offset, None, [expr, body]) def _switch_statement(t): - switch_startpos = t.expect(tok.SWITCH).startpos + switch_start_offset = t.expect(tok.SWITCH).start_offset t.expect(tok.LPAREN) expr = _expression(t, True) t.expect(tok.RPAREN) - lc_startpos = t.expect(tok.LBRACE).startpos + lc_start_offset = t.expect(tok.LBRACE).start_offset cases = [] while t.peek().tok != tok.RBRACE: case_kind = None case_expr = None if t.peek().tok == tok.CASE: - case_startpos = t.advance().startpos + case_start_offset = t.advance().start_offset case_kind = kind.CASE case_expr = _expression(t, True) elif t.peek().tok == tok.DEFAULT: - case_startpos = t.advance().startpos + case_start_offset = t.advance().start_offset case_kind = kind.DEFAULT else: - raise JSSyntaxError(t.peek().startpos, 'invalid_case') + raise JSSyntaxError(t.peek().start_offset, 'invalid_case') - case_endpos = t.expect(tok.COLON).endpos + case_end_offset = t.expect(tok.COLON).end_offset statements = [] while t.peek().tok not in (tok.DEFAULT, tok.CASE, tok.RBRACE): statements.append(_statement(t)) if statements: - statements_startpos = statements[0].startpos - statements_endpos = statements[-1].endpos - case_endpos = statements[-1].endpos + statements_start_offset = statements[0].start_offset + statements_end_offset = statements[-1].end_offset + case_end_offset = statements[-1].end_offset else: - statements_startpos = case_endpos - statements_endpos = case_endpos + statements_start_offset = case_end_offset + statements_end_offset = case_end_offset - cases.append(ParseNode(case_kind, None, case_startpos, case_endpos, + cases.append(ParseNode(case_kind, None, case_start_offset, case_end_offset, None, [ case_expr, - ParseNode(kind.LC, None, statements_startpos, - statements_endpos, None, statements) + ParseNode(kind.LC, None, statements_start_offset, + statements_end_offset, None, statements) ])) - rc_endpos = t.expect(tok.RBRACE).endpos - return ParseNode(kind.SWITCH, None, switch_startpos, rc_endpos, + rc_end_offset = t.expect(tok.RBRACE).end_offset + return ParseNode(kind.SWITCH, None, switch_start_offset, rc_end_offset, None, [expr, - ParseNode(kind.LC, None, lc_startpos, rc_endpos, None, cases)]) + ParseNode(kind.LC, None, lc_start_offset, rc_end_offset, None, cases)]) def _throw_statement(t): # TODO: Validate Scope - startpos = t.expect(tok.THROW).startpos + start_offset = t.expect(tok.THROW).start_offset if t.peek_sameline().tok == tok.EOL: - raise JSSyntaxError(t.peek_sameline().startpos, 'expected_statement') + raise JSSyntaxError(t.peek_sameline().start_offset, 'expected_statement') expr = _expression(t, True) - return _auto_semicolon(t, kind.THROW, op.THROW, startpos, expr.endpos, + return _auto_semicolon(t, kind.THROW, op.THROW, start_offset, expr.end_offset, None, [expr]) def _try_statement(t): - try_startpos = t.expect(tok.TRY).startpos + try_start_offset = t.expect(tok.TRY).start_offset try_node = _block_statement(t) catch_node = None finally_node = None - try_endpos = None + try_end_offset = None if t.peek().tok == tok.CATCH: - catch_startpos = t.advance().startpos + catch_start_offset = t.advance().start_offset t.expect(tok.LPAREN) x = t.expect(tok.NAME) - catch_expr = ParseNode(kind.NAME, None, x.startpos, x.endpos, + catch_expr = ParseNode(kind.NAME, None, x.start_offset, x.end_offset, x.atom, [None]) t.expect(tok.RPAREN) catch_block = _block_statement(t) - catch_endpos = catch_block.endpos + catch_end_offset = catch_block.end_offset catch_node = \ ParseNode(kind.RESERVED, None, None, None, None, [ ParseNode(kind.LEXICALSCOPE, op.LEAVEBLOCK, - catch_startpos, catch_endpos, None, [ - ParseNode(kind.CATCH, None, catch_startpos, - catch_endpos, None, + catch_start_offset, catch_end_offset, None, [ + ParseNode(kind.CATCH, None, catch_start_offset, + catch_end_offset, None, [catch_expr, None, catch_block]) ]) ]) - try_endpos = catch_endpos + try_end_offset = catch_end_offset if t.peek().tok == tok.FINALLY: t.advance() finally_node = _block_statement(t) - try_endpos = finally_node.endpos + try_end_offset = finally_node.end_offset if not catch_node and not finally_node: - raise JSSyntaxError(try_endpos, 'invalid_catch') + raise JSSyntaxError(try_end_offset, 'invalid_catch') - return ParseNode(kind.TRY, None, try_startpos, try_endpos, + return ParseNode(kind.TRY, None, try_start_offset, try_end_offset, None, [try_node, catch_node, finally_node]) @@ -779,7 +779,7 @@ def _statement(t): elif x.tok == tok.TRY: return _try_statement(t) elif x.tok == tok.EOF: - raise JSSyntaxError(x.startpos, 'unexpected_eof') + raise JSSyntaxError(x.start_offset, 'unexpected_eof') elif x.tok == tok.FUNCTION: return _function_declaration(t, op.CLOSURE) #TODO: warn, since this is not reliable @@ -788,13 +788,13 @@ def _statement(t): if expr.kind == tok.NAME and t.peek().tok == tok.COLON: t.expect(tok.COLON) stmt = _statement(t) - return ParseNode(kind.COLON, op.NAME, expr.startpos, - stmt.endpos, expr.atom, [stmt]) + return ParseNode(kind.COLON, op.NAME, expr.start_offset, + stmt.end_offset, expr.atom, [stmt]) - return _auto_semicolon(t, kind.SEMI, None, expr.startpos, expr.endpos, + return _auto_semicolon(t, kind.SEMI, None, expr.start_offset, expr.end_offset, None, [expr]) else: - raise JSSyntaxError(x.startpos, 'syntax_error') + raise JSSyntaxError(x.start_offset, 'syntax_error') def _sourceelements(t, end_tok): nodes = [] @@ -807,13 +807,14 @@ def _sourceelements(t, end_tok): else: nodes.append(_statement(t)) -def parsestring(s, startpos=None): - stream = tokenizer.TokenStream(s, startpos) +def parsestring(s, start_offset=0): + assert not start_offset is None + stream = tokenizer.TokenStream(s, start_offset) t = tokenizer.Tokenizer(stream) nodes = _sourceelements(t, tok.EOF) - lc_endpos = t.expect(tok.EOF).endpos - lc_startpos = nodes[-1].startpos if nodes else lc_endpos - return ParseNode(kind.LC, None, lc_startpos, lc_endpos, None, nodes) + lc_end_offset = t.expect(tok.EOF).end_offset + lc_start_offset = nodes[-1].start_offset if nodes else lc_end_offset + return ParseNode(kind.LC, None, lc_start_offset, lc_end_offset, None, nodes) def is_valid_version(version): return version in _VERSIONS @@ -824,14 +825,13 @@ def _validate(node, depth=0): assert kid.parent is node _validate(kid, depth+1) -def parse(script, jsversion, - error_callback, startpos): +def parse(script, jsversion, error_callback, start_offset): # TODO: respect version assert is_valid_version(jsversion) try: - root = parsestring(script, startpos) + root = parsestring(script, start_offset) except JSSyntaxError as error: - error_callback(error.pos.line, error.pos.col, error.msg, error.msg_args) + error_callback(error.offset, error.msg, error.msg_args) return None _validate(root) return root @@ -868,8 +868,8 @@ def testObjectEndComma(self): self.assertEquals(right.kind, kind.RC) node = right.end_comma self.assertEquals(node.kind, tok.COMMA) - self.assertEquals(node.startpos, NodePos(0, 6)) - self.assertEquals(node.endpos, NodePos(0, 6)) + self.assertEquals(node.start_offset, NodePos(0, 6)) + self.assertEquals(node.end_offset, NodePos(0, 6)) def _testArrayEndComma(self, script, col): root = parsestring(script) node, = root.kids @@ -885,8 +885,8 @@ def _testArrayEndComma(self, script, col): self.assert_(node is None) else: self.assertEquals(node.kind, tok.COMMA) - self.assertEquals(node.startpos, NodePos(0, col)) - self.assertEquals(node.endpos, NodePos(0, col)) + self.assertEquals(node.start_offset, NodePos(0, col)) + self.assertEquals(node.end_offset, NodePos(0, col)) def testArrayEndComma(self): self._testArrayEndComma('a=[,]', 3) self._testArrayEndComma('a=[a,]', 4) diff --git a/jsengine/structs.py b/jsengine/structs.py index 55676f1..a7be3a6 100644 --- a/jsengine/structs.py +++ b/jsengine/structs.py @@ -90,26 +90,27 @@ def __eq__(self, other): class ParseNode: node_index = None parent = None - def __init__(self, kind_, op_, start_pos, end_pos, atom, kids, + def __init__(self, kind_, op_, start_offset, end_offset, atom, kids, no_semi=False, end_comma=None, fn_args=None): assert not kids is None assert kind.contains(kind_) assert op_ is None or op.contains(op_) if kind_ == kind.RESERVED: - assert start_pos is None - assert end_pos is None + assert start_offset is None + assert end_offset is None else: - assert isinstance(start_pos, NodePos), repr(start_pos) - assert isinstance(end_pos, NodePos), repr(end_pos) + assert isinstance(start_offset, int), repr(start_offset) + assert isinstance(end_offset, int), repr(end_offset) assert end_comma is None or isinstance(end_comma, ParseNode) - assert (start_pos is None and end_pos is None) or start_pos <= end_pos + assert (start_offset is None and end_offset is None) or start_offset <= end_offset, \ + (start_offset, end_offset) self.kind = kind_ self.opcode = op_ self.atom = atom self.kids = kids self._lefthandside = False - self.startpos = start_pos - self.endpos = end_pos + self.start_offset = start_offset + self.end_offset = end_offset self.no_semi = no_semi self.end_comma = end_comma @@ -132,11 +133,6 @@ def __init__(self, kind_, op_, start_pos, end_pos, atom, kids, else: self.dval = float(self.atom) - def start_pos(self): - return self.startpos - def end_pos(self): - return self.endpos - def is_equivalent(self, other, are_functions_equiv=False): if not other: return False diff --git a/jsengine/tokenizer/__init__.py b/jsengine/tokenizer/__init__.py index 38c0a32..44d79b9 100644 --- a/jsengine/tokenizer/__init__.py +++ b/jsengine/tokenizer/__init__.py @@ -1,6 +1,5 @@ # vim: sw=4 ts=4 et from jsengine import JSSyntaxError -from jsengine.structs import NodePositions _WHITESPACE = u'\u0020\t\u000B\u000C\u00A0\uFFFF' _LINETERMINATOR = u'\u000A\u000D\u2028\u2029' @@ -141,59 +140,60 @@ class Token: def __init__(self, tok, atom=None): self.tok = tok self.atom = atom - self.startpos = None - self.endpos = None - def setpos(self, startpos, endpos): - self.startpos = startpos - self.endpos = endpos + self.start_offset = None + self.end_offset = None + def set_offset(self, start_offset, end_offset): + self.start_offset = start_offset + self.end_offset = end_offset def __repr__(self): return 'Token(%r, %r)' % \ (self.tok, self.atom) class TokenStream: - def __init__(self, content, startpos=None): + def __init__(self, content, start_offset=0): + assert isinstance(start_offset, int) self._content = content - self._pos = 0 - self._watched_pos = None - self._nodepositions = NodePositions(content, startpos) + self._start_offset = start_offset + self._offset = 0 + self._watched_offset = None - def getpos(self, offset=0): - return self._nodepositions.from_offset(self._pos+offset) + def get_offset(self, offset=0): + return self._start_offset + self._offset + offset def watch_reads(self): - self._watched_pos = self._pos + self._watched_offset = self._offset def get_watched_reads(self): - assert not self._watched_pos == None - s = self._content[self._watched_pos:self._pos] - self._watched_pos = None + assert not self._watched_offset == None + s = self._content[self._watched_offset:self._offset] + self._watched_offset = None return s def eof(self): - return self._pos >= len(self._content) + return self._offset >= len(self._content) def readchr(self): - if self._pos < len(self._content): - self._pos += 1 - return self._content[self._pos - 1] - raise JSSyntaxError(self.getpos(-1), 'unexpected_eof') + if self._offset < len(self._content): + self._offset += 1 + return self._content[self._offset - 1] + raise JSSyntaxError(self.get_offset(-1), 'unexpected_eof') def readif(self, len_, seq): s = self.peekif(len_, seq) if s: assert len(s) == len_ - self._pos += len_ + self._offset += len_ return s def peekchr(self, seq): - if self._pos < len(self._content) and self._content[self._pos] in seq: - return self._content[self._pos] + if self._offset < len(self._content) and self._content[self._offset] in seq: + return self._content[self._offset] def peekif(self, len_, seq): """ Returns the string if found. Otherwise returns None. """ - if self._pos + len_ <= len(self._content): - peeked = self._content[self._pos:self._pos+len_] + if self._offset + len_ <= len(self._content): + peeked = self._content[self._offset:self._offset+len_] if peeked in seq: return peeked @@ -230,7 +230,7 @@ def advance(self, skipspace=True, skipcomments=True): self._peeked = [] if peek.tok == tok.ERROR: self._error = True - raise JSSyntaxError(peek.startpos, peek.atom or 'syntax_error') + raise JSSyntaxError(peek.start_offset, peek.atom or 'syntax_error') return peek def next_withregexp(self): @@ -238,11 +238,11 @@ def next_withregexp(self): self._readahead() if self._peeked[-1].tok == tok.DIV: token = self._parse_rest_of_regexp() - token.setpos(self._peeked[-1].startpos, self._stream.getpos(-1)) + token.set_offset(self._peeked[-1].start_offset, self._stream.get_offset(-1)) self._peeked = [] if token.tok == tok.ERROR: self._error = True - raise JSSyntaxError(peek.startpos, peek.atom or 'syntax_error') + raise JSSyntaxError(peek.start_offset, peek.atom or 'syntax_error') return token else: return self.advance() @@ -250,7 +250,7 @@ def next_withregexp(self): def expect(self, tok): encountered = self.advance() if encountered.tok != tok: - raise JSSyntaxError(encountered.startpos, 'expected_tok', + raise JSSyntaxError(encountered.start_offset, 'expected_tok', { 'token': tok }) return encountered @@ -259,7 +259,7 @@ def expect_identifiername(self): if encountered.tok in list(_KEYWORDS.values()): encountered.tok = tok.NAME if encountered.tok != tok.NAME: - raise JSSyntaxError(encountered.startpos, 'syntax_error') + raise JSSyntaxError(encountered.start_offset, 'syntax_error') return encountered def _readahead(self): @@ -271,13 +271,13 @@ def _readahead(self): tok.HTML_COMMENT) return while True: - startpos = self._stream.getpos() + start_offset = self._stream.get_offset() peek = self._next() - endpos = self._stream.getpos(-1) + end_offset = self._stream.get_offset(-1) if peek.tok == tok.ERROR: - peek.setpos(endpos, endpos) + peek.set_offset(end_offset, end_offset) else: - peek.setpos(startpos, endpos) + peek.set_offset(start_offset, end_offset) self._peeked.append(peek) assert isinstance(peek.tok, _Token), repr(peek.tok) @@ -398,7 +398,7 @@ def _next(self, parse_regexp=False): if stream.readif(1, 'eE'): stream.readif(1, '+-') if not stream.readif(1, _DIGITS): - raise JSSyntaxError(stream.getpos(), 'syntax_error') + raise JSSyntaxError(stream.get_offset(), 'syntax_error') while stream.readif(1, _DIGITS): pass @@ -420,7 +420,7 @@ def _next(self, parse_regexp=False): return Token(d[None]) except KeyError: print('oops') - raise JSSyntaxError(stream.getpos(), 'syntax_error') + raise JSSyntaxError(stream.get_offset(), 'syntax_error') if c in _IDENT: s = '' @@ -432,5 +432,5 @@ def _next(self, parse_regexp=False): elif s: return Token(tok.NAME, atom=s) - raise JSSyntaxError(stream.getpos(), 'unexpected_char', + raise JSSyntaxError(stream.get_offset(), 'unexpected_char', { 'char': c }) diff --git a/test.py b/test.py index 857a462..b544203 100755 --- a/test.py +++ b/test.py @@ -107,7 +107,7 @@ def _get_python_modules(dir_): for root, dirs, files in os.walk(dir_): for exclude in ('build', 'dist'): if exclude in dirs: - build.remove(exclude) + dirs.remove(exclude) if '.svn' in dirs: dirs.remove('.svn') From 4ac4322431f6b8125995d9decc616a3d53b375e6 Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Wed, 9 Oct 2013 13:52:50 +0000 Subject: [PATCH 34/59] r335: Simplify TokenStream. --- jsengine/tokenizer/__init__.py | 69 ++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/jsengine/tokenizer/__init__.py b/jsengine/tokenizer/__init__.py index 44d79b9..f249298 100644 --- a/jsengine/tokenizer/__init__.py +++ b/jsengine/tokenizer/__init__.py @@ -178,24 +178,27 @@ def readchr(self): return self._content[self._offset - 1] raise JSSyntaxError(self.get_offset(-1), 'unexpected_eof') - def readif(self, len_, seq): - s = self.peekif(len_, seq) + def readchrif(self, seq): + s = self.peekchrif(seq) if s: - assert len(s) == len_ - self._offset += len_ + assert len(s) == 1 + self._offset += 1 return s - def peekchr(self, seq): - if self._offset < len(self._content) and self._content[self._offset] in seq: + def peekchrif(self, seq): + if self._offset < len(self._content) and \ + self._content[self._offset] in seq: return self._content[self._offset] - def peekif(self, len_, seq): + def readtextif(self, text): """ Returns the string if found. Otherwise returns None. """ + len_ = len(text) if self._offset + len_ <= len(self._content): peeked = self._content[self._offset:self._offset+len_] - if peeked in seq: - return peeked + if peeked == text: + self._offset += len_ + return text class Tokenizer: def __init__(self, stream): @@ -308,7 +311,7 @@ def _parse_rest_of_regexp(self): # TODO: Validate and save while True: - c = stream.readif(1, _IDENT) + c = stream.readchrif(_IDENT) if not c: break @@ -326,9 +329,9 @@ def _next(self, parse_regexp=False): if c in _WHITESPACE or c in _LINETERMINATOR: linebreak = c in _LINETERMINATOR while True: - if stream.readif(1, _LINETERMINATOR): + if stream.readchrif(_LINETERMINATOR): linebreak = True - elif stream.readif(1, _WHITESPACE): + elif stream.readchrif(_WHITESPACE): pass else: break @@ -339,11 +342,11 @@ def _next(self, parse_regexp=False): # COMMENTS if c == '/': - if stream.peekchr("/"): - while not stream.eof() and not stream.peekif(1, _LINETERMINATOR): + if stream.peekchrif("/"): + while not stream.eof() and not stream.peekchrif(_LINETERMINATOR): stream.readchr() return Token(tok.CPP_COMMENT) - if stream.peekchr("*"): + if stream.peekchrif("*"): linebreak = False while True: if stream.eof(): @@ -351,12 +354,12 @@ def _next(self, parse_regexp=False): c = stream.readchr() if c in _LINETERMINATOR: linebreak = True - elif c == '*' and stream.readif(1, '/'): + elif c == '*' and stream.readchrif('/'): return Token(tok.C_COMMENT) return Token(tok.EOF) elif c == '<': - if stream.readif(3, ('!--',)): - while not stream.eof() and not stream.peekif(1, _LINETERMINATOR): + if stream.readtextif('!--'): + while not stream.eof() and not stream.peekchrif(_LINETERMINATOR): stream.readchr() return Token(tok.HTML_COMMENT) @@ -374,35 +377,35 @@ def _next(self, parse_regexp=False): s += c # NUMBERS - if c in _DIGITS or (c == '.' and stream.peekchr(_DIGITS)): + if c in _DIGITS or (c == '.' and stream.peekchrif(_DIGITS)): s = c # TODO stream.watch_reads() - if c == '0' and stream.readif(1, 'xX'): + if c == '0' and stream.readchrif('xX'): # Hex - while stream.readif(1, _HEX_DIGITS): + while stream.readchrif(_HEX_DIGITS): pass - elif c == '0' and stream.readif(1, _DIGITS): + elif c == '0' and stream.readchrif(_DIGITS): # Octal - while stream.readif(1, _DIGITS): + while stream.readchrif(_DIGITS): pass else: # Decimal if c != '.': - while stream.readif(1, _DIGITS): + while stream.readchrif(_DIGITS): pass - stream.readif(1, '.') + stream.readchrif('.') - while stream.readif(1, _DIGITS): + while stream.readchrif(_DIGITS): pass - if stream.readif(1, 'eE'): - stream.readif(1, '+-') - if not stream.readif(1, _DIGITS): + if stream.readchrif('eE'): + stream.readchrif('+-') + if not stream.readchrif(_DIGITS): raise JSSyntaxError(stream.get_offset(), 'syntax_error') - while stream.readif(1, _DIGITS): + while stream.readchrif(_DIGITS): pass - if stream.peekchr(_IDENT): + if stream.peekchrif(_IDENT): return Token(tok.ERROR) atom = s + stream.get_watched_reads() @@ -411,7 +414,7 @@ def _next(self, parse_regexp=False): if c in _PUNCTUATOR_TREE: d = _PUNCTUATOR_TREE[c] while True: - c = stream.readif(1, list(d.keys())) + c = stream.readchrif(list(d.keys())) if c: d = d[c] else: @@ -426,7 +429,7 @@ def _next(self, parse_regexp=False): s = '' while c: s += c - c = stream.readif(1, _IDENT + _DIGITS) + c = stream.readchrif(_IDENT + _DIGITS) if s in _KEYWORDS: return Token(_KEYWORDS[s], atom=s) elif s: From ffe4578964f7070413808d169fe0fbd26c92ed13 Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Wed, 9 Oct 2013 19:41:02 +0000 Subject: [PATCH 35/59] r336: Change op.*, tok.*, and kind.* to not be derived from string. --- jsengine/parser/__init__.py | 2 +- jsengine/parser/_constants_kind.py | 15 +++++++++++---- jsengine/parser/_constants_op.py | 17 +++++++++++++---- jsengine/tokenizer/__init__.py | 12 ++++++++++-- 4 files changed, 35 insertions(+), 11 deletions(-) diff --git a/jsengine/parser/__init__.py b/jsengine/parser/__init__.py index 2d4d0a9..42c31fe 100644 --- a/jsengine/parser/__init__.py +++ b/jsengine/parser/__init__.py @@ -785,7 +785,7 @@ def _statement(t): elif x.tok not in (tok.LBRACE, tok.FUNCTION): expr = _expression(t, True) - if expr.kind == tok.NAME and t.peek().tok == tok.COLON: + if expr.kind == kind.NAME and t.peek().tok == tok.COLON: t.expect(tok.COLON) stmt = _statement(t) return ParseNode(kind.COLON, op.NAME, expr.start_offset, diff --git a/jsengine/parser/_constants_kind.py b/jsengine/parser/_constants_kind.py index 657c5ec..70271e7 100644 --- a/jsengine/parser/_constants_kind.py +++ b/jsengine/parser/_constants_kind.py @@ -65,15 +65,22 @@ 'STRING', 'YIELD', # TODO ] -class _Kind(str): +class _Kind(object): + def __init__(self, name): + self._name = name + + def __eq__(self, other): + assert isinstance(other, _Kind), repr(other) + return self is other + def __repr__(self): - return 'kind.%s' % self + return 'kind.%s' % self._name class _Kinds: def __init__(self): for kind in _KINDS: setattr(self, kind, _Kind(kind)) def contains(self, item): - return isinstance(item, _Kind) and \ - getattr(self, item) is item + return isinstance(item, _Kind) + kind = _Kinds() diff --git a/jsengine/parser/_constants_op.py b/jsengine/parser/_constants_op.py index 7aae168..a1a907e 100644 --- a/jsengine/parser/_constants_op.py +++ b/jsengine/parser/_constants_op.py @@ -70,9 +70,18 @@ 'VOID', 'CALL', ] -class _Op(str): +class _Op(object): + def __init__(self, name): + self._name = name + + def __eq__(self, other): + if other is None: + return False + assert isinstance(other, _Op), repr(other) + return self is other + def __repr__(self): - return 'op.%s' % self + return 'op.%s' % self._name class _Ops: NOP = None # TODO! @@ -80,6 +89,6 @@ def __init__(self): for op in _OPS: setattr(self, op, _Op(op)) def contains(self, item): - return isinstance(item, _Op) and \ - getattr(self, item) is item + return isinstance(item, _Op) + op = _Ops() diff --git a/jsengine/tokenizer/__init__.py b/jsengine/tokenizer/__init__.py index f249298..cf12783 100644 --- a/jsengine/tokenizer/__init__.py +++ b/jsengine/tokenizer/__init__.py @@ -109,8 +109,16 @@ 'STRING', ] -class _Token(str): - pass +class _Token(object): + def __init__(self, name): + self._name = name + + def __eq__(self, other): + assert isinstance(other, _Token) + return self is other + + def __repr__(self): + return 'tok.%s' % self._name class _Tokens: def __init__(self): From 95400b5229cf7f2fa62a3d5f7d95a246eacb3792 Mon Sep 17 00:00:00 2001 From: Matthias Miller Date: Wed, 9 Oct 2013 20:21:32 +0000 Subject: [PATCH 36/59] r337: Attempt to simplify token definitions. --- jsengine/tokenizer/__init__.py | 234 ++++++++++++++++----------------- 1 file changed, 113 insertions(+), 121 deletions(-) diff --git a/jsengine/tokenizer/__init__.py b/jsengine/tokenizer/__init__.py index cf12783..e20105f 100644 --- a/jsengine/tokenizer/__init__.py +++ b/jsengine/tokenizer/__init__.py @@ -10,139 +10,131 @@ u'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + \ u'$_' -_PUNCTUATORS = { - "<<<=": "ASSIGN_ULSHIFT", - ">>>=": "ASSIGN_URSHIFT", - "===": "EQ_STRICT", - "!==": "NE_STRICT", - ">>>": "URSHIFT", - "<<=": "ASSIGN_LSHIFT", - ">>=": "ASSIGN_RSHIFT", - "<=": "LE", - ">=": "GE", - "==": "EQ", - "!=": "NE", - "++": "INC", - "--": "DEC", - "<<": "LSHIFT", - ">>": "RSHIFT", - "&&": "LOGICAL_AND", - "||": "LOGICAL_OR", - "+=": "ASSIGN_ADD", - "-=": "ASSIGN_SUB", - "*=": "ASSIGN_MUL", - "%=": "ASSIGN_MOD", - "&=": "ASSIGN_BIT_AND", - "|=": "ASSIGN_BIT_OR", - "^=": "ASSIGN_BIT_XOR", - "/=": "ASSIGN_DIV", - "{": "LBRACE", - "}": "RBRACE", - "(": "LPAREN", - ")": "RPAREN", - "[": "LBRACKET", - "]": "RBRACKET", - ".": "DOT", - ";": "SEMI", - ",": "COMMA", - "<": "LT", - ">": "GT", - "+": "ADD", - "-": "SUB", - "*": "MUL", - "%": "MOD", - "|": "BIT_OR", - "&": "BIT_AND", - "^": "BIT_XOR", - "!": "LOGICAL_NOT", - "~": "BIT_NOT", - "?": "QUESTION", - ":": "COLON", - "=": "ASSIGN", - "/": "DIV", - "!": "LOGICAL_NOT", -} - -_KEYWORDS = dict((keyword, keyword.upper()) for keyword in [ - 'break', - 'case', - 'catch', - 'continue', - 'default', - 'delete', - 'do', - 'else', - 'false', - 'finally', - 'for', - 'function', - 'if', - 'in', - 'instanceof', - 'new', - 'null', - 'return', - 'switch', - 'this', - 'throw', - 'true', - 'typeof', - 'try', - 'var', - 'void', - 'while', - 'with', -]) - -_TOKENS = [ - 'C_COMMENT', - 'CPP_COMMENT', - 'HTML_COMMENT', - 'ERROR', - 'EOF', - 'EOL', - 'NAME', - 'NUMBER', - 'OPERATOR', - 'REGEXP', - 'SPACE', - 'STRING', -] +_ALL_TOKENS = [] class _Token(object): - def __init__(self, name): - self._name = name - - def __eq__(self, other): - assert isinstance(other, _Token) - return self is other + def __init__(self, category, literal): + self._category = category + self._literal = literal + _ALL_TOKENS.append(self) def __repr__(self): - return 'tok.%s' % self._name + return '_Token(%r, %r)' % (self._category, self._literal) -class _Tokens: - def __init__(self): - for token in _TOKENS: - setattr(self, token, _Token(token)) + @property + def category(self): + return self._category - for key, name in list(_KEYWORDS.items()): - _KEYWORDS[key] = _Token(name) - setattr(self, name, _KEYWORDS[key]) + @property + def literal(self): + return self._literal - for key, name in list(_PUNCTUATORS.items()): - _PUNCTUATORS[key] = _Token(name) - setattr(self, name, _PUNCTUATORS[key]) +class _Tokens(object): + def __init__(self): + # Load symbols + self.ASSIGN_ULSHIFT = _Token('sym', '<<<=') + self.ASSIGN_URSHIFT = _Token('sym', '>>>=') + self.EQ_STRICT = _Token('sym', '===') + self.NE_STRICT = _Token('sym', '!==') + self.URSHIFT = _Token('sym', '>>>') + self.ASSIGN_LSHIFT = _Token('sym', '<<=') + self.ASSIGN_RSHIFT = _Token('sym', '>>=') + self.LE = _Token('sym', '<=') + self.GE = _Token('sym', '>=') + self.EQ = _Token('sym', '==') + self.NE = _Token('sym', '!=') + self.INC = _Token('sym', '++') + self.DEC = _Token('sym', '--') + self.LSHIFT = _Token('sym', '<<') + self.RSHIFT = _Token('sym', '>>') + self.LOGICAL_AND = _Token('sym', '&&') + self.LOGICAL_OR = _Token('sym', '||') + self.ASSIGN_ADD = _Token('sym', '+=') + self.ASSIGN_SUB = _Token('sym', '-=') + self.ASSIGN_MUL = _Token('sym', '*=') + self.ASSIGN_MOD = _Token('sym', '%=') + self.ASSIGN_BIT_AND = _Token('sym', '&=') + self.ASSIGN_BIT_OR = _Token('sym', '|=') + self.ASSIGN_BIT_XOR = _Token('sym', '^=') + self.ASSIGN_DIV = _Token('sym', '/=') + self.LBRACE = _Token('sym', '{') + self.RBRACE = _Token('sym', '}') + self.LPAREN = _Token('sym', '(') + self.RPAREN = _Token('sym', ')') + self.LBRACKET = _Token('sym', '[') + self.RBRACKET = _Token('sym', ']') + self.DOT = _Token('sym', '.') + self.SEMI = _Token('sym', ';') + self.COMMA = _Token('sym', ',') + self.LT = _Token('sym', '<') + self.GT = _Token('sym', '>') + self.ADD = _Token('sym', '+') + self.SUB = _Token('sym', '-') + self.MUL = _Token('sym', '*') + self.MOD = _Token('sym', '%') + self.BIT_OR = _Token('sym', '|') + self.BIT_AND = _Token('sym', '&') + self.BIT_XOR = _Token('sym', '^') + self.LOGICAL_NOT = _Token('sym', '!') + self.BIT_NOT = _Token('sym', '~') + self.QUESTION = _Token('sym', '?') + self.COLON = _Token('sym', ':') + self.ASSIGN = _Token('sym', '=') + self.DIV = _Token('sym', '/') + + # Load keywords + self.BREAK = _Token('kw', 'break') + self.CASE = _Token('kw', 'case') + self.CATCH = _Token('kw', 'catch') + self.CONTINUE = _Token('kw', 'continue') + self.DEFAULT = _Token('kw', 'default') + self.DELETE = _Token('kw', 'delete') + self.DO = _Token('kw', 'do') + self.ELSE = _Token('kw', 'else') + self.FALSE = _Token('kw', 'false') + self.FINALLY = _Token('kw', 'finally') + self.FOR = _Token('kw', 'for') + self.FUNCTION = _Token('kw', 'function') + self.IF = _Token('kw', 'if') + self.IN = _Token('kw', 'in') + self.INSTANCEOF = _Token('kw', 'instanceof') + self.NEW = _Token('kw', 'new') + self.NULL = _Token('kw', 'null') + self.RETURN = _Token('kw', 'return') + self.SWITCH = _Token('kw', 'switch') + self.THIS = _Token('kw', 'this') + self.THROW = _Token('kw', 'throw') + self.TRUE = _Token('kw', 'true') + self.TYPEOF = _Token('kw', 'typeof') + self.TRY = _Token('kw', 'try') + self.VAR = _Token('kw', 'var') + self.VOID = _Token('kw', 'void') + self.WHILE = _Token('kw', 'while') + self.WITH = _Token('kw', 'with') + + # Load other tokens + self.C_COMMENT = _Token('other', '/*') + self.CPP_COMMENT = _Token('other', '//') + self.HTML_COMMENT = _Token('other', '