From 0dd4e7e1640e36ef2c265ec304c190c2bbd827f5 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Mon, 18 Nov 2013 13:02:11 -0800 Subject: [PATCH 001/144] Bug 928195 - Part 3: Consolidate all WebIDL Python logic into mozwebidl module; r=bz, froydnj --HG-- extra : rebase_source : 050f1c3eb3bccdf369164be4e58c59fa71c19060 --- build/virtualenv_packages.txt | 2 + dom/bindings/Makefile.in | 2 + dom/bindings/mozwebidlcodegen/__init__.py | 523 ++++++++++++++++++ .../mozwebidlcodegen/test/Child.webidl | 3 + .../mozwebidlcodegen/test/DummyBinding.webidl | 2 + .../test/ExampleBinding.webidl | 3 + .../mozwebidlcodegen/test/Parent.webidl | 3 + .../mozwebidlcodegen/test/TestEvent.webidl | 13 + .../test/test_mozwebidlcodegen.py | 278 ++++++++++ 9 files changed, 829 insertions(+) create mode 100644 dom/bindings/mozwebidlcodegen/__init__.py create mode 100644 dom/bindings/mozwebidlcodegen/test/Child.webidl create mode 100644 dom/bindings/mozwebidlcodegen/test/DummyBinding.webidl create mode 100644 dom/bindings/mozwebidlcodegen/test/ExampleBinding.webidl create mode 100644 dom/bindings/mozwebidlcodegen/test/Parent.webidl create mode 100644 dom/bindings/mozwebidlcodegen/test/TestEvent.webidl create mode 100644 dom/bindings/mozwebidlcodegen/test/test_mozwebidlcodegen.py diff --git a/build/virtualenv_packages.txt b/build/virtualenv_packages.txt index aade8e7598e3..4fd374decd99 100644 --- a/build/virtualenv_packages.txt +++ b/build/virtualenv_packages.txt @@ -13,6 +13,8 @@ mock.pth:python/mock-1.0.0 mozilla.pth:build mozilla.pth:config mozilla.pth:xpcom/typelib/xpt/tools +mozilla.pth:dom/bindings +mozilla.pth:dom/bindings/parser moztreedocs.pth:tools/docs copy:build/buildconfig.py packages.txt:testing/mozbase/packages.txt diff --git a/dom/bindings/Makefile.in b/dom/bindings/Makefile.in index 8bc94e224f03..a4a029ae5964 100644 --- a/dom/bindings/Makefile.in +++ b/dom/bindings/Makefile.in @@ -84,6 +84,8 @@ globalgen_headers_DEST = $(ABS_DIST)/include/mozilla/dom globalgen_headers_TARGET := export INSTALL_TARGETS += globalgen_headers +PYTHON_UNIT_TESTS += $(srcdir)/mozwebidlcodegen/test/test_mozwebidlcodegen.py + include $(topsrcdir)/config/rules.mk ifdef GNU_CC diff --git a/dom/bindings/mozwebidlcodegen/__init__.py b/dom/bindings/mozwebidlcodegen/__init__.py new file mode 100644 index 000000000000..4fc5d3bbd046 --- /dev/null +++ b/dom/bindings/mozwebidlcodegen/__init__.py @@ -0,0 +1,523 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# This module contains code for managing WebIDL files and bindings for +# the build system. + +from __future__ import unicode_literals + +import errno +import hashlib +import json +import logging +import os + +from copy import deepcopy + +from mach.mixin.logging import LoggingMixin + +from mozbuild.makeutil import Makefile +from mozbuild.pythonutil import iter_modules_in_path +from mozbuild.util import FileAvoidWrite + +import mozpack.path as mozpath + +import WebIDL +from Codegen import ( + CGBindingRoot, + CGEventRoot, + CGExampleRoot, + GlobalGenRoots, +) +from Configuration import Configuration + + +class BuildResult(object): + """Represents the result of processing WebIDL files. + + This holds a summary of output file generation during code generation. + """ + + def __init__(self): + # The .webidl files that had their outputs regenerated. + self.inputs = set() + + # The output files that were created. + self.created = set() + + # The output files that changed. + self.updated = set() + + # The output files that didn't change. + self.unchanged = set() + + +class WebIDLCodegenManagerState(dict): + """Holds state for the WebIDL code generation manager. + + State is currently just an extended dict. The internal implementation of + state should be considered a black box to everyone except + WebIDLCodegenManager. But we'll still document it. + + Fields: + + version + The integer version of the format. This is to detect incompatible + changes between state. It should be bumped whenever the format + changes or semantics change. + + webidls + A dictionary holding information about every known WebIDL input. + Keys are the basenames of input WebIDL files. Values are dicts of + metadata. Keys in those dicts are: + + * filename - The full path to the input filename. + * inputs - A set of full paths to other webidl files this webidl + depends on. + * outputs - Set of full output paths that are created/derived from + this file. + * sha1 - The hexidecimal SHA-1 of the input filename from the last + processing time. + + global_inputs + A dictionary defining files that influence all processing. Keys + are full filenames. Values are hexidecimal SHA-1 from the last + processing time. + """ + + VERSION = 1 + + def __init__(self, fh=None): + self['version'] = self.VERSION + self['webidls'] = {} + self['global_depends'] = {} + + if not fh: + return + + state = json.load(fh) + if state['version'] != self.VERSION: + raise Exception('Unknown state version: %s' % state['version']) + + self['version'] = state['version'] + self['global_depends'] = state['global_depends'] + + for k, v in state['webidls'].items(): + self['webidls'][k] = v + + # Sets are converted to lists for serialization because JSON + # doesn't support sets. + self['webidls'][k]['inputs'] = set(v['inputs']) + self['webidls'][k]['outputs'] = set(v['outputs']) + + def dump(self, fh): + """Dump serialized state to a file handle.""" + normalized = deepcopy(self) + + for k, v in self['webidls'].items(): + # Convert sets to lists because JSON doesn't support sets. + normalized['webidls'][k]['outputs'] = sorted(v['outputs']) + normalized['webidls'][k]['inputs'] = sorted(v['inputs']) + + json.dump(normalized, fh, sort_keys=True) + + +class WebIDLCodegenManager(LoggingMixin): + """Manages all code generation around WebIDL. + + To facilitate testing, this object is meant to be generic and reusable. + Paths, etc should be parameters and not hardcoded. + """ + + # Global parser derived declaration files. + GLOBAL_DECLARE_FILES = { + 'GeneratedAtomList.h', + 'PrototypeList.h', + 'RegisterBindings.h', + 'UnionConversions.h', + 'UnionTypes.h', + } + + # Global parser derived definition files. + GLOBAL_DEFINE_FILES = { + 'RegisterBindings.cpp', + 'UnionTypes.cpp', + } + + # Example interfaces to build along with the tree. Other example + # interfaces will need to be generated manually. + BUILD_EXAMPLE_INTERFACES = { + 'TestExampleInterface', + 'TestExampleProxyInterface', + } + + def __init__(self, config_path, inputs, exported_header_dir, + codegen_dir, state_path, cache_dir=None, make_deps_path=None, + make_deps_target=None): + """Create an instance that manages WebIDLs in the build system. + + config_path refers to a WebIDL config file (e.g. Bindings.conf). + inputs is a 3-tuple describing the input .webidl files and how to + process them. Members are: + (set(.webidl files), set(basenames of exported files), + set(basenames of generated events files)) + + exported_header_dir and codegen_dir are directories where generated + files will be written to. + state_path is the path to a file that will receive JSON state from our + actions. + make_deps_path is the path to a make dependency file that we can + optionally write. + make_deps_target is the target that receives the make dependencies. It + must be defined if using make_deps_path. + """ + self.populate_logger() + + input_paths, exported_stems, generated_events_stems = inputs + + self._config_path = config_path + self._input_paths = set(input_paths) + self._exported_stems = set(exported_stems) + self._generated_events_stems = set(generated_events_stems) + self._exported_header_dir = exported_header_dir + self._codegen_dir = codegen_dir + self._state_path = state_path + self._cache_dir = cache_dir + self._make_deps_path = make_deps_path + self._make_deps_target = make_deps_target + + if (make_deps_path and not make_deps_target) or (not make_deps_path and + make_deps_target): + raise Exception('Must define both make_deps_path and make_deps_target ' + 'if one is defined.') + + self._parser_results = None + self._config = None + self._state = WebIDLCodegenManagerState() + + if os.path.exists(state_path): + with open(state_path, 'rb') as fh: + try: + self._state = WebIDLCodegenManagerState(fh=fh) + except Exception as e: + self.log(logging.WARN, 'webidl_bad_state', {'msg': str(e)}, + 'Bad WebIDL state: {msg}') + + @property + def config(self): + if not self._config: + self._parse_webidl() + + return self._config + + def generate_build_files(self): + """Generate files required for the build. + + This function is in charge of generating all the .h/.cpp files derived + from input .webidl files. Please note that there are build actions + required to produce .webidl files and these build actions are + explicitly not captured here: this function assumes all .webidl files + are present and up to date. + + This routine is called as part of the build to ensure files that need + to exist are present and up to date. This routine may not be called if + the build dependencies (generated as a result of calling this the first + time) say everything is up to date. + + Because reprocessing outputs for every .webidl on every invocation + is expensive, we only regenerate the minimal set of files on every + invocation. The rules for deciding what needs done are roughly as + follows: + + 1. If any .webidl changes, reparse all .webidl files and regenerate + the global derived files. Only regenerate output files (.h/.cpp) + impacted by the modified .webidl files. + 2. If an non-.webidl dependency (Python files, config file) changes, + assume everything is out of date and regenerate the world. This + is because changes in those could globally impact every output + file. + 3. If an output file is missing, ensure it is present by performing + necessary regeneration. + """ + # Despite #1 above, we assume the build system is smart enough to not + # invoke us if nothing has changed. Therefore, any invocation means + # something has changed. And, if anything has changed, we need to + # parse the WebIDL. + self._parse_webidl() + + result = BuildResult() + + # If we parse, we always update globals - they are cheap and it is + # easier that way. + created, updated, unchanged = self._write_global_derived() + result.created |= created + result.updated |= updated + result.unchanged |= unchanged + + # If any of the extra dependencies changed, regenerate the world. + global_changed, global_hashes = self._global_dependencies_changed() + if global_changed: + # Make a copy because we may modify. + changed_inputs = set(self._input_paths) + else: + changed_inputs = self._compute_changed_inputs() + + self._state['global_depends'] = global_hashes + + # Generate bindings from .webidl files. + for filename in sorted(changed_inputs): + basename = mozpath.basename(filename) + result.inputs.add(filename) + written, deps = self._generate_build_files_for_webidl(filename) + result.created |= written[0] + result.updated |= written[1] + result.unchanged |= written[2] + + self._state['webidls'][basename] = dict( + filename=filename, + outputs=written[0] | written[1] | written[2], + inputs=set(deps), + sha1=self._input_hashes[filename], + ) + + # Process some special interfaces required for testing. + for interface in self.BUILD_EXAMPLE_INTERFACES: + written = self.generate_example_files(interface) + result.created |= written[0] + result.updated |= written[1] + result.unchanged |= written[2] + + # Generate a make dependency file. + if self._make_deps_path: + mk = Makefile() + codegen_rule = mk.create_rule([self._make_deps_target]) + codegen_rule.add_dependencies(global_hashes.keys()) + codegen_rule.add_dependencies(self._input_paths) + + with FileAvoidWrite(self._make_deps_path) as fh: + mk.dump(fh) + + self._save_state() + + return result + + def generate_example_files(self, interface): + """Generates example files for a given interface.""" + root = CGExampleRoot(self.config, interface) + + return self._maybe_write_codegen(root, *self._example_paths(interface)) + + def _parse_webidl(self): + self.log(logging.INFO, 'webidl_parse', + {'count': len(self._input_paths)}, + 'Parsing {count} WebIDL files.') + + hashes = {} + parser = WebIDL.Parser(self._cache_dir) + + for path in sorted(self._input_paths): + with open(path, 'rb') as fh: + data = fh.read() + hashes[path] = hashlib.sha1(data).hexdigest() + parser.parse(data, path) + + self._parser_results = parser.finish() + self._config = Configuration(self._config_path, self._parser_results) + self._input_hashes = hashes + + def _write_global_derived(self): + things = [('declare', f) for f in self.GLOBAL_DECLARE_FILES] + things.extend(('define', f) for f in self.GLOBAL_DEFINE_FILES) + + result = (set(), set(), set()) + + for what, filename in things: + stem = mozpath.splitext(filename)[0] + root = getattr(GlobalGenRoots, stem)(self._config) + + if what == 'declare': + code = root.declare() + output_root = self._exported_header_dir + elif what == 'define': + code = root.define() + output_root = self._codegen_dir + else: + raise Exception('Unknown global gen type: %s' % what) + + output_path = mozpath.join(output_root, filename) + self._maybe_write_file(output_path, code, result) + + return result + + def _compute_changed_inputs(self): + """Compute the set of input files that need to be regenerated.""" + changed_inputs = set() + expected_outputs = self.expected_build_output_files() + + # Look for missing output files. + if any(not os.path.exists(f) for f in expected_outputs): + # FUTURE Bug 940469 Only regenerate minimum set. + changed_inputs |= self._input_paths + + # That's it for examining output files. We /could/ examine SHA-1's of + # output files from a previous run to detect modifications. But that's + # a lot of extra work and most build systems don't do that anyway. + + # Now we move on to the input files. + old_hashes = {v['filename']: v['sha1'] + for v in self._state['webidls'].values()} + + old_filenames = set(old_hashes.keys()) + new_filenames = self._input_paths + + # If an old file has disappeared or a new file has arrived, mark + # it. + changed_inputs |= old_filenames ^ new_filenames + + # For the files in common between runs, compare content. If the file + # has changed, mark it. We don't need to perform mtime comparisons + # because content is a stronger validator. + for filename in old_filenames & new_filenames: + if old_hashes[filename] != self._input_hashes[filename]: + changed_inputs.add(filename) + + # We've now populated the base set of inputs that have changed. + + # Inherit dependencies from previous run. The full set of dependencies + # is associated with each record, so we don't need to perform any fancy + # graph traversal. + for v in self._state['webidls'].values(): + if any(dep for dep in v['inputs'] if dep in changed_inputs): + changed_inputs.add(v['filename']) + + # Ensure all changed inputs actually exist (some changed inputs could + # have been from deleted files). + return set(f for f in changed_inputs if os.path.exists(f)) + + def _binding_info(self, p): + """Compute binding metadata for an input path. + + Returns a tuple of: + + (stem, binding_stem, is_event, output_files) + + output_files is itself a tuple. The first two items are the binding + header and C++ paths, respectively. The 2nd pair are the event header + and C++ paths or None if this isn't an event binding. + """ + basename = mozpath.basename(p) + stem = mozpath.splitext(basename)[0] + binding_stem = '%sBinding' % stem + + if stem in self._exported_stems: + header_dir = self._exported_header_dir + else: + header_dir = self._codegen_dir + + is_event = stem in self._generated_events_stems + + files = ( + mozpath.join(header_dir, '%s.h' % binding_stem), + mozpath.join(self._codegen_dir, '%s.cpp' % binding_stem), + mozpath.join(header_dir, '%s.h' % stem) if is_event else None, + mozpath.join(self._codegen_dir, '%s.cpp' % stem) if is_event else None, + ) + + return stem, binding_stem, is_event, header_dir, files + + def _example_paths(self, interface): + return ( + mozpath.join(self._codegen_dir, '%s-example.h' % interface), + mozpath.join(self._codegen_dir, '%s-example.cpp' % interface)) + + def expected_build_output_files(self): + """Obtain the set of files generate_build_files() should write.""" + paths = set() + + # Account for global generation. + for p in self.GLOBAL_DECLARE_FILES: + paths.add(mozpath.join(self._exported_header_dir, p)) + for p in self.GLOBAL_DEFINE_FILES: + paths.add(mozpath.join(self._codegen_dir, p)) + + for p in self._input_paths: + stem, binding_stem, is_event, header_dir, files = self._binding_info(p) + paths |= {f for f in files if f} + + for interface in self.BUILD_EXAMPLE_INTERFACES: + for p in self._example_paths(interface): + paths.add(p) + + return paths + + def _generate_build_files_for_webidl(self, filename): + self.log(logging.INFO, 'webidl_generate_build_for_input', + {'filename': filename}, + 'Generating WebIDL files derived from {filename}') + + stem, binding_stem, is_event, header_dir, files = self._binding_info(filename) + root = CGBindingRoot(self._config, binding_stem, filename) + + result = self._maybe_write_codegen(root, files[0], files[1]) + + if is_event: + generated_event = CGEventRoot(self._config, stem) + result = self._maybe_write_codegen(generated_event, files[2], + files[3], result) + + return result, root.deps() + + def _global_dependencies_changed(self): + """Determine whether the global dependencies have changed.""" + current_files = set(iter_modules_in_path(mozpath.dirname(__file__))) + + # We need to catch other .py files from /dom/bindings. We assume these + # are in the same directory as the config file. + current_files |= set(iter_modules_in_path(mozpath.dirname(self._config_path))) + + current_files.add(self._config_path) + + current_hashes = {} + for f in current_files: + # This will fail if the file doesn't exist. If a current global + # dependency doesn't exist, something else is wrong. + with open(f, 'rb') as fh: + current_hashes[f] = hashlib.sha1(fh.read()).hexdigest() + + # The set of files has changed. + if current_files ^ set(self._state['global_depends'].keys()): + return True, current_hashes + + # Compare hashes. + for f, sha1 in current_hashes.items(): + if sha1 != self._state['global_depends'][f]: + return True, current_hashes + + return False, current_hashes + + def _save_state(self): + with open(self._state_path, 'wb') as fh: + self._state.dump(fh) + + def _maybe_write_codegen(self, obj, declare_path, define_path, result=None): + assert declare_path and define_path + if not result: + result = (set(), set(), set()) + + self._maybe_write_file(declare_path, obj.declare(), result) + self._maybe_write_file(define_path, obj.define(), result) + + return result + + def _maybe_write_file(self, path, content, result): + fh = FileAvoidWrite(path) + fh.write(content) + existed, updated = fh.close() + + if not existed: + result[0].add(path) + elif updated: + result[1].add(path) + else: + result[2].add(path) diff --git a/dom/bindings/mozwebidlcodegen/test/Child.webidl b/dom/bindings/mozwebidlcodegen/test/Child.webidl new file mode 100644 index 000000000000..f7d0a76c9d3f --- /dev/null +++ b/dom/bindings/mozwebidlcodegen/test/Child.webidl @@ -0,0 +1,3 @@ +interface Child : Parent { + void ChildBaz(); +}; diff --git a/dom/bindings/mozwebidlcodegen/test/DummyBinding.webidl b/dom/bindings/mozwebidlcodegen/test/DummyBinding.webidl new file mode 100644 index 000000000000..e2ccc4b69745 --- /dev/null +++ b/dom/bindings/mozwebidlcodegen/test/DummyBinding.webidl @@ -0,0 +1,2 @@ +interface DummyInterface {}; +interface DummyInterfaceWorkers {}; diff --git a/dom/bindings/mozwebidlcodegen/test/ExampleBinding.webidl b/dom/bindings/mozwebidlcodegen/test/ExampleBinding.webidl new file mode 100644 index 000000000000..34794993fe4a --- /dev/null +++ b/dom/bindings/mozwebidlcodegen/test/ExampleBinding.webidl @@ -0,0 +1,3 @@ +/* These interfaces are hard-coded and need to be defined. */ +interface TestExampleInterface {}; +interface TestExampleProxyInterface {}; diff --git a/dom/bindings/mozwebidlcodegen/test/Parent.webidl b/dom/bindings/mozwebidlcodegen/test/Parent.webidl new file mode 100644 index 000000000000..423f364aea23 --- /dev/null +++ b/dom/bindings/mozwebidlcodegen/test/Parent.webidl @@ -0,0 +1,3 @@ +interface Parent { + void MethodFoo(); +}; diff --git a/dom/bindings/mozwebidlcodegen/test/TestEvent.webidl b/dom/bindings/mozwebidlcodegen/test/TestEvent.webidl new file mode 100644 index 000000000000..db085629186c --- /dev/null +++ b/dom/bindings/mozwebidlcodegen/test/TestEvent.webidl @@ -0,0 +1,13 @@ +interface EventTarget { + void addEventListener(); +}; + +interface Event {}; + +callback EventHandlerNonNull = any (Event event); +typedef EventHandlerNonNull? EventHandler; + +[NoInterfaceObject] +interface TestEvent : EventTarget { + attribute EventHandler onfoo; +}; diff --git a/dom/bindings/mozwebidlcodegen/test/test_mozwebidlcodegen.py b/dom/bindings/mozwebidlcodegen/test/test_mozwebidlcodegen.py new file mode 100644 index 000000000000..fd35e8c63485 --- /dev/null +++ b/dom/bindings/mozwebidlcodegen/test/test_mozwebidlcodegen.py @@ -0,0 +1,278 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from __future__ import unicode_literals + +import imp +import json +import os +import shutil +import sys +import tempfile +import unittest + +import mozpack.path as mozpath + +from mozwebidlcodegen import ( + WebIDLCodegenManager, + WebIDLCodegenManagerState, +) + +from mozfile import NamedTemporaryFile + +from mozunit import ( + MockedOpen, + main, +) + + +OUR_DIR = mozpath.abspath(mozpath.dirname(__file__)) +TOPSRCDIR = mozpath.normpath(mozpath.join(OUR_DIR, '..', '..', '..', '..')) + + +class TestWebIDLCodegenManager(unittest.TestCase): + TEST_STEMS = { + 'Child', + 'Parent', + 'ExampleBinding', + 'TestEvent', + } + + @property + def _static_input_paths(self): + s = {mozpath.join(OUR_DIR, p) for p in os.listdir(OUR_DIR) + if p.endswith('.webidl')} + + return s + + @property + def _config_path(self): + config = mozpath.join(TOPSRCDIR, 'dom', 'bindings', 'Bindings.conf') + self.assertTrue(os.path.exists(config)) + + return config + + def _get_manager_args(self): + tmp = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, tmp) + + cache_dir = mozpath.join(tmp, 'cache') + os.mkdir(cache_dir) + + ip = self._static_input_paths + + inputs = ( + ip, + {mozpath.splitext(mozpath.basename(p))[0] for p in ip}, + set() + ) + + return dict( + config_path=self._config_path, + inputs=inputs, + exported_header_dir=mozpath.join(tmp, 'exports'), + codegen_dir=mozpath.join(tmp, 'codegen'), + state_path=mozpath.join(tmp, 'state.json'), + make_deps_path=mozpath.join(tmp, 'codegen.pp'), + make_deps_target='codegen.pp', + cache_dir=cache_dir, + ) + + def _get_manager(self): + return WebIDLCodegenManager(**self._get_manager_args()) + + def test_unknown_state_version(self): + """Loading a state file with a too new version resets state.""" + args = self._get_manager_args() + + p = args['state_path'] + + with open(p, 'wb') as fh: + json.dump({ + 'version': WebIDLCodegenManagerState.VERSION + 1, + 'foobar': '1', + }, fh) + + manager = WebIDLCodegenManager(**args) + + self.assertEqual(manager._state['version'], + WebIDLCodegenManagerState.VERSION) + self.assertNotIn('foobar', manager._state) + + def test_generate_build_files(self): + """generate_build_files() does the right thing from empty.""" + manager = self._get_manager() + result = manager.generate_build_files() + self.assertEqual(len(result.inputs), 5) + + output = manager.expected_build_output_files() + self.assertEqual(result.created, output) + self.assertEqual(len(result.updated), 0) + self.assertEqual(len(result.unchanged), 0) + + for f in output: + self.assertTrue(os.path.isfile(f)) + + for f in manager.GLOBAL_DECLARE_FILES: + self.assertIn(mozpath.join(manager._exported_header_dir, f), output) + + for f in manager.GLOBAL_DEFINE_FILES: + self.assertIn(mozpath.join(manager._codegen_dir, f), output) + + for s in self.TEST_STEMS: + self.assertTrue(os.path.isfile(mozpath.join( + manager._exported_header_dir, '%sBinding.h' % s))) + self.assertTrue(os.path.isfile(mozpath.join( + manager._codegen_dir, '%sBinding.cpp' % s))) + + self.assertTrue(os.path.isfile(manager._state_path)) + + with open(manager._state_path, 'rb') as fh: + state = json.load(fh) + self.assertEqual(state['version'], 1) + self.assertIn('webidls', state) + + child = state['webidls']['Child.webidl'] + self.assertEqual(len(child['inputs']), 2) + self.assertEqual(len(child['outputs']), 2) + self.assertEqual(child['sha1'], 'c41527cad3bc161fa6e7909e48fa11f9eca0468b') + + def test_generate_build_files_load_state(self): + """State should be equivalent when instantiating a new instance.""" + args = self._get_manager_args() + m1 = WebIDLCodegenManager(**args) + self.assertEqual(len(m1._state['webidls']), 0) + m1.generate_build_files() + + m2 = WebIDLCodegenManager(**args) + self.assertGreater(len(m2._state['webidls']), 2) + self.assertEqual(m1._state, m2._state) + + def test_no_change_no_writes(self): + """If nothing changes, no files should be updated.""" + args = self._get_manager_args() + m1 = WebIDLCodegenManager(**args) + m1.generate_build_files() + + m2 = WebIDLCodegenManager(**args) + result = m2.generate_build_files() + + self.assertEqual(len(result.inputs), 0) + self.assertEqual(len(result.created), 0) + self.assertEqual(len(result.updated), 0) + + def test_output_file_regenerated(self): + """If an output file disappears, it is regenerated.""" + args = self._get_manager_args() + m1 = WebIDLCodegenManager(**args) + m1.generate_build_files() + + rm_count = 0 + for p in m1._state['webidls']['Child.webidl']['outputs']: + rm_count += 1 + os.unlink(p) + + for p in m1.GLOBAL_DECLARE_FILES: + rm_count += 1 + os.unlink(mozpath.join(m1._exported_header_dir, p)) + + m2 = WebIDLCodegenManager(**args) + result = m2.generate_build_files() + self.assertEqual(len(result.created), rm_count) + + def test_only_rebuild_self(self): + """If an input file changes, only rebuild that one file.""" + args = self._get_manager_args() + m1 = WebIDLCodegenManager(**args) + m1.generate_build_files() + + child_path = None + for p in m1._input_paths: + if p.endswith('Child.webidl'): + child_path = p + break + + self.assertIsNotNone(child_path) + child_content = open(child_path, 'rb').read() + + with MockedOpen({child_path: child_content + '\n/* */'}): + m2 = WebIDLCodegenManager(**args) + result = m2.generate_build_files() + self.assertEqual(result.inputs, set([child_path])) + self.assertEqual(len(result.updated), 0) + self.assertEqual(len(result.created), 0) + + def test_rebuild_dependencies(self): + """Ensure an input file used by others results in others rebuilding.""" + args = self._get_manager_args() + m1 = WebIDLCodegenManager(**args) + m1.generate_build_files() + + parent_path = None + child_path = None + for p in m1._input_paths: + if p.endswith('Parent.webidl'): + parent_path = p + elif p.endswith('Child.webidl'): + child_path = p + + self.assertIsNotNone(parent_path) + parent_content = open(parent_path, 'rb').read() + + with MockedOpen({parent_path: parent_content + '\n/* */'}): + m2 = WebIDLCodegenManager(**args) + result = m2.generate_build_files() + self.assertEqual(result.inputs, {child_path, parent_path}) + self.assertEqual(len(result.updated), 0) + self.assertEqual(len(result.created), 0) + + def test_python_change_regenerate_everything(self): + """If a Python file changes, we should attempt to rebuild everything.""" + + # We don't want to mutate files in the source directory because we want + # to be able to build from a read-only filesystem. So, we install a + # dummy module and rewrite the metadata to say it comes from the source + # directory. + # + # Hacking imp to accept a MockedFile doesn't appear possible. So for + # the first iteration we read from a temp file. The second iteration + # doesn't need to import, so we are fine with a mocked file. + fake_path = mozpath.join(OUR_DIR, 'fakemodule.py') + with NamedTemporaryFile('wt') as fh: + fh.write('# Original content') + fh.flush() + mod = imp.load_source('mozwebidlcodegen.fakemodule', fh.name) + mod.__file__ = fake_path + + args = self._get_manager_args() + m1 = WebIDLCodegenManager(**args) + with MockedOpen({fake_path: '# Original content'}): + old_exists = os.path.exists + try: + def exists(p): + if p == fake_path: + return True + return old_exists(p) + + os.path.exists = exists + + result = m1.generate_build_files() + l = len(result.inputs) + + with open(fake_path, 'wt') as fh: + fh.write('# Modified content') + + m2 = WebIDLCodegenManager(**args) + result = m2.generate_build_files() + self.assertEqual(len(result.inputs), l) + + result = m2.generate_build_files() + self.assertEqual(len(result.inputs), 0) + finally: + os.path.exists = old_exists + del sys.modules['mozwebidlcodegen.fakemodule'] + + +if __name__ == '__main__': + main() From b47eeb6ab324dc64044dd09bb8b44abac4198ba0 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Thu, 12 Dec 2013 16:26:38 +0900 Subject: [PATCH 002/144] Bug 928195 - Part 4: Rewrite WebIDL build system integration; r=bz, r=glandium WebIDL build system integration has been rewritten from the ground up. Changes: * GlobalGen.py, BindingGen.py, and ExampleGen.py have been removed in favor of mozwebidl.py. * Static .webidl files are now processed directly in their original location and aren't copied to the object directory. * Generated events .cpp files are now compiled into the unified sources. Previously, only the Binding.cpp files were compiled into unified sources. * Exported .h files are now generated directly into their final location. Previously, they were generated into the local directory then installed in their final location. * The list of globalgen-generated files now lives in Python and isn't duplicated in 3 places. * The make dependencies are much simpler as a result of using a single command to perform all code generation. The auto-generated .pp file from code generation sets up all dependencies necessary to reinvoke code generation and Python takes care of dependency management. --HG-- extra : rebase_source : e4918878274b22a412329c7cb18cc7138daf5dc6 --- CLOBBER | 7 +- dom/bindings/BindingGen.py | 98 ------- dom/bindings/Codegen.py | 21 -- dom/bindings/ExampleGen.py | 46 --- dom/bindings/GlobalGen.py | 81 ----- dom/bindings/Makefile.in | 276 ++++-------------- dom/bindings/moz.build | 2 + dom/bindings/mozwebidlcodegen/__init__.py | 42 +++ dom/bindings/test/Makefile.in | 80 +---- dom/bindings/test/moz.build | 13 +- dom/moz.build | 3 - dom/webidl/moz.build | 12 - python/mozbuild/mozbuild/action/webidl.py | 17 ++ python/mozbuild/mozbuild/backend/common.py | 108 ++++++- .../mozbuild/backend/recursivemake.py | 179 ++++++------ python/mozbuild/mozbuild/base.py | 4 + 16 files changed, 351 insertions(+), 638 deletions(-) delete mode 100644 dom/bindings/BindingGen.py delete mode 100644 dom/bindings/ExampleGen.py delete mode 100644 dom/bindings/GlobalGen.py create mode 100644 python/mozbuild/mozbuild/action/webidl.py diff --git a/CLOBBER b/CLOBBER index 91acfa9390b2..f61ddd42af5e 100644 --- a/CLOBBER +++ b/CLOBBER @@ -18,4 +18,9 @@ # Modifying this file will now automatically clobber the buildbot machines \o/ # -Bug 887836 - webidl changes require a Windows clobber. +# Are you updating CLOBBER because you think it's needed for your WebIDL +# changes to stick? As of bug 928195, this shouldn't be necessary! Please +# don't change CLOBBER for WebIDL changes any more. +Bug 928195 rewrote WebIDL build system integration from the ground up. This +will hopefully be the last required clobber due to WebIDLs poorly interacting +with the build system. diff --git a/dom/bindings/BindingGen.py b/dom/bindings/BindingGen.py deleted file mode 100644 index e064270513f5..000000000000 --- a/dom/bindings/BindingGen.py +++ /dev/null @@ -1,98 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. - -import os -import cPickle -from Configuration import Configuration -from Codegen import CGBindingRoot, replaceFileIfChanged, CGEventRoot -from mozbuild.makeutil import Makefile -from mozbuild.pythonutil import iter_modules_in_path -from buildconfig import topsrcdir - - -def generate_binding_files(config, outputprefix, srcprefix, webidlfile, - generatedEventsWebIDLFiles): - """ - |config| Is the configuration object. - |outputprefix| is a prefix to use for the header guards and filename. - """ - - depsname = ".deps/" + outputprefix + ".pp" - root = CGBindingRoot(config, outputprefix, webidlfile) - replaceFileIfChanged(outputprefix + ".h", root.declare()) - replaceFileIfChanged(outputprefix + ".cpp", root.define()) - - if webidlfile in generatedEventsWebIDLFiles: - eventName = webidlfile[:-len(".webidl")] - generatedEvent = CGEventRoot(config, eventName) - replaceFileIfChanged(eventName + ".h", generatedEvent.declare()) - replaceFileIfChanged(eventName + ".cpp", generatedEvent.define()) - - mk = Makefile() - # NOTE: it's VERY important that we output dependencies for the FooBinding - # file here, not for the header or generated cpp file. These dependencies - # are used later to properly determine changedDeps and prevent rebuilding - # too much. See the comment explaining $(binding_dependency_trackers) in - # Makefile.in. - rule = mk.create_rule([outputprefix]) - rule.add_dependencies(os.path.join(srcprefix, x) for x in sorted(root.deps())) - rule.add_dependencies(iter_modules_in_path(topsrcdir)) - with open(depsname, 'w') as f: - mk.dump(f) - -def main(): - # Parse arguments. - from optparse import OptionParser - usagestring = "usage: %prog [header|cpp] configFile outputPrefix srcPrefix webIDLFile" - o = OptionParser(usage=usagestring) - o.add_option("--verbose-errors", action='store_true', default=False, - help="When an error happens, display the Python traceback.") - (options, args) = o.parse_args() - - configFile = os.path.normpath(args[0]) - srcPrefix = os.path.normpath(args[1]) - - # Load the configuration - f = open('ParserResults.pkl', 'rb') - config = cPickle.load(f) - f.close() - - def readFile(f): - file = open(f, 'rb') - try: - contents = file.read() - finally: - file.close() - return contents - allWebIDLFiles = readFile(args[2]).split() - generatedEventsWebIDLFiles = readFile(args[3]).split() - changedDeps = readFile(args[4]).split() - - if all(f.endswith("Binding") or f == "ParserResults.pkl" for f in changedDeps): - toRegenerate = filter(lambda f: f.endswith("Binding"), changedDeps) - if len(toRegenerate) == 0 and len(changedDeps) == 1: - # Work around build system bug 874923: if we get here that means - # that changedDeps contained only one entry and it was - # "ParserResults.pkl". That should never happen: if the - # ParserResults.pkl changes then either one of the globalgen files - # changed (in which case we wouldn't be in this "only - # ParserResults.pkl and *Binding changed" code) or some .webidl - # files changed (and then the corresponding *Binding files should - # show up in changedDeps). Since clearly the build system is - # confused, just regenerate everything to be safe. - toRegenerate = allWebIDLFiles - else: - toRegenerate = map(lambda f: f[:-len("Binding")] + ".webidl", - toRegenerate) - else: - toRegenerate = allWebIDLFiles - - for webIDLFile in toRegenerate: - assert webIDLFile.endswith(".webidl") - outputPrefix = webIDLFile[:-len(".webidl")] + "Binding" - generate_binding_files(config, outputPrefix, srcPrefix, webIDLFile, - generatedEventsWebIDLFiles); - -if __name__ == '__main__': - main() diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index c32b821fa513..97272e5a1b22 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -29,27 +29,6 @@ INSTANCE_RESERVED_SLOTS = 3 def memberReservedSlot(member): return "(DOM_INSTANCE_RESERVED_SLOTS + %d)" % member.slotIndex -def replaceFileIfChanged(filename, newContents): - """ - Read a copy of the old file, so that we don't touch it if it hasn't changed. - Returns True if the file was updated, false otherwise. - """ - oldFileContents = "" - try: - oldFile = open(filename, 'rb') - oldFileContents = ''.join(oldFile.readlines()) - oldFile.close() - except: - pass - - if newContents == oldFileContents: - return False - - f = open(filename, 'wb') - f.write(newContents) - f.close() - return True - def toStringBool(arg): return str(not not arg).lower() diff --git a/dom/bindings/ExampleGen.py b/dom/bindings/ExampleGen.py deleted file mode 100644 index 60eebe221a38..000000000000 --- a/dom/bindings/ExampleGen.py +++ /dev/null @@ -1,46 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. - -import os -import cPickle -from Configuration import Configuration -from Codegen import CGExampleRoot, replaceFileIfChanged - -def generate_interface_example(config, interfaceName): - """ - |config| Is the configuration object. - |interfaceName| is the name of the interface we're generating an example for. - """ - - root = CGExampleRoot(config, interfaceName) - exampleHeader = interfaceName + "-example.h" - exampleImpl = interfaceName + "-example.cpp" - replaceFileIfChanged(exampleHeader, root.declare()) - replaceFileIfChanged(exampleImpl, root.define()) - -def main(): - - # Parse arguments. - from optparse import OptionParser - usagestring = "usage: %prog configFile interfaceName" - o = OptionParser(usage=usagestring) - o.add_option("--verbose-errors", action='store_true', default=False, - help="When an error happens, display the Python traceback.") - (options, args) = o.parse_args() - - if len(args) != 2: - o.error(usagestring) - configFile = os.path.normpath(args[0]) - interfaceName = args[1] - - # Load the configuration - f = open('ParserResults.pkl', 'rb') - config = cPickle.load(f) - f.close() - - # Generate the example class. - generate_interface_example(config, interfaceName) - -if __name__ == '__main__': - main() diff --git a/dom/bindings/GlobalGen.py b/dom/bindings/GlobalGen.py deleted file mode 100644 index f3f02a4d1e48..000000000000 --- a/dom/bindings/GlobalGen.py +++ /dev/null @@ -1,81 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. - -# We do one global pass over all the WebIDL to generate our prototype enum -# and generate information for subsequent phases. - -import os -import WebIDL -import cPickle -from Configuration import Configuration -from Codegen import GlobalGenRoots, replaceFileIfChanged - -def generate_file(config, name, action): - - root = getattr(GlobalGenRoots, name)(config) - if action is 'declare': - filename = name + '.h' - code = root.declare() - else: - assert action is 'define' - filename = name + '.cpp' - code = root.define() - - if replaceFileIfChanged(filename, code): - print "Generating %s" % (filename) - else: - print "%s hasn't changed - not touching it" % (filename) - -def main(): - # Parse arguments. - from optparse import OptionParser - usageString = "usage: %prog [options] webidldir [files]" - o = OptionParser(usage=usageString) - o.add_option("--cachedir", dest='cachedir', default=None, - help="Directory in which to cache lex/parse tables.") - o.add_option("--verbose-errors", action='store_true', default=False, - help="When an error happens, display the Python traceback.") - (options, args) = o.parse_args() - - if len(args) < 2: - o.error(usageString) - - configFile = args[0] - baseDir = args[1] - fileList = args[2:] - - # Parse the WebIDL. - parser = WebIDL.Parser(options.cachedir) - for filename in fileList: - fullPath = os.path.normpath(os.path.join(baseDir, filename)) - f = open(fullPath, 'rb') - lines = f.readlines() - f.close() - parser.parse(''.join(lines), fullPath) - parserResults = parser.finish() - - # Load the configuration. - config = Configuration(configFile, parserResults) - - # Write the configuration out to a pickle. - resultsFile = open('ParserResults.pkl', 'wb') - cPickle.dump(config, resultsFile, -1) - resultsFile.close() - - # Generate the atom list. - generate_file(config, 'GeneratedAtomList', 'declare') - - # Generate the prototype list. - generate_file(config, 'PrototypeList', 'declare') - - # Generate the common code. - generate_file(config, 'RegisterBindings', 'declare') - generate_file(config, 'RegisterBindings', 'define') - - generate_file(config, 'UnionTypes', 'declare') - generate_file(config, 'UnionTypes', 'define') - generate_file(config, 'UnionConversions', 'declare') - -if __name__ == '__main__': - main() diff --git a/dom/bindings/Makefile.in b/dom/bindings/Makefile.in index a4a029ae5964..418a39837eec 100644 --- a/dom/bindings/Makefile.in +++ b/dom/bindings/Makefile.in @@ -1,245 +1,85 @@ # This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +abs_dist := $(abspath $(DIST)) +webidl_base := $(topsrcdir)/dom/webidl -webidl_base = $(topsrcdir)/dom/webidl # Generated by moz.build include webidlsrcs.mk -binding_include_path := mozilla/dom -webidl_files += $(generated_events_webidl_files) -all_webidl_files = $(webidl_files) $(generated_webidl_files) $(preprocessed_webidl_files) - -# Set exported_binding_headers before adding the test IDL to the mix -exported_binding_headers := $(subst .webidl,Binding.h,$(all_webidl_files)) -exported_generated_events_headers := $(subst .webidl,.h,$(generated_events_webidl_files)) - -# Set linked_binding_cpp_files before adding the test IDL to the mix -linked_binding_cpp_files := $(subst .webidl,Binding.cpp,$(all_webidl_files)) -linked_generated_events_cpp_files := $(subst .webidl,.cpp,$(generated_events_webidl_files)) - -all_webidl_files += $(test_webidl_files) $(preprocessed_test_webidl_files) - -generated_header_files := $(subst .webidl,Binding.h,$(all_webidl_files)) $(exported_generated_events_headers) -generated_cpp_files := $(subst .webidl,Binding.cpp,$(all_webidl_files)) $(linked_generated_events_cpp_files) - -# We want to be able to only regenerate the .cpp and .h files that really need -# to change when a .webidl file changes. We do this by making the -# binding_dependency_trackers targets have dependencies on the right .webidl -# files via generated .pp files, having a .BindingGen target that depends on the -# binding_dependency_trackers and which has all the generated binding .h/.cpp -# depending on it, and then in the make commands for that target being able to -# check which exact binding_dependency_trackers changed. -binding_dependency_trackers := $(subst .webidl,Binding,$(all_webidl_files)) - -globalgen_targets := \ - GeneratedAtomList.h \ - PrototypeList.h \ - RegisterBindings.h \ - RegisterBindings.cpp \ - UnionTypes.h \ - UnionTypes.cpp \ - UnionConversions.h \ - $(NULL) - -# Nasty hack: when the test/Makefile.in invokes us to do codegen, it -# uses a target of -# "export TestExampleInterface-example TestExampleProxyInterface-example". -# We don't actually need to load our .o.pp files in that case, so just -# pretend like we have no CPPSRCS if that's the target. It makes the -# test cycle much faster, which is why we're doing it. -# -# XXXbz We could try to cheat even more and only include our CPPSRCS -# when $(MAKECMDGOALS) contains libs, so that we can skip loading all -# those .o.pp when trying to make a single .cpp file too, but that -# would break |make FooBinding.o(bj)|. Ah, well. -ifneq (export TestExampleInterface-example TestExampleProxyInterface-example,$(MAKECMDGOALS)) -CPPSRCS = \ - $(unified_binding_cpp_files) \ - $(linked_generated_events_cpp_files) \ - $(filter %.cpp, $(globalgen_targets)) \ - $(NULL) +ifdef GNU_CC +OS_CXXFLAGS += -Wno-uninitialized endif -ABS_DIST := $(abspath $(DIST)) +# These come from webidlsrcs.mk. +# TODO Write directly into backend.mk. +CPPSRCS += $(globalgen_sources) $(unified_binding_cpp_files) -EXTRA_EXPORT_MDDEPEND_FILES := $(addsuffix .pp,$(binding_dependency_trackers)) - -EXPORTS_GENERATED_FILES := $(exported_binding_headers) $(exported_generated_events_headers) -EXPORTS_GENERATED_DEST := $(ABS_DIST)/include/$(binding_include_path) -EXPORTS_GENERATED_TARGET := export -INSTALL_TARGETS += EXPORTS_GENERATED - -# Install auto-generated GlobalGen files. The rules for the install must -# be in the same target/subtier as GlobalGen.py, otherwise the files will not -# get installed into the appropriate location as they are generated. -globalgen_headers_FILES := \ - GeneratedAtomList.h \ - PrototypeList.h \ - RegisterBindings.h \ - UnionConversions.h \ - UnionTypes.h \ - $(NULL) -globalgen_headers_DEST = $(ABS_DIST)/include/mozilla/dom -globalgen_headers_TARGET := export -INSTALL_TARGETS += globalgen_headers +# Generated bindings reference *Binding.h, not mozilla/dom/*Binding.h. And, +# since we generate exported bindings directly to $(DIST)/include, we need +# to add that path to the search list. +# +# Ideally, binding generation uses the prefixed header file names. +# Bug 932092 tracks. +LOCAL_INCLUDES += -I$(DIST)/include/mozilla/dom PYTHON_UNIT_TESTS += $(srcdir)/mozwebidlcodegen/test/test_mozwebidlcodegen.py include $(topsrcdir)/config/rules.mk -ifdef GNU_CC -CXXFLAGS += -Wno-uninitialized -endif - -# If you change bindinggen_dependencies here, change it in -# dom/bindings/test/Makefile.in too. -bindinggen_dependencies := \ - BindingGen.py \ - Bindings.conf \ - Configuration.py \ - Codegen.py \ - ParserResults.pkl \ - parser/WebIDL.py \ +# TODO This list should be emitted to a .pp file via +# GenerateCSS2PropertiesWebIDL.py. +css2properties_dependencies = \ + $(topsrcdir)/layout/style/nsCSSPropList.h \ + $(topsrcdir)/layout/style/nsCSSPropAliasList.h \ + $(webidl_base)/CSS2Properties.webidl.in \ + $(webidl_base)/CSS2PropertiesProps.h \ + $(srcdir)/GenerateCSS2PropertiesWebIDL.py \ $(GLOBAL_DEPS) \ $(NULL) -CSS2Properties.webidl: $(topsrcdir)/layout/style/nsCSSPropList.h \ - $(topsrcdir)/layout/style/nsCSSPropAliasList.h \ - $(webidl_base)/CSS2Properties.webidl.in \ - $(webidl_base)/CSS2PropertiesProps.h \ - $(srcdir)/GenerateCSS2PropertiesWebIDL.py \ - $(GLOBAL_DEPS) - $(CPP) $(DEFINES) $(ACDEFINES) -I$(topsrcdir)/layout/style $(webidl_base)/CSS2PropertiesProps.h | \ - PYTHONDONTWRITEBYTECODE=1 $(PYTHON) \ - $(srcdir)/GenerateCSS2PropertiesWebIDL.py $(webidl_base)/CSS2Properties.webidl.in > CSS2Properties.webidl +CSS2Properties.webidl: $(css2properties_dependencies) + $(CPP) $(DEFINES) $(ACDEFINES) -I$(topsrcdir)/layout/style \ + $(webidl_base)/CSS2PropertiesProps.h | \ + PYTHONDONTWRITEBYTECODE=1 $(PYTHON) \ + $(srcdir)/GenerateCSS2PropertiesWebIDL.py \ + $(webidl_base)/CSS2Properties.webidl.in > $@ -$(webidl_files): %: $(webidl_base)/% - $(INSTALL) $(IFLAGS1) $(webidl_base)/$* . - -$(test_webidl_files): %: $(srcdir)/test/% - $(INSTALL) $(IFLAGS1) $(srcdir)/test/$* . - -# We can't easily use PP_TARGETS here because it insists on outputting targets -# that look like "$(CURDIR)/foo" whereas we want our target to just be "foo". -# Make sure to include $(GLOBAL_DEPS) so we pick up changes to what symbols are -# defined. Also make sure to remove $@ before writing to it, because otherwise -# if a file goes from non-preprocessed to preprocessed we can end up writing to -# a symlink, which will clobber files in the srcdir, which is bad. -$(preprocessed_webidl_files): %: $(webidl_base)/% $(GLOBAL_DEPS) - $(RM) $@ - $(call py_action,preprocessor, \ - $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $(webidl_base)/$* -o $@) - -# See the comment about PP_TARGETS for $(preprocessed_webidl_files) -$(preprocessed_test_webidl_files): %: $(srcdir)/test/% $(GLOBAL_DEPS) - $(RM) $@ - $(call py_action,preprocessor, \ - $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $(srcdir)/test/$* -o $@) - -# Make is dumb and can get confused between "foo" and "$(CURDIR)/foo". Make -# sure that the latter depends on the former, since the latter gets used in .pp -# files. -all_webidl_files_absolute = $(addprefix $(CURDIR)/,$(all_webidl_files)) -$(all_webidl_files_absolute): $(CURDIR)/%: % - -$(generated_header_files): .BindingGen - -$(generated_cpp_files): .BindingGen - -# $(binding_dependency_trackers) pick up additional dependencies via .pp files -# The rule: just brings the tracker up to date, if it's out of date, so that -# we'll know that we have to redo binding generation and flag this prerequisite -# there as being newer than the bindinggen target. -$(binding_dependency_trackers): - @$(TOUCH) $@ - -$(globalgen_targets): ParserResults.pkl - -%-example: .BindingGen - PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \ - $(PLY_INCLUDE) -I$(srcdir)/parser \ - $(srcdir)/ExampleGen.py \ - $(srcdir)/Bindings.conf $* - -CACHE_DIR = _cache - -globalgen_dependencies := \ - GlobalGen.py \ - Bindings.conf \ - Configuration.py \ - Codegen.py \ - parser/WebIDL.py \ - webidlsrcs.mk \ - $(all_webidl_files) \ - $(CACHE_DIR)/.done \ +# Most of the logic for dependencies lives inside Python so it can be +# used by multiple build backends. We simply have rules to generate +# and include the .pp file. +# +# The generated .pp file contains all the important dependencies such as +# changes to .webidl or .py files should result in code generation being +# performed. +codegen_dependencies := \ + $(nonstatic_webidl_files) \ $(GLOBAL_DEPS) \ $(NULL) -$(CACHE_DIR)/.done: - $(MKDIR) -p $(CACHE_DIR) +$(call include_deps,codegen.pp) + +codegen.pp: $(codegen_dependencies) + $(call py_action,webidl,$(srcdir)) @$(TOUCH) $@ -# Running GlobalGen.py updates ParserResults.pkl as a side-effect -ParserResults.pkl: $(globalgen_dependencies) - $(info Generating global WebIDL files) - PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \ - $(PLY_INCLUDE) -I$(srcdir)/parser \ - $(srcdir)/GlobalGen.py $(srcdir)/Bindings.conf . \ - --cachedir=$(CACHE_DIR) \ - $(all_webidl_files) - -$(globalgen_headers_FILES): ParserResults.pkl - -# Make sure .deps actually exists, since we'll try to write to it from -# BindingGen.py but we're typically running in the export phase, which is -# before anyone has bothered creating .deps. -# Then, pass our long lists through files to try to avoid blowing out the -# command line. -# Next, BindingGen.py will examine the changed dependency list to figure out -# what it really needs to regenerate. -# Finally, touch the .BindingGen file so that we don't have to keep redoing -# all that until something else actually changes. -.BindingGen: $(bindinggen_dependencies) $(binding_dependency_trackers) - $(info Generating WebIDL bindings) - $(MKDIR) -p .deps - echo $(all_webidl_files) > .all-webidl-file-list - echo $(generated_events_webidl_files) > .generated-events-webidl-files - echo $? > .changed-dependency-list - PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \ - $(PLY_INCLUDE) -I$(srcdir)/parser \ - $(srcdir)/BindingGen.py \ - $(srcdir)/Bindings.conf \ - $(CURDIR) \ - .all-webidl-file-list \ - .generated-events-webidl-files \ - .changed-dependency-list - @$(TOUCH) $@ +.PHONY: compiletests +compiletests: + $(call SUBMAKE,libs,test) GARBAGE += \ - webidlyacc.py \ + codegen.pp \ + codegen.json \ parser.out \ - $(wildcard *-example.h) \ - $(wildcard *-example.cpp) \ - .BindingGen \ - .all-webidl-file-list \ - .generated-events-webidl-files \ - .changed-dependency-list \ - $(binding_dependency_trackers) \ + WebIDLGrammar.pkl \ + $(wildcard *.h) \ + $(wildcard *Binding.cpp) \ + $(wildcard *Event.cpp) \ + $(wildcard *-event.cpp) \ + $(wildcard *.webidl) \ $(NULL) -# Make sure all binding header files are created during the export stage, so we -# don't have issues with .cpp files being compiled before we've generated the -# headers they depend on. This is really only needed for the test files, since -# the non-test headers are all exported above anyway. Note that this means that -# we do all of our codegen during export. -export:: $(generated_header_files) - -distclean:: - -$(RM) \ - $(generated_header_files) \ - $(generated_cpp_files) \ - $(all_webidl_files) \ - $(globalgen_targets) \ - ParserResults.pkl \ - $(NULL) +DIST_GARBAGE += \ + file-lists.json \ + $(NULL) diff --git a/dom/bindings/moz.build b/dom/bindings/moz.build index bd46aa837b75..557d4a63e92f 100644 --- a/dom/bindings/moz.build +++ b/dom/bindings/moz.build @@ -4,6 +4,8 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +TEST_DIRS += ['test'] + EXPORTS.mozilla += [ 'ErrorResult.h', ] diff --git a/dom/bindings/mozwebidlcodegen/__init__.py b/dom/bindings/mozwebidlcodegen/__init__.py index 4fc5d3bbd046..5516dbb3a1a0 100644 --- a/dom/bindings/mozwebidlcodegen/__init__.py +++ b/dom/bindings/mozwebidlcodegen/__init__.py @@ -17,6 +17,7 @@ from copy import deepcopy from mach.mixin.logging import LoggingMixin +from mozbuild.base import MozbuildObject from mozbuild.makeutil import Makefile from mozbuild.pythonutil import iter_modules_in_path from mozbuild.util import FileAvoidWrite @@ -521,3 +522,44 @@ class WebIDLCodegenManager(LoggingMixin): result[1].add(path) else: result[2].add(path) + + +def create_build_system_manager(topsrcdir, topobjdir, dist_dir): + """Create a WebIDLCodegenManager for use by the build system.""" + src_dir = os.path.join(topsrcdir, 'dom', 'bindings') + obj_dir = os.path.join(topobjdir, 'dom', 'bindings') + + with open(os.path.join(obj_dir, 'file-lists.json'), 'rb') as fh: + files = json.load(fh) + + inputs = (files['webidls'], files['exported_stems'], + files['generated_events_stems']) + + cache_dir = os.path.join(obj_dir, '_cache') + try: + os.makedirs(cache_dir) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + return WebIDLCodegenManager( + os.path.join(src_dir, 'Bindings.conf'), + inputs, + os.path.join(dist_dir, 'include', 'mozilla', 'dom'), + obj_dir, + os.path.join(obj_dir, 'codegen.json'), + cache_dir=cache_dir, + # The make rules include a codegen.pp file containing dependencies. + make_deps_path=os.path.join(obj_dir, 'codegen.pp'), + make_deps_target='codegen.pp', + ) + + +class BuildSystemWebIDL(MozbuildObject): + @property + def manager(self): + if not hasattr(self, '_webidl_manager'): + self._webidl_manager = create_build_system_manager( + self.topsrcdir, self.topobjdir, self.distdir) + + return self._webidl_manager diff --git a/dom/bindings/test/Makefile.in b/dom/bindings/test/Makefile.in index 78756bf35a03..78a3cafd020c 100644 --- a/dom/bindings/test/Makefile.in +++ b/dom/bindings/test/Makefile.in @@ -2,89 +2,23 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this file, # You can obtain one at http://mozilla.org/MPL/2.0/. -# Do NOT export this library. We don't actually want our test code -# being added to libxul or anything. - -# pymake can't handle descending into dom/bindings several times simultaneously -ifdef .PYMAKE -.NOTPARALLEL: -endif - -# Need this for $(test_webidl_files) include ../webidlsrcs.mk -# But the webidl actually lives in our parent dir -test_webidl_files := $(addprefix ../,$(test_webidl_files)) -# Store the actual locations of our source preprocessed files, so we -# can depend on them sanely. -source_preprocessed_test_webidl_files := $(addprefix $(srcdir)/,$(preprocessed_test_webidl_files)) -preprocessed_test_webidl_files := $(addprefix ../,$(preprocessed_test_webidl_files)) - -CPPSRCS += \ - $(subst .webidl,Binding.cpp,$(test_webidl_files)) \ - $(subst .webidl,Binding.cpp,$(preprocessed_test_webidl_files)) \ - $(NULL) - -# If you change bindinggen_dependencies here, change it in -# dom/bindings/Makefile.in too. But note that we include ../Makefile -# here manually, since $(GLOBAL_DEPS) won't cover it. -bindinggen_dependencies := \ - ../BindingGen.py \ - ../Bindings.conf \ - ../Configuration.py \ - ../Codegen.py \ - ../ParserResults.pkl \ - ../parser/WebIDL.py \ - ../Makefile \ - $(GLOBAL_DEPS) \ - $(NULL) +# $(test_sources) comes from webidlsrcs.mk. +# TODO Update this variable in backend.mk. +CPPSRCS += $(addprefix ../,$(test_sources)) ifdef GNU_CC -CXXFLAGS += -Wno-uninitialized +OS_CXXFLAGS += -Wno-uninitialized endif +# Bug 932082 tracks having bindings use namespaced includes. +LOCAL_INCLUDES += -I$(DIST)/include/mozilla/dom -I.. + # Include rules.mk before any of our targets so our first target is coming from # rules.mk and running make with no target in this dir does the right thing. include $(topsrcdir)/config/rules.mk -$(CPPSRCS): .BindingGen - -.BindingGen: $(bindinggen_dependencies) \ - $(test_webidl_files) \ - $(source_preprocessed_test_webidl_files) \ - $(NULL) - # The export phase in dom/bindings is what actually looks at - # dependencies and regenerates things as needed, so just go ahead and - # make that phase here. Also make our example interface files. If the - # target used here ever changes, change the conditional around - # $(CPPSRCS) in dom/bindings/Makefile.in. - $(MAKE) -C .. export TestExampleInterface-example TestExampleProxyInterface-example - @$(TOUCH) $@ - check:: PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \ $(PLY_INCLUDE) $(srcdir)/../parser/runtests.py - -# Since we define MOCHITEST_FILES, config/makefiles/mochitest.mk goes ahead and -# sets up a rule with libs:: in itm which makes our .DEFAULT_TARGET be "libs". -# Then ruls.mk does |.DEFAULT_TARGET ?= default| which leaves it as "libs". So -# if we make without an explicit target in this directory, we try to make -# "libs", but with a $(MAKECMDGOALS) of empty string. And then rules.mk -# helpfully does not include our *.o.pp files, since it includes them only if -# filtering some stuff out from $(MAKECMDGOALS) leaves it nonempty. The upshot -# is that if some headers change and we run make in this dir without an explicit -# target things don't get rebuilt. -# -# On the other hand, if we set .DEFAULT_TARGET to "default" explicitly here, -# then rules.mk will reinvoke make with "export" and "libs" but this time hey -# will be passed as explicit targets, show up in $(MAKECMDGOALS), and things -# will work. Do this at the end of our Makefile so the rest of the build system -# does not get a chance to muck with it after we set it. -.DEFAULT_GOAL := default - -# Make sure to add .BindingGen to GARBAGE so we'll rebuild our example -# files if someone goes through and deletes GARBAGE all over, which -# will delete example files from our parent dir. -GARBAGE += \ - .BindingGen \ - $(NULL) diff --git a/dom/bindings/test/moz.build b/dom/bindings/test/moz.build index 82db3c691cbc..5b129eee9e20 100644 --- a/dom/bindings/test/moz.build +++ b/dom/bindings/test/moz.build @@ -14,9 +14,20 @@ MOCHITEST_MANIFESTS += ['mochitest.ini'] MOCHITEST_CHROME_MANIFESTS += ['chrome.ini'] +TEST_WEBIDL_FILES += [ + 'TestDictionary.webidl', + 'TestJSImplInheritanceGen.webidl', + 'TestTypedef.webidl', +] + +PREPROCESSED_TEST_WEBIDL_FILES += [ + 'TestCodeGen.webidl', + 'TestExampleGen.webidl', + 'TestJSImplGen.webidl', +] + LOCAL_INCLUDES += [ '/dom/bindings', '/js/xpconnect/src', '/js/xpconnect/wrappers', ] - diff --git a/dom/moz.build b/dom/moz.build index 09bf93c46ed5..60aa95f2991f 100644 --- a/dom/moz.build +++ b/dom/moz.build @@ -107,12 +107,9 @@ if CONFIG['MOZ_NFC']: if CONFIG['MOZ_B2G']: PARALLEL_DIRS += ['downloads'] -# bindings/test is here, because it needs to build after bindings/, and -# we build subdirectories before ourselves. TEST_DIRS += [ 'tests', 'imptests', - 'bindings/test', ] if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'cocoa', 'windows', 'android', 'qt', 'os2'): diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 108b38a1b3ec..e3f970284f36 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -553,18 +553,6 @@ if CONFIG['MOZ_B2G_FM']: 'FMRadio.webidl', ] -if CONFIG['ENABLE_TESTS']: - TEST_WEBIDL_FILES += [ - 'TestDictionary.webidl', - 'TestJSImplInheritanceGen.webidl', - 'TestTypedef.webidl', - ] - PREPROCESSED_TEST_WEBIDL_FILES += [ - 'TestCodeGen.webidl', - 'TestExampleGen.webidl', - 'TestJSImplGen.webidl', - ] - GENERATED_EVENTS_WEBIDL_FILES = [ 'BlobEvent.webidl', 'CallGroupErrorEvent.webidl', diff --git a/python/mozbuild/mozbuild/action/webidl.py b/python/mozbuild/mozbuild/action/webidl.py new file mode 100644 index 000000000000..6702701228e5 --- /dev/null +++ b/python/mozbuild/mozbuild/action/webidl.py @@ -0,0 +1,17 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import sys + +from mozwebidlcodegen import BuildSystemWebIDL + + +def main(argv): + """Perform WebIDL code generation required by the build system.""" + manager = BuildSystemWebIDL.from_environment().manager + manager.generate_build_files() + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/python/mozbuild/mozbuild/backend/common.py b/python/mozbuild/mozbuild/backend/common.py index eec09f7eecb8..0a6fe8ad4049 100644 --- a/python/mozbuild/mozbuild/backend/common.py +++ b/python/mozbuild/mozbuild/backend/common.py @@ -10,15 +10,19 @@ import re import mozpack.path as mozpath -from mozbuild.preprocessor import Preprocessor - from .base import BuildBackend from ..frontend.data import ( ConfigFileSubstitution, HeaderFileSubstitution, + GeneratedEventWebIDLFile, + GeneratedWebIDLFile, + PreprocessedTestWebIDLFile, + PreprocessedWebIDLFile, TestManifest, + TestWebIDLFile, XPIDLFile, + WebIDLFile, ) from ..util import DefaultOnReadDict @@ -56,6 +60,79 @@ class XPIDLManager(object): self.modules.setdefault(entry['module'], set()).add(entry['root']) +class WebIDLCollection(object): + """Collects WebIDL info referenced during the build.""" + + def __init__(self): + self.sources = set() + self.generated_sources = set() + self.generated_events_sources = set() + self.preprocessed_sources = set() + self.test_sources = set() + self.preprocessed_test_sources = set() + + def all_regular_sources(self): + return self.sources | self.generated_sources | \ + self.generated_events_sources | self.preprocessed_sources + + def all_regular_basenames(self): + return [os.path.basename(source) for source in self.all_regular_sources()] + + def all_regular_stems(self): + return [os.path.splitext(b)[0] for b in self.all_regular_basenames()] + + def all_regular_bindinggen_stems(self): + for stem in self.all_regular_stems(): + yield '%sBinding' % stem + + for source in self.generated_events_sources: + yield os.path.splitext(os.path.basename(source))[0] + + def all_regular_cpp_basenames(self): + for stem in self.all_regular_bindinggen_stems(): + yield '%s.cpp' % stem + + def all_test_sources(self): + return self.test_sources | self.preprocessed_test_sources + + def all_test_basenames(self): + return [os.path.basename(source) for source in self.all_test_sources()] + + def all_test_stems(self): + return [os.path.splitext(b)[0] for b in self.all_test_basenames()] + + def all_test_cpp_basenames(self): + return ['%sBinding.cpp' % s for s in self.all_test_stems()] + + def all_static_sources(self): + return self.sources | self.generated_events_sources | \ + self.test_sources + + def all_non_static_sources(self): + return self.generated_sources | self.all_preprocessed_sources() + + def all_non_static_basenames(self): + return [os.path.basename(s) for s in self.all_non_static_sources()] + + def all_preprocessed_sources(self): + return self.preprocessed_sources | self.preprocessed_test_sources + + def all_sources(self): + return set(self.all_regular_sources()) | set(self.all_test_sources()) + + def all_basenames(self): + return [os.path.basename(source) for source in self.all_sources()] + + def all_stems(self): + return [os.path.splitext(b)[0] for b in self.all_basenames()] + + def generated_events_basenames(self): + return [os.path.basename(s) for s in self.generated_events_sources] + + def generated_events_stems(self): + return [os.path.splitext(b)[0] for b in self.generated_events_basenames()] + + class TestManager(object): """Helps hold state related to tests.""" @@ -90,6 +167,7 @@ class CommonBackend(BuildBackend): def _init(self): self._idl_manager = XPIDLManager(self.environment) self._test_manager = TestManager(self.environment) + self._webidls = WebIDLCollection() def consume_object(self, obj): if isinstance(obj, TestManifest): @@ -113,6 +191,30 @@ class CommonBackend(BuildBackend): self._create_config_header(obj) self.backend_input_files.add(obj.input_path) + # We should consider aggregating WebIDL types in emitter.py. + elif isinstance(obj, WebIDLFile): + self._webidls.sources.add(mozpath.join(obj.srcdir, obj.basename)) + + elif isinstance(obj, GeneratedEventWebIDLFile): + self._webidls.generated_events_sources.add(mozpath.join( + obj.srcdir, obj.basename)) + + elif isinstance(obj, TestWebIDLFile): + self._webidls.test_sources.add(mozpath.join(obj.srcdir, + obj.basename)) + + elif isinstance(obj, PreprocessedTestWebIDLFile): + self._webidls.preprocessed_test_sources.add(mozpath.join( + obj.srcdir, obj.basename)) + + elif isinstance(obj, GeneratedWebIDLFile): + self._webidls.generated_sources.add(mozpath.join(obj.srcdir, + obj.basename)) + + elif isinstance(obj, PreprocessedWebIDLFile): + self._webidls.preprocessed_sources.add(mozpath.join( + obj.srcdir, obj.basename)) + else: return @@ -122,6 +224,8 @@ class CommonBackend(BuildBackend): if len(self._idl_manager.idls): self._handle_idl_manager(self._idl_manager) + self._handle_webidl_collection(self._webidls) + # Write out a machine-readable file describing every test. path = mozpath.join(self.environment.topobjdir, 'all-tests.json') with self._write_file(path) as fh: diff --git a/python/mozbuild/mozbuild/backend/recursivemake.py b/python/mozbuild/mozbuild/backend/recursivemake.py index 3742ad4f340b..c9a4000dd46a 100644 --- a/python/mozbuild/mozbuild/backend/recursivemake.py +++ b/python/mozbuild/mozbuild/backend/recursivemake.py @@ -5,13 +5,15 @@ from __future__ import unicode_literals import itertools +import json import logging import os -import re import types from collections import namedtuple +import mozwebidlcodegen + import mozbuild.makeutil as mozmakeutil from mozpack.copier import FilePurger from mozpack.manifests import ( @@ -25,9 +27,7 @@ from ..frontend.data import ( Defines, DirectoryTraversal, Exports, - GeneratedEventWebIDLFile, GeneratedInclude, - GeneratedWebIDLFile, HostProgram, HostSimpleProgram, InstallationTarget, @@ -35,17 +35,13 @@ from ..frontend.data import ( JavaJarData, LibraryDefinition, LocalInclude, - PreprocessedTestWebIDLFile, - PreprocessedWebIDLFile, Program, SandboxDerived, SandboxWrapped, SimpleProgram, - TestWebIDLFile, + TestManifest, VariablePassthru, XPIDLFile, - TestManifest, - WebIDLFile, ) from ..util import ( ensureParentDir, @@ -270,12 +266,6 @@ class RecursiveMakeBackend(CommonBackend): self._backend_files = {} self._ipdl_sources = set() - self._webidl_sources = set() - self._generated_events_webidl_sources = set() - self._test_webidl_sources = set() - self._preprocessed_test_webidl_sources = set() - self._preprocessed_webidl_sources = set() - self._generated_webidl_sources = set() def detailed(summary): s = '{:d} total backend files. {:d} created; {:d} updated; {:d} unchanged'.format( @@ -410,33 +400,6 @@ class RecursiveMakeBackend(CommonBackend): elif isinstance(obj, IPDLFile): self._ipdl_sources.add(mozpath.join(obj.srcdir, obj.basename)) - elif isinstance(obj, WebIDLFile): - self._webidl_sources.add(mozpath.join(obj.srcdir, obj.basename)) - self._process_webidl_basename(obj.basename) - - elif isinstance(obj, GeneratedEventWebIDLFile): - self._generated_events_webidl_sources.add(mozpath.join(obj.srcdir, obj.basename)) - - elif isinstance(obj, TestWebIDLFile): - self._test_webidl_sources.add(mozpath.join(obj.srcdir, - obj.basename)) - # Test WebIDL files are not exported. - - elif isinstance(obj, PreprocessedTestWebIDLFile): - self._preprocessed_test_webidl_sources.add(mozpath.join(obj.srcdir, - obj.basename)) - # Test WebIDL files are not exported. - - elif isinstance(obj, GeneratedWebIDLFile): - self._generated_webidl_sources.add(mozpath.join(obj.srcdir, - obj.basename)) - self._process_webidl_basename(obj.basename) - - elif isinstance(obj, PreprocessedWebIDLFile): - self._preprocessed_webidl_sources.add(mozpath.join(obj.srcdir, - obj.basename)) - self._process_webidl_basename(obj.basename) - elif isinstance(obj, Program): self._process_program(obj.program, backend_file) @@ -607,6 +570,9 @@ class RecursiveMakeBackend(CommonBackend): poison_windows_h=False, files_per_unified_file=16): + # In case it's a generator. + files = sorted(files) + explanation = "\n" \ "# We build files in 'unified' mode by including several files\n" \ "# together into a single source file. This cuts down on\n" \ @@ -632,7 +598,7 @@ class RecursiveMakeBackend(CommonBackend): return itertools.izip_longest(fillvalue=dummy_fill_value, *args) for i, unified_group in enumerate(grouper(files_per_unified_file, - sorted(files))): + files)): just_the_filenames = list(filter_out_dummy(unified_group)) yield '%s%d.%s' % (unified_prefix, i, unified_suffix), just_the_filenames @@ -750,42 +716,12 @@ class RecursiveMakeBackend(CommonBackend): with self._write_file(mozpath.join(ipdl_dir, 'ipdlsrcs.mk')) as ipdls: mk.dump(ipdls, removal_guard=False) - self._may_skip['compile'] -= set(['ipc/ipdl']) - - # Write out master lists of WebIDL source files. - bindings_dir = mozpath.join(self.environment.topobjdir, 'dom', 'bindings') - - mk = mozmakeutil.Makefile() - - def write_var(variable, sources): - files = [mozpath.basename(f) for f in sorted(sources)] - mk.add_statement('%s += %s' % (variable, ' '.join(files))) - write_var('webidl_files', self._webidl_sources) - write_var('generated_events_webidl_files', self._generated_events_webidl_sources) - write_var('test_webidl_files', self._test_webidl_sources) - write_var('preprocessed_test_webidl_files', self._preprocessed_test_webidl_sources) - write_var('generated_webidl_files', self._generated_webidl_sources) - write_var('preprocessed_webidl_files', self._preprocessed_webidl_sources) - - all_webidl_files = itertools.chain(iter(self._webidl_sources), - iter(self._generated_events_webidl_sources), - iter(self._generated_webidl_sources), - iter(self._preprocessed_webidl_sources)) - all_webidl_files = [mozpath.basename(x) for x in all_webidl_files] - all_webidl_sources = [re.sub(r'\.webidl$', 'Binding.cpp', x) for x in all_webidl_files] - - self._add_unified_build_rules(mk, all_webidl_sources, - bindings_dir, - unified_prefix='UnifiedBindings', - unified_files_makefile_variable='unified_binding_cpp_files', - poison_windows_h=True) - - # Assume that Somebody Else has responsibility for correctly - # specifying removal dependencies for |all_webidl_sources|. - with self._write_file(mozpath.join(bindings_dir, 'webidlsrcs.mk')) as webidls: - mk.dump(webidls, removal_guard=False) - - self._may_skip['compile'] -= set(['dom/bindings', 'dom/bindings/test']) + # These contain autogenerated sources that the build config doesn't + # yet know about. + # TODO Emit GENERATED_SOURCES so these special cases are dealt with + # the proper way. + self._may_skip['compile'] -= {'ipc/ipdl'} + self._may_skip['compile'] -= {'dom/bindings', 'dom/bindings/test'} self._fill_root_mk() @@ -1026,10 +962,6 @@ class RecursiveMakeBackend(CommonBackend): def _process_host_simple_program(self, program, backend_file): backend_file.write('HOST_SIMPLE_PROGRAMS += %s\n' % program) - def _process_webidl_basename(self, basename): - header = 'mozilla/dom/%sBinding.h' % mozpath.splitext(basename)[0] - self._install_manifests['dist_include'].add_optional_exists(header) - def _process_test_manifest(self, obj, backend_file): # Much of the logic in this function could be moved to CommonBackend. self.backend_input_files.add(mozpath.join(obj.topsrcdir, @@ -1171,3 +1103,86 @@ class RecursiveMakeBackend(CommonBackend): # Makefile.in files, the old file will get replaced with # the autogenerated one automatically. self.backend_input_files.add(obj.input_path) + + def _handle_webidl_collection(self, webidls): + if not webidls.all_stems(): + return + + bindings_dir = mozpath.join(self.environment.topobjdir, 'dom', + 'bindings') + + all_inputs = set(webidls.all_static_sources()) + for s in webidls.all_non_static_basenames(): + all_inputs.add(mozpath.join(bindings_dir, s)) + + generated_events_stems = webidls.generated_events_stems() + exported_stems = webidls.all_regular_stems() + + # The WebIDL manager reads configuration from a JSON file. So, we + # need to write this file early. + o = dict( + webidls=sorted(all_inputs), + generated_events_stems=sorted(generated_events_stems), + exported_stems=sorted(exported_stems), + ) + + file_lists = mozpath.join(bindings_dir, 'file-lists.json') + with self._write_file(file_lists) as fh: + json.dump(o, fh, sort_keys=True, indent=2) + + manager = mozwebidlcodegen.create_build_system_manager( + self.environment.topsrcdir, + self.environment.topobjdir, + mozpath.join(self.environment.topobjdir, 'dist') + ) + + # The manager is the source of truth on what files are generated. + # Consult it for install manifests. + include_dir = mozpath.join(self.environment.topobjdir, 'dist', + 'include') + for f in manager.expected_build_output_files(): + if f.startswith(include_dir): + self._install_manifests['dist_include'].add_optional_exists( + mozpath.relpath(f, include_dir)) + + # We pass WebIDL info to make via a completely generated make file. + mk = Makefile() + mk.add_statement('nonstatic_webidl_files := %s' % ' '.join( + sorted(webidls.all_non_static_basenames()))) + mk.add_statement('globalgen_sources := %s' % ' '.join( + sorted(manager.GLOBAL_DEFINE_FILES))) + mk.add_statement('test_sources := %s' % ' '.join( + sorted('%sBinding.cpp' % s for s in webidls.all_test_stems()))) + + # Add rules to preprocess bindings. + # This should ideally be using PP_TARGETS. However, since the input + # filenames match the output filenames, the existing PP_TARGETS rules + # result in circular dependencies and other make weirdness. One + # solution is to rename the input or output files repsectively. See + # bug 928195 comment 129. + for source in sorted(webidls.all_preprocessed_sources()): + basename = os.path.basename(source) + rule = mk.create_rule([basename]) + rule.add_dependencies([source, '$(GLOBAL_DEPS)']) + rule.add_commands([ + # Remove the file before writing so bindings that go from + # static to preprocessed don't end up writing to a symlink, + # which would modify content in the source directory. + '$(RM) $@', + '$(call py_action,preprocessor,$(DEFINES) $(ACDEFINES) ' + '$(XULPPFLAGS) $< -o $@)' + ]) + + # Bindings are compiled in unified mode to speed up compilation and + # to reduce linker memory size. Note that test bindings are separated + # from regular ones so tests bindings aren't shipped. + self._add_unified_build_rules(mk, + webidls.all_regular_cpp_basenames(), + bindings_dir, + unified_prefix='UnifiedBindings', + unified_files_makefile_variable='unified_binding_cpp_files', + poison_windows_h=True) + + webidls_mk = mozpath.join(bindings_dir, 'webidlsrcs.mk') + with self._write_file(webidls_mk) as fh: + mk.dump(fh, removal_guard=False) diff --git a/python/mozbuild/mozbuild/base.py b/python/mozbuild/mozbuild/base.py index 0c006855d0d4..6043c4e8f3e5 100644 --- a/python/mozbuild/mozbuild/base.py +++ b/python/mozbuild/mozbuild/base.py @@ -273,6 +273,10 @@ class MozbuildObject(ProcessExecutionMixin): def bindir(self): return os.path.join(self.topobjdir, 'dist', 'bin') + @property + def includedir(self): + return os.path.join(self.topobjdir, 'dist', 'include') + @property def statedir(self): return os.path.join(self.topobjdir, '.mozbuild') From faf292b942f8045390ec7af8d1b3116d36384a7f Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Thu, 14 Nov 2013 10:34:55 -0800 Subject: [PATCH 003/144] Bug 928195 - Part 5: mach command for generating WebIDL example files; r=froydnj --HG-- extra : rebase_source : c74d5f9556ca866622b7fefd4711e96fe4e4e020 --- dom/bindings/mach_commands.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/dom/bindings/mach_commands.py b/dom/bindings/mach_commands.py index e176d91cd4e3..8b890c94b319 100644 --- a/dom/bindings/mach_commands.py +++ b/dom/bindings/mach_commands.py @@ -18,6 +18,17 @@ from mozbuild.base import MachCommandBase @CommandProvider class WebIDLProvider(MachCommandBase): + @Command('webidl-example', category='misc', + description='Generate example files for a WebIDL interface.') + @CommandArgument('interface', nargs='+', + help='Interface(s) whose examples to generate.') + def webidl_example(self, interface): + from mozwebidlcodegen import BuildSystemWebIDL + + manager = self._spawn(BuildSystemWebIDL).manager + for i in interface: + manager.generate_example_files(i) + @Command('webidl-parser-test', category='testing', description='Run WebIDL tests.') @CommandArgument('--verbose', '-v', action='store_true', From 4190812d0a0f93dd62c1ba0644e5cb89408a49b4 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Fri, 13 Dec 2013 14:24:36 +1100 Subject: [PATCH 004/144] Bug 935793 (part 1) - Add ownerIsBrowserOrApp to nsIFrameLoader. r=smaug --HG-- extra : rebase_source : 189b45b39c04e4272d7b0cc188f35bb02bc19970 --- content/base/public/nsIFrameLoader.idl | 7 ++++++- content/base/src/nsFrameLoader.cpp | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/content/base/public/nsIFrameLoader.idl b/content/base/public/nsIFrameLoader.idl index a2f3e812d621..bcbc5fd93028 100644 --- a/content/base/public/nsIFrameLoader.idl +++ b/content/base/public/nsIFrameLoader.idl @@ -111,7 +111,7 @@ interface nsIContentViewManager : nsISupports readonly attribute nsIContentView rootContentView; }; -[scriptable, builtinclass, uuid(5b9949dc-56f1-47b6-b6d2-3785bb90ed6d)] +[scriptable, builtinclass, uuid(a723673b-a26e-4cc6-ae23-ec70df9d97c9)] interface nsIFrameLoader : nsISupports { /** @@ -272,6 +272,11 @@ interface nsIFrameLoader : nsISupports * have a notion of visibility in the parent process when frames are OOP. */ [infallible] attribute boolean visible; + + /** + * Find out whether the owner content really is a browser or app frame + */ + readonly attribute boolean ownerIsBrowserOrAppFrame; }; %{C++ diff --git a/content/base/src/nsFrameLoader.cpp b/content/base/src/nsFrameLoader.cpp index 1c4d99da8d62..306a90fedc4c 100644 --- a/content/base/src/nsFrameLoader.cpp +++ b/content/base/src/nsFrameLoader.cpp @@ -1409,6 +1409,14 @@ nsFrameLoader::OwnerIsBrowserOrAppFrame() return browserFrame ? browserFrame->GetReallyIsBrowserOrApp() : false; } +// The xpcom getter version +NS_IMETHODIMP +nsFrameLoader::GetOwnerIsBrowserOrAppFrame(bool* aResult) +{ + *aResult = OwnerIsBrowserOrAppFrame(); + return NS_OK; +} + bool nsFrameLoader::OwnerIsAppFrame() { From 4834261c634406983a4a33278dc3139867b6b2db Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Fri, 13 Dec 2013 14:24:37 +1100 Subject: [PATCH 005/144] Bug 935793 (part 2) - rename browser-shown notifications and send them for all frames. r=smaug --HG-- extra : rebase_source : 475b2cafc094117a2edf8ba95605e7f4d5f95410 --- accessible/src/jsat/AccessFu.jsm | 20 ++++++++++------- b2g/components/ErrorPage.jsm | 8 +++++-- content/base/src/nsFrameLoader.cpp | 24 ++++++++++----------- dom/browser-element/BrowserElementParent.js | 16 ++++++++++---- dom/inputmethod/Keyboard.jsm | 8 +++++-- dom/ipc/ProcessPriorityManager.cpp | 11 ++++++++-- 6 files changed, 56 insertions(+), 31 deletions(-) diff --git a/accessible/src/jsat/AccessFu.jsm b/accessible/src/jsat/AccessFu.jsm index c0cf612968c5..fb7bcdbbd263 100644 --- a/accessible/src/jsat/AccessFu.jsm +++ b/accessible/src/jsat/AccessFu.jsm @@ -118,8 +118,8 @@ this.AccessFu = { Output.start(); TouchAdapter.start(); - Services.obs.addObserver(this, 'remote-browser-frame-shown', false); - Services.obs.addObserver(this, 'in-process-browser-or-app-frame-shown', false); + Services.obs.addObserver(this, 'remote-browser-shown', false); + Services.obs.addObserver(this, 'inprocess-browser-shown', false); Services.obs.addObserver(this, 'Accessibility:NextObject', false); Services.obs.addObserver(this, 'Accessibility:PreviousObject', false); Services.obs.addObserver(this, 'Accessibility:Focus', false); @@ -162,8 +162,8 @@ this.AccessFu = { Utils.win.removeEventListener('TabClose', this); Utils.win.removeEventListener('TabSelect', this); - Services.obs.removeObserver(this, 'remote-browser-frame-shown'); - Services.obs.removeObserver(this, 'in-process-browser-or-app-frame-shown'); + Services.obs.removeObserver(this, 'remote-browser-shown'); + Services.obs.removeObserver(this, 'inprocess-browser-shown'); Services.obs.removeObserver(this, 'Accessibility:NextObject'); Services.obs.removeObserver(this, 'Accessibility:PreviousObject'); Services.obs.removeObserver(this, 'Accessibility:Focus'); @@ -304,11 +304,15 @@ this.AccessFu = { case 'Accessibility:MoveByGranularity': this.Input.moveByGranularity(JSON.parse(aData)); break; - case 'remote-browser-frame-shown': - case 'in-process-browser-or-app-frame-shown': + case 'remote-browser-shown': + case 'inprocess-browser-shown': { - let mm = aSubject.QueryInterface(Ci.nsIFrameLoader).messageManager; - this._handleMessageManager(mm); + // Ignore notifications that aren't from a BrowserOrApp + let frameLoader = aSubject.QueryInterface(Ci.nsIFrameLoader); + if (!frameLoader.ownerIsBrowserOrAppFrame) { + return; + } + this._handleMessageManager(frameLoader.messageManager); break; } } diff --git a/b2g/components/ErrorPage.jsm b/b2g/components/ErrorPage.jsm index 8b6c2985e638..ba9cb3af03e0 100644 --- a/b2g/components/ErrorPage.jsm +++ b/b2g/components/ErrorPage.jsm @@ -150,12 +150,16 @@ let ErrorPage = { }, init: function errorPageInit() { - Services.obs.addObserver(this, 'in-process-browser-or-app-frame-shown', false); - Services.obs.addObserver(this, 'remote-browser-frame-shown', false); + Services.obs.addObserver(this, 'inprocess-browser-shown', false); + Services.obs.addObserver(this, 'remote-browser-shown', false); }, observe: function errorPageObserve(aSubject, aTopic, aData) { let frameLoader = aSubject.QueryInterface(Ci.nsIFrameLoader); + // Ignore notifications that aren't from a BrowserOrApp + if (!frameLoader.ownerIsBrowserOrAppFrame) { + return; + } let mm = frameLoader.messageManager; // This won't happen from dom/ipc/preload.js in non-OOP builds. diff --git a/content/base/src/nsFrameLoader.cpp b/content/base/src/nsFrameLoader.cpp index 306a90fedc4c..9c74e96b4ae9 100644 --- a/content/base/src/nsFrameLoader.cpp +++ b/content/base/src/nsFrameLoader.cpp @@ -948,9 +948,9 @@ nsFrameLoader::ShowRemoteFrame(const nsIntSize& size, EnsureMessageManager(); nsCOMPtr os = services::GetObserverService(); - if (OwnerIsBrowserOrAppFrame() && os && !mRemoteBrowserInitialized) { + if (os && !mRemoteBrowserInitialized) { os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), - "remote-browser-frame-shown", nullptr); + "remote-browser-shown", nullptr); mRemoteBrowserInitialized = true; } } else { @@ -1685,18 +1685,16 @@ nsFrameLoader::MaybeCreateDocShell() mDocShell->SetIsBrowserInsideApp(containingAppId); } - if (OwnerIsBrowserOrAppFrame()) { - nsCOMPtr os = services::GetObserverService(); - if (os) { - os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), - "in-process-browser-or-app-frame-shown", nullptr); - } + nsCOMPtr os = services::GetObserverService(); + if (os) { + os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), + "inprocess-browser-shown", nullptr); + } - if (mMessageManager) { - mMessageManager->LoadFrameScript( - NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js"), - /* allowDelayedLoad = */ true); - } + if (OwnerIsBrowserOrAppFrame() && mMessageManager) { + mMessageManager->LoadFrameScript( + NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js"), + /* allowDelayedLoad = */ true); } return NS_OK; diff --git a/dom/browser-element/BrowserElementParent.js b/dom/browser-element/BrowserElementParent.js index 9bd9dd4280a3..b4741d477e0a 100644 --- a/dom/browser-element/BrowserElementParent.js +++ b/dom/browser-element/BrowserElementParent.js @@ -65,8 +65,8 @@ BrowserElementParentFactory.prototype = { // alive for as long as its frame element lives. this._bepMap = new WeakMap(); - Services.obs.addObserver(this, 'remote-browser-frame-shown', /* ownsWeak = */ true); - Services.obs.addObserver(this, 'in-process-browser-or-app-frame-shown', /* ownsWeak = */ true); + Services.obs.addObserver(this, 'remote-browser-shown', /* ownsWeak = */ true); + Services.obs.addObserver(this, 'inprocess-browser-shown', /* ownsWeak = */ true); }, _browserFramesPrefEnabled: function() { @@ -79,11 +79,19 @@ BrowserElementParentFactory.prototype = { }, _observeInProcessBrowserFrameShown: function(frameLoader) { + // Ignore notifications that aren't from a BrowserOrApp + if (!frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerIsBrowserOrAppFrame) { + return; + } debug("In-process browser frame shown " + frameLoader); this._createBrowserElementParent(frameLoader, /* hasRemoteFrame = */ false); }, _observeRemoteBrowserFrameShown: function(frameLoader) { + // Ignore notifications that aren't from a BrowserOrApp + if (!frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerIsBrowserOrAppFrame) { + return; + } debug("Remote browser frame shown " + frameLoader); this._createBrowserElementParent(frameLoader, /* hasRemoteFrame = */ true); }, @@ -103,10 +111,10 @@ BrowserElementParentFactory.prototype = { this._init(); } break; - case 'remote-browser-frame-shown': + case 'remote-browser-shown': this._observeRemoteBrowserFrameShown(subject); break; - case 'in-process-browser-or-app-frame-shown': + case 'inprocess-browser-shown': this._observeInProcessBrowserFrameShown(subject); break; case 'content-document-global-created': diff --git a/dom/inputmethod/Keyboard.jsm b/dom/inputmethod/Keyboard.jsm index 14b56041be66..94456ff8d510 100644 --- a/dom/inputmethod/Keyboard.jsm +++ b/dom/inputmethod/Keyboard.jsm @@ -44,8 +44,8 @@ this.Keyboard = { }, init: function keyboardInit() { - Services.obs.addObserver(this, 'in-process-browser-or-app-frame-shown', false); - Services.obs.addObserver(this, 'remote-browser-frame-shown', false); + Services.obs.addObserver(this, 'inprocess-browser-shown', false); + Services.obs.addObserver(this, 'remote-browser-shown', false); Services.obs.addObserver(this, 'oop-frameloader-crashed', false); for (let name of this._messageNames) @@ -63,6 +63,10 @@ this.Keyboard = { ppmm.broadcastAsyncMessage('Keyboard:FocusChange', { 'type': 'blur' }); } } else { + // Ignore notifications that aren't from a BrowserOrApp + if (!frameLoader.ownerIsBrowserOrAppFrame) { + return; + } this.initFormsFrameScript(mm); } }, diff --git a/dom/ipc/ProcessPriorityManager.cpp b/dom/ipc/ProcessPriorityManager.cpp index d6bb96cd352c..b9c34d7801cc 100644 --- a/dom/ipc/ProcessPriorityManager.cpp +++ b/dom/ipc/ProcessPriorityManager.cpp @@ -602,7 +602,7 @@ ParticularProcessPriorityManager::Init() nsCOMPtr os = services::GetObserverService(); if (os) { os->AddObserver(this, "audio-channel-process-changed", /* ownsWeak */ true); - os->AddObserver(this, "remote-browser-frame-shown", /* ownsWeak */ true); + os->AddObserver(this, "remote-browser-shown", /* ownsWeak */ true); os->AddObserver(this, "ipc:browser-destroyed", /* ownsWeak */ true); os->AddObserver(this, "frameloader-visible-changed", /* ownsWeak */ true); } @@ -674,7 +674,7 @@ ParticularProcessPriorityManager::Observe(nsISupports* aSubject, if (topic.EqualsLiteral("audio-channel-process-changed")) { OnAudioChannelProcessChanged(aSubject); - } else if (topic.EqualsLiteral("remote-browser-frame-shown")) { + } else if (topic.EqualsLiteral("remote-browser-shown")) { OnRemoteBrowserFrameShown(aSubject); } else if (topic.EqualsLiteral("ipc:browser-destroyed")) { OnTabParentDestroyed(aSubject); @@ -747,6 +747,13 @@ ParticularProcessPriorityManager::OnRemoteBrowserFrameShown(nsISupports* aSubjec nsCOMPtr fl = do_QueryInterface(aSubject); NS_ENSURE_TRUE_VOID(fl); + // Ignore notifications that aren't from a BrowserOrApp + bool isBrowserOrApp; + fl->GetOwnerIsBrowserOrAppFrame(&isBrowserOrApp); + if (!isBrowserOrApp) { + return; + } + nsCOMPtr tp; fl->GetTabParent(getter_AddRefs(tp)); NS_ENSURE_TRUE_VOID(tp); From 59eb74b6f543a3a03d5707847cb8f138e35f9f7f Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Thu, 12 Dec 2013 19:38:49 -0800 Subject: [PATCH 006/144] Bug 944492, part 1 - Make XPCWrappedJS use the purple buffer. r=smaug --- js/xpconnect/src/XPCWrappedJS.cpp | 128 ++++++++++++++++++++---------- js/xpconnect/src/xpcprivate.h | 15 ++-- 2 files changed, 92 insertions(+), 51 deletions(-) diff --git a/js/xpconnect/src/XPCWrappedJS.cpp b/js/xpconnect/src/XPCWrappedJS.cpp index f97b28c84ca2..f0217a990cf2 100644 --- a/js/xpconnect/src/XPCWrappedJS.cpp +++ b/js/xpconnect/src/XPCWrappedJS.cpp @@ -16,6 +16,40 @@ using namespace mozilla; // NOTE: much of the fancy footwork is done in xpcstubs.cpp + +// nsXPCWrappedJS lifetime. +// +// An nsXPCWrappedJS is either rooting its JS object or is subject to finalization. +// The subject-to-finalization state lets wrappers support +// nsSupportsWeakReference in the case where the underlying JS object +// is strongly owned, but the wrapper itself is only weakly owned. +// +// A wrapper is rooting its JS object whenever its refcount is greater than 1. In +// this state, root wrappers are always added to the cycle collector graph. The +// wrapper keeps around an extra refcount, added in the constructor, to support +// the possibility of an eventual transition to the subject-to-finalization state. +// This extra refcount is ignored by the cycle collector, which traverses the "self" +// edge for this refcount. +// +// When the refcount of a rooting wrapper drops to 1, if there is no weak reference +// to the wrapper (which can only happen for the root wrapper), it is immediately +// Destroy()'d. Otherwise, it becomes subject to finalization. +// +// When a wrapper is subject to finalization, the wrapper has a refcount of 1. It is +// now owned exclusively by its JS object. Either a weak reference will be turned into +// a strong ref which will bring its refcount up to 2 and change the wrapper back to +// the rooting state, or it will stay alive until the JS object dies. If the JS object +// dies, then when XPCJSRuntime::FinalizeCallback calls FindDyingJSObjects +// it will find the wrapper and call Release() in it, destroying the wrapper. +// Otherwise, the wrapper will stay alive, even if it no longer has a weak reference +// to it. +// +// When the wrapper is subject to finalization, it is kept alive by an implicit reference +// from the JS object which is invisible to the cycle collector, so the cycle collector +// does not traverse any children of wrappers that are subject to finalization. This will +// result in a leak if a wrapper in the non-rooting state has an aggregated native that +// keeps alive the wrapper's JS object. See bug 947049. + NS_IMETHODIMP NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Traverse (void *p, nsCycleCollectionTraversalCallback &cb) @@ -37,14 +71,17 @@ NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Traverse NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsXPCWrappedJS, refcnt) } - // nsXPCWrappedJS keeps its own refcount artificially at or above 1, see the - // comment above nsXPCWrappedJS::AddRef. + // A wrapper that is subject to finalization will only die when its JS object dies. + if (tmp->IsSubjectToFinalization()) + return NS_OK; + + // Don't let the extra reference for nsSupportsWeakReference keep a wrapper that is + // not subject to finalization alive. NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "self"); cb.NoteXPCOMChild(s); - if (refcnt > 1) { - // nsXPCWrappedJS roots its mJSObj when its refcount is > 1, see - // the comment above nsXPCWrappedJS::AddRef. + if (tmp->IsValid()) { + MOZ_ASSERT(refcnt > 1); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mJSObj"); cb.NoteJSChild(tmp->GetJSObjectPreserveColor()); } @@ -54,7 +91,7 @@ NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Traverse cb.NoteXPCOMChild(tmp->GetAggregatedNativeObject()); } else { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "root"); - cb.NoteXPCOMChild(static_cast(tmp->GetRootWrapper())); + cb.NoteXPCOMChild(ToSupports(tmp->GetRootWrapper())); } return NS_OK; @@ -127,37 +164,22 @@ nsXPCWrappedJS::QueryInterface(REFNSIID aIID, void** aInstancePtr) } -// Refcounting is now similar to that used in the chained (pre-flattening) -// wrappednative system. -// -// We are now holding an extra refcount for nsISupportsWeakReference support. -// -// Non-root wrappers remove themselves from the chain in their destructors. -// We root the JSObject as the refcount transitions from 1->2. And we unroot -// the JSObject when the refcount transitions from 2->1. -// -// When the transition from 2->1 is made and no one holds a weak ref to the -// (aggregated) object then we decrement the refcount again to 0 (and -// destruct) . However, if a weak ref is held at the 2->1 transition, then we -// leave the refcount at 1 to indicate that state. This leaves the JSObject -// no longer rooted by us and (as far as we know) subject to possible -// collection. Code in XPCJSRuntime watches for JS gc to happen and will do -// the final release on wrappers whose JSObjects get finalized. Note that -// even after tranistioning to this refcount-of-one state callers might do -// an addref and cause us to re-root the JSObject and continue on more normally. +// For a description of nsXPCWrappedJS lifetime and reference counting, see +// the comment at the top of this file. nsrefcnt nsXPCWrappedJS::AddRef(void) { if (!MOZ_LIKELY(NS_IsMainThread())) MOZ_CRASH(); - nsrefcnt cnt = ++mRefCnt; + + MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); + nsrefcnt cnt = mRefCnt.incr(); NS_LOG_ADDREF(this, cnt, "nsXPCWrappedJS", sizeof(*this)); if (2 == cnt && IsValid()) { GetJSObject(); // Unmark gray JSObject. - XPCJSRuntime* rt = mClass->GetRuntime(); - rt->AddWrappedJSRoot(this); + mClass->GetRuntime()->AddWrappedJSRoot(this); } return cnt; @@ -168,31 +190,44 @@ nsXPCWrappedJS::Release(void) { if (!MOZ_LIKELY(NS_IsMainThread())) MOZ_CRASH(); - NS_PRECONDITION(0 != mRefCnt, "dup release"); + MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); + NS_ASSERT_OWNINGTHREAD(nsXPCWrappedJS); -do_decrement: - - nsrefcnt cnt = --mRefCnt; + bool shouldDelete = false; + nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this); + nsrefcnt cnt = mRefCnt.decr(base, &shouldDelete); NS_LOG_RELEASE(this, cnt, "nsXPCWrappedJS"); if (0 == cnt) { - delete this; // also unlinks us from chain - return 0; - } - if (1 == cnt) { + if (MOZ_UNLIKELY(shouldDelete)) { + mRefCnt.stabilizeForDeletion(); + DeleteCycleCollectable(); + } else { + mRefCnt.incr(); + Destroy(); + mRefCnt.decr(base); + } + } else if (1 == cnt) { if (IsValid()) RemoveFromRootSet(); - // If we are not the root wrapper or if we are not being used from a - // weak reference, then this extra ref is not needed and we can let - // ourself be deleted. - // Note: HasWeakReferences() could only return true for the root. + // If we are not a root wrapper being used from a weak reference, + // then the extra ref is not needed and we can let outselves be + // deleted. if (!HasWeakReferences()) - goto do_decrement; + return Release(); + + MOZ_ASSERT(IsRootWrapper(), "Only root wrappers should have weak references"); } return cnt; } +NS_IMETHODIMP_(void) +nsXPCWrappedJS::DeleteCycleCollectable(void) +{ + delete this; +} + void nsXPCWrappedJS::TraceJS(JSTracer* trc) { @@ -345,9 +380,12 @@ nsXPCWrappedJS::nsXPCWrappedJS(JSContext* cx, { InitStub(GetClass()->GetIID()); - // intentionally do double addref - see Release(). + // There is an extra AddRef to support weak references to wrappers + // that are subject to finalization. See the top of the file for more + // details. NS_ADDREF_THIS(); NS_ADDREF_THIS(); + NS_ADDREF(aClass); NS_IF_ADDREF(mOuter); @@ -358,7 +396,13 @@ nsXPCWrappedJS::nsXPCWrappedJS(JSContext* cx, nsXPCWrappedJS::~nsXPCWrappedJS() { - NS_PRECONDITION(0 == mRefCnt, "refcounting error"); + Destroy(); +} + +void +nsXPCWrappedJS::Destroy() +{ + MOZ_ASSERT(1 == int32_t(mRefCnt), "should be stabilized for deletion"); if (IsRootWrapper()) { XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 99c2265a685b..2d9f539eba02 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -2464,14 +2464,13 @@ class nsXPCWrappedJS : protected nsAutoXPTCStub, public XPCRootSetElem { public: - NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_NSIXPCONNECTJSOBJECTHOLDER NS_DECL_NSIXPCONNECTWRAPPEDJS NS_DECL_NSISUPPORTSWEAKREFERENCE NS_DECL_NSIPROPERTYBAG NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXPCWrappedJS, nsIXPConnectWrappedJS) - void DeleteCycleCollectable() {} NS_IMETHOD CallMethod(uint16_t methodIndex, const XPTMethodDescriptor *info, @@ -2513,12 +2512,9 @@ public: bool IsValid() const {return mJSObj != nullptr;} void SystemIsBeingShutDown(); - // This is used by XPCJSRuntime::GCCallback to find wrappers that no - // longer root their JSObject and are only still alive because they - // were being used via nsSupportsWeakReference at the time when their - // last (outside) reference was released. Wrappers that fit into that - // category are only deleted when we see that their corresponding JSObject - // is to be finalized. + // These two methods are used by JSObject2WrappedJSMap::FindDyingJSObjects + // to find non-rooting wrappers for dying JS objects. See the top of + // XPCWrappedJS.cpp for more details. bool IsSubjectToFinalization() const {return IsValid() && mRefCnt == 1;} bool IsObjectAboutToBeFinalized() {return JS_IsAboutToBeFinalized(&mJSObj);} @@ -2537,7 +2533,8 @@ protected: nsXPCWrappedJS* root, nsISupports* aOuter); - void Unlink(); + void Destroy(); + void Unlink(); private: JS::Heap mJSObj; From a324b39e7f60e8757e93492f604ba8ab1d5449d0 Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Thu, 12 Dec 2013 19:38:50 -0800 Subject: [PATCH 007/144] Bug 944492, part 2 - Make XPCWrappedJS a proper skippable class. r=smaug --- js/xpconnect/src/XPCJSRuntime.cpp | 37 +--------------------- js/xpconnect/src/XPCWrappedJS.cpp | 51 +++++++++++++++++++++++++++++++ js/xpconnect/src/xpcprivate.h | 3 +- 3 files changed, 54 insertions(+), 37 deletions(-) diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 1a12e11d83b3..2f33a01747fa 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -668,35 +668,6 @@ XPCJSRuntime::SuspectWrappedNative(XPCWrappedNative *wrapper, cb.NoteJSRoot(obj); } -bool -CanSkipWrappedJS(nsXPCWrappedJS *wrappedJS) -{ - JSObject *obj = wrappedJS->GetJSObjectPreserveColor(); - // If traversing wrappedJS wouldn't release it, nor - // cause any other objects to be added to the graph, no - // need to add it to the graph at all. - bool isRootWrappedJS = wrappedJS->IsRootWrapper(); - if (nsCCUncollectableMarker::sGeneration && - (!obj || !xpc_IsGrayGCThing(obj)) && - !wrappedJS->IsSubjectToFinalization() && - (isRootWrappedJS || CanSkipWrappedJS(wrappedJS->GetRootWrapper()))) { - if (!wrappedJS->IsAggregatedToNative() || !isRootWrappedJS) { - return true; - } else { - nsISupports* agg = wrappedJS->GetAggregatedNativeObject(); - nsXPCOMCycleCollectionParticipant* cp = nullptr; - CallQueryInterface(agg, &cp); - nsISupports* canonical = nullptr; - agg->QueryInterface(NS_GET_IID(nsCycleCollectionISupports), - reinterpret_cast(&canonical)); - if (cp && canonical && cp->CanSkipInCC(canonical)) { - return true; - } - } - } - return false; -} - void XPCJSRuntime::TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback &cb) { @@ -714,13 +685,7 @@ XPCJSRuntime::TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback &c } for (XPCRootSetElem *e = mWrappedJSRoots; e ; e = e->GetNextRoot()) { - nsXPCWrappedJS *wrappedJS = static_cast(e); - if (!cb.WantAllTraces() && - CanSkipWrappedJS(wrappedJS)) { - continue; - } - - cb.NoteXPCOMRoot(static_cast(wrappedJS)); + cb.NoteXPCOMRoot(ToSupports(static_cast(e))); } } diff --git a/js/xpconnect/src/XPCWrappedJS.cpp b/js/xpconnect/src/XPCWrappedJS.cpp index f0217a990cf2..3480ef0f099b 100644 --- a/js/xpconnect/src/XPCWrappedJS.cpp +++ b/js/xpconnect/src/XPCWrappedJS.cpp @@ -8,6 +8,7 @@ #include "xpcprivate.h" #include "jsprf.h" +#include "nsCCUncollectableMarker.h" #include "nsCxPusher.h" #include "nsContentUtils.h" #include "nsThreadUtils.h" @@ -50,6 +51,42 @@ using namespace mozilla; // result in a leak if a wrapper in the non-rooting state has an aggregated native that // keeps alive the wrapper's JS object. See bug 947049. + +// If traversing wrappedJS wouldn't release it, nor cause any other objects to be +// added to the graph, there is no need to add it to the graph at all. +bool +nsXPCWrappedJS::CanSkip() +{ + if (!nsCCUncollectableMarker::sGeneration) + return false; + + if (IsSubjectToFinalization()) + return true; + + // If this wrapper holds a gray object, need to trace it. + JSObject *obj = GetJSObjectPreserveColor(); + if (obj && xpc_IsGrayGCThing(obj)) + return false; + + // For non-root wrappers, check if the root wrapper will be + // added to the CC graph. + if (!IsRootWrapper()) + return mRoot->CanSkip(); + + // For the root wrapper, check if there is an aggregated + // native object that will be added to the CC graph. + if (!IsAggregatedToNative()) + return true; + + nsISupports* agg = GetAggregatedNativeObject(); + nsXPCOMCycleCollectionParticipant* cp = nullptr; + CallQueryInterface(agg, &cp); + nsISupports* canonical = nullptr; + agg->QueryInterface(NS_GET_IID(nsCycleCollectionISupports), + reinterpret_cast(&canonical)); + return cp && canonical && cp->CanSkipThis(canonical); +} + NS_IMETHODIMP NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Traverse (void *p, nsCycleCollectionTraversalCallback &cb) @@ -103,6 +140,20 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXPCWrappedJS) tmp->Unlink(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END +// XPCJSRuntime keeps a table of WJS, so we can remove them from +// the purple buffer in between CCs. +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXPCWrappedJS) + return true; +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsXPCWrappedJS) + return tmp->CanSkip(); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsXPCWrappedJS) + return tmp->CanSkip(); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END + NS_IMETHODIMP nsXPCWrappedJS::AggregatedQueryInterface(REFNSIID aIID, void** aInstancePtr) { diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 2d9f539eba02..249b21911746 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -2470,7 +2470,7 @@ public: NS_DECL_NSISUPPORTSWEAKREFERENCE NS_DECL_NSIPROPERTYBAG - NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXPCWrappedJS, nsIXPConnectWrappedJS) + NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS_AMBIGUOUS(nsXPCWrappedJS, nsIXPConnectWrappedJS) NS_IMETHOD CallMethod(uint16_t methodIndex, const XPTMethodDescriptor *info, @@ -2533,6 +2533,7 @@ protected: nsXPCWrappedJS* root, nsISupports* aOuter); + bool CanSkip(); void Destroy(); void Unlink(); From f853c97cb8422c050ea8c3f5394552f35f7c3587 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Thu, 12 Dec 2013 20:29:14 -0800 Subject: [PATCH 008/144] Ensure that the software compositor never uses deprecated textures (bug 947038, r=mattwoodrow). --- dom/ipc/TabChild.cpp | 5 + gfx/layers/basic/BasicCompositor.cpp | 155 ------------------ gfx/layers/client/ContentClient.cpp | 5 + gfx/layers/composite/TextureHost.cpp | 8 - gfx/layers/composite/ThebesLayerComposite.cpp | 1 + gfx/thebes/gfxPlatform.cpp | 20 +++ gfx/thebes/gfxPlatform.h | 5 +- widget/xpwidgets/nsBaseWidget.cpp | 2 + 8 files changed, 37 insertions(+), 164 deletions(-) diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index 8306641297ff..7e88a30e1b88 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -72,6 +72,7 @@ #include "APZCCallbackHelper.h" #include "nsILoadContext.h" #include "ipc/nsGUIEventIPC.h" +#include "gfxPlatform.h" #ifdef DEBUG #include "PCOMContentPermissionRequestChild.h" @@ -2281,6 +2282,10 @@ TabChild::InitRenderingState() NS_WARNING("failed to properly allocate layer transaction"); return false; } + + // Track which compositor backend is in use (see bug 947038). This can + // be removed when deprecated textures are removed. + gfxPlatform::GetPlatform()->SetCompositorBackend(mTextureFactoryIdentifier.mParentBackend); } else { // Pushing transactions to the parent content. shadowManager = remoteFrame->SendPLayerTransactionConstructor(); diff --git a/gfx/layers/basic/BasicCompositor.cpp b/gfx/layers/basic/BasicCompositor.cpp index cc934831b3b8..0bc966af4163 100644 --- a/gfx/layers/basic/BasicCompositor.cpp +++ b/gfx/layers/basic/BasicCompositor.cpp @@ -63,161 +63,6 @@ public: RefPtr mSurface; }; -/** - * Texture source and host implementaion for software compositing. - */ -class DeprecatedTextureHostBasic : public DeprecatedTextureHost - , public TextureSourceBasic -{ -public: - DeprecatedTextureHostBasic() - : mCompositor(nullptr) - {} - - SurfaceFormat GetFormat() const MOZ_OVERRIDE { return mFormat; } - - virtual IntSize GetSize() const MOZ_OVERRIDE { return mSize; } - - virtual TextureSourceBasic* AsSourceBasic() MOZ_OVERRIDE { return this; } - - SourceSurface *GetSurface() MOZ_OVERRIDE { return mSurface; } - - virtual void SetCompositor(Compositor* aCompositor) - { - mCompositor = static_cast(aCompositor); - } - - virtual const char *Name() { return "DeprecatedTextureHostBasic"; } - -protected: - virtual void UpdateImpl(const SurfaceDescriptor& aImage, - nsIntRegion *aRegion, - nsIntPoint*) MOZ_OVERRIDE - { - AutoOpenSurface surf(OPEN_READ_ONLY, aImage); - nsRefPtr surface = ShadowLayerForwarder::OpenDescriptor(OPEN_READ_ONLY, aImage); - nsRefPtr image = surface->GetAsImageSurface(); - mFormat = ImageFormatToSurfaceFormat(image->Format()); - mSize = IntSize(image->Width(), image->Height()); - mSurface = Factory::CreateWrappingDataSourceSurface(image->Data(), - image->Stride(), - mSize, - mFormat); - } - - virtual bool EnsureSurface() { - return true; - } - - virtual bool Lock() MOZ_OVERRIDE { - return EnsureSurface(); - } - - virtual TemporaryRef GetAsSurface() MOZ_OVERRIDE { - if (!mSurface) { - return nullptr; - } - return mSurface->GetDataSurface(); - } - - BasicCompositor *mCompositor; - RefPtr mSurface; - IntSize mSize; - SurfaceFormat mFormat; -}; - -void -DeserializerToPlanarYCbCrImageData(YCbCrImageDataDeserializer& aDeserializer, PlanarYCbCrData& aData) -{ - aData.mYChannel = aDeserializer.GetYData(); - aData.mYStride = aDeserializer.GetYStride(); - aData.mYSize = aDeserializer.GetYSize(); - aData.mCbChannel = aDeserializer.GetCbData(); - aData.mCrChannel = aDeserializer.GetCrData(); - aData.mCbCrStride = aDeserializer.GetCbCrStride(); - aData.mCbCrSize = aDeserializer.GetCbCrSize(); - aData.mPicSize = aDeserializer.GetYSize(); -} - -class YCbCrDeprecatedTextureHostBasic : public DeprecatedTextureHostBasic -{ -public: - virtual void UpdateImpl(const SurfaceDescriptor& aImage, - nsIntRegion *aRegion, - nsIntPoint*) MOZ_OVERRIDE - { - MOZ_ASSERT(aImage.type() == SurfaceDescriptor::TYCbCrImage); - mSurface = nullptr; - ConvertImageToRGB(aImage); - } - - virtual void SwapTexturesImpl(const SurfaceDescriptor& aImage, - nsIntRegion* aRegion) MOZ_OVERRIDE - { - MOZ_ASSERT(aImage.type() == SurfaceDescriptor::TYCbCrImage); - mSurface = nullptr; - } - - virtual bool EnsureSurface() MOZ_OVERRIDE - { - if (mSurface) { - return true; - } - if (!mBuffer) { - return false; - } - return ConvertImageToRGB(*mBuffer); - } - - bool ConvertImageToRGB(const SurfaceDescriptor& aImage) - { - YCbCrImageDataDeserializer deserializer(aImage.get_YCbCrImage().data().get()); - PlanarYCbCrData data; - DeserializerToPlanarYCbCrImageData(deserializer, data); - - gfxImageFormat format = gfxImageFormatRGB24; - gfxIntSize size; - gfxUtils::GetYCbCrToRGBDestFormatAndSize(data, format, size); - if (size.width > PlanarYCbCrImage::MAX_DIMENSION || - size.height > PlanarYCbCrImage::MAX_DIMENSION) { - NS_ERROR("Illegal image dest width or height"); - return false; - } - - mSize = ToIntSize(size); - mFormat = (format == gfxImageFormatRGB24) - ? FORMAT_B8G8R8X8 - : FORMAT_B8G8R8A8; - - RefPtr surface = Factory::CreateDataSourceSurface(mSize, mFormat); - gfxUtils::ConvertYCbCrToRGB(data, format, size, - surface->GetData(), - surface->Stride()); - - mSurface = surface; - return true; - } -}; - -TemporaryRef -CreateBasicDeprecatedTextureHost(SurfaceDescriptorType aDescriptorType, - uint32_t aTextureHostFlags, - uint32_t aTextureFlags) -{ - RefPtr result = nullptr; - if (aDescriptorType == SurfaceDescriptor::TYCbCrImage) { - result = new YCbCrDeprecatedTextureHostBasic(); - } else { - MOZ_ASSERT(aDescriptorType == SurfaceDescriptor::TShmem || - aDescriptorType == SurfaceDescriptor::TMemoryImage, - "We can only support Shmem currently"); - result = new DeprecatedTextureHostBasic(); - } - - result->SetFlags(aTextureFlags); - return result.forget(); -} - BasicCompositor::BasicCompositor(nsIWidget *aWidget) : mWidget(aWidget) { diff --git a/gfx/layers/client/ContentClient.cpp b/gfx/layers/client/ContentClient.cpp index 544784812d91..157c01970fcf 100644 --- a/gfx/layers/client/ContentClient.cpp +++ b/gfx/layers/client/ContentClient.cpp @@ -58,6 +58,11 @@ ContentClient::CreateContentClient(CompositableForwarder* aForwarder) useDeprecatedTextures = gfxPlatform::GetPlatform()->UseDeprecatedTextures(); #endif + // Always use new textures for the basic compositor. + if (backend == LAYERS_BASIC) { + useDeprecatedTextures = false; + } + #ifdef XP_WIN if (backend == LAYERS_D3D11) { useDoubleBuffering = !!gfxWindowsPlatform::GetPlatform()->GetD2DDevice(); diff --git a/gfx/layers/composite/TextureHost.cpp b/gfx/layers/composite/TextureHost.cpp index 5e4da901d9ac..4caeb0b8030a 100644 --- a/gfx/layers/composite/TextureHost.cpp +++ b/gfx/layers/composite/TextureHost.cpp @@ -84,10 +84,6 @@ TextureHost::AsTextureHost(PTextureParent* actor) TemporaryRef CreateDeprecatedTextureHostOGL(SurfaceDescriptorType aDescriptorType, uint32_t aDeprecatedTextureHostFlags, uint32_t aTextureFlags); -// implemented in BasicCompositor.cpp -TemporaryRef CreateBasicDeprecatedTextureHost(SurfaceDescriptorType aDescriptorType, - uint32_t aDeprecatedTextureHostFlags, - uint32_t aTextureFlags); #ifdef XP_WIN TemporaryRef CreateDeprecatedTextureHostD3D9(SurfaceDescriptorType aDescriptorType, @@ -127,10 +123,6 @@ DeprecatedTextureHost::CreateDeprecatedTextureHost(SurfaceDescriptorType aDescri aDeprecatedTextureHostFlags, aTextureFlags); #endif - case LAYERS_BASIC: - return CreateBasicDeprecatedTextureHost(aDescriptorType, - aDeprecatedTextureHostFlags, - aTextureFlags); default: MOZ_CRASH("Couldn't create texture host"); } diff --git a/gfx/layers/composite/ThebesLayerComposite.cpp b/gfx/layers/composite/ThebesLayerComposite.cpp index 1f35533634d0..7a9cc3f00c45 100644 --- a/gfx/layers/composite/ThebesLayerComposite.cpp +++ b/gfx/layers/composite/ThebesLayerComposite.cpp @@ -138,6 +138,7 @@ ThebesLayerComposite::RenderLayer(const nsIntRect& aClipRect) mBuffer->SetPaintWillResample(MayResample()); + mBuffer->SetCompositor(mCompositeManager->GetCompositor()); mBuffer->Composite(effectChain, GetEffectiveOpacity(), transform, diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index b563f75429e4..89c74cc2c52e 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -281,6 +281,7 @@ gfxPlatform::gfxPlatform() , mDrawLayerBorders(false) , mDrawTileBorders(false) , mDrawBigImageBorders(false) + , mCompositorBackend(LAYERS_NONE) { mUseHarfBuzzScripts = UNINITIALIZED_VALUE; mAllowDownloadableFonts = UNINITIALIZED_VALUE; @@ -2213,3 +2214,22 @@ gfxPlatform::ComponentAlphaEnabled() InitLayersAccelerationPrefs(); return sComponentAlphaEnabled; } + +void +gfxPlatform::SetCompositorBackend(mozilla::layers::LayersBackend backend) +{ + // The compositor backend should always be the same after one has been chosen. + MOZ_ASSERT(mCompositorBackend == LAYERS_NONE || mCompositorBackend == backend); + if (mCompositorBackend == LAYERS_NONE) { + mCompositorBackend = backend; + } +} + +bool +gfxPlatform::UseDeprecatedTextures() const +{ + if (mCompositorBackend == LAYERS_BASIC) { + return false; + } + return mLayersUseDeprecated; +} diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h index 27ff830fd51e..64d138698558 100644 --- a/gfx/thebes/gfxPlatform.h +++ b/gfx/thebes/gfxPlatform.h @@ -20,6 +20,7 @@ #include "mozilla/RefPtr.h" #include "GfxInfoCollector.h" +#include "mozilla/layers/LayersTypes.h" #include "mozilla/layers/CompositorTypes.h" #ifdef XP_OS2 @@ -619,7 +620,8 @@ public: * This method should not be called from the compositor thread. */ bool PreferMemoryOverShmem() const; - bool UseDeprecatedTextures() const { return mLayersUseDeprecated; } + bool UseDeprecatedTextures() const; + void SetCompositorBackend(mozilla::layers::LayersBackend backend); protected: gfxPlatform(); @@ -736,6 +738,7 @@ private: bool mDrawLayerBorders; bool mDrawTileBorders; bool mDrawBigImageBorders; + mozilla::layers::LayersBackend mCompositorBackend; }; #endif /* GFX_PLATFORM_H */ diff --git a/widget/xpwidgets/nsBaseWidget.cpp b/widget/xpwidgets/nsBaseWidget.cpp index 1916ced3277d..f8f4ce68d058 100644 --- a/widget/xpwidgets/nsBaseWidget.cpp +++ b/widget/xpwidgets/nsBaseWidget.cpp @@ -988,6 +988,8 @@ void nsBaseWidget::CreateCompositor(int aWidth, int aHeight) ImageBridgeChild::IdentifyCompositorTextureHost(textureFactoryIdentifier); WindowUsesOMTC(); + gfxPlatform::GetPlatform()->SetCompositorBackend(textureFactoryIdentifier.mParentBackend); + mLayerManager = lm; return; } From db3e8ca04248c1ddfbc4b8e63cc130b44fb7b67b Mon Sep 17 00:00:00 2001 From: Brian Birtles Date: Fri, 13 Dec 2013 13:41:47 +0900 Subject: [PATCH 009/144] Bug 948245 part 1 - Rework and test repeat duration calculation; r=dholbert In order to implement the min attribute properly we need to distinguish between cases where the repeat duration is less than the active duration so we can apply the fill mode in the 'gap'. Currently the repeat duration calculation is mostly contained in GetRepeatDuration (but is hard to make sense of) and partly contained in the call site CalcActiveEnd. Furthermore, it does not have thorough tests (there were some unit tests but they were never converted to mochitests). This patch reworks the repeat duration calculation so it is contained in one place, easier to read, and thoroughly tested. --- content/smil/nsSMILRepeatCount.h | 6 +- content/smil/nsSMILTimedElement.cpp | 42 +++--- content/smil/test/mochitest.ini | 1 + .../smil/test/test_smilRepeatDuration.html | 139 ++++++++++++++++++ 4 files changed, 162 insertions(+), 26 deletions(-) create mode 100644 content/smil/test/test_smilRepeatDuration.html diff --git a/content/smil/nsSMILRepeatCount.h b/content/smil/nsSMILRepeatCount.h index 18c6d66db618..8561fe4d8bd4 100644 --- a/content/smil/nsSMILRepeatCount.h +++ b/content/smil/nsSMILRepeatCount.h @@ -27,7 +27,11 @@ public: explicit nsSMILRepeatCount(double aCount) : mCount(kNotSet) { SetCount(aCount); } - operator double() const { return mCount; } + operator double() const { + MOZ_ASSERT(IsDefinite(), + "Converting indefinite or unset repeat count to double"); + return mCount; + } bool IsDefinite() const { return mCount != kNotSet && mCount != kIndefinite; } diff --git a/content/smil/nsSMILTimedElement.cpp b/content/smil/nsSMILTimedElement.cpp index 3538ca5d78b2..4ffde26796ed 100644 --- a/content/smil/nsSMILTimedElement.cpp +++ b/content/smil/nsSMILTimedElement.cpp @@ -1803,11 +1803,7 @@ nsSMILTimedElement::CalcActiveEnd(const nsSMILTimeValue& aBegin, NS_ABORT_IF_FALSE(aBegin.IsDefinite(), "Indefinite or unresolved begin time in CalcActiveEnd"); - if (mRepeatDur.IsIndefinite()) { - result.SetIndefinite(); - } else { - result = GetRepeatDuration(); - } + result = GetRepeatDuration(); if (aEnd.IsDefinite()) { nsSMILTime activeDur = aEnd.GetMillis() - aBegin.GetMillis(); @@ -1832,29 +1828,25 @@ nsSMILTimedElement::CalcActiveEnd(const nsSMILTimeValue& aBegin, nsSMILTimeValue nsSMILTimedElement::GetRepeatDuration() const { - nsSMILTimeValue result; - - if (mRepeatCount.IsDefinite() && mRepeatDur.IsDefinite()) { - if (mSimpleDur.IsDefinite()) { - nsSMILTime activeDur = - nsSMILTime(mRepeatCount * double(mSimpleDur.GetMillis())); - result.SetMillis(std::min(activeDur, mRepeatDur.GetMillis())); - } else { - result = mRepeatDur; - } - } else if (mRepeatCount.IsDefinite() && mSimpleDur.IsDefinite()) { - nsSMILTime activeDur = - nsSMILTime(mRepeatCount * double(mSimpleDur.GetMillis())); - result.SetMillis(activeDur); - } else if (mRepeatDur.IsDefinite()) { - result = mRepeatDur; - } else if (mRepeatCount.IsIndefinite()) { - result.SetIndefinite(); + nsSMILTimeValue multipliedDuration; + if (mRepeatCount.IsDefinite() && mSimpleDur.IsDefinite()) { + multipliedDuration.SetMillis( + nsSMILTime(mRepeatCount * double(mSimpleDur.GetMillis()))); } else { - result = mSimpleDur; + multipliedDuration.SetIndefinite(); } - return result; + nsSMILTimeValue repeatDuration; + + if (mRepeatDur.IsResolved()) { + repeatDuration = std::min(multipliedDuration, mRepeatDur); + } else if (mRepeatCount.IsSet()) { + repeatDuration = multipliedDuration; + } else { + repeatDuration = mSimpleDur; + } + + return repeatDuration; } nsSMILTimeValue diff --git a/content/smil/test/mochitest.ini b/content/smil/test/mochitest.ini index 75d43a4371d7..f208c0e7a81f 100644 --- a/content/smil/test/mochitest.ini +++ b/content/smil/test/mochitest.ini @@ -38,6 +38,7 @@ support-files = [test_smilMappedAttrFromBy.xhtml] [test_smilMappedAttrFromTo.xhtml] [test_smilMappedAttrPaced.xhtml] +[test_smilRepeatDuration.html] [test_smilRepeatTiming.xhtml] [test_smilReset.xhtml] [test_smilRestart.xhtml] diff --git a/content/smil/test/test_smilRepeatDuration.html b/content/smil/test/test_smilRepeatDuration.html new file mode 100644 index 000000000000..99a95bb7b88e --- /dev/null +++ b/content/smil/test/test_smilRepeatDuration.html @@ -0,0 +1,139 @@ + + + + + + Test for repeat duration calculation (Bug 948245) + + + + +Mozilla Bug 948245 +

+ +
+
+
+ + From 11ce2f85048d55de49c7d7e49ff48f84ec0daece Mon Sep 17 00:00:00 2001 From: Brian Birtles Date: Fri, 13 Dec 2013 13:41:52 +0900 Subject: [PATCH 010/144] Bug 948245 part 2 - Allow the min attribute to extend the active duration; r=dholbert The min attribute on an animation element can extend the active duration making it longer than the "repeat duration" (the amount of the time the animation runs including all repeats). For the remaining time after the repeat duration has completed until the end of the active duration the animation should apply its fill behavior. Previously this was not implemented and min could not extend the active duration. Allowing this effectively introduces an additional kind of state where we are both within the active interval but not animating. In this case we set the animation function (referred to as the "client" for historical reasons) to inactive so that effectively the timing model is active but the animation model is inactive. (In the future we will come up with something a little easier to understand when we rework this in terms of Web Animations components.) --- content/smil/nsSMILAnimationFunction.h | 9 ++ content/smil/nsSMILTimedElement.cpp | 103 +++++++++++++++------- content/smil/nsSMILTimedElement.h | 1 + content/smil/test/mochitest.ini | 1 + content/smil/test/test_smilMinTiming.html | 93 +++++++++++++++++++ layout/reftests/svg/smil/min-1.svg | 24 +++++ layout/reftests/svg/smil/reftest.list | 2 + 7 files changed, 202 insertions(+), 31 deletions(-) create mode 100644 content/smil/test/test_smilMinTiming.html create mode 100644 layout/reftests/svg/smil/min-1.svg diff --git a/content/smil/nsSMILAnimationFunction.h b/content/smil/nsSMILAnimationFunction.h index 3f233758ac0c..27f4e6f20cb3 100644 --- a/content/smil/nsSMILAnimationFunction.h +++ b/content/smil/nsSMILAnimationFunction.h @@ -164,6 +164,15 @@ public: return (mIsActive || mIsFrozen); } + /** + * Indicates if the animation is active. + * + * @return true if the animation is active, false otherwise. + */ + bool IsActive() const { + return mIsActive; + } + /** * Indicates if this animation will replace the passed in result rather than * adding to it. Animations that replace the underlying value may be called diff --git a/content/smil/nsSMILTimedElement.cpp b/content/smil/nsSMILTimedElement.cpp index 4ffde26796ed..0da19cdbf412 100644 --- a/content/smil/nsSMILTimedElement.cpp +++ b/content/smil/nsSMILTimedElement.cpp @@ -667,19 +667,32 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, bool aEndOnly) NS_ASSERTION(aContainerTime >= beginTime, "Sample time should not precede current interval"); nsSMILTime activeTime = aContainerTime - beginTime; - SampleSimpleTime(activeTime); - // We register our repeat times as milestones (except when we're - // seeking) so we should get a sample at exactly the time we repeat. - // (And even when we are seeking we want to update - // mCurrentRepeatIteration so we do that first before testing the seek - // state.) - uint32_t prevRepeatIteration = mCurrentRepeatIteration; - if (ActiveTimeToSimpleTime(activeTime, mCurrentRepeatIteration)==0 && + + // The 'min' attribute can cause the active interval to be longer than + // the 'repeating interval'. + // In that extended period we apply the fill mode. + if (GetRepeatDuration() <= nsSMILTimeValue(activeTime)) { + if (mClient && mClient->IsActive()) { + mClient->Inactivate(mFillMode == FILL_FREEZE); + } + SampleFillValue(); + } else { + SampleSimpleTime(activeTime); + + // We register our repeat times as milestones (except when we're + // seeking) so we should get a sample at exactly the time we repeat. + // (And even when we are seeking we want to update + // mCurrentRepeatIteration so we do that first before testing the + // seek state.) + uint32_t prevRepeatIteration = mCurrentRepeatIteration; + if ( + ActiveTimeToSimpleTime(activeTime, mCurrentRepeatIteration)==0 && mCurrentRepeatIteration != prevRepeatIteration && mCurrentRepeatIteration && mSeekState == SEEK_NOT_SEEKING) { - FireTimeEventAsync(NS_SMIL_REPEAT, - static_cast(mCurrentRepeatIteration)); + FireTimeEventAsync(NS_SMIL_REPEAT, + static_cast(mCurrentRepeatIteration)); + } } } } @@ -1084,12 +1097,8 @@ nsSMILTimedElement::SetFillMode(const nsAString& aFillModeSpec) ? nsSMILFillMode(temp.GetEnumValue()) : FILL_REMOVE; - // Check if we're in a fill-able state: i.e. we've played at least one - // interval and are now between intervals or at the end of all intervals - bool isFillable = HasPlayed() && - (mElementState == STATE_WAITING || mElementState == STATE_POSTACTIVE); - - if (mClient && mFillMode != previousFillMode && isFillable) { + // Update fill mode of client + if (mFillMode != previousFillMode && HasClientInFillRange()) { mClient->Inactivate(mFillMode == FILL_FREEZE); SampleFillValue(); } @@ -1102,9 +1111,9 @@ nsSMILTimedElement::UnsetFillMode() { uint16_t previousFillMode = mFillMode; mFillMode = FILL_REMOVE; - if ((mElementState == STATE_WAITING || mElementState == STATE_POSTACTIVE) && - previousFillMode == FILL_FREEZE && mClient && HasPlayed()) + if (previousFillMode == FILL_FREEZE && HasClientInFillRange()) { mClient->Inactivate(false); + } } void @@ -1865,8 +1874,7 @@ nsSMILTimedElement::ApplyMinAndMax(const nsSMILTimeValue& aDuration) const if (aDuration > mMax) { result = mMax; } else if (aDuration < mMin) { - nsSMILTimeValue repeatDur = GetRepeatDuration(); - result = mMin > repeatDur ? repeatDur : mMin; + result = mMin; } else { result = aDuration; } @@ -2060,16 +2068,35 @@ nsSMILTimedElement::SampleFillValue() if (mFillMode != FILL_FREEZE || !mClient) return; - const nsSMILInterval* prevInterval = GetPreviousInterval(); - NS_ABORT_IF_FALSE(prevInterval, - "Attempting to sample fill value but there is no previous interval"); - NS_ABORT_IF_FALSE(prevInterval->End()->Time().IsDefinite() && - prevInterval->End()->IsFixedTime(), - "Attempting to sample fill value but the endpoint of the previous " - "interval is not resolved and fixed"); + nsSMILTime activeTime; - nsSMILTime activeTime = prevInterval->End()->Time().GetMillis() - - prevInterval->Begin()->Time().GetMillis(); + if (mElementState == STATE_WAITING || mElementState == STATE_POSTACTIVE) { + const nsSMILInterval* prevInterval = GetPreviousInterval(); + NS_ABORT_IF_FALSE(prevInterval, + "Attempting to sample fill value but there is no previous interval"); + NS_ABORT_IF_FALSE(prevInterval->End()->Time().IsDefinite() && + prevInterval->End()->IsFixedTime(), + "Attempting to sample fill value but the endpoint of the previous " + "interval is not resolved and fixed"); + + activeTime = prevInterval->End()->Time().GetMillis() - + prevInterval->Begin()->Time().GetMillis(); + + // If the interval's repeat duration was shorter than its active duration, + // use the end of the repeat duration to determine the frozen animation's + // state. + nsSMILTimeValue repeatDuration = GetRepeatDuration(); + if (repeatDuration.IsDefinite()) { + activeTime = std::min(repeatDuration.GetMillis(), activeTime); + } + } else if (mElementState == STATE_ACTIVE) { + // If we are being asked to sample the fill value while active we *must* + // have a repeat duration shorter than the active duration so use that. + MOZ_ASSERT(GetRepeatDuration().IsDefinite(), + "Attempting to sample fill value of an active animation with " + "an indefinite repeat duration"); + activeTime = GetRepeatDuration().GetMillis(); + } uint32_t repeatIteration; nsSMILTime simpleTime = @@ -2165,8 +2192,13 @@ nsSMILTimedElement::GetNextMilestone(nsSMILMilestone& aNextMilestone) const // Work out what comes next: the interval end or the next repeat iteration nsSMILTimeValue nextRepeat; if (mSeekState == SEEK_NOT_SEEKING && mSimpleDur.IsDefinite()) { - nextRepeat.SetMillis(mCurrentInterval->Begin()->Time().GetMillis() + - (mCurrentRepeatIteration + 1) * mSimpleDur.GetMillis()); + nsSMILTime nextRepeatActiveTime = + (mCurrentRepeatIteration + 1) * mSimpleDur.GetMillis(); + // Check that the repeat fits within the repeat duration + if (nsSMILTimeValue(nextRepeatActiveTime) < GetRepeatDuration()) { + nextRepeat.SetMillis(mCurrentInterval->Begin()->Time().GetMillis() + + nextRepeatActiveTime); + } } nsSMILTimeValue nextMilestone = std::min(mCurrentInterval->End()->Time(), nextRepeat); @@ -2275,6 +2307,15 @@ nsSMILTimedElement::GetPreviousInterval() const : mOldIntervals[mOldIntervals.Length()-1].get(); } +bool +nsSMILTimedElement::HasClientInFillRange() const +{ + // Returns true if we have a client that is in the range where it will fill + return mClient && + ((mElementState != STATE_ACTIVE && HasPlayed()) || + (mElementState == STATE_ACTIVE && !mClient->IsActive())); +} + bool nsSMILTimedElement::EndHasEventConditions() const { diff --git a/content/smil/nsSMILTimedElement.h b/content/smil/nsSMILTimedElement.h index c97ff7bd0b62..2b905499f96a 100644 --- a/content/smil/nsSMILTimedElement.h +++ b/content/smil/nsSMILTimedElement.h @@ -512,6 +512,7 @@ protected: const nsSMILInstanceTime* GetEffectiveBeginInstance() const; const nsSMILInterval* GetPreviousInterval() const; bool HasPlayed() const { return !mOldIntervals.IsEmpty(); } + bool HasClientInFillRange() const; bool EndHasEventConditions() const; bool AreEndTimesDependentOn( const nsSMILInstanceTime* aBase) const; diff --git a/content/smil/test/mochitest.ini b/content/smil/test/mochitest.ini index f208c0e7a81f..02b724f0a50e 100644 --- a/content/smil/test/mochitest.ini +++ b/content/smil/test/mochitest.ini @@ -38,6 +38,7 @@ support-files = [test_smilMappedAttrFromBy.xhtml] [test_smilMappedAttrFromTo.xhtml] [test_smilMappedAttrPaced.xhtml] +[test_smilMinTiming.html] [test_smilRepeatDuration.html] [test_smilRepeatTiming.xhtml] [test_smilReset.xhtml] diff --git a/content/smil/test/test_smilMinTiming.html b/content/smil/test/test_smilMinTiming.html new file mode 100644 index 000000000000..e336d0f9aca6 --- /dev/null +++ b/content/smil/test/test_smilMinTiming.html @@ -0,0 +1,93 @@ + + + + + + Test for Bug 948245 + + + + +Mozilla Bug 948245 +

+ +
+
+
+ + diff --git a/layout/reftests/svg/smil/min-1.svg b/layout/reftests/svg/smil/min-1.svg new file mode 100644 index 000000000000..6ba576d45213 --- /dev/null +++ b/layout/reftests/svg/smil/min-1.svg @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/layout/reftests/svg/smil/reftest.list b/layout/reftests/svg/smil/reftest.list index e58d9273220a..9f1ba4e80c28 100644 --- a/layout/reftests/svg/smil/reftest.list +++ b/layout/reftests/svg/smil/reftest.list @@ -251,6 +251,8 @@ fuzzy-if(cocoaWidget&&layersGPUAccelerated,1,2) == anim-gradient-attr-presence-0 # interaction between xml mapped attributes and their css equivalents == mapped-attr-vs-css-prop-1.svg lime.svg +== min-1.svg lime.svg + == smil-transitions-interaction-1a.svg lime.svg == smil-transitions-interaction-1b.svg lime.svg == smil-transitions-interaction-2a.svg lime.svg From 0de483c99ecbe1cbbd1f72972227daf430142050 Mon Sep 17 00:00:00 2001 From: Brian Birtles Date: Fri, 13 Dec 2013 13:41:56 +0900 Subject: [PATCH 011/144] Bug 941315 - Update the timing model even when invalid values are set; r=longsonr When an invalid dur, min, max, or repeatDur is set we were failing to call UpdateCurrentInterval to update the timing model. This patch makes sure we update the current interval in error cases too. Mochitests test the interval is being updated in these cases and, for completeness, the case of repeatCount as well. --- content/smil/nsSMILTimedElement.cpp | 54 +++++++-- content/smil/nsSMILTimedElement.h | 3 + content/smil/test/mochitest.ini | 1 + content/smil/test/test_smilInvalidValues.html | 113 ++++++++++++++++++ 4 files changed, 160 insertions(+), 11 deletions(-) create mode 100644 content/smil/test/test_smilInvalidValues.html diff --git a/content/smil/nsSMILTimedElement.cpp b/content/smil/nsSMILTimedElement.cpp index 0da19cdbf412..db6f05975a9e 100644 --- a/content/smil/nsSMILTimedElement.cpp +++ b/content/smil/nsSMILTimedElement.cpp @@ -112,8 +112,9 @@ namespace //---------------------------------------------------------------------- // Helper class: AutoIntervalUpdateBatcher -// RAII helper to set the mDeferIntervalUpdates flag on an nsSMILTimedElement -// and perform the UpdateCurrentInterval when the object is destroyed. +// Stack-based helper class to set the mDeferIntervalUpdates flag on an +// nsSMILTimedElement and perform the UpdateCurrentInterval when the object is +// destroyed. // // If several of these objects are allocated on the stack, the update will not // be performed until the last object for a given nsSMILTimedElement is @@ -146,6 +147,31 @@ private: bool mDidSetFlag; }; +//---------------------------------------------------------------------- +// Helper class: AutoIntervalUpdater + +// Stack-based helper class to call UpdateCurrentInterval when it is destroyed +// which helps avoid bugs where we forget to call UpdateCurrentInterval in the +// case of early returns (e.g. due to parse errors). +// +// This can be safely used in conjunction with AutoIntervalUpdateBatcher; any +// calls to UpdateCurrentInterval made by this class will simply be deferred if +// there is an AutoIntervalUpdateBatcher on the stack. +class MOZ_STACK_CLASS nsSMILTimedElement::AutoIntervalUpdater +{ +public: + AutoIntervalUpdater(nsSMILTimedElement& aTimedElement) + : mTimedElement(aTimedElement) { } + + ~AutoIntervalUpdater() + { + mTimedElement.UpdateCurrentInterval(); + } + +private: + nsSMILTimedElement& mTimedElement; +}; + //---------------------------------------------------------------------- // Templated helper functions @@ -918,8 +944,10 @@ nsSMILTimedElement::UnsetEndSpec(RemovalTestFunction aRemove) nsresult nsSMILTimedElement::SetSimpleDuration(const nsAString& aDurSpec) { - nsSMILTimeValue duration; + // Update the current interval before returning + AutoIntervalUpdater updater(*this); + nsSMILTimeValue duration; const nsAString& dur = nsSMILParserUtils::TrimWhitespace(aDurSpec); // SVG-specific: "For SVG's animation elements, if "media" is specified, the @@ -939,7 +967,6 @@ nsSMILTimedElement::SetSimpleDuration(const nsAString& aDurSpec) "Setting unresolved simple duration"); mSimpleDur = duration; - UpdateCurrentInterval(); return NS_OK; } @@ -954,8 +981,10 @@ nsSMILTimedElement::UnsetSimpleDuration() nsresult nsSMILTimedElement::SetMin(const nsAString& aMinSpec) { - nsSMILTimeValue duration; + // Update the current interval before returning + AutoIntervalUpdater updater(*this); + nsSMILTimeValue duration; const nsAString& min = nsSMILParserUtils::TrimWhitespace(aMinSpec); if (min.EqualsLiteral("media")) { @@ -970,7 +999,6 @@ nsSMILTimedElement::SetMin(const nsAString& aMinSpec) NS_ABORT_IF_FALSE(duration.GetMillis() >= 0L, "Invalid duration"); mMin = duration; - UpdateCurrentInterval(); return NS_OK; } @@ -985,8 +1013,10 @@ nsSMILTimedElement::UnsetMin() nsresult nsSMILTimedElement::SetMax(const nsAString& aMaxSpec) { - nsSMILTimeValue duration; + // Update the current interval before returning + AutoIntervalUpdater updater(*this); + nsSMILTimeValue duration; const nsAString& max = nsSMILParserUtils::TrimWhitespace(aMaxSpec); if (max.EqualsLiteral("media") || max.EqualsLiteral("indefinite")) { @@ -1001,7 +1031,6 @@ nsSMILTimedElement::SetMax(const nsAString& aMaxSpec) } mMax = duration; - UpdateCurrentInterval(); return NS_OK; } @@ -1036,15 +1065,16 @@ nsSMILTimedElement::UnsetRestart() nsresult nsSMILTimedElement::SetRepeatCount(const nsAString& aRepeatCountSpec) { + // Update the current interval before returning + AutoIntervalUpdater updater(*this); + nsSMILRepeatCount newRepeatCount; if (nsSMILParserUtils::ParseRepeatCount(aRepeatCountSpec, newRepeatCount)) { mRepeatCount = newRepeatCount; - UpdateCurrentInterval(); return NS_OK; } mRepeatCount.Unset(); - UpdateCurrentInterval(); return NS_ERROR_FAILURE; } @@ -1058,6 +1088,9 @@ nsSMILTimedElement::UnsetRepeatCount() nsresult nsSMILTimedElement::SetRepeatDur(const nsAString& aRepeatDurSpec) { + // Update the current interval before returning + AutoIntervalUpdater updater(*this); + nsSMILTimeValue duration; const nsAString& repeatDur = @@ -1073,7 +1106,6 @@ nsSMILTimedElement::SetRepeatDur(const nsAString& aRepeatDurSpec) } mRepeatDur = duration; - UpdateCurrentInterval(); return NS_OK; } diff --git a/content/smil/nsSMILTimedElement.h b/content/smil/nsSMILTimedElement.h index 2b905499f96a..a8086acb8c52 100644 --- a/content/smil/nsSMILTimedElement.h +++ b/content/smil/nsSMILTimedElement.h @@ -616,6 +616,9 @@ protected: bool mDoDeferredUpdate; // Set if an update to the current interval was // requested while mDeferIntervalUpdates was set + // Stack-based helper class to call UpdateCurrentInterval when it is destroyed + class AutoIntervalUpdater; + // Recursion depth checking uint8_t mDeleteCount; uint8_t mUpdateIntervalRecursionDepth; diff --git a/content/smil/test/mochitest.ini b/content/smil/test/mochitest.ini index 02b724f0a50e..c48b6f4330c7 100644 --- a/content/smil/test/mochitest.ini +++ b/content/smil/test/mochitest.ini @@ -32,6 +32,7 @@ support-files = [test_smilGetSimpleDuration.xhtml] [test_smilGetStartTime.xhtml] [test_smilHyperlinking.xhtml] +[test_smilInvalidValues.html] [test_smilKeySplines.xhtml] [test_smilKeyTimes.xhtml] [test_smilKeyTimesPacedMode.xhtml] diff --git a/content/smil/test/test_smilInvalidValues.html b/content/smil/test/test_smilInvalidValues.html new file mode 100644 index 000000000000..c24761c03efb --- /dev/null +++ b/content/smil/test/test_smilInvalidValues.html @@ -0,0 +1,113 @@ + + + + + + Test invalid values cause the model to be updated (bug 941315) + + + + +Mozilla Bug 941315 +

+ +
+
+
+ + From c2a5a16255ef8a7293281f8b390e4162fe92b34b Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Wed, 11 Dec 2013 17:16:14 -0800 Subject: [PATCH 012/144] Bug 940765 - preference service can GC, r=terrence --HG-- rename : layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-3-ref.xhtml => layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-1-ref.xhtml rename : layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-3.xhtml => layout/reftests/w3c-css/submitted/flexbox/flexbox-align-self-baseline-horiz-1.xhtml extra : rebase_source : cad260c0819a3ced47e1ec612de2788e1e991670 --- js/src/devtools/rootAnalysis/annotations.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js index 1803eb87f5a2..08267463757e 100644 --- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -160,10 +160,6 @@ var ignoreFunctions = { // FIXME! "NS_DebugBreak": true, - // Bug 940765 - fetching preferences should not GC - "PrefHashEntry* pref_HashTableLookup(void*)": true, - "uint8 mozilla::Preferences::InitStaticMembers()": true, // Temporary, see bug 940765 - // These are a little overzealous -- these destructors *can* GC if they end // up wrapping a pending exception. See bug 898815 for the heavyweight fix. "void js::AutoCompartment::~AutoCompartment(int32)" : true, From 6adc5e5233e69327afa3eb37610055ce53e63f03 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Wed, 11 Dec 2013 17:16:47 -0800 Subject: [PATCH 013/144] Bug 948646 - AutoJSContext cannot GC, r=terrence --HG-- extra : rebase_source : f015f6bce00faa2c8e77cb861ed0770710e718ed --- js/src/devtools/rootAnalysis/annotations.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js index 08267463757e..530e489bdf3d 100644 --- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -165,6 +165,13 @@ var ignoreFunctions = { "void js::AutoCompartment::~AutoCompartment(int32)" : true, "void JSAutoCompartment::~JSAutoCompartment(int32)" : true, + // Bug 948646 - the only thing AutoJSContext's constructor calls + // is an Init() routine whose entire body is covered with an + // AutoAssertNoGC. AutoSafeJSContext is the same thing, just with + // a different value for the 'aSafe' parameter. + "void mozilla::AutoJSContext::AutoJSContext(mozilla::detail::GuardObjectNotifier*)" : true, + "void mozilla::AutoSafeJSContext::~AutoSafeJSContext(int32)" : true, + // And these are workarounds to avoid even more analysis work, // which would sadly still be needed even with bug 898815. "void js::AutoCompartment::AutoCompartment(js::ExclusiveContext*, JSCompartment*)": true, From 89d34ffe587efae024fd1b1306ffc4149508560c Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Wed, 11 Dec 2013 17:17:14 -0800 Subject: [PATCH 014/144] Bug 948753 - Annotate sane nsISupports virtual methods, r=bhackett --HG-- extra : rebase_source : 85695549d5d068bd393fc50e7190da00f95970ca --- js/src/devtools/rootAnalysis/annotations.js | 14 ++++++++++++++ .../devtools/rootAnalysis/computeCallgraph.js | 17 ++++++++++------- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js index 530e489bdf3d..d2e479eb018c 100644 --- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -235,3 +235,17 @@ function isSuppressConstructor(name) || /::AutoEnterAnalysis/.test(name) || /::AutoAssertNoGC/.test(name); } + +// nsISupports subclasses' methods may be scriptable (or overridden +// via binary XPCOM), and so may GC. But some fields just aren't going +// to get overridden with something that can GC. +function isOverridableField(csu, field) +{ + if (csu != 'nsISupports') + return false; + if (field == 'GetCurrentJSContext') + return false; + if (field == 'IsOnCurrentThread') + return false; + return true; +} diff --git a/js/src/devtools/rootAnalysis/computeCallgraph.js b/js/src/devtools/rootAnalysis/computeCallgraph.js index 3b66de0a6045..493cb54e173a 100644 --- a/js/src/devtools/rootAnalysis/computeCallgraph.js +++ b/js/src/devtools/rootAnalysis/computeCallgraph.js @@ -61,13 +61,13 @@ function findVirtualFunctions(csu, field, suppressed) // which should never enter the JS engine (even when calling dtors). while (worklist.length) { var csu = worklist.pop(); - if (csu == "nsISupports") { - if (field == "AddRef" || field == "Release") { - suppressed[0] = true; - return []; - } - return null; + if (csu == "nsISupports" && (field == "AddRef" || field == "Release")) { + suppressed[0] = true; + return []; } + if (isOverridableField(csu, field)) + return null; + if (csu in superclasses) { for (var superclass of superclasses[csu]) worklist.push(superclass); @@ -258,6 +258,8 @@ for (var csuIndex = minStream; csuIndex <= maxStream; csuIndex++) { xdb.open("src_body.xdb"); +printErr("Finished loading data structures"); + var minStream = xdb.min_data_stream(); var maxStream = xdb.max_data_stream(); @@ -275,8 +277,9 @@ for (var nameIndex = minStream; nameIndex <= maxStream; nameIndex++) { seenCallees = {}; seenSuppressedCallees = {}; + var functionName = name.readString(); for (var body of functionBodies) - processBody(name.readString(), body); + processBody(functionName, body); xdb.free_string(name); xdb.free_string(data); From 2a86d8d7e6d707e37347fe2bc1e850b75c827e71 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Wed, 11 Dec 2013 17:17:17 -0800 Subject: [PATCH 015/144] No bug. Remove noisy debugging printout. --HG-- extra : rebase_source : f5aa45dcb6c5d051f8888682d3aeab71b7df56d5 --- js/src/devtools/rootAnalysis/loadCallgraph.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/js/src/devtools/rootAnalysis/loadCallgraph.js b/js/src/devtools/rootAnalysis/loadCallgraph.js index 80216a1258b3..295d3f9ce7ce 100644 --- a/js/src/devtools/rootAnalysis/loadCallgraph.js +++ b/js/src/devtools/rootAnalysis/loadCallgraph.js @@ -148,9 +148,7 @@ function loadCallgraph(file) // Any field call that has been resolved to all possible callees can be // trusted to not GC if all of those callees are known to not GC. for (var name in resolvedFunctions) { - if (!(name in gcFunctions)) { + if (!(name in gcFunctions)) suppressedFunctions[name] = true; - printErr("Adding " + name); - } } } From 3e95adf082f60705ac613efdd57834f9af0acc6a Mon Sep 17 00:00:00 2001 From: Chris Peterson Date: Wed, 11 Dec 2013 21:24:13 -0800 Subject: [PATCH 016/144] Bug 949324 - Fix -Wunused-function warnings in js/. r=luke --- js/src/jit/IonMacroAssembler.cpp | 2 +- js/src/shell/js.cpp | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/js/src/jit/IonMacroAssembler.cpp b/js/src/jit/IonMacroAssembler.cpp index c68c9de5ffa5..a0511b1c653c 100644 --- a/js/src/jit/IonMacroAssembler.cpp +++ b/js/src/jit/IonMacroAssembler.cpp @@ -1213,12 +1213,12 @@ IsCompilingAsmJS() IonContext *ictx = MaybeGetIonContext(); return ictx && ictx->compartment == nullptr; } -#endif static void AssumeUnreachable_(const char *output) { MOZ_ReportAssertionFailure(output, __FILE__, __LINE__); } +#endif void MacroAssembler::assumeUnreachable(const char *output) diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 501bc02295ff..594de924ed93 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -3591,13 +3591,17 @@ EscapeForShell(AutoCStringVector &argv) } #endif -Vector sPropagatedFlags; +static Vector sPropagatedFlags; +#ifdef DEBUG +#if (defined(JS_CPU_X86) || defined(JS_CPU_X64)) && defined(JS_ION) static bool PropagateFlagToNestedShells(const char *flag) { return sPropagatedFlags.append(flag); } +#endif +#endif static bool NestedShell(JSContext *cx, unsigned argc, jsval *vp) From 8c13061a4015001d2fd988359b8f2099e4191173 Mon Sep 17 00:00:00 2001 From: Chris Peterson Date: Wed, 11 Dec 2013 21:13:24 -0800 Subject: [PATCH 017/144] Bug 949353 - Fix -Wsometimes-uninitialized warnings in dom/workers/WorkerPrivate.cpp. r=khuey --- dom/workers/WorkerPrivate.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 67a65c071ea7..f89f14a88165 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -1208,7 +1208,7 @@ public: JS::Rooted target(aCx, JS::CurrentGlobalOrNull(aCx)); NS_ASSERTION(target, "This should never be null!"); - bool preventDefaultCalled; + nsEventStatus status = nsEventStatus_eIgnore; nsIScriptGlobalObject* sgo; if (aWorkerPrivate) { @@ -1222,15 +1222,12 @@ public: event.fileName = aFilename.get(); event.typeString = NS_LITERAL_STRING("error"); - nsEventStatus status = nsEventStatus_eIgnore; nsIDOMEventTarget* target = static_cast(globalTarget); if (NS_FAILED(nsEventDispatcher::Dispatch(target, nullptr, &event, nullptr, &status))) { NS_WARNING("Failed to dispatch worker thread error event!"); status = nsEventStatus_eIgnore; } - - preventDefaultCalled = status == nsEventStatus_eConsumeNoDefault; } else if ((sgo = nsJSUtils::GetStaticScriptGlobal(target))) { // Icky, we have to fire an InternalScriptErrorEvent... @@ -1239,16 +1236,14 @@ public: event.errorMsg = aMessage.get(); event.fileName = aFilename.get(); - nsEventStatus status = nsEventStatus_eIgnore; if (NS_FAILED(sgo->HandleScriptError(&event, &status))) { NS_WARNING("Failed to dispatch main thread error event!"); status = nsEventStatus_eIgnore; } - - preventDefaultCalled = status == nsEventStatus_eConsumeNoDefault; } - if (preventDefaultCalled) { + // Was preventDefault() called? + if (status == nsEventStatus_eConsumeNoDefault) { return true; } } From 41c9799518e68da8fd4c7af63db8f4ca914ff59d Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Wed, 11 Dec 2013 12:15:16 -0800 Subject: [PATCH 018/144] Bug 940765 - Root before preference checking, r=bz --- dom/base/Navigator.cpp | 12 ++++++++---- dom/base/Navigator.h | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index 908f1f936649..443deca78c45 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -1703,14 +1703,16 @@ Navigator::HasMobileMessageSupport(JSContext* /* unused */, JSObject* aGlobal) /* static */ bool -Navigator::HasTelephonySupport(JSContext* /* unused */, JSObject* aGlobal) +Navigator::HasTelephonySupport(JSContext* cx, JSObject* aGlobal) { + JS::Rooted global(cx, aGlobal); + // First of all, the general pref has to be turned on. bool enabled = false; Preferences::GetBool("dom.telephony.enabled", &enabled); NS_ENSURE_TRUE(enabled, false); - nsCOMPtr win = GetWindowFromGlobal(aGlobal); + nsCOMPtr win = GetWindowFromGlobal(global); return win && CheckPermission(win, "telephony"); } @@ -1854,8 +1856,10 @@ bool Navigator::HasInputMethodSupport(JSContext* /* unused */, /* static */ bool -Navigator::HasDataStoreSupport(JSContext* /* unused */, JSObject* aGlobal) +Navigator::HasDataStoreSupport(JSContext* cx, JSObject* aGlobal) { + JS::Rooted global(cx, aGlobal); + // First of all, the general pref has to be turned on. bool enabled = false; Preferences::GetBool("dom.datastore.enabled", &enabled); @@ -1866,7 +1870,7 @@ Navigator::HasDataStoreSupport(JSContext* /* unused */, JSObject* aGlobal) return true; } - nsCOMPtr win = GetWindowFromGlobal(aGlobal); + nsCOMPtr win = GetWindowFromGlobal(global); if (!win) { return false; } diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h index 607b7086a960..282b3c51bd60 100644 --- a/dom/base/Navigator.h +++ b/dom/base/Navigator.h @@ -250,7 +250,7 @@ public: } static bool HasMobileMessageSupport(JSContext* /* unused */, JSObject* aGlobal); - static bool HasTelephonySupport(JSContext* /* unused */, + static bool HasTelephonySupport(JSContext* cx, JSObject* aGlobal); static bool HasCameraSupport(JSContext* /* unused */, JSObject* aGlobal); @@ -286,7 +286,7 @@ public: static bool HasInputMethodSupport(JSContext* /* unused */, JSObject* aGlobal); - static bool HasDataStoreSupport(JSContext* /* unused */, JSObject* aGlobal); + static bool HasDataStoreSupport(JSContext* cx, JSObject* aGlobal); nsPIDOMWindow* GetParentObject() const { From 6f9caffe805cdf8a8b881e5bf7f221af679d26a4 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Fri, 13 Dec 2013 15:12:29 +0900 Subject: [PATCH 019/144] Bug 949875 - Delay import some WebIDL modules; r=glandium This prevents excessive dependencies on config.status. Those extra dependencies make WebIDL developer workflow inefficient. --HG-- extra : rebase_source : 37ba82773fe6d873e25fc8b61fef44ec9f191ebe extra : amend_source : 3fa0796f096efdbe6a74138ea0258f68cbcf4071 --- dom/bindings/mozwebidlcodegen/__init__.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/dom/bindings/mozwebidlcodegen/__init__.py b/dom/bindings/mozwebidlcodegen/__init__.py index 5516dbb3a1a0..cbd6312b23d4 100644 --- a/dom/bindings/mozwebidlcodegen/__init__.py +++ b/dom/bindings/mozwebidlcodegen/__init__.py @@ -24,14 +24,8 @@ from mozbuild.util import FileAvoidWrite import mozpack.path as mozpath -import WebIDL -from Codegen import ( - CGBindingRoot, - CGEventRoot, - CGExampleRoot, - GlobalGenRoots, -) -from Configuration import Configuration +# There are various imports in this file in functions to avoid adding +# dependencies to config.status. See bug 949875. class BuildResult(object): @@ -305,11 +299,16 @@ class WebIDLCodegenManager(LoggingMixin): def generate_example_files(self, interface): """Generates example files for a given interface.""" + from Codegen import CGExampleRoot + root = CGExampleRoot(self.config, interface) return self._maybe_write_codegen(root, *self._example_paths(interface)) def _parse_webidl(self): + import WebIDL + from Configuration import Configuration + self.log(logging.INFO, 'webidl_parse', {'count': len(self._input_paths)}, 'Parsing {count} WebIDL files.') @@ -328,6 +327,8 @@ class WebIDLCodegenManager(LoggingMixin): self._input_hashes = hashes def _write_global_derived(self): + from Codegen import GlobalGenRoots + things = [('declare', f) for f in self.GLOBAL_DECLARE_FILES] things.extend(('define', f) for f in self.GLOBAL_DEFINE_FILES) @@ -453,6 +454,11 @@ class WebIDLCodegenManager(LoggingMixin): return paths def _generate_build_files_for_webidl(self, filename): + from Codegen import ( + CGBindingRoot, + CGEventRoot, + ) + self.log(logging.INFO, 'webidl_generate_build_for_input', {'filename': filename}, 'Generating WebIDL files derived from {filename}') From 09c13814b1ee01c76e530aab1795c285281cdb74 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Fri, 13 Dec 2013 00:03:55 +0900 Subject: [PATCH 020/144] Bug 949304 - Print Makefile counts during config.status; r=glandium We now capture and print the number of Makefile.in and the number of generated Makefile as part of config.status. This should give us a nice, easy to extract metric going forward. --HG-- extra : rebase_source : 48a3e9f66975505dec76746703875b2364dedc87 --- .../mozbuild/backend/recursivemake.py | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/python/mozbuild/mozbuild/backend/recursivemake.py b/python/mozbuild/mozbuild/backend/recursivemake.py index c9a4000dd46a..a909efff8ee4 100644 --- a/python/mozbuild/mozbuild/backend/recursivemake.py +++ b/python/mozbuild/mozbuild/backend/recursivemake.py @@ -268,17 +268,25 @@ class RecursiveMakeBackend(CommonBackend): self._ipdl_sources = set() def detailed(summary): - s = '{:d} total backend files. {:d} created; {:d} updated; {:d} unchanged'.format( + s = '{:d} total backend files; ' \ + '{:d} created; {:d} updated; {:d} unchanged; ' \ + '{:d} deleted; {:d} -> {:d} Makefile'.format( summary.created_count + summary.updated_count + - summary.unchanged_count, summary.created_count, - summary.updated_count, summary.unchanged_count) - if summary.deleted_count: - s+= '; {:d} deleted'.format(summary.deleted_count) + summary.unchanged_count, + summary.created_count, + summary.updated_count, + summary.unchanged_count, + summary.deleted_count, + summary.makefile_in_count, + summary.makefile_out_count) + return s # This is a little kludgy and could be improved with a better API. self.summary.backend_detailed_summary = types.MethodType(detailed, self.summary) + self.summary.makefile_in_count = 0 + self.summary.makefile_out_count = 0 self._test_manifests = {} @@ -664,6 +672,7 @@ class RecursiveMakeBackend(CommonBackend): if not stub: self.log(logging.DEBUG, 'substitute_makefile', {'path': makefile}, 'Substituting makefile: {path}') + self.summary.makefile_in_count += 1 for tier, skiplist in self._may_skip.items(): if tier in ('compile', 'binaries'): @@ -1104,6 +1113,8 @@ class RecursiveMakeBackend(CommonBackend): # the autogenerated one automatically. self.backend_input_files.add(obj.input_path) + self.summary.makefile_out_count += 1 + def _handle_webidl_collection(self, webidls): if not webidls.all_stems(): return From d917ee6f02beb60e1e5f0a6d55fc209325656710 Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Thu, 12 Dec 2013 23:18:43 -0800 Subject: [PATCH 021/144] Bug 944074 - Remove the ParallelArray constructor. (r=nmatsakis) --- .../mochitest/general/test_interfaces.html | 1 - js/src/Makefile.in | 1 - js/src/builtin/Array.js | 4 +- js/src/builtin/ParallelArray.cpp | 247 ---- js/src/builtin/ParallelArray.h | 56 - js/src/builtin/ParallelArray.js | 1253 ----------------- js/src/builtin/Utilities.js | 30 + js/src/jit-test/lib/parallelarray-helpers.js | 98 +- .../jit-test/tests/auto-regress/bug755564.js | 9 - .../jit-test/tests/auto-regress/bug783923.js | 6 - .../jit-test/tests/auto-regress/bug784011.js | 8 - .../jit-test/tests/auto-regress/bug786106.js | 8 - .../jit-test/tests/auto-regress/bug789107.js | 7 - .../jit-test/tests/auto-regress/bug791445.js | 7 - js/src/jit-test/tests/baseline/bug846072.js | 7 - js/src/jit-test/tests/baseline/bug857579.js | 22 - js/src/jit-test/tests/basic/bug815652.js | 12 - .../tests/parallel/binary-arith-numbers.js | 5 +- js/src/jit-test/tests/parallel/bug783924.js | 7 - js/src/jit-test/tests/parallel/bug787282.js | 16 - js/src/jit-test/tests/parallel/bug853555.js | 12 - js/src/jit-test/tests/parallel/bug853573.js | 17 - js/src/jit-test/tests/parallel/bug854021.js | 3 - js/src/jit-test/tests/parallel/bug854050.js | 10 - js/src/jit-test/tests/parallel/bug854381.js | 20 - js/src/jit-test/tests/parallel/bug857846.js | 9 - js/src/jit-test/tests/parallel/bug858077.js | 5 - js/src/jit-test/tests/parallel/bug858582.js | 68 - js/src/jit-test/tests/parallel/bug890465.js | 6 - js/src/jit-test/tests/parallel/bug894782.js | 6 - js/src/jit-test/tests/parallel/bug895782.js | 12 - js/src/jit-test/tests/parallel/bug908939.js | 13 - js/src/jit-test/tests/parallel/bug909599.js | 6 - .../jit-test/tests/parallel/compare-values.js | 16 +- .../tests/parallel/comprehension-2.js | 13 - .../tests/parallel/comprehension-fn-args.js | 16 - .../tests/parallel/comprehension-scale.js | 22 - .../jit-test/tests/parallel/constructor-1.js | 15 - .../jit-test/tests/parallel/constructor-2.js | 15 - .../jit-test/tests/parallel/constructor-3.js | 14 - .../jit-test/tests/parallel/constructor-4.js | 16 - .../jit-test/tests/parallel/constructor-5.js | 18 - .../tests/parallel/constructor-throws.js | 10 - js/src/jit-test/tests/parallel/element-1.js | 14 - js/src/jit-test/tests/parallel/element-2.js | 26 - js/src/jit-test/tests/parallel/element-3.js | 12 - js/src/jit-test/tests/parallel/flatten-1.js | 14 - js/src/jit-test/tests/parallel/flatten-2.js | 10 - js/src/jit-test/tests/parallel/flatten-3.js | 25 - .../jit-test/tests/parallel/flatten-throws.js | 12 - js/src/jit-test/tests/parallel/get-1.js | 9 - js/src/jit-test/tests/parallel/get-2.js | 14 - js/src/jit-test/tests/parallel/get-3.js | 9 - js/src/jit-test/tests/parallel/get-4.js | 7 - js/src/jit-test/tests/parallel/get-6.js | 14 - js/src/jit-test/tests/parallel/holes-1.js | 31 - js/src/jit-test/tests/parallel/holes-2.js | 25 - js/src/jit-test/tests/parallel/index-1.js | 9 - js/src/jit-test/tests/parallel/index-2.js | 18 - js/src/jit-test/tests/parallel/index-3.js | 25 - js/src/jit-test/tests/parallel/index-4.js | 27 - .../tests/parallel/inline-new-bad-type.js | 18 - js/src/jit-test/tests/parallel/length-1.js | 12 - js/src/jit-test/tests/parallel/length-2.js | 13 - js/src/jit-test/tests/parallel/length-3.js | 11 - js/src/jit-test/tests/parallel/mandelbrot.js | 4 +- js/src/jit-test/tests/parallel/map-3.js | 12 - .../tests/parallel/overflow-throws.js | 12 - js/src/jit-test/tests/parallel/partition-1.js | 15 - .../tests/parallel/partition-throws.js | 10 - .../tests/parallel/reduce-higher-dim.js | 14 - .../jit-test/tests/parallel/reduce-throws.js | 17 - js/src/jit-test/tests/parallel/scan-3.js | 17 - js/src/jit-test/tests/parallel/scan-throws.js | 18 - js/src/jit-test/tests/parallel/scatter-1.js | 8 - js/src/jit-test/tests/parallel/scatter-10.js | 21 - js/src/jit-test/tests/parallel/scatter-11.js | 21 - js/src/jit-test/tests/parallel/scatter-12.js | 22 - js/src/jit-test/tests/parallel/scatter-13.js | 24 - js/src/jit-test/tests/parallel/scatter-2.js | 10 - js/src/jit-test/tests/parallel/scatter-3.js | 12 - js/src/jit-test/tests/parallel/scatter-4.js | 10 - js/src/jit-test/tests/parallel/scatter-5.js | 9 - js/src/jit-test/tests/parallel/scatter-6.js | 13 - js/src/jit-test/tests/parallel/scatter-7.js | 15 - js/src/jit-test/tests/parallel/scatter-8.js | 16 - js/src/jit-test/tests/parallel/scatter-9.js | 12 - .../tests/parallel/scatter-regression-1.js | 18 - .../jit-test/tests/parallel/scatter-throws.js | 31 - js/src/jit-test/tests/parallel/shape-1.js | 8 - js/src/jit-test/tests/parallel/shape-2.js | 15 - js/src/jit-test/tests/parallel/shape-3.js | 16 - js/src/jit-test/tests/parallel/shape-4.js | 13 - js/src/jit-test/tests/parallel/shape-5.js | 13 - .../jit-test/tests/parallel/stack-overflow.js | 4 +- js/src/jit-test/tests/parallel/toString-1.js | 6 - js/src/jit/BaselineIC.cpp | 13 - js/src/jit/CodeGenerator.cpp | 70 - js/src/jit/CodeGenerator.h | 4 - js/src/jit/IonBuilder.h | 10 - js/src/jit/LIR-Common.h | 10 - js/src/jit/LOpcodes.h | 1 - js/src/jit/Lowering.cpp | 7 - js/src/jit/Lowering.h | 1 - js/src/jit/MCallOptimize.cpp | 152 -- js/src/jit/MIR.h | 26 - js/src/jit/MOpcodes.h | 1 - js/src/jit/ParallelSafetyAnalysis.cpp | 7 - js/src/jit/VMFunctions.cpp | 16 - js/src/js.msg | 8 +- js/src/jsapi.cpp | 1 - js/src/jsinferinlines.h | 4 - js/src/jsprototypes.h | 7 +- js/src/moz.build | 1 - js/src/vm/ForkJoin.cpp | 2 +- js/src/vm/GlobalObject.cpp | 3 - js/src/vm/SelfHosting.cpp | 23 - 117 files changed, 58 insertions(+), 3246 deletions(-) delete mode 100644 js/src/builtin/ParallelArray.cpp delete mode 100644 js/src/builtin/ParallelArray.h delete mode 100644 js/src/builtin/ParallelArray.js delete mode 100644 js/src/jit-test/tests/auto-regress/bug755564.js delete mode 100644 js/src/jit-test/tests/auto-regress/bug783923.js delete mode 100644 js/src/jit-test/tests/auto-regress/bug784011.js delete mode 100644 js/src/jit-test/tests/auto-regress/bug786106.js delete mode 100644 js/src/jit-test/tests/auto-regress/bug789107.js delete mode 100644 js/src/jit-test/tests/auto-regress/bug791445.js delete mode 100644 js/src/jit-test/tests/baseline/bug846072.js delete mode 100644 js/src/jit-test/tests/baseline/bug857579.js delete mode 100644 js/src/jit-test/tests/basic/bug815652.js delete mode 100644 js/src/jit-test/tests/parallel/bug783924.js delete mode 100644 js/src/jit-test/tests/parallel/bug787282.js delete mode 100644 js/src/jit-test/tests/parallel/bug853555.js delete mode 100644 js/src/jit-test/tests/parallel/bug853573.js delete mode 100644 js/src/jit-test/tests/parallel/bug854021.js delete mode 100644 js/src/jit-test/tests/parallel/bug854050.js delete mode 100644 js/src/jit-test/tests/parallel/bug854381.js delete mode 100644 js/src/jit-test/tests/parallel/bug857846.js delete mode 100644 js/src/jit-test/tests/parallel/bug858077.js delete mode 100644 js/src/jit-test/tests/parallel/bug858582.js delete mode 100644 js/src/jit-test/tests/parallel/bug890465.js delete mode 100644 js/src/jit-test/tests/parallel/bug894782.js delete mode 100644 js/src/jit-test/tests/parallel/bug895782.js delete mode 100644 js/src/jit-test/tests/parallel/bug908939.js delete mode 100644 js/src/jit-test/tests/parallel/bug909599.js delete mode 100644 js/src/jit-test/tests/parallel/comprehension-2.js delete mode 100644 js/src/jit-test/tests/parallel/comprehension-fn-args.js delete mode 100644 js/src/jit-test/tests/parallel/comprehension-scale.js delete mode 100644 js/src/jit-test/tests/parallel/constructor-1.js delete mode 100644 js/src/jit-test/tests/parallel/constructor-2.js delete mode 100644 js/src/jit-test/tests/parallel/constructor-3.js delete mode 100644 js/src/jit-test/tests/parallel/constructor-4.js delete mode 100644 js/src/jit-test/tests/parallel/constructor-5.js delete mode 100644 js/src/jit-test/tests/parallel/constructor-throws.js delete mode 100644 js/src/jit-test/tests/parallel/element-1.js delete mode 100644 js/src/jit-test/tests/parallel/element-2.js delete mode 100644 js/src/jit-test/tests/parallel/element-3.js delete mode 100644 js/src/jit-test/tests/parallel/flatten-1.js delete mode 100644 js/src/jit-test/tests/parallel/flatten-2.js delete mode 100644 js/src/jit-test/tests/parallel/flatten-3.js delete mode 100644 js/src/jit-test/tests/parallel/flatten-throws.js delete mode 100644 js/src/jit-test/tests/parallel/get-1.js delete mode 100644 js/src/jit-test/tests/parallel/get-2.js delete mode 100644 js/src/jit-test/tests/parallel/get-3.js delete mode 100644 js/src/jit-test/tests/parallel/get-4.js delete mode 100644 js/src/jit-test/tests/parallel/get-6.js delete mode 100644 js/src/jit-test/tests/parallel/holes-1.js delete mode 100644 js/src/jit-test/tests/parallel/holes-2.js delete mode 100644 js/src/jit-test/tests/parallel/index-1.js delete mode 100644 js/src/jit-test/tests/parallel/index-2.js delete mode 100644 js/src/jit-test/tests/parallel/index-3.js delete mode 100644 js/src/jit-test/tests/parallel/index-4.js delete mode 100644 js/src/jit-test/tests/parallel/inline-new-bad-type.js delete mode 100644 js/src/jit-test/tests/parallel/length-1.js delete mode 100644 js/src/jit-test/tests/parallel/length-2.js delete mode 100644 js/src/jit-test/tests/parallel/length-3.js delete mode 100644 js/src/jit-test/tests/parallel/map-3.js delete mode 100644 js/src/jit-test/tests/parallel/overflow-throws.js delete mode 100644 js/src/jit-test/tests/parallel/partition-1.js delete mode 100644 js/src/jit-test/tests/parallel/partition-throws.js delete mode 100644 js/src/jit-test/tests/parallel/reduce-higher-dim.js delete mode 100644 js/src/jit-test/tests/parallel/reduce-throws.js delete mode 100644 js/src/jit-test/tests/parallel/scan-3.js delete mode 100644 js/src/jit-test/tests/parallel/scan-throws.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-1.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-10.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-11.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-12.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-13.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-2.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-3.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-4.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-5.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-6.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-7.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-8.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-9.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-regression-1.js delete mode 100644 js/src/jit-test/tests/parallel/scatter-throws.js delete mode 100644 js/src/jit-test/tests/parallel/shape-1.js delete mode 100644 js/src/jit-test/tests/parallel/shape-2.js delete mode 100644 js/src/jit-test/tests/parallel/shape-3.js delete mode 100644 js/src/jit-test/tests/parallel/shape-4.js delete mode 100644 js/src/jit-test/tests/parallel/shape-5.js delete mode 100644 js/src/jit-test/tests/parallel/toString-1.js diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index 3b2363f47d9f..0ec83523aa00 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -69,7 +69,6 @@ var ecmaGlobals = {name: "NaN", xbl: false}, "Number", "Object", - {name: "ParallelArray", nightly: true}, "Proxy", "RangeError", "ReferenceError", diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 1a7008f5c4c6..6999747d6d3d 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -579,7 +579,6 @@ selfhosting_srcs := \ $(srcdir)/builtin/Iterator.js \ $(srcdir)/builtin/Map.js \ $(srcdir)/builtin/Number.js \ - $(srcdir)/builtin/ParallelArray.js \ $(srcdir)/builtin/String.js \ $(srcdir)/builtin/Set.js \ $(srcdir)/builtin/TypedObject.js \ diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js index b61c6c6a3192..d27f159ac295 100644 --- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -556,6 +556,8 @@ function ArrayKeys() { return CreateArrayIterator(this, ITEM_KIND_KEY); } +#ifdef ENABLE_PARALLEL_JS + /* * Strawman spec: * http://wiki.ecmascript.org/doku.php?id=strawman:data_parallelism @@ -649,8 +651,6 @@ function ComputeAllSliceBounds(numItems, numSlices) { return info; } -#ifdef ENABLE_PARALLEL_JS - /** * Creates a new array by applying |func(e, i, self)| for each element |e| * with index |i|. diff --git a/js/src/builtin/ParallelArray.cpp b/js/src/builtin/ParallelArray.cpp deleted file mode 100644 index a046819a9a2e..000000000000 --- a/js/src/builtin/ParallelArray.cpp +++ /dev/null @@ -1,247 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "builtin/ParallelArray.h" - -#include "jsobj.h" - -#include "vm/GlobalObject.h" -#include "vm/String.h" - -#include "jsobjinlines.h" - -using namespace js; - -// -// ParallelArrayObject -// - -FixedHeapPtr ParallelArrayObject::ctorNames[NumCtors]; - -const JSFunctionSpec ParallelArrayObject::methods[] = { - JS_SELF_HOSTED_FN("map", "ParallelArrayMap", 2, 0), - JS_SELF_HOSTED_FN("reduce", "ParallelArrayReduce", 2, 0), - JS_SELF_HOSTED_FN("scan", "ParallelArrayScan", 2, 0), - JS_SELF_HOSTED_FN("scatter", "ParallelArrayScatter", 5, 0), - JS_SELF_HOSTED_FN("filter", "ParallelArrayFilter", 2, 0), - JS_SELF_HOSTED_FN("partition", "ParallelArrayPartition", 1, 0), - JS_SELF_HOSTED_FN("flatten", "ParallelArrayFlatten", 0, 0), - - // FIXME #838906. Note that `get()` is not currently defined on this table but - // rather is assigned to each instance of ParallelArray as an own - // property. This is a bit of a hack designed to supply a - // specialized version of get() based on the dimensionality of the - // receiver. In the future we can improve this by (1) extending - // TI to track the dimensionality of the receiver and (2) using a - // hint to aggressively inline calls to get(). - // JS_SELF_HOSTED_FN("get", "ParallelArrayGet", 1, 0), - - JS_SELF_HOSTED_FN("toString", "ParallelArrayToString", 0, 0), - JS_FS_END -}; - -const JSPropertySpec ParallelArrayObject::properties[] = { - JS_SELF_HOSTED_GET("length", "ParallelArrayLength", JSPROP_PERMANENT), - JS_PS_END -}; - -const Class ParallelArrayObject::protoClass = { - "ParallelArray", - JSCLASS_HAS_CACHED_PROTO(JSProto_ParallelArray), - JS_PropertyStub, // addProperty - JS_DeletePropertyStub, // delProperty - JS_PropertyStub, // getProperty - JS_StrictPropertyStub, // setProperty - JS_EnumerateStub, - JS_ResolveStub, - JS_ConvertStub -}; - -const Class ParallelArrayObject::class_ = { - "ParallelArray", - JSCLASS_HAS_CACHED_PROTO(JSProto_ParallelArray), - JS_PropertyStub, // addProperty - JS_DeletePropertyStub, // delProperty - JS_PropertyStub, // getProperty - JS_StrictPropertyStub, // setProperty - JS_EnumerateStub, - JS_ResolveStub, - JS_ConvertStub -}; - -/*static*/ bool -ParallelArrayObject::initProps(JSContext *cx, HandleObject obj) -{ - RootedValue undef(cx, UndefinedValue()); - RootedValue zero(cx, Int32Value(0)); - - if (!JSObject::defineProperty(cx, obj, cx->names().buffer, undef)) - return false; - if (!JSObject::defineProperty(cx, obj, cx->names().offset, zero)) - return false; - if (!JSObject::defineProperty(cx, obj, cx->names().shape, undef)) - return false; - if (!JSObject::defineProperty(cx, obj, cx->names().get, undef)) - return false; - - return true; -} - -/*static*/ bool -ParallelArrayObject::construct(JSContext *cx, unsigned argc, Value *vp) -{ - RootedFunction ctor(cx, getConstructor(cx, argc)); - if (!ctor) - return false; - CallArgs args = CallArgsFromVp(argc, vp); - return constructHelper(cx, &ctor, args); -} - -/* static */ JSFunction * -ParallelArrayObject::maybeGetConstructor(GlobalObject *global, unsigned argc) -{ - PropertyName *ctorName = ctorNames[js::Min(argc, NumCtors - 1)]; - Value ctorValue; - if (!global->maybeGetIntrinsicValue(ctorName, &ctorValue)) - return nullptr; - JS_ASSERT(ctorValue.isObject() && ctorValue.toObject().is()); - return &ctorValue.toObject().as(); -} - -/* static */ JSFunction * -ParallelArrayObject::getConstructor(JSContext *cx, unsigned argc) -{ - RootedPropertyName ctorName(cx, ctorNames[js::Min(argc, NumCtors - 1)]); - RootedValue ctorValue(cx); - if (!cx->global()->getIntrinsicValue(cx, ctorName, &ctorValue)) - return nullptr; - JS_ASSERT(ctorValue.isObject() && ctorValue.toObject().is()); - return &ctorValue.toObject().as(); -} - -/*static*/ JSObject * -ParallelArrayObject::newInstance(JSContext *cx, NewObjectKind newKind /* = GenericObject */) -{ - gc::AllocKind kind = gc::GetGCObjectKind(NumFixedSlots); - RootedObject result(cx, NewBuiltinClassInstance(cx, &class_, kind, newKind)); - if (!result) - return nullptr; - - // Add in the basic PA properties now with default values: - if (!initProps(cx, result)) - return nullptr; - - return result; -} - -/*static*/ bool -ParallelArrayObject::constructHelper(JSContext *cx, MutableHandleFunction ctor, CallArgs &args0) -{ - RootedObject result(cx, newInstance(cx, TenuredObject)); - if (!result) - return false; - - if (cx->typeInferenceEnabled()) { - jsbytecode *pc; - RootedScript script(cx, cx->currentScript(&pc)); - if (script) { - if (ctor->nonLazyScript()->shouldCloneAtCallsite()) { - ctor.set(CloneFunctionAtCallsite(cx, ctor, script, pc)); - if (!ctor) - return false; - } - - // Create the type object for the PA. Add in the current - // properties as definite properties if this type object is newly - // created. To tell if it is newly created, we check whether it - // has any properties yet or not, since any returned type object - // must have been created by this same C++ code and hence would - // already have properties if it had been returned before. - types::TypeObject *paTypeObject = - types::TypeScript::InitObject(cx, script, pc, JSProto_ParallelArray); - if (!paTypeObject) - return false; - if (paTypeObject->getPropertyCount() == 0 && !paTypeObject->unknownProperties()) { - if (!paTypeObject->addDefiniteProperties(cx, result)) - return false; - - // addDefiniteProperties() above should have added one - // property for each of the fixed slots: - JS_ASSERT(paTypeObject->getPropertyCount() == NumFixedSlots); - } - result->setType(paTypeObject); - } - } - - InvokeArgs args(cx); - if (!args.init(args0.length())) - return false; - - args.setCallee(ObjectValue(*ctor)); - args.setThis(ObjectValue(*result)); - - for (uint32_t i = 0; i < args0.length(); i++) - args[i].set(args0[i]); - - if (!Invoke(cx, args)) - return false; - - args0.rval().setObject(*result); - return true; -} - -JSObject * -ParallelArrayObject::initClass(JSContext *cx, HandleObject obj) -{ - JS_ASSERT(obj->isNative()); - - // Cache constructor names. - { - static const char *const ctorStrs[NumCtors] = { - "ParallelArrayConstructEmpty", - "ParallelArrayConstructFromArray", - "ParallelArrayConstructFromFunction", - "ParallelArrayConstructFromFunctionMode" - }; - for (uint32_t i = 0; i < NumCtors; i++) { - JSAtom *atom = Atomize(cx, ctorStrs[i], strlen(ctorStrs[i]), InternAtom); - if (!atom) - return nullptr; - ctorNames[i].init(atom->asPropertyName()); - } - } - - Rooted global(cx, &obj->as()); - - RootedObject proto(cx, global->createBlankPrototype(cx, &protoClass)); - if (!proto) - return nullptr; - - JSProtoKey key = JSProto_ParallelArray; - RootedFunction ctor(cx, global->createConstructor(cx, construct, - cx->names().ParallelArray, 0)); - if (!ctor || - !LinkConstructorAndPrototype(cx, ctor, proto) || - !DefinePropertiesAndBrand(cx, proto, properties, methods) || - !DefineConstructorAndPrototype(cx, global, key, ctor, proto)) - { - return nullptr; - } - - return proto; -} - -bool -ParallelArrayObject::is(const Value &v) -{ - return v.isObject() && v.toObject().hasClass(&class_); -} - -JSObject * -js_InitParallelArrayClass(JSContext *cx, js::HandleObject obj) -{ - return ParallelArrayObject::initClass(cx, obj); -} diff --git a/js/src/builtin/ParallelArray.h b/js/src/builtin/ParallelArray.h deleted file mode 100644 index 3232ae70b2d0..000000000000 --- a/js/src/builtin/ParallelArray.h +++ /dev/null @@ -1,56 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef builtin_ParallelArray_h -#define builtin_ParallelArray_h - -#include "jsobj.h" - -namespace js { - -class ParallelArrayObject : public JSObject -{ - static const Class protoClass; - static const JSFunctionSpec methods[]; - static const JSPropertySpec properties[]; - static const uint32_t NumFixedSlots = 4; - static const uint32_t NumCtors = 4; - static FixedHeapPtr ctorNames[NumCtors]; - - static bool initProps(JSContext *cx, HandleObject obj); - - public: - static const Class class_; - - static bool construct(JSContext *cx, unsigned argc, Value *vp); - static bool constructHelper(JSContext *cx, MutableHandleFunction ctor, CallArgs &args); - - // Creates a new ParallelArray instance with the correct number of slots - // and so forth. - // - // NOTE: This object will NOT have the correct type object! It is - // up to the caller to adjust the type object appropriately - // before releasing the object into the wild. You probably want - // to be calling construct() above, which will adjust the type - // object for you, since ParallelArray type objects must be setup - // in a rather particular way to interact well with the - // self-hosted code. See constructHelper() for details. - static JSObject *newInstance(JSContext *cx, NewObjectKind newKind = GenericObject); - - // Get the constructor function for argc number of arguments. - static JSFunction *maybeGetConstructor(GlobalObject *global, unsigned argc); - static JSFunction *getConstructor(JSContext *cx, unsigned argc); - - static JSObject *initClass(JSContext *cx, HandleObject obj); - static bool is(const Value &v); -}; - -} // namespace js - -extern JSObject * -js_InitParallelArrayClass(JSContext *cx, js::HandleObject obj); - -#endif /* builtin_ParallelArray_h */ diff --git a/js/src/builtin/ParallelArray.js b/js/src/builtin/ParallelArray.js deleted file mode 100644 index 4ae4c09c1850..000000000000 --- a/js/src/builtin/ParallelArray.js +++ /dev/null @@ -1,1253 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// FIXME(bug 844882): Parallel array properties should not be exposed. - -/** - * Compute the partial products in reverse order. - * e.g., if the shape is [A,B,C,D], then the - * array |products| will be [1,D,CD,BCD]. - */ -function ComputeProducts(shape) { - var product = 1; - var products = [1]; - var sdimensionality = shape.length; - for (var i = sdimensionality - 1; i > 0; i--) { - product *= shape[i]; - ARRAY_PUSH(products, product); - } - return products; -} - -/** - * Given a shape and some index |index1d|, computes and returns an - * array containing the N-dimensional index that maps to |index1d|. - */ -function ComputeIndices(shape, index1d) { - - var products = ComputeProducts(shape); - var l = shape.length; - - var result = []; - for (var i = 0; i < l; i++) { - // Obtain product of all higher dimensions. - // So if i == 0 and shape is [A,B,C,D], yields BCD. - var stride = products[l - i - 1]; - - // Compute how many steps of width stride we could take. - var index = (index1d / stride) | 0; - ARRAY_PUSH(result, index); - - // Adjust remaining indices for smaller dimensions. - index1d -= (index * stride); - } - - return result; -} - -function StepIndices(shape, indices) { - for (var i = shape.length - 1; i >= 0; i--) { - var indexi = indices[i] + 1; - if (indexi < shape[i]) { - indices[i] = indexi; - return; - } - indices[i] = 0; - } -} - -// Constructor -// -// We split the 3 construction cases so that we don't case on arguments. - -/** - * This is the function invoked for |new ParallelArray()| - */ -function ParallelArrayConstructEmpty() { - this.buffer = []; - this.offset = 0; - this.shape = [0]; - this.get = ParallelArrayGet1; -} - -/** - * This is the function invoked for |new ParallelArray(array)|. - * It copies the data from its array-like argument |array|. - */ -function ParallelArrayConstructFromArray(array) { - var buffer = ToObject(array); - var length = buffer.length >>> 0; - if (length !== buffer.length) - ThrowError(JSMSG_PAR_ARRAY_BAD_ARG, ""); - - var buffer1 = []; - for (var i = 0; i < length; i++) - ARRAY_PUSH(buffer1, buffer[i]); - - this.buffer = buffer1; - this.offset = 0; - this.shape = [length]; - this.get = ParallelArrayGet1; -} - -/** - * Wrapper around |ParallelArrayConstructFromComprehension()| for the - * case where 2 arguments are supplied. This is typically what users will - * invoke. We provide an explicit two-argument version rather than - * relying on JS's semantics for absent arguments because it simplifies - * the ion code that does inlining of PA constructors. - */ -function ParallelArrayConstructFromFunction(shape, func) { - return ParallelArrayConstructFromComprehension(this, shape, func, undefined); -} - -/** - * Wrapper around |ParallelArrayConstructFromComprehension()| for the - * case where 3 arguments are supplied. - */ -function ParallelArrayConstructFromFunctionMode(shape, func, mode) { - return ParallelArrayConstructFromComprehension(this, shape, func, mode); -} - -/** - * "Comprehension form": This is the function invoked for |new - * ParallelArray(dim, fn)|. If |dim| is a number, then it creates a - * new 1-dimensional parallel array with shape |[dim]| where index |i| - * is equal to |fn(i)|. If |dim| is a vector, then it creates a new - * N-dimensional parallel array where index |a, b, ... z| is equal to - * |fn(a, b, ...z)|. - * - * The final |mode| argument is an internal argument used only - * during our unit-testing. - */ -function ParallelArrayConstructFromComprehension(self, shape, func, mode) { - // FIXME(bug 844887): Check |IsCallable(func)| - - if (typeof shape === "number") { - var length = shape >>> 0; - if (length !== shape) - ThrowError(JSMSG_PAR_ARRAY_BAD_ARG, ""); - ParallelArrayBuild(self, [length], func, mode); - } else if (!shape || typeof shape.length !== "number") { - ThrowError(JSMSG_PAR_ARRAY_BAD_ARG, ""); - } else { - var shape1 = []; - for (var i = 0, l = shape.length; i < l; i++) { - var s0 = shape[i]; - var s1 = s0 >>> 0; - if (s1 !== s0) - ThrowError(JSMSG_PAR_ARRAY_BAD_ARG, ""); - ARRAY_PUSH(shape1, s1); - } - ParallelArrayBuild(self, shape1, func, mode); - } -} - -/** - * Internal function used when constructing new parallel arrays. The - * NewParallelArray() intrinsic takes a ctor function which it invokes - * with the given shape, buffer, offset. The |this| parameter will be - * the newly constructed parallel array. - */ -function ParallelArrayView(shape, buffer, offset) { - this.shape = shape; - this.buffer = buffer; - this.offset = offset; - - switch (shape.length) { - case 1: this.get = ParallelArrayGet1; break; - case 2: this.get = ParallelArrayGet2; break; - case 3: this.get = ParallelArrayGet3; break; - default: this.get = ParallelArrayGetN; break; - } - - // Due to inlining of NewParallelArray, the return type of this function - // gets recorded as the return type of NewParallelArray at inlined sites, so - // we must take care to return the same thing. - return this; -} - -/** - * Helper for the comprehension form. Constructs an N-dimensional - * array where |N == shape.length|. |shape| must be an array of - * integers. The data for any given index vector |i| is determined by - * |func(...i)|. - */ -function ParallelArrayBuild(self, shape, func, mode) { - self.offset = 0; - self.shape = shape; - - var length; - var xDimension, yDimension, zDimension; - var computefunc; - - switch (shape.length) { - case 1: - length = shape[0]; - self.get = ParallelArrayGet1; - computefunc = fill1; - break; - case 2: - xDimension = shape[0]; - yDimension = shape[1]; - length = xDimension * yDimension; - self.get = ParallelArrayGet2; - computefunc = fill2; - break; - case 3: - xDimension = shape[0]; - yDimension = shape[1]; - zDimension = shape[2]; - length = xDimension * yDimension * zDimension; - self.get = ParallelArrayGet3; - computefunc = fill3; - break; - default: - length = 1; - for (var i = 0; i < shape.length; i++) - length *= shape[i]; - self.get = ParallelArrayGetN; - computefunc = fillN; - break; - } - - var buffer = self.buffer = NewDenseArray(length); - - parallel: for (;;) { - // Avoid parallel compilation if we are already nested in another - // parallel section or the user told us not to parallelize. The - // use of a for (;;) loop is working around some ion limitations: - // - // - Breaking out of named blocks does not currently work (bug 684384); - // - Unreachable Code Elim. can't properly handle if (a && b) (bug 669796) - if (ShouldForceSequential()) - break parallel; - if (!TRY_PARALLEL(mode)) - break parallel; - if (computefunc === fillN) - break parallel; - - var chunks = ComputeNumChunks(length); - var numSlices = ForkJoinSlices(); - var info = ComputeAllSliceBounds(chunks, numSlices); - ForkJoin(constructSlice, ForkJoinMode(mode)); - return; - } - - // Sequential fallback: - ASSERT_SEQUENTIAL_IS_OK(mode); - computefunc(0, length); - return; - - function constructSlice(sliceId, numSlices, warmup) { - var chunkPos = info[SLICE_POS(sliceId)]; - var chunkEnd = info[SLICE_END(sliceId)]; - - if (warmup && chunkEnd > chunkPos) - chunkEnd = chunkPos + 1; - - while (chunkPos < chunkEnd) { - var indexStart = chunkPos << CHUNK_SHIFT; - var indexEnd = std_Math_min(indexStart + CHUNK_SIZE, length); - computefunc(indexStart, indexEnd); - UnsafePutElements(info, SLICE_POS(sliceId), ++chunkPos); - } - - return chunkEnd == info[SLICE_END(sliceId)]; - } - - function fill1(indexStart, indexEnd) { - for (var i = indexStart; i < indexEnd; i++) - UnsafePutElements(buffer, i, func(i)); - } - - function fill2(indexStart, indexEnd) { - var x = (indexStart / yDimension) | 0; - var y = indexStart - x * yDimension; - for (var i = indexStart; i < indexEnd; i++) { - UnsafePutElements(buffer, i, func(x, y)); - if (++y == yDimension) { - y = 0; - ++x; - } - } - } - - function fill3(indexStart, indexEnd) { - var x = (indexStart / (yDimension * zDimension)) | 0; - var r = indexStart - x * yDimension * zDimension; - var y = (r / zDimension) | 0; - var z = r - y * zDimension; - for (var i = indexStart; i < indexEnd; i++) { - UnsafePutElements(buffer, i, func(x, y, z)); - if (++z == zDimension) { - z = 0; - if (++y == yDimension) { - y = 0; - ++x; - } - } - } - } - - function fillN(indexStart, indexEnd) { - var indices = ComputeIndices(shape, indexStart); - for (var i = indexStart; i < indexEnd; i++) { - var result = callFunction(std_Function_apply, func, null, indices); - UnsafePutElements(buffer, i, result); - StepIndices(shape, indices); - } - } -} - -/** - * Creates a new parallel array by applying |func(e, i, self)| for each - * element |e| with index |i|. Note that - * this always operates on the outermost dimension only. - */ -function ParallelArrayMap(func, mode) { - // FIXME(bug 844887): Check |this instanceof ParallelArray| - // FIXME(bug 844887): Check |IsCallable(func)| - - var self = this; - var length = self.shape[0]; - var buffer = NewDenseArray(length); - - parallel: for (;;) { // see ParallelArrayBuild() to explain why for(;;) etc - if (ShouldForceSequential()) - break parallel; - if (!TRY_PARALLEL(mode)) - break parallel; - - var chunks = ComputeNumChunks(length); - var numSlices = ForkJoinSlices(); - var info = ComputeAllSliceBounds(chunks, numSlices); - ForkJoin(mapSlice, ForkJoinMode(mode)); - return NewParallelArray(ParallelArrayView, [length], buffer, 0); - } - - // Sequential fallback: - ASSERT_SEQUENTIAL_IS_OK(mode); - for (var i = 0; i < length; i++) { - // Note: Unlike JS arrays, parallel arrays cannot have holes. - var v = func(self.get(i), i, self); - UnsafePutElements(buffer, i, v); - } - return NewParallelArray(ParallelArrayView, [length], buffer, 0); - - function mapSlice(sliceId, numSlices, warmup) { - var chunkPos = info[SLICE_POS(sliceId)]; - var chunkEnd = info[SLICE_END(sliceId)]; - - if (warmup && chunkEnd > chunkPos + 1) - chunkEnd = chunkPos + 1; - - while (chunkPos < chunkEnd) { - var indexStart = chunkPos << CHUNK_SHIFT; - var indexEnd = std_Math_min(indexStart + CHUNK_SIZE, length); - - for (var i = indexStart; i < indexEnd; i++) - UnsafePutElements(buffer, i, func(self.get(i), i, self)); - - UnsafePutElements(info, SLICE_POS(sliceId), ++chunkPos); - } - - return chunkEnd == info[SLICE_END(sliceId)]; - } - - return undefined; -} - -/** - * Reduces the elements in a parallel array's outermost dimension - * using the given reduction function. - */ -function ParallelArrayReduce(func, mode) { - // FIXME(bug 844887): Check |this instanceof ParallelArray| - // FIXME(bug 844887): Check |IsCallable(func)| - - var self = this; - var length = self.shape[0]; - - if (length === 0) - ThrowError(JSMSG_PAR_ARRAY_REDUCE_EMPTY); - - parallel: for (;;) { // see ParallelArrayBuild() to explain why for(;;) etc - if (ShouldForceSequential()) - break parallel; - if (!TRY_PARALLEL(mode)) - break parallel; - - var chunks = ComputeNumChunks(length); - var numSlices = ForkJoinSlices(); - if (chunks < numSlices) - break parallel; - - var info = ComputeAllSliceBounds(chunks, numSlices); - var subreductions = NewDenseArray(numSlices); - ForkJoin(reduceSlice, ForkJoinMode(mode)); - var accumulator = subreductions[0]; - for (var i = 1; i < numSlices; i++) - accumulator = func(accumulator, subreductions[i]); - return accumulator; - } - - // Sequential fallback: - ASSERT_SEQUENTIAL_IS_OK(mode); - var accumulator = self.get(0); - for (var i = 1; i < length; i++) - accumulator = func(accumulator, self.get(i)); - return accumulator; - - function reduceSlice(sliceId, numSlices, warmup) { - var chunkStart = info[SLICE_START(sliceId)]; - var chunkPos = info[SLICE_POS(sliceId)]; - var chunkEnd = info[SLICE_END(sliceId)]; - - // (*) This function is carefully designed so that the warmup - // (which executes with chunkStart === chunkPos) will execute all - // potential loads and stores. In particular, the warmup run - // processes two chunks rather than one. Moreover, it stores - // accumulator into subreductions and then loads it again to - // ensure that the load is executed during the warmup, as it will - // certainly be executed during subsequent runs. - - if (warmup && chunkEnd > chunkPos + 2) - chunkEnd = chunkPos + 2; - - if (chunkStart === chunkPos) { - var indexPos = chunkStart << CHUNK_SHIFT; - var accumulator = reduceChunk(self.get(indexPos), indexPos + 1, indexPos + CHUNK_SIZE); - - UnsafePutElements(subreductions, sliceId, accumulator, // see (*) above - info, SLICE_POS(sliceId), ++chunkPos); - } - - var accumulator = subreductions[sliceId]; // see (*) above - - while (chunkPos < chunkEnd) { - var indexPos = chunkPos << CHUNK_SHIFT; - accumulator = reduceChunk(accumulator, indexPos, indexPos + CHUNK_SIZE); - UnsafePutElements(subreductions, sliceId, accumulator, info, SLICE_POS(sliceId), ++chunkPos); - } - - return chunkEnd == info[SLICE_END(sliceId)]; - } - - function reduceChunk(accumulator, from, to) { - to = std_Math_min(to, length); - for (var i = from; i < to; i++) - accumulator = func(accumulator, self.get(i)); - return accumulator; - } - - return undefined; -} - -/** - * |scan()| returns an array [s_0, ..., s_N] where - * |s_i| is equal to the reduction (as per |reduce()|) - * of elements |0..i|. This is the generalization - * of partial sum. - */ -function ParallelArrayScan(func, mode) { - // FIXME(bug 844887): Check |this instanceof ParallelArray| - // FIXME(bug 844887): Check |IsCallable(func)| - - var self = this; - var length = self.shape[0]; - - if (length === 0) - ThrowError(JSMSG_PAR_ARRAY_REDUCE_EMPTY); - - var buffer = NewDenseArray(length); - - parallel: for (;;) { // see ParallelArrayBuild() to explain why for(;;) etc - if (ShouldForceSequential()) - break parallel; - if (!TRY_PARALLEL(mode)) - break parallel; - - var chunks = ComputeNumChunks(length); - var numSlices = ForkJoinSlices(); - if (chunks < numSlices) - break parallel; - var info = ComputeAllSliceBounds(chunks, numSlices); - - // Scan slices individually (see comment on phase1()). - ForkJoin(phase1, ForkJoinMode(mode)); - - // Compute intermediates array (see comment on phase2()). - var intermediates = []; - var accumulator = buffer[finalElement(0)]; - ARRAY_PUSH(intermediates, accumulator); - for (var i = 1; i < numSlices - 1; i++) { - accumulator = func(accumulator, buffer[finalElement(i)]); - ARRAY_PUSH(intermediates, accumulator); - } - - // Reset the current position information for each slice, but - // convert from chunks to indices (see comment on phase2()). - for (var i = 0; i < numSlices; i++) { - info[SLICE_POS(i)] = info[SLICE_START(i)] << CHUNK_SHIFT; - info[SLICE_END(i)] = info[SLICE_END(i)] << CHUNK_SHIFT; - } - info[SLICE_END(numSlices - 1)] = std_Math_min(info[SLICE_END(numSlices - 1)], length); - - // Complete each slice using intermediates array (see comment on phase2()). - ForkJoin(phase2, ForkJoinMode(mode)); - return NewParallelArray(ParallelArrayView, [length], buffer, 0); - } - - // Sequential fallback: - ASSERT_SEQUENTIAL_IS_OK(mode); - scan(self.get(0), 0, length); - return NewParallelArray(ParallelArrayView, [length], buffer, 0); - - function scan(accumulator, start, end) { - UnsafePutElements(buffer, start, accumulator); - for (var i = start + 1; i < end; i++) { - accumulator = func(accumulator, self.get(i)); - UnsafePutElements(buffer, i, accumulator); - } - return accumulator; - } - - /** - * In phase 1, we divide the source array into |numSlices| slices and - * compute scan on each slice sequentially as if it were the entire - * array. This function is responsible for computing one of those - * slices. - * - * So, if we have an array [A,B,C,D,E,F,G,H,I], |numSlices == 3|, - * and our function |func| is sum, then we would wind up computing a - * result array like: - * - * [A, A+B, A+B+C, D, D+E, D+E+F, G, G+H, G+H+I] - * ^~~~~~~~~~~~^ ^~~~~~~~~~~~^ ^~~~~~~~~~~~~^ - * Slice 0 Slice 1 Slice 2 - * - * Read on in phase2 to see what we do next! - */ - function phase1(sliceId, numSlices, warmup) { - var chunkStart = info[SLICE_START(sliceId)]; - var chunkPos = info[SLICE_POS(sliceId)]; - var chunkEnd = info[SLICE_END(sliceId)]; - - if (warmup && chunkEnd > chunkPos + 2) - chunkEnd = chunkPos + 2; - - if (chunkPos == chunkStart) { - // For the first chunk, the accumulator begins as the value in - // the input at the start of the chunk. - var indexStart = chunkPos << CHUNK_SHIFT; - var indexEnd = std_Math_min(indexStart + CHUNK_SIZE, length); - scan(self.get(indexStart), indexStart, indexEnd); - UnsafePutElements(info, SLICE_POS(sliceId), ++chunkPos); - } - - while (chunkPos < chunkEnd) { - // For each subsequent chunk, the accumulator begins as the - // combination of the final value of prev chunk and the value in - // the input at the start of this chunk. Note that this loop is - // written as simple as possible, at the cost of an extra read - // from the buffer per iteration. - var indexStart = chunkPos << CHUNK_SHIFT; - var indexEnd = std_Math_min(indexStart + CHUNK_SIZE, length); - var accumulator = func(buffer[indexStart - 1], self.get(indexStart)); - scan(accumulator, indexStart, indexEnd); - UnsafePutElements(info, SLICE_POS(sliceId), ++chunkPos); - } - - return chunkEnd == info[SLICE_END(sliceId)]; - } - - /** - * Computes the index of the final element computed by the slice |sliceId|. - */ - function finalElement(sliceId) { - var chunkEnd = info[SLICE_END(sliceId)]; // last chunk written by |sliceId| is endChunk - 1 - var indexStart = std_Math_min(chunkEnd << CHUNK_SHIFT, length); - return indexStart - 1; - } - - /** - * After computing the phase1 results, we compute an - * |intermediates| array. |intermediates[i]| contains the result - * of reducing the final value from each preceding slice j> |length|, Divide-Scatter-Vector seems like - // a clear win over Divide-Output-Range, since for the latter, the - // expense of redundantly scanning the |targets| will diminish the - // gain from processing |length| in parallel, while for the former, - // the total expense of building separate output buffers and the - // merging post-process is small compared to the gain from - // processing |targets| in parallel. - // - // If |targets.length| << |length|, then Divide-Output-Range seems - // like it *could* win over Divide-Scatter-Vector. (But when is - // |targets.length| << |length| or even |targets.length| < |length|? - // Seems like an odd situation and an uncommon case at best.) - // - // The unanswered question is which strategy performs better when - // |targets.length| approximately equals |length|, especially for - // special cases like collision-free scatters and permutations. - - var targetsLength = std_Math_min(targets.length, self.shape[0]); - - if (targetsLength >>> 0 !== targetsLength) - ThrowError(JSMSG_PAR_ARRAY_BAD_ARG, ".prototype.scatter length"); - - if (length >>> 0 !== length) - ThrowError(JSMSG_PAR_ARRAY_BAD_ARG, ".prototype.scatter length"); - - parallel: for (;;) { // see ParallelArrayBuild() to explain why for(;;) etc - if (ShouldForceSequential()) - break parallel; - if (!TRY_PARALLEL(mode)) - break parallel; - - if (forceDivideScatterVector()) - return parDivideScatterVector(); - else if (forceDivideOutputRange()) - return parDivideOutputRange(); - else if (conflictFunc === undefined && targetsLength < length) - return parDivideOutputRange(); - return parDivideScatterVector(); - } - - // Sequential fallback: - ASSERT_SEQUENTIAL_IS_OK(mode); - return seq(); - - function forceDivideScatterVector() { - return mode && mode.strategy && mode.strategy == "divide-scatter-vector"; - } - - function forceDivideOutputRange() { - return mode && mode.strategy && mode.strategy == "divide-output-range"; - } - - function collide(elem1, elem2) { - if (conflictFunc === undefined) - ThrowError(JSMSG_PAR_ARRAY_SCATTER_CONFLICT); - - return conflictFunc(elem1, elem2); - } - - - function parDivideOutputRange() { - var chunks = ComputeNumChunks(targetsLength); - var numSlices = ForkJoinSlices(); - var checkpoints = NewDenseArray(numSlices); - for (var i = 0; i < numSlices; i++) - UnsafePutElements(checkpoints, i, 0); - - var buffer = NewDenseArray(length); - var conflicts = NewDenseArray(length); - - for (var i = 0; i < length; i++) { - UnsafePutElements(buffer, i, defaultValue); - UnsafePutElements(conflicts, i, false); - } - - ForkJoin(fill, ForkJoinMode(mode)); - return NewParallelArray(ParallelArrayView, [length], buffer, 0); - - function fill(sliceId, numSlices, warmup) { - var indexPos = checkpoints[sliceId]; - var indexEnd = targetsLength; - if (warmup) - indexEnd = std_Math_min(indexEnd, indexPos + CHUNK_SIZE); - - // Range in the output for which we are responsible: - var [outputStart, outputEnd] = ComputeSliceBounds(length, sliceId, numSlices); - - for (; indexPos < indexEnd; indexPos++) { - var x = self.get(indexPos); - var t = checkTarget(indexPos, targets[indexPos]); - if (t < outputStart || t >= outputEnd) - continue; - if (conflicts[t]) - x = collide(x, buffer[t]); - UnsafePutElements(buffer, t, x, conflicts, t, true, checkpoints, sliceId, indexPos + 1); - } - - return indexEnd == targetsLength; - } - - return undefined; - } - - function parDivideScatterVector() { - // Subtle: because we will be mutating the localBuffers and - // conflict arrays in place, we can never replay an entry in the - // target array for fear of inducing a conflict where none existed - // before. Therefore, we must proceed not by chunks but rather by - // individual indices. - var numSlices = ForkJoinSlices(); - var info = ComputeAllSliceBounds(targetsLength, numSlices); - - // FIXME(bug 844890): Use typed arrays here. - var localBuffers = NewDenseArray(numSlices); - for (var i = 0; i < numSlices; i++) - UnsafePutElements(localBuffers, i, NewDenseArray(length)); - var localConflicts = NewDenseArray(numSlices); - for (var i = 0; i < numSlices; i++) { - var conflicts_i = NewDenseArray(length); - for (var j = 0; j < length; j++) - UnsafePutElements(conflicts_i, j, false); - UnsafePutElements(localConflicts, i, conflicts_i); - } - - // Initialize the 0th buffer, which will become the output. For - // the other buffers, we track which parts have been written to - // using the conflict buffer so they do not need to be - // initialized. - var outputBuffer = localBuffers[0]; - for (var i = 0; i < length; i++) - UnsafePutElements(outputBuffer, i, defaultValue); - - ForkJoin(fill, ForkJoinMode(mode)); - mergeBuffers(); - return NewParallelArray(ParallelArrayView, [length], outputBuffer, 0); - - function fill(sliceId, numSlices, warmup) { - var indexPos = info[SLICE_POS(sliceId)]; - var indexEnd = info[SLICE_END(sliceId)]; - if (warmup) - indexEnd = std_Math_min(indexEnd, indexPos + CHUNK_SIZE); - - var localbuffer = localBuffers[sliceId]; - var conflicts = localConflicts[sliceId]; - while (indexPos < indexEnd) { - var x = self.get(indexPos); - var t = checkTarget(indexPos, targets[indexPos]); - if (conflicts[t]) - x = collide(x, localbuffer[t]); - UnsafePutElements(localbuffer, t, x, conflicts, t, true, - info, SLICE_POS(sliceId), ++indexPos); - } - - return indexEnd == info[SLICE_END(sliceId)]; - } - - /** - * Merge buffers 1..NUMSLICES into buffer 0. In principle, we could - * parallelize the merge work as well. But for this first cut, - * just do the merge sequentially. - */ - function mergeBuffers() { - var buffer = localBuffers[0]; - var conflicts = localConflicts[0]; - for (var i = 1; i < numSlices; i++) { - var otherbuffer = localBuffers[i]; - var otherconflicts = localConflicts[i]; - for (var j = 0; j < length; j++) { - if (otherconflicts[j]) { - if (conflicts[j]) { - buffer[j] = collide(otherbuffer[j], buffer[j]); - } else { - buffer[j] = otherbuffer[j]; - conflicts[j] = true; - } - } - } - } - } - - return undefined; - } - - function seq() { - var buffer = NewDenseArray(length); - var conflicts = NewDenseArray(length); - - for (var i = 0; i < length; i++) { - UnsafePutElements(buffer, i, defaultValue); - UnsafePutElements(conflicts, i, false); - } - - for (var i = 0; i < targetsLength; i++) { - var x = self.get(i); - var t = checkTarget(i, targets[i]); - if (conflicts[t]) - x = collide(x, buffer[t]); - - UnsafePutElements(buffer, t, x, conflicts, t, true); - } - - return NewParallelArray(ParallelArrayView, [length], buffer, 0); - } - - function checkTarget(i, t) { - if (TO_INT32(t) !== t) - ThrowError(JSMSG_PAR_ARRAY_SCATTER_BAD_TARGET, i); - - if (t < 0 || t >= length) - ThrowError(JSMSG_PAR_ARRAY_SCATTER_BOUNDS); - - // It's not enough to return t, as -0 | 0 === -0. - return TO_INT32(t); - } - - return undefined; -} - -/** - * The familiar filter() operation applied across the outermost - * dimension. - */ -function ParallelArrayFilter(func, mode) { - // FIXME(bug 844887): Check |this instanceof ParallelArray| - // FIXME(bug 844887): Check |IsCallable(func)| - - var self = this; - var length = self.shape[0]; - - parallel: for (;;) { // see ParallelArrayBuild() to explain why for(;;) etc - if (ShouldForceSequential()) - break parallel; - if (!TRY_PARALLEL(mode)) - break parallel; - - var chunks = ComputeNumChunks(length); - var numSlices = ForkJoinSlices(); - if (chunks < numSlices * 2) - break parallel; - - var info = ComputeAllSliceBounds(chunks, numSlices); - - // Step 1. Compute which items from each slice of the result - // buffer should be preserved. When we're done, we have an array - // |survivors| containing a bitset for each chunk, indicating - // which members of the chunk survived. We also keep an array - // |counts| containing the total number of items that are being - // preserved from within one slice. - // - // FIXME(bug 844890): Use typed arrays here. - var counts = NewDenseArray(numSlices); - for (var i = 0; i < numSlices; i++) - UnsafePutElements(counts, i, 0); - var survivors = NewDenseArray(chunks); - ForkJoin(findSurvivorsInSlice, ForkJoinMode(mode)); - - // Step 2. Compress the slices into one contiguous set. - var count = 0; - for (var i = 0; i < numSlices; i++) - count += counts[i]; - var buffer = NewDenseArray(count); - if (count > 0) - ForkJoin(copySurvivorsInSlice, ForkJoinMode(mode)); - - return NewParallelArray(ParallelArrayView, [count], buffer, 0); - } - - // Sequential fallback: - ASSERT_SEQUENTIAL_IS_OK(mode); - var buffer = []; - for (var i = 0; i < length; i++) { - var elem = self.get(i); - if (func(elem, i, self)) - ARRAY_PUSH(buffer, elem); - } - return NewParallelArray(ParallelArrayView, [buffer.length], buffer, 0); - - /** - * As described above, our goal is to determine which items we - * will preserve from a given slice. We do this one chunk at a - * time. When we finish a chunk, we record our current count and - * the next chunk sliceId, lest we should bail. - */ - function findSurvivorsInSlice(sliceId, numSlices, warmup) { - var chunkPos = info[SLICE_POS(sliceId)]; - var chunkEnd = info[SLICE_END(sliceId)]; - - if (warmup && chunkEnd > chunkPos) - chunkEnd = chunkPos + 1; - - var count = counts[sliceId]; - while (chunkPos < chunkEnd) { - var indexStart = chunkPos << CHUNK_SHIFT; - var indexEnd = std_Math_min(indexStart + CHUNK_SIZE, length); - var chunkBits = 0; - - for (var bit = 0; indexStart + bit < indexEnd; bit++) { - var keep = !!func(self.get(indexStart + bit), indexStart + bit, self); - chunkBits |= keep << bit; - count += keep; - } - - UnsafePutElements(survivors, chunkPos, chunkBits, - counts, sliceId, count, - info, SLICE_POS(sliceId), ++chunkPos); - } - - return chunkEnd == info[SLICE_END(sliceId)]; - } - - function copySurvivorsInSlice(sliceId, numSlices, warmup) { - // Copies the survivors from this slice into the correct position. - // Note that this is an idempotent operation that does not invoke - // user code. Therefore, we don't expect bailouts and make an - // effort to proceed chunk by chunk or avoid duplicating work. - - // Total up the items preserved by previous slices. - var count = 0; - if (sliceId > 0) { // FIXME(#819219)---work around a bug in Ion's range checks - for (var i = 0; i < sliceId; i++) - count += counts[i]; - } - - // Compute the final index we expect to write. - var total = count + counts[sliceId]; - if (count == total) - return true; - - // Iterate over the chunks assigned to us. Read the bitset for - // each chunk. Copy values where a 1 appears until we have - // written all the values that we expect to. We can just iterate - // from 0...CHUNK_SIZE without fear of a truncated final chunk - // because we are already checking for when count==total. - var chunkStart = info[SLICE_START(sliceId)]; - var chunkEnd = info[SLICE_END(sliceId)]; - for (var chunk = chunkStart; chunk < chunkEnd; chunk++) { - var chunkBits = survivors[chunk]; - if (!chunkBits) - continue; - - var indexStart = chunk << CHUNK_SHIFT; - for (var i = 0; i < CHUNK_SIZE; i++) { - if (chunkBits & (1 << i)) { - UnsafePutElements(buffer, count++, self.get(indexStart + i)); - if (count == total) - break; - } - } - } - - return true; - } - - return undefined; -} - -/** - * Divides the outermost dimension into two dimensions. Does not copy - * or affect the underlying data, just how it is divided amongst - * dimensions. So if we had a vector with shape [N, ...] and you - * partition with amount=4, you get a [N/4, 4, ...] vector. Note that - * N must be evenly divisible by 4 in that case. - */ -function ParallelArrayPartition(amount) { - if (amount >>> 0 !== amount) - ThrowError(JSMSG_PAR_ARRAY_BAD_ARG, ""); - - var length = this.shape[0]; - var partitions = (length / amount) | 0; - - if (partitions * amount !== length) - ThrowError(JSMSG_PAR_ARRAY_BAD_PARTITION); - - var shape = [partitions, amount]; - for (var i = 1; i < this.shape.length; i++) - ARRAY_PUSH(shape, this.shape[i]); - return NewParallelArray(ParallelArrayView, shape, this.buffer, this.offset); -} - -/** - * Collapses two outermost dimensions into one. So if you had - * a [X, Y, ...] vector, you get a [X*Y, ...] vector. - */ -function ParallelArrayFlatten() { - if (this.shape.length < 2) - ThrowError(JSMSG_PAR_ARRAY_ALREADY_FLAT); - - var shape = [this.shape[0] * this.shape[1]]; - for (var i = 2; i < this.shape.length; i++) - ARRAY_PUSH(shape, this.shape[i]); - return NewParallelArray(ParallelArrayView, shape, this.buffer, this.offset); -} - -// -// Accessors and utilities. -// - -/** - * Specialized variant of get() for one-dimensional case - */ -function ParallelArrayGet1(i) { - if (i === undefined) - return undefined; - return this.buffer[this.offset + i]; -} - -/** - * Specialized variant of get() for two-dimensional case - */ -function ParallelArrayGet2(x, y) { - var xDimension = this.shape[0]; - var yDimension = this.shape[1]; - if (x === undefined) - return undefined; - if (x >= xDimension) - return undefined; - if (y === undefined) - return NewParallelArray(ParallelArrayView, [yDimension], this.buffer, this.offset + x * yDimension); - if (y >= yDimension) - return undefined; - var offset = y + x * yDimension; - return this.buffer[this.offset + offset]; -} - -/** - * Specialized variant of get() for three-dimensional case - */ -function ParallelArrayGet3(x, y, z) { - var xDimension = this.shape[0]; - var yDimension = this.shape[1]; - var zDimension = this.shape[2]; - if (x === undefined) - return undefined; - if (x >= xDimension) - return undefined; - if (y === undefined) - return NewParallelArray(ParallelArrayView, [yDimension, zDimension], - this.buffer, this.offset + x * yDimension * zDimension); - if (y >= yDimension) - return undefined; - if (z === undefined) - return NewParallelArray(ParallelArrayView, [zDimension], - this.buffer, this.offset + y * zDimension + x * yDimension * zDimension); - if (z >= zDimension) - return undefined; - var offset = z + y*zDimension + x * yDimension * zDimension; - return this.buffer[this.offset + offset]; -} - -/** - * Generalized version of get() for N-dimensional case - */ -function ParallelArrayGetN(...coords) { - if (coords.length == 0) - return undefined; - - var products = ComputeProducts(this.shape); - - // Compute the offset of the given coordinates. Each index is - // multipled by its corresponding entry in the |products| - // array, counting in reverse. So if |coords| is [a,b,c,d], - // then you get |a*BCD + b*CD + c*D + d|. - var offset = this.offset; - var sDimensionality = this.shape.length; - var cDimensionality = coords.length; - for (var i = 0; i < cDimensionality; i++) { - if (coords[i] >= this.shape[i]) - return undefined; - offset += coords[i] * products[sDimensionality - i - 1]; - } - - if (cDimensionality < sDimensionality) { - var shape = callFunction(std_Array_slice, this.shape, cDimensionality); - return NewParallelArray(ParallelArrayView, shape, this.buffer, offset); - } - return this.buffer[offset]; -} - -/** The length property yields the outermost dimension */ -function ParallelArrayLength() { - return this.shape[0]; -} - -function ParallelArrayToString() { - var l = this.length; - if (l == 0) - return ""; - - var open, close; - if (this.shape.length > 1) { - open = "<"; - close = ">"; - } else { - open = close = ""; - } - - var result = ""; - for (var i = 0; i < l - 1; i++) { - result += open + String(this.get(i)) + close; - result += ","; - } - result += open + String(this.get(l - 1)) + close; - return result; -} - -/** - * Internal debugging tool: checks that the given `mode` permits - * sequential execution - */ -function AssertSequentialIsOK(mode) { - if (mode && mode.mode && mode.mode !== "seq" && ParallelTestsShouldPass()) - ThrowError(JSMSG_WRONG_VALUE, "parallel execution", "sequential was forced"); -} - -function ForkJoinMode(mode) { - // WARNING: this must match the enum ForkJoinMode in ForkJoin.cpp - if (!mode || !mode.mode) { - return 0; - } else if (mode.mode === "compile") { - return 1; - } else if (mode.mode === "par") { - return 2; - } else if (mode.mode === "recover") { - return 3; - } else if (mode.mode === "bailout") { - return 4; - } - ThrowError(JSMSG_PAR_ARRAY_BAD_ARG, ""); - return undefined; -} - -/* - * Mark the main operations as clone-at-callsite for better precision. - * This is slightly overkill, as all that we really need is to - * specialize to the receiver and the elemental function, but in - * practice this is likely not so different, since element functions - * are often used in exactly one place. - */ -SetScriptHints(ParallelArrayConstructEmpty, { cloneAtCallsite: true }); -SetScriptHints(ParallelArrayConstructFromArray, { cloneAtCallsite: true }); -SetScriptHints(ParallelArrayConstructFromFunction, { cloneAtCallsite: true }); -SetScriptHints(ParallelArrayConstructFromFunctionMode, { cloneAtCallsite: true }); -SetScriptHints(ParallelArrayConstructFromComprehension, { cloneAtCallsite: true }); -SetScriptHints(ParallelArrayView, { cloneAtCallsite: true }); -SetScriptHints(ParallelArrayBuild, { cloneAtCallsite: true }); -SetScriptHints(ParallelArrayMap, { cloneAtCallsite: true }); -SetScriptHints(ParallelArrayReduce, { cloneAtCallsite: true }); -SetScriptHints(ParallelArrayScan, { cloneAtCallsite: true }); -SetScriptHints(ParallelArrayScatter, { cloneAtCallsite: true }); -SetScriptHints(ParallelArrayFilter, { cloneAtCallsite: true }); - -/* - * Mark the common getters as clone-at-callsite and inline. This is - * overkill as we should only clone per receiver, but we have no - * mechanism for that right now. Bug 804767 might permit another - * alternative by specializing the inlined gets. - */ -SetScriptHints(ParallelArrayGet1, { cloneAtCallsite: true, inline: true }); -SetScriptHints(ParallelArrayGet2, { cloneAtCallsite: true, inline: true }); -SetScriptHints(ParallelArrayGet3, { cloneAtCallsite: true, inline: true }); diff --git a/js/src/builtin/Utilities.js b/js/src/builtin/Utilities.js index 77873cf58acf..8dceb836a257 100644 --- a/js/src/builtin/Utilities.js +++ b/js/src/builtin/Utilities.js @@ -161,3 +161,33 @@ function IsObject(v) { typeof v === "function" || (typeof v === "undefined" && v !== undefined); } + +#ifdef ENABLE_PARALLEL_JS + +/** + * Internal debugging tool: checks that the given `mode` permits + * sequential execution + */ +function AssertSequentialIsOK(mode) { + if (mode && mode.mode && mode.mode !== "seq" && ParallelTestsShouldPass()) + ThrowError(JSMSG_WRONG_VALUE, "parallel execution", "sequential was forced"); +} + +function ForkJoinMode(mode) { + // WARNING: this must match the enum ForkJoinMode in ForkJoin.cpp + if (!mode || !mode.mode) { + return 0; + } else if (mode.mode === "compile") { + return 1; + } else if (mode.mode === "par") { + return 2; + } else if (mode.mode === "recover") { + return 3; + } else if (mode.mode === "bailout") { + return 4; + } + ThrowError(JSMSG_PAR_ARRAY_BAD_ARG); + return undefined; +} + +#endif diff --git a/js/src/jit-test/lib/parallelarray-helpers.js b/js/src/jit-test/lib/parallelarray-helpers.js index a6afc03b07ef..9fdc4cea0b1f 100644 --- a/js/src/jit-test/lib/parallelarray-helpers.js +++ b/js/src/jit-test/lib/parallelarray-helpers.js @@ -79,13 +79,7 @@ function assertAlmostEq(v1, v2) { } function assertStructuralEq(e1, e2) { - if (e1 instanceof ParallelArray && e2 instanceof ParallelArray) { - assertEqParallelArray(e1, e2); - } else if (e1 instanceof Array && e2 instanceof ParallelArray) { - assertEqParallelArrayArray(e2, e1); - } else if (e1 instanceof ParallelArray && e2 instanceof Array) { - assertEqParallelArrayArray(e1, e2); - } else if (e1 instanceof Array && e2 instanceof Array) { + if (e1 instanceof Array && e2 instanceof Array) { assertEqArray(e1, e2); } else if (e1 instanceof Object && e2 instanceof Object) { assertEq(e1.__proto__, e2.__proto__); @@ -100,19 +94,6 @@ function assertStructuralEq(e1, e2) { } } -function assertEqParallelArrayArray(a, b) { - assertEq(a.shape.length, 1); - assertEq(a.length, b.length); - for (var i = 0, l = a.length; i < l; i++) { - try { - assertStructuralEq(a.get(i), b[i]); - } catch (e) { - print("...in index ", i, " of ", l); - throw e; - } - } -} - function assertEqArray(a, b) { assertEq(a.length, b.length); for (var i = 0, l = a.length; i < l; i++) { @@ -125,37 +106,6 @@ function assertEqArray(a, b) { } } -function assertEqParallelArray(a, b) { - assertEq(a instanceof ParallelArray, true); - assertEq(b instanceof ParallelArray, true); - - var shape = a.shape; - assertEqArray(shape, b.shape); - - function bump(indices) { - var d = indices.length - 1; - while (d >= 0) { - if (++indices[d] < shape[d]) - break; - indices[d] = 0; - d--; - } - return d >= 0; - } - - var iv = shape.map(function () { return 0; }); - do { - try { - var e1 = a.get.apply(a, iv); - var e2 = b.get.apply(b, iv); - assertStructuralEq(e1, e2); - } catch (e) { - print("...in indices ", iv, " of ", shape); - throw e; - } - } while (bump(iv)); -} - // Checks that whenever we execute this in parallel mode, // it bails out. `opFunction` should be a closure that takes a // mode parameter and performs some parallel array operation. @@ -164,10 +114,10 @@ function assertEqParallelArray(a, b) { // Here is an example of the expected usage: // // assertParallelExecWillBail(function(m) { -// new ParallelArray(..., m) +// Array.buildPar(..., m) // }); // -// where the `new ParallelArray(...)` is a stand-in +// where the `Array.buildPar(...)` is a stand-in // for some parallel array operation. function assertParallelExecWillBail(opFunction) { opFunction({mode:"compile"}); // get the script compiled @@ -236,30 +186,6 @@ function assertArraySeqParResultsEq(arr, op, func, cmpFunc) { function (r) { cmpFunc(expected, r); }); } -// Compares a ParallelArray function against its equivalent on the -// `Array` prototype. `func` should be the closure to provide as -// argument. For example: -// -// compareAgainstArray([1, 2, 3], "map", i => i + 1) -// -// would check that `[1, 2, 3].map(i => i+1)` and `new -// ParallelArray([1, 2, 3]).map(i => i+1)` yield the same result. -// -// Based on `assertParallelExecSucceeds` -function compareAgainstArray(jsarray, opname, func, cmpFunction) { - if (!cmpFunction) - cmpFunction = assertStructuralEq; - var expected = jsarray[opname].apply(jsarray, [func]); - var parray = new ParallelArray(jsarray); - assertParallelExecSucceeds( - function(m) { - return parray[opname].apply(parray, [func, m]); - }, - function(r) { - cmpFunction(expected, r); - }); -} - // Similar to `compareAgainstArray`, but for the `scan` method which // does not appear on array. function testArrayScanPar(jsarray, func, cmpFunction) { @@ -281,24 +207,6 @@ function testArrayScanPar(jsarray, func, cmpFunction) { }); } -// Similar to `compareAgainstArray`, but for the `scatter` method. -// In this case, because scatter is so complex, we do not attempt -// to compute the expected result and instead simply invoke -// `cmpFunction(r)` with the result `r` of the scatter operation. -function testScatter(opFunction, cmpFunction) { - var strategies = ["divide-scatter-version", "divide-output-range"]; - for (var i in strategies) { - assertParallelExecSucceeds( - function(m) { - var m1 = {mode: m.mode, - strategy: strategies[i]}; - print(JSON.stringify(m1)); - return opFunction(m1); - }, - cmpFunction); - } -} - // Checks that `opFunction`, when run with each of the modes // in `modes`, returns the same value each time. function assertParallelModesCommute(modes, opFunction) { diff --git a/js/src/jit-test/tests/auto-regress/bug755564.js b/js/src/jit-test/tests/auto-regress/bug755564.js deleted file mode 100644 index e692508ce1b1..000000000000 --- a/js/src/jit-test/tests/auto-regress/bug755564.js +++ /dev/null @@ -1,9 +0,0 @@ -// Binary: cache/js-dbg-64-50177d59c0e1-linux -// Flags: -// - -if (getBuildConfiguration().parallelJS) { - var p = new ParallelArray([1,2,3,4,5]); - var r = p.scatter([0,1,0,3,4], 9, function (a,b) { return a+b; }); - assertEq(r.toString( 5 ? r : 0, gc()) ,[4,2,9,4,5].join(",")); -} diff --git a/js/src/jit-test/tests/auto-regress/bug783923.js b/js/src/jit-test/tests/auto-regress/bug783923.js deleted file mode 100644 index 661c77b47972..000000000000 --- a/js/src/jit-test/tests/auto-regress/bug783923.js +++ /dev/null @@ -1,6 +0,0 @@ -// Binary: cache/js-dbg-64-35b8d6ef5d46-linux -// Flags: -// - -if (getBuildConfiguration().parallelJS) - print(ParallelArray()); diff --git a/js/src/jit-test/tests/auto-regress/bug784011.js b/js/src/jit-test/tests/auto-regress/bug784011.js deleted file mode 100644 index e419fcb9f6c6..000000000000 --- a/js/src/jit-test/tests/auto-regress/bug784011.js +++ /dev/null @@ -1,8 +0,0 @@ -// Binary: cache/js-dbg-64-c676b554c7bb-linux -// Flags: -// - -if (getBuildConfiguration().parallelJS) { - var p2 = new ParallelArray([2,2], function(i,j) { return i+j; }); - p2.get({ 0: 1, 1: 0, testGet: 2 }) -} diff --git a/js/src/jit-test/tests/auto-regress/bug786106.js b/js/src/jit-test/tests/auto-regress/bug786106.js deleted file mode 100644 index ab0f84f18080..000000000000 --- a/js/src/jit-test/tests/auto-regress/bug786106.js +++ /dev/null @@ -1,8 +0,0 @@ -// Binary: cache/js-dbg-64-92b9b2840a79-linux -// Flags: -// - -if (getBuildConfiguration().parallelJS) { - var p = new ParallelArray([2, 3,, 4, 5, 6]); - var r = p.scatter([0,1,0,3,4], 9, function (a,b) { return a+b; }); -} diff --git a/js/src/jit-test/tests/auto-regress/bug789107.js b/js/src/jit-test/tests/auto-regress/bug789107.js deleted file mode 100644 index 9998513cd19e..000000000000 --- a/js/src/jit-test/tests/auto-regress/bug789107.js +++ /dev/null @@ -1,7 +0,0 @@ -// Binary: cache/js-dbg-64-5d63594c05a9-linux -// Flags: -// - -if (getBuildConfiguration().parallelJS) { - ParallelArray().watch("shape", function() {}) -} diff --git a/js/src/jit-test/tests/auto-regress/bug791445.js b/js/src/jit-test/tests/auto-regress/bug791445.js deleted file mode 100644 index a7a3a3a10931..000000000000 --- a/js/src/jit-test/tests/auto-regress/bug791445.js +++ /dev/null @@ -1,7 +0,0 @@ -// Binary: cache/js-dbg-64-9fff2012b66c-linux -// Flags: -// - -if (getBuildConfiguration().parallelJS) { - ParallelArray(0, Proxy.createFunction(function(){}, function(){})) -} diff --git a/js/src/jit-test/tests/baseline/bug846072.js b/js/src/jit-test/tests/baseline/bug846072.js deleted file mode 100644 index 368e0f03c6e3..000000000000 --- a/js/src/jit-test/tests/baseline/bug846072.js +++ /dev/null @@ -1,7 +0,0 @@ -// |jit-test| error: TypeError -if (getBuildConfiguration().parallelJS) { - toString = undefined; - if (!(this in ParallelArray)) {} -} else { - throw new TypeError(); -} diff --git a/js/src/jit-test/tests/baseline/bug857579.js b/js/src/jit-test/tests/baseline/bug857579.js deleted file mode 100644 index 117e76c25747..000000000000 --- a/js/src/jit-test/tests/baseline/bug857579.js +++ /dev/null @@ -1,22 +0,0 @@ -// |jit-test| ion-eager; error: TypeError - -function testMonitorIntrinsic() { - var N = 2; - // Make an NxN array of zeros. - var p = new ParallelArray([N,N], function () 0); - // |i| will go out of bounds! - for (var i = 0; i < N+1; i++) { - for (var j = 0; j < 2; j++) { - // When |i| goes out of bounds, we will bail from Ion to BC on an - // 'undefined' result inside parallel array's .get, tripping a type - // barrier on GETINTRINSIC. - p.get(i).get(j); - } - } -} - -if (getBuildConfiguration().parallelJS) { - testMonitorIntrinsic(); -} else { - throw new TypeError(); -} diff --git a/js/src/jit-test/tests/basic/bug815652.js b/js/src/jit-test/tests/basic/bug815652.js deleted file mode 100644 index ad2b9ad86c65..000000000000 --- a/js/src/jit-test/tests/basic/bug815652.js +++ /dev/null @@ -1,12 +0,0 @@ - -gczeal(9, 2) -function testScatterConflict() { - var p = new ParallelArray([1,2,3,4,5]); - var r = p.scatter([0,1,0,3,(0)], 9, function (a,b) { return a+b; }); - function assertEqParallelArray(a, b) - assertEq(a instanceof ParallelArray, true); - assertEqParallelArray(r, new ParallelArray([4,2,(false),4,5])); -} -if (getBuildConfiguration().parallelJS) { - testScatterConflict(); -} diff --git a/js/src/jit-test/tests/parallel/binary-arith-numbers.js b/js/src/jit-test/tests/parallel/binary-arith-numbers.js index 72d3a069f717..cc3a2e5a6397 100644 --- a/js/src/jit-test/tests/parallel/binary-arith-numbers.js +++ b/js/src/jit-test/tests/parallel/binary-arith-numbers.js @@ -18,12 +18,11 @@ function theTest() { jsarray0.map(op); // this version will never actually touch the strings: - var jsarray1 = range(0, 1024).map(i => i % 10); - compareAgainstArray(jsarray1, "map", op); + assertArraySeqParResultsEq(range(0, 1024), "map", function (i) { return i % 10; }); // but if we try against the original we get bailouts: assertParallelExecWillBail(function (mode) { - new ParallelArray(jsarray0).map(op, mode); + jsarray0.mapPar(op, mode); }); } diff --git a/js/src/jit-test/tests/parallel/bug783924.js b/js/src/jit-test/tests/parallel/bug783924.js deleted file mode 100644 index 2ca003d4a0c0..000000000000 --- a/js/src/jit-test/tests/parallel/bug783924.js +++ /dev/null @@ -1,7 +0,0 @@ -function bug783924() { - // Shouldn't throw. - Function("ParallelArray([])")(); -} - -if (getBuildConfiguration().parallelJS) - bug783924(); diff --git a/js/src/jit-test/tests/parallel/bug787282.js b/js/src/jit-test/tests/parallel/bug787282.js deleted file mode 100644 index e8848c618501..000000000000 --- a/js/src/jit-test/tests/parallel/bug787282.js +++ /dev/null @@ -1,16 +0,0 @@ -// |jit-test| error: TypeError - -var protoArr = Proxy.create({}, null); -void (Array.prototype.__proto__ = protoArr); -gczeal(2); -function testCopyBigArray() { - var a = new Array(10000); - for (var cnt = 0; cnt < a.length; cnt+=2) { - var p = new ParallelArray(a); - } -} - -if (getBuildConfiguration().parallelJS) - testCopyBigArray(); -else - throw new TypeError(); diff --git a/js/src/jit-test/tests/parallel/bug853555.js b/js/src/jit-test/tests/parallel/bug853555.js deleted file mode 100644 index 49338c2c3298..000000000000 --- a/js/src/jit-test/tests/parallel/bug853555.js +++ /dev/null @@ -1,12 +0,0 @@ -function test() { - Object.prototype[0] = /a/; - function getterFunction(v) { return "getter"; } - Object.defineProperty(Array.prototype, 1, { get: getterFunction }); - gczeal(4); - var p = new ParallelArray([1,2,3,4,5]); - p.scatter([0,1,0,3,01], 9, function (a,b) { return a+b; }); -} - -if (getBuildConfiguration().parallelJS) - test(); - diff --git a/js/src/jit-test/tests/parallel/bug853573.js b/js/src/jit-test/tests/parallel/bug853573.js deleted file mode 100644 index 8024a4269416..000000000000 --- a/js/src/jit-test/tests/parallel/bug853573.js +++ /dev/null @@ -1,17 +0,0 @@ -if (getBuildConfiguration().parallelJS) { - var p = Proxy.create({ - has : function(id) {} - }); - Object.prototype.__proto__ = p; - var pa0 = new ParallelArray(range(0, 256)); - var pa1 = new ParallelArray(256, function (x) { - return pa0.map(function(y) {}); - }); -} - -function range(n, m) { - var result = []; - for (var i = n; i < m; i++) - result.push(i); - return result; -} diff --git a/js/src/jit-test/tests/parallel/bug854021.js b/js/src/jit-test/tests/parallel/bug854021.js deleted file mode 100644 index 6b73e0bc9600..000000000000 --- a/js/src/jit-test/tests/parallel/bug854021.js +++ /dev/null @@ -1,3 +0,0 @@ -// Don't crash. -if (getBuildConfiguration().parallelJS) - ParallelArray(7, function ([y]) {}) diff --git a/js/src/jit-test/tests/parallel/bug854050.js b/js/src/jit-test/tests/parallel/bug854050.js deleted file mode 100644 index a839b3634b7a..000000000000 --- a/js/src/jit-test/tests/parallel/bug854050.js +++ /dev/null @@ -1,10 +0,0 @@ -function bug854050() { - // Shouldn't crash. Tests Ion's argumentsRectifier loading the right - // IonScript depending on execution mode. - for (z = 0; z < 1; z++) - function x(b, x) {} - ParallelArray(47, x); -} - -if (getBuildConfiguration().parallelJS) - bug854050(); diff --git a/js/src/jit-test/tests/parallel/bug854381.js b/js/src/jit-test/tests/parallel/bug854381.js deleted file mode 100644 index 99c0b1239820..000000000000 --- a/js/src/jit-test/tests/parallel/bug854381.js +++ /dev/null @@ -1,20 +0,0 @@ -function bug854381() { - // Don't crash. - function toString(r) { - var l = 2; - var result = ""; - for (var i = 0; i < l; i++) - result += r.get(i); - return result; - } - - var p = new ParallelArray(['x', 'x']); - var r = new ParallelArray([toString(p), 42]); - - gc(); - print(toString(r)); -} - -if (getBuildConfiguration().parallelJS) { - bug854381(); -} diff --git a/js/src/jit-test/tests/parallel/bug857846.js b/js/src/jit-test/tests/parallel/bug857846.js deleted file mode 100644 index 19ef69e25cd1..000000000000 --- a/js/src/jit-test/tests/parallel/bug857846.js +++ /dev/null @@ -1,9 +0,0 @@ -function testNegativeZeroScatter() { - // Don't crash. - var p = new ParallelArray([0]); - var r = p.scatter([-0], 0, undefined, 1); -} - -if (getBuildConfiguration().parallelJS) { - testNegativeZeroScatter(); -} diff --git a/js/src/jit-test/tests/parallel/bug858077.js b/js/src/jit-test/tests/parallel/bug858077.js deleted file mode 100644 index 4af7e467fb34..000000000000 --- a/js/src/jit-test/tests/parallel/bug858077.js +++ /dev/null @@ -1,5 +0,0 @@ -if (getBuildConfiguration().parallelJS) { - var a = new ParallelArray([1,2,3,4]); - for (var i = 0; i < 9 && i > -1000; i-- ) - a[i] += [0]; -} diff --git a/js/src/jit-test/tests/parallel/bug858582.js b/js/src/jit-test/tests/parallel/bug858582.js deleted file mode 100644 index da3f9df98a32..000000000000 --- a/js/src/jit-test/tests/parallel/bug858582.js +++ /dev/null @@ -1,68 +0,0 @@ -// |jit-test| error: TypeError -// Don't crash. -if (getBuildConfiguration().parallelJS) { -gczeal(2); -evaluate("\ -function assertAlmostEq(v1, v2) {\ - print(\"v2 = \" + v2);\ - print(\"% diff = \" + percent);\ -function assertStructuralEq(e1, e2) {}\ -function assertEqParallelArrayArray(a, b) {\ - try {} catch (e) {\ - print(\"...in index \", i, \" of \", l);\ - }\ -}\ - function assertEqArray(a, b) {\ - try {} catch (e) {}\ -}\ -function assertEqParallelArray(a, b) {\ - var shape = a.shape;\ - function bump(indices) {\ - var iv = shape.map(function () { return 0; });\ - print(\"...in indices \", iv, \" of \", shape);\ - }\ - } while (bump(iv));\ -}\ -function assertParallelArrayModesEq(modes, acc, opFunction, cmpFunction) {\ - modes.forEach(function (mode) {\ - var result = opFunction({ mode: mode, expect: \"success\" });\ - cmpFunction(acc, result);\ - });\ -function assertParallelArrayModesCommute(modes, opFunction) {\ - var acc = opFunction({ mode: modes[0], expect: \"success\" });\ -}\ -function comparePerformance(opts) {\ - print(\"Option \" + opts[i].name + \" took \" + diff + \"ms\");\ - print(\"Option \" + opts[i].name + \" relative to option \" +\ - opts[0].name + \": \" + (rel|0) + \"%\");\ - }\ -}\ -function compareAgainstArray(jsarray, opname, func, cmpFunction) {\ - var expected = jsarray[opname].apply(jsarray, [func]);\ - var parray = new ParallelArray(jsarray);\ - assertParallelArrayModesEq([\"seq\", \"par\", \"par\"], expected, function(m) {\ - var result = parray[opname].apply(parray, [func, m]);\ - }, cmpFunction);\ -}\ -function testFilter(jsarray, func, cmpFunction) {}\ -", { noScriptRval : true }); - compareAgainstArray([ - "a", - "b", - ('captures: 1,1; RegExp.leftContext: ""; RegExp.rightContext: "123456"'), - "d", "e", - "f", "g", "h", - "i", "j", "k", "l", - "m", "n", "o", "p", - "q", "r", "s", "t", - (.6 ), "v", "w", "x", "y", "z" - ], "map", function(e) { - return e != "u" - && - (function b ( ) { - } ) - != "x"; - }); -} else { - throw new TypeError(); -} diff --git a/js/src/jit-test/tests/parallel/bug890465.js b/js/src/jit-test/tests/parallel/bug890465.js deleted file mode 100644 index e47a157c0102..000000000000 --- a/js/src/jit-test/tests/parallel/bug890465.js +++ /dev/null @@ -1,6 +0,0 @@ -if (getBuildConfiguration().parallelJS) { - x = Uint8ClampedArray() - ParallelArray([320], function() { - return x[8] - }) -} diff --git a/js/src/jit-test/tests/parallel/bug894782.js b/js/src/jit-test/tests/parallel/bug894782.js deleted file mode 100644 index f25875b28320..000000000000 --- a/js/src/jit-test/tests/parallel/bug894782.js +++ /dev/null @@ -1,6 +0,0 @@ -// Don't crash - -if (getBuildConfiguration().parallelJS) { - print(ParallelArray()) - String(Object.create(ParallelArray(8077, function() {}))) -} diff --git a/js/src/jit-test/tests/parallel/bug895782.js b/js/src/jit-test/tests/parallel/bug895782.js deleted file mode 100644 index f8538fa6a918..000000000000 --- a/js/src/jit-test/tests/parallel/bug895782.js +++ /dev/null @@ -1,12 +0,0 @@ -// Don't crash - -if (getBuildConfiguration().parallelJS) { - Object.defineProperty(this, "y", { - get: function() { - return Object.create(x) - } - }) - x = ParallelArray([1142], function() {}) - x = x.partition(2) - y + y -} diff --git a/js/src/jit-test/tests/parallel/bug908939.js b/js/src/jit-test/tests/parallel/bug908939.js deleted file mode 100644 index d70a27858ad7..000000000000 --- a/js/src/jit-test/tests/parallel/bug908939.js +++ /dev/null @@ -1,13 +0,0 @@ -if (getBuildConfiguration().parallelJS) { - x = ParallelArray([9937], function() { - return /x/ - }) - Object.defineProperty(this, "y", { - get: function() { - return Object.create(x) - } - }) - y + x - x = x.scatter([]); - + y -} diff --git a/js/src/jit-test/tests/parallel/bug909599.js b/js/src/jit-test/tests/parallel/bug909599.js deleted file mode 100644 index 1dee7caaf61c..000000000000 --- a/js/src/jit-test/tests/parallel/bug909599.js +++ /dev/null @@ -1,6 +0,0 @@ -if (getBuildConfiguration().parallelJS) { - x = Math.tan(5) + [] - ParallelArray([764], (function() {})).filter(function(e) { - return e << x - }) -} diff --git a/js/src/jit-test/tests/parallel/compare-values.js b/js/src/jit-test/tests/parallel/compare-values.js index 398df363b37a..0da90f4fd5e0 100644 --- a/js/src/jit-test/tests/parallel/compare-values.js +++ b/js/src/jit-test/tests/parallel/compare-values.js @@ -15,29 +15,29 @@ function theTest() { return doubles[i] == e; } print("doubles"); - compareAgainstArray(doubles, "map", looselyCompareToDoubles) + assertArraySeqParResultsEq(doubles, "map", looselyCompareToDoubles); print("bools"); - compareAgainstArray(bools, "map", looselyCompareToDoubles) + assertArraySeqParResultsEq(bools, "map", looselyCompareToDoubles); // ion bails out when converting a string to a double right now, // so par exec cannot proceed print("strings"); assertParallelExecWillBail(function (mode) { - new ParallelArray(strings).map(looselyCompareToDoubles, mode) + strings.mapPar(looselyCompareToDoubles, mode) }); print("ints"); - compareAgainstArray(ints, "map", looselyCompareToDoubles) + assertArraySeqParResultsEq(ints, "map", looselyCompareToDoubles); function strictlyCompareToDoubles(e, i) { return doubles[i] === e; } print("doubles, strict"); - compareAgainstArray(doubles, "map", strictlyCompareToDoubles) + assertArraySeqParResultsEq(doubles, "map", strictlyCompareToDoubles); print("bools, strict"); - compareAgainstArray(bools, "map", strictlyCompareToDoubles) + assertArraySeqParResultsEq(bools, "map", strictlyCompareToDoubles); print("strings, strict"); - compareAgainstArray(strings, "map", strictlyCompareToDoubles) + assertArraySeqParResultsEq(strings, "map", strictlyCompareToDoubles); print("ints, strict"); - compareAgainstArray(ints, "map", strictlyCompareToDoubles) + assertArraySeqParResultsEq(ints, "map", strictlyCompareToDoubles); } if (getBuildConfiguration().parallelJS) diff --git a/js/src/jit-test/tests/parallel/comprehension-2.js b/js/src/jit-test/tests/parallel/comprehension-2.js deleted file mode 100644 index 4c61faffb271..000000000000 --- a/js/src/jit-test/tests/parallel/comprehension-2.js +++ /dev/null @@ -1,13 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); -load(libdir + "eqArrayHelper.js"); - -function buildMultidim() { - // 2D comprehension - var p = new ParallelArray([2,2], function (i,j) { return i + j; }); - var a = new ParallelArray([0,1,1,2]).partition(2); - assertEqArray(p.shape, [2,2]); - assertEqParallelArray(p, a); -} - -if (getBuildConfiguration().parallelJS) - buildMultidim(); diff --git a/js/src/jit-test/tests/parallel/comprehension-fn-args.js b/js/src/jit-test/tests/parallel/comprehension-fn-args.js deleted file mode 100644 index bb2004d6f46d..000000000000 --- a/js/src/jit-test/tests/parallel/comprehension-fn-args.js +++ /dev/null @@ -1,16 +0,0 @@ - -function buildComprehension() { - // Test kernel function arguments - var shape = []; - for (var i = 0; i < 8; i++) { - shape.push(i+1); - var p = new ParallelArray(shape, function () { - assertEq(arguments.length, shape.length); - for (var j = 0; j < shape.length; j++) - assertEq(arguments[j] >= 0 && arguments[j] < shape[j], true); - }); - } -} - -if (getBuildConfiguration().parallelJS) - buildComprehension(); diff --git a/js/src/jit-test/tests/parallel/comprehension-scale.js b/js/src/jit-test/tests/parallel/comprehension-scale.js deleted file mode 100644 index d10d01ea8552..000000000000 --- a/js/src/jit-test/tests/parallel/comprehension-scale.js +++ /dev/null @@ -1,22 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function buildComprehension() { - var H = 96; - var W = 96; - var d = 4; - // 3D 96x96x4 texture-like PA - var p = new ParallelArray([H,W,d], function (i,j,k) { return i + j + k; }); - var a = []; - for (var i = 0; i < H; i++) { - for (var j = 0; j < W; j++) { - for (var k = 0; k < d; k++) { - a.push(i+j+k); - } - } - } - var p2 = new ParallelArray(a).partition(d).partition(W); - assertEqParallelArray(p, p2); -} - -if (getBuildConfiguration().parallelJS) - buildComprehension(); diff --git a/js/src/jit-test/tests/parallel/constructor-1.js b/js/src/jit-test/tests/parallel/constructor-1.js deleted file mode 100644 index d5d5160344ad..000000000000 --- a/js/src/jit-test/tests/parallel/constructor-1.js +++ /dev/null @@ -1,15 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function buildSimple() { - // Simple constructor - var a = [1,2,3,4,5]; - var p = new ParallelArray(a); - assertEqParallelArrayArray(p, a); - var a2 = a.slice(); - a[0] = 9; - // No sharing - assertEqParallelArrayArray(p, a2); -} - -if (getBuildConfiguration().parallelJS) - buildSimple(); diff --git a/js/src/jit-test/tests/parallel/constructor-2.js b/js/src/jit-test/tests/parallel/constructor-2.js deleted file mode 100644 index 38a69b51f2cf..000000000000 --- a/js/src/jit-test/tests/parallel/constructor-2.js +++ /dev/null @@ -1,15 +0,0 @@ - -function buildWithHoles() { - // Test holes - var a = new Array(5); - for (var cnt = 0; cnt < a.length; cnt+=2) { - a[cnt] = cnt; - } - var b = [0,1,2,3,4]; - var p = new ParallelArray(a); - assertEq(Object.keys(p).join(","), Object.keys(b).join(",")); -} - -// FIXME(bug 844882) self-hosted object not array-like, exposes internal properties -// if (getBuildConfiguration().parallelJS) -// buildWithHoles(); diff --git a/js/src/jit-test/tests/parallel/constructor-3.js b/js/src/jit-test/tests/parallel/constructor-3.js deleted file mode 100644 index 6fc501650a36..000000000000 --- a/js/src/jit-test/tests/parallel/constructor-3.js +++ /dev/null @@ -1,14 +0,0 @@ -function bracket(s) { - return "<" + s + ">"; -} - -function buildArrayLike() { - // Construct copying from array-like - var a = { 0: 1, 1: 2, 2: 3, 3: 4, length: 4 }; - var p = new ParallelArray(a); - var e = Array.prototype.join.call(a, ","); - assertEq(p.toString(), e); -} - -if (getBuildConfiguration().parallelJS) - buildArrayLike(); diff --git a/js/src/jit-test/tests/parallel/constructor-4.js b/js/src/jit-test/tests/parallel/constructor-4.js deleted file mode 100644 index 7b435c2eb9df..000000000000 --- a/js/src/jit-test/tests/parallel/constructor-4.js +++ /dev/null @@ -1,16 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function buildPA() { - // Construct copying from PA - var p1 = new ParallelArray([1,2,3,4]); - var p2 = new ParallelArray(p1); - assertEqParallelArray(p1, p2); - - var p1d = new ParallelArray([2,2], function(i,j) { return i + j; }); - var p2d = new ParallelArray(p1d); - assertEq(p1d.toString(), p2d.toString()); -} - -// FIXME(bug 844882) self-hosted object not array-like, exposes internal properties -// if (getBuildConfiguration().parallelJS) -// buildPA(); diff --git a/js/src/jit-test/tests/parallel/constructor-5.js b/js/src/jit-test/tests/parallel/constructor-5.js deleted file mode 100644 index 929c80fdabb4..000000000000 --- a/js/src/jit-test/tests/parallel/constructor-5.js +++ /dev/null @@ -1,18 +0,0 @@ -// |jit-test| slow; -// ^^ This test is slow when --no-ion is used, specifically, -// as part of TBPL. - -function testCopyBigArray() { - // Don't crash - var a = new Array(1000 * 1000); - var p = new ParallelArray(a); - // Holes! - var a = new Array(10000); - for (var cnt = 0; cnt < a.length; cnt+=2) { - a[cnt] = cnt; - var p = new ParallelArray(a); - } -} - -if (getBuildConfiguration().parallelJS) - testCopyBigArray(); diff --git a/js/src/jit-test/tests/parallel/constructor-throws.js b/js/src/jit-test/tests/parallel/constructor-throws.js deleted file mode 100644 index 20e760a8c0d8..000000000000 --- a/js/src/jit-test/tests/parallel/constructor-throws.js +++ /dev/null @@ -1,10 +0,0 @@ -load(libdir + "asserts.js"); - -function testThrows() { - assertThrowsInstanceOf(function () { - new ParallelArray({ length: 0xffffffff + 1 }); - }, RangeError); -} - -if (getBuildConfiguration().parallelJS) - testThrows(); diff --git a/js/src/jit-test/tests/parallel/element-1.js b/js/src/jit-test/tests/parallel/element-1.js deleted file mode 100644 index a26843aed7fc..000000000000 --- a/js/src/jit-test/tests/parallel/element-1.js +++ /dev/null @@ -1,14 +0,0 @@ -function testElement() { - // Test getting element from 1D - var a = [1,{},"a",false] - var p = new ParallelArray(a); - for (var i = 0; i < a.length; i++) { - assertEq(p.get(i), p.get(i)); - assertEq(p.get(i), a[i]); - } - // Test out of bounds - assertEq(p.get(42), undefined); -} - -if (getBuildConfiguration().parallelJS) - testElement(); diff --git a/js/src/jit-test/tests/parallel/element-2.js b/js/src/jit-test/tests/parallel/element-2.js deleted file mode 100644 index bb23b95f42eb..000000000000 --- a/js/src/jit-test/tests/parallel/element-2.js +++ /dev/null @@ -1,26 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testElement() { - // Test getting element from higher dimension - var p = new ParallelArray([2,2,2], function () { return 0; }); - var p0 = new ParallelArray([2,2], function () { return 0; }); - print("0"); - assertEqParallelArray(p.get(0), p0); - - // Should create new wrapper - print("1"); - assertEq(p.get(0) !== p.get(0), true); - - // Test out of bounds - print("2"); - assertEq(p.get(42), undefined); - - // Test getting element from 0-lengthed higher dimension - var pp = new ParallelArray([0,0], function() { return 0; }); - assertEq(pp.get(2), undefined); - var pp2 = new ParallelArray([2,0], function() { return 0; }); - assertEqParallelArray(pp2.get(0), new ParallelArray()); -} - -if (getBuildConfiguration().parallelJS) - testElement(); diff --git a/js/src/jit-test/tests/parallel/element-3.js b/js/src/jit-test/tests/parallel/element-3.js deleted file mode 100644 index 7872d9d001c8..000000000000 --- a/js/src/jit-test/tests/parallel/element-3.js +++ /dev/null @@ -1,12 +0,0 @@ -function testElement() { - var p = new ParallelArray([9]); - var desc = Object.getOwnPropertyDescriptor(p, "0"); - assertEq(desc.enumerable, true); - assertEq(desc.configurable, false); - assertEq(desc.writable, false); - assertEq(desc.value, 9); -} - -// FIXME(bug 844882) self-hosted object not array-like, exposes internal properties -// if (getBuildConfiguration().parallelJS) -// testElement(); diff --git a/js/src/jit-test/tests/parallel/flatten-1.js b/js/src/jit-test/tests/parallel/flatten-1.js deleted file mode 100644 index d7d68923e802..000000000000 --- a/js/src/jit-test/tests/parallel/flatten-1.js +++ /dev/null @@ -1,14 +0,0 @@ -load(libdir + "eqArrayHelper.js"); - -function testFlatten() { - var shape = [5]; - for (var i = 0; i < 7; i++) { - shape.push(i+1); - var p = new ParallelArray(shape, function(i,j) { return i+j; }); - var flatShape = ([shape[0] * shape[1]]).concat(shape.slice(2)); - assertEqArray(p.flatten().shape, flatShape); - } -} - -if (getBuildConfiguration().parallelJS) - testFlatten(); diff --git a/js/src/jit-test/tests/parallel/flatten-2.js b/js/src/jit-test/tests/parallel/flatten-2.js deleted file mode 100644 index 7a7249b15e68..000000000000 --- a/js/src/jit-test/tests/parallel/flatten-2.js +++ /dev/null @@ -1,10 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testFlatten() { - var p = new ParallelArray([2,2], function(i,j) { return i+j; }); - var p2 = new ParallelArray([0,1,1,2]); - assertEqParallelArray(p.flatten(), p2); -} - -if (getBuildConfiguration().parallelJS) - testFlatten(); diff --git a/js/src/jit-test/tests/parallel/flatten-3.js b/js/src/jit-test/tests/parallel/flatten-3.js deleted file mode 100644 index 82967567c12b..000000000000 --- a/js/src/jit-test/tests/parallel/flatten-3.js +++ /dev/null @@ -1,25 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testFlatten() { - var p0 = new ParallelArray([0,1]); - var p1 = new ParallelArray([2,3]); - var p = new ParallelArray([p0, p1]); - var p2 = new ParallelArray([0,1,2,3]); - assertEqParallelArray(p.flatten(), p2); - - // Test flatten crossing packed boundary with non-shape uniform elements - var blah = new ParallelArray([2,2], function() { return 0; }); - var pp = new ParallelArray([p0, p1, blah]); - var p2 = new ParallelArray([0,1,2,3,blah[0],blah[1]]); - assertEqParallelArray(pp.flatten(), p2); - - var p0 = new ParallelArray([2,2], function() { return 1; }); - var p1 = new ParallelArray([2,2], function() { return 2; }); - var p = new ParallelArray([p0, p1]); - var p2 = new ParallelArray([p0[0],p0[1],p1[0],p1[1]]); - assertEqParallelArray(p.flatten(), p2); -} - -// FIXME(bug 844991) logical shape not implemented -// if (getBuildConfiguration().parallelJS) -// testFlatten(); diff --git a/js/src/jit-test/tests/parallel/flatten-throws.js b/js/src/jit-test/tests/parallel/flatten-throws.js deleted file mode 100644 index 47fd1ce3fc5f..000000000000 --- a/js/src/jit-test/tests/parallel/flatten-throws.js +++ /dev/null @@ -1,12 +0,0 @@ -// |jit-test| error: Error; - -function testFlattenFlat() { - // Throw on flattening flat array - var p = new ParallelArray([1]); - var f = p.flatten(); -} - -if (getBuildConfiguration().parallelJS) - testFlattenFlat(); -else - throw new Error(); diff --git a/js/src/jit-test/tests/parallel/get-1.js b/js/src/jit-test/tests/parallel/get-1.js deleted file mode 100644 index 5a0c5cfc5146..000000000000 --- a/js/src/jit-test/tests/parallel/get-1.js +++ /dev/null @@ -1,9 +0,0 @@ -function testGet() { - var a = [1,2,3,4,5]; - var p = new ParallelArray(a); - for (var i = 0; i < a.length; i++) - assertEq(p.get(i), a[i]); -} - -if (getBuildConfiguration().parallelJS) - testGet(); diff --git a/js/src/jit-test/tests/parallel/get-2.js b/js/src/jit-test/tests/parallel/get-2.js deleted file mode 100644 index 2601f4e1d967..000000000000 --- a/js/src/jit-test/tests/parallel/get-2.js +++ /dev/null @@ -1,14 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testGet() { - var p = new ParallelArray([2,2,2], function(i,j,k) { return i+j+k; }); - assertEq(p.get(1,1,1), 1+1+1); - var p2 = new ParallelArray([2], function(i) { return 1+1+i; }); - assertEqParallelArray(p.get(1,1), p2); - var p3 = new ParallelArray([2,2], function(i,j) { return 1+i+j; }); - assertEqParallelArray(p.get(1), p3); - assertEq(p.get(5,5), undefined); -} - -if (getBuildConfiguration().parallelJS) - testGet(); diff --git a/js/src/jit-test/tests/parallel/get-3.js b/js/src/jit-test/tests/parallel/get-3.js deleted file mode 100644 index f548db7fed80..000000000000 --- a/js/src/jit-test/tests/parallel/get-3.js +++ /dev/null @@ -1,9 +0,0 @@ -function testGetNoCraziness() { - // .get shouldn't do prototype walks - ParallelArray.prototype[42] = "foo"; - var p = new ParallelArray([1,2,3,4]); - assertEq(p.get(42), undefined); -} - -if (getBuildConfiguration().parallelJS) - testGetNoCraziness(); diff --git a/js/src/jit-test/tests/parallel/get-4.js b/js/src/jit-test/tests/parallel/get-4.js deleted file mode 100644 index 96568978e4bf..000000000000 --- a/js/src/jit-test/tests/parallel/get-4.js +++ /dev/null @@ -1,7 +0,0 @@ -function testGetBounds() { - var p = new ParallelArray([1,2,3,4]); - assertEq(p.get(42), undefined); -} - -if (getBuildConfiguration().parallelJS) - testGetBounds(); diff --git a/js/src/jit-test/tests/parallel/get-6.js b/js/src/jit-test/tests/parallel/get-6.js deleted file mode 100644 index 8d652193612c..000000000000 --- a/js/src/jit-test/tests/parallel/get-6.js +++ /dev/null @@ -1,14 +0,0 @@ -function testGet() { - // Test getting higher dimension inferred shape - var p0 = new ParallelArray([0,1]); - var p1 = new ParallelArray([2,3]); - var p = new ParallelArray([p0, p1]); - assertEq(p.get(0,0), 0); - assertEq(p.get(0,1), 1); - assertEq(p.get(1,0), 2); - assertEq(p.get(1,1), 3); -} - -// FIXME(bug 844991) logical shape not implemented -// if (getBuildConfiguration().parallelJS) -// testGet(); diff --git a/js/src/jit-test/tests/parallel/holes-1.js b/js/src/jit-test/tests/parallel/holes-1.js deleted file mode 100644 index d6b21e71ea9b..000000000000 --- a/js/src/jit-test/tests/parallel/holes-1.js +++ /dev/null @@ -1,31 +0,0 @@ -function testHoles() { - function f1(a) { return a * 42; } - function f2(a,b) { return a * b; } - // Don't crash when getting holes out. - // - // We do the multiplications below instead of asserting against undefined to - // force the VM to call a conversion function, which will crash if the value - // we got out is a JS_ARRAY_HOLE. - var p = new ParallelArray([,1]); - assertEq(p.get(0) * 42, NaN); - var m = p.map(f1); - assertEq(m.get(0), NaN); - assertEq(m.get(1), 42); - var r = p.reduce(f2); - assertEq(r, NaN); - var s = p.scan(f2); - assertEq(s.get(0) * 42, NaN); - assertEq(s.get(1), NaN); - var k = p.scatter([1,0]); - assertEq(k.get(0), 1); - assertEq(k[1] * 42, NaN); - var l = p.filter(function (e, i) { return i == 0; }); - assertEq(l.get(0) * 42, NaN); - var p2 = p.partition(1); - assertEq(p2.get(0).get(0) * 42, NaN); - var g = p.get(0); - assertEq(g * 42, NaN); -} - -if (getBuildConfiguration().parallelJS) - testHoles(); diff --git a/js/src/jit-test/tests/parallel/holes-2.js b/js/src/jit-test/tests/parallel/holes-2.js deleted file mode 100644 index 161a0001e948..000000000000 --- a/js/src/jit-test/tests/parallel/holes-2.js +++ /dev/null @@ -1,25 +0,0 @@ -function testElement() { - // No crazy prototype walking for indexed properties - ParallelArray.prototype[42] = "foo"; - ParallelArray.prototype.bar = "bar"; - var p = new ParallelArray([1,2,3,4]); - assertEq(p[42], undefined); - assertEq(42 in p, false); - assertEq("bar" in p, true); - // Don't inherit any indexed properties - for (var i in p) - assertEq(i !== 42, true); - for (var i in p) { - if (i % 1 !== 0) - assertEq(i, "bar"); - } - ParallelArray.prototype[0] = "foo"; - var p2 = new ParallelArray([,2]); - // ParallelArrays have no holes, so 0 must be 'in' p2 - assertEq(0 in p2, true); - assertEq(p2[0], undefined); -} - -// FIXME(bug 844882) self-hosted object not array-like, exposes internal properties -// if (getBuildConfiguration().parallelJS) -// testElement(); diff --git a/js/src/jit-test/tests/parallel/index-1.js b/js/src/jit-test/tests/parallel/index-1.js deleted file mode 100644 index c264c7b68dff..000000000000 --- a/js/src/jit-test/tests/parallel/index-1.js +++ /dev/null @@ -1,9 +0,0 @@ -function test() { - var N = 22; - var p = new ParallelArray([N], function(i) { return i; }); - for (var i = 0; i < N; i++) - assertEq(p.get(i), i); -} - -if (getBuildConfiguration().parallelJS) - test(); diff --git a/js/src/jit-test/tests/parallel/index-2.js b/js/src/jit-test/tests/parallel/index-2.js deleted file mode 100644 index 385f07157353..000000000000 --- a/js/src/jit-test/tests/parallel/index-2.js +++ /dev/null @@ -1,18 +0,0 @@ -function test() { - function mk(i, j) { return i*100+j; } - - var N = 22; - var M = 44; - - var p = new ParallelArray([N,M], mk); - for (var i = 0; i < N; i++) { - for (var j = 0; j < M; j++) { - print([i, j]); - assertEq(p.get(i, j), mk(i, j)); - assertEq(p.get(i).get(j), mk(i, j)); - } - } -} - -if (getBuildConfiguration().parallelJS) - test(); diff --git a/js/src/jit-test/tests/parallel/index-3.js b/js/src/jit-test/tests/parallel/index-3.js deleted file mode 100644 index 9606c24a3831..000000000000 --- a/js/src/jit-test/tests/parallel/index-3.js +++ /dev/null @@ -1,25 +0,0 @@ -function test() { - function mk(i, j, k) { return i*10000+j*100+k; } - - var N = 10; - var M = 20; - var O = 30; - - print("Computing"); - var p = new ParallelArray([N,M,O], mk); - - print("Checking"); - for (var i = 0; i < N; i++) { - for (var j = 0; j < M; j++) { - for (var k = 0; k < O; k++) { - assertEq(p.get(i, j, k), mk(i, j, k)); - assertEq(p.get(i, j).get(k), mk(i, j, k)); - assertEq(p.get(i).get(j).get(k), mk(i, j, k)); - assertEq(p.get(i).get(j, k), mk(i, j, k)); - } - } - } -} - -if (getBuildConfiguration().parallelJS) - test(); diff --git a/js/src/jit-test/tests/parallel/index-4.js b/js/src/jit-test/tests/parallel/index-4.js deleted file mode 100644 index 6a1696483139..000000000000 --- a/js/src/jit-test/tests/parallel/index-4.js +++ /dev/null @@ -1,27 +0,0 @@ -function test() { - function mk(i, j, k, l) { return i*1000000+j*10000+k*100+l; } - - var N = 2; - var M = 4; - var O = 6; - var P = 8; - - print("Computing"); - var p = new ParallelArray([N,M,O,P], mk); - - print("Checking"); - for (var i = 0; i < N; i++) { - for (var j = 0; j < M; j++) { - for (var k = 0; k < O; k++) { - for (var l = 0; l < P; l++) { - assertEq(p.get(i, j, k, l), mk(i, j, k, l)); - assertEq(p.get(i, j).get(k, l), mk(i, j, k, l)); - assertEq(p.get(i).get(j).get(k).get(l), mk(i, j, k, l)); - } - } - } - } -} - -if (getBuildConfiguration().parallelJS) - test(); diff --git a/js/src/jit-test/tests/parallel/inline-new-bad-type.js b/js/src/jit-test/tests/parallel/inline-new-bad-type.js deleted file mode 100644 index df9d1cbc62f7..000000000000 --- a/js/src/jit-test/tests/parallel/inline-new-bad-type.js +++ /dev/null @@ -1,18 +0,0 @@ -// |jit-test| error: RangeError -// -// Run with --ion-eager. -if (getBuildConfiguration().parallelJS) { - function TestCase(n, d, e, a) {}; - function reportCompare() { - var testcase = new TestCase("x", 0); - } - reportCompare(); - TestCase = ParallelArray; - gczeal(6); - try { - reportCompare(); - } catch(exc1) {} - reportCompare(); -} else { - throw new RangeError(); -} diff --git a/js/src/jit-test/tests/parallel/length-1.js b/js/src/jit-test/tests/parallel/length-1.js deleted file mode 100644 index 20d0656b808f..000000000000 --- a/js/src/jit-test/tests/parallel/length-1.js +++ /dev/null @@ -1,12 +0,0 @@ -function testLength() { - var a = [1,2,3]; - var p = new ParallelArray(a); - assertEq(p.length, a.length); - // Test holes - var a = [1,,3]; - var p = new ParallelArray(a); - assertEq(p.length, a.length); -} - -if (getBuildConfiguration().parallelJS) - testLength(); diff --git a/js/src/jit-test/tests/parallel/length-2.js b/js/src/jit-test/tests/parallel/length-2.js deleted file mode 100644 index 8e569abaebc0..000000000000 --- a/js/src/jit-test/tests/parallel/length-2.js +++ /dev/null @@ -1,13 +0,0 @@ -function testLength() { - // Test higher dimension shape up to 8D - var shape = []; - for (var i = 0; i < 8; i++) { - shape.push(i+1); - var p = new ParallelArray(shape, function () { return 0; }); - // Length should be outermost dimension - assertEq(p.length, shape[0]); - } -} - -if (getBuildConfiguration().parallelJS) - testLength(); diff --git a/js/src/jit-test/tests/parallel/length-3.js b/js/src/jit-test/tests/parallel/length-3.js deleted file mode 100644 index cb9d9ce519dc..000000000000 --- a/js/src/jit-test/tests/parallel/length-3.js +++ /dev/null @@ -1,11 +0,0 @@ -function testLength() { - // Test length immutability. - var p = new ParallelArray([1,2,3,4]); - p.length = 0; - assertEq(p[0], 1); - assertEq(p.length, 4); -} - -// FIXME(bug 844988) immutability not enforced -// if (getBuildConfiguration().parallelJS) -// testLength(); diff --git a/js/src/jit-test/tests/parallel/mandelbrot.js b/js/src/jit-test/tests/parallel/mandelbrot.js index 04306bb3dad3..8764399dfb88 100644 --- a/js/src/jit-test/tests/parallel/mandelbrot.js +++ b/js/src/jit-test/tests/parallel/mandelbrot.js @@ -46,6 +46,8 @@ var cols = 4; if (getBuildConfiguration().parallelJS) { var expected = computeSequentially(); assertParallelExecSucceeds( - function (m) new ParallelArray([rows, cols], computeSetByRow, m).flatten(), + function (m) Array.buildPar(rows * cols, function (xy) { + return computeSetByRow((xy/cols)|0,(xy%cols)) + }, m), function (r) assertStructuralEq(expected, r)); } diff --git a/js/src/jit-test/tests/parallel/map-3.js b/js/src/jit-test/tests/parallel/map-3.js deleted file mode 100644 index f92817fb7706..000000000000 --- a/js/src/jit-test/tests/parallel/map-3.js +++ /dev/null @@ -1,12 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testMap() { - // Test mapping higher dimensional - var p = new ParallelArray([2,2], function (i,j) { return i+j; }); - var m = p.map(function(x) { return x; }); - var p2 = new ParallelArray(p.shape[0], function (i) { return p.get(i); }); - assertEqParallelArray(m, p2); -} - -if (getBuildConfiguration().parallelJS) testMap(); - diff --git a/js/src/jit-test/tests/parallel/overflow-throws.js b/js/src/jit-test/tests/parallel/overflow-throws.js deleted file mode 100644 index 60f5e4264951..000000000000 --- a/js/src/jit-test/tests/parallel/overflow-throws.js +++ /dev/null @@ -1,12 +0,0 @@ -load(libdir + "asserts.js"); - -function testOverflow() { - assertThrowsInstanceOf(function () { - new ParallelArray([0xffffffff + 1], function() { return 0; }); - }, RangeError); - assertThrowsInstanceOf(function () { - new ParallelArray({ length: 0xffffffff + 1 }); - }, RangeError); -} - -if (getBuildConfiguration().parallelJS) testOverflow(); diff --git a/js/src/jit-test/tests/parallel/partition-1.js b/js/src/jit-test/tests/parallel/partition-1.js deleted file mode 100644 index 56b5a64d3af9..000000000000 --- a/js/src/jit-test/tests/parallel/partition-1.js +++ /dev/null @@ -1,15 +0,0 @@ -load(libdir + "eqArrayHelper.js"); - -function testPartition() { - var p = new ParallelArray([1,2,3,4,5,6,7,8]); - var pp = p.partition(2); - var ppp = pp.partition(2); - var ppShape = [p.shape[0] / 2, 2].concat(p.shape.slice(1)); - var pppShape = [pp.shape[0] / 2, 2].concat(pp.shape.slice(1)); - assertEqArray(pp.shape, ppShape); - assertEq(pp.toString(), "<1,2>,<3,4>,<5,6>,<7,8>"); - assertEqArray(ppp.shape, pppShape); - assertEq(ppp.toString(), "<<1,2>,<3,4>>,<<5,6>,<7,8>>"); -} - -if (getBuildConfiguration().parallelJS) testPartition(); diff --git a/js/src/jit-test/tests/parallel/partition-throws.js b/js/src/jit-test/tests/parallel/partition-throws.js deleted file mode 100644 index 7126b3a39002..000000000000 --- a/js/src/jit-test/tests/parallel/partition-throws.js +++ /dev/null @@ -1,10 +0,0 @@ -load(libdir + "asserts.js"); - -function testPartitionDivisible() { - var p = new ParallelArray([1,2,3,4]); - var pp; - assertThrowsInstanceOf(function () { pp = p.partition(3); }, Error); - assertThrowsInstanceOf(function () { pp = p.partition(.34); }, Error); -} - -if (getBuildConfiguration().parallelJS) testPartitionDivisible(); diff --git a/js/src/jit-test/tests/parallel/reduce-higher-dim.js b/js/src/jit-test/tests/parallel/reduce-higher-dim.js deleted file mode 100644 index efe8d60fbab2..000000000000 --- a/js/src/jit-test/tests/parallel/reduce-higher-dim.js +++ /dev/null @@ -1,14 +0,0 @@ - -function testReduce() { - // Test reduce on higher dimensional - // XXX Can we test this better? - var shape = [2]; - for (var i = 0; i < 7; i++) { - shape.push(i+1); - var p = new ParallelArray(shape, function () { return i+1; }); - var r = p.reduce(function (a, b) { return a; }); - assertEq(r.shape.length, i + 1); - } -} - -if (getBuildConfiguration().parallelJS) testReduce(); diff --git a/js/src/jit-test/tests/parallel/reduce-throws.js b/js/src/jit-test/tests/parallel/reduce-throws.js deleted file mode 100644 index bc2b45546281..000000000000 --- a/js/src/jit-test/tests/parallel/reduce-throws.js +++ /dev/null @@ -1,17 +0,0 @@ -load(libdir + "asserts.js"); - -function testReduceThrows() { - // Throw on empty - assertThrowsInstanceOf(function () { - var p = new ParallelArray([]); - p.reduce(function (v, p) { return v*p; }); - }, Error); - // Throw on not function - assertThrowsInstanceOf(function () { - var p = new ParallelArray([1]); - p.reduce(42); - }, TypeError); -} - -// FIXME(bug 844886) sanity check argument types -// if (getBuildConfiguration().parallelJS) testReduceThrows(); diff --git a/js/src/jit-test/tests/parallel/scan-3.js b/js/src/jit-test/tests/parallel/scan-3.js deleted file mode 100644 index 9d2f812c97f4..000000000000 --- a/js/src/jit-test/tests/parallel/scan-3.js +++ /dev/null @@ -1,17 +0,0 @@ -function testScan() { - // Test reduce on higher dimensional - // XXX Can we test this better? - function f(a, b) { return a; } - var shape = [2]; - for (var i = 0; i < 7; i++) { - shape.push(i+1); - var p = new ParallelArray(shape, function () { return i+1; }); - var r = p.reduce(f); - var s = p.scan(f) - for (var j = 0; j < s.length; j++) - assertEq(s.get(j).shape.length, i + 1); - assertEq(r.shape.length, i + 1); - } -} - -if (getBuildConfiguration().parallelJS) testScan(); diff --git a/js/src/jit-test/tests/parallel/scan-throws.js b/js/src/jit-test/tests/parallel/scan-throws.js deleted file mode 100644 index 4cdcceb095b5..000000000000 --- a/js/src/jit-test/tests/parallel/scan-throws.js +++ /dev/null @@ -1,18 +0,0 @@ -load(libdir + "asserts.js"); - -function testScanThrows() { - // Throw on empty - assertThrowsInstanceOf(function () { - var p = new ParallelArray([]); - p.scan(function (v, p) { return v*p; }); - }, Error); - - // Throw on not function - assertThrowsInstanceOf(function () { - var p = new ParallelArray([1]); - p.scan(42); - }, TypeError); -} - -// FIXME(bug 844886) sanity check argument types -// if (getBuildConfiguration().parallelJS) testScanThrows(); diff --git a/js/src/jit-test/tests/parallel/scatter-1.js b/js/src/jit-test/tests/parallel/scatter-1.js deleted file mode 100644 index 69e963982a06..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-1.js +++ /dev/null @@ -1,8 +0,0 @@ - -function testScatter() { - var p = new ParallelArray([1,2,3,4,5]); - var r = p.scatter([0,1,0,3,4], 9, function (a,b) { return a+b; }, 10); - assertEq(r.length, 10); -} - -if (getBuildConfiguration().parallelJS) testScatter(); diff --git a/js/src/jit-test/tests/parallel/scatter-10.js b/js/src/jit-test/tests/parallel/scatter-10.js deleted file mode 100644 index 01f3bacbec9c..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-10.js +++ /dev/null @@ -1,21 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -// Test specific scatter implementation strategies, and compare them -// each against the sequential version. -// -// This is just a simple reverse permutation of the input: -// [A, B, ..., Y, Z] ==> [Z, Y, ..., B, A] - -function testDivideScatterVector() { - var len = 1024; - function add1(x) { return x+1; } - function id(x) { return x; } - var p = new ParallelArray(len, add1); - var revidx = build(len, id).reverse(); - var p2 = new ParallelArray(revidx.map(add1)); - testScatter( - m => p.scatter(revidx, 0, undefined, len, m), - r => assertEqParallelArray(r, p2)); -} - -if (getBuildConfiguration().parallelJS) testDivideScatterVector(); diff --git a/js/src/jit-test/tests/parallel/scatter-11.js b/js/src/jit-test/tests/parallel/scatter-11.js deleted file mode 100644 index e7b443bdd735..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-11.js +++ /dev/null @@ -1,21 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -// Test specific scatter implementation strategies, and compare them -// each against the sequential version. -// -// This is a reverse permutation that has a gap at the end. -// [A, B, ..., Y, Z] ==> [Z, Y, ..., B, A, 0] - -function testDivideScatterVector() { - var len = 1024; - function add1(x) { return x+1; } - function id(x) { return x; } - var p = new ParallelArray(len, add1); - var revidx = build(len, id).reverse(); - var p2 = new ParallelArray(revidx.map(add1).concat([0])); - testScatter( - m => p.scatter(revidx, 0, undefined, len+1, m), - r => assertEqParallelArray(r, p2)); -} - -if (getBuildConfiguration().parallelJS) testDivideScatterVector(); diff --git a/js/src/jit-test/tests/parallel/scatter-12.js b/js/src/jit-test/tests/parallel/scatter-12.js deleted file mode 100644 index 1cf9bd90e7d4..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-12.js +++ /dev/null @@ -1,22 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -// Test specific scatter implementation strategies, and compare them -// each against the sequential version. -// -// This is a reverse permutation that has a gap at the start and at the end. -// [A, B, ..., Y, Z] ==> [0, Z, Y, ..., B, A, 0] - -function testDivideScatterVector() { - var len = 1024; - function add1(x) { return x+1; } - function id(x) { return x; } - var p = new ParallelArray(len, add1); - var revidx = build(len, add1).reverse(); - var p2 = new ParallelArray([0].concat(revidx).concat([0])); - - testScatter( - m => p.scatter(revidx, 0, undefined, len+2, m), - r => assertEqParallelArray(r, p2)); -} - -if (getBuildConfiguration().parallelJS) testDivideScatterVector(); diff --git a/js/src/jit-test/tests/parallel/scatter-13.js b/js/src/jit-test/tests/parallel/scatter-13.js deleted file mode 100644 index 20d0c19c2d9d..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-13.js +++ /dev/null @@ -1,24 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -// Test specific scatter implementation strategies, and compare them -// each against the sequential version. -// -// This is a reverse permutation of the input with collisions at front and end -// [A, B, C, D, ..., W, X, Y, Z] ==> [Z+Y, X, W, ..., D, C, B+A] - -function testDivideScatterVector() { - var len = 1024; - function add1(x) { return x+1; } - function add3(x) { return x+3; } - function id(x) { return x; } - var p = new ParallelArray(len, add1); - var idx = [0,0].concat(build(len-4, add1)).concat([len-3,len-3]); - var revidx = idx.reverse(); - var p2 = [3].concat(build(len-4, add3)).concat([2*len-1]); - var expect = new ParallelArray(p2.reverse()); - testScatter( - m => p.scatter(revidx, 0, function (x,y) { return x+y; }, len-2, m), - r => assertEqParallelArray(r, expect)); -} - -if (getBuildConfiguration().parallelJS) testDivideScatterVector(); diff --git a/js/src/jit-test/tests/parallel/scatter-2.js b/js/src/jit-test/tests/parallel/scatter-2.js deleted file mode 100644 index 07cfb5006a04..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-2.js +++ /dev/null @@ -1,10 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testScatterIdentity() { - var p = new ParallelArray([1,2,3,4,5]); - var r = p.scatter([0,1,2,3,4]); - assertEqParallelArray(p, r); -} - -if (getBuildConfiguration().parallelJS) testScatterIdentity(); - diff --git a/js/src/jit-test/tests/parallel/scatter-3.js b/js/src/jit-test/tests/parallel/scatter-3.js deleted file mode 100644 index 06a4a22ce8de..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-3.js +++ /dev/null @@ -1,12 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testScatter3() { - var p = new ParallelArray([1,2,3,4,5]); - var r = p.scatter([1,0,3,2,4]); - var p2 = new ParallelArray([2,1,4,3,5]); - assertStructuralEq(r, p2); -} - -if (getBuildConfiguration().parallelJS) - testScatter3(); - diff --git a/js/src/jit-test/tests/parallel/scatter-4.js b/js/src/jit-test/tests/parallel/scatter-4.js deleted file mode 100644 index 6180397b92a1..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-4.js +++ /dev/null @@ -1,10 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testScatterDefault() { - var p = new ParallelArray([1,2,3,4,5]); - var r = p.scatter([0,2,4], 9); - assertEqParallelArray(r, new ParallelArray([1,9,2,9,3])); -} - -if (getBuildConfiguration().parallelJS) testScatterDefault(); - diff --git a/js/src/jit-test/tests/parallel/scatter-5.js b/js/src/jit-test/tests/parallel/scatter-5.js deleted file mode 100644 index ccb88684f45b..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-5.js +++ /dev/null @@ -1,9 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testScatterConflict() { - var p = new ParallelArray([1,2,3,4,5]); - var r = p.scatter([0,1,0,3,4], 9, function (a,b) { return a+b; }); - assertEqParallelArray(r, new ParallelArray([4,2,9,4,5])); -} - -if (getBuildConfiguration().parallelJS) testScatterConflict(); diff --git a/js/src/jit-test/tests/parallel/scatter-6.js b/js/src/jit-test/tests/parallel/scatter-6.js deleted file mode 100644 index 35996d791f36..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-6.js +++ /dev/null @@ -1,13 +0,0 @@ - -function testScatter() { - // Test scatter on higher dimension - var shape = [5]; - for (var i = 0; i < 7; i++) { - shape.push(i+1); - var p = new ParallelArray(shape, function(k) { return k; }); - var r = p.scatter([0,1,0,3,4], 9, function (a,b) { return a+b; }, 10); - assertEq(r.length, 10); - } -} - -if (getBuildConfiguration().parallelJS) testScatter(); diff --git a/js/src/jit-test/tests/parallel/scatter-7.js b/js/src/jit-test/tests/parallel/scatter-7.js deleted file mode 100644 index f266fe2e2d24..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-7.js +++ /dev/null @@ -1,15 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testScatterIdentity() { - var shape = [5]; - for (var i = 0; i < 7; i++) { - shape.push(2); - var p = new ParallelArray(shape, function(k) { return k; }); - var r = p.scatter([0,1,2,3,4]); - var p2 = new ParallelArray([p.get(0), p.get(1), p.get(2), p.get(3), p.get(4)]); - assertEqParallelArray(p2, r); - } -} - -if (getBuildConfiguration().parallelJS) testScatterIdentity(); - diff --git a/js/src/jit-test/tests/parallel/scatter-8.js b/js/src/jit-test/tests/parallel/scatter-8.js deleted file mode 100644 index d735df6af2c3..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-8.js +++ /dev/null @@ -1,16 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testScatter8() { - var shape = [5]; - for (var i = 0; i < 7; i++) { - shape.push(2); - var p = new ParallelArray(shape, function(k) { return k; }); - var r = p.scatter([1,0,3,2,4]); - var p2 = new ParallelArray([p.get(1), p.get(0), p.get(3), p.get(2), p.get(4)]); - assertEqParallelArray(p2, r); - } -} - -if (getBuildConfiguration().parallelJS) - testScatter8(); - diff --git a/js/src/jit-test/tests/parallel/scatter-9.js b/js/src/jit-test/tests/parallel/scatter-9.js deleted file mode 100644 index 22be34e50056..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-9.js +++ /dev/null @@ -1,12 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testScatter9() { - // Ignore the rest of the scatter vector if longer than source - var p = new ParallelArray([1,2,3,4,5]); - var r = p.scatter([1,0,3,2,4,1,2,3]); - var p2 = new ParallelArray([2,1,4,3,5]); - assertEqParallelArray(r, p2); -} - -if (getBuildConfiguration().parallelJS) - testScatter9(); diff --git a/js/src/jit-test/tests/parallel/scatter-regression-1.js b/js/src/jit-test/tests/parallel/scatter-regression-1.js deleted file mode 100644 index 4a6fc5173a20..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-regression-1.js +++ /dev/null @@ -1,18 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -// The bug this is testing for: -// the input p and the scatter vector have length 4 -// and the output p2 has length 3. Even though p2 -// is shorter than the input lengths, we still need -// to scan the entirety of the scatter vector, because -// it may hold valid targets at distant indices. -function test() { - var p = new ParallelArray([2,3,5,17]); - var r = p.scatter([0,0,2,1], 9, function (x,y) { return x * y; }, 3); - var p2 = new ParallelArray([6,17,5]); - assertEqParallelArray(r, p2); -} - -if (getBuildConfiguration().parallelJS) - test(); - diff --git a/js/src/jit-test/tests/parallel/scatter-throws.js b/js/src/jit-test/tests/parallel/scatter-throws.js deleted file mode 100644 index 61c37b9a0f24..000000000000 --- a/js/src/jit-test/tests/parallel/scatter-throws.js +++ /dev/null @@ -1,31 +0,0 @@ -load(libdir + "asserts.js"); - -function testScatterThrows() { - var p = new ParallelArray([1,2,3,4,5]); - - // Throw on conflict with no resolution function - assertThrowsInstanceOf(function () { - var r = p.scatter([0,1,0,3,4]); - }, Error); - // Throw on out of bounds - assertThrowsInstanceOf(function () { - var r = p.scatter([0,1,0,3,11]); - }, Error); - - assertThrowsInstanceOf(function () { - p.scatter([-1,1,0,3,4], 9, function (a,b) { return a+b; }, 10); - }, TypeError); - assertThrowsInstanceOf(function () { - p.scatter([0,1,0,3,4], 9, function (a,b) { return a+b; }, -1); - }, RangeError); - assertThrowsInstanceOf(function () { - p.scatter([0,1,0,3,4], 9, function (a,b) { return a+b; }, 0xffffffff + 1); - }, RangeError); - assertThrowsInstanceOf(function () { - p.scatter({ length: 0xffffffff + 1 }, 9, function (a,b) { return a+b; }, 10); - }, RangeError); - -} - -// FIXME(bug 844886) sanity check argument types -// if (getBuildConfiguration().parallelJS) testScatterThrows(); diff --git a/js/src/jit-test/tests/parallel/shape-1.js b/js/src/jit-test/tests/parallel/shape-1.js deleted file mode 100644 index bb455da812b1..000000000000 --- a/js/src/jit-test/tests/parallel/shape-1.js +++ /dev/null @@ -1,8 +0,0 @@ -function testShape() { - var a = [1,2,3]; - var p = new ParallelArray(a); - assertEq(p.shape.length, 1); - assertEq(p.shape[0], a.length); -} - -if (getBuildConfiguration().parallelJS) testShape(); diff --git a/js/src/jit-test/tests/parallel/shape-2.js b/js/src/jit-test/tests/parallel/shape-2.js deleted file mode 100644 index 8ab7efada68b..000000000000 --- a/js/src/jit-test/tests/parallel/shape-2.js +++ /dev/null @@ -1,15 +0,0 @@ -load(libdir + "eqArrayHelper.js"); - -function testShape() { - // Test higher dimension shape up to 8D - var shape = []; - for (var i = 0; i < 8; i++) { - shape.push(i+1); - var p = new ParallelArray(shape, function () { return 0; }); - // Test shape identity and shape - assertEqArray(p.shape, shape); - assertEq(p.shape !== shape, true); - } -} - -if (getBuildConfiguration().parallelJS) testShape(); diff --git a/js/src/jit-test/tests/parallel/shape-3.js b/js/src/jit-test/tests/parallel/shape-3.js deleted file mode 100644 index e8af4033b67d..000000000000 --- a/js/src/jit-test/tests/parallel/shape-3.js +++ /dev/null @@ -1,16 +0,0 @@ -load(libdir + "eqArrayHelper.js"); - -function testInfer() { - // Test that shapes are inferred - var p0 = new ParallelArray([0,1]); - var p1 = new ParallelArray([2,3]); - var p = new ParallelArray([p0, p1]); - assertEqArray(p.shape, [2,2]); - var p0 = new ParallelArray([0,1]); - var p1 = new ParallelArray([2]); - var p = new ParallelArray([p0, p1]); - assertEqArray(p.shape, [2]); -} - -// FIXME(bug 844991) logical shape not implemented -// if (parallelEnabled) testInfer(); diff --git a/js/src/jit-test/tests/parallel/shape-4.js b/js/src/jit-test/tests/parallel/shape-4.js deleted file mode 100644 index ddd47bb9c12f..000000000000 --- a/js/src/jit-test/tests/parallel/shape-4.js +++ /dev/null @@ -1,13 +0,0 @@ -load(libdir + "eqArrayHelper.js"); - -function testShape() { - // Test shape immutability. - var p = new ParallelArray([1,2,3,4]); - p.shape[0] = 0; - p.shape[1] = 42; - assertEq(p[0], 1); - assertEqArray(p.shape, [4]); -} - -// FIXME(bug 844988) immutability not enforced -// if (getBuildConfiguration().parallelJS) testShape(); diff --git a/js/src/jit-test/tests/parallel/shape-5.js b/js/src/jit-test/tests/parallel/shape-5.js deleted file mode 100644 index ddd47bb9c12f..000000000000 --- a/js/src/jit-test/tests/parallel/shape-5.js +++ /dev/null @@ -1,13 +0,0 @@ -load(libdir + "eqArrayHelper.js"); - -function testShape() { - // Test shape immutability. - var p = new ParallelArray([1,2,3,4]); - p.shape[0] = 0; - p.shape[1] = 42; - assertEq(p[0], 1); - assertEqArray(p.shape, [4]); -} - -// FIXME(bug 844988) immutability not enforced -// if (getBuildConfiguration().parallelJS) testShape(); diff --git a/js/src/jit-test/tests/parallel/stack-overflow.js b/js/src/jit-test/tests/parallel/stack-overflow.js index 8993ff5703d9..c62f1de071c7 100644 --- a/js/src/jit-test/tests/parallel/stack-overflow.js +++ b/js/src/jit-test/tests/parallel/stack-overflow.js @@ -9,9 +9,9 @@ function kernel(n) { } function testMap() { - var p = new ParallelArray(range(0, 2048)); + var p = range(0, 2048); assertParallelExecWillBail( - m => p.map(kernel, m)); + m => p.mapPar(kernel, m)); } if (getBuildConfiguration().parallelJS) diff --git a/js/src/jit-test/tests/parallel/toString-1.js b/js/src/jit-test/tests/parallel/toString-1.js deleted file mode 100644 index 96514373e399..000000000000 --- a/js/src/jit-test/tests/parallel/toString-1.js +++ /dev/null @@ -1,6 +0,0 @@ -function testToString() { - var p = new ParallelArray(); - assertEq(p.toString(), ""); -} - -if (getBuildConfiguration().parallelJS) testToString(); diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index dc8b1231cd60..e12f63ccfe7c 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -7868,19 +7868,6 @@ GetTemplateObjectForNative(JSContext *cx, HandleScript script, jsbytecode *pc, return true; } - if (native == intrinsic_NewParallelArray || native == ParallelArrayObject::construct) { - res.set(ParallelArrayObject::newInstance(cx, TenuredObject)); - if (!res) - return false; - - types::TypeObject *type = - types::TypeScript::InitObject(cx, script, pc, JSProto_ParallelArray); - if (!type) - return false; - res->setType(type); - return true; - } - return true; } diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 7cbe48d16495..b9c2d6bd1525 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -2937,50 +2937,6 @@ CodeGenerator::generateBody() return true; } -// Out-of-line object allocation for LNewParallelArray. -class OutOfLineNewParallelArray : public OutOfLineCodeBase -{ - LNewParallelArray *lir_; - - public: - OutOfLineNewParallelArray(LNewParallelArray *lir) - : lir_(lir) - { } - - bool accept(CodeGenerator *codegen) { - return codegen->visitOutOfLineNewParallelArray(this); - } - - LNewParallelArray *lir() const { - return lir_; - } -}; - -typedef JSObject *(*NewInitParallelArrayFn)(JSContext *, HandleObject); -static const VMFunction NewInitParallelArrayInfo = - FunctionInfo(NewInitParallelArray); - -bool -CodeGenerator::visitNewParallelArrayVMCall(LNewParallelArray *lir) -{ - JS_ASSERT(gen->info().executionMode() == SequentialExecution); - - Register objReg = ToRegister(lir->output()); - - JS_ASSERT(!lir->isCall()); - saveLive(lir); - - pushArg(ImmGCPtr(lir->mir()->templateObject())); - if (!callVM(NewInitParallelArrayInfo, lir)) - return false; - - if (ReturnReg != objReg) - masm.movePtr(ReturnReg, objReg); - - restoreLive(lir); - return true; -} - // Out-of-line object allocation for LNewArray. class OutOfLineNewArray : public OutOfLineCodeBase { @@ -3104,32 +3060,6 @@ bool CodeGenerator::visitHypot(LHypot *lir) return true; } -bool -CodeGenerator::visitNewParallelArray(LNewParallelArray *lir) -{ - Register objReg = ToRegister(lir->output()); - JSObject *templateObject = lir->mir()->templateObject(); - - OutOfLineNewParallelArray *ool = new(alloc()) OutOfLineNewParallelArray(lir); - if (!addOutOfLineCode(ool)) - return false; - - masm.newGCThing(objReg, templateObject, ool->entry(), gc::DefaultHeap); - masm.initGCThing(objReg, templateObject); - - masm.bind(ool->rejoin()); - return true; -} - -bool -CodeGenerator::visitOutOfLineNewParallelArray(OutOfLineNewParallelArray *ool) -{ - if (!visitNewParallelArrayVMCall(ool->lir())) - return false; - masm.jump(ool->rejoin()); - return true; -} - bool CodeGenerator::visitNewArray(LNewArray *lir) { diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index e522f27da234..7f264c981c8c 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -25,7 +25,6 @@ namespace js { namespace jit { -class OutOfLineNewParallelArray; class OutOfLineTestObject; class OutOfLineNewArray; class OutOfLineNewObject; @@ -127,9 +126,6 @@ class CodeGenerator : public CodeGeneratorSpecific bool visitDoubleToInt32(LDoubleToInt32 *lir); bool visitFloat32ToInt32(LFloat32ToInt32 *lir); bool visitNewSlots(LNewSlots *lir); - bool visitNewParallelArrayVMCall(LNewParallelArray *lir); - bool visitNewParallelArray(LNewParallelArray *lir); - bool visitOutOfLineNewParallelArray(OutOfLineNewParallelArray *ool); bool visitNewArrayCallVM(LNewArray *lir); bool visitNewArray(LNewArray *lir); bool visitOutOfLineNewArray(OutOfLineNewArray *ool); diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 2404e147d2df..b3094b74e461 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -645,16 +645,6 @@ class IonBuilder : public MIRGenerator InliningStatus inlineUnsafeSetReservedSlot(CallInfo &callInfo); InliningStatus inlineUnsafeGetReservedSlot(CallInfo &callInfo); - // Parallel intrinsics. - InliningStatus inlineNewParallelArray(CallInfo &callInfo); - InliningStatus inlineParallelArray(CallInfo &callInfo); - InliningStatus inlineParallelArrayTail(CallInfo &callInfo, - JSFunction *target, - MDefinition *ctor, - types::TemporaryTypeSet *ctorTypes, - uint32_t discards, - Native native); - // Utility intrinsics. InliningStatus inlineIsCallable(CallInfo &callInfo); InliningStatus inlineHaveSameClass(CallInfo &callInfo); diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index 13c803502d6c..8f94028624a1 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -302,16 +302,6 @@ class LNewSlots : public LCallInstructionHelper<1, 0, 3> } }; -class LNewParallelArray : public LInstructionHelper<1, 0, 0> -{ - public: - LIR_HEADER(NewParallelArray); - - MNewParallelArray *mir() const { - return mir_->toNewParallelArray(); - } -}; - class LNewArray : public LInstructionHelper<1, 0, 0> { public: diff --git a/js/src/jit/LOpcodes.h b/js/src/jit/LOpcodes.h index 44d9db360964..9e731b593006 100644 --- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -22,7 +22,6 @@ _(TableSwitch) \ _(TableSwitchV) \ _(Goto) \ - _(NewParallelArray) \ _(NewArray) \ _(NewObject) \ _(NewSlots) \ diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 92ceb8941332..54b3a02c0a76 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -160,13 +160,6 @@ LIRGenerator::visitNewSlots(MNewSlots *ins) return defineReturn(lir, ins); } -bool -LIRGenerator::visitNewParallelArray(MNewParallelArray *ins) -{ - LNewParallelArray *lir = new(alloc()) LNewParallelArray(); - return define(lir, ins) && assignSafepoint(lir, ins); -} - bool LIRGenerator::visitNewArray(MNewArray *ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 8302b9f16d68..9e772343fd30 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -81,7 +81,6 @@ class LIRGenerator : public LIRGeneratorSpecific bool visitGoto(MGoto *ins); bool visitTableSwitch(MTableSwitch *tableswitch); bool visitNewSlots(MNewSlots *ins); - bool visitNewParallelArray(MNewParallelArray *ins); bool visitNewArray(MNewArray *ins); bool visitNewObject(MNewObject *ins); bool visitNewDeclEnvObject(MNewDeclEnvObject *ins); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 6292fc1ab510..4b010bb50650 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -6,7 +6,6 @@ #include "jsmath.h" -#include "builtin/ParallelArray.h" #include "builtin/TestingFunctions.h" #include "jit/BaselineInspector.h" #include "jit/IonBuilder.h" @@ -137,10 +136,6 @@ IonBuilder::inlineNativeCall(CallInfo &callInfo, JSNative native) // Parallel intrinsics. if (native == intrinsic_ShouldForceSequential) return inlineForceSequentialOrInParallelSection(callInfo); - if (native == intrinsic_NewParallelArray) - return inlineNewParallelArray(callInfo); - if (native == ParallelArrayObject::construct) - return inlineParallelArray(callInfo); // Utility intrinsics. if (native == intrinsic_IsCallable) @@ -1244,153 +1239,6 @@ IonBuilder::inlineForceSequentialOrInParallelSection(CallInfo &callInfo) MOZ_ASSUME_UNREACHABLE("Invalid execution mode"); } -IonBuilder::InliningStatus -IonBuilder::inlineNewParallelArray(CallInfo &callInfo) -{ - // Rewrites a call like - // - // NewParallelArray(ParallelArrayView, arg0, ..., argN) - // - // to - // - // x = MNewParallelArray() - // ParallelArrayView(x, arg0, ..., argN) - - uint32_t argc = callInfo.argc(); - if (argc < 1 || callInfo.constructing()) - return InliningStatus_NotInlined; - - types::TemporaryTypeSet *ctorTypes = callInfo.getArg(0)->resultTypeSet(); - JSObject *targetObj = ctorTypes ? ctorTypes->getSingleton() : nullptr; - JSFunction *target = nullptr; - if (targetObj && targetObj->is()) - target = &targetObj->as(); - if (target && target->isInterpreted() && target->nonLazyScript()->shouldCloneAtCallsite()) { - if (JSFunction *clone = ExistingCloneFunctionAtCallsite(compartment->callsiteClones(), target, script(), pc)) - target = clone; - } - MDefinition *ctor = makeCallsiteClone( - target, - callInfo.getArg(0)->toPassArg()->getArgument()); - - // Discard the function. - return inlineParallelArrayTail(callInfo, target, ctor, - target ? nullptr : ctorTypes, 1, - intrinsic_NewParallelArray); -} - -IonBuilder::InliningStatus -IonBuilder::inlineParallelArray(CallInfo &callInfo) -{ - if (!callInfo.constructing()) - return InliningStatus_NotInlined; - - uint32_t argc = callInfo.argc(); - JSFunction *target = ParallelArrayObject::maybeGetConstructor(&script()->global(), argc); - if (!target) - return InliningStatus_NotInlined; - - JS_ASSERT(target->nonLazyScript()->shouldCloneAtCallsite()); - if (JSFunction *clone = ExistingCloneFunctionAtCallsite(compartment->callsiteClones(), target, script(), pc)) - target = clone; - - MConstant *ctor = MConstant::New(alloc(), ObjectValue(*target)); - current->add(ctor); - - return inlineParallelArrayTail(callInfo, target, ctor, nullptr, 0, - ParallelArrayObject::construct); -} - -IonBuilder::InliningStatus -IonBuilder::inlineParallelArrayTail(CallInfo &callInfo, - JSFunction *target, - MDefinition *ctor, - types::TemporaryTypeSet *ctorTypes, - uint32_t discards, - Native native) -{ - // Rewrites either NewParallelArray(...) or new ParallelArray(...) from a - // call to a native ctor into a call to the relevant function in the - // self-hosted code. - - uint32_t argc = callInfo.argc() - discards; - - // Create the new parallel array object. Parallel arrays have specially - // constructed type objects, so we can only perform the inlining if we - // already have one of these type objects. - types::TemporaryTypeSet *returnTypes = getInlineReturnTypeSet(); - if (returnTypes->getKnownTypeTag() != JSVAL_TYPE_OBJECT) - return InliningStatus_NotInlined; - if (returnTypes->unknownObject() || returnTypes->getObjectCount() != 1) - return InliningStatus_NotInlined; - types::TypeObject *typeObject = returnTypes->getTypeObject(0); - if (!typeObject || typeObject->clasp != &ParallelArrayObject::class_) - return InliningStatus_NotInlined; - - JSObject *templateObject = inspector->getTemplateObjectForNative(pc, native); - if (!templateObject || templateObject->type() != typeObject) - return InliningStatus_NotInlined; - - // Create the call and add in the non-this arguments. - uint32_t targetArgs = argc; - if (target && !target->isNative()) - targetArgs = Max(target->nargs, argc); - - MCall *call = MCall::New(alloc(), target, targetArgs + 1, argc, false); - if (!call) - return InliningStatus_Error; - - callInfo.unwrapArgs(); - - // Explicitly pad any missing arguments with |undefined|. - // This permits skipping the argumentsRectifier. - for (uint32_t i = targetArgs; i > argc; i--) { - JS_ASSERT_IF(target, !target->isNative()); - MConstant *undef = MConstant::New(alloc(), UndefinedValue()); - current->add(undef); - MPassArg *pass = MPassArg::New(alloc(), undef); - current->add(pass); - call->addArg(i, pass); - } - - MPassArg *oldThis = MPassArg::New(alloc(), callInfo.thisArg()); - current->add(oldThis); - - // Add explicit arguments. - // Skip addArg(0) because it is reserved for this - for (uint32_t i = 0; i < argc; i++) { - MDefinition *arg = callInfo.getArg(i + discards); - MPassArg *passArg = MPassArg::New(alloc(), arg); - current->add(passArg); - call->addArg(i + 1, passArg); - } - - // Place an MPrepareCall before the first passed argument, before we - // potentially perform rearrangement. - MPrepareCall *start = MPrepareCall::New(alloc()); - oldThis->block()->insertBefore(oldThis, start); - call->initPrepareCall(start); - - // Create the MIR to allocate the new parallel array. Take the type - // object is taken from the prediction set. - MNewParallelArray *newObject = MNewParallelArray::New(alloc(), templateObject); - current->add(newObject); - MPassArg *newThis = MPassArg::New(alloc(), newObject); - current->add(newThis); - call->addArg(0, newThis); - - // Set the new callee. - call->initFunction(ctor); - - current->add(call); - current->push(newObject); - - if (!resumeAfter(call)) - return InliningStatus_Error; - - return InliningStatus_Inlined; -} - IonBuilder::InliningStatus IonBuilder::inlineNewDenseArray(CallInfo &callInfo) { diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 23713460447c..97207eed77b7 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -1363,32 +1363,6 @@ class MThrow } }; -class MNewParallelArray : public MNullaryInstruction -{ - CompilerRootObject templateObject_; - - MNewParallelArray(JSObject *templateObject) - : templateObject_(templateObject) - { - setResultType(MIRType_Object); - } - - public: - INSTRUCTION_HEADER(NewParallelArray); - - static MNewParallelArray *New(TempAllocator &alloc, JSObject *templateObject) { - return new(alloc) MNewParallelArray(templateObject); - } - - AliasSet getAliasSet() const { - return AliasSet::None(); - } - - JSObject *templateObject() const { - return templateObject_; - } -}; - // Fabricate a type set containing only the type of the specified object. types::TemporaryTypeSet * MakeSingletonTypeSet(JSObject *obj); diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 12cee893cefa..e2d348dc6a28 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -87,7 +87,6 @@ namespace jit { _(TruncateToInt32) \ _(ToString) \ _(NewSlots) \ - _(NewParallelArray) \ _(NewArray) \ _(NewObject) \ _(NewDeclEnvObject) \ diff --git a/js/src/jit/ParallelSafetyAnalysis.cpp b/js/src/jit/ParallelSafetyAnalysis.cpp index 446e0f159f6c..40494356146c 100644 --- a/js/src/jit/ParallelSafetyAnalysis.cpp +++ b/js/src/jit/ParallelSafetyAnalysis.cpp @@ -184,7 +184,6 @@ class ParallelSafetyVisitor : public MInstructionVisitor CUSTOM_OP(NewArray) CUSTOM_OP(NewObject) CUSTOM_OP(NewCallObject) - CUSTOM_OP(NewParallelArray) UNSAFE_OP(NewDerivedTypedObject) UNSAFE_OP(InitElem) UNSAFE_OP(InitElemGetterSetter) @@ -517,12 +516,6 @@ ParallelSafetyVisitor::visitCreateThisWithTemplate(MCreateThisWithTemplate *ins) return replaceWithNewPar(ins, ins->templateObject()); } -bool -ParallelSafetyVisitor::visitNewParallelArray(MNewParallelArray *ins) -{ - return replaceWithNewPar(ins, ins->templateObject()); -} - bool ParallelSafetyVisitor::visitNewCallObject(MNewCallObject *ins) { diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 7e76e8857473..8c8ad1c9c753 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -6,7 +6,6 @@ #include "jit/VMFunctions.h" -#include "builtin/ParallelArray.h" #include "builtin/TypedObject.h" #include "frontend/BytecodeCompiler.h" #include "jit/BaselineIC.h" @@ -277,21 +276,6 @@ IteratorMore(JSContext *cx, HandleObject obj, bool *res) return true; } -JSObject * -NewInitParallelArray(JSContext *cx, HandleObject templateObject) -{ - JS_ASSERT(templateObject->getClass() == &ParallelArrayObject::class_); - JS_ASSERT(!templateObject->hasSingletonType()); - - RootedObject obj(cx, ParallelArrayObject::newInstance(cx, TenuredObject)); - if (!obj) - return nullptr; - - obj->setType(templateObject->type()); - - return obj; -} - JSObject* NewInitArray(JSContext *cx, uint32_t count, types::TypeObject *typeArg) { diff --git a/js/src/js.msg b/js/src/js.msg index 5a8344163bab..072f28a8a3e6 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -354,10 +354,10 @@ MSG_DEF(JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT, 300, 0, JSEXN_SYNTAXERR, "paramet MSG_DEF(JSMSG_YIELD_IN_DEFAULT, 301, 0, JSEXN_SYNTAXERR, "yield in default expression") MSG_DEF(JSMSG_INTRINSIC_NOT_DEFINED, 302, 1, JSEXN_REFERENCEERR, "no intrinsic function {0}") MSG_DEF(JSMSG_ALREADY_HAS_PRAGMA, 303, 2, JSEXN_ERR, "{0} is being assigned a {1}, but already has one") -MSG_DEF(JSMSG_PAR_ARRAY_BAD_ARG, 304, 1, JSEXN_RANGEERR, "invalid ParallelArray{0} argument") -MSG_DEF(JSMSG_PAR_ARRAY_BAD_PARTITION, 305, 0, JSEXN_ERR, "argument must be divisible by outermost dimension") -MSG_DEF(JSMSG_PAR_ARRAY_REDUCE_EMPTY, 306, 0, JSEXN_ERR, "cannot reduce ParallelArray object whose outermost dimension is empty") -MSG_DEF(JSMSG_PAR_ARRAY_ALREADY_FLAT, 307, 0, JSEXN_ERR, "cannot flatten 1-dimensional ParallelArray object") +MSG_DEF(JSMSG_PAR_ARRAY_BAD_ARG, 304, 0, JSEXN_RANGEERR, "invalid parallel method argument") +MSG_DEF(JSMSG_UNUSED305, 305, 0, JSEXN_NONE, "") +MSG_DEF(JSMSG_UNUSED306, 306, 0, JSEXN_NONE, "") +MSG_DEF(JSMSG_UNUSED307, 307, 0, JSEXN_NONE, "") MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_CONFLICT, 308, 0, JSEXN_ERR, "no conflict resolution function provided") MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_BOUNDS, 309, 0, JSEXN_ERR, "index in scatter vector out of bounds") MSG_DEF(JSMSG_CANT_REPORT_NC_AS_NE, 310, 0, JSEXN_TYPEERR, "proxy can't report a non-configurable own property as non-existent") diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 38996d6f9ae1..882c65b08b8e 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -52,7 +52,6 @@ #include "builtin/Eval.h" #include "builtin/Intl.h" #include "builtin/MapObject.h" -#include "builtin/ParallelArray.h" #include "builtin/RegExp.h" #include "builtin/TypedObject.h" #include "frontend/BytecodeCompiler.h" diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index c4e07abd3271..0166b368d659 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -15,7 +15,6 @@ #include "jsanalyze.h" -#include "builtin/ParallelArray.h" #include "jit/ExecutionModeInlines.h" #include "vm/ArrayObject.h" #include "vm/BooleanObject.h" @@ -354,9 +353,6 @@ GetClassForProtoKey(JSProtoKey key) case JSProto_DataView: return &DataViewObject::class_; - case JSProto_ParallelArray: - return &ParallelArrayObject::class_; - default: MOZ_ASSUME_UNREACHABLE("Bad proto key"); } diff --git a/js/src/jsprototypes.h b/js/src/jsprototypes.h index bc4a767033de..447e080ca5fa 100644 --- a/js/src/jsprototypes.h +++ b/js/src/jsprototypes.h @@ -91,10 +91,9 @@ real(Map, 33, js_InitMapClass, OCLASP(Map)) \ real(Set, 34, js_InitSetClass, OCLASP(Set)) \ real(DataView, 35, js_InitTypedArrayClasses, OCLASP(DataView)) \ -IF_PJS(real,imaginary) (ParallelArray, 36, js_InitParallelArrayClass, OCLASP(ParallelArray)) \ -IF_INTL(real,imaginary) (Intl, 37, js_InitIntlClass, CLASP(Intl)) \ -IF_BDATA(real,imaginary)(TypedObject, 38, js_InitTypedObjectModuleObject, OCLASP(TypedObjectModule)) \ - imaginary(GeneratorFunction, 39, js_InitIteratorClasses, dummy) \ +IF_INTL(real,imaginary) (Intl, 36, js_InitIntlClass, CLASP(Intl)) \ +IF_BDATA(real,imaginary)(TypedObject, 37, js_InitTypedObjectModuleObject, OCLASP(TypedObjectModule)) \ + imaginary(GeneratorFunction, 38, js_InitIteratorClasses, dummy) \ #define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro) diff --git a/js/src/moz.build b/js/src/moz.build index 31769c7439b5..73d1083e9a30 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -96,7 +96,6 @@ UNIFIED_SOURCES += [ 'builtin/Intl.cpp', 'builtin/MapObject.cpp', 'builtin/Object.cpp', - 'builtin/ParallelArray.cpp', 'builtin/Profilers.cpp', 'builtin/TestingFunctions.cpp', 'builtin/TypedObject.cpp', diff --git a/js/src/vm/ForkJoin.cpp b/js/src/vm/ForkJoin.cpp index 1f2e41df78fe..c60745b0cd1d 100644 --- a/js/src/vm/ForkJoin.cpp +++ b/js/src/vm/ForkJoin.cpp @@ -191,7 +191,7 @@ namespace js { // of operation. enum ForkJoinMode { // WARNING: If you change this enum, you MUST update - // ForkJoinMode() in ParallelArray.js + // ForkJoinMode() in Utilities.js // The "normal" behavior: attempt parallel, fallback to // sequential. If compilation is ongoing in a helper thread, then diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index 6ec8721d960a..51948545ca95 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -516,9 +516,6 @@ GlobalObject::initStandardClasses(JSContext *cx, Handle global) GlobalObject::initSetIteratorProto(cx, global) && #if EXPOSE_INTL_API js_InitIntlClass(cx, global) && -#endif -#if ENABLE_PARALLEL_JS - js_InitParallelArrayClass(cx, global) && #endif true; } diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 62f86a4795d6..55335eacfa05 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -12,7 +12,6 @@ #include "selfhosted.out.h" #include "builtin/Intl.h" -#include "builtin/ParallelArray.h" #include "builtin/TypedObject.h" #include "gc/Marking.h" #include "vm/ForkJoin.h" @@ -311,27 +310,6 @@ intrinsic_ForkJoinSlices(JSContext *cx, unsigned argc, Value *vp) return true; } -/* - * NewParallelArray(init, ...args): Creates a new parallel array using - * an initialization function |init|. All subsequent arguments are - * passed to |init|. The new instance will be passed as the |this| - * argument. - */ -bool -js::intrinsic_NewParallelArray(JSContext *cx, unsigned argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - JS_ASSERT(args[0].isObject() && args[0].toObject().is()); - - RootedFunction init(cx, &args[0].toObject().as()); - CallArgs args0 = CallArgsFromVp(argc - 1, vp + 1); - if (!js::ParallelArrayObject::constructHelper(cx, &init, args0)) - return false; - args.rval().set(args0.rval()); - return true; -} - /* * NewDenseArray(length): Allocates and returns a new dense array with * the given length where all values are initialized to holes. @@ -640,7 +618,6 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("ForkJoin", intrinsic_ForkJoin, 2,0), JS_FN("ForkJoinSlices", intrinsic_ForkJoinSlices, 0,0), - JS_FN("NewParallelArray", intrinsic_NewParallelArray, 3,0), JS_FN("NewDenseArray", intrinsic_NewDenseArray, 1,0), JS_FN("ShouldForceSequential", intrinsic_ShouldForceSequential, 0,0), JS_FN("ParallelTestsShouldPass", intrinsic_ParallelTestsShouldPass, 0,0), From 36b2bcec465709fcbdbb7b8bbc30f3e3f42402ac Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Thu, 12 Dec 2013 23:19:55 -0800 Subject: [PATCH 022/144] Bug 944074 - Followup: undefined min and max from to prevent conflicts. (r=jandem) --- js/src/jswin.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/src/jswin.h b/js/src/jswin.h index acab9e74a821..7cff6c84cb2d 100644 --- a/js/src/jswin.h +++ b/js/src/jswin.h @@ -14,6 +14,8 @@ #ifdef XP_WIN # include +# undef min +# undef max # undef GetProp # undef SetProp # undef CONST From 86b703757e390287ad38146b8ec63c97a6ba98b4 Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Thu, 12 Dec 2013 23:23:57 -0800 Subject: [PATCH 023/144] Bug 949916 - Fix race between off-main-thread-compilation and PJS. (r=bhackett) --- js/src/jit/Ion.cpp | 10 +++++----- js/src/jit/Ion.h | 1 + js/src/jsworkers.cpp | 6 +++--- js/src/vm/ForkJoin.cpp | 13 ++++++++++++- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index ba3ee46a154d..3ca40db2aad5 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -2454,8 +2454,8 @@ InvalidateActivation(FreeOp *fop, uint8_t *ionTop, bool invalidateAll) IonSpew(IonSpew_Invalidate, "END invalidating activation"); } -static void -StopOffThreadCompilation(JSCompartment *comp) +void +jit::StopAllOffThreadCompilations(JSCompartment *comp) { if (!comp->jitCompartment()) return; @@ -2467,7 +2467,7 @@ void jit::InvalidateAll(FreeOp *fop, Zone *zone) { for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) - StopOffThreadCompilation(comp); + StopAllOffThreadCompilations(comp); for (JitActivationIterator iter(fop->runtime()); !iter.done(); ++iter) { if (iter.activation()->compartment()->zone() == zone) { @@ -2845,10 +2845,10 @@ AutoDebugModeInvalidation::~AutoDebugModeInvalidation() FreeOp *fop = rt->defaultFreeOp(); if (comp_) { - StopOffThreadCompilation(comp_); + StopAllOffThreadCompilations(comp_); } else { for (CompartmentsInZoneIter comp(zone_); !comp.done(); comp.next()) - StopOffThreadCompilation(comp); + StopAllOffThreadCompilations(comp); } if (invalidateStack) { diff --git a/js/src/jit/Ion.h b/js/src/jit/Ion.h index 37ad4e778387..bf495ccf0e43 100644 --- a/js/src/jit/Ion.h +++ b/js/src/jit/Ion.h @@ -391,6 +391,7 @@ CodeGenerator *CompileBackEnd(MIRGenerator *mir, MacroAssembler *maybeMasm = nul void AttachFinishedCompilations(JSContext *cx); void FinishOffThreadBuilder(IonBuilder *builder); +void StopAllOffThreadCompilations(JSCompartment *comp); static inline bool IsIonEnabled(JSContext *cx) diff --git a/js/src/jsworkers.cpp b/js/src/jsworkers.cpp index 61cf70e8eca8..0136601f6a2e 100644 --- a/js/src/jsworkers.cpp +++ b/js/src/jsworkers.cpp @@ -767,14 +767,14 @@ WorkerThread::handleIonWorkload(WorkerThreadState &state) FinishOffThreadIonCompile(ionBuilder); ionBuilder = nullptr; - // Notify the main thread in case it is waiting for the compilation to finish. - state.notifyAll(WorkerThreadState::CONSUMER); - // Ping the main thread so that the compiled code can be incorporated // at the next operation callback. Don't interrupt Ion code for this, as // this incorporation can be delayed indefinitely without affecting // performance as long as the main thread is actually executing Ion code. runtime->triggerOperationCallback(JSRuntime::TriggerCallbackAnyThreadDontStopIon); + + // Notify the main thread in case it is waiting for the compilation to finish. + state.notifyAll(WorkerThreadState::CONSUMER); } void diff --git a/js/src/vm/ForkJoin.cpp b/js/src/vm/ForkJoin.cpp index c60745b0cd1d..007d729c9218 100644 --- a/js/src/vm/ForkJoin.cpp +++ b/js/src/vm/ForkJoin.cpp @@ -867,8 +867,19 @@ js::ParallelDo::compileForParallelExecution(ExecutionStatus *status) } } } - if (allScriptsPresent) + + if (allScriptsPresent) { + // For testing modes, we want to make sure that all off thread + // compilation tasks are finished, so we don't race with + // off-main-thread-compilation setting an interrupt flag while we + // are in the middle of a test, causing unexpected bailouts. + if (mode_ != ForkJoinModeNormal) { + StopAllOffThreadCompilations(cx_->compartment()); + if (!js_HandleExecutionInterrupt(cx_)) + return fatalError(status); + } break; + } } Spew(SpewCompile, "Compilation complete (final worklist length %d)", From ef063fb0d4a6eecae13ecd2b915d1137bb60f4db Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Thu, 12 Dec 2013 23:33:50 -0800 Subject: [PATCH 024/144] Back out b6f9dbc91dc4 (bug 947038) for Cipc crashes --- dom/ipc/TabChild.cpp | 5 - gfx/layers/basic/BasicCompositor.cpp | 155 ++++++++++++++++++ gfx/layers/client/ContentClient.cpp | 5 - gfx/layers/composite/TextureHost.cpp | 8 + gfx/layers/composite/ThebesLayerComposite.cpp | 1 - gfx/thebes/gfxPlatform.cpp | 20 --- gfx/thebes/gfxPlatform.h | 5 +- widget/xpwidgets/nsBaseWidget.cpp | 2 - 8 files changed, 164 insertions(+), 37 deletions(-) diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index 7e88a30e1b88..8306641297ff 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -72,7 +72,6 @@ #include "APZCCallbackHelper.h" #include "nsILoadContext.h" #include "ipc/nsGUIEventIPC.h" -#include "gfxPlatform.h" #ifdef DEBUG #include "PCOMContentPermissionRequestChild.h" @@ -2282,10 +2281,6 @@ TabChild::InitRenderingState() NS_WARNING("failed to properly allocate layer transaction"); return false; } - - // Track which compositor backend is in use (see bug 947038). This can - // be removed when deprecated textures are removed. - gfxPlatform::GetPlatform()->SetCompositorBackend(mTextureFactoryIdentifier.mParentBackend); } else { // Pushing transactions to the parent content. shadowManager = remoteFrame->SendPLayerTransactionConstructor(); diff --git a/gfx/layers/basic/BasicCompositor.cpp b/gfx/layers/basic/BasicCompositor.cpp index 0bc966af4163..cc934831b3b8 100644 --- a/gfx/layers/basic/BasicCompositor.cpp +++ b/gfx/layers/basic/BasicCompositor.cpp @@ -63,6 +63,161 @@ public: RefPtr mSurface; }; +/** + * Texture source and host implementaion for software compositing. + */ +class DeprecatedTextureHostBasic : public DeprecatedTextureHost + , public TextureSourceBasic +{ +public: + DeprecatedTextureHostBasic() + : mCompositor(nullptr) + {} + + SurfaceFormat GetFormat() const MOZ_OVERRIDE { return mFormat; } + + virtual IntSize GetSize() const MOZ_OVERRIDE { return mSize; } + + virtual TextureSourceBasic* AsSourceBasic() MOZ_OVERRIDE { return this; } + + SourceSurface *GetSurface() MOZ_OVERRIDE { return mSurface; } + + virtual void SetCompositor(Compositor* aCompositor) + { + mCompositor = static_cast(aCompositor); + } + + virtual const char *Name() { return "DeprecatedTextureHostBasic"; } + +protected: + virtual void UpdateImpl(const SurfaceDescriptor& aImage, + nsIntRegion *aRegion, + nsIntPoint*) MOZ_OVERRIDE + { + AutoOpenSurface surf(OPEN_READ_ONLY, aImage); + nsRefPtr surface = ShadowLayerForwarder::OpenDescriptor(OPEN_READ_ONLY, aImage); + nsRefPtr image = surface->GetAsImageSurface(); + mFormat = ImageFormatToSurfaceFormat(image->Format()); + mSize = IntSize(image->Width(), image->Height()); + mSurface = Factory::CreateWrappingDataSourceSurface(image->Data(), + image->Stride(), + mSize, + mFormat); + } + + virtual bool EnsureSurface() { + return true; + } + + virtual bool Lock() MOZ_OVERRIDE { + return EnsureSurface(); + } + + virtual TemporaryRef GetAsSurface() MOZ_OVERRIDE { + if (!mSurface) { + return nullptr; + } + return mSurface->GetDataSurface(); + } + + BasicCompositor *mCompositor; + RefPtr mSurface; + IntSize mSize; + SurfaceFormat mFormat; +}; + +void +DeserializerToPlanarYCbCrImageData(YCbCrImageDataDeserializer& aDeserializer, PlanarYCbCrData& aData) +{ + aData.mYChannel = aDeserializer.GetYData(); + aData.mYStride = aDeserializer.GetYStride(); + aData.mYSize = aDeserializer.GetYSize(); + aData.mCbChannel = aDeserializer.GetCbData(); + aData.mCrChannel = aDeserializer.GetCrData(); + aData.mCbCrStride = aDeserializer.GetCbCrStride(); + aData.mCbCrSize = aDeserializer.GetCbCrSize(); + aData.mPicSize = aDeserializer.GetYSize(); +} + +class YCbCrDeprecatedTextureHostBasic : public DeprecatedTextureHostBasic +{ +public: + virtual void UpdateImpl(const SurfaceDescriptor& aImage, + nsIntRegion *aRegion, + nsIntPoint*) MOZ_OVERRIDE + { + MOZ_ASSERT(aImage.type() == SurfaceDescriptor::TYCbCrImage); + mSurface = nullptr; + ConvertImageToRGB(aImage); + } + + virtual void SwapTexturesImpl(const SurfaceDescriptor& aImage, + nsIntRegion* aRegion) MOZ_OVERRIDE + { + MOZ_ASSERT(aImage.type() == SurfaceDescriptor::TYCbCrImage); + mSurface = nullptr; + } + + virtual bool EnsureSurface() MOZ_OVERRIDE + { + if (mSurface) { + return true; + } + if (!mBuffer) { + return false; + } + return ConvertImageToRGB(*mBuffer); + } + + bool ConvertImageToRGB(const SurfaceDescriptor& aImage) + { + YCbCrImageDataDeserializer deserializer(aImage.get_YCbCrImage().data().get()); + PlanarYCbCrData data; + DeserializerToPlanarYCbCrImageData(deserializer, data); + + gfxImageFormat format = gfxImageFormatRGB24; + gfxIntSize size; + gfxUtils::GetYCbCrToRGBDestFormatAndSize(data, format, size); + if (size.width > PlanarYCbCrImage::MAX_DIMENSION || + size.height > PlanarYCbCrImage::MAX_DIMENSION) { + NS_ERROR("Illegal image dest width or height"); + return false; + } + + mSize = ToIntSize(size); + mFormat = (format == gfxImageFormatRGB24) + ? FORMAT_B8G8R8X8 + : FORMAT_B8G8R8A8; + + RefPtr surface = Factory::CreateDataSourceSurface(mSize, mFormat); + gfxUtils::ConvertYCbCrToRGB(data, format, size, + surface->GetData(), + surface->Stride()); + + mSurface = surface; + return true; + } +}; + +TemporaryRef +CreateBasicDeprecatedTextureHost(SurfaceDescriptorType aDescriptorType, + uint32_t aTextureHostFlags, + uint32_t aTextureFlags) +{ + RefPtr result = nullptr; + if (aDescriptorType == SurfaceDescriptor::TYCbCrImage) { + result = new YCbCrDeprecatedTextureHostBasic(); + } else { + MOZ_ASSERT(aDescriptorType == SurfaceDescriptor::TShmem || + aDescriptorType == SurfaceDescriptor::TMemoryImage, + "We can only support Shmem currently"); + result = new DeprecatedTextureHostBasic(); + } + + result->SetFlags(aTextureFlags); + return result.forget(); +} + BasicCompositor::BasicCompositor(nsIWidget *aWidget) : mWidget(aWidget) { diff --git a/gfx/layers/client/ContentClient.cpp b/gfx/layers/client/ContentClient.cpp index 157c01970fcf..544784812d91 100644 --- a/gfx/layers/client/ContentClient.cpp +++ b/gfx/layers/client/ContentClient.cpp @@ -58,11 +58,6 @@ ContentClient::CreateContentClient(CompositableForwarder* aForwarder) useDeprecatedTextures = gfxPlatform::GetPlatform()->UseDeprecatedTextures(); #endif - // Always use new textures for the basic compositor. - if (backend == LAYERS_BASIC) { - useDeprecatedTextures = false; - } - #ifdef XP_WIN if (backend == LAYERS_D3D11) { useDoubleBuffering = !!gfxWindowsPlatform::GetPlatform()->GetD2DDevice(); diff --git a/gfx/layers/composite/TextureHost.cpp b/gfx/layers/composite/TextureHost.cpp index 4caeb0b8030a..5e4da901d9ac 100644 --- a/gfx/layers/composite/TextureHost.cpp +++ b/gfx/layers/composite/TextureHost.cpp @@ -84,6 +84,10 @@ TextureHost::AsTextureHost(PTextureParent* actor) TemporaryRef CreateDeprecatedTextureHostOGL(SurfaceDescriptorType aDescriptorType, uint32_t aDeprecatedTextureHostFlags, uint32_t aTextureFlags); +// implemented in BasicCompositor.cpp +TemporaryRef CreateBasicDeprecatedTextureHost(SurfaceDescriptorType aDescriptorType, + uint32_t aDeprecatedTextureHostFlags, + uint32_t aTextureFlags); #ifdef XP_WIN TemporaryRef CreateDeprecatedTextureHostD3D9(SurfaceDescriptorType aDescriptorType, @@ -123,6 +127,10 @@ DeprecatedTextureHost::CreateDeprecatedTextureHost(SurfaceDescriptorType aDescri aDeprecatedTextureHostFlags, aTextureFlags); #endif + case LAYERS_BASIC: + return CreateBasicDeprecatedTextureHost(aDescriptorType, + aDeprecatedTextureHostFlags, + aTextureFlags); default: MOZ_CRASH("Couldn't create texture host"); } diff --git a/gfx/layers/composite/ThebesLayerComposite.cpp b/gfx/layers/composite/ThebesLayerComposite.cpp index 7a9cc3f00c45..1f35533634d0 100644 --- a/gfx/layers/composite/ThebesLayerComposite.cpp +++ b/gfx/layers/composite/ThebesLayerComposite.cpp @@ -138,7 +138,6 @@ ThebesLayerComposite::RenderLayer(const nsIntRect& aClipRect) mBuffer->SetPaintWillResample(MayResample()); - mBuffer->SetCompositor(mCompositeManager->GetCompositor()); mBuffer->Composite(effectChain, GetEffectiveOpacity(), transform, diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 89c74cc2c52e..b563f75429e4 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -281,7 +281,6 @@ gfxPlatform::gfxPlatform() , mDrawLayerBorders(false) , mDrawTileBorders(false) , mDrawBigImageBorders(false) - , mCompositorBackend(LAYERS_NONE) { mUseHarfBuzzScripts = UNINITIALIZED_VALUE; mAllowDownloadableFonts = UNINITIALIZED_VALUE; @@ -2214,22 +2213,3 @@ gfxPlatform::ComponentAlphaEnabled() InitLayersAccelerationPrefs(); return sComponentAlphaEnabled; } - -void -gfxPlatform::SetCompositorBackend(mozilla::layers::LayersBackend backend) -{ - // The compositor backend should always be the same after one has been chosen. - MOZ_ASSERT(mCompositorBackend == LAYERS_NONE || mCompositorBackend == backend); - if (mCompositorBackend == LAYERS_NONE) { - mCompositorBackend = backend; - } -} - -bool -gfxPlatform::UseDeprecatedTextures() const -{ - if (mCompositorBackend == LAYERS_BASIC) { - return false; - } - return mLayersUseDeprecated; -} diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h index 64d138698558..27ff830fd51e 100644 --- a/gfx/thebes/gfxPlatform.h +++ b/gfx/thebes/gfxPlatform.h @@ -20,7 +20,6 @@ #include "mozilla/RefPtr.h" #include "GfxInfoCollector.h" -#include "mozilla/layers/LayersTypes.h" #include "mozilla/layers/CompositorTypes.h" #ifdef XP_OS2 @@ -620,8 +619,7 @@ public: * This method should not be called from the compositor thread. */ bool PreferMemoryOverShmem() const; - bool UseDeprecatedTextures() const; - void SetCompositorBackend(mozilla::layers::LayersBackend backend); + bool UseDeprecatedTextures() const { return mLayersUseDeprecated; } protected: gfxPlatform(); @@ -738,7 +736,6 @@ private: bool mDrawLayerBorders; bool mDrawTileBorders; bool mDrawBigImageBorders; - mozilla::layers::LayersBackend mCompositorBackend; }; #endif /* GFX_PLATFORM_H */ diff --git a/widget/xpwidgets/nsBaseWidget.cpp b/widget/xpwidgets/nsBaseWidget.cpp index f8f4ce68d058..1916ced3277d 100644 --- a/widget/xpwidgets/nsBaseWidget.cpp +++ b/widget/xpwidgets/nsBaseWidget.cpp @@ -988,8 +988,6 @@ void nsBaseWidget::CreateCompositor(int aWidth, int aHeight) ImageBridgeChild::IdentifyCompositorTextureHost(textureFactoryIdentifier); WindowUsesOMTC(); - gfxPlatform::GetPlatform()->SetCompositorBackend(textureFactoryIdentifier.mParentBackend); - mLayerManager = lm; return; } From abf326e01b4a344e13d26de7437c41495404ac64 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Fri, 13 Dec 2013 16:06:53 +0900 Subject: [PATCH 025/144] Bug 949906 - Add a callback to modify evaluated moz.build sandboxes, fix Sphinx docs; r=glandium --HG-- extra : rebase_source : 9ef2219145fb754a9cbe9e7e30b6f2841910f13f --- python/mozbuild/mozbuild/frontend/reader.py | 16 ++++++++++++++- .../mozbuild/test/frontend/test_reader.py | 20 +++++++++++++++++-- tools/docs/mach_commands.py | 9 ++++++++- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/python/mozbuild/mozbuild/frontend/reader.py b/python/mozbuild/mozbuild/frontend/reader.py index 76ee9d0c866e..da87965ea099 100644 --- a/python/mozbuild/mozbuild/frontend/reader.py +++ b/python/mozbuild/mozbuild/frontend/reader.py @@ -592,13 +592,20 @@ class BuildReader(object): This is where the build system starts. You give it a tree configuration (the output of configuration) and it executes the moz.build files and collects the data they define. + + The reader can optionally call a callable after each sandbox is evaluated + but before its evaluated content is processed. This gives callers the + opportunity to modify sandboxes before side-effects occur from their + content. This callback receives the ``Sandbox`` that was evaluated. The + return value is ignored. """ - def __init__(self, config): + def __init__(self, config, sandbox_post_eval_cb=None): self.config = config self.topsrcdir = config.topsrcdir self.topobjdir = config.topobjdir + self._sandbox_post_eval_cb = sandbox_post_eval_cb self._log = logging.getLogger(__name__) self._read_files = set() self._execution_stack = [] @@ -719,6 +726,10 @@ class BuildReader(object): sandbox = MozbuildSandbox(self.config, path, metadata=metadata) sandbox.exec_file(path, filesystem_absolute=filesystem_absolute) sandbox.execution_time = time.time() - time_start + + if self._sandbox_post_eval_cb: + self._sandbox_post_eval_cb(sandbox) + var = metadata.get('var', None) forbidden = { 'TOOL_DIRS': ['DIRS', 'PARALLEL_DIRS', 'TEST_DIRS'], @@ -770,6 +781,9 @@ class BuildReader(object): # so until the library linking operations are moved out of it, at which # point PARALLEL_DIRS will be irrelevant anyways. for gyp_sandbox in gyp_sandboxes: + if self._sandbox_post_eval_cb: + self._sandbox_post_eval_cb(gyp_sandbox) + sandbox['DIRS'].append(mozpath.relpath(gyp_sandbox['OBJDIR'], sandbox['OBJDIR'])) yield sandbox diff --git a/python/mozbuild/mozbuild/test/frontend/test_reader.py b/python/mozbuild/mozbuild/test/frontend/test_reader.py index 2d2bf672e38c..d057ef8335bc 100644 --- a/python/mozbuild/mozbuild/test/frontend/test_reader.py +++ b/python/mozbuild/mozbuild/test/frontend/test_reader.py @@ -33,13 +33,13 @@ class TestBuildReader(unittest.TestCase): return MockConfig(path, **kwargs) - def reader(self, name, enable_tests=False): + def reader(self, name, enable_tests=False, **kwargs): extra = {} if enable_tests: extra['ENABLE_TESTS'] = '1' config = self.config(name, extra_substs=extra) - return BuildReader(config) + return BuildReader(config, **kwargs) def file_path(self, name, *args): return mozpath.join(data_path, name, *args) @@ -266,5 +266,21 @@ class TestBuildReader(unittest.TestCase): self.assertEqual([sandbox['XPIDL_MODULE'] for sandbox in sandboxes], ['foobar', 'foobar', 'foobar', 'foobar']) + def test_process_eval_callback(self): + def strip_dirs(sandbox): + sandbox['DIRS'][:] = [] + count[0] += 1 + + reader = self.reader('traversal-simple', + sandbox_post_eval_cb=strip_dirs) + + count = [0] + + sandboxes = list(reader.read_topsrcdir()) + + self.assertEqual(len(sandboxes), 1) + self.assertEqual(len(count), 1) + + if __name__ == '__main__': main() diff --git a/tools/docs/mach_commands.py b/tools/docs/mach_commands.py index b1bbb4b923b4..7948f2490de6 100644 --- a/tools/docs/mach_commands.py +++ b/tools/docs/mach_commands.py @@ -38,7 +38,14 @@ class Documentation(MachCommandBase): manager = SphinxManager(self.topsrcdir, os.path.join(self.topsrcdir, 'tools', 'docs'), outdir) - reader = BuildReader(self.config_environment) + # We don't care about GYP projects, so don't process them. This makes + # scanning faster and may even prevent an exception. + def remove_gyp_dirs(sandbox): + sandbox['GYP_DIRS'][:] = [] + + reader = BuildReader(self.config_environment, + sandbox_post_eval_cb=remove_gyp_dirs) + for sandbox in reader.walk_topsrcdir(): for dest_dir, source_dir in sandbox['SPHINX_TREES'].items(): manager.add_tree(os.path.join(sandbox['RELATIVEDIR'], From 3ec0b0a106095e0d914f72026429806ae675cfb3 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Wed, 13 Nov 2013 15:41:22 -0800 Subject: [PATCH 026/144] Bug 928195 - Part 6: Add docs for WebIDL and the build system; r=froydnj --HG-- extra : rebase_source : cee29c0bf5c050d73ef02d6ad933802b8fad21e0 --- dom/bindings/docs/index.rst | 122 ++++++++++++++++++++++++++++++++++++ dom/bindings/moz.build | 3 + 2 files changed, 125 insertions(+) create mode 100644 dom/bindings/docs/index.rst diff --git a/dom/bindings/docs/index.rst b/dom/bindings/docs/index.rst new file mode 100644 index 000000000000..16a92b64dba9 --- /dev/null +++ b/dom/bindings/docs/index.rst @@ -0,0 +1,122 @@ +.. _webidl: + +====== +WebIDL +====== + +WebIDL describes interfaces web browsers are supposed to implement. + +The interaction between WebIDL and the build system is somewhat complex. +This document will attempt to explain how it all works. + +Overview +======== + +``.webidl`` files throughout the tree define interfaces the browser +implements. Since Gecko/Firefox is implemented in C++, there is a +mechanism to convert these interfaces and associated metadata to +C++ code. That's where the build system comes into play. + +All the code for interacting with ``.webidl`` files lives under +``dom/bindings``. There is code in the build system to deal with +WebIDLs explicitly. + +WebIDL source file flavors +========================== + +Not all ``.webidl`` files are created equal! There are several flavors, +each represented by a separate symbol from :ref:`mozbuild_symbols`. + +WEBIDL_FILES + Refers to regular/static ``.webidl`` files. Most WebIDL interfaces + are defined this way. + +GENERATED_EVENTS_WEBIDL_FILES + In addition to generating a binding, these ``.webidl`` files also + generate a source file implementing the event object in C++ + +PREPROCESSED_WEBIDL_FILES + The ``.webidl`` files are generated by preprocessing an input file. + They otherwise behave like *WEBIDL_FILES*. + +TEST_WEBIDL_FILES + Like *WEBIDL_FILES* but the interfaces are for testing only and + aren't shipped with the browser. + +PREPROCESSED_TEST_WEBIDL_FILES + Like *TEST_WEBIDL_FILES* except the ``.webidl`` is obtained via + preprocessing, much like *PREPROCESSED_WEBIDL_FILES*. + +GENERATED_WEBIDL_FILES + The ``.webidl`` for these is obtained through an *external* + mechanism. Typically there are custom build rules for producing these + files. + +Producing C++ code +================== + +The most complicated part about WebIDLs is the process by which +``.webidl`` files are converted into C++. + +This process is handled by code in the :py:mod:`mozwebidlcodegen` +package. :py:class:`mozwebidlcodegen.WebIDLCodegenManager` is +specifically where you want to look for how code generation is +performed. This includes complex dependency management. + +Requirements +============ + +This section aims to document the build and developer workflow requirements +for WebIDL. + +Parser unit tests + There are parser tests provided by ``dom/bindings/parser/runtests.py`` + that should run as part of ``make check``. There must be a mechanism + to run the tests in *human* mode so they output friendly error + messages. + + The current mechanism for this is ``mach webidl-parser-test``. + +Mochitests + There are various mochitests under ``dom/bindings/test``. They should + be runnable through the standard mechanisms. + +Working with test interfaces + ``TestExampleGenBinding.cpp`` calls into methods from the + ``TestExampleInterface`` and ``TestExampleProxyInterface`` interfaces. + These interfaces need to be generated as part of the build. These + interfaces should not be exported or packaged. + + There is a ``compiletests`` make target in ``dom/bindings`` that + isn't part of the build that facilitates turnkey code generation + and test file compilation. + +Minimal rebuilds + Reprocessing every output for every change is expensive. So we don't + inconvenience people changing ``.webidl`` files, the build system + should only perform a minimal rebuild when sources change. + + This logic is mostly all handled in + :py:class:`mozwebidlcodegen.WebIDLCodegenManager`. The unit tests for + that Python code should adequately test typical rebuild scenarios. + + Bug 940469 tracks making the existing implementation better. + +Explicit method for performing codegen + There needs to be an explicit method for invoking code generation. + It needs to cover regular and test files. + + This is implemented via ``make export`` in ``dom/bindings``. + +No-op binding generation should be fast + So developers touching ``.webidl`` files are not inconvenienced, + no-op binding generation should be fast. Watch out for the build system + processing large dependency files it doesn't need in order to perform + code generation. + +Ability to generate example files + *Any* interface can have example ``.h``/``.cpp`` files generated. + There must be a mechanism to facilitate this. + + This is currently facilitated through ``mach webidl-example``. e.g. + ``mach webidl-example HTMLStyleElement``. diff --git a/dom/bindings/moz.build b/dom/bindings/moz.build index 557d4a63e92f..acb65bedc69d 100644 --- a/dom/bindings/moz.build +++ b/dom/bindings/moz.build @@ -87,3 +87,6 @@ if CONFIG['MOZ_AUDIO_CHANNEL_MANAGER']: ] FINAL_LIBRARY = 'xul' + +SPHINX_TREES['webidl'] = 'docs' +SPHINX_PYTHON_PACKAGE_DIRS += ['mozwebidlcodegen'] From 7da8970d0e765fd024b89506587929f60740dd14 Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Fri, 13 Dec 2013 08:34:24 +0000 Subject: [PATCH 027/144] Bug 944442 - DeCOMify imgIContainer::GetFrame. r=seth --HG-- extra : rebase_source : 612c1923f3ed8a01fc30f2d306b4682e585e53fe --- .../shell/src/nsWindowsShellService.cpp | 7 ++-- content/svg/content/src/SVGFEImageElement.cpp | 6 +-- image/public/imgIContainer.idl | 6 ++- image/src/ClippedImage.cpp | 22 ++++------ image/src/ClippedImage.h | 14 +++---- image/src/FrozenImage.cpp | 7 ++-- image/src/FrozenImage.h | 5 +-- image/src/ImageWrapper.cpp | 7 ++-- image/src/OrientedImage.cpp | 18 ++++---- image/src/OrientedImage.h | 5 +-- image/src/RasterImage.cpp | 41 ++++++++----------- image/src/VectorImage.cpp | 18 ++++---- image/src/imgTools.cpp | 7 ++-- layout/base/nsLayoutUtils.cpp | 8 ++-- widget/cocoa/nsClipboard.mm | 7 ++-- widget/cocoa/nsCocoaUtils.mm | 6 +-- widget/cocoa/nsMenuItemIconX.mm | 7 ++-- widget/gtk/nsImageToPixbuf.cpp | 12 +++--- widget/os2/nsWindow.cpp | 7 ++-- widget/qt/nsClipboard.cpp | 7 ++-- widget/windows/WinUtils.cpp | 4 +- widget/windows/nsImageClipboard.cpp | 7 ++-- widget/windows/nsWindowGfx.cpp | 7 ++-- 23 files changed, 100 insertions(+), 135 deletions(-) diff --git a/browser/components/shell/src/nsWindowsShellService.cpp b/browser/components/shell/src/nsWindowsShellService.cpp index 35b9bcfc46f0..908508639a97 100644 --- a/browser/components/shell/src/nsWindowsShellService.cpp +++ b/browser/components/shell/src/nsWindowsShellService.cpp @@ -752,10 +752,9 @@ WriteBitmap(nsIFile* aFile, imgIContainer* aImage) { nsresult rv; - nsRefPtr surface; - aImage->GetFrame(imgIContainer::FRAME_FIRST, - imgIContainer::FLAG_SYNC_DECODE, - getter_AddRefs(surface)); + nsRefPtr surface = + aImage->GetFrame(imgIContainer::FRAME_FIRST, + imgIContainer::FLAG_SYNC_DECODE); NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE); nsRefPtr image(surface->GetAsReadableARGB32ImageSurface()); diff --git a/content/svg/content/src/SVGFEImageElement.cpp b/content/svg/content/src/SVGFEImageElement.cpp index 5ebf78c9d0f7..c91795c30447 100644 --- a/content/svg/content/src/SVGFEImageElement.cpp +++ b/content/svg/content/src/SVGFEImageElement.cpp @@ -211,9 +211,9 @@ SVGFEImageElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance, nsRefPtr currentFrame; if (imageContainer) { - imageContainer->GetFrame(imgIContainer::FRAME_CURRENT, - imgIContainer::FLAG_SYNC_DECODE, - getter_AddRefs(currentFrame)); + currentFrame = + imageContainer->GetFrame(imgIContainer::FRAME_CURRENT, + imgIContainer::FLAG_SYNC_DECODE); } if (!currentFrame) { diff --git a/image/public/imgIContainer.idl b/image/public/imgIContainer.idl index 3bcfc05689fd..884f27e30838 100644 --- a/image/public/imgIContainer.idl +++ b/image/public/imgIContainer.idl @@ -55,6 +55,7 @@ native nsSize(nsSize); native Orientation(mozilla::image::Orientation); [ref] native TimeStamp(mozilla::TimeStamp); [ptr] native SVGImageContext(mozilla::SVGImageContext); +native already_AddRefed_gfxASurface(already_AddRefed); /** @@ -64,7 +65,7 @@ native Orientation(mozilla::image::Orientation); * * Internally, imgIContainer also manages animation of images. */ -[scriptable, builtinclass, uuid(73340b79-e3ae-4f02-97d0-822db78017e5)] +[scriptable, builtinclass, uuid(8b7db7dd-bfe9-40d3-9114-3a79c0658afd)] interface imgIContainer : nsISupports { /** @@ -172,7 +173,8 @@ interface imgIContainer : nsISupports * @param aWhichFrame Frame specifier of the FRAME_* variety. * @param aFlags Flags of the FLAG_* variety */ - [noscript] gfxASurface getFrame(in uint32_t aWhichFrame, + [noscript, notxpcom] already_AddRefed_gfxASurface + getFrame(in uint32_t aWhichFrame, in uint32_t aFlags); /** diff --git a/image/src/ClippedImage.cpp b/image/src/ClippedImage.cpp index d5cf33ca1578..c09e8d2aa4c6 100644 --- a/image/src/ClippedImage.cpp +++ b/image/src/ClippedImage.cpp @@ -209,23 +209,21 @@ ClippedImage::GetIntrinsicRatio(nsSize* aRatio) return NS_OK; } -NS_IMETHODIMP +NS_IMETHODIMP_(already_AddRefed) ClippedImage::GetFrame(uint32_t aWhichFrame, - uint32_t aFlags, - gfxASurface** _retval) + uint32_t aFlags) { - return GetFrameInternal(mClip.Size(), nullptr, aWhichFrame, aFlags, _retval); + return GetFrameInternal(mClip.Size(), nullptr, aWhichFrame, aFlags); } -nsresult +already_AddRefed ClippedImage::GetFrameInternal(const nsIntSize& aViewportSize, const SVGImageContext* aSVGContext, uint32_t aWhichFrame, - uint32_t aFlags, - gfxASurface** _retval) + uint32_t aFlags) { if (!ShouldClip()) { - return InnerImage()->GetFrame(aWhichFrame, aFlags, _retval); + return InnerImage()->GetFrame(aWhichFrame, aFlags); } float frameToDraw = InnerImage()->GetFrameIndex(aWhichFrame); @@ -273,9 +271,7 @@ ClippedImage::GetFrameInternal(const nsIntSize& aViewportSize, } MOZ_ASSERT(mCachedSurface, "Should have a cached surface now"); - nsRefPtr surf = mCachedSurface->Surface(); - surf.forget(_retval); - return NS_OK; + return mCachedSurface->Surface(); } NS_IMETHODIMP @@ -335,8 +331,8 @@ ClippedImage::Draw(gfxContext* aContext, if (MustCreateSurface(aContext, aUserSpaceToImageSpace, sourceRect, aSubimage, aFlags)) { // Create a temporary surface containing a single tile of this image. // GetFrame will call DrawSingleTile internally. - nsRefPtr surface; - GetFrameInternal(aViewportSize, aSVGContext, aWhichFrame, aFlags, getter_AddRefs(surface)); + nsRefPtr surface = + GetFrameInternal(aViewportSize, aSVGContext, aWhichFrame, aFlags); NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE); // Create a drawable from that surface. diff --git a/image/src/ClippedImage.h b/image/src/ClippedImage.h index 51bffa5f5805..69c609fdece1 100644 --- a/image/src/ClippedImage.h +++ b/image/src/ClippedImage.h @@ -35,9 +35,8 @@ public: NS_IMETHOD GetHeight(int32_t* aHeight) MOZ_OVERRIDE; NS_IMETHOD GetIntrinsicSize(nsSize* aSize) MOZ_OVERRIDE; NS_IMETHOD GetIntrinsicRatio(nsSize* aRatio) MOZ_OVERRIDE; - NS_IMETHOD GetFrame(uint32_t aWhichFrame, - uint32_t aFlags, - gfxASurface** _retval) MOZ_OVERRIDE; + NS_IMETHOD_(already_AddRefed) GetFrame(uint32_t aWhichFrame, + uint32_t aFlags) MOZ_OVERRIDE; NS_IMETHOD GetImageContainer(mozilla::layers::LayerManager* aManager, mozilla::layers::ImageContainer** _retval) MOZ_OVERRIDE; NS_IMETHOD Draw(gfxContext* aContext, @@ -56,11 +55,10 @@ protected: ClippedImage(Image* aImage, nsIntRect aClip); private: - nsresult GetFrameInternal(const nsIntSize& aViewportSize, - const SVGImageContext* aSVGContext, - uint32_t aWhichFrame, - uint32_t aFlags, - gfxASurface** _retval); + already_AddRefed GetFrameInternal(const nsIntSize& aViewportSize, + const SVGImageContext* aSVGContext, + uint32_t aWhichFrame, + uint32_t aFlags); bool ShouldClip(); bool MustCreateSurface(gfxContext* aContext, const gfxMatrix& aTransform, diff --git a/image/src/FrozenImage.cpp b/image/src/FrozenImage.cpp index a308fbc914b0..2b2607953449 100644 --- a/image/src/FrozenImage.cpp +++ b/image/src/FrozenImage.cpp @@ -40,12 +40,11 @@ FrozenImage::GetAnimated(bool* aAnimated) return rv; } -NS_IMETHODIMP +NS_IMETHODIMP_(already_AddRefed) FrozenImage::GetFrame(uint32_t aWhichFrame, - uint32_t aFlags, - gfxASurface** _retval) + uint32_t aFlags) { - return InnerImage()->GetFrame(FRAME_FIRST, aFlags, _retval); + return InnerImage()->GetFrame(FRAME_FIRST, aFlags); } NS_IMETHODIMP_(bool) diff --git a/image/src/FrozenImage.h b/image/src/FrozenImage.h index 3cb1e8e33aef..947dc322a553 100644 --- a/image/src/FrozenImage.h +++ b/image/src/FrozenImage.h @@ -34,9 +34,8 @@ public: virtual void DecrementAnimationConsumers() MOZ_OVERRIDE; NS_IMETHOD GetAnimated(bool* aAnimated) MOZ_OVERRIDE; - NS_IMETHOD GetFrame(uint32_t aWhichFrame, - uint32_t aFlags, - gfxASurface** _retval) MOZ_OVERRIDE; + NS_IMETHOD_(already_AddRefed) GetFrame(uint32_t aWhichFrame, + uint32_t aFlags) MOZ_OVERRIDE; NS_IMETHOD_(bool) FrameIsOpaque(uint32_t aWhichFrame) MOZ_OVERRIDE; NS_IMETHOD GetImageContainer(layers::LayerManager* aManager, layers::ImageContainer** _retval) MOZ_OVERRIDE; diff --git a/image/src/ImageWrapper.cpp b/image/src/ImageWrapper.cpp index 6e2c9adddf6c..aaba02f77c96 100644 --- a/image/src/ImageWrapper.cpp +++ b/image/src/ImageWrapper.cpp @@ -196,12 +196,11 @@ ImageWrapper::GetAnimated(bool* aAnimated) return mInnerImage->GetAnimated(aAnimated); } -NS_IMETHODIMP +NS_IMETHODIMP_(already_AddRefed) ImageWrapper::GetFrame(uint32_t aWhichFrame, - uint32_t aFlags, - gfxASurface** _retval) + uint32_t aFlags) { - return mInnerImage->GetFrame(aWhichFrame, aFlags, _retval); + return mInnerImage->GetFrame(aWhichFrame, aFlags); } NS_IMETHODIMP_(bool) diff --git a/image/src/OrientedImage.cpp b/image/src/OrientedImage.cpp index fb3ae9c52955..24d4e3b79413 100644 --- a/image/src/OrientedImage.cpp +++ b/image/src/OrientedImage.cpp @@ -75,15 +75,14 @@ OrientedImage::GetIntrinsicRatio(nsSize* aRatio) return rv; } -NS_IMETHODIMP +NS_IMETHODIMP_(already_AddRefed) OrientedImage::GetFrame(uint32_t aWhichFrame, - uint32_t aFlags, - gfxASurface** _retval) + uint32_t aFlags) { nsresult rv; if (mOrientation.IsIdentity()) { - return InnerImage()->GetFrame(aWhichFrame, aFlags, _retval); + return InnerImage()->GetFrame(aWhichFrame, aFlags); } // Get the underlying dimensions. @@ -95,7 +94,7 @@ OrientedImage::GetFrame(uint32_t aWhichFrame, rv = InnerImage()->GetWidth(&width); rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&height); } - NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_SUCCESS(rv, nullptr); // Determine an appropriate format for the surface. gfx::SurfaceFormat surfaceFormat; @@ -116,9 +115,9 @@ OrientedImage::GetFrame(uint32_t aWhichFrame, GetThebesSurfaceForDrawTarget(target); // Create our drawable. - nsRefPtr innerSurface; - rv = InnerImage()->GetFrame(aWhichFrame, aFlags, getter_AddRefs(innerSurface)); - NS_ENSURE_SUCCESS(rv, rv); + nsRefPtr innerSurface = + InnerImage()->GetFrame(aWhichFrame, aFlags); + NS_ENSURE_TRUE(innerSurface, nullptr); nsRefPtr drawable = new gfxSurfaceDrawable(innerSurface, gfxIntSize(width, height)); @@ -129,8 +128,7 @@ OrientedImage::GetFrame(uint32_t aWhichFrame, imageRect, imageRect, imageRect, imageRect, imageFormat, GraphicsFilter::FILTER_FAST); - surface.forget(_retval); - return NS_OK; + return surface.forget(); } NS_IMETHODIMP diff --git a/image/src/OrientedImage.h b/image/src/OrientedImage.h index 6f4c209efdf0..1c1886bd655e 100644 --- a/image/src/OrientedImage.h +++ b/image/src/OrientedImage.h @@ -32,9 +32,8 @@ public: NS_IMETHOD GetHeight(int32_t* aHeight) MOZ_OVERRIDE; NS_IMETHOD GetIntrinsicSize(nsSize* aSize) MOZ_OVERRIDE; NS_IMETHOD GetIntrinsicRatio(nsSize* aRatio) MOZ_OVERRIDE; - NS_IMETHOD GetFrame(uint32_t aWhichFrame, - uint32_t aFlags, - gfxASurface** _retval) MOZ_OVERRIDE; + NS_IMETHOD_(already_AddRefed) GetFrame(uint32_t aWhichFrame, + uint32_t aFlags) MOZ_OVERRIDE; NS_IMETHOD GetImageContainer(mozilla::layers::LayerManager* aManager, mozilla::layers::ImageContainer** _retval) MOZ_OVERRIDE; NS_IMETHOD Draw(gfxContext* aContext, diff --git a/image/src/RasterImage.cpp b/image/src/RasterImage.cpp index 39a8d2b0a3ce..f224c362a909 100644 --- a/image/src/RasterImage.cpp +++ b/image/src/RasterImage.cpp @@ -879,30 +879,29 @@ RasterImage::CopyFrame(uint32_t aWhichFrame, //****************************************************************************** /* [noscript] gfxASurface getFrame(in uint32_t aWhichFrame, * in uint32_t aFlags); */ -NS_IMETHODIMP +NS_IMETHODIMP_(already_AddRefed) RasterImage::GetFrame(uint32_t aWhichFrame, - uint32_t aFlags, - gfxASurface **_retval) + uint32_t aFlags) { + MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE); + if (aWhichFrame > FRAME_MAX_VALUE) - return NS_ERROR_INVALID_ARG; + return nullptr; if (mError) - return NS_ERROR_FAILURE; + return nullptr; // Disallowed in the API if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE)) - return NS_ERROR_FAILURE; - - nsresult rv = NS_OK; + return nullptr; if (!ApplyDecodeFlags(aFlags)) - return NS_ERROR_NOT_AVAILABLE; + return nullptr; // If the caller requested a synchronous decode, do it if (aFlags & FLAG_SYNC_DECODE) { - rv = SyncDecode(); - CONTAINER_ENSURE_SUCCESS(rv); + nsresult rv = SyncDecode(); + CONTAINER_ENSURE_TRUE(NS_SUCCEEDED(rv), nullptr); } // Get the frame. If it's not there, it's probably the caller's fault for @@ -912,8 +911,7 @@ RasterImage::GetFrame(uint32_t aWhichFrame, 0 : GetCurrentImgFrameIndex(); imgFrame *frame = GetDrawableImgFrame(frameIndex); if (!frame) { - *_retval = nullptr; - return NS_ERROR_FAILURE; + return nullptr; } nsRefPtr framesurf; @@ -924,19 +922,17 @@ RasterImage::GetFrame(uint32_t aWhichFrame, if (framerect.x == 0 && framerect.y == 0 && framerect.width == mSize.width && framerect.height == mSize.height) - rv = frame->GetSurface(getter_AddRefs(framesurf)); + frame->GetSurface(getter_AddRefs(framesurf)); // The image doesn't have a surface because it's been optimized away. Create // one. if (!framesurf) { nsRefPtr imgsurf; - rv = CopyFrame(aWhichFrame, aFlags, getter_AddRefs(imgsurf)); + CopyFrame(aWhichFrame, aFlags, getter_AddRefs(imgsurf)); framesurf = imgsurf; } - *_retval = framesurf.forget().get(); - - return rv; + return framesurf.forget(); } already_AddRefed @@ -949,13 +945,8 @@ RasterImage::GetCurrentImage() return nullptr; } - nsRefPtr imageSurface; - nsresult rv = GetFrame(FRAME_CURRENT, FLAG_NONE, getter_AddRefs(imageSurface)); - NS_ENSURE_SUCCESS(rv, nullptr); - - if (!imageSurface) { - return nullptr; - } + nsRefPtr imageSurface = GetFrame(FRAME_CURRENT, FLAG_NONE); + NS_ENSURE_TRUE(imageSurface, nullptr); if (!mImageContainer) { mImageContainer = LayerManager::CreateImageContainer(); diff --git a/image/src/VectorImage.cpp b/image/src/VectorImage.cpp index e99c7a1f4fc3..aaadb9cc9b1c 100644 --- a/image/src/VectorImage.cpp +++ b/image/src/VectorImage.cpp @@ -647,18 +647,17 @@ VectorImage::FrameIsOpaque(uint32_t aWhichFrame) //****************************************************************************** /* [noscript] gfxASurface getFrame(in uint32_t aWhichFrame, * in uint32_t aFlags; */ -NS_IMETHODIMP +NS_IMETHODIMP_(already_AddRefed) VectorImage::GetFrame(uint32_t aWhichFrame, - uint32_t aFlags, - gfxASurface** _retval) + uint32_t aFlags) { - NS_ENSURE_ARG_POINTER(_retval); + MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE); if (aWhichFrame > FRAME_MAX_VALUE) - return NS_ERROR_INVALID_ARG; + return nullptr; if (mError) - return NS_ERROR_FAILURE; + return nullptr; // Look up height & width // ---------------------- @@ -668,7 +667,7 @@ VectorImage::GetFrame(uint32_t aWhichFrame, !mSVGDocumentWrapper->GetWidthOrHeight(SVGDocumentWrapper::eHeight, imageIntSize.height)) { // We'll get here if our SVG doc has a percent-valued width or height. - return NS_ERROR_FAILURE; + return nullptr; } // Create a surface that we'll ultimately return @@ -689,9 +688,8 @@ VectorImage::GetFrame(uint32_t aWhichFrame, nsIntRect(nsIntPoint(0,0), imageIntSize), imageIntSize, nullptr, aWhichFrame, aFlags); - NS_ENSURE_SUCCESS(rv, rv); - *_retval = surface.forget().get(); - return rv; + NS_ENSURE_SUCCESS(rv, nullptr); + return surface.forget(); } //****************************************************************************** diff --git a/image/src/imgTools.cpp b/image/src/imgTools.cpp index 0dd5640ed8ee..30a6b5c59ea5 100644 --- a/image/src/imgTools.cpp +++ b/image/src/imgTools.cpp @@ -276,10 +276,9 @@ NS_IMETHODIMP imgTools::EncodeImageData(gfxImageSurface *aSurface, NS_IMETHODIMP imgTools::GetFirstImageFrame(imgIContainer *aContainer, gfxImageSurface **aSurface) { - nsRefPtr surface; - aContainer->GetFrame(imgIContainer::FRAME_FIRST, - imgIContainer::FLAG_SYNC_DECODE, - getter_AddRefs(surface)); + nsRefPtr surface = + aContainer->GetFrame(imgIContainer::FRAME_FIRST, + imgIContainer::FLAG_SYNC_DECODE); NS_ENSURE_TRUE(surface, NS_ERROR_NOT_AVAILABLE); nsRefPtr frame(surface->CopyToARGB32ImageSurface()); diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index aef7bb43b61a..898e2f75df38 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -4816,11 +4816,9 @@ nsLayoutUtils::SurfaceFromElement(nsIImageLoadingContent* aElement, frameFlags |= imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION; if (aSurfaceFlags & SFE_NO_PREMULTIPLY_ALPHA) frameFlags |= imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA; - nsRefPtr framesurf; - rv = imgContainer->GetFrame(whichFrame, - frameFlags, - getter_AddRefs(framesurf)); - if (NS_FAILED(rv)) + nsRefPtr framesurf = + imgContainer->GetFrame(whichFrame, frameFlags); + if (!framesurf) return result; int32_t imgWidth, imgHeight; diff --git a/widget/cocoa/nsClipboard.mm b/widget/cocoa/nsClipboard.mm index 6e8973dcd054..33f430424e4a 100644 --- a/widget/cocoa/nsClipboard.mm +++ b/widget/cocoa/nsClipboard.mm @@ -431,10 +431,9 @@ nsClipboard::PasteboardDictFromTransferable(nsITransferable* aTransferable) continue; } - nsRefPtr surface; - image->GetFrame(imgIContainer::FRAME_CURRENT, - imgIContainer::FLAG_SYNC_DECODE, - getter_AddRefs(surface)); + nsRefPtr surface = + image->GetFrame(imgIContainer::FRAME_CURRENT, + imgIContainer::FLAG_SYNC_DECODE); if (!surface) { continue; } diff --git a/widget/cocoa/nsCocoaUtils.mm b/widget/cocoa/nsCocoaUtils.mm index daa877d430a7..def55acb5f19 100644 --- a/widget/cocoa/nsCocoaUtils.mm +++ b/widget/cocoa/nsCocoaUtils.mm @@ -372,10 +372,8 @@ nsresult nsCocoaUtils::CreateNSImageFromImageContainer(imgIContainer *aImage, ui } else { - nsRefPtr surface; - aImage->GetFrame(aWhichFrame, - imgIContainer::FLAG_SYNC_DECODE, - getter_AddRefs(surface)); + nsRefPtr surface = + aImage->GetFrame(aWhichFrame, imgIContainer::FLAG_SYNC_DECODE); NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE); frame = surface->GetAsReadableARGB32ImageSurface(); diff --git a/widget/cocoa/nsMenuItemIconX.mm b/widget/cocoa/nsMenuItemIconX.mm index 2975155c0d1d..a31b0247e702 100644 --- a/widget/cocoa/nsMenuItemIconX.mm +++ b/widget/cocoa/nsMenuItemIconX.mm @@ -384,10 +384,9 @@ nsMenuItemIconX::OnStopFrame(imgIRequest* aRequest) mImageRegionRect.SetRect(0, 0, origWidth, origHeight); } - nsRefPtr surface; - imageContainer->GetFrame(imgIContainer::FRAME_CURRENT, - imgIContainer::FLAG_NONE, - getter_AddRefs(surface)); + nsRefPtr surface = + imageContainer->GetFrame(imgIContainer::FRAME_CURRENT, + imgIContainer::FLAG_NONE); if (!surface) { [mNativeMenuItem setImage:nil]; return NS_ERROR_FAILURE; diff --git a/widget/gtk/nsImageToPixbuf.cpp b/widget/gtk/nsImageToPixbuf.cpp index 35319b503c16..0acb7f787dc1 100644 --- a/widget/gtk/nsImageToPixbuf.cpp +++ b/widget/gtk/nsImageToPixbuf.cpp @@ -36,19 +36,17 @@ nsImageToPixbuf::ConvertImageToPixbuf(imgIContainer* aImage) GdkPixbuf* nsImageToPixbuf::ImageToPixbuf(imgIContainer* aImage) { - nsRefPtr surface; - aImage->GetFrame(imgIContainer::FRAME_CURRENT, - imgIContainer::FLAG_SYNC_DECODE, - getter_AddRefs(surface)); + nsRefPtr surface = + aImage->GetFrame(imgIContainer::FRAME_CURRENT, + imgIContainer::FLAG_SYNC_DECODE); // If the last call failed, it was probably because our call stack originates // in an imgINotificationObserver event, meaning that we're not allowed request // a sync decode. Presumably the originating event is something sensible like // OnStopFrame(), so we can just retry the call without a sync decode. if (!surface) - aImage->GetFrame(imgIContainer::FRAME_CURRENT, - imgIContainer::FLAG_NONE, - getter_AddRefs(surface)); + surface = aImage->GetFrame(imgIContainer::FRAME_CURRENT, + imgIContainer::FLAG_NONE); NS_ENSURE_TRUE(surface, nullptr); diff --git a/widget/os2/nsWindow.cpp b/widget/os2/nsWindow.cpp index a8245bd80deb..5c39f900f121 100644 --- a/widget/os2/nsWindow.cpp +++ b/widget/os2/nsWindow.cpp @@ -1334,10 +1334,9 @@ NS_IMETHODIMP nsWindow::SetCursor(imgIContainer* aCursor, return NS_OK; } - nsRefPtr surface; - aCursor->GetFrame(imgIContainer::FRAME_CURRENT, - imgIContainer::FLAG_SYNC_DECODE, - getter_AddRefs(surface)); + nsRefPtr surface = + aCursor->GetFrame(imgIContainer::FRAME_CURRENT, + imgIContainer::FLAG_SYNC_DECODE); NS_ENSURE_TRUE(surface, NS_ERROR_NOT_AVAILABLE); nsRefPtr frame(surface->GetAsReadableARGB32ImageSurface()); diff --git a/widget/qt/nsClipboard.cpp b/widget/qt/nsClipboard.cpp index 6fe5fd1d8951..dfabcfaf17ba 100644 --- a/widget/qt/nsClipboard.cpp +++ b/widget/qt/nsClipboard.cpp @@ -176,10 +176,9 @@ nsClipboard::SetNativeClipboardData( nsITransferable *aTransferable, if (!image) // Not getting an image for an image mime type!? continue; - nsRefPtr surface; - image->GetFrame(imgIContainer::FRAME_CURRENT, - imgIContainer::FLAG_SYNC_DECODE, - getter_AddRefs(surface)); + nsRefPtr surface = + image->GetFrame(imgIContainer::FRAME_CURRENT, + imgIContainer::FLAG_SYNC_DECODE); if (!surface) continue; diff --git a/widget/windows/WinUtils.cpp b/widget/windows/WinUtils.cpp index 7a2aafaba080..0c41149f9376 100644 --- a/widget/windows/WinUtils.cpp +++ b/widget/windows/WinUtils.cpp @@ -694,8 +694,8 @@ AsyncFaviconDataReady::OnComplete(nsIURI *aFaviconURI, getter_AddRefs(container)); NS_ENSURE_SUCCESS(rv, rv); - nsRefPtr imgFrame; - rv = container->GetFrame(imgIContainer::FRAME_FIRST, 0, getter_AddRefs(imgFrame)); + nsRefPtr imgFrame = + container->GetFrame(imgIContainer::FRAME_FIRST, 0); NS_ENSURE_SUCCESS(rv, rv); nsRefPtr imageSurface; diff --git a/widget/windows/nsImageClipboard.cpp b/widget/windows/nsImageClipboard.cpp index 230692f5242c..58f5e300b8af 100644 --- a/widget/windows/nsImageClipboard.cpp +++ b/widget/windows/nsImageClipboard.cpp @@ -116,10 +116,9 @@ nsImageToClipboard::CreateFromImage ( imgIContainer* inImage, HANDLE* outBitmap nsresult rv; *outBitmap = nullptr; - nsRefPtr surface; - inImage->GetFrame(imgIContainer::FRAME_CURRENT, - imgIContainer::FLAG_SYNC_DECODE, - getter_AddRefs(surface)); + nsRefPtr surface = + inImage->GetFrame(imgIContainer::FRAME_CURRENT, + imgIContainer::FLAG_SYNC_DECODE); NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE); nsRefPtr frame(surface->GetAsReadableARGB32ImageSurface()); diff --git a/widget/windows/nsWindowGfx.cpp b/widget/windows/nsWindowGfx.cpp index c34e87597135..e086bb613ad4 100644 --- a/widget/windows/nsWindowGfx.cpp +++ b/widget/windows/nsWindowGfx.cpp @@ -647,10 +647,9 @@ nsresult nsWindowGfx::CreateIcon(imgIContainer *aContainer, HICON *aIcon) { // Get the image data - nsRefPtr surface; - aContainer->GetFrame(imgIContainer::FRAME_CURRENT, - imgIContainer::FLAG_SYNC_DECODE, - getter_AddRefs(surface)); + nsRefPtr surface = + aContainer->GetFrame(imgIContainer::FRAME_CURRENT, + imgIContainer::FLAG_SYNC_DECODE); NS_ENSURE_TRUE(surface, NS_ERROR_NOT_AVAILABLE); nsRefPtr frame(surface->GetAsReadableARGB32ImageSurface()); From 27618ee0afd77fc4898e5a6918ce79dde9dab83f Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Fri, 13 Dec 2013 08:40:57 +0000 Subject: [PATCH 028/144] Bug 944442 follow-up to check for null instead of rv error. r=me --- widget/windows/WinUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/windows/WinUtils.cpp b/widget/windows/WinUtils.cpp index 0c41149f9376..4457d57d0bba 100644 --- a/widget/windows/WinUtils.cpp +++ b/widget/windows/WinUtils.cpp @@ -696,7 +696,7 @@ AsyncFaviconDataReady::OnComplete(nsIURI *aFaviconURI, nsRefPtr imgFrame = container->GetFrame(imgIContainer::FRAME_FIRST, 0); - NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(imgFrame, NS_ERROR_FAILURE); nsRefPtr imageSurface; gfxIntSize size; From 790a58b7dd08ae0c8bb7f06ef24893de121efde4 Mon Sep 17 00:00:00 2001 From: Terrence Cole Date: Wed, 4 Dec 2013 15:23:30 -0800 Subject: [PATCH 029/144] Bug 945250 - Evict the nursery before using CellIter; r=jonco --- js/public/GCAPI.h | 3 +++ js/src/gc/GCInternals.h | 2 +- js/src/gc/Iteration.cpp | 3 +++ js/src/gc/Nursery.cpp | 25 ++++++++++++++++--------- js/src/gc/Nursery.h | 8 ++++++++ js/src/gc/Verifier.cpp | 22 ++++++++++++++++------ js/src/jit/BaselineJIT.cpp | 10 ++++++++++ js/src/jscompartment.cpp | 8 -------- js/src/jsfriendapi.cpp | 25 ++++++++++++++++++------- js/src/jsgc.cpp | 10 +++++++++- js/src/jsgcinlines.h | 14 ++++++++++++++ js/src/jsobjinlines.h | 5 ++++- js/src/vm/Runtime.cpp | 2 +- js/src/vm/Runtime.h | 2 +- js/src/vm/Shape.cpp | 3 +++ 15 files changed, 107 insertions(+), 35 deletions(-) diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h index b37740838a9d..b48106041caa 100644 --- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -187,6 +187,9 @@ DisableGenerationalGC(JSRuntime *rt); extern JS_FRIEND_API(void) EnableGenerationalGC(JSRuntime *rt); +extern JS_FRIEND_API(bool) +IsGenerationalGCEnabled(JSRuntime *rt); + extern JS_FRIEND_API(bool) IsIncrementalBarrierNeeded(JSRuntime *rt); diff --git a/js/src/gc/GCInternals.h b/js/src/gc/GCInternals.h index d675a47c4b46..2acd4fe379bb 100644 --- a/js/src/gc/GCInternals.h +++ b/js/src/gc/GCInternals.h @@ -130,7 +130,7 @@ class AutoStopVerifyingBarriers : runtime(rt) { restartPreVerifier = !isShutdown && rt->gcVerifyPreData; - restartPostVerifier = !isShutdown && rt->gcVerifyPostData && rt->gcGenerationalEnabled; + restartPostVerifier = !isShutdown && rt->gcVerifyPostData && JS::IsGenerationalGCEnabled(rt); if (rt->gcVerifyPreData) EndVerifyPreBarriers(rt); if (rt->gcVerifyPostData) diff --git a/js/src/gc/Iteration.cpp b/js/src/gc/Iteration.cpp index 94dbbe998d5d..6b692b55f9db 100644 --- a/js/src/gc/Iteration.cpp +++ b/js/src/gc/Iteration.cpp @@ -22,6 +22,7 @@ js::TraceRuntime(JSTracer *trc) { JS_ASSERT(!IS_GC_MARKING_TRACER(trc)); + MinorGC(trc->runtime, JS::gcreason::EVICT_NURSERY); AutoPrepareForTracing prep(trc->runtime, WithAtoms); MarkRuntime(trc); } @@ -91,6 +92,7 @@ void js::IterateScripts(JSRuntime *rt, JSCompartment *compartment, void *data, IterateScriptCallback scriptCallback) { + MinorGC(rt, JS::gcreason::EVICT_NURSERY); AutoPrepareForTracing prep(rt, SkipAtoms); if (compartment) { @@ -110,6 +112,7 @@ js::IterateScripts(JSRuntime *rt, JSCompartment *compartment, void js::IterateGrayObjects(Zone *zone, GCThingCallback cellCallback, void *data) { + MinorGC(zone->runtimeFromMainThread(), JS::gcreason::EVICT_NURSERY); AutoPrepareForTracing prep(zone->runtimeFromMainThread(), SkipAtoms); for (size_t finalizeKind = 0; finalizeKind <= FINALIZE_OBJECT_LAST; finalizeKind++) { diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index 08924deaa8e1..71d38094b102 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -75,9 +75,9 @@ js::Nursery::~Nursery() void js::Nursery::enable() { + JS_ASSERT(isEmpty()); if (isEnabled()) return; - JS_ASSERT_IF(runtime()->gcZeal_ != ZealGenerationalGCValue, position_ == start()); numActiveChunks_ = 1; setCurrentChunk(0); #ifdef JS_GC_ZEAL @@ -91,11 +91,19 @@ js::Nursery::disable() { if (!isEnabled()) return; - JS_ASSERT_IF(runtime()->gcZeal_ != ZealGenerationalGCValue, position_ == start()); + JS_ASSERT(isEmpty()); numActiveChunks_ = 0; currentEnd_ = 0; } +bool +js::Nursery::isEmpty() const +{ + JS_ASSERT(runtime_); + JS_ASSERT_IF(runtime_->gcZeal_ != ZealGenerationalGCValue, currentStart_ == start()); + return !isEnabled() || position() == currentStart_; +} + void * js::Nursery::allocate(size_t size) { @@ -355,8 +363,6 @@ js::Nursery::forwardBufferPointer(HeapSlot **pSlotsElems) JS_ASSERT(!isInside(*pSlotsElems)); } -namespace { - // Structure for counting how many times objects of a particular type have been // tenured during a minor collection. struct TenureCount @@ -365,8 +371,6 @@ struct TenureCount int count; }; -} // anonymous namespace - // Keep rough track of how many times we tenure objects of particular types // during minor collections, using a fixed size hash for efficiency at the cost // of potential collisions. @@ -598,11 +602,11 @@ js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason, TypeObjectList if (!isEnabled()) return; - AutoStopVerifyingBarriers av(rt, false); - - if (position() == start()) + if (isEmpty()) return; + AutoStopVerifyingBarriers av(rt, false); + rt->gcHelperThread.waitBackgroundSweepEnd(); /* Move objects pointed to by roots from the nursery to the major heap. */ @@ -679,6 +683,9 @@ js::Nursery::sweep(JSRuntime *rt) if (currentChunk_ + 1 == NumNurseryChunks) setCurrentChunk(0); + /* Set current start position for isEmpty checks. */ + currentStart_ = position(); + return; } #endif diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h index 55680cd1adbd..a2a30bd0b91e 100644 --- a/js/src/gc/Nursery.h +++ b/js/src/gc/Nursery.h @@ -57,6 +57,7 @@ class Nursery explicit Nursery(JSRuntime *rt) : runtime_(rt), position_(0), + currentStart_(0), currentEnd_(0), currentChunk_(0), numActiveChunks_(0) @@ -69,6 +70,9 @@ class Nursery void disable(); bool isEnabled() const { return numActiveChunks_ != 0; } + /* Return true if no allocations have been made since the last collection. */ + bool isEmpty() const; + template JS_ALWAYS_INLINE bool isInside(const T *p) const { return gc::IsInsideNursery((JS::shadow::Runtime *)runtime_, p); @@ -144,6 +148,9 @@ class Nursery /* Pointer to the first unallocated byte in the nursery. */ uintptr_t position_; + /* Pointer to the logic start of the Nursery. */ + uintptr_t currentStart_; + /* Pointer to the last byte of space in the current chunk. */ uintptr_t currentEnd_; @@ -196,6 +203,7 @@ class Nursery JS_ASSERT(chunkno < numActiveChunks_); currentChunk_ = chunkno; position_ = chunk(chunkno).start(); + currentStart_ = chunk(0).start(); currentEnd_ = chunk(chunkno).end(); } diff --git a/js/src/gc/Verifier.cpp b/js/src/gc/Verifier.cpp index 1d0bd8281dd9..6e7732e507d4 100644 --- a/js/src/gc/Verifier.cpp +++ b/js/src/gc/Verifier.cpp @@ -14,6 +14,7 @@ #include "gc/GCInternals.h" #include "gc/Zone.h" +#include "js/GCAPI.h" #include "js/HashTable.h" #include "jscntxtinlines.h" @@ -365,7 +366,8 @@ typedef HashMap, SystemAllocPolicy> * The nodemap field is a hashtable that maps from the address of the GC thing * to the VerifyNode that represents it. */ -struct VerifyPreTracer : JSTracer { +struct VerifyPreTracer : JSTracer +{ /* The gcNumber when the verification began. */ uint64_t number; @@ -379,8 +381,14 @@ struct VerifyPreTracer : JSTracer { char *term; NodeMap nodemap; - VerifyPreTracer() : root(nullptr) {} - ~VerifyPreTracer() { js_free(root); } + VerifyPreTracer(JSRuntime *rt) : root(nullptr) { + JS::DisableGenerationalGC(rt); + } + + ~VerifyPreTracer() { + js_free(root); + JS::EnableGenerationalGC(runtime); + } }; /* @@ -446,7 +454,7 @@ gc::StartVerifyPreBarriers(JSRuntime *rt) if (rt->gcVerifyPreData || rt->gcIncrementalState != NO_INCREMENTAL) return; - MinorGC(rt, JS::gcreason::API); + MinorGC(rt, JS::gcreason::EVICT_NURSERY); AutoPrepareForTracing prep(rt, WithAtoms); @@ -456,7 +464,7 @@ gc::StartVerifyPreBarriers(JSRuntime *rt) for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront()) r.front()->bitmap.clear(); - VerifyPreTracer *trc = js_new(); + VerifyPreTracer *trc = js_new(rt); if (!trc) return; @@ -576,6 +584,8 @@ AssertMarkedOrAllocated(const EdgeValue &edge) void gc::EndVerifyPreBarriers(JSRuntime *rt) { + JS_ASSERT(!JS::IsGenerationalGCEnabled(rt)); + AutoPrepareForTracing prep(rt, SkipAtoms); VerifyPreTracer *trc = (VerifyPreTracer *)rt->gcVerifyPreData; @@ -658,7 +668,7 @@ gc::StartVerifyPostBarriers(JSRuntime *rt) return; } - MinorGC(rt, JS::gcreason::API); + MinorGC(rt, JS::gcreason::EVICT_NURSERY); VerifyPostTracer *trc = js_new(); if (!trc) diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index 0956e75f4ca6..36e2867a888a 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -443,6 +443,16 @@ BaselineScript::Trace(JSTracer *trc, BaselineScript *script) void BaselineScript::Destroy(FreeOp *fop, BaselineScript *script) { +#ifdef JSGC_GENERATIONAL + /* + * When the script contains pointers to nursery things, the store buffer + * will contain entries refering to the referenced things. Since we can + * destroy scripts outside the context of a GC, this situation can result + * in invalid store buffer entries. Assert that if we do destroy scripts + * outside of a GC that we at least emptied the nursery first. + */ + JS_ASSERT(fop->runtime()->gcNursery.isEmpty()); +#endif fop->delete_(script); } diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index a4132018c96f..2d1fbff3ca19 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -634,14 +634,6 @@ JSCompartment::setObjectMetadataCallback(js::ObjectMetadataCallback callback) // whether there is a creation callback. ReleaseAllJITCode(runtime_->defaultFreeOp()); - // Turn off GGC while the metadata hook is present, to prevent - // nursery-allocated metadata from being used as a lookup key in - // InitialShapeTable entries. - if (callback) - JS::DisableGenerationalGC(runtime_); - else - JS::EnableGenerationalGC(runtime_); - objectMetadataCallback = callback; } diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 5e2806f42612..e62b9dec3781 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -933,24 +933,35 @@ JS::DisableIncrementalGC(JSRuntime *rt) extern JS_FRIEND_API(void) JS::DisableGenerationalGC(JSRuntime *rt) { - rt->gcGenerationalEnabled = false; #ifdef JSGC_GENERATIONAL - MinorGC(rt, JS::gcreason::API); - rt->gcNursery.disable(); - rt->gcStoreBuffer.disable(); + if (IsGenerationalGCEnabled(rt)) { + MinorGC(rt, JS::gcreason::API); + rt->gcNursery.disable(); + rt->gcStoreBuffer.disable(); + } #endif + ++rt->gcGenerationalDisabled; } extern JS_FRIEND_API(void) JS::EnableGenerationalGC(JSRuntime *rt) { - rt->gcGenerationalEnabled = true; + JS_ASSERT(rt->gcGenerationalDisabled > 0); + --rt->gcGenerationalDisabled; #ifdef JSGC_GENERATIONAL - rt->gcNursery.enable(); - rt->gcStoreBuffer.enable(); + if (IsGenerationalGCEnabled(rt)) { + rt->gcNursery.enable(); + rt->gcStoreBuffer.enable(); + } #endif } +extern JS_FRIEND_API(bool) +JS::IsGenerationalGCEnabled(JSRuntime *rt) +{ + return rt->gcGenerationalDisabled == 0; +} + JS_FRIEND_API(bool) JS::IsIncrementalBarrierNeeded(JSRuntime *rt) { diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index dd3cde985825..b2e65166aea5 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -5267,8 +5267,16 @@ void js::ReleaseAllJITCode(FreeOp *fop) { #ifdef JS_ION - for (ZonesIter zone(fop->runtime(), SkipAtoms); !zone.done(); zone.next()) { +# ifdef JSGC_GENERATIONAL + /* + * Scripts can entrain nursery things, inserting references to the script + * into the store buffer. Clear the store buffer before discarding scripts. + */ + MinorGC(fop->runtime(), JS::gcreason::EVICT_NURSERY); +# endif + + for (ZonesIter zone(fop->runtime(), SkipAtoms); !zone.done(); zone.next()) { # ifdef DEBUG /* Assert no baseline scripts are marked as active. */ for (CellIter i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) { diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index 46c0c81d75f2..c162df0b8946 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -244,6 +244,9 @@ class CellIterUnderGC : public CellIterImpl { public: CellIterUnderGC(JS::Zone *zone, AllocKind kind) { +#ifdef JSGC_GENERATIONAL + JS_ASSERT(zone->runtimeFromAnyThread()->gcNursery.isEmpty()); +#endif JS_ASSERT(zone->runtimeFromAnyThread()->isHeapBusy()); init(zone, kind); } @@ -277,16 +280,27 @@ class CellIter : public CellIterImpl { gc::FinishBackgroundFinalize(zone->runtimeFromMainThread()); } + +#ifdef JSGC_GENERATIONAL + /* Evict the nursery before iterating so we can see all things. */ + JSRuntime *rt = zone->runtimeFromMainThread(); + if (!rt->gcNursery.isEmpty()) + MinorGC(rt, JS::gcreason::EVICT_NURSERY); +#endif + if (lists->isSynchronizedFreeList(kind)) { lists = nullptr; } else { JS_ASSERT(!zone->runtimeFromMainThread()->isHeapBusy()); lists->copyFreeListToArena(kind); } + #ifdef DEBUG + /* Assert that no GCs can occur while a CellIter is live. */ counter = &zone->runtimeFromAnyThread()->noGCOrAllocationCheck; ++*counter; #endif + init(zone, kind); } diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 4bda696f6ac1..5704017a5eb4 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -1019,8 +1019,11 @@ NewObjectMetadata(ExclusiveContext *cxArg, JSObject **pmetadata) !cx->compartment()->activeAnalysis && !cx->runtime()->mainThread.activeCompilations) { + JS::DisableGenerationalGC(cx->runtime()); gc::AutoSuppressGC suppress(cx); - return cx->compartment()->callObjectMetadataCallback(cx, pmetadata); + bool status = cx->compartment()->callObjectMetadataCallback(cx, pmetadata); + JS::EnableGenerationalGC(cx->runtime()); + return status; } } return true; diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index d9e57b8cfb26..169921adeee0 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -219,7 +219,7 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads) gcInterFrameGC(0), gcSliceBudget(SliceBudget::Unlimited), gcIncrementalEnabled(true), - gcGenerationalEnabled(true), + gcGenerationalDisabled(0), gcManipulatingDeadZones(false), gcObjectsMarkedInDeadZones(0), gcPoke(false), diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index e60ed6f0b220..0667445320bf 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -1153,7 +1153,7 @@ struct JSRuntime : public JS::shadow::Runtime, /* * GGC can be enabled from the command line while testing. */ - bool gcGenerationalEnabled; + unsigned gcGenerationalDisabled; /* * This is true if we are in the middle of a brain transplant (e.g., diff --git a/js/src/vm/Shape.cpp b/js/src/vm/Shape.cpp index 8fb1d34a2a11..f95670834921 100644 --- a/js/src/vm/Shape.cpp +++ b/js/src/vm/Shape.cpp @@ -1588,6 +1588,9 @@ EmptyShape::getInitialShape(ExclusiveContext *cx, const Class *clasp, TaggedProt { JS_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject())); JS_ASSERT_IF(parent, cx->isInsideCurrentCompartment(parent)); +#ifdef JSGC_GENERATIONAL + JS_ASSERT_IF(metadata && cx->hasNursery(), !cx->nursery().isInside(metadata)); +#endif InitialShapeSet &table = cx->compartment()->initialShapes; From bc744f6bf13d3f8c8487f6d692632fb238a77ccd Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Fri, 13 Dec 2013 09:32:19 +0000 Subject: [PATCH 030/144] Bug 945285 - Don't verify post barriers if store buffer is disabled r=sfink --- js/src/gc/Verifier.cpp | 4 +++- js/src/jit-test/tests/gc/bug-945285.js | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 js/src/jit-test/tests/gc/bug-945285.js diff --git a/js/src/gc/Verifier.cpp b/js/src/gc/Verifier.cpp index 6e7732e507d4..9cd3710e57e5 100644 --- a/js/src/gc/Verifier.cpp +++ b/js/src/gc/Verifier.cpp @@ -830,10 +830,11 @@ MaybeVerifyPreBarriers(JSRuntime *rt, bool always) static void MaybeVerifyPostBarriers(JSRuntime *rt, bool always) { +#ifdef JSGC_GENERATIONAL if (rt->gcZeal() != ZealVerifierPostValue) return; - if (rt->mainThread.suppressGC) + if (rt->mainThread.suppressGC || !rt->gcStoreBuffer.isEnabled()) return; if (VerifyPostTracer *trc = (VerifyPostTracer *)rt->gcVerifyPostData) { @@ -843,6 +844,7 @@ MaybeVerifyPostBarriers(JSRuntime *rt, bool always) EndVerifyPostBarriers(rt); } StartVerifyPostBarriers(rt); +#endif } void diff --git a/js/src/jit-test/tests/gc/bug-945285.js b/js/src/jit-test/tests/gc/bug-945285.js new file mode 100644 index 000000000000..c2d83d215f82 --- /dev/null +++ b/js/src/jit-test/tests/gc/bug-945285.js @@ -0,0 +1,3 @@ +gczeal(11); +function callback(obj) {} +setObjectMetadataCallback(callback); From 0fa113316c1441697e58148e2c52d547a984bd0b Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Fri, 13 Dec 2013 09:32:19 +0000 Subject: [PATCH 031/144] Bug 945280 - Reset nursery to initial state on disabling generational GC zeal mode r=terrence --- js/src/gc/Nursery.h | 6 ++++++ js/src/jit-test/tests/gc/bug-945280.js | 4 ++++ js/src/jsgc.cpp | 23 +++++++++++++---------- 3 files changed, 23 insertions(+), 10 deletions(-) create mode 100644 js/src/jit-test/tests/gc/bug-945280.js diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h index a2a30bd0b91e..524ba30c1fc0 100644 --- a/js/src/gc/Nursery.h +++ b/js/src/gc/Nursery.h @@ -135,6 +135,12 @@ class Nursery if (isEnabled()) numActiveChunks_ = NumNurseryChunks; } + void leaveZealMode() { + if (isEnabled()) { + JS_ASSERT(isEmpty()); + setCurrentChunk(0); + } + } #endif private: diff --git a/js/src/jit-test/tests/gc/bug-945280.js b/js/src/jit-test/tests/gc/bug-945280.js new file mode 100644 index 000000000000..48b9695d8ed4 --- /dev/null +++ b/js/src/jit-test/tests/gc/bug-945280.js @@ -0,0 +1,4 @@ +gczeal(7,1); +setObjectMetadataCallback(setObjectMetadataCallback); +gczeal(false); +var statusitems = []; diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index b2e65166aea5..909dff6c5c63 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1014,22 +1014,25 @@ PickChunk(Zone *zone) extern void js::SetGCZeal(JSRuntime *rt, uint8_t zeal, uint32_t frequency) { - if (zeal == 0) { - if (rt->gcVerifyPreData) - VerifyBarriers(rt, PreBarrierVerifier); - if (rt->gcVerifyPostData) - VerifyBarriers(rt, PostBarrierVerifier); + if (rt->gcVerifyPreData) + VerifyBarriers(rt, PreBarrierVerifier); + if (rt->gcVerifyPostData) + VerifyBarriers(rt, PostBarrierVerifier); + +#ifdef JSGC_GENERATIONAL + if (rt->gcZeal_ == ZealGenerationalGCValue) { + MinorGC(rt, JS::gcreason::DEBUG_GC); + rt->gcNursery.leaveZealMode(); } + if (zeal == ZealGenerationalGCValue) + rt->gcNursery.enterZealMode(); +#endif + bool schedule = zeal >= js::gc::ZealAllocValue; rt->gcZeal_ = zeal; rt->gcZealFrequency = frequency; rt->gcNextScheduled = schedule ? frequency : 0; - -#ifdef JSGC_GENERATIONAL - if (zeal == ZealGenerationalGCValue) - rt->gcNursery.enterZealMode(); -#endif } static bool From cd447e7fa50fcc0b22f17c508cb7af2518748e56 Mon Sep 17 00:00:00 2001 From: Simon Montagu Date: Fri, 13 Dec 2013 12:09:26 +0200 Subject: [PATCH 032/144] Add NS_FRAME_IS_BIDI bit even when short-circuiting bidi resolution. Bug 942690, r=roc --- layout/base/crashtests/942690.html | 15 +++++++++++++++ layout/base/crashtests/crashtests.list | 1 + layout/base/nsBidiPresUtils.cpp | 1 + 3 files changed, 17 insertions(+) create mode 100644 layout/base/crashtests/942690.html diff --git a/layout/base/crashtests/942690.html b/layout/base/crashtests/942690.html new file mode 100644 index 000000000000..da64dd00dc97 --- /dev/null +++ b/layout/base/crashtests/942690.html @@ -0,0 +1,15 @@ + + + + + 14 Rendering — HTML Standard + + + +
foo
+
+bar ׳
+
+ diff --git a/layout/base/crashtests/crashtests.list b/layout/base/crashtests/crashtests.list index c79e526dd48f..9dc19257ec07 100644 --- a/layout/base/crashtests/crashtests.list +++ b/layout/base/crashtests/crashtests.list @@ -422,3 +422,4 @@ load 931464.html load 936988-1.html load 931460-1.html load 935765-1.html +load 942690.html diff --git a/layout/base/nsBidiPresUtils.cpp b/layout/base/nsBidiPresUtils.cpp index 5ec7c469d9d2..6c223c742fc4 100644 --- a/layout/base/nsBidiPresUtils.cpp +++ b/layout/base/nsBidiPresUtils.cpp @@ -687,6 +687,7 @@ nsBidiPresUtils::ResolveParagraph(nsBlockFrame* aBlockFrame, printf("early return for single direction frame %p\n", (void*)frame); #endif #endif + frame->AddStateBits(NS_FRAME_IS_BIDI); return NS_OK; } } From bb725d63df84bd72e71455940eea49f5d247c24a Mon Sep 17 00:00:00 2001 From: Victor Porof Date: Fri, 13 Dec 2013 13:17:33 +0200 Subject: [PATCH 033/144] Bug 949968 - Intermittent TEST-UNEXPECTED-FAIL | chrome://mochitests/content/browser/browser/devtools/debugger/test/browser_dbg_variables-view-override-02.js | Test timed out, r=me --- .../test/browser_dbg_variables-view-override-02.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/browser/devtools/debugger/test/browser_dbg_variables-view-override-02.js b/browser/devtools/debugger/test/browser_dbg_variables-view-override-02.js index d515359c41d1..f9da58be6877 100644 --- a/browser/devtools/debugger/test/browser_dbg_variables-view-override-02.js +++ b/browser/devtools/debugger/test/browser_dbg_variables-view-override-02.js @@ -14,13 +14,13 @@ function test() { let events = win.EVENTS; let variables = win.DebuggerView.Variables; - // Allow this generator function to yield first. - executeSoon(() => debuggee.test()); - yield waitForSourceAndCaretAndScopes(panel, ".html", 23); - // Wait for the hierarchy to be committed by the VariablesViewController. let committed = promise.defer(); variables.oncommit = committed.resolve; + + // Allow this generator function to yield first. + executeSoon(() => debuggee.test()); + yield waitForSourceAndCaretAndScopes(panel, ".html", 23); yield committed.promise; let firstScope = variables.getScopeAtIndex(0); From 27f0f88f2992bb62e23dc9af2fca8f30de95469b Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Fri, 13 Dec 2013 12:46:03 +0100 Subject: [PATCH 034/144] Backed out changeset 273e7ed05fa3 (bug 949304) for Windows Bustage on a CLOSED TREE --- .../mozbuild/backend/recursivemake.py | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/python/mozbuild/mozbuild/backend/recursivemake.py b/python/mozbuild/mozbuild/backend/recursivemake.py index a909efff8ee4..c9a4000dd46a 100644 --- a/python/mozbuild/mozbuild/backend/recursivemake.py +++ b/python/mozbuild/mozbuild/backend/recursivemake.py @@ -268,25 +268,17 @@ class RecursiveMakeBackend(CommonBackend): self._ipdl_sources = set() def detailed(summary): - s = '{:d} total backend files; ' \ - '{:d} created; {:d} updated; {:d} unchanged; ' \ - '{:d} deleted; {:d} -> {:d} Makefile'.format( + s = '{:d} total backend files. {:d} created; {:d} updated; {:d} unchanged'.format( summary.created_count + summary.updated_count + - summary.unchanged_count, - summary.created_count, - summary.updated_count, - summary.unchanged_count, - summary.deleted_count, - summary.makefile_in_count, - summary.makefile_out_count) - + summary.unchanged_count, summary.created_count, + summary.updated_count, summary.unchanged_count) + if summary.deleted_count: + s+= '; {:d} deleted'.format(summary.deleted_count) return s # This is a little kludgy and could be improved with a better API. self.summary.backend_detailed_summary = types.MethodType(detailed, self.summary) - self.summary.makefile_in_count = 0 - self.summary.makefile_out_count = 0 self._test_manifests = {} @@ -672,7 +664,6 @@ class RecursiveMakeBackend(CommonBackend): if not stub: self.log(logging.DEBUG, 'substitute_makefile', {'path': makefile}, 'Substituting makefile: {path}') - self.summary.makefile_in_count += 1 for tier, skiplist in self._may_skip.items(): if tier in ('compile', 'binaries'): @@ -1113,8 +1104,6 @@ class RecursiveMakeBackend(CommonBackend): # the autogenerated one automatically. self.backend_input_files.add(obj.input_path) - self.summary.makefile_out_count += 1 - def _handle_webidl_collection(self, webidls): if not webidls.all_stems(): return From dcf3207d311b948d6f45025757873065caeee20e Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Fri, 13 Dec 2013 12:46:22 +0100 Subject: [PATCH 035/144] Backed out changeset 203e2bcc575d (bug 949875) for Windows Bustage on a CLOSED TREE --- dom/bindings/mozwebidlcodegen/__init__.py | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/dom/bindings/mozwebidlcodegen/__init__.py b/dom/bindings/mozwebidlcodegen/__init__.py index cbd6312b23d4..5516dbb3a1a0 100644 --- a/dom/bindings/mozwebidlcodegen/__init__.py +++ b/dom/bindings/mozwebidlcodegen/__init__.py @@ -24,8 +24,14 @@ from mozbuild.util import FileAvoidWrite import mozpack.path as mozpath -# There are various imports in this file in functions to avoid adding -# dependencies to config.status. See bug 949875. +import WebIDL +from Codegen import ( + CGBindingRoot, + CGEventRoot, + CGExampleRoot, + GlobalGenRoots, +) +from Configuration import Configuration class BuildResult(object): @@ -299,16 +305,11 @@ class WebIDLCodegenManager(LoggingMixin): def generate_example_files(self, interface): """Generates example files for a given interface.""" - from Codegen import CGExampleRoot - root = CGExampleRoot(self.config, interface) return self._maybe_write_codegen(root, *self._example_paths(interface)) def _parse_webidl(self): - import WebIDL - from Configuration import Configuration - self.log(logging.INFO, 'webidl_parse', {'count': len(self._input_paths)}, 'Parsing {count} WebIDL files.') @@ -327,8 +328,6 @@ class WebIDLCodegenManager(LoggingMixin): self._input_hashes = hashes def _write_global_derived(self): - from Codegen import GlobalGenRoots - things = [('declare', f) for f in self.GLOBAL_DECLARE_FILES] things.extend(('define', f) for f in self.GLOBAL_DEFINE_FILES) @@ -454,11 +453,6 @@ class WebIDLCodegenManager(LoggingMixin): return paths def _generate_build_files_for_webidl(self, filename): - from Codegen import ( - CGBindingRoot, - CGEventRoot, - ) - self.log(logging.INFO, 'webidl_generate_build_for_input', {'filename': filename}, 'Generating WebIDL files derived from {filename}') From 8167b5ca6f52a3c4ecb213a42bd7ed7fceb79edb Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Fri, 13 Dec 2013 00:03:55 +0900 Subject: [PATCH 036/144] Bug 949304 - Print Makefile counts during config.status; r=glandium We now capture and print the number of Makefile.in and the number of generated Makefile as part of config.status. This should give us a nice, easy to extract metric going forward. Pushing on a CLOSED TREE because this patch didn't deserve to get backed out. --HG-- extra : amend_source : 36028b17fd09c57455aef681bbe459891f5ed607 --- .../mozbuild/backend/recursivemake.py | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/python/mozbuild/mozbuild/backend/recursivemake.py b/python/mozbuild/mozbuild/backend/recursivemake.py index c9a4000dd46a..a909efff8ee4 100644 --- a/python/mozbuild/mozbuild/backend/recursivemake.py +++ b/python/mozbuild/mozbuild/backend/recursivemake.py @@ -268,17 +268,25 @@ class RecursiveMakeBackend(CommonBackend): self._ipdl_sources = set() def detailed(summary): - s = '{:d} total backend files. {:d} created; {:d} updated; {:d} unchanged'.format( + s = '{:d} total backend files; ' \ + '{:d} created; {:d} updated; {:d} unchanged; ' \ + '{:d} deleted; {:d} -> {:d} Makefile'.format( summary.created_count + summary.updated_count + - summary.unchanged_count, summary.created_count, - summary.updated_count, summary.unchanged_count) - if summary.deleted_count: - s+= '; {:d} deleted'.format(summary.deleted_count) + summary.unchanged_count, + summary.created_count, + summary.updated_count, + summary.unchanged_count, + summary.deleted_count, + summary.makefile_in_count, + summary.makefile_out_count) + return s # This is a little kludgy and could be improved with a better API. self.summary.backend_detailed_summary = types.MethodType(detailed, self.summary) + self.summary.makefile_in_count = 0 + self.summary.makefile_out_count = 0 self._test_manifests = {} @@ -664,6 +672,7 @@ class RecursiveMakeBackend(CommonBackend): if not stub: self.log(logging.DEBUG, 'substitute_makefile', {'path': makefile}, 'Substituting makefile: {path}') + self.summary.makefile_in_count += 1 for tier, skiplist in self._may_skip.items(): if tier in ('compile', 'binaries'): @@ -1104,6 +1113,8 @@ class RecursiveMakeBackend(CommonBackend): # the autogenerated one automatically. self.backend_input_files.add(obj.input_path) + self.summary.makefile_out_count += 1 + def _handle_webidl_collection(self, webidls): if not webidls.all_stems(): return From f47e1b29895d17dbc4ef2e6aa53967c9d3ad385c Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Fri, 13 Dec 2013 12:14:36 +0000 Subject: [PATCH 037/144] Bug 944704, part 1 - Extend Moz2D's ArcToBezier helper so that it can draw ellipse arcs in addition to circle arcs. r=Bas --- gfx/2d/Path.cpp | 2 +- gfx/2d/PathCairo.cpp | 2 +- gfx/2d/PathHelpers.h | 24 +++++++++++++----------- gfx/2d/PathSkia.cpp | 2 +- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/gfx/2d/Path.cpp b/gfx/2d/Path.cpp index 1bf3563313d5..c3902e816d94 100644 --- a/gfx/2d/Path.cpp +++ b/gfx/2d/Path.cpp @@ -121,7 +121,7 @@ void FlattenedPath::Arc(const Point &aOrigin, float aRadius, float aStartAngle, float aEndAngle, bool aAntiClockwise) { - ArcToBezier(this, aOrigin, aRadius, aStartAngle, aEndAngle, aAntiClockwise); + ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise); } Float diff --git a/gfx/2d/PathCairo.cpp b/gfx/2d/PathCairo.cpp index 63836f0bb4e5..e94b44c76e05 100644 --- a/gfx/2d/PathCairo.cpp +++ b/gfx/2d/PathCairo.cpp @@ -113,7 +113,7 @@ void PathBuilderCairo::Arc(const Point &aOrigin, float aRadius, float aStartAngle, float aEndAngle, bool aAntiClockwise) { - ArcToBezier(this, aOrigin, aRadius, aStartAngle, aEndAngle, aAntiClockwise); + ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise); } Point diff --git a/gfx/2d/PathHelpers.h b/gfx/2d/PathHelpers.h index eb8c9df43e55..7d3ef446e390 100644 --- a/gfx/2d/PathHelpers.h +++ b/gfx/2d/PathHelpers.h @@ -13,11 +13,11 @@ namespace mozilla { namespace gfx { template -void ArcToBezier(T* aSink, const Point &aOrigin, float aRadius, float aStartAngle, - float aEndAngle, bool aAntiClockwise) +void ArcToBezier(T* aSink, const Point &aOrigin, const Size &aRadius, + float aStartAngle, float aEndAngle, bool aAntiClockwise) { - Point startPoint(aOrigin.x + cos(aStartAngle) * aRadius, - aOrigin.y + sin(aStartAngle) * aRadius); + Point startPoint(aOrigin.x + cos(aStartAngle) * aRadius.width, + aOrigin.y + sin(aStartAngle) * aRadius.height); aSink->LineTo(startPoint); @@ -56,23 +56,25 @@ void ArcToBezier(T* aSink, const Point &aOrigin, float aRadius, float aStartAngl currentEndAngle = currentStartAngle + arcSweepLeft * sweepDirection; } - Point currentStartPoint(aOrigin.x + cos(currentStartAngle) * aRadius, - aOrigin.y + sin(currentStartAngle) * aRadius); - Point currentEndPoint(aOrigin.x + cos(currentEndAngle) * aRadius, - aOrigin.y + sin(currentEndAngle) * aRadius); + Point currentStartPoint(aOrigin.x + cos(currentStartAngle) * aRadius.width, + aOrigin.y + sin(currentStartAngle) * aRadius.height); + Point currentEndPoint(aOrigin.x + cos(currentEndAngle) * aRadius.width, + aOrigin.y + sin(currentEndAngle) * aRadius.height); // Calculate kappa constant for partial curve. The sign of angle in the // tangent will actually ensure this is negative for a counter clockwise // sweep, so changing signs later isn't needed. - Float kappa = (4.0f / 3.0f) * tan((currentEndAngle - currentStartAngle) / 4.0f) * aRadius; + Float kappaFactor = (4.0f / 3.0f) * tan((currentEndAngle - currentStartAngle) / 4.0f); + Float kappaX = kappaFactor * aRadius.width; + Float kappaY = kappaFactor * aRadius.height; Point tangentStart(-sin(currentStartAngle), cos(currentStartAngle)); Point cp1 = currentStartPoint; - cp1 += tangentStart * kappa; + cp1 += Point(tangentStart.x * kappaX, tangentStart.y * kappaY); Point revTangentEnd(sin(currentEndAngle), -cos(currentEndAngle)); Point cp2 = currentEndPoint; - cp2 += revTangentEnd * kappa; + cp2 += Point(revTangentEnd.x * kappaX, revTangentEnd.y * kappaY); aSink->BezierTo(cp1, cp2, currentEndPoint); diff --git a/gfx/2d/PathSkia.cpp b/gfx/2d/PathSkia.cpp index a28d96e47268..95c0e6b29664 100644 --- a/gfx/2d/PathSkia.cpp +++ b/gfx/2d/PathSkia.cpp @@ -88,7 +88,7 @@ void PathBuilderSkia::Arc(const Point &aOrigin, float aRadius, float aStartAngle, float aEndAngle, bool aAntiClockwise) { - ArcToBezier(this, aOrigin, aRadius, aStartAngle, aEndAngle, aAntiClockwise); + ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise); } Point From 911790747368ddc7ee2ffc13a964d5983769322a Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Fri, 13 Dec 2013 12:14:36 +0000 Subject: [PATCH 038/144] Bug 944704, part 2 - Update the Mozilla callers of Moz2D's ArcToBezier for its new signature. r=Bas --- content/canvas/src/CanvasRenderingContext2D.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/canvas/src/CanvasRenderingContext2D.cpp b/content/canvas/src/CanvasRenderingContext2D.cpp index 02c6f453031d..74b9992e4b39 100644 --- a/content/canvas/src/CanvasRenderingContext2D.cpp +++ b/content/canvas/src/CanvasRenderingContext2D.cpp @@ -1890,7 +1890,7 @@ CanvasRenderingContext2D::Arc(double x, double y, double r, EnsureWritablePath(); - ArcToBezier(this, Point(x, y), r, startAngle, endAngle, anticlockwise); + ArcToBezier(this, Point(x, y), Size(r, r), startAngle, endAngle, anticlockwise); } void From 609fb5fc12993e93e49dfcd1a318b6702d8ee75a Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Fri, 13 Dec 2013 12:14:37 +0000 Subject: [PATCH 039/144] Bug 944704, part 3 - Fix stroke dashing for SVG ellipses so that the dashing starts from the correct point and progresses in the correct direction. r=Bas --- content/svg/content/src/SVGEllipseElement.cpp | 11 ++++++++- layout/reftests/svg/reftest.list | 1 + layout/reftests/svg/stroke-dasharray-02.svg | 23 +++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 layout/reftests/svg/stroke-dasharray-02.svg diff --git a/content/svg/content/src/SVGEllipseElement.cpp b/content/svg/content/src/SVGEllipseElement.cpp index e405500f8416..d82d939ac39d 100644 --- a/content/svg/content/src/SVGEllipseElement.cpp +++ b/content/svg/content/src/SVGEllipseElement.cpp @@ -95,6 +95,15 @@ SVGEllipseElement::GetLengthInfo() void SVGEllipseElement::ConstructPath(gfxContext *aCtx) { + if (!aCtx->IsCairo()) { + RefPtr path = BuildPath(); + if (path) { + gfxPath gfxpath(path); + aCtx->SetPath(&gfxpath); + } + return; + } + float x, y, rx, ry; GetAnimatedLengthValues(&x, &y, &rx, &ry, nullptr); @@ -116,7 +125,7 @@ SVGEllipseElement::BuildPath() RefPtr pathBuilder = CreatePathBuilder(); - AppendEllipseToPath(pathBuilder, Point(x, y), Size(2.0*rx, 2.0*ry)); + ArcToBezier(pathBuilder.get(), Point(x, y), Size(rx, ry), 0, Float(2*M_PI), false); return pathBuilder->Finish(); } diff --git a/layout/reftests/svg/reftest.list b/layout/reftests/svg/reftest.list index 001bc0355343..4d83eed91f74 100644 --- a/layout/reftests/svg/reftest.list +++ b/layout/reftests/svg/reftest.list @@ -319,6 +319,7 @@ HTTP(..) == text-scale-02.svg text-scale-02-ref.svg HTTP(..) == text-scale-03.svg text-scale-03-ref.svg == text-stroke-scaling-01.svg text-stroke-scaling-01-ref.svg fails-if(OSX==10.8) == stroke-dasharray-01.svg stroke-dasharray-01-ref.svg # bug 896487 +== stroke-dasharray-02.svg pass.svg == stroke-dasharray-and-pathLength-01.svg pass.svg == stroke-dasharray-and-text-01.svg stroke-dasharray-and-text-01-ref.svg == stroke-dashoffset-01.svg pass.svg diff --git a/layout/reftests/svg/stroke-dasharray-02.svg b/layout/reftests/svg/stroke-dasharray-02.svg new file mode 100644 index 000000000000..ec1c449473c5 --- /dev/null +++ b/layout/reftests/svg/stroke-dasharray-02.svg @@ -0,0 +1,23 @@ + + + + Test the start point and direction of dashing on circle and ellipse + + + + + + + + + + + + + + + + From f36cf5885cddf41c32c96beba0b572a0b6b85cbe Mon Sep 17 00:00:00 2001 From: Kannan Vijayan Date: Fri, 13 Dec 2013 08:37:46 -0500 Subject: [PATCH 040/144] Bug 949496 - Correctly handle frame return values when bailing to baseline. r=jandem --- js/src/jit/BaselineBailouts.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/js/src/jit/BaselineBailouts.cpp b/js/src/jit/BaselineBailouts.cpp index 9cba919eef9c..d2fccb1da4fa 100644 --- a/js/src/jit/BaselineBailouts.cpp +++ b/js/src/jit/BaselineBailouts.cpp @@ -587,8 +587,10 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC, } } - // Second slot holds the return value. + // Make sure to add HAS_RVAL to flags here because setFlags() below + // will clobber it. returnValue = iter.read(); + flags |= BaselineFrame::HAS_RVAL; // If script maybe has an arguments object, the third slot will hold it. if (script->argumentsHasVarBinding()) { @@ -603,8 +605,7 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC, IonSpew(IonSpew_BaselineBailouts, " ReturnValue=%016llx", *((uint64_t *) &returnValue)); blFrame->setReturnValue(returnValue); - // Do not need to initialize scratchValue or returnValue fields in BaselineFrame. - + // Do not need to initialize scratchValue field in BaselineFrame. blFrame->setFlags(flags); // initArgsObjUnchecked modifies the frame's flags, so call it after setFlags. From 12453ea7a3ac4518771b167ca25edcd06a879318 Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Fri, 13 Dec 2013 13:41:46 +0000 Subject: [PATCH 041/144] Bug 944704 follow-up to mark zoomed-svg-with-viewBox-01.svg very slightly fuzzy on windows. r=orange --- layout/reftests/svg/reftest.list | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layout/reftests/svg/reftest.list b/layout/reftests/svg/reftest.list index 4d83eed91f74..ddec78aef847 100644 --- a/layout/reftests/svg/reftest.list +++ b/layout/reftests/svg/reftest.list @@ -319,7 +319,7 @@ HTTP(..) == text-scale-02.svg text-scale-02-ref.svg HTTP(..) == text-scale-03.svg text-scale-03-ref.svg == text-stroke-scaling-01.svg text-stroke-scaling-01-ref.svg fails-if(OSX==10.8) == stroke-dasharray-01.svg stroke-dasharray-01-ref.svg # bug 896487 -== stroke-dasharray-02.svg pass.svg +fuzzy-if(winWidget,12,7) == stroke-dasharray-02.svg pass.svg == stroke-dasharray-and-pathLength-01.svg pass.svg == stroke-dasharray-and-text-01.svg stroke-dasharray-and-text-01-ref.svg == stroke-dashoffset-01.svg pass.svg From c21b58cae24cc128066bab99c35c605c0bbebcfb Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Fri, 13 Dec 2013 13:43:58 +0000 Subject: [PATCH 042/144] Bug 944704 follow-up to mark zoomed-svg-with-viewBox-01.svg very slightly fuzzy on windows. r=orange --- layout/reftests/svg/moz-only/reftest.list | 2 +- layout/reftests/svg/reftest.list | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/layout/reftests/svg/moz-only/reftest.list b/layout/reftests/svg/moz-only/reftest.list index b91d2fec0deb..a21d70834598 100644 --- a/layout/reftests/svg/moz-only/reftest.list +++ b/layout/reftests/svg/moz-only/reftest.list @@ -19,4 +19,4 @@ skip-if(B2G) random-if(d2d) == feImage-zoom-01a.svg feImage-zoom-01-ref skip-if(B2G) random-if(d2d) == feImage-zoom-01b.svg feImage-zoom-01-ref.svg # bug 773482 skip-if(B2G) == foreignObject-zoom-01.svg pass.svg # bug 773482 skip-if(B2G) == zoom-invalidation-01.svg pass.svg # bug 773482 -== zoomed-svg-with-viewBox-01.svg zoomed-svg-with-viewBox-01-ref.svg +fuzzy-if(winWidget,12,7) == zoomed-svg-with-viewBox-01.svg zoomed-svg-with-viewBox-01-ref.svg diff --git a/layout/reftests/svg/reftest.list b/layout/reftests/svg/reftest.list index ddec78aef847..4d83eed91f74 100644 --- a/layout/reftests/svg/reftest.list +++ b/layout/reftests/svg/reftest.list @@ -319,7 +319,7 @@ HTTP(..) == text-scale-02.svg text-scale-02-ref.svg HTTP(..) == text-scale-03.svg text-scale-03-ref.svg == text-stroke-scaling-01.svg text-stroke-scaling-01-ref.svg fails-if(OSX==10.8) == stroke-dasharray-01.svg stroke-dasharray-01-ref.svg # bug 896487 -fuzzy-if(winWidget,12,7) == stroke-dasharray-02.svg pass.svg +== stroke-dasharray-02.svg pass.svg == stroke-dasharray-and-pathLength-01.svg pass.svg == stroke-dasharray-and-text-01.svg stroke-dasharray-and-text-01-ref.svg == stroke-dashoffset-01.svg pass.svg From 1da060fb8985be26a6af4f5ce7293ae21ad90705 Mon Sep 17 00:00:00 2001 From: Bas Schouten Date: Fri, 13 Dec 2013 15:22:37 +0100 Subject: [PATCH 043/144] Bug 946540: Deal with inflection points that all lie outside of (0, 1). r=jrmuizel --- gfx/2d/Path.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gfx/2d/Path.cpp b/gfx/2d/Path.cpp index c3902e816d94..a505d921e018 100644 --- a/gfx/2d/Path.cpp +++ b/gfx/2d/Path.cpp @@ -407,7 +407,8 @@ FlattenBezier(const BezierControlPoints &aControlPoints, return; } - if (count == 0) { + // Check that at least one of the inflection points is inside [0..1] + if (count == 0 || ((t1 < 0 || t1 > 1.0) && ((t2 < 0 || t2 > 1.0) || count == 1)) ) { FlattenBezierCurveSegment(aControlPoints, aSink, aTolerance); return; } From 912bf8a84622f675678601bc1b1fed33cae5dd06 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 13 Dec 2013 09:55:39 -0500 Subject: [PATCH 044/144] Bug 940821 - Build crashreporter in unified mode; r=ted --- toolkit/crashreporter/InjectCrashReporter.h | 5 +++++ .../crashreporter/client/crashreporter.cpp | 20 +++++++++---------- .../client/crashreporter_win.cpp | 4 ++++ toolkit/crashreporter/client/moz.build | 10 +++++----- .../client/linux/crash_generation/moz.build | 2 +- .../src/client/linux/handler/moz.build | 2 +- .../client/linux/minidump_writer/moz.build | 2 +- .../src/client/mac/crash_generation/moz.build | 2 +- .../src/client/mac/handler/moz.build | 2 +- .../google-breakpad/src/common/Makefile.in | 7 ------- .../src/common/linux/moz.build | 10 +++++++--- .../google-breakpad/src/common/mac/moz.build | 4 ++-- .../google-breakpad/src/common/moz.build | 14 ++++++------- .../src/common/solaris/moz.build | 2 +- .../google-breakpad/src/processor/moz.build | 2 +- toolkit/crashreporter/moz.build | 6 +++--- toolkit/crashreporter/test/moz.build | 2 +- 17 files changed, 50 insertions(+), 46 deletions(-) diff --git a/toolkit/crashreporter/InjectCrashReporter.h b/toolkit/crashreporter/InjectCrashReporter.h index c315277f5e6a..38c62acfc38b 100644 --- a/toolkit/crashreporter/InjectCrashReporter.h +++ b/toolkit/crashreporter/InjectCrashReporter.h @@ -3,6 +3,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef InjectCrashReporter_h +#define InjectCrashReporter_h + #include "nsThreadUtils.h" #include @@ -21,3 +24,5 @@ private: }; } // Namespace mozilla + +#endif diff --git a/toolkit/crashreporter/client/crashreporter.cpp b/toolkit/crashreporter/client/crashreporter.cpp index 5e43ea0bdce7..d00311ed933e 100644 --- a/toolkit/crashreporter/client/crashreporter.cpp +++ b/toolkit/crashreporter/client/crashreporter.cpp @@ -36,7 +36,7 @@ int gArgc; char** gArgv; static auto_ptr gLogStream(nullptr); -static string gDumpFile; +static string gReporterDumpFile; static string gExtraFile; static string kExtraDataExtension = ".extra"; @@ -323,8 +323,8 @@ void DeleteDump() { const char* noDelete = getenv("MOZ_CRASHREPORTER_NO_DELETE_DUMP"); if (!noDelete || *noDelete == '\0') { - if (!gDumpFile.empty()) - UIDeleteFile(gDumpFile); + if (!gReporterDumpFile.empty()) + UIDeleteFile(gReporterDumpFile); if (!gExtraFile.empty()) UIDeleteFile(gExtraFile); } @@ -337,7 +337,7 @@ void SendCompleted(bool success, const string& serverResponse) DeleteDump(); } else { - string directory = gDumpFile; + string directory = gReporterDumpFile; int slashpos = directory.find_last_of("/\\"); if (slashpos < 2) return; @@ -446,14 +446,14 @@ int main(int argc, char** argv) return 0; if (argc > 1) { - gDumpFile = argv[1]; + gReporterDumpFile = argv[1]; } - if (gDumpFile.empty()) { + if (gReporterDumpFile.empty()) { // no dump file specified, run the default UI UIShowDefaultUI(); } else { - gExtraFile = GetExtraDataFilename(gDumpFile); + gExtraFile = GetExtraDataFilename(gReporterDumpFile); if (gExtraFile.empty()) { UIError(gStrings[ST_ERROR_BADARGUMENTS]); return 0; @@ -514,13 +514,13 @@ int main(int argc, char** argv) OpenLogFile(); - if (!UIFileExists(gDumpFile)) { + if (!UIFileExists(gReporterDumpFile)) { UIError(gStrings[ST_ERROR_DUMPFILEEXISTS]); return 0; } string pendingDir = gSettingsPath + UI_DIR_SEPARATOR + "pending"; - if (!MoveCrashData(pendingDir, gDumpFile, gExtraFile)) { + if (!MoveCrashData(pendingDir, gReporterDumpFile, gExtraFile)) { return 0; } @@ -575,7 +575,7 @@ int main(int argc, char** argv) return 0; } - if (!UIShowCrashUI(gDumpFile, queryParameters, sendURL, restartArgs)) + if (!UIShowCrashUI(gReporterDumpFile, queryParameters, sendURL, restartArgs)) DeleteDump(); } diff --git a/toolkit/crashreporter/client/crashreporter_win.cpp b/toolkit/crashreporter/client/crashreporter_win.cpp index 69c1864164d5..3f2f597cf60f 100644 --- a/toolkit/crashreporter/client/crashreporter_win.cpp +++ b/toolkit/crashreporter/client/crashreporter_win.cpp @@ -35,6 +35,10 @@ #define WM_UPLOADCOMPLETE WM_APP +// Thanks, Windows.h :( +#undef min +#undef max + using std::string; using std::wstring; using std::map; diff --git a/toolkit/crashreporter/client/moz.build b/toolkit/crashreporter/client/moz.build index bc2d59715c0b..e5b938bb37fd 100644 --- a/toolkit/crashreporter/client/moz.build +++ b/toolkit/crashreporter/client/moz.build @@ -8,18 +8,18 @@ if CONFIG['OS_TARGET'] != 'Android': PROGRAM = 'crashreporter' # The xpcshell test case here verifies that the CA certificate list -SOURCES += [ +UNIFIED_SOURCES += [ 'crashreporter.cpp', ] if CONFIG['OS_ARCH'] == 'WINNT': - SOURCES += [ + UNIFIED_SOURCES += [ 'crashreporter_win.cpp', ] DEFINES['UNICODE'] = True DEFINES['_UNICODE'] = True elif CONFIG['OS_ARCH'] == 'Darwin': - SOURCES += [ + UNIFIED_SOURCES += [ 'crashreporter_unix_common.cpp', ] elif CONFIG['OS_ARCH'] == 'SunOS': @@ -29,13 +29,13 @@ elif CONFIG['OS_ARCH'] == 'SunOS': ] if CONFIG['MOZ_ENABLE_GTK']: - SOURCES += [ + UNIFIED_SOURCES += [ 'crashreporter_gtk_common.cpp', 'crashreporter_linux.cpp', 'crashreporter_unix_common.cpp' ] if CONFIG['OS_ARCH'] == 'Darwin': - SOURCES += [ + UNIFIED_SOURCES += [ 'crashreporter_osx.mm', ] diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/moz.build b/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/moz.build index 80b84c416e7f..99439b9ff769 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/moz.build +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/moz.build @@ -4,7 +4,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -SOURCES += [ +UNIFIED_SOURCES += [ 'crash_generation_client.cc', 'crash_generation_server.cc', ] diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/moz.build b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/moz.build index 837b28422ac9..934f2cb58c29 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/moz.build +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/moz.build @@ -4,7 +4,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -SOURCES += [ +UNIFIED_SOURCES += [ '../log/log.cc', 'exception_handler.cc', 'minidump_descriptor.cc', diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/moz.build b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/moz.build index 13e04e1651d0..7fb163eb5c2c 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/moz.build +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/moz.build @@ -4,7 +4,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -SOURCES += [ +UNIFIED_SOURCES += [ 'linux_dumper.cc', 'linux_ptrace_dumper.cc', 'minidump_writer.cc', diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/moz.build b/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/moz.build index 1667edeb8220..ef24c724a105 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/moz.build +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/moz.build @@ -4,7 +4,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -SOURCES += [ +UNIFIED_SOURCES += [ 'crash_generation_client.cc', 'crash_generation_server.cc', ] diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/moz.build b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/moz.build index 44696c5f941f..e5938e280e14 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/moz.build +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/moz.build @@ -4,7 +4,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -SOURCES += [ +UNIFIED_SOURCES += [ 'breakpad_nlist_64.cc', 'dynamic_images.cc', 'exception_handler.cc', diff --git a/toolkit/crashreporter/google-breakpad/src/common/Makefile.in b/toolkit/crashreporter/google-breakpad/src/common/Makefile.in index 9c463d44d21d..38a65ab2a880 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/Makefile.in +++ b/toolkit/crashreporter/google-breakpad/src/common/Makefile.in @@ -25,10 +25,3 @@ OS_CXXFLAGS += -DHAVE_MACH_O_NLIST_H endif include $(topsrcdir)/config/rules.mk - -ifneq (WINNT,$(OS_TARGET)) -# Headers from this directory are included as "common/header.h". Having -# -I$(srcdir) on the command line makes us use common/memory.h when -# is included from system headers, which is not intended. -INCLUDES = $(LOCAL_INCLUDES) -I$(DIST)/include -endif diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/moz.build b/toolkit/crashreporter/google-breakpad/src/common/linux/moz.build index 9af1365e8a56..d54190456f26 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/moz.build +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/moz.build @@ -4,17 +4,21 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -SOURCES += [ +UNIFIED_SOURCES += [ 'elfutils.cc', - 'file_id.cc', 'guid_creator.cc', 'linux_libc_support.cc', 'memory_mapped_file.cc', 'safe_readlink.cc', ] +# file_id.cc cannot be built in unified mode because it uses a custom STL_FLAGS +SOURCES += [ + 'file_id.cc', +] + if CONFIG['OS_TARGET'] != 'Android': - SOURCES += [ + UNIFIED_SOURCES += [ 'http_upload.cc', ] diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/moz.build b/toolkit/crashreporter/google-breakpad/src/common/mac/moz.build index 86fb60c332ac..bc785a110583 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/mac/moz.build +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/moz.build @@ -4,7 +4,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -SOURCES += [ +UNIFIED_SOURCES += [ 'arch_utilities.cc', 'bootstrap_compat.cc', 'file_id.cc', @@ -19,7 +19,7 @@ SOURCES += [ # The host lib is used for dump_syms, and the target lib for the # crash reporter client. Therefore, we don't need all the srcs in both. if CONFIG['MOZ_CRASHREPORTER']: - HOST_SOURCES += SOURCES + HOST_SOURCES += UNIFIED_SOURCES HOST_SOURCES += [ 'dump_syms.mm', ] diff --git a/toolkit/crashreporter/google-breakpad/src/common/moz.build b/toolkit/crashreporter/google-breakpad/src/common/moz.build index 4b5f4d0b3dd3..37a5d9350ea0 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/moz.build +++ b/toolkit/crashreporter/google-breakpad/src/common/moz.build @@ -7,11 +7,8 @@ if CONFIG['OS_ARCH'] in ('Darwin', 'Linux'): DIRS += ['dwarf'] -SOURCES += [ +UNIFIED_SOURCES += [ 'convert_UTF.c', -] - -SOURCES += [ 'logging.cc', 'module.cc', 'pathname_stripper.cc', @@ -20,7 +17,7 @@ SOURCES += [ ] if CONFIG['OS_TARGET'] != 'WINNT': - SOURCES += [ + UNIFIED_SOURCES += [ 'arm_ex_reader.cc', 'arm_ex_to_module.cc', 'dwarf/bytereader.cc', @@ -34,7 +31,7 @@ if CONFIG['OS_TARGET'] != 'WINNT': ] if CONFIG['OS_ARCH'] == 'Linux': - SOURCES += [ + UNIFIED_SOURCES += [ 'linux/dump_symbols.cc', 'linux/elf_symbols_to_module.cc', ] @@ -43,7 +40,7 @@ if CONFIG['OS_TARGET'] == 'Android': pass else: if CONFIG['OS_TARGET'] != 'WINNT': - SOURCES += [ + UNIFIED_SOURCES += [ 'stabs_reader.cc', 'stabs_to_module.cc', ] @@ -69,11 +66,12 @@ if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_CRASHREPORTER']: ] if CONFIG['OS_ARCH'] == 'Darwin': - SOURCES += [ + UNIFIED_SOURCES += [ 'mac/dump_syms.mm', ] if CONFIG['OS_TARGET'] == 'Android': + # We don't support unifying assembly files. SOURCES += [ 'android/breakpad_getcontext.S', ] diff --git a/toolkit/crashreporter/google-breakpad/src/common/solaris/moz.build b/toolkit/crashreporter/google-breakpad/src/common/solaris/moz.build index c84c532f7ef0..d32c5f6d4694 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/solaris/moz.build +++ b/toolkit/crashreporter/google-breakpad/src/common/solaris/moz.build @@ -4,7 +4,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -SOURCES += [ +UNIFIED_SOURCES += [ 'dump_symbols.cc', 'file_id.cc', 'guid_creator.cc', diff --git a/toolkit/crashreporter/google-breakpad/src/processor/moz.build b/toolkit/crashreporter/google-breakpad/src/processor/moz.build index d1fb342f6e68..2812b12d78d2 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/moz.build +++ b/toolkit/crashreporter/google-breakpad/src/processor/moz.build @@ -4,7 +4,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -SOURCES += [ +UNIFIED_SOURCES += [ 'basic_code_modules.cc', 'basic_source_line_resolver.cc', 'call_stack.cc', diff --git a/toolkit/crashreporter/moz.build b/toolkit/crashreporter/moz.build index c09f9dcddf6e..312a81d52a95 100644 --- a/toolkit/crashreporter/moz.build +++ b/toolkit/crashreporter/moz.build @@ -50,7 +50,7 @@ DIRS += ['client'] if CONFIG['MOZ_CRASHREPORTER_INJECTOR']: DIRS += ['injector'] - SOURCES += [ + UNIFIED_SOURCES += [ 'InjectCrashReporter.cpp', 'LoadLibraryRemote.cpp', ] @@ -61,12 +61,12 @@ EXPORTS += [ 'nsExceptionHandler.h', ] -SOURCES += [ +UNIFIED_SOURCES += [ 'nsExceptionHandler.cpp', ] if CONFIG['OS_ARCH'] == 'Darwin': - SOURCES += [ + UNIFIED_SOURCES += [ 'mac_utils.mm', ] diff --git a/toolkit/crashreporter/test/moz.build b/toolkit/crashreporter/test/moz.build index d4a143826186..bea32bc8813b 100644 --- a/toolkit/crashreporter/test/moz.build +++ b/toolkit/crashreporter/test/moz.build @@ -8,7 +8,7 @@ NO_DIST_INSTALL = True XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini', 'unit_ipc/xpcshell.ini'] BROWSER_CHROME_MANIFESTS += ['browser/browser.ini'] -SOURCES += [ +UNIFIED_SOURCES += [ '../google-breakpad/src/common/logging.cc', '../google-breakpad/src/common/pathname_stripper.cc', '../google-breakpad/src/processor/basic_code_modules.cc', From 501e006ac9362fe93ae27b0d085ccf94437a0a52 Mon Sep 17 00:00:00 2001 From: Julian Seward Date: Fri, 13 Dec 2013 12:40:24 +0100 Subject: [PATCH 045/144] Bug 867597 - IonMonkey: ARM hwcaps detection depends on uninitialised garbage on the stack. r=mjrosenb --HG-- extra : rebase_source : aaa617de72c55552bd63f23d392e25785c1bf65a --- js/src/jit/arm/Architecture-arm.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/src/jit/arm/Architecture-arm.cpp b/js/src/jit/arm/Architecture-arm.cpp index 2c21891fba82..952c830f0e0d 100644 --- a/js/src/jit/arm/Architecture-arm.cpp +++ b/js/src/jit/arm/Architecture-arm.cpp @@ -71,7 +71,8 @@ uint32_t GetARMFlags() return false; char buf[1024]; - fread(buf, sizeof(char), sizeof(buf), fp); + memset(buf, 0, sizeof(buf)); + fread(buf, sizeof(char), sizeof(buf)-1, fp); fclose(fp); if (strstr(buf, "vfp")) flags |= HWCAP_VFP; From 5477bd694306033337486f8e5c151aaa01ef3e41 Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Fri, 13 Dec 2013 16:36:29 +0200 Subject: [PATCH 046/144] Bug 944847, don't try to compile event handlers for data documents, r=bz --HG-- extra : rebase_source : 2b4ce65fad2433475f4b50ccd89b4301cee6c8b2 --- content/events/src/nsEventListenerManager.cpp | 4 +- content/events/test/mochitest.ini | 1 + content/events/test/test_bug944847.html | 42 +++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 content/events/test/test_bug944847.html diff --git a/content/events/src/nsEventListenerManager.cpp b/content/events/src/nsEventListenerManager.cpp index bcdda331095b..5fbb90c768c0 100644 --- a/content/events/src/nsEventListenerManager.cpp +++ b/content/events/src/nsEventListenerManager.cpp @@ -1347,7 +1347,9 @@ nsEventListenerManager::GetScriptGlobalAndDocument(nsIDocument** aDoc) // XXX sXBL/XBL2 issue -- do we really want the owner here? What // if that's the XBL document? doc = node->OwnerDoc(); - MOZ_ASSERT(!doc->IsLoadedAsData(), "Should not get in here at all"); + if (doc->IsLoadedAsData()) { + return nullptr; + } // We want to allow compiling an event handler even in an unloaded // document, so use GetScopeObject here, not GetScriptHandlingObject. diff --git a/content/events/test/mochitest.ini b/content/events/test/mochitest.ini index 53e20c50f22f..c42d5277e837 100644 --- a/content/events/test/mochitest.ini +++ b/content/events/test/mochitest.ini @@ -84,6 +84,7 @@ skip-if = true # Disabled due to timeouts. [test_bug855741.html] [test_bug864040.html] [test_bug930374-content.html] +[test_bug944847.html] skip-if = toolkit == "gonk" [test_clickevent_on_input.html] [test_continuous_wheel_events.html] diff --git a/content/events/test/test_bug944847.html b/content/events/test/test_bug944847.html new file mode 100644 index 000000000000..105c0d5c93dd --- /dev/null +++ b/content/events/test/test_bug944847.html @@ -0,0 +1,42 @@ + + + + + + Test for Bug 944847 + + + + + +Mozilla Bug 944847 +

+ +
+
+ + From 9d189300bb7c81e32df5dc4eb616f6c00611e86e Mon Sep 17 00:00:00 2001 From: Alexander Surkov Date: Fri, 13 Dec 2013 11:26:35 -0500 Subject: [PATCH 047/144] Bug 943612 - don't use GetPosAndText to convert offsets to DOM range, r=tbsaunde --- .../src/generic/HyperTextAccessible.cpp | 93 +++++++++++-------- accessible/src/generic/HyperTextAccessible.h | 26 ++++-- 2 files changed, 73 insertions(+), 46 deletions(-) diff --git a/accessible/src/generic/HyperTextAccessible.cpp b/accessible/src/generic/HyperTextAccessible.cpp index 3770733b8e00..3baed7afd17c 100644 --- a/accessible/src/generic/HyperTextAccessible.cpp +++ b/accessible/src/generic/HyperTextAccessible.cpp @@ -548,14 +548,34 @@ HyperTextAccessible::DOMPointToHypertextOffset(nsINode* aNode, return nullptr; } -nsresult -HyperTextAccessible::HypertextOffsetsToDOMRange(int32_t aStartHTOffset, - int32_t aEndHTOffset, - nsRange* aRange) +bool +HyperTextAccessible::OffsetsToDOMRange(int32_t aStartOffset, int32_t aEndOffset, + nsRange* aRange) { - // If the given offsets are 0 and associated editor is empty then return - // collapsed range with editor root element as range container. - if (aStartHTOffset == 0 && aEndHTOffset == 0) { + DOMPoint startPoint = OffsetToDOMPoint(aStartOffset); + if (!startPoint.node) + return false; + + aRange->SetStart(startPoint.node, startPoint.idx); + if (aStartOffset == aEndOffset) { + aRange->SetEnd(startPoint.node, startPoint.idx); + return true; + } + + DOMPoint endPoint = OffsetToDOMPoint(aEndOffset); + if (!endPoint.node) + return false; + + aRange->SetEnd(endPoint.node, endPoint.idx); + return true; +} + +DOMPoint +HyperTextAccessible::OffsetToDOMPoint(int32_t aOffset) +{ + // 0 offset is valid even if no children. In this case the associated editor + // is empty so return a DOM point for editor root element. + if (aOffset == 0) { nsCOMPtr editor = GetEditor(); if (editor) { bool isEmpty = false; @@ -565,40 +585,36 @@ HyperTextAccessible::HypertextOffsetsToDOMRange(int32_t aStartHTOffset, editor->GetRootElement(getter_AddRefs(editorRootElm)); nsCOMPtr editorRoot(do_QueryInterface(editorRootElm)); - if (editorRoot) { - aRange->SetStart(editorRoot, 0); - aRange->SetEnd(editorRoot, 0); - - return NS_OK; - } + return DOMPoint(editorRoot, 0); } } } - nsRefPtr startAcc, endAcc; - int32_t startOffset = aStartHTOffset, endOffset = aEndHTOffset; - nsIFrame *startFrame = nullptr, *endFrame = nullptr; + int32_t childIdx = GetChildIndexAtOffset(aOffset); + if (childIdx == -1) + return DOMPoint(); - startFrame = GetPosAndText(startOffset, endOffset, nullptr, &endFrame, - getter_AddRefs(startAcc), getter_AddRefs(endAcc)); - if (!startAcc || !endAcc) - return NS_ERROR_FAILURE; + Accessible* child = GetChildAt(childIdx); + int32_t innerOffset = aOffset - GetChildOffset(childIdx); - DOMPoint startPoint, endPoint; - nsresult rv = GetDOMPointByFrameOffset(startFrame, startOffset, startAcc, - &startPoint); - NS_ENSURE_SUCCESS(rv, rv); + // A text leaf case. The point is inside the text node. + if (child->IsTextLeaf()) { + nsIContent* content = child->GetContent(); + int32_t idx = 0; + if (NS_FAILED(RenderedToContentOffset(content->GetPrimaryFrame(), + innerOffset, &idx))) + return DOMPoint(); - rv = aRange->SetStart(startPoint.node, startPoint.idx); - NS_ENSURE_SUCCESS(rv, rv); + return DOMPoint(content, idx); + } - if (aStartHTOffset == aEndHTOffset) - return aRange->SetEnd(startPoint.node, startPoint.idx); - - rv = GetDOMPointByFrameOffset(endFrame, endOffset, endAcc, &endPoint); - NS_ENSURE_SUCCESS(rv, rv); - - return aRange->SetEnd(endPoint.node, endPoint.idx); + // Case of embedded object. The point is either before or after the element. + NS_ASSERTION(innerOffset == 0 || innerOffset == 1, "A wrong inner offset!"); + nsINode* node = child->GetNode(); + nsINode* parentNode = node->GetParentNode(); + return parentNode ? + DOMPoint(parentNode, parentNode->IndexOf(node) + innerOffset) : + DOMPoint(); } int32_t @@ -1580,7 +1596,8 @@ HyperTextAccessible::SetSelectionBoundsAt(int32_t aSelectionNum, if (!range) return false; - HypertextOffsetsToDOMRange(startOffset, endOffset, range); + if (!OffsetsToDOMRange(startOffset, endOffset, range)) + return false; // If new range was created then add it, otherwise notify selection listeners // that existing selection range was changed. @@ -1610,8 +1627,7 @@ HyperTextAccessible::ScrollSubstringTo(int32_t aStartOffset, int32_t aEndOffset, uint32_t aScrollType) { nsRefPtr range = new nsRange(mContent); - nsresult rv = HypertextOffsetsToDOMRange(aStartOffset, aEndOffset, range); - if (NS_SUCCEEDED(rv)) + if (OffsetsToDOMRange(aStartOffset, aEndOffset, range)) nsCoreUtils::ScrollSubstringTo(GetFrame(), range, aScrollType); } @@ -1629,8 +1645,7 @@ HyperTextAccessible::ScrollSubstringToPoint(int32_t aStartOffset, this); nsRefPtr range = new nsRange(mContent); - nsresult rv = HypertextOffsetsToDOMRange(aStartOffset, aEndOffset, range); - if (NS_FAILED(rv)) + if (!OffsetsToDOMRange(aStartOffset, aEndOffset, range)) return; nsPresContext* presContext = frame->PresContext(); @@ -1658,7 +1673,7 @@ HyperTextAccessible::ScrollSubstringToPoint(int32_t aStartOffset, int16_t hPercent = offsetPointX * 100 / size.width; int16_t vPercent = offsetPointY * 100 / size.height; - rv = nsCoreUtils::ScrollSubstringTo(frame, range, vPercent, hPercent); + nsresult rv = nsCoreUtils::ScrollSubstringTo(frame, range, vPercent, hPercent); if (NS_FAILED(rv)) return; diff --git a/accessible/src/generic/HyperTextAccessible.h b/accessible/src/generic/HyperTextAccessible.h index b7b33fa378d1..93396c119076 100644 --- a/accessible/src/generic/HyperTextAccessible.h +++ b/accessible/src/generic/HyperTextAccessible.h @@ -17,6 +17,9 @@ namespace mozilla { namespace a11y { struct DOMPoint { + DOMPoint() : node(nullptr), idx(0) { } + DOMPoint(nsINode* aNode, int32_t aIdx) : node(aNode), idx(aIdx) { } + nsINode* node; int32_t idx; }; @@ -128,15 +131,24 @@ public: bool aIsEndOffset = false) const; /** - * Turn a start and end hypertext offsets into DOM range. + * Convert start and end hypertext offsets into DOM range. * - * @param aStartHTOffset [in] the given start hypertext offset - * @param aEndHTOffset [in] the given end hypertext offset - * @param aRange [out] the range whose bounds to set + * @param aStartOffset [in] the given start hypertext offset + * @param aEndOffset [in] the given end hypertext offset + * @param aRange [in, out] the range whose bounds to set + * @return true if conversion was successful */ - nsresult HypertextOffsetsToDOMRange(int32_t aStartHTOffset, - int32_t aEndHTOffset, - nsRange* aRange); + bool OffsetsToDOMRange(int32_t aStartOffset, int32_t aEndOffset, + nsRange* aRange); + + /** + * Convert the given offset into DOM point. + * + * If offset is at text leaf then DOM point is (text node, offsetInTextNode), + * if before embedded object then (parent node, indexInParent), if after then + * (parent node, indexInParent + 1). + */ + DOMPoint OffsetToDOMPoint(int32_t aOffset); /** * Return true if the used ARIA role (if any) allows the hypertext accessible From 7185e14125d3ca3e26b667aa6799c616fff38d31 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 13 Dec 2013 08:27:46 -0800 Subject: [PATCH 048/144] Bug 949668 - SpiderMonkey: Rename MoveResolver::Move to MoveOp. r=jandem --- js/src/jit/CodeGenerator.cpp | 4 +- js/src/jit/MoveResolver.cpp | 2 +- js/src/jit/MoveResolver.h | 286 +++++++++--------- js/src/jit/arm/CodeGenerator-arm.cpp | 2 - js/src/jit/arm/CodeGenerator-arm.h | 2 +- js/src/jit/arm/MacroAssembler-arm.cpp | 12 +- js/src/jit/arm/MacroAssembler-arm.h | 5 +- js/src/jit/arm/MoveEmitter-arm.cpp | 12 +- js/src/jit/arm/MoveEmitter-arm.h | 9 +- js/src/jit/arm/Trampoline-arm.cpp | 2 - .../jit/shared/CodeGenerator-x86-shared.cpp | 2 - js/src/jit/shared/CodeGenerator-x86-shared.h | 2 +- js/src/jit/shared/MoveEmitter-x86-shared.cpp | 16 +- js/src/jit/shared/MoveEmitter-x86-shared.h | 7 +- js/src/jit/x64/MacroAssembler-x64.cpp | 6 +- js/src/jit/x64/MacroAssembler-x64.h | 3 - js/src/jit/x64/Trampoline-x64.cpp | 2 - js/src/jit/x86/MacroAssembler-x86.cpp | 4 +- js/src/jit/x86/MacroAssembler-x86.h | 3 - js/src/jit/x86/Trampoline-x86.cpp | 2 - 20 files changed, 178 insertions(+), 205 deletions(-) diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index b9c2d6bd1525..b6fac075a84a 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -1183,9 +1183,7 @@ CodeGenerator::visitMoveGroup(LMoveGroup *group) JS_ASSERT(!from->isConstant()); JS_ASSERT(from->isDouble() == to->isDouble()); - MoveResolver::Move::Kind kind = from->isDouble() - ? MoveResolver::Move::DOUBLE - : MoveResolver::Move::GENERAL; + MoveOp::Kind kind = from->isDouble() ? MoveOp::DOUBLE : MoveOp::GENERAL; if (!resolver.addMove(toMoveOperand(from), toMoveOperand(to), kind)) return false; diff --git a/js/src/jit/MoveResolver.cpp b/js/src/jit/MoveResolver.cpp index 522bd3b17470..4354f24d8ef5 100644 --- a/js/src/jit/MoveResolver.cpp +++ b/js/src/jit/MoveResolver.cpp @@ -21,7 +21,7 @@ MoveResolver::resetState() } bool -MoveResolver::addMove(const MoveOperand &from, const MoveOperand &to, Move::Kind kind) +MoveResolver::addMove(const MoveOperand &from, const MoveOperand &to, MoveOp::Kind kind) { // Assert that we're not doing no-op moves. JS_ASSERT(!(from == to)); diff --git a/js/src/jit/MoveResolver.h b/js/src/jit/MoveResolver.h index 601113b8ac01..bbe6f1201a32 100644 --- a/js/src/jit/MoveResolver.h +++ b/js/src/jit/MoveResolver.h @@ -14,156 +14,156 @@ namespace js { namespace jit { +// This is similar to Operand, but carries more information. We're also not +// guaranteed that Operand looks like this on all ISAs. +class MoveOperand +{ + enum Kind { + REG, + FLOAT_REG, + ADDRESS, + FLOAT_ADDRESS, + EFFECTIVE_ADDRESS + }; + + Kind kind_; + uint32_t code_; + int32_t disp_; + + public: + enum AddressKind { + MEMORY = ADDRESS, + EFFECTIVE = EFFECTIVE_ADDRESS, + FLOAT = FLOAT_ADDRESS + }; + + MoveOperand() + { } + explicit MoveOperand(const Register ®) : kind_(REG), code_(reg.code()) + { } + explicit MoveOperand(const FloatRegister ®) : kind_(FLOAT_REG), code_(reg.code()) + { } + MoveOperand(const Register ®, int32_t disp, AddressKind addrKind = MEMORY) + : kind_((Kind) addrKind), + code_(reg.code()), + disp_(disp) + { + // With a zero offset, this is a plain reg-to-reg move. + if (disp == 0 && addrKind == EFFECTIVE) + kind_ = REG; + } + MoveOperand(const MoveOperand &other) + : kind_(other.kind_), + code_(other.code_), + disp_(other.disp_) + { } + bool isFloatReg() const { + return kind_ == FLOAT_REG; + } + bool isGeneralReg() const { + return kind_ == REG; + } + bool isDouble() const { + return kind_ == FLOAT_REG || kind_ == FLOAT_ADDRESS; + } + bool isMemory() const { + return kind_ == ADDRESS; + } + bool isFloatAddress() const { + return kind_ == FLOAT_ADDRESS; + } + bool isEffectiveAddress() const { + return kind_ == EFFECTIVE_ADDRESS; + } + Register reg() const { + JS_ASSERT(isGeneralReg()); + return Register::FromCode(code_); + } + FloatRegister floatReg() const { + JS_ASSERT(isFloatReg()); + return FloatRegister::FromCode(code_); + } + Register base() const { + JS_ASSERT(isMemory() || isEffectiveAddress() || isFloatAddress()); + return Register::FromCode(code_); + } + int32_t disp() const { + return disp_; + } + + bool operator ==(const MoveOperand &other) const { + if (kind_ != other.kind_) + return false; + if (code_ != other.code_) + return false; + if (isMemory() || isEffectiveAddress()) + return disp_ == other.disp_; + return true; + } + bool operator !=(const MoveOperand &other) const { + return !operator==(other); + } +}; + +// This represents a move operation. +class MoveOp +{ + protected: + MoveOperand from_; + MoveOperand to_; + bool cycle_; + + public: + enum Kind { + GENERAL, + DOUBLE + }; + + protected: + Kind kind_; + + public: + MoveOp() + { } + MoveOp(const MoveOp &other) + : from_(other.from_), + to_(other.to_), + cycle_(other.cycle_), + kind_(other.kind_) + { } + MoveOp(const MoveOperand &from, const MoveOperand &to, Kind kind, bool cycle = false) + : from_(from), + to_(to), + cycle_(cycle), + kind_(kind) + { } + + bool inCycle() const { + return cycle_; + } + const MoveOperand &from() const { + return from_; + } + const MoveOperand &to() const { + return to_; + } + Kind kind() const { + return kind_; + } +}; + class MoveResolver { - public: - // This is similar to Operand, but carries more information. We're also not - // guaranteed that Operand looks like this on all ISAs. - class MoveOperand - { - enum Kind { - REG, - FLOAT_REG, - ADDRESS, - FLOAT_ADDRESS, - EFFECTIVE_ADDRESS - }; - - Kind kind_; - uint32_t code_; - int32_t disp_; - - public: - enum AddressKind { - MEMORY = ADDRESS, - EFFECTIVE = EFFECTIVE_ADDRESS, - FLOAT = FLOAT_ADDRESS - }; - - MoveOperand() - { } - explicit MoveOperand(const Register ®) : kind_(REG), code_(reg.code()) - { } - explicit MoveOperand(const FloatRegister ®) : kind_(FLOAT_REG), code_(reg.code()) - { } - MoveOperand(const Register ®, int32_t disp, AddressKind addrKind = MEMORY) - : kind_((Kind) addrKind), - code_(reg.code()), - disp_(disp) - { - // With a zero offset, this is a plain reg-to-reg move. - if (disp == 0 && addrKind == EFFECTIVE) - kind_ = REG; - } - MoveOperand(const MoveOperand &other) - : kind_(other.kind_), - code_(other.code_), - disp_(other.disp_) - { } - bool isFloatReg() const { - return kind_ == FLOAT_REG; - } - bool isGeneralReg() const { - return kind_ == REG; - } - bool isDouble() const { - return kind_ == FLOAT_REG || kind_ == FLOAT_ADDRESS; - } - bool isMemory() const { - return kind_ == ADDRESS; - } - bool isFloatAddress() const { - return kind_ == FLOAT_ADDRESS; - } - bool isEffectiveAddress() const { - return kind_ == EFFECTIVE_ADDRESS; - } - Register reg() const { - JS_ASSERT(isGeneralReg()); - return Register::FromCode(code_); - } - FloatRegister floatReg() const { - JS_ASSERT(isFloatReg()); - return FloatRegister::FromCode(code_); - } - Register base() const { - JS_ASSERT(isMemory() || isEffectiveAddress() || isFloatAddress()); - return Register::FromCode(code_); - } - int32_t disp() const { - return disp_; - } - - bool operator ==(const MoveOperand &other) const { - if (kind_ != other.kind_) - return false; - if (code_ != other.code_) - return false; - if (isMemory() || isEffectiveAddress()) - return disp_ == other.disp_; - return true; - } - bool operator !=(const MoveOperand &other) const { - return !operator==(other); - } - }; - - class Move - { - protected: - MoveOperand from_; - MoveOperand to_; - bool cycle_; - - public: - enum Kind { - GENERAL, - DOUBLE - }; - - protected: - Kind kind_; - - public: - Move() - { } - Move(const Move &other) - : from_(other.from_), - to_(other.to_), - cycle_(other.cycle_), - kind_(other.kind_) - { } - Move(const MoveOperand &from, const MoveOperand &to, Kind kind, bool cycle = false) - : from_(from), - to_(to), - cycle_(cycle), - kind_(kind) - { } - - bool inCycle() const { - return cycle_; - } - const MoveOperand &from() const { - return from_; - } - const MoveOperand &to() const { - return to_; - } - Kind kind() const { - return kind_; - } - }; - private: struct PendingMove - : public Move, + : public MoveOp, public TempObject, public InlineListNode { PendingMove() { } PendingMove(const MoveOperand &from, const MoveOperand &to, Kind kind) - : Move(from, to, kind, false) + : MoveOp(from, to, kind, false) { } void setInCycle() { @@ -178,7 +178,7 @@ class MoveResolver private: // Moves that are definitely unblocked (constants to registers). These are // emitted last. - js::Vector orderedMoves_; + js::Vector orderedMoves_; bool hasCycles_; TempObjectPool movePool_; @@ -200,13 +200,13 @@ class MoveResolver // // After calling addMove() for each parallel move, resolve() performs the // cycle resolution algorithm. Calling addMove() again resets the resolver. - bool addMove(const MoveOperand &from, const MoveOperand &to, Move::Kind kind); + bool addMove(const MoveOperand &from, const MoveOperand &to, MoveOp::Kind kind); bool resolve(); size_t numMoves() const { return orderedMoves_.length(); } - const Move &getMove(size_t i) const { + const MoveOp &getMove(size_t i) const { return orderedMoves_[i]; } bool hasCycles() const { diff --git a/js/src/jit/arm/CodeGenerator-arm.cpp b/js/src/jit/arm/CodeGenerator-arm.cpp index ae1984e96efc..c53edb5bf8c8 100644 --- a/js/src/jit/arm/CodeGenerator-arm.cpp +++ b/js/src/jit/arm/CodeGenerator-arm.cpp @@ -1020,8 +1020,6 @@ CodeGeneratorARM::visitPowHalfD(LPowHalfD *ins) return true; } -typedef MoveResolver::MoveOperand MoveOperand; - MoveOperand CodeGeneratorARM::toMoveOperand(const LAllocation *a) const { diff --git a/js/src/jit/arm/CodeGenerator-arm.h b/js/src/jit/arm/CodeGenerator-arm.h index a916315a485b..08c90dfd8920 100644 --- a/js/src/jit/arm/CodeGenerator-arm.h +++ b/js/src/jit/arm/CodeGenerator-arm.h @@ -43,7 +43,7 @@ class CodeGeneratorARM : public CodeGeneratorShared return ToOperand(def->output()); } - MoveResolver::MoveOperand toMoveOperand(const LAllocation *a) const; + MoveOperand toMoveOperand(const LAllocation *a) const; bool bailoutIf(Assembler::Condition condition, LSnapshot *snapshot); bool bailoutFrom(Label *label, LSnapshot *snapshot); diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp index fe039c5ec771..512aad71c061 100644 --- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -3518,26 +3518,26 @@ MacroAssemblerARMCompat::passABIArg(const MoveOperand &from) FloatRegister fr; if (GetFloatArgReg(usedIntSlots_, usedFloatSlots_, &fr)) { if (!from.isFloatReg() || from.floatReg() != fr) { - enoughMemory_ = moveResolver_.addMove(from, MoveOperand(fr), Move::DOUBLE); + enoughMemory_ = moveResolver_.addMove(from, MoveOperand(fr), MoveOp::DOUBLE); } // else nothing to do; the value is in the right register already } else { // If (and only if) the integer registers have started spilling, do we // need to take the double register's alignment into account uint32_t disp = GetFloatArgStackDisp(usedIntSlots_, usedFloatSlots_, &padding_); - enoughMemory_ = moveResolver_.addMove(from, MoveOperand(sp, disp), Move::DOUBLE); + enoughMemory_ = moveResolver_.addMove(from, MoveOperand(sp, disp), MoveOp::DOUBLE); } usedFloatSlots_++; } else { Register r; if (GetIntArgReg(usedIntSlots_, usedFloatSlots_, &r)) { if (!from.isGeneralReg() || from.reg() != r) { - enoughMemory_ = moveResolver_.addMove(from, MoveOperand(r), Move::GENERAL); + enoughMemory_ = moveResolver_.addMove(from, MoveOperand(r), MoveOp::GENERAL); } // else nothing to do; the value is in the right register already } else { uint32_t disp = GetIntArgStackDisp(usedIntSlots_, usedFloatSlots_, &padding_); - enoughMemory_ = moveResolver_.addMove(from, MoveOperand(sp, disp), Move::GENERAL); + enoughMemory_ = moveResolver_.addMove(from, MoveOperand(sp, disp), MoveOp::GENERAL); } usedIntSlots_++; } @@ -3552,13 +3552,13 @@ MacroAssemblerARMCompat::passABIArg(const MoveOperand &from) uint32_t increment = 1; bool useResolver = true; ++passedArgs_; - Move::Kind kind = Move::GENERAL; + MoveOp::Kind kind = MoveOp::GENERAL; if (from.isDouble()) { // Double arguments need to be rounded up to the nearest doubleword // boundary, even if it is in a register! usedSlots_ = (usedSlots_ + 1) & ~1; increment = 2; - kind = Move::DOUBLE; + kind = MoveOp::DOUBLE; } Register destReg; diff --git a/js/src/jit/arm/MacroAssembler-arm.h b/js/src/jit/arm/MacroAssembler-arm.h index 9fb423cb43b1..ad0e79f2c58f 100644 --- a/js/src/jit/arm/MacroAssembler-arm.h +++ b/js/src/jit/arm/MacroAssembler-arm.h @@ -462,7 +462,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM // Used to work around the move resolver's lack of support for // moving into register pairs, which the softfp ABI needs. - mozilla::Array floatArgsInGPR; + mozilla::Array floatArgsInGPR; mozilla::Array floatArgsInGPRValid; // Compute space needed for the function call and set the properties of the @@ -485,9 +485,6 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM setFramePushed(framePushed_ + value); } public: - typedef MoveResolver::MoveOperand MoveOperand; - typedef MoveResolver::Move Move; - enum Result { GENERAL, DOUBLE, diff --git a/js/src/jit/arm/MoveEmitter-arm.cpp b/js/src/jit/arm/MoveEmitter-arm.cpp index 72fabee9a4e1..f5ded1122b99 100644 --- a/js/src/jit/arm/MoveEmitter-arm.cpp +++ b/js/src/jit/arm/MoveEmitter-arm.cpp @@ -100,7 +100,7 @@ MoveEmitterARM::tempReg() } void -MoveEmitterARM::breakCycle(const MoveOperand &from, const MoveOperand &to, Move::Kind kind) +MoveEmitterARM::breakCycle(const MoveOperand &from, const MoveOperand &to, MoveOp::Kind kind) { // There is some pattern: // (A -> B) @@ -108,7 +108,7 @@ MoveEmitterARM::breakCycle(const MoveOperand &from, const MoveOperand &to, Move: // // This case handles (A -> B), which we reach first. We save B, then allow // the original move to continue. - if (kind == Move::DOUBLE) { + if (kind == MoveOp::DOUBLE) { if (to.isMemory()) { FloatRegister temp = ScratchFloatReg; masm.ma_vldr(toOperand(to, true), temp); @@ -134,7 +134,7 @@ MoveEmitterARM::breakCycle(const MoveOperand &from, const MoveOperand &to, Move: } void -MoveEmitterARM::completeCycle(const MoveOperand &from, const MoveOperand &to, Move::Kind kind) +MoveEmitterARM::completeCycle(const MoveOperand &from, const MoveOperand &to, MoveOp::Kind kind) { // There is some pattern: // (A -> B) @@ -142,7 +142,7 @@ MoveEmitterARM::completeCycle(const MoveOperand &from, const MoveOperand &to, Mo // // This case handles (B -> A), which we reach last. We emit a move from the // saved value of B, to A. - if (kind == Move::DOUBLE) { + if (kind == MoveOp::DOUBLE) { if (to.isMemory()) { FloatRegister temp = ScratchFloatReg; masm.ma_vldr(cycleSlot(), temp); @@ -232,7 +232,7 @@ MoveEmitterARM::emitDoubleMove(const MoveOperand &from, const MoveOperand &to) } void -MoveEmitterARM::emit(const Move &move) +MoveEmitterARM::emit(const MoveOp &move) { const MoveOperand &from = move.from(); const MoveOperand &to = move.to(); @@ -248,7 +248,7 @@ MoveEmitterARM::emit(const Move &move) inCycle_ = true; } - if (move.kind() == Move::DOUBLE) + if (move.kind() == MoveOp::DOUBLE) emitDoubleMove(from, to); else emitMove(from, to); diff --git a/js/src/jit/arm/MoveEmitter-arm.h b/js/src/jit/arm/MoveEmitter-arm.h index 049dc28fdf13..c1dca9f5f38f 100644 --- a/js/src/jit/arm/MoveEmitter-arm.h +++ b/js/src/jit/arm/MoveEmitter-arm.h @@ -17,9 +17,6 @@ class CodeGenerator; class MoveEmitterARM { - typedef MoveResolver::Move Move; - typedef MoveResolver::MoveOperand MoveOperand; - bool inCycle_; MacroAssemblerARMCompat &masm; @@ -49,9 +46,9 @@ class MoveEmitterARM void emitMove(const MoveOperand &from, const MoveOperand &to); void emitDoubleMove(const MoveOperand &from, const MoveOperand &to); - void breakCycle(const MoveOperand &from, const MoveOperand &to, Move::Kind kind); - void completeCycle(const MoveOperand &from, const MoveOperand &to, Move::Kind kind); - void emit(const Move &move); + void breakCycle(const MoveOperand &from, const MoveOperand &to, MoveOp::Kind kind); + void completeCycle(const MoveOperand &from, const MoveOperand &to, MoveOp::Kind kind); + void emit(const MoveOp &move); public: MoveEmitterARM(MacroAssemblerARMCompat &masm); diff --git a/js/src/jit/arm/Trampoline-arm.cpp b/js/src/jit/arm/Trampoline-arm.cpp index 33478fb93f74..6a7291338d3f 100644 --- a/js/src/jit/arm/Trampoline-arm.cpp +++ b/js/src/jit/arm/Trampoline-arm.cpp @@ -654,8 +654,6 @@ JitRuntime::generateBailoutHandler(JSContext *cx) IonCode * JitRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f) { - typedef MoveResolver::MoveOperand MoveOperand; - JS_ASSERT(functionWrappers_); JS_ASSERT(functionWrappers_->initialized()); VMWrapperMap::AddPtr p = functionWrappers_->lookupForAdd(&f); diff --git a/js/src/jit/shared/CodeGenerator-x86-shared.cpp b/js/src/jit/shared/CodeGenerator-x86-shared.cpp index e7a1fff0ccc7..2a563f422cac 100644 --- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp @@ -1329,8 +1329,6 @@ CodeGeneratorX86Shared::visitUrshD(LUrshD *ins) return true; } -typedef MoveResolver::MoveOperand MoveOperand; - MoveOperand CodeGeneratorX86Shared::toMoveOperand(const LAllocation *a) const { diff --git a/js/src/jit/shared/CodeGenerator-x86-shared.h b/js/src/jit/shared/CodeGenerator-x86-shared.h index 8525a41ab8dd..5ec76b33da0a 100644 --- a/js/src/jit/shared/CodeGenerator-x86-shared.h +++ b/js/src/jit/shared/CodeGenerator-x86-shared.h @@ -49,7 +49,7 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared return ToOperand(def->output()); } - MoveResolver::MoveOperand toMoveOperand(const LAllocation *a) const; + MoveOperand toMoveOperand(const LAllocation *a) const; bool bailoutIf(Assembler::Condition condition, LSnapshot *snapshot); bool bailoutIf(Assembler::DoubleCondition condition, LSnapshot *snapshot); diff --git a/js/src/jit/shared/MoveEmitter-x86-shared.cpp b/js/src/jit/shared/MoveEmitter-x86-shared.cpp index 55ce862e0c6d..51ef2b76b73e 100644 --- a/js/src/jit/shared/MoveEmitter-x86-shared.cpp +++ b/js/src/jit/shared/MoveEmitter-x86-shared.cpp @@ -27,7 +27,7 @@ MoveEmitterX86::characterizeCycle(const MoveResolver &moves, size_t i, size_t swapCount = 0; for (size_t j = i; ; j++) { - const Move &move = moves.getMove(j); + const MoveOp &move = moves.getMove(j); // If it isn't a cycle of registers of the same kind, we won't be able // to optimize it. @@ -56,7 +56,7 @@ MoveEmitterX86::characterizeCycle(const MoveResolver &moves, size_t i, } // Check that the last move cycles back to the first move. - const Move &move = moves.getMove(i + swapCount); + const MoveOp &move = moves.getMove(i + swapCount); if (move.from() != moves.getMove(i).to()) { *allGeneralRegs = false; *allFloatRegs = false; @@ -99,7 +99,7 @@ void MoveEmitterX86::emit(const MoveResolver &moves) { for (size_t i = 0; i < moves.numMoves(); i++) { - const Move &move = moves.getMove(i); + const MoveOp &move = moves.getMove(i); const MoveOperand &from = move.from(); const MoveOperand &to = move.to(); @@ -128,7 +128,7 @@ MoveEmitterX86::emit(const MoveResolver &moves) } // A normal move which is not part of a cycle. - if (move.kind() == Move::DOUBLE) + if (move.kind() == MoveOp::DOUBLE) emitDoubleMove(from, to); else emitGeneralMove(from, to); @@ -204,7 +204,7 @@ MoveEmitterX86::toPopOperand(const MoveOperand &operand) const } void -MoveEmitterX86::breakCycle(const MoveOperand &to, Move::Kind kind) +MoveEmitterX86::breakCycle(const MoveOperand &to, MoveOp::Kind kind) { // There is some pattern: // (A -> B) @@ -212,7 +212,7 @@ MoveEmitterX86::breakCycle(const MoveOperand &to, Move::Kind kind) // // This case handles (A -> B), which we reach first. We save B, then allow // the original move to continue. - if (kind == Move::DOUBLE) { + if (kind == MoveOp::DOUBLE) { if (to.isMemory()) { masm.loadDouble(toAddress(to), ScratchFloatReg); masm.storeDouble(ScratchFloatReg, cycleSlot()); @@ -225,7 +225,7 @@ MoveEmitterX86::breakCycle(const MoveOperand &to, Move::Kind kind) } void -MoveEmitterX86::completeCycle(const MoveOperand &to, Move::Kind kind) +MoveEmitterX86::completeCycle(const MoveOperand &to, MoveOp::Kind kind) { // There is some pattern: // (A -> B) @@ -233,7 +233,7 @@ MoveEmitterX86::completeCycle(const MoveOperand &to, Move::Kind kind) // // This case handles (B -> A), which we reach last. We emit a move from the // saved value of B, to A. - if (kind == Move::DOUBLE) { + if (kind == MoveOp::DOUBLE) { if (to.isMemory()) { masm.loadDouble(cycleSlot(), ScratchFloatReg); masm.storeDouble(ScratchFloatReg, toAddress(to)); diff --git a/js/src/jit/shared/MoveEmitter-x86-shared.h b/js/src/jit/shared/MoveEmitter-x86-shared.h index b7d8fe7446df..01636af4d063 100644 --- a/js/src/jit/shared/MoveEmitter-x86-shared.h +++ b/js/src/jit/shared/MoveEmitter-x86-shared.h @@ -23,9 +23,6 @@ class CodeGenerator; class MoveEmitterX86 { - typedef MoveResolver::Move Move; - typedef MoveResolver::MoveOperand MoveOperand; - bool inCycle_; MacroAssemblerSpecific &masm; @@ -48,8 +45,8 @@ class MoveEmitterX86 bool allGeneralRegs, bool allFloatRegs, size_t swapCount); void emitGeneralMove(const MoveOperand &from, const MoveOperand &to); void emitDoubleMove(const MoveOperand &from, const MoveOperand &to); - void breakCycle(const MoveOperand &to, Move::Kind kind); - void completeCycle(const MoveOperand &to, Move::Kind kind); + void breakCycle(const MoveOperand &to, MoveOp::Kind kind); + void completeCycle(const MoveOperand &to, MoveOp::Kind kind); public: MoveEmitterX86(MacroAssemblerSpecific &masm); diff --git a/js/src/jit/x64/MacroAssembler-x64.cpp b/js/src/jit/x64/MacroAssembler-x64.cpp index 4586b3f01675..395a90955272 100644 --- a/js/src/jit/x64/MacroAssembler-x64.cpp +++ b/js/src/jit/x64/MacroAssembler-x64.cpp @@ -149,7 +149,7 @@ MacroAssemblerX64::passABIArg(const MoveOperand &from) to = MoveOperand(StackPointer, stackForCall_); stackForCall_ += sizeof(double); } - enoughMemory_ = moveResolver_.addMove(from, to, Move::DOUBLE); + enoughMemory_ = moveResolver_.addMove(from, to, MoveOp::DOUBLE); } else { Register dest; if (GetIntArgReg(passedIntArgs_++, passedFloatArgs_, &dest)) { @@ -162,7 +162,7 @@ MacroAssemblerX64::passABIArg(const MoveOperand &from) to = MoveOperand(StackPointer, stackForCall_); stackForCall_ += sizeof(int64_t); } - enoughMemory_ = moveResolver_.addMove(from, to, Move::GENERAL); + enoughMemory_ = moveResolver_.addMove(from, to, MoveOp::GENERAL); } } @@ -264,7 +264,7 @@ MacroAssemblerX64::callWithABI(Address fun, Result result) if (IsIntArgReg(fun.base)) { // Callee register may be clobbered for an argument. Move the callee to // r10, a volatile, non-argument register. - moveResolver_.addMove(MoveOperand(fun.base), MoveOperand(r10), Move::GENERAL); + moveResolver_.addMove(MoveOperand(fun.base), MoveOperand(r10), MoveOp::GENERAL); fun.base = r10; } diff --git a/js/src/jit/x64/MacroAssembler-x64.h b/js/src/jit/x64/MacroAssembler-x64.h index fc68f4ad9601..28f4eed9f6f8 100644 --- a/js/src/jit/x64/MacroAssembler-x64.h +++ b/js/src/jit/x64/MacroAssembler-x64.h @@ -85,9 +85,6 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared FLOAT }; - typedef MoveResolver::MoveOperand MoveOperand; - typedef MoveResolver::Move Move; - MacroAssemblerX64() : inCall_(false), enoughMemory_(true) diff --git a/js/src/jit/x64/Trampoline-x64.cpp b/js/src/jit/x64/Trampoline-x64.cpp index d41d42bea39d..14348720e429 100644 --- a/js/src/jit/x64/Trampoline-x64.cpp +++ b/js/src/jit/x64/Trampoline-x64.cpp @@ -503,8 +503,6 @@ JitRuntime::generateBailoutHandler(JSContext *cx) IonCode * JitRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f) { - typedef MoveResolver::MoveOperand MoveOperand; - JS_ASSERT(!StackKeptAligned); JS_ASSERT(functionWrappers_); JS_ASSERT(functionWrappers_->initialized()); diff --git a/js/src/jit/x86/MacroAssembler-x86.cpp b/js/src/jit/x86/MacroAssembler-x86.cpp index ace3e98d36af..c430c2778441 100644 --- a/js/src/jit/x86/MacroAssembler-x86.cpp +++ b/js/src/jit/x86/MacroAssembler-x86.cpp @@ -169,10 +169,10 @@ MacroAssemblerX86::passABIArg(const MoveOperand &from) MoveOperand to = MoveOperand(StackPointer, stackForCall_); if (from.isDouble()) { stackForCall_ += sizeof(double); - enoughMemory_ &= moveResolver_.addMove(from, to, Move::DOUBLE); + enoughMemory_ &= moveResolver_.addMove(from, to, MoveOp::DOUBLE); } else { stackForCall_ += sizeof(int32_t); - enoughMemory_ &= moveResolver_.addMove(from, to, Move::GENERAL); + enoughMemory_ &= moveResolver_.addMove(from, to, MoveOp::GENERAL); } } diff --git a/js/src/jit/x86/MacroAssembler-x86.h b/js/src/jit/x86/MacroAssembler-x86.h index 0d1395a08300..e7ee36481786 100644 --- a/js/src/jit/x86/MacroAssembler-x86.h +++ b/js/src/jit/x86/MacroAssembler-x86.h @@ -76,9 +76,6 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared FLOAT }; - typedef MoveResolver::MoveOperand MoveOperand; - typedef MoveResolver::Move Move; - MacroAssemblerX86() : inCall_(false), enoughMemory_(true) diff --git a/js/src/jit/x86/Trampoline-x86.cpp b/js/src/jit/x86/Trampoline-x86.cpp index a14d8e2c2dfc..2c6558ccee22 100644 --- a/js/src/jit/x86/Trampoline-x86.cpp +++ b/js/src/jit/x86/Trampoline-x86.cpp @@ -538,8 +538,6 @@ JitRuntime::generateBailoutHandler(JSContext *cx) IonCode * JitRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f) { - typedef MoveResolver::MoveOperand MoveOperand; - JS_ASSERT(!StackKeptAligned); JS_ASSERT(functionWrappers_); JS_ASSERT(functionWrappers_->initialized()); From 371b48053374054a33428f21d3b39d5324474af9 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 13 Dec 2013 08:27:47 -0800 Subject: [PATCH 049/144] Bug 949668 - SpiderMonkey: MoveOp cleanups. r=jandem --- js/src/jit/MoveResolver.h | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/js/src/jit/MoveResolver.h b/js/src/jit/MoveResolver.h index bbe6f1201a32..3ae8a50fd053 100644 --- a/js/src/jit/MoveResolver.h +++ b/js/src/jit/MoveResolver.h @@ -125,16 +125,10 @@ class MoveOp public: MoveOp() { } - MoveOp(const MoveOp &other) - : from_(other.from_), - to_(other.to_), - cycle_(other.cycle_), - kind_(other.kind_) - { } - MoveOp(const MoveOperand &from, const MoveOperand &to, Kind kind, bool cycle = false) + MoveOp(const MoveOperand &from, const MoveOperand &to, Kind kind) : from_(from), to_(to), - cycle_(cycle), + cycle_(false), kind_(kind) { } @@ -163,14 +157,13 @@ class MoveResolver PendingMove() { } PendingMove(const MoveOperand &from, const MoveOperand &to, Kind kind) - : MoveOp(from, to, kind, false) + : MoveOp(from, to, kind) { } - + void setInCycle() { JS_ASSERT(!inCycle()); cycle_ = true; } - }; typedef InlineList::iterator PendingMoveIterator; From 12ce37d8aa70f31130fa0288d356fc93ac2755f4 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 13 Dec 2013 08:27:47 -0800 Subject: [PATCH 050/144] Bug 949668 - SpiderMonkey: Add an LDefinition::Float32 r=jandem --- js/src/jit/BacktrackingAllocator.cpp | 4 +++- js/src/jit/LIR.cpp | 5 +++-- js/src/jit/LIR.h | 6 ++++-- js/src/jit/LinearScan.cpp | 6 ++++-- js/src/jit/LiveRangeAllocator.cpp | 1 + js/src/jit/LiveRangeAllocator.h | 6 +++--- js/src/jit/Lowering.cpp | 26 +++++++++++------------ js/src/jit/RegisterAllocator.h | 2 +- js/src/jit/StupidAllocator.cpp | 6 ++++-- js/src/jit/arm/Lowering-arm.cpp | 2 +- js/src/jit/shared/Lowering-shared-inl.h | 12 +++++++++-- js/src/jit/shared/Lowering-shared.h | 3 ++- js/src/jit/shared/Lowering-x86-shared.cpp | 6 +++--- 13 files changed, 52 insertions(+), 33 deletions(-) diff --git a/js/src/jit/BacktrackingAllocator.cpp b/js/src/jit/BacktrackingAllocator.cpp index 604f397b1ae9..f7b6ad07d6e7 100644 --- a/js/src/jit/BacktrackingAllocator.cpp +++ b/js/src/jit/BacktrackingAllocator.cpp @@ -1127,7 +1127,9 @@ BacktrackingAllocator::populateSafepoints() if (ins == reg->ins() && !reg->isTemp()) { DebugOnly def = reg->def(); JS_ASSERT_IF(def->policy() == LDefinition::MUST_REUSE_INPUT, - def->type() == LDefinition::GENERAL || def->type() == LDefinition::DOUBLE); + def->type() == LDefinition::GENERAL || + def->type() == LDefinition::FLOAT32 || + def->type() == LDefinition::DOUBLE); continue; } diff --git a/js/src/jit/LIR.cpp b/js/src/jit/LIR.cpp index 9588b00bf6cd..e5bd77ca8986 100644 --- a/js/src/jit/LIR.cpp +++ b/js/src/jit/LIR.cpp @@ -196,10 +196,11 @@ static const char * const TypeChars[] = { "i", // INTEGER "o", // OBJECT - "f", // DOUBLE + "f", // FLOAT32 + "d", // DOUBLE #ifdef JS_NUNBOX32 "t", // TYPE - "d" // PAYLOAD + "p" // PAYLOAD #elif JS_PUNBOX64 "x" // BOX #endif diff --git a/js/src/jit/LIR.h b/js/src/jit/LIR.h index 4f46e117a4d7..9b38d0d77637 100644 --- a/js/src/jit/LIR.h +++ b/js/src/jit/LIR.h @@ -446,7 +446,8 @@ class LDefinition GENERAL, // Generic, integer or pointer-width data (GPR). OBJECT, // Pointer that may be collected as garbage (GPR). SLOTS, // Slots/elements pointer that may be moved by minor GCs (GPR). - DOUBLE, // 64-bit point value (FPU). + FLOAT32, // 32-bit floating-point value (FPU). + DOUBLE, // 64-bit floating-point value (FPU). #ifdef JS_NUNBOX32 // A type virtual register must be followed by a payload virtual // register, as both will be tracked as a single gcthing. @@ -540,8 +541,9 @@ class LDefinition case MIRType_Object: return LDefinition::OBJECT; case MIRType_Double: - case MIRType_Float32: return LDefinition::DOUBLE; + case MIRType_Float32: + return LDefinition::FLOAT32; #if defined(JS_PUNBOX64) case MIRType_Value: return LDefinition::BOX; diff --git a/js/src/jit/LinearScan.cpp b/js/src/jit/LinearScan.cpp index cd22f4d36818..86731c3ece01 100644 --- a/js/src/jit/LinearScan.cpp +++ b/js/src/jit/LinearScan.cpp @@ -515,7 +515,9 @@ LinearScanAllocator::populateSafepoints() if (ins == reg->ins() && !reg->isTemp()) { DebugOnly def = reg->def(); JS_ASSERT_IF(def->policy() == LDefinition::MUST_REUSE_INPUT, - def->type() == LDefinition::GENERAL || def->type() == LDefinition::DOUBLE); + def->type() == LDefinition::GENERAL || + def->type() == LDefinition::FLOAT32 || + def->type() == LDefinition::DOUBLE); continue; } @@ -792,7 +794,7 @@ LinearScanAllocator::allocateSlotFor(const LiveInterval *interval) LinearScanVirtualRegister *reg = &vregs[interval->vreg()]; SlotList *freed; - if (reg->type() == LDefinition::DOUBLE) + if (reg->type() == LDefinition::DOUBLE || reg->type() == LDefinition::FLOAT32) freed = &finishedDoubleSlots_; #ifdef JS_NUNBOX32 else if (IsNunbox(reg)) diff --git a/js/src/jit/LiveRangeAllocator.cpp b/js/src/jit/LiveRangeAllocator.cpp index 361fdb60ebcd..0d457286657e 100644 --- a/js/src/jit/LiveRangeAllocator.cpp +++ b/js/src/jit/LiveRangeAllocator.cpp @@ -468,6 +468,7 @@ AddRegisterToSafepoint(LSafepoint *safepoint, AnyRegister reg, const LDefinition JS_ASSERT(def.type() == LDefinition::GENERAL || def.type() == LDefinition::DOUBLE || + def.type() == LDefinition::FLOAT32 || def.type() == LDefinition::OBJECT); if (def.type() == LDefinition::OBJECT) diff --git a/js/src/jit/LiveRangeAllocator.h b/js/src/jit/LiveRangeAllocator.h index a469ebda4aeb..cfff988c4163 100644 --- a/js/src/jit/LiveRangeAllocator.h +++ b/js/src/jit/LiveRangeAllocator.h @@ -136,7 +136,7 @@ static inline bool DefinitionCompatibleWith(LInstruction *ins, const LDefinition *def, LAllocation alloc) { if (ins->isPhi()) { - if (def->type() == LDefinition::DOUBLE) + if (def->type() == LDefinition::DOUBLE || def->type() == LDefinition::FLOAT32) return alloc.isFloatReg() || alloc.kind() == LAllocation::DOUBLE_SLOT; return alloc.isGeneralReg() || alloc.kind() == LAllocation::STACK_SLOT; } @@ -145,7 +145,7 @@ DefinitionCompatibleWith(LInstruction *ins, const LDefinition *def, LAllocation case LDefinition::DEFAULT: if (!alloc.isRegister()) return false; - return alloc.isFloatReg() == (def->type() == LDefinition::DOUBLE); + return alloc.isFloatReg() == (def->type() == LDefinition::DOUBLE || def->type() == LDefinition::FLOAT32); case LDefinition::PRESET: return alloc == *def->output(); case LDefinition::MUST_REUSE_INPUT: @@ -473,7 +473,7 @@ class VirtualRegister return intervals_.insert(found, interval); } bool isDouble() const { - return def_->type() == LDefinition::DOUBLE; + return def_->type() == LDefinition::DOUBLE || def_->type() == LDefinition::FLOAT32; } LiveInterval *intervalFor(CodePosition pos); diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 54b3a02c0a76..875ae3b7ff1e 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -659,7 +659,7 @@ LIRGenerator::visitTest(MTest *test) temp0 = LDefinition::BogusTemp(); temp1 = LDefinition::BogusTemp(); } - LTestVAndBranch *lir = new(alloc()) LTestVAndBranch(ifTrue, ifFalse, tempFloat(), temp0, temp1); + LTestVAndBranch *lir = new(alloc()) LTestVAndBranch(ifTrue, ifFalse, tempDouble(), temp0, temp1); if (!useBox(lir, LTestVAndBranch::Input, opd)) return false; return add(lir, test); @@ -1026,7 +1026,7 @@ LIRGenerator::visitTypeOf(MTypeOf *ins) bool LIRGenerator::visitToId(MToId *ins) { - LToIdV *lir = new(alloc()) LToIdV(tempFloat()); + LToIdV *lir = new(alloc()) LToIdV(tempDouble()); if (!useBox(lir, LToIdV::Object, ins->lhs())) return false; if (!useBox(lir, LToIdV::Index, ins->rhs())) @@ -1175,7 +1175,7 @@ bool LIRGenerator::visitRound(MRound *ins) { JS_ASSERT(ins->num()->type() == MIRType_Double); - LRound *lir = new(alloc()) LRound(useRegister(ins->num()), tempFloat()); + LRound *lir = new(alloc()) LRound(useRegister(ins->num()), tempDouble()); if (!assignSnapshot(lir)) return false; return define(lir, ins); @@ -1754,7 +1754,7 @@ LIRGenerator::visitToInt32(MToInt32 *convert) switch (opd->type()) { case MIRType_Value: { - LValueToInt32 *lir = new(alloc()) LValueToInt32(tempFloat(), temp(), LValueToInt32::NORMAL); + LValueToInt32 *lir = new(alloc()) LValueToInt32(tempDouble(), temp(), LValueToInt32::NORMAL); if (!useBox(lir, LValueToInt32::Input, opd)) return false; return assignSnapshot(lir) && define(lir, convert) && assignSafepoint(lir, convert); @@ -1799,7 +1799,7 @@ LIRGenerator::visitTruncateToInt32(MTruncateToInt32 *truncate) switch (opd->type()) { case MIRType_Value: { - LValueToInt32 *lir = new(alloc()) LValueToInt32(tempFloat(), temp(), LValueToInt32::TRUNCATE); + LValueToInt32 *lir = new(alloc()) LValueToInt32(tempDouble(), temp(), LValueToInt32::TRUNCATE); if (!useBox(lir, LValueToInt32::Input, opd)) return false; return assignSnapshot(lir) && define(lir, truncate) && assignSafepoint(lir, truncate); @@ -2025,7 +2025,7 @@ LIRGenerator::visitMaybeToDoubleElement(MMaybeToDoubleElement *ins) LMaybeToDoubleElement *lir = new(alloc()) LMaybeToDoubleElement(useRegisterAtStart(ins->elements()), useRegisterAtStart(ins->value()), - tempFloat()); + tempDouble()); return defineBox(lir, ins); } @@ -2342,7 +2342,7 @@ LIRGenerator::visitNot(MNot *ins) temp1 = LDefinition::BogusTemp(); } - LNotV *lir = new(alloc()) LNotV(tempFloat(), temp0, temp1); + LNotV *lir = new(alloc()) LNotV(tempDouble(), temp0, temp1); if (!useBox(lir, LNotV::Input, op)) return false; return define(lir, ins); @@ -2625,7 +2625,7 @@ LIRGenerator::visitClampToUint8(MClampToUint8 *ins) case MIRType_Value: { - LClampVToUint8 *lir = new(alloc()) LClampVToUint8(tempFloat()); + LClampVToUint8 *lir = new(alloc()) LClampVToUint8(tempDouble()); if (!useBox(lir, LClampVToUint8::Input, in)) return false; return assignSnapshot(lir) && define(lir, ins) && assignSafepoint(lir, ins); @@ -2912,15 +2912,15 @@ LIRGenerator::visitAssertRange(MAssertRange *ins) break; case MIRType_Double: - lir = new(alloc()) LAssertRangeD(useRegister(input), tempFloat()); + lir = new(alloc()) LAssertRangeD(useRegister(input), tempDouble()); break; case MIRType_Float32: - lir = new(alloc()) LAssertRangeF(useRegister(input), tempFloat()); + lir = new(alloc()) LAssertRangeF(useRegister(input), tempFloat32()); break; case MIRType_Value: - lir = new(alloc()) LAssertRangeV(tempToUnbox(), tempFloat(), tempFloat()); + lir = new(alloc()) LAssertRangeV(tempToUnbox(), tempDouble(), tempDouble()); if (!useBox(lir, LAssertRangeV::Input, input)) return false; break; @@ -3026,7 +3026,7 @@ LIRGenerator::visitSetElementCache(MSetElementCache *ins) LInstruction *lir; if (ins->value()->type() == MIRType_Value) { lir = new(alloc()) LSetElementCacheV(useByteOpRegister(ins->object()), tempToUnbox(), - temp(), tempFloat()); + temp(), tempDouble()); if (!useBox(lir, LSetElementCacheV::Index, ins->index())) return false; @@ -3035,7 +3035,7 @@ LIRGenerator::visitSetElementCache(MSetElementCache *ins) } else { lir = new(alloc()) LSetElementCacheT(useByteOpRegister(ins->object()), useRegisterOrConstant(ins->value()), - tempToUnbox(), temp(), tempFloat()); + tempToUnbox(), temp(), tempDouble()); if (!useBox(lir, LSetElementCacheT::Index, ins->index())) return false; diff --git a/js/src/jit/RegisterAllocator.h b/js/src/jit/RegisterAllocator.h index e6059eaef31d..f5f0d2770cfd 100644 --- a/js/src/jit/RegisterAllocator.h +++ b/js/src/jit/RegisterAllocator.h @@ -369,7 +369,7 @@ class RegisterAllocator static inline AnyRegister GetFixedRegister(const LDefinition *def, const LUse *use) { - return def->type() == LDefinition::DOUBLE + return (def->type() == LDefinition::DOUBLE || def->type() == LDefinition::FLOAT32) ? AnyRegister(FloatRegister::FromCode(use->registerCode())) : AnyRegister(Register::FromCode(use->registerCode())); } diff --git a/js/src/jit/StupidAllocator.cpp b/js/src/jit/StupidAllocator.cpp index 921bdf9d08d4..9c350555d9cc 100644 --- a/js/src/jit/StupidAllocator.cpp +++ b/js/src/jit/StupidAllocator.cpp @@ -28,7 +28,9 @@ StupidAllocator::stackLocation(uint32_t vreg) if (def->policy() == LDefinition::PRESET && def->output()->isArgument()) return def->output(); - return new(alloc()) LStackSlot(DefaultStackSlot(vreg), def->type() == LDefinition::DOUBLE); + return new(alloc()) LStackSlot(DefaultStackSlot(vreg), + def->type() == LDefinition::DOUBLE || + def->type() == LDefinition::FLOAT32); } StupidAllocator::RegisterIndex @@ -162,7 +164,7 @@ StupidAllocator::allocateRegister(LInstruction *ins, uint32_t vreg) for (size_t i = 0; i < registerCount; i++) { AnyRegister reg = registers[i].reg; - if (reg.isFloat() != (def->type() == LDefinition::DOUBLE)) + if (reg.isFloat() != (def->type() == LDefinition::DOUBLE || def->type() == LDefinition::FLOAT32)) continue; // Skip the register if it is in use for an allocated input or output. diff --git a/js/src/jit/arm/Lowering-arm.cpp b/js/src/jit/arm/Lowering-arm.cpp index 8f4ca0c78d19..b74971f3e3f6 100644 --- a/js/src/jit/arm/Lowering-arm.cpp +++ b/js/src/jit/arm/Lowering-arm.cpp @@ -362,7 +362,7 @@ LIRGeneratorARM::newLTableSwitch(const LAllocation &in, const LDefinition &input LTableSwitchV * LIRGeneratorARM::newLTableSwitchV(MTableSwitch *tableswitch) { - return new(alloc()) LTableSwitchV(temp(), tempFloat(), tableswitch); + return new(alloc()) LTableSwitchV(temp(), tempDouble(), tableswitch); } bool diff --git a/js/src/jit/shared/Lowering-shared-inl.h b/js/src/jit/shared/Lowering-shared-inl.h index 940d346cae9d..fcc6fdc68ca7 100644 --- a/js/src/jit/shared/Lowering-shared-inl.h +++ b/js/src/jit/shared/Lowering-shared-inl.h @@ -149,12 +149,14 @@ LIRGeneratorShared::defineReturn(LInstruction *lir, MDefinition *mir) #endif break; case MIRType_Float32: + lir->setDef(0, LDefinition(vreg, LDefinition::FLOAT32, LFloatReg(ReturnFloatReg))); + break; case MIRType_Double: lir->setDef(0, LDefinition(vreg, LDefinition::DOUBLE, LFloatReg(ReturnFloatReg))); break; default: LDefinition::Type type = LDefinition::TypeFrom(mir->type()); - JS_ASSERT(type != LDefinition::DOUBLE); + JS_ASSERT(type != LDefinition::DOUBLE && type != LDefinition::FLOAT32); lir->setDef(0, LDefinition(vreg, type, LGeneralReg(ReturnReg))); break; } @@ -421,7 +423,13 @@ LIRGeneratorShared::tempFixed(Register reg) } LDefinition -LIRGeneratorShared::tempFloat() +LIRGeneratorShared::tempFloat32() +{ + return temp(LDefinition::FLOAT32); +} + +LDefinition +LIRGeneratorShared::tempDouble() { return temp(LDefinition::DOUBLE); } diff --git a/js/src/jit/shared/Lowering-shared.h b/js/src/jit/shared/Lowering-shared.h index fdfb74276682..35075335af13 100644 --- a/js/src/jit/shared/Lowering-shared.h +++ b/js/src/jit/shared/Lowering-shared.h @@ -104,7 +104,8 @@ class LIRGeneratorShared : public MInstructionVisitorWithDefaults // These create temporary register requests. inline LDefinition temp(LDefinition::Type type = LDefinition::GENERAL, LDefinition::Policy policy = LDefinition::DEFAULT); - inline LDefinition tempFloat(); + inline LDefinition tempFloat32(); + inline LDefinition tempDouble(); inline LDefinition tempCopy(MDefinition *input, uint32_t reusedInput); // Note that the fixed register has a GENERAL type. diff --git a/js/src/jit/shared/Lowering-x86-shared.cpp b/js/src/jit/shared/Lowering-x86-shared.cpp index 8895dba562bc..71d687543dfa 100644 --- a/js/src/jit/shared/Lowering-x86-shared.cpp +++ b/js/src/jit/shared/Lowering-x86-shared.cpp @@ -27,7 +27,7 @@ LIRGeneratorX86Shared::newLTableSwitch(const LAllocation &in, const LDefinition LTableSwitchV * LIRGeneratorX86Shared::newLTableSwitchV(MTableSwitch *tableswitch) { - return new(alloc()) LTableSwitchV(temp(), tempFloat(), temp(), tableswitch); + return new(alloc()) LTableSwitchV(temp(), tempDouble(), temp(), tableswitch); } bool @@ -333,7 +333,7 @@ LIRGeneratorX86Shared::lowerTruncateDToInt32(MTruncateToInt32 *ins) MDefinition *opd = ins->input(); JS_ASSERT(opd->type() == MIRType_Double); - LDefinition maybeTemp = Assembler::HasSSE3() ? LDefinition::BogusTemp() : tempFloat(); + LDefinition maybeTemp = Assembler::HasSSE3() ? LDefinition::BogusTemp() : tempDouble(); return define(new(alloc()) LTruncateDToInt32(useRegister(opd), maybeTemp), ins); } @@ -343,6 +343,6 @@ LIRGeneratorX86Shared::lowerTruncateFToInt32(MTruncateToInt32 *ins) MDefinition *opd = ins->input(); JS_ASSERT(opd->type() == MIRType_Float32); - LDefinition maybeTemp = Assembler::HasSSE3() ? LDefinition::BogusTemp() : tempFloat(); + LDefinition maybeTemp = Assembler::HasSSE3() ? LDefinition::BogusTemp() : tempFloat32(); return define(new(alloc()) LTruncateFToInt32(useRegister(opd), maybeTemp), ins); } From e55bd88d090603b1aa5f8272cf572b71a4de9a04 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 13 Dec 2013 08:27:47 -0800 Subject: [PATCH 051/144] Bug 949668 - SpiderMonkey: Add a type to LMoveGroup. r=jandem --- js/src/jit/BacktrackingAllocator.cpp | 15 +++++++------- js/src/jit/LIR-Common.h | 13 +++++++++---- js/src/jit/LIR.cpp | 10 +++++----- js/src/jit/LinearScan.cpp | 29 +++++++++++++++------------- js/src/jit/LinearScan.h | 2 +- js/src/jit/LiveRangeAllocator.h | 20 +++++++++---------- js/src/jit/StupidAllocator.cpp | 17 +++++++++------- js/src/jit/StupidAllocator.h | 5 ++++- 8 files changed, 63 insertions(+), 48 deletions(-) diff --git a/js/src/jit/BacktrackingAllocator.cpp b/js/src/jit/BacktrackingAllocator.cpp index f7b6ad07d6e7..bd601cc0a884 100644 --- a/js/src/jit/BacktrackingAllocator.cpp +++ b/js/src/jit/BacktrackingAllocator.cpp @@ -917,10 +917,10 @@ BacktrackingAllocator::resolveControlFlow() LiveInterval *prevInterval = reg->intervalFor(start.previous()); if (start.subpos() == CodePosition::INPUT) { - if (!moveInput(inputOf(data->ins()), prevInterval, interval)) + if (!moveInput(inputOf(data->ins()), prevInterval, interval, reg->type())) return false; } else { - if (!moveAfter(outputOf(data->ins()), prevInterval, interval)) + if (!moveAfter(outputOf(data->ins()), prevInterval, interval, reg->type())) return false; } } @@ -940,7 +940,8 @@ BacktrackingAllocator::resolveControlFlow() for (size_t j = 0; j < successor->numPhis(); j++) { LPhi *phi = successor->getPhi(j); JS_ASSERT(phi->numDefs() == 1); - VirtualRegister *vreg = &vregs[phi->getDef(0)]; + LDefinition *def = phi->getDef(0); + VirtualRegister *vreg = &vregs[def]; LiveInterval *to = vreg->intervalFor(inputOf(successor->firstId())); JS_ASSERT(to); @@ -952,7 +953,7 @@ BacktrackingAllocator::resolveControlFlow() LiveInterval *from = vregs[input].intervalFor(outputOf(predecessor->lastId())); JS_ASSERT(from); - if (!moveAtExit(predecessor, from, to)) + if (!moveAtExit(predecessor, from, to, def->type())) return false; } } @@ -977,10 +978,10 @@ BacktrackingAllocator::resolveControlFlow() if (mSuccessor->numPredecessors() > 1) { JS_ASSERT(predecessor->mir()->numSuccessors() == 1); - if (!moveAtExit(predecessor, from, to)) + if (!moveAtExit(predecessor, from, to, reg.type())) return false; } else { - if (!moveAtEntry(successor, from, to)) + if (!moveAtEntry(successor, from, to, reg.type())) return false; } } @@ -1076,7 +1077,7 @@ BacktrackingAllocator::reifyAllocations() if (*res != *alloc) { LMoveGroup *group = getInputMoveGroup(inputOf(ins)); - if (!group->addAfter(sourceAlloc, res)) + if (!group->addAfter(sourceAlloc, res, def->type())) return false; *alloc = *res; } diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index 8f94028624a1..c4d11cee87e2 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -71,11 +71,13 @@ class LMove { LAllocation *from_; LAllocation *to_; + LDefinition::Type type_; public: - LMove(LAllocation *from, LAllocation *to) + LMove(LAllocation *from, LAllocation *to, LDefinition::Type type) : from_(from), - to_(to) + to_(to), + type_(type) { } LAllocation *from() { @@ -90,6 +92,9 @@ class LMove const LAllocation *to() const { return to_; } + LDefinition::Type type() const { + return type_; + } }; class LMoveGroup : public LInstructionHelper<0, 0, 0> @@ -110,10 +115,10 @@ class LMoveGroup : public LInstructionHelper<0, 0, 0> void printOperands(FILE *fp); // Add a move which takes place simultaneously with all others in the group. - bool add(LAllocation *from, LAllocation *to); + bool add(LAllocation *from, LAllocation *to, LDefinition::Type type); // Add a move which takes place after existing moves in the group. - bool addAfter(LAllocation *from, LAllocation *to); + bool addAfter(LAllocation *from, LAllocation *to, LDefinition::Type type); size_t numMoves() const { return moves_.length(); diff --git a/js/src/jit/LIR.cpp b/js/src/jit/LIR.cpp index e5bd77ca8986..114a6724841e 100644 --- a/js/src/jit/LIR.cpp +++ b/js/src/jit/LIR.cpp @@ -363,18 +363,18 @@ LInstruction::initSafepoint(TempAllocator &alloc) } bool -LMoveGroup::add(LAllocation *from, LAllocation *to) +LMoveGroup::add(LAllocation *from, LAllocation *to, LDefinition::Type type) { #ifdef DEBUG JS_ASSERT(*from != *to); for (size_t i = 0; i < moves_.length(); i++) JS_ASSERT(*to != *moves_[i].to()); #endif - return moves_.append(LMove(from, to)); + return moves_.append(LMove(from, to, type)); } bool -LMoveGroup::addAfter(LAllocation *from, LAllocation *to) +LMoveGroup::addAfter(LAllocation *from, LAllocation *to, LDefinition::Type type) { // Transform the operands to this move so that performing the result // simultaneously with existing moves in the group will have the same @@ -392,12 +392,12 @@ LMoveGroup::addAfter(LAllocation *from, LAllocation *to) for (size_t i = 0; i < moves_.length(); i++) { if (*to == *moves_[i].to()) { - moves_[i] = LMove(from, to); + moves_[i] = LMove(from, to, type); return true; } } - return add(from, to); + return add(from, to, type); } void diff --git a/js/src/jit/LinearScan.cpp b/js/src/jit/LinearScan.cpp index 86731c3ece01..6a46531ab37b 100644 --- a/js/src/jit/LinearScan.cpp +++ b/js/src/jit/LinearScan.cpp @@ -233,7 +233,8 @@ LinearScanAllocator::resolveControlFlow() for (size_t j = 0; j < successor->numPhis(); j++) { LPhi *phi = successor->getPhi(j); JS_ASSERT(phi->numDefs() == 1); - LinearScanVirtualRegister *vreg = &vregs[phi->getDef(0)]; + LDefinition *def = phi->getDef(0); + LinearScanVirtualRegister *vreg = &vregs[def]; LiveInterval *to = vreg->intervalFor(inputOf(successor->firstId())); JS_ASSERT(to); @@ -245,14 +246,15 @@ LinearScanAllocator::resolveControlFlow() LiveInterval *from = vregs[input].intervalFor(outputOf(predecessor->lastId())); JS_ASSERT(from); - if (!moveAtExit(predecessor, from, to)) + if (!moveAtExit(predecessor, from, to, def->type())) return false; } if (vreg->mustSpillAtDefinition() && !to->isSpill()) { // Make sure this phi is spilled at the loop header. LMoveGroup *moves = successor->getEntryMoveGroup(alloc()); - if (!moves->add(to->getAllocation(), vregs[to->vreg()].canonicalSpill())) + if (!moves->add(to->getAllocation(), vregs[to->vreg()].canonicalSpill(), + def->type())) return false; } } @@ -283,10 +285,10 @@ LinearScanAllocator::resolveControlFlow() if (mSuccessor->numPredecessors() > 1) { JS_ASSERT(predecessor->mir()->numSuccessors() == 1); - if (!moveAtExit(predecessor, from, to)) + if (!moveAtExit(predecessor, from, to, vreg->type())) return false; } else { - if (!moveAtEntry(successor, from, to)) + if (!moveAtEntry(successor, from, to, vreg->type())) return false; } } @@ -297,12 +299,13 @@ LinearScanAllocator::resolveControlFlow() } bool -LinearScanAllocator::moveInputAlloc(CodePosition pos, LAllocation *from, LAllocation *to) +LinearScanAllocator::moveInputAlloc(CodePosition pos, LAllocation *from, LAllocation *to, + LDefinition::Type type) { if (*from == *to) return true; LMoveGroup *moves = getInputMoveGroup(pos); - return moves->add(from, to); + return moves->add(from, to, type); } static inline void @@ -350,7 +353,7 @@ LinearScanAllocator::reifyAllocations() LiveInterval *to = fixedIntervals[GetFixedRegister(reg->def(), usePos->use).code()]; *static_cast(usePos->use) = *to->getAllocation(); - if (!moveInput(usePos->pos, interval, to)) + if (!moveInput(usePos->pos, interval, to, reg->type())) return false; } else { JS_ASSERT(UseCompatibleWith(usePos->use, *interval->getAllocation())); @@ -376,7 +379,7 @@ LinearScanAllocator::reifyAllocations() // it should use the fixed register instead. SetOsiPointUses(interval, defEnd, LAllocation(fixedReg)); - if (!moveAfter(defEnd, from, interval)) + if (!moveAfter(defEnd, from, interval, def->type())) return false; spillFrom = from->getAllocation(); } else { @@ -387,7 +390,7 @@ LinearScanAllocator::reifyAllocations() JS_ASSERT(!inputAlloc->isUse()); *inputAlloc = *interval->getAllocation(); - if (!moveInputAlloc(inputOf(reg->ins()), origAlloc, inputAlloc)) + if (!moveInputAlloc(inputOf(reg->ins()), origAlloc, inputAlloc, def->type())) return false; } @@ -417,7 +420,7 @@ LinearScanAllocator::reifyAllocations() // or Nop instructions). Note that we explicitly ignore phis, // which should have been handled in resolveControlFlow(). LMoveGroup *moves = getMoveGroupAfter(defEnd); - if (!moves->add(spillFrom, reg->canonicalSpill())) + if (!moves->add(spillFrom, reg->canonicalSpill(), def->type())) return false; } } @@ -443,10 +446,10 @@ LinearScanAllocator::reifyAllocations() JS_ASSERT(start == inputOf(data->ins()) || start == outputOf(data->ins())); if (start.subpos() == CodePosition::INPUT) { - if (!moveInput(inputOf(data->ins()), prevInterval, interval)) + if (!moveInput(inputOf(data->ins()), prevInterval, interval, reg->type())) return false; } else { - if (!moveAfter(outputOf(data->ins()), prevInterval, interval)) + if (!moveAfter(outputOf(data->ins()), prevInterval, interval, reg->type())) return false; } diff --git a/js/src/jit/LinearScan.h b/js/src/jit/LinearScan.h index 64ce47d9191a..cf194b9e20b9 100644 --- a/js/src/jit/LinearScan.h +++ b/js/src/jit/LinearScan.h @@ -111,7 +111,7 @@ class LinearScanAllocator AnyRegister::Code findBestFreeRegister(CodePosition *freeUntil); AnyRegister::Code findBestBlockedRegister(CodePosition *nextUsed); bool canCoexist(LiveInterval *a, LiveInterval *b); - bool moveInputAlloc(CodePosition pos, LAllocation *from, LAllocation *to); + bool moveInputAlloc(CodePosition pos, LAllocation *from, LAllocation *to, LDefinition::Type type); void setIntervalRequirement(LiveInterval *interval); bool isSpilledAt(LiveInterval *interval, CodePosition pos); diff --git a/js/src/jit/LiveRangeAllocator.h b/js/src/jit/LiveRangeAllocator.h index cfff988c4163..993f1c495451 100644 --- a/js/src/jit/LiveRangeAllocator.h +++ b/js/src/jit/LiveRangeAllocator.h @@ -633,37 +633,37 @@ class LiveRangeAllocator : protected RegisterAllocator } #endif - bool addMove(LMoveGroup *moves, LiveInterval *from, LiveInterval *to) { + bool addMove(LMoveGroup *moves, LiveInterval *from, LiveInterval *to, LDefinition::Type type) { JS_ASSERT(*from->getAllocation() != *to->getAllocation()); - return moves->add(from->getAllocation(), to->getAllocation()); + return moves->add(from->getAllocation(), to->getAllocation(), type); } - bool moveInput(CodePosition pos, LiveInterval *from, LiveInterval *to) { + bool moveInput(CodePosition pos, LiveInterval *from, LiveInterval *to, LDefinition::Type type) { if (*from->getAllocation() == *to->getAllocation()) return true; LMoveGroup *moves = getInputMoveGroup(pos); - return addMove(moves, from, to); + return addMove(moves, from, to, type); } - bool moveAfter(CodePosition pos, LiveInterval *from, LiveInterval *to) { + bool moveAfter(CodePosition pos, LiveInterval *from, LiveInterval *to, LDefinition::Type type) { if (*from->getAllocation() == *to->getAllocation()) return true; LMoveGroup *moves = getMoveGroupAfter(pos); - return addMove(moves, from, to); + return addMove(moves, from, to, type); } - bool moveAtExit(LBlock *block, LiveInterval *from, LiveInterval *to) { + bool moveAtExit(LBlock *block, LiveInterval *from, LiveInterval *to, LDefinition::Type type) { if (*from->getAllocation() == *to->getAllocation()) return true; LMoveGroup *moves = block->getExitMoveGroup(alloc()); - return addMove(moves, from, to); + return addMove(moves, from, to, type); } - bool moveAtEntry(LBlock *block, LiveInterval *from, LiveInterval *to) { + bool moveAtEntry(LBlock *block, LiveInterval *from, LiveInterval *to, LDefinition::Type type) { if (*from->getAllocation() == *to->getAllocation()) return true; LMoveGroup *moves = block->getEntryMoveGroup(alloc()); - return addMove(moves, from, to); + return addMove(moves, from, to, type); } size_t findFirstNonCallSafepoint(CodePosition from) const diff --git a/js/src/jit/StupidAllocator.cpp b/js/src/jit/StupidAllocator.cpp index 9c350555d9cc..8f9751faba72 100644 --- a/js/src/jit/StupidAllocator.cpp +++ b/js/src/jit/StupidAllocator.cpp @@ -143,7 +143,7 @@ StupidAllocator::ensureHasRegister(LInstruction *ins, uint32_t vreg) } RegisterIndex best = allocateRegister(ins, vreg); - loadRegister(ins, vreg, best); + loadRegister(ins, vreg, best, virtualRegisters[vreg]->type()); return registers[best].reg; } @@ -192,7 +192,7 @@ StupidAllocator::syncRegister(LInstruction *ins, RegisterIndex index) uint32_t existing = registers[index].vreg; LAllocation *dest = stackLocation(existing); - input->addAfter(source, dest); + input->addAfter(source, dest, registers[index].type); registers[index].dirty = false; } @@ -206,14 +206,15 @@ StupidAllocator::evictRegister(LInstruction *ins, RegisterIndex index) } void -StupidAllocator::loadRegister(LInstruction *ins, uint32_t vreg, RegisterIndex index) +StupidAllocator::loadRegister(LInstruction *ins, uint32_t vreg, RegisterIndex index, LDefinition::Type type) { // Load a vreg from its stack location to a register. LMoveGroup *input = getInputMoveGroup(ins->id()); LAllocation *source = stackLocation(vreg); LAllocation *dest = new(alloc()) LAllocation(registers[index].reg); - input->addAfter(source, dest); + input->addAfter(source, dest, type); registers[index].set(vreg, ins); + registers[index].type = type; } StupidAllocator::RegisterIndex @@ -312,7 +313,7 @@ StupidAllocator::syncForBlockEnd(LBlock *block, LInstruction *ins) } } - group->add(source, dest); + group->add(source, dest, phi->getDef(0)->type()); } } } @@ -336,14 +337,14 @@ StupidAllocator::allocateForInstruction(LInstruction *ins) AnyRegister reg = ensureHasRegister(ins, vreg); alloc.replace(LAllocation(reg)); } else if (use->policy() == LUse::FIXED) { - AnyRegister reg = GetFixedRegister(virtualRegisters[use->virtualRegister()], use); + AnyRegister reg = GetFixedRegister(virtualRegisters[vreg], use); RegisterIndex index = registerIndex(reg); if (registers[index].vreg != vreg) { evictRegister(ins, index); RegisterIndex existing = findExistingRegister(vreg); if (existing != UINT32_MAX) evictRegister(ins, existing); - loadRegister(ins, vreg, index); + loadRegister(ins, vreg, index, virtualRegisters[vreg]->type()); } alloc.replace(LAllocation(reg)); } else { @@ -409,6 +410,7 @@ StupidAllocator::allocateForDefinition(LInstruction *ins, LDefinition *def) : ins->getOperand(def->getReusedInput())->toRegister()); evictRegister(ins, index); registers[index].set(vreg, ins, true); + registers[index].type = virtualRegisters[vreg]->type(); def->setOutput(LAllocation(registers[index].reg)); } else if (def->policy() == LDefinition::PRESET) { // The result must be a stack location. @@ -417,6 +419,7 @@ StupidAllocator::allocateForDefinition(LInstruction *ins, LDefinition *def) // Find a register to hold the result of the instruction. RegisterIndex best = allocateRegister(ins, vreg); registers[best].set(vreg, ins, true); + registers[best].type = virtualRegisters[vreg]->type(); def->setOutput(LAllocation(registers[best].reg)); } } diff --git a/js/src/jit/StupidAllocator.h b/js/src/jit/StupidAllocator.h index f48befa0ff8b..e7891b65a264 100644 --- a/js/src/jit/StupidAllocator.h +++ b/js/src/jit/StupidAllocator.h @@ -22,6 +22,9 @@ class StupidAllocator : public RegisterAllocator struct AllocatedRegister { AnyRegister reg; + // The type of the value in the register. + LDefinition::Type type; + // Virtual register this physical reg backs, or MISSING_ALLOCATION. uint32_t vreg; @@ -72,7 +75,7 @@ class StupidAllocator : public RegisterAllocator void syncRegister(LInstruction *ins, RegisterIndex index); void evictRegister(LInstruction *ins, RegisterIndex index); - void loadRegister(LInstruction *ins, uint32_t vreg, RegisterIndex index); + void loadRegister(LInstruction *ins, uint32_t vreg, RegisterIndex index, LDefinition::Type type); RegisterIndex findExistingRegister(uint32_t vreg); From cf80b63f425d81d156dc2a1847acc945b6de84bb Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 13 Dec 2013 08:27:47 -0800 Subject: [PATCH 052/144] Bug 949668 - SpiderMonkey: Delete an unused function. r=jandem --- js/src/jit/LIR.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/js/src/jit/LIR.h b/js/src/jit/LIR.h index 9b38d0d77637..15baf7bee34e 100644 --- a/js/src/jit/LIR.h +++ b/js/src/jit/LIR.h @@ -363,10 +363,6 @@ class LStackSlot : public LAllocation : LAllocation(isDouble ? DOUBLE_SLOT : STACK_SLOT, slot) { } - bool isDouble() const { - return kind() == DOUBLE_SLOT; - } - uint32_t slot() const { return data(); } From d6a58ddb0bbcc9fe95568cbcc90112cce73fb16d Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 13 Dec 2013 08:27:47 -0800 Subject: [PATCH 053/144] Bug 949668 - SpiderMonkey: Enum simplification. r=jandem --- js/src/jit/BaselineIC.cpp | 10 ++-- js/src/jit/CodeGenerator.cpp | 59 ++++++++++++-------- js/src/jit/IonMacroAssembler.h | 4 +- js/src/jit/MoveResolver.h | 39 +++++++------ js/src/jit/arm/MacroAssembler-arm.cpp | 49 +++++++++------- js/src/jit/arm/MacroAssembler-arm.h | 18 ++---- js/src/jit/arm/MoveEmitter-arm.cpp | 6 +- js/src/jit/arm/Trampoline-arm.cpp | 8 +-- js/src/jit/shared/CodeGenerator-shared.cpp | 2 +- js/src/jit/shared/MoveEmitter-x86-shared.cpp | 4 +- js/src/jit/x64/MacroAssembler-x64.cpp | 32 +++++++---- js/src/jit/x64/MacroAssembler-x64.h | 18 ++---- js/src/jit/x64/Trampoline-x64.cpp | 37 ++++++------ js/src/jit/x86/CodeGenerator-x86.cpp | 4 +- js/src/jit/x86/MacroAssembler-x86.cpp | 29 ++++++---- js/src/jit/x86/MacroAssembler-x86.h | 18 ++---- js/src/jit/x86/Trampoline-x86.cpp | 50 ++++++++--------- 17 files changed, 204 insertions(+), 183 deletions(-) diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index e12f63ccfe7c..d6a5177a6081 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -2841,9 +2841,9 @@ ICBinaryArith_Double::Compiler::generateStubCode(MacroAssembler &masm) break; case JSOP_MOD: masm.setupUnalignedABICall(2, R0.scratchReg()); - masm.passABIArg(FloatReg0); - masm.passABIArg(FloatReg1); - masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NumberMod), MacroAssembler::DOUBLE); + masm.passABIArg(FloatReg0, MoveOp::DOUBLE); + masm.passABIArg(FloatReg1, MoveOp::DOUBLE); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NumberMod), MoveOp::DOUBLE); JS_ASSERT(ReturnFloatReg == FloatReg0); break; default: @@ -2968,7 +2968,7 @@ ICBinaryArith_DoubleWithInt32::Compiler::generateStubCode(MacroAssembler &masm) masm.bind(&truncateABICall); masm.push(intReg); masm.setupUnalignedABICall(1, scratchReg); - masm.passABIArg(FloatReg0); + masm.passABIArg(FloatReg0, MoveOp::DOUBLE); masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::ToInt32)); masm.storeCallResult(scratchReg); masm.pop(intReg); @@ -3117,7 +3117,7 @@ ICUnaryArith_Double::Compiler::generateStubCode(MacroAssembler &masm) masm.bind(&truncateABICall); masm.setupUnalignedABICall(1, scratchReg); - masm.passABIArg(FloatReg0); + masm.passABIArg(FloatReg0, MoveOp::DOUBLE); masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::ToInt32)); masm.storeCallResult(scratchReg); diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index b6fac075a84a..6a03835e0605 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -1,4 +1,4 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- +/* -*- Mnde: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -1177,13 +1177,28 @@ CodeGenerator::visitMoveGroup(LMoveGroup *group) const LAllocation *from = move.from(); const LAllocation *to = move.to(); + LDefinition::Type type = move.type(); // No bogus moves. JS_ASSERT(*from != *to); JS_ASSERT(!from->isConstant()); JS_ASSERT(from->isDouble() == to->isDouble()); - MoveOp::Kind kind = from->isDouble() ? MoveOp::DOUBLE : MoveOp::GENERAL; + MoveOp::Kind kind; + switch (type) { + case LDefinition::OBJECT: + case LDefinition::SLOTS: +#ifdef JS_NUNBOX32 + case LDefinition::TYPE: + case LDefinition::PAYLOAD: +#else + case LDefinition::BOX: +#endif + case LDefinition::GENERAL: kind = MoveOp::GENERAL; break; + case LDefinition::FLOAT32: + case LDefinition::DOUBLE: kind = MoveOp::DOUBLE; break; + default: MOZ_ASSUME_UNREACHABLE("Unexpected move type"); + } if (!resolver.addMove(toMoveOperand(from), toMoveOperand(to), kind)) return false; @@ -3035,9 +3050,9 @@ bool CodeGenerator::visitAtan2D(LAtan2D *lir) FloatRegister x = ToFloatRegister(lir->x()); masm.setupUnalignedABICall(2, temp); - masm.passABIArg(y); - masm.passABIArg(x); - masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ecmaAtan2), MacroAssembler::DOUBLE); + masm.passABIArg(y, MoveOp::DOUBLE); + masm.passABIArg(x, MoveOp::DOUBLE); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ecmaAtan2), MoveOp::DOUBLE); JS_ASSERT(ToFloatRegister(lir->output()) == ReturnFloatReg); return true; @@ -3050,9 +3065,9 @@ bool CodeGenerator::visitHypot(LHypot *lir) FloatRegister y = ToFloatRegister(lir->y()); masm.setupUnalignedABICall(2, temp); - masm.passABIArg(x); - masm.passABIArg(y); - masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ecmaHypot), MacroAssembler::DOUBLE); + masm.passABIArg(x, MoveOp::DOUBLE); + masm.passABIArg(y, MoveOp::DOUBLE); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ecmaHypot), MoveOp::DOUBLE); JS_ASSERT(ToFloatRegister(lir->output()) == ReturnFloatReg); return true; @@ -3791,10 +3806,10 @@ CodeGenerator::visitPowI(LPowI *ins) // its scratch register. We can therefore save an input register by // reusing the scratch register to pass constants to callWithABI. masm.setupUnalignedABICall(2, temp); - masm.passABIArg(value); + masm.passABIArg(value, MoveOp::DOUBLE); masm.passABIArg(power); - masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::powi), MacroAssembler::DOUBLE); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::powi), MoveOp::DOUBLE); JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg); return true; @@ -3808,9 +3823,9 @@ CodeGenerator::visitPowD(LPowD *ins) Register temp = ToRegister(ins->temp()); masm.setupUnalignedABICall(2, temp); - masm.passABIArg(value); - masm.passABIArg(power); - masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ecmaPow), MacroAssembler::DOUBLE); + masm.passABIArg(value, MoveOp::DOUBLE); + masm.passABIArg(power, MoveOp::DOUBLE); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ecmaPow), MoveOp::DOUBLE); JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg); return true; @@ -3826,7 +3841,7 @@ CodeGenerator::visitRandom(LRandom *ins) masm.setupUnalignedABICall(1, temp2); masm.passABIArg(temp); - masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, math_random_no_outparam), MacroAssembler::DOUBLE); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, math_random_no_outparam), MoveOp::DOUBLE); JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg); return true; @@ -3846,7 +3861,7 @@ CodeGenerator::visitMathFunctionD(LMathFunctionD *ins) masm.movePtr(ImmPtr(mathCache), temp); masm.passABIArg(temp); } - masm.passABIArg(input); + masm.passABIArg(input, MoveOp::DOUBLE); # define MAYBE_CACHED(fcn) (mathCache ? (void*)fcn ## _impl : (void*)fcn ## _uncached) @@ -3927,7 +3942,7 @@ CodeGenerator::visitMathFunctionD(LMathFunctionD *ins) # undef MAYBE_CACHED - masm.callWithABI(funptr, MacroAssembler::DOUBLE); + masm.callWithABI(funptr, MoveOp::DOUBLE); return true; } @@ -3939,7 +3954,7 @@ CodeGenerator::visitMathFunctionF(LMathFunctionF *ins) JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg); masm.setupUnalignedABICall(1, temp); - masm.passABIArg(input); + masm.passABIArg(input, MoveOp::DOUBLE); void *funptr = nullptr; switch (ins->mir()->function()) { @@ -3956,7 +3971,7 @@ CodeGenerator::visitMathFunctionF(LMathFunctionF *ins) MOZ_ASSUME_UNREACHABLE("Unknown or unsupported float32 math function"); } - masm.callWithABI(funptr, MacroAssembler::FLOAT); + masm.callWithABI(funptr, MoveOp::DOUBLE); return true; } @@ -3970,13 +3985,13 @@ CodeGenerator::visitModD(LModD *ins) JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg); masm.setupUnalignedABICall(2, temp); - masm.passABIArg(lhs); - masm.passABIArg(rhs); + masm.passABIArg(lhs, MoveOp::DOUBLE); + masm.passABIArg(rhs, MoveOp::DOUBLE); if (gen->compilingAsmJS()) - masm.callWithABI(AsmJSImm_ModD, MacroAssembler::DOUBLE); + masm.callWithABI(AsmJSImm_ModD, MoveOp::DOUBLE); else - masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NumberMod), MacroAssembler::DOUBLE); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NumberMod), MoveOp::DOUBLE); return true; } diff --git a/js/src/jit/IonMacroAssembler.h b/js/src/jit/IonMacroAssembler.h index 4a03191b8973..297c1c3bb654 100644 --- a/js/src/jit/IonMacroAssembler.h +++ b/js/src/jit/IonMacroAssembler.h @@ -865,12 +865,12 @@ class MacroAssembler : public MacroAssemblerSpecific // been made so that a safepoint can be made at that location. template - void callWithABINoProfiling(const T &fun, Result result = GENERAL) { + void callWithABINoProfiling(const T &fun, MoveOp::Kind result = MoveOp::GENERAL) { MacroAssemblerSpecific::callWithABI(fun, result); } template - void callWithABI(const T &fun, Result result = GENERAL) { + void callWithABI(const T &fun, MoveOp::Kind result = MoveOp::GENERAL) { leaveSPSFrame(); callWithABINoProfiling(fun, result); reenterSPSFrame(); diff --git a/js/src/jit/MoveResolver.h b/js/src/jit/MoveResolver.h index 3ae8a50fd053..099125b57f3d 100644 --- a/js/src/jit/MoveResolver.h +++ b/js/src/jit/MoveResolver.h @@ -18,38 +18,39 @@ namespace jit { // guaranteed that Operand looks like this on all ISAs. class MoveOperand { + public: enum Kind { + // A register in the "integer", aka "general purpose", class. REG, + // A register in the "float" register class. FLOAT_REG, - ADDRESS, - FLOAT_ADDRESS, + // A memory region. + MEMORY, + // The address of a memory region. EFFECTIVE_ADDRESS }; + private: Kind kind_; uint32_t code_; int32_t disp_; public: - enum AddressKind { - MEMORY = ADDRESS, - EFFECTIVE = EFFECTIVE_ADDRESS, - FLOAT = FLOAT_ADDRESS - }; - MoveOperand() { } explicit MoveOperand(const Register ®) : kind_(REG), code_(reg.code()) { } explicit MoveOperand(const FloatRegister ®) : kind_(FLOAT_REG), code_(reg.code()) { } - MoveOperand(const Register ®, int32_t disp, AddressKind addrKind = MEMORY) - : kind_((Kind) addrKind), + MoveOperand(const Register ®, int32_t disp, Kind kind = MEMORY) + : kind_(kind), code_(reg.code()), disp_(disp) { + JS_ASSERT(isMemoryOrEffectiveAddress()); + // With a zero offset, this is a plain reg-to-reg move. - if (disp == 0 && addrKind == EFFECTIVE) + if (disp == 0 && kind_ == EFFECTIVE_ADDRESS) kind_ = REG; } MoveOperand(const MoveOperand &other) @@ -63,18 +64,15 @@ class MoveOperand bool isGeneralReg() const { return kind_ == REG; } - bool isDouble() const { - return kind_ == FLOAT_REG || kind_ == FLOAT_ADDRESS; - } bool isMemory() const { - return kind_ == ADDRESS; - } - bool isFloatAddress() const { - return kind_ == FLOAT_ADDRESS; + return kind_ == MEMORY; } bool isEffectiveAddress() const { return kind_ == EFFECTIVE_ADDRESS; } + bool isMemoryOrEffectiveAddress() const { + return isMemory() || isEffectiveAddress(); + } Register reg() const { JS_ASSERT(isGeneralReg()); return Register::FromCode(code_); @@ -84,10 +82,11 @@ class MoveOperand return FloatRegister::FromCode(code_); } Register base() const { - JS_ASSERT(isMemory() || isEffectiveAddress() || isFloatAddress()); + JS_ASSERT(isMemoryOrEffectiveAddress()); return Register::FromCode(code_); } int32_t disp() const { + JS_ASSERT(isMemoryOrEffectiveAddress()); return disp_; } @@ -96,7 +95,7 @@ class MoveOperand return false; if (code_ != other.code_) return false; - if (isMemory() || isEffectiveAddress()) + if (isMemoryOrEffectiveAddress()) return disp_ == other.disp_; return true; } diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp index 512aad71c061..a8bc75f6f74d 100644 --- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -3508,13 +3508,14 @@ MacroAssemblerARMCompat::setupUnalignedABICall(uint32_t args, const Register &sc } #ifdef JS_CPU_ARM_HARDFP void -MacroAssemblerARMCompat::passABIArg(const MoveOperand &from) +MacroAssemblerARMCompat::passABIArg(const MoveOperand &from, MoveOp::Kind kind) { MoveOperand to; ++passedArgs_; if (!enoughMemory_) return; - if (from.isDouble()) { + switch (kind) { + case MoveOp::DOUBLE: { FloatRegister fr; if (GetFloatArgReg(usedIntSlots_, usedFloatSlots_, &fr)) { if (!from.isFloatReg() || from.floatReg() != fr) { @@ -3528,7 +3529,9 @@ MacroAssemblerARMCompat::passABIArg(const MoveOperand &from) enoughMemory_ = moveResolver_.addMove(from, MoveOperand(sp, disp), MoveOp::DOUBLE); } usedFloatSlots_++; - } else { + break; + } + case MoveOp::GENERAL: { Register r; if (GetIntArgReg(usedIntSlots_, usedFloatSlots_, &r)) { if (!from.isGeneralReg() || from.reg() != r) { @@ -3540,31 +3543,39 @@ MacroAssemblerARMCompat::passABIArg(const MoveOperand &from) enoughMemory_ = moveResolver_.addMove(from, MoveOperand(sp, disp), MoveOp::GENERAL); } usedIntSlots_++; + break; + } + default: + MOZ_ASSUME_UNREACHABLE("Unexpected argument kind"); } } #else void -MacroAssemblerARMCompat::passABIArg(const MoveOperand &from) +MacroAssemblerARMCompat::passABIArg(const MoveOperand &from, MoveOp::Kind kind) { MoveOperand to; uint32_t increment = 1; bool useResolver = true; ++passedArgs_; - MoveOp::Kind kind = MoveOp::GENERAL; - if (from.isDouble()) { + switch (kind) { + case MoveOp::DOUBLE: // Double arguments need to be rounded up to the nearest doubleword // boundary, even if it is in a register! usedSlots_ = (usedSlots_ + 1) & ~1; increment = 2; - kind = MoveOp::DOUBLE; + break; + case MoveOp::GENERAL: + break; + default: + MOZ_ASSUME_UNREACHABLE("Unexpected argument kind"); } Register destReg; MoveOperand dest; if (GetIntArgReg(usedSlots_, 0, &destReg)) { - if (from.isDouble()) { + if (kind == MoveOp::DOUBLE) { floatArgsInGPR[destReg.code() >> 1] = from; floatArgsInGPRValid[destReg.code() >> 1] = true; useResolver = false; @@ -3588,13 +3599,13 @@ MacroAssemblerARMCompat::passABIArg(const MoveOperand &from) void MacroAssemblerARMCompat::passABIArg(const Register ®) { - passABIArg(MoveOperand(reg)); + passABIArg(MoveOperand(reg), MoveOp::GENERAL); } void -MacroAssemblerARMCompat::passABIArg(const FloatRegister &freg) +MacroAssemblerARMCompat::passABIArg(const FloatRegister &freg, MoveOp::Kind kind) { - passABIArg(MoveOperand(freg)); + passABIArg(MoveOperand(freg), kind); } void MacroAssemblerARMCompat::checkStackAlignment() @@ -3642,7 +3653,7 @@ MacroAssemblerARMCompat::callWithABIPre(uint32_t *stackAdjust) if (from.isFloatReg()) { ma_vxfer(VFPRegister(from.floatReg()), to0, to1); } else { - JS_ASSERT(from.isFloatAddress()); + JS_ASSERT(from.isMemory()); // Note: We can safely use the MoveOperand's displacement here, // even if the base is SP: MoveEmitter::toOperand adjusts // SP-relative operands by the difference between the current @@ -3664,25 +3675,25 @@ MacroAssemblerARMCompat::callWithABIPre(uint32_t *stackAdjust) } void -MacroAssemblerARMCompat::callWithABIPost(uint32_t stackAdjust, Result result) +MacroAssemblerARMCompat::callWithABIPost(uint32_t stackAdjust, MoveOp::Kind result) { if (secondScratchReg_ != lr) ma_mov(secondScratchReg_, lr); switch (result) { - case DOUBLE: + case MoveOp::DOUBLE: #ifndef JS_CPU_ARM_HARDFP // Move double from r0/r1 to ReturnFloatReg. as_vxfer(r0, r1, ReturnFloatReg, CoreToFloat); break; #endif - case FLOAT: + case MoveOp::FLOAT32: #ifndef JS_CPU_ARM_HARDFP // Move float32 from r0 to ReturnFloatReg. as_vxfer(r0, InvalidReg, VFPRegister(d0).singleOverlay(), CoreToFloat); break; #endif - case GENERAL: + case MoveOp::GENERAL: break; default: @@ -3702,7 +3713,7 @@ MacroAssemblerARMCompat::callWithABIPost(uint32_t stackAdjust, Result result) } void -MacroAssemblerARMCompat::callWithABI(void *fun, Result result) +MacroAssemblerARMCompat::callWithABI(void *fun, MoveOp::Kind result) { uint32_t stackAdjust; callWithABIPre(&stackAdjust); @@ -3711,7 +3722,7 @@ MacroAssemblerARMCompat::callWithABI(void *fun, Result result) } void -MacroAssemblerARMCompat::callWithABI(AsmJSImmPtr imm, Result result) +MacroAssemblerARMCompat::callWithABI(AsmJSImmPtr imm, MoveOp::Kind result) { uint32_t stackAdjust; callWithABIPre(&stackAdjust); @@ -3720,7 +3731,7 @@ MacroAssemblerARMCompat::callWithABI(AsmJSImmPtr imm, Result result) } void -MacroAssemblerARMCompat::callWithABI(const Address &fun, Result result) +MacroAssemblerARMCompat::callWithABI(const Address &fun, MoveOp::Kind result) { // Load the callee in r12, no instruction between the ldr and call // should clobber it. Note that we can't use fun.base because it may diff --git a/js/src/jit/arm/MacroAssembler-arm.h b/js/src/jit/arm/MacroAssembler-arm.h index ad0e79f2c58f..91612e990896 100644 --- a/js/src/jit/arm/MacroAssembler-arm.h +++ b/js/src/jit/arm/MacroAssembler-arm.h @@ -485,12 +485,6 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM setFramePushed(framePushed_ + value); } public: - enum Result { - GENERAL, - DOUBLE, - FLOAT - }; - MacroAssemblerARMCompat() : inCall_(false), enoughMemory_(true), @@ -1409,9 +1403,9 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM // automatically adjusted. It is extremely important that esp-relative // addresses are computed *after* setupABICall(). Furthermore, no // operations should be emitted while setting arguments. - void passABIArg(const MoveOperand &from); + void passABIArg(const MoveOperand &from, MoveOp::Kind kind); void passABIArg(const Register ®); - void passABIArg(const FloatRegister ®); + void passABIArg(const FloatRegister ®, MoveOp::Kind kind); void passABIArg(const ValueOperand ®s); protected: @@ -1419,13 +1413,13 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM private: void callWithABIPre(uint32_t *stackAdjust); - void callWithABIPost(uint32_t stackAdjust, Result result); + void callWithABIPost(uint32_t stackAdjust, MoveOp::Kind result); public: // Emits a call to a C/C++ function, resolving all argument moves. - void callWithABI(void *fun, Result result = GENERAL); - void callWithABI(AsmJSImmPtr imm, Result result = GENERAL); - void callWithABI(const Address &fun, Result result = GENERAL); + void callWithABI(void *fun, MoveOp::Kind result = MoveOp::GENERAL); + void callWithABI(AsmJSImmPtr imm, MoveOp::Kind result = MoveOp::GENERAL); + void callWithABI(const Address &fun, MoveOp::Kind result = MoveOp::GENERAL); CodeOffsetLabel labelForPatch() { return CodeOffsetLabel(nextOffset().getOffset()); diff --git a/js/src/jit/arm/MoveEmitter-arm.cpp b/js/src/jit/arm/MoveEmitter-arm.cpp index f5ded1122b99..c7543d7e9543 100644 --- a/js/src/jit/arm/MoveEmitter-arm.cpp +++ b/js/src/jit/arm/MoveEmitter-arm.cpp @@ -58,7 +58,7 @@ MoveEmitterARM::spillSlot() const Operand MoveEmitterARM::toOperand(const MoveOperand &operand, bool isFloat) const { - if (operand.isMemory() || operand.isEffectiveAddress() || operand.isFloatAddress()) { + if (operand.isMemoryOrEffectiveAddress()) { if (operand.base() != StackPointer) { JS_ASSERT(operand.disp() < 1024 && operand.disp() > -1024); return Operand(operand.base(), operand.disp()); @@ -193,7 +193,7 @@ MoveEmitterARM::emitMove(const MoveOperand &from, const MoveOperand &to) MOZ_ASSUME_UNREACHABLE("strange move!"); } } else if (to.isGeneralReg()) { - JS_ASSERT(from.isMemory() || from.isEffectiveAddress()); + JS_ASSERT(from.isMemoryOrEffectiveAddress()); if (from.isMemory()) masm.ma_ldr(toOperand(from, false), to.reg()); else @@ -202,7 +202,7 @@ MoveEmitterARM::emitMove(const MoveOperand &from, const MoveOperand &to) // Memory to memory gpr move. Register reg = tempReg(); - JS_ASSERT(from.isMemory() || from.isEffectiveAddress()); + JS_ASSERT(from.isMemoryOrEffectiveAddress()); if (from.isMemory()) masm.ma_ldr(toOperand(from, false), reg); else diff --git a/js/src/jit/arm/Trampoline-arm.cpp b/js/src/jit/arm/Trampoline-arm.cpp index 6a7291338d3f..a53d0d02dd9d 100644 --- a/js/src/jit/arm/Trampoline-arm.cpp +++ b/js/src/jit/arm/Trampoline-arm.cpp @@ -735,22 +735,22 @@ JitRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f) MoveOperand from; switch (f.argProperties(explicitArg)) { case VMFunction::WordByValue: - masm.passABIArg(MoveOperand(argsBase, argDisp)); + masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL); argDisp += sizeof(void *); break; case VMFunction::DoubleByValue: // Values should be passed by reference, not by value, so we // assert that the argument is a double-precision float. JS_ASSERT(f.argPassedInFloatReg(explicitArg)); - masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::FLOAT)); + masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::DOUBLE); argDisp += sizeof(double); break; case VMFunction::WordByRef: - masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE)); + masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS), MoveOp::GENERAL); argDisp += sizeof(void *); break; case VMFunction::DoubleByRef: - masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE)); + masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS), MoveOp::GENERAL); argDisp += 2 * sizeof(void *); break; } diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp index bb6aecb62e36..410af5d2b062 100644 --- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -768,7 +768,7 @@ CodeGeneratorShared::visitOutOfLineTruncateSlow(OutOfLineTruncateSlow *ool) } masm.setupUnalignedABICall(1, dest); - masm.passABIArg(src); + masm.passABIArg(src, MoveOp::DOUBLE); if (gen->compilingAsmJS()) masm.callWithABI(AsmJSImm_ToInt32); else diff --git a/js/src/jit/shared/MoveEmitter-x86-shared.cpp b/js/src/jit/shared/MoveEmitter-x86-shared.cpp index 51ef2b76b73e..0054a2c0e718 100644 --- a/js/src/jit/shared/MoveEmitter-x86-shared.cpp +++ b/js/src/jit/shared/MoveEmitter-x86-shared.cpp @@ -170,7 +170,7 @@ MoveEmitterX86::toAddress(const MoveOperand &operand) const Operand MoveEmitterX86::toOperand(const MoveOperand &operand) const { - if (operand.isMemory() || operand.isEffectiveAddress() || operand.isFloatAddress()) + if (operand.isMemoryOrEffectiveAddress()) return Operand(toAddress(operand)); if (operand.isGeneralReg()) return Operand(operand.reg()); @@ -255,7 +255,7 @@ MoveEmitterX86::emitGeneralMove(const MoveOperand &from, const MoveOperand &to) if (from.isGeneralReg()) { masm.mov(from.reg(), toOperand(to)); } else if (to.isGeneralReg()) { - JS_ASSERT(from.isMemory() || from.isEffectiveAddress()); + JS_ASSERT(from.isMemoryOrEffectiveAddress()); if (from.isMemory()) masm.loadPtr(toAddress(from), to.reg()); else diff --git a/js/src/jit/x64/MacroAssembler-x64.cpp b/js/src/jit/x64/MacroAssembler-x64.cpp index 395a90955272..e9591858bfe8 100644 --- a/js/src/jit/x64/MacroAssembler-x64.cpp +++ b/js/src/jit/x64/MacroAssembler-x64.cpp @@ -134,10 +134,11 @@ MacroAssemblerX64::setupUnalignedABICall(uint32_t args, const Register &scratch) } void -MacroAssemblerX64::passABIArg(const MoveOperand &from) +MacroAssemblerX64::passABIArg(const MoveOperand &from, MoveOp::Kind kind) { MoveOperand to; - if (from.isDouble()) { + switch (kind) { + case MoveOp::DOUBLE: { FloatRegister dest; if (GetFloatArgReg(passedIntArgs_, passedFloatArgs_++, &dest)) { if (from.isFloatReg() && from.floatReg() == dest) { @@ -147,10 +148,15 @@ MacroAssemblerX64::passABIArg(const MoveOperand &from) to = MoveOperand(dest); } else { to = MoveOperand(StackPointer, stackForCall_); - stackForCall_ += sizeof(double); + switch (kind) { + case MoveOp::DOUBLE: stackForCall_ += sizeof(double); break; + default: MOZ_ASSUME_UNREACHABLE("Unexpected float register class argument kind"); + } } enoughMemory_ = moveResolver_.addMove(from, to, MoveOp::DOUBLE); - } else { + break; + } + case MoveOp::GENERAL: { Register dest; if (GetIntArgReg(passedIntArgs_++, passedFloatArgs_, &dest)) { if (from.isGeneralReg() && from.reg() == dest) { @@ -163,19 +169,23 @@ MacroAssemblerX64::passABIArg(const MoveOperand &from) stackForCall_ += sizeof(int64_t); } enoughMemory_ = moveResolver_.addMove(from, to, MoveOp::GENERAL); + break; + } + default: + MOZ_ASSUME_UNREACHABLE("Unexpected argument kind"); } } void MacroAssemblerX64::passABIArg(const Register ®) { - passABIArg(MoveOperand(reg)); + passABIArg(MoveOperand(reg), MoveOp::GENERAL); } void -MacroAssemblerX64::passABIArg(const FloatRegister ®) +MacroAssemblerX64::passABIArg(const FloatRegister ®, MoveOp::Kind kind) { - passABIArg(MoveOperand(reg)); + passABIArg(MoveOperand(reg), kind); } void @@ -219,7 +229,7 @@ MacroAssemblerX64::callWithABIPre(uint32_t *stackAdjust) } void -MacroAssemblerX64::callWithABIPost(uint32_t stackAdjust, Result result) +MacroAssemblerX64::callWithABIPost(uint32_t stackAdjust, MoveOp::Kind result) { freeStack(stackAdjust); if (dynamicAlignment_) @@ -230,7 +240,7 @@ MacroAssemblerX64::callWithABIPost(uint32_t stackAdjust, Result result) } void -MacroAssemblerX64::callWithABI(void *fun, Result result) +MacroAssemblerX64::callWithABI(void *fun, MoveOp::Kind result) { uint32_t stackAdjust; callWithABIPre(&stackAdjust); @@ -239,7 +249,7 @@ MacroAssemblerX64::callWithABI(void *fun, Result result) } void -MacroAssemblerX64::callWithABI(AsmJSImmPtr imm, Result result) +MacroAssemblerX64::callWithABI(AsmJSImmPtr imm, MoveOp::Kind result) { uint32_t stackAdjust; callWithABIPre(&stackAdjust); @@ -259,7 +269,7 @@ IsIntArgReg(Register reg) } void -MacroAssemblerX64::callWithABI(Address fun, Result result) +MacroAssemblerX64::callWithABI(Address fun, MoveOp::Kind result) { if (IsIntArgReg(fun.base)) { // Callee register may be clobbered for an argument. Move the callee to diff --git a/js/src/jit/x64/MacroAssembler-x64.h b/js/src/jit/x64/MacroAssembler-x64.h index 28f4eed9f6f8..d8bd8b36d077 100644 --- a/js/src/jit/x64/MacroAssembler-x64.h +++ b/js/src/jit/x64/MacroAssembler-x64.h @@ -79,12 +79,6 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared using MacroAssemblerX86Shared::callWithExitFrame; using MacroAssemblerX86Shared::branch32; - enum Result { - GENERAL, - DOUBLE, - FLOAT - }; - MacroAssemblerX64() : inCall_(false), enoughMemory_(true) @@ -1199,19 +1193,19 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared // automatically adjusted. It is extremely important that esp-relative // addresses are computed *after* setupABICall(). Furthermore, no // operations should be emitted while setting arguments. - void passABIArg(const MoveOperand &from); + void passABIArg(const MoveOperand &from, MoveOp::Kind kind); void passABIArg(const Register ®); - void passABIArg(const FloatRegister ®); + void passABIArg(const FloatRegister ®, MoveOp::Kind kind); private: void callWithABIPre(uint32_t *stackAdjust); - void callWithABIPost(uint32_t stackAdjust, Result result); + void callWithABIPost(uint32_t stackAdjust, MoveOp::Kind result); public: // Emits a call to a C/C++ function, resolving all argument moves. - void callWithABI(void *fun, Result result = GENERAL); - void callWithABI(AsmJSImmPtr imm, Result result = GENERAL); - void callWithABI(Address fun, Result result = GENERAL); + void callWithABI(void *fun, MoveOp::Kind result = MoveOp::GENERAL); + void callWithABI(AsmJSImmPtr imm, MoveOp::Kind result = MoveOp::GENERAL); + void callWithABI(Address fun, MoveOp::Kind result = MoveOp::GENERAL); void handleFailureWithHandler(void *handler); void handleFailureWithHandlerTail(); diff --git a/js/src/jit/x64/Trampoline-x64.cpp b/js/src/jit/x64/Trampoline-x64.cpp index 14348720e429..0ebc99a01326 100644 --- a/js/src/jit/x64/Trampoline-x64.cpp +++ b/js/src/jit/x64/Trampoline-x64.cpp @@ -586,25 +586,24 @@ JitRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f) size_t argDisp = 0; // Copy arguments. - if (f.explicitArgs) { - for (uint32_t explicitArg = 0; explicitArg < f.explicitArgs; explicitArg++) { - MoveOperand from; - switch (f.argProperties(explicitArg)) { - case VMFunction::WordByValue: - if (f.argPassedInFloatReg(explicitArg)) - masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::FLOAT)); - else - masm.passABIArg(MoveOperand(argsBase, argDisp)); - argDisp += sizeof(void *); - break; - case VMFunction::WordByRef: - masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE)); - argDisp += sizeof(void *); - break; - case VMFunction::DoubleByValue: - case VMFunction::DoubleByRef: - MOZ_ASSUME_UNREACHABLE("NYI: x64 callVM should not be used with 128bits values."); - } + for (uint32_t explicitArg = 0; explicitArg < f.explicitArgs; explicitArg++) { + MoveOperand from; + switch (f.argProperties(explicitArg)) { + case VMFunction::WordByValue: + if (f.argPassedInFloatReg(explicitArg)) + masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::DOUBLE); + else + masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL); + argDisp += sizeof(void *); + break; + case VMFunction::WordByRef: + masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS), + MoveOp::GENERAL); + argDisp += sizeof(void *); + break; + case VMFunction::DoubleByValue: + case VMFunction::DoubleByRef: + MOZ_ASSUME_UNREACHABLE("NYI: x64 callVM should not be used with 128bits values."); } } diff --git a/js/src/jit/x86/CodeGenerator-x86.cpp b/js/src/jit/x86/CodeGenerator-x86.cpp index 534518ff2eaa..48914c71a1fd 100644 --- a/js/src/jit/x86/CodeGenerator-x86.cpp +++ b/js/src/jit/x86/CodeGenerator-x86.cpp @@ -900,7 +900,7 @@ CodeGeneratorX86::visitOutOfLineTruncate(OutOfLineTruncate *ool) saveVolatile(output); masm.setupUnalignedABICall(1, output); - masm.passABIArg(input); + masm.passABIArg(input, MoveOp::DOUBLE); if (gen->compilingAsmJS()) masm.callWithABI(AsmJSImm_ToInt32); else @@ -991,7 +991,7 @@ CodeGeneratorX86::visitOutOfLineTruncateFloat32(OutOfLineTruncateFloat32 *ool) masm.push(input); masm.setupUnalignedABICall(1, output); masm.cvtss2sd(input, input); - masm.passABIArg(input); + masm.passABIArg(input, MoveOp::DOUBLE); if (gen->compilingAsmJS()) masm.callWithABI(AsmJSImm_ToInt32); diff --git a/js/src/jit/x86/MacroAssembler-x86.cpp b/js/src/jit/x86/MacroAssembler-x86.cpp index c430c2778441..3a1629fe45d9 100644 --- a/js/src/jit/x86/MacroAssembler-x86.cpp +++ b/js/src/jit/x86/MacroAssembler-x86.cpp @@ -163,29 +163,34 @@ MacroAssemblerX86::setupUnalignedABICall(uint32_t args, const Register &scratch) } void -MacroAssemblerX86::passABIArg(const MoveOperand &from) +MacroAssemblerX86::passABIArg(const MoveOperand &from, MoveOp::Kind kind) { ++passedArgs_; MoveOperand to = MoveOperand(StackPointer, stackForCall_); - if (from.isDouble()) { + switch (kind) { + case MoveOp::DOUBLE: stackForCall_ += sizeof(double); enoughMemory_ &= moveResolver_.addMove(from, to, MoveOp::DOUBLE); - } else { + break; + case MoveOp::GENERAL: stackForCall_ += sizeof(int32_t); enoughMemory_ &= moveResolver_.addMove(from, to, MoveOp::GENERAL); + break; + default: + MOZ_ASSUME_UNREACHABLE("Unexpected argument kind"); } } void MacroAssemblerX86::passABIArg(const Register ®) { - passABIArg(MoveOperand(reg)); + passABIArg(MoveOperand(reg), MoveOp::GENERAL); } void -MacroAssemblerX86::passABIArg(const FloatRegister ®) +MacroAssemblerX86::passABIArg(const FloatRegister ®, MoveOp::Kind kind) { - passABIArg(MoveOperand(reg)); + passABIArg(MoveOperand(reg), kind); } void @@ -230,16 +235,16 @@ MacroAssemblerX86::callWithABIPre(uint32_t *stackAdjust) } void -MacroAssemblerX86::callWithABIPost(uint32_t stackAdjust, Result result) +MacroAssemblerX86::callWithABIPost(uint32_t stackAdjust, MoveOp::Kind result) { freeStack(stackAdjust); - if (result == DOUBLE) { + if (result == MoveOp::DOUBLE) { reserveStack(sizeof(double)); fstp(Operand(esp, 0)); loadDouble(Operand(esp, 0), ReturnFloatReg); freeStack(sizeof(double)); } - if (result == FLOAT) { + if (result == MoveOp::FLOAT32) { reserveStack(sizeof(float)); fstp32(Operand(esp, 0)); loadFloat(Operand(esp, 0), ReturnFloatReg); @@ -253,7 +258,7 @@ MacroAssemblerX86::callWithABIPost(uint32_t stackAdjust, Result result) } void -MacroAssemblerX86::callWithABI(void *fun, Result result) +MacroAssemblerX86::callWithABI(void *fun, MoveOp::Kind result) { uint32_t stackAdjust; callWithABIPre(&stackAdjust); @@ -262,7 +267,7 @@ MacroAssemblerX86::callWithABI(void *fun, Result result) } void -MacroAssemblerX86::callWithABI(AsmJSImmPtr fun, Result result) +MacroAssemblerX86::callWithABI(AsmJSImmPtr fun, MoveOp::Kind result) { uint32_t stackAdjust; callWithABIPre(&stackAdjust); @@ -271,7 +276,7 @@ MacroAssemblerX86::callWithABI(AsmJSImmPtr fun, Result result) } void -MacroAssemblerX86::callWithABI(const Address &fun, Result result) +MacroAssemblerX86::callWithABI(const Address &fun, MoveOp::Kind result) { uint32_t stackAdjust; callWithABIPre(&stackAdjust); diff --git a/js/src/jit/x86/MacroAssembler-x86.h b/js/src/jit/x86/MacroAssembler-x86.h index e7ee36481786..eb4a64cb8ae7 100644 --- a/js/src/jit/x86/MacroAssembler-x86.h +++ b/js/src/jit/x86/MacroAssembler-x86.h @@ -70,12 +70,6 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared using MacroAssemblerX86Shared::callWithExitFrame; using MacroAssemblerX86Shared::branch32; - enum Result { - GENERAL, - DOUBLE, - FLOAT - }; - MacroAssemblerX86() : inCall_(false), enoughMemory_(true) @@ -1043,19 +1037,19 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared // automatically adjusted. It is extremely important that esp-relative // addresses are computed *after* setupABICall(). Furthermore, no // operations should be emitted while setting arguments. - void passABIArg(const MoveOperand &from); + void passABIArg(const MoveOperand &from, MoveOp::Kind kind); void passABIArg(const Register ®); - void passABIArg(const FloatRegister ®); + void passABIArg(const FloatRegister ®, MoveOp::Kind kind); private: void callWithABIPre(uint32_t *stackAdjust); - void callWithABIPost(uint32_t stackAdjust, Result result); + void callWithABIPost(uint32_t stackAdjust, MoveOp::Kind result); public: // Emits a call to a C/C++ function, resolving all argument moves. - void callWithABI(void *fun, Result result = GENERAL); - void callWithABI(AsmJSImmPtr fun, Result result = GENERAL); - void callWithABI(const Address &fun, Result result = GENERAL); + void callWithABI(void *fun, MoveOp::Kind result = MoveOp::GENERAL); + void callWithABI(AsmJSImmPtr fun, MoveOp::Kind result = MoveOp::GENERAL); + void callWithABI(const Address &fun, MoveOp::Kind result = MoveOp::GENERAL); // Used from within an Exit frame to handle a pending exception. void handleFailureWithHandler(void *handler); diff --git a/js/src/jit/x86/Trampoline-x86.cpp b/js/src/jit/x86/Trampoline-x86.cpp index 2c6558ccee22..6b584b6cbfc1 100644 --- a/js/src/jit/x86/Trampoline-x86.cpp +++ b/js/src/jit/x86/Trampoline-x86.cpp @@ -614,31 +614,31 @@ JitRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f) size_t argDisp = 0; // Copy arguments. - if (f.explicitArgs) { - for (uint32_t explicitArg = 0; explicitArg < f.explicitArgs; explicitArg++) { - MoveOperand from; - switch (f.argProperties(explicitArg)) { - case VMFunction::WordByValue: - masm.passABIArg(MoveOperand(argsBase, argDisp)); - argDisp += sizeof(void *); - break; - case VMFunction::DoubleByValue: - // We don't pass doubles in float registers on x86, so no need - // to check for argPassedInFloatReg. - masm.passABIArg(MoveOperand(argsBase, argDisp)); - argDisp += sizeof(void *); - masm.passABIArg(MoveOperand(argsBase, argDisp)); - argDisp += sizeof(void *); - break; - case VMFunction::WordByRef: - masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE)); - argDisp += sizeof(void *); - break; - case VMFunction::DoubleByRef: - masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE)); - argDisp += 2 * sizeof(void *); - break; - } + for (uint32_t explicitArg = 0; explicitArg < f.explicitArgs; explicitArg++) { + MoveOperand from; + switch (f.argProperties(explicitArg)) { + case VMFunction::WordByValue: + masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL); + argDisp += sizeof(void *); + break; + case VMFunction::DoubleByValue: + // We don't pass doubles in float registers on x86, so no need + // to check for argPassedInFloatReg. + masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL); + argDisp += sizeof(void *); + masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL); + argDisp += sizeof(void *); + break; + case VMFunction::WordByRef: + masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS), + MoveOp::GENERAL); + argDisp += sizeof(void *); + break; + case VMFunction::DoubleByRef: + masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS), + MoveOp::GENERAL); + argDisp += 2 * sizeof(void *); + break; } } From 18f4be66a837a5df5accfd6aef845712fd5233d4 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 13 Dec 2013 08:27:47 -0800 Subject: [PATCH 054/144] Bug 949668 - SpiderMonkey: Add a MoveOp::FLOAT32 r=jandem --- js/src/jit/CodeGenerator.cpp | 6 ++-- js/src/jit/MoveResolver.h | 1 + js/src/jit/arm/Assembler-arm.h | 12 +++++++- js/src/jit/arm/MacroAssembler-arm.cpp | 25 +++++++++------- js/src/jit/arm/MoveEmitter-arm.cpp | 31 ++++++++++++++++---- js/src/jit/shared/MoveEmitter-x86-shared.cpp | 31 ++++++++++++++++---- js/src/jit/x64/MacroAssembler-x64.cpp | 6 ++-- js/src/jit/x86/MacroAssembler-x86.cpp | 18 ++++-------- 8 files changed, 90 insertions(+), 40 deletions(-) diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 6a03835e0605..9f27af7b3c79 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -1195,7 +1195,7 @@ CodeGenerator::visitMoveGroup(LMoveGroup *group) case LDefinition::BOX: #endif case LDefinition::GENERAL: kind = MoveOp::GENERAL; break; - case LDefinition::FLOAT32: + case LDefinition::FLOAT32: kind = MoveOp::FLOAT32; break; case LDefinition::DOUBLE: kind = MoveOp::DOUBLE; break; default: MOZ_ASSUME_UNREACHABLE("Unexpected move type"); } @@ -3954,7 +3954,7 @@ CodeGenerator::visitMathFunctionF(LMathFunctionF *ins) JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg); masm.setupUnalignedABICall(1, temp); - masm.passABIArg(input, MoveOp::DOUBLE); + masm.passABIArg(input, MoveOp::FLOAT32); void *funptr = nullptr; switch (ins->mir()->function()) { @@ -3971,7 +3971,7 @@ CodeGenerator::visitMathFunctionF(LMathFunctionF *ins) MOZ_ASSUME_UNREACHABLE("Unknown or unsupported float32 math function"); } - masm.callWithABI(funptr, MoveOp::DOUBLE); + masm.callWithABI(funptr, MoveOp::FLOAT32); return true; } diff --git a/js/src/jit/MoveResolver.h b/js/src/jit/MoveResolver.h index 099125b57f3d..acb1492051d6 100644 --- a/js/src/jit/MoveResolver.h +++ b/js/src/jit/MoveResolver.h @@ -115,6 +115,7 @@ class MoveOp public: enum Kind { GENERAL, + FLOAT32, DOUBLE }; diff --git a/js/src/jit/arm/Assembler-arm.h b/js/src/jit/arm/Assembler-arm.h index 362d7d183470..9c5107672729 100644 --- a/js/src/jit/arm/Assembler-arm.h +++ b/js/src/jit/arm/Assembler-arm.h @@ -2152,9 +2152,19 @@ GetIntArgStackDisp(uint32_t usedIntArgs, uint32_t usedFloatArgs, uint32_t *paddi } static inline uint32_t -GetFloatArgStackDisp(uint32_t usedIntArgs, uint32_t usedFloatArgs, uint32_t *padding) +GetFloat32ArgStackDisp(uint32_t usedIntArgs, uint32_t usedFloatArgs, uint32_t *padding) { + JS_ASSERT(usedFloatArgs >= NumFloatArgRegs); + uint32_t intSlots = 0; + if (usedIntArgs > NumIntArgRegs) + intSlots = usedIntArgs - NumIntArgRegs; + uint32_t float32Slots = usedFloatArgs - NumFloatArgRegs; + return (intSlots + float32Slots + *padding) * STACK_SLOT_SIZE; +} +static inline uint32_t +GetDoubleArgStackDisp(uint32_t usedIntArgs, uint32_t usedFloatArgs, uint32_t *padding) +{ JS_ASSERT(usedFloatArgs >= NumFloatArgRegs); uint32_t intSlots = 0; if (usedIntArgs > NumIntArgRegs) { diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp index a8bc75f6f74d..64fbbf5b9f23 100644 --- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -3515,18 +3515,20 @@ MacroAssemblerARMCompat::passABIArg(const MoveOperand &from, MoveOp::Kind kind) if (!enoughMemory_) return; switch (kind) { + case MoveOp::FLOAT32: case MoveOp::DOUBLE: { FloatRegister fr; if (GetFloatArgReg(usedIntSlots_, usedFloatSlots_, &fr)) { - if (!from.isFloatReg() || from.floatReg() != fr) { - enoughMemory_ = moveResolver_.addMove(from, MoveOperand(fr), MoveOp::DOUBLE); + if (from.isFloatReg() && from.floatReg() == fr) { + // Nothing to do; the value is in the right register already + return; } - // else nothing to do; the value is in the right register already + to = MoveOperand(fr); } else { // If (and only if) the integer registers have started spilling, do we - // need to take the double register's alignment into account + // need to take the register's alignment into account uint32_t disp = GetFloatArgStackDisp(usedIntSlots_, usedFloatSlots_, &padding_); - enoughMemory_ = moveResolver_.addMove(from, MoveOperand(sp, disp), MoveOp::DOUBLE); + to = MoveOperand(sp, disp); } usedFloatSlots_++; break; @@ -3534,13 +3536,14 @@ MacroAssemblerARMCompat::passABIArg(const MoveOperand &from, MoveOp::Kind kind) case MoveOp::GENERAL: { Register r; if (GetIntArgReg(usedIntSlots_, usedFloatSlots_, &r)) { - if (!from.isGeneralReg() || from.reg() != r) { - enoughMemory_ = moveResolver_.addMove(from, MoveOperand(r), MoveOp::GENERAL); + if (from.isGeneralReg() && from.reg() == r) { + // Nothing to do; the value is in the right register already + return; } - // else nothing to do; the value is in the right register already + to = MoveOperand(r); } else { uint32_t disp = GetIntArgStackDisp(usedIntSlots_, usedFloatSlots_, &padding_); - enoughMemory_ = moveResolver_.addMove(from, MoveOperand(sp, disp), MoveOp::GENERAL); + to = MoveOperand(sp, disp); } usedIntSlots_++; break; @@ -3549,6 +3552,7 @@ MacroAssemblerARMCompat::passABIArg(const MoveOperand &from, MoveOp::Kind kind) MOZ_ASSUME_UNREACHABLE("Unexpected argument kind"); } + enoughMemory_ = moveResolver_.addMove(from, to, kind); } #else @@ -3566,6 +3570,7 @@ MacroAssemblerARMCompat::passABIArg(const MoveOperand &from, MoveOp::Kind kind) usedSlots_ = (usedSlots_ + 1) & ~1; increment = 2; break; + case MoveOp::FLOAT32: case MoveOp::GENERAL: break; default: @@ -3575,7 +3580,7 @@ MacroAssemblerARMCompat::passABIArg(const MoveOperand &from, MoveOp::Kind kind) Register destReg; MoveOperand dest; if (GetIntArgReg(usedSlots_, 0, &destReg)) { - if (kind == MoveOp::DOUBLE) { + if (kind == MoveOp::DOUBLE || kind == MoveOp::FLOAT32) { floatArgsInGPR[destReg.code() >> 1] = from; floatArgsInGPRValid[destReg.code() >> 1] = true; useResolver = false; diff --git a/js/src/jit/arm/MoveEmitter-arm.cpp b/js/src/jit/arm/MoveEmitter-arm.cpp index c7543d7e9543..549d9f6e1a05 100644 --- a/js/src/jit/arm/MoveEmitter-arm.cpp +++ b/js/src/jit/arm/MoveEmitter-arm.cpp @@ -108,7 +108,9 @@ MoveEmitterARM::breakCycle(const MoveOperand &from, const MoveOperand &to, MoveO // // This case handles (A -> B), which we reach first. We save B, then allow // the original move to continue. - if (kind == MoveOp::DOUBLE) { + switch (kind) { + case MoveOp::FLOAT32: + case MoveOp::DOUBLE: if (to.isMemory()) { FloatRegister temp = ScratchFloatReg; masm.ma_vldr(toOperand(to, true), temp); @@ -116,7 +118,8 @@ MoveEmitterARM::breakCycle(const MoveOperand &from, const MoveOperand &to, MoveO } else { masm.ma_vstr(to.floatReg(), cycleSlot()); } - } else { + break; + case MoveOp::GENERAL: // an non-vfp value if (to.isMemory()) { Register temp = tempReg(); @@ -130,6 +133,9 @@ MoveEmitterARM::breakCycle(const MoveOperand &from, const MoveOperand &to, MoveO } masm.ma_str(to.reg(), cycleSlot()); } + break; + default: + MOZ_ASSUME_UNREACHABLE("Unexpected move kind"); } } @@ -142,7 +148,9 @@ MoveEmitterARM::completeCycle(const MoveOperand &from, const MoveOperand &to, Mo // // This case handles (B -> A), which we reach last. We emit a move from the // saved value of B, to A. - if (kind == MoveOp::DOUBLE) { + switch (kind) { + case MoveOp::FLOAT32: + case MoveOp::DOUBLE: if (to.isMemory()) { FloatRegister temp = ScratchFloatReg; masm.ma_vldr(cycleSlot(), temp); @@ -150,7 +158,8 @@ MoveEmitterARM::completeCycle(const MoveOperand &from, const MoveOperand &to, Mo } else { masm.ma_vldr(cycleSlot(), to.floatReg()); } - } else { + break; + case MoveOp::GENERAL: if (to.isMemory()) { Register temp = tempReg(); masm.ma_ldr(cycleSlot(), temp); @@ -162,6 +171,9 @@ MoveEmitterARM::completeCycle(const MoveOperand &from, const MoveOperand &to, Mo } masm.ma_ldr(cycleSlot(), to.reg()); } + break; + default: + MOZ_ASSUME_UNREACHABLE("Unexpected move kind"); } } @@ -248,10 +260,17 @@ MoveEmitterARM::emit(const MoveOp &move) inCycle_ = true; } - if (move.kind() == MoveOp::DOUBLE) + switch (move.kind()) { + case MoveOp::FLOAT32: + case MoveOp::DOUBLE: emitDoubleMove(from, to); - else + break; + case MoveOp::GENERAL: emitMove(from, to); + break; + default: + MOZ_ASSUME_UNREACHABLE("Unexpected move kind"); + } } void diff --git a/js/src/jit/shared/MoveEmitter-x86-shared.cpp b/js/src/jit/shared/MoveEmitter-x86-shared.cpp index 0054a2c0e718..597926d11770 100644 --- a/js/src/jit/shared/MoveEmitter-x86-shared.cpp +++ b/js/src/jit/shared/MoveEmitter-x86-shared.cpp @@ -128,10 +128,17 @@ MoveEmitterX86::emit(const MoveResolver &moves) } // A normal move which is not part of a cycle. - if (move.kind() == MoveOp::DOUBLE) + switch (move.kind()) { + case MoveOp::FLOAT32: + case MoveOp::DOUBLE: emitDoubleMove(from, to); - else + break; + case MoveOp::GENERAL: emitGeneralMove(from, to); + break; + default: + MOZ_ASSUME_UNREACHABLE("Unexpected move kind"); + } } } @@ -212,15 +219,21 @@ MoveEmitterX86::breakCycle(const MoveOperand &to, MoveOp::Kind kind) // // This case handles (A -> B), which we reach first. We save B, then allow // the original move to continue. - if (kind == MoveOp::DOUBLE) { + switch (kind) { + case MoveOp::FLOAT32: + case MoveOp::DOUBLE: if (to.isMemory()) { masm.loadDouble(toAddress(to), ScratchFloatReg); masm.storeDouble(ScratchFloatReg, cycleSlot()); } else { masm.storeDouble(to.floatReg(), cycleSlot()); } - } else { + break; + case MoveOp::GENERAL: masm.Push(toOperand(to)); + break; + default: + MOZ_ASSUME_UNREACHABLE("Unexpected move kind"); } } @@ -233,19 +246,25 @@ MoveEmitterX86::completeCycle(const MoveOperand &to, MoveOp::Kind kind) // // This case handles (B -> A), which we reach last. We emit a move from the // saved value of B, to A. - if (kind == MoveOp::DOUBLE) { + switch (kind) { + case MoveOp::FLOAT32: + case MoveOp::DOUBLE: if (to.isMemory()) { masm.loadDouble(cycleSlot(), ScratchFloatReg); masm.storeDouble(ScratchFloatReg, toAddress(to)); } else { masm.loadDouble(cycleSlot(), to.floatReg()); } - } else { + break; + case MoveOp::GENERAL: if (to.isMemory()) { masm.Pop(toPopOperand(to)); } else { masm.Pop(to.reg()); } + break; + default: + MOZ_ASSUME_UNREACHABLE("Unexpected move kind"); } } diff --git a/js/src/jit/x64/MacroAssembler-x64.cpp b/js/src/jit/x64/MacroAssembler-x64.cpp index e9591858bfe8..db522ee87ed4 100644 --- a/js/src/jit/x64/MacroAssembler-x64.cpp +++ b/js/src/jit/x64/MacroAssembler-x64.cpp @@ -138,6 +138,7 @@ MacroAssemblerX64::passABIArg(const MoveOperand &from, MoveOp::Kind kind) { MoveOperand to; switch (kind) { + case MoveOp::FLOAT32: case MoveOp::DOUBLE: { FloatRegister dest; if (GetFloatArgReg(passedIntArgs_, passedFloatArgs_++, &dest)) { @@ -149,11 +150,11 @@ MacroAssemblerX64::passABIArg(const MoveOperand &from, MoveOp::Kind kind) } else { to = MoveOperand(StackPointer, stackForCall_); switch (kind) { + case MoveOp::FLOAT32: stackForCall_ += sizeof(float); break; case MoveOp::DOUBLE: stackForCall_ += sizeof(double); break; default: MOZ_ASSUME_UNREACHABLE("Unexpected float register class argument kind"); } } - enoughMemory_ = moveResolver_.addMove(from, to, MoveOp::DOUBLE); break; } case MoveOp::GENERAL: { @@ -168,12 +169,13 @@ MacroAssemblerX64::passABIArg(const MoveOperand &from, MoveOp::Kind kind) to = MoveOperand(StackPointer, stackForCall_); stackForCall_ += sizeof(int64_t); } - enoughMemory_ = moveResolver_.addMove(from, to, MoveOp::GENERAL); break; } default: MOZ_ASSUME_UNREACHABLE("Unexpected argument kind"); } + + enoughMemory_ = moveResolver_.addMove(from, to, kind); } void diff --git a/js/src/jit/x86/MacroAssembler-x86.cpp b/js/src/jit/x86/MacroAssembler-x86.cpp index 3a1629fe45d9..a4b00cdc27fb 100644 --- a/js/src/jit/x86/MacroAssembler-x86.cpp +++ b/js/src/jit/x86/MacroAssembler-x86.cpp @@ -168,17 +168,12 @@ MacroAssemblerX86::passABIArg(const MoveOperand &from, MoveOp::Kind kind) ++passedArgs_; MoveOperand to = MoveOperand(StackPointer, stackForCall_); switch (kind) { - case MoveOp::DOUBLE: - stackForCall_ += sizeof(double); - enoughMemory_ &= moveResolver_.addMove(from, to, MoveOp::DOUBLE); - break; - case MoveOp::GENERAL: - stackForCall_ += sizeof(int32_t); - enoughMemory_ &= moveResolver_.addMove(from, to, MoveOp::GENERAL); - break; - default: - MOZ_ASSUME_UNREACHABLE("Unexpected argument kind"); + case MoveOp::FLOAT32: stackForCall_ += sizeof(float); break; + case MoveOp::DOUBLE: stackForCall_ += sizeof(double); break; + case MoveOp::GENERAL: stackForCall_ += sizeof(int32_t); break; + default: MOZ_ASSUME_UNREACHABLE("Unexpected argument kind"); } + enoughMemory_ &= moveResolver_.addMove(from, to, kind); } void @@ -243,8 +238,7 @@ MacroAssemblerX86::callWithABIPost(uint32_t stackAdjust, MoveOp::Kind result) fstp(Operand(esp, 0)); loadDouble(Operand(esp, 0), ReturnFloatReg); freeStack(sizeof(double)); - } - if (result == MoveOp::FLOAT32) { + } else if (result == MoveOp::FLOAT32) { reserveStack(sizeof(float)); fstp32(Operand(esp, 0)); loadFloat(Operand(esp, 0), ReturnFloatReg); From accec5413439fa92818b2a8b7c6f1e50fa115db6 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 13 Dec 2013 08:27:47 -0800 Subject: [PATCH 055/144] Bug 949171 - SpiderMonkey: Pad the local slot size by at least sizeof(Value). r=nbp --- js/src/jit/LIR.h | 21 +++++++++++++++++++-- js/src/jit/shared/CodeGenerator-shared.cpp | 3 +-- js/src/jit/shared/CodeGenerator-shared.h | 11 ++++++----- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/js/src/jit/LIR.h b/js/src/jit/LIR.h index 15baf7bee34e..20ad0db4317d 100644 --- a/js/src/jit/LIR.h +++ b/js/src/jit/LIR.h @@ -1394,7 +1394,20 @@ class LIRGraph localSlotCount_ = localSlotCount; } uint32_t localSlotCount() const { - return AlignBytes(localSlotCount_, StackAlignment / STACK_SLOT_SIZE); + return localSlotCount_; + } + // Return the localSlotCount() value rounded up so that it satisfies the + // platform stack alignment requirement, and so that it's a multiple of + // the number of slots per Value. + uint32_t paddedLocalSlotCount() const { + // Round to StackAlignment, but also round to at least sizeof(Value) in + // case that's greater, because StackOffsetOfPassedArg rounds argument + // slots to 8-byte boundaries. + size_t Alignment = Max(sizeof(StackAlignment), sizeof(Value)); + return AlignBytes(localSlotCount(), Alignment / STACK_SLOT_SIZE); + } + size_t paddedLocalSlotsSize() const { + return paddedLocalSlotCount() * STACK_SLOT_SIZE; } void setArgumentSlotCount(uint32_t argumentSlotCount) { argumentSlotCount_ = argumentSlotCount; @@ -1402,8 +1415,12 @@ class LIRGraph uint32_t argumentSlotCount() const { return argumentSlotCount_; } + size_t argumentsSize() const { + JS_STATIC_ASSERT(sizeof(Value) >= size_t(STACK_SLOT_SIZE)); + return argumentSlotCount() * sizeof(Value); + } uint32_t totalSlotCount() const { - return localSlotCount() + (argumentSlotCount() * sizeof(Value) / STACK_SLOT_SIZE); + return paddedLocalSlotCount() + (argumentsSize() / STACK_SLOT_SIZE); } bool addConstantToPool(const Value &v, uint32_t *index); size_t numConstants() const { diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp index 410af5d2b062..4441b399e9a7 100644 --- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -49,8 +49,7 @@ CodeGeneratorShared::CodeGeneratorShared(MIRGenerator *gen, LIRGraph *graph, Mac sps_(&GetIonContext()->runtime->spsProfiler(), &lastPC_), osrEntryOffset_(0), skipArgCheckEntryOffset_(0), - frameDepth_(graph->localSlotCount() * sizeof(STACK_SLOT_SIZE) + - graph->argumentSlotCount() * sizeof(Value)) + frameDepth_(graph->paddedLocalSlotsSize() + graph->argumentsSize()) { if (!gen->compilingAsmJS()) masm.setInstrumentation(&sps_); diff --git a/js/src/jit/shared/CodeGenerator-shared.h b/js/src/jit/shared/CodeGenerator-shared.h index f2bc4f82d894..f174640cf459 100644 --- a/js/src/jit/shared/CodeGenerator-shared.h +++ b/js/src/jit/shared/CodeGenerator-shared.h @@ -170,16 +170,17 @@ class CodeGeneratorShared : public LInstructionVisitor // A slot of 0 is permitted only to calculate %esp offset for calls. JS_ASSERT(slot >= 0 && slot <= int32_t(graph.argumentSlotCount())); int32_t offset = masm.framePushed() - - (graph.localSlotCount() * STACK_SLOT_SIZE) - + graph.paddedLocalSlotsSize() - (slot * sizeof(Value)); + // Passed arguments go below A function's local stack storage. // When arguments are being pushed, there is nothing important on the stack. // Therefore, It is safe to push the arguments down arbitrarily. Pushing - // by 8 is desirable since everything on the stack is a Value, which is 8 - // bytes large. - - offset &= ~7; + // by sizeof(Value) is desirable since everything on the stack is a Value. + // Note that paddedLocalSlotCount() aligns to at least a Value boundary + // specifically to support this. JS_ASSERT(offset >= 0); + JS_ASSERT(offset % sizeof(Value) == 0); return offset; } From bf65a5665cb0cd2a61969a29c17b9dda7015712e Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 13 Dec 2013 08:27:47 -0800 Subject: [PATCH 056/144] Bug 949171 - IonMonkey: Use the right block for EffectiveAddressAnalysis r=nbp --- js/src/jit/EffectiveAddressAnalysis.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/src/jit/EffectiveAddressAnalysis.cpp b/js/src/jit/EffectiveAddressAnalysis.cpp index 698696f97367..207e23a60467 100644 --- a/js/src/jit/EffectiveAddressAnalysis.cpp +++ b/js/src/jit/EffectiveAddressAnalysis.cpp @@ -12,7 +12,7 @@ using namespace js; using namespace jit; static void -AnalyzeLsh(TempAllocator &alloc, MBasicBlock *block, MLsh *lsh) +AnalyzeLsh(TempAllocator &alloc, MLsh *lsh) { if (lsh->specialization() != MIRType_Int32) return; @@ -86,7 +86,7 @@ AnalyzeLsh(TempAllocator &alloc, MBasicBlock *block, MLsh *lsh) MEffectiveAddress *eaddr = MEffectiveAddress::New(alloc, base, index, scale, displacement); last->replaceAllUsesWith(eaddr); - block->insertAfter(last, eaddr); + last->block()->insertAfter(last, eaddr); } // This analysis converts patterns of the form: @@ -109,7 +109,7 @@ EffectiveAddressAnalysis::analyze() for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) { for (MInstructionIterator i = block->begin(); i != block->end(); i++) { if (i->isLsh()) - AnalyzeLsh(graph_.alloc(), *block, i->toLsh()); + AnalyzeLsh(graph_.alloc(), i->toLsh()); } } return true; From 9f4e4d2fefa7cb85a36104ab82f2c0a412db1209 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 13 Dec 2013 08:27:47 -0800 Subject: [PATCH 057/144] Bug 949171 - SpiderMonkey: Tighten up various assertions. r=nbp --- js/src/jit/BacktrackingAllocator.cpp | 1 + js/src/jit/BitSet.h | 12 ++++++------ js/src/jit/CodeGenerator.cpp | 12 ++++++++++-- js/src/jit/LIR.cpp | 5 +++-- js/src/jit/Lowering.cpp | 1 + js/src/jit/MIR.h | 1 + js/src/jit/MIRGraph.cpp | 2 ++ js/src/jit/RangeAnalysis.cpp | 2 +- 8 files changed, 25 insertions(+), 11 deletions(-) diff --git a/js/src/jit/BacktrackingAllocator.cpp b/js/src/jit/BacktrackingAllocator.cpp index bd601cc0a884..803079fc2879 100644 --- a/js/src/jit/BacktrackingAllocator.cpp +++ b/js/src/jit/BacktrackingAllocator.cpp @@ -835,6 +835,7 @@ BacktrackingAllocator::spill(LiveInterval *interval) IonSpew(IonSpew_RegAlloc, "Spilling interval"); JS_ASSERT(interval->requirement()->kind() == Requirement::NONE); + JS_ASSERT(!interval->getAllocation()->isStackSlot()); // We can't spill bogus intervals. JS_ASSERT(interval->hasVreg()); diff --git a/js/src/jit/BitSet.h b/js/src/jit/BitSet.h index 730557be1efb..4135c346ad38 100644 --- a/js/src/jit/BitSet.h +++ b/js/src/jit/BitSet.h @@ -58,7 +58,7 @@ class BitSet : private TempObject // O(1): Check if this set contains the given value. bool contains(unsigned int value) const { JS_ASSERT(bits_); - JS_ASSERT(value <= max_); + JS_ASSERT(value < max_); return !!(bits_[wordForValue(value)] & bitForValue(value)); } @@ -69,7 +69,7 @@ class BitSet : private TempObject // O(1): Insert the given value into this set. void insert(unsigned int value) { JS_ASSERT(bits_); - JS_ASSERT(value <= max_); + JS_ASSERT(value < max_); bits_[wordForValue(value)] |= bitForValue(value); } @@ -80,7 +80,7 @@ class BitSet : private TempObject // O(1): Remove the given value from this set. void remove(unsigned int value) { JS_ASSERT(bits_); - JS_ASSERT(value <= max_); + JS_ASSERT(value < max_); bits_[wordForValue(value)] &= ~bitForValue(value); } @@ -137,7 +137,7 @@ class BitSet::Iterator inline Iterator& operator++(int dummy) { JS_ASSERT(more()); - JS_ASSERT(index_ <= set_.max_); + JS_ASSERT(index_ < set_.max_); index_++; value_ >>= 1; @@ -158,12 +158,12 @@ class BitSet::Iterator index_ += numZeros; value_ >>= numZeros; - JS_ASSERT_IF(index_ <= set_.max_, set_.contains(index_)); + JS_ASSERT_IF(index_ < set_.max_, set_.contains(index_)); return *this; } unsigned int operator *() { - JS_ASSERT(index_ <= set_.max_); + JS_ASSERT(index_ < set_.max_); return index_; } }; diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 9f27af7b3c79..f0c05f4ed83b 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -1139,6 +1139,7 @@ CodeGenerator::visitStackArgT(LStackArgT *lir) const LAllocation *arg = lir->getArgument(); MIRType argType = lir->mir()->getArgument()->type(); uint32_t argslot = lir->argslot(); + JS_ASSERT(argslot - 1u < graph.argumentSlotCount()); int32_t stack_offset = StackOffsetOfPassedArg(argslot); Address dest(StackPointer, stack_offset); @@ -1150,7 +1151,9 @@ CodeGenerator::visitStackArgT(LStackArgT *lir) else masm.storeValue(*(arg->toConstant()), dest); - return pushedArgumentSlots_.append(StackOffsetToSlot(stack_offset)); + uint32_t slot = StackOffsetToSlot(stack_offset); + JS_ASSERT(slot - 1u < graph.totalSlotCount()); + return pushedArgumentSlots_.append(slot); } bool @@ -1158,10 +1161,15 @@ CodeGenerator::visitStackArgV(LStackArgV *lir) { ValueOperand val = ToValue(lir, 0); uint32_t argslot = lir->argslot(); + JS_ASSERT(argslot - 1u < graph.argumentSlotCount()); + int32_t stack_offset = StackOffsetOfPassedArg(argslot); masm.storeValue(val, Address(StackPointer, stack_offset)); - return pushedArgumentSlots_.append(StackOffsetToSlot(stack_offset)); + + uint32_t slot = StackOffsetToSlot(stack_offset); + JS_ASSERT(slot - 1u < graph.totalSlotCount()); + return pushedArgumentSlots_.append(slot); } bool diff --git a/js/src/jit/LIR.cpp b/js/src/jit/LIR.cpp index 114a6724841e..7fef35f37b2f 100644 --- a/js/src/jit/LIR.cpp +++ b/js/src/jit/LIR.cpp @@ -73,8 +73,9 @@ LBlock::lastId() { LInstruction *last = *instructions_.rbegin(); JS_ASSERT(last->id()); - if (last->numDefs()) - return last->getDef(last->numDefs() - 1)->virtualRegister(); + // The last instruction is a control flow instruction which does not have + // any output. + JS_ASSERT(last->numDefs() == 0); return last->id(); } diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 875ae3b7ff1e..5f0b099ba712 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -305,6 +305,7 @@ LIRGenerator::visitPassArg(MPassArg *arg) { MDefinition *opd = arg->getArgument(); uint32_t argslot = getArgumentSlot(arg->getArgnum()); + JS_ASSERT(arg->getArgnum() < prepareCallStack_.back()->argc()); // Pass through the virtual register of the operand. // This causes snapshots to correctly copy the operand on the stack. diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 97207eed77b7..3eff668f0e5a 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -2829,6 +2829,7 @@ class MPassArg // Set by the MCall. void setArgnum(uint32_t argnum) { argnum_ = argnum; + JS_ASSERT(argnum_ >= 0); } uint32_t getArgnum() const { JS_ASSERT(argnum_ >= 0); diff --git a/js/src/jit/MIRGraph.cpp b/js/src/jit/MIRGraph.cpp index ac4034f6a536..96fb758b3016 100644 --- a/js/src/jit/MIRGraph.cpp +++ b/js/src/jit/MIRGraph.cpp @@ -763,6 +763,7 @@ MBasicBlock::discardAllResumePoints(bool discardEntry) void MBasicBlock::insertBefore(MInstruction *at, MInstruction *ins) { + JS_ASSERT(at->block() == this); ins->setBlock(this); graph().allocDefinitionId(ins); instructions_.insertBefore(at, ins); @@ -772,6 +773,7 @@ MBasicBlock::insertBefore(MInstruction *at, MInstruction *ins) void MBasicBlock::insertAfter(MInstruction *at, MInstruction *ins) { + JS_ASSERT(at->block() == this); ins->setBlock(this); graph().allocDefinitionId(ins); instructions_.insertAfter(at, ins); diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp index a60f4ca555a1..bef8925e1d0a 100644 --- a/js/src/jit/RangeAnalysis.cpp +++ b/js/src/jit/RangeAnalysis.cpp @@ -994,7 +994,7 @@ MPhi::computeRange(TempAllocator &alloc) return; Range *range = nullptr; - JS_ASSERT(getOperand(0)->op() != MDefinition::Op_OsrValue); + JS_ASSERT(!isOSRLikeValue(getOperand(0))); for (size_t i = 0, e = numOperands(); i < e; i++) { if (getOperand(i)->block()->unreachable()) { IonSpew(IonSpew_Range, "Ignoring unreachable input %d", getOperand(i)->id()); From 2607a0f9638a03520f94f5b3b57de18f09a168de Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 13 Dec 2013 08:27:47 -0800 Subject: [PATCH 058/144] Bug 949171 - SpiderMonkey: Rename bitset's max to numBits. r=nbp --- js/src/jit/BitSet.cpp | 12 ++++++------ js/src/jit/BitSet.h | 43 ++++++++++++++++++++++--------------------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/js/src/jit/BitSet.cpp b/js/src/jit/BitSet.cpp index c320ac8574e4..df5fe4846eae 100644 --- a/js/src/jit/BitSet.cpp +++ b/js/src/jit/BitSet.cpp @@ -10,9 +10,9 @@ using namespace js; using namespace js::jit; BitSet * -BitSet::New(TempAllocator &alloc, unsigned int max) +BitSet::New(TempAllocator &alloc, unsigned int numBits) { - BitSet *result = new(alloc) BitSet(max); + BitSet *result = new(alloc) BitSet(numBits); if (!result->init(alloc)) return nullptr; return result; @@ -47,7 +47,7 @@ void BitSet::insertAll(const BitSet *other) { JS_ASSERT(bits_); - JS_ASSERT(other->max_ == max_); + JS_ASSERT(other->numBits_ == numBits_); JS_ASSERT(other->bits_); for (unsigned int i = 0; i < numWords(); i++) @@ -58,7 +58,7 @@ void BitSet::removeAll(const BitSet *other) { JS_ASSERT(bits_); - JS_ASSERT(other->max_ == max_); + JS_ASSERT(other->numBits_ == numBits_); JS_ASSERT(other->bits_); for (unsigned int i = 0; i < numWords(); i++) @@ -69,7 +69,7 @@ void BitSet::intersect(const BitSet *other) { JS_ASSERT(bits_); - JS_ASSERT(other->max_ == max_); + JS_ASSERT(other->numBits_ == numBits_); JS_ASSERT(other->bits_); for (unsigned int i = 0; i < numWords(); i++) @@ -81,7 +81,7 @@ bool BitSet::fixedPointIntersect(const BitSet *other) { JS_ASSERT(bits_); - JS_ASSERT(other->max_ == max_); + JS_ASSERT(other->numBits_ == numBits_); JS_ASSERT(other->bits_); bool changed = false; diff --git a/js/src/jit/BitSet.h b/js/src/jit/BitSet.h index 4135c346ad38..434b2bac5a77 100644 --- a/js/src/jit/BitSet.h +++ b/js/src/jit/BitSet.h @@ -16,7 +16,8 @@ namespace jit { // Provides constant time set insertion and removal, and fast linear // set operations such as intersection, difference, and union. -// N.B. All set operations must be performed on sets with the same maximum. +// N.B. All set operations must be performed on sets with the same number +// of bits. class BitSet : private TempObject { public: @@ -25,11 +26,11 @@ class BitSet : private TempObject } private: - BitSet(unsigned int max) : - max_(max), + BitSet(unsigned int numBits) : + numBits_(numBits), bits_(nullptr) {} - unsigned int max_; + unsigned int numBits_; uint32_t *bits_; static inline uint32_t bitForValue(unsigned int value) { @@ -41,7 +42,7 @@ class BitSet : private TempObject } inline unsigned int numWords() const { - return RawLengthForBits(max_); + return RawLengthForBits(numBits_); } bool init(TempAllocator &alloc); @@ -49,56 +50,56 @@ class BitSet : private TempObject public: class Iterator; - static BitSet *New(TempAllocator &alloc, unsigned int max); + static BitSet *New(TempAllocator &alloc, unsigned int numBits); - unsigned int getMax() const { - return max_; + unsigned int getNumBits() const { + return numBits_; } // O(1): Check if this set contains the given value. bool contains(unsigned int value) const { JS_ASSERT(bits_); - JS_ASSERT(value < max_); + JS_ASSERT(value < numBits_); return !!(bits_[wordForValue(value)] & bitForValue(value)); } - // O(max): Check if this set contains any value. + // O(numBits): Check if this set contains any value. bool empty() const; // O(1): Insert the given value into this set. void insert(unsigned int value) { JS_ASSERT(bits_); - JS_ASSERT(value < max_); + JS_ASSERT(value < numBits_); bits_[wordForValue(value)] |= bitForValue(value); } - // O(max): Insert every element of the given set into this set. + // O(numBits): Insert every element of the given set into this set. void insertAll(const BitSet *other); // O(1): Remove the given value from this set. void remove(unsigned int value) { JS_ASSERT(bits_); - JS_ASSERT(value < max_); + JS_ASSERT(value < numBits_); bits_[wordForValue(value)] &= ~bitForValue(value); } - // O(max): Remove the every element of the given set from this set. + // O(numBits): Remove the every element of the given set from this set. void removeAll(const BitSet *other); - // O(max): Intersect this set with the given set. + // O(numBits): Intersect this set with the given set. void intersect(const BitSet *other); - // O(max): Intersect this set with the given set; return whether the + // O(numBits): Intersect this set with the given set; return whether the // intersection caused the set to change. bool fixedPointIntersect(const BitSet *other); - // O(max): Does inplace complement of the set. + // O(numBits): Does inplace complement of the set. void complement(); - // O(max): Clear this set. + // O(numBits): Clear this set. void clear(); uint32_t *raw() const { @@ -137,7 +138,7 @@ class BitSet::Iterator inline Iterator& operator++(int dummy) { JS_ASSERT(more()); - JS_ASSERT(index_ < set_.max_); + JS_ASSERT(index_ < set_.numBits_); index_++; value_ >>= 1; @@ -158,12 +159,12 @@ class BitSet::Iterator index_ += numZeros; value_ >>= numZeros; - JS_ASSERT_IF(index_ < set_.max_, set_.contains(index_)); + JS_ASSERT_IF(index_ < set_.numBits_, set_.contains(index_)); return *this; } unsigned int operator *() { - JS_ASSERT(index_ < set_.max_); + JS_ASSERT(index_ < set_.numBits_); return index_; } }; From c9d9fd8426fb4189f8fe8c8e69a652a9d3c710b0 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 13 Dec 2013 08:27:47 -0800 Subject: [PATCH 059/144] Bug 949171 - SpiderMonkey: Remove fudge from the RawLengthForBits calculation, and fix Safepoint reading to be ok with this. r=nbp --- js/src/jit/BitSet.h | 8 +++++--- js/src/jit/Safepoints.cpp | 15 +++++++-------- js/src/jit/Safepoints.h | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/js/src/jit/BitSet.h b/js/src/jit/BitSet.h index 434b2bac5a77..b602b4285222 100644 --- a/js/src/jit/BitSet.h +++ b/js/src/jit/BitSet.h @@ -21,8 +21,10 @@ namespace jit { class BitSet : private TempObject { public: + static const size_t BitsPerWord = 8 * sizeof(uint32_t); + static size_t RawLengthForBits(size_t bits) { - return 1 + bits / (8 * sizeof(uint32_t)); + return (bits + BitsPerWord - 1) / BitsPerWord; } private: @@ -34,11 +36,11 @@ class BitSet : private TempObject uint32_t *bits_; static inline uint32_t bitForValue(unsigned int value) { - return 1l << (uint32_t)(value % (8 * sizeof(uint32_t))); + return 1l << uint32_t(value % BitsPerWord); } static inline unsigned int wordForValue(unsigned int value) { - return value / (8 * sizeof(uint32_t)); + return value / BitsPerWord; } inline unsigned int numWords() const { diff --git a/js/src/jit/Safepoints.cpp b/js/src/jit/Safepoints.cpp index 125d35acb012..c4f0c7906ecf 100644 --- a/js/src/jit/Safepoints.cpp +++ b/js/src/jit/Safepoints.cpp @@ -374,22 +374,21 @@ SafepointReader::InvalidationPatchPoint(IonScript *script, const SafepointIndex void SafepointReader::advanceFromGcRegs() { - currentSlotChunkNumber_ = 0; - currentSlotChunk_ = stream_.readUnsigned(); + currentSlotChunk_ = 0; + nextSlotChunkNumber_ = 0; } bool SafepointReader::getSlotFromBitmap(uint32_t *slot) { while (currentSlotChunk_ == 0) { - currentSlotChunkNumber_++; - // Are there any more chunks to read? - if (currentSlotChunkNumber_ == BitSet::RawLengthForBits(frameSlots_)) + if (nextSlotChunkNumber_ == BitSet::RawLengthForBits(frameSlots_)) return false; // Yes, read the next chunk. currentSlotChunk_ = stream_.readUnsigned(); + nextSlotChunkNumber_++; } // The current chunk still has bits in it, so get the next bit, then mask @@ -399,7 +398,7 @@ SafepointReader::getSlotFromBitmap(uint32_t *slot) // Return the slot, taking care to add 1 back in since it was subtracted // when added in the original bitset. - *slot = (currentSlotChunkNumber_ * sizeof(uint32_t) * 8) + bit + 1; + *slot = ((nextSlotChunkNumber_ - 1) * BitSet::BitsPerWord) + bit + 1; return true; } @@ -416,8 +415,8 @@ void SafepointReader::advanceFromGcSlots() { // No, reset the counter. - currentSlotChunkNumber_ = 0; - currentSlotChunk_ = stream_.readUnsigned(); + currentSlotChunk_ = 0; + nextSlotChunkNumber_ = 0; } bool diff --git a/js/src/jit/Safepoints.h b/js/src/jit/Safepoints.h index c151634452ac..ea30e50b29be 100644 --- a/js/src/jit/Safepoints.h +++ b/js/src/jit/Safepoints.h @@ -61,7 +61,7 @@ class SafepointReader CompactBufferReader stream_; uint32_t frameSlots_; uint32_t currentSlotChunk_; - uint32_t currentSlotChunkNumber_; + uint32_t nextSlotChunkNumber_; uint32_t osiCallPointOffset_; GeneralRegisterSet gcSpills_; GeneralRegisterSet valueSpills_; From 329bfe15baf1ab30630e84c70852cc59244e1344 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 13 Dec 2013 08:27:47 -0800 Subject: [PATCH 060/144] Bug 949171 - IonMonkey: Check more things in AssertBasicGraphCoherency. r=nbp --- js/src/jit/IonAnalysis.cpp | 63 ++++++++++++++++++++++++++------------ js/src/jit/MIR.h | 1 + 2 files changed, 44 insertions(+), 20 deletions(-) diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index 839c3ecb2305..049c9de5c5c6 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -1253,29 +1253,19 @@ CheckPredecessorImpliesSuccessor(MBasicBlock *A, MBasicBlock *B) } static bool -CheckOperandImpliesUse(MInstruction *ins, MDefinition *operand) +CheckOperandImpliesUse(MNode *n, MDefinition *operand) { for (MUseIterator i = operand->usesBegin(); i != operand->usesEnd(); i++) { - if (i->consumer()->isDefinition() && i->consumer()->toDefinition() == ins) + if (i->consumer() == n) return true; } return false; } static bool -CheckUseImpliesOperand(MInstruction *ins, MUse *use) +CheckUseImpliesOperand(MDefinition *def, MUse *use) { - MNode *consumer = use->consumer(); - uint32_t index = use->index(); - - if (consumer->isDefinition()) { - MDefinition *def = consumer->toDefinition(); - return (def->getOperand(index) == ins); - } - - JS_ASSERT(consumer->isResumePoint()); - MResumePoint *res = consumer->toResumePoint(); - return (res->getOperand(index) == ins); + return use->consumer()->getOperand(use->index()) == def; } #endif // DEBUG @@ -1283,11 +1273,27 @@ void jit::AssertBasicGraphCoherency(MIRGraph &graph) { #ifdef DEBUG + JS_ASSERT(graph.entryBlock()->numPredecessors() == 0); + JS_ASSERT(graph.entryBlock()->phisEmpty()); + JS_ASSERT(!graph.entryBlock()->unreachable()); + + if (MBasicBlock *osrBlock = graph.osrBlock()) { + JS_ASSERT(osrBlock->numPredecessors() == 0); + JS_ASSERT(osrBlock->phisEmpty()); + JS_ASSERT(osrBlock != graph.entryBlock()); + JS_ASSERT(!osrBlock->unreachable()); + } + + if (MResumePoint *resumePoint = graph.entryResumePoint()) + JS_ASSERT(resumePoint->block() == graph.entryBlock()); + // Assert successor and predecessor list coherency. uint32_t count = 0; for (MBasicBlockIterator block(graph.begin()); block != graph.end(); block++) { count++; + JS_ASSERT(&block->graph() == &graph); + for (size_t i = 0; i < block->numSuccessors(); i++) JS_ASSERT(CheckSuccessorImpliesPredecessor(*block, block->getSuccessor(i))); @@ -1295,13 +1301,30 @@ jit::AssertBasicGraphCoherency(MIRGraph &graph) JS_ASSERT(CheckPredecessorImpliesSuccessor(*block, block->getPredecessor(i))); // Assert that use chains are valid for this instruction. - for (MInstructionIterator ins = block->begin(); ins != block->end(); ins++) { - for (uint32_t i = 0, e = ins->numOperands(); i < e; i++) - JS_ASSERT(CheckOperandImpliesUse(*ins, ins->getOperand(i))); + for (MDefinitionIterator iter(*block); iter; iter++) { + for (uint32_t i = 0, e = iter->numOperands(); i < e; i++) + JS_ASSERT(CheckOperandImpliesUse(*iter, iter->getOperand(i))); } - for (MInstructionIterator ins = block->begin(); ins != block->end(); ins++) { - for (MUseIterator i(ins->usesBegin()); i != ins->usesEnd(); i++) - JS_ASSERT(CheckUseImpliesOperand(*ins, *i)); + for (MResumePointIterator iter(block->resumePointsBegin()); iter != block->resumePointsEnd(); iter++) { + for (uint32_t i = 0, e = iter->numOperands(); i < e; i++) { + if (iter->getUseFor(i)->hasProducer()) + JS_ASSERT(CheckOperandImpliesUse(*iter, iter->getOperand(i))); + } + } + for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd(); phi++) { + JS_ASSERT(phi->numOperands() == block->numPredecessors()); + } + for (MDefinitionIterator iter(*block); iter; iter++) { + JS_ASSERT(iter->block() == *block); + for (MUseIterator i(iter->usesBegin()); i != iter->usesEnd(); i++) + JS_ASSERT(CheckUseImpliesOperand(*iter, *i)); + + if (iter->isInstruction()) { + if (MResumePoint *resume = iter->toInstruction()->resumePoint()) { + if (MInstruction *ins = resume->instruction()) + JS_ASSERT(ins->block() == iter->block()); + } + } } } diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 3eff668f0e5a..b8bd03eb23f1 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -9005,6 +9005,7 @@ class MResumePoint MOZ_FINAL : public MNode, public InlineForwardListNode operands_; uint32_t stackDepth_; From 7bfe3843b4f032b3ac890deb89eeb985497c5301 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 13 Dec 2013 08:27:48 -0800 Subject: [PATCH 061/144] Bug 949171 - IonMonkey: Enforce some invariants for LSafepoint. r=nbp --- js/src/jit/LIR.h | 63 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/js/src/jit/LIR.h b/js/src/jit/LIR.h index 20ad0db4317d..3181cd053871 100644 --- a/js/src/jit/LIR.h +++ b/js/src/jit/LIR.h @@ -1026,6 +1026,14 @@ class LSafepoint : public TempObject SlotList slotsOrElementsSlots_; public: + void assertInvariants() { + // Every register in valueRegs and gcRegs should also be in liveRegs. +#ifndef JS_NUNBOX32 + JS_ASSERT((valueRegs().bits() & ~liveRegs().gprs().bits()) == 0); +#endif + JS_ASSERT((gcRegs().bits() & ~liveRegs().gprs().bits()) == 0); + } + LSafepoint(TempAllocator &alloc) : safepointOffset_(INVALID_SAFEPOINT_OFFSET) , osiCallPointOffset_(0) @@ -1036,9 +1044,12 @@ class LSafepoint : public TempObject , partialNunboxes_(0) #endif , slotsOrElementsSlots_(alloc) - { } + { + assertInvariants(); + } void addLiveRegister(AnyRegister reg) { liveRegs_.addUnchecked(reg); + assertInvariants(); } const RegisterSet &liveRegs() const { return liveRegs_; @@ -1046,6 +1057,7 @@ class LSafepoint : public TempObject #ifdef CHECK_OSIPOINT_REGISTERS void addTempRegister(AnyRegister reg) { tempRegs_.addUnchecked(reg); + assertInvariants(); } const RegisterSet &tempRegs() const { return tempRegs_; @@ -1053,12 +1065,16 @@ class LSafepoint : public TempObject #endif void addGcRegister(Register reg) { gcRegs_.addUnchecked(reg); + assertInvariants(); } GeneralRegisterSet gcRegs() const { return gcRegs_; } bool addGcSlot(uint32_t slot) { - return gcSlots_.append(slot); + bool result = gcSlots_.append(slot); + if (result) + assertInvariants(); + return result; } SlotList &gcSlots() { return gcSlots_; @@ -1072,18 +1088,23 @@ class LSafepoint : public TempObject } void addSlotsOrElementsRegister(Register reg) { slotsOrElementsRegs_.addUnchecked(reg); + assertInvariants(); } bool addSlotsOrElementsSlot(uint32_t slot) { - return slotsOrElementsSlots_.append(slot); + bool result = slotsOrElementsSlots_.append(slot); + if (result) + assertInvariants(); + return result; } bool addSlotsOrElementsPointer(LAllocation alloc) { if (alloc.isStackSlot()) return addSlotsOrElementsSlot(alloc.toStackSlot()->slot()); JS_ASSERT(alloc.isRegister()); addSlotsOrElementsRegister(alloc.toRegister().gpr()); + assertInvariants(); return true; } - bool hasSlotsOrElementsPointer(LAllocation alloc) { + bool hasSlotsOrElementsPointer(LAllocation alloc) const { if (alloc.isRegister()) return slotsOrElementsRegs().has(alloc.toRegister().gpr()); if (alloc.isStackSlot()) { @@ -1101,10 +1122,11 @@ class LSafepoint : public TempObject return addGcSlot(alloc.toStackSlot()->slot()); if (alloc.isRegister()) addGcRegister(alloc.toRegister().gpr()); + assertInvariants(); return true; } - bool hasGcPointer(LAllocation alloc) { + bool hasGcPointer(LAllocation alloc) const { if (alloc.isRegister()) return gcRegs().has(alloc.toRegister().gpr()); if (alloc.isStackSlot()) { @@ -1119,13 +1141,16 @@ class LSafepoint : public TempObject } bool addValueSlot(uint32_t slot) { - return valueSlots_.append(slot); + bool result = valueSlots_.append(slot); + if (result) + assertInvariants(); + return result; } SlotList &valueSlots() { return valueSlots_; } - bool hasValueSlot(uint32_t slot) { + bool hasValueSlot(uint32_t slot) const { for (size_t i = 0; i < valueSlots_.length(); i++) { if (valueSlots_[i] == slot) return true; @@ -1136,7 +1161,10 @@ class LSafepoint : public TempObject #ifdef JS_NUNBOX32 bool addNunboxParts(LAllocation type, LAllocation payload) { - return nunboxParts_.append(NunboxEntry(type, payload)); + bool result = nunboxParts_.append(NunboxEntry(type, payload)); + if (result) + assertInvariants(); + return result; } bool addNunboxType(uint32_t typeVreg, LAllocation type) { @@ -1153,10 +1181,13 @@ class LSafepoint : public TempObject // vregs for nunbox pairs are adjacent, with the type coming first. uint32_t payloadVreg = typeVreg + 1; - return nunboxParts_.append(NunboxEntry(type, LUse(payloadVreg, LUse::ANY))); + bool result = nunboxParts_.append(NunboxEntry(type, LUse(payloadVreg, LUse::ANY))); + if (result) + assertInvariants(); + return result; } - bool hasNunboxType(LAllocation type) { + bool hasNunboxType(LAllocation type) const { if (type.isArgument()) return true; if (type.isStackSlot() && hasValueSlot(type.toStackSlot()->slot() + 1)) @@ -1182,10 +1213,13 @@ class LSafepoint : public TempObject // vregs for nunbox pairs are adjacent, with the type coming first. uint32_t typeVreg = payloadVreg - 1; - return nunboxParts_.append(NunboxEntry(LUse(typeVreg, LUse::ANY), payload)); + bool result = nunboxParts_.append(NunboxEntry(LUse(typeVreg, LUse::ANY), payload)); + if (result) + assertInvariants(); + return result; } - bool hasNunboxPayload(LAllocation payload) { + bool hasNunboxPayload(LAllocation payload) const { if (payload.isArgument()) return true; if (payload.isStackSlot() && hasValueSlot(payload.toStackSlot()->slot())) @@ -1209,8 +1243,9 @@ class LSafepoint : public TempObject void addValueRegister(Register reg) { valueRegs_.add(reg); + assertInvariants(); } - GeneralRegisterSet valueRegs() { + GeneralRegisterSet valueRegs() const { return valueRegs_; } @@ -1233,7 +1268,7 @@ class LSafepoint : public TempObject return true; } - bool hasBoxedValue(LAllocation alloc) { + bool hasBoxedValue(LAllocation alloc) const { if (alloc.isRegister()) return valueRegs().has(alloc.toRegister().gpr()); if (alloc.isStackSlot()) From 4047601939ef0dd079b0efce318b8d36feef348b Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 13 Dec 2013 08:54:04 -0800 Subject: [PATCH 062/144] Bug 944407 - Allow scripts for an XBL binding if and only if the XBL document comes from a scriptable domain. r=bz --- content/xbl/src/nsXBLBinding.cpp | 23 +---------------------- content/xbl/src/nsXBLBinding.h | 2 +- content/xbl/src/nsXBLDocumentInfo.cpp | 18 ++++++++++++++++++ content/xbl/src/nsXBLDocumentInfo.h | 2 +- content/xbl/src/nsXBLPrototypeBinding.cpp | 2 +- content/xbl/src/nsXBLPrototypeBinding.h | 2 +- 6 files changed, 23 insertions(+), 26 deletions(-) diff --git a/content/xbl/src/nsXBLBinding.cpp b/content/xbl/src/nsXBLBinding.cpp index f8dbfc46df4c..a91fe995ce68 100644 --- a/content/xbl/src/nsXBLBinding.cpp +++ b/content/xbl/src/nsXBLBinding.cpp @@ -1082,28 +1082,7 @@ nsXBLBinding::DoInitJSClass(JSContext *cx, JS::Handle global, bool nsXBLBinding::AllowScripts() { - if (!mPrototypeBinding->GetAllowScripts()) - return false; - - // Nasty hack. Use the JSContext of the bound node, since the - // security manager API expects to get the docshell type from - // that. But use the nsIPrincipal of our document. - nsIScriptSecurityManager* mgr = nsContentUtils::GetSecurityManager(); - if (!mgr) { - return false; - } - - nsIDocument* doc = mBoundElement ? mBoundElement->OwnerDoc() : nullptr; - if (!doc) { - return false; - } - - nsCOMPtr global = do_QueryInterface(doc->GetInnerWindow()); - if (!global || !global->GetGlobalJSObject()) { - return false; - } - - return mgr->ScriptAllowed(global->GetGlobalJSObject()); + return mPrototypeBinding->GetAllowScripts(); } nsXBLBinding* diff --git a/content/xbl/src/nsXBLBinding.h b/content/xbl/src/nsXBLBinding.h index ac577fe3b1bd..c2af9a993db8 100644 --- a/content/xbl/src/nsXBLBinding.h +++ b/content/xbl/src/nsXBLBinding.h @@ -141,7 +141,7 @@ public: JS::MutableHandle aClassObject, bool* aNew); - bool AllowScripts(); // XXX make const + bool AllowScripts(); mozilla::dom::XBLChildrenElement* FindInsertionPointFor(nsIContent* aChild); diff --git a/content/xbl/src/nsXBLDocumentInfo.cpp b/content/xbl/src/nsXBLDocumentInfo.cpp index 5c304c26566e..b3f6acde8d4c 100644 --- a/content/xbl/src/nsXBLDocumentInfo.cpp +++ b/content/xbl/src/nsXBLDocumentInfo.cpp @@ -403,6 +403,24 @@ nsXBLDocumentInfo::nsXBLDocumentInfo(nsIDocument* aDocument) mScriptAccess = allow; } mIsChrome = true; + } else { + // If this binding isn't running with system principal, then it's running + // from a remote-XUL whitelisted domain. This is already a not-really- + // supported configuration (among other things, we don't use XBL scopes in + // that configuration for compatibility reasons). But we should still at + // least make an effort to prevent binding code from running if content + // script is disabled or if the source domain is blacklisted (since the + // source domain for remote XBL must always be the same as the source domain + // of the bound content). + // + // If we just ask the binding document if script is enabled, it will + // discover that it has no inner window, and return false. So instead, we + // short-circuit the normal compartment-managed script-disabling machinery, + // and query the policy for the URI directly. + bool allow; + nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); + nsresult rv = ssm->PolicyAllowsScript(uri, &allow); + mScriptAccess = NS_SUCCEEDED(rv) && allow; } } diff --git a/content/xbl/src/nsXBLDocumentInfo.h b/content/xbl/src/nsXBLDocumentInfo.h index 9c88d504e90a..412738a4118d 100644 --- a/content/xbl/src/nsXBLDocumentInfo.h +++ b/content/xbl/src/nsXBLDocumentInfo.h @@ -27,7 +27,7 @@ public: already_AddRefed GetDocument() { nsCOMPtr copy = mDocument; return copy.forget(); } - bool GetScriptAccess() { return mScriptAccess; } + bool GetScriptAccess() const { return mScriptAccess; } nsIURI* DocumentURI() { return mDocument->GetDocumentURI(); } diff --git a/content/xbl/src/nsXBLPrototypeBinding.cpp b/content/xbl/src/nsXBLPrototypeBinding.cpp index eb0e351ab593..385c3dd3ba4b 100644 --- a/content/xbl/src/nsXBLPrototypeBinding.cpp +++ b/content/xbl/src/nsXBLPrototypeBinding.cpp @@ -214,7 +214,7 @@ nsXBLPrototypeBinding::SetBindingElement(nsIContent* aElement) } bool -nsXBLPrototypeBinding::GetAllowScripts() +nsXBLPrototypeBinding::GetAllowScripts() const { return mXBLDocInfoWeak->GetScriptAccess(); } diff --git a/content/xbl/src/nsXBLPrototypeBinding.h b/content/xbl/src/nsXBLPrototypeBinding.h index e38fc8e9e355..eb3fe54df2d8 100644 --- a/content/xbl/src/nsXBLPrototypeBinding.h +++ b/content/xbl/src/nsXBLPrototypeBinding.h @@ -48,7 +48,7 @@ public: // binding URIs. bool CompareBindingURI(nsIURI* aURI) const; - bool GetAllowScripts(); + bool GetAllowScripts() const; nsresult BindingAttached(nsIContent* aBoundElement); nsresult BindingDetached(nsIContent* aBoundElement); From 9d29199dc3aff1c1cfd8a892b77cf7a6f138330a Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 13 Dec 2013 08:54:04 -0800 Subject: [PATCH 063/144] Bug 944407 - Tests. r=bz --- content/xbl/test/chrome.ini | 3 ++ content/xbl/test/file_bug944407.html | 8 +++ content/xbl/test/file_bug944407.xml | 78 ++++++++++++++++++++++++++++ content/xbl/test/mochitest.ini | 2 + content/xbl/test/test_bug944407.xul | 44 ++++++++++++++++ 5 files changed, 135 insertions(+) create mode 100644 content/xbl/test/file_bug944407.html create mode 100644 content/xbl/test/file_bug944407.xml create mode 100644 content/xbl/test/test_bug944407.xul diff --git a/content/xbl/test/chrome.ini b/content/xbl/test/chrome.ini index f55d0ff8c4e4..cf117273acfb 100644 --- a/content/xbl/test/chrome.ini +++ b/content/xbl/test/chrome.ini @@ -1,4 +1,6 @@ [DEFAULT] +support-files = + file_bug944407.xml [test_bug378518.xul] [test_bug398135.xul] @@ -6,3 +8,4 @@ [test_bug721452.xul] [test_bug723676.xul] [test_bug772966.xul] +[test_bug944407.xul] diff --git a/content/xbl/test/file_bug944407.html b/content/xbl/test/file_bug944407.html new file mode 100644 index 000000000000..5df5d5bc97b0 --- /dev/null +++ b/content/xbl/test/file_bug944407.html @@ -0,0 +1,8 @@ + + + +
+
+ + + diff --git a/content/xbl/test/file_bug944407.xml b/content/xbl/test/file_bug944407.xml new file mode 100644 index 000000000000..6aa980dfce9c --- /dev/null +++ b/content/xbl/test/file_bug944407.xml @@ -0,0 +1,78 @@ + + + + + + return 3; + + + + + + + + + + + + + + + Anonymous Content + + diff --git a/content/xbl/test/mochitest.ini b/content/xbl/test/mochitest.ini index 8c82329009d9..b390426f19a5 100644 --- a/content/xbl/test/mochitest.ini +++ b/content/xbl/test/mochitest.ini @@ -12,6 +12,8 @@ support-files = file_bug591198_xbl.xml file_bug821850.xhtml file_bug844783.xhtml + file_bug944407.html + file_bug944407.xml [test_bug310107.html] [test_bug366770.html] diff --git a/content/xbl/test/test_bug944407.xul b/content/xbl/test/test_bug944407.xul new file mode 100644 index 000000000000..c4ddd200f7e0 --- /dev/null +++ b/content/xbl/test/test_bug944407.xul @@ -0,0 +1,44 @@ + + + + + + +